@uniflowed/ui 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accordion.js +22 -9
- package/alert-dialog.js +41 -19
- package/alert.js +14 -8
- package/avatar.js +14 -8
- package/breadcrumb.js +24 -6
- package/calendar.js +54 -10
- package/carousel.js +24 -6
- package/collapsible.js +14 -3
- package/color-picker.js +22 -5
- package/combobox.js +30 -21
- package/context-menu.js +31 -2
- package/date-picker.js +37 -11
- package/date-range-picker.js +25 -8
- package/dialog.js +39 -11
- package/drawer.js +47 -34
- package/field.js +24 -6
- package/hover-card.js +14 -3
- package/index.js +107 -740
- package/input-otp.js +20 -4
- package/interactions.js +0 -1
- package/internal/collection.js +262 -95
- package/internal/focus.js +13 -4
- package/internal/roving-focus.js +12 -3
- package/internal/segmented-field.js +2 -1
- package/internal/selection.js +171 -0
- package/internal/visually-hidden-style.js +41 -0
- package/menu.js +36 -13
- package/menubar.js +32 -5
- package/navigation-menu.js +24 -6
- package/number-field.js +20 -12
- package/package.json +5 -5
- package/pagination.js +22 -5
- package/popover.js +14 -4
- package/radio-group.js +15 -4
- package/range-calendar.js +22 -3
- package/resizable.js +14 -3
- package/scroll-area.js +14 -3
- package/select.js +30 -13
- package/sheet.js +39 -18
- package/sidebar.js +31 -16
- package/skeleton.js +13 -3
- package/slider.js +15 -4
- package/table.js +34 -24
- package/tabs.js +15 -9
- package/toast.js +24 -6
- package/toggle-group.js +14 -3
- package/tooltip.js +21 -5
- package/visually-hidden.js +259 -0
package/color-picker.js
CHANGED
|
@@ -26,7 +26,7 @@ hook useColor(): ColorState {
|
|
|
26
26
|
if (color == null) throw new Error("ColorPicker parts must be inside ColorPicker.Root");
|
|
27
27
|
return color;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
component ColorPickerRoot(
|
|
30
30
|
children: React.Node,
|
|
31
31
|
value?: string,
|
|
32
32
|
defaultValue?: string = "#000000",
|
|
@@ -53,7 +53,7 @@ export component ColorPickerRoot(
|
|
|
53
53
|
</ColorContext.Provider>
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
component ColorPickerInput(render?: RenderProp, ...rest: Rest) {
|
|
57
57
|
const color = useColor();
|
|
58
58
|
const props = withProps(rest, {
|
|
59
59
|
type: "color",
|
|
@@ -65,7 +65,7 @@ export component ColorPickerInput(render?: RenderProp, ...rest: Rest) {
|
|
|
65
65
|
});
|
|
66
66
|
return render != null ? render(props) : <input {...props} />;
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
component ColorPickerField(render?: RenderProp, ...rest: Rest) {
|
|
69
69
|
const color = useColor();
|
|
70
70
|
const [draft, setDraft] = useState<string | null>(null);
|
|
71
71
|
const text = draft ?? color.value;
|
|
@@ -94,7 +94,7 @@ export component ColorPickerField(render?: RenderProp, ...rest: Rest) {
|
|
|
94
94
|
});
|
|
95
95
|
return render != null ? render(props) : <input {...props} />;
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
component ColorPickerChannel(
|
|
98
98
|
channel: "red" | "green" | "blue" | "alpha",
|
|
99
99
|
render?: RenderProp,
|
|
100
100
|
...rest: Rest
|
|
@@ -161,7 +161,7 @@ export component ColorPickerChannel(
|
|
|
161
161
|
});
|
|
162
162
|
return render != null ? render(props) : <input {...props} />;
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
component ColorPickerSwatch(render?: RenderProp, ...rest: Rest) {
|
|
165
165
|
const color = useColor();
|
|
166
166
|
const props = withProps(rest, {
|
|
167
167
|
"aria-hidden": "true",
|
|
@@ -170,3 +170,20 @@ export component ColorPickerSwatch(render?: RenderProp, ...rest: Rest) {
|
|
|
170
170
|
});
|
|
171
171
|
return render != null ? render(props) : <span {...props} />;
|
|
172
172
|
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The parts, under the names the `ColorPicker` namespace gives them.
|
|
176
|
+
*
|
|
177
|
+
* `index.js` re-exports this module whole — `export * as ColorPicker from "./color-picker.js"` —
|
|
178
|
+
* so a caller writes `<ColorPicker.Root>`, and the namespace is the prefix. Each
|
|
179
|
+
* part is still *declared* as `ColorPickerRoot`, so React DevTools, a component
|
|
180
|
+
* stack and an error name the part a reader can find rather than one of forty
|
|
181
|
+
* `Root`s.
|
|
182
|
+
*/
|
|
183
|
+
export {
|
|
184
|
+
ColorPickerRoot as Root,
|
|
185
|
+
ColorPickerInput as Input,
|
|
186
|
+
ColorPickerField as Field,
|
|
187
|
+
ColorPickerChannel as Channel,
|
|
188
|
+
ColorPickerSwatch as Swatch,
|
|
189
|
+
};
|
package/combobox.js
CHANGED
|
@@ -210,7 +210,7 @@ const ComboboxGroupContext: React.Context<ComboboxGroupState | null> = createCon
|
|
|
210
210
|
* `internal/form-value.js` says why it is an `<input>` and why
|
|
211
211
|
* `@uniflowed/form` does not need it.
|
|
212
212
|
*/
|
|
213
|
-
|
|
213
|
+
component ComboboxRoot(
|
|
214
214
|
children: React.Node,
|
|
215
215
|
value?: string | null,
|
|
216
216
|
defaultValue?: string | null = null,
|
|
@@ -294,7 +294,7 @@ export component ComboboxRoot(
|
|
|
294
294
|
* because the list names it, and naming a label that is not rendered is worse
|
|
295
295
|
* than leaving the list unnamed.
|
|
296
296
|
*/
|
|
297
|
-
|
|
297
|
+
component ComboboxLabel(children: React.Node, ...rest: Rest) {
|
|
298
298
|
const combobox = useCombobox("Combobox.Label");
|
|
299
299
|
const register = combobox.registerLabel;
|
|
300
300
|
useEffect(() => {
|
|
@@ -310,7 +310,7 @@ export component ComboboxLabel(children: React.Node, ...rest: Rest) {
|
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
/** The text field, and every key the pattern defines. */
|
|
313
|
-
|
|
313
|
+
component ComboboxInput(...rest: Rest) {
|
|
314
314
|
const combobox = useCombobox("Combobox.Input");
|
|
315
315
|
// `rest` filtering is render-time props work; ref objects are only passed through later.
|
|
316
316
|
// uf-lint-disable-next-line react-compiler/refs
|
|
@@ -442,7 +442,7 @@ export component ComboboxInput(...rest: Rest) {
|
|
|
442
442
|
* the count the live region announces, and the invariant that
|
|
443
443
|
* `aria-activedescendant` never names an option that has left the list.
|
|
444
444
|
*/
|
|
445
|
-
|
|
445
|
+
component ComboboxList(
|
|
446
446
|
children: renders* (ComboboxOption | ComboboxGroup),
|
|
447
447
|
align?: Align = "start",
|
|
448
448
|
alignOffset?: number = 0,
|
|
@@ -463,19 +463,13 @@ export component ComboboxList(
|
|
|
463
463
|
// `align="start"` because a list of options belongs under the edge the text
|
|
464
464
|
// starts at, and `--uf-anchor-trigger-width` is what a stylesheet reads to
|
|
465
465
|
// make it exactly as wide as the field.
|
|
466
|
-
// useAnchor accepts ref objects and reads them from layout/effects.
|
|
467
|
-
// uf-lint-disable-next-line react-compiler/refs
|
|
468
466
|
const anchored = useAnchor({
|
|
469
467
|
align,
|
|
470
468
|
alignOffset,
|
|
471
|
-
// uf-lint-disable-next-line react-compiler/refs
|
|
472
469
|
anchorRef: inputRef,
|
|
473
470
|
avoidCollisions,
|
|
474
471
|
collisionPadding,
|
|
475
|
-
// `open` is combobox metadata; no ref value is read during render.
|
|
476
|
-
// uf-lint-disable-next-line react-compiler/refs
|
|
477
472
|
open: combobox.open,
|
|
478
|
-
// uf-lint-disable-next-line react-compiler/refs
|
|
479
473
|
overlayRef: listRef,
|
|
480
474
|
side,
|
|
481
475
|
sideOffset,
|
|
@@ -486,8 +480,6 @@ export component ComboboxList(
|
|
|
486
480
|
// `children` that no dependency list can describe. Every write below is
|
|
487
481
|
// guarded by a comparison, so the effect settles after one extra pass rather
|
|
488
482
|
// than looping.
|
|
489
|
-
// This effect measures caller-rendered options after commit.
|
|
490
|
-
// uf-lint-disable-next-line react-compiler/immutability
|
|
491
483
|
useEffect(() => {
|
|
492
484
|
const list = listRef.current;
|
|
493
485
|
if (list == null) {
|
|
@@ -505,7 +497,6 @@ export component ComboboxList(
|
|
|
505
497
|
|
|
506
498
|
const wanted = pendingActiveRef.current;
|
|
507
499
|
if (wanted != null) {
|
|
508
|
-
// uf-lint-disable-next-line react-compiler/immutability
|
|
509
500
|
pendingActiveRef.current = null;
|
|
510
501
|
setActiveId(moveTo(items, -1, wanted, false)?.id ?? null);
|
|
511
502
|
return;
|
|
@@ -562,7 +553,7 @@ export component ComboboxList(
|
|
|
562
553
|
* finds the active option in the document rather than in a registry that could
|
|
563
554
|
* disagree with it.
|
|
564
555
|
*/
|
|
565
|
-
|
|
556
|
+
component ComboboxOption(
|
|
566
557
|
value: string,
|
|
567
558
|
children: React.Node,
|
|
568
559
|
label?: string,
|
|
@@ -629,10 +620,7 @@ export component ComboboxOption(
|
|
|
629
620
|
* and nothing else. `Select.Group` says the same since ubugeeei-prod/uf#562 —
|
|
630
621
|
* it is the same listbox, and it took a second breaking change to get there.
|
|
631
622
|
*/
|
|
632
|
-
|
|
633
|
-
children: renders* (ComboboxOption | ComboboxGroupLabel),
|
|
634
|
-
...rest: Rest
|
|
635
|
-
) {
|
|
623
|
+
component ComboboxGroup(children: renders* (ComboboxOption | ComboboxGroupLabel), ...rest: Rest) {
|
|
636
624
|
const base = useId();
|
|
637
625
|
const [labelled, setLabelled] = useState(false);
|
|
638
626
|
|
|
@@ -657,7 +645,7 @@ export component ComboboxGroup(
|
|
|
657
645
|
* This is not `Combobox.Label`. That one names the field; this one names a
|
|
658
646
|
* group of options, and a combobox with groups has both.
|
|
659
647
|
*/
|
|
660
|
-
|
|
648
|
+
component ComboboxGroupLabel(children: React.Node, ...rest: Rest) {
|
|
661
649
|
const group = useContext(ComboboxGroupContext);
|
|
662
650
|
const register = group?.registerLabel;
|
|
663
651
|
|
|
@@ -683,7 +671,7 @@ export component ComboboxGroupLabel(children: React.Node, ...rest: Rest) {
|
|
|
683
671
|
* contain options: an "no matches" row inside one is announced as an option a
|
|
684
672
|
* reader can choose, and choosing it does nothing.
|
|
685
673
|
*/
|
|
686
|
-
|
|
674
|
+
component ComboboxEmpty(children: React.Node, ...rest: Rest) {
|
|
687
675
|
const combobox = useCombobox("Combobox.Empty");
|
|
688
676
|
if (!combobox.open || combobox.count > 0) {
|
|
689
677
|
return null;
|
|
@@ -702,7 +690,7 @@ export component ComboboxEmpty(children: React.Node, ...rest: Rest) {
|
|
|
702
690
|
* `children` overrides the wording — the default is English and a real
|
|
703
691
|
* application has a translation table.
|
|
704
692
|
*/
|
|
705
|
-
|
|
693
|
+
component ComboboxStatus(children?: React.Node, ...rest: Rest) {
|
|
706
694
|
const combobox = useCombobox("Combobox.Status");
|
|
707
695
|
const message = children ?? defaultAnnouncement(combobox.open, combobox.count);
|
|
708
696
|
|
|
@@ -732,3 +720,24 @@ function labelOf(element: HTMLElement): string {
|
|
|
732
720
|
function textOf(element: HTMLElement): string {
|
|
733
721
|
return (element.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
734
722
|
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* The parts, under the names the `Combobox` namespace gives them.
|
|
726
|
+
*
|
|
727
|
+
* `index.js` re-exports this module whole — `export * as Combobox from "./combobox.js"` —
|
|
728
|
+
* so a caller writes `<Combobox.Root>`, and the namespace is the prefix. Each
|
|
729
|
+
* part is still *declared* as `ComboboxRoot`, so React DevTools, a component
|
|
730
|
+
* stack and an error name the part a reader can find rather than one of forty
|
|
731
|
+
* `Root`s.
|
|
732
|
+
*/
|
|
733
|
+
export {
|
|
734
|
+
ComboboxRoot as Root,
|
|
735
|
+
ComboboxLabel as Label,
|
|
736
|
+
ComboboxInput as Input,
|
|
737
|
+
ComboboxList as List,
|
|
738
|
+
ComboboxOption as Option,
|
|
739
|
+
ComboboxGroup as Group,
|
|
740
|
+
ComboboxGroupLabel as GroupLabel,
|
|
741
|
+
ComboboxEmpty as Empty,
|
|
742
|
+
ComboboxStatus as Status,
|
|
743
|
+
};
|
package/context-menu.js
CHANGED
|
@@ -91,7 +91,7 @@ function pointAt(x: number, y: number): Rect {
|
|
|
91
91
|
* Renders no element of its own, for the reason `Menu.Root` gives: the trigger
|
|
92
92
|
* and the body are siblings in whatever layout the caller wrote.
|
|
93
93
|
*/
|
|
94
|
-
|
|
94
|
+
component ContextMenuRoot(
|
|
95
95
|
children: React.Node,
|
|
96
96
|
defaultOpen?: boolean = false,
|
|
97
97
|
open?: boolean,
|
|
@@ -131,7 +131,7 @@ hook usePoint(part: string): PointState {
|
|
|
131
131
|
* region of the page. The module header says why it is in the tab order and
|
|
132
132
|
* when a caller should take it out again.
|
|
133
133
|
*/
|
|
134
|
-
|
|
134
|
+
component ContextMenuTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
|
|
135
135
|
const menu = useMenu("ContextMenu.Trigger");
|
|
136
136
|
const { openAt } = usePoint("ContextMenu.Trigger");
|
|
137
137
|
const triggerRef = useRef<HTMLElement | null>(null);
|
|
@@ -213,3 +213,32 @@ export component ContextMenuTrigger(children: React.Node, render?: RenderProp, .
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
export type { MenuSelect } from "./menu.js";
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The parts, under the names the `ContextMenu` namespace gives them.
|
|
219
|
+
*
|
|
220
|
+
* `index.js` re-exports this module whole — `export * as ContextMenu from "./context-menu.js"` —
|
|
221
|
+
* so a caller writes `<ContextMenu.Root>`, and the namespace is the prefix. Each
|
|
222
|
+
* part is still *declared* as `ContextMenuRoot`, so React DevTools, a component
|
|
223
|
+
* stack and an error name the part a reader can find rather than one of forty
|
|
224
|
+
* `Root`s.
|
|
225
|
+
*/
|
|
226
|
+
export { ContextMenuRoot as Root, ContextMenuTrigger as Trigger };
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The parts `ContextMenu` shares with `Menu`, re-exported so the `ContextMenu`
|
|
230
|
+
* namespace is complete on its own: `<ContextMenu.Body>` is `Menu`'s part,
|
|
231
|
+
* not a copy of it.
|
|
232
|
+
*/
|
|
233
|
+
export {
|
|
234
|
+
Body,
|
|
235
|
+
Item,
|
|
236
|
+
CheckboxItem,
|
|
237
|
+
RadioGroup,
|
|
238
|
+
RadioItem,
|
|
239
|
+
Separator,
|
|
240
|
+
Group,
|
|
241
|
+
Label,
|
|
242
|
+
Sub,
|
|
243
|
+
SubTrigger,
|
|
244
|
+
} from "./menu.js";
|
package/date-picker.js
CHANGED
|
@@ -51,17 +51,17 @@ import type { PlainDate } from "@uniflowed/core/temporal";
|
|
|
51
51
|
import { Temporal } from "@uniflowed/core/temporal";
|
|
52
52
|
|
|
53
53
|
import type { DateValue } from "./calendar.js";
|
|
54
|
-
import { CalendarRoot } from "./calendar.js";
|
|
54
|
+
import { Root as CalendarRoot } from "./calendar.js";
|
|
55
55
|
import { useControlled } from "./internal/controlled-state.js";
|
|
56
56
|
import type { Align, LogicalSide } from "./internal/anchor.js";
|
|
57
|
-
import type { Rest } from "./internal/merge-props.js";
|
|
57
|
+
import type { PartEvent, Rest } from "./internal/merge-props.js";
|
|
58
58
|
import {
|
|
59
59
|
composeHandlers,
|
|
60
60
|
composeRefs,
|
|
61
61
|
forwarded,
|
|
62
62
|
withoutComposed,
|
|
63
63
|
} from "./internal/merge-props.js";
|
|
64
|
-
import { PopoverBody, PopoverRoot, PopoverTrigger } from "./popover.js";
|
|
64
|
+
import { Body as PopoverBody, Root as PopoverRoot, Trigger as PopoverTrigger } from "./popover.js";
|
|
65
65
|
|
|
66
66
|
type DatePickerState = {|
|
|
67
67
|
/** The text in the field, which is the draft while one is being typed. */
|
|
@@ -118,7 +118,7 @@ function isoParse(text: string): PlainDate | null {
|
|
|
118
118
|
* siblings in whatever layout the caller wrote, and a wrapper would put a
|
|
119
119
|
* `<div>` between them that they then have to style around.
|
|
120
120
|
*/
|
|
121
|
-
|
|
121
|
+
component DatePickerRoot(
|
|
122
122
|
children: React.Node,
|
|
123
123
|
defaultOpen?: boolean = false,
|
|
124
124
|
defaultValue?: DateValue | null = null,
|
|
@@ -251,7 +251,17 @@ const CalendarSettings: React.Context<CalendarSettingsValue> = createContext({
|
|
|
251
251
|
* does not open the popover, the button beside it does, and telling a reader the
|
|
252
252
|
* field expands something would be a promise the field does not keep.
|
|
253
253
|
*/
|
|
254
|
-
|
|
254
|
+
/**
|
|
255
|
+
* The text in the field an event came from.
|
|
256
|
+
*
|
|
257
|
+
* `currentTarget` is the `input` this part renders, so it is always an
|
|
258
|
+
* `HTMLInputElement`. Asked rather than cast, so that the check is the type.
|
|
259
|
+
*/
|
|
260
|
+
function typedText(target: mixed): string {
|
|
261
|
+
return target instanceof HTMLInputElement ? target.value : "";
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
component DatePickerInput(...rest: Rest) {
|
|
255
265
|
const picker = useDatePicker("DatePicker.Input");
|
|
256
266
|
// `rest` filtering is render-time props work; ref objects are only passed through later.
|
|
257
267
|
// uf-lint-disable-next-line react-compiler/refs
|
|
@@ -264,12 +274,12 @@ export component DatePickerInput(...rest: Rest) {
|
|
|
264
274
|
// Input events commit typed text while focus stays on the field ref.
|
|
265
275
|
// uf-lint-disable-next-line react-compiler/refs
|
|
266
276
|
onBlur={composeHandlers(rest.onBlur, (event) => {
|
|
267
|
-
picker.commit((event.currentTarget
|
|
277
|
+
picker.commit(typedText(event.currentTarget));
|
|
268
278
|
})}
|
|
269
279
|
// Input events commit typed text while focus stays on the field ref.
|
|
270
280
|
// uf-lint-disable-next-line react-compiler/refs
|
|
271
281
|
onChange={composeHandlers(rest.onChange, (event) => {
|
|
272
|
-
picker.setDraft((event.currentTarget
|
|
282
|
+
picker.setDraft(typedText(event.currentTarget));
|
|
273
283
|
})}
|
|
274
284
|
// Key events commit typed text while focus stays on the field ref.
|
|
275
285
|
// uf-lint-disable-next-line react-compiler/refs
|
|
@@ -280,7 +290,7 @@ export component DatePickerInput(...rest: Rest) {
|
|
|
280
290
|
// Claimed, so a date picker inside a form is not a control where
|
|
281
291
|
// pressing Enter to confirm what you typed submits the page instead.
|
|
282
292
|
event.preventDefault();
|
|
283
|
-
picker.commit((event.currentTarget
|
|
293
|
+
picker.commit(typedText(event.currentTarget));
|
|
284
294
|
})}
|
|
285
295
|
// React calls callback refs during commit; the calendar and picker read the field later.
|
|
286
296
|
// uf-lint-disable-next-line react-compiler/refs
|
|
@@ -295,7 +305,7 @@ export component DatePickerInput(...rest: Rest) {
|
|
|
295
305
|
}
|
|
296
306
|
|
|
297
307
|
/** The button that opens the calendar. */
|
|
298
|
-
|
|
308
|
+
component DatePickerTrigger(children: React.Node, ...rest: Rest) {
|
|
299
309
|
// `forwarded`, because this part renders another part rather than an
|
|
300
310
|
// intrinsic; `internal/merge-props.js` says what that costs and why.
|
|
301
311
|
return <PopoverTrigger {...forwarded(rest)}>{children}</PopoverTrigger>;
|
|
@@ -308,7 +318,7 @@ export component DatePickerTrigger(children: React.Node, ...rest: Rest) {
|
|
|
308
318
|
* `Calendar.Month` — because where those sit is a design decision and there is
|
|
309
319
|
* no arrangement of them this module could impose that would suit every one.
|
|
310
320
|
*/
|
|
311
|
-
|
|
321
|
+
component DatePickerCalendar(
|
|
312
322
|
children: React.Node,
|
|
313
323
|
align?: Align = "start",
|
|
314
324
|
side?: LogicalSide = "bottom",
|
|
@@ -327,7 +337,7 @@ export component DatePickerCalendar(
|
|
|
327
337
|
// The date, not the first button in the popover. The APG's date picker
|
|
328
338
|
// dialog puts focus on the grid for the same reason.
|
|
329
339
|
initialFocus={dayRef}
|
|
330
|
-
onKeyDown={composeHandlers(rest.onKeyDown, (event) => {
|
|
340
|
+
onKeyDown={composeHandlers(rest.onKeyDown, (event: PartEvent) => {
|
|
331
341
|
if (event.key !== "Escape") {
|
|
332
342
|
return;
|
|
333
343
|
}
|
|
@@ -355,3 +365,19 @@ export component DatePickerCalendar(
|
|
|
355
365
|
</PopoverBody>
|
|
356
366
|
);
|
|
357
367
|
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* The parts, under the names the `DatePicker` namespace gives them.
|
|
371
|
+
*
|
|
372
|
+
* `index.js` re-exports this module whole — `export * as DatePicker from "./date-picker.js"` —
|
|
373
|
+
* so a caller writes `<DatePicker.Root>`, and the namespace is the prefix. Each
|
|
374
|
+
* part is still *declared* as `DatePickerRoot`, so React DevTools, a component
|
|
375
|
+
* stack and an error name the part a reader can find rather than one of forty
|
|
376
|
+
* `Root`s.
|
|
377
|
+
*/
|
|
378
|
+
export {
|
|
379
|
+
DatePickerRoot as Root,
|
|
380
|
+
DatePickerInput as Input,
|
|
381
|
+
DatePickerTrigger as Trigger,
|
|
382
|
+
DatePickerCalendar as Calendar,
|
|
383
|
+
};
|
package/date-range-picker.js
CHANGED
|
@@ -8,10 +8,10 @@ import type { Rest } from "./internal/merge-props.js";
|
|
|
8
8
|
import { forwarded } from "./internal/merge-props.js";
|
|
9
9
|
import type { DateRange } from "./internal/date-range.js";
|
|
10
10
|
import { validateRange, unavailableInRange } from "./internal/date-range.js";
|
|
11
|
-
import { CalendarMonth } from "./calendar.js";
|
|
12
|
-
import { RangeCalendarRoot } from "./range-calendar.js";
|
|
11
|
+
import { Month as CalendarMonth } from "./calendar.js";
|
|
12
|
+
import { Root as RangeCalendarRoot } from "./range-calendar.js";
|
|
13
13
|
import { DateField } from "./date-field.js";
|
|
14
|
-
import { PopoverRoot, PopoverBody, PopoverTrigger } from "./popover.js";
|
|
14
|
+
import { Root as PopoverRoot, Body as PopoverBody, Trigger as PopoverTrigger } from "./popover.js";
|
|
15
15
|
|
|
16
16
|
type Picker = {
|
|
17
17
|
range: DateRange | null,
|
|
@@ -27,7 +27,7 @@ hook usePicker(): Picker {
|
|
|
27
27
|
if (picker == null) throw new Error("DateRangePicker parts must be inside DateRangePicker.Root");
|
|
28
28
|
return picker;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
component DateRangePickerRoot(
|
|
31
31
|
children: React.Node,
|
|
32
32
|
value?: DateRange | null,
|
|
33
33
|
defaultValue?: DateRange | null = null,
|
|
@@ -58,7 +58,7 @@ export component DateRangePickerRoot(
|
|
|
58
58
|
</PickerContext.Provider>
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
component DateRangePickerStartField(...rest: Rest) {
|
|
62
62
|
const picker = usePicker();
|
|
63
63
|
return (
|
|
64
64
|
<DateField
|
|
@@ -76,7 +76,7 @@ export component DateRangePickerStartField(...rest: Rest) {
|
|
|
76
76
|
/>
|
|
77
77
|
);
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
component DateRangePickerEndField(...rest: Rest) {
|
|
80
80
|
const picker = usePicker();
|
|
81
81
|
return (
|
|
82
82
|
<DateField
|
|
@@ -94,10 +94,10 @@ export component DateRangePickerEndField(...rest: Rest) {
|
|
|
94
94
|
/>
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
component DateRangePickerTrigger(children: React.Node, ...rest: Rest) {
|
|
98
98
|
return <PopoverTrigger {...forwarded(rest)}>{children}</PopoverTrigger>;
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
component DateRangePickerCalendar(children?: React.Node = <CalendarMonth />, ...rest: Rest) {
|
|
101
101
|
const picker = usePicker();
|
|
102
102
|
const dayRef = useRef<HTMLElement | null>(null);
|
|
103
103
|
return (
|
|
@@ -118,3 +118,20 @@ export component DateRangePickerCalendar(children?: React.Node = <CalendarMonth
|
|
|
118
118
|
</PopoverBody>
|
|
119
119
|
);
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The parts, under the names the `DateRangePicker` namespace gives them.
|
|
124
|
+
*
|
|
125
|
+
* `index.js` re-exports this module whole — `export * as DateRangePicker from "./date-range-picker.js"` —
|
|
126
|
+
* so a caller writes `<DateRangePicker.Root>`, and the namespace is the prefix. Each
|
|
127
|
+
* part is still *declared* as `DateRangePickerRoot`, so React DevTools, a component
|
|
128
|
+
* stack and an error name the part a reader can find rather than one of forty
|
|
129
|
+
* `Root`s.
|
|
130
|
+
*/
|
|
131
|
+
export {
|
|
132
|
+
DateRangePickerRoot as Root,
|
|
133
|
+
DateRangePickerStartField as StartField,
|
|
134
|
+
DateRangePickerEndField as EndField,
|
|
135
|
+
DateRangePickerTrigger as Trigger,
|
|
136
|
+
DateRangePickerCalendar as Calendar,
|
|
137
|
+
};
|
package/dialog.js
CHANGED
|
@@ -133,7 +133,7 @@ hook useDialog(part: string): DialogState {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/** The dialog, open or closed. Uncontrolled unless `open` is given. */
|
|
136
|
-
|
|
136
|
+
component DialogRoot(
|
|
137
137
|
children: React.Node,
|
|
138
138
|
defaultOpen?: boolean = false,
|
|
139
139
|
open?: boolean,
|
|
@@ -170,7 +170,7 @@ export component DialogRoot(
|
|
|
170
170
|
* else, which is what keeps "focus comes back here" true of whatever the
|
|
171
171
|
* caller rendered.
|
|
172
172
|
*/
|
|
173
|
-
|
|
173
|
+
component DialogTrigger(children: React.Node, render?: RenderProp, ...rest: Rest) {
|
|
174
174
|
const dialog = useDialog("Dialog.Trigger");
|
|
175
175
|
const props = withProps(withoutComposed(rest, ["onClick", "ref"]), {
|
|
176
176
|
// Only while it is open. An `aria-controls` naming an element that is not
|
|
@@ -203,7 +203,7 @@ export component DialogTrigger(children: React.Node, render?: RenderProp, ...res
|
|
|
203
203
|
* their own backdrop or omits one entirely must still get it. That lives on
|
|
204
204
|
* `Dialog.Body`, which is the part that knows where "outside" is.
|
|
205
205
|
*/
|
|
206
|
-
|
|
206
|
+
component DialogOverlay(render?: RenderProp, ...rest: Rest) {
|
|
207
207
|
const dialog = useDialog("Dialog.Overlay");
|
|
208
208
|
if (!dialog.open) {
|
|
209
209
|
return null;
|
|
@@ -222,7 +222,7 @@ export component DialogOverlay(render?: RenderProp, ...rest: Rest) {
|
|
|
222
222
|
* which is the half of "modal" that CSS cannot express; `inert` on everything
|
|
223
223
|
* outside is the half the browser enforces.
|
|
224
224
|
*/
|
|
225
|
-
|
|
225
|
+
component DialogBody(
|
|
226
226
|
children: React.Node,
|
|
227
227
|
dismissOnOutsidePress?: boolean = true,
|
|
228
228
|
initialFocus?: { current: HTMLElement | null },
|
|
@@ -381,7 +381,7 @@ export component DialogBody(
|
|
|
381
381
|
* rendered — a conditional title that is absent used to leave the dialog
|
|
382
382
|
* pointing at an id nothing had.
|
|
383
383
|
*/
|
|
384
|
-
|
|
384
|
+
component DialogTitle(children: React.Node, render?: RenderProp, ...rest: Rest) {
|
|
385
385
|
const dialog = useDialog("Dialog.Title");
|
|
386
386
|
const register = dialog.registerTitle;
|
|
387
387
|
useEffect(() => {
|
|
@@ -407,7 +407,7 @@ export component DialogTitle(children: React.Node, render?: RenderProp, ...rest:
|
|
|
407
407
|
* the one moment the reader has to decide whether they care — so this is where
|
|
408
408
|
* "this cannot be undone" belongs, not in body text further down.
|
|
409
409
|
*/
|
|
410
|
-
|
|
410
|
+
component DialogDescription(children: React.Node, render?: RenderProp, ...rest: Rest) {
|
|
411
411
|
const dialog = useDialog("Dialog.Description");
|
|
412
412
|
const register = dialog.registerDescription;
|
|
413
413
|
useEffect(() => {
|
|
@@ -431,7 +431,7 @@ export component DialogDescription(children: React.Node, render?: RenderProp, ..
|
|
|
431
431
|
* styling layer has a name to attach to, and contributes no semantics because
|
|
432
432
|
* it has none to contribute.
|
|
433
433
|
*/
|
|
434
|
-
|
|
434
|
+
component DialogHeader(children: React.Node, render?: RenderProp, ...rest: Rest) {
|
|
435
435
|
const props = withProps(rest, { children });
|
|
436
436
|
if (render != null) {
|
|
437
437
|
return render(props);
|
|
@@ -440,7 +440,7 @@ export component DialogHeader(children: React.Node, render?: RenderProp, ...rest
|
|
|
440
440
|
}
|
|
441
441
|
|
|
442
442
|
/** The bottom of the dialog, where the actions go. See `Dialog.Header`. */
|
|
443
|
-
|
|
443
|
+
component DialogFooter(children: React.Node, render?: RenderProp, ...rest: Rest) {
|
|
444
444
|
const props = withProps(rest, { children });
|
|
445
445
|
if (render != null) {
|
|
446
446
|
return render(props);
|
|
@@ -449,7 +449,7 @@ export component DialogFooter(children: React.Node, render?: RenderProp, ...rest
|
|
|
449
449
|
}
|
|
450
450
|
|
|
451
451
|
/** A button that closes the dialog. */
|
|
452
|
-
|
|
452
|
+
component DialogClose(children: React.Node, render?: RenderProp, ...rest: Rest) {
|
|
453
453
|
const dialog = useDialog("Dialog.Close");
|
|
454
454
|
const props = withProps(withoutComposed(rest, ["onClick"]), {
|
|
455
455
|
children,
|
|
@@ -471,6 +471,11 @@ export component DialogClose(children: React.Node, render?: RenderProp, ...rest:
|
|
|
471
471
|
* hiding body's children would hide nothing new and the outer dialog's own
|
|
472
472
|
* content would stay readable behind the inner one.
|
|
473
473
|
*
|
|
474
|
+
* The one sibling left alone is the live announcer `visually-hidden.js`
|
|
475
|
+
* creates, which lives in `<body>` and would otherwise go silent for as long as
|
|
476
|
+
* any dialog is open. React Aria's `ariaHideOutside` keeps its announcer for the
|
|
477
|
+
* same reason.
|
|
478
|
+
*
|
|
474
479
|
* Both attributes, because they address different audiences. `aria-hidden`
|
|
475
480
|
* removes the subtree from the accessibility tree; `inert` also stops clicks
|
|
476
481
|
* and takes it out of the tab order, which is the browser's own enforcement of
|
|
@@ -488,12 +493,14 @@ function concealOutside(element: HTMLElement): () => void {
|
|
|
488
493
|
break;
|
|
489
494
|
}
|
|
490
495
|
for (const sibling of Array.from(parent.children)) {
|
|
491
|
-
|
|
496
|
+
// `announce()`'s regions stay readable: a message about what the dialog
|
|
497
|
+
// just did ("Saved", "3 results") is exactly what a modal needs to say.
|
|
498
|
+
if (sibling === node || sibling.hasAttribute("data-uf-live-announcer")) {
|
|
492
499
|
continue;
|
|
493
500
|
}
|
|
494
501
|
restore.push({
|
|
495
502
|
element: sibling,
|
|
496
|
-
hidden: sibling.getAttribute("aria-hidden"),
|
|
503
|
+
hidden: sibling.getAttribute("aria-hidden") ?? null,
|
|
497
504
|
inert: sibling.hasAttribute("inert"),
|
|
498
505
|
});
|
|
499
506
|
sibling.setAttribute("aria-hidden", "true");
|
|
@@ -518,3 +525,24 @@ function concealOutside(element: HTMLElement): () => void {
|
|
|
518
525
|
}
|
|
519
526
|
};
|
|
520
527
|
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* The parts, under the names the `Dialog` namespace gives them.
|
|
531
|
+
*
|
|
532
|
+
* `index.js` re-exports this module whole — `export * as Dialog from "./dialog.js"` —
|
|
533
|
+
* so a caller writes `<Dialog.Root>`, and the namespace is the prefix. Each
|
|
534
|
+
* part is still *declared* as `DialogRoot`, so React DevTools, a component
|
|
535
|
+
* stack and an error name the part a reader can find rather than one of forty
|
|
536
|
+
* `Root`s.
|
|
537
|
+
*/
|
|
538
|
+
export {
|
|
539
|
+
DialogRoot as Root,
|
|
540
|
+
DialogTrigger as Trigger,
|
|
541
|
+
DialogOverlay as Overlay,
|
|
542
|
+
DialogBody as Body,
|
|
543
|
+
DialogHeader as Header,
|
|
544
|
+
DialogFooter as Footer,
|
|
545
|
+
DialogTitle as Title,
|
|
546
|
+
DialogDescription as Description,
|
|
547
|
+
DialogClose as Close,
|
|
548
|
+
};
|