diff --git a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc
index 1d035998c..8871045f6 100644
--- a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc
+++ b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc
@@ -464,8 +464,13 @@
column-add-auto (/ free-column-space column-autos)
row-add-auto (/ free-row-space row-autos)
- column-tracks (add-auto-size column-tracks column-add-auto)
- row-tracks (add-auto-size row-tracks row-add-auto)
+ column-tracks (cond-> column-tracks
+ (= :stretch (:layout-align-content parent))
+ (add-auto-size column-add-auto))
+
+ row-tracks (cond-> row-tracks
+ (= :stretch (:layout-justify-content parent))
+ (add-auto-size row-add-auto))
column-total-size (tracks-total-size column-tracks)
row-total-size (tracks-total-size row-tracks)
@@ -556,8 +561,7 @@
:column-total-size column-total-size
:column-total-gap column-total-gap
:row-total-size row-total-size
- :row-total-gap row-total-gap
- }))
+ :row-total-gap row-total-gap}))
(defn get-cell-data
[{:keys [origin row-tracks column-tracks shape-cells]} _transformed-parent-bounds [_ child]]
diff --git a/frontend/resources/styles/main/layouts/inspect.scss b/frontend/resources/styles/main/layouts/inspect.scss
index 8b13d09f7..e26df3ece 100644
--- a/frontend/resources/styles/main/layouts/inspect.scss
+++ b/frontend/resources/styles/main/layouts/inspect.scss
@@ -106,9 +106,6 @@ $width-settings-bar: 256px;
.settings-bar {
transition: width 0.2s;
width: $width-settings-bar;
- &.expanded {
- width: $width-settings-bar * 3;
- }
&.settings-bar-right,
&.settings-bar-left {
diff --git a/frontend/resources/styles/main/partials/inspect.scss b/frontend/resources/styles/main/partials/inspect.scss
index 32ce5a9e7..8693da4e5 100644
--- a/frontend/resources/styles/main/partials/inspect.scss
+++ b/frontend/resources/styles/main/partials/inspect.scss
@@ -328,6 +328,7 @@
}
.code-block {
+ position: relative;
margin-top: 0.5rem;
border-top: 1px solid $color-gray-60;
@@ -353,17 +354,86 @@
.copy-button {
margin-top: 8px;
}
+
+ .custom-select {
+ border: 1px solid $color-gray-40;
+ border-radius: 3px;
+ cursor: pointer;
+ padding: 0.25rem 1.5rem 0.25rem 0.25rem;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+
+ .dropdown-button {
+ position: absolute;
+ right: 0.25rem;
+ top: 7px;
+
+ svg {
+ fill: $color-gray-40;
+ height: 10px;
+ width: 10px;
+ }
+ }
+ }
+
+ .custom-select-dropdown {
+ background-color: $color-white;
+ border-radius: 3px;
+ box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25);
+ left: 0;
+ max-height: 30rem;
+ min-width: 7rem;
+ position: absolute;
+ overflow-y: auto;
+ top: 30px;
+ z-index: 12;
+
+ li {
+ color: $color-gray-60;
+ cursor: pointer;
+ font-size: 0.875rem;
+ display: flex;
+ gap: 0 10px;
+ justify-content: flex-start;
+ padding: 0.5rem;
+
+ .checked-element {
+ padding-left: 0;
+ }
+ }
+
+ svg {
+ visibility: hidden;
+ width: 8px;
+ height: 8px;
+ background: none;
+ margin: 0.25rem;
+ fill: $color-black;
+ }
+
+ .is-selected svg {
+ visibility: visible;
+ }
+ }
+
}
.code-row-display {
+ line-height: 1;
margin: 0.5rem;
font-size: $fs14;
+ max-height: var(--code-height, 400px);
+ overflow: auto;
.code-display {
+ font-family: monospace;
border-radius: $br4;
- padding: 1rem;
+ padding: 0.5rem 1rem;
overflow: hidden;
- white-space: pre-wrap;
+ white-space: pre;
+ min-width: fit-content;
background: $color-gray-60;
user-select: text;
@@ -378,6 +448,15 @@
}
}
}
+ .resize-area {
+ width: 100%;
+ position: absolute;
+ bottom: -15px;
+ left: 0;
+ height: 18px;
+ z-index: 1;
+ cursor: ns-resize;
+ }
}
.element-options > :first-child {
diff --git a/frontend/resources/styles/main/partials/tab-container.scss b/frontend/resources/styles/main/partials/tab-container.scss
index 41490f896..33c759e0a 100644
--- a/frontend/resources/styles/main/partials/tab-container.scss
+++ b/frontend/resources/styles/main/partials/tab-container.scss
@@ -35,6 +35,10 @@
overflow-x: hidden;
}
+.inspect .tab-container-content {
+ overflow: hidden;
+}
+
.tab-element,
.tab-element-content {
height: 100%;
diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss
index 595d2d831..9f5b8104f 100644
--- a/frontend/resources/styles/main/partials/workspace.scss
+++ b/frontend/resources/styles/main/partials/workspace.scss
@@ -48,14 +48,11 @@ $height-palette-max: 80px;
}
.settings-bar.settings-bar-right {
- transition: width 0.2s;
- min-width: $width-settings-bar;
- max-width: $width-settings-bar * 3;
- width: $width-settings-bar;
+ width: var(--width, $width-settings-bar);
grid-area: right-sidebar;
- &.expanded {
- width: $width-settings-bar * 3;
+ &.not-expand {
+ max-width: $width-settings-bar;
}
}
diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs
index 33e019b99..0de1233fb 100644
--- a/frontend/src/app/main.cljs
+++ b/frontend/src/app/main.cljs
@@ -88,9 +88,9 @@
(defn ^:export reinit
[]
- (mf/unmount (dom/get-element "app"))
- (mf/unmount (dom/get-element "modal"))
- (st/emit! (ev/initialize))
+ #_(mf/unmount (dom/get-element "app"))
+ #_(mf/unmount (dom/get-element "modal"))
+ #_(st/emit! (ev/initialize))
(init-ui))
(defn ^:dev/after-load after-load
diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs
index b2e9436e7..ce0cf18a7 100644
--- a/frontend/src/app/main/data/workspace.cljs
+++ b/frontend/src/app/main/data/workspace.cljs
@@ -2160,20 +2160,6 @@
(let [orphans (set (into [] (keys (wsh/find-orphan-shapes state))))]
(rx/of (relocate-shapes orphans uuid/zero 0 true))))))
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Inspect
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-
-(defn set-inspect-expanded
- [expanded?]
- (ptk/reify ::set-inspect-expanded
- ptk/UpdateEvent
- (update [_ state]
- (assoc-in state [:workspace-local :inspect-expanded] expanded?))))
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Sitemap
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs
index bba3d9bda..15873ee1c 100644
--- a/frontend/src/app/main/data/workspace/shape_layout.cljs
+++ b/frontend/src/app/main/data/workspace/shape_layout.cljs
@@ -59,9 +59,9 @@
:layout-gap-type :multiple
:layout-gap {:row-gap 0 :column-gap 0}
:layout-align-items :start
- :layout-align-content :start
:layout-justify-items :start
- :layout-justify-content :start
+ :layout-align-content :stretch
+ :layout-justify-content :stretch
:layout-padding-type :simple
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}
:layout-grid-cells {}
diff --git a/frontend/src/app/main/ui/hooks/resize.cljs b/frontend/src/app/main/ui/hooks/resize.cljs
index 1782048ca..6dd334678 100644
--- a/frontend/src/app/main/ui/hooks/resize.cljs
+++ b/frontend/src/app/main/ui/hooks/resize.cljs
@@ -9,6 +9,7 @@
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.logging :as log]
+ [app.common.math :as mth]
[app.main.ui.context :as ctx]
[app.main.ui.hooks :as hooks]
[app.util.dom :as dom]
@@ -65,11 +66,19 @@
start-size (mf/ref-val start-size-ref)
new-size (-> (+ start-size delta) (max min-val) (min max-val))]
(reset! size-state new-size)
- (swap! storage assoc-in [::saved-resize current-file-id key] new-size)))))]
+ (swap! storage assoc-in [::saved-resize current-file-id key] new-size)))))
+
+ set-size
+ (mf/use-callback
+ (fn [new-size]
+ (let [new-size (mth/clamp new-size min-val max-val)]
+ (reset! size-state new-size)
+ (swap! storage assoc-in [::saved-resize current-file-id key] new-size))))]
{:on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture
:on-pointer-move on-pointer-move
:parent-ref parent-ref
+ :set-size set-size
:size @size-state}))
(defn use-resize-observer
diff --git a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs
index 6bfcdf675..56fa88488 100644
--- a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs
+++ b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs
@@ -48,6 +48,18 @@
(mf/ref-val fonts-css-ref)))
+(mf/defc fontfaces-style-html
+ {::mf/wrap-props false
+ ::mf/wrap [#(mf/memo' % (mf/check-props ["fonts"]))]}
+ [props]
+
+ (let [fonts (obj/get props "fonts")
+
+ ;; Fetch its CSS fontfaces
+ fonts-css (use-fonts-css fonts)]
+
+ [:style fonts-css]))
+
(mf/defc fontfaces-style-render
{::mf/wrap-props false
::mf/wrap [#(mf/memo' % (mf/check-props ["fonts"]))]}
@@ -63,7 +75,6 @@
(mf/deps fonts-css)
#(fonts/extract-fontface-urls fonts-css))
-
;; Calculate the data-uris for these fonts
fonts-embed (embed/use-data-uris fonts-urls)
diff --git a/frontend/src/app/main/ui/shapes/text/html_text.cljs b/frontend/src/app/main/ui/shapes/text/html_text.cljs
index d60810e16..399d57d3e 100644
--- a/frontend/src/app/main/ui/shapes/text/html_text.cljs
+++ b/frontend/src/app/main/ui/shapes/text/html_text.cljs
@@ -83,19 +83,24 @@
[props ref]
(let [shape (obj/get props "shape")
grow-type (obj/get props "grow-type")
- {:keys [id x y width height content]} shape]
+ code? (obj/get props "code?")
+ {:keys [id x y width height content]} shape
+
+ style
+ (when-not code?
+ #js {:position "fixed"
+ :left 0
+ :top 0
+ :background "white"
+ :width (if (#{:auto-width} grow-type) 100000 width)
+ :height (if (#{:auto-height :auto-width} grow-type) 100000 height)})]
[:div.text-node-html
{:id (dm/str "html-text-node-" id)
:ref ref
:data-x x
:data-y y
- :style {:position "fixed"
- :left 0
- :top 0
- :background "white"
- :width (if (#{:auto-width} grow-type) 100000 width)
- :height (if (#{:auto-height :auto-width} grow-type) 100000 height)}}
+ :style style}
;; We use a class here because react has a bug that won't use the appropriate selector for
;; `background-clip`
[:style ".text-node { background-clip: text;
diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs
index 5ad25a7df..891cca1ee 100644
--- a/frontend/src/app/main/ui/shapes/text/styles.cljs
+++ b/frontend/src/app/main/ui/shapes/text/styles.cljs
@@ -48,7 +48,8 @@
[_shape data]
(let [line-height (:line-height data 1.2)
text-align (:text-align data "start")
- base #js {:fontSize (str (:font-size data (:font-size txt/default-text-attrs)) "px")
+ base #js {;; Fix a problem when exporting HTML
+ :fontSize 0 ;;(str (:font-size data (:font-size txt/default-text-attrs)) "px")
:lineHeight (:line-height data (:line-height txt/default-text-attrs))
:margin 0}]
(cond-> base
diff --git a/frontend/src/app/main/ui/viewer/inspect/code.cljs b/frontend/src/app/main/ui/viewer/inspect/code.cljs
index 2d97af2ed..fc3f4dd6d 100644
--- a/frontend/src/app/main/ui/viewer/inspect/code.cljs
+++ b/frontend/src/app/main/ui/viewer/inspect/code.cljs
@@ -7,41 +7,36 @@
(ns app.main.ui.viewer.inspect.code
(:require
["js-beautify" :as beautify]
- ["react-dom/server" :as rds]
+ [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh]
+ [app.common.pages.helpers :as cph]
+ [app.common.types.shape-tree :as ctst]
+ [app.config :as cfg]
[app.main.data.events :as ev]
+ [app.main.fonts :as fonts]
[app.main.refs :as refs]
- [app.main.render :as render]
[app.main.store :as st]
[app.main.ui.components.code-block :refer [code-block]]
[app.main.ui.components.copy-button :refer [copy-button]]
+ [app.main.ui.components.select :refer [select]]
[app.main.ui.hooks :as hooks]
+ [app.main.ui.hooks.resize :refer [use-resize-hook]]
[app.main.ui.icons :as i]
+ [app.main.ui.shapes.text.fontfaces :refer [shapes->fonts]]
[app.util.code-gen :as cg]
+ [app.util.http :as http]
+ [beicon.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]
[rumext.v2 :as mf]))
-(defn generate-markup-code [objects shapes]
- ;; Here we can render specific HTML code
- (->> shapes
- (map (fn [shape]
- (dm/str
- ""
- (rds/renderToStaticMarkup
- (mf/element
- render/object-svg
- #js {:objects objects
- :object-id (-> shape :id)})))))
- (str/join "\n\n")))
-
(defn format-code [code type]
(let [code (-> code
(str/replace "" "")
(str/replace "><" ">\n<"))]
(cond-> code
- (= type "svg") (beautify/html #js {"indent_size" 2}))))
+ (or (= type "svg") (= type "html")) (beautify/html #js {"indent_size" 2}))))
(defn get-flex-elements [page-id shapes from]
(let [ids (mapv :id shapes)
@@ -62,46 +57,139 @@
(refs/get-viewer-objects))))]
(mf/deref page-objects-ref)))
+(defn shapes->images
+ [shapes]
+ (->> shapes
+ (keep
+ (fn [shape]
+ (when-let [data (or (:metadata shape) (:fill-image shape))]
+ [(:id shape) (cfg/resolve-file-media data)])))))
+
+(defn replace-map
+ [value map]
+ (reduce
+ (fn [value [old new]]
+ (str/replace value old new))
+ value map))
+
(mf/defc code
[{:keys [shapes frame on-expand from]}]
- (let [style-type (mf/use-state "css")
- markup-type (mf/use-state "svg")
+ (let [style-type* (mf/use-state "css")
+ markup-type* (mf/use-state "html")
+ fontfaces-css* (mf/use-state nil)
+ images-data* (mf/use-state nil)
+
+ style-type (deref style-type*)
+ markup-type (deref markup-type*)
+ fontfaces-css (deref fontfaces-css*)
+ images-data (deref images-data*)
+
shapes (->> shapes
(map #(gsh/translate-to-frame % frame)))
+
route (mf/deref refs/route)
page-id (:page-id (:query-params route))
flex-items (get-flex-elements page-id shapes from)
objects (get-objects from)
+
+ ;; TODO REMOVE THIS
shapes (->> shapes
(map #(assoc % :parent (get objects (:parent-id %))))
(map #(assoc % :flex-items flex-items)))
- style-code (-> (cg/generate-style-code @style-type shapes)
- (format-code "css"))
+
+ all-children (->> shapes
+ (map :id)
+ (cph/selected-with-children objects)
+ (ctst/sort-z-index objects)
+ (map (d/getf objects)))
+
+
+ shapes (hooks/use-equal-memo shapes)
+ all-children (hooks/use-equal-memo all-children)
+
+ fonts (-> (shapes->fonts all-children)
+ (hooks/use-equal-memo))
+
+ images-urls (-> (shapes->images all-children)
+ (hooks/use-equal-memo))
+
+ style-code
+ (mf/use-memo
+ (mf/deps fontfaces-css style-type all-children)
+ (fn []
+ (dm/str
+ fontfaces-css "\n"
+ (-> (cg/generate-style-code objects style-type all-children)
+ (format-code style-type)))))
markup-code
- (-> (mf/use-memo (mf/deps shapes) #(generate-markup-code objects shapes))
- (format-code "svg"))
+ (mf/use-memo
+ (mf/deps markup-type shapes images-data)
+ (fn []
+ (-> (cg/generate-markup-code objects markup-type (map :id shapes))
+ (format-code markup-type))))
on-markup-copied
(mf/use-callback
- (mf/deps @markup-type)
+ (mf/deps markup-type)
(fn []
(st/emit! (ptk/event ::ev/event
{::ev/name "copy-inspect-code"
- :type @markup-type}))))
+ :type markup-type}))))
on-style-copied
(mf/use-callback
- (mf/deps @style-type)
+ (mf/deps style-type)
(fn []
(st/emit! (ptk/event ::ev/event
{::ev/name "copy-inspect-style"
- :type @style-type}))))]
+ :type style-type}))))
+
+ {on-code-pointer-down :on-pointer-down
+ on-code-lost-pointer-capture :on-lost-pointer-capture
+ on-code-pointer-move :on-pointer-move
+ code-size :size}
+ (use-resize-hook :code 400 100 800 :y false :bottom)
+
+ set-style
+ (mf/use-callback
+ (fn [value]
+ (reset! style-type* value)))
+
+ set-markup
+ (mf/use-callback
+ (fn [value]
+ (reset! markup-type* value)))]
+
+ (mf/use-effect
+ (mf/deps fonts)
+ #(->> (rx/from fonts)
+ (rx/merge-map fonts/fetch-font-css)
+ (rx/reduce conj [])
+ (rx/subs
+ (fn [result]
+ (let [css (str/join "\n" result)]
+ (reset! fontfaces-css* css))))))
+
+ (mf/use-effect
+ (mf/deps images-urls)
+ #(->> (rx/from images-urls)
+ (rx/merge-map
+ (fn [[_ uri]]
+ (->> (http/fetch-data-uri uri true)
+ (rx/catch (fn [_] (rx/of (hash-map uri uri)))))))
+ (rx/reduce conj {})
+ (rx/subs
+ (fn [result]
+ (reset! images-data* result)))))
[:div.element-options
[:div.code-block
- [:div.code-row-lang "CSS"
-
+ [:div.code-row-lang
+ [:& select {:default-value style-type
+ :class "custom-select"
+ :options [{:label "CSS" :value "css"}]
+ :on-change set-style}]
[:button.expand-button
{:on-click on-expand}
i/full-screen]
@@ -109,19 +197,31 @@
[:& copy-button {:data style-code
:on-copied on-style-copied}]]
- [:div.code-row-display
- [:& code-block {:type @style-type
- :code style-code}]]]
+ [:div.code-row-display {:style #js {"--code-height" (str (or code-size 400) "px")}}
+ [:& code-block {:type style-type
+ :code style-code}]]
+
+ [:div.resize-area {:on-pointer-down on-code-pointer-down
+ :on-lost-pointer-capture on-code-lost-pointer-capture
+ :on-pointer-move on-code-pointer-move}]]
+
[:div.code-block
- [:div.code-row-lang "SVG"
+ [:div.code-row-lang
+ [:& select {:default-value markup-type
+ :class "input-option"
+ :options [{:label "HTML" :value "html"}
+ {:label "SVG" :value "svg"}]
+ :on-change set-markup}]
[:button.expand-button
{:on-click on-expand}
i/full-screen]
- [:& copy-button {:data markup-code
+ [:& copy-button {:data (replace-map markup-code images-data)
:on-copied on-markup-copied}]]
+
+
[:div.code-row-display
- [:& code-block {:type @markup-type
+ [:& code-block {:type markup-type
:code markup-code}]]]]))
diff --git a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs
index e3d03bc3f..2cceadd34 100644
--- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs
+++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.cljs
@@ -6,9 +6,7 @@
(ns app.main.ui.viewer.inspect.right-sidebar
(:require
- [app.main.data.workspace :as dw]
[app.main.refs :as refs]
- [app.main.store :as st]
[app.main.ui.components.shape-icon :as si]
[app.main.ui.components.tabs-container :refer [tabs-container tabs-element]]
[app.main.ui.icons :as i]
@@ -38,10 +36,9 @@
:data local})))))
(mf/defc right-sidebar
- [{:keys [frame page file selected shapes page-id file-id share-id from]
+ [{:keys [frame page file selected shapes page-id file-id share-id from on-change-section on-expand]
:or {from :inspect}}]
- (let [expanded (mf/use-state false)
- section (mf/use-state :info #_:code)
+ (let [section (mf/use-state :info #_:code)
shapes (or shapes
(resolve-shapes (:objects page) selected))
@@ -49,9 +46,29 @@
page-id (or page-id (:id page))
file-id (or file-id (:id file))
- libraries (get-libraries from)]
+ libraries (get-libraries from)
- [:aside.settings-bar.settings-bar-right {:class (when @expanded "expanded")}
+ handle-change-tab
+ (mf/use-callback
+ (mf/deps from on-change-section)
+ (fn [new-section]
+ (reset! section new-section)
+ (when on-change-section
+ (on-change-section new-section))))
+
+ handle-expand
+ (mf/use-callback
+ (mf/deps on-expand)
+ (fn []
+ (when on-expand (on-expand))))]
+
+ (mf/use-effect
+ (mf/deps shapes handle-change-tab)
+ (fn []
+ (when-not (seq shapes)
+ (handle-change-tab :info))))
+
+ [:aside.settings-bar.settings-bar-right
[:div.settings-bar-inside
(if (seq shapes)
[:div.tool-window
@@ -77,12 +94,8 @@
;; inspect.tabs.code.selected.text
[:span.tool-window-bar-title (:name first-shape)]])]
[:div.tool-window-content.inspect
- [:& tabs-container {:on-change-tab #(do
- (reset! expanded false)
- (reset! section %)
- (when (= from :workspace)
- (st/emit! (dw/set-inspect-expanded false))))
- :selected @section}
+ [:& tabs-container {:on-change-tab handle-change-tab
+ :selected @section}
[:& tabs-element {:id :info :title (tr "inspect.tabs.info")}
[:& attributes {:page-id page-id
:file-id file-id
@@ -95,10 +108,7 @@
[:& tabs-element {:id :code :title (tr "inspect.tabs.code")}
[:& code {:frame frame
:shapes shapes
- :on-expand (fn []
- (when (= from :workspace)
- (st/emit! (dw/set-inspect-expanded (not @expanded))))
- (swap! expanded not))
+ :on-expand handle-expand
:from from}]]]]]
[:div.empty
[:span.tool-window-bar-icon i/code]
diff --git a/frontend/src/app/main/ui/workspace/sidebar.cljs b/frontend/src/app/main/ui/workspace/sidebar.cljs
index 870487b71..f4ed014a7 100644
--- a/frontend/src/app/main/ui/workspace/sidebar.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar.cljs
@@ -26,6 +26,7 @@
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap]]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
+ [app.util.object :as obj]
[rumext.v2 :as mf]))
;; --- Left Sidebar (Component)
@@ -134,16 +135,45 @@
is-history? (contains? layout :document-history)
is-inspect? (= section :inspect)
- expanded? (mf/deref refs/inspect-expanded)
+ ;;expanded? (mf/deref refs/inspect-expanded)
+ ;;prev-expanded? (hooks/use-previous expanded?)
+
+ current-section* (mf/use-state :info)
+ current-section (deref current-section*)
+
can-be-expanded? (and (not is-comments?)
(not is-history?)
- is-inspect?)]
+ is-inspect?
+ (= current-section :code))
- (mf/with-effect [can-be-expanded?]
- (when (not can-be-expanded?)
- (st/emit! (dw/set-inspect-expanded false))))
+ {:keys [on-pointer-down on-lost-pointer-capture on-pointer-move set-size size]}
+ (use-resize-hook :code 256 256 768 :x true :right)
- [:aside.settings-bar.settings-bar-right {:class (when (and can-be-expanded? expanded?) "expanded")}
+ handle-change-section
+ (mf/use-callback
+ (fn [section]
+ (reset! current-section* section)))
+
+ handle-expand
+ (mf/use-callback
+ (mf/deps size)
+ (fn []
+ (set-size (if (> size 256) 256 768))))
+
+ props
+ (-> props
+ (obj/clone)
+ (obj/set! "on-change-section" handle-change-section)
+ (obj/set! "on-expand" handle-expand))]
+
+ [:aside.settings-bar.settings-bar-right
+ {:class (when (not can-be-expanded?) "not-expand")
+ :style #js {"--width" (when can-be-expanded? (dm/str size "px"))}}
+ (when can-be-expanded?
+ [:div.resize-area
+ {:on-pointer-down on-pointer-down
+ :on-lost-pointer-capture on-lost-pointer-capture
+ :on-pointer-move on-pointer-move}])
[:div.settings-bar-inside
(cond
(true? is-comments?)
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs
index 6151dade2..19d98b8ac 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs
@@ -67,7 +67,7 @@
(mf/defc options-content
{::mf/wrap [mf/memo]}
- [{:keys [selected section shapes shapes-with-children page-id file-id]}]
+ [{:keys [selected section shapes shapes-with-children page-id file-id on-change-section on-expand]}]
(let [drawing (mf/deref refs/workspace-drawing)
objects (mf/deref refs/workspace-page-objects)
shared-libs (mf/deref refs/workspace-libraries)
@@ -83,9 +83,8 @@
on-change-tab
(fn [options-mode]
- (st/emit! (udw/set-options-mode options-mode)
- (udw/set-inspect-expanded false))
- (if (= options-mode :inspect) ;;TODO maybe move this logic to set-options-mode
+ (st/emit! (udw/set-options-mode options-mode))
+ (if (= options-mode :inspect)
(st/emit! :interrupt (udw/set-workspace-read-only true))
(st/emit! :interrupt (udw/set-workspace-read-only false))))]
@@ -94,7 +93,7 @@
[:& tabs-container {:on-change-tab on-change-tab
:selected section}
[:& tabs-element {:id :design
- :title (tr "workspace.options.design")}
+ :title (tr "workspace.options.design")}
[:div.element-options
[:& align-options]
[:& bool-options]
@@ -143,13 +142,16 @@
[:& interactions-menu {:shape (first shapes)}]]]
[:& tabs-element {:id :inspect
- :title (tr "workspace.options.inspect")}
- [:div.element-options
- [:& hrs/right-sidebar {:page-id page-id
- :file-id file-id
- :frame shape-parent-frame
- :shapes selected-shapes
- :from :workspace}]]]]]]))
+ :title (tr "workspace.options.inspect")}
+
+ [:div.element-options.element-options-inspect
+ [:& hrs/right-sidebar {:page-id page-id
+ :file-id file-id
+ :frame shape-parent-frame
+ :shapes selected-shapes
+ :on-change-section on-change-section
+ :on-expand on-expand
+ :from :workspace}]]]]]]))
;; TODO: this need optimizations, selected-objects and
;; selected-objects-with-children are derefed always but they only
@@ -161,6 +163,8 @@
[props]
(let [section (obj/get props "section")
selected (obj/get props "selected")
+ on-change-section (obj/get props "on-change-section")
+ on-expand (obj/get props "on-expand")
page-id (mf/use-ctx ctx/current-page-id)
file-id (mf/use-ctx ctx/current-file-id)
shapes (mf/deref refs/selected-objects)
@@ -171,4 +175,6 @@
:shapes-with-children shapes-with-children
:file-id file-id
:page-id page-id
- :section section}]))
+ :section section
+ :on-change-section on-change-section
+ :on-expand on-expand}]))
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
index 9cb756546..16eaaa871 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
@@ -125,6 +125,7 @@
:justify-items
(if is-col?
(case val
+ :stretch i/grid-justify-content-column-around
:start i/grid-justify-content-column-start
:end i/grid-justify-content-column-end
:center i/grid-justify-content-column-center
@@ -133,6 +134,7 @@
:space-evenly i/grid-justify-content-column-between)
(case val
+ :stretch i/grid-justify-content-column-around
:start i/grid-justify-content-row-start
:end i/grid-justify-content-row-end
:center i/grid-justify-content-row-center
@@ -407,7 +409,7 @@
[{:keys [is-col? justify-items set-justify] :as props}]
(let [type (if is-col? :column :row)]
[:div.justify-content-style
- (for [align [:start :center :end :space-around :space-between :space-evenly]]
+ (for [align [:stretch :start :center :end :space-around :space-between]]
[:button.align-start.tooltip
{:class (dom/classnames :active (= justify-items align)
:tooltip-bottom-left (not= align :start)
@@ -748,7 +750,7 @@
align-items-row (:layout-align-items values)
align-items-column (:layout-justify-items values)
- set-align-grid
+ set-items-grid
(fn [value type]
(if (= type :row)
(st/emit! (dwsl/update-layout ids {:layout-align-items value}))
@@ -758,7 +760,7 @@
grid-justify-content-row (:layout-align-content values)
grid-justify-content-column (:layout-justify-content values)
- set-justify-grid
+ set-content-grid
(mf/use-callback
(mf/deps ids)
(fn [value type]
@@ -833,25 +835,25 @@
[:& grid-edit-mode {:id (first ids)}]])]]
[:div.layout-row
- [:div.align-items-grid.row-title "Align"]
+ [:div.align-items-grid.row-title "Items"]
[:div.btn-wrapper.align-grid
[:& align-grid-row {:is-col? false
:align-items align-items-row
- :set-align set-align-grid}]
+ :set-align set-items-grid}]
[:& align-grid-row {:is-col? true
:align-items align-items-column
- :set-align set-align-grid}]]]
+ :set-align set-items-grid}]]]
[:div.layout-row
- [:div.jusfiy-content-grid.row-title "Justify"]
+ [:div.jusfiy-content-grid.row-title "Content"]
[:div.btn-wrapper.align-grid
[:& justify-grid-row {:is-col? true
:justify-items grid-justify-content-column
- :set-justify set-justify-grid}]
+ :set-justify set-content-grid}]
[:& justify-grid-row {:is-col? false
:justify-items grid-justify-content-row
- :set-justify set-justify-grid}]]]
+ :set-justify set-content-grid}]]]
[:& grid-columns-row {:is-col? true
:expanded? @grid-columns-open?
:toggle toggle-columns-info
diff --git a/frontend/src/app/util/code_gen.cljs b/frontend/src/app/util/code_gen.cljs
index f3cf5eea4..24d680320 100644
--- a/frontend/src/app/util/code_gen.cljs
+++ b/frontend/src/app/util/code_gen.cljs
@@ -6,14 +6,19 @@
(ns app.util.code-gen
(:require
+ ["react-dom/server" :as rds]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph]
[app.common.text :as txt]
[app.common.types.shape.layout :as ctl]
+ [app.config :as cfg]
+ [app.main.render :as render]
[app.main.ui.formats :as fmt]
+ [app.main.ui.shapes.text.html-text :as text]
[app.util.color :as uc]
- [cuerdas.core :as str]))
+ [cuerdas.core :as str]
+ [rumext.v2 :as mf]))
(defn shadow->css [shadow]
(let [{:keys [style offset-x offset-y blur spread]} shadow
@@ -24,9 +29,14 @@
(defn fill-color->background
[fill]
- (uc/color->background {:color (:fill-color fill)
- :opacity (:fill-opacity fill)
- :gradient (:fill-color-gradient fill)}))
+ (cond
+ (not= (:fill-opacity fill) 1)
+ (uc/color->background {:color (:fill-color fill)
+ :opacity (:fill-opacity fill)
+ :gradient (:fill-color-gradient fill)})
+
+ :else
+ (str/upper (:fill-color fill))))
(defn format-fill-color [_ shape]
(let [fills (:fills shape)
@@ -41,7 +51,7 @@
[(fill-color->background first-fill)])]
(str/join ", " colors)))
-(defn format-stroke [_ shape]
+(defn format-stroke [shape]
(let [first-stroke (first (:strokes shape))
width (:stroke-width first-stroke)
style (let [style (:stroke-style first-stroke)]
@@ -52,16 +62,32 @@
(when-not (= :none (:stroke-style first-stroke))
(str/format "%spx %s %s" width style (uc/color->background color)))))
-(defn format-position [_ shape]
- (let [relative? (cph/frame-shape? shape)
- absolute? (or (empty? (:flex-items shape))
- (and (ctl/any-layout? (:parent shape)) (ctl/layout-absolute? shape)))]
+(defn format-position [objects]
+ (fn [_ shape]
(cond
- absolute? "absolute"
- relative? "relative"
+ (and (ctl/any-layout-immediate-child? objects shape)
+ (not (ctl/layout-absolute? shape))
+ (or (cph/group-shape? shape)
+ (cph/frame-shape? shape)))
+ "relative"
- ;; static is default value in css
- :else nil)))
+ (and (ctl/any-layout-immediate-child? objects shape)
+ (not (ctl/layout-absolute? shape)))
+ nil
+
+ :else
+ "absolute")))
+
+(defn mk-grid-coord
+ [objects prop span-prop]
+
+ (fn [_ shape]
+ (when (ctl/grid-layout-immediate-child? objects shape)
+ (let [parent (get objects (:parent-id shape))
+ cell (ctl/get-cell-by-shape-id parent (:id shape))]
+ (if (> (get cell span-prop) 1)
+ (dm/str (get cell prop) " / " (+ (get cell prop) (get cell span-prop)))
+ (get cell prop))))))
(defn get-size
[type values]
@@ -75,14 +101,35 @@
(fmt/format-size :width value values)
(fmt/format-size :heigth value values))))
+(defn make-format-absolute-pos
+ [objects shape coord]
+ (fn [value]
+ (let [parent-id (dm/get-in objects [(:id shape) :parent-id])
+ parent-value (dm/get-in objects [parent-id :selrect coord])]
+ (when-not (or (cph/root-frame? shape)
+ (ctl/any-layout-immediate-child? objects shape)
+ (ctl/layout-absolute? shape))
+ (fmt/format-pixels (- value parent-value))))))
+
+(defn format-tracks
+ [tracks]
+ (str/join
+ " "
+ (->> tracks (map (fn [{:keys [type value]}]
+ (case type
+ :flex (dm/str (fmt/format-number value) "fr")
+ :percent (fmt/format-percent (/ value 100))
+ :auto "auto"
+ (fmt/format-pixels value)))))))
+
(defn styles-data
- [shape]
+ [objects shape]
{:position {:props [:type]
:to-prop {:type "position"}
- :format {:type format-position}}
+ :format {:type (format-position objects)}}
:layout {:props (if (or (empty? (:flex-items shape))
(ctl/layout-absolute? shape))
- [:width :height :x :y :radius :rx :r1]
+ [:x :y :width :height :radius :rx :r1]
[:width :height :radius :rx :r1])
:to-prop {:x "left"
:y "top"
@@ -92,30 +139,60 @@
:format {:rotation #(str/fmt "rotate(%sdeg)" %)
:r1 #(apply str/fmt "%spx %spx %spx %spx" %)
:width #(get-size :width %)
- :height #(get-size :height %)}
+ :height #(get-size :height %)
+ :x (make-format-absolute-pos objects shape :x)
+ :y (make-format-absolute-pos objects shape :y)}
:multi {:r1 [:r1 :r2 :r3 :r4]}}
:fill {:props [:fills]
- :to-prop {:fills (if (> (count (:fills shape)) 1) "background-image" "background-color")}
+ :to-prop {:fills (cond
+ (or (cph/path-shape? shape)
+ (cph/mask-shape? shape)
+ (cph/bool-shape? shape)
+ (cph/svg-raw-shape? shape)
+ (some? (:svg-attrs shape)))
+ nil
+
+ (> (count (:fills shape)) 1)
+ "background-image"
+
+ (and (= (count (:fills shape)) 1)
+ (some? (:fill-color-gradient (first (:fills shape)))))
+ "background"
+
+ :else
+ "background-color")}
:format {:fills format-fill-color}}
:stroke {:props [:strokes]
:to-prop {:strokes "border"}
- :format {:strokes format-stroke}}
+ :format {:strokes (fn [_ shape]
+ (when-not (or (cph/path-shape? shape)
+ (cph/mask-shape? shape)
+ (cph/bool-shape? shape)
+ (cph/svg-raw-shape? shape)
+ (some? (:svg-attrs shape)))
+ (format-stroke shape)))}}
:shadow {:props [:shadow]
:to-prop {:shadow :box-shadow}
:format {:shadow #(str/join ", " (map shadow->css %1))}}
:blur {:props [:blur]
:to-prop {:blur "filter"}
:format {:blur #(str/fmt "blur(%spx)" (:value %))}}
+
:layout-flex {:props [:layout
:layout-flex-dir
:layout-align-items
+ :layout-justify-items
+ :layout-align-content
:layout-justify-content
:layout-gap
:layout-padding
:layout-wrap-type]
+ :gen-props [:flex-shrink]
:to-prop {:layout "display"
:layout-flex-dir "flex-direction"
:layout-align-items "align-items"
+ :layout-align-content "align-content"
+ :layout-justify-items "justify-items"
:layout-justify-content "justify-content"
:layout-wrap-type "flex-wrap"
:layout-gap "gap"
@@ -123,10 +200,24 @@
:format {:layout d/name
:layout-flex-dir d/name
:layout-align-items d/name
+ :layout-align-content d/name
+ :layout-justify-items d/name
:layout-justify-content d/name
:layout-wrap-type d/name
:layout-gap fmt/format-gap
- :layout-padding fmt/format-padding}}})
+ :layout-padding fmt/format-padding
+ :flex-shrink (fn [_ shape] (when (ctl/flex-layout-immediate-child? objects shape) 0))}}
+
+ :layout-grid {:props [:layout-grid-rows
+ :layout-grid-columns]
+ :gen-props [:grid-column
+ :grid-row]
+ :to-prop {:layout-grid-rows "grid-template-rows"
+ :layout-grid-columns "grid-template-columns"}
+ :format {:layout-grid-rows format-tracks
+ :layout-grid-columns format-tracks
+ :grid-column (mk-grid-coord objects :column :column-span)
+ :grid-row (mk-grid-coord objects :row :row-span)}}})
(def style-text
{:props [:fills
@@ -190,9 +281,12 @@
(defn generate-css-props
([values properties]
- (generate-css-props values properties nil))
+ (generate-css-props values properties [] nil))
- ([values properties params]
+ ([values properties gen-properties]
+ (generate-css-props values properties gen-properties nil))
+
+ ([values properties gen-properties params]
(let [{:keys [to-prop format tab-size multi]
:or {to-prop {} tab-size 0 multi {}}} params
@@ -210,7 +304,7 @@
to-prop)
get-value (fn [prop]
- (if-let [props (prop multi)]
+ (if-let [props (get multi prop)]
(map #(get values %) props)
(get-specific-value values prop)))
@@ -220,30 +314,35 @@
(or (nil? value) (= value 0))))
default-format (fn [value] (dm/str (fmt/format-pixels value)))
- format-property (fn [prop]
- (let [css-prop (or (prop to-prop) (d/name prop))
- format-fn (or (prop format) default-format)
- css-val (format-fn (get-value prop) values)]
- (when css-val
- (dm/str
- (str/repeat " " tab-size)
- (str/fmt "%s: %s;" css-prop css-val)))))]
- (->> properties
- (remove #(null? (get-value %)))
- (map format-property)
- (filter (comp not nil?))
+ format-property
+ (fn [prop]
+ (let [css-prop (or (get to-prop prop) (d/name prop))
+ format-fn (or (get format prop) default-format)
+ css-val (format-fn (get-value prop) values)]
+ (when (and css-val (not= css-val ""))
+ (dm/str
+ (str/repeat " " tab-size)
+ (dm/fmt "%: %;" css-prop css-val)))))]
+
+ (->> (concat
+ (->> properties
+ (remove #(null? (get-value %))))
+ gen-properties)
+ (keep format-property)
(str/join "\n")))))
-(defn shape->properties [shape]
- (let [;; This property is added in an earlier step (code.cljs),
+(defn shape->properties [objects shape]
+ (let [;; This property is added in an earlier step (code.cljs),
;; it will come with a vector of flex-items if any.
- ;; If there are none it will continue as usual.
+ ;; If there are none it will continue as usual.
flex-items (:flex-items shape)
- props (->> (styles-data shape) vals (mapcat :props))
- to-prop (->> (styles-data shape) vals (map :to-prop) (reduce merge))
- format (->> (styles-data shape) vals (map :format) (reduce merge))
- multi (->> (styles-data shape) vals (map :multi) (reduce merge))
+ props (->> (styles-data objects shape) vals (mapcat :props))
+ to-prop (->> (styles-data objects shape) vals (map :to-prop) (reduce merge))
+ format (->> (styles-data objects shape) vals (map :format) (reduce merge))
+ multi (->> (styles-data objects shape) vals (map :multi) (reduce merge))
+ gen-props (->> (styles-data objects shape) vals (mapcat :gen-props))
+
props (cond-> props
(seq flex-items) (concat (:props layout-flex-item-params))
(= :wrap (:layout-wrap-type shape)) (concat (:props layout-align-content)))
@@ -253,10 +352,14 @@
format (cond-> format
(seq flex-items) (merge (:format layout-flex-item-params))
(= :wrap (:layout-wrap-type shape)) (merge (:format layout-align-content)))]
- (generate-css-props shape props {:to-prop to-prop
- :format format
- :multi multi
- :tab-size 2})))
+ (generate-css-props
+ shape
+ props
+ gen-props
+ {:to-prop to-prop
+ :format format
+ :multi multi
+ :tab-size 2})))
(defn search-text-attrs
[node attrs]
@@ -269,36 +372,36 @@
(defn parse-style-text-blocks
[node attrs]
(letfn
- [(rec-style-text-map [acc node style]
- (let [node-style (merge style (select-keys node attrs))
- head (or (-> acc first) [{} ""])
- [head-style head-text] head
+ [(rec-style-text-map [acc node style]
+ (let [node-style (merge style (select-keys node attrs))
+ head (or (-> acc first) [{} ""])
+ [head-style head-text] head
- new-acc
- (cond
- (:children node)
- (reduce #(rec-style-text-map %1 %2 node-style) acc (:children node))
+ new-acc
+ (cond
+ (:children node)
+ (reduce #(rec-style-text-map %1 %2 node-style) acc (:children node))
- (not= head-style node-style)
- (cons [node-style (:text node "")] acc)
+ (not= head-style node-style)
+ (cons [node-style (:text node "")] acc)
- :else
- (cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
+ :else
+ (cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
;; We add an end-of-line when finish a paragraph
- new-acc
- (if (= (:type node) "paragraph")
- (let [[hs ht] (first new-acc)]
- (cons [hs (dm/str ht "\n")] (rest new-acc)))
- new-acc)]
- new-acc))]
+ new-acc
+ (if (= (:type node) "paragraph")
+ (let [[hs ht] (first new-acc)]
+ (cons [hs (dm/str ht "\n")] (rest new-acc)))
+ new-acc)]
+ new-acc))]
(-> (rec-style-text-map [] node {})
reverse)))
-(defn text->properties [shape]
+(defn text->properties [objects shape]
(let [flex-items (:flex-items shape)
- text-shape-style (select-keys (styles-data shape) [:layout :shadow :blur])
+ text-shape-style (d/without-keys (styles-data objects shape) [:fill :stroke])
shape-props (->> text-shape-style vals (mapcat :props))
shape-to-prop (->> text-shape-style vals (map :to-prop) (reduce merge))
@@ -315,6 +418,7 @@
(:content shape)
(conj (:props style-text) :fill-color-gradient :fill-opacity))
(d/merge txt/default-text-attrs))]
+
(str/join
"\n"
[(generate-css-props shape
@@ -328,21 +432,128 @@
:format (:format style-text)
:tab-size 2})])))
-(defn generate-css [shape]
- (let [name (:name shape)
- properties (if (= :text (:type shape))
- (text->properties shape)
- (shape->properties shape))
- selector (str/css-selector name)
+(defn selector-name [shape]
+ (let [
+ name (-> (:name shape)
+ #_(subs 0 (min 10 (count (:name shape)))))
+ ;; selectors cannot start with numbers
+ name (if (re-matches #"^\d.*" name) (dm/str "c-" name) name)
+ id (-> (dm/str (:id shape))
+ #_(subs 24 36))
+ selector (str/css-selector (dm/str name " " id))
selector (if (str/starts-with? selector "-") (subs selector 1) selector)]
+ selector))
+
+(defn generate-css [objects shape]
+ (let [name (:name shape)
+ properties (shape->properties objects shape)
+ selector (selector-name shape)]
(str/join "\n" [(str/fmt "/* %s */" name)
(str/fmt ".%s {" selector)
properties
"}"])))
-(defn generate-style-code [type shapes]
+(defn generate-svg
+ [objects shape-id]
+ (let [shape (get objects shape-id)]
+ (rds/renderToStaticMarkup
+ (mf/element
+ render/object-svg
+ #js {:objects objects
+ :object-id (-> shape :id)}))))
+
+(defn generate-html
+ ([objects shape-id]
+ (generate-html objects shape-id 0))
+
+ ([objects shape-id level]
+ (let [shape (get objects shape-id)
+ indent (str/repeat " " level)
+ maybe-reverse (if (ctl/any-layout? shape) reverse identity)]
+ (cond
+ (cph/text-shape? shape)
+ (let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape #js {:shape shape :code? true}))]
+ (dm/fmt "%
\n%\n%
"
+ indent
+ (selector-name shape)
+ text-shape-html
+ indent))
+
+ (cph/image-shape? shape)
+ (let [data (or (:metadata shape) (:fill-image shape))
+ image-url (cfg/resolve-file-media data)]
+ (dm/fmt "%
\n%"
+ indent
+ image-url
+ (selector-name shape)
+ indent))
+
+ (or (cph/path-shape? shape)
+ (cph/mask-shape? shape)
+ (cph/bool-shape? shape)
+ (cph/svg-raw-shape? shape)
+ (some? (:svg-attrs shape)))
+ (let [svg-markup (rds/renderToStaticMarkup (mf/element render/object-svg #js {:objects objects :object-id (:id shape) :render-embed? false}))]
+ (dm/fmt "%\n%\n%
"
+ indent
+ (selector-name shape)
+ svg-markup
+ indent))
+
+ (empty? (:shapes shape))
+ (dm/fmt "%\n%
"
+ indent
+ (selector-name shape)
+ indent)
+
+ :else
+ (dm/fmt "%\n%\n%
"
+ indent
+ (selector-name shape)
+ (->> (:shapes shape)
+ (maybe-reverse)
+ (map #(generate-html objects % (inc level)))
+ (str/join "\n"))
+ indent)))))
+
+(defn generate-markup-code [objects type shapes]
+ (let [generate-markup-fn (case type
+ "html" generate-html
+ "svg" generate-svg)]
+ (->> shapes
+ (map #(generate-markup-fn objects % 0))
+ (str/join "\n"))))
+
+(defn generate-style-code [objects type shapes]
(let [generate-style-fn (case type
"css" generate-css)]
- (->> shapes
- (map generate-style-fn)
- (str/join "\n\n"))))
+ (dm/str
+ "html, body {
+ background-color: #E8E9EA;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ width: 100%;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 2rem;
+}
+
+svg {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
+
+* {
+ box-sizing: border-box;
+}
+\n"
+ (->> shapes
+ (map (partial generate-style-fn objects))
+ (str/join "\n\n")))))