Alonso Torres 9f05ba2fdf
Add plugins and mcp event data (#9228)
*  Add plugins and mcp event data

* ♻️ Changed data-event ::ev/event to ev/event
2026-05-11 08:36:53 +02:00

759 lines
32 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.variants
(:require
[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.variant :as cfv]
[app.common.geom.point :as gpt]
[app.common.logic.variant-properties :as clvp]
[app.common.logic.variants :as clv]
[app.common.path-names :as cpn]
[app.common.types.color :as clr]
[app.common.types.component :as ctc]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf]
[app.common.types.shape.layout :as ctsl]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]
[app.main.data.changes :as dch]
[app.main.data.common :as dcm]
[app.main.data.event :as ev]
[app.main.data.helpers :as dsh]
[app.main.data.workspace.colors :as cl]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.pages :as dwpg]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu]
[app.util.dom :as dom]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn update-properties-names-and-values
"Compares the previous properties with the updated ones and executes the correspondent action
for each one depending on if it needs to be removed, updated or added"
[component-id variant-id previous-properties updated-properties]
(ptk/reify ::update-properties-names-and-values
ptk/UpdateEvent
(update [_ state]
(update state :workspace-local dissoc :shape-for-rename))
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
updated-properties (ctv/update-number-in-repeated-prop-names updated-properties)
properties-to-remove (ctv/find-properties-to-remove previous-properties updated-properties)
properties-to-add (ctv/find-properties-to-add previous-properties updated-properties)
properties-to-update (ctv/find-properties-to-update previous-properties updated-properties)
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(pcb/with-library-data data))
changes (reduce
(fn [changes {:keys [name]}]
(-> changes
(clvp/generate-update-property-value component-id (ctv/find-index-for-property-name previous-properties name) "")))
changes
properties-to-remove)
changes (reduce
(fn [changes {:keys [name value]}]
(-> changes
(clvp/generate-update-property-value component-id (ctv/find-index-for-property-name previous-properties name) value)))
changes
properties-to-update)
changes (reduce
(fn [changes [idx {:keys [name value]}]]
(-> changes
(clvp/generate-add-new-property variant-id {:property-name name})
(clvp/generate-update-property-value component-id (+ idx (count previous-properties)) value)))
changes
(map-indexed vector properties-to-add))
undo-id (js/Symbol)]
(rx/of
(when (or (seq properties-to-remove) (seq properties-to-update))
(ev/event {::ev/name "variant-edit-property-value" ::ev/origin "workspace:rename-in-layers"}))
(when (seq properties-to-add)
(ev/event {::ev/name "variant-add-property" ::ev/origin "workspace:rename-in-layers"}))
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn update-property-name
"Update the variant property name on the position pos
in all the components with this variant-id and remove the focus"
[variant-id pos new-name {:keys [trigger]}]
(ptk/reify ::update-property-name
ptk/UpdateEvent
(update [_ state]
(let [file-id (:current-file-id state)
data (dsh/lookup-file-data state)
objects (dsh/lookup-page-objects state)
related-components (cfv/find-variant-components data objects variant-id)]
(reduce
(fn [s related-component]
(update-in s
[:files file-id :data :components (:id related-component) :variant-properties]
(fn [props] (mapv #(with-meta % nil) props))))
state
related-components)))
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
related-components (cfv/find-variant-components data objects variant-id)
props (-> related-components last :variant-properties)
valid-pos? (> (count props) pos)
prop-name (when valid-pos? (-> props (nth pos) :name))
changes (when valid-pos?
(-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(pcb/with-library-data data)
(clvp/generate-update-property-name variant-id pos new-name)))
undo-id (js/Symbol)]
(when (and valid-pos? (not= prop-name new-name))
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id)
(ev/event {::ev/name "variant-edit-property-name" ::ev/origin trigger})))))))
(defn update-property-value
"Updates the variant property value on the position pos in a component"
[component-id pos value]
(ptk/reify ::update-property-value
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clvp/generate-update-property-value component-id pos value))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn update-error
"Sets or unsets an error for a component"
([component-id]
(update-error component-id nil))
([component-id value]
(ptk/reify ::update-error
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clvp/generate-set-variant-error component-id value))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id)))))))
(defn remove-property
"Remove the variant property on the position pos
in all the components with this variant-id"
[variant-id pos]
(ptk/reify ::remove-property
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clvp/generate-remove-property variant-id pos))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn remove-empty-properties
"Remove every empty property for all components when their respective values are empty
for all of them"
[variant-id]
(ptk/reify ::remove-empty-properties
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
variant-components (cfv/find-variant-components data objects variant-id)
properties-empty-pos (->> variant-components
(mapcat :variant-properties)
(group-by :name)
(map-indexed
(fn [i [_ v]]
[i (->> v
(map :value)
(remove empty?)
empty?)]))
(reverse))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects))
changes (reduce
(fn [changes [pos property-empty?]]
(if property-empty?
(-> changes
(clvp/generate-remove-property variant-id pos))
changes))
changes
properties-empty-pos)
undo-id (js/Symbol)]
(when (seq (:redo-changes changes))
(rx/of
(ev/event {::ev/name "variant-remove-property" ::ev/origin "workspace:rename-in-layers"})
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id)))))))
(defn add-new-property
"Add a new variant property to all the components with this variant-id"
[variant-id & [options]]
(ptk/reify ::add-new-property
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clvp/generate-add-new-property variant-id options))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn reorder-variant-poperties
"Reorder properties by moving a property from some position to some space between positions"
[variant-id from-pos to-space-between-pos]
(ptk/reify ::reorder-variant-properties
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(clvp/generate-reorder-variant-poperties variant-id from-pos to-space-between-pos))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn- set-variant-id
"Sets the variant-id on a component"
[component-id variant-id]
(ptk/reify ::set-variant-id
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/update-component component-id #(assoc % :variant-id variant-id)))
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn- focus-property
[variant-id]
(ptk/reify ::focus-property
ptk/EffectEvent
(effect [_ _ _]
(dom/focus! (dom/get-element (str "variant-prop-" variant-id "-0"))))))
(defn- resposition-and-resize-variant
"Resize the variant container, and move the shape (that is a variant) to the right"
[shape-id]
(ptk/reify ::resposition-and-resize-variant
ptk/WatchEvent
(watch [_ state _]
(let [page-id (:current-page-id state)
objects (dsh/lookup-page-objects state page-id)
shape (get objects shape-id)
container (get objects (:parent-id shape))
width (+ (:width container) (:width shape) 20) ;; 20 is the default gap for variants
x (- width (+ (:width shape) 30))] ;; 30 is the default margin for variants
(rx/of
(dwt/update-dimensions [(:parent-id shape)] :width width)
(dwt/update-position shape-id
{:x x}
{:absolute? false}))))))
(defn add-new-variant
"Create a new variant and add it to the variant-container"
([shape-id]
(add-new-variant shape-id false))
([shape-id multiselect?]
(ptk/reify ::add-new-variant
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
shape (get objects shape-id)
shape (if (ctc/is-variant-container? shape)
(get objects (last (:shapes shape)))
shape)
component-id (:component-id shape)
component (ctkl/get-component data component-id)
container-id (:parent-id shape)
variant-container (get objects container-id)
has-layout? (ctsl/any-layout? variant-container)
new-component-id (uuid/next)
new-shape-id (uuid/next)
prop-num (dec (count (:variant-properties component)))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(pcb/with-page-id page-id)
(clv/generate-add-new-variant shape (:variant-id component) new-component-id new-shape-id prop-num))
undo-id (js/Symbol)]
(rx/concat
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(when-not has-layout?
(resposition-and-resize-variant new-shape-id))
(dwu/commit-undo-transaction undo-id)
(ptk/data-event :layout/update {:ids [(:parent-id shape)]})
(if multiselect?
(dws/shift-select-shapes new-shape-id)
(dws/select-shape new-shape-id)))
(->> (rx/of (focus-property (:id variant-container)))
(rx/delay 250))))))))
(defn transform-in-variant
"Given the id of a main shape of a component, creates a variant structure for
that component"
([main-instance-id]
(transform-in-variant main-instance-id nil nil [] false true true))
([main-instance-id variant-id delta prefix add-wrapper? duplicate? flex?]
(ptk/reify ::transform-in-variant
ptk/WatchEvent
(watch [_ state _]
(let [variant-id (or variant-id (uuid/next))
variant-vec [variant-id]
file-id (:current-file-id state)
page-id (:current-page-id state)
objects (dsh/lookup-page-objects state file-id page-id)
main (get objects main-instance-id)
parent (get objects (:parent-id main))
component-id (:component-id main)
name (if add-wrapper?
(str "Component/" (:name main))
(:name main))
;; If there is a prefix, set is as first item of path
cpath (-> name
cpn/split-path
(cond->
(seq prefix)
(->> (drop (count prefix))
(cons (cpn/join-path prefix))
vec)))
name (first cpath)
num-props (max 1 (dec (count cpath)))
base-props {:is-variant-container true
:name name
:r1 20
:r2 20
:r3 20
:r4 20
:layout-item-absolute true}
flex-props {:layout-item-h-sizing :auto
:layout-item-v-sizing :auto
:layout-padding {:p1 30 :p2 30 :p3 30 :p4 30}
:layout-gap {:row-gap 0 :column-gap 20}}
cont-props (if flex?
(into base-props flex-props)
base-props)
main-props {:name name
:variant-id variant-id}
stroke-props {:stroke-alignment :inner
:stroke-style :solid
:stroke-color "#bb97d8" ;; todo use color var?
:stroke-opacity 1
:stroke-width 2}
;; Move the position of the variant container so the main shape doesn't
;; change its position
delta (or delta
(if (ctsl/any-layout? parent)
(gpt/point 0 0)
(gpt/point -30 -30)))
undo-id (js/Symbol)]
;;TODO Refactor all called methods in order to be able to
;;generate changes instead of call the events
(rx/concat
(rx/of
(dwu/start-undo-transaction undo-id)
(when (not= name (:name main))
(dwl/rename-component component-id name))
;; Create variant container
(dwsh/create-artboard-from-shapes [main-instance-id] variant-id nil nil nil delta flex?)
(cl/remove-all-fills variant-vec {:color clr/black :opacity 1})
(when flex? (dwsl/create-layout-from-id variant-id :flex))
(dwsh/update-shapes variant-vec #(merge % cont-props))
(dwsh/update-shapes [main-instance-id] #(merge % main-props))
(cl/add-stroke variant-vec stroke-props)
(set-variant-id component-id variant-id))
;; Add the necessary number of new properties, with default values
(rx/from
(repeatedly num-props
#(add-new-property variant-id {:fill-values? true})))
;; When the component has path, set the path items as properties values
(when (> (count cpath) 1)
(rx/from
(map
#(update-property-value component-id % (nth cpath (inc %)))
(range num-props))))
(rx/of
(when duplicate? (add-new-variant main-instance-id))
(dwsh/update-shapes variant-vec #(dissoc % :layout-item-absolute))
(dwu/commit-undo-transaction undo-id)
(when flex?
(ptk/data-event :layout/update {:ids [variant-id]})))))))))
(defn add-component-or-variant
"Manage the shared shortcut, and do the pertinent action"
[]
(ptk/reify ::add-component-or-variant
ptk/WatchEvent
(watch [_ state _]
(let [objects (dsh/lookup-page-objects state)
selected-ids (dsh/lookup-selected state)
selected-shapes (map (d/getf objects) selected-ids)
single? (= 1 (count selected-ids))
first-shape (first selected-shapes)
transform-in-variant? (and single?
(not (ctc/is-variant? first-shape))
(ctc/main-instance? first-shape))
add-new-variant? (every? ctc/is-variant? selected-shapes)
undo-id (js/Symbol)]
(cond
transform-in-variant?
(rx/of
(ev/event {::ev/name "transform-in-variant" ::ev/origin "workspace:shortcut"})
(transform-in-variant (:id first-shape)))
add-new-variant?
(rx/concat
(rx/of
(ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:shortcut-create-component"})
(dwu/start-undo-transaction undo-id))
(rx/from (map add-new-variant selected-ids))
(rx/of (dwu/commit-undo-transaction undo-id)))
:else
(rx/of (dwl/add-component)))))))
(defn duplicate-or-add-variant
"Manage the shared shortcut, and do the pertinent action"
[]
(ptk/reify ::duplicate-or-add-variant
ptk/WatchEvent
(watch [_ state _]
(let [objects (dsh/lookup-page-objects state)
selected-ids (dsh/lookup-selected state)
selected-shapes (map (d/getf objects) selected-ids)
add-new-variant? (every? ctc/is-variant? selected-shapes)
undo-id (js/Symbol)]
(if add-new-variant?
(rx/concat
(rx/of
(ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:shortcut-duplicate"})
(dwu/start-undo-transaction undo-id)
(add-new-variant (first selected-ids) false))
(rx/from (map #(add-new-variant % true) (rest selected-ids)))
(rx/of (dwu/commit-undo-transaction undo-id)))
(rx/of (dws/duplicate-selected true)))))))
(defn rename-variant
"Rename the variant container and all components belonging to this variant"
[variant-id name]
(ptk/reify ::rename-variant
ptk/WatchEvent
(watch [_ state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
variant-components (cfv/find-variant-components data objects variant-id)
clean-name (cpn/clean-path name)
undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id)
(dwsh/update-shapes [variant-id] #(assoc % :name clean-name)))
(rx/from (map
#(dwl/rename-component-and-main-instance (:id %) clean-name)
variant-components))
(rx/of (dwu/commit-undo-transaction undo-id)))))))
(defn rename-comp-or-variant-and-main
"If the component is in a variant, rename the variant.
If it is not, rename the component and its main"
[component-id name]
(ptk/reify ::rename-comp-or-variant-and-main
ptk/WatchEvent
(watch [_ state _]
(let [data (dsh/lookup-file-data state)
component (ctkl/get-component data component-id)]
(if (ctc/is-variant? component)
(rx/of (rename-variant (:variant-id component) name))
(rx/of (dwl/rename-component-and-main-instance component-id name)))))))
(defn- bounding-rect
"Receives a list of frames (with X, y, width and height) and
calculates a rect that contains them all"
[frames]
(let [xs (map :x frames)
ys (map :y frames)
x2s (map #(+ (:x %) (:width %)) frames)
y2s (map #(+ (:y %) (:height %)) frames)
min-x (apply min xs)
min-y (apply min ys)
max-x (apply max x2s)
max-y (apply max y2s)]
{:x min-x
:y min-y
:width (- max-x min-x)
:height (- max-y min-y)}))
(defn- common-prefix
[paths]
(->> (apply map vector paths)
(take-while #(apply = %))
(map first)
vec))
(defn combine-as-variants
[ids {:keys [page-id trigger variant-id]}]
(ptk/reify ::combine-as-variants
ptk/WatchEvent
(watch [it state stream]
(let [current-page (:current-page-id state)
combine
(fn [current-page]
(let [objects (dsh/lookup-page-objects state current-page)
ids (->> ids
(cfh/clean-loops objects)
(remove (fn [id]
(let [shape (get objects id)]
(or (not (ctc/main-instance? shape))
(ctc/is-variant? shape))))))]
(when (> (count ids) 1)
(let [shapes (mapv #(get objects %) ids)
rect (bounding-rect shapes)
prefix (->> shapes
(mapv #(cpn/split-path (:name %)))
(common-prefix))
;; When the common parent is root, add a wrapper
add-wrapper? (empty? prefix)
first-shape (first shapes)
delta (gpt/point (- (:x rect) (:x first-shape) 30)
(- (:y rect) (:y first-shape) 30))
common-parent (->> ids
(mapv #(-> (cfh/get-parent-ids objects %) reverse))
common-prefix
last)
index (-> (get objects common-parent)
:shapes
count
inc)
variant-id (or variant-id (uuid/next))
undo-id (js/Symbol)]
(rx/concat
(if (and page-id (not= current-page page-id))
(rx/of (dcm/go-to-workspace :page-id page-id))
(rx/empty))
(rx/of (dwu/start-undo-transaction undo-id)
(transform-in-variant (first ids) variant-id delta prefix add-wrapper? false false)
(dwsh/relocate-shapes (into #{} (-> ids rest reverse)) variant-id 0)
(dwsh/update-shapes ids #(-> %
(assoc :constraints-h :left)
(assoc :constraints-v :top)
(assoc :fixed-scroll false)))
(dwsh/relocate-shapes #{variant-id} common-parent index)
(dwt/update-dimensions [variant-id] :width (+ (:width rect) 60))
(dwt/update-dimensions [variant-id] :height (+ (:height rect) 60))
(ev/event (-> {::ev/name "combine-as-variants" ::ev/origin trigger :number-of-combined (count ids)}
(merge (meta it)))))
;; NOTE: we need to schedule a commit into a
;; microtask for ensure that all the scheduled
;; microtask of previous events execute before the
;; commit
(->> (rx/of (dwu/commit-undo-transaction undo-id))
(rx/observe-on :async)))))))
redirect-to-page
(fn [page-id]
(rx/merge
(->> stream
(rx/filter (ptk/type? ::dwpg/initialize-page))
(rx/take 1)
(rx/observe-on :async)
(rx/mapcat (fn [_] (combine page-id))))
(rx/of (dcm/go-to-workspace :page-id page-id))))]
(if (and page-id (not= page-id current-page))
(redirect-to-page page-id)
(combine current-page))))))
(defn combine-selected-as-variants
[options]
(ptk/reify ::combine-selected-as-variants
ptk/WatchEvent
(watch [_ state _]
(let [selected (dsh/lookup-selected state)]
(rx/of (combine-as-variants selected options))))))
(defn- variant-switch
"Switch the shape (that must be a variant copy head) for the closest one with the property value passed as parameter"
[shape {:keys [pos val] :as params}]
(ptk/reify ::variant-switch
ptk/WatchEvent
(watch [it state _]
(let [libraries (dsh/lookup-libraries state)
component-id (:component-id shape)
component (ctf/get-component libraries (:component-file shape) component-id :include-deleted? false)]
;; If the value is already val, do nothing
(when (not= val (dm/get-in component [:variant-properties pos :value]))
(let [current-page-objects (dsh/lookup-page-objects state)
variant-id (:variant-id component)
component-file-data (dm/get-in libraries [(:component-file shape) :data])
component-page-objects (-> (dsh/get-page component-file-data (:main-instance-page component))
(get :objects))
variant-comps (cfv/find-variant-components component-file-data component-page-objects variant-id)
target-props (-> (:variant-properties component)
(update pos assoc :value val))
valid-comps (->> variant-comps
(remove #(= (:id %) component-id))
(filter #(= (dm/get-in % [:variant-properties pos :value]) val))
(reverse))
nearest-comp (apply min-key #(ctv/distance target-props (:variant-properties %)) valid-comps)
shape-parents (cfh/get-parents-with-self current-page-objects (:parent-id shape))
nearest-comp-children (cfh/get-children-with-self component-page-objects (:main-instance-id nearest-comp))
comps-nesting-loop? (seq? (cfh/components-nesting-loop? nearest-comp-children shape-parents))
{:keys [on-error]
:or {on-error rx/throw}} (meta params)]
;; If there is no nearest-comp, do nothing
(when nearest-comp
(if comps-nesting-loop?
(do
(on-error)
(rx/empty))
(rx/of
(dwl/component-swap shape (:component-file shape) (:id nearest-comp) true)
(ev/event (-> {::ev/name "variant-switch" ::ev/origin "workspace:design-tab"}
(merge (meta it)))))))))))))
(defn variants-switch
"Switch each shape (that must be a variant copy head) for the closest one with the property value passed as parameter"
[{:keys [shapes] :as params}]
(ptk/reify ::variants-switch
ptk/WatchEvent
(watch [it _ _]
(let [ids (into (d/ordered-set) d/xf:map-id shapes)
undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(->> (rx/from shapes)
(rx/map (fn [data]
(-> (variant-switch data params)
(with-meta (meta it))))))
(rx/of (dwu/commit-undo-transaction undo-id)
(dws/select-shapes ids)))))))