@codecademy/gamut 68.1.1-alpha.613ffe.0 → 68.1.1-alpha.9135d1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/ConnectedForm/utils.d.ts +1 -1
  2. package/dist/DatePicker/Calendar/Calendar.d.ts +11 -0
  3. package/dist/DatePicker/Calendar/Calendar.js +16 -0
  4. package/dist/DatePicker/Calendar/CalendarBody.d.ts +3 -0
  5. package/dist/DatePicker/Calendar/CalendarBody.js +219 -0
  6. package/dist/DatePicker/Calendar/CalendarFooter.d.ts +3 -0
  7. package/dist/DatePicker/Calendar/CalendarFooter.js +60 -0
  8. package/dist/DatePicker/Calendar/CalendarHeader.d.ts +3 -0
  9. package/dist/DatePicker/Calendar/CalendarHeader.js +51 -0
  10. package/dist/DatePicker/Calendar/index.d.ts +6 -0
  11. package/dist/DatePicker/Calendar/index.js +5 -0
  12. package/dist/DatePicker/Calendar/types.d.ts +57 -0
  13. package/dist/DatePicker/Calendar/types.js +1 -0
  14. package/dist/DatePicker/Calendar/utils/dateGrid.d.ts +34 -0
  15. package/dist/DatePicker/Calendar/utils/dateGrid.js +101 -0
  16. package/dist/DatePicker/Calendar/utils/format.d.ts +39 -0
  17. package/dist/DatePicker/Calendar/utils/format.js +130 -0
  18. package/dist/DatePicker/Calendar/utils/index.d.ts +3 -0
  19. package/dist/DatePicker/Calendar/utils/index.js +3 -0
  20. package/dist/DatePicker/Calendar/utils/validation.d.ts +13 -0
  21. package/dist/DatePicker/Calendar/utils/validation.js +23 -0
  22. package/dist/DatePicker/DatePicker.d.ts +8 -0
  23. package/dist/DatePicker/DatePicker.js +125 -0
  24. package/dist/DatePicker/DatePickerCalendar.d.ts +13 -0
  25. package/dist/DatePicker/DatePickerCalendar.js +160 -0
  26. package/dist/DatePicker/DatePickerContext.d.ts +11 -0
  27. package/dist/DatePicker/DatePickerContext.js +18 -0
  28. package/dist/DatePicker/DatePickerInput.d.ts +16 -0
  29. package/dist/DatePicker/DatePickerInput.js +127 -0
  30. package/dist/DatePicker/index.d.ts +13 -0
  31. package/dist/DatePicker/index.js +10 -0
  32. package/dist/DatePicker/types.d.ts +93 -0
  33. package/dist/DatePicker/types.js +1 -0
  34. package/dist/FocusTrap/index.d.ts +2 -2
  35. package/dist/List/elements.d.ts +1 -1
  36. package/dist/PopoverContainer/PopoverContainer.js +3 -1
  37. package/dist/PopoverContainer/types.d.ts +5 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +1 -0
  40. package/package.json +6 -6
@@ -122,9 +122,9 @@ export declare function useDebouncedField<T extends InputTypes>({ name, watchUpd
122
122
  value: T extends "checkbox" ? boolean : string;
123
123
  error: string | FieldError | Merge<FieldError, FieldErrorsImpl<any>> | undefined;
124
124
  ref: import("react-hook-form").UseFormRegisterReturn<string>;
125
- control: import("react-hook-form").Control<import("react-hook-form").FieldValues, any, import("react-hook-form").FieldValues>;
126
125
  isDisabled: boolean;
127
126
  isLoading: boolean | undefined;
127
+ control: import("react-hook-form").Control<import("react-hook-form").FieldValues, any, import("react-hook-form").FieldValues>;
128
128
  isSoloField: boolean | undefined;
129
129
  validation: RegisterOptions | undefined;
130
130
  getValues: import("react-hook-form").UseFormGetValues<import("react-hook-form").FieldValues>;
@@ -0,0 +1,11 @@
1
+ /// <reference types="react" />
2
+ /**
3
+ * Outer wrapper for the calendar (header + body + footer).
4
+ * Used by DatePickerCalendar to group the calendar content.
5
+ */
6
+ export declare const Calendar: import("@emotion/styled").StyledComponent<{
7
+ theme?: import("@emotion/react").Theme | undefined;
8
+ as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
9
+ } & {
10
+ theme?: import("@emotion/react").Theme | undefined;
11
+ }, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
@@ -0,0 +1,16 @@
1
+ import _styled from "@emotion/styled/base";
2
+ import { css } from '@codecademy/gamut-styles';
3
+ /**
4
+ * Outer wrapper for the calendar (header + body + footer).
5
+ * Used by DatePickerCalendar to group the calendar content.
6
+ */
7
+ export const Calendar = /*#__PURE__*/_styled("div", {
8
+ target: "eq0g1i20",
9
+ label: "Calendar"
10
+ })(css({
11
+ backgroundColor: 'background',
12
+ borderRadius: 'lg',
13
+ boxShadow: '0 4px 16px rgba(0, 0, 0, 0.12)',
14
+ width: 'max-content',
15
+ p: 24
16
+ }), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9EYXRlUGlja2VyL0NhbGVuZGFyL0NhbGVuZGFyLnRzeCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFPd0IiLCJmaWxlIjoiLi4vLi4vLi4vc3JjL0RhdGVQaWNrZXIvQ2FsZW5kYXIvQ2FsZW5kYXIudHN4Iiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGNvZGVjYWRlbXkvZ2FtdXQtc3R5bGVzJztcbmltcG9ydCBzdHlsZWQgZnJvbSAnQGVtb3Rpb24vc3R5bGVkJztcblxuLyoqXG4gKiBPdXRlciB3cmFwcGVyIGZvciB0aGUgY2FsZW5kYXIgKGhlYWRlciArIGJvZHkgKyBmb290ZXIpLlxuICogVXNlZCBieSBEYXRlUGlja2VyQ2FsZW5kYXIgdG8gZ3JvdXAgdGhlIGNhbGVuZGFyIGNvbnRlbnQuXG4gKi9cbmV4cG9ydCBjb25zdCBDYWxlbmRhciA9IHN0eWxlZC5kaXYoXG4gIGNzcyh7XG4gICAgYmFja2dyb3VuZENvbG9yOiAnYmFja2dyb3VuZCcsXG4gICAgYm9yZGVyUmFkaXVzOiAnbGcnLFxuICAgIGJveFNoYWRvdzogJzAgNHB4IDE2cHggcmdiYSgwLCAwLCAwLCAwLjEyKScsXG4gICAgd2lkdGg6ICdtYXgtY29udGVudCcsXG4gICAgcDogMjQsXG4gIH0pXG4pO1xuIl19 */");
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ import { CalendarBodyProps } from './types';
3
+ export declare const CalendarBody: React.FC<CalendarBodyProps>;
@@ -0,0 +1,219 @@
1
+ import _styled from "@emotion/styled/base";
2
+ import { states } from '@codecademy/gamut-styles';
3
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
4
+ import * as React from 'react';
5
+ import { TextButton } from '../../Button';
6
+ import { clampToMonth, getMonthGrid, isDateDisabled, isDateInRange, isSameDay } from './utils/dateGrid';
7
+ import { getWeekdayFullNames, getWeekdayLabels } from './utils/format';
8
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
+ const DateButton = /*#__PURE__*/_styled(TextButton, {
10
+ target: "e12sl4cx0",
11
+ label: "DateButton"
12
+ })(states({
13
+ isToday: {
14
+ bg: 'navy-200'
15
+ },
16
+ isSelected: {
17
+ color: 'background',
18
+ bg: 'text'
19
+ },
20
+ isInRange: {
21
+ bg: 'border-secondary'
22
+ }
23
+ }), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9EYXRlUGlja2VyL0NhbGVuZGFyL0NhbGVuZGFyQm9keS50c3giXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBZ0JtQiIsImZpbGUiOiIuLi8uLi8uLi9zcmMvRGF0ZVBpY2tlci9DYWxlbmRhci9DYWxlbmRhckJvZHkudHN4Iiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgc3RhdGVzIH0gZnJvbSAnQGNvZGVjYWRlbXkvZ2FtdXQtc3R5bGVzJztcbmltcG9ydCBzdHlsZWQgZnJvbSAnQGVtb3Rpb24vc3R5bGVkJztcbmltcG9ydCB7IHVzZUNhbGxiYWNrLCB1c2VFZmZlY3QsIHVzZU1lbW8sIHVzZVJlZiB9IGZyb20gJ3JlYWN0JztcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0JztcblxuaW1wb3J0IHsgVGV4dEJ1dHRvbiB9IGZyb20gJy4uLy4uL0J1dHRvbic7XG5pbXBvcnQgeyBDYWxlbmRhckJvZHlQcm9wcyB9IGZyb20gJy4vdHlwZXMnO1xuaW1wb3J0IHtcbiAgY2xhbXBUb01vbnRoLFxuICBnZXRNb250aEdyaWQsXG4gIGlzRGF0ZURpc2FibGVkLFxuICBpc0RhdGVJblJhbmdlLFxuICBpc1NhbWVEYXksXG59IGZyb20gJy4vdXRpbHMvZGF0ZUdyaWQnO1xuaW1wb3J0IHsgZ2V0V2Vla2RheUZ1bGxOYW1lcywgZ2V0V2Vla2RheUxhYmVscyB9IGZyb20gJy4vdXRpbHMvZm9ybWF0JztcblxuY29uc3QgRGF0ZUJ1dHRvbiA9IHN0eWxlZChUZXh0QnV0dG9uKShcbiAgc3RhdGVzKHtcbiAgICBpc1RvZGF5OiB7XG4gICAgICBiZzogJ25hdnktMjAwJyxcbiAgICB9LFxuICAgIGlzU2VsZWN0ZWQ6IHtcbiAgICAgIGNvbG9yOiAnYmFja2dyb3VuZCcsXG4gICAgICBiZzogJ3RleHQnLFxuICAgIH0sXG4gICAgaXNJblJhbmdlOiB7XG4gICAgICBiZzogJ2JvcmRlci1zZWNvbmRhcnknLFxuICAgIH0sXG4gIH0pXG4pO1xuXG4vKiogRmxhdCBsaXN0IG9mIGRhdGVzIGluIGdyaWQgb3JkZXIgKHJvdy1tYWpvciwgbm9uLW51bGwgb25seSkgd2l0aCByb3cgaW5kZXggZm9yIEhvbWUvRW5kICovXG5mdW5jdGlvbiBnZXREYXRlc1dpdGhSb3coXG4gIHdlZWtzOiAoRGF0ZSB8IG51bGwpW11bXVxuKTogeyBkYXRlOiBEYXRlOyByb3dJbmRleDogbnVtYmVyIH1bXSB7XG4gIGNvbnN0IHJlc3VsdDogeyBkYXRlOiBEYXRlOyByb3dJbmRleDogbnVtYmVyIH1bXSA9IFtdO1xuICB3ZWVrcy5mb3JFYWNoKCh3ZWVrLCByb3dJbmRleCkgPT4ge1xuICAgIHdlZWsuZm9yRWFjaCgoZGF0ZSkgPT4ge1xuICAgICAgaWYgKGRhdGUgIT09IG51bGwpIHJlc3VsdC5wdXNoKHsgZGF0ZSwgcm93SW5kZXggfSk7XG4gICAgfSk7XG4gIH0pO1xuICByZXR1cm4gcmVzdWx0O1xufVxuXG5leHBvcnQgY29uc3QgQ2FsZW5kYXJCb2R5OiBSZWFjdC5GQzxDYWxlbmRhckJvZHlQcm9wcz4gPSAoe1xuICB2aXNpYmxlRGF0ZSxcbiAgc2VsZWN0ZWREYXRlLFxuICBlbmREYXRlID0gbnVsbCxcbiAgZGlzYWJsZWREYXRlcyA9IFtdLFxuICBvbkRhdGVTZWxlY3QsXG4gIGxvY2FsZSxcbiAgd2Vla1N0YXJ0c09uID0gMCxcbiAgbGFiZWxsZWRCeUlkLFxuICBmb2N1c2VkRGF0ZSxcbiAgb25Gb2N1c2VkRGF0ZUNoYW5nZSxcbiAgb25WaXNpYmxlRGF0ZUNoYW5nZSxcbiAgb25Fc2NhcGVLZXlQcmVzcyxcbn0pID0+IHtcbiAgY29uc3QgeWVhciA9IHZpc2libGVEYXRlLmdldEZ1bGxZZWFyKCk7XG4gIGNvbnN0IG1vbnRoID0gdmlzaWJsZURhdGUuZ2V0TW9udGgoKTtcbiAgY29uc3Qgd2Vla3MgPSBnZXRNb250aEdyaWQoeWVhciwgbW9udGgsIHdlZWtTdGFydHNPbik7XG4gIGNvbnN0IHdlZWtkYXlMYWJlbHMgPSBnZXRXZWVrZGF5TGFiZWxzKGxvY2FsZSwgd2Vla1N0YXJ0c09uKTtcbiAgY29uc3Qgd2Vla2RheUZ1bGxOYW1lcyA9IGdldFdlZWtkYXlGdWxsTmFtZXMobG9jYWxlLCB3ZWVrU3RhcnRzT24pO1xuICBjb25zdCBidXR0b25SZWZzID0gdXNlUmVmPE1hcDxudW1iZXIsIEhUTUxFbGVtZW50Pj4obmV3IE1hcCgpKTtcblxuICBjb25zdCBkYXRlc1dpdGhSb3cgPSB1c2VNZW1vKCgpID0+IGdldERhdGVzV2l0aFJvdyh3ZWVrcyksIFt3ZWVrc10pO1xuICBjb25zdCBmb2N1c1RhcmdldCA9IGZvY3VzZWREYXRlID8/IHNlbGVjdGVkRGF0ZTtcblxuICBjb25zdCBpc1RvZGF5ID0gdXNlQ2FsbGJhY2soXG4gICAgKGQ6IERhdGUgfCBudWxsKSA9PiBkICE9PSBudWxsICYmIGlzU2FtZURheShkLCBuZXcgRGF0ZSgpKSxcbiAgICBbXVxuICApO1xuXG4gIGNvbnN0IGZvY3VzQnV0dG9uID0gdXNlQ2FsbGJhY2soKGRhdGU6IERhdGUgfCBudWxsKSA9PiB7XG4gICAgaWYgKGRhdGUgPT09IG51bGwpIHJldHVybjtcbiAgICBjb25zdCBrZXkgPSBuZXcgRGF0ZShcbiAgICAgIGRhdGUuZ2V0RnVsbFllYXIoKSxcbiAgICAgIGRhdGUuZ2V0TW9udGgoKSxcbiAgICAgIGRhdGUuZ2V0RGF0ZSgpXG4gICAgKS5nZXRUaW1lKCk7XG4gICAgYnV0dG9uUmVmcy5jdXJyZW50LmdldChrZXkpPy5mb2N1cygpO1xuICB9LCBbXSk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZm9jdXNUYXJnZXQgIT09IG51bGwpIGZvY3VzQnV0dG9uKGZvY3VzVGFyZ2V0KTtcbiAgfSwgW2ZvY3VzVGFyZ2V0LCBmb2N1c0J1dHRvbl0pO1xuXG4gIGNvbnN0IGhhbmRsZUtleURvd24gPSB1c2VDYWxsYmFjayhcbiAgICAoZTogUmVhY3QuS2V5Ym9hcmRFdmVudCwgZGF0ZTogRGF0ZSkgPT4ge1xuICAgICAgaWYgKCFvbkZvY3VzZWREYXRlQ2hhbmdlKSByZXR1cm47XG4gICAgICBjb25zdCBrZXkgPSBkYXRlLmdldFRpbWUoKTtcbiAgICAgIGNvbnN0IGlkeCA9IGRhdGVzV2l0aFJvdy5maW5kSW5kZXgoKHsgZGF0ZTogZCB9KSA9PiBkLmdldFRpbWUoKSA9PT0ga2V5KTtcbiAgICAgIGlmIChpZHggPCAwKSByZXR1cm47XG5cbiAgICAgIGNvbnN0IGN1cnJlbnRSb3cgPSBkYXRlc1dpdGhSb3dbaWR4XS5yb3dJbmRleDtcbiAgICAgIGNvbnN0IGRheSA9IGRhdGUuZ2V0RGF0ZSgpO1xuXG4gICAgICBsZXQgbmV3RGF0ZTogRGF0ZSB8IG51bGwgPSBudWxsO1xuICAgICAgbGV0IG5ld1Zpc2libGVEYXRlOiBEYXRlIHwgbnVsbCA9IG51bGw7XG5cbiAgICAgIHN3aXRjaCAoZS5rZXkpIHtcbiAgICAgICAgY2FzZSAnQXJyb3dMZWZ0JzpcbiAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgaWYgKGlkeCA+IDApIHtcbiAgICAgICAgICAgIG5ld0RhdGUgPSBkYXRlc1dpdGhSb3dbaWR4IC0gMV0uZGF0ZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgbGFzdERheVByZXZNb250aCA9IG5ldyBEYXRlKHllYXIsIG1vbnRoLCAwKTtcbiAgICAgICAgICAgIG5ld0RhdGUgPSBsYXN0RGF5UHJldk1vbnRoO1xuICAgICAgICAgICAgbmV3VmlzaWJsZURhdGUgPSBuZXcgRGF0ZSh5ZWFyLCBtb250aCAtIDEsIDEpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnQXJyb3dSaWdodCc6XG4gICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIGlmIChpZHggPCBkYXRlc1dpdGhSb3cubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgbmV3RGF0ZSA9IGRhdGVzV2l0aFJvd1tpZHggKyAxXS5kYXRlO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBuZXdEYXRlID0gbmV3IERhdGUoeWVhciwgbW9udGggKyAxLCAxKTtcbiAgICAgICAgICAgIG5ld1Zpc2libGVEYXRlID0gbmV3IERhdGUoeWVhciwgbW9udGggKyAxLCAxKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ0Fycm93VXAnOlxuICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICBuZXdEYXRlID0gbmV3IERhdGUoZGF0ZSk7XG4gICAgICAgICAgbmV3RGF0ZS5zZXREYXRlKG5ld0RhdGUuZ2V0RGF0ZSgpIC0gNyk7XG4gICAgICAgICAgaWYgKG5ld0RhdGUuZ2V0TW9udGgoKSAhPT0gbW9udGggfHwgbmV3RGF0ZS5nZXRGdWxsWWVhcigpICE9PSB5ZWFyKSB7XG4gICAgICAgICAgICBuZXdWaXNpYmxlRGF0ZSA9IG5ldyBEYXRlKFxuICAgICAgICAgICAgICBuZXdEYXRlLmdldEZ1bGxZZWFyKCksXG4gICAgICAgICAgICAgIG5ld0RhdGUuZ2V0TW9udGgoKSxcbiAgICAgICAgICAgICAgMVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ0Fycm93RG93bic6XG4gICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIG5ld0RhdGUgPSBuZXcgRGF0ZShkYXRlKTtcbiAgICAgICAgICBuZXdEYXRlLnNldERhdGUobmV3RGF0ZS5nZXREYXRlKCkgKyA3KTtcbiAgICAgICAgICBpZiAobmV3RGF0ZS5nZXRNb250aCgpICE9PSBtb250aCB8fCBuZXdEYXRlLmdldEZ1bGxZZWFyKCkgIT09IHllYXIpIHtcbiAgICAgICAgICAgIG5ld1Zpc2libGVEYXRlID0gbmV3IERhdGUoXG4gICAgICAgICAgICAgIG5ld0RhdGUuZ2V0RnVsbFllYXIoKSxcbiAgICAgICAgICAgICAgbmV3RGF0ZS5nZXRNb250aCgpLFxuICAgICAgICAgICAgICAxXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnSG9tZSc6XG4gICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIG5ld0RhdGUgPVxuICAgICAgICAgICAgZGF0ZXNXaXRoUm93LmZpbmQoKHsgcm93SW5kZXggfSkgPT4gcm93SW5kZXggPT09IGN1cnJlbnRSb3cpXG4gICAgICAgICAgICAgID8uZGF0ZSA/PyBkYXRlO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdFbmQnOlxuICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICBuZXdEYXRlID1cbiAgICAgICAgICAgIFsuLi5kYXRlc1dpdGhSb3ddXG4gICAgICAgICAgICAgIC5yZXZlcnNlKClcbiAgICAgICAgICAgICAgLmZpbmQoKHsgcm93SW5kZXggfSkgPT4gcm93SW5kZXggPT09IGN1cnJlbnRSb3cpPy5kYXRlID8/IGRhdGU7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ1BhZ2VEb3duJzpcbiAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgaWYgKGUuc2hpZnRLZXkpIHtcbiAgICAgICAgICAgIG5ld0RhdGUgPSBjbGFtcFRvTW9udGgoeWVhciArIDEsIG1vbnRoLCBkYXkpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBuZXdEYXRlID0gY2xhbXBUb01vbnRoKHllYXIsIG1vbnRoICsgMSwgZGF5KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgbmV3VmlzaWJsZURhdGUgPSBuZXcgRGF0ZShcbiAgICAgICAgICAgIG5ld0RhdGUuZ2V0RnVsbFllYXIoKSxcbiAgICAgICAgICAgIG5ld0RhdGUuZ2V0TW9udGgoKSxcbiAgICAgICAgICAgIDFcbiAgICAgICAgICApO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdQYWdlVXAnOlxuICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICBpZiAoZS5zaGlmdEtleSkge1xuICAgICAgICAgICAgbmV3RGF0ZSA9IGNsYW1wVG9Nb250aCh5ZWFyIC0gMSwgbW9udGgsIGRheSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG5ld0RhdGUgPSBjbGFtcFRvTW9udGgoeWVhciwgbW9udGggLSAxLCBkYXkpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBuZXdWaXNpYmxlRGF0ZSA9IG5ldyBEYXRlKFxuICAgICAgICAgICAgbmV3RGF0ZS5nZXRGdWxsWWVhcigpLFxuICAgICAgICAgICAgbmV3RGF0ZS5nZXRNb250aCgpLFxuICAgICAgICAgICAgMVxuICAgICAgICAgICk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ0VudGVyJzpcbiAgICAgICAgY2FzZSAnICc6XG4gICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIGlmICghaXNEYXRlRGlzYWJsZWQoZGF0ZSwgZGlzYWJsZWREYXRlcykpIG9uRGF0ZVNlbGVjdChkYXRlKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNhc2UgJ0VzY2FwZSc6XG4gICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIG9uRXNjYXBlS2V5UHJlc3M/LigpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmIChuZXdEYXRlICE9PSBudWxsKSB7XG4gICAgICAgIG9uRm9jdXNlZERhdGVDaGFuZ2UobmV3RGF0ZSk7XG4gICAgICAgIGlmIChuZXdWaXNpYmxlRGF0ZSAhPT0gbnVsbCkgb25WaXNpYmxlRGF0ZUNoYW5nZT8uKG5ld1Zpc2libGVEYXRlKTtcbiAgICAgIH1cbiAgICB9LFxuICAgIFtcbiAgICAgIG9uRm9jdXNlZERhdGVDaGFuZ2UsXG4gICAgICBkYXRlc1dpdGhSb3csXG4gICAgICBtb250aCxcbiAgICAgIHllYXIsXG4gICAgICBkaXNhYmxlZERhdGVzLFxuICAgICAgb25EYXRlU2VsZWN0LFxuICAgICAgb25Fc2NhcGVLZXlQcmVzcyxcbiAgICAgIG9uVmlzaWJsZURhdGVDaGFuZ2UsXG4gICAgXVxuICApO1xuXG4gIGNvbnN0IHNldEJ1dHRvblJlZiA9IHVzZUNhbGxiYWNrKChkYXRlOiBEYXRlLCBlbDogSFRNTEVsZW1lbnQgfCBudWxsKSA9PiB7XG4gICAgY29uc3QgayA9IG5ldyBEYXRlKFxuICAgICAgZGF0ZS5nZXRGdWxsWWVhcigpLFxuICAgICAgZGF0ZS5nZXRNb250aCgpLFxuICAgICAgZGF0ZS5nZXREYXRlKClcbiAgICApLmdldFRpbWUoKTtcbiAgICBpZiAoZWwpIGJ1dHRvblJlZnMuY3VycmVudC5zZXQoaywgZWwpO1xuICAgIGVsc2UgYnV0dG9uUmVmcy5jdXJyZW50LmRlbGV0ZShrKTtcbiAgfSwgW10pO1xuXG4gIHJldHVybiAoXG4gICAgPHRhYmxlIGFyaWEtbGFiZWxsZWRieT17bGFiZWxsZWRCeUlkfSByb2xlPVwiZ3JpZFwiIHdpZHRoPVwiMTAwJVwiPlxuICAgICAgPHRoZWFkPlxuICAgICAgICA8dHI+XG4gICAgICAgICAge3dlZWtkYXlMYWJlbHMubWFwKChsYWJlbCwgaSkgPT4gKFxuICAgICAgICAgICAgPHRoIGFiYnI9e3dlZWtkYXlGdWxsTmFtZXNbaV19IGtleT17bGFiZWx9IHNjb3BlPVwiY29sXCI+XG4gICAgICAgICAgICAgIHtsYWJlbH1cbiAgICAgICAgICAgIDwvdGg+XG4gICAgICAgICAgKSl9XG4gICAgICAgIDwvdHI+XG4gICAgICA8L3RoZWFkPlxuICAgICAgPHRib2R5PlxuICAgICAgICB7d2Vla3MubWFwKCh3ZWVrLCByb3dJbmRleCkgPT4gKFxuICAgICAgICAgIDx0ciBrZXk9e3dlZWsuam9pbignLScpfT5cbiAgICAgICAgICAgIHt3ZWVrLm1hcCgoZGF0ZSwgY29sSW5kZXgpID0+IHtcbiAgICAgICAgICAgICAgaWYgKGRhdGUgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICAgICAgLy8gZml4IHRoaXMgZXJyb3JcbiAgICAgICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC9uby1hcnJheS1pbmRleC1rZXksIGpzeC1hMTF5L2NvbnRyb2wtaGFzLWFzc29jaWF0ZWQtbGFiZWxcbiAgICAgICAgICAgICAgICAgIDx0ZCBrZXk9e2BlbXB0eS0ke3Jvd0luZGV4fS0ke2NvbEluZGV4fWB9IHJvbGU9XCJncmlkY2VsbFwiIC8+XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBzZWxlY3RlZCA9XG4gICAgICAgICAgICAgICAgaXNTYW1lRGF5KGRhdGUsIHNlbGVjdGVkRGF0ZSkgfHwgaXNTYW1lRGF5KGRhdGUsIGVuZERhdGUpO1xuICAgICAgICAgICAgICBjb25zdCBpblJhbmdlID1cbiAgICAgICAgICAgICAgICAoc2VsZWN0ZWREYXRlICE9PSBudWxsIHx8IGVuZERhdGUgIT09IG51bGwpICYmXG4gICAgICAgICAgICAgICAgaXNEYXRlSW5SYW5nZShkYXRlLCBzZWxlY3RlZERhdGUsIGVuZERhdGUpO1xuICAgICAgICAgICAgICBjb25zdCBkaXNhYmxlZCA9IGlzRGF0ZURpc2FibGVkKGRhdGUsIGRpc2FibGVkRGF0ZXMpO1xuICAgICAgICAgICAgICBjb25zdCB0b2RheSA9IGlzVG9kYXkoZGF0ZSk7XG4gICAgICAgICAgICAgIGNvbnN0IGlzRm9jdXNlZCA9XG4gICAgICAgICAgICAgICAgZm9jdXNUYXJnZXQgIT09IG51bGwgJiYgaXNTYW1lRGF5KGRhdGUsIGZvY3VzVGFyZ2V0KTtcblxuICAgICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICAgIDx0ZFxuICAgICAgICAgICAgICAgICAgYXJpYS1zZWxlY3RlZD17c2VsZWN0ZWR9XG4gICAgICAgICAgICAgICAgICBrZXk9e2RhdGUuZ2V0VGltZSgpfVxuICAgICAgICAgICAgICAgICAgcm9sZT1cImdyaWRjZWxsXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICA8RGF0ZUJ1dHRvblxuICAgICAgICAgICAgICAgICAgICBkaXNhYmxlZD17ZGlzYWJsZWR9XG4gICAgICAgICAgICAgICAgICAgIGlzSW5SYW5nZT17aW5SYW5nZX1cbiAgICAgICAgICAgICAgICAgICAgaXNTZWxlY3RlZD17c2VsZWN0ZWR9XG4gICAgICAgICAgICAgICAgICAgIGlzVG9kYXk9e3RvZGF5fVxuICAgICAgICAgICAgICAgICAgICByZWY9eyhlbCkgPT4gc2V0QnV0dG9uUmVmKGRhdGUsIGVsIGFzIEhUTUxFbGVtZW50IHwgbnVsbCl9XG4gICAgICAgICAgICAgICAgICAgIHRhYkluZGV4PXtpc0ZvY3VzZWQgPyAwIDogLTF9XG4gICAgICAgICAgICAgICAgICAgIHZhcmlhbnQ9XCJzZWNvbmRhcnlcIlxuICAgICAgICAgICAgICAgICAgICB3aWR0aD1cIjM2cHhcIlxuICAgICAgICAgICAgICAgICAgICBvbkNsaWNrPXsoKSA9PiBvbkRhdGVTZWxlY3QoZGF0ZSl9XG4gICAgICAgICAgICAgICAgICAgIG9uRm9jdXM9eygpID0+IG9uRm9jdXNlZERhdGVDaGFuZ2U/LihkYXRlKX1cbiAgICAgICAgICAgICAgICAgICAgb25LZXlEb3duPXsoZTogUmVhY3QuS2V5Ym9hcmRFdmVudCkgPT5cbiAgICAgICAgICAgICAgICAgICAgICBoYW5kbGVLZXlEb3duKGUsIGRhdGUpXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAge2RhdGUuZ2V0RGF0ZSgpfVxuICAgICAgICAgICAgICAgICAgPC9EYXRlQnV0dG9uPlxuICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9KX1cbiAgICAgICAgICA8L3RyPlxuICAgICAgICApKX1cbiAgICAgIDwvdGJvZHk+XG4gICAgPC90YWJsZT5cbiAgKTtcbn07XG4iXX0= */");
24
+
25
+ /** Flat list of dates in grid order (row-major, non-null only) with row index for Home/End */
26
+ function getDatesWithRow(weeks) {
27
+ const result = [];
28
+ weeks.forEach((week, rowIndex) => {
29
+ week.forEach(date => {
30
+ if (date !== null) result.push({
31
+ date,
32
+ rowIndex
33
+ });
34
+ });
35
+ });
36
+ return result;
37
+ }
38
+ export const CalendarBody = ({
39
+ visibleDate,
40
+ selectedDate,
41
+ endDate = null,
42
+ disabledDates = [],
43
+ onDateSelect,
44
+ locale,
45
+ weekStartsOn = 0,
46
+ labelledById,
47
+ focusedDate,
48
+ onFocusedDateChange,
49
+ onVisibleDateChange,
50
+ onEscapeKeyPress
51
+ }) => {
52
+ const year = visibleDate.getFullYear();
53
+ const month = visibleDate.getMonth();
54
+ const weeks = getMonthGrid(year, month, weekStartsOn);
55
+ const weekdayLabels = getWeekdayLabels(locale, weekStartsOn);
56
+ const weekdayFullNames = getWeekdayFullNames(locale, weekStartsOn);
57
+ const buttonRefs = useRef(new Map());
58
+ const datesWithRow = useMemo(() => getDatesWithRow(weeks), [weeks]);
59
+ const focusTarget = focusedDate ?? selectedDate;
60
+ const isToday = useCallback(d => d !== null && isSameDay(d, new Date()), []);
61
+ const focusButton = useCallback(date => {
62
+ if (date === null) return;
63
+ const key = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
64
+ buttonRefs.current.get(key)?.focus();
65
+ }, []);
66
+ useEffect(() => {
67
+ if (focusTarget !== null) focusButton(focusTarget);
68
+ }, [focusTarget, focusButton]);
69
+ const handleKeyDown = useCallback((e, date) => {
70
+ if (!onFocusedDateChange) return;
71
+ const key = date.getTime();
72
+ const idx = datesWithRow.findIndex(({
73
+ date: d
74
+ }) => d.getTime() === key);
75
+ if (idx < 0) return;
76
+ const currentRow = datesWithRow[idx].rowIndex;
77
+ const day = date.getDate();
78
+ let newDate = null;
79
+ let newVisibleDate = null;
80
+ switch (e.key) {
81
+ case 'ArrowLeft':
82
+ e.preventDefault();
83
+ if (idx > 0) {
84
+ newDate = datesWithRow[idx - 1].date;
85
+ } else {
86
+ const lastDayPrevMonth = new Date(year, month, 0);
87
+ newDate = lastDayPrevMonth;
88
+ newVisibleDate = new Date(year, month - 1, 1);
89
+ }
90
+ break;
91
+ case 'ArrowRight':
92
+ e.preventDefault();
93
+ if (idx < datesWithRow.length - 1) {
94
+ newDate = datesWithRow[idx + 1].date;
95
+ } else {
96
+ newDate = new Date(year, month + 1, 1);
97
+ newVisibleDate = new Date(year, month + 1, 1);
98
+ }
99
+ break;
100
+ case 'ArrowUp':
101
+ e.preventDefault();
102
+ newDate = new Date(date);
103
+ newDate.setDate(newDate.getDate() - 7);
104
+ if (newDate.getMonth() !== month || newDate.getFullYear() !== year) {
105
+ newVisibleDate = new Date(newDate.getFullYear(), newDate.getMonth(), 1);
106
+ }
107
+ break;
108
+ case 'ArrowDown':
109
+ e.preventDefault();
110
+ newDate = new Date(date);
111
+ newDate.setDate(newDate.getDate() + 7);
112
+ if (newDate.getMonth() !== month || newDate.getFullYear() !== year) {
113
+ newVisibleDate = new Date(newDate.getFullYear(), newDate.getMonth(), 1);
114
+ }
115
+ break;
116
+ case 'Home':
117
+ e.preventDefault();
118
+ newDate = datesWithRow.find(({
119
+ rowIndex
120
+ }) => rowIndex === currentRow)?.date ?? date;
121
+ break;
122
+ case 'End':
123
+ e.preventDefault();
124
+ newDate = [...datesWithRow].reverse().find(({
125
+ rowIndex
126
+ }) => rowIndex === currentRow)?.date ?? date;
127
+ break;
128
+ case 'PageDown':
129
+ e.preventDefault();
130
+ if (e.shiftKey) {
131
+ newDate = clampToMonth(year + 1, month, day);
132
+ } else {
133
+ newDate = clampToMonth(year, month + 1, day);
134
+ }
135
+ newVisibleDate = new Date(newDate.getFullYear(), newDate.getMonth(), 1);
136
+ break;
137
+ case 'PageUp':
138
+ e.preventDefault();
139
+ if (e.shiftKey) {
140
+ newDate = clampToMonth(year - 1, month, day);
141
+ } else {
142
+ newDate = clampToMonth(year, month - 1, day);
143
+ }
144
+ newVisibleDate = new Date(newDate.getFullYear(), newDate.getMonth(), 1);
145
+ break;
146
+ case 'Enter':
147
+ case ' ':
148
+ e.preventDefault();
149
+ if (!isDateDisabled(date, disabledDates)) onDateSelect(date);
150
+ return;
151
+ case 'Escape':
152
+ e.preventDefault();
153
+ onEscapeKeyPress?.();
154
+ return;
155
+ default:
156
+ return;
157
+ }
158
+ if (newDate !== null) {
159
+ onFocusedDateChange(newDate);
160
+ if (newVisibleDate !== null) onVisibleDateChange?.(newVisibleDate);
161
+ }
162
+ }, [onFocusedDateChange, datesWithRow, month, year, disabledDates, onDateSelect, onEscapeKeyPress, onVisibleDateChange]);
163
+ const setButtonRef = useCallback((date, el) => {
164
+ const k = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
165
+ if (el) buttonRefs.current.set(k, el);else buttonRefs.current.delete(k);
166
+ }, []);
167
+ return /*#__PURE__*/_jsxs("table", {
168
+ "aria-labelledby": labelledById,
169
+ role: "grid",
170
+ width: "100%",
171
+ children: [/*#__PURE__*/_jsx("thead", {
172
+ children: /*#__PURE__*/_jsx("tr", {
173
+ children: weekdayLabels.map((label, i) => /*#__PURE__*/_jsx("th", {
174
+ abbr: weekdayFullNames[i],
175
+ scope: "col",
176
+ children: label
177
+ }, label))
178
+ })
179
+ }), /*#__PURE__*/_jsx("tbody", {
180
+ children: weeks.map((week, rowIndex) => /*#__PURE__*/_jsx("tr", {
181
+ children: week.map((date, colIndex) => {
182
+ if (date === null) {
183
+ return (
184
+ /*#__PURE__*/
185
+ // fix this error
186
+ // eslint-disable-next-line react/no-array-index-key, jsx-a11y/control-has-associated-label
187
+ _jsx("td", {
188
+ role: "gridcell"
189
+ }, `empty-${rowIndex}-${colIndex}`)
190
+ );
191
+ }
192
+ const selected = isSameDay(date, selectedDate) || isSameDay(date, endDate);
193
+ const inRange = (selectedDate !== null || endDate !== null) && isDateInRange(date, selectedDate, endDate);
194
+ const disabled = isDateDisabled(date, disabledDates);
195
+ const today = isToday(date);
196
+ const isFocused = focusTarget !== null && isSameDay(date, focusTarget);
197
+ return /*#__PURE__*/_jsx("td", {
198
+ "aria-selected": selected,
199
+ role: "gridcell",
200
+ children: /*#__PURE__*/_jsx(DateButton, {
201
+ disabled: disabled,
202
+ isInRange: inRange,
203
+ isSelected: selected,
204
+ isToday: today,
205
+ ref: el => setButtonRef(date, el),
206
+ tabIndex: isFocused ? 0 : -1,
207
+ variant: "secondary",
208
+ width: "36px",
209
+ onClick: () => onDateSelect(date),
210
+ onFocus: () => onFocusedDateChange?.(date),
211
+ onKeyDown: e => handleKeyDown(e, date),
212
+ children: date.getDate()
213
+ })
214
+ }, date.getTime());
215
+ })
216
+ }, week.join('-')))
217
+ })]
218
+ });
219
+ };
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ import { CalendarFooterProps } from './types';
3
+ export declare const CalendarFooter: React.FC<CalendarFooterProps>;
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import { FlexBox } from '../../Box';
3
+ import { TextButton } from '../../Button';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ // function formatQuickActionLabel(action: QuickAction): string {
6
+ // const { num, timePeriod } = action;
7
+ // const period =
8
+ // timePeriod === 'day'
9
+ // ? num === 1
10
+ // ? 'day'
11
+ // : 'days'
12
+ // : timePeriod === 'week'
13
+ // ? num === 1
14
+ // ? 'week'
15
+ // : 'weeks'
16
+ // : timePeriod === 'month'
17
+ // ? num === 1
18
+ // ? 'month'
19
+ // : 'months'
20
+ // : num === 1
21
+ // ? 'year'
22
+ // : 'years';
23
+ // return `${num} ${period}`;
24
+ // }
25
+
26
+ export const CalendarFooter = ({
27
+ onClearDate,
28
+ onTodayClick,
29
+ onSelectedDateChange,
30
+ onCurrentMonthYearChange
31
+ }) => {
32
+ const handleClearDate = () => {
33
+ onSelectedDateChange(null);
34
+ onClearDate?.();
35
+ };
36
+ const handleTodayClick = () => {
37
+ const today = new Date();
38
+ onSelectedDateChange(today);
39
+ onCurrentMonthYearChange(new Date(today.getFullYear(), today.getMonth(), 1));
40
+ onTodayClick?.();
41
+ };
42
+ // const actions = quickActions.slice(0, 3);
43
+
44
+ return /*#__PURE__*/_jsxs(FlexBox, {
45
+ alignItems: "center",
46
+ borderTop: 1,
47
+ justifyContent: "space-between",
48
+ py: 12,
49
+ children: [/*#__PURE__*/_jsx(TextButton, {
50
+ onClick: handleClearDate,
51
+ children: "Clear"
52
+ }), /*#__PURE__*/_jsx(FlexBox, {
53
+ gap: 32,
54
+ children: /*#__PURE__*/_jsx(TextButton, {
55
+ onClick: handleTodayClick,
56
+ children: "Today"
57
+ })
58
+ })]
59
+ });
60
+ };
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ import { CalendarHeaderProps } from './types';
3
+ export declare const CalendarHeader: React.FC<CalendarHeaderProps>;
@@ -0,0 +1,51 @@
1
+ import { MiniChevronLeftIcon, MiniChevronRightIcon } from '@codecademy/gamut-icons';
2
+ import * as React from 'react';
3
+ import { FlexBox } from '../../Box';
4
+ import { IconButton } from '../../Button';
5
+ import { Text } from '../../Typography';
6
+ import { formatMonthYear } from './utils/format';
7
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
+ export const CalendarHeader = ({
9
+ currentMonthYear,
10
+ onCurrentMonthYearChange,
11
+ onPreviousMonthClick,
12
+ onNextMonthClick,
13
+ locale,
14
+ headingId
15
+ }) => {
16
+ const monthYear = formatMonthYear(currentMonthYear, locale);
17
+ const handlePreviousMonth = () => {
18
+ const previousMonth = new Date(currentMonthYear.getFullYear(), currentMonthYear.getMonth() - 1, 1);
19
+ onCurrentMonthYearChange?.(previousMonth);
20
+ onPreviousMonthClick?.();
21
+ };
22
+ const handleNextMonth = () => {
23
+ const nextMonth = new Date(currentMonthYear.getFullYear(), currentMonthYear.getMonth() + 1, 1);
24
+ onCurrentMonthYearChange?.(nextMonth);
25
+ onNextMonthClick?.();
26
+ };
27
+ return /*#__PURE__*/_jsxs(FlexBox, {
28
+ alignItems: "center",
29
+ justifyContent: "space-between",
30
+ pb: 16,
31
+ children: [/*#__PURE__*/_jsx(IconButton, {
32
+ "aria-label": "Previous month",
33
+ icon: MiniChevronLeftIcon,
34
+ size: "small",
35
+ tip: "Previous month",
36
+ onClick: handlePreviousMonth
37
+ }), /*#__PURE__*/_jsx(Text, {
38
+ "aria-live": "polite",
39
+ as: "h2",
40
+ id: headingId,
41
+ variant: "title-sm",
42
+ children: monthYear
43
+ }), /*#__PURE__*/_jsx(IconButton, {
44
+ "aria-label": "Next month",
45
+ icon: MiniChevronRightIcon,
46
+ size: "small",
47
+ tip: "Next month",
48
+ onClick: handleNextMonth
49
+ })]
50
+ });
51
+ };
@@ -0,0 +1,6 @@
1
+ export { Calendar } from './Calendar';
2
+ export { CalendarHeader } from './CalendarHeader';
3
+ export { CalendarBody } from './CalendarBody';
4
+ export { CalendarFooter } from './CalendarFooter';
5
+ export type { CalendarHeaderProps, CalendarBodyProps, CalendarFooterProps, QuickAction, } from './types';
6
+ export * from './utils';
@@ -0,0 +1,5 @@
1
+ export { Calendar } from './Calendar';
2
+ export { CalendarHeader } from './CalendarHeader';
3
+ export { CalendarBody } from './CalendarBody';
4
+ export { CalendarFooter } from './CalendarFooter';
5
+ export * from './utils';
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Internal types for the Calendar subcomponents (used by DatePickerCalendar).
3
+ */
4
+ export interface CalendarHeaderProps {
5
+ /** Currently displayed month and year (used for heading and prev/next range) */
6
+ currentMonthYear: Date;
7
+ /** Called when the user navigates to a different month. Pass the new date (e.g. setVisibleDate) so the calendar updates. */
8
+ onCurrentMonthYearChange: (newDate: Date) => void;
9
+ /** Optional. Called after navigating to previous month; use for click tracking. */
10
+ onPreviousMonthClick?: () => void;
11
+ /** Optional. Called after navigating to next month; use for click tracking. */
12
+ onNextMonthClick?: () => void;
13
+ /** Locale for month/year formatting (e.g. 'en-US') */
14
+ locale?: string;
15
+ /** Optional id for the heading (for grid aria-labelledby) */
16
+ headingId?: string;
17
+ }
18
+ export interface CalendarBodyProps {
19
+ /** The month to display (typically first day of that month) */
20
+ visibleDate: Date;
21
+ /** Selected start date (single or range start) */
22
+ selectedDate: Date | null;
23
+ /** Selected end date (range only; null for single-date mode) */
24
+ endDate?: Date | null;
25
+ /** Dates that should be disabled (unselectable) */
26
+ disabledDates?: Date[];
27
+ /** Called when a date cell is selected */
28
+ onDateSelect: (date: Date) => void;
29
+ /** Locale for weekday names and week start */
30
+ locale?: string;
31
+ /** 0 = Sunday, 1 = Monday (default from locale if not set) */
32
+ weekStartsOn?: 0 | 1;
33
+ /** Id of the month/year heading (aria-labelledby on grid) */
34
+ labelledById?: string;
35
+ /** For keyboard nav: which cell has focus (roving tabindex) */
36
+ focusedDate?: Date | null;
37
+ /** Callback when focused date changes (e.g. arrow keys) */
38
+ onFocusedDateChange?: (date: Date | null) => void;
39
+ /** Called when grid keyboard nav changes month (e.g. Page Up/Down). Pass setVisibleDate so the calendar updates. */
40
+ onVisibleDateChange?: (newDate: Date) => void;
41
+ /** Called when the escape key is pressed */
42
+ onEscapeKeyPress?: () => void;
43
+ }
44
+ export interface QuickAction {
45
+ num: number;
46
+ timePeriod: 'day' | 'week' | 'month' | 'year';
47
+ onClick: () => void;
48
+ }
49
+ export interface CalendarFooterProps {
50
+ onClearDate?: () => void;
51
+ onTodayClick?: () => void;
52
+ /** Called when the user navigates to a different month. Pass the new date (e.g. setVisibleDate) so the calendar updates. */
53
+ onSelectedDateChange: (newDate: Date | null) => void;
54
+ onCurrentMonthYearChange: (newDate: Date) => void;
55
+ /** Max 3 quick actions (e.g. "7 days", "1 month") */
56
+ quickActions?: QuickAction[];
57
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Builds a grid of days for a calendar month using native Date and Intl.
3
+ * Each row has 7 cells; leading/trailing cells may be null (padding from adjacent months).
4
+ */
5
+ /**
6
+ * Get the weekday for a date (0 = Sunday, 6 = Saturday).
7
+ * Optionally use weekStartsOn to compute "offset" for display (e.g. Monday = 0).
8
+ */
9
+ export declare function getDayOfWeek(date: Date, weekStartsOn?: 0 | 1): number;
10
+ /**
11
+ * Returns an array of weeks for the given month. Each week is an array of 7 items:
12
+ * each item is either a Date (that day) or null (padding from previous/next month).
13
+ *
14
+ * @param year - Full year (e.g. 2026)
15
+ * @param month - Month 0-11 (0 = January)
16
+ * @param weekStartsOn - 0 = Sunday, 1 = Monday
17
+ */
18
+ export declare function getMonthGrid(year: number, month: number, weekStartsOn?: 0 | 1): (Date | null)[][];
19
+ /**
20
+ * Check if two dates are the same calendar day (ignoring time).
21
+ */
22
+ export declare function isSameDay(a: Date | null, b: Date | null): boolean;
23
+ /**
24
+ * Check if `date` is between `start` and `end` (inclusive), ignoring time.
25
+ */
26
+ export declare function isDateInRange(date: Date, start: Date | null, end: Date | null): boolean;
27
+ /**
28
+ * Check if `date` is in the `disabledDates` list (by calendar day).
29
+ */
30
+ export declare function isDateDisabled(date: Date, disabledDates?: Date[]): boolean;
31
+ /**
32
+ * Clamp a day to the last day of the given month (e.g. Jan 31 -> Feb 28).
33
+ */
34
+ export declare function clampToMonth(year: number, month: number, day: number): Date;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Builds a grid of days for a calendar month using native Date and Intl.
3
+ * Each row has 7 cells; leading/trailing cells may be null (padding from adjacent months).
4
+ */
5
+
6
+ const DAYS_PER_WEEK = 7;
7
+
8
+ /**
9
+ * Normalize to start of day in local time for comparison.
10
+ */
11
+ function toDateOnly(d) {
12
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
13
+ }
14
+
15
+ /**
16
+ * Get the weekday for a date (0 = Sunday, 6 = Saturday).
17
+ * Optionally use weekStartsOn to compute "offset" for display (e.g. Monday = 0).
18
+ */
19
+ export function getDayOfWeek(date, weekStartsOn = 0) {
20
+ const sundayBased = date.getDay();
21
+ if (weekStartsOn === 0) return sundayBased;
22
+ return (sundayBased + 6) % 7; // Monday = 0
23
+ }
24
+
25
+ /**
26
+ * Returns an array of weeks for the given month. Each week is an array of 7 items:
27
+ * each item is either a Date (that day) or null (padding from previous/next month).
28
+ *
29
+ * @param year - Full year (e.g. 2026)
30
+ * @param month - Month 0-11 (0 = January)
31
+ * @param weekStartsOn - 0 = Sunday, 1 = Monday
32
+ */
33
+ export function getMonthGrid(year, month, weekStartsOn = 0) {
34
+ const first = new Date(year, month, 1);
35
+ const last = new Date(year, month + 1, 0);
36
+ const firstDayOfWeek = getDayOfWeek(first, weekStartsOn);
37
+ const daysInMonth = last.getDate();
38
+ const weeks = [];
39
+ let currentWeek = [];
40
+
41
+ // Pad start of first week with nulls
42
+ // eslint-disable-next-line no-plusplus
43
+ for (let i = 0; i < firstDayOfWeek; i++) {
44
+ currentWeek.push(null);
45
+ }
46
+
47
+ // fix these
48
+ // eslint-disable-next-line no-plusplus
49
+ for (let day = 1; day <= daysInMonth; day++) {
50
+ currentWeek.push(new Date(year, month, day));
51
+ if (currentWeek.length === DAYS_PER_WEEK) {
52
+ weeks.push(currentWeek);
53
+ currentWeek = [];
54
+ }
55
+ }
56
+
57
+ // Pad end of last week with nulls
58
+ if (currentWeek.length > 0) {
59
+ while (currentWeek.length < DAYS_PER_WEEK) {
60
+ currentWeek.push(null);
61
+ }
62
+ weeks.push(currentWeek);
63
+ }
64
+ return weeks;
65
+ }
66
+
67
+ /**
68
+ * Check if two dates are the same calendar day (ignoring time).
69
+ */
70
+ export function isSameDay(a, b) {
71
+ if (a === null || b === null) return false;
72
+ return toDateOnly(a).getTime() === toDateOnly(b).getTime();
73
+ }
74
+
75
+ /**
76
+ * Check if `date` is between `start` and `end` (inclusive), ignoring time.
77
+ */
78
+ export function isDateInRange(date, start, end) {
79
+ if (start === null) return false;
80
+ const d = toDateOnly(date).getTime();
81
+ const s = toDateOnly(start).getTime();
82
+ const e = end !== null ? toDateOnly(end).getTime() : s;
83
+ const low = Math.min(s, e);
84
+ const high = Math.max(s, e);
85
+ return d >= low && d <= high;
86
+ }
87
+
88
+ /**
89
+ * Check if `date` is in the `disabledDates` list (by calendar day).
90
+ */
91
+ export function isDateDisabled(date, disabledDates = []) {
92
+ return disabledDates.some(d => isSameDay(date, d));
93
+ }
94
+
95
+ /**
96
+ * Clamp a day to the last day of the given month (e.g. Jan 31 -> Feb 28).
97
+ */
98
+ export function clampToMonth(year, month, day) {
99
+ const last = new Date(year, month + 1, 0).getDate();
100
+ return new Date(year, month, Math.min(day, last));
101
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Date formatting for the calendar using Intl.DateTimeFormat.
3
+ */
4
+ /**
5
+ * Format month and year for the calendar header (e.g. "February 2026").
6
+ */
7
+ export declare function formatMonthYear(date: Date, locale?: string): string;
8
+ /**
9
+ * Get short weekday labels for column headers (e.g. ["Su", "Mo", ...]).
10
+ * Order depends on weekStartsOn: 0 = Sunday first, 1 = Monday first.
11
+ */
12
+ export declare function getWeekdayLabels(locale?: string, weekStartsOn?: 0 | 1): string[];
13
+ /**
14
+ * Get full weekday names for abbr attributes (e.g. "Sunday", "Monday").
15
+ * Same order as getWeekdayLabels.
16
+ */
17
+ export declare function getWeekdayFullNames(locale?: string, weekStartsOn?: 0 | 1): string[];
18
+ /**
19
+ * Format a date for display in the date picker input (e.g. "2/15/2026").
20
+ */
21
+ export declare function formatDateForInput(date: Date, locale?: string): string;
22
+ /**
23
+ * Parse a string from the date input into a Date, or null if invalid.
24
+ * Only returns a date when the input is a complete valid date (e.g. "2/15/2026").
25
+ * Partial input like "1" or "2/15" returns null even though Date("1") would parse.
26
+ */
27
+ export declare function parseDateFromInput(value: string, locale?: string): Date | null;
28
+ /**
29
+ * Format a date range for the input (e.g. "2/15/2026 – 2/20/2026").
30
+ */
31
+ export declare function formatDateRangeForInput(startDate: Date | null, endDate: Date | null, locale?: string): string;
32
+ /**
33
+ * Parse a range string (e.g. "2/15/2026 – 2/20/2026") into { startDate, endDate }.
34
+ * Returns null if invalid. Single date is allowed and yields startDate = endDate.
35
+ */
36
+ export declare function parseDateRangeFromInput(value: string, locale?: string): {
37
+ startDate: Date;
38
+ endDate: Date;
39
+ } | null;