aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/components/ui-kit
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/ui-kit')
-rw-r--r--src/components/ui-kit/actions-feedback.tsx101
-rw-r--r--src/components/ui-kit/navigation-data.tsx75
-rw-r--r--src/components/ui-kit/ui-kit.module.css80
-rw-r--r--src/components/ui-kit/ui-kit.tsx6
4 files changed, 262 insertions, 0 deletions
diff --git a/src/components/ui-kit/actions-feedback.tsx b/src/components/ui-kit/actions-feedback.tsx
new file mode 100644
index 0000000..ade8d48
--- /dev/null
+++ b/src/components/ui-kit/actions-feedback.tsx
@@ -0,0 +1,101 @@
+import { useEffect, useRef, useState } from 'react'
+import { Button, Text } from '@react-spectrum/s2/Button'
+import { ActionButton } from '@react-spectrum/s2/ActionButton'
+import { ToggleButton } from '@react-spectrum/s2/ToggleButton'
+import { ActionMenu, MenuItem } from '@react-spectrum/s2/ActionMenu'
+import { Badge } from '@react-spectrum/s2/Badge'
+import { StatusLight } from '@react-spectrum/s2/StatusLight'
+import { ProgressBar } from '@react-spectrum/s2/ProgressBar'
+import { ProgressCircle } from '@react-spectrum/s2/ProgressCircle'
+import { Meter } from '@react-spectrum/s2/Meter'
+import { InlineAlert, Heading, Content } from '@react-spectrum/s2/InlineAlert'
+import { DialogTrigger, Dialog, ButtonGroup } from '@react-spectrum/s2/Dialog'
+import { Popover } from '@react-spectrum/s2/Popover'
+import { Tooltip, TooltipTrigger } from '@react-spectrum/s2/Tooltip'
+import { ToastContainer, ToastQueue } from '@react-spectrum/s2/Toast'
+import AddIcon from '@react-spectrum/s2/icons/Add'
+import EditIcon from '@react-spectrum/s2/icons/Edit'
+import InfoIcon from '@react-spectrum/s2/icons/InfoCircle'
+import styles from './ui-kit.module.css'
+
+export default function ActionsFeedback() {
+ const [message, setMessage] = useState('点击按钮体验交互。')
+ const [selected, setSelected] = useState(false)
+ const [progress, setProgress] = useState(40)
+ const toastClosers = useRef<Array<() => void>>([])
+ useEffect(() => () => { toastClosers.current.forEach((close) => close()) }, [])
+
+ return (
+ <>
+ <section className={styles.section} aria-labelledby="kit-buttons">
+ <h2 id="kit-buttons">按钮与操作</h2>
+ <div className={styles.row}>
+ <Button variant="primary" onPress={() => setMessage('已点击主要按钮。')}>主要按钮</Button>
+ <Button variant="secondary" onPress={() => setMessage('已点击次要按钮。')}>次要按钮</Button>
+ <Button variant="accent" onPress={() => setMessage('已点击强调按钮。')}><AddIcon /><Text>新建</Text></Button>
+ <Button variant="negative" onPress={() => setMessage('这是危险操作的样式示例。')}>危险操作</Button>
+ <Button isDisabled>禁用按钮</Button>
+ </div>
+ <div className={styles.row}>
+ {(['S', 'M', 'L', 'XL'] as const).map((size) => <Button key={size} size={size} variant="secondary" onPress={() => setMessage(`已点击 ${size} 尺寸按钮。`)}>{size}</Button>)}
+ <TooltipTrigger><ActionButton aria-label="编辑示例" onPress={() => setMessage('已点击图标按钮。')}><EditIcon /></ActionButton><Tooltip>编辑示例</Tooltip></TooltipTrigger>
+ <ToggleButton isSelected={selected} onChange={setSelected}>{selected ? '已收藏' : '收藏'}</ToggleButton>
+ <ActionMenu aria-label="更多示例操作" onAction={(key) => setMessage(key === 'copy' ? '已选择复制示例。' : '已选择重命名示例。')}>
+ <MenuItem id="copy">复制</MenuItem><MenuItem id="rename">重命名</MenuItem>
+ </ActionMenu>
+ </div>
+ <p role="status" className={styles.caption}>{message}</p>
+ </section>
+
+ <section className={styles.section} aria-labelledby="kit-status">
+ <h2 id="kit-status">徽标与状态</h2>
+ <div className={styles.row}>
+ <Badge variant="neutral">默认</Badge><Badge variant="informative">信息</Badge><Badge variant="positive">成功</Badge><Badge variant="notice">提醒</Badge><Badge variant="negative">错误</Badge><Badge variant="accent">强调</Badge>
+ </div>
+ <div className={styles.row}>
+ <StatusLight variant="positive">已完成</StatusLight><StatusLight variant="notice">待处理</StatusLight><StatusLight variant="negative">失败</StatusLight><StatusLight variant="neutral">草稿</StatusLight>
+ </div>
+ <div className={styles.grid}>
+ <InlineAlert variant="informative"><Heading>信息提示</Heading><Content>用于展示与当前操作相关的说明。</Content></InlineAlert>
+ <InlineAlert variant="positive"><Heading>成功提示</Heading><Content>这是一条成功状态示例。</Content></InlineAlert>
+ <InlineAlert variant="notice"><Heading>注意事项</Heading><Content>操作前需要留意的提示信息。</Content></InlineAlert>
+ <InlineAlert variant="negative"><Heading>错误提示</Heading><Content>展示问题原因及后续处理方式。</Content></InlineAlert>
+ </div>
+ </section>
+
+ <section className={styles.section} aria-labelledby="kit-progress">
+ <h2 id="kit-progress">进度与容量</h2>
+ <div className={styles.grid}>
+ <ProgressBar label="示例进度" value={progress} />
+ <Meter label="已用容量" value={65} variant="notice" />
+ <ProgressBar label="不确定进度" isIndeterminate />
+ <div className={styles.row}><ProgressCircle aria-label="正在加载" isIndeterminate /><span>加载中</span></div>
+ </div>
+ <div className={styles.row}>
+ <Button variant="secondary" isDisabled={progress === 100} onPress={() => setProgress((value) => Math.min(value + 20, 100))}>增加进度</Button>
+ <Button variant="secondary" onPress={() => setProgress(0)}>重置进度</Button>
+ </div>
+ </section>
+
+ <section className={styles.section} aria-labelledby="kit-overlays">
+ <h2 id="kit-overlays">弹层与通知</h2>
+ <div className={styles.row}>
+ <DialogTrigger>
+ <Button variant="secondary">打开对话框</Button>
+ <Dialog size="S">
+ {({ close }) => <><Heading>对话框示例</Heading><Content>对话框用于需要用户确认的操作。</Content><ButtonGroup><Button variant="secondary" onPress={close}>取消</Button><Button variant="primary" onPress={() => { setMessage('已确认对话框。'); close() }}>确认</Button></ButtonGroup></>}
+ </Dialog>
+ </DialogTrigger>
+ <DialogTrigger>
+ <Button variant="secondary">打开 Popover</Button>
+ <Popover aria-label="Popover 示例"><div className={styles.popover}><InfoIcon /><p>Popover 用于在操作附近展示补充内容,点击外部或按 Esc 关闭。</p></div></Popover>
+ </DialogTrigger>
+ <TooltipTrigger><Button variant="secondary">悬停查看提示</Button><Tooltip>这是一条 Tooltip 提示。</Tooltip></TooltipTrigger>
+ <Button variant="secondary" onPress={() => toastClosers.current.push(ToastQueue.positive('这是一条成功通知。', { timeout: 5000 }))}>成功通知</Button>
+ <Button variant="secondary" onPress={() => toastClosers.current.push(ToastQueue.negative('这是一条错误通知示例。', { timeout: 5000 }))}>错误通知</Button>
+ </div>
+ </section>
+ <ToastContainer />
+ </>
+ )
+}
diff --git a/src/components/ui-kit/navigation-data.tsx b/src/components/ui-kit/navigation-data.tsx
new file mode 100644
index 0000000..5ac2605
--- /dev/null
+++ b/src/components/ui-kit/navigation-data.tsx
@@ -0,0 +1,75 @@
+import { useState } from 'react'
+import { Tabs, TabList, Tab, TabPanel } from '@react-spectrum/s2/Tabs'
+import { Accordion, AccordionItem, AccordionItemTitle, AccordionItemPanel } from '@react-spectrum/s2/Accordion'
+import { Breadcrumbs, Breadcrumb } from '@react-spectrum/s2/Breadcrumbs'
+import { TagGroup, Tag } from '@react-spectrum/s2/TagGroup'
+import { Button } from '@react-spectrum/s2/Button'
+import { Card, CardPreview, Content, Text, Footer } from '@react-spectrum/s2/Card'
+import { TableView, TableHeader, Column, TableBody, Row, Cell, type Selection, type SortDescriptor } from '@react-spectrum/s2/TableView'
+import { StatusLight } from '@react-spectrum/s2/StatusLight'
+import EditIcon from '@react-spectrum/s2/icons/Edit'
+import styles from './ui-kit.module.css'
+
+const initialTags = ['设计', '开发', '文档']
+const rows = [
+ { id: 'buttons', name: '按钮', category: '基础操作', status: '已完成' },
+ { id: 'navigation', name: '导航', category: '页面组织', status: '已完成' },
+ { id: 'form', name: '表单', category: '数据输入', status: '示例' },
+]
+
+export default function NavigationData() {
+ const [tags, setTags] = useState(initialTags)
+ const [selection, setSelection] = useState<Selection>(new Set())
+ const [sort, setSort] = useState<SortDescriptor>({ column: 'name', direction: 'ascending' })
+ const sortedRows = [...rows].sort((a, b) => a.name.localeCompare(b.name, 'zh-CN') * (sort.direction === 'descending' ? -1 : 1))
+
+ return (
+ <>
+ <section className={styles.section} aria-labelledby="kit-navigation">
+ <h2 id="kit-navigation">导航与内容组织</h2>
+ <Breadcrumbs><Breadcrumb href="/">Home</Breadcrumb><Breadcrumb>UI Kit</Breadcrumb></Breadcrumbs>
+ <Tabs aria-label="组件内容示例" defaultSelectedKey="overview">
+ <TabList aria-label="组件内容示例"><Tab id="overview">概览</Tab><Tab id="details">详情</Tab><Tab id="disabled" isDisabled>禁用</Tab></TabList>
+ <TabPanel id="overview"><p className={styles.tabContent}>这是概览面板。点击“详情”切换内容。</p></TabPanel>
+ <TabPanel id="details"><p className={styles.tabContent}>这是详情面板。也可以使用键盘方向键切换标签。</p></TabPanel>
+ </Tabs>
+ <Accordion allowsMultipleExpanded defaultExpandedKeys={['usage']}>
+ <AccordionItem id="usage"><AccordionItemTitle>组件如何交互?</AccordionItemTitle><AccordionItemPanel>点击、聚焦或使用键盘体验组件的不同状态。</AccordionItemPanel></AccordionItem>
+ <AccordionItem id="theme"><AccordionItemTitle>如何查看暗色效果?</AccordionItemTitle><AccordionItemPanel>使用顶栏右上角的设置按钮切换亮色和暗色模式。</AccordionItemPanel></AccordionItem>
+ </Accordion>
+ </section>
+
+ <section className={styles.section} aria-labelledby="kit-tags">
+ <h2 id="kit-tags">标签与卡片</h2>
+ <TagGroup aria-label="可移除的示例标签" onRemove={(keys) => setTags((current) => current.filter((tag) => !keys.has(tag)))}>
+ {tags.map((tag) => <Tag key={tag} id={tag}>{tag}</Tag>)}
+ </TagGroup>
+ {tags.length === 0 && <p className={styles.caption}>标签已清空。</p>}
+ <div><Button variant="secondary" onPress={() => setTags(initialTags)}>恢复标签</Button></div>
+ <div className={styles.cards}>
+ <Card href="/form" aria-label="查看表单示例">
+ <CardPreview><div className={styles.cardPreview}><EditIcon /></div></CardPreview>
+ <Content><Text slot="title">表单组件</Text><Text slot="description">文本输入、选择、日期、颜色与文件控件。</Text></Content>
+ <Footer><StatusLight variant="informative">点击查看表单页面</StatusLight></Footer>
+ </Card>
+ <Card variant="secondary" aria-label="组件展示卡片">
+ <Content><Text slot="title">内容卡片</Text><Text slot="description">使用标题、描述和底部状态组织信息。</Text></Content>
+ <Footer><StatusLight variant="positive">静态展示</StatusLight></Footer>
+ </Card>
+ </div>
+ </section>
+
+ <section className={styles.section} aria-labelledby="kit-table">
+ <h2 id="kit-table">数据表格</h2>
+ <p className={styles.caption}>支持多选,点击“名称”可切换排序。</p>
+ <div className={styles.tableContainer}>
+ <TableView aria-label="组件示例列表" UNSAFE_className={styles.table} selectionMode="multiple" selectedKeys={selection} onSelectionChange={setSelection} sortDescriptor={sort} onSortChange={setSort}>
+ <TableHeader><Column id="name" isRowHeader allowsSorting>名称</Column><Column id="category">分类</Column><Column id="status">状态</Column></TableHeader>
+ <TableBody items={sortedRows}>{(row) => <Row id={row.id}><Cell>{row.name}</Cell><Cell>{row.category}</Cell><Cell>{row.status}</Cell></Row>}</TableBody>
+ </TableView>
+ </div>
+ <p role="status" className={styles.caption}>已选择 {selection === 'all' ? rows.length : selection.size} 项</p>
+ </section>
+ </>
+ )
+}
diff --git a/src/components/ui-kit/ui-kit.module.css b/src/components/ui-kit/ui-kit.module.css
new file mode 100644
index 0000000..d590bb4
--- /dev/null
+++ b/src/components/ui-kit/ui-kit.module.css
@@ -0,0 +1,80 @@
+.section {
+ min-width: 0;
+ display: grid;
+ gap: 24px;
+ padding: 24px;
+ margin-bottom: 24px;
+ border: 1px solid var(--border);
+ border-radius: 12px;
+}
+
+.section > h2 {
+ margin: 0;
+ font-size: 20px;
+}
+
+.row {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 16px;
+}
+
+.grid,
+.cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
+ gap: 20px;
+ min-width: 0;
+}
+
+.grid > *,
+.cards > * {
+ min-width: 0;
+}
+
+.caption,
+.tabContent,
+.popover p {
+ font-size: 14px;
+ line-height: 1.6;
+ overflow-wrap: anywhere;
+}
+
+.tabContent {
+ padding: 20px 0;
+}
+
+.popover {
+ display: grid;
+ gap: 12px;
+ padding: 16px;
+ width: min(260px, calc(100vw - 64px));
+ box-sizing: border-box;
+}
+
+.cardPreview {
+ display: grid;
+ place-items: center;
+ width: 100%;
+ height: 100px;
+ color: var(--accent);
+ background: var(--social-bg);
+}
+
+.tableContainer {
+ min-width: 0;
+ overflow: auto;
+}
+
+.table.table {
+ height: 280px;
+ min-width: 440px;
+ width: 100%;
+}
+
+@media (max-width: 640px) {
+ .section {
+ padding: 16px;
+ }
+}
diff --git a/src/components/ui-kit/ui-kit.tsx b/src/components/ui-kit/ui-kit.tsx
new file mode 100644
index 0000000..e12012d
--- /dev/null
+++ b/src/components/ui-kit/ui-kit.tsx
@@ -0,0 +1,6 @@
+import ActionsFeedback from './actions-feedback'
+import NavigationData from './navigation-data'
+
+export default function UIKit() {
+ return <><ActionsFeedback /><NavigationData /></>
+}