gbs-add-block 1.2.9 → 1.2.11

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 (75) hide show
  1. package/README.md +12 -2
  2. package/index.cjs +85 -32
  3. package/package.json +1 -1
  4. package/source/beta-components/datepicker/README.md +141 -0
  5. package/source/beta-components/datepicker/__tests__/core.test.ts +220 -0
  6. package/source/beta-components/datepicker/core/calendar.ts +263 -0
  7. package/source/beta-components/datepicker/core/format.ts +170 -0
  8. package/source/beta-components/datepicker/core/index.ts +40 -0
  9. package/source/beta-components/datepicker/core/types.ts +68 -0
  10. package/source/beta-components/datepicker/index.ts +16 -0
  11. package/source/beta-components/datepicker/react/Calendar.tsx +160 -0
  12. package/source/beta-components/datepicker/react/CalendarPanel.tsx +170 -0
  13. package/source/beta-components/datepicker/react/DatePicker.tsx +249 -0
  14. package/source/beta-components/datepicker/react/DateRangePicker.tsx +291 -0
  15. package/source/beta-components/datepicker/react/MonthYearPanels.tsx +123 -0
  16. package/source/beta-components/datepicker/react/Popover.tsx +87 -0
  17. package/source/beta-components/datepicker/react/icons.tsx +32 -0
  18. package/source/beta-components/datepicker/react/locale.ts +23 -0
  19. package/source/beta-components/datepicker/react/props.ts +76 -0
  20. package/source/beta-components/datepicker/react/useDatePicker.ts +547 -0
  21. package/source/beta-components/datepicker/styles.css +446 -0
  22. /package/source/{components → beta-components}/combobox/README.md +0 -0
  23. /package/source/{components → beta-components}/combobox/__tests__/core.test.ts +0 -0
  24. /package/source/{components → beta-components}/combobox/core/filter.ts +0 -0
  25. /package/source/{components → beta-components}/combobox/core/index.ts +0 -0
  26. /package/source/{components → beta-components}/combobox/core/types.ts +0 -0
  27. /package/source/{components → beta-components}/combobox/core/virtual.ts +0 -0
  28. /package/source/{components → beta-components}/combobox/index.ts +0 -0
  29. /package/source/{components → beta-components}/combobox/react/Listbox.tsx +0 -0
  30. /package/source/{components → beta-components}/combobox/react/MultiSelect.tsx +0 -0
  31. /package/source/{components → beta-components}/combobox/react/Popover.tsx +0 -0
  32. /package/source/{components → beta-components}/combobox/react/Select.tsx +0 -0
  33. /package/source/{components → beta-components}/combobox/react/icons.tsx +0 -0
  34. /package/source/{components → beta-components}/combobox/react/locale.ts +0 -0
  35. /package/source/{components → beta-components}/combobox/react/props.ts +0 -0
  36. /package/source/{components → beta-components}/combobox/react/useCombobox.ts +0 -0
  37. /package/source/{components → beta-components}/combobox/styles.css +0 -0
  38. /package/source/{components/datagridbeta → beta-components/datagrid}/README.md +0 -0
  39. /package/source/{components/datagridbeta → beta-components/datagrid}/__tests__/core.test.ts +0 -0
  40. /package/source/{components/datagridbeta → beta-components/datagrid}/__tests__/export.test.ts +0 -0
  41. /package/source/{components/datagridbeta → beta-components/datagrid}/core/columnHelper.ts +0 -0
  42. /package/source/{components/datagridbeta → beta-components/datagrid}/core/columns.ts +0 -0
  43. /package/source/{components/datagridbeta → beta-components/datagrid}/core/filtering.ts +0 -0
  44. /package/source/{components/datagridbeta → beta-components/datagrid}/core/grid.ts +0 -0
  45. /package/source/{components/datagridbeta → beta-components/datagrid}/core/index.ts +0 -0
  46. /package/source/{components/datagridbeta → beta-components/datagrid}/core/rows.ts +0 -0
  47. /package/source/{components/datagridbeta → beta-components/datagrid}/core/sorting.ts +0 -0
  48. /package/source/{components/datagridbeta → beta-components/datagrid}/core/state.ts +0 -0
  49. /package/source/{components/datagridbeta → beta-components/datagrid}/core/store.ts +0 -0
  50. /package/source/{components/datagridbeta → beta-components/datagrid}/core/types.ts +0 -0
  51. /package/source/{components/datagridbeta → beta-components/datagrid}/core/values.ts +0 -0
  52. /package/source/{components/datagridbeta → beta-components/datagrid}/core/virtual.ts +0 -0
  53. /package/source/{components/datagridbeta → beta-components/datagrid}/export/csv.ts +0 -0
  54. /package/source/{components/datagridbeta → beta-components/datagrid}/export/download.ts +0 -0
  55. /package/source/{components/datagridbeta → beta-components/datagrid}/export/pdf.ts +0 -0
  56. /package/source/{components/datagridbeta → beta-components/datagrid}/export/table.ts +0 -0
  57. /package/source/{components/datagridbeta → beta-components/datagrid}/export/xlsx.ts +0 -0
  58. /package/source/{components/datagridbeta → beta-components/datagrid}/export/zip.ts +0 -0
  59. /package/source/{components/datagridbeta → beta-components/datagrid}/index.ts +0 -0
  60. /package/source/{components/datagridbeta → beta-components/datagrid}/react/Cell.tsx +0 -0
  61. /package/source/{components/datagridbeta → beta-components/datagrid}/react/ColumnMenu.tsx +0 -0
  62. /package/source/{components/datagridbeta → beta-components/datagrid}/react/DataGrid.tsx +0 -0
  63. /package/source/{components/datagridbeta → beta-components/datagrid}/react/FilterForm.tsx +0 -0
  64. /package/source/{components/datagridbeta → beta-components/datagrid}/react/HeaderRow.tsx +0 -0
  65. /package/source/{components/datagridbeta → beta-components/datagrid}/react/Pagination.tsx +0 -0
  66. /package/source/{components/datagridbeta → beta-components/datagrid}/react/Popover.tsx +0 -0
  67. /package/source/{components/datagridbeta → beta-components/datagrid}/react/Row.tsx +0 -0
  68. /package/source/{components/datagridbeta → beta-components/datagrid}/react/Toolbar.tsx +0 -0
  69. /package/source/{components/datagridbeta → beta-components/datagrid}/react/Viewport.tsx +0 -0
  70. /package/source/{components/datagridbeta → beta-components/datagrid}/react/context.ts +0 -0
  71. /package/source/{components/datagridbeta → beta-components/datagrid}/react/hooks.ts +0 -0
  72. /package/source/{components/datagridbeta → beta-components/datagrid}/react/icons.tsx +0 -0
  73. /package/source/{components/datagridbeta → beta-components/datagrid}/react/keyboard.ts +0 -0
  74. /package/source/{components/datagridbeta → beta-components/datagrid}/react/locale.ts +0 -0
  75. /package/source/{components/datagridbeta → beta-components/datagrid}/styles.css +0 -0
@@ -0,0 +1,263 @@
1
+ import type {
2
+ CalendarDay,
3
+ CalendarMonth,
4
+ CalendarWeek,
5
+ DateInput,
6
+ DateLimits,
7
+ DateRange,
8
+ } from "./types";
9
+
10
+ /**
11
+ * Every date in this package is a local-time Date at midnight. Keeping one
12
+ * convention means day comparisons never depend on hours, time zones or DST,
13
+ * and `toISODate` never shifts a day the way `toISOString` does.
14
+ */
15
+ export function startOfDay(date: Date): Date {
16
+ const copy = new Date(date.getTime());
17
+ copy.setHours(0, 0, 0, 0);
18
+ return copy;
19
+ }
20
+
21
+ /** Strict `yyyy-mm-dd` in local time; null for anything else, including `2026-02-31`. */
22
+ export function parseISODate(text: string): Date | null {
23
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text.trim());
24
+ if (!match) return null;
25
+ const year = Number(match[1]);
26
+ const month = Number(match[2]) - 1;
27
+ const day = Number(match[3]);
28
+ const date = new Date(year, month, day);
29
+ date.setHours(0, 0, 0, 0);
30
+ const valid =
31
+ date.getFullYear() === year && date.getMonth() === month && date.getDate() === day;
32
+ return valid ? date : null;
33
+ }
34
+
35
+ export function toISODate(date: Date): string {
36
+ const month = String(date.getMonth() + 1).padStart(2, "0");
37
+ const day = String(date.getDate()).padStart(2, "0");
38
+ return `${date.getFullYear()}-${month}-${day}`;
39
+ }
40
+
41
+ /** Normalizes a prop value. ISO strings are read as local dates, not UTC. */
42
+ export function toDate(value: DateInput | null | undefined): Date | null {
43
+ if (value === null || value === undefined) return null;
44
+ if (typeof value === "string") {
45
+ const iso = parseISODate(value);
46
+ if (iso) return iso;
47
+ }
48
+ const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
49
+ return Number.isNaN(date.getTime()) ? null : startOfDay(date);
50
+ }
51
+
52
+ /** Negative when `a` is the earlier day, 0 on the same day. Ignores the time. */
53
+ export function compareDay(a: Date, b: Date): number {
54
+ return (
55
+ a.getFullYear() - b.getFullYear() || a.getMonth() - b.getMonth() || a.getDate() - b.getDate()
56
+ );
57
+ }
58
+
59
+ export const isSameDay = (a: Date | null | undefined, b: Date | null | undefined): boolean =>
60
+ a != null && b != null && compareDay(a, b) === 0;
61
+
62
+ export const isSameMonth = (a: Date, b: Date): boolean =>
63
+ a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
64
+
65
+ export const daysInMonth = (year: number, month: number): number =>
66
+ new Date(year, month + 1, 0).getDate();
67
+
68
+ export function addDays(date: Date, amount: number): Date {
69
+ const copy = new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
70
+ copy.setHours(0, 0, 0, 0);
71
+ return copy;
72
+ }
73
+
74
+ /** Keeps the day of the month where the target month is long enough (Jan 31 + 1 → Feb 28). */
75
+ export function addMonths(date: Date, amount: number): Date {
76
+ const year = date.getFullYear();
77
+ const month = date.getMonth() + amount;
78
+ const day = Math.min(date.getDate(), daysInMonth(year, month));
79
+ const copy = new Date(year, month, day);
80
+ copy.setHours(0, 0, 0, 0);
81
+ return copy;
82
+ }
83
+
84
+ export const startOfMonth = (date: Date): Date =>
85
+ new Date(date.getFullYear(), date.getMonth(), 1);
86
+
87
+ export const endOfMonth = (date: Date): Date =>
88
+ new Date(date.getFullYear(), date.getMonth() + 1, 0);
89
+
90
+ export function clampDate(date: Date, min?: Date | null, max?: Date | null): Date {
91
+ if (min && compareDay(date, min) < 0) return startOfDay(min);
92
+ if (max && compareDay(date, max) > 0) return startOfDay(max);
93
+ return date;
94
+ }
95
+
96
+ export function isDayDisabled(date: Date, limits: DateLimits = {}): boolean {
97
+ if (limits.min && compareDay(date, limits.min) < 0) return true;
98
+ if (limits.max && compareDay(date, limits.max) > 0) return true;
99
+ return limits.isDateDisabled?.(date) === true;
100
+ }
101
+
102
+ interface WeekInfoLocale {
103
+ getWeekInfo?(): { firstDay: number };
104
+ weekInfo?: { firstDay: number };
105
+ }
106
+
107
+ /**
108
+ * First day of the week for a locale, 0 = Sunday. Browsers expose this through
109
+ * `Intl.Locale`; where they don't, ISO Monday is used unless `explicit` is given.
110
+ */
111
+ export function resolveWeekStart(locale?: string, explicit?: number): number {
112
+ if (explicit !== undefined) return ((explicit % 7) + 7) % 7;
113
+ try {
114
+ const tag = locale ?? new Intl.DateTimeFormat().resolvedOptions().locale;
115
+ const info = new Intl.Locale(tag) as unknown as WeekInfoLocale;
116
+ const firstDay = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay;
117
+ if (typeof firstDay === "number") return firstDay % 7;
118
+ } catch {
119
+ // Locale data is unavailable in this runtime; fall through to the ISO default.
120
+ }
121
+ return 1;
122
+ }
123
+
124
+ /** ISO 8601 week number: week 1 is the one holding the first Thursday of the year. */
125
+ export function getISOWeek(date: Date): number {
126
+ const target = startOfDay(date);
127
+ target.setDate(target.getDate() - ((target.getDay() + 6) % 7) + 3);
128
+ const firstThursday = new Date(target.getFullYear(), 0, 4);
129
+ firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3);
130
+ const week = 7 * 24 * 60 * 60 * 1000;
131
+ return 1 + Math.round((target.getTime() - firstThursday.getTime()) / week);
132
+ }
133
+
134
+ export interface BuildMonthOptions extends DateLimits {
135
+ /** 0 = Sunday. Default Monday. */
136
+ weekStartsOn?: number;
137
+ /** Always six week rows, so the popover height never jumps. Default true. */
138
+ fixedWeeks?: boolean;
139
+ /** Injected so a test (or a server render) can pin "today". */
140
+ today?: Date;
141
+ }
142
+
143
+ export function buildMonth(
144
+ year: number,
145
+ month: number,
146
+ options: BuildMonthOptions = {},
147
+ ): CalendarMonth {
148
+ const { weekStartsOn = 1, fixedWeeks = true, today = startOfDay(new Date()) } = options;
149
+ const first = new Date(year, month, 1);
150
+ const lead = (first.getDay() - weekStartsOn + 7) % 7;
151
+ const rows = fixedWeeks ? 6 : Math.ceil((lead + daysInMonth(year, month)) / 7);
152
+
153
+ const weeks: CalendarWeek[] = [];
154
+ let cursor = addDays(first, -lead);
155
+ for (let row = 0; row < rows; row++) {
156
+ const days: CalendarDay[] = [];
157
+ for (let column = 0; column < 7; column++) {
158
+ const weekday = cursor.getDay();
159
+ days.push({
160
+ date: cursor,
161
+ day: cursor.getDate(),
162
+ key: toISODate(cursor),
163
+ outside: cursor.getMonth() !== month || cursor.getFullYear() !== year,
164
+ today: compareDay(cursor, today) === 0,
165
+ weekend: weekday === 0 || weekday === 6,
166
+ disabled: isDayDisabled(cursor, options),
167
+ });
168
+ cursor = addDays(cursor, 1);
169
+ }
170
+ weeks.push({ weekNumber: getISOWeek(days[0].date), days });
171
+ }
172
+ return { year, month, weeks };
173
+ }
174
+
175
+ /** `count` consecutive months starting at the month of `start`. */
176
+ export function buildMonths(
177
+ start: Date,
178
+ count: number,
179
+ options: BuildMonthOptions = {},
180
+ ): CalendarMonth[] {
181
+ return Array.from({ length: Math.max(1, count) }, (_, index) => {
182
+ const month = addMonths(startOfMonth(start), index);
183
+ return buildMonth(month.getFullYear(), month.getMonth(), options);
184
+ });
185
+ }
186
+
187
+ /** Puts a backwards range the right way round, so dragging in either direction works. */
188
+ export function normalizeRange(range: DateRange): DateRange {
189
+ const { start, end } = range;
190
+ if (start && end && compareDay(start, end) > 0) return { start: end, end: start };
191
+ return range;
192
+ }
193
+
194
+ export function isInRange(date: Date, start: Date | null, end: Date | null): boolean {
195
+ if (!start || !end) return false;
196
+ return compareDay(date, start) >= 0 && compareDay(date, end) <= 0;
197
+ }
198
+
199
+ export interface MoveOptions {
200
+ weekStartsOn?: number;
201
+ /** Swaps the arrow keys, so "next day" stays to the reading-forward side. */
202
+ rtl?: boolean;
203
+ shiftKey?: boolean;
204
+ }
205
+
206
+ /** Where a key press moves the focused day, or null when the key isn't ours. */
207
+ export function moveByKey(date: Date, key: string, options: MoveOptions = {}): Date | null {
208
+ const { weekStartsOn = 1, rtl = false, shiftKey = false } = options;
209
+ const offsetInWeek = (date.getDay() - weekStartsOn + 7) % 7;
210
+ switch (key) {
211
+ case rtl ? "ArrowRight" : "ArrowLeft":
212
+ return addDays(date, -1);
213
+ case rtl ? "ArrowLeft" : "ArrowRight":
214
+ return addDays(date, 1);
215
+ case "ArrowUp":
216
+ return addDays(date, -7);
217
+ case "ArrowDown":
218
+ return addDays(date, 7);
219
+ case "Home":
220
+ return addDays(date, -offsetInWeek);
221
+ case "End":
222
+ return addDays(date, 6 - offsetInWeek);
223
+ case "PageUp":
224
+ return addMonths(date, shiftKey ? -12 : -1);
225
+ case "PageDown":
226
+ return addMonths(date, shiftKey ? 12 : 1);
227
+ default:
228
+ return null;
229
+ }
230
+ }
231
+
232
+ /** Arrow-key movement inside the month and year panels. Returns null for other keys. */
233
+ export function moveInGrid(
234
+ index: number,
235
+ key: string,
236
+ columns: number,
237
+ count: number,
238
+ rtl = false,
239
+ ): number | null {
240
+ const clamp = (next: number) => Math.max(0, Math.min(count - 1, next));
241
+ switch (key) {
242
+ case rtl ? "ArrowRight" : "ArrowLeft":
243
+ return clamp(index - 1);
244
+ case rtl ? "ArrowLeft" : "ArrowRight":
245
+ return clamp(index + 1);
246
+ case "ArrowUp":
247
+ return clamp(index - columns);
248
+ case "ArrowDown":
249
+ return clamp(index + columns);
250
+ case "Home":
251
+ return 0;
252
+ case "End":
253
+ return count - 1;
254
+ default:
255
+ return null;
256
+ }
257
+ }
258
+
259
+ /** The page of years holding `year`, so the year panel pages in steady blocks. */
260
+ export function yearPage(year: number, size = 12): number[] {
261
+ const start = Math.floor(year / size) * size;
262
+ return Array.from({ length: size }, (_, index) => start + index);
263
+ }
@@ -0,0 +1,170 @@
1
+ import { parseISODate } from "./calendar";
2
+
3
+ /** `Intl.DateTimeFormat` is expensive to build, and these are reused every render. */
4
+ const formatters = new Map<string, Intl.DateTimeFormat>();
5
+
6
+ function formatter(locale: string | undefined, options: Intl.DateTimeFormatOptions) {
7
+ const key = `${locale ?? ""}|${JSON.stringify(options)}`;
8
+ let found = formatters.get(key);
9
+ if (!found) {
10
+ found = new Intl.DateTimeFormat(locale, options);
11
+ formatters.set(key, found);
12
+ }
13
+ return found;
14
+ }
15
+
16
+ export const DEFAULT_DATE_FORMAT: Intl.DateTimeFormatOptions = {
17
+ year: "numeric",
18
+ month: "short",
19
+ day: "numeric",
20
+ };
21
+ export const DEFAULT_INPUT_PLACEHOLDER = "dd/mm/yyyy";
22
+
23
+ export function formatDate(
24
+ date: Date,
25
+ locale?: string,
26
+ options: Intl.DateTimeFormatOptions = DEFAULT_DATE_FORMAT,
27
+ ): string {
28
+ return formatter(locale, options).format(date);
29
+ }
30
+
31
+ export function formatMonthYear(year: number, month: number, locale?: string): string {
32
+ return formatter(locale, { year: "numeric", month: "long" }).format(new Date(year, month, 1));
33
+ }
34
+
35
+ export function monthNames(locale?: string, width: "long" | "short" = "long"): string[] {
36
+ const format = formatter(locale, { month: width });
37
+ return Array.from({ length: 12 }, (_, month) => format.format(new Date(2024, month, 1)));
38
+ }
39
+
40
+ export interface WeekdayName {
41
+ short: string;
42
+ long: string;
43
+ }
44
+
45
+ /** The seven weekday names, ordered from `weekStartsOn`. */
46
+ export function weekdayNames(locale?: string, weekStartsOn = 1): WeekdayName[] {
47
+ const short = formatter(locale, { weekday: "short" });
48
+ const long = formatter(locale, { weekday: "long" });
49
+ // 2024-01-07 was a Sunday, so index 0 lines up with `Date.prototype.getDay`.
50
+ return Array.from({ length: 7 }, (_, offset) => {
51
+ const date = new Date(2024, 0, 7 + ((weekStartsOn + offset) % 7));
52
+ return { short: short.format(date), long: long.format(date) };
53
+ });
54
+ }
55
+
56
+ export type DateField = "day" | "month" | "year";
57
+
58
+ /** The order a locale writes a numeric date in, used for parsing and the placeholder. */
59
+ export function fieldOrder(locale?: string): DateField[] {
60
+ const parts = formatter(locale, {
61
+ year: "numeric",
62
+ month: "2-digit",
63
+ day: "2-digit",
64
+ }).formatToParts(new Date(2024, 10, 22));
65
+ const order = parts
66
+ .map((part) => part.type)
67
+ .filter((type): type is DateField => type === "day" || type === "month" || type === "year");
68
+ return order.length === 3 ? order : ["day", "month", "year"];
69
+ }
70
+
71
+ function separator(locale?: string): string {
72
+ const parts = formatter(locale, {
73
+ year: "numeric",
74
+ month: "2-digit",
75
+ day: "2-digit",
76
+ }).formatToParts(new Date(2024, 10, 22));
77
+ const literal = parts.find((part) => part.type === "literal" && part.value.trim());
78
+ return literal ? literal.value.trim() : "/";
79
+ }
80
+
81
+ /** A hint in the locale's own order, e.g. `dd/mm/yyyy` or `mm/dd/yyyy`. */
82
+ export function inputPlaceholder(locale?: string): string {
83
+ if (!locale) return DEFAULT_INPUT_PLACEHOLDER;
84
+ const hint: Record<DateField, string> = { day: "dd", month: "mm", year: "yyyy" };
85
+ return fieldOrder(locale)
86
+ .map((field) => hint[field])
87
+ .join(separator(locale));
88
+ }
89
+
90
+ /** Two-digit years follow the usual pivot: 00-68 is 2000s, 69-99 is 1900s. */
91
+ function expandYear(year: number): number {
92
+ if (year >= 100) return year;
93
+ return year <= 68 ? 2000 + year : 1900 + year;
94
+ }
95
+
96
+ function build(year: number, month: number, day: number): Date | null {
97
+ if (month < 0 || month > 11 || day < 1 || day > 31) return null;
98
+ const date = new Date(year, month, day);
99
+ date.setHours(0, 0, 0, 0);
100
+ const valid =
101
+ date.getFullYear() === year && date.getMonth() === month && date.getDate() === day;
102
+ return valid ? date : null;
103
+ }
104
+
105
+ /** Matches a written month name, long or short, by either being a prefix of the other. */
106
+ function monthFromWords(words: string, locale?: string): number | null {
107
+ const needle = words.toLocaleLowerCase();
108
+ const names = [...monthNames(locale, "long"), ...monthNames(locale, "short")];
109
+ const index = names.findIndex((name) => {
110
+ const candidate = name.toLocaleLowerCase().replace(/\.$/, "");
111
+ return candidate.startsWith(needle) || needle.startsWith(candidate);
112
+ });
113
+ return index === -1 ? null : index % 12;
114
+ }
115
+
116
+ /**
117
+ * Reads what someone typed into the field. ISO is accepted everywhere; anything
118
+ * else is read in the locale's field order, so `03/04/2026` is 3 April in
119
+ * en-GB and 4 March in en-US. Month names work too (`12 Mar 2026`).
120
+ * Missing parts fall back to `reference`. Returns null when it isn't a date.
121
+ */
122
+ export function parseDate(
123
+ text: string,
124
+ locale?: string,
125
+ reference: Date = new Date(),
126
+ ): Date | null {
127
+ const trimmed = text.trim();
128
+ if (!trimmed) return null;
129
+
130
+ const iso = parseISODate(trimmed);
131
+ if (iso) return iso;
132
+
133
+ let month: number | null = null;
134
+ const words = trimmed.match(/\p{L}+/gu);
135
+ if (words) {
136
+ month = monthFromWords(words.join(" "), locale);
137
+ if (month === null) return null;
138
+ }
139
+
140
+ const numbers = (trimmed.match(/\d+/g) ?? []).map(Number);
141
+ if (numbers.length === 0) return null;
142
+
143
+ const order = fieldOrder(locale);
144
+
145
+ if (month !== null) {
146
+ // A month name was given, so the numbers are the day and maybe the year.
147
+ if (numbers.length === 1) {
148
+ return build(reference.getFullYear(), month, numbers[0]);
149
+ }
150
+ const yearFirst = numbers[0] >= 32;
151
+ const year = expandYear(yearFirst ? numbers[0] : numbers[1]);
152
+ const day = yearFirst ? numbers[1] : numbers[0];
153
+ return build(year, month, day);
154
+ }
155
+
156
+ if (numbers.length >= 3) {
157
+ const fields = numbers[0] >= 1000 ? (["year", "month", "day"] as DateField[]) : order;
158
+ const value = (field: DateField) => numbers[fields.indexOf(field)];
159
+ return build(expandYear(value("year")), value("month") - 1, value("day"));
160
+ }
161
+
162
+ if (numbers.length === 2) {
163
+ const dayFirst = order.indexOf("day") < order.indexOf("month");
164
+ const day = dayFirst ? numbers[0] : numbers[1];
165
+ const monthNumber = dayFirst ? numbers[1] : numbers[0];
166
+ return build(reference.getFullYear(), monthNumber - 1, day);
167
+ }
168
+
169
+ return build(reference.getFullYear(), reference.getMonth(), numbers[0]);
170
+ }
@@ -0,0 +1,40 @@
1
+ // Framework-free date helpers: the same calendar building, parsing and
2
+ // formatting the components use, so a server (or a test) can reproduce them.
3
+ export {
4
+ addDays,
5
+ addMonths,
6
+ buildMonth,
7
+ buildMonths,
8
+ clampDate,
9
+ compareDay,
10
+ daysInMonth,
11
+ endOfMonth,
12
+ getISOWeek,
13
+ isDayDisabled,
14
+ isInRange,
15
+ isSameDay,
16
+ isSameMonth,
17
+ moveByKey,
18
+ moveInGrid,
19
+ normalizeRange,
20
+ parseISODate,
21
+ resolveWeekStart,
22
+ startOfDay,
23
+ startOfMonth,
24
+ toDate,
25
+ toISODate,
26
+ yearPage,
27
+ } from "./calendar";
28
+ export type { BuildMonthOptions, MoveOptions } from "./calendar";
29
+ export {
30
+ DEFAULT_DATE_FORMAT,
31
+ fieldOrder,
32
+ formatDate,
33
+ formatMonthYear,
34
+ inputPlaceholder,
35
+ monthNames,
36
+ parseDate,
37
+ weekdayNames,
38
+ } from "./format";
39
+ export type { DateField, WeekdayName } from "./format";
40
+ export type * from "./types";
@@ -0,0 +1,68 @@
1
+ /** Values accepted wherever a date comes in: a Date, an ISO `yyyy-mm-dd` string or a timestamp. */
2
+ export type DateInput = Date | string | number;
3
+
4
+ export interface DateRange {
5
+ start: Date | null;
6
+ end: Date | null;
7
+ }
8
+
9
+ export type DatePickerSize = "sm" | "md" | "lg";
10
+
11
+ /** Panel shown inside the popover. */
12
+ export type CalendarView = "days" | "months" | "years";
13
+
14
+ export interface CalendarDay {
15
+ date: Date;
16
+ /** Day of the month, 1-31. */
17
+ day: number;
18
+ /** `yyyy-mm-dd` in local time: a stable key, and safe to compare. */
19
+ key: string;
20
+ /** Belongs to a neighbouring month. */
21
+ outside: boolean;
22
+ today: boolean;
23
+ weekend: boolean;
24
+ disabled: boolean;
25
+ }
26
+
27
+ export interface CalendarWeek {
28
+ /** ISO week number, shown when `showWeekNumbers` is set. */
29
+ weekNumber: number;
30
+ days: CalendarDay[];
31
+ }
32
+
33
+ export interface CalendarMonth {
34
+ year: number;
35
+ /** 0-11, as in `Date.prototype.getMonth`. */
36
+ month: number;
37
+ weeks: CalendarWeek[];
38
+ }
39
+
40
+ /** What makes a day unselectable. */
41
+ export interface DateLimits {
42
+ min?: Date | null;
43
+ max?: Date | null;
44
+ isDateDisabled?(date: Date): boolean;
45
+ }
46
+
47
+ export interface DatePickerLocaleText {
48
+ calendarLabel: string;
49
+ openCalendar: string;
50
+ clear: string;
51
+ today: string;
52
+ previousMonth: string;
53
+ nextMonth: string;
54
+ previousYears: string;
55
+ nextYears: string;
56
+ /** Button that switches the day grid to the month and year panels. */
57
+ chooseMonthYear: string;
58
+ monthPanelLabel: string;
59
+ yearPanelLabel: string;
60
+ weekNumber: string;
61
+ startDate: string;
62
+ endDate: string;
63
+ invalidDate: string;
64
+ outOfRange: string;
65
+ presets: string;
66
+ selectedDate(date: string): string;
67
+ selectedRange(start: string, end: string): string;
68
+ }
@@ -0,0 +1,16 @@
1
+ export { DatePicker } from "./react/DatePicker";
2
+ export type { DatePickerProps } from "./react/DatePicker";
3
+ export { DateRangePicker } from "./react/DateRangePicker";
4
+ export type { DateRangePickerProps, DateRangeInput } from "./react/DateRangePicker";
5
+ export type { DatePreset } from "./react/CalendarPanel";
6
+ export { formatWith, useDatePicker } from "./react/useDatePicker";
7
+ export type { DatePickerEngine, RangeField, UseDatePickerParams } from "./react/useDatePicker";
8
+ export { defaultDatePickerText } from "./react/locale";
9
+ export type { DayState } from "./react/Calendar";
10
+ export type {
11
+ DateFormat,
12
+ DatePickerHandle,
13
+ DatePickerSharedProps,
14
+ DatePickerSlot,
15
+ } from "./react/props";
16
+ export * from "./core";