<template>
  <div
    :class="[ns.b(), ns.m(listType), ns.m(`pos-${triggerPosition}`), {
  [ns.is('disabled')]: disabled
}]"
    :style="themeStyle"
  >
    <!-- Hidden Input moved to top for better focus/click reliability in some browsers -->
    <input
      ref="inputRef"
      type="file"
      :class="ns.e('input')"
      :accept="accept"
      :multiple="multiple || directory"
      v-bind="directory ? {
  webkitdirectory: '',
  directory: '',
  mozdirectory: ''
} : {}"
      style="display: none"
      @change="onInputChange"
      @click.stop
    />

    <div :class="ns.e('header')">
      <!-- Trigger Area -->
      <div
        v-if="drag"
        :class="[ns.e('dragger'), {
  [ns.is('dragover')]: dragOver
}]"
        @drop.prevent="onDrop"
        @dragover.prevent="onDragOver"
        @dragenter.prevent="onDragOver"
        @dragleave.prevent="dragOver = false"
        @click="triggerInput"
      >
        <slot name="trigger">
          <slot>
            <div :class="ns.e('content')">
              <yh-icon name="plus" :size="32" :class="ns.e('icon')" />
              <!-- eslint-disable-next-line vue/no-v-html -->
              <div :class="ns.e('text')" v-html="t('upload.tip')"> </div>
            </div>
          </slot>
        </slot>
      </div>

      <template v-else>
        <!-- If trigger slot exists, it always gets the click handler -->
        <div v-if="$slots.trigger" :class="ns.e('trigger')" @click="triggerInput">
          <slot name="trigger" />
        </div>

        <!-- If picture-card and no trigger slot, use the plus button trigger -->
        <div v-else-if="listType === 'picture-card'" :class="ns.e('trigger')" @click="triggerInput">
          <div :class="ns.e('picture-card-plus')">
            <yh-icon name="plus" :size="28" />
          </div>
        </div>

        <!-- Default: the default slot is the trigger -->
        <div v-else :class="ns.e('trigger')" @click="triggerInput">
          <slot />
        </div>
      </template>

      <!-- Tip Slot -->
      <div v-if="$slots.tip" :class="ns.e('tip')">
        <slot name="tip" />
      </div>

      <!-- If trigger slot is used, the default slot can be used for other elements like submit buttons -->
      <div v-if="$slots.trigger" :class="ns.e('extra')">
        <slot />
      </div>
    </div>

    <!-- File List -->
    <transition-group
      v-if="showFileList"
      name="yh-list"
      tag="ul"
      :class="[ns.e('list'), ns.em('list', listType)]"
    >
      <li v-for="file in fileList" :key="file.uid" :class="[ns.e('item'), ns.is(file.status)]">
        <slot name="file" :file="file">
          <!-- Picture Card Layout -->
          <template v-if="listType === 'picture-card'">
            <div :class="ns.e('thumbnail')">
              <img v-if="file.url" :src="file.url" :alt="file.name" :crossorigin="crossorigin" />
              <div :class="ns.e('actions')">
                <span @click.stop="handlePreview(file)">
                  <yh-icon name="eye" :size="18" />
                </span>
                <span v-if="showDownload" @click.stop="handleDownload(file)">
                  <yh-icon name="download" :size="18" />
                </span>
                <span v-if="!disabled" @click.stop="handleRemove(file)">
                  <yh-icon name="delete" :size="18" />
                </span>
              </div>
              <!-- Progress Bar overlay -->
              <div v-if="file.status === 'uploading'" :class="ns.e('progress-overlay')">
                <div
                  :class="ns.e('progress-inner')"
                  :style="{
  width: (file.percentage || 0) + '%'
}"
                ></div>
              </div>
            </div>
          </template>

          <!-- Text/Picture Layout -->
          <template v-else>
            <div :class="ns.e('info')">
              <div v-if="listType === 'picture'" :class="ns.e('thumbnail-box')">
                <img
                  v-if="file.url"
                  :src="file.url"
                  :class="ns.e('thumbnail-img')"
                  :crossorigin="crossorigin"
                />
                <yh-icon v-else name="image" :size="24" :class="ns.e('thumbnail-icon')" />
              </div>
              <slot name="file-icon" :file="file">
                <yh-icon :name="getFileIcon(file)" :size="20" :class="ns.e('file-icon')" />
              </slot>

              <div :class="ns.e('details')">
                <div :class="ns.e('name')" @click="handlePreview(file)">
                  {{ file.name }}
                </div>
              </div>

              <div :class="ns.e('status-icon-wrapper')">
                <yh-icon
                  v-if="file.status === 'success'"
                  name="check"
                  :size="14"
                  :class="ns.e('status-icon')"
                  color="var(--yh-color-success)"
                />
                <yh-icon
                  v-if="file.status === 'fail'"
                  name="error"
                  :size="14"
                  :class="ns.e('status-icon')"
                  color="var(--yh-color-danger)"
                />

                <div v-if="!disabled" :class="ns.e('actions-inline')">
                  <yh-icon
                    v-if="showDownload"
                    name="download"
                    :size="16"
                    :class="ns.e('download-btn')"
                    @click.stop="handleDownload(file)"
                  />
                  <yh-icon
                    v-if="file.status === 'fail'"
                    name="refresh"
                    :size="16"
                    :class="ns.e('retry-btn')"
                    @click.stop="startUpload(file)"
                  />
                  <yh-icon
                    name="delete"
                    :size="16"
                    :class="ns.e('delete-btn')"
                    @click.stop="handleRemove(file)"
                  />
                </div>
              </div>

              <!-- Full width bottom progress for non-card modes -->
              <div v-if="file.status === 'uploading'" :class="ns.e('bottom-progress')">
                <div
                  :class="ns.e('bottom-progress-bar')"
                  :style="{
  width: (file.percentage || 0) + '%'
}"
                ></div>
              </div>
            </div>
          </template>
        </slot>
      </li>
    </transition-group>
  </div>
</template>

<script setup>
import { ref, onBeforeUnmount, computed } from "vue";
import {
  uploadProps,
  uploadEmits
} from "./upload";
import { useNamespace, useLocale } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import { YhIcon } from "../../icon";
import { loadViewer } from "../../viewerjs";
defineOptions({
  name: "YhUpload"
});
const props = defineProps(uploadProps);
const emit = defineEmits(uploadEmits);
const ns = useNamespace("upload");
const { t } = useLocale();
const { themeStyle } = useComponentTheme(
  "upload",
  computed(() => props.themeOverrides)
);
const inputRef = ref(null);
const dragOver = ref(false);
let viewer = null;
const uploadReqs = {};
const localBlobUrls = /* @__PURE__ */ new Set();
const triggerInput = () => {
  if (props.disabled) return;
  if (inputRef.value) {
    inputRef.value.value = "";
  }
  inputRef.value?.click();
};
const getFileIcon = (file) => {
  if (props.fileIcon) {
    if (typeof props.fileIcon === "function") {
      return props.fileIcon(file);
    }
    return props.fileIcon;
  }
  const fileName = file.name;
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
  const map = {
    // 图片
    jpg: "image",
    jpeg: "image",
    png: "image",
    gif: "image",
    webp: "image",
    svg: "image",
    // 文档
    pdf: "file-pdf",
    doc: "file-word",
    docx: "file-word",
    xls: "file-excel",
    xlsx: "file-excel",
    ppt: "file-word",
    pptx: "file-word",
    txt: "file-txt",
    // 媒体
    mp4: "file-video",
    mkv: "file-video",
    avi: "file-video",
    mov: "file-video",
    mp3: "file-audio",
    wav: "file-audio",
    flac: "file-audio"
  };
  return map[ext] || "attachment";
};
const onInputChange = (e) => {
  if (props.disabled) return;
  const files = e.target.files;
  if (!files) return;
  handleFiles(Array.from(files));
};
const onDragOver = () => {
  if (props.disabled || !props.drag) return;
  dragOver.value = true;
};
const onDrop = async (e) => {
  if (props.disabled || !props.drag) return;
  dragOver.value = false;
  const items = e.dataTransfer?.items;
  if (!items) return;
  const files = [];
  const readEntry = async (entry, path = "") => {
    if (entry.isFile) {
      const fileEntry = entry;
      const file = await new Promise((resolve) => fileEntry.file(resolve));
      if (path) {
        Object.defineProperty(file, "webkitRelativePath", {
          value: `${path}${file.name}`,
          configurable: true
        });
      }
      files.push(file);
    } else if (entry.isDirectory) {
      const dirEntry = entry;
      const reader = dirEntry.createReader();
      const allEntries = [];
      while (true) {
        const entries = await new Promise((resolve) => {
          reader.readEntries(
            (results) => resolve(results),
            () => resolve([])
          );
        });
        if (entries.length === 0) break;
        allEntries.push(...entries);
      }
      for (const subEntry of allEntries) {
        await readEntry(subEntry, `${path}${entry.name}/`);
      }
    }
  };
  const entriesToRead = [];
  for (const item of Array.from(items)) {
    const entry = item.webkitGetAsEntry?.();
    if (entry) {
      entriesToRead.push(readEntry(entry));
    }
  }
  if (entriesToRead.length > 0) {
    await Promise.all(entriesToRead);
    handleFiles(files);
  } else {
    const rawFiles = e.dataTransfer?.files;
    if (rawFiles) handleFiles(Array.from(rawFiles));
  }
};
const attrAccept = (file, accept) => {
  if (!accept) return true;
  const acceptList = accept.split(",").map((item) => item.trim());
  if (acceptList.length === 0) return true;
  const fileName = file.name;
  const type = file.type;
  return acceptList.some((item) => {
    if (item.startsWith(".")) {
      return fileName.toLowerCase().endsWith(item.toLowerCase());
    } else if (item.endsWith("/*")) {
      const baseType = item.replace("/*", "");
      return type.startsWith(baseType);
    }
    return type === item;
  });
};
const handleFiles = async (files) => {
  const filteredByRules = files.filter((file) => {
    if (props.accept && !attrAccept(file, props.accept)) {
      console.warn(
        `[YhUpload] Auto Remove: File format does not match "${props.accept}" - ${file.name}`
      );
      return false;
    }
    if (props.maxSize && file.size / 1024 > props.maxSize) {
      console.warn(`[YhUpload] Auto Remove: File size exceeds limit - ${file.name}`);
      return false;
    }
    return true;
  });
  const passedFiles = [];
  for (const file of filteredByRules) {
    const rawFile = file;
    let finalFile = rawFile;
    if (props.beforeUpload) {
      try {
        const result = await props.beforeUpload(rawFile);
        if (result === false) continue;
        if (result instanceof Blob) {
          const newRawFile = result;
          if (!newRawFile.uid) newRawFile.uid = Date.now() + Math.random();
          finalFile = newRawFile;
        }
      } catch {
        continue;
      }
    }
    passedFiles.push({ rawFile, finalFile });
  }
  if (props.limit && props.fileList.length + passedFiles.length > props.limit) {
    const rawFilesOnly = passedFiles.map((item) => item.finalFile);
    emit("exceed", rawFilesOnly, props.fileList);
    return;
  }
  const validFiles = [];
  for (const item of passedFiles) {
    const rawFile = item.finalFile;
    if (!rawFile.uid) rawFile.uid = Date.now() + Math.random();
    const uploadFile = {
      name: props.directory && rawFile.webkitRelativePath ? rawFile.webkitRelativePath : rawFile.name,
      percentage: 0,
      status: "ready",
      size: rawFile.size,
      uid: rawFile.uid,
      raw: rawFile
    };
    if (props.listType.includes("picture")) {
      if (props.thumbnailRequest) {
        uploadFile.url = await props.thumbnailRequest(rawFile);
      } else if (rawFile.type && rawFile.type.startsWith("image/")) {
        const url = URL.createObjectURL(rawFile);
        uploadFile.url = url;
        localBlobUrls.add(url);
      }
    }
    validFiles.push(uploadFile);
  }
  if (validFiles.length > 0) {
    const newFileList = [...props.fileList, ...validFiles];
    emit("update:fileList", newFileList);
    validFiles.forEach((file) => {
      emit("change", file, newFileList);
      if (props.autoUpload) {
        startUpload(file);
      }
    });
  }
};
const startUpload = async (file) => {
  file.status = "uploading";
  const options = {
    action: props.action,
    method: props.method,
    file: file.raw,
    filename: props.name,
    name: file.name,
    data: props.data,
    headers: props.headers,
    withCredentials: props.withCredentials,
    onProgress: (evt) => {
      file.percentage = evt.percent;
      emit("progress", evt, file, props.fileList);
    },
    onSuccess: (res) => {
      delete uploadReqs[file.uid];
      file.status = "success";
      file.response = res;
      emit("success", res, file, props.fileList);
    },
    onError: (err) => {
      delete uploadReqs[file.uid];
      file.status = "fail";
      file.error = err;
      emit("error", err, file, props.fileList);
    }
  };
  let req;
  if (props.httpRequest) {
    req = props.httpRequest(options);
  } else {
    req = ajaxUpload(options);
  }
  if (req) {
    uploadReqs[file.uid] = req;
  }
};
const ajaxUpload = (options) => {
  const xhr = new XMLHttpRequest();
  const formData = new FormData();
  if (options.data) {
    Object.keys(options.data).forEach((key) => {
      formData.append(key, options.data[key]);
    });
  }
  formData.append(options.filename, options.file, options.name);
  xhr.upload.onprogress = (e) => {
    if (e.total > 0) {
      const percent = Math.round(e.loaded / e.total * 100);
      options.onProgress({ ...e, percent });
    }
  };
  xhr.onload = () => {
    if (xhr.status >= 200 && xhr.status < 300) {
      let response = xhr.responseText;
      try {
        response = JSON.parse(response);
      } catch {
      }
      options.onSuccess(response);
    } else {
      options.onError(new Error(`Upload failed with status ${xhr.status}`));
    }
  };
  xhr.onerror = () => {
    options.onError(new Error("Network Error"));
  };
  xhr.open(options.method, options.action, true);
  if (options.withCredentials) {
    xhr.withCredentials = true;
  }
  if (options.headers) {
    Object.keys(options.headers).forEach((key) => {
      xhr.setRequestHeader(key, options.headers[key]);
    });
  }
  xhr.send(formData);
  return xhr;
};
const handleRemove = async (file) => {
  if (props.disabled) return;
  if (props.beforeRemove) {
    const result = await props.beforeRemove(file, props.fileList);
    if (result === false) return;
  }
  const req = uploadReqs[file.uid];
  if (req) {
    if (typeof req.abort === "function") {
      req.abort();
    }
    delete uploadReqs[file.uid];
  }
  const newFileList = props.fileList.filter((f) => f.uid !== file.uid);
  emit("update:fileList", newFileList);
  emit("remove", file, newFileList);
  if (file.url && file.url.startsWith("blob:")) {
    URL.revokeObjectURL(file.url);
    localBlobUrls.delete(file.url);
  }
};
const handlePreview = async (file) => {
  emit("preview", file);
  const isImage = file.url && (file.url.startsWith("blob:") || file.url.startsWith("data:") || /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(file.name));
  if (isImage) {
    const images = props.fileList.filter(
      (f) => f.url && (f.url.startsWith("blob:") || f.url.startsWith("data:") || /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(f.name))
    ).map((f) => f.url);
    if (images.length === 0) return;
    let ViewerClass;
    try {
      ViewerClass = await loadViewer();
    } catch {
      return;
    }
    const container = document.createElement("div");
    images.forEach((src) => {
      const img = document.createElement("img");
      img.src = src;
      img.style.display = "none";
      container.appendChild(img);
    });
    const initialIndex = images.indexOf(file.url);
    if (viewer) viewer.destroy();
    const nextViewer = new ViewerClass(container, {
      hidden: () => {
        viewer?.destroy();
        viewer = null;
      },
      navbar: images.length > 1,
      toolbar: {
        zoomIn: 4,
        zoomOut: 4,
        oneToOne: 4,
        reset: 4,
        prev: images.length > 1 ? 4 : 0,
        play: {
          show: 4,
          size: "large"
        },
        next: images.length > 1 ? 4 : 0,
        rotateLeft: 4,
        rotateRight: 4,
        flipHorizontal: 4,
        flipVertical: 4
      }
    });
    viewer = nextViewer;
    nextViewer.view(initialIndex !== -1 ? initialIndex : 0);
    nextViewer.show();
  }
};
const handleDownload = async (file) => {
  emit("download", file);
  if (!file.url) return;
  if (file.url.startsWith("blob:") || file.url.startsWith("data:")) {
    const link = document.createElement("a");
    link.href = file.url;
    link.download = file.name || "download";
    link.click();
    return;
  }
  try {
    const response = await fetch(file.url, {
      method: "GET",
      mode: "cors"
      // 必须开启 CORS 模式
    });
    if (!response.ok) throw new Error("Network response was not ok");
    const blob = await response.blob();
    const blobUrl = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = blobUrl;
    link.download = file.name || "download";
    link.style.display = "none";
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    setTimeout(() => URL.revokeObjectURL(blobUrl), 1e3);
  } catch (err) {
    console.warn("[YhUpload] CORS download restricted, falling back to preview mode", err);
    const link = document.createElement("a");
    link.href = file.url;
    link.target = "_blank";
    link.download = file.name || "download";
    link.click();
  }
};
onBeforeUnmount(() => {
  if (viewer) {
    viewer.destroy();
    viewer = null;
  }
  Object.keys(uploadReqs).forEach((uid) => {
    const req = uploadReqs[uid];
    if (req && typeof req.abort === "function") {
      req.abort();
    }
    delete uploadReqs[uid];
  });
  localBlobUrls.forEach((url) => {
    URL.revokeObjectURL(url);
  });
  localBlobUrls.clear();
});
defineExpose({
  triggerInput,
  handleRemove,
  handlePreview,
  handleDownload,
  handleFiles,
  submit: () => {
    props.fileList.filter((f) => f.status === "ready").forEach(startUpload);
  }
});
</script>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-upload {
  --yh-upload-dragger-bg: var(--yh-bg-color);
  --yh-upload-dragger-border: var(--yh-border-color-light);
  --yh-upload-item-bg: var(--yh-fill-color-blank);
  --yh-upload-progress-bg: var(--yh-color-primary);
  --yh-upload-error-bg: var(--yh-color-danger-light-9);
  --yh-upload-error-hover-bg: var(--yh-color-danger-light-7);
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  box-sizing: border-box;
}
.yh-upload--pos-bottom {
  flex-direction: column-reverse;
}

.yh-upload--pos-left {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

.yh-upload--pos-right {
  flex-direction: row-reverse;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
}

.yh-upload--picture-card {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: flex-start;
}
.yh-upload--picture-card .yh-upload__header {
  width: auto;
  margin-bottom: 0;
}
.yh-upload--picture-card .yh-upload__trigger {
  display: flex;
}

.yh-upload__input {
  display: none !important;
}

.yh-upload__header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  width: 100%;
  margin-bottom: 4px;
}
.yh-upload--picture-card .yh-upload__header {
  width: auto;
  margin-bottom: 0;
}
.yh-upload__header:has(.yh-upload__dragger) {
  flex-direction: column;
  align-items: flex-start;
}

.yh-upload__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  width: fit-content;
}

.yh-upload__tip {
  margin-top: 0;
  font-size: 12px;
  color: var(--yh-text-color-secondary);
}

.yh-upload__dragger {
  width: 100% !important;
  display: block;
  background-color: var(--yh-upload-dragger-bg);
  border: 1px dashed var(--yh-upload-dragger-border);
  border-radius: 12px;
  padding: 32px 16px;
  text-align: center;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  overflow: hidden;
  box-sizing: border-box;
}
.yh-upload__dragger > * {
  pointer-events: none;
}
.yh-upload__dragger:hover {
  border-color: var(--yh-color-primary);
  background-color: var(--yh-color-primary-light-9);
}
.yh-upload__dragger.is-dragover {
  border-color: var(--yh-color-primary);
  background-color: var(--yh-color-primary-light-8);
  border-width: 2px;
}
.yh-upload__icon {
  color: var(--yh-text-color-placeholder);
  margin-bottom: 12px;
}

.yh-upload__dragger:hover .yh-upload__icon {
  color: var(--yh-color-primary);
}
.yh-upload__text {
  color: var(--yh-text-color-regular);
  font-size: 14px;
}
.yh-upload__text em {
  color: var(--yh-color-primary);
  font-style: normal;
  margin: 0 4px;
  font-weight: 600;
}

.yh-upload__picture-card-plus {
  background-color: var(--yh-fill-color-light);
  border: 1px dashed var(--yh-border-color);
  border-radius: 12px;
  width: 120px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.3s;
  color: var(--yh-text-color-placeholder);
}
.yh-upload__picture-card-plus:hover {
  border-color: var(--yh-color-primary);
  color: var(--yh-color-primary);
}

.yh-upload__list {
  margin: 0;
  padding: 0;
  list-style: none !important;
  font-size: 14px;
}
.yh-upload__list li {
  list-style-type: none !important;
  margin: 0;
  padding: 0;
}
.yh-upload__list li::before {
  display: none !important;
}
.yh-upload__list--text {
  margin-top: 8px;
}

.yh-upload__list--picture {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.yh-upload__list--picture-card {
  display: contents;
}

.yh-upload__item {
  position: relative;
  box-sizing: border-box;
  list-style: none !important;
  transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
}
.yh-upload__item.is-success .yh-upload__name {
  color: var(--yh-color-success);
}
.yh-upload__item.is-fail {
  background-color: var(--yh-upload-error-bg) !important;
}
.yh-upload__item.is-fail .yh-upload__name {
  color: var(--yh-color-danger);
}
.yh-upload__item.is-fail:hover {
  background-color: var(--yh-upload-error-hover-bg) !important;
}
.yh-upload--text .yh-upload__item, .yh-upload--picture .yh-upload__item {
  width: 100%;
  display: flex;
  align-items: center;
  padding: 6px 12px;
  border-radius: 6px;
  margin-bottom: 4px;
  gap: 12px;
}
.yh-upload--text .yh-upload__item:hover, .yh-upload--picture .yh-upload__item:hover {
  background-color: var(--yh-fill-color-light);
}
.yh-upload--picture .yh-upload__item {
  border: 1px solid var(--yh-border-color-lighter);
  background: var(--yh-bg-color);
  padding: 8px;
}
.yh-upload--picture .yh-upload__item:hover {
  box-shadow: var(--yh-shadow-sm);
  border-color: var(--yh-color-primary-light-5);
}
.yh-upload--picture-card .yh-upload__item {
  display: inline-flex;
  width: 120px;
  height: 120px;
  margin: 0;
  border: 1px solid var(--yh-border-color-lighter);
  border-radius: 8px;
  overflow: hidden;
  vertical-align: top;
}

.yh-upload__thumbnail {
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.yh-upload__thumbnail img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.yh-upload__thumbnail-box {
  width: 48px;
  height: 48px;
  background: var(--yh-fill-color-light);
  border-radius: var(--yh-radius-base);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
}

.yh-upload__thumbnail-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.yh-upload__thumbnail-icon {
  color: var(--yh-text-color-placeholder);
}

.yh-upload__file-icon {
  color: var(--yh-text-color-secondary);
  flex-shrink: 0;
}

.yh-upload__actions {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  opacity: 0;
  transition: opacity 0.3s;
  color: #fff;
  z-index: 2;
}
.yh-upload__actions span {
  cursor: pointer;
}
.yh-upload__actions span:hover {
  color: var(--yh-color-primary-light-3);
}
.yh-upload__item:hover .yh-upload__actions {
  opacity: 1;
}

.yh-upload__info {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  position: relative;
  z-index: 1;
}

.yh-upload__details {
  flex: 1;
  min-width: 0;
}

.yh-upload__name {
  font-size: 14px;
  color: var(--yh-text-color-regular);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: color 0.2s;
}
.yh-upload__name:hover {
  color: var(--yh-color-primary);
}

.yh-upload__status-icon-wrapper {
  display: flex;
  align-items: center;
  gap: 12px;
}

.yh-upload__actions-inline {
  display: flex;
  align-items: center;
  gap: 8px;
  opacity: 0;
  transition: opacity 0.2s;
}
.yh-upload__item:hover .yh-upload__actions-inline {
  opacity: 1;
}

.yh-upload__delete-btn {
  cursor: pointer;
  color: var(--yh-text-color-secondary);
  transition: color 0.2s;
}
.yh-upload__delete-btn:hover {
  color: var(--yh-color-primary);
}

.yh-upload__retry-btn {
  cursor: pointer;
  color: var(--yh-text-color-secondary);
  transition: color 0.2s;
}
.yh-upload__retry-btn:hover {
  color: var(--yh-color-primary);
}

.yh-upload__delete-btn:hover {
  color: var(--yh-color-danger) !important;
}

.yh-upload__bottom-progress {
  position: absolute;
  bottom: 0px;
  left: 0px;
  right: 0px;
  height: 2px;
  background: var(--yh-fill-color-light);
  z-index: 10;
}

.yh-upload__bottom-progress-bar {
  height: 100%;
  background: var(--yh-color-primary);
  transition: width 0.3s linear;
}

.yh-upload.is-disabled {
  cursor: not-allowed;
}
.yh-upload.is-disabled .yh-upload__dragger,
.yh-upload.is-disabled .yh-upload__picture-card-plus {
  cursor: not-allowed;
  border-color: var(--yh-border-color-lighter) !important;
  background-color: var(--yh-fill-color-lighter) !important;
}

.yh-list-enter-active,
.yh-list-leave-active {
  transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
}

.yh-list-enter-from,
.yh-list-leave-to {
  opacity: 0;
  transform: translateY(-30px);
}

.yh-list-leave-active {
  position: absolute;
}
</style>
