@wordpress/components 29.12.0 → 29.13.1-next.719a03cbe.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.
Files changed (157) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/box-control/input-control.js +2 -2
  3. package/build/box-control/input-control.js.map +1 -1
  4. package/build/calendar/date-calendar/index.js +60 -0
  5. package/build/calendar/date-calendar/index.js.map +1 -0
  6. package/build/calendar/date-range-calendar/index.js +168 -0
  7. package/build/calendar/date-range-calendar/index.js.map +1 -0
  8. package/build/calendar/index.js +27 -0
  9. package/build/calendar/index.js.map +1 -0
  10. package/build/calendar/types.js +6 -0
  11. package/build/calendar/types.js.map +1 -0
  12. package/build/calendar/utils/constants.js +68 -0
  13. package/build/calendar/utils/constants.js.map +1 -0
  14. package/build/calendar/utils/day-cell.js +137 -0
  15. package/build/calendar/utils/day-cell.js.map +1 -0
  16. package/build/calendar/utils/misc.js +10 -0
  17. package/build/calendar/utils/misc.js.map +1 -0
  18. package/build/calendar/utils/use-controlled-value.js +58 -0
  19. package/build/calendar/utils/use-controlled-value.js.map +1 -0
  20. package/build/calendar/utils/use-localization-props.js +162 -0
  21. package/build/calendar/utils/use-localization-props.js.map +1 -0
  22. package/build/custom-gradient-picker/gradient-bar/control-points.js +1 -1
  23. package/build/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  24. package/build/custom-select-control-v2/custom-select.js +3 -3
  25. package/build/custom-select-control-v2/custom-select.js.map +1 -1
  26. package/build/date-time/date/index.js +1 -1
  27. package/build/date-time/date/index.js.map +1 -1
  28. package/build/form-token-field/index.js +11 -1
  29. package/build/form-token-field/index.js.map +1 -1
  30. package/build/form-token-field/token.js +1 -1
  31. package/build/form-token-field/token.js.map +1 -1
  32. package/build/index.js +19 -0
  33. package/build/index.js.map +1 -1
  34. package/build/mobile/bottom-sheet/cell.native.js +2 -2
  35. package/build/mobile/bottom-sheet/cell.native.js.map +1 -1
  36. package/build/mobile/link-picker/index.native.js +1 -1
  37. package/build/mobile/link-picker/index.native.js.map +1 -1
  38. package/build/navigation/menu/menu-title-search.js +1 -1
  39. package/build/navigation/menu/menu-title-search.js.map +1 -1
  40. package/build/palette-edit/index.js +4 -4
  41. package/build/palette-edit/index.js.map +1 -1
  42. package/build-module/box-control/input-control.js +2 -2
  43. package/build-module/box-control/input-control.js.map +1 -1
  44. package/build-module/calendar/date-calendar/index.js +51 -0
  45. package/build-module/calendar/date-calendar/index.js.map +1 -0
  46. package/build-module/calendar/date-range-calendar/index.js +157 -0
  47. package/build-module/calendar/date-range-calendar/index.js.map +1 -0
  48. package/build-module/calendar/index.js +4 -0
  49. package/build-module/calendar/index.js.map +1 -0
  50. package/build-module/calendar/types.js +2 -0
  51. package/build-module/calendar/types.js.map +1 -0
  52. package/build-module/calendar/utils/constants.js +61 -0
  53. package/build-module/calendar/utils/constants.js.map +1 -0
  54. package/build-module/calendar/utils/day-cell.js +131 -0
  55. package/build-module/calendar/utils/day-cell.js.map +1 -0
  56. package/build-module/calendar/utils/misc.js +4 -0
  57. package/build-module/calendar/utils/misc.js.map +1 -0
  58. package/build-module/calendar/utils/use-controlled-value.js +51 -0
  59. package/build-module/calendar/utils/use-controlled-value.js.map +1 -0
  60. package/build-module/calendar/utils/use-localization-props.js +154 -0
  61. package/build-module/calendar/utils/use-localization-props.js.map +1 -0
  62. package/build-module/custom-gradient-picker/gradient-bar/control-points.js +1 -1
  63. package/build-module/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  64. package/build-module/custom-select-control-v2/custom-select.js +4 -4
  65. package/build-module/custom-select-control-v2/custom-select.js.map +1 -1
  66. package/build-module/date-time/date/index.js +1 -1
  67. package/build-module/date-time/date/index.js.map +1 -1
  68. package/build-module/form-token-field/index.js +11 -1
  69. package/build-module/form-token-field/index.js.map +1 -1
  70. package/build-module/form-token-field/token.js +1 -1
  71. package/build-module/form-token-field/token.js.map +1 -1
  72. package/build-module/index.js +1 -0
  73. package/build-module/index.js.map +1 -1
  74. package/build-module/mobile/bottom-sheet/cell.native.js +2 -2
  75. package/build-module/mobile/bottom-sheet/cell.native.js.map +1 -1
  76. package/build-module/mobile/link-picker/index.native.js +1 -1
  77. package/build-module/mobile/link-picker/index.native.js.map +1 -1
  78. package/build-module/navigation/menu/menu-title-search.js +1 -1
  79. package/build-module/navigation/menu/menu-title-search.js.map +1 -1
  80. package/build-module/palette-edit/index.js +4 -4
  81. package/build-module/palette-edit/index.js.map +1 -1
  82. package/build-style/style-rtl.css +358 -5
  83. package/build-style/style.css +358 -5
  84. package/build-types/box-control/input-control.d.ts.map +1 -1
  85. package/build-types/box-control/utils.d.ts +7 -7
  86. package/build-types/calendar/date-calendar/index.d.ts +11 -0
  87. package/build-types/calendar/date-calendar/index.d.ts.map +1 -0
  88. package/build-types/calendar/date-range-calendar/index.d.ts +14 -0
  89. package/build-types/calendar/date-range-calendar/index.d.ts.map +1 -0
  90. package/build-types/calendar/index.d.ts +4 -0
  91. package/build-types/calendar/index.d.ts.map +1 -0
  92. package/build-types/calendar/stories/date-calendar.story.d.ts +16 -0
  93. package/build-types/calendar/stories/date-calendar.story.d.ts.map +1 -0
  94. package/build-types/calendar/stories/date-range-calendar.story.d.ts +16 -0
  95. package/build-types/calendar/stories/date-range-calendar.story.d.ts.map +1 -0
  96. package/build-types/calendar/test/__utils__/index.d.ts +10 -0
  97. package/build-types/calendar/test/__utils__/index.d.ts.map +1 -0
  98. package/build-types/calendar/test/date-calendar.d.ts +2 -0
  99. package/build-types/calendar/test/date-calendar.d.ts.map +1 -0
  100. package/build-types/calendar/test/date-range-calendar.d.ts +2 -0
  101. package/build-types/calendar/test/date-range-calendar.d.ts.map +1 -0
  102. package/build-types/calendar/types.d.ts +317 -0
  103. package/build-types/calendar/types.d.ts.map +1 -0
  104. package/build-types/calendar/utils/constants.d.ts +52 -0
  105. package/build-types/calendar/utils/constants.d.ts.map +1 -0
  106. package/build-types/calendar/utils/day-cell.d.ts +21 -0
  107. package/build-types/calendar/utils/day-cell.d.ts.map +1 -0
  108. package/build-types/calendar/utils/misc.d.ts +2 -0
  109. package/build-types/calendar/utils/misc.d.ts.map +1 -0
  110. package/build-types/calendar/utils/use-controlled-value.d.ts +27 -0
  111. package/build-types/calendar/utils/use-controlled-value.d.ts.map +1 -0
  112. package/build-types/calendar/utils/use-localization-props.d.ts +64 -0
  113. package/build-types/calendar/utils/use-localization-props.d.ts.map +1 -0
  114. package/build-types/custom-gradient-picker/constants.d.ts +6 -3
  115. package/build-types/custom-gradient-picker/constants.d.ts.map +1 -1
  116. package/build-types/custom-select-control-v2/custom-select.d.ts.map +1 -1
  117. package/build-types/dimension-control/sizes.d.ts +15 -3
  118. package/build-types/dimension-control/sizes.d.ts.map +1 -1
  119. package/build-types/font-size-picker/constants.d.ts +2 -2
  120. package/build-types/font-size-picker/constants.d.ts.map +1 -1
  121. package/build-types/form-token-field/index.d.ts.map +1 -1
  122. package/build-types/index.d.ts +1 -0
  123. package/build-types/index.d.ts.map +1 -1
  124. package/build-types/popover/overlay-middlewares.d.ts +6 -1
  125. package/build-types/popover/overlay-middlewares.d.ts.map +1 -1
  126. package/package.json +21 -20
  127. package/src/box-control/input-control.tsx +14 -5
  128. package/src/calendar/date-calendar/README.md +250 -0
  129. package/src/calendar/date-calendar/index.tsx +55 -0
  130. package/src/calendar/date-range-calendar/README.md +287 -0
  131. package/src/calendar/date-range-calendar/index.tsx +203 -0
  132. package/src/calendar/index.tsx +3 -0
  133. package/src/calendar/stories/date-calendar.story.tsx +221 -0
  134. package/src/calendar/stories/date-range-calendar.story.tsx +230 -0
  135. package/src/calendar/style.scss +431 -0
  136. package/src/calendar/test/__utils__/index.ts +56 -0
  137. package/src/calendar/test/date-calendar.tsx +975 -0
  138. package/src/calendar/test/date-range-calendar.tsx +1701 -0
  139. package/src/calendar/types.ts +342 -0
  140. package/src/calendar/utils/constants.ts +62 -0
  141. package/src/calendar/utils/day-cell.tsx +133 -0
  142. package/src/calendar/utils/misc.ts +3 -0
  143. package/src/calendar/utils/use-controlled-value.ts +61 -0
  144. package/src/calendar/utils/use-localization-props.ts +169 -0
  145. package/src/custom-gradient-picker/gradient-bar/control-points.tsx +1 -1
  146. package/src/custom-select-control-v2/custom-select.tsx +6 -3
  147. package/src/date-time/date/index.tsx +1 -1
  148. package/src/form-token-field/index.tsx +12 -1
  149. package/src/form-token-field/token.tsx +1 -1
  150. package/src/index.ts +1 -0
  151. package/src/mobile/bottom-sheet/cell.native.js +2 -2
  152. package/src/mobile/link-picker/index.native.js +1 -1
  153. package/src/navigation/menu/menu-title-search.tsx +1 -1
  154. package/src/palette-edit/index.tsx +4 -4
  155. package/src/select-control/style.scss +0 -6
  156. package/src/style.scss +1 -0
  157. package/tsconfig.tsbuildinfo +1 -1
@@ -8,6 +8,11 @@ export declare function overlayMiddlewares(): ({
8
8
  fn: (state: MiddlewareState) => import("@floating-ui/core").MiddlewareReturn | Promise<import("@floating-ui/core").MiddlewareReturn>;
9
9
  } | {
10
10
  name: string;
11
- fn({ rects }: MiddlewareState): import("@floating-ui/utils").Rect;
11
+ fn({ rects }: MiddlewareState): {
12
+ x: number;
13
+ y: number;
14
+ height: number;
15
+ width: number;
16
+ };
12
17
  })[];
13
18
  //# sourceMappingURL=overlay-middlewares.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overlay-middlewares.d.ts","sourceRoot":"","sources":["../../src/popover/overlay-middlewares.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,wBAAgB,kBAAkB;;;;;;kBAIhB,eAAe;KAqBhC"}
1
+ {"version":3,"file":"overlay-middlewares.d.ts","sourceRoot":"","sources":["../../src/popover/overlay-middlewares.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,wBAAgB,kBAAkB;;;;;;kBAIhB,eAAe;;;;;;KAqBhC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/components",
3
- "version": "29.12.0",
3
+ "version": "29.13.1-next.719a03cbe.0",
4
4
  "description": "UI components for WordPress.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -40,27 +40,27 @@
40
40
  "@emotion/serialize": "^1.0.2",
41
41
  "@emotion/styled": "^11.6.0",
42
42
  "@emotion/utils": "^1.0.0",
43
- "@floating-ui/react-dom": "^2.0.8",
43
+ "@floating-ui/react-dom": "2.0.8",
44
44
  "@types/gradient-parser": "0.1.3",
45
45
  "@types/highlight-words-core": "1.2.1",
46
46
  "@use-gesture/react": "^10.3.1",
47
- "@wordpress/a11y": "^4.26.0",
48
- "@wordpress/compose": "^7.26.0",
49
- "@wordpress/date": "^5.26.0",
50
- "@wordpress/deprecated": "^4.26.0",
51
- "@wordpress/dom": "^4.26.0",
52
- "@wordpress/element": "^6.26.0",
53
- "@wordpress/escape-html": "^3.26.0",
54
- "@wordpress/hooks": "^4.26.0",
55
- "@wordpress/html-entities": "^4.26.0",
56
- "@wordpress/i18n": "^5.26.0",
57
- "@wordpress/icons": "^10.26.0",
58
- "@wordpress/is-shallow-equal": "^5.26.0",
59
- "@wordpress/keycodes": "^4.26.0",
60
- "@wordpress/primitives": "^4.26.0",
61
- "@wordpress/private-apis": "^1.26.0",
62
- "@wordpress/rich-text": "^7.26.0",
63
- "@wordpress/warning": "^3.26.0",
47
+ "@wordpress/a11y": "^4.26.1-next.719a03cbe.0",
48
+ "@wordpress/compose": "^7.26.1-next.719a03cbe.0",
49
+ "@wordpress/date": "^5.26.1-next.719a03cbe.0",
50
+ "@wordpress/deprecated": "^4.26.1-next.719a03cbe.0",
51
+ "@wordpress/dom": "^4.26.1-next.719a03cbe.0",
52
+ "@wordpress/element": "^6.26.1-next.719a03cbe.0",
53
+ "@wordpress/escape-html": "^3.26.1-next.719a03cbe.0",
54
+ "@wordpress/hooks": "^4.26.1-next.719a03cbe.0",
55
+ "@wordpress/html-entities": "^4.26.1-next.719a03cbe.0",
56
+ "@wordpress/i18n": "^6.0.1-next.719a03cbe.0",
57
+ "@wordpress/icons": "^10.26.2-next.719a03cbe.0",
58
+ "@wordpress/is-shallow-equal": "^5.26.1-next.719a03cbe.0",
59
+ "@wordpress/keycodes": "^4.26.1-next.719a03cbe.0",
60
+ "@wordpress/primitives": "^4.26.1-next.719a03cbe.0",
61
+ "@wordpress/private-apis": "^1.26.1-next.719a03cbe.0",
62
+ "@wordpress/rich-text": "^7.26.1-next.719a03cbe.0",
63
+ "@wordpress/warning": "^3.26.1-next.719a03cbe.0",
64
64
  "change-case": "^4.1.2",
65
65
  "clsx": "^2.1.1",
66
66
  "colord": "^2.7.0",
@@ -75,6 +75,7 @@
75
75
  "path-to-regexp": "^6.2.1",
76
76
  "re-resizable": "^6.4.0",
77
77
  "react-colorful": "^5.3.1",
78
+ "react-day-picker": "^9.7.0",
78
79
  "remove-accents": "^0.5.0",
79
80
  "uuid": "^9.0.1"
80
81
  },
@@ -85,5 +86,5 @@
85
86
  "publishConfig": {
86
87
  "access": "public"
87
88
  },
88
- "gitHead": "35e26942820d8237771af0c58e45b4303f0497f1"
89
+ "gitHead": "5e146e949c2765411a8310bcc2641a88d036a6d9"
89
90
  }
@@ -178,14 +178,23 @@ export default function BoxInputControl( {
178
178
  const presetIndex = hasPresetValue
179
179
  ? getPresetIndexFromValue( mergedValue, presetKey, presets )
180
180
  : undefined;
181
- const marks = hasPresets
182
- ? [ { value: 0, label: '', tooltip: __( 'None' ) } ].concat(
183
- presets.map( ( preset, index ) => ( {
181
+ const marks: Array< {
182
+ value: number;
183
+ label: string;
184
+ tooltip: string;
185
+ } > = hasPresets
186
+ ? [
187
+ {
188
+ value: 0,
189
+ label: '',
190
+ tooltip: __( 'None' ),
191
+ },
192
+ ...presets.map( ( preset, index ) => ( {
184
193
  value: index + 1,
185
194
  label: '',
186
195
  tooltip: preset.name ?? preset.slug,
187
- } ) )
188
- )
196
+ } ) ),
197
+ ]
189
198
  : [];
190
199
 
191
200
  return (
@@ -0,0 +1,250 @@
1
+ # `DateCalendar`
2
+
3
+ `DateCalendar` is a React component that provides a customizable calendar interface for **single date** selection.
4
+
5
+ The component is built with accessibility in mind and follows ARIA best practices for calendar widgets. It provides keyboard navigation, screen reader support, and customizable labels for internationalization.
6
+
7
+ ## Usage example
8
+
9
+ ```tsx
10
+ import { DateCalendar } from '@automattic/components';
11
+
12
+ function MyComponent() {
13
+ const [ selected, setSelected ] = useState< Date >( new Date() );
14
+
15
+ return <DateCalendar selected={ selected } onSelect={ setSelected } />;
16
+ }
17
+ ```
18
+
19
+ ## Props
20
+
21
+ These props are shared between both single date and date range calendar modes.
22
+
23
+ ### `required`
24
+
25
+ - Type: `boolean`
26
+ - Required: No
27
+ - Default: `false`
28
+
29
+ Whether the selection is required. When `true`, there always needs to be a date selected.
30
+
31
+ ### `selected`
32
+
33
+ - Type: `Date | undefined | null`
34
+ - Required: No
35
+
36
+ The selected date.
37
+
38
+ ### `onSelect`
39
+
40
+ - Type: `(selected: Date | undefined, triggerDate: Date, modifiers: Modifiers, e: React.MouseEvent | React.KeyboardEvent) => void`
41
+ - Required: No
42
+
43
+ Event handler when a day is selected.
44
+
45
+ ### `defaultSelected`
46
+
47
+ - Type: `Date`
48
+ - Required: No
49
+
50
+ The default selected date (for uncontrolled usage).
51
+
52
+ ### `defaultMonth`
53
+
54
+ - Type: `Date`
55
+ - Required: No
56
+ - Default: Current month
57
+
58
+ The initial month to show in the calendar view (uncontrolled).
59
+
60
+ ### `month`
61
+
62
+ - Type: `Date`
63
+ - Required: No
64
+
65
+ The month displayed in the calendar view (controlled). Use together with `onMonthChange` to change the month programmatically.
66
+
67
+ ### `numberOfMonths`
68
+
69
+ - Type: `number`
70
+ - Required: No
71
+ - Default: `1`
72
+
73
+ The number of months displayed at once.
74
+
75
+ ### `startMonth`
76
+
77
+ - Type: `Date`
78
+ - Required: No
79
+
80
+ The earliest month to start the month navigation.
81
+
82
+ ### `endMonth`
83
+
84
+ - Type: `Date`
85
+ - Required: No
86
+
87
+ The latest month to end the month navigation.
88
+
89
+ ### `autoFocus`
90
+
91
+ - Type: `boolean`
92
+ - Required: No
93
+
94
+ Focus the first selected day (if set) or today's date (if not disabled). Use this prop when you need to focus the calendar after a user action (e.g. opening the dialog with the calendar).
95
+
96
+ ### `disabled`
97
+
98
+ - Type: `Matcher | Matcher[] | undefined`
99
+ - Required: No
100
+
101
+ Specify which days are disabled. Using `true` will disable all dates. See the [Matcher Types](#matcher-types) section for more details.
102
+
103
+ ### `disableNavigation`
104
+
105
+ - Type: `boolean`
106
+ - Required: No
107
+
108
+ Disable the navigation buttons.
109
+
110
+ ### `labels`
111
+
112
+ - Type: `object`
113
+ - Required: No
114
+
115
+ Use custom labels for internationalization. All labels are optional and have sensible defaults:
116
+
117
+ ```typescript
118
+ {
119
+ labelNav?: () => string; // Navigation toolbar label
120
+ labelGrid?: (date: Date) => string; // Month grid label (default: "LLLL y")
121
+ labelGridcell?: (date: Date, modifiers?: Modifiers) => string; // Grid cell label
122
+ labelNext?: (month: Date | undefined) => string; // Next month button label
123
+ labelPrevious?: (month: Date | undefined) => string; // Previous month button label
124
+ labelDayButton?: (date: Date, modifiers?: Modifiers) => string; // Day button label
125
+ labelWeekday?: (date: Date) => string; // Weekday label
126
+ }
127
+ ```
128
+
129
+ **Important: For a correct localized experience, consumers should make sure the locale used for the translated labels and `locale` prop are consistent.**
130
+
131
+ ### `locale`
132
+
133
+ - Type: `Locale`
134
+ - Required: No
135
+ - Default: `enUS` from `@date-fns/locale`
136
+
137
+ The locale object used to localize dates. Pass a locale from `@date-fns/locale` to localize the calendar.
138
+
139
+ **Important: For a correct localized experience, consumers should make sure the locale used for the translated labels and `locale` prop are consistent.**
140
+
141
+ ### `weekStartsOn`
142
+
143
+ - Type: `0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined`
144
+ - Required: No
145
+ - Default: Based on the `locale` prop
146
+
147
+ The index of the first day of the week (0 - Sunday). Overrides the locale's setting.
148
+
149
+ ### `onMonthChange`
150
+
151
+ - Type: `(month: Date) => void`
152
+ - Required: No
153
+
154
+ Event fired when the user navigates between months.
155
+
156
+ ### `timeZone`
157
+
158
+ - Type: `string`
159
+ - Required: No
160
+
161
+ The time zone (IANA or UTC offset) to use in the calendar. See [Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for possible values.
162
+
163
+ When working with time zones, use the `TZDate` object exported by this package instead of the native `Date` object.
164
+
165
+ ```tsx
166
+ import { DateCalendar, TZDate } from '@automattic/components';
167
+
168
+ export function WithTimeZone() {
169
+ const timeZone = 'America/New_York';
170
+ const [ selected, setSelected ] = useState< Date | undefined >(
171
+ new TZDate( 2024, 12, 10, timeZone ) // Use `TZDate` instead of `Date`
172
+ );
173
+ return <DateCalendar timeZone={ timeZone } selected={ selected } onSelect={ setSelected } />;
174
+ }
175
+ ```
176
+
177
+ ### `role`
178
+
179
+ - Type: `'application' | 'dialog' | undefined`
180
+ - Required: No
181
+ - Default: `'application'`
182
+
183
+ The role attribute to add to the container element.
184
+
185
+ ## Matcher Types
186
+
187
+ The calendar component uses a flexible matching system to determine which days should be disabled or have specific modifiers. Here are the available matcher types:
188
+
189
+ ### Boolean Matcher
190
+
191
+ ```typescript
192
+ const booleanMatcher: Matcher = true; // Will always match the day
193
+ ```
194
+
195
+ ### Date Matcher
196
+
197
+ ```typescript
198
+ const dateMatcher: Matcher = new Date(); // Will match today's date
199
+ ```
200
+
201
+ ### Array Matcher
202
+
203
+ ```typescript
204
+ const arrayMatcher: Matcher = [ new Date( 2019, 1, 2 ), new Date( 2019, 1, 4 ) ]; // Will match the days in the array
205
+ ```
206
+
207
+ ### Date After Matcher
208
+
209
+ ```typescript
210
+ const afterMatcher: DateAfter = { after: new Date( 2019, 1, 2 ) }; // Will match days after the 2nd of February 2019
211
+ ```
212
+
213
+ ### Date Before Matcher
214
+
215
+ ```typescript
216
+ const beforeMatcher: DateBefore = { before: new Date( 2019, 1, 2 ) }; // Will match days before the 2nd of February 2019
217
+ ```
218
+
219
+ ### Date Interval Matcher
220
+
221
+ ```typescript
222
+ const intervalMatcher: DateInterval = {
223
+ after: new Date( 2019, 1, 2 ),
224
+ before: new Date( 2019, 1, 5 ),
225
+ }; // Will match the days between the 2nd and the 5th of February 2019 (exclusive)
226
+ ```
227
+
228
+ ### Date Range Matcher
229
+
230
+ ```typescript
231
+ const rangeMatcher: DateRange = {
232
+ from: new Date( 2019, 1, 2 ),
233
+ to: new Date( 2019, 1, 5 ),
234
+ }; // Will match the days between the 2nd and the 5th of February 2019 (inclusive)
235
+ ```
236
+
237
+ ### Day of Week Matcher
238
+
239
+ ```typescript
240
+ const dayOfWeekMatcher: DayOfWeek = { dayOfWeek: 0 }; // Will match Sundays
241
+ const weekendMatcher: DayOfWeek = { dayOfWeek: [ 0, 6 ] }; // Will match weekends
242
+ ```
243
+
244
+ ### Function Matcher
245
+
246
+ ```typescript
247
+ const functionMatcher: Matcher = ( day: Date ) => {
248
+ return day.getMonth() === 2; // Will match when month is March
249
+ };
250
+ ```
@@ -0,0 +1,55 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { DayPicker } from 'react-day-picker';
5
+ import { enUS } from 'react-day-picker/locale';
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { COMMON_PROPS } from '../utils/constants';
10
+ import { clampNumberOfMonths } from '../utils/misc';
11
+ import { useControlledValue } from '../utils/use-controlled-value';
12
+ import { useLocalizationProps } from '../utils/use-localization-props';
13
+ import type { DateCalendarProps } from '../types';
14
+
15
+ /**
16
+ * `DateCalendar` is a React component that provides a customizable calendar
17
+ * interface for **single date** selection.
18
+ *
19
+ * The component is built with accessibility in mind and follows ARIA best
20
+ * practices for calendar widgets. It provides keyboard navigation, screen reader
21
+ * support, and customizable labels for internationalization.
22
+ */
23
+ export const DateCalendar = ( {
24
+ defaultSelected,
25
+ selected: selectedProp,
26
+ onSelect,
27
+ numberOfMonths = 1,
28
+ locale = enUS,
29
+ timeZone,
30
+ ...props
31
+ }: DateCalendarProps ) => {
32
+ const localizationProps = useLocalizationProps( {
33
+ locale,
34
+ timeZone,
35
+ mode: 'single',
36
+ } );
37
+
38
+ const [ selected, setSelected ] = useControlledValue< Date | undefined >( {
39
+ defaultValue: defaultSelected,
40
+ value: selectedProp,
41
+ onChange: onSelect,
42
+ } );
43
+
44
+ return (
45
+ <DayPicker
46
+ { ...COMMON_PROPS }
47
+ { ...localizationProps }
48
+ { ...props }
49
+ mode="single"
50
+ numberOfMonths={ clampNumberOfMonths( numberOfMonths ) }
51
+ selected={ selected }
52
+ onSelect={ setSelected }
53
+ />
54
+ );
55
+ };
@@ -0,0 +1,287 @@
1
+ # `DateRangeCalendar`
2
+
3
+ `DateRangeCalendar` is a React component that provides a customizable calendar interface for **date range** selection.
4
+
5
+ The component is built with accessibility in mind and follows ARIA best practices for calendar widgets. It provides keyboard navigation, screen reader support, and customizable labels for internationalization.
6
+
7
+ ## Usage example
8
+
9
+ ```tsx
10
+ import { DateRangeCalendar } from '@automattic/components';
11
+
12
+ type DateRange = {
13
+ from: Date | undefined;
14
+ to?: Date | undefined;
15
+ };
16
+
17
+ function MyComponent() {
18
+ const [ selected, setSelected ] = useState< DateRange >( {
19
+ from: new Date( date.getFullYear(), date.getMonth(), 1 ),
20
+ to: new Date(),
21
+ } );
22
+
23
+ return <DateRangeCalendar selected={ selected } onSelect={ setSelected } />;
24
+ }
25
+ ```
26
+
27
+ ## Props
28
+
29
+ These props are shared between both single date and date range calendar modes.
30
+
31
+ ### `required`
32
+
33
+ - Type: `boolean`
34
+ - Required: No
35
+ - Default: `false`
36
+
37
+ Whether the selection is required. When `true`, there always needs to be a date selected.
38
+
39
+ ### `selected`
40
+
41
+ - Type: `DateRange | undefined | null`
42
+ - Required: No
43
+
44
+ The selected date range. A `DateRange` object has the following shape:
45
+
46
+ ```typescript
47
+ {
48
+ from: Date | undefined;
49
+ to?: Date | undefined;
50
+ }
51
+ ```
52
+
53
+ ### `onSelect`
54
+
55
+ - Type: `(selected: DateRange | undefined, triggerDate: Date, modifiers: Modifiers, e: React.MouseEvent | React.KeyboardEvent) => void`
56
+ - Required: No
57
+
58
+ Event handler when the selection changes. The `selected` parameter will contain the new date range.
59
+
60
+ ### `defaultSelected`
61
+
62
+ - Type: `DateRange`
63
+ - Required: No
64
+
65
+ The default selected range (for uncontrolled usage).
66
+
67
+ ### `excludeDisabled`
68
+
69
+ - Type: `boolean`
70
+ - Required: No
71
+
72
+ When `true`, the range will reset when including a disabled day. This is useful to prevent users from selecting ranges that include unavailable dates.
73
+
74
+ ### `min`
75
+
76
+ - Type: `number`
77
+ - Required: No
78
+
79
+ The minimum number of days to include in the range. If a user tries to select a range shorter than this, the selection will be adjusted to meet the minimum requirement.
80
+
81
+ ### `max`
82
+
83
+ - Type: `number`
84
+ - Required: No
85
+
86
+ The maximum number of days to include in the range. If a user tries to select a range longer than this, the selection will be adjusted to meet the maximum requirement.
87
+
88
+ ### `defaultMonth`
89
+
90
+ - Type: `Date`
91
+ - Required: No
92
+ - Default: Current month
93
+
94
+ The initial month to show in the calendar view (uncontrolled).
95
+
96
+ ### `month`
97
+
98
+ - Type: `Date`
99
+ - Required: No
100
+
101
+ The month displayed in the calendar view (controlled). Use together with `onMonthChange` to change the month programmatically.
102
+
103
+ ### `numberOfMonths`
104
+
105
+ - Type: `number`
106
+ - Required: No
107
+ - Default: `1`
108
+
109
+ The number of months displayed at once.
110
+
111
+ ### `startMonth`
112
+
113
+ - Type: `Date`
114
+ - Required: No
115
+
116
+ The earliest month to start the month navigation.
117
+
118
+ ### `endMonth`
119
+
120
+ - Type: `Date`
121
+ - Required: No
122
+
123
+ The latest month to end the month navigation.
124
+
125
+ ### `autoFocus`
126
+
127
+ - Type: `boolean`
128
+ - Required: No
129
+
130
+ Focus the first selected day (if set) or today's date (if not disabled). Use this prop when you need to focus the calendar after a user action (e.g. opening the dialog with the calendar).
131
+
132
+ ### `disabled`
133
+
134
+ - Type: `Matcher | Matcher[] | undefined`
135
+ - Required: No
136
+
137
+ Specify which days are disabled. Using `true` will disable all dates. See the [Matcher Types](#matcher-types) section for more details.
138
+
139
+ ### `disableNavigation`
140
+
141
+ - Type: `boolean`
142
+ - Required: No
143
+
144
+ Disable the navigation buttons.
145
+
146
+ ### `labels`
147
+
148
+ - Type: `object`
149
+ - Required: No
150
+
151
+ Use custom labels for internationalization. All labels are optional and have sensible defaults:
152
+
153
+ ```typescript
154
+ {
155
+ labelNav?: () => string; // Navigation toolbar label
156
+ labelGrid?: (date: Date) => string; // Month grid label (default: "LLLL y")
157
+ labelGridcell?: (date: Date, modifiers?: Modifiers) => string; // Grid cell label
158
+ labelNext?: (month: Date | undefined) => string; // Next month button label
159
+ labelPrevious?: (month: Date | undefined) => string; // Previous month button label
160
+ labelDayButton?: (date: Date, modifiers?: Modifiers) => string; // Day button label
161
+ labelWeekday?: (date: Date) => string; // Weekday label
162
+ }
163
+ ```
164
+
165
+ **Important: For a correct localized experience, consumers should make sure the locale used for the translated labels and `locale` prop are consistent.**
166
+
167
+ ### `locale`
168
+
169
+ - Type: `Locale`
170
+ - Required: No
171
+ - Default: `enUS` from `@date-fns/locale`
172
+
173
+ The locale object used to localize dates. Pass a locale from `@date-fns/locale` to localize the calendar.
174
+
175
+ **Important: For a correct localized experience, consumers should make sure the locale used for the translated labels and `locale` prop are consistent.**
176
+
177
+ ### `weekStartsOn`
178
+
179
+ - Type: `0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined`
180
+ - Required: No
181
+ - Default: Based on the `locale` prop
182
+
183
+ The index of the first day of the week (0 - Sunday). Overrides the locale's setting.
184
+
185
+ ### `onMonthChange`
186
+
187
+ - Type: `(month: Date) => void`
188
+ - Required: No
189
+
190
+ Event fired when the user navigates between months.
191
+
192
+ ### `timeZone`
193
+
194
+ - Type: `string`
195
+ - Required: No
196
+
197
+ The time zone (IANA or UTC offset) to use in the calendar. See [Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for possible values.
198
+
199
+ When working with time zones, use the `TZDate` object exported by this package instead of the native `Date` object.
200
+
201
+ ```tsx
202
+ import { DateRangeCalendar, TZDate } from '@automattic/components';
203
+
204
+ export function WithTimeZone() {
205
+ const timeZone = 'America/New_York';
206
+ const [ selected, setSelected ] = useState< Date | undefined >( {
207
+ from: new TZDate( 2024, 12, 10, timeZone ), // Use `TZDate` instead of `Date`
208
+ to: new TZDate( 2024, 12, 8, timeZone ), // Use `TZDate` instead of `Date`
209
+ } );
210
+ return <DateRangeCalendar timeZone={ timeZone } selected={ selected } onSelect={ setSelected } />;
211
+ }
212
+ ```
213
+
214
+ ### `role`
215
+
216
+ - Type: `'application' | 'dialog' | undefined`
217
+ - Required: No
218
+ - Default: `'application'`
219
+
220
+ The role attribute to add to the container element.
221
+
222
+ ## Matcher Types
223
+
224
+ The calendar component uses a flexible matching system to determine which days should be disabled or have specific modifiers. Here are the available matcher types:
225
+
226
+ ### Boolean Matcher
227
+
228
+ ```typescript
229
+ const booleanMatcher: Matcher = true; // Will always match the day
230
+ ```
231
+
232
+ ### Date Matcher
233
+
234
+ ```typescript
235
+ const dateMatcher: Matcher = new Date(); // Will match today's date
236
+ ```
237
+
238
+ ### Array Matcher
239
+
240
+ ```typescript
241
+ const arrayMatcher: Matcher = [ new Date( 2019, 1, 2 ), new Date( 2019, 1, 4 ) ]; // Will match the days in the array
242
+ ```
243
+
244
+ ### Date After Matcher
245
+
246
+ ```typescript
247
+ const afterMatcher: DateAfter = { after: new Date( 2019, 1, 2 ) }; // Will match days after the 2nd of February 2019
248
+ ```
249
+
250
+ ### Date Before Matcher
251
+
252
+ ```typescript
253
+ const beforeMatcher: DateBefore = { before: new Date( 2019, 1, 2 ) }; // Will match days before the 2nd of February 2019
254
+ ```
255
+
256
+ ### Date Interval Matcher
257
+
258
+ ```typescript
259
+ const intervalMatcher: DateInterval = {
260
+ after: new Date( 2019, 1, 2 ),
261
+ before: new Date( 2019, 1, 5 ),
262
+ }; // Will match the days between the 2nd and the 5th of February 2019 (exclusive)
263
+ ```
264
+
265
+ ### Date Range Matcher
266
+
267
+ ```typescript
268
+ const rangeMatcher: DateRange = {
269
+ from: new Date( 2019, 1, 2 ),
270
+ to: new Date( 2019, 1, 5 ),
271
+ }; // Will match the days between the 2nd and the 5th of February 2019 (inclusive)
272
+ ```
273
+
274
+ ### Day of Week Matcher
275
+
276
+ ```typescript
277
+ const dayOfWeekMatcher: DayOfWeek = { dayOfWeek: 0 }; // Will match Sundays
278
+ const weekendMatcher: DayOfWeek = { dayOfWeek: [ 0, 6 ] }; // Will match weekends
279
+ ```
280
+
281
+ ### Function Matcher
282
+
283
+ ```typescript
284
+ const functionMatcher: Matcher = ( day: Date ) => {
285
+ return day.getMonth() === 2; // Will match when month is March
286
+ };
287
+ ```