@dashforge/tw 0.9.0-beta → 0.10.0-beta
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/CHANGELOG.md +60 -0
- package/dist/index.esm.js +1634 -256
- package/dist/src/components/Calendar/Calendar.d.ts +14 -0
- package/dist/src/components/Calendar/Calendar.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.types.d.ts +72 -0
- package/dist/src/components/Calendar/calendar.types.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts +99 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts.map +1 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts +14 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts +88 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts +97 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts +13 -0
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts +85 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts +183 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts.map +1 -0
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts +7 -43
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts +50 -55
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts +36 -94
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts.map +1 -1
- package/dist/src/components/TimePicker/TimePicker.d.ts +12 -0
- package/dist/src/components/TimePicker/TimePicker.d.ts.map +1 -0
- package/dist/src/components/TimePicker/timePicker.types.d.ts +80 -0
- package/dist/src/components/TimePicker/timePicker.types.d.ts.map +1 -0
- package/dist/src/components/TimePicker/timePicker.variants.d.ts +103 -0
- package/dist/src/components/TimePicker/timePicker.variants.d.ts.map +1 -0
- package/dist/src/index.d.ts +15 -3
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/components/Calendar/Calendar.test.tsx +75 -0
- package/src/components/Calendar/Calendar.tsx +264 -0
- package/src/components/Calendar/calendar.types.ts +65 -0
- package/src/components/Calendar/calendar.variants.ts +64 -0
- package/src/components/DatePicker/DatePicker.test.tsx +76 -0
- package/src/components/DatePicker/DatePicker.tsx +305 -0
- package/src/components/DatePicker/datePicker.types.ts +77 -0
- package/src/components/DatePicker/datePicker.variants.ts +50 -0
- package/src/components/DateRangePicker/DateRangePicker.test.tsx +92 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +510 -0
- package/src/components/DateRangePicker/dateRangePicker.types.ts +74 -0
- package/src/components/DateRangePicker/dateRangePicker.variants.ts +101 -0
- package/src/components/DateTimePicker/DateTimePicker.test.tsx +60 -186
- package/src/components/DateTimePicker/DateTimePicker.tsx +209 -181
- package/src/components/DateTimePicker/dateTimePicker.types.ts +50 -54
- package/src/components/DateTimePicker/dateTimePicker.variants.ts +22 -51
- package/src/components/TimePicker/TimePicker.test.tsx +65 -0
- package/src/components/TimePicker/TimePicker.tsx +274 -0
- package/src/components/TimePicker/timePicker.types.ts +69 -0
- package/src/components/TimePicker/timePicker.variants.ts +53 -0
- package/src/index.ts +38 -6
- package/tsconfig.lib.json +7 -4
- package/src/components/DateTimePicker/DateTimePicker.perf.test.tsx +0 -153
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Engine } from '@dashforge/ui-core';
|
|
3
|
+
import type { AccessRequirement } from '@dashforge/rbac';
|
|
4
|
+
/** Per-slot `className` overrides for `<TimePicker>`. */
|
|
5
|
+
export interface TimePickerSlotProps {
|
|
6
|
+
root?: {
|
|
7
|
+
className?: string;
|
|
8
|
+
};
|
|
9
|
+
label?: {
|
|
10
|
+
className?: string;
|
|
11
|
+
};
|
|
12
|
+
requiredMark?: {
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
trigger?: {
|
|
16
|
+
className?: string;
|
|
17
|
+
};
|
|
18
|
+
helperText?: {
|
|
19
|
+
className?: string;
|
|
20
|
+
};
|
|
21
|
+
errorText?: {
|
|
22
|
+
className?: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Props for the `<TimePicker>` form field (Tailwind skin).
|
|
27
|
+
*
|
|
28
|
+
* A read-only trigger paired with a `<Calendar>`-style time-list popover.
|
|
29
|
+
* The stored value is a canonical 24-hour `"HH:mm"` string, or `null`.
|
|
30
|
+
* 12-hour notation (`hour12`) is display-only. No date, no timezone.
|
|
31
|
+
*
|
|
32
|
+
* The prop surface mirrors the MUI `@dashforge/ui` `TimePicker`.
|
|
33
|
+
*/
|
|
34
|
+
export interface TimePickerProps {
|
|
35
|
+
/** Field name — the bridge registration key. Required. */
|
|
36
|
+
name: string;
|
|
37
|
+
/** Validation rules forwarded to the form bridge. */
|
|
38
|
+
rules?: unknown;
|
|
39
|
+
/** Field label. */
|
|
40
|
+
label?: ReactNode;
|
|
41
|
+
/** Helper text below the control (overrides a bridge error message). */
|
|
42
|
+
helperText?: ReactNode;
|
|
43
|
+
/** Explicit error state (overrides the bridge's auto error). */
|
|
44
|
+
error?: boolean;
|
|
45
|
+
/** Marks the field required (adds the label asterisk). */
|
|
46
|
+
required?: boolean;
|
|
47
|
+
/** Disables the field. */
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
/** Placeholder shown when no time is selected. */
|
|
50
|
+
placeholder?: string;
|
|
51
|
+
/** Label/control layout. */
|
|
52
|
+
layout?: 'stacked' | 'inline';
|
|
53
|
+
/** Reactive visibility predicate evaluated against the form engine. */
|
|
54
|
+
visibleWhen?: (engine: Engine) => boolean;
|
|
55
|
+
/** RBAC access requirement. */
|
|
56
|
+
access?: AccessRequirement;
|
|
57
|
+
/** Controlled value — `"HH:mm"` (24-hour) or `null`. */
|
|
58
|
+
value?: string | null;
|
|
59
|
+
/** Uncontrolled initial value. */
|
|
60
|
+
defaultValue?: string | null;
|
|
61
|
+
/** Fired with the new time (or `null` when cleared). */
|
|
62
|
+
onChange?: (value: string | null) => void;
|
|
63
|
+
/** Earliest selectable time, `"HH:mm"`. Default `"00:00"`. */
|
|
64
|
+
minTime?: string;
|
|
65
|
+
/** Latest selectable time, `"HH:mm"`. Default `"23:30"`. */
|
|
66
|
+
maxTime?: string;
|
|
67
|
+
/** Step between time-list options, in minutes. Default `30`. */
|
|
68
|
+
stepMinutes?: number;
|
|
69
|
+
/** Render the time list in 12-hour notation. Default `false`. */
|
|
70
|
+
hour12?: boolean;
|
|
71
|
+
/** Stretches the field to its container width. */
|
|
72
|
+
fullWidth?: boolean;
|
|
73
|
+
/** Root-level Tailwind class override. */
|
|
74
|
+
sx?: string;
|
|
75
|
+
/** Per-slot `className` overrides. */
|
|
76
|
+
slotProps?: TimePickerSlotProps;
|
|
77
|
+
/** Test id applied to the field root. */
|
|
78
|
+
testId?: string;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=timePicker.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timePicker.types.d.ts","sourceRoot":"","sources":["../../../../src/components/TimePicker/timePicker.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,KAAK,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,YAAY,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,UAAU,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,SAAS,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mBAAmB;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,wEAAwE;IACxE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC9B,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1C,+BAA+B;IAC/B,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kCAAkC;IAClC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0CAA0C;IAC1C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slots for `<TimePicker>` — a bridge-integrated time field whose trigger
|
|
3
|
+
* is a `<button>` styled as an input, opening a time-list popover.
|
|
4
|
+
*
|
|
5
|
+
* Theme identity: bare neutral / brand token-backed classes only — no
|
|
6
|
+
* `dark:` variants on the neutral palette (the preset CSS-var swap
|
|
7
|
+
* auto-inverts). See `_shared/themeIdentity.test.ts`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const timePickerVariants: import("tailwind-variants").TVReturnType<{
|
|
10
|
+
layout: {
|
|
11
|
+
stacked: {};
|
|
12
|
+
inline: {
|
|
13
|
+
root: string;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
error: {
|
|
18
|
+
true: {
|
|
19
|
+
trigger: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
fullWidth: {
|
|
23
|
+
true: {
|
|
24
|
+
root: string;
|
|
25
|
+
trigger: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
}, {
|
|
29
|
+
root: string;
|
|
30
|
+
label: string;
|
|
31
|
+
requiredMark: string;
|
|
32
|
+
trigger: string;
|
|
33
|
+
value: string;
|
|
34
|
+
placeholder: string;
|
|
35
|
+
icon: string;
|
|
36
|
+
list: string;
|
|
37
|
+
option: string;
|
|
38
|
+
helperText: string;
|
|
39
|
+
errorText: string;
|
|
40
|
+
}, undefined, {
|
|
41
|
+
layout: {
|
|
42
|
+
stacked: {};
|
|
43
|
+
inline: {
|
|
44
|
+
root: string;
|
|
45
|
+
label: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
error: {
|
|
49
|
+
true: {
|
|
50
|
+
trigger: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
fullWidth: {
|
|
54
|
+
true: {
|
|
55
|
+
root: string;
|
|
56
|
+
trigger: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
}, {
|
|
60
|
+
root: string;
|
|
61
|
+
label: string;
|
|
62
|
+
requiredMark: string;
|
|
63
|
+
trigger: string;
|
|
64
|
+
value: string;
|
|
65
|
+
placeholder: string;
|
|
66
|
+
icon: string;
|
|
67
|
+
list: string;
|
|
68
|
+
option: string;
|
|
69
|
+
helperText: string;
|
|
70
|
+
errorText: string;
|
|
71
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
72
|
+
layout: {
|
|
73
|
+
stacked: {};
|
|
74
|
+
inline: {
|
|
75
|
+
root: string;
|
|
76
|
+
label: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
error: {
|
|
80
|
+
true: {
|
|
81
|
+
trigger: string;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
fullWidth: {
|
|
85
|
+
true: {
|
|
86
|
+
root: string;
|
|
87
|
+
trigger: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
}, {
|
|
91
|
+
root: string;
|
|
92
|
+
label: string;
|
|
93
|
+
requiredMark: string;
|
|
94
|
+
trigger: string;
|
|
95
|
+
value: string;
|
|
96
|
+
placeholder: string;
|
|
97
|
+
icon: string;
|
|
98
|
+
list: string;
|
|
99
|
+
option: string;
|
|
100
|
+
helperText: string;
|
|
101
|
+
errorText: string;
|
|
102
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
103
|
+
//# sourceMappingURL=timePicker.variants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timePicker.variants.d.ts","sourceRoot":"","sources":["../../../../src/components/TimePicker/timePicker.variants.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2CA0C7B,CAAC"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -50,9 +50,21 @@ export { otpFieldVariants } from './components/OTPField/otpField.variants.js';
|
|
|
50
50
|
export { Autocomplete } from './components/Autocomplete/Autocomplete.js';
|
|
51
51
|
export type { AutocompleteProps, AutocompleteOption, AutocompleteSlotProps, AutocompleteValue, } from './components/Autocomplete/autocomplete.types.js';
|
|
52
52
|
export { autocompleteVariants } from './components/Autocomplete/autocomplete.variants.js';
|
|
53
|
-
export {
|
|
54
|
-
export type {
|
|
53
|
+
export { Calendar } from './components/Calendar/Calendar.js';
|
|
54
|
+
export type { CalendarProps, CalendarSlotProps, } from './components/Calendar/calendar.types.js';
|
|
55
|
+
export { calendarVariants, calendarDayVariants, } from './components/Calendar/calendar.variants.js';
|
|
56
|
+
export { DatePicker } from './components/DatePicker/DatePicker.js';
|
|
57
|
+
export type { DatePickerProps, DatePickerSlotProps, } from './components/DatePicker/datePicker.types.js';
|
|
58
|
+
export { datePickerVariants } from './components/DatePicker/datePicker.variants.js';
|
|
59
|
+
export { TimePicker } from './components/TimePicker/TimePicker.js';
|
|
60
|
+
export type { TimePickerProps, TimePickerSlotProps, } from './components/TimePicker/timePicker.types.js';
|
|
61
|
+
export { timePickerVariants } from './components/TimePicker/timePicker.variants.js';
|
|
62
|
+
export { DateTimePicker } from './components/DateTimePicker/DateTimePicker.js';
|
|
63
|
+
export type { DateTimePickerProps, DateTimePickerSlotProps, } from './components/DateTimePicker/dateTimePicker.types.js';
|
|
55
64
|
export { dateTimePickerVariants } from './components/DateTimePicker/dateTimePicker.variants.js';
|
|
65
|
+
export { DateRangePicker } from './components/DateRangePicker/DateRangePicker.js';
|
|
66
|
+
export type { DateRangePickerProps, DateRangePickerSlotProps, } from './components/DateRangePicker/dateRangePicker.types.js';
|
|
67
|
+
export { dateRangePickerVariants, dateRangeDayVariants, } from './components/DateRangePicker/dateRangePicker.variants.js';
|
|
56
68
|
export { Breadcrumbs } from './components/Breadcrumbs/Breadcrumbs.js';
|
|
57
69
|
export type { BreadcrumbsProps, BreadcrumbItem, BreadcrumbsSlotProps, BreadcrumbLinkComponent, } from './components/Breadcrumbs/breadcrumbs.types.js';
|
|
58
70
|
export { breadcrumbsVariants } from './components/Breadcrumbs/breadcrumbs.variants.js';
|
|
@@ -130,5 +142,5 @@ export type { VariantProps } from 'tailwind-variants';
|
|
|
130
142
|
/**
|
|
131
143
|
* Package version (synced with `package.json` at publish time).
|
|
132
144
|
*/
|
|
133
|
-
export declare const VERSION = "0.
|
|
145
|
+
export declare const VERSION = "0.9.1-beta";
|
|
134
146
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAChE,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAE9E,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,YAAY,EACV,WAAW,EACX,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAIxE,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AAEpF,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAI9E,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AACtE,YAAY,EACV,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAEvF,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAI9E,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzE,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oDAAoD,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAChE,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAE9E,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,YAAY,EACV,WAAW,EACX,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAIxE,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AAEpF,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAI9E,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AACtE,YAAY,EACV,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAEvF,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAI9E,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzE,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oDAAoD,CAAC;AAM1F,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,YAAY,EACV,eAAe,EACf,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AAEpF,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,YAAY,EACV,eAAe,EACf,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AAEpF,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,YAAY,EACV,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,iDAAiD,CAAC;AAClF,YAAY,EACV,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,uDAAuD,CAAC;AAC/D,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,0DAA0D,CAAC;AAIlE,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AACtE,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAEvF,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC1D,YAAY,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAE3E,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,YAAY,EACV,WAAW,EACX,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAI9E,OAAO,EACL,qBAAqB,EACrB,UAAU,GACX,MAAM,6CAA6C,CAAC;AACrD,YAAY,EACV,0BAA0B,EAC1B,sBAAsB,EACtB,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sDAAsD,CAAC;AAE7F,OAAO,EACL,gBAAgB,EAChB,WAAW,GACZ,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EACV,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAO9E,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AAEpF,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,YAAY,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAErE,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACjD,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,aAAa,EACb,OAAO,EACP,eAAe,GAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAOlE,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAEjF,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AAEtF,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,YAAY,EAAE,mBAAmB,EAAE,MAAM,qDAAqD,CAAC;AAI/F,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,YAAY,EACV,WAAW,EACX,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACjD,YAAY,EACV,SAAS,EACT,aAAa,EACb,OAAO,GACR,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAElE,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC1D,YAAY,EACV,YAAY,EACZ,gBAAgB,GACjB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC1D,YAAY,EACV,YAAY,EACZ,gBAAgB,GACjB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAE3E,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAChE,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,aAAa,GACd,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAIjF,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AACnE,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAC;AAMpF,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,YAAY,EACV,UAAU,EACV,WAAW,EACX,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,cAAc,EACd,WAAW,GACZ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAErE,OAAO,EACL,UAAU,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,GACf,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAQ7E,OAAO,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAG9E,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAInC,OAAO,EAAE,EAAE,EAAE,MAAM,mBAAmB,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,OAAO,eAAe,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dashforge/tw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-beta",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.esm.js",
|
|
@@ -28,9 +28,10 @@
|
|
|
28
28
|
"clsx": "^2.1.0",
|
|
29
29
|
"tailwind-merge": "^3.0.0",
|
|
30
30
|
"tailwind-variants": "^3.1.1",
|
|
31
|
-
"@dashforge/
|
|
31
|
+
"@dashforge/calendar-core": "0.2.0-beta",
|
|
32
|
+
"@dashforge/ui-core": "0.2.3-beta",
|
|
32
33
|
"@dashforge/rbac": "0.2.3-beta",
|
|
33
|
-
"@dashforge/
|
|
34
|
+
"@dashforge/forms": "0.2.3-beta"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
37
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
+
|
|
5
|
+
import { Calendar } from './Calendar.js';
|
|
6
|
+
|
|
7
|
+
describe('Calendar', () => {
|
|
8
|
+
it('renders a labelled month grid', () => {
|
|
9
|
+
render(<Calendar today="2026-05-20" />);
|
|
10
|
+
expect(screen.queryByRole('grid', { name: 'Calendar' })).not.toBeNull();
|
|
11
|
+
expect(screen.queryByText('May 2026')).not.toBeNull();
|
|
12
|
+
expect(screen.getAllByRole('columnheader')).toHaveLength(7);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders a fixed 42-cell grid', () => {
|
|
16
|
+
render(<Calendar today="2026-05-20" />);
|
|
17
|
+
expect(screen.getAllByRole('gridcell')).toHaveLength(42);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('fires onChange when a day is clicked', () => {
|
|
21
|
+
const onChange = vi.fn();
|
|
22
|
+
render(<Calendar today="2026-05-20" onChange={onChange} />);
|
|
23
|
+
fireEvent.click(screen.getByRole('button', { name: /May 15, 2026/ }));
|
|
24
|
+
expect(onChange).toHaveBeenCalledWith('2026-05-15');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('marks the controlled selected day as pressed', () => {
|
|
28
|
+
render(<Calendar today="2026-05-20" value="2026-05-10" />);
|
|
29
|
+
expect(
|
|
30
|
+
screen
|
|
31
|
+
.getByRole('button', { name: /May 10, 2026/ })
|
|
32
|
+
.getAttribute('aria-pressed'),
|
|
33
|
+
).toBe('true');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('navigates to the next and previous month', () => {
|
|
37
|
+
render(<Calendar today="2026-05-20" />);
|
|
38
|
+
fireEvent.click(screen.getByRole('button', { name: 'Next month' }));
|
|
39
|
+
expect(screen.queryByText('June 2026')).not.toBeNull();
|
|
40
|
+
fireEvent.click(screen.getByRole('button', { name: 'Previous month' }));
|
|
41
|
+
expect(screen.queryByText('May 2026')).not.toBeNull();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('moves roving focus with the arrow keys', () => {
|
|
45
|
+
render(<Calendar today="2026-05-20" />);
|
|
46
|
+
const grid = screen.getByRole('grid');
|
|
47
|
+
expect(
|
|
48
|
+
screen
|
|
49
|
+
.getByRole('button', { name: /May 20, 2026/ })
|
|
50
|
+
.getAttribute('tabindex'),
|
|
51
|
+
).toBe('0');
|
|
52
|
+
fireEvent.keyDown(grid, { key: 'ArrowRight' });
|
|
53
|
+
expect(
|
|
54
|
+
screen
|
|
55
|
+
.getByRole('button', { name: /May 21, 2026/ })
|
|
56
|
+
.getAttribute('tabindex'),
|
|
57
|
+
).toBe('0');
|
|
58
|
+
expect(
|
|
59
|
+
screen
|
|
60
|
+
.getByRole('button', { name: /May 20, 2026/ })
|
|
61
|
+
.getAttribute('tabindex'),
|
|
62
|
+
).toBe('-1');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('does not select a disabled date', () => {
|
|
66
|
+
const onChange = vi.fn();
|
|
67
|
+
render(
|
|
68
|
+
<Calendar today="2026-05-20" minDate="2026-05-15" onChange={onChange} />,
|
|
69
|
+
);
|
|
70
|
+
const blocked = screen.getByRole('button', { name: /May 10, 2026/ });
|
|
71
|
+
expect(blocked.getAttribute('aria-disabled')).toBe('true');
|
|
72
|
+
fireEvent.click(blocked);
|
|
73
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import type { KeyboardEvent } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
formatLongDate,
|
|
5
|
+
resolveCalendarKey,
|
|
6
|
+
useCalendar,
|
|
7
|
+
} from '@dashforge/calendar-core';
|
|
8
|
+
import type { ISODate } from '@dashforge/calendar-core';
|
|
9
|
+
import { cn } from '../../utils/cn.js';
|
|
10
|
+
import { calendarDayVariants, calendarVariants } from './calendar.variants.js';
|
|
11
|
+
import type { CalendarProps } from './calendar.types.js';
|
|
12
|
+
|
|
13
|
+
// Inline 16×16 stroke chevrons — no icon dependency (tw convention).
|
|
14
|
+
function ChevronLeftIcon() {
|
|
15
|
+
return (
|
|
16
|
+
<svg
|
|
17
|
+
width="1em"
|
|
18
|
+
height="1em"
|
|
19
|
+
viewBox="0 0 16 16"
|
|
20
|
+
fill="none"
|
|
21
|
+
aria-hidden="true"
|
|
22
|
+
>
|
|
23
|
+
<path
|
|
24
|
+
d="M10 4 6 8l4 4"
|
|
25
|
+
stroke="currentColor"
|
|
26
|
+
strokeWidth="1.5"
|
|
27
|
+
strokeLinecap="round"
|
|
28
|
+
strokeLinejoin="round"
|
|
29
|
+
/>
|
|
30
|
+
</svg>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ChevronRightIcon() {
|
|
35
|
+
return (
|
|
36
|
+
<svg
|
|
37
|
+
width="1em"
|
|
38
|
+
height="1em"
|
|
39
|
+
viewBox="0 0 16 16"
|
|
40
|
+
fill="none"
|
|
41
|
+
aria-hidden="true"
|
|
42
|
+
>
|
|
43
|
+
<path
|
|
44
|
+
d="m6 4 4 4-4 4"
|
|
45
|
+
stroke="currentColor"
|
|
46
|
+
strokeWidth="1.5"
|
|
47
|
+
strokeLinecap="round"
|
|
48
|
+
strokeLinejoin="round"
|
|
49
|
+
/>
|
|
50
|
+
</svg>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* `<Calendar>` — a standalone, inline month-grid date picker.
|
|
56
|
+
*
|
|
57
|
+
* The Tailwind presentation layer over the headless `useCalendar` engine
|
|
58
|
+
* from `@dashforge/calendar-core` (the same engine the MUI `@dashforge/ui`
|
|
59
|
+
* calendar consumes — only the skin differs). Implements the WCAG grid
|
|
60
|
+
* pattern with roving tab-index focus.
|
|
61
|
+
*
|
|
62
|
+
* It is a primitive: no form-bridge / RBAC integration — use `<DatePicker>`
|
|
63
|
+
* for a form-bound date field.
|
|
64
|
+
*/
|
|
65
|
+
export function Calendar(props: CalendarProps) {
|
|
66
|
+
const {
|
|
67
|
+
value,
|
|
68
|
+
defaultValue,
|
|
69
|
+
onChange,
|
|
70
|
+
month,
|
|
71
|
+
year,
|
|
72
|
+
defaultMonth,
|
|
73
|
+
defaultYear,
|
|
74
|
+
onMonthChange,
|
|
75
|
+
minDate,
|
|
76
|
+
maxDate,
|
|
77
|
+
disabledDates,
|
|
78
|
+
isDateDisabled,
|
|
79
|
+
weekStartDay,
|
|
80
|
+
locale,
|
|
81
|
+
today,
|
|
82
|
+
disabled = false,
|
|
83
|
+
autoFocus = false,
|
|
84
|
+
sx,
|
|
85
|
+
slotProps,
|
|
86
|
+
testId,
|
|
87
|
+
'aria-label': ariaLabel = 'Calendar',
|
|
88
|
+
} = props;
|
|
89
|
+
|
|
90
|
+
const isValueControlled = value !== undefined;
|
|
91
|
+
const [internalValue, setInternalValue] = useState<ISODate | null>(
|
|
92
|
+
defaultValue ?? null,
|
|
93
|
+
);
|
|
94
|
+
const selectedValue: ISODate | null = isValueControlled
|
|
95
|
+
? value
|
|
96
|
+
: internalValue;
|
|
97
|
+
|
|
98
|
+
const handleSelect = useCallback(
|
|
99
|
+
(iso: ISODate) => {
|
|
100
|
+
if (!isValueControlled) {
|
|
101
|
+
setInternalValue(iso);
|
|
102
|
+
}
|
|
103
|
+
onChange?.(iso);
|
|
104
|
+
},
|
|
105
|
+
[isValueControlled, onChange],
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const calendar = useCalendar({
|
|
109
|
+
selected: selectedValue,
|
|
110
|
+
month,
|
|
111
|
+
year,
|
|
112
|
+
defaultMonth,
|
|
113
|
+
defaultYear,
|
|
114
|
+
onMonthChange,
|
|
115
|
+
minDate,
|
|
116
|
+
maxDate,
|
|
117
|
+
disabledDates,
|
|
118
|
+
isDateDisabled,
|
|
119
|
+
weekStartDay,
|
|
120
|
+
locale,
|
|
121
|
+
today,
|
|
122
|
+
onSelect: handleSelect,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Roving focus: the active day cell holds DOM focus; it is moved only
|
|
126
|
+
// after a keyboard interaction (or on mount when `autoFocus`). `focus`
|
|
127
|
+
// uses `preventScroll` so opening the calendar inside a popup never
|
|
128
|
+
// scroll-jumps the page before the popup is positioned.
|
|
129
|
+
const focusedCellRef = useRef<HTMLButtonElement | null>(null);
|
|
130
|
+
const shouldFocusRef = useRef(autoFocus);
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (shouldFocusRef.current && focusedCellRef.current) {
|
|
134
|
+
focusedCellRef.current.focus({ preventScroll: true });
|
|
135
|
+
shouldFocusRef.current = false;
|
|
136
|
+
}
|
|
137
|
+
}, [calendar.focusedDate]);
|
|
138
|
+
|
|
139
|
+
const handleKeyDown = useCallback(
|
|
140
|
+
(event: KeyboardEvent<HTMLDivElement>) => {
|
|
141
|
+
if (disabled) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const action = resolveCalendarKey(event.key);
|
|
145
|
+
if (action === null) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
shouldFocusRef.current = true;
|
|
150
|
+
switch (action) {
|
|
151
|
+
case 'select':
|
|
152
|
+
calendar.selectDate(calendar.focusedDate);
|
|
153
|
+
break;
|
|
154
|
+
case 'monthForward':
|
|
155
|
+
calendar.goToNextMonth();
|
|
156
|
+
break;
|
|
157
|
+
case 'monthBackward':
|
|
158
|
+
calendar.goToPreviousMonth();
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
calendar.navigate(action);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
[disabled, calendar],
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const resolvedLocale = locale ?? 'en-US';
|
|
168
|
+
const v = calendarVariants({ disabled });
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
data-testid={testId}
|
|
173
|
+
className={cn(v.root(), sx, slotProps?.root?.className)}
|
|
174
|
+
>
|
|
175
|
+
{/* Header: month navigation + label */}
|
|
176
|
+
<div className={cn(v.header(), slotProps?.header?.className)}>
|
|
177
|
+
<button
|
|
178
|
+
type="button"
|
|
179
|
+
className={v.navButton()}
|
|
180
|
+
disabled={disabled}
|
|
181
|
+
aria-label="Previous month"
|
|
182
|
+
onClick={() => {
|
|
183
|
+
calendar.goToPreviousMonth();
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
186
|
+
<ChevronLeftIcon />
|
|
187
|
+
</button>
|
|
188
|
+
<span aria-live="polite" className={v.monthLabel()}>
|
|
189
|
+
{calendar.monthLabel}
|
|
190
|
+
</span>
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
className={v.navButton()}
|
|
194
|
+
disabled={disabled}
|
|
195
|
+
aria-label="Next month"
|
|
196
|
+
onClick={() => {
|
|
197
|
+
calendar.goToNextMonth();
|
|
198
|
+
}}
|
|
199
|
+
>
|
|
200
|
+
<ChevronRightIcon />
|
|
201
|
+
</button>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Grid */}
|
|
205
|
+
<div
|
|
206
|
+
role="grid"
|
|
207
|
+
aria-label={ariaLabel}
|
|
208
|
+
onKeyDown={handleKeyDown}
|
|
209
|
+
className={cn(v.grid(), slotProps?.grid?.className)}
|
|
210
|
+
>
|
|
211
|
+
<div role="row" className={v.weekdayRow()}>
|
|
212
|
+
{calendar.weekdayNames.map((dayName, index) => (
|
|
213
|
+
<span
|
|
214
|
+
key={`weekday-${String(index)}`}
|
|
215
|
+
role="columnheader"
|
|
216
|
+
className={v.weekday()}
|
|
217
|
+
>
|
|
218
|
+
{dayName}
|
|
219
|
+
</span>
|
|
220
|
+
))}
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
{calendar.weeks.map((week) => (
|
|
224
|
+
<div key={week.key} role="row" className={v.weekRow()}>
|
|
225
|
+
{week.days.map((day) => {
|
|
226
|
+
const isActiveCell = day.iso === calendar.focusedDate;
|
|
227
|
+
return (
|
|
228
|
+
<span key={day.iso} role="gridcell" className="flex">
|
|
229
|
+
<button
|
|
230
|
+
type="button"
|
|
231
|
+
ref={isActiveCell ? focusedCellRef : null}
|
|
232
|
+
tabIndex={isActiveCell ? 0 : -1}
|
|
233
|
+
disabled={disabled}
|
|
234
|
+
aria-label={formatLongDate(
|
|
235
|
+
{ year: day.year, month: day.month, day: day.day },
|
|
236
|
+
resolvedLocale,
|
|
237
|
+
)}
|
|
238
|
+
aria-current={day.isToday ? 'date' : undefined}
|
|
239
|
+
aria-pressed={day.isSelected}
|
|
240
|
+
aria-disabled={day.isDisabled}
|
|
241
|
+
onClick={() => {
|
|
242
|
+
calendar.selectDate(day.iso);
|
|
243
|
+
}}
|
|
244
|
+
className={cn(
|
|
245
|
+
calendarDayVariants({
|
|
246
|
+
siblingMonth: day.isSiblingMonth,
|
|
247
|
+
today: day.isToday,
|
|
248
|
+
selected: day.isSelected,
|
|
249
|
+
disabled: day.isDisabled,
|
|
250
|
+
}),
|
|
251
|
+
slotProps?.day?.className,
|
|
252
|
+
)}
|
|
253
|
+
>
|
|
254
|
+
{day.day}
|
|
255
|
+
</button>
|
|
256
|
+
</span>
|
|
257
|
+
);
|
|
258
|
+
})}
|
|
259
|
+
</div>
|
|
260
|
+
))}
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ISODate, WeekDay } from '@dashforge/calendar-core';
|
|
2
|
+
|
|
3
|
+
/** Per-slot `className` overrides for `<Calendar>`. */
|
|
4
|
+
export interface CalendarSlotProps {
|
|
5
|
+
root?: { className?: string };
|
|
6
|
+
header?: { className?: string };
|
|
7
|
+
grid?: { className?: string };
|
|
8
|
+
/** Applied to every day-cell button. */
|
|
9
|
+
day?: { className?: string };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props for the `<Calendar>` primitive — a standalone, inline month grid.
|
|
14
|
+
*
|
|
15
|
+
* `Calendar` is NOT a form field (no bridge / RBAC integration). It is the
|
|
16
|
+
* low-level building block used inside `<DatePicker>`. For a form-bound date
|
|
17
|
+
* input use `<DatePicker>`.
|
|
18
|
+
*
|
|
19
|
+
* Dates are ISO `YYYY-MM-DD` strings throughout. The headless date engine is
|
|
20
|
+
* `@dashforge/calendar-core` — shared with the MUI `@dashforge/ui` calendar.
|
|
21
|
+
*/
|
|
22
|
+
export interface CalendarProps {
|
|
23
|
+
/** Selected date (controlled). */
|
|
24
|
+
value?: ISODate | null;
|
|
25
|
+
/** Initial selected date (uncontrolled). */
|
|
26
|
+
defaultValue?: ISODate | null;
|
|
27
|
+
/** Fired when a day is chosen. */
|
|
28
|
+
onChange?: (value: ISODate) => void;
|
|
29
|
+
/** Controlled displayed month (1-indexed). Pair with `year`. */
|
|
30
|
+
month?: number;
|
|
31
|
+
/** Controlled displayed year. Pair with `month`. */
|
|
32
|
+
year?: number;
|
|
33
|
+
/** Initial displayed month (1-indexed, uncontrolled). */
|
|
34
|
+
defaultMonth?: number;
|
|
35
|
+
/** Initial displayed year (uncontrolled). */
|
|
36
|
+
defaultYear?: number;
|
|
37
|
+
/** Fired when the displayed month changes. */
|
|
38
|
+
onMonthChange?: (month: number, year: number) => void;
|
|
39
|
+
/** Earliest selectable date (inclusive). */
|
|
40
|
+
minDate?: ISODate;
|
|
41
|
+
/** Latest selectable date (inclusive). */
|
|
42
|
+
maxDate?: ISODate;
|
|
43
|
+
/** Explicit list of disabled dates. */
|
|
44
|
+
disabledDates?: readonly ISODate[];
|
|
45
|
+
/** Predicate marking arbitrary dates disabled. */
|
|
46
|
+
isDateDisabled?: (date: ISODate) => boolean;
|
|
47
|
+
/** Weekday of the grid's first column (`0` = Sunday). Default `0`. */
|
|
48
|
+
weekStartDay?: WeekDay;
|
|
49
|
+
/** BCP-47 locale for month/weekday names. Default `"en-US"`. */
|
|
50
|
+
locale?: string;
|
|
51
|
+
/** Overrides "today" (ISO `YYYY-MM-DD`), mainly for testing. */
|
|
52
|
+
today?: ISODate;
|
|
53
|
+
/** Disables the whole calendar. */
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
/** Moves DOM focus to the active day cell on mount. */
|
|
56
|
+
autoFocus?: boolean;
|
|
57
|
+
/** Accessible label for the grid. Default `"Calendar"`. */
|
|
58
|
+
'aria-label'?: string;
|
|
59
|
+
/** Root-level Tailwind class override. */
|
|
60
|
+
sx?: string;
|
|
61
|
+
/** Per-slot `className` overrides. */
|
|
62
|
+
slotProps?: CalendarSlotProps;
|
|
63
|
+
/** Test id applied to the root element. */
|
|
64
|
+
testId?: string;
|
|
65
|
+
}
|