aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/demos
diff options
context:
space:
mode:
Diffstat (limited to 'src/demos')
-rw-r--r--src/demos/ActionsDemo.tsx83
-rw-r--r--src/demos/ChatDemo.tsx50
-rw-r--r--src/demos/CommandPaletteDemo.tsx46
-rw-r--r--src/demos/DataDemo.tsx72
-rw-r--r--src/demos/DialogsDemo.tsx95
-rw-r--r--src/demos/FeedbackDemo.tsx35
-rw-r--r--src/demos/InputsDemo.tsx58
-rw-r--r--src/demos/LayoutDemo.tsx52
-rw-r--r--src/demos/MenusDemo.tsx97
-rw-r--r--src/demos/NavigationDemo.tsx35
-rw-r--r--src/demos/ProvidersDemo.tsx29
-rw-r--r--src/demos/SelectionDemo.tsx55
-rw-r--r--src/demos/SelectorsDemo.tsx33
-rw-r--r--src/demos/TypographyDemo.tsx36
14 files changed, 776 insertions, 0 deletions
diff --git a/src/demos/ActionsDemo.tsx b/src/demos/ActionsDemo.tsx
new file mode 100644
index 0000000..1f5f1ad
--- /dev/null
+++ b/src/demos/ActionsDemo.tsx
@@ -0,0 +1,83 @@
+import { useState } from 'react'
+import { Button } from '@astryxdesign/core/Button'
+import { HStack } from '@astryxdesign/core/HStack'
+import { Icon } from '@astryxdesign/core/Icon'
+import { IconButton } from '@astryxdesign/core/IconButton'
+import { Kbd } from '@astryxdesign/core/Kbd'
+import { Link } from '@astryxdesign/core/Link'
+import {
+ SegmentedControl,
+ SegmentedControlItem,
+} from '@astryxdesign/core/SegmentedControl'
+import { Text } from '@astryxdesign/core/Text'
+import { ToggleButton, ToggleButtonGroup } from '@astryxdesign/core/ToggleButton'
+import { Toolbar } from '@astryxdesign/core/Toolbar'
+import { VStack } from '@astryxdesign/core/VStack'
+import { Heart, List, LayoutGrid } from 'lucide-react'
+
+export function ActionsDemo() {
+ const [layout, setLayout] = useState('网格')
+ const [view, setView] = useState('概览')
+
+ return (
+ <VStack gap={4}>
+ <Toolbar
+ label="常用操作"
+ startContent={
+ <HStack gap={2} wrap="wrap">
+ <Button label="主要操作" variant="primary" size="sm" />
+ <Button label="次要操作" variant="secondary" size="sm" />
+ <Button label="幽灵按钮" variant="ghost" size="sm" />
+ </HStack>
+ }
+ endContent={
+ <IconButton
+ label="收藏"
+ tooltip="收藏"
+ variant="ghost"
+ icon={<Icon icon={Heart} />}
+ />
+ }
+ />
+
+ <HStack gap={4} wrap="wrap" vAlign="center">
+ <ToggleButtonGroup
+ label="调整视图布局"
+ value={layout}
+ onChange={(value) => setLayout((value as string | null) ?? '网格')}
+ size="sm">
+ <ToggleButton
+ label="网格"
+ value="网格"
+ icon={<Icon icon={LayoutGrid} size="sm" />}
+ />
+ <ToggleButton
+ label="列表"
+ value="列表"
+ icon={<Icon icon={List} size="sm" />}
+ />
+ </ToggleButtonGroup>
+
+ <SegmentedControl
+ label="切换视图"
+ value={view}
+ onChange={setView}
+ size="sm">
+ <SegmentedControlItem label="概览" value="概览" />
+ <SegmentedControlItem label="活动" value="活动" />
+ <SegmentedControlItem label="成员" value="成员" />
+ </SegmentedControl>
+ </HStack>
+
+ <HStack gap={3} vAlign="center" wrap="wrap">
+ <Link href="/about" isStandalone>
+ 查看关于页
+ </Link>
+ <Text type="supporting" size="sm">
+ 快捷键
+ </Text>
+ <Kbd keys="mod+k" />
+ </HStack>
+ </VStack>
+ )
+}
diff --git a/src/demos/ChatDemo.tsx b/src/demos/ChatDemo.tsx
new file mode 100644
index 0000000..cf371f2
--- /dev/null
+++ b/src/demos/ChatDemo.tsx
@@ -0,0 +1,50 @@
+import {
+ ChatMessage,
+ ChatMessageBubble,
+ ChatMessageList,
+ ChatMessageMetadata,
+} from '@astryxdesign/core/Chat'
+import { Text } from '@astryxdesign/core/Text'
+import { Timestamp } from '@astryxdesign/core/Timestamp'
+
+export function ChatDemo() {
+ return (
+ <ChatMessageList>
+ <ChatMessage sender="user">
+ <ChatMessageBubble group="first">
+ 我把权限模块重构好了。
+ </ChatMessageBubble>
+ <ChatMessageBubble
+ group="last"
+ metadata={
+ <ChatMessageMetadata
+ timestamp={
+ <Timestamp value="2026-04-28T14:30:00" format="time" />
+ }
+ status="read"
+ />
+ }>
+ 可以帮我检查一下令牌校验的改动吗?
+ </ChatMessageBubble>
+ </ChatMessage>
+ <ChatMessage sender="assistant">
+ <ChatMessageBubble
+ variant="ghost"
+ metadata={
+ <ChatMessageMetadata
+ timestamp={
+ <Timestamp value="2026-04-28T14:31:00" format="time" />
+ }
+ footer={
+ <Text type="supporting" color="secondary">
+ Astryx 助手
+ </Text>
+ }
+ />
+ }>
+ 看过了,刷新令牌轮换和错误处理都覆盖到了,可以发布。
+ </ChatMessageBubble>
+ </ChatMessage>
+ </ChatMessageList>
+ )
+}
diff --git a/src/demos/CommandPaletteDemo.tsx b/src/demos/CommandPaletteDemo.tsx
new file mode 100644
index 0000000..e3c4119
--- /dev/null
+++ b/src/demos/CommandPaletteDemo.tsx
@@ -0,0 +1,46 @@
+import { useMemo, useState } from 'react'
+import { Button } from '@astryxdesign/core/Button'
+import { CommandPalette } from '@astryxdesign/core/CommandPalette'
+import { HStack } from '@astryxdesign/core/HStack'
+import { useToast } from '@astryxdesign/core/Toast'
+import { createStaticSource } from '@astryxdesign/core/Typeahead'
+
+export function CommandPaletteDemo() {
+ const [open, setOpen] = useState(false)
+ const showToast = useToast()
+ const source = useMemo(
+ () =>
+ createStaticSource([
+ { id: 'home', label: '回到概览' },
+ { id: 'components', label: '浏览组件' },
+ { id: 'about', label: '查看关于' },
+ { id: 'theme', label: '切换主题' },
+ ]),
+ [],
+ )
+
+ return (
+ <>
+ <HStack gap={2} wrap="wrap">
+ <Button
+ label="打开命令面板"
+ variant="primary"
+ onClick={() => setOpen(true)}
+ />
+ <Button
+ label="显示通知"
+ variant="secondary"
+ onClick={() =>
+ showToast({ body: '设置已保存', type: 'info' })
+ }
+ />
+ </HStack>
+ <CommandPalette
+ isOpen={open}
+ onOpenChange={setOpen}
+ searchSource={source}
+ label="命令面板"
+ />
+ </>
+ )
+}
diff --git a/src/demos/DataDemo.tsx b/src/demos/DataDemo.tsx
new file mode 100644
index 0000000..f2a5be7
--- /dev/null
+++ b/src/demos/DataDemo.tsx
@@ -0,0 +1,72 @@
+import { Avatar } from '@astryxdesign/core/Avatar'
+import { Badge } from '@astryxdesign/core/Badge'
+import { Divider } from '@astryxdesign/core/Divider'
+import { HStack } from '@astryxdesign/core/HStack'
+import { List, ListItem } from '@astryxdesign/core/List'
+import { Text } from '@astryxdesign/core/Text'
+import { Table, pixel, proportional } from '@astryxdesign/core/Table'
+import type { TableColumn } from '@astryxdesign/core/Table'
+import { VStack } from '@astryxdesign/core/VStack'
+
+interface Member extends Record<string, unknown> {
+ id: string
+ name: string
+ role: string
+ projects: number
+}
+
+const MEMBERS: Member[] = [
+ { id: '1', name: '林晓', role: '产品设计', projects: 4 },
+ { id: '2', name: '陈默', role: '前端开发', projects: 7 },
+ { id: '3', name: '周雨', role: '内容运营', projects: 3 },
+]
+
+const COLUMNS: TableColumn<Member>[] = [
+ { key: 'name', header: '成员', width: proportional(1) },
+ { key: 'role', header: '角色', width: proportional(1) },
+ { key: 'projects', header: '进行中', width: pixel(90) },
+]
+
+export function DataDemo() {
+ return (
+ <VStack gap={4}>
+ <List hasDividers>
+ <ListItem
+ label="林晓"
+ description="产品设计 · 4 个项目"
+ startContent={<Avatar name="林晓" size="sm" />}
+ endContent={<Badge variant="blue" label="3" />}
+ />
+ <ListItem
+ label="陈默"
+ description="前端开发 · 7 个项目"
+ startContent={<Avatar name="陈默" size="sm" />}
+ endContent={<Badge variant="teal" label="8" />}
+ />
+ <ListItem
+ label="周雨"
+ description="内容运营 · 3 个项目"
+ startContent={<Avatar name="周雨" size="sm" />}
+ endContent={<Badge variant="purple" label="2" />}
+ />
+ </List>
+
+ <Divider />
+
+ <VStack gap={2}>
+ <HStack gap={2} vAlign="center">
+ <Text type="label">团队进度</Text>
+ <Text type="supporting" size="sm">
+ 数据表格
+ </Text>
+ </HStack>
+ <Table
+ data={MEMBERS}
+ columns={COLUMNS}
+ idKey="id"
+ density="compact"
+ />
+ </VStack>
+ </VStack>
+ )
+}
diff --git a/src/demos/DialogsDemo.tsx b/src/demos/DialogsDemo.tsx
new file mode 100644
index 0000000..1b887df
--- /dev/null
+++ b/src/demos/DialogsDemo.tsx
@@ -0,0 +1,95 @@
+import { useState } from 'react'
+import { BottomSheet } from '@astryxdesign/core/BottomSheet'
+import { Button } from '@astryxdesign/core/Button'
+import { Dialog, DialogHeader } from '@astryxdesign/core/Dialog'
+import { Drawer, DrawerHeader } from '@astryxdesign/core/Drawer'
+import { HStack } from '@astryxdesign/core/HStack'
+import { Layout, LayoutContent } from '@astryxdesign/core/Layout'
+import { Section } from '@astryxdesign/core/Section'
+import { Heading } from '@astryxdesign/core/Heading'
+import { Text } from '@astryxdesign/core/Text'
+import { VStack } from '@astryxdesign/core/VStack'
+
+type ActiveLayer = 'dialog' | 'drawer' | 'sheet' | null
+
+export function DialogsDemo() {
+ const [active, setActive] = useState<ActiveLayer>(null)
+
+ return (
+ <>
+ <HStack gap={2} wrap="wrap">
+ <Button label="打开对话框" onClick={() => setActive('dialog')} />
+ <Button
+ label="打开抽屉"
+ variant="secondary"
+ onClick={() => setActive('drawer')}
+ />
+ <Button
+ label="打开底部面板"
+ variant="secondary"
+ onClick={() => setActive('sheet')}
+ />
+ </HStack>
+
+ <Dialog
+ isOpen={active === 'dialog'}
+ onOpenChange={(open) => setActive(open ? 'dialog' : null)}>
+ <Layout
+ header={
+ <DialogHeader
+ title="确认发布"
+ onOpenChange={(open) => setActive(open ? 'dialog' : null)}
+ />
+ }
+ content={
+ <LayoutContent>
+ <Text type="body" as="p">
+ 发布后,当前版本会立即对全部用户可见。你仍然可以稍后回滚。
+ </Text>
+ </LayoutContent>
+ }
+ />
+ </Dialog>
+
+ <Drawer
+ isOpen={active === 'drawer'}
+ onOpenChange={(open) => setActive(open ? 'drawer' : null)}
+ label="项目详情"
+ width={360}>
+ <Layout
+ header={
+ <DrawerHeader
+ title="项目详情"
+ onOpenChange={(open) => setActive(open ? 'drawer' : null)}
+ />
+ }
+ content={
+ <LayoutContent>
+ <VStack gap={2}>
+ <Heading level={3}>Astryx 起始模板</Heading>
+ <Text type="body" color="secondary" as="p">
+ 抽屉适合承载不打断主流程的详细信息和次要操作。
+ </Text>
+ </VStack>
+ </LayoutContent>
+ }
+ />
+ </Drawer>
+
+ <BottomSheet
+ isOpen={active === 'sheet'}
+ onOpenChange={(open) => setActive(open ? 'sheet' : null)}
+ label="筛选">
+ <Section padding={4}>
+ <VStack gap={3}>
+ <Heading level={3}>筛选条件</Heading>
+ <Text type="body" color="secondary" as="p">
+ 底部面板适合移动端上的快速筛选和短操作。
+ </Text>
+ <Button label="应用筛选" onClick={() => setActive(null)} />
+ </VStack>
+ </Section>
+ </BottomSheet>
+ </>
+ )
+}
diff --git a/src/demos/FeedbackDemo.tsx b/src/demos/FeedbackDemo.tsx
new file mode 100644
index 0000000..2909878
--- /dev/null
+++ b/src/demos/FeedbackDemo.tsx
@@ -0,0 +1,35 @@
+import { Banner } from '@astryxdesign/core/Banner'
+import { HStack } from '@astryxdesign/core/HStack'
+import { ProgressBar } from '@astryxdesign/core/ProgressBar'
+import { Skeleton } from '@astryxdesign/core/Skeleton'
+import { Spinner } from '@astryxdesign/core/Spinner'
+import { Text } from '@astryxdesign/core/Text'
+import { VStack } from '@astryxdesign/core/VStack'
+
+export function FeedbackDemo() {
+ return (
+ <VStack gap={4}>
+ <Banner
+ status="info"
+ title="新版本已经准备好"
+ description="刷新页面后即可使用最新的组件与主题。"
+ />
+ <VStack gap={2}>
+ <Text type="label">工作区初始化</Text>
+ <ProgressBar label="工作区初始化" value={68} hasValueLabel isLabelHidden />
+ </VStack>
+ <HStack gap={4} vAlign="center" wrap="wrap">
+ <HStack gap={2} vAlign="center">
+ <Spinner size="sm" />
+ <Text type="supporting" size="sm">
+ 正在同步数据…
+ </Text>
+ </HStack>
+ <VStack gap={1} width={160}>
+ <Skeleton width="100%" height={14} index={0} />
+ <Skeleton width="70%" height={14} index={1} />
+ </VStack>
+ </HStack>
+ </VStack>
+ )
+}
diff --git a/src/demos/InputsDemo.tsx b/src/demos/InputsDemo.tsx
new file mode 100644
index 0000000..47c9724
--- /dev/null
+++ b/src/demos/InputsDemo.tsx
@@ -0,0 +1,58 @@
+import { useState } from 'react'
+import { Banner } from '@astryxdesign/core/Banner'
+import { Button } from '@astryxdesign/core/Button'
+import { HStack } from '@astryxdesign/core/HStack'
+import { Switch } from '@astryxdesign/core/Switch'
+import { TextArea } from '@astryxdesign/core/TextArea'
+import { TextInput } from '@astryxdesign/core/TextInput'
+import { VStack } from '@astryxdesign/core/VStack'
+
+export function InputsDemo() {
+ const [name, setName] = useState('模板项目')
+ const [description, setDescription] = useState(
+ '一个使用 Astryx 搭建的 React 应用。',
+ )
+ const [updates, setUpdates] = useState(true)
+ const [saved, setSaved] = useState(false)
+
+ return (
+ <VStack gap={4}>
+ <TextInput
+ label="项目名称"
+ value={name}
+ onChange={setName}
+ isRequired
+ width="100%"
+ />
+ <TextArea
+ label="项目描述"
+ value={description}
+ onChange={setDescription}
+ rows={3}
+ width="100%"
+ />
+ <Switch
+ label="版本更新提醒"
+ description="有新版本时发送一封简短的邮件。"
+ value={updates}
+ onChange={setUpdates}
+ />
+ <HStack gap={2} hAlign="end">
+ <Button
+ label="保存设置"
+ variant="primary"
+ onClick={() => setSaved(true)}
+ />
+ </HStack>
+ {saved ? (
+ <Banner
+ status="success"
+ title="设置已保存"
+ description="表单值只保存在当前演示的内存中。"
+ isDismissable
+ onDismiss={() => setSaved(false)}
+ />
+ ) : null}
+ </VStack>
+ )
+}
diff --git a/src/demos/LayoutDemo.tsx b/src/demos/LayoutDemo.tsx
new file mode 100644
index 0000000..53da1bd
--- /dev/null
+++ b/src/demos/LayoutDemo.tsx
@@ -0,0 +1,52 @@
+import { useState } from 'react'
+import { Collapsible, CollapsibleGroup } from '@astryxdesign/core/Collapsible'
+import { Grid } from '@astryxdesign/core/Grid'
+import { Heading } from '@astryxdesign/core/Heading'
+import { SelectableCard } from '@astryxdesign/core/SelectableCard'
+import { Text } from '@astryxdesign/core/Text'
+import { VStack } from '@astryxdesign/core/VStack'
+
+const PLANS = [
+ { id: 'basic', name: '基础版', price: '¥9/月', desc: '适合个人项目' },
+ { id: 'pro', name: '专业版', price: '¥29/月', desc: '适合小型团队' },
+ { id: 'team', name: '团队版', price: '¥99/月', desc: '适合协作组织' },
+]
+
+export function LayoutDemo() {
+ const [selected, setSelected] = useState('pro')
+
+ return (
+ <VStack gap={4}>
+ <Grid columns={{ minWidth: 140, max: 3 }} gap={3}>
+ {PLANS.map((plan) => (
+ <SelectableCard
+ key={plan.id}
+ label={plan.name}
+ isSelected={selected === plan.id}
+ onChange={() => setSelected(plan.id)}>
+ <VStack gap={1}>
+ <Heading level={4}>{plan.name}</Heading>
+ <Text type="large" weight="bold">
+ {plan.price}
+ </Text>
+ <Text type="supporting" size="sm">
+ {plan.desc}
+ </Text>
+ </VStack>
+ </SelectableCard>
+ ))}
+ </Grid>
+
+ <CollapsibleGroup type="single" defaultValue="layout">
+ <Collapsible
+ value="layout"
+ trigger={<Text type="label">这个布局是如何组织的?</Text>}>
+ <Text type="body" color="secondary" as="p">
+ 外层使用 Grid 负责响应式列数,SelectableCard 负责可选中的卡片语义,Collapsible
+ 负责按需展开说明。所有间距都来自同一套 token。
+ </Text>
+ </Collapsible>
+ </CollapsibleGroup>
+ </VStack>
+ )
+}
diff --git a/src/demos/MenusDemo.tsx b/src/demos/MenusDemo.tsx
new file mode 100644
index 0000000..c928e6f
--- /dev/null
+++ b/src/demos/MenusDemo.tsx
@@ -0,0 +1,97 @@
+import { useState } from 'react'
+import { Avatar } from '@astryxdesign/core/Avatar'
+import { Button } from '@astryxdesign/core/Button'
+import { Card } from '@astryxdesign/core/Card'
+import { ContextMenu } from '@astryxdesign/core/ContextMenu'
+import { DropdownMenu } from '@astryxdesign/core/DropdownMenu'
+import { HStack } from '@astryxdesign/core/HStack'
+import { Heading } from '@astryxdesign/core/Heading'
+import { HoverCard } from '@astryxdesign/core/HoverCard'
+import { MoreMenu } from '@astryxdesign/core/MoreMenu'
+import { Popover } from '@astryxdesign/core/Popover'
+import { Text } from '@astryxdesign/core/Text'
+import { Tooltip } from '@astryxdesign/core/Tooltip'
+import { VStack } from '@astryxdesign/core/VStack'
+
+export function MenusDemo() {
+ const [popoverOpen, setPopoverOpen] = useState(false)
+
+ return (
+ <VStack gap={4}>
+ <HStack gap={2} wrap="wrap" vAlign="center">
+ <DropdownMenu
+ button={{ label: '操作菜单' }}
+ items={[
+ { label: '编辑', onClick: () => {} },
+ { label: '复制', onClick: () => {} },
+ { label: '删除', onClick: () => {} },
+ ]}
+ />
+ <MoreMenu
+ items={[
+ { label: '导出', onClick: () => {} },
+ { label: '分享', onClick: () => {} },
+ { label: '归档', onClick: () => {} },
+ ]}
+ />
+ <Tooltip content="这是一段提示文字" placement="above">
+ <Button label="悬停查看提示" variant="secondary" />
+ </Tooltip>
+ <Popover
+ isOpen={popoverOpen}
+ onOpenChange={setPopoverOpen}
+ hasAutoFocus={false}
+ placement="below"
+ label="设置"
+ width={260}
+ content={
+ <VStack gap={2}>
+ <Heading level={4}>快捷设置</Heading>
+ <Text type="body" color="secondary" as="p">
+ 弹层可以承载简短设置、筛选或上下文信息。
+ </Text>
+ </VStack>
+ }>
+ <Button label="打开浮层" variant="secondary" />
+ </Popover>
+ <HoverCard
+ placement="above"
+ content={
+ <VStack gap={2} width={220}>
+ <HStack gap={2} vAlign="center">
+ <Avatar name="林晓" size="md" />
+ <VStack gap={0}>
+ <Heading level={5}>林晓</Heading>
+ <Text type="supporting" size="sm">
+ 产品设计
+ </Text>
+ </VStack>
+ </HStack>
+ <Text type="body" color="secondary" as="p">
+ 专注于设计系统与复杂交互体验。
+ </Text>
+ </VStack>
+ }>
+ <Button label="悬停查看资料" variant="ghost" />
+ </HoverCard>
+ </HStack>
+
+ <ContextMenu
+ presentation="adaptive"
+ items={[
+ { label: '剪切', onClick: () => {} },
+ { label: '复制', onClick: () => {} },
+ { label: '粘贴', onClick: () => {} },
+ ]}>
+ <Card padding={4}>
+ <VStack gap={1} hAlign="center">
+ <Text type="label">右键或长按此区域</Text>
+ <Text type="supporting" size="sm">
+ 打开上下文菜单
+ </Text>
+ </VStack>
+ </Card>
+ </ContextMenu>
+ </VStack>
+ )
+}
diff --git a/src/demos/NavigationDemo.tsx b/src/demos/NavigationDemo.tsx
new file mode 100644
index 0000000..ebc75f2
--- /dev/null
+++ b/src/demos/NavigationDemo.tsx
@@ -0,0 +1,35 @@
+import { useState } from 'react'
+import { BreadcrumbItem, Breadcrumbs } from '@astryxdesign/core/Breadcrumbs'
+import { VStack } from '@astryxdesign/core/VStack'
+import { Step, Stepper } from '@astryxdesign/core/Stepper'
+import { Tab, TabList } from '@astryxdesign/core/TabList'
+
+export function NavigationDemo() {
+ const [tab, setTab] = useState('overview')
+ const [step, setStep] = useState(2)
+
+ return (
+ <VStack gap={5}>
+ <Breadcrumbs>
+ <BreadcrumbItem href="/">首页</BreadcrumbItem>
+ <BreadcrumbItem href="/components">组件</BreadcrumbItem>
+ <BreadcrumbItem isCurrent>导航示例</BreadcrumbItem>
+ </Breadcrumbs>
+
+ <TabList value={tab} onChange={setTab} hasDivider>
+ <Tab value="overview" label="概览" />
+ <Tab value="activity" label="活动" />
+ <Tab value="settings" label="设置" />
+ </TabList>
+
+ <VStack width="100%">
+ <Stepper activeStep={step} orientation="horizontal" onStepClick={setStep}>
+ <Step step={0} label="购物车" />
+ <Step step={1} label="配送" />
+ <Step step={2} label="支付" />
+ <Step step={3} label="完成" />
+ </Stepper>
+ </VStack>
+ </VStack>
+ )
+}
diff --git a/src/demos/ProvidersDemo.tsx b/src/demos/ProvidersDemo.tsx
new file mode 100644
index 0000000..48f5a9f
--- /dev/null
+++ b/src/demos/ProvidersDemo.tsx
@@ -0,0 +1,29 @@
+import { Button } from '@astryxdesign/core/Button'
+import { HStack } from '@astryxdesign/core/HStack'
+import { Link } from '@astryxdesign/core/Link'
+import { Text } from '@astryxdesign/core/Text'
+import { VStack } from '@astryxdesign/core/VStack'
+import { useThemeMode } from '../theme/useThemeMode'
+
+export function ProvidersDemo() {
+ const { mode, toggleMode } = useThemeMode()
+ const nextModeLabel = mode === 'light' ? '切换到深色主题' : '切换到浅色主题'
+
+ return (
+ <VStack gap={3}>
+ <Text type="body" color="secondary" as="p">
+ 当前页面已经由 Theme、LinkProvider 和 InternationalizationProvider
+ 包裹。切换主题或点击站内链接即可看到实际效果。
+ </Text>
+ <HStack gap={3} wrap="wrap" vAlign="center">
+ <Button label={nextModeLabel} variant="primary" onClick={toggleMode} />
+ <Link href="/about" isStandalone>
+ 通过 LinkProvider 打开的站内链接
+ </Link>
+ </HStack>
+ <Text type="supporting" size="sm">
+ Astryx 内置文案同样是中文,例如「必填」「关闭」「跳至内容」。
+ </Text>
+ </VStack>
+ )
+}
diff --git a/src/demos/SelectionDemo.tsx b/src/demos/SelectionDemo.tsx
new file mode 100644
index 0000000..41ca918
--- /dev/null
+++ b/src/demos/SelectionDemo.tsx
@@ -0,0 +1,55 @@
+import { useState } from 'react'
+import {
+ CheckboxList,
+ CheckboxListItem,
+} from '@astryxdesign/core/CheckboxList'
+import { Grid } from '@astryxdesign/core/Grid'
+import { RadioList, RadioListItem } from '@astryxdesign/core/RadioList'
+import { Slider } from '@astryxdesign/core/Slider'
+
+export function SelectionDemo() {
+ const [notifications, setNotifications] = useState<string[]>(['email'])
+ const [plan, setPlan] = useState('team')
+ const [volume, setVolume] = useState(60)
+
+ return (
+ <Grid columns={{ minWidth: 220, max: 2 }} gap={5}>
+ <CheckboxList
+ label="通知方式"
+ description="可以选择多个渠道。"
+ value={notifications}
+ onChange={setNotifications}
+ hasDividers>
+ <CheckboxListItem
+ label="邮件"
+ value="email"
+ description="每周一发送摘要"
+ />
+ <CheckboxListItem
+ label="推送"
+ value="push"
+ description="在设备上即时提醒"
+ />
+ <CheckboxListItem
+ label="短信"
+ value="sms"
+ description="仅用于重要通知"
+ />
+ </CheckboxList>
+
+ <RadioList label="当前套餐" value={plan} onChange={setPlan}>
+ <RadioListItem label="个人版" value="personal" />
+ <RadioListItem label="团队版" value="team" />
+ <RadioListItem label="企业版" value="enterprise" />
+ </RadioList>
+
+ <Slider
+ label="提醒音量"
+ value={volume}
+ onChange={setVolume}
+ width="100%"
+ valueDisplay="text"
+ />
+ </Grid>
+ )
+}
diff --git a/src/demos/SelectorsDemo.tsx b/src/demos/SelectorsDemo.tsx
new file mode 100644
index 0000000..5fd299f
--- /dev/null
+++ b/src/demos/SelectorsDemo.tsx
@@ -0,0 +1,33 @@
+import { useState } from 'react'
+import { DateInput } from '@astryxdesign/core/DateInput'
+import { Grid } from '@astryxdesign/core/Grid'
+import { Selector } from '@astryxdesign/core/Selector'
+
+type DateString =
+ `${number}${number}${number}${number}-${number}${number}-${number}${number}`
+
+export function SelectorsDemo() {
+ const [fruit, setFruit] = useState<string | undefined>()
+ const [date, setDate] = useState<DateString | undefined>()
+
+ return (
+ <Grid columns={{ minWidth: 220, max: 2 }} gap={5}>
+ <Selector
+ label="最喜欢的水果"
+ options={['苹果', '香蕉', '橙子', '芒果', '菠萝']}
+ placeholder="请选择一种水果"
+ value={fruit}
+ onChange={setFruit}
+ width="100%"
+ />
+ <DateInput
+ label="开始日期"
+ placeholder="选择日期"
+ value={date}
+ onChange={setDate}
+ hasClear
+ width="100%"
+ />
+ </Grid>
+ )
+}
diff --git a/src/demos/TypographyDemo.tsx b/src/demos/TypographyDemo.tsx
new file mode 100644
index 0000000..75842d9
--- /dev/null
+++ b/src/demos/TypographyDemo.tsx
@@ -0,0 +1,36 @@
+import { Blockquote } from '@astryxdesign/core/Blockquote'
+import { CodeBlock } from '@astryxdesign/core/CodeBlock'
+import { Divider } from '@astryxdesign/core/Divider'
+import { Heading } from '@astryxdesign/core/Heading'
+import { Text } from '@astryxdesign/core/Text'
+import { VStack } from '@astryxdesign/core/VStack'
+
+const SNIPPET = `const theme = defineTheme({
+ name: 'ocean',
+ tokens: {'--color-accent': '#0077B6'},
+})`
+
+export function TypographyDemo() {
+ return (
+ <VStack gap={3}>
+ <VStack gap={1}>
+ <Heading level={3}>清晰的内容层级</Heading>
+ <Text type="body" color="secondary" as="p">
+ 标题、正文、辅助文字和代码共享同一套字体与节奏,让界面在中文和英文下都保持稳定。
+ </Text>
+ </VStack>
+ <Blockquote cite="Astryx 设计原则">
+ 把复杂性留在系统里,把清晰留给界面。
+ </Blockquote>
+ <Divider />
+ <CodeBlock
+ code={SNIPPET}
+ language="tsx"
+ title="theme.ts"
+ hasLineNumbers={false}
+ width="100%"
+ size="sm"
+ />
+ </VStack>
+ )
+}