function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
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; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
// BTime control (not form input control)
import { extend, REF_FOR_KEY } from '../../vue';
import { NAME_TIME } from '../../constants/components';
import { EVENT_NAME_CONTEXT } from '../../constants/events';
import { CODE_LEFT, CODE_RIGHT } from '../../constants/key-codes';
import { PROP_TYPE_ARRAY_STRING, PROP_TYPE_BOOLEAN, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props';
import { RX_TIME } from '../../constants/regex';
import { concat } from '../../utils/array';
import { createDate, createDateFormatter } from '../../utils/date';
import { attemptBlur, attemptFocus, contains, getActiveElement, requestAF } from '../../utils/dom';
import { stopEvent } from '../../utils/events';
import { identity } from '../../utils/identity';
import { isNull, isUndefinedOrNull } from '../../utils/inspect';
import { looseEqual } from '../../utils/loose-equal';
import { isLocaleRTL } from '../../utils/locale';
import { makeModelMixin } from '../../utils/model';
import { toInteger } from '../../utils/number';
import { pick, sortKeys } from '../../utils/object';
import { makeProp, makePropsConfigurable } from '../../utils/props';
import { toString } from '../../utils/string';
import { idMixin, props as idProps } from '../../mixins/id';
import { normalizeSlotMixin } from '../../mixins/normalize-slot';
import { BFormSpinbutton, props as BFormSpinbuttonProps } from '../form-spinbutton/form-spinbutton';
import { BIconCircleFill, BIconChevronUp } from '../../icons/icons';

// --- Constants ---

var _makeModelMixin = makeModelMixin('value', {
    type: PROP_TYPE_STRING,
    defaultValue: ''
  }),
  modelMixin = _makeModelMixin.mixin,
  modelProps = _makeModelMixin.props,
  MODEL_PROP_NAME = _makeModelMixin.prop,
  MODEL_EVENT_NAME = _makeModelMixin.event;
var NUMERIC = 'numeric';

// --- Helper methods ---

var padLeftZeros = function padLeftZeros(value) {
  return "00".concat(value || '').slice(-2);
};
var parseHMS = function parseHMS(value) {
  value = toString(value);
  var hh = null,
    mm = null,
    ss = null;
  if (RX_TIME.test(value)) {
    ;
    var _value$split$map = value.split(':').map(function (v) {
      return toInteger(v, null);
    });
    var _value$split$map2 = _slicedToArray(_value$split$map, 3);
    hh = _value$split$map2[0];
    mm = _value$split$map2[1];
    ss = _value$split$map2[2];
  }
  return {
    hours: isUndefinedOrNull(hh) ? null : hh,
    minutes: isUndefinedOrNull(mm) ? null : mm,
    seconds: isUndefinedOrNull(ss) ? null : ss,
    ampm: isUndefinedOrNull(hh) || hh < 12 ? 0 : 1
  };
};
var formatHMS = function formatHMS(_ref) {
  var hours = _ref.hours,
    minutes = _ref.minutes,
    seconds = _ref.seconds;
  var requireSeconds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  if (isNull(hours) || isNull(minutes) || requireSeconds && isNull(seconds)) {
    return '';
  }
  var hms = [hours, minutes, requireSeconds ? seconds : 0];
  return hms.map(padLeftZeros).join(':');
};

// --- Props ---

export var props = makePropsConfigurable(sortKeys(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, idProps), modelProps), pick(BFormSpinbuttonProps, ['labelIncrement', 'labelDecrement'])), {}, {
  // ID of label element
  ariaLabelledby: makeProp(PROP_TYPE_STRING),
  disabled: makeProp(PROP_TYPE_BOOLEAN, false),
  footerTag: makeProp(PROP_TYPE_STRING, 'footer'),
  headerTag: makeProp(PROP_TYPE_STRING, 'header'),
  hidden: makeProp(PROP_TYPE_BOOLEAN, false),
  hideHeader: makeProp(PROP_TYPE_BOOLEAN, false),
  // Explicitly force 12 or 24 hour time
  // Default is to use resolved locale for 12/24 hour display
  // Tri-state: `true` = 12, `false` = 24, `null` = auto
  hour12: makeProp(PROP_TYPE_BOOLEAN, null),
  labelAm: makeProp(PROP_TYPE_STRING, 'AM'),
  labelAmpm: makeProp(PROP_TYPE_STRING, 'AM/PM'),
  labelHours: makeProp(PROP_TYPE_STRING, 'Hours'),
  labelMinutes: makeProp(PROP_TYPE_STRING, 'Minutes'),
  labelNoTimeSelected: makeProp(PROP_TYPE_STRING, 'No time selected'),
  labelPm: makeProp(PROP_TYPE_STRING, 'PM'),
  labelSeconds: makeProp(PROP_TYPE_STRING, 'Seconds'),
  labelSelected: makeProp(PROP_TYPE_STRING, 'Selected time'),
  locale: makeProp(PROP_TYPE_ARRAY_STRING),
  minutesStep: makeProp(PROP_TYPE_NUMBER_STRING, 1),
  readonly: makeProp(PROP_TYPE_BOOLEAN, false),
  secondsStep: makeProp(PROP_TYPE_NUMBER_STRING, 1),
  // If `true`, show the second spinbutton
  showSeconds: makeProp(PROP_TYPE_BOOLEAN, false)
})), NAME_TIME);

// --- Main component ---

// @vue/component
export var BTime = /*#__PURE__*/extend({
  name: NAME_TIME,
  mixins: [idMixin, modelMixin, normalizeSlotMixin],
  props: props,
  data: function data() {
    var parsed = parseHMS(this[MODEL_PROP_NAME] || '');
    return {
      // Spin button models
      modelHours: parsed.hours,
      modelMinutes: parsed.minutes,
      modelSeconds: parsed.seconds,
      modelAmpm: parsed.ampm,
      // Internal flag to enable aria-live regions
      isLive: false
    };
  },
  computed: {
    computedHMS: function computedHMS() {
      var hours = this.modelHours;
      var minutes = this.modelMinutes;
      var seconds = this.modelSeconds;
      return formatHMS({
        hours: hours,
        minutes: minutes,
        seconds: seconds
      }, this.showSeconds);
    },
    resolvedOptions: function resolvedOptions() {
      // Resolved locale options
      var locale = concat(this.locale).filter(identity);
      var options = {
        hour: NUMERIC,
        minute: NUMERIC,
        second: NUMERIC
      };
      if (!isUndefinedOrNull(this.hour12)) {
        // Force 12 or 24 hour clock
        options.hour12 = !!this.hour12;
      }
      var dtf = new Intl.DateTimeFormat(locale, options);
      var resolved = dtf.resolvedOptions();
      var hour12 = resolved.hour12 || false;
      // IE 11 doesn't resolve the hourCycle, so we make
      // an assumption and fall back to common values
      var hourCycle = resolved.hourCycle || (hour12 ? 'h12' : 'h23');
      return {
        locale: resolved.locale,
        hour12: hour12,
        hourCycle: hourCycle
      };
    },
    computedLocale: function computedLocale() {
      return this.resolvedOptions.locale;
    },
    computedLang: function computedLang() {
      return (this.computedLocale || '').replace(/-u-.*$/, '');
    },
    computedRTL: function computedRTL() {
      return isLocaleRTL(this.computedLang);
    },
    computedHourCycle: function computedHourCycle() {
      // h11, h12, h23, or h24
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Locale/hourCycle
      // h12 - Hour system using 1–12. Corresponds to 'h' in patterns. The 12 hour clock, with midnight starting at 12:00 am
      // h23 - Hour system using 0–23. Corresponds to 'H' in patterns. The 24 hour clock, with midnight starting at 0:00
      // h11 - Hour system using 0–11. Corresponds to 'K' in patterns. The 12 hour clock, with midnight starting at 0:00 am
      // h24 - Hour system using 1–24. Corresponds to 'k' in pattern. The 24 hour clock, with midnight starting at 24:00
      // For h12 or h24, we visually format 00 hours as 12
      return this.resolvedOptions.hourCycle;
    },
    is12Hour: function is12Hour() {
      return !!this.resolvedOptions.hour12;
    },
    context: function context() {
      return {
        locale: this.computedLocale,
        isRTL: this.computedRTL,
        hourCycle: this.computedHourCycle,
        hour12: this.is12Hour,
        hours: this.modelHours,
        minutes: this.modelMinutes,
        seconds: this.showSeconds ? this.modelSeconds : 0,
        value: this.computedHMS,
        formatted: this.formattedTimeString
      };
    },
    valueId: function valueId() {
      return this.safeId() || null;
    },
    computedAriaLabelledby: function computedAriaLabelledby() {
      return [this.ariaLabelledby, this.valueId].filter(identity).join(' ') || null;
    },
    timeFormatter: function timeFormatter() {
      // Returns a formatter function reference
      // The formatter converts the time to a localized string
      var options = {
        hour12: this.is12Hour,
        hourCycle: this.computedHourCycle,
        hour: NUMERIC,
        minute: NUMERIC,
        timeZone: 'UTC'
      };
      if (this.showSeconds) {
        options.second = NUMERIC;
      }
      // Formats the time as a localized string
      return createDateFormatter(this.computedLocale, options);
    },
    numberFormatter: function numberFormatter() {
      // Returns a formatter function reference
      // The formatter always formats as 2 digits and is localized
      var nf = new Intl.NumberFormat(this.computedLocale, {
        style: 'decimal',
        minimumIntegerDigits: 2,
        minimumFractionDigits: 0,
        maximumFractionDigits: 0,
        notation: 'standard'
      });
      return nf.format;
    },
    formattedTimeString: function formattedTimeString() {
      var hours = this.modelHours;
      var minutes = this.modelMinutes;
      var seconds = this.showSeconds ? this.modelSeconds || 0 : 0;
      if (this.computedHMS) {
        return this.timeFormatter(createDate(Date.UTC(0, 0, 1, hours, minutes, seconds)));
      }
      return this.labelNoTimeSelected || ' ';
    },
    spinScopedSlots: function spinScopedSlots() {
      var h = this.$createElement;
      return {
        increment: function increment(_ref2) {
          var hasFocus = _ref2.hasFocus;
          return h(BIconChevronUp, {
            props: {
              scale: hasFocus ? 1.5 : 1.25
            },
            attrs: {
              'aria-hidden': 'true'
            }
          });
        },
        decrement: function decrement(_ref3) {
          var hasFocus = _ref3.hasFocus;
          return h(BIconChevronUp, {
            props: {
              flipV: true,
              scale: hasFocus ? 1.5 : 1.25
            },
            attrs: {
              'aria-hidden': 'true'
            }
          });
        }
      };
    }
  },
  watch: _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, MODEL_PROP_NAME, function (newValue, oldValue) {
    if (newValue !== oldValue && !looseEqual(parseHMS(newValue), parseHMS(this.computedHMS))) {
      var _parseHMS = parseHMS(newValue),
        hours = _parseHMS.hours,
        minutes = _parseHMS.minutes,
        seconds = _parseHMS.seconds,
        ampm = _parseHMS.ampm;
      this.modelHours = hours;
      this.modelMinutes = minutes;
      this.modelSeconds = seconds;
      this.modelAmpm = ampm;
    }
  }), "computedHMS", function computedHMS(newValue, oldValue) {
    if (newValue !== oldValue) {
      this.$emit(MODEL_EVENT_NAME, newValue);
    }
  }), "context", function context(newValue, oldValue) {
    if (!looseEqual(newValue, oldValue)) {
      this.$emit(EVENT_NAME_CONTEXT, newValue);
    }
  }), "modelAmpm", function modelAmpm(newValue, oldValue) {
    var _this = this;
    if (newValue !== oldValue) {
      var hours = isNull(this.modelHours) ? 0 : this.modelHours;
      this.$nextTick(function () {
        if (newValue === 0 && hours > 11) {
          // Switched to AM
          _this.modelHours = hours - 12;
        } else if (newValue === 1 && hours < 12) {
          // Switched to PM
          _this.modelHours = hours + 12;
        }
      });
    }
  }), "modelHours", function modelHours(newHours, oldHours) {
    if (newHours !== oldHours) {
      this.modelAmpm = newHours > 11 ? 1 : 0;
    }
  }),
  created: function created() {
    var _this2 = this;
    this.$nextTick(function () {
      _this2.$emit(EVENT_NAME_CONTEXT, _this2.context);
    });
  },
  mounted: function mounted() {
    this.setLive(true);
  },
  /* istanbul ignore next */activated: function activated() {
    this.setLive(true);
  },
  /* istanbul ignore next */deactivated: function deactivated() {
    this.setLive(false);
  },
  beforeDestroy: function beforeDestroy() {
    this.setLive(false);
  },
  methods: {
    // Public methods
    focus: function focus() {
      if (!this.disabled) {
        // We focus the first spin button
        attemptFocus(this.$refs.spinners[0]);
      }
    },
    blur: function blur() {
      if (!this.disabled) {
        var activeElement = getActiveElement();
        if (contains(this.$el, activeElement)) {
          attemptBlur(activeElement);
        }
      }
    },
    // Formatters for the spin buttons
    formatHours: function formatHours(hh) {
      var hourCycle = this.computedHourCycle;
      // We always store 0-23, but format based on h11/h12/h23/h24 formats
      hh = this.is12Hour && hh > 12 ? hh - 12 : hh;
      // Determine how 00:00 and 12:00 are shown
      hh = hh === 0 && hourCycle === 'h12' ? 12 : hh === 0 && hourCycle === 'h24' ? /* istanbul ignore next */24 : hh === 12 && hourCycle === 'h11' ? /* istanbul ignore next */0 : hh;
      return this.numberFormatter(hh);
    },
    formatMinutes: function formatMinutes(mm) {
      return this.numberFormatter(mm);
    },
    formatSeconds: function formatSeconds(ss) {
      return this.numberFormatter(ss);
    },
    formatAmpm: function formatAmpm(ampm) {
      // These should come from label props???
      // `ampm` should always be a value of `0` or `1`
      return ampm === 0 ? this.labelAm : ampm === 1 ? this.labelPm : '';
    },
    // Spinbutton on change handlers
    setHours: function setHours(value) {
      this.modelHours = value;
    },
    setMinutes: function setMinutes(value) {
      this.modelMinutes = value;
    },
    setSeconds: function setSeconds(value) {
      this.modelSeconds = value;
    },
    setAmpm: function setAmpm(value) {
      this.modelAmpm = value;
    },
    onSpinLeftRight: function onSpinLeftRight() {
      var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var type = event.type,
        keyCode = event.keyCode;
      if (!this.disabled && type === 'keydown' && (keyCode === CODE_LEFT || keyCode === CODE_RIGHT)) {
        stopEvent(event);
        var spinners = this.$refs.spinners || [];
        var index = spinners.map(function (cmp) {
          return !!cmp.hasFocus;
        }).indexOf(true);
        index = index + (keyCode === CODE_LEFT ? -1 : 1);
        index = index >= spinners.length ? 0 : index < 0 ? spinners.length - 1 : index;
        attemptFocus(spinners[index]);
      }
    },
    setLive: function setLive(on) {
      var _this3 = this;
      if (on) {
        this.$nextTick(function () {
          requestAF(function () {
            _this3.isLive = true;
          });
        });
      } else {
        this.isLive = false;
      }
    }
  },
  render: function render(h) {
    var _this4 = this;
    // If hidden, we just render a placeholder comment
    /* istanbul ignore if */
    if (this.hidden) {
      return h();
    }
    var disabled = this.disabled,
      readonly = this.readonly,
      locale = this.computedLocale,
      ariaLabelledby = this.computedAriaLabelledby,
      labelIncrement = this.labelIncrement,
      labelDecrement = this.labelDecrement,
      valueId = this.valueId,
      focusHandler = this.focus;
    var spinIds = [];

    // Helper method to render a spinbutton
    var makeSpinbutton = function makeSpinbutton(handler, key, classes) {
      var spinbuttonProps = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
      var id = _this4.safeId("_spinbutton_".concat(key, "_")) || null;
      spinIds.push(id);
      return h(BFormSpinbutton, _defineProperty({
        class: classes,
        props: _objectSpread({
          id: id,
          placeholder: '--',
          vertical: true,
          required: true,
          disabled: disabled,
          readonly: readonly,
          locale: locale,
          labelIncrement: labelIncrement,
          labelDecrement: labelDecrement,
          wrap: true,
          ariaControls: valueId,
          min: 0
        }, spinbuttonProps),
        scopedSlots: _this4.spinScopedSlots,
        on: {
          // We use `change` event to minimize SR verbosity
          // As the spinbutton will announce each value change
          // and we don't want the formatted time to be announced
          // on each value input if repeat is happening
          change: handler
        },
        key: key,
        ref: 'spinners'
      }, REF_FOR_KEY, true));
    };

    // Helper method to return a "colon" separator
    var makeColon = function makeColon() {
      return h('div', {
        staticClass: 'd-flex flex-column',
        class: {
          'text-muted': disabled || readonly
        },
        attrs: {
          'aria-hidden': 'true'
        }
      }, [h(BIconCircleFill, {
        props: {
          shiftV: 4,
          scale: 0.5
        }
      }), h(BIconCircleFill, {
        props: {
          shiftV: -4,
          scale: 0.5
        }
      })]);
    };
    var $spinners = [];

    // Hours
    $spinners.push(makeSpinbutton(this.setHours, 'hours', 'b-time-hours', {
      value: this.modelHours,
      max: 23,
      step: 1,
      formatterFn: this.formatHours,
      ariaLabel: this.labelHours
    }));

    // Spacer
    $spinners.push(makeColon());

    // Minutes
    $spinners.push(makeSpinbutton(this.setMinutes, 'minutes', 'b-time-minutes', {
      value: this.modelMinutes,
      max: 59,
      step: this.minutesStep || 1,
      formatterFn: this.formatMinutes,
      ariaLabel: this.labelMinutes
    }));
    if (this.showSeconds) {
      // Spacer
      $spinners.push(makeColon());
      // Seconds
      $spinners.push(makeSpinbutton(this.setSeconds, 'seconds', 'b-time-seconds', {
        value: this.modelSeconds,
        max: 59,
        step: this.secondsStep || 1,
        formatterFn: this.formatSeconds,
        ariaLabel: this.labelSeconds
      }));
    }

    // AM/PM ?
    // depends on client settings, shouldn't be rendered on server
    if (this.isLive && this.is12Hour) {
      // TODO:
      //   If locale is RTL, unshift this instead of push?
      //   And switch class `ml-2` to `mr-2`
      //   Note some LTR locales (i.e. zh) also place AM/PM to the left
      $spinners.push(makeSpinbutton(this.setAmpm, 'ampm', 'b-time-ampm', {
        value: this.modelAmpm,
        max: 1,
        formatterFn: this.formatAmpm,
        ariaLabel: this.labelAmpm,
        // We set `required` as `false`, since this always has a value
        required: false
      }));
    }

    // Assemble spinners
    $spinners = h('div', {
      staticClass: 'd-flex align-items-center justify-content-center mx-auto',
      attrs: {
        role: 'group',
        tabindex: disabled || readonly ? null : '-1',
        'aria-labelledby': ariaLabelledby
      },
      on: {
        keydown: this.onSpinLeftRight,
        click: /* istanbul ignore next */function click(event) {
          if (event.target === event.currentTarget) {
            focusHandler();
          }
        }
      }
    }, $spinners);

    // Selected type display
    var $value = h('output', {
      staticClass: 'form-control form-control-sm text-center',
      class: {
        disabled: disabled || readonly
      },
      attrs: {
        id: valueId,
        role: 'status',
        for: spinIds.filter(identity).join(' ') || null,
        tabindex: disabled ? null : '-1',
        'aria-live': this.isLive ? 'polite' : 'off',
        'aria-atomic': 'true'
      },
      on: {
        // Transfer focus/click to focus hours spinner
        click: focusHandler,
        focus: focusHandler
      }
    }, [h('bdi', this.formattedTimeString), this.computedHMS ? h('span', {
      staticClass: 'sr-only'
    }, " (".concat(this.labelSelected, ") ")) : '']);
    var $header = h(this.headerTag, {
      staticClass: 'b-time-header',
      class: {
        'sr-only': this.hideHeader
      }
    }, [$value]);
    var $content = this.normalizeSlot();
    var $footer = $content ? h(this.footerTag, {
      staticClass: 'b-time-footer'
    }, $content) : h();
    return h('div', {
      staticClass: 'b-time d-inline-flex flex-column text-center',
      attrs: {
        role: 'group',
        lang: this.computedLang || null,
        'aria-labelledby': ariaLabelledby || null,
        'aria-disabled': disabled ? 'true' : null,
        'aria-readonly': readonly && !disabled ? 'true' : null
      }
    }, [$header, $spinners, $footer]);
  }
});