🐛 Fix broken attribute on numeric input (#8250)

* 🐛 Fix broken attribute on numeric input

* 🐛 Fix tooltip position
This commit is contained in:
Eva Marco 2026-02-06 11:32:16 +01:00 committed by GitHub
parent 8acd031ab2
commit a5e36dbb3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 120 additions and 123 deletions

View File

@ -16,6 +16,7 @@
(def ^:private schema:icon-button (def ^:private schema:icon-button
[:map [:map
[:class {:optional true} :string] [:class {:optional true} :string]
[:tooltip-class {:optional true} [:maybe :string]]
[:icon-class {:optional true} :string] [:icon-class {:optional true} :string]
[:icon [:icon
[:and :string [:fn #(contains? icon-list %)]]] [:and :string [:fn #(contains? icon-list %)]]]
@ -28,7 +29,7 @@
(mf/defc icon-button* (mf/defc icon-button*
{::mf/schema schema:icon-button {::mf/schema schema:icon-button
::mf/memo true} ::mf/memo true}
[{:keys [class icon icon-class variant aria-label children tooltip-placement] :rest props}] [{:keys [class icon icon-class variant aria-label children tooltip-placement tooltip-class] :rest props}]
(let [variant (let [variant
(d/nilv variant "primary") (d/nilv variant "primary")
@ -49,6 +50,7 @@
:aria-labelledby tooltip-id})] :aria-labelledby tooltip-id})]
[:> tooltip* {:content aria-label [:> tooltip* {:content aria-label
:class tooltip-class
:placement tooltip-placement :placement tooltip-placement
:id tooltip-id} :id tooltip-id}
[:> :button props [:> :button props

View File

@ -18,7 +18,6 @@
[app.main.ui.ds.controls.utilities.input-field :refer [input-field*]] [app.main.ui.ds.controls.utilities.input-field :refer [input-field*]]
[app.main.ui.ds.controls.utilities.token-field :refer [token-field*]] [app.main.ui.ds.controls.utilities.token-field :refer [token-field*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list] :as i] [app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list] :as i]
[app.main.ui.ds.tooltip :refer [tooltip*]]
[app.main.ui.formats :as fmt] [app.main.ui.formats :as fmt]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
@ -638,27 +637,17 @@
:on-change store-raw-value :on-change store-raw-value
:variant "comfortable" :variant "comfortable"
:disabled disabled :disabled disabled
:slot-start (when (or icon text-icon) :icon icon
:aria-label property
:slot-start (when text-icon
(mf/html (mf/html
[:> tooltip* [:div {:class (stl/css :text-icon)}
{:content property text-icon]))
: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 :slot-end (when-not disabled
(when (some? tokens) (when (some? tokens)
(mf/html [:> icon-button* {:variant "ghost" (mf/html [:> icon-button* {:variant "ghost"
:icon i/tokens :icon i/tokens
:tooltip-class (stl/css :button-tooltip)
:class (stl/css :invisible-button) :class (stl/css :invisible-button)
:aria-label (tr "ds.inputs.numeric-input.open-token-list-dropdown") :aria-label (tr "ds.inputs.numeric-input.open-token-list-dropdown")
:ref open-dropdown-ref :ref open-dropdown-ref
@ -686,23 +675,19 @@
:disabled disabled :disabled disabled
:on-blur on-blur :on-blur on-blur
:class inner-class :class inner-class
:property property
:slot-start (when (or icon text-icon) :slot-start (when (or icon text-icon)
(mf/html (mf/html
[:> tooltip* (cond
{:content property icon
:id property} [:> icon*
(cond {:icon-id icon
icon :size "s"
[:> icon* :class (stl/css :icon)}]
{:icon-id icon
:size "s"
:aria-labelledby property
:class (stl/css :icon)}]
text-icon text-icon
[:div {:class (stl/css :text-icon) [:div {:class (stl/css :text-icon)}
:aria-labelledby property} text-icon])))
text-icon])]))
:token-wrapper-ref token-wrapper-ref :token-wrapper-ref token-wrapper-ref
:token-detach-btn-ref token-detach-btn-ref :token-detach-btn-ref token-detach-btn-ref
:detach-token detach-token})))] :detach-token detach-token})))]
@ -737,40 +722,21 @@
(mf/with-effect [dropdown-options] (mf/with-effect [dropdown-options]
(mf/set-ref-val! options-ref dropdown-options)) (mf/set-ref-val! options-ref dropdown-options))
(if (some? icon) [:div {:class [class (stl/css :input-wrapper)]
[:div {:class [class (stl/css :input-wrapper)] :ref wrapper-ref}
:ref wrapper-ref}
(if (and (some? token-applied) (if (and (some? token-applied)
(not= :multiple token-applied)) (not= :multiple token-applied))
[:> token-field* token-props] [:> token-field* token-props]
[:> input-field* input-props]) [:> input-field* input-props])
(when ^boolean is-open (when ^boolean is-open
(let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)] (let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)]
[:> options-dropdown* {:on-click on-option-click [:> options-dropdown* {:on-click on-option-click
:id listbox-id :id listbox-id
:options options :options options
:selected selected-id :selected selected-id
:focused focused-id :focused focused-id
:align align :align align
:empty-to-end empty-to-end :empty-to-end empty-to-end
:ref set-option-ref}]))] :ref set-option-ref}]))]))
[:div {:class [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])
(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

@ -55,3 +55,8 @@
--opacity-button: 1; --opacity-button: 1;
} }
} }
.button-tooltip {
inline-size: var($sz-28);
block-size: 100%;
}

View File

@ -42,7 +42,6 @@
type (d/nilv type "text") type (d/nilv type "text")
variant (d/nilv variant "dense") variant (d/nilv variant "dense")
tooltip-id (mf/use-id) tooltip-id (mf/use-id)
props (mf/spread-props props props (mf/spread-props props
{:class [class {:class [class
(stl/css-case (stl/css-case
@ -54,15 +53,11 @@
"true") "true")
:aria-describedby (when has-hint :aria-describedby (when has-hint
(str id "-hint")) (str id "-hint"))
:aria-labelledby tooltip-id
:type (d/nilv type "text") :type (d/nilv type "text")
:id id :id id
:max-length (d/nilv max-length max-input-length)}) :max-length (d/nilv max-length max-input-length)})
props (if (and aria-label (not (some? icon)))
(mf/spread-props props
{:aria-label aria-label})
(mf/spread-props props
{:aria-labelledby tooltip-id}))
inside-class (stl/css-case :input-wrapper true inside-class (stl/css-case :input-wrapper true
:has-hint has-hint :has-hint has-hint
:hint-type-hint (= hint-type "hint") :hint-type-hint (= hint-type "hint")
@ -83,11 +78,14 @@
(when (some? slot-start) (when (some? slot-start)
slot-start) slot-start)
(when (some? icon) (when (some? icon)
(if aria-label [:> icon* {:icon-id icon
[:> tooltip* {:content aria-label :class (stl/css :icon)
:id tooltip-id} :size "s"
[:> icon* {:icon-id icon :class (stl/css :icon) :on-click on-icon-click}]] :on-click on-icon-click}])
[:> icon* {:icon-id icon :class (stl/css :icon) :on-click on-icon-click}])) (if aria-label
[:> "input" props] [:> tooltip* {:content aria-label
:id tooltip-id}
[:> "input" props]]
[:> "input" props])
(when (some? slot-end) (when (some? slot-end)
slot-end)])) slot-end)]))

View File

@ -118,4 +118,5 @@
.icon { .icon {
color: var(--color-foreground-secondary); color: var(--color-foreground-secondary);
min-inline-size: var(--sp-l);
} }

View File

@ -22,6 +22,7 @@
[:class {:optional true} [:maybe :string]] [:class {:optional true} [:maybe :string]]
[:id {:optional true} [:maybe :string]] [:id {:optional true} [:maybe :string]]
[:label {:optional true} [:maybe :string]] [:label {:optional true} [:maybe :string]]
[:property {:optional true} [:maybe :string]]
[:value :any] [:value :any]
[:disabled {:optional true} :boolean] [:disabled {:optional true} :boolean]
[:slot-start {:optional true} [:maybe some?]] [:slot-start {:optional true} [:maybe some?]]
@ -35,7 +36,7 @@
{::mf/schema schema:token-field} {::mf/schema schema:token-field}
[{:keys [id label value slot-start disabled class [{:keys [id label value slot-start disabled class
on-click on-token-key-down on-blur detach-token on-click on-token-key-down on-blur detach-token
token-wrapper-ref token-detach-btn-ref on-focus]}] token-wrapper-ref token-detach-btn-ref on-focus property]}]
(let [set-active? (some? id) (let [set-active? (some? id)
content (if set-active? content (if set-active?
label label
@ -50,37 +51,42 @@
(when-not ^boolean disabled (when-not ^boolean disabled
(dom/prevent-default event) (dom/prevent-default event)
(dom/focus! (mf/ref-val token-wrapper-ref)))))] (dom/focus! (mf/ref-val token-wrapper-ref)))))]
[:> tooltip* {:content property
:class (stl/css :token-field-wrapper)
:id (dm/str default-id "-input")}
[:div {:class [class (stl/css-case :token-field true
:with-icon (some? slot-start)
:token-field-disabled disabled)]
:on-click focus-wrapper
:disabled disabled
:on-key-down on-token-key-down
:ref token-wrapper-ref
:on-blur on-blur
:on-focus on-focus
:aria-labelledby (dm/str default-id "-input")
:tab-index (if disabled -1 0)}
[:div {:class [class (stl/css-case :token-field true (when (some? slot-start) slot-start)
:with-icon (some? slot-start)
:token-field-disabled disabled)]
:on-click focus-wrapper
:disabled disabled
:on-key-down on-token-key-down
:ref token-wrapper-ref
:on-blur on-blur
:on-focus on-focus
:tab-index (if disabled -1 0)}
(when (some? slot-start) slot-start) [:div {:class (stl/css :content-wrapper)}
[:> tooltip* {:content content
:id (dm/str id "-pill")}
[:button {:on-click on-click
:class (stl/css-case :pill true
:no-set-pill (not set-active?)
:pill-disabled disabled)
:disabled disabled
:aria-labelledby (dm/str id "-pill")
:on-key-down on-token-key-down}
value
(when-not set-active?
[:div {:class (stl/css :pill-dot)}])]]]
[:> tooltip* {:content content (when-not ^boolean disabled
:id (dm/str id "-pill")} [:> icon-button* {:variant "ghost"
[:button {:on-click on-click :class (stl/css :invisible-button)
:class (stl/css-case :pill true :tooltip-class (stl/css :button-tooltip)
:no-set-pill (not set-active?) :icon i/broken-link
:pill-disabled disabled) :ref token-detach-btn-ref
:disabled disabled :aria-label (tr "ds.inputs.token-field.detach-token")
:aria-labelledby (dm/str id "-pill") :on-click detach-token}])]]))
:on-key-down on-token-key-down}
value
(when-not set-active?
[:div {:class (stl/css :pill-dot)}])]]
(when-not ^boolean disabled
[:> icon-button* {:variant "ghost"
:class (stl/css :invisible-button)
:icon i/broken-link
:ref token-detach-btn-ref
:aria-label (tr "ds.inputs.token-field.detach-token")
:on-click detach-token}])]))

View File

@ -9,6 +9,7 @@
@use "ds/typography.scss" as t; @use "ds/typography.scss" as t;
@use "ds/colors.scss" as *; @use "ds/colors.scss" as *;
@use "ds/mixins.scss" as *; @use "ds/mixins.scss" as *;
@use "ds/_utils.scss" as *;
.token-field { .token-field {
--token-field-bg-color: var(--color-background-tertiary); --token-field-bg-color: var(--color-background-tertiary);
@ -37,6 +38,9 @@
--token-field-outline-color: var(--color-accent-primary); --token-field-outline-color: var(--color-accent-primary);
} }
} }
.token-field-wrapper {
inline-size: 100%;
}
.with-icon { .with-icon {
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
@ -132,3 +136,12 @@
--opacity-button: 1; --opacity-button: 1;
} }
} }
.content-wrapper {
inline-size: 100%;
}
.button-tooltip {
inline-size: px2rem(28);
block-size: 100%;
}

View File

@ -171,7 +171,7 @@
(def ^:private schema:tooltip (def ^:private schema:tooltip
[:map [:map
[:class {:optional true} :string] [:class {:optional true} [:maybe :string]]
[:id {:optional true} :string] [:id {:optional true} :string]
[:offset {:optional true} :int] [:offset {:optional true} :int]
[:delay {:optional true} :int] [:delay {:optional true} :int]
@ -184,6 +184,7 @@
[{:keys [class id children content placement offset delay] :rest props}] [{:keys [class id children content placement offset delay] :rest props}]
(let [internal-id (let [internal-id
(mf/use-id) (mf/use-id)
trigger-ref (mf/use-ref nil)
id id
(d/nilv id internal-id) (d/nilv id internal-id)
@ -204,19 +205,23 @@
(mf/use-fn (mf/use-fn
(mf/deps id placement offset) (mf/deps id placement offset)
(fn [event] (fn [event]
(clear-schedule schedule-ref)
(when-let [tooltip (dom/get-element id)]
(let [origin-brect
(->> (dom/get-target event)
(dom/get-bounding-rect))
update-position (let [current (dom/get-current-target event)
(fn [] related (dom/get-related-target event)
(let [new-placement (update-tooltip-position tooltip placement origin-brect offset)] is-node? (fn [node] (and node (.-nodeType node)))]
(when (not= new-placement placement) (when-not (and related (is-node? related) (.contains current related))
(reset! placement* new-placement))))] (clear-schedule schedule-ref)
(when-let [tooltip (dom/get-element id)]
(let [origin-brect
(dom/get-bounding-rect (mf/ref-val trigger-ref))
(add-schedule schedule-ref delay update-position))))) update-position
(fn []
(let [new-placement (update-tooltip-position tooltip placement origin-brect offset)]
(when (not= new-placement placement)
(reset! placement* new-placement))))]
(add-schedule schedule-ref delay update-position)))))))
on-hide on-hide
(mf/use-fn (mf/use-fn
@ -252,6 +257,7 @@
:on-focus on-show :on-focus on-show
:on-blur on-hide :on-blur on-hide
:on-key-down handle-key-down :on-key-down handle-key-down
:ref trigger-ref
:class [class (stl/css :tooltip-trigger)] :class [class (stl/css :tooltip-trigger)]
:aria-describedby id}) :aria-describedby id})
content content