mirror of
https://github.com/penpot/penpot.git
synced 2025-07-31 05:58:46 +02:00
✨ Improved flex tracks behavior and auto sizing
This commit is contained in:
parent
06ab577e41
commit
e86939b8ee
7 changed files with 197 additions and 41 deletions
|
@ -7,6 +7,7 @@
|
||||||
(ns app.common.geom.shapes.grid-layout
|
(ns app.common.geom.shapes.grid-layout
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.geom.shapes.grid-layout.bounds :as glpb]
|
||||||
[app.common.geom.shapes.grid-layout.layout-data :as glld]
|
[app.common.geom.shapes.grid-layout.layout-data :as glld]
|
||||||
[app.common.geom.shapes.grid-layout.positions :as glp]))
|
[app.common.geom.shapes.grid-layout.positions :as glp]))
|
||||||
|
|
||||||
|
@ -16,3 +17,5 @@
|
||||||
(dm/export glp/get-position-grid-coord)
|
(dm/export glp/get-position-grid-coord)
|
||||||
(dm/export glp/get-drop-cell)
|
(dm/export glp/get-drop-cell)
|
||||||
(dm/export glp/cell-bounds)
|
(dm/export glp/cell-bounds)
|
||||||
|
(dm/export glpb/layout-content-points)
|
||||||
|
(dm/export glpb/layout-content-bounds)
|
||||||
|
|
53
common/src/app/common/geom/shapes/grid_layout/bounds.cljc
Normal file
53
common/src/app/common/geom/shapes/grid_layout/bounds.cljc
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
;; 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
|
||||||
|
|
||||||
|
(ns app.common.geom.shapes.grid-layout.bounds
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes.grid-layout.layout-data :as ld]
|
||||||
|
[app.common.geom.shapes.points :as gpo]))
|
||||||
|
|
||||||
|
(defn layout-content-points
|
||||||
|
[bounds parent children]
|
||||||
|
(let [parent-id (:id parent)
|
||||||
|
parent-bounds @(get bounds parent-id)
|
||||||
|
|
||||||
|
hv #(gpo/start-hv parent-bounds %)
|
||||||
|
vv #(gpo/start-vv parent-bounds %)
|
||||||
|
|
||||||
|
children (->> children
|
||||||
|
(map #(vector @(get bounds (:id %)) %)))
|
||||||
|
|
||||||
|
{:keys [row-tracks column-tracks]} (ld/calc-layout-data parent children parent-bounds)]
|
||||||
|
(d/concat-vec
|
||||||
|
(->> row-tracks
|
||||||
|
(mapcat #(vector (:start-p %)
|
||||||
|
(gpt/add (:start-p %) (vv (:size %))))))
|
||||||
|
(->> column-tracks
|
||||||
|
(mapcat #(vector (:start-p %)
|
||||||
|
(gpt/add (:start-p %) (hv (:size %)))))))))
|
||||||
|
|
||||||
|
(defn layout-content-bounds
|
||||||
|
[bounds {:keys [layout-padding] :as parent} children]
|
||||||
|
|
||||||
|
(let [parent-id (:id parent)
|
||||||
|
parent-bounds @(get bounds parent-id)
|
||||||
|
|
||||||
|
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
||||||
|
pad-top (or pad-top 0)
|
||||||
|
pad-right (or pad-right 0)
|
||||||
|
pad-bottom (or pad-bottom 0)
|
||||||
|
pad-left (or pad-left 0)
|
||||||
|
|
||||||
|
layout-points (layout-content-points bounds parent children)]
|
||||||
|
|
||||||
|
(if (d/not-empty? layout-points)
|
||||||
|
(-> layout-points
|
||||||
|
(gpo/merge-parent-coords-bounds parent-bounds)
|
||||||
|
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)))
|
||||||
|
;; Cannot create some bounds from the children so we return the parent's
|
||||||
|
parent-bounds)))
|
|
@ -52,6 +52,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[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.math :as mth]
|
||||||
[app.common.types.shape.layout :as ctl]))
|
[app.common.types.shape.layout :as ctl]))
|
||||||
|
|
||||||
(defn layout-bounds
|
(defn layout-bounds
|
||||||
|
@ -100,7 +101,8 @@
|
||||||
idx (dec (get cell prop))
|
idx (dec (get cell prop))
|
||||||
track (get tracks idx)]
|
track (get tracks idx)]
|
||||||
(cond-> tracks
|
(cond-> tracks
|
||||||
(and (= (get cell prop-span) 1) (= :auto (:type track)))
|
(and (= (get cell prop-span) 1)
|
||||||
|
(contains? #{:flex :auto} (:type track)))
|
||||||
(update-in [idx :size] max (size-fn child-shape child-bounds)))))
|
(update-in [idx :size] max (size-fn child-shape child-bounds)))))
|
||||||
track-list
|
track-list
|
||||||
children)))
|
children)))
|
||||||
|
@ -129,13 +131,68 @@
|
||||||
(cond-> acc (= type :auto) (inc)))]
|
(cond-> acc (= type :auto) (inc)))]
|
||||||
(->> track-list (reduce calc-tracks-total-autos 0))))
|
(->> track-list (reduce calc-tracks-total-autos 0))))
|
||||||
|
|
||||||
|
|
||||||
(defn set-fr-value
|
(defn set-fr-value
|
||||||
[track-list fr-value]
|
"Tries to assign the fr value distributing the excess between the free spaces"
|
||||||
(->> track-list
|
[track-list fr-value auto?]
|
||||||
(mapv (fn [{:keys [type value max-size] :as track}]
|
|
||||||
(cond-> track
|
(let [flex? #(= :flex (:type (second %)))
|
||||||
(= :flex type)
|
|
||||||
(assoc :size (min (* value fr-value) max-size)))))))
|
;; Fixes the assignments so they respect the min size constraint
|
||||||
|
;; returns pending with the necessary space to allocate and free-frs
|
||||||
|
;; are the addition of the fr tracks with free space
|
||||||
|
assign-fn
|
||||||
|
(fn [[assign-fr pending free-frs] [idx t]]
|
||||||
|
(let [fr (:value t)
|
||||||
|
current (get assign-fr idx (* fr-value fr))
|
||||||
|
full? (<= current (:size t))
|
||||||
|
cur-pending (if full? (- (:size t) current) 0)]
|
||||||
|
[(assoc assign-fr idx (if full? (:size t) current))
|
||||||
|
(+ pending cur-pending)
|
||||||
|
(cond-> free-frs (not full?) (+ fr))]))
|
||||||
|
|
||||||
|
;; Sets the assigned-fr map removing the pending/free-frs
|
||||||
|
change-fn
|
||||||
|
(fn [delta]
|
||||||
|
(fn [assign-fr [idx t]]
|
||||||
|
(let [fr (:value t)
|
||||||
|
current (get assign-fr idx)
|
||||||
|
full? (<= current (:size t))]
|
||||||
|
(cond-> assign-fr
|
||||||
|
(not full?)
|
||||||
|
(update idx - (* delta fr))))))
|
||||||
|
|
||||||
|
assign-fr
|
||||||
|
(loop [assign-fr {}]
|
||||||
|
(let [[assign-fr pending free-frs]
|
||||||
|
(->> (d/enumerate track-list)
|
||||||
|
(filter flex?)
|
||||||
|
(reduce assign-fn [assign-fr 0 0]))]
|
||||||
|
|
||||||
|
;; When auto, we don't need to remove the excess
|
||||||
|
(if (or auto?
|
||||||
|
(= free-frs 0)
|
||||||
|
(mth/almost-zero? pending))
|
||||||
|
assign-fr
|
||||||
|
|
||||||
|
(let [delta (/ pending free-frs)
|
||||||
|
assign-fr
|
||||||
|
(->> (d/enumerate track-list)
|
||||||
|
(filter flex?)
|
||||||
|
(reduce (change-fn delta) assign-fr))]
|
||||||
|
|
||||||
|
(recur assign-fr)))))
|
||||||
|
|
||||||
|
;; Apply assign-fr to the track-list
|
||||||
|
track-list
|
||||||
|
(reduce
|
||||||
|
(fn [track-list [idx assignment] ]
|
||||||
|
(-> track-list
|
||||||
|
(update-in [idx :size] max assignment)))
|
||||||
|
track-list
|
||||||
|
assign-fr)]
|
||||||
|
|
||||||
|
track-list))
|
||||||
|
|
||||||
(defn add-auto-size
|
(defn add-auto-size
|
||||||
[track-list add-size]
|
[track-list add-size]
|
||||||
|
@ -305,6 +362,15 @@
|
||||||
track-list)]
|
track-list)]
|
||||||
track-list))
|
track-list))
|
||||||
|
|
||||||
|
(defn min-fr-value
|
||||||
|
[tracks]
|
||||||
|
(loop [tracks (seq tracks)
|
||||||
|
min-fr 0.01]
|
||||||
|
(if (empty? tracks)
|
||||||
|
min-fr
|
||||||
|
(let [{:keys [size type value]} (first tracks)
|
||||||
|
min-fr (if (= type :flex) (max min-fr (/ size value)) min-fr)]
|
||||||
|
(recur (rest tracks) min-fr)))))
|
||||||
|
|
||||||
(defn calc-layout-data
|
(defn calc-layout-data
|
||||||
[parent children transformed-parent-bounds]
|
[parent children transformed-parent-bounds]
|
||||||
|
@ -319,13 +385,22 @@
|
||||||
bound-corner (gpo/origin layout-bounds)
|
bound-corner (gpo/origin layout-bounds)
|
||||||
|
|
||||||
[row-gap column-gap] (ctl/gaps parent)
|
[row-gap column-gap] (ctl/gaps parent)
|
||||||
|
auto-height? (ctl/auto-height? parent)
|
||||||
|
auto-width? (ctl/auto-width? parent)
|
||||||
|
|
||||||
|
{:keys [layout-grid-columns layout-grid-rows layout-grid-cells]} parent
|
||||||
|
num-columns (count layout-grid-columns)
|
||||||
|
num-rows (count layout-grid-rows)
|
||||||
|
|
||||||
|
column-total-gap (* column-gap (dec num-columns))
|
||||||
|
row-total-gap (* row-gap (dec num-rows))
|
||||||
|
|
||||||
;; Map shape->cell
|
;; Map shape->cell
|
||||||
shape-cells
|
shape-cells
|
||||||
(into {}
|
(into {}
|
||||||
(mapcat (fn [[_ cell]]
|
(mapcat (fn [[_ cell]]
|
||||||
(->> (:shapes cell) (map #(vector % cell)))))
|
(->> (:shapes cell) (map #(vector % cell)))))
|
||||||
(:layout-grid-cells parent))
|
layout-grid-cells)
|
||||||
|
|
||||||
children (->> children (remove #(ctl/layout-absolute? (second %))))
|
children (->> children (remove #(ctl/layout-absolute? (second %))))
|
||||||
children-map
|
children-map
|
||||||
|
@ -335,11 +410,11 @@
|
||||||
|
|
||||||
;; Initialize tracks
|
;; Initialize tracks
|
||||||
column-tracks
|
column-tracks
|
||||||
(->> (:layout-grid-columns parent)
|
(->> layout-grid-columns
|
||||||
(mapv (partial calculate-initial-track-size bound-width)))
|
(mapv (partial calculate-initial-track-size bound-width)))
|
||||||
|
|
||||||
row-tracks
|
row-tracks
|
||||||
(->> (:layout-grid-rows parent)
|
(->> layout-grid-rows
|
||||||
(mapv (partial calculate-initial-track-size bound-height)))
|
(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
|
;; Go through cells to adjust auto sizes for span=1. Base is the max of its children
|
||||||
|
@ -351,11 +426,8 @@
|
||||||
row-tracks (set-auto-multi-span parent row-tracks children-map shape-cells :row)
|
row-tracks (set-auto-multi-span parent row-tracks children-map shape-cells :row)
|
||||||
|
|
||||||
;; Calculate the `fr` unit and adjust the size
|
;; Calculate the `fr` unit and adjust the size
|
||||||
column-total-size (tracks-total-size column-tracks)
|
column-total-size-nofr (tracks-total-size (->> column-tracks (remove #(= :flex (:type %)))))
|
||||||
row-total-size (tracks-total-size row-tracks)
|
row-total-size-nofr (tracks-total-size (->> row-tracks (remove #(= :flex (:type %)))))
|
||||||
|
|
||||||
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)
|
column-frs (tracks-total-frs column-tracks)
|
||||||
row-frs (tracks-total-frs row-tracks)
|
row-frs (tracks-total-frs row-tracks)
|
||||||
|
@ -367,22 +439,28 @@
|
||||||
row-tracks (set-flex-multi-span parent row-tracks children-map shape-cells :row)
|
row-tracks (set-flex-multi-span parent row-tracks children-map shape-cells :row)
|
||||||
|
|
||||||
;; Once auto sizes have been calculated we get calculate the `fr` unit with the remainining size and adjust the size
|
;; 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-column-space (max 0 (- bound-width (+ column-total-size-nofr column-total-gap)))
|
||||||
free-row-space (- bound-height (+ row-total-size row-total-gap))
|
free-row-space (max 0 (- bound-height (+ row-total-size-nofr 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)
|
;; Get the minimum values for fr's
|
||||||
row-tracks (set-fr-value row-tracks row-fr)
|
min-column-fr (min-fr-value column-tracks)
|
||||||
|
min-row-fr (min-fr-value row-tracks)
|
||||||
|
|
||||||
|
column-fr (if auto-width? min-column-fr (mth/finite (/ free-column-space column-frs) 0))
|
||||||
|
row-fr (if auto-height? min-row-fr (mth/finite (/ free-row-space row-frs) 0))
|
||||||
|
|
||||||
|
column-tracks (set-fr-value column-tracks column-fr auto-width?)
|
||||||
|
row-tracks (set-fr-value row-tracks row-fr auto-height?)
|
||||||
|
|
||||||
;; Distribute free space between `auto` tracks
|
;; Distribute free space between `auto` tracks
|
||||||
column-total-size (tracks-total-size column-tracks)
|
column-total-size (tracks-total-size column-tracks)
|
||||||
row-total-size (tracks-total-size row-tracks)
|
row-total-size (tracks-total-size row-tracks)
|
||||||
|
|
||||||
free-column-space (- bound-width (+ column-total-size column-total-gap))
|
free-column-space (max 0 (if auto-width? 0 (- bound-width (+ column-total-size column-total-gap))))
|
||||||
free-row-space (- bound-height (+ row-total-size row-total-gap))
|
free-row-space (max 0 (if auto-height? 0 (- bound-height (+ row-total-size row-total-gap))))
|
||||||
column-autos (tracks-total-autos column-tracks)
|
column-autos (tracks-total-autos column-tracks)
|
||||||
row-autos (tracks-total-autos row-tracks)
|
row-autos (tracks-total-autos row-tracks)
|
||||||
|
|
||||||
column-add-auto (/ free-column-space column-autos)
|
column-add-auto (/ free-column-space column-autos)
|
||||||
row-add-auto (/ free-row-space row-autos)
|
row-add-auto (/ free-row-space row-autos)
|
||||||
|
|
||||||
|
@ -394,6 +472,9 @@
|
||||||
|
|
||||||
column-gap
|
column-gap
|
||||||
(case (:layout-align-content parent)
|
(case (:layout-align-content parent)
|
||||||
|
auto-width?
|
||||||
|
column-gap
|
||||||
|
|
||||||
:space-evenly
|
:space-evenly
|
||||||
(max column-gap (/ (- bound-width column-total-size) (inc (count column-tracks))))
|
(max column-gap (/ (- bound-width column-total-size) (inc (count column-tracks))))
|
||||||
|
|
||||||
|
@ -407,6 +488,9 @@
|
||||||
|
|
||||||
row-gap
|
row-gap
|
||||||
(case (:layout-justify-content parent)
|
(case (:layout-justify-content parent)
|
||||||
|
auto-height?
|
||||||
|
row-gap
|
||||||
|
|
||||||
:space-evenly
|
:space-evenly
|
||||||
(max row-gap (/ (- bound-height row-total-size) (inc (count row-tracks))))
|
(max row-gap (/ (- bound-height row-total-size) (inc (count row-tracks))))
|
||||||
|
|
||||||
|
@ -420,29 +504,28 @@
|
||||||
|
|
||||||
start-p
|
start-p
|
||||||
(cond-> bound-corner
|
(cond-> bound-corner
|
||||||
(= :end (:layout-align-content parent))
|
(and (not auto-width?) (= :end (:layout-align-content parent)))
|
||||||
(gpt/add (hv (- bound-width (+ column-total-size column-total-gap))))
|
(gpt/add (hv (- bound-width (+ column-total-size column-total-gap))))
|
||||||
|
|
||||||
(= :center (:layout-align-content parent))
|
(and (not auto-width?) (= :center (:layout-align-content parent)))
|
||||||
(gpt/add (hv (/ (- bound-width (+ column-total-size column-total-gap)) 2)))
|
(gpt/add (hv (/ (- bound-width (+ column-total-size column-total-gap)) 2)))
|
||||||
|
|
||||||
(= :end (:layout-justify-content parent))
|
(and (not auto-height?) (= :end (:layout-justify-content parent)))
|
||||||
(gpt/add (vv (- bound-height (+ row-total-size row-total-gap))))
|
(gpt/add (vv (- bound-height (+ row-total-size row-total-gap))))
|
||||||
|
|
||||||
(= :center (:layout-justify-content parent))
|
(and (not auto-height?) (= :center (:layout-justify-content parent)))
|
||||||
(gpt/add (vv (/ (- bound-height (+ row-total-size row-total-gap)) 2)))
|
(gpt/add (vv (/ (- bound-height (+ row-total-size row-total-gap)) 2)))
|
||||||
|
|
||||||
|
(and (not auto-width?) (= :space-around (:layout-align-content parent)))
|
||||||
(= :space-around (:layout-align-content parent))
|
|
||||||
(gpt/add (hv (/ column-gap 2)))
|
(gpt/add (hv (/ column-gap 2)))
|
||||||
|
|
||||||
(= :space-evenly (:layout-align-content parent))
|
(and (not auto-width?) (= :space-evenly (:layout-align-content parent)))
|
||||||
(gpt/add (hv column-gap))
|
(gpt/add (hv column-gap))
|
||||||
|
|
||||||
(= :space-around (:layout-justify-content parent))
|
(and (not auto-height?) (= :space-around (:layout-justify-content parent)))
|
||||||
(gpt/add (vv (/ row-gap 2)))
|
(gpt/add (vv (/ row-gap 2)))
|
||||||
|
|
||||||
(= :space-evenly (:layout-justify-content parent))
|
(and (not auto-height?) (= :space-evenly (:layout-justify-content parent)))
|
||||||
(gpt/add (vv row-gap)))
|
(gpt/add (vv row-gap)))
|
||||||
|
|
||||||
column-tracks
|
column-tracks
|
||||||
|
|
|
@ -256,7 +256,12 @@
|
||||||
|
|
||||||
content-bounds
|
content-bounds
|
||||||
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
|
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
|
||||||
(gcfl/layout-content-bounds bounds parent children))
|
(cond
|
||||||
|
(ctl/flex-layout? parent)
|
||||||
|
(gcfl/layout-content-bounds bounds parent children)
|
||||||
|
|
||||||
|
(ctl/grid-layout? parent)
|
||||||
|
(gcgl/layout-content-bounds bounds parent children)))
|
||||||
|
|
||||||
auto-width (when content-bounds (gpo/width-points content-bounds))
|
auto-width (when content-bounds (gpo/width-points content-bounds))
|
||||||
auto-height (when content-bounds (gpo/height-points content-bounds))]
|
auto-height (when content-bounds (gpo/height-points content-bounds))]
|
||||||
|
|
|
@ -597,6 +597,6 @@
|
||||||
{:zoom zoom
|
{:zoom zoom
|
||||||
:objects base-objects
|
:objects base-objects
|
||||||
:modifiers modifiers
|
:modifiers modifiers
|
||||||
:shape (or (get objects-modified edition)
|
:shape (or (get base-objects edition)
|
||||||
(get base-objects @hover-top-frame-id))
|
(get base-objects @hover-top-frame-id))
|
||||||
:view-only (not show-grid-editor?)}]])]]]))
|
:view-only (not show-grid-editor?)}]])]]]))
|
||||||
|
|
|
@ -40,8 +40,19 @@
|
||||||
(let [children (->> (cph/get-immediate-children objects (:id shape))
|
(let [children (->> (cph/get-immediate-children objects (:id shape))
|
||||||
(remove :hidden))
|
(remove :hidden))
|
||||||
bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points]))
|
bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points]))
|
||||||
layout-bounds (gsl/layout-content-bounds bounds shape children)
|
layout-bounds
|
||||||
layout-points (flatten (gsl/layout-content-points bounds shape children))]
|
(cond (ctl/flex-layout? shape)
|
||||||
|
(gsl/layout-content-bounds bounds shape children)
|
||||||
|
|
||||||
|
(ctl/grid-layout? shape)
|
||||||
|
(gsg/layout-content-bounds bounds shape children))
|
||||||
|
layout-points
|
||||||
|
(cond (ctl/flex-layout? shape)
|
||||||
|
(flatten (gsl/layout-content-points bounds shape children))
|
||||||
|
|
||||||
|
(ctl/grid-layout? shape)
|
||||||
|
(flatten (gsg/layout-content-points bounds shape children)))]
|
||||||
|
|
||||||
[:g.debug-layout {:pointer-events "none"}
|
[:g.debug-layout {:pointer-events "none"}
|
||||||
[:polygon {:points (->> layout-bounds (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
|
[:polygon {:points (->> layout-bounds (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
|
||||||
:style {:stroke "red" :fill "none"}}]
|
:style {:stroke "red" :fill "none"}}]
|
||||||
|
|
|
@ -665,11 +665,12 @@
|
||||||
zoom (unchecked-get props "zoom")
|
zoom (unchecked-get props "zoom")
|
||||||
view-only (unchecked-get props "view-only")
|
view-only (unchecked-get props "view-only")
|
||||||
|
|
||||||
shape (mf/use-memo
|
shape
|
||||||
(mf/deps modifiers base-shape)
|
(mf/use-memo
|
||||||
#(gsh/transform-shape
|
(mf/deps modifiers base-shape)
|
||||||
base-shape
|
#(gsh/transform-shape
|
||||||
(dm/get-in modifiers [(:id base-shape) :modifiers])))
|
base-shape
|
||||||
|
(dm/get-in modifiers [(:id base-shape) :modifiers])))
|
||||||
|
|
||||||
snap-pixel? (mf/deref refs/snap-pixel?)
|
snap-pixel? (mf/deref refs/snap-pixel?)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue