@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.
Files changed (58) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/index.esm.js +1634 -256
  3. package/dist/src/components/Calendar/Calendar.d.ts +14 -0
  4. package/dist/src/components/Calendar/Calendar.d.ts.map +1 -0
  5. package/dist/src/components/Calendar/calendar.types.d.ts +72 -0
  6. package/dist/src/components/Calendar/calendar.types.d.ts.map +1 -0
  7. package/dist/src/components/Calendar/calendar.variants.d.ts +99 -0
  8. package/dist/src/components/Calendar/calendar.variants.d.ts.map +1 -0
  9. package/dist/src/components/DatePicker/DatePicker.d.ts +14 -0
  10. package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -0
  11. package/dist/src/components/DatePicker/datePicker.types.d.ts +88 -0
  12. package/dist/src/components/DatePicker/datePicker.types.d.ts.map +1 -0
  13. package/dist/src/components/DatePicker/datePicker.variants.d.ts +97 -0
  14. package/dist/src/components/DatePicker/datePicker.variants.d.ts.map +1 -0
  15. package/dist/src/components/DateRangePicker/DateRangePicker.d.ts +13 -0
  16. package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +1 -0
  17. package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts +85 -0
  18. package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts.map +1 -0
  19. package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts +183 -0
  20. package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts.map +1 -0
  21. package/dist/src/components/DateTimePicker/DateTimePicker.d.ts +7 -43
  22. package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
  23. package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts +50 -55
  24. package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -1
  25. package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts +36 -94
  26. package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts.map +1 -1
  27. package/dist/src/components/TimePicker/TimePicker.d.ts +12 -0
  28. package/dist/src/components/TimePicker/TimePicker.d.ts.map +1 -0
  29. package/dist/src/components/TimePicker/timePicker.types.d.ts +80 -0
  30. package/dist/src/components/TimePicker/timePicker.types.d.ts.map +1 -0
  31. package/dist/src/components/TimePicker/timePicker.variants.d.ts +103 -0
  32. package/dist/src/components/TimePicker/timePicker.variants.d.ts.map +1 -0
  33. package/dist/src/index.d.ts +15 -3
  34. package/dist/src/index.d.ts.map +1 -1
  35. package/package.json +4 -3
  36. package/src/components/Calendar/Calendar.test.tsx +75 -0
  37. package/src/components/Calendar/Calendar.tsx +264 -0
  38. package/src/components/Calendar/calendar.types.ts +65 -0
  39. package/src/components/Calendar/calendar.variants.ts +64 -0
  40. package/src/components/DatePicker/DatePicker.test.tsx +76 -0
  41. package/src/components/DatePicker/DatePicker.tsx +305 -0
  42. package/src/components/DatePicker/datePicker.types.ts +77 -0
  43. package/src/components/DatePicker/datePicker.variants.ts +50 -0
  44. package/src/components/DateRangePicker/DateRangePicker.test.tsx +92 -0
  45. package/src/components/DateRangePicker/DateRangePicker.tsx +510 -0
  46. package/src/components/DateRangePicker/dateRangePicker.types.ts +74 -0
  47. package/src/components/DateRangePicker/dateRangePicker.variants.ts +101 -0
  48. package/src/components/DateTimePicker/DateTimePicker.test.tsx +60 -186
  49. package/src/components/DateTimePicker/DateTimePicker.tsx +209 -181
  50. package/src/components/DateTimePicker/dateTimePicker.types.ts +50 -54
  51. package/src/components/DateTimePicker/dateTimePicker.variants.ts +22 -51
  52. package/src/components/TimePicker/TimePicker.test.tsx +65 -0
  53. package/src/components/TimePicker/TimePicker.tsx +274 -0
  54. package/src/components/TimePicker/timePicker.types.ts +69 -0
  55. package/src/components/TimePicker/timePicker.variants.ts +53 -0
  56. package/src/index.ts +38 -6
  57. package/tsconfig.lib.json +7 -4
  58. 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"}
@@ -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 { DateTimePicker, isoToInputValue, } from './components/DateTimePicker/DateTimePicker.js';
54
- export type { DateTimePickerProps, DateTimePickerMode, DateTimePickerSlotProps, } from './components/DateTimePicker/dateTimePicker.types.js';
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.7.0-beta";
145
+ export declare const VERSION = "0.9.1-beta";
134
146
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAI1F,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,+CAA+C,CAAC;AACvD,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,qDAAqD,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AAIhG,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dashforge/tw",
3
- "version": "0.9.0-beta",
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/forms": "0.2.3-beta",
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/ui-core": "0.2.3-beta"
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
+ }