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/IconsPage.tsx | 334 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 src/pages/IconsPage.tsx (limited to 'src/pages/IconsPage.tsx') diff --git a/src/pages/IconsPage.tsx b/src/pages/IconsPage.tsx new file mode 100644 index 0000000..6b836ca --- /dev/null +++ b/src/pages/IconsPage.tsx @@ -0,0 +1,334 @@ +import { useMemo, useState } from 'react' +import { Button } from '@astryxdesign/core/Button' +import { Center } from '@astryxdesign/core/Center' +import { ClickableCard } from '@astryxdesign/core/ClickableCard' +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 type { IconName } from '@astryxdesign/core/Icon' +import { Text } from '@astryxdesign/core/Text' +import { TextInput } from '@astryxdesign/core/TextInput' +import { useToast } from '@astryxdesign/core/Toast' +import { ToggleButton, ToggleButtonGroup } from '@astryxdesign/core/ToggleButton' +import { VStack } from '@astryxdesign/core/VStack' +import { + ChevronLeft, + ChevronRight, + icons, + Search, + type LucideIcon, +} from 'lucide-react' +import { Page, PageHeader } from '../components/Page' + +type IconSet = 'semantic' | 'lucide' + +interface SemanticIcon { + name: IconName + label: string +} + +const SEMANTIC_ICONS: SemanticIcon[] = [ + { name: 'close', label: '关闭' }, + { name: 'chevronDown', label: '向下展开' }, + { name: 'chevronLeft', label: '向左' }, + { name: 'chevronRight', label: '向右' }, + { name: 'chevronsLeft', label: '跳到开头' }, + { name: 'chevronsRight', label: '跳到末尾' }, + { name: 'check', label: '确认' }, + { name: 'success', label: '成功' }, + { name: 'error', label: '错误' }, + { name: 'warning', label: '警告' }, + { name: 'info', label: '信息' }, + { name: 'calendar', label: '日历' }, + { name: 'clock', label: '时间' }, + { name: 'externalLink', label: '外部链接' }, + { name: 'menu', label: '菜单' }, + { name: 'moreHorizontal', label: '更多' }, + { name: 'search', label: '搜索' }, + { name: 'upload', label: '上传' }, + { name: 'arrowUp', label: '向上' }, + { name: 'arrowDown', label: '向下' }, + { name: 'arrowsUpDown', label: '排序' }, + { name: 'funnel', label: '筛选' }, + { name: 'eyeSlash', label: '隐藏' }, + { name: 'viewColumns', label: '列设置' }, + { name: 'copy', label: '复制' }, + { name: 'checkDouble', label: '已复制' }, + { name: 'wrench', label: '设置' }, + { name: 'stop', label: '停止' }, + { name: 'microphone', label: '麦克风' }, +] + +interface LucideEntry { + name: string + Icon: LucideIcon +} + +const LUCIDE_ICONS: LucideEntry[] = Object.entries(icons) + .map(([name, Icon]) => ({ name, Icon })) + .sort((a, b) => a.name.localeCompare(b.name)) + +const LUCIDE_PAGE_SIZE = 120 + +export function IconsPage() { + const [activeSet, setActiveSet] = useState('semantic') + const [query, setQuery] = useState('') + const [page, setPage] = useState(1) + const showToast = useToast() + + const changeSet = (value: string | null) => { + setActiveSet(value === 'lucide' ? 'lucide' : 'semantic') + setPage(1) + } + + const changeQuery = (value: string) => { + setQuery(value) + setPage(1) + } + + const copyUsage = async (code: string, name: string) => { + try { + if (!navigator.clipboard) throw new Error('Clipboard unavailable') + await navigator.clipboard.writeText(code) + showToast({ body: `已复制 ${name} 的用法`, type: 'info' }) + } catch { + showToast({ body: '复制失败,请检查浏览器权限', type: 'error' }) + } + } + + const normalizedQuery = query.trim().toLowerCase() + + const filteredSemantic = useMemo( + () => + SEMANTIC_ICONS.filter( + (icon) => + normalizedQuery.length === 0 || + icon.name.toLowerCase().includes(normalizedQuery) || + icon.label.includes(query.trim()), + ), + [normalizedQuery, query], + ) + + const filteredLucide = useMemo( + () => + normalizedQuery.length === 0 + ? LUCIDE_ICONS + : LUCIDE_ICONS.filter((icon) => + icon.name.toLowerCase().includes(normalizedQuery), + ), + [normalizedQuery], + ) + + const totalPages = Math.max(1, Math.ceil(filteredLucide.length / LUCIDE_PAGE_SIZE)) + const currentPage = Math.min(page, totalPages) + const pageStart = (currentPage - 1) * LUCIDE_PAGE_SIZE + const pageItems = filteredLucide.slice( + pageStart, + pageStart + LUCIDE_PAGE_SIZE, + ) + + const isSemantic = activeSet === 'semantic' + const visibleCount = isSemantic ? filteredSemantic.length : filteredLucide.length + + return ( + + + + + + + + + + + + + + + + {isSemantic + ? '语义图标通过名称使用,会自动适配当前主题。' + : 'Lucide 图标以组件形式传入 Icon,可与语义图标混用。'} + + + + {visibleCount === 0 ? ( +
+ + + 没有找到匹配的图标。 + + 试试更短的关键词。 + + +
+ ) : isSemantic ? ( + <> + + 共 {filteredSemantic.length} 个语义图标 + + + {filteredSemantic.map((semanticIcon) => ( + + void copyUsage( + ``, + semanticIcon.name, + ) + } + label={`复制 ${semanticIcon.name} 图标用法`}> + + + + {semanticIcon.name} + + + {semanticIcon.label} + + + + ))} + + + ) : ( + <> + + + 显示 {pageStart + 1}–{Math.min(pageStart + LUCIDE_PAGE_SIZE, filteredLucide.length)} 个,共 {filteredLucide.length} 个图标 + + +