@yarcl/react 0.1.0 → 0.2.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/README.md +14 -2
- package/{src/apply.ts → dist/apply.d.ts} +4 -23
- package/dist/apply.js +37 -0
- package/dist/classes.d.ts +13 -0
- package/dist/classes.js +22 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +58 -0
- package/dist/color.js +49 -32
- package/dist/components/Accordion.d.ts +96 -0
- package/dist/components/Accordion.js +148 -0
- package/dist/components/Alert.d.ts +60 -0
- package/dist/components/Alert.js +61 -0
- package/dist/components/Avatar.d.ts +100 -0
- package/dist/components/Avatar.js +89 -0
- package/dist/components/Badge.d.ts +44 -0
- package/dist/components/Badge.js +43 -0
- package/dist/components/Breadcrumb.d.ts +77 -0
- package/dist/components/Breadcrumb.js +126 -0
- package/dist/components/Button.d.ts +18 -0
- package/dist/components/Button.js +33 -0
- package/dist/components/ButtonGroup.d.ts +29 -0
- package/dist/components/ButtonGroup.js +37 -0
- package/dist/components/Card.d.ts +36 -0
- package/dist/components/Card.js +26 -0
- package/dist/components/Checkbox.d.ts +28 -0
- package/dist/components/Checkbox.js +38 -0
- package/dist/components/Combobox.d.ts +75 -0
- package/dist/components/Combobox.js +362 -0
- package/dist/components/CommandPalette.d.ts +92 -0
- package/dist/components/CommandPalette.js +293 -0
- package/dist/components/DatePicker.d.ts +157 -0
- package/dist/components/DatePicker.js +230 -0
- package/dist/components/Dialog.d.ts +25 -0
- package/dist/components/Dialog.js +29 -0
- package/dist/components/Divider.d.ts +23 -0
- package/dist/components/Divider.js +25 -0
- package/dist/components/Drawer.d.ts +21 -0
- package/dist/components/Drawer.js +25 -0
- package/dist/components/EmptyState.d.ts +51 -0
- package/dist/components/EmptyState.js +47 -0
- package/dist/components/Field.d.ts +27 -0
- package/dist/components/Field.js +59 -0
- package/dist/components/Heading.d.ts +26 -0
- package/dist/components/Heading.js +25 -0
- package/dist/components/HoverCard.d.ts +47 -0
- package/dist/components/HoverCard.js +53 -0
- package/dist/components/IconButton.d.ts +19 -0
- package/dist/components/IconButton.js +32 -0
- package/dist/components/Inline.d.ts +21 -0
- package/dist/components/Inline.js +24 -0
- package/dist/components/Input.d.ts +19 -0
- package/dist/components/Input.js +25 -0
- package/dist/components/Label.d.ts +46 -0
- package/dist/components/Label.js +33 -0
- package/dist/components/Link.d.ts +27 -0
- package/dist/components/Link.js +26 -0
- package/dist/components/Menu.d.ts +73 -0
- package/dist/components/Menu.js +143 -0
- package/dist/components/Modal.d.ts +45 -0
- package/dist/components/Modal.js +110 -0
- package/dist/components/NumberInput.d.ts +49 -0
- package/dist/components/NumberInput.js +130 -0
- package/dist/components/Pagination.d.ts +86 -0
- package/dist/components/Pagination.js +119 -0
- package/dist/components/Popover.d.ts +72 -0
- package/dist/components/Popover.js +87 -0
- package/dist/components/Progress.d.ts +60 -0
- package/dist/components/Progress.js +59 -0
- package/dist/components/Radio.d.ts +32 -0
- package/dist/components/Radio.js +47 -0
- package/dist/components/RadioGroup.d.ts +57 -0
- package/dist/components/RadioGroup.js +68 -0
- package/dist/components/Select.d.ts +47 -0
- package/dist/components/Select.js +138 -0
- package/dist/components/Skeleton.d.ts +46 -0
- package/dist/components/Skeleton.js +40 -0
- package/dist/components/Slider.d.ts +126 -0
- package/dist/components/Slider.js +166 -0
- package/dist/components/Spinner.d.ts +23 -0
- package/dist/components/Spinner.js +23 -0
- package/dist/components/Stack.d.ts +35 -0
- package/dist/components/Stack.js +24 -0
- package/dist/components/Switch.d.ts +27 -0
- package/dist/components/Switch.js +33 -0
- package/dist/components/Table.d.ts +71 -0
- package/dist/components/Table.js +61 -0
- package/dist/components/Tabs.d.ts +66 -0
- package/dist/components/Tabs.js +109 -0
- package/dist/components/Text.d.ts +32 -0
- package/dist/components/Text.js +28 -0
- package/dist/components/Textarea.d.ts +15 -0
- package/dist/components/Textarea.js +24 -0
- package/dist/components/Toast.d.ts +90 -0
- package/dist/components/Toast.js +156 -0
- package/dist/components/ToggleGroup.d.ts +79 -0
- package/dist/components/ToggleGroup.js +101 -0
- package/dist/components/Tooltip.d.ts +46 -0
- package/dist/components/Tooltip.js +62 -0
- package/dist/components/VisuallyHidden.d.ts +18 -0
- package/dist/components/VisuallyHidden.js +22 -0
- package/dist/contrast.d.ts +32 -0
- package/dist/css.d.ts +2 -2
- package/dist/css.js +193 -157
- package/dist/define.d.ts +40 -15
- package/dist/define.js +30 -27
- package/dist/demo.d.ts +2 -0
- package/dist/demo.js +2 -0
- package/dist/esbuild.cjs +5 -0
- package/dist/esbuild.d.ts +4 -0
- package/dist/esbuild.js +6 -0
- package/dist/field-context.d.ts +16 -0
- package/dist/field-context.js +17 -0
- package/dist/floating.d.ts +12 -0
- package/dist/floating.js +52 -0
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.js +12 -0
- package/{src/index.ts → dist/index.d.ts} +11 -33
- package/dist/index.js +50 -0
- package/dist/init.d.ts +18 -0
- package/dist/init.js +224 -0
- package/dist/plugin.d.ts +10 -26
- package/dist/plugin.js +152 -78
- package/dist/reference/DemoYarcl.d.ts +12 -0
- package/dist/reference/DemoYarcl.js +312 -0
- package/dist/reference/DesignReference.d.ts +24 -0
- package/dist/reference/DesignReference.js +403 -0
- package/dist/reference/index.js +2 -0
- package/dist/rollup.cjs +5 -0
- package/dist/rollup.d.ts +4 -0
- package/dist/rollup.js +6 -0
- package/dist/rspack.cjs +5 -0
- package/dist/rspack.d.ts +4 -0
- package/dist/rspack.js +6 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.js +30 -0
- package/dist/setup.d.ts +6 -0
- package/dist/setup.js +33 -0
- package/{src → dist}/styles.css +224 -106
- package/dist/themes/bloom.d.ts +295 -0
- package/dist/themes/bloom.js +202 -0
- package/dist/themes/brutalist.d.ts +277 -0
- package/dist/themes/brutalist.js +199 -0
- package/dist/themes/compact.d.ts +274 -0
- package/dist/themes/compact.js +200 -0
- package/dist/themes/contract.d.ts +23 -0
- package/dist/themes/editorial.d.ts +277 -0
- package/dist/themes/editorial.js +183 -0
- package/dist/themes/index.d.ts +1425 -0
- package/dist/themes/index.js +45 -0
- package/{src/types.ts → dist/types.d.ts} +40 -37
- package/dist/vite.cjs +5 -0
- package/dist/vite.d.ts +4 -0
- package/dist/vite.js +6 -0
- package/dist/webpack.cjs +5 -0
- package/dist/webpack.d.ts +4 -0
- package/dist/webpack.js +6 -0
- package/dist/yarcl.config.d.ts +276 -0
- package/dist/yarcl.config.js +273 -0
- package/package.json +69 -17
- package/src/classes.ts +0 -23
- package/src/color.ts +0 -48
- package/src/components/Accordion.tsx +0 -274
- package/src/components/Alert.tsx +0 -85
- package/src/components/Badge.tsx +0 -79
- package/src/components/Breadcrumb.tsx +0 -239
- package/src/components/Button.tsx +0 -58
- package/src/components/ButtonGroup.tsx +0 -61
- package/src/components/Card.tsx +0 -50
- package/src/components/Checkbox.tsx +0 -49
- package/src/components/Combobox.tsx +0 -522
- package/src/components/CommandPalette.tsx +0 -452
- package/src/components/DatePicker.tsx +0 -432
- package/src/components/Dialog.tsx +0 -39
- package/src/components/Divider.tsx +0 -34
- package/src/components/Drawer.tsx +0 -31
- package/src/components/Field.tsx +0 -61
- package/src/components/Heading.tsx +0 -40
- package/src/components/HoverCard.tsx +0 -108
- package/src/components/IconButton.tsx +0 -59
- package/src/components/Inline.tsx +0 -42
- package/src/components/Input.tsx +0 -28
- package/src/components/Label.tsx +0 -58
- package/src/components/Link.tsx +0 -41
- package/src/components/Menu.tsx +0 -207
- package/src/components/Modal.tsx +0 -164
- package/src/components/NumberInput.tsx +0 -208
- package/src/components/Pagination.tsx +0 -227
- package/src/components/Popover.tsx +0 -140
- package/src/components/Progress.tsx +0 -122
- package/src/components/Radio.tsx +0 -59
- package/src/components/RadioGroup.tsx +0 -114
- package/src/components/Select.tsx +0 -197
- package/src/components/Skeleton.tsx +0 -95
- package/src/components/Slider.tsx +0 -315
- package/src/components/Spinner.tsx +0 -38
- package/src/components/Stack.tsx +0 -57
- package/src/components/Switch.tsx +0 -42
- package/src/components/Table.tsx +0 -99
- package/src/components/Tabs.tsx +0 -159
- package/src/components/Text.tsx +0 -67
- package/src/components/Textarea.tsx +0 -26
- package/src/components/Toast.tsx +0 -190
- package/src/components/ToggleGroup.tsx +0 -227
- package/src/components/Tooltip.tsx +0 -78
- package/src/contrast.ts +0 -82
- package/src/css.ts +0 -228
- package/src/define.check.ts +0 -259
- package/src/define.ts +0 -379
- package/src/field-context.ts +0 -31
- package/src/floating.tsx +0 -61
- package/src/hooks.ts +0 -19
- package/src/plugin.ts +0 -94
- package/src/reference/DesignReference.tsx +0 -374
- package/src/runtime.ts +0 -38
- package/src/themes/bloom.ts +0 -72
- package/src/themes/brutalist.ts +0 -63
- package/src/themes/compact.ts +0 -63
- package/src/themes/contract.ts +0 -24
- package/src/themes/editorial.ts +0 -55
- package/src/themes/index.ts +0 -47
- package/src/yarcl.config.ts +0 -104
- /package/{src/reference/index.ts → dist/reference/index.d.ts} +0 -0
- /package/{src → dist}/reference/reference.css +0 -0
|
@@ -1,432 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
addDays,
|
|
3
|
-
addMonths,
|
|
4
|
-
addYears,
|
|
5
|
-
endOfMonth,
|
|
6
|
-
endOfWeek,
|
|
7
|
-
format,
|
|
8
|
-
isAfter,
|
|
9
|
-
isBefore,
|
|
10
|
-
isSameDay,
|
|
11
|
-
isSameMonth,
|
|
12
|
-
isToday,
|
|
13
|
-
startOfDay,
|
|
14
|
-
startOfMonth,
|
|
15
|
-
startOfWeek,
|
|
16
|
-
type Day,
|
|
17
|
-
type Locale,
|
|
18
|
-
} from 'date-fns';
|
|
19
|
-
import { enUS } from 'date-fns/locale/en-US';
|
|
20
|
-
import { useContext, useEffect, useId, useRef, useState, type ComponentProps, type KeyboardEvent, type ReactNode } from 'react';
|
|
21
|
-
import { colorClass, cx, radiusClass, sizeClass, softVariantClass, variantClass } from '../classes';
|
|
22
|
-
import { FieldContext, useFieldProps } from '../field-context';
|
|
23
|
-
import { useControllable } from '../hooks';
|
|
24
|
-
import { useDefaults } from '../runtime';
|
|
25
|
-
import type { TokenProps, VariantProps } from '../types';
|
|
26
|
-
import { Popover } from './Popover';
|
|
27
|
-
|
|
28
|
-
/** A date range picked with a {@link DatePicker} in `mode="range"`. `to` is `null` while only the start is picked. */
|
|
29
|
-
export interface DateRange {
|
|
30
|
-
/** First day of the range. */
|
|
31
|
-
from: Date;
|
|
32
|
-
/** Last day of the range, or `null` until the second day is picked. */
|
|
33
|
-
to: Date | null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Text for the parts of a {@link DatePicker} that have no visible label. */
|
|
37
|
-
export interface DatePickerLabels {
|
|
38
|
-
/** Accessible name of the calendar popover. */
|
|
39
|
-
dialog: string;
|
|
40
|
-
/** Accessible name of the previous month button. */
|
|
41
|
-
previousMonth: string;
|
|
42
|
-
/** Accessible name of the next month button. */
|
|
43
|
-
nextMonth: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Props shared by both modes of {@link DatePicker}. */
|
|
47
|
-
export interface DatePickerBaseProps
|
|
48
|
-
extends TokenProps,
|
|
49
|
-
VariantProps,
|
|
50
|
-
Omit<ComponentProps<'button'>, 'color' | 'value' | 'defaultValue' | 'onChange' | 'children' | 'type' | 'name'> {
|
|
51
|
-
/**
|
|
52
|
-
* Earliest day that can be picked. Earlier days are shown disabled and the calendar can't move before its month.
|
|
53
|
-
* @example
|
|
54
|
-
* ```tsx
|
|
55
|
-
* <DatePicker min={new Date()} />
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
min?: Date;
|
|
59
|
-
/**
|
|
60
|
-
* Latest day that can be picked.
|
|
61
|
-
* @example
|
|
62
|
-
* ```tsx
|
|
63
|
-
* <DatePicker max={addYears(new Date(), 1)} />
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
max?: Date;
|
|
67
|
-
/**
|
|
68
|
-
* Returns `true` for days that can't be picked, e.g. weekends or booked dates. They stay focusable but can't be chosen.
|
|
69
|
-
* @example
|
|
70
|
-
* ```tsx
|
|
71
|
-
* <DatePicker isDateDisabled={isWeekend} />
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
isDateDisabled?: (date: Date) => boolean;
|
|
75
|
-
/**
|
|
76
|
-
* A date-fns locale. Sets month and weekday names, the display format and the first day of the week.
|
|
77
|
-
* @default enUS
|
|
78
|
-
* @example
|
|
79
|
-
* ```tsx
|
|
80
|
-
* import { de } from 'date-fns/locale';
|
|
81
|
-
* <DatePicker locale={de} />
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
|
-
locale?: Locale;
|
|
85
|
-
/**
|
|
86
|
-
* First day of the week, from 0 (Sunday) to 6 (Saturday).
|
|
87
|
-
* @default the locale's first day of the week
|
|
88
|
-
* @example
|
|
89
|
-
* ```tsx
|
|
90
|
-
* <DatePicker weekStartsOn={1} />
|
|
91
|
-
* ```
|
|
92
|
-
*/
|
|
93
|
-
weekStartsOn?: Day;
|
|
94
|
-
/**
|
|
95
|
-
* A date-fns `format` pattern for the value shown in the trigger.
|
|
96
|
-
* @default 'PP'
|
|
97
|
-
* @example
|
|
98
|
-
* ```tsx
|
|
99
|
-
* <DatePicker displayFormat="yyyy-MM-dd" />
|
|
100
|
-
* ```
|
|
101
|
-
*/
|
|
102
|
-
displayFormat?: string;
|
|
103
|
-
/**
|
|
104
|
-
* Text shown when nothing is picked.
|
|
105
|
-
* @example
|
|
106
|
-
* ```tsx
|
|
107
|
-
* <DatePicker placeholder="Pick a date" />
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
placeholder?: ReactNode;
|
|
111
|
-
/**
|
|
112
|
-
* Form field name. Renders a hidden input with the value as `yyyy-MM-dd`, or `yyyy-MM-dd/yyyy-MM-dd` for a range.
|
|
113
|
-
* @example
|
|
114
|
-
* ```tsx
|
|
115
|
-
* <DatePicker name="birthday" />
|
|
116
|
-
* ```
|
|
117
|
-
*/
|
|
118
|
-
name?: string;
|
|
119
|
-
/**
|
|
120
|
-
* Accessible names for the popover and the month buttons, for translation.
|
|
121
|
-
* @default { dialog: 'Choose date', previousMonth: 'Previous month', nextMonth: 'Next month' }
|
|
122
|
-
* @example
|
|
123
|
-
* ```tsx
|
|
124
|
-
* <DatePicker labels={{ dialog: 'Datum wählen', previousMonth: 'Vorheriger Monat', nextMonth: 'Nächster Monat' }} />
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
labels?: Partial<DatePickerLabels>;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Props for {@link DatePicker} picking one day. */
|
|
131
|
-
export interface DatePickerSingleProps extends DatePickerBaseProps {
|
|
132
|
-
/**
|
|
133
|
-
* Picks one day.
|
|
134
|
-
* @default 'single'
|
|
135
|
-
*/
|
|
136
|
-
mode?: 'single';
|
|
137
|
-
/** Controlled value. `null` means nothing is picked. */
|
|
138
|
-
value?: Date | null;
|
|
139
|
-
/**
|
|
140
|
-
* Initial value when uncontrolled.
|
|
141
|
-
* @default null
|
|
142
|
-
*/
|
|
143
|
-
defaultValue?: Date | null;
|
|
144
|
-
/**
|
|
145
|
-
* Called with the picked day.
|
|
146
|
-
* @example
|
|
147
|
-
* ```tsx
|
|
148
|
-
* <DatePicker value={date} onValueChange={setDate} />
|
|
149
|
-
* ```
|
|
150
|
-
*/
|
|
151
|
-
onValueChange?: (value: Date | null) => void;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Props for {@link DatePicker} picking a range of days. */
|
|
155
|
-
export interface DatePickerRangeProps extends DatePickerBaseProps {
|
|
156
|
-
/** Picks a start and an end day. */
|
|
157
|
-
mode: 'range';
|
|
158
|
-
/** Controlled value. `null` means nothing is picked. */
|
|
159
|
-
value?: DateRange | null;
|
|
160
|
-
/**
|
|
161
|
-
* Initial value when uncontrolled.
|
|
162
|
-
* @default null
|
|
163
|
-
*/
|
|
164
|
-
defaultValue?: DateRange | null;
|
|
165
|
-
/**
|
|
166
|
-
* Called when the start is picked (with `to: null`) and again when the range is complete.
|
|
167
|
-
* @example
|
|
168
|
-
* ```tsx
|
|
169
|
-
* <DatePicker mode="range" value={range} onValueChange={setRange} />
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
|
-
onValueChange?: (value: DateRange | null) => void;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** Props for {@link DatePicker}. `mode` decides whether one day or a range is picked. */
|
|
176
|
-
export type DatePickerProps = DatePickerSingleProps | DatePickerRangeProps;
|
|
177
|
-
|
|
178
|
-
const defaultLabels: DatePickerLabels = { dialog: 'Choose date', previousMonth: 'Previous month', nextMonth: 'Next month' };
|
|
179
|
-
|
|
180
|
-
function Icon({ d }: { d: string }) {
|
|
181
|
-
return (
|
|
182
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
183
|
-
<path d={d} />
|
|
184
|
-
</svg>
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const calendarPath = 'M8 3v4M16 3v4M4 10h16M5 5h14a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z';
|
|
189
|
-
const iso = (date: Date) => format(date, 'yyyy-MM-dd');
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Picks a day or a range of days from a calendar popover. Built on {@link Popover}, with dates handled by date-fns.
|
|
193
|
-
* The trigger shares the size scale with {@link Input}. Picked days use `variant`, days inside a range use
|
|
194
|
-
* `defaults.softVariant`. The calendar follows the WAI-ARIA date picker dialog pattern. Works inside a {@link Field}.
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* ```tsx
|
|
198
|
-
* <Field label="Check-in">
|
|
199
|
-
* <DatePicker value={date} onValueChange={setDate} min={new Date()} placeholder="Pick a date" />
|
|
200
|
-
* </Field>
|
|
201
|
-
* ```
|
|
202
|
-
*/
|
|
203
|
-
export function DatePicker(props: DatePickerProps) {
|
|
204
|
-
const own = useDefaults('DatePicker');
|
|
205
|
-
const field = useContext(FieldContext);
|
|
206
|
-
const {
|
|
207
|
-
mode = 'single',
|
|
208
|
-
value: valueProp,
|
|
209
|
-
defaultValue = null,
|
|
210
|
-
onValueChange,
|
|
211
|
-
min,
|
|
212
|
-
max,
|
|
213
|
-
isDateDisabled,
|
|
214
|
-
locale = enUS,
|
|
215
|
-
weekStartsOn,
|
|
216
|
-
displayFormat = 'PP',
|
|
217
|
-
placeholder,
|
|
218
|
-
name,
|
|
219
|
-
labels: labelsProp,
|
|
220
|
-
size,
|
|
221
|
-
radius,
|
|
222
|
-
color,
|
|
223
|
-
variant,
|
|
224
|
-
className,
|
|
225
|
-
required: _required,
|
|
226
|
-
...rest
|
|
227
|
-
} = useFieldProps(props as DatePickerProps & { required?: boolean });
|
|
228
|
-
const range = mode === 'range';
|
|
229
|
-
const labels = { ...defaultLabels, ...labelsProp };
|
|
230
|
-
const [value, setValue] = useControllable<Date | DateRange | null>(
|
|
231
|
-
valueProp,
|
|
232
|
-
defaultValue,
|
|
233
|
-
onValueChange as ((value: Date | DateRange | null) => void) | undefined,
|
|
234
|
-
);
|
|
235
|
-
const from = value == null ? null : value instanceof Date ? value : value.from;
|
|
236
|
-
const to = value == null ? null : value instanceof Date ? value : value.to;
|
|
237
|
-
|
|
238
|
-
const [open, setOpen] = useState(false);
|
|
239
|
-
const [focused, setFocused] = useState(() => startOfDay(new Date()));
|
|
240
|
-
const [hovered, setHovered] = useState<Date | null>(null);
|
|
241
|
-
const cellRef = useRef<HTMLTableCellElement | null>(null);
|
|
242
|
-
const moveFocus = useRef(false);
|
|
243
|
-
const id = useId();
|
|
244
|
-
const titleId = `${id}-title`;
|
|
245
|
-
const valueId = `${id}-value`;
|
|
246
|
-
|
|
247
|
-
const minDay = min && startOfDay(min);
|
|
248
|
-
const maxDay = max && startOfDay(max);
|
|
249
|
-
const outOfRange = (day: Date) => (minDay != null && isBefore(day, minDay)) || (maxDay != null && isAfter(day, maxDay));
|
|
250
|
-
const unavailable = (day: Date) => outOfRange(day) || !!isDateDisabled?.(day);
|
|
251
|
-
const clamp = (day: Date) => (minDay && isBefore(day, minDay) ? minDay : maxDay && isAfter(day, maxDay) ? maxDay : day);
|
|
252
|
-
const week = { locale, weekStartsOn };
|
|
253
|
-
|
|
254
|
-
useEffect(() => {
|
|
255
|
-
if (!open || !moveFocus.current) return;
|
|
256
|
-
moveFocus.current = false;
|
|
257
|
-
cellRef.current?.focus();
|
|
258
|
-
}, [open, focused]);
|
|
259
|
-
|
|
260
|
-
function onOpenChange(next: boolean) {
|
|
261
|
-
if (next) {
|
|
262
|
-
setFocused(clamp(startOfDay(from ?? new Date())));
|
|
263
|
-
setHovered(null);
|
|
264
|
-
}
|
|
265
|
-
setOpen(next);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function pick(day: Date) {
|
|
269
|
-
if (unavailable(day)) return;
|
|
270
|
-
if (!range) {
|
|
271
|
-
setValue(day);
|
|
272
|
-
setOpen(false);
|
|
273
|
-
} else if (!from || to) {
|
|
274
|
-
setValue({ from: day, to: null });
|
|
275
|
-
} else {
|
|
276
|
-
setValue(isBefore(day, from) ? { from: day, to: from } : { from, to: day });
|
|
277
|
-
setOpen(false);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function navigate(day: Date) {
|
|
282
|
-
moveFocus.current = true;
|
|
283
|
-
setFocused(clamp(day));
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function onGridKeyDown(event: KeyboardEvent<HTMLTableElement>) {
|
|
287
|
-
const moves: Record<string, () => Date> = {
|
|
288
|
-
ArrowLeft: () => addDays(focused, -1),
|
|
289
|
-
ArrowRight: () => addDays(focused, 1),
|
|
290
|
-
ArrowUp: () => addDays(focused, -7),
|
|
291
|
-
ArrowDown: () => addDays(focused, 7),
|
|
292
|
-
Home: () => startOfWeek(focused, week),
|
|
293
|
-
End: () => startOfDay(endOfWeek(focused, week)),
|
|
294
|
-
PageUp: () => (event.shiftKey ? addYears(focused, -1) : addMonths(focused, -1)),
|
|
295
|
-
PageDown: () => (event.shiftKey ? addYears(focused, 1) : addMonths(focused, 1)),
|
|
296
|
-
};
|
|
297
|
-
const move = moves[event.key];
|
|
298
|
-
if (move) {
|
|
299
|
-
event.preventDefault();
|
|
300
|
-
navigate(move());
|
|
301
|
-
} else if (event.key === 'Enter' || event.key === ' ') {
|
|
302
|
-
event.preventDefault();
|
|
303
|
-
pick(focused);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const month = startOfMonth(focused);
|
|
308
|
-
const first = startOfWeek(month, week);
|
|
309
|
-
const days = Array.from({ length: 42 }, (_, i) => addDays(first, i));
|
|
310
|
-
const weeks = Array.from({ length: 6 }, (_, i) => days.slice(i * 7, i * 7 + 7));
|
|
311
|
-
const prevDisabled = minDay != null && isBefore(endOfMonth(addMonths(month, -1)), minDay);
|
|
312
|
-
const nextDisabled = maxDay != null && isAfter(addMonths(month, 1), maxDay);
|
|
313
|
-
|
|
314
|
-
const previewEnd = range && from && !to ? (hovered ?? focused) : null;
|
|
315
|
-
const [spanStart, spanEnd] = previewEnd && from
|
|
316
|
-
? isBefore(previewEnd, from) ? [previewEnd, from] : [from, previewEnd]
|
|
317
|
-
: [from, to];
|
|
318
|
-
const inSpan = (day: Date) => spanStart != null && spanEnd != null && isAfter(day, spanStart) && isBefore(day, spanEnd);
|
|
319
|
-
const isEndpoint = (day: Date) => (from != null && isSameDay(day, from)) || (to != null && isSameDay(day, to));
|
|
320
|
-
|
|
321
|
-
const show = (date: Date) => format(date, displayFormat, { locale });
|
|
322
|
-
const display = from ? (range && to ? `${show(from)} – ${show(to)}` : show(from)) : null;
|
|
323
|
-
const labelled = field != null || rest['aria-label'] != null || rest['aria-labelledby'] != null;
|
|
324
|
-
const s = size ?? own.size;
|
|
325
|
-
const hidden = value == null ? '' : range ? `${from ? iso(from) : ''}/${to ? iso(to) : ''}` : from ? iso(from) : '';
|
|
326
|
-
|
|
327
|
-
return (
|
|
328
|
-
<>
|
|
329
|
-
<Popover open={open} onOpenChange={onOpenChange} placement="bottom-start" modal>
|
|
330
|
-
<Popover.Trigger>
|
|
331
|
-
<button
|
|
332
|
-
type="button"
|
|
333
|
-
className={cx('yarcl-input yarcl-select yarcl-date-picker', sizeClass(s), radiusClass(radius ?? own.radius, s), colorClass(color ?? own.color), className)}
|
|
334
|
-
{...rest}
|
|
335
|
-
aria-describedby={cx(labelled && display && valueId, rest['aria-describedby']) || undefined}
|
|
336
|
-
>
|
|
337
|
-
<span id={valueId} className={cx('yarcl-select-value', !display && 'yarcl-select-placeholder')}>
|
|
338
|
-
{display ?? placeholder}
|
|
339
|
-
</span>
|
|
340
|
-
<Icon d={calendarPath} />
|
|
341
|
-
</button>
|
|
342
|
-
</Popover.Trigger>
|
|
343
|
-
<Popover.Content
|
|
344
|
-
aria-label={labels.dialog}
|
|
345
|
-
initialFocus={cellRef}
|
|
346
|
-
className={cx('yarcl-date-picker-panel', sizeClass(s), colorClass(color ?? own.color))}
|
|
347
|
-
>
|
|
348
|
-
<div className="yarcl-date-picker-header">
|
|
349
|
-
<button
|
|
350
|
-
type="button"
|
|
351
|
-
className="yarcl-date-picker-nav"
|
|
352
|
-
aria-label={labels.previousMonth}
|
|
353
|
-
aria-disabled={prevDisabled || undefined}
|
|
354
|
-
onClick={() => !prevDisabled && setFocused(clamp(addMonths(focused, -1)))}
|
|
355
|
-
>
|
|
356
|
-
<Icon d="m15 6-6 6 6 6" />
|
|
357
|
-
</button>
|
|
358
|
-
<div id={titleId} className="yarcl-date-picker-title" aria-live="polite">
|
|
359
|
-
{format(month, 'LLLL y', { locale })}
|
|
360
|
-
</div>
|
|
361
|
-
<button
|
|
362
|
-
type="button"
|
|
363
|
-
className="yarcl-date-picker-nav"
|
|
364
|
-
aria-label={labels.nextMonth}
|
|
365
|
-
aria-disabled={nextDisabled || undefined}
|
|
366
|
-
onClick={() => !nextDisabled && setFocused(clamp(addMonths(focused, 1)))}
|
|
367
|
-
>
|
|
368
|
-
<Icon d="m9 6 6 6-6 6" />
|
|
369
|
-
</button>
|
|
370
|
-
</div>
|
|
371
|
-
<table
|
|
372
|
-
role="grid"
|
|
373
|
-
aria-labelledby={titleId}
|
|
374
|
-
aria-multiselectable={range || undefined}
|
|
375
|
-
className={cx('yarcl-date-picker-grid', radiusClass(radius ?? own.radius, s))}
|
|
376
|
-
onKeyDown={onGridKeyDown}
|
|
377
|
-
onMouseLeave={() => setHovered(null)}
|
|
378
|
-
>
|
|
379
|
-
<thead>
|
|
380
|
-
<tr>
|
|
381
|
-
{weeks[0].map((day) => (
|
|
382
|
-
<th key={day.getDay()} scope="col" abbr={format(day, 'EEEE', { locale })}>
|
|
383
|
-
{format(day, 'EEEEEE', { locale })}
|
|
384
|
-
</th>
|
|
385
|
-
))}
|
|
386
|
-
</tr>
|
|
387
|
-
</thead>
|
|
388
|
-
<tbody>
|
|
389
|
-
{weeks.map((row) => (
|
|
390
|
-
<tr key={iso(row[0])}>
|
|
391
|
-
{row.map((day) => {
|
|
392
|
-
if (!isSameMonth(day, month)) return <td key={iso(day)} role="gridcell" />;
|
|
393
|
-
const isFocused = isSameDay(day, focused);
|
|
394
|
-
const endpoint = isEndpoint(day);
|
|
395
|
-
const between = inSpan(day);
|
|
396
|
-
const selected = endpoint || (between && !previewEnd);
|
|
397
|
-
return (
|
|
398
|
-
<td
|
|
399
|
-
key={iso(day)}
|
|
400
|
-
ref={isFocused ? cellRef : undefined}
|
|
401
|
-
role="gridcell"
|
|
402
|
-
tabIndex={isFocused ? 0 : -1}
|
|
403
|
-
aria-selected={selected}
|
|
404
|
-
aria-disabled={unavailable(day) || undefined}
|
|
405
|
-
aria-current={isToday(day) ? 'date' : undefined}
|
|
406
|
-
aria-label={format(day, 'PPPP', { locale })}
|
|
407
|
-
data-selected={endpoint || undefined}
|
|
408
|
-
data-in-range={between || undefined}
|
|
409
|
-
className={cx(
|
|
410
|
-
'yarcl-date-picker-day',
|
|
411
|
-
endpoint ? variantClass(variant ?? own.variant) : between && softVariantClass(),
|
|
412
|
-
)}
|
|
413
|
-
onClick={() => {
|
|
414
|
-
setFocused(day);
|
|
415
|
-
pick(day);
|
|
416
|
-
}}
|
|
417
|
-
onMouseEnter={() => range && setHovered(day)}
|
|
418
|
-
>
|
|
419
|
-
{format(day, 'd', { locale })}
|
|
420
|
-
</td>
|
|
421
|
-
);
|
|
422
|
-
})}
|
|
423
|
-
</tr>
|
|
424
|
-
))}
|
|
425
|
-
</tbody>
|
|
426
|
-
</table>
|
|
427
|
-
</Popover.Content>
|
|
428
|
-
</Popover>
|
|
429
|
-
{name && <input type="hidden" name={name} value={hidden} />}
|
|
430
|
-
</>
|
|
431
|
-
);
|
|
432
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { Modal, type ModalProps } from './Modal';
|
|
2
|
-
import type { ModalSize, Radius } from '../types';
|
|
3
|
-
import { useDefaults } from '../runtime';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/** Props for {@link Dialog}. */
|
|
7
|
-
export interface DialogProps extends ModalProps {
|
|
8
|
-
/**
|
|
9
|
-
* Corner radius, from the `radii` config.
|
|
10
|
-
* @default config.defaults.radius
|
|
11
|
-
*/
|
|
12
|
-
radius?: Radius;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* A modal dialog built on the native `<dialog>` element: focus is trapped, the page behind is inert
|
|
17
|
-
* and doesn't scroll, and Esc closes it.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* <Dialog
|
|
22
|
-
* trigger={<Button color="danger">Delete</Button>}
|
|
23
|
-
* title="Delete project?"
|
|
24
|
-
* description="This can't be undone."
|
|
25
|
-
* footer={<Button color="danger" onClick={remove}>Delete</Button>}
|
|
26
|
-
* />
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
export function Dialog(props: DialogProps) {
|
|
30
|
-
const own = useDefaults('Dialog');
|
|
31
|
-
return (
|
|
32
|
-
<Modal
|
|
33
|
-
{...props}
|
|
34
|
-
size={props.size ?? (own.size as ModalSize | undefined)}
|
|
35
|
-
radius={props.radius ?? (own.radius === 'size' ? undefined : own.radius)}
|
|
36
|
-
className="yarcl-dialog"
|
|
37
|
-
/>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { ComponentProps } from 'react';
|
|
2
|
-
import { cx } from '../classes';
|
|
3
|
-
|
|
4
|
-
/** Props for {@link Divider}. */
|
|
5
|
-
export interface DividerProps extends Omit<ComponentProps<'hr'>, 'children'> {
|
|
6
|
-
/**
|
|
7
|
-
* Direction of the line. Use `'vertical'` inside an {@link Inline}.
|
|
8
|
-
* @default 'horizontal'
|
|
9
|
-
*/
|
|
10
|
-
orientation?: 'horizontal' | 'vertical';
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* A thin separator line in the neutral border color.
|
|
15
|
-
* Spacing around it comes from the parent's `gap`.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```tsx
|
|
19
|
-
* <Stack>
|
|
20
|
-
* <Text>Above</Text>
|
|
21
|
-
* <Divider />
|
|
22
|
-
* <Text>Below</Text>
|
|
23
|
-
* </Stack>
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export function Divider({ orientation = 'horizontal', className, ...props }: DividerProps) {
|
|
27
|
-
return (
|
|
28
|
-
<hr
|
|
29
|
-
className={cx('yarcl-divider', `yarcl-divider-${orientation}`, className)}
|
|
30
|
-
aria-orientation={orientation === 'vertical' ? 'vertical' : undefined}
|
|
31
|
-
{...props}
|
|
32
|
-
/>
|
|
33
|
-
);
|
|
34
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { ModalSize } from '../types';
|
|
2
|
-
import { Modal, type ModalProps } from './Modal';
|
|
3
|
-
import { useDefaults } from '../runtime';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/** Props for {@link Drawer}. */
|
|
7
|
-
export interface DrawerProps extends ModalProps {
|
|
8
|
-
/**
|
|
9
|
-
* Edge the drawer is attached to.
|
|
10
|
-
* @default 'right'
|
|
11
|
-
*/
|
|
12
|
-
side?: 'left' | 'right';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* A modal panel that slides in from the left or right edge. Same behavior as {@link Dialog}:
|
|
17
|
-
* focus is trapped, the page behind is inert, Esc closes it.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* <Drawer side="left" trigger={<Button>Menu</Button>} title="Navigation">
|
|
22
|
-
* <Stack as="nav">…</Stack>
|
|
23
|
-
* </Drawer>
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export function Drawer({ side = 'right', ...props }: DrawerProps) {
|
|
27
|
-
const own = useDefaults('Drawer');
|
|
28
|
-
return (
|
|
29
|
-
<Modal {...props} size={props.size ?? (own.size as ModalSize | undefined)} className={`yarcl-drawer yarcl-drawer-${side}`} />
|
|
30
|
-
);
|
|
31
|
-
}
|
package/src/components/Field.tsx
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { useId, type ComponentProps, type ReactNode } from 'react';
|
|
2
|
-
import { cx, typeClass } from '../classes';
|
|
3
|
-
import { FieldContext } from '../field-context';
|
|
4
|
-
import { useConfig } from '../runtime';
|
|
5
|
-
import { Label } from './Label';
|
|
6
|
-
|
|
7
|
-
/** Props for {@link Field}. */
|
|
8
|
-
export interface FieldProps extends Omit<ComponentProps<'div'>, 'children'> {
|
|
9
|
-
/** Visible label for the control. */
|
|
10
|
-
label: ReactNode;
|
|
11
|
-
/** Helper text shown below the control. */
|
|
12
|
-
description?: ReactNode;
|
|
13
|
-
/** Error message. When set, the control is marked invalid and styled with `defaults.errorColor`. */
|
|
14
|
-
error?: ReactNode;
|
|
15
|
-
/** Marks the control as required and shows an indicator next to the label. */
|
|
16
|
-
required?: boolean;
|
|
17
|
-
/** A single form control: {@link Input}, {@link Textarea}, {@link Checkbox}, {@link Switch} or {@link Slider}. */
|
|
18
|
-
children: ReactNode;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Wraps a form control with a label, helper text and an error message.
|
|
23
|
-
* The control receives `id`, `aria-describedby`, `aria-invalid` and `required` automatically;
|
|
24
|
-
* don't set its `id` yourself.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```tsx
|
|
28
|
-
* <Field label="Email" description="We never share it." error={errors.email} required>
|
|
29
|
-
* <Input type="email" />
|
|
30
|
-
* </Field>
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export function Field({ label, description, error, required, children, className, ...props }: FieldProps) {
|
|
34
|
-
const config = useConfig();
|
|
35
|
-
const id = useId();
|
|
36
|
-
const descriptionId = description != null ? `${id}-description` : undefined;
|
|
37
|
-
const errorId = error != null && error !== false ? `${id}-error` : undefined;
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<FieldContext.Provider
|
|
41
|
-
value={{ id, labelId: `${id}-label`, describedBy: cx(descriptionId, errorId) || undefined, invalid: errorId != null, required }}
|
|
42
|
-
>
|
|
43
|
-
<div className={cx('yarcl-field', className)} {...props}>
|
|
44
|
-
<Label id={`${id}-label`} htmlFor={id} required={required}>
|
|
45
|
-
{label}
|
|
46
|
-
</Label>
|
|
47
|
-
{children}
|
|
48
|
-
{descriptionId && (
|
|
49
|
-
<p className={cx('yarcl-field-description', typeClass(config.defaults.helperStyle))} id={descriptionId}>
|
|
50
|
-
{description}
|
|
51
|
-
</p>
|
|
52
|
-
)}
|
|
53
|
-
{errorId && (
|
|
54
|
-
<p className={cx('yarcl-field-error', typeClass(config.defaults.helperStyle))} id={errorId}>
|
|
55
|
-
{error}
|
|
56
|
-
</p>
|
|
57
|
-
)}
|
|
58
|
-
</div>
|
|
59
|
-
</FieldContext.Provider>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { ComponentProps } from 'react';
|
|
2
|
-
import { colorClass, cx, typeClass } from '../classes';
|
|
3
|
-
import type { Color, TextStyle } from '../types';
|
|
4
|
-
import { useConfig } from '../runtime';
|
|
5
|
-
|
|
6
|
-
/** Props for {@link Heading}. */
|
|
7
|
-
export interface HeadingProps extends Omit<ComponentProps<'h2'>, 'color'> {
|
|
8
|
-
/** Heading level: renders `<h1>` … `<h6>`. Choose by document structure, not by size. */
|
|
9
|
-
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
10
|
-
/**
|
|
11
|
-
* Text style, from the `typography.styles` config. Overrides the level's style when the
|
|
12
|
-
* visual size should differ from the document structure.
|
|
13
|
-
* @default config.typography.headings[`h${level}`]
|
|
14
|
-
*/
|
|
15
|
-
textStyle?: TextStyle;
|
|
16
|
-
/** Semantic color, from the `colors` config. Uses the default text color when omitted. */
|
|
17
|
-
color?: Color;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* A section heading. Renders `<h1>` … `<h6>` styled with the level's text style from
|
|
22
|
-
* `typography.headings`; `textStyle` overrides the look without changing the level.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```tsx
|
|
26
|
-
* <Heading level={1}>Settings</Heading>
|
|
27
|
-
* <Heading level={2} textStyle="display">Big section title</Heading>
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export function Heading({ level, textStyle, color, className, ...props }: HeadingProps) {
|
|
31
|
-
const config = useConfig();
|
|
32
|
-
const Tag = `h${level}` as 'h2';
|
|
33
|
-
const style: TextStyle = textStyle ?? config.typography.headings[`h${level}`];
|
|
34
|
-
return (
|
|
35
|
-
<Tag
|
|
36
|
-
className={cx('yarcl-heading', typeClass(style), color && cx('yarcl-text-colored', colorClass(color)), className)}
|
|
37
|
-
{...props}
|
|
38
|
-
/>
|
|
39
|
-
);
|
|
40
|
-
}
|