@urbicon-ui/blocks 6.21.1 → 6.21.3
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/NumberInput/NumberInput.svelte +197 -0
- package/dist/components/NumberInput/NumberInput.svelte.d.ts +4 -0
- package/dist/components/NumberInput/index.d.ts +55 -0
- package/dist/components/NumberInput/index.js +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/primitives/Avatar/avatar.variants.d.ts +6 -6
- package/dist/primitives/Badge/Badge.svelte +18 -8
- package/dist/primitives/Badge/index.d.ts +26 -6
- package/dist/primitives/Breadcrumb/Breadcrumb.svelte +11 -6
- package/dist/primitives/Breadcrumb/index.d.ts +2 -2
- package/dist/primitives/Button/button.variants.d.ts +4 -4
- package/dist/primitives/ButtonGroup/index.d.ts +1 -1
- package/dist/primitives/Card/card.variants.d.ts +5 -5
- package/dist/primitives/Checkbox/Checkbox.svelte +3 -3
- package/dist/primitives/Combobox/Combobox.svelte +478 -104
- package/dist/primitives/Combobox/combobox.variants.d.ts +89 -17
- package/dist/primitives/Combobox/combobox.variants.js +94 -13
- package/dist/primitives/Combobox/index.d.ts +166 -51
- package/dist/primitives/ConfirmDialog/index.d.ts +19 -0
- package/dist/primitives/Dialog/Dialog.svelte +74 -0
- package/dist/primitives/Dialog/dialog.variants.d.ts +9 -9
- package/dist/primitives/Dialog/index.d.ts +12 -1
- package/dist/primitives/Drawer/Drawer.svelte +2 -1
- package/dist/primitives/Drawer/drawer.variants.d.ts +8 -0
- package/dist/primitives/Drawer/drawer.variants.js +45 -10
- package/dist/primitives/Drawer/index.d.ts +13 -4
- package/dist/primitives/FormField/FormField.svelte +6 -6
- package/dist/primitives/FormField/index.d.ts +13 -4
- package/dist/primitives/Input/Input.svelte +8 -4
- package/dist/primitives/Input/input.variants.d.ts +9 -9
- package/dist/primitives/Menu/Menu.svelte +66 -4
- package/dist/primitives/Menu/index.d.ts +25 -0
- package/dist/primitives/Pagination/Pagination.svelte +28 -13
- package/dist/primitives/Pagination/index.d.ts +44 -0
- package/dist/primitives/Popover/Popover.svelte +34 -3
- package/dist/primitives/Popover/index.d.ts +3 -3
- package/dist/primitives/RadioGroup/RadioGroup.svelte +3 -3
- package/dist/primitives/Select/Select.svelte +21 -11
- package/dist/primitives/Select/index.d.ts +7 -0
- package/dist/primitives/Select/select.variants.d.ts +17 -17
- package/dist/primitives/Select/select.variants.js +18 -0
- package/dist/primitives/Skeleton/skeleton.variants.d.ts +3 -3
- package/dist/primitives/Slider/Slider.svelte +4 -8
- package/dist/primitives/Spinner/spinner.variants.d.ts +16 -16
- package/dist/primitives/Textarea/Textarea.svelte +13 -5
- package/dist/primitives/Textarea/textarea.variants.d.ts +7 -7
- package/dist/primitives/Textarea/textarea.variants.js +5 -2
- package/dist/primitives/Toast/Toaster.svelte +42 -2
- package/dist/primitives/Toast/index.d.ts +30 -0
- package/dist/primitives/Toast/toast.store.svelte.d.ts +25 -1
- package/dist/primitives/Toast/toast.store.svelte.js +91 -1
- package/dist/primitives/Toast/toast.variants.d.ts +18 -0
- package/dist/primitives/Toast/toast.variants.js +15 -0
- package/dist/primitives/Toggle/Toggle.svelte +17 -3
- package/dist/primitives/Toggle/index.d.ts +8 -2
- package/dist/primitives/Toggle/toggle.variants.d.ts +7 -0
- package/dist/primitives/Toggle/toggle.variants.js +26 -0
- package/dist/primitives/Toolbar/toolbar.variants.d.ts +6 -6
- package/dist/primitives/Tooltip/Tooltip.svelte +47 -21
- package/dist/primitives/Tooltip/Tooltip.svelte.d.ts +1 -1
- package/dist/primitives/Tooltip/index.d.ts +31 -3
- package/dist/primitives/Tooltip/tooltip.variants.d.ts +4 -4
- package/dist/primitives/Tooltip/tooltip.variants.js +2 -2
- package/dist/primitives/index.d.ts +3 -3
- package/dist/style/semantic.css +55 -29
- package/dist/translations/de.d.ts +3 -0
- package/dist/translations/de.js +4 -1
- package/dist/translations/en.d.ts +3 -0
- package/dist/translations/en.js +4 -1
- package/dist/utils/figma-token-export.d.ts +13 -0
- package/dist/utils/figma-token-export.js +99 -26
- package/dist/utils/guide.svelte.d.ts +8 -2
- package/dist/utils/guide.svelte.js +86 -5
- package/dist/utils/use-form-field.svelte.d.ts +9 -9
- package/dist/utils/use-form-field.svelte.js +12 -12
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ToastData, ToastInput, ToastShorthandOpts } from './index.js';
|
|
1
|
+
import type { ToastData, ToastInput, ToastPromiseOptions, ToastShorthandOpts } from './index.js';
|
|
2
2
|
import type { ToastPlacement } from './toast.variants.js';
|
|
3
3
|
/**
|
|
4
4
|
* Reactive store that manages the global toast queue.
|
|
@@ -24,6 +24,30 @@ declare class ToastStore {
|
|
|
24
24
|
registerSubscriber(): () => void;
|
|
25
25
|
/** Create a toast with full control over all options. Returns the toast ID for programmatic dismissal. */
|
|
26
26
|
add(input: ToastInput): string;
|
|
27
|
+
/**
|
|
28
|
+
* Merge new fields into an existing toast in place — same id, same stack
|
|
29
|
+
* position — and reset its auto-dismiss timer to the new `duration`. No-op if
|
|
30
|
+
* the toast is gone. Backs `promise()`, which flips a pending toast to
|
|
31
|
+
* success/error without it sliding out and a new one flying in.
|
|
32
|
+
*/
|
|
33
|
+
update(id: string, input: ToastInput): void;
|
|
34
|
+
/**
|
|
35
|
+
* Drive a toast through a promise's lifecycle (Sonner-style). Shows a
|
|
36
|
+
* persistent, non-dismissible spinner toast while pending, then flips it in
|
|
37
|
+
* place to success on resolve or danger on reject. `success`/`error` may be a
|
|
38
|
+
* plain title, a full {@link ToastInput}, or a function of the resolved value
|
|
39
|
+
* / rejection reason. Returns the toast id.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* toaster.promise(saveDraft(), {
|
|
44
|
+
* loading: 'Saving…',
|
|
45
|
+
* success: (draft) => `Saved “${draft.title}”`,
|
|
46
|
+
* error: (e) => `Could not save: ${e.message}`
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
promise<T>(promise: Promise<T>, opts: ToastPromiseOptions<T>): string;
|
|
27
51
|
/** Remove a single toast by ID. Clears its auto-dismiss timer. */
|
|
28
52
|
dismiss(id: string): void;
|
|
29
53
|
/** Remove all toasts and cancel their timers. */
|
|
@@ -50,7 +50,10 @@ class ToastStore {
|
|
|
50
50
|
description: input.description,
|
|
51
51
|
duration: input.duration ?? 5000,
|
|
52
52
|
dismissible: input.dismissible ?? true,
|
|
53
|
-
showProgress: input.showProgress ?? true
|
|
53
|
+
showProgress: input.showProgress ?? true,
|
|
54
|
+
action: input.action,
|
|
55
|
+
cancel: input.cancel,
|
|
56
|
+
loading: input.loading ?? false
|
|
54
57
|
};
|
|
55
58
|
this.toasts = [...this.toasts, toast];
|
|
56
59
|
if (toast.duration > 0) {
|
|
@@ -58,6 +61,93 @@ class ToastStore {
|
|
|
58
61
|
}
|
|
59
62
|
return id;
|
|
60
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Merge new fields into an existing toast in place — same id, same stack
|
|
66
|
+
* position — and reset its auto-dismiss timer to the new `duration`. No-op if
|
|
67
|
+
* the toast is gone. Backs `promise()`, which flips a pending toast to
|
|
68
|
+
* success/error without it sliding out and a new one flying in.
|
|
69
|
+
*/
|
|
70
|
+
update(id, input) {
|
|
71
|
+
const idx = this.toasts.findIndex((t) => t.id === id);
|
|
72
|
+
if (idx === -1)
|
|
73
|
+
return;
|
|
74
|
+
const next = { ...this.toasts[idx], ...input };
|
|
75
|
+
this.toasts = [...this.toasts.slice(0, idx), next, ...this.toasts.slice(idx + 1)];
|
|
76
|
+
const timer = this.timers.get(id);
|
|
77
|
+
if (timer) {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
this.timers.delete(id);
|
|
80
|
+
}
|
|
81
|
+
if (next.duration > 0) {
|
|
82
|
+
this.timers.set(id, setTimeout(() => this.dismiss(id), next.duration));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Drive a toast through a promise's lifecycle (Sonner-style). Shows a
|
|
87
|
+
* persistent, non-dismissible spinner toast while pending, then flips it in
|
|
88
|
+
* place to success on resolve or danger on reject. `success`/`error` may be a
|
|
89
|
+
* plain title, a full {@link ToastInput}, or a function of the resolved value
|
|
90
|
+
* / rejection reason. Returns the toast id.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* toaster.promise(saveDraft(), {
|
|
95
|
+
* loading: 'Saving…',
|
|
96
|
+
* success: (draft) => `Saved “${draft.title}”`,
|
|
97
|
+
* error: (e) => `Could not save: ${e.message}`
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
promise(promise, opts) {
|
|
102
|
+
const loadingInput = typeof opts.loading === 'string' ? { title: opts.loading } : opts.loading;
|
|
103
|
+
const id = this.add({
|
|
104
|
+
intent: 'neutral',
|
|
105
|
+
...loadingInput,
|
|
106
|
+
loading: true,
|
|
107
|
+
duration: 0,
|
|
108
|
+
dismissible: false,
|
|
109
|
+
showProgress: false
|
|
110
|
+
});
|
|
111
|
+
// Shared settle fields: leave the spinner/persistent/non-dismissible state
|
|
112
|
+
// and become a normal auto-dismissing toast. `description: undefined` clears
|
|
113
|
+
// the loading description unless the success/error config sets its own.
|
|
114
|
+
const settled = {
|
|
115
|
+
loading: false,
|
|
116
|
+
duration: 5000,
|
|
117
|
+
dismissible: true,
|
|
118
|
+
showProgress: true
|
|
119
|
+
};
|
|
120
|
+
// A throwing user formatter must not turn into an unhandled rejection on the
|
|
121
|
+
// internal `.then`; fall back to a bare settled toast (still flips out of the
|
|
122
|
+
// loading state) and dev-warn instead.
|
|
123
|
+
const resolveMessage = (fn, arg, label) => {
|
|
124
|
+
let out;
|
|
125
|
+
try {
|
|
126
|
+
out = typeof fn === 'function' ? fn(arg) : fn;
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (import.meta.env?.DEV)
|
|
130
|
+
console.warn(`[Toast] promise ${label} formatter threw:`, err);
|
|
131
|
+
}
|
|
132
|
+
return typeof out === 'string' ? { title: out } : out;
|
|
133
|
+
};
|
|
134
|
+
promise.then((value) => {
|
|
135
|
+
this.update(id, {
|
|
136
|
+
intent: 'success',
|
|
137
|
+
description: undefined,
|
|
138
|
+
...settled,
|
|
139
|
+
...resolveMessage(opts.success, value, 'success')
|
|
140
|
+
});
|
|
141
|
+
}, (reason) => {
|
|
142
|
+
this.update(id, {
|
|
143
|
+
intent: 'danger',
|
|
144
|
+
description: undefined,
|
|
145
|
+
...settled,
|
|
146
|
+
...resolveMessage(opts.error, reason, 'error')
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
return id;
|
|
150
|
+
}
|
|
61
151
|
/** Remove a single toast by ID. Clears its auto-dismiss timer. */
|
|
62
152
|
dismiss(id) {
|
|
63
153
|
const timer = this.timers.get(id);
|
|
@@ -39,6 +39,24 @@ export declare const toastVariants: ((props?: {
|
|
|
39
39
|
} & {
|
|
40
40
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
41
41
|
}) | undefined) => string;
|
|
42
|
+
actions: (props?: ({
|
|
43
|
+
intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
|
|
44
|
+
placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
|
|
45
|
+
} & {
|
|
46
|
+
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
47
|
+
}) | undefined) => string;
|
|
48
|
+
actionButton: (props?: ({
|
|
49
|
+
intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
|
|
50
|
+
placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
|
|
51
|
+
} & {
|
|
52
|
+
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
53
|
+
}) | undefined) => string;
|
|
54
|
+
cancelButton: (props?: ({
|
|
55
|
+
intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
|
|
56
|
+
placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
|
|
57
|
+
} & {
|
|
58
|
+
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
59
|
+
}) | undefined) => string;
|
|
42
60
|
dismissButton: (props?: ({
|
|
43
61
|
intent?: "primary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
|
|
44
62
|
placement?: "bottom-right" | "top-right" | "bottom-left" | "top-left" | "top-center" | "bottom-center" | undefined;
|
|
@@ -16,6 +16,21 @@ export const toastVariants = tv({
|
|
|
16
16
|
content: 'flex-1 min-w-0',
|
|
17
17
|
title: 'font-semibold text-sm leading-tight',
|
|
18
18
|
description: 'text-sm mt-0.5 opacity-80',
|
|
19
|
+
// Action row (Sonner-style). Intent-neutral, prominent primary + quiet
|
|
20
|
+
// cancel — the toast's intent already reads through the icon/progress, so
|
|
21
|
+
// the buttons stay contrast-based rather than intent-coloured.
|
|
22
|
+
actions: 'flex items-center gap-2 mt-2.5',
|
|
23
|
+
// tier: modify — small interactive sub-elements on a contain surface.
|
|
24
|
+
actionButton: [
|
|
25
|
+
'rounded-modify px-2.5 py-1 text-xs font-medium',
|
|
26
|
+
'bg-text-primary/10 hover:bg-text-primary/20 text-text-primary',
|
|
27
|
+
'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current/40'
|
|
28
|
+
],
|
|
29
|
+
cancelButton: [
|
|
30
|
+
'rounded-modify px-2.5 py-1 text-xs font-medium',
|
|
31
|
+
'text-text-secondary hover:text-text-primary',
|
|
32
|
+
'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current/40'
|
|
33
|
+
],
|
|
19
34
|
// tier: modify — small interactive sub-element on a contain surface.
|
|
20
35
|
dismissButton: [
|
|
21
36
|
'shrink-0 rounded-modify p-1',
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
checked = $bindable(false),
|
|
12
12
|
label,
|
|
13
13
|
helper,
|
|
14
|
+
error,
|
|
14
15
|
tier,
|
|
15
16
|
size = 'md',
|
|
16
17
|
intent = 'primary',
|
|
@@ -35,7 +36,8 @@
|
|
|
35
36
|
const id = $derived(idProp ?? propsId);
|
|
36
37
|
const ff = useFormField(() => ({
|
|
37
38
|
fieldId: id,
|
|
38
|
-
|
|
39
|
+
helper,
|
|
40
|
+
error,
|
|
39
41
|
required,
|
|
40
42
|
disabled
|
|
41
43
|
}));
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
appearance,
|
|
62
64
|
checked,
|
|
63
65
|
disabled,
|
|
66
|
+
error: !!error,
|
|
64
67
|
withBorder
|
|
65
68
|
});
|
|
66
69
|
|
|
@@ -111,6 +114,7 @@
|
|
|
111
114
|
aria-checked={checked}
|
|
112
115
|
aria-label={label ? undefined : bt('accessibility.toggle') || 'Toggle'}
|
|
113
116
|
aria-describedby={ff.describedBy}
|
|
117
|
+
aria-invalid={ff.invalid ? 'true' : undefined}
|
|
114
118
|
onchange={handleChange}
|
|
115
119
|
{...restProps}
|
|
116
120
|
/>
|
|
@@ -135,9 +139,19 @@
|
|
|
135
139
|
{/if}
|
|
136
140
|
</label>
|
|
137
141
|
|
|
138
|
-
{#if ff.
|
|
142
|
+
{#if ff.errorId}
|
|
143
|
+
<div
|
|
144
|
+
id={ff.errorId}
|
|
145
|
+
class={unstyled
|
|
146
|
+
? (slotClasses?.message ?? '')
|
|
147
|
+
: styles.message({ class: slotClasses?.message })}
|
|
148
|
+
role="alert"
|
|
149
|
+
>
|
|
150
|
+
{error}
|
|
151
|
+
</div>
|
|
152
|
+
{:else if ff.helperId}
|
|
139
153
|
<div
|
|
140
|
-
id={ff.
|
|
154
|
+
id={ff.helperId}
|
|
141
155
|
class={unstyled
|
|
142
156
|
? (slotClasses?.message ?? '')
|
|
143
157
|
: styles.message({ class: slotClasses?.message })}
|
|
@@ -24,13 +24,19 @@ import type { ToggleSlots, ToggleVariants } from './toggle.variants.js';
|
|
|
24
24
|
* />
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
|
-
export interface ToggleProps extends Omit<ToggleVariants, 'checked'>, Omit<HTMLInputAttributes, 'type' | 'size' | 'checked' | 'class' | 'children'> {
|
|
27
|
+
export interface ToggleProps extends Omit<ToggleVariants, 'checked' | 'error'>, Omit<HTMLInputAttributes, 'type' | 'size' | 'checked' | 'class' | 'children'> {
|
|
28
28
|
/** Current on/off state. Supports two-way binding via `bind:checked`. */
|
|
29
29
|
checked?: boolean;
|
|
30
30
|
/** Text label displayed to the right of the toggle track. */
|
|
31
31
|
label?: string;
|
|
32
|
-
/**
|
|
32
|
+
/** Helper text shown below the control. Hidden when `error` is set. Useful for explaining side-effects of the toggle. */
|
|
33
33
|
helper?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Error message shown below the control. Replaces `helper`, flags the
|
|
36
|
+
* hidden input `aria-invalid`, and marks the unchecked track with the
|
|
37
|
+
* danger colour — e.g. for a consent toggle that must be switched on.
|
|
38
|
+
*/
|
|
39
|
+
error?: string;
|
|
34
40
|
/** Prevent interaction and dim the control. */
|
|
35
41
|
disabled?: boolean;
|
|
36
42
|
/** Mark the native input as required for form validation. */
|
|
@@ -6,6 +6,7 @@ export declare const toggleVariants: ((props?: {
|
|
|
6
6
|
appearance?: "default" | "dot" | undefined;
|
|
7
7
|
checked?: boolean | undefined;
|
|
8
8
|
disabled?: boolean | undefined;
|
|
9
|
+
error?: boolean | undefined;
|
|
9
10
|
withBorder?: boolean | undefined;
|
|
10
11
|
} | undefined) => {
|
|
11
12
|
wrapper: (props?: ({
|
|
@@ -15,6 +16,7 @@ export declare const toggleVariants: ((props?: {
|
|
|
15
16
|
appearance?: "default" | "dot" | undefined;
|
|
16
17
|
checked?: boolean | undefined;
|
|
17
18
|
disabled?: boolean | undefined;
|
|
19
|
+
error?: boolean | undefined;
|
|
18
20
|
withBorder?: boolean | undefined;
|
|
19
21
|
} & {
|
|
20
22
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
@@ -26,6 +28,7 @@ export declare const toggleVariants: ((props?: {
|
|
|
26
28
|
appearance?: "default" | "dot" | undefined;
|
|
27
29
|
checked?: boolean | undefined;
|
|
28
30
|
disabled?: boolean | undefined;
|
|
31
|
+
error?: boolean | undefined;
|
|
29
32
|
withBorder?: boolean | undefined;
|
|
30
33
|
} & {
|
|
31
34
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
@@ -37,6 +40,7 @@ export declare const toggleVariants: ((props?: {
|
|
|
37
40
|
appearance?: "default" | "dot" | undefined;
|
|
38
41
|
checked?: boolean | undefined;
|
|
39
42
|
disabled?: boolean | undefined;
|
|
43
|
+
error?: boolean | undefined;
|
|
40
44
|
withBorder?: boolean | undefined;
|
|
41
45
|
} & {
|
|
42
46
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
@@ -48,6 +52,7 @@ export declare const toggleVariants: ((props?: {
|
|
|
48
52
|
appearance?: "default" | "dot" | undefined;
|
|
49
53
|
checked?: boolean | undefined;
|
|
50
54
|
disabled?: boolean | undefined;
|
|
55
|
+
error?: boolean | undefined;
|
|
51
56
|
withBorder?: boolean | undefined;
|
|
52
57
|
} & {
|
|
53
58
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
@@ -59,6 +64,7 @@ export declare const toggleVariants: ((props?: {
|
|
|
59
64
|
appearance?: "default" | "dot" | undefined;
|
|
60
65
|
checked?: boolean | undefined;
|
|
61
66
|
disabled?: boolean | undefined;
|
|
67
|
+
error?: boolean | undefined;
|
|
62
68
|
withBorder?: boolean | undefined;
|
|
63
69
|
} & {
|
|
64
70
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
@@ -70,6 +76,7 @@ export declare const toggleVariants: ((props?: {
|
|
|
70
76
|
appearance?: "default" | "dot" | undefined;
|
|
71
77
|
checked?: boolean | undefined;
|
|
72
78
|
disabled?: boolean | undefined;
|
|
79
|
+
error?: boolean | undefined;
|
|
73
80
|
withBorder?: boolean | undefined;
|
|
74
81
|
} & {
|
|
75
82
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
@@ -86,6 +86,11 @@ export const toggleVariants = tv({
|
|
|
86
86
|
control: 'opacity-50 cursor-not-allowed pointer-events-none'
|
|
87
87
|
}
|
|
88
88
|
},
|
|
89
|
+
error: {
|
|
90
|
+
true: {
|
|
91
|
+
message: 'text-danger'
|
|
92
|
+
}
|
|
93
|
+
},
|
|
89
94
|
withBorder: {
|
|
90
95
|
true: {},
|
|
91
96
|
false: {}
|
|
@@ -153,6 +158,19 @@ export const toggleVariants = tv({
|
|
|
153
158
|
intent: 'neutral',
|
|
154
159
|
class: { track: 'bg-neutral border-neutral' }
|
|
155
160
|
},
|
|
161
|
+
// ── Error state: danger boundary on the unchecked track (Switch-Pill) ──
|
|
162
|
+
// Mirrors Checkbox ("error overrides unchecked border"): the off state
|
|
163
|
+
// carries the danger mark, the on state keeps its intent colour — an
|
|
164
|
+
// error on a boolean control usually means "must be switched on".
|
|
165
|
+
// The dot-appearance twin lives at the END of the compound list so it
|
|
166
|
+
// folds over the dot-unchecked `border-border-default` (order is
|
|
167
|
+
// semantic — later compounds win conflicting buckets).
|
|
168
|
+
{
|
|
169
|
+
appearance: 'default',
|
|
170
|
+
error: true,
|
|
171
|
+
checked: false,
|
|
172
|
+
class: { track: 'border-danger peer-focus-visible:ring-danger/40' }
|
|
173
|
+
},
|
|
156
174
|
// ── Thumb translation per size when checked (Switch-Pill only) ──
|
|
157
175
|
{ appearance: 'default', checked: true, size: 'xs', class: { thumb: 'translate-x-4' } },
|
|
158
176
|
{ appearance: 'default', checked: true, size: 'sm', class: { thumb: 'translate-x-5' } },
|
|
@@ -212,6 +230,13 @@ export const toggleVariants = tv({
|
|
|
212
230
|
checked: true,
|
|
213
231
|
intent: 'neutral',
|
|
214
232
|
class: { track: 'border-neutral bg-neutral' }
|
|
233
|
+
},
|
|
234
|
+
// ── Error state, dot appearance (must fold over dot-unchecked border) ──
|
|
235
|
+
{
|
|
236
|
+
appearance: 'dot',
|
|
237
|
+
error: true,
|
|
238
|
+
checked: false,
|
|
239
|
+
class: { track: 'border-danger' }
|
|
215
240
|
}
|
|
216
241
|
],
|
|
217
242
|
defaultVariants: {
|
|
@@ -221,6 +246,7 @@ export const toggleVariants = tv({
|
|
|
221
246
|
appearance: 'default',
|
|
222
247
|
checked: false,
|
|
223
248
|
disabled: false,
|
|
249
|
+
error: false,
|
|
224
250
|
withBorder: false
|
|
225
251
|
}
|
|
226
252
|
});
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { type SlotNames, type VariantProps } from '../../utils/variants.js';
|
|
2
2
|
export declare const toolbarVariants: ((props?: {
|
|
3
|
-
variant?: "ghost" | "
|
|
3
|
+
variant?: "ghost" | "quiet" | "elevated" | "outlined" | undefined;
|
|
4
4
|
orientation?: "horizontal" | "vertical" | undefined;
|
|
5
|
-
gap?: "sm" | "md" | "lg" | "
|
|
6
|
-
padding?: "sm" | "md" | "lg" | "
|
|
5
|
+
gap?: "sm" | "md" | "lg" | "xs" | "xl" | undefined;
|
|
6
|
+
padding?: "sm" | "md" | "lg" | "xs" | "xl" | undefined;
|
|
7
7
|
} | undefined) => {
|
|
8
8
|
base: (props?: ({
|
|
9
|
-
variant?: "ghost" | "
|
|
9
|
+
variant?: "ghost" | "quiet" | "elevated" | "outlined" | undefined;
|
|
10
10
|
orientation?: "horizontal" | "vertical" | undefined;
|
|
11
|
-
gap?: "sm" | "md" | "lg" | "
|
|
12
|
-
padding?: "sm" | "md" | "lg" | "
|
|
11
|
+
gap?: "sm" | "md" | "lg" | "xs" | "xl" | undefined;
|
|
12
|
+
padding?: "sm" | "md" | "lg" | "xs" | "xl" | undefined;
|
|
13
13
|
} & {
|
|
14
14
|
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
15
15
|
}) | undefined) => string;
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
transitionEasing,
|
|
26
26
|
disabled = false,
|
|
27
27
|
arrow = true,
|
|
28
|
-
|
|
28
|
+
open = $bindable(false),
|
|
29
|
+
onOpenChange,
|
|
29
30
|
class: className,
|
|
30
31
|
unstyled: unstyledProp = false,
|
|
31
32
|
slotClasses: slotClassesProp = {},
|
|
@@ -43,7 +44,6 @@
|
|
|
43
44
|
transitionDuration != null ? `${transitionDuration}ms` : undefined
|
|
44
45
|
);
|
|
45
46
|
|
|
46
|
-
let visible = $state(false);
|
|
47
47
|
let triggerElement = $state<HTMLElement>();
|
|
48
48
|
let tooltipElement = $state<HTMLElement>();
|
|
49
49
|
let arrowElement = $state<HTMLElement>();
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
const propsId = $props.id();
|
|
53
53
|
const tooltipId = `tooltip-${propsId}`;
|
|
54
54
|
|
|
55
|
-
const variantProps: VariantProps<typeof tooltipVariants> = $derived({
|
|
55
|
+
const variantProps: VariantProps<typeof tooltipVariants> = $derived({ open, intent, size });
|
|
56
56
|
const styles = $derived(tooltipVariants(variantProps));
|
|
57
57
|
const slotClasses = $derived(
|
|
58
58
|
resolveSlotClasses(blocksConfig, 'Tooltip', preset, variantProps, slotClassesProp)
|
|
@@ -60,16 +60,38 @@
|
|
|
60
60
|
|
|
61
61
|
// Inside an open modal <dialog>, a popover shown via showPopover() forms a
|
|
62
62
|
// second top-layer element WebKit won't render above the dialog (Codeberg
|
|
63
|
-
// #23). Evaluated while `
|
|
63
|
+
// #23). Evaluated while `open` so the check runs once the dialog is
|
|
64
64
|
// already modal; the tooltip then renders in place (position:fixed + the
|
|
65
|
-
// opacity `
|
|
65
|
+
// opacity `open` variant + pointer-events-none) within the dialog's own
|
|
66
66
|
// top-layer subtree, no showPopover() needed.
|
|
67
|
-
const topLayer = $derived(
|
|
67
|
+
const topLayer = $derived(open ? !isAnchoredInModalDialog(triggerElement) : true);
|
|
68
68
|
|
|
69
69
|
let showTimeout: number;
|
|
70
70
|
let hideTimeout: number;
|
|
71
71
|
let cleanup: (() => void) | undefined = undefined;
|
|
72
72
|
|
|
73
|
+
// Clear pending show/hide timers on unmount — a hover followed by a
|
|
74
|
+
// teardown within `showDelay` (route change, {#if} removal) would
|
|
75
|
+
// otherwise fire setOpen after destroy: a write to the destroyed
|
|
76
|
+
// $bindable plus a ghost onOpenChange(true) for a tooltip that no
|
|
77
|
+
// longer exists. Dependency-free effect → runs once, teardown on destroy.
|
|
78
|
+
$effect(() => {
|
|
79
|
+
return () => {
|
|
80
|
+
clearTimeout(showTimeout);
|
|
81
|
+
clearTimeout(hideTimeout);
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Single mutation point for interaction-driven changes, so `onOpenChange`
|
|
86
|
+
// fires exactly once per transition. The no-change guard keeps a re-hover
|
|
87
|
+
// during the hide delay quiet (open never flipped). Consumer writes via
|
|
88
|
+
// `bind:open` bypass this on purpose — they don't re-announce themselves.
|
|
89
|
+
function setOpen(next: boolean) {
|
|
90
|
+
if (open === next) return;
|
|
91
|
+
open = next;
|
|
92
|
+
onOpenChange?.(next);
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
function show() {
|
|
74
96
|
if (disabled) return;
|
|
75
97
|
clearTimeout(hideTimeout);
|
|
@@ -80,24 +102,21 @@
|
|
|
80
102
|
// call would briefly flash the tooltip before the effect tears it
|
|
81
103
|
// back down.
|
|
82
104
|
if (disabled) return;
|
|
83
|
-
|
|
84
|
-
onVisibleChange?.(true);
|
|
105
|
+
setOpen(true);
|
|
85
106
|
}, showDelay);
|
|
86
107
|
}
|
|
87
108
|
|
|
88
109
|
function hide() {
|
|
89
110
|
clearTimeout(showTimeout);
|
|
90
111
|
hideTimeout = window.setTimeout(() => {
|
|
91
|
-
|
|
92
|
-
onVisibleChange?.(false);
|
|
112
|
+
setOpen(false);
|
|
93
113
|
}, hideDelay);
|
|
94
114
|
}
|
|
95
115
|
|
|
96
116
|
function handleKeydown(e: KeyboardEvent) {
|
|
97
|
-
if (e.key === 'Escape' &&
|
|
117
|
+
if (e.key === 'Escape' && open) {
|
|
98
118
|
clearTimeout(showTimeout);
|
|
99
|
-
|
|
100
|
-
onVisibleChange?.(false);
|
|
119
|
+
setOpen(false);
|
|
101
120
|
}
|
|
102
121
|
}
|
|
103
122
|
|
|
@@ -156,7 +175,7 @@
|
|
|
156
175
|
});
|
|
157
176
|
}
|
|
158
177
|
|
|
159
|
-
// Drive native popover state from `
|
|
178
|
+
// Drive native popover state from `open`. `popover="manual"` puts the
|
|
160
179
|
// element in the browser top layer when shown, so the tooltip is never
|
|
161
180
|
// clipped by ancestor `overflow` (e.g. Toolbar). We deliberately avoid
|
|
162
181
|
// `popover="auto"` here — auto would dismiss the tooltip on any outside
|
|
@@ -170,13 +189,13 @@
|
|
|
170
189
|
$effect(() => {
|
|
171
190
|
if (!triggerElement || !tooltipElement) return;
|
|
172
191
|
|
|
173
|
-
const shouldShow = !disabled &&
|
|
192
|
+
const shouldShow = !disabled && open && !!label;
|
|
174
193
|
|
|
175
194
|
if (!shouldShow) {
|
|
176
|
-
// Force-clear `
|
|
195
|
+
// Force-clear `open` so a stale show timeout that fired right
|
|
177
196
|
// before `disabled` flipped doesn't immediately re-trigger this
|
|
178
197
|
// effect into the show branch on the next mouse event.
|
|
179
|
-
if (disabled &&
|
|
198
|
+
if (disabled && open) setOpen(false);
|
|
180
199
|
cleanup?.();
|
|
181
200
|
cleanup = undefined;
|
|
182
201
|
if (tooltipElement.matches(':popover-open')) {
|
|
@@ -219,15 +238,22 @@
|
|
|
219
238
|
</script>
|
|
220
239
|
|
|
221
240
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
241
|
+
<!--
|
|
242
|
+
focusin/focusout (not focus/blur): the wrapper span is never focusable
|
|
243
|
+
itself — the actual trigger is the consumer's child element. focus/blur
|
|
244
|
+
don't bubble, so a keyboard focus on the child never reached the wrapper
|
|
245
|
+
and the tooltip silently skipped its WCAG 1.4.13 focus-open path;
|
|
246
|
+
focusin/focusout bubble and cover any focusable descendant.
|
|
247
|
+
-->
|
|
222
248
|
<span
|
|
223
249
|
class="inline-flex"
|
|
224
250
|
bind:this={triggerElement}
|
|
225
251
|
onmouseenter={show}
|
|
226
252
|
onmouseleave={hide}
|
|
227
|
-
|
|
228
|
-
|
|
253
|
+
onfocusin={show}
|
|
254
|
+
onfocusout={hide}
|
|
229
255
|
onkeydown={handleKeydown}
|
|
230
|
-
aria-describedby={!disabled && label &&
|
|
256
|
+
aria-describedby={!disabled && label && open ? tooltipId : undefined}
|
|
231
257
|
>
|
|
232
258
|
{@render children()}
|
|
233
259
|
</span>
|
|
@@ -239,7 +265,7 @@
|
|
|
239
265
|
the arrow node existed). In top-layer mode the `popover="manual"` attribute
|
|
240
266
|
hides it via the UA stylesheet until `showPopover()` is called; inside a
|
|
241
267
|
modal dialog (`topLayer === false`) the attribute is dropped and the
|
|
242
|
-
`
|
|
268
|
+
`open` opacity variant drives visibility instead (Codeberg #23).
|
|
243
269
|
|
|
244
270
|
Load-bearing attributes (`popover`, `style`, `role`, `id`) intentionally
|
|
245
271
|
follow `{...restProps}` so a consumer-supplied `popover="auto"`, custom
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { TooltipProps } from './index.js';
|
|
2
|
-
declare const Tooltip: import("svelte").Component<TooltipProps, {}, "">;
|
|
2
|
+
declare const Tooltip: import("svelte").Component<TooltipProps, {}, "open">;
|
|
3
3
|
type Tooltip = ReturnType<typeof Tooltip>;
|
|
4
4
|
export default Tooltip;
|
|
@@ -4,7 +4,7 @@ import type { Placement } from '../../utils/floating.js';
|
|
|
4
4
|
import type { TooltipSlots, TooltipVariants } from './tooltip.variants.js';
|
|
5
5
|
/**
|
|
6
6
|
* @description Contextual overlay that displays brief, supplementary text on hover or focus.
|
|
7
|
-
* Built
|
|
7
|
+
* Built on the library's own positioning engine for precise placement and accessibility support.
|
|
8
8
|
*
|
|
9
9
|
* @tag display
|
|
10
10
|
* @related Popover
|
|
@@ -22,6 +22,22 @@ import type { TooltipSlots, TooltipVariants } from './tooltip.variants.js';
|
|
|
22
22
|
* <Button variant="ghost" size="sm" onclick={handleCopy}>Copy</Button>
|
|
23
23
|
* </Tooltip>
|
|
24
24
|
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example Controlled mode — programmatic "Copied!" feedback via `bind:open`
|
|
27
|
+
* ```svelte
|
|
28
|
+
* <script lang="ts">
|
|
29
|
+
* let copied = $state(false);
|
|
30
|
+
* async function copy() {
|
|
31
|
+
* await navigator.clipboard.writeText(text);
|
|
32
|
+
* copied = true;
|
|
33
|
+
* setTimeout(() => (copied = false), 1500);
|
|
34
|
+
* }
|
|
35
|
+
* </script>
|
|
36
|
+
*
|
|
37
|
+
* <Tooltip label="Copied!" bind:open={copied}>
|
|
38
|
+
* <Button onclick={copy}>Copy</Button>
|
|
39
|
+
* </Tooltip>
|
|
40
|
+
* ```
|
|
25
41
|
*/
|
|
26
42
|
export interface TooltipProps extends TooltipVariants, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
27
43
|
/** Text displayed inside the tooltip bubble. */
|
|
@@ -52,8 +68,20 @@ export interface TooltipProps extends TooltipVariants, Omit<HTMLAttributes<HTMLD
|
|
|
52
68
|
arrow?: boolean;
|
|
53
69
|
/** Prevent the tooltip from appearing regardless of hover/focus. @default false */
|
|
54
70
|
disabled?: boolean;
|
|
55
|
-
/**
|
|
56
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Controls tooltip visibility. Supports `bind:open` for programmatic
|
|
73
|
+
* display (e.g. a transient "Copied!" confirmation). Hover/focus keep
|
|
74
|
+
* driving the state in parallel — the delays apply only to those
|
|
75
|
+
* interaction paths, a `bind:open` write takes effect immediately.
|
|
76
|
+
* @default false
|
|
77
|
+
*/
|
|
78
|
+
open?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Fires when the tooltip opens or closes from user interaction (hover
|
|
81
|
+
* in/out, focus/blur, Escape). Receives the new open state. Not called
|
|
82
|
+
* when the consumer writes `bind:open` directly.
|
|
83
|
+
*/
|
|
84
|
+
onOpenChange?: (open: boolean) => void;
|
|
57
85
|
/** Remove default tv classes; only consumer classes apply. */
|
|
58
86
|
unstyled?: boolean;
|
|
59
87
|
/** Per-slot class overrides merged with (or replacing, when unstyled) tv styles. */
|