From fde0bcfd3e9583f74208f7755149a3962cabf8df Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 3 May 2024 13:44:31 +0200 Subject: [PATCH] :sparkles: Add grid layout options to context --- .../app/main/data/workspace/shape_layout.cljs | 18 +- frontend/src/app/plugins/grid.cljs | 162 ++++++++++++++++++ frontend/src/app/plugins/shape.cljs | 67 +++++--- frontend/src/app/plugins/utils.cljs | 38 ++-- 4 files changed, 245 insertions(+), 40 deletions(-) create mode 100644 frontend/src/app/plugins/grid.cljs diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 37e40cf91..b3c0d513f 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -72,7 +72,7 @@ :layout-grid-columns []}) (defn get-layout-initializer - [type from-frame?] + [type from-frame? calculate-params?] (let [[initial-layout-data calculate-params] (case type :flex [initial-flex-layout flex/calculate-params] @@ -87,9 +87,11 @@ (cond-> (not from-frame?) (assoc :show-content true :hide-in-viewer true))) - params (calculate-params objects (cfh/get-immediate-children objects (:id shape)) shape)] + params (when calculate-params? + (calculate-params objects (cfh/get-immediate-children objects (:id shape)) shape))] (cond-> (merge shape params) - (= type :grid) (-> (ctl/assign-cells objects) ctl/reorder-grid-children)))))) + (= type :grid) + (-> (ctl/assign-cells objects) ctl/reorder-grid-children)))))) ;; Never call this directly but through the data-event `:layout/update` ;; Otherwise a lot of cycle dependencies could be generated @@ -124,7 +126,7 @@ (ptk/reify ::finalize)) (defn create-layout-from-id - [id type from-frame?] + [id type & {:keys [from-frame? calculate-params?] :or {from-frame? false calculate-params? true}}] (dm/assert! "expected uuid for `id`" (uuid? id)) @@ -135,7 +137,7 @@ (let [objects (wsh/lookup-page-objects state) parent (get objects id) undo-id (js/Symbol) - layout-initializer (get-layout-initializer type from-frame?)] + layout-initializer (get-layout-initializer type from-frame? calculate-params?)] (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes [id] layout-initializer {:with-objects? true}) @@ -177,7 +179,7 @@ (dwse/select-shapes ordered-ids) (dwsh/create-artboard-from-selection new-shape-id parent-id group-index (:name (first selected-shapes))) (cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1}) - (create-layout-from-id new-shape-id type false) + (create-layout-from-id new-shape-id type) (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)) (dwsh/delete-shapes page-id selected) @@ -188,7 +190,7 @@ (rx/of (dwsh/create-artboard-from-selection new-shape-id) (cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1}) - (create-layout-from-id new-shape-id type false) + (create-layout-from-id new-shape-id type) (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)))) @@ -227,7 +229,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (if (and single? is-frame?) - (create-layout-from-id (first selected) type true) + (create-layout-from-id (first selected) type :from-frame? true) (create-layout-from-selection type)) (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/plugins/grid.cljs b/frontend/src/app/plugins/grid.cljs new file mode 100644 index 000000000..7dc46bc9e --- /dev/null +++ b/frontend/src/app/plugins/grid.cljs @@ -0,0 +1,162 @@ +;; 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.plugins.grid + (:require + [app.common.data :as d] + [app.common.record :as crc] + [app.common.spec :as us] + [app.common.types.shape.layout :as ctl] + [app.main.data.workspace.shape-layout :as dwsl] + [app.main.store :as st] + [app.plugins.utils :as utils :refer [get-data get-state]])) + +(defn- make-tracks + [tracks] + (.freeze + js/Object + (apply array (->> tracks (map utils/to-js))))) + +(deftype GridLayout [_data] + Object + + (addRow + [self type value] + (let [id (get-data self :id) + type (keyword type)] + (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value})))) + + (addRowAtIndex + [self type value index] + (let [id (get-data self :id) + type (keyword type)] + (st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index)))) + + (addColumn + [self type value] + (let [id (get-data self :id) + type (keyword type)] + (st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value})))) + + (addColumnAtIndex + [self type value index] + (let [id (get-data self :id) + type (keyword type)] + (st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value} index)))) + + (removeRow + [self index] + (let [id (get-data self :id)] + (st/emit! (dwsl/remove-layout-track #{id} :row index)))) + + (removeColumn + [self index] + (let [id (get-data self :id)] + (st/emit! (dwsl/remove-layout-track #{id} :column index)))) + + (setColumn + [self index type value] + (let [id (get-data self :id) + type (keyword type)] + (st/emit! (dwsl/change-layout-track #{id} :column index (d/without-nils {:type type :value value}))))) + + (setRow + [self index type value] + (let [id (get-data self :id) + type (keyword type)] + (st/emit! (dwsl/change-layout-track #{id} :row index (d/without-nils {:type type :value value}))))) + + (remove + [self] + (let [id (get-data self :id)] + (st/emit! (dwsl/remove-layout #{id}))))) + +(defn grid-layout-proxy + [data] + (-> (GridLayout. data) + (crc/add-properties! + {:name "dir" + :get #(get-state % :layout-grid-dir d/name) + :set + (fn [self value] + (let [id (get-data self :id) + value (keyword value)] + (when (contains? ctl/grid-direction-types value) + (st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value})))))} + + {:name "rows" + :get #(get-state % :layout-grid-rows make-tracks)} + + {:name "columns" + :get #(get-state % :layout-grid-columns make-tracks)} + + {:name "alignItems" + :get #(get-state % :layout-align-items d/name) + :set + (fn [self value] + (let [id (get-data self :id) + value (keyword value)] + (when (contains? ctl/align-items-types value) + (st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))} + + {:name "alignContent" + :get #(get-state % :layout-align-content d/name) + :set + (fn [self value] + (let [id (get-data self :id) + value (keyword value)] + (when (contains? ctl/align-content-types value) + (st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))} + + {:name "justifyItems" + :get #(get-state % :layout-justify-items d/name) + :set + (fn [self value] + (let [id (get-data self :id) + value (keyword value)] + (when (contains? ctl/justify-items-types value) + (st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))} + + {:name "justifyContent" + :get #(get-state % :layout-justify-content d/name) + :set + (fn [self value] + (let [id (get-data self :id) + value (keyword value)] + (when (contains? ctl/justify-content-types value) + (st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))} + + {:name "rowGap" + :get #(:row-gap (get-state % :layout-gap)) + :set + (fn [self value] + (let [id (get-data self :id)] + (when (us/safe-int? value) + (st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}})))))} + + {:name "columnGap" + :get #(:column-gap (get-state % :layout-gap)) + :set + (fn [self value] + (let [id (get-data self :id)] + (when (us/safe-int? value) + (st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}})))))} + + {:name "verticalPadding" + :get #(:p1 (get-state % :layout-padding)) + :set + (fn [self value] + (let [id (get-data self :id)] + (when (us/safe-int? value) + (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}})))))} + + {:name "horizontalPadding" + :get #(:p2 (get-state % :layout-padding)) + :set + (fn [self value] + (let [id (get-data self :id)] + (when (us/safe-int? value) + (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}))) diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index b537109d7..e4b466b2e 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -7,7 +7,6 @@ (ns app.plugins.shape "RPC for plugins runtime." (:require - [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.record :as crc] @@ -16,9 +15,11 @@ [app.main.data.workspace :as udw] [app.main.data.workspace.changes :as dwc] [app.main.data.workspace.selection :as dws] + [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shapes :as dwsh] [app.main.store :as st] - [app.plugins.utils :as utils :refer [get-data get-data-fn]] + [app.plugins.grid :as grid] + [app.plugins.utils :as utils :refer [get-data get-data-fn get-state]] [app.util.object :as obj])) (declare data->shape-proxy) @@ -40,23 +41,8 @@ (let [page-id (:current-page-id @st/state)] (dm/get-in @st/state [:workspace-data :pages-index page-id :objects shape-id]))) -(defn- get-state - ([self attr] - (let [id (get-data self :id) - page-id (d/nilv (get-data self :page-id) (:current-page-id @st/state))] - (dm/get-in @st/state [:workspace-data :pages-index page-id :objects id attr]))) - ([self attr mapfn] - (-> (get-state self attr) - (mapfn)))) - -(deftype ShapeProxy [^:mutable #_:clj-kondo/ignore _data] +(deftype ShapeProxy [#_:clj-kondo/ignore _data] Object - (getChildren - [self] - (apply array (->> (get-state self :shapes) - (map locate-shape) - (map data->shape-proxy)))) - (resize [self width height] (let [id (get-data self :id)] @@ -76,6 +62,13 @@ (let [id (get-data self :id)] (st/emit! (dwsh/delete-shapes #{id})))) + ;; Only for frames + groups + booleans + (getChildren + [self] + (apply array (->> (get-state self :shapes) + (map locate-shape) + (map data->shape-proxy)))) + (appendChild [self child] (let [parent-id (get-data self :id) child-id (uuid/uuid (obj/get child "id"))] @@ -84,7 +77,16 @@ (insertChild [self index child] (let [parent-id (get-data self :id) child-id (uuid/uuid (obj/get child "id"))] - (st/emit! (udw/relocate-shapes #{child-id} parent-id index))))) + (st/emit! (udw/relocate-shapes #{child-id} parent-id index)))) + + ;; Only for frames + (addFlexLayout [self] + (let [id (get-data self :id)] + (st/emit! (dwsl/create-layout-from-id id :flex :from-frame? true :calculate-params? false)))) + + (addGridLayout [self] + (let [id (get-data self :id)] + (st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false))))) (crc/define-properties! ShapeProxy @@ -206,6 +208,32 @@ {:name "children" :get #(.getChildren ^js %)})) + (cond-> (not (or (cfh/frame-shape? data) (cfh/group-shape? data) (cfh/svg-raw-shape? data) (cfh/bool-shape? data))) + (-> (obj/unset! "appendChild") + (obj/unset! "insertChild") + (obj/unset! "getChildren"))) + + (cond-> (cfh/frame-shape? data) + (-> (crc/add-properties! + {:name "grid" + :get + (fn [self] + (let [layout (get-state self :layout)] + (when (= :grid layout) + (grid/grid-layout-proxy data))))}) + + #_(crc/add-properties! + {:name "flex" + :get + (fn [self] + (let [layout (get-state self :layout)] + (when (= :flex layout) + (flex-layout-proxy data))))}))) + + (cond-> (not (cfh/frame-shape? data)) + (-> (obj/unset! "addGridLayout") + (obj/unset! "addFlexLayout"))) + (cond-> (cfh/text-shape? data) (crc/add-properties! {:name "characters" @@ -213,4 +241,3 @@ :set (fn [self value] (let [id (get-data self :id)] (st/emit! (dwc/update-shapes [id] #(txt/change-text % value)))))})))) - diff --git a/frontend/src/app/plugins/utils.cljs b/frontend/src/app/plugins/utils.cljs index 23d8f276a..35022c36e 100644 --- a/frontend/src/app/plugins/utils.cljs +++ b/frontend/src/app/plugins/utils.cljs @@ -7,9 +7,11 @@ (ns app.plugins.utils "RPC for plugins runtime." (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.spec :as us] [app.common.uuid :as uuid] + [app.main.store :as st] [app.util.object :as obj] [cuerdas.core :as str] [promesa.core :as p])) @@ -32,22 +34,34 @@ (fn [self] (get-data self attr transform-fn)))) +(defn get-state + ([self attr] + (let [id (get-data self :id) + page-id (d/nilv (get-data self :page-id) (:current-page-id @st/state))] + (dm/get-in @st/state [:workspace-data :pages-index page-id :objects id attr]))) + ([self attr mapfn] + (-> (get-state self attr) + (mapfn)))) + (defn from-js "Converts the object back to js" - [obj] - (let [ret (js->clj obj {:keyword-fn (fn [k] (str/camel (name k)))})] - (reduce-kv - (fn [m k v] - (let [v (cond (map? v) - (from-js v) + ([obj] + (from-js obj identity)) + ([obj vfn] + (let [ret (js->clj obj {:keyword-fn (fn [k] (str/camel (name k)))})] + (reduce-kv + (fn [m k v] + (let [k (keyword (str/kebab k)) + v (cond (map? v) + (from-js v) - (and (string? v) (re-matches us/uuid-rx v)) - (uuid/uuid v) + (and (string? v) (re-matches us/uuid-rx v)) + (uuid/uuid v) - :else v)] - (assoc m (keyword (str/kebab k)) v))) - {} - ret))) + :else (vfn k v))] + (assoc m k v))) + {} + ret)))) (defn to-js "Converts to javascript an camelize the keys"