aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/pages/ComponentsPage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/pages/ComponentsPage.tsx')
-rw-r--r--src/pages/ComponentsPage.tsx422
1 files changed, 422 insertions, 0 deletions
diff --git a/src/pages/ComponentsPage.tsx b/src/pages/ComponentsPage.tsx
new file mode 100644
index 0000000..8191acb
--- /dev/null
+++ b/src/pages/ComponentsPage.tsx
@@ -0,0 +1,422 @@
+import { Fragment, useMemo, useState, type ReactNode } from 'react'
+import { Card } from '@astryxdesign/core/Card'
+import { Center } from '@astryxdesign/core/Center'
+import { Collapsible, CollapsibleGroup } from '@astryxdesign/core/Collapsible'
+import { Divider } from '@astryxdesign/core/Divider'
+import { Grid } from '@astryxdesign/core/Grid'
+import { Heading } from '@astryxdesign/core/Heading'
+import { HStack } from '@astryxdesign/core/HStack'
+import { Icon } from '@astryxdesign/core/Icon'
+import { Section } from '@astryxdesign/core/Section'
+import { Text } from '@astryxdesign/core/Text'
+import { TextInput } from '@astryxdesign/core/TextInput'
+import { ToggleButton, ToggleButtonGroup } from '@astryxdesign/core/ToggleButton'
+import { VStack } from '@astryxdesign/core/VStack'
+import {
+ Blocks,
+ Layers,
+ Search,
+ SlidersHorizontal,
+} from 'lucide-react'
+import { Page, PageHeader } from '../components/Page'
+import { ASTRYX_COMPONENTS, type AstryxComponentGroup } from '../data/astryxComponents'
+import { ActionsDemo } from '../demos/ActionsDemo'
+import { ChatDemo } from '../demos/ChatDemo'
+import { CommandPaletteDemo } from '../demos/CommandPaletteDemo'
+import { DataDemo } from '../demos/DataDemo'
+import { DialogsDemo } from '../demos/DialogsDemo'
+import { FeedbackDemo } from '../demos/FeedbackDemo'
+import { InputsDemo } from '../demos/InputsDemo'
+import { LayoutDemo } from '../demos/LayoutDemo'
+import { MenusDemo } from '../demos/MenusDemo'
+import { NavigationDemo } from '../demos/NavigationDemo'
+import { ProvidersDemo } from '../demos/ProvidersDemo'
+import { SelectionDemo } from '../demos/SelectionDemo'
+import { SelectorsDemo } from '../demos/SelectorsDemo'
+import { TypographyDemo } from '../demos/TypographyDemo'
+
+interface DemoDefinition {
+ id: string
+ group: AstryxComponentGroup
+ title: string
+ description: string
+ preview: ReactNode
+ components: string[]
+}
+
+const DEMOS: DemoDefinition[] = [
+ {
+ id: 'actions',
+ group: 'base',
+ title: '操作与链接',
+ description: '按钮、图标按钮、工具栏、切换按钮和链接共享同一套交互状态。',
+ preview: <ActionsDemo />,
+ components: [
+ 'Button', 'ButtonGroup', 'IconButton', 'ToggleButton', 'ToggleButtonGroup',
+ 'SegmentedControl', 'SegmentedControlItem', 'Link', 'Kbd', 'Toolbar', 'Icon',
+ ],
+ },
+ {
+ id: 'typography',
+ group: 'base',
+ title: '文字与内容',
+ description: '标题、正文、引用、代码块和分隔线共同建立清晰的内容层级。',
+ preview: <TypographyDemo />,
+ components: [
+ 'Text', 'Heading', 'Blockquote', 'Code', 'CodeBlock', 'Markdown', 'Citation',
+ 'Token', 'Divider', 'VisuallyHidden', 'Timer', 'Timestamp',
+ ],
+ },
+ {
+ id: 'layout',
+ group: 'base',
+ title: '布局与卡片',
+ description: '响应式网格、可选卡片和折叠面板组成的真实布局结构。',
+ preview: <LayoutDemo />,
+ components: [
+ 'Stack', 'HStack', 'VStack', 'Grid', 'GridSpan', 'Center', 'Section', 'Card',
+ 'ClickableCard', 'SelectableCard', 'Collapsible', 'CollapsibleGroup',
+ 'ScrollableArea', 'Layout', 'LayoutHeader', 'LayoutContent', 'LayoutFooter',
+ 'LayoutPanel', 'Outline', 'OverflowList', 'AspectRatio', 'Thumbnail',
+ 'Carousel', 'MediaTheme',
+ ],
+ },
+ {
+ id: 'data',
+ group: 'base',
+ title: '列表、表格与头像',
+ description: '列表、表格、头像和计数徽标用于呈现结构化数据。',
+ preview: <DataDemo />,
+ components: [
+ 'List', 'ListItem', 'Item', 'MetadataList', 'MetadataListItem', 'Table',
+ 'TableBody', 'TableCell', 'TableFooter', 'TableHeader', 'TableHeaderCell',
+ 'TableRow', 'TableSelectionToolbar', 'TreeList', 'EmptyState', 'Skeleton',
+ 'Spinner', 'Avatar', 'AvatarGroup', 'AvatarGroupOverflow', 'AvatarStatusDot',
+ 'Badge', 'StatusDot', 'NavIcon',
+ ],
+ },
+ {
+ id: 'providers',
+ group: 'base',
+ title: '主题与全局能力',
+ description: '主题、链接和国际化 Provider 已经在页面中生效。',
+ preview: <ProvidersDemo />,
+ components: ['Theme', 'MediaTheme', 'LinkProvider', 'InternationalizationProvider'],
+ },
+ {
+ id: 'feedback',
+ group: 'base',
+ title: '反馈与状态',
+ description: '用横幅、进度、加载指示和骨架屏表达系统状态。',
+ preview: <FeedbackDemo />,
+ components: ['Banner', 'ProgressBar', 'Spinner', 'Skeleton', 'EmptyState'],
+ },
+ {
+ id: 'navigation',
+ group: 'base',
+ title: '导航与步骤',
+ description: '面包屑、标签页和步骤条帮助用户在层级和流程中定位。',
+ preview: <NavigationDemo />,
+ components: [
+ 'AppShell', 'TopNav', 'TopNavHeading', 'TopNavItem', 'TopNavMenu',
+ 'TopNavMegaMenu', 'TopNavMegaMenuFeaturedCard', 'TopNavMegaMenuItem',
+ 'SideNav', 'SideNavCollapseButton', 'SideNavHeading', 'SideNavItem',
+ 'SideNavSection', 'MobileNav', 'Breadcrumbs', 'BreadcrumbItem', 'Pagination',
+ 'TabList', 'Tab', 'TabMenu', 'Stepper', 'Step',
+ ],
+ },
+ {
+ id: 'chat',
+ group: 'base',
+ title: '聊天消息',
+ description: '消息列表、气泡、元数据和时间戳组成的对话界面。',
+ preview: <ChatDemo />,
+ components: [
+ 'ChatComposer', 'ChatComposerDrawer', 'ChatComposerInput', 'ChatDictationButton',
+ 'ChatLayout', 'ChatLayoutScrollButton', 'ChatMessage', 'ChatMessageBubble',
+ 'ChatMessageList', 'ChatMessageMetadata', 'ChatSendButton', 'ChatSystemMessage',
+ 'ChatTokenizedText', 'ChatToolCalls',
+ ],
+ },
+ {
+ id: 'inputs',
+ group: 'form',
+ title: '文本输入与校验',
+ description: '文本、长文本、开关和保存反馈组成的表单输入场景。',
+ preview: <InputsDemo />,
+ components: [
+ 'TextInput', 'TextArea', 'NumberInput', 'InputGroup', 'InputGroupText',
+ 'Field', 'FieldLabel', 'FieldStatus', 'FormLayout', 'FileInput', 'PowerSearch',
+ ],
+ },
+ {
+ id: 'selection',
+ group: 'form',
+ title: '选择控件',
+ description: '复选框、单选框和滑杆展示不同粒度的选择操作。',
+ preview: <SelectionDemo />,
+ components: [
+ 'CheckboxInput', 'CheckboxList', 'CheckboxListItem', 'RadioList',
+ 'RadioListItem', 'Switch', 'Slider',
+ ],
+ },
+ {
+ id: 'selectors',
+ group: 'form',
+ title: '选择器与日期',
+ description: '下拉选择、日期输入和更多选择模式集中在同一场景。',
+ preview: <SelectorsDemo />,
+ components: [
+ 'Selector', 'SelectorOption', 'MultiSelector', 'ComplexSelector', 'Typeahead',
+ 'BaseTypeahead', 'TypeaheadItem', 'Tokenizer', 'Calendar', 'DateInput',
+ 'DateRangeInput', 'DateTimeInput', 'TimeInput',
+ ],
+ },
+ {
+ id: 'dialogs',
+ group: 'overlay',
+ title: '对话框、抽屉与底部面板',
+ description: '点击按钮打开真实弹层,体验遮罩、焦点管理和关闭流程。',
+ preview: <DialogsDemo />,
+ components: [
+ 'Dialog', 'AlertDialog', 'DialogHeader', 'Drawer', 'DrawerHeader',
+ 'BottomSheet', 'BottomSheetSwitcher', 'Overlay',
+ ],
+ },
+ {
+ id: 'menus',
+ group: 'overlay',
+ title: '菜单与浮层',
+ description: '下拉菜单、上下文菜单、弹出框、提示和悬停卡片。',
+ preview: <MenusDemo />,
+ components: [
+ 'DropdownMenu', 'DropdownMenuCheckboxItem', 'DropdownMenuDivider',
+ 'DropdownMenuGroup', 'DropdownMenuItem', 'DropdownMenuRadioGroup',
+ 'DropdownMenuRadioItem', 'DropdownMenuSubMenu', 'ContextMenu', 'MoreMenu',
+ 'Popover', 'Tooltip', 'HoverCard',
+ ],
+ },
+ {
+ id: 'command',
+ group: 'overlay',
+ title: '命令面板与通知',
+ description: '命令面板把搜索、分组结果和键盘操作集中在一个浮层里。',
+ preview: <CommandPaletteDemo />,
+ components: [
+ 'CommandPalette', 'CommandPaletteEmpty', 'CommandPaletteFooter',
+ 'CommandPaletteGroup', 'CommandPaletteInput', 'CommandPaletteItem',
+ 'CommandPaletteList', 'Toast', 'Lightbox',
+ ],
+ },
+]
+
+const GROUPS = [
+ {
+ id: 'base',
+ label: '基础组件',
+ icon: Blocks,
+ description: '布局、排版、内容、操作、反馈、导航和数据展示等通用构建块。',
+ },
+ {
+ id: 'form',
+ label: '表单组件',
+ icon: SlidersHorizontal,
+ description: '输入、选择、日期、校验和表单布局控件。',
+ },
+ {
+ id: 'overlay',
+ label: '弹层组件',
+ icon: Layers,
+ description: '对话框、抽屉、菜单、浮层、提示和命令面板。',
+ },
+] as const
+
+type Filter = 'all' | AstryxComponentGroup
+
+const GROUP_COUNTS = GROUPS.reduce(
+ (counts, group) => {
+ counts[group.id] = ASTRYX_COMPONENTS.filter(
+ (component) => component.group === group.id,
+ ).length
+ return counts
+ },
+ { base: 0, form: 0, overlay: 0 } as Record<AstryxComponentGroup, number>,
+)
+
+function DemoCard({ demo }: { demo: DemoDefinition }) {
+ return (
+ <Card padding={0}>
+ <Section variant="muted" padding={5}>
+ {demo.preview}
+ </Section>
+ <Section variant="transparent" padding={5}>
+ <VStack gap={2}>
+ <Heading level={3}>{demo.title}</Heading>
+ <Text type="body" color="secondary" as="p">
+ {demo.description}
+ </Text>
+ <Text type="supporting" size="sm" maxLines={2}>
+ 覆盖组件:{demo.components.join('、')}
+ </Text>
+ </VStack>
+ </Section>
+ </Card>
+ )
+}
+
+export function ComponentsPage() {
+ const [filter, setFilter] = useState<Filter>('all')
+ const [query, setQuery] = useState('')
+
+ const results = useMemo(() => {
+ const normalized = query.trim().toLowerCase()
+
+ return DEMOS.filter((demo) => {
+ const matchesGroup = filter === 'all' || demo.group === filter
+ const matchesQuery =
+ normalized.length === 0 ||
+ demo.title.toLowerCase().includes(normalized) ||
+ demo.description.toLowerCase().includes(normalized) ||
+ demo.components.some((name) => name.toLowerCase().includes(normalized))
+
+ return matchesGroup && matchesQuery
+ })
+ }, [filter, query])
+
+ const sections = GROUPS.map((group) => ({
+ ...group,
+ demos: results.filter((demo) => demo.group === group.id),
+ })).filter((group) => group.demos.length > 0)
+
+ const coveredComponents = new Set(results.flatMap((demo) => demo.components)).size
+ const isFiltering = filter !== 'all' || query.trim().length > 0
+
+ return (
+ <Page>
+ <VStack gap={8}>
+ <PageHeader
+ eyebrow="实时预览"
+ title="组件"
+ description={`通过可交互的组合场景展示 Astryx 组件的使用效果。全部 ${ASTRYX_COMPONENTS.length} 个组件按基础、表单和弹层分类整理。`}
+ />
+
+ <VStack gap={2}>
+ <Text type="supporting" size="sm">
+ 组件名称与 API 保留 Astryx 原始命名;示例中的操作和内容均可直接体验。
+ </Text>
+ <HStack gap={3} wrap="wrap">
+ <Text type="supporting" size="sm">
+ 基础组件 {GROUP_COUNTS.base}
+ </Text>
+ <Text type="supporting" size="sm">
+ 表单组件 {GROUP_COUNTS.form}
+ </Text>
+ <Text type="supporting" size="sm">
+ 弹层组件 {GROUP_COUNTS.overlay}
+ </Text>
+ </HStack>
+ </VStack>
+
+ <Divider />
+
+ <VStack gap={4}>
+ <HStack gap={4} wrap="wrap" vAlign="end">
+ <TextInput
+ label="搜索组件"
+ isLabelHidden
+ placeholder="搜索组件名或场景"
+ value={query}
+ onChange={setQuery}
+ startIcon={Search}
+ size="lg"
+ width={360}
+ />
+ <ToggleButtonGroup
+ label="按组件分类筛选"
+ value={filter}
+ onChange={(value) => setFilter((value as Filter | null) ?? 'all')}
+ size="sm">
+ <ToggleButton label="全部" value="all" />
+ {GROUPS.map((group) => (
+ <ToggleButton
+ key={group.id}
+ label={group.label}
+ value={group.id}
+ />
+ ))}
+ </ToggleButtonGroup>
+ </HStack>
+ <Text type="supporting" size="sm">
+ {isFiltering
+ ? `${results.length} 个场景,覆盖 ${coveredComponents} 个组件`
+ : `${DEMOS.length} 个场景,覆盖 ${coveredComponents} 个组件`}
+ </Text>
+ </VStack>
+
+ {results.length === 0 ? (
+ <Center minHeight={220}>
+ <VStack gap={2} hAlign="center">
+ <Text type="label">没有找到匹配的场景。</Text>
+ <Text type="supporting" color="secondary">
+ 试试更宽泛的关键词,或切换回「全部」。
+ </Text>
+ </VStack>
+ </Center>
+ ) : (
+ sections.map((group, index) => (
+ <Fragment key={group.id}>
+ {index > 0 ? <Divider /> : null}
+ <VStack gap={4}>
+ <VStack gap={1}>
+ <HStack gap={2} vAlign="center" wrap="wrap">
+ <Icon icon={group.icon} size="md" color="accent" />
+ <Heading level={2}>{group.label}</Heading>
+ <Text type="supporting" size="sm">
+ {group.demos.length} 个场景
+ </Text>
+ </HStack>
+ <Text type="body" color="secondary" as="p">
+ {group.description}
+ </Text>
+ </VStack>
+ <Grid columns={{ minWidth: 460, max: 2 }} gap={4} align="start">
+ {group.demos.map((demo) => (
+ <DemoCard key={demo.id} demo={demo} />
+ ))}
+ </Grid>
+ </VStack>
+ </Fragment>
+ ))
+ )}
+
+ <Divider />
+
+ <CollapsibleGroup type="single">
+ <Collapsible
+ value="all-components"
+ trigger={<Text type="label">查看完整组件清单</Text>}>
+ <VStack gap={3}>
+ {GROUPS.map((group) => (
+ <VStack key={group.id} gap={1}>
+ <Text type="label">{group.label}</Text>
+ <Text type="supporting" size="sm">
+ {ASTRYX_COMPONENTS.filter(
+ (component) => component.group === group.id,
+ )
+ .map((component) => component.name)
+ .join('、')}
+ </Text>
+ </VStack>
+ ))}
+ </VStack>
+ </Collapsible>
+ </CollapsibleGroup>
+
+ <Text type="supporting" size="sm">
+ 共收录 {ASTRYX_COMPONENTS.length} 个组件,数据来自
+ @astryxdesign/core 0.7.2。
+ </Text>
+ </VStack>
+ </Page>
+ )
+}