UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

154 lines 11.7 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; /* eslint-disable @typescript-eslint/naming-convention */ /** * * 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/>. */ import { format } from 'date-fns'; import { useCallback, useRef, useState } from 'react'; import ReactDatePicker from 'react-datepicker'; import { classNames } from '../_common/defaultImports'; import { useIsInsideDialog } from '../dialog'; import { Divider } from '../divider'; import { forwardRef } from '../helpers'; import { IconButton } from '../icon-button'; import { ArrowLeftIconOutline, ArrowRightIconOutline, CalendarDaysIconOutline, ChevronDownIconOutline, } from '../icons'; import { TextInput } from '../text-input'; import { NeedleTime, TimePicker } from '../time-picker'; import { Typography } from '../typography'; import { daysInMonth, getYearsPeriodString } from './utils'; // This wrapper is needed due to react-datepicker injecting html attributes // into the custom input component on root level, since we use htmlAttributes // we need to help spread them there instead of the root level const DatePickerTextInputWrapper = forwardRef(function DatePickerTextInputWrapper(_a, ref) { var { datetimeRef, textInputProps, isDisabled } = _a, reactDatePickerInjectedProps = __rest(_a, ["datetimeRef", "textInputProps", "isDisabled"]); return (_jsx(TextInput, Object.assign({ ref: ref, "aria-label": "Date picker input", rightElement: _jsx(IconButton, { ariaLabel: "Open date picker", isClean: true, onClick: (e) => { var _a, _b; e.preventDefault(); e.stopPropagation(); if ((_a = datetimeRef.current) === null || _a === void 0 ? void 0 : _a.isCalendarOpen()) { datetimeRef.current.setOpen(false); } else { (_b = datetimeRef.current) === null || _b === void 0 ? void 0 : _b.setOpen(true); } }, children: _jsx(CalendarDaysIconOutline, {}) }) }, textInputProps, { isDisabled: isDisabled, // react-datepicker injects html attributes into the custom input htmlAttributes: Object.assign(Object.assign({}, textInputProps === null || textInputProps === void 0 ? void 0 : textInputProps.htmlAttributes), reactDatePickerInjectedProps) }))); }); export const DatePicker = forwardRef(function DatePicker(props, ref) { var _a; const { reactDatePickerProps, textInputProps, timePickerProps, isDisabled, className, } = props; const classes = classNames(`ndl-datepicker`, className, {}); const datetimeRef = useRef(null); const [picker, setPicker] = useState('day'); const [preSelectedDate, setPreSelectedDate] = useState((_a = reactDatePickerProps.selected) !== null && _a !== void 0 ? _a : reactDatePickerProps.startDate); const isInsideDialog = useIsInsideDialog(); const headerAction = useCallback((action) => { setPicker(picker === action ? 'day' : action); }, [picker]); const CustomHeader = useCallback((props) => { const { decreaseMonth, increaseMonth, increaseYear, decreaseYear, nextMonthButtonDisabled, nextYearButtonDisabled, prevMonthButtonDisabled, prevYearButtonDisabled, } = props; const prevCallback = picker === 'year' ? decreaseYear : decreaseMonth; const nextCallback = picker === 'year' ? increaseYear : increaseMonth; const prevDisabled = picker === 'year' ? prevYearButtonDisabled : prevMonthButtonDisabled; const nextDisabled = picker === 'year' ? nextYearButtonDisabled : nextMonthButtonDisabled; return (_jsxs("div", { className: "ndl-datepicker-header", children: [_jsxs("div", { className: "ndl-datepicker-selects", children: [picker !== 'year' && (_jsx("button", { "aria-label": `${picker === 'month' ? 'Close' : 'Open'} month picker`, "aria-pressed": picker === 'month', onClick: () => headerAction('month'), children: _jsxs("div", { className: "n-flex n-items-center n-gap-token-3", children: [_jsx(Typography, { variant: "subheading-small", children: format(props.date, 'MMM') }), _jsx(ChevronDownIconOutline, { className: classNames('ndl-datepicker-chevron', { 'n-rotate-180': picker === 'month', }), "aria-label": "Chevron icon" })] }) })), picker !== 'month' && (_jsx("button", { "aria-label": `${picker === 'year' ? 'Close' : 'Open'} year picker`, onClick: () => headerAction('year'), children: _jsxs("div", { className: "n-flex n-items-center n-gap-token-3", children: [_jsx(Typography, { variant: "subheading-small", children: picker === 'year' ? getYearsPeriodString(props.date, reactDatePickerProps === null || reactDatePickerProps === void 0 ? void 0 : reactDatePickerProps.yearItemNumber) : format(props.date, 'yyyy') }), _jsx(ChevronDownIconOutline, { className: classNames('ndl-datepicker-chevron', { 'n-rotate-180': picker === 'year', }), "aria-label": "Chevron icon" })] }) }))] }), picker !== 'month' && (_jsxs("div", { className: "n-flex n-justify-center n-gap-token-6", children: [_jsx(IconButton, { ariaLabel: "Previous period", isClean: true, onClick: prevCallback, isDisabled: prevDisabled, className: "n-text-palette-neutral-text-default;", size: "small", children: _jsx(ArrowLeftIconOutline, {}) }), _jsx(IconButton, { ariaLabel: "Next period", isClean: true, onClick: nextCallback, isDisabled: nextDisabled, className: "n-text-palette-neutral-text-default;", size: "small", children: _jsx(ArrowRightIconOutline, {}) })] }))] })); }, [picker, headerAction, reactDatePickerProps === null || reactDatePickerProps === void 0 ? void 0 : reactDatePickerProps.yearItemNumber]); const handleTimeChange = (newTime) => { if (reactDatePickerProps.selectsRange || reactDatePickerProps.selectsMultiple) { // this is consistent with the behavior of react-datepicker return; } const newDate = new Date(preSelectedDate !== null && preSelectedDate !== void 0 ? preSelectedDate : ''); newDate.setHours(newTime.hour, newTime.minute, 0, 0); setPreSelectedDate(newDate); reactDatePickerProps === null || reactDatePickerProps === void 0 ? void 0 : reactDatePickerProps.onChange(newDate, undefined); }; /** * Intercept onChange so we can work with * Month and Year pickers */ const interceptedChange = useCallback((...args) => { if (picker !== 'day') { // setTimeout to prevent picker change before the handleMonthChange logic setTimeout(() => { setPicker('day'); }); } else if (picker === 'day') { if (args[0] instanceof Date) { setPreSelectedDate(args[0]); } else if (args[0] !== null) { setPreSelectedDate(args[0][0]); } reactDatePickerProps === null || reactDatePickerProps === void 0 ? void 0 : reactDatePickerProps.onChange(...args); } }, [picker, reactDatePickerProps]); /** * Intercept onCalendarClose so we can * switch to "day" picker if we close on "month" or "year" * view */ const interceptedOnCalendarClose = useCallback(() => { var _a; if (picker !== 'day') { setPicker('day'); } (_a = reactDatePickerProps === null || reactDatePickerProps === void 0 ? void 0 : reactDatePickerProps.onCalendarClose) === null || _a === void 0 ? void 0 : _a.call(reactDatePickerProps); }, [picker, reactDatePickerProps]); const handleMonthChange = useCallback((date) => { var _a, _b, _c, _d, _e; if (picker === 'month') { const selectedDay = (_a = preSelectedDate === null || preSelectedDate === void 0 ? void 0 : preSelectedDate.getDate()) !== null && _a !== void 0 ? _a : 1; const daysInNewMonth = daysInMonth(date.getMonth(), date.getFullYear()); date.setDate(Math.min(selectedDay, daysInNewMonth)); (_b = reactDatePickerProps === null || reactDatePickerProps === void 0 ? void 0 : reactDatePickerProps.onMonthChange) === null || _b === void 0 ? void 0 : _b.call(reactDatePickerProps, date); } else if (picker === 'year') { const selectedDay = (_c = preSelectedDate === null || preSelectedDate === void 0 ? void 0 : preSelectedDate.getDate()) !== null && _c !== void 0 ? _c : 1; const selectedMonth = (_d = preSelectedDate === null || preSelectedDate === void 0 ? void 0 : preSelectedDate.getMonth()) !== null && _d !== void 0 ? _d : 0; const daysInNewMonth = daysInMonth(selectedMonth, date.getFullYear()); date.setMonth(selectedMonth, Math.min(selectedDay, daysInNewMonth)); (_e = reactDatePickerProps === null || reactDatePickerProps === void 0 ? void 0 : reactDatePickerProps.onMonthChange) === null || _e === void 0 ? void 0 : _e.call(reactDatePickerProps, date); } setPreSelectedDate(date); }, [picker, preSelectedDate, reactDatePickerProps]); return (_jsx("div", { className: classes, children: _jsx(ReactDatePicker, Object.assign({ ref: datetimeRef, customInput: _jsx(DatePickerTextInputWrapper, { ref: ref, datetimeRef: datetimeRef, isDisabled: isDisabled, textInputProps: textInputProps }), customTimeInput: _jsxs("span", { children: [_jsx(Divider, { className: "n-my-4" }), _jsx(TimePicker, Object.assign({ isFluid: true, selected: new NeedleTime(preSelectedDate === null || preSelectedDate === void 0 ? void 0 : preSelectedDate.getHours(), preSelectedDate === null || preSelectedDate === void 0 ? void 0 : preSelectedDate.getMinutes()), onChange: handleTimeChange }, timePickerProps, { floatingStrategy: "absolute" }))] }), disabled: isDisabled, showPopperArrow: false, showMonthYearPicker: picker === 'month', showYearPicker: picker === 'year', shouldCloseOnSelect: picker === 'day' && !reactDatePickerProps.showTimeInput, dayClassName: () => 'ndl-datepicker-day', renderCustomHeader: CustomHeader }, reactDatePickerProps, { popperProps: Object.assign({ strategy: isInsideDialog ? 'fixed' : 'absolute' }, reactDatePickerProps.popperProps), onChange: interceptedChange, onMonthChange: handleMonthChange, onCalendarClose: interceptedOnCalendarClose })) })); }); //# sourceMappingURL=DatePicker.js.map