Files
stash-ui/site/App.tsx
T
CNWei 707cf98fe0 feat(ui): 新增站点外壳组件 Brand / SearchField / Ticker
把应用里各自手写的三块收进库,风格沿用前台、行为参数化:

- Brand:logo 图片或首字圆形标记 + 名称,href 决定是链接还是纯展示,size/tone 可选
- SearchField:语义版搜索框(onChange 直接给字符串),variant 切 plain / card,
  支持 shortcut 提示与 hotkey 聚焦
- Ticker:无限滚动信息条,内部三份内容按实测宽度位移,循环不跳帧;
  speed / paused / label 可配
- 令牌补展示级字号 --su-font-size-2xl / --su-line-height-2xl(32 / 40)
- 展示页新增 #site-chrome(Brand / Ticker / SearchField),COMPONENTS.md 补契约与 changelog
2026-11-01 15:28:17 +08:00

242 lines
7.5 KiB
TypeScript

import { useEffect, useState } from 'react'
import { Toaster } from '../src'
import { Avatars } from './sections/Avatars'
import { Breadcrumbs } from './sections/Breadcrumbs'
import { Buttons } from './sections/Buttons'
import { Cards } from './sections/Cards'
import { Covers } from './sections/Covers'
import { Checkboxes } from './sections/Checkboxes'
import { Composition } from './sections/Composition'
import { Drawers } from './sections/Drawers'
import { Feedback } from './sections/Feedback'
import { Icons } from './sections/Icons'
import { Images } from './sections/Images'
import { Inputs } from './sections/Inputs'
import { Layouts } from './sections/Layouts'
import { SiteChrome } from './sections/SiteChrome'
import { Marks } from './sections/Marks'
import { MenuSection } from './sections/MenuSection'
import { Modals } from './sections/Modals'
import { Overview } from './sections/Overview'
import { PageLayout } from './sections/PageLayout'
import { Paginations } from './sections/Paginations'
import { Progresses } from './sections/Progresses'
import { RadioGroups } from './sections/RadioGroups'
import { Results } from './sections/Results'
import { ScrollTopSection } from './sections/ScrollTopSection'
import { Segmenteds } from './sections/Segmenteds'
import { Selects } from './sections/Selects'
import { Skeletons } from './sections/Skeletons'
import { Spinners } from './sections/Spinners'
import { StepsSection } from './sections/StepsSection'
import { Switches } from './sections/Switches'
import { Tables } from './sections/Tables'
import { TabsSection } from './sections/TabsSection'
import { Toasts } from './sections/Toasts'
import { Tooltips } from './sections/Tooltips'
import { Typographies } from './sections/Typographies'
/**
* 组件展示站:左侧目录 + 右侧示例。
*
* 页面只负责摆示例,所有样式都来自组件库本身;
* 站点自己的排版样式收在 `app.css`,不污染组件。
*/
const NAV = [
{
title: '基础',
items: [
{ id: 'overview', label: '设计令牌' },
{ id: 'typography', label: '排版 Typography' },
{ id: 'icons', label: '图标 Icon' },
{ id: 'buttons', label: '按钮 Button' },
],
},
{
title: '展示',
items: [
{ id: 'cards', label: '卡片 Card' },
{ id: 'cover', label: '封面卡 Cover' },
{ id: 'image', label: '图片 Image' },
{ id: 'marks', label: '标记 Tag / Badge' },
{ id: 'avatars', label: '头像 Avatar' },
{ id: 'table', label: '表格 Table' },
{ id: 'result', label: '结果 Result' },
{ id: 'progress', label: '进度 Progress' },
{ id: 'steps', label: '步骤 Steps' },
],
},
{
title: '表单',
items: [
{ id: 'inputs', label: '输入框 Input' },
{ id: 'select', label: '下拉 Select' },
{ id: 'switch', label: '开关 Switch' },
{ id: 'checkbox', label: '复选框 Checkbox' },
{ id: 'radio', label: '单选 Radio' },
{ id: 'segmented', label: '分段 Segmented' },
],
},
{
title: '导航',
items: [
{ id: 'breadcrumb', label: '面包屑 Breadcrumb' },
{ id: 'tabs', label: '标签页 Tabs' },
{ id: 'menu', label: '菜单 Menu' },
],
},
{
title: '交互',
items: [
{ id: 'skeleton', label: '骨架屏 Skeleton' },
{ id: 'spinner', label: '加载 Spinner' },
{ id: 'feedback', label: '提示与空态' },
{ id: 'toast', label: '轻提示 Toast' },
{ id: 'modal', label: '弹窗 Modal' },
{ id: 'drawer', label: '抽屉 Drawer' },
{ id: 'pagination', label: '分页 Pagination' },
{ id: 'scrolltop', label: '回到顶部' },
{ id: 'tooltip', label: '工具提示 Tooltip' },
],
},
{
title: '布局与组合',
items: [
{ id: 'layout', label: '页面骨架 Layout' },
{ id: 'layouts', label: '网格与间距' },
{ id: 'site-chrome', label: '站点外壳' },
{ id: 'composition', label: '组合示例' },
],
},
] as const
const SECTION_IDS = NAV.flatMap((group) => group.items.map((item) => item.id))
/** 滚动监听:当前视口里最靠上的那一节高亮 */
function useActiveSection(): string {
const [active, setActive] = useState<string>(SECTION_IDS[0])
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
const visible = entries
.filter((entry) => entry.isIntersecting)
.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)
if (visible.length > 0) setActive(visible[0].target.id)
},
{ rootMargin: '-72px 0px -68% 0px' },
)
for (const id of SECTION_IDS) {
const element = document.getElementById(id)
if (element !== null) observer.observe(element)
}
return () => observer.disconnect()
}, [])
return active
}
export function App() {
const active = useActiveSection()
return (
<div className="doc-shell">
<aside className="doc-sidebar">
<a className="doc-brand" href="#overview">
<span className="doc-brand-mark">S</span>
<span>
<strong>Stash UI</strong>
<em>v0.3.1</em>
</span>
</a>
<p className="doc-brand-desc">
一套按 Alright 主题打磨的基础组件,供 Stash 控制台与后续页面组合使用。
</p>
<nav className="doc-nav" aria-label="组件目录">
{NAV.map((group) => (
<section key={group.title}>
<h2>{group.title}</h2>
<ul>
{group.items.map((item) => (
<li key={item.id}>
<a href={`#${item.id}`} className={active === item.id ? 'is-active' : undefined}>
{item.label}
</a>
</li>
))}
</ul>
</section>
))}
</nav>
<footer className="doc-sidebar-footer">
令牌定义:<code>src/theme/tokens.css</code>
<a className="doc-demo-link" href="/demo.html">
整页拼装示例 →
</a>
</footer>
</aside>
<main className="doc-main">
<header className="doc-hero">
<p className="doc-eyebrow">DESIGN SYSTEM</p>
<h1>Stash UI</h1>
<p className="doc-hero-desc">
独立于应用的设计系统项目:令牌、基础组件、以及一个可以直接翻的组件展示页。
组件只依赖令牌,不依赖任何页面数据,所以换页面、换应用都能直接拼。
</p>
<div className="doc-hero-meta">
<span>42 个基础组件</span>
<span>零运行时依赖</span>
<span>CSS Modules + 设计令牌</span>
<span>对齐 Alright 参考站</span>
</div>
</header>
<Overview />
<Typographies />
<Icons />
<Buttons />
<Cards />
<Covers />
<Images />
<Marks />
<Avatars />
<Tables />
<Results />
<Progresses />
<StepsSection />
<Inputs />
<Selects />
<Switches />
<Checkboxes />
<RadioGroups />
<Segmenteds />
<Breadcrumbs />
<TabsSection />
<MenuSection />
<Skeletons />
<Spinners />
<Feedback />
<Toasts />
<Modals />
<Drawers />
<Paginations />
<ScrollTopSection />
<Tooltips />
<PageLayout />
<Layouts />
<SiteChrome />
<Composition />
</main>
<Toaster />
</div>
)
}