Reorder tracks from grid editor

This commit is contained in:
alonso.torres 2023-12-27 17:22:57 +01:00 committed by Andrey Antukh
parent 7508627dc5
commit da358d635b
5 changed files with 266 additions and 94 deletions

View file

@ -10,6 +10,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.line :as gl]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
@ -163,7 +164,7 @@
(mf/set-ref-val! dragging-ref true)
(mf/set-ref-val! start-pos-ref raw-pt)
(mf/set-ref-val! current-pos-ref raw-pt)
(when on-drag-start (on-drag-start position)))))
(when on-drag-start (on-drag-start event position)))))
handle-lost-pointer-capture
(mf/use-callback
@ -174,7 +175,7 @@
(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)))))
(when on-drag-end (on-drag-end event position)))))
handle-pointer-move
(mf/use-callback
@ -185,8 +186,8 @@
pos (dom/get-client-position event)
pt (uwvv/point->viewport pos)]
(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 pt))))))]
(when on-drag-delta (on-drag-delta event (gpt/to-vec start pos)))
(when on-drag-position (on-drag-position event pt))))))]
{:handle-pointer-down handle-pointer-down
:handle-lost-pointer-capture handle-lost-pointer-capture
@ -212,7 +213,7 @@
handle-drag-position
(mf/use-callback
(mf/deps shape row column row-span column-span)
(fn [position]
(fn [_ position]
(let [[drag-row drag-column] (gsg/get-position-grid-coord layout-data position)
[new-row new-column new-row-span new-column-span]
@ -410,7 +411,7 @@
handle-drag-position
(mf/use-callback
(mf/deps shape track-before track-after)
(fn [position]
(fn [_ position]
(let [[tracks-prop axis]
(if (= :column type) [:layout-grid-columns :x] [:layout-grid-rows :y])
@ -451,11 +452,14 @@
(let [shape (unchecked-get props "shape")
index (unchecked-get props "index")
last? (unchecked-get props "last?")
drop? (unchecked-get props "drop?")
track-before (unchecked-get props "track-before")
track-after (unchecked-get props "track-after")
snap-pixel? (unchecked-get props "snap-pixel?")
{:keys [column-total-size column-total-gap row-total-size row-total-gap]} (unchecked-get props "layout-data")
{:keys [column-total-size column-total-gap row-total-size row-total-gap] :as layout-data}
(unchecked-get props "layout-data")
start-p (unchecked-get props "start-p")
type (unchecked-get props "type")
zoom (unchecked-get props "zoom")
@ -477,7 +481,7 @@
[(+ column-total-size column-total-gap)
(max 0 (- layout-gap-row (/ 10 zoom)) (/ 8 zoom))])
start-p
start-p-resize
(cond-> start-p
(and (= type :column) (= index 0))
(gpt/subtract (hv (/ width 2)))
@ -491,22 +495,52 @@
(and (= type :row) (not= index 0) (not last?))
(-> (gpt/subtract (vv (/ layout-gap-row 2)))
(gpt/subtract (vv (/ height 2)))))]
(gpt/subtract (vv (/ height 2)))))
[:rect.resize-track-handler
{:x (:x start-p)
:y (:y start-p)
:height height
:width width
:on-pointer-down handle-pointer-down
:on-lost-pointer-capture handle-lost-pointer-capture
:on-pointer-move handle-pointer-move
:transform (dm/str (gmt/transform-in start-p (:transform shape)))
:class (if (= type :column)
(cur/get-dynamic "resize-ew" (:rotation shape))
(cur/get-dynamic "resize-ns" (:rotation shape)))
:style {:fill "transparent"
:stroke-width 0}}]))
start-p-drop
(cond-> start-p
(and (= type :column) (= index 0))
(gpt/subtract (hv (/ width 2)))
(and (= type :row) (= index 0))
(gpt/subtract (vv (/ height 2)))
(and (= type :column) last?)
(gpt/add (hv (/ width 2)))
(and (= type :row) last?)
(gpt/add (vv (/ height 2)))
(and (= type :column) (not= index 0) (not last?))
(-> (gpt/subtract (hv (/ layout-gap-col 2)))
(gpt/subtract (hv (/ 5 zoom))))
(and (= type :row) (not= index 0) (not last?))
(-> (gpt/subtract (vv (/ layout-gap-row 2)))
(gpt/subtract (vv (/ 5 zoom)))))]
[:*
(when drop?
[:rect.drop
{:x (:x start-p-drop)
:y (:y start-p-drop)
:width (if (= type :column)(/ 10 zoom) width)
:height (if (= type :row) (/ 10 zoom) height)
:fill "var(--grid-editor-area-background)"}])
[:rect.resize-track-handler
{:x (:x start-p-resize)
:y (:y start-p-resize)
:height height
:width width
:on-pointer-down handle-pointer-down
:on-lost-pointer-capture handle-lost-pointer-capture
:on-pointer-move handle-pointer-move
:transform (dm/str (gmt/transform-in start-p (:transform shape)))
:class (if (= type :column)
(cur/get-dynamic "resize-ew" (:rotation shape))
(cur/get-dynamic "resize-ns" (:rotation shape)))
:style {:fill "transparent"
:stroke-width 0}}]]))
(def marker-width 24)
(def marker-h1 20)
@ -620,7 +654,7 @@
(dm/str value)]]))
(mf/defc track
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "zoom" "index" "type" "track-data" "layout-data" "hovering?"]))]
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
@ -631,6 +665,11 @@
track-data (unchecked-get props "track-data")
layout-data (unchecked-get props "layout-data")
hovering? (unchecked-get props "hovering?")
drop? (unchecked-get props "drop?")
on-start-reorder-track (unchecked-get props "on-start-reorder-track")
on-move-reorder-track (unchecked-get props "on-move-reorder-track")
on-end-reorder-track (unchecked-get props "on-end-reorder-track")
track-input-ref (mf/use-ref)
[layout-gap-row layout-gap-col] (ctl/gaps shape)
@ -719,17 +758,41 @@
[(:x text-p) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)]
[(- (:x text-p) (max 0 (:size track-data))) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)])
handle-drag-start
(mf/use-callback
(mf/deps on-start-reorder-track type index)
(fn []
(on-start-reorder-track type index)))
handle-drag-end
(mf/use-callback
(mf/deps on-end-reorder-track type index)
(fn [event position]
(on-end-reorder-track type index position (not (kbd/mod? event)))))
handle-drag-position
(mf/use-callback
(mf/deps on-move-reorder-track type index)
(fn [_ position]
(on-move-reorder-track type index position)))
trackwidth (* text-width zoom)
medium? (and (>= trackwidth small-size-limit) (< trackwidth medium-size-limit))
small? (< trackwidth small-size-limit)
track-before (get-in layout-data [track-list-prop (dec index)])]
track-before (get-in layout-data [track-list-prop (dec index)])
{:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]}
(use-drag {:on-drag-start handle-drag-start
:on-drag-end handle-drag-end
:on-drag-position handle-drag-position})]
(mf/use-effect
(mf/deps track-data)
(fn []
(dom/set-value! (mf/ref-val track-input-ref) (format-size track-data))))
[:g.track
[:g {:on-pointer-enter handle-pointer-enter
:on-pointer-leave handle-pointer-leave
@ -737,17 +800,20 @@
(dm/str (gmt/transform-in text-p (:transform shape)))
(dm/str (gmt/transform-in text-p (gmt/rotate (:transform shape) -90))))}
(when (and hovering? (not small?))
[:rect {:x (+ text-x (/ 18 zoom))
:y text-y
:width (- text-width (/ 36 zoom))
:height (- text-height (/ 5 zoom))
:rx (/ 3 zoom)
:fill "var(--grid-editor-marker-color)"
:opacity 0.2}])
[:rect {:class (stl/css :grid-editor-header-hover)
:x (+ text-x (/ 18 zoom))
:y text-y
:width (- text-width (/ 36 zoom))
:height (- text-height (/ 5 zoom))
:rx (/ 3 zoom)
:style {:cursor "pointer"}
:opacity (if (and hovering? (not small?)) 0.2 0)}]
(when (not small?)
[:foreignObject {:x text-x :y text-y :width text-width :height text-height}
[:div {:class (stl/css :grid-editor-wrapper)}
[:div {:class (stl/css :grid-editor-wrapper)
:on-pointer-down handle-pointer-down
:on-lost-pointer-capture handle-lost-pointer-capture
:on-pointer-move handle-pointer-move}
[:input
{:ref track-input-ref
:style {}
@ -778,6 +844,7 @@
:layout-data layout-data
:shape shape
:snap-pixel? snap-pixel?
:drop? drop?
:start-p start-p
:track-after track-data
:track-before track-before
@ -855,8 +922,8 @@
(mf/deps shape children)
#(gsg/calc-layout-data shape bounds children all-bounds objects))
width (max (gpo/width-points bounds) (+ column-total-size column-total-gap))
height (max (gpo/height-points bounds) (+ row-total-size row-total-gap))
width (max (gpo/width-points bounds) (+ column-total-size column-total-gap (ctl/h-padding shape)))
height (max (gpo/height-points bounds) (+ row-total-size row-total-gap (ctl/v-padding shape)))
handle-pointer-down
(mf/use-callback
@ -875,7 +942,68 @@
(mf/use-callback
(mf/deps (:id shape))
(fn []
(st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value)))))]
(st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value)))))
target-tracks* (mf/use-ref nil)
drop-track-type* (mf/use-state nil)
drop-track-target* (mf/use-state nil)
handle-start-reorder-track
(mf/use-callback
(mf/deps layout-data)
(fn [type from-idx]
;; Initialize target-tracks
(let [line-vec (if (= type :column) (vv 1) (hv 1))
first-point origin
last-point (if (= type :column) (nth bounds 1) (nth bounds 3))
mid-points
(if (= type :column)
(->> (:column-tracks layout-data)
(mapv #(gpt/add (:start-p %) (hv (/ (:size %) 2)))))
(->> (:row-tracks layout-data)
(mapv #(gpt/add (:start-p %) (vv (/ (:size %) 2))))))
tracks
(->> (d/with-prev (d/concat-vec [first-point] mid-points [last-point]))
(d/enumerate)
(keep
(fn [[index [current prev]]]
(when (some? prev)
[[prev current line-vec] (dec index)]))))]
(mf/set-ref-val! target-tracks* tracks)
(reset! drop-track-type* type))))
handle-move-reorder-track
(mf/use-callback
(fn [type from-idx position]
(let [index
(->> (mf/ref-val target-tracks*)
(d/seek (fn [[[p1 p2 v] _]]
(gl/is-inside-lines? [p1 v] [p2 v] position)))
(second))]
(when (some? index)
(reset! drop-track-target* index)))))
handle-end-reorder-track
(mf/use-callback
(mf/deps base-shape @drop-track-target*)
(fn [type from-index position move-content?]
(when-let [to-index @drop-track-target*]
(let [ids [(:id base-shape)]]
(cond
(< from-index to-index)
(st/emit! (dwsl/reorder-layout-track ids type from-index (dec to-index) move-content?))
(> from-index to-index)
(st/emit! (dwsl/reorder-layout-track ids type from-index (dec to-index) move-content?)))))
(mf/set-ref-val! target-tracks* nil)
(reset! drop-track-type* nil)
(reset! drop-track-target* nil)))]
(mf/use-effect
(fn []
@ -914,15 +1042,21 @@
:on-click handle-add-row}]])
(for [[idx column-data] (d/enumerate column-tracks)]
[:& track {:key (dm/str "column-track-" idx)
:shape shape
:zoom zoom
:type :column
:index idx
:layout-data layout-data
:snap-pixel? snap-pixel?
:track-data column-data
:hovering? (contains? hover-columns idx)}])
(let [drop? (and (= :column @drop-track-type*)
(= idx @drop-track-target*))]
[:& track {:key (dm/str "column-track-" idx)
:shape shape
:zoom zoom
:type :column
:index idx
:layout-data layout-data
:snap-pixel? snap-pixel?
:drop? drop?
:track-data column-data
:hovering? (contains? hover-columns idx)
:on-start-reorder-track handle-start-reorder-track
:on-move-reorder-track handle-move-reorder-track
:on-end-reorder-track handle-end-reorder-track}]))
;; Last track resize handler
(when-not (empty? column-tracks)
@ -940,27 +1074,36 @@
:type :column
:value (dm/str (inc (count column-tracks)))
:zoom zoom}]
[:& resize-track-handler
{:index (count column-tracks)
:last? true
:shape shape
:layout-data layout-data
:snap-pixel? snap-pixel?
:start-p end-p
:type :column
:track-before (last column-tracks)
:zoom zoom}]]))
(let [drop? (and (= :column @drop-track-type*)
(= (count column-tracks) @drop-track-target*))]
[:& resize-track-handler
{:index (count column-tracks)
:last? true
:drop? drop?
:shape shape
:layout-data layout-data
:snap-pixel? snap-pixel?
:start-p end-p
:type :column
:track-before (last column-tracks)
:zoom zoom}])]))
(for [[idx row-data] (d/enumerate row-tracks)]
[:& track {:index idx
:key (dm/str "row-track-" idx)
:layout-data layout-data
:shape shape
:snap-pixel? snap-pixel?
:track-data row-data
:type :row
:zoom zoom
:hovering? (contains? hover-rows idx)}])
(let [drop? (and (= :row @drop-track-type*)
(= idx @drop-track-target*))]
[:& track {:index idx
:key (dm/str "row-track-" idx)
:layout-data layout-data
:shape shape
:snap-pixel? snap-pixel?
:drop? drop?
:track-data row-data
:type :row
:zoom zoom
:hovering? (contains? hover-rows idx)
:on-start-reorder-track handle-start-reorder-track
:on-move-reorder-track handle-move-reorder-track
:on-end-reorder-track handle-end-reorder-track}]))
(when-not (empty? row-tracks)
(let [last-track (last row-tracks)
start-p (:start-p last-track)
@ -977,13 +1120,16 @@
:type :row
:value (dm/str (inc (count row-tracks)))
:zoom zoom}]]
[:& resize-track-handler
{:index (count row-tracks)
:last? true
:shape shape
:layout-data layout-data
:start-p end-p
:type :row
:track-before (last row-tracks)
:snap-pixel? snap-pixel?
:zoom zoom}]]))])])))
(let [drop? (and (= :row @drop-track-type*)
(= (count row-tracks) @drop-track-target*))]
[:& resize-track-handler
{:index (count row-tracks)
:last? true
:drop? drop?
:shape shape
:layout-data layout-data
:start-p end-p
:type :row
:track-before (last row-tracks)
:snap-pixel? snap-pixel?
:zoom zoom}])]))])])))

View file

@ -18,30 +18,35 @@
}
.grid-editor-wrapper {
cursor: grab;
width: 100%;
height: 80%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.grid-editor-header-hover {
fill: var(--grid-editor-marker-color);
}
.grid-editor-label {
flex: 1;
background: none;
border-bottom: calc($s-1 / var(--zoom)) solid transparent;
border: 0;
color: var(--grid-editor-marker-text);
font-family: worksans;
font-size: calc($fs-12 / var(--zoom));
font-weight: 400;
margin: 0;
max-width: calc($s-80 / var(--zoom));
max-width: calc($s-60 / var(--zoom));
padding: 0;
padding: $s-4;
padding: calc($s-4 / var(--zoom));
text-align: center;
&:focus {
outline: none;
border-bottom: calc($s-1 / var(--zoom)) solid var(--grid-editor-marker-text);
}
}