mirror of
https://github.com/penpot/penpot.git
synced 2025-12-12 06:24:17 +01:00
740 lines
28 KiB
Clojure
740 lines
28 KiB
Clojure
;; 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.main.data.workspace.shape-layout
|
|
(:require
|
|
[app.common.colors :as clr]
|
|
[app.common.data :as d]
|
|
[app.common.data.macros :as dm]
|
|
[app.common.files.changes-builder :as pcb]
|
|
[app.common.files.helpers :as cfh]
|
|
[app.common.files.shapes-helpers :as cfsh]
|
|
[app.common.geom.point :as gpt]
|
|
[app.common.geom.shapes.flex-layout :as flex]
|
|
[app.common.geom.shapes.grid-layout :as grid]
|
|
[app.common.logic.libraries :as cll]
|
|
[app.common.types.component :as ctc]
|
|
[app.common.types.modifiers :as ctm]
|
|
[app.common.types.shape.layout :as ctl]
|
|
[app.common.uuid :as uuid]
|
|
[app.main.data.changes :as dch]
|
|
[app.main.data.events :as ev]
|
|
[app.main.data.workspace.colors :as cl]
|
|
[app.main.data.workspace.grid-layout.editor :as dwge]
|
|
[app.main.data.workspace.modifiers :as dwm]
|
|
[app.main.data.workspace.selection :as dwse]
|
|
[app.main.data.workspace.shapes :as dwsh]
|
|
[app.main.data.workspace.state-helpers :as wsh]
|
|
[app.main.data.workspace.undo :as dwu]
|
|
[beicon.v2.core :as rx]
|
|
[potok.v2.core :as ptk]))
|
|
|
|
(def layout-keys
|
|
[:layout
|
|
:layout-flex-dir
|
|
:layout-gap-type
|
|
:layout-gap
|
|
:layout-align-items
|
|
:layout-justify-content
|
|
:layout-align-content
|
|
:layout-wrap-type
|
|
:layout-padding-type
|
|
:layout-padding
|
|
:layout-gap-type])
|
|
|
|
(def initial-flex-layout
|
|
{:layout :flex
|
|
:layout-flex-dir :row
|
|
:layout-gap-type :multiple
|
|
:layout-gap {:row-gap 0 :column-gap 0}
|
|
:layout-align-items :start
|
|
:layout-justify-content :start
|
|
:layout-align-content :stretch
|
|
:layout-wrap-type :nowrap
|
|
:layout-padding-type :simple
|
|
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
|
|
|
|
(def initial-grid-layout
|
|
{:layout :grid
|
|
:layout-grid-dir :row
|
|
:layout-gap-type :multiple
|
|
:layout-gap {:row-gap 0 :column-gap 0}
|
|
:layout-align-items :start
|
|
:layout-justify-items :start
|
|
:layout-align-content :stretch
|
|
:layout-justify-content :stretch
|
|
:layout-padding-type :simple
|
|
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}
|
|
:layout-grid-cells {}
|
|
:layout-grid-rows []
|
|
:layout-grid-columns []})
|
|
|
|
(defn get-layout-initializer
|
|
[type from-frame? calculate-params?]
|
|
(let [[initial-layout-data calculate-params]
|
|
(case type
|
|
:flex [initial-flex-layout flex/calculate-params]
|
|
:grid [initial-grid-layout grid/calculate-params])]
|
|
|
|
(fn [shape objects]
|
|
(let [shape
|
|
(-> shape
|
|
(merge initial-layout-data)
|
|
|
|
;; If the original shape is not a frame we set clip content and show-viewer to false
|
|
(cond-> (not from-frame?)
|
|
(assoc :show-content true :hide-in-viewer true)))
|
|
|
|
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))))))
|
|
|
|
;; Never call this directly but through the data-event `:layout/update`
|
|
;; Otherwise a lot of cycle dependencies could be generated
|
|
(defn- update-layout-positions
|
|
[{:keys [ids undo-group]}]
|
|
(ptk/reify ::update-layout-positions
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [objects (wsh/lookup-page-objects state)
|
|
ids (->> ids (filter #(contains? objects %)))]
|
|
(if (d/not-empty? ids)
|
|
(let [modif-tree (dwm/create-modif-tree ids (ctm/reflow-modifiers))]
|
|
(rx/of (dwm/apply-modifiers {:modifiers modif-tree
|
|
:stack-undo? true
|
|
:undo-group undo-group})))
|
|
(rx/empty))))))
|
|
|
|
(defn initialize
|
|
[]
|
|
(ptk/reify ::initialize
|
|
ptk/WatchEvent
|
|
(watch [_ _ stream]
|
|
(let [stopper (rx/filter (ptk/type? ::finalize) stream)]
|
|
(->> stream
|
|
(rx/filter (ptk/type? :layout/update))
|
|
(rx/map deref)
|
|
;; We buffer the updates to the layout so if there are many changes at the same time
|
|
;; they are process together. It will get a better performance.
|
|
(rx/buffer-time 100)
|
|
(rx/filter #(d/not-empty? %))
|
|
(rx/map
|
|
(fn [data]
|
|
(let [ids (reduce #(into %1 (:ids %2)) #{} data)]
|
|
(update-layout-positions {:ids ids}))))
|
|
(rx/take-until stopper))))))
|
|
|
|
(defn finalize
|
|
[]
|
|
(ptk/reify ::finalize))
|
|
|
|
(defn create-layout-from-id
|
|
[id type & {:keys [from-frame? calculate-params?] :or {from-frame? false calculate-params? true}}]
|
|
(dm/assert!
|
|
"expected uuid for `id`"
|
|
(uuid? id))
|
|
|
|
(ptk/reify ::create-layout-from-id
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [objects (wsh/lookup-page-objects state)
|
|
parent (get objects id)
|
|
undo-id (js/Symbol)
|
|
layout-initializer (get-layout-initializer type from-frame? calculate-params?)]
|
|
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes [id] layout-initializer {:with-objects? true})
|
|
(dwsh/update-shapes (dm/get-prop parent :shapes) #(dissoc % :constraints-h :constraints-v))
|
|
(ptk/data-event :layout/update {:ids [id]})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn create-layout-from-selection
|
|
[type]
|
|
(ptk/reify ::create-layout-from-selection
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
|
|
(let [page-id (:current-page-id state)
|
|
objects (wsh/lookup-page-objects state page-id)
|
|
selected (wsh/lookup-selected state)
|
|
selected-shapes (map (d/getf objects) selected)
|
|
single? (= (count selected-shapes) 1)
|
|
has-group? (->> selected-shapes (d/seek cfh/group-shape?))
|
|
is-group? (and single? has-group?)
|
|
has-mask? (->> selected-shapes (d/seek cfh/mask-shape?))
|
|
is-mask? (and single? has-mask?)
|
|
has-component? (some true? (map ctc/instance-root? selected-shapes))
|
|
is-component? (and single? has-component?)
|
|
|
|
new-shape-id (uuid/next)
|
|
undo-id (js/Symbol)]
|
|
|
|
(rx/concat
|
|
(rx/of (dwu/start-undo-transaction undo-id))
|
|
(if (and is-group? (not is-component?) (not is-mask?))
|
|
;; Create layout from a group:
|
|
;; When creating a layout from a group we remove the group and create the layout with its children
|
|
(let [parent-id (:parent-id (first selected-shapes))
|
|
shapes-ids (:shapes (first selected-shapes))
|
|
ordered-ids (into (d/ordered-set) shapes-ids)
|
|
group-index (cfh/get-index-replacement selected objects)]
|
|
(rx/of
|
|
(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)
|
|
(dwsh/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto))
|
|
(dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix))
|
|
(dwsh/delete-shapes page-id selected)
|
|
(ptk/data-event :layout/update {:ids [new-shape-id]})
|
|
(dwu/commit-undo-transaction undo-id)))
|
|
|
|
;; Create Layout from selection
|
|
(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)
|
|
(dwsh/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto))
|
|
(dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix))))
|
|
|
|
(rx/of (ptk/data-event :layout/update {:ids [new-shape-id]})
|
|
(dwu/commit-undo-transaction undo-id)))))))
|
|
|
|
(defn remove-layout
|
|
[ids]
|
|
(ptk/reify ::remove-shape-layout
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of
|
|
(dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes ids #(apply dissoc % layout-keys))
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn create-layout
|
|
[type]
|
|
(ptk/reify ::create-shape-layout
|
|
ev/Event
|
|
(-data [_]
|
|
{:layout (d/name type)})
|
|
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [page-id (:current-page-id state)
|
|
objects (wsh/lookup-page-objects state page-id)
|
|
selected (wsh/lookup-selected state)
|
|
selected-shapes (map (d/getf objects) selected)
|
|
single? (= (count selected-shapes) 1)
|
|
is-frame? (= :frame (:type (first selected-shapes)))
|
|
undo-id (js/Symbol)]
|
|
|
|
(rx/of
|
|
(dwu/start-undo-transaction undo-id)
|
|
(if (and single? is-frame?)
|
|
(create-layout-from-id (first selected) type :from-frame? true)
|
|
(create-layout-from-selection type))
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn toggle-layout
|
|
[type]
|
|
(ptk/reify ::toggle-shape-layout
|
|
ptk/WatchEvent
|
|
(watch [it state _]
|
|
(let [objects (wsh/lookup-page-objects state)
|
|
selected (wsh/lookup-selected state)
|
|
selected-shapes (map (d/getf objects) selected)
|
|
single? (= (count selected-shapes) 1)
|
|
has-layout? (and single?
|
|
(ctl/any-layout? objects (:id (first selected-shapes))))]
|
|
|
|
(when (not= 0 (count selected))
|
|
(let [event (if has-layout?
|
|
(remove-layout selected)
|
|
(create-layout type))]
|
|
(rx/of (with-meta event (meta it)))))))))
|
|
|
|
(defn update-layout
|
|
[ids changes & options]
|
|
(ptk/reify ::update-layout
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes ids (d/patch-object changes) options)
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn add-layout-track
|
|
([ids type value]
|
|
(add-layout-track ids type value nil))
|
|
([ids type value index]
|
|
(assert (#{:row :column} type))
|
|
(ptk/reify ::add-layout-track
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes
|
|
ids
|
|
(fn [shape]
|
|
(case type
|
|
:row (ctl/add-grid-row shape value index)
|
|
:column (ctl/add-grid-column shape value index))))
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id)))))))
|
|
|
|
(defn remove-layout-track
|
|
[ids type index & {:keys [with-shapes?] :or {with-shapes? false}}]
|
|
(assert (#{:row :column} type))
|
|
|
|
(ptk/reify ::remove-layout-track
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [undo-id (js/Symbol)]
|
|
(let [objects (wsh/lookup-page-objects state)
|
|
|
|
shapes-to-delete
|
|
(when with-shapes?
|
|
(->> ids
|
|
(mapcat
|
|
(fn [id]
|
|
(let [shape (get objects id)]
|
|
(if (= type :column)
|
|
(ctl/shapes-by-column shape index)
|
|
(ctl/shapes-by-row shape index)))))
|
|
(into #{})))]
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(if shapes-to-delete
|
|
(dwsh/delete-shapes shapes-to-delete)
|
|
(rx/empty))
|
|
(dwsh/update-shapes
|
|
ids
|
|
(fn [shape objects]
|
|
(case type
|
|
:row (ctl/remove-grid-row shape index objects)
|
|
:column (ctl/remove-grid-column shape index objects)))
|
|
{:with-objects? true})
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id)))))))
|
|
|
|
(defn duplicate-layout-track
|
|
[ids type index]
|
|
(assert (#{:row :column} type))
|
|
|
|
(ptk/reify ::duplicate-layout-track
|
|
ptk/WatchEvent
|
|
(watch [it state _]
|
|
(let [file-id (:current-file-id state)
|
|
page (wsh/lookup-page state)
|
|
objects (:objects page)
|
|
libraries (wsh/get-libraries state)
|
|
library-data (wsh/get-file state file-id)
|
|
shape-id (first ids)
|
|
base-shape (get objects shape-id)
|
|
|
|
shapes-by-track
|
|
(if (= type :column)
|
|
(ctl/shapes-by-column base-shape index false)
|
|
(ctl/shapes-by-row base-shape index false))
|
|
|
|
;; Change to set in order to use auxiliary functions
|
|
selected (set shapes-by-track)
|
|
|
|
changes
|
|
(-> (pcb/empty-changes it)
|
|
(cll/generate-duplicate-changes objects page selected (gpt/point 0 0) libraries library-data file-id)
|
|
(cll/generate-duplicate-changes-update-indices objects selected))
|
|
|
|
;; Creates a map with shape-id => duplicated-shape-id
|
|
ids-map
|
|
(->> changes
|
|
:redo-changes
|
|
(filter #(= (:type %) :add-obj))
|
|
(filter #(selected (:old-id %)))
|
|
(map #(vector (:old-id %) (get-in % [:obj :id])))
|
|
(into {}))
|
|
|
|
changes
|
|
(-> changes
|
|
(pcb/update-shapes
|
|
ids
|
|
(fn [shape objects]
|
|
;; The duplication could have altered the grid so we restore the values, we'll calculate the good ones now
|
|
(let [shape (merge shape (select-keys base-shape [:layout-grid-cells :layout-grid-columns :layout-grid-rows]))]
|
|
(case type
|
|
:row (ctl/duplicate-row shape objects index ids-map)
|
|
:column (ctl/duplicate-column shape objects index ids-map))))
|
|
{:with-objects? true}))
|
|
|
|
undo-id (js/Symbol)]
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(dch/commit-changes changes)
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn reorder-layout-track
|
|
[ids type from-index to-index move-content?]
|
|
(assert (#{:row :column} type))
|
|
|
|
(ptk/reify ::reorder-layout-track
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes
|
|
ids
|
|
(fn [shape]
|
|
(case type
|
|
:row (ctl/reorder-grid-row shape from-index to-index move-content?)
|
|
:column (ctl/reorder-grid-column shape from-index to-index move-content?))))
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn hover-layout-track
|
|
[ids type index hover?]
|
|
(assert (#{:row :column} type))
|
|
|
|
(ptk/reify ::hover-layout-track
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(let [objects (wsh/lookup-page-objects state)
|
|
shape (get objects (first ids))
|
|
|
|
highlighted
|
|
(when hover?
|
|
(->> (if (= type :row)
|
|
(ctl/shapes-by-row shape index)
|
|
(ctl/shapes-by-column shape index))
|
|
(set)))]
|
|
(cond-> state
|
|
hover?
|
|
(update-in [:workspace-grid-edition (first ids) :hover-track] (fnil conj #{}) [type index])
|
|
|
|
(not hover?)
|
|
(update-in [:workspace-grid-edition (first ids) :hover-track] (fnil disj #{}) [type index])
|
|
|
|
:always
|
|
(assoc-in [:workspace-local :highlighted] highlighted))))))
|
|
|
|
(defn change-layout-track
|
|
[ids type index props]
|
|
(assert (#{:row :column} type))
|
|
(ptk/reify ::change-layout-track
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)
|
|
property (case type
|
|
:row :layout-grid-rows
|
|
:column :layout-grid-columns)]
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes
|
|
ids
|
|
(fn [shape]
|
|
(-> shape
|
|
(update-in [property index] merge props))))
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn fix-child-sizing
|
|
[objects parent-changes shape]
|
|
|
|
(let [parent (-> (cfh/get-parent objects (:id shape))
|
|
(d/deep-merge parent-changes))
|
|
|
|
auto-width? (ctl/auto-width? parent)
|
|
auto-height? (ctl/auto-height? parent)
|
|
col? (ctl/col? parent)
|
|
row? (ctl/row? parent)
|
|
|
|
all-children (->> parent
|
|
:shapes
|
|
(map (d/getf objects))
|
|
(remove ctl/position-absolute?))]
|
|
|
|
(cond-> shape
|
|
;; If the parent is hug width and the direction column
|
|
;; change to fixed when ALL children are fill
|
|
(and col? auto-width? (every? ctl/fill-width? all-children))
|
|
(assoc :layout-item-h-sizing :fix)
|
|
|
|
;; If the parent is hug height and the direction is column
|
|
;; change to fixed when ANY children is fill
|
|
(and col? auto-height? (ctl/fill-height? shape))
|
|
(assoc :layout-item-v-sizing :fix)
|
|
|
|
;; If the parent is hug width and the direction row
|
|
;; change to fixed when ANY children is fill
|
|
(and row? auto-width? (ctl/fill-width? shape))
|
|
(assoc :layout-item-h-sizing :fix)
|
|
|
|
;; If the parent is hug height and the direction row
|
|
;; change to fixed when ALL children are fill
|
|
(and row? auto-height? (every? ctl/fill-height? all-children))
|
|
(assoc :layout-item-v-sizing :fix))))
|
|
|
|
(defn fix-parent-sizing
|
|
[parent objects ids-set changes]
|
|
|
|
(let [auto-width? (ctl/auto-width? parent)
|
|
auto-height? (ctl/auto-height? parent)
|
|
col? (ctl/col? parent)
|
|
row? (ctl/row? parent)
|
|
|
|
all-children
|
|
(->> parent :shapes
|
|
(map (d/getf objects))
|
|
(map (fn [shape]
|
|
(if (contains? ids-set (:id shape))
|
|
(d/deep-merge shape changes)
|
|
shape))))]
|
|
|
|
(cond-> parent
|
|
;; Col layout and parent is hug-width if all children are fill-width
|
|
;; change parent to fixed
|
|
(and col? auto-width? (every? ctl/fill-width? all-children))
|
|
(assoc :layout-item-h-sizing :fix)
|
|
|
|
;; Col layout and parent is hug-height if any children is fill-height
|
|
;; change parent to fixed
|
|
(and col? auto-height? (some ctl/fill-height? all-children))
|
|
(assoc :layout-item-v-sizing :fix)
|
|
|
|
;; Row layout and parent is hug-width if any children is fill-width
|
|
;; change parent to fixed
|
|
(and row? auto-width? (some ctl/fill-width? all-children))
|
|
(assoc :layout-item-h-sizing :fix)
|
|
|
|
;; Row layout and parent is hug-height if all children are fill-height
|
|
;; change parent to fixed
|
|
(and row? auto-height? (every? ctl/fill-height? all-children))
|
|
(assoc :layout-item-v-sizing :fix))))
|
|
|
|
(defn update-layout-child
|
|
[ids changes & options]
|
|
(ptk/reify ::update-layout-child
|
|
ptk/WatchEvent
|
|
(watch [_ state _]
|
|
(let [objects (wsh/lookup-page-objects state)
|
|
children-ids (->> ids (mapcat #(cfh/get-children-ids objects %)))
|
|
parent-ids (->> ids (map #(cfh/get-parent-id objects %)))
|
|
undo-id (js/Symbol)]
|
|
(rx/of (dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes ids (d/patch-object changes) options)
|
|
(dwsh/update-shapes children-ids (partial fix-child-sizing objects changes) options)
|
|
(dwsh/update-shapes
|
|
parent-ids
|
|
(fn [parent objects]
|
|
(-> parent
|
|
(fix-parent-sizing objects (set ids) changes)
|
|
(cond-> (ctl/grid-layout? parent)
|
|
(ctl/assign-cells objects))))
|
|
(merge options {:with-objects? true}))
|
|
(ptk/data-event :layout/update {:ids ids})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn update-grid-cells
|
|
[layout-id ids props]
|
|
(ptk/reify ::update-grid-cells
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of
|
|
(dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes
|
|
[layout-id]
|
|
(fn [shape]
|
|
(->> ids
|
|
(reduce
|
|
(fn [shape cell-id]
|
|
(d/update-in-when
|
|
shape
|
|
[:layout-grid-cells cell-id]
|
|
d/patch-object props))
|
|
shape))))
|
|
(ptk/data-event :layout/update {:ids [layout-id]})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn change-cells-mode
|
|
[layout-id ids mode]
|
|
|
|
(ptk/reify ::change-cells-mode
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of
|
|
(dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes
|
|
[layout-id]
|
|
(fn [shape objects]
|
|
(case mode
|
|
:auto
|
|
;; change the manual cells and move to auto
|
|
(->> ids
|
|
(reduce
|
|
(fn [shape cell-id]
|
|
(let [cell (get-in shape [:layout-grid-cells cell-id])]
|
|
(cond-> shape
|
|
(or (contains? #{:area :manual} (:position cell))
|
|
(> (:row-span cell) 1)
|
|
(> (:column-span cell) 1))
|
|
(-> (d/update-in-when [:layout-grid-cells cell-id] assoc :shapes [] :position :auto)
|
|
(d/update-in-when [:layout-grid-cells cell-id] dissoc :area-name)
|
|
(ctl/resize-cell-area (:row cell) (:column cell) (:row cell) (:column cell) 1 1)
|
|
(ctl/assign-cells objects)))))
|
|
shape))
|
|
|
|
:manual
|
|
(->> ids
|
|
(reduce
|
|
(fn [shape cell-id]
|
|
(let [cell (get-in shape [:layout-grid-cells cell-id])]
|
|
(cond-> shape
|
|
(contains? #{:area :auto} (:position cell))
|
|
(-> (d/assoc-in-when [:layout-grid-cells cell-id :position] :manual)
|
|
(d/update-in-when [:layout-grid-cells cell-id] dissoc :area-name)
|
|
(ctl/assign-cells objects)))))
|
|
shape))
|
|
|
|
:area
|
|
;; Create area with the selected cells
|
|
(let [{:keys [first-row first-column last-row last-column]}
|
|
(ctl/cells-coordinates (->> ids (map #(get-in shape [:layout-grid-cells %]))))
|
|
|
|
target-cell
|
|
(ctl/get-cell-by-position shape first-row first-column)
|
|
|
|
shape
|
|
(-> shape
|
|
(ctl/resize-cell-area
|
|
(:row target-cell) (:column target-cell)
|
|
first-row
|
|
first-column
|
|
(inc (- last-row first-row))
|
|
(inc (- last-column first-column)))
|
|
(ctl/assign-cells objects))]
|
|
|
|
(-> shape
|
|
(d/update-in-when [:layout-grid-cells (:id target-cell)] assoc :position :area)))))
|
|
{:with-objects? true})
|
|
(dwge/clean-selection layout-id)
|
|
(ptk/data-event :layout/update {:ids [layout-id]})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn merge-cells
|
|
[layout-id ids]
|
|
|
|
(ptk/reify ::merge-cells
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of
|
|
(dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes
|
|
[layout-id]
|
|
(fn [shape objects]
|
|
(let [cells (->> ids (map #(get-in shape [:layout-grid-cells %])))
|
|
|
|
{:keys [first-row first-column last-row last-column]}
|
|
(ctl/cells-coordinates cells)
|
|
|
|
target-cell
|
|
(ctl/get-cell-by-position shape first-row first-column)]
|
|
(-> shape
|
|
(ctl/resize-cell-area
|
|
(:row target-cell) (:column target-cell)
|
|
first-row
|
|
first-column
|
|
(inc (- last-row first-row))
|
|
(inc (- last-column first-column)))
|
|
(ctl/assign-cells objects))))
|
|
{:with-objects? true})
|
|
(dwge/clean-selection layout-id)
|
|
(ptk/data-event :layout/update {:ids [layout-id]})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
(defn update-grid-cell-position
|
|
[layout-id cell-id props]
|
|
|
|
(ptk/reify ::update-grid-cell-position
|
|
ptk/WatchEvent
|
|
(watch [_ _ _]
|
|
(let [undo-id (js/Symbol)]
|
|
(rx/of
|
|
(dwu/start-undo-transaction undo-id)
|
|
(dwsh/update-shapes
|
|
[layout-id]
|
|
(fn [shape objects]
|
|
(let [prev-data (-> (dm/get-in shape [:layout-grid-cells cell-id])
|
|
(select-keys [:row :column :row-span :column-span]))
|
|
|
|
new-data (merge prev-data props)]
|
|
(-> shape
|
|
(ctl/resize-cell-area (:row prev-data) (:column prev-data)
|
|
(:row new-data) (:column new-data)
|
|
(:row-span new-data) (:column-span new-data))
|
|
(ctl/assign-cells objects))))
|
|
{:with-objects? true})
|
|
(ptk/data-event :layout/update {:ids [layout-id]})
|
|
(dwu/commit-undo-transaction undo-id))))))
|
|
|
|
|
|
(defn create-cell-board
|
|
[layout-id cell-ids]
|
|
(ptk/reify ::create-cell-board
|
|
ptk/WatchEvent
|
|
(watch [it state _]
|
|
(let [page-id (:current-page-id state)
|
|
objects (wsh/lookup-page-objects state)
|
|
frame-id (uuid/next)
|
|
|
|
undo-id (js/Symbol)
|
|
|
|
shape (get objects layout-id)
|
|
cells (->> cell-ids (map #(get-in shape [:layout-grid-cells %])))
|
|
selected (into #{} (mapcat :shapes) cells)
|
|
|
|
{:keys [first-row first-column last-row last-column]} (ctl/cells-coordinates cells)
|
|
|
|
target-cell (ctl/get-cell-by-position shape first-row first-column)
|
|
|
|
[_ changes]
|
|
(-> (pcb/empty-changes it page-id)
|
|
(pcb/with-objects objects)
|
|
(cond-> (d/not-empty? selected)
|
|
(cfsh/prepare-create-artboard-from-selection
|
|
frame-id layout-id objects selected 0 nil true (:id target-cell)))
|
|
|
|
(cond-> (empty? (seq selected))
|
|
(cfsh/prepare-create-empty-artboard
|
|
frame-id layout-id objects 0 nil true (:id target-cell))))
|
|
|
|
changes
|
|
(-> changes
|
|
(pcb/update-shapes
|
|
[frame-id]
|
|
(fn [shape]
|
|
(-> shape
|
|
(assoc :layout-item-h-sizing :fill)
|
|
(assoc :layout-item-v-sizing :fill))))
|
|
(pcb/update-shapes
|
|
[layout-id]
|
|
(fn [shape]
|
|
(let [new-row-span (inc (- last-row first-row))
|
|
new-col-span (inc (- last-column first-column))]
|
|
(-> shape
|
|
(ctl/resize-cell-area
|
|
(:row target-cell) (:column target-cell)
|
|
first-row first-column new-row-span new-col-span))))))]
|
|
|
|
(rx/of
|
|
(dwu/start-undo-transaction undo-id)
|
|
(dch/commit-changes changes)
|
|
(ptk/data-event :layout/update {:ids [layout-id]})
|
|
(dwu/commit-undo-transaction undo-id))))))
|