@dsh-plugin/dsh-ui-components 0.0.1 → 0.1.0-dev.43616582648
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/NOTICE +21 -21
- package/README.md +136 -136
- package/README.zh_CN.md +136 -136
- package/docs/tokens.md +67 -67
- package/lib/basic/form.js +49 -49
- package/lib/basic/styles.js +391 -391
- package/lib/composites/styles.js +427 -427
- package/lib/data/styles.js +257 -257
- package/lib/display/styles.js +310 -310
- package/lib/feedback/styles.js +228 -228
- package/lib/foundation/bridge.js +203 -203
- package/lib/navigation/styles.js +417 -417
- package/package.json +104 -104
- package/scripts/check-size.mjs +69 -69
- package/scripts/dump-npm-latest.mjs +59 -59
- package/scripts/gen-host-tokens.mjs +61 -61
- package/scripts/gen-icons.mjs +188 -178
- package/scripts/verify-no-literal-colors.mjs +63 -63
- package/scripts/verify-tokens.mjs +135 -135
- package/src/basic/button.tsx +76 -76
- package/src/basic/form.tsx +187 -187
- package/src/basic/icon-button.tsx +66 -66
- package/src/basic/index.ts +38 -38
- package/src/basic/input.tsx +140 -140
- package/src/basic/select.tsx +120 -120
- package/src/basic/styles.ts +412 -412
- package/src/basic/toggle.tsx +116 -116
- package/src/composites/data-view.tsx +189 -189
- package/src/composites/index.ts +11 -11
- package/src/composites/indicator.tsx +90 -90
- package/src/composites/menu-select.tsx +230 -230
- package/src/composites/panel.tsx +72 -72
- package/src/composites/settings.tsx +80 -80
- package/src/composites/styles.ts +450 -450
- package/src/data/collection.tsx +71 -71
- package/src/data/index.ts +39 -39
- package/src/data/info.tsx +89 -89
- package/src/data/picker.tsx +113 -113
- package/src/data/styles.ts +278 -278
- package/src/display/container.tsx +67 -67
- package/src/display/feedback.tsx +92 -92
- package/src/display/index.ts +44 -44
- package/src/display/label.tsx +165 -165
- package/src/display/overlay.tsx +103 -103
- package/src/display/styles.ts +332 -332
- package/src/feedback/global.tsx +110 -110
- package/src/feedback/index.ts +19 -19
- package/src/feedback/inline.tsx +50 -50
- package/src/feedback/panel.tsx +93 -93
- package/src/feedback/styles.ts +250 -250
- package/src/foundation/bridge.ts +318 -318
- package/src/foundation/icons.tsx +315 -315
- package/src/foundation/index.ts +33 -33
- package/src/foundation/palette.ts +153 -153
- package/src/foundation/provider.tsx +62 -62
- package/src/foundation/style.ts +63 -63
- package/src/foundation/tokens.ts +62 -62
- package/src/index.ts +18 -18
- package/src/navigation/hierarchy.tsx +82 -82
- package/src/navigation/index.ts +21 -21
- package/src/navigation/indicator.tsx +125 -125
- package/src/navigation/menu.tsx +94 -94
- package/src/navigation/segmented.tsx +136 -136
- package/src/navigation/styles.ts +438 -438
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* 校验源码里出现的每个 DSH 变量名都真实存在于宿主样式表中。
|
|
4
|
-
*
|
|
5
|
-
* 宿主升级后若重命名某个 `--dsw-*` 令牌,本脚本会在离线阶段直接报错,而不是
|
|
6
|
-
* 等到运行时样式静默失效。宿主样式表位置按顺序探测:
|
|
7
|
-
*
|
|
8
|
-
* 1. `DSH_UI_THEME_DIR` 环境变量指向的目录;
|
|
9
|
-
* 2. 工作区内的 DSH 源码副本 `.dsh/temp/session-tree-harness/...`;
|
|
10
|
-
* 3. `node_modules/@deepseek-ai/dsh-client-ui-theme/` 之类已安装布局。
|
|
11
|
-
*
|
|
12
|
-
* 全部探测失败时默认跳过本次校验并以 0 退出:宿主样式表不属于本包依赖,缺了
|
|
13
|
-
* 它只说明当前机器没装 DSH 源码,不该阻断发布。设置 `DSH_UI_REQUIRE_TOKENS=1`
|
|
14
|
-
* 可以让缺失变成失败,供需要强校验的 CI 使用。
|
|
15
|
-
*/
|
|
16
|
-
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'
|
|
17
|
-
import { dirname, join, resolve } from 'node:path'
|
|
18
|
-
import { fileURLToPath } from 'node:url'
|
|
19
|
-
|
|
20
|
-
const root = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
21
|
-
|
|
22
|
-
/** 宿主样式表文件相对于 ui-theme 包的路径。 */
|
|
23
|
-
const STYLE_FILES = ['base.css', 'design-platform.css', 'gradient-shadow-text.css', 'focus.css', 'scrollbar.css', 'corner-shape.css']
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 按候选顺序定位宿主样式目录。
|
|
27
|
-
*
|
|
28
|
-
* @returns 样式目录绝对路径。
|
|
29
|
-
*/
|
|
30
|
-
function locateThemeDir() {
|
|
31
|
-
const candidates = []
|
|
32
|
-
if (process.env.DSH_UI_THEME_DIR) candidates.push(process.env.DSH_UI_THEME_DIR)
|
|
33
|
-
candidates.push(resolve(root, '..', '.dsh', 'temp', 'session-tree-harness', 'packages', 'client', 'ui-theme', 'src', 'styles'))
|
|
34
|
-
candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'src', 'styles'))
|
|
35
|
-
candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'lib', 'styles'))
|
|
36
|
-
for (const candidate of candidates) {
|
|
37
|
-
if (existsSync(candidate) && statSync(candidate).isDirectory()) return candidate
|
|
38
|
-
}
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const themeDir = locateThemeDir()
|
|
43
|
-
|
|
44
|
-
if (themeDir === null) {
|
|
45
|
-
const message = [
|
|
46
|
-
'未找到 DSH 宿主样式目录,跳过令牌校验。',
|
|
47
|
-
'需要该校验时设置 DSH_UI_THEME_DIR,或在工作区保留 .dsh/temp/session-tree-harness 源码副本。',
|
|
48
|
-
].join('\n')
|
|
49
|
-
if (process.env.DSH_UI_REQUIRE_TOKENS === '1') {
|
|
50
|
-
console.error(message.replace('跳过令牌校验', '且已设置 DSH_UI_REQUIRE_TOKENS=1'))
|
|
51
|
-
process.exit(2)
|
|
52
|
-
}
|
|
53
|
-
console.log(message)
|
|
54
|
-
process.exit(0)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** 从宿主样式表解析出的全部变量名。 */
|
|
58
|
-
const hostTokens = new Set()
|
|
59
|
-
for (const file of STYLE_FILES) {
|
|
60
|
-
const path = join(themeDir, file)
|
|
61
|
-
if (!existsSync(path)) continue
|
|
62
|
-
const css = readFileSync(path, 'utf8')
|
|
63
|
-
for (const match of css.matchAll(/(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g)) hostTokens.add(match[1])
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (hostTokens.size === 0) {
|
|
67
|
-
console.error(`未能从 ${themeDir} 解析出任何 --dsw-* 变量`)
|
|
68
|
-
process.exit(2)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 匹配源码中一个完整的宿主令牌名。
|
|
73
|
-
*
|
|
74
|
-
* 覆盖宿主的三类前缀:`--dsw-*`(主题令牌)、`--dsh-*`(主题运行时写入的
|
|
75
|
-
* 变量)与 `--ds-*`(字体与动效)。令牌必须以字母或数字结尾,且后面不能
|
|
76
|
-
* 紧跟 `-`、`*` 或标识符字符,这样注释里的通配写法(`--dsw-alias-*`)与
|
|
77
|
-
* 半截前缀不会被当成真实令牌名。
|
|
78
|
-
*/
|
|
79
|
-
const TOKEN_PATTERN = /--ds[wh]?-[a-z0-9-]*[a-z0-9](?![-\w*])/g
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 递归列出目录下的 TypeScript 源文件。
|
|
83
|
-
*
|
|
84
|
-
* @param dir - 起始目录。
|
|
85
|
-
* @returns 文件绝对路径列表。
|
|
86
|
-
*/
|
|
87
|
-
function listSources(dir) {
|
|
88
|
-
const out = []
|
|
89
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
90
|
-
const path = join(dir, entry.name)
|
|
91
|
-
if (entry.isDirectory()) out.push(...listSources(path))
|
|
92
|
-
else if (/\.tsx?$/.test(entry.name)) out.push(path)
|
|
93
|
-
}
|
|
94
|
-
return out
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* 匹配本包自己定义局部变量的两种写法:CSS 里的 `--name:` 声明,以及 TSX
|
|
99
|
-
* inline style 里的 `'--name': value` 键。分段控件的位置变量就是这样注入的。
|
|
100
|
-
*/
|
|
101
|
-
const DECLARATION_PATTERNS = [
|
|
102
|
-
/(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g,
|
|
103
|
-
/['"](--ds[wh]?-[a-z0-9-]*[a-z0-9])['"]\s*:/g,
|
|
104
|
-
]
|
|
105
|
-
|
|
106
|
-
const sources = listSources(join(root, 'src')).filter(file => !file.endsWith('icons-index.ts'))
|
|
107
|
-
|
|
108
|
-
// 本包自己声明的局部变量(如分段控件的位置变量)不要求宿主定义。
|
|
109
|
-
const localTokens = new Set()
|
|
110
|
-
for (const file of sources) {
|
|
111
|
-
const text = readFileSync(file, 'utf8')
|
|
112
|
-
for (const pattern of DECLARATION_PATTERNS) {
|
|
113
|
-
for (const match of text.matchAll(pattern)) localTokens.add(match[1])
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const problems = []
|
|
118
|
-
const referenced = new Set()
|
|
119
|
-
for (const file of sources) {
|
|
120
|
-
const relative = file.slice(root.length + 1).replace(/\\/g, '/')
|
|
121
|
-
const text = readFileSync(file, 'utf8')
|
|
122
|
-
for (const match of text.matchAll(TOKEN_PATTERN)) {
|
|
123
|
-
const name = match[0]
|
|
124
|
-
referenced.add(name)
|
|
125
|
-
if (!hostTokens.has(name) && !localTokens.has(name)) problems.push(`${relative} 引用了不存在的令牌 ${name}`)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (problems.length > 0) {
|
|
130
|
-
console.error([...new Set(problems)].join('\n'))
|
|
131
|
-
console.error('对照宿主样式表修正上述令牌名')
|
|
132
|
-
process.exit(1)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.log(`令牌校验通过:引用 ${referenced.size} 个令牌(其中本包声明 ${localTokens.size} 个),宿主定义 ${hostTokens.size} 个`)
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 校验源码里出现的每个 DSH 变量名都真实存在于宿主样式表中。
|
|
4
|
+
*
|
|
5
|
+
* 宿主升级后若重命名某个 `--dsw-*` 令牌,本脚本会在离线阶段直接报错,而不是
|
|
6
|
+
* 等到运行时样式静默失效。宿主样式表位置按顺序探测:
|
|
7
|
+
*
|
|
8
|
+
* 1. `DSH_UI_THEME_DIR` 环境变量指向的目录;
|
|
9
|
+
* 2. 工作区内的 DSH 源码副本 `.dsh/temp/session-tree-harness/...`;
|
|
10
|
+
* 3. `node_modules/@deepseek-ai/dsh-client-ui-theme/` 之类已安装布局。
|
|
11
|
+
*
|
|
12
|
+
* 全部探测失败时默认跳过本次校验并以 0 退出:宿主样式表不属于本包依赖,缺了
|
|
13
|
+
* 它只说明当前机器没装 DSH 源码,不该阻断发布。设置 `DSH_UI_REQUIRE_TOKENS=1`
|
|
14
|
+
* 可以让缺失变成失败,供需要强校验的 CI 使用。
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs'
|
|
17
|
+
import { dirname, join, resolve } from 'node:path'
|
|
18
|
+
import { fileURLToPath } from 'node:url'
|
|
19
|
+
|
|
20
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
21
|
+
|
|
22
|
+
/** 宿主样式表文件相对于 ui-theme 包的路径。 */
|
|
23
|
+
const STYLE_FILES = ['base.css', 'design-platform.css', 'gradient-shadow-text.css', 'focus.css', 'scrollbar.css', 'corner-shape.css']
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 按候选顺序定位宿主样式目录。
|
|
27
|
+
*
|
|
28
|
+
* @returns 样式目录绝对路径。
|
|
29
|
+
*/
|
|
30
|
+
function locateThemeDir() {
|
|
31
|
+
const candidates = []
|
|
32
|
+
if (process.env.DSH_UI_THEME_DIR) candidates.push(process.env.DSH_UI_THEME_DIR)
|
|
33
|
+
candidates.push(resolve(root, '..', '.dsh', 'temp', 'session-tree-harness', 'packages', 'client', 'ui-theme', 'src', 'styles'))
|
|
34
|
+
candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'src', 'styles'))
|
|
35
|
+
candidates.push(resolve(root, 'node_modules', '@deepseek-ai', 'dsh-client-ui-theme', 'lib', 'styles'))
|
|
36
|
+
for (const candidate of candidates) {
|
|
37
|
+
if (existsSync(candidate) && statSync(candidate).isDirectory()) return candidate
|
|
38
|
+
}
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const themeDir = locateThemeDir()
|
|
43
|
+
|
|
44
|
+
if (themeDir === null) {
|
|
45
|
+
const message = [
|
|
46
|
+
'未找到 DSH 宿主样式目录,跳过令牌校验。',
|
|
47
|
+
'需要该校验时设置 DSH_UI_THEME_DIR,或在工作区保留 .dsh/temp/session-tree-harness 源码副本。',
|
|
48
|
+
].join('\n')
|
|
49
|
+
if (process.env.DSH_UI_REQUIRE_TOKENS === '1') {
|
|
50
|
+
console.error(message.replace('跳过令牌校验', '且已设置 DSH_UI_REQUIRE_TOKENS=1'))
|
|
51
|
+
process.exit(2)
|
|
52
|
+
}
|
|
53
|
+
console.log(message)
|
|
54
|
+
process.exit(0)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 从宿主样式表解析出的全部变量名。 */
|
|
58
|
+
const hostTokens = new Set()
|
|
59
|
+
for (const file of STYLE_FILES) {
|
|
60
|
+
const path = join(themeDir, file)
|
|
61
|
+
if (!existsSync(path)) continue
|
|
62
|
+
const css = readFileSync(path, 'utf8')
|
|
63
|
+
for (const match of css.matchAll(/(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g)) hostTokens.add(match[1])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (hostTokens.size === 0) {
|
|
67
|
+
console.error(`未能从 ${themeDir} 解析出任何 --dsw-* 变量`)
|
|
68
|
+
process.exit(2)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 匹配源码中一个完整的宿主令牌名。
|
|
73
|
+
*
|
|
74
|
+
* 覆盖宿主的三类前缀:`--dsw-*`(主题令牌)、`--dsh-*`(主题运行时写入的
|
|
75
|
+
* 变量)与 `--ds-*`(字体与动效)。令牌必须以字母或数字结尾,且后面不能
|
|
76
|
+
* 紧跟 `-`、`*` 或标识符字符,这样注释里的通配写法(`--dsw-alias-*`)与
|
|
77
|
+
* 半截前缀不会被当成真实令牌名。
|
|
78
|
+
*/
|
|
79
|
+
const TOKEN_PATTERN = /--ds[wh]?-[a-z0-9-]*[a-z0-9](?![-\w*])/g
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 递归列出目录下的 TypeScript 源文件。
|
|
83
|
+
*
|
|
84
|
+
* @param dir - 起始目录。
|
|
85
|
+
* @returns 文件绝对路径列表。
|
|
86
|
+
*/
|
|
87
|
+
function listSources(dir) {
|
|
88
|
+
const out = []
|
|
89
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
90
|
+
const path = join(dir, entry.name)
|
|
91
|
+
if (entry.isDirectory()) out.push(...listSources(path))
|
|
92
|
+
else if (/\.tsx?$/.test(entry.name)) out.push(path)
|
|
93
|
+
}
|
|
94
|
+
return out
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 匹配本包自己定义局部变量的两种写法:CSS 里的 `--name:` 声明,以及 TSX
|
|
99
|
+
* inline style 里的 `'--name': value` 键。分段控件的位置变量就是这样注入的。
|
|
100
|
+
*/
|
|
101
|
+
const DECLARATION_PATTERNS = [
|
|
102
|
+
/(--ds[wh]?-[a-z0-9-]*[a-z0-9])\s*:/g,
|
|
103
|
+
/['"](--ds[wh]?-[a-z0-9-]*[a-z0-9])['"]\s*:/g,
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
const sources = listSources(join(root, 'src')).filter(file => !file.endsWith('icons-index.ts'))
|
|
107
|
+
|
|
108
|
+
// 本包自己声明的局部变量(如分段控件的位置变量)不要求宿主定义。
|
|
109
|
+
const localTokens = new Set()
|
|
110
|
+
for (const file of sources) {
|
|
111
|
+
const text = readFileSync(file, 'utf8')
|
|
112
|
+
for (const pattern of DECLARATION_PATTERNS) {
|
|
113
|
+
for (const match of text.matchAll(pattern)) localTokens.add(match[1])
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const problems = []
|
|
118
|
+
const referenced = new Set()
|
|
119
|
+
for (const file of sources) {
|
|
120
|
+
const relative = file.slice(root.length + 1).replace(/\\/g, '/')
|
|
121
|
+
const text = readFileSync(file, 'utf8')
|
|
122
|
+
for (const match of text.matchAll(TOKEN_PATTERN)) {
|
|
123
|
+
const name = match[0]
|
|
124
|
+
referenced.add(name)
|
|
125
|
+
if (!hostTokens.has(name) && !localTokens.has(name)) problems.push(`${relative} 引用了不存在的令牌 ${name}`)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (problems.length > 0) {
|
|
130
|
+
console.error([...new Set(problems)].join('\n'))
|
|
131
|
+
console.error('对照宿主样式表修正上述令牌名')
|
|
132
|
+
process.exit(1)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(`令牌校验通过:引用 ${referenced.size} 个令牌(其中本包声明 ${localTokens.size} 个),宿主定义 ${hostTokens.size} 个`)
|
package/src/basic/button.tsx
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 按钮。
|
|
3
|
-
*
|
|
4
|
-
* 语义与键盘行为来自 Arco 的 `Button`,外观由桥接样式表与 `basic/styles`
|
|
5
|
-
* 决定。DSH 的按钮尺寸是 36(标准)与 28(紧凑),主按钮是中性近黑色而非
|
|
6
|
-
* 品牌蓝,这一层已经由桥接规则处理。
|
|
7
|
-
*
|
|
8
|
-
* @module @dsh-plugin/dsh-ui-components/basic/button
|
|
9
|
-
*/
|
|
10
|
-
import * as React from 'react'
|
|
11
|
-
import { Arco } from '@dsh-plugin/dsh-loader/client'
|
|
12
|
-
import { injectBasicStyles } from './styles.js'
|
|
13
|
-
|
|
14
|
-
/** 按钮的视觉层级。 */
|
|
15
|
-
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text'
|
|
16
|
-
|
|
17
|
-
/** 按钮尺寸,`md` 高 36,`sm` 高 28。 */
|
|
18
|
-
export type ButtonSize = 'md' | 'sm' | 'lg'
|
|
19
|
-
|
|
20
|
-
/** {@link Button} 的 props。 */
|
|
21
|
-
export type ButtonProps = Omit<React.ComponentPropsWithoutRef<typeof Arco.Button>, 'type' | 'size' | 'status'> & {
|
|
22
|
-
/** 视觉层级,默认 `secondary`。 */
|
|
23
|
-
variant?: ButtonVariant
|
|
24
|
-
/** 尺寸,默认 `md`。 */
|
|
25
|
-
size?: ButtonSize
|
|
26
|
-
/** 渲染为危险操作(删除、清空)。 */
|
|
27
|
-
danger?: boolean
|
|
28
|
-
/** 前置图标。 */
|
|
29
|
-
icon?: React.ReactNode
|
|
30
|
-
/** 撑满父容器宽度。 */
|
|
31
|
-
block?: boolean
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Arco 的 `type` 取值。 */
|
|
35
|
-
const ARCO_TYPE: Record<ButtonVariant, 'primary' | 'secondary' | 'outline' | 'text'> = {
|
|
36
|
-
primary: 'primary',
|
|
37
|
-
secondary: 'secondary',
|
|
38
|
-
outline: 'outline',
|
|
39
|
-
text: 'text',
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** DSH 尺寸到 Arco 尺寸的映射。 */
|
|
43
|
-
const ARCO_SIZE: Record<ButtonSize, 'default' | 'small' | 'large'> = {
|
|
44
|
-
md: 'default',
|
|
45
|
-
sm: 'small',
|
|
46
|
-
lg: 'large',
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 一个按钮。
|
|
51
|
-
*
|
|
52
|
-
* @param props - 见 {@link ButtonProps}。
|
|
53
|
-
* @param ref - 指向原生 `button` 元素,供焦点管理与浮层定位使用。
|
|
54
|
-
* @returns 按钮元素。
|
|
55
|
-
*/
|
|
56
|
-
export const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>> =
|
|
57
|
-
React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
58
|
-
{ variant = 'secondary', size = 'md', danger, icon, block, children, htmlType, ...rest },
|
|
59
|
-
ref,
|
|
60
|
-
) {
|
|
61
|
-
injectBasicStyles()
|
|
62
|
-
return (
|
|
63
|
-
<Arco.Button
|
|
64
|
-
{...rest}
|
|
65
|
-
ref={ref}
|
|
66
|
-
type={ARCO_TYPE[variant]}
|
|
67
|
-
size={ARCO_SIZE[size]}
|
|
68
|
-
status={danger === true ? 'danger' : undefined}
|
|
69
|
-
long={block === true}
|
|
70
|
-
htmlType={htmlType ?? 'button'}
|
|
71
|
-
>
|
|
72
|
-
{icon === undefined || icon === null ? null : icon}
|
|
73
|
-
{children}
|
|
74
|
-
</Arco.Button>
|
|
75
|
-
)
|
|
76
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* 按钮。
|
|
3
|
+
*
|
|
4
|
+
* 语义与键盘行为来自 Arco 的 `Button`,外观由桥接样式表与 `basic/styles`
|
|
5
|
+
* 决定。DSH 的按钮尺寸是 36(标准)与 28(紧凑),主按钮是中性近黑色而非
|
|
6
|
+
* 品牌蓝,这一层已经由桥接规则处理。
|
|
7
|
+
*
|
|
8
|
+
* @module @dsh-plugin/dsh-ui-components/basic/button
|
|
9
|
+
*/
|
|
10
|
+
import * as React from 'react'
|
|
11
|
+
import { Arco } from '@dsh-plugin/dsh-loader/client'
|
|
12
|
+
import { injectBasicStyles } from './styles.js'
|
|
13
|
+
|
|
14
|
+
/** 按钮的视觉层级。 */
|
|
15
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text'
|
|
16
|
+
|
|
17
|
+
/** 按钮尺寸,`md` 高 36,`sm` 高 28。 */
|
|
18
|
+
export type ButtonSize = 'md' | 'sm' | 'lg'
|
|
19
|
+
|
|
20
|
+
/** {@link Button} 的 props。 */
|
|
21
|
+
export type ButtonProps = Omit<React.ComponentPropsWithoutRef<typeof Arco.Button>, 'type' | 'size' | 'status'> & {
|
|
22
|
+
/** 视觉层级,默认 `secondary`。 */
|
|
23
|
+
variant?: ButtonVariant
|
|
24
|
+
/** 尺寸,默认 `md`。 */
|
|
25
|
+
size?: ButtonSize
|
|
26
|
+
/** 渲染为危险操作(删除、清空)。 */
|
|
27
|
+
danger?: boolean
|
|
28
|
+
/** 前置图标。 */
|
|
29
|
+
icon?: React.ReactNode
|
|
30
|
+
/** 撑满父容器宽度。 */
|
|
31
|
+
block?: boolean
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Arco 的 `type` 取值。 */
|
|
35
|
+
const ARCO_TYPE: Record<ButtonVariant, 'primary' | 'secondary' | 'outline' | 'text'> = {
|
|
36
|
+
primary: 'primary',
|
|
37
|
+
secondary: 'secondary',
|
|
38
|
+
outline: 'outline',
|
|
39
|
+
text: 'text',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** DSH 尺寸到 Arco 尺寸的映射。 */
|
|
43
|
+
const ARCO_SIZE: Record<ButtonSize, 'default' | 'small' | 'large'> = {
|
|
44
|
+
md: 'default',
|
|
45
|
+
sm: 'small',
|
|
46
|
+
lg: 'large',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 一个按钮。
|
|
51
|
+
*
|
|
52
|
+
* @param props - 见 {@link ButtonProps}。
|
|
53
|
+
* @param ref - 指向原生 `button` 元素,供焦点管理与浮层定位使用。
|
|
54
|
+
* @returns 按钮元素。
|
|
55
|
+
*/
|
|
56
|
+
export const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>> =
|
|
57
|
+
React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
58
|
+
{ variant = 'secondary', size = 'md', danger, icon, block, children, htmlType, ...rest },
|
|
59
|
+
ref,
|
|
60
|
+
) {
|
|
61
|
+
injectBasicStyles()
|
|
62
|
+
return (
|
|
63
|
+
<Arco.Button
|
|
64
|
+
{...rest}
|
|
65
|
+
ref={ref}
|
|
66
|
+
type={ARCO_TYPE[variant]}
|
|
67
|
+
size={ARCO_SIZE[size]}
|
|
68
|
+
status={danger === true ? 'danger' : undefined}
|
|
69
|
+
long={block === true}
|
|
70
|
+
htmlType={htmlType ?? 'button'}
|
|
71
|
+
>
|
|
72
|
+
{icon === undefined || icon === null ? null : icon}
|
|
73
|
+
{children}
|
|
74
|
+
</Arco.Button>
|
|
75
|
+
)
|
|
76
|
+
})
|