@adea-ai/ui 0.111.0 → 0.112.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/composites/action-button/action-button.d.ts +8 -0
- package/dist/components/composites/action-button/action-button.d.ts.map +1 -1
- package/dist/components/composites/action-button/action-button.js +9 -3
- package/dist/components/composites/action-button/action-button.js.map +1 -1
- package/dist/r/src/components/composites/action-button/action-button.tsx +10 -1
- package/package.json +1 -1
- package/src/components/composites/action-button/action-button.tsx +10 -1
|
@@ -19,6 +19,14 @@ export type ActionButtonProps<T extends ValidComponent = 'button'> = ButtonProps
|
|
|
19
19
|
tooltip?: string;
|
|
20
20
|
/** Placement of the explanation; icon actions live in bars, so it defaults below the control. */
|
|
21
21
|
tooltipSide?: 'top' | 'right' | 'bottom' | 'left';
|
|
22
|
+
/**
|
|
23
|
+
* A leading icon repeated inside the explanation, forwarded to the shared
|
|
24
|
+
* tooltip's `icon` slot so a bar of icon actions gets the icon+label tip
|
|
25
|
+
* without hand-composing Button and Tooltip. Decorative by contract: the tip
|
|
26
|
+
* is never the accessible name, so the icon must not carry meaning the label
|
|
27
|
+
* lacks. Size it by leaving the svg bare; the slot fixes it to control size.
|
|
28
|
+
*/
|
|
29
|
+
tooltipIcon?: JSX.Element;
|
|
22
30
|
/** Disables the action and announces the busy label through a polite status region. */
|
|
23
31
|
busy?: boolean;
|
|
24
32
|
/** The activity announced while busy. The Button's accessible name stays unchanged. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-button.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/action-button/action-button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,GAAG,EACR,KAAK,cAAc,EACpB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAI1D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IACpF,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iGAAiG;IACjG,WAAW,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACjD,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA+CD,wBAAgB,YAAY,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"action-button.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/action-button/action-button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,GAAG,EACR,KAAK,cAAc,EACpB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAI1D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IACpF,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iGAAiG;IACjG,WAAW,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACjD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACzB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA+CD,wBAAgB,YAAY,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,eA0G5F"}
|
|
@@ -47,6 +47,7 @@ function ActionButton(props) {
|
|
|
47
47
|
"aria-busy",
|
|
48
48
|
"tooltip",
|
|
49
49
|
"tooltipSide",
|
|
50
|
+
"tooltipIcon",
|
|
50
51
|
"busy",
|
|
51
52
|
"busyLabel"
|
|
52
53
|
]);
|
|
@@ -129,9 +130,14 @@ function ActionButton(props) {
|
|
|
129
130
|
get children() {
|
|
130
131
|
return contents();
|
|
131
132
|
}
|
|
132
|
-
})), createComponent(TooltipContent, {
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
})), createComponent(TooltipContent, {
|
|
134
|
+
get icon() {
|
|
135
|
+
return local.tooltipIcon;
|
|
136
|
+
},
|
|
137
|
+
get children() {
|
|
138
|
+
return local.tooltip;
|
|
139
|
+
}
|
|
140
|
+
})];
|
|
135
141
|
}
|
|
136
142
|
});
|
|
137
143
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-button.js","names":["Show","createSignal","splitProps","ComponentProps","JSX","ValidComponent","Button","ButtonProps","Spinner","Tooltip","TooltipContent","TooltipTrigger","ActionButtonProps","T","tooltip","tooltipSide","busy","busyLabel","invokeHandler","handler","event","Event","Array","isArray","callback","data","chainedHandler","before","guardedClick","disabled","onActivate","MouseEvent","preventDefault","stopImmediatePropagation","ButtonTargetProps","Omit","actionAs","ButtonTarget","props","local","rest","PolymorphicButton","targetProps","Element","_$createComponent","_$mergeProps","as","ActionButton","busyStatus","tooltipOpen","setTooltipOpen","tooltipSuppression","setTooltipSuppression","onClickCapture","buttonProps","PolymorphicButtonTarget","variant","size","touchTarget","class","filter","Boolean","join","tabIndex","undefined","role","href","onClick","detail","onPointerLeave","onBlur","Record","contents","when","children","label","_$memo","fallback","placement","open","onOpenChange","_el$","_tmpl$","_$insert","_$createComponent","PolymorphicButton","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/action-button/action-button.tsx"],"sourcesContent":["import {\n Show,\n createSignal,\n splitProps,\n type ComponentProps,\n type JSX,\n type ValidComponent,\n} from 'solid-js'\nimport { Button, type ButtonProps } from '../../ui/button'\nimport { Spinner } from '../../ui/spinner'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'\n\n/**\n * ActionButton.\n *\n * A Button with optional explanatory tooltip and busy feedback. Use it for a\n * simple action that needs these states; actions with product-specific rules\n * should compose Button directly.\n *\n * The tooltip trigger renders the polymorphic Button itself. A disabled native\n * control cannot receive pointer or keyboard events. When a tooltip is set,\n * disabled actions therefore use focusable `aria-disabled` semantics and guard\n * activation; without a tooltip, native buttons retain native disabling.\n * Custom polymorphic components must forward aria, event, and href props for\n * these guarantees to apply.\n */\nexport type ActionButtonProps<T extends ValidComponent = 'button'> = ButtonProps<T> & {\n /** A short explanation, also shown on keyboard focus. The Button still needs its own name. */\n tooltip?: string\n /** Placement of the explanation; icon actions live in bars, so it defaults below the control. */\n tooltipSide?: 'top' | 'right' | 'bottom' | 'left'\n /** Disables the action and announces the busy label through a polite status region. */\n busy?: boolean\n /** The activity announced while busy. The Button's accessible name stays unchanged. */\n busyLabel?: string\n}\n\nfunction invokeHandler(handler: unknown, event: Event) {\n if (Array.isArray(handler)) {\n const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]\n callback(data, event)\n } else if (typeof handler === 'function') {\n const callback = handler as (event: Event) => void\n callback(event)\n }\n}\n\nfunction chainedHandler(handler: unknown, before: (event: Event) => void) {\n return (event: Event) => {\n before(event)\n invokeHandler(handler, event)\n }\n}\n\nfunction guardedClick(\n handler: unknown,\n disabled: () => boolean,\n onActivate: (event: MouseEvent) => void = () => {}\n) {\n return (event: MouseEvent) => {\n if (disabled()) {\n event.preventDefault()\n event.stopImmediatePropagation()\n return\n }\n\n onActivate(event)\n invokeHandler(handler, event)\n }\n}\n\ntype ButtonTargetProps<T extends ValidComponent = 'button'> = Omit<ButtonProps<T>, 'as'> & {\n actionAs?: T\n}\n\n/** Adapter keeps TooltipTrigger's `as` separate from Button's polymorphic `as`. */\nfunction ButtonTarget<T extends ValidComponent = 'button'>(props: ButtonTargetProps<T>) {\n const [local, rest] = splitProps(props as ButtonTargetProps, ['actionAs'])\n const PolymorphicButton = Button as (targetProps: ButtonProps<T>) => JSX.Element\n return <PolymorphicButton as={(local.actionAs ?? 'button') as T} {...(rest as ButtonProps<T>)} />\n}\n\nexport function ActionButton<T extends ValidComponent = 'button'>(props: ActionButtonProps<T>) {\n const [local, rest] = splitProps(props as ActionButtonProps, [\n 'as',\n 'variant',\n 'size',\n 'touchTarget',\n 'class',\n 'children',\n 'disabled',\n 'aria-busy',\n 'tooltip',\n 'tooltipSide',\n 'busy',\n 'busyLabel',\n ])\n const busy = () => local.busy ?? false\n const disabled = () => busy() || !!local.disabled\n const busyStatus = () => local.busyLabel ?? 'Working'\n const [tooltipOpen, setTooltipOpen] = createSignal(false)\n const [tooltipSuppression, setTooltipSuppression] = createSignal<'pointer' | 'focus' | null>(null)\n const onClickCapture = (rest as { onClickCapture?: unknown }).onClickCapture\n const PolymorphicButton = Button as (buttonProps: ButtonProps<T>) => JSX.Element\n const PolymorphicButtonTarget = ButtonTarget as (targetProps: ButtonTargetProps<T>) => JSX.Element\n\n const buttonProps = () => ({\n ...rest,\n variant: local.variant,\n size: local.size,\n touchTarget: local.touchTarget,\n class: [local.class, local.tooltip && disabled() && 'aria-disabled:pointer-events-auto']\n .filter(Boolean)\n .join(' '),\n disabled: disabled() && !local.tooltip,\n 'aria-disabled': disabled() ? true : rest['aria-disabled'],\n // Make tooltip-bearing native controls explicit tab stops in WebKit configurations that\n // otherwise skip implicit button stops. A caller's tabIndex remains authoritative.\n tabIndex:\n rest.tabIndex ??\n (local.as === 'a' && disabled() ? (local.tooltip ? 0 : -1) : local.tooltip ? 0 : undefined),\n role: local.as === 'a' && disabled() ? 'link' : rest.role,\n href: local.as === 'a' && disabled() ? undefined : (rest as { href?: string }).href,\n 'aria-busy': busy() || local['aria-busy'],\n onClick: guardedClick(rest.onClick, disabled, (event) => {\n if (!local.tooltip) return\n setTooltipSuppression(event.detail > 0 ? 'pointer' : 'focus')\n setTooltipOpen(false)\n }),\n onPointerLeave: chainedHandler(rest.onPointerLeave, () => {\n if (tooltipSuppression() === 'pointer') setTooltipSuppression(null)\n }),\n onBlur: chainedHandler(rest.onBlur, () => {\n if (tooltipSuppression() === 'focus') setTooltipSuppression(null)\n }),\n onClickCapture: onClickCapture ? guardedClick(onClickCapture, disabled) : undefined,\n 'on:click': guardedClick((rest as Record<string, unknown>)['on:click'], disabled),\n 'oncapture:click': guardedClick((rest as Record<string, unknown>)['oncapture:click'], disabled),\n })\n\n const contents = () => (\n <>\n <Show when={busy()}>\n <Spinner size=\"sm\" label={false} aria-hidden=\"true\" data-icon=\"inline-start\" />\n </Show>\n {local.children}\n </>\n )\n\n return (\n <>\n <Show\n when={local.tooltip}\n fallback={\n <PolymorphicButton\n as={(local.as ?? 'button') as T}\n {...(buttonProps() as unknown as ButtonProps<T>)}\n >\n {contents()}\n </PolymorphicButton>\n }\n >\n <Tooltip\n placement={local.tooltipSide ?? 'bottom'}\n open={tooltipOpen()}\n onOpenChange={(open) => {\n if (open && tooltipSuppression()) return\n setTooltipOpen(open)\n }}\n >\n <TooltipTrigger\n as={PolymorphicButtonTarget}\n {...(buttonProps() as unknown as ComponentProps<typeof PolymorphicButtonTarget>)}\n actionAs={(local.as ?? 'button') as T}\n >\n {contents()}\n </TooltipTrigger>\n <TooltipContent>{local.tooltip}</TooltipContent>\n </Tooltip>\n </Show>\n <Show when={busy()}>\n <span role=\"status\" aria-live=\"polite\" class=\"visually-hidden\">\n {busyStatus()}\n </span>\n </Show>\n </>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"action-button.js","names":["Show","createSignal","splitProps","ComponentProps","JSX","ValidComponent","Button","ButtonProps","Spinner","Tooltip","TooltipContent","TooltipTrigger","ActionButtonProps","T","tooltip","tooltipSide","tooltipIcon","Element","busy","busyLabel","invokeHandler","handler","event","Event","Array","isArray","callback","data","chainedHandler","before","guardedClick","disabled","onActivate","MouseEvent","preventDefault","stopImmediatePropagation","ButtonTargetProps","Omit","actionAs","ButtonTarget","props","local","rest","PolymorphicButton","targetProps","_$createComponent","_$mergeProps","as","ActionButton","busyStatus","tooltipOpen","setTooltipOpen","tooltipSuppression","setTooltipSuppression","onClickCapture","buttonProps","PolymorphicButtonTarget","variant","size","touchTarget","class","filter","Boolean","join","tabIndex","undefined","role","href","onClick","detail","onPointerLeave","onBlur","Record","contents","when","children","label","_$memo","fallback","placement","open","onOpenChange","icon","_el$","_tmpl$","_$insert","_$createComponent","PolymorphicButton","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/action-button/action-button.tsx"],"sourcesContent":["import {\n Show,\n createSignal,\n splitProps,\n type ComponentProps,\n type JSX,\n type ValidComponent,\n} from 'solid-js'\nimport { Button, type ButtonProps } from '../../ui/button'\nimport { Spinner } from '../../ui/spinner'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip'\n\n/**\n * ActionButton.\n *\n * A Button with optional explanatory tooltip and busy feedback. Use it for a\n * simple action that needs these states; actions with product-specific rules\n * should compose Button directly.\n *\n * The tooltip trigger renders the polymorphic Button itself. A disabled native\n * control cannot receive pointer or keyboard events. When a tooltip is set,\n * disabled actions therefore use focusable `aria-disabled` semantics and guard\n * activation; without a tooltip, native buttons retain native disabling.\n * Custom polymorphic components must forward aria, event, and href props for\n * these guarantees to apply.\n */\nexport type ActionButtonProps<T extends ValidComponent = 'button'> = ButtonProps<T> & {\n /** A short explanation, also shown on keyboard focus. The Button still needs its own name. */\n tooltip?: string\n /** Placement of the explanation; icon actions live in bars, so it defaults below the control. */\n tooltipSide?: 'top' | 'right' | 'bottom' | 'left'\n /**\n * A leading icon repeated inside the explanation, forwarded to the shared\n * tooltip's `icon` slot so a bar of icon actions gets the icon+label tip\n * without hand-composing Button and Tooltip. Decorative by contract: the tip\n * is never the accessible name, so the icon must not carry meaning the label\n * lacks. Size it by leaving the svg bare; the slot fixes it to control size.\n */\n tooltipIcon?: JSX.Element\n /** Disables the action and announces the busy label through a polite status region. */\n busy?: boolean\n /** The activity announced while busy. The Button's accessible name stays unchanged. */\n busyLabel?: string\n}\n\nfunction invokeHandler(handler: unknown, event: Event) {\n if (Array.isArray(handler)) {\n const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]\n callback(data, event)\n } else if (typeof handler === 'function') {\n const callback = handler as (event: Event) => void\n callback(event)\n }\n}\n\nfunction chainedHandler(handler: unknown, before: (event: Event) => void) {\n return (event: Event) => {\n before(event)\n invokeHandler(handler, event)\n }\n}\n\nfunction guardedClick(\n handler: unknown,\n disabled: () => boolean,\n onActivate: (event: MouseEvent) => void = () => {}\n) {\n return (event: MouseEvent) => {\n if (disabled()) {\n event.preventDefault()\n event.stopImmediatePropagation()\n return\n }\n\n onActivate(event)\n invokeHandler(handler, event)\n }\n}\n\ntype ButtonTargetProps<T extends ValidComponent = 'button'> = Omit<ButtonProps<T>, 'as'> & {\n actionAs?: T\n}\n\n/** Adapter keeps TooltipTrigger's `as` separate from Button's polymorphic `as`. */\nfunction ButtonTarget<T extends ValidComponent = 'button'>(props: ButtonTargetProps<T>) {\n const [local, rest] = splitProps(props as ButtonTargetProps, ['actionAs'])\n const PolymorphicButton = Button as (targetProps: ButtonProps<T>) => JSX.Element\n return <PolymorphicButton as={(local.actionAs ?? 'button') as T} {...(rest as ButtonProps<T>)} />\n}\n\nexport function ActionButton<T extends ValidComponent = 'button'>(props: ActionButtonProps<T>) {\n const [local, rest] = splitProps(props as ActionButtonProps, [\n 'as',\n 'variant',\n 'size',\n 'touchTarget',\n 'class',\n 'children',\n 'disabled',\n 'aria-busy',\n 'tooltip',\n 'tooltipSide',\n 'tooltipIcon',\n 'busy',\n 'busyLabel',\n ])\n const busy = () => local.busy ?? false\n const disabled = () => busy() || !!local.disabled\n const busyStatus = () => local.busyLabel ?? 'Working'\n const [tooltipOpen, setTooltipOpen] = createSignal(false)\n const [tooltipSuppression, setTooltipSuppression] = createSignal<'pointer' | 'focus' | null>(null)\n const onClickCapture = (rest as { onClickCapture?: unknown }).onClickCapture\n const PolymorphicButton = Button as (buttonProps: ButtonProps<T>) => JSX.Element\n const PolymorphicButtonTarget = ButtonTarget as (targetProps: ButtonTargetProps<T>) => JSX.Element\n\n const buttonProps = () => ({\n ...rest,\n variant: local.variant,\n size: local.size,\n touchTarget: local.touchTarget,\n class: [local.class, local.tooltip && disabled() && 'aria-disabled:pointer-events-auto']\n .filter(Boolean)\n .join(' '),\n disabled: disabled() && !local.tooltip,\n 'aria-disabled': disabled() ? true : rest['aria-disabled'],\n // Make tooltip-bearing native controls explicit tab stops in WebKit configurations that\n // otherwise skip implicit button stops. A caller's tabIndex remains authoritative.\n tabIndex:\n rest.tabIndex ??\n (local.as === 'a' && disabled() ? (local.tooltip ? 0 : -1) : local.tooltip ? 0 : undefined),\n role: local.as === 'a' && disabled() ? 'link' : rest.role,\n href: local.as === 'a' && disabled() ? undefined : (rest as { href?: string }).href,\n 'aria-busy': busy() || local['aria-busy'],\n onClick: guardedClick(rest.onClick, disabled, (event) => {\n if (!local.tooltip) return\n setTooltipSuppression(event.detail > 0 ? 'pointer' : 'focus')\n setTooltipOpen(false)\n }),\n onPointerLeave: chainedHandler(rest.onPointerLeave, () => {\n if (tooltipSuppression() === 'pointer') setTooltipSuppression(null)\n }),\n onBlur: chainedHandler(rest.onBlur, () => {\n if (tooltipSuppression() === 'focus') setTooltipSuppression(null)\n }),\n onClickCapture: onClickCapture ? guardedClick(onClickCapture, disabled) : undefined,\n 'on:click': guardedClick((rest as Record<string, unknown>)['on:click'], disabled),\n 'oncapture:click': guardedClick((rest as Record<string, unknown>)['oncapture:click'], disabled),\n })\n\n const contents = () => (\n <>\n <Show when={busy()}>\n <Spinner size=\"sm\" label={false} aria-hidden=\"true\" data-icon=\"inline-start\" />\n </Show>\n {local.children}\n </>\n )\n\n return (\n <>\n <Show\n when={local.tooltip}\n fallback={\n <PolymorphicButton\n as={(local.as ?? 'button') as T}\n {...(buttonProps() as unknown as ButtonProps<T>)}\n >\n {contents()}\n </PolymorphicButton>\n }\n >\n <Tooltip\n placement={local.tooltipSide ?? 'bottom'}\n open={tooltipOpen()}\n onOpenChange={(open) => {\n if (open && tooltipSuppression()) return\n setTooltipOpen(open)\n }}\n >\n <TooltipTrigger\n as={PolymorphicButtonTarget}\n {...(buttonProps() as unknown as ComponentProps<typeof PolymorphicButtonTarget>)}\n actionAs={(local.as ?? 'button') as T}\n >\n {contents()}\n </TooltipTrigger>\n <TooltipContent icon={local.tooltipIcon}>{local.tooltip}</TooltipContent>\n </Tooltip>\n </Show>\n <Show when={busy()}>\n <span role=\"status\" aria-live=\"polite\" class=\"visually-hidden\">\n {busyStatus()}\n </span>\n </Show>\n </>\n )\n}\n"],"mappings":"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"}
|
|
@@ -29,6 +29,14 @@ export type ActionButtonProps<T extends ValidComponent = 'button'> = ButtonProps
|
|
|
29
29
|
tooltip?: string
|
|
30
30
|
/** Placement of the explanation; icon actions live in bars, so it defaults below the control. */
|
|
31
31
|
tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
|
|
32
|
+
/**
|
|
33
|
+
* A leading icon repeated inside the explanation, forwarded to the shared
|
|
34
|
+
* tooltip's `icon` slot so a bar of icon actions gets the icon+label tip
|
|
35
|
+
* without hand-composing Button and Tooltip. Decorative by contract: the tip
|
|
36
|
+
* is never the accessible name, so the icon must not carry meaning the label
|
|
37
|
+
* lacks. Size it by leaving the svg bare; the slot fixes it to control size.
|
|
38
|
+
*/
|
|
39
|
+
tooltipIcon?: JSX.Element
|
|
32
40
|
/** Disables the action and announces the busy label through a polite status region. */
|
|
33
41
|
busy?: boolean
|
|
34
42
|
/** The activity announced while busy. The Button's accessible name stays unchanged. */
|
|
@@ -92,6 +100,7 @@ export function ActionButton<T extends ValidComponent = 'button'>(props: ActionB
|
|
|
92
100
|
'aria-busy',
|
|
93
101
|
'tooltip',
|
|
94
102
|
'tooltipSide',
|
|
103
|
+
'tooltipIcon',
|
|
95
104
|
'busy',
|
|
96
105
|
'busyLabel',
|
|
97
106
|
])
|
|
@@ -175,7 +184,7 @@ export function ActionButton<T extends ValidComponent = 'button'>(props: ActionB
|
|
|
175
184
|
>
|
|
176
185
|
{contents()}
|
|
177
186
|
</TooltipTrigger>
|
|
178
|
-
<TooltipContent>{local.tooltip}</TooltipContent>
|
|
187
|
+
<TooltipContent icon={local.tooltipIcon}>{local.tooltip}</TooltipContent>
|
|
179
188
|
</Tooltip>
|
|
180
189
|
</Show>
|
|
181
190
|
<Show when={busy()}>
|
package/package.json
CHANGED
|
@@ -29,6 +29,14 @@ export type ActionButtonProps<T extends ValidComponent = 'button'> = ButtonProps
|
|
|
29
29
|
tooltip?: string
|
|
30
30
|
/** Placement of the explanation; icon actions live in bars, so it defaults below the control. */
|
|
31
31
|
tooltipSide?: 'top' | 'right' | 'bottom' | 'left'
|
|
32
|
+
/**
|
|
33
|
+
* A leading icon repeated inside the explanation, forwarded to the shared
|
|
34
|
+
* tooltip's `icon` slot so a bar of icon actions gets the icon+label tip
|
|
35
|
+
* without hand-composing Button and Tooltip. Decorative by contract: the tip
|
|
36
|
+
* is never the accessible name, so the icon must not carry meaning the label
|
|
37
|
+
* lacks. Size it by leaving the svg bare; the slot fixes it to control size.
|
|
38
|
+
*/
|
|
39
|
+
tooltipIcon?: JSX.Element
|
|
32
40
|
/** Disables the action and announces the busy label through a polite status region. */
|
|
33
41
|
busy?: boolean
|
|
34
42
|
/** The activity announced while busy. The Button's accessible name stays unchanged. */
|
|
@@ -92,6 +100,7 @@ export function ActionButton<T extends ValidComponent = 'button'>(props: ActionB
|
|
|
92
100
|
'aria-busy',
|
|
93
101
|
'tooltip',
|
|
94
102
|
'tooltipSide',
|
|
103
|
+
'tooltipIcon',
|
|
95
104
|
'busy',
|
|
96
105
|
'busyLabel',
|
|
97
106
|
])
|
|
@@ -175,7 +184,7 @@ export function ActionButton<T extends ValidComponent = 'button'>(props: ActionB
|
|
|
175
184
|
>
|
|
176
185
|
{contents()}
|
|
177
186
|
</TooltipTrigger>
|
|
178
|
-
<TooltipContent>{local.tooltip}</TooltipContent>
|
|
187
|
+
<TooltipContent icon={local.tooltipIcon}>{local.tooltip}</TooltipContent>
|
|
179
188
|
</Tooltip>
|
|
180
189
|
</Show>
|
|
181
190
|
<Show when={busy()}>
|