@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.
Files changed (30) hide show
  1. package/package.json +11 -20
  2. package/src/Components/DatePicker/DatePicker.context.ts +14 -0
  3. package/src/Components/DatePicker/DatePicker.stories.tsx +16 -0
  4. package/src/Components/DatePicker/DatePicker.test.tsx +135 -0
  5. package/src/Components/DatePicker/DatePicker.tsx +178 -0
  6. package/src/Components/DatePicker/DatePickerDay.theme.ts +108 -0
  7. package/src/Components/DatePicker/DatePickerDay.tsx +137 -0
  8. package/src/Components/DatePicker/DatePickerFooter.tsx +41 -0
  9. package/src/Components/DatePicker/DatePickerGrid.tsx +10 -0
  10. package/src/Components/DatePicker/DatePickerHeader.tsx +72 -0
  11. package/src/Components/DatePicker/DatePickerMonth.tsx +94 -0
  12. package/src/Components/DatePicker/DatePickerMonthSelector.tsx +132 -0
  13. package/src/Components/DatePicker/__snapshots__/DatePicker.test.tsx.snap +7534 -0
  14. package/src/Components/DatePicker/useDateRange.ts +48 -0
  15. package/src/Components/DatePicker/useViewDate.ts +35 -0
  16. package/src/Components/NumberInput/NumberInput.theme.ts +36 -0
  17. package/src/Foundations/Shadows/Shadows.ts +1 -0
  18. package/src/Foundations/Sizes/Sizes.ts +1 -0
  19. package/src/Old/hooks/index.ts +0 -1
  20. package/src/index.ts +3 -0
  21. package/src/old.ts +0 -3
  22. package/src/theme.ts +4 -0
  23. package/src/tsconfig.tsbuildinfo +1 -1
  24. package/src/utils/utils.ts +8 -0
  25. package/src/Old/DatePicker/DatePicker.css +0 -74
  26. package/src/Old/DatePicker/DatePicker.tsx +0 -194
  27. package/src/Old/DatePicker/DatePickerDay.tsx +0 -72
  28. package/src/Old/DatePicker/DatePickerGrid.tsx +0 -12
  29. package/src/Old/DatePicker/DatePickerMonth.tsx +0 -87
  30. 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;