<!-- 视频时长选择器，负责在模型允许范围内选择生成时长。 -->
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { ChevronDown } from "@/icons";

const props = withDefaults(
  defineProps<{ modelValue: number; min?: number; max?: number; compact?: boolean; placement?: string }>(),
  {
    min: 5,
    max: 15,
    compact: false,
    placement: "top-start",
  },
);
const emit = defineEmits<{ "update:modelValue": [value: number]; change: [value: number] }>();
const current = ref(props.modelValue);
const seconds = computed(() => Array.from({ length: props.max - props.min + 1 }, (_, index) => props.min + index));
watch(
  () => props.modelValue,
  (value) => {
    current.value = value;
  },
);
/** 更新本地时长并同步双向绑定值。 */
function update(value: number) {
  current.value = value;
  emit("update:modelValue", value);
}
/** 提交已确认的时长变更事件。 */
function commit(value: number) {
  current.value = value;
  emit("change", value);
}
/** 选择并立即提交指定时长。 */
function select(value: number) {
  update(value);
  commit(value);
}
/** 计算时长刻度在滑轨中的百分比位置。 */
function position(value: number) {
  return `${((value - props.min) / Math.max(1, props.max - props.min)) * 100}%`;
}
</script>

<template>
  <el-popover
    :placement="placement"
    trigger="click"
    :width="260"
    :show-arrow="false"
    popper-class="duration-popper"
  >
    <template #reference>
      <button
        class="duration-trigger"
        :class="{ compact }"
        type="button"
        @click.stop
      >
        <span>{{ current }}{{ compact ? "s" : " 秒" }}</span
        ><ChevronDown :size="15" />
      </button>
    </template>
    <div class="duration-popover-content">
      <div class="duration-range-head">
        <span>{{ min }} 秒</span><strong>{{ current }} 秒</strong><span>{{ max }} 秒</span>
      </div>
      <div class="duration-slider-wrap">
        <el-slider
          :model-value="current"
          :min="min"
          :max="max"
          :step="1"
          :format-tooltip="(value) => `${value} 秒`"
          @update:model-value="update"
          @change="commit"
        />
        <div class="duration-nodes">
          <button
            v-for="second in seconds"
            :key="second"
            type="button"
            :class="{ active: current === second }"
            :style="{ left: position(second) }"
            :title="`选择 ${second} 秒`"
            @click="select(second)"
          >
            <span>{{ second }} 秒</span>
          </button>
        </div>
      </div>
    </div>
  </el-popover>
</template>

<style scoped>
.duration-trigger {
  width: 100%;
  height: 38px;
  padding: 0 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  color: var(--web-text);
  background: var(--web-surface-soft);
  border: 1px solid var(--web-line-strong);
  border-radius: 7px;
  font-family: inherit;
  cursor: pointer;
}
.duration-trigger:hover,
.duration-trigger:focus-visible {
  background: var(--web-surface);
  border-color: var(--web-timeline-focus-line);
  outline: none;
}
.duration-trigger span {
  overflow: hidden;
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.duration-trigger svg {
  flex: 0 0 auto;
  color: var(--web-control-muted);
}
.duration-trigger.compact {
  width: 72px;
  height: 34px;
  color: var(--web-accent-dark);
  background: var(--web-surface);
  border-color: var(--web-line-emphasis);
}
.duration-trigger.compact:hover,
.duration-trigger.compact:focus-visible {
  border-color: var(--web-project-card-hover-line);
}
:global(.duration-popper.el-popper) {
  padding: 10px 14px 12px;
  background: var(--web-surface);
  border: 1px solid var(--web-line-strong);
  border-radius: 9px;
  box-shadow: var(--web-shadow-duration);
}
:global(.duration-range-head) {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  margin-bottom: 2px;
  color: var(--web-text-tertiary);
  font-size: 10px;
}
:global(.duration-range-head strong) {
  color: var(--web-accent-dark);
  font-size: 12px;
  text-align: center;
}
:global(.duration-range-head span:last-child) {
  text-align: right;
}
:global(.duration-slider-wrap) {
  position: relative;
  height: 28px;
  margin: 0 7px;
}
:global(.duration-slider-wrap .el-slider) {
  position: relative;
  z-index: 2;
  height: 24px;
}
:global(.duration-slider-wrap .el-slider__runway) {
  width: calc(100% - 8px);
  height: 6px;
  margin-right: 4px;
  margin-left: 4px;
  background: var(--web-hover);
  border-radius: 0;
}
:global(.duration-slider-wrap .el-slider__bar) {
  height: 6px;
  background: var(--web-primary);
  border-radius: 0;
}
:global(.duration-slider-wrap .el-slider__button-wrapper) {
  top: -15px;
  z-index: 5;
}
:global(.duration-slider-wrap .el-slider__button) {
  width: 18px;
  height: 18px;
  background: var(--web-primary);
  border: 3px solid var(--web-line-emphasis);
}
:global(.duration-nodes) {
  position: absolute;
  top: 0;
  right: 4px;
  left: 4px;
  z-index: 3;
  height: 24px;
  pointer-events: none;
}
:global(.duration-nodes button) {
  position: absolute;
  top: 9px;
  width: 6px;
  height: 6px;
  padding: 0;
  transform: translateX(-50%);
  background: var(--web-divider-soft);
  border: 0;
  border-radius: 50%;
  pointer-events: auto;
  cursor: pointer;
}
:global(.duration-nodes button.active) {
  background: var(--web-primary);
}
:global(.duration-nodes button span) {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  padding: 4px 7px;
  transform: translateX(-50%);
  color: var(--web-text);
  background: var(--web-hover);
  border-radius: 5px;
  font-size: 10px;
  line-height: 1;
  white-space: nowrap;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
}
:global(.duration-nodes button span::after) {
  position: absolute;
  top: 100%;
  left: 50%;
  width: 6px;
  height: 6px;
  transform: translate(-50%, -50%) rotate(45deg);
  content: "";
  background: var(--web-hover);
}
:global(.duration-nodes button:hover span),
:global(.duration-nodes button:focus-visible span) {
  visibility: visible;
  opacity: 1;
}
</style>
