@bitrise/bitkit 10.34.0 → 10.35.0-alpha-datepicker-rewrite.2

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 (29) hide show
  1. package/package.json +9 -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 +94 -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/useDateRange.ts +48 -0
  14. package/src/Components/DatePicker/useViewDate.ts +35 -0
  15. package/src/Components/NumberInput/NumberInput.theme.ts +36 -0
  16. package/src/Foundations/Shadows/Shadows.ts +1 -0
  17. package/src/Foundations/Sizes/Sizes.ts +1 -0
  18. package/src/Old/hooks/index.ts +0 -1
  19. package/src/index.ts +3 -0
  20. package/src/old.ts +0 -3
  21. package/src/theme.ts +4 -0
  22. package/src/tsconfig.tsbuildinfo +1 -1
  23. package/src/utils/utils.ts +8 -0
  24. package/src/Old/DatePicker/DatePicker.css +0 -74
  25. package/src/Old/DatePicker/DatePicker.tsx +0 -194
  26. package/src/Old/DatePicker/DatePickerDay.tsx +0 -72
  27. package/src/Old/DatePicker/DatePickerGrid.tsx +0 -12
  28. package/src/Old/DatePicker/DatePickerMonth.tsx +0 -87
  29. package/src/Old/hooks/useMediaQuery.ts +0 -91
@@ -1,3 +1,5 @@
1
+ import { useMemo } from 'react';
2
+
1
3
  export const rem = (input: number | string): number | string => {
2
4
  const value = typeof input === 'string' ? parseInt(input, 10) : input;
3
5
  if (Number.isNaN(value)) {
@@ -6,3 +8,9 @@ export const rem = (input: number | string): number | string => {
6
8
  }
7
9
  return `${value / 16}rem`;
8
10
  };
11
+
12
+ export function useObjectMemo<T extends object>(obj: T): T {
13
+ return useMemo(() => {
14
+ return obj;
15
+ }, Object.values(obj));
16
+ }
@@ -1,74 +0,0 @@
1
- .DatePicker__grid + .DatePicker__grid {
2
- border-top: 0.0625rem solid var(--color-gray--2);
3
- }
4
-
5
- .DatePicker__day {
6
- position: relative;
7
- width: 3rem;
8
- height: 3rem;
9
- transition-property: color;
10
- transition-duration: var(--transition-duration--fast);
11
- transition-timing-function: var(--transition-timing-function);
12
- }
13
-
14
- .DatePicker__day::before {
15
- content: '';
16
- position: absolute;
17
- top: 0;
18
- right: 0;
19
- bottom: 0;
20
- left: 0;
21
- transform: scale(0);
22
- opacity: 0;
23
- border-radius: 50%;
24
- background-color: var(--color-grape--4);
25
- transition-property: transform, opacity, background-color;
26
- transition-duration: var(--transition-duration--fast);
27
- transition-timing-function: var(--transition-timing-function);
28
- }
29
-
30
- .DatePicker__day-inner {
31
- position: relative;
32
- }
33
-
34
- .DatePicker__day--selectable {
35
- cursor: pointer;
36
- }
37
-
38
- .DatePicker__day--selectable.DatePicker__day--unselected:hover {
39
- color: var(--color-gray--1);
40
- }
41
-
42
- .DatePicker__day--selected::before {
43
- transform: scale(calc(2.5 / 3));
44
- opacity: 1;
45
- background-color: var(--color-grape--4);
46
- transform: scale(1);
47
- background-color: var(--color-aqua--3);
48
- }
49
-
50
- .DatePicker__day--selectable.DatePicker__day--unselected:hover::before {
51
- transform: scale(calc(2.5 / 3));
52
- opacity: 1;
53
- background-color: var(--color-grape--4);
54
- }
55
-
56
- .DatePicker__day--selected-from,
57
- .DatePicker__day--selected-between,
58
- .DatePicker__day--selected-to {
59
- background-color: var(--color-aqua--2);
60
- }
61
-
62
- .DatePicker__day--selected-from {
63
- border-top-left-radius: 50%;
64
- border-bottom-left-radius: 50%;
65
- }
66
-
67
- .DatePicker__day--selected-to {
68
- border-top-right-radius: 50%;
69
- border-bottom-right-radius: 50%;
70
- }
71
-
72
- .DatePicker__day--unselectable {
73
- color: var(--color-gray--5);
74
- }
@@ -1,194 +0,0 @@
1
- /* eslint-disable react/destructuring-assignment */
2
- import * as React from 'react';
3
- import { DateTime } from 'luxon';
4
- import { ReferenceChildrenProps } from 'react-popper';
5
- import { useMediaQuery, useSyncedStateAndProps } from '../hooks';
6
- import Button from '../../Components/Button/Button';
7
- import ButtonGroup from '../../Components/ButtonGroup/ButtonGroup';
8
- import { Props as FlexProps } from '../Flex/Flex';
9
- import Placement from '../Placement/Placement';
10
- import PlacementArea from '../Placement/PlacementArea';
11
- import PlacementManager from '../Placement/PlacementManager';
12
- import PlacementReference from '../Placement/PlacementReference';
13
- import DatePickerMonth from './DatePickerMonth';
14
- import './DatePicker.css';
15
-
16
- const { useState } = React;
17
-
18
- export interface Props extends FlexProps {
19
- /**
20
- * PlacementReference children prop for passing down
21
- * the ref of the target to be placed around.
22
- */
23
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
- children: (props: ReferenceChildrenProps) => React.ReactNode;
25
- /**
26
- * The earliest date that can be selected.
27
- * Should be given in millisecond form. For example 1558973847330
28
- */
29
- dateSelectableFrom?: number;
30
- /**
31
- * The latest date that can be selected.
32
- * Should be given in millisecond form. For example 1558973847330
33
- */
34
- dateSelectableTo?: number;
35
- /**
36
- * The start date of the selected period.
37
- * Should be given in millisecond form. For example 1558973847330
38
- */
39
- dateSelectedFrom?: number;
40
- /**
41
- * The end date of the selected period.
42
- * Should be given in millisecond form. For example 1558973847330
43
- */
44
- dateSelectedTo?: number;
45
- /** Handler for when the selected period is chosen. */
46
- onApply?: (dateSelectedFrom?: number, dateSelectedTo?: number) => void;
47
- /** Handler for when the date picker has been asked to be closed. */
48
- onClose: () => void;
49
- /** Flag for when the date picker should be visible. */
50
- visible: boolean;
51
- }
52
-
53
- /**
54
- * A simple date selection component, that supports a dual month view and
55
- * range selection.
56
- */
57
- const DatePicker: React.FunctionComponent<Props> = (props: Props) => {
58
- const { children, onApply, onClose, visible } = props;
59
-
60
- const match = useMediaQuery(['56rem']);
61
- const [dateSelectedFromMillis, setDateSelectedFromMillis] = useSyncedStateAndProps(props.dateSelectedFrom);
62
- const [dateSelectedToMillis, setDateSelectedToMillis] = useSyncedStateAndProps(props.dateSelectedTo);
63
- const [viewDateStartMillis, setViewDateStartMillis] = useState(dateSelectedFromMillis);
64
- const [viewDateEndMillis, setViewDateEndMillis] = useState(dateSelectedToMillis);
65
-
66
- const dateSelectableFrom =
67
- props.dateSelectableFrom === undefined ? undefined : DateTime.fromMillis(props.dateSelectableFrom).startOf('day');
68
- const dateSelectableTo =
69
- props.dateSelectableTo === undefined ? undefined : DateTime.fromMillis(props.dateSelectableTo);
70
- const dateSelectedFrom = dateSelectedFromMillis && DateTime.fromMillis(dateSelectedFromMillis).startOf('day');
71
- const dateSelectedTo = dateSelectedToMillis && DateTime.fromMillis(dateSelectedToMillis).startOf('day');
72
-
73
- const viewDateStart = viewDateStartMillis
74
- ? DateTime.fromMillis(viewDateStartMillis).startOf('month')
75
- : DateTime.local().startOf('month');
76
-
77
- const viewDateEnd =
78
- viewDateEndMillis && !DateTime.fromMillis(viewDateEndMillis).startOf('month').equals(viewDateStart)
79
- ? DateTime.fromMillis(viewDateEndMillis).startOf('month')
80
- : viewDateStart.plus({ months: 1 }).startOf('month');
81
-
82
- const handleClose = () => {
83
- onClose();
84
- setDateSelectedFromMillis(undefined);
85
- setDateSelectedToMillis(undefined);
86
- };
87
-
88
- const handleApply = () => {
89
- if (onApply) {
90
- onApply(dateSelectedFromMillis, dateSelectedToMillis);
91
- }
92
-
93
- handleClose();
94
- };
95
-
96
- const handlePreviousMonthStart = () => {
97
- setViewDateStartMillis(+viewDateStart.minus({ months: 1 }));
98
- };
99
-
100
- const handleNextMonthStart = () => {
101
- setViewDateStartMillis(+viewDateStart.plus({ months: 1 }));
102
-
103
- if (viewDateStart.plus({ months: 1 }).equals(viewDateEnd)) {
104
- setViewDateEndMillis(+viewDateEnd.plus({ months: 1 }));
105
- }
106
- };
107
-
108
- const handlePreviousMonthEnd = () => {
109
- setViewDateEndMillis(+viewDateEnd.minus({ months: 1 }));
110
-
111
- if (viewDateEnd.minus({ months: 1 }).equals(viewDateStart)) {
112
- setViewDateStartMillis(+viewDateStart.minus({ months: 1 }));
113
- }
114
- };
115
-
116
- const handleNextMonthEnd = () => {
117
- setViewDateEndMillis(+viewDateEnd.plus({ months: 1 }));
118
- };
119
-
120
- const handleSelect = (date: DateTime) => {
121
- let nextDateSelectedFrom: undefined | DateTime = dateSelectedFrom;
122
- let nextDateSelectedTo: undefined | DateTime = dateSelectedTo;
123
-
124
- if (dateSelectedFrom && dateSelectedTo) {
125
- nextDateSelectedFrom = date;
126
- nextDateSelectedTo = undefined;
127
- } else if (dateSelectedFrom) {
128
- nextDateSelectedTo = date;
129
- } else {
130
- nextDateSelectedFrom = date;
131
- }
132
-
133
- if (nextDateSelectedFrom && nextDateSelectedTo && +nextDateSelectedFrom > +nextDateSelectedTo) {
134
- [nextDateSelectedFrom, nextDateSelectedTo] = [nextDateSelectedTo, nextDateSelectedFrom];
135
- }
136
-
137
- setDateSelectedFromMillis(nextDateSelectedFrom && nextDateSelectedFrom.toMillis());
138
- setDateSelectedToMillis(nextDateSelectedTo && nextDateSelectedTo.toMillis());
139
- };
140
-
141
- return (
142
- <PlacementManager>
143
- <PlacementReference>{children}</PlacementReference>
144
-
145
- <Placement onClose={onClose} visible={visible}>
146
- {() => (
147
- <PlacementArea paddingVertical="x8">
148
- <PlacementArea direction="horizontal">
149
- <PlacementArea paddingHorizontal="x8">
150
- <DatePickerMonth
151
- dateSelectableFrom={dateSelectableFrom}
152
- dateSelectableTo={dateSelectableTo}
153
- dateSelectedFrom={dateSelectedFrom}
154
- dateSelectedTo={dateSelectedTo}
155
- onNextMonth={handleNextMonthStart}
156
- onPreviousMonth={handlePreviousMonthStart}
157
- onSelect={handleSelect}
158
- viewDate={viewDateStart}
159
- />
160
- </PlacementArea>
161
-
162
- {match('56rem') && (
163
- <PlacementArea paddingHorizontal="x8">
164
- <DatePickerMonth
165
- dateSelectableFrom={dateSelectableFrom}
166
- dateSelectableTo={dateSelectableTo}
167
- dateSelectedFrom={dateSelectedFrom}
168
- dateSelectedTo={dateSelectedTo}
169
- onNextMonth={handleNextMonthEnd}
170
- onPreviousMonth={handlePreviousMonthEnd}
171
- onSelect={handleSelect}
172
- viewDate={viewDateEnd}
173
- />
174
- </PlacementArea>
175
- )}
176
- </PlacementArea>
177
-
178
- <ButtonGroup alignItems="flex-end" marginY="8" paddingX="32">
179
- <Button variant="secondary" onClick={handleClose} size="small">
180
- Cancel
181
- </Button>
182
-
183
- <Button onClick={handleApply} size="small">
184
- Apply
185
- </Button>
186
- </ButtonGroup>
187
- </PlacementArea>
188
- )}
189
- </Placement>
190
- </PlacementManager>
191
- );
192
- };
193
-
194
- export default DatePicker;
@@ -1,72 +0,0 @@
1
- import * as React from 'react';
2
- import classnames from 'classnames';
3
- import { DateTime } from 'luxon';
4
- import Flex, { Props as FlexProps } from '../Flex/Flex';
5
-
6
- interface Props extends FlexProps {
7
- dateSelectableFrom?: DateTime;
8
- dateSelectableTo?: DateTime;
9
- dateSelectedFrom?: DateTime;
10
- dateSelectedTo?: DateTime;
11
- n: number;
12
- onSelect: (date: DateTime) => void;
13
- viewDate: DateTime;
14
- }
15
-
16
- const DatePickerDay: React.FunctionComponent<Props> = (props: Props) => {
17
- const { dateSelectableFrom, dateSelectableTo, dateSelectedFrom, dateSelectedTo, n, onSelect, viewDate, ...rest } =
18
- props;
19
-
20
- const date = viewDate.plus({ days: n - viewDate.weekday });
21
-
22
- const dayOfWeek = viewDate.weekday;
23
- const { daysInMonth } = viewDate;
24
- const daysInPreviousMonth = viewDate.minus({ month: 1 }).daysInMonth;
25
-
26
- const isPreviousMonth = n < dayOfWeek;
27
- const isNextMonth = n - dayOfWeek >= daysInMonth;
28
- const isCurrentMonth = !isPreviousMonth && !isNextMonth;
29
- const isAfterSelectableFromDate = !dateSelectableFrom || date.equals(dateSelectableFrom) || date > dateSelectableFrom;
30
- const isBeforeSelectableToDate = !dateSelectableTo || date.equals(dateSelectableTo) || date < dateSelectableTo;
31
- const isSelectable = isCurrentMonth && isAfterSelectableFromDate && isBeforeSelectableToDate;
32
-
33
- const hasSelectionRange = dateSelectedFrom && dateSelectedTo && !dateSelectedFrom.equals(dateSelectedTo);
34
-
35
- const isSelectedFrom = isCurrentMonth && dateSelectedFrom && date.equals(dateSelectedFrom);
36
- const isSelectedTo = isCurrentMonth && dateSelectedTo && date.equals(dateSelectedTo);
37
- const isSelectedBetween =
38
- isCurrentMonth &&
39
- dateSelectedFrom &&
40
- dateSelectedTo &&
41
- hasSelectionRange &&
42
- +date > +dateSelectedFrom &&
43
- +date < +dateSelectedTo;
44
-
45
- const classes = classnames('DatePicker__day', {
46
- 'DatePicker__day--selected': isSelectedFrom || isSelectedTo,
47
- 'DatePicker__day--selected-between': isSelectedBetween,
48
- 'DatePicker__day--selected-from': hasSelectionRange && isSelectedFrom,
49
- 'DatePicker__day--selected-to': hasSelectionRange && isSelectedTo,
50
- 'DatePicker__day--selectable': isSelectable,
51
- 'DatePicker__day--unselected': !isSelectedFrom && !isSelectedTo && !isSelectedBetween,
52
- 'DatePicker__day--unselectable': !isSelectable,
53
- });
54
-
55
- return (
56
- <Flex
57
- {...rest}
58
- alignChildren="middle"
59
- className={classes}
60
- direction="horizontal"
61
- onClick={isSelectable ? () => onSelect(date) : undefined}
62
- >
63
- <Flex alignChildren="middle" className="DatePicker__day-inner" direction="horizontal">
64
- {isPreviousMonth && daysInPreviousMonth - (dayOfWeek - n - 1)}
65
- {isCurrentMonth && n - dayOfWeek + 1}
66
- {isNextMonth && n - dayOfWeek - daysInMonth + 1}
67
- </Flex>
68
- </Flex>
69
- );
70
- };
71
-
72
- export default DatePickerDay;
@@ -1,12 +0,0 @@
1
- import * as React from 'react';
2
- import Box, { BoxProps } from '../../Components/Box/Box';
3
-
4
- type Props = BoxProps;
5
-
6
- const DatePickerGrid: React.FunctionComponent<Props> = (props: Props) => {
7
- return (
8
- <Box display="grid" {...props} gridTemplateColumns="repeat(7, 3rem)" gridAutoRows="max-content" gridRowGap="4" />
9
- );
10
- };
11
-
12
- export default DatePickerGrid;
@@ -1,87 +0,0 @@
1
- import * as React from 'react';
2
- import { DateTime, Info } from 'luxon';
3
- import Base, { Props as BaseProps } from '../Base/Base';
4
- import Flex from '../Flex/Flex';
5
- import Icon from '../../Components/Icon/Icon';
6
- import Link from '../../Components/Link/Link';
7
- import Text from '../../Components/Text/Text';
8
- import DatePickerGrid from './DatePickerGrid';
9
- import DatePickerDay from './DatePickerDay';
10
-
11
- const daysOfTheWeek = Info.weekdays('short');
12
- const daysCount = 6 * 7;
13
-
14
- interface Props extends BaseProps {
15
- dateSelectableFrom?: DateTime;
16
- dateSelectableTo?: DateTime;
17
- dateSelectedFrom?: DateTime;
18
- dateSelectedTo?: DateTime;
19
- onNextMonth: () => void;
20
- onPreviousMonth: () => void;
21
- onSelect: (date: DateTime) => void;
22
- viewDate: DateTime;
23
- }
24
-
25
- const DatePickerMonth: React.FunctionComponent<Props> = (props: Props) => {
26
- const {
27
- dateSelectableFrom,
28
- dateSelectableTo,
29
- dateSelectedFrom,
30
- dateSelectedTo,
31
- onNextMonth,
32
- onPreviousMonth,
33
- onSelect,
34
- viewDate,
35
- ...rest
36
- } = props;
37
-
38
- return (
39
- <Base {...rest}>
40
- <Flex alignChildren="middle" direction="horizontal" gap="x4" margin="x4" paddingHorizontal="x12">
41
- <Flex>
42
- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
43
- <Link as="button" onClick={onPreviousMonth}>
44
- <Icon name="ChevronLeft" />
45
- </Link>
46
- </Flex>
47
-
48
- <Flex grow>
49
- <Text align="center">{viewDate.toFormat('MMMM yyyy')}</Text>
50
- </Flex>
51
-
52
- <Flex>
53
- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
54
- <Link as="button" onClick={onNextMonth}>
55
- <Icon name="ChevronRight" />
56
- </Link>
57
- </Flex>
58
- </Flex>
59
-
60
- <DatePickerGrid marginY="4">
61
- {daysOfTheWeek.map((day) => (
62
- <Text textAlign="center" key={day} paddingY="16" size="2" color="neutral.70" textTransform="uppercase">
63
- {day}
64
- </Text>
65
- ))}
66
- </DatePickerGrid>
67
-
68
- <DatePickerGrid borderTop="1px solid" borderTopColor="neutral.93" paddingY="8">
69
- {Array.from({ length: daysCount }).map((_, i) => (
70
- <DatePickerDay
71
- dateSelectableFrom={dateSelectableFrom}
72
- dateSelectableTo={dateSelectableTo}
73
- dateSelectedFrom={dateSelectedFrom}
74
- dateSelectedTo={dateSelectedTo}
75
- // eslint-disable-next-line react/no-array-index-key
76
- key={i}
77
- n={i + 1}
78
- onSelect={onSelect}
79
- viewDate={viewDate}
80
- />
81
- ))}
82
- </DatePickerGrid>
83
- </Base>
84
- );
85
- };
86
-
87
- export default DatePickerMonth;
@@ -1,91 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-use-before-define */
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
- import { useEffect, useRef, useState } from 'react';
4
-
5
- interface MatchPatternConfig {
6
- [key: string]: any;
7
- }
8
-
9
- interface Listener {
10
- listener: MediaQueryList;
11
- media: string;
12
- query: string;
13
- }
14
-
15
- type Handler = (e: MediaQueryListEvent) => void;
16
-
17
- const getListeners = (queries: string[], handler: Handler) => queries.map((query) => {
18
- const media = `(min-width: ${query})`;
19
- const listener = window.matchMedia(media);
20
-
21
- listener.addListener(handler);
22
-
23
- return { listener, media, query };
24
- });
25
-
26
- const getMatchQueryIndex = (queries: string[], listeners: Listener[]) => {
27
- for (let i = queries.length; i--;) {
28
- if (listeners[i].listener.matches) {
29
- return queries.indexOf(listeners[i].query);
30
- }
31
- }
32
-
33
- return -1;
34
- };
35
-
36
- const removeListeners = (listeners: Listener[], handler: Handler) => {
37
- listeners.forEach(({ listener }) => {
38
- listener.removeListener(handler);
39
- });
40
- };
41
-
42
- export default (queries: string[]) => {
43
- const handleQueryChange = (event: MediaQueryListEvent) => {
44
- const listener = listeners.current.find(({ media }) => media === event.media);
45
-
46
- if (listener && hasMounted.current) {
47
- setHitIndex(getMatchQueryIndex(queries, listeners.current));
48
- }
49
- };
50
-
51
- const hasMounted = useRef(false);
52
- const listeners = useRef<Listener[]>(getListeners(queries, handleQueryChange));
53
- const [hitIndex, setHitIndex] = useState(getMatchQueryIndex(queries, listeners.current));
54
-
55
- useEffect(() => {
56
- if (!hasMounted.current) {
57
- hasMounted.current = true;
58
- } else {
59
- removeListeners(listeners.current, handleQueryChange);
60
- listeners.current = getListeners(queries, handleQueryChange);
61
- setHitIndex(getMatchQueryIndex(queries, listeners.current));
62
- }
63
-
64
- return () => {
65
- removeListeners(listeners.current, handleQueryChange);
66
- };
67
- }, [...queries]);
68
-
69
- useEffect(() => () => {
70
- hasMounted.current = false;
71
- }, []);
72
-
73
- function match(pattern: string, fallback?: never): boolean;
74
- function match(pattern: MatchPatternConfig, fallback?: string): any;
75
- function match(pattern: string | MatchPatternConfig, fallback?: string): any {
76
- if (typeof pattern === 'string') {
77
- const patternIndex = queries.indexOf(pattern);
78
- return patternIndex >= 0 && patternIndex <= hitIndex;
79
- }
80
-
81
- for (let i = hitIndex + 1; i--;) {
82
- if (queries[i] in pattern) {
83
- return pattern[queries[i]];
84
- }
85
- }
86
-
87
- return fallback;
88
- }
89
-
90
- return match;
91
- };