🐛 Fix ungroup component

This commit is contained in:
Pablo Alba 2023-06-15 14:25:09 +02:00 committed by Andrés Moya
parent 128fe29619
commit 7733bc4419
3 changed files with 167 additions and 101 deletions

View file

@ -328,6 +328,9 @@
The list of objects are returned in tree traversal order, respecting The list of objects are returned in tree traversal order, respecting
the order of the children of each parent." the order of the children of each parent."
([object parent-id objects]
(clone-object object parent-id objects (fn [object _] object) (fn [object _] object) nil false))
([object parent-id objects update-new-object] ([object parent-id objects update-new-object]
(clone-object object parent-id objects update-new-object (fn [object _] object) nil false)) (clone-object object parent-id objects update-new-object (fn [object _] object) nil false))

View file

@ -12,10 +12,14 @@
[app.common.pages.changes-builder :as pcb] [app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[beicon.core :as rx] [beicon.core :as rx]
@ -140,7 +144,7 @@
(mapv #(get objects %))) (mapv #(get objects %)))
parent-id (cph/get-parent-id objects (:id frame)) parent-id (cph/get-parent-id objects (:id frame))
idx-in-parent (->> (:id frame) idx-in-parent (->> (:id frame)
(cph/get-position-on-parent objects) (cph/get-position-on-parent objects)
inc)] inc)]
(-> (pcb/empty-changes it page-id) (-> (pcb/empty-changes it page-id)
@ -150,6 +154,46 @@
(pcb/change-parent parent-id children idx-in-parent) (pcb/change-parent parent-id children idx-in-parent)
(pcb/remove-objects [(:id frame)])))) (pcb/remove-objects [(:id frame)]))))
(defn- clone-component-shapes-changes
[changes shape objects]
(let [shape-parent-id (:parent-id shape)
new-shape-id (uuid/next)
[_ new-shapes _]
(ctst/clone-object shape
shape-parent-id
objects
(fn [object _]
(cond-> object
(= new-shape-id (:parent-id object))
(assoc :parent-id shape-parent-id)))
(fn [object _] object)
new-shape-id
false)
new-shapes (->> new-shapes
(filter #(not= (:id %) new-shape-id)))]
(reduce
(fn [changes shape]
(pcb/add-object changes shape))
changes
new-shapes)))
(defn remove-component-changes
[it page-id shape objects file-data file]
(let [page (ctpl/get-page file-data page-id)
components-v2 (dm/get-in file-data [:options :components-v2])
;; In order to ungroup a component, we first make a clone of its shapes,
;; and then we delete it
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(pcb/with-library-data file-data)
(pcb/with-page page)
(clone-component-shapes-changes shape objects)
(dwsh/delete-shapes-changes file page objects [(:id shape)] it components-v2))]
;; TODO: Should we call detach-comment-thread ?
changes))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GROUPS ;; GROUPS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -173,13 +217,18 @@
(ptk/reify ::ungroup-selected (ptk/reify ::ungroup-selected
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [page-id (:current-page-id state) (let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id) objects (wsh/lookup-page-objects state page-id)
file-data (get state :workspace-data)
file (wsh/get-local-file state)
prepare prepare
(fn [shape-id] (fn [shape-id]
(let [shape (get objects shape-id)] (let [shape (get objects shape-id)]
(cond (cond
(ctk/main-instance? shape)
(remove-component-changes it page-id shape objects file-data file)
(or (cph/group-shape? shape) (cph/bool-shape? shape)) (or (cph/group-shape? shape) (cph/bool-shape? shape))
(remove-group-changes it page-id shape objects) (remove-group-changes it page-id shape objects)

View file

@ -214,126 +214,140 @@
(real-delete-shapes file page objects ids-to-delete it components-v2) (real-delete-shapes file page objects ids-to-delete it components-v2)
(rx/of (dwu/commit-undo-transaction undo-id)))))))) (rx/of (dwu/commit-undo-transaction undo-id))))))))
(defn- real-delete-shapes (defn- real-delete-shapes-changes
[file page objects ids it components-v2] ([file page objects ids it components-v2]
(let [lookup (d/getf objects) (let [changes (-> (pcb/empty-changes it (:id page))
(pcb/with-page page)
groups-to-unmask (pcb/with-objects objects)
(reduce (fn [group-ids id] (pcb/with-library-data file))]
(real-delete-shapes-changes changes file page objects ids it components-v2)))
([changes file page objects ids _it components-v2]
(let [lookup (d/getf objects)
groups-to-unmask
(reduce (fn [group-ids id]
;; When the shape to delete is the mask of a masked group, ;; When the shape to delete is the mask of a masked group,
;; the mask condition must be removed, and it must be ;; the mask condition must be removed, and it must be
;; converted to a normal group. ;; converted to a normal group.
(let [obj (lookup id) (let [obj (lookup id)
parent (lookup (:parent-id obj))] parent (lookup (:parent-id obj))]
(if (and (:masked-group? parent) (if (and (:masked-group? parent)
(= id (first (:shapes parent)))) (= id (first (:shapes parent))))
(conj group-ids (:id parent)) (conj group-ids (:id parent))
group-ids))) group-ids)))
#{} #{}
ids) ids)
interacting-shapes interacting-shapes
(filter (fn [shape] (filter (fn [shape]
;; If any of the deleted shapes is the destination of ;; If any of the deleted shapes is the destination of
;; some interaction, this must be deleted, too. ;; some interaction, this must be deleted, too.
(let [interactions (:interactions shape)] (let [interactions (:interactions shape)]
(some #(and (ctsi/has-destination %) (some #(and (ctsi/has-destination %)
(contains? ids (:destination %))) (contains? ids (:destination %)))
interactions))) interactions)))
(vals objects)) (vals objects))
;; If any of the deleted shapes is a frame with guides ;; If any of the deleted shapes is a frame with guides
guides (into {} guides (into {}
(comp (map second) (comp (map second)
(remove #(contains? ids (:frame-id %))) (remove #(contains? ids (:frame-id %)))
(map (juxt :id identity))) (map (juxt :id identity)))
(dm/get-in page [:options :guides])) (dm/get-in page [:options :guides]))
starting-flows starting-flows
(filter (fn [flow] (filter (fn [flow]
;; If any of the deleted is a frame that starts a flow, ;; If any of the deleted is a frame that starts a flow,
;; this must be deleted, too. ;; this must be deleted, too.
(contains? ids (:starting-frame flow))) (contains? ids (:starting-frame flow)))
(-> page :options :flows)) (-> page :options :flows))
all-parents all-parents
(reduce (fn [res id] (reduce (fn [res id]
;; All parents of any deleted shape must be resized. ;; All parents of any deleted shape must be resized.
(into res (cph/get-parent-ids objects id))) (into res (cph/get-parent-ids objects id)))
(d/ordered-set) (d/ordered-set)
ids) ids)
all-children all-children
(->> ids ;; Children of deleted shapes must be also deleted. (->> ids ;; Children of deleted shapes must be also deleted.
(reduce (fn [res id] (reduce (fn [res id]
(into res (cph/get-children-ids objects id))) (into res (cph/get-children-ids objects id)))
[]) [])
(reverse) (reverse)
(into (d/ordered-set))) (into (d/ordered-set)))
find-all-empty-parents find-all-empty-parents
(fn recursive-find-empty-parents [empty-parents] (fn recursive-find-empty-parents [empty-parents]
(let [all-ids (into empty-parents ids) (let [all-ids (into empty-parents ids)
contains? (partial contains? all-ids) contains? (partial contains? all-ids)
xform (comp (map lookup) xform (comp (map lookup)
(filter cph/group-shape?) (filter cph/group-shape?)
(remove #(->> (:shapes %) (remove contains?) seq)) (remove #(->> (:shapes %) (remove contains?) seq))
(map :id)) (map :id))
parents (into #{} xform all-parents)] parents (into #{} xform all-parents)]
(if (= empty-parents parents) (if (= empty-parents parents)
empty-parents empty-parents
(recursive-find-empty-parents parents)))) (recursive-find-empty-parents parents))))
empty-parents empty-parents
;; Any parent whose children are all deleted, must be deleted too. ;; Any parent whose children are all deleted, must be deleted too.
(into (d/ordered-set) (find-all-empty-parents #{})) (into (d/ordered-set) (find-all-empty-parents #{}))
components-to-delete components-to-delete
(if components-v2 (if components-v2
(reduce (fn [components id] (reduce (fn [components id]
(let [shape (get objects id)] (let [shape (get objects id)]
(if (and (= (:component-file shape) (:id file)) ;; Main instances should exist only in local file (if (and (= (:component-file shape) (:id file)) ;; Main instances should exist only in local file
(:main-instance? shape)) ;; but check anyway (:main-instance? shape)) ;; but check anyway
(conj components (:component-id shape)) (conj components (:component-id shape))
components))) components)))
[] []
(into ids all-children)) (into ids all-children))
[]) [])
changes (-> (pcb/empty-changes it (:id page)) changes (-> changes
(pcb/with-page page) (pcb/set-page-option :guides guides))
(pcb/with-objects objects)
(pcb/with-library-data file)
(pcb/set-page-option :guides guides))
changes (reduce (fn [changes component-id] changes (reduce (fn [changes component-id]
;; It's important to delete the component before the main instance, because we ;; It's important to delete the component before the main instance, because we
;; need to store the instance position if we want to restore it later. ;; need to store the instance position if we want to restore it later.
(pcb/delete-component changes component-id)) (pcb/delete-component changes component-id))
changes changes
components-to-delete) components-to-delete)
changes (-> changes changes (-> changes
(pcb/remove-objects all-children {:ignore-touched true}) (pcb/remove-objects all-children {:ignore-touched true})
(pcb/remove-objects ids) (pcb/remove-objects ids)
(pcb/remove-objects empty-parents) (pcb/remove-objects empty-parents)
(pcb/resize-parents all-parents) (pcb/resize-parents all-parents)
(pcb/update-shapes groups-to-unmask (pcb/update-shapes groups-to-unmask
(fn [shape] (fn [shape]
(assoc shape :masked-group? false))) (assoc shape :masked-group? false)))
(pcb/update-shapes (map :id interacting-shapes) (pcb/update-shapes (map :id interacting-shapes)
(fn [shape] (fn [shape]
(d/update-when shape :interactions (d/update-when shape :interactions
(fn [interactions] (fn [interactions]
(into [] (into []
(remove #(and (ctsi/has-destination %) (remove #(and (ctsi/has-destination %)
(contains? ids (:destination %)))) (contains? ids (:destination %))))
interactions))))) interactions)))))
(cond-> (seq starting-flows) (cond-> (seq starting-flows)
(pcb/update-page-option :flows (fn [flows] (pcb/update-page-option :flows (fn [flows]
(->> (map :id starting-flows) (->> (map :id starting-flows)
(reduce ctp/remove-flow flows)))))) (reduce ctp/remove-flow flows))))))]
undo-id (js/Symbol)] [changes all-parents])))
(defn delete-shapes-changes
[changes file page objects ids it components-v2]
(let [[changes _all-parents] (real-delete-shapes-changes changes file page objects ids it components-v2)]
changes))
(defn- real-delete-shapes
[file page objects ids it components-v2]
(let [[changes all-parents] (real-delete-shapes-changes file page objects ids it components-v2)
undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id) (rx/of (dwu/start-undo-transaction undo-id)
(dc/detach-comment-thread ids) (dc/detach-comment-thread ids)
(dch/commit-changes changes) (dch/commit-changes changes)
@ -467,7 +481,7 @@
;; We have change only the hidden behaviour, to hide only the ;; We have change only the hidden behaviour, to hide only the
;; selected shape, block behaviour remains the same. ;; selected shape, block behaviour remains the same.
ids (if (boolean? blocked) ids (if (boolean? blocked)
(into ids (->> ids (mapcat #(cph/get-children-ids objects %)))) (into ids (->> ids (mapcat #(cph/get-children-ids objects %))))
ids)] ids)]
(rx/of (dch/update-shapes ids update-fn)))))) (rx/of (dch/update-shapes ids update-fn))))))