@c9up/nebula 0.1.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 (327) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +256 -0
  3. package/dist/adapters/css.d.ts +33 -0
  4. package/dist/adapters/css.js +67 -0
  5. package/dist/adapters/index.d.ts +16 -0
  6. package/dist/adapters/index.js +27 -0
  7. package/dist/adapters/tailwind.d.ts +25 -0
  8. package/dist/adapters/tailwind.js +122 -0
  9. package/dist/adapters/types.d.ts +49 -0
  10. package/dist/adapters/types.js +21 -0
  11. package/dist/adapters/unocss.d.ts +17 -0
  12. package/dist/adapters/unocss.js +107 -0
  13. package/dist/atoms/AspectRatio.d.ts +17 -0
  14. package/dist/atoms/AspectRatio.js +19 -0
  15. package/dist/atoms/Avatar.d.ts +23 -0
  16. package/dist/atoms/Avatar.js +46 -0
  17. package/dist/atoms/Badge.d.ts +25 -0
  18. package/dist/atoms/Badge.js +28 -0
  19. package/dist/atoms/Button.d.ts +58 -0
  20. package/dist/atoms/Button.js +67 -0
  21. package/dist/atoms/Checkbox.d.ts +41 -0
  22. package/dist/atoms/Checkbox.js +47 -0
  23. package/dist/atoms/Input.d.ts +36 -0
  24. package/dist/atoms/Input.js +50 -0
  25. package/dist/atoms/Kbd.d.ts +14 -0
  26. package/dist/atoms/Kbd.js +17 -0
  27. package/dist/atoms/Label.d.ts +24 -0
  28. package/dist/atoms/Label.js +26 -0
  29. package/dist/atoms/Marker.d.ts +42 -0
  30. package/dist/atoms/Marker.js +55 -0
  31. package/dist/atoms/NativeSelect.d.ts +43 -0
  32. package/dist/atoms/NativeSelect.js +81 -0
  33. package/dist/atoms/Progress.d.ts +21 -0
  34. package/dist/atoms/Progress.js +48 -0
  35. package/dist/atoms/ScrollArea.d.ts +28 -0
  36. package/dist/atoms/ScrollArea.js +37 -0
  37. package/dist/atoms/Separator.d.ts +16 -0
  38. package/dist/atoms/Separator.js +21 -0
  39. package/dist/atoms/Skeleton.d.ts +13 -0
  40. package/dist/atoms/Skeleton.js +18 -0
  41. package/dist/atoms/Slider.d.ts +30 -0
  42. package/dist/atoms/Slider.js +40 -0
  43. package/dist/atoms/Spinner.d.ts +17 -0
  44. package/dist/atoms/Spinner.js +20 -0
  45. package/dist/atoms/Switch.d.ts +24 -0
  46. package/dist/atoms/Switch.js +41 -0
  47. package/dist/atoms/Textarea.d.ts +28 -0
  48. package/dist/atoms/Textarea.js +36 -0
  49. package/dist/atoms/Toggle.d.ts +38 -0
  50. package/dist/atoms/Toggle.js +45 -0
  51. package/dist/atoms/index.d.ts +32 -0
  52. package/dist/atoms/index.js +32 -0
  53. package/dist/cli/add.d.ts +44 -0
  54. package/dist/cli/add.js +99 -0
  55. package/dist/cli/index.d.ts +10 -0
  56. package/dist/cli/index.js +10 -0
  57. package/dist/cli/registry.d.ts +31 -0
  58. package/dist/cli/registry.js +83 -0
  59. package/dist/config.d.ts +81 -0
  60. package/dist/config.js +43 -0
  61. package/dist/configure.d.ts +34 -0
  62. package/dist/configure.js +85 -0
  63. package/dist/index.d.ts +41 -0
  64. package/dist/index.js +41 -0
  65. package/dist/lib/children.d.ts +28 -0
  66. package/dist/lib/children.js +27 -0
  67. package/dist/lib/cn.d.ts +13 -0
  68. package/dist/lib/cn.js +13 -0
  69. package/dist/lib/cva.d.ts +83 -0
  70. package/dist/lib/cva.js +178 -0
  71. package/dist/lib/icons.d.ts +44 -0
  72. package/dist/lib/icons.js +79 -0
  73. package/dist/lib/id.d.ts +31 -0
  74. package/dist/lib/id.js +41 -0
  75. package/dist/lib/index.d.ts +12 -0
  76. package/dist/lib/index.js +12 -0
  77. package/dist/lib/motion.d.ts +31 -0
  78. package/dist/lib/motion.js +39 -0
  79. package/dist/lib/props.d.ts +48 -0
  80. package/dist/lib/props.js +59 -0
  81. package/dist/lib/styled.d.ts +31 -0
  82. package/dist/lib/styled.js +39 -0
  83. package/dist/molecules/Accordion.d.ts +35 -0
  84. package/dist/molecules/Accordion.js +117 -0
  85. package/dist/molecules/Alert.d.ts +30 -0
  86. package/dist/molecules/Alert.js +35 -0
  87. package/dist/molecules/Attachment.d.ts +39 -0
  88. package/dist/molecules/Attachment.js +103 -0
  89. package/dist/molecules/Breadcrumb.d.ts +29 -0
  90. package/dist/molecules/Breadcrumb.js +73 -0
  91. package/dist/molecules/Bubble.d.ts +56 -0
  92. package/dist/molecules/Bubble.js +92 -0
  93. package/dist/molecules/ButtonGroup.d.ts +23 -0
  94. package/dist/molecules/ButtonGroup.js +30 -0
  95. package/dist/molecules/Card.d.ts +17 -0
  96. package/dist/molecules/Card.js +17 -0
  97. package/dist/molecules/Collapsible.d.ts +40 -0
  98. package/dist/molecules/Collapsible.js +71 -0
  99. package/dist/molecules/Empty.d.ts +16 -0
  100. package/dist/molecules/Empty.js +16 -0
  101. package/dist/molecules/Field.d.ts +54 -0
  102. package/dist/molecules/Field.js +82 -0
  103. package/dist/molecules/InputGroup.d.ts +33 -0
  104. package/dist/molecules/InputGroup.js +35 -0
  105. package/dist/molecules/InputOTP.d.ts +33 -0
  106. package/dist/molecules/InputOTP.js +140 -0
  107. package/dist/molecules/Item.d.ts +41 -0
  108. package/dist/molecules/Item.js +48 -0
  109. package/dist/molecules/Message.d.ts +43 -0
  110. package/dist/molecules/Message.js +82 -0
  111. package/dist/molecules/Pagination.d.ts +41 -0
  112. package/dist/molecules/Pagination.js +144 -0
  113. package/dist/molecules/RadioGroup.d.ts +32 -0
  114. package/dist/molecules/RadioGroup.js +65 -0
  115. package/dist/molecules/Resizable.d.ts +37 -0
  116. package/dist/molecules/Resizable.js +136 -0
  117. package/dist/molecules/Table.d.ts +37 -0
  118. package/dist/molecules/Table.js +78 -0
  119. package/dist/molecules/Tabs.d.ts +37 -0
  120. package/dist/molecules/Tabs.js +109 -0
  121. package/dist/molecules/ToggleGroup.d.ts +37 -0
  122. package/dist/molecules/ToggleGroup.js +68 -0
  123. package/dist/molecules/Typography.d.ts +26 -0
  124. package/dist/molecules/Typography.js +67 -0
  125. package/dist/molecules/index.d.ts +37 -0
  126. package/dist/molecules/index.js +37 -0
  127. package/dist/organisms/AlertDialog.d.ts +40 -0
  128. package/dist/organisms/AlertDialog.js +104 -0
  129. package/dist/organisms/Calendar.d.ts +96 -0
  130. package/dist/organisms/Calendar.js +353 -0
  131. package/dist/organisms/Carousel.d.ts +29 -0
  132. package/dist/organisms/Carousel.js +128 -0
  133. package/dist/organisms/Chart.d.ts +55 -0
  134. package/dist/organisms/Chart.js +189 -0
  135. package/dist/organisms/Combobox.d.ts +41 -0
  136. package/dist/organisms/Combobox.js +102 -0
  137. package/dist/organisms/Command.d.ts +46 -0
  138. package/dist/organisms/Command.js +171 -0
  139. package/dist/organisms/CommandDialog.d.ts +42 -0
  140. package/dist/organisms/CommandDialog.js +116 -0
  141. package/dist/organisms/ContextMenu.d.ts +29 -0
  142. package/dist/organisms/ContextMenu.js +102 -0
  143. package/dist/organisms/DataTable.d.ts +50 -0
  144. package/dist/organisms/DataTable.js +257 -0
  145. package/dist/organisms/DatePicker.d.ts +34 -0
  146. package/dist/organisms/DatePicker.js +115 -0
  147. package/dist/organisms/DateRangePicker.d.ts +34 -0
  148. package/dist/organisms/DateRangePicker.js +122 -0
  149. package/dist/organisms/Dialog.d.ts +50 -0
  150. package/dist/organisms/Dialog.js +116 -0
  151. package/dist/organisms/Drawer.d.ts +40 -0
  152. package/dist/organisms/Drawer.js +131 -0
  153. package/dist/organisms/DropdownMenu.d.ts +30 -0
  154. package/dist/organisms/DropdownMenu.js +91 -0
  155. package/dist/organisms/Form.d.ts +114 -0
  156. package/dist/organisms/Form.js +151 -0
  157. package/dist/organisms/HoverCard.d.ts +30 -0
  158. package/dist/organisms/HoverCard.js +68 -0
  159. package/dist/organisms/Menubar.d.ts +28 -0
  160. package/dist/organisms/Menubar.js +147 -0
  161. package/dist/organisms/MessageScroller.d.ts +36 -0
  162. package/dist/organisms/MessageScroller.js +124 -0
  163. package/dist/organisms/NavigationMenu.d.ts +41 -0
  164. package/dist/organisms/NavigationMenu.js +139 -0
  165. package/dist/organisms/Popover.d.ts +33 -0
  166. package/dist/organisms/Popover.js +58 -0
  167. package/dist/organisms/Questionnaire.d.ts +74 -0
  168. package/dist/organisms/Questionnaire.js +235 -0
  169. package/dist/organisms/Select.d.ts +47 -0
  170. package/dist/organisms/Select.js +250 -0
  171. package/dist/organisms/Sheet.d.ts +34 -0
  172. package/dist/organisms/Sheet.js +107 -0
  173. package/dist/organisms/Sidebar.d.ts +100 -0
  174. package/dist/organisms/Sidebar.js +228 -0
  175. package/dist/organisms/Toaster.d.ts +67 -0
  176. package/dist/organisms/Toaster.js +189 -0
  177. package/dist/organisms/Tooltip.d.ts +39 -0
  178. package/dist/organisms/Tooltip.js +94 -0
  179. package/dist/organisms/index.d.ts +40 -0
  180. package/dist/organisms/index.js +40 -0
  181. package/dist/primitives/controllable.d.ts +38 -0
  182. package/dist/primitives/controllable.js +43 -0
  183. package/dist/primitives/dismissable.d.ts +53 -0
  184. package/dist/primitives/dismissable.js +158 -0
  185. package/dist/primitives/floating.d.ts +116 -0
  186. package/dist/primitives/floating.js +289 -0
  187. package/dist/primitives/floatingSurface.d.ts +56 -0
  188. package/dist/primitives/floatingSurface.js +142 -0
  189. package/dist/primitives/focusTrap.d.ts +47 -0
  190. package/dist/primitives/focusTrap.js +101 -0
  191. package/dist/primitives/focusable.d.ts +53 -0
  192. package/dist/primitives/focusable.js +109 -0
  193. package/dist/primitives/index.d.ts +27 -0
  194. package/dist/primitives/index.js +27 -0
  195. package/dist/primitives/menu.d.ts +102 -0
  196. package/dist/primitives/menu.js +356 -0
  197. package/dist/primitives/modalSurface.d.ts +54 -0
  198. package/dist/primitives/modalSurface.js +146 -0
  199. package/dist/primitives/portal.d.ts +34 -0
  200. package/dist/primitives/portal.js +42 -0
  201. package/dist/primitives/presence.d.ts +45 -0
  202. package/dist/primitives/presence.js +152 -0
  203. package/dist/primitives/rovingFocus.d.ts +47 -0
  204. package/dist/primitives/rovingFocus.js +176 -0
  205. package/dist/primitives/scrollLock.d.ts +21 -0
  206. package/dist/primitives/scrollLock.js +75 -0
  207. package/dist/primitives/typeahead.d.ts +39 -0
  208. package/dist/primitives/typeahead.js +123 -0
  209. package/dist/templates/AppShell.d.ts +30 -0
  210. package/dist/templates/AppShell.js +42 -0
  211. package/dist/templates/AuthLayout.d.ts +23 -0
  212. package/dist/templates/AuthLayout.js +42 -0
  213. package/dist/templates/SettingsLayout.d.ts +27 -0
  214. package/dist/templates/SettingsLayout.js +42 -0
  215. package/dist/templates/index.d.ts +16 -0
  216. package/dist/templates/index.js +16 -0
  217. package/nebula.css +2 -0
  218. package/package.json +92 -0
  219. package/registry.json +1107 -0
  220. package/src/adapters/css.ts +75 -0
  221. package/src/adapters/index.ts +40 -0
  222. package/src/adapters/tailwind.ts +134 -0
  223. package/src/adapters/types.ts +53 -0
  224. package/src/adapters/unocss.ts +116 -0
  225. package/src/atoms/AspectRatio.ts +28 -0
  226. package/src/atoms/Avatar.ts +67 -0
  227. package/src/atoms/Badge.ts +46 -0
  228. package/src/atoms/Button.ts +96 -0
  229. package/src/atoms/Checkbox.ts +76 -0
  230. package/src/atoms/Input.ts +76 -0
  231. package/src/atoms/Kbd.ts +28 -0
  232. package/src/atoms/Label.ts +38 -0
  233. package/src/atoms/Marker.ts +79 -0
  234. package/src/atoms/NativeSelect.ts +115 -0
  235. package/src/atoms/Progress.ts +64 -0
  236. package/src/atoms/ScrollArea.ts +53 -0
  237. package/src/atoms/Separator.ts +36 -0
  238. package/src/atoms/Skeleton.ts +24 -0
  239. package/src/atoms/Slider.ts +65 -0
  240. package/src/atoms/Spinner.ts +28 -0
  241. package/src/atoms/Switch.ts +56 -0
  242. package/src/atoms/Textarea.ts +58 -0
  243. package/src/atoms/Toggle.ts +67 -0
  244. package/src/atoms/index.ts +62 -0
  245. package/src/cli/add.ts +126 -0
  246. package/src/cli/index.ts +22 -0
  247. package/src/cli/registry.ts +116 -0
  248. package/src/config.ts +102 -0
  249. package/src/configure.ts +127 -0
  250. package/src/index.ts +53 -0
  251. package/src/lib/children.ts +43 -0
  252. package/src/lib/cn.ts +14 -0
  253. package/src/lib/cva.ts +249 -0
  254. package/src/lib/icons.ts +138 -0
  255. package/src/lib/id.ts +44 -0
  256. package/src/lib/index.ts +21 -0
  257. package/src/lib/motion.ts +48 -0
  258. package/src/lib/props.ts +83 -0
  259. package/src/lib/styled.ts +60 -0
  260. package/src/molecules/Accordion.ts +161 -0
  261. package/src/molecules/Alert.ts +58 -0
  262. package/src/molecules/Attachment.ts +132 -0
  263. package/src/molecules/Breadcrumb.ts +107 -0
  264. package/src/molecules/Bubble.ts +134 -0
  265. package/src/molecules/ButtonGroup.ts +55 -0
  266. package/src/molecules/Card.ts +42 -0
  267. package/src/molecules/Collapsible.ts +89 -0
  268. package/src/molecules/Empty.ts +43 -0
  269. package/src/molecules/Field.ts +131 -0
  270. package/src/molecules/InputGroup.ts +62 -0
  271. package/src/molecules/InputOTP.ts +166 -0
  272. package/src/molecules/Item.ts +89 -0
  273. package/src/molecules/Message.ts +127 -0
  274. package/src/molecules/Pagination.ts +197 -0
  275. package/src/molecules/RadioGroup.ts +104 -0
  276. package/src/molecules/Resizable.ts +166 -0
  277. package/src/molecules/Table.ts +118 -0
  278. package/src/molecules/Tabs.ts +148 -0
  279. package/src/molecules/ToggleGroup.ts +109 -0
  280. package/src/molecules/Typography.ts +94 -0
  281. package/src/molecules/index.ts +144 -0
  282. package/src/organisms/AlertDialog.ts +133 -0
  283. package/src/organisms/Calendar.ts +470 -0
  284. package/src/organisms/Carousel.ts +161 -0
  285. package/src/organisms/Chart.ts +257 -0
  286. package/src/organisms/Combobox.ts +140 -0
  287. package/src/organisms/Command.ts +211 -0
  288. package/src/organisms/CommandDialog.ts +149 -0
  289. package/src/organisms/ContextMenu.ts +125 -0
  290. package/src/organisms/DataTable.ts +328 -0
  291. package/src/organisms/DatePicker.ts +141 -0
  292. package/src/organisms/DateRangePicker.ts +149 -0
  293. package/src/organisms/Dialog.ts +162 -0
  294. package/src/organisms/Drawer.ts +167 -0
  295. package/src/organisms/DropdownMenu.ts +112 -0
  296. package/src/organisms/Form.ts +217 -0
  297. package/src/organisms/HoverCard.ts +92 -0
  298. package/src/organisms/Menubar.ts +167 -0
  299. package/src/organisms/MessageScroller.ts +145 -0
  300. package/src/organisms/NavigationMenu.ts +184 -0
  301. package/src/organisms/Popover.ts +84 -0
  302. package/src/organisms/Questionnaire.ts +322 -0
  303. package/src/organisms/Select.ts +286 -0
  304. package/src/organisms/Sheet.ts +144 -0
  305. package/src/organisms/Sidebar.ts +336 -0
  306. package/src/organisms/Toaster.ts +246 -0
  307. package/src/organisms/Tooltip.ts +124 -0
  308. package/src/organisms/index.ts +134 -0
  309. package/src/primitives/controllable.ts +64 -0
  310. package/src/primitives/dismissable.ts +195 -0
  311. package/src/primitives/floating.ts +453 -0
  312. package/src/primitives/floatingSurface.ts +209 -0
  313. package/src/primitives/focusTrap.ts +132 -0
  314. package/src/primitives/focusable.ts +108 -0
  315. package/src/primitives/index.ts +88 -0
  316. package/src/primitives/menu.ts +491 -0
  317. package/src/primitives/modalSurface.ts +199 -0
  318. package/src/primitives/portal.ts +61 -0
  319. package/src/primitives/presence.ts +185 -0
  320. package/src/primitives/rovingFocus.ts +212 -0
  321. package/src/primitives/scrollLock.ts +79 -0
  322. package/src/primitives/typeahead.ts +156 -0
  323. package/src/templates/AppShell.ts +56 -0
  324. package/src/templates/AuthLayout.ts +66 -0
  325. package/src/templates/SettingsLayout.ts +63 -0
  326. package/src/templates/index.ts +21 -0
  327. package/theme.css +151 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Tabs — one panel visible at a time.
3
+ *
4
+ * The WAI-ARIA tabs pattern in full: the tab list is a single tab stop with
5
+ * arrows moving between tabs, each tab points at its panel with
6
+ * `aria-controls`, and each panel points back with `aria-labelledby`.
7
+ *
8
+ * Activation is automatic — moving to a tab selects it — which is the right
9
+ * default when panels are already rendered and switching costs nothing. The
10
+ * `activateOnFocus: false` escape hatch exists for panels that fetch on
11
+ * display, where arrowing past three tabs would fire three requests.
12
+ *
13
+ * Hidden panels stay mounted so a form or a scroll position inside one
14
+ * survives a trip to another tab. `hidden` keeps them out of the layout and
15
+ * the accessibility tree, which is the behaviour `display: none` would give
16
+ * without also removing them from the DOM.
17
+ */
18
+ import { component, html, onMount, onUnmount, signal } from "@c9up/aurora";
19
+ import { slot } from "../lib/children.js";
20
+ import { cn } from "../lib/cn.js";
21
+ import { uid } from "../lib/id.js";
22
+ import { read } from "../lib/props.js";
23
+ import { rovingFocus } from "../primitives/rovingFocus.js";
24
+ export const Tabs = component((props) => {
25
+ const orientation = props.orientation ?? "horizontal";
26
+ const first = props.items.find((item) => item.disabled !== true);
27
+ const active = signal(props.defaultValue ?? first?.value ?? "");
28
+ const listId = uid("tabs-list");
29
+ // Ids are minted once per tab, up front, because a tab and its panel each
30
+ // need to name the other — one of them has to know both before rendering.
31
+ const ids = new Map();
32
+ for (const item of props.items) {
33
+ ids.set(item.value, { tab: uid("tab"), panel: uid("tab-panel") });
34
+ }
35
+ function select(value) {
36
+ if (active() === value)
37
+ return;
38
+ active(value);
39
+ props.onValueChange?.(value);
40
+ }
41
+ let group;
42
+ onMount(() => {
43
+ group = rovingFocus({
44
+ container: () => document.getElementById(listId),
45
+ itemSelector: "[role='tab']",
46
+ orientation,
47
+ onFocusChange: (item) => {
48
+ if (props.activateOnFocus === false)
49
+ return;
50
+ const value = item.getAttribute("data-value");
51
+ if (value !== null)
52
+ select(value);
53
+ },
54
+ onSelect: (item) => {
55
+ const value = item.getAttribute("data-value");
56
+ if (value !== null)
57
+ select(value);
58
+ },
59
+ });
60
+ group.sync();
61
+ });
62
+ onUnmount(() => group?.destroy());
63
+ return html `<div
64
+ data-slot="tabs"
65
+ data-orientation="${orientation}"
66
+ class="${() => cn("flex gap-2", orientation === "vertical" ? "flex-row" : "flex-col", read(props.class))}"
67
+ >
68
+ <div
69
+ role="tablist"
70
+ data-slot="tabs-list"
71
+ id="${listId}"
72
+ aria-orientation="${orientation}"
73
+ class="${() => cn("bg-muted text-muted-foreground inline-flex w-fit items-center justify-center rounded-lg p-[3px]", orientation === "vertical" ? "flex-col" : "h-9", read(props.listClass))}"
74
+ >
75
+ ${props.items.map((item) => renderTab(item, ids, active, select))}
76
+ </div>
77
+ ${props.items.map((item) => renderPanel(item, ids, active))}
78
+ </div>`;
79
+ });
80
+ function renderTab(item, ids, active, select) {
81
+ const id = ids.get(item.value);
82
+ const selected = () => active() === item.value;
83
+ return html `<button
84
+ type="button"
85
+ role="tab"
86
+ data-slot="tabs-trigger"
87
+ data-value="${item.value}"
88
+ data-state="${() => (selected() ? "active" : "inactive")}"
89
+ data-disabled="${item.disabled === true ? "" : undefined}"
90
+ id="${id?.tab}"
91
+ aria-selected="${() => (selected() ? "true" : "false")}"
92
+ aria-controls="${id?.panel}"
93
+ ?disabled="${item.disabled === true}"
94
+ class="data-[state=active]:bg-background data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0"
95
+ @click="${() => select(item.value)}"
96
+ >${item.label}</button>`;
97
+ }
98
+ function renderPanel(item, ids, active) {
99
+ const id = ids.get(item.value);
100
+ return html `<div
101
+ role="tabpanel"
102
+ data-slot="tabs-content"
103
+ id="${id?.panel}"
104
+ aria-labelledby="${id?.tab}"
105
+ tabindex="0"
106
+ ?hidden="${() => active() !== item.value}"
107
+ class="flex-1 outline-none"
108
+ >${slot(item.content)}</div>`;
109
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * ToggleGroup — a row of toggles acting as one control.
3
+ *
4
+ * `type` decides both the behaviour and the semantics, and they have to move
5
+ * together. `"single"` is a radio group: one value, `role="radio"` on each
6
+ * button, and picking one clears the rest. `"multiple"` is a set of
7
+ * independent toggle buttons, each with `aria-pressed`.
8
+ *
9
+ * Getting that pairing wrong is the usual bug here — a single-select group
10
+ * announcing each option as "pressed" tells a screen-reader user they can turn
11
+ * several on at once.
12
+ *
13
+ * The classes come from `Toggle`'s own `cva`, so a grouped button and a
14
+ * standalone one cannot drift apart. Only the corner rounding is overridden,
15
+ * to weld the row into one shape.
16
+ */
17
+ import { type ToggleVariants } from "../atoms/Toggle.js";
18
+ import type { Child } from "../lib/children.js";
19
+ import { type Reactive } from "../lib/props.js";
20
+ export interface ToggleGroupItem {
21
+ value: string;
22
+ label: Child;
23
+ /** Required when the label is an icon alone. */
24
+ ariaLabel?: string;
25
+ disabled?: boolean;
26
+ }
27
+ export interface ToggleGroupProps {
28
+ items: readonly ToggleGroupItem[];
29
+ type?: "single" | "multiple";
30
+ defaultValue?: string | readonly string[];
31
+ variant?: Reactive<ToggleVariants["variant"]>;
32
+ size?: Reactive<ToggleVariants["size"]>;
33
+ disabled?: Reactive<boolean>;
34
+ class?: Reactive<string>;
35
+ onValueChange?: (value: readonly string[]) => void;
36
+ }
37
+ export declare const ToggleGroup: (props?: ToggleGroupProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * ToggleGroup — a row of toggles acting as one control.
3
+ *
4
+ * `type` decides both the behaviour and the semantics, and they have to move
5
+ * together. `"single"` is a radio group: one value, `role="radio"` on each
6
+ * button, and picking one clears the rest. `"multiple"` is a set of
7
+ * independent toggle buttons, each with `aria-pressed`.
8
+ *
9
+ * Getting that pairing wrong is the usual bug here — a single-select group
10
+ * announcing each option as "pressed" tells a screen-reader user they can turn
11
+ * several on at once.
12
+ *
13
+ * The classes come from `Toggle`'s own `cva`, so a grouped button and a
14
+ * standalone one cannot drift apart. Only the corner rounding is overridden,
15
+ * to weld the row into one shape.
16
+ */
17
+ import { component, html, signal } from "@c9up/aurora";
18
+ import { toggleVariants } from "../atoms/Toggle.js";
19
+ import { cn } from "../lib/cn.js";
20
+ import { read, readOr } from "../lib/props.js";
21
+ export const ToggleGroup = component((props) => {
22
+ const type = props.type ?? "single";
23
+ const selected = signal(props.defaultValue === undefined
24
+ ? []
25
+ : typeof props.defaultValue === "string"
26
+ ? [props.defaultValue]
27
+ : props.defaultValue);
28
+ function toggle(value) {
29
+ const current = selected();
30
+ const next = type === "single"
31
+ ? current.includes(value)
32
+ ? []
33
+ : [value]
34
+ : current.includes(value)
35
+ ? current.filter((entry) => entry !== value)
36
+ : [...current, value];
37
+ selected(next);
38
+ props.onValueChange?.(next);
39
+ }
40
+ return html `<div
41
+ data-slot="toggle-group"
42
+ role="${type === "single" ? "radiogroup" : "group"}"
43
+ class="${() => cn("group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs", read(props.class))}"
44
+ data-variant="${() => readOr(props.variant, "default")}"
45
+ >
46
+ ${props.items.map((item) => renderItem(item, props, type, selected, toggle))}
47
+ </div>`;
48
+ });
49
+ function renderItem(item, props, type, selected, toggle) {
50
+ const on = () => selected().includes(item.value);
51
+ return html `<button
52
+ type="button"
53
+ data-slot="toggle-group-item"
54
+ data-state="${() => (on() ? "on" : "off")}"
55
+ data-value="${item.value}"
56
+ role="${type === "single" ? "radio" : undefined}"
57
+ aria-checked="${() => (type === "single" ? (on() ? "true" : "false") : undefined)}"
58
+ aria-pressed="${() => (type === "multiple" ? (on() ? "true" : "false") : undefined)}"
59
+ aria-label="${item.ariaLabel}"
60
+ ?disabled="${() => item.disabled === true || readOr(props.disabled, false)}"
61
+ class="${() => toggleVariants({
62
+ variant: read(props.variant),
63
+ size: read(props.size),
64
+ class: "min-w-0 flex-1 shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l aria-checked:bg-accent aria-checked:text-accent-foreground",
65
+ })}"
66
+ @click="${() => toggle(item.value)}"
67
+ >${item.label}</button>`;
68
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Typography — prose styles for content nebula does not otherwise own.
3
+ *
4
+ * shadcn documents these as copy-paste snippets rather than shipping them as
5
+ * components, which leaves every app to re-derive the same scale. They are
6
+ * cheap to ship and they are what keeps a marketing page, a changelog and an
7
+ * empty state reading as one product.
8
+ *
9
+ * `text-balance` on headings and `text-pretty` on body copy do the work here:
10
+ * they stop a two-word orphan on the last line of a heading and a single word
11
+ * ending a paragraph, which is most of what separates typeset text from text.
12
+ */
13
+ import { type StyledProps } from "../lib/styled.js";
14
+ export type TypographyProps = StyledProps;
15
+ export declare const H1: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
16
+ export declare const H2: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
17
+ export declare const H3: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
18
+ export declare const H4: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
19
+ export declare const P: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
20
+ export declare const Blockquote: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
21
+ export declare const InlineCode: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
22
+ export declare const List: (props?: StyledProps | undefined) => import("@c9up/aurora").TemplateResult;
23
+ export declare const Lead: (props?: StyledProps) => import("@c9up/aurora").TemplateResult;
24
+ export declare const Large: (props?: StyledProps) => import("@c9up/aurora").TemplateResult;
25
+ export declare const Small: (props?: StyledProps) => import("@c9up/aurora").TemplateResult;
26
+ export declare const Muted: (props?: StyledProps) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Typography — prose styles for content nebula does not otherwise own.
3
+ *
4
+ * shadcn documents these as copy-paste snippets rather than shipping them as
5
+ * components, which leaves every app to re-derive the same scale. They are
6
+ * cheap to ship and they are what keeps a marketing page, a changelog and an
7
+ * empty state reading as one product.
8
+ *
9
+ * `text-balance` on headings and `text-pretty` on body copy do the work here:
10
+ * they stop a two-word orphan on the last line of a heading and a single word
11
+ * ending a paragraph, which is most of what separates typeset text from text.
12
+ */
13
+ import { component, html } from "@c9up/aurora";
14
+ import { slot } from "../lib/children.js";
15
+ import { cn } from "../lib/cn.js";
16
+ import { read } from "../lib/props.js";
17
+ import { styledDiv } from "../lib/styled.js";
18
+ export const H1 = component((props) => {
19
+ return html `<h1
20
+ id="${props.id}"
21
+ class="${() => cn("scroll-m-20 text-4xl font-extrabold tracking-tight text-balance", read(props.class))}"
22
+ >${slot(props.children)}</h1>`;
23
+ });
24
+ export const H2 = component((props) => {
25
+ return html `<h2
26
+ id="${props.id}"
27
+ class="${() => cn("scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0", read(props.class))}"
28
+ >${slot(props.children)}</h2>`;
29
+ });
30
+ export const H3 = component((props) => {
31
+ return html `<h3
32
+ id="${props.id}"
33
+ class="${() => cn("scroll-m-20 text-2xl font-semibold tracking-tight", read(props.class))}"
34
+ >${slot(props.children)}</h3>`;
35
+ });
36
+ export const H4 = component((props) => {
37
+ return html `<h4
38
+ id="${props.id}"
39
+ class="${() => cn("scroll-m-20 text-xl font-semibold tracking-tight", read(props.class))}"
40
+ >${slot(props.children)}</h4>`;
41
+ });
42
+ export const P = component((props) => {
43
+ return html `<p
44
+ id="${props.id}"
45
+ class="${() => cn("leading-7 text-pretty [&:not(:first-child)]:mt-6", read(props.class))}"
46
+ >${slot(props.children)}</p>`;
47
+ });
48
+ export const Blockquote = component((props) => {
49
+ return html `<blockquote
50
+ id="${props.id}"
51
+ class="${() => cn("mt-6 border-l-2 pl-6 italic", read(props.class))}"
52
+ >${slot(props.children)}</blockquote>`;
53
+ });
54
+ export const InlineCode = component((props) => {
55
+ return html `<code
56
+ class="${() => cn("bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold", read(props.class))}"
57
+ >${slot(props.children)}</code>`;
58
+ });
59
+ export const List = component((props) => {
60
+ return html `<ul
61
+ class="${() => cn("my-6 ml-6 list-disc [&>li]:mt-2", read(props.class))}"
62
+ >${slot(props.children)}</ul>`;
63
+ });
64
+ export const Lead = styledDiv("lead", "text-muted-foreground text-xl text-pretty");
65
+ export const Large = styledDiv("large", "text-lg font-semibold");
66
+ export const Small = styledDiv("small", "text-sm leading-none font-medium");
67
+ export const Muted = styledDiv("muted", "text-muted-foreground text-sm");
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Molecules — assemblies of atoms, and small state machines.
3
+ *
4
+ * A component lands here when it composes other nebula components, or owns
5
+ * state that spans more than one element (Accordion's open set, Tabs'
6
+ * selection, InputOTP's characters). What it does *not* do is escape its own
7
+ * DOM position: nothing here portals, traps focus, or floats. Those are
8
+ * organisms.
9
+ *
10
+ * Compound components are data-driven — `Accordion({ items: [...] })`, not
11
+ * `<Accordion><AccordionItem>`. shadcn's compound API is a React-context
12
+ * artifact; Aurora has no context, and the alternatives (a factory returning
13
+ * bound parts, a handle threaded through props) are more machinery for less
14
+ * clarity. The rendered markup is unchanged, so shadcn's CSS and its examples
15
+ * still read across.
16
+ */
17
+ export { Accordion, type AccordionItem, type AccordionProps, } from "./Accordion.js";
18
+ export { Alert, AlertDescription, type AlertProps, AlertTitle, type AlertVariants, alertVariants, } from "./Alert.js";
19
+ export { Attachment, type AttachmentProps, formatBytes, } from "./Attachment.js";
20
+ export { Breadcrumb, type BreadcrumbProps, type Crumb } from "./Breadcrumb.js";
21
+ export { Bubble, type BubbleProps, type BubbleReaction, type BubbleVariants, bubbleVariants, } from "./Bubble.js";
22
+ export { ButtonGroup, type ButtonGroupProps, ButtonGroupSeparator, ButtonGroupText, } from "./ButtonGroup.js";
23
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, type CardProps, CardTitle, } from "./Card.js";
24
+ export { Collapsible, type CollapsibleProps } from "./Collapsible.js";
25
+ export { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, type EmptyProps, EmptyTitle, } from "./Empty.js";
26
+ export { Field, FieldGroup, type FieldIds, type FieldProps, FieldSeparator, fieldIds, } from "./Field.js";
27
+ export { InputGroup, InputGroupAddon, type InputGroupProps, inputGroupControlClasses, } from "./InputGroup.js";
28
+ export { InputOTP, type InputOTPProps } from "./InputOTP.js";
29
+ export { Item, ItemActions, ItemContent, ItemDescription, ItemGroup, ItemMedia, type ItemProps, ItemSeparator, ItemTitle, type ItemVariants, itemVariants, } from "./Item.js";
30
+ export { Message, type MessageProps } from "./Message.js";
31
+ export { type PageSlot, Pagination, type PaginationProps, pageWindow, } from "./Pagination.js";
32
+ export { RadioGroup, type RadioGroupProps, type RadioOption, } from "./RadioGroup.js";
33
+ export { Resizable, type ResizableProps } from "./Resizable.js";
34
+ export { Table, TableBody, TableCaption, TableCell, type TableCellProps, TableFooter, TableHead, TableHeader, type TableProps, TableRow, type TableRowProps, } from "./Table.js";
35
+ export { type TabItem, Tabs, type TabsProps } from "./Tabs.js";
36
+ export { ToggleGroup, type ToggleGroupItem, type ToggleGroupProps, } from "./ToggleGroup.js";
37
+ export { Blockquote, H1, H2, H3, H4, InlineCode, Large, Lead, List, Muted, P, Small, type TypographyProps, } from "./Typography.js";
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Molecules — assemblies of atoms, and small state machines.
3
+ *
4
+ * A component lands here when it composes other nebula components, or owns
5
+ * state that spans more than one element (Accordion's open set, Tabs'
6
+ * selection, InputOTP's characters). What it does *not* do is escape its own
7
+ * DOM position: nothing here portals, traps focus, or floats. Those are
8
+ * organisms.
9
+ *
10
+ * Compound components are data-driven — `Accordion({ items: [...] })`, not
11
+ * `<Accordion><AccordionItem>`. shadcn's compound API is a React-context
12
+ * artifact; Aurora has no context, and the alternatives (a factory returning
13
+ * bound parts, a handle threaded through props) are more machinery for less
14
+ * clarity. The rendered markup is unchanged, so shadcn's CSS and its examples
15
+ * still read across.
16
+ */
17
+ export { Accordion, } from "./Accordion.js";
18
+ export { Alert, AlertDescription, AlertTitle, alertVariants, } from "./Alert.js";
19
+ export { Attachment, formatBytes, } from "./Attachment.js";
20
+ export { Breadcrumb } from "./Breadcrumb.js";
21
+ export { Bubble, bubbleVariants, } from "./Bubble.js";
22
+ export { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, } from "./ButtonGroup.js";
23
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "./Card.js";
24
+ export { Collapsible } from "./Collapsible.js";
25
+ export { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, } from "./Empty.js";
26
+ export { Field, FieldGroup, FieldSeparator, fieldIds, } from "./Field.js";
27
+ export { InputGroup, InputGroupAddon, inputGroupControlClasses, } from "./InputGroup.js";
28
+ export { InputOTP } from "./InputOTP.js";
29
+ export { Item, ItemActions, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemSeparator, ItemTitle, itemVariants, } from "./Item.js";
30
+ export { Message } from "./Message.js";
31
+ export { Pagination, pageWindow, } from "./Pagination.js";
32
+ export { RadioGroup, } from "./RadioGroup.js";
33
+ export { Resizable } from "./Resizable.js";
34
+ export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from "./Table.js";
35
+ export { Tabs } from "./Tabs.js";
36
+ export { ToggleGroup, } from "./ToggleGroup.js";
37
+ export { Blockquote, H1, H2, H3, H4, InlineCode, Large, Lead, List, Muted, P, Small, } from "./Typography.js";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * AlertDialog — a modal that interrupts to ask something.
3
+ *
4
+ * The differences from Dialog are all about refusing to be dismissed by
5
+ * accident, because the user is being asked to confirm something they cannot
6
+ * undo:
7
+ *
8
+ * - `role="alertdialog"`, which tells assistive technology this is not merely
9
+ * a window but a message requiring a response.
10
+ * - No close button and no backdrop dismissal. Both are the "click somewhere
11
+ * else and it goes away" gesture, and an accidental dismissal of "delete
12
+ * this?" reads as a cancel — the safe answer — but leaves the user unsure
13
+ * which one they gave.
14
+ * - Escape still closes. It is unambiguous and deliberate, and taking it away
15
+ * would trap a keyboard user in the dialog.
16
+ * - Focus starts on **Cancel**, not the action. The destructive button is the
17
+ * one the user must reach for on purpose.
18
+ */
19
+ import type { Child } from "../lib/children.js";
20
+ import { type Slot } from "../lib/children.js";
21
+ import { type Reactive } from "../lib/props.js";
22
+ export interface AlertDialogProps {
23
+ trigger?: Slot;
24
+ title: Child;
25
+ description: Child;
26
+ /** Label for the confirming action. Default `"Continue"`. */
27
+ actionLabel?: Child;
28
+ /** Label for the way out. Default `"Cancel"`. */
29
+ cancelLabel?: Child;
30
+ /** Styles the action as destructive. */
31
+ destructive?: boolean;
32
+ open?: Reactive<boolean>;
33
+ defaultOpen?: boolean;
34
+ onOpenChange?: (open: boolean) => void;
35
+ onConfirm?: () => void;
36
+ onCancel?: () => void;
37
+ triggerClass?: Reactive<string>;
38
+ contentClass?: Reactive<string>;
39
+ }
40
+ export declare const AlertDialog: (props?: AlertDialogProps | undefined) => import("@c9up/aurora").TemplateResult;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * AlertDialog — a modal that interrupts to ask something.
3
+ *
4
+ * The differences from Dialog are all about refusing to be dismissed by
5
+ * accident, because the user is being asked to confirm something they cannot
6
+ * undo:
7
+ *
8
+ * - `role="alertdialog"`, which tells assistive technology this is not merely
9
+ * a window but a message requiring a response.
10
+ * - No close button and no backdrop dismissal. Both are the "click somewhere
11
+ * else and it goes away" gesture, and an accidental dismissal of "delete
12
+ * this?" reads as a cancel — the safe answer — but leaves the user unsure
13
+ * which one they gave.
14
+ * - Escape still closes. It is unambiguous and deliberate, and taking it away
15
+ * would trap a keyboard user in the dialog.
16
+ * - Focus starts on **Cancel**, not the action. The destructive button is the
17
+ * one the user must reach for on purpose.
18
+ */
19
+ import { component, html } from "@c9up/aurora";
20
+ import { buttonVariants } from "../atoms/Button.js";
21
+ import { slot } from "../lib/children.js";
22
+ import { cn } from "../lib/cn.js";
23
+ import { uid } from "../lib/id.js";
24
+ import { fadeInOut, zoomInOut } from "../lib/motion.js";
25
+ import { read } from "../lib/props.js";
26
+ import { controllable } from "../primitives/controllable.js";
27
+ import { modalSurface } from "../primitives/modalSurface.js";
28
+ import { dialogBackdropClasses, dialogPanelClasses } from "./Dialog.js";
29
+ export const AlertDialog = component((props) => {
30
+ const triggerId = uid("alert-dialog-trigger");
31
+ const panelId = uid("alert-dialog-panel");
32
+ const titleId = uid("alert-dialog-title");
33
+ const descriptionId = uid("alert-dialog-description");
34
+ const cancelId = uid("alert-dialog-cancel");
35
+ const state = controllable({
36
+ value: props.open,
37
+ initial: props.defaultOpen ?? false,
38
+ onChange: props.onOpenChange,
39
+ });
40
+ function cancel() {
41
+ props.onCancel?.();
42
+ state.set(false);
43
+ }
44
+ function confirm() {
45
+ props.onConfirm?.();
46
+ state.set(false);
47
+ }
48
+ modalSurface({
49
+ open: () => state.current(),
50
+ onClose: cancel,
51
+ panel: (root) => root.querySelector(`#${CSS.escape(panelId)}`),
52
+ initialFocus: (panel) => panel.querySelector(`#${CSS.escape(cancelId)}`),
53
+ returnFocus: () => document.getElementById(triggerId),
54
+ dismissOnOutside: false,
55
+ content: () => html `<div data-slot="alert-dialog-overlay" class="${cn("fixed inset-0 z-50", fadeInOut)}">
56
+ <div class="${dialogBackdropClasses}"></div>
57
+ <div
58
+ data-slot="alert-dialog-content"
59
+ id="${panelId}"
60
+ role="alertdialog"
61
+ aria-modal="true"
62
+ aria-labelledby="${titleId}"
63
+ aria-describedby="${descriptionId}"
64
+ tabindex="-1"
65
+ class="${cn(dialogPanelClasses, zoomInOut, read(props.contentClass))}"
66
+ >
67
+ <div class="flex flex-col gap-2 text-center sm:text-left">
68
+ <h2 id="${titleId}" class="text-lg font-semibold">${props.title}</h2>
69
+ <p id="${descriptionId}" class="text-muted-foreground text-sm">
70
+ ${props.description}
71
+ </p>
72
+ </div>
73
+ <div class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
74
+ <button
75
+ type="button"
76
+ id="${cancelId}"
77
+ data-slot="alert-dialog-cancel"
78
+ class="${buttonVariants({ variant: "outline" })}"
79
+ @click="${cancel}"
80
+ >${props.cancelLabel ?? "Cancel"}</button>
81
+ <button
82
+ type="button"
83
+ data-slot="alert-dialog-action"
84
+ class="${buttonVariants({
85
+ variant: props.destructive === true ? "destructive" : "default",
86
+ })}"
87
+ @click="${confirm}"
88
+ >${props.actionLabel ?? "Continue"}</button>
89
+ </div>
90
+ </div>
91
+ </div>`,
92
+ });
93
+ if (props.trigger === undefined) {
94
+ return html `<span data-slot="alert-dialog" hidden></span>`;
95
+ }
96
+ return html `<button
97
+ type="button"
98
+ data-slot="alert-dialog-trigger"
99
+ id="${triggerId}"
100
+ aria-haspopup="dialog"
101
+ class="${() => cn(read(props.triggerClass))}"
102
+ @click="${() => state.set(true)}"
103
+ >${slot(props.trigger)}</button>`;
104
+ });
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Calendar — a month grid for picking a date.
3
+ *
4
+ * shadcn wraps `react-day-picker`, which brings `date-fns` with it. nebula
5
+ * uses `Date` and `Intl`, which cover everything this needs: month lengths and
6
+ * leap years come from `Date`, and weekday and month names come from
7
+ * `Intl.DateTimeFormat` in the user's locale for free — which is more than a
8
+ * hardcoded English array would give.
9
+ *
10
+ * Dates are handled at local midnight throughout. A calendar cell means "this
11
+ * day", not "this instant", and mixing the two is how a date picker ends up
12
+ * off by one for users west of UTC: `new Date("2026-03-14")` parses as UTC
13
+ * midnight, which is the 13th in New York.
14
+ *
15
+ * The keyboard model is the WAI-ARIA grid pattern, and it is what makes the
16
+ * control usable at all without a pointer: arrows move a day, PageUp/PageDown
17
+ * move a month, Home and End jump to the ends of the week. Only one cell is
18
+ * ever tabbable — the focused day — so Tab leaves the grid instead of walking
19
+ * through thirty-one buttons.
20
+ */
21
+ import { type Reactive } from "../lib/props.js";
22
+ /**
23
+ * A span of days. `to` is absent while the second end is still being picked —
24
+ * that half-open state is what the hover preview renders against, so it is
25
+ * part of the type rather than something the component hides internally.
26
+ */
27
+ export interface DateRange {
28
+ readonly from: Date;
29
+ readonly to?: Date;
30
+ }
31
+ export interface CalendarProps {
32
+ /**
33
+ * `"range"` picks a span in two clicks. Default `"single"`.
34
+ *
35
+ * The two modes carry their value in separate props rather than one union.
36
+ * A union would make every read of `value` a narrowing exercise for the
37
+ * caller as much as for this file, and the two `onChange` shapes genuinely
38
+ * differ — a range handler that receives a bare `Date` has no way to say
39
+ * which end moved.
40
+ */
41
+ mode?: "single" | "range";
42
+ /** Selected day, in `"single"` mode. Local midnight. */
43
+ value?: Reactive<Date | undefined>;
44
+ defaultValue?: Date;
45
+ /** Selected span, in `"range"` mode. */
46
+ range?: Reactive<DateRange | undefined>;
47
+ defaultRange?: DateRange;
48
+ /** Month shown at first render. Defaults to the selection, then today. */
49
+ defaultMonth?: Date;
50
+ min?: Date;
51
+ max?: Date;
52
+ /** Rule out individual days — weekends, holidays, taken slots. */
53
+ disabled?: (date: Date) => boolean;
54
+ /** `0` Sunday, `1` Monday. Defaults to the locale's own first day. */
55
+ weekStartsOn?: number;
56
+ locale?: string;
57
+ class?: Reactive<string>;
58
+ /** `"single"` mode. */
59
+ onValueChange?: (date: Date) => void;
60
+ /** `"range"` mode. Fires on both clicks — `to` is absent after the first. */
61
+ onRangeChange?: (range: DateRange) => void;
62
+ }
63
+ /** Local midnight of the given day — the canonical form for a calendar date. */
64
+ export declare function startOfDay(date: Date): Date;
65
+ export declare function isSameDay(a: Date, b: Date): boolean;
66
+ export declare function addDays(date: Date, days: number): Date;
67
+ export declare function addMonths(date: Date, months: number): Date;
68
+ /**
69
+ * The six-week grid covering a month.
70
+ *
71
+ * Always six weeks, always 42 cells, even when five would do. A grid that
72
+ * changes height between months makes the whole popover jump as you page
73
+ * through it.
74
+ */
75
+ export declare function monthGrid(month: Date, weekStartsOn: number): Date[];
76
+ /** Where a day sits within a span. `"single"` is a span of one day. */
77
+ export type RangePosition = "single" | "start" | "end" | "middle" | "none";
78
+ /**
79
+ * Place a day relative to a span.
80
+ *
81
+ * `end` is passed in rather than read off the range because it may be the
82
+ * *preview* end — wherever the pointer is, before the second click. Keeping
83
+ * that decision at the call site is what lets this stay a pure function over
84
+ * three dates, which is the part worth testing exhaustively.
85
+ */
86
+ export declare function rangePosition(date: Date, range: DateRange | undefined, end: Date | undefined): RangePosition;
87
+ /**
88
+ * The span after clicking `date`.
89
+ *
90
+ * A click before the fixed start restarts the span rather than producing an
91
+ * inverted one. Silently swapping the ends would be the other option, and it
92
+ * is worse: the user who clicked the 3rd after the 10th almost always meant to
93
+ * start again, not to select the week between them.
94
+ */
95
+ export declare function nextRange(current: DateRange | undefined, date: Date): DateRange;
96
+ export declare const Calendar: (props?: CalendarProps | undefined) => import("@c9up/aurora").TemplateResult;