UNPKG

@harvest-profit/npk

Version:
209 lines 14 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importStar(require("react")); const BaseInput_1 = __importDefault(require("../BaseInput")); const Group_1 = __importDefault(require("../Input/Group")); const Calendar_1 = __importDefault(require("../Calendar")); const Menu_1 = __importDefault(require("../Menu")); const Button_1 = __importDefault(require("../Button")); const regular_1 = require("@harvest-profit/npk/icons/regular"); const InputSegment_1 = __importDefault(require("./InputSegment")); const utils_1 = require("../Calendar/utils"); const helpers_1 = require("./helpers"); const GranularityInclude = ({ children, active }) => active ? children : null; // Shifts focus to the next input segment const useDateGroupFocus = () => { const onMouseDown = (event) => { const target = event.target; if (document.activeElement === target) return; if (!target.ariaHidden && target.dataset.component === 'input-segment') return; const rootNode = target?.dataset?.component === 'input-group' ? target : target?.closest('[data-component="input-group"]'); if (rootNode && rootNode.contains(document.activeElement)) { event.preventDefault(); return; } const focusNearest = (node) => { if (!node) return; if (node.ariaHidden) focusNearest(node.nextElementSibling); if (node.dataset.component === 'input-group') focusNearest(node.firstElementChild); node.focus(); }; focusNearest(target); }; return { onMouseDown }; }; // Simple hook to manage state and call a callback when the value changes const useOnChangeState = (initialValue, cb) => { const [state, setState] = (0, react_1.useState)(initialValue); return [state, (newValue) => { if (newValue === state) return; setState(newValue); if (cb && newValue) cb(newValue); }]; }; // Controls the individual segments of the date input const DateInputInternal = ({ onChange = (_value) => null, value, includeYear = true, monthAsName = false, granularity = 'day', excludeGroup = false, formatter, ...props }) => { const [didPasteValue, setDidPasteValue] = (0, react_1.useState)(false); let updateValue = (0, utils_1.today)(); // When updating a new date, we build off the current date and then set individual values let segmentDateValue = value; // We then use a date segment and a time segment for the values of the segments let segmentTimeValue = value; // If "value" is not a complete date, we do not fill out the input. Time will be null if the time is 00:00:00.000 if (value instanceof Date && !isNaN(value.getTime())) { updateValue = value; if (value.getHours() === 0 && value.getMinutes() === 0 && value.getMilliseconds() === 0) { segmentTimeValue = null; } } else { segmentDateValue = null; segmentTimeValue = null; } const monthIndexToMonthValue = monthAsName ? utils_1.monthIndexToAbbrev : utils_1.monthIndexToMonthNumber; const monthValueToMonthIndex = monthAsName ? utils_1.monthAbbrevToMonthIndex : utils_1.monthNumberToMonthIndex; // Specify the rules of getting and setting the values of the segments const [monthValue, setMonthValue] = useOnChangeState(monthIndexToMonthValue(parseInt((0, utils_1.get)(segmentDateValue, 'monthIndex'))), (month) => onChange((0, utils_1.change)(updateValue, monthValueToMonthIndex(month), 'monthIndex'))); const [dayValue, setDayValue] = useOnChangeState((0, utils_1.get)(segmentDateValue, 'day'), (day => onChange((0, utils_1.change)(updateValue, day, 'day')))); const [yearValue, setYearValue] = useOnChangeState((0, utils_1.get)(segmentDateValue, 'year'), (year => onChange((0, utils_1.change)(updateValue, year, 'year')))); const [minuteValue, setMinuteValue] = useOnChangeState((0, utils_1.get)(segmentTimeValue, 'minute'), (minutes => onChange((0, utils_1.change)(updateValue, minutes, 'minute')))); const [todValue, setTODValue] = useOnChangeState((0, utils_1.get)(segmentTimeValue, 'TOD'), (tod) => onChange((0, utils_1.change)(updateValue, tod, 'TOD'))); const [hourValue, setHourValue] = useOnChangeState((0, utils_1.get)(segmentTimeValue, 'hour'), (hour) => { const intHours = parseInt(hour); if (todValue === 'AM' && intHours === 12) { // hours are tricky because of the 12-hour format. Native date objects are 24-hour format onChange((0, utils_1.change)(updateValue, 0, 'hour')); } else if (todValue === 'PM' && intHours < 12) { onChange((0, utils_1.change)(updateValue, intHours + 12, 'hour')); } else { onChange((0, utils_1.change)(updateValue, hour, 'hour')); } }); const ref = (0, react_1.useRef)(null); const [isInputSegmentInFocus, setInputSegmentFocused] = (0, react_1.useState)(false); const [isFocused, setIsFocused] = (0, react_1.useState)(false); (0, react_1.useEffect)(() => { if (!isFocused || didPasteValue) { // if the input is out of focus, we will accept updates from outside changes setMonthValue(monthIndexToMonthValue(segmentDateValue?.getMonth())); setDayValue((0, utils_1.get)(segmentDateValue, 'day')); setYearValue((0, utils_1.get)(segmentDateValue, 'year')); setHourValue((0, utils_1.get)(segmentTimeValue, 'hour')); setTODValue((0, utils_1.get)(segmentTimeValue, 'TOD')); setMinuteValue((0, utils_1.get)(segmentDateValue, 'minute')); if (didPasteValue) setDidPasteValue(false); } }, [`${value}`, didPasteValue]); (0, react_1.useEffect)(() => { setTimeout(() => { if (ref.current) { if (!isFocused && ref.current.contains(document.activeElement)) { setIsFocused(true); if (props.onFocus) props.onFocus({ target: ref.current }); } else if (isFocused && !ref.current.contains(document.activeElement)) { setIsFocused(false); if (props.onBlur) props.onBlur({ target: ref.current }); } } }, 10); }, [isInputSegmentInFocus]); const onChangeFullValue = (date) => { setDidPasteValue(true); onChange(date); }; const dateParts = []; if (['day', 'month', 'minute'].includes(granularity)) { dateParts.push((0, jsx_runtime_1.jsx)(InputSegment_1.default, { "aria-label": "month", segment: monthAsName ? 'monthName' : 'month', setIsFocused: setInputSegmentFocused, value: monthValue, onChange: setMonthValue, dateValue: updateValue, onChangeFullValue: onChangeFullValue })); } if (['day', 'minute'].includes(granularity)) { const dayPart = ((0, jsx_runtime_1.jsx)(InputSegment_1.default, { "aria-label": "day", segment: "day", setIsFocused: setInputSegmentFocused, value: dayValue, onChange: setDayValue, dateValue: updateValue, onChangeFullValue: onChangeFullValue })); ((0, utils_1.dayIsInFrontForCurrentLocale)() && !monthAsName) ? dateParts.unshift(dayPart) : dateParts.push(dayPart); } if (includeYear && ['day', 'month', 'year', 'minute'].includes(granularity)) { dateParts.push((0, jsx_runtime_1.jsx)(InputSegment_1.default, { "aria-label": "year", segment: "year", setIsFocused: setInputSegmentFocused, value: yearValue, onChange: setYearValue, dateValue: updateValue, onChangeFullValue: onChangeFullValue })); } const contents = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [dateParts.map((part, index) => { return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [index !== 0 && (0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", "data-component": "input-segment", children: monthAsName ? ' ' : '/' }), part] }, index)); }), (0, jsx_runtime_1.jsxs)(GranularityInclude, { active: ['minute'].includes(granularity), children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", "data-component": "input-segment", children: ", " }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", "data-component": "input-segment" })] }), (0, jsx_runtime_1.jsxs)(GranularityInclude, { active: ['minute', 'time'].includes(granularity), children: [(0, jsx_runtime_1.jsx)(InputSegment_1.default, { "aria-label": "hour", segment: "hour", setIsFocused: setInputSegmentFocused, value: hourValue, onChange: setHourValue, dateValue: updateValue, onChangeFullValue: onChangeFullValue }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", "data-component": "input-segment", children: ":" }), (0, jsx_runtime_1.jsx)(InputSegment_1.default, { "aria-label": "minute", segment: "minute", setIsFocused: setInputSegmentFocused, value: minuteValue, onChange: setMinuteValue, dateValue: updateValue, onChangeFullValue: onChangeFullValue }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": "true", "data-component": "input-segment" }), (0, jsx_runtime_1.jsx)(InputSegment_1.default, { "aria-label": "time of day", segment: "TOD", setIsFocused: setInputSegmentFocused, value: todValue, onChange: setTODValue, dateValue: updateValue, onChangeFullValue: onChangeFullValue })] })] })); const dateGroupProps = useDateGroupFocus(); if (excludeGroup) { return ((0, jsx_runtime_1.jsxs)(BaseInput_1.default, { containsSegments: true, contentsRef: ref, ...dateGroupProps, ...props, children: [contents, (0, jsx_runtime_1.jsx)("input", { type: "hidden", value: `${formatter.to(updateValue, 'ISO')}`, name: props.name })] })); } return ((0, jsx_runtime_1.jsxs)(Group_1.default, { containsSegments: true, contentsRef: ref, ...dateGroupProps, ...props, children: [contents, (0, jsx_runtime_1.jsx)("input", { type: "hidden", value: `${formatter.to(updateValue, 'ISO')}`, name: props.name })] })); }; const DateInput = ({ visibleMonths = 1, autoDismiss = true, presets = false, picker = false, isoType = null, output = null, includeYear = true, monthAsName = false, onChange: onExternalChange = (_value) => null, value: externalValue, granularity = 'day', excludeGroup, ...props }) => { let includeTime = (0, helpers_1.isoDateIncludesTime)(externalValue); if (isoType === 'DateTime') includeTime = true; if (isoType === 'Date') includeTime = false; const formatter = (0, helpers_1.useValueFormatter)(output, includeTime); const onValueChange = (value) => onExternalChange(formatter.to(value)); // SYNC formatted external value with value const [value, setValue] = (0, react_1.useState)(formatter.from(externalValue)); (0, react_1.useEffect)(() => { // Ensure internal value matches external value if (value !== formatter.from(externalValue)) setValue(formatter.from(externalValue)); }, [formatter.from(externalValue)?.toString()]); // END SYNC (0, react_1.useEffect)(() => { // If internal value is different than the external value based on granularity, update // the external value. The granularity ensures that if this is a date picker, changes to // minutes do not trigger a change. if (!(0, utils_1.isEqual)(value, formatter.from(externalValue), granularity)) onValueChange(value); }, [value?.toString()]); const extraProps = {}; if (picker) { extraProps.trailingVisual = ((0, jsx_runtime_1.jsxs)(Menu_1.default, { arrow: true, placement: "bottom", autoDismiss: false, children: [(0, jsx_runtime_1.jsx)(Button_1.default, { invisible: true, icon: regular_1.CalendarIcon, "aria-label": "Pick a date", tabIndex: -1 }), (0, jsx_runtime_1.jsx)(Menu_1.default.Overlay, { "aria-label": "Pick a date", children: (0, jsx_runtime_1.jsx)(Calendar_1.default, { visibleMonths: visibleMonths, presets: presets, value: value, onChange: setValue, output: "Date", autoDismiss: autoDismiss }) })] })); } return ((0, jsx_runtime_1.jsx)(DateInputInternal, { onChange: setValue, value: value, granularity: granularity, excludeGroup: excludeGroup, includeYear: includeYear, monthAsName: monthAsName, formatter: formatter, ...props, ...extraProps })); }; exports.default = DateInput; //# sourceMappingURL=DateInput.js.map