import React, { useState } from 'react'; import { Button, Input, Switch, Modal, Card, Collapse, Divider, Typewriter, Tabs } from '../src'; import { labelStyle, sectionStyle, sectionTitleStyle, tagStyle, demoBodyStyle, ApiTable, ApiRow, CodeBlock, demoDashedBoxStyle, } from './tools'; import TimeDemo from './components/Time'; import PhoneDemo from './components/Phone'; import FooterDemo from './components/Footer'; import IconDemo from './components/Icon/IconDemo'; import TabsDemo from './components/Tabs'; import CheckboxDemo from './components/Checkbox'; import CodeBlockDemo from './components/CodeBlock'; import LoadingDemo from './components/Loading/LoadingDemo'; import TableDemo from './components/Table/TableDemo'; import type { TabItem } from '../src'; // ============================================ // Styles // ============================================ const S = { pageDesc: { fontSize: 14, color: '#794f27', marginBottom: 20, } as React.CSSProperties, row: { display: 'flex', gap: 16, flexWrap: 'wrap', alignItems: 'flex-start', } as React.CSSProperties, col: { display: 'flex', flexDirection: 'column', gap: 12, } as React.CSSProperties, demoBox: { padding: 16, borderRadius: 18, fontWeight: 500, marginBottom: 20, } as React.CSSProperties, }; // ============================================ // API data // ============================================ const BUTTON_API: ApiRow[] = [ { prop: 'type', desc: '按钮类型', type: `'primary' | 'default' | 'dashed' | 'text' | 'link'`, defaultVal: "'default'", }, { prop: 'size', desc: '按钮尺寸', type: `'small' | 'middle' | 'large'`, defaultVal: "'middle'", }, { prop: 'danger', desc: '是否危险按钮', type: 'boolean', defaultVal: 'false', }, { prop: 'ghost', desc: '是否幽灵按钮(透明背景)', type: 'boolean', defaultVal: 'false', }, { prop: 'block', desc: '是否块级按钮', type: 'boolean', defaultVal: 'false', }, { prop: 'loading', desc: '加载状态', type: 'boolean', defaultVal: 'false' }, { prop: 'disabled', desc: '禁用状态', type: 'boolean', defaultVal: 'false', }, { prop: 'icon', desc: '图标', type: 'ReactNode', defaultVal: '-' }, { prop: 'htmlType', desc: '原生 button type', type: `'submit' | 'reset' | 'button'`, defaultVal: "'button'", }, { prop: 'children', desc: '按钮内容', type: 'ReactNode', defaultVal: '-' }, { prop: 'className', desc: '自定义类名', type: 'string', defaultVal: '-' }, { prop: '...', desc: '继承 React.ButtonHTMLAttributes', type: 'HTMLButtonElement', defaultVal: '-', }, ]; const INPUT_API: ApiRow[] = [ { prop: 'size', desc: '输入框尺寸', type: `'small' | 'middle' | 'large'`, defaultVal: "'middle'", }, { prop: 'prefix', desc: '前缀图标', type: 'ReactNode', defaultVal: '-' }, { prop: 'suffix', desc: '后缀图标', type: 'ReactNode', defaultVal: '-' }, { prop: 'allowClear', desc: '允许清除', type: 'boolean', defaultVal: 'false', }, { prop: 'status', desc: '校验状态', type: `'error' | 'warning'`, defaultVal: '-', }, { prop: 'shadow', desc: '是否显示阴影', type: 'boolean', defaultVal: 'false', }, { prop: 'onChange', desc: '值变化回调', type: 'ChangeEventHandler', defaultVal: '-', }, { prop: 'onClear', desc: '清除回调', type: '() => void', defaultVal: '-' }, { prop: '...', desc: '继承 React.InputHTMLAttributes', type: 'HTMLInputElement', defaultVal: '-', }, ]; const SWITCH_API: ApiRow[] = [ { prop: 'checked', desc: '是否选中(受控)', type: 'boolean', defaultVal: '-', }, { prop: 'defaultChecked', desc: '默认是否选中', type: 'boolean', defaultVal: 'false', }, { prop: 'size', desc: '尺寸', type: `'small' | 'default'`, defaultVal: "'default'", }, { prop: 'disabled', desc: '禁用', type: 'boolean', defaultVal: 'false' }, { prop: 'loading', desc: '加载状态', type: 'boolean', defaultVal: 'false' }, { prop: 'checkedChildren', desc: '选中时文案', type: 'ReactNode', defaultVal: '-', }, { prop: 'unCheckedChildren', desc: '未选中时文案', type: 'ReactNode', defaultVal: '-', }, { prop: 'onChange', desc: '变化回调', type: '(checked: boolean) => void', defaultVal: '-', }, { prop: 'className', desc: '自定义类名', type: 'string', defaultVal: '-' }, ]; const MODAL_API: ApiRow[] = [ { prop: 'open', desc: '是否可见', type: 'boolean', defaultVal: '-', required: true, }, { prop: 'title', desc: '标题', type: 'ReactNode', defaultVal: '-' }, { prop: 'width', desc: '宽度', type: 'number | string', defaultVal: '520' }, { prop: 'maskClosable', desc: '点击遮罩关闭', type: 'boolean', defaultVal: 'true', }, { prop: 'footer', desc: '底部按钮区域,传 null 则不显示', type: 'ReactNode | null', defaultVal: '默认按钮', }, { prop: 'onClose', desc: '关闭回调', type: '() => void', defaultVal: '-' }, { prop: 'onOk', desc: '确认回调', type: '() => void', defaultVal: '-' }, { prop: 'children', desc: '自定义内容', type: 'ReactNode', defaultVal: '-', }, { prop: 'className', desc: '自定义类名', type: 'string', defaultVal: '-', }, { prop: 'typeSpeed', desc: '打字机每字间隔 (ms)', type: 'number', defaultVal: '80', }, { prop: 'typewriter', desc: '是否启用打字机效果', type: 'boolean', defaultVal: 'true', }, ]; const CARD_API: ApiRow[] = [ { prop: 'type', desc: '卡片类型', type: `'default' | 'title' | 'dashed'`, defaultVal: "'default'", }, { prop: 'color', desc: '背景颜色类型', type: `'default' | 'app-pink' | 'purple' | 'app-blue' | 'app-yellow' | 'app-orange' | 'app-teal' | 'app-green' | 'app-red' | 'lime-green' | 'yellow-green' | 'brown' | 'warm-peach-pink'`, defaultVal: "'default'", }, { prop: 'children', desc: '自定义内容', type: 'ReactNode', defaultVal: '-', }, { prop: '...', desc: '继承 React.HTMLAttributes', type: 'HTMLDivElement', defaultVal: '-', }, ]; const COLLAPSE_API: ApiRow[] = [ { prop: 'question', desc: '问题标题', type: 'ReactNode', defaultVal: '-', required: true, }, { prop: 'answer', desc: '答案内容', type: 'ReactNode', defaultVal: '-', required: true, }, { prop: 'defaultExpanded', desc: '是否默认展开', type: 'boolean', defaultVal: 'false', }, { prop: 'disabled', desc: '是否禁用', type: 'boolean', defaultVal: 'false', }, { prop: 'className', desc: '自定义类名', type: 'string', defaultVal: '-' }, { prop: 'style', desc: '自定义样式', type: 'CSSProperties', defaultVal: '-', }, ]; const CURSOR_API: ApiRow[] = [ { prop: 'children', desc: '子元素', type: 'ReactNode', defaultVal: '-' }, { prop: 'className', desc: '自定义类名', type: 'string', defaultVal: '-' }, { prop: 'style', desc: '自定义样式', type: 'CSSProperties', defaultVal: '-', }, ]; const DIVIDER_API: ApiRow[] = [ { prop: 'type', desc: '分隔线类型', type: `'line-brown' | 'line-teal' | 'line-white' | 'line-yellow' | 'wave-yellow'`, defaultVal: "'line-brown'", }, { prop: 'className', desc: '自定义类名', type: 'string', defaultVal: '-' }, { prop: 'style', desc: '自定义样式', type: 'CSSProperties', defaultVal: '-', }, ]; // ============================================ // Demo sections // ============================================ const ButtonDemo: React.FC = () => (
Button 6 types
type 按钮类型
danger / ghost / loading / disabled 状态
size 尺寸
icon 图标按钮
block 块级按钮
danger 组合
{ return (
{/* Primary */} {/* Default */} {/* Dashed */} {/* Text */} {/* Link */} {/* Danger */} {/* Ghost */} {/* Loading */} {/* Large */} {/* Icon */} {/* Block */}
); }; export default App;`} />
); const InputDemo: React.FC = () => { const [inputValue, setInputValue] = useState(''); return (
Input 3 sizes
shadow 阴影控制
基础用法
setInputValue(e.target.value)} onClear={() => setInputValue('')} />
size 尺寸
status 校验状态
disabled 禁用
{ const [val, setVal] = useState(''); return (
{/* 基础输入框 */} {/* 带清除按钮 */} setVal(e.target.value)} /> {/* 前后缀 */} {/* 小尺寸 */} {/* 大尺寸 */} {/* 错误状态 */} {/* 警告状态 */} {/* 有阴影 */}
); }; export default App;`} />
); }; const SwitchDemo: React.FC = () => { const [switchChecked, setSwitchChecked] = useState(false); return (
Switch 2 sizes
基础用法
{switchChecked ? 'ON' : 'OFF'}
checkedChildren / unCheckedChildren 自定义文案
size 尺寸
disabled / loading 状态
{ const [checked, setChecked] = useState(false); return (
{/* 受控模式 */} {/* 自定义文案 */} {/* 小尺寸 */}
); }; export default App;`} />
); }; const CardDemo: React.FC = () => (
Card 3 types 13 colors
{/* ---- type ---- */}
type="default"

基础卡片

在Nintendo 3DS《Animal Island: New Leaf》和《Animal Island: Happy Home Designer》中製作的「我的設計」QR Code,以智慧型裝置讀取就能通過狸端機入口站下載至《集合啦!動物森友會》。

type="title"

Title标题卡片

欢迎来到无人岛!在Nintendo 3DS《Animal Island: New Leaf》和《Animal Island: Happy Home Designer》中製作的「我的設計」QR Code,以智慧型裝置讀取就能通過狸端機入口站下載至《集合啦!動物森友會》。

type="dashed"

虚线边框卡片

欢迎来到无人岛!虚线边框适合用于轻量提示或次要信息展示。

{/* ---- color variants ---- */}
color — NookPhone 颜色
{( [ ['default', 'Default', '默认奶油色'], ['app-pink', 'App Pink', '应用粉'], ['purple', 'Purple', '紫色'], ['app-blue', 'App Blue', '应用蓝'], ['app-yellow', 'App Yellow', '应用黄'], ['app-orange', 'App Orange', '应用橙'], ['app-teal', 'App Teal', '应用青'], ['app-green', 'App Green', '应用绿'], ['app-red', 'App Red', '应用红'], ['lime-green', 'Lime Green', '青柠绿'], ['yellow-green', 'Yellow-Green', '黄绿色'], ['brown', 'Brown', '棕色'], ['warm-peach-pink', 'Warm Peach Pink', '暖桃粉'], ] as const ).map(([color, en, cn]) => (
{en}
{cn}
))}
{/* ---- color + title ---- */}
color + type="title"
蓝色标题卡片
type="title" + color="app-blue"
绿色标题卡片
type="title" + color="app-green"
紫色标题卡片
type="title" + color="purple"
{ return (
{/* 基础卡片 */} 基础卡片 {/* 标题卡片 */} 标题卡片 {/* 颜色变体 */} 蓝色卡片 暖桃粉卡片 {/* 颜色 + 标题 组合 */} 紫色标题卡片
); }; export default App;`} />
); const CollapseDemo: React.FC = () => (
Collapse FAQ
基础用法
1座島嶼最多可以容納8位居民(用戶)。

} /> 同住1個島的居民可以最多4人一起遊玩。透過通訊最多8人一起遊玩。

} />
defaultExpanded 默认展开
答案已经展示出来了!可以点击收起。

} defaultExpanded />
disabled 禁用状态
这段文字不应该被看到。

} disabled />
{ return (
{/* 基础用法 */} 回答内容

} /> {/* 默认展开 */} 答案

} defaultExpanded /> {/* 禁用状态 */} 答案

} disabled />
); }; export default App;`} />
); const CursorDemo: React.FC = () => (
Cursor 光标

Cursor 组件通过 CSS cursor 属性将子元素的鼠标光标替换为自定义手指图标,当前 Demo 全局已应用。

{ return (
{/* 光标组件 */}
鼠标移入此区域将显示自定义光标
); }; export default App;`} />
); const ModalDemo: React.FC = () => { const [modalOpen, setModalOpen] = useState(false); const [titleModalOpen, setTitleModalOpen] = useState(false); const [customFooterOpen, setCustomFooterOpen] = useState(false); const [noTypewriterOpen, setNoTypewriterOpen] = useState(false); return (
Modal 弹窗
基础弹窗
关闭打字机效果
setModalOpen(false)} onOk={() => setModalOpen(false)} >
钓到石头了! 竟然连这种都能钓起来...
setTitleModalOpen(false)} onOk={() => setTitleModalOpen(false)} > 是否愿意将这条鱼捐赠给博物馆呢?傅达会好好照顾它的!这可是博物馆的新展品哦~ setCustomFooterOpen(false)} footer={ <> } > 确定要让这位居民搬走吗?这个操作不可撤销。 setNoTypewriterOpen(false)} onOk={() => setNoTypewriterOpen(false)} typewriter={false} > 明天天气晴朗,气温 20-28°C,适合外出活动! { const [open, setOpen] = useState(false); return (
setOpen(false)} onOk={() => setOpen(false)}> Modal 内容 {/* 带标题 */} setOpen(false)}> 内容 {/* 自定义 Footer */} 自定义按钮}> 内容 {/* 无 Footer */} 无底部按钮 {/* 关闭打字机效果 */} 直接显示全部内容
); }; export default App;`} />
); }; const TypewriterDemo: React.FC = () => { const [replayKey, setReplayKey] = useState(0); return (
Typewriter 打字机
基础用法
你好,欢迎来到动物岛!今天的天气真不错呢~
保留多行与富内容 (速度 40ms)
第一行:钓到石头了!
第二行:竟然连这种都能钓起来...
第三行:继续加油吧!
{ const [key, setKey] = useState(0); return ( <> 你好,欢迎来到动物岛! {/* 支持多行与内联样式 */}
第一行
第二行
); }; export default App;`} />
); }; const TYPEWRITER_API: ApiRow[] = [ { prop: 'children', desc: '需要逐字显示的内容,支持 ReactNode', type: 'ReactNode', defaultVal: '-', }, { prop: 'speed', desc: '每字间隔 (ms)', type: 'number', defaultVal: '90', }, { prop: 'trigger', desc: '值变化时重新播放', type: 'unknown', defaultVal: '-', }, { prop: 'autoPlay', desc: '是否自动从头开始播放', type: 'boolean', defaultVal: 'true', }, { prop: 'onDone', desc: '播放完成回调', type: '() => void', defaultVal: '-', }, ]; const DividerDemo: React.FC = () => (
Divider 5 types
line-brown
line-teal
line-white
line-yellow
wave-yellow
{ return (
{/* line-brown */} {/* line-teal */} {/* line-white */} {/* line-yellow */} {/* wave-yellow */}
); }; export default App;`} />
); // ============================================ // SelectDemo // ============================================ import { Select } from '../src'; import type { SelectOption } from '../src'; const SELECT_API = [ { prop: 'options', desc: '选项列表', type: 'SelectOption[]', defaultVal: '-', required: true }, { prop: 'value', desc: '当前选中值', type: 'string', defaultVal: '-', required: true }, { prop: 'onChange', desc: '选中变化回调', type: '(key: string) => void', defaultVal: '-', required: true }, { prop: 'placeholder', desc: '占位文本', type: 'string', defaultVal: '请选择' }, { prop: 'disabled', desc: '禁用状态', type: 'boolean', defaultVal: 'false' }, ]; const SelectDemo: React.FC = () => { const [value1, setValue1] = useState('fish1'); const [value2, setValue2] = useState(''); const [value3, setValue3] = useState('flower2'); const [value4, setValue4] = useState(''); const fishOptions: SelectOption[] = [ { key: 'fish1', label: '鲈鱼' }, { key: 'fish2', label: '鲷鱼' }, { key: 'fish3', label: '草鱼' }, { key: 'fish4', label: '龙睛鱼' }, { key: 'fish5', label: '神仙鱼' }, ]; const flowerOptions: SelectOption[] = [ { key: 'flower1', label: '樱花' }, { key: 'flower2', label: '玫瑰' }, { key: 'flower3', label: '向日葵' }, { key: 'flower4', label: '薰衣草' }, { key: 'flower5', label: '郁金香' }, ]; const fruitOptions: SelectOption[] = [ { key: 'fruit1', label: '草莓' }, { key: 'fruit2', label: '蓝莓' }, { key: 'fruit3', label: '桃子' }, { key: 'fruit4', label: '樱桃' }, { key: 'fruit5', label: '猕猴桃' }, ]; return (
Select 基础用法
默认状态
当前选中:{' '} {fishOptions.find((o) => o.key === value1)?.label}
{ const [value, setValue] = useState('option1'); return (
{/* 受控模式 */} {/* 禁用状态 */}