跳到主要内容

Sheet (Drawer)

  • 组件说明:侧边面板,用于表单、详情视图或辅助内容展示。支持四个方向:上、右(默认)、下、左。基于 Radix Dialog。别名 Drawer
  • 当前状态:支持声明式(trigger 作为 children)和配置式(title/content/footer)两种模式,以及命令式 Sheet.open() API。
  • 实现约定ui/sheet 封装 Radix Dialog primitive,design 层提供配置式 API、方向/宽度控制与命令式弹层能力。

基础用法

通过 props 传入 titlecontentfooter,触发器作为 children 传入。

结果
Loading...
实时编辑器
render(
  <div style={{ padding: '40px 0' }}>
    <Sheet
      title="个人资料"
      content={
        <div style={{ padding: '0 16px', flex: 1 }}>
          <p style={{ fontSize: 14, color: '#6b7280', marginBottom: 8 }}>查看和编辑个人信息。</p>
          <p>姓名:张三</p>
          <p>邮箱:zhangsan@example.com</p>
        </div>
      }
      footer={
        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <Button variant="secondary">取消</Button>
          <Button>保存</Button>
        </div>
      }
    >
      <Button>打开面板</Button>
    </Sheet>
  </div>,
)

方向变体

通过 side 控制面板滑出方向,默认为 right

结果
Loading...
实时编辑器
render(
  <div style={{ display: 'flex', gap: 12, padding: '40px 0', flexWrap: 'wrap' }}>
    <Sheet side="right" title="右侧面板" content={<div style={{ padding: '0 16px', flex: 1 }}>从右侧滑出(默认)。</div>}>
      <Button variant="secondary"></Button>
    </Sheet>
    <Sheet side="left" title="左侧面板" content={<div style={{ padding: '0 16px', flex: 1 }}>从左侧滑出。</div>}>
      <Button variant="secondary"></Button>
    </Sheet>
    <Sheet side="top" title="顶部面板" content={<div style={{ padding: '0 16px' }}>从顶部滑出。</div>}>
      <Button variant="secondary"></Button>
    </Sheet>
    <Sheet side="bottom" title="底部面板" content={<div style={{ padding: '0 16px' }}>从底部滑出。</div>}>
      <Button variant="secondary"></Button>
    </Sheet>
  </div>,
)

自定义宽度

左右方向的面板可通过 width 设置宽度,接受数字(px)或字符串。

结果
Loading...
实时编辑器
render(
  <div style={{ display: 'flex', gap: 12, padding: '40px 0' }}>
    <Sheet width={400} title="窄面板" content={<div style={{ padding: '0 16px', flex: 1 }}>宽度:400px</div>}>
      <Button variant="secondary">400px</Button>
    </Sheet>
    <Sheet width="80vw" title="宽面板" content={<div style={{ padding: '0 16px', flex: 1 }}>宽度:80vw</div>}>
      <Button variant="secondary">80vw</Button>
    </Sheet>
  </div>,
)

受控模式

结果
Loading...
实时编辑器
const Demo = () => {
  const [open, setOpen] = useState(false)
  return (
    <div style={{ display: 'flex', gap: 12, alignItems: 'center', padding: '40px 0' }}>
      <Button onClick={() => setOpen(true)}>打开受控面板</Button>
      <span style={{ fontSize: 14, color: '#999' }}>open: {String(open)}</span>
      <Sheet
        open={open}
        onOpenChange={setOpen}
        title="受控面板"
        content={
          <div style={{ padding: '0 16px', flex: 1 }}>
            <p>此面板由外部状态控制。</p>
            <Button variant="secondary" onClick={() => setOpen(false)}>从内部关闭</Button>
          </div>
        }
      />
    </div>
  )
}
render(<Demo />)

命令式 API

Sheet.open() 提供命令式调用,适合异步回调、快捷键、表格 action 等非 JSX trigger 场景。需要在应用根部挂载 DesignProviderOverlayHost

import { Sheet } from '@plaud/design'

Sheet.open({
title: '编辑资料',
content: ({ close }) => (
<div>
<p>内容区域。</p>
<Button onClick={close}>关闭</Button>
</div>
),
})

更新已打开的面板

open() 返回控制器,可在打开后更新配置:

const controller = Sheet.open({
title: '加载中...',
content: <div>请稍候。</div>,
})

setTimeout(() => {
controller.update({
title: '完成',
content: <div>数据已加载。</div>,
})
}, 1000)

控制器

interface ImperativeOverlayController<TOptions> {
id: string
close: () => void
update: (updater: Partial<TOptions> | ((prev: TOptions) => TOptions)) => void
afterClosed: Promise<void>
}

Props

属性类型默认值说明
titleReactNode标题
contentReactNode主体内容
footerReactNode底部区域
childrenReactNode触发器(非受控模式)
openboolean受控打开状态
onOpenChange(open: boolean) => void打开状态变更回调
side'top' | 'right' | 'bottom' | 'left''right'滑出方向
widthnumber | string560左右方向内容宽度(px 或 CSS 值)
showClosebooleantrue是否显示关闭按钮
destroyOnClosebooleantrue关闭后是否销毁内容
contentClassNamestring内容区域自定义 class

使用约束

  • 左右方向默认宽度 560px。
  • 命令式 API 需要在应用根部挂载 DesignProviderOverlayHost