{"version":3,"file":"video-progress.vue2.mjs","sources":["../../../components/video/video-progress.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ResizeObserver } from '@/components/resize-observer'\nimport { Slider } from '@/components/slider'\n\nimport { computed, ref } from 'vue'\n\nimport { getStepByWord, useLocale, useNameHelper } from '@vexip-ui/config'\nimport { useListener, useSetTimeout } from '@vexip-ui/hooks'\nimport { boundRange, throttle } from '@vexip-ui/utils'\nimport { formatSeconds } from './helper'\n\nimport type { PropType } from 'vue'\nimport type { SliderExposed } from '@/components/slider'\nimport type { VideoSegment } from './symbol'\n\ninterface PointState {\n  start: number,\n  startPercent: number,\n  end: number,\n  endPercent: number,\n  duration: number,\n  durationPercent: number,\n  width: number\n}\n\ndefineOptions({ name: 'VideoProgress' })\n\nconst props = defineProps({\n  time: {\n    type: Number,\n    default: 0\n  },\n  duration: {\n    type: Number,\n    default: 0\n  },\n  segments: {\n    type: Array as PropType<VideoSegment[]>,\n    default: () => []\n  },\n  noPreview: {\n    type: Boolean,\n    default: false\n  },\n  previewSrc: {\n    type: String,\n    default: ''\n  }\n})\n\nconst emit = defineEmits(['change'])\n\nconst nh = useNameHelper('video')\nconst locale = useLocale('video')\n\nconst { timer } = useSetTimeout()\n\nconst slidTime = ref(0)\nconst hovered = ref(false)\nconst hoveredTime = ref(0)\nconst indicatorLeft = ref(0)\nconst previewLeft = ref(0)\n\nlet paddingX = [0, 0]\nlet sliderWidth = 100\nlet previewWidth = 60\n\nconst wrapper = ref<HTMLElement>()\nconst slider = ref<SliderExposed>()\nconst sliderEl = computed(() => slider.value?.$el as HTMLElement | undefined)\nconst preview = ref<HTMLElement>()\n\nconst sliding = computed(() => !!slider.value?.sliding[1])\nconst percent = computed(() => {\n  return props.duration ? ((sliding.value ? slidTime.value : props.time) / props.duration) * 100 : 0\n})\nconst className = computed(() => {\n  return {\n    [nh.be('progress')]: true,\n    [nh.bem('progress', 'sliding')]: sliding.value,\n    [nh.bem('progress', 'disabled')]: props.duration <= 0\n  }\n})\nconst points = computed<PointState[]>(() => {\n  const duration = Math.max(1, props.duration)\n\n  let times = props.segments.map(segment => segment.time)\n\n  if (!times.length) times = [0, duration]\n\n  times = times.at(-1) === duration ? times : [...times, duration]\n  times = times[0] === 0 ? times : [0, ...times]\n\n  const points: PointState[] = []\n\n  for (let i = 0, len = times.length - 1; i < len; ++i) {\n    const start = times[i]\n    const end = times[i + 1]\n    const pointDuration = end - start\n\n    points.push({\n      start,\n      startPercent: (start / duration) * 100,\n      end,\n      endPercent: (end / duration) * 100,\n      duration: pointDuration,\n      durationPercent: (pointDuration / duration) * 100,\n      width: ((end - start) / duration) * 100\n    })\n  }\n\n  return points\n})\nconst segmentLabel = computed(() => {\n  const time = hoveredTime.value\n  const segments = props.segments\n\n  if (!segments.length) return ''\n\n  let index = -1\n\n  if (time <= 0) {\n    index = 0\n  } else {\n    for (let i = 1, len = segments.length; i < len; ++i) {\n      if (segments[i].time > time) {\n        index = i - 1\n        break\n      }\n    }\n  }\n\n  if (index < 0) {\n    index = segments.length - 1\n  }\n\n  const title = segments[index]?.title || getStepByWord(locale.value.chapterCount, index + 1)\n\n  return title && ` (${title})`\n})\n\nuseListener(sliderEl, 'pointerenter', () => {\n  clearTimeout(timer.hover)\n\n  timer.hover = setTimeout(() => {\n    hovered.value = true\n  }, 100)\n})\nuseListener(sliderEl, 'pointerleave', () => {\n  clearTimeout(timer.hover)\n\n  timer.hover = setTimeout(() => {\n    hovered.value = false\n  }, 100)\n})\nuseListener(\n  sliderEl,\n  'pointermove',\n  throttle((event: PointerEvent) => {\n    if (!sliding.value) {\n      processMoveOnTrack(event)\n    }\n  })\n)\n\nfunction processMoveOnTrack(event: PointerEvent) {\n  if (!sliderEl.value) return\n\n  const offsetX = boundRange(\n    event.clientX - sliderEl.value.getBoundingClientRect().left,\n    0,\n    sliderWidth\n  )\n\n  hoveredTime.value = (offsetX / sliderWidth) * props.duration\n  indicatorLeft.value = offsetX + paddingX[0]\n  previewLeft.value = boundRange(\n    offsetX - previewWidth * 0.5 + paddingX[0],\n    0,\n    sliderWidth - previewWidth + paddingX[0] + paddingX[1]\n  )\n}\n\nfunction onSliderResize(entry: ResizeObserverEntry) {\n  if (!wrapper.value) return\n\n  const style = getComputedStyle(wrapper.value)\n\n  paddingX = [parseFloat(style.paddingLeft), parseFloat(style.paddingRight)]\n  sliderWidth = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width\n}\n\nfunction onPreviewResize(entry: ResizeObserverEntry) {\n  previewWidth = entry.borderBoxSize?.[0]?.inlineSize ?? entry.contentRect.width\n}\n\nfunction handleChange(permillage: number) {\n  emit('change', (permillage / 1000) * props.duration)\n}\n\nconst onSlideMove = throttle(processMoveOnTrack)\n\nfunction onSlideStart() {\n  slidTime.value = props.time\n\n  document.addEventListener('pointermove', onSlideMove)\n  document.addEventListener('pointerup', onSlideEnd)\n}\n\nfunction onSlideEnd() {\n  document.removeEventListener('pointermove', onSlideMove)\n  document.removeEventListener('pointerup', onSlideEnd)\n}\n</script>\n\n<template>\n  <div ref=\"wrapper\" :class=\"className\">\n    <ResizeObserver :on-resize=\"onSliderResize\">\n      <Slider\n        ref=\"slider\"\n        :class=\"nh.be('progress-slider')\"\n        :value=\"percent * 10\"\n        :max=\"1000\"\n        :vertical=\"false\"\n        :range=\"false\"\n        hide-tip\n        trigger-fade\n        flip-marker\n        :disabled=\"duration <= 0\"\n        @change=\"handleChange\"\n        @pointerdown=\"onSlideStart\"\n      >\n        <template #filler=\"state\">\n          <div\n            v-for=\"(point, index) in points\"\n            :key=\"index\"\n            :class=\"nh.be('progress-segment')\"\n            :style=\"{ width: `${point.width}%` }\"\n          >\n            <div :class=\"nh.be('progress-track')\">\n              <div\n                :class=\"nh.be('progress-filler')\"\n                :style=\"{\n                  visibility: state.percent[1] < point.startPercent ? 'hidden' : undefined,\n                  transform: `translateX(${Math.min(\n                    (Math.max(state.percent[1] - point.startPercent, 0) / point.durationPercent) *\n                      100 -\n                      100,\n                    0\n                  )}%) translateZ(0)`\n                }\"\n              ></div>\n            </div>\n          </div>\n        </template>\n        <template #trigger>\n          <slot name=\"trigger\">\n            <div :class=\"nh.be('progress-trigger')\"></div>\n          </slot>\n        </template>\n      </Slider>\n    </ResizeObserver>\n    <div\n      :class=\"{\n        [nh.be('progress-indicator')]: true,\n        [nh.bem('progress-indicator', 'active')]: hovered && !sliding\n      }\"\n      :style=\"{ transform: `translateX(${indicatorLeft}px) translateZ(0)` }\"\n    ></div>\n    <ResizeObserver v-if=\"!noPreview\" :on-resize=\"onPreviewResize\">\n      <div\n        ref=\"preview\"\n        :class=\"{\n          [nh.be('preview')]: true,\n          [nh.bem('preview', 'has-image')]: previewSrc,\n          [nh.bem('preview', 'active')]: hovered || sliding\n        }\"\n        :style=\"{ transform: `translateX(${previewLeft}px) translateZ(0)` }\"\n      >\n        <slot name=\"preview\">\n          <div v-if=\"previewSrc\" :class=\"nh.be('preview-image')\">\n            <img :src=\"previewSrc\" />\n          </div>\n          <div :class=\"nh.be('preview-time')\">\n            {{ formatSeconds(hoveredTime) + segmentLabel }}\n          </div>\n        </slot>\n      </div>\n    </ResizeObserver>\n  </div>\n</template>\n"],"names":["props","__props","emit","__emit","nh","useNameHelper","locale","useLocale","timer","useSetTimeout","slidTime","ref","hovered","hoveredTime","indicatorLeft","previewLeft","paddingX","sliderWidth","previewWidth","wrapper","slider","sliderEl","computed","_a","preview","sliding","percent","className","points","duration","times","segment","i","len","start","end","pointDuration","segmentLabel","time","segments","index","title","getStepByWord","useListener","throttle","event","processMoveOnTrack","offsetX","boundRange","onSliderResize","entry","style","_b","onPreviewResize","handleChange","permillage","onSlideMove","onSlideStart","onSlideEnd"],"mappings":"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"}