@toptal/picasso-timepicker 100.0.4-alpha-pf-react-19-improvements-970580a42.0 → 100.1.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.
- package/dist-package/src/TimePicker/TimePicker.d.ts +4 -1
- package/dist-package/src/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist-package/src/TimePicker/TimePicker.js +64 -10
- package/dist-package/src/TimePicker/TimePicker.js.map +1 -1
- package/dist-package/src/TimePicker/use-time-picker-popover.d.ts +21 -0
- package/dist-package/src/TimePicker/use-time-picker-popover.d.ts.map +1 -0
- package/dist-package/src/TimePicker/use-time-picker-popover.js +96 -0
- package/dist-package/src/TimePicker/use-time-picker-popover.js.map +1 -0
- package/dist-package/src/TimePicker/utils/get-hour-cycle.d.ts +3 -0
- package/dist-package/src/TimePicker/utils/get-hour-cycle.d.ts.map +1 -0
- package/dist-package/src/TimePicker/utils/get-hour-cycle.js +42 -0
- package/dist-package/src/TimePicker/utils/get-hour-cycle.js.map +1 -0
- package/dist-package/src/TimePicker/utils/index.d.ts +5 -0
- package/dist-package/src/TimePicker/utils/index.d.ts.map +1 -0
- package/dist-package/src/TimePicker/utils/index.js +3 -0
- package/dist-package/src/TimePicker/utils/index.js.map +1 -0
- package/dist-package/src/TimePicker/utils/time.d.ts +16 -0
- package/dist-package/src/TimePicker/utils/time.d.ts.map +1 -0
- package/dist-package/src/TimePicker/utils/time.js +41 -0
- package/dist-package/src/TimePicker/utils/time.js.map +1 -0
- package/dist-package/src/TimePickerDropdown/TimePickerDropdown.d.ts +13 -0
- package/dist-package/src/TimePickerDropdown/TimePickerDropdown.d.ts.map +1 -0
- package/dist-package/src/TimePickerDropdown/TimePickerDropdown.js +28 -0
- package/dist-package/src/TimePickerDropdown/TimePickerDropdown.js.map +1 -0
- package/dist-package/src/TimePickerDropdown/index.d.ts +2 -0
- package/dist-package/src/TimePickerDropdown/index.d.ts.map +1 -0
- package/dist-package/src/TimePickerDropdown/index.js +2 -0
- package/dist-package/src/TimePickerDropdown/index.js.map +1 -0
- package/dist-package/src/TimePickerList/TimePickerList.d.ts +23 -0
- package/dist-package/src/TimePickerList/TimePickerList.d.ts.map +1 -0
- package/dist-package/src/TimePickerList/TimePickerList.js +97 -0
- package/dist-package/src/TimePickerList/TimePickerList.js.map +1 -0
- package/dist-package/src/TimePickerList/index.d.ts +4 -0
- package/dist-package/src/TimePickerList/index.d.ts.map +1 -0
- package/dist-package/src/TimePickerList/index.js +2 -0
- package/dist-package/src/TimePickerList/index.js.map +1 -0
- package/dist-package/src/TimePickerTrigger/TimePickerTrigger.d.ts +16 -0
- package/dist-package/src/TimePickerTrigger/TimePickerTrigger.d.ts.map +1 -0
- package/dist-package/src/TimePickerTrigger/TimePickerTrigger.js +11 -0
- package/dist-package/src/TimePickerTrigger/TimePickerTrigger.js.map +1 -0
- package/dist-package/src/TimePickerTrigger/index.d.ts +2 -0
- package/dist-package/src/TimePickerTrigger/index.d.ts.map +1 -0
- package/dist-package/src/TimePickerTrigger/index.js +2 -0
- package/dist-package/src/TimePickerTrigger/index.js.map +1 -0
- package/package.json +14 -9
- package/src/TimePicker/TimePicker.tsx +173 -33
- package/src/TimePicker/story/MinuteStep.example.tsx +16 -0
- package/src/TimePicker/story/index.jsx +10 -0
- package/src/TimePicker/test.tsx +649 -1
- package/src/TimePicker/use-time-picker-popover.ts +120 -0
- package/src/TimePicker/utils/get-hour-cycle.test.ts +76 -0
- package/src/TimePicker/utils/get-hour-cycle.ts +63 -0
- package/src/TimePicker/utils/index.ts +10 -0
- package/src/TimePicker/utils/time.test.ts +143 -0
- package/src/TimePicker/utils/time.ts +76 -0
- package/src/TimePickerDropdown/TimePickerDropdown.tsx +45 -0
- package/src/TimePickerDropdown/index.ts +1 -0
- package/src/TimePickerList/TimePickerList.tsx +190 -0
- package/src/TimePickerList/index.ts +4 -0
- package/src/TimePickerTrigger/TimePickerTrigger.tsx +53 -0
- package/src/TimePickerTrigger/index.ts +1 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import type { BaseProps } from '@toptal/picasso-shared';
|
|
2
3
|
import type { InputProps } from '@toptal/picasso-input';
|
|
3
4
|
import type { Status } from '@toptal/picasso-outlined-input';
|
|
@@ -8,9 +9,11 @@ export interface Props extends BaseProps, Omit<InputProps, 'id' | 'value' | 'onS
|
|
|
8
9
|
status?: Extract<Status, 'error' | 'warning' | 'default'>;
|
|
9
10
|
/** Called on input change */
|
|
10
11
|
onChange?: (value: string) => void;
|
|
12
|
+
/** Interval in minutes between the times offered in a dropdown list. When omitted, the browser's own time picker is used */
|
|
13
|
+
minuteStep?: 5 | 10 | 15 | 20 | 30 | 60;
|
|
11
14
|
}
|
|
12
15
|
export declare const TimePicker: {
|
|
13
|
-
({ status, ...props }: Props):
|
|
16
|
+
({ status, ...props }: Props): React.ReactElement;
|
|
14
17
|
displayName: string;
|
|
15
18
|
};
|
|
16
19
|
export default TimePicker;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimePicker.d.ts","sourceRoot":"","sources":["../../../src/TimePicker/TimePicker.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TimePicker.d.ts","sourceRoot":"","sources":["../../../src/TimePicker/TimePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAKvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAA;AAQ5D,MAAM,WAAW,KACf,SAAQ,SAAS,EACf,IAAI,CACF,UAAU,EACR,IAAI,GACJ,OAAO,GACP,UAAU,GACV,UAAU,GACV,MAAM,GACN,WAAW,GACX,MAAM,GACN,cAAc,GACd,MAAM,GACN,MAAM,GACN,cAAc,GACd,SAAS,GACT,cAAc,GACd,gBAAgB,GAChB,oBAAoB,GACpB,SAAS,GACT,OAAO,GACP,aAAa,GACb,QAAQ,CACX;IACH,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,CAAA;IACzD,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,4HAA4H;IAC5H,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;CACxC;AAqBD,eAAO,MAAM,UAAU;2BAGpB,KAAK,GAAG,KAAK,CAAC,YAAY;;CAmL5B,CAAA;AAID,eAAe,UAAU,CAAA"}
|
|
@@ -10,22 +10,39 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import React, { useEffect, useState } from 'react';
|
|
13
|
+
import InputMask from 'react-input-mask';
|
|
14
|
+
import { detect } from 'detect-browser';
|
|
13
15
|
import { Input } from '@toptal/picasso-input';
|
|
14
16
|
import { Time16 } from '@toptal/picasso-icons';
|
|
15
17
|
import { twMerge } from '@toptal/picasso-tailwind-merge';
|
|
16
|
-
|
|
18
|
+
import { TimePickerDropdown } from '../TimePickerDropdown';
|
|
19
|
+
import { TimePickerTrigger } from '../TimePickerTrigger';
|
|
20
|
+
import { useTimePickerPopover } from './use-time-picker-popover';
|
|
21
|
+
import { VALID_TIME_REGEX, getHourCycle } from './utils';
|
|
22
|
+
const NATIVE_PICKER_CLASS_NAME = `-mr-2 [&::-webkit-calendar-picker-indicator]:absolute [&::-webkit-calendar-picker-indicator]:right-2
|
|
23
|
+
[&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:bg-none`;
|
|
24
|
+
const TIME_LIST_CLASS_NAME = `${NATIVE_PICKER_CLASS_NAME} pointer-fine:[&::-webkit-calendar-picker-indicator]:hidden`;
|
|
25
|
+
const nativePickerIcon = (React.createElement(Time16, { classes: {
|
|
26
|
+
root: 'bg-white absolute right-[0.625rem] pointer-events-none m-0 select-none',
|
|
27
|
+
} }));
|
|
28
|
+
const getInputMask = (value) => {
|
|
29
|
+
const startsWithTwo = value && value[0] === '2';
|
|
30
|
+
return [/[0-2]/, startsWithTwo ? /[0-3]/ : /[0-9]/, ':', /[0-5]/, /[0-9]/];
|
|
31
|
+
};
|
|
17
32
|
export const TimePicker = (_a) => {
|
|
33
|
+
var _b;
|
|
18
34
|
var { status = 'default' } = _a, props = __rest(_a, ["status"]);
|
|
19
|
-
const { onChange: externalOnChange, value: externalValue, width, className, highlight, size } = props, rest = __rest(props, ["onChange", "value", "width", "className", "highlight", "size"]);
|
|
35
|
+
const { onChange: externalOnChange, onKeyDown: externalOnKeyDown, onBlur: externalOnBlur, onFocus: externalOnFocus, value: externalValue, width, className, highlight, size, minuteStep } = props, rest = __rest(props, ["onChange", "onKeyDown", "onBlur", "onFocus", "value", "width", "className", "highlight", "size", "minuteStep"]);
|
|
20
36
|
const [value, setValue] = useState(externalValue);
|
|
37
|
+
const hasTimeList = minuteStep !== undefined;
|
|
38
|
+
const interactive = hasTimeList && !rest.disabled && !rest.readOnly;
|
|
21
39
|
useEffect(() => {
|
|
22
40
|
// Set internal value based on the provided one if the later is correct
|
|
23
41
|
if (externalValue && VALID_TIME_REGEX.test(externalValue)) {
|
|
24
42
|
setValue(externalValue);
|
|
25
43
|
}
|
|
26
44
|
}, [externalValue]);
|
|
27
|
-
const
|
|
28
|
-
const newValue = event.target.value;
|
|
45
|
+
const changeValue = (newValue) => {
|
|
29
46
|
setValue(newValue);
|
|
30
47
|
if (newValue && VALID_TIME_REGEX.test(newValue)) {
|
|
31
48
|
externalOnChange === null || externalOnChange === void 0 ? void 0 : externalOnChange(newValue);
|
|
@@ -34,13 +51,50 @@ export const TimePicker = (_a) => {
|
|
|
34
51
|
externalOnChange === null || externalOnChange === void 0 ? void 0 : externalOnChange('');
|
|
35
52
|
}
|
|
36
53
|
};
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
const onChange = (event) => changeValue(event.target.value);
|
|
55
|
+
const isSafari = ((_b = detect()) === null || _b === void 0 ? void 0 : _b.name) === 'safari';
|
|
56
|
+
const { isOpen, hourCycle, anchorRef, listRef, focusList, isFocusChangeWithinList, handleTriggerClick, handleClickAway, closeAndFocusField, handleFieldFocus, handleFieldKeyDown, } = useTimePickerPopover({
|
|
57
|
+
enabled: interactive,
|
|
58
|
+
// Safari renders its own 24-hour masked field, so the list has to match it
|
|
59
|
+
getHourCycle: () => (isSafari ? 24 : getHourCycle()),
|
|
60
|
+
});
|
|
61
|
+
const handleTimePick = (time) => {
|
|
62
|
+
changeValue(time);
|
|
63
|
+
closeAndFocusField();
|
|
64
|
+
};
|
|
65
|
+
const handleKeyDown = (event) => {
|
|
66
|
+
externalOnKeyDown === null || externalOnKeyDown === void 0 ? void 0 : externalOnKeyDown(event);
|
|
67
|
+
handleFieldKeyDown(event);
|
|
68
|
+
};
|
|
69
|
+
const handleBlur = (event) => {
|
|
70
|
+
if (!isFocusChangeWithinList(event)) {
|
|
71
|
+
externalOnBlur === null || externalOnBlur === void 0 ? void 0 : externalOnBlur(event);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const handleFocus = (event) => {
|
|
75
|
+
handleFieldFocus();
|
|
76
|
+
if (!isFocusChangeWithinList(event)) {
|
|
77
|
+
externalOnFocus === null || externalOnFocus === void 0 ? void 0 : externalOnFocus(event);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const adornmentProps = hasTimeList
|
|
81
|
+
? {
|
|
82
|
+
endAdornment: (React.createElement(TimePickerTrigger, { open: isOpen, disabled: !interactive, onClick: handleTriggerClick })),
|
|
83
|
+
}
|
|
84
|
+
: { iconPosition: 'end', icon: nativePickerIcon };
|
|
85
|
+
const timeList = hasTimeList && (React.createElement(TimePickerDropdown, { ref: listRef, open: isOpen, anchorEl: anchorRef.current, onClickAway: handleClickAway, value: value, minuteStep: minuteStep, hourCycle: hourCycle, autoFocus: focusList, onChange: handleTimePick, onClose: closeAndFocusField }));
|
|
40
86
|
const inputClassName = twMerge('cursor-default', className);
|
|
41
|
-
const inputPropClassName =
|
|
42
|
-
|
|
43
|
-
|
|
87
|
+
const inputPropClassName = hasTimeList
|
|
88
|
+
? TIME_LIST_CLASS_NAME
|
|
89
|
+
: NATIVE_PICKER_CLASS_NAME;
|
|
90
|
+
if (isSafari) {
|
|
91
|
+
return (React.createElement(React.Fragment, null,
|
|
92
|
+
React.createElement(Input, Object.assign({ type: 'text', readOnly: true }, adornmentProps, { width: width, status: status, className: inputClassName, highlight: highlight, size: size, outlineRef: anchorRef, inputProps: Object.assign({ className: inputPropClassName }, rest), startAdornment: React.createElement(InputMask, { mask: getInputMask(value), alwaysShowMask: true, maskPlaceholder: '-', value: value, onChange: onChange, onKeyDown: handleKeyDown, onBlur: handleBlur, onFocus: handleFocus, className: 'text-sm border-none p-0 m-0 outline-hidden' }) })),
|
|
93
|
+
timeList));
|
|
94
|
+
}
|
|
95
|
+
return (React.createElement(React.Fragment, null,
|
|
96
|
+
React.createElement(Input, Object.assign({ type: 'time', value: value, className: inputClassName, onChange: onChange, onKeyDown: handleKeyDown, onBlur: handleBlur, onFocus: handleFocus }, adornmentProps, { highlight: highlight, width: width, size: size, status: status, outlineRef: anchorRef, inputProps: Object.assign({ className: inputPropClassName, step: 60 }, rest) })),
|
|
97
|
+
timeList));
|
|
44
98
|
};
|
|
45
99
|
TimePicker.displayName = 'TimePicker';
|
|
46
100
|
export default TimePicker;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimePicker.js","sourceRoot":"","sources":["../../../src/TimePicker/TimePicker.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAElD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAG9C,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"TimePicker.js","sourceRoot":"","sources":["../../../src/TimePicker/TimePicker.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { HourCycle } from './utils';
|
|
3
|
+
interface Options {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
getHourCycle: () => HourCycle;
|
|
6
|
+
}
|
|
7
|
+
export declare const useTimePickerPopover: ({ enabled, getHourCycle }: Options) => {
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
hourCycle: HourCycle;
|
|
10
|
+
anchorRef: React.RefObject<HTMLElement>;
|
|
11
|
+
listRef: React.RefObject<HTMLDivElement>;
|
|
12
|
+
focusList: boolean;
|
|
13
|
+
isFocusChangeWithinList: (event: React.FocusEvent) => boolean;
|
|
14
|
+
handleTriggerClick: () => void;
|
|
15
|
+
handleClickAway: (event: React.MouseEvent) => void;
|
|
16
|
+
closeAndFocusField: () => void;
|
|
17
|
+
handleFieldFocus: () => void;
|
|
18
|
+
handleFieldKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=use-time-picker-popover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-time-picker-popover.d.ts","sourceRoot":"","sources":["../../../src/TimePicker/use-time-picker-popover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,UAAU,OAAO;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,MAAM,SAAS,CAAA;CAC9B;AAMD,eAAO,MAAM,oBAAoB,8BAA+B,OAAO;;;;;;qCAgD7B,KAAK,CAAC,UAAU;;6BANxB,KAAK,CAAC,UAAU;;;gCAgBb,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC;CA+CzE,CAAA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
// The shortcuts a browser uses to open the native time picker
|
|
3
|
+
const isOpenShortcut = (event) => event.key === ' ' || (event.key === 'ArrowDown' && event.altKey);
|
|
4
|
+
export const useTimePickerPopover = ({ enabled, getHourCycle }) => {
|
|
5
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
6
|
+
const [focusList, setFocusList] = useState(false);
|
|
7
|
+
const [hourCycle, setHourCycle] = useState(24);
|
|
8
|
+
const anchorRef = useRef(null);
|
|
9
|
+
const listRef = useRef(null);
|
|
10
|
+
const focusField = () => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
return (_b = (_a = anchorRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('input:not([readonly])')) === null || _b === void 0 ? void 0 : _b.focus();
|
|
13
|
+
};
|
|
14
|
+
const open = ({ focusList: shouldFocusList }) => {
|
|
15
|
+
setFocusList(shouldFocusList);
|
|
16
|
+
setHourCycle(getHourCycle());
|
|
17
|
+
setIsOpen(true);
|
|
18
|
+
};
|
|
19
|
+
const close = () => setIsOpen(false);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!enabled) {
|
|
22
|
+
close();
|
|
23
|
+
}
|
|
24
|
+
}, [enabled]);
|
|
25
|
+
const closeAndFocusField = () => {
|
|
26
|
+
close();
|
|
27
|
+
focusField();
|
|
28
|
+
};
|
|
29
|
+
const handleTriggerClick = () => {
|
|
30
|
+
if (isOpen) {
|
|
31
|
+
closeAndFocusField();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
open({ focusList: false });
|
|
35
|
+
focusField();
|
|
36
|
+
};
|
|
37
|
+
const handleClickAway = (event) => {
|
|
38
|
+
var _a;
|
|
39
|
+
if (!((_a = anchorRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))) {
|
|
40
|
+
close();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const isFocusChangeWithinList = (event) => {
|
|
44
|
+
var _a;
|
|
45
|
+
return event.relatedTarget instanceof Node &&
|
|
46
|
+
Boolean((_a = listRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.relatedTarget));
|
|
47
|
+
};
|
|
48
|
+
const handleFieldFocus = () => {
|
|
49
|
+
// Focus is back in the field, so the next shortcut must be able to move it
|
|
50
|
+
// into the list again
|
|
51
|
+
setFocusList(false);
|
|
52
|
+
};
|
|
53
|
+
const handleFieldKeyDown = (event) => {
|
|
54
|
+
if (!isOpen) {
|
|
55
|
+
if (enabled && isOpenShortcut(event)) {
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
open({ focusList: true });
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (isOpenShortcut(event)) {
|
|
62
|
+
// Keeps the browser from opening its own time picker over the list
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
setFocusList(true);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
switch (event.key) {
|
|
68
|
+
case 'Escape':
|
|
69
|
+
// Keeps a surrounding Modal or Drawer open while the list closes
|
|
70
|
+
event.stopPropagation();
|
|
71
|
+
close();
|
|
72
|
+
break;
|
|
73
|
+
case 'Enter':
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
close();
|
|
76
|
+
break;
|
|
77
|
+
case 'Tab':
|
|
78
|
+
close();
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
isOpen,
|
|
84
|
+
hourCycle,
|
|
85
|
+
anchorRef,
|
|
86
|
+
listRef,
|
|
87
|
+
focusList,
|
|
88
|
+
isFocusChangeWithinList,
|
|
89
|
+
handleTriggerClick,
|
|
90
|
+
handleClickAway,
|
|
91
|
+
closeAndFocusField,
|
|
92
|
+
handleFieldFocus,
|
|
93
|
+
handleFieldKeyDown,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=use-time-picker-popover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-time-picker-popover.js","sourceRoot":"","sources":["../../../src/TimePicker/use-time-picker-popover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-hour-cycle.d.ts","sourceRoot":"","sources":["../../../../src/TimePicker/utils/get-hour-cycle.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,CAAA;AA6D/B,eAAO,MAAM,YAAY,YAAa,MAAM,KAAG,SACU,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const PROBE_CLASS_NAME = 'fixed top-0 left-0 invisible pointer-events-none h-auto p-0 border-0';
|
|
2
|
+
// Blink calls the AM/PM segment `ampm-field`, WebKit calls it `meridiem-field`
|
|
3
|
+
const SEGMENT_CLASS_NAMES = {
|
|
4
|
+
hour: '[&::-webkit-datetime-edit-hour-field]:inline-block [&::-webkit-datetime-edit-hour-field]:h-screen',
|
|
5
|
+
ampm: '[&::-webkit-datetime-edit-ampm-field]:inline-block [&::-webkit-datetime-edit-ampm-field]:h-screen [&::-webkit-datetime-edit-meridiem-field]:inline-block [&::-webkit-datetime-edit-meridiem-field]:h-screen',
|
|
6
|
+
};
|
|
7
|
+
const measureProbe = (segment) => {
|
|
8
|
+
const probe = document.createElement('input');
|
|
9
|
+
probe.type = 'time';
|
|
10
|
+
probe.tabIndex = -1;
|
|
11
|
+
probe.setAttribute('aria-hidden', 'true');
|
|
12
|
+
probe.className = PROBE_CLASS_NAME;
|
|
13
|
+
if (segment) {
|
|
14
|
+
probe.className += ` ${SEGMENT_CLASS_NAMES[segment]}`;
|
|
15
|
+
probe.setAttribute('data-testid', `time-picker-probe-${segment}`);
|
|
16
|
+
}
|
|
17
|
+
document.body.appendChild(probe);
|
|
18
|
+
const height = probe.offsetHeight;
|
|
19
|
+
probe.remove();
|
|
20
|
+
return height;
|
|
21
|
+
};
|
|
22
|
+
// The native field follows the operating system's 12/24-hour setting, which
|
|
23
|
+
// `Intl` does not see, so the field itself is the only reliable source
|
|
24
|
+
const getNativeFieldHourCycle = () => {
|
|
25
|
+
if (typeof document === 'undefined') {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const stretchedHeight = measureProbe() * 2;
|
|
29
|
+
const rendersSegment = (segment) => measureProbe(segment) > stretchedHeight;
|
|
30
|
+
if (!rendersSegment('hour')) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return rendersSegment('ampm') ? 12 : 24;
|
|
34
|
+
};
|
|
35
|
+
const getLocaleHourCycle = (locale) => {
|
|
36
|
+
const { hour12 } = new Intl.DateTimeFormat(locale, {
|
|
37
|
+
hour: 'numeric',
|
|
38
|
+
}).resolvedOptions();
|
|
39
|
+
return hour12 ? 12 : 24;
|
|
40
|
+
};
|
|
41
|
+
export const getHourCycle = (locale) => { var _a; return (_a = getNativeFieldHourCycle()) !== null && _a !== void 0 ? _a : getLocaleHourCycle(locale); };
|
|
42
|
+
//# sourceMappingURL=get-hour-cycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-hour-cycle.js","sourceRoot":"","sources":["../../../../src/TimePicker/utils/get-hour-cycle.ts"],"names":[],"mappings":"AAIA,MAAM,gBAAgB,GACpB,sEAAsE,CAAA;AAExE,+EAA+E;AAC/E,MAAM,mBAAmB,GAA4B;IACnD,IAAI,EAAE,mGAAmG;IACzG,IAAI,EAAE,6MAA6M;CACpN,CAAA;AAED,MAAM,YAAY,GAAG,CAAC,OAAiB,EAAE,EAAE;IACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IAE7C,KAAK,CAAC,IAAI,GAAG,MAAM,CAAA;IACnB,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAA;IACnB,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;IACzC,KAAK,CAAC,SAAS,GAAG,gBAAgB,CAAA;IAElC,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,SAAS,IAAI,IAAI,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAA;QACrD,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,qBAAqB,OAAO,EAAE,CAAC,CAAA;IACnE,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;IAEhC,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAA;IAEjC,KAAK,CAAC,MAAM,EAAE,CAAA;IAEd,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,4EAA4E;AAC5E,uEAAuE;AACvE,MAAM,uBAAuB,GAAG,GAA0B,EAAE;IAC1D,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,eAAe,GAAG,YAAY,EAAE,GAAG,CAAC,CAAA;IAC1C,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAE,EAAE,CAC1C,YAAY,CAAC,OAAO,CAAC,GAAG,eAAe,CAAA;IAEzC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AACzC,CAAC,CAAA;AAED,MAAM,kBAAkB,GAAG,CAAC,MAAe,EAAa,EAAE;IACxD,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE;QACjD,IAAI,EAAE,SAAS;KAChB,CAAC,CAAC,eAAe,EAAE,CAAA;IAEpB,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AACzB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAe,EAAa,EAAE,WACzD,OAAA,MAAA,uBAAuB,EAAE,mCAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA,EAAA,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { getHourCycle } from './get-hour-cycle';
|
|
2
|
+
export type { HourCycle } from './get-hour-cycle';
|
|
3
|
+
export { VALID_TIME_REGEX, getCurrentTime, getNearestOptionIndex, getTimeOptions, parseTime, } from './time';
|
|
4
|
+
export type { Time, TimeOption } from './time';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/TimePicker/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,cAAc,EACd,SAAS,GACV,MAAM,QAAQ,CAAA;AACf,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/TimePicker/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE/C,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,cAAc,EACd,SAAS,GACV,MAAM,QAAQ,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HourCycle } from './get-hour-cycle';
|
|
2
|
+
export interface Time {
|
|
3
|
+
hours: number;
|
|
4
|
+
minutes: number;
|
|
5
|
+
}
|
|
6
|
+
export interface TimeOption {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const VALID_TIME_REGEX: RegExp;
|
|
11
|
+
export declare const parseTime: (value?: string) => Time | undefined;
|
|
12
|
+
export declare const formatTime: ({ hours, minutes }: Time) => string;
|
|
13
|
+
export declare const getCurrentTime: () => Time;
|
|
14
|
+
export declare const getTimeOptions: (minuteStep: number, hourCycle: HourCycle) => TimeOption[];
|
|
15
|
+
export declare const getNearestOptionIndex: (time: Time, minuteStep: number) => number;
|
|
16
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../../../src/TimePicker/utils/time.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,eAAO,MAAM,gBAAgB,QAAqC,CAAA;AAQlE,eAAO,MAAM,SAAS,WAAY,MAAM,KAAG,IAAI,GAAG,SAQjD,CAAA;AAED,eAAO,MAAM,UAAU,uBAAwB,IAAI,WACF,CAAA;AAEjD,eAAO,MAAM,cAAc,QAAO,IAIjC,CAAA;AAaD,eAAO,MAAM,cAAc,eACb,MAAM,aACP,SAAS,KACnB,UAAU,EAgBZ,CAAA;AAED,eAAO,MAAM,qBAAqB,SAAU,IAAI,cAAc,MAAM,WAIjE,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const VALID_TIME_REGEX = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/;
|
|
2
|
+
const MINUTES_IN_HOUR = 60;
|
|
3
|
+
const MINUTES_IN_DAY = 24 * MINUTES_IN_HOUR;
|
|
4
|
+
const HALF_DAY = 12;
|
|
5
|
+
const padTimePart = (part) => String(part).padStart(2, '0');
|
|
6
|
+
export const parseTime = (value) => {
|
|
7
|
+
if (!value || !VALID_TIME_REGEX.test(value)) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const [hours, minutes] = value.split(':').map(Number);
|
|
11
|
+
return { hours, minutes };
|
|
12
|
+
};
|
|
13
|
+
export const formatTime = ({ hours, minutes }) => `${padTimePart(hours)}:${padTimePart(minutes)}`;
|
|
14
|
+
export const getCurrentTime = () => {
|
|
15
|
+
const now = new Date();
|
|
16
|
+
return { hours: now.getHours(), minutes: now.getMinutes() };
|
|
17
|
+
};
|
|
18
|
+
const formatLabel = (time, hourCycle) => {
|
|
19
|
+
if (hourCycle === 24) {
|
|
20
|
+
return formatTime(time);
|
|
21
|
+
}
|
|
22
|
+
const hours = time.hours % HALF_DAY || HALF_DAY;
|
|
23
|
+
const period = time.hours < HALF_DAY ? 'AM' : 'PM';
|
|
24
|
+
return `${formatTime({ hours, minutes: time.minutes })} ${period}`;
|
|
25
|
+
};
|
|
26
|
+
export const getTimeOptions = (minuteStep, hourCycle) => {
|
|
27
|
+
const options = [];
|
|
28
|
+
for (let minutes = 0; minutes < MINUTES_IN_DAY; minutes += minuteStep) {
|
|
29
|
+
const time = {
|
|
30
|
+
hours: Math.floor(minutes / MINUTES_IN_HOUR),
|
|
31
|
+
minutes: minutes % MINUTES_IN_HOUR,
|
|
32
|
+
};
|
|
33
|
+
options.push({
|
|
34
|
+
value: formatTime(time),
|
|
35
|
+
label: formatLabel(time, hourCycle),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return options;
|
|
39
|
+
};
|
|
40
|
+
export const getNearestOptionIndex = (time, minuteStep) => Math.min(Math.round((time.hours * MINUTES_IN_HOUR + time.minutes) / minuteStep), Math.ceil(MINUTES_IN_DAY / minuteStep) - 1);
|
|
41
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../../../src/TimePicker/utils/time.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,gBAAgB,GAAG,kCAAkC,CAAA;AAElE,MAAM,eAAe,GAAG,EAAE,CAAA;AAC1B,MAAM,cAAc,GAAG,EAAE,GAAG,eAAe,CAAA;AAC3C,MAAM,QAAQ,GAAG,EAAE,CAAA;AAEnB,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAEnE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAc,EAAoB,EAAE;IAC5D,IAAI,CAAC,KAAK,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAErD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;AAC3B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAQ,EAAE,EAAE,CACrD,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAA;AAEjD,MAAM,CAAC,MAAM,cAAc,GAAG,GAAS,EAAE;IACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IAEtB,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,EAAE,EAAE,CAAA;AAC7D,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,IAAU,EAAE,SAAoB,EAAE,EAAE;IACvD,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;QACrB,OAAO,UAAU,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,QAAQ,IAAI,QAAQ,CAAA;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;IAElD,OAAO,GAAG,UAAU,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,MAAM,EAAE,CAAA;AACpE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,UAAkB,EAClB,SAAoB,EACN,EAAE;IAChB,MAAM,OAAO,GAAiB,EAAE,CAAA;IAEhC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,cAAc,EAAE,OAAO,IAAI,UAAU,EAAE,CAAC;QACtE,MAAM,IAAI,GAAG;YACX,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,eAAe,CAAC;YAC5C,OAAO,EAAE,OAAO,GAAG,eAAe;SACnC,CAAA;QAED,OAAO,CAAC,IAAI,CAAC;YACX,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC;YACvB,KAAK,EAAE,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC;SACpC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,IAAU,EAAE,UAAkB,EAAE,EAAE,CACtE,IAAI,CAAC,GAAG,CACN,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,EACtE,IAAI,CAAC,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,GAAG,CAAC,CAC3C,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { TimePickerListProps } from '../TimePickerList';
|
|
3
|
+
export interface Props extends TimePickerListProps {
|
|
4
|
+
/** Whether the dropdown is shown */
|
|
5
|
+
open: boolean;
|
|
6
|
+
/** Element the dropdown is positioned under */
|
|
7
|
+
anchorEl: HTMLElement | null;
|
|
8
|
+
/** Called when a click lands outside of the dropdown */
|
|
9
|
+
onClickAway: (event: React.MouseEvent) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const TimePickerDropdown: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLDivElement>>;
|
|
12
|
+
export default TimePickerDropdown;
|
|
13
|
+
//# sourceMappingURL=TimePickerDropdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimePickerDropdown.d.ts","sourceRoot":"","sources":["../../../src/TimePickerDropdown/TimePickerDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAA;AAKzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAE5D,MAAM,WAAW,KAAM,SAAQ,mBAAmB;IAChD,oCAAoC;IACpC,IAAI,EAAE,OAAO,CAAA;IACb,+CAA+C;IAC/C,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,wDAAwD;IACxD,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAA;CAC/C;AAED,eAAO,MAAM,kBAAkB,8EAwB9B,CAAA;AAID,eAAe,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { forwardRef } from 'react';
|
|
13
|
+
import { Popper } from '@toptal/picasso-popper';
|
|
14
|
+
import { ClickAwayListener } from '@toptal/picasso-utils';
|
|
15
|
+
import { TimePickerList } from '../TimePickerList';
|
|
16
|
+
export const TimePickerDropdown = forwardRef(function TimePickerDropdown(_a, ref) {
|
|
17
|
+
var { open, anchorEl, onClickAway } = _a, rest = __rest(_a, ["open", "anchorEl", "onClickAway"]);
|
|
18
|
+
if (!open || !anchorEl) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return (React.createElement(Popper, { open: true, role: 'presentation', anchorEl: anchorEl, placement: 'bottom-start' },
|
|
22
|
+
React.createElement(ClickAwayListener, { onClickAway: onClickAway },
|
|
23
|
+
React.createElement("div", { ref: ref },
|
|
24
|
+
React.createElement(TimePickerList, Object.assign({}, rest))))));
|
|
25
|
+
});
|
|
26
|
+
TimePickerDropdown.displayName = 'TimePickerDropdown';
|
|
27
|
+
export default TimePickerDropdown;
|
|
28
|
+
//# sourceMappingURL=TimePickerDropdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimePickerDropdown.js","sourceRoot":"","sources":["../../../src/TimePickerDropdown/TimePickerDropdown.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAYlD,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAC1C,SAAS,kBAAkB,CACzB,EAA+C,EAC/C,GAAG;QADH,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,OAAkB,EAAb,IAAI,cAAtC,mCAAwC,CAAF;IAGtC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACvB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,oBAAC,MAAM,IACL,IAAI,QACJ,IAAI,EAAC,cAAc,EACnB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAC,cAAc;QAExB,oBAAC,iBAAiB,IAAC,WAAW,EAAE,WAAW;YACzC,6BAAK,GAAG,EAAE,GAAG;gBACX,oBAAC,cAAc,oBAAK,IAAI,EAAI,CACxB,CACY,CACb,CACV,CAAA;AACH,CAAC,CACF,CAAA;AAED,kBAAkB,CAAC,WAAW,GAAG,oBAAoB,CAAA;AAErD,eAAe,kBAAkB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/TimePickerDropdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/TimePickerDropdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { BaseProps } from '@toptal/picasso-shared';
|
|
3
|
+
import type { HourCycle } from '../TimePicker/utils';
|
|
4
|
+
export interface Props extends BaseProps {
|
|
5
|
+
/** Selected time in 24-hour `HH:mm` format */
|
|
6
|
+
value?: string;
|
|
7
|
+
/** Interval in minutes between the offered times */
|
|
8
|
+
minuteStep: number;
|
|
9
|
+
/** Whether the times are labelled on a 12-hour clock with AM/PM or a 24-hour clock */
|
|
10
|
+
hourCycle: HourCycle;
|
|
11
|
+
/** Moves focus to the selected or nearest time when the list appears */
|
|
12
|
+
autoFocus?: boolean;
|
|
13
|
+
/** Called with the picked time in 24-hour `HH:mm` format */
|
|
14
|
+
onChange: (value: string) => void;
|
|
15
|
+
/** Called when the keyboard asks to close the list */
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
}
|
|
18
|
+
export declare const TimePickerList: {
|
|
19
|
+
({ value, minuteStep, hourCycle, autoFocus, onChange, onClose, className, style, "data-testid": dataTestId, }: Props): React.ReactElement;
|
|
20
|
+
displayName: string;
|
|
21
|
+
};
|
|
22
|
+
export default TimePickerList;
|
|
23
|
+
//# sourceMappingURL=TimePickerList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TimePickerList.d.ts","sourceRoot":"","sources":["../../../src/TimePickerList/TimePickerList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAA;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAWvD,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,qBAAqB,CAAA;AAEhE,MAAM,WAAW,KAAM,SAAQ,SAAS;IACtC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAA;IAClB,sFAAsF;IACtF,SAAS,EAAE,SAAS,CAAA;IACpB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,4DAA4D;IAC5D,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,sDAAsD;IACtD,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AA4CD,eAAO,MAAM,cAAc;mHAUxB,KAAK,GAAG,KAAK,CAAC,YAAY;;CAwG5B,CAAA;AAID,eAAe,cAAc,CAAA"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React, { useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { Menu, MenuItem } from '@toptal/picasso-menu';
|
|
3
|
+
import { useIsomorphicLayoutEffect } from '@toptal/picasso-utils';
|
|
4
|
+
import { twMerge } from '@toptal/picasso-tailwind-merge';
|
|
5
|
+
import { getCurrentTime, getNearestOptionIndex, getTimeOptions, parseTime, } from '../TimePicker/utils';
|
|
6
|
+
const OPTION = '[role="option"]';
|
|
7
|
+
const preventFocusLoss = (event) => {
|
|
8
|
+
// Clicking a time must keep the caret in the field, like the browser picker does
|
|
9
|
+
event.preventDefault();
|
|
10
|
+
};
|
|
11
|
+
const getStartingIndex = (options, minuteStep, value) => {
|
|
12
|
+
var _a;
|
|
13
|
+
const selectedIndex = options.findIndex(option => option.value === value);
|
|
14
|
+
return selectedIndex >= 0
|
|
15
|
+
? selectedIndex
|
|
16
|
+
: getNearestOptionIndex((_a = parseTime(value)) !== null && _a !== void 0 ? _a : getCurrentTime(), minuteStep);
|
|
17
|
+
};
|
|
18
|
+
const scrollToOption = (list, option) => {
|
|
19
|
+
const listRect = list.getBoundingClientRect();
|
|
20
|
+
const optionRect = option.getBoundingClientRect();
|
|
21
|
+
list.scrollTop +=
|
|
22
|
+
optionRect.top - listRect.top - (listRect.height - optionRect.height) / 2;
|
|
23
|
+
};
|
|
24
|
+
const getNextIndex = (key, index, count) => {
|
|
25
|
+
switch (key) {
|
|
26
|
+
case 'ArrowDown':
|
|
27
|
+
return Math.min(index + 1, count - 1);
|
|
28
|
+
case 'ArrowUp':
|
|
29
|
+
return Math.max(index - 1, 0);
|
|
30
|
+
case 'Home':
|
|
31
|
+
return 0;
|
|
32
|
+
case 'End':
|
|
33
|
+
return count - 1;
|
|
34
|
+
default:
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export const TimePickerList = ({ value, minuteStep, hourCycle, autoFocus = false, onChange, onClose, className, style, 'data-testid': dataTestId, }) => {
|
|
39
|
+
const listRef = useRef(null);
|
|
40
|
+
const options = useMemo(() => getTimeOptions(minuteStep, hourCycle), [minuteStep, hourCycle]);
|
|
41
|
+
const [initialIndex] = useState(() => getStartingIndex(options, minuteStep, value));
|
|
42
|
+
const getOptionElements = () => { var _a, _b; return Array.from((_b = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll(OPTION)) !== null && _b !== void 0 ? _b : []); };
|
|
43
|
+
useIsomorphicLayoutEffect(() => {
|
|
44
|
+
const list = listRef.current;
|
|
45
|
+
const option = getOptionElements()[initialIndex];
|
|
46
|
+
if (list && option) {
|
|
47
|
+
scrollToOption(list, option);
|
|
48
|
+
}
|
|
49
|
+
}, [initialIndex]);
|
|
50
|
+
useIsomorphicLayoutEffect(() => {
|
|
51
|
+
if (!autoFocus) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// The value may have been typed since the list opened, so the time to
|
|
55
|
+
// focus is looked up at the moment focus is requested
|
|
56
|
+
const list = listRef.current;
|
|
57
|
+
const option = getOptionElements()[getStartingIndex(options, minuteStep, value)];
|
|
58
|
+
if (list && option) {
|
|
59
|
+
scrollToOption(list, option);
|
|
60
|
+
option.focus({ preventScroll: true });
|
|
61
|
+
}
|
|
62
|
+
}, [autoFocus]);
|
|
63
|
+
const handleKeyDown = (event) => {
|
|
64
|
+
var _a;
|
|
65
|
+
const optionElements = getOptionElements();
|
|
66
|
+
const index = optionElements.indexOf(event.target.closest(OPTION));
|
|
67
|
+
switch (event.key) {
|
|
68
|
+
case 'Enter':
|
|
69
|
+
case ' ':
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
event.stopPropagation();
|
|
72
|
+
if (index >= 0) {
|
|
73
|
+
onChange(options[index].value);
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case 'Escape':
|
|
77
|
+
// Keeps a surrounding Modal or Drawer open while the list closes
|
|
78
|
+
event.stopPropagation();
|
|
79
|
+
onClose();
|
|
80
|
+
break;
|
|
81
|
+
case 'Tab':
|
|
82
|
+
onClose();
|
|
83
|
+
break;
|
|
84
|
+
default: {
|
|
85
|
+
const nextIndex = getNextIndex(event.key, index, options.length);
|
|
86
|
+
if (nextIndex !== undefined) {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
(_a = optionElements[nextIndex]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
return (React.createElement(Menu, { ref: listRef, role: 'listbox', "aria-label": 'Choose time', allowNestedNavigation: false, onKeyDown: handleKeyDown, onMouseDown: preventFocusLoss, className: twMerge('bg-white shadow-5 max-h-[15rem] overflow-y-auto', className), style: style, "data-testid": dataTestId }, options.map(option => (React.createElement(MenuItem, { key: option.value, role: 'option', "aria-selected": option.value === value, checkmarked: option.value === value, titleCase: false, className: 'tabular-nums', onClick: () => onChange(option.value) }, option.label)))));
|
|
94
|
+
};
|
|
95
|
+
TimePickerList.displayName = 'TimePickerList';
|
|
96
|
+
export default TimePickerList;
|
|
97
|
+
//# sourceMappingURL=TimePickerList.js.map
|