🐛 Fix problem when resizing texts inside groups

This commit is contained in:
alonso.torres 2022-01-04 14:19:21 +01:00
parent 6334520c66
commit 51ea354bcb
5 changed files with 250 additions and 150 deletions

View file

@ -35,6 +35,7 @@
- Fix problem with booleans [Taiga #2356](https://tree.taiga.io/project/penpot/issue/2356) - Fix problem with booleans [Taiga #2356](https://tree.taiga.io/project/penpot/issue/2356)
- Fix line-height/letter-spacing inputs behaviour [Taiga #2331](https://tree.taiga.io/project/penpot/issue/2331) - Fix line-height/letter-spacing inputs behaviour [Taiga #2331](https://tree.taiga.io/project/penpot/issue/2331)
- Fix dotted style in strokes [Taiga #2312](https://tree.taiga.io/project/penpot/issue/2312) - Fix dotted style in strokes [Taiga #2312](https://tree.taiga.io/project/penpot/issue/2312)
- Fix problem when resizing texts inside groups [Taiga #2310](https://tree.taiga.io/project/penpot/issue/2310)
### :arrow_up: Deps updates ### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)

View file

@ -451,9 +451,6 @@
rt-modif (:rotation modifiers)] rt-modif (:rotation modifiers)]
(cond-> (gmt/matrix) (cond-> (gmt/matrix)
(some? displacement)
(gmt/multiply displacement)
(some? resize-1) (some? resize-1)
(-> (gmt/translate origin-1) (-> (gmt/translate origin-1)
(gmt/multiply resize-transform) (gmt/multiply resize-transform)
@ -468,6 +465,9 @@
(gmt/multiply resize-transform-inverse) (gmt/multiply resize-transform-inverse)
(gmt/translate (gpt/negate origin-2))) (gmt/translate (gpt/negate origin-2)))
(some? displacement)
(gmt/multiply displacement)
(some? rt-modif) (some? rt-modif)
(-> (gmt/translate center) (-> (gmt/translate center)
(gmt/multiply (gmt/rotate-matrix rt-modif)) (gmt/multiply (gmt/rotate-matrix rt-modif))

View file

@ -14,11 +14,10 @@
[cuerdas.core :as str])) [cuerdas.core :as str]))
(defn generate-root-styles (defn generate-root-styles
[shape node] [_shape node]
(let [valign (:vertical-align node "top") (let [valign (:vertical-align node "top")
width (some-> (:width shape) (+ 1)) base #js {:height "100%"
base #js {:height (or (:height shape) "100%") :width "100%"
:width (or width "100%")
:fontFamily "sourcesanspro"}] :fontFamily "sourcesanspro"}]
(cond-> base (cond-> base
(= valign "top") (obj/set! "justifyContent" "flex-start") (= valign "top") (obj/set! "justifyContent" "flex-start")

View file

@ -16,30 +16,54 @@
(defn- text-corrected-transform (defn- text-corrected-transform
"If we apply a scale directly to the texts it will show deformed so we need to create this "If we apply a scale directly to the texts it will show deformed so we need to create this
correction matrix to \"undo\" the resize but keep the other transformations." correction matrix to \"undo\" the resize but keep the other transformations."
[{:keys [points transform transform-inverse]} current-transform modifiers] [{:keys [x y width height points transform transform-inverse] :as shape} current-transform modifiers]
(let [corner-pt (first points) (let [corner-pt (first points)
transform (or transform (gmt/matrix)) corner-pt (cond-> corner-pt (some? transform-inverse) (gpt/transform transform-inverse))
transform-inverse (or transform-inverse (gmt/matrix))
current-transform resize-x? (some? (:resize-vector modifiers))
(if (some? (:resize-vector modifiers)) resize-y? (some? (:resize-vector-2 modifiers))
(gmt/multiply
current-transform
transform
(gmt/scale-matrix (gpt/inverse (:resize-vector modifiers)) (gpt/transform corner-pt transform-inverse))
transform-inverse)
current-transform)
current-transform flip-x? (neg? (get-in modifiers [:resize-vector :x]))
(if (some? (:resize-vector-2 modifiers)) flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
(gmt/multiply (neg? (get-in modifiers [:resize-vector-2 :y])))
current-transform
transform result (cond-> (gmt/matrix)
(gmt/scale-matrix (gpt/inverse (:resize-vector-2 modifiers)) (gpt/transform corner-pt transform-inverse)) (and (some? transform) (or resize-x? resize-y?))
transform-inverse) (gmt/multiply transform)
current-transform)]
current-transform)) resize-x?
(gmt/scale (gpt/inverse (:resize-vector modifiers)) corner-pt)
resize-y?
(gmt/scale (gpt/inverse (:resize-vector-2 modifiers)) corner-pt)
flip-x?
(gmt/scale (gpt/point -1 1) corner-pt)
flip-y?
(gmt/scale (gpt/point 1 -1) corner-pt)
(and (some? transform) (or resize-x? resize-y?))
(gmt/multiply transform-inverse))
[width height]
(if (or resize-x? resize-y?)
(let [pc (-> (gpt/point x y)
(gpt/transform transform)
(gpt/transform current-transform))
pw (-> (gpt/point (+ x width) y)
(gpt/transform transform)
(gpt/transform current-transform))
ph (-> (gpt/point x (+ y height))
(gpt/transform transform)
(gpt/transform current-transform))]
[(gpt/distance pc pw) (gpt/distance pc ph)])
[width height])]
[result width height]))
(defn get-nodes (defn get-nodes
"Retrieve the DOM nodes to apply the matrix transformation" "Retrieve the DOM nodes to apply the matrix transformation"
@ -48,6 +72,7 @@
frame? (= :frame type) frame? (= :frame type)
group? (= :group type) group? (= :group type)
text? (= :text type)
mask? (and group? masked-group?) mask? (and group? masked-group?)
;; When the shape is a frame we maybe need to move its thumbnail ;; When the shape is a frame we maybe need to move its thumbnail
@ -68,6 +93,11 @@
group? group?
[] []
text?
[shape-node
(dom/query shape-node "foreignObject")
(dom/query shape-node ".text-shape")]
:else :else
[shape-node]))) [shape-node])))
@ -76,11 +106,23 @@
(when-let [nodes (get-nodes shape)] (when-let [nodes (get-nodes shape)]
(let [transform (get transforms id) (let [transform (get transforms id)
modifiers (get-in modifiers [id :modifiers]) modifiers (get-in modifiers [id :modifiers])
transform (case type
:text (text-corrected-transform shape transform modifiers) [text-transform text-width text-height]
transform)] (when (= :text type)
(text-corrected-transform shape transform modifiers))]
(doseq [node nodes] (doseq [node nodes]
(when (and (some? transform) (some? node)) (cond
(dom/class? node "text-shape")
(when (some? text-transform)
(dom/set-attribute node "transform" (str text-transform)))
(= (dom/get-tag-name node) "foreignObject")
(when (and (some? text-width) (some? text-height))
(dom/set-attribute node "width" text-width)
(dom/set-attribute node "height" text-height))
(and (some? transform) (some? node))
(dom/set-attribute node "transform" (str transform)))))))) (dom/set-attribute node "transform" (str transform))))))))
(defn remove-transform [shapes] (defn remove-transform [shapes]
@ -88,7 +130,13 @@
(when-let [nodes (get-nodes shape)] (when-let [nodes (get-nodes shape)]
(doseq [node nodes] (doseq [node nodes]
(when (some? node) (when (some? node)
(dom/remove-attribute node "transform")))))) (cond
(= (dom/get-tag-name node) "foreignObject")
;; The shape width/height will be automaticaly setup when the modifiers are applied
nil
:else
(dom/remove-attribute node "transform")))))))
(defn format-viewbox [vbox] (defn format-viewbox [vbox]
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0)) (str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))

View file

@ -17,21 +17,24 @@
;; --- Deprecated methods ;; --- Deprecated methods
(defn event->inner-text (defn event->inner-text
[e] [^js e]
(.-innerText (.-target e))) (when (some? e)
(.-innerText (.-target e))))
(defn event->value (defn event->value
[e] [^js e]
(.-value (.-target e))) (when (some? e)
(.-value (.-target e))))
(defn event->target (defn event->target
[e] [^js e]
(.-target e)) (when (some? e)
(.-target e)))
;; --- New methods ;; --- New methods
(defn set-html-title (defn set-html-title
[title] [^string title]
(set! (.-title globals/document) title)) (set! (.-title globals/document) title))
(defn set-page-style (defn set-page-style
@ -61,98 +64,117 @@
(dom/getElement id)) (dom/getElement id))
(defn get-elements-by-tag (defn get-elements-by-tag
[node tag] [^js node tag]
(.getElementsByTagName node tag)) (when (some? node)
(.getElementsByTagName node tag)))
(defn stop-propagation (defn stop-propagation
[e] [^js event]
(when e (when event
(.stopPropagation e))) (.stopPropagation event)))
(defn prevent-default (defn prevent-default
[e] [^js event]
(when e (when event
(.preventDefault e))) (.preventDefault event)))
(defn get-target (defn get-target
"Extract the target from event instance." "Extract the target from event instance."
[event] [^js event]
(.-target event)) (when (some? event)
(.-target event)))
(defn get-current-target (defn get-current-target
"Extract the current target from event instance (different from target "Extract the current target from event instance (different from target
when event triggered in a child of the subscribing element)." when event triggered in a child of the subscribing element)."
[event] [^js event]
(.-currentTarget event)) (when (some? event)
(.-currentTarget event)))
(defn get-parent (defn get-parent
[dom] [^js node]
(.-parentElement ^js dom)) (when (some? node)
(.-parentElement ^js node)))
(defn get-value (defn get-value
"Extract the value from dom node." "Extract the value from dom node."
[node] [^js node]
(.-value node)) (when (some? node)
(.-value node)))
(defn get-attribute (defn get-attribute
"Extract the value of one attribute of a dom node." "Extract the value of one attribute of a dom node."
[node attr-name] [^js node ^string attr-name]
(.getAttribute ^js node attr-name)) (when (some? node)
(.getAttribute ^js node attr-name)))
(def get-target-val (comp get-value get-target)) (def get-target-val (comp get-value get-target))
(defn click (defn click
"Click a node" "Click a node"
[node] [^js node]
(.click node)) (when (some? node)
(.click node)))
(defn get-files (defn get-files
"Extract the files from dom node." "Extract the files from dom node."
[node] [^js node]
(array-seq (.-files node))) (when (some? node)
(array-seq (.-files node))))
(defn checked? (defn checked?
"Check if the node that represents a radio "Check if the node that represents a radio
or checkbox is checked or not." or checkbox is checked or not."
[node] [^js node]
(.-checked node)) (when (some? node)
(.-checked node)))
(defn valid? (defn valid?
"Check if the node that is a form input "Check if the node that is a form input
has a valid value, against html5 form validation has a valid value, against html5 form validation
properties (required, min/max, pattern...)." properties (required, min/max, pattern...)."
[node] [^js node]
(.-valid (.-validity node))) (when (some? node)
(when-let [validity (.-validity node)]
(.-valid validity))))
(defn set-validity! (defn set-validity!
"Manually set the validity status of a node that "Manually set the validity status of a node that
is a form input. If the state is an empty string, is a form input. If the state is an empty string,
the input will be valid. If not, the string will the input will be valid. If not, the string will
be set as the error message." be set as the error message."
[node status] [^js node status]
(.setCustomValidity node status) (when (some? node)
(.reportValidity node)) (.setCustomValidity node status)
(.reportValidity node)))
(defn clean-value! (defn clean-value!
[node] [^js node]
(set! (.-value node) "")) (when (some? node)
(set! (.-value node) "")))
(defn set-value! (defn set-value!
[node value] [^js node value]
(set! (.-value ^js node) value)) (when (some? node)
(set! (.-value ^js node) value)))
(defn select-text! (defn select-text!
[node] [^js node]
(.select ^js node)) (when (some? node)
(.select ^js node)))
(defn ^boolean equals? (defn ^boolean equals?
[node-a node-b] [^js node-a ^js node-b]
(.isEqualNode ^js node-a node-b))
(or (and (nil? node-a) (nil? node-b))
(and (some? node-a)
(.isEqualNode ^js node-a node-b))))
(defn get-event-files (defn get-event-files
"Extract the files from event instance." "Extract the files from event instance."
[event] [^js event]
(get-files (get-target event))) (when (some? event)
(get-files (get-target event))))
(defn create-element (defn create-element
([tag] ([tag]
@ -161,50 +183,58 @@
(.createElementNS globals/document ns tag))) (.createElementNS globals/document ns tag)))
(defn set-html! (defn set-html!
[el html] [^js el html]
(set! (.-innerHTML el) html)) (when (some? el)
(set! (.-innerHTML el) html)))
(defn append-child! (defn append-child!
[el child] [^js el child]
(.appendChild ^js el child)) (when (some? el)
(.appendChild ^js el child)))
(defn get-first-child (defn get-first-child
[el] [^js el]
(.-firstChild el)) (when (some? el)
(.-firstChild el)))
(defn get-tag-name (defn get-tag-name
[el] [^js el]
(.-tagName el)) (when (some? el)
(.-tagName el)))
(defn get-outer-html (defn get-outer-html
[el] [^js el]
(.-outerHTML el)) (when (some? el)
(.-outerHTML el)))
(defn get-inner-text (defn get-inner-text
[el] [^js el]
(.-innerText el)) (when (some? el)
(.-innerText el)))
(defn query (defn query
[el query] [^js el ^string query]
(when (some? el) (when (some? el)
(.querySelector el query))) (.querySelector el query)))
(defn get-client-position (defn get-client-position
[event] [^js event]
(let [x (.-clientX event) (let [x (.-clientX event)
y (.-clientY event)] y (.-clientY event)]
(gpt/point x y))) (gpt/point x y)))
(defn get-offset-position (defn get-offset-position
[event] [^js event]
(let [x (.-offsetX event) (when (some? event)
y (.-offsetY event)] (let [x (.-offsetX event)
(gpt/point x y))) y (.-offsetY event)]
(gpt/point x y))))
(defn get-client-size (defn get-client-size
[node] [^js node]
{:width (.-clientWidth ^js node) (when (some? node)
:height (.-clientHeight ^js node)}) {:width (.-clientWidth ^js node)
:height (.-clientHeight ^js node)}))
(defn get-bounding-rect (defn get-bounding-rect
[node] [node]
@ -222,12 +252,12 @@
:height (.-innerHeight ^js js/window)}) :height (.-innerHeight ^js js/window)})
(defn focus! (defn focus!
[node] [^js node]
(when (some? node) (when (some? node)
(.focus node))) (.focus node)))
(defn blur! (defn blur!
[node] [^js node]
(when (some? node) (when (some? node)
(.blur node))) (.blur node)))
@ -245,8 +275,9 @@
:hint "seems like the current browser does not support fullscreen api."))) :hint "seems like the current browser does not support fullscreen api.")))
(defn ^boolean blob? (defn ^boolean blob?
[v] [^js v]
(instance? js/Blob v)) (when (some? v)
(instance? js/Blob v)))
(defn create-blob (defn create-blob
"Create a blob from content." "Create a blob from content."
@ -265,20 +296,24 @@
{:pre [(blob? b)]} {:pre [(blob? b)]}
(js/URL.createObjectURL b)) (js/URL.createObjectURL b))
(defn set-property! [node property value] (defn set-property! [^js node property value]
(.setAttribute node property value)) (when (some? node)
(.setAttribute node property value)))
(defn set-text! [node text] (defn set-text! [^js node text]
(set! (.-textContent node) text)) (when (some? node)
(set! (.-textContent node) text)))
(defn set-css-property! [node property value] (defn set-css-property! [^js node property value]
(.setProperty (.-style ^js node) property value)) (when (some? node)
(.setProperty (.-style ^js node) property value)))
(defn capture-pointer [event] (defn capture-pointer [^js event]
(-> event get-target (.setPointerCapture (.-pointerId event)))) (when (some? event)
(-> event get-target (.setPointerCapture (.-pointerId event)))))
(defn release-pointer [event] (defn release-pointer [^js event]
(when (.-pointerId event) (when (and (some? event) (.-pointerId event))
(-> event get-target (.releasePointerCapture (.-pointerId event))))) (-> event get-target (.releasePointerCapture (.-pointerId event)))))
(defn get-root [] (defn get-root []
@ -295,19 +330,23 @@
(partition 2 params)))) (partition 2 params))))
(defn ^boolean class? [node class-name] (defn ^boolean class? [node class-name]
(let [class-list (.-classList ^js node)] (when (some? node)
(.contains ^js class-list class-name))) (let [class-list (.-classList ^js node)]
(.contains ^js class-list class-name))))
(defn add-class! [node class-name] (defn add-class! [^js node class-name]
(let [class-list (.-classList ^js node)] (when (some? node)
(.add ^js class-list class-name))) (let [class-list (.-classList ^js node)]
(.add ^js class-list class-name))))
(defn remove-class! [node class-name] (defn remove-class! [^js node class-name]
(let [class-list (.-classList ^js node)] (when (some? node)
(.remove ^js class-list class-name))) (let [class-list (.-classList ^js node)]
(.remove ^js class-list class-name))))
(defn child? [node1 node2] (defn child? [^js node1 ^js node2]
(.contains ^js node2 ^js node1)) (when (some? node1)
(.contains ^js node2 ^js node1)))
(defn get-user-agent [] (defn get-user-agent []
(.-userAgent globals/navigator)) (.-userAgent globals/navigator))
@ -315,11 +354,13 @@
(defn get-active [] (defn get-active []
(.-activeElement globals/document)) (.-activeElement globals/document))
(defn active? [node] (defn active? [^js node]
(= (get-active) node)) (when (some? node)
(= (get-active) node)))
(defn get-data [^js node ^string attr] (defn get-data [^js node ^string attr]
(.getAttribute node (str "data-" attr))) (when (some? node)
(.getAttribute node (str "data-" attr))))
(defn mtype->extension [mtype] (defn mtype->extension [mtype]
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types ;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
@ -336,42 +377,53 @@
nil)) nil))
(defn set-attribute [^js node ^string attr value] (defn set-attribute [^js node ^string attr value]
(.setAttribute node attr value)) (when (some? node)
(.setAttribute node attr value)))
(defn remove-attribute [^js node ^string attr] (defn remove-attribute [^js node ^string attr]
(.removeAttribute node attr)) (when (some? node)
(.removeAttribute node attr)))
(defn get-scroll-pos (defn get-scroll-pos
[element] [^js element]
(.-scrollTop ^js element)) (when (some? element)
(.-scrollTop element)))
(defn set-scroll-pos! (defn set-scroll-pos!
[element scroll] [^js element scroll]
(obj/set! ^js element "scrollTop" scroll)) (when (some? element)
(obj/set! element "scrollTop" scroll)))
(defn scroll-into-view! (defn scroll-into-view!
([element] ([^js element]
(.scrollIntoView ^js element false)) (when (some? element)
([element scroll-top] (.scrollIntoView element false)))
(.scrollIntoView ^js element scroll-top)))
([^js element scroll-top]
(when (some? element)
(.scrollIntoView element scroll-top))))
(defn scroll-into-view-if-needed! (defn scroll-into-view-if-needed!
([element] ([^js element]
(.scrollIntoViewIfNeeded ^js element false)) (when (some? element)
([element scroll-top] (.scrollIntoViewIfNeeded ^js element false)))
(.scrollIntoViewIfNeeded ^js element scroll-top)))
([^js element scroll-top]
(when (some? element)
(.scrollIntoViewIfNeeded ^js element scroll-top))))
(defn is-in-viewport? (defn is-in-viewport?
[element] [^js element]
(let [rect (.getBoundingClientRect element) (when (some? element)
height (or (.-innerHeight js/window) (let [rect (.getBoundingClientRect element)
(.. js/document -documentElement -clientHeight)) height (or (.-innerHeight js/window)
width (or (.-innerWidth js/window) (.. js/document -documentElement -clientHeight))
(.. js/document -documentElement -clientWidth))] width (or (.-innerWidth js/window)
(and (>= (.-top rect) 0) (.. js/document -documentElement -clientWidth))]
(>= (.-left rect) 0) (and (>= (.-top rect) 0)
(<= (.-bottom rect) height) (>= (.-left rect) 0)
(<= (.-right rect) width)))) (<= (.-bottom rect) height)
(<= (.-right rect) width)))))
(defn trigger-download-uri (defn trigger-download-uri
[filename mtype uri] [filename mtype uri]