@loidolt/theme-svelte 0.5.0 → 0.7.0

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.
Files changed (125) hide show
  1. package/README.md +11 -0
  2. package/dist/announcer.svelte.d.ts +36 -0
  3. package/dist/announcer.svelte.js +73 -0
  4. package/dist/attachments.d.ts +83 -0
  5. package/dist/attachments.js +211 -0
  6. package/dist/components/ActiveFilterChips.svelte +80 -0
  7. package/dist/components/ActiveFilterChips.svelte.d.ts +41 -0
  8. package/dist/components/AspectRatio.svelte +32 -0
  9. package/dist/components/AspectRatio.svelte.d.ts +13 -0
  10. package/dist/components/AudioPlayer.svelte +94 -0
  11. package/dist/components/AudioPlayer.svelte.d.ts +24 -0
  12. package/dist/components/Avatar.svelte +85 -0
  13. package/dist/components/Avatar.svelte.d.ts +31 -0
  14. package/dist/components/Badge.svelte +28 -2
  15. package/dist/components/Badge.svelte.d.ts +13 -1
  16. package/dist/components/Combobox.svelte +180 -0
  17. package/dist/components/Combobox.svelte.d.ts +77 -0
  18. package/dist/components/DataTable.svelte +228 -0
  19. package/dist/components/DataTable.svelte.d.ts +62 -0
  20. package/dist/components/DataTableColumnVisibility.svelte +46 -0
  21. package/dist/components/DataTableColumnVisibility.svelte.d.ts +31 -0
  22. package/dist/components/DataTableFacetedFilter.svelte +87 -0
  23. package/dist/components/DataTableFacetedFilter.svelte.d.ts +40 -0
  24. package/dist/components/DataTablePagination.svelte +68 -0
  25. package/dist/components/DataTablePagination.svelte.d.ts +42 -0
  26. package/dist/components/DataTableSearch.svelte +67 -0
  27. package/dist/components/DataTableSearch.svelte.d.ts +37 -0
  28. package/dist/components/Dialog.svelte +32 -11
  29. package/dist/components/Dialog.svelte.d.ts +7 -0
  30. package/dist/components/DropdownMenu.svelte +95 -28
  31. package/dist/components/DropdownMenu.svelte.d.ts +12 -3
  32. package/dist/components/Field.svelte +2 -3
  33. package/dist/components/Fieldset.svelte +2 -3
  34. package/dist/components/Filmstrip.svelte +10 -3
  35. package/dist/components/FilterPanel.svelte +62 -0
  36. package/dist/components/FilterPanel.svelte.d.ts +22 -0
  37. package/dist/components/Lightbox.svelte +239 -0
  38. package/dist/components/Lightbox.svelte.d.ts +40 -0
  39. package/dist/components/LiveRegion.svelte +48 -0
  40. package/dist/components/LiveRegion.svelte.d.ts +20 -0
  41. package/dist/components/MediaCarousel.svelte +177 -0
  42. package/dist/components/MediaCarousel.svelte.d.ts +32 -0
  43. package/dist/components/MediaEmbed.svelte +116 -0
  44. package/dist/components/MediaEmbed.svelte.d.ts +34 -0
  45. package/dist/components/MediaGrid.svelte +91 -0
  46. package/dist/components/MediaGrid.svelte.d.ts +24 -0
  47. package/dist/components/MultiSelect.svelte +232 -0
  48. package/dist/components/MultiSelect.svelte.d.ts +67 -0
  49. package/dist/components/NumberField.svelte +53 -25
  50. package/dist/components/NumberField.svelte.d.ts +47 -18
  51. package/dist/components/OTPInput.svelte +94 -0
  52. package/dist/components/OTPInput.svelte.d.ts +26 -0
  53. package/dist/components/PasswordInput.svelte +93 -0
  54. package/dist/components/PasswordInput.svelte.d.ts +24 -0
  55. package/dist/components/Progress.svelte +57 -14
  56. package/dist/components/Progress.svelte.d.ts +13 -0
  57. package/dist/components/RadioGroup.svelte +19 -5
  58. package/dist/components/RadioGroup.svelte.d.ts +3 -2
  59. package/dist/components/Select.svelte +15 -5
  60. package/dist/components/Select.svelte.d.ts +3 -2
  61. package/dist/components/SignaturePad.svelte +317 -0
  62. package/dist/components/SignaturePad.svelte.d.ts +48 -0
  63. package/dist/components/Skeleton.svelte +40 -9
  64. package/dist/components/Skeleton.svelte.d.ts +8 -0
  65. package/dist/components/SkipLink.svelte +48 -0
  66. package/dist/components/SkipLink.svelte.d.ts +14 -0
  67. package/dist/components/Slider.svelte +148 -0
  68. package/dist/components/Slider.svelte.d.ts +55 -0
  69. package/dist/components/Spinner.svelte +11 -2
  70. package/dist/components/Spinner.svelte.d.ts +3 -0
  71. package/dist/components/Tabs.svelte.d.ts +1 -1
  72. package/dist/components/Thumbnail.svelte +52 -6
  73. package/dist/components/Thumbnail.svelte.d.ts +14 -2
  74. package/dist/components/Toast.svelte +24 -0
  75. package/dist/components/Toast.svelte.d.ts +10 -0
  76. package/dist/components/ToastViewport.svelte +9 -1
  77. package/dist/components/ToastViewport.svelte.d.ts +2 -0
  78. package/dist/components/Toolbar.svelte +49 -0
  79. package/dist/components/Toolbar.svelte.d.ts +23 -0
  80. package/dist/components/Tooltip.svelte +9 -1
  81. package/dist/components/Tooltip.svelte.d.ts +2 -0
  82. package/dist/components/VideoPlayer.svelte +136 -0
  83. package/dist/components/VideoPlayer.svelte.d.ts +38 -0
  84. package/dist/data-table-utils.d.ts +62 -0
  85. package/dist/data-table-utils.js +91 -0
  86. package/dist/data-table.svelte.d.ts +132 -0
  87. package/dist/data-table.svelte.js +292 -0
  88. package/dist/hls.d.ts +35 -0
  89. package/dist/hls.js +37 -0
  90. package/dist/hls.svelte.d.ts +22 -0
  91. package/dist/hls.svelte.js +66 -0
  92. package/dist/index.d.ts +49 -4
  93. package/dist/index.js +36 -2
  94. package/dist/internal/EditableCell.svelte +58 -0
  95. package/dist/internal/EditableCell.svelte.d.ts +8 -0
  96. package/dist/internal/ListboxEntries.svelte +49 -0
  97. package/dist/internal/ListboxEntries.svelte.d.ts +35 -0
  98. package/dist/internal/media/MediaControls.svelte +163 -0
  99. package/dist/internal/media/MediaControls.svelte.d.ts +12 -0
  100. package/dist/internal/media/MediaStage.svelte +49 -0
  101. package/dist/internal/media/MediaStage.svelte.d.ts +11 -0
  102. package/dist/internal/media/MediaTile.svelte +36 -0
  103. package/dist/internal/media/MediaTile.svelte.d.ts +11 -0
  104. package/dist/internal/media/keys.d.ts +7 -0
  105. package/dist/internal/media/keys.js +51 -0
  106. package/dist/internal/media/labels.d.ts +19 -0
  107. package/dist/internal/media/labels.js +15 -0
  108. package/dist/internal/options.d.ts +9 -0
  109. package/dist/internal/options.js +20 -0
  110. package/dist/media-player.svelte.d.ts +61 -0
  111. package/dist/media-player.svelte.js +246 -0
  112. package/dist/media-utils.d.ts +112 -0
  113. package/dist/media-utils.js +205 -0
  114. package/dist/media-zoom.svelte.d.ts +33 -0
  115. package/dist/media-zoom.svelte.js +126 -0
  116. package/dist/signature.d.ts +55 -0
  117. package/dist/signature.js +52 -0
  118. package/dist/toaster.svelte.d.ts +24 -0
  119. package/dist/toaster.svelte.js +23 -0
  120. package/dist/token-colors.svelte.d.ts +49 -0
  121. package/dist/token-colors.svelte.js +165 -0
  122. package/dist/types.d.ts +62 -3
  123. package/dist/utils.d.ts +9 -0
  124. package/dist/utils.js +11 -0
  125. package/package.json +10 -4
@@ -0,0 +1,165 @@
1
+ import { onDestroy } from 'svelte';
2
+ import { resolveRoles, roleVar, } from '@loidolt/theme-tokens';
3
+ const hex = (value) => Math.round(Math.min(255, Math.max(0, value)))
4
+ .toString(16)
5
+ .padStart(2, '0');
6
+ /**
7
+ * A computed colour as `#rrggbb`, or `#rrggbbaa` when it is translucent. Accepts what browsers
8
+ * return from `getComputedStyle`: `rgb()`/`rgba()` in either syntax, and `color(srgb …)`, which
9
+ * is what a `color-mix()` token computes to. Anything else is `null`.
10
+ */
11
+ export function normalizeColor(value) {
12
+ const text = value.trim().toLowerCase();
13
+ if (/^#[0-9a-f]{6}([0-9a-f]{2})?$/.test(text))
14
+ return text;
15
+ const rgb = /^rgba?\(([^)]+)\)$/.exec(text);
16
+ const srgb = /^color\(srgb ([^)]+)\)$/.exec(text);
17
+ const parts = (rgb?.[1] ?? srgb?.[1])?.split(/[\s,/]+/).filter(Boolean);
18
+ if (!parts || parts.length < 3)
19
+ return null;
20
+ const numbers = parts.map((part) => part.endsWith('%') ? Number.parseFloat(part) / 100 : Number(part));
21
+ if (numbers.some((number) => !Number.isFinite(number)))
22
+ return null;
23
+ const channels = numbers.slice(0, 3).map((number, index) =>
24
+ // `rgb()` channels are 0–255 unless written as percentages; `color(srgb)` channels are 0–1.
25
+ srgb || parts[index].endsWith('%') ? number * 255 : number);
26
+ const alpha = numbers.length > 3 ? numbers[3] : 1;
27
+ const base = `#${channels.map(hex).join('')}`;
28
+ return alpha >= 1 ? base : `${base}${hex(alpha * 255)}`;
29
+ }
30
+ /**
31
+ * Reads one role's live value from the page, as the element would paint it — so a scoped
32
+ * `[data-theme]` subtree or an app's own overrides are honoured. `null` when the environment
33
+ * cannot say (the server, or a DOM without custom-property support such as jsdom).
34
+ */
35
+ export function readRoleColor(element, role) {
36
+ const view = element.ownerDocument?.defaultView;
37
+ if (!view)
38
+ return null;
39
+ const probe = element.ownerDocument.createElement('span');
40
+ probe.hidden = true;
41
+ probe.style.color = `var(${roleVar(role)})`;
42
+ element.appendChild(probe);
43
+ try {
44
+ return normalizeColor(view.getComputedStyle(probe).color);
45
+ }
46
+ finally {
47
+ probe.remove();
48
+ }
49
+ }
50
+ /**
51
+ * Which theme an element is in: an explicit `data-theme` on it or an ancestor wins, then the
52
+ * computed `color-scheme` (which the dark stylesheets set, including the system-following one).
53
+ */
54
+ export function colorSchemeOf(element) {
55
+ if (!element)
56
+ return 'light';
57
+ const explicit = element.closest('[data-theme]')?.getAttribute('data-theme');
58
+ if (explicit === 'dark' || explicit === 'light')
59
+ return explicit;
60
+ const view = element.ownerDocument?.defaultView;
61
+ const computed = view?.getComputedStyle(element).colorScheme ?? '';
62
+ return /\bdark\b/.test(computed) && !/\blight\b/.test(computed) ? 'dark' : 'light';
63
+ }
64
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- subscriber bookkeeping, never rendered
65
+ const listeners = new Set();
66
+ let teardown;
67
+ /** One set of observers for the whole page, however many charts and maps are listening. */
68
+ function subscribe(listener) {
69
+ listeners.add(listener);
70
+ if (!teardown && typeof document !== 'undefined') {
71
+ let queued = false;
72
+ const notify = () => {
73
+ if (queued)
74
+ return;
75
+ queued = true;
76
+ queueMicrotask(() => {
77
+ queued = false;
78
+ for (const each of [...listeners])
79
+ each();
80
+ });
81
+ };
82
+ const root = document.documentElement;
83
+ const scoped = new MutationObserver(notify);
84
+ // A theme can be scoped to any subtree, so `data-theme` is watched everywhere; `class` and
85
+ // `style` only on the root, where apps toggle theme classes and inline overrides.
86
+ scoped.observe(root, { attributes: true, attributeFilter: ['data-theme'], subtree: true });
87
+ const rootObserver = new MutationObserver(notify);
88
+ rootObserver.observe(root, { attributes: true, attributeFilter: ['class', 'style'] });
89
+ const media = typeof window.matchMedia === 'function'
90
+ ? window.matchMedia('(prefers-color-scheme: dark)')
91
+ : undefined;
92
+ media?.addEventListener('change', notify);
93
+ teardown = () => {
94
+ scoped.disconnect();
95
+ rootObserver.disconnect();
96
+ media?.removeEventListener('change', notify);
97
+ teardown = undefined;
98
+ };
99
+ }
100
+ return () => {
101
+ listeners.delete(listener);
102
+ if (listeners.size === 0)
103
+ teardown?.();
104
+ };
105
+ }
106
+ /**
107
+ * Calls `callback` whenever the page's colour scheme may have changed: a `data-theme` anywhere,
108
+ * a class or inline style on `<html>`, or the system preference. Returns the unsubscribe.
109
+ */
110
+ export function observeColorScheme(callback) {
111
+ return subscribe(callback);
112
+ }
113
+ /**
114
+ * Semantic token values for renderers that cannot read CSS — a chart canvas, a WebGL map. Reads
115
+ * the live values off the page, so an app's own theme reaches the canvas, and follows theme
116
+ * changes. On the server, and wherever values cannot be read, it falls back to the reference
117
+ * light or dark theme.
118
+ *
119
+ * ```ts
120
+ * const palette = createTokenColors({ series: ['chart1', 'chart2'], ink: 'text' }, { element: () => node });
121
+ * $effect(() => chart?.setOption(build(palette.colors)));
122
+ * ```
123
+ */
124
+ export function createTokenColors(spec, options = {}) {
125
+ const target = () => options.element?.() ?? (typeof document === 'undefined' ? undefined : document.documentElement);
126
+ const compute = () => {
127
+ const element = target();
128
+ const scheme = colorSchemeOf(element);
129
+ const read = (role) => (element && readRoleColor(element, role)) || resolveRoles({ role }, scheme).role;
130
+ return { scheme, colors: resolveRoles(spec, scheme, read) };
131
+ };
132
+ const initial = compute();
133
+ let colors = $state.raw(initial.colors);
134
+ let scheme = $state(initial.scheme);
135
+ let version = $state(0);
136
+ const refresh = () => {
137
+ const next = compute();
138
+ scheme = next.scheme;
139
+ if (JSON.stringify(next.colors) === JSON.stringify(colors))
140
+ return;
141
+ colors = next.colors;
142
+ version += 1;
143
+ };
144
+ const unsubscribe = subscribe(refresh);
145
+ const destroy = () => unsubscribe();
146
+ try {
147
+ onDestroy(destroy);
148
+ }
149
+ catch {
150
+ /* module scope: the caller owns `destroy()` */
151
+ }
152
+ return {
153
+ get colors() {
154
+ return colors;
155
+ },
156
+ get scheme() {
157
+ return scheme;
158
+ },
159
+ get version() {
160
+ return version;
161
+ },
162
+ refresh,
163
+ destroy,
164
+ };
165
+ }
package/dist/types.d.ts CHANGED
@@ -5,9 +5,10 @@ export type ActionVariant = 'default' | 'primary' | 'quiet' | 'danger' | 'ghost'
5
5
  export type StatusVariant = 'info' | 'success' | 'warning' | 'error';
6
6
  /** Badge spans both sets: neutral, brand accent, or a status. */
7
7
  export type BadgeVariant = 'default' | 'accent' | StatusVariant;
8
- /** Shared control scale. Dialogs extend it with `xl`. */
8
+ /** Shared control scale. Dialogs extend it with `xl` and `full`. */
9
9
  export type ControlSize = 'sm' | 'md' | 'lg';
10
- export type DialogSize = ControlSize | 'xl';
10
+ /** `full` takes the viewport less a gutter — for editors and previews that need the room. */
11
+ export type DialogSize = ControlSize | 'xl' | 'full';
11
12
  /**
12
13
  * Badges default to `inline`: a compact chip sized to the text it annotates, for table cells and
13
14
  * running copy. The `ControlSize` steps match the button scale so a badge standing in a row of
@@ -28,6 +29,17 @@ export interface Option<T extends string = string> {
28
29
  label: string;
29
30
  disabled?: boolean;
30
31
  }
32
+ /** A labelled run of options: an `<optgroup>` in `Select`, a group heading in list popups. */
33
+ export interface OptionGroup<T extends string = string> {
34
+ label: string;
35
+ options: Option<T>[];
36
+ /** Disables every option in the group. */
37
+ disabled?: boolean;
38
+ }
39
+ /** An option with a secondary line, for choices that need a sentence of explanation. */
40
+ export interface ChoiceOption<T extends string = string> extends Option<T> {
41
+ description?: string;
42
+ }
31
43
  /** `Option` is the single vocabulary for choice lists; `NavItem` is kept as a readable alias. */
32
44
  export type NavItem<T extends string = string> = Option<T>;
33
45
  /** One step in a breadcrumb trail. The final crumb is the current page and takes no `href`. */
@@ -43,14 +55,61 @@ export interface Disclosure<T extends string = string> {
43
55
  }
44
56
  /** A `DropdownMenu` row. Renders as a link when `href` is set. */
45
57
  export interface MenuItem<T extends string = string> extends Option<T> {
58
+ type?: 'item';
46
59
  href?: string;
47
- /** Trailing hint, e.g. a keyboard shortcut. */
60
+ /** Trailing hint. Defaults to `shortcut` when that is set. */
48
61
  hint?: string;
62
+ /**
63
+ * The keyboard shortcut this action also has elsewhere in the app, in `aria-keyshortcuts`
64
+ * syntax (`'Control+D'`). Announced with the item and shown as its hint. The menu does not
65
+ * bind the key — your app does.
66
+ */
67
+ shortcut?: string;
68
+ /** Styles the row as destructive. Pair it with a confirmation for anything irreversible. */
69
+ destructive?: boolean;
49
70
  /** Leading visual. */
50
71
  icon?: Snippet;
51
72
  /** Draw a separator above this item. */
52
73
  separatorBefore?: boolean;
53
74
  }
75
+ /** Rows under an optional heading, announced as one group. */
76
+ export interface MenuGroup<T extends string = string> {
77
+ type: 'group';
78
+ label?: string;
79
+ items: MenuEntry<T>[];
80
+ separatorBefore?: boolean;
81
+ }
82
+ /** A toggle that stays in the menu, e.g. "Show grid". Reported through `onCheckedChange`. */
83
+ export interface MenuCheckboxItem<T extends string = string> {
84
+ type: 'checkbox';
85
+ value: T;
86
+ label: string;
87
+ checked: boolean;
88
+ disabled?: boolean;
89
+ hint?: string;
90
+ separatorBefore?: boolean;
91
+ }
92
+ /** A one-of-many choice inside a menu, e.g. a sort order. Reported through `onValueChange`. */
93
+ export interface MenuRadioGroup<T extends string = string> {
94
+ type: 'radio';
95
+ /** Identifies the group in `onValueChange`. */
96
+ name: string;
97
+ label?: string;
98
+ value: T | '';
99
+ options: Option<T>[];
100
+ separatorBefore?: boolean;
101
+ }
102
+ /** A nested menu that opens beside its row. */
103
+ export interface MenuSub<T extends string = string> {
104
+ type: 'sub';
105
+ label: string;
106
+ icon?: Snippet;
107
+ disabled?: boolean;
108
+ items: MenuEntry<T>[];
109
+ separatorBefore?: boolean;
110
+ }
111
+ /** Anything `DropdownMenu`'s `items` can hold. A plain `MenuItem` needs no `type`. */
112
+ export type MenuEntry<T extends string = string> = MenuItem<T> | MenuGroup<T> | MenuCheckboxItem<T> | MenuRadioGroup<T> | MenuSub<T>;
54
113
  /**
55
114
  * Payload of Bits UI's `child` snippet: spread `props` onto your own element to take over
56
115
  * rendering of a trigger without nesting an extra button inside it.
package/dist/utils.d.ts CHANGED
@@ -3,3 +3,12 @@
3
3
  * Accepts the `class` prop of any component in this package.
4
4
  */
5
5
  export declare function cx(...values: Array<string | false | null | undefined>): string;
6
+ /**
7
+ * Joins the ids of the elements that describe a control into one `aria-describedby` value, or
8
+ * `undefined` when there are none — an empty attribute is still an attribute.
9
+ *
10
+ * ```svelte
11
+ * <input aria-describedby={describedBy(hint && hintId, error && errorId)} />
12
+ * ```
13
+ */
14
+ export declare function describedBy(...ids: Array<string | false | null | undefined>): string | undefined;
package/dist/utils.js CHANGED
@@ -5,3 +5,14 @@
5
5
  export function cx(...values) {
6
6
  return values.filter(Boolean).join(' ');
7
7
  }
8
+ /**
9
+ * Joins the ids of the elements that describe a control into one `aria-describedby` value, or
10
+ * `undefined` when there are none — an empty attribute is still an attribute.
11
+ *
12
+ * ```svelte
13
+ * <input aria-describedby={describedBy(hint && hintId, error && errorId)} />
14
+ * ```
15
+ */
16
+ export function describedBy(...ids) {
17
+ return ids.filter(Boolean).join(' ') || undefined;
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loidolt/theme-svelte",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Accessible Svelte 5 components for Loidolt applications",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,12 +31,18 @@
31
31
  "clean": "rimraf dist"
32
32
  },
33
33
  "dependencies": {
34
- "@loidolt/theme-styles": "^0.5.0",
35
- "@loidolt/theme-tokens": "^0.5.0",
34
+ "@loidolt/theme-styles": "^0.7.0",
35
+ "@loidolt/theme-tokens": "^0.7.0",
36
36
  "bits-ui": "^2.19.0"
37
37
  },
38
38
  "peerDependencies": {
39
- "svelte": "^5.33.0"
39
+ "svelte": "^5.33.0",
40
+ "hls.js": "^1.5.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "hls.js": {
44
+ "optional": true
45
+ }
40
46
  },
41
47
  "devDependencies": {
42
48
  "@axe-core/playwright": "^4.13.0",