UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

452 lines (451 loc) 13.8 kB
"use client"; import _extends from "@babel/runtime-corejs3/helpers/esm/extends"; import _pushInstanceProperty from "core-js-pure/stable/instance/push.js"; import React, { useCallback, useContext, useMemo, useRef } from 'react'; import * as z from 'zod'; import { DatePicker } from "../../../../components/index.js"; import { useFieldProps } from "../../hooks/index.js"; import { pickSpacingProps } from "../../../../components/flex/utils.js"; import classnames from 'classnames'; import FieldBlock from "../../FieldBlock/index.js"; import SharedContext from "../../../../shared/Context.js"; import { parseISO, isValid, isBefore, isAfter, startOfDay } from 'date-fns'; import useTranslation from "../../hooks/useTranslation.js"; import { convertStringToDate } from "../../../../components/date-picker/DatePickerCalc.js"; import { FormError } from "../../utils/index.js"; import useInvalidDates from "./hooks/useInvalidDates.js"; import { formatDate } from "../../../../components/date-format/DateFormatUtils.js"; function DateComponent(props) { var _props$value; const { errorRequired, label: defaultLabel } = useTranslation().Date; const { locale } = useContext(SharedContext); const { invalidDatesRef, setInvalidDates } = useInvalidDates(); const lastEditedRangeDateRef = useRef('start'); const errorMessages = useMemo(() => { return { 'Field.errorRequired': errorRequired, 'Field.errorPattern': errorRequired, ...props.errorMessages }; }, [props.errorMessages, errorRequired]); const schema = useMemo(() => { if (props.schema) { return props.schema; } return p => { let s = z.string(); if (p !== null && p !== void 0 && p.pattern) { try { s = s.regex(new RegExp(p.pattern, 'u')); } catch (_e) {} } return s; }; }, [props.schema, props.pattern]); const validateRequired = useCallback((value, { required, error }) => { if (!required) { return undefined; } if (props.range) { if (value) { const [startDate, endDate] = parseRangeValue(value); if (startDate && endDate) { return undefined; } } return new FormError('Date.errorRequiredRange'); } return !value || !isValid(parseISO(value)) ? error : undefined; }, [props.range]); const dateValidator = useCallback(value => { const invalidDateErrors = validateDate(invalidDatesRef.current); const rangeOrderErrors = validateRangeOrder({ value, isRange: props.range, lastEditedDate: lastEditedRangeDateRef.current, locale }); if (!props.minDate && !props.maxDate) { return [...invalidDateErrors, ...rangeOrderErrors]; } const dateLimitErrors = validateDateLimit({ value, locale, minDate: props.minDate, maxDate: props.maxDate, isRange: props.range }); return [...invalidDateErrors, ...dateLimitErrors, ...(dateLimitErrors.length > 0 ? [] : rangeOrderErrors)]; }, [props.maxDate, props.minDate, props.range, invalidDatesRef, locale]); const { onBlurValidator: propOnBlurValidator, onChangeValidator, ...restProps } = props; const onBlurValidator = useMemo(() => { if (propOnBlurValidator === false) { return undefined; } if (propOnBlurValidator) { return propOnBlurValidator; } return dateValidator; }, [propOnBlurValidator, dateValidator]); const fromInput = useCallback(({ date, start_date, end_date, invalidDate, invalidStartDate, invalidEndDate }) => { setInvalidDates({ invalidDate, invalidStartDate, invalidEndDate }); return props.range ? `${start_date}|${end_date}` : date; }, [props.range, setInvalidDates]); const preparedProps = { ...restProps, errorMessages, schema, fromInput, validateRequired, onBlurValidator, onChangeValidator, exportValidators: { dateValidator }, invalidDatesRef }; const { id, path, itemPath, className, label, value: internalValue, hasError, disabled, htmlAttributes, handleError, handleFocus, handleBlur, handleChange, setChanged, setDisplayValue, range, showCancelButton = true, showResetButton = true, showInput = true, onReset, minDate, maxDate, width, ...rest } = useFieldProps(preparedProps); const datePickerProps = pickDatePickerProps(rest); const initialValueRef = useRef((_props$value = props.value) !== null && _props$value !== void 0 ? _props$value : props.defaultValue); const handleReset = useCallback(event => { const initialValue = initialValueRef.current; if (initialValue) { if (range) { const [startDate, endDate] = parseRangeValue(initialValue); handleChange({ start_date: startDate !== null && startDate !== void 0 ? startDate : undefined, end_date: endDate !== null && endDate !== void 0 ? endDate : undefined }); } else { handleChange({ date: initialValue }); } onReset === null || onReset === void 0 || onReset(event); return; } const reset = { date: undefined, start_date: undefined, end_date: undefined, is_valid: false }; handleChange(reset); setDisplayValue(undefined); onReset === null || onReset === void 0 || onReset({ ...event, ...reset }); }, [handleChange, onReset, setDisplayValue, range]); const onFocus = useCallback(() => { handleFocus(); handleError(); }, [handleFocus, handleError]); const onType = useCallback(event => { const { date, start_date, end_date, ...rest } = event; if (props.range) { const [prevStart, prevEnd] = parseRangeValue(internalValue); if (start_date !== prevStart) { lastEditedRangeDateRef.current = 'start'; } else if (end_date !== prevEnd) { lastEditedRangeDateRef.current = 'end'; } const parsedStartDate = parseISO(start_date); const parsedEndDate = parseISO(end_date); if (isValid(parsedStartDate) || isValid(parsedEndDate)) { handleChange({ ...(isValid(parsedStartDate) && { start_date }), ...(isValid(parsedEndDate) && { end_date }), ...rest }); } else { handleChange(rest); } } else if (isValid(parseISO(date))) { handleChange({ date, ...rest }); } else { handleChange(rest); } setChanged(true); }, [handleChange, props.range, setChanged, internalValue]); const { value, startDate, endDate } = useMemo(() => { if (!range || !internalValue) { return { value: internalValue !== null && internalValue !== void 0 ? internalValue : null, startDate: undefined, endDate: undefined }; } const [startDate, endDate] = parseRangeValue(internalValue); return { value: undefined, startDate, endDate }; }, [range, internalValue]); useMemo(() => { if ((path || itemPath) && value) { setDisplayValue(formatDate(value, { locale })); } }, [itemPath, locale, path, setDisplayValue, value]); const fieldBlockProps = { forId: id, label: label !== null && label !== void 0 ? label : defaultLabel, className: classnames('dnb-forms-field-string', className), width, ...pickSpacingProps(props) }; return React.createElement(FieldBlock, fieldBlockProps, React.createElement(DatePicker, _extends({ id: id, date: value, disabled: disabled, showInput: showInput, alignPicker: showInput && (width === 'large' || width === 'stretch') ? 'right' : undefined, showCancelButton: showCancelButton, showResetButton: showResetButton, stretch: width !== undefined, startDate: startDate, endDate: endDate, minDate: minDate, maxDate: maxDate, status: hasError ? 'error' : undefined, range: range, onReset: handleReset, onType: onType, onChange: handleChange, onFocus: onFocus, onBlur: handleBlur }, datePickerProps, htmlAttributes))); } export function parseRangeValue(value) { return String(value).split('|').map(value => /(undefined|null)/.test(value) ? null : value); } function validateDateLimit({ value, isRange, locale, ...dates }) { if (!dates.minDate && !dates.maxDate || !value) { return []; } const [startDateParsed, endDateParsed] = parseRangeValue(value); const convertedMinDate = convertStringToDate(dates.minDate); const convertedMaxDate = convertStringToDate(dates.maxDate); const convertedStartDate = convertStringToDate(startDateParsed); const convertedEndDate = convertStringToDate(endDateParsed); const minDate = convertedMinDate ? startOfDay(convertedMinDate) : undefined; const maxDate = convertedMaxDate ? startOfDay(convertedMaxDate) : undefined; const startDate = convertedStartDate ? startOfDay(convertedStartDate) : undefined; const endDate = convertedEndDate ? startOfDay(convertedEndDate) : undefined; const isoDates = { minDate: dates.minDate instanceof Date ? dates.minDate.toISOString() : dates.minDate, maxDate: dates.maxDate instanceof Date ? dates.maxDate.toISOString() : dates.maxDate }; const options = { locale, options: { dateStyle: 'long' } }; const messages = []; if (!isRange) { if (isBefore(startDate, minDate)) { _pushInstanceProperty(messages).call(messages, new FormError('Date.errorMinDate', { messageValues: { date: formatDate(isoDates.minDate, options) } })); } if (isAfter(startDate, maxDate)) { _pushInstanceProperty(messages).call(messages, new FormError('Date.errorMaxDate', { messageValues: { date: formatDate(isoDates.maxDate, options) } })); } return messages; } if (isBefore(startDate, minDate)) { _pushInstanceProperty(messages).call(messages, new FormError('Date.errorStartDateMinDate', { messageValues: { date: formatDate(isoDates.minDate, options) } })); } if (isAfter(startDate, maxDate)) { _pushInstanceProperty(messages).call(messages, new FormError('Date.errorStartDateMaxDate', { messageValues: { date: formatDate(isoDates.maxDate, options) } })); } if (isBefore(endDate, minDate)) { _pushInstanceProperty(messages).call(messages, new FormError('Date.errorEndDateMinDate', { messageValues: { date: formatDate(isoDates.minDate, options) } })); } if (isAfter(endDate, maxDate)) { _pushInstanceProperty(messages).call(messages, new FormError('Date.errorEndDateMaxDate', { messageValues: { date: formatDate(isoDates.maxDate, options) } })); } return messages; } function validateRangeOrder({ value, isRange, lastEditedDate, locale }) { if (!isRange || !value) { return []; } const [startDateParsed, endDateParsed] = parseRangeValue(value); const convertedStartDate = convertStringToDate(startDateParsed); const convertedEndDate = convertStringToDate(endDateParsed); const startDate = convertedStartDate ? startOfDay(convertedStartDate) : undefined; const endDate = convertedEndDate ? startOfDay(convertedEndDate) : undefined; if (startDate && endDate && isAfter(startDate, endDate)) { const options = { locale, options: { dateStyle: 'long' } }; if (lastEditedDate === 'end') { return [new FormError('Date.errorEndDateMinDate', { messageValues: { date: formatDate(startDateParsed, options) } })]; } return [new FormError('Date.errorStartDateMaxDate', { messageValues: { date: formatDate(endDateParsed, options) } })]; } return []; } function validateDate({ invalidDate, invalidStartDate, invalidEndDate }) { if (invalidDate && !isEmptyOrPlaceholder(invalidDate)) { return [new FormError('Date.errorInvalidDate', { messageValues: { date: invalidDate } })]; } const errors = []; if (invalidStartDate && !isEmptyOrPlaceholder(invalidStartDate)) { _pushInstanceProperty(errors).call(errors, new FormError('Date.errorInvalidStartDate', { messageValues: { date: invalidStartDate } })); } if (invalidEndDate && !isEmptyOrPlaceholder(invalidEndDate)) { _pushInstanceProperty(errors).call(errors, new FormError('Date.errorInvalidEndDate', { messageValues: { date: invalidEndDate } })); } return errors; } function isEmptyOrPlaceholder(date) { if (!date) { return true; } return !/\d/.test(date); } const datePickerPropKeys = ['month', 'startMonth', 'endMonth', 'minDate', 'maxDate', 'correctInvalidDate', 'maskOrder', 'maskPlaceholder', 'dateFormat', 'returnFormat', 'hideNavigation', 'hideDays', 'onlyMonth', 'hideLastWeek', 'disableAutofocus', 'showSubmitButton', 'submitButtonText', 'cancelButtonText', 'resetButtonText', 'firstDay', 'link', 'size', 'sync', 'addonElement', 'shortcuts', 'opened', 'direction', 'alignPicker', 'onDaysRender', 'showInput', 'onDaysRender', 'onType', 'onShow', 'onHide', 'onSubmit', 'onCancel', 'onReset', 'skipPortal', 'yearNavigation', 'tooltip']; function pickDatePickerProps(props) { const datePickerProps = Object.keys(props).reduce((datePickerProps, key) => { if (datePickerPropKeys.includes(key)) { datePickerProps[key] = props[key]; } return datePickerProps; }, {}); return datePickerProps; } DateComponent._supportsSpacingProps = true; export default DateComponent; //# sourceMappingURL=Date.js.map