From 6d40166de7ba9b0e1ec96350688df30f14b2cb18 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 25 Feb 2025 10:11:14 +0100 Subject: [PATCH] :sparkles: Remove menu options and shortcuts actions over variants --- .../src/app/main/data/workspace/bool.cljs | 2 + .../src/app/main/data/workspace/groups.cljs | 4 +- .../app/main/data/workspace/shape_layout.cljs | 65 +++++++----- .../src/app/main/data/workspace/shapes.cljs | 6 +- .../app/main/data/workspace/transforms.cljs | 10 +- .../ui/ds/controls/input_with_values.cljs | 30 +++--- .../main/ui/ds/controls/input_with_values.mdx | 2 +- .../app/main/ui/workspace/context_menu.cljs | 41 ++++--- .../sidebar/options/menus/component.cljs | 63 ++++++----- .../sidebar/options/shapes/frame.cljs | 100 +++++++++--------- 10 files changed, 185 insertions(+), 138 deletions(-) diff --git a/frontend/src/app/main/data/workspace/bool.cljs b/frontend/src/app/main/data/workspace/bool.cljs index 55049b512b..e5228cc506 100644 --- a/frontend/src/app/main/data/workspace/bool.cljs +++ b/frontend/src/app/main/data/workspace/bool.cljs @@ -11,6 +11,7 @@ [app.common.files.helpers :as cph] [app.common.geom.shapes :as gsh] [app.common.svg.path.shapes-to-path :as stp] + [app.common.types.component :as ctc] [app.common.types.container :as ctn] [app.common.types.shape :as cts] [app.common.types.shape.layout :as ctl] @@ -99,6 +100,7 @@ shapes (->> ordered-indexes (map (d/getf objects)) (remove cph/frame-shape?) + (remove ctc/is-variant?) (remove #(ctn/has-any-copy-parent? objects %)))] (when-not (empty? shapes) diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index 6d7d18f1ce..2e1c1c0ff0 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -188,7 +188,9 @@ (->> ids (cfh/clean-loops objects) (remove #(ctn/has-any-copy-parent? objects (get objects %))) - (shapes-for-grouping objects)) + (shapes-for-grouping objects) + (remove ctk/is-variant?)) + parents (into #{} (map :parent-id) shapes)] (when-not (empty? shapes) (let [[group changes] diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 9852c74688..7e5d4b3afb 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -172,47 +172,55 @@ is-mask? (and single? has-mask?) has-component? (some true? (map ctc/instance-root? selected-shapes)) is-component? (and single? has-component?) + has-variant? (some ctc/is-variant? selected-shapes) new-shape-id (uuid/next) undo-id (js/Symbol)] - (rx/concat - (rx/of (dwu/start-undo-transaction undo-id)) - (if (and is-group? (not is-component?) (not is-mask?)) - ;; Create layout from a group: - ;; When creating a layout from a group we remove the group and create the layout with its children - (let [parent-id (:parent-id (first selected-shapes)) - shapes-ids (:shapes (first selected-shapes)) - ordered-ids (into (d/ordered-set) shapes-ids) - group-index (cfh/get-index-replacement selected objects)] + (if has-variant? + (rx/empty) + (rx/concat + (rx/of (dwu/start-undo-transaction undo-id)) + (if (and is-group? (not is-component?) (not is-mask?)) + ;; Create layout from a group: + ;; When creating a layout from a group we remove the group and create the layout with its children + (let [parent-id (:parent-id (first selected-shapes)) + shapes-ids (:shapes (first selected-shapes)) + ordered-ids (into (d/ordered-set) shapes-ids) + group-index (cfh/get-index-replacement selected objects)] + (rx/of + (dwse/select-shapes ordered-ids) + (dwsh/create-artboard-from-selection new-shape-id parent-id group-index (:name (first selected-shapes))) + (cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1}) + (create-layout-from-id new-shape-id type) + (dwsh/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) + (dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)) + (dwsh/delete-shapes page-id selected) + (ptk/data-event :layout/update {:ids [new-shape-id]}) + (dwu/commit-undo-transaction undo-id))) + + ;; Create Layout from selection (rx/of - (dwse/select-shapes ordered-ids) - (dwsh/create-artboard-from-selection new-shape-id parent-id group-index (:name (first selected-shapes))) + (dwsh/create-artboard-from-selection new-shape-id) (cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1}) (create-layout-from-id new-shape-id type) (dwsh/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) - (dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)) - (dwsh/delete-shapes page-id selected) - (ptk/data-event :layout/update {:ids [new-shape-id]}) - (dwu/commit-undo-transaction undo-id))) + (dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)))) - ;; Create Layout from selection - (rx/of - (dwsh/create-artboard-from-selection new-shape-id) - (cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1}) - (create-layout-from-id new-shape-id type) - (dwsh/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) - (dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)))) - - (rx/of (ptk/data-event :layout/update {:ids [new-shape-id]}) - (dwu/commit-undo-transaction undo-id))))))) + (rx/of (ptk/data-event :layout/update {:ids [new-shape-id]}) + (dwu/commit-undo-transaction undo-id)))))))) (defn remove-layout [ids] (ptk/reify ::remove-shape-layout ptk/WatchEvent - (watch [_ _ _] - (let [undo-id (js/Symbol)] + (watch [_ state _] + (let [objects (dsh/lookup-page-objects state) + ids (->> ids + (remove #(->> % + (get objects) + (ctc/is-variant?)))) + undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dwsh/update-shapes ids #(apply dissoc % layout-keys)) @@ -234,11 +242,12 @@ selected-shapes (map (d/getf objects) selected) single? (= (count selected-shapes) 1) is-frame? (= :frame (:type (first selected-shapes))) + is-variant-cont? (ctc/is-variant-container? (first selected-shapes)) undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (if (and single? is-frame?) + (if (and single? is-frame? (not is-variant-cont?)) (create-layout-from-id (first selected) type :from-frame? true) (create-layout-from-selection type)) (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index f421e6dc55..e384bf1903 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -13,6 +13,7 @@ [app.common.files.shapes-helpers :as cfsh] [app.common.logic.shapes :as cls] [app.common.schema :as sm] + [app.common.types.component :as ctc] [app.common.types.container :as ctn] [app.common.types.shape :as cts] [app.common.types.shape-tree :as ctst] @@ -248,7 +249,10 @@ objects (dsh/lookup-page-objects state page-id) selected (->> (dsh/lookup-selected state) (cfh/clean-loops objects) - (remove #(ctn/has-any-copy-parent? objects (get objects %)))) + (remove #(ctn/has-any-copy-parent? objects (get objects %))) + (remove #(->> % + (get objects) + (ctc/is-variant?)))) changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects)) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index ea8c7c1103..caf83183fd 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -968,7 +968,10 @@ (watch [_ state _] (let [objects (dsh/lookup-page-objects state) selected (or ids (dsh/lookup-selected state {:omit-blocked? true})) - shapes (map #(get objects %) selected) + shapes (->> selected + (map (d/getf objects)) + (remove ctk/is-variant-container?)) + selected (->> shapes (map :id)) selrect (gsh/shapes->rect shapes) center (grc/rect->center selrect) modifiers (dwm/create-modif-tree selected (ctm/resize-modifiers (gpt/point -1.0 1.0) center))] @@ -983,7 +986,10 @@ (watch [_ state _] (let [objects (dsh/lookup-page-objects state) selected (or ids (dsh/lookup-selected state {:omit-blocked? true})) - shapes (map #(get objects %) selected) + shapes (->> selected + (map #(get objects %)) + (remove ctk/is-variant-container?)) + selected (->> shapes (map :id)) selrect (gsh/shapes->rect shapes) center (grc/rect->center selrect) modifiers (dwm/create-modif-tree selected (ctm/resize-modifiers (gpt/point 1.0 -1.0) center))] diff --git a/frontend/src/app/main/ui/ds/controls/input_with_values.cljs b/frontend/src/app/main/ui/ds/controls/input_with_values.cljs index 42aa8137f7..4f62df9cfb 100644 --- a/frontend/src/app/main/ui/ds/controls/input_with_values.cljs +++ b/frontend/src/app/main/ui/ds/controls/input_with_values.cljs @@ -23,7 +23,7 @@ (mf/defc input-with-values* {::mf/props :obj ::mf/schema schema:input-with-values} - [{:keys [name values on-blur]}] + [{:keys [name values on-blur] :rest props}] (let [editing* (mf/use-state false) editing? (deref editing*) input-ref (mf/use-ref) @@ -39,11 +39,10 @@ (mf/use-fn (mf/deps on-blur) (fn [event] - (let [new-name (dom/get-target-val event)] - (dom/stop-propagation event) - (reset! editing* false) - (when on-blur - (on-blur new-name))))) + (dom/stop-propagation event) + (reset! editing* false) + (when on-blur + (on-blur event)))) on-focus (mf/use-fn (fn [event] @@ -57,18 +56,19 @@ esc? (kbd/esc? event) node (dom/get-target event)] (when ^boolean enter? (dom/blur! node)) - (when ^boolean esc? (dom/blur! node)))))] + (when ^boolean esc? (dom/blur! node))))) + + props (mf/spread-props props {:ref input-ref + :class (stl/css :input-with-values-editing) + :default-value name + :auto-focus true + :on-focus on-focus + :on-blur on-stop-edit + :on-key-down handle-key-down})] (if editing? [:div {:class (stl/css :input-with-values-edit-container)} - [:> input* - {:ref input-ref - :class (stl/css :input-with-values-editing) - :default-value name - :auto-focus true - :on-focus on-focus - :on-blur on-stop-edit - :on-key-down handle-key-down}]] + [:> input* props]] [:div {:class (stl/css :input-with-values-container :input-with-values-grid) :title title :on-click on-edit} [:span {:class (stl/css :input-with-values-name)} name] diff --git a/frontend/src/app/main/ui/ds/controls/input_with_values.mdx b/frontend/src/app/main/ui/ds/controls/input_with_values.mdx index 7374e3827a..2c6261be09 100644 --- a/frontend/src/app/main/ui/ds/controls/input_with_values.mdx +++ b/frontend/src/app/main/ui/ds/controls/input_with_values.mdx @@ -13,7 +13,7 @@ The `input-with-values*` acts as an input with an addition of a series of values ## Technical notes * You need to pass the mandatory string properties `name` and `values` -* You can pass a function property `on-blur` that will be called with the new value when the component lost focus (including when the user press enter or esc) +* You can pass a function property `on-blur` that will be called with the blur event when the component lost focus (including when the user press enter or esc) ```clj [:> input-with-values* diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 81d63dd328..df34e8406e 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -317,11 +317,12 @@ {::mf/props :obj ::mf/private true} [{:keys [shapes]}] - (let [multiple? (> (count shapes) 1) - single? (= (count shapes) 1) + (let [multiple? (> (count shapes) 1) + single? (= (count shapes) 1) - objects (deref refs/workspace-page-objects) - any-in-copy? (some true? (map #(ctn/has-any-copy-parent? objects %) shapes)) + objects (deref refs/workspace-page-objects) + any-in-copy? (some #(ctn/has-any-copy-parent? objects %) shapes) + any-is-variant? (some ctk/is-variant? shapes) ;; components can't be ungrouped has-frame? (->> shapes (d/seek #(and (cfh/frame-shape? %) (not (ctk/instance-head? %)) (not (ctk/is-variant-container? %))))) @@ -340,7 +341,7 @@ #(st/emit! (dwsh/create-artboard-from-selection))] [:* - (when (not any-in-copy?) + (when (not (or any-in-copy? any-is-variant?)) [:* (when (or has-bool? has-group? has-mask? has-frame?) [:> menu-entry* {:title (tr "workspace.shape.menu.ungroup") @@ -503,6 +504,8 @@ has-grid? (and single? (every? ctl/grid-layout? shapes)) + any-is-variant? (some ctk/is-variant? shapes) + on-add-layout (mf/use-fn (fn [event] @@ -532,16 +535,17 @@ :shortcut (sc/get-tooltip :toggle-layout-grid) :on-click on-remove-layout}])] - [:div - [:> menu-separator* {}] - [:> menu-entry* {:title (tr "workspace.shape.menu.add-flex") - :shortcut (sc/get-tooltip :toggle-layout-flex) - :value "flex" - :on-click on-add-layout}] - [:> menu-entry* {:title (tr "workspace.shape.menu.add-grid") - :shortcut (sc/get-tooltip :toggle-layout-grid) - :value "grid" - :on-click on-add-layout}]]))])) + (when (or single? (not any-is-variant?)) + [:div + [:> menu-separator* {}] + [:> menu-entry* {:title (tr "workspace.shape.menu.add-flex") + :shortcut (sc/get-tooltip :toggle-layout-flex) + :value "flex" + :on-click on-add-layout}] + [:> menu-entry* {:title (tr "workspace.shape.menu.add-grid") + :shortcut (sc/get-tooltip :toggle-layout-grid) + :value "grid" + :on-click on-add-layout}]])))])) (mf/defc context-menu-component* {:mf/private true} @@ -593,6 +597,7 @@ [{:keys [mdata]}] (let [{:keys [disable-booleans disable-flatten]} mdata shapes (mf/deref refs/selected-objects) + is-not-variant-container? (->> shapes (d/seek #(not (ctk/is-variant-container? %)))) props (mf/props {:shapes shapes :disable-booleans disable-booleans @@ -601,7 +606,8 @@ [:* [:> context-menu-edit* props] [:> context-menu-layer-position* props] - [:> context-menu-flip* props] + (when is-not-variant-container? + [:> context-menu-flip* props]) [:> context-menu-thumbnail* props] [:> context-menu-rename* props] [:> context-menu-group* props] @@ -609,7 +615,8 @@ [:> context-menu-path* props] [:> context-menu-layer-options* props] [:> context-menu-prototype* props] - [:> context-menu-layout* props] + (when is-not-variant-container? + [:> context-menu-layout* props]) [:> context-menu-component* props] [:> context-menu-delete* props]]))) 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 976072e77c..662cca7ae4 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 @@ -740,8 +740,7 @@ (mf/defc variant-menu* [{:keys [shapes]}] - (let [;; TODO check multi. What is shown? User can change properties like width? - multi (> (count shapes) 1) + (let [multi (> (count shapes) 1) shape (first shapes) shape-name (:name shape) @@ -757,13 +756,15 @@ first-variant (get objects (first (:shapes shape))) variant-id (:variant-id first-variant) - properties (->> (dwv/find-related-components data objects variant-id) - (mapcat :variant-properties) - (group-by :name) - (map (fn [[k v]] - {:name k - :values (distinct - (map #(if (str/empty? (:value %)) "--" (:value %)) v))}))) + properties (mf/with-memo [data objects variant-id] + (->> (dwv/find-related-components data objects variant-id) + (mapcat :variant-properties) + (group-by :name) + (map-indexed (fn [index [k v]] + {:name k + :pos index + :values (distinct + (map #(if (str/empty? (:value %)) "--" (:value %)) v))})))) menu-open* (mf/use-state false) menu-open? (deref menu-open*) @@ -790,15 +791,22 @@ update-property-name (mf/use-fn (mf/deps variant-id) - (fn [pos new-name] - (st/emit! (dwv/update-property-name variant-id pos new-name)))) + (fn [event] + (let [new-name (dom/get-target-val event) + pos (-> (dom/get-current-target event) + (dom/get-data "position") + int)] + (st/emit! (dwv/update-property-name variant-id pos new-name))))) remove-property (mf/use-fn - (mf/deps variant-id) - (fn [pos] - (when (> (count properties) 1) - (st/emit! (dwv/remove-property variant-id pos)))))] + (mf/deps variant-id properties) + (fn [event] + (let [pos (-> (dom/get-current-target event) + (dom/get-data "position") + int)] + (when (> (count properties) 1) + (st/emit! (dwv/remove-property variant-id pos))))))] (when (seq shapes) [:div {:class (stl/css :element-set)} [:div {:class (stl/css :element-title)} @@ -839,13 +847,18 @@ :on-close on-menu-close :menu-entries menu-entries :main-instance true}]])] - [:* - (for [[pos property] (map vector (range) properties)] - (let [val (str/join ", " (:values property))] - [:div {:key (str (:id shape) (:name property)) :class (stl/css :variant-property-row)} - [:> input-with-values* {:name (:name property) :values val :on-blur (partial update-property-name pos)}] - [:> icon-button* {:variant "ghost" - :aria-label (tr "workspace.shape.menu.remove-variant-property") - :on-click (partial remove-property pos) - :icon "remove" - :disabled (<= (count properties) 1)}]]))]]]))) + (when-not multi + [:* + (for [property properties] + (let [val (str/join ", " (:values property))] + [:div {:key (str (:id shape) (:name property)) :class (stl/css :variant-property-row)} + [:> input-with-values* {:name (:name property) + :values val + :data-position (:pos property) + :on-blur update-property-name}] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.shape.menu.remove-variant-property") + :on-click remove-property + :data-position (:pos property) + :icon "remove" + :disabled (<= (count properties) 1)}]]))])]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs index cad5009a70..d7938b69bc 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs @@ -33,6 +33,9 @@ ids (mf/with-memo [shape-id] [shape-id]) + shapes (mf/with-memo [shape] + [shape]) + stroke-values (select-keys shape stroke-attrs) layer-values (select-keys shape layer-attrs) measure-values (select-measure-keys shape) @@ -71,58 +74,59 @@ variants? (features/use-feature "variants/v1") is-variant? (when variants? (:is-variant-container shape))] - (if is-variant? - [:> variant-menu* {:shapes [shape]}] - [:* - [:& layer-menu {:ids ids - :type shape-type - :values layer-values}] - [:> measures-menu* {:ids ids - :values measure-values - :type shape-type - :shape shape}] + [:* + [:& layer-menu {:ids ids + :type shape-type + :values layer-values}] + [:> measures-menu* {:ids ids + :values measure-values + :type shape-type + :shape shape}] - [:& component-menu {:shapes [shape]}] + [:& component-menu {:shapes shapes}] - [:& layout-container-menu - {:type shape-type - :ids [(:id shape)] - :values layout-container-values - :multiple false}] + (when is-variant? + [:> variant-menu* {:shapes shapes}]) - (when (and (= (count ids) 1) is-layout-child? is-grid-parent?) - [:& grid-cell/options - {:shape (first parents) - :cell (ctl/get-cell-by-shape-id (first parents) (first ids))}]) + [:& layout-container-menu + {:type shape-type + :ids ids + :values layout-container-values + :multiple false}] - (when (or is-layout-child? is-layout-container?) - [:& layout-item-menu - {:ids ids - :type shape-type - :values layout-item-values - :is-flex-parent? is-flex-parent? - :is-grid-parent? is-grid-parent? - :is-flex-layout? is-flex-layout? - :is-grid-layout? is-grid-layout? - :is-layout-child? is-layout-child? - :is-layout-container? is-layout-container? - :shape shape}]) + (when (and (= (count ids) 1) is-layout-child? is-grid-parent?) + [:& grid-cell/options + {:shape (first parents) + :cell (ctl/get-cell-by-shape-id (first parents) (first ids))}]) - (when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?) - [:& constraints-menu {:ids ids - :values constraint-values}]) + (when (or is-layout-child? is-layout-container?) + [:& layout-item-menu + {:ids ids + :type shape-type + :values layout-item-values + :is-flex-parent? is-flex-parent? + :is-grid-parent? is-grid-parent? + :is-flex-layout? is-flex-layout? + :is-grid-layout? is-grid-layout? + :is-layout-child? is-layout-child? + :is-layout-container? is-layout-container? + :shape shape}]) - [:& fill-menu {:ids ids + (when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?) + [:& constraints-menu {:ids ids + :values constraint-values}]) + + [:& fill-menu {:ids ids + :type shape-type + :values (select-keys shape fill-attrs-shape)}] + [:& stroke-menu {:ids ids :type shape-type - :values (select-keys shape fill-attrs-shape)}] - [:& stroke-menu {:ids ids - :type shape-type - :values stroke-values}] - [:> color-selection-menu* {:type shape-type - :shapes shapes-with-children - :file-id file-id - :libraries shared-libs}] - [:> shadow-menu* {:ids ids :values (get shape :shadow)}] - [:& blur-menu {:ids ids - :values (select-keys shape [:blur])}] - [:& frame-grid {:shape shape}]]))) + :values stroke-values}] + [:> color-selection-menu* {:type shape-type + :shapes shapes-with-children + :file-id file-id + :libraries shared-libs}] + [:> shadow-menu* {:ids ids :values (get shape :shadow)}] + [:& blur-menu {:ids ids + :values (select-keys shape [:blur])}] + [:& frame-grid {:shape shape}]]))