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
|
@@ -23,3 +23,38 @@ Pass `value` (or `defaultValue`) as `[low, high]` and the Slider has two thumbs,
|
|
|
23
23
|
- **Names.** Give each thumb its own name (`['Minimum price', 'Maximum price']`), or one name for the pair: the thumbs are then "Price, minimum" and "Price, maximum", in a `role="group"` named "Price".
|
|
24
24
|
|
|
25
25
|
- Do show the current value in text near the slider when it matters precisely; the bubble only shows while dragging or focused.
|
|
26
|
+
|
|
27
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
`Slider` as first used in its preview:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { Slider } from 'altitude-design';
|
|
34
|
+
|
|
35
|
+
<Slider value={v} onValueChange={setV} aria-label="Volume" format={(n) => n + '%'} />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `wrap`, `cap`, `gap` are the preview page's own layout classes, not Altitude's):
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import * as React from 'react';
|
|
42
|
+
import { Slider } from 'altitude-design';
|
|
43
|
+
|
|
44
|
+
export default function App() {
|
|
45
|
+
const [v, setV] = React.useState(64);
|
|
46
|
+
const [price, setPrice] = React.useState([20, 80]);
|
|
47
|
+
return (
|
|
48
|
+
<div className="col wrap">
|
|
49
|
+
<Slider value={v} onValueChange={setV} aria-label="Volume" format={(n) => n + '%'} />
|
|
50
|
+
<Slider defaultValue={30} disabled aria-label="Disabled" />
|
|
51
|
+
<p className="cap">Volume {v}% — drag, or focus and use the arrow keys.</p>
|
|
52
|
+
<div className="gap" />
|
|
53
|
+
<Slider value={price} onValueChange={setPrice} min={0} max={200} step={5} minStepsBetween={2}
|
|
54
|
+
aria-label={['Minimum price', 'Maximum price']} format={(n) => '$' + n} />
|
|
55
|
+
<p className="cap">Price ${price[0]}–${price[1]} — drag either thumb, or the fill to move both.</p>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
<!-- /example -->
|
|
@@ -9,3 +9,37 @@ Pressing and dragging an item lifts it to level 3 (floating surface, `elev-3`),
|
|
|
9
9
|
**You provide:** `items`, `getKey`, `onReorder` (receives the new array), `renderItem` (the row content; the row itself and its grip are drawn for you), `getLabel` for announcements, `aria-label`.
|
|
10
10
|
|
|
11
11
|
- Do keep rows short and uniform; the slot is sized to the item being moved.
|
|
12
|
+
|
|
13
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
14
|
+
## Example
|
|
15
|
+
|
|
16
|
+
`SortableList` as first used in its preview:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { Badge, SortableList } from 'altitude-design';
|
|
20
|
+
|
|
21
|
+
<SortableList aria-label="Priorities" items={items} getKey={(i) => i.id} getLabel={(i) => i.t} onReorder={setItems}
|
|
22
|
+
renderItem={(i) => <div className="it">{i.t}<Badge tone={i.p === 'High' ? 'critical' : i.p === 'Med' ? 'caution' : 'neutral'}>{i.p}</Badge></div>} />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `wrap`, `it`, `cap` are the preview page's own layout classes, not Altitude's):
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import * as React from 'react';
|
|
29
|
+
import { Badge, SortableList } from 'altitude-design';
|
|
30
|
+
|
|
31
|
+
export default function App() {
|
|
32
|
+
const [items, setItems] = React.useState([
|
|
33
|
+
{ id: 'a', t: 'Write release notes', p: 'High' }, { id: 'b', t: 'Review pricing copy', p: 'Med' },
|
|
34
|
+
{ id: 'c', t: 'Fix cold-start regression', p: 'High' }, { id: 'd', t: 'Update onboarding emails', p: 'Low' }, { id: 'e', t: 'Plan Q1 roadmap', p: 'Med' },
|
|
35
|
+
]);
|
|
36
|
+
return (
|
|
37
|
+
<div className="col wrap">
|
|
38
|
+
<SortableList aria-label="Priorities" items={items} getKey={(i) => i.id} getLabel={(i) => i.t} onReorder={setItems}
|
|
39
|
+
renderItem={(i) => <div className="it">{i.t}<Badge tone={i.p === 'High' ? 'critical' : i.p === 'Med' ? 'caution' : 'neutral'}>{i.p}</Badge></div>} />
|
|
40
|
+
<p className="cap">Drag, or focus a row and press Space, then the arrow keys.</p>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
<!-- /example -->
|
|
@@ -27,3 +27,61 @@ A trend the size of a word: the last 30 days of a metric in a `DataTable` cell,
|
|
|
27
27
|
- Don't give it axes, a tooltip or a legend; when someone needs to read values, that's a `LineChart`.
|
|
28
28
|
- Don't colour it with a status hue unless the series means good or bad, and say so in words beside it.
|
|
29
29
|
- Don't compare sparklines in different rows as if they shared a scale: each is scaled to its own values.
|
|
30
|
+
|
|
31
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
32
|
+
## Example
|
|
33
|
+
|
|
34
|
+
`Sparkline` as first used in its preview:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { Sparkline } from 'altitude-design';
|
|
38
|
+
|
|
39
|
+
<Sparkline data={signups} x="day" y="signups" tone="data" width={120} aria-label="Signups, last 30 days" />
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The full preview, as rendered in Storybook and checked in CI (`sp`, `row`, `big`, `note` are the preview page's own layout classes, not Altitude's):
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import * as React from 'react';
|
|
46
|
+
import { Badge, DataTable, Sparkline } from 'altitude-design';
|
|
47
|
+
|
|
48
|
+
const spDays = (vals) => vals.map((v, i) => ({ day: `2026-10-${String(i + 1).padStart(2, '0')}`, signups: v }));
|
|
49
|
+
const signups = spDays([1200, 1180, 1230, 1260, 1210, 1290, 1310, 1280, 1300, 1350, 1330, 1290, 1340, 1370, 1360, 1320, 1380, 1400, 1390, 1360, 1410, 1430, 1390, 1420, 1450, 1410, 1380, 1420, 1440, 1400]);
|
|
50
|
+
const teams = [
|
|
51
|
+
{ id: 'core', name: 'Core', builds: [12, 14, 13, 15, 16, 18, 17, 19, 21, 20, 22, 24, 23, 25], errors: [0.4, 0.5, 0.4, 0.6, 0.5, 0.7, 0.9, 1.1, 1.0, 1.3, 1.5, 1.4, 1.8, 2.1] },
|
|
52
|
+
{ id: 'web', name: 'Web', builds: [30, 28, 31, 27, 29, 26, 25, 27, 24, 23, 25, 22, 21, 20], errors: [1.2, 1.1, 1.3, 1.2, 1.1, 1.2, 1.0, 1.1, 1.2, 1.1, 1.0, 1.1, 1.2, 1.1] },
|
|
53
|
+
{ id: 'data', name: 'Data', builds: [8, 9, null, null, 11, 10, 12, 14, 13, 15, 14, 16, 18, 17], errors: [2.0, 1.8, null, null, 1.6, 1.5, 1.6, 1.4, 1.3, 1.2, 1.3, 1.1, 1.0, 0.9] },
|
|
54
|
+
];
|
|
55
|
+
const latency = [182, 176, 190, 240, 178, 184, 171, 169, 175, 260, 181, 177, 174, 172, 179, 185, 176, 173];
|
|
56
|
+
export default function App() {
|
|
57
|
+
const [slow, setSlow] = React.useState(undefined);
|
|
58
|
+
React.useEffect(() => { const t = setTimeout(() => setSlow([4, 6, 5, 8, 7, 9, 12, 11, 13, 12]), 2600); return () => clearTimeout(t); }, []);
|
|
59
|
+
return (
|
|
60
|
+
<div className="sp">
|
|
61
|
+
<section>
|
|
62
|
+
<h3>Beside a number</h3>
|
|
63
|
+
<div className="row"><span className="big">1,400</span><Sparkline data={signups} x="day" y="signups" tone="data" width={120} aria-label="Signups, last 30 days" /></div>
|
|
64
|
+
<p className="note">Signups, last 30 days. Up 17% from 1,200.</p>
|
|
65
|
+
</section>
|
|
66
|
+
<section>
|
|
67
|
+
<h3>In running text</h3>
|
|
68
|
+
<p>p95 latency <Sparkline data={latency} band={[160, 200]} format={(ms) => `${ms} ms`} aria-label="p95 latency, last 18 hours" /> spiked twice outside its normal range.</p>
|
|
69
|
+
<p>Deploys this week <Sparkline data={[3, 5, 2, 6, 4, 0, 1]} kind="bar" aria-label="Deploys per day" /> peaked on Thursday.</p>
|
|
70
|
+
<p className="note">Loading: <Sparkline data={slow} aria-label="Queue depth" /></p>
|
|
71
|
+
</section>
|
|
72
|
+
<section style={{ gridColumn: '1 / -1' }}>
|
|
73
|
+
<h3 id="sp-teams">In a table</h3>
|
|
74
|
+
<DataTable aria-labelledby="sp-teams" getRowId={(r) => r.id} rows={teams} columns={[
|
|
75
|
+
{ key: 'name', header: 'Team' },
|
|
76
|
+
{ key: 'builds', header: 'Builds per day', cell: (r) => <span className="row">{r.builds[r.builds.length - 1]}<Sparkline data={r.builds} decorative /></span> },
|
|
77
|
+
{ key: 'errors', header: 'Error rate, 14 days', cell: (r) => {
|
|
78
|
+
const rising = r.errors[r.errors.length - 1] > r.errors[0] * 1.5;
|
|
79
|
+
return <span className="row">{r.errors[r.errors.length - 1]}%<Sparkline data={r.errors} tone={rising ? 'critical' : 'neutral'} format={(v) => `${v}%`} aria-label={`${r.name} error rate`} />{rising && <Badge tone="critical">Rising</Badge>}</span>;
|
|
80
|
+
} },
|
|
81
|
+
]} />
|
|
82
|
+
</section>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
<!-- /example -->
|
|
@@ -24,3 +24,42 @@ One action with its alternatives: "Save changes ▾" (Save as draft, Save and cl
|
|
|
24
24
|
- Do mark a destructive alternative with `tone: 'critical'`.
|
|
25
25
|
- Don't hide the default inside the menu, or offer only one alternative (use two buttons).
|
|
26
26
|
- Don't nest a SplitButton in a `Toolbar`; its tools are ghosts.
|
|
27
|
+
|
|
28
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
29
|
+
## Example
|
|
30
|
+
|
|
31
|
+
`SplitButton` as first used in its preview:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { SplitButton } from 'altitude-design';
|
|
35
|
+
|
|
36
|
+
<SplitButton label="Save changes" menuLabel="More save options" onClick={() => setSaid('Saved.')}
|
|
37
|
+
items={[{ label: 'Save as draft', onSelect: () => setSaid('Saved as a draft.') }, { label: 'Save and close', onSelect: () => setSaid('Saved and closed.') }]} />
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `row`, `cap` are the preview page's own layout classes, not Altitude's):
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
import * as React from 'react';
|
|
44
|
+
import { SplitButton } from 'altitude-design';
|
|
45
|
+
|
|
46
|
+
export default function App() {
|
|
47
|
+
const [said, setSaid] = React.useState('');
|
|
48
|
+
const [deploying, setDeploying] = React.useState(false);
|
|
49
|
+
const deploy = (where) => { setSaid(`Deploying to ${where}…`); setDeploying(true); setTimeout(() => { setDeploying(false); setSaid(`Deployed to ${where}.`); }, 1600); };
|
|
50
|
+
return (
|
|
51
|
+
<div className="col">
|
|
52
|
+
<div className="row">
|
|
53
|
+
<SplitButton label="Save changes" menuLabel="More save options" onClick={() => setSaid('Saved.')}
|
|
54
|
+
items={[{ label: 'Save as draft', onSelect: () => setSaid('Saved as a draft.') }, { label: 'Save and close', onSelect: () => setSaid('Saved and closed.') }]} />
|
|
55
|
+
<SplitButton variant="secondary" label="Merge" menuLabel="More merge options" onClick={() => setSaid('Merged.')}
|
|
56
|
+
items={[{ label: 'Squash and merge', onSelect: () => setSaid('Squashed and merged.') }, { label: 'Rebase and merge', onSelect: () => setSaid('Rebased and merged.') }]} />
|
|
57
|
+
<SplitButton variant="secondary" label="Deploy" menuLabel="More deploy options" loading={deploying} onClick={() => deploy('staging')}
|
|
58
|
+
items={[{ label: 'Deploy to staging', onSelect: () => deploy('staging') }, { label: 'Deploy to production', onSelect: () => deploy('production'), tone: 'critical' }]} />
|
|
59
|
+
</div>
|
|
60
|
+
<p className="cap" role="status">{said || 'Each face lifts on its own. Alt+Down on an action opens its menu. While Deploy is loading, its chevron still works.'}</p>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
<!-- /example -->
|
|
@@ -29,3 +29,64 @@ The tile at the top of a dashboard: a label, a big number, how it changed and ag
|
|
|
29
29
|
- Do lead a dashboard with stats, then the charts that explain them, then the table that is the record.
|
|
30
30
|
- Don't use `size="hero"` more than once in a view.
|
|
31
31
|
- Don't animate the number counting up, or colour the value itself: colour belongs to the change, and only with `good`.
|
|
32
|
+
|
|
33
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
34
|
+
## Example
|
|
35
|
+
|
|
36
|
+
`Stat` as first used in its preview:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { Stat } from 'altitude-design';
|
|
40
|
+
|
|
41
|
+
<Stat label="Monthly recurring revenue" size="hero" value={mrr} format={usd.format} good="up"
|
|
42
|
+
change={{ value: 0.042, vs: 'September', previous: 176800 }} help="Paid plans only, before refunds." />
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The full preview, as rendered in Storybook and checked in CI (`st`, `st-bar` are the preview page's own layout classes, not Altitude's):
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import * as React from 'react';
|
|
49
|
+
import { Button, Card, CardContent, Sparkline, Stat, StatGroup } from 'altitude-design';
|
|
50
|
+
|
|
51
|
+
const stDays = (vals) => vals.map((v, i) => ({ day: `2026-10-${String(i + 1).padStart(2, '0')}`, v }));
|
|
52
|
+
const active = stDays([11.2, 11.4, 11.3, 11.6, 11.5, 11.8, 11.9, 12.0, 11.9, 12.1, 12.2, 12.4, 12.3, 12.48].map((k) => k * 1000));
|
|
53
|
+
const errors = stDays([0.8, 0.9, 0.8, 1.0, 1.1, 1.0, 1.2, 1.3, 1.2, 1.4, 1.5, 1.4, 1.6, 1.7]);
|
|
54
|
+
const usd = new Intl.NumberFormat('en', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
|
|
55
|
+
export default function App() {
|
|
56
|
+
const [n, setN] = React.useState(0);
|
|
57
|
+
const [mrr, setMrr] = React.useState(undefined);
|
|
58
|
+
React.useEffect(() => { const t = setTimeout(() => setMrr(184200), 2400); return () => clearTimeout(t); }, []);
|
|
59
|
+
const users = 12480 + n * 312;
|
|
60
|
+
return (
|
|
61
|
+
<div className="st">
|
|
62
|
+
<Card>
|
|
63
|
+
<CardContent>
|
|
64
|
+
<Stat label="Monthly recurring revenue" size="hero" value={mrr} format={usd.format} good="up"
|
|
65
|
+
change={{ value: 0.042, vs: 'September', previous: 176800 }} help="Paid plans only, before refunds." />
|
|
66
|
+
</CardContent>
|
|
67
|
+
</Card>
|
|
68
|
+
<div className="st-bar">
|
|
69
|
+
<h3>Last 30 days</h3>
|
|
70
|
+
<Button variant="secondary" size="sm" onClick={() => setN((k) => k + 1)}>Refresh</Button>
|
|
71
|
+
</div>
|
|
72
|
+
<StatGroup aria-label="Last 30 days">
|
|
73
|
+
<Card><CardContent>
|
|
74
|
+
<Stat label="Active users" value={users} live good="up" change={{ value: users / 11556 - 1, vs: 'last month', previous: 11556 }}
|
|
75
|
+
trend={<Sparkline data={active} x="day" y="v" decorative width={72} />} />
|
|
76
|
+
</CardContent></Card>
|
|
77
|
+
<Card><CardContent>
|
|
78
|
+
<Stat label="Error rate" value={1.7 + n * 0.1} format={(v) => `${v.toFixed(1)}%`} good="down" change={{ value: 0.21 + n * 0.07, vs: 'last month' }}
|
|
79
|
+
trend={<Sparkline data={errors} x="day" y="v" tone="critical" decorative width={72} />} />
|
|
80
|
+
</CardContent></Card>
|
|
81
|
+
<Card><CardContent>
|
|
82
|
+
<Stat label="Median response" value={182 - n * 4} format={(v) => `${v} ms`} change={{ value: -12 - n * 4, vs: 'last week', format: (m) => `${m} ms` }} />
|
|
83
|
+
</CardContent></Card>
|
|
84
|
+
<Card><CardContent>
|
|
85
|
+
<Stat label="Refunds" value={null} help="No refunds this period." />
|
|
86
|
+
</CardContent></Card>
|
|
87
|
+
</StatGroup>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
<!-- /example -->
|
|
@@ -27,3 +27,64 @@ Connectors between steps are `accent` behind you and `line-control` ahead.
|
|
|
27
27
|
|
|
28
28
|
- Do keep it to three to six steps; more than that is a list, not a staircase.
|
|
29
29
|
- Don't use it for places people move between freely; that's Tabs, or Drilldown for a hierarchy.
|
|
30
|
+
|
|
31
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
32
|
+
## Example
|
|
33
|
+
|
|
34
|
+
`Stepper` as first used in its preview:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { Stepper } from 'altitude-design';
|
|
38
|
+
|
|
39
|
+
<Stepper
|
|
40
|
+
steps={steps}
|
|
41
|
+
current={at}
|
|
42
|
+
onStepChange={setAt}
|
|
43
|
+
orientation="auto"
|
|
44
|
+
aria-label="Workspace setup"
|
|
45
|
+
/>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The full preview, as rendered in Storybook and checked in CI (`wrap`, `row`, `cap`, `pair` are the preview page's own layout classes, not Altitude's):
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import * as React from 'react';
|
|
52
|
+
import { Button, Stepper } from 'altitude-design';
|
|
53
|
+
|
|
54
|
+
const steps = [{ title: "Account" }, { title: "Workspace" }, { title: "Invite team" }, { title: "Billing" }, { title: "Launch" }];
|
|
55
|
+
const setup = [
|
|
56
|
+
{ title: "Connect repository", description: "github.com/acme/northwind" },
|
|
57
|
+
{ title: "Configure build", description: "Framework, output directory, env" },
|
|
58
|
+
{ title: "Deploy", description: "First production deployment" }
|
|
59
|
+
];
|
|
60
|
+
export default function App() {
|
|
61
|
+
const [at, setAt] = React.useState(2);
|
|
62
|
+
return <div className="wrap">
|
|
63
|
+
<Stepper
|
|
64
|
+
steps={steps}
|
|
65
|
+
current={at}
|
|
66
|
+
onStepChange={setAt}
|
|
67
|
+
orientation="auto"
|
|
68
|
+
aria-label="Workspace setup"
|
|
69
|
+
/>
|
|
70
|
+
<div className="row">
|
|
71
|
+
<Button variant="ghost" disabled={at === 0} onClick={() => setAt(at - 1)}>
|
|
72
|
+
Back
|
|
73
|
+
</Button>
|
|
74
|
+
<Button variant="primary" disabled={at === steps.length} onClick={() => setAt(at + 1)}>
|
|
75
|
+
{at >= steps.length - 1 ? "Finish" : "Continue"}
|
|
76
|
+
</Button>
|
|
77
|
+
<span className="cap">
|
|
78
|
+
Continue presses the current step into the record and raises the next one; click a completed step to go back.
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
<div className="pair">
|
|
82
|
+
<Stepper steps={setup} current={1} orientation="vertical" aria-label="Deployment setup" />
|
|
83
|
+
<p className="cap">
|
|
84
|
+
Vertical: the same plates, stacked. Completed steps are flush and checked, the current one stands on its wall, and steps to come are imprints.
|
|
85
|
+
</p>
|
|
86
|
+
</div>
|
|
87
|
+
</div>;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
<!-- /example -->
|
|
@@ -27,3 +27,61 @@ Prompts to start from or follow up with. An empty conversation offers starting p
|
|
|
27
27
|
- Do keep them short, and few (three is plenty).
|
|
28
28
|
- Do use `fill` when the person will likely want to adjust the prompt.
|
|
29
29
|
- Don't make a suggestion the primary button, or animate follow-ups in with a rise.
|
|
30
|
+
|
|
31
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
32
|
+
## Example
|
|
33
|
+
|
|
34
|
+
`Suggestions` as first used in its preview:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { Suggestion, Suggestions } from 'altitude-design';
|
|
38
|
+
|
|
39
|
+
<Suggestions aria-label="Start with" onChoose={setDraft}>
|
|
40
|
+
<Suggestion>Summarise last week's incidents</Suggestion>
|
|
41
|
+
<Suggestion>Draft a release note</Suggestion>
|
|
42
|
+
<Suggestion value="Which of my open pull requests are blocked, and by whom?">What's blocked?</Suggestion>
|
|
43
|
+
</Suggestions>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The full preview, as rendered in Storybook and checked in CI (`sg`, `col`, `lbl` are the preview page's own layout classes, not Altitude's):
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import * as React from 'react';
|
|
50
|
+
import { EmptyState, Message, MessageContent, PromptInput, PromptInputSubmit, PromptInputTextarea, PromptInputToolbar, Suggestion, Suggestions } from 'altitude-design';
|
|
51
|
+
|
|
52
|
+
export default function App() {
|
|
53
|
+
const [draft, setDraft] = React.useState('');
|
|
54
|
+
const [sent, setSent] = React.useState([]);
|
|
55
|
+
return (
|
|
56
|
+
<div className="sg">
|
|
57
|
+
<div className="col">
|
|
58
|
+
<p className="lbl">Starters in an empty conversation (fill the composer)</p>
|
|
59
|
+
<EmptyState title="Ask about this workspace" description="Pick a starting point, or write your own." size="sm">
|
|
60
|
+
<Suggestions aria-label="Start with" onChoose={setDraft}>
|
|
61
|
+
<Suggestion>Summarise last week's incidents</Suggestion>
|
|
62
|
+
<Suggestion>Draft a release note</Suggestion>
|
|
63
|
+
<Suggestion value="Which of my open pull requests are blocked, and by whom?">What's blocked?</Suggestion>
|
|
64
|
+
</Suggestions>
|
|
65
|
+
</EmptyState>
|
|
66
|
+
<PromptInput onSubmit={({ text }) => { setSent((s) => [...s, text]); setDraft(''); }} value={draft} onValueChange={setDraft}>
|
|
67
|
+
<PromptInputTextarea placeholder="Ask anything" />
|
|
68
|
+
<PromptInputToolbar><PromptInputSubmit /></PromptInputToolbar>
|
|
69
|
+
</PromptInput>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="col">
|
|
72
|
+
<p className="lbl">Follow-ups under an answer (send at once)</p>
|
|
73
|
+
<Message from="assistant">
|
|
74
|
+
<MessageContent>Revenue grew 12% in Q3, led by the annual plan. Churn held at 2.1%.</MessageContent>
|
|
75
|
+
<Suggestions aria-label="Suggested follow-ups" mode="send" onChoose={(t) => setSent((s) => [...s, t])}>
|
|
76
|
+
<Suggestion>Compare with Q2</Suggestion>
|
|
77
|
+
<Suggestion>Make it shorter</Suggestion>
|
|
78
|
+
<Suggestion value="List the three biggest risks, with owners">Biggest risks</Suggestion>
|
|
79
|
+
</Suggestions>
|
|
80
|
+
</Message>
|
|
81
|
+
{sent.length > 0 && <p className="lbl">Sent: {sent[sent.length - 1]}</p>}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
<!-- /example -->
|
|
@@ -18,3 +18,31 @@ The primitive every component is built from: a box placed on one rung of the ele
|
|
|
18
18
|
- Do use Surface for one-off panels, sidebars and app chrome so they share the ladder with the components.
|
|
19
19
|
- Don't set `interactive` on something that doesn't act when clicked: a lift is a promise.
|
|
20
20
|
- Don't stack more than three levels in one region; if you need a fourth, the layout probably wants a dialog or a page.
|
|
21
|
+
|
|
22
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
23
|
+
## Example
|
|
24
|
+
|
|
25
|
+
`Surface` as first used in its preview:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { Surface } from 'altitude-design';
|
|
29
|
+
|
|
30
|
+
<Surface level={-1} className="s"><b>level={'{-1}'}</b><span>A well. Holds things; never lifts.</span></Surface>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The full preview, as rendered in Storybook and checked in CI (`grid`, `s` are the preview page's own layout classes, not Altitude's):
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Surface } from 'altitude-design';
|
|
37
|
+
|
|
38
|
+
export default function App() {
|
|
39
|
+
return (
|
|
40
|
+
<div className="grid" data-tells-ignore="LA4 the three surfaces side by side, to compare">
|
|
41
|
+
<Surface level={-1} className="s"><b>level={'{-1}'}</b><span>A well. Holds things; never lifts.</span></Surface>
|
|
42
|
+
<Surface level={1} className="s"><b>level={'{1}'}</b><span>Static grouping. Doesn't react to the pointer.</span></Surface>
|
|
43
|
+
<Surface level={1} interactive className="s" onClick={() => {}}><b>level={'{1}'} interactive</b><span>Hover: climbs to 2. Press: drops to ground.</span></Surface>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
<!-- /example -->
|
|
@@ -7,3 +7,32 @@ The track is level −2 (`surface-trench`, `trench`). Turning it on pours the `a
|
|
|
7
7
|
**You provide:** `label` (or `aria-label`), and `checked`/`onCheckedChange` or `defaultChecked`. It renders `role="switch"`.
|
|
8
8
|
|
|
9
9
|
- Do use it only for changes that apply instantly. If the change needs a Save, use a Checkbox.
|
|
10
|
+
|
|
11
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
12
|
+
## Example
|
|
13
|
+
|
|
14
|
+
`Switch` as first used in its preview:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Switch } from 'altitude-design';
|
|
18
|
+
|
|
19
|
+
<Switch label="Sync across devices" defaultChecked />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `cap` are the preview page's own layout classes, not Altitude's):
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { Switch } from 'altitude-design';
|
|
26
|
+
|
|
27
|
+
export default function App() {
|
|
28
|
+
return (
|
|
29
|
+
<div className="col">
|
|
30
|
+
<Switch label="Sync across devices" defaultChecked />
|
|
31
|
+
<Switch label="Email me when someone comments" />
|
|
32
|
+
<Switch label="Disabled" disabled />
|
|
33
|
+
<p className="cap">Hold the pointer down: the thumb stretches before it travels.</p>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
<!-- /example -->
|
|
@@ -22,3 +22,57 @@ One honest answer to "is my work saved?", for autosaving editors, optimistic UIs
|
|
|
22
22
|
- Do count what's waiting when offline.
|
|
23
23
|
- Don't put a spinner beside it; the pulsing lamp and the word say it.
|
|
24
24
|
- Don't raise the plate to get attention when a sync fails; the lamp and the words change.
|
|
25
|
+
|
|
26
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
27
|
+
## Example
|
|
28
|
+
|
|
29
|
+
`SyncIndicator` as first used in its preview:
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { Surface, SyncIndicator } from 'altitude-design';
|
|
33
|
+
|
|
34
|
+
<SyncIndicator state={state} pending={waiting.length} lastSyncedAt={at} conflicts={1} onRetry={() => { setState('saving'); setTimeout(() => { setState('saved'); setAt(new Date()); }, 700); }}
|
|
35
|
+
onOpenConflicts={() => setState('saved')}
|
|
36
|
+
queue={waiting.length ? waiting.map((w, i) => <Surface key={i} status="pending" className="change">{w}</Surface>) : undefined} />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The full preview, as rendered in Storybook and checked in CI (`si`, `bar`, `change`, `col`, `cap` are the preview page's own layout classes, not Altitude's):
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import * as React from 'react';
|
|
43
|
+
import { Button, Surface, Switch, SyncIndicator, Textarea } from 'altitude-design';
|
|
44
|
+
|
|
45
|
+
export default function App() {
|
|
46
|
+
const [text, setText] = React.useState('Q4 goals: grow self-serve revenue 20% and halve time to first value.');
|
|
47
|
+
const [state, setState] = React.useState('saved');
|
|
48
|
+
const [at, setAt] = React.useState(() => new Date(Date.now() - 120000));
|
|
49
|
+
const [offline, setOffline] = React.useState(false);
|
|
50
|
+
const [waiting, setWaiting] = React.useState([]);
|
|
51
|
+
const timer = React.useRef();
|
|
52
|
+
const edit = (v) => {
|
|
53
|
+
setText(v);
|
|
54
|
+
if (offline) { setWaiting((w) => [...w.slice(-2), 'Edited the goals paragraph']); setState('offline'); return; }
|
|
55
|
+
setState('saving'); clearTimeout(timer.current);
|
|
56
|
+
timer.current = setTimeout(() => { setState('saved'); setAt(new Date()); }, 700);
|
|
57
|
+
};
|
|
58
|
+
return (
|
|
59
|
+
<div className="si">
|
|
60
|
+
<div>
|
|
61
|
+
<div className="bar"><span>Q4 planning</span>
|
|
62
|
+
<SyncIndicator state={state} pending={waiting.length} lastSyncedAt={at} conflicts={1} onRetry={() => { setState('saving'); setTimeout(() => { setState('saved'); setAt(new Date()); }, 700); }}
|
|
63
|
+
onOpenConflicts={() => setState('saved')}
|
|
64
|
+
queue={waiting.length ? waiting.map((w, i) => <Surface key={i} status="pending" className="change">{w}</Surface>) : undefined} />
|
|
65
|
+
</div>
|
|
66
|
+
<Textarea value={text} onChange={(e) => edit(e.target.value)} aria-label="Q4 goals" />
|
|
67
|
+
</div>
|
|
68
|
+
<div className="col">
|
|
69
|
+
<Switch label="Offline" checked={offline} onCheckedChange={(on) => { setOffline(on); if (!on) { setWaiting([]); setState('saving'); setTimeout(() => { setState('saved'); setAt(new Date()); }, 700); } }} />
|
|
70
|
+
<Button size="sm" onClick={() => setState('failed')}>Make a save fail</Button>
|
|
71
|
+
<Button size="sm" onClick={() => setState('conflict')}>Cause a conflict</Button>
|
|
72
|
+
<p className="cap">Type to save. Go offline and the plate counts what's waiting; press it to see those changes, each hovering until it lands.</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
<!-- /example -->
|
|
@@ -74,3 +74,65 @@ To bulk-act on a filtered set larger than a page ("Archive all 2,341 inactive us
|
|
|
74
74
|
|
|
75
75
|
`selected` and `onSelectedChange` keep working for selections of loaded rows. Report each record's outcome of a long bulk action in place (`RemovalReceipt`'s reading) and in a toast.
|
|
76
76
|
|
|
77
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
78
|
+
## Example
|
|
79
|
+
|
|
80
|
+
The full preview, as rendered in Storybook and checked in CI (`col`, `filter`, `cap` are the preview page's own layout classes, not Altitude's):
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import * as React from 'react';
|
|
84
|
+
import { Badge, Button, DataTable, Input } from 'altitude-design';
|
|
85
|
+
|
|
86
|
+
const data = [
|
|
87
|
+
{ id: "nw", name: "Northwind", env: "Production", owner: "Mara Quist", updated: 2, req: 1240 },
|
|
88
|
+
{ id: "bc", name: "Beacon", env: "Preview", owner: "Jon Reyes", updated: 5, req: 312 },
|
|
89
|
+
{ id: "ci", name: "Cinder", env: "Production", owner: "Lena Park", updated: 26, req: 5906 },
|
|
90
|
+
{ id: "dl", name: "Delta", env: "Staging", owner: "Sam Okafor", updated: 49, req: 88 },
|
|
91
|
+
{ id: "em", name: "Ember", env: "Production", owner: "Ivy Chen", updated: 70, req: 2410 },
|
|
92
|
+
{ id: "fb", name: "Fable", env: "Preview", owner: "Ada Obi", updated: 96, req: 19 },
|
|
93
|
+
{ id: "gn", name: "Garnet", env: "Staging", owner: "Theo Marsh", updated: 120, req: 640 },
|
|
94
|
+
{ id: "hb", name: "Harbor", env: "Production", owner: "Noa Levi", updated: 168, req: 3120 }
|
|
95
|
+
];
|
|
96
|
+
const tone = { Production: "positive", Preview: "accent", Staging: "neutral" };
|
|
97
|
+
export default function App() {
|
|
98
|
+
const [rows, setRows] = React.useState(data);
|
|
99
|
+
const [q, setQ] = React.useState("");
|
|
100
|
+
const [opened, setOpened] = React.useState(null);
|
|
101
|
+
return <div className="col" style={{ gap: 8 }}>
|
|
102
|
+
<DataTable
|
|
103
|
+
aria-label="Projects"
|
|
104
|
+
rows={rows}
|
|
105
|
+
onReorder={setRows}
|
|
106
|
+
getRowId={(r) => r.id}
|
|
107
|
+
getRowLabel={(r) => r.name}
|
|
108
|
+
selectable={true}
|
|
109
|
+
defaultSelected={["bc", "ci"]}
|
|
110
|
+
maxHeight={300}
|
|
111
|
+
onRowClick={(r) => setOpened(r.name)}
|
|
112
|
+
relevance={(r) => q ? r.name.toLowerCase().includes(q.toLowerCase()) || r.owner.toLowerCase().includes(q.toLowerCase()) ? "match" : "miss" : void 0}
|
|
113
|
+
actions={() => <Button size="sm">Archive</Button>}
|
|
114
|
+
columnsMenu={true}
|
|
115
|
+
toolbar={<div className="filter">
|
|
116
|
+
<Input
|
|
117
|
+
size="sm"
|
|
118
|
+
placeholder="Filter in place"
|
|
119
|
+
value={q}
|
|
120
|
+
onChange={(e) => setQ(e.target.value)}
|
|
121
|
+
aria-label="Filter projects"
|
|
122
|
+
/>
|
|
123
|
+
</div>}
|
|
124
|
+
columns={[
|
|
125
|
+
{ key: "name", header: "Project", sortValue: (r) => r.name },
|
|
126
|
+
{ key: "env", header: "Environment", cell: (r) => <Badge tone={tone[r.env]}>{r.env}</Badge>, sortValue: (r) => r.env },
|
|
127
|
+
{ key: "owner", header: "Owner", sortValue: (r) => r.owner },
|
|
128
|
+
{ key: "updated", header: "Updated", align: "end", sortValue: (r) => r.updated, cell: (r) => r.updated < 24 ? r.updated + "h ago" : Math.round(r.updated / 24) + "d ago" },
|
|
129
|
+
{ key: "req", header: "Requests", align: "end", sortValue: (r) => r.req, cell: (r) => r.req.toLocaleString("en-US") }
|
|
130
|
+
]}
|
|
131
|
+
/>
|
|
132
|
+
<p className="cap">
|
|
133
|
+
{opened ? `Opened ${opened}.` : "Sort a column: rows travel to their places. Filter: matches rise, misses sink. Drag a handle, or focus it and press Space, then the arrow keys. Columns shows, hides and reorders columns, and sets the row density."}
|
|
134
|
+
</p>
|
|
135
|
+
</div>;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
<!-- /example -->
|
|
@@ -16,3 +16,58 @@ Arrow keys, Home and End move between tabs in both.
|
|
|
16
16
|
- Don't use tabs for sequential steps; they imply the views are peers.
|
|
17
17
|
|
|
18
18
|
`TabsContent` takes `forceMount` to keep an unchosen panel mounted (inert, hidden from assistive technology, `data-state="inactive"`), for views that hold state or cross-fade, as a chart's Chart | Table views do.
|
|
19
|
+
|
|
20
|
+
<!-- example: generated from build/previews.txt by build/previews.mjs. Edit the preview, not this section. -->
|
|
21
|
+
## Example
|
|
22
|
+
|
|
23
|
+
`Tabs` as first used in its preview:
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from 'altitude-design';
|
|
27
|
+
|
|
28
|
+
<Tabs defaultValue="overview">
|
|
29
|
+
<TabsList aria-label="Project">
|
|
30
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
31
|
+
<TabsTrigger value="activity">Activity</TabsTrigger>
|
|
32
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
33
|
+
<TabsTrigger value="members">Members</TabsTrigger>
|
|
34
|
+
</TabsList>
|
|
35
|
+
<TabsContent value="overview"><p className="body">Three deployments this week. Everything is healthy.</p></TabsContent>
|
|
36
|
+
<TabsContent value="activity"><p className="body">Mara merged “Faster cold starts” 2 hours ago.</p></TabsContent>
|
|
37
|
+
<TabsContent value="settings"><p className="body">Region, domains and environment variables.</p></TabsContent>
|
|
38
|
+
<TabsContent value="members"><p className="body">Six people have access to this project.</p></TabsContent>
|
|
39
|
+
</Tabs>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The full preview, as rendered in Storybook and checked in CI (`stack`, `body` are the preview page's own layout classes, not Altitude's):
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from 'altitude-design';
|
|
46
|
+
|
|
47
|
+
export default function App() {
|
|
48
|
+
return (
|
|
49
|
+
<div className="stack">
|
|
50
|
+
<Tabs defaultValue="overview">
|
|
51
|
+
<TabsList aria-label="Project">
|
|
52
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
53
|
+
<TabsTrigger value="activity">Activity</TabsTrigger>
|
|
54
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
55
|
+
<TabsTrigger value="members">Members</TabsTrigger>
|
|
56
|
+
</TabsList>
|
|
57
|
+
<TabsContent value="overview"><p className="body">Three deployments this week. Everything is healthy.</p></TabsContent>
|
|
58
|
+
<TabsContent value="activity"><p className="body">Mara merged “Faster cold starts” 2 hours ago.</p></TabsContent>
|
|
59
|
+
<TabsContent value="settings"><p className="body">Region, domains and environment variables.</p></TabsContent>
|
|
60
|
+
<TabsContent value="members"><p className="body">Six people have access to this project.</p></TabsContent>
|
|
61
|
+
</Tabs>
|
|
62
|
+
<Tabs defaultValue="week" variant="segmented">
|
|
63
|
+
<TabsList aria-label="Range">
|
|
64
|
+
<TabsTrigger value="day">Day</TabsTrigger>
|
|
65
|
+
<TabsTrigger value="week">Week</TabsTrigger>
|
|
66
|
+
<TabsTrigger value="month">Month</TabsTrigger>
|
|
67
|
+
</TabsList>
|
|
68
|
+
</Tabs>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
<!-- /example -->
|