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.
- package/README.md +1 -0
- package/dist/guidance/components/AILabel/README.md +35 -0
- package/dist/guidance/components/Accordion/README.md +25 -0
- package/dist/guidance/components/AgentFleet/README.md +48 -1
- package/dist/guidance/components/AgentLanes/README.md +32 -0
- package/dist/guidance/components/AgentRequests/README.md +47 -0
- package/dist/guidance/components/Alert/README.md +49 -0
- package/dist/guidance/components/AltitudeProvider/README.md +63 -0
- package/dist/guidance/components/AppShell/README.md +70 -0
- package/dist/guidance/components/AreaChart/README.md +58 -0
- package/dist/guidance/components/Artifact/README.md +69 -0
- package/dist/guidance/components/Avatar/README.md +41 -0
- package/dist/guidance/components/Await/README.md +48 -0
- package/dist/guidance/components/Badge/README.md +44 -0
- package/dist/guidance/components/BarChart/README.md +62 -0
- package/dist/guidance/components/BarList/README.md +58 -0
- package/dist/guidance/components/Board/README.md +67 -0
- package/dist/guidance/components/Breadcrumb/README.md +31 -0
- package/dist/guidance/components/Button/README.md +41 -0
- package/dist/guidance/components/Card/README.md +38 -0
- package/dist/guidance/components/ChartBrush/README.md +57 -0
- package/dist/guidance/components/ChartContainer/README.md +77 -0
- package/dist/guidance/components/ChartLegend/README.md +43 -0
- package/dist/guidance/components/ChartReference/README.md +74 -0
- package/dist/guidance/components/ChartTooltip/README.md +63 -0
- package/dist/guidance/components/Checkbox/README.md +35 -0
- package/dist/guidance/components/Checklist/README.md +66 -0
- package/dist/guidance/components/CodeBlock/README.md +44 -0
- package/dist/guidance/components/ColorPicker/README.md +39 -0
- package/dist/guidance/components/Combobox/README.md +35 -0
- package/dist/guidance/components/CommandPalette/README.md +177 -0
- package/dist/guidance/components/CommentThread/README.md +51 -0
- package/dist/guidance/components/Confirmation/README.md +60 -0
- package/dist/guidance/components/ContextMenu/README.md +51 -0
- package/dist/guidance/components/Conversation/README.md +124 -0
- package/dist/guidance/components/CopyButton/README.md +31 -0
- package/dist/guidance/components/DataGrid/README.md +75 -0
- package/dist/guidance/components/DatePicker/README.md +49 -0
- package/dist/guidance/components/Dialog/README.md +175 -0
- package/dist/guidance/components/DiffView/README.md +77 -0
- package/dist/guidance/components/DotPlot/README.md +44 -0
- package/dist/guidance/components/Drilldown/README.md +28 -0
- package/dist/guidance/components/DropZone/README.md +50 -0
- package/dist/guidance/components/DropdownMenu/README.md +44 -0
- package/dist/guidance/components/EditRegion/README.md +159 -0
- package/dist/guidance/components/EmptyState/README.md +49 -0
- package/dist/guidance/components/FeatureGate/README.md +39 -0
- package/dist/guidance/components/Field/README.md +36 -0
- package/dist/guidance/components/FilterBar/README.md +63 -0
- package/dist/guidance/components/FormattedNumber/README.md +44 -0
- package/dist/guidance/components/Heatmap/README.md +71 -0
- package/dist/guidance/components/HoldButton/README.md +32 -0
- package/dist/guidance/components/HoverCard/README.md +53 -0
- package/dist/guidance/components/InlineCitation/README.md +32 -0
- package/dist/guidance/components/InlineEdit/README.md +247 -1
- package/dist/guidance/components/Input/README.md +36 -0
- package/dist/guidance/components/InputOTP/README.md +46 -0
- package/dist/guidance/components/ItemGroup/README.md +100 -0
- package/dist/guidance/components/Kbd/README.md +27 -0
- package/dist/guidance/components/LineChart/README.md +62 -0
- package/dist/guidance/components/Link/README.md +41 -0
- package/dist/guidance/components/Markdown/README.md +81 -0
- package/dist/guidance/components/Meter/README.md +52 -0
- package/dist/guidance/components/NotificationCenter/README.md +39 -0
- package/dist/guidance/components/NumberInput/README.md +37 -0
- package/dist/guidance/components/PageHeader/README.md +45 -0
- package/dist/guidance/components/Pagination/README.md +33 -0
- package/dist/guidance/components/PasswordInput/README.md +36 -0
- package/dist/guidance/components/PermissionMatrix/README.md +40 -0
- package/dist/guidance/components/Pile/README.md +42 -0
- package/dist/guidance/components/PlanPicker/README.md +37 -0
- package/dist/guidance/components/Popover/README.md +36 -0
- package/dist/guidance/components/Progress/README.md +31 -0
- package/dist/guidance/components/PromptInput/README.md +98 -0
- package/dist/guidance/components/PropertyList/README.md +80 -0
- package/dist/guidance/components/Question/README.md +54 -0
- package/dist/guidance/components/RadioGroup/README.md +37 -0
- package/dist/guidance/components/RelativeTime/README.md +47 -0
- package/dist/guidance/components/RemovalReceipt/README.md +77 -0
- package/dist/guidance/components/Resizable/README.md +29 -0
- package/dist/guidance/components/SaveBar/README.md +38 -0
- package/dist/guidance/components/SavedViews/README.md +58 -0
- package/dist/guidance/components/ScatterChart/README.md +63 -0
- package/dist/guidance/components/Schedule/README.md +66 -0
- package/dist/guidance/components/ScrollArea/README.md +28 -0
- package/dist/guidance/components/SecretField/README.md +46 -0
- package/dist/guidance/components/Select/README.md +70 -0
- package/dist/guidance/components/Separator/README.md +44 -0
- package/dist/guidance/components/Sheet/README.md +94 -0
- package/dist/guidance/components/Skeleton/README.md +36 -0
- package/dist/guidance/components/Slider/README.md +35 -0
- package/dist/guidance/components/SortableList/README.md +34 -0
- package/dist/guidance/components/Sparkline/README.md +58 -0
- package/dist/guidance/components/SplitButton/README.md +39 -0
- package/dist/guidance/components/Stat/README.md +61 -0
- package/dist/guidance/components/Stepper/README.md +61 -0
- package/dist/guidance/components/Suggestions/README.md +58 -0
- package/dist/guidance/components/Surface/README.md +28 -0
- package/dist/guidance/components/Switch/README.md +29 -0
- package/dist/guidance/components/SyncIndicator/README.md +54 -0
- package/dist/guidance/components/Table/README.md +62 -0
- package/dist/guidance/components/Tabs/README.md +55 -0
- package/dist/guidance/components/TagInput/README.md +50 -0
- package/dist/guidance/components/Terminal/README.md +42 -0
- package/dist/guidance/components/ThreadList/README.md +73 -0
- package/dist/guidance/components/TimeInput/README.md +37 -0
- package/dist/guidance/components/Timeline/README.md +55 -0
- package/dist/guidance/components/Toaster/README.md +38 -0
- package/dist/guidance/components/Toggle/README.md +52 -0
- package/dist/guidance/components/Toolbar/README.md +69 -0
- package/dist/guidance/components/Tooltip/README.md +29 -0
- package/dist/guidance/components/Tour/README.md +44 -0
- package/dist/guidance/components/Trace/README.md +92 -0
- package/dist/guidance/components/Tree/README.md +85 -0
- package/dist/guidance/index.json +117 -117
- package/dist/guidance/llms.txt +1 -1
- package/dist/guidance/manifest.json +120 -120
- package/dist/guidance/visual/ActionsInPlace.md +5 -5
- package/dist/guidance/visual/Distinctiveness.md +6 -2
- package/dist/guidance/visual/Research.md +14 -2
- package/dist/guidance/visual/Theming.md +1 -1
- package/dist/types/agent-fleet.d.ts +28 -0
- package/dist/types/ai-label.d.ts +7 -0
- package/dist/types/app-shell.d.ts +23 -0
- package/dist/types/area-chart.d.ts +8 -0
- package/dist/types/artifact.d.ts +14 -0
- package/dist/types/bar-chart.d.ts +6 -0
- package/dist/types/bar-list.d.ts +6 -0
- package/dist/types/breadcrumb.d.ts +6 -0
- package/dist/types/chart-brush.d.ts +6 -0
- package/dist/types/chart.d.ts +29 -1
- package/dist/types/checklist.d.ts +6 -0
- package/dist/types/code-block.d.ts +6 -0
- package/dist/types/collections.d.ts +15 -0
- package/dist/types/color-picker.d.ts +6 -0
- package/dist/types/command.d.ts +13 -0
- package/dist/types/comment-thread.d.ts +11 -0
- package/dist/types/confirmation.d.ts +8 -0
- package/dist/types/context-menu.d.ts +11 -0
- package/dist/types/controls.d.ts +174 -10
- package/dist/types/conversation.d.ts +21 -0
- package/dist/types/copy-button.d.ts +6 -0
- package/dist/types/core.d.ts +31 -0
- package/dist/types/data-grid.d.ts +25 -0
- package/dist/types/date-picker.d.ts +6 -0
- package/dist/types/diff-view.d.ts +8 -0
- package/dist/types/dot-plot.d.ts +6 -0
- package/dist/types/edit-region.d.ts +23 -0
- package/dist/types/empty.d.ts +9 -0
- package/dist/types/feature-gate.d.ts +8 -0
- package/dist/types/filter-bar.d.ts +8 -0
- package/dist/types/formatted-number.d.ts +6 -0
- package/dist/types/heatmap.d.ts +6 -0
- package/dist/types/inline-citation.d.ts +6 -0
- package/dist/types/inline-edit.d.ts +7 -0
- package/dist/types/input-otp.d.ts +11 -0
- package/dist/types/item-group.d.ts +29 -0
- package/dist/types/line-chart.d.ts +6 -0
- package/dist/types/link.d.ts +8 -1
- package/dist/types/markdown.d.ts +6 -0
- package/dist/types/meter.d.ts +6 -0
- package/dist/types/nav.d.ts +6 -0
- package/dist/types/notifications.d.ts +6 -0
- package/dist/types/number-input.d.ts +6 -0
- package/dist/types/overlays.d.ts +61 -0
- package/dist/types/page-header.d.ts +14 -0
- package/dist/types/pagination.d.ts +6 -0
- package/dist/types/password-input.d.ts +6 -0
- package/dist/types/permission-matrix.d.ts +7 -0
- package/dist/types/pile.d.ts +6 -0
- package/dist/types/plan-picker.d.ts +6 -0
- package/dist/types/prompt-input.d.ts +11 -0
- package/dist/types/property-list.d.ts +17 -0
- package/dist/types/question.d.ts +11 -0
- package/dist/types/relative-time.d.ts +8 -1
- package/dist/types/removal-receipt.d.ts +18 -1
- package/dist/types/save-bar.d.ts +6 -0
- package/dist/types/saved-views.d.ts +12 -0
- package/dist/types/scatter-chart.d.ts +8 -0
- package/dist/types/schedule.d.ts +21 -0
- package/dist/types/secret-field.d.ts +6 -0
- package/dist/types/select.d.ts +12 -0
- package/dist/types/separator.d.ts +8 -1
- package/dist/types/sheet.d.ts +19 -0
- package/dist/types/sparkline.d.ts +6 -0
- package/dist/types/split-button.d.ts +7 -0
- package/dist/types/stat.d.ts +7 -0
- package/dist/types/stepper.d.ts +12 -0
- package/dist/types/suggestions.d.ts +10 -0
- package/dist/types/sync-indicator.d.ts +8 -0
- package/dist/types/tag-input.d.ts +8 -0
- package/dist/types/terminal.d.ts +6 -0
- package/dist/types/thread-list.d.ts +16 -0
- package/dist/types/time-input.d.ts +6 -0
- package/dist/types/timeline.d.ts +9 -0
- package/dist/types/toggle.d.ts +6 -0
- package/dist/types/toolbar.d.ts +20 -0
- package/dist/types/tour.d.ts +10 -0
- package/dist/types/trace.d.ts +16 -0
- package/dist/types/tree.d.ts +21 -0
- package/dist/types-cjs/agent-fleet.d.cts +28 -0
- package/dist/types-cjs/ai-label.d.cts +7 -0
- package/dist/types-cjs/app-shell.d.cts +23 -0
- package/dist/types-cjs/area-chart.d.cts +8 -0
- package/dist/types-cjs/artifact.d.cts +14 -0
- package/dist/types-cjs/bar-chart.d.cts +6 -0
- package/dist/types-cjs/bar-list.d.cts +6 -0
- package/dist/types-cjs/breadcrumb.d.cts +6 -0
- package/dist/types-cjs/chart-brush.d.cts +6 -0
- package/dist/types-cjs/chart.d.cts +29 -1
- package/dist/types-cjs/checklist.d.cts +6 -0
- package/dist/types-cjs/code-block.d.cts +6 -0
- package/dist/types-cjs/collections.d.cts +15 -0
- package/dist/types-cjs/color-picker.d.cts +6 -0
- package/dist/types-cjs/command.d.cts +13 -0
- package/dist/types-cjs/comment-thread.d.cts +11 -0
- package/dist/types-cjs/confirmation.d.cts +8 -0
- package/dist/types-cjs/context-menu.d.cts +11 -0
- package/dist/types-cjs/controls.d.cts +174 -10
- package/dist/types-cjs/conversation.d.cts +21 -0
- package/dist/types-cjs/copy-button.d.cts +6 -0
- package/dist/types-cjs/core.d.cts +31 -0
- package/dist/types-cjs/data-grid.d.cts +25 -0
- package/dist/types-cjs/date-picker.d.cts +6 -0
- package/dist/types-cjs/diff-view.d.cts +8 -0
- package/dist/types-cjs/dot-plot.d.cts +6 -0
- package/dist/types-cjs/edit-region.d.cts +23 -0
- package/dist/types-cjs/empty.d.cts +9 -0
- package/dist/types-cjs/feature-gate.d.cts +8 -0
- package/dist/types-cjs/filter-bar.d.cts +8 -0
- package/dist/types-cjs/formatted-number.d.cts +6 -0
- package/dist/types-cjs/heatmap.d.cts +6 -0
- package/dist/types-cjs/inline-citation.d.cts +6 -0
- package/dist/types-cjs/inline-edit.d.cts +7 -0
- package/dist/types-cjs/input-otp.d.cts +11 -0
- package/dist/types-cjs/item-group.d.cts +29 -0
- package/dist/types-cjs/line-chart.d.cts +6 -0
- package/dist/types-cjs/link.d.cts +8 -1
- package/dist/types-cjs/markdown.d.cts +6 -0
- package/dist/types-cjs/meter.d.cts +6 -0
- package/dist/types-cjs/nav.d.cts +6 -0
- package/dist/types-cjs/notifications.d.cts +6 -0
- package/dist/types-cjs/number-input.d.cts +6 -0
- package/dist/types-cjs/overlays.d.cts +61 -0
- package/dist/types-cjs/page-header.d.cts +14 -0
- package/dist/types-cjs/pagination.d.cts +6 -0
- package/dist/types-cjs/password-input.d.cts +6 -0
- package/dist/types-cjs/permission-matrix.d.cts +7 -0
- package/dist/types-cjs/pile.d.cts +6 -0
- package/dist/types-cjs/plan-picker.d.cts +6 -0
- package/dist/types-cjs/prompt-input.d.cts +11 -0
- package/dist/types-cjs/property-list.d.cts +17 -0
- package/dist/types-cjs/question.d.cts +11 -0
- package/dist/types-cjs/relative-time.d.cts +8 -1
- package/dist/types-cjs/removal-receipt.d.cts +18 -1
- package/dist/types-cjs/save-bar.d.cts +6 -0
- package/dist/types-cjs/saved-views.d.cts +12 -0
- package/dist/types-cjs/scatter-chart.d.cts +8 -0
- package/dist/types-cjs/schedule.d.cts +21 -0
- package/dist/types-cjs/secret-field.d.cts +6 -0
- package/dist/types-cjs/select.d.cts +12 -0
- package/dist/types-cjs/separator.d.cts +8 -1
- package/dist/types-cjs/sheet.d.cts +19 -0
- package/dist/types-cjs/sparkline.d.cts +6 -0
- package/dist/types-cjs/split-button.d.cts +7 -0
- package/dist/types-cjs/stat.d.cts +7 -0
- package/dist/types-cjs/stepper.d.cts +12 -0
- package/dist/types-cjs/suggestions.d.cts +10 -0
- package/dist/types-cjs/sync-indicator.d.cts +8 -0
- package/dist/types-cjs/tag-input.d.cts +8 -0
- package/dist/types-cjs/terminal.d.cts +6 -0
- package/dist/types-cjs/thread-list.d.cts +16 -0
- package/dist/types-cjs/time-input.d.cts +6 -0
- package/dist/types-cjs/timeline.d.cts +9 -0
- package/dist/types-cjs/toggle.d.cts +6 -0
- package/dist/types-cjs/toolbar.d.cts +20 -0
- package/dist/types-cjs/tour.d.cts +10 -0
- package/dist/types-cjs/trace.d.cts +16 -0
- package/dist/types-cjs/tree.d.cts +21 -0
- 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 -->
|