@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
452 lines (451 loc) • 13.8 kB
JavaScript
"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