Skip to content

Commit 9c51ee7

Browse files
committed
feat(docs): add demo components and enhance button documentation with new examples
1 parent 5eabac4 commit 9c51ee7

File tree

22 files changed

+598
-0
lines changed

22 files changed

+598
-0
lines changed

docs/locales/en.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ layout:
1919
default: Default Layout
2020
home: Home Layout
2121
about: About Layout
22+
components:
23+
common:
24+
demo: Demos
25+
button:
26+
title: Button
27+
desc: Button component

docs/locales/zh-CN.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ layout:
1919
default: 默认布局
2020
home: 首页布局
2121
about: 关于布局
22+
components:
23+
common:
24+
demo: 演示
25+
button:
26+
title: 按钮
27+
desc: 按钮组件

docs/src/components/code-block.vue

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue';
3+
import { Icon } from '@iconify/vue';
4+
5+
interface Props {
6+
code: string;
7+
language?: string;
8+
filename?: string;
9+
collapsible?: boolean;
10+
}
11+
12+
const props = withDefaults(defineProps<Props>(), {
13+
language: 'vue',
14+
collapsible: true,
15+
filename: ''
16+
});
17+
18+
const expanded = ref(false);
19+
const copied = ref(false);
20+
21+
const copyCode = async () => {
22+
try {
23+
await navigator.clipboard.writeText(props.code);
24+
copied.value = true;
25+
setTimeout(() => {
26+
copied.value = false;
27+
}, 2000);
28+
} catch (err) {
29+
console.error('Failed to copy code:', err);
30+
}
31+
};
32+
33+
const toggleExpanded = () => {
34+
expanded.value = !expanded.value;
35+
};
36+
</script>
37+
38+
<template>
39+
<div class="code-block-wrapper">
40+
<div v-if="filename" class="code-block-header">
41+
<span class="filename">{{ filename }}</span>
42+
</div>
43+
44+
<div class="code-block-toolbar">
45+
<button v-if="collapsible" class="toolbar-btn" @click="toggleExpanded">
46+
<Icon :icon="expanded ? 'lucide:chevron-up' : 'lucide:chevron-down'" class="w-4 h-4" />
47+
<span>{{ expanded ? '收起' : '展开' }}</span>
48+
</button>
49+
50+
<button class="toolbar-btn" @click="copyCode">
51+
<Icon :icon="copied ? 'lucide:check' : 'lucide:copy'" class="w-4 h-4" />
52+
<span>{{ copied ? '已复制' : '复制' }}</span>
53+
</button>
54+
</div>
55+
56+
<div v-show="expanded || !collapsible" class="code-content">
57+
<pre><code :class="`language-${language}`">{{ code }}</code></pre>
58+
</div>
59+
</div>
60+
</template>
61+
62+
<style scoped>
63+
.code-block-wrapper {
64+
position: relative;
65+
border-radius: 0.5rem;
66+
overflow: hidden;
67+
border: 1px solid rgb(15 23 42 / 0.1);
68+
background: rgb(15 23 42 / 0.05);
69+
}
70+
71+
.code-block-header {
72+
padding: 0.75rem 1rem;
73+
background: rgb(15 23 42 / 0.08);
74+
border-bottom: 1px solid rgb(15 23 42 / 0.1);
75+
}
76+
77+
.filename {
78+
font-size: 0.875rem;
79+
font-weight: 500;
80+
color: rgb(15 23 42 / 0.7);
81+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
82+
}
83+
84+
.code-block-toolbar {
85+
display: flex;
86+
gap: 0.5rem;
87+
padding: 0.5rem;
88+
justify-content: flex-end;
89+
background: rgb(15 23 42 / 0.03);
90+
border-bottom: 1px solid rgb(15 23 42 / 0.05);
91+
}
92+
93+
.toolbar-btn {
94+
display: flex;
95+
align-items: center;
96+
gap: 0.375rem;
97+
padding: 0.375rem 0.75rem;
98+
font-size: 0.875rem;
99+
color: rgb(15 23 42 / 0.7);
100+
background: white;
101+
border: 1px solid rgb(15 23 42 / 0.1);
102+
border-radius: 0.375rem;
103+
cursor: pointer;
104+
transition: all 0.2s;
105+
}
106+
107+
.toolbar-btn:hover {
108+
color: rgb(var(--primary));
109+
border-color: rgb(var(--primary) / 0.3);
110+
background: rgb(var(--primary) / 0.05);
111+
}
112+
113+
.dark .toolbar-btn {
114+
color: rgb(248 250 252 / 0.7);
115+
background: rgb(15 23 42 / 0.5);
116+
border-color: rgb(248 250 252 / 0.1);
117+
}
118+
119+
.dark .toolbar-btn:hover {
120+
color: rgb(var(--primary));
121+
border-color: rgb(var(--primary) / 0.3);
122+
background: rgb(var(--primary) / 0.1);
123+
}
124+
125+
.toolbar-btn svg {
126+
flex-shrink: 0;
127+
}
128+
129+
.code-content {
130+
position: relative;
131+
}
132+
133+
pre {
134+
margin: 0;
135+
padding: 1rem;
136+
overflow-x: auto;
137+
background: transparent !important;
138+
}
139+
140+
code {
141+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
142+
font-size: 0.875rem;
143+
line-height: 1.5;
144+
}
145+
</style>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script setup lang="ts">
2+
import { computed, defineAsyncComponent } from 'vue';
3+
import type { Component } from 'vue';
4+
5+
interface Props {
6+
/** 组件 docs 根目录,例如:/src/docs/button/*.vue */
7+
docsRoot: string;
8+
/**
9+
* demo 文件名列表,如:['basic.vue', 'sizes.vue']
10+
*/
11+
files: string[];
12+
}
13+
14+
const props = defineProps<Props>();
15+
16+
// 收集所有 docs 下的示例 .vue 文件,供动态解析使用
17+
const demoModules = import.meta.glob<{ default: Component }>('/src/docs/**/*.vue');
18+
19+
const components = computed(() => {
20+
return props.files.map(file => {
21+
const candidates = `${props.docsRoot}/${file}`;
22+
const loader = demoModules[candidates];
23+
return loader ? defineAsyncComponent(loader) : null;
24+
});
25+
});
26+
</script>
27+
28+
<template>
29+
<div class="space-y-4">
30+
<template v-for="(file, index) in files" :key="index">
31+
<component :is="components[index]" v-if="components[index]" />
32+
<div v-else class="text-red-6 text-sm">示例未找到: {{ file }}</div>
33+
</template>
34+
</div>
35+
</template>

docs/src/docs/button/color.vue

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script setup lang="ts">
2+
import { SButton } from '@soybeanjs/ui';
3+
import type { ThemeColor } from '@soybeanjs/ui';
4+
5+
const colors: ThemeColor[] = ['primary', 'secondary', 'destructive', 'success', 'warning', 'info', 'accent'];
6+
</script>
7+
8+
<template>
9+
<div class="space-y-4">
10+
<div>
11+
<h3>颜色主题</h3>
12+
<div class="flex gap-2 flex-wrap">
13+
<SButton v-for="color in colors" :key="color" :color="color">{{ color }}</SButton>
14+
</div>
15+
</div>
16+
</div>
17+
</template>

docs/src/docs/button/disabled.vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script setup lang="ts">
2+
import { SButton } from '@soybeanjs/ui';
3+
</script>
4+
5+
<template>
6+
<div class="space-y-4">
7+
<h3>禁用状态</h3>
8+
<div class="flex flex-wrap gap-3">
9+
<SButton disabled>disabled</SButton>
10+
</div>
11+
</div>
12+
</template>

docs/src/docs/button/group.vue

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script setup lang="ts">
2+
import { SButton, SButtonGroup } from '@soybeanjs/ui';
3+
</script>
4+
5+
<template>
6+
<div class="space-y-4">
7+
<div>
8+
<h3>按钮组</h3>
9+
<SButtonGroup variant="pure" color="accent" class="whitespace-nowrap mb-4">
10+
<SButton v-for="i in 5" :key="i">Button {{ i }}</SButton>
11+
</SButtonGroup>
12+
<SButtonGroup orientation="vertical" class="w-30" variant="outline" color="warning">
13+
<SButton v-for="i in 10" :key="i">Button {{ i }}</SButton>
14+
</SButtonGroup>
15+
</div>
16+
</div>
17+
</template>

docs/src/docs/button/icon.vue

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script setup lang="ts">
2+
import { SButtonIcon } from '@soybeanjs/ui';
3+
</script>
4+
5+
<template>
6+
<div class="space-y-4">
7+
<h3>图标按钮</h3>
8+
<div class="flex flex-wrap gap-3">
9+
<SButtonIcon icon="lucide:skip-back" />
10+
<SButtonIcon icon="lucide:skip-forward" />
11+
<SButtonIcon icon="lucide:pause" />
12+
</div>
13+
</div>
14+
</template>

docs/src/docs/button/index.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# <i18n-t keypath="components.button.title" scope="global" />
2+
3+
<i18n-t keypath="components.button.desc" scope="global" />
4+
5+
## <i18n-t keypath="components.common.demo" scope="global" />
6+
7+
```demo
8+
color.vue
9+
variant.vue
10+
size.vue
11+
shape.vue
12+
shadow.vue
13+
slot.vue
14+
disabled.vue
15+
loading.vue
16+
icon.vue
17+
link.vue
18+
group.vue
19+
```
20+
21+
## API
22+
23+
### Button Props
24+
25+
| 名称 | 类型 | 默认值 | 说明 |
26+
| -------------- | ---------------------------------------------------------------------------------------------------- | --------- | ------------------ |
27+
| **color** | "primary" \| "secondary" \| "destructive" \| "success" \| "warning" \| "info" \| "muted" \| "accent" | "primary" | 按钮颜色 |
28+
| **size** | "xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" | "md" | 按钮尺寸 |
29+
| **variant** | "solid" \| "outline" \| "dashed" \| "soft" \| "ghost" \| "link" | "solid" | 样式变体 |
30+
| **shape** | "auto" \| "rounded" \| "square" \| "circle" | "auto" | 按钮形状 |
31+
| **shadow** | "none" \| "sm" \| "md" \| "lg" | "none" | 阴影效果 |
32+
| **fitContent** | boolean | false | 根据内容自适应尺寸 |
33+
| **disabled** | boolean | false | 是否禁用 |
34+
35+
### Button Emits
36+
37+
| 事件名 | 参数 | 说明 |
38+
| ------ | ----------------------- | -------------- |
39+
| click | **(event: MouseEvent)** | 点击按钮时触发 |
40+
41+
### Button Slots
42+
43+
| 插槽名 | 参数 | 说明 |
44+
| ------- | ---- | -------- |
45+
| default | - | 按钮内容 |
46+
47+
## 组件类型
48+
49+
Button 组件族包含以下组件:
50+
51+
- **SButton** - 基础按钮组件
52+
- **SButtonLink** - 链接按钮,支持路由导航
53+
- **SButtonIcon** - 图标按钮,紧凑设计
54+
- **SButtonLoading** - 加载状态按钮
55+
- **SButtonGroup** - 按钮组组件
56+
57+
## 主要特性
58+
59+
- 🎨 6 种样式变体:solid、outline、dashed、soft、ghost、link
60+
- 🌈 8 种颜色主题:primary、secondary、destructive、success、warning、info、muted、accent
61+
- 📏 6 种尺寸:xs、sm、md、lg、xl、2xl
62+
- 🔲 4 种形状:auto、rounded、square、circle
63+
- ⚡ 加载状态支持
64+
- 🌐 链接功能支持(SButtonLink)
65+
- ♿ 完全可访问性支持
66+
- 🎯 TypeScript 类型安全
67+
68+
## 基础用法
69+
70+
```vue
71+
<script setup lang="ts">
72+
import { SButton } from '@soybeanjs/ui';
73+
</script>
74+
75+
<template>
76+
<SButton>Default Button</SButton>
77+
</template>
78+
```
79+
80+
## 相关资源
81+
82+
- [Playground 演示](/playground/examples/button)
83+
- [快速开始](/guide/quick-start)

docs/src/docs/button/link.vue

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script setup lang="ts">
2+
import { SButtonLink } from '@soybeanjs/ui';
3+
</script>
4+
5+
<template>
6+
<div class="space-y-4">
7+
<div>
8+
<h3>链接按钮</h3>
9+
<div class="flex gap-2 flex-wrap">
10+
<SButtonLink to="/home">内部链接</SButtonLink>
11+
<SButtonLink href="https://soybeanjs.cn" rel="noopener noreferrer" target="_blank">外部链接</SButtonLink>
12+
<SButtonLink to="/home" variant="outline">Outline</SButtonLink>
13+
<SButtonLink to="/home" variant="ghost">Ghost</SButtonLink>
14+
</div>
15+
</div>
16+
</div>
17+
</template>

0 commit comments

Comments
 (0)