diff --git a/common/src/app/common/pages/changes.cljc b/common/src/app/common/pages/changes.cljc index 36a8a576c..6e0c74115 100644 --- a/common/src/app/common/pages/changes.cljc +++ b/common/src/app/common/pages/changes.cljc @@ -392,11 +392,13 @@ ;; -- Components (defmethod process-change :add-component - [data {:keys [id name path shapes]}] + [data {:keys [id name path main-instance-id main-instance-page shapes]}] (assoc-in data [:components id] {:id id :name name :path path + :main-instance-id main-instance-id + :main-instance-page main-instance-page :objects (d/index-by :id shapes)})) (defmethod process-change :mod-component diff --git a/common/src/app/common/pages/changes_builder.cljc b/common/src/app/common/pages/changes_builder.cljc index 25808b136..13e9d710f 100644 --- a/common/src/app/common/pages/changes_builder.cljc +++ b/common/src/app/common/pages/changes_builder.cljc @@ -532,7 +532,7 @@ (apply-changes-local)))) (defn add-component - [changes id path name new-shapes updated-shapes] + [changes id path name new-shapes updated-shapes main-instance-id main-instance-page] (assert-page-id changes) (assert-objects changes) (let [page-id (::page-id (meta changes)) @@ -566,6 +566,8 @@ :id id :path path :name name + :main-instance-id main-instance-id + :main-instance-page main-instance-page :shapes new-shapes}) (into (map mk-change) updated-shapes)))) (update :undo-changes @@ -611,5 +613,7 @@ :id id :name (:name prev-component) :path (:path prev-component) + :main-instance-id (:main-instance-id prev-component) + :main-instance-page (:main-instance-page prev-component) :shapes (vals (:objects prev-component))})))) diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 044d97346..52fa4748d 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -362,6 +362,11 @@ (or (= (:shape-ref shape-inst) (:id shape-main)) (= (:shape-ref shape-inst) (:shape-ref shape-main))))) +(defn is-main-instance? + [shape-id page-id component] + (and (= shape-id (:main-instance-id component)) + (= page-id (:main-instance-page component)))) + (defn get-component-root [component] (get-in component [:objects (:id component)])) diff --git a/frontend/resources/images/icons/component-copy.svg b/frontend/resources/images/icons/component-copy.svg new file mode 100644 index 000000000..ab45fb4a8 --- /dev/null +++ b/frontend/resources/images/icons/component-copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index d9671bfed..6f9ac6d35 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -108,4 +108,3 @@ :undo-changes [] :origin it :save-undo? false}))))))))))) - diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 7663a0585..6e4fa70ca 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -354,16 +354,20 @@ unames (into #{} (map :name) all-components) new-name (un/generate-unique-name unames (:name component)) - [new-shape new-shapes _updated-shapes] + [new-shape new-shapes _updated-shapes main-instance main-instance-page] (dwlh/duplicate-component component) + _ (prn "OJOOOOOOOOOOOOOOO falta calcular main-instance") + changes (-> (pcb/empty-changes it nil) ;; no objects are changed (pcb/with-objects nil) ;; in the current page (pcb/add-component (:id new-shape) (:path component) new-name new-shapes - []))] + [] + (:id main-instance) + (:id main-instance-page)))] (rx/of (dch/commit-changes changes)))))) diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index a1e50c89f..1c74d9d1e 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -121,7 +121,9 @@ path name new-shapes - updated-shapes))] + updated-shapes + (:id group) + page-id))] [group new-shape changes]))) (defn duplicate-component diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 28adef987..77d526f4b 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -138,13 +138,17 @@ (ptk/reify ::delete-shapes ptk/WatchEvent (watch [it state _] - (let [page-id (:current-page-id state) - objects (wsh/lookup-page-objects state page-id) - page (wsh/lookup-page state page-id) + (let [file-id (:current-file-id state) + page-id (:current-page-id state) + file (wsh/get-file state file-id) + page (wsh/lookup-page state page-id) + objects (wsh/lookup-page-objects state page-id) ids (cph/clean-loops objects ids) lookup (d/getf objects) + local-library {file-id {:data file}} + groups-to-unmask (reduce (fn [group-ids id] ;; When the shape to delete is the mask of a masked group, @@ -164,7 +168,7 @@ ;; If any of the deleted shapes is the destination of ;; some interaction, this must be deleted, too. (let [interactions (:interactions shape)] - (some #(and (csi/has-destination %) + (some #(and (ctsi/has-destination %) (contains? ids (:destination %))) interactions))) (vals objects)) @@ -215,9 +219,29 @@ ;; Any parent whose children are all deleted, must be deleted too. (into (d/ordered-set) (find-all-empty-parents #{})) + components-to-delete + (reduce (fn [components id] + (let [shape (get objects id) + + component + (when (and (:component-id shape) (:component-file shape)) + ;; Only local components may have main instances + (cph/get-component local-library (:component-file shape) (:component-id shape))) + + main-instance? + (when component + (cph/is-main-instance? (:id shape) (:id page) component))] + + (if main-instance? + (conj components (:component-id shape)) + components))) + [] + (into ids all-children)) + changes (-> (pcb/empty-changes it page-id) (pcb/with-page page) (pcb/with-objects objects) + (pcb/with-library-data file) (pcb/set-page-option :guides guides) (pcb/remove-objects all-children) (pcb/remove-objects ids) @@ -231,13 +255,18 @@ (d/update-when shape :interactions (fn [interactions] (into [] - (remove #(and (csi/has-destination %) + (remove #(and (ctsi/has-destination %) (contains? ids (:destination %)))) interactions))))) (cond-> (seq starting-flows) (pcb/update-page-option :flows (fn [flows] (->> (map :id starting-flows) - (reduce csp/remove-flow flows))))))] + (reduce ctp/remove-flow flows)))))) + + changes (reduce (fn [changes component-id] + (pcb/delete-component changes component-id)) + changes + components-to-delete)] (rx/of (dch/commit-changes changes) (dwsl/update-layout-positions all-parents)))))) diff --git a/frontend/src/app/main/ui/components/shape_icon.cljs b/frontend/src/app/main/ui/components/shape_icon.cljs index 0a54feeae..21b5e1e32 100644 --- a/frontend/src/app/main/ui/components/shape_icon.cljs +++ b/frontend/src/app/main/ui/components/shape_icon.cljs @@ -11,7 +11,7 @@ (mf/defc element-icon - [{:keys [shape] :as props}] + [{:keys [shape main-instance?] :as props}] (case (:type shape) :frame i/artboard :image i/image @@ -21,7 +21,9 @@ :rect i/box :text i/text :group (if (some? (:component-id shape)) - i/component + (if main-instance? + i/component + i/component-copy) (if (:masked-group? shape) i/mask i/folder)) diff --git a/frontend/src/app/main/ui/context.cljs b/frontend/src/app/main/ui/context.cljs index 34a3518c9..7f2f191ac 100644 --- a/frontend/src/app/main/ui/context.cljs +++ b/frontend/src/app/main/ui/context.cljs @@ -21,6 +21,7 @@ (def current-project-id (mf/create-context nil)) (def current-page-id (mf/create-context nil)) (def current-file-id (mf/create-context nil)) +(def libraries (mf/create-context nil)) (def scroll-ctx (mf/create-context nil)) (def active-frames-ctx (mf/create-context nil)) -(def render-thumbnails (mf/create-context nil)) +(def render-thumbnails (mf/create-context nil)) diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index b8232b043..ae5d38542 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -53,6 +53,7 @@ (def close (icon-xref :close)) (def code (icon-xref :code)) (def component (icon-xref :component)) +(def component-copy (icon-xref :component-copy)) (def copy (icon-xref :copy)) (def curve (icon-xref :curve)) (def cross (icon-xref :cross)) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 17b1fce61..6068c1799 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -114,10 +114,13 @@ (mf/defc workspace {::mf/wrap [mf/memo]} [{:keys [project-id file-id page-id layout-name] :as props}] - (let [file (mf/deref refs/workspace-file) - project (mf/deref refs/workspace-project) - layout (mf/deref refs/workspace-layout) - wglobal (mf/deref refs/workspace-global) + (let [file (mf/deref refs/workspace-file) + project (mf/deref refs/workspace-project) + layout (mf/deref refs/workspace-layout) + wglobal (mf/deref refs/workspace-global) + wglobal (mf/deref refs/workspace-global) + libraries (mf/deref refs/workspace-libraries) + local-library (mf/deref refs/workspace-local-library) background-color (:background-color wglobal)] @@ -145,23 +148,26 @@ [:& (mf/provider ctx/current-team-id) {:value (:team-id project)} [:& (mf/provider ctx/current-project-id) {:value (:id project)} [:& (mf/provider ctx/current-page-id) {:value page-id} - [:section#workspace {:style {:background-color background-color}} - (when (not (:hide-ui layout)) - [:& header {:file file - :page-id page-id - :project project - :layout layout}]) + [:& (mf/provider ctx/libraries) {:value (assoc libraries + (:id local-library) + {:data local-library})} + [:section#workspace {:style {:background-color background-color}} + (when (not (:hide-ui layout)) + [:& header {:file file + :page-id page-id + :project project + :layout layout}]) - [:& context-menu] + [:& context-menu] - (if (and (and file project) - (:initialized file)) - [:& workspace-page {:key (dm/str "page-" page-id) - :page-id page-id - :file file - :wglobal wglobal - :layout layout}] - [:& workspace-loader])]]]]])) + (if (and (and file project) + (:initialized file)) + [:& workspace-page {:key (dm/str "page-" page-id) + :page-id page-id + :file file + :wglobal wglobal + :layout layout}] + [:& workspace-loader])]]]]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 32b19d0fa..27a9fe468 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -15,6 +15,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.shape-icon :as si] + [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -85,7 +86,7 @@ (when (seq (:touched shape)) " *")]))) (mf/defc layer-item - [{:keys [index item selected objects] :as props}] + [{:keys [index page item selected objects] :as props}] (let [id (:id item) blocked? (:blocked item) hidden? (:hidden item) @@ -101,6 +102,12 @@ container? (or (cph/frame-shape? item) (cph/group-shape? item)) + libraries (mf/use-ctx ctx/libraries) + component (when (and (:component-id item) (:component-file item)) + (cph/get-component libraries (:component-file item) (:component-id item))) + main-instance? (when component + (cph/is-main-instance? (:id item) (:id page) component)) + toggle-collapse (mf/use-fn (mf/deps expanded?) @@ -244,7 +251,8 @@ [:div {:on-double-click #(do (dom/stop-propagation %) (dom/prevent-default %) (st/emit! dw/zoom-to-selected-shape))} - [:& si/element-icon {:shape item}]] + [:& si/element-icon {:shape item + :main-instance? main-instance?}]] [:& layer-name {:shape item :name-ref ref :on-start-edit #(reset! disable-drag true) @@ -268,7 +276,8 @@ (for [[index id] (reverse (d/enumerate (:shapes item)))] (when-let [item (get objects id)] [:& layer-item - {:item item + {:page page + :item item :selected selected :index index :objects objects @@ -289,8 +298,9 @@ {::mf/wrap [#(mf/memo % =) #(mf/throttle % 200)]} [{:keys [objects] :as props}] - (let [selected (mf/deref refs/selected-shapes) - selected (hooks/use-equal-memo selected) + (let [page (mf/deref refs/workspace-page) + selected (mf/deref refs/selected-shapes) + selected (hooks/use-equal-memo selected) root (get objects uuid/zero)] [:ul.element-list [:& hooks/sortable-container {} @@ -304,7 +314,8 @@ :objects objects :key id}] [:& layer-item - {:item obj + {:page page + :item obj :selected selected :index index :objects objects @@ -314,14 +325,16 @@ {::mf/wrap [#(mf/memo % =) #(mf/throttle % 200)]} [{:keys [objects] :as props}] - (let [selected (mf/deref refs/selected-shapes) + (let [page (mf/deref refs/workspace-page) + selected (mf/deref refs/selected-shapes) selected (hooks/use-equal-memo selected) root (get objects uuid/zero)] [:ul.element-list (for [[index id] (d/enumerate (:shapes root))] (when-let [obj (get objects id)] [:& layer-item - {:item obj + {:page page + :item obj :selected selected :index index :objects objects @@ -444,7 +457,6 @@ (take (:num-items @filter-state)) filtered-objects-total)))) - handle-show-more (fn [] (when (<= (:num-items @filter-state) (count filtered-objects-total)) @@ -542,7 +554,6 @@ (when last-hidden-frame (dom/add-class! last-hidden-frame "sticky"))))] - [:div#layers.tool-window (if (d/not-empty? focus) [:div.tool-window-bar diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs index 0a28635ce..3e19ec5f9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs @@ -22,6 +22,8 @@ (mf/defc component-menu [{:keys [ids values shape-name] :as props}] (let [current-file-id (mf/use-ctx ctx/current-file-id) + current-page-id (mf/use-ctx ctx/current-page-id) + libraries (mf/use-ctx ctx/libraries) id (first ids) local (mf/use-state {:menu-open false}) @@ -30,6 +32,10 @@ library-id (:component-file values) show? (some? component-id) + component (when (and component-id library-id) + (cph/get-component libraries library-id component-id)) + main-instance? (cph/is-main-instance? id current-page-id component) + on-menu-click (mf/use-callback (fn [event] @@ -69,7 +75,9 @@ [:span (tr "workspace.options.component")]] [:div.element-set-content [:div.row-flex.component-row - i/component + (if main-instance? + i/component + i/component-copy) shape-name [:div.row-actions {:on-click on-menu-click}