0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-06 12:01:19 -05:00

Support for multi-track span in cells

This commit is contained in:
alonso.torres 2023-04-27 10:35:06 +02:00
parent 43d1f676ef
commit 0eff2e8887
9 changed files with 354 additions and 145 deletions

View file

@ -0,0 +1,95 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
;; Based on the code in:
;; https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Rectangle_difference
(ns app.common.geom.shapes.grid-layout.areas
(:refer-clojure :exclude [contains?]))
(defn area->cell-props [[column row column-span row-span]]
{:row row
:column column
:row-span row-span
:column-span column-span})
(defn make-area
([{:keys [column row column-span row-span]}]
(make-area column row column-span row-span))
([x y width height]
[x y width height]))
(defn contains?
[[a-x a-y a-width a-height :as a]
[b-x b-y b-width b-height :as b]]
(and (>= b-x a-x)
(>= b-y a-y)
(<= (+ b-x b-width) (+ a-x a-width))
(<= (+ b-y b-height) (+ a-y a-height))))
(defn intersects?
[[a-x a-y a-width a-height ]
[b-x b-y b-width b-height]]
(not (or (<= (+ b-x b-width) a-x)
(<= (+ b-y b-height) a-y)
(>= b-x (+ a-x a-width))
(>= b-y (+ a-y a-height)))))
(defn top-rect
[[a-x a-y a-width _]
[_ b-y _ _]]
(let [height (- b-y a-y)]
(when (> height 0)
(make-area a-x a-y a-width height))))
(defn bottom-rect
[[a-x a-y a-width a-height]
[_ b-y _ b-height]]
(let [y (+ b-y b-height)
height (- a-height (- y a-y))]
(when (and (> height 0) (< y (+ a-y a-height)))
(make-area a-x y a-width height))))
(defn left-rect
[[a-x a-y _ a-height]
[b-x b-y _ b-height]]
(let [rb-y (+ b-y b-height)
ra-y (+ a-y a-height)
y1 (max a-y b-y)
y2 (min ra-y rb-y)
height (- y2 y1)
width (- b-x a-x)]
(when (and (> width 0) (> height 0))
(make-area a-x y1 width height))))
(defn right-rect
[[a-x a-y a-width a-height]
[b-x b-y b-width b-height]]
(let [rb-y (+ b-y b-height)
ra-y (+ a-y a-height)
y1 (max a-y b-y)
y2 (min ra-y rb-y)
height (- y2 y1)
rb-x (+ b-x b-width)
width (- a-width (- rb-x a-x))
]
(when (and (> width 0) (> height 0))
(make-area rb-x y1 width height)))
)
(defn difference
[area-a area-b]
(if (or (nil? area-b)
(not (intersects? area-a area-b))
(contains? area-b area-a))
[]
(into []
(keep #(% area-a area-b))
[top-rect left-rect right-rect bottom-rect])))

View file

@ -49,8 +49,6 @@
;;
;; - Stretch auto tracks
(ns app.common.geom.shapes.grid-layout.layout-data
(:require
[app.common.geom.point :as gpt]
@ -62,47 +60,6 @@
(let [[pad-top pad-right pad-bottom pad-left] (ctl/paddings parent)]
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
#_(defn set-sample-data
[parent children]
(let [parent (assoc parent
:layout-grid-columns
[{:type :percent :value 25}
{:type :percent :value 25}
{:type :fixed :value 100}
;;{:type :auto}
;;{:type :flex :value 1}
]
:layout-grid-rows
[{:type :percent :value 50}
{:type :percent :value 50}
;;{:type :fixed :value 100}
;;{:type :auto}
;;{:type :flex :value 1}
])
num-rows (count (:layout-grid-rows parent))
num-columns (count (:layout-grid-columns parent))
layout-grid-cells
(into
{}
(for [[row-idx _row] (d/enumerate (:layout-grid-rows parent))
[col-idx _col] (d/enumerate (:layout-grid-columns parent))]
(let [[_bounds shape] (nth children (+ (* row-idx num-columns) col-idx) nil)
cell-data {:id (uuid/next)
:row (inc row-idx)
:column (inc col-idx)
:row-span 1
:col-span 1
:shapes (when shape [(:id shape)])}]
[(:id cell-data) cell-data])))
parent (assoc parent :layout-grid-cells layout-grid-cells)]
[parent children]))
(defn calculate-initial-track-size
[total-value {:keys [type value] :as track}]
@ -119,7 +76,6 @@
[0.01 ##Inf])]
(assoc track :size size :max-size max-size)))
(defn set-auto-base-size
[track-list children shape-cells type]
@ -280,7 +236,6 @@
(gpt/add start-p (vv (+ size row-gap)))])
[[] start-p])
(first))
]
{:origin start-p
@ -288,6 +243,8 @@
:row-tracks row-tracks
:column-tracks column-tracks
:shape-cells shape-cells
:column-gap column-gap
:row-gap row-gap
;; Convenient informaton for visualization
:column-total-size column-total-size
@ -300,7 +257,6 @@
[{:keys [origin row-tracks column-tracks shape-cells]} _transformed-parent-bounds [_ child]]
(let [grid-cell (get shape-cells (:id child))]
(when (some? grid-cell)
(let [column (nth column-tracks (dec (:column grid-cell)) nil)
row (nth row-tracks (dec (:row grid-cell)) nil)
@ -311,7 +267,7 @@
start-p (gpt/add origin
(gpt/add
(gpt/to-vec origin column-start-p)
(gpt/to-vec origin row-start-p)))
]
(gpt/to-vec origin row-start-p)))]
(assoc grid-cell :start-p start-p)))))

View file

@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.shapes.grid-layout.areas :as sga]
[app.common.math :as mth]
[app.common.schema :as sm]
[app.common.uuid :as uuid]))
@ -673,12 +674,6 @@
(update :layout-grid-cells remove-cells)
(assign-cells))))
;; TODO: Mix the cells given as arguments leaving only one. It should move all the shapes in those cells in the direction for the grid
;; and lastly use assign-cells to reassing the orphaned shapes
(defn merge-cells
[parent _cells]
parent)
(defn get-cells
([parent]
(get-cells parent nil))
@ -815,31 +810,128 @@
[(assoc-in parent [:layout-grid-cells (get-in cells [index :id]) :shapes] [])
(assoc-in result-cells [index :shapes] [])]))))
(defn in-cell?
"Given a cell check if the row+column is inside this cell"
[{cell-row :row cell-column :column :keys [row-span column-span]} row column]
(and (>= row cell-row)
(>= column cell-column)
(<= row (+ cell-row row-span -1))
(<= column (+ cell-column column-span -1))))
(defn cell-by-row-column
[parent row column]
(->> (:layout-grid-cells parent)
(vals)
(d/seek #(in-cell? % row column))))
(defn seek-indexed-cell
[cells row column]
(let [cells+index (d/enumerate cells)]
(d/seek (fn [[_ {cell-row :row cell-column :column}]]
(and (= cell-row row)
(= cell-column column))) cells+index)))
(d/seek #(in-cell? (second %) row column) cells+index)))
(defn push-into-cell
"Push the shapes into the row/column cell and moves the rest"
[parent shape-ids row column]
(let [cells (vec (get-cells parent {:sort? true}))
cells+index (d/enumerate cells)
[start-index start-cell] (seek-indexed-cell cells row column)]
[start-index _] (seek-indexed-cell cells row column)
(if (some? start-cell)
(let [ ;; start-index => to-index is the range where the shapes inserted will be added
to-index (min (+ start-index (count shape-ids)) (dec (count cells)))]
;; start-index => to-index is the range where the shapes inserted will be added
to-index (min (+ start-index (count shape-ids)) (dec (count cells)))]
;; Move shift the `shapes` attribute between cells
(->> (range start-index (inc to-index))
(map vector shape-ids)
(reduce (fn [[parent cells] [shape-id idx]]
(let [[parent cells] (free-cell-push parent cells idx)]
[(assoc-in parent [:layout-grid-cells (get-in cells [idx :id]) :shapes] [shape-id])
cells]))
[parent cells])
(first)))
parent)))
;; Move shift the `shapes` attribute between cells
(->> (range start-index (inc to-index))
(map vector shape-ids)
(reduce (fn [[parent cells] [shape-id idx]]
(let [[parent cells] (free-cell-push parent cells idx)]
[(assoc-in parent [:layout-grid-cells (get-in cells [idx :id]) :shapes] [shape-id])
cells]))
[parent cells])
(first))))
(defn resize-cell-area
"Increases/decreases the cell size"
[parent row column new-row new-column new-row-span new-column-span]
(let [cells (vec (get-cells parent {:sort? true}))
prev-cell (cell-by-row-column parent row column)
prev-area (sga/make-area prev-cell)
target-cell
(-> prev-cell
(assoc
:row new-row
:column new-column
:row-span new-row-span
:column-span new-column-span))
target-area (sga/make-area target-cell)]
(if (sga/contains? prev-area target-area)
;; The new area is smaller than the previous. We need to create cells in the empty space
(let [parent
(-> parent
(assoc-in [:layout-grid-cells (:id target-cell)] target-cell))
new-cells
(->> (sga/difference prev-area target-area)
(mapcat (fn [[column row column-span row-span]]
(for [new-col (range column (+ column column-span))
new-row (range row (+ row row-span))]
(merge grid-cell-defaults
{:id (uuid/next)
:row new-row
:column new-col
:row-span 1
:column-span 1})))))
parent
(->> new-cells
(reduce #(assoc-in %1 [:layout-grid-cells (:id %2)] %2) parent))]
parent)
;; The new area is bigger we need to remove the cells filled and split the intersections
(let [remove-cells (->> cells
(filter #(and (not= (:id target-cell) (:id %))
(sga/contains? target-area (sga/make-area %))))
(into #{}))
split-cells (->> cells (filter #(and (not= (:id target-cell) (:id %))
(not (contains? remove-cells %))
(sga/intersects? target-area (sga/make-area %)))))
[parent _]
(->> (d/enumerate cells)
(reduce (fn [[parent cells] [index cur-cell]]
(if (contains? remove-cells cur-cell)
(let [[parent cells] (free-cell-push parent cells index)]
[parent (conj cells cur-cell)])
[parent cells]))
[parent cells]))
parent
(-> parent
(assoc-in [:layout-grid-cells (:id target-cell)] target-cell))
parent
(->> remove-cells
(reduce (fn [parent cell]
(update parent :layout-grid-cells dissoc (:id cell)))
parent))
parent
(->> split-cells
(reduce (fn [parent cell]
(let [new-areas (sga/difference (sga/make-area cell) target-area)]
(as-> parent $
(update-in $ [:layout-grid-cells (:id cell)] merge (sga/area->cell-props (first new-areas)))
(reduce (fn [parent area]
(let [cell (merge (assoc grid-cell-defaults :id (uuid/next)) (sga/area->cell-props area))]
(assoc-in parent [:layout-grid-cells (:id cell)] cell))) $ new-areas))))
parent))]
parent))))

View file

@ -9,7 +9,7 @@
[potok.core :as ptk]))
(defn hover-grid-cell
[grid-id row column add-to-set]
[grid-id cell-id add-to-set]
(ptk/reify ::hover-grid-cell
ptk/UpdateEvent
(update [_ state]
@ -19,15 +19,15 @@
(fn [hover-set]
(let [hover-set (or hover-set #{})]
(if add-to-set
(conj hover-set [row column])
(disj hover-set [row column]))))))))
(conj hover-set cell-id)
(disj hover-set cell-id))))))))
(defn select-grid-cell
[grid-id row column]
[grid-id cell-id]
(ptk/reify ::select-grid-cell
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-grid-edition grid-id :selected] [row column]))))
(assoc-in state [:workspace-grid-edition grid-id :selected] cell-id))))
(defn remove-selection
[grid-id]

View file

@ -73,10 +73,10 @@
first-selected-shape (first selected-shapes)
shape-parent-frame (cph/get-frame objects (:frame-id first-selected-shape))
[grid-id {[row-selected col-selected] :selected}]
[grid-id {cell-id :selected}]
(d/seek (fn [[_ {:keys [selected]}]] (some? selected)) grid-edition)
grid-cell-selected? (and (some? grid-id) (some? row-selected) (some? col-selected))
grid-cell-selected? (and (some? grid-id) (some? cell-id))
on-change-tab
(fn [options-mode]
@ -96,8 +96,7 @@
[:& bool-options]
(cond
grid-cell-selected? [:& grid-cell/options {:shape (get objects grid-id)
:row row-selected
:column col-selected}]
:cell (get-in objects [grid-id :layout-grid-cells cell-id])}]
(d/not-empty? drawing) [:& shape-options {:shape (:object drawing)
:page-id page-id

View file

@ -447,7 +447,7 @@
(when expanded?
[:div.columns-info-wrapper
(for [[index column] (d/enumerate column-values)]
[:div.column-info
[:div.column-info {:key (dm/str index "-" (name type) "-" column)}
[:div.direction-grid-icon
(if is-col?
i/layout-rows

View file

@ -31,7 +31,7 @@
(mf/defc options
{::mf/wrap [mf/memo]}
[{:keys [_shape row column] :as props}]
[{:keys [_shape cell] :as props}]
(let [position-mode (mf/use-state :auto) ;; TODO this should come from shape
@ -51,10 +51,10 @@
#_(if (= align-self value)
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil}))
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value}))))
column-start column
column-end (inc column)
row-start row
row-end (inc row)
column-start (:column cell)
column-end (+ (:column cell) (:column-span cell))
row-start (:row cell)
row-end (+ (:row cell) (:row-span cell))
on-change
(fn [_side _orientation _value]

View file

@ -203,6 +203,9 @@
show-pixel-grid? (and (contains? layout :show-pixel-grid)
(>= zoom 8))
show-text-editor? (and editing-shape (= :text (:type editing-shape)))
hover-grid? (and (some? @hover-top-frame-id) (ctl/grid-layout? objects @hover-top-frame-id))
show-grid-editor? (and editing-shape (ctl/grid-layout? editing-shape))
show-presence? page-id
show-prototypes? (= options-mode :prototype)
@ -586,8 +589,15 @@
{:id (first selected)
:zoom zoom}])
(when show-grid-editor?
;; TODO: Temporary showing on hover. Remove eventualy
(when (or show-grid-editor? hover-grid?)
[:& grid-layout/editor
{:zoom zoom
:objects base-objects
:shape (get base-objects edition)}])]]]))
:objects objects-modified
:shape (or (get objects-modified edition)
(gsh/transform-shape
(get base-objects @hover-top-frame-id)
(dm/get-in modifiers [@hover-top-frame-id :modifiers])))
:view-only (not show-grid-editor?)
}])
]]]))

View file

@ -11,9 +11,10 @@
[app.common.geom.point :as gpt]
[app.common.geom.shapes.grid-layout :as gsg]
[app.common.geom.shapes.points :as gpo]
[app.common.pages.helpers :as cph]
[app.common.types.modifiers :as ctm]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.grid-layout.editor :as dwge]
[app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.refs :as refs]
[app.main.store :as st]
@ -145,8 +146,7 @@
(defn use-drag
[{:keys [on-drag-start on-drag-end on-drag-delta on-drag-position]}]
(let [
dragging-ref (mf/use-ref false)
(let [dragging-ref (mf/use-ref false)
start-pos-ref (mf/use-ref nil)
current-pos-ref (mf/use-ref nil)
@ -175,6 +175,7 @@
handle-pointer-move
(mf/use-callback
(mf/deps on-drag-delta on-drag-position)
(fn [event]
(when (mf/ref-val dragging-ref)
(let [start (mf/ref-val start-pos-ref)
@ -191,22 +192,67 @@
(mf/defc resize-cell-handler
{::mf/wrap-props false}
[props]
(let [x (unchecked-get props "x")
(let [shape (unchecked-get props "shape")
x (unchecked-get props "x")
y (unchecked-get props "y")
width (unchecked-get props "width")
height (unchecked-get props "height")
handler (unchecked-get props "handler")
{cell-id :id} (unchecked-get props "cell")
{:keys [row column row-span column-span]} (get-in shape [:layout-grid-cells cell-id])
direction (unchecked-get props "direction")
layout-data (unchecked-get props "layout-data")
cursor (if (= direction :row) (cur/scale-ns 0) (cur/scale-ew 0))
handle-drag-position
(mf/use-callback
(mf/deps shape row column row-span column-span)
(fn [position]
(prn ">>>" (gsg/get-position-grid-coord layout-data position))))
(let [[drag-row drag-column] (gsg/get-position-grid-coord layout-data position)
[new-row new-column new-row-span new-column-span]
(case handler
:top
(let [new-row (min (+ row (dec row-span)) drag-row)
new-row-span (+ (- row new-row) row-span)]
[new-row column new-row-span column-span])
:left
(let [new-column (min (+ column (dec column-span)) drag-column)
new-column-span (+ (- column new-column) column-span)]
[row new-column row-span new-column-span])
:bottom
(let [new-row-span (max 1 (inc (- drag-row row)))]
[row column new-row-span column-span])
:right
(let [new-column-span (max 1 (inc (- drag-column column)))]
[row column row-span new-column-span]))
shape
(-> (ctl/resize-cell-area shape row column new-row new-column new-row-span new-column-span)
(ctl/assign-cells))
modifiers
(-> (ctm/empty)
(ctm/change-property :layout-grid-rows (:layout-grid-rows shape))
(ctm/change-property :layout-grid-columns (:layout-grid-columns shape))
(ctm/change-property :layout-grid-cells (:layout-grid-cells shape)))]
(st/emit! (dwm/set-modifiers (dwm/create-modif-tree [(:id shape)] modifiers))))))
handle-drag-end
(mf/use-callback
(fn []
(st/emit! (dwm/apply-modifiers))))
{:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]}
(use-drag {:on-drag-position handle-drag-position})]
(use-drag {:on-drag-position handle-drag-position
;;:on-drag-start handle-drag-start
:on-drag-end handle-drag-end})]
[:rect
{:x x
:y y
@ -223,7 +269,8 @@
[props]
(let [shape (unchecked-get props "shape")
{:keys [origin row-tracks column-tracks layout-bounds] :as layout-data}
{:keys [row column row-span column-span] :as cell} (unchecked-get props "cell")
{:keys [origin row-tracks column-tracks layout-bounds column-gap row-gap] :as layout-data}
(unchecked-get props "layout-data")
zoom (unchecked-get props "zoom")
@ -231,23 +278,23 @@
hover? (unchecked-get props "hover?")
selected? (unchecked-get props "selected?")
row (unchecked-get props "row")
column (unchecked-get props "column")
column-track (nth column-tracks (dec column) nil)
row-track (nth row-tracks (dec row) nil)
span-column-tracks (subvec column-tracks (dec column) (+ (dec column) column-span))
span-row-tracks (subvec row-tracks (dec row) (+ (dec row) row-span))
hv #(gpo/start-hv layout-bounds %)
vv #(gpo/start-vv layout-bounds %)
start-p (gpt/add origin
(gpt/add
(gpt/to-vec origin (:start-p column-track))
(gpt/to-vec origin (:start-p row-track))))
(gpt/to-vec origin (dm/get-in span-column-tracks [0 :start-p]))
(gpt/to-vec origin (dm/get-in span-row-tracks [0 :start-p]))))
end-p (-> start-p
(gpt/add (hv (:size column-track)))
(gpt/add (vv (:size row-track))))
end-p
(as-> start-p $
(reduce (fn [p track] (gpt/add p (hv (:size track)))) $ span-column-tracks)
(reduce (fn [p track] (gpt/add p (vv (:size track)))) $ span-row-tracks)
(gpt/add $ (hv (* column-gap (dec (count span-column-tracks)))))
(gpt/add $ (vv (* row-gap (dec (count span-row-tracks))))))
cell-width (- (:x end-p) (:x start-p))
cell-height (- (:y end-p) (:y start-p))]
@ -259,10 +306,9 @@
:width cell-width
:height cell-height
:on-pointer-enter #(st/emit! (dwge/hover-grid-cell (:id shape) row column true))
:on-pointer-leave #(st/emit! (dwge/hover-grid-cell (:id shape) row column false))
:on-click #(st/emit! (dwge/select-grid-cell (:id shape) row column))
:on-pointer-enter #(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) true))
:on-pointer-leave #(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) false))
:on-click #(st/emit! (dwge/select-grid-cell (:id shape) (:id cell)))
:style {:fill "transparent"
:stroke "var(--color-distance)"
@ -274,14 +320,18 @@
(when selected?
(let [handlers
;; Handlers positions, size and cursor
[[(: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]]]
[[:top (:x start-p) (+ (:y start-p) (/ -10 zoom)) cell-width (/ 20 zoom) :row]
[:right (+ (:x start-p) cell-width (/ -10 zoom)) (:y start-p) (/ 20 zoom) cell-height :column]
[:bottom (:x start-p) (+ (:y start-p) cell-height (/ -10 zoom)) cell-width (/ 20 zoom) :row]
[:left (+ (:x start-p) (/ -10 zoom)) (:y start-p) (/ 20 zoom) cell-height :column]]]
[:*
(for [[x y width height dir] handlers]
[:& resize-cell-handler {:x x
(for [[handler x y width height dir] handlers]
[:& resize-cell-handler {:key (dm/str "resize-" (d/name handler) "-" (:id cell))
:shape shape
:handler handler
:x x
:y y
:cell cell
:width width
:height height
:direction dir
@ -355,21 +405,28 @@
:style {:fill "transparent"}}]))
(mf/defc editor
{::mf/wrap-props false}
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
(let [shape (unchecked-get props "shape")
objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
view-only (unchecked-get props "view-only")
bounds (:points shape)
;; We need to know the state unmodified so we can create the modifiers
shape-ref (mf/use-memo (mf/deps (:id shape)) #(refs/object-by-id (:id shape)))
base-shape (mf/deref shape-ref)
grid-edition-id-ref (mf/use-memo #(refs/workspace-grid-edition-id (:id shape)))
grid-edition (mf/deref grid-edition-id-ref)
hover-cells (:hover grid-edition)
selected-cells (:selected grid-edition)
children (->> (cph/get-immediate-children objects (:id shape))
children (->> (:shapes shape)
(map (d/getf objects))
(remove :hidden)
(map #(vector (gpo/parent-coords-bounds (:points %) (:points shape)) %)))
@ -400,22 +457,22 @@
(fn []
#(st/emit! (dwge/stop-grid-layout-editing (:id shape)))))
[:g.grid-editor
[:& grid-editor-frame {:zoom zoom
:bounds bounds}]
(let [start-p (-> origin (gpt/add (hv width)))]
[:& plus-btn {:start-p start-p
:zoom zoom
:type :column
:on-click handle-add-column}])
(let [start-p (-> origin (gpt/add (vv height)))]
[:& plus-btn {:start-p start-p
:zoom zoom
:type :row
:on-click handle-add-row}])
[:g.grid-editor {:pointer-events (when view-only "none")}
(when-not view-only
[:*
[:& grid-editor-frame {:zoom zoom
:bounds bounds}]
(let [start-p (-> origin (gpt/add (hv width)))]
[:& plus-btn {:start-p start-p
:zoom zoom
:type :column
:on-click handle-add-column}])
(let [start-p (-> origin (gpt/add (vv height)))]
[:& plus-btn {:start-p start-p
:zoom zoom
:type :row
:on-click handle-add-row}])])
(for [[idx column-data] (d/enumerate column-tracks)]
(let [start-p (:start-p column-data)
@ -427,7 +484,7 @@
text-p (-> start-p
(gpt/subtract (vv (/ 20 zoom)))
(gpt/add (hv (/ (:size column-data) 2))))]
[:*
[:* {:key (dm/str "column-" idx)}
[:& track-marker {:center marker-p
:value (dm/str (inc idx))
:zoom zoom}]
@ -454,12 +511,12 @@
marker-p (-> start-p
(gpt/subtract (hv (/ 20 zoom)))
(cond-> (not= idx 0)
(gpt/subtract (vv (/ layout-gap-row 2)))))
(gpt/subtract (vv (/ layout-gap-row 2)))))
text-p (-> start-p
(gpt/subtract (hv (/ 20 zoom)))
(gpt/add (vv (/ (:size row-data) 2))))]
[:*
[:* {:key (dm/str "row-" idx)}
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))}
[:& track-marker {:center marker-p
:value (dm/str (inc idx))
@ -483,11 +540,11 @@
:type :column
:zoom zoom}])]))
(for [[_ {:keys [column row]}] (:layout-grid-cells shape)]
[:& grid-cell {:shape shape
(for [[_ cell] (:layout-grid-cells shape)]
[:& grid-cell {:key (dm/str "cell-" (:id cell))
:shape base-shape
:layout-data layout-data
:row row
:column column
:cell cell
:zoom zoom
:hover? (contains? hover-cells [row column])
:selected? (= selected-cells [row column])}])]))
:hover? (contains? hover-cells (:id cell))
:selected? (= selected-cells (:id cell))}])]))