@nurix/etna 0.20.37 → 0.20.39
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/data/catalog/rules/frontend.md +5 -5
- package/data/catalog/rules/pnpm.md +2 -2
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/agents/brainstorm.md +5 -3
- package/data/harnesses/code/codex/agents/brainstorm.toml +5 -3
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/hooks/approved-packages.md +1 -2
- package/data/harnesses/code/rules/frontend.md +5 -5
- package/data/harnesses/code/rules/pnpm.md +2 -2
- package/data/harnesses/code/skills/boundary/references/packs/build-config-runtime.md +1 -1
- package/data/harnesses/code/skills/brainstorm/README.md +18 -45
- package/data/harnesses/code/skills/brainstorm/SKILL.md +105 -173
- package/data/harnesses/code/skills/brainstorm/references/doctrine.md +70 -141
- package/data/harnesses/code/skills/brainstorm/references/entry.md +70 -0
- package/data/harnesses/code/skills/brainstorm/references/harvest.md +85 -137
- package/data/harnesses/code/skills/brainstorm/references/session-schema.md +98 -135
- package/data/harnesses/code/skills/design/components/charts.md +121 -43
- package/data/harnesses/code/skills/design/components/collections.md +209 -41
- package/data/harnesses/code/skills/design/components/feedback.md +86 -25
- package/data/harnesses/code/skills/design/components/hooks.md +198 -12
- package/data/harnesses/code/skills/design/components/index.md +93 -71
- package/data/harnesses/code/skills/design/components/inputs.md +249 -157
- package/data/harnesses/code/skills/design/components/layout.md +98 -38
- package/data/harnesses/code/skills/design/components/media.md +46 -25
- package/data/harnesses/code/skills/design/components/motion.md +56 -46
- package/data/harnesses/code/skills/design/components/navigation.md +54 -15
- package/data/harnesses/code/skills/design/components/overlays.md +51 -41
- package/data/harnesses/code/skills/design/components/setup.md +84 -5
- package/data/harnesses/code/skills/design/components/typography.md +72 -22
- package/data/harnesses/code/skills/design/components/utils.md +47 -13
- package/data/harnesses/code/skills/design/data/chrome/board-column.md +12 -12
- package/data/harnesses/code/skills/design/data/chrome/data-table.md +6 -6
- package/data/harnesses/code/skills/design/data/chrome/side-panel.md +15 -15
- package/data/harnesses/code/skills/design/data/chrome/sidebar.md +25 -25
- package/data/harnesses/code/skills/design/data/chrome/stat-cards.md +22 -22
- package/data/harnesses/code/skills/design/data/chrome/topbar.md +12 -12
- package/data/harnesses/code/skills/design/references/emission.md +6 -6
- package/data/harnesses/code/skills/design/scripts/README-validator.md +20 -8
- package/data/harnesses/code/skills/design/scripts/validate-design-md.mjs +15 -10
- package/data/harnesses/delivery/agents/brainstorm.md +5 -3
- package/data/harnesses/delivery/codex/agents/brainstorm.toml +5 -3
- package/data/harnesses/delivery/harness.json +2 -2
- package/data/harnesses/delivery/skills/brainstorm/README.md +18 -45
- package/data/harnesses/delivery/skills/brainstorm/SKILL.md +105 -173
- package/data/harnesses/delivery/skills/brainstorm/references/doctrine.md +70 -141
- package/data/harnesses/delivery/skills/brainstorm/references/entry.md +70 -0
- package/data/harnesses/delivery/skills/brainstorm/references/harvest.md +85 -137
- package/data/harnesses/delivery/skills/brainstorm/references/session-schema.md +98 -135
- package/data/harnesses/etna/agents/brainstorm.md +5 -3
- package/data/harnesses/etna/codex/agents/brainstorm.toml +5 -3
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/boundary/references/packs/build-config-runtime.md +1 -1
- package/data/harnesses/etna/skills/brainstorm/README.md +18 -45
- package/data/harnesses/etna/skills/brainstorm/SKILL.md +105 -173
- package/data/harnesses/etna/skills/brainstorm/references/doctrine.md +70 -141
- package/data/harnesses/etna/skills/brainstorm/references/entry.md +70 -0
- package/data/harnesses/etna/skills/brainstorm/references/harvest.md +85 -137
- package/data/harnesses/etna/skills/brainstorm/references/session-schema.md +98 -135
- package/data/harnesses/hr/harness.json +1 -1
- package/package.json +2 -2
|
@@ -4,79 +4,94 @@
|
|
|
4
4
|
|
|
5
5
|
## Shared Props
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Every Control takes one value vocabulary, so a host (a `Cell`, a field grid, a table cell) binds any of them the same way:
|
|
8
8
|
|
|
9
9
|
| Prop | Type | Description |
|
|
10
10
|
| --- | --- | --- |
|
|
11
|
-
| `value` | `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `readOnly` | `boolean` |
|
|
16
|
-
| `
|
|
11
|
+
| `value` / `defaultValue` | `T` | Controlled value or uncontrolled seed; binary Controls use `checked` (Checkbox, Switch) or `pressed` (Toggle) |
|
|
12
|
+
| `onValueChange` | `(value, details) => void` | Every change, value first; `details` is Base UI's event details, and `details.cancel()` vetoes a discrete change |
|
|
13
|
+
| `onValueCommitted` | `(value) => void` | The one commit channel: once per commit, never for an unchanged value. Typed fields commit on Enter (Cmd/Ctrl+Enter when multi-line) or blur and revert on Escape; discrete Controls commit in the same tick as `onValueChange` |
|
|
14
|
+
| `validate` | `(value) => string \| null \| Promise<string \| null>` | A message refuses the commit, keeps the draft and sets `aria-invalid` |
|
|
15
|
+
| `disabled`, `readOnly`, `required`, `name` | `boolean`, `string` | Field state; `required` removes the automatic Clear on Select, DatePicker and IconPicker, `name` submits through a hidden input |
|
|
16
|
+
| `variant="bare"`, `size` | `bare`, `xs\|sm\|default\|lg` | `bare` drops the frame, focus ring and invalid paint a surface underneath already draws; a host supplies both through host context |
|
|
17
|
+
|
|
18
|
+
Each value-holding Control also exports a `format` static (`Input.format`, `Select.format`, `DatePicker.format` …) that writes the at-rest text a host shows.
|
|
17
19
|
|
|
18
20
|
## Cell
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
Form-field host: label, control, addons, helper and error in one block, with field state set once on the root (`@nurix/components/compound/cell`, plus `useCellContext`).
|
|
23
|
+
|
|
24
|
+
**Props:** `mode` [default|inline], `size` [xs|sm|default|lg], `orientation` [vertical|horizontal], `disabled`, `readOnly`, `required`, `invalid`
|
|
21
25
|
|
|
22
|
-
**
|
|
26
|
+
**Sub-components:** `Cell.Label`, `Cell.Content`, `Cell.Field` (`type` [text|textarea|number|checkbox|switch|rating|slider|select], `value`, `onValueCommitted`, `options`, `format`), `Cell.Prefix`, `Cell.Suffix`, `Cell.Stepper`, `Cell.Helper`, `Cell.Error`
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
`Cell.Error` renders only while the root's `invalid` is true; `Cell.Stepper` drives a hosted NumberInput; `disabled` makes the whole `Cell.Content` inert, Prefix/Suffix buttons included. `mode="inline"` shows text-like types as text until clicked, while marks (checkbox, switch, rating) stay live in place.
|
|
25
29
|
|
|
26
30
|
```tsx
|
|
27
|
-
<Cell
|
|
28
|
-
<Cell.Label>
|
|
31
|
+
<Cell required invalid={!!seatsError}>
|
|
32
|
+
<Cell.Label>Seats</Cell.Label>
|
|
29
33
|
<Cell.Content>
|
|
30
|
-
<
|
|
34
|
+
<Cell.Field type="number" value={seats} onValueCommitted={setSeats} options={{ min: 0, max: 20 }} />
|
|
35
|
+
<Cell.Suffix>seats</Cell.Suffix>
|
|
36
|
+
<Cell.Stepper />
|
|
31
37
|
</Cell.Content>
|
|
32
|
-
<Cell.Helper>
|
|
38
|
+
<Cell.Helper>Whole seats, 0 to 20</Cell.Helper>
|
|
39
|
+
<Cell.Error>{seatsError}</Cell.Error>
|
|
33
40
|
</Cell>
|
|
34
41
|
```
|
|
35
42
|
|
|
36
43
|
## Input
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
Single-line text field; `onValueCommitted` turns on the local draft, Enter/blur commits, Escape reverts (`@nurix/components/element/input`).
|
|
46
|
+
|
|
47
|
+
**Props:** `onValueChange`, `onValueCommitted`, `validate`, `prefix`, `suffix` (decorative glyphs only), `size`, `variant` [default|bare]
|
|
39
48
|
|
|
40
|
-
|
|
49
|
+
Without `onValueCommitted`, `validate` never runs and a controlled `value` reads straight through. Labels, hints and interactive affixes belong to `Cell`.
|
|
41
50
|
|
|
42
51
|
```tsx
|
|
43
52
|
<Input
|
|
44
53
|
value={name}
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
onValueChange={setName}
|
|
55
|
+
onValueCommitted={saveName}
|
|
56
|
+
validate={(v) => (v.trim() ? null : "Name is required")}
|
|
47
57
|
placeholder="Enter name"
|
|
48
58
|
/>
|
|
49
59
|
```
|
|
50
60
|
|
|
51
61
|
## NumberInput
|
|
52
62
|
|
|
53
|
-
|
|
63
|
+
Typed, bounded number; commits on Enter, blur and each arrow or step press, Escape reverts (`@nurix/components/element/number-input`).
|
|
54
64
|
|
|
55
|
-
**Props:** `min`, `max`, `step`, `
|
|
65
|
+
**Props:** `min`, `max`, `step`, `format` (Intl options), `locale`, `onValueCommitted`, `validate`, `size`, `variant` [default|bare]
|
|
66
|
+
|
|
67
|
+
An empty field commits `null`, not `0`. No negatives is `min={0}`; whole numbers are `format={{ maximumFractionDigits: 0 }}`. Never `Input type="number"`.
|
|
56
68
|
|
|
57
69
|
```tsx
|
|
58
|
-
<NumberInput value={count}
|
|
70
|
+
<NumberInput value={count} onValueCommitted={setCount} min={0} max={100} step={1} />
|
|
59
71
|
```
|
|
60
72
|
|
|
61
73
|
## Textarea
|
|
62
74
|
|
|
63
|
-
Multi-line
|
|
75
|
+
Multi-line field; commits on Cmd/Ctrl+Enter or blur (bare Enter is a newline), Escape reverts (`@nurix/components/element/textarea`).
|
|
64
76
|
|
|
65
|
-
**Props:** `
|
|
77
|
+
**Props:** `onValueChange`, `onValueCommitted`, `validate`, `rows` (default 3), `autosize` [boolean|{ maxRows }], `variant` [default|bare]
|
|
66
78
|
|
|
67
79
|
```tsx
|
|
68
80
|
<Textarea
|
|
69
81
|
value={description}
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
onValueChange={setDescription}
|
|
83
|
+
onValueCommitted={saveDescription}
|
|
84
|
+
autosize={{ maxRows: 8 }}
|
|
72
85
|
/>
|
|
73
86
|
```
|
|
74
87
|
|
|
75
88
|
## Checkbox
|
|
76
89
|
|
|
77
|
-
Boolean
|
|
90
|
+
Boolean box on Base UI `Checkbox.Root` (`@nurix/components/element/checkbox`).
|
|
91
|
+
|
|
92
|
+
**Props:** `checked`, `defaultChecked`, `onCheckedChange`, `onValueCommitted`, `indeterminate`, `size` [sm|default], `variant` [default|bare]
|
|
78
93
|
|
|
79
|
-
|
|
94
|
+
`indeterminate` is the caller's to clear, and while set the box emits only `data-indeterminate`, never `data-checked`. `value` is the string a form posts, not the checked state.
|
|
80
95
|
|
|
81
96
|
```tsx
|
|
82
97
|
<Checkbox checked={agreed} onCheckedChange={setAgreed} />
|
|
@@ -84,244 +99,321 @@ Boolean toggle supporting both `value`/`onChange` and `checked`/`onCheckedChange
|
|
|
84
99
|
|
|
85
100
|
## Switch
|
|
86
101
|
|
|
87
|
-
|
|
102
|
+
On/off setting that applies the moment it flips; ships no label (`@nurix/components/element/switch`).
|
|
88
103
|
|
|
89
|
-
**Props:** `checked`, `
|
|
104
|
+
**Props:** `checked`, `defaultChecked`, `onCheckedChange`, `onValueCommitted`, `size` [sm|default], `variant` [default|bare]
|
|
105
|
+
|
|
106
|
+
There is no `onChange`, and `value` is the submitted string, never a boolean. Name it with `aria-label` or wrap it in `Cell`.
|
|
90
107
|
|
|
91
108
|
```tsx
|
|
92
|
-
<Switch checked={enabled} onCheckedChange={setEnabled} />
|
|
109
|
+
<Switch checked={enabled} onCheckedChange={setEnabled} aria-label="Notifications" />
|
|
93
110
|
```
|
|
94
111
|
|
|
95
112
|
## Select
|
|
96
113
|
|
|
97
|
-
|
|
114
|
+
Closed listbox picking one string value, with no query field (`@nurix/components/element/select`).
|
|
115
|
+
|
|
116
|
+
**Props:** `items` (required; `{ value: label }` map or `{ value, label }[]`), `value`, `defaultValue`, `onValueChange`, `onValueCommitted`, `validate`, `required`, `variant` [default|bare], `labels` (`clear`)
|
|
98
117
|
|
|
99
|
-
**
|
|
118
|
+
**Sub-components:** `Select.Trigger` (`size` [xs|sm|default|lg]), `Select.Value` (`placeholder`), `Select.Popup` (`side`, `align`, `sideOffset`, `alignItemWithTrigger`), `Select.Group`, `Select.Label`, `Select.Item`, `Select.Separator`
|
|
100
119
|
|
|
101
|
-
|
|
120
|
+
Values are strings only. A non-required Select with a value shows a Clear row that commits `null`. Search, async loading, multi-select or a long list is `Combobox`.
|
|
102
121
|
|
|
103
122
|
```tsx
|
|
104
|
-
<Select
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
>
|
|
109
|
-
<Select.Trigger display="tags" />
|
|
110
|
-
<Select.Content searchable>
|
|
123
|
+
<Select items={statusItems} value={status} onValueChange={setStatus}>
|
|
124
|
+
<Select.Trigger>
|
|
125
|
+
<Select.Value placeholder="Status" />
|
|
126
|
+
</Select.Trigger>
|
|
127
|
+
<Select.Popup>
|
|
111
128
|
<Select.Item value="active">Active</Select.Item>
|
|
112
|
-
|
|
129
|
+
<Select.Item value="paused">Paused</Select.Item>
|
|
130
|
+
</Select.Popup>
|
|
113
131
|
</Select>
|
|
114
132
|
```
|
|
115
133
|
|
|
116
|
-
##
|
|
134
|
+
## Combobox
|
|
135
|
+
|
|
136
|
+
Searchable option picker: single or multi, client or remote filtering, paging (`@nurix/components/compound/combobox`, plus `useComboboxContext`).
|
|
117
137
|
|
|
118
|
-
|
|
138
|
+
**Props:** `options` (`{ value, label }[]`), `value`, `onValueChange(value, { option })`, `onValueCommitted`, `multiple`, `max`, `isClearable`, `searchValue`, `onSearchValueChange`, `loading`, `hasMore`, `loadingMore`, `onLoadMore`, `selectionIndicator` [checkbox|check-end|bg-only], `variant` [default|bare], `labels`
|
|
119
139
|
|
|
120
|
-
**
|
|
140
|
+
**Sub-components:** `Combobox.Trigger` (`display` [tags|comma|count], `size` [sm|default]), `Combobox.Popup` (`maxHeight`), `Combobox.Search`, `Combobox.List`, `Combobox.Item`, `Combobox.Loading`, `Combobox.Empty`, `Combobox.SelectAll`
|
|
141
|
+
|
|
142
|
+
Holding the query (`onSearchValueChange`) turns the built-in filter off. Multi mode commits once on close, and Escape reverts. The placeholder never names the trigger, so pass `aria-label`.
|
|
121
143
|
|
|
122
144
|
```tsx
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
145
|
+
<Combobox options={owners} value={owner} onValueChange={setOwner}>
|
|
146
|
+
<Combobox.Trigger aria-label="Owner" />
|
|
147
|
+
<Combobox.Popup>
|
|
148
|
+
<Combobox.Search />
|
|
149
|
+
<Combobox.List>
|
|
150
|
+
{owners.map((o) => <Combobox.Item key={o.value} value={o.value}>{o.label}</Combobox.Item>)}
|
|
151
|
+
</Combobox.List>
|
|
152
|
+
<Combobox.Empty />
|
|
153
|
+
</Combobox.Popup>
|
|
154
|
+
</Combobox>
|
|
126
155
|
```
|
|
127
156
|
|
|
128
157
|
## DatePicker
|
|
129
158
|
|
|
130
|
-
Date
|
|
159
|
+
Date field with a calendar popover; the value is a `Date`, an ISO string or `null` (`@nurix/components/compound/date-picker`).
|
|
160
|
+
|
|
161
|
+
**Props:** `onValueChange`, `onValueCommitted`, `validate`, `placeholder`, `formatPattern`, `isRelative`, `hasIcon`, `hasPresets`, `presets`, `captionLayout` [label|dropdown|dropdown-months|dropdown-years], `numberOfMonths`, `showOutsideDays`, `min`, `max`, `weekStartsOn`, `required`, `name`, `size`, `variant` [default|bare]
|
|
131
162
|
|
|
132
|
-
|
|
163
|
+
A non-required picker shows a Clear action in its popup. `name` submits local `yyyy-MM-dd`. `DatePicker.format(value)` is the at-rest text.
|
|
133
164
|
|
|
134
165
|
```tsx
|
|
135
166
|
<DatePicker
|
|
136
167
|
value={date}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
placeholder="Pick a date"
|
|
168
|
+
onValueChange={setDate}
|
|
169
|
+
onValueCommitted={commitDate}
|
|
170
|
+
placeholder="Choose date"
|
|
141
171
|
/>
|
|
142
172
|
```
|
|
143
173
|
|
|
174
|
+
## DatePicker.Range
|
|
175
|
+
|
|
176
|
+
Date-window picker on the same namespace; the value is `{ from: Date, to: Date } | null` (`DateRangeValue`).
|
|
177
|
+
|
|
178
|
+
**Props:** the DatePicker props, with `presets` as range presets (`DateRangePreset[]`)
|
|
179
|
+
|
|
180
|
+
A window commits on the second grid press; closing after one press drops it without a commit. `DatePicker.Range.format(value)` is the at-rest text.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<DatePicker.Range value={range} onValueChange={setRange} onValueCommitted={commitRange} hasPresets />
|
|
184
|
+
```
|
|
185
|
+
|
|
144
186
|
## Slider
|
|
145
187
|
|
|
146
|
-
Range
|
|
188
|
+
Range control for one number between `min` and `max` (`@nurix/components/element/slider`).
|
|
189
|
+
|
|
190
|
+
**Props:** `min`, `max`, `step`, `onValueChange`, `onValueCommitted`, `hasValueReadout`, `hasBoundsReadout`, `format` (Intl options), `locale`, `inputRef`, `variant` [default|bare]
|
|
147
191
|
|
|
148
|
-
|
|
192
|
+
`value` is always a single number. `onValueCommitted` fires once per release or key press that changes the value, and Escape reverts an uncommitted drag.
|
|
149
193
|
|
|
150
194
|
```tsx
|
|
151
|
-
<Slider
|
|
152
|
-
value={volume}
|
|
153
|
-
onChange={setVolume}
|
|
154
|
-
onSave={handleSave}
|
|
155
|
-
min={0}
|
|
156
|
-
max={100}
|
|
157
|
-
/>
|
|
195
|
+
<Slider value={volume} onValueChange={setVolume} onValueCommitted={saveVolume} min={0} max={100} aria-label="Volume" />
|
|
158
196
|
```
|
|
159
197
|
|
|
160
198
|
## Rating
|
|
161
199
|
|
|
162
|
-
Icon
|
|
200
|
+
Icon score picker (`@nurix/components/element/rating`, plus `RATING_ICONS`).
|
|
201
|
+
|
|
202
|
+
**Props:** `max`, `step` [1|0.5], `size` [xs|sm|default|lg], `icon` [Star|Heart|ThumbsUp|Circle|Flame|Zap|Trophy|Diamond], `hasValueText`, `onValueCommitted`, `validate`, `name`, `variant` [default|bare], `labels`
|
|
163
203
|
|
|
164
|
-
|
|
204
|
+
A controlled `value` moves no star until the parent updates it. No click brings a set score below one `step`, so clearing means setting `value` to `0`. It ships no group name.
|
|
165
205
|
|
|
166
206
|
```tsx
|
|
167
|
-
<Rating
|
|
168
|
-
value={rating}
|
|
169
|
-
onChange={setRating}
|
|
170
|
-
onSave={handleSave}
|
|
171
|
-
maxRating={5}
|
|
172
|
-
allowHalf
|
|
173
|
-
/>
|
|
207
|
+
<Rating value={score} onValueChange={setScore} max={5} step={0.5} icon="Heart" aria-label="Score" />
|
|
174
208
|
```
|
|
175
209
|
|
|
176
210
|
## Button
|
|
177
211
|
|
|
178
|
-
|
|
212
|
+
Action button; the element changes through `render`, never by wrapping (`@nurix/components/element/button`, plus `buttonVariants`).
|
|
213
|
+
|
|
214
|
+
**Props:** `variant` [default|secondary|outline|ghost|link], `intent` [neutral|danger], `size` [xs|sm|default|lg], `isIconOnly`, `loading`, `pressed`, `focusableWhenDisabled`, `render`, `labels` (`pending`)
|
|
179
215
|
|
|
180
|
-
|
|
216
|
+
An irreversible action takes `intent="danger"` on any variant. An icon-only button adds `isIconOnly` plus an `aria-label`. `loading` keeps focus and width; `pressed` is a controlled latch rendering `aria-pressed`. Inside a host, omit `size`.
|
|
181
217
|
|
|
182
218
|
```tsx
|
|
183
|
-
<Button variant="outline"
|
|
184
|
-
|
|
219
|
+
<Button variant="outline" intent="danger" loading={isDeleting} onClick={handleDelete}>
|
|
220
|
+
Delete
|
|
185
221
|
</Button>
|
|
186
222
|
```
|
|
187
223
|
|
|
188
224
|
## Calendar
|
|
189
225
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
**Props:** `mode` [single|multiple|range], `selected`, `onSelect`, `captionLayout` [label|dropdown|dropdown-months|dropdown-years], `numberOfMonths`, `showOutsideDays`, `buttonVariant`
|
|
193
|
-
|
|
194
|
-
```tsx
|
|
195
|
-
<Calendar
|
|
196
|
-
mode="single"
|
|
197
|
-
selected={date}
|
|
198
|
-
onSelect={setDate}
|
|
199
|
-
captionLayout="dropdown"
|
|
200
|
-
/>
|
|
201
|
-
```
|
|
226
|
+
Inline month grid on `react-day-picker` (`@nurix/components/compound/calendar`); a form field wants `DatePicker` instead.
|
|
202
227
|
|
|
203
|
-
|
|
228
|
+
**Props:** `value`, `onValueChange`, `onValueCommitted`, `multiple`, `min`, `max`, `disabledDates` (react-day-picker matchers), `disabled`, `readOnly`, `required`, `name`, `validate`, `captionLayout`, `numberOfMonths`
|
|
204
229
|
|
|
205
|
-
|
|
230
|
+
**Sub-components:** `Calendar.Range` (value `{ from, to? } | null`, `excludeDisabled`, `resetOnSelect`), `Calendar.DayButton`
|
|
206
231
|
|
|
207
|
-
|
|
232
|
+
Re-pressing the selected day clears to `null` unless `required`. `Calendar.Range` commits only a complete or cleared range. Returned dates are local midnight.
|
|
208
233
|
|
|
209
234
|
```tsx
|
|
210
|
-
<
|
|
211
|
-
value={range}
|
|
212
|
-
onChange={setRange}
|
|
213
|
-
onSave={handleSave}
|
|
214
|
-
showPresets
|
|
215
|
-
/>
|
|
235
|
+
<Calendar value={date} onValueChange={setDate} captionLayout="dropdown" min={today} />
|
|
216
236
|
```
|
|
217
237
|
|
|
218
238
|
## IconPicker
|
|
219
239
|
|
|
220
|
-
Searchable
|
|
240
|
+
Searchable lucide icon picker popover over caller-loaded records (`@nurix/components/compound/icon-picker`; optional peers `@tanstack/react-virtual` and `fuse.js`).
|
|
221
241
|
|
|
222
|
-
**Props:** `
|
|
242
|
+
**Props:** `data` (required; `iconsData` from `lib/icons-data`, loaded lazily), `loading`, `value` (`IconName | null`, kebab-case), `onValueChange`, `onValueCommitted`, `isSearchable`, `isCategorized`, `modal`, `render`, `required`, `size`, `variant` [default|bare], `labels`
|
|
243
|
+
|
|
244
|
+
A non-required picker carries an automatic Clear. Draw the pick with `Icon` from `element/icon`.
|
|
223
245
|
|
|
224
246
|
```tsx
|
|
225
|
-
<IconPicker value={
|
|
247
|
+
<IconPicker data={icons} loading={isLoading} value={icon} onValueChange={setIcon} />
|
|
226
248
|
```
|
|
227
249
|
|
|
228
250
|
## InputOTP
|
|
229
251
|
|
|
230
|
-
|
|
252
|
+
Segmented one-time-code field on Base UI's OTP field (`@nurix/components/compound/input-otp`).
|
|
253
|
+
|
|
254
|
+
**Props:** `length` (must equal the slot count), `value`, `onValueChange`, `onValueCommitted` (fires when the last slot fills), `status` [pending|error|success], `mask`, `autoSubmit`, `labels`
|
|
231
255
|
|
|
232
|
-
**
|
|
256
|
+
**Sub-components:** `InputOTP.Group`, `InputOTP.Slot`, `InputOTP.Separator`, `InputOTP.Check`
|
|
233
257
|
|
|
234
|
-
|
|
258
|
+
`status` is the caller's verdict: the field never decides a code is wrong.
|
|
235
259
|
|
|
236
260
|
```tsx
|
|
237
|
-
<InputOTP
|
|
238
|
-
<
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<InputOTPSlot index={3} />
|
|
243
|
-
</InputOTPGroup>
|
|
261
|
+
<InputOTP length={4} value={code} onValueChange={setCode} onValueCommitted={verify} status={status}>
|
|
262
|
+
<InputOTP.Group>
|
|
263
|
+
<InputOTP.Slot /><InputOTP.Slot /><InputOTP.Slot /><InputOTP.Slot />
|
|
264
|
+
</InputOTP.Group>
|
|
265
|
+
<InputOTP.Check />
|
|
244
266
|
</InputOTP>
|
|
245
267
|
```
|
|
246
268
|
|
|
247
|
-
##
|
|
269
|
+
## FilePicker
|
|
248
270
|
|
|
249
|
-
|
|
271
|
+
Acquires and stages local files through a dropzone and a picker button (`@nurix/components/compound/file-picker`, plus `useFilePicker`).
|
|
250
272
|
|
|
251
|
-
**Props:** `
|
|
273
|
+
**Props:** `value` (`File[]`), `onValueChange`, `onValueCommitted`, `multiple`, `accept`, `maxFiles`, `maxSize`, `validate(file)`, `onFileReject`, `onUpload`, `readOnly`, `labels`
|
|
252
274
|
|
|
253
|
-
|
|
254
|
-
<MediaFilePicker value={files} onChange={setFiles} mode="multi" maxFiles={5} />
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
## NativeSelect
|
|
258
|
-
|
|
259
|
-
Radix-based select primitive family on the `native-select` subpath (distinct from the compound `Select` above); exported symbols are named `Select*`.
|
|
260
|
-
|
|
261
|
-
**Props:** `value`, `onValueChange`, `defaultValue`, `disabled` (root passes through all Radix Select props)
|
|
275
|
+
**Sub-components:** `FilePicker.Dropzone`, `FilePicker.Trigger`, `FilePicker.List`, `FilePicker.Item` (`value` = the File), `FilePicker.ItemPreview`, `FilePicker.ItemMetadata`, `FilePicker.ItemProgress`, `FilePicker.ItemDelete`, `FilePicker.Clear`
|
|
262
276
|
|
|
263
|
-
|
|
277
|
+
The dropzone is a non-interactive region, so it always holds a `FilePicker.Trigger`. Supply `onUpload` only when the product owns a transport. An existing attachment is `FilePreview` (`compound/file-preview`).
|
|
264
278
|
|
|
265
279
|
```tsx
|
|
266
|
-
<
|
|
267
|
-
<
|
|
268
|
-
<
|
|
269
|
-
</
|
|
270
|
-
<
|
|
271
|
-
<
|
|
272
|
-
</
|
|
273
|
-
</
|
|
280
|
+
<FilePicker value={files} onValueChange={setFiles} multiple maxFiles={5}>
|
|
281
|
+
<FilePicker.Dropzone>
|
|
282
|
+
<FilePicker.Trigger>Choose files</FilePicker.Trigger>
|
|
283
|
+
</FilePicker.Dropzone>
|
|
284
|
+
<FilePicker.List>
|
|
285
|
+
{files.map((file) => <FilePicker.Item key={file.name} value={file}><FilePicker.ItemMetadata /><FilePicker.ItemDelete>Remove</FilePicker.ItemDelete></FilePicker.Item>)}
|
|
286
|
+
</FilePicker.List>
|
|
287
|
+
</FilePicker>
|
|
274
288
|
```
|
|
275
289
|
|
|
276
290
|
## RadioGroup
|
|
277
291
|
|
|
278
|
-
|
|
292
|
+
Small set of mutually exclusive options, as radio marks or whole-card radios (`@nurix/components/element/radio-group`).
|
|
293
|
+
|
|
294
|
+
**Props:** `value`, `defaultValue`, `onValueChange`, `onValueCommitted`, `name` (on the root, never an item), `orientation` [vertical|horizontal], `variant` [default|bare]
|
|
279
295
|
|
|
280
|
-
**
|
|
296
|
+
**Sub-components:** `RadioGroup.Item` (`size` [sm|default]), `RadioGroup.Card` (`variant` [default|list]), `RadioGroup.Header`, `RadioGroup.Title`, `RadioGroup.Description`, `RadioGroup.Indicator`
|
|
281
297
|
|
|
282
|
-
|
|
298
|
+
An `Item`'s `id` lands on its hidden input, so aim the `Label`'s `htmlFor` (`element/label`) at it. The segmented control is `ToggleGroup`.
|
|
283
299
|
|
|
284
300
|
```tsx
|
|
285
|
-
<RadioGroup value={plan} onValueChange={setPlan}>
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
</
|
|
290
|
-
<RadioGroupCard>
|
|
291
|
-
<RadioGroupItem value="pro" />
|
|
292
|
-
<RadioGroupLabel clickable>Pro</RadioGroupLabel>
|
|
293
|
-
</RadioGroupCard>
|
|
301
|
+
<RadioGroup value={plan} onValueChange={setPlan} aria-label="Plan">
|
|
302
|
+
<RadioGroup.Item value="free" id="plan-free" />
|
|
303
|
+
<Label htmlFor="plan-free">Free</Label>
|
|
304
|
+
<RadioGroup.Item value="pro" id="plan-pro" />
|
|
305
|
+
<Label htmlFor="plan-pro">Pro</Label>
|
|
294
306
|
</RadioGroup>
|
|
295
307
|
```
|
|
296
308
|
|
|
297
309
|
## Toggle
|
|
298
310
|
|
|
299
|
-
|
|
311
|
+
Single control that holds its own on/off state (`@nurix/components/element/toggle`, plus `toggleVariants`).
|
|
312
|
+
|
|
313
|
+
**Props:** `pressed`, `defaultPressed`, `onPressedChange`, `onValueCommitted`, `variant` [ghost|outline|default|bare] (default `ghost`), `size` [sm|default|lg], `isIconOnly`
|
|
300
314
|
|
|
301
|
-
|
|
315
|
+
`value` and `onChange` are not boolean aliases. Style off `data-pressed`.
|
|
302
316
|
|
|
303
317
|
```tsx
|
|
304
|
-
<Toggle pressed={isBold} onPressedChange={setIsBold}
|
|
305
|
-
|
|
318
|
+
<Toggle pressed={isBold} onPressedChange={setIsBold} isIconOnly aria-label="Toggle bold">
|
|
319
|
+
<Bold />
|
|
306
320
|
</Toggle>
|
|
307
321
|
```
|
|
308
322
|
|
|
309
323
|
## ToggleGroup
|
|
310
324
|
|
|
311
|
-
|
|
325
|
+
Strip of toggles picking one value, or several with `multiple` (`@nurix/components/compound/toggle-group`).
|
|
312
326
|
|
|
313
|
-
**Props:** `
|
|
327
|
+
**Props:** `multiple`, `value`, `defaultValue`, `onValueChange`, `onValueCommitted`, `variant` [ghost|outline|default|bare], `size` [sm|default|lg], `orientation`
|
|
314
328
|
|
|
315
|
-
**Sub-components:** `
|
|
329
|
+
**Sub-components:** `ToggleGroup.Item` (takes no `variant` or `size`; both cascade from the group)
|
|
330
|
+
|
|
331
|
+
Single mode publishes `""` when the pressed item is toggled off, so guard it when a selection is mandatory.
|
|
316
332
|
|
|
317
333
|
```tsx
|
|
318
|
-
<ToggleGroup
|
|
319
|
-
|
|
320
|
-
value=
|
|
321
|
-
onValueChange={setAlign}
|
|
322
|
-
variant="outline"
|
|
323
|
-
>
|
|
324
|
-
<ToggleGroupItem value="left">Left</ToggleGroupItem>
|
|
325
|
-
<ToggleGroupItem value="right">Right</ToggleGroupItem>
|
|
334
|
+
<ToggleGroup variant="outline" value={view} onValueChange={(next) => next && setView(next)}>
|
|
335
|
+
<ToggleGroup.Item value="table">Table</ToggleGroup.Item>
|
|
336
|
+
<ToggleGroup.Item value="card">Card</ToggleGroup.Item>
|
|
326
337
|
</ToggleGroup>
|
|
327
338
|
```
|
|
339
|
+
|
|
340
|
+
## ThemeToggle
|
|
341
|
+
|
|
342
|
+
Light/dark toggle (`@nurix/components/element/theme-toggle`) with a circular view-transition reveal from the button; it is the library `Toggle`, with `pressed` meaning dark.
|
|
343
|
+
|
|
344
|
+
**Props:** `pressed` + `onPressedChange` (pass both when the app owns theme state; omitted, it reads and writes the `.dark` class on `document.documentElement`), `onValueCommitted`, `labels` (`{ name }`, default "Dark theme"), plus Toggle's `variant` [ghost|outline|default|bare] and `size` [sm|default|lg]. It persists nothing — store the choice yourself; the reveal is timed by the motion tokens (the `pace` trait), with no `duration` prop.
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
<ThemeToggle pressed={theme === "dark"} onPressedChange={(isDark) => setTheme(isDark ? "dark" : "light")} />
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## InlineEdit
|
|
351
|
+
|
|
352
|
+
Click-to-edit single-line text: one `<span contentEditable>` that reads as text until focused (`@nurix/components/element/inline-edit`).
|
|
353
|
+
|
|
354
|
+
**Props:** `value`, `defaultValue`, `placeholder`, `onSave`, `validator` (RegExp), `onValidationError`, `hideHoverEffect`, `readOnly`
|
|
355
|
+
|
|
356
|
+
Enter or blur commits, Escape reverts, and unchanged text skips `onSave`. A labelled field that edits in place is `Cell mode="inline"`.
|
|
357
|
+
|
|
358
|
+
```tsx
|
|
359
|
+
<InlineEdit value={title} onSave={setTitle} placeholder="Click to add a title" />
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Reaction
|
|
363
|
+
|
|
364
|
+
Strip of toggleable emoji chips with counts (`@nurix/components/compound/reaction`, plus `reactionChipVariants`).
|
|
365
|
+
|
|
366
|
+
**Props:** `data` (`{ emoji, count? }[]`, sets order and counts), `value` (pressed emoji), `onValueChange(next, { emoji, pressed })`, `onValueCommitted`, `maxVisible`, `size` [xs|sm], `readOnly`, `variant` [default|bare], `labels`
|
|
367
|
+
|
|
368
|
+
**Sub-components:** `Reaction.Chip`, `Reaction.Add`
|
|
369
|
+
|
|
370
|
+
Counts render exactly as `data` gives them, so an optimistic UI steps the count itself. `Reaction.Add` is an empty shell, so give it the glyph.
|
|
371
|
+
|
|
372
|
+
```tsx
|
|
373
|
+
<Reaction data={counts} value={pressed} onValueChange={(next, { emoji, pressed: isOn }) => { setPressed(next); write(emoji, isOn); }} aria-label="Reactions">
|
|
374
|
+
<Reaction.Add aria-label="Add reaction">
|
|
375
|
+
<SmilePlus className="size-4" />
|
|
376
|
+
</Reaction.Add>
|
|
377
|
+
</Reaction>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## ActionCluster
|
|
381
|
+
|
|
382
|
+
Record-header action row: up to four inline buttons, pinned actions after a divider, the rest in a "More actions" dropdown (`@nurix/components/compound/action-cluster`).
|
|
383
|
+
|
|
384
|
+
**Props:** `actions` (`ClusterAction<TData>[]`: `label`, `id`, `icon`, `variant`, `intent`, `pinned`, `disabled`, `loading`, `onClick(data, event)`), `data` (required), `size` (default `sm`), `labels` (`group`, `more`)
|
|
385
|
+
|
|
386
|
+
A plain row of buttons is `Row` with `Button`s; hover-revealed icon actions are `ActionBar`.
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
<ActionCluster
|
|
390
|
+
data={account}
|
|
391
|
+
actions={[
|
|
392
|
+
{ label: "Edit", icon: <Pencil />, onClick: openEditor },
|
|
393
|
+
{ label: "Delete", intent: "danger", pinned: true, onClick: confirmDelete },
|
|
394
|
+
]}
|
|
395
|
+
/>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## TiptapEditor
|
|
399
|
+
|
|
400
|
+
Rich-text editor, markdown in and markdown out (`@nurix/components/block/tiptap-editor`, plus `NurixTiptapKit`, `getMarkdown`, `blockMarkdown`; import `@nurix/components/tiptap-editor.css` once; optional peers `@tiptap/*` and `tiptap-markdown`).
|
|
401
|
+
|
|
402
|
+
**Props:** `value`, `defaultValue`, `onValueChange`, `onValueCommitted` (Cmd/Ctrl+Enter or blur), `validate`, `readOnly`, `disabled`, `name`, `extensions`, `toolbar`, `tableControls`, `variant` [default|bare], `labels`
|
|
403
|
+
|
|
404
|
+
**Sub-components:** `TiptapEditor.Toolbar`, `TiptapEditor.TableControls`, `TiptapEditor.TableGridPicker`
|
|
405
|
+
|
|
406
|
+
A controlled `value` is safe, because the editor ignores an echo of its own last emission. Read markdown through `getMarkdown(editor)`, never `editor.storage.markdown`.
|
|
407
|
+
|
|
408
|
+
```tsx
|
|
409
|
+
<div className="h-96">
|
|
410
|
+
<TiptapEditor defaultValue={doc} onValueCommitted={saveNotes} aria-label="Notes" />
|
|
411
|
+
</div>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Fallback-only concepts
|
|
415
|
+
|
|
416
|
+
No nurix primitive — build each from its plain-spec fallback in [index.md](index.md):
|
|
417
|
+
|
|
418
|
+
- `form-validation`
|
|
419
|
+
- `code-editor`
|