@spaced-out/ui-design-system 0.1.92 → 0.1.93-beta.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 (38) hide show
  1. package/.cspell/custom-words.txt +41 -0
  2. package/CHANGELOG.md +12 -0
  3. package/lib/components/DateRangePicker/Calendar.js +75 -0
  4. package/lib/components/DateRangePicker/Calendar.js.flow +113 -0
  5. package/lib/components/DateRangePicker/Calendar.module.css +49 -0
  6. package/lib/components/DateRangePicker/DateRangePicker.js +120 -0
  7. package/lib/components/DateRangePicker/DateRangePicker.js.flow +173 -0
  8. package/lib/components/DateRangePicker/DateRangePicker.module.css +8 -0
  9. package/lib/components/DateRangePicker/DateRangeWrapper.js +175 -0
  10. package/lib/components/DateRangePicker/DateRangeWrapper.js.flow +282 -0
  11. package/lib/components/DateRangePicker/DateRangeWrapper.module.css +64 -0
  12. package/lib/components/DateRangePicker/Day.js +65 -0
  13. package/lib/components/DateRangePicker/Day.js.flow +80 -0
  14. package/lib/components/DateRangePicker/Day.module.css +91 -0
  15. package/lib/components/DateRangePicker/index.js +16 -0
  16. package/lib/components/DateRangePicker/index.js.flow +3 -0
  17. package/lib/components/DateRangePicker/timezones.js +262 -0
  18. package/lib/components/DateRangePicker/timezones.js.flow +256 -0
  19. package/lib/components/DateRangePicker/utils.js +218 -0
  20. package/lib/components/DateRangePicker/utils.js.flow +231 -0
  21. package/lib/components/Dropdown/Dropdown.js +7 -1
  22. package/lib/components/Dropdown/Dropdown.js.flow +7 -0
  23. package/lib/components/SpiderChart/SpiderChart.js +1 -20
  24. package/lib/components/SpiderChart/SpiderChart.js.flow +0 -20
  25. package/lib/components/index.js +11 -0
  26. package/lib/components/index.js.flow +1 -0
  27. package/lib/types/charts.js.flow +1 -0
  28. package/lib/utils/charts/charts.js +3 -0
  29. package/lib/utils/charts/charts.js.flow +3 -0
  30. package/lib/utils/charts/columnChart.js +1 -0
  31. package/lib/utils/charts/columnChart.js.flow +1 -0
  32. package/lib/utils/charts/lineChart.js +1 -0
  33. package/lib/utils/charts/lineChart.js.flow +1 -0
  34. package/lib/utils/charts/spiderChart.js +3 -1
  35. package/lib/utils/charts/spiderChart.js.flow +4 -17
  36. package/lib/utils/charts/typography.js +2 -3
  37. package/lib/utils/charts/typography.js.flow +1 -3
  38. package/package.json +2 -1
@@ -70,3 +70,44 @@ VishalBarnawal
70
70
  xmark
71
71
  yourcomponentname
72
72
  yxxx
73
+ Pago
74
+ Rica
75
+ Cuiaba
76
+ Branco
77
+ Manaus
78
+ Velho
79
+ Santo
80
+ Araguaina
81
+ Paulo
82
+ Rothera
83
+ Noronha
84
+ Scoresbysund
85
+ Aaiun
86
+ Danmarkshavn
87
+ Syowa
88
+ Mahe
89
+ Aqtau
90
+ Aqtobe
91
+ Hovd
92
+ Minh
93
+ Choibalsan
94
+ Kuala
95
+ Lumpur
96
+ Tongatapu
97
+ Kiritimati
98
+ Fakaofo
99
+ Enderbury
100
+ Kamchatskiy
101
+ Pohnpei
102
+ Kosrae
103
+ Efate
104
+ Moresby
105
+ Chuuk
106
+ Sakhalin
107
+ Ulaanbaatar
108
+ Dumont
109
+ Dili
110
+ Urville
111
+ D'Urville
112
+ Magadan
113
+ Yuzhno
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.1.93-beta.0](https://github.com/spaced-out/ui-design-system/compare/v0.1.92...v0.1.93-beta.0) (2024-04-29)
6
+
7
+
8
+ ### Features
9
+
10
+ * [GDS-367] Date Range Picker ([#195](https://github.com/spaced-out/ui-design-system/issues/195)) ([19626ca](https://github.com/spaced-out/ui-design-system/commit/19626ca27c6f2129a2a414bb7018c7e6127ae22e))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **SpiderChart.js:** added categories in the chart ([#196](https://github.com/spaced-out/ui-design-system/issues/196)) ([7cf85d6](https://github.com/spaced-out/ui-design-system/commit/7cf85d6984b4830ad4751598218a522e68446309))
16
+
5
17
  ### [0.1.92](https://github.com/spaced-out/ui-design-system/compare/v0.1.91...v0.1.92) (2024-04-28)
6
18
 
7
19
 
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Calendar = void 0;
7
+ var React = _interopRequireWildcard(require("react"));
8
+ var _momentTimezone = _interopRequireDefault(require("moment-timezone"));
9
+ var _Text = require("../Text");
10
+ var _Day = require("./Day");
11
+ var _utils = require("./utils");
12
+ var _CalendarModule = _interopRequireDefault(require("./Calendar.module.css"));
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
16
+
17
+ // $FlowFixMe[untyped-import]
18
+
19
+ const getFormattedDate = (marker, dateRange) => {
20
+ const {
21
+ startDate,
22
+ endDate
23
+ } = dateRange;
24
+ switch (marker) {
25
+ case _utils.MARKERS.DATE_RANGE_START:
26
+ return startDate ? (0, _momentTimezone.default)(startDate).format('MMMM D, YYYY') : 'Start Date';
27
+ default:
28
+ return endDate ? (0, _momentTimezone.default)(endDate).format('MMMM D, YYYY') : 'End Date';
29
+ }
30
+ };
31
+ const Calendar = _ref => {
32
+ let {
33
+ value,
34
+ marker,
35
+ handlers,
36
+ hoverDay,
37
+ dateRange,
38
+ inHoverRange
39
+ } = _ref;
40
+ return /*#__PURE__*/React.createElement("div", {
41
+ className: _CalendarModule.default.calendar
42
+ }, /*#__PURE__*/React.createElement(_Text.SubTitleExtraSmall, {
43
+ className: _CalendarModule.default.selectedDate,
44
+ color: _Text.TEXT_COLORS.secondary
45
+ }, getFormattedDate(marker, dateRange)), /*#__PURE__*/React.createElement("div", {
46
+ className: _CalendarModule.default.calendarRow
47
+ }, _utils.WEEKDAYS.map(day => /*#__PURE__*/React.createElement(_Text.BodySmall, {
48
+ key: day,
49
+ className: _CalendarModule.default.calendarRowItem,
50
+ color: _Text.TEXT_COLORS.tertiary
51
+ }, day))), (0, _utils.getDaysInMonth)(value).map((week, index) => /*#__PURE__*/React.createElement("div", {
52
+ key: week[index],
53
+ className: _CalendarModule.default.calendarRow
54
+ }, week.map(date => {
55
+ const isStart = (0, _utils.isStartOfRange)(dateRange, date);
56
+ const isEnd = (0, _utils.isEndOfRange)(dateRange, date);
57
+ const isRangeValid = (0, _utils.isStartDateEndDateSame)(dateRange);
58
+ const highlighted = (0, _utils.inDateRange)(dateRange, date) || inHoverRange(date);
59
+ return /*#__PURE__*/React.createElement(_Day.Day, {
60
+ key: date,
61
+ date: date,
62
+ filled: isStart || isEnd,
63
+ onClick: () => handlers.onDayClick(date),
64
+ onHover: () => handlers.onDayHover(date),
65
+ outlined: (0, _momentTimezone.default)(date).isSame((0, _momentTimezone.default)(), 'd'),
66
+ disabled: (0, _momentTimezone.default)(date).isAfter((0, _utils.formatIsoDate)()) || !(0, _momentTimezone.default)(date).isSame((0, _momentTimezone.default)(value), 'month'),
67
+ endOfRange: isEnd && !isRangeValid,
68
+ highlighted: highlighted && !isRangeValid,
69
+ startOfRange: isStart && !isRangeValid,
70
+ value: (0, _momentTimezone.default)(date).date(),
71
+ hoverDay: hoverDay
72
+ });
73
+ }))));
74
+ };
75
+ exports.Calendar = Calendar;
@@ -0,0 +1,113 @@
1
+ // @flow strict
2
+
3
+ import * as React from 'react';
4
+ // $FlowFixMe[untyped-import]
5
+ import moment from 'moment-timezone';
6
+
7
+ import {BodySmall, SubTitleExtraSmall, TEXT_COLORS} from '../Text';
8
+
9
+ import {Day} from './Day';
10
+ import {
11
+ type DateRange,
12
+ formatIsoDate,
13
+ getDaysInMonth,
14
+ inDateRange,
15
+ isEndOfRange,
16
+ isStartDateEndDateSame,
17
+ isStartOfRange,
18
+ MARKERS,
19
+ NAVIGATION_ACTION,
20
+ WEEKDAYS,
21
+ } from './utils';
22
+
23
+ import css from './Calendar.module.css';
24
+
25
+
26
+ type CalendarProps = {
27
+ value: string,
28
+ marker: $Values<typeof MARKERS>,
29
+ hoverDay: string,
30
+ dateRange: DateRange,
31
+ inHoverRange: (day: string) => boolean,
32
+ handlers: {
33
+ onDayClick: (day: string) => void,
34
+ onDayHover: (day: string) => void,
35
+ onMonthNavigate: (
36
+ marker: $Values<typeof MARKERS>,
37
+ action: $Values<typeof NAVIGATION_ACTION>,
38
+ ) => void,
39
+ },
40
+ };
41
+
42
+ const getFormattedDate = (marker: string, dateRange: DateRange) => {
43
+ const {startDate, endDate} = dateRange;
44
+ switch (marker) {
45
+ case MARKERS.DATE_RANGE_START:
46
+ return startDate
47
+ ? moment(startDate).format('MMMM D, YYYY')
48
+ : 'Start Date';
49
+ default:
50
+ return endDate ? moment(endDate).format('MMMM D, YYYY') : 'End Date';
51
+ }
52
+ };
53
+
54
+ export const Calendar = ({
55
+ value,
56
+ marker,
57
+ handlers,
58
+ hoverDay,
59
+ dateRange,
60
+ inHoverRange,
61
+ }: CalendarProps): React.Node => (
62
+ <div className={css.calendar}>
63
+ <SubTitleExtraSmall
64
+ className={css.selectedDate}
65
+ color={TEXT_COLORS.secondary}
66
+ >
67
+ {getFormattedDate(marker, dateRange)}
68
+ </SubTitleExtraSmall>
69
+
70
+ <div className={css.calendarRow}>
71
+ {WEEKDAYS.map((day) => (
72
+ <BodySmall
73
+ key={day}
74
+ className={css.calendarRowItem}
75
+ color={TEXT_COLORS.tertiary}
76
+ >
77
+ {day}
78
+ </BodySmall>
79
+ ))}
80
+ </div>
81
+
82
+ {getDaysInMonth(value).map((week, index) => (
83
+ <div key={week[index]} className={css.calendarRow}>
84
+ {week.map((date) => {
85
+ const isStart = isStartOfRange(dateRange, date);
86
+ const isEnd = isEndOfRange(dateRange, date);
87
+ const isRangeValid = isStartDateEndDateSame(dateRange);
88
+ const highlighted =
89
+ inDateRange(dateRange, date) || inHoverRange(date);
90
+ return (
91
+ <Day
92
+ key={date}
93
+ date={date}
94
+ filled={isStart || isEnd}
95
+ onClick={() => handlers.onDayClick(date)}
96
+ onHover={() => handlers.onDayHover(date)}
97
+ outlined={moment(date).isSame(moment(), 'd')}
98
+ disabled={
99
+ moment(date).isAfter(formatIsoDate()) ||
100
+ !moment(date).isSame(moment(value), 'month')
101
+ }
102
+ endOfRange={isEnd && !isRangeValid}
103
+ highlighted={highlighted && !isRangeValid}
104
+ startOfRange={isStart && !isRangeValid}
105
+ value={moment(date).date()}
106
+ hoverDay={hoverDay}
107
+ />
108
+ );
109
+ })}
110
+ </div>
111
+ ))}
112
+ </div>
113
+ );
@@ -0,0 +1,49 @@
1
+ @value (
2
+ size5,
3
+ size30
4
+ ) from '../../styles/variables/_size.css';
5
+ @value (
6
+ spaceNone,
7
+ spaceSmall,
8
+ spaceLarge,
9
+ spaceFluid,
10
+ spaceXSmall,
11
+ spaceHalfFluid
12
+ ) from '../../styles/variables/_space.css';
13
+ @value (
14
+ borderRadiusCircle
15
+ ) from '../../styles/variables/_border.css';
16
+
17
+ .calendar {
18
+ display: flex;
19
+ flex-direction: column;
20
+ justify-content: flex-start;
21
+ width: spaceHalfFluid;
22
+ padding: spaceXSmall spaceLarge;
23
+ gap: spaceNone;
24
+ }
25
+
26
+ .calendarRow {
27
+ display: grid;
28
+ grid-template-columns: repeat(7, 1fr);
29
+ height: calc(size30 + size2);
30
+ box-sizing: border-box;
31
+ }
32
+
33
+ .calendarRowItem {
34
+ justify-content: center;
35
+ align-self: center;
36
+ width: spaceFluid;
37
+ padding: size5;
38
+ cursor: pointer;
39
+ border-radius: borderRadiusCircle;
40
+ box-sizing: border-box;
41
+ width: calc(size30 + size2);
42
+ height: calc(size30 + size2);
43
+ }
44
+
45
+ .selectedDate {
46
+ display: flex;
47
+ justify-content: center;
48
+ margin: spaceSmall spaceNone;
49
+ }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DateRangePicker = void 0;
7
+ var React = _interopRequireWildcard(require("react"));
8
+ var _momentTimezone = _interopRequireDefault(require("moment-timezone"));
9
+ var _classify = _interopRequireDefault(require("../../utils/classify"));
10
+ var _DateRangeWrapper = require("./DateRangeWrapper");
11
+ var _utils = require("./utils");
12
+ var _DateRangePickerModule = _interopRequireDefault(require("./DateRangePicker.module.css"));
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
16
+
17
+ // $FlowFixMe[untyped-import]
18
+
19
+ const DateRangePicker = /*#__PURE__*/React.forwardRef((_ref, ref) => {
20
+ let {
21
+ onApply,
22
+ onCancel,
23
+ classNames,
24
+ initialDateRange
25
+ } = _ref;
26
+ const today = (0, _utils.formatIsoDate)();
27
+ let initialFirstMonth = (0, _utils.getSubtractedDate)(today, 1, 'M'),
28
+ initialSecondMonth = today,
29
+ initialRangeStart = '',
30
+ initialRangeEnd = '';
31
+ if (initialDateRange?.startDate && initialDateRange.endDate) {
32
+ initialRangeStart = initialFirstMonth = (0, _utils.formatIsoDate)((0, _momentTimezone.default)(initialDateRange.startDate));
33
+ initialRangeEnd = initialSecondMonth = (0, _utils.formatIsoDate)((0, _momentTimezone.default)(initialDateRange.endDate));
34
+
35
+ // If start and end dates are in the same current month, render them in the second calendar since next month isn't enabled yet.
36
+ if ((0, _momentTimezone.default)(initialRangeStart).isSame((0, _momentTimezone.default)(initialRangeEnd), 'M') && (0, _momentTimezone.default)(initialRangeStart).isSame((0, _momentTimezone.default)(today), 'M')) {
37
+ initialFirstMonth = (0, _utils.getSubtractedDate)(initialSecondMonth, 1, 'M');
38
+ } else {
39
+ initialSecondMonth = (0, _utils.getAddedDate)(initialFirstMonth, 1, 'M');
40
+ }
41
+ }
42
+ const [dateRange, setDateRange] = React.useState({
43
+ startDate: initialRangeStart,
44
+ endDate: initialRangeEnd
45
+ });
46
+ const [rangeStartMonth, setRangeStartMonth] = React.useState(initialFirstMonth);
47
+ const [rangeEndMonth, setRangeEndMonth] = React.useState(initialSecondMonth);
48
+ const [hoverDay, setHoverDay] = React.useState('');
49
+ const [timeZone, setTimeZone] = React.useState(_momentTimezone.default.tz.guess());
50
+ const {
51
+ startDate,
52
+ endDate
53
+ } = dateRange;
54
+ const setRangeStartMonthValidated = date => {
55
+ if ((0, _momentTimezone.default)(date).isBefore(rangeEndMonth)) {
56
+ setRangeStartMonth(date);
57
+ } else {
58
+ setRangeStartMonth((0, _utils.getSubtractedDate)(rangeEndMonth, 1, 'M'));
59
+ }
60
+ };
61
+ const setRangeEndMonthValidated = date => {
62
+ if ((0, _momentTimezone.default)(date).isAfter(rangeStartMonth)) {
63
+ setRangeEndMonth(date);
64
+ } else {
65
+ setRangeEndMonth((0, _utils.getAddedDate)(rangeStartMonth, 1, 'M'));
66
+ }
67
+ };
68
+ const onDayClick = day => {
69
+ if (startDate && !endDate && (0, _momentTimezone.default)(day).isSameOrAfter(startDate)) {
70
+ setDateRange({
71
+ startDate,
72
+ endDate: day
73
+ });
74
+ } else {
75
+ setDateRange({
76
+ startDate: day,
77
+ endDate: undefined
78
+ });
79
+ }
80
+ setHoverDay(day);
81
+ };
82
+ const onMonthNavigate = (marker, action) => {
83
+ const [firstMonth, secondMonth] = marker === _utils.MARKERS.DATE_RANGE_START ? [rangeStartMonth, rangeEndMonth] : [rangeEndMonth, rangeStartMonth];
84
+ const newMonth = action === _utils.NAVIGATION_ACTION.NEXT ? (0, _utils.getAddedDate)(firstMonth, 1, 'M') : (0, _utils.getSubtractedDate)(firstMonth, 1, 'M');
85
+ const isRangeStartMonth = marker === _utils.MARKERS.DATE_RANGE_START;
86
+ const isBeforeCurrentMonth = (0, _momentTimezone.default)(newMonth).isBefore((0, _momentTimezone.default)());
87
+ const isValidMonth = isRangeStartMonth ? (0, _momentTimezone.default)(newMonth).isBefore((0, _momentTimezone.default)(secondMonth)) : (0, _momentTimezone.default)(secondMonth).isBefore((0, _momentTimezone.default)(newMonth));
88
+ if (isBeforeCurrentMonth && isValidMonth) {
89
+ isRangeStartMonth ? setRangeStartMonth(newMonth) : setRangeEndMonth(newMonth);
90
+ }
91
+ };
92
+ const onDayHover = date => {
93
+ if (startDate && !endDate && (!hoverDay || date !== hoverDay)) {
94
+ setHoverDay(date);
95
+ }
96
+ };
97
+ const inHoverRange = day => Boolean(startDate && !endDate && hoverDay && (0, _momentTimezone.default)(hoverDay).isAfter((0, _momentTimezone.default)(startDate)) && (0, _momentTimezone.default)(day).isBetween((0, _momentTimezone.default)(startDate), (0, _momentTimezone.default)(hoverDay)));
98
+ const handlers = {
99
+ onDayClick,
100
+ onDayHover,
101
+ onMonthNavigate
102
+ };
103
+ return /*#__PURE__*/React.createElement(_DateRangeWrapper.DateRangeWrapper, {
104
+ ref: ref,
105
+ onApply: onApply,
106
+ handlers: handlers,
107
+ hoverDay: hoverDay,
108
+ onCancel: onCancel,
109
+ timeZone: timeZone,
110
+ dateRange: dateRange,
111
+ rangeStartMonth: rangeStartMonth,
112
+ setTimeZone: setTimeZone,
113
+ rangeEndMonth: rangeEndMonth,
114
+ inHoverRange: inHoverRange,
115
+ setRangeStartMonth: setRangeStartMonthValidated,
116
+ setRangeEndMonth: setRangeEndMonthValidated,
117
+ cardWrapperClass: (0, _classify.default)(_DateRangePickerModule.default.container, classNames?.wrapper)
118
+ });
119
+ });
120
+ exports.DateRangePicker = DateRangePicker;
@@ -0,0 +1,173 @@
1
+ // @flow strict
2
+
3
+ import * as React from 'react';
4
+ // $FlowFixMe[untyped-import]
5
+ import moment from 'moment-timezone';
6
+
7
+ import classify from '../../utils/classify';
8
+
9
+ import {DateRangeWrapper} from './DateRangeWrapper';
10
+ import {
11
+ type DatePickerSelectedRange,
12
+ type DateRange,
13
+ formatIsoDate,
14
+ getAddedDate,
15
+ getSubtractedDate,
16
+ MARKERS,
17
+ NAVIGATION_ACTION,
18
+ } from './utils';
19
+
20
+ import css from './DateRangePicker.module.css';
21
+
22
+
23
+ type ClassNames = $ReadOnly<{wrapper?: string}>;
24
+
25
+ export type DateRangePickerProps = {
26
+ classNames?: ClassNames,
27
+ initialDateRange?: DateRange,
28
+ onApply: (datePickerSelectedRange: DatePickerSelectedRange) => void,
29
+ onCancel: () => void,
30
+ };
31
+
32
+ export const DateRangePicker: React$AbstractComponent<
33
+ DateRangePickerProps,
34
+ HTMLDivElement,
35
+ > = React.forwardRef<DateRangePickerProps, HTMLDivElement>(
36
+ (
37
+ {onApply, onCancel, classNames, initialDateRange}: DateRangePickerProps,
38
+ ref,
39
+ ): React.Node => {
40
+ const today = formatIsoDate();
41
+
42
+ let initialFirstMonth = getSubtractedDate(today, 1, 'M'),
43
+ initialSecondMonth = today,
44
+ initialRangeStart = '',
45
+ initialRangeEnd = '';
46
+
47
+ if (initialDateRange?.startDate && initialDateRange.endDate) {
48
+ initialRangeStart = initialFirstMonth = formatIsoDate(
49
+ moment(initialDateRange.startDate),
50
+ );
51
+ initialRangeEnd = initialSecondMonth = formatIsoDate(
52
+ moment(initialDateRange.endDate),
53
+ );
54
+
55
+ // If start and end dates are in the same current month, render them in the second calendar since next month isn't enabled yet.
56
+ if (
57
+ moment(initialRangeStart).isSame(moment(initialRangeEnd), 'M') &&
58
+ moment(initialRangeStart).isSame(moment(today), 'M')
59
+ ) {
60
+ initialFirstMonth = getSubtractedDate(initialSecondMonth, 1, 'M');
61
+ } else {
62
+ initialSecondMonth = getAddedDate(initialFirstMonth, 1, 'M');
63
+ }
64
+ }
65
+
66
+ const [dateRange, setDateRange] = React.useState<DateRange>({
67
+ startDate: initialRangeStart,
68
+ endDate: initialRangeEnd,
69
+ });
70
+
71
+ const [rangeStartMonth, setRangeStartMonth] =
72
+ React.useState<string>(initialFirstMonth);
73
+ const [rangeEndMonth, setRangeEndMonth] =
74
+ React.useState<string>(initialSecondMonth);
75
+
76
+ const [hoverDay, setHoverDay] = React.useState<string>('');
77
+ const [timeZone, setTimeZone] = React.useState<string>(moment.tz.guess());
78
+
79
+ const {startDate, endDate} = dateRange;
80
+
81
+ const setRangeStartMonthValidated = (date: string) => {
82
+ if (moment(date).isBefore(rangeEndMonth)) {
83
+ setRangeStartMonth(date);
84
+ } else {
85
+ setRangeStartMonth(getSubtractedDate(rangeEndMonth, 1, 'M'));
86
+ }
87
+ };
88
+
89
+ const setRangeEndMonthValidated = (date: string) => {
90
+ if (moment(date).isAfter(rangeStartMonth)) {
91
+ setRangeEndMonth(date);
92
+ } else {
93
+ setRangeEndMonth(getAddedDate(rangeStartMonth, 1, 'M'));
94
+ }
95
+ };
96
+
97
+ const onDayClick = (day: string) => {
98
+ if (startDate && !endDate && moment(day).isSameOrAfter(startDate)) {
99
+ setDateRange({startDate, endDate: day});
100
+ } else {
101
+ setDateRange({startDate: day, endDate: undefined});
102
+ }
103
+ setHoverDay(day);
104
+ };
105
+
106
+ const onMonthNavigate = (
107
+ marker: $Values<typeof MARKERS>,
108
+ action: $Values<typeof NAVIGATION_ACTION>,
109
+ ) => {
110
+ const [firstMonth, secondMonth] =
111
+ marker === MARKERS.DATE_RANGE_START
112
+ ? [rangeStartMonth, rangeEndMonth]
113
+ : [rangeEndMonth, rangeStartMonth];
114
+
115
+ const newMonth =
116
+ action === NAVIGATION_ACTION.NEXT
117
+ ? getAddedDate(firstMonth, 1, 'M')
118
+ : getSubtractedDate(firstMonth, 1, 'M');
119
+
120
+ const isRangeStartMonth = marker === MARKERS.DATE_RANGE_START;
121
+ const isBeforeCurrentMonth = moment(newMonth).isBefore(moment());
122
+ const isValidMonth = isRangeStartMonth
123
+ ? moment(newMonth).isBefore(moment(secondMonth))
124
+ : moment(secondMonth).isBefore(moment(newMonth));
125
+
126
+ if (isBeforeCurrentMonth && isValidMonth) {
127
+ isRangeStartMonth
128
+ ? setRangeStartMonth(newMonth)
129
+ : setRangeEndMonth(newMonth);
130
+ }
131
+ };
132
+
133
+ const onDayHover = (date: string) => {
134
+ if (startDate && !endDate && (!hoverDay || date !== hoverDay)) {
135
+ setHoverDay(date);
136
+ }
137
+ };
138
+
139
+ const inHoverRange = (day: string): boolean =>
140
+ Boolean(
141
+ startDate &&
142
+ !endDate &&
143
+ hoverDay &&
144
+ moment(hoverDay).isAfter(moment(startDate)) &&
145
+ moment(day).isBetween(moment(startDate), moment(hoverDay)),
146
+ );
147
+
148
+ const handlers = {
149
+ onDayClick,
150
+ onDayHover,
151
+ onMonthNavigate,
152
+ };
153
+
154
+ return (
155
+ <DateRangeWrapper
156
+ ref={ref}
157
+ onApply={onApply}
158
+ handlers={handlers}
159
+ hoverDay={hoverDay}
160
+ onCancel={onCancel}
161
+ timeZone={timeZone}
162
+ dateRange={dateRange}
163
+ rangeStartMonth={rangeStartMonth}
164
+ setTimeZone={setTimeZone}
165
+ rangeEndMonth={rangeEndMonth}
166
+ inHoverRange={inHoverRange}
167
+ setRangeStartMonth={setRangeStartMonthValidated}
168
+ setRangeEndMonth={setRangeEndMonthValidated}
169
+ cardWrapperClass={classify(css.container, classNames?.wrapper)}
170
+ />
171
+ );
172
+ },
173
+ );
@@ -0,0 +1,8 @@
1
+ @value (
2
+ size580
3
+ ) from '../../styles/variables/_size.css';
4
+
5
+ .container {
6
+ display: flex;
7
+ width: size580;
8
+ }