import _defineProperty from '@babel/runtime/helpers/defineProperty';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import debounce from '../../node_modules/lodash-es/debounce.js';
import cloneDeep from '../../node_modules/lodash-es/cloneDeep.js';
import 'core-js/modules/es.symbol.js';
import 'core-js/modules/es.array.concat.js';
import 'core-js/modules/es.array.filter.js';
import 'core-js/modules/es.array.find-index.js';
import 'core-js/modules/es.array.from.js';
import 'core-js/modules/es.array.includes.js';
import 'core-js/modules/es.number.constructor.js';
import 'core-js/modules/es.number.is-nan.js';
import 'core-js/modules/es.object.get-own-property-descriptor.js';
import 'core-js/modules/es.object.get-own-property-descriptors.js';
import 'core-js/modules/es.object.keys.js';
import 'core-js/modules/es.object.to-string.js';
import 'core-js/modules/es.regexp.exec.js';
import 'core-js/modules/es.string.includes.js';
import 'core-js/modules/es.string.iterator.js';
import 'core-js/modules/es.string.replace.js';
import 'core-js/modules/web.dom-collections.for-each.js';
import { useState, useRef, useCallback, useEffect } from 'react';
import customParseFormat from '../../packages/react/node_modules/dayjs/plugin/customParseFormat.js';
import { settings } from '../../constants/Settings.js';
import { PICKER_KINDS, RELATIVE_VALUES, INTERVAL_VALUES } from '../../constants/DateConstants.js';
import dayjs from '../../utils/dayjs.js';

function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var iotPrefix = settings.iotPrefix,
  prefix = settings.prefix;

/** check if current time is 24 hours
 *
 * @param {string} dateTimeMask like YYYY-MM-DD HH:MM
 * @returns true or false
 */
var is24hours = function is24hours(dateTimeMask) {
  var _dateTimeMask$split = dateTimeMask.split(' '),
    _dateTimeMask$split2 = _slicedToArray(_dateTimeMask$split, 2),
    time = _dateTimeMask$split2[1];
  var hoursMask = time === null || time === void 0 ? void 0 : time.split(':')[0];
  return hoursMask ? hoursMask.includes('H') : false;
};

/** convert time from 12 hours to 24 hours, if time12hour is 24 hours format, return immediately
 * @param {Object} object hh:mm A time oject
 * @returns HH:mm time object
 */
var format12hourTo24hour = function format12hourTo24hour(time12hour) {
  if (time12hour === '' || !time12hour) {
    return '00:00';
  }
  var _time12hour$split = time12hour.split(' '),
    _time12hour$split2 = _slicedToArray(_time12hour$split, 2),
    time = _time12hour$split2[0],
    modifier = _time12hour$split2[1];
  if (!modifier) {
    return time12hour;
  }

  // eslint-disable-next-line prefer-const
  var _time$split = time.split(':'),
    _time$split2 = _slicedToArray(_time$split, 2),
    hours = _time$split2[0],
    minutes = _time$split2[1];
  if (hours === '12') {
    hours = '00';
  }
  if (modifier === 'PM') {
    hours = parseInt(hours, 10) + 12;
  }
  return "".concat(hours, ":").concat(minutes);
};

/**
 * Parses a value object into a human readable value
 * @param {Object} value - the currently selected value
 * @param {string} value.kind - preset/relative/absolute
 * @param {Object} value.preset - the preset selection
 * @param {Object} value - the relative time selection
 * @param {Object} value - the absolute time selection
 * @returns {Object} a human readable value and a furtherly augmented value object
 */
var parseValue = function parseValue(timeRange, dateTimeMask, toLabel, hasTimeInput, timeZone) {
  var _timeRange$kind, _timeRange$relative, _timeRange$absolute, _timeRange$single, _timeRange$preset;
  var readableValue = '';
  if (!timeRange) {
    return {
      readableValue: readableValue
    };
  }
  var kind = (_timeRange$kind = timeRange.kind) !== null && _timeRange$kind !== void 0 ? _timeRange$kind : timeRange.timeRangeKind;
  var value = kind === PICKER_KINDS.RELATIVE ? (_timeRange$relative = timeRange === null || timeRange === void 0 ? void 0 : timeRange.relative) !== null && _timeRange$relative !== void 0 ? _timeRange$relative : timeRange.timeRangeValue : kind === PICKER_KINDS.ABSOLUTE ? (_timeRange$absolute = timeRange === null || timeRange === void 0 ? void 0 : timeRange.absolute) !== null && _timeRange$absolute !== void 0 ? _timeRange$absolute : timeRange.timeRangeValue : kind === PICKER_KINDS.SINGLE ? (_timeRange$single = timeRange === null || timeRange === void 0 ? void 0 : timeRange.single) !== null && _timeRange$single !== void 0 ? _timeRange$single : timeRange.timeSingleValue : (_timeRange$preset = timeRange === null || timeRange === void 0 ? void 0 : timeRange.preset) !== null && _timeRange$preset !== void 0 ? _timeRange$preset : timeRange.timeRangeValue;
  if (!value) {
    return {
      readableValue: readableValue
    };
  }
  var returnValue = cloneDeep(timeRange);
  dayjs.extend(customParseFormat);
  switch (kind) {
    case PICKER_KINDS.RELATIVE:
      {
        var endDate = dayjs.tz();
        if (value.relativeToWhen !== '') {
          endDate = value.relativeToWhen === RELATIVE_VALUES.YESTERDAY ? dayjs.tz().subtract(1, INTERVAL_VALUES.DAYS) : dayjs.tz();
          // wait to parse it until fully typed
          if (value.relativeToTime.length === 5) {
            endDate = endDate.hour(Number(value.relativeToTime.split(':')[0]));
            endDate = endDate.minute(Number(value.relativeToTime.split(':')[1]));
          }
          var startDate = endDate.clone().subtract(value.lastNumber, value.lastInterval ? value.lastInterval : INTERVAL_VALUES.MINUTES);
          if (!returnValue.relative) {
            returnValue.relative = {};
          }
          returnValue.relative.start = new Date(startDate.valueOf());
          returnValue.relative.end = new Date(endDate.valueOf());
          readableValue = "".concat(startDate.format(dateTimeMask), " ").concat(toLabel, " ").concat(endDate.format(dateTimeMask));
        }
        break;
      }
    case PICKER_KINDS.ABSOLUTE:
      {
        var _value$start;
        if ((_value$start = value.start) !== null && _value$start !== void 0 ? _value$start : value.startDate) {
          var _value$end;
          var _startDate = value.start instanceof Date ? dayjs(value.start).tz(timeZone, true) // Date from picker - preserve local time
          : value.start ? dayjs.tz(value.start) // Timestamp - convert from UTC
          : dayjs.tz(value.startDate, 'MM/DD/YYYY', timeZone); // String - parse with format

          if (value.startTime && value.startTime.includes(':')) {
            var formatedStartTime = is24hours(dateTimeMask) ? value.startTime : format12hourTo24hour(value.startTime);
            _startDate = _startDate.hours(formatedStartTime.split(':')[0]);
            _startDate = _startDate.minutes(formatedStartTime.split(':')[1]);
          }
          if (!returnValue.absolute) {
            returnValue.absolute = {};
          }
          returnValue.absolute.start = _startDate.valueOf();
          var startTimeValue = value.startTime ? "".concat(_startDate.format(dateTimeMask)) : "".concat(_startDate.format(dateTimeMask)).split(' ')[0];
          if ((_value$end = value.end) !== null && _value$end !== void 0 ? _value$end : value.endDate) {
            var _endDate = value.end instanceof Date ? dayjs(value.end).tz(timeZone, true) : value.end ? dayjs(value.end).tz(timeZone) : dayjs.tz(value.endDate, 'MM/DD/YYYY', timeZone);
            if (value.endTime && value.endTime.includes(':')) {
              var formatedEndTime = is24hours(dateTimeMask) ? value.endTime : format12hourTo24hour(value.endTime);
              _endDate = _endDate.hours(formatedEndTime.split(':')[0]);
              _endDate = _endDate.minutes(formatedEndTime.split(':')[1]);
            }
            var endTimeValue = value.endTime ? "".concat(_endDate.format(dateTimeMask)) : "".concat(_endDate.format(dateTimeMask)).split(' ')[0];
            returnValue.absolute.end = _endDate.valueOf();
            readableValue = "".concat(startTimeValue, " ").concat(toLabel, " ").concat(endTimeValue);
          } else {
            readableValue = "".concat(startTimeValue, " ").concat(toLabel, " ").concat(startTimeValue);
          }
        }
        break;
      }
    case PICKER_KINDS.SINGLE:
      {
        var _value$start2;
        // replace 'a' or 'A' in dateTimeMask to be consistent with time picker placeholder text
        var updatedDateTimeMask = dateTimeMask.replace(/a|A/, 'XM');
        if (!value.start && !value.startDate) {
          readableValue = updatedDateTimeMask;
          returnValue.single.start = null;
          break;
        }
        var _startDate2 = dayjs((_value$start2 = value.start) !== null && _value$start2 !== void 0 ? _value$start2 : value.startDate);
        if (value.startTime) {
          var _formatedStartTime = is24hours(dateTimeMask) ? value.startTime : format12hourTo24hour(value.startTime);
          _startDate2 = _startDate2.hours(_formatedStartTime.split(':')[0]);
          _startDate2 = _startDate2.minutes(_formatedStartTime.split(':')[1]);
        } else if (hasTimeInput) {
          returnValue.absolute.startTime = null;
          readableValue = updatedDateTimeMask;
          break;
        }
        returnValue.single.start = new Date(_startDate2.valueOf());
        readableValue = value.startTime ? "".concat(dayjs(_startDate2).format(dateTimeMask)) : "".concat(dayjs(_startDate2).format(dateTimeMask)).split(' ')[0];
        break;
      }
    default:
      readableValue = value.label;
      break;
  }
  return _objectSpread({
    readableValue: readableValue
  }, returnValue);
};

/**
 * Simple date/time validator
 *
 * @param {Date} date The date to check
 * @param {string} time The time string to check
 * @returns bool
 */
var isValidDate = function isValidDate(date, time) {
  var isValid24HoursRegex = /^([01][0-9]|2[0-3]):([0-5][0-9])$/;
  return date instanceof Date && !Number.isNaN(date) && isValid24HoursRegex.test(time);
};

/**
 * 12 hour time validator
 *
 * @param {string} time The time string to check
 * @returns bool
 */
var isValid12HourTime = function isValid12HourTime(time) {
  var isValid12HoursRegex = /^((0[1-9])?|(1[0-2])?)*:[0-5][0-9] (AM|PM)$/;
  return isValid12HoursRegex.test(time) || time === '';
};

/**
 * 24 hour time validator
 *
 * @param {string} time The time string to check
 * @returns bool
 */
var isValid24HourTime = function isValid24HourTime(time) {
  var isValid24HoursRegex = /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9])$/;
  return isValid24HoursRegex.test(time) || time === '';
};

// Validates absolute start date
var invalidStartDate = function invalidStartDate(startTime, endTime, absoluteValues) {
  // If start and end date have been selected
  var formatedStartTime = format12hourTo24hour(startTime);
  var formatedEndTime = format12hourTo24hour(endTime);
  if (absoluteValues.hasOwnProperty('start') && absoluteValues.hasOwnProperty('end') && isValidDate(new Date(absoluteValues.start), formatedStartTime)) {
    var startDate = new Date("".concat(absoluteValues.startDate, " ").concat(formatedStartTime));
    var endDate = new Date("".concat(absoluteValues.endDate, " ").concat(formatedEndTime));
    return startDate >= endDate;
  }
  // Return invalid date if start time and end date not selected or if inputted time is not valid
  return true;
};

/**
 *
 * @param {*} startTime
 * @param {*} endTime
 * @param {*} absoluteValues
 * @returns
 */
var invalidEndDate = function invalidEndDate(startTime, endTime, absoluteValues) {
  // If start and end date have been selected
  var formatedStartTime = format12hourTo24hour(startTime);
  var formatedEndTime = format12hourTo24hour(endTime);
  if (absoluteValues.hasOwnProperty('start') && absoluteValues.hasOwnProperty('end') && isValidDate(new Date(absoluteValues.end), formatedEndTime)) {
    var startDate = new Date("".concat(absoluteValues.startDate, " ").concat(formatedStartTime));
    var endDate = new Date("".concat(absoluteValues.endDate, " ").concat(formatedEndTime));
    return startDate >= endDate;
  }

  // Return invalid date if start time and end date not selected or if inputted time is not valid
  return true;
};

/**
 * A DateTimePicker hook for handling all absolute time values
 *
 * @returns Object an object containing:
 *    absoluteValue (object): The currently selected absolute value
 *    setAbsoluteValue (function): Set the current absolute value
 *    absoluteStartTimeInvalid (bool): Is the start time invalid
 *    setAbsoluteStartTimeInvalid (function): Set the start time invalid
 *    absoluteEndTimeInvalid (bool): Is the end time invalid
 *    setAbsoluteEndTimeInvalid (function): Set the end time invalid
 *    onAbsoluteStartTimeChange (function): handles changes to start time
 *    onAbsoluteEndTimeChange (function): handles changes to end time
 *    resetAbsoluteValue (function): reset absolute value to empty defaults
 */
var useAbsoluteDateTimeValue = function useAbsoluteDateTimeValue() {
  var _useState5 = useState(null),
    _useState6 = _slicedToArray(_useState5, 2),
    absoluteValue = _useState6[0],
    setAbsoluteValue = _useState6[1];
  var _useState7 = useState(false),
    _useState8 = _slicedToArray(_useState7, 2),
    absoluteStartTimeInvalid = _useState8[0],
    setAbsoluteStartTimeInvalid = _useState8[1];
  var _useState9 = useState(false),
    _useState10 = _slicedToArray(_useState9, 2),
    absoluteEndTimeInvalid = _useState10[0],
    setAbsoluteEndTimeInvalid = _useState10[1];

  // Util func to update the absolute value
  var changeAbsolutePropertyValue = function changeAbsolutePropertyValue(property, value) {
    setAbsoluteValue(function (prev) {
      return _objectSpread(_objectSpread({}, prev), {}, _defineProperty({}, property, value));
    });
  };
  var resetAbsoluteValue = function resetAbsoluteValue() {
    setAbsoluteValue({
      startDate: '',
      startTime: null,
      endDate: '',
      endTime: null
    });
  };

  // on change functions that trigger a absolute value update
  var onAbsoluteStartTimeChange = function onAbsoluteStartTimeChange(startTime, evt, meta) {
    var endTime = absoluteValue.endTime;
    var invalidStart = invalidStartDate(startTime, endTime, absoluteValue);
    var invalidEnd = invalidEndDate(startTime, endTime, absoluteValue);
    setAbsoluteStartTimeInvalid(meta.invalid || invalidStart);
    setAbsoluteEndTimeInvalid(invalidEnd);
    changeAbsolutePropertyValue('startTime', startTime);
  };
  var onAbsoluteEndTimeChange = function onAbsoluteEndTimeChange(endTime, evt, meta) {
    var startTime = absoluteValue.startTime;
    var invalidEnd = invalidEndDate(startTime, endTime, absoluteValue);
    var invalidStart = invalidStartDate(startTime, endTime, absoluteValue);
    setAbsoluteEndTimeInvalid(meta.invalid || invalidEnd);
    setAbsoluteStartTimeInvalid(invalidStart);
    changeAbsolutePropertyValue('endTime', endTime);
  };
  return {
    absoluteValue: absoluteValue,
    setAbsoluteValue: setAbsoluteValue,
    absoluteStartTimeInvalid: absoluteStartTimeInvalid,
    setAbsoluteStartTimeInvalid: setAbsoluteStartTimeInvalid,
    absoluteEndTimeInvalid: absoluteEndTimeInvalid,
    setAbsoluteEndTimeInvalid: setAbsoluteEndTimeInvalid,
    onAbsoluteStartTimeChange: onAbsoluteStartTimeChange,
    onAbsoluteEndTimeChange: onAbsoluteEndTimeChange,
    resetAbsoluteValue: resetAbsoluteValue,
    changeAbsolutePropertyValue: changeAbsolutePropertyValue,
    format12hourTo24hour: format12hourTo24hour,
    isValid12HourTime: isValid12HourTime,
    isValid24HourTime: isValid24HourTime
  };
};

/**
 * A DateTimePicker hook for handling all relative time values
 *
 * @param {object} object an object containing the interval and default relativeTo values for relative times
 * @returns Object an object containing:
 *    relativeValue (object): The currently set relative value object
 *    setRelativeValue (function): Set the current relative value
 *    relativeToTimeInvalid (bool): Is the current relative time invalid
 *    setRelativeToTimeInvalid (function): Set the current relative time invalid
 *    relativeLastNumberInvalid (bool): Is the relative last number invalid
 *    setRelativeLastNumberInvalid (function): Set the relative last number
 *    resetRelativeValue (function): Resets the relative value to empty defaults
 *    onRelativeLastNumberChange (function): handles changes to last number
 *    onRelativeLastIntervalChange (function): handles changes to interval
 *    onRelativeToWhenChange (function): handles changes to relative to when
 *    onRelativeToTimeChange (function): handles changes to relative to time
 */
var useRelativeDateTimeValue = function useRelativeDateTimeValue(_ref2) {
  var defaultInterval = _ref2.defaultInterval,
    defaultRelativeTo = _ref2.defaultRelativeTo;
  var _useState11 = useState(null),
    _useState12 = _slicedToArray(_useState11, 2),
    relativeValue = _useState12[0],
    setRelativeValue = _useState12[1];
  var _useState13 = useState(false),
    _useState14 = _slicedToArray(_useState13, 2),
    relativeToTimeInvalid = _useState14[0],
    setRelativeToTimeInvalid = _useState14[1];
  var _useState15 = useState(false),
    _useState16 = _slicedToArray(_useState15, 2),
    relativeLastNumberInvalid = _useState16[0],
    setRelativeLastNumberInvalid = _useState16[1];
  var resetRelativeValue = function resetRelativeValue() {
    setRelativeValue({
      lastNumber: 0,
      lastInterval: defaultInterval,
      relativeToWhen: defaultRelativeTo,
      relativeToTime: ''
    });
  };

  // Util func to update the relative value
  var changeRelativePropertyValue = function changeRelativePropertyValue(property, value) {
    setRelativeValue(function (prev) {
      return _objectSpread(_objectSpread({}, prev), {}, _defineProperty({}, property, value));
    });
  };

  // on change functions that trigger a relative value update
  var onRelativeLastNumberChange = function onRelativeLastNumberChange(event, _ref3) {
    var value = _ref3.value;
    var valid = !event.currentTarget.getAttribute('data-invalid');
    setRelativeLastNumberInvalid(!valid);
    if (valid) {
      changeRelativePropertyValue('lastNumber', Number(value));
    }
  };
  var onRelativeLastIntervalChange = function onRelativeLastIntervalChange(event) {
    changeRelativePropertyValue('lastInterval', event.currentTarget.value);
  };
  var onRelativeToWhenChange = function onRelativeToWhenChange(event) {
    changeRelativePropertyValue('relativeToWhen', event.currentTarget.value);
  };
  var onRelativeToTimeChange = function onRelativeToTimeChange(pickerValue, evt, meta) {
    setRelativeToTimeInvalid(meta.invalid);
    changeRelativePropertyValue('relativeToTime', pickerValue);
  };
  return {
    relativeValue: relativeValue,
    setRelativeValue: setRelativeValue,
    relativeToTimeInvalid: relativeToTimeInvalid,
    setRelativeToTimeInvalid: setRelativeToTimeInvalid,
    relativeLastNumberInvalid: relativeLastNumberInvalid,
    setRelativeLastNumberInvalid: setRelativeLastNumberInvalid,
    resetRelativeValue: resetRelativeValue,
    onRelativeLastNumberChange: onRelativeLastNumberChange,
    onRelativeLastIntervalChange: onRelativeLastIntervalChange,
    onRelativeToWhenChange: onRelativeToWhenChange,
    onRelativeToTimeChange: onRelativeToTimeChange
  };
};

/**
 * Simple hook to change the type of DateTimePicker we're working with (relative or absolute)
 *
 * @param {boolean} showRelativeOption Are the relative options shown by default
 * @returns an array containing [string (current kind), function (set the current range type), function (handles changing the range kind)]
 */
var useDateTimePickerRangeKind = function useDateTimePickerRangeKind(showRelativeOption) {
  var _useState17 = useState(showRelativeOption ? PICKER_KINDS.RELATIVE : PICKER_KINDS.ABSOLUTE),
    _useState18 = _slicedToArray(_useState17, 2),
    customRangeKind = _useState18[0],
    setCustomRangeKind = _useState18[1];
  var onCustomRangeChange = function onCustomRangeChange(kind) {
    setCustomRangeKind(kind);
  };
  return [customRangeKind, setCustomRangeKind, onCustomRangeChange];
};

/**
 * A hook handling interactions related to keyboard navigation and changing of the currently selected
 * DateTimePicker kind (relative/absolute)
 *
 * @param {boolean} showRelativeOption boolean determining if relative options are shown
 * @returns Object An object containing:
 *    presetListRef (Object): the ref to the preset div element
 *    isExpanded (boolean): is the DateTimePicker expanded
 *    setIsExpanded (function): set the isExpanded state
 *    getFocusableSiblings (function): return other focusable siblings from the presetListRef
 *    onFieldInteraction (function): handles changing expanded or focus state on different key presses
 *    onNavigateRadioButton (function): handles changing the focus state of the current radio button (relative/absolute)
 *    onNavigatePresets (function): handles changing the currently focuses preset as keyboard navigates
 */
var useDateTimePickerKeyboardInteraction = function useDateTimePickerKeyboardInteraction(_ref4) {
  var expanded = _ref4.expanded,
    setCustomRangeKind = _ref4.setCustomRangeKind;
  var _useState19 = useState(expanded),
    _useState20 = _slicedToArray(_useState19, 2),
    isExpanded = _useState20[0],
    setIsExpanded = _useState20[1];
  var presetListRef = useRef(null);
  var getFocusableSiblings = function getFocusableSiblings() {
    /* istanbul ignore else */
    if (presetListRef !== null && presetListRef !== void 0 && presetListRef.current) {
      var siblings = presetListRef.current.querySelectorAll('[tabindex]');
      return Array.from(siblings).filter(function (sibling) {
        return parseInt(sibling.getAttribute('tabindex'), 10) !== -1;
      });
    }
    return [];
  };
  var onFieldInteraction = function onFieldInteraction(_ref5) {
    var key = _ref5.key;
    switch (key) {
      case 'Escape':
        setIsExpanded(false);
        break;
      // if the input box is focused and a down arrow is pressed this
      // moves focus to the first item in the preset list that has a tabindex
      case 'ArrowDown':
        /* istanbul ignore else */
        if (presetListRef !== null && presetListRef !== void 0 && presetListRef.current) {
          var _listItems$;
          var listItems = getFocusableSiblings();
          /* istanbul ignore else */
          if (listItems !== null && listItems !== void 0 && (_listItems$ = listItems[0]) !== null && _listItems$ !== void 0 && _listItems$.focus) {
            listItems[0].focus();
          }
        }
        break;
      default:
        setIsExpanded(!isExpanded);
        break;
    }
  };
  var onFieldClick = function onFieldClick(e) {
    if (e.target.innerText !== 'Apply') setIsExpanded(!isExpanded);
  };

  /**
   * Moves up the preset list to the previous focusable element or wraps around to the bottom
   * if already at the top.
   */
  var moveToPreviousElement = function moveToPreviousElement() {
    var siblings = getFocusableSiblings();
    var index = siblings.findIndex(function (elem) {
      return elem === document.activeElement;
    });
    var previous = siblings[index - 1];
    if (previous) {
      previous.focus();
    } else {
      siblings[siblings.length - 1].focus();
    }
  };

  /**
   * Moves down the preset list to the next focusable element or wraps around to the top
   * if already at the bottom
   */
  var moveToNextElement = function moveToNextElement() {
    var siblings = getFocusableSiblings();
    var index = siblings.findIndex(function (elem) {
      return elem === document.activeElement;
    });
    var next = siblings[index + 1];
    if (next) {
      next.focus();
    } else {
      siblings[0].focus();
    }
  };
  var onNavigatePresets = function onNavigatePresets(_ref6) {
    var key = _ref6.key;
    switch (key) {
      case 'ArrowUp':
        moveToPreviousElement();
        break;
      case 'ArrowDown':
        moveToNextElement();
        break;
    }
  };

  /**
   * Allows navigation back and forth between the radio buttons for Relative/Absolute
   *
   * @param {KeyboardEvent} e
   */
  var onNavigateRadioButton = function onNavigateRadioButton(e) {
    if (e.target.getAttribute('id').includes('absolute')) {
      setCustomRangeKind(PICKER_KINDS.RELATIVE);
      document.activeElement.parentNode.previousSibling.querySelector('input[type="radio"]').focus();
    } else {
      setCustomRangeKind(PICKER_KINDS.ABSOLUTE);
      document.activeElement.parentNode.nextSibling.querySelector('input[type="radio"]').focus();
    }
  };
  return {
    presetListRef: presetListRef,
    isExpanded: isExpanded,
    setIsExpanded: setIsExpanded,
    getFocusableSiblings: getFocusableSiblings,
    onFieldInteraction: onFieldInteraction,
    onNavigateRadioButton: onNavigateRadioButton,
    onNavigatePresets: onNavigatePresets,
    onFieldClick: onFieldClick
  };
};

/**
 * Get an alternative human readable value for a preset to show in tooltips and dropdown
 * ie. 'Last 30 minutes' displays '2020-04-01 11:30 to Now' on the tooltip
 * @param {Object} object an object containing:
 *    currentValue: the current picker value
 *    strings: i18n translation strings
 *    dateTimeMask: the current date/time string mask
 *    humanValue: the human readable string value for the current time
 *
 * @returns {string} an interval string, starting point in time to now
 */
var getIntervalValue = function getIntervalValue(_ref7) {
  var currentValue = _ref7.currentValue,
    mergedI18n = _ref7.mergedI18n,
    dateTimeMask = _ref7.dateTimeMask,
    humanValue = _ref7.humanValue;
  if (currentValue) {
    if (currentValue.kind === PICKER_KINDS.PRESET) {
      return "".concat(dayjs.tz().subtract(currentValue.preset.offset, 'minutes').format(dateTimeMask), " ").concat(mergedI18n.toNowLabel);
    }
    return humanValue;
  }
  return '';
};

/**
 * Hook to validate event and invoke callback
 * @param {function} closeDropdownCallback: function that will be called if validation passes
 * @returns void
 */
var useDateTimePickerClickOutside = function useDateTimePickerClickOutside(closeDropdownCallback, containerRef) {
  return function (evt) {
    var _evt$target$classList, _evt$target$classList2, _evt$target$classList3, _evt$target$classList4, _evt$target$classList5, _containerRef$current;
    if (evt !== null && evt !== void 0 && (_evt$target$classList = evt.target.classList) !== null && _evt$target$classList !== void 0 && _evt$target$classList.contains("".concat(iotPrefix, "--date-time-picker__listitem--custom")) || evt !== null && evt !== void 0 && (_evt$target$classList2 = evt.target.classList) !== null && _evt$target$classList2 !== void 0 && _evt$target$classList2.contains("".concat(iotPrefix, "--date-time-picker__menu-btn-back")) || evt !== null && evt !== void 0 && (_evt$target$classList3 = evt.target.classList) !== null && _evt$target$classList3 !== void 0 && _evt$target$classList3.contains("".concat(iotPrefix, "--date-time-picker__menu-btn-reset")) || evt !== null && evt !== void 0 && (_evt$target$classList4 = evt.target.classList) !== null && _evt$target$classList4 !== void 0 && _evt$target$classList4.contains("".concat(iotPrefix, "--date-time-picker__menu-btn-cancel")) || evt !== null && evt !== void 0 && (_evt$target$classList5 = evt.target.classList) !== null && _evt$target$classList5 !== void 0 && _evt$target$classList5.contains("".concat(iotPrefix, "--date-time-picker__menu-btn-apply"))) {
      return;
    }
    if ((_containerRef$current = containerRef.current) !== null && _containerRef$current !== void 0 && _containerRef$current.firstChild.contains(evt.target)) {
      closeDropdownCallback({
        isEventOnField: true
      });
      return;
    }

    // Composed path is needed in order to detect if event is bubbled from TimePickerSpinner which is a React Portal
    if (evt.composed && evt.composedPath().some(function (el) {
      var _el$classList, _el$classList2;
      return ((_el$classList = el.classList) === null || _el$classList === void 0 ? void 0 : _el$classList.contains("".concat(iotPrefix, "--time-picker-spinner"))) || ((_el$classList2 = el.classList) === null || _el$classList2 === void 0 ? void 0 : _el$classList2.contains("".concat(prefix, "--date-picker__calendar")));
    })) {
      return;
    }
    closeDropdownCallback({
      isEventOnField: false
    });
  };
};

/**
 * Utility function to get time picker kind key
 * @param {Object} object: an object containing:
 *   kind: time picker kind
 *   timeRangeKind: time range kind
 * @returns
 */
var getTimeRangeKindKey = function getTimeRangeKindKey(_ref9) {
  var kind = _ref9.kind,
    timeRangeKind = _ref9.timeRangeKind;
  if (kind === PICKER_KINDS.SINGLE || timeRangeKind === PICKER_KINDS.SINGLE) {
    return 'timeSingleValue';
  }
  return 'timeRangeValue';
};

/**
 * Hook to close time picker dropdown and reset default value
 * @param {Object} object: an object containing:
 *   isExpanded: current state of the dropdown
 *   setIsExpanded: useState callback
 *   isCustomRange: if dropdown was opened in custom range
 *   setIsCustomRange: useState callback
 *   defaultValue: props value for time picker
 *   parseDefaultValue: parses value from string to time picker format
 *   setCustomRangeKind: useState callback
 *   lastAppliedValue: last saved value
 * @returns {function}
 */
var useCloseDropdown = function useCloseDropdown(_ref10) {
  var isExpanded = _ref10.isExpanded,
    setIsExpanded = _ref10.setIsExpanded,
    isCustomRange = _ref10.isCustomRange,
    setIsCustomRange = _ref10.setIsCustomRange,
    defaultValue = _ref10.defaultValue,
    parseDefaultValue = _ref10.parseDefaultValue,
    setCustomRangeKind = _ref10.setCustomRangeKind,
    lastAppliedValue = _ref10.lastAppliedValue,
    singleTimeValue = _ref10.singleTimeValue,
    setSingleDateValue = _ref10.setSingleDateValue,
    setSingleTimeValue = _ref10.setSingleTimeValue;
  return useCallback(function (_ref11) {
    var isEventOnField = _ref11.isEventOnField;
    if (!isExpanded) {
      return;
    }
    if (!isEventOnField) {
      setIsExpanded(false);
    }

    // memoized value at the time when dropdown was opened
    if (!isCustomRange) {
      setIsCustomRange(false);
    }
    if (((lastAppliedValue === null || lastAppliedValue === void 0 ? void 0 : lastAppliedValue.timeRangeKind) === PICKER_KINDS.SINGLE || (lastAppliedValue === null || lastAppliedValue === void 0 ? void 0 : lastAppliedValue.kind) === PICKER_KINDS.SINGLE) && !singleTimeValue) {
      setSingleDateValue({
        start: null,
        startDate: null
      });
      setSingleTimeValue(null);
      return;
    }
    if (lastAppliedValue) {
      setCustomRangeKind(lastAppliedValue.kind || lastAppliedValue.timeRangeKind);
      parseDefaultValue(_objectSpread(_objectSpread({}, lastAppliedValue), !lastAppliedValue.timeRangeKind && _defineProperty({
        timeRangeKind: lastAppliedValue === null || lastAppliedValue === void 0 ? void 0 : lastAppliedValue.kind
      }, getTimeRangeKindKey(lastAppliedValue), lastAppliedValue[lastAppliedValue === null || lastAppliedValue === void 0 ? void 0 : lastAppliedValue.kind.toLowerCase()])));
    } else {
      setCustomRangeKind(defaultValue ? defaultValue.timeRangeKind : PICKER_KINDS.RELATIVE);
      parseDefaultValue(defaultValue);
    }
  },
  // eslint-disable-next-line react-hooks/exhaustive-deps
  [defaultValue, isExpanded, setCustomRangeKind, setIsExpanded, lastAppliedValue]);
};

/**
 * For a given element, walk up the dom to find scroll container.
 * Only gets first as modals should prevent scrolling in elements above.
 * @param{element} element
 */
var getScrollParent = function getScrollParent(element) {
  try {
    /* istanbul ignore next */
    if (element.scrollHeight > parseInt(element.clientHeight, 10) + 10 || element.scrollWidth > parseInt(element.clientWidth, 10) + 10) {
      var computedStyle = window.getComputedStyle(element);
      if (['scroll', 'auto'].includes(computedStyle.overflowY) || ['scroll', 'auto'].includes(computedStyle.overflow)) {
        return element;
      }
    }
    if (element.parentElement) {
      return getScrollParent(element.parentElement);
    }
    return document.scrollingElement;
  } catch (error) {
    /* istanbul ignore next */
    return window;
  }
};

/**
 * A hook handling the height of the drop down menu
 *
 * @param {object} containerRef the ref to the container div of the drop down menu
 * @param {boolean} isSingleSelect if it is single select calendar
 * @param {boolean} isCustomRange if dropdown was opened in custom range
 * @param {boolean} showRelativeOption are the relative options shown by default
 * @param {string} customRangeKind custom range kind is either relative or absolute
 * @param {function} setIsExpanded set the isExpanded state
 * @returns Object An object containing:
 *    offTop (boolean): if the menu is off top
 *    offBottom (boolean): if the menu is off bottom
 *    inputTop (string) : the top position of the date time input
 *    inputBottom (string): the bottom position of the date time input
 *    customHeight (string): the adjusted height of the drop down menu if both offTop and offBottom are true
 *    maxHeight (string) : maximum height of the drop down menu
 */
var useCustomHeight = function useCustomHeight(_ref13) {
  var _containerRef$current2, _containerRef$current3;
  var containerRef = _ref13.containerRef,
    isSingleSelect = _ref13.isSingleSelect,
    isCustomRange = _ref13.isCustomRange,
    showRelativeOption = _ref13.showRelativeOption,
    customRangeKind = _ref13.customRangeKind,
    setIsExpanded = _ref13.setIsExpanded;
  // calculate max height for varies dropdown
  var presetMaxHeight = 315;
  var relativeMaxHeight = 200;
  var absoluteMaxHeight = 200;
  var singleMaxHeight = 200;
  var footerHeight = 40;
  var invalidDateWarningHeight = 38;
  var invalidTimeWarningHeight = 22;
  var relativeOptionHeight = 69;
  var timeInputHeight = 64;
  var maxHeight = isSingleSelect ? singleMaxHeight : isCustomRange ? (customRangeKind === PICKER_KINDS.ABSOLUTE ? absoluteMaxHeight : relativeMaxHeight) + (showRelativeOption ? relativeOptionHeight : 0) : presetMaxHeight;
  var closeDropDown = function closeDropDown() {
    setIsExpanded(false);
  };
  useEffect(function () {
    var firstScrollableParent = getScrollParent(containerRef.current);
    if (firstScrollableParent) {
      firstScrollableParent.addEventListener('scroll', closeDropDown);
    }
    window.addEventListener('scroll', closeDropDown);
    return function () {
      if (firstScrollableParent) {
        firstScrollableParent.removeEventListener('scroll', closeDropDown);
      }
      window.removeEventListener('scroll', closeDropDown);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // re-calculate window height when resize
  var _useState23 = useState(window.innerHeight || document.documentElement.clientHeight),
    _useState24 = _slicedToArray(_useState23, 2),
    windowHeight = _useState24[0],
    setWindowHeight = _useState24[1];
  var handleWindowResize = debounce(function () {
    setWindowHeight(window.innerHeight || document.documentElement.clientHeight);
  }, 50);
  useEffect(function () {
    window.addEventListener('resize', handleWindowResize);
    return function () {
      return window.removeEventListener('resize', handleWindowResize);
    };
  }, [handleWindowResize]);

  // calculate if flyout menu will be off top or bottom of the screen
  var inputBottom = containerRef === null || containerRef === void 0 || (_containerRef$current2 = containerRef.current) === null || _containerRef$current2 === void 0 ? void 0 : _containerRef$current2.getBoundingClientRect().bottom;
  var inputTop = containerRef === null || containerRef === void 0 || (_containerRef$current3 = containerRef.current) === null || _containerRef$current3 === void 0 ? void 0 : _containerRef$current3.getBoundingClientRect().top;
  var flyoutMenuHeight = maxHeight + footerHeight;
  var offBottom = windowHeight - inputBottom < flyoutMenuHeight;
  var offTop = inputTop < flyoutMenuHeight;
  var topGap = inputTop;
  var bottomGap = windowHeight - inputBottom;
  var customHeight = offBottom && offTop ? (topGap > bottomGap ? topGap : bottomGap) - footerHeight : undefined;
  return [offTop, offBottom, inputTop, inputBottom, customHeight, maxHeight, invalidDateWarningHeight, invalidTimeWarningHeight, timeInputHeight];
};

export { format12hourTo24hour, getIntervalValue, getScrollParent, invalidEndDate, invalidStartDate, isValid12HourTime, isValid24HourTime, isValidDate, parseValue, useAbsoluteDateTimeValue, useCloseDropdown, useCustomHeight, useDateTimePickerClickOutside, useDateTimePickerKeyboardInteraction, useDateTimePickerRangeKind, useRelativeDateTimeValue };
