import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { c as maskitoDateRangeOptionsGenerator, a as maskitoWithPlaceholder, u as useMaskito, b as maskitoParseDate } from './index.esm-C2ZhC_8d.js';
import { T as TextField } from './TextField-CRDTnuUN.js';
import { forwardRef, useState, useRef, useEffect, useMemo, useImperativeHandle, useCallback } from 'react';
import { u as useMergeRefs, P as Popover } from './AiMark-B1-M3ZgP.js';
import { I as Icon } from './Icon-BMMbp40n.js';
import { D as DateModeToPlaceholderMap, a as DateModeToFormatMap, S as SvgEvent, j as jsDateToUtcDateTime, c as convertStringToDate, T as TimezoneMessage, i as isoToUtcDate, v as validateDate } from './TimezoneMessage-BrKB_psP.js';
import { m as makeZeroShortcutPreprocessor } from './makeZeroShortcutPreprocessor-CxsiGTRW.js';
import { u as usePrevious } from './usePrevious-Bvq-5auG.js';
import { D as DateTime } from './luxon-wpz4A-OQ.js';
import { a as Calendar } from './Calendar-CaCMa_jq.js';
import { u as useOptionallyControlledState } from './useOptionallyControlledState-DbDuos5L.js';
import { m as mergeRefs } from './useMergeRefs-Dfmtq9cI.js';
import { c as childrenToString } from './childrenToString-Bz9MqbHb.js';
import { useTrackingId } from './useTrackingId.js';
import { u as useFocusWithin } from './useFocusWithin-hi77jsrB.js';

const datePlaceholderMask = ({
  mode,
  dateSeparator = "/",
  rangeSeparator = " - ",
  placeholder
}) => {
  const dateRangeOptions = maskitoDateRangeOptionsGenerator({
    mode,
    dateSeparator,
    rangeSeparator
  });
  const { plugins, removePlaceholder, ...placeholderOptions } = maskitoWithPlaceholder(placeholder);
  const datePlaceholderMask2 = {
    ...dateRangeOptions,
    plugins: plugins.concat(dateRangeOptions.plugins || []),
    preprocessors: [
      ...placeholderOptions.preprocessors,
      ...dateRangeOptions.preprocessors,
      makeZeroShortcutPreprocessor(placeholder, dateSeparator)
    ],
    postprocessors: [
      // NOTE this is super fragile. If Maskito maintainers change the order of the post-processors, this will break.
      // The last postprocessor is the date swap postprocessor, which we don't want to run.
      // A unit test is added to ensure this doesn't break on a dependency update.
      ...dateRangeOptions.postprocessors.slice(0, -1),
      ...placeholderOptions.postprocessors
    ]
  };
  return { options: datePlaceholderMask2, removePlaceholder };
};

const RANGE_SEPARATOR = " - ";
const MaskedDateRangeInput = forwardRef(
  ({
    onChange,
    mode = "mm/dd/yyyy",
    startDate,
    endDate,
    disableHint = false,
    ...props
  }, ref) => {
    const halfPlaceholder = DateModeToPlaceholderMap[mode];
    const fullPlaceholder = `${halfPlaceholder}${RANGE_SEPARATOR}${halfPlaceholder}`;
    const [inputValue, setInputValue] = useState(fullPlaceholder);
    const { options, removePlaceholder } = datePlaceholderMask({
      mode,
      placeholder: fullPlaceholder,
      dateSeparator: "/",
      rangeSeparator: RANGE_SEPARATOR
    });
    const maskedInputRef = useMaskito({ options });
    const inputRef = useRef(null);
    const combinedRef = useMergeRefs([maskedInputRef, inputRef, ref]);
    const previousStartDate = usePrevious(startDate);
    const previousEndDate = usePrevious(endDate);
    const previousMode = usePrevious(mode);
    useEffect(() => {
      if (mode !== previousMode) {
        setInputValue(
          (previousInputValue) => swapMode(previousInputValue, previousMode ?? mode, mode)
        );
      }
    }, [mode, fullPlaceholder, previousMode]);
    useEffect(() => {
      if (startDate === void 0 || endDate === void 0) return;
      if (startDate === previousStartDate && endDate === previousEndDate)
        return;
      if (
        // plus one just represents a date that is guaranteed to be different.
        startDate?.equals(previousStartDate ?? startDate?.plus({ days: 1 })) && (endDate?.equals(previousEndDate ?? endDate?.plus({ days: 1 })) || endDate === previousEndDate)
      )
        return;
      const startDateString = startDate?.toFormat(DateModeToFormatMap[mode]) ?? halfPlaceholder;
      const endDateString = endDate?.toFormat(DateModeToFormatMap[mode]) ?? halfPlaceholder;
      const newInputValue = `${startDateString}${RANGE_SEPARATOR}${endDateString}`;
      setInputValue(newInputValue);
    }, [
      startDate,
      endDate,
      mode,
      halfPlaceholder,
      previousStartDate,
      previousEndDate
    ]);
    const currentParsedData = useMemo(() => {
      return parseRangeInputValue(inputValue, mode, removePlaceholder);
    }, [inputValue, mode, removePlaceholder]);
    const handleChange = (event) => {
      setInputValue(event.target.value);
      const {
        startDate: newlyParsedStartDate,
        endDate: newlyParsedEndDate,
        isInputValid,
        isInputEmpty,
        isHalfEmpty
      } = parseRangeInputValue(event.target.value, mode, removePlaceholder);
      const isValueDifferent = isInputValid !== currentParsedData.isInputValid || // The input has changed validity
      isInputEmpty !== currentParsedData.isInputEmpty || // The input has changed emptiness
      newlyParsedStartDate === null !== (currentParsedData.startDate === null) || // The start date has changed from empty to non-empty or vice versa
      newlyParsedEndDate === null !== (currentParsedData.endDate === null) || // The end date has changed from empty to non-empty or vice versa
      isHalfEmpty !== currentParsedData.isHalfEmpty || // The half has changed from empty to non-empty or vice versa
      newlyParsedStartDate !== null && currentParsedData.startDate !== null && !newlyParsedStartDate.equals(currentParsedData.startDate) || // The start date has changed
      newlyParsedEndDate !== null && currentParsedData.endDate !== null && !newlyParsedEndDate.equals(currentParsedData.endDate);
      if (!isValueDifferent) return;
      onChange?.({
        startDate: isInputEmpty ? null : newlyParsedStartDate ?? startDate ?? null,
        endDate: isInputEmpty || isHalfEmpty ? null : newlyParsedEndDate ?? endDate ?? null,
        isInputValid,
        isInputEmpty
      });
    };
    useImperativeHandle(ref, () => {
      const input = inputRef.current;
      if (!input) return null;
      return Object.assign(input, {
        setDateRange: (startDate2, endDate2) => {
          const startDateString = startDate2?.toFormat(
            DateModeToFormatMap[mode]
          );
          const endDateString = endDate2?.toFormat(DateModeToFormatMap[mode]);
          const newInputValue = `${startDateString ?? halfPlaceholder}${RANGE_SEPARATOR}${endDateString ?? halfPlaceholder}`;
          setInputValue(newInputValue);
        }
      });
    }, [mode, halfPlaceholder]);
    return /* @__PURE__ */ jsx(
      TextField,
      {
        ref: combinedRef,
        "data-start-date": startDate?.toISODate() ?? "",
        "data-end-date": endDate?.toISODate() ?? "",
        "data-input-valid": currentParsedData.isInputValid,
        "data-input-empty": currentParsedData.isInputEmpty,
        ...props,
        showCounter: false,
        value: inputValue,
        onChange: handleChange,
        prefix: /* @__PURE__ */ jsx(Icon, { svg: SvgEvent }),
        hint: disableHint ? void 0 : `Format: ${mode}`
      }
    );
  }
);
MaskedDateRangeInput.displayName = "MaskedDateRangeInput";
function parseRangeInputValue(value, mode, removePlaceholder) {
  const valueMinusPlaceholder = removePlaceholder(value);
  const [startDate, endDate] = valueMinusPlaceholder.split(RANGE_SEPARATOR);
  const startJsDate = maskitoParseDate(startDate, { mode });
  const endJsDate = endDate ? maskitoParseDate(endDate, { mode }) : null;
  const startLuxonDate = startJsDate ? jsDateToUtcDateTime(startJsDate) : null;
  const endLuxonDate = endJsDate ? jsDateToUtcDateTime(endJsDate) : null;
  return {
    startDate: startLuxonDate,
    endDate: endLuxonDate,
    isInputValid: !!(startLuxonDate && endLuxonDate),
    // input valid if both dates are filled
    isInputEmpty: valueMinusPlaceholder === "",
    // input empty if nothing is typed
    isHalfEmpty: endDate === void 0
  };
}
function swapMode(inputString, previousMode, mode) {
  const halves = inputString.split(RANGE_SEPARATOR);
  const segments = halves.map((half) => half.split("/"));
  let startDay, startMonth, startYear, endDay, endMonth, endYear;
  if (previousMode === "mm/dd/yyyy") {
    startDay = segments[0][1];
    startMonth = segments[0][0];
    startYear = segments[0][2];
    endDay = segments[1][1];
    endMonth = segments[1][0];
    endYear = segments[1][2];
  }
  if (previousMode === "dd/mm/yyyy") {
    startDay = segments[0][0];
    startMonth = segments[0][1];
    startYear = segments[0][2];
    endDay = segments[1][0];
    endMonth = segments[1][1];
    endYear = segments[1][2];
  }
  if (previousMode === "yyyy/mm/dd") {
    startDay = segments[0][2];
    startMonth = segments[0][1];
    startYear = segments[0][0];
    endDay = segments[1][2];
    endMonth = segments[1][1];
    endYear = segments[1][0];
  }
  if (mode === "mm/dd/yyyy") {
    return `${startMonth}/${startDay}/${startYear}${RANGE_SEPARATOR}${endMonth}/${endDay}/${endYear}`;
  }
  if (mode === "dd/mm/yyyy") {
    return `${startDay}/${startMonth}/${startYear}${RANGE_SEPARATOR}${endDay}/${endMonth}/${endYear}`;
  }
  if (mode === "yyyy/mm/dd") {
    return `${startYear}/${startMonth}/${startDay}${RANGE_SEPARATOR}${endYear}/${endMonth}/${endDay}`;
  }
  return inputString;
}

const useDateFieldRangeConversion = (props) => {
  const { value, defaultValue, minDate, maxDate, unavailable, onChange } = props;
  const normalizedValue = useMemo(() => {
    if (value === null || value === void 0) return value;
    return {
      startDate: convertStringToDate(value.startDate) ?? null,
      endDate: convertStringToDate(value.endDate) ?? null
    };
  }, [value]);
  const normalizedDefaultValue = useMemo(() => {
    if (defaultValue === null || defaultValue === void 0)
      return defaultValue;
    return {
      startDate: convertStringToDate(defaultValue.startDate) ?? null,
      endDate: convertStringToDate(defaultValue.endDate) ?? null
    };
  }, [defaultValue]);
  const normalizedMinDate = useMemo(
    () => convertStringToDate(minDate),
    [minDate]
  );
  const normalizedMaxDate = useMemo(
    () => convertStringToDate(maxDate),
    [maxDate]
  );
  const normalizedUnavailableDates = useMemo(() => {
    return unavailable?.dates?.map((date) => convertStringToDate(date)).filter((date) => date !== null && date !== void 0);
  }, [unavailable?.dates]);
  const handleChange = useCallback(
    (change) => {
      onChange?.({
        ...change,
        startDate: change.startDate?.toISODate() ?? null,
        endDate: change.endDate?.toISODate() ?? null
      });
    },
    [onChange]
  );
  return {
    value: normalizedValue,
    defaultValue: normalizedDefaultValue,
    minDate: normalizedMinDate,
    maxDate: normalizedMaxDate,
    unavailable: {
      dates: normalizedUnavailableDates,
      daysOfWeek: unavailable?.daysOfWeek
    },
    onChange: handleChange
  };
};

function useDateFieldRangeState({
  valueProp,
  defaultValueProp,
  onChange
}) {
  const [startDate, setStartDate] = useOptionallyControlledState({
    controlledValue: valueProp !== void 0 ? valueProp?.startDate : void 0,
    defaultValue: defaultValueProp !== void 0 ? defaultValueProp?.startDate : void 0
  });
  const [endDate, setEndDate] = useOptionallyControlledState({
    controlledValue: valueProp !== void 0 ? valueProp?.endDate : void 0,
    defaultValue: defaultValueProp !== void 0 ? defaultValueProp?.endDate : void 0
  });
  const setSharedValue = (value) => {
    setStartDate(value.startDate);
    setEndDate(value.endDate);
  };
  const handleInputChange = (change) => {
    const range = change.isInputEmpty ? null : {
      startDate: change.startDate?.startOf("day") ?? null,
      endDate: change.endDate?.startOf("day") ?? null
    };
    onChange?.({
      startDate: range?.startDate ?? null,
      endDate: range?.endDate ?? null,
      isInputValid: change.isInputValid,
      isInputEmpty: change.isInputEmpty
    });
    setStartDate(range?.startDate ?? null);
    setEndDate(range?.endDate ?? null);
  };
  const handleCalendarSelection = ({
    startDate: startDate2,
    endDate: endDate2
  }) => {
    setSharedValue({ startDate: startDate2, endDate: endDate2 });
    onChange?.({
      startDate: startDate2,
      endDate: endDate2,
      isInputValid: true,
      isInputEmpty: false
    });
  };
  return {
    startDate,
    endDate,
    setStartDate,
    setEndDate,
    handleInputChange,
    handleCalendarSelection
  };
}

const DateFieldRangeCalendar = ({
  startDate,
  endDate,
  onKeyDown,
  onSelection,
  minDate,
  maxDate,
  unavailable,
  timezone: timezoneProp
}) => {
  const previousStartDate = usePrevious(startDate);
  const previousEndDate = usePrevious(endDate);
  const calendarRef = useRef(null);
  const timezone = useMemo(() => {
    return timezoneProp ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
  }, [timezoneProp]);
  useEffect(() => {
    if (startDate && startDate !== previousStartDate && endDate === null) {
      const startDateId = startDate.toISODate();
      if (!startDateId) return;
      const root = calendarRef.current?.getRootNode() ?? document;
      const deepActiveEl = root.activeElement?.shadowRoot?.activeElement ?? root.activeElement;
      if (deepActiveEl?.tagName === "INPUT") return;
      requestAnimationFrame(() => {
        calendarRef.current?.querySelector(`button[id$="${startDateId}"]`)?.focus();
      });
    }
  }, [startDate, endDate, previousStartDate]);
  const handleCalendarSelection = (data) => {
    if (!data.value) return;
    const calStartDate = data.value.start ? isoToUtcDate(data.value.start) : null;
    const calEndDate = data.value.end ? isoToUtcDate(data.value.end) : null;
    onSelection({
      startDate: calStartDate,
      endDate: calEndDate
    });
  };
  const defaultFocusedDate = useMemo(() => {
    const todayISODate = DateTime.now().setZone(timezone).toISODate();
    if (!startDate && !endDate) return todayISODate;
    if (!startDate) return endDate?.toISODate();
    if (!endDate) return startDate?.toISODate();
    if (endDate && !previousEndDate?.equals(endDate)) {
      return endDate.toISODate();
    } else if (startDate && !previousStartDate?.equals(startDate)) {
      return startDate.toISODate();
    }
    if (endDate) return endDate.toISODate();
    if (startDate) return startDate.toISODate();
    return todayISODate;
  }, [previousStartDate, previousEndDate, startDate, endDate, timezone]);
  return /* @__PURE__ */ jsxs(Fragment, { children: [
    /* @__PURE__ */ jsx(
      Calendar,
      {
        ref: calendarRef,
        range: true,
        onKeyDown,
        defaultFocusedDate,
        value: {
          start: startDate?.toISODate() || void 0,
          end: endDate?.toISODate() || void 0
        },
        onSelection: handleCalendarSelection,
        defaultTimeZone: timezone ?? "UTC",
        minDate: minDate?.toISODate() ?? void 0,
        maxDate: maxDate?.toISODate() ?? void 0,
        unavailable: unavailable ? {
          dates: unavailable.dates?.map((d) => d.toISODate() ?? ""),
          daysOfWeek: unavailable.daysOfWeek
        } : void 0,
        _disableAutofocus: true,
        _disableFocus: true
      },
      `${startDate?.toISODate()}-${endDate?.toISODate()}`
    ),
    /* @__PURE__ */ jsx(TimezoneMessage, { timezone })
  ] });
};

function validateDateRange({
  required,
  startDate,
  endDate,
  startDateConstraints,
  endDateConstraints
}) {
  if (!required && !startDate && !endDate) return true;
  return validateDate({
    date: startDate,
    constraints: { ...startDateConstraints, required: true }
  }) && validateDate({
    date: endDate,
    constraints: { ...endDateConstraints, required: true }
  }) && (!startDate || !endDate || startDate <= endDate);
}

const DateFieldRange = (props) => {
  const { aiMark, ...labelPropsWithoutAiMark } = props.labelProps || {};
  const data = {
    label: childrenToString(props.label),
    labelProps: labelPropsWithoutAiMark,
    description: childrenToString(props.description)
  };
  const trackingId = useTrackingId({
    name: "DateFieldRange",
    data,
    hasOverride: !!props["data-tracking-id"]
  });
  const {
    onFocus,
    onBlur,
    disableCalendar,
    required,
    mode,
    label,
    value: valueProp,
    defaultValue: defaultValueProp,
    minDate: minDateProp,
    maxDate: maxDateProp,
    unavailable: unavailableProp,
    onChange: onChangeProp,
    timezone,
    ...rest
  } = props;
  const popoverRef = useRef(null);
  const handleInputKeyDown = (ev) => {
    if (ev.key === "ArrowDown") {
      ev.preventDefault();
      popoverRef.current?.openPopover();
      requestAnimationFrame(() => {
        popoverContentRef.current?.querySelector("button")?.focus();
      });
    }
  };
  const { props: wrapperProps } = useFocusWithin({
    onBlur: (e) => {
      onBlur?.(e);
      popoverRef.current?.closePopover();
    },
    onFocus
  });
  const { value, defaultValue, minDate, maxDate, unavailable, onChange } = useDateFieldRangeConversion({
    value: valueProp,
    defaultValue: defaultValueProp,
    minDate: minDateProp,
    maxDate: maxDateProp,
    unavailable: unavailableProp,
    onChange: onChangeProp
  });
  const inputRef = useRef(null);
  const popoverContentRef = useRef(null);
  const handleChange = (change) => {
    const sharedConstraints = {
      unavailable,
      minDate: minDate ?? void 0,
      maxDate: maxDate ?? void 0
    };
    onChange?.({
      startDate: change.startDate?.startOf("day") ?? null,
      endDate: change.endDate?.startOf("day") ?? null,
      isInputValid: change.isInputValid,
      isInputEmpty: change.isInputEmpty,
      isDateRangeValid: validateDateRange({
        required,
        startDate: change.startDate?.startOf("day") ?? null,
        endDate: change.endDate?.startOf("day") ?? null,
        startDateConstraints: sharedConstraints,
        endDateConstraints: sharedConstraints
      })
    });
  };
  const { startDate, endDate, handleInputChange, handleCalendarSelection } = useDateFieldRangeState({
    valueProp: value,
    defaultValueProp: defaultValue,
    onChange: handleChange
  });
  const currentValidity = useMemo(() => {
    return validateDateRange({
      required,
      startDate,
      endDate,
      startDateConstraints: {
        unavailable,
        minDate: minDate ?? void 0,
        maxDate: maxDate ?? void 0
      },
      endDateConstraints: {
        unavailable,
        minDate: minDate ?? void 0,
        maxDate: maxDate ?? void 0
      }
    });
  }, [required, startDate, endDate, minDate, maxDate, unavailable]);
  const commonFieldProps = {
    ...rest,
    mode,
    label,
    onChange: handleInputChange,
    startDate: startDate ?? null,
    endDate: endDate ?? null,
    required,
    autoComplete: "off",
    "data-date-range-valid": currentValidity,
    "data-tracking-id": trackingId,
    "aria-label": label ? void 0 : "Select a date range"
  };
  const justTheField = /* @__PURE__ */ jsx(MaskedDateRangeInput, { ...commonFieldProps, ref: inputRef });
  if (disableCalendar) {
    return justTheField;
  }
  return /* @__PURE__ */ jsx("div", { ...wrapperProps, children: /* @__PURE__ */ jsxs(Popover, { ref: popoverRef, placement: "bottom-start", role: "grid", children: [
    /* @__PURE__ */ jsx(Popover.Trigger, { children: ({ ref: iRef, onKeyDown }) => {
      const mergedRef = mergeRefs([
        inputRef,
        iRef
      ]);
      return /* @__PURE__ */ jsx(
        MaskedDateRangeInput,
        {
          ...commonFieldProps,
          ref: mergedRef,
          onKeyDown: (ev) => {
            onKeyDown?.(ev);
            handleInputKeyDown(ev);
          },
          onClick: () => popoverRef.current?.openPopover(),
          onFocus,
          onBlur
        }
      );
    } }),
    /* @__PURE__ */ jsx(Popover.Content, { ref: popoverContentRef, "data-testid": "calendar-popover", children: /* @__PURE__ */ jsx(
      DateFieldRangeCalendar,
      {
        startDate,
        endDate,
        onSelection: (v) => {
          if (v.endDate) {
            inputRef.current?.focus();
            popoverRef.current?.closePopover();
          }
          handleCalendarSelection(v);
        },
        minDate,
        maxDate,
        unavailable,
        timezone
      }
    ) })
  ] }) });
};
DateFieldRange.displayName = "DateFieldRange";

export { DateFieldRange as D, MaskedDateRangeInput as M, useDateFieldRangeState as a, DateFieldRangeCalendar as b, useDateFieldRangeConversion as u };
//# sourceMappingURL=DateFieldRange-DPTC3EZz.js.map
