# 缩略图

## 使用方法

`Thumbnail` 是 64px 的预览。没有 `src` 时是占位图。只有 `isLoading`、没有 `src` 时是骨架。两者都有时，图片上盖一层加载指示。监听 `click` 后，预览变成按钮。监听 `remove` 后，出现关闭按钮。

`showRemoveOn` 默认是 `hover`。键盘焦点时也会出现，粗指针设备上则一直可见。`always` 让它一直可见。`isDisabled` 会把缩略图调淡，并忽略点击和移除。

`label` 是无障碍名称，不会画在方块上。提示和移除按钮都会用到它。

```vue
<script setup lang="ts">
import { Thumbnail } from '@astryx-vue/core/Thumbnail'
</script>

<template>
  <Thumbnail src="/photo.jpg" alt="雪山" label="snowy-peaks.jpg" />
</template>
```

## 建议

| 建议 | 做法 |
| --- | --- |
| 推荐 | `alt` 写图片内容，`label` 写移除时能认出来的文件名。 |
| 推荐 | 指针可能很粗，或者移除是主要操作时，用 `showRemoveOn="always"`。 |
| 推荐 | 文件还没有预览时先放占位，有地址后再传 `src`。 |
| 避免 | 把文件名画进方块。提示里已经有 `label`。 |
| 避免 | 在 `isDisabled` 或 `isLoading` 时还指望 `click` 会触发。 |

### 结构

- **图片**: 预览图，按正方形、cover 的方式显示。
- **占位**: 没有 src 时显示的图片剪影图标。
- **移除按钮**: 叠在右上角的关闭按钮。什么时候出现由 showRemoveOn 决定，默认是悬停。点击会发出 remove。它坐在固定的 --color-overlay 遮罩上，图标用 --color-on-dark，好在任何图片上都看得见。
- **上传遮罩**: 半透明遮罩加转圈。isLoading 为真、并且已经有 src 预览时显示。
- **骨架**: isLoading 为真、并且没有 src 时的闪烁动画。

## 示例

### Thumbnail — 禁用

禁用后透明度降低，移除按钮和点击都不再响应。

```vue
<script setup lang="ts">
import { Stack } from '@astryx-vue/core/Stack'
import { Text } from '@astryx-vue/core/Text'
import { Thumbnail } from '@astryx-vue/core/Thumbnail'
import { GOLDEN_SUNSET } from './images'

function noop() {}
</script>

<template>
  <Stack direction="vertical" :gap="4">
    <Stack direction="vertical" :gap="1">
      <Text type="supporting" color="secondary">
        {{ 'Enabled' }}
      </Text>
      <Stack align="center" direction="horizontal" :gap="3">
        <Thumbnail
          :src="GOLDEN_SUNSET"
          alt="Golden sunset over mountains"
          label="golden-sunset.jpg"
          show-remove-on="always"
          @remove="noop"
        />
        <Thumbnail
          label="document.pdf"
          show-remove-on="always"
          @remove="noop"
        />
      </Stack>
    </Stack>
    <Stack direction="vertical" :gap="1">
      <Text type="supporting" color="secondary">
        {{ 'Disabled' }}
      </Text>
      <Stack align="center" direction="horizontal" :gap="3">
        <Thumbnail
          :src="GOLDEN_SUNSET"
          alt="Golden sunset over mountains"
          label="golden-sunset.jpg"
          is-disabled
          @remove="noop"
        />
        <Thumbnail label="document.pdf" is-disabled @remove="noop" />
      </Stack>
    </Stack>
  </Stack>
</template>
```

### Thumbnail — 图库

一排可点击的缩略图。适合附件列表：点开预览，点关闭移除。

```vue
<script setup lang="ts">
import type { ThumbnailScene } from './images'
import { Stack } from '@astryx-vue/core/Stack'
import { Text } from '@astryx-vue/core/Text'
import { Thumbnail } from '@astryx-vue/core/Thumbnail'
import { ref } from 'vue'
import { thumbnailScenes } from './images'

const items = ref<ThumbnailScene[]>([...thumbnailScenes])
const selected = ref<string | null>(null)

function remove(id: number) {
  items.value = items.value.filter(item => item.id !== id)
}
</script>

<template>
  <Stack direction="vertical" :gap="3">
    <Text type="supporting" color="secondary">
      {{ 'Click to preview, dismiss to remove' }}
    </Text>
    <Stack align="center" direction="horizontal" :gap="2">
      <Thumbnail
        v-for="item in items"
        :key="item.id"
        :src="item.src"
        :alt="item.alt"
        :label="item.label"
        show-remove-on="always"
        @click="selected = item.label"
        @remove="remove(item.id)"
      />
    </Stack>
    <Text v-if="selected" type="supporting" color="secondary">
      Selected: {{ selected }}
    </Text>
  </Stack>
</template>
```

### Thumbnail — 可移除

右上角的移除按钮在深色和浅色图片上都看得见。

```vue
<script setup lang="ts">
import type { ThumbnailScene } from './images'
import { Stack } from '@astryx-vue/core/Stack'
import { Text } from '@astryx-vue/core/Text'
import { Thumbnail } from '@astryx-vue/core/Thumbnail'
import { ref } from 'vue'
import { thumbnailScenes } from './images'

const items = ref<ThumbnailScene[]>([...thumbnailScenes])

function remove(id: number) {
  items.value = items.value.filter(item => item.id !== id)
}
</script>

<template>
  <Stack direction="vertical" :gap="4">
    <Text type="supporting" color="secondary">
      {{ 'Remove button adapts contrast to image luminance' }}
    </Text>
    <Stack align="center" direction="horizontal" :gap="3">
      <Thumbnail
        v-for="item in items"
        :key="item.id"
        :src="item.src"
        :alt="item.alt"
        :label="item.label"
        show-remove-on="always"
        @remove="remove(item.id)"
      />
      <Text v-if="items.length === 0" type="supporting" color="secondary">
        {{ 'All removed.' }}
      </Text>
    </Stack>
  </Stack>
</template>
```

### Thumbnail — 状态

占位、骨架、上传中和已加载并排，对应从空到完成的过程。

```vue
<script setup lang="ts">
import { Stack } from '@astryx-vue/core/Stack'
import { Text } from '@astryx-vue/core/Text'
import { Thumbnail } from '@astryx-vue/core/Thumbnail'
import { MISTY_VALLEY } from './images'
</script>

<template>
  <Stack direction="vertical" :gap="4">
    <Stack direction="vertical" :gap="1">
      <Text type="supporting" color="secondary">
        {{ 'Lifecycle: empty → uploading → processing → loaded' }}
      </Text>
      <Stack align="end" direction="horizontal" :gap="3">
        <Stack direction="vertical" :gap="1" h-align="center">
          <Thumbnail label="report.pdf" />
          <Text type="supporting" color="secondary">
            {{ 'Placeholder' }}
          </Text>
        </Stack>
        <Stack direction="vertical" :gap="1" h-align="center">
          <Thumbnail is-loading label="uploading.jpg" />
          <Text type="supporting" color="secondary">
            {{ 'Skeleton' }}
          </Text>
        </Stack>
        <Stack direction="vertical" :gap="1" h-align="center">
          <Thumbnail
            :src="MISTY_VALLEY"
            alt="Misty mountain valley"
            is-loading
            label="misty-valley.jpg"
          />
          <Text type="supporting" color="secondary">
            {{ 'Uploading' }}
          </Text>
        </Stack>
        <Stack direction="vertical" :gap="1" h-align="center">
          <Thumbnail
            :src="MISTY_VALLEY"
            alt="Misty mountain valley"
            label="misty-valley.jpg"
          />
          <Text type="supporting" color="secondary">
            {{ 'Loaded' }}
          </Text>
        </Stack>
      </Stack>
    </Stack>
  </Stack>
</template>
```

## 属性

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `src` | `string` | - | 图片地址。加载失败时，这一张回退到占位图。 |
| `alt` | `string` | - | 图片的替代文字。 |
| `label` | `string` | - | 无障碍名称，不画出来。提示和移除按钮都会用到。 |
| `isLoading` | `boolean` | `false` | 没有 `src` 时是骨架。有图片时在上面盖加载指示。 |
| `isDisabled` | `boolean` | `false` | 把缩略图调淡，并忽略点击和移除。 |
| `showRemoveOn` | `'always' \| 'hover'` | `'hover'` | 移除按钮何时可见。`hover` 在键盘焦点时也会出现，粗指针设备上则一直可见。 |

## 事件

| 名称 | 参数 | 时机 |
| --- | --- | --- |
| <code>click</code> | <code>MouseEvent</code> | 预览被激活，且没有禁用、也没有在加载。 |
| <code>remove</code> | <code>MouseEvent</code> | 使用移除按钮。只有监听了这个事件才会渲染按钮。 |

## 主题

方块是 64px，圆角用元素圆角，底色是中性色。边框 1px，画在内侧。占位图标 24px。移除按钮 20×20，距上沿和结束沿一个间距台阶，背景是遮罩色，图标是深色底上的浅色。粗指针会把点击区域向外扩 2px。禁用时不透明度为 0.5。
