@mtes-mct/monitor-ui 1.1.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/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildFormatLongFn/index.js +12 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildFormatLongFn/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildLocalizeFn/index.js +25 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildLocalizeFn/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildMatchFn/index.js +51 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildMatchFn/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildMatchPatternFn/index.js +20 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/_lib/buildMatchPatternFn/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/formatDistance/index.js +89 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/formatDistance/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/formatLong/index.js +37 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/formatLong/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/formatRelative/index.js +15 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/formatRelative/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/localize/index.js +101 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/localize/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/match/index.js +101 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/_lib/match/index.js.map +1 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/index.js +34 -0
- package/.yarn/cache/date-fns-npm-2.29.3-fef7e3c72c-e01cf5b62a.zip/node_modules/date-fns/esm/locale/fr/index.js.map +1 -0
- package/.yarn/cache/dayjs-npm-1.11.5-a825142dc5-e3bbaa7b48.zip/node_modules/dayjs/locale/fr.js +8 -0
- package/.yarn/cache/dayjs-npm-1.11.5-a825142dc5-e3bbaa7b48.zip/node_modules/dayjs/locale/fr.js.map +1 -0
- package/.yarn/cache/dayjs-npm-1.11.5-a825142dc5-e3bbaa7b48.zip/node_modules/dayjs/plugin/timezone.js +11 -0
- package/.yarn/cache/dayjs-npm-1.11.5-a825142dc5-e3bbaa7b48.zip/node_modules/dayjs/plugin/timezone.js.map +1 -0
- package/.yarn/cache/dayjs-npm-1.11.5-a825142dc5-e3bbaa7b48.zip/node_modules/dayjs/plugin/utc.js +11 -0
- package/.yarn/cache/dayjs-npm-1.11.5-a825142dc5-e3bbaa7b48.zip/node_modules/dayjs/plugin/utc.js.map +1 -0
- package/.yarn/cache/lodash.throttle-npm-4.1.1-856641af92-129c0a28ce.zip/node_modules/lodash.throttle/index.js +444 -0
- package/.yarn/cache/lodash.throttle-npm-4.1.1-856641af92-129c0a28ce.zip/node_modules/lodash.throttle/index.js.map +1 -0
- package/CHANGELOG.md +15 -0
- package/LICENSE +661 -0
- package/README.md +25 -0
- package/ThemeProvider.d.ts +7 -0
- package/_virtual/_commonjsHelpers.js +4 -0
- package/_virtual/_commonjsHelpers.js.map +1 -0
- package/_virtual/fr.js +4 -0
- package/_virtual/fr.js.map +1 -0
- package/_virtual/timezone.js +4 -0
- package/_virtual/timezone.js.map +1 -0
- package/_virtual/utc.js +4 -0
- package/_virtual/utc.js.map +1 -0
- package/constants.d.ts +1 -0
- package/fields/DatePicker/CalendarPicker.d.ts +9 -0
- package/fields/DatePicker/index.d.ts +23 -0
- package/fields/DateRangePicker/DateInput.d.ts +22 -0
- package/fields/DateRangePicker/NumberInput.d.ts +32 -0
- package/fields/DateRangePicker/RangeCalendarPicker.d.ts +10 -0
- package/fields/DateRangePicker/RangedTimePicker.d.ts +9 -0
- package/fields/DateRangePicker/TimeInput.d.ts +24 -0
- package/fields/DateRangePicker/constants.d.ts +11 -0
- package/fields/DateRangePicker/index.d.ts +24 -0
- package/fields/DateRangePicker/types.d.ts +18 -0
- package/fields/DateRangePicker/utils.d.ts +24 -0
- package/fields/Select.d.ts +11 -0
- package/formiks/FormikEffect.d.ts +5 -0
- package/formiks/FormikSelect.d.ts +5 -0
- package/hooks/useForceUpdate.d.ts +10 -0
- package/index.d.ts +14 -0
- package/package.json +53 -0
- package/src/ThemeProvider.js +13 -0
- package/src/ThemeProvider.js.map +1 -0
- package/src/constants.js +2 -0
- package/src/constants.js.map +1 -0
- package/src/fields/DatePicker/CalendarPicker.js +137 -0
- package/src/fields/DatePicker/CalendarPicker.js.map +1 -0
- package/src/fields/DatePicker/index.js +136 -0
- package/src/fields/DatePicker/index.js.map +1 -0
- package/src/fields/DateRangePicker/DateInput.js +84 -0
- package/src/fields/DateRangePicker/DateInput.js.map +1 -0
- package/src/fields/DateRangePicker/NumberInput.js +82 -0
- package/src/fields/DateRangePicker/NumberInput.js.map +1 -0
- package/src/fields/DateRangePicker/RangeCalendarPicker.js +147 -0
- package/src/fields/DateRangePicker/RangeCalendarPicker.js.map +1 -0
- package/src/fields/DateRangePicker/RangedTimePicker.js +90 -0
- package/src/fields/DateRangePicker/RangedTimePicker.js.map +1 -0
- package/src/fields/DateRangePicker/TimeInput.js +116 -0
- package/src/fields/DateRangePicker/TimeInput.js.map +1 -0
- package/src/fields/DateRangePicker/constants.js +34 -0
- package/src/fields/DateRangePicker/constants.js.map +1 -0
- package/src/fields/DateRangePicker/index.js +197 -0
- package/src/fields/DateRangePicker/index.js.map +1 -0
- package/src/fields/DateRangePicker/types.js +8 -0
- package/src/fields/DateRangePicker/types.js.map +1 -0
- package/src/fields/DateRangePicker/utils.js +73 -0
- package/src/fields/DateRangePicker/utils.js.map +1 -0
- package/src/fields/Select.js +38 -0
- package/src/fields/Select.js.map +1 -0
- package/src/formiks/FormikEffect.js +14 -0
- package/src/formiks/FormikEffect.js.map +1 -0
- package/src/formiks/FormikSelect.js +21 -0
- package/src/formiks/FormikSelect.js.map +1 -0
- package/src/hooks/useForceUpdate.js +17 -0
- package/src/hooks/useForceUpdate.js.map +1 -0
- package/src/index.js +10 -0
- package/src/index.js.map +1 -0
- package/src/theme.js +68 -0
- package/src/theme.js.map +1 -0
- package/src/utils/capitalizeFirstLetter.js +9 -0
- package/src/utils/capitalizeFirstLetter.js.map +1 -0
- package/src/utils/dayjs.js +11 -0
- package/src/utils/dayjs.js.map +1 -0
- package/src/utils/getLocalizedDayjs.js +19 -0
- package/src/utils/getLocalizedDayjs.js.map +1 -0
- package/src/utils/getUtcDayjs.js +9 -0
- package/src/utils/getUtcDayjs.js.map +1 -0
- package/src/utils/getUtcizedDayjs.js +20 -0
- package/src/utils/getUtcizedDayjs.js.map +1 -0
- package/src/utils/sortDates.js +12 -0
- package/src/utils/sortDates.js.map +1 -0
- package/src/utils/stopMouseEventPropagation.js +6 -0
- package/src/utils/stopMouseEventPropagation.js.map +1 -0
- package/theme.d.ts +54 -0
- package/types/index.d.ts +8 -0
- package/utils/capitalizeFirstLetter.d.ts +4 -0
- package/utils/cleanInputString.d.ts +4 -0
- package/utils/dayjs.d.ts +3 -0
- package/utils/getLocalizedDayjs.d.ts +8 -0
- package/utils/getUtcDayjs.d.ts +2 -0
- package/utils/getUtcizedDayjs.d.ts +8 -0
- package/utils/sortDates.d.ts +1 -0
- package/utils/stopMouseEventPropagation.d.ts +2 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useRef, useMemo, useCallback, useEffect } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { useForceUpdate } from '../../hooks/useForceUpdate.js';
|
|
5
|
+
import { getLocalizedDayjs } from '../../utils/getLocalizedDayjs.js';
|
|
6
|
+
import { getUtcizedDayjs } from '../../utils/getUtcizedDayjs.js';
|
|
7
|
+
import { DateInput } from '../DateRangePicker/DateInput.js';
|
|
8
|
+
import { TimeInput } from '../DateRangePicker/TimeInput.js';
|
|
9
|
+
import { getDateTupleFromDate, getTimeTupleFromDate, getDateFromDateAndTimeTuple } from '../DateRangePicker/utils.js';
|
|
10
|
+
import { CalendarPicker } from './CalendarPicker.js';
|
|
11
|
+
|
|
12
|
+
function DatePicker({ defaultValue, isHistorical = false, isLabelHidden = false, label, minutesRange = 15, onChange, withTime = false }) {
|
|
13
|
+
const dateInputRef = useRef();
|
|
14
|
+
const timeInputRef = useRef();
|
|
15
|
+
const isCalendarPickerOpenRef = useRef(false);
|
|
16
|
+
const selectedDateRef = useRef(defaultValue ? getLocalizedDayjs(defaultValue).toDate() : undefined);
|
|
17
|
+
const selectedDateTupleRef = useRef(getDateTupleFromDate(selectedDateRef.current));
|
|
18
|
+
const selectedTimeTupleRef = useRef(getTimeTupleFromDate(selectedDateRef.current));
|
|
19
|
+
const { forceUpdate } = useForceUpdate();
|
|
20
|
+
const rangeCalendarPickerDefaultValue = useMemo(() => selectedDateTupleRef.current
|
|
21
|
+
? getDateFromDateAndTimeTuple(selectedDateTupleRef.current, ['00', '00'])
|
|
22
|
+
: undefined,
|
|
23
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
24
|
+
[selectedDateTupleRef.current]);
|
|
25
|
+
const submit = useCallback(() => {
|
|
26
|
+
if (!selectedDateRef.current) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const nextDate = getUtcizedDayjs(selectedDateRef.current).toDate();
|
|
30
|
+
onChange(nextDate);
|
|
31
|
+
}, [onChange]);
|
|
32
|
+
const closeCalendarPicker = useCallback(() => {
|
|
33
|
+
isCalendarPickerOpenRef.current = false;
|
|
34
|
+
forceUpdate();
|
|
35
|
+
}, [forceUpdate]);
|
|
36
|
+
const handleClickOutside = useCallback((event) => {
|
|
37
|
+
const target = event.target;
|
|
38
|
+
if (dateInputRef.current.boxSpan.contains(target)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
closeCalendarPicker();
|
|
42
|
+
}, [closeCalendarPicker]);
|
|
43
|
+
const handleDateInputNext = useCallback(() => {
|
|
44
|
+
if (!withTime) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
timeInputRef.current.focus();
|
|
48
|
+
}, [withTime]);
|
|
49
|
+
const handleDateInputFilled = useCallback((nextDateTuple) => {
|
|
50
|
+
selectedDateTupleRef.current = nextDateTuple;
|
|
51
|
+
// If there is no time input or a time has already been selected,
|
|
52
|
+
if (!withTime || selectedTimeTupleRef.current) {
|
|
53
|
+
// we must update the selected date and call onChange()
|
|
54
|
+
const timeTuple = (withTime ? selectedTimeTupleRef.current : ['00', '00']);
|
|
55
|
+
const nextDate = getDateFromDateAndTimeTuple(nextDateTuple, timeTuple);
|
|
56
|
+
selectedDateRef.current = nextDate;
|
|
57
|
+
submit();
|
|
58
|
+
}
|
|
59
|
+
handleDateInputNext();
|
|
60
|
+
}, [handleDateInputNext, submit, withTime]);
|
|
61
|
+
const handleCalendarPickerChange = useCallback((nextDateTuple) => {
|
|
62
|
+
// If this is a date picker without a time input,
|
|
63
|
+
if (!withTime) {
|
|
64
|
+
// we have to fix the date at the beginning of the day
|
|
65
|
+
const nextDate = getDateFromDateAndTimeTuple(nextDateTuple, ['00', '00']);
|
|
66
|
+
selectedDateRef.current = nextDate;
|
|
67
|
+
}
|
|
68
|
+
// If this is a date picker with a time input,
|
|
69
|
+
else {
|
|
70
|
+
// we include the selected time if it exists, set it at the beginning of the day if not
|
|
71
|
+
const nextDate = getDateFromDateAndTimeTuple(nextDateTuple, selectedTimeTupleRef.current || ['00', '00']);
|
|
72
|
+
selectedDateRef.current = nextDate;
|
|
73
|
+
}
|
|
74
|
+
selectedDateTupleRef.current = nextDateTuple;
|
|
75
|
+
selectedTimeTupleRef.current = getTimeTupleFromDate(selectedDateRef.current);
|
|
76
|
+
closeCalendarPicker();
|
|
77
|
+
forceUpdate();
|
|
78
|
+
submit();
|
|
79
|
+
}, [closeCalendarPicker, forceUpdate, submit, withTime]);
|
|
80
|
+
const handleTimeInputFilled = useCallback((nextTimeTuple) => {
|
|
81
|
+
// If a date has already been selected
|
|
82
|
+
if (selectedDateTupleRef.current) {
|
|
83
|
+
// we must update the selected date accordingly and submit it
|
|
84
|
+
const nextDate = getDateFromDateAndTimeTuple(selectedDateTupleRef.current, nextTimeTuple);
|
|
85
|
+
selectedDateRef.current = nextDate;
|
|
86
|
+
submit();
|
|
87
|
+
}
|
|
88
|
+
selectedTimeTupleRef.current = nextTimeTuple;
|
|
89
|
+
submit();
|
|
90
|
+
}, [submit]);
|
|
91
|
+
const openCalendarPicker = useCallback(() => {
|
|
92
|
+
isCalendarPickerOpenRef.current = true;
|
|
93
|
+
forceUpdate();
|
|
94
|
+
}, [forceUpdate]);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
window.document.addEventListener('click', handleClickOutside);
|
|
97
|
+
return () => {
|
|
98
|
+
window.document.removeEventListener('click', handleClickOutside);
|
|
99
|
+
};
|
|
100
|
+
}, [handleClickOutside]);
|
|
101
|
+
return (jsxs(Fieldset, { className: "DateRangePicker", children: [jsx(Legend, { isHidden: isLabelHidden, children: label }), jsxs(Box, { children: [jsx(Field, { children: jsx(DateInput, { ref: dateInputRef, defaultValue: selectedDateTupleRef.current, isForcedFocused: isCalendarPickerOpenRef.current, onChange: handleDateInputFilled, onClick: openCalendarPicker, onNext: handleDateInputNext }) }), withTime && (jsx(Field, { isTimeField: true, children: jsx(TimeInput, { ref: timeInputRef, defaultValue: selectedTimeTupleRef.current, minutesRange: minutesRange, onBack: () => dateInputRef.current.focus(true), onChange: handleTimeInputFilled, onFocus: closeCalendarPicker, onPrevious: () => dateInputRef.current.focus(true) }) }))] }), isCalendarPickerOpenRef.current && (jsx(CalendarPicker, { defaultValue: rangeCalendarPickerDefaultValue, isHistorical: isHistorical, onChange: handleCalendarPickerChange }))] }));
|
|
102
|
+
}
|
|
103
|
+
const Fieldset = styled.fieldset `
|
|
104
|
+
border: 0;
|
|
105
|
+
margin: 0;
|
|
106
|
+
padding: 0;
|
|
107
|
+
`;
|
|
108
|
+
const Box = styled.div `
|
|
109
|
+
* {
|
|
110
|
+
font-weight: 500;
|
|
111
|
+
line-height: 1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
color: ${p => p.theme.color.gunMetal};
|
|
115
|
+
font-size: 13px;
|
|
116
|
+
position: relative;
|
|
117
|
+
`;
|
|
118
|
+
const Legend = styled.legend `
|
|
119
|
+
color: ${p => p.theme.color.slateGray};
|
|
120
|
+
display: ${p => (p.isHidden ? 'none' : 'table')};
|
|
121
|
+
font-weight: inherit;
|
|
122
|
+
margin-bottom: 0.5rem;
|
|
123
|
+
padding: 0;
|
|
124
|
+
`;
|
|
125
|
+
const Field = styled.span `
|
|
126
|
+
font-size: inherit;
|
|
127
|
+
margin-left: ${p => {
|
|
128
|
+
if (p.isEndDateField) {
|
|
129
|
+
return '0.625rem';
|
|
130
|
+
}
|
|
131
|
+
return p.isTimeField ? '0.125rem' : 0;
|
|
132
|
+
}};
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
export { DatePicker };
|
|
136
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/fields/DatePicker/index.tsx"],"sourcesContent":["// TODO We should make this component both form- & a11y-compliant with a `name` and proper (aria-)labels.\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react'\nimport styled from 'styled-components'\n\nimport { useForceUpdate } from '../../hooks/useForceUpdate'\nimport { getLocalizedDayjs } from '../../utils/getLocalizedDayjs'\nimport { getUtcizedDayjs } from '../../utils/getUtcizedDayjs'\nimport { DateInput } from '../DateRangePicker/DateInput'\nimport { TimeInput } from '../DateRangePicker/TimeInput'\nimport { getDateFromDateAndTimeTuple, getDateTupleFromDate, getTimeTupleFromDate } from '../DateRangePicker/utils'\nimport { CalendarPicker } from './CalendarPicker'\n\nimport type { DateOrTimeInputRef, DateTuple, TimeTuple } from '../DateRangePicker/types'\nimport type { MutableRefObject } from 'react'\nimport type { Promisable } from 'type-fest'\n\nexport type DatePickerProps = {\n defaultValue?: Date\n /** Only allow past dates until today. */\n isHistorical?: boolean\n isLabelHidden?: boolean\n label: string\n /**\n * Range of minutes used to generate the time picker list.\n *\n * @example\n * `15` would produce a list with `..., 10:45, 11:00, 11:15, ...`.\n */\n minutesRange?: number\n /**\n * Called each time the date range picker is changed to a new valid value.\n *\n * @param nextUtcDateRange - A utcized date to be used as is to interact with the API.\n */\n onChange: (nextUtcDate: Date) => Promisable<void>\n withTime?: boolean\n}\nexport function DatePicker({\n defaultValue,\n isHistorical = false,\n isLabelHidden = false,\n label,\n minutesRange = 15,\n onChange,\n withTime = false\n}: DatePickerProps) {\n const dateInputRef = useRef() as MutableRefObject<DateOrTimeInputRef>\n const timeInputRef = useRef() as MutableRefObject<DateOrTimeInputRef>\n\n const isCalendarPickerOpenRef = useRef(false)\n\n const selectedDateRef = useRef<Date | undefined>(defaultValue ? getLocalizedDayjs(defaultValue).toDate() : undefined)\n const selectedDateTupleRef = useRef<DateTuple | undefined>(getDateTupleFromDate(selectedDateRef.current))\n const selectedTimeTupleRef = useRef<TimeTuple | undefined>(getTimeTupleFromDate(selectedDateRef.current))\n\n const { forceUpdate } = useForceUpdate()\n\n const rangeCalendarPickerDefaultValue = useMemo(\n () =>\n selectedDateTupleRef.current\n ? getDateFromDateAndTimeTuple(selectedDateTupleRef.current, ['00', '00'])\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [selectedDateTupleRef.current]\n )\n\n const submit = useCallback(() => {\n if (!selectedDateRef.current) {\n return\n }\n\n const nextDate = getUtcizedDayjs(selectedDateRef.current).toDate()\n\n onChange(nextDate)\n }, [onChange])\n\n const closeCalendarPicker = useCallback(() => {\n isCalendarPickerOpenRef.current = false\n\n forceUpdate()\n }, [forceUpdate])\n\n const handleClickOutside = useCallback(\n (event: globalThis.MouseEvent) => {\n const target = event.target as Node | null\n\n if (dateInputRef.current.boxSpan.contains(target)) {\n return\n }\n\n closeCalendarPicker()\n },\n [closeCalendarPicker]\n )\n\n const handleDateInputNext = useCallback(() => {\n if (!withTime) {\n return\n }\n\n timeInputRef.current.focus()\n }, [withTime])\n\n const handleDateInputFilled = useCallback(\n (nextDateTuple: DateTuple) => {\n selectedDateTupleRef.current = nextDateTuple\n\n // If there is no time input or a time has already been selected,\n if (!withTime || selectedTimeTupleRef.current) {\n // we must update the selected date and call onChange()\n const timeTuple = (withTime ? selectedTimeTupleRef.current : ['00', '00']) as TimeTuple\n const nextDate = getDateFromDateAndTimeTuple(nextDateTuple, timeTuple)\n\n selectedDateRef.current = nextDate\n\n submit()\n }\n\n handleDateInputNext()\n },\n [handleDateInputNext, submit, withTime]\n )\n\n const handleCalendarPickerChange = useCallback(\n (nextDateTuple: DateTuple) => {\n // If this is a date picker without a time input,\n if (!withTime) {\n // we have to fix the date at the beginning of the day\n const nextDate = getDateFromDateAndTimeTuple(nextDateTuple, ['00', '00'])\n\n selectedDateRef.current = nextDate\n }\n\n // If this is a date picker with a time input,\n else {\n // we include the selected time if it exists, set it at the beginning of the day if not\n const nextDate = getDateFromDateAndTimeTuple(nextDateTuple, selectedTimeTupleRef.current || ['00', '00'])\n selectedDateRef.current = nextDate\n }\n\n selectedDateTupleRef.current = nextDateTuple\n selectedTimeTupleRef.current = getTimeTupleFromDate(selectedDateRef.current)\n\n closeCalendarPicker()\n forceUpdate()\n\n submit()\n },\n [closeCalendarPicker, forceUpdate, submit, withTime]\n )\n\n const handleTimeInputFilled = useCallback(\n (nextTimeTuple: TimeTuple) => {\n // If a date has already been selected\n if (selectedDateTupleRef.current) {\n // we must update the selected date accordingly and submit it\n const nextDate = getDateFromDateAndTimeTuple(selectedDateTupleRef.current, nextTimeTuple)\n\n selectedDateRef.current = nextDate\n\n submit()\n }\n\n selectedTimeTupleRef.current = nextTimeTuple\n\n submit()\n },\n [submit]\n )\n\n const openCalendarPicker = useCallback(() => {\n isCalendarPickerOpenRef.current = true\n\n forceUpdate()\n }, [forceUpdate])\n\n useEffect(() => {\n window.document.addEventListener('click', handleClickOutside)\n\n return () => {\n window.document.removeEventListener('click', handleClickOutside)\n }\n }, [handleClickOutside])\n\n return (\n <Fieldset className=\"DateRangePicker\">\n <Legend isHidden={isLabelHidden}>{label}</Legend>\n\n <Box>\n <Field>\n <DateInput\n ref={dateInputRef}\n defaultValue={selectedDateTupleRef.current}\n isForcedFocused={isCalendarPickerOpenRef.current}\n onChange={handleDateInputFilled}\n onClick={openCalendarPicker}\n onNext={handleDateInputNext}\n />\n </Field>\n\n {withTime && (\n <Field isTimeField>\n <TimeInput\n ref={timeInputRef}\n defaultValue={selectedTimeTupleRef.current}\n minutesRange={minutesRange}\n onBack={() => dateInputRef.current.focus(true)}\n onChange={handleTimeInputFilled}\n onFocus={closeCalendarPicker}\n onPrevious={() => dateInputRef.current.focus(true)}\n />\n </Field>\n )}\n </Box>\n\n {isCalendarPickerOpenRef.current && (\n <CalendarPicker\n defaultValue={rangeCalendarPickerDefaultValue}\n isHistorical={isHistorical}\n onChange={handleCalendarPickerChange}\n />\n )}\n </Fieldset>\n )\n}\n\nconst Fieldset = styled.fieldset`\n border: 0;\n margin: 0;\n padding: 0;\n`\n\nconst Box = styled.div`\n * {\n font-weight: 500;\n line-height: 1;\n }\n\n color: ${p => p.theme.color.gunMetal};\n font-size: 13px;\n position: relative;\n`\n\nconst Legend = styled.legend<{\n isHidden: boolean\n}>`\n color: ${p => p.theme.color.slateGray};\n display: ${p => (p.isHidden ? 'none' : 'table')};\n font-weight: inherit;\n margin-bottom: 0.5rem;\n padding: 0;\n`\n\nconst Field = styled.span<{\n isEndDateField?: boolean\n isTimeField?: boolean\n}>`\n font-size: inherit;\n margin-left: ${p => {\n if (p.isEndDateField) {\n return '0.625rem'\n }\n\n return p.isTimeField ? '0.125rem' : 0\n }};\n`\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { forwardRef, useRef, useState, useImperativeHandle, useMemo, useCallback } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { getUtcizedDayjs } from '../../utils/getUtcizedDayjs.js';
|
|
5
|
+
import { NumberInput } from './NumberInput.js';
|
|
6
|
+
import { formatNumberAsDoubleDigit } from './utils.js';
|
|
7
|
+
|
|
8
|
+
function DateInputWithRef({ defaultValue, isEndDate = false, isForcedFocused, isStartDate = false, onBack, onChange, onClick, onNext, onPrevious }, ref) {
|
|
9
|
+
const boxSpanRef = useRef();
|
|
10
|
+
const dayInputRef = useRef();
|
|
11
|
+
const monthInputRef = useRef();
|
|
12
|
+
const yearInputRef = useRef();
|
|
13
|
+
const [hasFormatError, setHasFormatError] = useState(false);
|
|
14
|
+
const [hasValidationError, setHasValidationError] = useState(false);
|
|
15
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
16
|
+
useImperativeHandle(ref, () => ({
|
|
17
|
+
boxSpan: boxSpanRef.current,
|
|
18
|
+
focus: (isInLastInputOfTheGroup = false) => {
|
|
19
|
+
if (isInLastInputOfTheGroup) {
|
|
20
|
+
yearInputRef.current.focus();
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
dayInputRef.current.focus();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
const currentUtcYear = useMemo(() => getUtcizedDayjs().year(), []);
|
|
28
|
+
const handleBlur = useCallback(() => {
|
|
29
|
+
setIsFocused(false);
|
|
30
|
+
}, []);
|
|
31
|
+
const handleFocus = useCallback(() => {
|
|
32
|
+
setIsFocused(true);
|
|
33
|
+
}, []);
|
|
34
|
+
const handleFormatError = useCallback((hasNextFormatError) => {
|
|
35
|
+
setHasFormatError(hasNextFormatError);
|
|
36
|
+
}, []);
|
|
37
|
+
const submit = useCallback(() => {
|
|
38
|
+
setHasValidationError(false);
|
|
39
|
+
switch (window.document.activeElement) {
|
|
40
|
+
case dayInputRef.current:
|
|
41
|
+
monthInputRef.current.focus();
|
|
42
|
+
break;
|
|
43
|
+
case monthInputRef.current:
|
|
44
|
+
yearInputRef.current.focus();
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
if (!yearInputRef.current.value.length ||
|
|
48
|
+
!monthInputRef.current.value.length ||
|
|
49
|
+
!dayInputRef.current.value.length) {
|
|
50
|
+
if ((monthInputRef.current.value.length && !dayInputRef.current.value.length) ||
|
|
51
|
+
(yearInputRef.current.value.length &&
|
|
52
|
+
(!dayInputRef.current.value.length || !monthInputRef.current.value.length))) {
|
|
53
|
+
setHasValidationError(true);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const nextDateTuple = [
|
|
58
|
+
String(yearInputRef.current.value),
|
|
59
|
+
formatNumberAsDoubleDigit(monthInputRef.current.value),
|
|
60
|
+
formatNumberAsDoubleDigit(dayInputRef.current.value)
|
|
61
|
+
];
|
|
62
|
+
onChange(nextDateTuple);
|
|
63
|
+
}, [onChange]);
|
|
64
|
+
return (jsxs(Box, { ref: boxSpanRef, hasError: hasFormatError || hasValidationError, isFocused: isForcedFocused || isFocused, children: [isStartDate && 'Du ', isEndDate && 'Au ', jsx(NumberInput, { ref: dayInputRef, "data-cy": `date-range-picker-${isStartDate ? 'start' : 'end'}-day`, defaultValue: defaultValue && formatNumberAsDoubleDigit(defaultValue[2]), max: 31, min: 1, onBack: onBack, onBlur: handleBlur, onClick: onClick, onFilled: submit, onFocus: handleFocus, onFormatError: handleFormatError, onNext: () => monthInputRef.current.focus(), onPrevious: onPrevious, size: 2 }), "/", jsx(NumberInput, { ref: monthInputRef, "data-cy": `date-range-picker-${isStartDate ? 'start' : 'end'}-month`, defaultValue: defaultValue && formatNumberAsDoubleDigit(defaultValue[1]), max: 12, min: 1, onBack: () => dayInputRef.current.focus(), onBlur: handleBlur, onClick: onClick, onFilled: submit, onFocus: handleFocus, onFormatError: handleFormatError, onNext: () => yearInputRef.current.focus(), onPrevious: () => dayInputRef.current.focus(), size: 2 }), "/", jsx(NumberInput, { ref: yearInputRef, "data-cy": `date-range-picker-${isStartDate ? 'start' : 'end'}-year`, defaultValue: defaultValue && defaultValue[0], max: currentUtcYear, min: 2020, onBack: () => monthInputRef.current.focus(), onBlur: handleBlur, onClick: onClick, onFilled: submit, onFocus: handleFocus, onFormatError: handleFormatError, onNext: onNext, onPrevious: () => monthInputRef.current.focus(), size: 4 })] }));
|
|
65
|
+
}
|
|
66
|
+
const DateInput = forwardRef(DateInputWithRef);
|
|
67
|
+
const Box = styled.span `
|
|
68
|
+
background-color: ${p => p.theme.color.gainsboro};
|
|
69
|
+
box-shadow: ${p => p.hasError || p.isFocused
|
|
70
|
+
? `inset 0px 0px 0px 1px ${p.hasError ? p.theme.color.maximumRed : p.theme.color.blueGray[100]}`
|
|
71
|
+
: 'none'};
|
|
72
|
+
color: ${p => p.theme.color.slateGray};
|
|
73
|
+
display: inline-block;
|
|
74
|
+
font-size: inherit;
|
|
75
|
+
padding: 0.3125rem 0.5rem 0.4375rem;
|
|
76
|
+
user-select: none;
|
|
77
|
+
|
|
78
|
+
:hover {
|
|
79
|
+
box-shadow: ${p => `inset 0px 0px 0px 1px ${p.theme.color.blueYonder[100]}`};
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
export { DateInput };
|
|
84
|
+
//# sourceMappingURL=DateInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateInput.js","sources":["../../../../src/fields/DateRangePicker/DateInput.tsx"],"sourcesContent":["import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'\nimport styled from 'styled-components'\n\nimport { getUtcizedDayjs } from '../../utils/getUtcizedDayjs'\nimport { NumberInput } from './NumberInput'\nimport { formatNumberAsDoubleDigit } from './utils'\n\nimport type { NumberInputProps } from './NumberInput'\nimport type { DateTuple, DateOrTimeInputRef } from './types'\nimport type { ForwardedRef, MutableRefObject } from 'react'\nimport type { Promisable } from 'type-fest'\n\nexport type DateInputProps = Pick<NumberInputProps, 'onBack' | 'onPrevious' | 'onNext'> & {\n defaultValue?: DateTuple\n isEndDate?: boolean\n isForcedFocused: boolean\n isStartDate?: boolean\n /** Called each time the date input is changed to a new valid value. */\n onChange: (nextDateTuple: DateTuple) => Promisable<void>\n onClick: () => Promisable<void>\n}\nfunction DateInputWithRef(\n {\n defaultValue,\n isEndDate = false,\n isForcedFocused,\n isStartDate = false,\n onBack,\n onChange,\n onClick,\n onNext,\n onPrevious\n }: DateInputProps,\n ref: ForwardedRef<DateOrTimeInputRef>\n) {\n const boxSpanRef = useRef() as MutableRefObject<HTMLSpanElement>\n const dayInputRef = useRef() as MutableRefObject<HTMLInputElement>\n const monthInputRef = useRef() as MutableRefObject<HTMLInputElement>\n const yearInputRef = useRef() as MutableRefObject<HTMLInputElement>\n\n const [hasFormatError, setHasFormatError] = useState(false)\n const [hasValidationError, setHasValidationError] = useState(false)\n const [isFocused, setIsFocused] = useState(false)\n\n useImperativeHandle<DateOrTimeInputRef, DateOrTimeInputRef>(ref, () => ({\n boxSpan: boxSpanRef.current,\n focus: (isInLastInputOfTheGroup = false) => {\n if (isInLastInputOfTheGroup) {\n yearInputRef.current.focus()\n } else {\n dayInputRef.current.focus()\n }\n }\n }))\n\n const currentUtcYear = useMemo(() => getUtcizedDayjs().year(), [])\n\n const handleBlur = useCallback(() => {\n setIsFocused(false)\n }, [])\n\n const handleFocus = useCallback(() => {\n setIsFocused(true)\n }, [])\n\n const handleFormatError = useCallback((hasNextFormatError: boolean) => {\n setHasFormatError(hasNextFormatError)\n }, [])\n\n const submit = useCallback(() => {\n setHasValidationError(false)\n\n switch (window.document.activeElement) {\n case dayInputRef.current:\n monthInputRef.current.focus()\n break\n\n case monthInputRef.current:\n yearInputRef.current.focus()\n break\n\n default:\n break\n }\n\n if (\n !yearInputRef.current.value.length ||\n !monthInputRef.current.value.length ||\n !dayInputRef.current.value.length\n ) {\n if (\n (monthInputRef.current.value.length && !dayInputRef.current.value.length) ||\n (yearInputRef.current.value.length &&\n (!dayInputRef.current.value.length || !monthInputRef.current.value.length))\n ) {\n setHasValidationError(true)\n }\n\n return\n }\n\n const nextDateTuple: DateTuple = [\n String(yearInputRef.current.value),\n formatNumberAsDoubleDigit(monthInputRef.current.value),\n formatNumberAsDoubleDigit(dayInputRef.current.value)\n ]\n\n onChange(nextDateTuple)\n }, [onChange])\n\n return (\n <Box ref={boxSpanRef} hasError={hasFormatError || hasValidationError} isFocused={isForcedFocused || isFocused}>\n {isStartDate && 'Du '}\n {isEndDate && 'Au '}\n <NumberInput\n ref={dayInputRef}\n data-cy={`date-range-picker-${isStartDate ? 'start' : 'end'}-day`}\n defaultValue={defaultValue && formatNumberAsDoubleDigit(defaultValue[2])}\n max={31}\n min={1}\n onBack={onBack}\n onBlur={handleBlur}\n onClick={onClick}\n onFilled={submit}\n onFocus={handleFocus}\n onFormatError={handleFormatError}\n onNext={() => monthInputRef.current.focus()}\n onPrevious={onPrevious}\n size={2}\n />\n /\n <NumberInput\n ref={monthInputRef}\n data-cy={`date-range-picker-${isStartDate ? 'start' : 'end'}-month`}\n defaultValue={defaultValue && formatNumberAsDoubleDigit(defaultValue[1])}\n max={12}\n min={1}\n onBack={() => dayInputRef.current.focus()}\n onBlur={handleBlur}\n onClick={onClick}\n onFilled={submit}\n onFocus={handleFocus}\n onFormatError={handleFormatError}\n onNext={() => yearInputRef.current.focus()}\n onPrevious={() => dayInputRef.current.focus()}\n size={2}\n />\n /\n <NumberInput\n ref={yearInputRef}\n data-cy={`date-range-picker-${isStartDate ? 'start' : 'end'}-year`}\n defaultValue={defaultValue && defaultValue[0]}\n max={currentUtcYear}\n min={2020}\n onBack={() => monthInputRef.current.focus()}\n onBlur={handleBlur}\n onClick={onClick}\n onFilled={submit}\n onFocus={handleFocus}\n onFormatError={handleFormatError}\n onNext={onNext}\n onPrevious={() => monthInputRef.current.focus()}\n size={4}\n />\n </Box>\n )\n}\n\nexport const DateInput = forwardRef(DateInputWithRef)\n\nconst Box = styled.span<{\n hasError: boolean\n isFocused: boolean\n}>`\n background-color: ${p => p.theme.color.gainsboro};\n box-shadow: ${p =>\n p.hasError || p.isFocused\n ? `inset 0px 0px 0px 1px ${p.hasError ? p.theme.color.maximumRed : p.theme.color.blueGray[100]}`\n : 'none'};\n color: ${p => p.theme.color.slateGray};\n display: inline-block;\n font-size: inherit;\n padding: 0.3125rem 0.5rem 0.4375rem;\n user-select: none;\n\n :hover {\n box-shadow: ${p => `inset 0px 0px 0px 1px ${p.theme.color.blueYonder[100]}`};\n }\n`\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { forwardRef, useRef, useMemo, useImperativeHandle, useCallback } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
|
|
5
|
+
function NumberInputWithRef({ defaultValue, max, min, onBack, onClick, onFilled, onFocus, onFormatError, onInput, onNext, onPrevious, size, ...nativeProps }, ref) {
|
|
6
|
+
const inputRef = useRef();
|
|
7
|
+
const placeholder = useMemo(() => '-'.repeat(size), [size]);
|
|
8
|
+
useImperativeHandle(ref, () => inputRef.current);
|
|
9
|
+
const handleClick = useCallback((event) => {
|
|
10
|
+
// event.stopPropagation()
|
|
11
|
+
if (onClick) {
|
|
12
|
+
onClick(event);
|
|
13
|
+
}
|
|
14
|
+
}, [onClick]);
|
|
15
|
+
const handleFocus = useCallback((event) => {
|
|
16
|
+
inputRef.current.select();
|
|
17
|
+
if (onFocus) {
|
|
18
|
+
onFocus(event);
|
|
19
|
+
}
|
|
20
|
+
}, [onFocus]);
|
|
21
|
+
const handleInput = useCallback(() => {
|
|
22
|
+
onFormatError(false);
|
|
23
|
+
const { value } = inputRef.current;
|
|
24
|
+
if (onInput) {
|
|
25
|
+
onInput(value);
|
|
26
|
+
}
|
|
27
|
+
if (value.length !== size) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const valueAsNumber = Number(inputRef.current.value);
|
|
31
|
+
if (Number.isNaN(valueAsNumber) || valueAsNumber < min || valueAsNumber > max) {
|
|
32
|
+
onFormatError(true);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (onFilled && value.length === size) {
|
|
36
|
+
onFilled();
|
|
37
|
+
}
|
|
38
|
+
}, [max, min, onFilled, onFormatError, onInput, size]);
|
|
39
|
+
const handleKeyDown = useCallback((event) => {
|
|
40
|
+
if (onPrevious &&
|
|
41
|
+
event.key === 'ArrowLeft' &&
|
|
42
|
+
inputRef.current.selectionStart === 0 &&
|
|
43
|
+
// We don't want to call that function when the user is selecting the input text
|
|
44
|
+
inputRef.current.selectionEnd === inputRef.current.selectionStart) {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
onPrevious();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (onNext &&
|
|
50
|
+
event.key === 'ArrowRight' &&
|
|
51
|
+
inputRef.current.selectionStart === inputRef.current.value.length &&
|
|
52
|
+
// We don't want to call that function when the user is selecting the input text
|
|
53
|
+
inputRef.current.selectionEnd === inputRef.current.selectionStart) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
onNext();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (onBack && event.key === 'Backspace' && !inputRef.current.value.length) {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
onBack();
|
|
61
|
+
}
|
|
62
|
+
}, [onBack, onNext, onPrevious]);
|
|
63
|
+
return (jsx(StyledNumberInput, { ref: inputRef, defaultValue: defaultValue, maxLength: size, onClick: handleClick, onFocus: handleFocus, onInput: handleInput, onKeyDown: handleKeyDown, pattern: "\\d*", placeholder: placeholder, size: size, type: "text", ...nativeProps }, String(defaultValue)));
|
|
64
|
+
}
|
|
65
|
+
const NumberInput = forwardRef(NumberInputWithRef);
|
|
66
|
+
const StyledNumberInput = styled.input `
|
|
67
|
+
background-color: transparent;
|
|
68
|
+
border: 0;
|
|
69
|
+
font-size: inherit;
|
|
70
|
+
outline: none;
|
|
71
|
+
padding: 0;
|
|
72
|
+
text-align: center;
|
|
73
|
+
/* 1 digit = 0.5rem */
|
|
74
|
+
width: ${p => p.size * 0.5}rem;
|
|
75
|
+
|
|
76
|
+
::placeholder {
|
|
77
|
+
color: ${p => p.theme.color.slateGray};
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
export { NumberInput };
|
|
82
|
+
//# sourceMappingURL=NumberInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NumberInput.js","sources":["../../../../src/fields/DateRangePicker/NumberInput.tsx"],"sourcesContent":["import { forwardRef, KeyboardEvent, useCallback, useImperativeHandle, useMemo, useRef } from 'react'\nimport styled from 'styled-components'\n\nimport type { FocusEvent, ForwardedRef, InputHTMLAttributes, MouseEvent, MutableRefObject } from 'react'\nimport type { Promisable } from 'type-fest'\n\nexport type NumberInputProps = Omit<\n InputHTMLAttributes<HTMLInputElement>,\n 'maxLength' | 'onInput' | 'pattern' | 'type'\n> & {\n max: number\n min: number\n /** Called when the use press backspace key while the input is empty. */\n onBack?: () => Promisable<void>\n /** Called when the input value reaches the size property. */\n onFilled?: () => Promisable<void>\n onFormatError: (hasNextFormatError: boolean) => Promisable<void>\n onInput?: (nextValue: string) => Promisable<void>\n /** Called when the right arrow is pressed while the cursor is positionned at the input end. */\n onNext?: () => Promisable<void>\n /** Called when the left arrow is pressed while the cursor is positionned at the input start. */\n onPrevious?: () => Promisable<void>\n size: number\n}\nfunction NumberInputWithRef(\n {\n defaultValue,\n max,\n min,\n onBack,\n onClick,\n onFilled,\n onFocus,\n onFormatError,\n onInput,\n onNext,\n onPrevious,\n size,\n ...nativeProps\n }: NumberInputProps,\n ref: ForwardedRef<HTMLInputElement>\n) {\n const inputRef = useRef<HTMLInputElement>() as MutableRefObject<HTMLInputElement>\n\n const placeholder = useMemo(() => '-'.repeat(size), [size])\n\n useImperativeHandle(ref, () => inputRef.current as HTMLInputElement)\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLInputElement>) => {\n // event.stopPropagation()\n\n if (onClick) {\n onClick(event)\n }\n },\n [onClick]\n )\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLInputElement>) => {\n inputRef.current.select()\n\n if (onFocus) {\n onFocus(event)\n }\n },\n [onFocus]\n )\n\n const handleInput = useCallback(() => {\n onFormatError(false)\n\n const { value } = inputRef.current\n if (onInput) {\n onInput(value)\n }\n if (value.length !== size) {\n return\n }\n\n const valueAsNumber = Number(inputRef.current.value)\n if (Number.isNaN(valueAsNumber) || valueAsNumber < min || valueAsNumber > max) {\n onFormatError(true)\n\n return\n }\n\n if (onFilled && value.length === size) {\n onFilled()\n }\n }, [max, min, onFilled, onFormatError, onInput, size])\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n if (\n onPrevious &&\n event.key === 'ArrowLeft' &&\n inputRef.current.selectionStart === 0 &&\n // We don't want to call that function when the user is selecting the input text\n inputRef.current.selectionEnd === inputRef.current.selectionStart\n ) {\n event.preventDefault()\n\n onPrevious()\n\n return\n }\n\n if (\n onNext &&\n event.key === 'ArrowRight' &&\n inputRef.current.selectionStart === inputRef.current.value.length &&\n // We don't want to call that function when the user is selecting the input text\n inputRef.current.selectionEnd === inputRef.current.selectionStart\n ) {\n event.preventDefault()\n\n onNext()\n\n return\n }\n\n if (onBack && event.key === 'Backspace' && !inputRef.current.value.length) {\n event.preventDefault()\n\n onBack()\n }\n },\n [onBack, onNext, onPrevious]\n )\n\n return (\n <StyledNumberInput\n key={String(defaultValue)}\n ref={inputRef}\n defaultValue={defaultValue}\n maxLength={size}\n onClick={handleClick}\n onFocus={handleFocus}\n onInput={handleInput}\n onKeyDown={handleKeyDown}\n pattern=\"\\d*\"\n placeholder={placeholder}\n size={size}\n type=\"text\"\n {...nativeProps}\n />\n )\n}\n\nexport const NumberInput = forwardRef(NumberInputWithRef)\n\nconst StyledNumberInput = styled.input<{\n size: number\n}>`\n background-color: transparent;\n border: 0;\n font-size: inherit;\n outline: none;\n padding: 0;\n text-align: center;\n /* 1 digit = 0.5rem */\n width: ${p => p.size * 0.5}rem;\n\n ::placeholder {\n color: ${p => p.theme.color.slateGray};\n }\n`\n"],"names":["_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useRef, useState, useMemo, useCallback, useEffect } from 'react';
|
|
3
|
+
import { DateRangePicker } from 'rsuite';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
import { capitalizeFirstLetter } from '../../utils/capitalizeFirstLetter.js';
|
|
6
|
+
import '../../utils/dayjs.js';
|
|
7
|
+
import { getUtcDayjs } from '../../utils/getUtcDayjs.js';
|
|
8
|
+
import { getUtcizedDayjs } from '../../utils/getUtcizedDayjs.js';
|
|
9
|
+
import { sortDates } from '../../utils/sortDates.js';
|
|
10
|
+
import { stopMouseEventPropagation } from '../../utils/stopMouseEventPropagation.js';
|
|
11
|
+
import { RSUITE_CALENDAR_LOCALE } from './constants.js';
|
|
12
|
+
import { getDateTupleFromDate } from './utils.js';
|
|
13
|
+
import dayjs from 'dayjs';
|
|
14
|
+
|
|
15
|
+
function RangeCalendarPicker({ defaultValue, isHistorical, onChange }) {
|
|
16
|
+
const boxRef = useRef();
|
|
17
|
+
const selectedFirstDate = useRef();
|
|
18
|
+
const calendarRef = useRef();
|
|
19
|
+
const [isFirstLoad, setIsFirstLoad] = useState(true);
|
|
20
|
+
const controlledValue = useMemo(() => (defaultValue ? sortDates(defaultValue) : undefined), [defaultValue]);
|
|
21
|
+
const utcTodayAsDayjs = useMemo(() => getUtcDayjs().endOf('day'), []);
|
|
22
|
+
const disabledDate = useMemo(() => (date) => isHistorical ? getUtcizedDayjs(date).isAfter(utcTodayAsDayjs) : false, [isHistorical, utcTodayAsDayjs]);
|
|
23
|
+
const handleSelect = useCallback((nextDate) => {
|
|
24
|
+
if (!selectedFirstDate.current) {
|
|
25
|
+
selectedFirstDate.current = nextDate;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const sortedDateRange = sortDates([selectedFirstDate.current, nextDate]);
|
|
29
|
+
const [startDate, endDate] = sortedDateRange;
|
|
30
|
+
const startDateTuple = getDateTupleFromDate(startDate);
|
|
31
|
+
const endDateTuple = getDateTupleFromDate(endDate);
|
|
32
|
+
const nextDateTupleRange = [startDateTuple, endDateTuple];
|
|
33
|
+
onChange(nextDateTupleRange);
|
|
34
|
+
}, [onChange]);
|
|
35
|
+
const renderTitle = useCallback((date) => capitalizeFirstLetter(dayjs(date).format('MMMM YYYY')), []);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
// We wait for the <Box /> to render so that `boxRef` is defined
|
|
38
|
+
// and can be used as a container for <RsuiteDateRangePicker />
|
|
39
|
+
setIsFirstLoad(false);
|
|
40
|
+
}, []);
|
|
41
|
+
return (jsx(Box, { ref: boxRef, onClick: stopMouseEventPropagation, children: !isFirstLoad && (jsx(DateRangePicker, { ref: calendarRef, container: boxRef.current, disabledDate: disabledDate, format: "yyyy-MM-dd", locale: RSUITE_CALENDAR_LOCALE, onSelect: handleSelect, open: true, ranges: [], renderTitle: renderTitle,
|
|
42
|
+
// `defaultValue` seems to be immediatly cancelled so we come down to using a controlled `value`
|
|
43
|
+
value: controlledValue })) }));
|
|
44
|
+
}
|
|
45
|
+
const Box = styled.div `
|
|
46
|
+
height: 0;
|
|
47
|
+
position: relative;
|
|
48
|
+
user-select: none;
|
|
49
|
+
|
|
50
|
+
.rs-picker-toggle {
|
|
51
|
+
display: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.rs-picker-daterange-panel {
|
|
55
|
+
height: 290px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.rs-picker-daterange-menu {
|
|
59
|
+
border: solid 1px ${p => p.theme.color.lightGray};
|
|
60
|
+
border-radius: 0;
|
|
61
|
+
margin-top: 0.25rem;
|
|
62
|
+
|
|
63
|
+
.rs-picker-daterange-header,
|
|
64
|
+
.rs-calendar-header-time-toolbar,
|
|
65
|
+
.rs-picker-toolbar {
|
|
66
|
+
display: none;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.rs-calendar {
|
|
70
|
+
height: auto !important;
|
|
71
|
+
padding: 0;
|
|
72
|
+
|
|
73
|
+
:first-child {
|
|
74
|
+
border-right: solid 1px ${p => p.theme.color.lightGray};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.rs-calendar-header {
|
|
78
|
+
border-bottom: solid 1px ${p => p.theme.color.lightGray};
|
|
79
|
+
padding: 0.5rem;
|
|
80
|
+
|
|
81
|
+
.rs-calendar-header-month-toolbar {
|
|
82
|
+
align-items: center;
|
|
83
|
+
color: ${p => p.theme.color.slateGray};
|
|
84
|
+
display: flex;
|
|
85
|
+
justify-content: space-between;
|
|
86
|
+
|
|
87
|
+
.rs-calendar-header-title {
|
|
88
|
+
font-size: inherit;
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
|
|
91
|
+
&.rs-calendar-header-error {
|
|
92
|
+
color: ${p => p.theme.color.slateGray};
|
|
93
|
+
|
|
94
|
+
:hover {
|
|
95
|
+
color: ${p => p.theme.color.slateGray};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.rs-calendar-view {
|
|
103
|
+
padding: 0.75rem 0.5rem 0;
|
|
104
|
+
|
|
105
|
+
.rs-calendar-table-cell {
|
|
106
|
+
padding: 0 0 0.25rem 0;
|
|
107
|
+
width: 33px;
|
|
108
|
+
|
|
109
|
+
&.rs-calendar-table-cell-in-range:before {
|
|
110
|
+
background-color: ${p => p.theme.color.blueGray[25]};
|
|
111
|
+
height: 33px;
|
|
112
|
+
margin-top: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
> .rs-calendar-table-cell-content {
|
|
116
|
+
align-items: center;
|
|
117
|
+
border-radius: 0 !important;
|
|
118
|
+
display: inline-flex;
|
|
119
|
+
height: 33px;
|
|
120
|
+
justify-content: center;
|
|
121
|
+
padding-bottom: 3px;
|
|
122
|
+
width: 33px;
|
|
123
|
+
}
|
|
124
|
+
:hover .rs-calendar-table-cell-content {
|
|
125
|
+
background-color: ${p => p.theme.color.blueYonder[25]};
|
|
126
|
+
color: ${p => p.theme.color.blueYonder[100]};
|
|
127
|
+
}
|
|
128
|
+
&[role='columnheader'] .rs-calendar-table-cell-content,
|
|
129
|
+
&[role='columnheader']:hover .rs-calendar-table-cell-content {
|
|
130
|
+
background-color: transparent;
|
|
131
|
+
color: ${p => p.theme.color.slateGray};
|
|
132
|
+
}
|
|
133
|
+
&.rs-calendar-table-cell-disabled .rs-calendar-table-cell-content {
|
|
134
|
+
background-color: transparent;
|
|
135
|
+
color: ${p => p.theme.color.lightGray};
|
|
136
|
+
}
|
|
137
|
+
&.rs-calendar-table-cell-selected > .rs-calendar-table-cell-content {
|
|
138
|
+
background-color: ${p => p.theme.color.blueGray[100]};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
export { RangeCalendarPicker };
|
|
147
|
+
//# sourceMappingURL=RangeCalendarPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RangeCalendarPicker.js","sources":["../../../../src/fields/DateRangePicker/RangeCalendarPicker.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { DateRangePicker as RsuiteDateRangePicker } from 'rsuite'\nimport styled from 'styled-components'\n\nimport { capitalizeFirstLetter } from '../../utils/capitalizeFirstLetter'\nimport { dayjs } from '../../utils/dayjs'\nimport { getUtcDayjs } from '../../utils/getUtcDayjs'\nimport { getUtcizedDayjs } from '../../utils/getUtcizedDayjs'\nimport { sortDates } from '../../utils/sortDates'\nimport { stopMouseEventPropagation } from '../../utils/stopMouseEventPropagation'\nimport { RSUITE_CALENDAR_LOCALE } from './constants'\nimport { getDateTupleFromDate } from './utils'\n\nimport type { DateRange } from '../../types'\nimport type { DateTupleRange } from './types'\nimport type { MutableRefObject } from 'react'\nimport type { Promisable } from 'type-fest'\n\ntype RangeCalendarPickerProps = {\n defaultValue?: DateRange\n isHistorical?: boolean\n onChange: (nextDateTupleRange: DateTupleRange) => Promisable<void>\n}\nexport function RangeCalendarPicker({ defaultValue, isHistorical, onChange }: RangeCalendarPickerProps) {\n const boxRef = useRef() as MutableRefObject<HTMLDivElement>\n const selectedFirstDate = useRef<Date>()\n const calendarRef = useRef<any>()\n\n const [isFirstLoad, setIsFirstLoad] = useState(true)\n\n const controlledValue = useMemo(\n () => (defaultValue ? (sortDates(defaultValue) as DateRange) : undefined),\n [defaultValue]\n )\n const utcTodayAsDayjs = useMemo(() => getUtcDayjs().endOf('day'), [])\n const disabledDate = useMemo(\n () => (date: Date) => isHistorical ? getUtcizedDayjs(date).isAfter(utcTodayAsDayjs) : false,\n [isHistorical, utcTodayAsDayjs]\n )\n\n const handleSelect = useCallback(\n (nextDate: Date) => {\n if (!selectedFirstDate.current) {\n selectedFirstDate.current = nextDate\n\n return\n }\n\n const sortedDateRange = sortDates([selectedFirstDate.current, nextDate]) as DateRange\n const [startDate, endDate] = sortedDateRange\n const startDateTuple = getDateTupleFromDate(startDate)\n const endDateTuple = getDateTupleFromDate(endDate)\n const nextDateTupleRange = [startDateTuple, endDateTuple] as DateTupleRange\n\n onChange(nextDateTupleRange)\n },\n [onChange]\n )\n\n const renderTitle = useCallback((date: Date) => capitalizeFirstLetter(dayjs(date).format('MMMM YYYY')), [])\n\n useEffect(() => {\n // We wait for the <Box /> to render so that `boxRef` is defined\n // and can be used as a container for <RsuiteDateRangePicker />\n setIsFirstLoad(false)\n }, [])\n\n return (\n <Box ref={boxRef} onClick={stopMouseEventPropagation}>\n {!isFirstLoad && (\n <RsuiteDateRangePicker\n ref={calendarRef}\n container={boxRef.current}\n disabledDate={disabledDate}\n format=\"yyyy-MM-dd\"\n locale={RSUITE_CALENDAR_LOCALE}\n onSelect={handleSelect}\n open\n ranges={[]}\n renderTitle={renderTitle}\n // `defaultValue` seems to be immediatly cancelled so we come down to using a controlled `value`\n value={controlledValue}\n />\n )}\n </Box>\n )\n}\n\nconst Box = styled.div`\n height: 0;\n position: relative;\n user-select: none;\n\n .rs-picker-toggle {\n display: none;\n }\n\n .rs-picker-daterange-panel {\n height: 290px;\n }\n\n .rs-picker-daterange-menu {\n border: solid 1px ${p => p.theme.color.lightGray};\n border-radius: 0;\n margin-top: 0.25rem;\n\n .rs-picker-daterange-header,\n .rs-calendar-header-time-toolbar,\n .rs-picker-toolbar {\n display: none;\n }\n\n .rs-calendar {\n height: auto !important;\n padding: 0;\n\n :first-child {\n border-right: solid 1px ${p => p.theme.color.lightGray};\n }\n\n .rs-calendar-header {\n border-bottom: solid 1px ${p => p.theme.color.lightGray};\n padding: 0.5rem;\n\n .rs-calendar-header-month-toolbar {\n align-items: center;\n color: ${p => p.theme.color.slateGray};\n display: flex;\n justify-content: space-between;\n\n .rs-calendar-header-title {\n font-size: inherit;\n text-transform: uppercase;\n\n &.rs-calendar-header-error {\n color: ${p => p.theme.color.slateGray};\n\n :hover {\n color: ${p => p.theme.color.slateGray};\n }\n }\n }\n }\n }\n\n .rs-calendar-view {\n padding: 0.75rem 0.5rem 0;\n\n .rs-calendar-table-cell {\n padding: 0 0 0.25rem 0;\n width: 33px;\n\n &.rs-calendar-table-cell-in-range:before {\n background-color: ${p => p.theme.color.blueGray[25]};\n height: 33px;\n margin-top: 0;\n }\n\n > .rs-calendar-table-cell-content {\n align-items: center;\n border-radius: 0 !important;\n display: inline-flex;\n height: 33px;\n justify-content: center;\n padding-bottom: 3px;\n width: 33px;\n }\n :hover .rs-calendar-table-cell-content {\n background-color: ${p => p.theme.color.blueYonder[25]};\n color: ${p => p.theme.color.blueYonder[100]};\n }\n &[role='columnheader'] .rs-calendar-table-cell-content,\n &[role='columnheader']:hover .rs-calendar-table-cell-content {\n background-color: transparent;\n color: ${p => p.theme.color.slateGray};\n }\n &.rs-calendar-table-cell-disabled .rs-calendar-table-cell-content {\n background-color: transparent;\n color: ${p => p.theme.color.lightGray};\n }\n &.rs-calendar-table-cell-selected > .rs-calendar-table-cell-content {\n background-color: ${p => p.theme.color.blueGray[100]};\n }\n }\n }\n }\n }\n`\n"],"names":["_jsx","RsuiteDateRangePicker"],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useMemo, useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { stopMouseEventPropagation } from '../../utils/stopMouseEventPropagation.js';
|
|
5
|
+
import { getRangedTimeOptions } from './utils.js';
|
|
6
|
+
|
|
7
|
+
function RangedTimePicker({ filter, minutesRange, onChange }) {
|
|
8
|
+
const rangedTimeOptions = useMemo(() => getRangedTimeOptions(minutesRange), [minutesRange]);
|
|
9
|
+
const filteredRangedTimeOptions = useMemo(() => rangedTimeOptions.filter(({ label }) => filter.test(label)), [filter, rangedTimeOptions]);
|
|
10
|
+
const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
|
|
11
|
+
const handleBoxKeyDown = useCallback((event) => {
|
|
12
|
+
if (event.key === 'ArrowDown') {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
const nextSelectedOptionIndex = selectedOptionIndex < filteredRangedTimeOptions.length - 1 ? selectedOptionIndex + 1 : 0;
|
|
15
|
+
setSelectedOptionIndex(nextSelectedOptionIndex);
|
|
16
|
+
window.document.querySelectorAll('.js-ranged-time-picker-option')[nextSelectedOptionIndex]?.scrollIntoView();
|
|
17
|
+
}
|
|
18
|
+
if (event.key === 'ArrowUp') {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
const nextSelectedOptionIndex = selectedOptionIndex > 0 ? selectedOptionIndex - 1 : filteredRangedTimeOptions.length - 1;
|
|
21
|
+
setSelectedOptionIndex(nextSelectedOptionIndex);
|
|
22
|
+
window.document.querySelectorAll('.js-ranged-time-picker-option')[nextSelectedOptionIndex]?.scrollIntoView();
|
|
23
|
+
}
|
|
24
|
+
if (['Enter', 'Space', 'Tab'].includes(event.key)) {
|
|
25
|
+
const selectedRangedTimeOption = filteredRangedTimeOptions[selectedOptionIndex];
|
|
26
|
+
if (!selectedRangedTimeOption) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
onChange(selectedRangedTimeOption.value);
|
|
30
|
+
}
|
|
31
|
+
}, [filteredRangedTimeOptions, selectedOptionIndex, onChange]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
window.addEventListener('keydown', handleBoxKeyDown, {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
35
|
+
once: true
|
|
36
|
+
});
|
|
37
|
+
return () => {
|
|
38
|
+
window.removeEventListener('keydown', handleBoxKeyDown);
|
|
39
|
+
};
|
|
40
|
+
}, [handleBoxKeyDown]);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setSelectedOptionIndex(0);
|
|
43
|
+
}, [filteredRangedTimeOptions]);
|
|
44
|
+
if (!filteredRangedTimeOptions.length) {
|
|
45
|
+
return jsx(Fragment, {});
|
|
46
|
+
}
|
|
47
|
+
return (jsx(Box, { onClick: stopMouseEventPropagation, role: "listbox", children: filteredRangedTimeOptions.map(({ label, value }, index) => (jsx(Option, { "aria-selected": false, className: "js-ranged-time-picker-option", isSelected: index === selectedOptionIndex, onClick: () => onChange(value), role: "option", tabIndex: -1, children: label }, label))) }));
|
|
48
|
+
}
|
|
49
|
+
const Box = styled.div `
|
|
50
|
+
background-color: ${p => p.theme.color.gainsboro};
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
left: -1px;
|
|
54
|
+
max-height: 10rem;
|
|
55
|
+
overflow: auto;
|
|
56
|
+
position: absolute;
|
|
57
|
+
/* Non-WebKit Firefox Compatibility */
|
|
58
|
+
scrollbar-color: ${p => p.theme.color.lightGray};
|
|
59
|
+
scrollbar-width: thin;
|
|
60
|
+
top: 2.25rem;
|
|
61
|
+
z-index: 9999;
|
|
62
|
+
|
|
63
|
+
::-webkit-scrollbar {
|
|
64
|
+
-webkit-appearance: none;
|
|
65
|
+
}
|
|
66
|
+
::-webkit-scrollbar:vertical {
|
|
67
|
+
width: 0.33rem;
|
|
68
|
+
}
|
|
69
|
+
::-webkit-scrollbar-thumb {
|
|
70
|
+
border: 0;
|
|
71
|
+
background-color: ${p => p.theme.color.lightGray};
|
|
72
|
+
}
|
|
73
|
+
::-webkit-scrollbar-track {
|
|
74
|
+
background-color: ${p => p.theme.color.gainsboro};
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
const Option = styled.div `
|
|
78
|
+
background-color: ${p => (p.isSelected ? p.theme.color.blueGray[100] : 'transparent')};
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
line-height: 1;
|
|
81
|
+
padding: 5px 9px 7px 8px;
|
|
82
|
+
text-align: center;
|
|
83
|
+
|
|
84
|
+
:hover {
|
|
85
|
+
background-color: ${p => (p.isSelected ? p.theme.color.blueGray[100] : p.theme.color.blueYonder[25])};
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
export { RangedTimePicker };
|
|
90
|
+
//# sourceMappingURL=RangedTimePicker.js.map
|