diff --git a/common/src/app/common/pages/changes.cljc b/common/src/app/common/pages/changes.cljc index 77ad253fb..6bf52f568 100644 --- a/common/src/app/common/pages/changes.cljc +++ b/common/src/app/common/pages/changes.cljc @@ -326,8 +326,8 @@ (ctf/delete-component data id skip-undelete?)) (defmethod process-change :restore-component - [data {:keys [id]}] - (ctf/restore-component data id)) + [data {:keys [id page-id]}] + (ctf/restore-component data id page-id)) (defmethod process-change :purge-component [data {:keys [id]}] diff --git a/common/src/app/common/pages/changes_builder.cljc b/common/src/app/common/pages/changes_builder.cljc index 313370c86..0eda06bb8 100644 --- a/common/src/app/common/pages/changes_builder.cljc +++ b/common/src/app/common/pages/changes_builder.cljc @@ -656,13 +656,16 @@ :id id}))) (defn restore-component - [changes id] - (assert-library changes) - (-> changes - (update :redo-changes conj {:type :restore-component - :id id}) - (update :undo-changes d/preconj {:type :del-component - :id id}))) + ([changes id] + (restore-component changes id nil)) + ([changes id page-id] + (assert-library changes) + (-> changes + (update :redo-changes conj {:type :restore-component + :id id + :page-id page-id}) + (update :undo-changes d/preconj {:type :del-component + :id id})))) (defn ignore-remote [changes] diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 4045043a0..3beb6ac49 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -201,10 +201,14 @@ (defn restore-component "Recover a deleted component and all its shapes and put all this again in place." - [file-data component-id] - (-> file-data - (ctkl/update-component component-id #(dissoc % :objects)) - (ctkl/mark-component-undeleted component-id))) + [file-data component-id page-id] + (let [components-v2 (dm/get-in file-data [:options :components-v2]) + update-page? (and components-v2 (not (nil? page-id)))] + (-> file-data + (ctkl/update-component component-id #(dissoc % :objects)) + (ctkl/mark-component-undeleted component-id) + (cond-> update-page? + (ctkl/update-component component-id #(assoc % :main-instance-page page-id)))))) (defn purge-component "Remove permanently a component." @@ -383,7 +387,6 @@ (some? (:component-file %)) (assoc :component-file (:id file-data))) main-instance-shapes) - ; Add all shapes of the main instance to the library page add-main-instance-shapes (fn [page] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index f4dc5c1a6..001f9704f 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1600,9 +1600,10 @@ (gpt/subtract (gpt/point (:selrect base)) orig-pos))] [frame-id parent-id delta index])))) - ;; Change the indexes if the paste is done with an element selected + ;; Change the indexes of the pasted shapes (change-add-obj-index [paste-objects selected index change] - (let [set-index (fn [[result index] id] + (let [index (or index -1) ;; if there is no current element selected, we want the first (inc index) to be 0 + set-index (fn [[result index] id] [(assoc result id index) (inc index)]) map-ids @@ -1626,7 +1627,8 @@ ;; Proceed with the standard shape paste process. (do-paste [it state mouse-pos media] - (let [file-id (:current-file-id state) + (let [libraries (wsh/get-libraries state) + file-id (:current-file-id state) page (wsh/lookup-page state) media-idx (d/index-by :prev-id media) @@ -1651,7 +1653,9 @@ all-objects (merge (:objects page) paste-objects) - changes (-> (dws/prepare-duplicate-changes all-objects page selected delta it nil) + library-data (wsh/get-file state file-id) + + changes (-> (dws/prepare-duplicate-changes all-objects page selected delta it libraries library-data) (pcb/amend-changes (partial process-rchange media-idx)) (pcb/amend-changes (partial change-add-obj-index paste-objects selected index))) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index fa2df7b68..23e6f5d78 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -152,7 +152,6 @@ changes (-> (pcb/empty-changes it) (pcb/with-library-data data) (pcb/update-color color)) - undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) @@ -436,18 +435,10 @@ (ptk/reify ::restore-component ptk/WatchEvent (watch [it state _] - (let [library-data (wsh/get-file state library-id) - component (ctkl/get-deleted-component library-data component-id) - page (ctf/get-component-page library-data component) - shapes (cph/get-children-with-self (:objects component) (:main-instance-id component)) - changes (-> (pcb/empty-changes it) - (pcb/with-library-data library-data) - (pcb/with-page page)) - changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true}) - changes - shapes) - changes (pcb/restore-component changes component-id)] - (rx/of (dch/commit-changes changes)))))) + (let [library-data (wsh/get-file state library-id) + changes (:changes (dwlh/prepare-restore-component library-data component-id it))] + (rx/of (dch/commit-changes changes)))))) + (defn instantiate-component "Create a new shape in the current page, from the component with the given id @@ -475,8 +466,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (ptk/data-event :layout/update [(:id new-shape)]) - (dws/select-shapes (d/ordered-set (:id new-shape))) - + (dws/select-shapes (d/ordered-set (:id new-shape))) (dwu/commit-undo-transaction undo-id)))))) (defn detach-component diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index c7c2ca3c4..88272e990 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -146,24 +146,30 @@ (defn generate-instantiate-component "Generate changes to create a new instance from a component." - [changes file-id component-id position page libraries] - (let [component (ctf/get-component libraries file-id component-id) - library (get libraries file-id) + ([changes file-id component-id position page libraries] + (generate-instantiate-component changes file-id component-id position page libraries nil)) - components-v2 (dm/get-in library [:data :options :components-v2]) + ([changes file-id component-id position page libraries old-id] + (let [component (ctf/get-component libraries file-id component-id) + library (get libraries file-id) - [new-shape new-shapes] - (ctn/make-component-instance page - component - (:data library) - position - components-v2) + components-v2 (dm/get-in library [:data :options :components-v2]) - changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true}) - changes - new-shapes)] + [new-shape new-shapes] + (ctn/make-component-instance page + component + (:data library) + position + components-v2) - [new-shape changes])) + changes (cond-> (pcb/add-object changes (first new-shapes) {:ignore-touched true}) + (some? old-id) (pcb/amend-last-change #(assoc % :old-id old-id))) ; on copy/paste old id is used later to reorder the paster layers + + changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true}) + changes + (rest new-shapes))] + + [new-shape changes]))) (defn generate-detach-instance "Generate changes to remove the links between a shape and all its children @@ -185,6 +191,29 @@ (pcb/update-shapes changes shapes update-fn))) + +(defn prepare-restore-component + ([library-data component-id it] + (let [component (ctkl/get-deleted-component library-data component-id) + page (ctf/get-component-page library-data component)] + (prepare-restore-component library-data component-id it page (gpt/point 0 0) nil nil))) + + ([library-data component-id it page delta old-id changes] + (let [component (ctkl/get-deleted-component library-data component-id) + + shapes (cph/get-children-with-self (:objects component) (:main-instance-id component)) + shapes (map #(gsh/move % delta) shapes) + changes (-> (or changes (pcb/empty-changes it)) + (pcb/with-page page) + (pcb/with-library-data library-data)) + changes (cond-> (pcb/add-object changes (first shapes) {:ignore-touched true}) + (some? old-id) (pcb/amend-last-change #(assoc % :old-id old-id))) ; on copy/paste old id is used later to reorder the paster layers + changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true}) + changes + (rest shapes))] + {:changes (pcb/restore-component changes component-id (:id page)) + :shape (first shapes)}))) + ;; ---- General library synchronization functions ---- (defn generate-sync-file diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index f97dc792c..fc55f8b6d 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -15,6 +15,7 @@ [app.common.pages.helpers :as cph] [app.common.spec :as us] [app.common.types.component :as ctk] + [app.common.types.file :as ctf] [app.common.types.page :as ctp] [app.common.types.shape.interactions :as ctsi] [app.common.uuid :as uuid] @@ -332,54 +333,78 @@ (defn prepare-duplicate-changes "Prepare objects to duplicate: generate new id, give them unique names, move to the desired position, and recalculate parents and frames as needed." - [all-objects page ids delta it libraries] - (let [shapes (map (d/getf all-objects) ids) - unames (volatile! (cp/retrieve-used-names (:objects page))) - update-unames! (fn [new-name] (vswap! unames conj new-name)) - all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids all-objects %2))) (d/ordered-set) ids) - ids-map (into {} (map #(vector % (uuid/next))) all-ids) + ([all-objects page ids delta it libraries library-data] + (let [init-changes + (-> (pcb/empty-changes it) + (pcb/with-page page) + (pcb/with-objects all-objects))] + (prepare-duplicate-changes all-objects page ids delta it libraries library-data init-changes))) - init-changes - (-> (pcb/empty-changes it) - (pcb/with-page page) - (pcb/with-objects all-objects)) + ([all-objects page ids delta it libraries library-data init-changes] + (let [shapes (map (d/getf all-objects) ids) + unames (volatile! (cp/retrieve-used-names (:objects page))) + update-unames! (fn [new-name] (vswap! unames conj new-name)) + all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids all-objects %2))) (d/ordered-set) ids) + ids-map (into {} (map #(vector % (uuid/next))) all-ids) - changes - (->> shapes - (reduce #(prepare-duplicate-shape-change %1 - all-objects - page - unames - update-unames! - ids-map - %2 - delta - libraries) - init-changes))] + changes + (->> shapes + (reduce #(prepare-duplicate-shape-change %1 + all-objects + page + unames + update-unames! + ids-map + %2 + delta + libraries + library-data + it) + init-changes))] - (-> changes - (prepare-duplicate-flows shapes page ids-map) - (prepare-duplicate-guides shapes page ids-map delta)))) + (-> changes + (prepare-duplicate-flows shapes page ids-map) + (prepare-duplicate-guides shapes page ids-map delta))))) + +(defn- prepare-duplicate-component-change + ([changes page component-root delta libraries library-data it] + (let [component-id (:component-id component-root) + file-id (:component-file component-root) + main-component (ctf/get-component libraries file-id component-id) + moved-component (gsh/move component-root delta) + pos (gpt/point (:x moved-component) (:y moved-component)) + + instantiate-component + #(dwlh/generate-instantiate-component changes + file-id + (:component-id component-root) + pos + page + libraries + (:id component-root)) + + restore-component + #(let [restore (dwlh/prepare-restore-component library-data (:component-id component-root) it page delta (:id component-root) changes)] + [(:shape restore) (:changes restore)]) + + [_shape changes] + (if (nil? main-component) + (restore-component) + (instantiate-component))] + changes))) (defn- prepare-duplicate-shape-change - ([changes objects page unames update-unames! ids-map obj delta libraries] - (prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta libraries (:frame-id obj) (:parent-id obj))) + ([changes objects page unames update-unames! ids-map obj delta libraries library-data it] + (prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta libraries library-data it (:frame-id obj) (:parent-id obj))) - ([changes objects page unames update-unames! ids-map obj delta libraries frame-id parent-id] + ([changes objects page unames update-unames! ids-map obj delta libraries library-data it frame-id parent-id] (cond (nil? obj) changes (ctk/main-instance? obj) - (let [[_new-shape changes] - (dwlh/generate-instantiate-component changes - (:component-file obj) - (:component-id obj) - (gpt/point (:x obj) (:y obj)) - page - libraries)] - changes) - + (prepare-duplicate-component-change changes page obj delta libraries library-data it) + :else (let [frame? (cph/frame-shape? obj) new-id (ids-map (:id obj)) @@ -410,6 +435,8 @@ child delta libraries + library-data + it (if frame? new-id frame-id) new-id)) changes @@ -562,9 +589,11 @@ (calc-duplicate-delta obj state objects) (gpt/point 0 0)) + file-id (:current-file-id state) libraries (wsh/get-libraries state) + library-data (wsh/get-file state file-id) - changes (->> (prepare-duplicate-changes objects page selected delta it libraries) + changes (->> (prepare-duplicate-changes objects page selected delta it libraries library-data) (duplicate-changes-update-indices objects selected)) changes (cond-> changes add-undo-group? (assoc :undo-group (uuid/random)))