<script setup>
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from "vue";
import { useNamespace, useLocale } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
defineOptions({
  name: "YhWaterfall"
});
const props = defineProps({
  items: { type: Array, required: false, default: () => [] },
  cols: { type: [Number, Object], required: false, default: 2 },
  gap: { type: Number, required: false, default: 16 },
  animation: { type: Boolean, required: false, default: true },
  delay: { type: Number, required: false, default: 100 },
  rowKey: { type: String, required: false, default: "id" },
  responsive: { type: Boolean, required: false, default: true },
  loading: { type: Boolean, required: false, default: false },
  heightProperty: { type: String, required: false, default: "height" },
  imgSelector: { type: String, required: false, default: "img" },
  themeOverrides: { type: null, required: false, default: void 0 }
});
const ns = useNamespace("waterfall");
const { t } = useLocale();
const { themeStyle } = useComponentTheme(
  "waterfall",
  computed(() => props.themeOverrides)
);
const containerRef = ref();
const containerWidth = ref(0);
const BREAKPOINTS = {
  xs: 0,
  sm: 576,
  md: 768,
  lg: 992,
  xl: 1200
};
const currentCols = computed(() => {
  if (typeof props.cols === "number") return props.cols;
  const width = containerWidth.value;
  const { xs, sm, md, lg, xl } = props.cols || {};
  if (width >= BREAKPOINTS.xl && xl !== void 0) return xl;
  if (width >= BREAKPOINTS.lg && lg !== void 0) return lg;
  if (width >= BREAKPOINTS.md && md !== void 0) return md;
  if (width >= BREAKPOINTS.sm && sm !== void 0) return sm;
  return xs ?? 1;
});
const columnsData = computed(() => {
  const count = currentCols.value;
  const result = Array.from({ length: count }, () => []);
  const columnHeights = new Array(count).fill(0);
  if (!props.items) return result;
  props.items.forEach((item) => {
    const itemHeight = Number(item[props.heightProperty]);
    if (!isNaN(itemHeight) && count > 1) {
      let minHeight = columnHeights[0];
      let minIdx = 0;
      for (let i = 1; i < count; i++) {
        if (columnHeights[i] < minHeight) {
          minHeight = columnHeights[i];
          minIdx = i;
        }
      }
      result[minIdx].push(item);
      columnHeights[minIdx] += itemHeight + props.gap;
    } else {
      const idx = props.items.indexOf(item) % count;
      result[idx].push(item);
    }
  });
  return result;
});
let resizeObserver = null;
const updateWidth = () => {
  if (containerRef.value) {
    containerWidth.value = containerRef.value.offsetWidth;
  }
};
const handleImageLoad = (e) => {
  const target = e.target;
  if (props.imgSelector && target.matches(props.imgSelector)) {
    nextTick(updateWidth);
  }
};
onMounted(() => {
  updateWidth();
  if (props.responsive && typeof ResizeObserver !== "undefined") {
    resizeObserver = new ResizeObserver(() => {
      requestAnimationFrame(updateWidth);
    });
    if (containerRef.value) {
      resizeObserver.observe(containerRef.value);
    }
  }
});
onBeforeUnmount(() => {
  if (resizeObserver) {
    resizeObserver.disconnect();
    resizeObserver = null;
  }
  if (heightResetTimer !== null) {
    clearTimeout(heightResetTimer);
    heightResetTimer = null;
  }
});
const isRefreshing = computed(() => props.loading && props.items && props.items.length > 0);
const minContainerHeight = ref("auto");
let heightResetTimer = null;
watch(
  () => props.loading,
  (val) => {
    if (val && containerRef.value) {
      if (heightResetTimer !== null) {
        clearTimeout(heightResetTimer);
        heightResetTimer = null;
      }
      const height = containerRef.value.offsetHeight;
      if (height > 0) {
        minContainerHeight.value = `${height}px`;
      }
    } else {
      if (heightResetTimer !== null) {
        clearTimeout(heightResetTimer);
      }
      heightResetTimer = setTimeout(() => {
        minContainerHeight.value = "auto";
        heightResetTimer = null;
      }, 300);
    }
  }
);
defineExpose({
  layout: updateWidth
});
</script>

<template>
  <div
    ref="containerRef"
    :class="[ns.b(), ns.is('loading', loading), ns.is('refreshing', isRefreshing)]"
    :style="[themeStyle, {
  gap: `${gap}px`,
  minHeight: minContainerHeight
}]"
    @load.capture="handleImageLoad"
  >
    <!-- 内容区域：不管是数据还是骨架屏，都保持 DOM 结构一致性 -->
    <template v-if="loading && items.length === 0 || items.length > 0">
      <div
        v-for="(col, colIdx) in columnsData"
        :key="colIdx"
        :class="ns.e('column')"
        :style="{
  gap: `${gap}px`
}"
      >
        <!-- 骨架屏分支 -->
        <template v-if="loading && items.length === 0">
          <slot name="loading">
            <div
              :class="[ns.e('item'), ns.em('item', 'skeleton')]"
              :style="{
  height: '220px'
}"
            ></div>
            <div
              :class="[ns.e('item'), ns.em('item', 'skeleton')]"
              :style="{
  height: '160px'
}"
            ></div>
            <div
              :class="[ns.e('item'), ns.em('item', 'skeleton')]"
              :style="{
  height: '200px'
}"
            ></div>
          </slot>
        </template>

        <!-- 实际数据分支 -->
        <template v-else>
          <div
            v-for="(item, itemIdx) in col"
            :key="item[rowKey] || `${colIdx}-${itemIdx}`"
            :class="[ns.e('item'), ns.em('item', animation ? 'animated' : '')]"
            :style="animation ? {
  transitionDelay: `${itemIdx * delay % 600}ms`
} : {}"
          >
            <slot :item="item" :index="itemIdx" :column="colIdx" />
          </div>
        </template>
      </div>
    </template>

    <!-- 空状态 -->
    <div v-else :class="ns.e('empty')">
      <slot name="empty">{{ t("table.emptyText") }}</slot>
    </div>

    <!-- 刷新态蒙层：当有数据且正在加载时显示 -->
    <div v-if="isRefreshing" :class="ns.e('refresh-overlay')">
      <slot name="loading-overlay">
        <div :class="ns.e('loading-spinner')"></div>
      </slot>
    </div>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-waterfall {
  display: flex;
  align-items: flex-start;
  width: 100%;
  box-sizing: border-box;
}
.yh-waterfall__column {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.yh-waterfall__item {
  width: 100%;
  box-sizing: border-box;
  will-change: transform, opacity;
}
.yh-waterfall__item--animated {
  animation: yh-waterfall-in 0.6s cubic-bezier(0.23, 1, 0.32, 1) both;
}

.yh-waterfall__item--skeleton {
  background: var(--yh-bg-color-page);
  border-radius: 8px;
  position: relative;
  overflow: hidden;
}
.yh-waterfall__item--skeleton::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, var(--yh-fill-color-light), transparent);
  animation: yh-skeleton-loading 1.5s infinite;
}

.yh-waterfall__empty {
  width: 100%;
  padding: 40px 0;
  text-align: center;
  color: var(--yh-text-color-secondary);
  font-size: 14px;
}

.yh-waterfall {
  position: relative;
  transition: min-height 0.3s ease;
}
.yh-waterfall.is-loading {
  cursor: wait;
}

.yh-waterfall__refresh-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, 0.6);
}
html.dark .yh-waterfall__refresh-overlay {
  background: rgba(20, 20, 20, 0.6);
}
.yh-waterfall__refresh-overlay {
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  border-radius: inherit;
  animation: yh-fade-in 0.3s ease;
}

.yh-waterfall__loading-spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--yh-color-primary-light-8);
  border-top-color: var(--yh-color-primary);
  border-radius: 50%;
  animation: yh-rotate 0.8s linear infinite;
}

@keyframes yh-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes yh-rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@keyframes yh-skeleton-loading {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(100%);
  }
}
@keyframes yh-waterfall-in {
  from {
    opacity: 0;
    transform: translateY(20px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}
</style>
