@bitrise/bitkit 10.34.0 → 10.35.0-alpha-datepicker-rewrite.1
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/package.json +11 -20
- package/src/Components/DatePicker/DatePicker.context.ts +14 -0
- package/src/Components/DatePicker/DatePicker.stories.tsx +16 -0
- package/src/Components/DatePicker/DatePicker.test.tsx +135 -0
- package/src/Components/DatePicker/DatePicker.tsx +178 -0
- package/src/Components/DatePicker/DatePickerDay.theme.ts +108 -0
- package/src/Components/DatePicker/DatePickerDay.tsx +137 -0
- package/src/Components/DatePicker/DatePickerFooter.tsx +41 -0
- package/src/Components/DatePicker/DatePickerGrid.tsx +10 -0
- package/src/Components/DatePicker/DatePickerHeader.tsx +72 -0
- package/src/Components/DatePicker/DatePickerMonth.tsx +94 -0
- package/src/Components/DatePicker/DatePickerMonthSelector.tsx +132 -0
- package/src/Components/DatePicker/__snapshots__/DatePicker.test.tsx.snap +7534 -0
- package/src/Components/DatePicker/useDateRange.ts +48 -0
- package/src/Components/DatePicker/useViewDate.ts +35 -0
- package/src/Components/NumberInput/NumberInput.theme.ts +36 -0
- package/src/Foundations/Shadows/Shadows.ts +1 -0
- package/src/Foundations/Sizes/Sizes.ts +1 -0
- package/src/Old/hooks/index.ts +0 -1
- package/src/index.ts +3 -0
- package/src/old.ts +0 -3
- package/src/theme.ts +4 -0
- package/src/tsconfig.tsbuildinfo +1 -1
- package/src/utils/utils.ts +8 -0
- package/src/Old/DatePicker/DatePicker.css +0 -74
- package/src/Old/DatePicker/DatePicker.tsx +0 -194
- package/src/Old/DatePicker/DatePickerDay.tsx +0 -72
- package/src/Old/DatePicker/DatePickerGrid.tsx +0 -12
- package/src/Old/DatePicker/DatePickerMonth.tsx +0 -87
- package/src/Old/hooks/useMediaQuery.ts +0 -91
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Box from '../Box/Box';
|
|
2
|
+
import Text from '../Text/Text';
|
|
3
|
+
import Button from '../Button/Button';
|
|
4
|
+
import ButtonGroup from '../ButtonGroup/ButtonGroup';
|
|
5
|
+
import { DateRange } from './useDateRange';
|
|
6
|
+
|
|
7
|
+
const DatePickerFooter = ({
|
|
8
|
+
selected,
|
|
9
|
+
onClose,
|
|
10
|
+
onApply,
|
|
11
|
+
}: {
|
|
12
|
+
selected?: DateRange;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
onApply: () => void;
|
|
15
|
+
}) => {
|
|
16
|
+
return (
|
|
17
|
+
<Box
|
|
18
|
+
display="grid"
|
|
19
|
+
gridTemplateColumns={['unset', '1fr auto 1fr']}
|
|
20
|
+
gridTemplateRows={['1.25rem 2rem', 'unset']}
|
|
21
|
+
gap="24"
|
|
22
|
+
>
|
|
23
|
+
<Text alignSelf="center" justifySelf="center" size="2" color="text.secondary" gridColumn={['1', '2']}>
|
|
24
|
+
{selected?.from?.toFormat('DD', { locale: 'en-US' })}
|
|
25
|
+
{selected?.from || selected?.to ? ' - ' : undefined}
|
|
26
|
+
{selected?.to?.toFormat('DD', { locale: 'en-US' })}
|
|
27
|
+
</Text>
|
|
28
|
+
<ButtonGroup gridColumn={['1', '3']} justifyContent="end">
|
|
29
|
+
<Button variant="secondary" onClick={onClose} size="small">
|
|
30
|
+
Cancel
|
|
31
|
+
</Button>
|
|
32
|
+
|
|
33
|
+
<Button onClick={onApply} size="small">
|
|
34
|
+
Apply
|
|
35
|
+
</Button>
|
|
36
|
+
</ButtonGroup>
|
|
37
|
+
</Box>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default DatePickerFooter;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import Box, { BoxProps } from '../Box/Box';
|
|
3
|
+
|
|
4
|
+
type Props = BoxProps;
|
|
5
|
+
|
|
6
|
+
const DatePickerGrid: React.FunctionComponent<Props> = (props: Props) => {
|
|
7
|
+
return <Box display="grid" {...props} gridTemplateColumns="repeat(7, 2.5rem)" gridAutoRows="2rem" gridRowGap="4" />;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default DatePickerGrid;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { createContext } from '@chakra-ui/react-utils';
|
|
3
|
+
import { useObjectMemo } from '../../utils/utils';
|
|
4
|
+
import Box, { BoxProps } from '../Box/Box';
|
|
5
|
+
import IconButton from '../IconButton/IconButton';
|
|
6
|
+
|
|
7
|
+
const [HeaderContext, useHeaderContext] = createContext<{
|
|
8
|
+
onPrevious: () => void;
|
|
9
|
+
onNext: () => void;
|
|
10
|
+
controls: 'left' | 'right' | 'both';
|
|
11
|
+
}>();
|
|
12
|
+
export const DatePickerHeaderContent = (props: BoxProps) => (
|
|
13
|
+
<Box alignSelf="center" display="flex" justifyContent="center" gap="8" {...props} />
|
|
14
|
+
);
|
|
15
|
+
export const DatePickerHeaderPrevious = ({ label }: { label: string }) => {
|
|
16
|
+
const { onPrevious, controls } = useHeaderContext();
|
|
17
|
+
return (
|
|
18
|
+
<IconButton
|
|
19
|
+
size="small"
|
|
20
|
+
alignSelf="start"
|
|
21
|
+
visibility={controls === 'right' ? 'hidden' : undefined}
|
|
22
|
+
aria-label={label}
|
|
23
|
+
variant="tertiary"
|
|
24
|
+
as="button"
|
|
25
|
+
onClick={onPrevious}
|
|
26
|
+
iconName="ChevronLeft"
|
|
27
|
+
isTooltipDisabled
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
export const DatePickerHeaderNext = ({ label }: { label: string }) => {
|
|
32
|
+
const { onNext, controls } = useHeaderContext();
|
|
33
|
+
return (
|
|
34
|
+
<IconButton
|
|
35
|
+
alignSelf="end"
|
|
36
|
+
visibility={controls === 'left' ? 'hidden' : undefined}
|
|
37
|
+
size="small"
|
|
38
|
+
aria-label={label}
|
|
39
|
+
variant="tertiary"
|
|
40
|
+
onClick={onNext}
|
|
41
|
+
iconName="ChevronRight"
|
|
42
|
+
isTooltipDisabled
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const DatePickerHeader = ({
|
|
48
|
+
onPrevious,
|
|
49
|
+
onNext,
|
|
50
|
+
children,
|
|
51
|
+
controls = 'both',
|
|
52
|
+
}: {
|
|
53
|
+
onPrevious: () => void;
|
|
54
|
+
onNext: () => void;
|
|
55
|
+
controls?: 'left' | 'right' | 'both';
|
|
56
|
+
children?: ReactNode;
|
|
57
|
+
}): JSX.Element => {
|
|
58
|
+
const ctx = useObjectMemo({ onPrevious, onNext, controls });
|
|
59
|
+
return (
|
|
60
|
+
<Box
|
|
61
|
+
marginBottom="24"
|
|
62
|
+
width="17.5rem"
|
|
63
|
+
display="grid"
|
|
64
|
+
gridTemplateColumns="2rem auto 2rem"
|
|
65
|
+
alignItems="center"
|
|
66
|
+
gap="4"
|
|
67
|
+
>
|
|
68
|
+
<HeaderContext value={ctx}>{children}</HeaderContext>
|
|
69
|
+
</Box>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
export default DatePickerHeader;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createElement, Fragment, useCallback, useId } from 'react';
|
|
2
|
+
import { DateTime, Info } from 'luxon';
|
|
3
|
+
import {
|
|
4
|
+
NumberDecrementStepper,
|
|
5
|
+
NumberIncrementStepper,
|
|
6
|
+
NumberInput,
|
|
7
|
+
NumberInputField,
|
|
8
|
+
NumberInputStepper,
|
|
9
|
+
} from '@chakra-ui/react';
|
|
10
|
+
import { useObjectMemo } from '../../utils/utils';
|
|
11
|
+
import Button from '../Button/Button';
|
|
12
|
+
import Box from '../Box/Box';
|
|
13
|
+
import Icon from '../Icon/Icon';
|
|
14
|
+
import Text from '../Text/Text';
|
|
15
|
+
import DatePickerGrid from './DatePickerGrid';
|
|
16
|
+
import DatePickerDay, { DatePickerDayContext } from './DatePickerDay';
|
|
17
|
+
import DatePickerHeader, {
|
|
18
|
+
DatePickerHeaderContent,
|
|
19
|
+
DatePickerHeaderNext,
|
|
20
|
+
DatePickerHeaderPrevious,
|
|
21
|
+
} from './DatePickerHeader';
|
|
22
|
+
|
|
23
|
+
const daysOfTheWeek = Info.weekdays('short');
|
|
24
|
+
const daysCount = 6 * 7;
|
|
25
|
+
|
|
26
|
+
interface Props {
|
|
27
|
+
controls: 'left' | 'right' | 'both';
|
|
28
|
+
onViewDateChange: (viewDate: DateTime) => void;
|
|
29
|
+
onMonthClick: () => void;
|
|
30
|
+
viewDate: DateTime;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const days = createElement(
|
|
34
|
+
Fragment,
|
|
35
|
+
{},
|
|
36
|
+
...Array.from({ length: daysCount }).map((_, i) => <DatePickerDay n={i + 1} />),
|
|
37
|
+
);
|
|
38
|
+
const DatePickerMonth = ({ controls, onViewDateChange, viewDate, onMonthClick }: Props) => {
|
|
39
|
+
const onNextMonth = useCallback(() => onViewDateChange(viewDate.plus({ months: 1 })), [onViewDateChange, viewDate]);
|
|
40
|
+
const onPreviousMonth = useCallback(
|
|
41
|
+
() => onViewDateChange(viewDate.minus({ months: 1 })),
|
|
42
|
+
[onViewDateChange, viewDate],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const dayContext = useObjectMemo({ onPreviousMonth, onNextMonth, viewDate });
|
|
46
|
+
|
|
47
|
+
const monthLabelId = useId();
|
|
48
|
+
return (
|
|
49
|
+
<Box>
|
|
50
|
+
<DatePickerHeader onPrevious={onPreviousMonth} onNext={onNextMonth} controls={controls}>
|
|
51
|
+
<DatePickerHeaderPrevious label="previous month" />
|
|
52
|
+
<DatePickerHeaderContent id={monthLabelId}>
|
|
53
|
+
<Button onClick={onMonthClick} size="small" variant="tertiary" flexShrink={0} rightIconName="ChevronDown">
|
|
54
|
+
{viewDate.monthLong}
|
|
55
|
+
</Button>
|
|
56
|
+
<NumberInput
|
|
57
|
+
aria-label="year"
|
|
58
|
+
flexShrink={1}
|
|
59
|
+
size="small"
|
|
60
|
+
min={1990}
|
|
61
|
+
w="4.5rem"
|
|
62
|
+
max={2100}
|
|
63
|
+
value={viewDate.year}
|
|
64
|
+
onChange={(_, year) => onViewDateChange(viewDate.set({ year }))}
|
|
65
|
+
>
|
|
66
|
+
<NumberInputField />
|
|
67
|
+
<NumberInputStepper>
|
|
68
|
+
<NumberIncrementStepper>
|
|
69
|
+
<Icon size="16" name="ChevronUp" />
|
|
70
|
+
</NumberIncrementStepper>
|
|
71
|
+
<NumberDecrementStepper>
|
|
72
|
+
<Icon size="16" name="ChevronDown" />
|
|
73
|
+
</NumberDecrementStepper>
|
|
74
|
+
</NumberInputStepper>
|
|
75
|
+
</NumberInput>
|
|
76
|
+
</DatePickerHeaderContent>
|
|
77
|
+
<DatePickerHeaderNext label="next month" />
|
|
78
|
+
</DatePickerHeader>
|
|
79
|
+
|
|
80
|
+
<DatePickerGrid alignItems="center" justifyItems="center" gridTemplateRows="2rem">
|
|
81
|
+
{daysOfTheWeek.map((day) => (
|
|
82
|
+
<Text key={day} size="2" color="neutral.50" textTransform="capitalize">
|
|
83
|
+
{day}
|
|
84
|
+
</Text>
|
|
85
|
+
))}
|
|
86
|
+
</DatePickerGrid>
|
|
87
|
+
<DatePickerGrid aria-labelledby={monthLabelId} role="listbox" paddingTop="8">
|
|
88
|
+
<DatePickerDayContext value={dayContext}>{days}</DatePickerDayContext>
|
|
89
|
+
</DatePickerGrid>
|
|
90
|
+
</Box>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default DatePickerMonth;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { ReactNode, useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
NumberDecrementStepper,
|
|
4
|
+
NumberIncrementStepper,
|
|
5
|
+
NumberInput,
|
|
6
|
+
NumberInputField,
|
|
7
|
+
NumberInputStepper,
|
|
8
|
+
useMultiStyleConfig,
|
|
9
|
+
} from '@chakra-ui/react';
|
|
10
|
+
import { DateTime, Info } from 'luxon';
|
|
11
|
+
import Box from '../Box/Box';
|
|
12
|
+
import Text from '../Text/Text';
|
|
13
|
+
import Icon from '../Icon/Icon';
|
|
14
|
+
import DatePickerHeader, {
|
|
15
|
+
DatePickerHeaderContent,
|
|
16
|
+
DatePickerHeaderNext,
|
|
17
|
+
DatePickerHeaderPrevious,
|
|
18
|
+
} from './DatePickerHeader';
|
|
19
|
+
|
|
20
|
+
const DatePickerMonthItem = ({
|
|
21
|
+
children,
|
|
22
|
+
selected,
|
|
23
|
+
'aria-label': label,
|
|
24
|
+
onClick,
|
|
25
|
+
n,
|
|
26
|
+
id,
|
|
27
|
+
}: {
|
|
28
|
+
id?: string;
|
|
29
|
+
'aria-label': string;
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
selected: boolean;
|
|
32
|
+
n: number;
|
|
33
|
+
onClick: (n: number) => void;
|
|
34
|
+
}) => {
|
|
35
|
+
const { text, day } = useMultiStyleConfig('DatePickerDay', {
|
|
36
|
+
selectable: true,
|
|
37
|
+
selection: selected ? 'incomplete' : undefined,
|
|
38
|
+
currentMonth: true,
|
|
39
|
+
});
|
|
40
|
+
return (
|
|
41
|
+
<Box
|
|
42
|
+
id={id}
|
|
43
|
+
aria-selected={selected ? true : undefined}
|
|
44
|
+
aria-label={label}
|
|
45
|
+
as="button"
|
|
46
|
+
role="option"
|
|
47
|
+
sx={day}
|
|
48
|
+
onClick={() => onClick(n)}
|
|
49
|
+
width="48"
|
|
50
|
+
>
|
|
51
|
+
<Text sx={{ ...text, width: '48' }}>{children}</Text>
|
|
52
|
+
</Box>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const monthNames = Info.months('short');
|
|
57
|
+
const longMonthNames = Info.months('long');
|
|
58
|
+
const DatePickerMonthSelector = ({
|
|
59
|
+
viewDate,
|
|
60
|
+
onMonthSelected,
|
|
61
|
+
}: {
|
|
62
|
+
viewDate: DateTime;
|
|
63
|
+
onMonthSelected: (month: number, year: number) => void;
|
|
64
|
+
}): JSX.Element => {
|
|
65
|
+
const [selectedYear, setSelectedYear] = useState(viewDate.year);
|
|
66
|
+
const onPreviousYear = useCallback(() => setSelectedYear((year) => year - 1), []);
|
|
67
|
+
const onNextYear = useCallback(() => setSelectedYear((year) => year + 1), []);
|
|
68
|
+
const yearRef = useRef<HTMLInputElement>(null);
|
|
69
|
+
useEffect(() => yearRef.current?.focus(), []);
|
|
70
|
+
const monthClicked = useCallback((m: number) => onMonthSelected(m, selectedYear), [selectedYear]);
|
|
71
|
+
const monthId = useId();
|
|
72
|
+
return (
|
|
73
|
+
<Box>
|
|
74
|
+
<DatePickerHeader onPrevious={onPreviousYear} onNext={onNextYear}>
|
|
75
|
+
<DatePickerHeaderPrevious label="previous year" />
|
|
76
|
+
<DatePickerHeaderContent>
|
|
77
|
+
<NumberInput
|
|
78
|
+
aria-label="year"
|
|
79
|
+
flexShrink={1}
|
|
80
|
+
size="small"
|
|
81
|
+
min={1990}
|
|
82
|
+
w="4.5rem"
|
|
83
|
+
max={2100}
|
|
84
|
+
value={selectedYear}
|
|
85
|
+
onChange={(_, year) => {
|
|
86
|
+
setSelectedYear(year);
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
<NumberInputField ref={yearRef} />
|
|
90
|
+
<NumberInputStepper>
|
|
91
|
+
<NumberIncrementStepper>
|
|
92
|
+
<Icon size="16" name="ChevronUp" />
|
|
93
|
+
</NumberIncrementStepper>
|
|
94
|
+
<NumberDecrementStepper>
|
|
95
|
+
<Icon size="16" name="ChevronDown" />
|
|
96
|
+
</NumberDecrementStepper>
|
|
97
|
+
</NumberInputStepper>
|
|
98
|
+
</NumberInput>
|
|
99
|
+
</DatePickerHeaderContent>
|
|
100
|
+
<DatePickerHeaderNext label="next year" />
|
|
101
|
+
</DatePickerHeader>
|
|
102
|
+
<Box
|
|
103
|
+
display="grid"
|
|
104
|
+
rowGap="24"
|
|
105
|
+
alignItems="center"
|
|
106
|
+
justifyItems="center"
|
|
107
|
+
gridTemplateColumns="repeat(3,1fr)"
|
|
108
|
+
gridTemplateRows="repeat(4, 1fr)"
|
|
109
|
+
role="listbox"
|
|
110
|
+
aria-label="month"
|
|
111
|
+
aria-activedescendant={monthId}
|
|
112
|
+
>
|
|
113
|
+
{monthNames.map((month, idx) => {
|
|
114
|
+
const selected = viewDate.month === idx + 1;
|
|
115
|
+
return (
|
|
116
|
+
<DatePickerMonthItem
|
|
117
|
+
id={selected ? monthId : undefined}
|
|
118
|
+
onClick={monthClicked}
|
|
119
|
+
n={idx + 1}
|
|
120
|
+
aria-label={longMonthNames[idx]}
|
|
121
|
+
selected={selected}
|
|
122
|
+
key={month}
|
|
123
|
+
>
|
|
124
|
+
{month}
|
|
125
|
+
</DatePickerMonthItem>
|
|
126
|
+
);
|
|
127
|
+
})}
|
|
128
|
+
</Box>
|
|
129
|
+
</Box>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
export default DatePickerMonthSelector;
|