@cueplusplus/ui 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/chrome/_drag.js +59 -0
- package/dist/chrome/_status-bar-item.js +85 -0
- package/dist/chrome/_status-bar.d.ts +15 -0
- package/dist/chrome/_status-bar.js +171 -0
- package/dist/chrome/app-shell.d.ts +69 -1
- package/dist/chrome/app-shell.js +38 -11
- package/dist/chrome/index.d.ts +3 -2
- package/dist/chrome/status-bar.d.ts +87 -4
- package/dist/chrome/status-bar.js +80 -16
- package/dist/chrome/title-bar.d.ts +11 -1
- package/dist/chrome/title-bar.js +1 -3
- package/dist/date/_segments.js +15 -4
- package/dist/forms/_chassis.d.ts +104 -4
- package/dist/forms/_chassis.js +104 -4
- package/dist/forms/input-group.d.ts +8 -3
- package/dist/forms/input-group.js +8 -3
- package/dist/forms/number-field.d.ts +3 -2
- package/dist/forms/number-field.js +3 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -1
- package/dist/instruments/_data-row.js +118 -0
- package/dist/instruments/data-row.d.ts +94 -0
- package/dist/instruments/data-row.js +151 -0
- package/dist/instruments/data-tree.d.ts +126 -0
- package/dist/instruments/data-tree.js +303 -0
- package/dist/instruments/index.d.ts +5 -2
- package/dist/instruments/index.js +4 -1
- package/dist/instruments/ledger.d.ts +105 -0
- package/dist/instruments/ledger.js +114 -0
- package/dist/instruments/table.d.ts +70 -2
- package/dist/instruments/table.js +133 -39
- package/dist/theming/_presets.js +3 -3
- package/manifest/components/app-shell.json +45 -4
- package/manifest/components/autocomplete.json +2 -0
- package/manifest/components/color-field.json +2 -0
- package/manifest/components/color-picker.json +2 -0
- package/manifest/components/combobox.json +2 -0
- package/manifest/components/composer.json +2 -0
- package/manifest/components/data-row.json +203 -0
- package/manifest/components/data-tree.json +151 -0
- package/manifest/components/date-field.json +2 -0
- package/manifest/components/date-picker.json +2 -0
- package/manifest/components/date-range-picker.json +2 -0
- package/manifest/components/env-var-input.json +2 -0
- package/manifest/components/input-group.json +4 -2
- package/manifest/components/input.json +3 -1
- package/manifest/components/ledger.json +187 -0
- package/manifest/components/multi-select.json +2 -0
- package/manifest/components/musical-time-input.json +2 -0
- package/manifest/components/number-field.json +3 -1
- package/manifest/components/otp-field.json +2 -0
- package/manifest/components/password-input.json +2 -0
- package/manifest/components/scrub-input.json +2 -0
- package/manifest/components/search-input.json +2 -0
- package/manifest/components/select.json +1 -0
- package/manifest/components/status-bar.json +146 -12
- package/manifest/components/table-scroll-region.json +6 -0
- package/manifest/components/table.json +88 -4
- package/manifest/components/tags-input.json +2 -0
- package/manifest/components/textarea.json +3 -1
- package/manifest/components/time-field.json +2 -0
- package/manifest/components/title-bar.json +1 -1
- package/manifest/components/toggle-group.json +2 -0
- package/manifest/components/toggle.json +2 -0
- package/manifest/manifest.json +72 -33
- package/manifest/tokens.json +1 -1
- package/package.json +4 -4
|
@@ -5,11 +5,100 @@
|
|
|
5
5
|
"importPath": "@cueplusplus/ui",
|
|
6
6
|
"peerDependencies": [],
|
|
7
7
|
"clientOnly": false,
|
|
8
|
-
"description": "The quiet strip along the bottom of a window.\n\nThe smallest type in the system in the subtlest ink: a status bar is read\nwhen you go looking for it and ignored otherwise, so it must never compete\nwith the work above it. Height comes from `--cue-chrome-statusbar
|
|
8
|
+
"description": "The quiet strip along the bottom of a window.\n\nThe smallest type in the system in the subtlest ink: a status bar is read\nwhen you go looking for it and ignored otherwise, so it must never compete\nwith the work above it. Height comes from `--cue-chrome-statusbar`, and the\nbar clips rather than wrapping — one rung of the frame, always, whatever it\nis asked to hold.\n\nItems come in two kinds and the difference is deliberate. A plain\n`StatusBar.Item` is a **reading**: a `<div>`, inert by construction, with no\nhover, no focus and nothing to click — which is what most of a status bar is.\n`interactive` (or a `render`) makes one a **control**, and only then does it\ngrow the button chassis, the full-height hover fill and the inset focus ring.\n`StatusBar.Group` fuses adjacent items into one segmented control.\n\nNot a live region — a status bar changes constantly (frame counters, DMX\noutput), and announcing every change would make the app unusable with a\nscreen reader. Put anything that genuinely must be announced in a `Toast`.\n\nStatic markup — no `\"use client\"`. A bar of readings renders on the server\nand ships no JavaScript at all; the interactive item is the one part that\ncrosses the boundary, and it crosses it alone (`./_status-bar-item.tsx`).",
|
|
9
9
|
"props": [],
|
|
10
|
-
"typeReferences": [
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
"typeReferences": [
|
|
11
|
+
"React.ComponentPropsWithoutRef<\"div\">"
|
|
12
|
+
],
|
|
13
|
+
"subcomponents": [
|
|
14
|
+
"Item",
|
|
15
|
+
"Group"
|
|
16
|
+
],
|
|
17
|
+
"parts": [
|
|
18
|
+
{
|
|
19
|
+
"name": "Item",
|
|
20
|
+
"usage": "StatusBar.Item",
|
|
21
|
+
"description": "One reading in the bar: a mode, a count, a connection state — or, given\n`interactive`, one control in it.",
|
|
22
|
+
"props": [
|
|
23
|
+
{
|
|
24
|
+
"name": "tone",
|
|
25
|
+
"type": "\"accent\" | \"danger\" | \"busy\" | \"neutral\" | \"ok\" | \"warn\" | \"info\"",
|
|
26
|
+
"required": false,
|
|
27
|
+
"defaultValue": "neutral",
|
|
28
|
+
"description": "Ink for this item. Defaults to the bar's own subtle grey."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "push",
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"required": false,
|
|
34
|
+
"defaultValue": "false",
|
|
35
|
+
"description": "Start the trailing cluster here: pushes this item and everything after it\nto the far edge. Cheaper and more robust than a spacer element, which a\ncaller has to remember to keep in the right place."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "icon",
|
|
39
|
+
"type": "ElementType<any, keyof IntrinsicElements>",
|
|
40
|
+
"required": false,
|
|
41
|
+
"defaultValue": null,
|
|
42
|
+
"description": "Optional leading icon (e.g. a lucide icon), drawn `aria-hidden`."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "interactive",
|
|
46
|
+
"type": "boolean",
|
|
47
|
+
"required": false,
|
|
48
|
+
"defaultValue": "false",
|
|
49
|
+
"description": "Make the item a real control: a `<button type=\"button\">` with hover, press\nand keyboard-focus feedback. Defaults to `false`.\n\nA native button, not a `<div>` with an `onClick` — keyboard order is DOM\norder, `Enter` and `Space` work because the platform makes them work, and\nassistive technology is told what the thing is by the element rather than\nby a role attribute that has to be kept honest by hand.\n\n**An icon-only interactive item needs an `aria-label`.** There is no text\nfor a screen reader to read and the icon is drawn `aria-hidden`, so without\none the control announces as \"button\" and nothing else."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "prominence",
|
|
53
|
+
"type": "\"quiet\" | \"filled\"",
|
|
54
|
+
"required": false,
|
|
55
|
+
"defaultValue": "quiet",
|
|
56
|
+
"description": "How loudly the item states itself. Defaults to `\"quiet\"`.\n\n`quiet` is a reading: tone ink on the bar's own ground. `filled` is a chip\n— a full-height tone wash behind tone ink — for the small set of states\nthat earn a colour of their own: a warning, an error, the remote you are\nattached to. It is loud deliberately, and a bar of five filled items is a\nbar nobody reads."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "render",
|
|
60
|
+
"type": "RenderProp<Record<string, unknown>>",
|
|
61
|
+
"required": false,
|
|
62
|
+
"defaultValue": null,
|
|
63
|
+
"description": "Replace the rendered element (Base UI render prop) — `render={<a href=\"…\" />}`\nfor a status item that navigates.\n\nImplies {@link StatusBarItemProps.interactive}: whatever is rendered gets\nthe control chassis, because an element a reader can click has to look like\none whether it is a button or a link."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "disabled",
|
|
67
|
+
"type": "boolean",
|
|
68
|
+
"required": false,
|
|
69
|
+
"defaultValue": null,
|
|
70
|
+
"description": "Grey the control out, take it out of the pointer's reach and announce it as\ndisabled. Defaults to `false`. Ignored by a static item, which is a reading\nand not a control.\n\nHonoured whatever {@link StatusBarItemProps.render} renders, but by two\nroutes: the `disabled` attribute where it means something — the default\n`<button>`, a form control, a component that takes a `disabled` prop of its\nown — and `aria-disabled` plus `data-disabled` everywhere, which is all an\nanchor gets. There is no such thing as a disabled link; a link that should\nnot be followed should not be a link, and this states the intent rather\nthan pretending to enforce it."
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"typeReferences": [
|
|
74
|
+
"React.ComponentPropsWithoutRef<\"div\">"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "Group",
|
|
79
|
+
"usage": "StatusBar.Group",
|
|
80
|
+
"description": "Items that belong to each other, one pixel apart.\n\nThe bar spaces its readings at `--cue-space-4` because they are unrelated\nfacts. A branch name and its ahead/behind count are not unrelated facts; they\nare one control that happens to have two halves, and the gap between them is\nwhat says so. This is VS Code's `compact` mechanic — the seam collapses, and\nhovering either half grounds both while the half under the cursor goes one\nrung further — expressed as a wrapper rather than as an id-reference between\ntwo items, because a design system's items do not have ids to point at.\n\nA `role=\"group\"` rather than nothing: the members are announced as a set, and\nthe DOM order they are announced in is the order they are read in. Give it an\n`aria-label` when the members only make sense together.",
|
|
81
|
+
"props": [
|
|
82
|
+
{
|
|
83
|
+
"name": "push",
|
|
84
|
+
"type": "boolean",
|
|
85
|
+
"required": false,
|
|
86
|
+
"defaultValue": "false",
|
|
87
|
+
"description": "Start the trailing cluster at this group: pushes it and everything after it\nto the far edge, exactly as `push` does on a single item."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "aria-label",
|
|
91
|
+
"type": "string",
|
|
92
|
+
"required": false,
|
|
93
|
+
"defaultValue": null,
|
|
94
|
+
"description": "What the cluster is called. Optional, and worth giving whenever the members\nonly make sense together — \"Repository\", \"Connection\" — because a `group`\nwith no name is a landmark a screen reader reads straight past."
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"typeReferences": [
|
|
98
|
+
"React.ComponentPropsWithoutRef<\"div\">"
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
],
|
|
13
102
|
"variants": {},
|
|
14
103
|
"defaultVariants": {},
|
|
15
104
|
"tokensUsed": [
|
|
@@ -18,6 +107,7 @@
|
|
|
18
107
|
"--cue-busy",
|
|
19
108
|
"--cue-chrome-statusbar",
|
|
20
109
|
"--cue-danger",
|
|
110
|
+
"--cue-fg",
|
|
21
111
|
"--cue-fg-subtle",
|
|
22
112
|
"--cue-font-mono",
|
|
23
113
|
"--cue-icon-sm",
|
|
@@ -25,23 +115,68 @@
|
|
|
25
115
|
"--cue-ok",
|
|
26
116
|
"--cue-pad-row-x",
|
|
27
117
|
"--cue-space-2",
|
|
118
|
+
"--cue-space-3",
|
|
28
119
|
"--cue-space-4",
|
|
29
120
|
"--cue-surface-1",
|
|
121
|
+
"--cue-surface-2",
|
|
122
|
+
"--cue-surface-3",
|
|
30
123
|
"--cue-text-label",
|
|
31
124
|
"--cue-warn"
|
|
32
125
|
],
|
|
33
|
-
"summary": "The quiet strip along the bottom of a window.",
|
|
126
|
+
"summary": "The quiet strip along the bottom of a window: readings by default, controls where the reading is worth clicking.",
|
|
34
127
|
"examples": [
|
|
35
128
|
{
|
|
36
|
-
"title": "
|
|
129
|
+
"title": "Readings and controls",
|
|
37
130
|
"code": "<StatusBar>\n <StatusBar.Item icon={Plug} tone=\"ok\">Art-Net connected</StatusBar.Item>\n <StatusBar.Item push>512 channels</StatusBar.Item>\n</StatusBar>",
|
|
38
131
|
"language": "tsx"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"title": "Readings and controls",
|
|
135
|
+
"code": "<StatusBar.Item interactive icon={GitBranch} onClick={openBranchPicker}>act-2</StatusBar.Item>\n<StatusBar.Item render={<a href=\"/runs/42\" />}>run 42</StatusBar.Item>",
|
|
136
|
+
"language": "tsx"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"title": "`push`, and the two clusters",
|
|
140
|
+
"code": "<StatusBar>\n <StatusBar.Item>universe 1</StatusBar.Item>\n <StatusBar.Item tone=\"ok\">44 Hz</StatusBar.Item>\n <StatusBar.Item push>10.0.0.42</StatusBar.Item>\n <StatusBar.Item tone=\"info\">art-net</StatusBar.Item>\n</StatusBar>",
|
|
141
|
+
"language": "tsx"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"title": "Groups: items that belong to each other",
|
|
145
|
+
"code": "<StatusBar.Group aria-label=\"Show file\">\n <StatusBar.Item interactive icon={GitBranch}>act-2</StatusBar.Item>\n <StatusBar.Item interactive icon={ArrowUp}>2</StatusBar.Item>\n</StatusBar.Group>",
|
|
146
|
+
"language": "tsx"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"title": "`prominence`, and how rarely to reach for it",
|
|
150
|
+
"code": "<StatusBar.Item prominence=\"filled\" tone=\"danger\" icon={TriangleAlert}>3 faults</StatusBar.Item>",
|
|
151
|
+
"language": "tsx"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"title": "Usage",
|
|
155
|
+
"code": "<StatusBar>\n <StatusBar.Item icon={Plug} tone=\"ok\">Art-Net connected</StatusBar.Item>\n <StatusBar.Item push>512 channels</StatusBar.Item>\n</StatusBar>\n// The VS Code shape: a clickable cluster left, a loud state right.\n<StatusBar>\n <StatusBar.Group aria-label=\"Repository\">\n <StatusBar.Item interactive icon={GitBranch}>main</StatusBar.Item>\n <StatusBar.Item interactive icon={ArrowUp}>2</StatusBar.Item>\n </StatusBar.Group>\n <StatusBar.Item push interactive prominence=\"filled\" tone=\"danger\">3 faults</StatusBar.Item>\n</StatusBar>",
|
|
156
|
+
"language": "tsx"
|
|
39
157
|
}
|
|
40
158
|
],
|
|
41
|
-
"status": "
|
|
159
|
+
"status": "stable",
|
|
42
160
|
"url": "/docs/components/status-bar",
|
|
43
161
|
"mdUrl": "/docs/components/status-bar.md",
|
|
44
162
|
"jsonUrl": "/r/components/status-bar.json",
|
|
163
|
+
"whenToUse": [
|
|
164
|
+
"Ambient state a reader goes looking for rather than is interrupted by: a connection, a frame rate, a cursor position, a branch name.",
|
|
165
|
+
"A state that is also a destination. `interactive` makes an item a real button, `render={<a href=\"…\" />}` makes it a link, and `StatusBar.Group` fuses two of them into one control."
|
|
166
|
+
],
|
|
167
|
+
"whenNotToUse": [
|
|
168
|
+
"Anything that must be noticed. A status bar is deliberately the quietest thing on screen and nothing in it is announced — urgent messages are `Toast`'s job.",
|
|
169
|
+
"A row of actions. That is `Toolbar`, which is sized for controls and has the keyboard model to match.",
|
|
170
|
+
"The end of a document — an address, fine print, a sitemap. That is `Footer`, which scrolls away with the content; the bar is part of the frame and never leaves the screen."
|
|
171
|
+
],
|
|
172
|
+
"commonMistakes": [
|
|
173
|
+
"Making every item interactive. Most of a status bar is a reading, and a bar where everything lights up under the pointer is a bar where nothing means anything.",
|
|
174
|
+
"Filling more than one or two items. `prominence=\"filled\"` is the loudest thing the bar can do; a bar of five filled chips is a bar nobody reads.",
|
|
175
|
+
"An icon-only interactive item with no `aria-label`. The icon is drawn `aria-hidden`, so the control announces as \"button\" and nothing else.",
|
|
176
|
+
"Wrapping related items in a plain `<div>` to close the gap. `StatusBar.Group` closes the seam *and* neutralises the interactive item's negative margin, which a bare div does not.",
|
|
177
|
+
"Reaching for `aria-live` to make a reading announce itself. The bar changes constantly, and a live region here makes the app unusable with a screen reader.",
|
|
178
|
+
"Fixing the height with `h-6` instead of letting `--cue-chrome-statusbar` set it, which stops tracking density."
|
|
179
|
+
],
|
|
45
180
|
"specimens": [
|
|
46
181
|
{
|
|
47
182
|
"title": "StatusBar",
|
|
@@ -49,10 +184,9 @@
|
|
|
49
184
|
"components": [
|
|
50
185
|
"StatusBar"
|
|
51
186
|
],
|
|
52
|
-
"code": "<div className=\"w-full overflow-hidden rounded-(--radius-surface) border border-border\">\n
|
|
53
|
-
"note": "The bottom strip: tone-inked items, and a `push` item that takes the remaining space.",
|
|
54
|
-
"interaction": "
|
|
187
|
+
"code": "<div className=\"flex w-full flex-col gap-(--cue-space-4)\">\n <div className=\"w-full overflow-hidden rounded-(--radius-surface) border border-border\">\n <StatusBar>\n <StatusBar.Item>universe 1</StatusBar.Item>\n <StatusBar.Item tone=\"ok\">44 Hz</StatusBar.Item>\n <StatusBar.Item tone=\"warn\">2 conflicts</StatusBar.Item>\n <StatusBar.Item push>10.0.0.42</StatusBar.Item>\n <StatusBar.Item tone=\"info\">art-net</StatusBar.Item>\n </StatusBar>\n </div>\n\n <div className=\"w-full overflow-hidden rounded-(--radius-surface) border border-border\">\n <StatusBar>\n <StatusBar.Group aria-label=\"Show file\">\n <StatusBar.Item interactive icon={GitBranch}>\n act-2\n </StatusBar.Item>\n <StatusBar.Item interactive icon={ArrowUp}>\n 2\n </StatusBar.Item>\n </StatusBar.Group>\n <StatusBar.Item interactive tone=\"ok\">\n 44 Hz\n </StatusBar.Item>\n <StatusBar.Item interactive icon={Radio} aria-label=\"Art-Net output\" />\n <StatusBar.Item push interactive prominence=\"filled\" tone=\"warn\" icon={TriangleAlert}>\n 2 conflicts\n </StatusBar.Item>\n </StatusBar>\n </div>\n</div>",
|
|
188
|
+
"note": "The bottom strip: tone-inked items, and a `push` item that takes the remaining space. Drawn twice — the readings-only bar it has always been, and the VS Code shape beneath it, where some of the items are controls: a compact Group whose two halves read as one, an icon-only control that carries its name in aria-label, and a filled item for the one state that has earned a colour.",
|
|
189
|
+
"interaction": "the plain items are inert by construction — no hover, no focus, nothing to click. The controls take a full-height fill on hover and a darker one while pressed, Tab reaches them in DOM order and Enter fires them, and the focus ring is drawn inside the item so it never paints across the bar. Hovering either half of the Group grounds both, and the half under the pointer goes one rung further."
|
|
55
190
|
}
|
|
56
|
-
]
|
|
57
|
-
"todo": true
|
|
191
|
+
]
|
|
58
192
|
}
|
|
@@ -24,14 +24,20 @@
|
|
|
24
24
|
"defaultVariants": {},
|
|
25
25
|
"tokensUsed": [
|
|
26
26
|
"--cue-accent",
|
|
27
|
+
"--cue-accent-soft",
|
|
28
|
+
"--cue-bg",
|
|
27
29
|
"--cue-border",
|
|
28
30
|
"--cue-fg",
|
|
29
31
|
"--cue-fg-subtle",
|
|
30
32
|
"--cue-font-mono",
|
|
33
|
+
"--cue-hair-strong",
|
|
31
34
|
"--cue-pad-row-x",
|
|
32
35
|
"--cue-pad-row-y",
|
|
36
|
+
"--cue-row-hover",
|
|
37
|
+
"--cue-space-2",
|
|
33
38
|
"--cue-space-3",
|
|
34
39
|
"--cue-surface-1",
|
|
40
|
+
"--cue-table-group-top",
|
|
35
41
|
"--cue-text-label",
|
|
36
42
|
"--cue-text-ui"
|
|
37
43
|
],
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"importPath": "@cueplusplus/ui",
|
|
6
6
|
"peerDependencies": [],
|
|
7
7
|
"clientOnly": false,
|
|
8
|
-
"description": "The canonical console table: hairlines, mono figures, no zebra striping.\n\nSemantic `<table>` markup throughout — a grid of divs loses row/column\nannouncement, and a console table is exactly the place a screen reader user\nneeds it. Zebra rows are deliberately absent: at console density the stripes\nare thicker than the data.\n\nStatic markup — no `\"use client\"`. Compose it with `Panel` for the rim.\nUse {@link TableScrollRegion} only when the table can exceed its container.",
|
|
8
|
+
"description": "The canonical console table: hairlines, mono figures, no zebra striping.\n\nSemantic `<table>` markup throughout — a grid of divs loses row/column\nannouncement, and a console table is exactly the place a screen reader user\nneeds it. Zebra rows are deliberately absent: at console density the stripes\nare thicker than the data.\n\nStatic markup — no `\"use client\"`. Compose it with `Panel` for the rim.\nUse {@link TableScrollRegion} only when the table can exceed its container.\n\nFor a grouped list, the table stays *one* table: `Table.Head sticky` docks the\ncolumn names, and one `Table.Body` per group opens with a `Table.GroupRow`\ndivider — so the columns are named once and scan straight through every group\nboundary. Rows may hold `DataRow` slots, and `DataRow.Actions` reveals inside\na cell on hover and on selection exactly as it does in a `Ledger` row — on\nfocus too, once the row holds something focusable outside the track, which a\n`<tr>` never is by itself. See {@link TableRowProps.interactive}.",
|
|
9
9
|
"props": [],
|
|
10
10
|
"typeReferences": [],
|
|
11
11
|
"subcomponents": [
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"Header",
|
|
14
14
|
"Body",
|
|
15
15
|
"Row",
|
|
16
|
+
"GroupRow",
|
|
16
17
|
"Head",
|
|
17
18
|
"Cell",
|
|
18
19
|
"Caption"
|
|
@@ -48,16 +49,76 @@
|
|
|
48
49
|
{
|
|
49
50
|
"name": "Row",
|
|
50
51
|
"usage": "Table.Row",
|
|
51
|
-
"description": "One row, separated by a hairline underneath — dropped on the last row so the\ntable never draws a rule against the panel rim it sits in. In a `<thead>` the\n\"last row\" is the header row itself, so the header's own bottom border (on\nthe cells) is the only rule there and the two can never stack into 2px.",
|
|
52
|
-
"props": [
|
|
52
|
+
"description": "One row, separated by a hairline underneath — dropped on the last row so the\ntable never draws a rule against the panel rim it sits in. In a `<thead>` the\n\"last row\" is the header row itself, so the header's own bottom border (on\nthe cells) is the only rule there and the two can never stack into 2px.\n\nEvery row carries the `group/data-row` name unconditionally. It paints\nnothing by itself, and it is what lets a `DataRow.Actions` cluster inside a\n`Table.Cell` reveal on hover, on focus and on selection with no second\nimplementation of that idiom for the table — the whole point of the three\nidioms sharing one row anatomy.\n\n**The focus half of that reveal is the caller's to supply here.** A `<tr>` is\nnot focusable and this component does not make it one — a tab stop on every\nrow of a long table is a tab stop a keyboard user has to walk past hundreds\nof times, which is the reason `Ledger` rows opt in individually. So the\nreveal-on-focus only happens if the row *contains* something focusable\noutside the hidden track: a link on the row's name, a checkbox in the leading\ncell, any real control. Without one, `:focus-within` can never fire — content\ninside a `visibility: hidden` subtree cannot be focused, so the track cannot\nbootstrap its own reveal — and a hidden action cluster is then reachable by\npointer only, which is a WCAG 2.1.1 failure the screen gives no sign of. A\ntable whose only per-row controls live in a hidden track wants either a\nfocusable cell beside it or an always-visible cluster.\n\nThe selected tint is the row's, not the cell's: a `background` on the `<tr>`\nshows through every cell in it, so the fill cannot end up ragged where one\ncell sets a ground of its own.",
|
|
53
|
+
"props": [
|
|
54
|
+
{
|
|
55
|
+
"name": "interactive",
|
|
56
|
+
"type": "boolean",
|
|
57
|
+
"required": false,
|
|
58
|
+
"defaultValue": "false",
|
|
59
|
+
"description": "The row responds to a pointer: the row-hover wash and a pointer cursor\nacross the whole row. Defaults to `false`.\n\nOpt-in rather than automatic, because a table of figures nobody can click\nthat lit up under the pointer would be promising an interaction it does not\nhave. Wiring the row up to actually do something — a click handler, a\nkeyboard path to the same thing — stays the caller's job; this is the paint."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "selected",
|
|
63
|
+
"type": "boolean",
|
|
64
|
+
"required": false,
|
|
65
|
+
"defaultValue": "false",
|
|
66
|
+
"description": "Draw the row as picked out of the table. Defaults to `false`.\n\nSets `data-selected`, which is what the tint and the hover-revealed\n`DataRow.Actions` inside the row both key off — so a hand-written\n`<tr data-selected>` in a data-driven body gets exactly the same treatment."
|
|
67
|
+
}
|
|
68
|
+
],
|
|
53
69
|
"typeReferences": [
|
|
54
70
|
"React.ComponentPropsWithoutRef<\"tr\">"
|
|
55
71
|
]
|
|
56
72
|
},
|
|
73
|
+
{
|
|
74
|
+
"name": "GroupRow",
|
|
75
|
+
"usage": "Table.GroupRow",
|
|
76
|
+
"description": "A group divider inside one continuous table.\n\nThe whole point is that it is *inside*: a grouped list drawn as one table per\ngroup names its columns once per group, so a reader scanning a column\ntop-to-bottom crosses a fresh header band at every boundary and a screen\nreader announces four tables where there is one dataset. So the table stays\none `<table>` with one `<thead>`, and each group is its own `<tbody>` — which\nis valid HTML, keeps the group's rows together, and gives the divider a row\ngroup to be the header of.\n\nThat last part is why the cell is a `<th scope=\"rowgroup\">` rather than a\nstyled `<td>`: `rowgroup` scope is exactly the statement \"this heading names\nthe rows of this `<tbody>`\", and it leaves the column headers' associations\nuntouched. A `<td colSpan>` divider is invisible to assistive technology as a\nheading, and a `<th scope=\"col\">` one would quietly re-scope the columns.\n\nNo `aria-expanded` here, and no toggle: groups in a table divide, they do not\nfold. A hierarchy that folds is `DataTree`, whose treeitem rows own their own\nexpansion — and if a table genuinely needs to fold rows, the pattern is a real\n`<button aria-expanded aria-controls>` inside this cell, never the row itself.",
|
|
77
|
+
"props": [
|
|
78
|
+
{
|
|
79
|
+
"name": "span",
|
|
80
|
+
"type": "number",
|
|
81
|
+
"required": true,
|
|
82
|
+
"defaultValue": null,
|
|
83
|
+
"description": "How many columns the divider spans. Required: it must equal the table's\ncolumn count, or the group's cell stops covering the row and the columns\nbelow it shift by one."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "label",
|
|
87
|
+
"type": "ReactNode",
|
|
88
|
+
"required": true,
|
|
89
|
+
"defaultValue": null,
|
|
90
|
+
"description": "What the group is called."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "count",
|
|
94
|
+
"type": "number",
|
|
95
|
+
"required": false,
|
|
96
|
+
"defaultValue": null,
|
|
97
|
+
"description": "How many rows the group holds, printed beside its name."
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "sticky",
|
|
101
|
+
"type": "boolean",
|
|
102
|
+
"required": false,
|
|
103
|
+
"defaultValue": "false",
|
|
104
|
+
"description": "Dock the divider under the header band while the group's rows scroll past.\nDefaults to `false`.\n\nThe offset is the custom property `--cue-table-group-top` (default `0`):\nhow far down the divider docks is the rendered height of the sticky header\nabove it, which depends on the density and type the caller configured. Set\nit on the table — `className=\"[--cue-table-group-top:2rem]\"` — rather than\nmeasuring at runtime."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"name": "className",
|
|
108
|
+
"type": "string",
|
|
109
|
+
"required": false,
|
|
110
|
+
"defaultValue": null,
|
|
111
|
+
"description": "Classes for the divider's *cell*, not its row.\n\nThe `<tr>` paints nothing — the ground, the type and the sticky offset all\nlive on the `<th>` that spans it, and a background on the row would sit\nbehind the cell's own. Every other prop, and the ref, go to the row."
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"typeReferences": [
|
|
115
|
+
"Omit<React.ComponentPropsWithoutRef<\"tr\">, \"children\">"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
57
118
|
{
|
|
58
119
|
"name": "Head",
|
|
59
120
|
"usage": "Table.Head",
|
|
60
|
-
"description": "A column heading: the same mono uppercase label as `SectionHeader` and\n`Eyebrow`, on the raised surface so the band reads as chrome rather than as\nthe first row of data.",
|
|
121
|
+
"description": "A column heading: the same mono uppercase label as `SectionHeader` and\n`Eyebrow`, on the raised surface so the band reads as chrome rather than as\nthe first row of data.\n\n**Sticky swaps the bottom border for an inset shadow.** `Table.Root` collapses\nits borders, and a collapsed border belongs to the table's shared grid rather\nthan to the cell — so when a cell detaches to stay at the top of the scroll\ncontainer, its rule stays behind with the rows and the stuck band appears to\nlose its edge. An inset `box-shadow` is a paint effect on the cell itself, so\nit travels with it. The alternative — putting the whole table on\n`border-separate` — would change the geometry of every table in the library\nto fix one that scrolls.\n\nThe opaque `bg-surface-1` is not decoration either: a transparent sticky cell\nis a sticky cell you read the rows through. A caller who wants the header on\nthe page's own ground can pass `className=\"bg-bg\"` — what it must not pass is\nnothing.",
|
|
61
122
|
"props": [
|
|
62
123
|
{
|
|
63
124
|
"name": "numeric",
|
|
@@ -65,6 +126,13 @@
|
|
|
65
126
|
"required": false,
|
|
66
127
|
"defaultValue": "false",
|
|
67
128
|
"description": "Right-align this column and set it in tabular figures."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"name": "sticky",
|
|
132
|
+
"type": "boolean",
|
|
133
|
+
"required": false,
|
|
134
|
+
"defaultValue": "false",
|
|
135
|
+
"description": "Dock the header band to the top of the scroll container while the rows\nscroll under it. Defaults to `false`.\n\nSet it on the cells, not on the `<tr>`: sticky on a table row is honoured\ninconsistently across engines, and the cell is the reliable baseline\neverywhere."
|
|
68
136
|
}
|
|
69
137
|
],
|
|
70
138
|
"typeReferences": [
|
|
@@ -102,14 +170,20 @@
|
|
|
102
170
|
"defaultVariants": {},
|
|
103
171
|
"tokensUsed": [
|
|
104
172
|
"--cue-accent",
|
|
173
|
+
"--cue-accent-soft",
|
|
174
|
+
"--cue-bg",
|
|
105
175
|
"--cue-border",
|
|
106
176
|
"--cue-fg",
|
|
107
177
|
"--cue-fg-subtle",
|
|
108
178
|
"--cue-font-mono",
|
|
179
|
+
"--cue-hair-strong",
|
|
109
180
|
"--cue-pad-row-x",
|
|
110
181
|
"--cue-pad-row-y",
|
|
182
|
+
"--cue-row-hover",
|
|
183
|
+
"--cue-space-2",
|
|
111
184
|
"--cue-space-3",
|
|
112
185
|
"--cue-surface-1",
|
|
186
|
+
"--cue-table-group-top",
|
|
113
187
|
"--cue-text-label",
|
|
114
188
|
"--cue-text-ui"
|
|
115
189
|
],
|
|
@@ -150,6 +224,16 @@
|
|
|
150
224
|
"code": "<Panel className=\"w-full\">\n <TableScrollRegion aria-label=\"Cue list table\">\n <Table.Root>\n <Table.Caption>Cue list</Table.Caption>\n <Table.Header>\n <Table.Row>\n <Table.Head numeric>#</Table.Head>\n <Table.Head>Label</Table.Head>\n <Table.Head numeric>Fade</Table.Head>\n <Table.Head>Follow</Table.Head>\n <Table.Head numeric>Level</Table.Head>\n </Table.Row>\n </Table.Header>\n <Table.Body>\n {CUES.map((cue) => (\n <Table.Row key={cue.id}>\n <Table.Cell numeric>{cue.id}</Table.Cell>\n <Table.Cell>{cue.label}</Table.Cell>\n <Table.Cell numeric>{cue.time}</Table.Cell>\n <Table.Cell>{cue.follow}</Table.Cell>\n <Table.Cell numeric>{cue.level}%</Table.Cell>\n </Table.Row>\n ))}\n </Table.Body>\n </Table.Root>\n </TableScrollRegion>\n</Panel>",
|
|
151
225
|
"note": "The canonical console table inside its named horizontal scroll region: hairline rows, mono headers, and `numeric` cells that are right-aligned and tabular.",
|
|
152
226
|
"interaction": "the scroll region is keyboard-focusable. Sorting, filtering and paging come from the DataTable recipe below."
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"title": "Table — grouped",
|
|
230
|
+
"group": "instruments",
|
|
231
|
+
"components": [
|
|
232
|
+
"Table"
|
|
233
|
+
],
|
|
234
|
+
"code": "<Panel className=\"w-full\">\n <div className=\"max-h-[18rem] overflow-y-auto [--cue-table-group-top:1.75rem]\">\n <Table.Root>\n <Table.Caption>Patch, one table, two row groups.</Table.Caption>\n <Table.Header>\n <Table.Row>\n <Table.Head sticky>Device</Table.Head>\n <Table.Head sticky>Detail</Table.Head>\n <Table.Head sticky numeric>\n Latency\n </Table.Head>\n <Table.Head sticky className=\"sr-only\">\n Actions\n </Table.Head>\n </Table.Row>\n </Table.Header>\n {PATCH_GROUPS.map((group) => (\n <Table.Body key={group.id}>\n <Table.GroupRow\n span={4}\n label={group.label}\n count={group.rows.length}\n sticky\n />\n {group.rows.map((device) => (\n <Table.Row key={device.id} interactive>\n {/* The name is a link, and that is load-bearing rather\n than decorative. A `<tr>` is not focusable, so\n without a real control somewhere outside the hidden\n action track nothing in the row can take focus,\n `:focus-within` never fires, and the Copy and\n Actions buttons are reachable by pointer only —\n visible to a mouse user, invisible to Tab. The\n ledger and the tree get this for free because their\n rows are focusable themselves; the table has to be\n given it. */}\n <Table.Cell>\n <Link href={`#patch-${device.id}`}>{device.name}</Link>\n </Table.Cell>\n <Table.Cell className=\"text-fg-muted\">{device.detail}</Table.Cell>\n <Table.Cell numeric>{device.latency}</Table.Cell>\n {/* The action column's width is a sum of the tokens it\n holds — two small controls, the gap between them and\n the cell's own padding on each side — so it stays\n right whatever the density does to any of them. */}\n <Table.Cell className=\"w-[calc(2_*_var(--cue-control-sm)_+_var(--cue-space-1)_+_2_*_var(--cue-pad-row-x))]\">\n <DataRow.Actions>\n <CopyButton\n value={device.id}\n size=\"sm\"\n className=\"w-control-sm px-0\"\n />\n <IconButton\n icon={MoreVertical}\n size=\"sm\"\n aria-label={`Actions for ${device.name}`}\n />\n </DataRow.Actions>\n </Table.Cell>\n </Table.Row>\n ))}\n </Table.Body>\n ))}\n </Table.Root>\n </div>\n</Panel>",
|
|
235
|
+
"note": "The same rows as one continuous table: `Table.Head sticky` docks the column names, and one `Table.Body` per group opens with a `Table.GroupRow` divider — a `th` with `scope='rowgroup'`, so the columns are named once and scan straight through every boundary.",
|
|
236
|
+
"interaction": "rows marked `interactive` take the row-hover wash across the whole row; `selected` tints it and reveals the action track. Tab reaches each row through its name link, which is what lets `:focus-within` reveal the track at all — a `tr` cannot take focus, so a table row whose only controls are in the hidden cluster would be pointer-only."
|
|
153
237
|
}
|
|
154
238
|
]
|
|
155
239
|
}
|
|
@@ -137,6 +137,7 @@
|
|
|
137
137
|
"--cue-accent-soft",
|
|
138
138
|
"--cue-border",
|
|
139
139
|
"--cue-border-overlay",
|
|
140
|
+
"--cue-border-strong",
|
|
140
141
|
"--cue-chip-h",
|
|
141
142
|
"--cue-control-lg",
|
|
142
143
|
"--cue-control-md",
|
|
@@ -145,6 +146,7 @@
|
|
|
145
146
|
"--cue-fg",
|
|
146
147
|
"--cue-fg-muted",
|
|
147
148
|
"--cue-fg-subtle",
|
|
149
|
+
"--cue-focus",
|
|
148
150
|
"--cue-font-mono",
|
|
149
151
|
"--cue-icon-sm",
|
|
150
152
|
"--cue-radius-control",
|
|
@@ -32,12 +32,14 @@
|
|
|
32
32
|
"tokensUsed": [
|
|
33
33
|
"--cue-accent",
|
|
34
34
|
"--cue-border",
|
|
35
|
+
"--cue-border-strong",
|
|
35
36
|
"--cue-control-lg",
|
|
36
37
|
"--cue-control-md",
|
|
37
38
|
"--cue-control-sm",
|
|
38
39
|
"--cue-danger",
|
|
39
40
|
"--cue-fg",
|
|
40
41
|
"--cue-fg-subtle",
|
|
42
|
+
"--cue-focus",
|
|
41
43
|
"--cue-font-mono",
|
|
42
44
|
"--cue-radius-control",
|
|
43
45
|
"--cue-space-3",
|
|
@@ -69,7 +71,7 @@
|
|
|
69
71
|
],
|
|
70
72
|
"code": "<div className=\"grid w-full gap-(--cue-space-5) lg:grid-cols-3\">\n <Textarea aria-label=\"Cue notes\" rows={3} defaultValue={\"House to half.\\nHold for VO.\"} />\n <Textarea aria-label=\"Empty notes\" rows={3} placeholder=\"Notes for the operator\" />\n <Textarea aria-label=\"Disabled notes\" rows={3} defaultValue=\"Read-only\" disabled />\n</div>",
|
|
71
73
|
"note": "The same chassis, grown to a block.",
|
|
72
|
-
"interaction": "same rim and ring as Input."
|
|
74
|
+
"interaction": "same rim and ring as Input: one flush focus-colour ring, no outer halo."
|
|
73
75
|
}
|
|
74
76
|
],
|
|
75
77
|
"todo": true
|
|
@@ -35,12 +35,14 @@
|
|
|
35
35
|
"--cue-accent",
|
|
36
36
|
"--cue-accent-fg",
|
|
37
37
|
"--cue-border",
|
|
38
|
+
"--cue-border-strong",
|
|
38
39
|
"--cue-control-lg",
|
|
39
40
|
"--cue-control-md",
|
|
40
41
|
"--cue-control-sm",
|
|
41
42
|
"--cue-danger",
|
|
42
43
|
"--cue-fg",
|
|
43
44
|
"--cue-fg-subtle",
|
|
45
|
+
"--cue-focus",
|
|
44
46
|
"--cue-font-mono",
|
|
45
47
|
"--cue-radius-control",
|
|
46
48
|
"--cue-space-1",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "boolean",
|
|
13
13
|
"required": false,
|
|
14
14
|
"defaultValue": "false",
|
|
15
|
-
"description": "Make the bar a window drag handle.\n\nEmits `data-tauri-drag-region` (Tauri reads the attribute) *and* the\n`app-region: drag` CSS property (Electron and Chromium read that), because\nthe same component ships into both shells and neither one recognises the\nother's contract. The interactive clusters are carved back out with\n`app-region: no-drag`, or the buttons in the bar would only ever move the\nwindow instead of firing."
|
|
15
|
+
"description": "Make the bar a window drag handle.\n\nEmits `data-tauri-drag-region` (Tauri reads the attribute) *and* the\n`app-region: drag` CSS property (Electron and Chromium read that), because\nthe same component ships into both shells and neither one recognises the\nother's contract. The interactive clusters are carved back out with\n`app-region: no-drag`, or the buttons in the bar would only ever move the\nwindow instead of firing. Both halves of that contract live in\n`./_drag.ts`, which `AppShell.Bar` emits from as well — the two bars are\ndraggable the same way or they are not draggable the same way.\n\nDouble-click-to-maximise is not wired here. Tauri's own injected script\nalready provides it on any drag region, cancel-by-dragging-away on macOS\nincluded; Electron provides nothing, and an Electron app has to listen for\n`dblclick` and toggle the window itself. Neither is a decision a component\nin a design system can make on the window's behalf.\n\nDefaults to `false`."
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"name": "center",
|
|
@@ -60,12 +60,14 @@
|
|
|
60
60
|
"tokensUsed": [
|
|
61
61
|
"--cue-accent",
|
|
62
62
|
"--cue-border",
|
|
63
|
+
"--cue-border-strong",
|
|
63
64
|
"--cue-control-lg",
|
|
64
65
|
"--cue-control-md",
|
|
65
66
|
"--cue-control-sm",
|
|
66
67
|
"--cue-danger",
|
|
67
68
|
"--cue-fg",
|
|
68
69
|
"--cue-fg-subtle",
|
|
70
|
+
"--cue-focus",
|
|
69
71
|
"--cue-font-mono",
|
|
70
72
|
"--cue-radius-control",
|
|
71
73
|
"--cue-space-1",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"--cue-accent",
|
|
61
61
|
"--cue-accent-soft",
|
|
62
62
|
"--cue-border",
|
|
63
|
+
"--cue-border-strong",
|
|
63
64
|
"--cue-control-lg",
|
|
64
65
|
"--cue-control-md",
|
|
65
66
|
"--cue-control-sm",
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
"--cue-fg",
|
|
68
69
|
"--cue-fg-muted",
|
|
69
70
|
"--cue-fg-subtle",
|
|
71
|
+
"--cue-focus",
|
|
70
72
|
"--cue-font-mono",
|
|
71
73
|
"--cue-icon-md",
|
|
72
74
|
"--cue-icon-sm",
|