From a303df9c349e1c2c501403bb2fee86e32ad22cc6 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 27 Aug 2025 10:47:09 +0200 Subject: [PATCH 1/5] :recycle: Refactor right sidebar state management Also removing duplicated refs and improve efficiency of several other refs used on sidebar. --- CHANGES.md | 1 + .../data/workspace/get-file-blank.json | 6 +- .../workspace/get-file-fragment-blank.json | 2 +- .../get-file-fragment-gradient-limits.json | 2 +- .../workspace/get-file-fragment-tokens.json | 4 +- .../get-file-fragment-typography-tokens.json | 4 +- .../data/workspace/get-file-tokens.json | 8 +- .../workspace/get-file-typography-tokens.json | 10 +- frontend/playwright/ui/pages/WorkspacePage.js | 5 +- .../playwright/ui/specs/colorpicker.spec.js | 7 +- .../ui/specs/text-editor-v2.spec.js | 5 +- frontend/playwright/ui/specs/tokens.spec.js | 9 +- frontend/src/app/main/data/helpers.cljs | 20 +- .../main/data/workspace/text/shortcuts.cljs | 19 +- .../src/app/main/data/workspace/texts.cljs | 5 +- frontend/src/app/main/refs.cljs | 71 ++--- .../main/ui/ds/foundations/assets/icon.cljs | 29 +- .../app/main/ui/inspect/right_sidebar.cljs | 4 +- frontend/src/app/main/ui/viewer/inspect.cljs | 16 +- .../app/main/ui/workspace/context_menu.cljs | 9 +- .../main/ui/workspace/sidebar/layer_item.cljs | 11 +- .../main/ui/workspace/sidebar/options.cljs | 248 +++++++++++------- .../sidebar/options/menus/align.cljs | 15 +- .../workspace/sidebar/options/menus/bool.cljs | 70 +++-- .../sidebar/options/menus/exports.cljs | 63 +++-- .../sidebar/options/menus/layout_item.cljs | 1 + .../sidebar/options/shapes/bool.cljs | 14 +- .../sidebar/options/shapes/circle.cljs | 12 +- .../sidebar/options/shapes/frame.cljs | 21 +- .../sidebar/options/shapes/group.cljs | 13 +- .../sidebar/options/shapes/multiple.cljs | 9 +- .../sidebar/options/shapes/path.cljs | 26 +- .../sidebar/options/shapes/rect.cljs | 12 +- .../sidebar/options/shapes/svg_raw.cljs | 13 +- .../sidebar/options/shapes/text.cljs | 13 +- frontend/src/app/util/shape_icon.cljs | 3 +- 36 files changed, 473 insertions(+), 307 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 116e008ec9..a222eeaaec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,7 @@ ### :sparkles: New features & Enhancements +- Add efficiency enhancements to right sidebar [Github #7182](https://github.com/penpot/penpot/pull/7182) - Add defaults for artboard drawing [Taiga #494](https://tree.taiga.io/project/penpot/us/494?milestone=465047) - Continuous display of distances between elements when moving a layer with the keyboard [Taiga #1780](https://tree.taiga.io/project/penpot/us/1780) - New Number token - unitless values [Taiga #10936](https://tree.taiga.io/project/penpot/us/10936) diff --git a/frontend/playwright/data/workspace/get-file-blank.json b/frontend/playwright/data/workspace/get-file-blank.json index 9e05e3b50a..160137844c 100644 --- a/frontend/playwright/data/workspace/get-file-blank.json +++ b/frontend/playwright/data/workspace/get-file-blank.json @@ -29,10 +29,10 @@ "~:created-at": "~m1713536343369", "~:data": { "~:pages": [ - "~uc7ce0794-0992-8105-8004-38f28044384a" + "~u66697432-c33d-8055-8006-2c62cc084cad" ], "~:pages-index": { - "~uc7ce0794-0992-8105-8004-38f28044384a": { + "~u66697432-c33d-8055-8006-2c62cc084cad": { "~#penpot/pointer": [ "~ude58c8f6-c5c2-8196-8004-3df9e2e52d88", { @@ -55,4 +55,4 @@ } ] } -} \ No newline at end of file +} diff --git a/frontend/playwright/data/workspace/get-file-fragment-blank.json b/frontend/playwright/data/workspace/get-file-fragment-blank.json index 7760aaa927..02f0e289e0 100644 --- a/frontend/playwright/data/workspace/get-file-fragment-blank.json +++ b/frontend/playwright/data/workspace/get-file-fragment-blank.json @@ -91,7 +91,7 @@ } } }, - "~:id": "~uc7ce0794-0992-8105-8004-38f28044384a", + "~:id": "~u66697432-c33d-8055-8006-2c62cc084cad", "~:name": "Page 1" } } diff --git a/frontend/playwright/data/workspace/get-file-fragment-gradient-limits.json b/frontend/playwright/data/workspace/get-file-fragment-gradient-limits.json index f1c89c7922..16a238cb18 100644 --- a/frontend/playwright/data/workspace/get-file-fragment-gradient-limits.json +++ b/frontend/playwright/data/workspace/get-file-fragment-gradient-limits.json @@ -276,4 +276,4 @@ "~:id": "~u66697432-c33d-8055-8006-2c62cc084cad", "~:name": "Page 1" } -} \ No newline at end of file +} diff --git a/frontend/playwright/data/workspace/get-file-fragment-tokens.json b/frontend/playwright/data/workspace/get-file-fragment-tokens.json index c41deaef14..128f45d28d 100644 --- a/frontend/playwright/data/workspace/get-file-fragment-tokens.json +++ b/frontend/playwright/data/workspace/get-file-fragment-tokens.json @@ -1,6 +1,6 @@ { "~:id": "~u51e13852-1a8e-8037-8005-9eabb500f7c7", - "~:file-id": "~u51e13852-1a8e-8037-8005-9e9413a1f1f6", + "~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849", "~:created-at": "~m1737542758401", "~:data": { "~:options": {}, @@ -454,7 +454,7 @@ } } }, - "~:id": "~u51e13852-1a8e-8037-8005-9e9413a1f1f7", + "~:id": "~u66697432-c33d-8055-8006-2c62cc084cad", "~:name": "Page 1", "~:background": "#e8eae9", "~:guides": { diff --git a/frontend/playwright/data/workspace/get-file-fragment-typography-tokens.json b/frontend/playwright/data/workspace/get-file-fragment-typography-tokens.json index a0de35cfac..4752ca68f5 100644 --- a/frontend/playwright/data/workspace/get-file-fragment-typography-tokens.json +++ b/frontend/playwright/data/workspace/get-file-fragment-typography-tokens.json @@ -1,6 +1,6 @@ { "~:id": "~u021b87d4-813e-8066-8006-b36537098786", - "~:file-id": "~uef9b2783-804c-8017-8006-ae6f7eab52ad", + "~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849", "~:created-at": "~m1756113434655", "~:data": { "~:objects": { @@ -258,7 +258,7 @@ } } }, - "~:id": "~uef9b2783-804c-8017-8006-ae6f7eab52ae", + "~:id": "~u66697432-c33d-8055-8006-2c62cc084cad", "~:name": "Page 1" } } diff --git a/frontend/playwright/data/workspace/get-file-tokens.json b/frontend/playwright/data/workspace/get-file-tokens.json index 9b5303e64a..597aa2d55b 100644 --- a/frontend/playwright/data/workspace/get-file-tokens.json +++ b/frontend/playwright/data/workspace/get-file-tokens.json @@ -23,15 +23,17 @@ "~:revn": 36, "~:modified-at": "~m1737542758402", "~:vern": 0, - "~:id": "~u51e13852-1a8e-8037-8005-9e9413a1f1f6", + "~:id": "~uc7ce0794-0992-8105-8004-38f280443849", "~:is-shared": false, "~:version": 60, "~:project-id": "~u0df61468-6cbf-8067-8005-6b453ce996d0", "~:created-at": "~m1737536563847", "~:data": { - "~:pages": ["~u51e13852-1a8e-8037-8005-9e9413a1f1f7"], + "~:pages": [ + "~u66697432-c33d-8055-8006-2c62cc084cad" + ], "~:pages-index": { - "~u51e13852-1a8e-8037-8005-9e9413a1f1f7": { + "~u66697432-c33d-8055-8006-2c62cc084cad": { "~#penpot/pointer": [ "~u51e13852-1a8e-8037-8005-9eabb500f7c7", { diff --git a/frontend/playwright/data/workspace/get-file-typography-tokens.json b/frontend/playwright/data/workspace/get-file-typography-tokens.json index 746aeb132d..35ed171867 100644 --- a/frontend/playwright/data/workspace/get-file-typography-tokens.json +++ b/frontend/playwright/data/workspace/get-file-typography-tokens.json @@ -27,7 +27,7 @@ "~:revn": 133, "~:modified-at": "~m1756113434658", "~:vern": 0, - "~:id": "~uef9b2783-804c-8017-8006-ae6f7eab52ad", + "~:id": "~uc7ce0794-0992-8105-8004-38f280443849", "~:is-shared": false, "~:migrations": { "~#ordered-set": [] @@ -36,9 +36,11 @@ "~:project-id": "~u0df61468-6cbf-8067-8005-6b453ce996d0", "~:created-at": "~m1755780585133", "~:data": { - "~:pages": ["~uef9b2783-804c-8017-8006-ae6f7eab52ae"], + "~:pages": [ + "~u66697432-c33d-8055-8006-2c62cc084cad" + ], "~:pages-index": { - "~uef9b2783-804c-8017-8006-ae6f7eab52ae": { + "~u66697432-c33d-8055-8006-2c62cc084cad": { "~#penpot/pointer": [ "~u021b87d4-813e-8066-8006-b36537098786", { @@ -47,7 +49,7 @@ ] } }, - "~:id": "~uef9b2783-804c-8017-8006-ae6f7eab52ad", + "~:id": "~uc7ce0794-0992-8105-8004-38f280443849", "~:options": { "~:components-v2": true, "~:base-font-size": "16px" diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index 5afc536b9a..20a16ecdd8 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -159,7 +159,10 @@ export class WorkspacePage extends BaseWebSocketPage { "get-profiles-for-file-comments?file-id=*", "workspace/get-profile-for-file-comments.json", ); - await this.mockRPC(/get\-file\?/, "workspace/get-file-blank.json"); + await this.mockRPC( + /get\-file\?/, + "workspace/get-file-blank.json" + ); await this.mockRPC( "get-file-object-thumbnails?file-id=*", "workspace/get-file-object-thumbnails-blank.json", diff --git a/frontend/playwright/ui/specs/colorpicker.spec.js b/frontend/playwright/ui/specs/colorpicker.spec.js index 012bb52379..9521cdb06c 100644 --- a/frontend/playwright/ui/specs/colorpicker.spec.js +++ b/frontend/playwright/ui/specs/colorpicker.spec.js @@ -182,12 +182,17 @@ test("Gradient stops limit", async ({ page }) => { const workspacePage = new WorkspacePage(page); await workspacePage.mockConfigFlags(["enable-frontend-binary-fills"]); await workspacePage.setupEmptyFile(page); + await workspacePage.mockRPC( "get-file-fragment?file-id=*&fragment-id=*", "workspace/get-file-fragment-gradient-limits.json", ); - await workspacePage.goToWorkspace(); + await workspacePage.goToWorkspace({ + fileId: "c7ce0794-0992-8105-8004-38f280443849", + pageId: "66697432-c33d-8055-8006-2c62cc084cad" + }); + await workspacePage.clickLeafLayer("Rectangle"); const swatch = workspacePage.page.getByRole("button", { diff --git a/frontend/playwright/ui/specs/text-editor-v2.spec.js b/frontend/playwright/ui/specs/text-editor-v2.spec.js index 2b4559cadc..dec3b71a0b 100644 --- a/frontend/playwright/ui/specs/text-editor-v2.spec.js +++ b/frontend/playwright/ui/specs/text-editor-v2.spec.js @@ -15,7 +15,10 @@ test("BUG 11552 - Apply styles to the current caret", async ({ page }) => { "text-editor/update-file-11552.json", ); - await workspace.goToWorkspace(); + await workspace.goToWorkspace({ + fileId: "238a17e0-75ff-8075-8006-934586ea2230", + pageId: "238a17e0-75ff-8075-8006-934586ea2231", + }); await workspace.clickLeafLayer("Lorem ipsum"); await workspace.clickLeafLayer("Lorem ipsum"); diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index 0ef78107d0..e74397db68 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -20,7 +20,10 @@ const setupEmptyTokensFile = async (page) => { "workspace/update-file-create-rect.json", ); - await workspacePage.goToWorkspace(); + await workspacePage.goToWorkspace({ + fileId: "c7ce0794-0992-8105-8004-38f280443849", + pageId: "66697432-c33d-8055-8006-2c62cc084cad", + }); const tokensTabButton = page.getByRole("tab", { name: "Tokens" }); await tokensTabButton.click(); @@ -60,8 +63,8 @@ const setupTokensFile = async (page, options = {}) => { ); await workspacePage.goToWorkspace({ - fileId: "51e13852-1a8e-8037-8005-9e9413a1f1f6", - pageId: "51e13852-1a8e-8037-8005-9e9413a1f1f7", + fileId: "c7ce0794-0992-8105-8004-38f280443849", + pageId: "66697432-c33d-8055-8006-2c62cc084cad", }); const tokensTabButton = page.getByRole("tab", { name: "Tokens" }); diff --git a/frontend/src/app/main/data/helpers.cljs b/frontend/src/app/main/data/helpers.cljs index ee3d406c53..05237fcd1b 100644 --- a/frontend/src/app/main/data/helpers.cljs +++ b/frontend/src/app/main/data/helpers.cljs @@ -69,14 +69,18 @@ (process-selected objects selected nil)) ([objects selected {:keys [omit-blocked?] :or {omit-blocked? false}}] - (letfn [(selectable? [id] - (and (contains? objects id) - (or (not omit-blocked?) - (not (dm/get-in objects [id :blocked] false)))))] - (let [selected (->> selected (cfh/clean-loops objects))] - (into (d/ordered-set) - (filter selectable?) - selected))))) + (let [selectable? + (fn [id] + (and (contains? objects id) + (or (not omit-blocked?) + (not (dm/get-in objects [id :blocked] false))))) + + selected + (cfh/clean-loops objects selected)] + + (into (d/ordered-set) + (filter selectable?) + selected)))) (defn split-text-shapes "Split text shapes from non-text shapes" diff --git a/frontend/src/app/main/data/workspace/text/shortcuts.cljs b/frontend/src/app/main/data/workspace/text/shortcuts.cljs index 0379e7a398..93a02f5bca 100644 --- a/frontend/src/app/main/data/workspace/text/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/text/shortcuts.cljs @@ -16,7 +16,8 @@ [app.main.fonts :as fonts] [app.main.refs :as refs] [app.main.store :as st] - [cuerdas.core :as str])) + [cuerdas.core :as str] + [okulary.core :as l])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shortcuts @@ -111,7 +112,6 @@ :font-weight (:weight new-variant) :font-style (:style new-variant)}))) - (defn calculate-text-values [shape] (let [state-map (if (features/active-feature? @st/state "text-editor/v2") @@ -196,13 +196,26 @@ :else props))) +(def ^:private selected-shapes-with-children + "A derived state that resolves to a lazy sequence of all selected + shapes and its children." + (l/derived + (fn [{:keys [objects selected]}] + (let [xform (comp (remove nil?) + (mapcat #(cfh/get-children-ids objects %))) + shapes (into selected xform selected)] + (sequence (keep (d/getf objects)) shapes))) + ;; WORKAROUND: we should not use it here, but util we restructure + ;; this, the simplest way is just deref private var + @#'refs/selected-shapes-data)) + (defn- update-attrs-when-no-readonly [props] (let [undo-id (js/Symbol) can-edit? (:can-edit (deref refs/permissions)) read-only? (deref refs/workspace-read-only?) - text-shapes (->> (deref refs/selected-shapes-with-children) + text-shapes (->> (deref selected-shapes-with-children) (filter cfh/text-shape?) (not-empty)) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index e780226bd5..9c92f72f91 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -422,8 +422,9 @@ (txt/update-text-content shape txt/is-root-node? d/txt-merge attrs) (assoc shape :content (d/txt-merge {:type "root"} attrs)))) - shape-ids (cond (cfh/text-shape? shape) [id] - (cfh/group-shape? shape) (cfh/get-children-ids objects id))] + shape-ids + (cond (cfh/text-shape? shape) [id] + (cfh/group-shape? shape) (cfh/get-children-ids objects id))] (rx/of (dwsh/update-shapes shape-ids update-fn)))))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 73b41500e2..74b509ff6b 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -151,8 +151,8 @@ "All tokens related ephimeral state" (l/derived :workspace-tokens st/state)) -;; TODO: rename to workspace-selected (?) -;; Don't use directly from components, this is a proxy to improve performance of selected-shapes +;; WARNING: Don't use directly from components, this is a proxy to +;; improve performance of selected-shapes and (def ^:private selected-shapes-data (l/derived (fn [state] @@ -295,10 +295,8 @@ [page-id shape-id] (l/derived #(dsh/lookup-shape % page-id shape-id) st/state =)) -;; TODO: Looks like using the `=` comparator can be pretty expensive -;; on large pages, we are using this for some reason? (def workspace-page-objects - (l/derived dsh/lookup-page-objects st/state =)) + (l/derived dsh/lookup-page-objects st/state)) (def workspace-read-only? (l/derived :read-only? workspace-global)) @@ -366,36 +364,35 @@ (l/derived :workspace-v2-editor-state st/state)) (def workspace-modifiers - (l/derived :workspace-modifiers st/state =)) + (l/derived :workspace-modifiers st/state)) -(def workspace-modifiers-with-objects +(def ^:private workspace-modifiers-with-objects (l/derived (fn [state] - {:modifiers (:workspace-modifiers state) + {:modifiers (get state :workspace-modifiers) :objects (dsh/lookup-page-objects state)}) st/state (fn [a b] - (and (= (:modifiers a) (:modifiers b)) + (and (identical? (:modifiers a) (:modifiers b)) (identical? (:objects a) (:objects b)))))) (def workspace-frame-modifiers (l/derived (fn [{:keys [modifiers objects]}] - (->> modifiers - (reduce - (fn [result [id modifiers]] - (let [shape (get objects id) - frame-id (:frame-id shape)] - (cond - (cph/frame-shape? shape) - (assoc-in result [id id] modifiers) + (reduce (fn [result [id modifiers]] + (let [shape (get objects id) + frame-id (:frame-id shape)] + (cond + (cph/frame-shape? shape) + (assoc-in result [id id] modifiers) - (some? frame-id) - (assoc-in result [frame-id id] modifiers) + (some? frame-id) + (assoc-in result [frame-id id] modifiers) - :else - result))) - {}))) + :else + result))) + {} + modifiers)) workspace-modifiers-with-objects)) (defn workspace-modifiers-by-frame-id @@ -408,32 +405,14 @@ (defn select-bool-children [id] (l/derived #(dsh/select-bool-children % id) st/state =)) -(def selected-data - (l/derived #(let [selected (dsh/lookup-selected %) - objects (dsh/lookup-page-objects %)] - (hash-map :selected selected - :objects objects)) - st/state =)) - (defn is-child-selected? [id] - (letfn [(selector [{:keys [selected objects]}] - (let [children (cph/get-children-ids objects id)] - (some #(contains? selected %) children)))] - (l/derived selector selected-data =))) - -(def selected-objects - (letfn [(selector [{:keys [selected objects]}] - (into [] (keep (d/getf objects)) selected))] - (l/derived selector selected-data =))) - -(def selected-shapes-with-children - (letfn [(selector [{:keys [selected objects]}] - (let [xform (comp (remove nil?) - (mapcat #(cph/get-children-ids objects %))) - shapes (into selected xform selected)] - (mapv (d/getf objects) shapes)))] - (l/derived selector selected-data =))) + (l/derived + (fn [{:keys [selected objects]}] + (let [children (cph/get-children-ids objects id)] + (some #(contains? selected %) children))) + selected-shapes-data + =)) (def workspace-focus-selected (l/derived :workspace-focus-selected st/state)) diff --git a/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs b/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs index f4b0474c1f..0ca9fbeb59 100644 --- a/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs +++ b/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs @@ -294,10 +294,12 @@ (def ^:icon-id view-as-list "view-as-list") (def ^:icon-id wrap "wrap") -(def icon-list "A collection of all icons" (collect-icons)) +(def icon-list + "A collection of all icons" + (collect-icons)) -(def ^:private icon-size-m 16) -(def ^:private icon-size-s 12) +(def ^:private ^:const icon-size-m 16) +(def ^:private ^:const icon-size-s 12) (def ^:private schema:icon [:map @@ -309,9 +311,18 @@ (mf/defc icon* {::mf/schema schema:icon} [{:keys [icon-id size class] :rest props}] - (let [class (dm/str (or class "") " " (stl/css :icon)) - props (mf/spread-props props {:class class :width icon-size-m :height icon-size-m}) - size-px (cond (= size "s") icon-size-s :else icon-size-m) - offset (/ (- icon-size-m size-px) 2)] - [:> "svg" props - [:use {:href (dm/str "#icon-" icon-id) :width size-px :height size-px :x offset :y offset}]])) + (let [props (mf/spread-props props + {:class [class (stl/css :icon)] + :width icon-size-m + :height icon-size-m}) + size-px (if (= size "s") + icon-size-s + icon-size-m) + offset (/ (- icon-size-m size-px) 2)] + + [:> :svg props + [:use {:href (dm/str "#icon-" icon-id) + :width size-px + :height size-px + :x offset + :y offset}]])) diff --git a/frontend/src/app/main/ui/inspect/right_sidebar.cljs b/frontend/src/app/main/ui/inspect/right_sidebar.cljs index 4c25d5c3cc..3b3d5d37b4 100644 --- a/frontend/src/app/main/ui/inspect/right_sidebar.cljs +++ b/frontend/src/app/main/ui/inspect/right_sidebar.cljs @@ -39,11 +39,11 @@ (assoc id {:id id :data local}))))) -(mf/defc right-sidebar +(mf/defc right-sidebar* [{:keys [frame page objects file selected shapes page-id file-id share-id from on-change-section on-expand] :or {from :viewer}}] (let [color-space* (mf/use-state "hex") - color-space (deref color-space*) + color-space (deref color-space*) section (mf/use-state #(if (contains? cf/flags :inspect-styles) :styles :info)) objects (or objects (:objects page)) diff --git a/frontend/src/app/main/ui/viewer/inspect.cljs b/frontend/src/app/main/ui/viewer/inspect.cljs index 0093292edd..36a9f83239 100644 --- a/frontend/src/app/main/ui/viewer/inspect.cljs +++ b/frontend/src/app/main/ui/viewer/inspect.cljs @@ -14,7 +14,7 @@ [app.main.ui.hooks.resize :refer [use-resize-hook]] [app.main.ui.inspect.left-sidebar :refer [left-sidebar]] [app.main.ui.inspect.render :refer [render-frame-svg]] - [app.main.ui.inspect.right-sidebar :refer [right-sidebar]] + [app.main.ui.inspect.right-sidebar :refer [right-sidebar*]] [app.util.dom :as dom] [app.util.keyboard :as kbd] [goog.events :as events] @@ -112,10 +112,10 @@ :on-pointer-down on-pointer-down :on-lost-pointer-capture on-lost-pointer-capture :on-pointer-move on-pointer-move}]) - [:& right-sidebar {:frame frame - :selected (:selected local) - :page page - :file file - :on-change-section handle-change-section - :on-expand handle-expand - :share-id share-id}]]])) + [:> right-sidebar* {:frame frame + :selected (:selected local) + :page page + :file file + :on-change-section handle-change-section + :on-expand handle-expand + :share-id share-id}]]])) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 33a5ff7030..7d7cee43ea 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -628,11 +628,10 @@ (mf/defc shape-context-menu* {::mf/wrap [mf/memo] - ::mf/private true - ::mf/props :obj} + ::mf/private true} [{:keys [mdata]}] (let [{:keys [disable-booleans disable-flatten]} mdata - shapes (mf/deref refs/selected-objects) + shapes (mf/deref refs/selected-shapes) is-not-variant-container? (->> shapes (d/seek #(not (ctk/is-variant-container? %)))) props (mf/props {:shapes shapes @@ -828,8 +827,8 @@ (if ^boolean read-only? [:> viewport-context-menu* {:mdata mdata}] (case (:kind mdata) - :shape [:> shape-context-menu* {:mdata mdata}] - :page [:> page-item-context-menu* {:mdata mdata}] + :shape [:> shape-context-menu* {:mdata mdata}] + :page [:> page-item-context-menu* {:mdata mdata}] :grid-track [:> grid-track-context-menu* {:mdata mdata}] :grid-cells [:> grid-cells-context-menu* {:mdata mdata}] [:> viewport-context-menu* {:mdata mdata}]))]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs index b4cd1fcd00..3f2024e8b8 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs @@ -35,16 +35,14 @@ [rumext.v2 :as mf])) (mf/defc layer-item-inner - {::mf/wrap-props false - ::mf/forward-ref true} - [{:keys [item depth parent-size name-ref children + {::mf/wrap-props false} + [{:keys [item depth parent-size name-ref children ref ;; Flags read-only? highlighted? selected? component-tree? filtered? expanded? dnd-over? dnd-over-top? dnd-over-bot? hide-toggle? ;; Callbacks on-select-shape on-context-menu on-pointer-enter on-pointer-leave on-zoom-to-selected - on-toggle-collapse on-enable-drag on-disable-drag on-toggle-visibility on-toggle-blocking]} - dref] + on-toggle-collapse on-enable-drag on-disable-drag on-toggle-visibility on-toggle-blocking]}] (let [id (:id item) name (:name item) @@ -67,9 +65,10 @@ component (ctkl/get-component data (:component-id item)) variant-properties (:variant-properties component) icon-shape (usi/get-shape-icon item)] + [:* [:div {:id id - :ref dref + :ref ref :on-click on-select-shape :on-context-menu on-context-menu :data-testid "layer-row" diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index d88d29b019..f2811ba786 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -12,6 +12,7 @@ [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] [app.common.types.shape.layout :as ctl] + [app.main.data.helpers :as dsh] [app.main.data.workspace :as udw] [app.main.data.workspace.common :as dwc] [app.main.refs :as refs] @@ -23,7 +24,6 @@ [app.main.ui.workspace.sidebar.options.menus.align :refer [align-options*]] [app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options*]] [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]] - [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu]] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.interactions :refer [interactions-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-container :as layout-container] @@ -38,13 +38,15 @@ [app.main.ui.workspace.sidebar.options.shapes.svg-raw :as svg-raw] [app.main.ui.workspace.sidebar.options.shapes.text :as text] [app.util.i18n :as i18n :refer [tr]] + [okulary.core :as l] + [rumext.v2 :as mf])) ;; --- Options -(mf/defc shape-options* - {::mf/wrap [#(mf/throttle % 60)]} - [{:keys [shape shapes-with-children page-id file-id libraries] :as props}] +(mf/defc single-shape-options* + {::mf/private true} + [{:keys [shape page-id file-id libraries] :as props}] (let [shape-type (dm/get-prop shape :type) shape-id (dm/get-prop shape :id) @@ -53,45 +55,83 @@ shape (gsh/transform-shape shape modifiers)] - [:* - (case shape-type - :frame [:> frame/options* props] - :group [:> group/options* {:shape shape :shape-with-children shapes-with-children :file-id file-id :libraries libraries}] - :text [:> text/options* {:shape shape :file-id file-id :libraries libraries}] - :rect [:> rect/options* {:shape shape}] - :circle [:> circle/options* {:shape shape}] - :path [:> path/options* {:shape shape}] - :svg-raw [:> svg-raw/options* {:shape shape}] - :bool [:> bool/options* {:shape shape}] - nil) - [:& exports-menu - {:ids [(:id shape)] - :values (select-keys shape [:exports]) - :shape shape - :page-id page-id - :file-id file-id}]])) + (case shape-type + :frame [:> frame/options* props] + :group [:> group/options* props] + :text [:> text/options* {:shape shape :file-id file-id :page-id page-id :libraries libraries}] + :rect [:> rect/options* {:shape shape :file-id file-id :page-id page-id}] + :circle [:> circle/options* {:shape shape :file-id file-id :page-id page-id}] + :path [:> path/options* {:shape shape :file-id file-id :page-id page-id}] + :svg-raw [:> svg-raw/options* {:shape shape :file-id file-id :page-id page-id}] + :bool [:> bool/options* {:shape shape :file-id file-id :page-id page-id}] + nil))) + +(mf/defc shape-options* + {::mf/wrap [#(mf/throttle % 100)] + ::mf/private true} + [{:keys [shapes shapes-with-children selected page-id file-id libraries]}] + (if (= 1 (count selected)) + [:> single-shape-options* + {:page-id page-id + :file-id file-id + :libraries libraries + :shape (first shapes) + :shapes-with-children shapes-with-children}] + [:> multiple/options* + {:shapes-with-children shapes-with-children + :shapes shapes + :page-id page-id + :file-id file-id + :libraries libraries}])) (mf/defc specialized-panel* - {::mf/wrap [mf/memo]} + {::mf/private true} [{:keys [panel]}] (when (= (:type panel) :component-swap) [:& component-menu {:shapes (:shapes panel) :swap-opened? true}])) (mf/defc design-menu* - {::mf/wrap [mf/memo]} - [{:keys [selected objects page-id file-id selected-shapes shapes-with-children]}] - (let [sp-panel (mf/deref refs/specialized-panel) - drawing (mf/deref refs/workspace-drawing) - libraries (mf/deref refs/libraries) - edition (mf/deref refs/selected-edition) - edit-grid? (ctl/grid-layout? objects edition) - grid-edition (mf/deref refs/workspace-grid-edition) - selected-cells (->> (dm/get-in grid-edition [edition :selected]) - (map #(dm/get-in objects [edition :layout-grid-cells %])))] + {::mf/private true} + [{:keys [selected objects page-id file-id shapes]}] + (let [sp-panel (mf/deref refs/specialized-panel) + drawing (mf/deref refs/workspace-drawing) + edition (mf/deref refs/selected-edition) + + files + (mf/deref refs/files) + + libraries + (mf/with-memo [files file-id] + (refs/select-libraries files file-id)) + + edit-grid? + (mf/with-memo [objects edition] + (ctl/grid-layout? objects edition)) + + grid-edition + (mf/deref refs/workspace-grid-edition) + + selected-cells + (->> (dm/get-in grid-edition [edition :selected]) + (map #(dm/get-in objects [edition :layout-grid-cells %]))) + + shapes-with-children + (mf/with-memo [selected objects shapes] + (let [xform (comp (remove nil?) + (mapcat #(cfh/get-children-ids objects %))) + selected (into selected xform selected)] + (sequence (keep (d/getf objects)) selected))) + + + total-selected + (count selected)] [:div {:class (stl/css :element-options :design-options)} - [:> align-options*] - [:> bool-options*] + [:> align-options* {:shapes shapes + :objects objects}] + [:> bool-options* {:total-selected total-selected + :shapes shapes + :shapes-with-children shapes-with-children}] (cond (and edit-grid? (d/not-empty? selected-cells)) @@ -104,67 +144,71 @@ {:ids [edition] :values (get objects edition)}] - (not (nil? sp-panel)) + (some? sp-panel) [:> specialized-panel* {:panel sp-panel}] (d/not-empty? drawing) [:> drawing/drawing-options* {:drawing-state drawing}] - (= 0 (count selected)) + (zero? total-selected) [:> page/options*] - (= 1 (count selected)) - [:> shape-options* - {:shape (first selected-shapes) - :page-id page-id - :file-id file-id - :libraries libraries - :shapes-with-children shapes-with-children}] - :else - [:> multiple/options* - {:shapes-with-children shapes-with-children - :shapes selected-shapes + [:> shape-options* + {:shapes shapes + :shapes-with-children shapes-with-children :page-id page-id :file-id file-id + :selected selected :libraries libraries}])])) -;; FIXME: need optimizations +(mf/defc inspect-tab* + {::mf/private true} + [{:keys [objects shapes] :as props}] + (let [frame + (cfh/get-frame objects (first shapes)) + + props + (mf/spread-props props + {:frame frame + :from :workspace})] + + [:> hrs/right-sidebar* props])) + +(def ^:private options-tabs + [{:label (tr "workspace.options.design") + :id "design"} + {:label (tr "workspace.options.prototype") + :id "prototype"} + {:label (tr "workspace.options.inspect") + :id "inspect"}]) + +(defn- on-option-tab-change + [mode] + (let [mode (keyword mode)] + (st/emit! (udw/set-options-mode mode)) + (if (= mode :inspect) + (st/emit! :interrupt (dwc/set-workspace-read-only true)) + (st/emit! :interrupt (dwc/set-workspace-read-only false))))) + (mf/defc options-content* - {::mf/memo true - ::mf/private true} - [{:keys [selected shapes shapes-with-children page-id file-id on-change-section on-expand]}] - (let [objects (mf/deref refs/workspace-page-objects) - permissions (mf/use-ctx ctx/permissions) + {::mf/private true} + [{:keys [objects selected page-id file-id on-change-section on-expand]}] + (let [permissions + (mf/use-ctx ctx/permissions) - selected-shapes (into [] (keep (d/getf objects)) selected) - first-selected-shape (first selected-shapes) - shape-parent-frame (cfh/get-frame objects (:frame-id first-selected-shape)) + options-mode + (mf/deref refs/options-mode-global) - options-mode (mf/deref refs/options-mode-global) - - on-change-tab - (fn [options-mode] - (let [options-mode (keyword options-mode)] - (st/emit! (udw/set-options-mode options-mode)) - (if (= options-mode :inspect) - (st/emit! :interrupt (dwc/set-workspace-read-only true)) - (st/emit! :interrupt (dwc/set-workspace-read-only false))))) - - tabs - (mf/with-memo [] - [{:label (tr "workspace.options.design") - :id "design"} - {:label (tr "workspace.options.prototype") - :id "prototype"} - {:label (tr "workspace.options.inspect") - :id "inspect"}])] + shapes + (mf/with-memo [selected objects] + (sequence (keep (d/getf objects)) selected))] [:div {:class (stl/css :tool-window)} (if (:can-edit permissions) - [:> tab-switcher* {:tabs tabs - :on-change on-change-tab + [:> tab-switcher* {:tabs options-tabs + :on-change on-option-tab-change :selected (name options-mode) :class (stl/css :options-tab-switcher)} (case options-mode @@ -174,46 +218,46 @@ :inspect [:div {:class (stl/css :element-options :inspect-options)} - [:& hrs/right-sidebar {:page-id page-id - :objects objects - :file-id file-id - :frame shape-parent-frame - :shapes selected-shapes - :on-change-section on-change-section - :on-expand on-expand - :from :workspace}]] + [:> inspect-tab* {:page-id page-id + :file-id file-id + :objects objects + :selected selected + :shapes shapes + :on-change-section on-change-section + :on-expand on-expand}]] :design [:> design-menu* {:selected selected :objects objects :page-id page-id :file-id file-id - :selected-shapes selected-shapes - :shapes-with-children shapes-with-children}])] + :shapes shapes}])] - ;; FIXME: Reuse tab??? [:div {:class (stl/css :element-options :inspect-options :read-only)} - [:& hrs/right-sidebar {:page-id page-id - :objects objects - :file-id file-id - :frame shape-parent-frame - :shapes selected-shapes - :on-change-section on-change-section - :on-expand on-expand - :from :workspace}]])])) + [:> inspect-tab* {:page-id page-id + :file-id file-id + :objects objects + :selected selected + :shapes shapes + :on-change-section on-change-section + :on-expand on-expand}]])])) -;; TODO: this need optimizations, selected-objects and -;; selected-objects-with-children are derefed always but they only -;; need on multiple selection in majority of cases +(defn- make-page-objects-ref + [file-id page-id] + (l/derived #(dsh/lookup-page-objects % file-id page-id) st/state)) (mf/defc options-toolbox* {::mf/memo true} [{:keys [page-id file-id section selected on-change-section on-expand]}] - (let [shapes (mf/deref refs/selected-objects) - shapes-with-children (mf/deref refs/selected-shapes-with-children)] - [:> options-content* {:shapes shapes + (let [objects-ref + (mf/with-memo [page-id file-id] + (make-page-objects-ref file-id page-id)) + + objects + (mf/deref objects-ref)] + + [:> options-content* {:objects objects :selected selected - :shapes-with-children shapes-with-children :file-id file-id :page-id page-id :section section diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs index b4f97f78ec..f0c0778a64 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs @@ -9,7 +9,6 @@ (:require [app.main.data.workspace :as dw] [app.main.data.workspace.shortcuts :as sc] - [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -17,14 +16,12 @@ [rumext.v2 :as mf])) (mf/defc align-options* - {::mf/memo true} - [] - (let [selected (mf/deref refs/selected-shapes) - ;; don't need to watch objects, only read the value - objects (deref refs/workspace-page-objects) + [{:keys [shapes objects]}] + (let [disabled-align + (not (dw/can-align? shapes objects)) - disabled-align (not (dw/can-align? selected objects)) - disabled-distribute (not (dw/can-distribute? selected)) + disabled-distribute + (not (dw/can-distribute? shapes)) align-objects (mf/use-fn @@ -42,7 +39,7 @@ (keyword))] (st/emit! (dw/distribute-objects value)))))] - (when (not (and disabled-align disabled-distribute)) + (when-not (and disabled-align disabled-distribute) [:div {:class (stl/css :align-options)} [:div {:class (stl/css :align-group-horizontal)} [:button {:class (stl/css-case :align-button true diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs index 5d6359a082..5e61e91514 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs @@ -8,11 +8,12 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.files.helpers :as cfh] [app.main.data.workspace.bool :as dwb] [app.main.data.workspace.path.shapes-to-path :as dwps] [app.main.data.workspace.shortcuts :as sc] [app.main.features :as features] - [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.icons :as i] @@ -23,48 +24,61 @@ (i/icon-xref :boolean-flatten (stl/css :flatten-icon))) (mf/defc bool-options* - {::mf/memo true} - [] - (let [selected (mf/deref refs/selected-objects) - head (first selected) - selected-with-children (mf/deref refs/selected-shapes-with-children) - has-invalid-shapes? (->> selected-with-children - (some (comp #{:frame :text} :type))) - is-group? (and (some? head) (= :group (:type head))) - is-bool? (and (some? head) (= :bool (:type head))) - head-bool-type (and (some? head) is-bool? (:bool-type head)) + [{:keys [total-selected shapes shapes-with-children]}] + (let [head (first shapes) + head-id (dm/get-prop head :id) - first-not-group-like? - (and (= (count selected) 1) - (not (contains? #{:group :bool} (:type (first selected))))) + is-group? (cfh/group-shape? head) + is-bool? (cfh/bool-shape? head) + + head-bool-type + (and is-bool? (get head :bool-type)) + + render-wasm-enabled? + (features/use-feature "render-wasm/v1") + + has-invalid-shapes? + (if render-wasm-enabled? + false + (some (fn [shape] + (or (cfh/frame-shape? shape) + (cfh/text-shape? shape))) + shapes-with-children)) + + head-not-group-like? + (and (= 1 total-selected) + (not is-group?) + (not is-bool?)) disabled-bool-btns - (if (features/active-feature? @st/state "render-wasm/v1") + (if render-wasm-enabled? false - (or (empty? selected) has-invalid-shapes? first-not-group-like?)) + (or (zero? total-selected) + has-invalid-shapes? + head-not-group-like?)) disabled-flatten - (if (features/active-feature? @st/state "render-wasm/v1") + (if render-wasm-enabled? false - (or (empty? selected) has-invalid-shapes?)) + (or (zero? total-selected) + has-invalid-shapes?)) - set-bool + on-change (mf/use-fn - (mf/deps selected is-group? is-bool?) + (mf/deps total-selected is-group? is-bool? head-id head-bool-type) (fn [bool-type] (let [bool-type (keyword bool-type)] - (cond - (> (count selected) 1) + (> total-selected 1) (st/emit! (dwb/create-bool bool-type)) - (and (= (count selected) 1) is-group?) - (st/emit! (dwb/group-to-bool (:id head) bool-type)) + (and (= total-selected 1) is-group?) + (st/emit! (dwb/group-to-bool head-id bool-type)) - (and (= (count selected) 1) is-bool?) + (and (= total-selected 1) is-bool?) (if (= head-bool-type bool-type) - (st/emit! (dwb/bool-to-group (:id head))) - (st/emit! (dwb/change-bool-type (:id head) bool-type))))))) + (st/emit! (dwb/bool-to-group head-id)) + (st/emit! (dwb/change-bool-type head-id bool-type))))))) flatten-objects (mf/use-fn #(st/emit! (dwps/convert-selected-to-path)))] @@ -74,7 +88,7 @@ [:div {:class (stl/css :bool-group)} [:& radio-buttons {:selected (d/name head-bool-type) :class (stl/css :boolean-radio-btn) - :on-change set-bool + :on-change on-change :name "bool-options"} [:& radio-button {:icon i/boolean-union :value "union" 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 dd486cfece..b29de7957e 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 @@ -9,7 +9,6 @@ (:require [app.common.data :as d] [app.main.data.exports.assets :as de] - [app.main.data.helpers :as dsh] [app.main.data.workspace.shapes :as dwsh] [app.main.refs :as refs] [app.main.store :as st] @@ -26,29 +25,55 @@ "Shape attrs that corresponds to exports. Used in other namespaces." [:exports]) -(mf/defc exports-menu - {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "page-id" "file-id"]))]} - [{:keys [ids type values page-id file-id] :as props}] - (let [exports (:exports values []) - has-exports? (or (= :multiple exports) (some? (seq exports))) +(defn- check-exports-menu-props + [old-props new-props] + (and (identical? (unchecked-get old-props "ids") + (unchecked-get new-props "ids")) + (identical? (unchecked-get old-props "type") + (unchecked-get new-props "type")) + (identical? (unchecked-get old-props "pageId") + (unchecked-get new-props "pageId")) + (identical? (unchecked-get old-props "fileId") + (unchecked-get new-props "fileId")) - comp-state* (mf/use-state true) - open? (deref comp-state*) + ;; NOTE: we explicitly ignore "shapes" prop and use values for + ;; track if the "value" changes (checking by value equality); + ;; this prevents rerender the component when no real change is + ;; made to exports + (= (unchecked-get old-props "values") + (unchecked-get new-props "values")))) - toggle-content (mf/use-fn #(swap! comp-state* not)) +(mf/defc exports-menu* + {::mf/wrap [#(mf/memo' % check-exports-menu-props)]} + [{:keys [ids type shapes values file-id page-id]}] - state (mf/deref refs/export) - in-progress? (:in-progress state) + (let [exports (get values :exports []) + open* (mf/use-state true) + open? (deref open*) - shapes-with-exports (->> (dsh/lookup-shapes @st/state ids) - (filter #(pos? (count (:exports %))))) + state (mf/deref refs/export) - sname (when (seqable? exports) - (let [sname (-> shapes-with-exports first :name) - suffix (-> exports first :suffix)] - (cond-> sname - (and (= 1 (count exports)) (some? suffix)) - (str suffix)))) + in-progress? + (get state :in-progress) + + has-exports? + (or (= :multiple exports) + (some? (seq exports))) + + toggle-content + (mf/use-fn #(swap! open* not)) + + shapes-with-exports + (mf/with-memo [shapes] + (filter (comp seq :exports) shapes)) + + sname + (when (seqable? exports) + (let [sname (-> shapes-with-exports first :name) + suffix (-> exports first :suffix)] + (cond-> sname + (and (= 1 (count exports)) (some? suffix)) + (str suffix)))) scale-enabled? (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index f76c6a06fb..4a83f2b0b5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -309,6 +309,7 @@ :title "Align self end" :id "align-self-end"}]]) + (mf/defc layout-item-menu {::mf/memo #{:ids :values :type :is-layout-child? :is-grid-parent :is-flex-parent? :is-grid-layout? :is-flex-layout?} ::mf/props :obj} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs index 5fb56162b2..bddea47dcb 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs @@ -11,6 +11,7 @@ [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] @@ -22,7 +23,7 @@ [rumext.v2 :as mf])) (mf/defc options* - [{:keys [shape] :as props}] + [{:keys [shape file-id page-id]}] (let [id (dm/get-prop shape :id) type (dm/get-prop shape :type) ids (mf/with-memo [id] [id]) @@ -124,4 +125,13 @@ [:> shadow-menu* {:ids ids :values (get shape :shadow)}] [:& blur-menu {:ids ids - :values (select-keys shape [:blur])}]])) + :values (select-keys shape [:blur])}] + + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]])) + + diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs index de2524a82e..4ef21adcdd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs @@ -11,6 +11,7 @@ [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] @@ -23,7 +24,7 @@ [rumext.v2 :as mf])) (mf/defc options* - [{:keys [shape] :as props}] + [{:keys [shape file-id page-id]}] (let [id (dm/get-prop shape :id) type (dm/get-prop shape :type) ids (mf/with-memo [id] [id]) @@ -123,4 +124,11 @@ [:& blur-menu {:ids ids :values (select-keys shape [:blur])}] [:& svg-attrs-menu {:ids ids - :values (select-keys shape [:svg-attrs])}]])) + :values (select-keys shape [:svg-attrs])}] + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]])) + 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 29d51767dd..b676ef0b52 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 @@ -15,6 +15,7 @@ [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]] [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu variant-menu*]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] @@ -27,17 +28,11 @@ [rumext.v2 :as mf])) (mf/defc options* - [{:keys [shape file-id shapes-with-children libraries] :as props}] + [{:keys [shape shapes-with-children libraries file-id page-id] :as props}] (let [shape-id (dm/get-prop shape :id) shape-type (dm/get-prop shape :type) - - ids - (mf/with-memo [shape-id] - [shape-id]) - - shapes - (mf/with-memo [shape] - [shape]) + ids (mf/with-memo [shape-id] [shape-id]) + shapes (mf/with-memo [shape] [shape]) stroke-values (select-keys shape stroke-attrs) @@ -160,4 +155,10 @@ [:> shadow-menu* {:ids ids :values (get shape :shadow)}] [:& blur-menu {:ids ids :values (select-keys shape [:blur])}] - [:& frame-grid {:shape shape}]])) + [:& frame-grid {:shape shape}] + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs index a7adc2aeda..13289a0bb3 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs @@ -14,6 +14,7 @@ [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-menu]] @@ -29,7 +30,7 @@ (mf/defc options* {::mf/wrap [mf/memo]} - [{:keys [shape shapes-with-children libraries file-id] :as props}] + [{:keys [shape shapes-with-children libraries file-id page-id]}] (let [id (dm/get-prop shape :id) type (dm/get-prop shape :type) @@ -157,6 +158,14 @@ [:& ot/text-menu {:type type :ids text-ids :values text-values}]) (when-not (empty? svg-values) - [:& svg-attrs-menu {:ids ids :values svg-values}])])) + [:& svg-attrs-menu {:ids ids :values svg-values}]) + + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]])) + diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 29628903c7..6d5e5cd0cf 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -23,7 +23,7 @@ [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]] [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] - [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu*]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] @@ -485,4 +485,9 @@ [:& blur-menu {:type type :ids blur-ids :values blur-values}]) (when-not (empty? exports-ids) - [:& exports-menu {:type type :ids exports-ids :values exports-values :page-id page-id :file-id file-id}])])) + [:> exports-menu* {:type type + :ids exports-ids + :shapes shapes + :values exports-values + :page-id page-id + :file-id file-id}])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs index 8ad6e802ea..bade487724 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs @@ -11,6 +11,7 @@ [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] @@ -23,17 +24,11 @@ [rumext.v2 :as mf])) (mf/defc options* - [{:keys [shape] :as props}] - (let [ids - (mf/with-memo [shape] - [(dm/get-prop shape :id)]) - - shapes - (mf/with-memo [shape] - [shape]) - - type - (dm/get-prop shape :type) + [{:keys [shape file-id page-id]}] + (let [id (dm/get-prop shape :id) + type (dm/get-prop shape :type) + ids (mf/with-memo [id] [id]) + shapes (mf/with-memo [shape] [shape]) measure-values (select-keys shape measure-attrs) @@ -131,4 +126,11 @@ [:& blur-menu {:ids ids :values (select-keys shape [:blur])}] [:& svg-attrs-menu {:ids ids - :values (select-keys shape [:svg-attrs])}]])) + :values (select-keys shape [:svg-attrs])}] + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]])) + diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs index 75d67efe29..9b5e62fd31 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs @@ -11,6 +11,7 @@ [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] @@ -23,7 +24,7 @@ [rumext.v2 :as mf])) (mf/defc options* - [{:keys [shape]}] + [{:keys [shape file-id page-id]}] (let [id (dm/get-prop shape :id) type (dm/get-prop shape :type) ids (mf/with-memo [id] [id]) @@ -127,4 +128,11 @@ :values (select-keys shape [:blur])}] [:& svg-attrs-menu {:ids ids - :values (select-keys shape [:svg-attrs])}]])) + :values (select-keys shape [:svg-attrs])}] + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]])) + diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs index e0af0d6d07..faba956f2b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs @@ -13,6 +13,7 @@ [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] @@ -87,8 +88,7 @@ stroke-values)) (mf/defc options* - {::mf/wrap [mf/memo]} - [{:keys [shape] :as props}] + [{:keys [shape file-id page-id]}] (let [id (dm/get-prop shape :id) type (dm/get-prop shape :type) @@ -196,4 +196,11 @@ :values (select-keys shape [:blur])}] [:& svg-attrs-menu {:ids ids - :values (select-keys shape [:svg-attrs])}]]))) + :values (select-keys shape [:svg-attrs])}] + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]]))) + diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs index 8e344ad41d..0641652975 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs @@ -16,6 +16,7 @@ [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] @@ -28,7 +29,7 @@ [rumext.v2 :as mf])) (mf/defc options* - [{:keys [shape file-id libraries] :as props}] + [{:keys [shape libraries file-id page-id]}] (let [id (dm/get-prop shape :id) type (dm/get-prop shape :type) ids (mf/with-memo [id] [id]) @@ -186,4 +187,12 @@ [:& blur-menu {:ids ids - :values (select-keys shape [:blur])}]])) + :values (select-keys shape [:blur])}] + + [:> exports-menu* {:type type + :ids ids + :shapes shapes + :values (select-keys shape exports-attrs) + :page-id page-id + :file-id file-id}]])) + diff --git a/frontend/src/app/util/shape_icon.cljs b/frontend/src/app/util/shape_icon.cljs index bb8bc3f5ba..dcc484184b 100644 --- a/frontend/src/app/util/shape_icon.cljs +++ b/frontend/src/app/util/shape_icon.cljs @@ -14,10 +14,11 @@ (defn- get-bool-icon "Returns the icon for a boolean shape" [shape] - (case (:bool-type shape) + (case (get shape :bool-type) :difference "boolean-difference" :exclude "boolean-exclude" :intersection "boolean-intersection" + :union "boolean-union" nil)) (defn- get-frame-icon From a2c3208af9aa53c23910bfc13db733c1ec31532a Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Aug 2025 09:35:19 +0200 Subject: [PATCH 2/5] :bug: Fix regression on not updating measures ui on moving frames --- frontend/src/app/main/ui/workspace/sidebar/options.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index f2811ba786..37510ba166 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -53,7 +53,8 @@ modifiers (mf/deref refs/workspace-modifiers) modifiers (dm/get-in modifiers [shape-id :modifiers]) - shape (gsh/transform-shape shape modifiers)] + shape (gsh/transform-shape shape modifiers) + props (mf/spread-props props {:shape shape})] (case shape-type :frame [:> frame/options* props] From f8bc6e12a99e218abdd9fbfcf20521ebac124117 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Aug 2025 09:39:57 +0200 Subject: [PATCH 3/5] :zap: Improve efficiency of border radius menu --- .../sidebar/options/menus/border_radius.cljs | 28 +++++++++++++++---- .../sidebar/options/menus/measures.cljs | 6 +--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs index 3901b56866..f41eba18f6 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs @@ -14,23 +14,39 @@ [potok.v2.core :as ptk] [rumext.v2 :as mf])) -(defn all-equal? +(defn- all-equal? [shape] (= (:r1 shape) (:r2 shape) (:r3 shape) (:r4 shape))) +(defn- check-border-radius-menu-props + [old-props new-props] + (let [old-values (unchecked-get old-props "values") + new-values (unchecked-get new-props "values")] + (and (identical? (unchecked-get old-props "class") + (unchecked-get new-props "class")) + (identical? (unchecked-get old-props "ids") + (unchecked-get new-props "ids")) + (identical? (get old-values :r1) + (get new-values :r1)) + (identical? (get old-values :r2) + (get new-values :r2)) + (identical? (get old-values :r3) + (get new-values :r3)) + (identical? (get old-values :r4) + (get new-values :r4))))) + (mf/defc border-radius-menu* - {::mf/props :obj - ::mf/wrap [mf/memo]} - [{:keys [class ids ids-with-children values]}] + {::mf/wrap [#(mf/memo' % check-border-radius-menu-props)]} + [{:keys [class ids values]}] (let [all-equal? (all-equal? values) radius-expanded* (mf/use-state false) radius-expanded (deref radius-expanded*) change-radius (mf/use-fn - (mf/deps ids-with-children) + (mf/deps ids) (fn [update-fn] - (dwsh/update-shapes ids-with-children + (dwsh/update-shapes ids (fn [shape] (if (ctsr/has-radius? shape) (update-fn shape) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 5712d0977b..c93bf3320e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -92,7 +92,7 @@ (mf/defc measures-menu* {::mf/memo true} - [{:keys [ids ids-with-children values type shapes]}] + [{:keys [ids values type shapes]}] (let [all-types (mf/with-memo [type shapes] ;; We only need this when multiple type is used @@ -105,9 +105,6 @@ (into #{} xf:mapcat-type-to-options all-types) (type->options type))) - ids-with-children - (d/nilv ids-with-children ids) - frames (mf/with-memo [shapes] (let [objects (deref refs/workspace-page-objects)] @@ -466,7 +463,6 @@ (when (options :radius) [:> border-radius-menu* {:class (stl/css :border-radius) :ids ids - :ids-with-children ids-with-children :values values :shape shape}])]) (when (or (options :clip-content) (options :show-in-viewer)) From e94abad3ebca760b5321083e3dd9ea96dba0f2d8 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Aug 2025 10:31:20 +0200 Subject: [PATCH 4/5] :zap: Add efficiency refactor for layer-menu* --- .../sidebar/options/menus/layer.cljs | 96 ++++++++++--------- .../sidebar/options/shapes/bool.cljs | 8 +- .../sidebar/options/shapes/circle.cljs | 8 +- .../sidebar/options/shapes/frame.cljs | 8 +- .../sidebar/options/shapes/group.cljs | 6 +- .../sidebar/options/shapes/multiple.cljs | 6 +- .../sidebar/options/shapes/path.cljs | 8 +- .../sidebar/options/shapes/rect.cljs | 8 +- .../sidebar/options/shapes/text.cljs | 8 +- 9 files changed, 84 insertions(+), 72 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs index 4af557679b..f5c7eaf14f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs @@ -9,7 +9,6 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.data.helpers :as dsh] [app.main.data.workspace :as dw] [app.main.data.workspace.shapes :as dwsh] [app.main.features :as features] @@ -24,7 +23,7 @@ (def layer-attrs [:opacity :blend-mode :blocked :hidden]) -(defn opacity->string +(defn- opacity->string [opacity] (if (not= opacity :multiple) (dm/str (-> opacity @@ -32,61 +31,70 @@ (* 100))) :multiple)) -(mf/defc layer-menu - {::mf/wrap-props false} - [props] - (let [ids (unchecked-get props "ids") - values (unchecked-get props "values") +(defn- on-change + [ids prop value] + (st/emit! (dwsh/update-shapes ids #(assoc % prop value)))) - hidden? (:hidden values) - blocked? (:blocked values) +(defn- check-layer-menu-props + [old-props new-props] + (let [old-values (unchecked-get old-props "values") + new-values (unchecked-get new-props "values")] + (and (identical? (unchecked-get old-props "class") + (unchecked-get new-props "class")) + (identical? (unchecked-get old-props "ids") + (unchecked-get new-props "ids")) + (identical? (get old-values :opacity) + (get new-values :opacity)) + (identical? (get old-values :blend-mode) + (get new-values :blend-mode)) + (identical? (get old-values :blocked) + (get new-values :blocked)) + (identical? (get old-values :hidden) + (get new-values :hidden))))) - current-blend-mode (or (:blend-mode values) :normal) - current-opacity (opacity->string (:opacity values)) +(mf/defc layer-menu* + {::mf/wrap [#(mf/memo' % check-layer-menu-props)]} + [{:keys [ids values]}] + (let [hidden? (get values :hidden) + blocked? (get values :blocked) - state* (mf/use-state - {:selected-blend-mode current-blend-mode - :option-highlighted? false - :preview-complete? true}) + current-blend-mode (or (get values :blend-mode) :normal) + current-opacity (opacity->string (:opacity values)) - state (deref state*) - selected-blend-mode (get state :selected-blend-mode) - option-highlighted? (get state :option-highlighted?) - preview-complete? (get state :preview-complete?) - wasm-renderer-enabled? (features/use-feature "render-wasm/v1") + state* (mf/use-state + #(do {:selected-blend-mode current-blend-mode + :option-highlighted? false + :preview-complete? true})) - shapes (-> - (dsh/lookup-page-objects @st/state) - (select-keys ids) - vals) + state (deref state*) + selected-blend-mode (get state :selected-blend-mode) + option-highlighted? (get state :option-highlighted?) + preview-complete? (get state :preview-complete?) - on-change - (mf/use-fn - (mf/deps ids) - (fn [prop value] - (st/emit! (dwsh/update-shapes ids #(assoc % prop value))))) + wasm-renderer-enabled? + (features/use-feature "render-wasm/v1") handle-change-blend-mode (mf/use-fn - (mf/deps on-change) + (mf/deps ids) (fn [value] (swap! state* assoc :selected-blend-mode value :option-highlighted? false :preview-complete? true) (st/emit! (dw/unset-preview-blend-mode ids)) - (on-change :blend-mode value))) + (on-change ids :blend-mode value))) handle-blend-mode-enter (mf/use-fn - (mf/deps on-change current-blend-mode) + (mf/deps ids current-blend-mode) (fn [value] (swap! state* assoc :preview-complete? false :option-highlighted? true) (when wasm-renderer-enabled? - (doseq [shape shapes] + (doseq [shape ids] (wasm.api/use-shape (:id shape)) (wasm.api/set-shape-blend-mode value))) @@ -95,46 +103,46 @@ handle-blend-mode-leave (mf/use-fn - (mf/deps on-change selected-blend-mode) + (mf/deps ids) (fn [_value] (swap! state* assoc :preview-complete? true) (st/emit! (dw/unset-preview-blend-mode ids)))) handle-opacity-change (mf/use-fn - (mf/deps on-change ids) + (mf/deps ids) (fn [value] (st/emit! (dw/trigger-bounding-box-cloaking ids)) (let [value (/ value 100)] - (on-change :opacity value)))) + (on-change ids :opacity value)))) handle-set-hidden (mf/use-fn - (mf/deps on-change ids) + (mf/deps ids) (fn [_] (st/emit! (dw/trigger-bounding-box-cloaking ids)) - (on-change :hidden true))) + (on-change ids :hidden true))) handle-set-visible (mf/use-fn - (mf/deps on-change ids) + (mf/deps ids) (fn [_] (st/emit! (dw/trigger-bounding-box-cloaking ids)) - (on-change :hidden false))) + (on-change ids :hidden false))) handle-set-blocked (mf/use-fn - (mf/deps on-change ids) + (mf/deps ids) (fn [_] (st/emit! (dw/trigger-bounding-box-cloaking ids)) - (on-change :blocked true))) + (on-change ids :blocked true))) handle-set-unblocked (mf/use-fn - (mf/deps on-change ids) + (mf/deps ids) (fn [_] (st/emit! (dw/trigger-bounding-box-cloaking ids)) - (on-change :blocked false))) + (on-change ids :blocked false))) options (mf/with-memo [current-blend-mode] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs index bddea47dcb..56fb1dbdb2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs @@ -14,7 +14,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]] @@ -79,9 +79,9 @@ (mf/deref parents-by-ids-ref)] [:* - [:& layer-menu {:ids ids - :type type - :values layer-values}] + [:> layer-menu* {:ids ids + :type type + :values layer-values}] [:> measures-menu* {:ids ids :type type diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs index 4ef21adcdd..89e9ad4f8c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs @@ -14,7 +14,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]] @@ -79,9 +79,9 @@ parents (mf/deref parents-by-ids-ref)] [:* - [:& layer-menu {:ids ids - :type type - :values layer-values}] + [:> layer-menu* {:ids ids + :type type + :values layer-values}] [:> measures-menu* {:ids ids :type type 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 b676ef0b52..b1bca96a00 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 @@ -19,7 +19,7 @@ [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [select-measure-keys measures-menu*]] @@ -99,9 +99,9 @@ (when variants? (ctk/is-variant-container? shape))] [:* - [:& layer-menu {:ids ids - :type shape-type - :values layer-values}] + [:> layer-menu* {:ids ids + :type shape-type + :values layer-values}] [:> measures-menu* {:ids ids :values measure-values :type shape-type diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs index 13289a0bb3..4ef159d6d5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs @@ -17,7 +17,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measures-menu*]] @@ -106,7 +106,9 @@ (get-attrs shapes objects :layout-item)] [:div {:class (stl/css :options)} - [:& layer-menu {:type type :ids layer-ids :values layer-values}] + [:> layer-menu* {:type type + :ids layer-ids + :values layer-values}] [:> measures-menu* {:type type :ids measure-ids :values measure-values diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 6d5e5cd0cf..b19523e0f7 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -25,7 +25,7 @@ [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu*]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [select-measure-keys measure-attrs measures-menu*]] @@ -425,7 +425,9 @@ [:div {:class (stl/css :options)} (when-not (empty? layer-ids) - [:& layer-menu {:type type :ids layer-ids :values layer-values}]) + [:> layer-menu* {:type type + :ids layer-ids + :values layer-values}]) (when-not (empty? measure-ids) [:> measures-menu* diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs index bade487724..bba37db31a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs @@ -14,7 +14,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]] @@ -80,9 +80,9 @@ (mf/deref parents-by-ids-ref)] [:* - [:& layer-menu {:ids ids - :type type - :values layer-values}] + [:> layer-menu* {:ids ids + :type type + :values layer-values}] [:> measures-menu* {:ids ids :type type :values measure-values diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs index 9b5e62fd31..da138d37a5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs @@ -14,7 +14,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]] @@ -80,9 +80,9 @@ (mf/deref parents-by-ids-ref)] [:* - [:& layer-menu {:ids ids - :type type - :values layer-values}] + [:> layer-menu* {:ids ids + :type type + :values layer-values}] [:> measures-menu* {:ids ids :type type :values measure-values diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs index 0641652975..26b1c88e4e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs @@ -19,7 +19,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] - [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu*]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]] @@ -126,9 +126,9 @@ :attrs txt/text-node-attrs}))] [:* - [:& layer-menu {:ids ids - :type type - :values layer-values}] + [:> layer-menu* {:ids ids + :type type + :values layer-values}] [:> measures-menu* {:ids ids :type type From dede2a8f8e8a493e6a2f3d92e045c3efe63cc387 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Aug 2025 17:53:07 +0200 Subject: [PATCH 5/5] :lipstick: Fix JS files formatting issues --- frontend/playwright/ui/pages/RegisterPage.js | 4 +- frontend/playwright/ui/pages/WorkspacePage.js | 5 +- .../ui/render-wasm-specs/shapes.spec.js | 14 ++-- .../ui/render-wasm-specs/texts.spec.js | 65 ++++++++++++------- .../playwright/ui/specs/colorpicker.spec.js | 2 +- frontend/playwright/ui/specs/register.spec.js | 16 +++-- frontend/playwright/ui/specs/tokens.spec.js | 1 - frontend/scripts/_helpers.js | 6 +- frontend/scripts/watch-storybook.js | 16 ++--- frontend/scripts/watch.js | 16 ++--- .../ui/ds/controls/numeric_input.stories.jsx | 2 +- .../app/main/ui/ds/product/avatar.stories.jsx | 8 +-- .../main/ui/ds/product/milestone.stories.jsx | 10 ++- .../ui/ds/product/milestone_group.stories.jsx | 2 +- 14 files changed, 93 insertions(+), 74 deletions(-) diff --git a/frontend/playwright/ui/pages/RegisterPage.js b/frontend/playwright/ui/pages/RegisterPage.js index 945a3d6c42..097bbefb97 100644 --- a/frontend/playwright/ui/pages/RegisterPage.js +++ b/frontend/playwright/ui/pages/RegisterPage.js @@ -3,7 +3,9 @@ import { BasePage } from "./BasePage"; export class RegisterPage extends BasePage { constructor(page) { super(page); - this.registerButton = page.getByRole("button", { name: "Create an account" }); + this.registerButton = page.getByRole("button", { + name: "Create an account", + }); this.password = page.getByLabel("Password"); this.email = page.getByLabel("Work email"); this.fullName = page.getByLabel("Full name"); diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index 20a16ecdd8..5afc536b9a 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -159,10 +159,7 @@ export class WorkspacePage extends BaseWebSocketPage { "get-profiles-for-file-comments?file-id=*", "workspace/get-profile-for-file-comments.json", ); - await this.mockRPC( - /get\-file\?/, - "workspace/get-file-blank.json" - ); + await this.mockRPC(/get\-file\?/, "workspace/get-file-blank.json"); await this.mockRPC( "get-file-object-thumbnails?file-id=*", "workspace/get-file-object-thumbnails-blank.json", diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js index 1effc84679..a2717632b4 100644 --- a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js +++ b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js @@ -124,11 +124,13 @@ test("Renders shapes with exif rotated images fills and strokes", async ({ "27270c45-35b4-80f3-8006-63a39cf292e7", "27270c45-35b4-80f3-8006-63a41d147866", "27270c45-35b4-80f3-8006-63a43dc4984b", - "27270c45-35b4-80f3-8006-63a3ea82557f" + "27270c45-35b4-80f3-8006-63a3ea82557f", ], "render-wasm/assets/landscape.jpg", ); - await workspace.mockGetFile("render-wasm/get-file-shapes-exif-rotated-fills.json"); + await workspace.mockGetFile( + "render-wasm/get-file-shapes-exif-rotated-fills.json", + ); await workspace.goToWorkspace({ id: "27270c45-35b4-80f3-8006-63a3912bdce8", @@ -139,9 +141,7 @@ test("Renders shapes with exif rotated images fills and strokes", async ({ await expect(workspace.canvas).toHaveScreenshot(); }); -test("Updates canvas background", async ({ - page, -}) => { +test("Updates canvas background", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-text.json"); @@ -152,7 +152,9 @@ test("Updates canvas background", async ({ }); await workspace.waitForFirstRender({ hideUI: false }); - const canvasBackgroundInput = workspace.page.getByRole("textbox", { name: 'Color' }); + const canvasBackgroundInput = workspace.page.getByRole("textbox", { + name: "Color", + }); await canvasBackgroundInput.fill("FABADA"); await workspace.page.keyboard.press("Enter"); diff --git a/frontend/playwright/ui/render-wasm-specs/texts.spec.js b/frontend/playwright/ui/render-wasm-specs/texts.spec.js index 11c4e72e51..f7fcb4b754 100644 --- a/frontend/playwright/ui/render-wasm-specs/texts.spec.js +++ b/frontend/playwright/ui/render-wasm-specs/texts.spec.js @@ -12,37 +12,36 @@ test.beforeEach(async ({ page }) => { async function mockGetEmojiFont(workspace) { await workspace.mockGetAsset( /notocoloremoji.*\.ttf$/, - "render-wasm/assets/notocoloremojisubset.ttf" + "render-wasm/assets/notocoloremojisubset.ttf", ); } async function mockGetJapaneseFont(workspace) { await workspace.mockGetAsset( /notosansjp.*\.ttf$/, - "render-wasm/assets/notosansjpsubset.ttf" + "render-wasm/assets/notosansjpsubset.ttf", ); await workspace.mockGetAsset( /notosanssc.*\.ttf$/, - "render-wasm/assets/notosansjpsubset.ttf" + "render-wasm/assets/notosansjpsubset.ttf", ); } async function mockGetSymbolsFont(workspace) { await workspace.mockGetAsset( /notosanssymbols.*\.ttf$/, - "render-wasm/assets/notosanssymbolssubset.ttf" + "render-wasm/assets/notosanssymbolssubset.ttf", ); await workspace.mockGetAsset( /notosanssymbols2.*\.ttf$/, - "render-wasm/assets/notosanssymbols2subset.ttf" + "render-wasm/assets/notosanssymbols2subset.ttf", ); await workspace.mockGetAsset( /notomusic.*\.ttf$/, - "render-wasm/assets/notomusicsubset.ttf" + "render-wasm/assets/notomusicsubset.ttf", ); } - test("Renders a file with texts", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); @@ -139,7 +138,7 @@ test("Renders a file with texts with images", async ({ page }) => { await workspace.mockFileMediaAsset( [ "4f89252d-ebbc-813e-8006-8699e4170e17", - "4f89252d-ebbc-813e-8006-8699e4170e18" + "4f89252d-ebbc-813e-8006-8699e4170e18", ], "render-wasm/assets/pattern.png", ); @@ -156,7 +155,9 @@ test("Renders a file with texts with images", async ({ page }) => { await expect(workspace.canvas).toHaveScreenshot(); }); -test("Renders a file with texts with emoji and different symbols", async ({ page }) => { +test("Renders a file with texts with emoji and different symbols", async ({ + page, +}) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await mockGetEmojiFont(workspace); @@ -176,9 +177,7 @@ test("Renders a file with text decoration", async ({ page }) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockFileMediaAsset( - [ - "d6c33e7b-7b64-80f3-8006-78509a3a2d21", - ], + ["d6c33e7b-7b64-80f3-8006-78509a3a2d21"], "render-wasm/assets/pattern.png", ); await mockGetEmojiFont(workspace); @@ -199,7 +198,9 @@ test("Renders a file with emoji and text decoration", async ({ page }) => { await workspace.setupEmptyFile(); await mockGetEmojiFont(workspace); - await workspace.mockGetFile("render-wasm/get-file-emoji-and-text-decoration.json"); + await workspace.mockGetFile( + "render-wasm/get-file-emoji-and-text-decoration.json", + ); await workspace.goToWorkspace({ id: "82d128e1-d3b1-80a5-8006-ae60fedcd5e7", @@ -222,11 +223,13 @@ test("Renders a file with multiple emoji", async ({ page }) => { pageId: "6bd7c17d-4f59-815e-8006-5e999f38f211", }); - await workspace.waitForFirstRender(); + await workspace.waitForFirstRender(); await expect(workspace.canvas).toHaveScreenshot(); }); -test("Renders a file with texts with different alignments", async ({ page }) => { +test("Renders a file with texts with different alignments", async ({ + page, +}) => { const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("render-wasm/get-file-text-align.json"); @@ -251,12 +254,16 @@ test("Updates text alignment edition - part 1", async ({ page }) => { await workspace.waitForFirstRender({ hideUI: false }); await workspace.clickLeafLayer("Text 1"); - const textOptionsButton = workspace.page.getByTestId("text-align-options-button"); + const textOptionsButton = workspace.page.getByTestId( + "text-align-options-button", + ); const autoWidthButton = workspace.page.getByTitle("Auto width"); const autoHeightButton = workspace.page.getByTitle("Auto height"); const alignMiddleButton = workspace.page.getByTitle("Align middle"); const alignBottomButton = workspace.page.getByTitle("Align bottom"); - const alignRightButton = workspace.page.getByTitle("Align right (Ctrl+Alt+R)"); + const alignRightButton = workspace.page.getByTitle( + "Align right (Ctrl+Alt+R)", + ); await textOptionsButton.click(); @@ -276,7 +283,7 @@ test("Updates text alignment edition - part 1", async ({ page }) => { await workspace.page.keyboard.press("Escape"); await workspace.hideUI(); - await expect(workspace.canvas).toHaveScreenshot({timeout: 10000}); + await expect(workspace.canvas).toHaveScreenshot({ timeout: 10000 }); }); test("Updates text alignment edition - part 2", async ({ page }) => { @@ -291,11 +298,15 @@ test("Updates text alignment edition - part 2", async ({ page }) => { await workspace.waitForFirstRender({ hideUI: false }); await workspace.clickLeafLayer("Text 1"); - const textOptionsButton = workspace.page.getByTestId("text-align-options-button"); + const textOptionsButton = workspace.page.getByTestId( + "text-align-options-button", + ); const alignTopButton = workspace.page.getByTitle("Align top"); const alignMiddleButton = workspace.page.getByTitle("Align middle"); const alignBottomButton = workspace.page.getByTitle("Align bottom"); - const alignCenterButton = workspace.page.getByTitle("Align center (Ctrl+Alt+T)"); + const alignCenterButton = workspace.page.getByTitle( + "Align center (Ctrl+Alt+T)", + ); const alignJustifyButton = workspace.page.getByTitle("Justify (Ctrl+Alt+J)"); const LTRButton = workspace.page.getByTitle("LTR"); const RTLButton = workspace.page.getByTitle("RTL"); @@ -324,7 +335,7 @@ test("Updates text alignment edition - part 2", async ({ page }) => { await workspace.page.keyboard.press("Escape"); await workspace.hideUI(); - await expect(workspace.canvas).toHaveScreenshot({timeout: 10000}); + await expect(workspace.canvas).toHaveScreenshot({ timeout: 10000 }); }); test("Updates text alignment edition - part 3", async ({ page }) => { @@ -339,13 +350,17 @@ test("Updates text alignment edition - part 3", async ({ page }) => { await workspace.waitForFirstRender({ hideUI: false }); await workspace.clickLeafLayer("Text 1"); - const textOptionsButton = workspace.page.getByTestId("text-align-options-button"); + const textOptionsButton = workspace.page.getByTestId( + "text-align-options-button", + ); const autoWidthButton = workspace.page.getByTitle("Auto width"); const autoHeightButton = workspace.page.getByTitle("Auto height"); const alignMiddleButton = workspace.page.getByTitle("Align middle"); const alignBottomButton = workspace.page.getByTitle("Align bottom"); const alignLeftButton = workspace.page.getByTitle("Align left (Ctrl+Alt+L)"); - const alignCenterButton = workspace.page.getByTitle("Align center (Ctrl+Alt+T)"); + const alignCenterButton = workspace.page.getByTitle( + "Align center (Ctrl+Alt+T)", + ); const alignJustifyButton = workspace.page.getByTitle("Justify (Ctrl+Alt+J)"); const RTLButton = workspace.page.getByTitle("RTL"); @@ -375,5 +390,5 @@ test("Updates text alignment edition - part 3", async ({ page }) => { await workspace.page.keyboard.press("Escape"); await workspace.hideUI(); - await expect(workspace.canvas).toHaveScreenshot({timeout: 10000}); -}); \ No newline at end of file + await expect(workspace.canvas).toHaveScreenshot({ timeout: 10000 }); +}); diff --git a/frontend/playwright/ui/specs/colorpicker.spec.js b/frontend/playwright/ui/specs/colorpicker.spec.js index 9521cdb06c..db2e3ccdfe 100644 --- a/frontend/playwright/ui/specs/colorpicker.spec.js +++ b/frontend/playwright/ui/specs/colorpicker.spec.js @@ -190,7 +190,7 @@ test("Gradient stops limit", async ({ page }) => { await workspacePage.goToWorkspace({ fileId: "c7ce0794-0992-8105-8004-38f280443849", - pageId: "66697432-c33d-8055-8006-2c62cc084cad" + pageId: "66697432-c33d-8055-8006-2c62cc084cad", }); await workspacePage.clickLeafLayer("Rectangle"); diff --git a/frontend/playwright/ui/specs/register.spec.js b/frontend/playwright/ui/specs/register.spec.js index 301701a225..24e835c829 100644 --- a/frontend/playwright/ui/specs/register.spec.js +++ b/frontend/playwright/ui/specs/register.spec.js @@ -7,15 +7,21 @@ test.beforeEach(async ({ page }) => { }); test.describe("Register form errors", () => { - test("User gets error message when email does not match invitation", async ({ page }) => { + test("User gets error message when email does not match invitation", async ({ + page, + }) => { const registerPage = new RegisterPage(page); await registerPage.setupMismatchedEmailError(); - await registerPage.fillRegisterFormInputs("John Doe", "john.doe@example.com", "password123"); + await registerPage.fillRegisterFormInputs( + "John Doe", + "john.doe@example.com", + "password123", + ); await registerPage.clickRegisterButton(); - await expect(page.getByText( - "Email does not match the invitation.", - )).toBeVisible(); + await expect( + page.getByText("Email does not match the invitation."), + ).toBeVisible(); }); }); diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index e74397db68..5878c762a8 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -119,7 +119,6 @@ test.describe("Tokens: Tokens Tab", () => { .click(); // Create color token with mouse - await expect(tokensUpdateCreateModal).toBeVisible(); const nameField = tokensUpdateCreateModal.getByLabel("Name"); diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js index b3f79fca45..323da942b0 100644 --- a/frontend/scripts/_helpers.js +++ b/frontend/scripts/_helpers.js @@ -255,9 +255,9 @@ const markedOptions = { const text = token.text; return `${text}`; } - } - } -} + }, + }, +}; marked.use(markedOptions); diff --git a/frontend/scripts/watch-storybook.js b/frontend/scripts/watch-storybook.js index 36592145e1..d2bef90884 100644 --- a/frontend/scripts/watch-storybook.js +++ b/frontend/scripts/watch-storybook.js @@ -79,15 +79,11 @@ h.watch("translations", null, async function (path) { }); log.info("watch: assets (~)"); -h.watch( - ["resources/images", "resources/fonts"], - null, - async function (path) { - log.info("changed:", path); - await h.compileSvgSprites(); - await h.copyAssets(); - await h.compileTemplates(); - }, -); +h.watch(["resources/images", "resources/fonts"], null, async function (path) { + log.info("changed:", path); + await h.compileSvgSprites(); + await h.copyAssets(); + await h.compileTemplates(); +}); worker.terminate(); diff --git a/frontend/scripts/watch.js b/frontend/scripts/watch.js index eddd3df6d4..eeeb06eec9 100644 --- a/frontend/scripts/watch.js +++ b/frontend/scripts/watch.js @@ -78,16 +78,12 @@ h.watch("translations", null, async function (path) { }); log.info("watch: assets (~)"); -h.watch( - ["resources/images", "resources/fonts"], - null, - async function (path) { - log.info("changed:", path); - await h.compileSvgSprites(); - await h.copyAssets(); - await h.compileTemplates(); - }, -); +h.watch(["resources/images", "resources/fonts"], null, async function (path) { + log.info("changed:", path); + await h.compileSvgSprites(); + await h.copyAssets(); + await h.compileTemplates(); +}); log.info("watch: wasm playground (~)"); h.watch(["resources/wasm-playground"], null, async function (path) { diff --git a/frontend/src/app/main/ui/ds/controls/numeric_input.stories.jsx b/frontend/src/app/main/ui/ds/controls/numeric_input.stories.jsx index 8cbf44a481..f3d2d20c8c 100644 --- a/frontend/src/app/main/ui/ds/controls/numeric_input.stories.jsx +++ b/frontend/src/app/main/ui/ds/controls/numeric_input.stories.jsx @@ -44,7 +44,7 @@ export default { property: "search", }, parameters: { - controls: { exclude: [ "tokens" ] }, + controls: { exclude: ["tokens"] }, }, render: ({ ...args }) => , }; diff --git a/frontend/src/app/main/ui/ds/product/avatar.stories.jsx b/frontend/src/app/main/ui/ds/product/avatar.stories.jsx index 6bbecd3448..9041a4a5ea 100644 --- a/frontend/src/app/main/ui/ds/product/avatar.stories.jsx +++ b/frontend/src/app/main/ui/ds/product/avatar.stories.jsx @@ -27,17 +27,17 @@ export default { variant: "S", selected: false, }, - render: ({name, url, ...args }) => { + render: ({ name, url, ...args }) => { const profile = { id: "00000000-0000-0000-0000-000000000000", - fullname: name + fullname: name, }; if (url) { profile.photoUrl = url; - }; + } return ; - } + }, }; export const Default = {}; diff --git a/frontend/src/app/main/ui/ds/product/milestone.stories.jsx b/frontend/src/app/main/ui/ds/product/milestone.stories.jsx index 1af1762f26..85d637236b 100644 --- a/frontend/src/app/main/ui/ds/product/milestone.stories.jsx +++ b/frontend/src/app/main/ui/ds/product/milestone.stories.jsx @@ -38,10 +38,16 @@ export default { active: false, editing: false, }, - render: ({ profileName, profileAvatar, profileColor, createdAt, ...args }) => { + render: ({ + profileName, + profileAvatar, + profileColor, + createdAt, + ...args + }) => { const profile = { id: "00000000-0000-0000-0000-000000000000", - fullname: profileName + fullname: profileName, }; if (profileAvatar) { diff --git a/frontend/src/app/main/ui/ds/product/milestone_group.stories.jsx b/frontend/src/app/main/ui/ds/product/milestone_group.stories.jsx index 293ad54528..f43162967c 100644 --- a/frontend/src/app/main/ui/ds/product/milestone_group.stories.jsx +++ b/frontend/src/app/main/ui/ds/product/milestone_group.stories.jsx @@ -27,7 +27,7 @@ export default { args: { label: "Milestone 1", active: false, - snapshots: [1737452413841, 1737452422063, 1737452431603] + snapshots: [1737452413841, 1737452422063, 1737452431603], }, render: ({ ...args }) => { return ;