From 3482d6c3038884ff031ceb7a0f4096d0c76b242b Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 18 Jan 2022 16:48:58 +0100 Subject: [PATCH] :sparkles: Add Update component in bulk option --- CHANGES.md | 1 + .../resources/styles/main/partials/modal.scss | 20 ++++ .../app/main/data/workspace/libraries.cljs | 92 ++++++++++--------- frontend/src/app/main/ui/confirm.cljs | 14 ++- .../app/main/ui/workspace/context_menu.cljs | 50 ++++++---- frontend/translations/en.po | 20 +++- frontend/translations/es.po | 20 +++- 7 files changed, 155 insertions(+), 62 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 634e6a503..8886b953a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ - Guides [Taiga #290](https://tree.taiga.io/project/penpot/us/290?milestone=307334) - Improve file menu by adding semantically groups [Github #1203](https://github.com/penpot/penpot/issues/1203). +- Add update components in bulk option in context menu [Taiga #1975](https://tree.taiga.io/project/penpot/us/1975). - Create e2e tests for drawing basic fors [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608). - Create firsts e2e test [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608). diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index d852fbd0e..e48eb161d 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -105,6 +105,26 @@ } } + .modal-item-element { + display: flex; + padding-bottom: 3px; + margin-left: 10px; + font-size: $fs14; + color: $color-info; + + .modal-component-icon { + margin-right: 16px; + display: flex; + justify-content: center; + align-items: center; + svg { + width: 16px; + height: 16px; + fill: $color-info; + } + } + } + .modal-content { display: flex; flex-direction: column; diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index fa8132e04..7fe7ccf31 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -347,7 +347,7 @@ (let [component (cp/get-component id (:current-file-id state) (dwlh/get-local-file state) - nil) + nil) all-components (vals (get-in state [:workspace-data :components])) unames (set (map :name all-components)) new-name (dwc/generate-unique-name unames (:name component)) @@ -424,7 +424,7 @@ (cond-> new-shape true (as-> $ - (geom/move $ delta) + (geom/move $ delta) (assoc $ :frame-id frame-id) (assoc $ :parent-id (or (:parent-id $) (:frame-id $))) @@ -444,9 +444,9 @@ [new-shape new-shapes _] (cp/clone-object component-shape - nil - (get component :objects) - update-new-shape) + nil + (get component :objects) + update-new-shape) rchanges (mapv (fn [obj] {:type :add-obj @@ -506,8 +506,8 @@ [rchanges uchanges] (reduce (fn [changes id] (dwlh/concat-changes - changes - (dwlh/generate-detach-instance id container))) + changes + (dwlh/generate-detach-instance id container))) dwlh/empty-changes selected)] @@ -565,8 +565,8 @@ libraries true)] (log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes - rchanges - local-library)) + rchanges + local-library)) (rx/of (dch/commit-changes {:redo-changes rchanges :undo-changes uchanges @@ -603,26 +603,26 @@ file (dwlh/get-file state file-id) xf-filter (comp - (filter :local-change?) - (map #(dissoc % :local-change?))) + (filter :local-change?) + (map #(dissoc % :local-change?))) local-rchanges (into [] xf-filter rchanges) local-uchanges (into [] xf-filter uchanges) xf-remove (comp - (remove :local-change?) - (map #(dissoc % :local-change?))) + (remove :local-change?) + (map #(dissoc % :local-change?))) rchanges (into [] xf-remove rchanges) uchanges (into [] xf-remove uchanges)] (log/debug :msg "UPDATE-COMPONENT finished" :js/local-rchanges (log-changes - local-rchanges - local-library) + local-rchanges + local-library) :js/rchanges (log-changes - rchanges - file)) + rchanges + file)) (rx/of (when (seq local-rchanges) (dch/commit-changes {:redo-changes local-rchanges @@ -649,6 +649,16 @@ (sync-file file-id file-id)) (dwu/commit-undo-transaction)))))) +(defn update-component-in-bulk + [shapes file-id] + (ptk/reify ::update-component-in-bulk + ptk/WatchEvent + (watch [_ _ _] + (rx/concat + (rx/of (dwu/start-undo-transaction)) + (rx/map #(update-component-sync (:id %) file-id) (rx/from shapes)) + (rx/of (dwu/commit-undo-transaction)))))) + (declare sync-file-2nd-stage) (defn sync-file @@ -690,28 +700,28 @@ (sequence xf-scat file-changes))] (log/debug :msg "SYNC-FILE finished" :js/rchanges (log-changes - rchanges - file)) + rchanges + file)) (rx/concat - (rx/of (dm/hide-tag :sync-dialog)) - (when rchanges - (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it - :file-id file-id}))) - (when (not= file-id library-id) + (rx/of (dm/hide-tag :sync-dialog)) + (when rchanges + (rx/of (dch/commit-changes {:redo-changes rchanges + :undo-changes uchanges + :origin it + :file-id file-id}))) + (when (not= file-id library-id) ;; When we have just updated the library file, give some time for the ;; update to finish, before marking this file as synced. ;; TODO: look for a more precise way of syncing this. ;; Maybe by using the stream (second argument passed to watch) ;; to wait for the corresponding changes-committed and then proceed ;; with the :update-sync mutation. - (rx/concat (rx/timer 3000) - (rp/mutation :update-sync - {:file-id file-id - :library-id library-id}))) - (when (some? library-changes) - (rx/of (sync-file-2nd-stage file-id library-id)))))))) + (rx/concat (rx/timer 3000) + (rp/mutation :update-sync + {:file-id file-id + :library-id library-id}))) + (when (some? library-changes) + (rx/of (sync-file-2nd-stage file-id library-id)))))))) (defn sync-file-2nd-stage "If some components have been modified, we need to launch another synchronization @@ -738,8 +748,8 @@ uchanges (d/concat-vec uchanges1 uchanges2)] (when rchanges (log/debug :msg "SYNC-FILE (2nd stage) finished" :js/rchanges (log-changes - rchanges - file)) + rchanges + file)) (rx/of (dch/commit-changes {:redo-changes rchanges :undo-changes uchanges :origin it @@ -774,11 +784,11 @@ (st/emit! dm/hide))] (rx/of (dm/info-dialog - (tr "workspace.updates.there-are-updates") - :inline-actions - [{:label (tr "workspace.updates.update") - :callback do-update} - {:label (tr "workspace.updates.dismiss") - :callback do-dismiss}] - :sync-dialog)))))) + (tr "workspace.updates.there-are-updates") + :inline-actions + [{:label (tr "workspace.updates.update") + :callback do-update} + {:label (tr "workspace.updates.dismiss") + :callback do-dismiss}] + :sync-dialog)))))) diff --git a/frontend/src/app/main/ui/confirm.cljs b/frontend/src/app/main/ui/confirm.cljs index 6efc5bc9b..7e154ef95 100644 --- a/frontend/src/app/main/ui/confirm.cljs +++ b/frontend/src/app/main/ui/confirm.cljs @@ -24,6 +24,7 @@ on-accept on-cancel hint + items cancel-label accept-label accept-style] :as props}] @@ -70,9 +71,18 @@ {:on-click cancel-fn} i/close]] [:div.modal-content - [:h3 message] + (when (and (string? message) (not= message "")) + [:h3 message]) (when (string? hint) - [:p hint])] + [:p hint]) + (when (> (count items) 0) + [:* + [:p (tr "ds.component-subtitle")] + [:ul + (for [item items] + [:li.modal-item-element + [:span.modal-component-icon i/component] + [:span (:name item)]])]])] [:div.modal-footer [:div.action-buttons diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 58556d94f..cc13bcecb 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -186,10 +186,10 @@ (when (not has-frame?) [:* - [:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection") - :shortcut (sc/get-tooltip :create-artboard-from-selection) - :on-click do-create-artboard-from-selection}] - [:& menu-separator]])])) + [:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection") + :shortcut (sc/get-tooltip :create-artboard-from-selection) + :on-click do-create-artboard-from-selection}] + [:& menu-separator]])])) (mf/defc context-menu-path [{:keys [shapes disable-flatten? disable-booleans?]}] @@ -303,6 +303,7 @@ shape-id (->> shapes first :id) component-id (->> shapes first :component-id) component-file (-> shapes first :component-file) + component-shapes (filter #(contains? % :component-id) shapes) current-file-id (mf/use-ctx ctx/current-file-id) local-component? (= component-file current-file-id) @@ -314,6 +315,7 @@ do-show-component (st/emitf (dw/go-to-component component-id)) do-navigate-component-file (st/emitf (dwl/nav-to-component-file component-file)) do-update-component (st/emitf (dwl/update-component-sync shape-id component-file)) + do-update-component-in-bulk (st/emitf (dwl/update-component-in-bulk component-shapes component-file)) do-update-remote-component (st/emitf (modal/show @@ -324,7 +326,18 @@ :cancel-label (tr "modals.update-remote-component.cancel") :accept-label (tr "modals.update-remote-component.accept") :accept-style :primary - :on-accept do-update-component}))] + :on-accept do-update-component})) + + do-update-in-bulk (st/emitf (modal/show + {:type :confirm + :message "" + :title (tr "modals.update-remote-component-in-bulk.message") + :hint (tr "modals.update-remote-component-in-bulk.hint") + :items component-shapes + :cancel-label (tr "modals.update-remote-component.cancel") + :accept-label (tr "modals.update-remote-component.accept") + :accept-style :primary + :on-accept do-update-component-in-bulk}))] [:* (when (and (not has-frame?) (not is-component?)) [:* @@ -335,7 +348,10 @@ (when has-component? [:& menu-entry {:title (tr "workspace.shape.menu.detach-instances-in-bulk") :shortcut (sc/get-tooltip :detach-component) - :on-click do-detach-component-in-bulk}])]) + :on-click do-detach-component-in-bulk}] + (when (not single?) + [:& menu-entry {:title (tr "workspace.shape.menu.update-components-in-bulk") + :on-click do-update-in-bulk}]))]) (when is-component? ;; WARNING: this menu is the same as the context menu at the sidebar. @@ -407,17 +423,17 @@ dropdown-ref (mf/use-ref)] (mf/use-effect - (mf/deps mdata) - #(let [dropdown (mf/ref-val dropdown-ref)] - (when dropdown - (let [bounding-rect (dom/get-bounding-rect dropdown) - window-size (dom/get-window-size) - delta-x (max (- (+ (:right bounding-rect) 250) (:width window-size)) 0) - delta-y (max (- (:bottom bounding-rect) (:height window-size)) 0) - new-style (str "top: " (- top delta-y) "px; " - "left: " (- left delta-x) "px;")] - (when (or (> delta-x 0) (> delta-y 0)) - (.setAttribute ^js dropdown "style" new-style)))))) + (mf/deps mdata) + #(let [dropdown (mf/ref-val dropdown-ref)] + (when dropdown + (let [bounding-rect (dom/get-bounding-rect dropdown) + window-size (dom/get-window-size) + delta-x (max (- (+ (:right bounding-rect) 250) (:width window-size)) 0) + delta-y (max (- (:bottom bounding-rect) (:height window-size)) 0) + new-style (str "top: " (- top delta-y) "px; " + "left: " (- left delta-x) "px;")] + (when (or (> delta-x 0) (> delta-y 0)) + (.setAttribute ^js dropdown "style" new-style)))))) [:& dropdown {:show (boolean mdata) :on-close (st/emitf dw/hide-context-menu)} diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 2910557c7..fcf7dc389 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -588,6 +588,10 @@ msgstr "Ok" msgid "ds.confirm-title" msgstr "Are you sure?" +#: src/app/main/ui/confirm.cljs +msgid "ds.component-subtitle" +msgstr "Components to update:" + #: src/app/main/ui/dashboard/grid.cljs msgid "ds.updated-at" msgstr "Updated: %s" @@ -1550,7 +1554,7 @@ msgstr "Remove “%s” as Shared Library" #: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs msgid "modals.update-remote-component.accept" -msgstr "Update component" +msgstr "Update" #: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs msgid "modals.update-remote-component.cancel" @@ -1562,10 +1566,20 @@ msgstr "" "You are about to update a component in a shared library. This may affect " "other files that use it." +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component-in-bulk.hint" +msgstr "" +"You are about to update components in a shared library. This may affect " +"other files that use it." + #: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs msgid "modals.update-remote-component.message" msgstr "Update a component in a shared library" +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component-in-bulk.message" +msgstr "Update components in a shared library" + #: src/app/main/ui/dashboard/team.cljs msgid "notifications.invitation-email-sent" msgstr "Invitation sent successfully" @@ -3115,6 +3129,10 @@ msgstr "Detach instance" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Detach instances" +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.update-components-in-bulk" +msgstr "Update main components" + msgid "workspace.shape.menu.difference" msgstr "Difference" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 60a97fc0f..cec3547e5 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -590,6 +590,10 @@ msgstr "Cancelar" msgid "ds.confirm-ok" msgstr "Ok" +#: src/app/main/ui/confirm.cljs +msgid "ds.component-subtitle" +msgstr "Componentes a actualizar:" + #: src/app/main/ui/confirm.cljs, src/app/main/ui/confirm.cljs msgid "ds.confirm-title" msgstr "¿Seguro?" @@ -1552,7 +1556,7 @@ msgstr "Añadir “%s” como Biblioteca Compartida" #: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs msgid "modals.update-remote-component.accept" -msgstr "Actualizar componente" +msgstr "Actualizar" #: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs msgid "modals.update-remote-component.cancel" @@ -1564,10 +1568,20 @@ msgstr "" "Vas a actualizar un componente en una librería compartida. Esto puede " "afectar a otros archivos que la usen." +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component-in-bulk.hint" +msgstr "" +"Vas a actualizar componentes en una librería compartida. Esto puede " +"afectar a otros archivos que la usen." + #: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs msgid "modals.update-remote-component.message" msgstr "Actualizar un componente en librería" +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "modals.update-remote-component-in-bulk.message" +msgstr "Actualizar componentes en librería" + #: src/app/main/ui/dashboard/team.cljs msgid "notifications.invitation-email-sent" msgstr "Invitación enviada con éxito" @@ -3128,6 +3142,10 @@ msgstr "Desacoplar instancia" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Desacoplar instancias" +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs +msgid "workspace.shape.menu.update-components-in-bulk" +msgstr "Actualizar componentes" + msgid "workspace.shape.menu.difference" msgstr "Diferencia"