feat: stash-ui 0.1.0(40 个组件 + 文档站 + 主题机制)
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
import { useState } from 'react'
|
||||
import { Card, Grid, Layout, Segmented, Space, Tag, Text, Title } from '../../src'
|
||||
import { Demo } from '../components/Demo'
|
||||
import { PropsTable } from '../components/PropsTable'
|
||||
import { Section } from '../components/Section'
|
||||
|
||||
/** 文档里的迷你页面内容:只为了把 Layout 的结构摆出来 */
|
||||
|
||||
function DemoHeader() {
|
||||
return (
|
||||
<div className="layout-bar">
|
||||
<span className="layout-brand">STASH</span>
|
||||
<span className="layout-bar-actions">
|
||||
<span className="layout-nav-item is-active">工作台</span>
|
||||
<span className="layout-nav-item">常用推荐</span>
|
||||
<span className="layout-nav-item">设置</span>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DemoSider({ compact = false }: { compact?: boolean }) {
|
||||
return (
|
||||
<div className="layout-side">
|
||||
<span className="layout-nav-item is-active">常用站点</span>
|
||||
<span className="layout-nav-item">前端工具</span>
|
||||
{!compact && <span className="layout-nav-item">设计资源</span>}
|
||||
{!compact && <span className="layout-nav-item">推荐电影</span>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DemoAside() {
|
||||
return (
|
||||
<div className="layout-aside">
|
||||
<Text size="sm" strong>
|
||||
目录
|
||||
</Text>
|
||||
<span className="layout-aside-item is-active">一、内容库结构</span>
|
||||
<span className="layout-aside-item">二、令牌与主题</span>
|
||||
<span className="layout-aside-item">三、暗色模式</span>
|
||||
<Text size="xs" tone="muted">
|
||||
共 3 节
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DemoArticle() {
|
||||
return (
|
||||
<div className="layout-content">
|
||||
<Title level={3}>内容库结构</Title>
|
||||
<Text size="sm" tone="sub">
|
||||
分区 → 分组 → 条目三层;侧栏由内容树派生,首页卡片由 section_block 决定。
|
||||
</Text>
|
||||
<Space gap="sm">
|
||||
<Tag>设计系统</Tag>
|
||||
<Tag tone="success">已发布</Tag>
|
||||
</Space>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DemoCards() {
|
||||
return (
|
||||
<div className="layout-content">
|
||||
<Title level={3}>常用站点</Title>
|
||||
<Text size="sm" tone="sub">
|
||||
内容区放任何组件:卡片、列表、表格。Layout 只负责它坐在哪里。
|
||||
</Text>
|
||||
<Grid columns={2} gap="sm">
|
||||
<Card padding="md">
|
||||
<Text strong>GitHub</Text>
|
||||
</Card>
|
||||
<Card padding="md">
|
||||
<Text strong>Figma</Text>
|
||||
</Card>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const RECIPES = [
|
||||
{ value: 'top', label: '顶栏 + 内容' },
|
||||
{ value: 'sider', label: '左栏 + 内容' },
|
||||
{ value: 'aside', label: '内容 + 右栏' },
|
||||
{ value: 'three', label: '三栏' },
|
||||
{ value: 'narrow', label: '窄内容' },
|
||||
] as const
|
||||
|
||||
type RecipeValue = (typeof RECIPES)[number]['value']
|
||||
|
||||
const CODE: Record<RecipeValue, string> = {
|
||||
top: `<Layout header={<Header />} footer={<Footer />}>
|
||||
<Grid columns={2}>…</Grid>
|
||||
</Layout>`,
|
||||
sider: `<Layout header={<Header />} sider={<Nav />} siderWidth={180}>
|
||||
<Title>常用站点</Title>
|
||||
</Layout>`,
|
||||
aside: `<Layout header={<Header />} aside={<Toc />} maxWidth={960}>
|
||||
<Article />
|
||||
</Layout>`,
|
||||
three: `<Layout
|
||||
header={<Header />}
|
||||
sider={<Nav />}
|
||||
aside={<Toc />}
|
||||
siderWidth={160}
|
||||
asideWidth={200}
|
||||
>
|
||||
<Content />
|
||||
</Layout>`,
|
||||
narrow: `<Layout header={<Header />} maxWidth={640}>
|
||||
<Settings />
|
||||
</Layout>`,
|
||||
}
|
||||
|
||||
export function PageLayout() {
|
||||
const [recipe, setRecipe] = useState<RecipeValue>('three')
|
||||
|
||||
return (
|
||||
<Section
|
||||
id="layout"
|
||||
title="页面骨架"
|
||||
api="Layout"
|
||||
desc="页头 / 侧栏 / 内容 / 右栏 / 页脚的外壳:只做结构(区域、最大宽度、吸顶页头),不写业务内容。下面五个是实际项目里最常用的配方。响应式:≤1200px 右栏堆到内容下方,≤1100px 侧栏堆到内容上方,内容都不隐藏。"
|
||||
>
|
||||
<Demo
|
||||
title="五种常用布局"
|
||||
desc="切换配方看结构差异:顶栏+内容(首页)、左栏+内容(管理页)、内容+右栏(文章)、三栏(工作台)、窄内容(设置/阅读)。"
|
||||
layout="column"
|
||||
code={CODE[recipe]}
|
||||
>
|
||||
<Segmented options={[...RECIPES]} value={recipe} onChange={(value) => setRecipe(value as RecipeValue)} />
|
||||
|
||||
<div className="layout-frame" style={{ height: 440 }}>
|
||||
<Layout
|
||||
header={<DemoHeader />}
|
||||
sider={recipe === 'sider' || recipe === 'three' ? <DemoSider compact /> : undefined}
|
||||
aside={recipe === 'aside' || recipe === 'three' ? <DemoAside /> : undefined}
|
||||
footer={<span>© 2026 Stash</span>}
|
||||
siderWidth={recipe === 'three' ? 150 : 180}
|
||||
asideWidth={190}
|
||||
maxWidth={recipe === 'narrow' ? 560 : recipe === 'aside' ? 900 : 1280}
|
||||
style={{ minHeight: 420 }}
|
||||
>
|
||||
{recipe === 'aside' || recipe === 'three' ? <DemoArticle /> : null}
|
||||
{recipe === 'top' || recipe === 'sider' ? <DemoCards /> : null}
|
||||
{recipe === 'narrow' ? (
|
||||
<div className="layout-content">
|
||||
<Title level={4}>设置</Title>
|
||||
<Text size="sm" tone="sub">
|
||||
单栏 + 560 最大宽度,阅读型页面常用这个组合。
|
||||
</Text>
|
||||
<Card padding="md">
|
||||
<Text size="sm">自动同步</Text>
|
||||
</Card>
|
||||
<Card padding="md">
|
||||
<Text size="sm">每周摘要提醒</Text>
|
||||
</Card>
|
||||
</div>
|
||||
) : null}
|
||||
</Layout>
|
||||
</div>
|
||||
</Demo>
|
||||
|
||||
<Demo
|
||||
title="页头可关闭吸顶"
|
||||
desc="长表单页可以把 stickyHeader 关掉,避免页头一直挡住内容。"
|
||||
layout="column"
|
||||
code={`<Layout header={<Header />} stickyHeader={false} maxWidth={720}>
|
||||
<Form />
|
||||
</Layout>`}
|
||||
>
|
||||
<div className="layout-frame" style={{ height: 300 }}>
|
||||
<Layout header={<DemoHeader />} maxWidth={720} stickyHeader={false} style={{ minHeight: 280 }}>
|
||||
<div className="layout-content">
|
||||
<Title level={4}>新建条目</Title>
|
||||
<Text size="sm" tone="sub">
|
||||
stickyHeader=false:页头随内容滚走,适合长表单 / 长文阅读。
|
||||
</Text>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
</Demo>
|
||||
|
||||
<Demo
|
||||
title="整屏 + 内部滚动"
|
||||
desc="height='screen' 锁视口、页头与侧栏固定;scrollable 让内容区自己滚,hideScrollbar 隐藏滚动条(应用型界面)。"
|
||||
layout="column"
|
||||
code={`<Layout height="screen" scrollable hideScrollbar contentId="demo-scroll"
|
||||
header={<Header />} sider={<Nav />} style={{ height: 320 }}>
|
||||
<LongContent />
|
||||
</Layout>`}
|
||||
>
|
||||
<div className="layout-frame" style={{ height: 320, overflow: 'hidden' }}>
|
||||
<Layout
|
||||
header={<DemoHeader />}
|
||||
sider={<DemoSider compact />}
|
||||
headerHeight={56}
|
||||
height="screen"
|
||||
scrollable
|
||||
hideScrollbar
|
||||
contentId="demo-scroll"
|
||||
style={{ height: 320 }}
|
||||
>
|
||||
<div className="layout-content">
|
||||
{Array.from({ length: 12 }, (_, index) => (
|
||||
<Card padding="md" key={index}>
|
||||
<Text size="sm">第 {index + 1} 行:只有内容区滚动,页头和侧栏保持不动。</Text>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
</Demo>
|
||||
|
||||
<PropsTable
|
||||
rows={[
|
||||
{ name: 'header', type: 'ReactNode', desc: '页头,默认吸顶' },
|
||||
{ name: 'sider', type: 'ReactNode', desc: '左侧栏,固定宽度' },
|
||||
{ name: 'aside', type: 'ReactNode', desc: '右侧栏,固定宽度(目录 / 聚合 / 广告位)' },
|
||||
{ name: 'footer', type: 'ReactNode', desc: '页脚' },
|
||||
{ name: 'children', type: 'ReactNode', required: true, desc: '内容区' },
|
||||
{ name: 'maxWidth', type: "number | 'full'", defaultValue: '1280', desc: '内容最大宽度' },
|
||||
{ name: 'siderWidth', type: 'number', defaultValue: '260', desc: '左侧栏宽度' },
|
||||
{ name: 'asideWidth', type: 'number', defaultValue: '300', desc: '右侧栏宽度' },
|
||||
{ name: 'stickyHeader', type: 'boolean', defaultValue: 'true', desc: '页头是否吸顶' },
|
||||
{ name: 'contained', type: 'boolean', defaultValue: 'false', desc: '页身套居中容器(页头仍通栏)' },
|
||||
{ name: 'gap', type: 'number', defaultValue: '0', desc: '侧栏 / 内容 / 右栏间距(px)' },
|
||||
{ name: 'contentPadding', type: "'none' | 'sm' | 'md' | 'lg'", defaultValue: "'md'", desc: '内容留白 0 / 12 / 20 / 24' },
|
||||
{ name: 'contentOffset', type: 'number', defaultValue: '0', desc: '内容区顶部偏移(给悬浮搜索留位)' },
|
||||
{ name: 'gutter', type: 'number', defaultValue: '0', desc: 'contained 容器的左右留白' },
|
||||
{ name: 'headerHeight', type: 'number', defaultValue: '60', desc: '页头最小高度' },
|
||||
{ name: 'headerVariant', type: "'blur' | 'solid'", defaultValue: "'blur'", desc: '半透明模糊 / 实底 + 边框' },
|
||||
{ name: 'siderBordered', type: 'boolean', defaultValue: 'true', desc: '侧栏右侧分隔线' },
|
||||
{ name: 'height', type: "'auto' | 'screen'", defaultValue: "'auto'", desc: 'screen 锁视口' },
|
||||
{ name: 'scrollable', type: 'boolean', defaultValue: 'false', desc: '内容区内部滚动' },
|
||||
{ name: 'hideScrollbar', type: 'boolean', defaultValue: 'false', desc: '内部滚动条隐藏' },
|
||||
{ name: 'contentId', type: 'string', desc: '内容区 id(ScrollTop 用)' },
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user