@phreshos/react-ui 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +197 -39
- package/dist/accordion.d.ts +45 -0
- package/dist/accordion.js +62 -0
- package/dist/alert-dialog.d.ts +0 -1
- package/dist/alert-dialog.js +0 -1
- package/dist/aria-direction.d.ts +9 -0
- package/dist/aria-direction.js +49 -0
- package/dist/calendar-layout.d.ts +8 -0
- package/dist/calendar-layout.js +103 -0
- package/dist/calendar-trigger.d.ts +5 -0
- package/dist/calendar-trigger.js +30 -0
- package/dist/calendar.d.ts +18 -0
- package/dist/calendar.js +13 -0
- package/dist/color.d.ts +16 -1
- package/dist/color.js +60 -11
- package/dist/combo-box.d.ts +20 -0
- package/dist/combo-box.js +68 -0
- package/dist/context-menu.d.ts +2 -4
- package/dist/context-menu.js +0 -1
- package/dist/control-surface.d.ts +2 -1
- package/dist/control-surface.js +2 -2
- package/dist/control.d.ts +1 -1
- package/dist/control.js +7 -8
- package/dist/date-control.d.ts +24 -0
- package/dist/date-control.js +69 -0
- package/dist/date-field.d.ts +19 -0
- package/dist/date-field.js +13 -0
- package/dist/date-picker.d.ts +19 -0
- package/dist/date-picker.js +16 -0
- package/dist/date-range-picker.d.ts +20 -0
- package/dist/date-range-picker.js +25 -0
- package/dist/date-range.d.ts +6 -0
- package/dist/date-range.js +0 -0
- package/dist/dialog.d.ts +4 -2
- package/dist/dialog.js +24 -7
- package/dist/disclosure.d.ts +42 -0
- package/dist/disclosure.js +97 -0
- package/dist/dropdown-menu.d.ts +0 -2
- package/dist/dropdown-menu.js +0 -1
- package/dist/list-box.d.ts +49 -0
- package/dist/list-box.js +129 -0
- package/dist/main.d.ts +27 -11
- package/dist/main.js +26 -10
- package/dist/material-options.d.ts +4 -2
- package/dist/material-paint.js +19 -1
- package/dist/menu.d.ts +22 -4
- package/dist/menu.js +25 -6
- package/dist/panel.d.ts +0 -1
- package/dist/panel.js +0 -1
- package/dist/popover.d.ts +0 -1
- package/dist/popover.js +13 -3
- package/dist/progress-bar.d.ts +22 -0
- package/dist/progress-bar.js +46 -0
- package/dist/radio.d.ts +5 -5
- package/dist/radio.js +8 -4
- package/dist/radius.js +1 -1
- package/dist/range-calendar.d.ts +19 -0
- package/dist/range-calendar.js +15 -0
- package/dist/scroll-area.d.ts +4 -1
- package/dist/scroll-area.js +3 -3
- package/dist/select.js +4 -14
- package/dist/selection.d.ts +18 -0
- package/dist/selection.js +28 -0
- package/dist/slider.js +1 -1
- package/dist/spacing.js +1 -1
- package/dist/surface.d.ts +1 -1
- package/dist/surface.js +22 -3
- package/dist/table.d.ts +78 -0
- package/dist/table.js +148 -0
- package/dist/tabs.d.ts +45 -0
- package/dist/tabs.js +103 -0
- package/dist/time-field.d.ts +22 -0
- package/dist/time-field.js +13 -0
- package/dist/toggle-indicator.js +1 -1
- package/dist/toolbar.d.ts +46 -0
- package/dist/toolbar.js +121 -0
- package/dist/tooltip.d.ts +0 -1
- package/dist/tooltip.js +0 -1
- package/dist/tree.d.ts +56 -0
- package/dist/tree.js +134 -0
- package/dist/window.d.ts +39 -0
- package/dist/{window-header.js → window.js} +57 -31
- package/package.json +4 -2
- package/dist/window-header.d.ts +0 -42
package/README.md
CHANGED
|
@@ -74,14 +74,20 @@ function Example() {
|
|
|
74
74
|
}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
`Button`, `Input`, `Textarea`, `Select`, `Checkbox`, `Switch`, and `
|
|
77
|
+
`Button`, `Input`, `Textarea`, `Select`, `Checkbox`, `Switch`, and `RadioGroup.Item`
|
|
78
78
|
use the shared Surface implementation while retaining their native behavior. Color and material
|
|
79
79
|
are independent inputs. `color` accepts an Appearance color and resting level such
|
|
80
80
|
as `background:base` or `primary:soft`, or a direct CSS color. Surface defaults
|
|
81
81
|
to `background:base`; solid controls default to `default:base`.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
Every independently owned base paint is exposed through its component's
|
|
83
|
+
`color` prop. Hover, press, focus, selection, and inactive treatments derive
|
|
84
|
+
from that base rather than introducing separate color inputs. Surface and solid controls compare
|
|
85
|
+
the fill with an adaptive perceptual boundary biased toward lighter text, then
|
|
86
|
+
use the Appearance candidate on the opposite side; interaction shades keep that
|
|
87
|
+
choice.
|
|
88
|
+
The omitted value follows the component's meaning: neutral controls use
|
|
89
|
+
`default:base`, while a role with inherent visual meaning may choose another
|
|
90
|
+
Appearance color. Every semantic default remains overridable through `color`.
|
|
85
91
|
`size` accepts `xsmall`, `small`, `medium` (default), `large`, or `xlarge`;
|
|
86
92
|
spacing and radius follow Appearance, while typography remains inherited and
|
|
87
93
|
component sizes are relative to the surrounding text. `disabled` prevents activation and focus;
|
|
@@ -115,11 +121,18 @@ required geometry.
|
|
|
115
121
|
`MaterialOptions` defines `opacity`, `backdrop`, `grain`, `grainAmount`,
|
|
116
122
|
`distortion`, and `saturation`. Every material-bearing component, including
|
|
117
123
|
Surface, exposes these values through its `material` prop. Color remains a
|
|
118
|
-
separate property. Omission
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
opacity
|
|
122
|
-
|
|
124
|
+
separate property. Omission uses the resource-efficient `basic` rendering mode.
|
|
125
|
+
`none` ignores Material and paints the resolved color as a normal background;
|
|
126
|
+
`basic` adds Material paint, grain, and its edge while remaining solid, with
|
|
127
|
+
Material opacity applied only to the grain; `extended` applies that opacity to
|
|
128
|
+
the complete Material instead; and `full` adds backdrop, distortion, and
|
|
129
|
+
saturation. A `MaterialOptions` object selects full rendering with those
|
|
130
|
+
overrides. Effect options accept a scale level or a direct number; opacity
|
|
131
|
+
affects Surface paint only, never its content. The material edge is part of the
|
|
132
|
+
same Surface rather than a second public entity.
|
|
133
|
+
|
|
134
|
+
The modes are progressive: each level preserves the rendering of the previous
|
|
135
|
+
one and adds only its own Material capability.
|
|
123
136
|
|
|
124
137
|
Surface-based controls expose the same separate `color` and `material` props.
|
|
125
138
|
`ShadowOptions` similarly groups `x`, `y`, `blur`, `spread`, and `opacity` under
|
|
@@ -128,12 +141,13 @@ branch, while `false` removes the shadow. Each option accepts a scale level or
|
|
|
128
141
|
direct number. The shadow is a neutral black
|
|
129
142
|
outer shadow. Surface-based controls expose the same `shadow` prop.
|
|
130
143
|
For text fields and Select, `material` targets the field or trigger; for Checkbox,
|
|
131
|
-
Switch, and
|
|
132
|
-
to its options, and
|
|
144
|
+
Switch, and RadioGroup.Item, it targets the indicator. RadioGroup supplies material defaults
|
|
145
|
+
to its options, and an Item can override them. Shadow follows the same targets and
|
|
133
146
|
inheritance path.
|
|
134
147
|
|
|
135
148
|
```tsx
|
|
136
|
-
<Button color="primary:base" material=
|
|
149
|
+
<Button color="primary:base" material="full">Save</Button>
|
|
150
|
+
<Surface material={{ opacity: 0.6, backdrop: 0 }}>Translucent</Surface>
|
|
137
151
|
<Input label="Name" radius="large" material={{ grain: "small" }} shadow={{ blur: "small" }} />
|
|
138
152
|
```
|
|
139
153
|
|
|
@@ -141,6 +155,11 @@ inheritance path.
|
|
|
141
155
|
`Surface`. Both materials retain Surface defaults; the content inset follows
|
|
142
156
|
Appearance spacing. Positioning, modality, and lifecycle belong to the caller.
|
|
143
157
|
|
|
158
|
+
Component families use one public path: the family name is the root and parts
|
|
159
|
+
that depend on it are properties of that family. For example, use
|
|
160
|
+
`Panel.Header` and `Dialog.Title`; those parts are not separate top-level
|
|
161
|
+
components. Components with independent contracts remain top-level exports.
|
|
162
|
+
|
|
144
163
|
```tsx
|
|
145
164
|
import { Panel } from "@phreshos/react-ui"
|
|
146
165
|
|
|
@@ -154,23 +173,29 @@ Native properties and the root ref target the outer Surface. `Panel.Header`
|
|
|
154
173
|
owns the optional leading region, while `Panel.Content` is the independently
|
|
155
174
|
configurable inner Surface.
|
|
156
175
|
|
|
157
|
-
`
|
|
158
|
-
|
|
159
|
-
optional flexible space, and
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
176
|
+
`Window` provides one Surface with a ready header-and-content layout.
|
|
177
|
+
`Window.Header.Identity` presents the icon and truncating title,
|
|
178
|
+
`Window.Header.Center` is optional flexible space, and
|
|
179
|
+
`Window.Header.Actions` aligns compact controls at the end. `Window.Content`
|
|
180
|
+
fills the remaining area without deciding its overflow behavior. The header
|
|
181
|
+
uses the enclosing Surface rather than creating another material layer, and it
|
|
182
|
+
can also be composed independently inside any Surface. Its controls are React
|
|
183
|
+
UI Buttons; callers connect them to their own window operations. Pointer and
|
|
184
|
+
double-click handlers on the header can implement a drag area, while the center
|
|
185
|
+
and actions keep their interactions separate.
|
|
164
186
|
|
|
165
187
|
```tsx
|
|
166
|
-
<
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
</
|
|
188
|
+
<Window>
|
|
189
|
+
<Window.Header active={active} onPointerDown={beginDrag}>
|
|
190
|
+
<Window.Header.Identity icon={icon} title={title} />
|
|
191
|
+
<Window.Header.Actions>
|
|
192
|
+
<Window.Header.Minimize onPress={minimize} />
|
|
193
|
+
<Window.Header.Maximize maximized={maximized} onPress={toggleMaximize} />
|
|
194
|
+
<Window.Header.Close onPress={close} />
|
|
195
|
+
</Window.Header.Actions>
|
|
196
|
+
</Window.Header>
|
|
197
|
+
<Window.Content>{children}</Window.Content>
|
|
198
|
+
</Window>
|
|
174
199
|
```
|
|
175
200
|
|
|
176
201
|
## Overlays
|
|
@@ -193,6 +218,9 @@ semantic content remains explicit.
|
|
|
193
218
|
|
|
194
219
|
`Popover` presents anchored non-modal content. `DropdownMenu` and
|
|
195
220
|
`ContextMenu` open the same `Menu` contract through different interactions.
|
|
221
|
+
Menu is selection-free by default. Selectable menus use string identities
|
|
222
|
+
through `value`, `defaultValue`, `onChange`, and `disabledValues`, matching the
|
|
223
|
+
other selectable collections; root-level command dispatch uses `onItemAction`.
|
|
196
224
|
`Dialog` blocks interaction behind it and may be dismissable, while
|
|
197
225
|
`AlertDialog` requires an explicit decision by default. `Tooltip` supplies a
|
|
198
226
|
short description on focus or hover. Portal cleanup, focus restoration,
|
|
@@ -205,8 +233,8 @@ Every input uses Appearance colors and the same five `size` levels as Button.
|
|
|
205
233
|
`color` selects the fill independently from material. Input, Textarea, Select,
|
|
206
234
|
and Button use `default:base` when it is omitted. Invalid
|
|
207
235
|
fields use danger instead. Text fields share Surface's glass edge. Interaction shades
|
|
208
|
-
derive from the base color's lightness, and text or selection marks use
|
|
209
|
-
|
|
236
|
+
derive from the base color's lightness, and text or selection marks use the Appearance
|
|
237
|
+
background or foreground that is perceptually opposite the base fill.
|
|
210
238
|
That text or mark color stays unchanged across interaction shades.
|
|
211
239
|
These are component-owned derivations, not additional Appearance settings.
|
|
212
240
|
Theme names never imply particular colors. No component requires a Client or
|
|
@@ -231,10 +259,19 @@ behavior are never delayed by visual animation.
|
|
|
231
259
|
| --- | --- | --- |
|
|
232
260
|
| `Input` | `value`, `defaultValue`, `onChange(string)` | Single-line text; supports text, email, password, search, URL, and telephone types |
|
|
233
261
|
| `Textarea` | `value`, `defaultValue`, `onChange(string)` | Multiline text; four rows by default, vertically resizable |
|
|
262
|
+
| `DateField` | `CalendarDate \| null` through `value`, `defaultValue`, and `onChange` | A locale-aware date whose year, month, and day are edited as independent segments |
|
|
263
|
+
| `TimeField` | `Time \| null` through `value`, `defaultValue`, and `onChange` | A locale-aware clock time whose visible units are edited as independent segments |
|
|
264
|
+
| `Calendar` | `CalendarDate \| null` selection | A keyboard-navigable month view for choosing one date |
|
|
265
|
+
| `RangeCalendar` | `DateRange \| null` selection | A month view for choosing an inclusive start and end date |
|
|
266
|
+
| `DatePicker` | The same `CalendarDate \| null` contract as DateField | Segmented date entry with a calendar selection popover |
|
|
267
|
+
| `DateRangePicker` | The same `DateRange \| null` contract as RangeCalendar | Two segmented date fields with a shared range calendar popover |
|
|
234
268
|
| `Checkbox` | `checked`, `defaultChecked`, `onChange(boolean)` | Independent selection; `indeterminate` represents a mixed state |
|
|
235
269
|
| `Switch` | `checked`, `defaultChecked`, `onChange(boolean)` | An on/off setting |
|
|
236
|
-
| `RadioGroup` / `
|
|
270
|
+
| `RadioGroup` / `RadioGroup.Item` | Group `value`, `defaultValue`, `onChange(string)`; Item `value` | One exclusive choice; vertical by default, optionally horizontal |
|
|
237
271
|
| `Select` | `value: string \| null`, `defaultValue`, `onChange(string \| null)` | One choice from `options: { value, label, disabled? }[]` |
|
|
272
|
+
| `ComboBox` | Selected `value: string \| null` and query `inputValue: string` | One searchable choice from `options: { value, label, disabled? }[]` |
|
|
273
|
+
| `ListBox` | Single string or multiple string-array `value`, `defaultValue`, and `onChange` | A visible selectable collection with composable items and sections |
|
|
274
|
+
| `Tree` | Optional selection plus controlled or uncontrolled string-array expansion | Nested hierarchical navigation with arbitrary branch depth |
|
|
238
275
|
| `Slider` | `value`, `defaultValue`, `onChange(number)` | One numeric value; `minValue`, `maxValue`, `step`, and `onChangeEnd`; horizontal by default |
|
|
239
276
|
|
|
240
277
|
Use `label` for visible labels, or `aria-label` / `aria-labelledby` for an
|
|
@@ -243,44 +280,165 @@ accessible name without visible text. `description` provides associated help.
|
|
|
243
280
|
`name` participates in native form submission. Controlled values remain owned
|
|
244
281
|
by the caller; omit them and use defaults for internal state and form reset.
|
|
245
282
|
|
|
246
|
-
Text fields, Checkbox, Switch, and RadioGroup also support `readOnly`: the
|
|
283
|
+
Text fields, ComboBox, Checkbox, Switch, and RadioGroup also support `readOnly`: the
|
|
247
284
|
value cannot change, but the control remains focusable. These fields and
|
|
248
285
|
Select support `required`, `invalid`, `errorMessage`, and React Aria's native
|
|
249
|
-
or ARIA validation behavior. A
|
|
286
|
+
or ARIA validation behavior. A RadioGroup Item's selection and validation belong to its
|
|
250
287
|
group. Slider represents a bounded number rather than a required text or
|
|
251
288
|
choice field, and has no read-only or validation-error mode. Select has no
|
|
252
289
|
read-only mode; disable it when selection must be unavailable.
|
|
253
290
|
|
|
254
|
-
`Input`, `Textarea`,
|
|
255
|
-
|
|
291
|
+
`Input`, `Textarea`, `DateField`, `TimeField`, `DatePicker`, `DateRangePicker`,
|
|
292
|
+
`Calendar`, `RangeCalendar`, `Select`, and `ComboBox` accept `radius`. Other inputs
|
|
293
|
+
retain their intrinsic indicator shapes. `className` and `style` address the field's root;
|
|
256
294
|
default visual properties remain component-owned. Input and Textarea refs
|
|
257
295
|
target their native text controls; other refs target the root div. Checkbox,
|
|
258
|
-
Switch, and
|
|
296
|
+
Switch, and RadioGroup.Item additionally accept `inputRef` for their native input.
|
|
259
297
|
|
|
260
298
|
```tsx
|
|
261
|
-
import { Input, Textarea,
|
|
299
|
+
import { Input, Textarea, DateField, TimeField, Calendar, RangeCalendar, DatePicker, DateRangePicker, Checkbox, RadioGroup, Switch, Select, ComboBox, ListBox, Tree, Slider } from "@phreshos/react-ui"
|
|
300
|
+
import { parseDate, parseTime } from "@internationalized/date"
|
|
262
301
|
|
|
263
302
|
<Input label="Name" name="name" required />
|
|
264
303
|
<Textarea label="Description" name="description" />
|
|
304
|
+
<DateField label="Due date" name="dueDate" defaultValue={parseDate("2026-09-21")} />
|
|
305
|
+
<TimeField label="Start time" name="startTime" defaultValue={parseTime("09:30")} />
|
|
306
|
+
<DatePicker label="Appointment" name="appointment" defaultValue={parseDate("2026-09-21")} />
|
|
307
|
+
<Calendar aria-label="Release date" defaultValue={parseDate("2026-09-21")} />
|
|
308
|
+
<RangeCalendar aria-label="Trip dates" defaultValue={{ start: parseDate("2026-09-21"), end: parseDate("2026-09-24") }} />
|
|
309
|
+
<DateRangePicker label="Trip dates" defaultValue={{ start: parseDate("2026-09-21"), end: parseDate("2026-09-24") }} />
|
|
265
310
|
<Checkbox label="Remember this choice" name="remember" />
|
|
266
311
|
<Switch label="Notifications" name="notifications" defaultChecked />
|
|
267
312
|
<RadioGroup label="Layout" name="layout" defaultValue="grid">
|
|
268
|
-
<
|
|
269
|
-
<
|
|
313
|
+
<RadioGroup.Item label="Grid" value="grid" />
|
|
314
|
+
<RadioGroup.Item label="List" value="list" />
|
|
270
315
|
</RadioGroup>
|
|
271
316
|
<Select label="Sort" name="sort" options={[
|
|
272
317
|
{ value: "name", label: "Name" },
|
|
273
318
|
{ value: "date", label: "Date" }
|
|
274
319
|
]} />
|
|
320
|
+
<ComboBox label="Region" name="region" options={[
|
|
321
|
+
{ value: "eu", label: "Europe" },
|
|
322
|
+
{ value: "us", label: "United States" }
|
|
323
|
+
]} />
|
|
324
|
+
<ListBox aria-label="Programs" defaultValue="editor">
|
|
325
|
+
<ListBox.Section id="productivity">
|
|
326
|
+
<ListBox.Header>Productivity</ListBox.Header>
|
|
327
|
+
<ListBox.Item id="editor">Editor</ListBox.Item>
|
|
328
|
+
<ListBox.Item id="terminal">Terminal</ListBox.Item>
|
|
329
|
+
</ListBox.Section>
|
|
330
|
+
</ListBox>
|
|
331
|
+
<Tree aria-label="Project files" defaultExpanded={["source"]}>
|
|
332
|
+
<Tree.Item id="source" textValue="Source">
|
|
333
|
+
<Tree.Content>Source</Tree.Content>
|
|
334
|
+
<Tree.Item id="main" textValue="main.ts">
|
|
335
|
+
<Tree.Content>main.ts</Tree.Content>
|
|
336
|
+
</Tree.Item>
|
|
337
|
+
</Tree.Item>
|
|
338
|
+
</Tree>
|
|
275
339
|
<Slider label="Volume" name="volume" defaultValue={50} minValue={0} maxValue={100} step={1} />
|
|
276
340
|
```
|
|
277
341
|
|
|
342
|
+
`ComboBox` filters option labels as the user types. Selection uses `value` and
|
|
343
|
+
`onChange`; its query is independently controllable through `inputValue` and
|
|
344
|
+
`onInputChange`. Use `defaultValue` and `defaultInputValue` for uncontrolled
|
|
345
|
+
state.
|
|
346
|
+
|
|
347
|
+
`ListBox` uses single selection by default and accepts `selectionMode="multiple"`
|
|
348
|
+
for a string array or `"all"`. Its Items require stable string identities.
|
|
349
|
+
Sections group options under Headers; they do not introduce expandable hierarchy.
|
|
350
|
+
ListBox owns collection layout, selection, focus, keyboard behavior, and item
|
|
351
|
+
presentation. An enclosing Surface or ScrollArea remains an explicit consumer
|
|
352
|
+
composition.
|
|
353
|
+
|
|
354
|
+
`Tree` represents hierarchy rather than grouping. Branches may contain other
|
|
355
|
+
branches to any depth. Expansion uses `expanded`, `defaultExpanded`, and
|
|
356
|
+
`onExpandedChange`, all expressed as string identities. Selection is disabled
|
|
357
|
+
by default and uses the same explicit single- or multiple-selection contract as
|
|
358
|
+
Table when enabled. `Tree.Content` supplies each row's visible content and its
|
|
359
|
+
accessible disclosure control; `Tree.Collection` recursively renders
|
|
360
|
+
data-backed child collections.
|
|
361
|
+
|
|
278
362
|
Each control delegates focus, keyboard, form, and selection behavior to its
|
|
279
363
|
accessible behavioral primitive rather than recreating those systems. Select's
|
|
280
|
-
popup
|
|
364
|
+
popup and ComboBox's searchable popup use Surface defaults. Toggle indicators use Motion internally and respect
|
|
281
365
|
animation preferences. The preview Program demonstrates each input's
|
|
282
366
|
sizes, colors, state, and interaction without overriding its visual defaults.
|
|
283
367
|
|
|
368
|
+
## Tabs
|
|
369
|
+
|
|
370
|
+
`Tabs` organizes related peer views under one selected string identity. Its
|
|
371
|
+
horizontal list divides the available width equally, while its active indicator
|
|
372
|
+
moves continuously between selections.
|
|
373
|
+
|
|
374
|
+
```tsx
|
|
375
|
+
<Tabs defaultValue="overview">
|
|
376
|
+
<Tabs.List aria-label="Project views">
|
|
377
|
+
<Tabs.Tab id="overview">Overview</Tabs.Tab>
|
|
378
|
+
<Tabs.Tab id="activity">Activity</Tabs.Tab>
|
|
379
|
+
</Tabs.List>
|
|
380
|
+
<Tabs.Panel id="overview">Project overview</Tabs.Panel>
|
|
381
|
+
<Tabs.Panel id="activity">Recent activity</Tabs.Panel>
|
|
382
|
+
</Tabs>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Each `Tabs.Tab` identity must match one `Tabs.Panel`. Use `Tabs.Panels` when
|
|
386
|
+
the panels come from a dynamic collection; static panels may remain direct
|
|
387
|
+
children of `Tabs`.
|
|
388
|
+
|
|
389
|
+
## Table
|
|
390
|
+
|
|
391
|
+
`Table` presents structured rows and columns with accessible navigation. Row
|
|
392
|
+
selection uses the same string-value contract as ListBox, while sorting reports
|
|
393
|
+
the requested column and direction without taking ownership of the data order.
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
<Table
|
|
397
|
+
aria-label="Processes"
|
|
398
|
+
selectionMode="multiple"
|
|
399
|
+
value={selectedProcesses}
|
|
400
|
+
onChange={setSelectedProcesses}
|
|
401
|
+
sort={sort}
|
|
402
|
+
onSortChange={setSort}
|
|
403
|
+
>
|
|
404
|
+
<Table.Header>
|
|
405
|
+
<Table.Column id="name" isRowHeader allowsSorting>Name</Table.Column>
|
|
406
|
+
<Table.Column id="state" allowsSorting>State</Table.Column>
|
|
407
|
+
</Table.Header>
|
|
408
|
+
<Table.Body>
|
|
409
|
+
<Table.Row id="editor">
|
|
410
|
+
<Table.Cell>Editor</Table.Cell>
|
|
411
|
+
<Table.Cell>Running</Table.Cell>
|
|
412
|
+
</Table.Row>
|
|
413
|
+
</Table.Body>
|
|
414
|
+
</Table>
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Selection is disabled by default. Set `selectionMode` to `"single"` or
|
|
418
|
+
`"multiple"` to enable it. Sortable columns emit `{ column, direction }`
|
|
419
|
+
through `onSortChange`; reorder the rows in application state and pass the
|
|
420
|
+
descriptor back through `sort`. `Table.Body` supports static rows, dynamic
|
|
421
|
+
`items`, and `renderEmptyState`.
|
|
422
|
+
|
|
423
|
+
Table owns tabular behavior and row presentation without adding a material
|
|
424
|
+
container or scrolling boundary. Compose Surface and ScrollArea around it when
|
|
425
|
+
the surrounding interface needs either one.
|
|
426
|
+
|
|
427
|
+
## ProgressBar
|
|
428
|
+
|
|
429
|
+
`ProgressBar` communicates the progress of an operation without becoming an
|
|
430
|
+
input. Determinate progress exposes its current value and percentage;
|
|
431
|
+
indeterminate progress omits the current value and presents ongoing activity.
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
<ProgressBar label="Uploading" value={64} />
|
|
435
|
+
<ProgressBar aria-label="Connecting" indeterminate />
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
The default range is `0` through `100`. `formatOptions` controls the generated
|
|
439
|
+
value text, while `valueLabel` supplies an explicit visible and accessible
|
|
440
|
+
value. Color, size, typography, and motion follow the active React UI contracts.
|
|
441
|
+
|
|
284
442
|
## Development
|
|
285
443
|
|
|
286
444
|
```sh
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CSSProperties, ReactElement, ReactNode } from "react";
|
|
2
|
+
import type { DisclosureGroupProps as AriaDisclosureGroupProps } from "react-aria-components";
|
|
3
|
+
import type { ControlColor } from "./control.js";
|
|
4
|
+
import { DisclosureContent, DisclosureTrigger, type DisclosureRootProps } from "./disclosure.js";
|
|
5
|
+
import { type LayoutGap } from "./layout.js";
|
|
6
|
+
import type { RadiusProps } from "./radius.js";
|
|
7
|
+
import type { ScaleLevel } from "./scale.js";
|
|
8
|
+
type AccordionRootBaseProps = Omit<AriaDisclosureGroupProps, "allowsMultipleExpanded" | "className" | "defaultExpandedKeys" | "expandedKeys" | "isDisabled" | "onExpandedChange" | "style"> & RadiusProps & Readonly<{
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
color?: ControlColor;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
gap?: LayoutGap;
|
|
14
|
+
size?: ScaleLevel;
|
|
15
|
+
style?: CSSProperties;
|
|
16
|
+
}>;
|
|
17
|
+
export type AccordionSingleExpansionProps = Readonly<{
|
|
18
|
+
multiple?: false;
|
|
19
|
+
value?: string | null;
|
|
20
|
+
defaultValue?: string | null;
|
|
21
|
+
onChange?: (value: string | null) => void;
|
|
22
|
+
}>;
|
|
23
|
+
export type AccordionMultipleExpansionProps = Readonly<{
|
|
24
|
+
multiple: true;
|
|
25
|
+
value?: readonly string[];
|
|
26
|
+
defaultValue?: readonly string[];
|
|
27
|
+
onChange?: (value: readonly string[]) => void;
|
|
28
|
+
}>;
|
|
29
|
+
export type AccordionRootProps = AccordionRootBaseProps & (AccordionSingleExpansionProps | AccordionMultipleExpansionProps);
|
|
30
|
+
/** A coordinated group of identified Disclosure items. */
|
|
31
|
+
export declare const AccordionRoot: import("react").ForwardRefExoticComponent<AccordionRootProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
32
|
+
export interface AccordionItemProps extends Omit<DisclosureRootProps, "defaultExpanded" | "expanded" | "onChange"> {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
}
|
|
35
|
+
/** One identified Disclosure governed by its nearest Accordion. */
|
|
36
|
+
export declare const AccordionItem: import("react").ForwardRefExoticComponent<AccordionItemProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
37
|
+
interface AccordionComponent {
|
|
38
|
+
(properties: AccordionRootProps): ReactElement | null;
|
|
39
|
+
readonly Item: typeof AccordionItem;
|
|
40
|
+
readonly Trigger: typeof DisclosureTrigger;
|
|
41
|
+
readonly Content: typeof DisclosureContent;
|
|
42
|
+
}
|
|
43
|
+
export declare const Accordion: AccordionComponent;
|
|
44
|
+
export type AccordionProps = AccordionRootProps;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, forwardRef, useContext, useMemo } from "react";
|
|
3
|
+
import { DisclosureGroup as AriaDisclosureGroup } from "react-aria-components";
|
|
4
|
+
import { DisclosureContent, DisclosureRoot, DisclosureTrigger } from "./disclosure.js";
|
|
5
|
+
import { resolveGap } from "./layout.js";
|
|
6
|
+
import { stringKeys } from "./selection.js";
|
|
7
|
+
import { useAppearance } from "./ui-provider.js";
|
|
8
|
+
const AccordionThemeContext = createContext(null);
|
|
9
|
+
/** A coordinated group of identified Disclosure items. */
|
|
10
|
+
export const AccordionRoot = forwardRef(function AccordionRoot(properties, ref) {
|
|
11
|
+
const { children, className, color, defaultValue, disabled = false, gap = "xsmall", multiple = false, onChange, radius = "medium", size = "medium", style, value, ...native } = properties;
|
|
12
|
+
const appearance = useAppearance();
|
|
13
|
+
const context = useMemo(() => ({ color, radius, size }), [color, radius, size]);
|
|
14
|
+
return _jsx(AccordionThemeContext.Provider, { value: context, children: _jsx(AriaDisclosureGroup, { ...native, ...expansionProperties(properties), ref: ref, allowsMultipleExpanded: multiple, className: className, isDisabled: disabled, style: {
|
|
15
|
+
display: "grid",
|
|
16
|
+
gap: resolveGap(gap, appearance),
|
|
17
|
+
width: "100%",
|
|
18
|
+
minWidth: 0,
|
|
19
|
+
...style
|
|
20
|
+
}, children: children }) });
|
|
21
|
+
});
|
|
22
|
+
function expansionProperties(properties) {
|
|
23
|
+
const expandedKeys = expansionKeys(properties.value);
|
|
24
|
+
const defaultExpandedKeys = expansionKeys(properties.defaultValue);
|
|
25
|
+
const onExpandedChange = properties.onChange == null
|
|
26
|
+
? undefined
|
|
27
|
+
: (keys) => {
|
|
28
|
+
const values = stringKeys(keys);
|
|
29
|
+
if (properties.multiple)
|
|
30
|
+
properties.onChange?.(values);
|
|
31
|
+
else
|
|
32
|
+
properties.onChange?.(values[0] ?? null);
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
...(expandedKeys === undefined ? {} : { expandedKeys }),
|
|
36
|
+
...(defaultExpandedKeys === undefined ? {} : { defaultExpandedKeys }),
|
|
37
|
+
...(onExpandedChange === undefined ? {} : { onExpandedChange })
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function expansionKeys(value) {
|
|
41
|
+
if (value === undefined)
|
|
42
|
+
return undefined;
|
|
43
|
+
if (value === null)
|
|
44
|
+
return new Set();
|
|
45
|
+
return new Set(typeof value === "string" ? [value] : value);
|
|
46
|
+
}
|
|
47
|
+
/** One identified Disclosure governed by its nearest Accordion. */
|
|
48
|
+
export const AccordionItem = forwardRef(function AccordionItem({ color, radius, size, ...properties }, ref) {
|
|
49
|
+
const inherited = useAccordionTheme();
|
|
50
|
+
return _jsx(DisclosureRoot, { ...properties, ref: ref, color: color ?? inherited.color, radius: radius ?? inherited.radius, size: size ?? inherited.size });
|
|
51
|
+
});
|
|
52
|
+
function useAccordionTheme() {
|
|
53
|
+
const context = useContext(AccordionThemeContext);
|
|
54
|
+
if (context == null)
|
|
55
|
+
throw new Error("Accordion.Item must be used inside Accordion");
|
|
56
|
+
return context;
|
|
57
|
+
}
|
|
58
|
+
export const Accordion = Object.assign(AccordionRoot, {
|
|
59
|
+
Item: AccordionItem,
|
|
60
|
+
Trigger: DisclosureTrigger,
|
|
61
|
+
Content: DisclosureContent
|
|
62
|
+
});
|
package/dist/alert-dialog.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export type AlertDialogContentProps = Omit<DialogContentProps, "role">;
|
|
|
9
9
|
export declare const AlertDialogContent: import("react").ForwardRefExoticComponent<AlertDialogContentProps & import("react").RefAttributes<HTMLElement>>;
|
|
10
10
|
/** A dialog requiring an explicit decision rather than ambient dismissal. */
|
|
11
11
|
export declare const AlertDialog: typeof AlertDialogRoot & {
|
|
12
|
-
Root: typeof AlertDialogRoot;
|
|
13
12
|
Trigger: import("react").ForwardRefExoticComponent<import("./button.js").ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
14
13
|
Backdrop: import("react").ForwardRefExoticComponent<DialogBackdropProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
15
14
|
Content: import("react").ForwardRefExoticComponent<AlertDialogContentProps & import("react").RefAttributes<HTMLElement>>;
|
package/dist/alert-dialog.js
CHANGED
|
@@ -12,7 +12,6 @@ export const AlertDialogContent = forwardRef(function AlertDialogContent(propert
|
|
|
12
12
|
});
|
|
13
13
|
/** A dialog requiring an explicit decision rather than ambient dismissal. */
|
|
14
14
|
export const AlertDialog = Object.assign(AlertDialogRoot, {
|
|
15
|
-
Root: AlertDialogRoot,
|
|
16
15
|
Trigger: DialogTrigger,
|
|
17
16
|
Backdrop: AlertDialogBackdrop,
|
|
18
17
|
Content: AlertDialogContent,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { KeyboardEventHandler, ReactNode } from "react";
|
|
2
|
+
import type { Direction } from "./direction.js";
|
|
3
|
+
/** Aligns React Aria's directional keyboard behavior with the React UI direction contract. */
|
|
4
|
+
export declare function useAriaDirectionBridge(direction: Direction): KeyboardEventHandler<HTMLElement> | undefined;
|
|
5
|
+
/** Owns the non-layout DOM boundary needed to intercept React Aria keyboard behavior. */
|
|
6
|
+
export declare function AriaDirectionBoundary({ children, direction }: Readonly<{
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
direction: Direction;
|
|
9
|
+
}>): import("react").JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { useLocale } from "react-aria/I18nProvider";
|
|
4
|
+
const ariaDirectionBoundary = "data-phreshos-ui-aria-direction";
|
|
5
|
+
const redirectedEvents = new WeakSet();
|
|
6
|
+
/** Aligns React Aria's directional keyboard behavior with the React UI direction contract. */
|
|
7
|
+
export function useAriaDirectionBridge(direction) {
|
|
8
|
+
const { direction: ariaDirection } = useLocale();
|
|
9
|
+
return useMemo(() => {
|
|
10
|
+
if (direction === ariaDirection)
|
|
11
|
+
return undefined;
|
|
12
|
+
return event => {
|
|
13
|
+
if (redirectedEvents.has(event.nativeEvent))
|
|
14
|
+
return;
|
|
15
|
+
if (event.defaultPrevented || (event.key !== "ArrowLeft" && event.key !== "ArrowRight"))
|
|
16
|
+
return;
|
|
17
|
+
const target = event.target;
|
|
18
|
+
if (!(target instanceof Element) || target.closest(`[${ariaDirectionBoundary}]`) !== event.currentTarget)
|
|
19
|
+
return;
|
|
20
|
+
const editable = target.closest("[contenteditable=true]");
|
|
21
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || (editable && editable.getAttribute("role") !== "spinbutton"))
|
|
22
|
+
return;
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
event.stopPropagation();
|
|
25
|
+
// React Aria currently derives keyboard direction from locale. Redirect only
|
|
26
|
+
// the directional key so language and every other locale behavior stay intact.
|
|
27
|
+
const redirected = new KeyboardEvent(event.nativeEvent.type, {
|
|
28
|
+
key: event.key === "ArrowLeft" ? "ArrowRight" : "ArrowLeft",
|
|
29
|
+
code: event.code,
|
|
30
|
+
location: event.location,
|
|
31
|
+
ctrlKey: event.ctrlKey,
|
|
32
|
+
shiftKey: event.shiftKey,
|
|
33
|
+
altKey: event.altKey,
|
|
34
|
+
metaKey: event.metaKey,
|
|
35
|
+
repeat: event.repeat,
|
|
36
|
+
bubbles: true,
|
|
37
|
+
cancelable: true,
|
|
38
|
+
composed: true
|
|
39
|
+
});
|
|
40
|
+
redirectedEvents.add(redirected);
|
|
41
|
+
target.dispatchEvent(redirected);
|
|
42
|
+
};
|
|
43
|
+
}, [ariaDirection, direction]);
|
|
44
|
+
}
|
|
45
|
+
/** Owns the non-layout DOM boundary needed to intercept React Aria keyboard behavior. */
|
|
46
|
+
export function AriaDirectionBoundary({ children, direction }) {
|
|
47
|
+
const onKeyDownCapture = useAriaDirectionBridge(direction);
|
|
48
|
+
return _jsx("div", { dir: direction, "data-phreshos-ui-aria-direction": "", onKeyDownCapture: onKeyDownCapture, style: { display: "contents" }, children: children });
|
|
49
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import { type ControlProps, type ControlTheme } from "./control.js";
|
|
3
|
+
/** Shared month layout used by both single-date and range calendar roots. */
|
|
4
|
+
export declare function CalendarLayout({ color, range, size, theme }: Pick<ControlProps, "color" | "size"> & Readonly<{
|
|
5
|
+
range?: boolean;
|
|
6
|
+
theme: ControlTheme;
|
|
7
|
+
}>): import("react").JSX.Element;
|
|
8
|
+
export declare function calendarRootStyle(theme: ControlTheme, disabled?: boolean, style?: CSSProperties): CSSProperties;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { CalendarCell, CalendarGrid, CalendarGridBody, CalendarGridHeader, CalendarHeaderCell, CalendarHeading } from "react-aria-components";
|
|
3
|
+
import { Button } from "./button.js";
|
|
4
|
+
import { resolveColorLevel, solidColors } from "./color.js";
|
|
5
|
+
import { controlFontWeight, controlOpacity } from "./control.js";
|
|
6
|
+
import { useDirection } from "./direction.js";
|
|
7
|
+
/** Shared month layout used by both single-date and range calendar roots. */
|
|
8
|
+
export function CalendarLayout({ color, range = false, size, theme }) {
|
|
9
|
+
const direction = useDirection();
|
|
10
|
+
return _jsxs(_Fragment, { children: [_jsxs("header", { style: { display: "grid", gridTemplateColumns: "auto 1fr auto", alignItems: "center", gap: theme.gap }, children: [_jsx(Button, { slot: "previous", color: color, size: size, "aria-label": "Previous month", style: navigationStyle(theme), children: _jsx(Chevron, { direction: direction === "rtl" ? "next" : "previous" }) }), _jsx(CalendarHeading, { style: {
|
|
11
|
+
margin: 0,
|
|
12
|
+
textAlign: "center",
|
|
13
|
+
fontFamily: "inherit",
|
|
14
|
+
fontSize: "inherit",
|
|
15
|
+
fontStyle: "inherit",
|
|
16
|
+
lineHeight: "inherit",
|
|
17
|
+
fontWeight: controlFontWeight
|
|
18
|
+
} }), _jsx(Button, { slot: "next", color: color, size: size, "aria-label": "Next month", style: navigationStyle(theme), children: _jsx(Chevron, { direction: direction === "rtl" ? "previous" : "next" }) })] }), _jsxs(CalendarGrid, { style: {
|
|
19
|
+
width: "max-content",
|
|
20
|
+
margin: 0,
|
|
21
|
+
tableLayout: "fixed",
|
|
22
|
+
border: 0,
|
|
23
|
+
borderRadius: 0,
|
|
24
|
+
borderCollapse: "separate",
|
|
25
|
+
borderSpacing: Math.max(1, theme.gap / 4),
|
|
26
|
+
background: "transparent"
|
|
27
|
+
}, children: [_jsx(CalendarGridHeader, { children: day => _jsx(CalendarHeaderCell, { style: {
|
|
28
|
+
height: theme.height * 0.72,
|
|
29
|
+
padding: 0,
|
|
30
|
+
border: 0,
|
|
31
|
+
background: "transparent",
|
|
32
|
+
textAlign: "center",
|
|
33
|
+
fontFamily: "inherit",
|
|
34
|
+
fontSize: "inherit",
|
|
35
|
+
fontStyle: "inherit",
|
|
36
|
+
lineHeight: "inherit",
|
|
37
|
+
fontWeight: controlFontWeight,
|
|
38
|
+
opacity: controlOpacity.secondary
|
|
39
|
+
}, children: day }) }), _jsx(CalendarGridBody, { children: date => _jsx(CalendarCell, { date: date, style: state => calendarCellStyle(theme, state, range) }) })] })] });
|
|
40
|
+
}
|
|
41
|
+
export function calendarRootStyle(theme, disabled = false, style) {
|
|
42
|
+
return {
|
|
43
|
+
...style,
|
|
44
|
+
display: "grid",
|
|
45
|
+
width: "max-content",
|
|
46
|
+
maxWidth: "100%",
|
|
47
|
+
gap: theme.gap,
|
|
48
|
+
padding: theme.spacing,
|
|
49
|
+
fontFamily: "inherit",
|
|
50
|
+
fontSize: theme.fontSize,
|
|
51
|
+
lineHeight: 1.5,
|
|
52
|
+
opacity: disabled ? controlOpacity.disabled : 1
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function navigationStyle(theme) {
|
|
56
|
+
return {
|
|
57
|
+
width: theme.height,
|
|
58
|
+
paddingInline: 0
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function calendarCellStyle(theme, state, range) {
|
|
62
|
+
const rangeInterior = range && state.isSelected && !state.isSelectionStart && !state.isSelectionEnd
|
|
63
|
+
? rangeInteriorPaint(theme)
|
|
64
|
+
: null;
|
|
65
|
+
const paint = state.isSelected
|
|
66
|
+
? rangeInterior ?? theme.paints.palette.pressed
|
|
67
|
+
: state.isPressed
|
|
68
|
+
? theme.paints.subtle.pressed
|
|
69
|
+
: state.isHovered || state.isFocused
|
|
70
|
+
? theme.paints.subtle.hover
|
|
71
|
+
: null;
|
|
72
|
+
return {
|
|
73
|
+
...theme.transition,
|
|
74
|
+
display: "grid",
|
|
75
|
+
placeItems: "center",
|
|
76
|
+
boxSizing: "border-box",
|
|
77
|
+
width: theme.height,
|
|
78
|
+
height: theme.height,
|
|
79
|
+
borderRadius: theme.radius,
|
|
80
|
+
outline: state.isFocusVisible ? `1px solid ${theme.focusColor}` : "none",
|
|
81
|
+
outlineOffset: -1,
|
|
82
|
+
background: paint?.background ?? "transparent",
|
|
83
|
+
color: paint?.color ?? "inherit",
|
|
84
|
+
opacity: state.isDisabled || state.isUnavailable
|
|
85
|
+
? controlOpacity.disabled
|
|
86
|
+
: state.isOutsideMonth ? controlOpacity.secondary : 1,
|
|
87
|
+
textDecoration: state.isUnavailable ? "line-through" : "none",
|
|
88
|
+
cursor: state.isDisabled || state.isUnavailable ? "not-allowed" : "pointer",
|
|
89
|
+
fontVariantNumeric: "tabular-nums"
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function rangeInteriorPaint(theme) {
|
|
93
|
+
// Range interiors are the same Appearance color at its defined lighter
|
|
94
|
+
// treatment; endpoints retain the stronger selected control paint.
|
|
95
|
+
const background = resolveColorLevel(theme.tint, "soft");
|
|
96
|
+
return {
|
|
97
|
+
background,
|
|
98
|
+
color: solidColors(background, theme.background, theme.foreground).rest.color
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function Chevron({ direction }) {
|
|
102
|
+
return _jsx("svg", { "aria-hidden": "true", width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: _jsx("path", { d: direction === "previous" ? "m7.5 2-4 4 4 4" : "m4.5 2 4 4-4 4" }) });
|
|
103
|
+
}
|