@urbicon-ui/blocks 6.21.1 → 6.21.2
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/Badge/Badge.svelte +12 -4
- 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/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/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/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 +10 -0
- 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/Slider/Slider.svelte +4 -8
- package/dist/primitives/Textarea/Textarea.svelte +4 -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/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/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/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
|
@@ -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. */
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { type SlotNames, type VariantProps } from '../../utils/variants.js';
|
|
2
2
|
export declare const tooltipVariants: ((props?: {
|
|
3
|
-
|
|
3
|
+
open?: boolean | undefined;
|
|
4
4
|
intent?: "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
|
|
5
5
|
size?: "sm" | "md" | "lg" | undefined;
|
|
6
6
|
} | undefined) => {
|
|
7
7
|
base: (props?: ({
|
|
8
|
-
|
|
8
|
+
open?: boolean | undefined;
|
|
9
9
|
intent?: "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
|
|
10
10
|
size?: "sm" | "md" | "lg" | undefined;
|
|
11
11
|
} & {
|
|
12
12
|
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;
|
|
13
13
|
}) | undefined) => string;
|
|
14
14
|
arrow: (props?: ({
|
|
15
|
-
|
|
15
|
+
open?: boolean | undefined;
|
|
16
16
|
intent?: "primary" | "secondary" | "info" | "success" | "warning" | "danger" | "neutral" | undefined;
|
|
17
17
|
size?: "sm" | "md" | "lg" | undefined;
|
|
18
18
|
} & {
|
|
@@ -21,6 +21,6 @@ export declare const tooltipVariants: ((props?: {
|
|
|
21
21
|
}) & {
|
|
22
22
|
readonly config: import("../../utils/variants.js").TVConfig;
|
|
23
23
|
};
|
|
24
|
-
export type TooltipVariants = Omit<VariantProps<typeof tooltipVariants>, '
|
|
24
|
+
export type TooltipVariants = Omit<VariantProps<typeof tooltipVariants>, 'open'>;
|
|
25
25
|
/** Slot names derived from the `tv()` config above — single source of truth for `slotClasses`. */
|
|
26
26
|
export type TooltipSlots = SlotNames<typeof tooltipVariants>;
|
|
@@ -33,7 +33,7 @@ export const tooltipVariants = tv({
|
|
|
33
33
|
arrow: ['absolute w-2 h-2', 'bg-inherit transform rotate-45']
|
|
34
34
|
},
|
|
35
35
|
variants: {
|
|
36
|
-
|
|
36
|
+
open: {
|
|
37
37
|
true: { base: 'opacity-100' },
|
|
38
38
|
false: { base: 'opacity-0' }
|
|
39
39
|
},
|
|
@@ -53,7 +53,7 @@ export const tooltipVariants = tv({
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
defaultVariants: {
|
|
56
|
-
|
|
56
|
+
open: false,
|
|
57
57
|
intent: 'neutral',
|
|
58
58
|
size: 'md'
|
|
59
59
|
}
|
|
@@ -18,7 +18,7 @@ export type { CheckboxProps } from './Checkbox/index.js';
|
|
|
18
18
|
export * from './Checkbox/index.js';
|
|
19
19
|
export type { CollapsibleProps } from './Collapsible/index.js';
|
|
20
20
|
export * from './Collapsible/index.js';
|
|
21
|
-
export type { ComboboxOptionType, ComboboxProps } from './Combobox/index.js';
|
|
21
|
+
export type { ComboboxGroup, ComboboxOptionType, ComboboxProps } from './Combobox/index.js';
|
|
22
22
|
export * from './Combobox/index.js';
|
|
23
23
|
export type { ConfirmDialogProps, ConfirmIntent } from './ConfirmDialog/index.js';
|
|
24
24
|
export * from './ConfirmDialog/index.js';
|
|
@@ -34,7 +34,7 @@ export type { JourneyNode, JourneyStatus, JourneyTimelineProps } from './Journey
|
|
|
34
34
|
export * from './JourneyTimeline/index.js';
|
|
35
35
|
export type { MenuContext, MenuCustomSlots, MenuItemType, MenuObjectOption, MenuOption, MenuProps, MenuSectionHeader } from './Menu/index.js';
|
|
36
36
|
export * from './Menu/index.js';
|
|
37
|
-
export type { PaginationItemProps, PaginationProps } from './Pagination/index.js';
|
|
37
|
+
export type { PaginationItemContext, PaginationItemProps, PaginationProps } from './Pagination/index.js';
|
|
38
38
|
export * from './Pagination/index.js';
|
|
39
39
|
export * from './Popover/index.js';
|
|
40
40
|
export type { ProgressProps } from './Progress/index.js';
|
|
@@ -61,7 +61,7 @@ export type { TabItemProps, TabPanelProps, TabProps } from './Tab/index.js';
|
|
|
61
61
|
export * from './Tab/index.js';
|
|
62
62
|
export type { TextareaProps } from './Textarea/index.js';
|
|
63
63
|
export * from './Textarea/index.js';
|
|
64
|
-
export type { ToastData, ToastInput, ToastIntent, ToastPlacement, ToastProps, ToastShorthandOpts } from './Toast/index.js';
|
|
64
|
+
export type { ToastAction, ToastData, ToastInput, ToastIntent, ToastPlacement, ToastPromiseOptions, ToastProps, ToastShorthandOpts } from './Toast/index.js';
|
|
65
65
|
export * from './Toast/index.js';
|
|
66
66
|
export { TOAST_INTENTS } from './Toast/index.js';
|
|
67
67
|
export type { ToggleProps } from './Toggle/index.js';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
2
|
readonly accessibility: {
|
|
3
3
|
readonly avatar: "Avatar";
|
|
4
|
+
readonly breadcrumb: "Pfadnavigation";
|
|
5
|
+
readonly breadcrumbExpand: "Alle Pfadebenen anzeigen";
|
|
4
6
|
readonly clearInput: "Eingabe löschen";
|
|
5
7
|
readonly clearSearch: "Suche löschen";
|
|
6
8
|
readonly clearSelection: "Auswahl löschen";
|
|
@@ -19,6 +21,7 @@ declare const _default: {
|
|
|
19
21
|
readonly removableBadge: "Entfernbarer Badge";
|
|
20
22
|
readonly removeBadge: "Badge entfernen";
|
|
21
23
|
readonly removeFile: "{{name}} entfernen";
|
|
24
|
+
readonly removeTag: "{{label}} entfernen";
|
|
22
25
|
};
|
|
23
26
|
readonly button: {
|
|
24
27
|
readonly close: "Schließen";
|
package/dist/translations/de.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
accessibility: {
|
|
3
3
|
avatar: 'Avatar',
|
|
4
|
+
breadcrumb: 'Pfadnavigation',
|
|
5
|
+
breadcrumbExpand: 'Alle Pfadebenen anzeigen',
|
|
4
6
|
clearInput: 'Eingabe löschen',
|
|
5
7
|
clearSearch: 'Suche löschen',
|
|
6
8
|
clearSelection: 'Auswahl löschen',
|
|
@@ -18,7 +20,8 @@ export default {
|
|
|
18
20
|
toggleOptions: 'Optionen umschalten',
|
|
19
21
|
removableBadge: 'Entfernbarer Badge',
|
|
20
22
|
removeBadge: 'Badge entfernen',
|
|
21
|
-
removeFile: '{{name}} entfernen'
|
|
23
|
+
removeFile: '{{name}} entfernen',
|
|
24
|
+
removeTag: '{{label}} entfernen'
|
|
22
25
|
},
|
|
23
26
|
button: {
|
|
24
27
|
close: 'Schließen',
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
2
|
readonly accessibility: {
|
|
3
3
|
readonly avatar: "Avatar";
|
|
4
|
+
readonly breadcrumb: "Breadcrumb";
|
|
5
|
+
readonly breadcrumbExpand: "Show all breadcrumb items";
|
|
4
6
|
readonly clearInput: "Clear input";
|
|
5
7
|
readonly clearSearch: "Clear search";
|
|
6
8
|
readonly clearSelection: "Clear selection";
|
|
@@ -19,6 +21,7 @@ declare const _default: {
|
|
|
19
21
|
readonly removableBadge: "Removable badge";
|
|
20
22
|
readonly removeBadge: "Remove badge";
|
|
21
23
|
readonly removeFile: "Remove {{name}}";
|
|
24
|
+
readonly removeTag: "Remove {{label}}";
|
|
22
25
|
};
|
|
23
26
|
readonly button: {
|
|
24
27
|
readonly close: "Close";
|
package/dist/translations/en.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
accessibility: {
|
|
3
3
|
avatar: 'Avatar',
|
|
4
|
+
breadcrumb: 'Breadcrumb',
|
|
5
|
+
breadcrumbExpand: 'Show all breadcrumb items',
|
|
4
6
|
clearInput: 'Clear input',
|
|
5
7
|
clearSearch: 'Clear search',
|
|
6
8
|
clearSelection: 'Clear selection',
|
|
@@ -18,7 +20,8 @@ export default {
|
|
|
18
20
|
toggleOptions: 'Toggle options',
|
|
19
21
|
removableBadge: 'Removable badge',
|
|
20
22
|
removeBadge: 'Remove badge',
|
|
21
|
-
removeFile: 'Remove {{name}}'
|
|
23
|
+
removeFile: 'Remove {{name}}',
|
|
24
|
+
removeTag: 'Remove {{label}}'
|
|
22
25
|
},
|
|
23
26
|
button: {
|
|
24
27
|
close: 'Close',
|
|
@@ -103,8 +103,14 @@ export interface GuideTour {
|
|
|
103
103
|
* consumer can inject a server-state adapter. Kept deliberately tiny.
|
|
104
104
|
*/
|
|
105
105
|
export interface GuideStorageAdapter {
|
|
106
|
-
/**
|
|
107
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Returns the persisted set of seen/completed ids. Return a `string[]`
|
|
108
|
+
* synchronously (localStorage, the default) or a `Promise<string[]>` for a
|
|
109
|
+
* DB-/remote-backed store. While an async load is pending the seen set starts
|
|
110
|
+
* empty and is merged (union) with the resolved ids once they arrive — so a
|
|
111
|
+
* tour may briefly be startable before the store answers.
|
|
112
|
+
*/
|
|
113
|
+
load(): string[] | Promise<string[]>;
|
|
108
114
|
/** Persists the full set of seen/completed ids. */
|
|
109
115
|
save(ids: string[]): void;
|
|
110
116
|
}
|
|
@@ -156,6 +156,22 @@ export class GuideController {
|
|
|
156
156
|
#targets = new SvelteMap();
|
|
157
157
|
/** Persisted "seen" ids. */
|
|
158
158
|
#seen;
|
|
159
|
+
/** False only while an async `storage.load()` is still pending. */
|
|
160
|
+
#seenLoaded = true;
|
|
161
|
+
/**
|
|
162
|
+
* Set when the user writes (markSeen/resetSeen) while an async load is still
|
|
163
|
+
* pending, so the load's `.then` persists the merged union once — writing
|
|
164
|
+
* mid-load directly would clobber the remote store before its ids are merged in.
|
|
165
|
+
*/
|
|
166
|
+
#pendingSeenWrite = false;
|
|
167
|
+
/**
|
|
168
|
+
* Ids the user forgot during the load window, so the merge doesn't resurrect
|
|
169
|
+
* them from the freshly-loaded remote set. A plain `Set` — pure bookkeeping,
|
|
170
|
+
* never rendered, so the `SvelteSet` rule doesn't apply.
|
|
171
|
+
*/
|
|
172
|
+
#pendingRemovals = new Set();
|
|
173
|
+
/** `resetSeen()` (clear-all) during the load window: drop the remote set entirely. */
|
|
174
|
+
#pendingClearAll = false;
|
|
159
175
|
#storage;
|
|
160
176
|
#overlayStack;
|
|
161
177
|
#dev;
|
|
@@ -209,7 +225,58 @@ export class GuideController {
|
|
|
209
225
|
// `?? false` keeps `#dev` a strict boolean and means non-Vite consumers
|
|
210
226
|
// simply get no dev warnings (rather than a crash).
|
|
211
227
|
this.#dev = options.dev ?? import.meta.env?.DEV ?? false;
|
|
212
|
-
|
|
228
|
+
// Sync adapters (localStorage) seed the set immediately. Async adapters
|
|
229
|
+
// (DB/remote) start empty and get their ids merged in when the promise
|
|
230
|
+
// settles — union, not replace, so anything marked seen in the meantime
|
|
231
|
+
// survives. A rejected load is swallowed (dev-warned): the tour just stays
|
|
232
|
+
// "unseen", which fails open rather than hiding help behind a broken store.
|
|
233
|
+
const loaded = this.#storage.load();
|
|
234
|
+
if (Array.isArray(loaded)) {
|
|
235
|
+
this.#seen = new SvelteSet(loaded);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
this.#seen = new SvelteSet();
|
|
239
|
+
this.#seenLoaded = false;
|
|
240
|
+
loaded
|
|
241
|
+
.then((ids) => {
|
|
242
|
+
// Merge the loaded set in, but honour writes made during the window:
|
|
243
|
+
// skip everything if the user cleared all, and don't resurrect ids the
|
|
244
|
+
// user explicitly forgot. Adds are safe (union is idempotent).
|
|
245
|
+
if (!this.#pendingClearAll) {
|
|
246
|
+
for (const id of ids) {
|
|
247
|
+
if (!this.#pendingRemovals.has(id))
|
|
248
|
+
this.#seen.add(id);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
this.#seenLoaded = true;
|
|
252
|
+
// Reconcile persistence: if the user wrote during the window, the store
|
|
253
|
+
// still holds the pre-merge state (we deliberately didn't save then), so
|
|
254
|
+
// persist the union now. Nothing wrote → leave the store untouched (no
|
|
255
|
+
// write-amplification of a remote we just read).
|
|
256
|
+
if (this.#pendingSeenWrite)
|
|
257
|
+
this.#storage.save([...this.#seen]);
|
|
258
|
+
})
|
|
259
|
+
.catch((err) => {
|
|
260
|
+
// Read failed: fail open (tour stays "unseen"). We don't persist the
|
|
261
|
+
// window's in-memory writes here — the store is in an unknown state, and
|
|
262
|
+
// blindly saving could destroy data we simply couldn't read ("read
|
|
263
|
+
// tolerant, write strict"). Later writes persist normally.
|
|
264
|
+
this.#seenLoaded = true;
|
|
265
|
+
if (this.#dev)
|
|
266
|
+
console.warn('[Guide] storage.load() rejected:', err);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Persist the seen-set — unless an async load is still pending, in which case a
|
|
272
|
+
* direct save would clobber the not-yet-loaded remote store. Defer to the load's
|
|
273
|
+
* `.then` reconciliation instead (see the constructor).
|
|
274
|
+
*/
|
|
275
|
+
#persistSeen() {
|
|
276
|
+
if (this.#seenLoaded)
|
|
277
|
+
this.#storage.save([...this.#seen]);
|
|
278
|
+
else
|
|
279
|
+
this.#pendingSeenWrite = true;
|
|
213
280
|
}
|
|
214
281
|
// ─── Target registry ──────────────────────────────────────────────────────
|
|
215
282
|
/**
|
|
@@ -738,15 +805,29 @@ export class GuideController {
|
|
|
738
805
|
if (this.#seen.has(id))
|
|
739
806
|
return;
|
|
740
807
|
this.#seen.add(id);
|
|
741
|
-
|
|
808
|
+
// Re-seeing an id undoes a forget that's still pending reconciliation.
|
|
809
|
+
if (!this.#seenLoaded)
|
|
810
|
+
this.#pendingRemovals.delete(id);
|
|
811
|
+
this.#persistSeen();
|
|
742
812
|
}
|
|
743
813
|
/** Forget one id, or all of them when called without an argument. */
|
|
744
814
|
resetSeen(id) {
|
|
745
|
-
if (id === undefined)
|
|
815
|
+
if (id === undefined) {
|
|
746
816
|
this.#seen.clear();
|
|
747
|
-
|
|
817
|
+
// Clear-all during a pending load must survive the merge: drop the whole
|
|
818
|
+
// remote set, superseding any earlier pending removals.
|
|
819
|
+
if (!this.#seenLoaded) {
|
|
820
|
+
this.#pendingClearAll = true;
|
|
821
|
+
this.#pendingRemovals.clear();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
748
825
|
this.#seen.delete(id);
|
|
749
|
-
|
|
826
|
+
// Record the forget so the pending-load merge doesn't resurrect it.
|
|
827
|
+
if (!this.#seenLoaded)
|
|
828
|
+
this.#pendingRemovals.add(id);
|
|
829
|
+
}
|
|
830
|
+
this.#persistSeen();
|
|
750
831
|
}
|
|
751
832
|
/** Snapshot of all seen ids. */
|
|
752
833
|
get seenIds() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `useFormField` — Single source of truth for the ARIA wiring that every
|
|
3
|
-
* Form primitive duplicates: error/
|
|
4
|
-
* `aria-invalid`, and the mutually-exclusive error-over-
|
|
3
|
+
* Form primitive duplicates: error/helper id derivation, `aria-describedby`,
|
|
4
|
+
* `aria-invalid`, and the mutually-exclusive error-over-helper convention.
|
|
5
5
|
*
|
|
6
|
-
* Layout (label position, error/
|
|
6
|
+
* Layout (label position, error/helper placement, required marker) stays
|
|
7
7
|
* component-specific because real Form primitives differ in shape — Input
|
|
8
8
|
* stacks the label on top, Checkbox/Toggle put it inline, RadioGroup and
|
|
9
9
|
* SegmentGroup have a group caption + per-item labels. The hook does not
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
* ```svelte
|
|
24
24
|
* <script lang="ts">
|
|
25
25
|
* import { useFormField } from './index.js';
|
|
26
|
-
* let { id: idProp,
|
|
26
|
+
* let { id: idProp, helper, error, required, disabled, label } = $props();
|
|
27
27
|
* const propsId = $props.id();
|
|
28
28
|
* const ff = useFormField(() => ({
|
|
29
29
|
* fieldId: idProp ?? `field-${propsId}`,
|
|
30
|
-
*
|
|
30
|
+
* helper,
|
|
31
31
|
* error,
|
|
32
32
|
* required,
|
|
33
33
|
* disabled
|
|
@@ -47,8 +47,8 @@ export interface UseFormFieldInputs {
|
|
|
47
47
|
/** The DOM id to apply to the field element. Compute once in the caller via `$props.id()`. */
|
|
48
48
|
fieldId: string;
|
|
49
49
|
/** Helper text shown below the field when no error is set. */
|
|
50
|
-
|
|
51
|
-
/** Error message — when truthy, replaces `
|
|
50
|
+
helper?: string;
|
|
51
|
+
/** Error message — when truthy, replaces `helper` and flags the field invalid. */
|
|
52
52
|
error?: string;
|
|
53
53
|
/** Required flag — exposed verbatim for the caller's label markup. */
|
|
54
54
|
required?: boolean;
|
|
@@ -60,8 +60,8 @@ export interface UseFormFieldReturn {
|
|
|
60
60
|
readonly fieldId: string;
|
|
61
61
|
/** Id of the error description, or undefined when no error is set. */
|
|
62
62
|
readonly errorId: string | undefined;
|
|
63
|
-
/** Id of the
|
|
64
|
-
readonly
|
|
63
|
+
/** Id of the helper description, or undefined when error is set or no helper is given. */
|
|
64
|
+
readonly helperId: string | undefined;
|
|
65
65
|
/** Concatenation suitable for `aria-describedby` (`undefined` when both are unset). */
|
|
66
66
|
readonly describedBy: string | undefined;
|
|
67
67
|
/** `true` iff `error` is truthy. Use for `aria-invalid={ff.invalid || undefined}`. */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `useFormField` — Single source of truth for the ARIA wiring that every
|
|
3
|
-
* Form primitive duplicates: error/
|
|
4
|
-
* `aria-invalid`, and the mutually-exclusive error-over-
|
|
3
|
+
* Form primitive duplicates: error/helper id derivation, `aria-describedby`,
|
|
4
|
+
* `aria-invalid`, and the mutually-exclusive error-over-helper convention.
|
|
5
5
|
*
|
|
6
|
-
* Layout (label position, error/
|
|
6
|
+
* Layout (label position, error/helper placement, required marker) stays
|
|
7
7
|
* component-specific because real Form primitives differ in shape — Input
|
|
8
8
|
* stacks the label on top, Checkbox/Toggle put it inline, RadioGroup and
|
|
9
9
|
* SegmentGroup have a group caption + per-item labels. The hook does not
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
* ```svelte
|
|
24
24
|
* <script lang="ts">
|
|
25
25
|
* import { useFormField } from './index.js';
|
|
26
|
-
* let { id: idProp,
|
|
26
|
+
* let { id: idProp, helper, error, required, disabled, label } = $props();
|
|
27
27
|
* const propsId = $props.id();
|
|
28
28
|
* const ff = useFormField(() => ({
|
|
29
29
|
* fieldId: idProp ?? `field-${propsId}`,
|
|
30
|
-
*
|
|
30
|
+
* helper,
|
|
31
31
|
* error,
|
|
32
32
|
* required,
|
|
33
33
|
* disabled
|
|
@@ -51,20 +51,20 @@
|
|
|
51
51
|
export function computeFormFieldAria(input) {
|
|
52
52
|
const fieldId = input.fieldId;
|
|
53
53
|
const errorId = input.error ? `${fieldId}-error` : undefined;
|
|
54
|
-
//
|
|
54
|
+
// Helper is suppressed when an error is present — the error message is
|
|
55
55
|
// the more important description at that point. This is the same
|
|
56
56
|
// exclusive convention as Material / Carbon / Polaris.
|
|
57
|
-
const
|
|
58
|
-
// Error first,
|
|
57
|
+
const helperId = !input.error && input.helper ? `${fieldId}-helper` : undefined;
|
|
58
|
+
// Error first, helper second — error is the more semantically urgent
|
|
59
59
|
// description and should reach assistive tech first.
|
|
60
|
-
const describedBy = [errorId,
|
|
60
|
+
const describedBy = [errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
61
61
|
const invalid = !!input.error;
|
|
62
62
|
const required = !!input.required;
|
|
63
63
|
const disabled = !!input.disabled;
|
|
64
64
|
return {
|
|
65
65
|
fieldId,
|
|
66
66
|
errorId,
|
|
67
|
-
|
|
67
|
+
helperId,
|
|
68
68
|
describedBy,
|
|
69
69
|
invalid,
|
|
70
70
|
required,
|
|
@@ -80,8 +80,8 @@ export function useFormField(inputs) {
|
|
|
80
80
|
get errorId() {
|
|
81
81
|
return result.errorId;
|
|
82
82
|
},
|
|
83
|
-
get
|
|
84
|
-
return result.
|
|
83
|
+
get helperId() {
|
|
84
|
+
return result.helperId;
|
|
85
85
|
},
|
|
86
86
|
get describedBy() {
|
|
87
87
|
return result.describedBy;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urbicon-ui/blocks",
|
|
3
|
-
"version": "6.21.
|
|
3
|
+
"version": "6.21.2",
|
|
4
4
|
"description": "Svelte 5 UI component library with Tailwind CSS 4, OKLCH design tokens and zero runtime dependencies",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
"@sveltejs/package": "^2.5.8",
|
|
92
92
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
93
93
|
"@tailwindcss/vite": "^4.3.1",
|
|
94
|
-
"@urbicon-ui/i18n": "6.21.
|
|
95
|
-
"@urbicon-ui/shared-types": "6.21.
|
|
94
|
+
"@urbicon-ui/i18n": "6.21.2",
|
|
95
|
+
"@urbicon-ui/shared-types": "6.21.2",
|
|
96
96
|
"prettier": "^3.8.4",
|
|
97
97
|
"prettier-plugin-svelte": "^4.1.1",
|
|
98
98
|
"prettier-plugin-tailwindcss": "^0.8.0",
|