@mirohq/design-system-calendar 2.4.42 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +1268 -63
- package/dist/main.js.map +1 -1
- package/dist/module.js +1272 -67
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +48 -2
- package/package.json +5 -5
package/dist/module.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import React, { createContext, useContext, useState, useMemo,
|
|
3
|
-
import { useDateFormatter, useCalendarCell, useFocusRing, mergeProps,
|
|
2
|
+
import React, { createContext, useContext, useState, useMemo, useRef, useEffect, useCallback, useLayoutEffect } from 'react';
|
|
3
|
+
import { useLocale, useDateFormatter, useCalendarCell, useFocusRing, mergeProps, useCalendarGrid, useCalendar, useRangeCalendar, I18nProvider } from 'react-aria';
|
|
4
4
|
import * as RadixPopover from '@radix-ui/react-popover';
|
|
5
5
|
import { Trigger as Trigger$1, Content as Content$1 } from '@radix-ui/react-popover';
|
|
6
|
-
import { IconCross, IconCalendarBlank, IconChevronLeft, IconChevronRight } from '@mirohq/design-system-icons';
|
|
6
|
+
import { IconCross, IconCalendarBlank, IconChevronLeft, IconChevronRight, IconClock } from '@mirohq/design-system-icons';
|
|
7
7
|
import { useFormFieldContext } from '@mirohq/design-system-base-form';
|
|
8
8
|
import { stringAttrValue, booleanify } from '@mirohq/design-system-utils';
|
|
9
9
|
import { Primitive } from '@mirohq/design-system-primitive';
|
|
@@ -14,7 +14,7 @@ import { BaseButton } from '@mirohq/design-system-base-button';
|
|
|
14
14
|
import { Tooltip } from '@mirohq/design-system-tooltip';
|
|
15
15
|
import { useControllableState } from '@radix-ui/react-use-controllable-state';
|
|
16
16
|
import { useCalendarState, useRangeCalendarState } from 'react-stately';
|
|
17
|
-
import {
|
|
17
|
+
import { getLocalTimeZone, startOfWeek, endOfWeek, today, isSameDay, isSameMonth, isToday, getWeeksInMonth, CalendarDate, createCalendar } from '@internationalized/date';
|
|
18
18
|
import { Flex } from '@mirohq/design-system-flex';
|
|
19
19
|
import { Heading } from '@mirohq/design-system-typography';
|
|
20
20
|
import { IconButton } from '@mirohq/design-system-icon-button';
|
|
@@ -103,6 +103,18 @@ const ClearAction = React.forwardRef(({ "aria-label": ariaLabel, label, ...restP
|
|
|
103
103
|
/* @__PURE__ */ jsx(Tooltip.Content, { children: label })
|
|
104
104
|
] }));
|
|
105
105
|
|
|
106
|
+
const PERIOD_TO_VIEW_MODE = {
|
|
107
|
+
quarter: "quarters",
|
|
108
|
+
month: "months"
|
|
109
|
+
};
|
|
110
|
+
const getNavigationYear = (value) => {
|
|
111
|
+
var _a;
|
|
112
|
+
if (value == null) {
|
|
113
|
+
return (/* @__PURE__ */ new Date()).getFullYear();
|
|
114
|
+
}
|
|
115
|
+
const date = "start" in value ? value.start : value;
|
|
116
|
+
return (_a = date == null ? void 0 : date.year) != null ? _a : (/* @__PURE__ */ new Date()).getFullYear();
|
|
117
|
+
};
|
|
106
118
|
const CalendarContext = createContext({});
|
|
107
119
|
const CalendarProvider = ({
|
|
108
120
|
children,
|
|
@@ -116,7 +128,7 @@ const CalendarProvider = ({
|
|
|
116
128
|
onClose,
|
|
117
129
|
...restProps
|
|
118
130
|
}) => {
|
|
119
|
-
var _a;
|
|
131
|
+
var _a, _b;
|
|
120
132
|
const [immediateSave, setImmediateSave] = useState(false);
|
|
121
133
|
const [internalCalendarValue, setInternalCalendarValue] = useState(valueProp != null ? valueProp : defaultValueProp);
|
|
122
134
|
const visibleDuration = useMemo(
|
|
@@ -130,6 +142,13 @@ const CalendarProvider = ({
|
|
|
130
142
|
defaultProp: defaultValueProp,
|
|
131
143
|
onChange: onValueChange
|
|
132
144
|
});
|
|
145
|
+
const defaultViewMode = (_a = restProps.period != null ? PERIOD_TO_VIEW_MODE[restProps.period] : void 0) != null ? _a : "days";
|
|
146
|
+
const [viewMode, setViewMode] = useState(defaultViewMode);
|
|
147
|
+
const [navigationYear, setNavigationYear] = useState(
|
|
148
|
+
() => getNavigationYear(value)
|
|
149
|
+
);
|
|
150
|
+
const valueRef = useRef(value);
|
|
151
|
+
valueRef.current = value;
|
|
133
152
|
const [open = false, setOpen] = useControllableState({
|
|
134
153
|
prop: openProp,
|
|
135
154
|
defaultProp: defaultOpenProp,
|
|
@@ -137,10 +156,20 @@ const CalendarProvider = ({
|
|
|
137
156
|
if (state) {
|
|
138
157
|
onOpen == null ? void 0 : onOpen();
|
|
139
158
|
} else {
|
|
159
|
+
setViewMode(defaultViewMode);
|
|
140
160
|
onClose == null ? void 0 : onClose();
|
|
141
161
|
}
|
|
142
162
|
}
|
|
143
163
|
});
|
|
164
|
+
const valueYear = getNavigationYear(value);
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
setNavigationYear(valueYear);
|
|
167
|
+
}, [valueYear]);
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
if (open) {
|
|
170
|
+
setNavigationYear(getNavigationYear(valueRef.current));
|
|
171
|
+
}
|
|
172
|
+
}, [open]);
|
|
144
173
|
const onCalendarDateClick = useCallback(
|
|
145
174
|
(value2) => {
|
|
146
175
|
if (immediateSave) {
|
|
@@ -156,11 +185,15 @@ const CalendarProvider = ({
|
|
|
156
185
|
setValue(internalCalendarValue);
|
|
157
186
|
setOpen(false);
|
|
158
187
|
};
|
|
188
|
+
const periodImpliesNavigation = restProps.period === "quarter" || restProps.period === "month" || restProps.period === "week";
|
|
189
|
+
const effectivePeriodNavigation = restProps.navigation === true && visibleMonths <= 1 || periodImpliesNavigation;
|
|
190
|
+
const effectiveViewMode = effectivePeriodNavigation ? viewMode : "days";
|
|
159
191
|
return /* @__PURE__ */ jsx(
|
|
160
192
|
CalendarContext.Provider,
|
|
161
193
|
{
|
|
162
194
|
value: {
|
|
163
195
|
...restProps,
|
|
196
|
+
navigation: effectivePeriodNavigation,
|
|
164
197
|
visibleDuration,
|
|
165
198
|
value,
|
|
166
199
|
setValue,
|
|
@@ -169,8 +202,11 @@ const CalendarProvider = ({
|
|
|
169
202
|
onCalendarDateClick,
|
|
170
203
|
setImmediateSave,
|
|
171
204
|
onSaveClick,
|
|
172
|
-
|
|
173
|
-
|
|
205
|
+
calendarValue: (_b = immediateSave ? internalCalendarValue : value) != null ? _b : null,
|
|
206
|
+
viewMode: effectiveViewMode,
|
|
207
|
+
setViewMode,
|
|
208
|
+
navigationYear,
|
|
209
|
+
setNavigationYear
|
|
174
210
|
},
|
|
175
211
|
children
|
|
176
212
|
}
|
|
@@ -233,6 +269,298 @@ const Trigger = React.forwardRef(
|
|
|
233
269
|
}
|
|
234
270
|
);
|
|
235
271
|
|
|
272
|
+
const DECADE_SIZE = 12;
|
|
273
|
+
|
|
274
|
+
const StyledValue = styled(Primitive.span, {});
|
|
275
|
+
|
|
276
|
+
const en = {
|
|
277
|
+
quarter: (q) => "Q".concat(q),
|
|
278
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
279
|
+
views: { months: "months", quarters: "quarters", years: "years" },
|
|
280
|
+
switchView: (title, nextView) => "".concat(title, ", switch to ").concat(nextView, " view"),
|
|
281
|
+
previousYear: "Previous year",
|
|
282
|
+
nextYear: "Next year",
|
|
283
|
+
previousYears: (n) => "Previous ".concat(n, " years"),
|
|
284
|
+
nextYears: (n) => "Next ".concat(n, " years")
|
|
285
|
+
};
|
|
286
|
+
const STRINGS = {
|
|
287
|
+
en,
|
|
288
|
+
de: {
|
|
289
|
+
quarter: (q) => "Q".concat(q),
|
|
290
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
291
|
+
views: { months: "Monate", quarters: "Quartale", years: "Jahre" },
|
|
292
|
+
switchView: (title, nextView) => "".concat(title, ", zur Ansicht ").concat(nextView, " wechseln"),
|
|
293
|
+
previousYear: "Vorheriges Jahr",
|
|
294
|
+
nextYear: "N\xE4chstes Jahr",
|
|
295
|
+
previousYears: (n) => "Vorherige ".concat(n, " Jahre"),
|
|
296
|
+
nextYears: (n) => "N\xE4chste ".concat(n, " Jahre")
|
|
297
|
+
},
|
|
298
|
+
es: {
|
|
299
|
+
quarter: (q) => "T".concat(q),
|
|
300
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
301
|
+
views: { months: "meses", quarters: "trimestres", years: "a\xF1os" },
|
|
302
|
+
switchView: (title, nextView) => "".concat(title, ", cambiar a la vista de ").concat(nextView),
|
|
303
|
+
previousYear: "A\xF1o anterior",
|
|
304
|
+
nextYear: "A\xF1o siguiente",
|
|
305
|
+
previousYears: (n) => "".concat(n, " a\xF1os anteriores"),
|
|
306
|
+
nextYears: (n) => "".concat(n, " a\xF1os siguientes")
|
|
307
|
+
},
|
|
308
|
+
fr: {
|
|
309
|
+
quarter: (q) => "T".concat(q),
|
|
310
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
311
|
+
views: { months: "mois", quarters: "trimestres", years: "ann\xE9es" },
|
|
312
|
+
switchView: (title, nextView) => "".concat(title, ", passer \xE0 la vue ").concat(nextView),
|
|
313
|
+
previousYear: "Ann\xE9e pr\xE9c\xE9dente",
|
|
314
|
+
nextYear: "Ann\xE9e suivante",
|
|
315
|
+
previousYears: (n) => "".concat(n, " ann\xE9es pr\xE9c\xE9dentes"),
|
|
316
|
+
nextYears: (n) => "".concat(n, " ann\xE9es suivantes")
|
|
317
|
+
},
|
|
318
|
+
it: {
|
|
319
|
+
quarter: (q) => "T".concat(q),
|
|
320
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
321
|
+
views: { months: "mesi", quarters: "trimestri", years: "anni" },
|
|
322
|
+
switchView: (title, nextView) => "".concat(title, ", passa alla vista ").concat(nextView),
|
|
323
|
+
previousYear: "Anno precedente",
|
|
324
|
+
nextYear: "Anno successivo",
|
|
325
|
+
previousYears: (n) => "".concat(n, " anni precedenti"),
|
|
326
|
+
nextYears: (n) => "".concat(n, " anni successivi")
|
|
327
|
+
},
|
|
328
|
+
ja: {
|
|
329
|
+
quarter: (q) => "\u7B2C".concat(q, "\u56DB\u534A\u671F"),
|
|
330
|
+
quarterWithYear: (label, year) => "".concat(year, "\u5E74").concat(label),
|
|
331
|
+
views: { months: "\u6708", quarters: "\u56DB\u534A\u671F", years: "\u5E74" },
|
|
332
|
+
switchView: (title, nextView) => "".concat(title, "\u3001").concat(nextView, "\u8868\u793A\u306B\u5207\u308A\u66FF\u3048"),
|
|
333
|
+
previousYear: "\u524D\u5E74",
|
|
334
|
+
nextYear: "\u7FCC\u5E74",
|
|
335
|
+
previousYears: (n) => "\u524D\u306E".concat(n, "\u5E74"),
|
|
336
|
+
nextYears: (n) => "\u6B21\u306E".concat(n, "\u5E74")
|
|
337
|
+
},
|
|
338
|
+
ko: {
|
|
339
|
+
quarter: (q) => "".concat(q, "\uBD84\uAE30"),
|
|
340
|
+
quarterWithYear: (label, year) => "".concat(year, "\uB144 ").concat(label),
|
|
341
|
+
views: { months: "\uC6D4", quarters: "\uBD84\uAE30", years: "\uB144" },
|
|
342
|
+
switchView: (title, nextView) => "".concat(title, ", ").concat(nextView, " \uBCF4\uAE30\uB85C \uC804\uD658"),
|
|
343
|
+
previousYear: "\uC774\uC804 \uD574",
|
|
344
|
+
nextYear: "\uB2E4\uC74C \uD574",
|
|
345
|
+
previousYears: (n) => "\uC774\uC804 ".concat(n, "\uB144"),
|
|
346
|
+
nextYears: (n) => "\uB2E4\uC74C ".concat(n, "\uB144")
|
|
347
|
+
},
|
|
348
|
+
nl: {
|
|
349
|
+
quarter: (q) => "K".concat(q),
|
|
350
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
351
|
+
views: { months: "maanden", quarters: "kwartalen", years: "jaren" },
|
|
352
|
+
switchView: (title, nextView) => "".concat(title, ", ga naar weergave ").concat(nextView),
|
|
353
|
+
previousYear: "Vorig jaar",
|
|
354
|
+
nextYear: "Volgend jaar",
|
|
355
|
+
previousYears: (n) => "Vorige ".concat(n, " jaar"),
|
|
356
|
+
nextYears: (n) => "Volgende ".concat(n, " jaar")
|
|
357
|
+
},
|
|
358
|
+
pl: {
|
|
359
|
+
quarter: (q) => "K".concat(q),
|
|
360
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
361
|
+
views: { months: "miesi\u0105ce", quarters: "kwarta\u0142y", years: "lata" },
|
|
362
|
+
switchView: (title, nextView) => "".concat(title, ", prze\u0142\u0105cz na widok: ").concat(nextView),
|
|
363
|
+
previousYear: "Poprzedni rok",
|
|
364
|
+
nextYear: "Nast\u0119pny rok",
|
|
365
|
+
previousYears: (n) => "Poprzednie ".concat(n, " lat"),
|
|
366
|
+
nextYears: (n) => "Nast\u0119pne ".concat(n, " lat")
|
|
367
|
+
},
|
|
368
|
+
pt: {
|
|
369
|
+
quarter: (q) => "T".concat(q),
|
|
370
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
371
|
+
views: { months: "meses", quarters: "trimestres", years: "anos" },
|
|
372
|
+
switchView: (title, nextView) => "".concat(title, ", mudar para a visualiza\xE7\xE3o de ").concat(nextView),
|
|
373
|
+
previousYear: "Ano anterior",
|
|
374
|
+
nextYear: "Pr\xF3ximo ano",
|
|
375
|
+
previousYears: (n) => "".concat(n, " anos anteriores"),
|
|
376
|
+
nextYears: (n) => "Pr\xF3ximos ".concat(n, " anos")
|
|
377
|
+
},
|
|
378
|
+
ru: {
|
|
379
|
+
quarter: (q) => "".concat(q, " \u043A\u0432."),
|
|
380
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
381
|
+
views: { months: "\u043C\u0435\u0441\u044F\u0446\u044B", quarters: "\u043A\u0432\u0430\u0440\u0442\u0430\u043B\u044B", years: "\u0433\u043E\u0434\u044B" },
|
|
382
|
+
switchView: (title, nextView) => "".concat(title, ", \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043D\u0430 ").concat(nextView),
|
|
383
|
+
previousYear: "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0439 \u0433\u043E\u0434",
|
|
384
|
+
nextYear: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0433\u043E\u0434",
|
|
385
|
+
previousYears: (n) => "\u041F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0438\u0435 ".concat(n, " \u043B\u0435\u0442"),
|
|
386
|
+
nextYears: (n) => "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0435 ".concat(n, " \u043B\u0435\u0442")
|
|
387
|
+
},
|
|
388
|
+
tr: {
|
|
389
|
+
quarter: (q) => "\xC7".concat(q),
|
|
390
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
391
|
+
views: { months: "aylar", quarters: "\xE7eyrekler", years: "y\u0131llar" },
|
|
392
|
+
switchView: (title, nextView) => "".concat(title, ", ").concat(nextView, " g\xF6r\xFCn\xFCm\xFCne ge\xE7"),
|
|
393
|
+
previousYear: "\xD6nceki y\u0131l",
|
|
394
|
+
nextYear: "Sonraki y\u0131l",
|
|
395
|
+
previousYears: (n) => "\xD6nceki ".concat(n, " y\u0131l"),
|
|
396
|
+
nextYears: (n) => "Sonraki ".concat(n, " y\u0131l")
|
|
397
|
+
},
|
|
398
|
+
uk: {
|
|
399
|
+
quarter: (q) => "".concat(q, " \u043A\u0432."),
|
|
400
|
+
quarterWithYear: (label, year) => "".concat(label, " ").concat(year),
|
|
401
|
+
views: { months: "\u043C\u0456\u0441\u044F\u0446\u0456", quarters: "\u043A\u0432\u0430\u0440\u0442\u0430\u043B\u0438", years: "\u0440\u043E\u043A\u0438" },
|
|
402
|
+
switchView: (title, nextView) => "".concat(title, ", \u043F\u0435\u0440\u0435\u0439\u0442\u0438 \u0434\u043E ").concat(nextView),
|
|
403
|
+
previousYear: "\u041F\u043E\u043F\u0435\u0440\u0435\u0434\u043D\u0456\u0439 \u0440\u0456\u043A",
|
|
404
|
+
nextYear: "\u041D\u0430\u0441\u0442\u0443\u043F\u043D\u0438\u0439 \u0440\u0456\u043A",
|
|
405
|
+
previousYears: (n) => "\u041F\u043E\u043F\u0435\u0440\u0435\u0434\u043D\u0456 ".concat(n, " \u0440\u043E\u043A\u0456\u0432"),
|
|
406
|
+
nextYears: (n) => "\u041D\u0430\u0441\u0442\u0443\u043F\u043D\u0456 ".concat(n, " \u0440\u043E\u043A\u0456\u0432")
|
|
407
|
+
},
|
|
408
|
+
zh: {
|
|
409
|
+
quarter: (q) => "\u7B2C".concat(q, "\u5B63\u5EA6"),
|
|
410
|
+
quarterWithYear: (label, year) => "".concat(year, "\u5E74").concat(label),
|
|
411
|
+
views: { months: "\u6708\u4EFD", quarters: "\u5B63\u5EA6", years: "\u5E74\u4EFD" },
|
|
412
|
+
switchView: (title, nextView) => "".concat(title, "\uFF0C\u5207\u6362\u5230").concat(nextView, "\u89C6\u56FE"),
|
|
413
|
+
previousYear: "\u4E0A\u4E00\u5E74",
|
|
414
|
+
nextYear: "\u4E0B\u4E00\u5E74",
|
|
415
|
+
previousYears: (n) => "\u524D".concat(n, "\u5E74"),
|
|
416
|
+
nextYears: (n) => "\u540E".concat(n, "\u5E74")
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
const getCalendarStrings = (locale) => {
|
|
420
|
+
var _a;
|
|
421
|
+
return (_a = STRINGS[locale.split(/[-_]/)[0].toLowerCase()]) != null ? _a : en;
|
|
422
|
+
};
|
|
423
|
+
const useCalendarStrings = () => {
|
|
424
|
+
const { locale } = useLocale();
|
|
425
|
+
return useMemo(() => getCalendarStrings(locale), [locale]);
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
const DEFAULT_FORMAT = {
|
|
429
|
+
day: "2-digit",
|
|
430
|
+
month: "short",
|
|
431
|
+
year: "numeric"
|
|
432
|
+
};
|
|
433
|
+
const PERIOD_DAY_FORMAT = {
|
|
434
|
+
day: "numeric",
|
|
435
|
+
month: "short"
|
|
436
|
+
};
|
|
437
|
+
const PERIOD_WEEK_FORMAT = PERIOD_DAY_FORMAT;
|
|
438
|
+
const PERIOD_MONTH_FORMAT = {
|
|
439
|
+
month: "short",
|
|
440
|
+
year: "2-digit"
|
|
441
|
+
};
|
|
442
|
+
function formatDate(date, formatter, timeZone) {
|
|
443
|
+
return formatter.format(date.toDate(timeZone));
|
|
444
|
+
}
|
|
445
|
+
function formatValue(value, formatter, timeZone) {
|
|
446
|
+
if (value == null || value === void 0) {
|
|
447
|
+
return void 0;
|
|
448
|
+
}
|
|
449
|
+
if ("start" in value) {
|
|
450
|
+
if (value.start == null || value.end == null) {
|
|
451
|
+
return void 0;
|
|
452
|
+
}
|
|
453
|
+
return "".concat(formatDate(value.start, formatter, timeZone), " - ").concat(formatDate(
|
|
454
|
+
value.end,
|
|
455
|
+
formatter,
|
|
456
|
+
timeZone
|
|
457
|
+
));
|
|
458
|
+
}
|
|
459
|
+
return formatDate(value, formatter, timeZone);
|
|
460
|
+
}
|
|
461
|
+
function isCurrentPeriod(value, period, locale) {
|
|
462
|
+
const todayDate = today(getLocalTimeZone());
|
|
463
|
+
switch (period) {
|
|
464
|
+
case "day":
|
|
465
|
+
case void 0:
|
|
466
|
+
return isSameDay(value, todayDate);
|
|
467
|
+
case "week": {
|
|
468
|
+
const currentWeekStart = startOfWeek(todayDate, locale);
|
|
469
|
+
const selectedWeekStart = startOfWeek(value, locale);
|
|
470
|
+
return isSameDay(currentWeekStart, selectedWeekStart);
|
|
471
|
+
}
|
|
472
|
+
case "month":
|
|
473
|
+
return value.year === todayDate.year && value.month === todayDate.month;
|
|
474
|
+
case "quarter": {
|
|
475
|
+
const currentQuarter = Math.ceil(todayDate.month / 3);
|
|
476
|
+
const selectedQuarter = Math.ceil(value.month / 3);
|
|
477
|
+
return value.year === todayDate.year && selectedQuarter === currentQuarter;
|
|
478
|
+
}
|
|
479
|
+
default:
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const Value = React.forwardRef(({ format, currentLabel, ...restProps }, forwardRef) => {
|
|
484
|
+
const { value, period, formatQuarterLabel } = useCalendarContext();
|
|
485
|
+
const { locale } = useLocale();
|
|
486
|
+
const strings = useCalendarStrings();
|
|
487
|
+
const yearFormatter = useMemo(
|
|
488
|
+
() => new Intl.NumberFormat(locale, { useGrouping: false }),
|
|
489
|
+
[locale]
|
|
490
|
+
);
|
|
491
|
+
const timeZone = useMemo(getLocalTimeZone, []);
|
|
492
|
+
const effectivePeriod = period != null ? period : "day";
|
|
493
|
+
const periodFormat = useMemo(() => {
|
|
494
|
+
if (format != null) return format;
|
|
495
|
+
if (period == null) return DEFAULT_FORMAT;
|
|
496
|
+
switch (period) {
|
|
497
|
+
case "day":
|
|
498
|
+
return PERIOD_DAY_FORMAT;
|
|
499
|
+
case "week":
|
|
500
|
+
return PERIOD_WEEK_FORMAT;
|
|
501
|
+
case "month":
|
|
502
|
+
return PERIOD_MONTH_FORMAT;
|
|
503
|
+
default:
|
|
504
|
+
return DEFAULT_FORMAT;
|
|
505
|
+
}
|
|
506
|
+
}, [format, period]);
|
|
507
|
+
const formatWithTimeZone = useMemo(
|
|
508
|
+
() => ({ ...periodFormat, timeZone }),
|
|
509
|
+
[periodFormat, timeZone]
|
|
510
|
+
);
|
|
511
|
+
const dateFormatter = useDateFormatter(formatWithTimeZone);
|
|
512
|
+
const dayOnlyFormat = useMemo(
|
|
513
|
+
() => ({ day: "numeric", timeZone }),
|
|
514
|
+
[timeZone]
|
|
515
|
+
);
|
|
516
|
+
const dayOnlyFormatter = useDateFormatter(dayOnlyFormat);
|
|
517
|
+
let valueToDisplay;
|
|
518
|
+
if (value == null) {
|
|
519
|
+
valueToDisplay = void 0;
|
|
520
|
+
} else if ("start" in value) {
|
|
521
|
+
valueToDisplay = formatValue(value, dateFormatter, timeZone);
|
|
522
|
+
} else {
|
|
523
|
+
switch (effectivePeriod) {
|
|
524
|
+
case "week": {
|
|
525
|
+
const calendarDate = value;
|
|
526
|
+
const weekStart = startOfWeek(calendarDate, locale);
|
|
527
|
+
const weekEnd = endOfWeek(calendarDate, locale);
|
|
528
|
+
const startStr = formatDate(weekStart, dateFormatter, timeZone);
|
|
529
|
+
if (weekStart.month === weekEnd.month) {
|
|
530
|
+
const endDay = dayOnlyFormatter.format(weekEnd.toDate(timeZone));
|
|
531
|
+
valueToDisplay = "".concat(startStr, " - ").concat(endDay);
|
|
532
|
+
} else {
|
|
533
|
+
const endStr = formatDate(weekEnd, dateFormatter, timeZone);
|
|
534
|
+
if (startStr != null && endStr != null) {
|
|
535
|
+
valueToDisplay = "".concat(startStr, " - ").concat(endStr);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
case "quarter": {
|
|
541
|
+
const quarter = Math.ceil(value.month / 3);
|
|
542
|
+
valueToDisplay = strings.quarterWithYear(
|
|
543
|
+
(formatQuarterLabel != null ? formatQuarterLabel : strings.quarter)(quarter),
|
|
544
|
+
yearFormatter.format(value.year)
|
|
545
|
+
);
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
case "month": {
|
|
549
|
+
valueToDisplay = formatDate(value, dateFormatter, timeZone);
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
default: {
|
|
553
|
+
valueToDisplay = formatDate(value, dateFormatter, timeZone);
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (valueToDisplay != null && currentLabel != null && !("start" in value) && isCurrentPeriod(value, period, locale)) {
|
|
558
|
+
valueToDisplay = "".concat(valueToDisplay, " (").concat(currentLabel, ")");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return /* @__PURE__ */ jsx(StyledValue, { ref: forwardRef, ...restProps, children: valueToDisplay });
|
|
562
|
+
});
|
|
563
|
+
|
|
236
564
|
const StyledContent = styled(Content$1, {
|
|
237
565
|
...floating.css(),
|
|
238
566
|
zIndex: "$calendar",
|
|
@@ -254,12 +582,54 @@ const StyledClearContent = styled(Primitive.div, {
|
|
|
254
582
|
padding: "$200",
|
|
255
583
|
borderTop: "1px solid $border-default"
|
|
256
584
|
});
|
|
585
|
+
const StyledGoToCurrent = styled(BaseButton, {
|
|
586
|
+
display: "flex",
|
|
587
|
+
alignItems: "center",
|
|
588
|
+
gap: "$100",
|
|
589
|
+
marginTop: "$150",
|
|
590
|
+
padding: "$100 $150",
|
|
591
|
+
borderRadius: "$calendar",
|
|
592
|
+
color: "$text-secondary",
|
|
593
|
+
fontSize: "$body-xs",
|
|
594
|
+
lineHeight: "$body-xs",
|
|
595
|
+
fontWeight: "$body-xs",
|
|
596
|
+
"& svg": {
|
|
597
|
+
width: "16px",
|
|
598
|
+
height: "16px",
|
|
599
|
+
color: "$icon-secondary"
|
|
600
|
+
},
|
|
601
|
+
_hover: {
|
|
602
|
+
backgroundColor: "$background-interactive-surface-secondary-hover"
|
|
603
|
+
},
|
|
604
|
+
...focus.css({
|
|
605
|
+
boxShadow: "$focus",
|
|
606
|
+
outline: "1px solid transparent"
|
|
607
|
+
})
|
|
608
|
+
});
|
|
257
609
|
|
|
258
610
|
const StyledHeaderTitle = styled(Heading, {
|
|
259
611
|
margin: 0,
|
|
260
612
|
flex: "2",
|
|
261
613
|
textAlign: "center"
|
|
262
614
|
});
|
|
615
|
+
const StyledHeaderTitleButton = styled(BaseButton, {
|
|
616
|
+
display: "inline-block",
|
|
617
|
+
textAlign: "center",
|
|
618
|
+
fontSize: "$250",
|
|
619
|
+
fontWeight: "$bold",
|
|
620
|
+
lineHeight: "$250",
|
|
621
|
+
color: "$text-interactive-primary",
|
|
622
|
+
borderRadius: "$button",
|
|
623
|
+
padding: "$50 $100",
|
|
624
|
+
...focus.css({
|
|
625
|
+
boxShadow: "$focus",
|
|
626
|
+
outline: "1px solid transparent"
|
|
627
|
+
}),
|
|
628
|
+
_hover: {
|
|
629
|
+
color: "$text-interactive-primary-hover",
|
|
630
|
+
backgroundColor: "$background-interactive-surface-secondary-hover"
|
|
631
|
+
}
|
|
632
|
+
});
|
|
263
633
|
const StyledHeaderPlaceholder = styled("div", {
|
|
264
634
|
height: "$8",
|
|
265
635
|
// to compensate icon buttons' width and space between 2 calendars
|
|
@@ -287,25 +657,88 @@ const Header = ({
|
|
|
287
657
|
state,
|
|
288
658
|
prevButtonProps,
|
|
289
659
|
nextButtonProps,
|
|
290
|
-
visibleMonths
|
|
660
|
+
visibleMonths,
|
|
661
|
+
yearFormatter,
|
|
662
|
+
onPrevOverride,
|
|
663
|
+
onNextOverride,
|
|
664
|
+
onZoomOut,
|
|
665
|
+
isPrevDisabled,
|
|
666
|
+
isNextDisabled
|
|
291
667
|
}) => {
|
|
668
|
+
const { navigation, viewMode, navigationYear, formatSwitchViewLabel } = useCalendarContext();
|
|
669
|
+
const strings = useCalendarStrings();
|
|
292
670
|
const monthDateFormatter = useDateFormatter({
|
|
293
671
|
month: "long",
|
|
294
672
|
year: "numeric",
|
|
295
673
|
timeZone: state.timeZone
|
|
296
674
|
});
|
|
675
|
+
const getTitleText = () => {
|
|
676
|
+
switch (viewMode) {
|
|
677
|
+
case "months":
|
|
678
|
+
case "quarters":
|
|
679
|
+
return yearFormatter.format(navigationYear);
|
|
680
|
+
case "years": {
|
|
681
|
+
const decadeStart = Math.floor(navigationYear / DECADE_SIZE) * DECADE_SIZE;
|
|
682
|
+
return "".concat(yearFormatter.format(decadeStart), "\u2013").concat(yearFormatter.format(
|
|
683
|
+
decadeStart + DECADE_SIZE - 1
|
|
684
|
+
));
|
|
685
|
+
}
|
|
686
|
+
default:
|
|
687
|
+
return monthDateFormatter.format(
|
|
688
|
+
state.visibleRange.start.toDate(state.timeZone)
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
const getNextViewLabel = () => {
|
|
693
|
+
if (viewMode === "days") return strings.views.months;
|
|
694
|
+
if (viewMode === "months" || viewMode === "quarters") {
|
|
695
|
+
return strings.views.years;
|
|
696
|
+
}
|
|
697
|
+
return "";
|
|
698
|
+
};
|
|
699
|
+
const isTopLevel = viewMode === "years";
|
|
700
|
+
const showClickableTitle = navigation === true && !isTopLevel;
|
|
701
|
+
const isDayView = viewMode === "days";
|
|
702
|
+
const stepLabels = viewMode === "years" ? {
|
|
703
|
+
prev: strings.previousYears(DECADE_SIZE),
|
|
704
|
+
next: strings.nextYears(DECADE_SIZE)
|
|
705
|
+
} : { prev: strings.previousYear, next: strings.nextYear };
|
|
297
706
|
return /* @__PURE__ */ jsxs(StyledHeader, { children: [
|
|
298
|
-
/* @__PURE__ */ jsx(HeaderButton, { ...prevButtonProps, children: /* @__PURE__ */ jsx(IconChevronLeft, {}) })
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
707
|
+
isDayView ? /* @__PURE__ */ jsx(HeaderButton, { ...prevButtonProps, children: /* @__PURE__ */ jsx(IconChevronLeft, {}) }) : /* @__PURE__ */ jsx(
|
|
708
|
+
HeaderButton,
|
|
709
|
+
{
|
|
710
|
+
"aria-label": stepLabels.prev,
|
|
711
|
+
isDisabled: isPrevDisabled,
|
|
712
|
+
onClick: onPrevOverride,
|
|
713
|
+
children: /* @__PURE__ */ jsx(IconChevronLeft, {})
|
|
714
|
+
}
|
|
715
|
+
),
|
|
716
|
+
/* @__PURE__ */ jsx(StyledHeaderTitle, { level: 2, styledAs: "h3", children: showClickableTitle ? /* @__PURE__ */ jsx(
|
|
717
|
+
StyledHeaderTitleButton,
|
|
718
|
+
{
|
|
719
|
+
onClick: onZoomOut,
|
|
720
|
+
"aria-label": (formatSwitchViewLabel != null ? formatSwitchViewLabel : strings.switchView)(
|
|
721
|
+
getTitleText(),
|
|
722
|
+
getNextViewLabel()
|
|
723
|
+
),
|
|
724
|
+
children: getTitleText()
|
|
725
|
+
}
|
|
726
|
+
) : getTitleText() }),
|
|
727
|
+
visibleMonths === 2 && isDayView && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
303
728
|
/* @__PURE__ */ jsx(StyledHeaderPlaceholder, {}),
|
|
304
729
|
/* @__PURE__ */ jsx(StyledHeaderTitle, { level: 2, styledAs: "h3", children: monthDateFormatter.format(
|
|
305
730
|
state.visibleRange.start.add({ months: 1 }).toDate(state.timeZone)
|
|
306
731
|
) })
|
|
307
732
|
] }),
|
|
308
|
-
/* @__PURE__ */ jsx(HeaderButton, { ...nextButtonProps, children: /* @__PURE__ */ jsx(IconChevronRight, {}) })
|
|
733
|
+
isDayView ? /* @__PURE__ */ jsx(HeaderButton, { ...nextButtonProps, children: /* @__PURE__ */ jsx(IconChevronRight, {}) }) : /* @__PURE__ */ jsx(
|
|
734
|
+
HeaderButton,
|
|
735
|
+
{
|
|
736
|
+
"aria-label": stepLabels.next,
|
|
737
|
+
isDisabled: isNextDisabled,
|
|
738
|
+
onClick: onNextOverride,
|
|
739
|
+
children: /* @__PURE__ */ jsx(IconChevronRight, {})
|
|
740
|
+
}
|
|
741
|
+
)
|
|
309
742
|
] });
|
|
310
743
|
};
|
|
311
744
|
|
|
@@ -385,13 +818,30 @@ const StyledCellContent = styled(Primitive.div, {
|
|
|
385
818
|
true: {
|
|
386
819
|
display: "none"
|
|
387
820
|
}
|
|
821
|
+
},
|
|
822
|
+
outsideMonth: {
|
|
823
|
+
true: {
|
|
824
|
+
color: "$text-secondary",
|
|
825
|
+
'&[aria-disabled="true"]': {
|
|
826
|
+
textDecoration: "none"
|
|
827
|
+
}
|
|
828
|
+
}
|
|
388
829
|
}
|
|
830
|
+
},
|
|
831
|
+
// Week period: the row is the week, so hovering or focusing anywhere in it
|
|
832
|
+
// highlights the whole week. Cells in the selected week carry no
|
|
833
|
+
// data-week-hover, so the selected styling is not overridden.
|
|
834
|
+
"tr:hover > td > &[data-week-hover], tr:focus-within > td > &[data-week-hover]": {
|
|
835
|
+
backgroundColor: "$background-interactive-surface-secondary-hover",
|
|
836
|
+
borderRadius: 0
|
|
389
837
|
}
|
|
390
838
|
});
|
|
391
839
|
|
|
392
840
|
const Cell = (props) => {
|
|
393
841
|
let { state, date, currentMonth } = props;
|
|
394
842
|
const ref = useRef(null);
|
|
843
|
+
const { period, minDate, maxDate, onCalendarDateClick } = useCalendarContext();
|
|
844
|
+
const { locale } = useLocale();
|
|
395
845
|
const { cellProps, buttonProps, isSelected, formattedDate } = useCalendarCell(
|
|
396
846
|
{ date },
|
|
397
847
|
state,
|
|
@@ -404,15 +854,65 @@ const Cell = (props) => {
|
|
|
404
854
|
const { focusProps } = useFocusRing();
|
|
405
855
|
const isRange = isSelected && (!isSelectionStart || !isSelectionEnd);
|
|
406
856
|
const rangeVariant = isRange ? isSelectionStart ? "start" : isSelectionEnd ? "end" : "middle" : void 0;
|
|
407
|
-
|
|
857
|
+
let effectiveSelected = isSelected && (isSelectionStart || isSelectionEnd);
|
|
858
|
+
let effectiveRange = rangeVariant;
|
|
859
|
+
const isWeekPeriod = period === "week";
|
|
860
|
+
if (isWeekPeriod) {
|
|
861
|
+
const selectedValue = props.state.value;
|
|
862
|
+
if (selectedValue != null) {
|
|
863
|
+
const weekStart2 = startOfWeek(selectedValue, locale);
|
|
864
|
+
const weekEnd2 = endOfWeek(selectedValue, locale);
|
|
865
|
+
const inWeek = date.compare(weekStart2) >= 0 && date.compare(weekEnd2) <= 0;
|
|
866
|
+
if (inWeek) {
|
|
867
|
+
const isStart = isSameDay(date, weekStart2);
|
|
868
|
+
const isEnd = isSameDay(date, weekEnd2);
|
|
869
|
+
effectiveSelected = isStart || isEnd;
|
|
870
|
+
effectiveRange = isStart ? "start" : isEnd ? "end" : "middle";
|
|
871
|
+
} else {
|
|
872
|
+
effectiveSelected = false;
|
|
873
|
+
effectiveRange = void 0;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
const weekStart = isWeekPeriod ? startOfWeek(date, locale) : null;
|
|
878
|
+
const weekEnd = isWeekPeriod ? endOfWeek(date, locale) : null;
|
|
879
|
+
const isWeekOutOfBounds = minDate != null && weekStart != null && weekStart.compare(minDate) < 0 || maxDate != null && weekEnd != null && weekEnd.compare(maxDate) > 0;
|
|
880
|
+
const enableOutsideDay = isWeekPeriod && isOutsideMonth && !isWeekOutOfBounds;
|
|
881
|
+
const selectWeek = () => {
|
|
882
|
+
if (weekStart != null) {
|
|
883
|
+
onCalendarDateClick(weekStart);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
let weekDayProps = {};
|
|
887
|
+
if (isWeekPeriod && isWeekOutOfBounds) {
|
|
888
|
+
weekDayProps = { "aria-disabled": true, tabIndex: -1 };
|
|
889
|
+
} else if (enableOutsideDay) {
|
|
890
|
+
weekDayProps = {
|
|
891
|
+
"aria-disabled": void 0,
|
|
892
|
+
tabIndex: effectiveRange === "start" ? 0 : -1,
|
|
893
|
+
onClick: selectWeek,
|
|
894
|
+
onKeyDown: (event) => {
|
|
895
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
896
|
+
event.preventDefault();
|
|
897
|
+
event.stopPropagation();
|
|
898
|
+
selectWeek();
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
const gridCellProps = isWeekPeriod && (enableOutsideDay || isWeekOutOfBounds) ? { ...cellProps, "aria-disabled": isWeekOutOfBounds ? true : void 0 } : cellProps;
|
|
904
|
+
return /* @__PURE__ */ jsx(StyledCell, { ...gridCellProps, children: /* @__PURE__ */ jsx(
|
|
408
905
|
StyledCellContent,
|
|
409
906
|
{
|
|
410
907
|
...mergeProps(buttonProps, focusProps),
|
|
908
|
+
...weekDayProps,
|
|
411
909
|
ref,
|
|
412
|
-
selected:
|
|
910
|
+
selected: effectiveSelected,
|
|
413
911
|
today: isToday(date, getLocalTimeZone()),
|
|
414
|
-
range:
|
|
415
|
-
hidden: isOutsideMonth,
|
|
912
|
+
range: effectiveRange,
|
|
913
|
+
hidden: isOutsideMonth && !isWeekPeriod,
|
|
914
|
+
outsideMonth: isOutsideMonth && isWeekPeriod,
|
|
915
|
+
"data-week-hover": isWeekPeriod && effectiveRange == null ? "" : void 0,
|
|
416
916
|
children: formattedDate
|
|
417
917
|
}
|
|
418
918
|
) });
|
|
@@ -456,6 +956,507 @@ const Grid = React.forwardRef(
|
|
|
456
956
|
}
|
|
457
957
|
);
|
|
458
958
|
|
|
959
|
+
const StyledPeriodGrid = styled(Primitive.div, {
|
|
960
|
+
display: "grid",
|
|
961
|
+
gap: "$50",
|
|
962
|
+
padding: "$50 0",
|
|
963
|
+
// Match the day grid width: 7 cells × $10 (40px) = 280px
|
|
964
|
+
width: "calc($10 * 7)",
|
|
965
|
+
variants: {
|
|
966
|
+
columns: {
|
|
967
|
+
2: { gridTemplateColumns: "repeat(2, 1fr)" },
|
|
968
|
+
3: { gridTemplateColumns: "repeat(3, 1fr)" },
|
|
969
|
+
4: { gridTemplateColumns: "repeat(4, 1fr)" }
|
|
970
|
+
}
|
|
971
|
+
},
|
|
972
|
+
defaultVariants: {
|
|
973
|
+
columns: 4
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
const StyledPeriodRow = styled(Primitive.div, {
|
|
977
|
+
display: "contents"
|
|
978
|
+
});
|
|
979
|
+
const StyledPeriodGridCell = styled(Primitive.div, {
|
|
980
|
+
display: "contents"
|
|
981
|
+
});
|
|
982
|
+
const StyledPeriodCell = styled(BaseButton, {
|
|
983
|
+
justifyContent: "center",
|
|
984
|
+
height: "$10",
|
|
985
|
+
color: "$text-default",
|
|
986
|
+
fontSize: "$200",
|
|
987
|
+
...focus.css({
|
|
988
|
+
position: "relative",
|
|
989
|
+
zIndex: "1",
|
|
990
|
+
boxShadow: "$focus",
|
|
991
|
+
outline: "1px solid transparent"
|
|
992
|
+
}),
|
|
993
|
+
'&:not([aria-disabled="true"])': {
|
|
994
|
+
_hover: {
|
|
995
|
+
backgroundColor: "$background-interactive-surface-secondary-hover"
|
|
996
|
+
}
|
|
997
|
+
},
|
|
998
|
+
'&[aria-disabled="true"]': {
|
|
999
|
+
color: "$text-disabled"
|
|
1000
|
+
},
|
|
1001
|
+
variants: {
|
|
1002
|
+
current: {
|
|
1003
|
+
true: {
|
|
1004
|
+
backgroundColor: "$background-interactive-surface-secondary-hover",
|
|
1005
|
+
color: "$text-default"
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
selected: {
|
|
1009
|
+
true: {
|
|
1010
|
+
backgroundColor: "$background-interactive-selected",
|
|
1011
|
+
color: "$text-on-interactive-primary",
|
|
1012
|
+
"& span": {
|
|
1013
|
+
color: "inherit"
|
|
1014
|
+
},
|
|
1015
|
+
'&:not([aria-disabled="true"])': {
|
|
1016
|
+
_hover: {
|
|
1017
|
+
backgroundColor: "$background-interactive-primary-hover",
|
|
1018
|
+
color: "$text-on-interactive-primary"
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
tall: {
|
|
1024
|
+
true: {
|
|
1025
|
+
height: "$14",
|
|
1026
|
+
flexDirection: "column",
|
|
1027
|
+
gap: "$25"
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
const StyledPeriodCellLabel = styled(Primitive.span, {
|
|
1033
|
+
fontSize: "$100",
|
|
1034
|
+
color: "$text-secondary"
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
const useGridNavigation = ({
|
|
1038
|
+
rows,
|
|
1039
|
+
columns,
|
|
1040
|
+
selectedIndex,
|
|
1041
|
+
currentIndex,
|
|
1042
|
+
resetKey,
|
|
1043
|
+
focusOnMount
|
|
1044
|
+
}) => {
|
|
1045
|
+
const cellRefs = useRef(/* @__PURE__ */ new Map());
|
|
1046
|
+
const [focusedRow, setFocusedRow] = useState(0);
|
|
1047
|
+
const [focusedCol, setFocusedCol] = useState(0);
|
|
1048
|
+
const hasFocusedRef = useRef(false);
|
|
1049
|
+
const key = (r, c) => "".concat(r, "-").concat(c);
|
|
1050
|
+
const isEnabled = useCallback((r, c) => {
|
|
1051
|
+
const el = cellRefs.current.get(key(r, c));
|
|
1052
|
+
return el != null && el.getAttribute("aria-disabled") !== "true";
|
|
1053
|
+
}, []);
|
|
1054
|
+
const findFirstEnabled = useCallback(() => {
|
|
1055
|
+
for (let r = 0; r < rows; r++) {
|
|
1056
|
+
for (let c = 0; c < columns; c++) {
|
|
1057
|
+
if (isEnabled(r, c)) {
|
|
1058
|
+
return [r, c];
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
return null;
|
|
1063
|
+
}, [rows, columns, isEnabled]);
|
|
1064
|
+
const focusCell = useCallback(
|
|
1065
|
+
(row, col) => {
|
|
1066
|
+
var _a;
|
|
1067
|
+
if (isEnabled(row, col)) {
|
|
1068
|
+
(_a = cellRefs.current.get(key(row, col))) == null ? void 0 : _a.focus();
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
[isEnabled]
|
|
1072
|
+
);
|
|
1073
|
+
const resolveFocusTarget = useCallback(() => {
|
|
1074
|
+
for (const index of [selectedIndex, currentIndex]) {
|
|
1075
|
+
if (index == null || index < 0 || index >= rows * columns) {
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
const candidate = [
|
|
1079
|
+
Math.floor(index / columns),
|
|
1080
|
+
index % columns
|
|
1081
|
+
];
|
|
1082
|
+
if (isEnabled(...candidate)) {
|
|
1083
|
+
return candidate;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return findFirstEnabled();
|
|
1087
|
+
}, [selectedIndex, currentIndex, rows, columns, isEnabled, findFirstEnabled]);
|
|
1088
|
+
useLayoutEffect(() => {
|
|
1089
|
+
const target = resolveFocusTarget();
|
|
1090
|
+
if (target == null) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const [row, col] = target;
|
|
1094
|
+
setFocusedRow(row);
|
|
1095
|
+
setFocusedCol(col);
|
|
1096
|
+
if (focusOnMount === true && !hasFocusedRef.current) {
|
|
1097
|
+
hasFocusedRef.current = true;
|
|
1098
|
+
focusCell(row, col);
|
|
1099
|
+
}
|
|
1100
|
+
}, [resolveFocusTarget, focusCell, focusOnMount, resetKey]);
|
|
1101
|
+
const findNextEnabled = useCallback(
|
|
1102
|
+
(row, col, dr, dc) => {
|
|
1103
|
+
let r = row + dr;
|
|
1104
|
+
let c = col + dc;
|
|
1105
|
+
while (r >= 0 && r < rows && c >= 0 && c < columns) {
|
|
1106
|
+
if (isEnabled(r, c)) {
|
|
1107
|
+
return [r, c];
|
|
1108
|
+
}
|
|
1109
|
+
r += dr;
|
|
1110
|
+
c += dc;
|
|
1111
|
+
}
|
|
1112
|
+
return [row, col];
|
|
1113
|
+
},
|
|
1114
|
+
[rows, columns, isEnabled]
|
|
1115
|
+
);
|
|
1116
|
+
const getCellProps = useCallback(
|
|
1117
|
+
(row, col) => ({
|
|
1118
|
+
tabIndex: row === focusedRow && col === focusedCol ? 0 : -1,
|
|
1119
|
+
ref: (el) => {
|
|
1120
|
+
const k = key(row, col);
|
|
1121
|
+
if (el != null) {
|
|
1122
|
+
cellRefs.current.set(k, el);
|
|
1123
|
+
} else {
|
|
1124
|
+
cellRefs.current.delete(k);
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
onFocus: () => {
|
|
1128
|
+
setFocusedRow(row);
|
|
1129
|
+
setFocusedCol(col);
|
|
1130
|
+
},
|
|
1131
|
+
onKeyDown: (e) => {
|
|
1132
|
+
let nextRow = row;
|
|
1133
|
+
let nextCol = col;
|
|
1134
|
+
switch (e.key) {
|
|
1135
|
+
case "ArrowRight":
|
|
1136
|
+
if (col < columns - 1) {
|
|
1137
|
+
[nextRow, nextCol] = findNextEnabled(row, col, 0, 1);
|
|
1138
|
+
}
|
|
1139
|
+
break;
|
|
1140
|
+
case "ArrowLeft":
|
|
1141
|
+
if (col > 0) {
|
|
1142
|
+
[nextRow, nextCol] = findNextEnabled(row, col, 0, -1);
|
|
1143
|
+
}
|
|
1144
|
+
break;
|
|
1145
|
+
case "ArrowDown":
|
|
1146
|
+
if (row < rows - 1) {
|
|
1147
|
+
[nextRow, nextCol] = findNextEnabled(row, col, 1, 0);
|
|
1148
|
+
}
|
|
1149
|
+
break;
|
|
1150
|
+
case "ArrowUp":
|
|
1151
|
+
if (row > 0) {
|
|
1152
|
+
[nextRow, nextCol] = findNextEnabled(row, col, -1, 0);
|
|
1153
|
+
}
|
|
1154
|
+
break;
|
|
1155
|
+
case "Home": {
|
|
1156
|
+
for (let r = 0; r < rows; r++) {
|
|
1157
|
+
for (let c = 0; c < columns; c++) {
|
|
1158
|
+
if (isEnabled(r, c)) {
|
|
1159
|
+
[nextRow, nextCol] = [r, c];
|
|
1160
|
+
r = rows;
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
break;
|
|
1166
|
+
}
|
|
1167
|
+
case "End": {
|
|
1168
|
+
for (let r = rows - 1; r >= 0; r--) {
|
|
1169
|
+
for (let c = columns - 1; c >= 0; c--) {
|
|
1170
|
+
if (isEnabled(r, c)) {
|
|
1171
|
+
[nextRow, nextCol] = [r, c];
|
|
1172
|
+
r = -1;
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
break;
|
|
1178
|
+
}
|
|
1179
|
+
default:
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (nextRow !== row || nextCol !== col) {
|
|
1183
|
+
e.preventDefault();
|
|
1184
|
+
focusCell(nextRow, nextCol);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}),
|
|
1188
|
+
[
|
|
1189
|
+
focusedRow,
|
|
1190
|
+
focusedCol,
|
|
1191
|
+
rows,
|
|
1192
|
+
columns,
|
|
1193
|
+
focusCell,
|
|
1194
|
+
findNextEnabled,
|
|
1195
|
+
isEnabled
|
|
1196
|
+
]
|
|
1197
|
+
);
|
|
1198
|
+
return { getCellProps };
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
const ROWS$2 = 3;
|
|
1202
|
+
const COLUMNS$2 = 4;
|
|
1203
|
+
const MonthGrid = ({
|
|
1204
|
+
navigationYear,
|
|
1205
|
+
timeZone,
|
|
1206
|
+
onMonthSelect,
|
|
1207
|
+
currentMonth,
|
|
1208
|
+
currentYear,
|
|
1209
|
+
selectedMonth,
|
|
1210
|
+
selectedYear,
|
|
1211
|
+
minDate,
|
|
1212
|
+
maxDate,
|
|
1213
|
+
yearFormatter,
|
|
1214
|
+
focusOnMount
|
|
1215
|
+
}) => {
|
|
1216
|
+
const monthFormatter = useDateFormatter({
|
|
1217
|
+
month: "short",
|
|
1218
|
+
timeZone
|
|
1219
|
+
});
|
|
1220
|
+
const { getCellProps } = useGridNavigation({
|
|
1221
|
+
rows: ROWS$2,
|
|
1222
|
+
columns: COLUMNS$2,
|
|
1223
|
+
selectedIndex: selectedYear === navigationYear && selectedMonth != null ? selectedMonth - 1 : -1,
|
|
1224
|
+
currentIndex: currentYear === navigationYear && currentMonth != null ? currentMonth - 1 : -1,
|
|
1225
|
+
resetKey: navigationYear,
|
|
1226
|
+
focusOnMount
|
|
1227
|
+
});
|
|
1228
|
+
const isMonthDisabled = (month) => {
|
|
1229
|
+
if (minDate != null) {
|
|
1230
|
+
const minYear = minDate.year;
|
|
1231
|
+
const minMonth = minDate.month;
|
|
1232
|
+
if (navigationYear < minYear || navigationYear === minYear && month < minMonth) {
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (maxDate != null) {
|
|
1237
|
+
const maxYear = maxDate.year;
|
|
1238
|
+
const maxMonth = maxDate.month;
|
|
1239
|
+
if (navigationYear > maxYear || navigationYear === maxYear && month > maxMonth) {
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
return false;
|
|
1244
|
+
};
|
|
1245
|
+
return /* @__PURE__ */ jsx(
|
|
1246
|
+
StyledPeriodGrid,
|
|
1247
|
+
{
|
|
1248
|
+
columns: 4,
|
|
1249
|
+
role: "grid",
|
|
1250
|
+
"aria-label": yearFormatter.format(navigationYear),
|
|
1251
|
+
children: Array.from({ length: ROWS$2 }, (_, row) => /* @__PURE__ */ jsx(StyledPeriodRow, { role: "row", children: Array.from({ length: COLUMNS$2 }, (_2, col) => {
|
|
1252
|
+
const month = row * COLUMNS$2 + col + 1;
|
|
1253
|
+
const date = new CalendarDate(navigationYear, month, 1).toDate(
|
|
1254
|
+
timeZone
|
|
1255
|
+
);
|
|
1256
|
+
const label = monthFormatter.format(date);
|
|
1257
|
+
const disabled = isMonthDisabled(month);
|
|
1258
|
+
const isCurrent = navigationYear === currentYear && month === currentMonth;
|
|
1259
|
+
const isSelected = navigationYear === selectedYear && month === selectedMonth;
|
|
1260
|
+
const cellProps = getCellProps(row, col);
|
|
1261
|
+
return /* @__PURE__ */ jsx(
|
|
1262
|
+
StyledPeriodGridCell,
|
|
1263
|
+
{
|
|
1264
|
+
role: "gridcell",
|
|
1265
|
+
"aria-selected": isSelected || void 0,
|
|
1266
|
+
children: /* @__PURE__ */ jsx(
|
|
1267
|
+
StyledPeriodCell,
|
|
1268
|
+
{
|
|
1269
|
+
...cellProps,
|
|
1270
|
+
current: isCurrent,
|
|
1271
|
+
selected: isSelected,
|
|
1272
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
1273
|
+
"aria-disabled": disabled || void 0,
|
|
1274
|
+
"aria-label": "".concat(label, " ").concat(yearFormatter.format(
|
|
1275
|
+
navigationYear
|
|
1276
|
+
)),
|
|
1277
|
+
onPress: () => onMonthSelect(month),
|
|
1278
|
+
children: label
|
|
1279
|
+
}
|
|
1280
|
+
)
|
|
1281
|
+
},
|
|
1282
|
+
month
|
|
1283
|
+
);
|
|
1284
|
+
}) }, row))
|
|
1285
|
+
}
|
|
1286
|
+
);
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
const ROWS$1 = 2;
|
|
1290
|
+
const COLUMNS$1 = 2;
|
|
1291
|
+
const QUARTER_MONTHS = [
|
|
1292
|
+
[1, 3],
|
|
1293
|
+
[4, 6],
|
|
1294
|
+
[7, 9],
|
|
1295
|
+
[10, 12]
|
|
1296
|
+
];
|
|
1297
|
+
const QuarterGrid = ({
|
|
1298
|
+
navigationYear,
|
|
1299
|
+
timeZone,
|
|
1300
|
+
onQuarterSelect,
|
|
1301
|
+
currentQuarter,
|
|
1302
|
+
currentYear,
|
|
1303
|
+
selectedQuarter,
|
|
1304
|
+
selectedYear,
|
|
1305
|
+
minDate,
|
|
1306
|
+
maxDate,
|
|
1307
|
+
formatQuarterLabel,
|
|
1308
|
+
yearFormatter,
|
|
1309
|
+
focusOnMount
|
|
1310
|
+
}) => {
|
|
1311
|
+
const strings = useCalendarStrings();
|
|
1312
|
+
const monthFormatter = useDateFormatter({
|
|
1313
|
+
month: "short",
|
|
1314
|
+
timeZone
|
|
1315
|
+
});
|
|
1316
|
+
const { getCellProps } = useGridNavigation({
|
|
1317
|
+
rows: ROWS$1,
|
|
1318
|
+
columns: COLUMNS$1,
|
|
1319
|
+
selectedIndex: selectedYear === navigationYear && selectedQuarter != null ? selectedQuarter - 1 : -1,
|
|
1320
|
+
currentIndex: currentYear === navigationYear && currentQuarter != null ? currentQuarter - 1 : -1,
|
|
1321
|
+
resetKey: navigationYear,
|
|
1322
|
+
focusOnMount
|
|
1323
|
+
});
|
|
1324
|
+
const isQuarterDisabled = (quarter) => {
|
|
1325
|
+
const [startMonth, endMonth] = QUARTER_MONTHS[quarter - 1];
|
|
1326
|
+
if (minDate != null) {
|
|
1327
|
+
if (navigationYear < minDate.year || navigationYear === minDate.year && endMonth < minDate.month) {
|
|
1328
|
+
return true;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (maxDate != null) {
|
|
1332
|
+
if (navigationYear > maxDate.year || navigationYear === maxDate.year && startMonth > maxDate.month) {
|
|
1333
|
+
return true;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
return false;
|
|
1337
|
+
};
|
|
1338
|
+
const formatRange = (quarter) => {
|
|
1339
|
+
const [startMonth, endMonth] = QUARTER_MONTHS[quarter - 1];
|
|
1340
|
+
const start = monthFormatter.format(
|
|
1341
|
+
new CalendarDate(navigationYear, startMonth, 1).toDate(timeZone)
|
|
1342
|
+
);
|
|
1343
|
+
const end = monthFormatter.format(
|
|
1344
|
+
new CalendarDate(navigationYear, endMonth, 1).toDate(timeZone)
|
|
1345
|
+
);
|
|
1346
|
+
return "".concat(start, " \u2013 ").concat(end);
|
|
1347
|
+
};
|
|
1348
|
+
return /* @__PURE__ */ jsx(
|
|
1349
|
+
StyledPeriodGrid,
|
|
1350
|
+
{
|
|
1351
|
+
columns: 2,
|
|
1352
|
+
role: "grid",
|
|
1353
|
+
"aria-label": yearFormatter.format(navigationYear),
|
|
1354
|
+
children: Array.from({ length: ROWS$1 }, (_, row) => /* @__PURE__ */ jsx(StyledPeriodRow, { role: "row", children: Array.from({ length: COLUMNS$1 }, (_2, col) => {
|
|
1355
|
+
const quarter = row * COLUMNS$1 + col + 1;
|
|
1356
|
+
const disabled = isQuarterDisabled(quarter);
|
|
1357
|
+
const isCurrent = navigationYear === currentYear && quarter === currentQuarter;
|
|
1358
|
+
const isSelected = navigationYear === selectedYear && quarter === selectedQuarter;
|
|
1359
|
+
const cellProps = getCellProps(row, col);
|
|
1360
|
+
const rangeLabel = formatRange(quarter);
|
|
1361
|
+
const label = (formatQuarterLabel != null ? formatQuarterLabel : strings.quarter)(quarter);
|
|
1362
|
+
return /* @__PURE__ */ jsx(
|
|
1363
|
+
StyledPeriodGridCell,
|
|
1364
|
+
{
|
|
1365
|
+
role: "gridcell",
|
|
1366
|
+
"aria-selected": isSelected || void 0,
|
|
1367
|
+
children: /* @__PURE__ */ jsxs(
|
|
1368
|
+
StyledPeriodCell,
|
|
1369
|
+
{
|
|
1370
|
+
...cellProps,
|
|
1371
|
+
current: isCurrent,
|
|
1372
|
+
selected: isSelected,
|
|
1373
|
+
tall: true,
|
|
1374
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
1375
|
+
"aria-disabled": disabled || void 0,
|
|
1376
|
+
"aria-label": "".concat(label, " ").concat(yearFormatter.format(
|
|
1377
|
+
navigationYear
|
|
1378
|
+
), ", ").concat(rangeLabel),
|
|
1379
|
+
onPress: () => onQuarterSelect(quarter),
|
|
1380
|
+
children: [
|
|
1381
|
+
label,
|
|
1382
|
+
/* @__PURE__ */ jsx(StyledPeriodCellLabel, { children: rangeLabel })
|
|
1383
|
+
]
|
|
1384
|
+
}
|
|
1385
|
+
)
|
|
1386
|
+
},
|
|
1387
|
+
quarter
|
|
1388
|
+
);
|
|
1389
|
+
}) }, row))
|
|
1390
|
+
}
|
|
1391
|
+
);
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1394
|
+
const ROWS = 3;
|
|
1395
|
+
const COLUMNS = 4;
|
|
1396
|
+
const TOTAL_CELLS = ROWS * COLUMNS;
|
|
1397
|
+
const YearGrid = ({
|
|
1398
|
+
decadeStart,
|
|
1399
|
+
onYearSelect,
|
|
1400
|
+
currentYear,
|
|
1401
|
+
selectedYear,
|
|
1402
|
+
minDate,
|
|
1403
|
+
maxDate,
|
|
1404
|
+
yearFormatter,
|
|
1405
|
+
focusOnMount
|
|
1406
|
+
}) => {
|
|
1407
|
+
const { getCellProps } = useGridNavigation({
|
|
1408
|
+
rows: ROWS,
|
|
1409
|
+
columns: COLUMNS,
|
|
1410
|
+
selectedIndex: selectedYear != null ? selectedYear - decadeStart : -1,
|
|
1411
|
+
currentIndex: currentYear != null ? currentYear - decadeStart : -1,
|
|
1412
|
+
resetKey: decadeStart,
|
|
1413
|
+
focusOnMount
|
|
1414
|
+
});
|
|
1415
|
+
const isYearDisabled = (year) => {
|
|
1416
|
+
if (minDate != null && year < minDate.year) return true;
|
|
1417
|
+
if (maxDate != null && year > maxDate.year) return true;
|
|
1418
|
+
return false;
|
|
1419
|
+
};
|
|
1420
|
+
return /* @__PURE__ */ jsx(
|
|
1421
|
+
StyledPeriodGrid,
|
|
1422
|
+
{
|
|
1423
|
+
columns: 4,
|
|
1424
|
+
role: "grid",
|
|
1425
|
+
"aria-label": "".concat(yearFormatter.format(decadeStart), "\u2013").concat(yearFormatter.format(
|
|
1426
|
+
decadeStart + TOTAL_CELLS - 1
|
|
1427
|
+
)),
|
|
1428
|
+
children: Array.from({ length: ROWS }, (_, row) => /* @__PURE__ */ jsx(StyledPeriodRow, { role: "row", children: Array.from({ length: COLUMNS }, (_2, col) => {
|
|
1429
|
+
const year = decadeStart + row * COLUMNS + col;
|
|
1430
|
+
const disabled = isYearDisabled(year);
|
|
1431
|
+
const isCurrent = year === currentYear;
|
|
1432
|
+
const isSelected = year === selectedYear;
|
|
1433
|
+
const cellProps = getCellProps(row, col);
|
|
1434
|
+
return /* @__PURE__ */ jsx(
|
|
1435
|
+
StyledPeriodGridCell,
|
|
1436
|
+
{
|
|
1437
|
+
role: "gridcell",
|
|
1438
|
+
"aria-selected": isSelected || void 0,
|
|
1439
|
+
children: /* @__PURE__ */ jsx(
|
|
1440
|
+
StyledPeriodCell,
|
|
1441
|
+
{
|
|
1442
|
+
...cellProps,
|
|
1443
|
+
current: isCurrent,
|
|
1444
|
+
selected: isSelected,
|
|
1445
|
+
"aria-current": isCurrent ? "date" : void 0,
|
|
1446
|
+
"aria-disabled": disabled || void 0,
|
|
1447
|
+
"aria-label": yearFormatter.format(year),
|
|
1448
|
+
onPress: () => onYearSelect(year),
|
|
1449
|
+
children: yearFormatter.format(year)
|
|
1450
|
+
}
|
|
1451
|
+
)
|
|
1452
|
+
},
|
|
1453
|
+
year
|
|
1454
|
+
);
|
|
1455
|
+
}) }, row))
|
|
1456
|
+
}
|
|
1457
|
+
);
|
|
1458
|
+
};
|
|
1459
|
+
|
|
459
1460
|
const StyledPredefinedRanges = styled(Primitive.div, {
|
|
460
1461
|
display: "flex",
|
|
461
1462
|
flexDirection: "column",
|
|
@@ -500,9 +1501,31 @@ const PredefinedRanges = () => {
|
|
|
500
1501
|
};
|
|
501
1502
|
|
|
502
1503
|
const CONTENT_OFFSET = parseInt(theme.space[50]);
|
|
1504
|
+
const VIEW_MODE_UP = {
|
|
1505
|
+
days: "months",
|
|
1506
|
+
months: "years",
|
|
1507
|
+
quarters: "years",
|
|
1508
|
+
years: "years"
|
|
1509
|
+
};
|
|
503
1510
|
const SharedContent = React.forwardRef(
|
|
504
1511
|
({ calendarProps, prevButtonProps, nextButtonProps, state }, forwardRef) => {
|
|
505
|
-
const {
|
|
1512
|
+
const {
|
|
1513
|
+
visibleDuration,
|
|
1514
|
+
viewMode,
|
|
1515
|
+
setViewMode,
|
|
1516
|
+
navigationYear,
|
|
1517
|
+
setNavigationYear,
|
|
1518
|
+
navigation,
|
|
1519
|
+
period,
|
|
1520
|
+
picker,
|
|
1521
|
+
minDate,
|
|
1522
|
+
maxDate,
|
|
1523
|
+
onCalendarDateClick,
|
|
1524
|
+
formatQuarterLabel,
|
|
1525
|
+
goToCurrentLabel,
|
|
1526
|
+
value: selectedValue
|
|
1527
|
+
} = useCalendarContext();
|
|
1528
|
+
const { locale } = useLocale();
|
|
506
1529
|
const monthDateFormatter = useDateFormatter({
|
|
507
1530
|
month: "long",
|
|
508
1531
|
year: "numeric",
|
|
@@ -514,8 +1537,185 @@ const SharedContent = React.forwardRef(
|
|
|
514
1537
|
const nextAriaLabel = "".concat(nextButtonProps["aria-label"], ", ").concat(monthDateFormatter.format(
|
|
515
1538
|
state.visibleRange.start.add({ months: visibleDuration.months === 2 ? 2 : 1 }).toDate(state.timeZone)
|
|
516
1539
|
));
|
|
1540
|
+
const todayDate = useMemo(() => today(getLocalTimeZone()), []);
|
|
1541
|
+
const currentYear = todayDate.year;
|
|
1542
|
+
const currentMonth = todayDate.month;
|
|
1543
|
+
const yearFormatter = useMemo(
|
|
1544
|
+
() => new Intl.NumberFormat(locale, { useGrouping: false }),
|
|
1545
|
+
[locale]
|
|
1546
|
+
);
|
|
1547
|
+
const handleZoomOut = useCallback(() => {
|
|
1548
|
+
if (viewMode === "days") {
|
|
1549
|
+
setNavigationYear(state.visibleRange.start.year);
|
|
1550
|
+
}
|
|
1551
|
+
setViewMode(VIEW_MODE_UP[viewMode]);
|
|
1552
|
+
}, [
|
|
1553
|
+
viewMode,
|
|
1554
|
+
setViewMode,
|
|
1555
|
+
setNavigationYear,
|
|
1556
|
+
state.visibleRange.start.year
|
|
1557
|
+
]);
|
|
1558
|
+
const handleMonthSelect = useCallback(
|
|
1559
|
+
(month) => {
|
|
1560
|
+
if (period === "month") {
|
|
1561
|
+
const date = new CalendarDate(navigationYear, month, 1);
|
|
1562
|
+
onCalendarDateClick(date);
|
|
1563
|
+
} else {
|
|
1564
|
+
state.setFocusedDate(new CalendarDate(navigationYear, month, 1));
|
|
1565
|
+
state.setFocused(true);
|
|
1566
|
+
setViewMode("days");
|
|
1567
|
+
}
|
|
1568
|
+
},
|
|
1569
|
+
[navigationYear, state, setViewMode, period, onCalendarDateClick]
|
|
1570
|
+
);
|
|
1571
|
+
const handleQuarterSelect = useCallback(
|
|
1572
|
+
(quarter) => {
|
|
1573
|
+
const firstMonth = (quarter - 1) * 3 + 1;
|
|
1574
|
+
const date = new CalendarDate(navigationYear, firstMonth, 1);
|
|
1575
|
+
onCalendarDateClick(date);
|
|
1576
|
+
},
|
|
1577
|
+
[navigationYear, onCalendarDateClick]
|
|
1578
|
+
);
|
|
1579
|
+
const handleYearSelect = useCallback(
|
|
1580
|
+
(year) => {
|
|
1581
|
+
setNavigationYear(year);
|
|
1582
|
+
setViewMode(period === "quarter" ? "quarters" : "months");
|
|
1583
|
+
},
|
|
1584
|
+
[setNavigationYear, setViewMode, period]
|
|
1585
|
+
);
|
|
1586
|
+
const handlePrevOverride = useCallback(() => {
|
|
1587
|
+
if (viewMode === "years") {
|
|
1588
|
+
setNavigationYear((y) => y - DECADE_SIZE);
|
|
1589
|
+
} else {
|
|
1590
|
+
setNavigationYear((y) => y - 1);
|
|
1591
|
+
}
|
|
1592
|
+
}, [viewMode, setNavigationYear]);
|
|
1593
|
+
const handleNextOverride = useCallback(() => {
|
|
1594
|
+
if (viewMode === "years") {
|
|
1595
|
+
setNavigationYear((y) => y + DECADE_SIZE);
|
|
1596
|
+
} else {
|
|
1597
|
+
setNavigationYear((y) => y + 1);
|
|
1598
|
+
}
|
|
1599
|
+
}, [viewMode, setNavigationYear]);
|
|
1600
|
+
const decadeStart = Math.floor(navigationYear / DECADE_SIZE) * DECADE_SIZE;
|
|
1601
|
+
const [isPrevDisabled, isNextDisabled] = useMemo(() => {
|
|
1602
|
+
if (viewMode === "days") {
|
|
1603
|
+
return [void 0, void 0];
|
|
1604
|
+
}
|
|
1605
|
+
if (viewMode === "years") {
|
|
1606
|
+
return [
|
|
1607
|
+
minDate != null && decadeStart - 1 < minDate.year,
|
|
1608
|
+
maxDate != null && decadeStart + DECADE_SIZE > maxDate.year
|
|
1609
|
+
];
|
|
1610
|
+
}
|
|
1611
|
+
return [
|
|
1612
|
+
minDate != null && navigationYear - 1 < minDate.year,
|
|
1613
|
+
maxDate != null && navigationYear + 1 > maxDate.year
|
|
1614
|
+
];
|
|
1615
|
+
}, [viewMode, decadeStart, navigationYear, minDate, maxDate]);
|
|
1616
|
+
const currentQuarter = Math.ceil(currentMonth / 3);
|
|
1617
|
+
const handleGoToCurrent = useCallback(() => {
|
|
1618
|
+
const now = today(getLocalTimeZone());
|
|
1619
|
+
let date;
|
|
1620
|
+
switch (period) {
|
|
1621
|
+
case "week":
|
|
1622
|
+
date = startOfWeek(now, locale);
|
|
1623
|
+
break;
|
|
1624
|
+
case "month":
|
|
1625
|
+
date = new CalendarDate(currentYear, currentMonth, 1);
|
|
1626
|
+
break;
|
|
1627
|
+
case "quarter": {
|
|
1628
|
+
const firstMonth = (currentQuarter - 1) * 3 + 1;
|
|
1629
|
+
date = new CalendarDate(currentYear, firstMonth, 1);
|
|
1630
|
+
break;
|
|
1631
|
+
}
|
|
1632
|
+
default:
|
|
1633
|
+
date = now;
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
onCalendarDateClick(date);
|
|
1637
|
+
}, [
|
|
1638
|
+
period,
|
|
1639
|
+
locale,
|
|
1640
|
+
currentYear,
|
|
1641
|
+
currentMonth,
|
|
1642
|
+
currentQuarter,
|
|
1643
|
+
onCalendarDateClick
|
|
1644
|
+
]);
|
|
1645
|
+
const prevViewModeRef = useRef(viewMode);
|
|
1646
|
+
const shouldAutoFocus = prevViewModeRef.current !== viewMode && viewMode !== "days";
|
|
1647
|
+
useEffect(() => {
|
|
1648
|
+
prevViewModeRef.current = viewMode;
|
|
1649
|
+
}, [viewMode]);
|
|
1650
|
+
const selectedDate = selectedValue != null && !("start" in selectedValue) ? selectedValue : null;
|
|
1651
|
+
const selectedYear = selectedDate == null ? void 0 : selectedDate.year;
|
|
1652
|
+
const selectedMonth = selectedDate == null ? void 0 : selectedDate.month;
|
|
1653
|
+
const selectedQuarter = selectedMonth != null ? Math.ceil(selectedMonth / 3) : void 0;
|
|
1654
|
+
const isSelectionInCurrentPeriod = useMemo(
|
|
1655
|
+
() => selectedDate != null && isCurrentPeriod(selectedDate, period, locale),
|
|
1656
|
+
[selectedDate, period, locale]
|
|
1657
|
+
);
|
|
1658
|
+
const showGoToCurrent = goToCurrentLabel != null && picker === "single" && !isSelectionInCurrentPeriod;
|
|
1659
|
+
const renderGrid = () => {
|
|
1660
|
+
switch (viewMode) {
|
|
1661
|
+
case "months":
|
|
1662
|
+
return /* @__PURE__ */ jsx(
|
|
1663
|
+
MonthGrid,
|
|
1664
|
+
{
|
|
1665
|
+
navigationYear,
|
|
1666
|
+
timeZone: state.timeZone,
|
|
1667
|
+
onMonthSelect: handleMonthSelect,
|
|
1668
|
+
currentMonth,
|
|
1669
|
+
currentYear,
|
|
1670
|
+
selectedMonth,
|
|
1671
|
+
selectedYear,
|
|
1672
|
+
minDate,
|
|
1673
|
+
maxDate,
|
|
1674
|
+
yearFormatter,
|
|
1675
|
+
focusOnMount: shouldAutoFocus
|
|
1676
|
+
}
|
|
1677
|
+
);
|
|
1678
|
+
case "quarters":
|
|
1679
|
+
return /* @__PURE__ */ jsx(
|
|
1680
|
+
QuarterGrid,
|
|
1681
|
+
{
|
|
1682
|
+
navigationYear,
|
|
1683
|
+
timeZone: state.timeZone,
|
|
1684
|
+
onQuarterSelect: handleQuarterSelect,
|
|
1685
|
+
currentQuarter,
|
|
1686
|
+
currentYear,
|
|
1687
|
+
selectedQuarter,
|
|
1688
|
+
selectedYear,
|
|
1689
|
+
minDate,
|
|
1690
|
+
maxDate,
|
|
1691
|
+
formatQuarterLabel,
|
|
1692
|
+
yearFormatter,
|
|
1693
|
+
focusOnMount: shouldAutoFocus
|
|
1694
|
+
}
|
|
1695
|
+
);
|
|
1696
|
+
case "years":
|
|
1697
|
+
return /* @__PURE__ */ jsx(
|
|
1698
|
+
YearGrid,
|
|
1699
|
+
{
|
|
1700
|
+
decadeStart,
|
|
1701
|
+
onYearSelect: handleYearSelect,
|
|
1702
|
+
currentYear,
|
|
1703
|
+
selectedYear,
|
|
1704
|
+
minDate,
|
|
1705
|
+
maxDate,
|
|
1706
|
+
yearFormatter,
|
|
1707
|
+
focusOnMount: shouldAutoFocus
|
|
1708
|
+
}
|
|
1709
|
+
);
|
|
1710
|
+
default:
|
|
1711
|
+
return /* @__PURE__ */ jsxs(StyledGridContent, { children: [
|
|
1712
|
+
/* @__PURE__ */ jsx(StyledBodyContentLeft, { children: /* @__PURE__ */ jsx(Grid, { state }) }),
|
|
1713
|
+
visibleDuration.months === 2 && /* @__PURE__ */ jsx(StyledBodyContentRight, { children: /* @__PURE__ */ jsx(Grid, { state, offset: { months: 1 } }) })
|
|
1714
|
+
] });
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
517
1717
|
return /* @__PURE__ */ jsxs(StyledCalendarBody, { ...calendarProps, ref: forwardRef, children: [
|
|
518
|
-
/* @__PURE__ */ jsx(PredefinedRanges, {}),
|
|
1718
|
+
viewMode === "days" && /* @__PURE__ */ jsx(PredefinedRanges, {}),
|
|
519
1719
|
/* @__PURE__ */ jsxs(Flex, { direction: "column", children: [
|
|
520
1720
|
/* @__PURE__ */ jsx(
|
|
521
1721
|
Header,
|
|
@@ -529,12 +1729,19 @@ const SharedContent = React.forwardRef(
|
|
|
529
1729
|
...nextButtonProps,
|
|
530
1730
|
"aria-label": nextAriaLabel
|
|
531
1731
|
},
|
|
532
|
-
visibleMonths: visibleDuration.months
|
|
1732
|
+
visibleMonths: visibleDuration.months,
|
|
1733
|
+
yearFormatter,
|
|
1734
|
+
onPrevOverride: handlePrevOverride,
|
|
1735
|
+
onNextOverride: handleNextOverride,
|
|
1736
|
+
isPrevDisabled,
|
|
1737
|
+
isNextDisabled,
|
|
1738
|
+
onZoomOut: navigation === true ? handleZoomOut : void 0
|
|
533
1739
|
}
|
|
534
1740
|
),
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
1741
|
+
renderGrid(),
|
|
1742
|
+
showGoToCurrent && /* @__PURE__ */ jsxs(StyledGoToCurrent, { onPress: handleGoToCurrent, children: [
|
|
1743
|
+
/* @__PURE__ */ jsx(IconClock, { "aria-hidden": "true" }),
|
|
1744
|
+
goToCurrentLabel
|
|
538
1745
|
] })
|
|
539
1746
|
] })
|
|
540
1747
|
] });
|
|
@@ -547,15 +1754,31 @@ const DatePickerContent = () => {
|
|
|
547
1754
|
visibleDuration,
|
|
548
1755
|
minDate,
|
|
549
1756
|
maxDate,
|
|
550
|
-
onCalendarDateClick
|
|
1757
|
+
onCalendarDateClick,
|
|
1758
|
+
period
|
|
551
1759
|
} = useCalendarContext();
|
|
1760
|
+
const handleChange = useCallback(
|
|
1761
|
+
(date) => {
|
|
1762
|
+
if (period === "week") {
|
|
1763
|
+
const weekStart = startOfWeek(date, locale);
|
|
1764
|
+
const weekEnd = weekStart.add({ days: 6 });
|
|
1765
|
+
if (minDate != null && weekStart.compare(minDate) < 0 || maxDate != null && weekEnd.compare(maxDate) > 0) {
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
onCalendarDateClick(weekStart);
|
|
1769
|
+
} else {
|
|
1770
|
+
onCalendarDateClick(date);
|
|
1771
|
+
}
|
|
1772
|
+
},
|
|
1773
|
+
[period, locale, minDate, maxDate, onCalendarDateClick]
|
|
1774
|
+
);
|
|
552
1775
|
const state = useCalendarState({
|
|
553
1776
|
value: calendarValue,
|
|
554
1777
|
visibleDuration,
|
|
555
1778
|
minValue: minDate,
|
|
556
1779
|
maxValue: maxDate,
|
|
557
1780
|
autoFocus: true,
|
|
558
|
-
onChange:
|
|
1781
|
+
onChange: handleChange,
|
|
559
1782
|
locale,
|
|
560
1783
|
createCalendar
|
|
561
1784
|
});
|
|
@@ -652,44 +1875,6 @@ const Content = React.forwardRef(
|
|
|
652
1875
|
}
|
|
653
1876
|
);
|
|
654
1877
|
|
|
655
|
-
const StyledValue = styled(Primitive.span, {});
|
|
656
|
-
|
|
657
|
-
const DEFAULT_FORMAT = {
|
|
658
|
-
day: "2-digit",
|
|
659
|
-
month: "short",
|
|
660
|
-
year: "numeric"
|
|
661
|
-
};
|
|
662
|
-
function formatDate(date, formatter, timeZone) {
|
|
663
|
-
return formatter.format(date.toDate(timeZone));
|
|
664
|
-
}
|
|
665
|
-
function formatValue(value, formatter, timeZone) {
|
|
666
|
-
if (value == null || value === void 0) {
|
|
667
|
-
return void 0;
|
|
668
|
-
}
|
|
669
|
-
if ("start" in value) {
|
|
670
|
-
if (value.start == null || value.end == null) {
|
|
671
|
-
return void 0;
|
|
672
|
-
}
|
|
673
|
-
return "".concat(formatDate(value.start, formatter, timeZone), " - ").concat(formatDate(
|
|
674
|
-
value.end,
|
|
675
|
-
formatter,
|
|
676
|
-
timeZone
|
|
677
|
-
));
|
|
678
|
-
}
|
|
679
|
-
return formatDate(value, formatter, timeZone);
|
|
680
|
-
}
|
|
681
|
-
const Value = React.forwardRef(({ format = DEFAULT_FORMAT, ...restProps }, forwardRef) => {
|
|
682
|
-
const { value } = useCalendarContext();
|
|
683
|
-
const timeZone = useMemo(getLocalTimeZone, [format]);
|
|
684
|
-
const formatWithTimeZone = useMemo(
|
|
685
|
-
() => ({ ...format, timeZone }),
|
|
686
|
-
[format, timeZone]
|
|
687
|
-
);
|
|
688
|
-
const dateFormatter = useDateFormatter(formatWithTimeZone);
|
|
689
|
-
const valueToDisplay = formatValue(value, dateFormatter, timeZone);
|
|
690
|
-
return /* @__PURE__ */ jsx(StyledValue, { ref: forwardRef, ...restProps, children: valueToDisplay });
|
|
691
|
-
});
|
|
692
|
-
|
|
693
1878
|
const ClearButton = React.forwardRef(({ onClear, children, ...restProps }, forwardRef) => {
|
|
694
1879
|
const { value, setValue } = useCalendarContext();
|
|
695
1880
|
const onClearHandler = () => {
|
|
@@ -780,6 +1965,11 @@ const Calendar = React.forwardRef(
|
|
|
780
1965
|
"aria-disabled": ariaDisabled,
|
|
781
1966
|
children,
|
|
782
1967
|
predefinedValues,
|
|
1968
|
+
navigation,
|
|
1969
|
+
period,
|
|
1970
|
+
formatQuarterLabel,
|
|
1971
|
+
formatSwitchViewLabel,
|
|
1972
|
+
goToCurrentLabel,
|
|
783
1973
|
...props
|
|
784
1974
|
}, forwardRef) => {
|
|
785
1975
|
const providerProps = {
|
|
@@ -796,7 +1986,12 @@ const Calendar = React.forwardRef(
|
|
|
796
1986
|
onOpen,
|
|
797
1987
|
disabled,
|
|
798
1988
|
ariaDisabled,
|
|
799
|
-
predefinedValues
|
|
1989
|
+
predefinedValues,
|
|
1990
|
+
navigation,
|
|
1991
|
+
period,
|
|
1992
|
+
formatQuarterLabel,
|
|
1993
|
+
formatSwitchViewLabel,
|
|
1994
|
+
goToCurrentLabel
|
|
800
1995
|
};
|
|
801
1996
|
const validatedLocale = getValidLocale(locale);
|
|
802
1997
|
return /* @__PURE__ */ jsx(I18nProvider, { locale: validatedLocale, children: /* @__PURE__ */ jsx(CalendarProvider, { ...providerProps, children: /* @__PURE__ */ jsx(Root, { ...props, ref: forwardRef, children }) }) });
|
|
@@ -811,7 +2006,12 @@ const CalendarGrid = ({
|
|
|
811
2006
|
visibleMonths,
|
|
812
2007
|
minDate,
|
|
813
2008
|
maxDate,
|
|
814
|
-
predefinedValues
|
|
2009
|
+
predefinedValues,
|
|
2010
|
+
navigation,
|
|
2011
|
+
period,
|
|
2012
|
+
formatQuarterLabel,
|
|
2013
|
+
formatSwitchViewLabel,
|
|
2014
|
+
goToCurrentLabel
|
|
815
2015
|
}) => {
|
|
816
2016
|
const providerProps = {
|
|
817
2017
|
picker,
|
|
@@ -821,7 +2021,12 @@ const CalendarGrid = ({
|
|
|
821
2021
|
visibleMonths,
|
|
822
2022
|
minDate,
|
|
823
2023
|
maxDate,
|
|
824
|
-
predefinedValues
|
|
2024
|
+
predefinedValues,
|
|
2025
|
+
navigation,
|
|
2026
|
+
period,
|
|
2027
|
+
formatQuarterLabel,
|
|
2028
|
+
formatSwitchViewLabel,
|
|
2029
|
+
goToCurrentLabel
|
|
825
2030
|
};
|
|
826
2031
|
const validatedLocale = getValidLocale(locale);
|
|
827
2032
|
return /* @__PURE__ */ jsx(I18nProvider, { locale: validatedLocale, children: /* @__PURE__ */ jsx(CalendarProvider, { ...providerProps, children: /* @__PURE__ */ jsx(CalendarGridContent, {}) }) });
|