# Icon 图标

图标是按钮、列表项、状态提示等几乎所有组件的视觉零件。`MIcon` 只做一件事：把一个 24x24 viewBox 的 SVG path 字符串渲染成可着色、可缩放、可旋转的 SVG，颜色默认跟随 `currentColor`，因此放进任何容器都会自动继承文字色。

## 代码演示

### 基础用法

`path` 来自 `@mdi/js`，按需 import 单个图标常量即可，不会把整套图标打进产物。

**演示说明：** 点击任意图标会弹出提示。`title` 同时提供悬停提示与无障碍名称。

```vue
<script setup lang="ts">
import {
  mdiBellOutline,
  mdiCogOutline,
  mdiContentCopy,
  mdiDeleteOutline,
  mdiMagnify,
  mdiPencilOutline,
  mdiRefresh,
  mdiStarOutline,
} from '@mdi/js'
import { toast } from 'miao-design'

const icons = [
  { path: mdiPencilOutline, label: '编辑' },
  { path: mdiDeleteOutline, label: '删除' },
  { path: mdiContentCopy, label: '复制' },
  { path: mdiMagnify, label: '搜索' },
  { path: mdiCogOutline, label: '设置' },
  { path: mdiBellOutline, label: '通知' },
  { path: mdiRefresh, label: '刷新' },
  { path: mdiStarOutline, label: '收藏' },
]

function handleClick(label: string) {
  toast.success(`点击了「${label}」图标`)
}
</script>

<template>
  <div class="m-row-demo">
    <MIcon
      v-for="icon in icons"
      :key="icon.label"
      :path="icon.path"
      :size="28"
      :title="icon.label"
      style="cursor: pointer"
      @click="handleClick(icon.label)"
    />
  </div>
</template>
```

### 尺寸

`size` 传数字按 px 处理，传字符串则原样输出，可以用 `em` 这类相对单位跟随父级字号。

**演示说明：** `size` 会同时写到 `width`、`height` 与 `font-size` 上。

```vue
<script setup lang="ts">
import { mdiCogOutline } from '@mdi/js'

const sizes = [16, 20, 24, 32, 40] as const
</script>

<template>
  <div class="m-row-demo">
    <!-- 数字按 px 处理 -->
    <MIcon v-for="size in sizes" :key="size" :path="mdiCogOutline" :size="size" />
    <!-- 字符串原样输出，可跟随父级字号 -->
    <span style="font-size: 20px">
      <MIcon :path="mdiCogOutline" size="1.5em" />
    </span>
  </div>
</template>
```

### 语义色

六种语义色与主题令牌一一对应；不传 `color` 时继承 `currentColor`，`disabled` 使用禁用态文字色。

**演示说明：** 紫色的那个图标没有设置 `color`，颜色来自外层 `span` 的 `color`。

```vue
<script setup lang="ts">
import { mdiHeartOutline } from '@mdi/js'

const colors = ['primary', 'secondary', 'success', 'warning', 'error', 'info'] as const
</script>

<template>
  <div class="m-row-demo">
    <MIcon v-for="color in colors" :key="color" :path="mdiHeartOutline" :size="28" :color="color" />
    <!-- 未指定 color 时继承 currentColor -->
    <span style="color: #7e57c2">
      <MIcon :path="mdiHeartOutline" :size="28" />
    </span>
    <MIcon :path="mdiHeartOutline" :size="28" disabled />
  </div>
</template>
```

### 翻转与旋转

`flip` 处理镜像，`rotate` 处理旋转，两者同时设置时会合并成一个 `transform`：先镜像再旋转。

**演示说明：** 第一行依次是原始、`horizontal`、`vertical`、`both`；第二行依次是 `rotate` 45 / 90 / 180 / 270 度。

```vue
<script setup lang="ts">
import { mdiArrowRight, mdiPencilOutline } from '@mdi/js'

const flips = ['horizontal', 'vertical', 'both'] as const
const rotates = [45, 90, 180, 270] as const
</script>

<template>
  <div class="m-row-demo--col">
    <div class="m-row-demo">
      <MIcon :path="mdiPencilOutline" :size="30" title="原始" />
      <MIcon v-for="flip in flips" :key="flip" :path="mdiPencilOutline" :size="30" :flip="flip" />
    </div>

    <div class="m-row-demo">
      <MIcon :path="mdiArrowRight" :size="30" title="原始" />
      <MIcon v-for="rotate in rotates" :key="rotate" :path="mdiArrowRight" :size="30" :rotate="rotate" />
    </div>
  </div>
</template>
```

### 自旋

`spin` 用于加载、同步这类需要持续动效的场景。

**演示说明：** 开启 `spin` 后图标以 1.6s 线性循环旋转；系统开启「减少动态效果」时该动画会被禁用。

```vue
<script setup lang="ts">
import { mdiLoading, mdiRefresh, mdiSync } from '@mdi/js'

const spinning = [
  { path: mdiLoading, color: 'primary' },
  { path: mdiSync, color: 'success' },
  { path: mdiRefresh, color: 'info' },
] as const
</script>

<template>
  <div class="m-row-demo">
    <MIcon
      v-for="item in spinning"
      :key="item.color"
      :path="item.path"
      :size="28"
      :color="item.color"
      :spin="true"
      :title="`${item.color} 加载中`"
    />
    <!-- 静态对照 -->
    <MIcon :path="mdiRefresh" :size="28" />
  </div>
</template>
```

## API

### Props

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `path` | `string` | `undefined` | SVG path data，通常来自 `@mdi/js`；也可传入任意 24x24 viewBox 的单条 path `d` 字符串 |
| `size` | `number \| string` | `24` | 图标尺寸，数字按 px；字符串原样输出（如 `'1.5em'`） |
| `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info'` | `undefined` | 语义色；未提供时继承 `currentColor` |
| `disabled` | `boolean` | `false` | 禁用态：使用 `--m-action-disabled` 文字色 |
| `flip` | `'horizontal' \| 'vertical' \| 'both'` | `undefined` | 翻转方向 |
| `rotate` | `number` | `0` | 旋转角度（deg），正向为顺时针 |
| `spin` | `boolean` | `false` | 自旋动画（加载、同步等场景） |
| `title` | `string` | `undefined` | 无障碍可访问名称；缺省时图标对屏幕阅读器隐藏 |

### Events

无。

### Slots

无（不支持插槽内容，图标只渲染 `path`）。

### 类型定义

```ts
export type IconColor =
  | 'primary' | 'secondary' | 'error' | 'success' | 'warning' | 'info'
export type IconFlip = 'horizontal' | 'vertical' | 'both'
```

## 使用建议

::: tip 图标名在 @mdi/js 里按需 import
`MIcon` 不内置任何图标，`path` 必须由使用方提供。安装 `@mdi/js` 后按需引入，避免整套图标进入产物：

```bash
npm i @mdi/js
```

```vue
<script setup lang="ts">
import { MIcon } from 'miao-design'
import { mdiPencilOutline } from '@mdi/js'
</script>

<template>
  <MIcon :path="mdiPencilOutline" :size="20" color="primary" title="编辑" />
</template>
```

[图标检索页](/guide/icons) 可以按名称搜索并直接复制常量名。
:::

::: tip 组件上挂的点击事件会落到 svg 上
`MIcon` 没有声明 emits，所以 `@click` 这类监听器会作为普通属性透传到根 `<svg>` 上。做可点击图标时建议外面套一层 `MButton`，语义与焦点行为更完整。
:::

::: warning 只支持单条 path
组件内部只渲染一个 `<path :d="path">`。需要多路径的图标（例如带镂空的组合图形）要自己用内联 `<svg>` 写，或者换一个单路径的图标。
:::

::: warning path 为空不会报错
`path` 为空时组件渲染一个占位 `<svg class="m-icon--placeholder">`（透明、不占视觉），而不是抛错或警告。图标没显示出来时，先确认 `@mdi/js` 的常量名拼写与 import 是否漏了。
:::

## 相关组件

- [Button 按钮](/components/button)：按钮里最常出现的图标用法，直接放进默认插槽即可对齐。
- [Fab 悬浮按钮](/components/fab)：`icon` 插槽接收任意内容，通常放一个 `MIcon`。
