diff --git a/frontend/src/app/main/data/preview.cljs b/frontend/src/app/main/data/preview.cljs index d00daefb9..d50e0a5e9 100644 --- a/frontend/src/app/main/data/preview.cljs +++ b/frontend/src/app/main/data/preview.cljs @@ -48,11 +48,11 @@ (beautify/html #js {"indent_size" 2}))) (defn update-preview-window - [preview code] + [preview code width height] (when preview (if (aget preview "load") - (.load preview code) - (ts/schedule #(update-preview-window preview code))))) + (.load preview code width height) + (ts/schedule #(update-preview-window preview code width height))))) (defn shapes->fonts [shapes] @@ -92,7 +92,11 @@ (-> (cg/generate-markup-code objects markup-type [shape]) (format-code markup-type))] - (update-preview-window preview (str/format page-template style-code markup-code)))))))))) + (update-preview-window + preview + (str/format page-template style-code markup-code) + (-> shape :selrect :width) + (-> shape :selrect :height)))))))))) (defn open-preview-selected [] diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 77106939e..dbacccfdd 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -8,6 +8,7 @@ (:require [app.main.data.events :as ev] [app.main.data.exports :as de] + [app.main.data.preview :as dp] [app.main.data.shortcuts :as ds] [app.main.data.workspace :as dw] [app.main.data.workspace.colors :as mdc] @@ -539,8 +540,12 @@ :bool-exclude {:tooltip (ds/meta (ds/alt "E")) :command (ds/c-mod "alt+e") :subsections [:shape] - :fn #(emit-when-no-readonly (dw/create-bool :exclude))}} - ) + :fn #(emit-when-no-readonly (dw/create-bool :exclude))} + + ;; PREVIEW + :preview-frame {:tooltip (ds/meta (ds/alt ds/enter)) + :command (ds/c-mod "alt+enter") + :fn #(emit-when-no-readonly (dp/open-preview-selected))}}) (def opacity-shortcuts (into {} (->> diff --git a/frontend/src/app/main/ui/frame_preview.cljs b/frontend/src/app/main/ui/frame_preview.cljs index 9188edb51..805b73a1e 100644 --- a/frontend/src/app/main/ui/frame_preview.cljs +++ b/frontend/src/app/main/ui/frame_preview.cljs @@ -24,11 +24,13 @@ handle-load (mf/use-callback - (fn [data] + (fn [data width height] (prn "handle-load" data) (reset! last-data* data) (let [iframe-dom (mf/ref-val iframe-ref)] (when iframe-dom + (-> iframe-dom (aset "width" width)) + (-> iframe-dom (aset "height" height)) (-> iframe-dom .-contentWindow .-document .open) (-> iframe-dom .-contentWindow .-document (.write data)) (-> iframe-dom .-contentWindow .-document .close))))) @@ -65,11 +67,5 @@ [:iframe {:ref load-ref :frameborder "0" :scrolling "no" - :style {:width (str (* 100 (if (> zoom 1) - (* 1 zoom) - (/ 1 zoom))) "%") - :height (str (* 100 (if (> zoom 1) - (* 1 zoom) - (/ 1 zoom))) "%") - :transform-origin "left top" + :style {:transform-origin "top center" :transform (str "scale(" zoom ")")}}]]])) diff --git a/frontend/src/app/util/code_gen/common.cljs b/frontend/src/app/util/code_gen/common.cljs index aa4719c62..5abbe40fb 100644 --- a/frontend/src/app/util/code_gen/common.cljs +++ b/frontend/src/app/util/code_gen/common.cljs @@ -6,7 +6,11 @@ (ns app.util.code-gen.common (:require + [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.geom.matrix :as gmt] + [app.common.pages.helpers :as cph] + [app.common.types.shape.layout :as ctl] [cuerdas.core :as str])) (defn shape->selector @@ -23,3 +27,36 @@ selector (if (str/starts-with? selector "-") (subs selector 1) selector)] selector) "")) + +(defn svg-markup? + "Function to determine whether a shape is rendered in HTML+CSS or is rendered + through a SVG" + [shape] + (or + ;; path and path-like shapes + (cph/path-shape? shape) + (cph/bool-shape? shape) + + ;; imported SVG images + (cph/svg-raw-shape? shape) + (some? (:svg-attrs shape)) + + ;; CSS masks are not enough we need to delegate to SVG + (cph/mask-shape? shape) + + ;; Texts with shadows or strokes we render in SVG + (and (cph/text-shape? shape) + (or (d/not-empty? (:shadow shape)) + (d/not-empty? (:strokes shape)))) + + ;; When a shape has several strokes or the stroke is not a "border" + (or (> (count (:strokes shape)) 1) + (and (= (count (:strokes shape)) 1) + (not= (-> shape :strokes first :stroke-alignment) :inner))))) + +(defn has-wrapper? + [objects shape] + ;; Layout children with a transform should be wrapped + (and (ctl/any-layout-immediate-child? objects shape) + (not (gmt/unit? (:transform shape))))) + diff --git a/frontend/src/app/util/code_gen/markup_html.cljs b/frontend/src/app/util/code_gen/markup_html.cljs index 4c5fdf0c5..3229bf70a 100644 --- a/frontend/src/app/util/code_gen/markup_html.cljs +++ b/frontend/src/app/util/code_gen/markup_html.cljs @@ -18,32 +18,6 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) -(defn svg-markup? - "Function to determine whether a shape is rendered in HTML+CSS or is rendered - through a SVG" - [shape] - (or - ;; path and path-like shapes - (cph/path-shape? shape) - (cph/bool-shape? shape) - - ;; imported SVG images - (cph/svg-raw-shape? shape) - (some? (:svg-attrs shape)) - - ;; CSS masks are not enough we need to delegate to SVG - (cph/mask-shape? shape) - - ;; Texts with shadows or strokes we render in SVG - (and (cph/text-shape? shape) - (or (d/not-empty? (:shadow shape)) - (d/not-empty? (:strokes shape)))) - - ;; When a shape has several strokes or the stroke is not a "border" - (or (> (count (:strokes shape)) 1) - (and (= (count (:strokes shape)) 1) - (not= (-> shape :strokes first :stroke-alignment) :inner))))) - (defn generate-html ([objects shape] (generate-html objects shape 0)) @@ -54,7 +28,7 @@ shape-html (cond - (svg-markup? shape) + (cgc/svg-markup? shape) (let [svg-markup (generate-svg objects shape)] (dm/fmt "%
\n%\n%
" indent @@ -98,7 +72,15 @@ (maybe-reverse) (map #(generate-html objects (get objects %) (inc level))) (str/join "\n")) - indent))] + indent)) + + shape-html + (if (cgc/has-wrapper? objects shape) + (dm/fmt "
%
" + (dm/str (cgc/shape->selector shape) "-wrapper") + shape-html) + + shape-html)] (dm/fmt "%\n%" indent (dm/str (d/name (:type shape)) ": " (:name shape)) shape-html)))) (defn generate-markup diff --git a/frontend/src/app/util/code_gen/style_css.cljs b/frontend/src/app/util/code_gen/style_css.cljs index cabbda805..ed70144d1 100644 --- a/frontend/src/app/util/code_gen/style_css.cljs +++ b/frontend/src/app/util/code_gen/style_css.cljs @@ -8,8 +8,11 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.geom.matrix :as gmt] + [app.common.geom.shapes :as gsh] [app.common.pages.helpers :as cph] [app.common.text :as txt] + [app.common.types.shape.layout :as ctl] [app.main.ui.shapes.text.styles :as sts] [app.util.code-gen.common :as cgc] [app.util.code-gen.style-css-formats :refer [format-value]] @@ -50,6 +53,19 @@ svg { ") +(def shape-wrapper-css-properties + #{:flex-shrink + :margin + :max-height + :min-height + :max-width + :min-width + :align-self + :justify-self + :grid-column + :grid-row + :z-index}) + (def shape-css-properties [:position :left @@ -82,6 +98,7 @@ svg { ;; Flex related properties :flex-direction :flex-wrap + :flex ;; Grid related properties :grid-template-rows @@ -118,6 +135,24 @@ svg { (when-let [value (get-value property shape objects)] [property value])) +(defn shape->wrapper-css-properties + [shape objects] + (when (and (ctl/any-layout-immediate-child? objects shape) + (not (gmt/unit? (:transform shape)))) + (let [{:keys [width height]} (gsh/shapes->rect [shape])] + (cond-> [[:position "relative"] + [:width width] + [:height height]] + (ctl/flex-layout-immediate-child? objects shape) + (conj [:flex-shrink 0]))))) + +(defn shape->wrapper-child-css-properties + [shape objects] + (when (and (ctl/any-layout-immediate-child? objects shape) (not (gmt/unit? (:transform shape)))) + [[:position "absolute"] + [:left "50%"] + [:top "50%"]])) + (defn shape->css-properties "Given a shape extract the CSS properties in the format of list [property value]" [shape objects properties] @@ -143,9 +178,10 @@ svg { (defn format-css-properties "Format a list of [property value] into a list of css properties in the format 'property: value;'" [properties options] - (->> properties - (map #(dm/str " " (format-css-property % options))) - (str/join "\n"))) + (when properties + (->> properties + (map #(dm/str " " (format-css-property % options))) + (str/join "\n")))) (defn get-shape-properties-css ([objects shape properties] @@ -199,13 +235,40 @@ svg { (get-shape-css-selector shape objects nil)) ([shape objects options] - (let [properties (-> shape - (shape->css-properties objects shape-css-properties) - (format-css-properties options)) - selector (cgc/shape->selector shape)] - (str/join "\n" [(str/fmt "/* %s */" (:name shape)) + (let [selector (cgc/shape->selector shape) + + wrapper? (cgc/has-wrapper? objects shape) + + css-properties + (if wrapper? + (filter (complement shape-wrapper-css-properties) shape-css-properties) + shape-css-properties) + + properties + (-> shape + (shape->css-properties objects css-properties) + (format-css-properties options)) + + wrapper-properties + (when wrapper? + (-> (d/concat-vec + (shape->css-properties shape objects shape-wrapper-css-properties) + (shape->wrapper-css-properties shape objects)) + (format-css-properties options))) + + wrapper-child-properties + (when wrapper? + (-> shape + (shape->wrapper-child-css-properties objects) + (format-css-properties options)))] + + (str/join + "\n" + (filter some? [(str/fmt "/* %s */" (:name shape)) + (when wrapper? (str/fmt ".%s-wrapper {\n%s\n}" selector wrapper-properties)) + (when wrapper? (str/fmt ".%s-wrapper > * {\n%s\n}" selector wrapper-child-properties)) (str/fmt ".%s {\n%s\n}" selector properties) - (when (cph/text-shape? shape) (generate-text-css shape))])))) + (when (cph/text-shape? shape) (generate-text-css shape))]))))) (defn get-css-property ([objects shape property] diff --git a/frontend/src/app/util/code_gen/style_css_formats.cljs b/frontend/src/app/util/code_gen/style_css_formats.cljs index 83ce8580c..7717d9e51 100644 --- a/frontend/src/app/util/code_gen/style_css_formats.cljs +++ b/frontend/src/app/util/code_gen/style_css_formats.cljs @@ -33,7 +33,6 @@ :margin :size-array :grid-template-rows :tracks :grid-template-columns :tracks - :transform :matrix }) (defmulti format-value diff --git a/frontend/src/app/util/code_gen/style_css_values.cljs b/frontend/src/app/util/code_gen/style_css_values.cljs index 148adcb50..dbbb18b03 100644 --- a/frontend/src/app/util/code_gen/style_css_values.cljs +++ b/frontend/src/app/util/code_gen/style_css_values.cljs @@ -7,12 +7,14 @@ (ns app.util.code-gen.style-css-values (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.matrix :as gmt] [app.common.geom.shapes :as gsh] [app.common.pages.helpers :as cph] [app.common.types.shape.layout :as ctl] - [app.util.code-gen.markup-html :refer [svg-markup?]])) + [app.main.ui.formats :as fmt] + [app.util.code-gen.common :as cgc])) (defn fill->color [{:keys [fill-color fill-opacity fill-color-gradient]}] @@ -30,7 +32,7 @@ (not (ctl/layout-absolute? shape)) (or (cph/group-like-shape? shape) (cph/frame-shape? shape) - (svg-markup? shape))) + (cgc/svg-markup? shape))) (cph/root-frame? shape)) :relative @@ -66,14 +68,37 @@ [_ shape objects] (get-shape-position shape objects :y)) +(defmethod get-value :flex + [_ shape objects] + (let [parent (cph/get-parent objects (:id shape))] + (when (and (ctl/flex-layout-immediate-child? objects shape) + (or (and (contains? #{:row :reverse-row} (:layout-flex-dir parent)) + (= :fill (:layout-item-h-sizing shape))) + (and (contains? #{:column :column-row} (:layout-flex-dir parent)) + (= :fill (:layout-item-v-sizing shape))))) + 1))) + (defn get-shape-size [shape objects type] - (let [sizing (if (= type :width) + (let [parent (cph/get-parent objects (:id shape)) + sizing (if (= type :width) (:layout-item-h-sizing shape) (:layout-item-v-sizing shape))] (cond - (or (and (ctl/any-layout? shape) (= sizing :auto) (not (svg-markup? shape))) - (and (ctl/any-layout-immediate-child? objects shape) (= sizing :fill))) + (and (ctl/flex-layout-immediate-child? objects shape) + (or (and (= type :height) + (contains? #{:row :reverse-row} (:layout-flex-dir parent)) + (= :fill (:layout-item-v-sizing shape))) + (and (= type :width) + (contains? #{:column :column-row} (:layout-flex-dir parent)) + (= :fill (:layout-item-h-sizing shape))))) + :fill + + (and (ctl/flex-layout-immediate-child? objects shape) (= sizing :fill)) + nil + + (or (and (ctl/any-layout? shape) (= sizing :auto) (not (cgc/svg-markup? shape))) + (and (ctl/grid-layout-immediate-child? objects shape) (= sizing :fill))) sizing (some? (:selrect shape)) @@ -92,21 +117,25 @@ (defmethod get-value :transform [_ shape objects] - (when-not (svg-markup? shape) + (when-not (cgc/svg-markup? shape) (let [parent (get objects (:parent-id shape)) transform (gmt/multiply (:transform shape (gmt/matrix)) - (:transform-inverse parent (gmt/matrix)))] - (when-not (gmt/unit? transform) - transform)))) + (:transform-inverse parent (gmt/matrix))) + + transform-str (when-not (gmt/unit? transform) (fmt/format-matrix transform))] + + (if (cgc/has-wrapper? objects shape) + (dm/str "translate(-50%, -50%) " (d/nilv transform-str "")) + transform-str)))) (defmethod get-value :background [_ {:keys [fills] :as shape} _] (let [single-fill? (= (count fills) 1) ffill (first fills) gradient? (some? (:fill-color-gradient ffill))] - (when (and (not (svg-markup? shape)) (not (cph/group-shape? shape)) single-fill? gradient?) + (when (and (not (cgc/svg-markup? shape)) (not (cph/group-shape? shape)) single-fill? gradient?) (fill->color ffill)))) (defmethod get-value :background-color @@ -114,12 +143,12 @@ (let [single-fill? (= (count fills) 1) ffill (first fills) gradient? (some? (:fill-color-gradient ffill))] - (when (and (not (svg-markup? shape)) (not (cph/group-shape? shape)) single-fill? (not gradient?)) + (when (and (not (cgc/svg-markup? shape)) (not (cph/group-shape? shape)) single-fill? (not gradient?)) (fill->color ffill)))) (defmethod get-value :background-image [_ {:keys [fills] :as shape} _] - (when (and (not (svg-markup? shape)) (not (cph/group-shape? shape)) (> (count fills) 1)) + (when (and (not (cgc/svg-markup? shape)) (not (cph/group-shape? shape)) (> (count fills) 1)) (->> fills (map fill->color)))) @@ -138,7 +167,7 @@ (defmethod get-value :border [_ shape _] - (when-not (svg-markup? shape) + (when-not (cgc/svg-markup? shape) (get-stroke-data (first (:strokes shape))))) (defmethod get-value :border-radius @@ -155,12 +184,12 @@ (defmethod get-value :box-shadow [_ shape _] - (when-not (svg-markup? shape) + (when-not (cgc/svg-markup? shape) (:shadow shape))) (defmethod get-value :filter [_ shape _] - (when-not (svg-markup? shape) + (when-not (cgc/svg-markup? shape) (get-in shape [:blur :value]))) (defmethod get-value :display @@ -258,8 +287,15 @@ (defmethod get-value :flex-shrink [_ shape objects] (when (and (ctl/flex-layout-immediate-child? objects shape) - (not= :fill (:layout-item-h-sizing shape)) - (not= :fill (:layout-item-v-sizing shape)) + + (not (and (contains? #{:row :reverse-row} (:layout-flex-dir shape)) + (= :fill (:layout-item-h-sizing shape)))) + + (not (and (contains? #{:column :column-row} (:layout-flex-dir shape)) + (= :fill (:layout-item-v-sizing shape)))) + + ;;(not= :fill (:layout-item-h-sizing shape)) + ;;(not= :fill (:layout-item-v-sizing shape)) (not= :auto (:layout-item-h-sizing shape)) (not= :auto (:layout-item-v-sizing shape))) 0)) @@ -336,5 +372,3 @@ (defmethod get-value :default [property shape _] (get shape property)) - -