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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from 'react';
|
|
4
|
+
import { useDatePicker, useButton } from 'react-aria';
|
|
5
|
+
import { useDatePickerState } from 'react-stately';
|
|
6
|
+
import { DateField } from './DateField.js';
|
|
7
|
+
import { SingleCalendar } from './Calendar.js';
|
|
8
|
+
import { CalendarPopover } from './CalendarPopover.js';
|
|
9
|
+
import { toCalendarDate, toIso, wrapUnavailable } from './iso-date.js';
|
|
10
|
+
const CalendarIcon = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M8 2v4m8-4v4M3 10h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
11
|
+
/**
|
|
12
|
+
* DatePicker — a segmented date field with a calendar attached.
|
|
13
|
+
*
|
|
14
|
+
* WHY NOT `<input type="date">`
|
|
15
|
+
*
|
|
16
|
+
* The native control is genuinely good on mobile and genuinely unusable as a
|
|
17
|
+
* design system component on desktop: its calendar cannot be styled at all, it
|
|
18
|
+
* cannot express an unavailable date that is not part of a continuous min/max
|
|
19
|
+
* range, and Safari on macOS renders no calendar button whatsoever. A system
|
|
20
|
+
* whose date field looks like a different control in every browser is not a
|
|
21
|
+
* design system, so this rebuilds it — and pays for that by having to rebuild
|
|
22
|
+
* the keyboard handling and the screen-reader semantics the native control gave
|
|
23
|
+
* away for free. React Aria's `useDatePicker` is what builds them back.
|
|
24
|
+
*
|
|
25
|
+
* THE VALUE IS A STRING, THE DISPLAY IS NOT
|
|
26
|
+
*
|
|
27
|
+
* `value` is always `YYYY-MM-DD` in the Gregorian calendar. What the user SEES
|
|
28
|
+
* is their own locale's field order and their own calendar system, resolved
|
|
29
|
+
* from `Intl` — so a German reader edits DD.MM.YYYY and the value still comes
|
|
30
|
+
* back as `2026-04-12`. See `iso-date.ts` for why the public type is not
|
|
31
|
+
* react-aria's `CalendarDate`.
|
|
32
|
+
*
|
|
33
|
+
* THE FIELD IS EDITABLE WITHOUT THE CALENDAR, AND THAT IS THE POINT
|
|
34
|
+
*
|
|
35
|
+
* Typing a date is faster than clicking one for anybody who knows the date they
|
|
36
|
+
* want, and paging a calendar back to a birth year is miserable. The segments
|
|
37
|
+
* take digits and arrow keys directly; the calendar is for the cases where the
|
|
38
|
+
* date is being chosen rather than recalled.
|
|
39
|
+
*/
|
|
40
|
+
export function DatePicker({ label, description, errorMessage, isInvalid, isDisabled, isReadOnly, isRequired, size = 'md', value, defaultValue, onChange, minValue, maxValue, isDateUnavailable, name, calendarLabel = 'Open calendar', className, wrapperClassName, id, }) {
|
|
41
|
+
/*
|
|
42
|
+
* The ISO boundary, crossed once on the way in and once on the way out.
|
|
43
|
+
*
|
|
44
|
+
* `value === undefined` has to survive the conversion as `undefined`, not
|
|
45
|
+
* become `null`. react-aria reads the two differently: `undefined` means
|
|
46
|
+
* uncontrolled and `null` means controlled-and-empty, so collapsing them
|
|
47
|
+
* would make an uncontrolled picker refuse to hold the date it was given.
|
|
48
|
+
*/
|
|
49
|
+
const ariaProps = {
|
|
50
|
+
label: typeof label === 'string' ? label : undefined,
|
|
51
|
+
value: value === undefined ? undefined : toCalendarDate(value, 'value'),
|
|
52
|
+
defaultValue: toCalendarDate(defaultValue, 'defaultValue') ?? undefined,
|
|
53
|
+
minValue: toCalendarDate(minValue, 'minValue') ?? undefined,
|
|
54
|
+
maxValue: toCalendarDate(maxValue, 'maxValue') ?? undefined,
|
|
55
|
+
isDateUnavailable: wrapUnavailable(isDateUnavailable),
|
|
56
|
+
isDisabled,
|
|
57
|
+
isReadOnly,
|
|
58
|
+
isRequired,
|
|
59
|
+
isInvalid,
|
|
60
|
+
/*
|
|
61
|
+
* `granularity: 'day'` is what keeps this a DATE picker. Left unset,
|
|
62
|
+
* react-aria infers granularity from the value's type, and a value of
|
|
63
|
+
* `undefined` infers 'minute' — so an empty picker renders hour and minute
|
|
64
|
+
* segments that disappear the moment a date-only value arrives.
|
|
65
|
+
*/
|
|
66
|
+
granularity: 'day',
|
|
67
|
+
onChange: onChange
|
|
68
|
+
? (date) => onChange(date ? toIso(date) : null)
|
|
69
|
+
: undefined,
|
|
70
|
+
};
|
|
71
|
+
const state = useDatePickerState(ariaProps);
|
|
72
|
+
const groupRef = useRef(null);
|
|
73
|
+
const buttonRef = useRef(null);
|
|
74
|
+
const { groupProps, labelProps, fieldProps, buttonProps: triggerProps, dialogProps, calendarProps, descriptionProps, errorMessageProps, } = useDatePicker({ ...ariaProps, id }, state, groupRef);
|
|
75
|
+
const { buttonProps } = useButton(triggerProps, buttonRef);
|
|
76
|
+
const helper = isInvalid && errorMessage ? errorMessage : description;
|
|
77
|
+
return (_jsxs("div", { className: [
|
|
78
|
+
'ion-field',
|
|
79
|
+
isInvalid ? 'ion-field--error' : '',
|
|
80
|
+
wrapperClassName || '',
|
|
81
|
+
]
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
.join(' '), children: [label && (
|
|
84
|
+
// A <span>, not a <label>. The group is a set of spinbuttons rather
|
|
85
|
+
// than one form control, so `htmlFor` has nothing to point at —
|
|
86
|
+
// `useDatePicker` wires the name through `aria-labelledby` instead.
|
|
87
|
+
_jsx("span", { ...labelProps, className: "ion-field__label", children: label })), _jsxs("div", { ...groupProps, ref: groupRef, className: [
|
|
88
|
+
'ion-input',
|
|
89
|
+
'ion-date-picker',
|
|
90
|
+
size !== 'md' ? `ion-input--${size}` : '',
|
|
91
|
+
isInvalid ? 'ion-input--invalid' : '',
|
|
92
|
+
isDisabled ? 'ion-input--disabled' : '',
|
|
93
|
+
isReadOnly ? 'ion-input--readonly' : '',
|
|
94
|
+
className || '',
|
|
95
|
+
]
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.join(' '), "data-open": state.isOpen || undefined, "data-invalid": isInvalid || undefined, "data-disabled": isDisabled || undefined, children: [_jsx(DateField, { ...fieldProps }), name && (_jsx("input", { type: "hidden", name: name, value: state.value ? toIso(state.value) : '' })), _jsx("button", { ...buttonProps, ref: buttonRef, type: "button", "aria-labelledby": undefined, "aria-label": calendarLabel, className: "ion-date-picker__button", children: _jsx(CalendarIcon, {}) })] }), state.isOpen && !isDisabled && !isReadOnly && (_jsx(CalendarPopover, { state: state, triggerRef: groupRef, dialogProps: dialogProps, children: _jsx(SingleCalendar, { ...calendarProps }) })), helper && (_jsx("span", { ...(isInvalid && errorMessage
|
|
98
|
+
? errorMessageProps
|
|
99
|
+
: descriptionProps), className: "ion-field__helper", children: helper }))] }));
|
|
100
|
+
}
|
|
101
|
+
DatePicker.displayName = 'DatePicker';
|
|
102
|
+
//# sourceMappingURL=DatePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatePicker.js","sourceRoot":"","sources":["../../src/components/DatePicker.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { IsoDate } from './iso-date.js';
|
|
3
|
+
export type DateRangePickerSize = 'sm' | 'md' | 'lg';
|
|
4
|
+
/** A closed interval of calendar dates. `end` is included in the range. */
|
|
5
|
+
export interface DateRange {
|
|
6
|
+
/** `YYYY-MM-DD`. */
|
|
7
|
+
start: IsoDate;
|
|
8
|
+
/** `YYYY-MM-DD`, inclusive. */
|
|
9
|
+
end: IsoDate;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A shortcut in the rail beside the calendar — "Last 7 days", "Year to date".
|
|
13
|
+
*/
|
|
14
|
+
export interface DateRangePreset {
|
|
15
|
+
/** What the row reads. Also its accessible name. */
|
|
16
|
+
label: string;
|
|
17
|
+
/**
|
|
18
|
+
* The range it selects.
|
|
19
|
+
*
|
|
20
|
+
* A FUNCTION for anything relative to today, which is most of them. A literal
|
|
21
|
+
* object is evaluated once, when the caller's module is first imported, so
|
|
22
|
+
* "Last 7 days" built as a literal is seven days before whenever the tab was
|
|
23
|
+
* opened — correct on load and increasingly wrong for a session left open
|
|
24
|
+
* overnight. The function is re-run every time the calendar opens.
|
|
25
|
+
*/
|
|
26
|
+
value: DateRange | (() => DateRange);
|
|
27
|
+
}
|
|
28
|
+
export interface DateRangePickerProps {
|
|
29
|
+
/** Field label. Required for a usable control — see `a11y.requires`. */
|
|
30
|
+
label?: React.ReactNode;
|
|
31
|
+
/** Helper text below the field. */
|
|
32
|
+
description?: React.ReactNode;
|
|
33
|
+
/** Replaces the helper text when `isInvalid` is set. */
|
|
34
|
+
errorMessage?: React.ReactNode;
|
|
35
|
+
isInvalid?: boolean;
|
|
36
|
+
isDisabled?: boolean;
|
|
37
|
+
isReadOnly?: boolean;
|
|
38
|
+
isRequired?: boolean;
|
|
39
|
+
/** Matches Input's `Size` variant: Small, Medium, Large. */
|
|
40
|
+
size?: DateRangePickerSize;
|
|
41
|
+
/**
|
|
42
|
+
* The selected range, both ends as `YYYY-MM-DD`. `null` means no range.
|
|
43
|
+
* A half-open range is not representable on purpose — see `antiPatterns`.
|
|
44
|
+
*/
|
|
45
|
+
value?: DateRange | null;
|
|
46
|
+
/** The initial range, for an uncontrolled picker. */
|
|
47
|
+
defaultValue?: DateRange;
|
|
48
|
+
/** Fires with both ends, or `null` when the range is cleared. */
|
|
49
|
+
onChange?: (value: DateRange | null) => void;
|
|
50
|
+
/** Earliest selectable date, `YYYY-MM-DD`. */
|
|
51
|
+
minValue?: IsoDate;
|
|
52
|
+
/** Latest selectable date, `YYYY-MM-DD`. */
|
|
53
|
+
maxValue?: IsoDate;
|
|
54
|
+
/**
|
|
55
|
+
* Individual dates the calendar refuses. Receives `YYYY-MM-DD`.
|
|
56
|
+
*
|
|
57
|
+
* A range may not be dragged ACROSS one of these — react-aria stops the
|
|
58
|
+
* selection at the first unavailable day, which is what makes "book a room
|
|
59
|
+
* for these nights" correct rather than merely styled.
|
|
60
|
+
*/
|
|
61
|
+
isDateUnavailable?: (date: IsoDate) => boolean;
|
|
62
|
+
/** Shortcut rows beside the calendar. Omitted, no rail is rendered. */
|
|
63
|
+
presets?: readonly DateRangePreset[];
|
|
64
|
+
/**
|
|
65
|
+
* How many months the calendar shows at once. Two by default: a range that
|
|
66
|
+
* crosses a month boundary is the common case, and a one-month calendar
|
|
67
|
+
* makes the user page back and forth to see both ends of their own selection.
|
|
68
|
+
*/
|
|
69
|
+
visibleMonths?: 1 | 2 | 3;
|
|
70
|
+
/** Show a Clear button under the calendar. Defaults to `true`. */
|
|
71
|
+
isClearable?: boolean;
|
|
72
|
+
/** Posts `<name>-start` and `<name>-end` for an uncontrolled form. */
|
|
73
|
+
name?: string;
|
|
74
|
+
/** Accessible label for the button that opens the calendar. */
|
|
75
|
+
calendarLabel?: string;
|
|
76
|
+
clearLabel?: string;
|
|
77
|
+
presetsLabel?: string;
|
|
78
|
+
className?: string;
|
|
79
|
+
wrapperClassName?: string;
|
|
80
|
+
id?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* DateRangePicker — two segmented date fields with one calendar between them.
|
|
84
|
+
*
|
|
85
|
+
* NOT TWO `DatePicker`s SIDE BY SIDE, and the difference is the whole reason
|
|
86
|
+
* this exists. Two independent pickers cannot stop the end date being before
|
|
87
|
+
* the start, cannot show the days between them as a band, and give the user two
|
|
88
|
+
* calendars to reconcile by memory. `useDateRangePicker` owns both ends: the
|
|
89
|
+
* second click completes the range in whichever order the two dates fall, the
|
|
90
|
+
* band follows the pointer while it is being dragged out, and a range cannot be
|
|
91
|
+
* made to run backwards.
|
|
92
|
+
*
|
|
93
|
+
* THE VALUE COMMITS LIVE. THERE IS NO APPLY BUTTON.
|
|
94
|
+
*
|
|
95
|
+
* `onChange` fires the moment the second end lands, and the calendar closes.
|
|
96
|
+
* The reference designs this was drawn from put Clear and Apply under the grid,
|
|
97
|
+
* which is a draft model: the popover holds a pending range and only Apply
|
|
98
|
+
* promotes it. That was considered and rejected. Escape and an outside click
|
|
99
|
+
* would then silently discard a selection the user watched themselves make —
|
|
100
|
+
* and there is no way to tell those two gestures apart from "I am done". A
|
|
101
|
+
* button labelled Apply beside a value that already applied is worse still.
|
|
102
|
+
*
|
|
103
|
+
* Clear stays, because clearing is a real action with no other affordance.
|
|
104
|
+
*
|
|
105
|
+
* BOTH ENDS OR NEITHER
|
|
106
|
+
*
|
|
107
|
+
* `value` has no half-open form. A range with a start and no end is a state the
|
|
108
|
+
* calendar passes THROUGH between the two clicks; it is never a value the
|
|
109
|
+
* caller is handed, because "everything after March" and "March to today" are
|
|
110
|
+
* different questions and a `null` end cannot say which one was meant. A caller
|
|
111
|
+
* who wants an open end passes a sentinel of their own choosing as `maxValue`.
|
|
112
|
+
*/
|
|
113
|
+
export declare function DateRangePicker({ label, description, errorMessage, isInvalid, isDisabled, isReadOnly, isRequired, size, value, defaultValue, onChange, minValue, maxValue, isDateUnavailable, presets, visibleMonths, isClearable, name, calendarLabel, clearLabel, presetsLabel, className, wrapperClassName, id, }: DateRangePickerProps): React.JSX.Element;
|
|
114
|
+
export declare namespace DateRangePicker {
|
|
115
|
+
var displayName: string;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=DateRangePicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateRangePicker.d.ts","sourceRoot":"","sources":["../../src/components/DateRangePicker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA0B,MAAM,OAAO,CAAC;AAO/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,2EAA2E;AAC3E,MAAM,WAAW,SAAS;IACxB,oBAAoB;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,GAAG,EAAE,OAAO,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,KAAK,EAAE,SAAS,GAAG,CAAC,MAAM,SAAS,CAAC,CAAC;CACtC;AAcD,MAAM,WAAW,oBAAoB;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,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,qDAAqD;IACrD,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;IAC/C,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACrC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,eAAe,CAAC,EAC9B,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,OAAO,EACP,aAAiB,EACjB,WAAkB,EAClB,IAAI,EACJ,aAA+B,EAC/B,UAAoB,EACpB,YAAqC,EACrC,SAAS,EACT,gBAAgB,EAChB,EAAE,GACH,EAAE,oBAAoB,qBAuPtB;yBAhRe,eAAe"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
|
+
import { useDateRangePicker, useButton } from 'react-aria';
|
|
5
|
+
import { useDateRangePickerState } from 'react-stately';
|
|
6
|
+
import { DateField } from './DateField.js';
|
|
7
|
+
import { RangeCalendar } from './Calendar.js';
|
|
8
|
+
import { CalendarPopover } from './CalendarPopover.js';
|
|
9
|
+
import { toCalendarDate, toIso, wrapUnavailable } from './iso-date.js';
|
|
10
|
+
const CalendarIcon = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M8 2v4m8-4v4M3 10h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
11
|
+
/** Resolves a preset's `value`, which may be a literal or a function. */
|
|
12
|
+
const resolvePreset = (p) => typeof p.value === 'function' ? p.value() : p.value;
|
|
13
|
+
/**
|
|
14
|
+
* DateRangePicker — two segmented date fields with one calendar between them.
|
|
15
|
+
*
|
|
16
|
+
* NOT TWO `DatePicker`s SIDE BY SIDE, and the difference is the whole reason
|
|
17
|
+
* this exists. Two independent pickers cannot stop the end date being before
|
|
18
|
+
* the start, cannot show the days between them as a band, and give the user two
|
|
19
|
+
* calendars to reconcile by memory. `useDateRangePicker` owns both ends: the
|
|
20
|
+
* second click completes the range in whichever order the two dates fall, the
|
|
21
|
+
* band follows the pointer while it is being dragged out, and a range cannot be
|
|
22
|
+
* made to run backwards.
|
|
23
|
+
*
|
|
24
|
+
* THE VALUE COMMITS LIVE. THERE IS NO APPLY BUTTON.
|
|
25
|
+
*
|
|
26
|
+
* `onChange` fires the moment the second end lands, and the calendar closes.
|
|
27
|
+
* The reference designs this was drawn from put Clear and Apply under the grid,
|
|
28
|
+
* which is a draft model: the popover holds a pending range and only Apply
|
|
29
|
+
* promotes it. That was considered and rejected. Escape and an outside click
|
|
30
|
+
* would then silently discard a selection the user watched themselves make —
|
|
31
|
+
* and there is no way to tell those two gestures apart from "I am done". A
|
|
32
|
+
* button labelled Apply beside a value that already applied is worse still.
|
|
33
|
+
*
|
|
34
|
+
* Clear stays, because clearing is a real action with no other affordance.
|
|
35
|
+
*
|
|
36
|
+
* BOTH ENDS OR NEITHER
|
|
37
|
+
*
|
|
38
|
+
* `value` has no half-open form. A range with a start and no end is a state the
|
|
39
|
+
* calendar passes THROUGH between the two clicks; it is never a value the
|
|
40
|
+
* caller is handed, because "everything after March" and "March to today" are
|
|
41
|
+
* different questions and a `null` end cannot say which one was meant. A caller
|
|
42
|
+
* who wants an open end passes a sentinel of their own choosing as `maxValue`.
|
|
43
|
+
*/
|
|
44
|
+
export function DateRangePicker({ label, description, errorMessage, isInvalid, isDisabled, isReadOnly, isRequired, size = 'md', value, defaultValue, onChange, minValue, maxValue, isDateUnavailable, presets, visibleMonths = 2, isClearable = true, name, calendarLabel = 'Open calendar', clearLabel = 'Clear', presetsLabel = 'Date range shortcuts', className, wrapperClassName, id, }) {
|
|
45
|
+
/*
|
|
46
|
+
* The ISO boundary. `undefined` has to stay `undefined` — react-aria reads it
|
|
47
|
+
* as "uncontrolled" where `null` means "controlled and empty", so collapsing
|
|
48
|
+
* the two makes an uncontrolled picker refuse to hold its own value.
|
|
49
|
+
*/
|
|
50
|
+
const toRange = (r, prop) => r == null
|
|
51
|
+
? null
|
|
52
|
+
: {
|
|
53
|
+
start: toCalendarDate(r.start, `${prop}.start`),
|
|
54
|
+
end: toCalendarDate(r.end, `${prop}.end`),
|
|
55
|
+
};
|
|
56
|
+
const ariaProps = {
|
|
57
|
+
label: typeof label === 'string' ? label : undefined,
|
|
58
|
+
value: value === undefined ? undefined : toRange(value, 'value'),
|
|
59
|
+
defaultValue: toRange(defaultValue, 'defaultValue') ?? undefined,
|
|
60
|
+
minValue: toCalendarDate(minValue, 'minValue') ?? undefined,
|
|
61
|
+
maxValue: toCalendarDate(maxValue, 'maxValue') ?? undefined,
|
|
62
|
+
isDateUnavailable: wrapUnavailable(isDateUnavailable),
|
|
63
|
+
isDisabled,
|
|
64
|
+
isReadOnly,
|
|
65
|
+
isRequired,
|
|
66
|
+
isInvalid,
|
|
67
|
+
/*
|
|
68
|
+
* Same reason as DatePicker: left unset, react-aria infers granularity from
|
|
69
|
+
* the value, and an empty picker infers 'minute' and grows time segments
|
|
70
|
+
* that vanish the moment a date arrives.
|
|
71
|
+
*/
|
|
72
|
+
granularity: 'day',
|
|
73
|
+
onChange: onChange
|
|
74
|
+
? (r) => onChange(r ? { start: toIso(r.start), end: toIso(r.end) } : null)
|
|
75
|
+
: undefined,
|
|
76
|
+
};
|
|
77
|
+
const state = useDateRangePickerState(ariaProps);
|
|
78
|
+
const groupRef = useRef(null);
|
|
79
|
+
const buttonRef = useRef(null);
|
|
80
|
+
const { groupProps, labelProps, startFieldProps, endFieldProps, buttonProps: triggerProps, dialogProps, calendarProps, descriptionProps, errorMessageProps, } = useDateRangePicker({ ...ariaProps, id }, state, groupRef);
|
|
81
|
+
const { buttonProps } = useButton(triggerProps, buttonRef);
|
|
82
|
+
/*
|
|
83
|
+
* Resolved once per open, not once per render.
|
|
84
|
+
*
|
|
85
|
+
* `state.isOpen` is the dependency on purpose. A relative preset is a
|
|
86
|
+
* function of today, so re-resolving it on every render would be correct and
|
|
87
|
+
* wasteful; resolving it once when the module loads would be cheap and wrong
|
|
88
|
+
* by morning. Re-resolving each time the calendar opens is the only cadence
|
|
89
|
+
* at which "Last 7 days" is both stable while the user reads it and accurate
|
|
90
|
+
* when they come back to it.
|
|
91
|
+
*/
|
|
92
|
+
const resolvedPresets = useMemo(() => (presets ?? []).map((p) => ({ label: p.label, ...resolvePreset(p) })),
|
|
93
|
+
// `state.isOpen` is in here as a CADENCE, not as a value this reads —
|
|
94
|
+
// see above. An exhaustive-deps rule would call it extraneous and be
|
|
95
|
+
// wrong: dropping it is what makes a relative preset go stale.
|
|
96
|
+
[presets, state.isOpen]);
|
|
97
|
+
const selectedIso = state.value?.start &&
|
|
98
|
+
state.value?.end && {
|
|
99
|
+
start: toIso(state.value.start),
|
|
100
|
+
end: toIso(state.value.end),
|
|
101
|
+
};
|
|
102
|
+
const helper = isInvalid && errorMessage ? errorMessage : description;
|
|
103
|
+
const rail = resolvedPresets.length ? (_jsx("div", { className: "ion-range-presets", role: "group", "aria-label": presetsLabel, children: resolvedPresets.map((p) => {
|
|
104
|
+
const isSelected = selectedIso?.start === p.start && selectedIso?.end === p.end;
|
|
105
|
+
return (_jsx("button", { type: "button", className: "ion-range-presets__item", "aria-pressed": isSelected, "data-selected": isSelected || undefined, onClick: () => {
|
|
106
|
+
state.setValue({
|
|
107
|
+
start: toCalendarDate(p.start, `preset "${p.label}"`),
|
|
108
|
+
end: toCalendarDate(p.end, `preset "${p.label}"`),
|
|
109
|
+
});
|
|
110
|
+
/*
|
|
111
|
+
* Closed explicitly. A complete range from the GRID closes the
|
|
112
|
+
* calendar on its own, and a preset produces the same complete
|
|
113
|
+
* range — leaving it open after a shortcut was pressed would make
|
|
114
|
+
* the two paths behave differently for no reason the user could
|
|
115
|
+
* see.
|
|
116
|
+
*/
|
|
117
|
+
state.setOpen(false);
|
|
118
|
+
}, children: p.label }, p.label));
|
|
119
|
+
}) })) : null;
|
|
120
|
+
const footer = isClearable && state.value ? (_jsx("div", { className: "ion-range-footer", children: _jsx("button", { type: "button", className: "ion-range-footer__clear", onClick: () => {
|
|
121
|
+
state.setValue(null);
|
|
122
|
+
/*
|
|
123
|
+
* Deliberately left OPEN. Clearing is nearly always the first half
|
|
124
|
+
* of choosing something else; closing the calendar would make the
|
|
125
|
+
* user reopen it to do the thing they cleared it for.
|
|
126
|
+
*/
|
|
127
|
+
}, children: clearLabel }) })) : null;
|
|
128
|
+
return (_jsxs("div", { className: [
|
|
129
|
+
'ion-field',
|
|
130
|
+
isInvalid ? 'ion-field--error' : '',
|
|
131
|
+
wrapperClassName || '',
|
|
132
|
+
]
|
|
133
|
+
.filter(Boolean)
|
|
134
|
+
.join(' '), children: [label && (
|
|
135
|
+
// A <span>, not a <label> — the group holds two fields of spinbuttons
|
|
136
|
+
// rather than one form control, so `htmlFor` has nothing to point at.
|
|
137
|
+
_jsx("span", { ...labelProps, className: "ion-field__label", children: label })), _jsxs("div", { ...groupProps, ref: groupRef, className: [
|
|
138
|
+
'ion-input',
|
|
139
|
+
'ion-date-range-picker',
|
|
140
|
+
size !== 'md' ? `ion-input--${size}` : '',
|
|
141
|
+
isInvalid ? 'ion-input--invalid' : '',
|
|
142
|
+
isDisabled ? 'ion-input--disabled' : '',
|
|
143
|
+
isReadOnly ? 'ion-input--readonly' : '',
|
|
144
|
+
className || '',
|
|
145
|
+
]
|
|
146
|
+
.filter(Boolean)
|
|
147
|
+
.join(' '), "data-open": state.isOpen || undefined, "data-invalid": isInvalid || undefined, "data-disabled": isDisabled || undefined, children: [_jsx(DateField, { ...startFieldProps }), _jsx("span", { className: "ion-date-range-picker__dash", "aria-hidden": "true", children: "\u2013" }), _jsx(DateField, { ...endFieldProps }), name && (_jsxs(_Fragment, { children: [_jsx("input", { type: "hidden", name: `${name}-start`, value: state.value?.start ? toIso(state.value.start) : '' }), _jsx("input", { type: "hidden", name: `${name}-end`, value: state.value?.end ? toIso(state.value.end) : '' })] })), _jsx("button", { ...buttonProps, ref: buttonRef, type: "button", "aria-labelledby": undefined, "aria-label": calendarLabel, className: "ion-date-range-picker__button", children: _jsx(CalendarIcon, {}) })] }), state.isOpen && !isDisabled && !isReadOnly && (_jsx(CalendarPopover, { state: state, triggerRef: groupRef, dialogProps: dialogProps, className: "ion-calendar-popover--range", children: _jsx(RangeCalendar, { ...calendarProps, visibleMonths: visibleMonths, aside: rail, footer: footer }) })), helper && (_jsx("span", { ...(isInvalid && errorMessage
|
|
148
|
+
? errorMessageProps
|
|
149
|
+
: descriptionProps), className: "ion-field__helper", children: helper }))] }));
|
|
150
|
+
}
|
|
151
|
+
DateRangePicker.displayName = 'DateRangePicker';
|
|
152
|
+
//# sourceMappingURL=DateRangePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateRangePicker.js","sourceRoot":"","sources":["../../src/components/DateRangePicker.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAgCvE,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,CACzB,cAAK,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,iBAAa,MAAM,EAAC,SAAS,EAAC,OAAO,YACvE,eACE,CAAC,EAAC,6FAA6F,EAC/F,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,GACtB,GACE,CACP,CAAC;AAyDF,yEAAyE;AACzE,MAAM,aAAa,GAAG,CAAC,CAAkB,EAAa,EAAE,CACtD,OAAO,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,KAAK,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,IAAI,GAAG,IAAI,EACX,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,OAAO,EACP,aAAa,GAAG,CAAC,EACjB,WAAW,GAAG,IAAI,EAClB,IAAI,EACJ,aAAa,GAAG,eAAe,EAC/B,UAAU,GAAG,OAAO,EACpB,YAAY,GAAG,sBAAsB,EACrC,SAAS,EACT,gBAAgB,EAChB,EAAE,GACmB;IACrB;;;;OAIG;IACH,MAAM,OAAO,GAAG,CAAC,CAA+B,EAAE,IAAY,EAAE,EAAE,CAChE,CAAC,IAAI,IAAI;QACP,CAAC,CAAC,IAAI;QACN,CAAC,CAAC;YACE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,IAAI,QAAQ,CAAE;YAChD,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,MAAM,CAAE;SAC3C,CAAC;IAER,MAAM,SAAS,GAAG;QAChB,KAAK,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACpD,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;QAChE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,cAAc,CAAC,IAAI,SAAS;QAChE,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,SAAS;QAC3D,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,SAAS;QAC3D,iBAAiB,EAAE,eAAe,CAAC,iBAAiB,CAAC;QACrD,UAAU;QACV,UAAU;QACV,UAAU;QACV,SAAS;QACT;;;;WAIG;QACH,WAAW,EAAE,KAAc;QAC3B,QAAQ,EAAE,QAAQ;YAChB,CAAC,CAAC,CAAC,CAA8C,EAAE,EAAE,CACjD,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACrE,CAAC,CAAC,SAAS;KACd,CAAC;IAEF,MAAM,KAAK,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;IAElD,MAAM,EACJ,UAAU,EACV,UAAU,EACV,eAAe,EACf,aAAa,EACb,WAAW,EAAE,YAAY,EACzB,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,iBAAiB,GAClB,GAAG,kBAAkB,CAAC,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE9D,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAE3D;;;;;;;;;OASG;IACH,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3E,sEAAsE;IACtE,qEAAqE;IACrE,+DAA+D;IAC/D,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CACxB,CAAC;IAEF,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK;QACpC,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI;QAClB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;QAC/B,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;KAC5B,CAAC;IAEJ,MAAM,MAAM,GAAG,SAAS,IAAI,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC;IAEtE,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CACpC,cAAK,SAAS,EAAC,mBAAmB,EAAC,IAAI,EAAC,OAAO,gBAAa,YAAY,YACrE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACzB,MAAM,UAAU,GACd,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC;YAC/D,OAAO,CACL,iBAEE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,yBAAyB,kBAQrB,UAAU,mBACT,UAAU,IAAI,SAAS,EACtC,OAAO,EAAE,GAAG,EAAE;oBACZ,KAAK,CAAC,QAAQ,CAAC;wBACb,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,KAAK,GAAG,CAAE;wBACtD,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,KAAK,GAAG,CAAE;qBACnD,CAAC,CAAC;oBACH;;;;;;uBAMG;oBACH,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBACvB,CAAC,YAEA,CAAC,CAAC,KAAK,IA3BH,CAAC,CAAC,KAAK,CA4BL,CACV,CAAC;QACJ,CAAC,CAAC,GACE,CACP,CAAC,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,MAAM,GACV,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAC3B,cAAK,SAAS,EAAC,kBAAkB,YAC/B,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,yBAAyB,EACnC,OAAO,EAAE,GAAG,EAAE;gBACZ,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACrB;;;;mBAIG;YACL,CAAC,YAEA,UAAU,GACJ,GACL,CACP,CAAC,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO,CACL,eACE,SAAS,EAAE;YACT,WAAW;YACX,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE;YACnC,gBAAgB,IAAI,EAAE;SACvB;aACE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,GAAG,CAAC,aAEX,KAAK,IAAI;YACR,sEAAsE;YACtE,sEAAsE;YACtE,kBAAU,UAAU,EAAE,SAAS,EAAC,kBAAkB,YAC/C,KAAK,GACD,CACR,EAED,kBACM,UAAU,EACd,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE;oBACT,WAAW;oBACX,uBAAuB;oBACvB,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;oBACzC,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE;oBACrC,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE;oBACvC,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE;oBACvC,SAAS,IAAI,EAAE;iBAChB;qBACE,MAAM,CAAC,OAAO,CAAC;qBACf,IAAI,CAAC,GAAG,CAAC,eACD,KAAK,CAAC,MAAM,IAAI,SAAS,kBACtB,SAAS,IAAI,SAAS,mBACrB,UAAU,IAAI,SAAS,aAEtC,KAAC,SAAS,OAAK,eAAe,GAAI,EAQlC,eAAM,SAAS,EAAC,6BAA6B,iBAAa,MAAM,uBAEzD,EACP,KAAC,SAAS,OAAK,aAAa,GAAI,EAE/B,IAAI,IAAI,CACP,8BACE,gBACE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAE,GAAG,IAAI,QAAQ,EACrB,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GACzD,EACF,gBACE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAE,GAAG,IAAI,MAAM,EACnB,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GACrD,IACD,CACJ,EAED,oBACM,WAAW,EACf,GAAG,EAAE,SAAS,EACd,IAAI,EAAC,QAAQ,qBACI,SAAS,gBACd,aAAa,EACzB,SAAS,EAAC,+BAA+B,YAEzC,KAAC,YAAY,KAAG,GACT,IACL,EAEL,KAAK,CAAC,MAAM,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,CAC7C,KAAC,eAAe,IACd,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,QAAQ,EACpB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAC,6BAA6B,YAEvC,KAAC,aAAa,OACR,aAAa,EACjB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,MAAM,GACd,GACc,CACnB,EAEA,MAAM,IAAI,CACT,kBACM,CAAC,SAAS,IAAI,YAAY;oBAC5B,CAAC,CAAC,iBAAiB;oBACnB,CAAC,CAAC,gBAAgB,CAAC,EACrB,SAAS,EAAC,mBAAmB,YAE5B,MAAM,GACF,CACR,IACG,CACP,CAAC;AACJ,CAAC;AAED,eAAe,CAAC,WAAW,GAAG,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AriaModalOverlayProps } from 'react-aria';
|
|
3
|
+
export type DrawerPlacement = 'start' | 'end' | 'top' | 'bottom';
|
|
4
|
+
export type DrawerSize = 'sm' | 'md' | 'lg';
|
|
5
|
+
export interface DrawerProps extends AriaModalOverlayProps {
|
|
6
|
+
/** Whether the drawer is shown. */
|
|
7
|
+
isOpen?: boolean;
|
|
8
|
+
/** Called with the next open state — `false` when the drawer asks to close. */
|
|
9
|
+
onOpenChange?: (isOpen: boolean) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Which edge it enters from. `start` and `end` follow writing direction, so
|
|
12
|
+
* a right-hand drawer in English is a left-hand one in Arabic without a
|
|
13
|
+
* second variant.
|
|
14
|
+
*/
|
|
15
|
+
placement?: DrawerPlacement;
|
|
16
|
+
size?: DrawerSize;
|
|
17
|
+
/** Required: the dialog's accessible name, wired via `aria-labelledby`. */
|
|
18
|
+
title: React.ReactNode;
|
|
19
|
+
/** Supporting copy under the title. */
|
|
20
|
+
description?: React.ReactNode;
|
|
21
|
+
/** Action row, pinned to the bottom edge. */
|
|
22
|
+
footer?: React.ReactNode;
|
|
23
|
+
showClose?: boolean;
|
|
24
|
+
closeLabel?: string;
|
|
25
|
+
children?: React.ReactNode;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Drawer — a modal panel anchored to an edge.
|
|
30
|
+
*
|
|
31
|
+
* WHY IT IS A MODAL AND NOT A SIDEBAR
|
|
32
|
+
*
|
|
33
|
+
* It takes focus, traps it, closes on Escape and on an outside click, and marks
|
|
34
|
+
* the rest of the page inert — all of `Modal`'s behaviour, differing only in
|
|
35
|
+
* where the panel sits and how it enters. A persistent side panel that does not
|
|
36
|
+
* do those things is layout, not a drawer, and belongs in the page rather than
|
|
37
|
+
* in an overlay.
|
|
38
|
+
*
|
|
39
|
+
* WHEN TO REACH FOR MODAL INSTEAD
|
|
40
|
+
*
|
|
41
|
+
* A drawer suits work that is long or list-shaped — filters, a record's detail,
|
|
42
|
+
* a sequence of settings — because an edge panel can be tall without becoming a
|
|
43
|
+
* square that fights the viewport. A decision, especially a destructive one,
|
|
44
|
+
* belongs in a Modal: it is centred, it is smaller, and it does not invite
|
|
45
|
+
* scrolling past the thing being confirmed.
|
|
46
|
+
*
|
|
47
|
+
* RENDERS NOTHING WHEN CLOSED, and portals when open, for the reasons recorded
|
|
48
|
+
* on Modal: a closed overlay left in the tree is still keyboard-focusable, and
|
|
49
|
+
* an inline one inherits whatever `overflow` and stacking context surrounds it.
|
|
50
|
+
*/
|
|
51
|
+
export declare const Drawer: React.ForwardRefExoticComponent<DrawerProps & React.RefAttributes<HTMLDivElement>>;
|
|
52
|
+
//# sourceMappingURL=Drawer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../src/components/Drawer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAkD,MAAM,OAAO,CAAC;AASvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AACjE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,WAAY,SAAQ,qBAAqB;IACxD,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC;;;;OAIG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2EAA2E;IAC3E,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,uCAAuC;IACvC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,6CAA6C;IAC7C,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkGD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,MAAM,oFAUjB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useRef, useImperativeHandle } from 'react';
|
|
4
|
+
import { Overlay, useModalOverlay, useDialog, useButton, mergeProps, } from 'react-aria';
|
|
5
|
+
import { useOverlayTriggerState } from 'react-stately';
|
|
6
|
+
const CloseGlyph = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M6 6l12 12M18 6L6 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
|
|
7
|
+
/**
|
|
8
|
+
* The panel. Mounted only while open, which is what makes the hooks work.
|
|
9
|
+
*
|
|
10
|
+
* `useDialog` resolves the title id and moves focus in effects keyed on the
|
|
11
|
+
* panel ref — effects that run when the component CALLING them mounts. Called
|
|
12
|
+
* beside `useOverlayTriggerState` in `Drawer` below, that moment would be when
|
|
13
|
+
* the trigger mounts, with this panel not yet in the DOM: no `aria-labelledby`,
|
|
14
|
+
* and focus never entering the drawer, so Escape never reaches the key handler.
|
|
15
|
+
* Silent in every case. AGENTS.md records the same split for Modal and Popover.
|
|
16
|
+
*/
|
|
17
|
+
function DrawerDialog(props, forwardedRef) {
|
|
18
|
+
const { state, placement = 'end', size = 'md', title, description, footer, showClose = true, closeLabel = 'Close drawer', children, className, } = props;
|
|
19
|
+
const panelRef = useRef(null);
|
|
20
|
+
useImperativeHandle(forwardedRef, () => panelRef.current);
|
|
21
|
+
const { modalProps, underlayProps } = useModalOverlay(props, state, panelRef);
|
|
22
|
+
const { dialogProps, titleProps } = useDialog({ role: 'dialog' }, panelRef);
|
|
23
|
+
const closeRef = useRef(null);
|
|
24
|
+
const { buttonProps } = useButton({ onPress: () => state.close(), 'aria-label': closeLabel }, closeRef);
|
|
25
|
+
const panelClassNames = [
|
|
26
|
+
'ion-drawer',
|
|
27
|
+
`ion-drawer--${placement}`,
|
|
28
|
+
`ion-drawer--${size}`,
|
|
29
|
+
className || '',
|
|
30
|
+
]
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.join(' ');
|
|
33
|
+
return (_jsx("div", { ...underlayProps, className: "ion-drawer__scrim", children: _jsxs("div", { ...mergeProps(modalProps, dialogProps), ref: panelRef, className: panelClassNames, children: [_jsxs("div", { className: "ion-drawer__header", children: [_jsxs("div", { className: "ion-drawer__heading", children: [_jsx("h2", { ...titleProps, className: "ion-drawer__title", children: title }), description && (_jsx("p", { className: "ion-drawer__description", children: description }))] }), showClose && (_jsx("button", { ...buttonProps, ref: closeRef, type: "button", className: "ion-drawer__close", children: _jsx(CloseGlyph, {}) }))] }), children && _jsx("div", { className: "ion-drawer__body", children: children }), footer && _jsx("div", { className: "ion-drawer__footer", children: footer })] }) }));
|
|
34
|
+
}
|
|
35
|
+
const DrawerDialogWithRef = forwardRef(DrawerDialog);
|
|
36
|
+
/**
|
|
37
|
+
* Drawer — a modal panel anchored to an edge.
|
|
38
|
+
*
|
|
39
|
+
* WHY IT IS A MODAL AND NOT A SIDEBAR
|
|
40
|
+
*
|
|
41
|
+
* It takes focus, traps it, closes on Escape and on an outside click, and marks
|
|
42
|
+
* the rest of the page inert — all of `Modal`'s behaviour, differing only in
|
|
43
|
+
* where the panel sits and how it enters. A persistent side panel that does not
|
|
44
|
+
* do those things is layout, not a drawer, and belongs in the page rather than
|
|
45
|
+
* in an overlay.
|
|
46
|
+
*
|
|
47
|
+
* WHEN TO REACH FOR MODAL INSTEAD
|
|
48
|
+
*
|
|
49
|
+
* A drawer suits work that is long or list-shaped — filters, a record's detail,
|
|
50
|
+
* a sequence of settings — because an edge panel can be tall without becoming a
|
|
51
|
+
* square that fights the viewport. A decision, especially a destructive one,
|
|
52
|
+
* belongs in a Modal: it is centred, it is smaller, and it does not invite
|
|
53
|
+
* scrolling past the thing being confirmed.
|
|
54
|
+
*
|
|
55
|
+
* RENDERS NOTHING WHEN CLOSED, and portals when open, for the reasons recorded
|
|
56
|
+
* on Modal: a closed overlay left in the tree is still keyboard-focusable, and
|
|
57
|
+
* an inline one inherits whatever `overflow` and stacking context surrounds it.
|
|
58
|
+
*/
|
|
59
|
+
export const Drawer = forwardRef((props, ref) => {
|
|
60
|
+
const state = useOverlayTriggerState(props);
|
|
61
|
+
if (!state.isOpen)
|
|
62
|
+
return null;
|
|
63
|
+
return (_jsx(Overlay, { children: _jsx(DrawerDialogWithRef, { ...props, state: state, ref: ref }) }));
|
|
64
|
+
});
|
|
65
|
+
Drawer.displayName = 'Drawer';
|
|
66
|
+
//# sourceMappingURL=Drawer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Drawer.js","sourceRoot":"","sources":["../../src/components/Drawer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type FileUploadSize = 'sm' | 'md';
|
|
3
|
+
/** A file the control refused, and the reason, so the caller can say so. */
|
|
4
|
+
export interface RejectedFile {
|
|
5
|
+
file: File;
|
|
6
|
+
reason: 'type' | 'size' | 'count';
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
export interface FileUploadProps {
|
|
10
|
+
/** Field label. Also the accessible name of the file input. */
|
|
11
|
+
label?: React.ReactNode;
|
|
12
|
+
/** Helper text below the control. */
|
|
13
|
+
description?: React.ReactNode;
|
|
14
|
+
/** Replaces the helper text when `isInvalid` is set. */
|
|
15
|
+
errorMessage?: React.ReactNode;
|
|
16
|
+
isInvalid?: boolean;
|
|
17
|
+
isDisabled?: boolean;
|
|
18
|
+
/** Matches the Figma `Size` variant when drawn. Height of the drop target. */
|
|
19
|
+
size?: FileUploadSize;
|
|
20
|
+
/** Native `accept` — a comma-separated list of extensions or MIME types. */
|
|
21
|
+
accept?: string;
|
|
22
|
+
/** Allow more than one file. */
|
|
23
|
+
multiple?: boolean;
|
|
24
|
+
/** Reject anything larger, in bytes. */
|
|
25
|
+
maxSize?: number;
|
|
26
|
+
/** Reject once this many files are held. Only meaningful with `multiple`. */
|
|
27
|
+
maxFiles?: number;
|
|
28
|
+
/**
|
|
29
|
+
* The files held. Pass it to control the list; omit and the component keeps
|
|
30
|
+
* its own. A controlled list is the usual case, because the caller is the
|
|
31
|
+
* only thing that knows which uploads have since succeeded or failed.
|
|
32
|
+
*/
|
|
33
|
+
files?: readonly File[];
|
|
34
|
+
/** Fires with the full list after every add or remove, never with a delta. */
|
|
35
|
+
onChange?: (files: File[]) => void;
|
|
36
|
+
/** Fires with anything refused by `accept`, `maxSize` or `maxFiles`. */
|
|
37
|
+
onReject?: (rejected: RejectedFile[]) => void;
|
|
38
|
+
/** Call-to-action inside the drop target. */
|
|
39
|
+
prompt?: React.ReactNode;
|
|
40
|
+
/** Second line inside the drop target — the constraints, in words. */
|
|
41
|
+
hint?: React.ReactNode;
|
|
42
|
+
/** Accessible label for each file's remove button. Receives the file name. */
|
|
43
|
+
removeLabel?: (name: string) => string;
|
|
44
|
+
className?: string;
|
|
45
|
+
wrapperClassName?: string;
|
|
46
|
+
id?: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* FileUpload — a drop target wrapped around a real `<input type="file">`.
|
|
51
|
+
*
|
|
52
|
+
* Drawn in Figma as `File Upload` (1367:2333) — two sizes by five states. The
|
|
53
|
+
* measurements came from `Input` and `EmptyState` rather than being invented:
|
|
54
|
+
* the border, radius and disabled treatment are Input's; the centred
|
|
55
|
+
* icon-over-text stack is EmptyState's. Figma draws the drop zone alone, the
|
|
56
|
+
* same split `Input` makes against `Form Field`.
|
|
57
|
+
*
|
|
58
|
+
* THE INPUT IS THE CONTROL. THE DROP ZONE IS DECORATION.
|
|
59
|
+
*
|
|
60
|
+
* The usual build of this component is a `<div>` with drag handlers and a
|
|
61
|
+
* click that calls `input.click()`. That version cannot be reached by keyboard,
|
|
62
|
+
* has no accessible name, does not participate in a form, and does not work in
|
|
63
|
+
* any environment without a pointer — which includes switch access, voice
|
|
64
|
+
* control and most screen-reader browse modes.
|
|
65
|
+
*
|
|
66
|
+
* Here the file input is a real, focusable, labelled control that is visually
|
|
67
|
+
* hidden but NOT `display: none` — it keeps its place in the tab order and its
|
|
68
|
+
* label. Drag-and-drop is layered on top as an enhancement, and every path it
|
|
69
|
+
* offers is also reachable without it. That ordering is the whole component.
|
|
70
|
+
*
|
|
71
|
+
* VALIDATION IS ADVISORY, NOT SECURITY. `accept` and `maxSize` are checked here
|
|
72
|
+
* so the user finds out immediately instead of after an upload. A server that
|
|
73
|
+
* trusts either one is trusting a value the client chose.
|
|
74
|
+
*/
|
|
75
|
+
export declare function FileUpload({ label, description, errorMessage, isInvalid, isDisabled: isDisabledProp, size, accept, multiple, maxSize, maxFiles, files: controlledFiles, onChange, onReject, prompt, hint, removeLabel, className, wrapperClassName, id: providedId, name: inputName, }: FileUploadProps): React.JSX.Element;
|
|
76
|
+
export declare namespace FileUpload {
|
|
77
|
+
var displayName: string;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=FileUpload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileUpload.d.ts","sourceRoot":"","sources":["../../src/components/FileUpload.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAEpE,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;AAEzC,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,qCAAqC;IACrC,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,8EAA8E;IAC9E,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAC9C,6CAA6C;IAC7C,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,sEAAsE;IACtE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAmED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EAAE,cAAc,EAC1B,IAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,KAAK,EAAE,eAAe,EACtB,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,WAAwC,EACxC,SAAS,EACT,gBAAgB,EAChB,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,SAAS,GAChB,EAAE,eAAe,qBAuPjB;yBA5Qe,UAAU"}
|