♻️ Refactor right sidebar state management

Also removing duplicated refs and improve efficiency of
several other refs used on sidebar.
This commit is contained in:
Andrey Antukh
2025-08-27 10:47:09 +02:00
parent bda24f3829
commit a303df9c34
36 changed files with 473 additions and 307 deletions

View File

@@ -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)

View File

@@ -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",
{

View File

@@ -91,7 +91,7 @@
}
}
},
"~:id": "~uc7ce0794-0992-8105-8004-38f28044384a",
"~:id": "~u66697432-c33d-8055-8006-2c62cc084cad",
"~:name": "Page 1"
}
}

View File

@@ -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": {

View File

@@ -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"
}
}

View File

@@ -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",
{

View File

@@ -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"

View File

@@ -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",

View File

@@ -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", {

View File

@@ -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");

View File

@@ -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" });

View File

@@ -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"

View File

@@ -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))

View File

@@ -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))))))

View File

@@ -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))

View File

@@ -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}]]))

View File

@@ -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))

View File

@@ -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}]]]))

View File

@@ -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}]))]]]))

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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}

View File

@@ -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}]]))

View File

@@ -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}]]))

View File

@@ -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}]]))

View File

@@ -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}]]))

View File

@@ -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}])]))

View File

@@ -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}]]))

View File

@@ -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}]]))

View File

@@ -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}]])))

View File

@@ -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}]]))

View File

@@ -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