# 浮层 Provider

## 使用方法

浮层这一套东西有一个应用级的 provider。要配 toast 或者整个应用的浮层参数，就在应用最外层放一个 `LayerProvider`；一般的浮层界面直接用 `Popover`、`HoverCard`、`Tooltip` 这些更上层的组件就行。浮层内容渲染出来之后，用的是主题里正文的默认文字样式，也不再继承外面那层 surface/group 的成员关系，所以每个浮层里该有的分组和必需的 provider 都要自己补上。每个贴着触发器定位的浮层，离视口四条边都留着一段空隙：间距第 4 档和设备安全区取较大的那个；它只受视口限制，不会被触发器旁边的空间限制。

如果应用有一条常驻的条子浮在视口某条边上，就在 `LayerProvider` 上用 `inset` 声明一次，写明 `blockStart`、`blockEnd`、`inlineStart`、`inlineEnd`。每条边上的值会加到所有贴边浮层离那条边的空隙上，toast 视口也按同样的量挪进去。不写就是每条边都是 0。这份 inset 属于应用配置，不是主题里的值。

```ts
import { LayerProvider } from '@astryx-vue/core/LayerProvider'
```

`toast` 里有三样：`position` 是 toast 视口贴哪个角，`maxVisible` 是同时最多显示几条，还有一个只对 toast 生效的 `inset`，会逐边盖掉 provider 上的同一条边。

嵌套的 `LayerProvider` 是个例外：它不会再开一套 toast 视口，但如果它自己声明了 `inset`，这份 inset 只收紧它子树里的锚定浮层，toast 视口仍然看最外层。不声明 `inset` 的内层 provider 什么也不做。

## 建议

| Guidance | Practices |
| --- | --- |
| Do | 视口边上有常驻条子时，在 `LayerProvider` 的 `inset` 里声明一次，之后所有贴边浮层和 toast 都会绕开它。 |
| Do | 需要全局共享 toast 或浮层配置时，在应用根部附近放一个 `LayerProvider`。 |
| Do | 常见的浮层场景用更上层的 `Popover`、`HoverCard`、`Tooltip`，不用自己拼。 |
| Don't | 不要嵌套 `LayerProvider`：内层不再开 toast 视口，只是白白多一层；确实要给某块子树单独声明常驻条时，才用内层的 `inset`。 |

## 示例

常见的用法和不同的状态。

### LayerProvider — Toast Viewport

把 toast 视口钉在右上角，最多同时显示两条，并往下挪 8px，给顶部那条常驻栏让位。

```vue
<script setup lang="ts">
import { LayerProvider } from '@astryx-vue/core/LayerProvider'
import { ToastViewport, useToast } from '@astryx-vue/core/Toast'

const toast = useToast()

// Pin the toast viewport to the top end, keep two on screen, and clear the top edge by 8px.
const toastConfig = {
  position: 'top-end' as const,
  maxVisible: 2,
  inset: { blockStart: 8 },
}

function showToasts() {
  toast({ body: 'Draft saved.', type: 'success' })
  toast({ body: 'Syncing 3 files…' })
  toast({ body: 'Storage is nearly full.', type: 'warning' })
}
</script>

<template>
  <div class="layer-provider-preview">
    <LayerProvider :toast="toastConfig">
      <button class="layer-provider-preview__trigger" type="button" @click="showToasts">
        Show toasts
      </button>
      <ToastViewport />
    </LayerProvider>
  </div>
</template>
```

## 属性

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `toast` | `LayerToastConfig` | — | toast 视口的配置：`position` 贴哪个角、`maxVisible` 最多显示几条，还有一个只对 toast 生效的 `inset`，会逐边盖掉 provider 上的同一条边。 |
| `inset` | `LayerInset` | 每条边 `0` | 浮在视口某条边上的常驻条，声明一次就够：`blockStart`、`blockEnd`、`inlineStart`、`inlineEnd`，写数字就是像素，也可以写 CSS 长度。每条边会加到所有贴边浮层离那条边的空隙上，toast 视口也按同样的量挪进去。 |

## 插槽

| 插槽 | 说明 |
| --- | --- |
| `default` | 应用子树，可以读取这份共享的浮层配置。 |

## LayerToastConfig

| 字段 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `position` | `ToastPlacement` | `'bottom-end'` | toast 视口贴在哪个角：`top-start`、`top-center`、`top-end`、`bottom-start`、`bottom-center`、`bottom-end`。直接写在 `ToastViewport` 上的 `placement` 优先。 |
| `maxVisible` | `number` | 不限制 | 同时最多显示几条。超出的先不渲染，只留最新的那几条。 |
| `inset` | `LayerInset` | — | 只对 toast 视口生效的逐边偏移。这条边写了就用它，没写的边继续用 provider 的 `inset`。 |

## LayerInset

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `blockStart` | `number \| string` | 加在顶部空隙上的偏移。数字按像素算，字符串可以是任意 CSS 长度，例如 `'4rem'` 或 `'env(safe-area-inset-top, 0px)'`。 |
| `blockEnd` | `number \| string` | 同上，底部那条边。 |
| `inlineStart` | `number \| string` | 同上，行首那条边。 |
| `inlineEnd` | `number \| string` | 同上，行尾那条边。 |

## 其它导出

| 导出 | 类型 | 说明 |
| --- | --- | --- |
| `useLayerConfig` | `() => ComputedRef \| null` | 读最近一层 provider 的配置，外层没有就是 `null`。 |
| `useLayerGutter` | `() => ComputedRef` | 贴边浮层离视口每条边的距离：间距第 4 档和设备安全区取大，再加上 provider 的偏移。 |
| `resolveInset` | `(inset?: LayerInset) => ResolvedLayerInset` | 把一份 inset 换算成像素。 |
| `mergeInset` | `(primary?: LayerInset, fallback?: LayerInset) => ResolvedLayerInset` | 逐边合并两份 inset：`primary` 写了的边用它，剩下的从 `fallback` 来。 |

## 原生属性

组件本身不渲染元素，<code>class</code> 和 <code>style</code> 不会传下去。
