zabi-components 8.1.0 → 9.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 (273) hide show
  1. package/CHANGELOG.md +1011 -0
  2. package/README.md +1 -1
  3. package/THEME.md +18 -5
  4. package/THEMING.md +308 -74
  5. package/dist/atoms/ActionPanel.svelte +1 -1
  6. package/dist/atoms/Avatar.svelte +19 -3
  7. package/dist/atoms/Avatar.svelte.d.ts +12 -0
  8. package/dist/atoms/Badge.svelte +37 -8
  9. package/dist/atoms/Badge.svelte.d.ts +6 -0
  10. package/dist/atoms/Block.svelte +74 -0
  11. package/dist/atoms/Block.svelte.d.ts +29 -0
  12. package/dist/atoms/Button.svelte +52 -15
  13. package/dist/atoms/Button.svelte.d.ts +3 -2
  14. package/dist/atoms/Card.svelte +181 -25
  15. package/dist/atoms/Card.svelte.d.ts +55 -3
  16. package/dist/atoms/Checkbox.svelte +33 -0
  17. package/dist/atoms/Checkbox.svelte.d.ts +27 -0
  18. package/dist/atoms/Chip.svelte +351 -0
  19. package/dist/atoms/Chip.svelte.d.ts +57 -0
  20. package/dist/atoms/CodeField.svelte +385 -0
  21. package/dist/atoms/CodeField.svelte.d.ts +61 -0
  22. package/dist/atoms/ColorPicker.svelte +1 -0
  23. package/dist/atoms/DateField.svelte +41 -3
  24. package/dist/atoms/DateField.svelte.d.ts +31 -3
  25. package/dist/atoms/FloatingActionButton.svelte +5 -3
  26. package/dist/atoms/FloatingActionButton.svelte.d.ts +1 -1
  27. package/dist/atoms/Heading.svelte +27 -1
  28. package/dist/atoms/Heading.svelte.d.ts +7 -0
  29. package/dist/atoms/IconButton.svelte +81 -9
  30. package/dist/atoms/IconButton.svelte.d.ts +13 -0
  31. package/dist/atoms/Input.svelte +10 -5
  32. package/dist/atoms/Input.svelte.d.ts +3 -2
  33. package/dist/atoms/List.svelte +3 -2
  34. package/dist/atoms/ListItem.svelte +31 -7
  35. package/dist/atoms/ListItem.svelte.d.ts +6 -0
  36. package/dist/atoms/PickerField.svelte +239 -0
  37. package/dist/atoms/PickerField.svelte.d.ts +67 -0
  38. package/dist/atoms/Progress.svelte +78 -17
  39. package/dist/atoms/Progress.svelte.d.ts +11 -1
  40. package/dist/atoms/Radio.svelte +32 -0
  41. package/dist/atoms/Radio.svelte.d.ts +26 -0
  42. package/dist/atoms/Rating.svelte +132 -6
  43. package/dist/atoms/Rating.svelte.d.ts +24 -3
  44. package/dist/atoms/Select.svelte +24 -26
  45. package/dist/atoms/Select.svelte.d.ts +12 -0
  46. package/dist/atoms/SelectionControl.svelte +99 -6
  47. package/dist/atoms/SelectionControl.svelte.d.ts +25 -0
  48. package/dist/atoms/Skeleton.svelte +114 -18
  49. package/dist/atoms/Skeleton.svelte.d.ts +30 -3
  50. package/dist/atoms/SkeletonGroup.svelte +43 -0
  51. package/dist/atoms/SkeletonGroup.svelte.d.ts +20 -0
  52. package/dist/atoms/Slider.svelte +35 -0
  53. package/dist/atoms/Sparkline.svelte +109 -0
  54. package/dist/atoms/Sparkline.svelte.d.ts +27 -0
  55. package/dist/atoms/SpeechBubble.svelte +187 -0
  56. package/dist/atoms/SpeechBubble.svelte.d.ts +33 -0
  57. package/dist/atoms/Stat.svelte +72 -0
  58. package/dist/atoms/Stat.svelte.d.ts +19 -0
  59. package/dist/atoms/TemporalField.svelte +342 -10
  60. package/dist/atoms/TemporalField.svelte.d.ts +20 -4
  61. package/dist/atoms/Textarea.svelte +100 -5
  62. package/dist/atoms/Textarea.svelte.d.ts +21 -0
  63. package/dist/atoms/TimeColumn.svelte +150 -0
  64. package/dist/atoms/TimeColumn.svelte.d.ts +25 -0
  65. package/dist/atoms/TimeField.svelte +46 -3
  66. package/dist/atoms/TimeField.svelte.d.ts +34 -3
  67. package/dist/atoms/Toast.svelte +6 -5
  68. package/dist/atoms/Toggle.svelte +61 -10
  69. package/dist/atoms/Toggle.svelte.d.ts +15 -0
  70. package/dist/atoms/Tooltip.svelte +20 -1
  71. package/dist/atoms/Tooltip.svelte.d.ts +7 -0
  72. package/dist/atoms/index.d.ts +21 -0
  73. package/dist/atoms/index.js +17 -0
  74. package/dist/atoms/selection-control.styles.d.ts +15 -4
  75. package/dist/atoms/selection-control.styles.js +16 -5
  76. package/dist/components/atoms/index.d.ts +21 -0
  77. package/dist/components/atoms/index.d.ts.map +1 -1
  78. package/dist/components/atoms/selection-control.styles.d.ts +15 -4
  79. package/dist/components/atoms/selection-control.styles.d.ts.map +1 -1
  80. package/dist/components/index.d.ts +27 -3
  81. package/dist/components/index.d.ts.map +1 -1
  82. package/dist/components/map.d.ts +11 -0
  83. package/dist/components/map.d.ts.map +1 -0
  84. package/dist/components/molecules/index.d.ts +7 -2
  85. package/dist/components/molecules/index.d.ts.map +1 -1
  86. package/dist/components/molecules/toast-store.d.ts +30 -0
  87. package/dist/components/molecules/toast-store.d.ts.map +1 -1
  88. package/dist/components/organisms/index.d.ts +6 -1
  89. package/dist/components/organisms/index.d.ts.map +1 -1
  90. package/dist/components/types/variants.d.ts +12 -3
  91. package/dist/components/types/variants.d.ts.map +1 -1
  92. package/dist/components/util/app-shell.d.ts +55 -4
  93. package/dist/components/util/app-shell.d.ts.map +1 -1
  94. package/dist/components/util/avatar.d.ts +3 -0
  95. package/dist/components/util/avatar.d.ts.map +1 -1
  96. package/dist/components/util/bottom-tab-bar.d.ts +6 -0
  97. package/dist/components/util/bottom-tab-bar.d.ts.map +1 -1
  98. package/dist/components/util/chart.d.ts +140 -0
  99. package/dist/components/util/chart.d.ts.map +1 -0
  100. package/dist/components/util/chip.d.ts +66 -0
  101. package/dist/components/util/chip.d.ts.map +1 -0
  102. package/dist/components/util/cn.d.ts.map +1 -1
  103. package/dist/components/util/code-field.d.ts +63 -0
  104. package/dist/components/util/code-field.d.ts.map +1 -0
  105. package/dist/components/util/collapsible.d.ts +6 -0
  106. package/dist/components/util/collapsible.d.ts.map +1 -1
  107. package/dist/components/util/display-voice.d.ts +10 -0
  108. package/dist/components/util/display-voice.d.ts.map +1 -0
  109. package/dist/components/util/field-control.d.ts +30 -0
  110. package/dist/components/util/field-control.d.ts.map +1 -0
  111. package/dist/components/util/field.d.ts +6 -0
  112. package/dist/components/util/field.d.ts.map +1 -1
  113. package/dist/components/util/focus-utils.d.ts +2 -0
  114. package/dist/components/util/focus-utils.d.ts.map +1 -1
  115. package/dist/components/util/map-view.d.ts +144 -0
  116. package/dist/components/util/map-view.d.ts.map +1 -0
  117. package/dist/components/util/photo.d.ts +19 -0
  118. package/dist/components/util/photo.d.ts.map +1 -1
  119. package/dist/components/util/progress.d.ts +19 -0
  120. package/dist/components/util/progress.d.ts.map +1 -0
  121. package/dist/components/util/scroll-edge.d.ts +33 -0
  122. package/dist/components/util/scroll-edge.d.ts.map +1 -0
  123. package/dist/components/util/sheet-drag.d.ts +25 -1
  124. package/dist/components/util/sheet-drag.d.ts.map +1 -1
  125. package/dist/components/util/skeleton.d.ts +15 -0
  126. package/dist/components/util/skeleton.d.ts.map +1 -0
  127. package/dist/components/util/sliding-indicator.d.ts +25 -0
  128. package/dist/components/util/sliding-indicator.d.ts.map +1 -0
  129. package/dist/components/util/temporal-field.d.ts +89 -0
  130. package/dist/components/util/temporal-field.d.ts.map +1 -0
  131. package/dist/components/util/toaster.d.ts +40 -0
  132. package/dist/components/util/toaster.d.ts.map +1 -1
  133. package/dist/components/util/tone-surface.d.ts +34 -0
  134. package/dist/components/util/tone-surface.d.ts.map +1 -0
  135. package/dist/components/util/top-navbar.d.ts +26 -2
  136. package/dist/components/util/top-navbar.d.ts.map +1 -1
  137. package/dist/components/util/zabi-strings.d.ts +9 -0
  138. package/dist/components/util/zabi-strings.d.ts.map +1 -1
  139. package/dist/create-theme/cli.js +6 -2
  140. package/dist/create-theme/index.d.ts +17 -3
  141. package/dist/create-theme/index.js +252 -35
  142. package/dist/create-theme/lib/resolve.js +97 -0
  143. package/dist/create-theme/theme-data.js +1618 -90
  144. package/dist/index.d.ts +27 -3
  145. package/dist/index.js +18 -0
  146. package/dist/map.d.ts +10 -0
  147. package/dist/map.js +9 -0
  148. package/dist/molecules/Alert.svelte +31 -12
  149. package/dist/molecules/Alert.svelte.d.ts +12 -1
  150. package/dist/molecules/AppBar.svelte +287 -28
  151. package/dist/molecules/AppBar.svelte.d.ts +62 -0
  152. package/dist/molecules/AvatarGroup.svelte +1 -1
  153. package/dist/molecules/BarChart.svelte +381 -0
  154. package/dist/molecules/BarChart.svelte.d.ts +45 -0
  155. package/dist/molecules/BottomSheet.svelte +88 -20
  156. package/dist/molecules/BottomSheet.svelte.d.ts +17 -1
  157. package/dist/molecules/BottomTabBar.svelte +134 -28
  158. package/dist/molecules/BottomTabBar.svelte.d.ts +36 -2
  159. package/dist/molecules/Calendar.svelte.d.ts +1 -1
  160. package/dist/molecules/ChipGroup.svelte +325 -0
  161. package/dist/molecules/ChipGroup.svelte.d.ts +41 -0
  162. package/dist/molecules/Collapsible.svelte +158 -54
  163. package/dist/molecules/Collapsible.svelte.d.ts +23 -6
  164. package/dist/molecules/CollapsibleGroup.svelte +16 -6
  165. package/dist/molecules/ConfirmDialog.svelte +5 -1
  166. package/dist/molecules/ConfirmDialog.svelte.d.ts +3 -0
  167. package/dist/molecules/Drawer.svelte +33 -12
  168. package/dist/molecules/Dropdown.svelte +9 -1
  169. package/dist/molecules/Dropdown.svelte.d.ts +2 -0
  170. package/dist/molecules/DropdownItem.svelte +1 -1
  171. package/dist/molecules/ImageUpload.svelte +230 -120
  172. package/dist/molecules/ImageUpload.svelte.d.ts +38 -3
  173. package/dist/molecules/LineChart.svelte +459 -0
  174. package/dist/molecules/LineChart.svelte.d.ts +53 -0
  175. package/dist/molecules/MapView.svelte +565 -0
  176. package/dist/molecules/MapView.svelte.d.ts +88 -0
  177. package/dist/molecules/Modal.svelte +11 -6
  178. package/dist/molecules/PhotoGrid.svelte +100 -10
  179. package/dist/molecules/PhotoGrid.svelte.d.ts +11 -0
  180. package/dist/molecules/PhotoViewer.svelte +121 -24
  181. package/dist/molecules/PhotoViewer.svelte.d.ts +18 -0
  182. package/dist/molecules/PieChart.svelte +421 -0
  183. package/dist/molecules/PieChart.svelte.d.ts +51 -0
  184. package/dist/molecules/RadioGroup.svelte +20 -4
  185. package/dist/molecules/RadioGroup.svelte.d.ts +5 -0
  186. package/dist/molecules/SegmentedControl.svelte +136 -3
  187. package/dist/molecules/SegmentedControl.svelte.d.ts +6 -0
  188. package/dist/molecules/SidebarFooter.svelte +1 -1
  189. package/dist/molecules/SidebarNavSection.svelte +1 -1
  190. package/dist/molecules/SortableList.svelte +1 -0
  191. package/dist/molecules/Stepper.svelte +21 -7
  192. package/dist/molecules/StickyActionBar.svelte +67 -8
  193. package/dist/molecules/StickyActionBar.svelte.d.ts +14 -1
  194. package/dist/molecules/Tabs.svelte +79 -2
  195. package/dist/molecules/Toaster.svelte +171 -7
  196. package/dist/molecules/Toaster.svelte.d.ts +60 -2
  197. package/dist/molecules/ToasterToast.svelte +292 -60
  198. package/dist/molecules/ToasterToast.svelte.d.ts +6 -0
  199. package/dist/molecules/UnsavedChangesBar.svelte +13 -2
  200. package/dist/molecules/index.d.ts +7 -2
  201. package/dist/molecules/index.js +4 -0
  202. package/dist/molecules/toast-store.d.ts +30 -0
  203. package/dist/molecules/toast-store.js +4 -1
  204. package/dist/organisms/AppNavigation.svelte +440 -0
  205. package/dist/organisms/AppNavigation.svelte.d.ts +72 -0
  206. package/dist/organisms/AppShell.svelte +539 -39
  207. package/dist/organisms/AppShell.svelte.d.ts +102 -6
  208. package/dist/organisms/AppShellHeaderPart.svelte +163 -0
  209. package/dist/organisms/AppShellHeaderPart.svelte.d.ts +57 -0
  210. package/dist/organisms/SidebarNavigation.svelte +23 -36
  211. package/dist/organisms/SidebarPanel.svelte +10 -14
  212. package/dist/organisms/SidebarPanel.svelte.d.ts +1 -0
  213. package/dist/organisms/SidebarShell.svelte +2 -3
  214. package/dist/organisms/TopNavbar.svelte +117 -28
  215. package/dist/organisms/TopNavbar.svelte.d.ts +22 -1
  216. package/dist/organisms/TopNavbarSheetContent.svelte +145 -0
  217. package/dist/organisms/TopNavbarSheetContent.svelte.d.ts +24 -0
  218. package/dist/organisms/index.d.ts +6 -1
  219. package/dist/organisms/index.js +4 -0
  220. package/dist/types/components.d.ts +17 -1
  221. package/dist/types/components.d.ts.map +1 -1
  222. package/dist/types/index.d.ts +497 -10
  223. package/dist/types/index.d.ts.map +1 -1
  224. package/dist/types/variants.ts +14 -3
  225. package/dist/util/app-shell.d.ts +55 -4
  226. package/dist/util/app-shell.js +32 -7
  227. package/dist/util/avatar.d.ts +3 -0
  228. package/dist/util/bottom-tab-bar.d.ts +6 -0
  229. package/dist/util/chart.d.ts +139 -0
  230. package/dist/util/chart.js +305 -0
  231. package/dist/util/chip.d.ts +65 -0
  232. package/dist/util/chip.js +58 -0
  233. package/dist/util/cn.js +28 -2
  234. package/dist/util/code-field.d.ts +62 -0
  235. package/dist/util/code-field.js +112 -0
  236. package/dist/util/collapsible.d.ts +6 -0
  237. package/dist/util/display-voice.d.ts +9 -0
  238. package/dist/util/display-voice.js +9 -0
  239. package/dist/util/field-control.d.ts +29 -0
  240. package/dist/util/field-control.js +32 -0
  241. package/dist/util/field.d.ts +6 -0
  242. package/dist/util/focus-utils.d.ts +2 -0
  243. package/dist/util/focus-utils.js +4 -0
  244. package/dist/util/map-view.d.ts +143 -0
  245. package/dist/util/map-view.js +121 -0
  246. package/dist/util/photo.d.ts +19 -0
  247. package/dist/util/photo.js +3 -0
  248. package/dist/util/progress.d.ts +18 -0
  249. package/dist/util/progress.js +17 -0
  250. package/dist/util/scroll-edge.d.ts +32 -0
  251. package/dist/util/scroll-edge.js +87 -0
  252. package/dist/util/sheet-drag.d.ts +25 -1
  253. package/dist/util/sheet-drag.js +71 -1
  254. package/dist/util/skeleton.d.ts +14 -0
  255. package/dist/util/skeleton.js +22 -0
  256. package/dist/util/sliding-indicator.d.ts +24 -0
  257. package/dist/util/sliding-indicator.js +44 -0
  258. package/dist/util/temporal-field.d.ts +88 -0
  259. package/dist/util/temporal-field.js +136 -0
  260. package/dist/util/toaster.d.ts +40 -0
  261. package/dist/util/toaster.js +55 -0
  262. package/dist/util/tone-surface.d.ts +33 -0
  263. package/dist/util/tone-surface.js +35 -0
  264. package/dist/util/top-navbar.d.ts +26 -2
  265. package/dist/util/top-navbar.js +23 -0
  266. package/dist/util/zabi-strings.d.ts +9 -0
  267. package/dist/zabi-components-colors.css +9 -3
  268. package/dist/zabi-components-theme-dark-only.css +474 -164
  269. package/dist/zabi-components-theme-dark.css +474 -164
  270. package/dist/zabi-components-theme-only.css +933 -99
  271. package/dist/zabi-components-theme.css +933 -99
  272. package/dist/zabi-components.css +2275 -321
  273. package/package.json +11 -1
@@ -1,11 +1,19 @@
1
1
  import { type Snippet } from "svelte";
2
2
  import type { HTMLAttributes } from "svelte/elements";
3
3
  import { type CollapsibleHeadingLevel, type CollapsibleTriggerProps, type CollapsibleTriggerState } from "../util/collapsible.js";
4
- /** Other attributes (`id`, `data-*`, ...) land on the host `<div>`. */
4
+ /**
5
+ * A trigger, then a panel of its own that it opens. It is not for a "show
6
+ * all" under a list: there the control comes after the content, and what
7
+ * it shows is more items of a list that is already on the screen. For
8
+ * that, put a `Button` with `aria-expanded` and `aria-controls` under the
9
+ * list and let the list draw the rest (Z-127).
10
+ *
11
+ * Other attributes (`id`, `data-*`, ...) land on the host `<div>`.
12
+ */
5
13
  type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title"> & {
6
14
  /** Extra classes for the host element. */
7
15
  class?: string;
8
- /** Extra classes for the default trigger button. */
16
+ /** Extra classes for the default trigger (a `<summary>`). */
9
17
  triggerClass?: string;
10
18
  /** Extra classes for the panel. */
11
19
  panelClass?: string;
@@ -18,18 +26,27 @@ type Props = Omit<HTMLAttributes<HTMLDivElement>, "class" | "title"> & {
18
26
  * a group does not close a disabled panel either.
19
27
  */
20
28
  disabled?: boolean;
21
- /** Text of the default trigger, a full-width header button with a chevron. */
29
+ /**
30
+ * Text of the default trigger, a full-width header with a chevron. It is
31
+ * a native `<details>` / `<summary>`, so it opens and closes without
32
+ * scripts: before hydration, and for a visitor who has none.
33
+ */
22
34
  title?: string;
23
35
  /**
24
36
  * Your own header, in place of the default trigger. Spread the first
25
- * argument on a `<button>`; the second carries the open state.
37
+ * argument on a `<button>`; the second carries the open state. This
38
+ * path needs scripts: the button and the `hidden` panel are yours to
39
+ * wire, and nothing opens until the page has hydrated. Use `title` for
40
+ * content that must open without scripts.
26
41
  */
27
42
  trigger?: Snippet<[CollapsibleTriggerProps, CollapsibleTriggerState]>;
28
43
  /** Wraps the default trigger in `<h1>`–`<h6>`. No heading when omitted. */
29
44
  headingLevel?: CollapsibleHeadingLevel;
30
45
  /**
31
- * Closed content is kept in the DOM under `hidden`, so a form inside it
32
- * keeps its values. Set this to remove the content while closed instead.
46
+ * Closed content stays in the DOM, inside the closed `<details>`, so a
47
+ * form inside it keeps its values and still submits them: closed
48
+ * details content is part of the form. Set this to remove the content
49
+ * while closed instead.
33
50
  */
34
51
  unmountOnClose?: boolean;
35
52
  /**
@@ -2,6 +2,7 @@
2
2
  import { setContext, untrack, type Snippet } from "svelte";
3
3
  import type { HTMLAttributes } from "svelte/elements";
4
4
  import { cn } from "../util/cn.js";
5
+ import { generateId } from "../util/ssr-safe.js";
5
6
  import {
6
7
  COLLAPSIBLE_GROUP,
7
8
  nextTriggerIndex,
@@ -29,6 +30,9 @@
29
30
 
30
31
  let hostElement: HTMLDivElement | undefined = $state();
31
32
 
33
+ /** Names the native exclusive accordion of a single-open group. */
34
+ const exclusiveName = generateId("collapsible-group");
35
+
32
36
  /** In the order the Collapsibles were created. */
33
37
  const members = new Set<CollapsibleGroupMember>();
34
38
 
@@ -47,6 +51,9 @@
47
51
  get multiple() {
48
52
  return multiple;
49
53
  },
54
+ get exclusiveName() {
55
+ return multiple ? undefined : exclusiveName;
56
+ },
50
57
  register(member) {
51
58
  // Settled here, while the members initialise, so the first render
52
59
  // (the server's included) already shows one open panel.
@@ -91,16 +98,19 @@
91
98
  if (!ids.has(target.id)) return;
92
99
 
93
100
  // Read from the DOM so the order is the visual one. A disabled button
94
- // cannot take focus, so it is skipped.
101
+ // (or a summary marked `aria-disabled`) cannot take focus, so it is skipped.
95
102
  const triggers = Array.from(
96
- hostElement.querySelectorAll<HTMLButtonElement>(
97
- "[data-collapsible-trigger]",
98
- ),
99
- ).filter((element) => ids.has(element.id) && !element.disabled);
103
+ hostElement.querySelectorAll<HTMLElement>("[data-collapsible-trigger]"),
104
+ ).filter(
105
+ (element) =>
106
+ ids.has(element.id) &&
107
+ !(element as HTMLButtonElement).disabled &&
108
+ element.getAttribute("aria-disabled") !== "true",
109
+ );
100
110
 
101
111
  const next = nextTriggerIndex(
102
112
  event.key,
103
- triggers.indexOf(target as HTMLButtonElement),
113
+ triggers.indexOf(target),
104
114
  triggers.length,
105
115
  );
106
116
  if (next === null) return;
@@ -13,6 +13,7 @@
13
13
  ConfirmDialogResult,
14
14
  ConfirmDialogVariant,
15
15
  } from "../util/confirm-dialog.js";
16
+ import type { ButtonVariant } from "../types/variants.js";
16
17
 
17
18
  interface Props {
18
19
  /** Whether the dialog is shown; supports `bind:open`. */
@@ -25,6 +26,8 @@
25
26
  variant?: ConfirmDialogVariant;
26
27
  confirmLabel?: string;
27
28
  cancelLabel?: string;
29
+ /** The `Button` variant of the cancel button: `tonal` or `ghost` for an app that has no outline button. */
30
+ cancelVariant?: ButtonVariant;
28
31
  /**
29
32
  * The confirm button shows its loading state, cancel is disabled and
30
33
  * the dialog cannot be dismissed. Set by the dialog itself while a
@@ -70,6 +73,7 @@
70
73
  variant = "info",
71
74
  confirmLabel: confirmLabelGiven,
72
75
  cancelLabel: cancelLabelGiven,
76
+ cancelVariant = "outline",
73
77
  loading = false,
74
78
  loadingLabel = "Working…",
75
79
  onconfirm,
@@ -234,7 +238,7 @@
234
238
 
235
239
  {#snippet footer()}
236
240
  <Button
237
- variant="outline"
241
+ variant={cancelVariant}
238
242
  disabled={busy}
239
243
  onclick={() => cancel("cancel-button")}
240
244
  data-confirm-dialog-cancel=""
@@ -1,5 +1,6 @@
1
1
  import { type Snippet } from "svelte";
2
2
  import type { ConfirmDialogCancelReason, ConfirmDialogResult, ConfirmDialogVariant } from "../util/confirm-dialog.js";
3
+ import type { ButtonVariant } from "../types/variants.js";
3
4
  interface Props {
4
5
  /** Whether the dialog is shown; supports `bind:open`. */
5
6
  open?: boolean;
@@ -11,6 +12,8 @@ interface Props {
11
12
  variant?: ConfirmDialogVariant;
12
13
  confirmLabel?: string;
13
14
  cancelLabel?: string;
15
+ /** The `Button` variant of the cancel button: `tonal` or `ghost` for an app that has no outline button. */
16
+ cancelVariant?: ButtonVariant;
14
17
  /**
15
18
  * The confirm button shows its loading state, cancel is disabled and
16
19
  * the dialog cannot be dismissed. Set by the dialog itself while a
@@ -121,6 +121,7 @@
121
121
  applyDefaults();
122
122
  $effect.pre(applyDefaults);
123
123
 
124
+ // Used only where --duration-moderate cannot be read.
124
125
  const SLIDE_MS = 200;
125
126
 
126
127
  const titleId = generateId("drawer-title");
@@ -159,14 +160,14 @@
159
160
  }[size] ?? "w-[min(100%,28rem)]",
160
161
  );
161
162
 
162
- /** The border is on the edge that faces the page. */
163
+ /** The edge the panel is attached to. The rim comes from the material. */
163
164
  const sideClasses = $derived(
164
165
  {
165
- "left": "left-0 border-r",
166
- "right": "right-0 border-l",
167
- "start": "start-0 border-e",
168
- "end": "end-0 border-s",
169
- }[side] ?? "right-0 border-l",
166
+ "left": "left-0",
167
+ "right": "right-0",
168
+ "start": "start-0",
169
+ "end": "end-0",
170
+ }[side] ?? "right-0",
170
171
  );
171
172
 
172
173
  function close(reason: DrawerCloseReason) {
@@ -179,6 +180,14 @@
179
180
  onclose?.({ reason });
180
181
  }
181
182
 
183
+ /** `200ms`, `0.2s` or `0s` to milliseconds; the fallback when it is not a time. */
184
+ function parseDuration(value: string): number {
185
+ const match = /^\s*(-?\d*\.?\d+)(ms|s)\s*$/.exec(value);
186
+ if (!match) return SLIDE_MS;
187
+ const ms = parseFloat(match[1]) * (match[2] === "s" ? 1000 : 1);
188
+ return ms >= 0 ? ms : SLIDE_MS;
189
+ }
190
+
182
191
  /** Slides the panel in from its edge. Skipped where motion is unwanted or unsupported. */
183
192
  function slideIn(container: HTMLElement) {
184
193
  if (typeof container.animate !== "function") return;
@@ -190,10 +199,16 @@
190
199
  }
191
200
  const rtl = getComputedStyle(container).direction === "rtl";
192
201
  const from = resolveDrawerEdge(side, rtl) === "left" ? "-100%" : "100%";
193
- container.animate(
194
- [{ transform: `translateX(${from})` }, { transform: "translateX(0)" }],
195
- { duration: SLIDE_MS, easing: "ease-out" },
196
- );
202
+ // Read from the element so a theme that overrides the tokens is followed.
203
+ const style = getComputedStyle(container);
204
+ const duration = parseDuration(style.getPropertyValue("--duration-moderate"));
205
+ const easing = style.getPropertyValue("--ease-out").trim() || "ease-out";
206
+ const frames = [{ transform: `translateX(${from})` }, { transform: "translateX(0)" }];
207
+ try {
208
+ container.animate(frames, { duration, easing });
209
+ } catch {
210
+ container.animate(frames, { duration, easing: "ease-out" });
211
+ }
197
212
  }
198
213
 
199
214
  $effect(() => {
@@ -296,8 +311,11 @@
296
311
 
297
312
  {#if isOpen}
298
313
  <!-- svelte-ignore a11y_click_events_have_key_events -->
314
+ <!-- `overflow-clip`, not `hidden` (as in BottomSheet): a clipped box is a
315
+ scroll container nobody can scroll, and WebKit scrolls one to a focused
316
+ element while the panel is still off screen. -->
299
317
  <div
300
- class="group/overlay fixed inset-0 z-modal overflow-hidden {dismissible ? 'cursor-pointer' : 'cursor-default'} bg-overlay"
318
+ class="group/overlay fixed inset-0 z-modal overflow-clip {dismissible ? 'cursor-pointer' : 'cursor-default'} bg-overlay"
301
319
  style:z-index={depth > 0 ? `calc(var(--z-modal) + ${depth})` : undefined}
302
320
  data-overlay-depth={depth}
303
321
  use:portalTo={portal}
@@ -308,7 +326,10 @@
308
326
  <div
309
327
  bind:this={panel}
310
328
  class={cn(
311
- "absolute inset-y-0 flex cursor-default flex-col border-border-overlay bg-surface-overlay shadow-lg",
329
+ // The glass is a layer behind the content (`::before`), not a filter on
330
+ // the panel: a backdrop-filter would make the panel the containing
331
+ // block of every fixed-position popover inside it.
332
+ "material-layer-thick absolute inset-y-0 flex cursor-default flex-col",
312
333
  // Above the home indicator: the last row, or the footer, ends
313
334
  // where a finger can reach it. Over a keyboard that is under
314
335
  // the keyboard.
@@ -60,6 +60,8 @@
60
60
  * to six `options`, all of it for more. The grip moves it either way.
61
61
  */
62
62
  sheetSnap?: BottomSheetSnap;
63
+ /** The sheet is as tall as the list, up to half the screen (BottomSheet `fit`); the grip then only drags. Off by default. */
64
+ sheetFit?: boolean;
63
65
  /** Accessible name of the sheet's close button. */
64
66
  sheetCloseLabel?: string;
65
67
  /** Accessible name of the sheet's grip while its button takes the sheet up a step. */
@@ -93,6 +95,7 @@
93
95
  fullWidth = false,
94
96
  sheetTitle = undefined,
95
97
  sheetSnap = undefined,
98
+ sheetFit = false,
96
99
  sheetCloseLabel: sheetCloseLabelGiven,
97
100
  sheetExpandLabel: sheetExpandLabelGiven,
98
101
  sheetCollapseLabel: sheetCollapseLabelGiven,
@@ -562,7 +565,11 @@
562
565
  // the corner: within a pixel of concentric.
563
566
  // A column, so that the list below can be the part that scrolls
564
567
  // when the menu is limited in height.
565
- 'flex flex-col rounded-overlay border border-border-overlay bg-surface-overlay py-2 shadow-lg transition-[opacity,translate] duration-200 ease-in-out motion-reduce:transition-none',
568
+ // The layer form of the material: a menu may hold content that
569
+ // places itself with `position: fixed` (a nested Dropdown, a
570
+ // Tooltip), and a backdrop-filter on the menu itself would make it
571
+ // the containing block of that content.
572
+ 'material-layer-thick flex flex-col rounded-overlay py-2 transition-[opacity,translate] duration-(--duration-moderate) ease-standard motion-reduce:transition-none',
566
573
  transformClasses(),
567
574
  ]
568
575
  .join(' ')
@@ -590,6 +597,7 @@
590
597
  <BottomSheet
591
598
  bind:isOpen
592
599
  snap={sheetSnap ?? (options.length > 6 ? 'full' : 'half')}
600
+ fit={sheetFit}
593
601
  title={sheetTitle || ariaLabel}
594
602
  closeLabel={sheetCloseLabel}
595
603
  expandLabel={sheetExpandLabel}
@@ -39,6 +39,8 @@ interface Props {
39
39
  * to six `options`, all of it for more. The grip moves it either way.
40
40
  */
41
41
  sheetSnap?: BottomSheetSnap;
42
+ /** The sheet is as tall as the list, up to half the screen (BottomSheet `fit`); the grip then only drags. Off by default. */
43
+ sheetFit?: boolean;
42
44
  /** Accessible name of the sheet's close button. */
43
45
  sheetCloseLabel?: string;
44
46
  /** Accessible name of the sheet's grip while its button takes the sheet up a step. */
@@ -69,7 +69,7 @@
69
69
  const itemClasses = $derived(
70
70
  cn(
71
71
  "focus-ring flex w-full items-start justify-start gap-2 rounded-control px-3 py-2 pointer-coarse:py-3 text-start text-sm transition-colors focus:outline-none",
72
- tone === "danger" ? "text-error focus-ring--danger" : "text-body",
72
+ tone === "danger" ? "text-error-text focus-ring--danger" : "text-body",
73
73
  disabled
74
74
  ? "cursor-not-allowed"
75
75
  : tone === "danger"