@charcuterie/ui 3.0.0 → 3.2.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 (130) hide show
  1. package/README.md +10 -0
  2. package/dist/Badge/Badge.d.ts +33 -3
  3. package/dist/Badge/Badge.d.ts.map +1 -1
  4. package/dist/Badge/Badge.js +20 -2
  5. package/dist/Badge/Badge.js.map +1 -1
  6. package/dist/Board/Board.d.ts +119 -0
  7. package/dist/Board/Board.d.ts.map +1 -0
  8. package/dist/Board/Board.js +165 -0
  9. package/dist/Board/Board.js.map +1 -0
  10. package/dist/Board/BoardCard.d.ts +120 -0
  11. package/dist/Board/BoardCard.d.ts.map +1 -0
  12. package/dist/Board/BoardCard.js +77 -0
  13. package/dist/Board/BoardCard.js.map +1 -0
  14. package/dist/Board/BoardLaneList.d.ts +103 -0
  15. package/dist/Board/BoardLaneList.d.ts.map +1 -0
  16. package/dist/Board/BoardLaneList.js +69 -0
  17. package/dist/Board/BoardLaneList.js.map +1 -0
  18. package/dist/Board/boardMove.d.ts +116 -0
  19. package/dist/Board/boardMove.d.ts.map +1 -0
  20. package/dist/Board/boardMove.js +116 -0
  21. package/dist/Board/boardMove.js.map +1 -0
  22. package/dist/Board/useBoardDrag.d.ts +44 -0
  23. package/dist/Board/useBoardDrag.d.ts.map +1 -0
  24. package/dist/Board/useBoardDrag.js +237 -0
  25. package/dist/Board/useBoardDrag.js.map +1 -0
  26. package/dist/Checkbox/Checkbox.d.ts.map +1 -1
  27. package/dist/Checkbox/Checkbox.js +15 -4
  28. package/dist/Checkbox/Checkbox.js.map +1 -1
  29. package/dist/DataTable/DataTable.d.ts +159 -0
  30. package/dist/DataTable/DataTable.d.ts.map +1 -0
  31. package/dist/DataTable/DataTable.js +197 -0
  32. package/dist/DataTable/DataTable.js.map +1 -0
  33. package/dist/DatePicker/DateGrid.d.ts +79 -0
  34. package/dist/DatePicker/DateGrid.d.ts.map +1 -0
  35. package/dist/DatePicker/DateGrid.js +220 -0
  36. package/dist/DatePicker/DateGrid.js.map +1 -0
  37. package/dist/DatePicker/DatePicker.d.ts +138 -0
  38. package/dist/DatePicker/DatePicker.d.ts.map +1 -0
  39. package/dist/DatePicker/DatePicker.js +384 -0
  40. package/dist/DatePicker/DatePicker.js.map +1 -0
  41. package/dist/DatePicker/datePresets.d.ts +29 -0
  42. package/dist/DatePicker/datePresets.d.ts.map +1 -0
  43. package/dist/DatePicker/datePresets.js +28 -0
  44. package/dist/DatePicker/datePresets.js.map +1 -0
  45. package/dist/DatePicker/parseDateInput.d.ts +106 -0
  46. package/dist/DatePicker/parseDateInput.d.ts.map +1 -0
  47. package/dist/DatePicker/parseDateInput.js +419 -0
  48. package/dist/DatePicker/parseDateInput.js.map +1 -0
  49. package/dist/DatePicker/plainDate.d.ts +167 -0
  50. package/dist/DatePicker/plainDate.d.ts.map +1 -0
  51. package/dist/DatePicker/plainDate.js +272 -0
  52. package/dist/DatePicker/plainDate.js.map +1 -0
  53. package/dist/Dialog/Dialog.d.ts +12 -2
  54. package/dist/Dialog/Dialog.d.ts.map +1 -1
  55. package/dist/Dialog/Dialog.js +2 -2
  56. package/dist/Dialog/Dialog.js.map +1 -1
  57. package/dist/MarkdownEditor/MarkdownEditor.d.ts +147 -0
  58. package/dist/MarkdownEditor/MarkdownEditor.d.ts.map +1 -0
  59. package/dist/MarkdownEditor/MarkdownEditor.js +492 -0
  60. package/dist/MarkdownEditor/MarkdownEditor.js.map +1 -0
  61. package/dist/MarkdownEditor/markdownCommands.d.ts +202 -0
  62. package/dist/MarkdownEditor/markdownCommands.d.ts.map +1 -0
  63. package/dist/MarkdownEditor/markdownCommands.js +379 -0
  64. package/dist/MarkdownEditor/markdownCommands.js.map +1 -0
  65. package/dist/MarkdownEditor/markdownSpans.d.ts +92 -0
  66. package/dist/MarkdownEditor/markdownSpans.d.ts.map +1 -0
  67. package/dist/MarkdownEditor/markdownSpans.js +303 -0
  68. package/dist/MarkdownEditor/markdownSpans.js.map +1 -0
  69. package/dist/Modal/Modal.d.ts +6 -2
  70. package/dist/Modal/Modal.d.ts.map +1 -1
  71. package/dist/Modal/Modal.js +1 -1
  72. package/dist/Modal/Modal.js.map +1 -1
  73. package/dist/Overlay/OverlayPanel.d.ts +13 -2
  74. package/dist/Overlay/OverlayPanel.d.ts.map +1 -1
  75. package/dist/Overlay/OverlayPanel.js +5 -1
  76. package/dist/Overlay/OverlayPanel.js.map +1 -1
  77. package/dist/categoricalStyles.d.ts +53 -0
  78. package/dist/categoricalStyles.d.ts.map +1 -0
  79. package/dist/categoricalStyles.js +195 -0
  80. package/dist/categoricalStyles.js.map +1 -0
  81. package/dist/index.d.ts +22 -0
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +11 -0
  84. package/dist/index.js.map +1 -1
  85. package/package.json +2 -2
  86. package/src/Badge/Badge.mdx +46 -0
  87. package/src/Badge/Badge.stories.tsx +98 -4
  88. package/src/Badge/Badge.test.tsx +157 -2
  89. package/src/Badge/Badge.tsx +49 -3
  90. package/src/Board/Board.mdx +199 -0
  91. package/src/Board/Board.stories.tsx +707 -0
  92. package/src/Board/Board.test.tsx +310 -0
  93. package/src/Board/Board.tsx +333 -0
  94. package/src/Board/BoardCard.tsx +290 -0
  95. package/src/Board/BoardLaneList.tsx +275 -0
  96. package/src/Board/boardMove.test.ts +213 -0
  97. package/src/Board/boardMove.ts +225 -0
  98. package/src/Board/useBoardDrag.ts +413 -0
  99. package/src/Checkbox/Checkbox.tsx +34 -3
  100. package/src/DataTable/DataTable.mdx +123 -0
  101. package/src/DataTable/DataTable.stories.tsx +411 -0
  102. package/src/DataTable/DataTable.test.tsx +386 -0
  103. package/src/DataTable/DataTable.tsx +497 -0
  104. package/src/DatePicker/DateGrid.tsx +467 -0
  105. package/src/DatePicker/DatePicker.mdx +170 -0
  106. package/src/DatePicker/DatePicker.stories.tsx +268 -0
  107. package/src/DatePicker/DatePicker.test.tsx +455 -0
  108. package/src/DatePicker/DatePicker.tsx +852 -0
  109. package/src/DatePicker/datePresets.ts +34 -0
  110. package/src/DatePicker/parseDateInput.test.ts +187 -0
  111. package/src/DatePicker/parseDateInput.ts +629 -0
  112. package/src/DatePicker/plainDate.test.ts +375 -0
  113. package/src/DatePicker/plainDate.ts +465 -0
  114. package/src/Dialog/Dialog.stories.tsx +48 -0
  115. package/src/Dialog/Dialog.test.tsx +30 -0
  116. package/src/Dialog/Dialog.tsx +13 -1
  117. package/src/MarkdownEditor/MarkdownEditor.mdx +189 -0
  118. package/src/MarkdownEditor/MarkdownEditor.stories.tsx +267 -0
  119. package/src/MarkdownEditor/MarkdownEditor.test.tsx +627 -0
  120. package/src/MarkdownEditor/MarkdownEditor.tsx +862 -0
  121. package/src/MarkdownEditor/markdownCommands.test.ts +384 -0
  122. package/src/MarkdownEditor/markdownCommands.ts +566 -0
  123. package/src/MarkdownEditor/markdownSpans.test.ts +217 -0
  124. package/src/MarkdownEditor/markdownSpans.ts +458 -0
  125. package/src/Modal/Modal.tsx +7 -1
  126. package/src/Overlay/OverlayPanel.tsx +17 -1
  127. package/src/argTypes.storyHelpers.ts +39 -0
  128. package/src/categoricalStyles.ts +244 -0
  129. package/src/index.ts +89 -0
  130. package/src/sourceRules.test.ts +52 -1
@@ -10,7 +10,7 @@ import {
10
10
  useInteractions,
11
11
  useRole,
12
12
  } from "@floating-ui/react"
13
- import type { ReactNode } from "react"
13
+ import type { ReactNode, RefObject } from "react"
14
14
  import { useCallback, useEffect } from "react"
15
15
 
16
16
  import { lockScrollBehind } from "./lockScrollBehind.ts"
@@ -58,6 +58,17 @@ export type OverlayPanelProps = {
58
58
  "aria-labelledby"?: string
59
59
  children: ReactNode
60
60
  className?: string
61
+ /**
62
+ * Which element takes the caret when the panel opens.
63
+ *
64
+ * Without it the focus manager takes the first tabbable element,
65
+ * which for anything with chrome is the **Close button** — so a
66
+ * panel wrapping a form opens with the caret on "Close" and the
67
+ * first thing typed goes nowhere. A consumer cannot fix that from
68
+ * outside: focusing in its own effect races the manager, and the
69
+ * manager wins, which reads as the app ignoring you.
70
+ */
71
+ initialFocus?: RefObject<HTMLElement | null>
61
72
  /** Escape and an outside press both close. */
62
73
  isDismissable?: boolean
63
74
  isVisible: boolean
@@ -70,6 +81,7 @@ export const OverlayPanel = ({
70
81
  "aria-labelledby": ariaLabelledBy,
71
82
  children,
72
83
  className,
84
+ initialFocus,
73
85
  isDismissable = true,
74
86
  isVisible,
75
87
  onClose,
@@ -160,6 +172,10 @@ export const OverlayPanel = ({
160
172
  <div className="fixed inset-0 z-[var(--layer-modal)] flex items-center justify-center">
161
173
  <FloatingFocusManager
162
174
  context={context}
175
+ // `undefined` keeps the manager's own default (the first
176
+ // tabbable), so nothing changes for a panel that does not
177
+ // ask.
178
+ initialFocus={initialFocus ?? undefined}
163
179
  // Only the top traps focus; a lower panel is `inert`
164
180
  // anyway, and two live traps fight over one caret.
165
181
  disabled={!isTop}
@@ -1,4 +1,8 @@
1
1
  import { connectionTransitions } from "@charcuterie/logic"
2
+ import {
3
+ CATEGORICAL_HUES,
4
+ CATEGORICAL_INDEXES,
5
+ } from "@charcuterie/tokens"
2
6
  import type { InputType } from "storybook/internal/types"
3
7
 
4
8
  import { CONTROL_SIZE_CLASS } from "./controlStyles.ts"
@@ -95,6 +99,41 @@ export const PLACEMENT_OPTIONS = [
95
99
 
96
100
  export const intentArgType = toChoiceArgType(INTENT_OPTIONS)
97
101
 
102
+ /**
103
+ * `categorical` is the one enumerable prop in the package whose
104
+ * values are **numbers**, so it cannot go through `toOptions` —
105
+ * `Object.keys` on a numerically-keyed record returns
106
+ * `["1", "2", …]`, and a control handing the component `"3"` where
107
+ * it wants `3` indexes a `Record<CategoricalIndex, …>` with a
108
+ * string and paints nothing. Reading `CATEGORICAL_INDEXES` keeps
109
+ * the type and the control agreeing.
110
+ *
111
+ * `undefined` leads the list because it is the real default: a
112
+ * badge with no `categorical` is an `intent` badge, and a control
113
+ * with no way back to that would make the two props look like a
114
+ * choice the reader cannot undo.
115
+ */
116
+ export const CATEGORICAL_OPTIONS = [...CATEGORICAL_INDEXES]
117
+
118
+ export const categoricalArgType: InputType = {
119
+ control: { type: "select" },
120
+ options: [undefined, ...CATEGORICAL_OPTIONS],
121
+ // The names are `CATEGORICAL_HUES`', not invented here — a picker
122
+ // that says "3" tells a reader nothing, and `Swatch` already
123
+ // settled that a colour needs a word attached.
124
+ mapping: Object.fromEntries(
125
+ CATEGORICAL_OPTIONS.map((index) => [index, index]),
126
+ ),
127
+ table: {
128
+ type: {
129
+ summary: CATEGORICAL_OPTIONS.map(
130
+ (index) =>
131
+ `${index} (${CATEGORICAL_HUES[index].label})`,
132
+ ).join(" | "),
133
+ },
134
+ },
135
+ }
136
+
98
137
  export const controlSizeArgType = toChoiceArgType(
99
138
  CONTROL_SIZE_OPTIONS,
100
139
  )
@@ -0,0 +1,244 @@
1
+ /**
2
+ * One categorical index → class map, the twin of
3
+ * `intentStyles.ts`.
4
+ *
5
+ * Its own file rather than ten more entries in that one, because
6
+ * the two families are keyed by different things and mean different
7
+ * things. `intentStyles.ts` opens by explaining that it exists to
8
+ * delete rip-deck's duplicated `TONE_CLASS` — a *status* map, where
9
+ * every key is a claim about state. Nothing here is a claim about
10
+ * anything: an index is a user's pick for a label, and the only
11
+ * thing a component may do with it is paint it. Keeping them apart
12
+ * is also what keeps `IntentName` an exhaustive union that
13
+ * `getAsyncIntent` can switch over.
14
+ *
15
+ * **Every class name is written out in full, and that is not
16
+ * negotiable.** Tailwind v4 scans source text for *complete* class
17
+ * strings, so `` `bg-categorical-${index}-solid` `` generates
18
+ * nothing at all — the badge renders unstyled with no error, no
19
+ * warning, and no failing test unless something checks. Something
20
+ * does: `tailwindCandidates.test.ts` compiles every literal in this
21
+ * package through the real Tailwind and fails on any candidate it
22
+ * cannot generate. Forty lines of near-duplication is the price,
23
+ * and the alternative is the `--color-danger-9` that shipped in
24
+ * Docket: a token name that had never existed, resolving to
25
+ * nothing, painting transparent, and passing every assertion about
26
+ * whether the element was rendered.
27
+ */
28
+
29
+ import type { CategoricalIndex } from "@charcuterie/tokens"
30
+
31
+ import type { IntentAppearance } from "./intentStyles.ts"
32
+
33
+ export const CATEGORICAL_APPEARANCE_CLASS: Record<
34
+ CategoricalIndex,
35
+ Record<IntentAppearance, string>
36
+ > = {
37
+ 1: {
38
+ ghost:
39
+ "border-transparent bg-transparent text-categorical-1-content",
40
+ outline:
41
+ "border-categorical-1-border bg-transparent text-categorical-1-content",
42
+ soft: "border-categorical-1-border bg-categorical-1-surface text-categorical-1-content",
43
+ solid:
44
+ "border-transparent bg-categorical-1-solid text-categorical-1-on-solid",
45
+ },
46
+ 2: {
47
+ ghost:
48
+ "border-transparent bg-transparent text-categorical-2-content",
49
+ outline:
50
+ "border-categorical-2-border bg-transparent text-categorical-2-content",
51
+ soft: "border-categorical-2-border bg-categorical-2-surface text-categorical-2-content",
52
+ solid:
53
+ "border-transparent bg-categorical-2-solid text-categorical-2-on-solid",
54
+ },
55
+ 3: {
56
+ ghost:
57
+ "border-transparent bg-transparent text-categorical-3-content",
58
+ outline:
59
+ "border-categorical-3-border bg-transparent text-categorical-3-content",
60
+ soft: "border-categorical-3-border bg-categorical-3-surface text-categorical-3-content",
61
+ solid:
62
+ "border-transparent bg-categorical-3-solid text-categorical-3-on-solid",
63
+ },
64
+ 4: {
65
+ ghost:
66
+ "border-transparent bg-transparent text-categorical-4-content",
67
+ outline:
68
+ "border-categorical-4-border bg-transparent text-categorical-4-content",
69
+ soft: "border-categorical-4-border bg-categorical-4-surface text-categorical-4-content",
70
+ solid:
71
+ "border-transparent bg-categorical-4-solid text-categorical-4-on-solid",
72
+ },
73
+ 5: {
74
+ ghost:
75
+ "border-transparent bg-transparent text-categorical-5-content",
76
+ outline:
77
+ "border-categorical-5-border bg-transparent text-categorical-5-content",
78
+ soft: "border-categorical-5-border bg-categorical-5-surface text-categorical-5-content",
79
+ solid:
80
+ "border-transparent bg-categorical-5-solid text-categorical-5-on-solid",
81
+ },
82
+ 6: {
83
+ ghost:
84
+ "border-transparent bg-transparent text-categorical-6-content",
85
+ outline:
86
+ "border-categorical-6-border bg-transparent text-categorical-6-content",
87
+ soft: "border-categorical-6-border bg-categorical-6-surface text-categorical-6-content",
88
+ solid:
89
+ "border-transparent bg-categorical-6-solid text-categorical-6-on-solid",
90
+ },
91
+ 7: {
92
+ ghost:
93
+ "border-transparent bg-transparent text-categorical-7-content",
94
+ outline:
95
+ "border-categorical-7-border bg-transparent text-categorical-7-content",
96
+ soft: "border-categorical-7-border bg-categorical-7-surface text-categorical-7-content",
97
+ solid:
98
+ "border-transparent bg-categorical-7-solid text-categorical-7-on-solid",
99
+ },
100
+ 8: {
101
+ ghost:
102
+ "border-transparent bg-transparent text-categorical-8-content",
103
+ outline:
104
+ "border-categorical-8-border bg-transparent text-categorical-8-content",
105
+ soft: "border-categorical-8-border bg-categorical-8-surface text-categorical-8-content",
106
+ solid:
107
+ "border-transparent bg-categorical-8-solid text-categorical-8-on-solid",
108
+ },
109
+ 9: {
110
+ ghost:
111
+ "border-transparent bg-transparent text-categorical-9-content",
112
+ outline:
113
+ "border-categorical-9-border bg-transparent text-categorical-9-content",
114
+ soft: "border-categorical-9-border bg-categorical-9-surface text-categorical-9-content",
115
+ solid:
116
+ "border-transparent bg-categorical-9-solid text-categorical-9-on-solid",
117
+ },
118
+ 10: {
119
+ ghost:
120
+ "border-transparent bg-transparent text-categorical-10-content",
121
+ outline:
122
+ "border-categorical-10-border bg-transparent text-categorical-10-content",
123
+ soft: "border-categorical-10-border bg-categorical-10-surface text-categorical-10-content",
124
+ solid:
125
+ "border-transparent bg-categorical-10-solid text-categorical-10-on-solid",
126
+ },
127
+ }
128
+
129
+ /**
130
+ * Hover, split out for exactly the reason `INTENT_HOVER_CLASS` is:
131
+ * most things wearing a colour are not interactive. A label pill in
132
+ * a task list that lightens under the cursor is telling the user it
133
+ * can be clicked, and a `Badge` cannot be. So an interactive
134
+ * component opts in, and a static one cannot accidentally opt in.
135
+ *
136
+ * `solid` hovers toward `solidHover`, which for this family is
137
+ * **brighter** rather than deeper — the fill carries a near-black
138
+ * label, so it moves away from it in the opposite direction to an
139
+ * intent's white-labelled fill.
140
+ */
141
+ export const CATEGORICAL_HOVER_CLASS: Record<
142
+ CategoricalIndex,
143
+ Record<IntentAppearance, string>
144
+ > = {
145
+ 1: {
146
+ ghost: "hover:bg-categorical-1-surface",
147
+ outline: "hover:bg-categorical-1-surface",
148
+ soft: "hover:bg-categorical-1-surface-hover",
149
+ solid: "hover:bg-categorical-1-solid-hover",
150
+ },
151
+ 2: {
152
+ ghost: "hover:bg-categorical-2-surface",
153
+ outline: "hover:bg-categorical-2-surface",
154
+ soft: "hover:bg-categorical-2-surface-hover",
155
+ solid: "hover:bg-categorical-2-solid-hover",
156
+ },
157
+ 3: {
158
+ ghost: "hover:bg-categorical-3-surface",
159
+ outline: "hover:bg-categorical-3-surface",
160
+ soft: "hover:bg-categorical-3-surface-hover",
161
+ solid: "hover:bg-categorical-3-solid-hover",
162
+ },
163
+ 4: {
164
+ ghost: "hover:bg-categorical-4-surface",
165
+ outline: "hover:bg-categorical-4-surface",
166
+ soft: "hover:bg-categorical-4-surface-hover",
167
+ solid: "hover:bg-categorical-4-solid-hover",
168
+ },
169
+ 5: {
170
+ ghost: "hover:bg-categorical-5-surface",
171
+ outline: "hover:bg-categorical-5-surface",
172
+ soft: "hover:bg-categorical-5-surface-hover",
173
+ solid: "hover:bg-categorical-5-solid-hover",
174
+ },
175
+ 6: {
176
+ ghost: "hover:bg-categorical-6-surface",
177
+ outline: "hover:bg-categorical-6-surface",
178
+ soft: "hover:bg-categorical-6-surface-hover",
179
+ solid: "hover:bg-categorical-6-solid-hover",
180
+ },
181
+ 7: {
182
+ ghost: "hover:bg-categorical-7-surface",
183
+ outline: "hover:bg-categorical-7-surface",
184
+ soft: "hover:bg-categorical-7-surface-hover",
185
+ solid: "hover:bg-categorical-7-solid-hover",
186
+ },
187
+ 8: {
188
+ ghost: "hover:bg-categorical-8-surface",
189
+ outline: "hover:bg-categorical-8-surface",
190
+ soft: "hover:bg-categorical-8-surface-hover",
191
+ solid: "hover:bg-categorical-8-solid-hover",
192
+ },
193
+ 9: {
194
+ ghost: "hover:bg-categorical-9-surface",
195
+ outline: "hover:bg-categorical-9-surface",
196
+ soft: "hover:bg-categorical-9-surface-hover",
197
+ solid: "hover:bg-categorical-9-solid-hover",
198
+ },
199
+ 10: {
200
+ ghost: "hover:bg-categorical-10-surface",
201
+ outline: "hover:bg-categorical-10-surface",
202
+ soft: "hover:bg-categorical-10-surface-hover",
203
+ solid: "hover:bg-categorical-10-solid-hover",
204
+ },
205
+ }
206
+
207
+ /**
208
+ * The fill on its own, for the parts of a component that are a dot
209
+ * or a bar rather than a box — the swatch beside a label in a
210
+ * picker, a series marker on a chart. Same values, no border and no
211
+ * hover, because neither of those is a surface anyone can point at.
212
+ */
213
+ export const CATEGORICAL_SOLID_FILL_CLASS: Record<
214
+ CategoricalIndex,
215
+ string
216
+ > = {
217
+ 1: "bg-categorical-1-solid",
218
+ 2: "bg-categorical-2-solid",
219
+ 3: "bg-categorical-3-solid",
220
+ 4: "bg-categorical-4-solid",
221
+ 5: "bg-categorical-5-solid",
222
+ 6: "bg-categorical-6-solid",
223
+ 7: "bg-categorical-7-solid",
224
+ 8: "bg-categorical-8-solid",
225
+ 9: "bg-categorical-9-solid",
226
+ 10: "bg-categorical-10-solid",
227
+ }
228
+
229
+ /** The same ten as text, for a glyph or a dot's ring. */
230
+ export const CATEGORICAL_CONTENT_CLASS: Record<
231
+ CategoricalIndex,
232
+ string
233
+ > = {
234
+ 1: "text-categorical-1-content",
235
+ 2: "text-categorical-2-content",
236
+ 3: "text-categorical-3-content",
237
+ 4: "text-categorical-4-content",
238
+ 5: "text-categorical-5-content",
239
+ 6: "text-categorical-6-content",
240
+ 7: "text-categorical-7-content",
241
+ 8: "text-categorical-8-content",
242
+ 9: "text-categorical-9-content",
243
+ 10: "text-categorical-10-content",
244
+ }
package/src/index.ts CHANGED
@@ -65,6 +65,26 @@ export type {
65
65
  export { Alert } from "./Alert/Alert.tsx"
66
66
  export type { BadgeProps } from "./Badge/Badge.tsx"
67
67
  export { Badge } from "./Badge/Badge.tsx"
68
+ export type {
69
+ BoardMove,
70
+ BoardProps,
71
+ } from "./Board/Board.tsx"
72
+ export { Board } from "./Board/Board.tsx"
73
+ export type { BoardItem } from "./Board/BoardCard.tsx"
74
+ export type { BoardLane } from "./Board/BoardLaneList.tsx"
75
+ export type {
76
+ BoardDropLane,
77
+ BoardDropTarget,
78
+ BoardPoint,
79
+ BoardRect,
80
+ } from "./Board/boardMove.ts"
81
+ export {
82
+ chooseDropIndex,
83
+ chooseDropTarget,
84
+ describeMove,
85
+ getIsMoveMeaningful,
86
+ toSettledIndex,
87
+ } from "./Board/boardMove.ts"
68
88
  export type { ButtonProps } from "./Button/Button.tsx"
69
89
  export { Button } from "./Button/Button.tsx"
70
90
  export type { ButtonLinkProps } from "./ButtonLink/ButtonLink.tsx"
@@ -86,6 +106,12 @@ export type {
86
106
  export { ColorSchemeToggle } from "./ColorSchemeToggle/ColorSchemeToggle.tsx"
87
107
  export type { ComboboxProps } from "./Combobox/Combobox.tsx"
88
108
  export { Combobox } from "./Combobox/Combobox.tsx"
109
+ export {
110
+ CATEGORICAL_APPEARANCE_CLASS,
111
+ CATEGORICAL_CONTENT_CLASS,
112
+ CATEGORICAL_HOVER_CLASS,
113
+ CATEGORICAL_SOLID_FILL_CLASS,
114
+ } from "./categoricalStyles.ts"
89
115
  export type { BadgeSize } from "./controlStyles.ts"
90
116
  export {
91
117
  BADGE_SIZE_CLASS,
@@ -97,6 +123,53 @@ export {
97
123
  MIN_TOUCH_TARGET_CLASS,
98
124
  SPINNER_SIZE_CLASS,
99
125
  } from "./controlStyles.ts"
126
+ export type {
127
+ DataTableColumn,
128
+ DataTableProps,
129
+ DataTableSelection,
130
+ DataTableSort,
131
+ } from "./DataTable/DataTable.tsx"
132
+ export { DataTable } from "./DataTable/DataTable.tsx"
133
+ export type { DatePickerProps } from "./DatePicker/DatePicker.tsx"
134
+ export { DatePicker } from "./DatePicker/DatePicker.tsx"
135
+ export type { DatePreset } from "./DatePicker/datePresets.ts"
136
+ export { DEFAULT_DATE_PRESETS } from "./DatePicker/datePresets.ts"
137
+ export type {
138
+ DateInputKeywords,
139
+ DateInputResult,
140
+ ParseDateInputOptions,
141
+ } from "./DatePicker/parseDateInput.ts"
142
+ export {
143
+ DEFAULT_DATE_INPUT_KEYWORDS,
144
+ parseDateInput,
145
+ } from "./DatePicker/parseDateInput.ts"
146
+ export type {
147
+ DateRange,
148
+ PlainDate,
149
+ PlainDateFormatOptions,
150
+ } from "./DatePicker/plainDate.ts"
151
+ export {
152
+ addDays,
153
+ addMonths,
154
+ clampPlainDate,
155
+ comparePlainDates,
156
+ formatPlainDate,
157
+ getDayNumber,
158
+ getDaysBetween,
159
+ getDaysInMonth,
160
+ getFirstDayOfWeek,
161
+ getIsLeapYear,
162
+ getIsSameDay,
163
+ getIsValidPlainDate,
164
+ getIsWithinRange,
165
+ getLocalPlainDate,
166
+ getMonthNames,
167
+ getPlainDateFromDayNumber,
168
+ getWeekday,
169
+ getWeekdayNames,
170
+ parseIsoDate,
171
+ toIsoDate,
172
+ } from "./DatePicker/plainDate.ts"
100
173
  export type {
101
174
  DialogProps,
102
175
  DialogSize,
@@ -146,6 +219,22 @@ export type {
146
219
  export { LogViewer } from "./LogViewer/LogViewer.tsx"
147
220
  export type { MainProps } from "./Main/Main.tsx"
148
221
  export { Main } from "./Main/Main.tsx"
222
+ export type {
223
+ MarkdownEditorProps,
224
+ MarkdownImageUpload,
225
+ } from "./MarkdownEditor/MarkdownEditor.tsx"
226
+ export { MarkdownEditor } from "./MarkdownEditor/MarkdownEditor.tsx"
227
+ export { toMarkdownImage } from "./MarkdownEditor/markdownCommands.ts"
228
+ export type {
229
+ MarkdownLine,
230
+ MarkdownLineKind,
231
+ MarkdownSpan,
232
+ MarkdownSpanKind,
233
+ } from "./MarkdownEditor/markdownSpans.ts"
234
+ export {
235
+ toLineIndex,
236
+ toMarkdownLines,
237
+ } from "./MarkdownEditor/markdownSpans.ts"
149
238
  export type {
150
239
  MediaTileProps,
151
240
  MediaTileRatio,
@@ -211,9 +211,30 @@ test("a container-query component is never storied in a shrink-to-fit cell", ()
211
211
  // inside it.
212
212
  expect(containerComponents).toEqual([
213
213
  "Alert",
214
+ // `Board` declares **two** containers and neither is the element
215
+ // that queries it: the board's own box decides how many lanes are
216
+ // on screen, and each lane's list — in `BoardLaneList.tsx`, which
217
+ // is a member rather than a component by the `<Name>/<Name>.tsx`
218
+ // rule — decides what shape a card is. Only the first of those is
219
+ // in this list, and that is enough: it is `<Board` a story renders,
220
+ // so `<Board` is what a shrink-to-fit `StoryCell` would collapse.
221
+ "Board",
214
222
  "Card",
223
+ // `DataTable` joins them as the first component whose *layout*
224
+ // is the container query rather than its padding: below `--cq-md`
225
+ // a row stops being a row and becomes a labelled block, so the
226
+ // whole point of the component is a query it declares itself.
227
+ "DataTable",
228
+ // `DatePicker`'s panel is the container, and it is the first one
229
+ // here whose container is *not* the element a caller sized. A
230
+ // portalled panel is clamped to the space floating-ui found for
231
+ // it, so the calendar reads that and not the window — which is
232
+ // the case a media query cannot express at all, and the case the
233
+ // owner is in whenever he browses zoomed in.
234
+ "DatePicker",
215
235
  "EmptyState",
216
236
  "Main",
237
+ "MarkdownEditor",
217
238
  "MediaTile",
218
239
  ])
219
240
 
@@ -374,12 +395,42 @@ test("the barrel is the only place components are re-exported", async () => {
374
395
  // the barrel by the `<Name>/<Name>.tsx` rule, exactly as
375
396
  // `SegmentedOption` and `ListboxOption` do.
376
397
  //
398
+ // `MarkdownEditor` — Docket's live hybrid editor, a real
399
+ // `<textarea>` with a painted layer behind it — is +1 -> 46. Its
400
+ // `markdownSpans.ts` and `markdownCommands.ts` are pure modules
401
+ // rather than components and stay out of this count by the
402
+ // `<Name>/<Name>.tsx` rule.
403
+ //
377
404
  // `Picker` — a `Listbox` with its trigger already attached — is +1 ->
378
405
  // 45. It is a component rather than a recipe in the docs because the
379
406
  // fleet wrote it four separate times (plex-channels' `SelectListbox`,
380
407
  // board-games' `SelectMenu`, mux-magic's `ListboxPicker`, and twice
381
408
  // inside this package), each with its own hand-rolled chevron.
382
- expect(componentNames.length).toBe(45)
409
+ // `DatePicker` — a typed date field with a calendar dialog — is +1
410
+ // -> 47. `DateGrid.tsx`, `plainDate.ts` and `parseDateInput.ts` are
411
+ // its members and stay out of this count by the `<Name>/<Name>.tsx`
412
+ // rule, exactly as `SegmentedOption` and `ListboxOption` do. The
413
+ // two `.ts` ones are still exported from the barrel: the calendar
414
+ // arithmetic and the input grammar are the parts a consumer needs
415
+ // *outside* the field (Docket counts staleness in days), and they
416
+ // reach nothing but `Intl`.
417
+ //
418
+ // `DataTable` — the reflowing table Docket's task lists, backlog and
419
+ // search results need — is +1 -> 48. It **composes**
420
+ // `SortableTableHeader` rather than subsuming it: that component is
421
+ // already in a published 1.0.0 with a consumer of its own, so
422
+ // removing it would be a breaking change bought for nothing, and
423
+ // both stay in this count.
424
+ //
425
+ // `Board` — lanes of cards, sized by their container and moved by
426
+ // keyboard or by pointer — is +1 -> 49. It is the library's first
427
+ // component whose own operation is a **write** rather than a
428
+ // selection, and the first to declare two nested containers.
429
+ // `BoardCard.tsx`, `BoardLaneList.tsx`, `boardMove.ts` and
430
+ // `useBoardDrag.ts` are its members and stay out of this count and
431
+ // the barrel by the `<Name>/<Name>.tsx` rule, exactly as
432
+ // `ToolbarSlot` and `QueryBuilderRow` do.
433
+ expect(componentNames.length).toBe(49)
383
434
 
384
435
  for (const name of componentNames) {
385
436
  expect(barrel).toContain(`export { ${name} }`)