altitude-design 0.4.0 → 0.4.2

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 (280) hide show
  1. package/README.md +1 -0
  2. package/dist/guidance/components/AILabel/README.md +35 -0
  3. package/dist/guidance/components/Accordion/README.md +25 -0
  4. package/dist/guidance/components/AgentFleet/README.md +48 -1
  5. package/dist/guidance/components/AgentLanes/README.md +32 -0
  6. package/dist/guidance/components/AgentRequests/README.md +47 -0
  7. package/dist/guidance/components/Alert/README.md +49 -0
  8. package/dist/guidance/components/AltitudeProvider/README.md +63 -0
  9. package/dist/guidance/components/AppShell/README.md +70 -0
  10. package/dist/guidance/components/AreaChart/README.md +58 -0
  11. package/dist/guidance/components/Artifact/README.md +69 -0
  12. package/dist/guidance/components/Avatar/README.md +41 -0
  13. package/dist/guidance/components/Await/README.md +48 -0
  14. package/dist/guidance/components/Badge/README.md +44 -0
  15. package/dist/guidance/components/BarChart/README.md +62 -0
  16. package/dist/guidance/components/BarList/README.md +58 -0
  17. package/dist/guidance/components/Board/README.md +67 -0
  18. package/dist/guidance/components/Breadcrumb/README.md +31 -0
  19. package/dist/guidance/components/Button/README.md +41 -0
  20. package/dist/guidance/components/Card/README.md +38 -0
  21. package/dist/guidance/components/ChartBrush/README.md +57 -0
  22. package/dist/guidance/components/ChartContainer/README.md +77 -0
  23. package/dist/guidance/components/ChartLegend/README.md +43 -0
  24. package/dist/guidance/components/ChartReference/README.md +74 -0
  25. package/dist/guidance/components/ChartTooltip/README.md +63 -0
  26. package/dist/guidance/components/Checkbox/README.md +35 -0
  27. package/dist/guidance/components/Checklist/README.md +66 -0
  28. package/dist/guidance/components/CodeBlock/README.md +44 -0
  29. package/dist/guidance/components/ColorPicker/README.md +39 -0
  30. package/dist/guidance/components/Combobox/README.md +35 -0
  31. package/dist/guidance/components/CommandPalette/README.md +177 -0
  32. package/dist/guidance/components/CommentThread/README.md +51 -0
  33. package/dist/guidance/components/Confirmation/README.md +60 -0
  34. package/dist/guidance/components/ContextMenu/README.md +51 -0
  35. package/dist/guidance/components/Conversation/README.md +124 -0
  36. package/dist/guidance/components/CopyButton/README.md +31 -0
  37. package/dist/guidance/components/DataGrid/README.md +75 -0
  38. package/dist/guidance/components/DatePicker/README.md +49 -0
  39. package/dist/guidance/components/Dialog/README.md +175 -0
  40. package/dist/guidance/components/DiffView/README.md +77 -0
  41. package/dist/guidance/components/DotPlot/README.md +44 -0
  42. package/dist/guidance/components/Drilldown/README.md +28 -0
  43. package/dist/guidance/components/DropZone/README.md +50 -0
  44. package/dist/guidance/components/DropdownMenu/README.md +44 -0
  45. package/dist/guidance/components/EditRegion/README.md +159 -0
  46. package/dist/guidance/components/EmptyState/README.md +49 -0
  47. package/dist/guidance/components/FeatureGate/README.md +39 -0
  48. package/dist/guidance/components/Field/README.md +36 -0
  49. package/dist/guidance/components/FilterBar/README.md +63 -0
  50. package/dist/guidance/components/FormattedNumber/README.md +44 -0
  51. package/dist/guidance/components/Heatmap/README.md +71 -0
  52. package/dist/guidance/components/HoldButton/README.md +32 -0
  53. package/dist/guidance/components/HoverCard/README.md +53 -0
  54. package/dist/guidance/components/InlineCitation/README.md +32 -0
  55. package/dist/guidance/components/InlineEdit/README.md +247 -1
  56. package/dist/guidance/components/Input/README.md +36 -0
  57. package/dist/guidance/components/InputOTP/README.md +46 -0
  58. package/dist/guidance/components/ItemGroup/README.md +100 -0
  59. package/dist/guidance/components/Kbd/README.md +27 -0
  60. package/dist/guidance/components/LineChart/README.md +62 -0
  61. package/dist/guidance/components/Link/README.md +41 -0
  62. package/dist/guidance/components/Markdown/README.md +81 -0
  63. package/dist/guidance/components/Meter/README.md +52 -0
  64. package/dist/guidance/components/NotificationCenter/README.md +39 -0
  65. package/dist/guidance/components/NumberInput/README.md +37 -0
  66. package/dist/guidance/components/PageHeader/README.md +45 -0
  67. package/dist/guidance/components/Pagination/README.md +33 -0
  68. package/dist/guidance/components/PasswordInput/README.md +36 -0
  69. package/dist/guidance/components/PermissionMatrix/README.md +40 -0
  70. package/dist/guidance/components/Pile/README.md +42 -0
  71. package/dist/guidance/components/PlanPicker/README.md +37 -0
  72. package/dist/guidance/components/Popover/README.md +36 -0
  73. package/dist/guidance/components/Progress/README.md +31 -0
  74. package/dist/guidance/components/PromptInput/README.md +98 -0
  75. package/dist/guidance/components/PropertyList/README.md +80 -0
  76. package/dist/guidance/components/Question/README.md +54 -0
  77. package/dist/guidance/components/RadioGroup/README.md +37 -0
  78. package/dist/guidance/components/RelativeTime/README.md +47 -0
  79. package/dist/guidance/components/RemovalReceipt/README.md +77 -0
  80. package/dist/guidance/components/Resizable/README.md +29 -0
  81. package/dist/guidance/components/SaveBar/README.md +38 -0
  82. package/dist/guidance/components/SavedViews/README.md +58 -0
  83. package/dist/guidance/components/ScatterChart/README.md +63 -0
  84. package/dist/guidance/components/Schedule/README.md +66 -0
  85. package/dist/guidance/components/ScrollArea/README.md +28 -0
  86. package/dist/guidance/components/SecretField/README.md +46 -0
  87. package/dist/guidance/components/Select/README.md +70 -0
  88. package/dist/guidance/components/Separator/README.md +44 -0
  89. package/dist/guidance/components/Sheet/README.md +94 -0
  90. package/dist/guidance/components/Skeleton/README.md +36 -0
  91. package/dist/guidance/components/Slider/README.md +35 -0
  92. package/dist/guidance/components/SortableList/README.md +34 -0
  93. package/dist/guidance/components/Sparkline/README.md +58 -0
  94. package/dist/guidance/components/SplitButton/README.md +39 -0
  95. package/dist/guidance/components/Stat/README.md +61 -0
  96. package/dist/guidance/components/Stepper/README.md +61 -0
  97. package/dist/guidance/components/Suggestions/README.md +58 -0
  98. package/dist/guidance/components/Surface/README.md +28 -0
  99. package/dist/guidance/components/Switch/README.md +29 -0
  100. package/dist/guidance/components/SyncIndicator/README.md +54 -0
  101. package/dist/guidance/components/Table/README.md +62 -0
  102. package/dist/guidance/components/Tabs/README.md +55 -0
  103. package/dist/guidance/components/TagInput/README.md +50 -0
  104. package/dist/guidance/components/Terminal/README.md +42 -0
  105. package/dist/guidance/components/ThreadList/README.md +73 -0
  106. package/dist/guidance/components/TimeInput/README.md +37 -0
  107. package/dist/guidance/components/Timeline/README.md +55 -0
  108. package/dist/guidance/components/Toaster/README.md +38 -0
  109. package/dist/guidance/components/Toggle/README.md +52 -0
  110. package/dist/guidance/components/Toolbar/README.md +69 -0
  111. package/dist/guidance/components/Tooltip/README.md +29 -0
  112. package/dist/guidance/components/Tour/README.md +44 -0
  113. package/dist/guidance/components/Trace/README.md +92 -0
  114. package/dist/guidance/components/Tree/README.md +85 -0
  115. package/dist/guidance/index.json +117 -117
  116. package/dist/guidance/llms.txt +1 -1
  117. package/dist/guidance/manifest.json +120 -120
  118. package/dist/guidance/visual/ActionsInPlace.md +5 -5
  119. package/dist/guidance/visual/Distinctiveness.md +6 -2
  120. package/dist/guidance/visual/Research.md +14 -2
  121. package/dist/guidance/visual/Theming.md +1 -1
  122. package/dist/types/agent-fleet.d.ts +28 -0
  123. package/dist/types/ai-label.d.ts +7 -0
  124. package/dist/types/app-shell.d.ts +23 -0
  125. package/dist/types/area-chart.d.ts +8 -0
  126. package/dist/types/artifact.d.ts +14 -0
  127. package/dist/types/bar-chart.d.ts +6 -0
  128. package/dist/types/bar-list.d.ts +6 -0
  129. package/dist/types/breadcrumb.d.ts +6 -0
  130. package/dist/types/chart-brush.d.ts +6 -0
  131. package/dist/types/chart.d.ts +29 -1
  132. package/dist/types/checklist.d.ts +6 -0
  133. package/dist/types/code-block.d.ts +6 -0
  134. package/dist/types/collections.d.ts +15 -0
  135. package/dist/types/color-picker.d.ts +6 -0
  136. package/dist/types/command.d.ts +13 -0
  137. package/dist/types/comment-thread.d.ts +11 -0
  138. package/dist/types/confirmation.d.ts +8 -0
  139. package/dist/types/context-menu.d.ts +11 -0
  140. package/dist/types/controls.d.ts +174 -10
  141. package/dist/types/conversation.d.ts +21 -0
  142. package/dist/types/copy-button.d.ts +6 -0
  143. package/dist/types/core.d.ts +31 -0
  144. package/dist/types/data-grid.d.ts +25 -0
  145. package/dist/types/date-picker.d.ts +6 -0
  146. package/dist/types/diff-view.d.ts +8 -0
  147. package/dist/types/dot-plot.d.ts +6 -0
  148. package/dist/types/edit-region.d.ts +23 -0
  149. package/dist/types/empty.d.ts +9 -0
  150. package/dist/types/feature-gate.d.ts +8 -0
  151. package/dist/types/filter-bar.d.ts +8 -0
  152. package/dist/types/formatted-number.d.ts +6 -0
  153. package/dist/types/heatmap.d.ts +6 -0
  154. package/dist/types/inline-citation.d.ts +6 -0
  155. package/dist/types/inline-edit.d.ts +7 -0
  156. package/dist/types/input-otp.d.ts +11 -0
  157. package/dist/types/item-group.d.ts +29 -0
  158. package/dist/types/line-chart.d.ts +6 -0
  159. package/dist/types/link.d.ts +8 -1
  160. package/dist/types/markdown.d.ts +6 -0
  161. package/dist/types/meter.d.ts +6 -0
  162. package/dist/types/nav.d.ts +6 -0
  163. package/dist/types/notifications.d.ts +6 -0
  164. package/dist/types/number-input.d.ts +6 -0
  165. package/dist/types/overlays.d.ts +61 -0
  166. package/dist/types/page-header.d.ts +14 -0
  167. package/dist/types/pagination.d.ts +6 -0
  168. package/dist/types/password-input.d.ts +6 -0
  169. package/dist/types/permission-matrix.d.ts +7 -0
  170. package/dist/types/pile.d.ts +6 -0
  171. package/dist/types/plan-picker.d.ts +6 -0
  172. package/dist/types/prompt-input.d.ts +11 -0
  173. package/dist/types/property-list.d.ts +17 -0
  174. package/dist/types/question.d.ts +11 -0
  175. package/dist/types/relative-time.d.ts +8 -1
  176. package/dist/types/removal-receipt.d.ts +18 -1
  177. package/dist/types/save-bar.d.ts +6 -0
  178. package/dist/types/saved-views.d.ts +12 -0
  179. package/dist/types/scatter-chart.d.ts +8 -0
  180. package/dist/types/schedule.d.ts +21 -0
  181. package/dist/types/secret-field.d.ts +6 -0
  182. package/dist/types/select.d.ts +12 -0
  183. package/dist/types/separator.d.ts +8 -1
  184. package/dist/types/sheet.d.ts +19 -0
  185. package/dist/types/sparkline.d.ts +6 -0
  186. package/dist/types/split-button.d.ts +7 -0
  187. package/dist/types/stat.d.ts +7 -0
  188. package/dist/types/stepper.d.ts +12 -0
  189. package/dist/types/suggestions.d.ts +10 -0
  190. package/dist/types/sync-indicator.d.ts +8 -0
  191. package/dist/types/tag-input.d.ts +8 -0
  192. package/dist/types/terminal.d.ts +6 -0
  193. package/dist/types/thread-list.d.ts +16 -0
  194. package/dist/types/time-input.d.ts +6 -0
  195. package/dist/types/timeline.d.ts +9 -0
  196. package/dist/types/toggle.d.ts +6 -0
  197. package/dist/types/toolbar.d.ts +20 -0
  198. package/dist/types/tour.d.ts +10 -0
  199. package/dist/types/trace.d.ts +16 -0
  200. package/dist/types/tree.d.ts +21 -0
  201. package/dist/types-cjs/agent-fleet.d.cts +28 -0
  202. package/dist/types-cjs/ai-label.d.cts +7 -0
  203. package/dist/types-cjs/app-shell.d.cts +23 -0
  204. package/dist/types-cjs/area-chart.d.cts +8 -0
  205. package/dist/types-cjs/artifact.d.cts +14 -0
  206. package/dist/types-cjs/bar-chart.d.cts +6 -0
  207. package/dist/types-cjs/bar-list.d.cts +6 -0
  208. package/dist/types-cjs/breadcrumb.d.cts +6 -0
  209. package/dist/types-cjs/chart-brush.d.cts +6 -0
  210. package/dist/types-cjs/chart.d.cts +29 -1
  211. package/dist/types-cjs/checklist.d.cts +6 -0
  212. package/dist/types-cjs/code-block.d.cts +6 -0
  213. package/dist/types-cjs/collections.d.cts +15 -0
  214. package/dist/types-cjs/color-picker.d.cts +6 -0
  215. package/dist/types-cjs/command.d.cts +13 -0
  216. package/dist/types-cjs/comment-thread.d.cts +11 -0
  217. package/dist/types-cjs/confirmation.d.cts +8 -0
  218. package/dist/types-cjs/context-menu.d.cts +11 -0
  219. package/dist/types-cjs/controls.d.cts +174 -10
  220. package/dist/types-cjs/conversation.d.cts +21 -0
  221. package/dist/types-cjs/copy-button.d.cts +6 -0
  222. package/dist/types-cjs/core.d.cts +31 -0
  223. package/dist/types-cjs/data-grid.d.cts +25 -0
  224. package/dist/types-cjs/date-picker.d.cts +6 -0
  225. package/dist/types-cjs/diff-view.d.cts +8 -0
  226. package/dist/types-cjs/dot-plot.d.cts +6 -0
  227. package/dist/types-cjs/edit-region.d.cts +23 -0
  228. package/dist/types-cjs/empty.d.cts +9 -0
  229. package/dist/types-cjs/feature-gate.d.cts +8 -0
  230. package/dist/types-cjs/filter-bar.d.cts +8 -0
  231. package/dist/types-cjs/formatted-number.d.cts +6 -0
  232. package/dist/types-cjs/heatmap.d.cts +6 -0
  233. package/dist/types-cjs/inline-citation.d.cts +6 -0
  234. package/dist/types-cjs/inline-edit.d.cts +7 -0
  235. package/dist/types-cjs/input-otp.d.cts +11 -0
  236. package/dist/types-cjs/item-group.d.cts +29 -0
  237. package/dist/types-cjs/line-chart.d.cts +6 -0
  238. package/dist/types-cjs/link.d.cts +8 -1
  239. package/dist/types-cjs/markdown.d.cts +6 -0
  240. package/dist/types-cjs/meter.d.cts +6 -0
  241. package/dist/types-cjs/nav.d.cts +6 -0
  242. package/dist/types-cjs/notifications.d.cts +6 -0
  243. package/dist/types-cjs/number-input.d.cts +6 -0
  244. package/dist/types-cjs/overlays.d.cts +61 -0
  245. package/dist/types-cjs/page-header.d.cts +14 -0
  246. package/dist/types-cjs/pagination.d.cts +6 -0
  247. package/dist/types-cjs/password-input.d.cts +6 -0
  248. package/dist/types-cjs/permission-matrix.d.cts +7 -0
  249. package/dist/types-cjs/pile.d.cts +6 -0
  250. package/dist/types-cjs/plan-picker.d.cts +6 -0
  251. package/dist/types-cjs/prompt-input.d.cts +11 -0
  252. package/dist/types-cjs/property-list.d.cts +17 -0
  253. package/dist/types-cjs/question.d.cts +11 -0
  254. package/dist/types-cjs/relative-time.d.cts +8 -1
  255. package/dist/types-cjs/removal-receipt.d.cts +18 -1
  256. package/dist/types-cjs/save-bar.d.cts +6 -0
  257. package/dist/types-cjs/saved-views.d.cts +12 -0
  258. package/dist/types-cjs/scatter-chart.d.cts +8 -0
  259. package/dist/types-cjs/schedule.d.cts +21 -0
  260. package/dist/types-cjs/secret-field.d.cts +6 -0
  261. package/dist/types-cjs/select.d.cts +12 -0
  262. package/dist/types-cjs/separator.d.cts +8 -1
  263. package/dist/types-cjs/sheet.d.cts +19 -0
  264. package/dist/types-cjs/sparkline.d.cts +6 -0
  265. package/dist/types-cjs/split-button.d.cts +7 -0
  266. package/dist/types-cjs/stat.d.cts +7 -0
  267. package/dist/types-cjs/stepper.d.cts +12 -0
  268. package/dist/types-cjs/suggestions.d.cts +10 -0
  269. package/dist/types-cjs/sync-indicator.d.cts +8 -0
  270. package/dist/types-cjs/tag-input.d.cts +8 -0
  271. package/dist/types-cjs/terminal.d.cts +6 -0
  272. package/dist/types-cjs/thread-list.d.cts +16 -0
  273. package/dist/types-cjs/time-input.d.cts +6 -0
  274. package/dist/types-cjs/timeline.d.cts +9 -0
  275. package/dist/types-cjs/toggle.d.cts +6 -0
  276. package/dist/types-cjs/toolbar.d.cts +20 -0
  277. package/dist/types-cjs/tour.d.cts +10 -0
  278. package/dist/types-cjs/trace.d.cts +16 -0
  279. package/dist/types-cjs/tree.d.cts +21 -0
  280. package/package.json +1 -1
@@ -39,3 +39,77 @@ Reading a chart against something: an SLO target, a budget, a normal range, the
39
39
  - Do keep annotations to a few words; the plate is a label, not a note.
40
40
  - Don't dash a reference line; a dash means "projected".
41
41
  - Don't use a reference band for data. A range in the data is an uncertainty band (`LineChart`'s `band`).
42
+
43
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
44
+ ## Example
45
+
46
+ `ChartReference` as first used in its preview:
47
+
48
+ ```tsx
49
+ import { ChartReference } from 'altitude-design';
50
+
51
+ <ChartReference y={[0.995, 1]} label="Normal range" />
52
+ ```
53
+
54
+ The full preview, as rendered in Storybook and checked in CI (`cr`, `row` are the preview page's own layout classes, not Altitude's):
55
+
56
+ ```tsx
57
+ import * as React from 'react';
58
+ import { BarChart, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, ChartAnnotation, ChartContainer, ChartReference, LineChart } from 'altitude-design';
59
+
60
+ const uptime = Array.from({ length: 30 }, (_, i) => ({ day: `2026-03-${String(i + 1).padStart(2, '0')}`, uptime: [0.9995, 0.9991, 0.9998, 0.9962, 0.9971, 0.9993, 0.9997, 0.9999, 0.9994, 0.9988, 0.9996, 0.9999, 0.9997, 0.9921, 0.9984, 0.9995, 0.9998, 0.9996, 0.9999, 0.9997, 0.9993, 0.9996, 0.9998, 0.9999, 0.9995, 0.9997, 0.9999, 0.9998, 0.9996, 0.9999][i] }));
61
+ const pct = new Intl.NumberFormat('en', { style: 'percent', maximumFractionDigits: 2 });
62
+ const usd = new Intl.NumberFormat('en', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
63
+ const weekA = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map((day, i) => ({ day, cost: [412, 455, 498, 430, 610, 220, 190][i] }));
64
+ const weekB = weekA.map((d, i) => ({ ...d, cost: [520, 590, 640, 575, 780, 300, 260][i] }));
65
+ const teams = [['Search', 92], ['Checkout', 81], ['Accounts', 74], ['Messaging', 66], ['Billing', 58]].map(([team, score]) => ({ team, score }));
66
+ const deploys = Array.from({ length: 21 }, (_, i) => ({ day: `2026-04-${String(i + 1).padStart(2, '0')}`, latency: 180 + Math.round(30 * Math.sin(i / 3)) + (i > 9 ? -40 : 0) + (i === 14 ? 60 : 0) }));
67
+ function Chart({ id, title, sub, children, ...props }) {
68
+ return (
69
+ <Card>
70
+ <CardHeader><CardTitle id={id}>{title}</CardTitle><CardDescription>{sub}</CardDescription></CardHeader>
71
+ <CardContent><ChartContainer aria-labelledby={id} aspect={1.9} {...props}>{children}</ChartContainer></CardContent>
72
+ </Card>
73
+ );
74
+ }
75
+ export default function App() {
76
+ const [later, setLater] = React.useState(false);
77
+ const [picked, setPicked] = React.useState(null);
78
+ return (
79
+ <div className="cr" data-tells-ignore="LA4 a gallery of references in each chart, to compare">
80
+ <Chart id="r1" title="Uptime" sub="March, against the SLO" config={{ uptime: { label: 'Uptime', format: pct.format } }}>
81
+ <LineChart data={uptime} x="day">
82
+ <ChartReference y={[0.995, 1]} label="Normal range" />
83
+ <ChartReference y={0.999} label="Target 99.9%" />
84
+ <ChartReference y={0.99} label="Limit 99%" tone="critical" />
85
+ <ChartAnnotation x="2026-03-14" label="DNS outage" />
86
+ </LineChart>
87
+ </Chart>
88
+ <Card>
89
+ <CardHeader><CardTitle id="r2">Cloud spend</CardTitle><CardDescription>Against the daily budget; the median moves with the week</CardDescription></CardHeader>
90
+ <CardContent>
91
+ <ChartContainer aria-labelledby="r2" aspect={2.2} config={{ cost: { label: 'Cost', format: usd.format } }}>
92
+ <BarChart data={later ? weekB : weekA} x="day">
93
+ <ChartReference y={700} label="Budget $700" tone="caution" />
94
+ <ChartReference y="median" label="Median" />
95
+ </BarChart>
96
+ </ChartContainer>
97
+ <div className="row" style={{ marginTop: 12 }}><Button size="sm" onClick={() => setLater((v) => !v)}>{later ? 'Last week' : 'This week'}</Button></div>
98
+ </CardContent>
99
+ </Card>
100
+ <Chart id="r3" title="Accessibility score" sub="By team, against the bar of 75" config={{ score: { label: 'Score' } }}>
101
+ <BarChart data={teams} x="team" layout="horizontal"><ChartReference y={75} label="Bar 75" /></BarChart>
102
+ </Chart>
103
+ <Chart id="r4" title="p95 latency" sub={picked ? `Picked: ${picked}` : 'Events stack into rows where they would overlap'} config={{ latency: { label: 'p95', format: (v) => `${v} ms` } }}>
104
+ <LineChart data={deploys} x="day">
105
+ <ChartReference y="mean" label="Average" />
106
+ <ChartAnnotation x="2026-04-10" label="Cache rollout" onSelect={() => setPicked('Cache rollout')} />
107
+ <ChartAnnotation x="2026-04-11" label="v2 launch" onSelect={() => setPicked('v2 launch')} />
108
+ <ChartAnnotation x="2026-04-15" label="Incident 412" />
109
+ </LineChart>
110
+ </Chart>
111
+ </div>
112
+ );
113
+ }
114
+ ```
115
+ <!-- /example -->
@@ -36,3 +36,66 @@ Turn the readout off with `tooltip={false}` on the chart type or the container.
36
36
  - Do format the heading the way people say the x ("Week of Mon, April 6"), not as stored.
37
37
  - Don't put anything in it that can't be reached another way: it's a convenience over the summary, the keyboard path and the table.
38
38
  - Don't add buttons or links to it; it doesn't take the pointer, and it moves away as soon as the pointer does.
39
+
40
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
41
+ ## Example
42
+
43
+ `ChartTooltip` as first used in its preview:
44
+
45
+ ```tsx
46
+ import { ChartTooltip, ChartTooltipContent } from 'altitude-design';
47
+
48
+ <ChartTooltip content={<ChartTooltipContent indicator="dot" labelFormat={(w) => 'Week of ' + long.format(new Date(w + 'T00:00:00Z'))} />} />
49
+ ```
50
+
51
+ The full preview, as rendered in Storybook and checked in CI (`ct` is the preview page's own layout class, not Altitude's):
52
+
53
+ ```tsx
54
+ import * as React from 'react';
55
+ import { BarChart, Card, CardContent, CardDescription, CardHeader, CardTitle, ChartContainer, ChartTooltip, ChartTooltipContent, LineChart, useChart } from 'altitude-design';
56
+
57
+ // The readout is on for every chart; point at one (or press and hold on touch). Here it's opened with setActive,
58
+ // the same call the keyboard path uses, so the preview shows it without a pointer.
59
+ const weeks = ['2026-03-02', '2026-03-09', '2026-03-16', '2026-03-23', '2026-03-30', '2026-04-06', '2026-04-13'].map((week, i) => ({ week, opened: [31, 38, 35, 44, 41, 47, 52][i], closed: [28, 30, 36, 39, 43, 45, 49][i] }));
60
+ const long = new Intl.DateTimeFormat('en', { weekday: 'short', day: 'numeric', month: 'long', timeZone: 'UTC' });
61
+ function Open({ at }) {
62
+ const { setActive } = useChart();
63
+ React.useEffect(() => { setActive(at); }, []);
64
+ return null;
65
+ }
66
+ export default function App() {
67
+ const [pins, setPins] = React.useState(['2026-03-09', '2026-04-06']);
68
+ return (
69
+ <div className="ct" data-tells-ignore="LA4 a gallery of the readout in each chart, to compare">
70
+ <Card>
71
+ <CardHeader><CardTitle id="t1">Issues opened and closed</CardTitle><CardDescription>The default readout, open on 23 Mar</CardDescription></CardHeader>
72
+ <CardContent>
73
+ <ChartContainer aria-labelledby="t1" aspect={1.8} config={{ opened: { label: 'Opened' }, closed: { label: 'Closed' } }}>
74
+ <LineChart data={weeks} x="week" /><Open at={3} />
75
+ </ChartContainer>
76
+ </CardContent>
77
+ </Card>
78
+ <Card>
79
+ <CardHeader><CardTitle id="t2">Same data, your own content</CardTitle><CardDescription>indicator="dot" and a long date</CardDescription></CardHeader>
80
+ <CardContent>
81
+ <ChartContainer aria-labelledby="t2" aspect={1.8} config={{ opened: { label: 'Opened' }, closed: { label: 'Closed' } }}>
82
+ <BarChart data={weeks} x="week">
83
+ <ChartTooltip content={<ChartTooltipContent indicator="dot" labelFormat={(w) => 'Week of ' + long.format(new Date(w + 'T00:00:00Z'))} />} />
84
+ </BarChart>
85
+ <Open at={5} />
86
+ </ChartContainer>
87
+ </CardContent>
88
+ </Card>
89
+ <Card>
90
+ <CardHeader><CardTitle id="t3">Pinned readings</CardTitle><CardDescription>pinnable: click (or P) sets a reading down; two compare</CardDescription></CardHeader>
91
+ <CardContent>
92
+ <ChartContainer aria-labelledby="t3" aspect={1.8} config={{ opened: { label: 'Opened' } }}>
93
+ <LineChart data={weeks} x="week" pinnable pins={pins} onPinsChange={setPins} />
94
+ </ChartContainer>
95
+ </CardContent>
96
+ </Card>
97
+ </div>
98
+ );
99
+ }
100
+ ```
101
+ <!-- /example -->
@@ -9,3 +9,38 @@ Toggles one independent option. A checkbox is built like the Switch and the Radi
9
9
  **You provide:** `label`, optional `description`, and `checked`/`onCheckedChange` (or `defaultChecked`). It's a real `<input type="checkbox">`, so it works in forms with `name` and `value`.
10
10
 
11
11
  - Use a Switch when the change takes effect immediately; a Checkbox is for choices submitted together.
12
+
13
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
14
+ ## Example
15
+
16
+ `Checkbox` as first used in its preview:
17
+
18
+ ```tsx
19
+ import { Checkbox } from 'altitude-design';
20
+
21
+ <Checkbox label="Select all" checked={a && b} indeterminate={a !== b} onCheckedChange={(c) => { setA(c); setB(c); }} />
22
+ ```
23
+
24
+ The full preview, as rendered in Storybook and checked in CI (`col`, `family`, `cap` are the preview page's own layout classes, not Altitude's):
25
+
26
+ ```tsx
27
+ import * as React from 'react';
28
+ import { Checkbox, Switch } from 'altitude-design';
29
+
30
+ export default function App() {
31
+ const [a, setA] = React.useState(true);
32
+ const [b, setB] = React.useState(false);
33
+ return (
34
+ <div className="col">
35
+ <Checkbox label="Select all" checked={a && b} indeterminate={a !== b} onCheckedChange={(c) => { setA(c); setB(c); }} />
36
+ <div className="col" style={{ paddingLeft: 34 }}>
37
+ <Checkbox label="Weekly digest" description="A summary every Monday morning." checked={a} onCheckedChange={setA} />
38
+ <Checkbox label="Product updates" checked={b} onCheckedChange={setB} />
39
+ </div>
40
+ <Checkbox label="Disabled" disabled defaultChecked />
41
+ <div className="family"><span className="cap">Same build as</span><Switch defaultChecked aria-label="Switch" /><Switch aria-label="Switch off" /></div>
42
+ </div>
43
+ );
44
+ }
45
+ ```
46
+ <!-- /example -->
@@ -69,3 +69,69 @@ The items, applying and persisting each op, and reversing ops for Undo.
69
69
  ## Demo and verification
70
70
 
71
71
  The `Checklist` section of `build/previews.txt` shows nested launch tasks (a fictional service refuses “TBD”) and a flat packing list with `sortDone`. `node checks/checklist.mjs` (from `build/`; `ALT_FIXTURE_ROOT` selects React 19 fixtures) covers Enter, split and merge at the caret, Backspace on an empty line, Tab and Shift+Tab, Escape then Tab, ↑/↓, Alt+↑, Ctrl+Enter, IME Enter, a refused edit with Retry, remove and Ctrl+Z, the add line, task-list paste, `sortDone`, and 320/960px layouts in both schemes under reduced motion, forced colours and `contrast="more"`.
72
+
73
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
74
+ ## Example
75
+
76
+ `Checklist` as first used in its preview:
77
+
78
+ ```tsx
79
+ import { Checklist } from 'altitude-design';
80
+
81
+ <Checklist aria-labelledby="cl-launch" nested {...props} {...launch} />
82
+ ```
83
+
84
+ The full preview, as rendered in Storybook and checked in CI (`cl-demo` is the preview page's own layout class, not Altitude's):
85
+
86
+ ```tsx
87
+ import * as React from 'react';
88
+ import { Checklist } from 'altitude-design';
89
+
90
+ // A tiny store that applies the Checklist's ops to nested items, and reverses them for Undo.
91
+ const clStrip = (list, id) => { let found = null; const out = list.filter(x => { if (x.id === id) { found = x; return false; } return true; }).map(x => { if (found || !x.children) return x; const [kids, hit] = clStrip(x.children, id); if (hit) found = hit; return hit ? { ...x, children: kids } : x; }); return [out, found]; };
92
+ const clInsert = (list, parentId, index, node) => parentId === null ? [...list.slice(0, index), node, ...list.slice(index)]
93
+ : list.map(x => x.id === parentId ? { ...x, children: [...(x.children || []).slice(0, index), node, ...(x.children || []).slice(index)] } : x.children ? { ...x, children: clInsert(x.children, parentId, index, node) } : x);
94
+ const clUpdate = (list, id, patch) => list.map(x => x.id === id ? { ...x, ...patch } : x.children ? { ...x, children: clUpdate(x.children, id, patch) } : x);
95
+ const clApply = (list, op) => {
96
+ if (op.type === 'add') return clInsert(list, op.parentId, op.index, { id: op.id, text: op.text, done: op.done });
97
+ if (op.type === 'edit') return clUpdate(list, op.id, { text: op.text });
98
+ if (op.type === 'toggle') return clUpdate(list, op.id, { done: op.done });
99
+ const [rest, node] = clStrip(list, op.id);
100
+ if (op.type === 'remove') return rest;
101
+ return clInsert(rest, op.parentId, op.index, node);
102
+ };
103
+ function useChecklist(seed, refuse) {
104
+ const [items, setItems] = React.useState(seed);
105
+ const removedNodes = React.useRef(new Map());
106
+ const onChange = op => {
107
+ if (refuse && refuse(op)) return { ok: false, error: refuse(op) };
108
+ if (op.type === 'remove') removedNodes.current.set(op.id, clStrip(items, op.id)[1]);
109
+ setItems(list => clApply(list, op));
110
+ };
111
+ const onUndo = op => setItems(list => {
112
+ if (op.type === 'add') return clStrip(list, op.id)[0];
113
+ if (op.type === 'remove') return clInsert(list, op.parentId, op.index, removedNodes.current.get(op.id));
114
+ const [rest, node] = clStrip(list, op.id); return clInsert(rest, op.from.parentId, op.from.index, node);
115
+ });
116
+ return { items, onChange, onUndo };
117
+ }
118
+ const CL_LAUNCH = [
119
+ { id: 'venue', text: 'Book the venue', done: true },
120
+ { id: 'invites', text: 'Send invitations', done: false, children: [
121
+ { id: 'list', text: 'Confirm the guest list', done: true }, { id: 'design', text: 'Design the card', done: false } ] },
122
+ { id: 'catering', text: 'Choose catering', done: false },
123
+ ];
124
+ const CL_PACK = [{ id: 'p1', text: 'Passport', done: false }, { id: 'p2', text: 'Charger', done: true }, { id: 'p3', text: 'Rain jacket', done: false }];
125
+ export default function App() {
126
+ const launch = useChecklist(CL_LAUNCH, op => op.type === 'edit' && /tbd/i.test(op.text) ? 'Write the task, not “TBD”.' : undefined);
127
+ const pack = useChecklist(CL_PACK);
128
+ const props = { getKey: t => t.id, getText: t => t.text, isDone: t => t.done, getChildren: t => t.children };
129
+ return <div className="cl-demo">
130
+ <section><h3 id="cl-launch">Launch tasks</h3><p>Nested: Tab and Shift+Tab indent and outdent. Backspace on an empty line removes it; Ctrl+Z (⌘Z) brings it back. Writing “TBD” is refused.</p>
131
+ <Checklist aria-labelledby="cl-launch" nested {...props} {...launch} /></section>
132
+ <section><h3 id="cl-pack">Packing</h3><p>Flat, done items last.</p>
133
+ <Checklist aria-labelledby="cl-pack" sortDone {...props} {...pack} placeholder="Add something to pack" /></section>
134
+ </div>;
135
+ }
136
+ ```
137
+ <!-- /example -->
@@ -26,3 +26,47 @@ Code to read and copy: install commands, API examples in several languages, webh
26
26
  - Do use tabs for the same thing in several forms, not for different things.
27
27
  - Don't raise a code block into a card or give it a shadow.
28
28
  - Don't colour code with the chart palette.
29
+
30
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
31
+ ## Example
32
+
33
+ `CodeBlock` as first used in its preview:
34
+
35
+ ```tsx
36
+ import { CodeBlock } from 'altitude-design';
37
+
38
+ <CodeBlock title="Install" tabs={[{ label: 'npm', language: 'sh', code: 'npm install altitude-design' }, { label: 'pnpm', language: 'sh', code: 'pnpm add altitude-design' }, { label: 'yarn', language: 'sh', code: 'yarn add altitude-design' }]} />
39
+ ```
40
+
41
+ The full preview, as rendered in Storybook and checked in CI (`cbk` is the preview page's own layout class, not Altitude's):
42
+
43
+ ```tsx
44
+ import * as React from 'react';
45
+ import { CodeBlock } from 'altitude-design';
46
+
47
+ const payload = `{
48
+ "id": "evt_1Q8xZ2",
49
+ "type": "invoice.paid",
50
+ "created": 1793304000,
51
+ "data": {
52
+ "invoice": "INV-0042",
53
+ "amount": 4900,
54
+ "currency": "eur"
55
+ }
56
+ }`;
57
+ // A stand-in highlighter, to show the syntax prop: comments in ink-muted, keywords in accent-ink.
58
+ const tiny = (code) => code.split(/(\/\/.*$|\b(?:const|await|import|from)\b)/m).map((part, i) => part.startsWith('//') ? <span key={i} className="tok-comment">{part}</span> : /^(const|await|import|from)$/.test(part) ? <span key={i} className="tok-keyword">{part}</span> : part);
59
+ export default function App() {
60
+ const [streamed, setStreamed] = React.useState('');
61
+ const full = "import { Altitude } from 'altitude-design';\n// Verify the signature before trusting the event\nconst event = await verify(request, secret);";
62
+ React.useEffect(() => { let i = 0; const t = setInterval(() => { i = Math.min(full.length, i + 3); setStreamed(full.slice(0, i)); if (i >= full.length) clearInterval(t); }, 60); return () => clearInterval(t); }, []);
63
+ return (
64
+ <div className="cbk">
65
+ <CodeBlock title="Install" tabs={[{ label: 'npm', language: 'sh', code: 'npm install altitude-design' }, { label: 'pnpm', language: 'sh', code: 'pnpm add altitude-design' }, { label: 'yarn', language: 'sh', code: 'yarn add altitude-design' }]} />
66
+ <CodeBlock title="invoice.paid" language="json" code={payload} highlight={[3, 7]} lineNumbers maxHeight={220} />
67
+ <CodeBlock title="webhook.ts" language="ts" code={streamed} syntax={tiny} streaming={streamed.length < full.length} />
68
+ </div>
69
+ );
70
+ }
71
+ ```
72
+ <!-- /example -->
@@ -28,3 +28,42 @@ A brand colour for a white-label portal, a label or tag colour, a calendar colou
28
28
  - Do turn on `checkContrast` wherever the colour will carry text (an accent, a button).
29
29
  - Don't offer a free colour where a palette will do; custom colours make labels hard to tell apart.
30
30
  - Don't use the `data-*` chart palette as label colours; it means series.
31
+
32
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
33
+ ## Example
34
+
35
+ `ColorPicker` as first used in its preview:
36
+
37
+ ```tsx
38
+ import { ColorPicker } from 'altitude-design';
39
+
40
+ <ColorPicker label="Brand colour" value={color} onValueChange={setColor} swatches={brand} allowCustom checkContrast />
41
+ ```
42
+
43
+ The full preview, as rendered in Storybook and checked in CI (`cpk`, `col`, `demo`, `cap` are the preview page's own layout classes, not Altitude's):
44
+
45
+ ```tsx
46
+ import * as React from 'react';
47
+ import { AltitudeProvider, Button, ColorPicker } from 'altitude-design';
48
+
49
+ const brand = [
50
+ { value: '#0f8a6b', name: 'Teal' }, { value: '#2f5fd0', name: 'Blue' }, { value: '#7d3c6f', name: 'Plum' },
51
+ { value: '#c23a6b', name: 'Rose' }, { value: '#c8571f', name: 'Ember' }, { value: '#d9a521', name: 'Ochre' },
52
+ ];
53
+ const labels = [{ value: '#66738a', name: 'Slate' }, { value: '#2f8f4e', name: 'Green' }, { value: '#c9372c', name: 'Red' }, { value: '#16869e', name: 'Cyan' }];
54
+ export default function App() {
55
+ const [color, setColor] = React.useState('#0f8a6b');
56
+ const [tag, setTag] = React.useState('#2f8f4e');
57
+ return (
58
+ <div className="cpk">
59
+ <ColorPicker label="Brand colour" value={color} onValueChange={setColor} swatches={brand} allowCustom checkContrast />
60
+ <div className="col">
61
+ <ColorPicker label="Label colour" value={tag} onValueChange={setTag} swatches={labels} />
62
+ <div className="demo"><span className="cap">Preview:</span><AltitudeProvider accent={color}><Button variant="primary">Upgrade plan</Button></AltitudeProvider></div>
63
+ <p className="cap">The check says, for white text and for dark text, whether it reaches 4.5:1 on the colour. Arrow keys move the mark between swatches; Custom… opens a full picker.</p>
64
+ </div>
65
+ </div>
66
+ );
67
+ }
68
+ ```
69
+ <!-- /example -->
@@ -14,3 +14,38 @@ An editable Select: type to narrow a long list, then choose. What you type is a
14
14
 
15
15
  - Do use it when the list is long enough that scrolling is slower than typing (people, countries, repositories).
16
16
  - Don't use it for free text that may not be in the list; it only accepts listed values.
17
+
18
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
19
+ ## Example
20
+
21
+ `Combobox` as first used in its preview:
22
+
23
+ ```tsx
24
+ import { Combobox } from 'altitude-design';
25
+
26
+ <Combobox items={people} value={who} onValueChange={setWho} placeholder="Search people" />
27
+ ```
28
+
29
+ The full preview, as rendered in Storybook and checked in CI (`col`, `grid` are the preview page's own layout classes, not Altitude's):
30
+
31
+ ```tsx
32
+ import * as React from 'react';
33
+ import { Combobox, Field } from 'altitude-design';
34
+
35
+ const people = ["Ada Obi", "Eli Ward", "Ivy Chen", "Jon Reyes", "Kai Ito", "Lena Park", "Mara Quist", "Noa Levi", "Rui Costa", "Sam Okafor", "Theo Marsh"].map((n) => ({ value: n.toLowerCase().replace(" ", "-"), label: n }));
36
+ const countries = ["Brazil", "Canada", "Germany", "Japan", "Peru", "Portugal", "Singapore", "Spain"].map((c) => ({ value: c.toLowerCase(), label: c }));
37
+ export default function App() {
38
+ const [who, setWho] = React.useState("");
39
+ return <div className="col">
40
+ <div className="grid">
41
+ <Field label="Assignee" hint={who ? "Assigned." : "Start typing a name."}>
42
+ <Combobox items={people} value={who} onValueChange={setWho} placeholder="Search people" />
43
+ </Field>
44
+ <Field label="Country">
45
+ <Combobox items={countries} defaultValue="portugal" defaultOpen={true} />
46
+ </Field>
47
+ </div>
48
+ </div>;
49
+ }
50
+ ```
51
+ <!-- /example -->
@@ -25,3 +25,180 @@ Finds and runs anything in the product from one field: the summit, at level 5, o
25
25
  - Don't render two palettes, and don't open a Sheet from one: one summit, and a level-4 sheet can't sit on it.
26
26
 
27
27
  The palette and Dialog share one summit per document, including across providers. Its shortcut cannot take an occupied summit. A simultaneous controlled request is rejected with a development warning; set it to `false` before trying again. Rejected requests do not queue. A selected command can close the palette and open a Dialog synchronously: callback ordering is unchanged, the exiting palette retires, and scroll/isolation ownership transfers without unlocking the page. Only the active modal has `aria-modal`; composition Enter and Escape remain with the input method.
28
+
29
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
30
+ ## Example
31
+
32
+ `CommandPalette` as first used in its preview:
33
+
34
+ ```tsx
35
+ import { CommandPalette } from 'altitude-design';
36
+
37
+ <CommandPalette
38
+ open={open}
39
+ onOpenChange={onOpenChange}
40
+ query={text}
41
+ onQueryChange={setText}
42
+ items={groups}
43
+ onSelect={(item) => setLast(item.label)}
44
+ />
45
+ ```
46
+
47
+ The full preview, as rendered in Storybook and checked in CI (`app`, `bar`, `find`, `grid`, `cap` are the preview page's own layout classes, not Altitude's):
48
+
49
+ ```tsx
50
+ import * as React from 'react';
51
+ import { Badge, Card, CommandPalette, Input, Kbd } from 'altitude-design';
52
+
53
+ const I = {
54
+ up: <svg
55
+ width="16"
56
+ height="16"
57
+ viewBox="0 0 24 24"
58
+ fill="none"
59
+ stroke="currentColor"
60
+ strokeWidth="1.75"
61
+ strokeLinecap="round"
62
+ strokeLinejoin="round"
63
+ >
64
+ <path d="M7 17L17 7M9 7h8v8" />
65
+ </svg>,
66
+ folder: <svg
67
+ width="16"
68
+ height="16"
69
+ viewBox="0 0 24 24"
70
+ fill="none"
71
+ stroke="currentColor"
72
+ strokeWidth="1.75"
73
+ strokeLinejoin="round"
74
+ >
75
+ <path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
76
+ </svg>,
77
+ plus: <svg
78
+ width="16"
79
+ height="16"
80
+ viewBox="0 0 24 24"
81
+ fill="none"
82
+ stroke="currentColor"
83
+ strokeWidth="1.75"
84
+ strokeLinecap="round"
85
+ >
86
+ <path d="M12 5v14M5 12h14" />
87
+ </svg>,
88
+ user: <svg
89
+ width="16"
90
+ height="16"
91
+ viewBox="0 0 24 24"
92
+ fill="none"
93
+ stroke="currentColor"
94
+ strokeWidth="1.75"
95
+ strokeLinecap="round"
96
+ >
97
+ <circle cx="12" cy="8" r="4" />
98
+ <path d="M4 20c1.5-4 4.5-6 8-6s6.5 2 8 6" />
99
+ </svg>,
100
+ key: <svg
101
+ width="16"
102
+ height="16"
103
+ viewBox="0 0 24 24"
104
+ fill="none"
105
+ stroke="currentColor"
106
+ strokeWidth="1.75"
107
+ strokeLinecap="round"
108
+ strokeLinejoin="round"
109
+ >
110
+ <circle cx="8" cy="15" r="4" />
111
+ <path d="M11 12l9-9M17 6l3 3" />
112
+ </svg>,
113
+ trash: <svg
114
+ width="16"
115
+ height="16"
116
+ viewBox="0 0 24 24"
117
+ fill="none"
118
+ stroke="currentColor"
119
+ strokeWidth="1.75"
120
+ strokeLinecap="round"
121
+ strokeLinejoin="round"
122
+ >
123
+ <path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13" />
124
+ </svg>
125
+ };
126
+ const groups = [
127
+ { label: "Recent", predicted: true, items: [
128
+ { label: "Deploy to production", icon: I.up, shortcut: ["\u2318", "D"], keywords: ["ship", "release"] },
129
+ { label: "Open Northwind", icon: I.folder, description: "Marketing site" }
130
+ ] },
131
+ { label: "Actions", items: [
132
+ { label: "New project", icon: I.plus, shortcut: ["\u2318", "N"] },
133
+ { label: "Invite people", icon: I.user, keywords: ["team", "member"] },
134
+ { label: "Deploy preview", icon: I.up, description: "main@8dc01b2" },
135
+ { label: "Delete project\u2026", icon: I.trash, tone: "critical" }
136
+ ] },
137
+ { label: "Projects", items: [
138
+ { label: "Northwind", icon: I.folder, description: "acme.dev" },
139
+ { label: "Beacon", icon: I.folder, description: "Internal dashboard" },
140
+ { label: "Cinder", icon: I.folder, description: "API gateway" }
141
+ ] },
142
+ { label: "Settings", items: [
143
+ { label: "Environment variables", icon: I.key, keywords: ["env", "secrets", "dependencies"] },
144
+ { label: "Billing", icon: I.key, disabled: true }
145
+ ] }
146
+ ];
147
+ const searchIcon = <svg
148
+ width="16"
149
+ height="16"
150
+ viewBox="0 0 24 24"
151
+ fill="none"
152
+ stroke="currentColor"
153
+ strokeWidth="1.75"
154
+ strokeLinecap="round"
155
+ aria-hidden="true"
156
+ >
157
+ <circle cx="11" cy="11" r="7" />
158
+ <path d="M20 20l-3.5-3.5" />
159
+ </svg>;
160
+ export default function App() {
161
+ const [open, setOpen] = React.useState(true);
162
+ const [text, setText] = React.useState("");
163
+ const [last, setLast] = React.useState(null);
164
+ const onOpenChange = (v) => {
165
+ setOpen(v);
166
+ if (!v) setText("");
167
+ };
168
+ return <div className="app">
169
+ <div className="bar">
170
+ <b>Northwind</b>
171
+ <Badge tone="positive">Operational</Badge>
172
+ <Input
173
+ className="find"
174
+ aria-label="Search or run a command"
175
+ placeholder="Search or run a command"
176
+ value={text}
177
+ leading={searchIcon}
178
+ trailing={<span aria-hidden="true"><Kbd>⌘</Kbd><Kbd>K</Kbd></span>}
179
+ onChange={(e) => {
180
+ setText(e.target.value);
181
+ if (e.target.value) setOpen(true);
182
+ }}
183
+ />
184
+ </div>
185
+ <div className="grid" data-tells-ignore="LA4 placeholder page behind the overlay">
186
+ <Card />
187
+ <Card />
188
+ <Card />
189
+ </div>
190
+ <p className="cap">
191
+ {last ? `Chose \u201C${last}\u201D.` : "Type \u201Cdep\u201D: the best match moves to the top and the rows that stay travel there. Close it and type in the search field: the palette opens with what you typed."}
192
+ </p>
193
+ <CommandPalette
194
+ open={open}
195
+ onOpenChange={onOpenChange}
196
+ query={text}
197
+ onQueryChange={setText}
198
+ items={groups}
199
+ onSelect={(item) => setLast(item.label)}
200
+ />
201
+ </div>;
202
+ }
203
+ ```
204
+ <!-- /example -->
@@ -28,3 +28,54 @@ Comments on a task, a document, a design or a deal: write with @mentions, post,
28
28
  - Do keep a resolved thread in place, stamped.
29
29
  - Don't announce typing.
30
30
  - Don't post on Enter alone; people write several lines.
31
+
32
+ <!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
33
+ ## Example
34
+
35
+ `CommentThread` as first used in its preview:
36
+
37
+ ```tsx
38
+ import { CommentThread } from 'altitude-design';
39
+
40
+ <CommentThread comments={comments} resolved={resolved ? { by: 'You' } : false} onPost={post}
41
+ onResolve={() => setResolved(true)} onReopen={() => setResolved(false)}
42
+ onRetry={(id) => setComments((xs) => xs.map((x) => (x.id === id ? { ...x, status: undefined } : x)))}
43
+ onDelete={(id) => setComments((xs) => xs.map((x) => (x.id === id ? { ...x, deleted: true } : x)))}
44
+ onUndoDelete={(id) => setComments((xs) => xs.map((x) => (x.id === id ? { ...x, deleted: false } : x)))}
45
+ mentionable={(q) => people.filter((p) => p.name.toLowerCase().includes(q.toLowerCase()))} typing={resolved ? [] : ['Ada Obi']} />
46
+ ```
47
+
48
+ The full preview, as rendered in Storybook and checked in CI (`col`, `ct` are the preview page's own layout classes, not Altitude's):
49
+
50
+ ```tsx
51
+ import * as React from 'react';
52
+ import { CommentThread, Switch } from 'altitude-design';
53
+
54
+ const people = [{ id: 'maya', name: 'Maya Chen' }, { id: 'theo', name: 'Theo Marsh' }, { id: 'ada', name: 'Ada Obi' }];
55
+ const ago = (m) => new Date(Date.now() - m * 60000);
56
+ export default function App() {
57
+ const [comments, setComments] = React.useState([
58
+ { id: 'c1', author: { name: 'Theo Marsh' }, body: 'Can we ship this before the release freeze on Friday?', createdAt: ago(90) },
59
+ { id: 'c2', author: { name: 'Maya Chen' }, body: '@Theo Marsh yes, if QA signs off by Thursday.', mentions: ['Theo Marsh'], createdAt: ago(40) },
60
+ ]);
61
+ const [resolved, setResolved] = React.useState(false);
62
+ const [failNext, setFailNext] = React.useState(false);
63
+ const post = (body, mentions) => {
64
+ const id = 'n' + Date.now(), fail = failNext;
65
+ setComments((xs) => [...xs, { id, author: { name: 'You' }, body, mentions: mentions.map((m) => m.name), createdAt: new Date(), status: 'pending', mine: true }]);
66
+ setTimeout(() => setComments((xs) => xs.map((x) => (x.id === id ? { ...x, status: fail ? 'failed' : undefined } : x))), 900);
67
+ };
68
+ return (
69
+ <div className="col ct">
70
+ <CommentThread comments={comments} resolved={resolved ? { by: 'You' } : false} onPost={post}
71
+ onResolve={() => setResolved(true)} onReopen={() => setResolved(false)}
72
+ onRetry={(id) => setComments((xs) => xs.map((x) => (x.id === id ? { ...x, status: undefined } : x)))}
73
+ onDelete={(id) => setComments((xs) => xs.map((x) => (x.id === id ? { ...x, deleted: true } : x)))}
74
+ onUndoDelete={(id) => setComments((xs) => xs.map((x) => (x.id === id ? { ...x, deleted: false } : x)))}
75
+ mentionable={(q) => people.filter((p) => p.name.toLowerCase().includes(q.toLowerCase()))} typing={resolved ? [] : ['Ada Obi']} />
76
+ <Switch label="Make the next post fail" checked={failNext} onCheckedChange={setFailNext} />
77
+ </div>
78
+ );
79
+ }
80
+ ```
81
+ <!-- /example -->