From 2df40ad767a9a8bd3fc184858e78d4c908cc54be Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 12 Apr 2023 16:26:40 +0200 Subject: [PATCH] :sparkles: Adds grid column/row sizing without spanned tracks --- .../common/geom/shapes/flex_layout/lines.cljc | 5 +- .../geom/shapes/grid_layout/layout_data.cljc | 302 ++++++++++++++---- .../src/app/common/geom/shapes/modifiers.cljc | 3 +- common/src/app/common/types/shape/layout.cljc | 22 +- .../viewport/grid_layout_editor.cljs | 215 ++++++++++--- 5 files changed, 424 insertions(+), 123 deletions(-) diff --git a/common/src/app/common/geom/shapes/flex_layout/lines.cljc b/common/src/app/common/geom/shapes/flex_layout/lines.cljc index 1aa12f6e9..14b4d110d 100644 --- a/common/src/app/common/geom/shapes/flex_layout/lines.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/lines.cljc @@ -15,9 +15,8 @@ (def conjv (fnil conj [])) (defn layout-bounds - [{:keys [layout-padding] :as shape} shape-bounds] - (let [;; Add padding to the bounds - {pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding] + [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 init-layout-lines diff --git a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc index 73d500b57..f45fb7326 100644 --- a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc +++ b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc @@ -4,10 +4,63 @@ ;; ;; 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 (:require [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 [parent children] @@ -50,91 +103,218 @@ [parent children])) -(defn calculate-initial-track-values - [{:keys [type value]} total-value] +(defn calculate-initial-track-size + [total-value {:keys [type value] :as track}] - (case type - :percent - (let [value (/ (* total-value value) 100) ] - value) + (let [[size max-size] + (case type + :percent + (let [value (/ (* total-value value) 100) ] + [value value]) - :fixed - value + :fixed + [value value] - :auto - 0 - )) + ;; flex, auto + [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 - [parent _children transformed-parent-bounds] + [parent children transformed-parent-bounds] - (let [height (gpo/height-points transformed-parent-bounds) - width (gpo/width-points transformed-parent-bounds) + (let [hv #(gpo/start-hv transformed-parent-bounds %) + vv #(gpo/start-vv transformed-parent-bounds %) - ;; Initialize tracks - column-tracks - (->> (:layout-grid-columns parent) - (map (fn [track] - (let [initial (calculate-initial-track-values track width)] - (assoc track :value initial))))) + layout-bounds (layout-bounds parent transformed-parent-bounds) - row-tracks - (->> (:layout-grid-rows parent) - (map (fn [track] - (let [initial (calculate-initial-track-values track height)] - (assoc track :value initial))))) + bound-height (gpo/height-points layout-bounds) + bound-width (gpo/width-points layout-bounds) + bound-corner (gpo/origin layout-bounds) - ;; 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 - - - ;; 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) - + ;; Map shape->cell shape-cells (into {} (mapcat (fn [[_ cell]] - (->> (:shapes cell) - (map #(vector % cell))))) + (->> (:shapes cell) (map #(vector % cell))))) (: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 - :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 - [{: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) - hv #(gpo/start-hv transformed-parent-bounds %) - vv #(gpo/start-vv transformed-parent-bounds %) - - grid-cell (get shape-cells (:id 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) - start-p (-> origin - (gpt/add (hv (:distance column))) - (gpt/add (vv (:distance row))))] + column-start-p (:start-p column) + row-start-p (:start-p 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))))) diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 9b0fd7722..37dd7019a 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -221,8 +221,7 @@ bound+child (first children) pending (rest children)] (if (some? bound+child) - (let [[_ child] bound+child - cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds bound+child) + (let [cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds bound+child) modif-tree (cond-> modif-tree (some? cell-data) (set-child-modifiers cell-data bound+child))] diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index 3ce36466f..8fc029b43 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -298,6 +298,13 @@ layout-gap-col (or (-> layout-gap :column-gap (mth/finite 0)) 0)] [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 [child] (if (and (fill-width? child) @@ -556,8 +563,7 @@ (declare assign-cells) (def default-track-value - {:type :fixed - :value 100}) + {:type :auto}) (def grid-cell-defaults {:row-span 1 @@ -740,21 +746,21 @@ to-add-tracks (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 (->> (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) free-cells (get-free-cells parent {:sort? true}) pending no-cell-shapes] (if (or (empty? free-cells) (empty? pending)) - [pending cells] + cells (let [next-free (first free-cells) current (first pending) cells (update-in cells [next-free :shapes] conj current)] diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs index 897e03eaa..af7897717 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs @@ -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])}])]))