mirror of
https://github.com/penpot/penpot.git
synced 2025-06-08 01:11:37 +02:00
✨ Allows svg text on test edit and creation
This commit is contained in:
parent
18dded1a00
commit
6cb6adc134
8 changed files with 198 additions and 123 deletions
54
frontend/src/app/main/ui/hooks/mutable_observer.cljs
Normal file
54
frontend/src/app/main/ui/hooks/mutable_observer.cljs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.ui.hooks.mutable-observer
|
||||||
|
(:require
|
||||||
|
[app.common.logging :as log]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(log/set-level! :warn)
|
||||||
|
|
||||||
|
(defn use-mutable-observer
|
||||||
|
[on-change]
|
||||||
|
|
||||||
|
(let [prev-obs-ref (mf/use-ref nil)
|
||||||
|
node-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
on-mutation
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps on-change)
|
||||||
|
(fn [mutation]
|
||||||
|
(log/debug :action "mutation" :js/mutation mutation)
|
||||||
|
(on-change (mf/ref-val node-ref))))
|
||||||
|
|
||||||
|
set-node
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps on-mutation)
|
||||||
|
(fn [^js node]
|
||||||
|
(when (and (some? node) (not= (mf/ref-val node-ref) node))
|
||||||
|
(mf/set-ref-val! node-ref node)
|
||||||
|
|
||||||
|
(when-let [^js prev-obs (mf/ref-val prev-obs-ref)]
|
||||||
|
(.disconnect prev-obs)
|
||||||
|
(mf/set-ref-val! prev-obs-ref nil))
|
||||||
|
|
||||||
|
(when (some? node)
|
||||||
|
(let [options #js {:attributes true
|
||||||
|
:childList true
|
||||||
|
:subtree true
|
||||||
|
:characterData true}
|
||||||
|
mutation-obs (js/MutationObserver. on-mutation)]
|
||||||
|
(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)))))
|
||||||
|
|
||||||
|
set-node))
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.main.ui.shapes.gradients
|
(ns app.main.ui.shapes.gradients
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
@ -106,6 +107,7 @@
|
||||||
:gradient gradient
|
:gradient gradient
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
(when gradient
|
(when gradient
|
||||||
(case (:type gradient)
|
(case (d/name (:type gradient))
|
||||||
:linear [:> linear-gradient gradient-props]
|
"linear" [:> linear-gradient gradient-props]
|
||||||
:radial [:> radial-gradient gradient-props]))))
|
"radial" [:> radial-gradient gradient-props]
|
||||||
|
nil))))
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
[app.common.colors :as clr]
|
[app.common.colors :as clr]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.transit :as transit]
|
|
||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.main.ui.shapes.text.styles :as sts]
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
|
@ -24,12 +23,7 @@
|
||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
text (:text node)
|
text (:text node)
|
||||||
style (sts/generate-text-styles node)]
|
style (sts/generate-text-styles node)]
|
||||||
[:span.text-node {:style style
|
[:span.text-node {:style style}
|
||||||
:data-fill-color (:fill-color node)
|
|
||||||
:data-fill-color-gradient (transit/encode-str (:fill-color-gradient node))
|
|
||||||
:data-fill-color-ref-file (transit/encode-str (:fill-color-ref-file node))
|
|
||||||
:data-fill-color-ref-id (transit/encode-str (:fill-color-ref-id node))
|
|
||||||
:data-fill-opacity (:fill-opacity node)}
|
|
||||||
(if (= text "") "\u00A0" text)]))
|
(if (= text "") "\u00A0" text)]))
|
||||||
|
|
||||||
(mf/defc render-root
|
(mf/defc render-root
|
||||||
|
@ -193,7 +187,10 @@
|
||||||
{::mf/wrap-props false
|
{::mf/wrap-props false
|
||||||
::mf/forward-ref true}
|
::mf/forward-ref true}
|
||||||
[props ref]
|
[props ref]
|
||||||
(let [{:keys [id x y width height content] :as shape} (obj/get props "shape")
|
(let [shape (obj/get props "shape")
|
||||||
|
transform (str (geom/transform-matrix shape))
|
||||||
|
|
||||||
|
{:keys [id x y width height content]} shape
|
||||||
grow-type (obj/get props "grow-type") ;; This is only needed in workspace
|
grow-type (obj/get props "grow-type") ;; This is only needed in workspace
|
||||||
;; We add 8px to add a padding for the exporter
|
;; We add 8px to add a padding for the exporter
|
||||||
;; width (+ width 8)
|
;; width (+ width 8)
|
||||||
|
@ -205,12 +202,13 @@
|
||||||
plain-colors?
|
plain-colors?
|
||||||
(remap-colors color-mapping))]
|
(remap-colors color-mapping))]
|
||||||
|
|
||||||
[:foreignObject {:x x
|
[:foreignObject
|
||||||
|
{:x x
|
||||||
:y y
|
:y y
|
||||||
:id id
|
:id id
|
||||||
:data-colors (->> colors (str/join ","))
|
:data-colors (->> colors (str/join ","))
|
||||||
:data-mapping (-> color-mapping-inverse (clj->js) (js/JSON.stringify))
|
:data-mapping (-> color-mapping-inverse (clj->js) (js/JSON.stringify))
|
||||||
:transform (geom/transform-matrix shape)
|
:transform transform
|
||||||
:width (if (#{:auto-width} grow-type) 100000 width)
|
:width (if (#{:auto-width} grow-type) 100000 width)
|
||||||
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)
|
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)
|
||||||
:style (-> (obj/new) (attrs/add-layer-props shape))
|
:style (-> (obj/new) (attrs/add-layer-props shape))
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
|
[app.common.transit :as transit]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.util.color :as uc]
|
[app.util.color :as uc]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
|
@ -71,31 +72,22 @@
|
||||||
fill-color (:fill-color data)
|
fill-color (:fill-color data)
|
||||||
fill-opacity (:fill-opacity data)
|
fill-opacity (:fill-opacity data)
|
||||||
|
|
||||||
;; Uncomment this to allow to remove text colors. This could break the texts that already exist
|
|
||||||
;;[r g b a] (if (nil? fill-color)
|
|
||||||
;; [0 0 0 0] ;; Transparent color
|
|
||||||
;; (uc/hex->rgba fill-color fill-opacity))
|
|
||||||
|
|
||||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||||
text-color (when (and (some? fill-color) (some? fill-opacity))
|
text-color (when (and (some? fill-color) (some? fill-opacity))
|
||||||
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
||||||
|
|
||||||
fontsdb (deref fonts/fontsdb)
|
fontsdb (deref fonts/fontsdb)
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
base #js {:textDecoration text-decoration
|
||||||
:textTransform text-transform
|
:textTransform text-transform
|
||||||
:lineHeight (or line-height "inherit")
|
:lineHeight (or line-height "inherit")
|
||||||
:color text-color
|
:color "transparent"
|
||||||
:caretColor "black"}]
|
:caretColor (or text-color "black")}
|
||||||
|
|
||||||
(when-let [gradient (:fill-color-gradient data)]
|
base (-> base
|
||||||
(let [text-color (-> (update gradient :type keyword)
|
(obj/set! "--fill-color" fill-color)
|
||||||
(uc/gradient->css))]
|
(obj/set! "--fill-color-gradient" (transit/encode-str (:fill-color-gradient data)))
|
||||||
(-> base
|
(obj/set! "--fill-opacity" fill-opacity))]
|
||||||
(obj/set! "color" text-color)
|
|
||||||
#_(obj/set! "--text-color" text-color)
|
|
||||||
#_(obj/set! "backgroundImage" "var(--text-color)")
|
|
||||||
#_(obj/set! "WebkitTextFillColor" "transparent")
|
|
||||||
#_(obj/set! "WebkitBackgroundClip" "text"))))
|
|
||||||
|
|
||||||
(when (and (string? letter-spacing)
|
(when (and (string? letter-spacing)
|
||||||
(pos? (alength letter-spacing)))
|
(pos? (alength letter-spacing)))
|
||||||
|
|
|
@ -45,10 +45,7 @@
|
||||||
[:> :g group-props
|
[:> :g group-props
|
||||||
[:defs
|
[:defs
|
||||||
[:clipPath {:id clip-id}
|
[:clipPath {:id clip-id}
|
||||||
[:rect.text-clip
|
[:rect.text-clip {:x x :y y :width width :height height}]]]
|
||||||
{:x x :y y
|
|
||||||
:width width :height height
|
|
||||||
:transform (gsh/transform-matrix shape)}]]]
|
|
||||||
(for [[index data] (d/enumerate position-data)]
|
(for [[index data] (d/enumerate position-data)]
|
||||||
(let [props (-> #js {:x (:x data)
|
(let [props (-> #js {:x (:x data)
|
||||||
:y (:y data)
|
:y (:y data)
|
||||||
|
@ -63,3 +60,5 @@
|
||||||
:whiteSpace "pre"}
|
:whiteSpace "pre"}
|
||||||
(attrs/add-fill data (get-gradient-id index)))})]
|
(attrs/add-fill data (get-gradient-id index)))})]
|
||||||
[:> :text props (:text data)]))]]]))
|
[:> :text props (:text data)]))]]]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,15 @@
|
||||||
(ns app.main.ui.workspace.shapes.text
|
(ns app.main.ui.workspace.shapes.text
|
||||||
(:require
|
(:require
|
||||||
[app.common.attrs :as attrs]
|
[app.common.attrs :as attrs]
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.transit :as transit]
|
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.hooks.mutable-observer :refer [use-mutable-observer]]
|
||||||
[app.main.ui.shapes.shape :refer [shape-container]]
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.ui.shapes.text.fo-text :as fo]
|
[app.main.ui.shapes.text.fo-text :as fo]
|
||||||
[app.main.ui.shapes.text.svg-text :as svg]
|
[app.main.ui.shapes.text.svg-text :as svg]
|
||||||
|
@ -114,36 +113,10 @@
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn [] #(mf/set-ref-val! mnt false)))
|
(fn [] #(mf/set-ref-val! mnt false)))
|
||||||
|
|
||||||
[:& fo/text-shape {:ref text-ref-cb :shape shape :grow-type (:grow-type shape)}]))
|
[:& fo/text-shape {:ref text-ref-cb
|
||||||
|
:shape shape
|
||||||
|
:grow-type (:grow-type shape)
|
||||||
(defn calc-position-data
|
:key (str "shape-" (:id shape))}]))
|
||||||
[base-node]
|
|
||||||
(let [viewport (dom/get-element "render")
|
|
||||||
zoom (get-in @st/state [:workspace-local :zoom])
|
|
||||||
text-data (utp/calc-text-node-positions base-node viewport zoom)]
|
|
||||||
(->> text-data
|
|
||||||
(map (fn [{:keys [node position text]}]
|
|
||||||
(let [{:keys [x y width height]} position
|
|
||||||
rtl? (= "rtl" (.-dir (.-parentElement ^js node)))
|
|
||||||
styles (.computedStyleMap ^js node)]
|
|
||||||
(d/without-nils
|
|
||||||
{:rtl? rtl?
|
|
||||||
:x (if rtl? (+ x width) x)
|
|
||||||
:y (+ y height)
|
|
||||||
:width width
|
|
||||||
:height height
|
|
||||||
:font-family (str (.get styles "font-family"))
|
|
||||||
:font-size (str (.get styles "font-size"))
|
|
||||||
:font-weight (str (.get styles "font-weight"))
|
|
||||||
:text-transform (str (.get styles "text-transform"))
|
|
||||||
:text-decoration (str (.get styles "text-decoration"))
|
|
||||||
:font-style (str (.get styles "font-style"))
|
|
||||||
:fill-color (or (dom/get-attribute node "data-fill-color") "#000000")
|
|
||||||
:fill-color-gradient (transit/decode-str (dom/get-attribute node "data-fill-color-gradient"))
|
|
||||||
:fill-opacity (d/parse-double (or (:fill-opacity node) "1"))
|
|
||||||
:text text})))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(mf/defc text-wrapper
|
(mf/defc text-wrapper
|
||||||
|
@ -154,13 +127,13 @@
|
||||||
edition? (mf/deref edition-ref)
|
edition? (mf/deref edition-ref)
|
||||||
shape-ref (mf/use-ref nil)
|
shape-ref (mf/use-ref nil)
|
||||||
|
|
||||||
prev-obs-ref (mf/use-ref nil)
|
|
||||||
local-position-data (mf/use-state nil)
|
local-position-data (mf/use-state nil)
|
||||||
|
|
||||||
handle-change-foreign-object
|
handle-change-foreign-object
|
||||||
(fn []
|
(fn [node]
|
||||||
(when-let [node (mf/ref-val shape-ref)]
|
(when (some? node)
|
||||||
(let [position-data (calc-position-data node)
|
(mf/set-ref-val! shape-ref node)
|
||||||
|
(let [position-data (utp/calc-position-data node)
|
||||||
parent (dom/get-parent node)
|
parent (dom/get-parent node)
|
||||||
parent-transform (dom/get-attribute parent "transform")
|
parent-transform (dom/get-attribute parent "transform")
|
||||||
node-transform (dom/get-attribute node "transform")
|
node-transform (dom/get-attribute node "transform")
|
||||||
|
@ -173,40 +146,20 @@
|
||||||
mtx (-> (gmt/multiply parent-mtx node-mtx)
|
mtx (-> (gmt/multiply parent-mtx node-mtx)
|
||||||
(gmt/inverse))
|
(gmt/inverse))
|
||||||
|
|
||||||
position-data'
|
position-data
|
||||||
(->> position-data
|
(->> position-data
|
||||||
(mapv #(merge % (-> (select-keys % [:x :y :width :height])
|
(mapv #(merge % (-> (select-keys % [:x :y :width :height])
|
||||||
(gsh/transform-rect mtx)))))]
|
(gsh/transform-rect mtx)))))]
|
||||||
(reset! local-position-data position-data'))))
|
(reset! local-position-data position-data))))
|
||||||
|
|
||||||
on-change-node
|
on-change-node (use-mutable-observer handle-change-foreign-object)]
|
||||||
(fn [^js node]
|
|
||||||
(mf/set-ref-val! shape-ref node)
|
|
||||||
|
|
||||||
(when-let [^js prev-obs (mf/ref-val prev-obs-ref)]
|
|
||||||
(.disconnect prev-obs)
|
|
||||||
(mf/set-ref-val! prev-obs-ref nil))
|
|
||||||
|
|
||||||
(when (some? node)
|
|
||||||
(let [fo-node (dom/query node "foreignObject")
|
|
||||||
options #js {:attributes true
|
|
||||||
:childList true
|
|
||||||
:subtree true}
|
|
||||||
mutation-obs (js/MutationObserver. handle-change-foreign-object)]
|
|
||||||
(mf/set-ref-val! prev-obs-ref mutation-obs)
|
|
||||||
(.observe mutation-obs fo-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)))))
|
|
||||||
|
|
||||||
|
;; When the text is "dirty?" we get recalculate the positions
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
(mf/deps id dirty?)
|
(mf/deps id dirty?)
|
||||||
(fn []
|
(fn []
|
||||||
(let [node (mf/ref-val shape-ref)
|
(let [node (mf/ref-val shape-ref)
|
||||||
position-data (calc-position-data node)]
|
position-data (utp/calc-position-data node)]
|
||||||
(reset! local-position-data nil)
|
(reset! local-position-data nil)
|
||||||
(st/emit! (dch/update-shapes
|
(st/emit! (dch/update-shapes
|
||||||
[id]
|
[id]
|
||||||
|
@ -233,9 +186,10 @@
|
||||||
:edition? edition?
|
:edition? edition?
|
||||||
:key (str id edition?)}]]
|
:key (str id edition?)}]]
|
||||||
|
|
||||||
[:g.text-svg {:opacity (when edition? 0)
|
(when (and (not edition?) (or (some? (:position-data shape)) (some? local-position-data)))
|
||||||
:pointer-events "none"}
|
(let [shape
|
||||||
(when (some? (:position-data shape))
|
(cond-> shape
|
||||||
[:& svg/text-shape {:shape (cond-> shape
|
|
||||||
(some? @local-position-data)
|
(some? @local-position-data)
|
||||||
(assoc :position-data @local-position-data))}])]]]))
|
(assoc :position-data @local-position-data))]
|
||||||
|
[:g.text-svg {:pointer-events "none"}
|
||||||
|
[:& svg/text-shape {:shape shape}]]))]]))
|
||||||
|
|
|
@ -14,11 +14,15 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.cursors :as cur]
|
[app.main.ui.cursors :as cur]
|
||||||
|
[app.main.ui.hooks.mutable-observer :refer [use-mutable-observer]]
|
||||||
|
[app.main.ui.shapes.shape :refer [shape-container]]
|
||||||
[app.main.ui.shapes.text.styles :as sts]
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
|
[app.main.ui.shapes.text.svg-text :as svg]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.util.text-editor :as ted]
|
[app.util.text-editor :as ted]
|
||||||
|
[app.util.text-svg-position :as utp]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[rumext.alpha :as mf])
|
[rumext.alpha :as mf])
|
||||||
(:import
|
(:import
|
||||||
|
@ -233,17 +237,56 @@
|
||||||
::mf/forward-ref true}
|
::mf/forward-ref true}
|
||||||
[props _]
|
[props _]
|
||||||
(let [{:keys [id x y width height grow-type] :as shape} (obj/get props "shape")
|
(let [{:keys [id x y width height grow-type] :as shape} (obj/get props "shape")
|
||||||
clip-id (str "clip-" id)]
|
transform (str (gsh/transform-matrix shape))
|
||||||
[:g.text-editor {:clip-path (str "url(#" clip-id ")")}
|
|
||||||
|
clip-id (str "clip-" id)
|
||||||
|
|
||||||
|
shape-ref (mf/use-ref nil)
|
||||||
|
local-position-data (mf/use-state nil)
|
||||||
|
|
||||||
|
handle-change-foreign-object
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [node]
|
||||||
|
(when node
|
||||||
|
(mf/set-ref-val! shape-ref node)
|
||||||
|
(let [position-data (utp/calc-position-data node)]
|
||||||
|
(reset! local-position-data position-data)))))
|
||||||
|
|
||||||
|
handle-interaction
|
||||||
|
(mf/use-callback
|
||||||
|
(fn []
|
||||||
|
(handle-change-foreign-object (mf/ref-val shape-ref))))
|
||||||
|
|
||||||
|
on-change-node (use-mutable-observer handle-change-foreign-object)]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/use-callback handle-interaction)
|
||||||
|
(fn []
|
||||||
|
(let [keys [(events/listen js/document EventType.KEYUP handle-interaction)
|
||||||
|
(events/listen js/document EventType.KEYDOWN handle-interaction)
|
||||||
|
(events/listen js/document EventType.MOUSEDOWN handle-interaction)]]
|
||||||
|
#(doseq [key keys]
|
||||||
|
(events/unlistenByKey key)))))
|
||||||
|
[:*
|
||||||
|
[:> shape-container {:shape shape
|
||||||
|
:pointer-events "none"}
|
||||||
|
[:& svg/text-shape {:shape (cond-> shape
|
||||||
|
(some? @local-position-data)
|
||||||
|
(assoc :position-data @local-position-data))}]]
|
||||||
|
|
||||||
|
[:g.text-editor {:clip-path (str "url(#" clip-id ")")
|
||||||
|
:ref on-change-node
|
||||||
|
:key (str "editor-" id)}
|
||||||
[:defs
|
[:defs
|
||||||
;; This clippath will cut the huge foreign object we use to calculate the automatic resize
|
;; This clippath will cut the huge foreign object we use to calculate the automatic resize
|
||||||
[:clipPath {:id clip-id}
|
[:clipPath {:id clip-id}
|
||||||
[:rect {:x x :y y
|
[:rect {:x x :y y
|
||||||
:width (+ width 8) :height (+ height 8)
|
:width (+ width 8) :height (+ height 8)
|
||||||
:transform (gsh/transform-matrix shape)}]]]
|
:transform transform}]]]
|
||||||
[:foreignObject {:transform (gsh/transform-matrix shape)
|
|
||||||
|
[:foreignObject {:transform transform
|
||||||
:x x :y y
|
:x x :y y
|
||||||
:width (if (#{:auto-width} grow-type) 100000 width)
|
:width (if (#{:auto-width} grow-type) 100000 width)
|
||||||
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)}
|
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)}
|
||||||
|
|
||||||
[:& text-shape-edit-html {:shape shape :key (str id)}]]]))
|
[:& text-shape-edit-html {:shape shape :key (str id)}]]]]))
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.transit :as transit]
|
||||||
|
[app.main.store :as st]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.globals :as global]))
|
[app.util.globals :as global]))
|
||||||
|
|
||||||
|
@ -79,7 +81,7 @@
|
||||||
:width (- (:x p2) (:x p1))
|
:width (- (:x p2) (:x p1))
|
||||||
:height (- (:y p2) (:y p1)))))
|
:height (- (:y p2) (:y p1)))))
|
||||||
|
|
||||||
text-nodes (dom/query-all base-node ".text-node")]
|
text-nodes (dom/query-all base-node ".text-node, span[data-text]")]
|
||||||
|
|
||||||
(->> text-nodes
|
(->> text-nodes
|
||||||
(mapcat
|
(mapcat
|
||||||
|
@ -90,3 +92,34 @@
|
||||||
(map #(update % :position translate-rect))))))
|
(map #(update % :position translate-rect))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn calc-position-data
|
||||||
|
[base-node]
|
||||||
|
(let [viewport (dom/get-element "render")
|
||||||
|
zoom (get-in @st/state [:workspace-local :zoom])
|
||||||
|
text-data (calc-text-node-positions base-node viewport zoom)]
|
||||||
|
(->> text-data
|
||||||
|
(map (fn [{:keys [node position text]}]
|
||||||
|
(let [{:keys [x y width height]} position
|
||||||
|
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)
|
||||||
|
:y (+ y height)
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:font-family (str (get "font-family"))
|
||||||
|
:font-size (str (get "font-size"))
|
||||||
|
:font-weight (str (get "font-weight"))
|
||||||
|
:text-transform (str (get "text-transform"))
|
||||||
|
:text-decoration (str (get "text-decoration"))
|
||||||
|
:font-style (str (get "font-style"))
|
||||||
|
:fill-color (or (get "--fill-color") "#000000")
|
||||||
|
:fill-color-gradient (transit/decode-str (get "--fill-color-gradient"))
|
||||||
|
:fill-opacity (d/parse-double (or (get "--fill-opacity") "1"))
|
||||||
|
:text text})))))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue