@mitcsutt/kiln-ui 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 (188) hide show
  1. package/dist/components/actions/CopyButton/CopyButton.d.ts +58 -0
  2. package/dist/components/actions/CopyButton/CopyButton.js +63 -0
  3. package/dist/components/actions/CopyButton/CopyButton.js.map +1 -0
  4. package/dist/components/actions/CopyButton/CopyButton.module.js +9 -0
  5. package/dist/components/actions/CopyButton/CopyButton.module.js.map +1 -0
  6. package/dist/components/actions/CopyButton/index.d.ts +4 -0
  7. package/dist/components/actions/CopyButton/useClipboard.d.ts +35 -0
  8. package/dist/components/actions/CopyButton/useClipboard.js +62 -0
  9. package/dist/components/actions/CopyButton/useClipboard.js.map +1 -0
  10. package/dist/components/actions/ToggleChip/ToggleChip.d.ts +6 -0
  11. package/dist/components/actions/ToggleChip/ToggleChip.js +26 -24
  12. package/dist/components/actions/ToggleChip/ToggleChip.js.map +1 -1
  13. package/dist/components/display/Badge/Badge.d.ts +6 -0
  14. package/dist/components/display/Badge/Badge.js +16 -15
  15. package/dist/components/display/Badge/Badge.js.map +1 -1
  16. package/dist/components/display/CodeBlock/CodeBlock.d.ts +44 -3
  17. package/dist/components/display/CodeBlock/CodeBlock.js +39 -47
  18. package/dist/components/display/CodeBlock/CodeBlock.js.map +1 -1
  19. package/dist/components/display/CodeBlock/CodeBlock.module.js +4 -3
  20. package/dist/components/display/CodeBlock/CodeBlock.module.js.map +1 -1
  21. package/dist/components/display/CodeBlock/index.d.ts +1 -1
  22. package/dist/components/display/Swatch/Swatch.d.ts +52 -0
  23. package/dist/components/display/Swatch/Swatch.js +27 -0
  24. package/dist/components/display/Swatch/Swatch.js.map +1 -0
  25. package/dist/components/display/Swatch/Swatch.module.js +6 -0
  26. package/dist/components/display/Swatch/Swatch.module.js.map +1 -0
  27. package/dist/components/display/Swatch/index.d.ts +2 -0
  28. package/dist/components/display/Tag/Tag.d.ts +7 -0
  29. package/dist/components/display/Tag/Tag.js +31 -26
  30. package/dist/components/display/Tag/Tag.js.map +1 -1
  31. package/dist/components/feedback/Indicator/Indicator.d.ts +74 -0
  32. package/dist/components/feedback/Indicator/Indicator.js +50 -0
  33. package/dist/components/feedback/Indicator/Indicator.js.map +1 -0
  34. package/dist/components/feedback/Indicator/Indicator.module.js +12 -0
  35. package/dist/components/feedback/Indicator/Indicator.module.js.map +1 -0
  36. package/dist/components/feedback/Indicator/index.d.ts +2 -0
  37. package/dist/components/feedback/Toaster/Toaster.d.ts +71 -0
  38. package/dist/components/feedback/Toaster/Toaster.js +121 -0
  39. package/dist/components/feedback/Toaster/Toaster.js.map +1 -0
  40. package/dist/components/feedback/Toaster/Toaster.module.js +20 -0
  41. package/dist/components/feedback/Toaster/Toaster.module.js.map +1 -0
  42. package/dist/components/feedback/Toaster/index.d.ts +4 -0
  43. package/dist/components/feedback/Toaster/toast.d.ts +96 -0
  44. package/dist/components/feedback/Toaster/toast.js +131 -0
  45. package/dist/components/feedback/Toaster/toast.js.map +1 -0
  46. package/dist/components/inputs/Calendar/Calendar.d.ts +87 -0
  47. package/dist/components/inputs/Calendar/Calendar.js +217 -0
  48. package/dist/components/inputs/Calendar/Calendar.js.map +1 -0
  49. package/dist/components/inputs/Calendar/Calendar.module.js +18 -0
  50. package/dist/components/inputs/Calendar/Calendar.module.js.map +1 -0
  51. package/dist/components/inputs/Calendar/index.d.ts +2 -0
  52. package/dist/components/inputs/Combobox/Combobox.js +36 -44
  53. package/dist/components/inputs/Combobox/Combobox.js.map +1 -1
  54. package/dist/components/inputs/DateField/DateField.d.ts +17 -0
  55. package/dist/components/inputs/DateField/DateField.js +26 -0
  56. package/dist/components/inputs/DateField/DateField.js.map +1 -0
  57. package/dist/components/inputs/DateField/index.d.ts +2 -0
  58. package/dist/components/inputs/DateInput/DateInput.d.ts +75 -0
  59. package/dist/components/inputs/DateInput/DateInput.js +139 -0
  60. package/dist/components/inputs/DateInput/DateInput.js.map +1 -0
  61. package/dist/components/inputs/DateInput/index.d.ts +3 -0
  62. package/dist/components/inputs/DateRangeField/DateRangeField.d.ts +18 -3
  63. package/dist/components/inputs/DateRangeField/DateRangeField.js.map +1 -1
  64. package/dist/components/inputs/DateRangeField/DateRangeField.module.js +8 -3
  65. package/dist/components/inputs/DateRangeField/DateRangeField.module.js.map +1 -1
  66. package/dist/components/inputs/DateRangeField/DateRangePickerField.d.ts +48 -0
  67. package/dist/components/inputs/DateRangeField/DateRangePickerField.js +246 -0
  68. package/dist/components/inputs/DateRangeField/DateRangePickerField.js.map +1 -0
  69. package/dist/components/inputs/DateRangeField/index.d.ts +2 -0
  70. package/dist/components/inputs/DateTimeField/DateTimeField.d.ts +17 -0
  71. package/dist/components/inputs/DateTimeField/DateTimeField.js +26 -0
  72. package/dist/components/inputs/DateTimeField/DateTimeField.js.map +1 -0
  73. package/dist/components/inputs/DateTimeField/index.d.ts +2 -0
  74. package/dist/components/inputs/DateTimeInput/DateTimeInput.d.ts +83 -0
  75. package/dist/components/inputs/DateTimeInput/DateTimeInput.js +216 -0
  76. package/dist/components/inputs/DateTimeInput/DateTimeInput.js.map +1 -0
  77. package/dist/components/inputs/DateTimeInput/index.d.ts +2 -0
  78. package/dist/components/inputs/JsonField/JsonField.d.ts +23 -0
  79. package/dist/components/inputs/JsonField/JsonField.js +33 -0
  80. package/dist/components/inputs/JsonField/JsonField.js.map +1 -0
  81. package/dist/components/inputs/JsonField/index.d.ts +2 -0
  82. package/dist/components/inputs/JsonInput/JsonInput.d.ts +119 -0
  83. package/dist/components/inputs/JsonInput/JsonInput.js +282 -0
  84. package/dist/components/inputs/JsonInput/JsonInput.js.map +1 -0
  85. package/dist/components/inputs/JsonInput/JsonInput.module.js +16 -0
  86. package/dist/components/inputs/JsonInput/JsonInput.module.js.map +1 -0
  87. package/dist/components/inputs/JsonInput/index.d.ts +4 -0
  88. package/dist/components/inputs/JsonInput/json.d.ts +59 -0
  89. package/dist/components/inputs/JsonInput/json.js +231 -0
  90. package/dist/components/inputs/JsonInput/json.js.map +1 -0
  91. package/dist/components/inputs/TagsInput/TagsInput.js +5 -5
  92. package/dist/components/inputs/TimeField/TimeField.d.ts +17 -0
  93. package/dist/components/inputs/TimeField/TimeField.js +26 -0
  94. package/dist/components/inputs/TimeField/TimeField.js.map +1 -0
  95. package/dist/components/inputs/TimeField/index.d.ts +2 -0
  96. package/dist/components/inputs/TimeInput/TimeInput.d.ts +54 -0
  97. package/dist/components/inputs/TimeInput/TimeInput.js +107 -0
  98. package/dist/components/inputs/TimeInput/TimeInput.js.map +1 -0
  99. package/dist/components/inputs/TimeInput/index.d.ts +2 -0
  100. package/dist/components/inputs/internal/DateSegments.d.ts +80 -0
  101. package/dist/components/inputs/internal/DateSegments.js +345 -0
  102. package/dist/components/inputs/internal/DateSegments.js.map +1 -0
  103. package/dist/components/inputs/internal/dates.d.ts +101 -0
  104. package/dist/components/inputs/internal/dates.js +262 -0
  105. package/dist/components/inputs/internal/dates.js.map +1 -0
  106. package/dist/components/inputs/internal/filterOptions.d.ts +22 -1
  107. package/dist/components/inputs/internal/filterOptions.js +32 -1
  108. package/dist/components/inputs/internal/filterOptions.js.map +1 -1
  109. package/dist/components/inputs/internal/picker.d.ts +31 -0
  110. package/dist/components/inputs/internal/picker.js +29 -0
  111. package/dist/components/inputs/internal/picker.js.map +1 -0
  112. package/dist/components/inputs/internal/picker.module.js +15 -0
  113. package/dist/components/inputs/internal/picker.module.js.map +1 -0
  114. package/dist/components/inputs/internal/segments.module.js +10 -0
  115. package/dist/components/inputs/internal/segments.module.js.map +1 -0
  116. package/dist/components/layout/Collapse/Collapse.d.ts +39 -0
  117. package/dist/components/layout/Collapse/Collapse.js +24 -0
  118. package/dist/components/layout/Collapse/Collapse.js.map +1 -0
  119. package/dist/components/layout/Collapse/Collapse.module.js +6 -0
  120. package/dist/components/layout/Collapse/Collapse.module.js.map +1 -0
  121. package/dist/components/layout/Collapse/index.d.ts +2 -0
  122. package/dist/components/layout/Collapse/useHeightMotion.d.ts +16 -0
  123. package/dist/components/layout/Collapse/useHeightMotion.js +60 -0
  124. package/dist/components/layout/Collapse/useHeightMotion.js.map +1 -0
  125. package/dist/components/layout/ShowMore/ShowMore.d.ts +59 -0
  126. package/dist/components/layout/ShowMore/ShowMore.js +114 -0
  127. package/dist/components/layout/ShowMore/ShowMore.js.map +1 -0
  128. package/dist/components/layout/ShowMore/ShowMore.module.js +12 -0
  129. package/dist/components/layout/ShowMore/ShowMore.module.js.map +1 -0
  130. package/dist/components/layout/ShowMore/index.d.ts +2 -0
  131. package/dist/components/layout/Transition/Transition.d.ts +63 -0
  132. package/dist/components/layout/Transition/Transition.js +63 -0
  133. package/dist/components/layout/Transition/Transition.js.map +1 -0
  134. package/dist/components/layout/Transition/Transition.module.js +17 -0
  135. package/dist/components/layout/Transition/Transition.module.js.map +1 -0
  136. package/dist/components/layout/Transition/index.d.ts +2 -0
  137. package/dist/components/navigation/Collapsible/Collapsible.d.ts +49 -0
  138. package/dist/components/navigation/Collapsible/Collapsible.js +83 -0
  139. package/dist/components/navigation/Collapsible/Collapsible.js.map +1 -0
  140. package/dist/components/navigation/Collapsible/Collapsible.module.js +11 -0
  141. package/dist/components/navigation/Collapsible/Collapsible.module.js.map +1 -0
  142. package/dist/components/navigation/Collapsible/index.d.ts +2 -0
  143. package/dist/components/navigation/NavLinks/NavLinks.d.ts +32 -4
  144. package/dist/components/navigation/NavLinks/NavLinks.js +69 -39
  145. package/dist/components/navigation/NavLinks/NavLinks.js.map +1 -1
  146. package/dist/components/navigation/NavLinks/NavLinks.module.js +13 -3
  147. package/dist/components/navigation/NavLinks/NavLinks.module.js.map +1 -1
  148. package/dist/components/navigation/NavLinks/NavLinksGroup.d.ts +49 -0
  149. package/dist/components/navigation/NavLinks/NavLinksGroup.js +185 -0
  150. package/dist/components/navigation/NavLinks/NavLinksGroup.js.map +1 -0
  151. package/dist/components/navigation/NavLinks/context.d.ts +28 -0
  152. package/dist/components/navigation/NavLinks/context.js +13 -0
  153. package/dist/components/navigation/NavLinks/context.js.map +1 -0
  154. package/dist/components/navigation/NavLinks/index.d.ts +2 -0
  155. package/dist/components/navigation/NavLinks/treeKeys.d.ts +8 -0
  156. package/dist/components/navigation/NavLinks/treeKeys.js +57 -0
  157. package/dist/components/navigation/NavLinks/treeKeys.js.map +1 -0
  158. package/dist/components/overlays/CommandPalette/CommandPalette.d.ts +120 -0
  159. package/dist/components/overlays/CommandPalette/CommandPalette.js +386 -0
  160. package/dist/components/overlays/CommandPalette/CommandPalette.js.map +1 -0
  161. package/dist/components/overlays/CommandPalette/CommandPalette.module.js +31 -0
  162. package/dist/components/overlays/CommandPalette/CommandPalette.module.js.map +1 -0
  163. package/dist/components/overlays/CommandPalette/index.d.ts +2 -0
  164. package/dist/components/overlays/CommandPalette/shortcut.d.ts +28 -0
  165. package/dist/components/overlays/CommandPalette/shortcut.js +104 -0
  166. package/dist/components/overlays/CommandPalette/shortcut.js.map +1 -0
  167. package/dist/highlight/index.d.ts +18 -0
  168. package/dist/highlight/index.js +103 -0
  169. package/dist/highlight/index.js.map +1 -0
  170. package/dist/index.d.ts +41 -5
  171. package/dist/index.js +123 -100
  172. package/dist/styles.css +1 -1
  173. package/dist/themes/fiesta.css +1 -1
  174. package/dist/themes/flightdeck.css +1 -1
  175. package/dist/themes/ledger.css +1 -1
  176. package/dist/themes/monograph.css +1 -1
  177. package/dist/themes/riso.css +1 -1
  178. package/dist/utils/newlines.d.ts +2 -0
  179. package/dist/utils/newlines.js +10 -0
  180. package/dist/utils/newlines.js.map +1 -0
  181. package/package.json +13 -2
  182. package/skills/custom-theme/SKILL.md +32 -0
  183. package/skills/custom-theme/references/colour.md +20 -0
  184. package/skills/custom-theme/references/motion.md +9 -0
  185. package/skills/custom-theme/references/tokens.md +35 -0
  186. package/skills/design-rules/SKILL.md +5 -2
  187. package/skills/setup-and-theming/SKILL.md +2 -1
  188. package/skills/setup-and-theming/references/paper.md +1 -1
@@ -0,0 +1,12 @@
1
+ //#region src/components/feedback/Indicator/Indicator.module.css
2
+ var e = "kiln-Indicator__indicator", t = "kiln-Indicator__mark", n = "kiln-Indicator__fill", r = "kiln-Indicator__ring", i = "kiln-Indicator__label", a = {
3
+ indicator: e,
4
+ mark: t,
5
+ fill: n,
6
+ ring: r,
7
+ label: i
8
+ };
9
+ //#endregion
10
+ export { a as default, n as fill, e as indicator, i as label, t as mark, r as ring };
11
+
12
+ //# sourceMappingURL=Indicator.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Indicator.module.js","names":["indicator","mark","fill","ring","label","Indicator_module_default"],"sources":["../../../../src/components/feedback/Indicator/Indicator.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --indicator-ring the gap drawn round the mark so it reads over its child,\n * default var(--color-surface)\n * --indicator-dot-size default var(--space-2)\n * --indicator-count-size height of a count, default var(--space-4)\n * --indicator-radius default var(--radius-chip)\n * --indicator-pulse-scale how far the processing ring swells, default 2.4\n *\n * The mark sits centred on the child's corner: the inset follows the child's outline (`shape`)\n * plus `offset`, and negative margins of half the mark's height centre it on that point. A count\n * grows inward from there, so it never needs a transform and RTL needs nothing extra.\n */\n.indicator {\n --_offset: 0px;\n --_inset: 0px;\n\n position: relative;\n display: inline-flex;\n flex: none;\n vertical-align: middle;\n}\n\n/* On a circle, the point of the outline nearest the corner is 1 − 1/√2 of the radius in. */\n.indicator[data-shape='round'] {\n --_inset: 14.645%;\n}\n.indicator[data-shape='avatar'] {\n --_inset: calc(var(--avatar-radius, var(--radius-avatar)) * 0.2929);\n}\n\n.mark {\n --_c: var(--color-accent);\n --_c-ink: var(--color-accent-ink);\n --_h: var(--indicator-dot-size, var(--space-2));\n --_edge: calc(var(--_inset) + var(--_offset));\n\n position: relative;\n display: inline-grid;\n place-items: center;\n box-sizing: border-box;\n min-inline-size: var(--_h);\n block-size: var(--_h);\n border-radius: var(--indicator-radius, var(--radius-chip));\n background: var(--_c);\n color: var(--_c-ink);\n box-shadow: 0 0 0 2px var(--indicator-ring, var(--color-surface));\n font-family: var(--font-numeric);\n font-stretch: var(--numeric-stretch);\n font-size: var(--text-2xs);\n font-weight: var(--label-weight);\n font-variant-numeric: tabular-nums lining-nums;\n line-height: 1;\n white-space: nowrap;\n pointer-events: none;\n}\n.mark[data-kind='count'] {\n --_h: var(--indicator-count-size, var(--space-4));\n\n padding-inline: calc(var(--_h) / 4);\n}\n.mark[data-kind='swatch'] {\n inline-size: var(--_h);\n background: none;\n}\n.mark > .fill {\n position: relative;\n inline-size: 100%;\n block-size: 100%;\n border-radius: inherit;\n}\n\n.indicator[data-pinned] > .mark {\n position: absolute;\n z-index: 1;\n}\n.indicator[data-position^='top'] > .mark {\n inset-block-start: var(--_edge);\n margin-block-start: calc(var(--_h) / -2);\n}\n.indicator[data-position^='bottom'] > .mark {\n inset-block-end: var(--_edge);\n margin-block-end: calc(var(--_h) / -2);\n}\n.indicator[data-position$='-start'] > .mark {\n inset-inline-start: var(--_edge);\n margin-inline-start: calc(var(--_h) / -2);\n}\n.indicator[data-position$='-end'] > .mark {\n inset-inline-end: var(--_edge);\n margin-inline-end: calc(var(--_h) / -2);\n}\n\n.mark[data-tone='neutral'] {\n --_c: var(--color-surface-inverse);\n --_c-ink: var(--color-ink-inverse);\n}\n.mark[data-tone='positive'] {\n --_c: var(--tone-positive);\n --_c-ink: var(--tone-positive-ink);\n}\n.mark[data-tone='caution'] {\n --_c: var(--tone-caution);\n --_c-ink: var(--tone-caution-ink);\n}\n.mark[data-tone='critical'] {\n --_c: var(--tone-critical);\n --_c-ink: var(--tone-critical-ink);\n}\n.mark[data-tone='info'] {\n --_c: var(--tone-info);\n --_c-ink: var(--tone-info-ink);\n}\n\n/* The same slow ring as LiveIndicator. */\n.mark[data-processing]:not([data-kind='swatch'])::after,\n.mark[data-processing] > .fill::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: currentColor;\n color: var(--_c);\n animation: ring calc(var(--dur-3) * 5) var(--ease-out) infinite;\n}\n.mark[data-processing] > .fill::after {\n background: inherit;\n}\n\n@keyframes ring {\n 0% {\n scale: 1;\n opacity: 0.5;\n }\n 70%,\n 100% {\n scale: var(--indicator-pulse-scale, 2.4);\n opacity: 0;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .mark[data-processing]:not([data-kind='swatch'])::after,\n .mark[data-processing] > .fill::after {\n animation: none;\n opacity: 0;\n }\n}\n\n/* Read through the child's aria-describedby; kept in the tree for reading order. */\n.label {\n position: absolute;\n inline-size: 1px;\n block-size: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n}\n\n@media (forced-colors: active) {\n .mark:not([data-kind='swatch']) {\n border: 1px solid CanvasText;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { Indicator } from './Indicator.js';
2
+ export type { IndicatorProps, IndicatorPosition, IndicatorShape } from './Indicator.js';
@@ -0,0 +1,71 @@
1
+ export type ToastPosition = 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end';
2
+ export interface ToasterProps {
3
+ /** Where the toasts stack. Default `bottom-end`. */
4
+ position?: ToastPosition;
5
+ /**
6
+ * How many toasts show at once. Older ones beyond it wait, with their timers stopped, and come
7
+ * back as the newer ones close. Default 3.
8
+ */
9
+ max?: number;
10
+ /**
11
+ * Names the region for screen readers. `{hotkey}` is replaced with the shortcut. Default
12
+ * "Notifications ({hotkey})".
13
+ */
14
+ label?: string;
15
+ /** The keys that move focus to the toasts. Default `['F8']`. */
16
+ hotkey?: string[];
17
+ /** Accessible name of each toast's close button. Default "Dismiss". */
18
+ closeLabel?: string;
19
+ /** Portal target. Defaults to `document.body`. */
20
+ container?: HTMLElement | null;
21
+ /** Class on the region the toasts stack in. */
22
+ className?: string;
23
+ }
24
+ /**
25
+ * The region that shows toasts. Mount it once near the root, then raise toasts with `toast()` from
26
+ * anywhere.
27
+ *
28
+ * @remarks
29
+ * A toast tells someone that something happened away from where they're looking: a save
30
+ * finished, a background job failed, a link landed on the clipboard. For a message about the
31
+ * content beside it, use an {@link Alert | Alert} instead; it stays in the layout.
32
+ *
33
+ * ```tsx
34
+ * <ThemeProvider>
35
+ * <App />
36
+ * <Toaster />
37
+ * </ThemeProvider>
38
+ * ```
39
+ *
40
+ * Each toast has the same frame as an `Alert`: a hairline, the tone in its glyph and a short tab
41
+ * on the top edge. It can carry one action, such as Undo, and a close button. It closes on its
42
+ * own after a few seconds; `critical` toasts and loading ones stay until they're dismissed. The
43
+ * timer pauses while the pointer or keyboard focus is on the toasts, and while the window is in
44
+ * the background.
45
+ *
46
+ * Toasts are announced as they appear, and again when one changes (a loading toast turning into
47
+ * "Saved"). `critical` and `caution` toasts interrupt; the rest wait their turn. F8 moves focus to
48
+ * the region, Escape closes the focused toast, and a swipe toward the nearest edge dismisses one.
49
+ * The toasts take the theme of the place the `Toaster` is mounted, and slide in from that edge;
50
+ * with reduced motion they appear and go without moving. If more than one `Toaster` is mounted,
51
+ * the first one shows the toasts.
52
+ *
53
+ * ## Updating and dismissing
54
+ *
55
+ * `toast()` returns an id. `toast.update(id, options)` changes that toast, `toast.dismiss(id)`
56
+ * closes it, and `toast.dismiss()` closes every toast. Raising a toast with an `id` that's still on
57
+ * screen updates it rather than stacking a copy.
58
+ *
59
+ * `toast.promise(promise, { loading, success, error })` shows a loading toast, then turns it into
60
+ * the success or error message when the promise settles.
61
+ *
62
+ * @privateRemarks
63
+ * Radix Toast underneath (swipe, hotkey, pause, live announcements). Radix announces a toast's
64
+ * text once, on mount, so a changed message bumps `version` and remounts the toast under a new
65
+ * key; `data-updated` stops that remount from replaying the entrance. The viewport is portalled
66
+ * and copies the theme of a hidden anchor where the Toaster sits (usePortalTheme).
67
+ *
68
+ * <Toaster position="bottom-end" />
69
+ * toast({ title: 'Invoice sent', tone: 'positive' })
70
+ */
71
+ export declare function Toaster({ position, max, label, hotkey, closeLabel, container, className, }: ToasterProps): import("react").JSX.Element | null;
@@ -0,0 +1,121 @@
1
+ import { cx as e } from "../../../utils/cx.js";
2
+ import { CircleCheckIcon as t, CloseIcon as n, ErrorIcon as r, InfoIcon as i, WarningIcon as a } from "../../../icons/icons.js";
3
+ import { Spinner as o } from "../Spinner/Spinner.js";
4
+ import { PortalAnchorContext as s, composeRefs as c, usePortalAnchor as l, usePortalAnchorRef as u, usePortalTheme as d } from "../../overlays/usePortalTheme.js";
5
+ import { getServerToasts as f, getToasts as p, primaryToaster as m, registerToaster as h, subscribeToasters as g, subscribeToasts as _, toast as v } from "./toast.js";
6
+ import y from "./Toaster.module.js";
7
+ import { forwardRef as b, useEffect as x, useState as S, useSyncExternalStore as C } from "react";
8
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
9
+ import { Portal as E, Toast as D } from "radix-ui";
10
+ //#region src/components/feedback/Toaster/Toaster.tsx
11
+ var O = {
12
+ neutral: /* @__PURE__ */ w(i, {}),
13
+ info: /* @__PURE__ */ w(i, {}),
14
+ positive: /* @__PURE__ */ w(t, {}),
15
+ caution: /* @__PURE__ */ w(a, {}),
16
+ critical: /* @__PURE__ */ w(r, {})
17
+ };
18
+ function k(e) {
19
+ if (e.duration !== void 0) return e.duration;
20
+ if (e.loading || e.tone === "critical") return Infinity;
21
+ if (e.tone === "caution") return 8e3;
22
+ }
23
+ function A(e) {
24
+ return e.endsWith("-end") ? "right" : e.endsWith("-start") ? "left" : e.startsWith("top") ? "up" : "down";
25
+ }
26
+ function j({ record: e, closeLabel: t }) {
27
+ let r = e.tone ?? "neutral", i = r === "critical" || r === "caution";
28
+ return /* @__PURE__ */ T(D.Root, {
29
+ className: y.toast,
30
+ open: e.open,
31
+ onOpenChange: (t) => {
32
+ t || v.dismiss(e.id);
33
+ },
34
+ duration: k(e),
35
+ type: i ? "foreground" : "background",
36
+ "data-tone": r,
37
+ "data-loading": e.loading === !0 ? "" : void 0,
38
+ "data-updated": e.version > 0 || void 0,
39
+ children: [
40
+ /* @__PURE__ */ w("span", {
41
+ className: y.glyph,
42
+ "aria-hidden": "true",
43
+ children: e.loading ? /* @__PURE__ */ w(o, { label: null }) : O[r]
44
+ }),
45
+ /* @__PURE__ */ T("div", {
46
+ className: y.body,
47
+ children: [
48
+ e.title === void 0 ? null : /* @__PURE__ */ w(D.Title, {
49
+ className: y.title,
50
+ children: e.title
51
+ }),
52
+ e.description === void 0 ? null : /* @__PURE__ */ w(D.Description, {
53
+ className: y.description,
54
+ children: e.description
55
+ }),
56
+ e.action ? /* @__PURE__ */ w("div", {
57
+ className: y.actions,
58
+ children: /* @__PURE__ */ w(D.Action, {
59
+ className: y.action,
60
+ altText: e.action.altText ?? e.action.label,
61
+ onClick: e.action.onClick,
62
+ children: e.action.label
63
+ })
64
+ }) : null
65
+ ]
66
+ }),
67
+ e.dismissible === !1 ? null : /* @__PURE__ */ w(D.Close, {
68
+ className: y.close,
69
+ "aria-label": t,
70
+ children: /* @__PURE__ */ w(n, {})
71
+ })
72
+ ]
73
+ });
74
+ }
75
+ var M = b(function({ position: t, label: n, hotkey: r, container: i, className: a }, o) {
76
+ let s = d(i);
77
+ return /* @__PURE__ */ w(D.Viewport, {
78
+ ref: o,
79
+ className: e(y.viewport, a),
80
+ "data-position": t,
81
+ label: n,
82
+ hotkey: r,
83
+ ...s
84
+ });
85
+ });
86
+ function N() {
87
+ let e = l();
88
+ return /* @__PURE__ */ w("span", {
89
+ ref: c(e ?? void 0),
90
+ hidden: !0
91
+ });
92
+ }
93
+ function P({ position: e = "bottom-end", max: t = 3, label: n = "Notifications ({hotkey})", hotkey: r, closeLabel: i = "Dismiss", container: a, className: o }) {
94
+ let c = u(), l = C(_, p, f), [d] = S(() => ({}));
95
+ if (x(() => h(d), [d]), C(g, m, () => void 0) !== d) return null;
96
+ let v = new Set(l.filter((e) => e.open).slice(-Math.max(1, t)).map((e) => e.id)), y = l.filter((e) => !e.open || v.has(e.id));
97
+ return /* @__PURE__ */ T(s.Provider, {
98
+ value: c,
99
+ children: [/* @__PURE__ */ w(N, {}), /* @__PURE__ */ T(D.Provider, {
100
+ swipeDirection: A(e),
101
+ label: "Notification",
102
+ children: [y.map((e) => /* @__PURE__ */ w(j, {
103
+ record: e,
104
+ closeLabel: i
105
+ }, `${e.id}:${String(e.version)}`)), /* @__PURE__ */ w(E.Root, {
106
+ container: a,
107
+ children: /* @__PURE__ */ w(M, {
108
+ position: e,
109
+ label: n,
110
+ hotkey: r,
111
+ container: a,
112
+ className: o
113
+ })
114
+ })]
115
+ })]
116
+ });
117
+ }
118
+ //#endregion
119
+ export { P as Toaster };
120
+
121
+ //# sourceMappingURL=Toaster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toaster.js","names":["styles","Toaster_module_default","GLYPH","InfoIcon","CircleCheckIcon","WarningIcon","ErrorIcon","durationOf","record","swipeDirection","position","ToastItem","tone","interrupt","Toast","open","toast","Spinner","closeLabel","CloseIcon","Viewport","forwardRef","theme","usePortalTheme","container","cx","className","Anchor","anchor","usePortalAnchor","composeRefs","Toaster","usePortalAnchorRef","records","useSyncExternalStore","subscribeToasts","getToasts","getServerToasts","token","useState","useEffect","registerToaster","subscribeToasters","primaryToaster","shown","max","visible","PortalAnchorContext","Portal"],"sources":["../../../../src/components/feedback/Toaster/Toaster.tsx"],"sourcesContent":["import { forwardRef, useEffect, useState, useSyncExternalStore, type ReactNode } from 'react'\nimport { Portal, Toast } from 'radix-ui'\nimport { cx } from '#utils/cx'\nimport { CircleCheckIcon, CloseIcon, ErrorIcon, InfoIcon, WarningIcon } from '#icons'\nimport { Spinner } from '#components/feedback/Spinner'\nimport {\n PortalAnchorContext,\n composeRefs,\n usePortalAnchor,\n usePortalAnchorRef,\n usePortalTheme,\n} from '#components/overlays/usePortalTheme'\nimport {\n getServerToasts,\n getToasts,\n primaryToaster,\n registerToaster,\n subscribeToasters,\n subscribeToasts,\n toast,\n type ToastRecord,\n type ToastTone,\n} from './toast'\nimport styles from './Toaster.module.css'\n\nexport type ToastPosition =\n 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end'\n\nexport interface ToasterProps {\n /** Where the toasts stack. Default `bottom-end`. */\n position?: ToastPosition\n /**\n * How many toasts show at once. Older ones beyond it wait, with their timers stopped, and come\n * back as the newer ones close. Default 3.\n */\n max?: number\n /**\n * Names the region for screen readers. `{hotkey}` is replaced with the shortcut. Default\n * \"Notifications ({hotkey})\".\n */\n label?: string\n /** The keys that move focus to the toasts. Default `['F8']`. */\n hotkey?: string[]\n /** Accessible name of each toast's close button. Default \"Dismiss\". */\n closeLabel?: string\n /** Portal target. Defaults to `document.body`. */\n container?: HTMLElement | null\n /** Class on the region the toasts stack in. */\n className?: string\n}\n\nconst GLYPH: Record<ToastTone, ReactNode> = {\n neutral: <InfoIcon />,\n info: <InfoIcon />,\n positive: <CircleCheckIcon />,\n caution: <WarningIcon />,\n critical: <ErrorIcon />,\n}\n\nfunction durationOf(record: ToastRecord): number | undefined {\n if (record.duration !== undefined) return record.duration\n if (record.loading || record.tone === 'critical') return Infinity\n if (record.tone === 'caution') return 8000\n return undefined\n}\n\nfunction swipeDirection(position: ToastPosition): 'up' | 'down' | 'left' | 'right' {\n if (position.endsWith('-end')) return 'right'\n if (position.endsWith('-start')) return 'left'\n return position.startsWith('top') ? 'up' : 'down'\n}\n\nfunction ToastItem({ record, closeLabel }: { record: ToastRecord; closeLabel: string }) {\n const tone = record.tone ?? 'neutral'\n const interrupt = tone === 'critical' || tone === 'caution'\n return (\n <Toast.Root\n className={styles.toast}\n open={record.open}\n onOpenChange={(open) => {\n if (!open) toast.dismiss(record.id)\n }}\n duration={durationOf(record)}\n type={interrupt ? 'foreground' : 'background'}\n data-tone={tone}\n data-loading={record.loading === true ? '' : undefined}\n data-updated={record.version > 0 || undefined}\n >\n <span className={styles.glyph} aria-hidden=\"true\">\n {record.loading ? <Spinner label={null} /> : GLYPH[tone]}\n </span>\n <div className={styles.body}>\n {record.title !== undefined ? (\n <Toast.Title className={styles.title}>{record.title}</Toast.Title>\n ) : null}\n {record.description !== undefined ? (\n <Toast.Description className={styles.description}>{record.description}</Toast.Description>\n ) : null}\n {record.action ? (\n <div className={styles.actions}>\n <Toast.Action\n className={styles.action}\n altText={record.action.altText ?? record.action.label}\n onClick={record.action.onClick}\n >\n {record.action.label}\n </Toast.Action>\n </div>\n ) : null}\n </div>\n {record.dismissible !== false ? (\n <Toast.Close className={styles.close} aria-label={closeLabel}>\n <CloseIcon />\n </Toast.Close>\n ) : null}\n </Toast.Root>\n )\n}\n\nconst Viewport = forwardRef<\n HTMLOListElement,\n {\n position: ToastPosition\n label: string\n hotkey?: string[]\n container?: HTMLElement | null\n className?: string\n }\n>(function Viewport({ position, label, hotkey, container, className }, ref) {\n const theme = usePortalTheme(container)\n return (\n <Toast.Viewport\n ref={ref}\n className={cx(styles.viewport, className)}\n data-position={position}\n label={label}\n hotkey={hotkey}\n {...theme}\n />\n )\n})\n\n/** Marks where the Toaster is mounted, so the toasts take the theme of that place. */\nfunction Anchor() {\n const anchor = usePortalAnchor()\n return <span ref={composeRefs(anchor ?? undefined)} hidden />\n}\n\n/**\n * The region that shows toasts. Mount it once near the root, then raise toasts with `toast()` from\n * anywhere.\n *\n * @remarks\n * A toast tells someone that something happened away from where they're looking: a save\n * finished, a background job failed, a link landed on the clipboard. For a message about the\n * content beside it, use an {@link Alert | Alert} instead; it stays in the layout.\n *\n * ```tsx\n * <ThemeProvider>\n * <App />\n * <Toaster />\n * </ThemeProvider>\n * ```\n *\n * Each toast has the same frame as an `Alert`: a hairline, the tone in its glyph and a short tab\n * on the top edge. It can carry one action, such as Undo, and a close button. It closes on its\n * own after a few seconds; `critical` toasts and loading ones stay until they're dismissed. The\n * timer pauses while the pointer or keyboard focus is on the toasts, and while the window is in\n * the background.\n *\n * Toasts are announced as they appear, and again when one changes (a loading toast turning into\n * \"Saved\"). `critical` and `caution` toasts interrupt; the rest wait their turn. F8 moves focus to\n * the region, Escape closes the focused toast, and a swipe toward the nearest edge dismisses one.\n * The toasts take the theme of the place the `Toaster` is mounted, and slide in from that edge;\n * with reduced motion they appear and go without moving. If more than one `Toaster` is mounted,\n * the first one shows the toasts.\n *\n * ## Updating and dismissing\n *\n * `toast()` returns an id. `toast.update(id, options)` changes that toast, `toast.dismiss(id)`\n * closes it, and `toast.dismiss()` closes every toast. Raising a toast with an `id` that's still on\n * screen updates it rather than stacking a copy.\n *\n * `toast.promise(promise, { loading, success, error })` shows a loading toast, then turns it into\n * the success or error message when the promise settles.\n *\n * @privateRemarks\n * Radix Toast underneath (swipe, hotkey, pause, live announcements). Radix announces a toast's\n * text once, on mount, so a changed message bumps `version` and remounts the toast under a new\n * key; `data-updated` stops that remount from replaying the entrance. The viewport is portalled\n * and copies the theme of a hidden anchor where the Toaster sits (usePortalTheme).\n *\n * <Toaster position=\"bottom-end\" />\n * toast({ title: 'Invoice sent', tone: 'positive' })\n */\nexport function Toaster({\n position = 'bottom-end',\n max = 3,\n label = 'Notifications ({hotkey})',\n hotkey,\n closeLabel = 'Dismiss',\n container,\n className,\n}: ToasterProps) {\n const anchor = usePortalAnchorRef()\n const records = useSyncExternalStore(subscribeToasts, getToasts, getServerToasts)\n const [token] = useState(() => ({}))\n useEffect(() => registerToaster(token), [token])\n const primary = useSyncExternalStore(subscribeToasters, primaryToaster, () => undefined)\n if (primary !== token) return null\n // The newest `max` open toasts show; closing ones stay until their exit has played.\n const shown = new Set(\n records\n .filter((record) => record.open)\n .slice(-Math.max(1, max))\n .map((record) => record.id),\n )\n const visible = records.filter((record) => !record.open || shown.has(record.id))\n return (\n <PortalAnchorContext.Provider value={anchor}>\n <Anchor />\n <Toast.Provider swipeDirection={swipeDirection(position)} label=\"Notification\">\n {visible.map((record) => (\n <ToastItem\n key={`${record.id}:${String(record.version)}`}\n record={record}\n closeLabel={closeLabel}\n />\n ))}\n <Portal.Root container={container}>\n <Viewport\n position={position}\n label={label}\n hotkey={hotkey}\n container={container}\n className={className}\n />\n </Portal.Root>\n </Toast.Provider>\n </PortalAnchorContext.Provider>\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ //#region src/components/feedback/Toaster/Toaster.module.css
2
+ var e = "kiln-Toaster__viewport", t = "kiln-Toaster__toast", n = "kiln-Toaster__glyph", r = "kiln-Toaster__body", i = "kiln-Toaster__title", a = "kiln-Toaster__description", o = "kiln-Toaster__actions", s = "kiln-Toaster__action", c = "kiln-Toaster__close", l = {
3
+ viewport: e,
4
+ toast: t,
5
+ glyph: n,
6
+ body: r,
7
+ title: i,
8
+ description: a,
9
+ actions: o,
10
+ action: s,
11
+ close: c,
12
+ "toast-in": "kiln-Toaster__toast-in",
13
+ "toast-out": "kiln-Toaster__toast-out",
14
+ "toast-swipe-out": "kiln-Toaster__toast-swipe-out",
15
+ "toast-fade-in": "kiln-Toaster__toast-fade-in"
16
+ };
17
+ //#endregion
18
+ export { s as action, o as actions, r as body, c as close, l as default, a as description, n as glyph, i as title, t as toast, e as viewport };
19
+
20
+ //# sourceMappingURL=Toaster.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toaster.module.js","names":["viewport","toast","glyph","body","title","description","actions","action","close","Toaster_module_default"],"sources":["../../../../src/components/feedback/Toaster/Toaster.module.css"],"sourcesContent":["/*\n * Component tokens (themes may set any of these on [data-theme]):\n * --toast-width default 24rem (capped by the viewport)\n * --toast-radius default var(--radius-surface)\n * --toast-bg default var(--color-surface-raised)\n * --toast-edge default var(--color-line-strong)\n * --toast-rule-length the tone tab on the top edge, default var(--space-6)\n *\n * A toast floats, so it takes the floating shadow; otherwise it's the Alert frame: a hairline,\n * the tone only in the glyph and a short tab set into the top edge. No tinted box, no stripe.\n * It slides in from the edge it's pinned to (the same edge a swipe sends it back to) and fades\n * out; reduced motion keeps only the fade, which the collapsed durations make instant.\n */\n.viewport {\n --_gap: var(--space-3);\n --_edge-gap: var(--space-5);\n\n position: fixed;\n z-index: var(--z-toast);\n display: flex;\n flex-direction: column;\n gap: var(--_gap);\n inline-size: min(var(--toast-width, 24rem), calc(100vw - 2 * var(--_edge-gap)));\n max-block-size: 100vh;\n margin: 0;\n padding: 0;\n list-style: none;\n outline: none;\n}\n.viewport:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 4px;\n}\n\n.viewport[data-position^='top'] {\n inset-block-start: var(--_edge-gap);\n flex-direction: column-reverse;\n}\n.viewport[data-position^='bottom'] {\n inset-block-end: var(--_edge-gap);\n}\n.viewport[data-position$='-start'] {\n inset-inline-start: var(--_edge-gap);\n}\n.viewport[data-position$='-end'] {\n inset-inline-end: var(--_edge-gap);\n}\n.viewport[data-position$='-center'] {\n inset-inline-start: 50%;\n translate: -50% 0;\n}\n\n.toast {\n --_c: var(--tone-info);\n --_text: var(--tone-info-text);\n --_from-x: 0px;\n --_from-y: 0px;\n\n position: relative;\n display: grid;\n grid-template-columns: auto minmax(0, 1fr) auto;\n align-items: start;\n column-gap: var(--space-3);\n padding-block: var(--space-3);\n padding-inline: var(--space-4);\n border: var(--border-width) solid var(--toast-edge, var(--color-line-strong));\n border-radius: var(--toast-radius, var(--radius-surface));\n background: var(--toast-bg, var(--color-surface-raised));\n box-shadow: var(--shadow-float);\n color: var(--color-ink);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n line-height: var(--leading-body);\n}\n.toast:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n}\n\n.toast::before {\n content: '';\n position: absolute;\n inset-block-start: calc(var(--border-width) * -1);\n inset-inline-start: var(--space-4);\n inline-size: var(--toast-rule-length, var(--space-6));\n block-size: calc(var(--border-width) + 2px);\n background: var(--_c);\n}\n\n.toast[data-tone='neutral'] {\n --_c: var(--color-ink-subtle);\n --_text: var(--color-ink-muted);\n}\n.toast[data-tone='positive'] {\n --_c: var(--tone-positive);\n --_text: var(--tone-positive-text);\n}\n.toast[data-tone='caution'] {\n --_c: var(--tone-caution);\n --_text: var(--tone-caution-text);\n}\n.toast[data-tone='critical'] {\n --_c: var(--tone-critical);\n --_text: var(--tone-critical-text);\n}\n\n.glyph {\n display: inline-flex;\n block-size: calc(var(--text-md) * var(--leading-snug));\n align-items: center;\n color: var(--_text);\n font-size: var(--text-lg);\n}\n.toast[data-loading] .glyph {\n color: var(--color-ink-muted);\n font-size: var(--text-md);\n}\n\n.body {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n min-inline-size: 0;\n}\n\n.title {\n margin: 0;\n color: var(--color-ink);\n font-family: var(--heading-font);\n font-size: var(--text-md);\n font-weight: var(--heading-weight);\n letter-spacing: var(--heading-tracking);\n text-transform: var(--heading-transform);\n line-height: var(--leading-snug);\n text-wrap: balance;\n}\n\n.description {\n margin: 0;\n color: var(--color-ink-muted);\n text-wrap: pretty;\n}\n.body > .description:first-child {\n color: var(--color-ink);\n}\n\n.actions {\n display: flex;\n gap: var(--space-2);\n margin-block-start: var(--space-2);\n}\n\n.action {\n display: inline-flex;\n align-items: center;\n min-block-size: var(--control-sm);\n padding-inline: var(--space-3);\n border: var(--border-width) solid var(--color-line-control);\n border-radius: var(--radius-action);\n background: transparent;\n color: var(--color-ink);\n font-family: var(--font-text);\n font-size: var(--text-sm);\n font-weight: var(--label-weight);\n letter-spacing: var(--label-tracking);\n text-transform: var(--label-transform);\n cursor: pointer;\n transition: background-color var(--dur-1) var(--ease-out);\n}\n@media (hover: hover) {\n .action:hover {\n background: var(--color-surface-sunken);\n }\n}\n.action:focus-visible,\n.close:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n}\n\n.close {\n display: grid;\n place-items: center;\n inline-size: var(--control-sm);\n block-size: var(--control-sm);\n margin-block: calc(var(--space-2) * -1);\n margin-inline-end: calc(var(--space-2) * -1);\n padding: 0;\n border: 0;\n border-radius: var(--radius-action);\n background: transparent;\n color: var(--color-ink-muted);\n font-size: var(--text-md);\n cursor: pointer;\n transition:\n background-color var(--dur-1) var(--ease-out),\n color var(--dur-1) var(--ease-out);\n}\n@media (hover: hover) {\n .close:hover {\n background: var(--color-surface-sunken);\n color: var(--color-ink);\n }\n}\n\n/* ── Motion: in from the pinned edge, out by fading; a swipe follows the finger ─────── */\n.toast[data-swipe-direction='right'] {\n --_from-x: var(--space-7);\n}\n.toast[data-swipe-direction='left'] {\n --_from-x: calc(var(--space-7) * -1);\n}\n.toast[data-swipe-direction='up'] {\n --_from-y: calc(var(--space-7) * -1);\n}\n.toast[data-swipe-direction='down'] {\n --_from-y: var(--space-7);\n}\n\n.toast[data-state='open'] {\n animation: toast-in var(--dur-3) var(--ease-out);\n}\n.toast[data-state='open'][data-updated] {\n animation: none;\n}\n.toast[data-state='closed'] {\n animation: toast-out var(--dur-2) var(--ease-in-out) forwards;\n}\n.toast[data-swipe='move'] {\n translate: var(--radix-toast-swipe-move-x, 0px) var(--radix-toast-swipe-move-y, 0px);\n}\n.toast[data-swipe='cancel'] {\n translate: 0 0;\n transition: translate var(--dur-2) var(--ease-out);\n}\n.toast[data-swipe='end'] {\n animation: toast-swipe-out var(--dur-2) var(--ease-out) forwards;\n}\n\n@keyframes toast-in {\n from {\n opacity: 0;\n translate: var(--_from-x) var(--_from-y);\n }\n}\n@keyframes toast-out {\n to {\n opacity: 0;\n }\n}\n@keyframes toast-swipe-out {\n from {\n translate: var(--radix-toast-swipe-end-x, 0px) var(--radix-toast-swipe-end-y, 0px);\n }\n to {\n opacity: 0;\n translate: calc(var(--radix-toast-swipe-end-x, 0px) + var(--_from-x))\n calc(var(--radix-toast-swipe-end-y, 0px) + var(--_from-y));\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .toast[data-state='open'] {\n animation-name: toast-fade-in;\n }\n .toast[data-swipe='end'] {\n animation-name: toast-out;\n }\n .toast[data-swipe='cancel'] {\n transition: none;\n }\n}\n@keyframes toast-fade-in {\n from {\n opacity: 0;\n }\n}\n"],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export { Toaster } from './Toaster.js';
2
+ export type { ToasterProps, ToastPosition } from './Toaster.js';
3
+ export { toast } from './toast.js';
4
+ export type { ToastOptions, ToastTone, ToastAction, ToastPromiseMessage, ToastPromiseMessages, } from './toast.js';
@@ -0,0 +1,96 @@
1
+ import { type ReactNode } from 'react';
2
+ export type ToastTone = 'neutral' | 'info' | 'positive' | 'caution' | 'critical';
3
+ export interface ToastAction {
4
+ /** The button's label: "Undo", "Retry", "View invoice". */
5
+ label: string;
6
+ /** Runs when the button is pressed. The toast closes afterwards. */
7
+ onClick: () => void;
8
+ /**
9
+ * What a screen reader user hears about how to do this without the toast, since it may have
10
+ * gone by the time they reach it: "Undo from the Trash page". Default: the label.
11
+ */
12
+ altText?: string;
13
+ }
14
+ export interface ToastOptions {
15
+ /**
16
+ * Names the toast. Raising a toast with the `id` of one still on screen updates it instead of
17
+ * stacking a second, so a repeated "Sync failed" shows once.
18
+ */
19
+ id?: string;
20
+ /** The short message, read first: "Invoice sent". */
21
+ title?: ReactNode;
22
+ /** One more sentence when the title needs it: "Kittiwake Ltd, £1,240.00". */
23
+ description?: ReactNode;
24
+ /** Meaning: the glyph and the tab on the top edge. `critical` and `caution` interrupt. Default `neutral`. */
25
+ tone?: ToastTone;
26
+ /** Shows a spinner in place of the glyph, for work still in progress. */
27
+ loading?: boolean;
28
+ /** One button, such as Undo or Retry. */
29
+ action?: ToastAction;
30
+ /**
31
+ * How long it stays, in milliseconds. `Infinity` keeps it until it's dismissed. Default 5000,
32
+ * 8000 for `caution`, and `Infinity` for `critical` and `loading`, so errors wait to be read.
33
+ * The timer pauses while the pointer or focus is on the toasts, and while the window is in the
34
+ * background.
35
+ */
36
+ duration?: number;
37
+ /** Show the close button. Default `true`. */
38
+ dismissible?: boolean;
39
+ /** Runs when the toast closes: on a timer, a swipe, Escape or the close button. */
40
+ onDismiss?: () => void;
41
+ }
42
+ /** A toast as the `Toaster` sees it. */
43
+ export interface ToastRecord extends ToastOptions {
44
+ id: string;
45
+ /** `false` once it's dismissed: it plays its exit and is then removed. */
46
+ open: boolean;
47
+ /** Goes up each time the message changes, so the change is announced again. */
48
+ version: number;
49
+ }
50
+ type Listener = () => void;
51
+ /** @internal The `Toaster`'s subscription. */
52
+ export declare function subscribeToasts(listener: Listener): () => void;
53
+ /** @internal The current toasts, oldest first. */
54
+ export declare function getToasts(): ToastRecord[];
55
+ /** @internal The server snapshot: nothing is raised during a server render. */
56
+ export declare function getServerToasts(): ToastRecord[];
57
+ /** A message for one stage of `toast.promise`: text, full options, or either worked out from the result. */
58
+ export type ToastPromiseMessage<T> = ReactNode | ToastOptions | ((value: T) => ReactNode | ToastOptions);
59
+ export interface ToastPromiseMessages<T> {
60
+ /** While it's pending. Shown with a spinner. */
61
+ loading: ReactNode | ToastOptions;
62
+ /** When it resolves. Shown in the `positive` tone. */
63
+ success: ToastPromiseMessage<T>;
64
+ /** When it rejects, with the error. Shown in the `critical` tone. */
65
+ error: ToastPromiseMessage<unknown>;
66
+ }
67
+ /**
68
+ * Raises a toast and returns its id. Takes the message as text, or full options.
69
+ *
70
+ * @remarks
71
+ * Call it from anywhere, inside React or out of it: an event handler, a query's error callback, a
72
+ * socket listener. A {@link Toaster | Toaster} mounted once near the root shows it.
73
+ *
74
+ * - `toast.update(id, options)` changes a toast on screen, such as a retry count.
75
+ * - `toast.dismiss(id)` closes one; `toast.dismiss()` closes them all.
76
+ * - `toast.promise(promise, { loading, success, error })` shows a loading toast that turns into
77
+ * the success or error message when the promise settles, and returns the same promise.
78
+ */
79
+ export declare function toast(message: ReactNode | ToastOptions): string;
80
+ export declare namespace toast {
81
+ var update: (id: string, options: ToastOptions) => void;
82
+ var dismiss: (id?: string) => void;
83
+ var promise: <T>(promise: Promise<T>, messages: ToastPromiseMessages<T>) => Promise<T>;
84
+ }
85
+ /**
86
+ * @internal Registers a mounted `Toaster`. Only the first one registered shows toasts, so a page
87
+ * that mounts two (a docs page with several examples) doesn't show each toast twice.
88
+ */
89
+ export declare function registerToaster(token: object): () => void;
90
+ /** @internal Subscribes to changes in which `Toaster` shows toasts. */
91
+ export declare function subscribeToasters(listener: Listener): () => void;
92
+ /** @internal The `Toaster` that shows toasts, if any is mounted. */
93
+ export declare function primaryToaster(): object | undefined;
94
+ /** @internal Clears every toast at once, with no exit. For tests. */
95
+ export declare function resetToasts(): void;
96
+ export {};
@@ -0,0 +1,131 @@
1
+ import { isValidElement as e } from "react";
2
+ //#region src/components/feedback/Toaster/toast.ts
3
+ var t = [], n = /* @__PURE__ */ new Set(), r = 0, i = 1e3;
4
+ function a(e) {
5
+ t = e;
6
+ for (let e of n) e();
7
+ }
8
+ function o(e) {
9
+ return n.add(e), () => {
10
+ n.delete(e);
11
+ };
12
+ }
13
+ function s() {
14
+ return t;
15
+ }
16
+ var c = [];
17
+ function l() {
18
+ return c;
19
+ }
20
+ function u(e, t) {
21
+ return [
22
+ "title",
23
+ "description",
24
+ "tone",
25
+ "loading"
26
+ ].some((n) => n in t && t[n] !== e[n]);
27
+ }
28
+ function d(e) {
29
+ let n = e.id ?? `toast-${String(++r)}`, i = t.find((e) => e.id === n && e.open);
30
+ if (i) {
31
+ let r = u(i, e) ? i.version + 1 : i.version;
32
+ a(t.map((t) => t === i ? {
33
+ ...t,
34
+ ...e,
35
+ id: n,
36
+ version: r
37
+ } : t));
38
+ } else a([...t.filter((e) => e.id !== n), {
39
+ ...e,
40
+ id: n,
41
+ open: !0,
42
+ version: 0
43
+ }]);
44
+ return n;
45
+ }
46
+ function f(e) {
47
+ let n = t.filter((t) => t.open && (e === void 0 || t.id === e));
48
+ if (n.length !== 0) {
49
+ a(t.map((e) => n.includes(e) ? {
50
+ ...e,
51
+ open: !1
52
+ } : e));
53
+ for (let e of n) e.onDismiss?.();
54
+ setTimeout(() => {
55
+ a(t.filter((e) => !n.some((t) => t.id === e.id && !e.open)));
56
+ }, i);
57
+ }
58
+ }
59
+ function p(t) {
60
+ return typeof t == "object" && !!t && !e(t) && !Array.isArray(t);
61
+ }
62
+ function m(e, t) {
63
+ let n = typeof e == "function" ? e(t) : e;
64
+ return p(n) ? n : { title: n };
65
+ }
66
+ function h(e) {
67
+ return d(p(e) ? e : { title: e });
68
+ }
69
+ h.update = (e, n) => {
70
+ t.some((t) => t.id === e && t.open) && d({
71
+ ...n,
72
+ id: e
73
+ });
74
+ }, h.dismiss = (e) => {
75
+ f(e);
76
+ }, h.promise = (e, t) => {
77
+ let n = d({
78
+ ...m(t.loading, void 0),
79
+ loading: !0,
80
+ dismissible: !1
81
+ });
82
+ return e.then((e) => {
83
+ d({
84
+ tone: "positive",
85
+ duration: void 0,
86
+ title: void 0,
87
+ description: void 0,
88
+ action: void 0,
89
+ onDismiss: void 0,
90
+ ...m(t.success, e),
91
+ id: n,
92
+ loading: !1,
93
+ dismissible: !0
94
+ });
95
+ }, (e) => {
96
+ d({
97
+ tone: "critical",
98
+ duration: void 0,
99
+ title: void 0,
100
+ description: void 0,
101
+ action: void 0,
102
+ onDismiss: void 0,
103
+ ...m(t.error, e),
104
+ id: n,
105
+ loading: !1,
106
+ dismissible: !0
107
+ });
108
+ }), e;
109
+ };
110
+ var g = [], _ = /* @__PURE__ */ new Set();
111
+ function v(e) {
112
+ g.push(e);
113
+ for (let e of _) e();
114
+ return () => {
115
+ let t = g.indexOf(e);
116
+ t >= 0 && g.splice(t, 1);
117
+ for (let e of _) e();
118
+ };
119
+ }
120
+ function y(e) {
121
+ return _.add(e), () => {
122
+ _.delete(e);
123
+ };
124
+ }
125
+ function b() {
126
+ return g[0];
127
+ }
128
+ //#endregion
129
+ export { l as getServerToasts, s as getToasts, b as primaryToaster, v as registerToaster, y as subscribeToasters, o as subscribeToasts, h as toast };
130
+
131
+ //# sourceMappingURL=toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.js","names":["toasts","listeners","nextId","REMOVE_AFTER_MS","emit","next","listener","subscribeToasts","getToasts","EMPTY","getServerToasts","changesMessage","key","options","record","upsert","id","existing","version","close","closing","closed","isOptions","message","isValidElement","toOptions","resolved","value","toast","messages","promise","error","toasters","toasterListeners","registerToaster","token","index","subscribeToasters","primaryToaster"],"sources":["../../../../src/components/feedback/Toaster/toast.ts"],"sourcesContent":["import { isValidElement, type ReactNode } from 'react'\n\nexport type ToastTone = 'neutral' | 'info' | 'positive' | 'caution' | 'critical'\n\nexport interface ToastAction {\n /** The button's label: \"Undo\", \"Retry\", \"View invoice\". */\n label: string\n /** Runs when the button is pressed. The toast closes afterwards. */\n onClick: () => void\n /**\n * What a screen reader user hears about how to do this without the toast, since it may have\n * gone by the time they reach it: \"Undo from the Trash page\". Default: the label.\n */\n altText?: string\n}\n\nexport interface ToastOptions {\n /**\n * Names the toast. Raising a toast with the `id` of one still on screen updates it instead of\n * stacking a second, so a repeated \"Sync failed\" shows once.\n */\n id?: string\n /** The short message, read first: \"Invoice sent\". */\n title?: ReactNode\n /** One more sentence when the title needs it: \"Kittiwake Ltd, £1,240.00\". */\n description?: ReactNode\n /** Meaning: the glyph and the tab on the top edge. `critical` and `caution` interrupt. Default `neutral`. */\n tone?: ToastTone\n /** Shows a spinner in place of the glyph, for work still in progress. */\n loading?: boolean\n /** One button, such as Undo or Retry. */\n action?: ToastAction\n /**\n * How long it stays, in milliseconds. `Infinity` keeps it until it's dismissed. Default 5000,\n * 8000 for `caution`, and `Infinity` for `critical` and `loading`, so errors wait to be read.\n * The timer pauses while the pointer or focus is on the toasts, and while the window is in the\n * background.\n */\n duration?: number\n /** Show the close button. Default `true`. */\n dismissible?: boolean\n /** Runs when the toast closes: on a timer, a swipe, Escape or the close button. */\n onDismiss?: () => void\n}\n\n/** A toast as the `Toaster` sees it. */\nexport interface ToastRecord extends ToastOptions {\n id: string\n /** `false` once it's dismissed: it plays its exit and is then removed. */\n open: boolean\n /** Goes up each time the message changes, so the change is announced again. */\n version: number\n}\n\ntype Listener = () => void\n\nlet toasts: ToastRecord[] = []\nconst listeners = new Set<Listener>()\nlet nextId = 0\n\nconst REMOVE_AFTER_MS = 1000\n\nfunction emit(next: ToastRecord[]) {\n toasts = next\n for (const listener of listeners) listener()\n}\n\n/** @internal The `Toaster`'s subscription. */\nexport function subscribeToasts(listener: Listener): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** @internal The current toasts, oldest first. */\nexport function getToasts(): ToastRecord[] {\n return toasts\n}\n\nconst EMPTY: ToastRecord[] = []\n/** @internal The server snapshot: nothing is raised during a server render. */\nexport function getServerToasts(): ToastRecord[] {\n return EMPTY\n}\n\nfunction changesMessage(record: ToastRecord, options: ToastOptions): boolean {\n return (['title', 'description', 'tone', 'loading'] as const).some(\n (key) => key in options && options[key] !== record[key],\n )\n}\n\nfunction upsert(options: ToastOptions): string {\n const id = options.id ?? `toast-${String(++nextId)}`\n const existing = toasts.find((record) => record.id === id && record.open)\n if (existing) {\n const version = changesMessage(existing, options) ? existing.version + 1 : existing.version\n emit(\n toasts.map((record) =>\n record === existing ? { ...record, ...options, id, version } : record,\n ),\n )\n } else {\n emit([\n ...toasts.filter((record) => record.id !== id),\n { ...options, id, open: true, version: 0 },\n ])\n }\n return id\n}\n\nfunction close(id?: string) {\n const closing = toasts.filter((record) => record.open && (id === undefined || record.id === id))\n if (closing.length === 0) return\n emit(toasts.map((record) => (closing.includes(record) ? { ...record, open: false } : record)))\n for (const record of closing) record.onDismiss?.()\n // Long enough for the exit animation; then the record goes.\n setTimeout(() => {\n emit(\n toasts.filter((record) => !closing.some((closed) => closed.id === record.id && !record.open)),\n )\n }, REMOVE_AFTER_MS)\n}\n\n/** A message for one stage of `toast.promise`: text, full options, or either worked out from the result. */\nexport type ToastPromiseMessage<T> =\n ReactNode | ToastOptions | ((value: T) => ReactNode | ToastOptions)\n\nexport interface ToastPromiseMessages<T> {\n /** While it's pending. Shown with a spinner. */\n loading: ReactNode | ToastOptions\n /** When it resolves. Shown in the `positive` tone. */\n success: ToastPromiseMessage<T>\n /** When it rejects, with the error. Shown in the `critical` tone. */\n error: ToastPromiseMessage<unknown>\n}\n\nfunction isOptions(message: unknown): message is ToastOptions {\n return (\n typeof message === 'object' &&\n message !== null &&\n !isValidElement(message) &&\n !Array.isArray(message)\n )\n}\n\nfunction toOptions<T>(message: ToastPromiseMessage<T>, value: T): ToastOptions {\n const resolved = typeof message === 'function' ? message(value) : message\n return isOptions(resolved) ? resolved : { title: resolved }\n}\n\n/**\n * Raises a toast and returns its id. Takes the message as text, or full options.\n *\n * @remarks\n * Call it from anywhere, inside React or out of it: an event handler, a query's error callback, a\n * socket listener. A {@link Toaster | Toaster} mounted once near the root shows it.\n *\n * - `toast.update(id, options)` changes a toast on screen, such as a retry count.\n * - `toast.dismiss(id)` closes one; `toast.dismiss()` closes them all.\n * - `toast.promise(promise, { loading, success, error })` shows a loading toast that turns into\n * the success or error message when the promise settles, and returns the same promise.\n */\nexport function toast(message: ReactNode | ToastOptions): string {\n return upsert(isOptions(message) ? message : { title: message })\n}\n\n/** Changes a toast that's on screen. Does nothing if it has gone. */\ntoast.update = (id: string, options: ToastOptions): void => {\n if (toasts.some((record) => record.id === id && record.open)) upsert({ ...options, id })\n}\n\n/** Closes one toast, or every toast when no id is given. */\ntoast.dismiss = (id?: string): void => {\n close(id)\n}\n\n/**\n * Shows a loading toast while `promise` is pending, then the success or error message in its\n * place. Returns `promise`, so you can still await it.\n */\ntoast.promise = <T>(promise: Promise<T>, messages: ToastPromiseMessages<T>): Promise<T> => {\n const id = upsert({\n ...toOptions(messages.loading, undefined),\n loading: true,\n dismissible: false,\n })\n promise.then(\n (value) => {\n upsert({\n tone: 'positive',\n duration: undefined,\n title: undefined,\n description: undefined,\n action: undefined,\n onDismiss: undefined,\n ...toOptions(messages.success, value),\n id,\n loading: false,\n dismissible: true,\n })\n },\n (error: unknown) => {\n upsert({\n tone: 'critical',\n duration: undefined,\n title: undefined,\n description: undefined,\n action: undefined,\n onDismiss: undefined,\n ...toOptions(messages.error, error),\n id,\n loading: false,\n dismissible: true,\n })\n },\n )\n return promise\n}\n\nconst toasters: object[] = []\nconst toasterListeners = new Set<Listener>()\n\n/**\n * @internal Registers a mounted `Toaster`. Only the first one registered shows toasts, so a page\n * that mounts two (a docs page with several examples) doesn't show each toast twice.\n */\nexport function registerToaster(token: object): () => void {\n toasters.push(token)\n for (const listener of toasterListeners) listener()\n return () => {\n const index = toasters.indexOf(token)\n if (index >= 0) toasters.splice(index, 1)\n for (const listener of toasterListeners) listener()\n }\n}\n\n/** @internal Subscribes to changes in which `Toaster` shows toasts. */\nexport function subscribeToasters(listener: Listener): () => void {\n toasterListeners.add(listener)\n return () => {\n toasterListeners.delete(listener)\n }\n}\n\n/** @internal The `Toaster` that shows toasts, if any is mounted. */\nexport function primaryToaster(): object | undefined {\n return toasters[0]\n}\n\n/** @internal Clears every toast at once, with no exit. For tests. */\nexport function resetToasts(): void {\n emit([])\n}\n"],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import type { DateRangeValue } from '../DateRangeField/index.js';
3
+ /** Accessible names of the calendar's own buttons. */
4
+ export interface CalendarLabels {
5
+ /** Default `'Previous month'`. */
6
+ previousMonth: string;
7
+ /** Default `'Next month'`. */
8
+ nextMonth: string;
9
+ }
10
+ interface CalendarCommonProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'> {
11
+ /** Earliest date that can be picked (`YYYY-MM-DD`). */
12
+ min?: string;
13
+ /** Latest date that can be picked (`YYYY-MM-DD`). */
14
+ max?: string;
15
+ /** Marks other dates unavailable: weekends, public holidays, booked nights. */
16
+ isDateDisabled?: (date: string) => boolean;
17
+ /** 0 (Sunday) to 6 (Saturday). Defaults to the locale's own week. */
18
+ firstDayOfWeek?: number;
19
+ /** BCP 47 locale for month and weekday names. Default `en-AU`. */
20
+ locale?: string;
21
+ /** The zone "today" is read in. Defaults to the device's. */
22
+ timeZone?: string;
23
+ /** How many months to show side by side (they wrap when narrow). Default `1`. */
24
+ months?: 1 | 2;
25
+ /** The first month shown (`YYYY-MM`). Controlled. */
26
+ month?: string;
27
+ /** The first month shown at first (`YYYY-MM`). Defaults to the selected or current month. */
28
+ defaultMonth?: string;
29
+ onMonthChange?: (month: string) => void;
30
+ disabled?: boolean;
31
+ /** Dates can be focused and read but not picked. */
32
+ readOnly?: boolean;
33
+ /** Focus the current day once mounted, as a picker's popover does. */
34
+ autoFocus?: boolean;
35
+ labels?: Partial<CalendarLabels>;
36
+ }
37
+ export interface CalendarSingleProps extends CalendarCommonProps {
38
+ /** `single` (default) picks one date; `range` picks a start and an end. */
39
+ mode?: 'single';
40
+ /** `YYYY-MM-DD`, or `''` for none. */
41
+ value?: string;
42
+ defaultValue?: string;
43
+ onValueChange?: (value: string) => void;
44
+ }
45
+ export interface CalendarRangeProps extends CalendarCommonProps {
46
+ mode: 'range';
47
+ /** The first click sets `start` (and clears `end`), the second sets `end`. */
48
+ value?: DateRangeValue;
49
+ defaultValue?: DateRangeValue;
50
+ onValueChange?: (value: DateRangeValue) => void;
51
+ }
52
+ export type CalendarProps = CalendarSingleProps | CalendarRangeProps;
53
+ /**
54
+ * A month grid for picking a date or a range, with the full keyboard of a date grid.
55
+ *
56
+ * @remarks
57
+ * `Calendar` shows one or two months as a grid of days. Click a day or move with the arrow keys
58
+ * and press Enter. In `range` mode the first pick is the start and the second the end, with the
59
+ * days between shaded as you go. `min`, `max` and `isDateDisabled` mark days that can't be picked;
60
+ * they stay focusable, so a screen reader can still say why a date is missing. Month and weekday
61
+ * names come from `locale`, and so does the first day of the week unless you set
62
+ * `firstDayOfWeek`.
63
+ *
64
+ * {@link DateInput | DateInput}, {@link DateTimeInput | DateTimeInput} and
65
+ * {@link DateRangePickerField | DateRangePickerField} open it in a popover. Use it on its own for a calendar
66
+ * that's always visible, such as a booking page.
67
+ *
68
+ * ## Keyboard
69
+ *
70
+ * | Key | Moves to |
71
+ * | --- | --- |
72
+ * | Arrow keys | The previous or next day, or the same day in the previous or next week |
73
+ * | Home / End | The first or last day of the week |
74
+ * | Page Up / Page Down | The same day in the previous or next month |
75
+ * | Shift + Page Up / Page Down | The same day in the previous or next year |
76
+ * | Enter / Space | Picks the focused day |
77
+ *
78
+ * @privateRemarks
79
+ * ARIA grid pattern: one `<table role="grid">` per month, labelled by its title, with a roving
80
+ * tabindex on the day cells (the focused date is the only tab stop). Cells outside the month are
81
+ * empty, so two months side by side never show a date twice. Values are ISO strings; all date
82
+ * maths goes through `internal/dates` (ADR 0042).
83
+ *
84
+ * <Calendar mode="range" months={2} value={stay} onValueChange={setStay} />
85
+ */
86
+ export declare const Calendar: import("react").ForwardRefExoticComponent<CalendarProps & import("react").RefAttributes<HTMLDivElement>>;
87
+ export {};