@westpac/ui 0.48.0 → 0.50.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 (102) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/component-type.json +1 -1
  3. package/dist/components/accordion/components/accordion-item/accordion-item.component.js +3 -2
  4. package/dist/components/button-dropdown/button-dropdown.component.d.ts +1 -1
  5. package/dist/components/button-dropdown/button-dropdown.component.js +3 -2
  6. package/dist/components/button-dropdown/button-dropdown.types.d.ts +2 -1
  7. package/dist/components/button-dropdown/components/button-dropdown-panel/button-dropdown-panel.component.js +0 -1
  8. package/dist/components/button-dropdown/components/button-dropdown-panel/button-dropdown-panel.styles.js +1 -1
  9. package/dist/components/date-picker-beta/components/calendar/calendar.component.d.ts +5 -0
  10. package/dist/components/date-picker-beta/components/calendar/calendar.component.js +101 -0
  11. package/dist/components/date-picker-beta/components/calendar/calendar.types.d.ts +2 -0
  12. package/dist/components/date-picker-beta/components/calendar/calendar.types.js +1 -0
  13. package/dist/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.component.d.ts +5 -0
  14. package/dist/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.component.js +28 -0
  15. package/dist/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.styles.d.ts +103 -0
  16. package/dist/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.styles.js +39 -0
  17. package/dist/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.types.d.ts +5 -0
  18. package/dist/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.types.js +1 -0
  19. package/dist/components/date-picker-beta/components/calendar/components/calendar-grid/calendar-grid.component.d.ts +5 -0
  20. package/dist/components/date-picker-beta/components/calendar/components/calendar-grid/calendar-grid.component.js +26 -0
  21. package/dist/components/date-picker-beta/components/calendar/components/calendar-grid/calendar-grid.types.d.ts +5 -0
  22. package/dist/components/date-picker-beta/components/calendar/components/calendar-grid/calendar-grid.types.js +1 -0
  23. package/dist/components/date-picker-beta/components/calendar/components/select/select.component.d.ts +5 -0
  24. package/dist/components/date-picker-beta/components/calendar/components/select/select.component.js +23 -0
  25. package/dist/components/date-picker-beta/components/calendar/components/select/select.styles.d.ts +28 -0
  26. package/dist/components/date-picker-beta/components/calendar/components/select/select.styles.js +14 -0
  27. package/dist/components/date-picker-beta/components/calendar/components/select/select.types.d.ts +2 -0
  28. package/dist/components/date-picker-beta/components/calendar/components/select/select.types.js +1 -0
  29. package/dist/components/date-picker-beta/components/date-field/components/date-segment/date-segment.component.d.ts +5 -0
  30. package/dist/components/date-picker-beta/components/date-field/components/date-segment/date-segment.component.js +20 -0
  31. package/dist/components/date-picker-beta/components/date-field/components/date-segment/date-segment.styles.d.ts +44 -0
  32. package/dist/components/date-picker-beta/components/date-field/components/date-segment/date-segment.styles.js +26 -0
  33. package/dist/components/date-picker-beta/components/date-field/components/date-segment/date-segment.types.d.ts +7 -0
  34. package/dist/components/date-picker-beta/components/date-field/components/date-segment/date-segment.types.js +1 -0
  35. package/dist/components/date-picker-beta/components/date-field/date-field.component.d.ts +5 -0
  36. package/dist/components/date-picker-beta/components/date-field/date-field.component.js +25 -0
  37. package/dist/components/date-picker-beta/components/date-field/date-field.types.d.ts +4 -0
  38. package/dist/components/date-picker-beta/components/date-field/date-field.types.js +1 -0
  39. package/dist/components/date-picker-beta/components/dialog/dialog.component.d.ts +5 -0
  40. package/dist/components/date-picker-beta/components/dialog/dialog.component.js +16 -0
  41. package/dist/components/date-picker-beta/components/dialog/dialog.types.d.ts +3 -0
  42. package/dist/components/date-picker-beta/components/dialog/dialog.types.js +1 -0
  43. package/dist/components/date-picker-beta/components/popover/popover.component.d.ts +5 -0
  44. package/dist/components/date-picker-beta/components/popover/popover.component.js +38 -0
  45. package/dist/components/date-picker-beta/components/popover/popover.styles.d.ts +71 -0
  46. package/dist/components/date-picker-beta/components/popover/popover.styles.js +35 -0
  47. package/dist/components/date-picker-beta/components/popover/popover.types.d.ts +9 -0
  48. package/dist/components/date-picker-beta/components/popover/popover.types.js +1 -0
  49. package/dist/components/date-picker-beta/date-picker.component.d.ts +2 -0
  50. package/dist/components/date-picker-beta/date-picker.component.js +114 -0
  51. package/dist/components/date-picker-beta/date-picker.styles.d.ts +107 -0
  52. package/dist/components/date-picker-beta/date-picker.styles.js +47 -0
  53. package/dist/components/date-picker-beta/date-picker.types.d.ts +32 -0
  54. package/dist/components/date-picker-beta/date-picker.types.js +1 -0
  55. package/dist/components/date-picker-beta/index.d.ts +2 -0
  56. package/dist/components/date-picker-beta/index.js +1 -0
  57. package/dist/components/index.d.ts +1 -0
  58. package/dist/components/index.js +1 -0
  59. package/dist/components/input-group/input-group.component.d.ts +1 -1
  60. package/dist/components/input-group/input-group.component.js +9 -3
  61. package/dist/components/input-group/input-group.styles.js +1 -1
  62. package/dist/components/input-group/input-group.types.d.ts +16 -0
  63. package/dist/css/westpac-ui.css +1015 -3
  64. package/dist/css/westpac-ui.min.css +1015 -3
  65. package/dist/tailwind/constants/colors.d.ts +25 -25
  66. package/dist/tailwind/tailwind-plugin.js +20 -0
  67. package/dist/tailwind/themes/index.d.ts +27 -27
  68. package/package.json +7 -3
  69. package/src/components/accordion/components/accordion-item/accordion-item.component.tsx +23 -24
  70. package/src/components/button-dropdown/button-dropdown.component.tsx +2 -0
  71. package/src/components/button-dropdown/button-dropdown.types.ts +3 -1
  72. package/src/components/button-dropdown/components/button-dropdown-panel/button-dropdown-panel.component.tsx +1 -1
  73. package/src/components/button-dropdown/components/button-dropdown-panel/button-dropdown-panel.styles.ts +1 -1
  74. package/src/components/date-picker-beta/components/calendar/calendar.component.tsx +106 -0
  75. package/src/components/date-picker-beta/components/calendar/calendar.types.ts +3 -0
  76. package/src/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.component.tsx +42 -0
  77. package/src/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.styles.ts +40 -0
  78. package/src/components/date-picker-beta/components/calendar/components/calendar-cell/calendar-cell.types.ts +8 -0
  79. package/src/components/date-picker-beta/components/calendar/components/calendar-grid/calendar-grid.component.tsx +36 -0
  80. package/src/components/date-picker-beta/components/calendar/components/calendar-grid/calendar-grid.types.ts +4 -0
  81. package/src/components/date-picker-beta/components/calendar/components/select/select.component.tsx +21 -0
  82. package/src/components/date-picker-beta/components/calendar/components/select/select.styles.ts +13 -0
  83. package/src/components/date-picker-beta/components/calendar/components/select/select.types.ts +3 -0
  84. package/src/components/date-picker-beta/components/date-field/components/date-segment/date-segment.component.tsx +26 -0
  85. package/src/components/date-picker-beta/components/date-field/components/date-segment/date-segment.styles.ts +22 -0
  86. package/src/components/date-picker-beta/components/date-field/components/date-segment/date-segment.types.ts +8 -0
  87. package/src/components/date-picker-beta/components/date-field/date-field.component.tsx +32 -0
  88. package/src/components/date-picker-beta/components/date-field/date-field.types.ts +3 -0
  89. package/src/components/date-picker-beta/components/dialog/dialog.component.tsx +23 -0
  90. package/src/components/date-picker-beta/components/dialog/dialog.types.ts +4 -0
  91. package/src/components/date-picker-beta/components/popover/popover.component.tsx +34 -0
  92. package/src/components/date-picker-beta/components/popover/popover.styles.ts +34 -0
  93. package/src/components/date-picker-beta/components/popover/popover.types.ts +10 -0
  94. package/src/components/date-picker-beta/date-picker.component.tsx +114 -0
  95. package/src/components/date-picker-beta/date-picker.styles.ts +44 -0
  96. package/src/components/date-picker-beta/date-picker.types.ts +40 -0
  97. package/src/components/date-picker-beta/index.ts +2 -0
  98. package/src/components/index.ts +1 -0
  99. package/src/components/input-group/input-group.component.tsx +9 -3
  100. package/src/components/input-group/input-group.styles.ts +1 -1
  101. package/src/components/input-group/input-group.types.ts +16 -0
  102. package/src/tailwind/tailwind-plugin.ts +20 -0
@@ -0,0 +1,106 @@
1
+ 'use client';
2
+
3
+ import { CalendarDate, createCalendar } from '@internationalized/date';
4
+ import React, { ChangeEvent, useCallback, useMemo, useRef } from 'react';
5
+ import { useButton, useCalendar, useLocale } from 'react-aria';
6
+ import { useCalendarState } from 'react-stately';
7
+
8
+ import { Button } from '../../../button/index.js';
9
+ import { Circle } from '../../../circle/circle.component.js';
10
+ import { ArrowLeftIcon, ArrowRightIcon } from '../../../icon/index.js';
11
+
12
+ import { type CalendarProps } from './calendar.types.js';
13
+ import { CalendarGrid } from './components/calendar-grid/calendar-grid.component.js';
14
+ import { Select } from './components/select/select.component.js';
15
+
16
+ const MONTHS = Array.from({ length: 12 }, (_, i) => {
17
+ const date = new Date(2020, i, 1); // Year doesn't matter, use 2020 as a common year
18
+ const monthName = new Intl.DateTimeFormat('en', { month: 'short' }).format(date);
19
+
20
+ return {
21
+ value: i + 1,
22
+ label: monthName,
23
+ };
24
+ });
25
+
26
+ const YEAR_OFFSET = 10;
27
+
28
+ /**
29
+ * @private
30
+ */
31
+ export function Calendar({ value, ...props }: CalendarProps) {
32
+ const { locale } = useLocale();
33
+ const refPrevButton = useRef(null);
34
+ const refNextButton = useRef(null);
35
+ const state = useCalendarState({
36
+ createCalendar,
37
+ ...props,
38
+ value,
39
+ locale,
40
+ });
41
+
42
+ const { calendarProps, prevButtonProps, nextButtonProps } = useCalendar({ value, ...props }, state);
43
+ const { buttonProps: newPrevButtonProps } = useButton(prevButtonProps, refPrevButton);
44
+ const { buttonProps: newNextButtonProps } = useButton(nextButtonProps, refNextButton);
45
+
46
+ const years = useMemo(() => {
47
+ const beginning = state.focusedDate.year - YEAR_OFFSET;
48
+ return Array.from({ length: 20 }, (_, i) => {
49
+ return {
50
+ value: beginning + i,
51
+ label: beginning + i,
52
+ };
53
+ });
54
+ }, [state.focusedDate.year]);
55
+
56
+ const handleMonthChange = useCallback(
57
+ (ev: ChangeEvent<HTMLSelectElement>) => {
58
+ state.setFocusedDate(new CalendarDate(state.focusedDate.year, +ev.target.value, state.focusedDate.day));
59
+ },
60
+ [state],
61
+ );
62
+
63
+ const handleYearChange = useCallback(
64
+ (ev: ChangeEvent<HTMLSelectElement>) => {
65
+ state.setFocusedDate(new CalendarDate(+ev.target.value, state.focusedDate.month, state.focusedDate.day));
66
+ },
67
+ [state],
68
+ );
69
+
70
+ return (
71
+ <div {...calendarProps}>
72
+ <div className="flex items-center justify-between">
73
+ <div className="flex gap-3">
74
+ <Select onChange={handleMonthChange} value={state.visibleRange.start.month}>
75
+ {MONTHS.map(month => (
76
+ <option key={month.value} value={month.value}>
77
+ {month.label}
78
+ </option>
79
+ ))}
80
+ </Select>
81
+
82
+ <Select onChange={handleYearChange} value={state.visibleRange.start.year}>
83
+ {years.map(year => (
84
+ <option key={year.value} value={year.value}>
85
+ {year.label}
86
+ </option>
87
+ ))}
88
+ </Select>
89
+ </div>
90
+ <div className="flex gap-2">
91
+ <Button look="unstyled" {...newPrevButtonProps}>
92
+ <Circle className="bg-light">
93
+ <ArrowLeftIcon size="small" color="primary" />
94
+ </Circle>
95
+ </Button>
96
+ <Button look="unstyled" {...newNextButtonProps}>
97
+ <Circle className="bg-light">
98
+ <ArrowRightIcon size="small" color="primary" />
99
+ </Circle>
100
+ </Button>
101
+ </div>
102
+ </div>
103
+ <CalendarGrid state={state} firstDayOfWeek={props.firstDayOfWeek} />
104
+ </div>
105
+ );
106
+ }
@@ -0,0 +1,3 @@
1
+ import { AriaCalendarProps, DateValue } from 'react-aria';
2
+
3
+ export type CalendarProps<T extends DateValue = DateValue> = AriaCalendarProps<T>;
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import React, { useRef } from 'react';
3
+ import { useCalendarCell, useFocusVisible } from 'react-aria';
4
+
5
+ import { styles as calendarCellStyles } from './calendar-cell.styles.js';
6
+ import { CalendarCellProps } from './calendar-cell.types.js';
7
+
8
+ /**
9
+ * @private
10
+ */
11
+ export function CalendarCell({ state, date }: CalendarCellProps) {
12
+ const ref = useRef(null);
13
+ const { isFocusVisible } = useFocusVisible();
14
+ const {
15
+ cellProps,
16
+ buttonProps,
17
+ isSelected,
18
+ isOutsideVisibleRange,
19
+ isDisabled,
20
+ isUnavailable,
21
+ formattedDate,
22
+ isFocused,
23
+ } = useCalendarCell({ date }, state, ref);
24
+ const isToday = buttonProps['aria-label']?.indexOf('Today') !== -1;
25
+ const styles = calendarCellStyles({
26
+ isSelected,
27
+ isFocused: isFocused && isFocusVisible,
28
+ isToday,
29
+ isDisabled,
30
+ isUnavailable,
31
+ });
32
+
33
+ return (
34
+ <td {...cellProps}>
35
+ <div className={styles.base()}>
36
+ <div {...buttonProps} ref={ref} hidden={isOutsideVisibleRange} className={styles.text()}>
37
+ {formattedDate}
38
+ </div>
39
+ </div>
40
+ </td>
41
+ );
42
+ }
@@ -0,0 +1,40 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv({
4
+ slots: {
5
+ base: 'flex items-center justify-center',
6
+ text: 'size-6 rounded-full border border-white text-center leading-[2.125rem]',
7
+ },
8
+ variants: {
9
+ isDisabled: {
10
+ true: {
11
+ text: 'cursor-default line-through opacity-50',
12
+ },
13
+ false: {
14
+ text: 'hover:bg-primary/5',
15
+ },
16
+ },
17
+ isUnavailable: {
18
+ true: {
19
+ text: 'cursor-default line-through opacity-50',
20
+ },
21
+ false: {},
22
+ },
23
+ isFocused: {
24
+ true: {
25
+ text: '!outline-offset-0 focus-outline',
26
+ },
27
+ },
28
+ isToday: {
29
+ true: {
30
+ text: 'border-primary bg-primary/5',
31
+ },
32
+ },
33
+ isSelected: {
34
+ true: {
35
+ text: 'bg-primary text-white hover:bg-primary',
36
+ },
37
+ false: 'bg-white',
38
+ },
39
+ },
40
+ });
@@ -0,0 +1,8 @@
1
+ import { AriaCalendarCellProps } from 'react-aria';
2
+ import { CalendarState, RangeCalendarState } from 'react-stately';
3
+
4
+ // import { styles } from './calendar-Cell.styles.js';
5
+
6
+ // type Variants = VariantProps<typeof styles>;
7
+
8
+ export type CalendarCellProps = AriaCalendarCellProps & { state: CalendarState | RangeCalendarState };
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { useCalendarGrid } from 'react-aria';
3
+
4
+ import { CalendarCell } from '../calendar-cell/calendar-cell.component.js';
5
+
6
+ import { CalendarGridProps } from './calendar-grid.types.js';
7
+
8
+ /**
9
+ * @private
10
+ */
11
+ export function CalendarGrid({ state, weekdayStyle = 'short', ...props }: CalendarGridProps) {
12
+ const { gridProps, headerProps, weekDays, weeksInMonth } = useCalendarGrid({ weekdayStyle, ...props }, state);
13
+
14
+ return (
15
+ <table className="w-full" {...gridProps}>
16
+ <thead {...headerProps}>
17
+ <tr>
18
+ {weekDays.map((day, index) => (
19
+ <th key={index} className="text-text size-6 text-center text-[0.75rem] font-semibold leading-9">
20
+ {day.toUpperCase().slice(0, 2)}
21
+ </th>
22
+ ))}
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ {[...new Array(weeksInMonth).keys()].map(weekIndex => (
27
+ <tr key={weekIndex}>
28
+ {state
29
+ .getDatesInWeek(weekIndex)
30
+ .map((date, i) => (date ? <CalendarCell key={i} state={state} date={date} /> : <td key={i} />))}
31
+ </tr>
32
+ ))}
33
+ </tbody>
34
+ </table>
35
+ );
36
+ }
@@ -0,0 +1,4 @@
1
+ import { AriaCalendarGridProps } from 'react-aria';
2
+ import { CalendarState, RangeCalendarState } from 'react-stately';
3
+
4
+ export type CalendarGridProps = AriaCalendarGridProps & { state: CalendarState | RangeCalendarState };
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { useFocusRing } from 'react-aria';
3
+
4
+ import { ExpandMoreIcon } from '../../../../../icon/index.js';
5
+
6
+ import { styles as selectStyles } from './select.styles.js';
7
+ import { SelectProps } from './select.types.js';
8
+
9
+ /**
10
+ * @private
11
+ */
12
+ export function Select({ ...props }: SelectProps) {
13
+ const { isFocusVisible, focusProps } = useFocusRing();
14
+ const styles = selectStyles({ isFocusVisible });
15
+ return (
16
+ <div className="relative">
17
+ <select {...focusProps} className={styles.base({ className: props.className })} {...props} />
18
+ <ExpandMoreIcon color="primary" size="small" className={styles.caret()} />
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,13 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv({
4
+ slots: {
5
+ base: '!typography-body-8 appearance-none border-none !bg-none pl-0 pr-4 font-semibold',
6
+ caret: 'pointer-events-none absolute right-0 top-1/2 -translate-y-1/2 touch-none',
7
+ },
8
+ variants: {
9
+ isFocusVisible: {
10
+ true: { base: 'focus-outline' },
11
+ },
12
+ },
13
+ });
@@ -0,0 +1,3 @@
1
+ import { SelectHTMLAttributes } from 'react';
2
+
3
+ export type SelectProps = SelectHTMLAttributes<HTMLSelectElement>;
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import React, { useRef } from 'react';
4
+ import { mergeProps, useDateSegment, useFocusRing } from 'react-aria';
5
+
6
+ import { styles as dateSegmentStyles } from './date-segment.styles.js';
7
+ import { DateSegmentProps } from './date-segment.types.js';
8
+
9
+ /**
10
+ * @private
11
+ */
12
+ export function DateSegment({ segment, state, separator, ...props }: DateSegmentProps) {
13
+ const ref = useRef(null);
14
+ const { focusProps, isFocusVisible } = useFocusRing();
15
+ const { segmentProps } = useDateSegment(segment, state, ref);
16
+ const styles = dateSegmentStyles({
17
+ isFocusVisible,
18
+ isPlaceholder: segment.isPlaceholder,
19
+ isSeparator: segmentProps.role !== 'spinbutton',
20
+ });
21
+ return (
22
+ <span {...props} {...mergeProps(focusProps, segmentProps)} ref={ref} className={styles}>
23
+ {segment.type === 'literal' ? separator || segment.text : segment.text}
24
+ </span>
25
+ );
26
+ }
@@ -0,0 +1,22 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv(
4
+ {
5
+ base: 'font-light disabled:form-control-disabled',
6
+ variants: {
7
+ isPlaceholder: {
8
+ true: 'text-text-50 opacity-100',
9
+ false: '',
10
+ },
11
+ isFocusVisible: {
12
+ true: 'focus-outline',
13
+ false: '',
14
+ },
15
+ isSeparator: {
16
+ true: 'px-0.5 text-text-50',
17
+ false: '',
18
+ },
19
+ },
20
+ },
21
+ { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
22
+ );
@@ -0,0 +1,8 @@
1
+ import { HTMLAttributes } from 'react';
2
+ import { DateFieldState, DateSegment } from 'react-stately';
3
+
4
+ export type DateSegmentProps = {
5
+ segment: DateSegment;
6
+ separator?: string;
7
+ state: DateFieldState;
8
+ } & HTMLAttributes<Element>;
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+
3
+ import { createCalendar } from '@internationalized/date';
4
+ import React, { useRef } from 'react';
5
+ import { useDateField, useLocale } from 'react-aria';
6
+ import { useDateFieldState } from 'react-stately';
7
+
8
+ import { DateSegment } from './components/date-segment/date-segment.component.js';
9
+ import { type DateFieldProps } from './date-field.types.js';
10
+
11
+ /**
12
+ * @private
13
+ */
14
+ export function DateField({ separator, ...props }: DateFieldProps) {
15
+ const { locale } = useLocale();
16
+ const state = useDateFieldState({
17
+ ...props,
18
+ locale,
19
+ createCalendar,
20
+ });
21
+
22
+ const ref = useRef(null);
23
+ const { fieldProps } = useDateField(props, state, ref);
24
+
25
+ return (
26
+ <div {...fieldProps} ref={ref}>
27
+ {state.segments.map((segment, i) => (
28
+ <DateSegment separator={separator} key={i} segment={segment} state={state} />
29
+ ))}
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,3 @@
1
+ import { AriaDateFieldProps, DateValue } from 'react-aria';
2
+
3
+ export type DateFieldProps<T extends DateValue = DateValue> = AriaDateFieldProps<T> & { separator?: string };
@@ -0,0 +1,23 @@
1
+ import React, { useRef } from 'react';
2
+ import { useDialog } from 'react-aria';
3
+
4
+ import { DialogProps } from './dialog.types.js';
5
+
6
+ /**
7
+ * @private
8
+ */
9
+ export function Dialog({ title, children, ...props }: DialogProps) {
10
+ const ref = useRef(null);
11
+ const { dialogProps, titleProps } = useDialog(props, ref);
12
+
13
+ return (
14
+ <div {...dialogProps} ref={ref} className="p-3">
15
+ {title && (
16
+ <h3 {...titleProps} style={{ marginTop: 0 }}>
17
+ {title}
18
+ </h3>
19
+ )}
20
+ {children}
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,4 @@
1
+ import { HTMLAttributes } from 'react';
2
+ import { AriaDialogProps } from 'react-aria';
3
+
4
+ export type DialogProps = AriaDialogProps & HTMLAttributes<Element>;
@@ -0,0 +1,34 @@
1
+ import React, { useRef } from 'react';
2
+ import { Overlay, usePopover } from 'react-aria';
3
+
4
+ import { CloseIcon } from '../../../icon/index.js';
5
+
6
+ import { styles as popoverStyles } from './popover.styles.js';
7
+ import { PopoverProps } from './popover.types.js';
8
+
9
+ /**
10
+ * @private
11
+ */
12
+ export function Popover({ state, portalContainer, children, showAsBottomSheet, ...props }: PopoverProps) {
13
+ const ref = useRef(null);
14
+ const { popoverProps, underlayProps } = usePopover(
15
+ { ...props, popoverRef: ref, containerPadding: 0, offset: 0 },
16
+ state,
17
+ );
18
+ const styles = popoverStyles({ showAsBottomSheet });
19
+
20
+ return (
21
+ <Overlay portalContainer={portalContainer}>
22
+ <div {...underlayProps} className={styles.underlay()} />
23
+ <div {...popoverProps} ref={ref} className={styles.popover()}>
24
+ <div className={styles.header()}>
25
+ <p className={styles.headerLabel()}>Choose a date</p>
26
+ <button className={styles.closeButton()} onClick={() => state.close()} aria-label="Close window">
27
+ <CloseIcon color="primary" className="block" size="small" />
28
+ </button>
29
+ </div>
30
+ {children}
31
+ </div>
32
+ </Overlay>
33
+ );
34
+ }
@@ -0,0 +1,34 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv(
4
+ {
5
+ slots: {
6
+ underlay: 'fixed inset-0',
7
+ popover: 'bg-white',
8
+ header: '',
9
+ headerLabel: '',
10
+ closeButton: 'flex items-center justify-center focus-outline',
11
+ },
12
+ variants: {
13
+ showAsBottomSheet: {
14
+ true: {
15
+ underlay: 'animate-fadeIn bg-black/20',
16
+ popover: '!fixed inset-x-0 !bottom-0 !left-0 !top-auto animate-slideUp overflow-auto rounded-t px-[8%] pb-3',
17
+ header: 'mx-[-8vw] flex items-center justify-between border-b border-b-border px-3.5 py-2',
18
+ headerLabel: 'typography-body-10 text-text',
19
+ closeButton:
20
+ 'pointer-events-none touch-none rounded-full bg-white opacity-0 focus:pointer-events-auto focus:touch-auto focus:opacity-100',
21
+ },
22
+ false: {
23
+ underlay: '',
24
+ closeButton:
25
+ 'border-border pointer-events-none absolute right-0 top-0 size-5 -translate-y-1/2 translate-x-1/2 touch-none rounded-full border bg-white opacity-0 focus:pointer-events-auto focus:touch-auto focus:opacity-100',
26
+ headerLabel: 'hidden',
27
+ popover:
28
+ 'absolute mt-1 scale-100 animate-fadeIn rounded border border-border bg-white opacity-100 shadow-[0_5px_10px_rgba(0,0,0,0.2)]',
29
+ },
30
+ },
31
+ },
32
+ },
33
+ { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
34
+ );
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ import { AriaPopoverProps } from 'react-aria';
3
+ import { OverlayTriggerState } from 'react-stately';
4
+
5
+ export type PopoverProps = Omit<AriaPopoverProps, 'popoverRef'> & {
6
+ children: ReactNode;
7
+ showAsBottomSheet?: boolean;
8
+ state: OverlayTriggerState;
9
+ portalContainer?: Element;
10
+ };
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import { DateValue, getDayOfWeek, isWeekend } from '@internationalized/date';
4
+ import React, { useMemo, useRef } from 'react';
5
+ import { useButton, useDatePicker, useLocale } from 'react-aria';
6
+ import { useDatePickerState } from 'react-stately';
7
+
8
+ import { useBreakpoint } from '../../hook/breakpoints.hook.js';
9
+ import { Breakpoint } from '../../tailwind/constants/index.js';
10
+ import { Button } from '../button/index.js';
11
+ import { CalendarIcon } from '../icon/index.js';
12
+
13
+ import { Calendar } from './components/calendar/calendar.component.js';
14
+ import { DateField } from './components/date-field/date-field.component.js';
15
+ import { Dialog } from './components/dialog/dialog.component.js';
16
+ import { Popover } from './components/popover/popover.component.js';
17
+ import { styles as datePickerStyles } from './date-picker.styles.js';
18
+ import { type DatePickerBetaProps } from './date-picker.types.js';
19
+
20
+ const BREAKPOINTS_DECRECENT = ['xl', 'lg', 'md', 'sm', 'xsl', 'initial'] as const;
21
+ export function DatePickerBeta({
22
+ size = 'medium',
23
+ className,
24
+ bottomSheetView = { initial: true, xsl: false },
25
+ isDateUnavailable,
26
+ disableDaysOfWeek,
27
+ disableWeekends,
28
+ separator,
29
+ portalContainer,
30
+ ...props
31
+ }: DatePickerBetaProps) {
32
+ const { locale } = useLocale();
33
+
34
+ const enhancedIsDateUnavailable = useMemo(() => {
35
+ return disableDaysOfWeek || disableWeekends
36
+ ? (date: DateValue) => {
37
+ let conditions = [isDateUnavailable?.(date) || false];
38
+ if (disableDaysOfWeek) {
39
+ conditions = [disableDaysOfWeek.indexOf(getDayOfWeek(date, locale)) !== -1, ...conditions];
40
+ }
41
+ if (disableWeekends) {
42
+ conditions = [isWeekend(date, locale), ...conditions];
43
+ }
44
+ return conditions.some(condition => condition);
45
+ }
46
+ : isDateUnavailable;
47
+ }, [disableDaysOfWeek, disableWeekends, isDateUnavailable, locale]);
48
+
49
+ const state = useDatePickerState({ isDateUnavailable: enhancedIsDateUnavailable, ...props });
50
+ const styles = datePickerStyles({ size, isInvalid: state.isInvalid, isDisabled: props.isDisabled });
51
+ const breakpoint = useBreakpoint();
52
+ const ref = useRef(null);
53
+
54
+ const { groupProps, labelProps, fieldProps, buttonProps, dialogProps, calendarProps } = useDatePicker(
55
+ { isDateUnavailable: enhancedIsDateUnavailable, ...props },
56
+ state,
57
+ ref,
58
+ );
59
+
60
+ const showAsBottomSheet: boolean = useMemo(() => {
61
+ if (typeof bottomSheetView === 'boolean') {
62
+ return bottomSheetView;
63
+ }
64
+ const currentBreakpointIndex = BREAKPOINTS_DECRECENT.findIndex(bp => bp === breakpoint);
65
+ const finalBreakPoint = [breakpoint, ...BREAKPOINTS_DECRECENT.slice(currentBreakpointIndex)].find(
66
+ currentBreakpoint => bottomSheetView[currentBreakpoint] !== undefined,
67
+ ) as Breakpoint | 'initial';
68
+
69
+ return bottomSheetView[finalBreakPoint] || false;
70
+ }, [bottomSheetView, breakpoint]);
71
+
72
+ const buttonRef = useRef(null);
73
+
74
+ const { buttonProps: newButtonProps } = useButton(buttonProps, buttonRef);
75
+
76
+ const brandContainer = useMemo(() => {
77
+ return (
78
+ document.querySelector('[data-theme]') ||
79
+ document.querySelector('[class^="theme-"], [class*=" theme-"]') ||
80
+ undefined
81
+ );
82
+ }, []);
83
+
84
+ return (
85
+ <>
86
+ <div {...labelProps}>{props.label}</div>
87
+ <div {...props} {...groupProps} ref={ref} className={styles.input({ className })}>
88
+ <DateField separator={separator} {...fieldProps} />
89
+ <Button
90
+ look="faint"
91
+ className={styles.button()}
92
+ iconColor="muted"
93
+ size={size}
94
+ iconAfter={CalendarIcon}
95
+ {...newButtonProps}
96
+ aria-labelledby={undefined}
97
+ />
98
+ </div>
99
+ {state.isOpen && (
100
+ <Popover
101
+ portalContainer={portalContainer || brandContainer}
102
+ showAsBottomSheet={showAsBottomSheet}
103
+ state={state}
104
+ triggerRef={ref}
105
+ placement="bottom left"
106
+ >
107
+ <Dialog {...dialogProps}>
108
+ <Calendar {...calendarProps} firstDayOfWeek={props.firstDayOfWeek} />
109
+ </Dialog>
110
+ </Popover>
111
+ )}
112
+ </>
113
+ );
114
+ }
@@ -0,0 +1,44 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv(
4
+ {
5
+ slots: {
6
+ input: 'form-control flex items-center border-borderDark disabled:form-control-disabled',
7
+ button:
8
+ 'flex h-auto items-center justify-center rounded-l-none border-y-0 border-l border-r-0 border-l-borderDark bg-light',
9
+ },
10
+ variants: {
11
+ size: {
12
+ small: {
13
+ input: 'form-control-small gap-1.5',
14
+ button: '-my-0.5 -mr-1.5 mb-[-0.25rem] min-h-5 py-[0.25rem]',
15
+ },
16
+ medium: {
17
+ input: 'form-control-medium gap-2',
18
+ button: 'my-[-0.3125rem] -mr-2 min-h-6 py-[0.3125rem]',
19
+ },
20
+ large: {
21
+ input: 'form-control-large gap-2.5',
22
+ button: 'my-[-0.5rem] -mr-2.5 min-h-7 py-[0.5rem]',
23
+ },
24
+ xlarge: {
25
+ input: 'form-control-xlarge gap-3',
26
+ button: '-my-1.5 -mr-3 mb-[-0.625rem] min-h-8 py-1.5',
27
+ },
28
+ },
29
+ isInvalid: {
30
+ true: {
31
+ input: 'border-danger',
32
+ },
33
+ false: {},
34
+ },
35
+ isDisabled: {
36
+ true: {
37
+ input: 'form-control-disabled',
38
+ },
39
+ false: {},
40
+ },
41
+ },
42
+ },
43
+ { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
44
+ );