From 18d5b84b00e4c06916907b52ebda48bfa588bc93 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 16 Sep 2025 13:09:23 +0200 Subject: [PATCH 1/3] :bug: Fix variants events (#7320) * :bug: Add missing event add-component-to-variant * :bug: Fix event apply-tokens, param applied-to-variant * :bug: Fix missing case on event "add new variant" * :bug: Fix event combine-as-variants * :bug: Fix event variant-edit-property-name * :bug: On variants events, change trigger for origin * :bug: Split combine-as-variants to not have an optional first parameter --- .../app/main/data/workspace/clipboard.cljs | 18 +- .../app/main/data/workspace/libraries.cljs | 6 +- .../app/main/data/workspace/selection.cljs | 9 +- .../src/app/main/data/workspace/shapes.cljs | 24 ++- .../data/workspace/tokens/application.cljs | 2 +- .../app/main/data/workspace/transforms.cljs | 24 ++- .../src/app/main/data/workspace/variants.cljs | 164 ++++++++++-------- .../app/main/ui/workspace/context_menu.cljs | 6 +- .../ui/workspace/sidebar/assets/common.cljs | 13 +- .../workspace/sidebar/assets/components.cljs | 10 +- .../sidebar/options/menus/component.cljs | 30 ++-- 11 files changed, 169 insertions(+), 137 deletions(-) diff --git a/frontend/src/app/main/data/workspace/clipboard.cljs b/frontend/src/app/main/data/workspace/clipboard.cljs index 55ae0b371e..f53519074f 100644 --- a/frontend/src/app/main/data/workspace/clipboard.cljs +++ b/frontend/src/app/main/data/workspace/clipboard.cljs @@ -887,6 +887,12 @@ (pcb/update-shapes [parent-id] #(ctl/add-children-to-cell % selected all-objects drop-cell))) + add-component-to-variant? (and + ;; Any of the shapes is a head + (some ctc/instance-head? orig-shapes) + ;; Any ancestor of the destination parent is a variant + (->> (cfh/get-parents-with-self page-objects parent-id) + (some ctc/is-variant?))) undo-id (js/Symbol)] (rx/concat @@ -895,10 +901,7 @@ (let [parent-type (cfh/get-shape-type all-objects (:parent-id shape)) external-lib? (not= file-id (:component-file shape)) component (ctn/get-component-from-shape shape libraries) - origin "workspace:paste" - any-parent-is-variant (->> (cfh/get-parents-with-self all-objects (:parent-id shape)) - (some ctc/is-variant?) - boolean)] + origin "workspace:paste"] ;; NOTE: we don't emit the create-shape event all the time for ;; avoid send a lot of events (that are not necessary); this @@ -909,8 +912,7 @@ :is-external-library external-lib? :type (get shape :type) :parent-type parent-type - :is-variant (ctc/is-variant? component) - :any-parent-is-variant any-parent-is-variant}) + :is-variant (ctc/is-variant? component)}) (if (cfh/has-layout? objects (:parent-id shape)) (ev/event {::ev/name "layout-add-element" ::ev/origin origin @@ -925,7 +927,9 @@ (dch/commit-changes changes) (dws/select-shapes selected) (ptk/data-event :layout/update {:ids [frame-id]}) - (dwu/commit-undo-transaction undo-id)))))))) + (dwu/commit-undo-transaction undo-id) + (when add-component-to-variant? + (ptk/event ::ev/event {::ev/name "add-component-to-variant"}))))))))) (defn- as-content [text] (let [paragraphs (->> (str/lines text) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index e642763f16..287531d458 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -592,9 +592,6 @@ page libraries) component (ctn/get-component-from-shape new-shape libraries) - any-parent-is-variant (->> (cfh/get-parents-with-self objects (:parent-id new-shape)) - (some ctk/is-variant?) - boolean) undo-id (js/Symbol)] @@ -605,8 +602,7 @@ {::ev/name "use-library-component" ::ev/origin origin :external-library (not= file-id current-file-id) - :is-variant (ctk/is-variant? component) - :any-parent-is-variant any-parent-is-variant}) + :is-variant (ctk/is-variant? component)}) (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (ptk/data-event :layout/update {:ids [(:id new-shape)]}) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 11a00dae61..498dbd2bad 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -486,11 +486,7 @@ parent-type (cfh/get-shape-type objects (:parent-id shape)) external-lib? (not= file-id (:component-file shape)) component (ctn/get-component-from-shape shape libraries) - origin "workspace:duplicate-shapes" - - any-parent-is-variant (->> (cfh/get-parents-with-self objects (:parent-id shape)) - (some ctk/is-variant?) - boolean)] + origin "workspace:duplicate-shapes"] ;; NOTE: we don't emit the create-shape event all the time for ;; avoid send a lot of events (that are not necessary); this @@ -501,8 +497,7 @@ :is-external-library external-lib? :type (get shape :type) :parent-type parent-type - :is-variant (ctk/is-variant? component) - :any-parent-is-variant any-parent-is-variant}) + :is-variant (ctk/is-variant? component)}) (if (cfh/has-layout? objects (:parent-id shape)) (ev/event {::ev/name "layout-add-element" ::ev/origin origin diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 06e9c200b6..3df0560a32 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -235,7 +235,6 @@ parent-id (:id (ctn/get-first-valid-parent objects parent-id)) ;; We don't want to change the structure of component copies frame-id (:id (ctn/get-first-valid-parent objects frame-id)) - shape (cts/setup-shape (-> attrs (assoc :type type) @@ -312,7 +311,6 @@ (get objects) (ctc/is-variant?))))] - (rx/of (create-artboard-from-shapes selected id parent-id index name delta))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -362,6 +360,7 @@ ;; FIXME: this need to be refactored + (defn toggle-file-thumbnail-selected [] (ptk/reify ::toggle-file-thumbnail-selected @@ -397,6 +396,7 @@ ;; --- Change Shape Order (D&D Ordering) + (defn relocate-shapes [ids parent-id to-index & [ignore-parents?]] (dm/assert! (every? uuid? ids)) @@ -428,10 +428,28 @@ to-index ids :ignore-parents? ignore-parents?)) + + add-component-to-variant? (and + ;; Any of the shapes is a head + (some (comp ctc/instance-head? objects) ids) + ;; Any ancestor of the destination parent is a variant + (->> (cfh/get-parents-with-self objects parent-id) + (some ctc/is-variant?))) + + add-new-variant? (and + ;; The parent is a variant container + (-> parent-id objects ctc/is-variant-container?) + ;; Any of the shapes is a main instance + (some (comp ctc/main-instance? objects) ids)) + undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dwco/expand-collapse parent-id) (ptk/data-event :layout/update {:ids (concat all-parents ids)}) - (dwu/commit-undo-transaction undo-id)))))) + (dwu/commit-undo-transaction undo-id) + (when add-component-to-variant? + (ev/event {::ev/name "add-component-to-variant"})) + (when add-new-variant? + (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:move-shapes-in-layers-tab"}))))))) diff --git a/frontend/src/app/main/data/workspace/tokens/application.cljs b/frontend/src/app/main/data/workspace/tokens/application.cljs index fc384e7ee0..d650962b1f 100644 --- a/frontend/src/app/main/data/workspace/tokens/application.cljs +++ b/frontend/src/app/main/data/workspace/tokens/application.cljs @@ -489,7 +489,7 @@ (some ctt/spacing-margin-keys attributes)) (ctt/any-appliable-attr? attributes (:type shape)))))) shape-ids (d/nilv (keys shapes) []) - any-variant? (->> shapes (some ctk/is-variant?) boolean) + any-variant? (->> shapes vals (some ctk/is-variant?) boolean) resolved-value (get-in resolved-tokens [(cft/token-identifier token) :resolved-value]) tokenized-attributes (cft/attributes-map attributes token) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 93a12b82d4..9a1bcaa319 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -295,7 +295,6 @@ (rx/map #(resize shape initial-position layout %)) (rx/share)) - modifiers-stream (if (features/active-feature? state "render-wasm/v1") (rx/merge @@ -1024,7 +1023,6 @@ delta (calculate-delta position bbox frame) modifiers (dwm/create-modif-tree [id] (ctm/move-modifiers delta))] - (if (features/active-feature? state "render-wasm/v1") (rx/of (dwm/apply-wasm-modifiers modifiers {:ignore-constraints false @@ -1105,7 +1103,19 @@ frame-id drop-index ids - :cell cell))] + :cell cell)) + + add-component-to-variant? (and + ;; Any of the shapes is a head + (some (comp ctk/instance-head? objects) ids) + ;; Any ancestor of the destination parent is a variant + (->> (cfh/get-parents-with-self objects frame-id) + (some ctk/is-variant?))) + add-new-variant? (and + ;; The parent is a variant container + (-> frame-id objects ctk/is-variant-container?) + ;; Any of the shapes is a main instance + (some (comp ctk/main-instance? objects) ids))] (rx/concat (let [shapes (mapv #(get objects %) ids) @@ -1120,7 +1130,11 @@ (when (and (some? frame-id) (d/not-empty? changes)) (rx/of (dch/commit-changes changes) - (dwc/expand-collapse frame-id)))))))) + (dwc/expand-collapse frame-id))) + (when add-component-to-variant? + (rx/of (ev/event {::ev/name "add-component-to-variant"}))) + (when add-new-variant? + (rx/of (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:move-shapes-to-frame"})))))))) (defn- get-displacement "Retrieve the correct displacement delta point for the @@ -1135,6 +1149,7 @@ ;; -- Flip ---------------------------------------------------------- + (defn flip-horizontal-selected ([] (flip-horizontal-selected nil)) @@ -1152,7 +1167,6 @@ center (grc/rect->center selrect) modifiers (dwm/create-modif-tree selected (ctm/resize-modifiers (gpt/point -1.0 1.0) center))] - (if (features/active-feature? state "render-wasm/v1") (rx/of (dwm/apply-wasm-modifiers modifiers {:ignore-snap-pixel true})) (rx/of (dwm/apply-modifiers {:modifiers modifiers :ignore-snap-pixel true})))))))) diff --git a/frontend/src/app/main/data/workspace/variants.cljs b/frontend/src/app/main/data/workspace/variants.cljs index 49bfa2dacf..af0a2c6dba 100644 --- a/frontend/src/app/main/data/workspace/variants.cljs +++ b/frontend/src/app/main/data/workspace/variants.cljs @@ -87,9 +87,9 @@ (rx/of (when (or (seq properties-to-remove) (seq properties-to-update)) - (ptk/event ::ev/event {::ev/name "variant-edit-property-value" :trigger "rename-in-layers"})) + (ev/event {::ev/name "variant-edit-property-value" ::ev/origin "workspace:rename-in-layers"})) (when (seq properties-to-add) - (ptk/event ::ev/event {::ev/name "variant-add-property" :trigger "rename-in-layers"})) + (ev/event {::ev/name "variant-add-property" ::ev/origin "workspace:rename-in-layers"})) (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dwu/commit-undo-transaction undo-id)))))) @@ -97,7 +97,7 @@ (defn update-property-name "Update the variant property name on the position pos in all the components with this variant-id" - [variant-id pos new-name] + [variant-id pos new-name {:keys [trigger]}] (ptk/reify ::update-property-name ptk/WatchEvent (watch [it state _] @@ -106,15 +106,22 @@ objects (-> (dsh/get-page data page-id) (get :objects)) + related-components (cfv/find-variant-components data objects variant-id) + + props (-> related-components last :variant-properties) + prop-name (-> props (nth pos) :name) + changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) (pcb/with-library-data data) (clvp/generate-update-property-name variant-id pos new-name)) undo-id (js/Symbol)] - (rx/of - (dwu/start-undo-transaction undo-id) - (dch/commit-changes changes) - (dwu/commit-undo-transaction undo-id)))))) + (when (not= prop-name new-name) + (rx/of + (dwu/start-undo-transaction undo-id) + (dch/commit-changes changes) + (dwu/commit-undo-transaction undo-id) + (ev/event {::ev/name "variant-edit-property-name" ::ev/origin trigger}))))))) (defn update-property-value "Updates the variant property value on the position pos in a component" @@ -225,7 +232,7 @@ (when (seq (:redo-changes changes)) (rx/of - (ptk/event ::ev/event {::ev/name "variant-remove-property" :trigger "rename-in-layers"}) + (ev/event {::ev/name "variant-remove-property" ::ev/origin "workspace:rename-in-layers"}) (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dwu/commit-undo-transaction undo-id))))))) @@ -466,13 +473,13 @@ (cond transform-in-variant? (rx/of - (ptk/event ::ev/event {::ev/name "transform-in-variant" :trigger "shortcut"}) + (ev/event {::ev/name "transform-in-variant" ::ev/origin "workspace:shortcut"}) (transform-in-variant (:id first-shape))) add-new-variant? (rx/concat (rx/of - (ptk/event ::ev/event {::ev/name "add-new-variant" :trigger "shortcut-create-component"}) + (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:shortcut-create-component"}) (dwu/start-undo-transaction undo-id)) (rx/from (map add-new-variant selected-ids)) (rx/of (dwu/commit-undo-transaction undo-id))) @@ -494,7 +501,7 @@ (if add-new-variant? (rx/concat (rx/of - (ptk/event ::ev/event {::ev/name "add-new-variant" :trigger "shortcut-duplicate"}) + (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:shortcut-duplicate"}) (dwu/start-undo-transaction undo-id) (add-new-variant (first selected-ids) false)) (rx/from (map #(add-new-variant % true) (rest selected-ids))) @@ -563,79 +570,86 @@ vec)) (defn combine-as-variants - ([] - (combine-as-variants nil {})) - ([selected {:keys [page-id]}] - (ptk/reify ::combine-as-variants - ptk/WatchEvent - (watch [_ state stream] - (let [current-page (:current-page-id state) + [ids {:keys [page-id trigger]}] + (ptk/reify ::combine-as-variants + ptk/WatchEvent + (watch [_ state stream] + (let [current-page (:current-page-id state) - combine - (fn [current-page] - (let [objects (dsh/lookup-page-objects state current-page) - selected (->> (or selected (dsh/lookup-selected state)) - (cfh/clean-loops objects) - (remove (fn [id] - (let [shape (get objects id)] - (or (not (ctc/main-instance? shape)) - (ctc/is-variant? shape))))))] - (when (> (count selected) 1) - (let [shapes (mapv #(get objects %) selected) - rect (bounding-rect shapes) - prefix (->> shapes - (mapv #(cfh/split-path (:name %))) - (common-prefix)) + combine + (fn [current-page] + (let [objects (dsh/lookup-page-objects state current-page) + ids (->> ids + (cfh/clean-loops objects) + (remove (fn [id] + (let [shape (get objects id)] + (or (not (ctc/main-instance? shape)) + (ctc/is-variant? shape))))))] + (when (> (count ids) 1) + (let [shapes (mapv #(get objects %) ids) + rect (bounding-rect shapes) + prefix (->> shapes + (mapv #(cfh/split-path (:name %))) + (common-prefix)) ;; When the common parent is root, add a wrapper - add-wrapper? (empty? prefix) - first-shape (first shapes) - delta (gpt/point (- (:x rect) (:x first-shape) 30) - (- (:y rect) (:y first-shape) 30)) - common-parent (->> selected - (mapv #(-> (cfh/get-parent-ids objects %) reverse)) - common-prefix - last) - index (-> (get objects common-parent) - :shapes - count - inc) - variant-id (uuid/next) - undo-id (js/Symbol)] + add-wrapper? (empty? prefix) + first-shape (first shapes) + delta (gpt/point (- (:x rect) (:x first-shape) 30) + (- (:y rect) (:y first-shape) 30)) + common-parent (->> ids + (mapv #(-> (cfh/get-parent-ids objects %) reverse)) + common-prefix + last) + index (-> (get objects common-parent) + :shapes + count + inc) + variant-id (uuid/next) + undo-id (js/Symbol)] - (rx/concat - (if (and page-id (not= current-page page-id)) - (rx/of (dcm/go-to-workspace :page-id page-id)) - (rx/empty)) + (rx/concat + (if (and page-id (not= current-page page-id)) + (rx/of (dcm/go-to-workspace :page-id page-id)) + (rx/empty)) - (rx/of (dwu/start-undo-transaction undo-id) - (transform-in-variant (first selected) variant-id delta prefix add-wrapper? false false) - (dwsh/relocate-shapes (into #{} (-> selected rest reverse)) variant-id 0) - (dwsh/update-shapes selected #(-> % - (assoc :constraints-h :left) - (assoc :constraints-v :top) - (assoc :fixed-scroll false))) - (dwsh/relocate-shapes #{variant-id} common-parent index) - (dwt/update-dimensions [variant-id] :width (+ (:width rect) 60)) - (dwt/update-dimensions [variant-id] :height (+ (:height rect) 60))) + (rx/of (dwu/start-undo-transaction undo-id) + (transform-in-variant (first ids) variant-id delta prefix add-wrapper? false false) + (dwsh/relocate-shapes (into #{} (-> ids rest reverse)) variant-id 0) + (dwsh/update-shapes ids #(-> % + (assoc :constraints-h :left) + (assoc :constraints-v :top) + (assoc :fixed-scroll false))) + (dwsh/relocate-shapes #{variant-id} common-parent index) + (dwt/update-dimensions [variant-id] :width (+ (:width rect) 60)) + (dwt/update-dimensions [variant-id] :height (+ (:height rect) 60)) + (ev/event {::ev/name "combine-as-variants" ::ev/origin trigger :number-of-combined (count ids)})) ;; NOTE: we need to schedule a commit into a ;; microtask for ensure that all the scheduled ;; microtask of previous events execute before the ;; commit - (->> (rx/of (dwu/commit-undo-transaction undo-id)) - (rx/observe-on :async))))))) + (->> (rx/of (dwu/commit-undo-transaction undo-id)) + (rx/observe-on :async))))))) - redirect-to-page - (fn [page-id] - (rx/merge - (->> stream - (rx/filter (ptk/type? ::dwpg/initialize-page)) - (rx/take 1) - (rx/observe-on :async) - (rx/mapcat (fn [_] (combine page-id)))) - (rx/of (dcm/go-to-workspace :page-id page-id))))] + redirect-to-page + (fn [page-id] + (rx/merge + (->> stream + (rx/filter (ptk/type? ::dwpg/initialize-page)) + (rx/take 1) + (rx/observe-on :async) + (rx/mapcat (fn [_] (combine page-id)))) + (rx/of (dcm/go-to-workspace :page-id page-id))))] - (if (and page-id (not= page-id current-page)) - (redirect-to-page page-id) - (combine current-page))))))) + (if (and page-id (not= page-id current-page)) + (redirect-to-page page-id) + (combine current-page)))))) + +(defn combine-selected-as-variants + [options] + (ptk/reify ::combine-selected-as-variants + ptk/WatchEvent + (watch [_ state _] + (let [selected (dsh/lookup-selected state)] + (rx/of (combine-as-variants selected options)))))) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 0e0f1b8558..4739f36385 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -576,12 +576,11 @@ do-add-component (mf/use-fn #(st/emit! (dwl/add-component))) do-add-multiple-components (mf/use-fn #(st/emit! (dwl/add-multiple-components))) do-combine-as-variants (mf/use-fn #(st/emit! - (ptk/event ::ev/event {::ev/name "combine-as-variants" :trigger "context-menu-component"}) - (dwv/combine-as-variants))) + (dwv/combine-selected-as-variants {:trigger "workspace:context-menu-component"}))) do-add-variant (mf/use-fn (mf/deps shapes) #(st/emit! - (ptk/event ::ev/event {::ev/name "add-new-variant" :trigger "context-menu-component"}) + (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:context-menu-component"}) (dwv/add-new-variant (:id (first shapes)))))] [:* (when can-make-component ;; We don't want to change the structure of component copies @@ -802,6 +801,7 @@ ;; FIXME: optimize because it is rendered always + (mf/defc context-menu* [] (let [mdata (mf/deref menu-ref) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs index e7d5647cc2..223056ba12 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs @@ -39,7 +39,6 @@ [app.util.timers :as ts] [cljs.spec.alpha :as s] [cuerdas.core :as str] - [potok.v2.core :as ptk] [rumext.v2 :as mf])) (def assets-filters (mf/create-context nil)) @@ -357,6 +356,8 @@ ;; For when it's only one shape + + shape (first shapes) shape-id (:id shape) @@ -442,17 +443,17 @@ do-add-variant #(if (ctk/is-variant? shape) (st/emit! - (ptk/event ::ev/event {::ev/name "add-new-variant" - :trigger (if for-design-tab? "design-tab-menu-variant" "context-menu-variant")}) + (ev/event {::ev/name "add-new-variant" + ::ev/origin (if for-design-tab? "workspace:design-tab-menu-variant" "workspace:context-menu-variant")}) (dwv/add-new-variant shape-id)) (st/emit! - (ptk/event ::ev/event {::ev/name "transform-in-variant" - :trigger (if for-design-tab? "design-tab-menu" "context-menu")}) + (ev/event {::ev/name "transform-in-variant" + ::ev/origin (if for-design-tab? "workspace:design-tab-menu" "workspace:context-menu")}) (dwv/transform-in-variant shape-id))) do-add-new-property #(st/emit! - (ptk/event ::ev/event {::ev/name "add-new-property" :trigger "design-tab-menu-variant"}) + (ev/event {::ev/name "add-new-property" ::ev/origin "workspace:design-tab-menu-variant"}) (dwv/add-new-property variant-id {:property-value "Value 1" :editing? true})) do-show-local-component diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs index f0c7b1ed5d..288dbe4f87 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -176,7 +176,6 @@ :title (tr "workspace.assets.components.num-variants" num-variants)} [:> icon* {:icon-id i/variant :size "s"}]])])])) - (defn- count-leaves "Counts the total number of leaf elements in a nested map structure. A leaf element is considered any element inside a vector." @@ -202,7 +201,6 @@ dragging* (mf/use-state false) dragging? (deref dragging*) - selected-paths (mf/with-memo [selected-full] (into #{} (comp (map :path) (d/nilv "")) @@ -262,7 +260,6 @@ (when ^boolean dragging? [:div {:class (stl/css :grid-placeholder)} "\u00A0"]) - (when (and (empty? components) (some? groups) is-local) @@ -506,9 +503,7 @@ page-id (->> comps first :main-instance-page)] (st/emit! - (ptk/event ::ev/event {::ev/name "combine-as-variants" :trigger "context-menu-assets-group"}) - (dwv/combine-as-variants ids {:page-id page-id}))))) - + (dwv/combine-as-variants ids {:page-id page-id :trigger "workspace:context-menu-assets-group"}))))) on-drag-start (mf/use-fn @@ -557,8 +552,7 @@ ids (into #{} (map :main-instance-id selected-full))] (st/emit! - (ptk/event ::ev/event {::ev/name "combine-as-variants" :trigger "context-menu-assets"}) - (dwv/combine-as-variants ids {:page-id page-id})))))] + (dwv/combine-as-variants ids {:page-id page-id :trigger "workspace:context-menu-assets"})))))] [:& cmm/asset-section {:file-id file-id :title (tr "workspace.assets.components") 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 46c431ef3a..830b364c69 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 @@ -47,7 +47,6 @@ [app.util.timers :as tm] [cuerdas.core :as str] [okulary.core :as l] - [potok.v2.core :as ptk] [rumext.v2 :as mf])) (def ref:annotations-state @@ -342,7 +341,7 @@ (let [value (d/nilv (str/trim value) "")] (doseq [id component-ids] (st/emit! - (ptk/event ::ev/event {::ev/name "variant-edit-property-value" :trigger "combo-design-tab"}) + (ev/event {::ev/name "variant-edit-property-value" ::ev/origin "workspace:combo-design-tab"}) (dwv/update-property-value id pos value)) (st/emit! (dwv/update-error id)))))) @@ -356,8 +355,7 @@ int)] (when (seq value) (st/emit! - (ptk/event ::ev/event {::ev/name "variant-edit-property-name" :trigger "design-tab-variant"}) - (dwv/update-property-name variant-id pos value))))))] + (dwv/update-property-name variant-id pos value {:trigger "workspace:design-tab-variant"}))))))] [:* [:div {:class (stl/css :variant-property-list)} @@ -895,22 +893,21 @@ (mf/use-fn (mf/deps id) #(st/emit! - (ptk/event ::ev/event {::ev/name "add-new-variant" :trigger "button-design-tab-variant"}) + (ev/event {::ev/name "add-new-variant" ::ev/origin "workspace:button-design-tab-variant"}) (dwv/add-new-variant id))) add-new-property (mf/use-fn (mf/deps shape) #(st/emit! - (ptk/event ::ev/event {::ev/name "add-new-property" :trigger "button-design-tab-variant"}) + (ev/event {::ev/name "add-new-property" ::ev/origin "workspace:button-design-tab-variant"}) (dwv/add-new-property (:variant-id shape) {:property-value "Value 1" :editing? true}))) on-combine-as-variants (mf/use-fn #(st/emit! - (ptk/event ::ev/event {::ev/name "combine-as-variants" :trigger "button-design-tab"}) - (dwv/combine-as-variants))) + (dwv/combine-selected-as-variants {:trigger "workspace:button-design-tab"}))) ;; NOTE: function needed for force rerender from the bottom ;; components. This is because `component-annotation` @@ -1097,7 +1094,7 @@ (mf/use-fn (mf/deps shape) (fn [trigger] - (st/emit! (ptk/event ::ev/event {::ev/name "add-new-variant" :trigger trigger}) + (st/emit! (ev/event {::ev/name "add-new-variant" ::ev/origin trigger}) (dwv/add-new-variant (:id shape))))) add-new-property @@ -1105,16 +1102,16 @@ (mf/deps variant-id) (fn [trigger] (st/emit! - (ptk/event ::ev/event {::ev/name "add-new-property" :trigger trigger}) + (ev/event {::ev/name "add-new-property" ::ev/origin trigger}) (dwv/add-new-property variant-id {:property-value "Value 1" :editing? true})))) menu-entries [{:title (tr "workspace.shape.menu.show-in-assets") :action show-in-assets-panel} {:title (tr "workspace.shape.menu.add-variant") - :action (partial create-variant "design-tab-menu-component")} + :action (partial create-variant "workspace:design-tab-menu-component")} {:title (tr "workspace.shape.menu.add-variant-property") - :action (partial add-new-property "design-tab-menu-component")}] + :action (partial add-new-property "workspace:design-tab-menu-component")}] toggle-content (mf/use-fn @@ -1149,8 +1146,7 @@ int)] (when (seq value) (st/emit! - (ptk/event ::ev/event {::ev/name "variant-edit-property-name" :trigger "design-tab-component"}) - (dwv/update-property-name variant-id pos value)))))) + (dwv/update-property-name variant-id pos value {:trigger "workspace:design-tab-component"})))))) remove-property (mf/use-fn @@ -1161,7 +1157,7 @@ int)] (when (> (count properties) 1) (st/emit! - (ptk/event ::ev/event {::ev/name "variant-remove-property" :trigger "button-design-tab"}) + (ev/event {::ev/name "variant-remove-property" ::ev/origin "workspace:button-design-tab"}) (dwv/remove-property variant-id pos)))))) select-shapes-with-malformed @@ -1195,7 +1191,7 @@ :icon i/help}] [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant") - :on-click (partial create-variant "button-design-tab-component") + :on-click (partial create-variant "workspace:button-design-tab-component") :icon i/variant}]]]] (when open? @@ -1233,7 +1229,7 @@ [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant-property") - :on-click (partial add-new-property "button-design-tab-component") + :on-click (partial add-new-property "workspace:button-design-tab-component") :icon i/add}]] (when-not multi? From ef376fbb7b9602bb71b8fb7a0b5ba4e625d88713 Mon Sep 17 00:00:00 2001 From: Luis de Dios Date: Tue, 16 Sep 2025 14:06:47 +0200 Subject: [PATCH 2/3] :sparkles: Add shortcut for creating variant to the shortcuts panel (#7319) * :sparkles: Add shortcut for creating variant to the shortcuts panel * :recycle: Update components to new rumext syntax * :bug: Fix unique "key" prop error for each child in a list * :recycle: Remove deprecated icons and CSS cleanup * :paperclip: PR changes --- .../app/main/data/workspace/shortcuts.cljs | 8 +- .../app/main/ui/components/search_bar.cljs | 1 + .../app/main/ui/components/search_bar.scss | 40 +++--- .../app/main/ui/workspace/context_menu.cljs | 4 +- .../ui/workspace/sidebar/assets/common.cljs | 2 +- .../main/ui/workspace/sidebar/shortcuts.cljs | 119 +++++++++--------- .../main/ui/workspace/sidebar/shortcuts.scss | 88 +------------ frontend/translations/en.po | 4 +- frontend/translations/es.po | 4 +- 9 files changed, 98 insertions(+), 172 deletions(-) diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index b36a28006b..4f4d9296cc 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -173,10 +173,10 @@ :subsections [:modify-layers] :fn #(emit-when-no-readonly (dw/unmask-group))} - :create-component {:tooltip (ds/meta "K") - :command (ds/c-mod "k") - :subsections [:modify-layers] - :fn #(emit-when-no-readonly (dwv/add-component-or-variant))} + :create-component-variant {:tooltip (ds/meta "K") + :command (ds/c-mod "k") + :subsections [:modify-layers] + :fn #(emit-when-no-readonly (dwv/add-component-or-variant))} :detach-component {:tooltip (ds/meta-shift "K") :command (ds/c-mod "shift+k") diff --git a/frontend/src/app/main/ui/components/search_bar.cljs b/frontend/src/app/main/ui/components/search_bar.cljs index aa9fb0dcf7..bd75464433 100644 --- a/frontend/src/app/main/ui/components/search_bar.cljs +++ b/frontend/src/app/main/ui/components/search_bar.cljs @@ -46,6 +46,7 @@ :size "s" :class (stl/css :icon)}]) [:input {:id id + :class (stl/css :search-input) :on-change handle-change :value value :auto-focus auto-focus diff --git a/frontend/src/app/main/ui/components/search_bar.scss b/frontend/src/app/main/ui/components/search_bar.scss index d6b559e3e4..5dfa161079 100644 --- a/frontend/src/app/main/ui/components/search_bar.scss +++ b/frontend/src/app/main/ui/components/search_bar.scss @@ -18,6 +18,7 @@ .icon { margin-left: $s-8; flex: 0 0 $s-16; + color: var(--color-foreground-primary); } .search-input-wrapper { @@ -27,46 +28,47 @@ border: $s-1 solid var(--search-bar-input-border-color); border-radius: $br-8; background-color: var(--search-bar-input-background-color); - input { - width: 100%; - height: 100%; - margin: 0 $s-8 0 $s-4; - border: 0; - background-color: var(--input-background-color); - font-size: $fs-12; - color: var(--input-foreground-color); - border-radius: $br-8; - &:focus { - outline: none; - } - } + &:hover { border: $s-1 solid var(--input-border-color-hover); background-color: var(--input-background-color-hover); - input { + .search-input { background-color: var(--input-background-color-hover); } } + &:focus-within { background-color: var(--input-background-color-active); color: var(--input-foreground-color-active); border: $s-1 solid var(--input-border-color-focus); - input { + .search-input { background-color: var(--input-background-color-active); } } } +.search-input { + width: 100%; + height: 100%; + margin: 0 $s-8 0 $s-4; + border: 0; + background-color: var(--input-background-color); + font-size: $fs-12; + color: var(--input-foreground-color); + border-radius: $br-8; + &:focus { + outline: none; + } +} + .clear-icon { @extend .button-tag; flex: 0 0 $s-32; height: 100%; - svg { - stroke: var(--icon-foreground); - } + color: var(--color-icon-default); } .search-box.has-children .search-input-wrapper { - border-radius: $br-2 $br-8 $br-8 $br-2; + border-radius: 0 $br-8 $br-8 0; margin-left: 0; } diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 4739f36385..29e802b567 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -588,7 +588,7 @@ [:> menu-separator* {}] [:> menu-entry* {:title (tr "workspace.shape.menu.create-component") - :shortcut (sc/get-tooltip :create-component) + :shortcut (sc/get-tooltip :create-component-variant) :on-click do-add-component}] (when (not single?) [:> menu-entry* {:title (tr "workspace.shape.menu.create-multiple-components") @@ -608,7 +608,7 @@ [:* [:> menu-separator*] [:> menu-entry* {:title (tr "workspace.shape.menu.add-variant") - :shortcut (sc/get-tooltip :create-component) + :shortcut (sc/get-tooltip :create-component-variant) :on-click do-add-variant}]]) (when (and (not single?) all-main? (not any-variant?)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs index 223056ba12..6ae9a43479 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/common.cljs @@ -514,7 +514,7 @@ :action do-update-component}) (when (and (or (not multi) same-variant?) main-instance?) {:title (tr "workspace.shape.menu.add-variant") - :shortcut :create-component + :shortcut :create-component-variant :action do-add-variant}) (when (and same-variant? main-instance? variant-id for-design-tab?) {:title (tr "workspace.shape.menu.add-variant-property") diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs index 27dee45b59..fcd08e9265 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs @@ -18,17 +18,18 @@ [app.main.data.workspace.shortcuts] [app.main.store :as st] [app.main.ui.components.search-bar :refer [search-bar*]] - [app.main.ui.ds.foundations.assets.icon :as i] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] [app.util.strings :refer [matches-search]] [clojure.set :as set] [clojure.string] + [cuerdas.core :as str] [rumext.v2 :as mf])) -(mf/defc converted-chars - [{:keys [char command] :as props}] +(mf/defc converted-chars* + [{:keys [char command]}] (let [modified-keys {:up ds/up-arrow :down ds/down-arrow :left ds/left-arrow @@ -93,7 +94,7 @@ (tr "shortcuts.clear-undo") (tr "shortcuts.copy") (tr "shortcuts.copy-link") - (tr "shortcuts.create-component") + (tr "shortcuts.create-component-variant") (tr "shortcuts.create-new-project") (tr "shortcuts.cut") (tr "shortcuts.decrease-zoom") @@ -238,8 +239,8 @@ (assoc acc subsection {:children shortcuts-by-subsection})))] (reduce reduce-sc {} subsections))) -(mf/defc shortcuts-keys - [{:keys [content command] :as props}] +(mf/defc shortcuts-keys* + [{:keys [content command]}] (let [managed-list (if (coll? content) content (conj () content)) @@ -251,26 +252,26 @@ penultimate (last short-char-list)] [:span {:class (stl/css :keys)} (for [chars short-char-list] - [:* + [:* {:key (str/join chars)} (for [char chars] - [:& converted-chars {:key (dm/str char "-" (name command)) - :char char - :command command}]) + [:> converted-chars* {:key (dm/str char "-" (name command)) + :char char + :command command}]) (when (not= chars penultimate) [:span {:class (stl/css :space)} ","])]) (when (not= last-element penultimate) [:* [:span {:class (stl/css :space)} (tr "shortcuts.or")] (for [char last-element] - [:& converted-chars {:key (dm/str char "-" (name command)) - :char char - :command command}])])])) + [:> converted-chars* {:key (dm/str char "-" (name command)) + :char char + :command command}])])])) -(mf/defc shortcut-row - [{:keys [elements filter-term match-section? match-subsection?] :as props}] +(mf/defc shortcut-row* + [{:keys [elements filter-term is-match-section is-match-subsection]}] (let [shortcut-name (keys elements) shortcut-translations (map #(translation-keyname :sc %) shortcut-name) match-shortcut? (some #(matches-search % @filter-term) shortcut-translations) - filtered (if (and (or match-section? match-subsection?) (not match-shortcut?)) + filtered (if (and (or is-match-section is-match-subsection) (not match-shortcut?)) shortcut-translations (filter #(matches-search % @filter-term) shortcut-translations)) sorted-filtered (sort filtered)] @@ -284,22 +285,22 @@ :key command-translate} [:span {:class (stl/css :command-name)} command-translate] - [:& shortcuts-keys {:content content - :command command}]]))])) + [:> shortcuts-keys* {:content content + :command command}]]))])) -(mf/defc section-title - [{:keys [is-visible? name is-sub?] :as props}] - [:div {:class (if is-sub? +(mf/defc section-title* + [{:keys [name is-visible is-sub]}] + [:div {:class (if is-sub (stl/css :subsection-title) (stl/css :section-title))} - [:span {:class (stl/css-case :open is-visible? - :collapsed-shortcuts true)} deprecated-icon/arrow] - [:span {:class (if is-sub? + [:> icon* {:icon-id (if is-visible i/arrow-down i/arrow-right) + :size "s"}] + [:span {:class (if is-sub (stl/css :subsection-name) (stl/css :section-name))} name]]) -(mf/defc shortcut-subsection - [{:keys [subsections manage-sections filter-term match-section? open-sections] :as props}] +(mf/defc shortcut-subsection* + [{:keys [subsections manage-sections filter-term is-match-section open-sections]}] (let [subsections-names (keys subsections) subsection-translations (if (= :none (first subsections-names)) (map #(translation-keyname :sc %) subsections-names) @@ -308,10 +309,10 @@ ;; Basics section is treated different because it has no sub sections (if (= :none (first subsections-names)) (let [basic-shortcuts (:none subsections)] - [:& shortcut-row {:elements (:children basic-shortcuts) - :filter-term filter-term - :match-section? match-section? - :match-subsection? true}]) + [:> shortcut-row* {:elements (:children basic-shortcuts) + :filter-term filter-term + :is-match-section is-match-section + :is-match-subsection true}]) [:ul {:class (stl/css :subsection-menu)} (for [sub-translated sorted-translations] @@ -321,21 +322,21 @@ match-subsection? (matches-search (translation-keyname :sub-sec sub-name) @filter-term) shortcut-names (map #(translation-keyname :sc %) (keys (:children sub-info))) match-shortcuts? (some #(matches-search % @filter-term) shortcut-names)] - (when (or match-subsection? match-shortcuts? match-section?) + (when (or match-subsection? match-shortcuts? is-match-section) [:li {:key sub-translated :on-click (manage-sections (:id sub-info))} - [:& section-title {:name sub-translated - :is-sub? true - :is-visible? visible?}] + [:> section-title* {:name sub-translated + :is-visible visible? + :is-sub true}] [:div {:style {:display (if visible? "initial" "none")}} - [:& shortcut-row {:elements (:children sub-info) - :filter-term filter-term - :match-section? match-section? - :match-subsection? match-subsection?}]]])))]))) + [:> shortcut-row* {:elements (:children sub-info) + :filter-term filter-term + :is-match-section is-match-section + :is-match-subsection match-subsection?}]]])))]))) -(mf/defc shortcut-section - [{:keys [section manage-sections open-sections filter-term] :as props}] +(mf/defc shortcut-section* + [{:keys [section manage-sections open-sections filter-term]}] (let [[section-key section-info] section section-id (:id section-info) section-translation (translation-keyname :sec section-key) @@ -354,16 +355,16 @@ (when (or match-section? match-subsection? match-shortcut?) [:div {:class (stl/css :section) :on-click (manage-sections section-id)} - [:& section-title {:is-visible? visible? - :is-sub? false - :name section-translation}] + [:> section-title* {:name section-translation + :is-visible visible? + :is-sub false}] [:div {:style {:display (if visible? "initial" "none")}} - [:& shortcut-subsection {:subsections subsections - :open-sections open-sections - :manage-sections manage-sections - :match-section? match-section? - :filter-term filter-term}]]]))) + [:> shortcut-subsection* {:subsections subsections + :open-sections open-sections + :manage-sections manage-sections + :is-match-section match-section? + :filter-term filter-term}]]]))) (mf/defc shortcuts-container* [{:keys [class]}] @@ -496,11 +497,13 @@ [:div {:class (dm/str class " " (stl/css :shortcuts))} [:div {:class (stl/css :shortcuts-header)} [:div {:class (stl/css :shortcuts-title)} (tr "shortcuts.title")] - [:div {:class (stl/css :shortcuts-close-button) - :on-click close-fn} - deprecated-icon/close]] - [:div {:class (stl/css :search-field)} + [:> icon-button* {:variant "ghost" + :icon i/close + :class (stl/css :shortcuts-close-button) + :on-click close-fn + :aria-label (tr "labels.close")}]] + [:div {:class (stl/css :search-field)} [:> search-bar* {:on-change on-search-term-change-2 :on-clear on-search-clear-click :value @filter-term @@ -510,9 +513,9 @@ (if match-any? [:div {:class (stl/css :shortcuts-list)} (for [section all-shortcuts] - [:& shortcut-section - {:section section - :manage-sections manage-sections - :open-sections open-sections - :filter-term filter-term}])] + [:> shortcut-section* {:key (->> section second :id first) + :section section + :manage-sections manage-sections + :open-sections open-sections + :filter-term filter-term}])] [:div {:class (stl/css :not-found)} (tr "shortcuts.not-found")])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss index e5353c9e59..1df0af953c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.scss @@ -15,64 +15,7 @@ } .search-field { - display: flex; - align-items: center; - height: $s-32; margin: $s-16 $s-12 $s-4 $s-12; - border-radius: $br-8; - font-family: "worksans", "vazirmatn", sans-serif; - background-color: var(--color-background-tertiary); - .search-box { - align-items: center; - display: flex; - width: 100%; - - .icon-wrapper { - display: flex; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } - - .input-text { - @include removeInputStyle; - height: $s-32; - width: 100%; - margin: 0; - padding: $s-4; - border: 0; - font-size: $fs-12; - color: var(--color-foreground-primary); - &::placeholder { - color: var(--color-foreground-secondary); - } - &:focus-visible { - border-color: var(--color-accent-primary-muted); - } - } - .clear-btn { - @include buttonStyle; - @include flexCenter; - height: $s-16; - width: $s-16; - .clear-icon { - @include flexCenter; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } - } - } - .search-icon { - @include flexCenter; - width: $s-28; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } } .shortcuts-header { @@ -92,18 +35,9 @@ } .shortcuts-close-button { - @extend .button-tertiary; position: absolute; - right: $s-2; - top: $s-2; - height: $s-28; - width: $s-28; - border-radius: $br-5; - - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } + right: 0; + top: 0; } } @@ -122,7 +56,7 @@ flex-direction: column; height: 100%; padding: $s-12; - overflow-y: scroll; + overflow-y: auto; font-size: $fs-12; color: var(--title-foreground-color); @@ -135,27 +69,13 @@ padding: $s-8 0; cursor: pointer; - .collapsed-shortcuts { - @include flexCenter; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - &.open { - transform: rotate(90deg); - } - } .subsection-name, .section-name { padding-left: $s-4; } + &:hover { color: var(--title-foreground-color-hover); - .collapsed-shortcuts { - svg { - stroke: var(--title-foreground-color-hover); - } - } } } diff --git a/frontend/translations/en.po b/frontend/translations/en.po index cd42243ad3..e7445eadab 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -3903,8 +3903,8 @@ msgid "shortcuts.copy-props" msgstr "Copy properties" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:95 -msgid "shortcuts.create-component" -msgstr "Create component" +msgid "shortcuts.create-component-variant" +msgstr "Create component / variant" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:96 msgid "shortcuts.create-new-project" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index dca71130e1..934211ae8c 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -3902,8 +3902,8 @@ msgid "shortcuts.copy-props" msgstr "Copiar propiedades" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:95 -msgid "shortcuts.create-component" -msgstr "Crear componente" +msgid "shortcuts.create-component-variant" +msgstr "Crear componente / variante" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:96 msgid "shortcuts.create-new-project" From 2eed7444b74cce1df7fd542c25e4bca8d1d52ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 16 Sep 2025 15:07:38 +0200 Subject: [PATCH 3/3] :wrench: Add migration to automatically fix validation errors --- common/src/app/common/files/migrations.cljc | 19 ++++++++++++- common/src/app/common/files/validate.cljc | 30 ++++++++++----------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/common/src/app/common/files/migrations.cljc b/common/src/app/common/files/migrations.cljc index 1e1950f85e..2284f95e45 100644 --- a/common/src/app/common/files/migrations.cljc +++ b/common/src/app/common/files/migrations.cljc @@ -1568,6 +1568,22 @@ (-> data (update :pages-index d/update-vals update-page)))) +(defmethod migrate-data "0011-fix-invalid-text-touched-flags" + [data _] + (letfn [(fix-shape [shape] + (let [touched-groups (ctk/normal-touched-groups shape) + content-touched? (touched-groups :content-group) + text-touched? (or (touched-groups :text-content-text) + (touched-groups :text-content-attribute) + (touched-groups :text-content-structure))] + (if (and text-touched? (not content-touched?)) + (update shape :touched ctk/set-touched-group :content-group) + shape))) + + (update-page [page] + (d/update-when page :objects d/update-vals fix-shape))] + (-> data + (update :pages-index d/update-vals update-page)))) (def available-migrations (into (d/ordered-set) @@ -1635,4 +1651,5 @@ "0008-fix-library-colors-v4" "0009-clean-library-colors" "0009-add-partial-text-touched-flags" - "0010-fix-swap-slots-pointing-non-existent-shapes"])) + "0010-fix-swap-slots-pointing-non-existent-shapes" + "0011-fix-invalid-text-touched-flags"])) diff --git a/common/src/app/common/files/validate.cljc b/common/src/app/common/files/validate.cljc index a2e6bbe832..02887b5f5e 100644 --- a/common/src/app/common/files/validate.cljc +++ b/common/src/app/common/files/validate.cljc @@ -329,19 +329,19 @@ "This shape has children with the same swap slot" shape file page))) -#_(defn- check-valid-touched - "Validate that the text touched flags are coherent." - [shape file page] - (let [touched-groups (ctk/normal-touched-groups shape) - content-touched? (touched-groups :content-group) - text-touched? (or (touched-groups :text-content-text) - (touched-groups :text-content-attribute) - (touched-groups :text-content-structure))] +(defn- check-valid-touched + "Validate that the text touched flags are coherent." + [shape file page] + (let [touched-groups (ctk/normal-touched-groups shape) + content-touched? (touched-groups :content-group) + text-touched? (or (touched-groups :text-content-text) + (touched-groups :text-content-attribute) + (touched-groups :text-content-structure))] ;; For now we only check this combination, that has been reported in some bugs - (when (and text-touched? (not content-touched?)) - (report-error :invalid-text-touched - "This thape has text type touched but not content touched" - shape file page)))) + (when (and text-touched? (not content-touched?)) + (report-error :invalid-text-touched + "This thape has text type touched but not content touched" + shape file page)))) (defn- check-shape-main-root-top "Root shape of a top main instance: @@ -384,7 +384,7 @@ (check-component-ref shape file page libraries) (check-empty-swap-slot shape file page) (check-duplicate-swap-slot shape file page) - #_(check-valid-touched shape file page) ;; TODO: activate this again after we add a migration that repairs files automatically + (check-valid-touched shape file page) (run! #(check-shape % file page libraries :context :copy-top :library-exists library-exists) (:shapes shape)))) (defn- check-shape-copy-root-nested @@ -395,7 +395,7 @@ [shape file page libraries library-exists] (check-component-not-main-head shape file page libraries) (check-component-not-root shape file page) - #_(check-valid-touched shape file page) + (check-valid-touched shape file page) ;; We can have situations where the nested copy and the ancestor copy come from different libraries and some of them have been dettached ;; so we only validate the shape-ref if the ancestor is from a valid library (when library-exists @@ -418,7 +418,7 @@ (check-component-not-root shape file page) (check-component-ref shape file page libraries) (check-empty-swap-slot shape file page) - #_(check-valid-touched shape file page) + (check-valid-touched shape file page) (run! #(check-shape % file page libraries :context :copy-any) (:shapes shape))) (defn- check-shape-not-component