From 6727717d1adbfaf96633d3ea0b98c1e4c73f6104 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 29 Dec 2021 14:35:32 +0100 Subject: [PATCH 1/5] :bug: Fix problem with join nodes --- CHANGES.md | 1 + frontend/src/app/util/path/tools.cljs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 1f3977252..bbb7ec334 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ - Fix problems when exporting all artboards [Taiga #2234](https://tree.taiga.io/project/penpot/issue/2234). - Fix problems with team management [#1353](https://github.com/penpot/penpot/issues/1353) - Fix problem when importing in shared libraries [#1362](https://github.com/penpot/penpot/issues/1362) +- Fix problem with join nodes [#1422](https://github.com/penpot/penpot/issues/1422) ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) diff --git a/frontend/src/app/util/path/tools.cljs b/frontend/src/app/util/path/tools.cljs index df1eb38f3..97a8a0ddd 100644 --- a/frontend/src/app/util/path/tools.cljs +++ b/frontend/src/app/util/path/tools.cljs @@ -304,7 +304,7 @@ [content points] (let [segments-set (into #{} - (juxt :start :end) + (map (juxt :start :end)) (get-segments content points)) create-line-command (fn [point other] From 4b490e3ca462175e0bf3c410aeba1ea7f016f615 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 29 Dec 2021 15:00:38 +0100 Subject: [PATCH 2/5] :bug: After team onboarding importing a file will import into the team drafts --- CHANGES.md | 1 + frontend/src/app/main/ui/onboarding/templates.cljs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bbb7ec334..3acf56bb6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ - Fix problems with team management [#1353](https://github.com/penpot/penpot/issues/1353) - Fix problem when importing in shared libraries [#1362](https://github.com/penpot/penpot/issues/1362) - Fix problem with join nodes [#1422](https://github.com/penpot/penpot/issues/1422) +- After team onboarding importing a file will import into the team drafts [Taiga #2408](https://tree.taiga.io/project/penpot/issue/2408) ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) diff --git a/frontend/src/app/main/ui/onboarding/templates.cljs b/frontend/src/app/main/ui/onboarding/templates.cljs index 91a886d34..de4a5b381 100644 --- a/frontend/src/app/main/ui/onboarding/templates.cljs +++ b/frontend/src/app/main/ui/onboarding/templates.cljs @@ -61,8 +61,9 @@ ::mf/register-as :onboarding-templates} ;; NOTE: the project usually comes empty, it only comes fullfilled ;; when a user creates a new team just after signup. - [{:keys [project-id] :as props}] - (let [close-fn (mf/use-callback #(st/emit! (modal/hide))) + [props] + (let [project-id (unchecked-get props "project-id") + close-fn (mf/use-callback #(st/emit! (modal/hide))) profile (mf/deref refs/profile) project-id (or project-id (:default-project-id profile))] [:div.modal-overlay From 4a55ee296534e0df23dcd53b5d27717339a54a38 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 29 Dec 2021 15:26:04 +0100 Subject: [PATCH 3/5] :bug: Fix problem exporting shapes from handoff mode --- CHANGES.md | 1 + .../styles/main/layouts/handoff.scss | 1 + .../app/main/ui/viewer/handoff/exports.cljs | 29 +++----------- .../sidebar/options/menus/exports.cljs | 38 +++++++++++-------- 4 files changed, 29 insertions(+), 40 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3acf56bb6..f6d9a2d36 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ - Fix problem when importing in shared libraries [#1362](https://github.com/penpot/penpot/issues/1362) - Fix problem with join nodes [#1422](https://github.com/penpot/penpot/issues/1422) - After team onboarding importing a file will import into the team drafts [Taiga #2408](https://tree.taiga.io/project/penpot/issue/2408) +- Fix problem exporting shapes from handoff mode [Taiga #2386](https://tree.taiga.io/project/penpot/issue/2386) ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) diff --git a/frontend/resources/styles/main/layouts/handoff.scss b/frontend/resources/styles/main/layouts/handoff.scss index 917410eca..00ae458bc 100644 --- a/frontend/resources/styles/main/layouts/handoff.scss +++ b/frontend/resources/styles/main/layouts/handoff.scss @@ -1,6 +1,7 @@ $width-settings-bar: 16rem; .handoff-layout { + height: 100vh; display: grid; grid-template-rows: 40px auto; grid-template-columns: 1fr; diff --git a/frontend/src/app/main/ui/viewer/handoff/exports.cljs b/frontend/src/app/main/ui/viewer/handoff/exports.cljs index bbdc65cec..930281d30 100644 --- a/frontend/src/app/main/ui/viewer/handoff/exports.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/exports.cljs @@ -7,36 +7,17 @@ (ns app.main.ui.viewer.handoff.exports (:require [app.common.data :as d] - [app.main.data.messages :as dm] - [app.main.store :as st] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.menus.exports :as we] [app.util.dom :as dom] [app.util.i18n :refer [tr]] - [beicon.core :as rx] [rumext.alpha :as mf])) (mf/defc exports [{:keys [shape page-id file-id] :as props}] (let [exports (mf/use-state (:exports shape [])) - loading? (mf/use-state false) - on-download - (mf/use-callback - (mf/deps shape @exports) - (fn [event] - (dom/prevent-default event) - (swap! loading? not) - (->> (we/request-export (assoc shape :page-id page-id :file-id file-id) @exports) - (rx/subs - (fn [{:keys [status body] :as response}] - (js/console.log status body) - (if (= status 200) - (dom/trigger-download (:name shape) body) - (st/emit! (dm/error (tr "errors.unexpected-error"))))) - (constantly nil) - (fn [] - (swap! loading? not)))))) + [on-download loading?] (we/use-download-export shape page-id file-id @exports) add-export (mf/use-callback @@ -118,10 +99,10 @@ i/minus]]) [:div.btn-icon-dark.download-button - {:on-click (when-not @loading? on-download) - :class (dom/classnames :btn-disabled @loading?) - :disabled @loading?} - (if @loading? + {:on-click (when-not loading? on-download) + :class (dom/classnames :btn-disabled loading?) + :disabled loading?} + (if loading? (tr "workspace.options.exporting-object") (tr "workspace.options.export-object"))]])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs index 0327912f4..8e9fc5a25 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs @@ -30,24 +30,18 @@ :name (:name shape) :exports exports})) -(mf/defc exports-menu - [{:keys [shape page-id file-id] :as props}] - (let [exports (:exports shape []) - loading? (mf/use-state false) +(defn use-download-export + [shape page-id file-id exports] + (let [loading? (mf/use-state false) filename (cond-> (:name shape) (and (= (count exports) 1) (not (empty (:suffix (first exports))))) (str (:suffix (first exports)))) - scale-enabled? + on-download-callback (mf/use-callback - (fn [export] - (#{:png :jpeg} (:type export)))) - - on-download - (mf/use-callback - (mf/deps shape) + (mf/deps filename shape exports) (fn [event] (dom/prevent-default event) (swap! loading? not) @@ -59,7 +53,19 @@ (swap! loading? not) (st/emit! (dm/error (tr "errors.unexpected-error")))) (fn [] - (swap! loading? not)))))) + (swap! loading? not))))))] + [on-download-callback @loading?])) + +(mf/defc exports-menu + [{:keys [shape page-id file-id] :as props}] + (let [exports (:exports shape []) + + scale-enabled? + (mf/use-callback + (fn [export] + (#{:png :jpeg} (:type export)))) + + [on-download loading?] (use-download-export shape page-id file-id exports) add-export (mf/use-callback @@ -143,11 +149,11 @@ i/minus]]) [:div.btn-icon-dark.download-button - {:on-click (when-not @loading? on-download) + {:on-click (when-not loading? on-download) :class (dom/classnames - :btn-disabled @loading?) - :disabled @loading?} - (if @loading? + :btn-disabled loading?) + :disabled loading?} + (if loading? (tr "workspace.options.exporting-object") (tr "workspace.options.export-object"))]])])) From aee35cb456492c72fd6c65d7ca69bf6d69b885ed Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 30 Dec 2021 16:09:48 +0100 Subject: [PATCH 4/5] :bug: Fix lock/hide elements in context menu when multiples shapes selected --- CHANGES.md | 1 + .../styles/main/partials/workspace.scss | 1 - frontend/src/app/main/data/workspace.cljs | 72 ++-- .../app/main/data/workspace/libraries.cljs | 14 + .../app/main/data/workspace/selection.cljs | 12 +- .../app/main/ui/workspace/context_menu.cljs | 380 ++++++++++-------- .../app/main/ui/workspace/sidebar/layers.cljs | 10 +- .../sidebar/options/menus/component.cljs | 46 +-- .../src/app/main/ui/workspace/viewport.cljs | 4 +- .../main/ui/workspace/viewport/actions.cljs | 10 + .../main/ui/workspace/viewport/selection.cljs | 25 +- 11 files changed, 317 insertions(+), 258 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f6d9a2d36..6018d2a6d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -18,6 +18,7 @@ - Fix problem with join nodes [#1422](https://github.com/penpot/penpot/issues/1422) - After team onboarding importing a file will import into the team drafts [Taiga #2408](https://tree.taiga.io/project/penpot/issue/2408) - Fix problem exporting shapes from handoff mode [Taiga #2386](https://tree.taiga.io/project/penpot/issue/2386) +- Fix lock/hide elements in context menu when multiples shapes selected [Taiga #2340](https://tree.taiga.io/project/penpot/issue/2340) ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index 40424d672..8b6ce74e3 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -18,7 +18,6 @@ top: 40px; width: 240px; z-index: 12; - padding: $size-1 0; li { align-items: center; diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 6c345448b..0ac199c3a 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1227,8 +1227,8 @@ ;; --- Update Shape Flags (defn update-shape-flags - [id {:keys [blocked hidden] :as flags}] - (s/assert ::us/uuid id) + [ids {:keys [blocked hidden] :as flags}] + (us/verify (s/coll-of ::us/uuid) ids) (s/assert ::shape-attrs flags) (ptk/reify ::update-shape-flags ptk/WatchEvent @@ -1238,9 +1238,8 @@ (cond-> obj (boolean? blocked) (assoc :blocked blocked) (boolean? hidden) (assoc :hidden hidden))) - objects (wsh/lookup-page-objects state) - ids (into [id] (cp/get-children id objects))] + ids (into ids (->> ids (mapcat #(cp/get-children % objects))))] (rx/of (dch/update-shapes ids update-fn)))))) (defn toggle-visibility-selected @@ -1349,16 +1348,16 @@ :typographies #{}})))) (defn go-to-component - [objs] + [component-id] (ptk/reify ::set-workspace-layout-component IDeref (-deref [_] {:layout :assets}) + ptk/WatchEvent (watch [_ state _] (let [project-id (get-in state [:workspace-project :id]) file-id (get-in state [:workspace-file :id]) page-id (get state :current-page-id) - component-id (get (first objs) :component-id) pparams {:file-id file-id :project-id project-id} qparams {:page-id page-id :layout :assets}] (rx/of (rt/nav :workspace pparams qparams) @@ -1367,8 +1366,7 @@ (select-single-asset component-id :components)))) ptk/EffectEvent (effect [_ _ _] - (let [component-id (get (first objs) :component-id) - wrapper-id (str "component-shape-id-" component-id)] + (let [wrapper-id (str "component-shape-id-" component-id)] (tm/schedule-on-idle #(dom/scroll-into-view-if-needed! (dom/get-element wrapper-id))))))) (def go-to-file @@ -1422,53 +1420,37 @@ (s/def ::point gpt/point?) (defn show-context-menu - [{:keys [position shape] :as params}] + [{:keys [position] :as params}] (us/verify ::point position) - (us/verify (s/nilable ::cp/minimal-shape) shape) (ptk/reify ::show-context-menu ptk/UpdateEvent (update [_ state] - (let [selected (wsh/lookup-selected state) - objects (wsh/lookup-page-objects state) - - selected-with-children - (into [] - (mapcat #(cp/get-object-with-children % objects)) - selected) - - head (get objects (first selected)) - - first-not-group-like? - (and (= (count selected) 1) - (not (contains? #{:group :bool} (:type head)))) - - has-invalid-shapes? (->> selected-with-children - (some (comp #{:frame :text} :type))) - - disable-booleans? (or (empty? selected) has-invalid-shapes? first-not-group-like?) - disable-flatten? (or (empty? selected) has-invalid-shapes?) - - mdata - (-> params - (assoc :disable-booleans? disable-booleans?) - (assoc :disable-flatten? disable-flatten?) - (cond-> (some? shape) - (assoc :selected selected)))] - - (assoc-in state [:workspace-local :context-menu] mdata))))) + (assoc-in state [:workspace-local :context-menu] params)))) (defn show-shape-context-menu - [{:keys [position shape] :as params}] - (us/verify ::point position) - (us/verify ::cp/minimal-shape shape) + [{:keys [shape] :as params}] + (us/verify (s/nilable ::cp/minimal-shape) shape) (ptk/reify ::show-shape-context-menu ptk/WatchEvent (watch [_ state _] - (let [selected (wsh/lookup-selected state)] + (let [selected (wsh/lookup-selected state) + objects (wsh/lookup-page-objects state) + all-selected (into [] (mapcat #(cp/get-object-with-children % objects)) selected) + head (get objects (first selected)) + + not-group-like? (and (= (count selected) 1) + (not (contains? #{:group :bool} (:type head)))) + no-bool-shapes? (->> all-selected (some (comp #{:frame :text} :type)))] + (rx/concat - (when-not (selected (:id shape)) - (rx/of (dws/select-shape (:id shape)))) - (rx/of (show-context-menu params))))))) + (when (and (some? shape) (not (contains? selected (:id shape)))) + (rx/of (dws/select-shape (:id shape)))) + (rx/of (show-context-menu + (-> params + (assoc + :disable-booleans? (or no-bool-shapes? not-group-like?) + :disable-flatten? no-bool-shapes? + :selected (conj selected (:id shape))))))))))) (def hide-context-menu (ptk/reify ::hide-context-menu diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 3458ae1d4..fa8132e04 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -635,6 +635,20 @@ :origin it :file-id file-id}))))))) +(defn update-component-sync + [shape-id file-id] + (ptk/reify ::update-component-sync + ptk/WatchEvent + (watch [_ state _] + (let [current-file-id (:current-file-id state)] + (rx/of + (dwu/start-undo-transaction) + (update-component shape-id) + (sync-file current-file-id file-id) + (when (not= current-file-id file-id) + (sync-file file-id file-id)) + (dwu/commit-undo-transaction)))))) + (declare sync-file-2nd-stage) (defn sync-file diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 890d576f7..5763b5e84 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -184,11 +184,13 @@ is-not-blocked (fn [shape-id] (not (get-in state [:workspace-data :pages-index page-id :objects shape-id - :blocked] false)))] - (rx/of (->> new-selected - (filter is-not-blocked) - (into lks/empty-linked-set) - (select-shapes))))))) + :blocked] false))) + + selected-ids (into lks/empty-linked-set + (comp (filter some?) + (filter is-not-blocked)) + new-selected)] + (rx/of (select-shapes selected-ids)))))) (defn deselect-all "Clear all possible state of drawing, edition diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 24b1f4751..05873156e 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -7,13 +7,13 @@ (ns app.main.ui.workspace.context-menu "A workspace specific context menu (mouse right click)." (:require + [app.common.data :as d] [app.common.types.page-options :as cto] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.interactions :as dwi] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.shortcuts :as sc] - [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] @@ -85,95 +85,12 @@ [] [:li.separator]) -(mf/defc shape-context-menu - [{:keys [mdata] :as props}] - (let [{:keys [shape selected disable-booleans? disable-flatten?]} mdata - {:keys [id type]} shape - - single? (= (count selected) 1) - multiple? (> (count selected) 1) - editable-shape? (#{:group :text :path} type) - - is-group? (and (some? shape) (= :group type)) - is-bool? (and (some? shape) (= :bool type)) - - options (mf/deref refs/workspace-page-options) - selected-objects (mf/deref refs/selected-objects) - flows (:flows options) - - has-group? (some #(= :group (:type %)) selected-objects) - has-bool? (some #(= :bool (:type %)) selected-objects) - has-mask-group? (some #(:masked-group? %) selected-objects) - - options-mode (mf/deref refs/options-mode) - - set-bool - (fn [bool-type] - #(cond - (> (count selected) 1) - (st/emit! (dw/create-bool bool-type)) - - (and (= (count selected) 1) is-group?) - (st/emit! (dw/group-to-bool (:id shape) bool-type)) - - (and (= (count selected) 1) is-bool?) - (st/emit! (dw/change-bool-type (:id shape) bool-type)))) - - current-file-id (mf/use-ctx ctx/current-file-id) - - do-duplicate (st/emitf (dw/duplicate-selected false)) - do-delete (st/emitf dw/delete-selected) - do-copy (st/emitf (dw/copy-selected)) - do-cut (st/emitf (dw/copy-selected) dw/delete-selected) - do-paste (st/emitf dw/paste) - do-bring-forward (st/emitf (dw/vertical-order-selected :up)) - do-bring-to-front (st/emitf (dw/vertical-order-selected :top)) - do-send-backward (st/emitf (dw/vertical-order-selected :down)) - do-send-to-back (st/emitf (dw/vertical-order-selected :bottom)) - do-show-shape (st/emitf (dw/update-shape-flags id {:hidden false})) - do-hide-shape (st/emitf (dw/update-shape-flags id {:hidden true})) - do-lock-shape (st/emitf (dw/update-shape-flags id {:blocked true})) - do-unlock-shape (st/emitf (dw/update-shape-flags id {:blocked false})) - do-add-flow (st/emitf (dwi/add-flow-selected-frame)) - do-remove-flow #(st/emitf (dwi/remove-flow (:id %))) - do-create-group (st/emitf dw/group-selected) - do-remove-group (st/emitf dw/ungroup-selected) - do-mask-group (st/emitf dw/mask-group) - do-unmask-group (st/emitf dw/unmask-group) - do-flip-vertical (st/emitf (dw/flip-vertical-selected)) - do-flip-horizontal (st/emitf (dw/flip-horizontal-selected)) - do-add-component (st/emitf (dwl/add-component)) - do-detach-component (st/emitf (dwl/detach-component id)) - do-reset-component (st/emitf (dwl/reset-component id)) - do-start-editing (fn [] - ;; We defer the execution so the mouse event won't close the editor - (timers/schedule #(st/emit! (dw/start-editing-selected)))) - do-update-component (st/emitf - (dwu/start-undo-transaction) - (dwl/update-component id) - (dwl/sync-file current-file-id (:component-file shape)) - (dwu/commit-undo-transaction)) - confirm-update-remote-component (st/emitf - (dwl/update-component id) - (dwl/sync-file current-file-id - (:component-file shape)) - (dwl/sync-file (:component-file shape) - (:component-file shape))) - do-update-remote-component (st/emitf (modal/show - {:type :confirm - :message "" - :title (tr "modals.update-remote-component.message") - :hint (tr "modals.update-remote-component.hint") - :cancel-label (tr "modals.update-remote-component.cancel") - :accept-label (tr "modals.update-remote-component.accept") - :accept-style :primary - :on-accept confirm-update-remote-component})) - do-show-component (st/emitf (dw/go-to-component selected-objects)) - do-navigate-component-file (st/emitf (dwl/nav-to-component-file - (:component-file shape))) - - do-transform-to-path (st/emitf (dw/convert-selected-to-path)) - do-flatten (st/emitf (dw/convert-selected-to-path))] +(mf/defc context-menu-edit + [] + (let [do-copy (st/emitf (dw/copy-selected)) + do-cut (st/emitf (dw/copy-selected) dw/delete-selected) + do-paste (st/emitf dw/paste) + do-duplicate (st/emitf (dw/duplicate-selected false))] [:* [:& menu-entry {:title (tr "workspace.shape.menu.copy") :shortcut (sc/get-tooltip :copy) @@ -187,7 +104,16 @@ [:& menu-entry {:title (tr "workspace.shape.menu.duplicate") :shortcut (sc/get-tooltip :duplicate) :on-click do-duplicate}] - [:& menu-separator] + + [:& menu-separator]])) + +(mf/defc context-menu-layer-position + [] + (let [do-bring-forward (st/emitf (dw/vertical-order-selected :up)) + do-bring-to-front (st/emitf (dw/vertical-order-selected :top)) + do-send-backward (st/emitf (dw/vertical-order-selected :down)) + do-send-to-back (st/emitf (dw/vertical-order-selected :bottom))] + [:* [:& menu-entry {:title (tr "workspace.shape.menu.forward") :shortcut (sc/get-tooltip :bring-forward) :on-click do-bring-forward}] @@ -200,49 +126,100 @@ [:& menu-entry {:title (tr "workspace.shape.menu.back") :shortcut (sc/get-tooltip :bring-back) :on-click do-send-to-back}] - [:& menu-separator] + + [:& menu-separator]])) + +(mf/defc context-menu-flip + [] + (let [do-flip-vertical (st/emitf (dw/flip-vertical-selected)) + do-flip-horizontal (st/emitf (dw/flip-horizontal-selected))] + [:* [:& menu-entry {:title (tr "workspace.shape.menu.flip-vertical") :shortcut (sc/get-tooltip :flip-vertical) :on-click do-flip-vertical}] + [:& menu-entry {:title (tr "workspace.shape.menu.flip-horizontal") :shortcut (sc/get-tooltip :flip-horizontal) :on-click do-flip-horizontal}] - [:& menu-separator] + [:& menu-separator]])) - (when multiple? - [:* - [:& menu-entry {:title (tr "workspace.shape.menu.group") - :shortcut (sc/get-tooltip :group) - :on-click do-create-group}] - [:& menu-entry {:title (tr "workspace.shape.menu.mask") - :shortcut (sc/get-tooltip :mask) - :on-click do-mask-group}]]) +(mf/defc context-menu-group + [{:keys [shapes]}] - (when (and single? (and (not has-mask-group?) (or has-bool? has-group?))) + (let [multiple? (> (count shapes) 1) + single? (= (count shapes) 1) + + has-group? (->> shapes (d/seek #(= :group (:type %)))) + has-bool? (->> shapes (d/seek #(= :bool (:type %)))) + has-mask? (->> shapes (d/seek :masked-group?)) + has-frame? (->> shapes (d/seek #(= :frame (:type %)))) + + is-group? (and single? has-group?) + is-bool? (and single? has-bool?) + + do-create-group (st/emitf dw/group-selected) + do-mask-group (st/emitf dw/mask-group) + do-remove-group (st/emitf dw/ungroup-selected) + do-unmask-group (st/emitf dw/unmask-group)] + + [:* + (when (or has-bool? has-group? has-mask?) + [:& menu-entry {:title (tr "workspace.shape.menu.ungroup") + :shortcut (sc/get-tooltip :ungroup) + :on-click do-remove-group}]) + + (when (not has-frame?) + [:& menu-entry {:title (tr "workspace.shape.menu.group") + :shortcut (sc/get-tooltip :group) + :on-click do-create-group}]) + + (when (or multiple? (and is-group? (not has-mask?)) is-bool?) [:& menu-entry {:title (tr "workspace.shape.menu.mask") :shortcut (sc/get-tooltip :mask) :on-click do-mask-group}]) - (when (or has-bool? has-group?) - [:& menu-entry {:title (tr "workspace.shape.menu.ungroup") - :shortcut (sc/get-tooltip :ungroup) - :on-click do-remove-group}]) - - (when has-mask-group? - [:& menu-entry {:title (tr "workspace.shape.menu.unmask") - :shortcut (sc/get-tooltip :unmask) - :on-click do-unmask-group}] - ) + (when has-mask? + [:& menu-entry {:title (tr "workspace.shape.menu.unmask") + :shortcut (sc/get-tooltip :unmask) + :on-click do-unmask-group}]) - (when (or multiple? has-mask-group? (or is-bool? has-group?) (and single? (or has-bool? has-group?)) ) - [:& menu-separator]) + (when (not has-frame?) + [:& menu-separator])])) - (when (and single? editable-shape?) +(mf/defc context-menu-path + [{:keys [shapes disable-flatten? disable-booleans?]}] + (let [multiple? (> (count shapes) 1) + single? (= (count shapes) 1) + + has-group? (->> shapes (d/seek #(= :group (:type %)))) + has-bool? (->> shapes (d/seek #(= :bool (:type %)))) + has-frame? (->> shapes (d/seek #(= :frame (:type %)))) + + is-group? (and single? has-group?) + is-bool? (and single? has-bool?) + is-frame? (and single? has-frame?) + + do-start-editing #(timers/schedule (st/emitf (dw/start-editing-selected))) + do-transform-to-path (st/emitf (dw/convert-selected-to-path)) + + make-do-bool + (fn [bool-type] + #(cond + multiple? + (st/emit! (dw/create-bool bool-type)) + + is-group? + (st/emit! (dw/group-to-bool (-> shapes first :id) bool-type)) + + is-bool? + (st/emit! (dw/change-bool-type (-> shapes first :id) bool-type))))] + [:* + (when (and single? (not is-frame?)) [:& menu-entry {:title (tr "workspace.shape.menu.edit") :shortcut (sc/get-tooltip :start-editing) :on-click do-start-editing}]) - (when-not disable-flatten? + (when-not (or disable-flatten? has-frame?) [:& menu-entry {:title (tr "workspace.shape.menu.transform-to-path") :on-click do-transform-to-path}]) @@ -251,84 +228,159 @@ [:& menu-entry {:title (tr "workspace.shape.menu.path")} [:& menu-entry {:title (tr "workspace.shape.menu.union") :shortcut (sc/get-tooltip :bool-union) - :on-click (set-bool :union)}] + :on-click (make-do-bool :union)}] [:& menu-entry {:title (tr "workspace.shape.menu.difference") :shortcut (sc/get-tooltip :bool-difference) - :on-click (set-bool :difference)}] + :on-click (make-do-bool :difference)}] [:& menu-entry {:title (tr "workspace.shape.menu.intersection") :shortcut (sc/get-tooltip :bool-intersection) - :on-click (set-bool :intersection)}] + :on-click (make-do-bool :intersection)}] [:& menu-entry {:title (tr "workspace.shape.menu.exclude") :shortcut (sc/get-tooltip :bool-exclude) - :on-click (set-bool :exclude)}] + :on-click (make-do-bool :exclude)}] (when (and single? is-bool? (not disable-flatten?)) [:* [:& menu-separator] [:& menu-entry {:title (tr "workspace.shape.menu.flatten") - :on-click do-flatten}]])]) + :on-click do-transform-to-path}]])])])) - (if (:hidden shape) +(mf/defc context-menu-layer-options + [{:keys [shapes]}] + (let [ids (mapv :id shapes) + do-show-shape (st/emitf (dw/update-shape-flags ids {:hidden false})) + do-hide-shape (st/emitf (dw/update-shape-flags ids {:hidden true})) + do-lock-shape (st/emitf (dw/update-shape-flags ids {:blocked true})) + do-unlock-shape (st/emitf (dw/update-shape-flags ids {:blocked false}))] + [:* + (if (every? :hidden shapes) [:& menu-entry {:title (tr "workspace.shape.menu.show") :on-click do-show-shape}] [:& menu-entry {:title (tr "workspace.shape.menu.hide") :on-click do-hide-shape}]) - (if (:blocked shape) + (if (every? :blocked shapes) [:& menu-entry {:title (tr "workspace.shape.menu.unlock") :on-click do-unlock-shape}] [:& menu-entry {:title (tr "workspace.shape.menu.lock") - :on-click do-lock-shape}]) + :on-click do-lock-shape}])])) - (when (and (= options-mode :prototype) (= (:type shape) :frame)) - (let [flow (cto/get-frame-flow flows (:id shape))] - (if (nil? flow) - [:& menu-entry {:title (tr "workspace.shape.menu.flow-start") - :on-click do-add-flow}] - [:& menu-entry {:title (tr "workspace.shape.menu.delete-flow-start") - :on-click (do-remove-flow flow)}]))) +(mf/defc context-menu-prototype + [{:keys [shapes]}] + (let [options (mf/deref refs/workspace-page-options) + options-mode (mf/deref refs/options-mode) + do-add-flow (st/emitf (dwi/add-flow-selected-frame)) + do-remove-flow #(st/emitf (dwi/remove-flow (:id %))) + flows (:flows options) - (when (and (not= (:type shape) :frame) - (or multiple? (nil? (:component-id shape)))) + prototype? (= options-mode :prototype) + single? (= (count shapes) 1) + has-frame? (->> shapes (d/seek #(= :frame (:type %)))) + is-frame? (and single? has-frame?)] + + (when (and prototype? is-frame?) + (let [flow (cto/get-frame-flow flows (-> shapes first :id))] + (if (some? flow) + [:& menu-entry {:title (tr "workspace.shape.menu.delete-flow-start") + :on-click (do-remove-flow flow)}] + + [:& menu-entry {:title (tr "workspace.shape.menu.flow-start") + :on-click do-add-flow}]))))) + +(mf/defc context-menu-component + [{:keys [shapes]}] + (let [single? (= (count shapes) 1) + + has-frame? (->> shapes (d/seek #(= :frame (:type %)))) + is-component? (and single? (-> shapes first :component-id some?)) + + shape-id (->> shapes first :id) + component-id (->> shapes first :component-id) + component-file (-> shapes first :component-file) + + current-file-id (mf/use-ctx ctx/current-file-id) + local-component? (= component-file current-file-id) + + do-add-component (st/emitf (dwl/add-component)) + do-detach-component (st/emitf (dwl/detach-component shape-id)) + do-reset-component (st/emitf (dwl/reset-component shape-id)) + 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-remote-component + (st/emitf (modal/show + {:type :confirm + :message "" + :title (tr "modals.update-remote-component.message") + :hint (tr "modals.update-remote-component.hint") + :cancel-label (tr "modals.update-remote-component.cancel") + :accept-label (tr "modals.update-remote-component.accept") + :accept-style :primary + :on-accept do-update-component}))] + [:* + (when (and (not has-frame?) (not is-component?)) [:* [:& menu-separator] [:& menu-entry {:title (tr "workspace.shape.menu.create-component") :shortcut (sc/get-tooltip :create-component) :on-click do-add-component}]]) - (when (and (:component-id shape) - (= (count selected) 1)) + (when is-component? ;; WARNING: this menu is the same as the context menu at the sidebar. ;; If you change it, you must change equally the file - ;; app/main/ui/workspace/sidebar/options/component.cljs - (if (= (:component-file shape) current-file-id) - [:* - [:& menu-separator] - [:& menu-entry {:title (tr "workspace.shape.menu.detach-instance") - :shortcut (sc/get-tooltip :detach-component) - :on-click do-detach-component}] - [:& menu-entry {:title (tr "workspace.shape.menu.reset-overrides") - :on-click do-reset-component}] - [:& menu-entry {:title (tr "workspace.shape.menu.update-main") - :on-click do-update-component}] - [:& menu-entry {:title (tr "workspace.shape.menu.show-main") - :on-click do-show-component}]] - [:* - [:& menu-separator] - [:& menu-entry {:title (tr "workspace.shape.menu.detach-instance") - :shortcut (sc/get-tooltip :detach-component) - :on-click do-detach-component}] - [:& menu-entry {:title (tr "workspace.shape.menu.reset-overrides") - :on-click do-reset-component}] - [:& menu-entry {:title (tr "workspace.shape.menu.go-main") - :on-click do-navigate-component-file}] - [:& menu-entry {:title (tr "workspace.shape.menu.update-main") - :on-click do-update-remote-component}]])) + ;; app/main/ui/workspace/sidebar/options/menus/component.cljs - [:& menu-separator] - [:& menu-entry {:title (tr "workspace.shape.menu.delete") - :shortcut (sc/get-tooltip :delete) - :on-click do-delete}]])) + [:* + [:& menu-separator] + [:& menu-entry {:title (tr "workspace.shape.menu.detach-instance") + :shortcut (sc/get-tooltip :detach-component) + :on-click do-detach-component}] + [:& menu-entry {:title (tr "workspace.shape.menu.reset-overrides") + :on-click do-reset-component}] + + + (if local-component? + [:* + [:& menu-entry {:title (tr "workspace.shape.menu.update-main") + :on-click do-update-component}] + [:& menu-entry {:title (tr "workspace.shape.menu.show-main") + :on-click do-show-component}]] + + [:* + [:& menu-entry {:title (tr "workspace.shape.menu.go-main") + :on-click do-navigate-component-file}] + [:& menu-entry {:title (tr "workspace.shape.menu.update-main") + :on-click do-update-remote-component}]])]) + + [:& menu-separator]])) + +(mf/defc context-menu-delete + [] + (let [do-delete (st/emitf dw/delete-selected)] + [:& menu-entry {:title (tr "workspace.shape.menu.delete") + :shortcut (sc/get-tooltip :delete) + :on-click do-delete}])) + +(mf/defc shape-context-menu + [{:keys [mdata] :as props}] + (let [{:keys [disable-booleans? disable-flatten?]} mdata + shapes (mf/deref refs/selected-objects) + + props #js {:shapes shapes + :disable-booleans? disable-booleans? + :disable-flatten? disable-flatten?}] + (when-not (empty? shapes) + [:* + [:> context-menu-edit props] + [:> context-menu-layer-position props] + [:> context-menu-flip props] + [:> context-menu-group props] + [:> context-menu-path props] + [:> context-menu-layer-options props] + [:> context-menu-prototype props] + [:> context-menu-component props] + [:> context-menu-delete props]]))) (mf/defc viewport-context-menu [] @@ -364,7 +416,7 @@ :style {:top top :left left} :on-context-menu prevent-default} - (if (:shape mdata) + (if (contains? mdata :selected) [:& shape-context-menu {:mdata mdata}] [:& viewport-context-menu {:mdata mdata}])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index e18c0ae06..824cd6138 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -139,16 +139,16 @@ (fn [event] (dom/stop-propagation event) (if (:blocked item) - (st/emit! (dw/update-shape-flags id {:blocked false})) - (st/emit! (dw/update-shape-flags id {:blocked true}) + (st/emit! (dw/update-shape-flags [id] {:blocked false})) + (st/emit! (dw/update-shape-flags [id] {:blocked true}) (dw/deselect-shape id)))) toggle-visibility (fn [event] (dom/stop-propagation event) (if (:hidden item) - (st/emit! (dw/update-shape-flags id {:hidden false})) - (st/emit! (dw/update-shape-flags id {:hidden true})))) + (st/emit! (dw/update-shape-flags [id] {:hidden false})) + (st/emit! (dw/update-shape-flags [id] {:hidden true})))) select-shape (fn [event] @@ -275,7 +275,7 @@ [:ul.element-list [:& hooks/sortable-container {} (for [[index id] (reverse (d/enumerate (:shapes root)))] - (let [obj (get objects id)] + (when-let [obj (get objects id)] (if (= (:type obj) :frame) [:& frame-wrapper {:item obj 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 024c391d1..8710ff7d5 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 @@ -10,7 +10,6 @@ [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.libraries :as dwl] - [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.context-menu :refer [context-menu]] @@ -33,10 +32,9 @@ show? (some? (:component-id values)) local-library (mf/deref refs/workspace-local-library) libraries (mf/deref refs/workspace-libraries) - component (cp/get-component (:component-id values) - (:component-file values) - local-library - libraries) + {:keys [component-id component-file]} values + + component (cp/get-component component-id component-file local-library libraries) on-menu-click (mf/use-callback (fn [event] @@ -49,29 +47,21 @@ do-detach-component (st/emitf (dwl/detach-component id)) do-reset-component (st/emitf (dwl/reset-component id)) - do-update-component (st/emitf - (dwu/start-undo-transaction) - (dwl/update-component id) - (dwl/sync-file current-file-id current-file-id) - (dwu/commit-undo-transaction)) - confirm-update-remote-component (st/emitf - (dwl/update-component id) - (dwl/sync-file current-file-id - (:component-file values)) - (dwl/sync-file (:component-file values) - (:component-file values))) - do-update-remote-component (st/emitf (modal/show - {:type :confirm - :message "" - :title (t locale "modals.update-remote-component.message") - :hint (t locale "modals.update-remote-component.hint") - :cancel-label (t locale "modals.update-remote-component.cancel") - :accept-label (t locale "modals.update-remote-component.accept") - :accept-style :primary - :on-accept confirm-update-remote-component})) - do-show-component (st/emitf (dw/go-to-layout :assets)) - do-navigate-component-file (st/emitf (dwl/nav-to-component-file - (:component-file values)))] + do-update-component (st/emitf (dwl/update-component-sync id component-file)) + + do-update-remote-component + (st/emitf (modal/show + {:type :confirm + :message "" + :title (t locale "modals.update-remote-component.message") + :hint (t locale "modals.update-remote-component.hint") + :cancel-label (t locale "modals.update-remote-component.cancel") + :accept-label (t locale "modals.update-remote-component.accept") + :accept-style :primary + :on-accept do-update-component})) + + do-show-component (st/emitf (dw/go-to-component component-id)) + do-navigate-component-file (st/emitf (dwl/nav-to-component-file component-file))] (when show? [:div.element-set [:div.element-set-title diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 51c288e33..0da6e0fa0 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -119,6 +119,7 @@ on-pointer-move (actions/on-pointer-move viewport-ref zoom move-stream) on-pointer-up (actions/on-pointer-up) on-move-selected (actions/on-move-selected hover hover-ids selected) + on-menu-selected (actions/on-menu-selected hover hover-ids selected) on-frame-enter (actions/on-frame-enter frame-hover) on-frame-leave (actions/on-frame-leave frame-hover) @@ -241,7 +242,8 @@ :zoom zoom :edition edition :disable-handlers (or drawing-tool edition) - :on-move-selected on-move-selected}]) + :on-move-selected on-move-selected + :on-context-menu on-menu-selected}]) (when show-measures? [:& msr/measurement diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 714b537a7..9f1fc6b52 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -203,6 +203,16 @@ :shape @hover}) (dw/show-context-menu {:position position}))))))))) +(defn on-menu-selected + [hover hover-ids selected] + (mf/use-callback + (mf/deps @hover hover-ids selected) + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (let [position (dom/get-client-position event)] + (st/emit! (dw/show-shape-context-menu {:position position})))))) + (defn on-mouse-up [disable-paste] (mf/use-callback diff --git a/frontend/src/app/main/ui/workspace/viewport/selection.cljs b/frontend/src/app/main/ui/workspace/viewport/selection.cljs index cf9ed2d8c..0b60675a7 100644 --- a/frontend/src/app/main/ui/workspace/viewport/selection.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/selection.cljs @@ -33,7 +33,7 @@ (def min-selrect-side 10) (def small-selrect-side 30) -(mf/defc selection-rect [{:keys [transform rect zoom color on-move-selected]}] +(mf/defc selection-rect [{:keys [transform rect zoom color on-move-selected on-context-menu]}] (when rect (let [{:keys [x y width height]} rect] [:rect.main.viewport-selrect @@ -43,6 +43,7 @@ :height height :transform transform :on-mouse-down on-move-selected + :on-context-menu on-context-menu :style {:stroke color :stroke-width (/ selection-rect-width zoom) :fill "none"}}]))) @@ -223,6 +224,7 @@ zoom (obj/get props "zoom") color (obj/get props "color") on-move-selected (obj/get props "on-move-selected") + on-context-menu (obj/get props "on-context-menu") on-resize (obj/get props "on-resize") on-rotate (obj/get props "on-rotate") disable-handlers (obj/get props "disable-handlers") @@ -244,7 +246,8 @@ :transform transform :zoom zoom :color color - :on-move-selected on-move-selected}] + :on-move-selected on-move-selected + :on-context-menu on-context-menu}] ;; Handlers (for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)] @@ -281,7 +284,7 @@ :fill "none"}}]])) (mf/defc multiple-selection-handlers - [{:keys [shapes selected zoom color disable-handlers on-move-selected] :as props}] + [{:keys [shapes selected zoom color disable-handlers on-move-selected on-context-menu] :as props}] (let [shape (mf/use-memo (mf/deps shapes) #(->> shapes @@ -310,13 +313,14 @@ :disable-handlers disable-handlers :on-move-selected on-move-selected :on-resize on-resize - :on-rotate on-rotate}] + :on-rotate on-rotate + :on-context-menu on-context-menu}] (when (debug? :selection-center) [:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])])) (mf/defc single-selection-handlers - [{:keys [shape zoom color disable-handlers on-move-selected] :as props}] + [{:keys [shape zoom color disable-handlers on-move-selected on-context-menu] :as props}] (let [shape-id (:id shape) shape (geom/transform-shape shape {:round-coords? false}) @@ -342,11 +346,12 @@ :on-rotate on-rotate :on-resize on-resize :disable-handlers disable-handlers - :on-move-selected on-move-selected}])) + :on-move-selected on-move-selected + :on-context-menu on-context-menu}])) (mf/defc selection-handlers {::mf/wrap [mf/memo]} - [{:keys [shapes selected edition zoom disable-handlers on-move-selected] :as props}] + [{:keys [shapes selected edition zoom disable-handlers on-move-selected on-context-menu] :as props}] (let [num (count shapes) {:keys [type] :as shape} (first shapes) @@ -363,7 +368,8 @@ :zoom zoom :color color :disable-handlers disable-handlers - :on-move-selected on-move-selected}] + :on-move-selected on-move-selected + :on-context-menu on-context-menu}] (and (= type :text) (= edition (:id shape))) @@ -380,4 +386,5 @@ :zoom zoom :color color :disable-handlers disable-handlers - :on-move-selected on-move-selected}]))) + :on-move-selected on-move-selected + :on-context-menu on-context-menu}]))) From 3b5aaf21fa3b66aec50b71c14c0f1619d9a68800 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 3 Jan 2022 10:00:17 +0100 Subject: [PATCH 5/5] :bug: Fix problem when resizing texts --- frontend/src/app/main/ui/workspace/viewport/utils.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index 074be0bb2..67621fa36 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -72,7 +72,7 @@ [shape-node]))) (defn update-transform [shapes transforms modifiers] - (doseq [{id :id :as shape} shapes] + (doseq [{:keys [id type] :as shape} shapes] (when-let [nodes (get-nodes shape)] (let [transform (get transforms id) modifiers (get-in modifiers [id :modifiers])