{"version":3,"file":"basic-time-spinner.mjs","names":[],"sources":["../../../../../../../packages/components/time-picker/src/time-picker-com/basic-time-spinner.vue"],"sourcesContent":["<template>\n  <div :class=\"[ns.b('spinner'), { 'has-seconds': showSeconds }]\">\n    <template v-if=\"!arrowControl\">\n      <el-scrollbar\n        v-for=\"item in spinnerItems\"\n        :key=\"item\"\n        :ref=\"(scrollbar: unknown) => setRef(scrollbar as any, item)\"\n        :class=\"ns.be('spinner', 'wrapper')\"\n        wrap-style=\"max-height: inherit;\"\n        :view-class=\"ns.be('spinner', 'list')\"\n        noresize\n        tag=\"ul\"\n        @mouseenter=\"emitSelectRange(item)\"\n        @mousemove=\"adjustCurrentSpinner(item)\"\n      >\n        <li\n          v-for=\"(disabled, key) in timeList[item]\"\n          :key=\"key\"\n          :class=\"[\n            ns.be('spinner', 'item'),\n            ns.is('active', key === timePartials[item]),\n            ns.is('disabled', disabled),\n          ]\"\n          @click=\"handleClick(item, { value: key, disabled })\"\n        >\n          <template v-if=\"item === 'hours'\">\n            {{ ('0' + (amPmMode ? key % 12 || 12 : key)).slice(-2)\n            }}{{ getAmPmFlag(key) }}\n          </template>\n          <template v-else>\n            {{ ('0' + key).slice(-2) }}\n          </template>\n        </li>\n      </el-scrollbar>\n    </template>\n    <template v-if=\"arrowControl\">\n      <div\n        v-for=\"item in spinnerItems\"\n        :key=\"item\"\n        :class=\"[ns.be('spinner', 'wrapper'), ns.is('arrow')]\"\n        @mouseenter=\"emitSelectRange(item)\"\n      >\n        <el-icon\n          v-repeat-click=\"onDecrement\"\n          :class=\"['arrow-up', ns.be('spinner', 'arrow')]\"\n        >\n          <arrow-up />\n        </el-icon>\n        <el-icon\n          v-repeat-click=\"onIncrement\"\n          :class=\"['arrow-down', ns.be('spinner', 'arrow')]\"\n        >\n          <arrow-down />\n        </el-icon>\n        <ul :class=\"ns.be('spinner', 'list')\">\n          <li\n            v-for=\"(time, key) in arrowControlTimeList[item]\"\n            :key=\"key\"\n            :class=\"[\n              ns.be('spinner', 'item'),\n              ns.is('active', time === timePartials[item]),\n              ns.is('disabled', timeList[item][time!]),\n            ]\"\n          >\n            <template v-if=\"isNumber(time)\">\n              <template v-if=\"item === 'hours'\">\n                {{ ('0' + (amPmMode ? time % 12 || 12 : time)).slice(-2)\n                }}{{ getAmPmFlag(time) }}\n              </template>\n              <template v-else>\n                {{ ('0' + time).slice(-2) }}\n              </template>\n            </template>\n          </li>\n        </ul>\n      </div>\n    </template>\n  </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, inject, nextTick, onMounted, ref, unref, watch } from 'vue'\nimport { debounce } from 'lodash-unified'\nimport { vRepeatClick } from '@element-plus/directives'\nimport ElScrollbar from '@element-plus/components/scrollbar'\nimport ElIcon from '@element-plus/components/icon'\nimport { ArrowDown, ArrowUp } from '@element-plus/icons-vue'\nimport { useNamespace } from '@element-plus/hooks'\nimport { getStyle, isNumber, rAF } from '@element-plus/utils'\nimport { CHANGE_EVENT } from '@element-plus/constants'\nimport {\n  DEFAULT_FORMATS_TIME,\n  PICKER_BASE_INJECTION_KEY,\n  timeUnits,\n} from '../constants'\nimport { buildTimeList } from '../utils'\nimport { basicTimeSpinnerProps } from '../props/basic-time-spinner'\nimport { getTimeLists } from '../composables/use-time-picker'\n\nimport type { Ref } from 'vue'\nimport type { ScrollbarInstance } from '@element-plus/components/scrollbar'\nimport type { TimeUnit } from '../constants'\nimport type { TimeList } from '../utils'\n\nconst props = defineProps(basicTimeSpinnerProps)\nconst pickerBase = inject(PICKER_BASE_INJECTION_KEY) as any\nconst { isRange, format, saveOnBlur } = pickerBase.props\nconst emit = defineEmits([CHANGE_EVENT, 'select-range', 'set-option'])\n\nconst ns = useNamespace('time')\n\nconst { getHoursList, getMinutesList, getSecondsList } = getTimeLists(\n  props.disabledHours,\n  props.disabledMinutes,\n  props.disabledSeconds\n)\n\n// data\nlet isScrolling = false\nconst ignoreScroll = {\n  hours: false,\n  minutes: false,\n  seconds: false,\n}\n\nconst currentScrollbar = ref<TimeUnit>()\nconst listHoursRef = ref<ScrollbarInstance>()\nconst listMinutesRef = ref<ScrollbarInstance>()\nconst listSecondsRef = ref<ScrollbarInstance>()\nconst listRefsMap: Record<TimeUnit, Ref<ScrollbarInstance | undefined>> = {\n  hours: listHoursRef,\n  minutes: listMinutesRef,\n  seconds: listSecondsRef,\n}\n\n// computed\nconst spinnerItems = computed(() => {\n  return props.showSeconds ? timeUnits : timeUnits.slice(0, 2)\n})\n\nconst timePartials = computed<Record<TimeUnit, number>>(() => {\n  const { spinnerDate } = props\n  const hours = spinnerDate.hour()\n  const minutes = spinnerDate.minute()\n  const seconds = spinnerDate.second()\n  return { hours, minutes, seconds }\n})\n\nconst timeList = computed(() => {\n  const { hours, minutes } = unref(timePartials)\n  const { role, spinnerDate } = props\n  const compare = !isRange ? spinnerDate : undefined\n  return {\n    hours: getHoursList(role, compare),\n    minutes: getMinutesList(hours, role, compare),\n    seconds: getSecondsList(hours, minutes, role, compare),\n  }\n})\n\nconst arrowControlTimeList = computed<Record<TimeUnit, TimeList>>(() => {\n  const { hours, minutes, seconds } = unref(timePartials)\n\n  return {\n    hours: buildTimeList(hours, 23),\n    minutes: buildTimeList(minutes, 59),\n    seconds: buildTimeList(seconds, 59),\n  }\n})\n\nconst debouncedResetScroll = debounce((type) => {\n  isScrolling = false\n  adjustCurrentSpinner(type)\n}, 200)\n\nconst getAmPmFlag = (hour: number) => {\n  const shouldShowAmPm = !!props.amPmMode\n  if (!shouldShowAmPm) return ''\n  const isCapital = props.amPmMode === 'A'\n  // todo locale\n  let content = hour < 12 ? ' am' : ' pm'\n  if (isCapital) content = content.toUpperCase()\n  return content\n}\n\nconst emitSelectRange = (type: TimeUnit) => {\n  let range = [0, 0]\n  const actualFormat = format || DEFAULT_FORMATS_TIME\n  const hourIndex = actualFormat.indexOf('HH')\n  const minuteIndex = actualFormat.indexOf('mm')\n  const secondIndex = actualFormat.indexOf('ss')\n  switch (type) {\n    case 'hours':\n      if (hourIndex !== -1) {\n        range = [hourIndex, hourIndex + 2]\n      }\n      break\n    case 'minutes':\n      if (minuteIndex !== -1) {\n        range = [minuteIndex, minuteIndex + 2]\n      }\n      break\n    case 'seconds':\n      if (secondIndex !== -1) {\n        range = [secondIndex, secondIndex + 2]\n      }\n      break\n  }\n  const [left, right] = range\n\n  emit('select-range', left, right)\n  currentScrollbar.value = type\n}\n\nconst adjustCurrentSpinner = (type: TimeUnit) => {\n  adjustSpinner(type, unref(timePartials)[type])\n}\n\nconst adjustSpinners = () => {\n  adjustCurrentSpinner('hours')\n  adjustCurrentSpinner('minutes')\n  adjustCurrentSpinner('seconds')\n}\n\nconst getScrollbarElement = (el: HTMLElement) =>\n  el.querySelector(`.${ns.namespace.value}-scrollbar__wrap`) as HTMLElement\n\nconst adjustSpinner = (type: TimeUnit, value: number) => {\n  if (props.arrowControl) return\n  const scrollbar = unref(listRefsMap[type])\n  if (scrollbar && scrollbar.$el) {\n    if (!saveOnBlur) {\n      ignoreScroll[type] = true\n      rAF(() => {\n        ignoreScroll[type] = false\n      })\n    }\n    getScrollbarElement(scrollbar.$el).scrollTop = Math.max(\n      0,\n      value * typeItemHeight(type)\n    )\n  }\n}\n\nconst typeItemHeight = (type: TimeUnit): number => {\n  const scrollbar = unref(listRefsMap[type])\n  const listItem = scrollbar?.$el.querySelector('li')\n  if (listItem) {\n    return Number.parseFloat(getStyle(listItem, 'height')) || 0\n  }\n  return 0\n}\n\nconst onIncrement = () => {\n  scrollDown(1)\n}\n\nconst onDecrement = () => {\n  scrollDown(-1)\n}\n\nconst scrollDown = (step: number) => {\n  if (!currentScrollbar.value) {\n    emitSelectRange('hours')\n  }\n\n  const label = currentScrollbar.value!\n  const now = unref(timePartials)[label]\n  const total = currentScrollbar.value === 'hours' ? 24 : 60\n  const next = findNextUnDisabled(label, now, step, total)\n\n  modifyDateField(label, next)\n  adjustSpinner(label, next)\n  nextTick(() => emitSelectRange(label))\n}\n\nconst findNextUnDisabled = (\n  type: TimeUnit,\n  now: number,\n  step: number,\n  total: number\n) => {\n  let next = (now + step + total) % total\n  const list = unref(timeList)[type]\n  while (list[next] && next !== now) {\n    next = (next + step + total) % total\n  }\n  return next\n}\n\nconst modifyDateField = (type: TimeUnit, value: number) => {\n  const list = unref(timeList)[type]\n  const isDisabled = list[value]\n  if (isDisabled) return\n\n  const { hours, minutes, seconds } = unref(timePartials)\n\n  let changeTo\n  switch (type) {\n    case 'hours':\n      changeTo = props.spinnerDate.hour(value).minute(minutes).second(seconds)\n      break\n    case 'minutes':\n      changeTo = props.spinnerDate.hour(hours).minute(value).second(seconds)\n      break\n    case 'seconds':\n      changeTo = props.spinnerDate.hour(hours).minute(minutes).second(value)\n      break\n  }\n  emit(CHANGE_EVENT, changeTo)\n}\n\nconst handleClick = (\n  type: TimeUnit,\n  { value, disabled }: { value: number; disabled: boolean }\n) => {\n  if (!disabled) {\n    modifyDateField(type, value)\n    emitSelectRange(type)\n    adjustSpinner(type, value)\n  }\n}\n\nconst handleScroll = (type: TimeUnit) => {\n  if (!saveOnBlur && ignoreScroll[type]) return\n  const scrollbar = unref(listRefsMap[type])\n  if (!scrollbar) return\n\n  isScrolling = true\n  debouncedResetScroll(type)\n  const value = Math.min(\n    Math.round(\n      (getScrollbarElement(scrollbar.$el).scrollTop -\n        (scrollBarHeight(type) * 0.5 - 10) / typeItemHeight(type) +\n        3) /\n        typeItemHeight(type)\n    ),\n    type === 'hours' ? 23 : 59\n  )\n  modifyDateField(type, value)\n}\n\nconst scrollBarHeight = (type: TimeUnit) => {\n  return unref(listRefsMap[type])!.$el.offsetHeight\n}\n\nconst bindScrollEvent = () => {\n  const bindFunction = (type: TimeUnit) => {\n    const scrollbar = unref(listRefsMap[type])\n    if (scrollbar && scrollbar.$el) {\n      getScrollbarElement(scrollbar.$el).onscroll = () => {\n        // TODO: scroll is emitted when set scrollTop programmatically\n        // should find better solutions in the future!\n        handleScroll(type)\n      }\n    }\n  }\n  bindFunction('hours')\n  bindFunction('minutes')\n  bindFunction('seconds')\n}\n\nonMounted(() => {\n  nextTick(() => {\n    !props.arrowControl && bindScrollEvent()\n    adjustSpinners()\n    // set selection on the first hour part\n    if (props.role === 'start') emitSelectRange('hours')\n  })\n})\n\nconst setRef = (scrollbar: ScrollbarInstance | null, type: TimeUnit) => {\n  listRefsMap[type].value = scrollbar ?? undefined\n}\n\nemit('set-option', [`${props.role}_scrollDown`, scrollDown])\nemit('set-option', [`${props.role}_emitSelectRange`, emitSelectRange])\n\nwatch(\n  () => props.spinnerDate,\n  () => {\n    if (isScrolling) return\n    adjustSpinners()\n  }\n)\n</script>\n"],"mappings":""}