♻️ Convert viewport outline/snap/rulers/pixel-overlay/debug/selection components to * suffix

This commit is contained in:
Andrey Antukh 2026-04-02 22:46:31 +00:00
parent 043e7bb8cd
commit ead24e5807
9 changed files with 144 additions and 239 deletions

View File

@ -354,10 +354,10 @@
:zoom zoom}])
(when picking-color?
[:& pixel-overlay/pixel-overlay {:vport vport
:vbox vbox
:layout layout
:viewport-ref viewport-ref}])]
[:> pixel-overlay/pixel-overlay* {:vport vport
:vbox vbox
:layout layout
:viewport-ref viewport-ref}])]
[:svg
{:id "render"
@ -453,7 +453,7 @@
(last))
outlined-frame (get objects outlined-frame-id)]
[:*
[:& outline/shape-outlines
[:> outline/shape-outlines*
{:objects base-objects
:hover #{outlined-frame-id}
:zoom zoom
@ -461,13 +461,13 @@
(when (ctl/any-layout? outlined-frame)
[:g.ghost-outline
[:& outline/shape-outlines
[:> outline/shape-outlines*
{:objects base-objects
:selected selected
:zoom zoom}]])]))
(when show-outlines?
[:& outline/shape-outlines
[:> outline/shape-outlines*
{:objects base-objects
:selected selected
:hover #{(:id @hover) @frame-hover}
@ -599,7 +599,7 @@
:focus focus}])
(when show-snap-distance?
[:& snap-distances/snap-distances
[:> snap-distances/snap-distances*
{:layout layout
:zoom zoom
:transform transform
@ -625,14 +625,14 @@
{:page-id page-id}])
(when-not hide-ui?
[:& rulers/rulers
[:> rulers/rulers*
{:zoom zoom
:zoom-inverse zoom-inverse
:vbox vbox
:selected-shapes selected-shapes
:offset-x offset-x
:offset-y offset-y
:show-rulers? show-rulers?}])
:show-rulers show-rulers?}])
(when (and show-rulers? show-grids?)
[:> guides/viewport-guides*
@ -645,34 +645,34 @@
;; DEBUG LAYOUT DROP-ZONES
(when (dbg/enabled? :layout-drop-zones)
[:& wvd/debug-drop-zones {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
[:> wvd/debug-drop-zones* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :layout-content-bounds)
[:& wvd/debug-content-bounds {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
[:> wvd/debug-content-bounds* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :layout-lines)
[:& wvd/debug-layout-lines {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :parent-bounds)
[:& wvd/debug-parent-bounds {:selected-shapes selected-shapes
[:> wvd/debug-layout-lines* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :parent-bounds)
[:> wvd/debug-parent-bounds* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :grid-layout)
[:& wvd/debug-grid-layout {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
[:> wvd/debug-grid-layout* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when show-selection-handlers?
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}

View File

@ -21,17 +21,10 @@
[rumext.v2 :as mf]))
;; Helper to debug the bounds when set the "hug" content property
(mf/defc debug-content-bounds
(mf/defc debug-content-bounds*
"Debug component to show the auto-layout drop areas"
{::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
[{:keys [objects zoom selected-shapes hover-top-frame-id]}]
(let [selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
@ -72,17 +65,10 @@
:r (/ 4 zoom)
:style {:fill "red"}}])]]))))
(mf/defc debug-layout-lines
(mf/defc debug-layout-lines*
"Debug component to show the auto-layout drop areas"
{::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
[{:keys [objects zoom selected-shapes hover-top-frame-id]}]
(let [selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
@ -117,18 +103,11 @@
[:polygon {:points (->> points (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
:style {:stroke "red" :stroke-width (/ 2 zoom) :stroke-dasharray (dm/str (/ 10 zoom) " " (/ 5 zoom))}}]]))]))))
(mf/defc debug-drop-zones
(mf/defc debug-drop-zones*
"Debug component to show the auto-layout drop areas"
{::mf/wrap [#(mf/memo' % (mf/check-props ["objects" "selected-shapes" "hover-top-frame-id"]))]
::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
zoom (unchecked-get props "objects")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
{::mf/wrap [#(mf/memo' % (mf/check-props ["objects" "selected-shapes" "hover-top-frame-id"]))]}
[{:keys [objects zoom selected-shapes hover-top-frame-id]}]
(let [selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
@ -159,15 +138,10 @@
:fill "black"}
(:index drop-area)]])]))))
(mf/defc shape-parent-bound
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "parent"]))]
::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
parent (unchecked-get props "parent")
zoom (unchecked-get props "zoom")
[i1 i2 i3 i4] (gpo/parent-coords-bounds (:points shape) (:points parent))]
(mf/defc shape-parent-bound*
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "parent"]))]}
[{:keys [shape parent zoom]}]
(let [[i1 i2 i3 i4] (gpo/parent-coords-bounds (:points shape) (:points parent))]
[:*
[:polygon {:points (->> [i1 i2 i3 i4] (map #(dm/fmt "%,%" (:x %) (:y %))) (str/join ","))
:style {:fill "none" :stroke "red" :stroke-width (/ 1 zoom)}}]
@ -183,16 +157,9 @@
:y2 (:y i4)
:style {:stroke "blue" :stroke-width (/ 1 zoom)}}]]))
(mf/defc debug-parent-bounds
{::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
(mf/defc debug-parent-bounds*
[{:keys [objects zoom selected-shapes hover-top-frame-id]}]
(let [selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
@ -207,10 +174,10 @@
[:g.debug-parent-bounds {:pointer-events "none"}
(for [[idx child] (d/enumerate children)]
[:*
[:> shape-parent-bound {:key (dm/str "bound-" idx)
:zoom zoom
:shape child
:parent parent}]
[:> shape-parent-bound* {:key (dm/str "bound-" idx)
:zoom zoom
:shape child
:parent parent}]
(let [child-bounds (:points child)
points
@ -223,16 +190,9 @@
:r (/ 2 zoom)
:style {:fill "red"}}]))])]))))
(mf/defc debug-grid-layout
{::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
(mf/defc debug-grid-layout*
[{:keys [objects zoom selected-shapes hover-top-frame-id]}]
(let [selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
@ -277,13 +237,9 @@
:style {:stroke "red"
:stroke-width (/ 1 zoom)}}]))]))))
(mf/defc debug-text-wasm-position-data
{::mf/wrap-props false}
[props]
(let [zoom (unchecked-get props "zoom")
selected-shapes (unchecked-get props "selected-shapes")
selected-text
(mf/defc debug-text-wasm-position-data*
[{:keys [zoom selected-shapes]}]
(let [selected-text
(when (and (= (count selected-shapes) 1) (= :text (-> selected-shapes first :type)))
(first selected-shapes))

View File

@ -19,7 +19,7 @@
[app.main.store :as st]
[app.main.ui.context :as muc]
[app.main.ui.shapes.embed :as embed]
[app.main.ui.workspace.viewport.outline :refer [outline]]
[app.main.ui.workspace.viewport.outline :refer [outline*]]
[app.util.dom :as dom]
[cuerdas.core :as str]
[okulary.core :as l]
@ -186,9 +186,8 @@
:d pdata}]
(when dest-shape
[:& outline {:zoom zoom
:shape dest-shape
:color "var(--color-accent-tertiary)"}])
[:> outline* {:zoom zoom
:shape dest-shape}])
[:> interaction-marker* {:index index
:x orig-x

View File

@ -19,14 +19,10 @@
[clojure.set :as set]
[rumext.v2 :as mf]))
(mf/defc outline
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
modifier (unchecked-get props "modifier")
zoom (d/nilv (unchecked-get props "zoom") 1)
shape (gsh/transform-shape shape (:modifiers modifier))
(mf/defc outline*
[{:keys [shape modifier zoom]
:or {zoom 1}}]
(let [shape (gsh/transform-shape shape (:modifiers modifier))
transform (gsh/transform-str shape)
;; NOTE: that we don't use mf/deref to avoid a repaint dependency here
@ -95,35 +91,21 @@
[:> outline-type props]))
(mf/defc shape-outlines-render
{::mf/wrap-props false
::mf/wrap [#(mf/memo' % (mf/check-props ["shapes" "zoom" "modifiers"]))]}
[props]
(let [shapes (unchecked-get props "shapes")
zoom (unchecked-get props "zoom")
modifiers (unchecked-get props "modifiers")]
(mf/defc shape-outlines-render*
{::mf/wrap [#(mf/memo' % (mf/check-props ["shapes" "zoom" "modifiers"]))]}
[{:keys [shapes zoom modifiers]}]
(for [shape shapes]
(let [shape-id (dm/get-prop shape :id)
modifier (get modifiers shape-id)]
[:> outline* {:key (dm/str "outline-" shape-id)
:shape shape
:modifier modifier
:zoom zoom}])))
(for [shape shapes]
(let [shape-id (dm/get-prop shape :id)
modifier (get modifiers shape-id)]
[:& outline {:key (dm/str "outline-" shape-id)
:shape shape
:modifier modifier
:zoom zoom}]))))
(mf/defc shape-outlines
{::mf/wrap-props false}
[props]
(let [selected (or (obj/get props "selected") #{})
hover (or (obj/get props "hover") #{})
highlighted (or (obj/get props "highlighted") #{})
objects (obj/get props "objects")
edition (obj/get props "edition")
zoom (obj/get props "zoom")
modifiers (obj/get props "modifiers")
lookup (d/getf objects)
(mf/defc shape-outlines*
[{:keys [selected hover highlighted objects edition zoom modifiers]
:or {selected #{} hover #{} highlighted #{}}}]
(let [lookup (d/getf objects)
edition? (fn [o] (= edition o))
shapes (-> #{}
@ -139,6 +121,6 @@
shapes (hooks/use-equal-memo shapes)]
[:g.outlines.blurrable
[:& shape-outlines-render {:shapes shapes
:zoom zoom
:modifiers modifiers}]]))
[:> shape-outlines-render* {:shapes shapes
:zoom zoom
:modifiers modifiers}]]))

View File

@ -91,13 +91,9 @@
(st/emit! (dwc/pick-color [r g b a]))))))))
(mf/defc pixel-overlay
{::mf/wrap-props false}
[props]
(let [vport (unchecked-get props "vport")
viewport-ref (unchecked-get props "viewport-ref")
viewport-node (mf/ref-val viewport-ref)
(mf/defc pixel-overlay*
[{:keys [vport viewport-ref]}]
(let [viewport-node (mf/ref-val viewport-ref)
canvas (get-offscreen-canvas (:width vport) (:height vport))
canvas-context (.getContext canvas "2d" #js {:willReadFrequently true})
@ -259,7 +255,6 @@
(st/emit! (dwc/pick-color [r g b a]))))))))
(mf/defc pixel-overlay-wasm*
{::mf/wrap-props false}
[{:keys [viewport-ref canvas-ref]}]
(let [viewport-node (mf/ref-val viewport-ref)
canvas (mf/ref-val canvas-ref)

View File

@ -12,7 +12,6 @@
[app.common.math :as mth]
[app.main.ui.formats :as fmt]
[app.main.ui.hooks :as hooks]
[app.util.object :as obj]
[rumext.v2 :as mf]))
(def rulers-pos 15)
@ -311,18 +310,10 @@
:fill selection-area-color}}
(fmt/format-number (- (:y1 selection-rect) offset-y))]])])
(mf/defc rulers
{::mf/wrap-props false
::mf/wrap [#(mf/memo' % (mf/check-props ["zoom" "vbox" "selected-shapes" "show-rulers?"]))]}
[props]
(let [zoom (obj/get props "zoom")
zoom-inverse (obj/get props "zoom-inverse")
vbox (obj/get props "vbox")
offset-x (obj/get props "offset-x")
offset-y (obj/get props "offset-y")
selected-shapes (-> (obj/get props "selected-shapes")
(hooks/use-equal-memo))
show-rulers? (obj/get props "show-rulers?")
(mf/defc rulers*
{::mf/wrap [#(mf/memo' % (mf/check-props ["zoom" "vbox" "selected-shapes" "show-rulers"]))]}
[{:keys [zoom zoom-inverse vbox offset-x offset-y selected-shapes show-rulers]}]
(let [selected-shapes (hooks/use-equal-memo selected-shapes)
selection-rect
(mf/use-memo
@ -333,14 +324,14 @@
(when (some? vbox)
[:g.viewport-frame {:pointer-events "none"}
[:> viewport-frame*
{:show-rulers show-rulers?
{:show-rulers show-rulers
:zoom zoom
:zoom-inverse zoom-inverse
:vbox vbox
:offset-x offset-x
:offset-y offset-y}]
(when (and show-rulers? (some? selection-rect))
(when (and show-rulers (some? selection-rect))
[:> selection-area*
{:zoom zoom
:zoom-inverse zoom-inverse

View File

@ -41,8 +41,7 @@
(def min-selrect-width 10)
(def min-selrect-height 10)
(mf/defc selection-rect
{::mf/wrap-props false}
(mf/defc selection-rect*
[{:keys [transform rect zoom color on-move-selected on-context-menu]}]
(let [x (dm/get-prop rect :x)
y (dm/get-prop rect :y)
@ -181,8 +180,7 @@
:position :bottom-left
:props #js {:cx x :cy (+ y height) :align align}}))))
(mf/defc rotation-handler
{::mf/wrap-props false}
(mf/defc rotation-handler*
[{:keys [cx cy transform position rotation zoom on-rotate] :as props}]
(let [size (/ rotation-handler-size zoom)
delta-x (if (or (= position :top-left)
@ -211,8 +209,7 @@
:transform (dm/str transform)
:on-pointer-down on-rotate}]))
(mf/defc resize-point-handler
{::mf/wrap-props false}
(mf/defc resize-point-handler*
[{:keys [cx cy zoom position on-resize transform rotation color align scale-text]}]
(let [cursor (if (or (= position :top-left)
(= position :bottom-right))
@ -276,8 +273,7 @@
:stroke-width 0}}])]))
;; The side handler is always rendered horizontally and then rotated
(mf/defc resize-side-handler
{::mf/wrap-props false}
(mf/defc resize-side-handler*
[{:keys [x y length align angle zoom position rotation transform on-resize color show-handler scale-text shape-id shape-type]}]
(let [height (/ resize-side-height zoom)
offset-y (if (= align :outside) (- height) (- (/ height 2)))
@ -338,12 +334,12 @@
(= transform-type :rotate))))
[:g.controls {:pointer-events (if ^boolean disabled "none" "visible")}
;; Selection rect
[:& selection-rect {:rect selrect
:transform transform
:zoom zoom
:color color
:on-move-selected on-move-selected
:on-context-menu on-context-menu}]])))
[:> selection-rect* {:rect selrect
:transform transform
:zoom zoom
:color color
:on-move-selected on-move-selected
:on-context-menu on-context-menu}]])))
(mf/defc controls-handlers*
{::mf/private true}
@ -406,14 +402,13 @@
:shape-type (dm/get-prop shape :type)}
props)]
(case type
:rotation [:> rotation-handler props]
:resize-point [:> resize-point-handler props]
:resize-side [:> resize-side-handler props])))])))
:rotation [:> rotation-handler* props]
:resize-point [:> resize-point-handler* props]
:resize-side [:> resize-side-handler* props])))])))
;; --- Selection Handlers (Component)
(mf/defc text-edition-selection
{::mf/wrap-props false}
(mf/defc text-edition-selection*
[{:keys [shape color zoom]}]
(let [x (dm/get-prop shape :x)
y (dm/get-prop shape :y)
@ -573,7 +568,7 @@
(and (cfh/text-shape? shape)
(= edition shape-id))
[:& text-edition-selection
[:> text-edition-selection*
{:shape shape
:zoom zoom
:color color}]

View File

@ -227,17 +227,9 @@
(rx/combine-latest (query-side lt-side)
(query-side gt-side))))
(mf/defc shape-distance
{::mf/wrap-props false}
[props]
(let [frame (unchecked-get props "frame")
selrect (unchecked-get props "selrect")
page-id (unchecked-get props "page-id")
zoom (unchecked-get props "zoom")
coord (unchecked-get props "coord")
selected (unchecked-get props "selected")
subject (mf/use-memo #(rx/subject))
(mf/defc shape-distance*
[{:keys [frame selrect page-id zoom coord selected]}]
(let [subject (mf/use-memo #(rx/subject))
lt-shapes* (mf/use-state nil)
@ -279,27 +271,22 @@
:coord coord
:zoom zoom}])))
(mf/defc snap-distances
{::mf/wrap-props false}
[props]
(let [page-id (unchecked-get props "page-id")
zoom (unchecked-get props "zoom")
selected (unchecked-get props "selected")
selected-shapes (unchecked-get props "selected-shapes")
frame-id (-> selected-shapes first :frame-id)
(mf/defc snap-distances*
[{:keys [page-id zoom selected selected-shapes]}]
(let [frame-id (-> selected-shapes first :frame-id)
frame (mf/deref (refs/object-by-id frame-id))
selrect (gsh/shapes->rect selected-shapes)]
(when-not (ctl/any-layout? frame)
[:g.distance
[:& shape-distance
[:> shape-distance*
{:selrect selrect
:page-id page-id
:frame frame
:zoom zoom
:coord :x
:selected selected}]
[:& shape-distance
[:> shape-distance*
{:selrect selrect
:page-id page-id
:frame frame

View File

@ -534,20 +534,20 @@
(last))
outlined-frame (get objects outlined-frame-id)]
[:*
[:& outline/shape-outlines
[:> outline/shape-outlines*
{:objects objects-for-outlines
:hover #{outlined-frame-id}
:zoom zoom}]
(when (ctl/any-layout? outlined-frame)
[:g.ghost-outline.blurrable
[:& outline/shape-outlines
[:> outline/shape-outlines*
{:objects objects-for-outlines
:selected selected
:zoom zoom}]])]))
(when show-outlines?
[:& outline/shape-outlines
[:> outline/shape-outlines*
{:objects objects-for-outlines
:selected selected
:hover #{(:id @hover) @frame-hover}
@ -658,7 +658,7 @@
:focus focus}])
(when show-snap-distance?
[:& snap-distances/snap-distances
[:> snap-distances/snap-distances*
{:layout layout
:zoom zoom
:transform transform
@ -679,14 +679,14 @@
{:page-id page-id}])
(when-not hide-ui?
[:& rulers/rulers
[:> rulers/rulers*
{:zoom zoom
:zoom-inverse zoom-inverse
:vbox vbox
:selected-shapes selected-shapes
:offset-x offset-x
:offset-y offset-y
:show-rulers? show-rulers?}])
:show-rulers show-rulers?}])
(when (and show-rulers? show-grids?)
[:> guides/viewport-guides*
@ -699,37 +699,37 @@
;; DEBUG LAYOUT DROP-ZONES
(when (dbg/enabled? :layout-drop-zones)
[:& wvd/debug-drop-zones {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :layout-content-bounds)
[:& wvd/debug-content-bounds {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :layout-lines)
[:& wvd/debug-layout-lines {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :parent-bounds)
[:& wvd/debug-parent-bounds {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :grid-layout)
[:& wvd/debug-grid-layout {:selected-shapes selected-shapes
[:> wvd/debug-drop-zones* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :layout-content-bounds)
[:> wvd/debug-content-bounds* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :layout-lines)
[:> wvd/debug-layout-lines* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :parent-bounds)
[:> wvd/debug-parent-bounds* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :grid-layout)
[:> wvd/debug-grid-layout* {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (dbg/enabled? :text-outline)
[:& wvd/debug-text-wasm-position-data
[:> wvd/debug-text-wasm-position-data*
{:selected-shapes selected-shapes
:objects base-objects
:zoom zoom}])