@harvest-profit/npk
Version:
NPK UI Design System
209 lines • 14 kB
JavaScript
;
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