Compare commits

...

5 Commits

Author SHA1 Message Date
Eva Marco
f02e8467bb ♻️ Extract common code to one file 2026-01-27 14:42:33 +01:00
Eva Marco
9e8e4909db 🎉 Add test 2026-01-27 11:56:43 +01:00
Eva Marco
64a6e51997 ♻️ Replace margin inputs 2026-01-27 08:56:37 +01:00
Eva Marco
f6dfac6e1f Add test 2026-01-27 08:56:37 +01:00
Eva Marco
4c823e7c74 Replace opacity numeric input 2026-01-27 08:56:37 +01:00
26 changed files with 929 additions and 509 deletions

View File

@@ -474,6 +474,8 @@
:height #{:sizing :dimensions}
:max-width #{:sizing :dimensions}
:max-height #{:sizing :dimensions}
:min-width #{:sizing :dimensions}
:min-height #{:sizing :dimensions}
:x #{:dimensions}
:y #{:dimensions}
:rotation #{:number :rotation}

View File

@@ -759,4 +759,87 @@ test.describe("Tokens: Apply token", () => {
});
await expect(StrokeWidthPillSmall).toBeVisible();
});
test("User applies margin token to a shape", async ({ page }) => {
const workspace = new WorkspacePage(page, {
textEditor: true,
});
// Set up
await workspace.mockConfigFlags(["enable-feature-token-input"]);
await workspace.setupEmptyFile();
await workspace.mockGetFile("workspace/get-file-layout-stroke-token-json");
await workspace.goToWorkspace();
// Select shape apply stroke
await workspace.layers
.getByTestId("layer-row")
.nth(1)
.getByRole("button", { name: "Toggle layer" })
.click();
await workspace.layers.getByTestId("layer-row").nth(2).click();
const rightSidebar = page.getByTestId("right-sidebar");
await expect(rightSidebar).toBeVisible();
await rightSidebar.getByTestId("add-stroke").click();
// Apply margin token from token panel
const tokensTab = page.getByRole("tab", { name: "Tokens" });
await expect(tokensTab).toBeVisible();
await tokensTab.click();
await page.getByRole("button", { name: "Dimensions 4" }).click();
await page.getByRole("button", { name: "dim", exact: true }).click();
const tokensSidebar = workspace.tokensSidebar;
await expect(
tokensSidebar.getByRole("button", { name: "dim.md" }),
).toBeVisible();
await tokensSidebar
.getByRole("button", { name: "dim.md" })
.click({ button: "right" });
await page
.getByTestId("tokens-context-menu-for-token")
.getByText("Spacing")
.hover();
await page
.getByTestId("tokens-context-menu-for-token")
.getByText("Horizontal")
.click();
// Check if token pill is visible on right sidebar
const layoutItemSectionSidebar = rightSidebar.getByRole("region", {
name: "layout item menu",
});
await expect(layoutItemSectionSidebar).toBeVisible();
const marginPillMd = layoutItemSectionSidebar.getByRole("button", {
name: "dim.md",
});
await expect(marginPillMd).toBeVisible();
await marginPillMd.click();
const dimensionTokenOptionXl = page.getByRole("option", { name: "dim.xl" });
await expect(dimensionTokenOptionXl).toBeVisible();
await dimensionTokenOptionXl.click();
const marginPillXL = layoutItemSectionSidebar.getByRole("button", {
name: "dim.xl",
});
await expect(marginPillXL).toBeVisible();
// Detach token from right sidebar and apply another from dropdown
const detachButton = layoutItemSectionSidebar.getByRole("button", {
name: "Detach token",
});
await detachButton.click();
await expect(marginPillXL).not.toBeVisible();
const horizontalMarginInput = layoutItemSectionSidebar.getByText('Horizontal marginOpen token');
await expect(horizontalMarginInput).toBeVisible();
const tokenDropdown = horizontalMarginInput.getByRole('button', { name: 'Open token list' });
await tokenDropdown.click();
await expect(dimensionTokenOptionXl).toBeVisible();
await dimensionTokenOptionXl.click();
await expect(marginPillXL).toBeVisible();
});
});

View File

@@ -539,10 +539,11 @@
value shape-ids
#{:stroke-width}
page-id))
(some attributes #{:max-width :max-height})
(some attributes #{:max-width :max-height :layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w})
(conj #(update-layout-sizing-limits
value shape-ids
(set (filter attributes #{:max-width :max-height}))
(set (filter attributes #{:max-width :max-height :layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w}))
page-id))))
(defn apply-dimensions-token

View File

@@ -189,6 +189,7 @@
:float
:string
[:= :multiple]]]]
[:text-icon {:optional true} :string]
[:default {:optional true} [:maybe :string]]
[:placeholder {:optional true} :string]
[:icon {:optional true} [:maybe schema:icon]]
@@ -216,7 +217,8 @@
is-selected-on-focus nillable
tokens applied-token empty-to-end
on-change on-blur on-focus on-detach
property align ref name]
property align ref name
text-icon]
:rest props}]
(let [;; NOTE: we use mfu/bean here for transparently handle
@@ -637,14 +639,23 @@
:on-change store-raw-value
:variant "comfortable"
:disabled disabled
:slot-start (when icon
(mf/html [:> tooltip*
{:content property
:id property}
[:> icon* {:icon-id icon
:size "s"
:aria-labelledby property
:class (stl/css :icon)}]]))
:slot-start (when (or icon text-icon)
(mf/html
[:> tooltip*
{:content property
:id property}
(cond
icon
[:> icon*
{:icon-id icon
:size "s"
:aria-labelledby property
:class (stl/css :icon)}]
text-icon
[:div {:class (stl/css :text-icon)
:aria-labelledby property}
text-icon])]))
:slot-end (when-not disabled
(when (some? tokens)
(mf/html [:> icon-button* {:variant "ghost"
@@ -676,14 +687,23 @@
:disabled disabled
:on-blur on-blur
:class inner-class
:slot-start (when icon
(mf/html [:> tooltip*
{:content property
:id property}
[:> icon* {:icon-id icon
:size "s"
:aria-labelledby property
:class (stl/css :icon)}]]))
:slot-start (when (or icon text-icon)
(mf/html
[:> tooltip*
{:content property
:id property}
(cond
icon
[:> icon*
{:icon-id icon
:size "s"
:aria-labelledby property
:class (stl/css :icon)}]
text-icon
[:div {:class (stl/css :text-icon)
:aria-labelledby property}
text-icon])]))
:token-wrapper-ref token-wrapper-ref
:token-detach-btn-ref token-detach-btn-ref
:detach-token detach-token})))]
@@ -718,21 +738,41 @@
(mf/with-effect [dropdown-options]
(mf/set-ref-val! options-ref dropdown-options))
[:div {:class (dm/str class " " (stl/css :input-wrapper))
:ref wrapper-ref}
(if (some? icon)
[:div {:class (dm/str class " " (stl/css :input-wrapper))
:ref wrapper-ref}
(if (and (some? token-applied)
(not= :multiple token-applied))
[:> token-field* token-props]
[:> input-field* input-props])
(if (and (some? token-applied)
(not= :multiple token-applied))
[:> token-field* token-props]
[:> input-field* input-props])
(when ^boolean is-open
(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}]))]))
(when ^boolean is-open
(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}]))]
[:div {:class (dm/str class " " (stl/css :input-wrapper))
:aria-labelledby property
:ref wrapper-ref}
(if (and (some? token-applied)
(not= :multiple token-applied))
[:> token-field* token-props]
[:> input-field* input-props])
(when ^boolean is-open
(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}]))])))

View File

@@ -29,7 +29,14 @@
.icon {
color: var(--color-foreground-secondary);
min-width: var(--sp-l);
min-inline-size: var(--sp-l);
}
.text-icon {
color: var(--color-foreground-secondary);
@include t.use-typography("code-font");
inline-size: fit-content;
min-inline-size: px2rem(40);
}
.invisible-button {

View File

@@ -17,7 +17,7 @@
--token-field-outline-color: none;
--token-field-height: var(--sp-xxxl);
--token-field-margin: unset;
display: grid;
display: inline-flex;
column-gap: var(--sp-xs);
align-items: center;
position: relative;

View File

@@ -119,6 +119,7 @@
[:button {:class (stl/css-case
:toggle-content true
:inverse expanded?)
:aria-label "Toggle layer"
:on-click on-toggle-collapse}
deprecated-icon/arrow])

View File

@@ -3,17 +3,15 @@
(:require
[app.common.data.macros :as dm]
[app.common.types.shape.radius :as ctsr]
[app.common.types.token :as tk]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.features :as features]
[app.main.store :as st]
[app.main.ui.components.numeric-input :as deprecated-input]
[app.main.ui.context :as muc]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
[app.util.i18n :as i18n :refer [tr]]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]
@@ -46,63 +44,6 @@
(identical? (get old-values :r4)
(get new-values :r4)))))
(mf/defc numeric-input-wrapper*
{::mf/private true}
[{:keys [values name applied-tokens align on-detach radius] :rest props}]
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
tokens (mf/with-memo [tokens name]
(delay
(-> (deref tokens)
(select-keys (get tk/tokens-by-input name))
(not-empty))))
on-detach-attr
(mf/use-fn
(mf/deps on-detach name)
#(on-detach % name))
r1-value (get applied-tokens :r1)
all-token-equal? (and (seq applied-tokens) (all-equal? applied-tokens))
all-values-equal? (all-equal? values)
applied-token (cond
(not (seq applied-tokens))
nil
(and (= radius :all) (or (not all-values-equal?) (not all-token-equal?)))
:multiple
(and all-token-equal? all-values-equal? (= radius :all))
r1-value
:else
(get applied-tokens radius))
placeholder (if (= radius :all)
(cond
(or (not all-values-equal?)
(not all-token-equal?))
(tr "settings.multiple")
:else
"--")
(cond
(or (= :multiple (:applied-tokens values))
(= :multiple (get values name)))
(tr "settings.multiple")
:else
"--"))
props (mf/spread-props props
{:placeholder placeholder
:applied-token applied-token
:tokens (if (delay? tokens) @tokens tokens)
:align align
:on-detach on-detach-attr
:value values})]
[:> numeric-input* props]))
(mf/defc border-radius-menu*
{::mf/wrap [#(mf/memo' % check-border-radius-menu-props)]}
[{:keys [class ids values applied-tokens]}]
@@ -110,6 +51,7 @@
(features/use-feature "tokens/numeric-input")
all-values-equal? (all-equal? values)
all-token-equal? (and (seq applied-tokens) (all-equal? applied-tokens))
radius-expanded* (mf/use-state false)
radius-expanded (deref radius-expanded*)
@@ -235,18 +177,31 @@
:on-detach on-detach-all
:icon i/corner-radius
:min 0
:name :border-radius
:attr :border-radius
:nillable true
:property (tr "workspace.options.radius")
:class (stl/css :radius-wrapper)
:applied-tokens applied-tokens
:radius :all
:applied-token (cond
(not (seq applied-tokens))
nil
(or (not all-values-equal?) (not all-token-equal?))
:multiple
:else
(get applied-tokens :r1))
:align :right
:values (if all-values-equal?
(if (nil? (:r1 values))
0
(:r1 values))
nil)}]
:placeholder (cond
(or (not all-values-equal?)
(not all-token-equal?))
(tr "settings.multiple")
:else
"--")
:value (if all-values-equal?
(if (nil? (:r1 values))
0
(:r1 values))
nil)}]
[:div {:class (stl/css :radius-1)
:title (tr "workspace.options.radius")}
@@ -276,56 +231,76 @@
{:on-change on-radius-r1-change
:on-detach on-detach-r1
:min 0
:name :border-radius
:attr :border-radius
:property (tr "workspace.options.radius-top-left")
:applied-tokens applied-tokens
:radius :r1
:applied-token (get applied-tokens :r1)
:align :right
:placeholder (cond
(or (= :multiple (:applied-tokens values))
(= :multiple (get values :r1)))
(tr "settings.multiple")
:else
"--")
:class (stl/css :radius-wrapper :dropdown-offset)
:inner-class (stl/css :no-icon-input)
:values (:r1 values)}]
:value (:r1 values)}]
[:> numeric-input-wrapper*
{:on-change on-radius-r2-change
:on-detach on-detach-r2
:min 0
:name :border-radius
:attr :border-radius
:nillable true
:property (tr "workspace.options.radius-top-right")
:applied-tokens applied-tokens
:applied-token (get applied-tokens :r2)
:align :right
:class (stl/css :radius-wrapper)
:inner-class (stl/css :no-icon-input)
:radius :r2
:values (:r2 values)}]
:placeholder (cond
(or (= :multiple (:applied-tokens values))
(= :multiple (get values :r2)))
(tr "settings.multiple")
:else
"--")
:value (:r2 values)}]
[:> numeric-input-wrapper*
{:on-change on-radius-r4-change
:on-detach on-detach-r4
:min 0
:name :border-radius
:attr :border-radius
:nillable true
:property (tr "workspace.options.radius-bottom-left")
:applied-tokens applied-tokens
:applied-token (get applied-tokens :r4)
:class (stl/css :radius-wrapper :dropdown-offset)
:inner-class (stl/css :no-icon-input)
:radius :r4
:placeholder (cond
(or (= :multiple (:applied-tokens values))
(= :multiple (get values :r4)))
(tr "settings.multiple")
:else
"--")
:align :right
:values (:r4 values)}]
:value (:r4 values)}]
[:> numeric-input-wrapper*
{:on-change on-radius-r3-change
:on-detach on-detach-r3
:min 0
:name :border-radius
:attr :border-radius
:nillable true
:property (tr "workspace.options.radius-bottom-right")
:applied-tokens applied-tokens
:radius :r3
:applied-token (get applied-tokens :r3)
:placeholder (cond
(or (= :multiple (:applied-tokens values))
(= :multiple (get values :r3)))
(tr "settings.multiple")
:else
"--")
:align :right
:class (stl/css :radius-wrapper)
:inner-class (stl/css :no-icon-input)
:values (:r3 values)}]]
:value (:r3 values)}]]
[:div {:class (stl/css :radius-4)}
[:div {:class (stl/css :small-input)}

View File

@@ -0,0 +1,35 @@
(ns app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens
(:require
[app.common.types.token :as tk]
[app.main.ui.context :as muc]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
(mf/defc numeric-input-wrapper*
[{:keys [value attr applied-token align on-detach placeholder input-type] :rest props}]
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
tokens (mf/with-memo [tokens input-type]
(delay
(-> (deref tokens)
(select-keys (get tk/tokens-by-input (or input-type attr)))
(not-empty))))
on-detach-attr
(mf/use-fn
(mf/deps on-detach attr)
#(on-detach % attr))
props (mf/spread-props props
{:placeholder (or placeholder
(if (= :multiple value)
(tr "settings.multiple")
"--"))
:applied-token applied-token
:tokens (if (delay? tokens) @tokens tokens)
:align align
:on-detach on-detach-attr
:name attr
:value value})]
[:> numeric-input* props]))

View File

@@ -9,7 +9,6 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.token :as tk]
[app.main.data.workspace :as dw]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.tokens.application :as dwta]
@@ -17,10 +16,9 @@
[app.main.store :as st]
[app.main.ui.components.numeric-input :as deprecated-input]
[app.main.ui.components.select :refer [select]]
[app.main.ui.context :as muc]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
[app.render-wasm.api :as wasm.api]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
@@ -62,36 +60,6 @@
(identical? (get old-values :hidden)
(get new-values :hidden)))))
(mf/defc numeric-input-wrapper*
{::mf/private true}
[{:keys [values name applied-tokens align on-detach] :rest props}]
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
tokens (mf/with-memo [tokens name]
(delay
(-> (deref tokens)
(select-keys (get tk/tokens-by-input name))
(not-empty))))
on-detach-attr (mf/use-fn
(mf/deps on-detach name)
#(on-detach % name))
applied-token (get applied-tokens name)
opacity-value (or (get values name) 1)
props (mf/spread-props props
{:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple opacity-value))
(tr "settings.multiple")
"--")
:applied-token applied-token
:tokens (if (delay? tokens) @tokens tokens)
:align align
:on-detach on-detach-attr
:name name
:value (* 100 opacity-value)})]
[:> numeric-input* props]))
(mf/defc layer-menu*
{::mf/wrap [#(mf/memo' % check-layer-menu-props)]}
[{:keys [ids values applied-tokens]}]
@@ -250,22 +218,24 @@
:on-pointer-enter-option handle-blend-mode-enter
:on-pointer-leave-option handle-blend-mode-leave}]]
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-opacity-change
:on-detach on-detach-token
:icon i/percentage
:min 0
:max 100
:name :opacity
:attr :opacity
:property (tr "workspace.options.opacity")
:applied-tokens applied-tokens
:applied-token (get applied-tokens :opacity)
:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple (or (get values name) 1)))
(tr "settings.multiple")
"--")
:align :right
:class (stl/css :numeric-input-wrapper)
:values values}]
:value (* 100
(or (get values name) 1))}]
[:div {:class (stl/css :input)
:title (tr "workspace.options.opacity")}

View File

@@ -11,7 +11,6 @@
[app.common.data.macros :as dm]
[app.common.math :as mth]
[app.common.types.shape.layout :as ctl]
[app.common.types.token :as tk]
[app.config :as cf]
[app.main.data.event :as-alias ev]
[app.main.data.workspace :as udw]
@@ -25,15 +24,14 @@
[app.main.ui.components.numeric-input :as deprecated-input]
[app.main.ui.components.select :refer [select]]
[app.main.ui.components.title-bar :refer [title-bar*]]
[app.main.ui.context :as muc]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.formats :as fmt]
[app.main.ui.hooks :as h]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
@@ -48,44 +46,6 @@
:column i/column
:column-reverse i/column-reverse))
(mf/defc numeric-input-wrapper*
{::mf/private true}
[{:keys [values name applied-tokens align on-detach] :rest props}]
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
input-type (cond
(some #{:p2 :p4} [name])
:horizontal-padding
(some #{:p1 :p3} [name])
:vertical-padding
:else
name)
tokens (mf/with-memo [tokens input-type]
(delay
(-> (deref tokens)
(select-keys (get tk/tokens-by-input input-type))
(not-empty))))
on-detach-attr
(mf/use-fn
(mf/deps on-detach name)
#(on-detach % name))
props (mf/spread-props props
{:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple (get values name))
(nil? (get values name)))
(tr "settings.multiple")
"--")
:class (stl/css :numeric-input-layout)
:applied-token (get applied-tokens name)
:tokens tokens
:align align
:on-detach on-detach-attr
:value (get values name)})]
[:> numeric-input* props]))
;; FLEX COMPONENTS
(def layout-container-flex-attrs
@@ -415,11 +375,17 @@
:on-focus on-focus-p1
:icon i/padding-top-bottom
:min 0
:name :p1
:attr :p1
:input-type :vertical-padding
:property (tr "workspace.layout-grid.editor.padding.vertical")
:nillable true
:applied-tokens {:p1 applied-to-p1}
:values {:p1 p1}}]
:placeholder (if (or (= :multiple (:applied-tokens p1))
(= :multiple p1)
(nil? p1))
(tr "settings.multiple")
"--")
:applied-token applied-to-p1
:value p1}]
[:div {:class (stl/css :padding-simple)
:title (tr "workspace.layout-grid.editor.padding.vertical")}
@@ -444,12 +410,18 @@
:on-focus on-focus-p2
:icon i/padding-left-right
:min 0
:name :p2
:attr :p2
:input-type :horizontal-padding
:align :right
:property (tr "workspace.layout-grid.editor.padding.horizontal")
:nillable true
:applied-tokens {:p2 applied-to-p2}
:values {:p2 p2}}]
:applied-token applied-to-p2
:placeholder (if (or (= :multiple (:applied-tokens p2))
(= :multiple p2)
(nil? p2))
(tr "settings.multiple")
"--")
:value p2}]
[:div {:class (stl/css :padding-simple)
:title (tr "workspace.layout-grid.editor.padding.horizontal")}
@@ -535,10 +507,15 @@
:on-focus on-focus-p1
:icon i/padding-top
:min 0
:name :p1
:attr :p1
:input-type :vertical-padding
:property (tr "workspace.layout-grid.editor.padding.top")
:applied-tokens applied-tokens
:values value}]
:placeholder (if (or (= :multiple (:applied-tokens p1))
(= :multiple p1))
(tr "settings.multiple")
"--")
:applied-token p1
:value p1}]
[:div {:class (stl/css :padding-multiple)
:title (tr "workspace.layout-grid.editor.padding.top")}
@@ -563,11 +540,16 @@
:on-focus on-focus-p2
:icon i/padding-right
:min 0
:name :p2
:attr :p2
:input-type :horizontal-padding
:align :right
:property (tr "workspace.layout-grid.editor.padding.right")
:applied-tokens applied-tokens
:values value}]
:placeholder (if (or (= :multiple (:applied-tokens p2))
(= :multiple p2))
(tr "settings.multiple")
"--")
:applied-token p2
:value p2}]
[:div {:class (stl/css :padding-multiple)
:title (tr "workspace.layout-grid.editor.padding.right")}
@@ -592,10 +574,15 @@
:on-focus on-focus-p3
:icon i/padding-bottom
:min 0
:name :p3
:attr :p3
:input-type :vertical-padding
:property (tr "workspace.layout-grid.editor.padding.bottom")
:applied-tokens applied-tokens
:values value}]
:placeholder (if (or (= :multiple (:applied-tokens p3))
(= :multiple p3))
(tr "settings.multiple")
"--")
:applied-token p3
:value p3}]
[:div {:class (stl/css :padding-multiple)
:title (tr "workspace.layout-grid.editor.padding.bottom")}
@@ -621,10 +608,15 @@
:icon i/padding-left
:min 0
:align :right
:name :p4
:attr :p4
:input-type :horizontal-padding
:property (tr "workspace.layout-grid.editor.padding.left")
:applied-tokens applied-tokens
:values value}]
:placeholder (if (or (= :multiple (:applied-tokens p3))
(= :multiple p3))
(tr "settings.multiple")
"--")
:applied-token p3
:value p3}]
[:div {:class (stl/css :padding-multiple)
:title (tr "workspace.layout-grid.editor.padding.left")}
@@ -757,11 +749,16 @@
:icon i/gap-vertical
:nillable true
:min 0
:name :row-gap
:applied-tokens applied-tokens
:attr :row-gap
:property "Row gap"
:values {:row-gap (:row-gap value)}
:disabled row-gap-disabled?}]
:disabled row-gap-disabled?
:placeholder (if (or (= :multiple (:applied-tokens (:row-gap value)))
(= :multiple (:row-gap value)))
(tr "settings.multiple")
"--")
:applied-token (:row-gap applied-tokens)
:value (:row-gap value)}]
[:div {:class (stl/css-case
:row-gap true
@@ -791,11 +788,15 @@
:icon i/gap-horizontal
:nillable true
:min 0
:name :column-gap
:attr :column-gap
:align :right
:applied-tokens applied-tokens
:property "Column gap"
:values {:column-gap (:column-gap value)}
:placeholder (if (or (= :multiple (:applied-tokens (:column-gap value)))
(= :multiple (:column-gap value)))
(tr "settings.multiple")
"--")
:applied-token (:column-gap applied-tokens)
:value (:column-gap value)
:disabled col-gap-disabled?}]
[:div {:class (stl/css-case

View File

@@ -11,6 +11,8 @@
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace :as udw]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.features :as features]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.numeric-input :as deprecated-input]
@@ -19,6 +21,7 @@
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
@@ -46,147 +49,345 @@
(select-margins (= prop :m1) (= prop :m2) (= prop :m3) (= prop :m4)))
(mf/defc margin-simple*
[{:keys [value on-change on-blur]}]
(let [m1 (:m1 value)
[{:keys [value on-change on-blur applied-tokens ids]}]
(let [token-numeric-inputs
(features/use-feature "tokens/numeric-input")
m1 (:m1 value)
m2 (:m2 value)
m3 (:m3 value)
m4 (:m4 value)
m1-placeholder (if (and (not= value :multiple) (not= m1 m3)) (tr "settings.multiple") "--")
m2-placeholder (if (and (not= value :multiple) (not= m2 m4)) (tr "settings.multiple") "--")
m1 (when (and (not= value :multiple) (= m1 m3)) m1)
m2 (when (and (not= value :multiple) (= m2 m4)) m2)
token-applied-m1 (:m1 applied-tokens)
token-applied-m2 (:m2 applied-tokens)
token-applied-m3 (:m3 applied-tokens)
token-applied-m4 (:m4 applied-tokens)
token-applied-m1 (if (and (not= applied-tokens :multiple) (= token-applied-m1 token-applied-m3)) token-applied-m1
:multiple)
token-applied-m2 (if (and (not= applied-tokens :multiple) (= token-applied-m2 token-applied-m4)) token-applied-m2
:multiple)
m1-placeholder (if (and (not= value :multiple)
(= m1 m3)
(= token-applied-m1 token-applied-m3))
"--"
(tr "settings.multiple"))
m2-placeholder (if (and (not= value :multiple)
(= m2 m4)
(= token-applied-m2 token-applied-m4))
"--"
(tr "settings.multiple"))
on-focus
(mf/use-fn
(fn [event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(case attr
:m1 (select-margins true false true false)
:m2 (select-margins false true false true))
(fn [attr event]
(case attr
:m1 (select-margins true false true false)
:m2 (select-margins false true false true))
(dom/select-target event)))
(dom/select-target event))))
on-detach-token
(mf/use-fn
(mf/deps ids)
(fn [token attr]
(st/emit! (dwta/unapply-token {:token (first token)
:attributes #{attr}
:shape-ids ids}))))
on-detach-horizontal
(mf/use-fn
(mf/deps on-detach-token)
(fn [token]
(prn "token" token)
(run! #(on-detach-token token %) [:m2 :m4])))
on-detach-vertical
(mf/use-fn
(mf/deps on-detach-token)
(fn [token]
(run! #(on-detach-token token %) [:m1 :m3])))
on-change'
(mf/use-fn
(mf/deps on-change)
(fn [value event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(on-change :simple attr value))))]
(mf/deps on-change ids)
(fn [value attr]
(if (or (string? value) (int? value))
(on-change :simple attr value)
(do
(st/emit!
(dwta/toggle-token {:token (first value)
:attrs (if (= :m1 attr)
#{:m1 :m3}
#{:m2 :m4})
:shape-ids ids}))))))
on-focus-m1
(mf/use-fn (mf/deps on-focus) #(on-focus :m1))
on-focus-m2
(mf/use-fn (mf/deps on-focus) #(on-focus :m2))
on-m1-change
(mf/use-fn (mf/deps on-change') #(on-change' % :m1))
on-m2-change
(mf/use-fn (mf/deps on-change') #(on-change' % :m2))]
[:div {:class (stl/css :margin-simple)}
[:div {:class (stl/css :vertical-margin)
:title "Vertical margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-top-bottom]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder m1-placeholder
:data-name "m1"
:on-focus on-focus
:on-change on-change'
:on-blur on-blur
:nillable true
:value m1}]]
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-m1-change
:on-detach on-detach-vertical
:class (stl/css :vertical-margin-wrapper)
:on-blur on-blur
:on-focus on-focus-m1
:placeholder m1-placeholder
:icon i/margin-top-bottom
:min 0
:attr :m1
:input-type :vertical-margin
:property "Vertical margin "
:nillable true
:applied-token token-applied-m1
:value m1}]
[:div {:class (stl/css :horizontal-margin)
:title "Horizontal margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-left-right]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder m2-placeholder
:data-name "m2"
:on-focus on-focus
:on-change on-change'
:on-blur on-blur
:nillable true
:value m2}]]]))
[:div {:class (stl/css :vertical-margin)
:title "Vertical margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-top-bottom]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder m1-placeholder
:data-name "m1"
:on-focus on-focus-m1
:on-change on-m1-change
:on-blur on-blur
:nillable true
:value m1}]])
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-m2-change
:on-detach on-detach-horizontal
:on-blur on-blur
:on-focus on-focus-m2
:placeholder m2-placeholder
:icon i/margin-left-right
:class (stl/css :horizontal-margin-wrapper)
:min 0
:attr :m2
:align :right
:input-type :horizontal-margin
:property "Horizontal margin"
:nillable true
:applied-token token-applied-m2
:value m2}]
[:div {:class (stl/css :horizontal-margin)
:title "Horizontal margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-left-right]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder m2-placeholder
:data-name "m2"
:on-focus on-focus-m2
:on-change on-m2-change
:on-blur on-blur
:nillable true
:value m2}]])]))
(mf/defc margin-multiple*
[{:keys [value on-change on-blur]}]
(let [m1 (:m1 value)
[{:keys [value on-change on-blur applied-tokens ids]}]
(let [token-numeric-inputs
(features/use-feature "tokens/numeric-input")
m1 (:m1 value)
m2 (:m2 value)
m3 (:m3 value)
m4 (:m4 value)
applied-token-to-m1 (:m1 applied-tokens)
applied-token-to-m2 (:m2 applied-tokens)
applied-token-to-m3 (:m3 applied-tokens)
applied-token-to-m4 (:m4 applied-tokens)
on-detach-token
(mf/use-fn
(mf/deps ids)
(fn [token attr]
(st/emit! (dwta/unapply-token {:token (first token)
:attributes #{attr}
:shape-ids ids}))))
on-focus
(mf/use-fn
(fn [event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(select-margin attr)
(dom/select-target event))))
(fn [attr event]
(select-margin attr)
(dom/select-target event)))
on-focus-m1
(mf/use-fn (mf/deps on-focus) #(on-focus :m1))
on-focus-m2
(mf/use-fn (mf/deps on-focus) #(on-focus :m2))
on-focus-m3
(mf/use-fn (mf/deps on-focus) #(on-focus :m1))
on-focus-m4
(mf/use-fn (mf/deps on-focus) #(on-focus :m2))
on-change'
(mf/use-fn
(mf/deps on-change)
(fn [value event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(on-change :multiple attr value))))]
(mf/deps on-change ids)
(fn [value attr]
(if (or (string? value) (int? value))
(on-change :multiple attr value)
(do
(st/emit!
(dwta/toggle-token {:token (first value)
:attrs #{attr}
:shape-ids ids}))))))
on-m1-change
(mf/use-fn (mf/deps on-change') #(on-change' % :m1))
on-m2-change
(mf/use-fn (mf/deps on-change') #(on-change' % :m2))
on-m3-change
(mf/use-fn (mf/deps on-change') #(on-change' % :m3))
on-m4-change
(mf/use-fn (mf/deps on-change') #(on-change' % :m4))]
[:div {:class (stl/css :margin-multiple)}
[:div {:class (stl/css :top-margin)
:title "Top margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-top]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m1"
:on-focus on-focus
:on-change on-change'
:on-blur on-blur
:nillable true
:value m1}]]
[:div {:class (stl/css :right-margin)
:title "Right margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-right]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m2"
:on-focus on-focus
:on-change on-change'
:on-blur on-blur
:nillable true
:value m2}]]
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-m1-change
:on-detach on-detach-token
:on-blur on-blur
:on-focus on-focus-m1
:icon i/margin-top
:class (stl/css :top-margin-wrapper)
:min 0
:attr :m1
:input-type :vertical-margin
:property "Top margin"
:nillable true
:applied-token applied-token-to-m1
:value m1}]
[:div {:class (stl/css :bottom-margin)
:title "Bottom margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-bottom]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m3"
:on-focus on-focus
:on-change on-change'
:on-blur on-blur
:nillable true
:value m3}]]
[:div {:class (stl/css :top-margin)
:title "Top margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-top]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m1"
:on-focus on-focus-m1
:on-change on-m1-change
:on-blur on-blur
:nillable true
:value m1}]])
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-m2-change
:on-detach on-detach-token
:on-blur on-blur
:on-focus on-focus-m2
:icon i/margin-right
:class (stl/css :right-margin-wrapper)
:min 0
:attr :m2
:align :right
:input-type :horizontal-margin
:property "Right margin"
:nillable true
:applied-token applied-token-to-m2
:value m2}]
[:div {:class (stl/css :left-margin)
:title "Left margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-left]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m4"
:on-focus on-focus
:on-change on-change'
:on-blur on-blur
:nillable true
:value m4}]]]))
[:div {:class (stl/css :right-margin)
:title "Right margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-right]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m2"
:on-focus on-focus-m2
:on-change on-m2-change
:on-blur on-blur
:nillable true
:value m2}]])
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-m3-change
:on-detach on-detach-token
:on-blur on-blur
:on-focus on-focus-m3
:icon i/margin-bottom
:class (stl/css :bottom-margin-wrapper)
:min 0
:attr :m3
:align :right
:input-type :vertical-margin
:property "Bottom margin"
:nillable true
:applied-token applied-token-to-m3
:value m3}]
[:div {:class (stl/css :bottom-margin)
:title "Bottom margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-bottom]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m3"
:on-focus on-focus-m3
:on-change on-m3-change
:on-blur on-blur
:nillable true
:value m3}]])
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-m4-change
:on-detach on-detach-token
:on-blur on-blur
:on-focus on-focus-m4
:icon i/margin-left
:class (stl/css :left-margin-wrapper)
:min 0
:attr :m4
:property "Left margin"
:input-type :horizontal-margin
:nillable true
:applied-token applied-token-to-m4
:value m4}]
[:div {:class (stl/css :left-margin)
:title "Left margin"}
[:span {:class (stl/css :icon)}
deprecated-icon/margin-left]
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
:placeholder "--"
:data-name "m4"
:on-focus on-focus-m4
:on-change on-m4-change
:on-blur on-blur
:nillable true
:value m4}]])]))
(mf/defc margin-section*
{::mf/private true
::mf/expect-props #{:value :type :on-type-change :on-change}}
::mf/expect-props #{:value :type :on-type-change :on-change :applied-tokens :ids}}
[{:keys [type on-type-change] :as props}]
(let [type (d/nilv type :simple)
on-blur (mf/use-fn #(select-margins false false false false))
@@ -292,8 +493,191 @@
:label "Align self end"
:value "end"}]}])
(mf/defc layout-size-constraints*
{::mf/private true
::mf/expect-props #{:value :applied-tokens :ids :v-sizing}}
[{:keys [values v-sizing ids applied-tokens] :as props}]
(let [token-numeric-inputs
(features/use-feature "tokens/numeric-input")
min-w (get values :layout-item-min-w)
max-w (get values :layout-item-max-w)
min-h (get values :layout-item-min-h)
max-h (get values :layout-item-max-h)
applied-token-to-min-w (get applied-tokens :layout-item-min-w)
applied-token-to-max-w (get applied-tokens :layout-item-max-w)
applied-token-to-min-h (get applied-tokens :layout-item-min-h)
applied-token-to-max-h (get applied-tokens :layout-item-max-h)
on-detach-token
(mf/use-fn
(mf/deps ids)
(fn [token attr]
(st/emit! (dwta/unapply-token {:token (first token)
:attributes #{attr}
:shape-ids ids}))))
on-size-change
(mf/use-fn
(mf/deps ids)
(fn [value attr]
(if (or (string? value) (int? value))
(st/emit! (dwsl/update-layout-child ids {attr value}))
(do
(st/emit!
(dwta/toggle-token {:token (first value)
:attrs #{attr}
:shape-ids ids}))))))
on-layout-item-min-w-change
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-min-w))
on-layout-item-max-w-change
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-max-w))
on-layout-item-min-h-change
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-min-h))
on-layout-item-max-h-change
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-max-h))]
[:div {:class (stl/css :advanced-options)}
(when (= (:layout-item-h-sizing values) :fill)
[:div {:class (stl/css :horizontal-fill)}
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-layout-item-min-w-change
:on-detach on-detach-token
:class (stl/css :min-w-wrapper)
:min 0
:attr :layout-item-min-w
:property (tr "workspace.options.layout-item.layout-item-min-w")
:text-icon "MIN W"
:input-type :min-width
:nillable true
:applied-token applied-token-to-min-w
:tooltip-class (stl/css :tooltip-wrapper)
:value min-w}]
[:div {:class (stl/css :layout-item-min-w)
:title (tr "workspace.options.layout-item.layout-item-min-w")}
[:span {:class (stl/css :icon-text)} "MIN W"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-min-w"
:on-focus dom/select-target
:on-change on-layout-item-min-w-change
:value (get values :layout-item-min-w)
:nillable true}]])
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-layout-item-max-w-change
:on-detach on-detach-token
:text-icon "MAX W"
:class (stl/css :max-w-wrapper)
:min 0
:input-type :max-width
:attr :layout-item-max-w
:property (tr "workspace.options.layout-item.layout-item-max-w")
:nillable true
:tooltip-class (stl/css :tooltip-wrapper)
:applied-token applied-token-to-max-w
:value max-w}]
[:div {:class (stl/css :layout-item-max-w)
:title (tr "workspace.options.layout-item.layout-item-max-w")}
[:span {:class (stl/css :icon-text)} "MAX W"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-max-w"
:on-focus dom/select-target
:on-change on-layout-item-max-w-change
:value (get values :layout-item-max-w)
:nillable true}]])])
(when (= v-sizing :fill)
[:div {:class (stl/css :vertical-fill)}
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-layout-item-min-h-change
:on-detach on-detach-token
:text-icon "MIN H"
:input-type :max-height
:class (stl/css :min-h-wrapper)
:min 0
:attr :layout-item-min-h
:property (tr "workspace.options.layout-item.layout-item-min-h")
:nillable true
:tooltip-class (stl/css :tooltip-wrapper)
:applied-token applied-token-to-min-h
:value min-h}]
[:div {:class (stl/css :layout-item-min-h)
:title (tr "workspace.options.layout-item.layout-item-min-h")}
[:span {:class (stl/css :icon-text)} "MIN H"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-min-h"
:on-focus dom/select-target
:on-change on-layout-item-min-h-change
:value (get values :layout-item-min-h)
:nillable true}]])
(if token-numeric-inputs
[:> numeric-input-wrapper*
{:on-change on-layout-item-max-h-change
:on-detach on-detach-token
:class (stl/css :max-h-wrapper)
:min 0
:text-icon "MAX H"
:input-type :max-height
:attr :layout-item-max-h
:property (tr "workspace.options.layout-item.layout-item-max-h")
:nillable true
:tooltip-class (stl/css :tooltip-wrapper)
:applied-token applied-token-to-max-h
:value max-h}]
[:div {:class (stl/css :layout-item-max-h)
:title (tr "workspace.options.layout-item.layout-item-max-h")}
[:span {:class (stl/css :icon-text)} "MAX H"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-max-h"
:on-focus dom/select-target
:on-change on-layout-item-max-h-change
:value (get values :layout-item-max-h)
:nillable true}]])])]))
(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/memo #{:ids :values :type :is-layout-child? :is-grid-parent :is-flex-parent? :is-grid-layout? :is-flex-layout? :applied-tokens}
::mf/props :obj}
[{:keys [ids values
^boolean is-layout-child?
@@ -301,7 +685,8 @@
^boolean is-grid-parent?
^boolean is-flex-parent?
^boolean is-flex-layout?
^boolean is-grid-layout?]}]
^boolean is-grid-layout?
applied-tokens]}]
(let [selection-parents* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
selection-parents (mf/deref selection-parents*)
@@ -397,16 +782,7 @@
(fn [value]
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing (keyword value)}))))
;; Size and position
on-size-change
(mf/use-fn
(mf/deps ids)
(fn [value event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "attr")
(keyword))]
(st/emit! (dwsl/update-layout-child ids {attr value})))))
;; Position
on-change-position
(mf/use-fn
(mf/deps ids)
@@ -423,7 +799,8 @@
(fn [value]
(st/emit! (dwsl/update-layout-child ids {:layout-item-z-index value}))))]
[:div {:class (stl/css :element-set)}
[:section {:class (stl/css :element-set)
:aria-label "layout item menu"}
[:div {:class (stl/css :element-title)}
[:> title-bar* {:collapsable has-content?
:collapsed (not open?)
@@ -483,74 +860,13 @@
[:> margin-section* {:value (:layout-item-margin values)
:type (:layout-item-margin-type values)
:on-type-change on-margin-type-change
:applied-tokens applied-tokens
:ids ids
:on-change on-margin-change}])
(when (or (= h-sizing :fill)
(= v-sizing :fill))
[:div {:class (stl/css :advanced-options)}
(when (= (:layout-item-h-sizing values) :fill)
[:div {:class (stl/css :horizontal-fill)}
[:div {:class (stl/css :layout-item-min-w)
:title (tr "workspace.options.layout-item.layout-item-min-w")}
[:span {:class (stl/css :icon-text)} "MIN W"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-min-w"
:on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-min-w)
:nillable true}]]
[:div {:class (stl/css :layout-item-max-w)
:title (tr "workspace.options.layout-item.layout-item-max-w")}
[:span {:class (stl/css :icon-text)} "MAX W"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-max-w"
:on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-max-w)
:nillable true}]]])
(when (= v-sizing :fill)
[:div {:class (stl/css :vertical-fill)}
[:div {:class (stl/css :layout-item-min-h)
:title (tr "workspace.options.layout-item.layout-item-min-h")}
[:span {:class (stl/css :icon-text)} "MIN H"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-min-h"
:on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-min-h)
:nillable true}]]
[:div {:class (stl/css :layout-item-max-h)
:title (tr "workspace.options.layout-item.layout-item-max-h")}
[:span {:class (stl/css :icon-text)} "MAX H"]
[:> deprecated-input/numeric-input*
{:class (stl/css :numeric-input)
:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:data-attr "layout-item-max-h"
:on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-max-h)
:nillable true}]]])])])]))
[:> layout-size-constraints* {:ids ids
:values values
:applied-tokens applied-tokens
:v-sizing v-sizing}])])]))

View File

@@ -6,17 +6,21 @@
@use "refactor/common-refactor.scss" as deprecated;
@use "../../../sidebar/common/sidebar.scss" as sidebar;
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
@use "ds/typography.scss" as *;
@use "ds/_utils.scss" as *;
.element-set {
margin: 0;
}
.title-spacing-layout-element {
margin: 0 0 deprecated.$s-4 0;
margin: 0 0 var(--sp-xs) 0;
}
.title-spacing-empty {
padding-left: deprecated.$s-2;
padding-inline-start: var(--sp-xxs);
}
.flex-element-menu {
@@ -35,8 +39,8 @@
}
.z-index-wrapper {
@include use-typography("body-small");
@extend .input-element;
@include deprecated.bodySmallTypography;
grid-column: 6 / span 3;
}
@@ -55,7 +59,7 @@
}
.position-options {
width: 100%;
inline-size: 100%;
grid-column: 1 / span 5;
}
@@ -75,7 +79,7 @@
.vertical-margin,
.horizontal-margin {
@extend .input-element;
@include deprecated.bodySmallTypography;
@include use-typography("body-small");
}
.vertical-margin {
grid-column: 1;
@@ -85,6 +89,16 @@
}
}
.vertical-margin-wrapper {
grid-column: 1;
--dropdown-width: var(--7-columns-dropdown-width);
}
.horizontal-margin-wrapper {
grid-column: 2;
--dropdown-width: var(--7-columns-dropdown-width);
}
.margin-multiple {
display: grid;
grid-template-columns: subgrid;
@@ -96,25 +110,33 @@
.left-margin,
.right-margin {
@extend .input-element;
@include deprecated.bodySmallTypography;
@include use-typography("body-small");
}
.top-margin {
.top-margin,
.top-margin-wrapper {
--dropdown-width: var(--7-columns-dropdown-width);
grid-column: 1;
grid-row: 1;
}
.bottom-margin {
.bottom-margin,
.bottom-margin-wrapper {
--dropdown-width: var(--7-columns-dropdown-width);
grid-column: 2;
grid-row: 1;
}
.left-margin {
.left-margin,
.left-margin-wrapper {
--dropdown-width: var(--7-columns-dropdown-width);
grid-column: 1;
grid-row: 2;
}
.right-margin {
.right-margin,
.right-margin-wrapper {
--dropdown-width: var(--7-columns-dropdown-width);
grid-column: 2;
grid-row: 2;
}
@@ -140,14 +162,18 @@
.layout-item-max-w,
.layout-item-max-h {
@extend .input-element;
@include deprecated.bodySmallTypography;
@include use-typography("body-small");
.icon-text {
justify-content: flex-start;
width: deprecated.$s-80;
padding-top: deprecated.$s-2;
inline-size: px2rem(80);
padding-block-start: var(--sp-xxs);
}
}
.inputs-wrapper {
grid-column: 1 / span 2;
}
.tooltip-wrapper {
inline-size: 100%;
}

View File

@@ -13,7 +13,6 @@
[app.common.geom.shapes :as gsh]
[app.common.logic.shapes :as cls]
[app.common.types.shape.layout :as ctl]
[app.common.types.token :as tk]
[app.main.constants :refer [size-presets]]
[app.main.data.workspace :as udw]
[app.main.data.workspace.interactions :as dwi]
@@ -26,13 +25,12 @@
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.components.numeric-input :as deprecated-input]
[app.main.ui.context :as muc]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.workspace.sidebar.options.menus.border-radius :refer [border-radius-menu*]]
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[clojure.set :as set]
@@ -91,32 +89,6 @@
shape)]
(select-keys shape measure-attrs)))
(mf/defc numeric-input-wrapper*
{::mf/private true}
[{:keys [values name applied-tokens align on-detach] :rest props}]
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
tokens (mf/with-memo [tokens name]
(delay
(-> (deref tokens)
(select-keys (get tk/tokens-by-input name))
(not-empty))))
on-detach-attr
(mf/use-fn
(mf/deps on-detach name)
#(on-detach % name))
props (mf/spread-props props
{:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple (get values name)))
(tr "settings.multiple") "--")
:class (stl/css :numeric-input-measures)
:applied-token (get applied-tokens name)
:tokens (if (delay? tokens) @tokens tokens)
:align align
:on-detach on-detach-attr
:value (get values name)})]
[:> numeric-input* props]))
(def ^:private xf:map-type (map :type))
(def ^:private xf:mapcat-type-to-options (mapcat type->options))
@@ -444,10 +416,13 @@
:on-detach on-detach-token
:icon i/character-w
:min 0.01
:name :width
:attr :width
:property (tr "workspace.options.width")
:applied-tokens applied-tokens
:values values}]
:applied-token (get applied-tokens :width)
:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple (get values :width)))
(tr "settings.multiple") "--")
:value (get values :width)}]
[:> numeric-input-wrapper*
{:disabled disabled-height-sizing?
@@ -455,11 +430,11 @@
:on-detach on-detach-token
:min 0.01
:icon i/character-h
:name :height
:attr :height
:align :right
:property (tr "workspace.options.height")
:applied-tokens applied-tokens
:values values}]]
:applied-token (get applied-tokens :height)
:value (get values :height)}]]
[:*
[:div {:class (stl/css-case :width true
@@ -503,20 +478,26 @@
:on-change on-pos-x-change
:on-detach on-detach-token
:icon i/character-x
:name :x
:attr :x
:property (tr "workspace.options.x")
:applied-tokens applied-tokens
:values values}]
:applied-token (get applied-tokens :x)
:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple (get values :x)))
(tr "settings.multiple") "--")
:value (get values :x)}]
[:> numeric-input-wrapper*
{:disabled disabled-position?
:on-change on-pos-y-change
:on-detach on-detach-token
:icon i/character-y
:name :y
:attr :y
:align :right
:property (tr "workspace.options.y")
:applied-tokens applied-tokens
:values values}]]
:applied-token (get applied-tokens :y)
:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple (get values :y)))
(tr "settings.multiple") "--")
:value (get values :y)}]]
[:*
[:div {:class (stl/css-case :x-position true
@@ -551,10 +532,13 @@
:icon i/rotation
:min -359
:max 359
:name :rotation
:attr :rotation
:property (tr "workspace.options.rotation")
:applied-tokens applied-tokens
:values values}]
:applied-token (get applied-tokens :rotation)
:placeholder (if (or (= :multiple (:applied-tokens values))
(= :multiple (get values :rotation)))
(tr "settings.multiple") "--")
:value (get values :rotation)}]
[:div {:class (stl/css :rotation)
:title (tr "workspace.options.rotation")}

View File

@@ -9,50 +9,20 @@
(:require
[app.common.data :as d]
[app.common.types.color :as ctc]
[app.common.types.token :as tk]
[app.main.data.workspace.tokens.application :as dwta]
[app.main.features :as features]
[app.main.store :as st]
[app.main.ui.components.numeric-input :as deprecated-input]
[app.main.ui.components.reorder-handler :refer [reorder-handler*]]
[app.main.ui.components.select :refer [select]]
[app.main.ui.context :as muc]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.hooks :as h]
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
(mf/defc numeric-input-wrapper*
{::mf/private true}
[{:keys [values name applied-tokens align on-detach] :rest props}]
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
tokens (mf/with-memo [tokens name]
(delay
(-> (deref tokens)
(select-keys (get tk/tokens-by-input name))
(not-empty))))
on-detach-attr (mf/use-fn
(mf/deps on-detach name)
#(on-detach % name))
applied-token (get applied-tokens name)
props (mf/spread-props props
{:placeholder (if (= :multiple values)
(tr "settings.multiple")
"--")
:applied-token applied-token
:tokens (if (delay? tokens) @tokens tokens)
:align align
:on-detach on-detach-attr
:name name
:value values})]
[:> numeric-input* props]))
(mf/defc stroke-row*
[{:keys [index
stroke
@@ -250,11 +220,11 @@
:min 0
:on-focus on-focus
:on-blur on-blur
:name :stroke-width
:attr :stroke-width
:class (stl/css :numeric-input-wrapper)
:property (tr "workspace.options.stroke-width")
:applied-tokens applied-tokens
:values stroke-width}]
:applied-token (get applied-tokens :stroke-width)
:value stroke-width}]
[:div {:class (stl/css :stroke-width-input)
:title (tr "workspace.options.stroke-width")}

View File

@@ -114,6 +114,7 @@
:is-layout-child? true
:is-flex-parent? is-flex-parent?
:is-grid-parent? is-grid-parent?
:applied-tokens applied-tokens
:shape shape}])
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)

View File

@@ -113,6 +113,7 @@
:is-layout-container? false
:is-flex-parent? is-flex-parent?
:is-grid-parent? is-grid-parent?
:applied-tokens applied-tokens
:shape shape}])
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)

View File

@@ -135,6 +135,7 @@
:is-flex-layout? is-flex-layout?
:is-grid-layout? is-grid-layout?
:is-layout-child? is-layout-child?
:applied-tokens applied-tokens
:is-layout-container? is-layout-container?
:shape shape}])

View File

@@ -139,6 +139,7 @@
:is-layout-container? false
:is-flex-parent? is-flex-parent?
:is-grid-parent? is-grid-parent?
:applied-tokens applied-tokens
:values layout-item-values}])
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)

View File

@@ -409,7 +409,7 @@
[layout-container-ids layout-container-values layout-container-tokens]
(get-attrs shapes objects :layout-container)
[layout-item-ids layout-item-values {}]
[layout-item-ids layout-item-values layout-item-tokens]
(get-attrs shapes objects :layout-item)
components
@@ -471,6 +471,7 @@
:is-layout-container? all-flex-layout-container?
:is-flex-parent? is-flex-parent?
:is-grid-parent? is-grid-parent?
:applied-tokens layout-item-tokens
:values layout-item-values}])
(when-not (or (empty? constraint-ids) ^boolean is-layout-child?)

View File

@@ -113,6 +113,7 @@
:is-layout-container? false
:is-flex-parent? is-flex-parent?
:is-grid-parent? is-grid-parent?
:applied-tokens applied-tokens
:shape shape}])
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)

View File

@@ -112,6 +112,7 @@
:values layout-item-values
:is-layout-child? true
:is-flex-parent? is-flex-parent?
:applied-tokens applied-tokens
:is-grid-parent? is-grid-parent?
:shape shape}])

View File

@@ -180,6 +180,7 @@
:is-layout-child? true
:is-flex-parent? is-flex-parent?
:is-grid-parent? is-grid-parent?
:applied-tokens applied-tokens
:shape shape}])
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)

View File

@@ -154,6 +154,7 @@
:is-layout-child? true
:is-flex-parent? is-flex-parent?
:is-grid-parent? is-grid-parent?
:applied-tokens applied-tokens
:shape shape}])
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)

View File

@@ -6713,19 +6713,19 @@ msgstr "Advanced options"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:543
msgid "workspace.options.layout-item.layout-item-max-h"
msgstr "Max.Height"
msgstr "Max height"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:510
msgid "workspace.options.layout-item.layout-item-max-w"
msgstr "Max.Width"
msgstr "Max width"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:527
msgid "workspace.options.layout-item.layout-item-min-h"
msgstr "Min.Height"
msgstr "Min height"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:494
msgid "workspace.options.layout-item.layout-item-min-w"
msgstr "Min.Width"
msgstr "Min width"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
#, unused

View File

@@ -7164,11 +7164,11 @@ msgstr "Ancho"
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:552
msgid "workspace.options.x"
msgstr "eje X"
msgstr "Eje X"
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:563
msgid "workspace.options.y"
msgstr "eje Y"
msgstr "Eje Y"
#: src/app/main/ui/workspace/viewport/path_actions.cljs:140
msgid "workspace.path.actions.add-node"