<script setup>
import { computed, useId } from "vue";
import { useNamespace } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import { progressProps } from "./progress";
defineOptions({
  name: "YhProgress"
});
const props = defineProps(progressProps);
const ns = useNamespace("progress");
const componentId = useId();
const { themeStyle } = useComponentTheme(
  "progress",
  computed(() => props.themeOverrides)
);
const percentages = computed(() => {
  return Array.isArray(props.percentage) ? props.percentage : [props.percentage];
});
const isGradient = computed(() => {
  if (typeof props.color === "object" && props.color !== null) {
    if (Array.isArray(props.color) && Array.isArray(props.percentage)) {
      return false;
    }
    return true;
  }
  return false;
});
const getGradientStops = computed(() => {
  if (!isGradient.value) return [];
  if (Array.isArray(props.color)) {
    const len = props.color.length;
    return props.color.map((c, i) => ({
      offset: `${len > 1 ? i / (len - 1) * 100 : 0}%`,
      color: c
    }));
  }
  return Object.entries(props.color).map(([offset, color]) => ({
    offset,
    color
  }));
});
const barStyle = computed(() => {
  const style = {
    width: props.indeterminate ? void 0 : `${percentages.value[0]}%`,
    "--yh-progress-duration": `${props.duration}s`
  };
  if (isGradient.value) {
    const stops = getGradientStops.value.map((s) => `${s.color} ${s.offset}`).join(", ");
    style.backgroundImage = `linear-gradient(to right, ${stops})`;
  } else {
    style.backgroundColor = getColor(percentages.value[0], 0);
  }
  return style;
});
const getColor = (percentage, index) => {
  if (typeof props.color === "function") return props.color(percentage);
  if (typeof props.color === "string" && props.color !== "") return props.color;
  if (Array.isArray(props.percentage) && Array.isArray(props.color) && typeof index === "number") {
    return props.color[index] || props.color[props.color.length - 1];
  }
  if (isGradient.value) return `url(#${componentId}-gradient)`;
  return "";
};
const getCircleParams = (index) => {
  const layerGap = 4;
  const layerStrokeWidth = props.strokeWidth;
  const r = props.width / 2 - props.strokeWidth / 2 - index * (layerStrokeWidth + layerGap);
  const p = 2 * Math.PI * r;
  const isDashboard = props.type === "dashboard";
  const rate = isDashboard ? 0.75 : 1;
  const path = `
    M ${props.width / 2} ${props.width / 2}
    m 0 ${isDashboard ? "" : "-"}${r}
    a ${r} ${r} 0 1 1 0 ${isDashboard ? "-" : ""}${r * 2}
    a ${r} ${r} 0 1 1 0 ${isDashboard ? "" : "-"}${r * 2}
  `;
  const offset = -1 * p * (1 - rate) / 2;
  const pathStyle = {
    strokeDasharray: `${p * rate * (percentages.value[index] / 100)}px, ${p}px`,
    strokeDashoffset: `${offset}px`,
    transition: "stroke-dasharray 0.6s ease 0s, stroke 0.6s ease"
  };
  const trackStyle = {
    strokeDasharray: `${p * rate}px, ${p}px`,
    strokeDashoffset: `${offset}px`
  };
  return { path, pathStyle, trackStyle, r, p };
};
const secondaryBarStyle = computed(() => ({
  width: `${props.secondaryPercentage}%`
}));
const content = computed(() => {
  if (props.format) return props.format(percentages.value[0]);
  return `${percentages.value[0]}%`;
});
const statusIconSvg = computed(() => {
  if (props.status === "success") {
    return `<path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm-55.8 560.3L342.3 510.4a32 32 0 1 0-45.2 45.2l136 136a32 32 0 0 0 45.2 0l311.4-311.4a32 32 0 1 0-45.2-45.2L456.2 624.3z" />`;
  }
  if (props.status === "exception") {
    return `<path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm165.4 533.4a32 32 0 0 0 0-45.2L557.3 532l120.1-120.1a32 32 0 0 0-45.2-45.2L512 486.8 391.9 366.7a32 32 0 0 0-45.2 45.2L466.8 532l-120.1 120.1a32 32 0 1 0 45.2 45.2L512 577.2l120.1 120.1a32 32 0 0 0 45.2 0z" />`;
  }
  if (props.status === "warning" || props.status === "info") {
    return `<path fill="currentColor" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 192a48 48 0 1 0 0 96 48 48 0 0 0 0-96zm0 160a32 32 0 0 0-32 32v320a32 32 0 0 0 64 0V448a32 32 0 0 0-32-32z" />`;
  }
  return "";
});
const progressClass = computed(() => [
  ns.b(),
  ns.m(props.type),
  ns.is(props.status, !!props.status),
  ns.is("without-text", !props.showText || props.indeterminate),
  ns.is("text-inside", props.textInside),
  ns.is("animated", props.animated)
]);
</script>

<template>
  <div
    :class="progressClass"
    :style="themeStyle"
    role="progressbar"
    :aria-valuenow="percentages[0]"
    aria-valuemin="0"
    aria-valuemax="100"
  >
    <!-- 线型进度条 -->
    <div v-if="type === 'line'" :class="ns.e('bar')">
      <div
        :class="ns.e('outer')"
        :style="{
  height: `${strokeWidth}px`,
  backgroundColor: defineBackgroundColor
}"
      >
        <div
          v-if="secondaryPercentage > 0"
          :class="ns.e('secondary-inner')"
          :style="secondaryBarStyle"
        ></div>
        <div
          :class="[ns.e('inner'), ns.is('indeterminate', indeterminate), ns.is('striped', striped), ns.is('striped-flow', stripedFlow)]"
          :style="barStyle"
        >
          <div v-if="showText && textInside && !indeterminate" :class="ns.e('innerText')">
            <slot name="default" :percentage="percentages[0]">
              <i v-if="icon" :class="icon"></i>
              <!-- eslint-disable vue/no-v-html -->
              <svg
                v-else-if="statusIconSvg"
                viewBox="0 0 1024 1024"
                width="16"
                height="16"
                style="vertical-align: middle"
                v-html="statusIconSvg"
              ></svg>
              <!-- eslint-enable vue/no-v-html -->
              <span v-else>{{ content }}</span>
            </slot>
          </div>
        </div>
        <div v-if="steps > 0" :class="ns.e('steps')">
          <div
            v-for="step in steps"
            :key="step"
            :class="ns.e('step-item')"
            :style="{
  left: `${100 / steps * step}%`
}"
          ></div>
        </div>
      </div>
    </div>

    <!-- 环形/仪表盘进度条 -->
    <div v-else :class="ns.e('circle')" :style="{
  height: `${width}px`,
  width: `${width}px`
}">
      <svg :viewBox="`0 0 ${width} ${width}`" :class="ns.e('svg')">
        <!-- 定义渐变 -->
        <defs v-if="isGradient">
          <linearGradient :id="`${componentId}-gradient`" x1="0%" y1="0%" x2="100%" y2="0%">
            <stop
              v-for="(stop, i) in getGradientStops"
              :key="i"
              :offset="stop.offset"
              :stop-color="stop.color"
            />
          </linearGradient>
        </defs>

        <!-- 循环渲染多环 -->
        <g v-for="(p, index) in percentages" :key="index">
          <path
            :class="ns.e('circle-track')"
            :d="getCircleParams(index).path"
            :stroke="defineBackgroundColor || '#e5e9f2'"
            :stroke-width="strokeWidth"
            fill="none"
            :stroke-linecap="strokeLinecap"
            :style="getCircleParams(index).trackStyle"
          />
          <path
            :class="ns.e('circle-path')"
            :d="getCircleParams(index).path"
            :stroke="getColor(p, index)"
            fill="none"
            :stroke-linecap="strokeLinecap"
            :stroke-width="p > 0 ? strokeWidth : 0"
            :style="getCircleParams(index).pathStyle"
          />
        </g>
      </svg>

      <!-- 环型中心内容区 -->
      <div
        v-if="showText && !indeterminate"
        :class="ns.e('text')"
        :style="{
  fontSize: `${width * 0.16}px`
}"
      >
        <slot name="default" :percentage="percentages[0]">
          <i
            v-if="icon"
            :class="[icon, ns.e('icon')]"
            :style="{
  color: getColor(percentages[0], 0)
}"
          ></i>
          <!-- eslint-disable vue/no-v-html -->
          <svg
            v-else-if="statusIconSvg"
            viewBox="0 0 1024 1024"
            :width="width * 0.22"
            :height="width * 0.22"
            :style="{
  color: getColor(percentages[0], 0),
  verticalAlign: 'middle'
}"
            v-html="statusIconSvg"
          ></svg>
          <!-- eslint-enable vue/no-v-html -->
          <span v-else>{{ content }}</span>
        </slot>
      </div>
    </div>

    <!-- 外部文字区 -->
    <div
      v-if="type === 'line' && showText && !textInside && !indeterminate"
      :class="ns.e('text')"
      :style="{
  fontSize: `${Math.max(12, strokeWidth * 0.8)}px`
}"
    >
      <slot name="default" :percentage="percentages[0]">
        <i
          v-if="icon"
          :class="[icon, ns.e('icon')]"
          :style="{
  color: getColor(percentages[0], 0)
}"
        ></i>
        <!-- eslint-disable vue/no-v-html -->
        <svg
          v-else-if="statusIconSvg"
          viewBox="0 0 1024 1024"
          width="16"
          height="16"
          :style="{
  color: getColor(percentages[0], 0),
  verticalAlign: 'middle'
}"
          v-html="statusIconSvg"
        ></svg>
        <!-- eslint-enable vue/no-v-html -->
        <span v-else>{{ content }}</span>
      </slot>
    </div>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-progress {
  position: relative;
  line-height: 1;
  display: flex;
  align-items: center;
}
.yh-progress__text {
  font-size: 14px;
  color: var(--yh-text-color-regular);
  margin-left: 10px;
  min-width: 40px;
  flex-shrink: 0;
}

.yh-progress {
  /* 线型布局 */
}
.yh-progress--line {
  width: 100%;
}
.yh-progress--line.is-without-text .yh-progress__bar {
  padding-right: 0;
  margin-right: 0;
}

.yh-progress--line.is-without-text .yh-progress__text {
  display: none;
}

.yh-progress--line.is-text-inside .yh-progress__bar {
  padding-right: 0;
  margin-right: 0;
}

.yh-progress__bar {
  flex: 1;
  margin-right: -45px;
  padding-right: 45px;
  box-sizing: border-box;
}

.yh-progress__outer {
  width: 100%;
  background-color: var(--yh-fill-color-darker);
  border-radius: 100px;
  overflow: hidden;
  position: relative;
  vertical-align: middle;
}

.yh-progress__inner {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  background-color: var(--yh-color-primary);
  border-radius: 100px;
  transition: width 0.6s ease;
  text-align: right;
  line-height: normal;
  white-space: nowrap;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  z-index: 1;
  /* 条纹模式 */
}
.yh-progress__inner.is-striped {
  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
  background-size: 40px 40px;
}
.yh-progress__inner {
  /* 条纹流动动画 (自创极致优化) */
}
.yh-progress__inner.is-striped-flow {
  animation-name: yh-progress-stripes;
  animation-duration: var(--yh-progress-duration, 2s);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.yh-progress__inner {
  /* 未确定状态 (左右滑动) */
}
.yh-progress__inner.is-indeterminate {
  animation-name: yh-progress-indeterminate;
  animation-duration: var(--yh-progress-duration, 3s);
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

.yh-progress__secondary-inner {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  background-color: var(--yh-fill-color-dark);
  opacity: 0.5;
  border-radius: 100px;
  transition: width 0.6s ease;
}

.yh-progress__innerText {
  display: inline-block;
  vertical-align: middle;
  color: #fff;
  font-size: 12px;
  margin: 0 5px;
}

.yh-progress__steps {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: 2;
}

.yh-progress__step-item {
  position: absolute;
  top: 0;
  width: 2px;
  height: 100%;
  background: var(--yh-bg-color, #fff);
  transform: translateX(-50%);
  opacity: 0.3;
}

.yh-progress {
  /* 环型/仪表盘 */
}
.yh-progress--circle {
  display: inline-block;
}
.yh-progress--circle .yh-progress__text {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  text-align: center;
  margin-left: 0;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
}

.yh-progress--dashboard {
  display: inline-block;
}
.yh-progress--dashboard .yh-progress__text {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  text-align: center;
  margin-left: 0;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
}

.yh-progress__circle {
  position: relative;
}

.yh-progress__svg {
  display: block;
  width: 100%;
  height: 100%;
}

.yh-progress__icon {
  vertical-align: middle;
  font-size: 1.2em;
}

.yh-progress {
  /* 动画效果 (Animated) */
}
.yh-progress.is-animated .yh-progress__circle-path {
  animation: yh-progress-rotate var(--yh-progress-duration, 3s) linear infinite;
  transform-origin: center;
}

.yh-progress.is-animated .yh-progress__text {
  animation: yh-progress-pulse 2s ease-in-out infinite;
}

.yh-progress {
  /* 语义化状态颜色覆盖 */
}
.yh-progress.is-success .yh-progress__inner {
  background-color: var(--yh-color-success);
}

.yh-progress.is-success .yh-progress__circle-path {
  stroke: var(--yh-color-success);
}

.yh-progress.is-success .yh-progress__text {
  color: var(--yh-color-success);
}

.yh-progress.is-exception .yh-progress__inner {
  background-color: var(--yh-color-danger);
}

.yh-progress.is-exception .yh-progress__circle-path {
  stroke: var(--yh-color-danger);
}

.yh-progress.is-exception .yh-progress__text {
  color: var(--yh-color-danger);
}

.yh-progress.is-warning .yh-progress__inner {
  background-color: var(--yh-color-warning);
}

.yh-progress.is-warning .yh-progress__circle-path {
  stroke: var(--yh-color-warning);
}

.yh-progress.is-warning .yh-progress__text {
  color: var(--yh-color-warning);
}

/* 动效关键帧 */
@keyframes yh-progress-stripes {
  from {
    background-position: 40px 0;
  }
  to {
    background-position: 0 0;
  }
}
@keyframes yh-progress-indeterminate {
  0% {
    left: -35%;
    width: 35%;
  }
  100% {
    left: 100%;
    width: 35%;
  }
}
@keyframes yh-progress-rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@keyframes yh-progress-pulse {
  0% {
    opacity: 1;
    transform: translateY(-50%) scale(1);
  }
  50% {
    opacity: 0.8;
    transform: translateY(-50%) scale(1.05);
  }
  100% {
    opacity: 1;
    transform: translateY(-50%) scale(1);
  }
}
</style>
