121 lines
4.2 KiB
TypeScript
121 lines
4.2 KiB
TypeScript
import { useState } from 'react'
|
|
import { Button, Modal, Notice, Space } from '../../src'
|
|
import { Demo } from '../components/Demo'
|
|
import { PropsTable } from '../components/PropsTable'
|
|
import { Section } from '../components/Section'
|
|
|
|
export function Modals() {
|
|
const [confirmOpen, setConfirmOpen] = useState(false)
|
|
const [infoOpen, setInfoOpen] = useState(false)
|
|
const [deleted, setDeleted] = useState(false)
|
|
|
|
return (
|
|
<Section
|
|
id="modal"
|
|
title="弹窗"
|
|
api="Modal"
|
|
desc="基于原生 <dialog>:焦点陷阱、Esc 关闭、背景 inert 由浏览器提供,组件补样式、滚动锁和受控开关。对应参考站的 .nice-tips(遮罩 60% 黑、内容 12px 圆角、上滑淡入)。"
|
|
>
|
|
<Demo
|
|
title="确认弹窗"
|
|
desc="危险操作要二次确认;确认后由调用方关闭并把结果反馈给页面(这里用 Notice 示意)。"
|
|
layout="column"
|
|
code={`const [open, setOpen] = useState(false)
|
|
|
|
<Modal
|
|
open={open}
|
|
onClose={() => setOpen(false)}
|
|
title="删除这篇内容?"
|
|
footer={
|
|
<>
|
|
<Button variant="ghost" onClick={() => setOpen(false)}>取消</Button>
|
|
<Button variant="danger" onClick={handleDelete}>删除</Button>
|
|
</>
|
|
}
|
|
>
|
|
删除后不可恢复,占位数据会被一起清掉。
|
|
</Modal>`}
|
|
>
|
|
<Space gap="md" wrap>
|
|
<Button variant="danger" onClick={() => setConfirmOpen(true)}>
|
|
删除内容
|
|
</Button>
|
|
<Button variant="secondary" onClick={() => setInfoOpen(true)}>
|
|
打开说明弹窗
|
|
</Button>
|
|
</Space>
|
|
|
|
{deleted && (
|
|
<Notice title="示例:已删除" action={{ label: '撤销', onClick: () => setDeleted(false) }}>
|
|
这里只是演示确认流程,没有真的删数据。
|
|
</Notice>
|
|
)}
|
|
</Demo>
|
|
|
|
<Demo
|
|
title="尺寸与长内容"
|
|
desc="sm 300 / md 460 / lg 640;内容多时弹窗内部不滚动、由页面决定(这里演示 lg 的排版)。"
|
|
layout="column"
|
|
code={`<Modal size="lg" title="关于这套组件" open={open} onClose={close}>…</Modal>`}
|
|
>
|
|
<Button variant="secondary" onClick={() => setInfoOpen(true)}>
|
|
打开 lg 弹窗
|
|
</Button>
|
|
</Demo>
|
|
|
|
<PropsTable
|
|
rows={[
|
|
{ name: 'open', type: 'boolean', required: true, desc: '受控开关' },
|
|
{ name: 'onClose', type: '() => void', required: true, desc: 'Esc / 遮罩 / 关闭按钮都会调用' },
|
|
{ name: 'title', type: 'ReactNode', required: true, desc: '标题,同时作为 aria-labelledby' },
|
|
{ name: 'children', type: 'ReactNode', desc: '正文' },
|
|
{ name: 'footer', type: 'ReactNode', desc: '底部操作区(通常放按钮)' },
|
|
{ name: 'size', type: "'sm' | 'md' | 'lg'", defaultValue: "'md'", desc: '300 / 460 / 640' },
|
|
{ name: 'closeOnOverlay', type: 'boolean', defaultValue: 'true', desc: '点遮罩是否关闭' },
|
|
]}
|
|
/>
|
|
|
|
<Modal
|
|
open={confirmOpen}
|
|
onClose={() => setConfirmOpen(false)}
|
|
title="删除这篇内容?"
|
|
footer={
|
|
<>
|
|
<Button variant="ghost" onClick={() => setConfirmOpen(false)}>
|
|
取消
|
|
</Button>
|
|
<Button
|
|
variant="danger"
|
|
onClick={() => {
|
|
setConfirmOpen(false)
|
|
setDeleted(true)
|
|
}}
|
|
>
|
|
删除
|
|
</Button>
|
|
</>
|
|
}
|
|
>
|
|
删除后不可恢复,占位数据会被一起清掉。
|
|
</Modal>
|
|
|
|
<Modal
|
|
open={infoOpen}
|
|
onClose={() => setInfoOpen(false)}
|
|
title="关于这套组件"
|
|
size="lg"
|
|
footer={
|
|
<Button onClick={() => setInfoOpen(false)}>知道了</Button>
|
|
}
|
|
>
|
|
<p>
|
|
组件只依赖令牌,不依赖任何业务数据;页面用 Card / Grid / Space 组合,弹窗只负责「打断」。
|
|
</p>
|
|
<p style={{ marginTop: 12 }}>
|
|
关闭方式有三种:右上角按钮、Esc、点击遮罩。打开时背景滚动会被锁住,关闭后恢复。
|
|
</p>
|
|
</Modal>
|
|
</Section>
|
|
)
|
|
}
|