diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc
index 3f40a9d0a1..82ed2097b5 100644
--- a/common/src/app/common/geom/shapes/constraints.cljc
+++ b/common/src/app/common/geom/shapes/constraints.cljc
@@ -288,7 +288,7 @@
constraints-h
(cond
- (and (ctl/layout? parent) (not (ctl/layout-absolute? child)))
+ (and (ctl/any-layout? parent) (not (ctl/layout-absolute? child)))
:left
(not ignore-constraints)
@@ -299,7 +299,7 @@
constraints-v
(cond
- (and (ctl/layout? parent) (not (ctl/layout-absolute? child)))
+ (and (ctl/any-layout? parent) (not (ctl/layout-absolute? child)))
:top
(not ignore-constraints)
diff --git a/common/src/app/common/geom/shapes/grid_layout.cljc b/common/src/app/common/geom/shapes/grid_layout.cljc
new file mode 100644
index 0000000000..eb45960f89
--- /dev/null
+++ b/common/src/app/common/geom/shapes/grid_layout.cljc
@@ -0,0 +1,19 @@
+;; 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
+ (:require
+ [app.common.data.macros :as dm]
+ [app.common.geom.shapes.grid-layout.layout-data :as glld]
+ [app.common.geom.shapes.grid-layout.positions :as glp]))
+
+(dm/export glld/calc-layout-data)
+(dm/export glld/get-cell-data)
+(dm/export glp/child-modifiers)
+
+(defn get-drop-index
+ [frame objects _position]
+ (dec (count (get-in objects [frame :shapes]))))
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
new file mode 100644
index 0000000000..e797b1c643
--- /dev/null
+++ b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc
@@ -0,0 +1,140 @@
+;; 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.layout-data
+ (:require
+ [app.common.geom.point :as gpt]
+ [app.common.geom.shapes.points :as gpo]))
+
+#_(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-values
+ [{:keys [type value]} total-value]
+
+ (case type
+ :percent
+ (let [value (/ (* total-value value) 100) ]
+ value)
+
+ :fixed
+ value
+
+ :auto
+ 0
+ ))
+
+(defn calc-layout-data
+ [parent _children transformed-parent-bounds]
+
+ (let [height (gpo/height-points transformed-parent-bounds)
+ width (gpo/width-points 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)))))
+
+ row-tracks
+ (->> (:layout-grid-rows parent)
+ (map (fn [track]
+ (let [initial (calculate-initial-track-values track height)]
+ (assoc track :value initial)))))
+
+ ;; Go through cells to adjust auto sizes
+
+
+ ;; 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)
+
+ shape-cells
+ (into {}
+ (mapcat (fn [[_ cell]]
+ (->> (:shapes cell)
+ (map #(vector % cell)))))
+ (:layout-grid-cells parent))
+ ]
+
+ {:row-tracks row-tracks
+ :column-tracks column-tracks
+ :shape-cells shape-cells}))
+
+(defn get-cell-data
+ [{:keys [row-tracks column-tracks shape-cells]} transformed-parent-bounds [_child-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))]
+
+ (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))))]
+
+ (assoc grid-cell :start-p start-p)))))
diff --git a/common/src/app/common/geom/shapes/grid_layout/positions.cljc b/common/src/app/common/geom/shapes/grid_layout/positions.cljc
new file mode 100644
index 0000000000..3f81928b48
--- /dev/null
+++ b/common/src/app/common/geom/shapes/grid_layout/positions.cljc
@@ -0,0 +1,16 @@
+;; 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.positions
+ (:require
+ [app.common.geom.point :as gpt]
+ [app.common.geom.shapes.points :as gpo]
+ [app.common.types.modifiers :as ctm]))
+
+(defn child-modifiers
+ [_parent _transformed-parent-bounds _child child-bounds cell-data]
+ (ctm/move-modifiers
+ (gpt/subtract (:start-p cell-data) (gpo/origin child-bounds))))
diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc
index df17fefd91..62a7e784db 100644
--- a/common/src/app/common/geom/shapes/modifiers.cljc
+++ b/common/src/app/common/geom/shapes/modifiers.cljc
@@ -10,7 +10,8 @@
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.constraints :as gct]
- [app.common.geom.shapes.flex-layout :as gcl]
+ [app.common.geom.shapes.flex-layout :as gcfl]
+ [app.common.geom.shapes.grid-layout :as gcgl]
[app.common.geom.shapes.pixel-precision :as gpp]
[app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.transforms :as gtr]
@@ -46,51 +47,56 @@
:expr (or (nil? ids) (set? ids))
:hint (dm/str "tree sequence from not set: " ids))
- (letfn [(get-tree-root ;; Finds the tree root for the current id
- [id]
+ (let [get-tree-root
+ (fn ;; Finds the tree root for the current id
+ [id]
- (loop [current id
- result id]
- (let [shape (get objects current)
- parent (get objects (:parent-id shape))]
- (cond
- (or (not shape) (= uuid/zero current))
+ (loop [current id
+ result id]
+ (let [shape (get objects current)
+ parent (get objects (:parent-id shape))]
+ (cond
+ (or (not shape) (= uuid/zero current))
+ result
+
+ ;; Frame found, but not layout we return the last layout found (or the id)
+ (and (= :frame (:type parent))
+ (not (ctl/any-layout? parent)))
+ result
+
+ ;; Layout found. We continue upward but we mark this layout
+ (ctl/any-layout? parent)
+ (recur (:id parent) (:id parent))
+
+ ;; If group or boolean or other type of group we continue with the last result
+ :else
+ (recur (:id parent) result)))))
+
+ is-child? #(cph/is-child? objects %1 %2)
+
+ calculate-common-roots
+ (fn ;; Given some roots retrieves the minimum number of tree roots
+ [result id]
+ (if (= id uuid/zero)
+ result
+ (let [root (get-tree-root id)
+
+ ;; Remove the children from the current root
result
+ (if (cph/has-children? objects root)
+ (into #{} (remove #(is-child? root %)) result)
+ result)
- ;; Frame found, but not layout we return the last layout found (or the id)
- (and (= :frame (:type parent))
- (not (ctl/layout? parent)))
- result
+ root-parents (cph/get-parent-ids objects root)
+ contains-parent? (some #(contains? result %) root-parents)]
+ (cond-> result
+ (not contains-parent?)
+ (conj root)))))
- ;; Layout found. We continue upward but we mark this layout
- (ctl/layout? parent)
- (recur (:id parent) (:id parent))
-
- ;; If group or boolean or other type of group we continue with the last result
- :else
- (recur (:id parent) result)))))
-
- (calculate-common-roots ;; Given some roots retrieves the minimum number of tree roots
- [result id]
- (if (= id uuid/zero)
- result
- (let [root (get-tree-root id)
-
- ;; Remove the children from the current root
- result
- (into #{} (remove #(cph/is-child? objects root %)) result)
-
- contains-parent?
- (some #(cph/is-child? objects % root) result)]
-
- (cond-> result
- (not contains-parent?)
- (conj root)))))]
-
- (let [roots (->> ids (reduce calculate-common-roots #{}))]
- (concat
- (when (contains? ids uuid/zero) [(get objects uuid/zero)])
- (mapcat #(children-sequence % objects) roots)))))
+ roots (->> ids (reduce calculate-common-roots #{}))]
+ (concat
+ (when (contains? ids uuid/zero) [(get objects uuid/zero)])
+ (mapcat #(children-sequence % objects) roots))))
(defn- set-children-modifiers
"Propagates the modifiers from a parent too its children applying constraints if necesary"
@@ -152,7 +158,7 @@
(not (ctm/empty? modifiers))
(gtr/transform-bounds modifiers)))))
-(defn- set-layout-modifiers
+(defn- set-flex-layout-modifiers
[modif-tree children objects bounds parent transformed-parent-bounds]
(letfn [(apply-modifiers [child]
@@ -162,7 +168,7 @@
(set-child-modifiers [[layout-line modif-tree] [child-bounds child]]
(let [[modifiers layout-line]
- (gcl/layout-child-modifiers parent transformed-parent-bounds child child-bounds layout-line)
+ (gcfl/layout-child-modifiers parent transformed-parent-bounds child child-bounds layout-line)
modif-tree
(cond-> modif-tree
@@ -175,7 +181,7 @@
(map (d/getf objects))
(remove :hidden)
(map apply-modifiers))
- layout-data (gcl/calc-layout-data parent children @transformed-parent-bounds)
+ layout-data (gcfl/calc-layout-data parent children @transformed-parent-bounds)
children (into [] (cond-> children (not (:reverse? layout-data)) reverse))
max-idx (dec (count children))
layout-lines (:layout-lines layout-data)]
@@ -193,6 +199,35 @@
modif-tree)))))
+(defn- set-grid-layout-modifiers
+ [modif-tree objects bounds parent transformed-parent-bounds]
+
+ (letfn [(apply-modifiers [child]
+ [(-> (get-group-bounds objects bounds modif-tree child)
+ (gpo/parent-coords-bounds @transformed-parent-bounds))
+ child])
+ (set-child-modifiers [modif-tree cell-data [child-bounds child]]
+ (let [modifiers (gcgl/child-modifiers parent transformed-parent-bounds child child-bounds cell-data)
+ modif-tree
+ (cond-> modif-tree
+ (d/not-empty? modifiers)
+ (update-in [(:id child) :modifiers] ctm/add-modifiers modifiers))]
+ modif-tree))]
+ (let [children (->> (cph/get-immediate-children objects (:id parent))
+ (remove :hidden)
+ (map apply-modifiers))
+ grid-data (gcgl/calc-layout-data parent children @transformed-parent-bounds)]
+ (loop [modif-tree modif-tree
+ child (first children)
+ pending (rest children)]
+ (if (some? child)
+ (let [cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds child)
+ modif-tree (cond-> modif-tree
+ (some? cell-data)
+ (set-child-modifiers cell-data child))]
+ (recur modif-tree (first pending) (rest pending)))
+ modif-tree)))))
+
(defn- calc-auto-modifiers
"Calculates the modifiers to adjust the bounds for auto-width/auto-height shapes"
[objects bounds parent]
@@ -218,7 +253,7 @@
content-bounds
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
- (gcl/layout-content-bounds bounds parent children))
+ (gcfl/layout-content-bounds bounds parent children))
auto-width (when content-bounds (gpo/width-points content-bounds))
auto-height (when content-bounds (gpo/height-points content-bounds))]
@@ -254,20 +289,21 @@
modifiers (-> (dm/get-in modif-tree [parent-id :modifiers])
(ctm/select-geometry))
has-modifiers? (ctm/child-modifiers? modifiers)
- layout? (ctl/layout? parent)
+ flex-layout? (ctl/flex-layout? parent)
+ grid-layout? (ctl/grid-layout? parent)
auto? (or (ctl/auto-height? parent) (ctl/auto-width? parent))
parent? (or (cph/group-like-shape? parent) (cph/frame-shape? parent))
transformed-parent-bounds (delay (gtr/transform-bounds @(get bounds parent-id) modifiers))
children-modifiers
- (if layout?
+ (if flex-layout?
(->> (:shapes parent)
(filter #(ctl/layout-absolute? objects %)))
(:shapes parent))
children-layout
- (when layout?
+ (when flex-layout?
(->> (:shapes parent)
(remove #(ctl/layout-absolute? objects %))))]
@@ -275,8 +311,11 @@
(and has-modifiers? parent? (not root?))
(set-children-modifiers children-modifiers objects bounds parent transformed-parent-bounds ignore-constraints)
- layout?
- (set-layout-modifiers children-layout objects bounds parent transformed-parent-bounds))
+ flex-layout?
+ (set-flex-layout-modifiers children-layout objects bounds parent transformed-parent-bounds)
+
+ grid-layout?
+ (set-grid-layout-modifiers objects bounds parent transformed-parent-bounds))
;; Auto-width/height can change the positions in the parent so we need to recalculate
(cond-> autolayouts auto? (conj (:id parent)))]))
@@ -372,7 +411,7 @@
to-reflow
(cond-> to-reflow
- (and (ctl/layout-descent? objects parent-base)
+ (and (ctl/flex-layout-descent? objects parent-base)
(not= uuid/zero (:frame-id parent-base)))
(conj (:frame-id parent-base)))]
(recur modif-tree
@@ -404,6 +443,7 @@
([old-modif-tree modif-tree objects
{:keys [ignore-constraints snap-pixel? snap-precision snap-ignore-axis]
:or {ignore-constraints false snap-pixel? false snap-precision 1 snap-ignore-axis nil}}]
+
(let [objects (-> objects
(cond-> (some? old-modif-tree)
(apply-structure-modifiers old-modif-tree))
diff --git a/common/src/app/common/geom/shapes/pixel_precision.cljc b/common/src/app/common/geom/shapes/pixel_precision.cljc
index e85c708932..9994b09788 100644
--- a/common/src/app/common/geom/shapes/pixel_precision.cljc
+++ b/common/src/app/common/geom/shapes/pixel_precision.cljc
@@ -80,7 +80,7 @@
(fn [modif-tree shape]
(let [modifiers (dm/get-in modif-tree [(:id shape) :modifiers])]
(cond-> modif-tree
- (ctm/has-geometry? modifiers)
+ (and (some? modifiers) (ctm/has-geometry? modifiers))
(update-in [(:id shape) :modifiers] set-pixel-precision shape precision ignore-axis))))]
(->> (keys modif-tree)
diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc
index 3c4649c5e9..eedc9beff6 100644
--- a/common/src/app/common/pages/helpers.cljc
+++ b/common/src/app/common/pages/helpers.cljc
@@ -76,6 +76,12 @@
(and (not (frame-shape? shape))
(= (:frame-id shape) uuid/zero)))
+(defn has-children?
+ ([objects id]
+ (has-children? (get objects id)))
+ ([shape]
+ (d/not-empty? (:shapes shape))))
+
(defn get-children-ids
[objects id]
(letfn [(get-children-ids-rec
@@ -487,8 +493,17 @@
(defn is-child?
[objects parent-id candidate-child-id]
- (let [parents (get-parent-ids objects candidate-child-id)]
- (some? (d/seek #(= % parent-id) parents))))
+ (loop [cur-id candidate-child-id]
+ (let [cur-parent-id (dm/get-in objects [cur-id :parent-id])]
+ (cond
+ (= parent-id cur-parent-id)
+ true
+
+ (or (= cur-parent-id uuid/zero) (nil? cur-parent-id))
+ false
+
+ :else
+ (recur cur-parent-id)))))
(defn reduce-objects
([objects reducer-fn init-val]
diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc
index 1f7b6950ad..c669aae620 100644
--- a/common/src/app/common/types/shape/layout.cljc
+++ b/common/src/app/common/types/shape/layout.cljc
@@ -6,14 +6,17 @@
(ns app.common.types.shape.layout
(:require
+ [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.spec :as us]
+ [app.common.uuid :as uuid]
[clojure.spec.alpha :as s]))
;; :layout ;; :flex, :grid in the future
;; :layout-flex-dir ;; :row, :row-reverse, :column, :column-reverse
;; :layout-gap-type ;; :simple, :multiple
;; :layout-gap ;; {:row-gap number , :column-gap number}
+
;; :layout-align-items ;; :start :end :center :stretch
;; :layout-justify-content ;; :start :center :end :space-between :space-around :space-evenly
;; :layout-align-content ;; :start :center :end :space-between :space-around :space-evenly :stretch (by default)
@@ -21,6 +24,10 @@
;; :layout-padding-type ;; :simple, :multiple
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
+;; layout-grid-rows
+;; layout-grid-columns
+;; layout-justify-items
+
;; ITEMS
;; :layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
;; :layout-item-margin-type ;; :simple :multiple
@@ -34,15 +41,49 @@
;; :layout-item-z-index
(s/def ::layout #{:flex :grid})
+
(s/def ::layout-flex-dir #{:row :reverse-row :row-reverse :column :reverse-column :column-reverse}) ;;TODO remove reverse-column and reverse-row after script
+(s/def ::layout-grid-dir #{:row :column})
(s/def ::layout-gap-type #{:simple :multiple})
(s/def ::layout-gap ::us/safe-number)
+
(s/def ::layout-align-items #{:start :end :center :stretch})
+(s/def ::layout-justify-items #{:start :end :center :stretch})
(s/def ::layout-align-content #{:start :end :center :space-between :space-around :space-evenly :stretch})
(s/def ::layout-justify-content #{:start :center :end :space-between :space-around :space-evenly})
(s/def ::layout-wrap-type #{:wrap :nowrap :no-wrap}) ;;TODO remove no-wrap after script
(s/def ::layout-padding-type #{:simple :multiple})
+(s/def :grid/type #{:percent :flex :auto :fixed})
+(s/def :grid/value (s/nilable ::us/safe-number))
+(s/def ::grid-definition (s/keys :req-un [:grid/type]
+ :opt-un [:grid/value]))
+(s/def ::layout-grid-rows (s/coll-of ::grid-definition :kind vector?))
+(s/def ::layout-grid-columns (s/coll-of ::grid-definition :kind vector?))
+
+(s/def :grid-cell/id uuid?)
+(s/def :grid-cell/area-name ::us/string)
+(s/def :grid-cell/row-start ::us/safe-integer)
+(s/def :grid-cell/row-span ::us/safe-integer)
+(s/def :grid-cell/column-start ::us/safe-integer)
+(s/def :grid-cell/column-span ::us/safe-integer)
+(s/def :grid-cell/position #{:auto :manual :area})
+(s/def :grid-cell/align-self #{:auto :start :end :center :stretch})
+(s/def :grid-cell/justify-self #{:auto :start :end :center :stretch})
+(s/def :grid-cell/shapes (s/coll-of uuid?))
+
+(s/def ::grid-cell (s/keys :opt-un [:grid-cell/id
+ :grid-cell/area-name
+ :grid-cell/row-start
+ :grid-cell/row-span
+ :grid-cell/column-start
+ :grid-cell/column-span
+ :grid-cell/position ;; auto, manual, area
+ :grid-cell/align-self
+ :grid-cell/justify-self
+ :grid-cell/shapes]))
+(s/def ::layout-grid-cells (s/map-of uuid? ::grid-cell))
+
(s/def ::p1 ::us/safe-number)
(s/def ::p2 ::us/safe-number)
(s/def ::p3 ::us/safe-number)
@@ -67,7 +108,15 @@
::layout-padding
::layout-justify-content
::layout-align-items
- ::layout-align-content]))
+ ::layout-align-content
+
+ ;; grid
+ ::layout-grid-dir
+ ::layout-justify-items
+ ::layout-grid-rows
+ ::layout-grid-columns
+ ::layout-grid-cells
+ ]))
(s/def ::m1 ::us/safe-number)
(s/def ::m2 ::us/safe-number)
@@ -100,26 +149,61 @@
::layout-item-absolute
::layout-item-z-index]))
-(defn layout?
+(defn flex-layout?
([objects id]
- (layout? (get objects id)))
+ (flex-layout? (get objects id)))
([shape]
- (and (= :frame (:type shape)) (= :flex (:layout shape)))))
+ (and (= :frame (:type shape))
+ (= :flex (:layout shape)))))
-(defn layout-immediate-child? [objects shape]
+(defn grid-layout?
+ ([objects id]
+ (grid-layout? (get objects id)))
+ ([shape]
+ (and (= :frame (:type shape))
+ (= :grid (:layout shape)))))
+
+(defn any-layout?
+ ([objects id]
+ (any-layout? (get objects id)))
+
+ ([shape]
+ (or (flex-layout? shape) (grid-layout? shape))))
+
+(defn flex-layout-immediate-child? [objects shape]
(let [parent-id (:parent-id shape)
parent (get objects parent-id)]
- (layout? parent)))
+ (flex-layout? parent)))
-(defn layout-immediate-child-id? [objects id]
+(defn any-layout-immediate-child? [objects shape]
+ (let [parent-id (:parent-id shape)
+ parent (get objects parent-id)]
+ (any-layout? parent)))
+
+(defn flex-layout-immediate-child-id? [objects id]
(let [parent-id (dm/get-in objects [id :parent-id])
parent (get objects parent-id)]
- (layout? parent)))
+ (flex-layout? parent)))
-(defn layout-descent? [objects shape]
+(defn any-layout-immediate-child-id? [objects id]
+ (let [parent-id (dm/get-in objects [id :parent-id])
+ parent (get objects parent-id)]
+ (any-layout? parent)))
+
+(defn flex-layout-descent? [objects shape]
(let [frame-id (:frame-id shape)
frame (get objects frame-id)]
- (layout? frame)))
+ (flex-layout? frame)))
+
+(defn grid-layout-descent? [objects shape]
+ (let [frame-id (:frame-id shape)
+ frame (get objects frame-id)]
+ (grid-layout? frame)))
+
+(defn any-layout-descent? [objects shape]
+ (let [frame-id (:frame-id shape)
+ frame (get objects frame-id)]
+ (any-layout? frame)))
(defn inside-layout?
"Check if the shape is inside a layout"
@@ -360,7 +444,7 @@
(defn change-h-sizing?
[frame-id objects children-ids]
- (and (layout? objects frame-id)
+ (and (flex-layout? objects frame-id)
(auto-width? objects frame-id)
(or (and (col? objects frame-id)
(->> children-ids
@@ -373,7 +457,7 @@
(defn change-v-sizing?
[frame-id objects children-ids]
- (and (layout? objects frame-id)
+ (and (flex-layout? objects frame-id)
(auto-height? objects frame-id)
(or (and (col? objects frame-id)
(some (partial fill-height? objects) children-ids))
@@ -392,7 +476,12 @@
:layout-padding
:layout-justify-content
:layout-align-items
- :layout-align-content))
+ :layout-align-content
+ :layout-grid-dir
+ :layout-justify-items
+ :layout-grid-columns
+ :layout-grid-rows
+ ))
(defn remove-layout-item-data
[shape]
@@ -408,3 +497,91 @@
:layout-item-align-self
:layout-item-absolute
:layout-item-z-index))
+(declare assign-cells)
+
+(def grid-cell-defaults
+ {:row-span 1
+ :column-span 1
+ :position :auto
+ :align-self :auto
+ :justify-self :auto
+ :shapes []})
+
+;; TODO: GRID ASSIGNMENTS
+
+;; Adding a track creates the cells. We should check the shapes that are not tracked (with default values) and assign to the correct tracked values
+(defn add-grid-column
+ [parent value]
+ (us/assert ::grid-definition value)
+ (let [rows (:layout-grid-rows parent)
+ new-col-num (count (:layout-grid-columns parent))
+
+ layout-grid-cells
+ (->> (d/enumerate rows)
+ (reduce (fn [result [row-idx _row]]
+ (let [id (uuid/next)]
+ (assoc result id
+ (merge {:id id
+ :row (inc row-idx)
+ :column new-col-num
+ :track? true}
+ grid-cell-defaults))))
+ (:layout-grid-cells parent)))]
+ (-> parent
+ (update :layout-grid-columns (fnil conj []) value)
+ (assoc :layout-grid-cells layout-grid-cells))))
+
+(defn add-grid-row
+ [parent value]
+ (us/assert ::grid-definition value)
+ (let [cols (:layout-grid-columns parent)
+ new-row-num (inc (count (:layout-grid-rows parent)))
+
+ layout-grid-cells
+ (->> (d/enumerate cols)
+ (reduce (fn [result [col-idx _col]]
+ (let [id (uuid/next)]
+ (assoc result id
+ (merge {:id id
+ :column (inc col-idx)
+ :row new-row-num
+ :track? true}
+ grid-cell-defaults))))
+ (:layout-grid-cells parent)))]
+ (-> parent
+ (update :layout-grid-rows (fnil conj []) value)
+ (assoc :layout-grid-cells layout-grid-cells))))
+
+;; TODO: Remove a track and its corresponding cells. We need to reassign the orphaned shapes into not-tracked cells
+(defn remove-grid-column
+ [parent _index]
+ parent)
+
+(defn remove-grid-row
+ [parent _index]
+ parent)
+
+;; 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)
+
+
+;; TODO
+;; Assign cells takes the children and move them into the allotted cells. If there are not enough cells it creates
+;; not-tracked rows/columns and put the shapes there
+;; Should be caled each time a child can be added like:
+;; - On shape creation
+;; - When moving a child from layers
+;; - Moving from the transform into a cell and there are shapes without cell
+;; - Shape duplication
+;; - (maybe) create group/frames. This case will assigna a cell that had one of its children
+(defn assign-cells
+ [parent]
+ #_(let [allocated-shapes
+ (into #{} (mapcat :shapes) (:layout-grid-cells parent))
+
+ no-cell-shapes
+ (->> (:shapes parent) (remove allocated-shapes))])
+ parent)
diff --git a/common/src/app/common/types/shape_tree.cljc b/common/src/app/common/types/shape_tree.cljc
index 356b99f5d6..3997cd3b30 100644
--- a/common/src/app/common/types/shape_tree.cljc
+++ b/common/src/app/common/types/shape_tree.cljc
@@ -164,7 +164,7 @@
:else
;; If the base is a layout we should check if the z-index property is set
(let [[z-index-a z-index-b]
- (if (ctl/layout? objects base)
+ (if (ctl/any-layout? objects base)
[(ctl/layout-z-index objects (dm/get-in objects [base :shapes index-a]))
(ctl/layout-z-index objects (dm/get-in objects [base :shapes index-b]))]
[0 0])]
diff --git a/frontend/resources/images/icons/grid-justify-content-column-around.svg b/frontend/resources/images/icons/grid-justify-content-column-around.svg
new file mode 100644
index 0000000000..65cd17d584
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-column-around.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-column-between.svg b/frontend/resources/images/icons/grid-justify-content-column-between.svg
new file mode 100644
index 0000000000..783d91a210
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-column-between.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-column-center.svg b/frontend/resources/images/icons/grid-justify-content-column-center.svg
new file mode 100644
index 0000000000..fc52ce5edf
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-column-center.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-column-end.svg b/frontend/resources/images/icons/grid-justify-content-column-end.svg
new file mode 100644
index 0000000000..18825a4129
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-column-end.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-column-start.svg b/frontend/resources/images/icons/grid-justify-content-column-start.svg
new file mode 100644
index 0000000000..823ef4ebf1
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-column-start.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-row-around.svg b/frontend/resources/images/icons/grid-justify-content-row-around.svg
new file mode 100644
index 0000000000..41a980b7f5
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-row-around.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-row-between.svg b/frontend/resources/images/icons/grid-justify-content-row-between.svg
new file mode 100644
index 0000000000..bfc38460a3
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-row-between.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-row-center.svg b/frontend/resources/images/icons/grid-justify-content-row-center.svg
new file mode 100644
index 0000000000..402b8dba2d
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-row-center.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-row-end.svg b/frontend/resources/images/icons/grid-justify-content-row-end.svg
new file mode 100644
index 0000000000..3515d5a957
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-row-end.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-justify-content-row-start.svg b/frontend/resources/images/icons/grid-justify-content-row-start.svg
new file mode 100644
index 0000000000..6539a9314d
--- /dev/null
+++ b/frontend/resources/images/icons/grid-justify-content-row-start.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/frontend/resources/images/icons/grid-layout-mode.svg b/frontend/resources/images/icons/grid-layout-mode.svg
new file mode 100644
index 0000000000..c90cf5c859
--- /dev/null
+++ b/frontend/resources/images/icons/grid-layout-mode.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/set-thumbnail.svg b/frontend/resources/images/icons/set-thumbnail.svg
index c90cf5c859..e82fa55b18 100644
--- a/frontend/resources/images/icons/set-thumbnail.svg
+++ b/frontend/resources/images/icons/set-thumbnail.svg
@@ -1,3 +1,3 @@
-