ionbase-ui 0.51.0 → 0.69.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/dist/components/Accordion.d.ts +54 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +68 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/AgentActivity.d.ts +2 -1
- package/dist/components/AgentActivity.d.ts.map +1 -1
- package/dist/components/AgentActivity.js +4 -17
- package/dist/components/AgentActivity.js.map +1 -1
- package/dist/components/Breadcrumb.d.ts +45 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +31 -0
- package/dist/components/Breadcrumb.js.map +1 -0
- package/dist/components/Calendar.d.ts +20 -0
- package/dist/components/Calendar.d.ts.map +1 -0
- package/dist/components/Calendar.js +151 -0
- package/dist/components/Calendar.js.map +1 -0
- package/dist/components/CalendarPopover.d.ts +26 -0
- package/dist/components/CalendarPopover.d.ts.map +1 -0
- package/dist/components/CalendarPopover.js +45 -0
- package/dist/components/CalendarPopover.js.map +1 -0
- package/dist/components/Checkbox.d.ts +2 -1
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +4 -2
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Combobox.d.ts +86 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +261 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/DateField.d.ts +15 -0
- package/dist/components/DateField.d.ts.map +1 -0
- package/dist/components/DateField.js +74 -0
- package/dist/components/DateField.js.map +1 -0
- package/dist/components/DatePicker.d.ts +81 -0
- package/dist/components/DatePicker.d.ts.map +1 -0
- package/dist/components/DatePicker.js +102 -0
- package/dist/components/DatePicker.js.map +1 -0
- package/dist/components/DateRangePicker.d.ts +117 -0
- package/dist/components/DateRangePicker.d.ts.map +1 -0
- package/dist/components/DateRangePicker.js +152 -0
- package/dist/components/DateRangePicker.js.map +1 -0
- package/dist/components/Drawer.d.ts +52 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +66 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/FileUpload.d.ts +79 -0
- package/dist/components/FileUpload.d.ts.map +1 -0
- package/dist/components/FileUpload.js +218 -0
- package/dist/components/FileUpload.js.map +1 -0
- package/dist/components/Header.d.ts +10 -0
- package/dist/components/Header.d.ts.map +1 -1
- package/dist/components/Header.js +18 -3
- package/dist/components/Header.js.map +1 -1
- package/dist/components/NumberInput.d.ts +63 -0
- package/dist/components/NumberInput.d.ts.map +1 -0
- package/dist/components/NumberInput.js +102 -0
- package/dist/components/NumberInput.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +46 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +52 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/PromptInput.d.ts +89 -0
- package/dist/components/PromptInput.d.ts.map +1 -0
- package/dist/components/PromptInput.js +130 -0
- package/dist/components/PromptInput.js.map +1 -0
- package/dist/components/SegmentedControl.d.ts +63 -0
- package/dist/components/SegmentedControl.d.ts.map +1 -0
- package/dist/components/SegmentedControl.js +79 -0
- package/dist/components/SegmentedControl.js.map +1 -0
- package/dist/components/Select.d.ts +9 -2
- package/dist/components/Select.d.ts.map +1 -1
- package/dist/components/Select.js.map +1 -1
- package/dist/components/Sidebar.d.ts +109 -0
- package/dist/components/Sidebar.d.ts.map +1 -0
- package/dist/components/Sidebar.js +106 -0
- package/dist/components/Sidebar.js.map +1 -0
- package/dist/components/Skeleton.d.ts +39 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton.js +50 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Spinner.d.ts +45 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +44 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/Stepper.d.ts +73 -0
- package/dist/components/Stepper.d.ts.map +1 -0
- package/dist/components/Stepper.js +82 -0
- package/dist/components/Stepper.js.map +1 -0
- package/dist/components/Toggle.d.ts +2 -1
- package/dist/components/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle.js +4 -2
- package/dist/components/Toggle.js.map +1 -1
- package/dist/components/ToolCall.d.ts +66 -0
- package/dist/components/ToolCall.d.ts.map +1 -0
- package/dist/components/ToolCall.js +78 -0
- package/dist/components/ToolCall.js.map +1 -0
- package/dist/components/agent-status.d.ts +14 -0
- package/dist/components/agent-status.d.ts.map +1 -0
- package/dist/components/agent-status.js +16 -0
- package/dist/components/agent-status.js.map +1 -0
- package/dist/components/index.d.ts +33 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +16 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/iso-date.d.ts +53 -0
- package/dist/components/iso-date.d.ts.map +1 -0
- package/dist/components/iso-date.js +102 -0
- package/dist/components/iso-date.js.map +1 -0
- package/dist/components/resolve-selection.d.ts +36 -0
- package/dist/components/resolve-selection.d.ts.map +1 -0
- package/dist/components/resolve-selection.js +14 -0
- package/dist/components/resolve-selection.js.map +1 -0
- package/dist/figma-descriptions.json +255 -145
- package/dist/figma-map.json +3205 -1860
- package/dist/meta/Accordion.json +123 -0
- package/dist/meta/AccordionItem.json +90 -0
- package/dist/meta/AgentActivityStep.json +2 -2
- package/dist/meta/Breadcrumb.json +87 -0
- package/dist/meta/BreadcrumbItem.json +78 -0
- package/dist/meta/Checkbox.json +18 -1
- package/dist/meta/Combobox.json +265 -0
- package/dist/meta/DatePicker.json +270 -0
- package/dist/meta/DateRangePicker.json +284 -0
- package/dist/meta/Drawer.json +234 -0
- package/dist/meta/EmptyState.json +1 -1
- package/dist/meta/FileUpload.json +246 -0
- package/dist/meta/Header.json +32 -2
- package/dist/meta/NumberInput.json +407 -0
- package/dist/meta/PhoneInput.json +39 -39
- package/dist/meta/Popover.json +3 -3
- package/dist/meta/ProgressBar.json +166 -0
- package/dist/meta/PromptInput.json +212 -0
- package/dist/meta/SegmentedControl.json +217 -0
- package/dist/meta/SegmentedControlItem.json +122 -0
- package/dist/meta/Select.json +4 -3
- package/dist/meta/Sidebar.json +150 -0
- package/dist/meta/SidebarItem.json +184 -0
- package/dist/meta/SidebarSection.json +131 -0
- package/dist/meta/Skeleton.json +120 -0
- package/dist/meta/Spinner.json +123 -0
- package/dist/meta/Stepper.json +135 -0
- package/dist/meta/StepperStep.json +148 -0
- package/dist/meta/Tabs.json +5 -0
- package/dist/meta/Toggle.json +18 -1
- package/dist/meta/ToolCall.json +192 -0
- package/dist/meta/Tooltip.json +3 -3
- package/dist/meta/components.json +6759 -2705
- package/dist/meta/contrast.json +11075 -3098
- package/dist/meta/index.json +266 -8
- package/dist/meta/patterns/AgentRun.json +27 -0
- package/dist/meta/patterns/Form.json +9 -0
- package/dist/meta/patterns/PageShell.json +43 -3
- package/dist/meta/patterns/Wizard.json +20 -1
- package/dist/meta/patterns/index.json +10 -1
- package/dist/styles/accordion.css +102 -0
- package/dist/styles/breadcrumb.css +72 -0
- package/dist/styles/combobox.css +154 -0
- package/dist/styles/date-picker.css +420 -0
- package/dist/styles/date-range-picker.css +280 -0
- package/dist/styles/drawer.css +181 -0
- package/dist/styles/empty-state.css +16 -4
- package/dist/styles/file-upload.css +236 -0
- package/dist/styles/header.css +26 -0
- package/dist/styles/index.css +23 -0
- package/dist/styles/number-input.css +83 -0
- package/dist/styles/progress-bar.css +107 -0
- package/dist/styles/prompt-input.css +127 -0
- package/dist/styles/segmented-control.css +182 -0
- package/dist/styles/sidebar.css +307 -0
- package/dist/styles/skeleton.css +76 -0
- package/dist/styles/spinner.css +70 -0
- package/dist/styles/stepper.css +250 -0
- package/dist/styles/tool-call.css +202 -0
- package/llms.txt +2 -2
- package/package.json +2 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { forwardRef, useRef, useImperativeHandle, useEffect, } from 'react';
|
|
4
4
|
import { resolveDisabled } from './resolve-disabled.js';
|
|
5
|
+
import { resolveSelection } from './resolve-selection.js';
|
|
5
6
|
/** Figma's check glyph. Inlined for the same reason as Select's chevron: it is
|
|
6
7
|
* part of the control, not a slot a caller fills. */
|
|
7
8
|
const CheckMark = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "m5 13 4 4L19 7", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
@@ -19,8 +20,9 @@ const DashMark = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-
|
|
|
19
20
|
* declaratively and it must be written after every render.
|
|
20
21
|
*/
|
|
21
22
|
export const Checkbox = forwardRef((props, forwardedRef) => {
|
|
22
|
-
const { size = 'md', intent = 'brand', isIndeterminate = false, className, children, isDisabled, disabled, ...rest } = props;
|
|
23
|
+
const { size = 'md', intent = 'brand', isIndeterminate = false, className, children, isDisabled, disabled, isSelected, checked, onChange, onSelectionChange, ...rest } = props;
|
|
23
24
|
const resolvedDisabled = resolveDisabled(isDisabled, disabled);
|
|
25
|
+
const selection = resolveSelection(isSelected, checked, onChange, onSelectionChange);
|
|
24
26
|
const domRef = useRef(null);
|
|
25
27
|
useImperativeHandle(forwardedRef, () => domRef.current);
|
|
26
28
|
useEffect(() => {
|
|
@@ -36,7 +38,7 @@ export const Checkbox = forwardRef((props, forwardedRef) => {
|
|
|
36
38
|
]
|
|
37
39
|
.filter(Boolean)
|
|
38
40
|
.join(' ');
|
|
39
|
-
return (_jsxs("label", { className: classNames, children: [_jsx("input", { ...rest, ref: domRef, type: "checkbox", disabled: resolvedDisabled, className: "ion-checkbox__input" }), _jsx("span", { className: "ion-checkbox__indicator", "aria-hidden": "true", children: _jsx("span", { className: "ion-checkbox__mark", children: isIndeterminate ? _jsx(DashMark, {}) : _jsx(CheckMark, {}) }) }), children && _jsx("span", { className: "ion-checkbox__label", children: children })] }));
|
|
41
|
+
return (_jsxs("label", { className: classNames, children: [_jsx("input", { ...rest, ref: domRef, type: "checkbox", checked: selection.checked, onChange: selection.onChange, disabled: resolvedDisabled, className: "ion-checkbox__input" }), _jsx("span", { className: "ion-checkbox__indicator", "aria-hidden": "true", children: _jsx("span", { className: "ion-checkbox__mark", children: isIndeterminate ? _jsx(DashMark, {}) : _jsx(CheckMark, {}) }) }), children && _jsx("span", { className: "ion-checkbox__label", children: children })] }));
|
|
40
42
|
});
|
|
41
43
|
Checkbox.displayName = 'Checkbox';
|
|
42
44
|
//# sourceMappingURL=Checkbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/components/Checkbox.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAc,EACZ,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,SAAS,GACV,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/components/Checkbox.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type ComboboxSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
export interface ComboboxOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
/** Second line in the row. Part of the option's name, so it is searchable. */
|
|
7
|
+
description?: string;
|
|
8
|
+
isDisabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ComboboxProps {
|
|
11
|
+
/** The full option list. Filtering happens here, against what is typed. */
|
|
12
|
+
options: readonly ComboboxOption[];
|
|
13
|
+
/** Field label. Required for a usable control — see `a11y.requires`. */
|
|
14
|
+
label?: React.ReactNode;
|
|
15
|
+
/** Helper text below the field. */
|
|
16
|
+
description?: React.ReactNode;
|
|
17
|
+
/** Replaces the helper text when `isInvalid` is set. */
|
|
18
|
+
errorMessage?: React.ReactNode;
|
|
19
|
+
isInvalid?: boolean;
|
|
20
|
+
isDisabled?: boolean;
|
|
21
|
+
isReadOnly?: boolean;
|
|
22
|
+
/** Matches Input's `Size` variant: Small, Medium, Large. */
|
|
23
|
+
size?: ComboboxSize;
|
|
24
|
+
placeholder?: string;
|
|
25
|
+
/** Controlled selection. `null` means nothing is selected. */
|
|
26
|
+
selectedKey?: string | null;
|
|
27
|
+
defaultSelectedKey?: string;
|
|
28
|
+
onSelectionChange?: (key: string | null) => void;
|
|
29
|
+
/** Controlled text. Usually only needed for async/remote filtering. */
|
|
30
|
+
inputValue?: string;
|
|
31
|
+
defaultInputValue?: string;
|
|
32
|
+
onInputChange?: (value: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Accept text that matches no option. Off by default: a combobox whose value
|
|
35
|
+
* is a free string is a text field with suggestions, and the caller should
|
|
36
|
+
* have to say that is what they want.
|
|
37
|
+
*/
|
|
38
|
+
allowsCustomValue?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* What opens the list. `input` — react-aria's default and this one — opens it
|
|
41
|
+
* on the first keystroke; the chevron opens it on demand. `focus` opens it
|
|
42
|
+
* the moment the field is tabbed into, which is loud in a long form.
|
|
43
|
+
*/
|
|
44
|
+
menuTrigger?: 'focus' | 'input' | 'manual';
|
|
45
|
+
/** Shown in place of the list when nothing matches. */
|
|
46
|
+
emptyLabel?: React.ReactNode;
|
|
47
|
+
/** Accessible label for the disclosure button. */
|
|
48
|
+
buttonLabel?: string;
|
|
49
|
+
/** Posts the selected value under this name, for an uncontrolled form. */
|
|
50
|
+
name?: string;
|
|
51
|
+
className?: string;
|
|
52
|
+
wrapperClassName?: string;
|
|
53
|
+
id?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Combobox — a text field that filters a list, with one selected value.
|
|
57
|
+
*
|
|
58
|
+
* Drawn in Figma as `Combobox` (1370:2359) — three sizes by seven states, the
|
|
59
|
+
* same axes `Input` carries, because the field IS Input's box. The open list is
|
|
60
|
+
* a second Figma component, `Combobox Menu`, with no React export behind it.
|
|
61
|
+
*
|
|
62
|
+
* WHY THIS IS NOT `Select` WITH A SEARCH BOX
|
|
63
|
+
*
|
|
64
|
+
* `Select` wraps a native `<select>`, whose list the browser owns: it cannot be
|
|
65
|
+
* filtered, and its rows can only hold text. Everything above about twenty
|
|
66
|
+
* options needs filtering, and `Select`'s own contract has pointed at "a
|
|
67
|
+
* combobox — not yet in this system" since it was written. This is it.
|
|
68
|
+
*
|
|
69
|
+
* The cost is that everything the native control gave away for free — keyboard
|
|
70
|
+
* handling, the mobile picker, screen-reader semantics — now has to be built.
|
|
71
|
+
* React Aria's `useComboBox` builds it: `role="combobox"` with `aria-expanded`,
|
|
72
|
+
* `aria-controls` and `aria-activedescendant`, arrow keys that move a virtual
|
|
73
|
+
* focus while the real focus stays in the input, and Escape that reverts.
|
|
74
|
+
*
|
|
75
|
+
* FILTERING IS LOCALE-AWARE, NOT `toLowerCase().includes()`
|
|
76
|
+
*
|
|
77
|
+
* `useFilter({ sensitivity: 'base' })` is `Intl.Collator` underneath, so
|
|
78
|
+
* "resume" matches "résumé" and Turkish dotted/dotless I behave the way a
|
|
79
|
+
* Turkish reader expects. The lowercase-and-includes version fails both, and
|
|
80
|
+
* fails them silently in exactly the locales least likely to be tested.
|
|
81
|
+
*/
|
|
82
|
+
export declare function Combobox({ options, label, description, errorMessage, isInvalid, isDisabled, isReadOnly, size, placeholder, selectedKey, defaultSelectedKey, onSelectionChange, inputValue, defaultInputValue, onInputChange, allowsCustomValue, menuTrigger, emptyLabel, buttonLabel, name, className, wrapperClassName, id, }: ComboboxProps): React.JSX.Element;
|
|
83
|
+
export declare namespace Combobox {
|
|
84
|
+
var displayName: string;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=Combobox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combobox.d.ts","sourceRoot":"","sources":["../../src/components/Combobox.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAiBzD,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IACnC,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,mCAAmC;IACnC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC3C,uDAAuD;IACvD,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAyMD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,KAAK,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,IAAW,EACX,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,WAAqB,EACrB,UAAyB,EACzB,WAAgC,EAChC,IAAI,EACJ,SAAS,EACT,gBAAgB,EAChB,EAAE,GACH,EAAE,aAAa,qBAyPf;yBAjRe,QAAQ"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Overlay, usePopover, useComboBox, useListBox, useOption, useButton, useFilter, DismissButton, mergeProps, } from 'react-aria';
|
|
5
|
+
import { useComboBoxState, Item } from 'react-stately';
|
|
6
|
+
const ChevronDown = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "m6 9 6 6 6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
7
|
+
/* ------------------------------------------------------------------ option */
|
|
8
|
+
function ComboboxOptionRow({ item, state, }) {
|
|
9
|
+
const ref = useRef(null);
|
|
10
|
+
const { optionProps, isSelected, isFocused, isDisabled } = useOption({ key: item.key }, state, ref);
|
|
11
|
+
const option = item.value;
|
|
12
|
+
return (_jsxs("li", { ...optionProps, ref: ref, className: [
|
|
13
|
+
'ion-combobox-menu__option',
|
|
14
|
+
isSelected ? 'ion-combobox-menu__option--selected' : '',
|
|
15
|
+
isFocused ? 'ion-combobox-menu__option--focused' : '',
|
|
16
|
+
isDisabled ? 'ion-combobox-menu__option--disabled' : '',
|
|
17
|
+
]
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.join(' '), "data-focused": isFocused || undefined, "data-selected": isSelected || undefined, "data-disabled": isDisabled || undefined, children: [_jsx("span", { className: "ion-combobox-menu__label", children: item.rendered }), option?.description && (_jsx("span", { className: "ion-combobox-menu__description", children: option.description }))] }));
|
|
20
|
+
}
|
|
21
|
+
/** Breathing room kept between the menu and the viewport edge, in px. */
|
|
22
|
+
const GUTTER = 8;
|
|
23
|
+
/**
|
|
24
|
+
* The listbox, in its own component because it is mounted only while open.
|
|
25
|
+
*
|
|
26
|
+
* Same split as Popover and Drawer, and for the same reason: `usePopover`
|
|
27
|
+
* positions against a ref in an effect that runs when the component CALLING it
|
|
28
|
+
* mounts. Called beside `useComboBoxState` in the parent, that is the moment
|
|
29
|
+
* the input mounts, and the panel is then positioned once, at the origin,
|
|
30
|
+
* before it exists.
|
|
31
|
+
*/
|
|
32
|
+
function ComboboxListBox({ state, listBoxRef, popoverRef, triggerRef, listBoxProps, emptyLabel, }) {
|
|
33
|
+
const { popoverProps } = usePopover({
|
|
34
|
+
triggerRef,
|
|
35
|
+
popoverRef,
|
|
36
|
+
placement: 'bottom start',
|
|
37
|
+
offset: 4,
|
|
38
|
+
/*
|
|
39
|
+
* Non-modal, unlike every other overlay in this system. A modal popover
|
|
40
|
+
* moves focus into itself and hides the rest of the page — correct for a
|
|
41
|
+
* dialog, fatal here, because the user is still typing in the input and
|
|
42
|
+
* focus must never leave it. The input keeps focus and drives the list
|
|
43
|
+
* through `aria-activedescendant`.
|
|
44
|
+
*/
|
|
45
|
+
isNonModal: true,
|
|
46
|
+
}, state);
|
|
47
|
+
const { listBoxProps: innerListBoxProps } = useListBox({ ...listBoxProps, disallowEmptySelection: true }, state, listBoxRef);
|
|
48
|
+
/*
|
|
49
|
+
* The menu matches the field's width rather than hugging its content.
|
|
50
|
+
* `usePopover` sizes to the panel, and a list narrower or wider than the
|
|
51
|
+
* control it belongs to reads as a different element. Measured rather than
|
|
52
|
+
* assumed, because the field is fluid.
|
|
53
|
+
*
|
|
54
|
+
* READ DURING RENDER, NOT FROM A ResizeObserver — and this cost an afternoon.
|
|
55
|
+
*
|
|
56
|
+
* The first version kept the width in state and synced it from a
|
|
57
|
+
* ResizeObserver on the field. It measured correctly and closed the menu:
|
|
58
|
+
* every open flashed a listbox that was gone about a hundred milliseconds
|
|
59
|
+
* later, before any option could take virtual focus. Writing a width onto a
|
|
60
|
+
* positioned popover feeds back into `usePopover`'s own position pass, and
|
|
61
|
+
* its scroll handling closes the overlay. Bisecting the panel down to nothing
|
|
62
|
+
* was the only way to see it — the symptom looked like a keyboard bug.
|
|
63
|
+
*
|
|
64
|
+
* The panel mounts only while open, so the trigger is already laid out by the
|
|
65
|
+
* time this runs. One read, no state, no observer, nothing to feed back.
|
|
66
|
+
*/
|
|
67
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
68
|
+
/*
|
|
69
|
+
* Clamped to what is actually to the right of the trigger. A field running
|
|
70
|
+
* the full bleed of the page sits flush to the viewport edge, and a menu that
|
|
71
|
+
* matched its width exactly would start at the field's left and end past the
|
|
72
|
+
* edge — a horizontal scrollbar, which `useCloseOnScroll` reads as a scroll
|
|
73
|
+
* and closes the menu on. The failure mode was a combobox that could not be
|
|
74
|
+
* opened at all, so the clamp trades a slightly narrow menu for one that
|
|
75
|
+
* exists.
|
|
76
|
+
*/
|
|
77
|
+
const width = rect
|
|
78
|
+
? Math.min(rect.width, document.documentElement.clientWidth - rect.left - GUTTER)
|
|
79
|
+
: undefined;
|
|
80
|
+
const isEmpty = state.collection.size === 0;
|
|
81
|
+
return (_jsx(Overlay, { children: _jsxs("div", { ...popoverProps, ref: popoverRef, className: "ion-combobox-menu", style: { ...popoverProps.style, width }, children: [_jsx(DismissButton, { onDismiss: () => state.close() }), _jsx("ul", { ...mergeProps(innerListBoxProps), ref: listBoxRef, className: "ion-combobox-menu__list", children: [...state.collection].map((item) => (_jsx(ComboboxOptionRow, { item: item, state: state }, item.key))) }), isEmpty && (_jsx("div", { className: "ion-combobox-menu__empty", role: "presentation", children: emptyLabel })), _jsx(DismissButton, { onDismiss: () => state.close() })] }) }));
|
|
82
|
+
}
|
|
83
|
+
/* ---------------------------------------------------------------- combobox */
|
|
84
|
+
/**
|
|
85
|
+
* Combobox — a text field that filters a list, with one selected value.
|
|
86
|
+
*
|
|
87
|
+
* Drawn in Figma as `Combobox` (1370:2359) — three sizes by seven states, the
|
|
88
|
+
* same axes `Input` carries, because the field IS Input's box. The open list is
|
|
89
|
+
* a second Figma component, `Combobox Menu`, with no React export behind it.
|
|
90
|
+
*
|
|
91
|
+
* WHY THIS IS NOT `Select` WITH A SEARCH BOX
|
|
92
|
+
*
|
|
93
|
+
* `Select` wraps a native `<select>`, whose list the browser owns: it cannot be
|
|
94
|
+
* filtered, and its rows can only hold text. Everything above about twenty
|
|
95
|
+
* options needs filtering, and `Select`'s own contract has pointed at "a
|
|
96
|
+
* combobox — not yet in this system" since it was written. This is it.
|
|
97
|
+
*
|
|
98
|
+
* The cost is that everything the native control gave away for free — keyboard
|
|
99
|
+
* handling, the mobile picker, screen-reader semantics — now has to be built.
|
|
100
|
+
* React Aria's `useComboBox` builds it: `role="combobox"` with `aria-expanded`,
|
|
101
|
+
* `aria-controls` and `aria-activedescendant`, arrow keys that move a virtual
|
|
102
|
+
* focus while the real focus stays in the input, and Escape that reverts.
|
|
103
|
+
*
|
|
104
|
+
* FILTERING IS LOCALE-AWARE, NOT `toLowerCase().includes()`
|
|
105
|
+
*
|
|
106
|
+
* `useFilter({ sensitivity: 'base' })` is `Intl.Collator` underneath, so
|
|
107
|
+
* "resume" matches "résumé" and Turkish dotted/dotless I behave the way a
|
|
108
|
+
* Turkish reader expects. The lowercase-and-includes version fails both, and
|
|
109
|
+
* fails them silently in exactly the locales least likely to be tested.
|
|
110
|
+
*/
|
|
111
|
+
export function Combobox({ options, label, description, errorMessage, isInvalid, isDisabled, isReadOnly, size = 'md', placeholder, selectedKey, defaultSelectedKey, onSelectionChange, inputValue, defaultInputValue, onInputChange, allowsCustomValue, menuTrigger = 'input', emptyLabel = 'No matches', buttonLabel = 'Show suggestions', name, className, wrapperClassName, id, }) {
|
|
112
|
+
const { contains } = useFilter({ sensitivity: 'base' });
|
|
113
|
+
/*
|
|
114
|
+
* The description is searchable, but it is NOT part of `textValue`.
|
|
115
|
+
*
|
|
116
|
+
* That distinction cost a test to find. `textValue` does double duty in
|
|
117
|
+
* react-stately: it is what the filter reads AND what the input is set to
|
|
118
|
+
* once an option is chosen. Folding the description into it made "Australia"
|
|
119
|
+
* searchable by "Oceania" and then wrote `Australia Oceania` into the field.
|
|
120
|
+
*
|
|
121
|
+
* So `textValue` stays the label, and the filter is widened instead —
|
|
122
|
+
* keyed by label, which is what a Node carries back to it.
|
|
123
|
+
*/
|
|
124
|
+
const searchable = useMemo(() => {
|
|
125
|
+
const map = new Map();
|
|
126
|
+
for (const o of options) {
|
|
127
|
+
map.set(o.label, [o.label, o.description].filter(Boolean).join(' '));
|
|
128
|
+
}
|
|
129
|
+
return map;
|
|
130
|
+
}, [options]);
|
|
131
|
+
/*
|
|
132
|
+
* Filtering is done HERE, not handed to react-stately's `defaultFilter`.
|
|
133
|
+
*
|
|
134
|
+
* `defaultFilter` is only consulted for an UNCONTROLLED collection — one
|
|
135
|
+
* passed as `defaultItems`. Passed as `items`, as it is here, react-stately
|
|
136
|
+
* takes the list as final and the filter is silently ignored: no error, no
|
|
137
|
+
* warning, and a combobox that lists every option no matter what is typed.
|
|
138
|
+
* Three tests failed on it before the cause was obvious.
|
|
139
|
+
*
|
|
140
|
+
* `items` is the right half of that trade anyway. `defaultItems` snapshots
|
|
141
|
+
* the list on first render, so a caller whose options arrive from a fetch
|
|
142
|
+
* would get an empty combobox forever.
|
|
143
|
+
*/
|
|
144
|
+
const [typed, setTyped] = useState(defaultInputValue ?? '');
|
|
145
|
+
const text = inputValue ?? typed;
|
|
146
|
+
const filtered = useMemo(() => {
|
|
147
|
+
if (!text)
|
|
148
|
+
return options;
|
|
149
|
+
/*
|
|
150
|
+
* An exact label match shows the WHOLE list, which looks like a bug and is
|
|
151
|
+
* the opposite. Selecting an option writes its label into the input, and a
|
|
152
|
+
* filter applied to that would narrow the list to the one row already
|
|
153
|
+
* chosen — so reopening it to change your mind would offer nothing else.
|
|
154
|
+
*/
|
|
155
|
+
if (options.some((o) => o.label === text))
|
|
156
|
+
return options;
|
|
157
|
+
return options.filter((o) => contains(searchable.get(o.label) ?? o.label, text));
|
|
158
|
+
}, [contains, options, searchable, text]);
|
|
159
|
+
const disabledKeys = useMemo(() => options.filter((o) => o.isDisabled).map((o) => o.value), [options]);
|
|
160
|
+
const state = useComboBoxState({
|
|
161
|
+
label: typeof label === 'string' ? label : undefined,
|
|
162
|
+
items: filtered,
|
|
163
|
+
children: (item) => (_jsx(Item, { textValue: item.label, children: item.label }, item.value)),
|
|
164
|
+
disabledKeys,
|
|
165
|
+
menuTrigger,
|
|
166
|
+
/*
|
|
167
|
+
* The list stays mounted with nothing in it so the empty message can be
|
|
168
|
+
* shown. Left off, react-aria closes the popover the moment the filter
|
|
169
|
+
* matches nothing, and the user sees the list vanish with no explanation.
|
|
170
|
+
*/
|
|
171
|
+
allowsEmptyCollection: true,
|
|
172
|
+
allowsCustomValue,
|
|
173
|
+
isDisabled,
|
|
174
|
+
isReadOnly,
|
|
175
|
+
selectedKey,
|
|
176
|
+
defaultSelectedKey,
|
|
177
|
+
onSelectionChange: onSelectionChange
|
|
178
|
+
? (key) => onSelectionChange(key === null ? null : String(key))
|
|
179
|
+
: undefined,
|
|
180
|
+
inputValue,
|
|
181
|
+
defaultInputValue,
|
|
182
|
+
onInputChange: (value) => {
|
|
183
|
+
setTyped(value);
|
|
184
|
+
onInputChange?.(value);
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
const inputRef = useRef(null);
|
|
188
|
+
const listBoxRef = useRef(null);
|
|
189
|
+
const popoverRef = useRef(null);
|
|
190
|
+
const buttonRef = useRef(null);
|
|
191
|
+
const boxRef = useRef(null);
|
|
192
|
+
const { buttonProps: triggerProps, inputProps, listBoxProps, labelProps, descriptionProps, errorMessageProps, } = useComboBox({
|
|
193
|
+
/*
|
|
194
|
+
* `id` goes to useComboBox, not to useComboBoxState. The state owns the
|
|
195
|
+
* collection; the hook owns the DOM wiring — the input's id, the label's
|
|
196
|
+
* htmlFor, and the aria-controls that points at the listbox. Setting it
|
|
197
|
+
* on the state is accepted by neither and silently does nothing.
|
|
198
|
+
*/
|
|
199
|
+
id,
|
|
200
|
+
label: typeof label === 'string' ? label : undefined,
|
|
201
|
+
placeholder,
|
|
202
|
+
isDisabled,
|
|
203
|
+
isReadOnly,
|
|
204
|
+
isInvalid,
|
|
205
|
+
inputRef,
|
|
206
|
+
listBoxRef,
|
|
207
|
+
popoverRef,
|
|
208
|
+
buttonRef,
|
|
209
|
+
}, state);
|
|
210
|
+
const { buttonProps } = useButton({
|
|
211
|
+
...triggerProps,
|
|
212
|
+
'aria-label': buttonLabel,
|
|
213
|
+
}, buttonRef);
|
|
214
|
+
const helper = isInvalid && errorMessage ? errorMessage : description;
|
|
215
|
+
const boxClassNames = [
|
|
216
|
+
'ion-input',
|
|
217
|
+
'ion-combobox',
|
|
218
|
+
size !== 'md' ? `ion-input--${size}` : '',
|
|
219
|
+
isInvalid ? 'ion-input--invalid' : '',
|
|
220
|
+
isDisabled ? 'ion-input--disabled' : '',
|
|
221
|
+
className || '',
|
|
222
|
+
]
|
|
223
|
+
.filter(Boolean)
|
|
224
|
+
.join(' ');
|
|
225
|
+
return (_jsxs("div", { className: [
|
|
226
|
+
'ion-field',
|
|
227
|
+
isInvalid ? 'ion-field--error' : '',
|
|
228
|
+
wrapperClassName || '',
|
|
229
|
+
]
|
|
230
|
+
.filter(Boolean)
|
|
231
|
+
.join(' '), children: [label && (_jsx("label", { ...labelProps, className: "ion-field__label", children: label })), _jsxs("div", { ref: boxRef, className: boxClassNames, "data-open": state.isOpen || undefined, "data-invalid": isInvalid || undefined, "data-disabled": isDisabled || undefined, children: [_jsx("input", { ...inputProps, ref: inputRef, className: "ion-input__field" }), name && (_jsx("input", { type: "hidden", name: name, value: state.selectedKey == null ? '' : String(state.selectedKey) })), _jsx("button", { ...buttonProps, "aria-labelledby": undefined, "aria-label": buttonLabel,
|
|
232
|
+
/*
|
|
233
|
+
* The press must not move focus to the button.
|
|
234
|
+
*
|
|
235
|
+
* `useComboBox` closes the list the moment focus leaves the input, so
|
|
236
|
+
* a disclosure button that takes focus opens the list and closes it
|
|
237
|
+
* again within the same gesture. A test caught exactly that: the
|
|
238
|
+
* listbox was found and was gone by the next assertion. Chromium
|
|
239
|
+
* focuses a button on mousedown, so this is not hypothetical.
|
|
240
|
+
*
|
|
241
|
+
* `preventDefault` on mousedown is what stops that — and the caret
|
|
242
|
+
* goes to the field instead, which is where someone pressing a
|
|
243
|
+
* chevron to browse a list wants to type next anyway.
|
|
244
|
+
*/
|
|
245
|
+
onMouseDown: (e) => {
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
inputRef.current?.focus();
|
|
248
|
+
}, ref: buttonRef, type: "button",
|
|
249
|
+
/*
|
|
250
|
+
* Out of the tab order on purpose, and this is the ARIA pattern
|
|
251
|
+
* rather than an oversight. The input already reaches the list with
|
|
252
|
+
* ArrowDown, so a tab stop on the disclosure button would make every
|
|
253
|
+
* combobox in a form cost two tabs to pass and offer nothing the
|
|
254
|
+
* input does not.
|
|
255
|
+
*/
|
|
256
|
+
tabIndex: -1, className: "ion-combobox__button", children: _jsx(ChevronDown, {}) })] }), state.isOpen && (_jsx(ComboboxListBox, { state: state, listBoxRef: listBoxRef, popoverRef: popoverRef, triggerRef: boxRef, listBoxProps: listBoxProps, emptyLabel: emptyLabel })), helper && (_jsx("span", { ...(isInvalid && errorMessage
|
|
257
|
+
? errorMessageProps
|
|
258
|
+
: descriptionProps), className: "ion-field__helper", children: helper }))] }));
|
|
259
|
+
}
|
|
260
|
+
Combobox.displayName = 'Combobox';
|
|
261
|
+
//# sourceMappingURL=Combobox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combobox.js","sourceRoot":"","sources":["../../src/components/Combobox.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AriaDatePickerProps, DateValue } from 'react-aria';
|
|
3
|
+
interface DateFieldProps extends AriaDatePickerProps<DateValue> {
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The field itself. Rendered inside the picker's `.ion-input` box, which owns
|
|
8
|
+
* the border, the background and the states — exactly as Combobox reuses it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function DateField({ className, ...props }: DateFieldProps): React.JSX.Element;
|
|
11
|
+
export declare namespace DateField {
|
|
12
|
+
var displayName: string;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=DateField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateField.d.ts","sourceRoot":"","sources":["../../src/components/DateField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAItC,OAAO,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA4DjE,UAAU,cAAe,SAAQ,mBAAmB,CAAC,SAAS,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyDD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,qBAqChE;yBArCe,SAAS"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from 'react';
|
|
4
|
+
import { useDateField, useDateSegment, useLocale } from 'react-aria';
|
|
5
|
+
import { useDateFieldState } from 'react-stately';
|
|
6
|
+
import { createCalendar } from '@internationalized/date';
|
|
7
|
+
/**
|
|
8
|
+
* One segment — a month, a day, a year, or a literal separator.
|
|
9
|
+
*
|
|
10
|
+
* The literal is rendered by the same component rather than being filtered out
|
|
11
|
+
* before the map. `useDateSegment` returns the separator's own props, including
|
|
12
|
+
* the `aria-hidden` that keeps `/` and `.` out of the announcement, and
|
|
13
|
+
* re-deriving that here would mean re-deriving it wrongly the first time a
|
|
14
|
+
* locale used a separator nobody tested.
|
|
15
|
+
*/
|
|
16
|
+
function Segment({ segment, state, }) {
|
|
17
|
+
const ref = useRef(null);
|
|
18
|
+
const { segmentProps } = useDateSegment(segment, state, ref);
|
|
19
|
+
return (_jsx("div", { ...segmentProps, ref: ref, className: [
|
|
20
|
+
'ion-date-field__segment',
|
|
21
|
+
segment.isPlaceholder ? 'ion-date-field__segment--placeholder' : '',
|
|
22
|
+
segment.type === 'literal' ? 'ion-date-field__segment--literal' : '',
|
|
23
|
+
]
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.join(' '), "data-placeholder": segment.isPlaceholder || undefined, style: {
|
|
26
|
+
/*
|
|
27
|
+
* Reserves the width of the widest value this segment can hold, so the
|
|
28
|
+
* field does not jitter as it is filled.
|
|
29
|
+
*
|
|
30
|
+
* `maxValue` is the segment's own maximum — 31 for a day, 12 for a
|
|
31
|
+
* month — not a guess at digit count, which is what makes it correct
|
|
32
|
+
* for a locale whose year segment is two digits or whose month segment
|
|
33
|
+
* is a name. A literal has no maxValue and needs no reservation.
|
|
34
|
+
*
|
|
35
|
+
* `ch` rather than `em`: the digits are what is being measured, and in
|
|
36
|
+
* every sans-serif worth shipping the digits are tabular-width.
|
|
37
|
+
*/
|
|
38
|
+
minWidth: segment.maxValue != null
|
|
39
|
+
? `${String(segment.maxValue).length}ch`
|
|
40
|
+
: undefined,
|
|
41
|
+
}, children: segment.text }));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The field itself. Rendered inside the picker's `.ion-input` box, which owns
|
|
45
|
+
* the border, the background and the states — exactly as Combobox reuses it.
|
|
46
|
+
*/
|
|
47
|
+
export function DateField({ className, ...props }) {
|
|
48
|
+
/*
|
|
49
|
+
* `useLocale` reads react-aria's I18nProvider, falling back to the browser's
|
|
50
|
+
* own locale when there is none. That fallback is why a consumer who never
|
|
51
|
+
* mounts a provider still gets their own date order rather than en-US.
|
|
52
|
+
*/
|
|
53
|
+
const { locale } = useLocale();
|
|
54
|
+
const state = useDateFieldState({
|
|
55
|
+
...props,
|
|
56
|
+
locale,
|
|
57
|
+
/*
|
|
58
|
+
* `createCalendar` is passed rather than imported by react-stately on
|
|
59
|
+
* purpose: it is the whole calendar-systems bundle, and a library that
|
|
60
|
+
* imported it directly would put every calendar in every consumer's bundle
|
|
61
|
+
* whether or not they render one. Handing it in is the opt-in.
|
|
62
|
+
*/
|
|
63
|
+
createCalendar,
|
|
64
|
+
});
|
|
65
|
+
const ref = useRef(null);
|
|
66
|
+
const { fieldProps } = useDateField(props, state, ref);
|
|
67
|
+
return (_jsx("div", { ...fieldProps, ref: ref, className: ['ion-date-field', className].filter(Boolean).join(' '), children: state.segments.map((segment, i) => (
|
|
68
|
+
// Keyed by position, which is the only stable identity a segment
|
|
69
|
+
// has: the list is rebuilt whole when the locale changes, and two
|
|
70
|
+
// segments of the same type never coexist.
|
|
71
|
+
_jsx(Segment, { segment: segment, state: state }, i))) }));
|
|
72
|
+
}
|
|
73
|
+
DateField.displayName = 'DateField';
|
|
74
|
+
//# sourceMappingURL=DateField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateField.js","sourceRoot":"","sources":["../../src/components/DateField.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAc,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAiEzD;;;;;;;;GAQG;AACH,SAAS,OAAO,CAAC,EACf,OAAO,EACP,KAAK,GAIN;IACC,MAAM,GAAG,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IACzC,MAAM,EAAE,YAAY,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAE7D,OAAO,CACL,iBACM,YAAY,EAChB,GAAG,EAAE,GAAG,EACR,SAAS,EAAE;YACT,yBAAyB;YACzB,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAC,EAAE;YACnE,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,EAAE;SACrE;aACE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,GAAG,CAAC,sBACM,OAAO,CAAC,aAAa,IAAI,SAAS,EACpD,KAAK,EAAE;YACL;;;;;;;;;;;eAWG;YACH,QAAQ,EACN,OAAO,CAAC,QAAQ,IAAI,IAAI;gBACtB,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,IAAI;gBACxC,CAAC,CAAC,SAAS;SAChB,YAEA,OAAO,CAAC,IAAI,GACT,CACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAkB;IAC/D;;;;OAIG;IACH,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAE/B,MAAM,KAAK,GAAG,iBAAiB,CAAC;QAC9B,GAAG,KAAK;QACR,MAAM;QACN;;;;;WAKG;QACH,cAAc;KACf,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IACzC,MAAM,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAEvD,OAAO,CACL,iBACM,UAAU,EACd,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,YAEjE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,iEAAiE;QACjE,kEAAkE;QAClE,2CAA2C;QAC3C,KAAC,OAAO,IAAS,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,IAAjC,CAAC,CAAoC,CACpD,CAAC,GACE,CACP,CAAC;AACJ,CAAC;AAED,SAAS,CAAC,WAAW,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { IsoDate } from './iso-date.js';
|
|
3
|
+
export type DatePickerSize = 'sm' | 'md' | 'lg';
|
|
4
|
+
export interface DatePickerProps {
|
|
5
|
+
/** Field label. Required for a usable control — see `a11y.requires`. */
|
|
6
|
+
label?: React.ReactNode;
|
|
7
|
+
/** Helper text below the field. */
|
|
8
|
+
description?: React.ReactNode;
|
|
9
|
+
/** Replaces the helper text when `isInvalid` is set. */
|
|
10
|
+
errorMessage?: React.ReactNode;
|
|
11
|
+
isInvalid?: boolean;
|
|
12
|
+
isDisabled?: boolean;
|
|
13
|
+
isReadOnly?: boolean;
|
|
14
|
+
isRequired?: boolean;
|
|
15
|
+
/** Matches Input's `Size` variant: Small, Medium, Large. */
|
|
16
|
+
size?: DatePickerSize;
|
|
17
|
+
/**
|
|
18
|
+
* The selected date as `YYYY-MM-DD`. `null` means nothing is selected.
|
|
19
|
+
*
|
|
20
|
+
* Not a `Date` and not a timestamp — a calendar date has no time and no
|
|
21
|
+
* timezone, so it cannot shift across midnight the way `new Date('2026-04-12')`
|
|
22
|
+
* does for every reader west of Greenwich. A malformed value throws rather
|
|
23
|
+
* than rendering an empty field.
|
|
24
|
+
*/
|
|
25
|
+
value?: IsoDate | null;
|
|
26
|
+
/** The initial date as `YYYY-MM-DD`, for an uncontrolled picker. */
|
|
27
|
+
defaultValue?: IsoDate;
|
|
28
|
+
/** Fires with `YYYY-MM-DD`, or `null` when the field is cleared. */
|
|
29
|
+
onChange?: (value: IsoDate | null) => void;
|
|
30
|
+
/** Earliest selectable date, `YYYY-MM-DD`. Earlier dates are unreachable. */
|
|
31
|
+
minValue?: IsoDate;
|
|
32
|
+
/** Latest selectable date, `YYYY-MM-DD`. */
|
|
33
|
+
maxValue?: IsoDate;
|
|
34
|
+
/**
|
|
35
|
+
* Individual dates the calendar refuses — weekends, holidays, booked days.
|
|
36
|
+
* Receives `YYYY-MM-DD`. Use `minValue`/`maxValue` for a continuous bound;
|
|
37
|
+
* this is for the holes inside it.
|
|
38
|
+
*/
|
|
39
|
+
isDateUnavailable?: (date: IsoDate) => boolean;
|
|
40
|
+
/** Posts the value under this name, for an uncontrolled form. */
|
|
41
|
+
name?: string;
|
|
42
|
+
/** Accessible label for the button that opens the calendar. */
|
|
43
|
+
calendarLabel?: string;
|
|
44
|
+
className?: string;
|
|
45
|
+
wrapperClassName?: string;
|
|
46
|
+
id?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* DatePicker — a segmented date field with a calendar attached.
|
|
50
|
+
*
|
|
51
|
+
* WHY NOT `<input type="date">`
|
|
52
|
+
*
|
|
53
|
+
* The native control is genuinely good on mobile and genuinely unusable as a
|
|
54
|
+
* design system component on desktop: its calendar cannot be styled at all, it
|
|
55
|
+
* cannot express an unavailable date that is not part of a continuous min/max
|
|
56
|
+
* range, and Safari on macOS renders no calendar button whatsoever. A system
|
|
57
|
+
* whose date field looks like a different control in every browser is not a
|
|
58
|
+
* design system, so this rebuilds it — and pays for that by having to rebuild
|
|
59
|
+
* the keyboard handling and the screen-reader semantics the native control gave
|
|
60
|
+
* away for free. React Aria's `useDatePicker` is what builds them back.
|
|
61
|
+
*
|
|
62
|
+
* THE VALUE IS A STRING, THE DISPLAY IS NOT
|
|
63
|
+
*
|
|
64
|
+
* `value` is always `YYYY-MM-DD` in the Gregorian calendar. What the user SEES
|
|
65
|
+
* is their own locale's field order and their own calendar system, resolved
|
|
66
|
+
* from `Intl` — so a German reader edits DD.MM.YYYY and the value still comes
|
|
67
|
+
* back as `2026-04-12`. See `iso-date.ts` for why the public type is not
|
|
68
|
+
* react-aria's `CalendarDate`.
|
|
69
|
+
*
|
|
70
|
+
* THE FIELD IS EDITABLE WITHOUT THE CALENDAR, AND THAT IS THE POINT
|
|
71
|
+
*
|
|
72
|
+
* Typing a date is faster than clicking one for anybody who knows the date they
|
|
73
|
+
* want, and paging a calendar back to a birth year is miserable. The segments
|
|
74
|
+
* take digits and arrow keys directly; the calendar is for the cases where the
|
|
75
|
+
* date is being chosen rather than recalled.
|
|
76
|
+
*/
|
|
77
|
+
export declare function DatePicker({ label, description, errorMessage, isInvalid, isDisabled, isReadOnly, isRequired, size, value, defaultValue, onChange, minValue, maxValue, isDateUnavailable, name, calendarLabel, className, wrapperClassName, id, }: DatePickerProps): React.JSX.Element;
|
|
78
|
+
export declare namespace DatePicker {
|
|
79
|
+
var displayName: string;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=DatePicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../src/components/DatePicker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAOtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAchD,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,mCAAmC;IACnC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACvB,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;IAC/C,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,IAAW,EACX,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,IAAI,EACJ,aAA+B,EAC/B,SAAS,EACT,gBAAgB,EAChB,EAAE,GACH,EAAE,eAAe,qBAiJjB;yBArKe,UAAU"}
|