mirror of
https://github.com/penpot/penpot.git
synced 2025-07-31 05:08:24 +02:00
✨ Adds grid column/row sizing without spanned tracks
This commit is contained in:
parent
4bfe81f771
commit
2df40ad767
5 changed files with 424 additions and 123 deletions
|
@ -24,6 +24,13 @@
|
|||
(defn apply-to-point [result next-fn]
|
||||
(conj result (next-fn (last result))))
|
||||
|
||||
(defn format-size [{:keys [type value]}]
|
||||
(case type
|
||||
:fixed (str value "PX")
|
||||
:percent (str value "%")
|
||||
:flex (str value "FR")
|
||||
:auto "AUTO"))
|
||||
|
||||
(mf/defc track-marker
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
@ -57,6 +64,8 @@
|
|||
:width (/ 26.26 zoom)
|
||||
:height (/ 32 zoom)
|
||||
:font-size (/ 16 zoom)
|
||||
:font-family "worksans"
|
||||
:font-weight 600
|
||||
:text-anchor "middle"
|
||||
:dominant-baseline "middle"
|
||||
:style {:fill "var(--color-distance)"}}
|
||||
|
@ -114,9 +123,7 @@
|
|||
handle-click
|
||||
(mf/use-callback
|
||||
(mf/deps on-click)
|
||||
(fn [event]
|
||||
(when on-click
|
||||
(on-click))))]
|
||||
#(when on-click (on-click)))]
|
||||
|
||||
[:g.plus-button {:cursor "pointer"
|
||||
:on-click handle-click}
|
||||
|
@ -135,12 +142,84 @@
|
|||
:href (dm/str "#icon-plus")
|
||||
:fill "white"}]]))
|
||||
|
||||
(defn use-drag
|
||||
[{:keys [on-drag-start on-drag-end on-drag-delta on-drag-position]}]
|
||||
(let [
|
||||
dragging-ref (mf/use-ref false)
|
||||
start-pos-ref (mf/use-ref nil)
|
||||
current-pos-ref (mf/use-ref nil)
|
||||
|
||||
handle-pointer-down
|
||||
(mf/use-callback
|
||||
(mf/deps on-drag-start)
|
||||
(fn [event]
|
||||
(let [position (dom/get-client-position event)]
|
||||
(dom/capture-pointer event)
|
||||
(mf/set-ref-val! dragging-ref true)
|
||||
(mf/set-ref-val! start-pos-ref position)
|
||||
(mf/set-ref-val! current-pos-ref position)
|
||||
(when on-drag-start (on-drag-start position)))))
|
||||
|
||||
handle-lost-pointer-capture
|
||||
(mf/use-callback
|
||||
(mf/deps on-drag-end)
|
||||
(fn [event]
|
||||
(let [position (mf/ref-val current-pos-ref)]
|
||||
(dom/release-pointer event)
|
||||
(mf/set-ref-val! dragging-ref false)
|
||||
(mf/set-ref-val! start-pos-ref nil)
|
||||
(when on-drag-end (on-drag-end position)))))
|
||||
|
||||
handle-pointer-move
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(when (mf/ref-val dragging-ref)
|
||||
(let [start (mf/ref-val start-pos-ref)
|
||||
pos (dom/get-client-position event)]
|
||||
(mf/set-ref-val! current-pos-ref pos)
|
||||
(when on-drag-delta (on-drag-delta (gpt/to-vec start pos)))
|
||||
(when on-drag-position (on-drag-position pos))))))]
|
||||
|
||||
{:handle-pointer-down handle-pointer-down
|
||||
:handle-lost-pointer-capture handle-lost-pointer-capture
|
||||
:handle-pointer-move handle-pointer-move}))
|
||||
|
||||
(mf/defc resize-cell-handler
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [x (unchecked-get props "x")
|
||||
y (unchecked-get props "y")
|
||||
width (unchecked-get props "width")
|
||||
height (unchecked-get props "height")
|
||||
direction (unchecked-get props "direction")
|
||||
cursor (if (= direction :row) (cur/scale-ns 0) (cur/scale-ew 0))
|
||||
|
||||
handle-drag-delta
|
||||
(mf/use-callback
|
||||
(fn [delta]
|
||||
(prn ">>>" delta)))
|
||||
|
||||
{:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]}
|
||||
(use-drag {:on-drag-delta handle-drag-delta})]
|
||||
|
||||
[:rect
|
||||
{:x x
|
||||
:y y
|
||||
:height height
|
||||
:width width
|
||||
:style {:fill "transparent" :stroke-width 0 :cursor cursor}
|
||||
|
||||
:on-pointer-down handle-pointer-down
|
||||
:on-lost-pointer-capture handle-lost-pointer-capture
|
||||
:on-pointer-move handle-pointer-move}]))
|
||||
|
||||
(mf/defc grid-cell
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
{:keys [row-tracks column-tracks]} (unchecked-get props "layout-data")
|
||||
bounds (unchecked-get props "bounds")
|
||||
|
||||
{:keys [origin row-tracks column-tracks layout-bounds]} (unchecked-get props "layout-data")
|
||||
|
||||
zoom (unchecked-get props "zoom")
|
||||
|
||||
hover? (unchecked-get props "hover?")
|
||||
|
@ -152,18 +231,17 @@
|
|||
column-track (nth column-tracks (dec column) nil)
|
||||
row-track (nth row-tracks (dec row) nil)
|
||||
|
||||
hv #(gpo/start-hv layout-bounds %)
|
||||
vv #(gpo/start-vv layout-bounds %)
|
||||
|
||||
origin (gpo/origin bounds)
|
||||
hv #(gpo/start-hv bounds %)
|
||||
vv #(gpo/start-vv bounds %)
|
||||
|
||||
start-p (-> origin
|
||||
(gpt/add (hv (:distance column-track)))
|
||||
(gpt/add (vv (:distance row-track))))
|
||||
start-p (gpt/add origin
|
||||
(gpt/add
|
||||
(gpt/to-vec origin (:start-p column-track))
|
||||
(gpt/to-vec origin (:start-p row-track))))
|
||||
|
||||
end-p (-> start-p
|
||||
(gpt/add (hv (:value column-track)))
|
||||
(gpt/add (vv (:value row-track))))
|
||||
(gpt/add (hv (:size column-track)))
|
||||
(gpt/add (vv (:size row-track))))
|
||||
|
||||
cell-width (- (:x end-p) (:x start-p))
|
||||
cell-height (- (:y end-p) (:y start-p))]
|
||||
|
@ -190,34 +268,33 @@
|
|||
(when selected?
|
||||
(let [handlers
|
||||
;; Handlers positions, size and cursor
|
||||
[[(:x start-p) (+ (:y start-p) (/ -10 zoom)) cell-width (/ 20 zoom) (cur/scale-ns 0)]
|
||||
[(+ (:x start-p) cell-width (/ -10 zoom)) (:y start-p) (/ 20 zoom) cell-height (cur/scale-ew 0)]
|
||||
[(:x start-p) (+ (:y start-p) cell-height (/ -10 zoom)) cell-width (/ 20 zoom) (cur/scale-ns 0)]
|
||||
[(+ (:x start-p) (/ -10 zoom)) (:y start-p) (/ 20 zoom) cell-height (cur/scale-ew 0)]]]
|
||||
[[(:x start-p) (+ (:y start-p) (/ -10 zoom)) cell-width (/ 20 zoom) :row]
|
||||
[(+ (:x start-p) cell-width (/ -10 zoom)) (:y start-p) (/ 20 zoom) cell-height :column]
|
||||
[(:x start-p) (+ (:y start-p) cell-height (/ -10 zoom)) cell-width (/ 20 zoom) :row]
|
||||
[(+ (:x start-p) (/ -10 zoom)) (:y start-p) (/ 20 zoom) cell-height :column]]]
|
||||
[:*
|
||||
(for [[x y width height cursor] handlers]
|
||||
[:rect
|
||||
{:x x
|
||||
:y y
|
||||
:height height
|
||||
:width width
|
||||
:style {:fill "transparent" :stroke-width 0 :cursor cursor}}])]))]))
|
||||
(for [[x y width height dir] handlers]
|
||||
[:& resize-cell-handler {:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:direction dir}])]))]))
|
||||
|
||||
(mf/defc resize-handler
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [start-p (unchecked-get props "start-p")
|
||||
(let [shape (unchecked-get props "shape")
|
||||
{:keys [column-total-size column-total-gap row-total-size row-total-gap]} (unchecked-get props "layout-data")
|
||||
start-p (unchecked-get props "start-p")
|
||||
type (unchecked-get props "type")
|
||||
bounds (unchecked-get props "bounds")
|
||||
zoom (unchecked-get props "zoom")
|
||||
|
||||
width (gpo/width-points bounds)
|
||||
height (gpo/height-points bounds)
|
||||
|
||||
dragging-ref (mf/use-ref false)
|
||||
start-ref (mf/use-ref nil)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||
|
||||
on-pointer-down
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
|
@ -247,15 +324,15 @@
|
|||
|
||||
[x y width height]
|
||||
(if (= type :column)
|
||||
[(- (:x start-p) (/ 8 zoom))
|
||||
[(- (:x start-p) (max layout-gap-col (/ 8 zoom)))
|
||||
(- (:y start-p) (/ 40 zoom))
|
||||
(/ 16 zoom)
|
||||
(+ height (/ 40 zoom))]
|
||||
(max layout-gap-col (/ 16 zoom))
|
||||
(+ row-total-size row-total-gap (/ 40 zoom))]
|
||||
|
||||
[(- (:x start-p) (/ 40 zoom))
|
||||
(- (:y start-p) (/ 8 zoom))
|
||||
(+ width (/ 40 zoom))
|
||||
(/ 16 zoom)])]
|
||||
(- (:y start-p) (max layout-gap-row (/ 8 zoom)))
|
||||
(+ column-total-size column-total-gap (/ 40 zoom))
|
||||
(max layout-gap-row (/ 16 zoom))])]
|
||||
|
||||
[:rect.resize-handler
|
||||
{:x x
|
||||
|
@ -295,6 +372,8 @@
|
|||
height (gpo/height-points bounds)
|
||||
origin (gpo/origin bounds)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||
|
||||
{:keys [row-tracks column-tracks] :as layout-data}
|
||||
(gsg/calc-layout-data shape children bounds)
|
||||
|
||||
|
@ -332,38 +411,76 @@
|
|||
|
||||
|
||||
(for [[idx column-data] (d/enumerate column-tracks)]
|
||||
(let [start-p (-> origin (gpt/add (hv (:distance column-data))))
|
||||
marker-p (-> start-p (gpt/subtract (vv (/ 20 zoom))))]
|
||||
(let [start-p (:start-p column-data)
|
||||
marker-p (-> start-p
|
||||
(gpt/subtract (vv (/ 20 zoom)))
|
||||
(cond-> (not= idx 0)
|
||||
(gpt/subtract (hv (/ layout-gap-col 2)))))
|
||||
|
||||
text-p (-> start-p
|
||||
(gpt/subtract (vv (/ 20 zoom)))
|
||||
(gpt/add (hv (/ (:size column-data) 2))))]
|
||||
[:*
|
||||
[:& track-marker {:center marker-p
|
||||
:value (dm/str (inc idx))
|
||||
:zoom zoom}]
|
||||
|
||||
[:& resize-handler {:type :column
|
||||
:start-p start-p
|
||||
:zoom zoom
|
||||
:bounds bounds}]]))
|
||||
[:text {:x (:x text-p)
|
||||
:y (:y text-p)
|
||||
:font-size (/ 14 zoom)
|
||||
:font-weight 600
|
||||
:font-family "worksans"
|
||||
:dominant-baseline "central"
|
||||
:text-anchor "middle"
|
||||
:style {:fill "var(--color-distance)"}}
|
||||
(format-size column-data)]
|
||||
|
||||
(when (not= idx 0)
|
||||
[:& resize-handler {:shape shape
|
||||
:layout-data layout-data
|
||||
:start-p start-p
|
||||
:type :column
|
||||
:zoom zoom}])]))
|
||||
|
||||
(for [[idx row-data] (d/enumerate row-tracks)]
|
||||
(let [start-p (-> origin (gpt/add (vv (:distance row-data))))
|
||||
marker-p (-> start-p (gpt/subtract (hv (/ 20 zoom))))]
|
||||
(let [start-p (:start-p row-data)
|
||||
marker-p (-> start-p
|
||||
(gpt/subtract (hv (/ 20 zoom)))
|
||||
(cond-> (not= idx 0)
|
||||
(gpt/subtract (vv (/ layout-gap-row 2)))))
|
||||
|
||||
text-p (-> start-p
|
||||
(gpt/subtract (hv (/ 20 zoom)))
|
||||
(gpt/add (vv (/ (:size row-data) 2))))]
|
||||
[:*
|
||||
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))}
|
||||
[:& track-marker {:center marker-p
|
||||
:value (dm/str (inc idx))
|
||||
:zoom zoom}]]
|
||||
|
||||
[:& resize-handler {:type :row
|
||||
:start-p start-p
|
||||
:zoom zoom
|
||||
:bounds bounds}]]))
|
||||
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x text-p) (:y text-p))}
|
||||
[:text {:x (:x text-p)
|
||||
:y (:y text-p)
|
||||
:font-size (/ 14 zoom)
|
||||
:font-weight 600
|
||||
:font-family "worksans"
|
||||
:dominant-baseline "central"
|
||||
:text-anchor "middle"
|
||||
:style {:fill "var(--color-distance)"}}
|
||||
(format-size row-data)]]
|
||||
|
||||
(when (not= idx 0)
|
||||
[:& resize-handler {:shape shape
|
||||
:layout-data layout-data
|
||||
:start-p start-p
|
||||
:type :column
|
||||
:zoom zoom}])]))
|
||||
|
||||
(for [[_ {:keys [column row]}] (:layout-grid-cells shape)]
|
||||
[:& grid-cell {:shape shape
|
||||
:layout-data layout-data
|
||||
:row row
|
||||
:column column
|
||||
:bounds bounds
|
||||
:zoom zoom
|
||||
:hover? (contains? hover-cells [row column])
|
||||
:selected? (= selected-cells [row column])}])]))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue