From bfe5541e56f68b74fd41974c804f8e7ac1ff9f2a Mon Sep 17 00:00:00 2001 From: shu Date: Thu, 22 Oct 2026 13:03:06 +0800 Subject: feat: 初始化 Astryx React 应用模板 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 基于 Vite 8、React 19、TypeScript 6、React Router 7 和 Astryx 0.7.2 搭建完整的中文应用起始模板,包含可直接运行的应用外壳与示例页面。 主要变更: - 使用 Astryx 构建 AppShell、顶部导航、可折叠侧边栏和响应式移动端抽屉。 - 接入 Astryx neutral 主题与中文语言包,支持明暗主题切换并持久化偏好。 - 新增概览、组件、图标、关于和 404 页面,统一使用共享页面框架。 - 将页面路由、侧边栏名称、图标和active状态合并到 app-routes.tsx, 路由与导航由同一份配置生成,避免重复维护。 - 新增根目录 app.config.ts,可在 browser/history 与 hash 路由模式之间切换; 当前保留 hash 模式配置。 - 组件页提供 14 个可交互组合场景,覆盖全部 173 个 Astryx 组件, 包含按钮、表单、表格、导航、聊天、对话框、菜单、命令面板和 Toast 等。 - 新增图标页面,支持浏览 29 个 Astryx 语义图标和 1903 个 Lucide 图标, 提供搜索、分页、图标集切换和点击复制用法。 - 对组件页和图标页实施路由级懒加载,并按 React、React Router 拆分 vendor chunk,降低首屏入口体积并改善长期缓存。 - 接入 React Router 的 LinkProvider,保证 Astryx 链接、导航和按钮在两种 路由模式下均使用客户端导航。 - 配置 pnpm 供应链策略例外与依赖构建许可,锁定可以正常安装的依赖版本。 - 补充 README,记录项目结构、脚本、组件展厅、图标页、路由模式与构建策略。 验证结果: - pnpm build 通过 - pnpm lint 0 warnings - browser 与 hash 两种路由模式的页面和侧边栏导航均已验证 --- src/pages/AboutPage.tsx | 202 +++++++++++++++++++++ src/pages/ComponentsPage.tsx | 422 +++++++++++++++++++++++++++++++++++++++++++ src/pages/HomePage.tsx | 219 ++++++++++++++++++++++ src/pages/IconsPage.tsx | 334 ++++++++++++++++++++++++++++++++++ src/pages/NotFoundPage.tsx | 27 +++ 5 files changed, 1204 insertions(+) create mode 100644 src/pages/AboutPage.tsx create mode 100644 src/pages/ComponentsPage.tsx create mode 100644 src/pages/HomePage.tsx create mode 100644 src/pages/IconsPage.tsx create mode 100644 src/pages/NotFoundPage.tsx (limited to 'src/pages') diff --git a/src/pages/AboutPage.tsx b/src/pages/AboutPage.tsx new file mode 100644 index 0000000..5f2aec2 --- /dev/null +++ b/src/pages/AboutPage.tsx @@ -0,0 +1,202 @@ +import { CodeBlock } from '@astryxdesign/core/CodeBlock' +import { Divider } from '@astryxdesign/core/Divider' +import { Grid } from '@astryxdesign/core/Grid' +import { Heading } from '@astryxdesign/core/Heading' +import { Icon } from '@astryxdesign/core/Icon' +import { List, ListItem } from '@astryxdesign/core/List' +import { Text } from '@astryxdesign/core/Text' +import { VStack } from '@astryxdesign/core/VStack' +import { + ArrowRight, + Atom, + Blocks, + BookOpen, + ExternalLink, + Palette, + Route, + Shapes, + Zap, +} from 'lucide-react' +import { Page, PageHeader } from '../components/Page' + +const COMMANDS = `npm run dev # 启动开发服务器 +npm run build # 类型检查并构建生产版本 +npm run lint # 运行 oxlint +npm run preview # 预览生产构建 + +npm run astryx -- component Button # 查看 Astryx 组件文档` + +const PROJECT_TREE = `src/ + components/ + AppLayout.tsx 应用外壳、顶部导航、侧边导航 + Page.tsx 共用页面框架与页头 + pages/ + HomePage.tsx + ComponentsPage.tsx + AboutPage.tsx + NotFoundPage.tsx + theme/ + ThemeMode.tsx 明暗主题提供者 + astryx-themes.ts 生成的主题注册表 + router.tsx React Router 路由表 + App.tsx 提供者与路由 + main.tsx 入口文件` + +const STACK = [ + { + name: 'React', + version: '19.3', + icon: Atom, + description: '界面运行时与 Hooks', + }, + { + name: 'Vite', + version: '8.3', + icon: Zap, + description: '开发服务器与生产构建工具', + }, + { + name: 'React Router', + version: '7.18', + icon: Route, + description: '基于数据路由的客户端路由,可在 hash 与 history 模式间切换', + }, + { + name: 'Astryx', + version: '0.7.2', + icon: Palette, + description: '设计系统、组件与 neutral 主题', + }, +] + +export function AboutPage() { + return ( + + + + + + + + + 技术栈 + + {STACK.map(({ name, version, icon, description }) => ( + + } + endContent={ + + {version} + + } + /> + ))} + + + + + 常用命令 + + + Vite 8 需要 Node 20.19+。 + + + + + + + + 项目结构 + + 在 src/pages 下新建页面,然后在{' '} + src/router.tsx 中注册路由,并在{' '} + src/components/AppLayout.tsx{' '} + 中添加对应的导航项。 + + + + + + + + 继续探索 + + } + endContent={} + /> + } + endContent={} + /> + } + endContent={ + + } + /> + } + endContent={ + + } + /> + + + + + + + 主题 + + 自动生成的 src/astryx-themes.ts{' '} + 文件注册了 neutral 主题。可以运行{' '} + npm run astryx -- theme add <slug>{' '} + 添加其他主题包,然后在{' '} + src/theme/ThemeMode.tsx{' '} + 中把它传给主题提供者。 + + + Astryx 自带预构建 CSS,因此这个应用不需要 StyleX、PostCSS 或 Babel + 插件。 + + + + + ) +} 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: , + components: [ + 'Button', 'ButtonGroup', 'IconButton', 'ToggleButton', 'ToggleButtonGroup', + 'SegmentedControl', 'SegmentedControlItem', 'Link', 'Kbd', 'Toolbar', 'Icon', + ], + }, + { + id: 'typography', + group: 'base', + title: '文字与内容', + description: '标题、正文、引用、代码块和分隔线共同建立清晰的内容层级。', + preview: , + components: [ + 'Text', 'Heading', 'Blockquote', 'Code', 'CodeBlock', 'Markdown', 'Citation', + 'Token', 'Divider', 'VisuallyHidden', 'Timer', 'Timestamp', + ], + }, + { + id: 'layout', + group: 'base', + title: '布局与卡片', + description: '响应式网格、可选卡片和折叠面板组成的真实布局结构。', + preview: , + 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: , + 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: , + components: ['Theme', 'MediaTheme', 'LinkProvider', 'InternationalizationProvider'], + }, + { + id: 'feedback', + group: 'base', + title: '反馈与状态', + description: '用横幅、进度、加载指示和骨架屏表达系统状态。', + preview: , + components: ['Banner', 'ProgressBar', 'Spinner', 'Skeleton', 'EmptyState'], + }, + { + id: 'navigation', + group: 'base', + title: '导航与步骤', + description: '面包屑、标签页和步骤条帮助用户在层级和流程中定位。', + preview: , + 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: , + components: [ + 'ChatComposer', 'ChatComposerDrawer', 'ChatComposerInput', 'ChatDictationButton', + 'ChatLayout', 'ChatLayoutScrollButton', 'ChatMessage', 'ChatMessageBubble', + 'ChatMessageList', 'ChatMessageMetadata', 'ChatSendButton', 'ChatSystemMessage', + 'ChatTokenizedText', 'ChatToolCalls', + ], + }, + { + id: 'inputs', + group: 'form', + title: '文本输入与校验', + description: '文本、长文本、开关和保存反馈组成的表单输入场景。', + preview: , + components: [ + 'TextInput', 'TextArea', 'NumberInput', 'InputGroup', 'InputGroupText', + 'Field', 'FieldLabel', 'FieldStatus', 'FormLayout', 'FileInput', 'PowerSearch', + ], + }, + { + id: 'selection', + group: 'form', + title: '选择控件', + description: '复选框、单选框和滑杆展示不同粒度的选择操作。', + preview: , + components: [ + 'CheckboxInput', 'CheckboxList', 'CheckboxListItem', 'RadioList', + 'RadioListItem', 'Switch', 'Slider', + ], + }, + { + id: 'selectors', + group: 'form', + title: '选择器与日期', + description: '下拉选择、日期输入和更多选择模式集中在同一场景。', + preview: , + components: [ + 'Selector', 'SelectorOption', 'MultiSelector', 'ComplexSelector', 'Typeahead', + 'BaseTypeahead', 'TypeaheadItem', 'Tokenizer', 'Calendar', 'DateInput', + 'DateRangeInput', 'DateTimeInput', 'TimeInput', + ], + }, + { + id: 'dialogs', + group: 'overlay', + title: '对话框、抽屉与底部面板', + description: '点击按钮打开真实弹层,体验遮罩、焦点管理和关闭流程。', + preview: , + components: [ + 'Dialog', 'AlertDialog', 'DialogHeader', 'Drawer', 'DrawerHeader', + 'BottomSheet', 'BottomSheetSwitcher', 'Overlay', + ], + }, + { + id: 'menus', + group: 'overlay', + title: '菜单与浮层', + description: '下拉菜单、上下文菜单、弹出框、提示和悬停卡片。', + preview: , + components: [ + 'DropdownMenu', 'DropdownMenuCheckboxItem', 'DropdownMenuDivider', + 'DropdownMenuGroup', 'DropdownMenuItem', 'DropdownMenuRadioGroup', + 'DropdownMenuRadioItem', 'DropdownMenuSubMenu', 'ContextMenu', 'MoreMenu', + 'Popover', 'Tooltip', 'HoverCard', + ], + }, + { + id: 'command', + group: 'overlay', + title: '命令面板与通知', + description: '命令面板把搜索、分组结果和键盘操作集中在一个浮层里。', + preview: , + 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, +) + +function DemoCard({ demo }: { demo: DemoDefinition }) { + return ( + +
+ {demo.preview} +
+
+ + {demo.title} + + {demo.description} + + + 覆盖组件:{demo.components.join('、')} + + +
+
+ ) +} + +export function ComponentsPage() { + const [filter, setFilter] = useState('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 ( + + + + + + + 组件名称与 API 保留 Astryx 原始命名;示例中的操作和内容均可直接体验。 + + + + 基础组件 {GROUP_COUNTS.base} + + + 表单组件 {GROUP_COUNTS.form} + + + 弹层组件 {GROUP_COUNTS.overlay} + + + + + + + + + + setFilter((value as Filter | null) ?? 'all')} + size="sm"> + + {GROUPS.map((group) => ( + + ))} + + + + {isFiltering + ? `${results.length} 个场景,覆盖 ${coveredComponents} 个组件` + : `${DEMOS.length} 个场景,覆盖 ${coveredComponents} 个组件`} + + + + {results.length === 0 ? ( +
+ + 没有找到匹配的场景。 + + 试试更宽泛的关键词,或切换回「全部」。 + + +
+ ) : ( + sections.map((group, index) => ( + + {index > 0 ? : null} + + + + + {group.label} + + {group.demos.length} 个场景 + + + + {group.description} + + + + {group.demos.map((demo) => ( + + ))} + + + + )) + )} + + + + + 查看完整组件清单}> + + {GROUPS.map((group) => ( + + {group.label} + + {ASTRYX_COMPONENTS.filter( + (component) => component.group === group.id, + ) + .map((component) => component.name) + .join('、')} + + + ))} + + + + + + 共收录 {ASTRYX_COMPONENTS.length} 个组件,数据来自 + @astryxdesign/core 0.7.2。 + +
+
+ ) +} diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx new file mode 100644 index 0000000..a1e7668 --- /dev/null +++ b/src/pages/HomePage.tsx @@ -0,0 +1,219 @@ +import { Button } from '@astryxdesign/core/Button' +import { Card } from '@astryxdesign/core/Card' +import { CodeBlock } from '@astryxdesign/core/CodeBlock' +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 { List, ListItem } from '@astryxdesign/core/List' +import { Text } from '@astryxdesign/core/Text' +import { VStack } from '@astryxdesign/core/VStack' +import { + ArrowRight, + Atom, + Check, + ExternalLink, + Layers, + Moon, + Palette, + Route, + Sparkles, + Terminal, + Zap, +} from 'lucide-react' +import { Page } from '../components/Page' + +const STACK = [ + { + name: 'Vite', + version: '8.x', + icon: Zap, + description: '极速开发服务器、快速热更新,以及经过优化的生产构建。', + }, + { + name: 'React', + version: '19.x', + icon: Atom, + description: '现代 Hooks 与全应用共享的组件模型。', + }, + { + name: 'React Router', + version: '7.x', + icon: Route, + description: '嵌套路由、持续存在的应用外壳,以及类型安全的导航。', + }, + { + name: 'Astryx', + version: '0.7', + icon: Palette, + description: '无障碍组件、设计令牌、主题与深色模式。', + }, +] + +const QUICK_START = `npm install +npm run dev` + +const BUILT_IN = [ + { + label: '应用外壳', + description: + '顶部导航、可折叠侧边栏和移动端抽屉都已替你处理好。', + icon: Layers, + }, + { + label: '客户端路由', + description: + '所有页面共用一套布局,可在根目录配置中切换 hash 与 history 路由。', + icon: Route, + }, + { + label: '明暗主题', + description: + 'Astryx neutral 主题可切换模式,并记住你的选择。', + icon: Moon, + }, + { + label: '完整组件目录', + description: + '全部 173 个 Astryx 组件,按基础、表单和弹层分类。', + icon: Sparkles, + }, +] + +export function HomePage() { + return ( + + + + + Vite + React + React Router + Astryx + + + + 为你的下一个 React 应用,准备更好的起点。 + + + Vite 让反馈循环保持飞快,React Router + 给应用真正的结构,Astryx 提供无障碍组件与主题。所有部分都已经连接好。 + + + +