{"version":3,"file":"datePicker.mjs","names":[],"sources":["../../../../package/common/datePicker/datePicker.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef } from 'react';\nimport cx from 'classnames';\nimport Input from '../input';\nimport { useComponentDefaults, useLabels, useLocale } from '../configProvider';\nimport { validLocale } from '../configProvider/validLocale';\nimport DatePickerMonth from './components/datePickerMonth';\nimport { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';\nimport ActionIcon from '../actionIcon';\nimport Modal from '../modal';\nimport Popover from '../popover';\nimport Dropdown from '../dropdown';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport MaskedInput from '../maskedInput';\nimport type { BeforeMaskedStateChangeStates, MaskState } from '../maskedInput';\nimport { asDay, parseDay } from '../utils/day';\nimport { getMonthNames, getDateString, getToday, parseDateString, correctDateMaskValue } from './dateFunctions';\nimport Button from '../button';\nimport Tooltip from '../tooltip';\nimport type { FormValidator } from 'services/formValidation';\nimport type { DatePickerLabels, DatePickerProps } from './datePicker.types';\n\nimport styles from './datePicker.module.pcss';\n\n/**\n * Why `value` is not a day, or `null` when it is one or is empty. Days were `Date`s before 0.39,\n * and stored as their JSON, a timestamp; either still gets through a loosely typed validator or\n * plain JavaScript, and would otherwise just show an empty field.\n */\nfunction notADay(value: unknown): string | null {\n  if (value === null || value === undefined || value === '' || asDay(value)) return null;\n  if (Object.prototype.toString.call(value) === '[object Date]') return 'a Date. Convert it with dateToDay()';\n  return `${JSON.stringify(value)}, which is not a day`;\n}\n\nconst DEFAULT_LABELS: Required<DatePickerLabels> = {\n  previousMonth: 'Previous month',\n  nextMonth: 'Next month',\n  clear: 'Clear',\n  month: 'Month',\n  year: 'Year',\n};\n\nexport default function DatePicker(props: DatePickerProps) {\n  const {\n    className = '',\n    selected,\n    onSelect = () => null,\n    formValidator = null,\n    noInput = false,\n    noHeader = false,\n    isModal = false,\n    isInline = false,\n    onChange = () => null,\n    minDate = null,\n    maxDate = null,\n    children = null,\n    showClearButton = false,\n    label = '',\n    isValid = true,\n    errorMessage = '',\n    isRequired = false,\n    startDate,\n    endDate,\n    locale: localeProp,\n    weekStartsOn,\n    labels,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('DatePicker', props);\n  const text = useLabels('DatePicker', labels, DEFAULT_LABELS);\n  const appLocale = useLocale();\n  const locale = validLocale(localeProp, 'DatePicker') ?? appLocale;\n  const getCurrValue = (): string | null => asDay(formValidator ? formValidator.value : selected);\n  const minDay = asDay(minDate);\n  const maxDay = asDay(maxDate);\n\n  const initialValue = getCurrValue();\n  const initialDay = parseDay(initialValue) ?? getToday();\n\n  const [currentMonth, setCurrentMonth] = useState(initialDay.month);\n  const [currentYear, setCurrentYear] = useState(initialDay.year);\n  const [isPickerOpen, setIsPickerOpen] = useState(false);\n  const [currentDateString, setCurrentDateString] = useState(() => getDateString(initialValue));\n  const [isFocused, setIsFocused] = useState(false);\n  const [isHovered, setIsHovered] = useState(false);\n  const [, forceUpdate] = useState(0);\n  const [prevSelected, setPrevSelected] = useState(selected);\n\n  const fvRef = useRef(formValidator);\n\n  // Sync navigation view when `selected` prop changes externally\n  if (prevSelected !== selected) {\n    setPrevSelected(selected);\n    const shown = parseDay(selected) ?? getToday();\n    setCurrentMonth(shown.month);\n    setCurrentYear(shown.year);\n    setCurrentDateString(getDateString(selected));\n  }\n\n  // Once per prop: a `new Date()` written inline is a new object on every render.\n  const warned = useRef(new Set<string>());\n  const validatorValue: unknown = formValidator?.value;\n  useEffect(() => {\n    const values = { selected, formValidator: validatorValue, minDate, maxDate, startDate, endDate };\n    for (const [name, value] of Object.entries(values)) {\n      const problem = notADay(value);\n      if (!problem || warned.current.has(name)) continue;\n      warned.current.add(name);\n      console.warn(\n        `DatePicker: \\`${name}\\` takes a day as a 'YYYY-MM-DD' string, and got ${problem}. ` +\n          `See \"Migration from Date values\" in docs/DatePicker.md.`,\n      );\n    }\n  }, [selected, validatorValue, minDate, maxDate, startDate, endDate]);\n\n  // Subscribe to formValidator updates (external system → setState in callback)\n  useEffect(() => {\n    fvRef.current = formValidator;\n    if (!formValidator) return;\n    formValidator.label = label;\n    formValidator.validate();\n    const listener = () => {\n      const shown = parseDay(formValidator.value);\n      if (shown) {\n        setCurrentMonth(shown.month);\n        setCurrentYear(shown.year);\n      }\n      setCurrentDateString(getDateString(formValidator.value));\n      forceUpdate((n: number) => n + 1);\n    };\n    formValidator.registerOnUpdateListener(listener);\n    return () => formValidator.removeOnUpdateListener(listener);\n  }, [formValidator, label]);\n\n  const hasError = (): boolean => {\n    return formValidator ? formValidator.shouldShowError : !isValid || !!errorMessage;\n  };\n\n  const computedIsRequired = (): boolean => {\n    return !!(\n      isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n    );\n  };\n\n  const handleChange = (day: string | null) => {\n    setCurrentDateString(getDateString(day));\n    // Typed as a union so each validator TypeScript infers for a day fits; all of them hold one.\n    if (formValidator) (formValidator as FormValidator<string | null>).set(day);\n    onChange(day);\n  };\n\n  const handleSelect = (day: string | null) => {\n    handleChange(day);\n    onSelect(day);\n    setIsPickerOpen(false);\n  };\n\n  const changeMonth = (delta: number) => {\n    const index = currentYear * 12 + currentMonth - 1 + delta;\n    const year = Math.floor(index / 12);\n    // A day is written with four digits, so the calendar stops at the years it can name.\n    if (year < 0 || year > 9999) return;\n    setCurrentYear(year);\n    setCurrentMonth((index % 12) + 1);\n  };\n\n  const tryParseDateString = (text: string): string | null => {\n    const day = parseDateString(text);\n    // `'YYYY-MM-DD'` sorts as it reads, so the bounds compare as plain strings.\n    if (!day || (minDay && day < minDay) || (maxDay && day > maxDay)) return null;\n    return day;\n  };\n\n  const beforeMaskedStateChange = ({ previousState, nextState }: BeforeMaskedStateChangeStates): MaskState => {\n    // A change that moved nothing needs no correcting, and re-running the day/month padding on\n    // an unchanged value would fight the caret the mask just placed.\n    if (nextState.value === previousState.value) return nextState;\n\n    const corrected = correctDateMaskValue(nextState.value, previousState.value, nextState.selection ?? null);\n\n    const parsed = tryParseDateString(corrected.value);\n    if (parsed) handleChange(parsed);\n\n    return corrected;\n  };\n\n  const getYearDropdownOptions = () => {\n    let length = 400;\n    let zeroOffset = getToday().year - 200;\n    const min = parseDay(minDay);\n    const max = parseDay(maxDay);\n    if (min) zeroOffset = min.year;\n    // Never negative: a `maxDate` before `minDate` made `Array(length)` throw during render.\n    if (max) length = Math.max(0, max.year - zeroOffset + 1);\n    return Array.from(Array(length).keys()).map((i) => ({ key: i + zeroOffset, label: i + zeroOffset }));\n  };\n\n  const currValue = getCurrValue();\n  const hasErr = hasError();\n\n  const header = noHeader ? null : (\n    <div className={cx(styles.datePickerHeader, classNames?.header)} style={slotStyles?.header}>\n      <ActionIcon icon={faChevronLeft} aria-label={text.previousMonth} onClick={() => changeMonth(-1)} />\n      <div className={styles.datePickerHeaderMonthAndYearContainer}>\n        <div className={styles.datePickerHeaderMonthAndYear}>\n          <Dropdown\n            plainStyle\n            scrollToSelected\n            aria-label={text.month}\n            onChange={(v) => setCurrentMonth(v as number)}\n            className={cx(styles.datePickerHeaderMonth, classNames?.monthDropdown)}\n            style={slotStyles?.monthDropdown}\n            value={currentMonth}\n            items={getMonthNames(locale).map((month, index) => ({ key: index + 1, label: month }))}\n          />\n          <Dropdown\n            plainStyle\n            scrollToSelected\n            useMatLabelStyle={false}\n            aria-label={text.year}\n            className={cx(styles.datePickerHeaderYear, classNames?.yearDropdown)}\n            style={slotStyles?.yearDropdown}\n            onChange={(v) => setCurrentYear(v as number)}\n            value={currentYear}\n            items={getYearDropdownOptions()}\n          />\n        </div>\n      </div>\n      <ActionIcon icon={faChevronRight} aria-label={text.nextMonth} onClick={() => changeMonth(1)} />\n    </div>\n  );\n\n  const pickerContent = (\n    <>\n      {header}\n      <DatePickerMonth\n        className={cx(styles.datePickerMonth, classNames?.calendar)}\n        style={slotStyles?.calendar}\n        month={currentMonth}\n        year={currentYear}\n        onSelect={handleSelect}\n        selectionStart={asDay(startDate) ?? currValue}\n        selectionEnd={asDay(endDate) ?? currValue}\n        minDate={minDay}\n        maxDate={maxDay}\n        onNavigateMonth={changeMonth}\n        locale={locale}\n        weekStartsOn={weekStartsOn}\n      />\n      {showClearButton && (\n        <Button onClick={() => handleSelect(null)} className={classNames?.clearButton} style={slotStyles?.clearButton}>\n          {text.clear}\n        </Button>\n      )}\n      {children}\n    </>\n  );\n\n  const input = noInput ? null : (\n    <Input\n      autoComplete='off'\n      className={classNames?.input}\n      style={slotStyles?.input}\n      readOnly={isModal}\n      classNames={isModal ? { input: styles.datePickerModalInput } : undefined}\n      label={`${label}${computedIsRequired() ? ' *' : ''}`}\n      value={currentDateString}\n      onChange={(v) => setCurrentDateString(v as string)}\n      onClick={() => setIsPickerOpen(true)}\n      customInput={<MaskedInput mask='99.99.9999' beforeMaskedStateChange={beforeMaskedStateChange} />}\n      onBlur={() => {\n        if (fvRef.current) fvRef.current.touched = true;\n        setIsFocused(false);\n        // Only an edit is written back. A value the field cannot show, a leftover Date or a\n        // timestamp, shows as empty, and writing that back would erase it on a mere tab-through.\n        if (currentDateString !== getDateString(getCurrValue())) handleChange(tryParseDateString(currentDateString));\n      }}\n      onFocus={() => setIsFocused(true)}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n      isValid={!hasErr}\n    />\n  );\n\n  const picker = (() => {\n    if (isInline) return pickerContent;\n    if (isModal) {\n      return (\n        <Modal hideHeader isOpen={isPickerOpen} onClose={() => setIsPickerOpen(false)}>\n          {pickerContent}\n        </Modal>\n      );\n    }\n    return (\n      <Popover\n        isOpen={isPickerOpen}\n        onOpenChange={(open: boolean) => setIsPickerOpen(open)}\n        content={pickerContent}\n        placement={PopoverPlacement.Bottom}\n        align={PopoverAlign.Start}\n        // A click to place the caret in the field must not take the panel away.\n        // The focus stays there on its own: Popover only follows a keyboard open.\n        closeOnTriggerClick={false}\n        constrainWidth={false}\n      >\n        {input}\n      </Popover>\n    );\n  })();\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.datePicker, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n    >\n      {(isModal || isInline) && input}\n      {picker}\n      <Tooltip\n        variant='error'\n        message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n        isVisible={hasErr && (isFocused || isHovered)}\n      />\n    </div>\n  );\n}\n"],"mappings":"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"}