diff --git a/common/src/app/common/spec/shape.cljc b/common/src/app/common/spec/shape.cljc index 45239f83e..ce152d0a4 100644 --- a/common/src/app/common/spec/shape.cljc +++ b/common/src/app/common/spec/shape.cljc @@ -224,7 +224,7 @@ :internal.shape.text.position-data/font-size :internal.shape.text.position-data/font-style :internal.shape.text.position-data/font-weight - :internal.shape.text.position-data/rtl? + :internal.shape.text.position-data/rtl :internal.shape.text.position-data/text :internal.shape.text.position-data/text-decoration :internal.shape.text.position-data/text-transform] @@ -243,7 +243,7 @@ (s/def :internal.shape.text.position-data/font-size string?) (s/def :internal.shape.text.position-data/font-style string?) (s/def :internal.shape.text.position-data/font-weight string?) -(s/def :internal.shape.text.position-data/rtl? boolean?) +(s/def :internal.shape.text.position-data/rtl boolean?) (s/def :internal.shape.text.position-data/text string?) (s/def :internal.shape.text.position-data/text-decoration string?) (s/def :internal.shape.text.position-data/text-transform string?) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 1cc798798..7b0b5f4c5 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -117,13 +117,31 @@ ;; --- Helpers +(defn to-new-fills + [data] + [(d/without-nils (select-keys data [:fill-color :fill-opacity :fill-color-gradient :fill-color-ref-id :fill-color-ref-file]))]) + (defn- shape-current-values [shape pred attrs] (let [root (:content shape) nodes (->> (txt/node-seq pred root) - (map #(if (txt/is-text-node? %) - (merge txt/default-text-attrs %) - %)))] + (map (fn [node] + (if (txt/is-text-node? node) + (let [fills + (cond + (or (some? (:fill-color node)) + (some? (:fill-opacity node)) + (some? (:fill-color-gradient node))) + (to-new-fills node) + + (some? (:fills node)) + (:fills node) + + :else + (:fills txt/default-text-attrs))] + (-> (merge txt/default-text-attrs node) + (assoc :fills fills))) + node))))] (attrs/get-attrs-multi nodes attrs))) (defn current-root-values @@ -140,8 +158,10 @@ (defn current-text-values [{:keys [editor-state attrs shape]}] (if editor-state - (-> (ted/get-editor-current-inline-styles editor-state) - (select-keys attrs)) + (let [result (-> (ted/get-editor-current-inline-styles editor-state) + (select-keys attrs)) + result (if (empty? result) txt/default-text-attrs result)] + result) (shape-current-values shape txt/is-text-node? attrs))) @@ -220,24 +240,27 @@ (cph/group-shape? shape) (cph/get-children-ids objects id))] (rx/of (dch/update-shapes shape-ids #(update-text-content % update-node? attrs/merge attrs)))))))) +(defn migrate-node + [node] + (let [color-attrs (select-keys node [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient])] + (cond-> node + (d/not-empty? color-attrs) + (-> (dissoc :fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient) + (assoc :fills [color-attrs])) + + (nil? (:fills node)) + (assoc :fills (:fills txt/default-text-attrs))))) + (defn migrate-content [content] - (txt/transform-nodes - #(or (txt/is-text-node? %) (txt/is-paragraph-node? %)) - (fn [node] - (let [color-attrs (select-keys node [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient])] - (cond-> node - (d/not-empty? color-attrs) - (-> (dissoc :fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient) - (assoc :fills [color-attrs]))))) - content)) + (txt/transform-nodes (some-fn txt/is-text-node? txt/is-paragraph-node?) migrate-node content)) (defn update-text-with-function [id update-node-fn] (ptk/reify ::update-text-with-function ptk/UpdateEvent (update [_ state] - (d/update-in-when state [:workspace-editor-state id] ted/update-editor-current-inline-styles-fn update-node-fn)) + (d/update-in-when state [:workspace-editor-state id] ted/update-editor-current-inline-styles-fn (comp update-node-fn migrate-node))) ptk/WatchEvent (watch [_ state _] @@ -245,10 +268,7 @@ (let [objects (wsh/lookup-page-objects state) shape (get objects id) - update-node? - (fn [node] - (or (txt/is-text-node? node) - (txt/is-paragraph-node? node))) + update-node? (some-fn txt/is-text-node? txt/is-paragraph-node?) shape-ids (cond diff --git a/frontend/src/app/main/ui/hooks/mutable_observer.cljs b/frontend/src/app/main/ui/hooks/mutable_observer.cljs index f9852fea9..b3dabaf45 100644 --- a/frontend/src/app/main/ui/hooks/mutable_observer.cljs +++ b/frontend/src/app/main/ui/hooks/mutable_observer.cljs @@ -48,11 +48,10 @@ (mf/set-ref-val! prev-obs-ref mutation-obs) (.observe mutation-obs node options))))))] - (mf/use-effect - (fn [] - (fn [] - (when-let [^js prev-obs (mf/ref-val prev-obs-ref)] - (.disconnect prev-obs) - (mf/set-ref-val! prev-obs-ref nil))))) + (mf/with-effect + (fn [] + (when-let [^js prev-obs (mf/ref-val prev-obs-ref)] + (.disconnect prev-obs) + (mf/set-ref-val! prev-obs-ref nil)))) [node-ref set-node])) diff --git a/frontend/src/app/main/ui/render.cljs b/frontend/src/app/main/ui/render.cljs index b5dc467a1..af1a5ae66 100644 --- a/frontend/src/app/main/ui/render.cljs +++ b/frontend/src/app/main/ui/render.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.render (:require + [app.common.data :as d] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] @@ -92,12 +93,11 @@ (mf/with-memo [objects] (render/shape-wrapper-factory objects)) - text-shapes - (->> objects - (filter (fn [[_ shape]] (= :text (:type shape)))) - (mapv second)) + is-text? (fn [shape] (= :text (:type shape))) - render-texts? (and render-texts? (some #(nil? (:position-data %)) text-shapes))] + text-shapes (sequence (comp (map second) (filter is-text?)) objects) + + render-texts? (and render-texts? (d/seek (comp nil? :position-data) text-shapes))] (mf/with-effect [width height] (dom/set-page-style {:size (str (mth/ceil width) "px " diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index d1abb8d81..60370404d 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -89,11 +89,11 @@ (cond (contains? shape :fill-image) (let [fill-image-id (str "fill-image-" render-id)] - {:fill (str/format "url(#%s)" fill-image-id)}) + {:fill (str "url(#" fill-image-id ")")}) (contains? shape :fill-color-gradient) (let [fill-color-gradient-id (str "fill-color-gradient_" render-id (if index (str "_" index) ""))] - {:fill (str/format "url(#%s)" fill-color-gradient-id)}) + {:fill (str "url(#" fill-color-gradient-id ")")}) (contains? shape :fill-color) {:fill (:fill-color shape)} diff --git a/frontend/src/app/main/ui/shapes/fills.cljs b/frontend/src/app/main/ui/shapes/fills.cljs index f6f8f67a8..ab98cf852 100644 --- a/frontend/src/app/main/ui/shapes/fills.cljs +++ b/frontend/src/app/main/ui/shapes/fills.cljs @@ -45,19 +45,18 @@ (= :path (:type shape)) (obj/set! "patternTransform" transform))] - [:* - (for [[_shape-index shape] (d/enumerate (or (:position-data shape) [shape]))] + (for [[shape-index shape] (d/enumerate (or (:position-data shape) [shape]))] + [:* (for [[fill-index value] (-> (d/enumerate (:fills shape [])) reverse)] - (cond (some? (:fill-color-gradient value)) - (case (d/name (:type (:fill-color-gradient value))) - "linear" [:> grad/linear-gradient #js {:id (str "fill-color-gradient_" render-id "_" fill-index) - :gradient (:fill-color-gradient value) - :shape shape}] - "radial" [:> grad/radial-gradient #js {:id (str "fill-color-gradient_" render-id "_" fill-index) - :gradient (:fill-color-gradient value) - :shape shape}])))) + (when (some? (:fill-color-gradient value)) + (let [props #js {:id (str "fill-color-gradient_" render-id "_" fill-index) + :gradient (:fill-color-gradient value) + :shape shape}] + (case (d/name (:type (:fill-color-gradient value))) + "linear" [:> grad/linear-gradient props] + "radial" [:> grad/radial-gradient props])))) + - (for [[shape-index shape] (d/enumerate (or (:position-data shape) [shape]))] (let [fill-id (str "fill-" shape-index "-" render-id)] [:> :pattern (-> (obj/clone pattern-attrs) (obj/set! "id" fill-id)) @@ -70,4 +69,4 @@ (when has-image [:image {:xlinkHref (get embed uri uri) :width width - :height height}])]]))])))) + :height height}])]])]))))) diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index 9f4dda7c3..232aa2acd 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -87,11 +87,22 @@ :caretColor (or text-color "black") :overflowWrap "initial"} - base (-> base - (obj/set! "--fills" (transit/encode-str (:fills data))) - #_(obj/set! "--fill-color" fill-color) - #_(obj/set! "--fill-color-gradient" (transit/encode-str (:fill-color-gradient data))) - #_(obj/set! "--fill-opacity" fill-opacity))] + fills + (cond + (some? (:fills data)) + (:fills data) + + (or (some? (:fill-color data)) + (some? (:fill-opacity data)) + (some? (:fill-color-gradient data))) + [(d/without-nils (select-keys data [:fill-color :fill-opacity :fill-color-gradient :fill-color-ref-id :fill-color-ref-file]))] + + (nil? (:fills data)) + [{:fill-color "#000000" :fill-opacity 1}]) + + base (cond-> base + (some? fills) + (obj/set! "--fills" (transit/encode-str fills)))] (when (and (string? letter-spacing) (pos? (alength letter-spacing))) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 26731e181..6bb26816b 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -57,10 +57,9 @@ :textTransform (:text-transform data) :textDecoration (:text-decoration data) :fontStyle (:font-style data) - :direction (if (:rtl? data) "rtl" "ltr") + :direction (if (:rtl data) "rtl" "ltr") :whiteSpace "pre"} - (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")) - #_(attrs/add-fill data (get-gradient-id index)))})] + (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))})] [:& shape-custom-stroke {:shape shape :index index} [:> :text props (:text data)]]))]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index 8bff9737b..18bf8fc09 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -131,25 +131,26 @@ sid-ref (mf/use-ref nil) handle-change-foreign-object - (fn [node] - (when-let [position-data (utp/calc-position-data node)] - (let [parent (dom/get-parent node) - parent-transform (dom/get-attribute parent "transform") - node-transform (dom/get-attribute node "transform") + (mf/use-callback + (fn [node] + (when-let [position-data (utp/calc-position-data node)] + (let [parent (dom/get-parent node) + parent-transform (dom/get-attribute parent "transform") + node-transform (dom/get-attribute node "transform") - parent-mtx (usvg/parse-transform parent-transform) - node-mtx (usvg/parse-transform node-transform) + parent-mtx (usvg/parse-transform parent-transform) + node-mtx (usvg/parse-transform node-transform) - ;; We need to see what transformation is applied in the DOM to reverse it - ;; before calculating the position data - mtx (-> (gmt/multiply parent-mtx node-mtx) - (gmt/inverse)) + ;; We need to see what transformation is applied in the DOM to reverse it + ;; before calculating the position data + mtx (-> (gmt/multiply parent-mtx node-mtx) + (gmt/inverse)) - position-data - (->> position-data - (mapv #(merge % (-> (select-keys % [:x :y :width :height]) - (gsh/transform-rect mtx)))))] - (reset! local-position-data position-data)))) + position-data + (->> position-data + (mapv #(merge % (-> (select-keys % [:x :y :width :height]) + (gsh/transform-rect mtx)))))] + (reset! local-position-data position-data))))) [node-ref on-change-node] (use-mutable-observer handle-change-foreign-object) diff --git a/frontend/src/app/util/text_svg_position.cljs b/frontend/src/app/util/text_svg_position.cljs index 5e62e3e82..b8a8a4f94 100644 --- a/frontend/src/app/util/text_svg_position.cljs +++ b/frontend/src/app/util/text_svg_position.cljs @@ -22,9 +22,10 @@ (.setEnd range node end-i) (.getClientRects range))) +;; TODO: Evaluate to change this function to Javascript (defn parse-text-nodes "Given a text node retrieves the rectangles for everyone of its paragraphs and its text." - [parent-node rtl? text-node] + [parent-node rtl text-node] (let [content (.-textContent text-node) text-size (.-length content)] @@ -45,7 +46,7 @@ ;; If the rects increase means we're in a new paragraph (if (> (.-length rects) 1) (let [entry {:node parent-node - :position (dom/bounding-rect->rect (if rtl? (second rects) (first rects))) + :position (dom/bounding-rect->rect (if rtl (second rects) (first rects))) :text current}] (recur to-i to-i "" (conj result entry))) (recur from-i (inc to-i) (str current (nth content to-i)) result))))))) @@ -86,9 +87,9 @@ (->> text-nodes (mapcat (fn [parent-node] - (let [rtl? (= "rtl" (.-dir (.-parentElement parent-node)))] + (let [rtl (= "rtl" (.-dir (.-parentElement parent-node)))] (->> (.-childNodes parent-node) - (mapcat #(parse-text-nodes parent-node rtl? %)))))) + (mapcat #(parse-text-nodes parent-node rtl %)))))) (mapv #(update % :position translate-rect)))))) (defn calc-position-data @@ -101,15 +102,15 @@ (->> text-data (mapv (fn [{:keys [node position text]}] (let [{:keys [x y width height]} position - rtl? (= "rtl" (.-dir (.-parentElement ^js node))) + rtl (= "rtl" (.-dir (.-parentElement ^js node))) styles (js/getComputedStyle ^js node) get (fn [prop] (let [value (.getPropertyValue styles prop)] (when (and value (not= value "")) value)))] (d/without-nils - {:rtl? rtl? - :x (if rtl? (+ x width) x) + {:rtl rtl + :x (if rtl (+ x width) x) :y (+ y height) :width width :height height