Compare commits

..

1 Commits

Author SHA1 Message Date
Eva Marco
7bd0f835ee Replace opacity numeric input 2026-01-14 13:17:57 +01:00
15 changed files with 129 additions and 30 deletions

View File

@@ -487,6 +487,7 @@
:vertical-margin #{:spacing :dimensions}
:sided-margins #{:spacing :dimensions}
:line-height #{:line-height :number}
:opacity #{:opacity}
:font-size #{:font-size}
:letter-spacing #{:letter-spacing}
:fill #{:color}

View File

@@ -216,7 +216,7 @@
is-selected-on-focus nillable
tokens applied-token empty-to-end
on-change on-blur on-focus on-detach
property align ref]
property align ref name]
:rest props}]
(let [;; NOTE: we use mfu/bean here for transparently handle
@@ -662,7 +662,10 @@
label (get token :name)
token-value (or (get token :resolved-value)
(or (mf/ref-val last-value*)
(fmt/format-number value)))]
(fmt/format-number value)))
token-value (if (= name :opacity)
(* 100 token-value)
token-value)]
(mf/spread-props props
{:id id
:label label

View File

@@ -7,6 +7,7 @@
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
@use "ds/typography.scss" as *;
@use "ds/_utils.scss" as *;
.option-list {
--options-dropdown-icon-fg-color: var(--color-foreground-secondary);
@@ -15,32 +16,32 @@
--options-dropdown-border-color: var(--color-background-quaternary);
position: absolute;
top: $sz-36;
width: var(--dropdown-width, 100%);
inset-block-start: $sz-36;
inline-size: var(--dropdown-width, 100%);
transform: translateX(var(--dropdown-translate-distance, 0));
background-color: var(--options-dropdown-bg-color);
border-radius: $br-8;
border: $b-1 solid var(--options-dropdown-border-color);
padding-block: var(--sp-xs);
margin-block-end: 0;
max-height: $sz-400;
max-block-size: $sz-400;
overflow-y: auto;
overflow-x: hidden;
z-index: var(--z-index-dropdown);
}
.left-align {
left: var(--dropdown-offset, 0);
inset-inline-start: var(--dropdown-offset, 0);
}
.right-align {
right: var(--dropdown-offset, 0);
inset-inline-end: var(--dropdown-offset, 0);
}
.option-separator {
border: $b-1 solid var(--options-dropdown-border-color);
margin-top: var(--sp-xs);
margin-bottom: var(--sp-xs);
margin-block-start: var(--sp-xs);
margin-block-end: var(--sp-xs);
}
.group-option,
@@ -51,11 +52,11 @@
gap: var(--sp-xs);
color: var(--color-foreground-secondary);
padding-inline: var(--sp-s);
height: var(--sp-xxxl);
block-size: var(--sp-xxxl);
}
.option-empty {
justify-content: center;
text-align: center;
padding: 0 40px;
padding: 0 px2rem(40);
}

View File

@@ -55,7 +55,6 @@
(-> (deref tokens)
(select-keys (get tk/tokens-by-input name))
(not-empty))))
on-detach-attr
(mf/use-fn
(mf/deps on-detach name)

View File

@@ -50,4 +50,4 @@
.dropdown-offset {
--dropdown-offset: #{px2rem(-65)};
}
}

View File

@@ -9,13 +9,17 @@
(: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]
[app.main.features :as features]
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input*]]
[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.render-wasm.api :as wasm.api]
[app.util.i18n :as i18n :refer [tr]]
@@ -39,11 +43,16 @@
(defn- check-layer-menu-props
[old-props new-props]
(let [old-values (unchecked-get old-props "values")
new-values (unchecked-get new-props "values")]
new-values (unchecked-get new-props "values")
old-applied-tokens (unchecked-get old-props "appliedTokens")
new-applied-tokens (unchecked-get new-props "appliedTokens")]
(and (identical? (unchecked-get old-props "class")
(unchecked-get new-props "class"))
(identical? (unchecked-get old-props "ids")
(unchecked-get new-props "ids"))
(identical? old-applied-tokens
new-applied-tokens)
(identical? (get old-values :opacity)
(get new-values :opacity))
(identical? (get old-values :blend-mode)
@@ -53,12 +62,54 @@
(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]}]
(let [hidden? (get values :hidden)
[{:keys [ids values applied-tokens]}]
(let [token-numeric-inputs
(features/use-feature "tokens/numeric-input")
hidden? (get values :hidden)
blocked? (get values :blocked)
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}))))
current-blend-mode (or (get values :blend-mode) :normal)
current-opacity (opacity->string (:opacity values))
@@ -118,6 +169,17 @@
(let [value (/ value 100)]
(on-change ids :opacity value))))
on-opacity-change
(mf/use-fn
(mf/deps on-change handle-opacity-change)
(fn [value]
(if (or (string? value) (int? value))
(handle-opacity-change value)
(do
(st/emit! (dwta/toggle-token {:token (first value)
:attrs #{:opacity}
:shape-ids ids}))))))
handle-set-hidden
(mf/use-fn
(mf/deps ids)
@@ -187,16 +249,34 @@
:class (stl/css-case :hidden-select hidden?)
:on-pointer-enter-option handle-blend-mode-enter
:on-pointer-leave-option handle-blend-mode-leave}]]
[:div {:class (stl/css :input)
:title (tr "workspace.options.opacity")}
[:span {:class (stl/css :icon)} "%"]
[:> numeric-input*
{:value current-opacity
:placeholder "--"
:on-change handle-opacity-change
:min 0
:max 100
:className (stl/css :numeric-input)}]]
(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
:property (tr "workspace.options.opacity")
:applied-tokens applied-tokens
:align :right
:class (stl/css :numeric-input-wrapper)
:values values}]
[:div {:class (stl/css :input)
:title (tr "workspace.options.opacity")}
[:span {:class (stl/css :icon)} "%"]
[:> deprecated-input/numeric-input*
{:value current-opacity
:placeholder "--"
:on-change handle-opacity-change
:min 0
:max 100
:className (stl/css :numeric-input)}]])
[:div {:class (stl/css :actions)}

View File

@@ -6,6 +6,7 @@
@use "refactor/common-refactor.scss" as deprecated;
@use "../../../sidebar/common/sidebar.scss" as sidebar;
@use "ds/_utils.scss" as *;
.element-set-content {
@include sidebar.option-grid-structure;
@@ -43,3 +44,9 @@
}
}
}
.numeric-input-wrapper {
grid-column: span 2;
--dropdown-width: var(--7-columns-dropdown-width);
--dropdown-offset: #{px2rem(-35)};
}

View File

@@ -85,6 +85,7 @@
[:*
[:> layer-menu* {:ids ids
:type type
:applied-tokens applied-tokens
:values layer-values}]
[:> measures-menu* {:ids ids

View File

@@ -84,6 +84,7 @@
[:*
[:> layer-menu* {:ids ids
:type type
:applied-tokens applied-tokens
:values layer-values}]
[:> measures-menu* {:ids ids

View File

@@ -100,6 +100,7 @@
[:*
[:> layer-menu* {:ids ids
:type shape-type
:applied-tokens applied-tokens
:values layer-values}]
[:> measures-menu* {:ids ids
:applied-tokens applied-tokens

View File

@@ -111,6 +111,7 @@
[:div {:class (stl/css :options)}
[:> layer-menu* {:type type
:ids layer-ids
:applied-tokens applied-tokens
:values layer-values}]
[:> measures-menu* {:type type
:ids measure-ids

View File

@@ -382,7 +382,7 @@
objects
objects)))
[layer-ids layer-values]
[layer-ids layer-values layer-tokens]
(get-attrs shapes objects :layer)
[text-ids text-values]
@@ -406,7 +406,7 @@
[exports-ids exports-values]
(get-attrs shapes objects :exports)
[layout-container-ids layout-container-values layout-contianer-tokens]
[layout-container-ids layout-container-values layout-container-tokens]
(get-attrs shapes objects :layout-container)
[layout-item-ids layout-item-values {}]
@@ -442,6 +442,7 @@
(when-not (empty? layer-ids)
[:> layer-menu* {:type type
:ids layer-ids
:applied-tokens layer-tokens
:values layer-values}])
(when-not (empty? measure-ids)
@@ -459,7 +460,7 @@
{:type type
:ids layout-container-ids
:values layout-container-values
:applied-tokens layout-contianer-tokens
:applied-tokens layout-container-tokens
:multiple true}]
(when (or is-layout-child? has-flex-layout-container?)

View File

@@ -84,6 +84,7 @@
[:*
[:> layer-menu* {:ids ids
:applied-tokens applied-tokens
:type type
:values layer-values}]
[:> measures-menu* {:ids ids

View File

@@ -85,6 +85,7 @@
[:*
[:> layer-menu* {:ids ids
:type type
:applied-tokens applied-tokens
:values layer-values}]
[:> measures-menu* {:ids ids
:type type

View File

@@ -125,6 +125,7 @@
[:*
[:> layer-menu* {:ids ids
:type type
:applied-tokens applied-tokens
:values layer-values}]
[:> measures-menu*
{:ids ids