diff --git a/package.json b/package.json
index e5b7f77a7..ca0d9b6ae 100644
--- a/package.json
+++ b/package.json
@@ -82,6 +82,7 @@
"author": "tdesign",
"license": "MIT",
"dependencies": {
+ "@babel/plugin-proposal-decorators": "^7.23.2",
"@babel/plugin-syntax-jsx": "^7.22.5",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
@@ -150,7 +151,7 @@
"omi-twind": "^0.0.4",
"prismjs": "^1.24.0",
"sortablejs": "^1.15.0",
- "tdesign-icons-omi": "^0.0.10",
+ "tdesign-icons-omi": "^0.0.12",
"tinycolor2": "^1.6.0",
"twind": "^0.16.16",
"validator": "^13.9.0"
diff --git a/site/plugin-api-doc/demo.js b/site/plugin-api-doc/demo.js
new file mode 100644
index 000000000..625b30ec2
--- /dev/null
+++ b/site/plugin-api-doc/demo.js
@@ -0,0 +1,39 @@
+/* eslint-disable */
+import path from 'path'
+
+export default function renderDemo(md, container) {
+ console.log('renderDemo')
+ md.use(container, 'demo', {
+ validate(params) {
+ return params.trim().match(/^demo\s+([\\/.\w-]+)(\s+(.+?))?(\s+--dev)?$/)
+ },
+ render(tokens, idx) {
+ if (tokens[idx].nesting === 1) {
+ const match = tokens[idx].info.trim().match(/^demo\s+([\\/.\w-]+)(\s+(.+?))?(\s+--dev)?$/)
+ const [, demoPath, componentName = ''] = match
+ const demoPathOnlyLetters = demoPath.replace(/[^a-zA-Z\d]/g, '')
+ const demoName = path.basename(demoPath)
+ const demoDefName = `Demo${demoPathOnlyLetters}`
+ const demoCodeDefName = `Demo${demoPathOnlyLetters}Code`
+ const tpl = `
+
+
+
+
+
+ <${demoDefName} />
+
+
+ `
+
+ tokens.tttpl = tpl
+
+ return `
`
+ }
+ if (tokens.tttpl) {
+ return `${tokens.tttpl || ''}
`
+ }
+ return ''
+ },
+ })
+}
diff --git a/site/plugin-api-doc/index.js b/site/plugin-api-doc/index.js
new file mode 100644
index 000000000..e6ce225dc
--- /dev/null
+++ b/site/plugin-api-doc/index.js
@@ -0,0 +1,25 @@
+import vitePluginTdoc from 'vite-plugin-tdoc'
+import renderDemo from './demo'
+import transforms from './transforms'
+
+export default () =>
+ vitePluginTdoc({
+ transforms, // 解析markdown 数据
+ markdown: {
+ anchor: {
+ tabIndex: false,
+ config: (anchor) => ({
+ permalink: anchor.permalink.linkInsideHeader({ symbol: '' }),
+ }),
+ },
+ toc: {
+ listClass: 'tdesign-toc_list',
+ itemClass: 'tdesign-toc_list_item',
+ linkClass: 'tdesign-toc_list_item_a',
+ containerClass: 'tdesign-toc_container',
+ },
+ container(md, container) {
+ renderDemo(md, container)
+ },
+ },
+ })
diff --git a/site/plugin-api-doc/md-to-omi.js b/site/plugin-api-doc/md-to-omi.js
new file mode 100644
index 000000000..73732c9e2
--- /dev/null
+++ b/site/plugin-api-doc/md-to-omi.js
@@ -0,0 +1,87 @@
+/* eslint-disable */
+import fs from 'fs'
+import path from 'path'
+import matter from 'gray-matter'
+import { getGitTimestamp } from '../../src/_common/docs/compile'
+import { transformSync } from '@babel/core'
+
+const DEFAULT_TABS = [
+ { tab: 'demo', name: '示例' },
+ { tab: 'api', name: 'API' },
+ { tab: 'design', name: '指南' },
+]
+
+const DEFAULT_EN_TABS = [
+ { tab: 'demo', name: 'DEMO' },
+ { tab: 'api', name: 'API' },
+ { tab: 'design', name: 'Guideline' },
+]
+
+export default async function mdToOmi(options) {
+ const mdSegment = await customRender(options)
+
+ const omiSource = `
+ import { h, tag, WeElement } from 'omi'
+
+ export default class ButtonMd extends WeElement {
+ render() {
+ return
+ }
+ }
+ `
+
+ const result = transformSync(omiSource, {
+ babelrc: false,
+ configFile: false,
+ sourceMaps: true,
+ generatorOpts: {
+ decoratorsBeforeExport: true,
+ },
+ plugins: [[require('@babel/plugin-transform-react-jsx'), { isTSX: true }]],
+ })
+
+ return { code: result.code, map: result.map }
+}
+
+// 解析 markdown 内容
+async function customRender({ source, file, md }) {
+ const { content, data } = matter(source)
+ const lastUpdated = (await getGitTimestamp(file)) || Math.round(fs.statSync(file).mtimeMs)
+ // console.log('data', data);
+ const isEn = file.endsWith('en-US.md')
+
+ // md top data
+ const pageData = {
+ spline: '',
+ toc: true,
+ title: '',
+ description: '',
+ isComponent: false,
+ tdDocHeader: true,
+ tdDocTabs: !isEn ? DEFAULT_TABS : DEFAULT_EN_TABS,
+ apiFlag: /#+\s*API/,
+ docClass: '',
+ lastUpdated,
+ designDocLastUpdated: lastUpdated,
+ ...data,
+ }
+
+ // md filename
+ const reg = file.match(/([\w-]+)\.?([\w-]+)?\.md/)
+ const componentName = reg && reg[1]
+
+ // split md
+ let [demoMd = '', apiMd = ''] = content.split(pageData.apiFlag)
+
+ const mdSegment = {
+ ...pageData,
+ componentName,
+ apiMd: '',
+ }
+
+ mdSegment.apiMd = md.render.call(md, `${pageData.toc ? '[toc]\n' : ''}${apiMd.replace(//g, '')}`).html
+
+ return mdSegment
+}
diff --git a/site/plugin-api-doc/transforms.js b/site/plugin-api-doc/transforms.js
new file mode 100644
index 000000000..ef8b9f743
--- /dev/null
+++ b/site/plugin-api-doc/transforms.js
@@ -0,0 +1,64 @@
+import path from 'path'
+import fs from 'fs'
+
+import mdToOmi from './md-to-omi'
+
+let demoImports = {}
+let demoCodesImports = {}
+
+export default {
+ before({ source, file }) {
+ const resourceDir = path.dirname(file)
+ const reg = file.match(/([\w-]+)\.?([\w-]+)?\.md/)
+ const fileName = reg && reg[0]
+ const componentName = reg && reg[1]
+ const localeName = reg && reg[2]
+
+ // 统一换成 common 公共文档内容
+ if (fileName && source.includes(':: BASE_DOC ::')) {
+ const localeDocPath = path.resolve(__dirname, `../../src/_common/docs/web/api/${fileName}`)
+ const defaultDocPath = path.resolve(
+ __dirname,
+ `../../src/_common/docs/web/api/${localeName ? `${componentName}.${localeName}` : componentName}.md`,
+ )
+ let baseDoc = ''
+
+ if (fs.existsSync(localeDocPath)) {
+ // 优先载入语言版本
+ baseDoc = fs.readFileSync(localeDocPath, 'utf-8')
+ } else if (fs.existsSync(defaultDocPath)) {
+ // 回退中文默认版本
+ baseDoc = fs.readFileSync(defaultDocPath, 'utf-8')
+ } else {
+ console.error(`未找到 ${defaultDocPath} 文件`)
+ }
+ source = source.replace(':: BASE_DOC ::', baseDoc)
+ }
+
+ return source
+ },
+ render({ source, file, md }) {
+ // console.log('source: ', source, ' file: ', file, ' md: ', md)
+ const demoDefsStr = Object.keys(demoImports)
+ .map((key) => demoImports[key])
+ .join(';\n')
+ const demoCodesDefsStr = Object.keys(demoCodesImports)
+ .map((key) => demoCodesImports[key])
+ .join(';\n')
+
+ const demoInstallStr = Object.keys(demoImports).join(',')
+ const demoCodeInstallStr = Object.keys(demoCodesImports).join(',')
+
+ const sfc = mdToOmi({
+ md,
+ file,
+ source,
+ demoDefsStr,
+ demoCodesDefsStr,
+ demoInstallStr,
+ demoCodeInstallStr,
+ })
+
+ return sfc
+ },
+}
diff --git a/site/src/components/web/button/button_md.tsx b/site/src/components/web/button/button_md.tsx
new file mode 100644
index 000000000..f529c3290
--- /dev/null
+++ b/site/src/components/web/button/button_md.tsx
@@ -0,0 +1,185 @@
+import { h, tag, WeElement } from 'omi'
+// import tdesign style
+import css from '@common/style/web/docs.less'
+
+// import site web components
+import 'tdesign-site-components'
+import css1 from 'tdesign-site-components/lib/styles/style.css'
+import css2 from 'tdesign-site-components/lib/styles/prism-theme.less'
+import css3 from 'tdesign-site-components/lib/styles/prism-theme-dark.less'
+@tag('button-md')
+export default class ButtonMd extends WeElement {
+ static css = css + css1 + css2 + css3
+ render(props: any) {
+ return (
+
+
+
+ -
+
+ Button Props
+
+
+
+
+
+
+
+
+ 名称 | 类型 | 默认值 | 说明 | 必传 |
+
+
+
+
+ class | String | - | 类名 | N |
+
+
+ style | Object | - |
+
+ 样式
+ |
+ N |
+
+
+ block | Boolean | false | 是否为块级元素 | N |
+
+
+ children | TNode | - |
+
+ 按钮内容,同 content。TS 类型:string | TNode 。
+
+ 通用类型定义
+
+ |
+ N |
+
+
+ content | TNode | - |
+
+ 按钮内容。TS 类型:string | TNode 。
+
+ 通用类型定义
+
+ |
+ N |
+
+
+ disabled | Boolean | false | 禁用状态 | N |
+
+
+ ghost | Boolean | false | 是否为幽灵按钮(镂空按钮) | N |
+
+
+ href | String | - |
+
+ 跳转地址。href 存在时,按钮标签默认使用 <a> 渲染;如果指定了 tag
+ 则使用指定的标签渲染
+ |
+ N |
+
+
+ icon | TElement | - |
+
+ 按钮内部图标,可完全自定义。TS 类型:TNode 。
+
+ 通用类型定义
+
+ |
+ N |
+
+
+ loading | Boolean | false | 是否显示为加载状态 | N |
+
+
+ shape | String | rectangle |
+ 按钮形状,有 4 种:长方形、正方形、圆角长方形、圆形。可选项:rectangle/square/round/circle |
+ N |
+
+
+ size | String | medium |
+
+ 组件尺寸。可选项:small/medium/large。TS 类型:SizeEnum 。
+
+ 通用类型定义
+
+ |
+ N |
+
+
+ suffix | TElement | - |
+
+ 右侧内容,可用于定义右侧图标。TS 类型:TNode 。
+
+ 通用类型定义
+
+ |
+ N |
+
+
+ tag | String | - |
+
+ 渲染按钮的 HTML 标签,默认使用标签 <button> 渲染,可以自定义为 <a>
+ <div> 等。透传全部 HTML 属性,如:href/target/data-* 等。⚠️ 禁用按钮
+ <button disabled> 无法显示 Popup 浮层信息,可通过修改 tag=div
+ 解决这个问题。可选项:button/a/div
+ |
+ N |
+
+
+ theme | String | - |
+
+ 组件风格,依次为默认色、品牌色、危险色、警告色、成功色。可选项:default/primary/danger/warning/success
+ |
+ N |
+
+
+ type | String | button | 按钮类型。可选项:submit/reset/button | N |
+
+
+ variant | String | base |
+ 按钮形式,基础、线框、虚线、文字。可选项:base/outline/dashed/text | N |
+
+
+ onClick | Function | |
+
+ TS 类型:(e: MouseEvent) => void
+
+ 点击时触发
+ |
+ N |
+
+
+
`,
+ }}
+ >
+
+ )
+ }
+}
diff --git a/site/src/components/web/button/index.tsx b/site/src/components/web/button/index.tsx
index 2e7dca5b2..c9f17a1fd 100644
--- a/site/src/components/web/button/index.tsx
+++ b/site/src/components/web/button/index.tsx
@@ -5,6 +5,8 @@ import './variant-checkbox'
import './size-checkbox'
import './shape-checkbox'
import '../common/index'
+import buttonMd from '../../../../../src/button/button.md'
+import './button_md'
import '../../../../../src/button'
import '../../../../../src/button/_example/base'
import '../../../../../src/button/_example/block'
@@ -16,6 +18,14 @@ import '../../../../../src/button/_example/size'
import '../../../../../src/button/_example/status'
import '../../../../../src/button/_example/theme'
import * as marked from 'marked'
+// import tdesign style
+import css from '@common/style/web/docs.less'
+
+// import site web components
+import 'tdesign-site-components'
+import css1 from 'tdesign-site-components/lib/styles/style.css'
+import css2 from 'tdesign-site-components/lib/styles/prism-theme.less'
+import css3 from 'tdesign-site-components/lib/styles/prism-theme-dark.less'
const docsHTML = marked.parse(`
:: BASE_DOC ::
@@ -58,6 +68,7 @@ export const buttonState = signal({
define(
'page-button',
class extends WeElement {
+ static css = css + css1 + css2 + css3
static defaultProps = {
tab: 'demo',
}
@@ -89,7 +100,7 @@ define(
render(props: {} | OmiProps<{}, any>, store: any) {
return (
- <>
+
@@ -200,15 +211,18 @@ define(
-
+ {/* {buttonMd} */}
+ {/*
+ >
*/}
- >
+
)
}
},
diff --git a/site/vite.config.js b/site/vite.config.js
index 73518fe5f..b432e4e44 100644
--- a/site/vite.config.js
+++ b/site/vite.config.js
@@ -1,14 +1,15 @@
-import { defineConfig } from 'vite';
-import { VitePWA } from 'vite-plugin-pwa';
-import tDocPlugin from './plugin-doc';
-import pwaConfig from './pwaConfig';
-import { resolveConfig, basePlugin } from '../script/vite.base.config';
+import { defineConfig } from 'vite'
+import { VitePWA } from 'vite-plugin-pwa'
+import tDocPlugin from './plugin-doc'
+import apiTDocPlugin from './plugin-api-doc'
+import pwaConfig from './pwaConfig'
+import { resolveConfig, basePlugin } from '../script/vite.base.config'
const publicPathMap = {
preview: '/',
intranet: '/omi/',
production: './',
-};
+}
export default ({ mode }) => {
return defineConfig({
@@ -26,15 +27,15 @@ export default ({ mode }) => {
},
build: {
outDir: '../tdesign',
- chunkSizeWarningLimit: 10000
+ chunkSizeWarningLimit: 10000,
},
esbuild: {
jsxFactory: 'h',
- jsxFragment: 'h.f'
+ jsxFragment: 'h.f',
},
- plugins: [...basePlugin, tDocPlugin()], // VitePWA(pwaConfig)
+ plugins: [...basePlugin, apiTDocPlugin()], // VitePWA(pwaConfig)
optimizeDeps: {
include: ['prismjs', 'prismjs/components/prism-bash.js'],
},
- });
-};
+ })
+}
diff --git a/src/button/button.md b/src/button/button.md
new file mode 100644
index 000000000..66db3c0f7
--- /dev/null
+++ b/src/button/button.md
@@ -0,0 +1,25 @@
+:: BASE_DOC ::
+
+## API
+### Button Props
+
+名称 | 类型 | 默认值 | 说明 | 必传
+-- | -- | -- | -- | --
+className | String | - | 类名 | N
+style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
+block | Boolean | false | 是否为块级元素 | N
+children | TNode | - | 按钮内容,同 content。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/omijs/tdesign/blob/main/src/common.ts) | N
+content | TNode | - | 按钮内容。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/omijs/tdesign/blob/main/src/common.ts) | N
+disabled | Boolean | false | 禁用状态 | N
+ghost | Boolean | false | 是否为幽灵按钮(镂空按钮) | N
+href | String | - | 跳转地址。href 存在时,按钮标签默认使用 `
` 渲染;如果指定了 `tag` 则使用指定的标签渲染 | N
+icon | TElement | - | 按钮内部图标,可完全自定义。TS 类型:`TNode`。[通用类型定义](https://github.com/omijs/tdesign/blob/main/src/common.ts) | N
+loading | Boolean | false | 是否显示为加载状态 | N
+shape | String | rectangle | 按钮形状,有 4 种:长方形、正方形、圆角长方形、圆形。可选项:rectangle/square/round/circle | N
+size | String | medium | 组件尺寸。可选项:small/medium/large。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/omijs/tdesign/blob/main/src/common.ts) | N
+suffix | TElement | - | 右侧内容,可用于定义右侧图标。TS 类型:`TNode`。[通用类型定义](https://github.com/omijs/tdesign/blob/main/src/common.ts) | N
+tag | String | - | 渲染按钮的 HTML 标签,默认使用标签 `