From dfab4725225acfbad4d3085487eeb79219df70ed Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Aug 2025 16:52:32 +0200 Subject: [PATCH 1/5] :lipstick: Add minor cosmetic change to shape layout type helper --- common/src/app/common/types/shape/layout.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index 10051ee759..cd53eea6ce 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -517,7 +517,7 @@ ([objects id] (item-absolute? (get objects id))) ([shape] - (true? (:layout-item-absolute shape)))) + (true? (get shape :layout-item-absolute)))) (defn position-absolute? ([objects id] From 65f4adc68ea0af0532172cdae346e0d663200884 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Aug 2025 16:54:22 +0200 Subject: [PATCH 2/5] :zap: Add minor efficiency enhancements to numeric-input* --- .../main/ui/ds/controls/numeric_input.cljs | 108 ++++++++++-------- .../src/app/main/ui/ds/controls/select.cljs | 5 +- 2 files changed, 64 insertions(+), 49 deletions(-) diff --git a/frontend/src/app/main/ui/ds/controls/numeric_input.cljs b/frontend/src/app/main/ui/ds/controls/numeric_input.cljs index 38a55ef1da..c78248e145 100644 --- a/frontend/src/app/main/ui/ds/controls/numeric_input.cljs +++ b/frontend/src/app/main/ui/ds/controls/numeric_input.cljs @@ -65,14 +65,17 @@ (defn- get-option-by-name [options name] - (d/seek #(= name (get % :name)) options)) + (let [options (if (delay? options) (deref options) options)] + (d/seek #(= name (get % :name)) options))) (defn- get-token-op [tokens name] - (->> tokens - vals - (apply concat) - (some #(when (= (:name %) name) %)))) + (let [tokens (if (delay? tokens) @tokens tokens) + xform (filter #(= (:name %) name))] + (reduce-kv (fn [result _ tokens] + (into result xform tokens)) + [] + tokens))) (defn- clean-token-name [s] @@ -114,14 +117,14 @@ (subs s (inc start)))) (defn- filter-token-groups-by-name - [tokens-by-type filter-text] + [tokens filter-text] (let [lc-filter (str/lower filter-text)] (into {} (keep (fn [[group tokens]] (let [filtered (filter #(str/includes? (str/lower (:name %)) lc-filter) tokens)] (when (seq filtered) [group filtered])))) - tokens-by-type))) + tokens))) (defn- focusable-option? [option] @@ -183,7 +186,9 @@ is-selected-on-focus nillable tokens applied-token empty-to-end on-change on-blur on-focus on-detach - property align ref] :rest props}] + property align ref] + :rest props}] + (let [;; NOTE: we use mfu/bean here for transparently handle ;; options provide as clojure data structures or javascript ;; plain objects and lists. @@ -253,12 +258,14 @@ dropdown-options (mf/with-memo [tokens filter-id] - (let [partial (extract-partial-brace-text filter-id) - options (if (seq partial) - (filter-token-groups-by-name tokens partial) - tokens) - no-sets? (nil? tokens)] - (generate-dropdown-options options no-sets?))) + (delay + (let [tokens (if (delay? tokens) @tokens tokens) + partial (extract-partial-brace-text filter-id) + options (if (seq partial) + (filter-token-groups-by-name tokens partial) + tokens) + no-sets? (nil? tokens)] + (generate-dropdown-options options no-sets?)))) selected-id* (mf/use-state (fn [] @@ -351,23 +358,27 @@ on-option-click (mf/use-fn - (mf/deps dropdown-options on-token-apply) + (mf/deps on-token-apply) (fn [event] - (let [node (dom/get-current-target event) - id (dom/get-data node "id") - option (get-option dropdown-options id) - value (get option :resolved-value) - name (get option :name)] + (let [node (dom/get-current-target event) + id (dom/get-data node "id") + options (mf/ref-val options-ref) + options (if (delay? options) @options options) + option (get-option options id) + value (get option :resolved-value) + name (get option :name)] (on-token-apply id value name) (reset! filter-id* "")))) on-option-enter (mf/use-fn - (mf/deps dropdown-options focused-id on-token-apply) + (mf/deps focused-id on-token-apply) (fn [_] - (let [option (get-option dropdown-options focused-id) - value (get option :resolved-value) - name (get option :name)] + (let [options (mf/ref-val options-ref) + options (if (delay? options) @options options) + option (get-option options focused-id) + value (get option :resolved-value) + name (get option :name)] (on-token-apply focused-id value name) (reset! filter-id* "")))) @@ -386,9 +397,9 @@ (when (fn? on-blur) (on-blur event))))) - handle-key-down + on-key-down (mf/use-fn - (mf/deps dropdown-options is-open apply-value update-input is-open focused-id handle-focus-change) + (mf/deps is-open apply-value update-input is-open focused-id handle-focus-change) (fn [event] (mf/set-ref-val! dirty-ref true) (let [up? (kbd/up-arrow? event) @@ -398,7 +409,8 @@ node (mf/ref-val ref) open-tokens (kbd/is-key? event "{") close-tokens (kbd/is-key? event "}") - options (mf/ref-val options-ref)] + options (mf/ref-val options-ref) + options (if (delay? options) @options options)] (cond (and (some? options) open-tokens) @@ -418,8 +430,8 @@ (dom/prevent-default event) (if focused-id (on-option-enter event) - (let [option-id (first-focusable-id dropdown-options) - option (get-option dropdown-options option-id) + (let [option-id (first-focusable-id options) + option (get-option options option-id) value (get option :resolved-value) name (get option :name)] (on-token-apply option-id value name) @@ -461,7 +473,7 @@ (update-input (fmt/format-number new-val)) (apply-value (dm/str new-val)))))))) - handle-focus + on-focus (mf/use-fn (mf/deps on-focus select-on-focus) (fn [event] @@ -473,7 +485,7 @@ ;; In webkit browsers the mouseup event will be called after the on-focus causing and unselect (.addEventListener target "mouseup" dom/prevent-default #js {:once true}))))) - handle-mouse-wheel + on-mouse-wheel (mf/use-fn (mf/deps apply-value parse-value min max nillable ref default step min max) (fn [event] @@ -580,8 +592,8 @@ placeholder) :default-value (or (mf/ref-val last-value*) (fmt/format-number value)) :on-blur on-blur - :on-key-down handle-key-down - :on-focus handle-focus + :on-key-down on-key-down + :on-focus on-focus :on-change store-raw-value :disabled disabled :slot-start (when icon @@ -603,11 +615,12 @@ token-props (when (and token-applied (not= :multiple token-applied)) - (let [token (get-option-by-name dropdown-options token-applied) - id (get token :id) - label (get token :name) + (let [token (get-option-by-name dropdown-options token-applied) + id (get token :id) + label (get token :name) token-value (or (get token :resolved-value) - (or (mf/ref-val last-value*) (fmt/format-number value)))] + (or (mf/ref-val last-value*) + (fmt/format-number value)))] (mf/spread-props props {:id id :label label @@ -649,9 +662,9 @@ (when-let [node (mf/ref-val ref)] (dom/set-value! node value')))) - (mf/with-layout-effect [handle-mouse-wheel] + (mf/with-layout-effect [on-mouse-wheel] (when-let [node (mf/ref-val ref)] - (let [key (events/listen node "wheel" handle-mouse-wheel #js {:passive false})] + (let [key (events/listen node "wheel" on-mouse-wheel #js {:passive false})] #(events/unlistenByKey key)))) (mf/with-effect [dropdown-options] @@ -666,11 +679,12 @@ [:> input-field* input-props]) (when ^boolean is-open - [:> options-dropdown* {:on-click on-option-click - :id listbox-id - :options dropdown-options - :selected selected-id - :focused focused-id - :align align - :empty-to-end empty-to-end - :ref set-option-ref}])])) + (let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)] + [:> options-dropdown* {:on-click on-option-click + :id listbox-id + :options options + :selected selected-id + :focused focused-id + :align align + :empty-to-end empty-to-end + :ref set-option-ref}]))])) diff --git a/frontend/src/app/main/ui/ds/controls/select.cljs b/frontend/src/app/main/ui/ds/controls/select.cljs index d5c91a3b81..0e31fe390d 100644 --- a/frontend/src/app/main/ui/ds/controls/select.cljs +++ b/frontend/src/app/main/ui/ds/controls/select.cljs @@ -20,8 +20,9 @@ (defn get-option [options id] - (or (d/seek #(= id (get % :id)) options) - (nth options 0))) + (let [options (if (delay? options) @options options)] + (or (d/seek #(= id (get % :id)) options) + (nth options 0)))) (defn- get-selected-option-id [options default] From 6401b25964a82b6f98a0ca00d057cee224a53284 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Tue, 26 Aug 2025 16:54:48 +0200 Subject: [PATCH 3/5] :lipstick: Format tab-switcher stories jsx file --- .../main/ui/ds/layout/tab_switcher.stories.jsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/main/ui/ds/layout/tab_switcher.stories.jsx b/frontend/src/app/main/ui/ds/layout/tab_switcher.stories.jsx index 33840f8802..f5b1e2e83f 100644 --- a/frontend/src/app/main/ui/ds/layout/tab_switcher.stories.jsx +++ b/frontend/src/app/main/ui/ds/layout/tab_switcher.stories.jsx @@ -13,8 +13,8 @@ const Padded = ({ children }) => (
{children}
); -const TabSwitcherWrapper = ({tabs, ...props}) => { - const navTabs = tabs.map(({content, ...item}) => { +const TabSwitcherWrapper = ({ tabs, ...props }) => { + const navTabs = tabs.map(({ content, ...item }) => { return item; }); @@ -28,7 +28,12 @@ const TabSwitcherWrapper = ({tabs, ...props}) => { }, {}); return ( - + {content[selected]} ); @@ -77,12 +82,7 @@ export default { }, parameters: { controls: { - exclude: [ - "tabs", - "actionButton", - "default", - "actionButtonPosition", - ], + exclude: ["tabs", "actionButton", "default", "actionButtonPosition"], }, }, render: ({ ...args }) => , From df083cb3159c09ea2032f491999ece455433991b Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Tue, 26 Aug 2025 16:55:26 +0200 Subject: [PATCH 4/5] :bug: Fix corner case on tooltip positioning --- frontend/src/app/main/ui/ds/tooltip/tooltip.cljs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs b/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs index 39995ab816..ac89d1fe24 100644 --- a/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs +++ b/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs @@ -160,11 +160,7 @@ tooltip-brect (assoc tooltip-brect :height (or saved-height (:height tooltip-brect)) :width (or saved-width (:width tooltip-brect))) window-size (dom/get-window-size)] (when-let [[placement placement-rect] (find-matching-placement placement tooltip-brect origin-brect window-size offset)] - (let [height (if (or (= placement "right") (= placement "left")) - (- (:height placement-rect) arrow-height) - (:height placement-rect))] - (dom/set-data! tooltip "height" (:height tooltip-brect)) - (dom/set-data! tooltip "width" (:width tooltip-brect)) + (let [height (:height placement-rect)] (dom/set-css-property! tooltip "block-size" (dm/str height "px")) (dom/set-css-property! tooltip "inset-block-start" (dm/str (:top placement-rect) "px")) (dom/set-css-property! tooltip "inset-inline-start" (dm/str (:left placement-rect) "px"))) @@ -253,7 +249,7 @@ :on-focus on-show :on-blur on-hide :on-key-down handle-key-down - :class (stl/css :tooltip-trigger) + :class [class (stl/css :tooltip-trigger)] :aria-describedby id}) content (if (fn? content) @@ -262,7 +258,7 @@ [:> :div props children - [:div {:class [class (stl/css :tooltip)] + [:div {:class (stl/css :tooltip) :id id :popover "auto" :role "tooltip"} From 4713d943d1b7865847000c79e79c120594b5bea0 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 26 Aug 2025 16:59:07 +0200 Subject: [PATCH 5/5] :recycle: Add efficiency refactor for workspace sidebars The main changes are for right sidebar but left sidebar is also slightly affected beacuse of the move where the active tokes are resolved. --- frontend/src/app/main/ui/context.cljs | 15 +- frontend/src/app/main/ui/workspace.cljs | 23 +- .../src/app/main/ui/workspace/sidebar.cljs | 120 +++++--- .../workspace/sidebar/collapsable_button.cljs | 4 +- .../main/ui/workspace/sidebar/options.cljs | 37 ++- .../sidebar/options/menus/align.cljs | 3 +- .../workspace/sidebar/options/menus/bool.cljs | 3 +- .../sidebar/options/menus/measures.cljs | 201 ++++++++----- .../sidebar/options/shapes/bool.cljs | 72 +++-- .../sidebar/options/shapes/circle.cljs | 71 +++-- .../sidebar/options/shapes/frame.cljs | 62 ++-- .../sidebar/options/shapes/group.cljs | 112 +++++--- .../sidebar/options/shapes/image.cljs | 97 ------- .../sidebar/options/shapes/multiple.cljs | 268 +++++++++++------- .../sidebar/options/shapes/path.cljs | 78 +++-- .../sidebar/options/shapes/rect.cljs | 76 +++-- .../sidebar/options/shapes/svg_raw.cljs | 93 +++--- .../sidebar/options/shapes/text.cljs | 143 ++++++---- .../main/ui/workspace/tokens/management.cljs | 21 +- .../app/main/ui/workspace/tokens/sidebar.cljs | 10 +- 20 files changed, 910 insertions(+), 599 deletions(-) delete mode 100644 frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs diff --git a/frontend/src/app/main/ui/context.cljs b/frontend/src/app/main/ui/context.cljs index 4e97749e9a..57672d4f14 100644 --- a/frontend/src/app/main/ui/context.cljs +++ b/frontend/src/app/main/ui/context.cljs @@ -30,7 +30,20 @@ (def workspace-read-only? (mf/create-context nil)) (def is-component? (mf/create-context false)) -(def sidebar (mf/create-context nil)) + +(def sidebar + "A context that intends to store the current sidebar position, + usefull for components that behaves distinctly if they are showed in + right sidebar or left sidebar. + + Possible values: `:right:` and `:left`." + (mf/create-context nil)) (def permissions (mf/create-context nil)) (def can-edit? (mf/create-context nil)) + +(def active-tokens-by-type + "Active tokens by type, used mainly for provide tokens data to the + right sidebar menu options components." + (mf/create-context nil)) + diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index b2c0b1192f..4125e47b99 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -29,8 +29,7 @@ [app.main.ui.workspace.nudge] [app.main.ui.workspace.palette :refer [palette]] [app.main.ui.workspace.plugins] - [app.main.ui.workspace.sidebar :refer [left-sidebar* right-sidebar*]] - [app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]] + [app.main.ui.workspace.sidebar :refer [sidebar*]] [app.main.ui.workspace.sidebar.history :refer [history-toolbox*]] [app.main.ui.workspace.tokens.export] [app.main.ui.workspace.tokens.export.modal] @@ -114,18 +113,14 @@ @palete-size)}]]] (when-not hide-ui? - [:* - (if (:collapse-left-sidebar layout) - [:& collapsed-button] - [:> left-sidebar* {:layout layout - :file file - :page-id page-id}]) - [:> right-sidebar* {:section options-mode - :selected selected - :drawing-tool (get drawing :tool) - :layout layout - :file file - :page-id page-id}]])])) + [:> sidebar* {:layout layout + ;; FIXME + :file-id (get file :id) + :page-id page-id + :file file + :selected selected + :section options-mode + :drawing-tool (get drawing :tool)}])])) (mf/defc workspace-loader* {::mf/private true} diff --git a/frontend/src/app/main/ui/workspace/sidebar.cljs b/frontend/src/app/main/ui/workspace/sidebar.cljs index 11ce398f89..a64fa30b7b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar.cljs @@ -8,9 +8,11 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] + [app.common.types.tokens-lib :as ctob] [app.main.constants :refer [sidebar-default-width sidebar-default-max-width]] [app.main.data.common :as dcm] [app.main.data.event :as ev] + [app.main.data.style-dictionary :as sd] [app.main.data.workspace :as dw] [app.main.features :as features] [app.main.refs :as refs] @@ -25,6 +27,7 @@ [app.main.ui.workspace.left-header :refer [left-header*]] [app.main.ui.workspace.right-header :refer [right-header*]] [app.main.ui.workspace.sidebar.assets :refer [assets-toolbox*]] + [app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button*]] [app.main.ui.workspace.sidebar.debug :refer [debug-panel*]] [app.main.ui.workspace.sidebar.debug-shape-info :refer [debug-shape-info*]] [app.main.ui.workspace.sidebar.history :refer [history-toolbox*]] @@ -96,7 +99,7 @@ (mf/defc left-sidebar* {::mf/memo true} - [{:keys [layout file page-id] :as props}] + [{:keys [layout file page-id tokens-lib active-tokens resolved-active-tokens]}] (let [options-mode (mf/deref refs/options-mode-global) project (mf/deref refs/project) file-id (get file :id) @@ -147,7 +150,7 @@ :left-settings-bar true :global/two-row (<= width 300) :global/three-row (and (> width 300) (<= width 400)) - :global/four-row (> width 400)) + :global/four-row (> width 400)) tabs-action-button (mf/with-memo [] @@ -197,7 +200,10 @@ :file-id file-id}] :tokens - [:> tokens-sidebar-tab*] + [:> tokens-sidebar-tab* + {:tokens-lib tokens-lib + :active-tokens active-tokens + :resolved-active-tokens resolved-active-tokens}] :layers [:> layers-content* @@ -255,8 +261,7 @@ [:> history-toolbox*]])])) (mf/defc right-sidebar* - {::mf/memo true} - [{:keys [layout section file page-id drawing-tool] :as props}] + [{:keys [layout section file page-id drawing-tool active-tokens] :as props}] (let [is-comments? (= drawing-tool :comments) is-history? (contains? layout :document-history) is-inspect? (= section :inspect) @@ -289,45 +294,84 @@ (fn [] (set-width (if (> width sidebar-default-width) sidebar-default-width - sidebar-default-max-width))))] + sidebar-default-max-width)))) + + active-tokens-by-type + (mf/with-memo [active-tokens] + (delay (ctob/group-by-type active-tokens)))] [:> (mf/provider muc/sidebar) {:value :right} - [:aside - {:class (stl/css-case :right-settings-bar true - :not-expand (not can-be-expanded?) - :expanded (> width sidebar-default-width)) + [:> (mf/provider muc/active-tokens-by-type) {:value active-tokens-by-type} - :id "right-sidebar-aside" - :data-testid "right-sidebar" - :data-size (str width) - :style {:--width (if can-be-expanded? - (dm/str width "px") - (dm/str sidebar-default-width "px"))}} + [:aside + {:class (stl/css-case :right-settings-bar true + :not-expand (not can-be-expanded?) + :expanded (> width sidebar-default-width)) - (when can-be-expanded? - [:div {:class (stl/css :resize-area) - :on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move}]) + :id "right-sidebar-aside" + :data-testid "right-sidebar" + :data-size (str width) + :style {:--width (if can-be-expanded? + (dm/str width "px") + (dm/str sidebar-default-width "px"))}} - [:> right-header* - {:file file - :layout layout - :page-id page-id}] + (when can-be-expanded? + [:div {:class (stl/css :resize-area) + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move}]) - [:div {:class (stl/css :settings-bar-inside)} - (cond - dbg-shape-panel? - [:> debug-shape-info*] + [:> right-header* + {:file file + :layout layout + :page-id page-id}] - is-comments? - [:> comments-sidebar* {}] + [:div {:class (stl/css :settings-bar-inside)} + (cond + dbg-shape-panel? + [:> debug-shape-info*] - is-history? - [:> history-content* {}] + is-comments? + [:> comments-sidebar* {}] - :else - (let [props (mf/spread-props props - {:on-change-section on-change-section - :on-expand on-expand})] - [:> options-toolbox* props]))]]])) + is-history? + [:> history-content* {}] + + :else + (let [props (mf/spread-props props + {:on-change-section on-change-section + :on-expand on-expand})] + [:> options-toolbox* props]))]]]])) + +(mf/defc sidebar* + [{:keys [layout file file-id page-id section drawing-tool selected]}] + (let [tokens-lib + (mf/deref refs/tokens-lib) + + active-tokens + (mf/with-memo [tokens-lib] + (if tokens-lib + (ctob/get-tokens-in-active-sets tokens-lib) + {})) + + resolved-active-tokens + (sd/use-resolved-tokens* active-tokens)] + + [:* + (if (:collapse-left-sidebar layout) + [:> collapsed-button*] + [:> left-sidebar* {:layout layout + :file file + :page-id page-id + :tokens-lib tokens-lib + :active-tokens active-tokens + :resolved-active-tokens resolved-active-tokens}]) + [:> right-sidebar* {:section section + :selected selected + :drawing-tool drawing-tool + :layout layout + :file file + :file-id file-id + :page-id page-id + :tokens-lib tokens-lib + :active-tokens resolved-active-tokens}]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs index 3f74853818..074e8e14b0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/collapsable_button.cljs @@ -13,8 +13,8 @@ [app.util.i18n :refer [tr]] [rumext.v2 :as mf])) -(mf/defc collapsed-button - {::mf/wrap-props false} +(mf/defc collapsed-button* + {::mf/memo true} [] (let [on-click (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))] [:div {:id "left-sidebar-aside" diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index 5c16891da1..d88d29b019 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -20,8 +20,8 @@ [app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]] [app.main.ui.inspect.right-sidebar :as hrs] [app.main.ui.workspace.sidebar.options.drawing :as drawing] - [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.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] @@ -32,7 +32,6 @@ [app.main.ui.workspace.sidebar.options.shapes.circle :as circle] [app.main.ui.workspace.sidebar.options.shapes.frame :as frame] [app.main.ui.workspace.sidebar.options.shapes.group :as group] - [app.main.ui.workspace.sidebar.options.shapes.image :as image] [app.main.ui.workspace.sidebar.options.shapes.multiple :as multiple] [app.main.ui.workspace.sidebar.options.shapes.path :as path] [app.main.ui.workspace.sidebar.options.shapes.rect :as rect] @@ -57,14 +56,13 @@ [:* (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}] - :image [:& image/options {:shape shape}] - :svg-raw [:& svg-raw/options {:shape shape}] - :bool [:& bool/options {:shape shape}] + :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)] @@ -73,7 +71,7 @@ :page-id page-id :file-id file-id}]])) -(mf/defc specialized-panel +(mf/defc specialized-panel* {::mf/wrap [mf/memo]} [{:keys [panel]}] (when (= (:type panel) :component-swap) @@ -92,8 +90,8 @@ (map #(dm/get-in objects [edition :layout-grid-cells %])))] [:div {:class (stl/css :element-options :design-options)} - [:& align-options] - [:& bool-options] + [:> align-options*] + [:> bool-options*] (cond (and edit-grid? (d/not-empty? selected-cells)) @@ -107,7 +105,7 @@ :values (get objects edition)}] (not (nil? sp-panel)) - [:& specialized-panel {:panel sp-panel}] + [:> specialized-panel* {:panel sp-panel}] (d/not-empty? drawing) [:> drawing/drawing-options* @@ -125,7 +123,7 @@ :shapes-with-children shapes-with-children}] :else - [:& multiple/options + [:> multiple/options* {:shapes-with-children shapes-with-children :shapes selected-shapes :page-id page-id @@ -210,12 +208,9 @@ (mf/defc options-toolbox* {::mf/memo true} - [{:keys [section selected on-change-section on-expand]}] - (let [page-id (mf/use-ctx ctx/current-page-id) - file-id (mf/use-ctx ctx/current-file-id) - shapes (mf/deref refs/selected-objects) + [{: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 :selected selected :shapes-with-children shapes-with-children 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 c34e757e50..b4f97f78ec 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 @@ -16,7 +16,8 @@ [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) -(mf/defc align-options +(mf/defc align-options* + {::mf/memo true} [] (let [selected (mf/deref refs/selected-shapes) ;; don't need to watch objects, only read the value 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 7c6c620dc2..5d6359a082 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 @@ -22,7 +22,8 @@ (def ^:private flatten-icon (i/icon-xref :boolean-flatten (stl/css :flatten-icon))) -(mf/defc bool-options +(mf/defc bool-options* + {::mf/memo true} [] (let [selected (mf/deref refs/selected-objects) head (first selected) 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 bfb460c624..5712d0977b 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 @@ -8,6 +8,8 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] [app.common.logic.shapes :as cls] [app.common.types.shape :as cts] @@ -25,7 +27,6 @@ [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as ds-i] - [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.menus.border-radius :refer [border-radius-menu*]] [app.util.dom :as dom] @@ -61,7 +62,6 @@ :circle generic-options :frame frame-options :group generic-options - :image rect-options :path generic-options :rect rect-options :svg-raw generic-options @@ -87,92 +87,139 @@ shape)] (select-keys shape measure-attrs))) +(def ^:private xf:map-type (map :type)) +(def ^:private xf:mapcat-type-to-options (mapcat type->options)) + (mf/defc measures-menu* - {::mf/props :obj - ::mf/wrap [mf/memo]} - [{:keys [ids ids-with-children values type all-types shape]}] - (let [options + {::mf/memo true} + [{:keys [ids ids-with-children values type shapes]}] + (let [all-types + (mf/with-memo [type shapes] + ;; We only need this when multiple type is used + (when (= type :multiple) + (into #{} xf:map-type shapes))) + + options (mf/with-memo [type all-types] (if (= type :multiple) - (into #{} (mapcat type->options) all-types) + (into #{} xf:mapcat-type-to-options all-types) (type->options type))) ids-with-children - (or ids-with-children ids) - - old-shapes - (if (= type :multiple) - (deref (refs/objects-by-id ids)) - [shape]) + (d/nilv ids-with-children ids) frames - (map #(deref (refs/object-by-id (:frame-id %))) old-shapes) + (mf/with-memo [shapes] + (let [objects (deref refs/workspace-page-objects)] + (into [] (comp (keep :frame-id) + (map (d/getf objects))) + shapes))) - ids (hooks/use-equal-memo ids) + selection-parents-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) - selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - selection-parents (mf/deref selection-parents-ref) + selection-parents + (mf/deref selection-parents-ref) - flex-child? (->> selection-parents (some ctl/flex-layout?)) - absolute? (ctl/item-absolute? shape) - flex-container? (ctl/flex-layout? shape) - flex-auto-width? (ctl/auto-width? shape) - flex-fill-width? (ctl/fill-width? shape) - flex-auto-height? (ctl/auto-height? shape) - flex-fill-height? (ctl/fill-height? shape) + shape + (first shapes) - disabled-position-x? (and flex-child? (not absolute?)) - disabled-position-y? (and flex-child? (not absolute?)) - disabled-width-sizing? (and (or flex-child? flex-container?) - (or flex-auto-width? flex-fill-width?) - (not absolute?)) - disabled-height-sizing? (and (or flex-child? flex-container?) - (or flex-auto-height? flex-fill-height?) - (not absolute?)) + flex-child? + (some ctl/flex-layout? selection-parents) + + absolute? + (ctl/item-absolute? shape) + + flex-container? + (ctl/flex-layout? shape) + + flex-auto-width? + (ctl/auto-width? shape) + + flex-fill-width? + (ctl/fill-width? shape) + + flex-auto-height? + (ctl/auto-height? shape) + + flex-fill-height? + (ctl/fill-height? shape) + + disabled-position? + (and flex-child? (not absolute?)) + + disabled-width-sizing? + (and (or flex-child? flex-container?) + (or flex-auto-width? flex-fill-width?) + (not absolute?)) + + disabled-height-sizing? + (and (or flex-child? flex-container?) + (or flex-auto-height? flex-fill-height?) + (not absolute?)) ;; To show interactively the measures while the user is manipulating ;; the shape with the mouse, generate a copy of the shapes applying ;; the transient transformations. - shapes (as-> old-shapes $ - (map gsh/translate-to-frame $ frames)) + shapes + (mf/with-memo [shapes frames] + (map gsh/translate-to-frame shapes frames)) + + ;; We repeatedly obtain the first shape after the + ;; transformation. + shape + (first shapes) ;; For rotated or stretched shapes, the origin point we show in the menu ;; is not the (:x :y) shape attribute, but the top left coordinate of the ;; wrapping rectangle. - values (let [{:keys [x y]} (gsh/shapes->rect [(first shapes)])] - (cond-> values - (not= (:x values) :multiple) (assoc :x x) - (not= (:y values) :multiple) (assoc :y y) - ;; In case of multiple selection, the origin point has been already - ;; calculated and given in the fake :ox and :oy attributes. See - ;; common/src/app/common/attrs.cljc - (and (= (:x values) :multiple) - (some? (:ox values))) (assoc :x (:ox values)) - (and (= (:y values) :multiple) - (some? (:oy values))) (assoc :y (:oy values)))) + values + (let [rect (-> (get shape :points) + (grc/points->rect)) + val-x (get values :x) + val-y (get values :y)] + (cond-> values + (not= val-x :multiple) (assoc :x (dm/get-prop rect :x)) + (not= val-y :multiple) (assoc :y (dm/get-prop rect :y)) + ;; In case of multiple selection, the origin point has been already + ;; calculated and given in the fake :ox and :oy attributes. See + ;; common/src/app/common/attrs.cljc + (and (= val-x :multiple) + (some? (:ox values))) + (assoc :x (:ox values)) + + (and (= val-y :multiple) + (some? (:oy values))) + (assoc :y (:oy values)))) ;; For :height and :width we take those in the :selrect attribute, because ;; not all shapes have an own :width and :height (e. g. paths). Here the ;; rotation is ignored (selrect always has the original size excluding ;; transforms). - values (let [{:keys [width height]} (-> shapes first :selrect)] - (cond-> values - (not= (:width values) :multiple) (assoc :width width) - (not= (:height values) :multiple) (assoc :height height))) + values + (let [selrect (get shape :selrect) + rotation (get shape :rotation 0)] + (cond-> values + (not= (:width values) :multiple) (assoc :width (dm/get-prop selrect :width)) + (not= (:height values) :multiple) (assoc :height (dm/get-prop selrect :height)) + (not= (:rotation values) :multiple) (assoc :rotation rotation))) - ;; The :rotation, however, does use the transforms. - values (let [{:keys [rotation] :or {rotation 0}} (-> shapes first)] - (cond-> values - (not= (:rotation values) :multiple) (assoc :rotation rotation))) + proportion-lock + (get values :proportion-lock) - proportion-lock (:proportion-lock values) + clip-content-ref + (mf/use-ref nil) - clip-content-ref (mf/use-ref nil) - show-in-viewer-ref (mf/use-ref nil) + show-in-viewer-ref + (mf/use-ref nil) ;; PRESETS - preset-state* (mf/use-state false) - show-presets-dropdown? (deref preset-state*) + preset-state* + (mf/use-state false) + + show-presets-dropdown? + (deref preset-state*) open-presets (mf/use-fn @@ -254,10 +301,17 @@ (st/emit! (udw/trigger-bounding-box-cloaking ids) (udw/increase-rotation ids value))))) - on-width-change #(on-size-change % :width) - on-height-change #(on-size-change % :height) - on-pos-x-change #(on-position-change % :x) - on-pos-y-change #(on-position-change % :y) + on-width-change + (mf/use-fn (mf/deps on-size-change) #(on-size-change % :width)) + + on-height-change + (mf/use-fn (mf/deps on-size-change) #(on-size-change % :height)) + + on-pos-x-change + (mf/use-fn (mf/deps on-position-change) #(on-position-change % :x)) + + on-pos-y-change + (mf/use-fn (mf/deps on-position-change) #(on-position-change % :y)) ;; CLIP CONTENT AND SHOW IN VIEWER on-change-clip-content @@ -277,9 +331,9 @@ (dwsh/update-shapes ids (fn [shape] (cls/change-show-in-viewer shape (not value))))) (when-not value - ;; when a frame is no longer shown in view mode, cannot have - ;; interactions that navigate to it. - (apply st/emit! (map #(dwi/remove-all-interactions-nav-to %) ids))) + ;; when a frame is no longer shown in view mode, cannot + ;; have interactions that navigate to it. + (run! st/emit! (map #(dwi/remove-all-interactions-nav-to %) ids))) (st/emit! (dwu/commit-undo-transaction undo-id))))) @@ -373,27 +427,28 @@ (when (options :position) [:div {:class (stl/css :position)} [:div {:class (stl/css-case :x-position true - :disabled disabled-position-x?) + :disabled disabled-position?) :title (tr "workspace.options.x")} [:span {:class (stl/css :icon-text)} "X"] [:> numeric-input* {:no-validate true :placeholder (if (= :multiple (:x values)) (tr "settings.multiple") "--") :on-change on-pos-x-change - :disabled disabled-position-x? + :disabled disabled-position? :class (stl/css :numeric-input) :value (:x values)}]] [:div {:class (stl/css-case :y-position true - :disabled disabled-position-y?) + :disabled disabled-position?) :title (tr "workspace.options.y")} [:span {:class (stl/css :icon-text)} "Y"] [:> numeric-input* {:no-validate true :placeholder (if (= :multiple (:y values)) (tr "settings.multiple") "--") - :disabled disabled-position-y? + :disabled disabled-position? :on-change on-pos-y-change :class (stl/css :numeric-input) :value (:y values)}]]]) - (when (or (options :rotation) (options :radius)) + (when (or (options :rotation) + (options :radius)) [:div {:class (stl/css :rotation-radius)} (when (options :rotation) [:div {:class (stl/css :rotation) @@ -409,7 +464,11 @@ :class (stl/css :numeric-input) :value (:rotation values)}]]) (when (options :radius) - [:> border-radius-menu* {:class (stl/css :border-radius) :ids ids :ids-with-children ids-with-children :values values :shape shape}])]) + [:> 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)) [:div {:class (stl/css :clip-show)} (when (options :clip-content) 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 1b8626b222..5fb56162b2 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 @@ -6,9 +6,9 @@ (ns app.main.ui.workspace.sidebar.options.shapes.bool (:require + [app.common.data.macros :as dm] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] [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.fill :as fill] @@ -21,31 +21,61 @@ [app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]] [rumext.v2 :as mf])) -(mf/defc options +(mf/defc options* [{:keys [shape] :as props}] - (let [ids [(:id shape)] - type (:type shape) - measure-values (select-keys shape measure-attrs) - stroke-values (select-keys shape stroke-attrs) - layer-values (select-keys shape layer-attrs) - constraint-values (select-keys shape constraint-attrs) - layout-item-values (select-keys shape layout-item-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) + (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]) - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) + measure-values + (select-keys shape measure-attrs) - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) + stroke-values + (select-keys shape stroke-attrs) - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) + layer-values + (select-keys shape layer-attrs) - is-layout-child-absolute? (ctl/item-absolute? shape) + constraint-values + (select-keys shape constraint-attrs) - ids (hooks/use-equal-memo ids) - parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids-ref)] + layout-item-values + (select-keys shape layout-item-attrs) + + layout-container-values + (select-keys shape layout-container-flex-attrs) + + is-layout-child-ref + (mf/with-memo [ids] + (refs/is-layout-child? ids)) + + is-layout-child? + (mf/deref is-layout-child-ref) + + is-flex-parent-ref + (mf/with-memo [ids] + (refs/flex-layout-child? ids)) + + is-flex-parent? + (mf/deref is-flex-parent-ref) + + is-grid-parent-ref + (mf/with-memo [ids] + (refs/grid-layout-child? ids)) + + is-grid-parent? + (mf/deref is-grid-parent-ref) + + is-layout-child-absolute? + (ctl/item-absolute? shape) + + parents-by-ids-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) + + parents + (mf/deref parents-by-ids-ref)] [:* [:& layer-menu {:ids ids @@ -55,7 +85,7 @@ [:> measures-menu* {:ids ids :type type :values measure-values - :shape shape}] + :shapes shapes}] [:& layout-container-menu {: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 e3f856541c..de2524a82e 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 @@ -6,9 +6,9 @@ (ns app.main.ui.workspace.sidebar.options.shapes.circle (:require + [app.common.data.macros :as dm] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] [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.fill :as fill] @@ -22,32 +22,61 @@ [app.main.ui.workspace.sidebar.options.menus.svg-attrs :refer [svg-attrs-menu]] [rumext.v2 :as mf])) -(mf/defc options +(mf/defc options* [{:keys [shape] :as props}] - (let [ids [(:id shape)] - type (:type shape) + (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) - stroke-values (select-keys shape stroke-attrs) - layer-values (select-keys shape layer-attrs) - constraint-values (select-keys shape constraint-attrs) - layout-item-values (select-keys shape layout-item-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) + measure-values + (select-keys shape measure-attrs) - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) + stroke-values + (select-keys shape stroke-attrs) - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) + layer-values + (select-keys shape layer-attrs) - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) + constraint-values + (select-keys shape constraint-attrs) - is-layout-child-absolute? (ctl/item-absolute? shape) + layout-item-values + (select-keys shape layout-item-attrs) - ids (hooks/use-equal-memo ids) - parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids-ref)] + layout-container-values + (select-keys shape layout-container-flex-attrs) + + is-layout-child-ref + (mf/with-memo [ids] + (refs/is-layout-child? ids)) + + is-layout-child? + (mf/deref is-layout-child-ref) + + is-flex-parent-ref + (mf/with-memo [ids] + (refs/flex-layout-child? ids)) + + is-flex-parent? + (mf/deref is-flex-parent-ref) + + is-grid-parent-ref + (mf/with-memo [ids] + (refs/grid-layout-child? ids)) + + is-grid-parent? + (mf/deref is-grid-parent-ref) + + is-layout-child-absolute? + (ctl/item-absolute? shape) + + parents-by-ids-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) + + parents + (mf/deref parents-by-ids-ref)] [:* [:& layer-menu {:ids ids :type type @@ -56,7 +85,7 @@ [:> measures-menu* {:ids ids :type type :values measure-values - :shape shape}] + :shapes shapes}] [:& layout-container-menu {: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 2319e89036..29d51767dd 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 @@ -31,49 +31,77 @@ (let [shape-id (dm/get-prop shape :id) shape-type (dm/get-prop shape :type) - ids (mf/with-memo [shape-id] - [shape-id]) + ids + (mf/with-memo [shape-id] + [shape-id]) - shapes (mf/with-memo [shape] - [shape]) + shapes + (mf/with-memo [shape] + [shape]) - stroke-values (select-keys shape stroke-attrs) - layer-values (select-keys shape layer-attrs) - measure-values (select-measure-keys shape) - constraint-values (select-keys shape constraint-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) - layout-item-values (select-keys shape layout-item-attrs) + stroke-values + (select-keys shape stroke-attrs) + + layer-values + (select-keys shape layer-attrs) + + measure-values + (select-measure-keys shape) + + constraint-values + (select-keys shape constraint-attrs) + + layout-container-values + (select-keys shape layout-container-flex-attrs) + + layout-item-values + (select-keys shape layout-item-attrs) is-layout-child-ref (mf/with-memo [ids] (refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref) is-flex-parent-ref (mf/with-memo [ids] (refs/flex-layout-child? ids)) + is-flex-parent? (mf/deref is-flex-parent-ref) is-grid-parent-ref (mf/with-memo [ids] (refs/grid-layout-child? ids)) + is-grid-parent? (mf/deref is-grid-parent-ref) parents-by-ids-ref (mf/with-memo [ids] (refs/parents-by-ids ids)) + parents (mf/deref parents-by-ids-ref) - is-layout-container? (ctl/any-layout? shape) - is-flex-layout? (ctl/flex-layout? shape) - is-grid-layout? (ctl/grid-layout? shape) - is-layout-child-absolute? (ctl/item-absolute? shape) - variants? (features/use-feature "variants/v1") - is-variant? (when variants? (ctk/is-variant-container? shape))] + is-layout-container? + (ctl/any-layout? shape) + + is-flex-layout? + (ctl/flex-layout? shape) + + is-grid-layout? + (ctl/grid-layout? shape) + + is-layout-child-absolute? + (ctl/item-absolute? shape) + + variants? + (features/use-feature "variants/v1") + + is-variant? + (when variants? (ctk/is-variant-container? shape))] [:* [:& layer-menu {:ids ids @@ -82,7 +110,7 @@ [:> measures-menu* {:ids ids :values measure-values :type shape-type - :shape shape}] + :shapes shapes}] [:& component-menu {:shapes shapes}] 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 a4ca975316..a7adc2aeda 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 @@ -8,9 +8,9 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] [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]] @@ -27,48 +27,89 @@ [app.main.ui.workspace.sidebar.options.shapes.multiple :refer [get-attrs]] [rumext.v2 :as mf])) -(mf/defc options - {::mf/wrap [mf/memo] - ::mf/wrap-props false} - [props] - (let [shape (unchecked-get props "shape") - shape-with-children (unchecked-get props "shape-with-children") - libraries (unchecked-get props "libraries") - objects (->> shape-with-children (group-by :id) (d/mapm (fn [_ v] (first v)))) - file-id (unchecked-get props "file-id") - layout-container-values (select-keys shape layout-container-flex-attrs) - ids [(:id shape)] - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) +(mf/defc options* + {::mf/wrap [mf/memo]} + [{:keys [shape shapes-with-children libraries file-id] :as props}] - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) + (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]) - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) + objects + (mf/with-memo [shapes-with-children] + (d/index-by :id shapes-with-children)) - is-layout-child-absolute? (ctl/item-absolute? shape) + layout-container-values + (select-keys shape layout-container-flex-attrs) - ids (hooks/use-equal-memo ids) - parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids-ref) + svg-values + (select-keys shape [:svg-attrs]) - type :group - [measure-ids measure-values] (get-attrs [shape] objects :measure) - [layer-ids layer-values] (get-attrs [shape] objects :layer) - [constraint-ids constraint-values] (get-attrs [shape] objects :constraint) - [fill-ids fill-values] (get-attrs [shape] objects :fill) - [shadow-ids _] (get-attrs [shape] objects :shadow) - [blur-ids blur-values] (get-attrs [shape] objects :blur) - [stroke-ids stroke-values] (get-attrs [shape] objects :stroke) - [text-ids text-values] (get-attrs [shape] objects :text) - [svg-ids svg-values] [[(:id shape)] (select-keys shape [:svg-attrs])] - [layout-item-ids layout-item-values] (get-attrs [shape] objects :layout-item)] + is-layout-child-ref + (mf/with-memo [ids] + (refs/is-layout-child? ids)) + is-layout-child? + (mf/deref is-layout-child-ref) + + is-flex-parent-ref + (mf/with-memo [ids] + (refs/flex-layout-child? ids)) + + is-flex-parent? + (mf/deref is-flex-parent-ref) + + is-grid-parent-ref + (mf/with-memo [ids] + (refs/grid-layout-child? ids)) + + is-grid-parent? + (mf/deref is-grid-parent-ref) + + is-layout-child-absolute? + (ctl/item-absolute? shape) + + parents-by-ids-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) + + parents + (mf/deref parents-by-ids-ref) + + [measure-ids measure-values] + (get-attrs shapes objects :measure) + + [layer-ids layer-values] + (get-attrs shapes objects :layer) + + [constraint-ids constraint-values] + (get-attrs shapes objects :constraint) + + [fill-ids fill-values] + (get-attrs shapes objects :fill) + + [shadow-ids] + (get-attrs shapes objects :shadow) + + [blur-ids blur-values] + (get-attrs shapes objects :blur) + + [stroke-ids stroke-values] + (get-attrs shapes objects :stroke) + + [text-ids text-values] + (get-attrs shapes objects :text) + + [layout-item-ids layout-item-values] + (get-attrs shapes objects :layout-item)] [:div {:class (stl/css :options)} [:& layer-menu {:type type :ids layer-ids :values layer-values}] - [:> measures-menu* {:type type :ids measure-ids :values measure-values :shape shape}] + [:> measures-menu* {:type type + :ids measure-ids + :values measure-values + :shapes shapes}] [:& layout-container-menu {:type type @@ -116,7 +157,6 @@ [:& ot/text-menu {:type type :ids text-ids :values text-values}]) (when-not (empty? svg-values) - [:& svg-attrs-menu {:ids svg-ids - :values svg-values}])])) + [:& svg-attrs-menu {:ids ids :values svg-values}])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs deleted file mode 100644 index 8dc58b05d9..0000000000 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs +++ /dev/null @@ -1,97 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) KALEIDOS INC - -(ns app.main.ui.workspace.sidebar.options.shapes.image - (:require - [app.common.types.shape.layout :as ctl] - [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] - [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.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.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*]] - [app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu*]] - [app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]] - [rumext.v2 :as mf])) - -(mf/defc options - [{:keys [shape] :as props}] - (let [ids [(:id shape)] - type (:type shape) - - measure-values (select-keys shape measure-attrs) - layer-values (select-keys shape layer-attrs) - constraint-values (select-keys shape constraint-attrs) - stroke-values (select-keys shape stroke-attrs) - layout-item-values (select-keys shape layout-item-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) - - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) - - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) - - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) - - is-layout-child-absolute? (ctl/item-absolute? shape) - - ids (hooks/use-equal-memo ids) - parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids-ref)] - [:* - [:& layer-menu {:ids ids - :type type - :values layer-values}] - - [:> measures-menu* {:ids ids - :type type - :values measure-values - :shape shape}] - - [:& layout-container-menu - {:type type - :ids [(:id shape)] - :values layout-container-values - :multiple false}] - - (when (and (= (count ids) 1) is-layout-child? is-grid-parent?) - [:& grid-cell/options - {:shape (first parents) - :cell (ctl/get-cell-by-shape-id (first parents) (first ids))}]) - - (when is-layout-child? - [:& layout-item-menu - {:ids ids - :type type - :values layout-item-values - :is-layout-child? true - :is-flex-parent? is-flex-parent? - :is-grid-parent? is-grid-parent? - :shape shape}]) - - (when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?) - [:& constraints-menu {:ids ids - :values constraint-values}]) - - [:> fill/fill-menu* - {:ids ids - :type type - :values shape}] - - [:& stroke-menu {:ids ids - :type type - :values stroke-values}] - - [:> shadow-menu* {:ids ids :values (get shape :shadow)}] - - [:& blur-menu {:ids ids - :values (select-keys shape [:blur])}]])) 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 2429021307..ae6295f6dc 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 @@ -9,14 +9,16 @@ (:require [app.common.attrs :as attrs] [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] [app.common.types.component :as ctk] [app.common.types.path :as path] [app.common.types.shape.attrs :refer [editable-attrs]] [app.common.types.shape.layout :as ctl] [app.common.types.text :as txt] + [app.common.weak :as weak] [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]] [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]] @@ -202,10 +204,6 @@ applies (some of them ignore some attributes)" [shapes objects attr-group] (let [attrs (group->attrs attr-group) - - default-text-attrs - (txt/get-default-text-attrs) - merge-attrs (fn [v1 v2] (cond @@ -213,8 +211,21 @@ (= attr-group :blur) (attrs/get-attrs-multi [v1 v2] attrs blur-eq blur-sel) :else (attrs/get-attrs-multi [v1 v2] attrs))) + merge-token-values + (fn [acc keys attrs] + (reduce + (fn [accum key] + (let [new-val (get attrs key) + existing (get accum key ::not-found)] + (cond + (= existing ::not-found) (assoc accum key new-val) + (= existing new-val) accum + :else (assoc accum key :multiple)))) + acc + keys)) + extract-attrs - (fn [[ids values] {:keys [id type] :as shape}] + (fn [[ids values token-acc] {:keys [id type applied-tokens] :as shape}] (let [read-mode (get-in type->read-mode [type attr-group]) editable-attrs (filter (get editable-attrs (:type shape)) attrs)] (case read-mode @@ -228,144 +239,192 @@ (into {} (map #(vector % nil)) editable-attrs) (cond (= attr-group :measure) (select-measure-keys shape) - :else (select-keys shape editable-attrs)))] + :else (select-keys shape editable-attrs))) + new-token-acc (merge-token-values token-acc editable-attrs applied-tokens)] [(conj ids id) - (merge-attrs values shape-values)]) + (merge-attrs values shape-values) + new-token-acc]) :text (let [shape-attrs (select-keys shape attrs) content-attrs - (attrs/get-text-attrs-multi shape default-text-attrs attrs) + (attrs/get-text-attrs-multi shape txt/default-text-attrs attrs) new-values (-> values (merge-attrs shape-attrs) - (merge-attrs content-attrs))] + (merge-attrs content-attrs)) + + new-token-acc (merge-token-values token-acc content-attrs applied-tokens)] [(conj ids id) - new-values]) + new-values + new-token-acc]) :children (let [children (->> (:shapes shape []) (map #(get objects %))) [new-ids new-values] (get-attrs* children objects attr-group)] - [(d/concat-vec ids new-ids) (merge-attrs values new-values)]) + [(d/concat-vec ids new-ids) (merge-attrs values new-values) {}]) [])))] - (reduce extract-attrs [[] []] shapes))) + (reduce extract-attrs [[] {} {}] shapes))) -(def get-attrs (memoize get-attrs*)) - -(defn basic-shape [_ shape] - (cond-> shape - :always - (dissoc :selrect :points :x :y :width :height :transform :transform-inverse :rotation :svg-transform :svg-viewbox :thumbnail) - - (= (:type shape) :path) - (dissoc :content))) +(def get-attrs + (weak/memoize get-attrs*)) (defn- is-bool-descendant? - [[_ shape] objects selected-shape-ids] + [objects selected-shape-ids shape] (let [parent-id (:parent-id shape) parent (get objects parent-id)] (cond - (nil? shape) false ;; failsafe - (contains? selected-shape-ids (:id shape)) false ;; if it is one of the selected shapes, it is considerer not a bool descendant - (= :bool (:type parent)) true ;; if its parent is of type bool, it is a bool descendant + (nil? shape) false ;; failsafe + (contains? selected-shape-ids (:id shape)) false ;; if it is one of the selected shapes, it is considerer not a bool descendant + (= :bool (:type parent)) true ;; if its parent is of type bool, it is a bool descendant :else (recur [parent-id parent] objects selected-shape-ids)))) ;; else, check its parent -(mf/defc options - {::mf/wrap [#(mf/memo' % (mf/check-props ["shapes" "shapes-with-children" "page-id" "file-id"]))] - ::mf/wrap-props false} - [props] - (let [shapes (unchecked-get props "shapes") - shapes-with-children (unchecked-get props "shapes-with-children") +(defn- check-options-props + [new-props old-props] + (and (= (unchecked-get new-props "shapes") + (unchecked-get old-props "shapes")) + (= (unchecked-get new-props "shapesWithChildren") + (unchecked-get old-props "shapesWithChildren")) + (= (unchecked-get new-props "pageId") + (unchecked-get old-props "pageId")) + (= (unchecked-get new-props "fileId") + (unchecked-get old-props "fileId")))) - ;; remove children from bool shapes - shape-ids (into #{} (map :id) shapes) +(mf/defc options* + {::mf/wrap [#(mf/memo' % check-options-props)]} + [{:keys [shapes shapes-with-children page-id file-id libraries] :as props}] + (let [shape-ids + (mf/with-memo [shapes] + (into #{} d/xf:map-id shapes)) + + is-layout-child-ref + (mf/with-memo [shape-ids] + (refs/is-layout-child? shape-ids)) + + is-layout-child? + (mf/deref is-layout-child-ref) + + is-flex-parent-ref + (mf/with-memo [shape-ids] + (refs/flex-layout-child? shape-ids)) + + is-flex-parent? + (mf/deref is-flex-parent-ref) + + is-grid-parent-ref + (mf/with-memo [shape-ids] + (refs/grid-layout-child? shape-ids)) + + is-grid-parent? + (mf/deref is-grid-parent-ref) + + has-flex-layout-container? + (some ctl/flex-layout? shapes) + + all-layout-child-ref + (mf/with-memo [shape-ids] + (refs/all-layout-child? shape-ids)) + + all-layout-child? + (mf/deref all-layout-child-ref) + + all-flex-layout-container? + (mf/with-memo [shapes] + (every? ctl/flex-layout? shapes)) + + show-caps? + (mf/with-memo [shapes] + (some #(and (cfh/path-shape? %) + (path/shape-with-open-path? %)) + shapes)) + + has-text? + (mf/with-memo [shapes] + (some cfh/text-shape? shapes)) - objects (->> shapes-with-children (group-by :id) (d/mapm (fn [_ v] (first v)))) objects - (into {} - (filter #(not (is-bool-descendant? % objects shape-ids))) - objects) + (mf/with-memo [shapes-with-children] + (let [objects (d/index-by :id shapes-with-children)] + (reduce-kv (fn [objects id object] + (if (is-bool-descendant? objects shape-ids object) + (dissoc objects id) + objects)) + objects + objects))) - workspace-modifiers (mf/deref refs/workspace-modifiers) - shapes (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers])) shapes) + [layer-ids layer-values] + (get-attrs shapes objects :layer) - page-id (unchecked-get props "page-id") - file-id (unchecked-get props "file-id") - shared-libs (unchecked-get props "libraries") + [text-ids text-values] + (get-attrs shapes objects :text) - show-caps (some #(and (= :path (:type %)) (path/shape-with-open-path? %)) shapes) + [constraint-ids constraint-values] + (get-attrs shapes objects :constraint) - ;; Selrect/points only used for measures and it's the one that changes the most. We separate it - ;; so we can memoize it - objects-no-measures (->> objects (d/mapm basic-shape)) - objects-no-measures (hooks/use-equal-memo objects-no-measures) + [fill-ids fill-values] + (get-attrs shapes objects :fill) + + [shadow-ids shadow-values] + (get-attrs shapes objects :shadow) + + [blur-ids blur-values] + (get-attrs shapes objects :blur) + + [stroke-ids stroke-values] + (get-attrs shapes objects :stroke) + + [exports-ids exports-values] + (get-attrs shapes objects :exports) + + [layout-container-ids layout-container-values] + (get-attrs shapes objects :layout-container) + + [layout-item-ids layout-item-values {}] + (get-attrs shapes objects :layout-item) + + components + (mf/with-memo [shapes] + (not-empty (filter ctk/instance-head? shapes))) + + workspace-modifiers + (mf/deref refs/workspace-modifiers) + + shapes + (mf/with-memo [workspace-modifiers shapes] + (into [] + (map (fn [shape] + (let [shape-id (dm/get-prop shape :id) + modifiers (dm/get-in workspace-modifiers [shape-id :modifiers])] + (gsh/transform-shape shape modifiers)))) + shapes)) type :multiple - all-types (into #{} (map :type shapes)) - ids (->> shapes (map :id)) - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) - - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) - - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) - - has-text? (contains? all-types :text) - - has-flex-layout-container? (->> shapes (some ctl/flex-layout?)) - - all-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/all-layout-child? ids)) - all-layout-child? (mf/deref all-layout-child-ref) - - all-flex-layout-container? (->> shapes (every? ctl/flex-layout?)) - - [measure-ids measure-values] (get-attrs shapes objects :measure) - - [layer-ids layer-values - text-ids text-values - constraint-ids constraint-values - fill-ids fill-values - shadow-ids shadow-values - blur-ids blur-values - stroke-ids stroke-values - exports-ids exports-values - layout-container-ids layout-container-values - layout-item-ids layout-item-values] - (mf/use-memo - (mf/deps shapes objects-no-measures) - (fn [] - (into - [] - (mapcat identity) - [(get-attrs shapes objects-no-measures :layer) - (get-attrs shapes objects-no-measures :text) - (get-attrs shapes objects-no-measures :constraint) - (get-attrs shapes objects-no-measures :fill) - (get-attrs shapes objects-no-measures :shadow) - (get-attrs shapes objects-no-measures :blur) - (get-attrs shapes objects-no-measures :stroke) - (get-attrs shapes objects-no-measures :exports) - (get-attrs shapes objects-no-measures :layout-container) - (get-attrs shapes objects-no-measures :layout-item)]))) - - components (filter ctk/instance-head? shapes)] + ;; NOTE: we only need transformed shapes for the measure menu, + ;; the rest of menus can live with shapes not transformed; we + ;; also don't use the memoized version of get-attrs because it + ;; makes no sense because the shapes object are changed on + ;; each rerender. + [measure-ids measure-values] + (get-attrs* shapes objects :measure)] [:div {:class (stl/css :options)} (when-not (empty? layer-ids) [:& layer-menu {:type type :ids layer-ids :values layer-values}]) (when-not (empty? measure-ids) - [:> measures-menu* {:type type :all-types all-types :ids measure-ids :values measure-values :shape shapes}]) + [:> measures-menu* + {:type type + :ids measure-ids + :values measure-values + :shapes shapes}]) - (when-not (empty? components) + (when (some? components) [:& component-menu {:shapes components}]) [:& layout-container-menu @@ -394,15 +453,18 @@ [:> fill/fill-menu* {:type type :ids fill-ids :values fill-values}]) (when-not (empty? stroke-ids) - [:& stroke-menu {:type type :ids stroke-ids :show-caps show-caps :values stroke-values + [:& stroke-menu {:type type + :ids stroke-ids + :show-caps show-caps? + :values stroke-values :disable-stroke-style has-text?}]) (when-not (empty? shapes) [:> color-selection-menu* {:file-id file-id :type type - :shapes (vals objects-no-measures) - :libraries shared-libs}]) + :shapes shapes + :libraries libraries}]) (when-not (empty? shadow-ids) [:> shadow-menu* {:type type 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 5c2e418f7b..8ad6e802ea 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 @@ -6,9 +6,9 @@ (ns app.main.ui.workspace.sidebar.options.shapes.path (:require + [app.common.data.macros :as dm] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] [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.fill :as fill] @@ -22,32 +22,68 @@ [app.main.ui.workspace.sidebar.options.menus.svg-attrs :refer [svg-attrs-menu]] [rumext.v2 :as mf])) -(mf/defc options +(mf/defc options* [{:keys [shape] :as props}] - (let [ids [(:id shape)] - type (:type shape) + (let [ids + (mf/with-memo [shape] + [(dm/get-prop shape :id)]) - measure-values (select-keys shape measure-attrs) - stroke-values (select-keys shape stroke-attrs) - layer-values (select-keys shape layer-attrs) - constraint-values (select-keys shape constraint-attrs) - layout-item-values (select-keys shape layout-item-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) + shapes + (mf/with-memo [shape] + [shape]) - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) + type + (dm/get-prop shape :type) - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) + measure-values + (select-keys shape measure-attrs) - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) + stroke-values + (select-keys shape stroke-attrs) - is-layout-child-absolute? (ctl/item-absolute? shape) + layer-values + (select-keys shape layer-attrs) + + constraint-values + (select-keys shape constraint-attrs) + + layout-item-values + (select-keys shape layout-item-attrs) + + layout-container-values + (select-keys shape layout-container-flex-attrs) + + is-layout-child-ref + (mf/with-memo [ids] + (refs/is-layout-child? ids)) + + is-layout-child? + (mf/deref is-layout-child-ref) + + is-flex-parent-ref + (mf/with-memo [ids] + (refs/flex-layout-child? ids)) + + is-flex-parent? + (mf/deref is-flex-parent-ref) + + is-grid-parent-ref + (mf/with-memo [ids] + (refs/grid-layout-child? ids)) + + is-grid-parent? + (mf/deref is-grid-parent-ref) + + is-layout-child-absolute? + (ctl/item-absolute? shape) + + parents-by-ids-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) + + parents + (mf/deref parents-by-ids-ref)] - ids (hooks/use-equal-memo ids) - parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids-ref)] [:* [:& layer-menu {:ids ids :type type @@ -55,7 +91,7 @@ [:> measures-menu* {:ids ids :type type :values measure-values - :shape shape}] + :shapes shapes}] [:& layout-container-menu {:type type 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 69a082a82d..75d67efe29 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 @@ -6,9 +6,9 @@ (ns app.main.ui.workspace.sidebar.options.shapes.rect (:require + [app.common.data.macros :as dm] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] [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.fill :as fill] @@ -16,39 +16,67 @@ [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*]] + [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu*]] [app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu*]] [app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]] [app.main.ui.workspace.sidebar.options.menus.svg-attrs :refer [svg-attrs-menu]] [rumext.v2 :as mf])) -(mf/defc options - {::mf/wrap [mf/memo] - ::mf/wrap-props false} +(mf/defc options* [{:keys [shape]}] - (let [shape-id (:id shape) - ids (hooks/use-equal-memo [shape-id]) - type (:type shape) - measure-values (select-measure-keys shape) - layer-values (select-keys shape layer-attrs) - constraint-values (select-keys shape constraint-attrs) - stroke-values (select-keys shape stroke-attrs) - layout-item-values (select-keys shape layout-item-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) + (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]) - is-layout-child* (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child*) + measure-values + (select-keys shape measure-attrs) - is-flex-parent* (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent*) + stroke-values + (select-keys shape stroke-attrs) - is-grid-parent* (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent*) + layer-values + (select-keys shape layer-attrs) - is-layout-child-absolute? (ctl/item-absolute? shape) + constraint-values + (select-keys shape constraint-attrs) - parents-by-ids* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids*)] + layout-item-values + (select-keys shape layout-item-attrs) + + layout-container-values + (select-keys shape layout-container-flex-attrs) + + is-layout-child-ref + (mf/with-memo [ids] + (refs/is-layout-child? ids)) + + is-layout-child? + (mf/deref is-layout-child-ref) + + is-flex-parent-ref + (mf/with-memo [ids] + (refs/flex-layout-child? ids)) + + is-flex-parent? + (mf/deref is-flex-parent-ref) + + is-grid-parent-ref + (mf/with-memo [ids] + (refs/grid-layout-child? ids)) + + is-grid-parent? + (mf/deref is-grid-parent-ref) + + is-layout-child-absolute? + (ctl/item-absolute? shape) + + parents-by-ids-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) + + parents + (mf/deref parents-by-ids-ref)] [:* [:& layer-menu {:ids ids @@ -57,7 +85,7 @@ [:> measures-menu* {:ids ids :type type :values measure-values - :shape shape}] + :shapes shapes}] [:& layout-container-menu {:type type 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 66dc19614f..e0af0d6d07 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 @@ -7,10 +7,10 @@ (ns app.main.ui.workspace.sidebar.options.shapes.svg-raw (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.types.color :as cc] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] - [app.main.ui.hooks :as hooks] [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.fill :as fill] @@ -26,15 +26,10 @@ ;; This is a list of svg tags that can be grouped in shape-container ;; this allows them to have gradients, shadows and masks -(def svg-elements #{:svg :g :circle :ellipse :image :line :path :polygon :polyline :rect :symbol :text :textPath}) +(def ^:private svg-elements + #{:svg :g :circle :ellipse :image :line :path :polygon :polyline :rect :symbol :text :textPath}) -(defn hex->number [_] 1) - -(defn shorthex->longhex [hex] - (let [[_ r g b] hex] - (str "#" r r g g b b))) - -(defn parse-color [color] +(defn- parse-color [color] (try (cond (or (not color) (= color "none")) nil @@ -51,8 +46,7 @@ (.error js/console "Error parsing color" e) nil))) - -(defn get-fill-values [shape] +(defn- get-fill-values [shape] (let [fill-values (select-keys shape fill/fill-attrs) color (-> (or (get-in shape [:content :attrs :fill]) (get-in shape [:content :attrs :style :fill])) @@ -64,7 +58,7 @@ fill-values)] fill-values)) -(defn get-stroke-values [shape] +(defn- get-stroke-values [shape] (let [stroke-values (select-keys shape stroke-attrs) color (-> (or (get-in shape [:content :attrs :stroke]) (get-in shape [:content :attrs :style :stroke])) @@ -92,42 +86,75 @@ stroke-values)] stroke-values)) -(mf/defc options +(mf/defc options* {::mf/wrap [mf/memo]} [{:keys [shape] :as props}] - (let [ids [(:id shape)] - type (:type shape) + (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]) - {:keys [tag] :as content} (:content shape) - measure-values (select-keys shape measure-attrs) - constraint-values (select-keys shape constraint-attrs) - fill-values (get-fill-values shape) - stroke-values (get-stroke-values shape) - layout-item-values (select-keys shape layout-item-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) + {:keys [tag] :as content} + (get shape :content) - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) + fill-values + (mf/with-memo [shape] + (get-fill-values shape)) - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) + stroke-values + (mf/with-memo [shape] + (get-stroke-values shape)) - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) + measure-values + (select-keys shape measure-attrs) - is-layout-child-absolute? (ctl/item-absolute? shape) + constraint-values + (select-keys shape constraint-attrs) - ids (hooks/use-equal-memo ids) - parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids-ref)] + layout-item-values + (select-keys shape layout-item-attrs) + + layout-container-values + (select-keys shape layout-container-flex-attrs) + + is-layout-child-ref + (mf/with-memo [ids] + (refs/is-layout-child? ids)) + + is-layout-child? + (mf/deref is-layout-child-ref) + + is-flex-parent-ref + (mf/with-memo [ids] + (refs/flex-layout-child? ids)) + + is-flex-parent? + (mf/deref is-flex-parent-ref) + + is-grid-parent-ref + (mf/with-memo [ids] + (refs/grid-layout-child? ids)) + + is-grid-parent? + (mf/deref is-grid-parent-ref) + + is-layout-child-absolute? + (ctl/item-absolute? shape) + + parents-by-ids-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) + + parents + (mf/deref parents-by-ids-ref)] (when (contains? svg-elements tag) [:* [:> measures-menu* {:ids ids :type type :values measure-values - :shape shape}] + :shapes shapes}] [:& layout-container-menu {:type type 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 4cc88c624b..8e344ad41d 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 @@ -6,14 +6,13 @@ (ns app.main.ui.workspace.sidebar.options.shapes.text (:require - [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.types.shape.layout :as ctl] [app.common.types.text :as txt] [app.main.data.workspace.texts :as dwt] [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.hooks :as hooks] [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]] @@ -28,68 +27,102 @@ [app.main.ui.workspace.sidebar.options.menus.text :refer [text-menu]] [rumext.v2 :as mf])) -(mf/defc options +(mf/defc options* [{:keys [shape file-id libraries] :as props}] - (let [ids [(:id shape)] - type (:type shape) + (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]) - is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref) + measure-values + (select-keys shape measure-attrs) - is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids)) - is-flex-parent? (mf/deref is-flex-parent-ref) + stroke-values + (select-keys shape stroke-attrs) - is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids)) - is-grid-parent? (mf/deref is-grid-parent-ref) + layer-values + (select-keys shape layer-attrs) - layout-container-values (select-keys shape layout-container-flex-attrs) - is-layout-child-absolute? (ctl/item-absolute? shape) + layout-item-values + (select-keys shape layout-item-attrs) - ids (hooks/use-equal-memo ids) - parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) - parents (mf/deref parents-by-ids-ref) + layout-container-values + (select-keys shape layout-container-flex-attrs) - state-map (if (features/active-feature? @st/state "text-editor/v2") - (mf/deref refs/workspace-v2-editor-state) - (mf/deref refs/workspace-editor-state)) + is-layout-child-ref + (mf/with-memo [ids] + (refs/is-layout-child? ids)) - editor-state (when (not (features/active-feature? @st/state "text-editor/v2")) - (get state-map (:id shape))) + is-layout-child? + (mf/deref is-layout-child-ref) - layer-values (select-keys shape layer-attrs) - editor-instance (when (features/active-feature? @st/state "text-editor/v2") - (mf/deref refs/workspace-editor)) + is-flex-parent-ref + (mf/with-memo [ids] + (refs/flex-layout-child? ids)) - fill-values (dwt/current-text-values - {:editor-state editor-state - :editor-instance editor-instance - :shape shape - :attrs (conj txt/text-fill-attrs :fills)}) + is-flex-parent? + (mf/deref is-flex-parent-ref) - fill-values (if (not (contains? fill-values :fills)) - ;; Old fill format - {:fills [fill-values]} - fill-values) + is-grid-parent-ref + (mf/with-memo [ids] + (refs/grid-layout-child? ids)) - stroke-values (select-keys shape stroke-attrs) + is-grid-parent? + (mf/deref is-grid-parent-ref) - text-values (d/merge - (select-keys shape [:grow-type]) - (select-keys shape fill/fill-attrs) - (dwt/current-root-values - {:shape shape - :attrs txt/root-attrs}) - (dwt/current-paragraph-values - {:editor-state editor-state - :editor-instance editor-instance - :shape shape - :attrs txt/paragraph-attrs}) - (dwt/current-text-values - {:editor-state editor-state - :editor-instance editor-instance - :shape shape - :attrs txt/text-node-attrs})) - layout-item-values (select-keys shape layout-item-attrs)] + is-layout-child-absolute? + (ctl/item-absolute? shape) + + parents-by-ids-ref + (mf/with-memo [ids] + (refs/parents-by-ids ids)) + + parents + (mf/deref parents-by-ids-ref) + + state-map + (if (features/active-feature? @st/state "text-editor/v2") + (mf/deref refs/workspace-v2-editor-state) + (mf/deref refs/workspace-editor-state)) + + editor-state + (when (not (features/active-feature? @st/state "text-editor/v2")) + (get state-map id)) + + editor-instance + (when (features/active-feature? @st/state "text-editor/v2") + (mf/deref refs/workspace-editor)) + + fill-values + (dwt/current-text-values + {:editor-state editor-state + :editor-instance editor-instance + :shape shape + :attrs (conj txt/text-fill-attrs :fills)}) + + fill-values + (if (not (contains? fill-values :fills)) + ;; Old fill format + {:fills [fill-values]} + fill-values) + + text-values + (merge + (select-keys shape [:grow-type]) + (select-keys shape fill/fill-attrs) + (dwt/current-root-values + {:shape shape + :attrs txt/root-attrs}) + (dwt/current-paragraph-values + {:editor-state editor-state + :editor-instance editor-instance + :shape shape + :attrs txt/paragraph-attrs}) + (dwt/current-text-values + {:editor-state editor-state + :editor-instance editor-instance + :shape shape + :attrs txt/text-node-attrs}))] [:* [:& layer-menu {:ids ids @@ -98,12 +131,12 @@ [:> measures-menu* {:ids ids :type type - :values (select-keys shape measure-attrs) - :shape shape}] + :values measure-values + :shapes shapes}] [:& layout-container-menu {:type type - :ids [(:id shape)] + :ids ids :values layout-container-values :multiple false}] @@ -145,7 +178,7 @@ (when (= :multiple (:fills fill-values)) [:> color-selection-menu* {:type type - :shapes [shape] + :shapes shapes :file-id file-id :libraries libraries}]) diff --git a/frontend/src/app/main/ui/workspace/tokens/management.cljs b/frontend/src/app/main/ui/workspace/tokens/management.cljs index 19d606c955..2ec2464359 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management.cljs @@ -53,7 +53,7 @@ (mf/defc tokens-section* {::mf/private true} - [{:keys [tokens-lib]}] + [{:keys [tokens-lib active-tokens resolved-active-tokens]}] (let [objects (mf/deref refs/workspace-page-objects) selected (mf/deref refs/selected-shapes) open-status (mf/deref ref:token-type-open-status) @@ -66,18 +66,9 @@ (mf/with-memo [selected-shapes objects] (some #(ctsl/any-layout-immediate-child? objects %) selected-shapes)) - active-theme-tokens - (mf/with-memo [tokens-lib] - (if tokens-lib - (ctob/get-tokens-in-active-sets tokens-lib) - {})) - - ;; Resolve tokens as second step - active-theme-tokens' - (sd/use-resolved-tokens* active-theme-tokens) - ;; This only checks for the currently explicitly selected set ;; name, it is ephimeral and can be nil + ;; FIXME: this is a repeated deref for the same `:workspace-tokens` state selected-token-set-name (mf/deref refs/selected-token-set-name) @@ -92,8 +83,8 @@ (ctob/get-tokens-map selected-token-set)) tokens - (mf/with-memo [active-theme-tokens selected-token-set-tokens] - (merge active-theme-tokens selected-token-set-tokens)) + (mf/with-memo [active-tokens selected-token-set-tokens] + (merge active-tokens selected-token-set-tokens)) tokens (sd/use-resolved-tokens* tokens) @@ -154,7 +145,7 @@ :type type :selected-shapes selected-shapes :is-selected-inside-layout is-selected-inside-layout - :active-theme-tokens active-theme-tokens' + :active-theme-tokens resolved-active-tokens :tokens tokens}])) (for [type empty-group] @@ -162,5 +153,5 @@ :type type :selected-shapes selected-shapes :is-selected-inside-layout :is-selected-inside-layout - :active-theme-tokens active-theme-tokens' + :active-theme-tokens resolved-active-tokens :tokens []}])])) diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index ac31db6008..e00b09286f 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -144,16 +144,12 @@ :on-click open-settings-modal}])])) (mf/defc tokens-sidebar-tab* - {::mf/wrap [mf/memo]} - [] + [{:keys [tokens-lib] :as props}] (let [{on-pointer-down-pages :on-pointer-down on-lost-pointer-capture-pages :on-lost-pointer-capture on-pointer-move-pages :on-pointer-move size-pages-opened :size} - (use-resize-hook :tokens 200 38 "0.6" :y false nil) - - tokens-lib - (mf/deref refs/tokens-lib)] + (use-resize-hook :tokens 200 38 "0.6" :y false nil)] [:div {:class (stl/css :sidebar-wrapper)} [:> token-management-section* @@ -166,5 +162,5 @@ :on-lost-pointer-capture on-lost-pointer-capture-pages :on-pointer-move on-pointer-move-pages} [:div {:class (stl/css :resize-handle-horiz)}]] - [:> tokens-section* {:tokens-lib tokens-lib}]] + [:> tokens-section* props]] [:> import-export-button*]]))