mirror of
https://github.com/penpot/penpot.git
synced 2025-06-07 13:01:40 +02:00
🐛 Fixes problems with masks rotation and some clipping problems
This commit is contained in:
parent
34af5e4563
commit
3a8a212432
10 changed files with 95 additions and 104 deletions
|
@ -860,11 +860,16 @@
|
||||||
(distinct))
|
(distinct))
|
||||||
shapes)))
|
shapes)))
|
||||||
(update-group [group objects]
|
(update-group [group objects]
|
||||||
(let [children (->> (if (:masked-group? group)
|
(let [children (->> group :shapes (map #(get objects %)))]
|
||||||
[(first (:shapes group))]
|
(if (:masked-group? group)
|
||||||
(:shapes group))
|
(let [mask (first children)]
|
||||||
(map #(get objects %)))]
|
(-> group
|
||||||
(gsh/update-group-selrect group children)))]
|
(merge (select-keys mask [:selrect :points]))
|
||||||
|
(assoc :x (-> mask :selrect :x)
|
||||||
|
:y (-> mask :selrect :y)
|
||||||
|
:width (-> mask :selrect :width)
|
||||||
|
:height (-> mask :selrect :height))))
|
||||||
|
(gsh/update-group-selrect group children))))]
|
||||||
|
|
||||||
(if page-id
|
(if page-id
|
||||||
(d/update-in-when data [:pages-index page-id :objects] reg-objects)
|
(d/update-in-when data [:pages-index page-id :objects] reg-objects)
|
||||||
|
|
|
@ -1146,8 +1146,11 @@
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(rx/of (dws/deselect-all)
|
(let [selected (get-in state [:workspace-local :selected])]
|
||||||
(dws/select-shape (:id shape))))))
|
(if (selected (:id shape))
|
||||||
|
(rx/empty)
|
||||||
|
(rx/of (dws/deselect-all)
|
||||||
|
(dws/select-shape (:id shape))))))))
|
||||||
|
|
||||||
(def hide-context-menu
|
(def hide-context-menu
|
||||||
(ptk/reify ::hide-context-menu
|
(ptk/reify ::hide-context-menu
|
||||||
|
@ -1478,14 +1481,20 @@
|
||||||
:id (:id group)
|
:id (:id group)
|
||||||
:operations [{:type :set
|
:operations [{:type :set
|
||||||
:attr :masked-group?
|
:attr :masked-group?
|
||||||
:val nil}]}]
|
:val nil}]}
|
||||||
|
{:type :reg-objects
|
||||||
|
:page-id page-id
|
||||||
|
:shapes [(:id group)]}]
|
||||||
|
|
||||||
uchanges [{:type :mod-obj
|
uchanges [{:type :mod-obj
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:id (:id group)
|
:id (:id group)
|
||||||
:operations [{:type :set
|
:operations [{:type :set
|
||||||
:attr :masked-group?
|
:attr :masked-group?
|
||||||
:val (:masked-group? group)}]}]]
|
:val (:masked-group? group)}]}
|
||||||
|
{:type :reg-objects
|
||||||
|
:page-id page-id
|
||||||
|
:shapes [(:id group)]}]]
|
||||||
|
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
|
@ -77,7 +77,10 @@
|
||||||
(fn []
|
(fn []
|
||||||
(let [keys [(events/listen js/window EventType.POPSTATE on-pop-state)
|
(let [keys [(events/listen js/window EventType.POPSTATE on-pop-state)
|
||||||
(events/listen js/document EventType.KEYDOWN handle-keydown)
|
(events/listen js/document EventType.KEYDOWN handle-keydown)
|
||||||
(events/listen js/document EventType.CLICK handle-click-outside)
|
|
||||||
|
;; Changing to js/document breaks the color picker
|
||||||
|
(events/listen (dom/get-root) EventType.CLICK handle-click-outside)
|
||||||
|
|
||||||
(events/listen js/document EventType.CONTEXTMENU handle-click-outside)]]
|
(events/listen js/document EventType.CONTEXTMENU handle-click-outside)]]
|
||||||
#(doseq [key keys]
|
#(doseq [key keys]
|
||||||
(events/unlistenByKey key)))))
|
(events/unlistenByKey key)))))
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
; The SVG standard does not implement yet the 'stroke-alignment'
|
; The SVG standard does not implement yet the 'stroke-alignment'
|
||||||
|
@ -25,16 +24,13 @@
|
||||||
elem-name (unchecked-get props "elem-name")
|
elem-name (unchecked-get props "elem-name")
|
||||||
;; {:keys [x y width height]} (geom/shape->rect-shape shape)
|
;; {:keys [x y width height]} (geom/shape->rect-shape shape)
|
||||||
{:keys [x y width height]} (:selrect shape)
|
{:keys [x y width height]} (:selrect shape)
|
||||||
mask-id (mf/use-ctx mask-id-ctx)
|
|
||||||
stroke-id (mf/use-var (uuid/next))
|
stroke-id (mf/use-var (uuid/next))
|
||||||
stroke-style (:stroke-style shape :none)
|
stroke-style (:stroke-style shape :none)
|
||||||
stroke-position (:stroke-alignment shape :center)]
|
stroke-position (:stroke-alignment shape :center)]
|
||||||
(cond
|
(cond
|
||||||
;; Center alignment (or no stroke): the default in SVG
|
;; Center alignment (or no stroke): the default in SVG
|
||||||
(or (= stroke-style :none) (= stroke-position :center))
|
(or (= stroke-style :none) (= stroke-position :center))
|
||||||
[:> elem-name (cond-> (obj/merge! #js {} base-props)
|
[:> elem-name (obj/merge! #js {} base-props)]
|
||||||
(some? mask-id)
|
|
||||||
(obj/merge! #js {:mask mask-id}))]
|
|
||||||
|
|
||||||
;; Inner alignment: display the shape with double width stroke,
|
;; Inner alignment: display the shape with double width stroke,
|
||||||
;; and clip the result with the original shape without stroke.
|
;; and clip the result with the original shape without stroke.
|
||||||
|
@ -54,15 +50,10 @@
|
||||||
shape-props (-> (obj/merge! #js {} base-props)
|
shape-props (-> (obj/merge! #js {} base-props)
|
||||||
(obj/merge! #js {:strokeWidth (* stroke-width 2)
|
(obj/merge! #js {:strokeWidth (* stroke-width 2)
|
||||||
:clipPath (str "url('#" clip-id "')")}))]
|
:clipPath (str "url('#" clip-id "')")}))]
|
||||||
(if (nil? mask-id)
|
[:*
|
||||||
[:*
|
[:> "clipPath" #js {:id clip-id}
|
||||||
[:> "clipPath" #js {:id clip-id}
|
[:> elem-name clip-props]]
|
||||||
[:> elem-name clip-props]]
|
[:> elem-name shape-props]])
|
||||||
[:> elem-name shape-props]]
|
|
||||||
[:g {:mask mask-id}
|
|
||||||
[:> "clipPath" #js {:id clip-id}
|
|
||||||
[:> elem-name clip-props]]
|
|
||||||
[:> elem-name shape-props]]))
|
|
||||||
|
|
||||||
;; Outer alingmnent: display the shape in two layers. One
|
;; Outer alingmnent: display the shape in two layers. One
|
||||||
;; without stroke (only fill), and another one only with stroke
|
;; without stroke (only fill), and another one only with stroke
|
||||||
|
@ -100,17 +91,10 @@
|
||||||
:fill "none"
|
:fill "none"
|
||||||
:fillOpacity 0
|
:fillOpacity 0
|
||||||
:mask (str "url('#" stroke-mask-id "')")}))]
|
:mask (str "url('#" stroke-mask-id "')")}))]
|
||||||
(if (nil? mask-id)
|
[:*
|
||||||
[:*
|
[:mask {:id stroke-mask-id}
|
||||||
[:mask {:id mask-id}
|
[:> elem-name mask-props1]
|
||||||
[:> elem-name mask-props1]
|
[:> elem-name mask-props2]]
|
||||||
[:> elem-name mask-props2]]
|
[:> elem-name shape-props1]
|
||||||
[:> elem-name shape-props1]
|
[:> elem-name shape-props2]]))))
|
||||||
[:> elem-name shape-props2]]
|
|
||||||
[:g {:mask mask-id}
|
|
||||||
[:mask {:id stroke-mask-id}
|
|
||||||
[:> elem-name mask-props1]
|
|
||||||
[:> elem-name mask-props2]]
|
|
||||||
[:> elem-name shape-props1]
|
|
||||||
[:> elem-name shape-props2]])))))
|
|
||||||
|
|
||||||
|
|
|
@ -18,56 +18,54 @@
|
||||||
|
|
||||||
(mf/defc linear-gradient [{:keys [id gradient shape]}]
|
(mf/defc linear-gradient [{:keys [id gradient shape]}]
|
||||||
(let [{:keys [x y width height]} shape]
|
(let [{:keys [x y width height]} shape]
|
||||||
[:defs
|
[:linearGradient {:id id
|
||||||
[:linearGradient {:id id
|
:x1 (:start-x gradient)
|
||||||
:x1 (:start-x gradient)
|
:y1 (:start-y gradient)
|
||||||
:y1 (:start-y gradient)
|
:x2 (:end-x gradient)
|
||||||
:x2 (:end-x gradient)
|
:y2 (:end-y gradient)}
|
||||||
:y2 (:end-y gradient)}
|
(for [{:keys [offset color opacity]} (:stops gradient)]
|
||||||
(for [{:keys [offset color opacity]} (:stops gradient)]
|
[:stop {:key (str id "-stop-" offset)
|
||||||
[:stop {:key (str id "-stop-" offset)
|
:offset (or offset 0)
|
||||||
:offset (or offset 0)
|
:stop-color color
|
||||||
:stop-color color
|
:stop-opacity opacity}])]))
|
||||||
:stop-opacity opacity}])]]))
|
|
||||||
|
|
||||||
(mf/defc radial-gradient [{:keys [id gradient shape]}]
|
(mf/defc radial-gradient [{:keys [id gradient shape]}]
|
||||||
(let [{:keys [x y width height]} shape]
|
(let [{:keys [x y width height]} shape]
|
||||||
[:defs
|
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
|
||||||
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
|
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
||||||
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
(+ y (* height (:start-y gradient))))
|
||||||
(+ y (* height (:start-y gradient))))
|
|
||||||
|
|
||||||
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
|
||||||
(* height (:start-y gradient)))
|
|
||||||
(gpt/point (* width (:end-x gradient))
|
|
||||||
(* height (:end-y gradient))))
|
|
||||||
|
|
||||||
angle (gpt/angle gradient-vec
|
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
||||||
(gpt/point 1 0))
|
(* height (:start-y gradient)))
|
||||||
|
(gpt/point (* width (:end-x gradient))
|
||||||
|
(* height (:end-y gradient))))
|
||||||
|
|
||||||
shape-height-vec (gpt/point 0 (/ height 2))
|
angle (gpt/angle gradient-vec
|
||||||
|
(gpt/point 1 0))
|
||||||
|
|
||||||
scale-factor-y (/ (gpt/length gradient-vec) (/ height 2))
|
shape-height-vec (gpt/point 0 (/ height 2))
|
||||||
scale-factor-x (* scale-factor-y (:width gradient))
|
|
||||||
|
|
||||||
scale-vec (gpt/point (* scale-factor-y (/ height 2))
|
scale-factor-y (/ (gpt/length gradient-vec) (/ height 2))
|
||||||
(* scale-factor-x (/ width 2)))
|
scale-factor-x (* scale-factor-y (:width gradient))
|
||||||
|
|
||||||
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
|
scale-vec (gpt/point (* scale-factor-y (/ height 2))
|
||||||
tr-rotate (str/fmt "rotate(%s)" angle)
|
(* scale-factor-x (/ width 2)))
|
||||||
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
|
|
||||||
transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)]
|
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
|
||||||
[:radialGradient {:id id
|
tr-rotate (str/fmt "rotate(%s)" angle)
|
||||||
:cx 0
|
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
|
||||||
:cy 0
|
transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)]
|
||||||
:r 1
|
[:radialGradient {:id id
|
||||||
:gradientUnits "userSpaceOnUse"
|
:cx 0
|
||||||
:gradientTransform transform}
|
:cy 0
|
||||||
(for [{:keys [offset color opacity]} (:stops gradient)]
|
:r 1
|
||||||
[:stop {:key (str id "-stop-" offset)
|
:gradientUnits "userSpaceOnUse"
|
||||||
:offset (or offset 0)
|
:gradientTransform transform}
|
||||||
:stop-color color
|
(for [{:keys [offset color opacity]} (:stops gradient)]
|
||||||
:stop-opacity opacity}])])]))
|
[:stop {:key (str id "-stop-" offset)
|
||||||
|
:offset (or offset 0)
|
||||||
|
:stop-color color
|
||||||
|
:stop-opacity opacity}])])))
|
||||||
|
|
||||||
(mf/defc gradient
|
(mf/defc gradient
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
|
|
@ -14,17 +14,16 @@
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.common.geom.shapes :as geom]))
|
[app.common.geom.shapes :as geom]))
|
||||||
|
|
||||||
(def mask-id-ctx (mf/create-context nil))
|
|
||||||
|
|
||||||
(defn group-shape
|
(defn group-shape
|
||||||
[shape-wrapper]
|
[shape-wrapper]
|
||||||
(mf/fnc group-shape
|
(mf/fnc group-shape
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [frame (unchecked-get props "frame")
|
(let [frame (unchecked-get props "frame")
|
||||||
shape (unchecked-get props "shape")
|
shape (unchecked-get props "shape")
|
||||||
childs (unchecked-get props "childs")
|
childs (unchecked-get props "childs")
|
||||||
expand-mask (unchecked-get props "expand-mask")
|
expand-mask (unchecked-get props "expand-mask")
|
||||||
|
pointer-events (unchecked-get props "pointer-events")
|
||||||
mask (if (and (:masked-group? shape) (not expand-mask))
|
mask (if (and (:masked-group? shape) (not expand-mask))
|
||||||
(first childs)
|
(first childs)
|
||||||
nil)
|
nil)
|
||||||
|
@ -33,7 +32,9 @@
|
||||||
childs)
|
childs)
|
||||||
{:keys [id x y width height]} shape
|
{:keys [id x y width height]} shape
|
||||||
transform (geom/transform-matrix shape)]
|
transform (geom/transform-matrix shape)]
|
||||||
[:g
|
[:g.group {:pointer-events pointer-events
|
||||||
|
:mask (when (and mask (not expand-mask))
|
||||||
|
(str/fmt "url(#%s)" (:id mask)))}
|
||||||
(when mask
|
(when mask
|
||||||
[:defs
|
[:defs
|
||||||
[:mask {:id (:id mask)
|
[:mask {:id (:id mask)
|
||||||
|
@ -41,11 +42,10 @@
|
||||||
:height height}
|
:height height}
|
||||||
[:& shape-wrapper {:frame frame
|
[:& shape-wrapper {:frame frame
|
||||||
:shape mask}]]])
|
:shape mask}]]])
|
||||||
[:& (mf/provider mask-id-ctx) {:value (str/fmt "url(#%s)" (:id mask))}
|
(for [item childs]
|
||||||
(for [item childs]
|
[:& shape-wrapper {:frame frame
|
||||||
[:& shape-wrapper {:frame frame
|
:shape item
|
||||||
:shape item
|
:key (:id item)}])])))
|
||||||
:key (:id item)}])]
|
|
||||||
])))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.data.fetch :as df]
|
[app.main.data.fetch :as df]
|
||||||
|
@ -27,7 +26,6 @@
|
||||||
{:keys [id x y width height rotation metadata]} shape
|
{:keys [id x y width height rotation metadata]} shape
|
||||||
uri (cfg/resolve-media-path (:path metadata))
|
uri (cfg/resolve-media-path (:path metadata))
|
||||||
embed-resources? (mf/use-ctx muc/embed-ctx)
|
embed-resources? (mf/use-ctx muc/embed-ctx)
|
||||||
mask-id (mf/use-ctx mask-id-ctx)
|
|
||||||
data-uri (mf/use-state (when (not embed-resources?) uri))]
|
data-uri (mf/use-state (when (not embed-resources?) uri))]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
|
@ -45,8 +43,7 @@
|
||||||
:transform transform
|
:transform transform
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:preserveAspectRatio "none"
|
:preserveAspectRatio "none"}))]
|
||||||
:mask mask-id}))]
|
|
||||||
(if (nil? @data-uri)
|
(if (nil? @data-uri)
|
||||||
[:> "rect" (obj/merge!
|
[:> "rect" (obj/merge!
|
||||||
props
|
props
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
|
[app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
|
||||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.util.geom.path :as ugp]))
|
[app.util.geom.path :as ugp]))
|
||||||
|
@ -27,7 +26,6 @@
|
||||||
background? (unchecked-get props "background?")
|
background? (unchecked-get props "background?")
|
||||||
;; {:keys [id x y width height]} (geom/shape->rect-shape shape)
|
;; {:keys [id x y width height]} (geom/shape->rect-shape shape)
|
||||||
{:keys [id x y width height]} (:selrect shape)
|
{:keys [id x y width height]} (:selrect shape)
|
||||||
mask-id (mf/use-ctx mask-id-ctx)
|
|
||||||
transform (geom/transform-matrix shape)
|
transform (geom/transform-matrix shape)
|
||||||
pdata (ugp/content->path (:content shape))
|
pdata (ugp/content->path (:content shape))
|
||||||
props (-> (attrs/extract-style-attrs shape)
|
props (-> (attrs/extract-style-attrs shape)
|
||||||
|
@ -35,7 +33,7 @@
|
||||||
#js {:transform transform
|
#js {:transform transform
|
||||||
:d pdata}))]
|
:d pdata}))]
|
||||||
(if background?
|
(if background?
|
||||||
[:g {:mask mask-id}
|
[:g
|
||||||
[:path {:stroke "transparent"
|
[:path {:stroke "transparent"
|
||||||
:fill "transparent"
|
:fill "transparent"
|
||||||
:stroke-width "20px"
|
:stroke-width "20px"
|
||||||
|
@ -45,6 +43,5 @@
|
||||||
:elem-name "path"}]]
|
:elem-name "path"}]]
|
||||||
[:& shape-custom-stroke {:shape shape
|
[:& shape-custom-stroke {:shape shape
|
||||||
:base-props props
|
:base-props props
|
||||||
:mask mask-id
|
|
||||||
:elem-name "path"}])))
|
:elem-name "path"}])))
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
|
@ -90,7 +89,6 @@
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
selected? (unchecked-get props "selected?")
|
selected? (unchecked-get props "selected?")
|
||||||
grow-type (:grow-type shape)
|
grow-type (:grow-type shape)
|
||||||
mask-id (mf/use-ctx mask-id-ctx)
|
|
||||||
{:keys [id x y width height content]} shape]
|
{:keys [id x y width height content]} shape]
|
||||||
[:foreignObject {:x x
|
[:foreignObject {:x x
|
||||||
:y y
|
:y y
|
||||||
|
@ -99,7 +97,6 @@
|
||||||
:transform (geom/transform-matrix shape)
|
:transform (geom/transform-matrix shape)
|
||||||
:width (if (#{:auto-width} grow-type) 10000 width)
|
:width (if (#{:auto-width} grow-type) 10000 width)
|
||||||
:height (if (#{:auto-height :auto-width} grow-type) 10000 height)
|
:height (if (#{:auto-height :auto-width} grow-type) 10000 height)
|
||||||
:mask mask-id
|
|
||||||
:ref ref
|
:ref ref
|
||||||
:pointer-events "none"}
|
:pointer-events "none"}
|
||||||
[:& text-content {:shape shape
|
[:& text-content {:shape shape
|
||||||
|
|
|
@ -80,7 +80,8 @@
|
||||||
{:frame frame
|
{:frame frame
|
||||||
:shape shape
|
:shape shape
|
||||||
:childs childs
|
:childs childs
|
||||||
:expand-mask is-mask-selected?}]
|
:expand-mask is-mask-selected?
|
||||||
|
:pointer-events (when (not is-child-selected?) "none")}]
|
||||||
|
|
||||||
(when-not is-child-selected?
|
(when-not is-child-selected?
|
||||||
[:rect.group-actions
|
[:rect.group-actions
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue