zabi-components 5.0.22 → 7.0.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 (207) hide show
  1. package/README.md +139 -1478
  2. package/THEME.md +473 -0
  3. package/THEMING.md +248 -0
  4. package/dist/atoms/ActionPanel.svelte +116 -0
  5. package/dist/atoms/ActionPanel.svelte.d.ts +19 -0
  6. package/dist/atoms/Badge.svelte +1 -1
  7. package/dist/atoms/Badge.svelte.d.ts +1 -1
  8. package/dist/atoms/Button.svelte +35 -16
  9. package/dist/atoms/Button.svelte.d.ts +3 -1
  10. package/dist/atoms/Card.svelte +49 -67
  11. package/dist/atoms/Card.svelte.d.ts +4 -4
  12. package/dist/atoms/CardHeader.svelte +15 -2
  13. package/dist/atoms/CardHeader.svelte.d.ts +5 -0
  14. package/dist/atoms/Checkbox.svelte +50 -76
  15. package/dist/atoms/Checkbox.svelte.d.ts +7 -2
  16. package/dist/atoms/CodeBlock.svelte +20 -34
  17. package/dist/atoms/CodeBlock.svelte.d.ts +1 -0
  18. package/dist/atoms/ColorPicker.svelte +3 -7
  19. package/dist/atoms/Container.svelte +41 -0
  20. package/dist/atoms/Container.svelte.d.ts +12 -0
  21. package/dist/atoms/Divider.svelte +68 -0
  22. package/dist/atoms/Divider.svelte.d.ts +13 -0
  23. package/dist/atoms/FeatureCard.svelte +35 -50
  24. package/dist/atoms/FeatureCard.svelte.d.ts +9 -4
  25. package/dist/atoms/IconButton.svelte +33 -24
  26. package/dist/atoms/IconButton.svelte.d.ts +4 -8
  27. package/dist/atoms/Input.svelte +58 -26
  28. package/dist/atoms/Input.svelte.d.ts +6 -1
  29. package/dist/atoms/List.svelte +2 -7
  30. package/dist/atoms/List.svelte.d.ts +1 -6
  31. package/dist/atoms/ListItem.svelte +54 -65
  32. package/dist/atoms/ListItem.svelte.d.ts +7 -13
  33. package/dist/atoms/ListItemLeading.svelte +19 -0
  34. package/dist/atoms/ListItemLeading.svelte.d.ts +9 -0
  35. package/dist/atoms/Progress.svelte +2 -2
  36. package/dist/atoms/Radio.svelte +51 -0
  37. package/dist/atoms/Radio.svelte.d.ts +14 -0
  38. package/dist/atoms/Select.svelte +28 -33
  39. package/dist/atoms/Select.svelte.d.ts +0 -10
  40. package/dist/atoms/SelectionControl.svelte +98 -0
  41. package/dist/atoms/SelectionControl.svelte.d.ts +26 -0
  42. package/dist/atoms/Skeleton.svelte +39 -15
  43. package/dist/atoms/Skeleton.svelte.d.ts +7 -3
  44. package/dist/atoms/Table.svelte +27 -0
  45. package/dist/atoms/Table.svelte.d.ts +10 -0
  46. package/dist/atoms/Text.svelte +46 -0
  47. package/dist/atoms/Text.svelte.d.ts +13 -0
  48. package/dist/atoms/Textarea.svelte +74 -53
  49. package/dist/atoms/Textarea.svelte.d.ts +6 -2
  50. package/dist/atoms/ThemeToggle.svelte +35 -24
  51. package/dist/atoms/ThemeToggle.svelte.d.ts +1 -0
  52. package/dist/atoms/Toast.svelte +55 -31
  53. package/dist/atoms/Toast.svelte.d.ts +4 -1
  54. package/dist/atoms/Toggle.svelte +37 -22
  55. package/dist/atoms/Toggle.svelte.d.ts +2 -1
  56. package/dist/atoms/Tooltip.svelte +90 -11
  57. package/dist/atoms/index.d.ts +6 -0
  58. package/dist/atoms/index.js +6 -0
  59. package/dist/atoms/selection-control.styles.d.ts +25 -0
  60. package/dist/atoms/selection-control.styles.js +30 -0
  61. package/dist/components/atoms/index.d.ts +6 -0
  62. package/dist/components/atoms/index.d.ts.map +1 -1
  63. package/dist/components/atoms/selection-control.styles.d.ts +26 -0
  64. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -0
  65. package/dist/components/index.d.ts +42 -7
  66. package/dist/components/index.d.ts.map +1 -1
  67. package/dist/components/molecules/index.d.ts +10 -0
  68. package/dist/components/molecules/index.d.ts.map +1 -1
  69. package/dist/components/molecules/navigation-menu-context.d.ts +11 -0
  70. package/dist/components/molecules/navigation-menu-context.d.ts.map +1 -1
  71. package/dist/components/molecules/toast-store.d.ts +39 -0
  72. package/dist/components/molecules/toast-store.d.ts.map +1 -0
  73. package/dist/components/organisms/index.d.ts +3 -3
  74. package/dist/components/organisms/index.d.ts.map +1 -1
  75. package/dist/components/types/page.types.d.ts +69 -0
  76. package/dist/components/types/page.types.d.ts.map +1 -0
  77. package/dist/components/types/page.types.js +1 -0
  78. package/dist/components/types/variants.d.ts +64 -0
  79. package/dist/components/types/variants.d.ts.map +1 -0
  80. package/dist/components/types/variants.js +44 -0
  81. package/dist/components/util/fixed-sidebar-flyout.d.ts +23 -0
  82. package/dist/components/util/fixed-sidebar-flyout.d.ts.map +1 -0
  83. package/dist/components/util/focus-utils.d.ts +12 -0
  84. package/dist/components/util/focus-utils.d.ts.map +1 -0
  85. package/dist/components/util/ssr-safe.d.ts +17 -0
  86. package/dist/components/util/ssr-safe.d.ts.map +1 -0
  87. package/dist/index.d.ts +42 -7
  88. package/dist/index.js +45 -10
  89. package/dist/lib/layout-width-tokens.d.ts +17 -0
  90. package/dist/lib/layout-width-tokens.d.ts.map +1 -0
  91. package/dist/lib/showcase/component-docs/Button.d.ts +3 -0
  92. package/dist/lib/showcase/component-docs/Button.d.ts.map +1 -0
  93. package/dist/lib/showcase/component-docs/Input.d.ts +3 -0
  94. package/dist/lib/showcase/component-docs/Input.d.ts.map +1 -0
  95. package/dist/lib/showcase/component-docs/List.d.ts +3 -0
  96. package/dist/lib/showcase/component-docs/List.d.ts.map +1 -0
  97. package/dist/lib/showcase/component-docs/Modal.d.ts +3 -0
  98. package/dist/lib/showcase/component-docs/Modal.d.ts.map +1 -0
  99. package/dist/lib/showcase/component-docs/Radio.d.ts +3 -0
  100. package/dist/lib/showcase/component-docs/Radio.d.ts.map +1 -0
  101. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts +3 -0
  102. package/dist/lib/showcase/component-docs/SidebarNavigation.d.ts.map +1 -0
  103. package/dist/lib/showcase/component-docs/Skeleton.d.ts +3 -0
  104. package/dist/lib/showcase/component-docs/Skeleton.d.ts.map +1 -0
  105. package/dist/lib/showcase/component-docs/_shared.d.ts +12 -0
  106. package/dist/lib/showcase/component-docs/_shared.d.ts.map +1 -0
  107. package/dist/lib/showcase/component-docs/index.d.ts +4 -0
  108. package/dist/lib/showcase/component-docs/index.d.ts.map +1 -0
  109. package/dist/lib/showcase/components-catalog.d.ts +3 -0
  110. package/dist/lib/showcase/components-catalog.d.ts.map +1 -0
  111. package/dist/lib/showcase/components-showcase-constants.d.ts +89 -0
  112. package/dist/lib/showcase/components-showcase-constants.d.ts.map +1 -0
  113. package/dist/lib/showcase/docs-sidebar-helpers.d.ts +15 -0
  114. package/dist/lib/showcase/docs-sidebar-helpers.d.ts.map +1 -0
  115. package/dist/molecules/Alert.svelte +50 -23
  116. package/dist/molecules/Alert.svelte.d.ts +1 -1
  117. package/dist/molecules/ComponentDemo.svelte +54 -51
  118. package/dist/molecules/ContactForm.svelte +70 -65
  119. package/dist/molecules/Dropdown.svelte +99 -108
  120. package/dist/molecules/Dropdown.svelte.d.ts +12 -6
  121. package/dist/molecules/EmptyState.svelte +44 -0
  122. package/dist/molecules/EmptyState.svelte.d.ts +11 -0
  123. package/dist/molecules/FormField.svelte +89 -0
  124. package/dist/molecules/FormField.svelte.d.ts +23 -0
  125. package/dist/molecules/Header.svelte +41 -0
  126. package/dist/molecules/Header.svelte.d.ts +10 -0
  127. package/dist/molecules/ImageUpload.svelte +1 -1
  128. package/dist/molecules/Modal.svelte +39 -26
  129. package/dist/molecules/Modal.svelte.d.ts +4 -2
  130. package/dist/molecules/NavigationMenu.svelte +16 -0
  131. package/dist/molecules/NavigationMenu.svelte.d.ts +2 -0
  132. package/dist/molecules/NavigationMenuContent.svelte +9 -1
  133. package/dist/molecules/NavigationMenuLink.svelte +1 -1
  134. package/dist/molecules/NavigationMenuTrigger.svelte +9 -4
  135. package/dist/molecules/Page.svelte +16 -0
  136. package/dist/molecules/Page.svelte.d.ts +8 -0
  137. package/dist/molecules/PropsTable.svelte +61 -0
  138. package/dist/molecules/PropsTable.svelte.d.ts +8 -0
  139. package/dist/molecules/RadioGroup.svelte +197 -0
  140. package/dist/molecules/RadioGroup.svelte.d.ts +20 -0
  141. package/dist/molecules/Section.svelte +14 -23
  142. package/dist/molecules/Section.svelte.d.ts +2 -11
  143. package/dist/molecules/SidebarBrandHeader.svelte +68 -0
  144. package/dist/molecules/SidebarBrandHeader.svelte.d.ts +10 -0
  145. package/dist/molecules/SidebarFooter.svelte +119 -0
  146. package/dist/molecules/SidebarFooter.svelte.d.ts +23 -0
  147. package/dist/molecules/SidebarNavSection.svelte +49 -0
  148. package/dist/molecules/SidebarNavSection.svelte.d.ts +14 -0
  149. package/dist/molecules/SlideUp.svelte +21 -16
  150. package/dist/molecules/SlideUp.svelte.d.ts +1 -1
  151. package/dist/molecules/Tabs.svelte +6 -11
  152. package/dist/molecules/Toaster.svelte +21 -0
  153. package/dist/molecules/Toaster.svelte.d.ts +6 -0
  154. package/dist/molecules/ToasterToast.svelte +217 -0
  155. package/dist/molecules/ToasterToast.svelte.d.ts +7 -0
  156. package/dist/molecules/index.d.ts +10 -0
  157. package/dist/molecules/index.js +10 -0
  158. package/dist/molecules/navigation-menu-context.d.ts +11 -0
  159. package/dist/molecules/navigation-menu-context.js +18 -0
  160. package/dist/molecules/toast-store.d.ts +38 -0
  161. package/dist/molecules/toast-store.js +34 -0
  162. package/dist/organisms/SidebarAccountPanel.svelte +118 -0
  163. package/dist/organisms/SidebarAccountPanel.svelte.d.ts +20 -0
  164. package/dist/organisms/SidebarNavigation.svelte +286 -212
  165. package/dist/organisms/SidebarNavigation.svelte.d.ts +22 -1
  166. package/dist/organisms/{SidebarProjectPanel.svelte → SidebarPanel.svelte} +67 -34
  167. package/dist/organisms/{SidebarProjectPanel.svelte.d.ts → SidebarPanel.svelte.d.ts} +9 -6
  168. package/dist/organisms/TopNavbar.svelte +214 -0
  169. package/dist/organisms/TopNavbar.svelte.d.ts +35 -0
  170. package/dist/organisms/index.d.ts +3 -3
  171. package/dist/organisms/index.js +3 -3
  172. package/dist/routes/lib/focus-utils.d.ts +7 -18
  173. package/dist/routes/lib/focus-utils.ts +32 -34
  174. package/dist/routes/lib/ssr-safe.ts +5 -4
  175. package/dist/routes/lib/variant-utils.ts +48 -39
  176. package/dist/types/components.d.ts +187 -0
  177. package/dist/types/components.d.ts.map +1 -0
  178. package/dist/types/events.d.ts +200 -0
  179. package/dist/types/events.d.ts.map +1 -0
  180. package/dist/types/index.d.ts +171 -0
  181. package/dist/types/index.d.ts.map +1 -0
  182. package/dist/types/page.types.d.ts +3 -0
  183. package/dist/types/page.types.d.ts.map +1 -0
  184. package/dist/types/page.types.js +1 -0
  185. package/dist/types/page.types.ts +79 -0
  186. package/dist/types/variants.d.ts +3 -0
  187. package/dist/types/variants.d.ts.map +1 -0
  188. package/dist/types/variants.js +1 -0
  189. package/dist/types/variants.ts +75 -0
  190. package/dist/util/fixed-sidebar-flyout.d.ts +22 -0
  191. package/dist/util/fixed-sidebar-flyout.js +148 -0
  192. package/dist/util/focus-utils.d.ts +11 -0
  193. package/dist/util/focus-utils.js +94 -0
  194. package/dist/util/ssr-safe.d.ts +16 -0
  195. package/dist/util/ssr-safe.js +48 -0
  196. package/dist/zabi-components-colors.css +360 -324
  197. package/dist/zabi-components-theme-dark-only.css +164 -151
  198. package/dist/zabi-components-theme-dark.css +164 -151
  199. package/dist/zabi-components-theme-only.css +253 -230
  200. package/dist/zabi-components-theme.css +253 -230
  201. package/dist/zabi-components.css +1262 -241
  202. package/docs/theme-imports.md +112 -0
  203. package/package.json +32 -10
  204. package/dist/organisms/Navbar.svelte +0 -92
  205. package/dist/organisms/Navbar.svelte.d.ts +0 -14
  206. package/dist/organisms/Navigation.svelte +0 -119
  207. package/dist/organisms/Navigation.svelte.d.ts +0 -21
@@ -1,13 +1,13 @@
1
1
  <script lang="ts">
2
2
  import Dropdown from "../molecules/Dropdown.svelte";
3
- import Button from "./Button.svelte";
3
+ import Input from "./Input.svelte";
4
4
  import {
5
5
  ChevronDown,
6
6
  CheckCircle,
7
7
  AlertTriangle,
8
8
  AlertCircle,
9
9
  } from "@lucide/svelte";
10
- import { generateId } from "../../routes/lib/ssr-safe.js";
10
+ import { generateId } from "../util/ssr-safe.js";
11
11
 
12
12
  interface Props {
13
13
  value?: string | number | undefined;
@@ -16,25 +16,15 @@
16
16
  label: string;
17
17
  disabled?: boolean;
18
18
  }>;
19
- /** Enables the search input inside the dropdown. */
20
19
  searchable?: boolean;
21
- /** Placeholder for the search input. */
22
20
  searchPlaceholder?: string;
23
- /** Max height for the options list (supports any valid CSS length). */
24
21
  maxMenuHeight?: string;
25
- /** Width for the dropdown menu (supports any valid CSS length). */
26
22
  menuWidth?: string;
27
- /** Message shown when no options match the search. */
28
23
  noResultsText?: string;
29
- /** Shows a loading state while options are being fetched. */
30
24
  isLoading?: boolean;
31
- /** Loading text shown in trigger and dropdown body. */
32
25
  loadingText?: string;
33
- /** Empty state title shown when there are no available options. */
34
26
  emptyStateTitle?: string;
35
- /** Empty state description shown when there are no available options. */
36
27
  emptyStateDescription?: string;
37
- /** Optional empty state action label. */
38
28
  emptyStateActionLabel?: string;
39
29
  placeholder?: string;
40
30
  label?: string;
@@ -100,18 +90,18 @@
100
90
 
101
91
  const variantClass = $derived(() => {
102
92
  return variant === "success"
103
- ? "border-success focus:border-success focus:ring-success"
93
+ ? "border-success focus-visible:border-success"
104
94
  : variant === "warning"
105
- ? "border-warning focus:border-warning focus:ring-warning"
95
+ ? "border-warning focus-visible:border-warning"
106
96
  : variant === "error"
107
- ? "border-error focus:border-error focus:ring-error"
108
- : "border-0 focus:ring-2 focus:ring-brand-500"; // default - no border
97
+ ? "border-error focus-visible:border-error"
98
+ : "border-input-border";
109
99
  });
110
100
 
111
101
  const triggerClasses = $derived(() => {
112
102
  const sizeStyles = sizeClass();
113
103
  const baseClasses =
114
- "w-full bg-input hover:bg-input-hover focus:bg-input-focus disabled:bg-input-disabled rounded-lg transition-all duration-200 text-body focus:outline-none focus:ring-offset-0 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-between";
104
+ "focus-ring flex w-full cursor-pointer items-center justify-between rounded-lg border bg-input text-body transition-all duration-200 hover:bg-input-hover active:bg-input-focus focus-visible:bg-input-focus focus:outline-none focus-visible:outline-none disabled:cursor-not-allowed disabled:bg-input-disabled disabled:opacity-50";
115
105
 
116
106
  return `${baseClasses} ${sizeStyles.padding} ${sizeStyles.text} ${sizeStyles.leading} ${variantClass()}`.trim();
117
107
  });
@@ -208,7 +198,6 @@
208
198
  const target = event.target as HTMLElement;
209
199
  const clickedSelectContainer = target.closest(".select-container");
210
200
 
211
- // Close if clicking outside any select container, or clicking on a different select
212
201
  if (
213
202
  !clickedSelectContainer ||
214
203
  clickedSelectContainer !== selectContainer
@@ -232,21 +221,21 @@
232
221
  {/if}
233
222
 
234
223
  <Dropdown
235
- {isOpen}
224
+ bind:isOpen={isOpen}
236
225
  placement="bottom-start"
237
226
  selectedValue={value}
238
227
  onOptionClick={handleOptionClick}
239
228
  ariaLabel="Select options"
229
+ menuRole="listbox"
240
230
  >
241
- {#snippet trigger()}
231
+ {#snippet trigger(aria)}
242
232
  <button
243
233
  type="button"
244
234
  id={selectId}
245
235
  class={triggerClasses()}
246
236
  {disabled}
247
237
  onclick={handleTriggerClick}
248
- aria-haspopup="listbox"
249
- aria-expanded={isOpen}
238
+ {...aria}
250
239
  aria-describedby={message ? `${selectId}-message` : undefined}
251
240
  >
252
241
  <span
@@ -277,9 +266,10 @@
277
266
  <div class="px-2 pb-2 pt-1" style:width={menuWidth}>
278
267
  {#if searchable && !isLoading}
279
268
  <div class="px-1 pb-2">
280
- <input
269
+ <Input
281
270
  type="text"
282
- class="w-full rounded-md bg-input hover:bg-input-hover focus:bg-input-focus text-body placeholder:text-description px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 disabled:opacity-50 disabled:cursor-not-allowed"
271
+ size="sm"
272
+ class="min-w-0"
283
273
  placeholder={searchPlaceholder}
284
274
  bind:value={searchQuery}
285
275
  aria-label={searchPlaceholder}
@@ -303,18 +293,23 @@
303
293
  "data-value": String(option.value),
304
294
  } as Record<string, string>}
305
295
  <div class="w-full my-0.5">
306
- <Button
307
- variant={value === option.value
308
- ? "outline"
309
- : "ghost"}
310
- size="sm"
311
- isFullWidth={true}
296
+ <button
297
+ type="button"
298
+ role="option"
299
+ aria-selected={value === option.value
300
+ ? true
301
+ : undefined}
302
+ class="focus-ring flex w-full items-center justify-start rounded-md border-2 px-3 py-2 text-left text-sm font-medium transition-colors focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 {value ===
303
+ option.value
304
+ ? 'border-action-primary bg-transparent text-headline'
305
+ : 'border-transparent bg-transparent text-body hover:bg-base-100'}"
312
306
  disabled={option.disabled}
313
- onclick={() => handleOptionClick(option.value)}
314
307
  {...buttonRestProps}
308
+ onclick={() =>
309
+ handleOptionClick(option.value)}
315
310
  >
316
311
  {option.label}
317
- </Button>
312
+ </button>
318
313
  </div>
319
314
  {/each}
320
315
  {:else if hasSearchQuery()}
@@ -328,7 +323,7 @@
328
323
  {#if emptyStateActionLabel && onEmptyStateAction}
329
324
  <button
330
325
  type="button"
331
- class="mt-3 inline-flex min-h-11 items-center rounded-lg bg-action-primary px-3 py-2 text-sm text-action-primary"
326
+ class="mt-3 inline-flex min-h-11 cursor-pointer items-center rounded-lg bg-action-primary px-3 py-2 text-sm text-action-primary"
332
327
  onclick={onEmptyStateAction}
333
328
  >
334
329
  {emptyStateActionLabel}
@@ -5,25 +5,15 @@ interface Props {
5
5
  label: string;
6
6
  disabled?: boolean;
7
7
  }>;
8
- /** Enables the search input inside the dropdown. */
9
8
  searchable?: boolean;
10
- /** Placeholder for the search input. */
11
9
  searchPlaceholder?: string;
12
- /** Max height for the options list (supports any valid CSS length). */
13
10
  maxMenuHeight?: string;
14
- /** Width for the dropdown menu (supports any valid CSS length). */
15
11
  menuWidth?: string;
16
- /** Message shown when no options match the search. */
17
12
  noResultsText?: string;
18
- /** Shows a loading state while options are being fetched. */
19
13
  isLoading?: boolean;
20
- /** Loading text shown in trigger and dropdown body. */
21
14
  loadingText?: string;
22
- /** Empty state title shown when there are no available options. */
23
15
  emptyStateTitle?: string;
24
- /** Empty state description shown when there are no available options. */
25
16
  emptyStateDescription?: string;
26
- /** Optional empty state action label. */
27
17
  emptyStateActionLabel?: string;
28
18
  placeholder?: string;
29
19
  label?: string;
@@ -0,0 +1,98 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import { generateId } from "../util/ssr-safe.js";
4
+ import {
5
+ SELECTION_CONTROL_LABEL_ROW,
6
+ selectionControlRingOverlayClasses,
7
+ selectionControlShellClasses,
8
+ type SelectionControlShape,
9
+ } from "./selection-control.styles";
10
+
11
+ type SelectionControlType = "checkbox" | "radio";
12
+
13
+ export type SelectionControlMarkProps = {
14
+ checked: boolean;
15
+ loading: boolean;
16
+ disabled: boolean;
17
+ };
18
+
19
+ interface Props {
20
+ id?: string;
21
+ type: SelectionControlType;
22
+ shape: SelectionControlShape;
23
+ name?: string;
24
+ value?: string;
25
+ label?: string;
26
+ disabled?: boolean;
27
+ loading?: boolean;
28
+ defaultChecked?: boolean;
29
+ checked?: boolean;
30
+ onChange?: (event: Event) => void;
31
+ onchange?: (event: Event) => void;
32
+ mark?: Snippet<[SelectionControlMarkProps]>;
33
+ }
34
+
35
+ let {
36
+ id: idProp,
37
+ type,
38
+ shape,
39
+ name = "",
40
+ value = "",
41
+ label = "",
42
+ disabled = false,
43
+ loading = false,
44
+ defaultChecked = false,
45
+ checked = $bindable(defaultChecked),
46
+ onChange,
47
+ onchange,
48
+ mark,
49
+ ...restProps
50
+ }: Props = $props();
51
+
52
+ /** Call once per mount — `generateId()` in `$derived` would churn ids every tick. */
53
+ const fallbackId = generateId("selection-control");
54
+ const controlId = $derived(idProp ?? fallbackId);
55
+ const isDisabled = $derived(disabled || loading);
56
+
57
+ const labelWrapperClasses = SELECTION_CONTROL_LABEL_ROW;
58
+
59
+ const controlContainerClasses = $derived(selectionControlShellClasses(shape));
60
+
61
+ const controlRingClasses = $derived(selectionControlRingOverlayClasses(shape));
62
+
63
+ function handleChange(event: Event) {
64
+ if (isDisabled) return;
65
+ const target = event.target as HTMLInputElement;
66
+ checked = target.checked;
67
+ onChange?.(event);
68
+ onchange?.(event);
69
+ }
70
+ </script>
71
+
72
+ <div class="flex items-center gap-2">
73
+ <label for={controlId} class={labelWrapperClasses}>
74
+ <span class={controlContainerClasses}>
75
+ <input
76
+ {type}
77
+ id={controlId}
78
+ {name}
79
+ {value}
80
+ {checked}
81
+ disabled={isDisabled}
82
+ class="sr-only"
83
+ aria-busy={loading ? "true" : undefined}
84
+ onchange={handleChange}
85
+ {...restProps}
86
+ />
87
+ <span class={controlRingClasses}></span>
88
+ {#if mark}
89
+ {@render mark({ checked, loading, disabled: isDisabled })}
90
+ {/if}
91
+ </span>
92
+ {#if label}
93
+ <span class="text-sm font-medium text-label">
94
+ {label}
95
+ </span>
96
+ {/if}
97
+ </label>
98
+ </div>
@@ -0,0 +1,26 @@
1
+ import type { Snippet } from "svelte";
2
+ import { type SelectionControlShape } from "./selection-control.styles";
3
+ type SelectionControlType = "checkbox" | "radio";
4
+ export type SelectionControlMarkProps = {
5
+ checked: boolean;
6
+ loading: boolean;
7
+ disabled: boolean;
8
+ };
9
+ interface Props {
10
+ id?: string;
11
+ type: SelectionControlType;
12
+ shape: SelectionControlShape;
13
+ name?: string;
14
+ value?: string;
15
+ label?: string;
16
+ disabled?: boolean;
17
+ loading?: boolean;
18
+ defaultChecked?: boolean;
19
+ checked?: boolean;
20
+ onChange?: (event: Event) => void;
21
+ onchange?: (event: Event) => void;
22
+ mark?: Snippet<[SelectionControlMarkProps]>;
23
+ }
24
+ declare const SelectionControl: import("svelte").Component<Props, {}, "checked">;
25
+ type SelectionControl = ReturnType<typeof SelectionControl>;
26
+ export default SelectionControl;
@@ -1,30 +1,54 @@
1
1
  <script lang="ts">
2
+ type Variant = "text" | "circle" | "block";
3
+
2
4
  interface Props {
3
- width?: string;
4
- height?: string;
5
- className?: string;
5
+ variant?: Variant;
6
+ /** CSS length or px number; omit to use variant default (circle stays square). */
7
+ width?: string | number;
8
+ /** CSS length or px number; omit for variant default height. */
9
+ height?: string | number;
10
+ class?: string;
6
11
  "aria-label"?: string;
7
12
  }
8
13
 
9
14
  let {
10
- width = "100%",
11
- height = "1rem",
12
- className = "",
13
- "aria-label": ariaLabel = "Loading...",
14
- ...restProps
15
+ variant = "text",
16
+ width,
17
+ height,
18
+ class: className = "",
19
+ "aria-label": ariaLabel = "Loading…",
15
20
  }: Props = $props();
16
21
 
17
- const classes = $derived(
18
- `animate-pulse bg-action-secondary rounded${className ? ` ${className}` : ""}`,
22
+ const radius: Record<Variant, string> = {
23
+ text: "rounded-md",
24
+ circle: "rounded-full",
25
+ block: "rounded-lg",
26
+ };
27
+
28
+ const defaultShape: Record<Variant, string> = {
29
+ text: "w-full h-3",
30
+ circle: "w-10 aspect-square",
31
+ block: "w-full h-32",
32
+ };
33
+
34
+ function toSize(value: string | number): string {
35
+ return typeof value === "number" ? `${value}px` : value;
36
+ }
37
+
38
+ const inlineStyle = $derived(
39
+ [
40
+ width !== undefined ? `width: ${toSize(width)}` : null,
41
+ height !== undefined ? `height: ${toSize(height)}` : null,
42
+ ]
43
+ .filter(Boolean)
44
+ .join("; "),
19
45
  );
20
- const styles = $derived(`width: ${width}; height: ${height};`);
21
46
  </script>
22
47
 
23
48
  <div
24
- class={classes}
25
- style={styles}
49
+ class="animate-pulse bg-surface-2 {defaultShape[variant]} {radius[variant]} {className}"
50
+ style={inlineStyle || undefined}
51
+ role="status"
26
52
  aria-busy="true"
27
53
  aria-label={ariaLabel}
28
- role="status"
29
- {...restProps}
30
54
  ></div>
@@ -1,7 +1,11 @@
1
+ type Variant = "text" | "circle" | "block";
1
2
  interface Props {
2
- width?: string;
3
- height?: string;
4
- className?: string;
3
+ variant?: Variant;
4
+ /** CSS length or px number; omit to use variant default (circle stays square). */
5
+ width?: string | number;
6
+ /** CSS length or px number; omit for variant default height. */
7
+ height?: string | number;
8
+ class?: string;
5
9
  "aria-label"?: string;
6
10
  }
7
11
  declare const Skeleton: import("svelte").Component<Props, {}, "">;
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ /** Visible `<caption>` when set. */
6
+ caption?: string;
7
+ class?: string;
8
+ children?: Snippet;
9
+ }
10
+
11
+ let { caption, class: className = '', children }: Props = $props();
12
+ </script>
13
+
14
+ <div
15
+ class="overflow-x-auto rounded-lg border border-border shadow-sm {className}"
16
+ >
17
+ <table class="w-full min-w-[20rem] border-collapse text-left text-sm text-body">
18
+ {#if caption}
19
+ <caption class="border-b border-border bg-base-50 px-4 py-3 text-left text-headline">
20
+ {caption}
21
+ </caption>
22
+ {/if}
23
+ {#if children}
24
+ {@render children()}
25
+ {/if}
26
+ </table>
27
+ </div>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** Visible `<caption>` when set. */
4
+ caption?: string;
5
+ class?: string;
6
+ children?: Snippet;
7
+ }
8
+ declare const Table: import("svelte").Component<Props, {}, "">;
9
+ type Table = ReturnType<typeof Table>;
10
+ export default Table;
@@ -0,0 +1,46 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ type Tone = 'body' | 'description' | 'caption' | 'headline' | 'label' | 'error';
5
+ type Size = 'sm' | 'md' | 'lg';
6
+
7
+ interface Props {
8
+ as?: 'p' | 'span' | 'div';
9
+ tone?: Tone;
10
+ size?: Size;
11
+ class?: string;
12
+ children?: Snippet;
13
+ }
14
+
15
+ let {
16
+ as: Tag = 'p',
17
+ tone = 'body',
18
+ size = 'md',
19
+ class: className = '',
20
+ children,
21
+ }: Props = $props();
22
+
23
+ const toneClass: Record<Tone, string> = {
24
+ body: 'text-body',
25
+ description: 'text-description',
26
+ caption: 'text-caption',
27
+ headline: 'text-headline',
28
+ label: 'text-label',
29
+ error: 'text-error',
30
+ };
31
+
32
+ const sizeClass: Record<Size, string> = {
33
+ sm: 'text-sm',
34
+ md: 'text-base',
35
+ lg: 'text-lg',
36
+ };
37
+ </script>
38
+
39
+ <svelte:element
40
+ this={Tag}
41
+ class={[toneClass[tone], sizeClass[size], className].filter(Boolean).join(' ')}
42
+ >
43
+ {#if children}
44
+ {@render children()}
45
+ {/if}
46
+ </svelte:element>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Tone = 'body' | 'description' | 'caption' | 'headline' | 'label' | 'error';
3
+ type Size = 'sm' | 'md' | 'lg';
4
+ interface Props {
5
+ as?: 'p' | 'span' | 'div';
6
+ tone?: Tone;
7
+ size?: Size;
8
+ class?: string;
9
+ children?: Snippet;
10
+ }
11
+ declare const Text: import("svelte").Component<Props, {}, "">;
12
+ type Text = ReturnType<typeof Text>;
13
+ export default Text;