@xenosystem/components 0.7.1 → 0.8.1
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/dist/catalog.json +1026 -0
- package/dist/chrome/index.d.ts +1181 -0
- package/dist/chrome/index.js +1923 -0
- package/package.json +5 -1
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
import { ReactNode, HTMLAttributes, ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Frame + strip primitives: `PanelFrame`, `Toolbar`, `ToolbarGroup`, `Divider`, `ScrollArea`.
|
|
5
|
+
*
|
|
6
|
+
* `PanelFrame` implements xeno-workflow's CSS contract verbatim (`xeno-panel` / `-header` /
|
|
7
|
+
* `-title` / `-content`), so a standalone panel gets the canonical floating-panel look with one
|
|
8
|
+
* component. **Inside `<Workbench>` you do NOT need it** — the Dockview group already paints the
|
|
9
|
+
* frame; use `Toolbar` + content directly.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Props for {@link PanelFrame}. */
|
|
15
|
+
interface PanelFrameProps {
|
|
16
|
+
/** Uppercase title shown in the header bar. Omit (with `showHeader` unset) to render no header. */
|
|
17
|
+
title?: ReactNode;
|
|
18
|
+
/** Trailing controls in the header bar. */
|
|
19
|
+
actions?: ReactNode;
|
|
20
|
+
/** Force the header on/off. Defaults to "on when `title` or `actions` is present". */
|
|
21
|
+
showHeader?: boolean;
|
|
22
|
+
/** Panel body. */
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
/** Extra classes on the outer frame. */
|
|
25
|
+
className?: string;
|
|
26
|
+
/** Extra classes on the content well. */
|
|
27
|
+
contentClassName?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The canonical standalone panel frame — header bar + content well, separated by the panel gap.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <PanelFrame title="Layers" actions={<IconButton icon={<PlusIcon />} label="Add layer" />}>
|
|
35
|
+
* <RowList>…</RowList>
|
|
36
|
+
* </PanelFrame>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare function PanelFrame({ title, actions, showHeader, children, className, contentClassName, }: PanelFrameProps): ReactNode;
|
|
40
|
+
/** Props for {@link Toolbar}. */
|
|
41
|
+
interface ToolbarProps {
|
|
42
|
+
/** Leading cluster (filters, title, counts). */
|
|
43
|
+
left?: ReactNode;
|
|
44
|
+
/** Trailing cluster (actions). Right-aligned. */
|
|
45
|
+
right?: ReactNode;
|
|
46
|
+
/** Free-form children, rendered between the clusters. */
|
|
47
|
+
children?: ReactNode;
|
|
48
|
+
/** Render a hairline under the strip. Default `true`. */
|
|
49
|
+
divided?: boolean;
|
|
50
|
+
/** Extra classes. */
|
|
51
|
+
className?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A dense horizontal control strip. Height matches the panel header so a toolbar stacked under a
|
|
55
|
+
* frame header reads as one continuous chrome band.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```tsx
|
|
59
|
+
* <Toolbar left={<SearchField value={q} onChange={setQ} />} right={<IconButton … />} />
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
declare function Toolbar({ left, right, children, divided, className, }: ToolbarProps): ReactNode;
|
|
63
|
+
/** Props for {@link ToolbarGroup}. */
|
|
64
|
+
interface ToolbarGroupProps {
|
|
65
|
+
/** Cluster contents. */
|
|
66
|
+
children?: ReactNode;
|
|
67
|
+
/** Push the cluster to the right edge. */
|
|
68
|
+
end?: boolean;
|
|
69
|
+
/** Extra classes. */
|
|
70
|
+
className?: string;
|
|
71
|
+
}
|
|
72
|
+
/** A cluster of controls inside a {@link Toolbar}. */
|
|
73
|
+
declare function ToolbarGroup({ children, end, className }: ToolbarGroupProps): ReactNode;
|
|
74
|
+
/** Props for {@link Divider}. */
|
|
75
|
+
interface DividerProps {
|
|
76
|
+
/** Render a short vertical rule (for inline use inside a toolbar). */
|
|
77
|
+
vertical?: boolean;
|
|
78
|
+
/** Extra classes. */
|
|
79
|
+
className?: string;
|
|
80
|
+
}
|
|
81
|
+
/** A hairline rule. Horizontal by default; `vertical` for inline separation inside a strip. */
|
|
82
|
+
declare function Divider({ vertical, className }: DividerProps): ReactNode;
|
|
83
|
+
/** Props for {@link ScrollArea}. */
|
|
84
|
+
interface ScrollAreaProps {
|
|
85
|
+
/** Scrollable content. */
|
|
86
|
+
children?: ReactNode;
|
|
87
|
+
/** Extra classes. */
|
|
88
|
+
className?: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A flex-filling scroll container with the canonical thin rectangular thumb and an unbroken
|
|
92
|
+
* height chain (`flex: 1 1 0; min-height: 0`) — the shape DESIGN_SYSTEM §3.1 requires so panels
|
|
93
|
+
* never reach for `max-height: calc(100vh - N)`.
|
|
94
|
+
*/
|
|
95
|
+
declare function ScrollArea({ children, className }: ScrollAreaProps): ReactNode;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* `Section` — the collapsible titled group.
|
|
99
|
+
*
|
|
100
|
+
* Lifted from `xeno-post`'s `Section` (title + action slot) and re-cut to the density and feature
|
|
101
|
+
* set of the only real in-panel implementation in the catalog, the Inspector's: 20px header, a
|
|
102
|
+
* disclosure twisty, a count badge, action buttons, and an ENABLE toggle that dims the body without
|
|
103
|
+
* hiding it (the "effect is off but still configured" state every property panel needs).
|
|
104
|
+
*
|
|
105
|
+
* ## The header is not a button
|
|
106
|
+
*
|
|
107
|
+
* A collapsible header wants to be a `<button>` for keyboard/AT, but actions and a toggle cannot be
|
|
108
|
+
* nested inside one (interactive content inside a button is invalid and breaks AT). So the header is
|
|
109
|
+
* a plain row and only the chevron+title span is the `<button>` — actions are its siblings. This is
|
|
110
|
+
* the standard resolution and it is why `Section` renders three elements rather than one.
|
|
111
|
+
*
|
|
112
|
+
* @module
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
/** Props for {@link Section}. */
|
|
116
|
+
interface SectionProps {
|
|
117
|
+
/** Uppercase section label. */
|
|
118
|
+
title: ReactNode;
|
|
119
|
+
/** Body. */
|
|
120
|
+
children?: ReactNode;
|
|
121
|
+
/** Small chip after the title (a count, a unit, a state word). */
|
|
122
|
+
badge?: ReactNode;
|
|
123
|
+
/** Trailing controls (icon buttons, a select). Rendered outside the collapse trigger. */
|
|
124
|
+
actions?: ReactNode;
|
|
125
|
+
/** Whether the section can collapse. Default `true`. */
|
|
126
|
+
collapsible?: boolean;
|
|
127
|
+
/** Controlled collapsed state. Omit for uncontrolled. */
|
|
128
|
+
collapsed?: boolean;
|
|
129
|
+
/** Initial collapsed state when uncontrolled. Default `false`. */
|
|
130
|
+
defaultCollapsed?: boolean;
|
|
131
|
+
/** Called with the next collapsed state. Required for the controlled form to do anything. */
|
|
132
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
133
|
+
/**
|
|
134
|
+
* Presence of this prop renders the enable toggle. `false` dims the body and blocks pointer
|
|
135
|
+
* events — the section stays visible and readable, it just stops applying.
|
|
136
|
+
*/
|
|
137
|
+
enabled?: boolean;
|
|
138
|
+
/** Called with the next enabled state. */
|
|
139
|
+
onEnabledChange?: (enabled: boolean) => void;
|
|
140
|
+
/** Render the body with no vertical padding (for a flush `RowList`). */
|
|
141
|
+
flush?: boolean;
|
|
142
|
+
/** Extra classes on the wrapper. */
|
|
143
|
+
className?: string;
|
|
144
|
+
/** Extra classes on the body. */
|
|
145
|
+
bodyClassName?: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A collapsible titled group.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```tsx
|
|
152
|
+
* <Section
|
|
153
|
+
* title="Transform"
|
|
154
|
+
* badge={<Badge>3</Badge>}
|
|
155
|
+
* enabled={fx.enabled}
|
|
156
|
+
* onEnabledChange={(on) => host.emit('intent', { kind: 'toggle', on })}
|
|
157
|
+
* actions={<IconButton icon={<ResetGlyph />} label="Reset transform" onClick={reset} />}
|
|
158
|
+
* >
|
|
159
|
+
* <FieldGroup>…</FieldGroup>
|
|
160
|
+
* </Section>
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
declare function Section({ title, children, badge, actions, collapsible, collapsed, defaultCollapsed, onCollapsedChange, enabled, onEnabledChange, flush, className, bodyClassName, }: SectionProps): ReactNode;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The canonical panel-chrome CLASS VOCABULARY — framework-free.
|
|
167
|
+
*
|
|
168
|
+
* These are the class names the primitives' stylesheet defines. They are exported as constants (and
|
|
169
|
+
* as small state→className helpers) so a panel written in ANY framework — or in plain DOM — gets the
|
|
170
|
+
* canonical chrome without importing React.
|
|
171
|
+
*
|
|
172
|
+
* Two naming families live here, deliberately:
|
|
173
|
+
*
|
|
174
|
+
* 1. **`xeno-panel*` — the FRAME contract, lifted verbatim from `xeno-workflow`'s `index.css`**
|
|
175
|
+
* (`xeno-panel` / `xeno-panel-header` / `xeno-panel-title` / `xeno-panel-content` /
|
|
176
|
+
* `xeno-panel-content--no-header`, driven by `--xeno-panel-gap` / `--xeno-panel-radius`).
|
|
177
|
+
* The names are byte-identical to workflow's so that repo can delete its local copy and import
|
|
178
|
+
* ours with zero markup churn. Note the frame is for panels rendered OUTSIDE a workbench — when
|
|
179
|
+
* a panel is mounted in `<Workbench>`, the Dockview GROUP is already the frame (see the theme).
|
|
180
|
+
*
|
|
181
|
+
* 2. **`xeno-<primitive>` — the KIT vocabulary, lifted from `xeno-post`'s `ui/kit.tsx`**
|
|
182
|
+
* (Section, RowList/Row, Toolbar, StatTile, ProportionBar, EmptyState, LoadingState, ErrorState,
|
|
183
|
+
* StatusBadge, IconFrame …), re-cut for dense in-panel density rather than dashboard-page density.
|
|
184
|
+
*
|
|
185
|
+
* Modifiers use BEM's double dash (`xeno-row--selected`), matching workflow's existing
|
|
186
|
+
* `xeno-panel-content--no-header`.
|
|
187
|
+
*
|
|
188
|
+
* @module
|
|
189
|
+
*/
|
|
190
|
+
/** Outer frame of a standalone panel. Layout-only; the `::before` paints the page backdrop. */
|
|
191
|
+
declare const CLS_PANEL = "xeno-panel";
|
|
192
|
+
/** The frame's compact header bar. */
|
|
193
|
+
declare const CLS_PANEL_HEADER = "xeno-panel-header";
|
|
194
|
+
/** Uppercase title text inside the frame header. */
|
|
195
|
+
declare const CLS_PANEL_TITLE = "xeno-panel-title";
|
|
196
|
+
/** The frame's content well. */
|
|
197
|
+
declare const CLS_PANEL_CONTENT = "xeno-panel-content";
|
|
198
|
+
/** Applied to the content well when the frame renders no header. */
|
|
199
|
+
declare const CLS_PANEL_CONTENT_NO_HEADER = "xeno-panel-content--no-header";
|
|
200
|
+
/** Horizontal control strip (`left` / `right` clusters). */
|
|
201
|
+
declare const CLS_TOOLBAR = "xeno-toolbar";
|
|
202
|
+
/** A cluster of controls inside a toolbar. */
|
|
203
|
+
declare const CLS_TOOLBAR_GROUP = "xeno-toolbar-group";
|
|
204
|
+
/** Footer strip: counts on the left, state on the right. */
|
|
205
|
+
declare const CLS_STATUSBAR = "xeno-statusbar";
|
|
206
|
+
/** 1px hairline; `--vertical` for an inline separator inside a toolbar. */
|
|
207
|
+
declare const CLS_DIVIDER = "xeno-divider";
|
|
208
|
+
/** Collapsible titled group. */
|
|
209
|
+
declare const CLS_SECTION = "xeno-section";
|
|
210
|
+
/** The section's header row. */
|
|
211
|
+
declare const CLS_SECTION_HEADER = "xeno-section-header";
|
|
212
|
+
/** The `<button>` inside the header that toggles collapse (siblings stay outside it — see `Section`). */
|
|
213
|
+
declare const CLS_SECTION_TRIGGER = "xeno-section-trigger";
|
|
214
|
+
/** Disclosure chevron wrapper (rotates via the `--collapsed` modifier). */
|
|
215
|
+
declare const CLS_SECTION_CHEVRON = "xeno-section-chevron";
|
|
216
|
+
/** Uppercase section label. */
|
|
217
|
+
declare const CLS_SECTION_TITLE = "xeno-section-title";
|
|
218
|
+
/** Trailing cluster in the section header (badge + actions + toggle). */
|
|
219
|
+
declare const CLS_SECTION_ACTIONS = "xeno-section-actions";
|
|
220
|
+
/** The section's body. */
|
|
221
|
+
declare const CLS_SECTION_BODY = "xeno-section-body";
|
|
222
|
+
/** Hairline-divided vertical list container. */
|
|
223
|
+
declare const CLS_ROWLIST = "xeno-rowlist";
|
|
224
|
+
/** A single dense row. */
|
|
225
|
+
declare const CLS_ROW = "xeno-row";
|
|
226
|
+
/** Fixed-width leading icon slot (keeps labels in one column — DESIGN_SYSTEM §5). */
|
|
227
|
+
declare const CLS_ROW_ICON = "xeno-row-icon";
|
|
228
|
+
/** The row's flexible label column. */
|
|
229
|
+
declare const CLS_ROW_LABEL = "xeno-row-label";
|
|
230
|
+
/** Secondary text after the label. */
|
|
231
|
+
declare const CLS_ROW_META = "xeno-row-meta";
|
|
232
|
+
/** Right-aligned trailing slot. */
|
|
233
|
+
declare const CLS_ROW_TRAILING = "xeno-row-trailing";
|
|
234
|
+
/** Square icon-only button. */
|
|
235
|
+
declare const CLS_ICON_BUTTON = "xeno-iconbtn";
|
|
236
|
+
/** Borderless inline text action (`+ Add`, `Clear`, `Unbind`). */
|
|
237
|
+
declare const CLS_TEXT_BUTTON = "xeno-textbtn";
|
|
238
|
+
/** Search / filter input wrapper. */
|
|
239
|
+
declare const CLS_SEARCH = "xeno-search";
|
|
240
|
+
/** The `<input>` inside a search field. */
|
|
241
|
+
declare const CLS_SEARCH_INPUT = "xeno-search-input";
|
|
242
|
+
/** Segmented (radio-group) control. */
|
|
243
|
+
declare const CLS_SEGMENTED = "xeno-segmented";
|
|
244
|
+
/** One segment button. */
|
|
245
|
+
declare const CLS_SEGMENT = "xeno-segment";
|
|
246
|
+
/** The signature XENO toggle (rectangular track, square knob). */
|
|
247
|
+
declare const CLS_TOGGLE = "xeno-toggle";
|
|
248
|
+
/** The toggle's knob. */
|
|
249
|
+
declare const CLS_TOGGLE_KNOB = "xeno-toggle-knob";
|
|
250
|
+
/** Label-column field group (all labels share one width). */
|
|
251
|
+
declare const CLS_FIELD_GROUP = "xeno-fieldgroup";
|
|
252
|
+
/** One label + control row. */
|
|
253
|
+
declare const CLS_FIELD = "xeno-field";
|
|
254
|
+
/** The field's label cell. */
|
|
255
|
+
declare const CLS_FIELD_LABEL = "xeno-field-label";
|
|
256
|
+
/** The field's control cell. */
|
|
257
|
+
declare const CLS_FIELD_CONTROL = "xeno-field-control";
|
|
258
|
+
/** Muted hint under a field. */
|
|
259
|
+
declare const CLS_FIELD_HINT = "xeno-field-hint";
|
|
260
|
+
/** Centered zero-content state. */
|
|
261
|
+
declare const CLS_EMPTY = "xeno-empty";
|
|
262
|
+
/** Centered busy state. */
|
|
263
|
+
declare const CLS_LOADING = "xeno-loading";
|
|
264
|
+
/** Centered failure state. */
|
|
265
|
+
declare const CLS_ERROR = "xeno-error";
|
|
266
|
+
/** Shared container for the three states above. */
|
|
267
|
+
declare const CLS_STATE = "xeno-state";
|
|
268
|
+
/** The state's icon chip. */
|
|
269
|
+
declare const CLS_STATE_ICON = "xeno-state-icon";
|
|
270
|
+
/** The state's headline. */
|
|
271
|
+
declare const CLS_STATE_TITLE = "xeno-state-title";
|
|
272
|
+
/** The state's supporting line — the ACTIONABLE hint. */
|
|
273
|
+
declare const CLS_STATE_HINT = "xeno-state-hint";
|
|
274
|
+
/** Indeterminate progress bar used by the loading state (no spinner — no circles). */
|
|
275
|
+
declare const CLS_STATE_PROGRESS = "xeno-state-progress";
|
|
276
|
+
/** Small rectangular status/metadata chip. */
|
|
277
|
+
declare const CLS_BADGE = "xeno-badge";
|
|
278
|
+
/** 2px-radius square status dot (never a circle). */
|
|
279
|
+
declare const CLS_DOT = "xeno-dot";
|
|
280
|
+
/** Flat outlined icon container. */
|
|
281
|
+
declare const CLS_ICON_FRAME = "xeno-iconframe";
|
|
282
|
+
/** Label + big value tile. */
|
|
283
|
+
declare const CLS_STAT = "xeno-stat";
|
|
284
|
+
/** The stat's uppercase label row. */
|
|
285
|
+
declare const CLS_STAT_LABEL = "xeno-stat-label";
|
|
286
|
+
/** The stat's value. */
|
|
287
|
+
declare const CLS_STAT_VALUE = "xeno-stat-value";
|
|
288
|
+
/** The stat's sub-line. */
|
|
289
|
+
declare const CLS_STAT_SUB = "xeno-stat-sub";
|
|
290
|
+
/** Horizontal stacked proportion bar (replaces a pie/donut — no circles). */
|
|
291
|
+
declare const CLS_BAR = "xeno-bar";
|
|
292
|
+
/** One segment of a proportion bar. */
|
|
293
|
+
declare const CLS_BAR_SEGMENT = "xeno-bar-segment";
|
|
294
|
+
/** Scroll container with the canonical thin rectangular thumb. */
|
|
295
|
+
declare const CLS_SCROLL = "xeno-scroll";
|
|
296
|
+
/**
|
|
297
|
+
* Semantic tone for badges, dots and states. `neutral` is the default and the only tone that is
|
|
298
|
+
* purely monochromatic; the other four are the ONLY places DESIGN_SYSTEM permits chromatic color
|
|
299
|
+
* (§2 "Color is reserved strictly for semantic/status contexts").
|
|
300
|
+
*/
|
|
301
|
+
type PanelTone = 'neutral' | 'info' | 'success' | 'warning' | 'error';
|
|
302
|
+
/** All tones, in severity order. Useful for exhaustive switches and demos. */
|
|
303
|
+
declare const PANEL_TONES: readonly PanelTone[];
|
|
304
|
+
/** Join truthy class fragments. Internal, but exported because vanilla callers want it too. */
|
|
305
|
+
declare function cx(...parts: Array<string | false | null | undefined>): string;
|
|
306
|
+
/** Interaction state shared by row-like elements. */
|
|
307
|
+
interface RowState {
|
|
308
|
+
/** Row is the current selection. */
|
|
309
|
+
selected?: boolean;
|
|
310
|
+
/** Row is non-interactive (dimmed, `aria-disabled`). */
|
|
311
|
+
disabled?: boolean;
|
|
312
|
+
/** Row is active/being dragged or edited. */
|
|
313
|
+
active?: boolean;
|
|
314
|
+
/** Row participates in click/keyboard interaction. */
|
|
315
|
+
interactive?: boolean;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Build the class list for a row.
|
|
319
|
+
*
|
|
320
|
+
* @param state - Interaction state.
|
|
321
|
+
* @param extra - Caller classes appended last.
|
|
322
|
+
* @returns The space-joined class list.
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* el.className = rowClass({ selected: id === selectedId, interactive: true })
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
declare function rowClass(state?: RowState, extra?: string): string;
|
|
330
|
+
/**
|
|
331
|
+
* Build the class list for a badge.
|
|
332
|
+
*
|
|
333
|
+
* @param tone - Semantic tone (default `neutral`).
|
|
334
|
+
* @param extra - Caller classes appended last.
|
|
335
|
+
* @returns The space-joined class list.
|
|
336
|
+
*/
|
|
337
|
+
declare function badgeClass(tone?: PanelTone, extra?: string): string;
|
|
338
|
+
/**
|
|
339
|
+
* Build the class list for a status dot.
|
|
340
|
+
*
|
|
341
|
+
* @param tone - Semantic tone (default `neutral`).
|
|
342
|
+
* @param extra - Caller classes appended last.
|
|
343
|
+
* @returns The space-joined class list.
|
|
344
|
+
*/
|
|
345
|
+
declare function dotClass(tone?: PanelTone, extra?: string): string;
|
|
346
|
+
/**
|
|
347
|
+
* Build the class list for a section wrapper.
|
|
348
|
+
*
|
|
349
|
+
* @param collapsed - Whether the body is hidden.
|
|
350
|
+
* @param extra - Caller classes appended last.
|
|
351
|
+
* @returns The space-joined class list.
|
|
352
|
+
*/
|
|
353
|
+
declare function sectionClass(collapsed?: boolean, extra?: string): string;
|
|
354
|
+
/**
|
|
355
|
+
* Build the class list for an icon button.
|
|
356
|
+
*
|
|
357
|
+
* @param state - `active` renders the pressed treatment; `disabled` dims and blocks pointers.
|
|
358
|
+
* @param extra - Caller classes appended last.
|
|
359
|
+
* @returns The space-joined class list.
|
|
360
|
+
*/
|
|
361
|
+
declare function iconButtonClass(state?: {
|
|
362
|
+
active?: boolean;
|
|
363
|
+
disabled?: boolean;
|
|
364
|
+
}, extra?: string): string;
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* List primitives: `RowList` + `Row`.
|
|
368
|
+
*
|
|
369
|
+
* This is the single most duplicated shape in the ecosystem — every shipped panel hand-rolls a
|
|
370
|
+
* 22px hover/selected row with a fixed icon gutter. The contract here is deliberately narrow:
|
|
371
|
+
* a leading icon slot of FIXED width (so labels align in one column across every row and every
|
|
372
|
+
* panel — DESIGN_SYSTEM §5), a flexible label, optional meta text, and a right-aligned trailing
|
|
373
|
+
* slot. Indentation for trees is expressed as `depth`, not as caller padding, so a tree row and a
|
|
374
|
+
* flat row remain the same height and the icon column stays honest.
|
|
375
|
+
*
|
|
376
|
+
* @module
|
|
377
|
+
*/
|
|
378
|
+
|
|
379
|
+
interface RowListProps extends HTMLAttributes<HTMLDivElement> {
|
|
380
|
+
/** Rows. */
|
|
381
|
+
children?: ReactNode;
|
|
382
|
+
/** Draw hairlines between rows. Default `false` (dense lists read better undivided). */
|
|
383
|
+
divided?: boolean;
|
|
384
|
+
/** ARIA role for the container. Default `list`; use `listbox` / `tree` for selectable sets. */
|
|
385
|
+
role?: string;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* A vertical list container for {@link Row}s.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```tsx
|
|
392
|
+
* <RowList role="listbox" aria-label="Layers">
|
|
393
|
+
* {items.map((it) => <Row key={it.id} …/>)}
|
|
394
|
+
* </RowList>
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
declare function RowList({ children, divided, role, className, ...rest }: RowListProps): ReactNode;
|
|
398
|
+
/** Props for {@link Row}. */
|
|
399
|
+
interface RowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onSelect'> {
|
|
400
|
+
/** Leading icon/graphic. Rendered in a fixed-width slot even when `undefined`, to hold the column. */
|
|
401
|
+
icon?: ReactNode;
|
|
402
|
+
/** Primary text. Truncates with an ellipsis. */
|
|
403
|
+
label?: ReactNode;
|
|
404
|
+
/** Secondary text after the label (counts, types, paths). */
|
|
405
|
+
meta?: ReactNode;
|
|
406
|
+
/** Right-aligned controls (toggles, badges, icon buttons). */
|
|
407
|
+
trailing?: ReactNode;
|
|
408
|
+
/** Free-form content, rendered INSTEAD of the label/meta pair when provided. */
|
|
409
|
+
children?: ReactNode;
|
|
410
|
+
/** Nesting depth for trees; each level indents by `PANEL_METRICS.rowIndent`. */
|
|
411
|
+
depth?: number;
|
|
412
|
+
/** Row is the current selection (`aria-selected`). */
|
|
413
|
+
selected?: boolean;
|
|
414
|
+
/** Row is active — being dragged, renamed, or otherwise operated on. */
|
|
415
|
+
active?: boolean;
|
|
416
|
+
/** Row is non-interactive: dimmed, `aria-disabled`, click/keyboard suppressed. */
|
|
417
|
+
disabled?: boolean;
|
|
418
|
+
/** Hide the leading icon slot entirely (for lists with no icons at all). */
|
|
419
|
+
noIcon?: boolean;
|
|
420
|
+
/** ARIA role for the row. Defaults to `listitem`, or `option` when `selected` is supplied. */
|
|
421
|
+
role?: string;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* One dense list row.
|
|
425
|
+
*
|
|
426
|
+
* Interaction contract: when `onClick` is supplied the row becomes keyboard-operable
|
|
427
|
+
* (`tabIndex=0`, Enter/Space activate). When `disabled`, pointer and keyboard activation are both
|
|
428
|
+
* suppressed — the handler is not called, rather than being called and ignored.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```tsx
|
|
432
|
+
* <Row
|
|
433
|
+
* icon={<EyeIcon />}
|
|
434
|
+
* label={layer.name}
|
|
435
|
+
* meta={layer.kind}
|
|
436
|
+
* trailing={<Badge>{layer.opacity}%</Badge>}
|
|
437
|
+
* depth={layer.depth}
|
|
438
|
+
* selected={layer.id === selectedId}
|
|
439
|
+
* onClick={() => select(layer.id)}
|
|
440
|
+
* />
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
declare function Row({ icon, label, meta, trailing, children, depth, selected, active, disabled, noIcon, role, className, style, onClick, onKeyDown, ...rest }: RowProps): ReactNode;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Control primitives: `IconButton`, `SearchField`, `SegmentedControl`, `Toggle`, `FieldGroup`,
|
|
447
|
+
* `Field`.
|
|
448
|
+
*
|
|
449
|
+
* **No icon library is imported here — ever.** Icons are `ReactNode` props and the two glyphs the
|
|
450
|
+
* primitives need internally (search, clear, chevron) are inline SVG paths at stroke 1.5. That is a
|
|
451
|
+
* deliberate consequence of the `lucide-react/dynamic` packaged-build incident: a chrome primitive
|
|
452
|
+
* must never be the thing that drags an icon bundle — or a dynamic-import trap — into every panel.
|
|
453
|
+
*
|
|
454
|
+
* @module
|
|
455
|
+
*/
|
|
456
|
+
|
|
457
|
+
/** Lucide `search` geometry. */
|
|
458
|
+
declare function SearchGlyph(): ReactNode;
|
|
459
|
+
/** Lucide `x` geometry. */
|
|
460
|
+
declare function ClearGlyph(): ReactNode;
|
|
461
|
+
/** Lucide `chevron-right` geometry — rotated by CSS when a section expands. */
|
|
462
|
+
declare function ChevronGlyph(): ReactNode;
|
|
463
|
+
/** Props for {@link IconButton}. */
|
|
464
|
+
interface IconButtonProps {
|
|
465
|
+
/** The glyph. Any node — the primitives never import an icon library. */
|
|
466
|
+
icon: ReactNode;
|
|
467
|
+
/** Accessible name; also used as the native tooltip. Required — an icon alone is not a name. */
|
|
468
|
+
label: string;
|
|
469
|
+
/** Click handler. */
|
|
470
|
+
onClick?: () => void;
|
|
471
|
+
/** Pressed/latched treatment (`aria-pressed`). */
|
|
472
|
+
active?: boolean;
|
|
473
|
+
/** Disabled treatment (`disabled`, `cursor: not-allowed`). */
|
|
474
|
+
disabled?: boolean;
|
|
475
|
+
/** `sm` = 20px (inside rows), `md` = 24px (toolbars, default). */
|
|
476
|
+
size?: 'sm' | 'md';
|
|
477
|
+
/** Extra classes. */
|
|
478
|
+
className?: string;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* A square icon-only button — the toolbar/row workhorse.
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```tsx
|
|
485
|
+
* <IconButton icon={<TrashGlyph />} label="Delete layer" onClick={remove} disabled={!selection} />
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
declare function IconButton({ icon, label, onClick, active, disabled, size, className, }: IconButtonProps): ReactNode;
|
|
489
|
+
/** Props for {@link TextButton}. */
|
|
490
|
+
interface TextButtonProps {
|
|
491
|
+
/** Label. Census copy patterns: `+ Add`, `Clear`, `Cancel`, `Unbind`. */
|
|
492
|
+
children?: ReactNode;
|
|
493
|
+
/** Click handler. */
|
|
494
|
+
onClick?: () => void;
|
|
495
|
+
/** Disabled treatment. */
|
|
496
|
+
disabled?: boolean;
|
|
497
|
+
/** Render at full body brightness instead of muted. */
|
|
498
|
+
strong?: boolean;
|
|
499
|
+
/** Extra classes. */
|
|
500
|
+
className?: string;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* A borderless text action — the inline `+ Add` / `Clear` / `Unbind` affordance. Muted by default so
|
|
504
|
+
* it reads as secondary; `strong` promotes it. Never a filled button: `DESIGN_SYSTEM.md` §7 allows
|
|
505
|
+
* exactly one primary action on screen and a panel is rarely the place for it.
|
|
506
|
+
*/
|
|
507
|
+
declare function TextButton({ children, onClick, disabled, strong, className, }: TextButtonProps): ReactNode;
|
|
508
|
+
/** Props for {@link SearchField}. */
|
|
509
|
+
interface SearchFieldProps {
|
|
510
|
+
/** Current query (controlled). */
|
|
511
|
+
value: string;
|
|
512
|
+
/** Called with the new query on every keystroke. */
|
|
513
|
+
onChange: (value: string) => void;
|
|
514
|
+
/** Placeholder text. Default `Search…`. */
|
|
515
|
+
placeholder?: string;
|
|
516
|
+
/** Accessible name. Default matches the placeholder. */
|
|
517
|
+
label?: string;
|
|
518
|
+
/** Called when the clear affordance or Escape empties the field. Defaults to `onChange('')`. */
|
|
519
|
+
onClear?: () => void;
|
|
520
|
+
/** Disable the input. */
|
|
521
|
+
disabled?: boolean;
|
|
522
|
+
/** Focus on mount. */
|
|
523
|
+
autoFocus?: boolean;
|
|
524
|
+
/** Extra classes. */
|
|
525
|
+
className?: string;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* The canonical filter input: recessed well, leading glyph, Escape-to-clear, and a clear button
|
|
529
|
+
* that only appears when there is something to clear.
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* ```tsx
|
|
533
|
+
* <SearchField value={query} onChange={setQuery} placeholder="Filter layers…" />
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
declare function SearchField({ value, onChange, placeholder, label, onClear, disabled, autoFocus, className, }: SearchFieldProps): ReactNode;
|
|
537
|
+
/** One option in a {@link SegmentedControl}. */
|
|
538
|
+
interface SegmentOption<T extends string> {
|
|
539
|
+
/** The value this segment selects. */
|
|
540
|
+
value: T;
|
|
541
|
+
/** Visible label. */
|
|
542
|
+
label: ReactNode;
|
|
543
|
+
/** Native tooltip / accessible name when the label is a glyph. */
|
|
544
|
+
title?: string;
|
|
545
|
+
/** Disable this segment only. */
|
|
546
|
+
disabled?: boolean;
|
|
547
|
+
}
|
|
548
|
+
/** Props for {@link SegmentedControl}. */
|
|
549
|
+
interface SegmentedControlProps<T extends string> {
|
|
550
|
+
/** The options, in display order. */
|
|
551
|
+
options: ReadonlyArray<SegmentOption<T>>;
|
|
552
|
+
/** The selected value. */
|
|
553
|
+
value: T;
|
|
554
|
+
/** Called with the newly selected value. Not called for the already-selected segment. */
|
|
555
|
+
onChange: (value: T) => void;
|
|
556
|
+
/** Accessible name for the group. */
|
|
557
|
+
label?: string;
|
|
558
|
+
/** `sm` = 20px, `md` = 24px (default). */
|
|
559
|
+
size?: 'sm' | 'md';
|
|
560
|
+
/** Stretch segments to fill the available width. */
|
|
561
|
+
stretch?: boolean;
|
|
562
|
+
/** Extra classes. */
|
|
563
|
+
className?: string;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* A radio group rendered as adjoined segments — the canonical "one of N modes" control (display
|
|
567
|
+
* units, view modes, sort order).
|
|
568
|
+
*
|
|
569
|
+
* Keyboard: arrow keys move the selection, matching the ARIA radiogroup pattern.
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```tsx
|
|
573
|
+
* <SegmentedControl
|
|
574
|
+
* label="Time display"
|
|
575
|
+
* options={[{ value: 'tc', label: 'TC' }, { value: 'frames', label: 'F' }]}
|
|
576
|
+
* value={display}
|
|
577
|
+
* onChange={setDisplay}
|
|
578
|
+
* />
|
|
579
|
+
* ```
|
|
580
|
+
*/
|
|
581
|
+
/**
|
|
582
|
+
* Resolve the value an arrow key moves to: wraps around, skips disabled segments, and returns the
|
|
583
|
+
* current value when there is nowhere to go.
|
|
584
|
+
*
|
|
585
|
+
* Exported because it is the only non-trivial logic in the control, and logic that only exists
|
|
586
|
+
* inside a DOM event handler is logic that never gets tested.
|
|
587
|
+
*
|
|
588
|
+
* @param options - The segments, in display order.
|
|
589
|
+
* @param value - The currently selected value.
|
|
590
|
+
* @param delta - `+1` for next, `-1` for previous.
|
|
591
|
+
* @returns The next value, or `value` if the move is a no-op.
|
|
592
|
+
*/
|
|
593
|
+
declare function nextSegmentValue<T extends string>(options: ReadonlyArray<SegmentOption<T>>, value: T, delta: number): T;
|
|
594
|
+
declare function SegmentedControl<T extends string>({ options, value, onChange, label, size, stretch, className, }: SegmentedControlProps<T>): ReactNode;
|
|
595
|
+
/** Props for {@link Toggle}. */
|
|
596
|
+
interface ToggleProps {
|
|
597
|
+
/** On/off state. */
|
|
598
|
+
checked: boolean;
|
|
599
|
+
/** Called with the next state. */
|
|
600
|
+
onChange: (checked: boolean) => void;
|
|
601
|
+
/** Accessible name. Required — a bare switch has no visible label. */
|
|
602
|
+
label: string;
|
|
603
|
+
/** Disabled treatment. */
|
|
604
|
+
disabled?: boolean;
|
|
605
|
+
/** `sm` = 16px track height, `md` = 18px (default). */
|
|
606
|
+
size?: 'sm' | 'md';
|
|
607
|
+
/** Extra classes. */
|
|
608
|
+
className?: string;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* The signature XENO toggle: a rectangular track with a SQUARE knob whose corner radius softens
|
|
612
|
+
* when it moves to the ON position (DESIGN_SYSTEM §3 "The Toggle Switch"). Never a pill.
|
|
613
|
+
*/
|
|
614
|
+
declare function Toggle({ checked, onChange, label, disabled, size, className, }: ToggleProps): ReactNode;
|
|
615
|
+
/** Props for {@link FieldGroup}. */
|
|
616
|
+
interface FieldGroupProps {
|
|
617
|
+
/** {@link Field} children. */
|
|
618
|
+
children?: ReactNode;
|
|
619
|
+
/** Width of the shared label column, in px. Default `PANEL_METRICS.fieldLabelWidth` (76). */
|
|
620
|
+
labelWidth?: number;
|
|
621
|
+
/** Extra classes. */
|
|
622
|
+
className?: string;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* A label-column layout: every {@link Field} inside shares ONE label width, so labels start at the
|
|
626
|
+
* same x and controls end at the same x — the column alignment DESIGN_SYSTEM §5 requires and the
|
|
627
|
+
* thing hand-rolled property grids always get wrong.
|
|
628
|
+
*
|
|
629
|
+
* @example
|
|
630
|
+
* ```tsx
|
|
631
|
+
* <FieldGroup labelWidth={64}>
|
|
632
|
+
* <Field label="Opacity"><Slider … /></Field>
|
|
633
|
+
* <Field label="Blend"><Select … /></Field>
|
|
634
|
+
* </FieldGroup>
|
|
635
|
+
* ```
|
|
636
|
+
*/
|
|
637
|
+
declare function FieldGroup({ children, labelWidth, className }: FieldGroupProps): ReactNode;
|
|
638
|
+
/** Props for {@link Field}. */
|
|
639
|
+
interface FieldProps {
|
|
640
|
+
/** The label text. */
|
|
641
|
+
label: ReactNode;
|
|
642
|
+
/** The control. */
|
|
643
|
+
children?: ReactNode;
|
|
644
|
+
/** Muted explanatory line under the control. */
|
|
645
|
+
hint?: ReactNode;
|
|
646
|
+
/** Id of the control the label points at. Generated when omitted and `children` is a single input. */
|
|
647
|
+
htmlFor?: string;
|
|
648
|
+
/** Stack the label above the control instead of beside it. */
|
|
649
|
+
stacked?: boolean;
|
|
650
|
+
/** Extra classes. */
|
|
651
|
+
className?: string;
|
|
652
|
+
}
|
|
653
|
+
/** One label + control row inside a {@link FieldGroup}. */
|
|
654
|
+
declare function Field({ label, children, hint, htmlFor, stacked, className, }: FieldProps): ReactNode;
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* The three terminal states: `EmptyState`, `LoadingState`, `ErrorState`.
|
|
658
|
+
*
|
|
659
|
+
* ## The actionable-hint rule
|
|
660
|
+
*
|
|
661
|
+
* The five shipped panels all render empty as a single dim uppercase noun — `No layers`,
|
|
662
|
+
* `No history`, `Nothing selected`. That tells a user what they can already see. `xeno-canvas`
|
|
663
|
+
* pioneered the better pattern: a second line that says what the thing IS and how to make one —
|
|
664
|
+
* *"Bind a text layer's style to one in Properties → Text."*, *"An alias points at another token;
|
|
665
|
+
* the resolver follows the chain."*
|
|
666
|
+
*
|
|
667
|
+
* So {@link EmptyState} splits them: `title` names the state (keeping the shipped typography, so
|
|
668
|
+
* adopting the primitive changes nothing visually for a panel that passes only a title), and `hint`
|
|
669
|
+
* carries the instruction. **A hint that merely restates the title is worse than no hint** — write
|
|
670
|
+
* the sentence that gets the user to a non-empty panel, or omit it.
|
|
671
|
+
*
|
|
672
|
+
* @module
|
|
673
|
+
*/
|
|
674
|
+
|
|
675
|
+
/** Props for {@link EmptyState}. */
|
|
676
|
+
interface EmptyStateProps {
|
|
677
|
+
/** Names the state. Keep it short: `No layers`, `Nothing selected`. */
|
|
678
|
+
title: ReactNode;
|
|
679
|
+
/**
|
|
680
|
+
* The ACTIONABLE line: what this panel holds and how to put something in it. Omit rather than
|
|
681
|
+
* restate the title.
|
|
682
|
+
*/
|
|
683
|
+
hint?: ReactNode;
|
|
684
|
+
/** Optional glyph, shown in a hairline chip above the title. */
|
|
685
|
+
icon?: ReactNode;
|
|
686
|
+
/** Optional call to action (usually a {@link TextButton}). */
|
|
687
|
+
action?: ReactNode;
|
|
688
|
+
/** Extra classes. */
|
|
689
|
+
className?: string;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* The canonical zero-content state.
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```tsx
|
|
696
|
+
* <EmptyState
|
|
697
|
+
* title="No tokens"
|
|
698
|
+
* hint="A token is a named value other layers bind to. Add one to start a palette."
|
|
699
|
+
* action={<TextButton onClick={createToken}>+ Add token</TextButton>}
|
|
700
|
+
* />
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
declare function EmptyState({ title, hint, icon, action, className }: EmptyStateProps): ReactNode;
|
|
704
|
+
/** Props for {@link LoadingState}. */
|
|
705
|
+
interface LoadingStateProps {
|
|
706
|
+
/** What is loading. Default `Loading…`. */
|
|
707
|
+
label?: ReactNode;
|
|
708
|
+
/** Extra classes. */
|
|
709
|
+
className?: string;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* The canonical busy state — an indeterminate BAR, never a spinner.
|
|
713
|
+
*
|
|
714
|
+
* `DESIGN_SYSTEM.md` §3 bans circles and §10 prefers skeletons over spinning indicators; a 2px
|
|
715
|
+
* indeterminate bar satisfies both and costs one element.
|
|
716
|
+
*/
|
|
717
|
+
declare function LoadingState({ label, className }: LoadingStateProps): ReactNode;
|
|
718
|
+
/** Props for {@link ErrorState}. */
|
|
719
|
+
interface ErrorStateProps {
|
|
720
|
+
/** Headline. Default `Something went wrong`. */
|
|
721
|
+
title?: ReactNode;
|
|
722
|
+
/** The failure detail — the message, not a stack. */
|
|
723
|
+
message?: ReactNode;
|
|
724
|
+
/** Retry handler; renders a retry action when supplied. */
|
|
725
|
+
onRetry?: () => void;
|
|
726
|
+
/** Label for the retry action. Default `Retry`. */
|
|
727
|
+
retryLabel?: string;
|
|
728
|
+
/** Extra classes. */
|
|
729
|
+
className?: string;
|
|
730
|
+
}
|
|
731
|
+
/** The canonical failure state. The title is the only place a panel renders status red by default. */
|
|
732
|
+
declare function ErrorState({ title, message, onRetry, retryLabel, className, }: ErrorStateProps): ReactNode;
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Indicator + data primitives: `Badge`, `StatusDot`, `StatusBadge`, `IconFrame`, `StatTile`,
|
|
736
|
+
* `ProportionBar`, `StatusBar`.
|
|
737
|
+
*
|
|
738
|
+
* Lifted from `xeno-post`'s kit, which is the only place in the ecosystem that solved the
|
|
739
|
+
* monochromatic data-display problem properly: a proportion BAR instead of a pie/donut (no circles),
|
|
740
|
+
* a square status dot instead of a round one, and value emphasis carried by size + `tabular-nums`
|
|
741
|
+
* rather than color. `StatTile` + `ProportionBar` are the two primitives the future `metrics` panel
|
|
742
|
+
* is built out of, so they land now rather than being invented a sixth time.
|
|
743
|
+
*
|
|
744
|
+
* @module
|
|
745
|
+
*/
|
|
746
|
+
|
|
747
|
+
/** Props for {@link Badge}. */
|
|
748
|
+
interface BadgeProps {
|
|
749
|
+
/** Contents — a count, a unit, a state word. */
|
|
750
|
+
children?: ReactNode;
|
|
751
|
+
/** Semantic tone. Default `neutral` (monochromatic). */
|
|
752
|
+
tone?: PanelTone;
|
|
753
|
+
/** `sm` = 12px (in-panel, default), `md` = 18px (DESIGN_SYSTEM §7 standard). */
|
|
754
|
+
size?: 'sm' | 'md';
|
|
755
|
+
/** Native tooltip. A chip is often the only place a long value can be read in full. */
|
|
756
|
+
title?: string;
|
|
757
|
+
/** Extra classes. */
|
|
758
|
+
className?: string;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* A small rectangular chip. Never interactive — `DESIGN_SYSTEM.md` §7: "If a badge needs to be
|
|
762
|
+
* clickable, it's a ghost button, not a badge."
|
|
763
|
+
*/
|
|
764
|
+
declare function Badge({ children, tone, size, title, className, }: BadgeProps): ReactNode;
|
|
765
|
+
/** Props for {@link StatusDot}. */
|
|
766
|
+
interface StatusDotProps {
|
|
767
|
+
/** Semantic tone. */
|
|
768
|
+
tone?: PanelTone;
|
|
769
|
+
/** Accessible label. Supply it when the dot is the ONLY carrier of the state. */
|
|
770
|
+
label?: string;
|
|
771
|
+
/** Extra classes. */
|
|
772
|
+
className?: string;
|
|
773
|
+
}
|
|
774
|
+
/** A 2px-radius SQUARE status dot. Never a circle (`DESIGN_SYSTEM.md` §3 / §13). */
|
|
775
|
+
declare function StatusDot({ tone, label, className }: StatusDotProps): ReactNode;
|
|
776
|
+
/** Props for {@link StatusBadge}. */
|
|
777
|
+
interface StatusBadgeProps {
|
|
778
|
+
/** The state text. */
|
|
779
|
+
children?: ReactNode;
|
|
780
|
+
/** Semantic tone. */
|
|
781
|
+
tone?: PanelTone;
|
|
782
|
+
/** Extra classes. */
|
|
783
|
+
className?: string;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Dot + label. Color is never the sole indicator (`DESIGN_SYSTEM.md` §11) — the word carries the
|
|
787
|
+
* state and the dot reinforces it.
|
|
788
|
+
*/
|
|
789
|
+
declare function StatusBadge({ children, tone, className }: StatusBadgeProps): ReactNode;
|
|
790
|
+
/** Props for {@link IconFrame}. */
|
|
791
|
+
interface IconFrameProps {
|
|
792
|
+
/** The glyph. */
|
|
793
|
+
icon: ReactNode;
|
|
794
|
+
/** Box edge in px. Default 20. */
|
|
795
|
+
size?: number;
|
|
796
|
+
/** Extra classes. */
|
|
797
|
+
className?: string;
|
|
798
|
+
}
|
|
799
|
+
/** A flat outlined icon container — a thumbnail stand-in when there is no thumbnail. */
|
|
800
|
+
declare function IconFrame({ icon, size, className }: IconFrameProps): ReactNode;
|
|
801
|
+
/** Props for {@link StatTile}. */
|
|
802
|
+
interface StatTileProps {
|
|
803
|
+
/** Uppercase label. */
|
|
804
|
+
label: ReactNode;
|
|
805
|
+
/** The value. Rendered with `tabular-nums` so a grid of tiles aligns. */
|
|
806
|
+
value: ReactNode;
|
|
807
|
+
/** Supporting line under the value (delta, window, unit). */
|
|
808
|
+
sub?: ReactNode;
|
|
809
|
+
/** Trailing glyph in the label row. */
|
|
810
|
+
icon?: ReactNode;
|
|
811
|
+
/** Native tooltip — where a metric's longer explanation goes. */
|
|
812
|
+
title?: string;
|
|
813
|
+
/** Extra classes. */
|
|
814
|
+
className?: string;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Label + big value tile — the KPI unit of the future `metrics` panel.
|
|
818
|
+
*
|
|
819
|
+
* @example
|
|
820
|
+
* ```tsx
|
|
821
|
+
* <StatTile label="p95 frame" value="14.2 ms" sub="last 60 s" />
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
824
|
+
declare function StatTile({ label, value, sub, icon, title, className }: StatTileProps): ReactNode;
|
|
825
|
+
/** One slice of a {@link ProportionBar}. */
|
|
826
|
+
interface ProportionSegment {
|
|
827
|
+
/** Relative magnitude. Non-positive values are skipped. */
|
|
828
|
+
value: number;
|
|
829
|
+
/** White alpha for this slice, `0`–`1`. Distinguish slices by OPACITY, not hue. */
|
|
830
|
+
opacity?: number;
|
|
831
|
+
/** Native tooltip. */
|
|
832
|
+
label?: string;
|
|
833
|
+
/** Stable key when the caller has one. */
|
|
834
|
+
key?: string;
|
|
835
|
+
}
|
|
836
|
+
/** Props for {@link ProportionBar}. */
|
|
837
|
+
interface ProportionBarProps {
|
|
838
|
+
/** The slices, in display order. */
|
|
839
|
+
segments: ReadonlyArray<ProportionSegment>;
|
|
840
|
+
/** Accessible description of the whole bar. */
|
|
841
|
+
label?: string;
|
|
842
|
+
/** Extra classes. */
|
|
843
|
+
className?: string;
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* A horizontal stacked bar — the monochromatic replacement for a pie/donut chart.
|
|
847
|
+
*
|
|
848
|
+
* Slices are separated by white ALPHA, not hue: `DESIGN_SYSTEM.md` §13 forbids chromatic color
|
|
849
|
+
* outside status contexts, and a categorical palette is exactly that. When no `opacity` is supplied
|
|
850
|
+
* the bar ramps evenly from bright to dim across the slices.
|
|
851
|
+
*/
|
|
852
|
+
declare function ProportionBar({ segments, label, className }: ProportionBarProps): ReactNode;
|
|
853
|
+
/** Props for {@link StatusBar}. */
|
|
854
|
+
interface StatusBarProps {
|
|
855
|
+
/** Left cluster — counts, selection summary. */
|
|
856
|
+
left?: ReactNode;
|
|
857
|
+
/** Right cluster — mode, state. */
|
|
858
|
+
right?: ReactNode;
|
|
859
|
+
/** Free-form children (used instead of the clusters). */
|
|
860
|
+
children?: ReactNode;
|
|
861
|
+
/** Extra classes. */
|
|
862
|
+
className?: string;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* The panel footer strip. Census: layers and history already ship a byte-identical one
|
|
866
|
+
* (`2px 6px`, 9px, muted, header surface, space-between) — this is that, once.
|
|
867
|
+
*/
|
|
868
|
+
declare function StatusBar({ left, right, children, className }: StatusBarProps): ReactNode;
|
|
869
|
+
/** Props for {@link Sparkline}. */
|
|
870
|
+
interface SparklineProps {
|
|
871
|
+
/** The series, oldest first. */
|
|
872
|
+
data: readonly number[];
|
|
873
|
+
/** Width in px. */
|
|
874
|
+
width?: number;
|
|
875
|
+
/** Height in px. */
|
|
876
|
+
height?: number;
|
|
877
|
+
/** Accessible description. Without one the mark is decorative and hidden from AT. */
|
|
878
|
+
label?: string;
|
|
879
|
+
/** Native tooltip. */
|
|
880
|
+
title?: string;
|
|
881
|
+
/** Extra classes. */
|
|
882
|
+
className?: string;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* A monochrome trend line.
|
|
886
|
+
*
|
|
887
|
+
* Lifted from `xeno-post`'s `ui/kit.tsx`, which is the only sparkline implementation in the
|
|
888
|
+
* ecosystem. It lives here rather than in a panel because three consumers want it — `metrics` (which
|
|
889
|
+
* shipped a local copy first), `chart`, and the future `runs` panel — and three copies of a mark is
|
|
890
|
+
* how a design system stops being one.
|
|
891
|
+
*
|
|
892
|
+
* Distinct from {@link ProportionBar}: a line is a different MARK, not a variant. The two are not
|
|
893
|
+
* interchangeable and neither is a special case of the other.
|
|
894
|
+
*
|
|
895
|
+
* Degenerate inputs render an empty well rather than throwing: a series of one point has no slope,
|
|
896
|
+
* and a flat series has no range to normalize against (it draws a centred straight line).
|
|
897
|
+
*/
|
|
898
|
+
declare function Sparkline({ data, width, height, label, title, className, }: SparklineProps): ReactNode;
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Zero-config stylesheet injection for the React primitives.
|
|
902
|
+
*
|
|
903
|
+
* Every primitive calls {@link usePanelPrimitives} so a panel author needs **no CSS import** — the
|
|
904
|
+
* canonical stylesheet lands in `<head>` the first time any primitive renders, exactly once per
|
|
905
|
+
* document. Injection happens in a `useState` LAZY INITIALIZER (during the first render) rather
|
|
906
|
+
* than in `useEffect`, so the styles are present before paint — an effect would flash unstyled
|
|
907
|
+
* chrome. The initializer is idempotent and guarded by an element id, so React StrictMode's
|
|
908
|
+
* double-invoke is harmless.
|
|
909
|
+
*
|
|
910
|
+
* @module
|
|
911
|
+
*/
|
|
912
|
+
/**
|
|
913
|
+
* Ensure the primitives stylesheet is present in the document.
|
|
914
|
+
*
|
|
915
|
+
* Safe to call from any number of components and on the server (where it is a no-op).
|
|
916
|
+
*/
|
|
917
|
+
declare function usePanelPrimitives(): void;
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* The canonical panel GEOMETRY constants — the single source of truth for both the stylesheet and
|
|
921
|
+
* any panel that needs a measurement as a NUMBER (virtualizers, hit-testing, canvas overlays).
|
|
922
|
+
*
|
|
923
|
+
* `PANEL_PRIMITIVES_CSS` is generated by interpolating this object, so the CSS and the JS can never
|
|
924
|
+
* drift. That matters: a virtualized list computes its scroll geometry from a row height in JS while
|
|
925
|
+
* the row is sized by CSS — when those two numbers disagree you get a list that scrolls to the wrong
|
|
926
|
+
* item, and it is invisible until someone changes one of them.
|
|
927
|
+
*
|
|
928
|
+
* ## Where these numbers come from
|
|
929
|
+
*
|
|
930
|
+
* Every value below was chosen from a census of the five shipped canonical panels (color, layers,
|
|
931
|
+
* history, inspector, transport) plus `xeno-workflow`'s CSS contract and `xeno-post`'s kit. Where the
|
|
932
|
+
* five agreed, the consensus value is used verbatim. Where they disagreed, the choice is annotated
|
|
933
|
+
* with what the alternatives were, so a future reader can see it was a decision and not a guess.
|
|
934
|
+
*
|
|
935
|
+
* @module
|
|
936
|
+
*/
|
|
937
|
+
/**
|
|
938
|
+
* Canonical panel metrics, in CSS pixels.
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* ```ts
|
|
942
|
+
* // A virtualizer that cannot drift from the stylesheet:
|
|
943
|
+
* const first = Math.floor(scrollTop / PANEL_METRICS.rowHeight)
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
946
|
+
declare const PANEL_METRICS: Readonly<{
|
|
947
|
+
/**
|
|
948
|
+
* Dense list row height.
|
|
949
|
+
*
|
|
950
|
+
* Census: history 22, layers 24, inspector 20 (as `min-height`). 22 is the median and the value
|
|
951
|
+
* of the densest *scrolling* list. Override per-list with `--xeno-row-h` — layers can keep 24
|
|
952
|
+
* without forking the primitive.
|
|
953
|
+
*/
|
|
954
|
+
readonly rowHeight: 22;
|
|
955
|
+
/** Indent per tree depth level. Census: layers 10 (the only implementation). */
|
|
956
|
+
readonly rowIndent: 10;
|
|
957
|
+
/** Gap between a row's slots. Census: layers 4, inspector 4, history 5. */
|
|
958
|
+
readonly rowGap: 4;
|
|
959
|
+
/** Fixed width of a row's leading icon slot — this is what keeps labels in ONE column. */
|
|
960
|
+
readonly rowIconSlot: 14;
|
|
961
|
+
/** Horizontal padding inside panel chrome. Census: `0 6px` / `4px 6px`, 6px in 5/5 packages. */
|
|
962
|
+
readonly gutter: 6;
|
|
963
|
+
/** Vertical padding of a header/toolbar strip. Census: `4px 6px` in 4/5 packages. */
|
|
964
|
+
readonly stripPadY: 4;
|
|
965
|
+
/**
|
|
966
|
+
* Minimum height of a toolbar strip. Not set by any panel today (all are content-sized); 26 is
|
|
967
|
+
* what `4 + 18 + 4` already produces, pinned so toolbars align across panels.
|
|
968
|
+
*/
|
|
969
|
+
readonly toolbarMinHeight: 26;
|
|
970
|
+
/** Footer / status strip padding-y. Census: `2px 6px`, identical in layers + history. */
|
|
971
|
+
readonly footerPadY: 2;
|
|
972
|
+
/** Section header height. Census: inspector 20 (the only real section header in the catalog). */
|
|
973
|
+
readonly sectionHeaderHeight: 20;
|
|
974
|
+
/**
|
|
975
|
+
* Dense control height (inputs, segmented control, toolbar buttons).
|
|
976
|
+
*
|
|
977
|
+
* Census: inspector 18, history 18, transport 18, layers 20. **This is BELOW `DESIGN_SYSTEM.md`
|
|
978
|
+
* §3.1's 24px "compact" floor** — see {@link PANEL_METRICS.controlHeight}. 20 is the largest value
|
|
979
|
+
* any shipped panel uses, so adopting the primitives never makes a panel denser than it is today.
|
|
980
|
+
*/
|
|
981
|
+
readonly controlHeightSm: 20;
|
|
982
|
+
/** Standard control height — the DESIGN_SYSTEM §3.1 "compact controls" value. Use in panel bodies. */
|
|
983
|
+
readonly controlHeight: 24;
|
|
984
|
+
/** In-row icon button. Census: inspector 14, layers 14, history 16. 14 is below a usable hit target. */
|
|
985
|
+
readonly iconButtonSm: 16;
|
|
986
|
+
/** Toolbar icon button. Census: transport 20×18, color 22. */
|
|
987
|
+
readonly iconButton: 20;
|
|
988
|
+
/** Icon glyph inside a `sm` button. Census: 11 is the most common glyph size across four packages. */
|
|
989
|
+
readonly iconGlyphSm: 11;
|
|
990
|
+
/** Icon glyph inside an `md` button. Census: layers + transport primary use 12. */
|
|
991
|
+
readonly iconGlyph: 12;
|
|
992
|
+
/** Badge height. Census: inspector + history byte-identical at 12. */
|
|
993
|
+
readonly badgeHeight: 12;
|
|
994
|
+
/** Standard badge height — the `DESIGN_SYSTEM.md` §7 "Status Badges / Pills" value. */
|
|
995
|
+
readonly badgeHeightMd: 18;
|
|
996
|
+
/** Square status dot edge. Never a circle (DESIGN_SYSTEM §3). */
|
|
997
|
+
readonly dotSize: 6;
|
|
998
|
+
/** Shared label-column width in a `FieldGroup`. Census: inspector's configurable default is 56. */
|
|
999
|
+
readonly fieldLabelWidth: 56;
|
|
1000
|
+
/** Base corner radius. Census: `border-radius: 2` appears 38 times across 5/5 packages. */
|
|
1001
|
+
readonly radius: 2;
|
|
1002
|
+
/** Outer-container radius (frames, cards). DESIGN_SYSTEM §7 "small radius". */
|
|
1003
|
+
readonly radiusMd: 6;
|
|
1004
|
+
/** Micro text — badges, units. */
|
|
1005
|
+
readonly fontMicro: 8;
|
|
1006
|
+
/** Label text — uppercase section/panel labels, footers. */
|
|
1007
|
+
readonly fontLabel: 9;
|
|
1008
|
+
/** Secondary text — metadata, empty-state titles. */
|
|
1009
|
+
readonly fontMeta: 10;
|
|
1010
|
+
/** Body text — the default panel size. */
|
|
1011
|
+
readonly fontBody: 11;
|
|
1012
|
+
}>;
|
|
1013
|
+
/** The type of {@link PANEL_METRICS}. */
|
|
1014
|
+
type PanelMetrics = typeof PANEL_METRICS;
|
|
1015
|
+
/**
|
|
1016
|
+
* Canonical color literals used by the stylesheet, exported for panels that must compute a color in
|
|
1017
|
+
* JS (canvas overlays, inline gradients) rather than declare it in CSS.
|
|
1018
|
+
*
|
|
1019
|
+
* Every one is either a `DESIGN_SYSTEM.md` token or the census consensus. The two status reds are
|
|
1020
|
+
* spelled once here because the shipped panels currently disagree (`#ef4444` vs
|
|
1021
|
+
* `rgba(239,68,68,0.65)`).
|
|
1022
|
+
*/
|
|
1023
|
+
declare const PANEL_COLORS: Readonly<{
|
|
1024
|
+
/** Row hover fill. DESIGN_SYSTEM §2 `rowHover`. **No shipped panel has a hover state at all.** */
|
|
1025
|
+
readonly rowHover: "rgba(255,255,255,0.05)";
|
|
1026
|
+
/** Row selected fill. Census: byte-identical in layers + history. */
|
|
1027
|
+
readonly rowSelected: "rgba(255,255,255,0.10)";
|
|
1028
|
+
/** Active/focus ring. Census: byte-identical `inset 0 0 0 1px` in layers + history. */
|
|
1029
|
+
readonly ring: "rgba(255,255,255,0.15)";
|
|
1030
|
+
/** Keyboard focus outline. DESIGN_SYSTEM §11. **No shipped panel has one.** */
|
|
1031
|
+
readonly focus: "rgba(255,255,255,0.25)";
|
|
1032
|
+
/** Recessed well fill for inputs. Census: 4/5 packages. */
|
|
1033
|
+
readonly well: "rgba(0,0,0,0.3)";
|
|
1034
|
+
/** Hairline border. */
|
|
1035
|
+
readonly border: "rgba(255,255,255,0.08)";
|
|
1036
|
+
/** Soft raised fill — chips, ghost buttons. Census: 5/5 packages. */
|
|
1037
|
+
readonly soft: "rgba(255,255,255,0.06)";
|
|
1038
|
+
/** Section-header wash. Census: inspector. */
|
|
1039
|
+
readonly wash: "rgba(255,255,255,0.03)";
|
|
1040
|
+
/** Solid status red (indicators). DESIGN_SYSTEM §2 `error`. */
|
|
1041
|
+
readonly error: "#ef4444";
|
|
1042
|
+
/** Status red for TEXT. DESIGN_SYSTEM §2 `error.text`. */
|
|
1043
|
+
readonly errorText: "rgba(239,68,68,0.65)";
|
|
1044
|
+
/** Status amber for text. */
|
|
1045
|
+
readonly warningText: "rgba(245,158,11,0.65)";
|
|
1046
|
+
/** Status green for text. */
|
|
1047
|
+
readonly successText: "#5a9a6a";
|
|
1048
|
+
/** Info blue-gray for text (the dimmest permitted chroma). */
|
|
1049
|
+
readonly infoText: "#7aa2c4";
|
|
1050
|
+
}>;
|
|
1051
|
+
/** The type of {@link PANEL_COLORS}. */
|
|
1052
|
+
type PanelColors = typeof PANEL_COLORS;
|
|
1053
|
+
/**
|
|
1054
|
+
* The opacity a disabled / "off" control drops to.
|
|
1055
|
+
*
|
|
1056
|
+
* Census: the five panels hand-wrote **eleven** different opacity pairs for this; `0.35` is the most
|
|
1057
|
+
* frequent "off" value (layers lock, layers toggle-chip, inspector stopwatch, inspector reset).
|
|
1058
|
+
*/
|
|
1059
|
+
declare const PANEL_DISABLED_OPACITY = 0.35;
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* The canonical panel-chrome stylesheet, as a string (the single source of truth).
|
|
1063
|
+
*
|
|
1064
|
+
* Generated by interpolating {@link PANEL_METRICS} / {@link PANEL_COLORS} so the CSS can never drift
|
|
1065
|
+
* from the numbers panels read in JS. `@xenosystem/workbench/primitives.css` is emitted from this at build
|
|
1066
|
+
* time for hosts that prefer a `<link>` / bundler import over runtime injection.
|
|
1067
|
+
*
|
|
1068
|
+
* ## Two invariants, both test-enforced
|
|
1069
|
+
*
|
|
1070
|
+
* 1. **Every `var()` carries a literal fallback.** The primitives must render correctly with NO
|
|
1071
|
+
* theme injected — a product embedding one canonical panel into its own chrome gets the right
|
|
1072
|
+
* look without adopting the workbench theme. The `--panel-*` → `--xeno-*` → literal chain is the
|
|
1073
|
+
* one 4 of the 5 shipped panels already use, so tokens set by either convention win.
|
|
1074
|
+
* 2. **Selectors are flat.** No `:has()`, no multi-level `:not()` chains — `DESIGN_SYSTEM.md` §3.1
|
|
1075
|
+
* records that Lightning CSS / Turbopack silently drop those rule blocks at build time.
|
|
1076
|
+
*
|
|
1077
|
+
* ## Three gaps this sheet closes
|
|
1078
|
+
*
|
|
1079
|
+
* The census of the five shipped panels found NO hover state, NO focus-visible state, and no shared
|
|
1080
|
+
* disabled treatment (eleven hand-written opacity pairs) anywhere in the catalog. Those are defined
|
|
1081
|
+
* here once, so every panel built on the primitives gets them for free.
|
|
1082
|
+
*
|
|
1083
|
+
* @module
|
|
1084
|
+
*/
|
|
1085
|
+
/** The canonical panel-chrome stylesheet. */
|
|
1086
|
+
declare const PANEL_PRIMITIVES_CSS: string;
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Idempotent injector for the panel-primitives stylesheet.
|
|
1090
|
+
*
|
|
1091
|
+
* Mirrors `ensureWorkbenchThemeInjected` (same `StyleInjectTarget` shape, same element-id
|
|
1092
|
+
* idempotency key) but ships the PRIMITIVES sheet, which is independent of the Dockview theme:
|
|
1093
|
+
* a product embedding a single canonical panel outside the workbench needs the primitives and NOT
|
|
1094
|
+
* the dock chrome.
|
|
1095
|
+
*
|
|
1096
|
+
* @module
|
|
1097
|
+
*/
|
|
1098
|
+
/** The `<style>` element id used for runtime injection (idempotency key). */
|
|
1099
|
+
declare const PANEL_PRIMITIVES_STYLE_ID = "xeno-panel-primitives";
|
|
1100
|
+
/** Minimal structural shape of the `document` bits the injector needs (for testability/SSR). */
|
|
1101
|
+
interface PrimitivesInjectTarget {
|
|
1102
|
+
getElementById(id: string): unknown;
|
|
1103
|
+
createElement(tagName: 'style'): {
|
|
1104
|
+
id: string;
|
|
1105
|
+
textContent: string | null;
|
|
1106
|
+
};
|
|
1107
|
+
head: {
|
|
1108
|
+
appendChild(node: unknown): void;
|
|
1109
|
+
} | null;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Inject the primitives stylesheet ONCE into a document head (idempotent by element id).
|
|
1113
|
+
*
|
|
1114
|
+
* @param target - The document (defaults to `globalThis.document`; injectable for tests).
|
|
1115
|
+
* @returns `true` if a `<style>` was inserted; `false` if already present or no document.
|
|
1116
|
+
*
|
|
1117
|
+
* @example
|
|
1118
|
+
* ```ts
|
|
1119
|
+
* // Optional: inject early, before any panel renders.
|
|
1120
|
+
* ensurePanelPrimitivesInjected()
|
|
1121
|
+
* ```
|
|
1122
|
+
*/
|
|
1123
|
+
declare function ensurePanelPrimitivesInjected(target?: PrimitivesInjectTarget): boolean;
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* `COMPONENTS` — the chrome family's units by their `chrome/Unit` names, for a block declaration's registry
|
|
1127
|
+
* (`@xenosystem/block-sdk`). One map per family, merged by the host; nothing is registered by hand elsewhere.
|
|
1128
|
+
*/
|
|
1129
|
+
|
|
1130
|
+
declare const COMPONENTS: {
|
|
1131
|
+
readonly 'chrome/IconButton': typeof IconButton;
|
|
1132
|
+
readonly 'chrome/TextButton': typeof TextButton;
|
|
1133
|
+
readonly 'chrome/SearchField': typeof SearchField;
|
|
1134
|
+
readonly 'chrome/SegmentedControl': typeof SegmentedControl;
|
|
1135
|
+
readonly 'chrome/Toggle': typeof Toggle;
|
|
1136
|
+
readonly 'chrome/FieldGroup': typeof FieldGroup;
|
|
1137
|
+
readonly 'chrome/Field': typeof Field;
|
|
1138
|
+
readonly 'chrome/SearchGlyph': typeof SearchGlyph;
|
|
1139
|
+
readonly 'chrome/ClearGlyph': typeof ClearGlyph;
|
|
1140
|
+
readonly 'chrome/ChevronGlyph': typeof ChevronGlyph;
|
|
1141
|
+
readonly 'chrome/PanelFrame': typeof PanelFrame;
|
|
1142
|
+
readonly 'chrome/Toolbar': typeof Toolbar;
|
|
1143
|
+
readonly 'chrome/ToolbarGroup': typeof ToolbarGroup;
|
|
1144
|
+
readonly 'chrome/Divider': typeof Divider;
|
|
1145
|
+
readonly 'chrome/ScrollArea': typeof ScrollArea;
|
|
1146
|
+
readonly 'chrome/Badge': typeof Badge;
|
|
1147
|
+
readonly 'chrome/StatusDot': typeof StatusDot;
|
|
1148
|
+
readonly 'chrome/StatusBadge': typeof StatusBadge;
|
|
1149
|
+
readonly 'chrome/IconFrame': typeof IconFrame;
|
|
1150
|
+
readonly 'chrome/StatTile': typeof StatTile;
|
|
1151
|
+
readonly 'chrome/ProportionBar': typeof ProportionBar;
|
|
1152
|
+
readonly 'chrome/StatusBar': typeof StatusBar;
|
|
1153
|
+
readonly 'chrome/Sparkline': typeof Sparkline;
|
|
1154
|
+
readonly 'chrome/RowList': typeof RowList;
|
|
1155
|
+
readonly 'chrome/Row': typeof Row;
|
|
1156
|
+
readonly 'chrome/Section': typeof Section;
|
|
1157
|
+
readonly 'chrome/EmptyState': typeof EmptyState;
|
|
1158
|
+
readonly 'chrome/LoadingState': typeof LoadingState;
|
|
1159
|
+
readonly 'chrome/ErrorState': typeof ErrorState;
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* `gallery()` — every unit of the CHROME family in every state, as renderable entries: the toolbar, the
|
|
1164
|
+
* search field, the rows, the empty/loading/error states and the indicators every block is built from.
|
|
1165
|
+
* This is what the workshop renders from source, the library shows read-only, and the gate screenshots.
|
|
1166
|
+
*
|
|
1167
|
+
* `render` is pure: fixture props in, element out, callbacks inert. Nothing here reaches a host.
|
|
1168
|
+
*/
|
|
1169
|
+
|
|
1170
|
+
interface GalleryEntry {
|
|
1171
|
+
family: 'chrome';
|
|
1172
|
+
unit: string;
|
|
1173
|
+
state: string;
|
|
1174
|
+
/** `form` (a 400 px column) or `door` (fills a box) — the same staging vocabulary as the auth family. */
|
|
1175
|
+
stage: 'form' | 'door';
|
|
1176
|
+
render: () => ReactElement;
|
|
1177
|
+
}
|
|
1178
|
+
/** The entries, one per unit × state. Adding a unit to {@link COMPONENTS} without an entry here fails the family's test. */
|
|
1179
|
+
declare function gallery(): GalleryEntry[];
|
|
1180
|
+
|
|
1181
|
+
export { Badge, type BadgeProps, CLS_BADGE, CLS_BAR, CLS_BAR_SEGMENT, CLS_DIVIDER, CLS_DOT, CLS_EMPTY, CLS_ERROR, CLS_FIELD, CLS_FIELD_CONTROL, CLS_FIELD_GROUP, CLS_FIELD_HINT, CLS_FIELD_LABEL, CLS_ICON_BUTTON, CLS_ICON_FRAME, CLS_LOADING, CLS_PANEL, CLS_PANEL_CONTENT, CLS_PANEL_CONTENT_NO_HEADER, CLS_PANEL_HEADER, CLS_PANEL_TITLE, CLS_ROW, CLS_ROWLIST, CLS_ROW_ICON, CLS_ROW_LABEL, CLS_ROW_META, CLS_ROW_TRAILING, CLS_SCROLL, CLS_SEARCH, CLS_SEARCH_INPUT, CLS_SECTION, CLS_SECTION_ACTIONS, CLS_SECTION_BODY, CLS_SECTION_CHEVRON, CLS_SECTION_HEADER, CLS_SECTION_TITLE, CLS_SECTION_TRIGGER, CLS_SEGMENT, CLS_SEGMENTED, CLS_STAT, CLS_STATE, CLS_STATE_HINT, CLS_STATE_ICON, CLS_STATE_PROGRESS, CLS_STATE_TITLE, CLS_STATUSBAR, CLS_STAT_LABEL, CLS_STAT_SUB, CLS_STAT_VALUE, CLS_TEXT_BUTTON, CLS_TOGGLE, CLS_TOGGLE_KNOB, CLS_TOOLBAR, CLS_TOOLBAR_GROUP, COMPONENTS, ChevronGlyph, ClearGlyph, Divider, type DividerProps, EmptyState, type EmptyStateProps, ErrorState, type ErrorStateProps, Field, FieldGroup, type FieldGroupProps, type FieldProps, type GalleryEntry, IconButton, type IconButtonProps, IconFrame, type IconFrameProps, LoadingState, type LoadingStateProps, PANEL_COLORS, PANEL_DISABLED_OPACITY, PANEL_METRICS, PANEL_PRIMITIVES_CSS, PANEL_PRIMITIVES_STYLE_ID, PANEL_TONES, type PanelColors, PanelFrame, type PanelFrameProps, type PanelMetrics, type PanelTone, type PrimitivesInjectTarget, ProportionBar, type ProportionBarProps, type ProportionSegment, Row, RowList, type RowListProps, type RowProps, type RowState, ScrollArea, type ScrollAreaProps, SearchField, type SearchFieldProps, SearchGlyph, Section, type SectionProps, type SegmentOption, SegmentedControl, type SegmentedControlProps, Sparkline, type SparklineProps, StatTile, type StatTileProps, StatusBadge, type StatusBadgeProps, StatusBar, type StatusBarProps, StatusDot, type StatusDotProps, TextButton, type TextButtonProps, Toggle, type ToggleProps, Toolbar, ToolbarGroup, type ToolbarGroupProps, type ToolbarProps, badgeClass, cx, dotClass, ensurePanelPrimitivesInjected, gallery, iconButtonClass, nextSegmentValue, rowClass, sectionClass, usePanelPrimitives };
|