@q1travel/design-system 0.10.0 → 0.11.0
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/README.md +1 -1
- package/bin/q1-design.mjs +152 -1
- package/dist-pkg/design-system.d.ts +156 -39
- package/dist-pkg/design-system.js +2524 -2356
- package/dist-pkg/style.css +432 -238
- package/package.json +1 -1
- package/tooling/README.md +1 -1
package/README.md
CHANGED
|
@@ -60,4 +60,4 @@ docs/ review/ 架构骨架、agent 规范、历史决策记录
|
|
|
60
60
|
- **AI Agent 必须遵循根目录 `AGENTS.md`**(Claude Code 与 Codex 共用的 canonical 入口:命令速查、红线约束、工作闭环与知识索引);`CLAUDE.md` 仅为其薄包装。
|
|
61
61
|
- `docs/architecture.md` — 双构建管线、契约生成与下游消费模型、发布溯源链路。
|
|
62
62
|
- `docs/adr/template.md` — 新 ADR 从此模板开始;早期已接受决策存于 `review/decision-log/`。
|
|
63
|
-
- 下游消费方式:安装 `@q1travel/design-system` 后 import 组件与 `style.css` / `tokens.css`;`npx q1-design sync` 把包内 skill 与契约物化为只读缓存,`q1-design verify` 校验缓存未被改动,`q1-design lint` 执行 `Q1-DS-*` 机械规则;`q1-design ls` 列出随包组件,`q1-design doc <组件名>` 打印其公共 API(两者现推自随包的 `.d.ts
|
|
63
|
+
- 下游消费方式:安装 `@q1travel/design-system` 后 import 组件与 `style.css` / `tokens.css`;`npx q1-design sync` 把包内 skill 与契约物化为只读缓存,`q1-design verify` 校验缓存未被改动,`q1-design lint` 执行 `Q1-DS-*` 机械规则;`q1-design ls` 列出随包组件,`q1-design doc <组件名>` 打印其公共 API(两者现推自随包的 `.d.ts`,不另存清单);`q1-design overrides` 统计并分类消费方对组件外观的覆写。
|
package/bin/q1-design.mjs
CHANGED
|
@@ -896,8 +896,154 @@ function runDoc(consumerRootDir, requestedName) {
|
|
|
896
896
|
}
|
|
897
897
|
}
|
|
898
898
|
|
|
899
|
+
// —— overrides ——
|
|
900
|
+
// 逃生舱口的可见化。Kumo 的做法是把这个 prop 叫 DANGEROUS_className,让每次绕过都自带
|
|
901
|
+
// 羞耻感。本库不照搬,因为实测数据不支持:q1travel-web 里 814 处组件使用中 223 处传了
|
|
902
|
+
// className(27%)。当四分之一的调用都在用它,它已经是事实上的 API——给常态路径起个
|
|
903
|
+
// 羞耻的名字,只会训练出对警告的免疫,那比没有警告更糟;何况改名要迁 227 处调用点。
|
|
904
|
+
//
|
|
905
|
+
// 真正缺的不是羞耻感,是**知道自己在哪里被绕过、绕过去做什么**。所以做成测量命令而不是
|
|
906
|
+
// pass/fail 规则:lint 只看得到 JSX,分不出「加个左边距」和「把按钮从头重画」——那要去
|
|
907
|
+
// 读消费方 CSS 里那个类的内容。这条命令做的正是这件事。
|
|
908
|
+
//
|
|
909
|
+
// 不进 lint 的第二个理由:覆写布局是正当的,规则拦不住也不该拦。一条会把正当用法一起
|
|
910
|
+
// 报红的规则,最后会被整体关掉,连同它本来能抓住的东西。
|
|
911
|
+
|
|
912
|
+
// 只动外观的属性:这些出现即意味着「在重画组件」,而不是「在摆放组件」。
|
|
913
|
+
const SKIN_PROPERTIES = new Set([
|
|
914
|
+
'background', 'background-color', 'background-image', 'border', 'border-color',
|
|
915
|
+
'border-radius', 'border-width', 'border-style', 'color', 'box-shadow',
|
|
916
|
+
'font-size', 'font-weight', 'outline', 'opacity',
|
|
917
|
+
]);
|
|
918
|
+
// 只摆位置的属性:这些是消费方的正当需求,组件本就不该管。
|
|
919
|
+
const BOX_PROPERTIES = new Set([
|
|
920
|
+
'margin', 'margin-top', 'margin-bottom', 'margin-left', 'margin-right',
|
|
921
|
+
'margin-block', 'margin-inline', 'width', 'max-width', 'min-width',
|
|
922
|
+
'flex', 'flex-grow', 'flex-shrink', 'grid-area', 'grid-column', 'grid-row',
|
|
923
|
+
'align-self', 'justify-self', 'order', 'position', 'top', 'left', 'right', 'bottom',
|
|
924
|
+
]);
|
|
925
|
+
|
|
926
|
+
const OVERRIDE_SOURCE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx']);
|
|
927
|
+
|
|
928
|
+
// 消费仓常在自己内部再导出一层(q1travel-web 的 src/components/ui/index.js 就是
|
|
929
|
+
// `export { Button } from "@q1travel/design-system"`,173 个文件走这条路,只有 64 个直接导入)。
|
|
930
|
+
// 只认包名会漏掉绝大多数调用点,所以额外接受调用方声明的再导出路径。
|
|
931
|
+
export function collectDesignSystemComponents(sourceText, reexportPaths = []) {
|
|
932
|
+
const names = new Set();
|
|
933
|
+
const sourcePatterns = [PACKAGE_NAME, ...reexportPaths].map((path) =>
|
|
934
|
+
path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
|
|
935
|
+
);
|
|
936
|
+
const importPattern = new RegExp(
|
|
937
|
+
`import\\s*\\{([^{}]*?)\\}\\s*from\\s*["'](?:${sourcePatterns.join('|')})["']`,
|
|
938
|
+
'gs',
|
|
939
|
+
);
|
|
940
|
+
for (const match of sourceText.matchAll(importPattern)) {
|
|
941
|
+
for (const part of match[1].split(',')) {
|
|
942
|
+
const name = part.replace(/\btype\b/, '').trim().split(/\s+as\s+/).pop()?.trim();
|
|
943
|
+
if (name && /^[A-Z][A-Za-z0-9_]*$/.test(name)) names.add(name);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return names;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// `<Button ... className={cx('a', 'b')}>` → ['a','b']。取标签内 400 字符窗口:
|
|
950
|
+
// 再长的标签里出现的 className 多半属于嵌套元素,算进来会虚报。
|
|
951
|
+
export function collectOverrideClassNames(sourceText, componentNames) {
|
|
952
|
+
const found = [];
|
|
953
|
+
for (const name of componentNames) {
|
|
954
|
+
const pattern = new RegExp(`<${name}\\b[^>]{0,400}?className=(\\{[^}]*\\}|"[^"]*"|'[^']*')`, 'gs');
|
|
955
|
+
for (const match of sourceText.matchAll(pattern)) {
|
|
956
|
+
for (const token of match[1].matchAll(/[A-Za-z][\w-]*/g)) {
|
|
957
|
+
const value = token[0];
|
|
958
|
+
if (['cx', 'clsx', 'classNames', 'undefined', 'true', 'false', 'null'].includes(value)) continue;
|
|
959
|
+
found.push({ component: name, className: value });
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return found;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
export function classifyOverride(className, cssText) {
|
|
967
|
+
const escaped = className.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
968
|
+
const bodies = [...cssText.matchAll(new RegExp(`\\.${escaped}\\b[^{]*\\{([^}]*)\\}`, 'gs'))];
|
|
969
|
+
if (bodies.length === 0) return 'undefined-in-css';
|
|
970
|
+
const properties = new Set();
|
|
971
|
+
for (const body of bodies) {
|
|
972
|
+
for (const declaration of body[1].split(';')) {
|
|
973
|
+
const [property] = declaration.split(':');
|
|
974
|
+
if (property) properties.add(property.trim());
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
for (const property of properties) if (SKIN_PROPERTIES.has(property)) return 'skin';
|
|
978
|
+
for (const property of properties) if (BOX_PROPERTIES.has(property)) return 'box';
|
|
979
|
+
return 'other';
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function runOverrides(consumerRootDir, inputPaths) {
|
|
983
|
+
const targets = collectLintTargetFiles(inputPaths.length > 0 ? inputPaths : ['src'], consumerRootDir);
|
|
984
|
+
let cssText = '';
|
|
985
|
+
const sources = [];
|
|
986
|
+
for (const file of targets) {
|
|
987
|
+
const text = readFileSync(file, 'utf8');
|
|
988
|
+
if (extname(file) === '.css') cssText += `\n${text}`;
|
|
989
|
+
else if (OVERRIDE_SOURCE_EXTENSIONS.has(extname(file))) sources.push(text);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// 自动发现再导出出口:仓内任何 `export { X } from '@q1travel/design-system'` 的模块,
|
|
993
|
+
// 它的导入路径同样是设计系统的调用点。不自动发现的话,用了再导出的仓库会得到一个
|
|
994
|
+
// 看起来很干净、实则漏掉大半调用点的报告——那比没有报告更误导。
|
|
995
|
+
const reexportPaths = [];
|
|
996
|
+
for (const file of targets) {
|
|
997
|
+
if (!OVERRIDE_SOURCE_EXTENSIONS.has(extname(file))) continue;
|
|
998
|
+
const text = readFileSync(file, 'utf8');
|
|
999
|
+
if (!new RegExp(`export\\s*\\{[^{}]*\\}\\s*from\\s*["']${PACKAGE_NAME}["']`).test(text)) continue;
|
|
1000
|
+
const relative_ = toPosixPath(relative(consumerRootDir, file)).replace(/\/index\.(t|j)sx?$/, '').replace(/\.(t|j)sx?$/, '');
|
|
1001
|
+
reexportPaths.push(relative_, `@/${relative_.replace(/^src\//, '')}`, `~/${relative_.replace(/^src\//, '')}`);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const overrides = [];
|
|
1005
|
+
for (const text of sources) {
|
|
1006
|
+
const components = collectDesignSystemComponents(text, reexportPaths);
|
|
1007
|
+
if (components.size === 0) continue;
|
|
1008
|
+
overrides.push(...collectOverrideClassNames(text, components));
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
if (overrides.length === 0) {
|
|
1012
|
+
console.log('[q1-design] overrides: 未发现对设计系统组件的 className 覆写。');
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const buckets = { skin: 0, box: 0, other: 0, 'undefined-in-css': 0 };
|
|
1017
|
+
const perClass = new Map();
|
|
1018
|
+
for (const { component, className } of overrides) {
|
|
1019
|
+
const kind = classifyOverride(className, cssText);
|
|
1020
|
+
buckets[kind] += 1;
|
|
1021
|
+
const key = `${kind}|${component}|${className}`;
|
|
1022
|
+
perClass.set(key, (perClass.get(key) ?? 0) + 1);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
console.log(`[q1-design] overrides: 共 ${overrides.length} 处`);
|
|
1026
|
+
console.log(` 重画外观 (skin) ${buckets.skin}`);
|
|
1027
|
+
console.log(` 仅布局 (box) ${buckets.box}`);
|
|
1028
|
+
console.log(` 其它属性 ${buckets.other}`);
|
|
1029
|
+
console.log(` CSS 中未找到定义 ${buckets['undefined-in-css']}`);
|
|
1030
|
+
const top = [...perClass.entries()]
|
|
1031
|
+
.filter(([key]) => key.startsWith('skin|'))
|
|
1032
|
+
.sort((left, right) => right[1] - left[1])
|
|
1033
|
+
.slice(0, 10);
|
|
1034
|
+
if (top.length > 0) {
|
|
1035
|
+
console.log('\n重画外观的覆写 Top:');
|
|
1036
|
+
for (const [key, count] of top) {
|
|
1037
|
+
const [, component, className] = key.split('|');
|
|
1038
|
+
console.log(` ${String(count).padStart(3)}× <${component} className="${className}">`);
|
|
1039
|
+
}
|
|
1040
|
+
console.log('\n「重画外观」意味着组件缺一档能力,或调用点该换一档现有 variant。');
|
|
1041
|
+
console.log('「仅布局」是正当用法,组件本就不该管间距与排布。');
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
899
1045
|
function printUsage() {
|
|
900
|
-
console.error('Usage: q1-design <ls|doc|sync|verify|lint|eval>');
|
|
1046
|
+
console.error('Usage: q1-design <ls|doc|sync|verify|lint|eval|overrides>');
|
|
901
1047
|
console.error(' ls 列出随包发布的组件与 hook');
|
|
902
1048
|
console.error(' doc <组件名> 打印该组件的公共 API(函数签名 + 同名前缀的类型)');
|
|
903
1049
|
console.error(
|
|
@@ -907,6 +1053,9 @@ function printUsage() {
|
|
|
907
1053
|
' lint --diff <base-ref> [路径...] 只扫相对 base-ref 有改动的文件(存量重的仓库用它当增量门禁)',
|
|
908
1054
|
);
|
|
909
1055
|
console.error(' eval 对 tooling/scripts/evals 的 fixtures 跑 before/after 断言');
|
|
1056
|
+
console.error(
|
|
1057
|
+
' overrides [路径...] 统计对设计系统组件的 className 覆写,并按「重画外观 / 仅布局」分类',
|
|
1058
|
+
);
|
|
910
1059
|
}
|
|
911
1060
|
|
|
912
1061
|
// 直跑守卫(与 scripts/ci-path-filter.mjs 同款):被 import 时只暴露纯函数,不执行 CLI,
|
|
@@ -941,6 +1090,8 @@ if (isDirectRun) {
|
|
|
941
1090
|
runLint(args.length > 0 ? args : ['src'], consumerRootDir, diffBaseRef);
|
|
942
1091
|
} else if (subcommand === 'eval') {
|
|
943
1092
|
runEval();
|
|
1093
|
+
} else if (subcommand === 'overrides') {
|
|
1094
|
+
runOverrides(consumerRootDir, process.argv.slice(3));
|
|
944
1095
|
} else {
|
|
945
1096
|
printUsage();
|
|
946
1097
|
process.exit(1);
|
|
@@ -18,6 +18,16 @@ import { TextareaHTMLAttributes } from 'react';
|
|
|
18
18
|
|
|
19
19
|
export declare function addDays(date: Date, amount: number): Date;
|
|
20
20
|
|
|
21
|
+
export declare function AuthCard({ title, description, action, className }: AuthCardProps): JSX.Element;
|
|
22
|
+
|
|
23
|
+
/** Focused presentational card for account entry and connection flows. */
|
|
24
|
+
export declare interface AuthCardProps {
|
|
25
|
+
title: ReactNode;
|
|
26
|
+
description: ReactNode;
|
|
27
|
+
action?: ReactNode;
|
|
28
|
+
className?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
export declare function Avatar({ title, src, letter, glyph, placeholder, icon, iconColor, size, className }: AvatarProps): JSX.Element;
|
|
22
32
|
|
|
23
33
|
export declare function AvatarGroup({ members, label, size, limit, overlap, reverse, className, }: {
|
|
@@ -57,7 +67,7 @@ export declare type AvatarProps = {
|
|
|
57
67
|
className?: string;
|
|
58
68
|
};
|
|
59
69
|
|
|
60
|
-
declare type Axis = 'vertical' | 'horizontal' | 'free';
|
|
70
|
+
export declare type Axis = 'vertical' | 'horizontal' | 'free';
|
|
61
71
|
|
|
62
72
|
export declare function Badge({ tone, subtle, quiet, inverted, size, icon, count, title, className, children, ...rest }: BadgeProps): JSX.Element;
|
|
63
73
|
|
|
@@ -111,7 +121,7 @@ export declare type BannerProps = {
|
|
|
111
121
|
|
|
112
122
|
export declare type BannerTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
|
|
113
123
|
|
|
114
|
-
declare type BannerVariant = 'default' | 'success' | 'warning' | 'error';
|
|
124
|
+
export declare type BannerVariant = 'default' | 'success' | 'warning' | 'error';
|
|
115
125
|
|
|
116
126
|
export declare function Book({ title, variant, color, bandColor, textColor, icon, illustration, textured, width, className, style, ...rest }: BookProps): JSX.Element;
|
|
117
127
|
|
|
@@ -133,7 +143,7 @@ export declare type BookProps = {
|
|
|
133
143
|
/** 细密交叉纹理(自有配方);只提供单一纹理样式,未做多纹理体系(记 non-goal)。仅用于主视觉,成排书架勿用。 */
|
|
134
144
|
textured?: boolean;
|
|
135
145
|
/** 数字按 px;字符串原样透传(如 clamp(...) 做响应式宽度)。内部排版随宽度等比缩放。
|
|
136
|
-
*
|
|
146
|
+
* width 是唯一尺寸入口:用 style.width 改宽会与内部缩放脱节。 */
|
|
137
147
|
width?: number | string;
|
|
138
148
|
} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>;
|
|
139
149
|
|
|
@@ -143,7 +153,7 @@ export declare function Breadcrumb({ items, label, type, }: {
|
|
|
143
153
|
type?: 'text' | 'menu';
|
|
144
154
|
}): JSX.Element;
|
|
145
155
|
|
|
146
|
-
declare type BreadcrumbCrumb = {
|
|
156
|
+
export declare type BreadcrumbCrumb = {
|
|
147
157
|
label: string;
|
|
148
158
|
href?: string;
|
|
149
159
|
icon?: ReactNode;
|
|
@@ -202,6 +212,14 @@ declare type ButtonBaseProps = {
|
|
|
202
212
|
circle?: boolean;
|
|
203
213
|
/** 药丸形(full radius)+ 柔和抬起阴影,营销页/落地页文字 CTA 用。与 circle 互斥。 */
|
|
204
214
|
rounded?: boolean;
|
|
215
|
+
/**
|
|
216
|
+
* 双态按钮的「当前已按下/已选中」态:分段控件、筛选器、视图切换里那个当前项。
|
|
217
|
+
* 它表达状态,不是强调层级,因此与 variant 正交——任何 variant 都可以处于选中态。
|
|
218
|
+
*
|
|
219
|
+
* 同时置 `aria-pressed`。视觉与语义在这里必须同源:只画视觉会让读屏用户完全看不到
|
|
220
|
+
* 选中状态,而这正是消费方手写覆写时普遍漏掉的一半。
|
|
221
|
+
*/
|
|
222
|
+
selected?: boolean;
|
|
205
223
|
/** 载入态:禁点 + 保留可聚焦 + spinner 的 role=status 播报;不改变控件尺寸。 */
|
|
206
224
|
loading?: boolean;
|
|
207
225
|
/** loading 时 role=status 播报的文案,默认"加载中…";仅供屏幕阅读器,不影响可见文案。 */
|
|
@@ -215,9 +233,9 @@ declare type ButtonBaseProps = {
|
|
|
215
233
|
shortcut?: string;
|
|
216
234
|
className?: string;
|
|
217
235
|
children?: ReactNode;
|
|
218
|
-
/**
|
|
236
|
+
/** 文字前置图标槽;14–16px、currentColor,装饰性(自带 aria-hidden)。loading 时被 spinner 接管。 */
|
|
219
237
|
prefix?: ReactNode;
|
|
220
|
-
/**
|
|
238
|
+
/** 文字后置图标槽,同上。 */
|
|
221
239
|
suffix?: ReactNode;
|
|
222
240
|
'aria-label'?: string;
|
|
223
241
|
/**
|
|
@@ -248,7 +266,7 @@ export declare type ButtonLinkProps = Omit<ButtonBaseProps, 'type'> & {
|
|
|
248
266
|
ref?: Ref<HTMLAnchorElement>;
|
|
249
267
|
};
|
|
250
268
|
|
|
251
|
-
declare type ButtonProps = ButtonBaseProps & Omit<ComponentPropsWithoutRef<'button'>, keyof ButtonBaseProps> & {
|
|
269
|
+
export declare type ButtonProps = ButtonBaseProps & Omit<ComponentPropsWithoutRef<'button'>, keyof ButtonBaseProps> & {
|
|
252
270
|
[dataAttribute: `data-${string}`]: string | number | boolean | null | undefined;
|
|
253
271
|
/** 供 focus 管理、测量、浮层定位等场景使用。 */
|
|
254
272
|
ref?: Ref<HTMLButtonElement>;
|
|
@@ -306,14 +324,44 @@ declare type CardOwnProps = {
|
|
|
306
324
|
|
|
307
325
|
export declare type CardProps = CardAsLink | CardAsContainer;
|
|
308
326
|
|
|
309
|
-
export declare function
|
|
327
|
+
export declare function Chart({ data, summary, axisLabels, height, className }: ChartProps): JSX.Element;
|
|
328
|
+
|
|
329
|
+
export declare type ChartPoint = {
|
|
330
|
+
/** 该点的横轴标签(日期、类目名…)。用于轴标签展示。 */
|
|
331
|
+
label: string;
|
|
332
|
+
value: number;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export declare type ChartProps = {
|
|
336
|
+
data: readonly ChartPoint[];
|
|
337
|
+
/**
|
|
338
|
+
* 读屏摘要,必填。
|
|
339
|
+
*
|
|
340
|
+
* 折线本身对读屏是不可读的——没有摘要,这张图对辅助技术等于不存在。设为必填而不是
|
|
341
|
+
* 可选,是因为可选的可访问性参数在实践中等于不存在:赶工时第一个被省掉的就是它。
|
|
342
|
+
* 内容应当是这张图在说什么,例如「销售趋势,近 7 天,累计 1,024 单」。
|
|
343
|
+
*/
|
|
344
|
+
summary: string;
|
|
345
|
+
/** 轴标签:两端 / 两端加中点 / 不显示。轴标签对读屏隐藏——摘要里已经说过了。 */
|
|
346
|
+
axisLabels?: 'ends' | 'ends-and-middle' | false;
|
|
347
|
+
/** 绘图区高度(px),默认 64。宽度始终撑满容器。 */
|
|
348
|
+
height?: number;
|
|
349
|
+
className?: string;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
export declare function Checkbox({ ...props }: CheckboxProps): JSX.Element;
|
|
310
353
|
|
|
311
354
|
declare type CheckboxOnlyProps = {
|
|
312
355
|
/** 半选:视觉态而非第三个值,由父级逻辑驱动(如"部分子项已选")。AT 读作 mixed。 */
|
|
313
356
|
indeterminate?: boolean;
|
|
314
357
|
};
|
|
315
358
|
|
|
316
|
-
|
|
359
|
+
/** Checkbox 的完整 props。消费方包一层时用它标注,无需知道内部由哪几块组合而成。 */
|
|
360
|
+
export declare type CheckboxProps = ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & CheckboxOnlyProps & ControlOptionNativeInputProps;
|
|
361
|
+
|
|
362
|
+
export declare function Choicebox({ type, label, description, extra, className, ...rest }: ChoiceboxProps): JSX.Element;
|
|
363
|
+
|
|
364
|
+
declare type ChoiceboxNativeInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>;
|
|
317
365
|
|
|
318
366
|
declare type ChoiceboxOwnProps = {
|
|
319
367
|
type: 'radio' | 'checkbox';
|
|
@@ -324,9 +372,12 @@ declare type ChoiceboxOwnProps = {
|
|
|
324
372
|
extra?: ReactNode;
|
|
325
373
|
};
|
|
326
374
|
|
|
375
|
+
/** Choicebox 的完整 props。 */
|
|
376
|
+
export declare type ChoiceboxProps = ChoiceboxOwnProps & ChoiceboxNativeInputProps;
|
|
377
|
+
|
|
327
378
|
export declare function ClearableInput({ id, placeholder, defaultValue, disabled, kbdHint, onClear, ariaLabel, }: ClearableInputProps): JSX.Element;
|
|
328
379
|
|
|
329
|
-
declare type ClearableInputProps = {
|
|
380
|
+
export declare type ClearableInputProps = {
|
|
330
381
|
id: string;
|
|
331
382
|
placeholder?: string;
|
|
332
383
|
defaultValue?: string;
|
|
@@ -336,7 +387,7 @@ declare type ClearableInputProps = {
|
|
|
336
387
|
/** 清空(点击清除按钮或按 Esc)时回调,用于联动外部状态。 */
|
|
337
388
|
onClear?: () => void;
|
|
338
389
|
/** 无可见 label 时的可访问名。有可见 label(FormField 组合)时勿再传——aria-label 会覆盖
|
|
339
|
-
*
|
|
390
|
+
* 可见标签成为可访问名,视觉与播报分叉(WCAG 2.5.3 Label in Name)。 */
|
|
340
391
|
ariaLabel?: string;
|
|
341
392
|
};
|
|
342
393
|
|
|
@@ -362,7 +413,7 @@ export declare type CodeBlockLine = {
|
|
|
362
413
|
state?: 'highlight' | 'added' | 'removed';
|
|
363
414
|
};
|
|
364
415
|
|
|
365
|
-
declare type CodeBlockProps = CodeBlockBaseProps & ({
|
|
416
|
+
export declare type CodeBlockProps = CodeBlockBaseProps & ({
|
|
366
417
|
filename?: string;
|
|
367
418
|
lines: CodeBlockLine[];
|
|
368
419
|
tabs?: undefined;
|
|
@@ -425,7 +476,7 @@ export declare type ComboboxOption = {
|
|
|
425
476
|
suffix?: ReactNode;
|
|
426
477
|
};
|
|
427
478
|
|
|
428
|
-
declare type ComboboxProps = {
|
|
479
|
+
export declare type ComboboxProps = {
|
|
429
480
|
/** 唯一 shape,后端字段在 adapter 层转换。 */
|
|
430
481
|
options: ComboboxOption[];
|
|
431
482
|
/** 受控选中值;'' = 未选。 */
|
|
@@ -479,7 +530,7 @@ export declare type CommandMenuItem = {
|
|
|
479
530
|
suffix?: ReactNode;
|
|
480
531
|
};
|
|
481
532
|
|
|
482
|
-
declare type CommandMenuProps = {
|
|
533
|
+
export declare type CommandMenuProps = {
|
|
483
534
|
sections: CommandMenuSection[];
|
|
484
535
|
triggerLabel?: string;
|
|
485
536
|
/** 绑定全局 ⌘K(macOS)/ Ctrl+K 打开。 */
|
|
@@ -498,7 +549,7 @@ export declare type ContextCardAlign = 'start' | 'center' | 'end';
|
|
|
498
549
|
|
|
499
550
|
export declare type ContextCardPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
500
551
|
|
|
501
|
-
declare type ContextCardProps = {
|
|
552
|
+
export declare type ContextCardProps = {
|
|
502
553
|
name: string;
|
|
503
554
|
identity: string;
|
|
504
555
|
rows: [string, string][];
|
|
@@ -602,9 +653,9 @@ export declare type ContextSwitcherSearch = {
|
|
|
602
653
|
noResults: ContextSwitcherEmptyState;
|
|
603
654
|
};
|
|
604
655
|
|
|
605
|
-
export declare function ControlOption({ id, label, description, error, indeterminate, ref, type, ...rest }:
|
|
606
|
-
|
|
607
|
-
|
|
656
|
+
export declare function ControlOption({ id, label, description, error, indeterminate, ref, type, ...rest }: ControlOptionProps): JSX.Element;
|
|
657
|
+
|
|
658
|
+
declare type ControlOptionNativeInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'id' | 'type'>;
|
|
608
659
|
|
|
609
660
|
declare type ControlOptionOwnProps = {
|
|
610
661
|
/** 缺省走 useId;显式传入时行为同现状(specimen/测试可寻址)。 */
|
|
@@ -613,6 +664,11 @@ declare type ControlOptionOwnProps = {
|
|
|
613
664
|
ref?: Ref<HTMLInputElement>;
|
|
614
665
|
};
|
|
615
666
|
|
|
667
|
+
/** ControlOption 的完整 props(Checkbox / Radio 的共同底座,须显式给 type)。 */
|
|
668
|
+
export declare type ControlOptionProps = ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & CheckboxOnlyProps & {
|
|
669
|
+
type: 'checkbox' | 'radio';
|
|
670
|
+
} & ControlOptionNativeInputProps;
|
|
671
|
+
|
|
616
672
|
export declare function CopyButton({ value, label, successMessage, size, className, }: {
|
|
617
673
|
value: string;
|
|
618
674
|
label: string;
|
|
@@ -778,9 +834,9 @@ export declare function Gauge({ value, size, showValue, tone, toneDirection, lab
|
|
|
778
834
|
indeterminate?: boolean;
|
|
779
835
|
}): JSX.Element;
|
|
780
836
|
|
|
781
|
-
declare type GaugeSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
837
|
+
export declare type GaugeSize = 'xs' | 'sm' | 'md' | 'lg';
|
|
782
838
|
|
|
783
|
-
declare type GaugeToneDirection = 'quota' | 'health';
|
|
839
|
+
export declare type GaugeToneDirection = 'quota' | 'health';
|
|
784
840
|
|
|
785
841
|
export declare function IconButton({ 'aria-label': canonicalAriaLabel, ariaLabel: legacyAriaLabel, ...rest }: IconButtonProps): JSX.Element;
|
|
786
842
|
|
|
@@ -817,7 +873,7 @@ export declare type IconButtonProps = IconButtonAccessibleName & IconButtonBaseP
|
|
|
817
873
|
ref?: Ref<HTMLButtonElement>;
|
|
818
874
|
};
|
|
819
875
|
|
|
820
|
-
export declare function Input({ id, label, description, error, invalid, errorId, ref, prefix, suffix, prefixStyling, suffixStyling, rounded, wide, size, className, ...rest }: {
|
|
876
|
+
export declare function Input({ id, label, description, error, invalid, errorId, ref, prefix, suffix, prefixStyling, suffixStyling, rounded, wide, size, className, revealable, revealed, onRevealedChange, revealLabel, hideLabel, ...rest }: {
|
|
821
877
|
/** 可选:未传时内部 useId 兜底(对齐 Combobox);显式传入时行为同 0.3.0。 */
|
|
822
878
|
id?: string;
|
|
823
879
|
/** 置 aria-invalid 的业务校验态,不渲染文案;与壳模式 error 并轨(invalid || Boolean(error))。 */
|
|
@@ -832,6 +888,24 @@ export declare function Input({ id, label, description, error, invalid, errorId,
|
|
|
832
888
|
prefixStyling?: boolean;
|
|
833
889
|
/** 置 false 去掉后缀的灰块底色与分隔线。 */
|
|
834
890
|
suffixStyling?: boolean;
|
|
891
|
+
/**
|
|
892
|
+
* 遮罩输入:渲染 type="password",并在后缀槽给出显隐切换。
|
|
893
|
+
*
|
|
894
|
+
* 名字说的是能力(可揭示)而不是数据分类——后者由 type="password" 表达。
|
|
895
|
+
*
|
|
896
|
+
* 存在的理由不是「少写几行」,而是三个每次手写都要重新答对的细节:切换按钮的可访问名
|
|
897
|
+
* 必须随状态变化(否则读屏用户不知道当前是遮罩还是明文);它必须 type="button"
|
|
898
|
+
* (否则在表单里会触发提交);图标不该在每个消费方各画一遍。
|
|
899
|
+
*/
|
|
900
|
+
revealable?: boolean;
|
|
901
|
+
/** 受控显隐。不传则组件自持状态。 */
|
|
902
|
+
revealed?: boolean;
|
|
903
|
+
/** 受控显隐的变更回调;非受控时同样触发,供业务侧埋点。 */
|
|
904
|
+
onRevealedChange?: (revealed: boolean) => void;
|
|
905
|
+
/** 遮罩态下切换按钮的可访问名,默认「显示内容」。密码场景建议传「显示密码」。 */
|
|
906
|
+
revealLabel?: string;
|
|
907
|
+
/** 明文态下切换按钮的可访问名,默认「隐藏内容」。 */
|
|
908
|
+
hideLabel?: string;
|
|
835
909
|
/** 药丸形全圆角外框(9999px 级 pill 半径)。 */
|
|
836
910
|
rounded?: boolean;
|
|
837
911
|
wide?: boolean;
|
|
@@ -850,7 +924,7 @@ export declare type JsonViewProps = {
|
|
|
850
924
|
/** 初始折叠深度:0 表示只展开根,1 表示展开一层;缺省全部展开。 */
|
|
851
925
|
collapsedDepth?: number;
|
|
852
926
|
/** 需要强调的键。`a.b` 形式按完整路径精确匹配;裸键名(如 `status`)在任意深度命中,
|
|
853
|
-
*
|
|
927
|
+
* 用于「把所有 status 字段都标出来」这类排查。两种写法可混用。 */
|
|
854
928
|
highlight?: string[];
|
|
855
929
|
/** 嵌入模式:去掉外框、背景与内边距,由宿主容器决定表面。 */
|
|
856
930
|
embedded?: boolean;
|
|
@@ -886,12 +960,31 @@ export declare function Label({ htmlFor, required, children, }: {
|
|
|
886
960
|
children: string;
|
|
887
961
|
}): JSX.Element;
|
|
888
962
|
|
|
963
|
+
export declare function Link({ href, tone, external, className, children, ref, ...rest }: LinkProps): JSX.Element;
|
|
964
|
+
|
|
965
|
+
export declare type LinkProps = {
|
|
966
|
+
/** 目标地址。省略时渲染为不可点的当前项文本(例:面包屑末节、已停用的外链)。 */
|
|
967
|
+
href?: string;
|
|
968
|
+
tone?: LinkTone;
|
|
969
|
+
/**
|
|
970
|
+
* 站外链接:追加一枚外链图标,并补 rel="noopener noreferrer"。
|
|
971
|
+
*
|
|
972
|
+
* 图标不是装饰——它是「点下去会离开当前上下文」的预告。省掉它,用户要到新标签页
|
|
973
|
+
* 打开之后才知道自己被带走了。
|
|
974
|
+
*/
|
|
975
|
+
external?: boolean;
|
|
976
|
+
ref?: Ref<HTMLAnchorElement>;
|
|
977
|
+
children?: ReactNode;
|
|
978
|
+
} & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>;
|
|
979
|
+
|
|
980
|
+
export declare type LinkTone = 'default' | 'muted';
|
|
981
|
+
|
|
889
982
|
export declare function LoadingDots({ label, size }: {
|
|
890
983
|
label?: string;
|
|
891
984
|
size?: LoadingDotsSize;
|
|
892
985
|
}): JSX.Element;
|
|
893
986
|
|
|
894
|
-
declare type LoadingDotsSize = 'sm' | 'md' | 'lg' | number;
|
|
987
|
+
export declare type LoadingDotsSize = 'sm' | 'md' | 'lg' | number;
|
|
895
988
|
|
|
896
989
|
export declare function LoadMoreButton({ loading, noGap, noBorderRadius, onClick, children, }: {
|
|
897
990
|
loading?: boolean;
|
|
@@ -986,10 +1079,6 @@ export declare function MultiSelect({ options, defaultSelected, placeholder, onC
|
|
|
986
1079
|
onChange?: (next: string[]) => void;
|
|
987
1080
|
}): JSX.Element;
|
|
988
1081
|
|
|
989
|
-
declare type NativeInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'id' | 'type'>;
|
|
990
|
-
|
|
991
|
-
declare type NativeInputProps_2 = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>;
|
|
992
|
-
|
|
993
1082
|
export declare function Navigation({ className, orientation, ...props }: NavigationProps): JSX.Element;
|
|
994
1083
|
|
|
995
1084
|
export declare function NavigationGroup({ label, icon, expanded, defaultExpanded, onExpandedChange, children, }: NavigationGroupProps): JSX.Element;
|
|
@@ -1069,7 +1158,7 @@ export declare function Note({ tone, size, filled, disabled, label, action, chil
|
|
|
1069
1158
|
children: ReactNode;
|
|
1070
1159
|
}): JSX.Element;
|
|
1071
1160
|
|
|
1072
|
-
declare type NoteSize = 'small' | 'default' | 'large';
|
|
1161
|
+
export declare type NoteSize = 'small' | 'default' | 'large';
|
|
1073
1162
|
|
|
1074
1163
|
export declare type NoteTone = 'info' | 'success' | 'warning' | 'error' | 'secondary';
|
|
1075
1164
|
|
|
@@ -1131,7 +1220,7 @@ export declare function Progress({ value, max, tone, slim, stops, label, }: {
|
|
|
1131
1220
|
label: string;
|
|
1132
1221
|
}): JSX.Element;
|
|
1133
1222
|
|
|
1134
|
-
declare type ProgressStop = number | {
|
|
1223
|
+
export declare type ProgressStop = number | {
|
|
1135
1224
|
at: number;
|
|
1136
1225
|
label?: string;
|
|
1137
1226
|
};
|
|
@@ -1142,7 +1231,10 @@ export declare function ProjectBanner({ variant, message, cta }: {
|
|
|
1142
1231
|
cta: string;
|
|
1143
1232
|
}): JSX.Element;
|
|
1144
1233
|
|
|
1145
|
-
export declare function Radio({ ...props }:
|
|
1234
|
+
export declare function Radio({ ...props }: RadioProps): JSX.Element;
|
|
1235
|
+
|
|
1236
|
+
/** Radio 的完整 props。 */
|
|
1237
|
+
export declare type RadioProps = ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & ControlOptionNativeInputProps;
|
|
1146
1238
|
|
|
1147
1239
|
export declare function RangeField({ layout, trigger, size, minValue, maxValue, pinnedTimezone, today, value, onChange, }: {
|
|
1148
1240
|
layout?: PopoverLayout;
|
|
@@ -1340,7 +1432,7 @@ export declare function Snippet({ text, prompt, inverted, tone, copyText, onCopy
|
|
|
1340
1432
|
onCopy?: () => void;
|
|
1341
1433
|
}): JSX.Element;
|
|
1342
1434
|
|
|
1343
|
-
declare type SnippetTone = 'info' | 'danger' | 'warning';
|
|
1435
|
+
export declare type SnippetTone = 'info' | 'danger' | 'warning';
|
|
1344
1436
|
|
|
1345
1437
|
export declare function Spinner({ size, tone, label, }: {
|
|
1346
1438
|
size?: SpinnerSize;
|
|
@@ -1490,7 +1582,10 @@ export declare function Tabs({ tabs, ariaLabel, variant, }: {
|
|
|
1490
1582
|
variant?: 'default' | 'secondary';
|
|
1491
1583
|
}): JSX.Element | null;
|
|
1492
1584
|
|
|
1493
|
-
export declare function Textarea({ id, label, description, error, invalid, errorId, ref, wide, size, className, maxLength, onInput, ...rest }:
|
|
1585
|
+
export declare function Textarea({ id, label, description, error, invalid, errorId, ref, wide, size, className, maxLength, onInput, ...rest }: TextareaProps): JSX.Element;
|
|
1586
|
+
|
|
1587
|
+
/** Textarea 的完整 props。消费方包一层时用它标注,无需知道内部由哪几块组合而成。 */
|
|
1588
|
+
export declare type TextareaProps = {
|
|
1494
1589
|
/** 缺省走 useId;显式传入时行为同现状(specimen/测试可寻址)。 */
|
|
1495
1590
|
id?: string;
|
|
1496
1591
|
/** 业务校验模型(主):置 aria-invalid;文案由业务层经 errorId 关联,组件不渲染。 */
|
|
@@ -1501,7 +1596,7 @@ export declare function Textarea({ id, label, description, error, invalid, error
|
|
|
1501
1596
|
ref?: Ref<HTMLTextAreaElement>;
|
|
1502
1597
|
wide?: boolean;
|
|
1503
1598
|
size?: FieldSize;
|
|
1504
|
-
} & (FieldShellProps | FieldBareProps) & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'id'
|
|
1599
|
+
} & (FieldShellProps | FieldBareProps) & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'id'>;
|
|
1505
1600
|
|
|
1506
1601
|
export declare function TextWithCopyButton({ text, copyValue, successMessage, size, }: {
|
|
1507
1602
|
text: string;
|
|
@@ -1515,7 +1610,7 @@ export declare function ThemeSwitcher({ small, disabled }: {
|
|
|
1515
1610
|
disabled?: boolean;
|
|
1516
1611
|
}): JSX.Element;
|
|
1517
1612
|
|
|
1518
|
-
declare type ThresholdTone = 'primary' | 'success' | 'warning' | 'danger';
|
|
1613
|
+
export declare type ThresholdTone = 'primary' | 'success' | 'warning' | 'danger';
|
|
1519
1614
|
|
|
1520
1615
|
declare type ToastContextValue = {
|
|
1521
1616
|
push: (toast: ToastOptions) => void;
|
|
@@ -1559,6 +1654,24 @@ export declare function Toggle({ checked, defaultOn, onChange, disabled, size, o
|
|
|
1559
1654
|
|
|
1560
1655
|
export declare type ToggleSize = 'sm' | 'md' | 'lg';
|
|
1561
1656
|
|
|
1657
|
+
export declare function Toolbar({ 'aria-label': ariaLabel, orientation, children, className, }: ToolbarProps): JSX.Element;
|
|
1658
|
+
|
|
1659
|
+
export declare type ToolbarOrientation = 'horizontal' | 'vertical';
|
|
1660
|
+
|
|
1661
|
+
export declare type ToolbarProps = {
|
|
1662
|
+
/**
|
|
1663
|
+
* 可访问名,必填。
|
|
1664
|
+
*
|
|
1665
|
+
* 一个没有名字的工具栏在读屏的地标/控件列表里是「toolbar」四个字,用户无从判断
|
|
1666
|
+
* 这是哪一组操作。页面上出现第二个工具栏时,两者完全无法区分。
|
|
1667
|
+
*/
|
|
1668
|
+
'aria-label': string;
|
|
1669
|
+
/** 方向决定哪组方向键生效。横向不接管上下键——那会夺走页面滚动。 */
|
|
1670
|
+
orientation?: ToolbarOrientation;
|
|
1671
|
+
children?: ReactNode;
|
|
1672
|
+
className?: string;
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1562
1675
|
export declare function Tooltip({ content, placement, delay, triggerLabel, children, align, tip, center, }: {
|
|
1563
1676
|
content: ReactNode;
|
|
1564
1677
|
placement?: TooltipPlacement;
|
|
@@ -1575,11 +1688,12 @@ export declare function Tooltip({ content, placement, delay, triggerLabel, child
|
|
|
1575
1688
|
center?: boolean;
|
|
1576
1689
|
}): JSX.Element;
|
|
1577
1690
|
|
|
1578
|
-
declare type TooltipAlign = 'start' | 'center' | 'end';
|
|
1691
|
+
export declare type TooltipAlign = 'start' | 'center' | 'end';
|
|
1579
1692
|
|
|
1580
1693
|
export declare type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
1581
1694
|
|
|
1582
|
-
|
|
1695
|
+
/** Tooltip 会向 children 注入的 props:消费方自定义 trigger 元素时用它约束自己的组件签名。 */
|
|
1696
|
+
export declare type TooltipTriggerProps = {
|
|
1583
1697
|
'aria-describedby'?: string;
|
|
1584
1698
|
onFocus?: (event: never) => void;
|
|
1585
1699
|
onBlur?: (event: never) => void;
|
|
@@ -1603,12 +1717,15 @@ export declare function useCopy(): {
|
|
|
1603
1717
|
reset: () => void;
|
|
1604
1718
|
};
|
|
1605
1719
|
|
|
1606
|
-
export declare function useRadio({ id, ...rest }: {
|
|
1607
|
-
id: string;
|
|
1608
|
-
} & NativeInputProps): {
|
|
1720
|
+
export declare function useRadio({ id, ...rest }: UseRadioOptions): {
|
|
1609
1721
|
component: JSX.Element;
|
|
1610
1722
|
};
|
|
1611
1723
|
|
|
1724
|
+
/** useRadio 的入参。 */
|
|
1725
|
+
export declare type UseRadioOptions = {
|
|
1726
|
+
id: string;
|
|
1727
|
+
} & ControlOptionNativeInputProps;
|
|
1728
|
+
|
|
1612
1729
|
export declare function useToast(): ToastContextValue;
|
|
1613
1730
|
|
|
1614
1731
|
export declare function Video({ src, poster, tracks, label, controls, loop, }: VideoProps): JSX.Element;
|