@neo4j-ndl/react
Version:
React implementation of Neo4j Design System
229 lines • 11.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimePicker = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
/**
*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const classnames_1 = __importDefault(require("classnames"));
const react_1 = require("react");
const icons_1 = require("../icons");
const popover_1 = require("../popover");
const text_input_1 = require("../text-input");
const theme_1 = require("../theme");
const generate_time_options_1 = require("./generate-time-options");
const needle_time_1 = require("./needle-time");
const time_picker_hooks_1 = require("./time-picker-hooks");
exports.TimePicker = (0, react_1.forwardRef)(function TimePicker(props, ref) {
var _a;
const { format = 'hh:mm', isDisabled, isFluid, isReadOnly, isRequired, selected, timeInterval = 15, label, onChange, className, onError, style, size = 'medium', errorText, htmlAttributes, } = props;
const [editedInput, setEditedInput] = (0, react_1.useState)(selected);
const is24Hour = format === 'hh:mm';
const [inputValue, setInputValue] = (0, react_1.useState)((selected === null || selected === void 0 ? void 0 : selected.toString(is24Hour)) || '');
const listRef = (0, react_1.useRef)(null);
const inputRef = (0, react_1.useRef)(null);
(0, react_1.useImperativeHandle)(ref, () => inputRef.current, []);
const [lastAction, setLastAction] = (0, react_1.useState)('type');
const [error, setError] = (0, react_1.useState)(undefined);
const timeOptions = (0, react_1.useMemo)(() => (0, generate_time_options_1.generateTimeOptions)(is24Hour, timeInterval), [is24Hour, timeInterval]);
const { themeClassName } = (0, theme_1.useNeedleTheme)();
const { isPopoverOpen, setIsPopoverOpen } = (0, time_picker_hooks_1.useTimePickerPopover)(false);
const { focusedIndex, setFocusedIndex, handleArrowNavigation } = (0, time_picker_hooks_1.useKeyboardNavigation)(listRef, timeOptions.length);
const isValidTime = (0, react_1.useCallback)((hours, minutes) => {
if (hours > 23 || hours < 0) {
return false;
}
if (minutes > 59 || minutes < 0) {
return false;
}
return true;
}, []);
(0, react_1.useEffect)(() => {
if (error) {
onError === null || onError === void 0 ? void 0 : onError(error, needle_time_1.NeedleTime.fromString(inputValue));
}
}, [error, inputValue, onError]);
const handleOnClick = (timeText) => {
if (onChange) {
onChange(needle_time_1.NeedleTime.fromString(timeText));
}
setError(undefined);
setInputValue(timeText);
setEditedInput(needle_time_1.NeedleTime.fromString(timeText));
setIsPopoverOpen(false);
};
(0, react_1.useEffect)(() => {
setEditedInput(selected);
setInputValue((selected === null || selected === void 0 ? void 0 : selected.toString(is24Hour)) || '');
}, [is24Hour, selected]);
(0, react_1.useEffect)(() => {
if (isPopoverOpen) {
setTimeout(() => {
var _a, _b;
(_b = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.children[focusedIndex]) === null || _b === void 0 ? void 0 : _b.scrollIntoView({
block: 'center',
inline: 'nearest',
});
}, 0);
}
else {
setTimeout(() => {
var _a, _b;
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
const selectedIndex = (_b = timeOptions.indexOf((selected === null || selected === void 0 ? void 0 : selected.toString(is24Hour)) || '')) !== null && _b !== void 0 ? _b : 0;
setFocusedIndex(selectedIndex);
}, 0);
}
}, [
focusedIndex,
is24Hour,
isPopoverOpen,
selected,
setFocusedIndex,
timeOptions,
]);
const sizeClasses = {
'ndl-small': size === 'small',
'ndl-medium': size === 'medium',
'ndl-large': size === 'large',
};
const newHandleInputChange = (e) => {
scrollToTime(e.target.value);
setInputValue(e.target.value);
setLastAction('type');
const time = needle_time_1.NeedleTime.fromString(e.target.value);
if (!isValidTime(time.hour, time.minute)) {
setError('Invalid time');
}
else {
setError(undefined);
}
};
const handleKeyDown = (e) => {
var _a;
if (e.key === 'Escape') {
setIsPopoverOpen(false);
}
setIsPopoverOpen(true);
if (e.key === 'Enter') {
if (lastAction === 'type' && inputValue.length >= (is24Hour ? 5 : 6)) {
const time = needle_time_1.NeedleTime.fromString(inputValue);
handleOnClick(time.toString(is24Hour));
}
else if (lastAction === 'type' && !inputValue.includes(':')) {
const time = needle_time_1.NeedleTime.fromString(inputValue);
if (isValidTime(time.hour, time.minute)) {
handleOnClick(time.toString(is24Hour));
}
setIsPopoverOpen(false);
}
else {
handleOnClick(timeOptions[focusedIndex]);
}
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
}
else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
setLastAction('arrow');
handleArrowNavigation(e.key);
}
};
const scrollToTime = (startOfTime) => {
const parsedTime = startOfTime.toLowerCase().replace(/[^0-9amp]/g, '');
let index = -1;
// Handle AM/PM input
const isPM = parsedTime.includes('p');
const isAM = parsedTime.includes('a');
const timeDigits = parsedTime.replace(/[amp]/g, '');
if (timeDigits.length === 1) {
// Single digit - assume it's an hour
const hour = `0${timeDigits}`;
const searchTime = is24Hour
? `${hour}:00`
: `${hour}:00 ${isPM ? 'PM' : 'AM'}`;
index = timeOptions.indexOf(searchTime);
}
else {
// Find closest matching time
const closestTime = timeOptions.find((time) => {
const timeWithoutSpecialChars = time
.toLowerCase()
.replace(/[^0-9amp]/g, '');
if (isPM || isAM) {
// If AM/PM specified, match that specifically
return timeWithoutSpecialChars.startsWith(parsedTime);
}
else {
// Otherwise just match the numbers
return timeWithoutSpecialChars
.replace(/[amp]/g, '')
.startsWith(timeDigits);
}
});
index = closestTime ? timeOptions.indexOf(closestTime) : -1;
}
if (index !== -1) {
setFocusedIndex(index);
}
return index;
};
const inputWidth = isFluid ? undefined : is24Hour ? '6rem' : '9rem';
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(text_input_1.TextInput, { onChange: newHandleInputChange, isFluid: isFluid, label: label, placeholder: is24Hour ? 'hh:mm' : 'hh:mm aa', ref: inputRef, value: inputValue, size: size, style: Object.assign({ width: inputWidth }, style), className: (0, classnames_1.default)(className, 'ndl-time-picker'), isRequired: isRequired, isDisabled: isDisabled, isReadOnly: isReadOnly, errorText: errorText || error, rightElement: (0, jsx_runtime_1.jsx)(icons_1.ClockIconOutline, { className: "ndl-icon-svg" }), htmlAttributes: Object.assign({ onClick: () => {
if (isReadOnly)
return;
setIsPopoverOpen(true);
}, onBlur: (e) => {
var _a;
if ((_a = e.relatedTarget) === null || _a === void 0 ? void 0 : _a.classList.contains('ndl-time-picker-popover-item')) {
return;
}
if (inputValue.length < 4) {
setError('Invalid time');
return;
}
const time = needle_time_1.NeedleTime.fromString(inputValue);
if (!time) {
setError('Invalid time');
return;
}
if (!isValidTime(time.hour, time.minute)) {
setError('Invalid time');
}
else {
setError(undefined);
setInputValue(time.toString(is24Hour));
}
}, onKeyDown: handleKeyDown, maxLength: is24Hour ? 5 : 8 }, htmlAttributes) }), (0, jsx_runtime_1.jsx)(popover_1.Popover, { isOpen: !isDisabled && isPopoverOpen, onOpenChange: setIsPopoverOpen, anchorElement: inputRef.current, initialFocus: -1, children: (0, jsx_runtime_1.jsx)(popover_1.Popover.Content, { className: (0, classnames_1.default)(themeClassName, 'ndl-time-picker-popover', Object.assign(Object.assign({}, sizeClasses), { 'ndl-fluid': isFluid })), style: {
width: isFluid ? (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth : undefined,
}, children: (0, jsx_runtime_1.jsx)("ul", { ref: listRef, tabIndex: -1, children: timeOptions.map((time, i) => ((0, jsx_runtime_1.jsx)("li", { role: "option", "aria-selected": (selected === null || selected === void 0 ? void 0 : selected.toString(is24Hour)) === time ||
(editedInput === null || editedInput === void 0 ? void 0 : editedInput.toString(is24Hour)) === time, value: time, onClick: (e) => {
e.stopPropagation();
handleOnClick(time);
}, onMouseDown: (e) => e.stopPropagation(), tabIndex: -1, className: (0, classnames_1.default)(focusedIndex === i ? 'focused' : '', 'ndl-time-picker-popover-item'), onKeyDown: (e) => {
if (e.key === 'Enter') {
e.stopPropagation();
handleOnClick(time);
}
}, children: time }, i))) }) }) })] }));
});
//# sourceMappingURL=TimePicker.js.map