mirror of
https://github.com/penpot/penpot.git
synced 2025-02-15 11:38:24 -05: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
|
@ -15,9 +15,8 @@
|
||||||
(def conjv (fnil conj []))
|
(def conjv (fnil conj []))
|
||||||
|
|
||||||
(defn layout-bounds
|
(defn layout-bounds
|
||||||
[{:keys [layout-padding] :as shape} shape-bounds]
|
[parent shape-bounds]
|
||||||
(let [;; Add padding to the bounds
|
(let [[pad-top pad-right pad-bottom pad-left] (ctl/paddings parent)]
|
||||||
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding]
|
|
||||||
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
|
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
|
||||||
|
|
||||||
(defn init-layout-lines
|
(defn init-layout-lines
|
||||||
|
|
|
@ -4,10 +4,63 @@
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) KALEIDOS INC
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
;; Each track has specified minimum and maximum sizing functions (which may be the same)
|
||||||
|
;; - Fixed
|
||||||
|
;; - Percent
|
||||||
|
;; - Auto
|
||||||
|
;; - Flex
|
||||||
|
;;
|
||||||
|
;;
|
||||||
|
;;
|
||||||
|
;; Min functions:
|
||||||
|
;; - Fixed: value
|
||||||
|
;; - Percent: value to pixels
|
||||||
|
;; - Auto: auto
|
||||||
|
;; - Flex: auto
|
||||||
|
;;
|
||||||
|
;; Max functions:
|
||||||
|
;; - Fixed: value
|
||||||
|
;; - Percent: value to pixels
|
||||||
|
;; - Auto: max-content
|
||||||
|
;; - Flex: flex
|
||||||
|
|
||||||
|
;; Algorithm
|
||||||
|
;; - Initialize tracks:
|
||||||
|
;; * base = size or 0
|
||||||
|
;; * max = size or INF
|
||||||
|
;;
|
||||||
|
;; - Resolve intrinsic sizing
|
||||||
|
;; 1. Shim baseline-aligned items so their intrinsic size contributions reflect their baseline alignment
|
||||||
|
;;
|
||||||
|
;; 2. Size tracks to fit non-spanning items
|
||||||
|
;; base-size = max (children min contribution) floored 0
|
||||||
|
;;
|
||||||
|
;; 3. Increase sizes to accommodate spanning items crossing content-sized tracks
|
||||||
|
;;
|
||||||
|
;; 4. Increase sizes to accommodate spanning items crossing flexible tracks:
|
||||||
|
;;
|
||||||
|
;; 5. If any track still has an infinite growth limit set its growth limit to its base size.
|
||||||
|
|
||||||
|
;; - Distribute extra space accross spaned tracks
|
||||||
|
;; - Maximize tracks
|
||||||
|
;;
|
||||||
|
;; - Expand flexible tracks
|
||||||
|
;; - Find `fr` size
|
||||||
|
;;
|
||||||
|
;; - Stretch auto tracks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(ns app.common.geom.shapes.grid-layout.layout-data
|
(ns app.common.geom.shapes.grid-layout.layout-data
|
||||||
(:require
|
(:require
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes.points :as gpo]))
|
[app.common.geom.shapes.points :as gpo]
|
||||||
|
[app.common.types.shape.layout :as ctl]))
|
||||||
|
|
||||||
|
(defn layout-bounds
|
||||||
|
[parent shape-bounds]
|
||||||
|
(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
|
#_(defn set-sample-data
|
||||||
[parent children]
|
[parent children]
|
||||||
|
@ -50,91 +103,218 @@
|
||||||
|
|
||||||
[parent children]))
|
[parent children]))
|
||||||
|
|
||||||
(defn calculate-initial-track-values
|
(defn calculate-initial-track-size
|
||||||
[{:keys [type value]} total-value]
|
[total-value {:keys [type value] :as track}]
|
||||||
|
|
||||||
(case type
|
(let [[size max-size]
|
||||||
:percent
|
(case type
|
||||||
(let [value (/ (* total-value value) 100) ]
|
:percent
|
||||||
value)
|
(let [value (/ (* total-value value) 100) ]
|
||||||
|
[value value])
|
||||||
|
|
||||||
:fixed
|
:fixed
|
||||||
value
|
[value value]
|
||||||
|
|
||||||
:auto
|
;; flex, auto
|
||||||
0
|
[0.01 ##Inf])]
|
||||||
))
|
(assoc track :size size :max-size max-size)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn set-auto-base-size
|
||||||
|
[track-list children shape-cells type]
|
||||||
|
|
||||||
|
(let [[prop prop-span size-fn]
|
||||||
|
(if (= type :column)
|
||||||
|
[:column :column-span gpo/width-points]
|
||||||
|
[:row :row-span gpo/height-points])]
|
||||||
|
|
||||||
|
(reduce (fn [tracks [child-bounds child-shape]]
|
||||||
|
(let [cell (get shape-cells (:id child-shape))
|
||||||
|
idx (dec (get cell prop))
|
||||||
|
track (nth tracks idx)]
|
||||||
|
(cond-> tracks
|
||||||
|
(and (= (get cell prop-span) 1) (= :auto (:type track)))
|
||||||
|
(update-in [idx :size] max (size-fn child-bounds)))))
|
||||||
|
track-list
|
||||||
|
children)))
|
||||||
|
|
||||||
|
(defn tracks-total-size
|
||||||
|
[track-list]
|
||||||
|
(let [calc-tracks-total-size
|
||||||
|
(fn [acc {:keys [size]}]
|
||||||
|
(+ acc size))]
|
||||||
|
(->> track-list (reduce calc-tracks-total-size 0))))
|
||||||
|
|
||||||
|
(defn tracks-total-frs
|
||||||
|
[track-list]
|
||||||
|
(let [calc-tracks-total-frs
|
||||||
|
(fn [acc {:keys [type value]}]
|
||||||
|
(let [value (max 1 value)]
|
||||||
|
(cond-> acc
|
||||||
|
(= type :flex)
|
||||||
|
(+ value))))]
|
||||||
|
(->> track-list (reduce calc-tracks-total-frs 0))))
|
||||||
|
|
||||||
|
(defn tracks-total-autos
|
||||||
|
[track-list]
|
||||||
|
(let [calc-tracks-total-autos
|
||||||
|
(fn [acc {:keys [type]}]
|
||||||
|
(cond-> acc (= type :auto) (inc)))]
|
||||||
|
(->> track-list (reduce calc-tracks-total-autos 0))))
|
||||||
|
|
||||||
|
(defn set-fr-value
|
||||||
|
[track-list fr-value]
|
||||||
|
(->> track-list
|
||||||
|
(mapv (fn [{:keys [type value max-size] :as track}]
|
||||||
|
(cond-> track
|
||||||
|
(= :flex type)
|
||||||
|
(assoc :size (min (* value fr-value) max-size)))))))
|
||||||
|
|
||||||
|
(defn add-auto-size
|
||||||
|
[track-list add-size]
|
||||||
|
(->> track-list
|
||||||
|
(mapv (fn [{:keys [type size max-size] :as track}]
|
||||||
|
(cond-> track
|
||||||
|
(= :auto type)
|
||||||
|
(assoc :size (min (+ size add-size) max-size)))))))
|
||||||
|
|
||||||
(defn calc-layout-data
|
(defn calc-layout-data
|
||||||
[parent _children transformed-parent-bounds]
|
[parent children transformed-parent-bounds]
|
||||||
|
|
||||||
(let [height (gpo/height-points transformed-parent-bounds)
|
(let [hv #(gpo/start-hv transformed-parent-bounds %)
|
||||||
width (gpo/width-points transformed-parent-bounds)
|
vv #(gpo/start-vv transformed-parent-bounds %)
|
||||||
|
|
||||||
;; Initialize tracks
|
layout-bounds (layout-bounds parent transformed-parent-bounds)
|
||||||
column-tracks
|
|
||||||
(->> (:layout-grid-columns parent)
|
|
||||||
(map (fn [track]
|
|
||||||
(let [initial (calculate-initial-track-values track width)]
|
|
||||||
(assoc track :value initial)))))
|
|
||||||
|
|
||||||
row-tracks
|
bound-height (gpo/height-points layout-bounds)
|
||||||
(->> (:layout-grid-rows parent)
|
bound-width (gpo/width-points layout-bounds)
|
||||||
(map (fn [track]
|
bound-corner (gpo/origin layout-bounds)
|
||||||
(let [initial (calculate-initial-track-values track height)]
|
|
||||||
(assoc track :value initial)))))
|
|
||||||
|
|
||||||
;; Go through cells to adjust auto sizes
|
grid-columns (:layout-grid-columns parent)
|
||||||
|
grid-rows (:layout-grid-rows parent)
|
||||||
|
|
||||||
|
[row-gap column-gap] (ctl/gaps parent)
|
||||||
|
|
||||||
;; Once auto sizes have been calculated we get calculate the `fr` with the remainining size and adjust the size
|
;; Map shape->cell
|
||||||
|
|
||||||
|
|
||||||
;; Adjust final distances
|
|
||||||
|
|
||||||
acc-track-distance
|
|
||||||
(fn [[result next-distance] data]
|
|
||||||
(let [result (conj result (assoc data :distance next-distance))
|
|
||||||
next-distance (+ next-distance (:value data))]
|
|
||||||
[result next-distance]))
|
|
||||||
|
|
||||||
column-tracks
|
|
||||||
(->> column-tracks
|
|
||||||
(reduce acc-track-distance [[] 0])
|
|
||||||
first)
|
|
||||||
|
|
||||||
row-tracks
|
|
||||||
(->> row-tracks
|
|
||||||
(reduce acc-track-distance [[] 0])
|
|
||||||
first)
|
|
||||||
|
|
||||||
shape-cells
|
shape-cells
|
||||||
(into {}
|
(into {}
|
||||||
(mapcat (fn [[_ cell]]
|
(mapcat (fn [[_ cell]]
|
||||||
(->> (:shapes cell)
|
(->> (:shapes cell) (map #(vector % cell)))))
|
||||||
(map #(vector % cell)))))
|
|
||||||
(:layout-grid-cells parent))
|
(:layout-grid-cells parent))
|
||||||
|
|
||||||
|
|
||||||
|
;; Initialize tracks
|
||||||
|
column-tracks
|
||||||
|
(->> grid-columns
|
||||||
|
(mapv (partial calculate-initial-track-size bound-width)))
|
||||||
|
|
||||||
|
row-tracks
|
||||||
|
(->> grid-rows
|
||||||
|
(mapv (partial calculate-initial-track-size bound-height)))
|
||||||
|
|
||||||
|
;; Go through cells to adjust auto sizes for span=1. Base is the max of its children
|
||||||
|
column-tracks (set-auto-base-size column-tracks children shape-cells :column)
|
||||||
|
row-tracks (set-auto-base-size row-tracks children shape-cells :row)
|
||||||
|
|
||||||
|
|
||||||
|
;; Adjust multi-spaned cells with no flex columns
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; Calculate the `fr` unit and adjust the size
|
||||||
|
column-total-size (tracks-total-size column-tracks)
|
||||||
|
row-total-size (tracks-total-size row-tracks)
|
||||||
|
|
||||||
|
column-total-gap (* column-gap (dec (count column-tracks)))
|
||||||
|
row-total-gap (* row-gap (dec (count row-tracks)))
|
||||||
|
|
||||||
|
column-frs (tracks-total-frs column-tracks)
|
||||||
|
row-frs (tracks-total-frs row-tracks)
|
||||||
|
|
||||||
|
;; Once auto sizes have been calculated we get calculate the `fr` unit with the remainining size and adjust the size
|
||||||
|
free-column-space (- bound-width (+ column-total-size column-total-gap))
|
||||||
|
free-row-space (- bound-height (+ row-total-size row-total-gap))
|
||||||
|
column-fr (/ free-column-space column-frs)
|
||||||
|
row-fr (/ free-row-space row-frs)
|
||||||
|
|
||||||
|
column-tracks (set-fr-value column-tracks column-fr)
|
||||||
|
row-tracks (set-fr-value row-tracks row-fr)
|
||||||
|
|
||||||
|
;; Distribute free space between `auto` tracks
|
||||||
|
column-total-size (tracks-total-size column-tracks)
|
||||||
|
row-total-size (tracks-total-size row-tracks)
|
||||||
|
|
||||||
|
free-column-space (- bound-width (+ column-total-size column-total-gap))
|
||||||
|
free-row-space (- bound-height (+ row-total-size row-total-gap))
|
||||||
|
column-autos (tracks-total-autos column-tracks)
|
||||||
|
row-autos (tracks-total-autos row-tracks)
|
||||||
|
column-add-auto (/ free-column-space column-autos)
|
||||||
|
row-add-auto (/ free-row-space row-autos)
|
||||||
|
|
||||||
|
column-tracks (add-auto-size column-tracks column-add-auto)
|
||||||
|
row-tracks (add-auto-size row-tracks row-add-auto)
|
||||||
|
|
||||||
|
start-p
|
||||||
|
(cond-> bound-corner
|
||||||
|
(= :end (:layout-align-content parent))
|
||||||
|
(gpt/add (hv (- bound-width (+ column-total-size column-total-gap))))
|
||||||
|
|
||||||
|
(= :center (:layout-align-content parent))
|
||||||
|
(gpt/add (hv (/ (- bound-width (+ column-total-size column-total-gap)) 2)))
|
||||||
|
|
||||||
|
(= :end (:layout-justify-content parent))
|
||||||
|
(gpt/add (vv (- bound-height (+ row-total-size row-total-gap))))
|
||||||
|
|
||||||
|
(= :center (:layout-justify-content parent))
|
||||||
|
(gpt/add (vv (/ (- bound-height (+ row-total-size row-total-gap)) 2))))
|
||||||
|
|
||||||
|
column-tracks
|
||||||
|
(->> column-tracks
|
||||||
|
(reduce (fn [[tracks start-p] {:keys [size] :as track}]
|
||||||
|
[(conj tracks (assoc track :start-p start-p))
|
||||||
|
(gpt/add start-p (hv (+ size column-gap)))])
|
||||||
|
[[] start-p])
|
||||||
|
(first))
|
||||||
|
|
||||||
|
row-tracks
|
||||||
|
(->> row-tracks
|
||||||
|
(reduce (fn [[tracks start-p] {:keys [size] :as track}]
|
||||||
|
[(conj tracks (assoc track :start-p start-p))
|
||||||
|
(gpt/add start-p (vv (+ size row-gap)))])
|
||||||
|
[[] start-p])
|
||||||
|
(first))
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
{:row-tracks row-tracks
|
{:origin start-p
|
||||||
|
:layout-bounds layout-bounds
|
||||||
|
:row-tracks row-tracks
|
||||||
:column-tracks column-tracks
|
:column-tracks column-tracks
|
||||||
:shape-cells shape-cells}))
|
:shape-cells shape-cells
|
||||||
|
|
||||||
|
;; Convenient informaton for visualization
|
||||||
|
:column-total-size column-total-size
|
||||||
|
:column-total-gap column-total-gap
|
||||||
|
:row-total-size row-total-size
|
||||||
|
:row-total-gap row-total-gap
|
||||||
|
}))
|
||||||
|
|
||||||
(defn get-cell-data
|
(defn get-cell-data
|
||||||
[{:keys [row-tracks column-tracks shape-cells]} transformed-parent-bounds [_ child]]
|
[{:keys [origin row-tracks column-tracks shape-cells]} _transformed-parent-bounds [_ child]]
|
||||||
|
|
||||||
(let [origin (gpo/origin transformed-parent-bounds)
|
(let [grid-cell (get shape-cells (:id child))]
|
||||||
hv #(gpo/start-hv transformed-parent-bounds %)
|
|
||||||
vv #(gpo/start-vv transformed-parent-bounds %)
|
|
||||||
|
|
||||||
grid-cell (get shape-cells (:id child))]
|
|
||||||
|
|
||||||
(when (some? grid-cell)
|
(when (some? grid-cell)
|
||||||
(let [column (nth column-tracks (dec (:column grid-cell)) nil)
|
(let [column (nth column-tracks (dec (:column grid-cell)) nil)
|
||||||
row (nth row-tracks (dec (:row grid-cell)) nil)
|
row (nth row-tracks (dec (:row grid-cell)) nil)
|
||||||
|
|
||||||
start-p (-> origin
|
column-start-p (:start-p column)
|
||||||
(gpt/add (hv (:distance column)))
|
row-start-p (:start-p row)
|
||||||
(gpt/add (vv (:distance row))))]
|
|
||||||
|
start-p (gpt/add origin
|
||||||
|
(gpt/add
|
||||||
|
(gpt/to-vec origin column-start-p)
|
||||||
|
(gpt/to-vec origin row-start-p)))
|
||||||
|
]
|
||||||
|
|
||||||
(assoc grid-cell :start-p start-p)))))
|
(assoc grid-cell :start-p start-p)))))
|
||||||
|
|
|
@ -221,8 +221,7 @@
|
||||||
bound+child (first children)
|
bound+child (first children)
|
||||||
pending (rest children)]
|
pending (rest children)]
|
||||||
(if (some? bound+child)
|
(if (some? bound+child)
|
||||||
(let [[_ child] bound+child
|
(let [cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds bound+child)
|
||||||
cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds bound+child)
|
|
||||||
modif-tree (cond-> modif-tree
|
modif-tree (cond-> modif-tree
|
||||||
(some? cell-data)
|
(some? cell-data)
|
||||||
(set-child-modifiers cell-data bound+child))]
|
(set-child-modifiers cell-data bound+child))]
|
||||||
|
|
|
@ -298,6 +298,13 @@
|
||||||
layout-gap-col (or (-> layout-gap :column-gap (mth/finite 0)) 0)]
|
layout-gap-col (or (-> layout-gap :column-gap (mth/finite 0)) 0)]
|
||||||
[layout-gap-row layout-gap-col]))
|
[layout-gap-row layout-gap-col]))
|
||||||
|
|
||||||
|
(defn paddings
|
||||||
|
[{:keys [layout-padding-type layout-padding]}]
|
||||||
|
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding]
|
||||||
|
(if (= :simple layout-padding-type)
|
||||||
|
[pad-top pad-right pad-top pad-right]
|
||||||
|
[pad-top pad-right pad-bottom pad-left])))
|
||||||
|
|
||||||
(defn child-min-width
|
(defn child-min-width
|
||||||
[child]
|
[child]
|
||||||
(if (and (fill-width? child)
|
(if (and (fill-width? child)
|
||||||
|
@ -556,8 +563,7 @@
|
||||||
(declare assign-cells)
|
(declare assign-cells)
|
||||||
|
|
||||||
(def default-track-value
|
(def default-track-value
|
||||||
{:type :fixed
|
{:type :auto})
|
||||||
:value 100})
|
|
||||||
|
|
||||||
(def grid-cell-defaults
|
(def grid-cell-defaults
|
||||||
{:row-span 1
|
{:row-span 1
|
||||||
|
@ -740,21 +746,21 @@
|
||||||
|
|
||||||
to-add-tracks
|
to-add-tracks
|
||||||
(if (= (:layout-grid-dir parent) :row)
|
(if (= (:layout-grid-dir parent) :row)
|
||||||
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-columns parent))))
|
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-rows parent))))
|
||||||
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-rows parent)))))
|
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-columns parent)))))
|
||||||
|
|
||||||
add-track (if (= (:layout-grid-dir parent) :row) add-grid-row add-grid-column)
|
add-track (if (= (:layout-grid-dir parent) :row) add-grid-column add-grid-row)
|
||||||
|
|
||||||
parent
|
parent
|
||||||
(->> (range to-add-tracks)
|
(->> (range to-add-tracks)
|
||||||
(reduce #(add-track %1 default-track-value) parent))
|
(reduce (fn [parent _] (add-track parent default-track-value)) parent))
|
||||||
|
|
||||||
[pending-shapes cells]
|
cells
|
||||||
(loop [cells (:layout-grid-cells parent)
|
(loop [cells (:layout-grid-cells parent)
|
||||||
free-cells (get-free-cells parent {:sort? true})
|
free-cells (get-free-cells parent {:sort? true})
|
||||||
pending no-cell-shapes]
|
pending no-cell-shapes]
|
||||||
(if (or (empty? free-cells) (empty? pending))
|
(if (or (empty? free-cells) (empty? pending))
|
||||||
[pending cells]
|
cells
|
||||||
(let [next-free (first free-cells)
|
(let [next-free (first free-cells)
|
||||||
current (first pending)
|
current (first pending)
|
||||||
cells (update-in cells [next-free :shapes] conj current)]
|
cells (update-in cells [next-free :shapes] conj current)]
|
||||||
|
|
|
@ -24,6 +24,13 @@
|
||||||
(defn apply-to-point [result next-fn]
|
(defn apply-to-point [result next-fn]
|
||||||
(conj result (next-fn (last result))))
|
(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/defc track-marker
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
|
@ -57,6 +64,8 @@
|
||||||
:width (/ 26.26 zoom)
|
:width (/ 26.26 zoom)
|
||||||
:height (/ 32 zoom)
|
:height (/ 32 zoom)
|
||||||
:font-size (/ 16 zoom)
|
:font-size (/ 16 zoom)
|
||||||
|
:font-family "worksans"
|
||||||
|
:font-weight 600
|
||||||
:text-anchor "middle"
|
:text-anchor "middle"
|
||||||
:dominant-baseline "middle"
|
:dominant-baseline "middle"
|
||||||
:style {:fill "var(--color-distance)"}}
|
:style {:fill "var(--color-distance)"}}
|
||||||
|
@ -114,9 +123,7 @@
|
||||||
handle-click
|
handle-click
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps on-click)
|
(mf/deps on-click)
|
||||||
(fn [event]
|
#(when on-click (on-click)))]
|
||||||
(when on-click
|
|
||||||
(on-click))))]
|
|
||||||
|
|
||||||
[:g.plus-button {:cursor "pointer"
|
[:g.plus-button {:cursor "pointer"
|
||||||
:on-click handle-click}
|
:on-click handle-click}
|
||||||
|
@ -135,12 +142,84 @@
|
||||||
:href (dm/str "#icon-plus")
|
:href (dm/str "#icon-plus")
|
||||||
:fill "white"}]]))
|
: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/defc grid-cell
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [shape (unchecked-get props "shape")
|
(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")
|
zoom (unchecked-get props "zoom")
|
||||||
|
|
||||||
hover? (unchecked-get props "hover?")
|
hover? (unchecked-get props "hover?")
|
||||||
|
@ -152,18 +231,17 @@
|
||||||
column-track (nth column-tracks (dec column) nil)
|
column-track (nth column-tracks (dec column) nil)
|
||||||
row-track (nth row-tracks (dec row) 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)
|
start-p (gpt/add origin
|
||||||
hv #(gpo/start-hv bounds %)
|
(gpt/add
|
||||||
vv #(gpo/start-vv bounds %)
|
(gpt/to-vec origin (:start-p column-track))
|
||||||
|
(gpt/to-vec origin (:start-p row-track))))
|
||||||
start-p (-> origin
|
|
||||||
(gpt/add (hv (:distance column-track)))
|
|
||||||
(gpt/add (vv (:distance row-track))))
|
|
||||||
|
|
||||||
end-p (-> start-p
|
end-p (-> start-p
|
||||||
(gpt/add (hv (:value column-track)))
|
(gpt/add (hv (:size column-track)))
|
||||||
(gpt/add (vv (:value row-track))))
|
(gpt/add (vv (:size row-track))))
|
||||||
|
|
||||||
cell-width (- (:x end-p) (:x start-p))
|
cell-width (- (:x end-p) (:x start-p))
|
||||||
cell-height (- (:y end-p) (:y start-p))]
|
cell-height (- (:y end-p) (:y start-p))]
|
||||||
|
@ -190,34 +268,33 @@
|
||||||
(when selected?
|
(when selected?
|
||||||
(let [handlers
|
(let [handlers
|
||||||
;; Handlers positions, size and cursor
|
;; Handlers positions, size and cursor
|
||||||
[[(:x start-p) (+ (:y start-p) (/ -10 zoom)) cell-width (/ 20 zoom) (cur/scale-ns 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 (cur/scale-ew 0)]
|
[(+ (: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) (cur/scale-ns 0)]
|
[(: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 (cur/scale-ew 0)]]]
|
[(+ (:x start-p) (/ -10 zoom)) (:y start-p) (/ 20 zoom) cell-height :column]]]
|
||||||
[:*
|
[:*
|
||||||
(for [[x y width height cursor] handlers]
|
(for [[x y width height dir] handlers]
|
||||||
[:rect
|
[:& resize-cell-handler {:x x
|
||||||
{:x x
|
:y y
|
||||||
:y y
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:width width
|
:direction dir}])]))]))
|
||||||
:style {:fill "transparent" :stroke-width 0 :cursor cursor}}])]))]))
|
|
||||||
|
|
||||||
(mf/defc resize-handler
|
(mf/defc resize-handler
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[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")
|
type (unchecked-get props "type")
|
||||||
bounds (unchecked-get props "bounds")
|
|
||||||
zoom (unchecked-get props "zoom")
|
zoom (unchecked-get props "zoom")
|
||||||
|
|
||||||
width (gpo/width-points bounds)
|
|
||||||
height (gpo/height-points bounds)
|
|
||||||
|
|
||||||
dragging-ref (mf/use-ref false)
|
dragging-ref (mf/use-ref false)
|
||||||
start-ref (mf/use-ref nil)
|
start-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||||
|
|
||||||
on-pointer-down
|
on-pointer-down
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
@ -247,15 +324,15 @@
|
||||||
|
|
||||||
[x y width height]
|
[x y width height]
|
||||||
(if (= type :column)
|
(if (= type :column)
|
||||||
[(- (:x start-p) (/ 8 zoom))
|
[(- (:x start-p) (max layout-gap-col (/ 8 zoom)))
|
||||||
(- (:y start-p) (/ 40 zoom))
|
(- (:y start-p) (/ 40 zoom))
|
||||||
(/ 16 zoom)
|
(max layout-gap-col (/ 16 zoom))
|
||||||
(+ height (/ 40 zoom))]
|
(+ row-total-size row-total-gap (/ 40 zoom))]
|
||||||
|
|
||||||
[(- (:x start-p) (/ 40 zoom))
|
[(- (:x start-p) (/ 40 zoom))
|
||||||
(- (:y start-p) (/ 8 zoom))
|
(- (:y start-p) (max layout-gap-row (/ 8 zoom)))
|
||||||
(+ width (/ 40 zoom))
|
(+ column-total-size column-total-gap (/ 40 zoom))
|
||||||
(/ 16 zoom)])]
|
(max layout-gap-row (/ 16 zoom))])]
|
||||||
|
|
||||||
[:rect.resize-handler
|
[:rect.resize-handler
|
||||||
{:x x
|
{:x x
|
||||||
|
@ -295,6 +372,8 @@
|
||||||
height (gpo/height-points bounds)
|
height (gpo/height-points bounds)
|
||||||
origin (gpo/origin bounds)
|
origin (gpo/origin bounds)
|
||||||
|
|
||||||
|
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||||
|
|
||||||
{:keys [row-tracks column-tracks] :as layout-data}
|
{:keys [row-tracks column-tracks] :as layout-data}
|
||||||
(gsg/calc-layout-data shape children bounds)
|
(gsg/calc-layout-data shape children bounds)
|
||||||
|
|
||||||
|
@ -332,38 +411,76 @@
|
||||||
|
|
||||||
|
|
||||||
(for [[idx column-data] (d/enumerate column-tracks)]
|
(for [[idx column-data] (d/enumerate column-tracks)]
|
||||||
(let [start-p (-> origin (gpt/add (hv (:distance column-data))))
|
(let [start-p (:start-p column-data)
|
||||||
marker-p (-> start-p (gpt/subtract (vv (/ 20 zoom))))]
|
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
|
[:& track-marker {:center marker-p
|
||||||
:value (dm/str (inc idx))
|
:value (dm/str (inc idx))
|
||||||
:zoom zoom}]
|
:zoom zoom}]
|
||||||
|
|
||||||
[:& resize-handler {:type :column
|
[:text {:x (:x text-p)
|
||||||
:start-p start-p
|
:y (:y text-p)
|
||||||
:zoom zoom
|
:font-size (/ 14 zoom)
|
||||||
:bounds bounds}]]))
|
: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)]
|
(for [[idx row-data] (d/enumerate row-tracks)]
|
||||||
(let [start-p (-> origin (gpt/add (vv (:distance row-data))))
|
(let [start-p (:start-p row-data)
|
||||||
marker-p (-> start-p (gpt/subtract (hv (/ 20 zoom))))]
|
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))}
|
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))}
|
||||||
[:& track-marker {:center marker-p
|
[:& track-marker {:center marker-p
|
||||||
:value (dm/str (inc idx))
|
:value (dm/str (inc idx))
|
||||||
:zoom zoom}]]
|
:zoom zoom}]]
|
||||||
|
|
||||||
[:& resize-handler {:type :row
|
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x text-p) (:y text-p))}
|
||||||
:start-p start-p
|
[:text {:x (:x text-p)
|
||||||
:zoom zoom
|
:y (:y text-p)
|
||||||
:bounds bounds}]]))
|
: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)]
|
(for [[_ {:keys [column row]}] (:layout-grid-cells shape)]
|
||||||
[:& grid-cell {:shape shape
|
[:& grid-cell {:shape shape
|
||||||
:layout-data layout-data
|
:layout-data layout-data
|
||||||
:row row
|
:row row
|
||||||
:column column
|
:column column
|
||||||
:bounds bounds
|
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:hover? (contains? hover-cells [row column])
|
:hover? (contains? hover-cells [row column])
|
||||||
:selected? (= selected-cells [row column])}])]))
|
:selected? (= selected-cells [row column])}])]))
|
||||||
|
|
Loading…
Add table
Reference in a new issue