From 929d620c60109770b21b99070a2af518771ea398 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 15 Oct 2020 15:44:00 +0200 Subject: [PATCH] :sparkles: Fixes exports and preview --- frontend/src/app/main/exports.cljs | 46 +++++++----- frontend/src/app/main/ui/context.cljs | 2 + frontend/src/app/main/ui/shapes/attrs.cljs | 21 +++--- frontend/src/app/main/ui/shapes/filters.cljs | 65 ++++++++--------- .../src/app/main/ui/shapes/gradients.cljs | 14 ++-- frontend/src/app/main/ui/shapes/text.cljs | 25 +++---- frontend/src/app/main/ui/viewer/shapes.cljs | 45 +++++++----- .../app/main/ui/workspace/colorpicker.cljs | 10 +-- .../workspace/colorpicker/pixel_overlay.cljs | 59 ++++++++++----- .../src/app/main/ui/workspace/gradients.cljs | 4 +- .../app/main/ui/workspace/shapes/common.cljs | 28 +++---- .../app/main/ui/workspace/shapes/frame.cljs | 28 ++++--- .../app/main/ui/workspace/shapes/path.cljs | 32 ++++---- .../app/main/ui/workspace/shapes/text.cljs | 73 +++++++++++++------ .../ui/workspace/sidebar/options/text.cljs | 2 + 15 files changed, 255 insertions(+), 199 deletions(-) diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index 7169782d6f..c495b37667 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -26,7 +26,9 @@ [app.main.ui.shapes.path :as path] [app.main.ui.shapes.rect :as rect] [app.main.ui.shapes.text :as text] - [app.main.ui.shapes.group :as group])) + [app.main.ui.shapes.group :as group] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc])) (def ^:private default-color "#E8E9EA") ;; $color-canvas @@ -55,8 +57,14 @@ (mf/fnc frame-wrapper [{:keys [shape] :as props}] (let [childs (mapv #(get objects %) (:shapes shape)) - shape (geom/transform-shape shape)] - [:& frame-shape {:shape shape :childs childs}])))) + shape (geom/transform-shape shape) + render-id (mf/use-memo #(str (uuid/next)))] + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.frame + [:defs + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + [:& frame-shape {:shape shape :childs childs}]]])))) (defn group-wrapper-factory [objects] @@ -79,20 +87,24 @@ (when (and shape (not (:hidden shape))) (let [shape (geom/transform-shape frame shape) opts #js {:shape shape} - filter-id (filters/get-filter-id)] - [:g {:filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - (case (:type shape) - :curve [:> path/path-shape opts] - :text [:> text/text-shape opts] - :icon [:> icon/icon-shape opts] - :rect [:> rect/rect-shape opts] - :path [:> path/path-shape opts] - :image [:> image/image-shape opts] - :circle [:> circle/circle-shape opts] - :frame [:> frame-wrapper {:shape shape}] - :group [:> group-wrapper {:shape shape :frame frame}] - nil)]))))) + render-id (mf/use-memo #(str (uuid/next)))] + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g {:filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + (case (:type shape) + :curve [:> path/path-shape opts] + :text [:> text/text-shape opts] + :icon [:> icon/icon-shape opts] + :rect [:> rect/rect-shape opts] + :path [:> path/path-shape opts] + :image [:> image/image-shape opts] + :circle [:> circle/circle-shape opts] + :frame [:> frame-wrapper {:shape shape}] + :group [:> group-wrapper {:shape shape :frame frame}] + nil)]]))))) (mf/defc page-svg {::mf/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/context.cljs b/frontend/src/app/main/ui/context.cljs index 9b56bc217e..27df726cd7 100644 --- a/frontend/src/app/main/ui/context.cljs +++ b/frontend/src/app/main/ui/context.cljs @@ -12,3 +12,5 @@ [rumext.alpha :as mf])) (def embed-ctx (mf/create-context false)) + +(def render-ctx (mf/create-context nil)) diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 6db6b6f272..27083b0ab3 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -9,8 +9,10 @@ (ns app.main.ui.shapes.attrs (:require + [rumext.alpha :as mf] [cuerdas.core :as str] - [app.util.object :as obj])) + [app.util.object :as obj] + [app.main.ui.context :as muc])) (defn- stroke-type->dasharray [style] @@ -24,16 +26,16 @@ (obj/merge! attrs #js {:rx (:rx shape) :ry (:ry shape)})) -(defn add-fill [attrs shape] - (let [fill-color-gradient-id (str "fill-color-gradient_" (:render-id shape))] +(defn add-fill [attrs shape render-id] + (let [fill-color-gradient-id (str "fill-color-gradient_" render-id)] (if (:fill-color-gradient shape) (obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)}) (obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent") :fillOpacity (:fill-opacity shape nil)})))) -(defn add-stroke [attrs shape] +(defn add-stroke [attrs shape render-id] (let [stroke-style (:stroke-style shape :none) - stroke-color-gradient-id (str "stroke-color-gradient_" (:render-id shape))] + stroke-color-gradient-id (str "stroke-color-gradient_" render-id)] (if (not= stroke-style :none) (if (:stroke-color-gradient shape) (obj/merge! attrs @@ -49,7 +51,8 @@ (defn extract-style-attrs ([shape] - (-> (obj/new) - (add-border-radius shape) - (add-fill shape) - (add-stroke shape)))) + (let [render-id (mf/use-ctx muc/render-ctx)] + (-> (obj/new) + (add-border-radius shape) + (add-fill shape render-id) + (add-stroke shape render-id))))) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index e7286e8588..3fafec419a 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -123,45 +123,42 @@ [filter-x filter-y filter-width filter-height] (get-filters-bounds shape filters)] (when (seq filters) - [:defs - [:filter {:id filter-id - :x filter-x :y filter-y - :width filter-width :height filter-height - :filterUnits "userSpaceOnUse" - :color-interpolation-filters "sRGB"} + [:filter {:id filter-id + :x filter-x :y filter-y + :width filter-width :height filter-height + :filterUnits "userSpaceOnUse" + :color-interpolation-filters "sRGB"} - (let [;; Add as a paramter the input filter - drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %)))) - drop-shadow-filters (->> drop-shadow-filters - (map #(str "filter" (:id %))) - (cons "BackgroundImageFix") - (map add-in-filter drop-shadow-filters)) + (let [;; Add as a paramter the input filter + drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %)))) + drop-shadow-filters (->> drop-shadow-filters + (map #(str "filter" (:id %))) + (cons "BackgroundImageFix") + (map add-in-filter drop-shadow-filters)) - inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %)))) - inner-shadow-filters (->> inner-shadow-filters + inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %)))) + inner-shadow-filters (->> inner-shadow-filters (map #(str "filter" (:id %))) (cons "shape") (map add-in-filter inner-shadow-filters))] - [:* - [:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}] - (for [{:keys [id type] :as filter} drop-shadow-filters] - [:& drop-shadow-filter {:key id + [:* + [:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}] + (for [{:keys [id type] :as filter} drop-shadow-filters] + [:& drop-shadow-filter {:key id + :filter-id filter-id + :filter filter + :shape shape}]) + + [:feBlend {:mode "normal" + :in "SourceGraphic" + :in2 (if (seq drop-shadow-filters) + (str "filter" (:id (last drop-shadow-filters))) + "BackgroundImageFix") + :result "shape"}] + + (for [{:keys [id type] :as filter} inner-shadow-filters] + [:& inner-shadow-filter {:key id :filter-id filter-id :filter filter - :shape shape}]) - - [:feBlend {:mode "normal" - :in "SourceGraphic" - :in2 (if (seq drop-shadow-filters) - (str "filter" (:id (last drop-shadow-filters))) - "BackgroundImageFix") - :result "shape"}] - - (for [{:keys [id type] :as filter} inner-shadow-filters] - [:& inner-shadow-filter {:key id - :filter-id filter-id - :filter filter - :shape shape}]) - ]) - ]]))) + :shape shape}])])]))) diff --git a/frontend/src/app/main/ui/shapes/gradients.cljs b/frontend/src/app/main/ui/shapes/gradients.cljs index a1c216b46c..120e623dd7 100644 --- a/frontend/src/app/main/ui/shapes/gradients.cljs +++ b/frontend/src/app/main/ui/shapes/gradients.cljs @@ -13,6 +13,7 @@ [cuerdas.core :as str] [app.util.object :as obj] [app.common.uuid :as uuid] + [app.main.ui.context :as muc] [app.common.geom.point :as gpt])) (mf/defc linear-gradient [{:keys [id gradient shape]}] @@ -32,7 +33,8 @@ (mf/defc radial-gradient [{:keys [id gradient shape]}] (let [{:keys [x y width height]} shape] [:defs - (let [translate-vec (gpt/point (+ x (* width (:start-x gradient))) + (let [[x y] (if (= (:type shape) :frame) [0 0] [x y]) + translate-vec (gpt/point (+ x (* width (:start-x gradient))) (+ y (* height (:start-y gradient)))) gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient)) @@ -72,13 +74,13 @@ [props] (let [attr (obj/get props "attr") shape (obj/get props "shape") - render-id (obj/get props "render-id") - + render-id (mf/use-ctx muc/render-ctx) id (str (name attr) "_" render-id) gradient (get shape attr) gradient-props #js {:id id :gradient gradient :shape shape}] - (case (:type gradient) - :linear [:> linear-gradient gradient-props] - :radial [:> radial-gradient gradient-props]))) + (when gradient + (case (:type gradient) + :linear [:> linear-gradient gradient-props] + :radial [:> radial-gradient gradient-props])))) diff --git a/frontend/src/app/main/ui/shapes/text.cljs b/frontend/src/app/main/ui/shapes/text.cljs index 205a6817b1..ba773fc449 100644 --- a/frontend/src/app/main/ui/shapes/text.cljs +++ b/frontend/src/app/main/ui/shapes/text.cljs @@ -68,9 +68,10 @@ fill-color (obj/get data "fill-color" fill) fill-opacity (obj/get data "fill-opacity" opacity) - fill-color-gradient (obj/get data "fill-color-gradient" opacity) - fill-color-gradient (-> (js->clj fill-color-gradient :keywordize-keys true) - (update :type keyword)) + fill-color-gradient (obj/get data "fill-color-gradient" nil) + fill-color-gradient (when fill-color-gradient + (-> (js->clj fill-color-gradient :keywordize-keys true) + (update :type keyword))) fill-color-ref-id (obj/get data "fill-color-ref-id") fill-color-ref-file (obj/get data "fill-color-ref-file") @@ -83,14 +84,9 @@ fontsdb (deref fonts/fontsdb) base #js {:textDecoration text-decoration - ;:color (str/format "rgba(%s, %s, %s, %s)" r g b a) :textTransform text-transform :lineHeight (or line-height "inherit") - - :background background - :WebkitTextFillColor "transparent" - :WebkitBackgroundClip "text" - }] + "--text-color" background}] (when (and (string? letter-spacing) (pos? (alength letter-spacing))) @@ -179,8 +175,7 @@ (if (string? text) (let [style (generate-text-styles (clj->js node))] - [:span {:style style - :key (str index "-" (:fill-color node))} (if (= text "") "\u00A0" text)]) + [:span.text-node {:style style} (if (= text "") "\u00A0" text)]) (let [children (map-indexed (fn [index node] (mf/element text-node {:index index :node node :key index})) children)] @@ -192,13 +187,15 @@ {:key index :style style :xmlns "http://www.w3.org/1999/xhtml"} - (when (not (nil? @embeded-fonts)) - [:style @embeded-fonts]) + [:* + [:style ".text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"] + (when (not (nil? @embeded-fonts)) + [:style @embeded-fonts])] children]) "paragraph-set" (let [style #js {:display "inline-block"}] - [:div.paragraphs {:key index :style style} children]) + [:div.paragraphs {:key index :style style} children]) "paragraph" (let [style (generate-paragraph-styles (clj->js node))] diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 2ed021856c..ceaa0f7b19 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -29,7 +29,10 @@ [app.util.object :as obj] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] - [app.common.geom.shapes :as geom])) + [app.common.geom.shapes :as geom] + [app.common.uuid :as uuid] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc])) (defn on-mouse-down [event {:keys [interactions] :as shape}] @@ -56,25 +59,29 @@ (mf/deps shape) #(on-mouse-down % shape)) - filter-id (filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] - [:g.shape {:on-mouse-down on-mouse-down - :cursor (when (:interactions shape) "pointer") - :filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - [:& component {:shape shape - :frame frame - :childs childs - :is-child-selected? true}] - (when (and (:interactions shape) show-interactions?) - [:rect {:x (- x 1) - :y (- y 1) - :width (+ width 2) - :height (+ height 2) - :fill "#31EFB8" - :stroke "#31EFB8" - :stroke-width 1 - :fill-opacity 0.2}])]))) + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-mouse-down on-mouse-down + :cursor (when (:interactions shape) "pointer") + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + [:& component {:shape shape + :frame frame + :childs childs + :is-child-selected? true}] + (when (and (:interactions shape) show-interactions?) + [:rect {:x (- x 1) + :y (- y 1) + :width (+ width 2) + :height (+ height 2) + :fill "#31EFB8" + :stroke "#31EFB8" + :stroke-width 1 + :fill-opacity 0.2}])]]))) (defn frame-wrapper [shape-container show-interactions?] diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index 29f4a21ecf..dc81151e47 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -369,15 +369,7 @@ (reset! dirty? (not= data new-data)) (reset! last-change new-data) (when on-change - (on-change new-data))) - - ;; handle-change (fn [new-value new-opacity id file-id shift-clicked?] - ;; (when (or (not= new-value value) (not= new-opacity opacity)) - ;; (reset! dirty? true)) - ;; (reset! last-change [new-value new-opacity id file-id]) - ;; (when on-change - ;; (on-change new-value new-opacity id file-id shift-clicked?))) - ] + (on-change new-data)))] (mf/use-effect (fn [] diff --git a/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs index 9aea134b3b..4f6bb25e82 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs @@ -13,6 +13,7 @@ [cuerdas.core :as str] [okulary.core :as l] [promesa.core :as p] + [beicon.core :as rx] [goog.events :as events] [app.common.uuid :as uuid] [app.util.timers :as timers] @@ -53,6 +54,19 @@ [:& shape-wrapper {:shape item :key (:id item)}]))]])) +(defn draw-picker-canvas [svg-node canvas-node] + (let [canvas-context (.getContext canvas-node "2d") + xml (.serializeToString (js/XMLSerializer.) svg-node) + img-src (str "data:image/svg+xml;base64," + (-> xml js/encodeURIComponent js/unescape js/btoa)) + img (js/Image.) + + on-error (fn [err] (.error js/console "ERROR" err)) + on-load (fn [] (.drawImage canvas-context img 0 0))] + (.addEventListener img "error" on-error) + (.addEventListener img "load" on-load) + (obj/set! img "src" img-src))) + (mf/defc pixel-overlay {::mf/wrap-props false} [props] @@ -64,6 +78,8 @@ canvas-ref (mf/use-ref nil) fetch-pending (mf/deref (mdf/pending-ref)) + update-canvas-stream (rx/subject) + handle-keydown (fn [event] (when (and (kbd/esc? event)) @@ -117,26 +133,30 @@ #(events/unlistenByKey listener)))) (mf/use-effect - ;; Everytime we finish retrieving a new URL we redraw the canvas - ;; so even if we're not finished the user can start to pick basic - ;; shapes - (mf/deps fetch-pending) (fn [] - (try - (let [canvas-node (mf/ref-val canvas-ref) - canvas-context (.getContext canvas-node "2d") - svg-node (mf/ref-val svg-ref)] - (timers/schedule 100 - #(let [xml (.serializeToString (js/XMLSerializer.) svg-node) - img-src (str "data:image/svg+xml;base64," - (-> xml js/encodeURIComponent js/unescape js/btoa)) - img (js/Image.) - on-error (fn [err] (.error js/console "ERROR" err)) - on-load (fn [] (.drawImage canvas-context img 0 0))] - (.addEventListener img "error" on-error) - (.addEventListener img "load" on-load) - (obj/set! img "src" img-src)))) - (catch :default e (.error js/console e))))) + (let [sub (->> update-canvas-stream + (rx/debounce 10) + (rx/subs #(draw-picker-canvas (mf/ref-val svg-ref) + (mf/ref-val canvas-ref))))] + + #(rx/dispose! sub)))) + + (mf/use-effect + (mf/deps svg-ref canvas-ref) + (fn [] + (when (and svg-ref canvas-ref) + + (let [config (clj->js {:attributes true + :childList true + :subtree true + :characterData true}) + on-svg-change (fn [mutation-list] (rx/push! update-canvas-stream :update)) + observer (js/MutationObserver. on-svg-change)] + + (.observe observer (mf/ref-val svg-ref) config) + + ;; Disconnect on unmount + #(.disconnect observer))))) [:* [:div.overlay @@ -154,6 +174,7 @@ :width (:width vport 0) :height (:height vport 0) :style {:display "none"}}] + [:& (mf/provider muc/embed-ctx) {:value true} [:svg.viewport {:ref svg-ref diff --git a/frontend/src/app/main/ui/workspace/gradients.cljs b/frontend/src/app/main/ui/workspace/gradients.cljs index 085872e576..007b8631d8 100644 --- a/frontend/src/app/main/ui/workspace/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/gradients.cljs @@ -284,7 +284,9 @@ (change! {:width norm-dist})))] - (when (and gradient (= id (:shape-id gradient))) + (when (and gradient + (= id (:shape-id gradient)) + (not= (:type shape) :text)) [:& gradient-handler-transformed {:editing editing-spot :from-p from-p diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs index 51c183c23f..698b4a642b 100644 --- a/frontend/src/app/main/ui/workspace/shapes/common.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs @@ -20,7 +20,8 @@ [app.common.uuid :as uuid] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] - [app.common.geom.shapes :as geom])) + [app.common.geom.shapes :as geom] + [app.main.ui.context :as muc])) (defn- on-mouse-down [event {:keys [id type] :as shape}] @@ -75,22 +76,15 @@ #(on-context-menu % shape)) render-id (mf/use-memo #(str (uuid/next)))] - [:g.shape {:on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str (str "filter_" render-id) shape)} + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-mouse-down on-mouse-down + :on-context-menu on-context-menu + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - [:& filters/filters {:filter-id (str "filter_" render-id) :shape shape}] - - (when (:fill-color-gradient shape) - [:& grad/gradient {:attr :fill-color-gradient - :render-id render-id - :shape shape}]) - - (when (:stroke-color-gradient shape) - [:& grad/gradient {:attr :stroke-color-gradient - :render-id render-id - :shape shape}]) - - [:& component {:shape (assoc shape :render-id render-id)}]]))) + [:& component {:shape shape}]]]))) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 4a27b69466..18086d3adb 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -26,7 +26,9 @@ [app.common.geom.shapes :as geom] [app.util.dom :as dom] [app.main.streams :as ms] - [app.util.timers :as ts])) + [app.util.timers :as ts] + [app.main.ui.context :as muc] + [app.common.uuid :as uuid])) (defn- frame-wrapper-factory-equals? [np op] @@ -115,7 +117,7 @@ (fn [] (st/emit! (dws/change-hover-state (:id shape) false)))) - filter-id (mf/use-memo filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] (when-not (:hidden shape) [:g {:class (when selected? "selected") @@ -128,18 +130,14 @@ :on-double-click on-double-click :on-mouse-down on-mouse-down}] - [:g.frame {:filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - - (when (:fill-color-gradient shape) - [:& grad/gradient {:attr :fill-color-gradient - :shape shape}]) + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.frame {:filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - (when (:stroke-color-gradient shape) - [:& grad/gradient {:attr :stroke-color-gradient - :shape shape}]) - - [:& frame-shape - {:shape shape - :childs children}]]]))))) + [:& frame-shape + {:shape shape + :childs children}]]]]))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/path.cljs b/frontend/src/app/main/ui/workspace/shapes/path.cljs index bf19a128e7..125b9f2372 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path.cljs @@ -23,7 +23,10 @@ [app.main.data.workspace.drawing :as dr] [app.util.dom :as dom] [app.main.streams :as ms] - [app.util.timers :as ts])) + [app.util.timers :as ts] + [app.common.uuid :as uuid] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc])) (mf/defc path-wrapper {::mf/wrap-props false} @@ -45,21 +48,18 @@ (dom/prevent-default event) (st/emit! (dw/start-edition-mode (:id shape))))))) - filter-id (mf/use-memo filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] - [:g.shape {:on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-context-menu on-context-menu + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - (when (:fill-color-gradient shape) - [:& grad/gradient {:attr :fill-color-gradient - :shape shape}]) - - (when (:stroke-color-gradient shape) - [:& grad/gradient {:attr :stroke-color-gradient - :shape shape}]) - [:& path/path-shape {:shape shape - :background? true}]])) + [:& path/path-shape {:shape shape + :background? true}]]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index 3f22cbf7f1..f93dae4b72 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -34,7 +34,10 @@ [app.util.color :as uc] [app.util.timers :as timers] ["slate" :as slate] - ["slate-react" :as rslate]) + ["slate-react" :as rslate] + [app.common.uuid :as uuid] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc]) (:import goog.events.EventType goog.events.KeyCodes)) @@ -80,7 +83,7 @@ (when selected? (st/emit! (dw/start-edition-mode (:id shape))))) - filter-id (mf/use-memo filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] (mf/use-effect (mf/deps shape edition selected? current-transform) @@ -91,23 +94,27 @@ (nil? current-transform))] (timers/schedule #(reset! render-editor check?))))) - [:g.shape {:on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - [:* - (when @render-editor - [:g {:opacity 0 - :style {:pointer-events "none"}} - ;; We only render the component for its side-effect - [:& text-shape-edit {:shape shape - :read-only? true}]]) + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-context-menu on-context-menu + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + [:* + (when @render-editor + [:g {:opacity 0 + :style {:pointer-events "none"}} + ;; We only render the component for its side-effect + [:& text-shape-edit {:shape shape + :read-only? true}]]) - (if edition? - [:& text-shape-edit {:shape shape}] - [:& text/text-shape {:shape shape - :selected? selected?}])]])) + (if edition? + [:& text-shape-edit {:shape shape}] + [:& text/text-shape {:shape shape + :selected? selected?}])]]])) ;; --- Text Editor Rendering @@ -158,17 +165,25 @@ fill-color (obj/get data "fill-color" fill) fill-opacity (obj/get data "fill-opacity" opacity) + fill-color-gradient (obj/get data "fill-color-gradient" nil) + fill-color-gradient (when fill-color-gradient + (-> (js->clj fill-color-gradient :keywordize-keys true) + (update :type keyword))) + fill-color-ref-id (obj/get data "fill-color-ref-id") fill-color-ref-file (obj/get data "fill-color-ref-file") [r g b a] (uc/hex->rgba fill-color fill-opacity) + background (if fill-color-gradient + (uc/gradient->css (js->clj fill-color-gradient)) + (str/format "rgba(%s, %s, %s, %s)" r g b a)) fontsdb (deref fonts/fontsdb) base #js {:textDecoration text-decoration - :color (str/format "rgba(%s, %s, %s, %s)" r g b a) :textTransform text-transform - :lineHeight (or line-height "inherit")}] + :lineHeight (or line-height "inherit") + "--text-color" background}] (when (and (string? letter-spacing) (pos? (alength letter-spacing))) @@ -243,7 +258,9 @@ childs (obj/get props "children") data (obj/get props "leaf") style (generate-text-styles data) - attrs (obj/set! attrs "style" style)] + attrs (-> attrs + (obj/set! "style" style) + (obj/set! "className" "text-node"))] [:> :span attrs childs])) (defn- render-element @@ -284,6 +301,14 @@ children-count (->> node :children (map content-size) (reduce +))] (+ current children-count))) +(defn fix-gradients + "Fix for the gradient types that need to be keywords" + [content] + (let [fix-node + (fn [node] + (d/update-in-when node [:fill-color-gradient :type] keyword))] + (ut/map-node fix-node content))) + (mf/defc text-shape-edit {::mf/wrap [mf/memo]} [{:keys [shape read-only?] :or {read-only? false} :as props}] @@ -364,7 +389,8 @@ (fn [val] (when (not read-only?) (let [content (js->clj val :keywordize-keys true) - content (first content)] + content (first content) + content (fix-gradients content)] ;; Append timestamp so we can react to cursor change events (st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))})) (reset! state val) @@ -419,7 +445,8 @@ :x x :y y :width (if (= :auto-width grow-type) 10000 width) :height height} - [:style "span { line-height: inherit; }"] + [:style "span { line-height: inherit; } + .text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"] [:> rslate/Slate {:editor editor :value @state :on-change on-change} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs index 4c1a219966..682a7ba5ce 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs @@ -290,6 +290,8 @@ :shape shape :attrs text-fill-attrs}) + fill-values (d/update-in-when fill-values [:fill-color-gradient :type] keyword) + fill-values (cond-> fill-values ;; Keep for backwards compatibility (:fill fill-values) (assoc :fill-color (:fill fill-values))