import React, { useEffect, useRef, useMemo, useState } from 'react'; import { Cell, DatePicker, DatePickerProps, FormField, Layout, TimeInput, } from '@wix/design-system'; import { BaseInputProps, BaseInputRef } from '../BaseInput'; import { Field } from '../Field'; import { dataHooks } from '../dataHooks'; import { useCustomFieldsFormContext } from '../../components/CustomFieldsFormContext'; import { observer } from 'mobx-react-lite'; import { useWixPatternsContainer } from '@wix/bex-core/react'; import { parse } from 'date-fns'; import { shortDateFormats } from '../../utils/datesUtil'; export interface DateTimeProps extends BaseInputProps {} const _DateTime = ({ dataHook, state, schema, initialValue, inputRef, onChange, }: DateTimeProps) => { const { environment: { language }, } = useWixPatternsContainer(); const rawValue = state._extendedFieldsDataMap.get(schema.id) || initialValue; const date = useMemo(() => { if (!rawValue) { return undefined; } const d = new Date(rawValue); return isNaN(d.getTime()) ? undefined : d; }, [rawValue]); const [statusMessage, setStatusMessage] = useState(''); const ref = useRef({}); const dateRef = useRef({}); const timeRef = useRef({}); const [dateValue, setDateValue] = useState( date?.getDate(), ); const [timeValue, setTimeValue] = useState( date?.getTime(), ); const formState = useCustomFieldsFormContext(); useEffect(() => { ref.current.focus = () => { if (dateRef.current.invalid) { return dateRef.current.focus?.(); } return timeRef.current.focus?.(); }; inputRef?.(ref.current); }, []); return ( { dateRef.current.focus = () => // TODO: PR to WSR needed to expose as public API // @ts-expect-error internalRef?.state?.inputRef?.focus?.(); }} width="100%" value={date} locale={language as DatePickerProps['locale']} // onChange is triggered only when you have valid, non-empty value. and onValidate is triggered on every change to we use onValidate onChange={() => {}} onValidate={({ validationType, value, format }) => { if (validationType === 'valid') { const dateWithNewDate = date ? new Date(date) : new Date(value); const _value = parse( value, shortDateFormats[language as keyof typeof shortDateFormats], new Date(), ); dateWithNewDate.setFullYear(_value.getFullYear()); dateWithNewDate.setMonth(_value.getMonth()); dateWithNewDate.setDate(_value.getDate()); onChange(dateWithNewDate.toISOString()); setDateValue(new Date(value).getDate()); setStatusMessage(''); dateRef.current.invalid = false; } else if (validationType === 'formatError' && value === '') { setStatusMessage(''); dateRef.current.invalid = false; setDateValue(value); if (!timeValue) { onChange(null); } } else { setStatusMessage( formState._translate( 'cairo.customFields.fieldType.date.invalid.value', { dateFormat: format ?? '' }, ), ); dateRef.current.invalid = true; } ref.current.invalid = dateRef.current.invalid || timeRef.current.invalid; }} status={ref.current.invalid ? 'error' : undefined} statusMessage={statusMessage} dataHook={`${dataHooks.Input}-date`} /> { timeRef.current.focus = () => internalRef?.focus(); }} dataHook={`${dataHooks.Input}-time`} value={date ?? null} invalidMessage={formState._translate( 'cairo.customFields.fieldType.dateAndTime.invalid.value', )} onInvalid={() => { timeRef.current.invalid = true; ref.current.invalid = dateRef.current.invalid || timeRef.current.invalid; }} onChange={({ date: newValue }) => { timeRef.current.invalid = false; ref.current.invalid = dateRef.current.invalid || timeRef.current.invalid; if (newValue) { const dateWithNewTime = date ? new Date(date) : newValue; dateWithNewTime.setHours(newValue.getHours()); dateWithNewTime.setMinutes(newValue.getMinutes()); dateWithNewTime.setSeconds(newValue.getSeconds()); dateWithNewTime.setMilliseconds(newValue.getMilliseconds()); onChange(dateWithNewTime.toISOString()); } else if (!ref.current.invalid) { if (!newValue) { setTimeValue(''); if (!dateValue) { onChange(null); } } } }} /> ); }; export const DateTime = observer(_DateTime);