`,会因继承行高使其按行盒基线下沉、与兄弟胶囊错位几像素;包裹层用 `flex items-center` + `leading-none` 即可对齐。
## 相关
[Navbar](https://hulianui.haloritual.com/components/navbar) · [BeianFooter](https://hulianui.haloritual.com/components/beian-footer) · [NavMenu](https://hulianui.haloritual.com/components/nav-menu) · [NavigationMenu](https://hulianui.haloritual.com/components/navigation-menu) · [Menu](https://hulianui.haloritual.com/components/menu) · [Menubar](https://hulianui.haloritual.com/components/menubar)
# StaggeredMenu
> 侧滑分层导航菜单 · 触发按钮(菜单↔关闭文字滚动 + 加号旋转 225°)唤起面板 · 多层色层错峰滑入做叠纸质感 + 主条目自下而上带旋转依次入场 + 序号/社交链接尾随淡入(motion 去 gsap·token·reduced-motion) · navigation/global · #animated
## 何时用
需要一个有强叠纸/错峰仪式感的侧滑全屏(或全容器)抽屉式导航时用,常见于品牌站 / 作品集的汉堡菜单。要水平常驻导航条用 [PillNav](https://hulianui.haloritual.com/components/pill-nav) 或 [GooeyNav](https://hulianui.haloritual.com/components/gooey-nav);要功能性下拉/多级菜单用 [NavigationMenu](https://hulianui.haloritual.com/components/navigation-menu) 或 [Menu](https://hulianui.haloritual.com/components/menu)。本组件内部 `absolute` 定位,需放进 `relative + 固定高度 + overflow-hidden` 的承托容器;整页罩层场景设 `isFixed`。
## 导入
```ts
import { StaggeredMenu } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| items | `StaggeredMenuItem[]` | — | 主条目列表,空数组渲染占位「No items」 |
| socialItems | `StaggeredMenuSocial[]` | — | 社交链接列表,配合 `displaySocials` 在面板底部展示 |
| position | `"left" \| "right"` | `"right"` | 面板与色层滑出方向 |
| colors | `string[]` | chart-4 / chart-1 两层 | 面板背后多层色层颜色(错峰滑入),最多取前 4 个;建议喂 `var(--color-chart-*)` token |
| displaySocials | `boolean` | `true` | 是否展示底部社交区(仅 `socialItems` 非空才实际渲染) |
| displayItemNumbering | `boolean` | `true` | 是否给主条目展示前缀序号(01 / 02 …) |
| accentColor | `string` | `var(--color-primary)` | 强调色(序号 / 社交标题 / 条目 hover) |
| isFixed | `boolean` | `false` | 是否 `fixed` 铺满视口(整页罩层);否则相对父容器铺满 |
| closeOnClickAway | `boolean` | `true` | 点击面板外区域是否关闭 |
| className | `string` | — | 透传根容器类名 |
| style | `CSSProperties` | — | 透传根容器内联样式 |
`StaggeredMenuItem`:`{ label; link?; ariaLabel? }`(`link` 缺省渲染为不可跳转 span)。`StaggeredMenuSocial`:`{ label; link }`。
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| onMenuOpen | `() => void` | 菜单打开回调 |
| onMenuClose | `() => void` | 菜单关闭回调 |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| brand | `ReactNode` | 触发按钮旁的品牌槽(通常放 logo 文字或图标),缺省渲染默认文字「瑚琏」 |
## 示例
```tsx
```
整页罩层 + 左侧滑入:
```tsx
```
## 禁忌 / 坑
- 非 `isFixed` 时必须放进 `relative + 固定高度 + overflow-hidden` 容器:组件内部 `absolute` 定位,缺承托会塌缩或溢出(showcase 用 `relative h-96 overflow-hidden`)。
- `displaySocials` 为 `true` 但 `socialItems` 为空时底部社交区不渲染,二者需同时给。
- 色层 `colors` 喂主题色请用 `var(--color-chart-*)` 这类带 `--color-` 前缀的 token;裸 `var(--primary)` 在 SVG/canvas 取色处可能不解析。
- reduced-motion 下错峰入场退化为直接显示,属预期。
## 相关
[Navbar](https://hulianui.haloritual.com/components/navbar) · [BeianFooter](https://hulianui.haloritual.com/components/beian-footer) · [NavMenu](https://hulianui.haloritual.com/components/nav-menu) · [NavigationMenu](https://hulianui.haloritual.com/components/navigation-menu) · [Menu](https://hulianui.haloritual.com/components/menu) · [Menubar](https://hulianui.haloritual.com/components/menubar)
# Stepper
> 步骤条 · MUI 桥 + active/completed 走瑚琏 token · navigation/inpage · MUI 桥
## 何时用
基于 MUI 的极简横向步骤条,仅 `steps` + `activeStep` 受控展示当前进度。新代码优先用零依赖、功能更全(垂直/状态派生/可点击/描述/图标)的 [Steps](https://hulianui.haloritual.com/components/steps);本组件保留作 MUI 桥过渡,需要 MUI 主题一致性时才用。
## 导入
```ts
import { Stepper } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| steps* | `StepItem[]` | — | 步骤数组,每项 `{ label: ReactNode }`。 |
| activeStep* | `number` | — | 受控当前步(0-based)。值 ≥ steps.length 表示全部完成。 |
| className | `string` | — | — |
## 示例
```tsx
const steps = [{ label: "下单" }, { label: "付款" }, { label: "发货" }, { label: "完成" }];
```
## 禁忌 / 坑
- `activeStep` 是纯受控、0-based;index < activeStep 显示为已完成,== 为进行中。要表达「全部完成」传 `activeStep={steps.length}`。
- 它是 MUI 桥(取非 overlay 件),样式经 emotion theme 读瑚琏 token;功能远弱于 Steps,能用 Steps 就别用它。
## 相关
[Tabs](https://hulianui.haloritual.com/components/tabs) · [Breadcrumb](https://hulianui.haloritual.com/components/breadcrumb) · [Pagination](https://hulianui.haloritual.com/components/pagination) · [Anchor](https://hulianui.haloritual.com/components/anchor) · [Affix](https://hulianui.haloritual.com/components/affix) · [BackTop](https://hulianui.haloritual.com/components/back-top)
# Steps
> 步骤条(原生) · 零依赖数据驱动 items + 水平/垂直 + wait/process/finish/error 状态派生 + 可点击受控(替代 MUI Stepper 桥 · 分步表单/审批流) · navigation/inpage
## 何时用
零依赖、数据驱动的步骤条,用于分步表单、订单流转、审批流的进度展示,支持水平/垂直、状态派生、可点击受控、描述/图标。是 [Stepper](https://hulianui.haloritual.com/components/stepper)(MUI 桥)的首选替代——新代码一律用 Steps,功能更全且不引 MUI。
## 导入
```ts
import { Steps } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| items* | `StepsItem[]` | — | 步骤数组,见下表。 |
| current | `number` | `0` | 当前步骤索引(从 0 起),用于派生各步状态。 |
| status | `"process" \| "finish" \| "error"` | `"process"` | 当前步(index===current)的状态。 |
| direction | `"horizontal" \| "vertical"` | `"horizontal"` | 排布方向。 |
| size | `"sm" \| "md"` | `"md"` | 尺寸。 |
| className | `string` | — | — |
**StepsItem**
| 字段 | 类型 | 说明 |
|------|------|------|
| title* | `ReactNode` | 步骤标题。 |
| description | `ReactNode` | 标题下方次要文案。 |
| icon | `ReactNode` | 自定义指示器内容(覆盖默认序号/状态图标)。 |
| status | `"wait" \| "process" \| "finish" \| "error"` | 显式状态,覆盖由 current 派生的状态。 |
| disabled | `boolean` | 禁用:不可点击、降透明度。 |
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| onChange | `(index: number) => void` | 提供则每步可点击,点击非禁用步触发(index)。 |
## 示例
```tsx
const ORDER = [
{ title: "提交申请", description: "填写报销单据" },
{ title: "部门审批", description: "主管复核金额" },
{ title: "财务打款", description: "对公转账" },
{ title: "完成", description: "归档结案" },
];
// 静态进度
// 可点击 + 受控
const [current, setCurrent] = useState(1);
```
## 禁忌 / 坑
- 步状态默认由 `current` 派生:index < current → finish,== current → `status`(默认 process),> current → wait;要单独标某步可在该 `StepsItem.status` 显式覆盖。
- 传了 `onChange` 才可点击;点击禁用步(`disabled`)不触发回调。
## 相关
[Tabs](https://hulianui.haloritual.com/components/tabs) · [Breadcrumb](https://hulianui.haloritual.com/components/breadcrumb) · [Pagination](https://hulianui.haloritual.com/components/pagination) · [Anchor](https://hulianui.haloritual.com/components/anchor) · [Affix](https://hulianui.haloritual.com/components/affix) · [BackTop](https://hulianui.haloritual.com/components/back-top)
# Tabs
> 选项卡 · Base UI 无浮层 + underline/solid 滑块 · navigation/inpage
## 何时用
同一区域内切换若干并列内容面板(账户/密码/团队),内容互斥、平级、无层级。表达页面在站点中的位置用 [Breadcrumb](https://hulianui.haloritual.com/components/breadcrumb);长文内随阅读进度高亮的目录用 [Anchor](https://hulianui.haloritual.com/components/anchor);有序步骤流程用 [Stepper](https://hulianui.haloritual.com/components/stepper)。
## 导入
```ts
import { Tabs, TabsList, TabsTab, TabsPanel, tabsListVariants } from "@hulianui/ui"
```
## Props
`Tabs` 透传 Base UI `Tabs.Root`(`value`/`defaultValue`/`onValueChange`/`orientation`),默认非受控。皮肤变体在 `TabsList` 上。
### Tabs(根)
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| value | `any` | — | 受控当前 tab |
| defaultValue | `any` | — | 非受控初始 tab |
| orientation | `"horizontal" \| "vertical"` | `"horizontal"` | 方向 |
### TabsList
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| variant | `"underline" \| "solid"` | `"underline"` | 皮肤:下划滑块 / 实心药丸 |
| className | `string` | — | — |
`TabsTab` 接 `value`(必填)、`disabled`、`className`;`TabsPanel` 接 `value`、`className`。
## Events
### Tabs(根)
| 事件 | 类型 | 说明 |
|------|------|------|
| onValueChange | `(value) => void` | 切换回调(透传 Base UI `Tabs.Root`) |
## 示例
```tsx
账户
密码
团队
管理你的账户资料与偏好设置。
在这里修改登录密码。
邀请成员、分配角色。
```
## 禁忌 / 坑
- [[base-ui-tabs-indicator-slider-via-active-tab-css-vars]]:滑块靠 Base UI 写在 indicator 上的 `--active-tab-*` CSS 变量 + 纯 CSS transition 实现,不引动画库。坑点:激活态钩子是 `data-active` 而非 `data-selected`,写错样式不生效;jsdom 单测无 ResizeObserver 也能跑(指示条几何不会真渲染)。
## 相关
[Breadcrumb](https://hulianui.haloritual.com/components/breadcrumb) · [Pagination](https://hulianui.haloritual.com/components/pagination) · [Anchor](https://hulianui.haloritual.com/components/anchor) · [Affix](https://hulianui.haloritual.com/components/affix) · [BackTop](https://hulianui.haloritual.com/components/back-top) · [Stepper](https://hulianui.haloritual.com/components/stepper)
# Toolbar
> 工具栏 · Base UI role=toolbar + 键盘漫游 + Button/Group/Separator · navigation/action
## 何时用
一排相关的操作控件(富文本格式、对齐、分享等)横/竖排,带 `role=toolbar` 与方向键漫游焦点。需要点击弹出的命令清单用 [ContextMenu](https://hulianui.haloritual.com/components/context-menu);需要 ⌘K 搜索式命令面板用 [Command](https://hulianui.haloritual.com/components/command);Toolbar 是常驻可见的一排按钮/开关。
## 导入
```ts
import { Toolbar, ToolbarButton, ToolbarToggle, ToolbarGroup, ToolbarSeparator } from "@hulianui/ui"
```
## Props
**Toolbar**
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| orientation | `"horizontal" \| "vertical"` | `"horizontal"` | 排布方向。 |
| disabled | `boolean` | — | 整条禁用。 |
| loopFocus | `boolean` | `true` | 键盘导航到末端时是否回环。 |
| aria-label | `string` | — | 工具栏无障碍标签。 |
| className | `string` | — | — |
**ToolbarButton**
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| disabled | `boolean` | — | — |
| aria-label | `string` | — | 图标按钮需补无障碍标签。 |
| className | `string` | — | — |
**ToolbarToggle**
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| pressed | `boolean` | — | 受控选中态(按下=开)。 |
| defaultPressed | `boolean` | `false` | 非受控初始选中态。 |
| disabled | `boolean` | — | — |
| aria-label | `string` | — | — |
| className | `string` | — | — |
**ToolbarGroup**:Props `disabled` / `aria-label` / `className`;Slots `children`。
**ToolbarSeparator**:Props `orientation?: "horizontal" \| "vertical"` / `className`。
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| onClick | `(e: MouseEvent
) => void` | `ToolbarButton` 点击回调。 |
| onPressedChange | `(pressed: boolean) => void` | `ToolbarToggle` 选中态变化回调。 |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| children | `ReactNode` | `Toolbar` / `ToolbarButton` / `ToolbarToggle` / `ToolbarGroup` 各自的子内容。 |
## 示例
```tsx
分享
```
## 禁忌 / 坑
- 纯图标的 `ToolbarButton`/`ToolbarToggle` 必须补 `aria-label`,否则屏幕阅读器读不出。
- `ToolbarToggle` 受控(`pressed`)与非受控(`defaultPressed`)二选一,混用会导致状态不一致。
## 相关
[Command](https://hulianui.haloritual.com/components/command) · [ContextMenu](https://hulianui.haloritual.com/components/context-menu) · [Accordion](https://hulianui.haloritual.com/components/accordion) · [Collapsible](https://hulianui.haloritual.com/components/collapsible) · [Link](https://hulianui.haloritual.com/components/link) · [AnimatedThemeToggler](https://hulianui.haloritual.com/components/animated-theme-toggler)
# ━━━━━━━━ 反馈 ━━━━━━━━
# Alert
> 提示条 · tone×variant 皮肤 + a11y role · feedback/message
## 何时用
页面/区块内的静态提示卡片(表单校验汇总、状态说明、内联警告),常驻直到调用方移除。横贯容器顶部全宽公告 bar 用 [Banner](https://hulianui.haloritual.com/components/banner);命令式自动消失的轻提示用 [Toast](https://hulianui.haloritual.com/components/toast);四角弹出的富通知用 [Notification](https://hulianui.haloritual.com/components/notification)。
## 导入
```ts
import { Alert, alertVariants } from "@hulianui/ui"
```
## Props
继承 `HTMLAttributes`(除 `title`)+ `alertVariants` 的变体:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| tone | `"neutral"|"info"|"success"|"warning"|"danger"` | `"info"` | 语气色 |
| variant | `"soft"|"outline"` | `"soft"` | soft=浅底 / outline=描边 |
| closeLabel | `string` | `"关闭"` | 关闭按钮的无障碍标签 |
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| onClose | `() => void` | 传入则渲染右上角关闭按钮,点击触发回调(关闭/消隐由调用方控制) |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| icon | `ReactNode` | 可选图标 slot(调用方自带 SVG/emoji;设计系统不绑图标库) |
| title | `ReactNode` | 标题(可选);children 为正文 description |
| action | `ReactNode` | 右侧动作 slot(如 ``),与关闭按钮并排 |
## 示例
```tsx
个人资料已保存。
setShown(false)} action={}>
出现连接问题,请稍后重试。
```
## 禁忌 / 坑
- `onClose` 只触发回调、不自行消隐,是否移除/隐藏由调用方控制(受控);不传 `onClose` 则不渲染关闭按钮。
- 不绑图标库,`icon` 须调用方自带 SVG/emoji。
- 接口已 `Omit<…, "title">` 避开 HTML 原生 title 属性与 ReactNode title 冲突,直接传 `title` 即为标题。
## 相关
[Banner](https://hulianui.haloritual.com/components/banner) · [Toast](https://hulianui.haloritual.com/components/toast) · [Notification](https://hulianui.haloritual.com/components/notification) · [ServiceMessage](https://hulianui.haloritual.com/components/service-message) · [Result](https://hulianui.haloritual.com/components/result) · [GiftFeed](https://hulianui.haloritual.com/components/gift-feed)
# AlertDialog
> 确认对话框 · Base UI 强制决策(不点遮罩/Esc 关) + Dialog 引擎 · feedback/overlay
## 何时用
破坏性/不可逆操作前强制用户明确决策时用(删除、清空、退出未保存),点遮罩和 Esc **不会**关闭,必须点取消或确认。普通可随手关的对话框用 [Dialog](https://hulianui.haloritual.com/components/dialog);一行命令式确认用 [Modal](https://hulianui.haloritual.com/components/modal)。
## 导入
```ts
import { AlertDialog, AlertDialogTrigger, AlertDialogClose, AlertDialogContent } from "@hulianui/ui"
```
## Props
`AlertDialog` / `AlertDialogTrigger` / `AlertDialogClose` 为 Base UI AlertDialog 对应原语薄包(Trigger/Close 可传 `className` 或 `render` 接管元素)。`AlertDialogContent` 为瑚琏皮肤:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `AlertDialogContent.className` | `string` | — | 内容容器类名 |
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| `AlertDialog.onOpenChange` | `(open: boolean) => void` | 开关态变化回调(透传 Base UI AlertDialog Root) |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| `AlertDialogContent.title` * | `ReactNode` | 标题(a11y label,必填) |
| `AlertDialogContent.description` | `ReactNode` | 说明文案 |
| `AlertDialogContent.children` | `ReactNode` | 底部操作区,放「取消 / 确认」按钮(取消用 `AlertDialogClose`) |
## 示例
```tsx
删除项目
取消
删除
```
## 禁忌 / 坑
- 故意不响应点遮罩/Esc 关闭——这是 AlertDialog 的设计意图(强制决策);想要可随手关闭就改用 Dialog,别在这里恢复轻关闭行为。
- 取消按钮必须用 `AlertDialogClose` 才能关闭弹窗;确认按钮通常也用 `AlertDialogClose` 包裹并在其 onClick 执行操作。
## 相关
[Dialog](https://hulianui.haloritual.com/components/dialog) · [Modal](https://hulianui.haloritual.com/components/modal) · [Drawer](https://hulianui.haloritual.com/components/drawer) · [Popover](https://hulianui.haloritual.com/components/popover) · [Tooltip](https://hulianui.haloritual.com/components/tooltip) · [HoverCard](https://hulianui.haloritual.com/components/hover-card)
# Banner
> 公告条 · 横贯容器顶部全宽 bar(站点维护/促销/版本更新) · 6 语气×soft/solid + 前导图标/操作区/可关闭 + 居中或左对齐 + 长文案 dogfood Marquee 单行无缝滚动 · 区别 Alert(局部卡片)/Notification(命令式四角) · feedback/message
## 何时用
横贯容器顶部的全宽公告 bar(站点维护、促销、版本更新),全局级、横向铺满。局部区块内的静态提示卡片用 [Alert](https://hulianui.haloritual.com/components/alert);命令式自动消失的轻提示用 [Toast](https://hulianui.haloritual.com/components/toast);四角弹出的富通知用 [Notification](https://hulianui.haloritual.com/components/notification)。
## 导入
```ts
import { Banner } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| tone | `"neutral"|"info"|"brand"|"success"|"warning"|"danger"` | `"info"` | 语气色 |
| variant | `"soft"|"solid"` | `"soft"` | soft=浅底 / solid=实色填充(更醒目,适合促销/重大公告) |
| align | `"start"|"center"` | `"center"` | 内容对齐 |
| scrollable | `boolean` | `false` | 文案过长时单行无缝滚动(纯 CSS marquee·hover 暂停) |
| closeLabel | `string` | — | 关闭按钮无障碍标签 |
| className | `string` | — | 额外类名 |
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| onClose | `() => void` | 传入则渲染关闭按钮 |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| icon | `ReactNode` | 前导图标(传入 svg;随 tone 自动着色) |
| children | `ReactNode` | 文案主体 |
| action | `ReactNode` | 右侧操作区(如「查看详情」链接/按钮) |
## 示例
```tsx
}>部署成功,服务已切换到新版本
}
action={立即查看}
onClose={() => setOpen(false)}
>
双十一大促开启,全场组件五折
```
## 禁忌 / 坑
- `onClose` 只触发回调、不自管显隐,显示/隐藏由调用方 state 控制(受控);不传则不渲染关闭按钮。
- `scrollable` 走纯 CSS marquee(hover 暂停),仅用于单行长文案;多行内容别开。
- `solid` 变体下 `action` 里的链接用 `text-current` 跟随实色底的前景色,别硬编码颜色。
## 相关
[Alert](https://hulianui.haloritual.com/components/alert) · [Toast](https://hulianui.haloritual.com/components/toast) · [Notification](https://hulianui.haloritual.com/components/notification) · [ServiceMessage](https://hulianui.haloritual.com/components/service-message) · [Result](https://hulianui.haloritual.com/components/result) · [GiftFeed](https://hulianui.haloritual.com/components/gift-feed)
# Dialog
> 对话框 · Base UI Portal + focus trap · feedback/overlay
## 何时用
需要打断当前流程、在遮罩层上承载表单/详情/确认内容时用,自带 Portal + 焦点锁 + Esc 关闭。命令式一行弹(confirm/info/success)用 [Modal](https://hulianui.haloritual.com/components/modal);强制决策不让点遮罩关用 [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog);侧滑面板用 [Drawer](https://hulianui.haloritual.com/components/drawer)。
## 导入
```ts
import { Dialog, DialogTrigger, DialogClose, DialogContent } from "@hulianui/ui"
```
## Props
`Dialog` / `DialogTrigger` / `DialogClose` 为 Base UI Dialog 对应原语薄包(`Dialog` 透传 Root 的 `open`/`defaultOpen`/`onOpenChange` 等;`DialogTrigger`/`DialogClose` 支持 `render` 接管渲染元素)。`DialogContent` 为瑚琏皮肤:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `DialogContent.title` * | `string` | — | 标题(a11y label) |
| `DialogContent.description` | `string` | — | 说明文案 |
| `DialogContent.className` | `string` | — | 内容容器类名 |
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| `Dialog.onOpenChange` | `(open: boolean) => void` | 开关态变化回调(透传 Base UI Dialog Root) |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| `DialogContent.footer` | `ReactNode` | 底部操作区,渲染在正文下方,顶部分隔线 + 右对齐,与 DrawerContent 对齐 |
| `DialogContent.children` | `ReactNode` | 正文内容 |
## 示例
```tsx
```
## 禁忌 / 坑
- `DialogTrigger` / `DialogClose` 用 `render={}` 把自家行为合并到目标元素上,**不要**再额外包一层按钮,否则会出现嵌套交互元素 / 双重 onClick。
- 操作按钮优先放 `footer` 槽(带分隔线、右对齐),正文 `children` 留给主内容。
## 相关
[Modal](https://hulianui.haloritual.com/components/modal) · [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog) · [Drawer](https://hulianui.haloritual.com/components/drawer) · [Popover](https://hulianui.haloritual.com/components/popover) · [Tooltip](https://hulianui.haloritual.com/components/tooltip) · [HoverCard](https://hulianui.haloritual.com/components/hover-card)
# Drawer
> 抽屉 · Base UI Dialog 引擎 + 四向侧滑 · feedback/overlay
## 何时用
需要从屏幕某一边滑入的面板(筛选器、详情、表单、移动端菜单)时用,正文可独立滚动、操作按钮钉底。居中弹出的对话框用 [Dialog](https://hulianui.haloritual.com/components/dialog);强制决策用 [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog);锚定小浮层用 [Popover](https://hulianui.haloritual.com/components/popover)。
## 导入
```ts
import { Drawer, DrawerTrigger, DrawerClose, DrawerContent, drawerVariants } from "@hulianui/ui"
```
## Props
`Drawer` 透传 Base UI Dialog Root(`open`/`defaultOpen`/`onOpenChange` 等);`DrawerTrigger`/`DrawerClose` 支持 `render` 接管元素。`DrawerContent` 为瑚琏皮肤:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `DrawerContent.side` | `"left" \| "right" \| "top" \| "bottom"` | `"right"` | 贴边方向 + 对应滑入方向 |
| `DrawerContent.container` | `Element \| Ref` | — | 就地挂载目标;提供后 portal 进该容器并改用 absolute 贴其边(容器须 `position:relative` + `overflow-hidden`),用于手机框预览等局部容器 |
| `DrawerContent.className` | `string` | — | 内容容器类名 |
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| `Drawer.onOpenChange` | `(open: boolean) => void` | 开关态变化回调(透传 Base UI Dialog Root) |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| `DrawerContent.title` | `ReactNode` | 提供则渲 Dialog.Title 作 a11y label |
| `DrawerContent.description` | `ReactNode` | 说明文案 |
| `DrawerContent.footer` | `ReactNode` | 钉底操作区(带分隔线,正文独立滚动,footer 始终可见) |
| `DrawerContent.children` | `ReactNode` | 正文内容 |
## 示例
```tsx
打开抽屉} />
取消} />
保存} />
>
}
>
{/* 长正文自动滚动,footer 钉底 */}
```
## 禁忌 / 坑
- Base UI rc.0 没有独立 Drawer 原语,本组件是 Dialog(Portal+Backdrop+Popup)重皮、靠 `transform: translateX/Y` 按 `side` 侧滑;Dialog 没有 Positioner,定位别按 Tooltip/Popover 那套想。详见 [[base-ui-dialog-drawer-side-slide-via-transform]]。
- 「取消 / 保存 / 关闭」放 `footer` 槽而非正文末尾,否则正文滚动后按钮会滚出可视区。
- 用 `container` 收进局部容器(如手机框)时,容器必须 `position:relative` + `overflow-hidden`,否则抽屉会逃逸出框、遮罩盖满整屏。
## 相关
[Dialog](https://hulianui.haloritual.com/components/dialog) · [Modal](https://hulianui.haloritual.com/components/modal) · [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog) · [Popover](https://hulianui.haloritual.com/components/popover) · [Tooltip](https://hulianui.haloritual.com/components/tooltip) · [HoverCard](https://hulianui.haloritual.com/components/hover-card)
# FloatingReactions
> 飘心点赞 · 命令式 ref.emit(content,{count}) 从底部喷射上浮表情 + 随机起点/横向漂移/缩放/时长扰动 + 上浮渐隐(keyframe)动画结束自移除 + palette 随机池 + rise/drift/duration 可调(forwardRef+useImperativeHandle·pointer-events none·可复用于任意点赞钮) · feedback/message · #animated
## 何时用
点赞/互动时从底部喷射上浮飘心表情的特效层用它:命令式 `ref.emit()` 触发,随机起点/漂移/缩放、上浮渐隐后自移除。它是**叠加特效层**(`pointer-events:none`),只管单个表情上浮动画;要做礼物连击合并计数横幅用 [GiftFeed](https://hulianui.haloritual.com/components/gift-feed)。
## 导入
```ts
import { FloatingReactions } from "@hulianui/ui"
```
## Props
通过 `ref` 取 `FloatingReactionsHandle`,命令式调用 `emit(content?, opts?: { count?: number })`(不传 content 时从 palette 随机取)。
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| palette | `ReactNode[]` | 一组心 | 不传 content 时随机取的表情池 |
| rise | `number` | `220` | 上浮高度 px |
| drift | `number` | `40` | 横向漂移幅度 px(左右随机) |
| duration | `number` | `2200` | 单个动画时长 ms |
| size | `number` | `24` | 字号 px |
| className | `string` | — | 容器类名 |
## 示例
```tsx
const ref = useRef(null);
```
## 禁忌 / 坑
- 命令式 API:必须经 `ref` 调 `emit`,没有 props 控制触发。容器须 `position:relative` 且建议 `overflow-hidden`,否则上浮表情会溢出可视区。
- 表情元素 `pointer-events:none`,不会拦截点击;放在按钮上方安全。
- 暂无其它已知坑。
## 相关
[Alert](https://hulianui.haloritual.com/components/alert) · [Banner](https://hulianui.haloritual.com/components/banner) · [Toast](https://hulianui.haloritual.com/components/toast) · [Notification](https://hulianui.haloritual.com/components/notification) · [ServiceMessage](https://hulianui.haloritual.com/components/service-message) · [Result](https://hulianui.haloritual.com/components/result)
# GiftFeed
> 礼物连击 · 受控礼物事件流 + 同 id 连击合并 combo ×N 滚动弹跳计数(纯函数 applyGiftEvent 可测) + 横幅左滑入/自动消散计时(每连击重置) + max 同屏上限挤旧 + dogfood Avatar · 直播打赏特效(pointer-events none) · feedback/message · #animated
## 何时用
直播间/互动场景展示打赏礼物连击横幅时用:同一用户对同一礼物快速连点合并为 `combo ×N` 弹跳计数、自动消散、同屏上限挤旧。它是**叠在内容之上的特效层**(`pointer-events:none`);通用结果反馈用 [Result](https://hulianui.haloritual.com/components/result),普通通知用 [Notification](https://hulianui.haloritual.com/components/notification)。配合 [FloatingReactions](https://hulianui.haloritual.com/components/floating-reactions) 做点赞飘心。
## 导入
```ts
import { GiftFeed, applyGiftEvent } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| events* | `GiftEvent[]` | — | 受控礼物事件流(追加)。`GiftEvent` = `{id, user:{name,avatar?}, gift:{name,icon?,color?}, combo?}`,同 `id` 再次传入视为同一连击 |
| max | `number` | `3` | 同时显示横幅上限(超出挤掉最旧) |
| duration | `number` | `4000` | 单条无新连击后停留 ms |
| className | `string` | — | 容器类名 |
`applyGiftEvent` 为纯函数:把一个 `GiftEvent` 归并进当前在屏横幅数组(同 id 合并、combo 递增、超 max 挤旧),可单独单元测试。
## 示例
```tsx
const [events, setEvents] = useState([]);
// 收到打赏时追加事件(combo 由调用方维护)
function onGift(g: GiftEvent) {
setEvents((prev) => [...prev.slice(-30), g]);
}
```
## 禁忌 / 坑
- `events` 是「追加流」语义:靠 `id` 区分是否同一连击,同 id 才合并 combo。不同礼物务必给不同 `id`,否则会被误并为连击。
- `combo` 由调用方维护并递增(组件只负责动画呈现);不传则按出现次数自增。
- 注意 `events` 数组无限增长会占内存,示例里用 `.slice(-30)` 截断;生产同理别让它无界累积。
- 暂无其它已知坑。
## 相关
[Alert](https://hulianui.haloritual.com/components/alert) · [Banner](https://hulianui.haloritual.com/components/banner) · [Toast](https://hulianui.haloritual.com/components/toast) · [Notification](https://hulianui.haloritual.com/components/notification) · [ServiceMessage](https://hulianui.haloritual.com/components/service-message) · [Result](https://hulianui.haloritual.com/components/result)
# Glimpse
> 链接预览 · dogfood HoverCard 引擎换皮成「封面图+标题+描述+域名」预览卡(维基式 hover preview) · 触发器随 href 渲染外链 a 或纯 span 保持行内排版 · 描述多行截断 · feedback/overlay
## 何时用
行文中悬停链接/术语时弹出「封面图 + 标题 + 描述 + 域名」预览卡(维基式 hover preview),不打断阅读。需要完全自定义卡片内容用底层 [HoverCard](https://hulianui.haloritual.com/components/hover-card);纯文本短提示用 [Tooltip](https://hulianui.haloritual.com/components/tooltip)。传 `href` 则触发器渲染为新标签页外链并在卡底显示域名,不传则渲染纯 `span` 保持行内排版。
## 导入
```ts
import { Glimpse } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| image | `string` | — | 预览图 URL(顶部封面) |
| href | `string` | — | 链接地址;传入则触发器渲染为新标签页外链,卡底显示域名 |
| side | `"top"|"right"|"bottom"|"left"` | `"bottom"` | 浮层方位 |
| align | `"start"|"center"|"end"` | — | 对齐 |
| openDelay | `number` | `300` | 悬停打开延迟(ms) |
| closeDelay | `number` | `150` | 移出关闭延迟(ms) |
| className | `string` | — | 触发器额外类名 |
| contentClassName | `string` | — | 预览卡片额外类名 |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| children* | `ReactNode` | 触发元素(行内链接文字/缩略词) |
| title | `ReactNode` | 预览标题 |
| description | `ReactNode` | 预览描述(多行截断) |
## 示例
```tsx
我们的设计系统基于{" "}
语义 token
{" "}
构建,悬停链接即可预览。
```
## 禁忌 / 坑
- 文档站门禁禁外链图片,`image` 在 showcase/demo 里用本地资源或 SVG data-uri 占位,别引远程图片。
- 不传 `href` 时触发器是纯 `span`(无跳转语义),只做术语释义;要可点击跳转必须给 `href`。
- 基于 HoverCard 引擎,hover 防误触延迟由 `openDelay`/`closeDelay` 控,别设 0。
## 相关
[Dialog](https://hulianui.haloritual.com/components/dialog) · [Modal](https://hulianui.haloritual.com/components/modal) · [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog) · [Drawer](https://hulianui.haloritual.com/components/drawer) · [Popover](https://hulianui.haloritual.com/components/popover) · [Tooltip](https://hulianui.haloritual.com/components/tooltip)
# HeroVideoDialog
> 视频弹层 · 缩略图+播放钮→Portal 模态(Esc/遮罩关 + 锁滚) · feedback/overlay
## 何时用
落地页/营销页放一张视频缩略图 + 播放钮,点击后弹出 Portal 模态播放嵌入视频(YouTube/Bilibili embed),支持 Esc/遮罩关闭并锁背景滚动。需要通用模态承载任意内容用 [Dialog](https://hulianui.haloritual.com/components/dialog);需要原生播放器全套控制(进度/倍速/PiP)用 Video 播放器组件。
## 导入
```ts
import { HeroVideoDialog } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| thumbnailSrc* | `string` | — | 缩略图地址 |
| thumbnailAlt | `string` | — | 缩略图 alt 文本 |
| videoSrc* | `string` | — | 视频嵌入地址(iframe src,如 youtube/bilibili embed) |
| className | `string` | — | 额外类名(控制缩略图尺寸等) |
## 示例
```tsx
```
## 禁忌 / 坑
- `videoSrc` 必须是 iframe **embed** 地址(`.../embed/...`),不是普通观看页 URL,否则模态内 iframe 加载失败。
- 缩略图尺寸靠 `className`(如 `w-80`)控制,组件本身不设固定宽度。
## 相关
[Dialog](https://hulianui.haloritual.com/components/dialog) · [Modal](https://hulianui.haloritual.com/components/modal) · [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog) · [Drawer](https://hulianui.haloritual.com/components/drawer) · [Popover](https://hulianui.haloritual.com/components/popover) · [Tooltip](https://hulianui.haloritual.com/components/tooltip)
# HoverCard
> 悬停卡片 · Popover 引擎自研 hover 开/移出延迟关(复刻 Tooltip delay 范式) + 富内容 · feedback/overlay
## 何时用
hover 触发展开的富内容卡片(用户名片、术语释义、链接预览),带开/关延迟避免误触。纯文本短提示用 [Tooltip](https://hulianui.haloritual.com/components/tooltip);click 触发 + 操作按钮用 [Popover](https://hulianui.haloritual.com/components/popover);专做「封面图 + 标题 + 域名」链接预览用 [Glimpse](https://hulianui.haloritual.com/components/glimpse)。
## 导入
```ts
import { HoverCard, HoverCardTrigger, HoverCardContent } from "@hulianui/ui"
```
## Props
`HoverCard`:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| openDelay | `number` | `300` | 悬停多少毫秒后打开 |
| closeDelay | `number` | `150` | 移出多少毫秒后关闭 |
`HoverCardContent`:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| side | `"top"|"right"|"bottom"|"left"` | `"bottom"` | 浮层方位 |
| align | `"start"|"center"|"end"` | `"center"` | 对齐 |
| sideOffset | `number` | — | 与触发器的间距 |
| className | `string` | — | 额外类名 |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| `HoverCard` children | `ReactNode` | Trigger + Content |
| `HoverCardContent` children | `ReactNode` | 卡片内容 |
`HoverCardTrigger` 用 `render` prop 接管触发元素(行内链接/按钮)。
## 示例
```tsx
@瑚琏设计系统}
/>
```
## 禁忌 / 坑
- 本组件已按 [[hovercard-on-focus-managing-popover-flickers-set-initial-final-focus-false]] 把焦点交还语义关掉(不在打开时夺取焦点),避免 hover + focus 在 focus-managing popover 上 ping-pong 无限闪烁;若 fork 改造别重新打开焦点托管。
- 用 `openDelay`/`closeDelay` 调防误触,别把 `closeDelay` 设 0,否则光标在 trigger 与卡片间移动的瞬间会闪关。
## 相关
[Dialog](https://hulianui.haloritual.com/components/dialog) · [Modal](https://hulianui.haloritual.com/components/modal) · [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog) · [Drawer](https://hulianui.haloritual.com/components/drawer) · [Popover](https://hulianui.haloritual.com/components/popover) · [Tooltip](https://hulianui.haloritual.com/components/tooltip)
# Modal
> 命令式对话框 · confirm/info/success/error/warning 函数式 API + Dialog 引擎 · feedback/overlay
## 何时用
逻辑流程里需要一行代码弹确认/提示(删除确认、操作结果反馈)时用,无需在 JSX 里铺 Trigger/Content。需要复杂自定义内容 / 表单的声明式弹窗用 [Dialog](https://hulianui.haloritual.com/components/dialog);强制决策的破坏性确认用 [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog)。
## 导入
```ts
import { modal, ModalProvider, hulianModalManager } from "@hulianui/ui"
```
## API / Options
调用入口:`modal.confirm(opts)` / `modal.info` / `modal.success` / `modal.error` / `modal.warning`,返回 `ModalInstance`。语调由入口隐含。`ModalProvider` 需在应用根挂一次(同 Toast 范式)。
`ModalOptions` Props:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `type` | `"confirm" \| "info" \| "success" \| "error" \| "warning"` | — | 语调;命令式入口已隐含,一般无需显式传 |
`ModalOptions` Events:
| 事件 | 类型 | 说明 |
|------|------|------|
| `onOk` | `() => void \| Promise` | 点确定回调;返回 Promise 时确定键进 loading,resolve → 自动关闭,reject → 保持打开 |
| `onCancel` | `() => void` | 点取消 / Esc / 点遮罩关闭时回调 |
`ModalOptions` Slots:
| 插槽 | 类型 | 说明 |
|------|------|------|
| `title` | `ReactNode` | 标题(加粗主行) |
| `content` | `ReactNode` | 正文内容 |
| `okText` | `ReactNode` | 确定按钮文案,默认 `"确定"` |
| `cancelText` | `ReactNode` | 取消按钮文案,默认 `"取消"`(仅 confirm 渲染取消键) |
`ModalInstance`:`destroy()` 立即关闭销毁;`update(next)` 更新已打开对话框配置。
## 示例
```tsx
// 根布局挂一次
// 任意逻辑处调用
modal.confirm({
title: "确认删除该记录?",
content: "删除后无法恢复。",
onOk: () => {},
});
// 异步确定:确定键自动 loading,resolve 后关闭
modal.confirm({
title: "提交订单?",
content: "点确定将发起请求。",
onOk: () => fetch("/api/order", { method: "POST" }),
});
```
## 禁忌 / 坑
- 必须在应用根挂一次 `ModalProvider`,否则命令式调用无处渲染;showcase 里也是 layout 单挂、触发按钮里只调 `modal.*`(同 Toast)。
- `onOk` 返回 Promise 时:resolve 才自动关闭,reject 会保持打开(由调用方提示错误)——别在 reject 分支里又手动 `destroy`。
## 相关
[Dialog](https://hulianui.haloritual.com/components/dialog) · [AlertDialog](https://hulianui.haloritual.com/components/alert-dialog) · [Drawer](https://hulianui.haloritual.com/components/drawer) · [Popover](https://hulianui.haloritual.com/components/popover) · [Tooltip](https://hulianui.haloritual.com/components/tooltip) · [HoverCard](https://hulianui.haloritual.com/components/hover-card)
# Notification
> 通知 · 四角堆叠卡片 + 图标/操作区/位置 + 命令式 API(比 Toast 重) · feedback/message
## 何时用
需要在四角弹出**带标题+描述+操作按钮**的较重通知卡片时用(比如「收到好友请求 / 上传失败可重试」)。比 [Toast](https://hulianui.haloritual.com/components/toast) 信息量大、可常驻、可带操作;只需一行短反馈、自动消失时用 Toast。命令式调用,无需在 JSX 里挂节点(但 `NotificationProvider` 须由 layout 单挂一次)。
## 导入
```ts
import { notification, NotificationProvider, hulianNotificationManager } from "@hulianui/ui"
```
## Props
`notification.success/error/info/warning/open(options)` 接收 `NotificationOptions`:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| type | `"open"|"success"|"error"|"info"|"warning"` | — | 经方法名隐含(`open` 为中性无图标);派生左侧色条 + 默认图标,token 同 Alert(info→primary) |
| duration | `number` | `4500` | 自动关闭毫秒数;`0` = 不自动关(常驻) |
| placement | `"topRight"|"topLeft"|"bottomRight"|"bottomLeft"` | `"topRight"` | 弹出位置(四角) |
调用返回 `NotificationInstance`,含 `destroy(): void` 立即关闭该通知。
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| onClose | `() => void` | 关闭时回调(自动/手动/编程关闭均触发一次) |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| title | `ReactNode` | 标题(加粗主行) |
| description | `ReactNode` | 描述(次行,恒 text-muted) |
| icon | `ReactNode` | 自定义图标,覆盖默认(不传按类型派生) |
| btn | `ReactNode` | 操作区(按钮等),渲染在描述下方 |
## 示例
```tsx
// 普通成功通知(4.5s 自动关闭)
notification.success({ title: "保存成功", description: "更改已同步。" })
// 带操作按钮 + 常驻(duration:0)
notification.open({
title: "收到一条好友请求",
description: "来自 设计部·小琏",
duration: 0,
btn: ,
})
```
## 禁忌 / 坑
- `NotificationProvider` 须在 layout 单挂一次(同 Toast/Modal 范式),不要在每个调用点或 showcase 里挂 Provider,否则通知无处渲染。
- `duration: 0` 才是常驻;不传 duration 默认 4500ms 自动关,需要用户必看的通知务必显式置 0。
- 命令式触发的副作用通知,注意「触发即忘」的回调语义——`onClose` 仅保证触发一次,别在其中做依赖外部最新状态的操作。参见 [[fire-and-forget-side-effect-notification]]。
## 相关
[Alert](https://hulianui.haloritual.com/components/alert) · [Banner](https://hulianui.haloritual.com/components/banner) · [Toast](https://hulianui.haloritual.com/components/toast) · [ServiceMessage](https://hulianui.haloritual.com/components/service-message) · [Result](https://hulianui.haloritual.com/components/result) · [GiftFeed](https://hulianui.haloritual.com/components/gift-feed)
# Popconfirm
> 气泡确认 · dogfood Popover 引擎 + 危险操作就地确认(标题/图标/确认取消) + async onConfirm loading + 受控开合 · feedback/message
## 何时用
危险/不可逆操作(删除、归档)需要就地二次确认时用:锚定到触发器旁弹出气泡,含标题/描述/确认取消按钮。比 [Toast](https://hulianui.haloritual.com/components/toast) 重、比全屏 Dialog 轻;需要居中、信息量大的确认弹窗用 Modal/AlertDialog,本组件适合表格行内、按钮旁的轻量确认。
## 导入
```ts
import { Popconfirm } from "@hulianui/ui"
```
## Props
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| danger | `boolean` | `false` | 危险操作:确认按钮 tone=danger + 默认图标转 text-danger |
| open | `boolean` | — | 受控打开态,须配合 onOpenChange |
| defaultOpen | `boolean` | `false` | 非受控初始打开态 |
| side | `"top"|"right"|"bottom"|"left"` | `"top"` | 浮层方位 |
| align | `"start"|"center"|"end"` | `"center"` | 浮层对齐 |
| sideOffset | `number` | `8` | 浮层与触发器间距 |
| disabled | `boolean` | `false` | 禁用:触发器照常渲染但不唤起浮层 |
| className | `string` | — | 透传到浮层 Popup 的类名 |
## Events
| 事件 | 类型 | 说明 |
|------|------|------|
| onConfirm | `() => void \| Promise` | 点确认回调。返回 Promise 时按钮进 loading,resolve 后自动关闭;reject 保持打开并清 loading |
| onCancel | `() => void` | 仅显式点取消按钮触发(点外部/Esc 走 onOpenChange,不触发此回调) |
| onOpenChange | `(open: boolean) => void` | 打开态变化回调(含点外部/Esc/确认/取消) |
## Slots
| 插槽 | 类型 | 说明 |
|------|------|------|
| title* | `ReactNode` | 确认主问句,串 `aria-labelledby` |
| description | `ReactNode` | 次要说明,串 `aria-describedby` |
| icon | `ReactNode` | `undefined`=默认警示三角;`null`=不渲染;ReactNode=自定义。默认色随 danger 切换 |
| okText | `ReactNode` | 确认按钮文案(默认「确认」) |
| cancelText | `ReactNode` | 取消按钮文案(默认「取消」) |
| children* | `ReactElement` | 触发器(单个元素,浮层锚定到它) |
## 示例
```tsx
// 危险删除确认
{}}>
// 异步确认(loading 后自动关闭)
{ await api.archive(id); }}
>
```
## 禁忌 / 坑
- `onConfirm` 返回 Promise 时:resolve 才自动关闭,reject **保持打开并清 loading**,错误反馈(如弹 Toast)须由调用方自己负责。
- `onCancel` 只在显式点「取消」按钮时触发;点外部/按 Esc 关闭只走 `onOpenChange`,别在 onCancel 里做必跑的清理。
- `children` 必须是**单个 ReactElement**(浮层要锚定它),不能传文本或 Fragment。
- 受控用法须 `open` + `onOpenChange` 成对;只传 `open` 不传 onOpenChange 会卡死无法关闭。
## 相关
[Alert](https://hulianui.haloritual.com/components/alert) · [Banner](https://hulianui.haloritual.com/components/banner) · [Toast](https://hulianui.haloritual.com/components/toast) · [Notification](https://hulianui.haloritual.com/components/notification) · [ServiceMessage](https://hulianui.haloritual.com/components/service-message) · [Result](https://hulianui.haloritual.com/components/result)
# Popover
> 气泡卡片 · click 触发 + 标题/描述/Close · feedback/overlay
## 何时用
click 触发的轻量浮层,承载标题/描述/少量操作(确认、快捷设置、表单片段),点外部或 Esc 关闭。需要纯文本提示用 [Tooltip](https://hulianui.haloritual.com/components/tooltip)(hover 触发);需要 hover 展开富内容卡片用 [HoverCard](https://hulianui.haloritual.com/components/hover-card);需要遮罩 + 焦点锁定的强中断流程用 [Dialog](https://hulianui.haloritual.com/components/dialog)。
## 导入
```ts
import { Popover, PopoverTrigger, PopoverClose, PopoverContent } from "@hulianui/ui"
```
## Props
`PopoverContent`:
| 名称 | 类型 | 默认 | 说明 |
|------|------|------|------|
| side | `"top"|"right"|"bottom"|"left"` | `"bottom"` | 浮层方位 |
| align | `"start"|"center"|"end"` | `"center"` | 对齐 |
| sideOffset | `number` | — | 与触发器的间距 |
| className | `string` | — | 额外类名 |
## Slots
`PopoverContent`:
| 插槽 | 类型 | 说明 |
|------|------|------|
| title | `ReactNode` | 标题 |
| description | `ReactNode` | 描述 |
| children | `ReactNode` | 正文/操作区内容 |
`PopoverTrigger` / `PopoverClose` 用 `render` prop 接管自定义触发/关闭元素(如 `render={}`)。
## 示例
```tsx
打开弹层} />
```
## 禁忌 / 坑
- 触发/关闭走 `render` prop 注入元素,别再在 `PopoverTrigger` 里二次嵌套交互元素,避免 `