mirror of
https://github.com/penpot/penpot.git
synced 2025-05-19 09:06:12 +02:00
commit
14c28ccce7
20 changed files with 542 additions and 142 deletions
|
@ -31,6 +31,13 @@
|
||||||
- Support for import/export binary format [Taiga #2991](https://tree.taiga.io/project/penpot/us/2991)
|
- Support for import/export binary format [Taiga #2991](https://tree.taiga.io/project/penpot/us/2991)
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
|
|
||||||
|
- Fix problem with group coordinates [#2008](https://github.com/penpot/penpot/issues/2008)
|
||||||
|
- Fix problem with line-height and texts [Taiga #3578](https://tree.taiga.io/project/penpot/issue/3578)
|
||||||
|
- Fix moving frame-guides outside frames [Taiga #3839](https://tree.taiga.io/project/penpot/issue/3839)
|
||||||
|
- Fix problem with 180 degree rotations [#2082](https://github.com/penpot/penpot/issues/2082)
|
||||||
|
|
||||||
|
|
||||||
### :arrow_up: Deps updates
|
### :arrow_up: Deps updates
|
||||||
### :heart: Community contributions by (Thank you!)
|
### :heart: Community contributions by (Thank you!)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
|
(def precision 3)
|
||||||
|
|
||||||
;; --- Matrix Impl
|
;; --- Matrix Impl
|
||||||
|
|
||||||
(defrecord Matrix [^double a
|
(defrecord Matrix [^double a
|
||||||
|
@ -24,7 +26,13 @@
|
||||||
^double f]
|
^double f]
|
||||||
Object
|
Object
|
||||||
(toString [_]
|
(toString [_]
|
||||||
(str "matrix(" a "," b "," c "," d "," e "," f ")")))
|
(str "matrix("
|
||||||
|
(mth/precision a precision) ","
|
||||||
|
(mth/precision b precision) ","
|
||||||
|
(mth/precision c precision) ","
|
||||||
|
(mth/precision d precision) ","
|
||||||
|
(mth/precision e precision) ","
|
||||||
|
(mth/precision f precision) ")")))
|
||||||
|
|
||||||
(defn matrix?
|
(defn matrix?
|
||||||
"Return true if `v` is Matrix instance."
|
"Return true if `v` is Matrix instance."
|
||||||
|
@ -66,6 +74,15 @@
|
||||||
(mth/close? (.-e m1) (.-e m2))
|
(mth/close? (.-e m1) (.-e m2))
|
||||||
(mth/close? (.-f m1) (.-f m2))))
|
(mth/close? (.-f m1) (.-f m2))))
|
||||||
|
|
||||||
|
(defn unit? [m1]
|
||||||
|
(and (some? m1)
|
||||||
|
(mth/close? (.-a m1) 1)
|
||||||
|
(mth/close? (.-b m1) 0)
|
||||||
|
(mth/close? (.-c m1) 0)
|
||||||
|
(mth/close? (.-d m1) 1)
|
||||||
|
(mth/close? (.-e m1) 0)
|
||||||
|
(mth/close? (.-f m1) 0)))
|
||||||
|
|
||||||
(defn multiply
|
(defn multiply
|
||||||
([^Matrix m1 ^Matrix m2]
|
([^Matrix m1 ^Matrix m2]
|
||||||
(let [m1a (.-a m1)
|
(let [m1a (.-a m1)
|
||||||
|
|
|
@ -251,11 +251,6 @@
|
||||||
(gmt/rotate-matrix (- rotation-angle)))]
|
(gmt/rotate-matrix (- rotation-angle)))]
|
||||||
[stretch-matrix stretch-matrix-inverse rotation-angle])))
|
[stretch-matrix stretch-matrix-inverse rotation-angle])))
|
||||||
|
|
||||||
(defn is-rotated?
|
|
||||||
[[a b _c _d]]
|
|
||||||
;; true if either a-b or c-d are parallel to the axis
|
|
||||||
(not (mth/close? (:y a) (:y b))))
|
|
||||||
|
|
||||||
(defn- adjust-rotated-transform
|
(defn- adjust-rotated-transform
|
||||||
[{:keys [transform transform-inverse flip-x flip-y]} points]
|
[{:keys [transform transform-inverse flip-x flip-y]} points]
|
||||||
(let [center (gco/center-points points)
|
(let [center (gco/center-points points)
|
||||||
|
@ -287,12 +282,9 @@
|
||||||
points (gco/transform-points points' transform-mtx)
|
points (gco/transform-points points' transform-mtx)
|
||||||
bool? (= (:type shape) :bool)
|
bool? (= (:type shape) :bool)
|
||||||
path? (= (:type shape) :path)
|
path? (= (:type shape) :path)
|
||||||
rotated? (is-rotated? points)
|
|
||||||
|
|
||||||
[selrect transform transform-inverse]
|
[selrect transform transform-inverse]
|
||||||
(if (not rotated?)
|
(adjust-rotated-transform shape points)
|
||||||
[(gpr/points->selrect points) nil nil]
|
|
||||||
(adjust-rotated-transform shape points))
|
|
||||||
|
|
||||||
base-rotation (or (:rotation shape) 0)
|
base-rotation (or (:rotation shape) 0)
|
||||||
modif-rotation (or (get-in shape [:modifiers :rotation]) 0)
|
modif-rotation (or (get-in shape [:modifiers :rotation]) 0)
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
:stroke-opacity 1}]}
|
:stroke-opacity 1}]}
|
||||||
|
|
||||||
{:type :frame
|
{:type :frame
|
||||||
:name "Artboard-1"
|
:name "Board-1"
|
||||||
:fills [{:fill-color clr/white
|
:fills [{:fill-color clr/white
|
||||||
:fill-opacity 1}]
|
:fill-opacity 1}]
|
||||||
:strokes []
|
:strokes []
|
||||||
|
|
|
@ -282,9 +282,9 @@
|
||||||
|
|
||||||
(defn set-pixel-precision
|
(defn set-pixel-precision
|
||||||
"Adjust modifiers so they adjust to the pixel grid"
|
"Adjust modifiers so they adjust to the pixel grid"
|
||||||
[modifiers shape]
|
[{:keys [resize-transform] :as modifiers} shape]
|
||||||
|
|
||||||
(if (some? (:resize-transform modifiers))
|
(if (and (some? resize-transform) (not (gmt/unit? resize-transform)))
|
||||||
;; If we're working with a rotation we don't handle pixel precision because
|
;; If we're working with a rotation we don't handle pixel precision because
|
||||||
;; the transformation won't have the precision anyway
|
;; the transformation won't have the precision anyway
|
||||||
modifiers
|
modifiers
|
||||||
|
@ -870,13 +870,19 @@
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
frame-id (cph/frame-id-by-position objects position)
|
frame-id (cph/frame-id-by-position objects position)
|
||||||
|
|
||||||
moving-shapes (->> ids
|
moving-shapes
|
||||||
(cph/clean-loops objects)
|
(->> ids
|
||||||
(keep #(get objects %))
|
(cph/clean-loops objects)
|
||||||
(remove (partial check-frame-move? frame-id objects position)))
|
(keep #(get objects %))
|
||||||
|
(remove (partial check-frame-move? frame-id objects position)))
|
||||||
|
|
||||||
|
moving-frames
|
||||||
|
(->> ids
|
||||||
|
(filter #(cph/frame-shape? objects %)))
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it page-id)
|
changes (-> (pcb/empty-changes it page-id)
|
||||||
(pcb/with-objects objects)
|
(pcb/with-objects objects)
|
||||||
|
(pcb/update-shapes moving-frames (fn [shape] (assoc shape :hide-in-viewer true)))
|
||||||
(pcb/change-parent frame-id moving-shapes))]
|
(pcb/change-parent frame-id moving-shapes))]
|
||||||
|
|
||||||
(when-not (empty? changes)
|
(when-not (empty? changes)
|
||||||
|
|
|
@ -354,7 +354,7 @@
|
||||||
(assoc :fills []))
|
(assoc :fills []))
|
||||||
|
|
||||||
|
|
||||||
bounds (gsb/get-object-bounds objects object)
|
{:keys [width height] :as bounds} (gsb/get-object-bounds objects object)
|
||||||
vbox (format-viewbox bounds)
|
vbox (format-viewbox bounds)
|
||||||
fonts (ff/shape->fonts object objects)
|
fonts (ff/shape->fonts object objects)
|
||||||
|
|
||||||
|
@ -366,8 +366,8 @@
|
||||||
[:& (mf/provider embed/context) {:value render-embed?}
|
[:& (mf/provider embed/context) {:value render-embed?}
|
||||||
[:svg {:id (dm/str "screenshot-" object-id)
|
[:svg {:id (dm/str "screenshot-" object-id)
|
||||||
:view-box vbox
|
:view-box vbox
|
||||||
:width (:width bounds)
|
:width (ust/format-precision width viewbox-decimal-precision)
|
||||||
:height (:height bounds)
|
:height (ust/format-precision height viewbox-decimal-precision)
|
||||||
:version "1.1"
|
:version "1.1"
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
:xmlns "http://www.w3.org/2000/svg"
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
|
|
105
frontend/src/app/main/ui/shapes/text/html_text.cljs
Normal file
105
frontend/src/app/main/ui/shapes/text/html_text.cljs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
;; 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.shapes.text.html-text
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(mf/defc render-text
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [node (obj/get props "node")
|
||||||
|
parent (obj/get props "parent")
|
||||||
|
shape (obj/get props "shape")
|
||||||
|
text (:text node)
|
||||||
|
style (if (= text "")
|
||||||
|
(sts/generate-text-styles shape parent)
|
||||||
|
(sts/generate-text-styles shape node))]
|
||||||
|
[:span.text-node {:style style}
|
||||||
|
(if (= text "") "\u00A0" text)]))
|
||||||
|
|
||||||
|
(mf/defc render-root
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [node (obj/get props "node")
|
||||||
|
children (obj/get props "children")
|
||||||
|
shape (obj/get props "shape")
|
||||||
|
style (sts/generate-root-styles shape node)]
|
||||||
|
[:div.root.rich-text
|
||||||
|
{:style style
|
||||||
|
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||||
|
children]))
|
||||||
|
|
||||||
|
(mf/defc render-paragraph-set
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [children (obj/get props "children")
|
||||||
|
shape (obj/get props "shape")
|
||||||
|
style (sts/generate-paragraph-set-styles shape)]
|
||||||
|
[:div.paragraph-set {:style style} children]))
|
||||||
|
|
||||||
|
(mf/defc render-paragraph
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [node (obj/get props "node")
|
||||||
|
shape (obj/get props "shape")
|
||||||
|
children (obj/get props "children")
|
||||||
|
style (sts/generate-paragraph-styles shape node)
|
||||||
|
dir (:text-direction node "auto")]
|
||||||
|
[:p.paragraph {:style style :dir dir} children]))
|
||||||
|
|
||||||
|
;; -- Text nodes
|
||||||
|
(mf/defc render-node
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [{:keys [type text children] :as parent} (obj/get props "node")]
|
||||||
|
(if (string? text)
|
||||||
|
[:> render-text props]
|
||||||
|
(let [component (case type
|
||||||
|
"root" render-root
|
||||||
|
"paragraph-set" render-paragraph-set
|
||||||
|
"paragraph" render-paragraph
|
||||||
|
nil)]
|
||||||
|
(when component
|
||||||
|
[:> component props
|
||||||
|
(for [[index node] (d/enumerate children)]
|
||||||
|
(let [props (-> (obj/clone props)
|
||||||
|
(obj/set! "node" node)
|
||||||
|
(obj/set! "parent" parent)
|
||||||
|
(obj/set! "index" index)
|
||||||
|
(obj/set! "key" index))]
|
||||||
|
[:> render-node props]))])))))
|
||||||
|
|
||||||
|
(mf/defc text-shape
|
||||||
|
{::mf/wrap-props false
|
||||||
|
::mf/forward-ref true}
|
||||||
|
[props ref]
|
||||||
|
(let [shape (obj/get props "shape")
|
||||||
|
grow-type (obj/get props "grow-type")
|
||||||
|
{:keys [id x y width height content]} shape]
|
||||||
|
|
||||||
|
[: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)}}
|
||||||
|
;; 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;
|
||||||
|
-webkit-background-clip: text;" ]
|
||||||
|
[:& render-node {:index 0
|
||||||
|
:shape shape
|
||||||
|
:node content}]]))
|
|
@ -63,7 +63,6 @@
|
||||||
(let [letter-spacing (:letter-spacing data 0)
|
(let [letter-spacing (:letter-spacing data 0)
|
||||||
text-decoration (:text-decoration data)
|
text-decoration (:text-decoration data)
|
||||||
text-transform (:text-transform data)
|
text-transform (:text-transform data)
|
||||||
line-height (:line-height data 1.2)
|
|
||||||
|
|
||||||
font-id (:font-id data (:font-id txt/default-text-attrs))
|
font-id (:font-id data (:font-id txt/default-text-attrs))
|
||||||
font-variant-id (:font-variant-id data)
|
font-variant-id (:font-variant-id data)
|
||||||
|
@ -80,7 +79,6 @@
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
base #js {:textDecoration text-decoration
|
||||||
:textTransform text-transform
|
:textTransform text-transform
|
||||||
:lineHeight (or line-height "1.2")
|
|
||||||
:color (if show-text? text-color "transparent")
|
:color (if show-text? text-color "transparent")
|
||||||
:caretColor (or text-color "black")
|
:caretColor (or text-color "black")
|
||||||
:overflowWrap "initial"
|
:overflowWrap "initial"
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
[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]
|
||||||
[app.config :as cfg]
|
|
||||||
[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.custom-stroke :refer [shape-custom-strokes]]
|
[app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]]
|
||||||
|
@ -87,18 +86,13 @@
|
||||||
|
|
||||||
[:> :g group-props
|
[:> :g group-props
|
||||||
(for [[index data] (d/enumerate position-data)]
|
(for [[index data] (d/enumerate position-data)]
|
||||||
(let [y (if (cfg/check-browser? :safari)
|
(let [y (- (:y data) (:height data))
|
||||||
(- (:y data) (:height data))
|
dominant-bl "text-before-edge"
|
||||||
(:y data))
|
|
||||||
|
|
||||||
alignment-bl (when (cfg/check-browser? :safari) "text-before-edge")
|
|
||||||
dominant-bl (when-not (cfg/check-browser? :safari) "ideographic")
|
|
||||||
rtl? (= "rtl" (:direction data))
|
rtl? (= "rtl" (:direction data))
|
||||||
props (-> #js {:key (dm/str "text-" (:id shape) "-" index)
|
props (-> #js {:key (dm/str "text-" (:id shape) "-" index)
|
||||||
:x (if rtl? (+ (:x data) (:width data)) (:x data))
|
:x (if rtl? (+ (:x data) (:width data)) (:x data))
|
||||||
:y y
|
:y y
|
||||||
:transform (position-data-transform shape data)
|
:transform (position-data-transform shape data)
|
||||||
:alignmentBaseline alignment-bl
|
|
||||||
:dominantBaseline dominant-bl
|
:dominantBaseline dominant-bl
|
||||||
:style (-> #js {:fontFamily (:font-family data)
|
:style (-> #js {:fontFamily (:font-family data)
|
||||||
:fontSize (:font-size data)
|
:fontSize (:font-size data)
|
||||||
|
@ -115,4 +109,3 @@
|
||||||
[:& (mf/provider muc/render-ctx) {:key index :value (str render-id "_" (:id shape) "_" index)}
|
[:& (mf/provider muc/render-ctx) {:key index :value (str render-id "_" (:id shape) "_" index)}
|
||||||
[:& shape-custom-strokes {:shape shape :position index :render-id render-id}
|
[:& shape-custom-strokes {:shape shape :position index :render-id render-id}
|
||||||
[:> :text props (:text data)]]]))]]))
|
[:> :text props (:text data)]]]))]]))
|
||||||
|
|
||||||
|
|
|
@ -288,7 +288,7 @@
|
||||||
:height (or height (:height shape))
|
:height (or height (:height shape))
|
||||||
:fill "red"}]]]
|
:fill "red"}]]]
|
||||||
|
|
||||||
[:foreignObject {:x (:x shape) :y (:y shape) :width "100%" :height "100%"}
|
[:foreignObject {:x (:x shape) :y (:y shape) :width width :height height}
|
||||||
[:div {:style {:position "absolute"
|
[:div {:style {:position "absolute"
|
||||||
:left 0
|
:left 0
|
||||||
:top 0
|
:top 0
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
[{:keys [grow-type id migrate] :as shape} node]
|
[{:keys [grow-type id migrate] :as shape} node]
|
||||||
;; Check if we need to update the size because it's auto-width or auto-height
|
;; Check if we need to update the size because it's auto-width or auto-height
|
||||||
;; Update the position-data of every text fragment
|
;; Update the position-data of every text fragment
|
||||||
(p/let [position-data (tsp/calc-position-data node)]
|
(p/let [position-data (tsp/calc-position-data id)]
|
||||||
;; At least one paragraph needs to be inside the bounding box
|
;; At least one paragraph needs to be inside the bounding box
|
||||||
(when (gsht/overlaps-position-data? shape position-data)
|
(when (gsht/overlaps-position-data? shape position-data)
|
||||||
(st/emit! (dwt/update-position-data id position-data)))
|
(st/emit! (dwt/update-position-data id position-data)))
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
|
|
||||||
(defn- update-text-modifier
|
(defn- update-text-modifier
|
||||||
[{:keys [grow-type id]} node]
|
[{:keys [grow-type id]} node]
|
||||||
(p/let [position-data (tsp/calc-position-data node)
|
(p/let [position-data (tsp/calc-position-data id)
|
||||||
props {:position-data position-data}
|
props {:position-data position-data}
|
||||||
|
|
||||||
props
|
props
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
;; 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.workspace.shapes.text.viewport-texts-html
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.geom.shapes.text :as gsht]
|
||||||
|
[app.common.math :as mth]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.text :as txt]
|
||||||
|
[app.main.data.workspace.texts :as dwt]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
|
[app.main.ui.shapes.text.html-text :as html]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.util.text-editor :as ted]
|
||||||
|
[app.util.text-svg-position :as tsp]
|
||||||
|
[app.util.timers :as ts]
|
||||||
|
[promesa.core :as p]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(defn strip-position-data [shape]
|
||||||
|
(dissoc shape :position-data :transform :transform-inverse))
|
||||||
|
|
||||||
|
(defn strip-modifier
|
||||||
|
[modifier]
|
||||||
|
(if (or (some? (dm/get-in modifier [:modifiers :resize-vector]))
|
||||||
|
(some? (dm/get-in modifier [:modifiers :resize-vector-2])))
|
||||||
|
modifier
|
||||||
|
(d/update-when modifier :modifiers dissoc :displacement :rotation)))
|
||||||
|
|
||||||
|
(defn process-shape [modifiers {:keys [id] :as shape}]
|
||||||
|
(let [modifier (-> (get modifiers id) strip-modifier)
|
||||||
|
shape (cond-> shape
|
||||||
|
(not (gsh/empty-modifiers? (:modifiers modifier)))
|
||||||
|
(-> (assoc :grow-type :fixed)
|
||||||
|
(merge modifier) gsh/transform-shape))]
|
||||||
|
(-> shape
|
||||||
|
(cond-> (nil? (:position-data shape))
|
||||||
|
(assoc :migrate true))
|
||||||
|
strip-position-data)))
|
||||||
|
|
||||||
|
(defn- update-with-editor-state
|
||||||
|
"Updates the shape with the current state in the editor"
|
||||||
|
[shape editor-state]
|
||||||
|
(let [content (:content shape)
|
||||||
|
editor-content
|
||||||
|
(when editor-state
|
||||||
|
(-> editor-state
|
||||||
|
(ted/get-editor-current-content)
|
||||||
|
(ted/export-content)))]
|
||||||
|
|
||||||
|
(cond-> shape
|
||||||
|
(and (some? shape) (some? editor-content))
|
||||||
|
(assoc :content (d/txt-merge content editor-content)))))
|
||||||
|
|
||||||
|
(defn- update-text-shape
|
||||||
|
[{:keys [grow-type id migrate] :as shape} node]
|
||||||
|
;; Check if we need to update the size because it's auto-width or auto-height
|
||||||
|
;; Update the position-data of every text fragment
|
||||||
|
(p/let [position-data (tsp/calc-position-data id)]
|
||||||
|
;; At least one paragraph needs to be inside the bounding box
|
||||||
|
(when (gsht/overlaps-position-data? shape position-data)
|
||||||
|
(st/emit! (dwt/update-position-data id position-data)))
|
||||||
|
|
||||||
|
(when (contains? #{:auto-height :auto-width} grow-type)
|
||||||
|
(let [{:keys [width height]}
|
||||||
|
(-> (dom/query node ".paragraph-set")
|
||||||
|
(dom/get-client-size))
|
||||||
|
width (mth/ceil width)
|
||||||
|
height (mth/ceil height)]
|
||||||
|
(when (and (not (mth/almost-zero? width))
|
||||||
|
(not (mth/almost-zero? height))
|
||||||
|
(not migrate))
|
||||||
|
(st/emit! (dwt/resize-text id width height)))))
|
||||||
|
(st/emit! (dwt/clean-text-modifier id))))
|
||||||
|
|
||||||
|
(defn- update-text-modifier
|
||||||
|
[{:keys [grow-type id]} node]
|
||||||
|
(p/let [position-data (tsp/calc-position-data id)
|
||||||
|
props {:position-data position-data}
|
||||||
|
|
||||||
|
props
|
||||||
|
(if (contains? #{:auto-height :auto-width} grow-type)
|
||||||
|
(let [{:keys [width height]} (-> (dom/query node ".paragraph-set") (dom/get-client-size))
|
||||||
|
width (mth/ceil width)
|
||||||
|
height (mth/ceil height)]
|
||||||
|
(if (and (not (mth/almost-zero? width)) (not (mth/almost-zero? height)))
|
||||||
|
(assoc props :width width :height height)
|
||||||
|
props))
|
||||||
|
props)]
|
||||||
|
|
||||||
|
(st/emit! (dwt/update-text-modifier id props))))
|
||||||
|
|
||||||
|
(mf/defc text-container
|
||||||
|
{::mf/wrap-props false
|
||||||
|
::mf/wrap [mf/memo]}
|
||||||
|
[props]
|
||||||
|
(let [shape (obj/get props "shape")
|
||||||
|
on-update (obj/get props "on-update")
|
||||||
|
|
||||||
|
handle-update
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps shape on-update)
|
||||||
|
(fn [node]
|
||||||
|
(when (some? node)
|
||||||
|
(on-update shape node))))]
|
||||||
|
|
||||||
|
[:& html/text-shape {:key (str "shape-" (:id shape))
|
||||||
|
:ref handle-update
|
||||||
|
:shape shape
|
||||||
|
:grow-type (:grow-type shape)}]))
|
||||||
|
|
||||||
|
(mf/defc viewport-texts-wrapper
|
||||||
|
{::mf/wrap-props false
|
||||||
|
::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
||||||
|
[props]
|
||||||
|
(let [text-shapes (obj/get props "text-shapes")
|
||||||
|
modifiers (obj/get props "modifiers")
|
||||||
|
prev-modifiers (hooks/use-previous modifiers)
|
||||||
|
prev-text-shapes (hooks/use-previous text-shapes)
|
||||||
|
|
||||||
|
;; A change in position-data won't be a "real" change
|
||||||
|
text-change?
|
||||||
|
(fn [id]
|
||||||
|
(let [old-shape (get prev-text-shapes id)
|
||||||
|
new-shape (get text-shapes id)
|
||||||
|
old-modifiers (-> (get prev-modifiers id) strip-modifier)
|
||||||
|
new-modifiers (-> (get modifiers id) strip-modifier)]
|
||||||
|
|
||||||
|
(or (and (not (identical? old-shape new-shape))
|
||||||
|
(not= (dissoc old-shape :migrate :position-data)
|
||||||
|
(dissoc new-shape :migrate :position-data)))
|
||||||
|
(not= new-modifiers old-modifiers)
|
||||||
|
|
||||||
|
;; When the position data is nil we force to recalculate
|
||||||
|
(:migrate new-shape))))
|
||||||
|
|
||||||
|
changed-texts
|
||||||
|
(mf/use-memo
|
||||||
|
(mf/deps text-shapes modifiers)
|
||||||
|
#(->> (keys text-shapes)
|
||||||
|
(filter text-change?)
|
||||||
|
(map (d/getf text-shapes))))
|
||||||
|
|
||||||
|
handle-update-modifier (mf/use-callback update-text-modifier)
|
||||||
|
handle-update-shape (mf/use-callback update-text-shape)]
|
||||||
|
|
||||||
|
[:*
|
||||||
|
(for [{:keys [id] :as shape} changed-texts]
|
||||||
|
[:& text-container {:shape (gsh/transform-shape shape)
|
||||||
|
:on-update (if (some? (get modifiers (:id shape)))
|
||||||
|
handle-update-modifier
|
||||||
|
handle-update-shape)
|
||||||
|
:key (str (dm/str "text-container-" id))}])]))
|
||||||
|
|
||||||
|
(mf/defc viewport-text-editing
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
|
||||||
|
(let [shape (obj/get props "shape")
|
||||||
|
|
||||||
|
;; Join current objects with the state of the editor
|
||||||
|
editor-state
|
||||||
|
(-> (mf/deref refs/workspace-editor-state)
|
||||||
|
(get (:id shape)))
|
||||||
|
|
||||||
|
text-modifier-ref
|
||||||
|
(mf/use-memo (mf/deps (:id shape)) #(refs/workspace-text-modifier-by-id (:id shape)))
|
||||||
|
|
||||||
|
text-modifier
|
||||||
|
(mf/deref text-modifier-ref)
|
||||||
|
|
||||||
|
shape (cond-> shape
|
||||||
|
(some? editor-state)
|
||||||
|
(update-with-editor-state editor-state))
|
||||||
|
|
||||||
|
;; When we have a text with grow-type :auto-height we need to check the correct height
|
||||||
|
;; otherwise the center alignment will break
|
||||||
|
shape
|
||||||
|
(if (or (not= :auto-height (:grow-type shape)) (empty? text-modifier))
|
||||||
|
shape
|
||||||
|
(let [tr-shape (dwt/apply-text-modifier shape text-modifier)]
|
||||||
|
(cond-> shape
|
||||||
|
;; we only change the height otherwise could cause problems with the other fields
|
||||||
|
(some? text-modifier)
|
||||||
|
(assoc :height (:height tr-shape)))))
|
||||||
|
|
||||||
|
shape (hooks/use-equal-memo shape)
|
||||||
|
|
||||||
|
handle-update-shape (mf/use-callback update-text-modifier)]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps (:id shape))
|
||||||
|
(fn []
|
||||||
|
#(st/emit! (dwt/remove-text-modifier (:id shape)))))
|
||||||
|
|
||||||
|
[:& text-container {:shape shape
|
||||||
|
:on-update handle-update-shape}]))
|
||||||
|
|
||||||
|
(defn check-props
|
||||||
|
[new-props old-props]
|
||||||
|
(and (identical? (unchecked-get new-props "objects")
|
||||||
|
(unchecked-get old-props "objects"))
|
||||||
|
(identical? (unchecked-get new-props "modifiers")
|
||||||
|
(unchecked-get old-props "modifiers"))
|
||||||
|
(= (unchecked-get new-props "edition")
|
||||||
|
(unchecked-get old-props "edition"))))
|
||||||
|
|
||||||
|
(mf/defc viewport-texts
|
||||||
|
{::mf/wrap-props false
|
||||||
|
::mf/wrap [#(mf/memo' % check-props)]}
|
||||||
|
[props]
|
||||||
|
(let [objects (obj/get props "objects")
|
||||||
|
edition (obj/get props "edition")
|
||||||
|
modifiers (obj/get props "modifiers")
|
||||||
|
|
||||||
|
text-shapes
|
||||||
|
(mf/use-memo
|
||||||
|
(mf/deps objects)
|
||||||
|
#(into {} (filter (comp cph/text-shape? second)) objects))
|
||||||
|
|
||||||
|
text-shapes
|
||||||
|
(mf/use-memo
|
||||||
|
(mf/deps text-shapes modifiers)
|
||||||
|
#(d/update-vals text-shapes (partial process-shape modifiers)))
|
||||||
|
|
||||||
|
editing-shape (get text-shapes edition)
|
||||||
|
|
||||||
|
;; This memo is necesary so the viewport-text-wrapper memoize its props correctly
|
||||||
|
text-shapes-wrapper
|
||||||
|
(mf/use-memo
|
||||||
|
(mf/deps text-shapes edition)
|
||||||
|
(fn []
|
||||||
|
(dissoc text-shapes edition)))]
|
||||||
|
|
||||||
|
;; We only need the effect to run on "mount" because the next fonts will be changed when the texts are
|
||||||
|
;; edited
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
(let [text-nodes (->> text-shapes (vals)(mapcat #(txt/node-seq txt/is-text-node? (:content %))))
|
||||||
|
fonts (into #{} (keep :font-id) text-nodes)]
|
||||||
|
(run! fonts/ensure-loaded! fonts))))
|
||||||
|
|
||||||
|
[:*
|
||||||
|
(when editing-shape
|
||||||
|
[:& viewport-text-editing {:shape editing-shape}])
|
||||||
|
|
||||||
|
[:& viewport-texts-wrapper {:text-shapes text-shapes-wrapper
|
||||||
|
:modifiers modifiers}]]))
|
|
@ -78,8 +78,10 @@
|
||||||
;; In case of multiple selection, the origin point has been already
|
;; In case of multiple selection, the origin point has been already
|
||||||
;; calculated and given in the fake :ox and :oy attributes. See
|
;; calculated and given in the fake :ox and :oy attributes. See
|
||||||
;; common/src/app/common/attrs.cljc
|
;; common/src/app/common/attrs.cljc
|
||||||
(some? (:ox values)) (assoc :x (:ox values))
|
(and (= (:x values) :multiple)
|
||||||
(some? (:oy values)) (assoc :y (:oy values))))
|
(some? (:ox values))) (assoc :x (:ox values))
|
||||||
|
(and (= (:y values) :multiple)
|
||||||
|
(some? (:oy values))) (assoc :y (:oy values))))
|
||||||
|
|
||||||
;; For :height and :width we take those in the :selrect attribute, because
|
;; For :height and :width we take those in the :selrect attribute, because
|
||||||
;; not all shapes have an own :width and :height (e. g. paths). Here the
|
;; not all shapes have an own :width and :height (e. g. paths). Here the
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
[app.main.ui.workspace.shapes :as shapes]
|
[app.main.ui.workspace.shapes :as shapes]
|
||||||
[app.main.ui.workspace.shapes.text.editor :as editor]
|
[app.main.ui.workspace.shapes.text.editor :as editor]
|
||||||
[app.main.ui.workspace.shapes.text.text-edition-outline :refer [text-edition-outline]]
|
[app.main.ui.workspace.shapes.text.text-edition-outline :refer [text-edition-outline]]
|
||||||
[app.main.ui.workspace.shapes.text.viewport-texts :as stv]
|
[app.main.ui.workspace.shapes.text.viewport-texts-html :as stvh]
|
||||||
[app.main.ui.workspace.viewport.actions :as actions]
|
[app.main.ui.workspace.viewport.actions :as actions]
|
||||||
[app.main.ui.workspace.viewport.comments :as comments]
|
[app.main.ui.workspace.viewport.comments :as comments]
|
||||||
[app.main.ui.workspace.viewport.drawarea :as drawarea]
|
[app.main.ui.workspace.viewport.drawarea :as drawarea]
|
||||||
|
@ -194,6 +194,13 @@
|
||||||
|
|
||||||
[:div.viewport
|
[:div.viewport
|
||||||
[:div.viewport-overlays {:ref overlays-ref}
|
[:div.viewport-overlays {:ref overlays-ref}
|
||||||
|
[:div {:style {:pointer-events "none" :opacity 0}}
|
||||||
|
[:& stvh/viewport-texts
|
||||||
|
{:key (dm/str "texts-" page-id)
|
||||||
|
:page-id page-id
|
||||||
|
:objects objects
|
||||||
|
:modifiers modifiers
|
||||||
|
:edition edition}]]
|
||||||
|
|
||||||
(when show-comments?
|
(when show-comments?
|
||||||
[:& comments/comments-layer {:vbox vbox
|
[:& comments/comments-layer {:vbox vbox
|
||||||
|
@ -236,23 +243,6 @@
|
||||||
:objects base-objects
|
:objects base-objects
|
||||||
:active-frames @active-frames}]]]]
|
:active-frames @active-frames}]]]]
|
||||||
|
|
||||||
[:svg.render-shapes
|
|
||||||
{:id "text-position-layer"
|
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
|
||||||
:preserveAspectRatio "xMidYMid meet"
|
|
||||||
:key (str "text-position-layer" page-id)
|
|
||||||
:width (:width vport 0)
|
|
||||||
:height (:height vport 0)
|
|
||||||
:view-box (utils/format-viewbox vbox)}
|
|
||||||
|
|
||||||
[:g {:pointer-events "none" :opacity 0}
|
|
||||||
[:& stv/viewport-texts {:key (dm/str "texts-" page-id)
|
|
||||||
:page-id page-id
|
|
||||||
:objects objects
|
|
||||||
:modifiers modifiers
|
|
||||||
:edition edition}]]]
|
|
||||||
|
|
||||||
[:svg.viewport-controls
|
[:svg.viewport-controls
|
||||||
{:xmlns "http://www.w3.org/2000/svg"
|
{:xmlns "http://www.w3.org/2000/svg"
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
|
|
|
@ -284,13 +284,16 @@
|
||||||
|
|
||||||
pos (+ (or (:new-position @state) (:position guide)) (get move-vec axis))
|
pos (+ (or (:new-position @state) (:position guide)) (get move-vec axis))
|
||||||
guide-width (/ guide-width zoom)
|
guide-width (/ guide-width zoom)
|
||||||
guide-pill-corner-radius (/ guide-pill-corner-radius zoom)]
|
guide-pill-corner-radius (/ guide-pill-corner-radius zoom)
|
||||||
|
|
||||||
|
frame-guide-outside?
|
||||||
|
(and (some? frame)
|
||||||
|
(not (is-guide-inside-frame? (assoc guide :position pos) frame)))]
|
||||||
|
|
||||||
(when (or (nil? frame)
|
(when (or (nil? frame)
|
||||||
(and (is-guide-inside-frame? (assoc guide :position pos) frame)
|
(and (cph/root-frame? frame)
|
||||||
(cph/root-frame? frame)
|
|
||||||
(not (cph/rotated-frame? frame))))
|
(not (cph/rotated-frame? frame))))
|
||||||
[:g.guide-area
|
[:g.guide-area {:opacity (when frame-guide-outside? 0)}
|
||||||
(when-not disabled-guides?
|
(when-not disabled-guides?
|
||||||
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
|
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
|
||||||
[:rect {:x x
|
[:rect {:x x
|
||||||
|
@ -298,7 +301,7 @@
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:style {:fill "none"
|
:style {:fill "none"
|
||||||
:pointer-events "fill"
|
:pointer-events (if frame-guide-outside? "none" "fill")
|
||||||
:cursor (if (= axis :x) (cur/resize-ew 0) (cur/resize-ns 0))}
|
:cursor (if (= axis :x) (cur/resize-ew 0) (cur/resize-ns 0))}
|
||||||
:on-pointer-enter on-pointer-enter
|
:on-pointer-enter on-pointer-enter
|
||||||
:on-pointer-leave on-pointer-leave
|
:on-pointer-leave on-pointer-leave
|
||||||
|
|
|
@ -10,13 +10,14 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.main.ui.cursors :as cur]
|
[app.main.ui.cursors :as cur]
|
||||||
|
[app.main.ui.formats :refer [format-number]]
|
||||||
[app.util.dom :as dom]))
|
[app.util.dom :as dom]))
|
||||||
|
|
||||||
(defn format-viewbox [vbox]
|
(defn format-viewbox [vbox]
|
||||||
(dm/str (:x vbox 0) " "
|
(dm/str (format-number(:x vbox 0)) " "
|
||||||
(:y vbox 0) " "
|
(format-number (:y vbox 0)) " "
|
||||||
(:width vbox 0) " "
|
(format-number (:width vbox 0)) " "
|
||||||
(:height vbox 0)))
|
(format-number (:height vbox 0))))
|
||||||
|
|
||||||
(defn translate-point-to-viewport [viewport zoom pt]
|
(defn translate-point-to-viewport [viewport zoom pt]
|
||||||
(let [vbox (.. ^js viewport -viewBox -baseVal)
|
(let [vbox (.. ^js viewport -viewBox -baseVal)
|
||||||
|
|
|
@ -140,8 +140,7 @@
|
||||||
text-pos-x (if (:use-for-thumbnail? frame) 15 0)]
|
text-pos-x (if (:use-for-thumbnail? frame) 15 0)]
|
||||||
|
|
||||||
(when (not (:hidden frame))
|
(when (not (:hidden frame))
|
||||||
[:g {:id (dm/str "frame-title-" (:id frame))
|
[:g {:id (dm/str "frame-title-" (:id frame))}
|
||||||
}
|
|
||||||
(when (:use-for-thumbnail? frame)
|
(when (:use-for-thumbnail? frame)
|
||||||
[:g {:transform (dm/str frame-transform " " (text-transform label-pos zoom))}
|
[:g {:transform (dm/str frame-transform " " (text-transform label-pos zoom))}
|
||||||
[:svg {:x 0
|
[:svg {:x 0
|
||||||
|
|
|
@ -91,6 +91,11 @@
|
||||||
(when event
|
(when event
|
||||||
(.stopPropagation event)))
|
(.stopPropagation event)))
|
||||||
|
|
||||||
|
(defn stop-immediate-propagation
|
||||||
|
[^js event]
|
||||||
|
(when event
|
||||||
|
(.stopImmediatePropagation event)))
|
||||||
|
|
||||||
(defn prevent-default
|
(defn prevent-default
|
||||||
[^js event]
|
[^js event]
|
||||||
(when event
|
(when event
|
||||||
|
@ -128,6 +133,14 @@
|
||||||
(when (some? node)
|
(when (some? node)
|
||||||
(.-parentElement ^js node)))
|
(.-parentElement ^js node)))
|
||||||
|
|
||||||
|
(defn get-parent-with-selector
|
||||||
|
[^js node selector]
|
||||||
|
|
||||||
|
(loop [current node]
|
||||||
|
(if (or (nil? current) (.matches current selector) )
|
||||||
|
current
|
||||||
|
(recur (.-parentElement current)))))
|
||||||
|
|
||||||
(defn get-value
|
(defn get-value
|
||||||
"Extract the value from dom node."
|
"Extract the value from dom node."
|
||||||
[^js node]
|
[^js node]
|
||||||
|
@ -588,3 +601,12 @@
|
||||||
(defn load-font [font]
|
(defn load-font [font]
|
||||||
(let [fonts (.-fonts globals/document)]
|
(let [fonts (.-fonts globals/document)]
|
||||||
(.load fonts font)))
|
(.load fonts font)))
|
||||||
|
|
||||||
|
(defn text-measure [font]
|
||||||
|
(let [element (.createElement globals/document "canvas")
|
||||||
|
context (.getContext element "2d")
|
||||||
|
_ (set! (.-font context) font)
|
||||||
|
measure ^js (.measureText context "Ag")]
|
||||||
|
|
||||||
|
{:ascent (.-fontBoundingBoxAscent measure)
|
||||||
|
:descent (.-fontBoundingBoxDescent measure)}))
|
||||||
|
|
|
@ -6,25 +6,58 @@
|
||||||
|
|
||||||
(ns app.util.path.format
|
(ns app.util.path.format
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.common.path.commands :as upc]
|
[app.common.path.commands :as upc]
|
||||||
[app.common.path.subpaths :refer [pt=]]
|
[app.common.path.subpaths :refer [pt=]]
|
||||||
[app.util.array :as arr]))
|
[app.util.array :as arr]))
|
||||||
|
|
||||||
|
(def path-precision 3)
|
||||||
|
|
||||||
(defn- join-params
|
(defn- join-params
|
||||||
([a]
|
([a]
|
||||||
(js* "\"\"+~{}" a))
|
(js* "\"\"+~{}"
|
||||||
|
(mth/precision a path-precision)))
|
||||||
([a b]
|
([a b]
|
||||||
(js* "\"\"+~{}+\",\"+~{}" a b))
|
(js* "\"\"+~{}+\",\"+~{}"
|
||||||
|
(mth/precision a path-precision)
|
||||||
|
(mth/precision b path-precision)))
|
||||||
([a b c]
|
([a b c]
|
||||||
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}" a b c))
|
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}"
|
||||||
|
(mth/precision a path-precision)
|
||||||
|
(mth/precision b path-precision)
|
||||||
|
(mth/precision c path-precision)))
|
||||||
([a b c d]
|
([a b c d]
|
||||||
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d))
|
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
|
||||||
|
(mth/precision a path-precision)
|
||||||
|
(mth/precision b path-precision)
|
||||||
|
(mth/precision c path-precision)
|
||||||
|
(mth/precision d path-precision)
|
||||||
|
))
|
||||||
([a b c d e]
|
([a b c d e]
|
||||||
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d e))
|
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
|
||||||
|
(mth/precision a path-precision)
|
||||||
|
(mth/precision b path-precision)
|
||||||
|
(mth/precision c path-precision)
|
||||||
|
(mth/precision d path-precision)
|
||||||
|
(mth/precision e path-precision)))
|
||||||
([a b c d e f]
|
([a b c d e f]
|
||||||
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d e f))
|
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
|
||||||
|
(mth/precision a path-precision)
|
||||||
|
(mth/precision b path-precision)
|
||||||
|
(mth/precision c path-precision)
|
||||||
|
(mth/precision d path-precision)
|
||||||
|
(mth/precision e path-precision)
|
||||||
|
(mth/precision f path-precision)
|
||||||
|
))
|
||||||
([a b c d e f g]
|
([a b c d e f g]
|
||||||
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}" a b c d e f g)))
|
(js* "\"\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}+\",\"+~{}"
|
||||||
|
(mth/precision a path-precision)
|
||||||
|
(mth/precision b path-precision)
|
||||||
|
(mth/precision c path-precision)
|
||||||
|
(mth/precision d path-precision)
|
||||||
|
(mth/precision e path-precision)
|
||||||
|
(mth/precision f path-precision)
|
||||||
|
(mth/precision g path-precision))))
|
||||||
|
|
||||||
(defn- translate-params
|
(defn- translate-params
|
||||||
[command {:keys [x y] :as params}]
|
[command {:keys [x y] :as params}]
|
||||||
|
|
|
@ -8,10 +8,8 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.point :as gpt]
|
|
||||||
[app.common.transit :as transit]
|
[app.common.transit :as transit]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.main.store :as st]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.text-position-data :as tpd]
|
[app.util.text-position-data :as tpd]
|
||||||
[promesa.core :as p]))
|
[promesa.core :as p]))
|
||||||
|
@ -59,78 +57,54 @@
|
||||||
(p/then #(when (not (dom/check-font? font))
|
(p/then #(when (not (dom/check-font? font))
|
||||||
(load-font font))))))
|
(load-font font))))))
|
||||||
|
|
||||||
(defn calc-text-node-positions
|
(defn- calc-text-node-positions
|
||||||
[base-node viewport zoom]
|
[shape-id]
|
||||||
|
|
||||||
(when (and (some? base-node)(some? viewport))
|
(when (some? shape-id)
|
||||||
(let [translate-point
|
(let [text-nodes (dom/query-all (dm/str "#html-text-node-" shape-id " .text-node"))
|
||||||
(fn [pt]
|
load-fonts (->> text-nodes (map resolve-font))
|
||||||
(let [vbox (.. ^js viewport -viewBox -baseVal)
|
|
||||||
brect (dom/get-bounding-rect viewport)
|
|
||||||
brect (gpt/point (d/parse-integer (:left brect))
|
|
||||||
(d/parse-integer (:top brect)))
|
|
||||||
box (gpt/point (.-x vbox) (.-y vbox))
|
|
||||||
zoom (gpt/point zoom)]
|
|
||||||
|
|
||||||
(-> (gpt/subtract pt brect)
|
process-text-node
|
||||||
(gpt/divide zoom)
|
(fn [parent-node]
|
||||||
(gpt/add box))))
|
(let [root (dom/get-parent-with-selector parent-node ".text-node-html")
|
||||||
|
shape-x (-> (dom/get-attribute root "data-x") d/parse-double)
|
||||||
translate-rect
|
shape-y (-> (dom/get-attribute root "data-y") d/parse-double)
|
||||||
(fn [{:keys [x y width height] :as rect}]
|
direction (.-direction (js/getComputedStyle parent-node))]
|
||||||
(let [p1 (-> (gpt/point x y)
|
(->> (.-childNodes parent-node)
|
||||||
(translate-point))
|
(mapcat #(parse-text-nodes parent-node direction %))
|
||||||
|
(mapv #(-> %
|
||||||
p2 (-> (gpt/point (+ x width) (+ y height))
|
(update-in [:position :x] + shape-x)
|
||||||
(translate-point))]
|
(update-in [:position :y] + shape-y))))))]
|
||||||
(assoc rect
|
|
||||||
:x (:x p1)
|
|
||||||
:y (:y p1)
|
|
||||||
:width (- (:x p2) (:x p1))
|
|
||||||
:height (- (:y p2) (:y p1)))))
|
|
||||||
|
|
||||||
text-nodes (dom/query-all base-node ".text-node, span[data-text]")
|
|
||||||
load-fonts (->> text-nodes (map resolve-font))]
|
|
||||||
(-> (p/all load-fonts)
|
(-> (p/all load-fonts)
|
||||||
(p/then
|
(p/then
|
||||||
(fn []
|
(fn []
|
||||||
(->> text-nodes
|
(->> text-nodes (mapcat process-text-node))))))))
|
||||||
(mapcat
|
|
||||||
(fn [parent-node]
|
|
||||||
(let [direction (.-direction (js/getComputedStyle parent-node))]
|
|
||||||
(->> (.-childNodes parent-node)
|
|
||||||
(mapcat #(parse-text-nodes parent-node direction %))))))
|
|
||||||
(mapv #(update % :position translate-rect)))))))))
|
|
||||||
|
|
||||||
(defn calc-position-data
|
(defn calc-position-data
|
||||||
[base-node]
|
[shape-id]
|
||||||
(let [viewport (dom/get-element "render")
|
(when (some? shape-id)
|
||||||
zoom (or (get-in @st/state [:workspace-local :zoom]) 1)]
|
(p/let [text-data (calc-text-node-positions shape-id)]
|
||||||
(when (and (some? base-node) (some? viewport))
|
(when (d/not-empty? text-data)
|
||||||
(p/let [text-data (calc-text-node-positions base-node viewport zoom)]
|
(->> text-data
|
||||||
(when (d/not-empty? text-data)
|
(mapv (fn [{:keys [node position text direction]}]
|
||||||
(->> text-data
|
(let [{:keys [x y width height]} position
|
||||||
(mapv (fn [{:keys [node position text direction]}]
|
styles (js/getComputedStyle ^js node)
|
||||||
(let [{:keys [x y width height]} position
|
get (fn [prop]
|
||||||
styles (js/getComputedStyle ^js node)
|
(let [value (.getPropertyValue styles prop)]
|
||||||
get (fn [prop]
|
(when (and value (not= value ""))
|
||||||
(let [value (.getPropertyValue styles prop)]
|
value)))]
|
||||||
(when (and value (not= value ""))
|
(d/without-nils
|
||||||
value)))]
|
{:x x
|
||||||
(d/without-nils
|
:y (+ y height)
|
||||||
{:x x
|
:width width
|
||||||
:y (+ y height)
|
:height height
|
||||||
:width width
|
:direction direction
|
||||||
:height height
|
:font-family (str (get "font-family"))
|
||||||
:direction direction
|
:font-size (str (get "font-size"))
|
||||||
:font-family (str (get "font-family"))
|
:font-weight (str (get "font-weight"))
|
||||||
:font-size (str (get "font-size"))
|
:text-transform (str (get "text-transform"))
|
||||||
:font-weight (str (get "font-weight"))
|
:text-decoration (str (get "text-decoration"))
|
||||||
:text-transform (str (get "text-transform"))
|
:letter-spacing (str (get "letter-spacing"))
|
||||||
:text-decoration (str (get "text-decoration"))
|
:font-style (str (get "font-style"))
|
||||||
:letter-spacing (str (get "letter-spacing"))
|
:fills (transit/decode-str (get "--fills"))
|
||||||
:font-style (str (get "font-style"))
|
:text text})))))))))
|
||||||
:fills (transit/decode-str (get "--fills"))
|
|
||||||
:text text}))))))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue