@godxjp/ui 20.2.1 → 21.0.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/dist/components/data-display/chat-bubble.d.ts +30 -0
- package/dist/components/data-display/chat-bubble.js +229 -0
- package/dist/components/data-display/descriptions.js +4 -1
- package/dist/components/data-display/index.d.ts +4 -2
- package/dist/components/data-display/index.js +5 -2
- package/dist/components/data-display/popover.js +3 -2
- package/dist/components/data-display/scroll-area.js +26 -1
- package/dist/components/data-display/tree.d.ts +8 -0
- package/dist/components/data-display/tree.js +426 -0
- package/dist/components/data-entry/chat-composer.d.ts +50 -0
- package/dist/components/data-entry/chat-composer.js +163 -0
- package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
- package/dist/components/data-entry/chat-suggestion.js +285 -0
- package/dist/components/data-entry/index.d.ts +4 -0
- package/dist/components/data-entry/index.js +4 -0
- package/dist/components/data-entry/textarea.js +3 -1
- package/dist/components/data-entry/tree-utils.d.ts +10 -48
- package/dist/components/data-entry/tree-utils.js +1 -154
- package/dist/i18n/messages/en.json +45 -0
- package/dist/i18n/messages/ja.json +43 -0
- package/dist/i18n/messages/vi.json +44 -0
- package/dist/lib/tree.d.ts +53 -0
- package/dist/lib/tree.js +155 -0
- package/dist/props/components/data-display.prop.d.ts +187 -1
- package/dist/props/components/data-entry.prop.d.ts +127 -0
- package/dist/props/registry.d.ts +140 -3
- package/dist/props/registry.js +202 -3
- package/dist/styles/control.css +7 -0
- package/dist/styles/data-display-layout.css +309 -55
- package/dist/styles/data-entry-layout.css +64 -0
- package/dist/styles/shell-layout.css +2 -1
- package/dist/tokens/base.css +3 -0
- package/dist/tokens/components/chat-bubble.css +36 -0
- package/dist/tokens/components/chat-composer.css +19 -0
- package/dist/tokens/components/data-display.css +0 -6
- package/dist/tokens/components/descriptions.css +4 -0
- package/dist/tokens/components/shell.css +1 -0
- package/dist/tokens/components/tree.css +27 -0
- package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
- package/docs/FRAME-COVERAGE-REPORT.md +8 -3
- package/docs/data-display/chat-bubble.tsx +397 -0
- package/docs/data-display/timeline.tsx +46 -0
- package/docs/data-display/tree.tsx +394 -0
- package/docs/data-entry/chat-composer.tsx +464 -0
- package/docs/data-entry/chat-suggestion.tsx +301 -0
- package/docs/roadmap/ai-chat-components.md +207 -0
- package/docs/roadmap/antd-parity.md +154 -0
- package/docs/roadmap/badge-tag-chip-count.md +172 -0
- package/docs/roadmap/list-masonry.md +159 -0
- package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
- package/docs/roadmap/parity-audit-data-entry.md +344 -0
- package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
- package/docs/roadmap/parity-backlog.md +79 -0
- package/docs/roadmap/tree-components.md +151 -0
- package/docs/showcase/table-tree-rows.tsx +4 -4
- package/package.json +5 -3
- package/dist/components/data-display/tree-list.d.ts +0 -13
- package/dist/components/data-display/tree-list.js +0 -26
- package/docs/data-display/tree-list.tsx +0 -107
|
@@ -159,6 +159,22 @@
|
|
|
159
159
|
"error": "Không hợp lệ",
|
|
160
160
|
"validating": "Đang kiểm tra",
|
|
161
161
|
"submitFailed": "Không thể gửi biểu mẫu. Vui lòng thử lại."
|
|
162
|
+
},
|
|
163
|
+
"chatComposer": {
|
|
164
|
+
"label": "Tin nhắn",
|
|
165
|
+
"placeholder": "Nhập tin nhắn",
|
|
166
|
+
"send": "Gửi tin nhắn",
|
|
167
|
+
"cancel": "Dừng tạo câu trả lời",
|
|
168
|
+
"hintEnter": "Enter để gửi · Shift + Enter để xuống dòng",
|
|
169
|
+
"hintShiftEnter": "Shift + Enter để gửi · Enter để xuống dòng"
|
|
170
|
+
},
|
|
171
|
+
"chatSuggestion": {
|
|
172
|
+
"label": "Gợi ý",
|
|
173
|
+
"empty": "Không có gợi ý phù hợp",
|
|
174
|
+
"count": {
|
|
175
|
+
"one": "{count} gợi ý",
|
|
176
|
+
"other": "{count} gợi ý"
|
|
177
|
+
}
|
|
162
178
|
}
|
|
163
179
|
},
|
|
164
180
|
"feedback": {
|
|
@@ -283,6 +299,13 @@
|
|
|
283
299
|
"progress": {
|
|
284
300
|
"ariaLabel": "Tiến độ",
|
|
285
301
|
"breakdownSeparator": ", "
|
|
302
|
+
},
|
|
303
|
+
"tree": {
|
|
304
|
+
"expand": "Mở rộng",
|
|
305
|
+
"collapse": "Thu gọn",
|
|
306
|
+
"loading": "Đang tải nút con…",
|
|
307
|
+
"empty": "Không có gì để hiển thị",
|
|
308
|
+
"selected": "Đã chọn"
|
|
286
309
|
}
|
|
287
310
|
},
|
|
288
311
|
"ui": {
|
|
@@ -524,5 +547,26 @@
|
|
|
524
547
|
"textExamples": {
|
|
525
548
|
"preservedText": "Văn bản thuần",
|
|
526
549
|
"sampleText": "Dòng đầu\n Dòng thụt vào\n\nDòng cuối"
|
|
550
|
+
},
|
|
551
|
+
"chat": {
|
|
552
|
+
"bubble": {
|
|
553
|
+
"typing": "Đang nhập…",
|
|
554
|
+
"loading": "Đang tải tin nhắn",
|
|
555
|
+
"you": "Bạn",
|
|
556
|
+
"assistant": "Trợ lý",
|
|
557
|
+
"tone": {
|
|
558
|
+
"info": "Thông tin",
|
|
559
|
+
"success": "Thành công",
|
|
560
|
+
"warning": "Cảnh báo",
|
|
561
|
+
"destructive": "Lỗi"
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
"list": {
|
|
565
|
+
"label": "Cuộc trò chuyện",
|
|
566
|
+
"jumpToLatest": "Đến tin mới nhất",
|
|
567
|
+
"newMessages": {
|
|
568
|
+
"other": "{count} tin nhắn mới"
|
|
569
|
+
}
|
|
570
|
+
}
|
|
527
571
|
}
|
|
528
572
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
/** Normalized tree node — the conventional `treeData` / Cascader `options` shape. */
|
|
3
|
+
export type TreeOption = {
|
|
4
|
+
value: string;
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
disableCheckbox?: boolean;
|
|
8
|
+
/** When false with `loadData`, shows expand affordance */
|
|
9
|
+
isLeaf?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Per-node glyph, drawn by `Tree` when `showIcon` is on. Carried through the normalizer so the
|
|
12
|
+
* page tree and the dropdown tree keep ONE node shape; `TreeSelect` simply never reads it.
|
|
13
|
+
*/
|
|
14
|
+
icon?: React.ReactNode;
|
|
15
|
+
children?: TreeOption[];
|
|
16
|
+
};
|
|
17
|
+
export type TreeFieldNames = {
|
|
18
|
+
label?: string;
|
|
19
|
+
value?: string;
|
|
20
|
+
children?: string;
|
|
21
|
+
};
|
|
22
|
+
export type NormalizedTreeOption = TreeOption & {
|
|
23
|
+
children?: NormalizedTreeOption[];
|
|
24
|
+
};
|
|
25
|
+
type RawTreeNode = Record<string, unknown>;
|
|
26
|
+
export declare function reactNodeText(value: React.ReactNode): string;
|
|
27
|
+
export declare function normalizeTreeOptions(nodes: RawTreeNode[] | undefined, fieldNames?: TreeFieldNames): NormalizedTreeOption[];
|
|
28
|
+
export declare function getNodeByPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
|
|
29
|
+
export declare function getOptionsAtPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
|
|
30
|
+
export declare function formatPathLabels(chain: NormalizedTreeOption[], separator?: string): string;
|
|
31
|
+
export type TreePath = {
|
|
32
|
+
path: string[];
|
|
33
|
+
labels: string[];
|
|
34
|
+
};
|
|
35
|
+
export declare function collectLeafPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
|
|
36
|
+
export declare function collectAllPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
|
|
37
|
+
export declare function pathKey(path: string[]): string;
|
|
38
|
+
export declare function pathsEqual(a: string[], b: string[]): boolean;
|
|
39
|
+
export declare function filterTreeOptions(options: NormalizedTreeOption[], query: string, filter?: (query: string, path: NormalizedTreeOption[]) => boolean): TreePath[];
|
|
40
|
+
export declare function getDescendantValues(node: NormalizedTreeOption): string[];
|
|
41
|
+
export declare function flattenVisibleTree(options: NormalizedTreeOption[], expandedKeys: Set<string>, depth?: number): {
|
|
42
|
+
node: NormalizedTreeOption;
|
|
43
|
+
depth: number;
|
|
44
|
+
hasChildren: boolean;
|
|
45
|
+
}[];
|
|
46
|
+
export declare function filterVisibleTree(options: NormalizedTreeOption[], query: string): {
|
|
47
|
+
node: NormalizedTreeOption;
|
|
48
|
+
depth: number;
|
|
49
|
+
hasChildren: boolean;
|
|
50
|
+
}[];
|
|
51
|
+
export declare function collectAllExpandableKeys(options: NormalizedTreeOption[]): string[];
|
|
52
|
+
export declare function findNodeByValue(options: NormalizedTreeOption[], value: string): NormalizedTreeOption | undefined;
|
|
53
|
+
export {};
|
package/dist/lib/tree.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
function reactNodeText(value) {
|
|
2
|
+
if (value == null || typeof value === "boolean") return "";
|
|
3
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
|
|
4
|
+
return String(value);
|
|
5
|
+
}
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return value.map((item) => reactNodeText(item)).join("");
|
|
8
|
+
}
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
function unknownText(value) {
|
|
12
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
|
|
13
|
+
return String(value);
|
|
14
|
+
}
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
function normalizeTreeOptions(nodes, fieldNames) {
|
|
18
|
+
if (!nodes?.length) return [];
|
|
19
|
+
const labelKey = fieldNames?.label ?? "label";
|
|
20
|
+
const valueKey = fieldNames?.value ?? "value";
|
|
21
|
+
const childrenKey = fieldNames?.children ?? "children";
|
|
22
|
+
return nodes.map((node) => {
|
|
23
|
+
const children = node[childrenKey];
|
|
24
|
+
const value = unknownText(node[valueKey]);
|
|
25
|
+
const label = node[labelKey];
|
|
26
|
+
return {
|
|
27
|
+
value,
|
|
28
|
+
label: label ?? value,
|
|
29
|
+
disabled: Boolean(node.disabled),
|
|
30
|
+
disableCheckbox: Boolean(node.disableCheckbox),
|
|
31
|
+
isLeaf: node.isLeaf,
|
|
32
|
+
icon: node.icon,
|
|
33
|
+
children: Array.isArray(children) ? normalizeTreeOptions(children, fieldNames) : void 0
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function getNodeByPath(options, path) {
|
|
38
|
+
const chain = [];
|
|
39
|
+
let level = options;
|
|
40
|
+
for (const segment of path) {
|
|
41
|
+
const found = level.find((n) => n.value === segment);
|
|
42
|
+
if (!found) break;
|
|
43
|
+
chain.push(found);
|
|
44
|
+
level = found.children ?? [];
|
|
45
|
+
}
|
|
46
|
+
return chain;
|
|
47
|
+
}
|
|
48
|
+
function getOptionsAtPath(options, path) {
|
|
49
|
+
if (!path.length) return options;
|
|
50
|
+
const chain = getNodeByPath(options, path);
|
|
51
|
+
return chain.at(-1)?.children ?? [];
|
|
52
|
+
}
|
|
53
|
+
function formatPathLabels(chain, separator = " / ") {
|
|
54
|
+
return chain.map((n) => reactNodeText(n.label)).join(separator);
|
|
55
|
+
}
|
|
56
|
+
function collectLeafPaths(options, prefix = [], root = options) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const node of options) {
|
|
59
|
+
const path = [...prefix, node.value];
|
|
60
|
+
const hasChildren = (node.children?.length ?? 0) > 0;
|
|
61
|
+
if (!hasChildren || node.isLeaf === true) {
|
|
62
|
+
out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
|
|
63
|
+
}
|
|
64
|
+
if (hasChildren) out.push(...collectLeafPaths(node.children, path, root));
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
function collectAllPaths(options, prefix = [], root = options) {
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const node of options) {
|
|
71
|
+
const path = [...prefix, node.value];
|
|
72
|
+
out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
|
|
73
|
+
if (node.children?.length) out.push(...collectAllPaths(node.children, path, root));
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function pathKey(path) {
|
|
78
|
+
return path.join("\0");
|
|
79
|
+
}
|
|
80
|
+
function pathsEqual(a, b) {
|
|
81
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
82
|
+
}
|
|
83
|
+
function filterTreeOptions(options, query, filter) {
|
|
84
|
+
const q = query.trim().toLowerCase();
|
|
85
|
+
if (!q) return [];
|
|
86
|
+
const paths = collectLeafPaths(options);
|
|
87
|
+
return paths.filter(({ path }) => {
|
|
88
|
+
const chain = getNodeByPath(options, path);
|
|
89
|
+
if (filter) return filter(query, chain);
|
|
90
|
+
return chain.some((n) => reactNodeText(n.label).toLowerCase().includes(q));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function getDescendantValues(node) {
|
|
94
|
+
const values = [node.value];
|
|
95
|
+
for (const child of node.children ?? []) values.push(...getDescendantValues(child));
|
|
96
|
+
return values;
|
|
97
|
+
}
|
|
98
|
+
function flattenVisibleTree(options, expandedKeys, depth = 0) {
|
|
99
|
+
const out = [];
|
|
100
|
+
for (const node of options) {
|
|
101
|
+
const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
|
|
102
|
+
out.push({ node, depth, hasChildren });
|
|
103
|
+
if (hasChildren && expandedKeys.has(node.value)) {
|
|
104
|
+
out.push(...flattenVisibleTree(node.children, expandedKeys, depth + 1));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
function filterVisibleTree(options, query) {
|
|
110
|
+
const q = query.trim().toLowerCase();
|
|
111
|
+
if (!q) return flattenVisibleTree(options, new Set(collectAllExpandableKeys(options)));
|
|
112
|
+
function matches(nodes, depth) {
|
|
113
|
+
return nodes.flatMap((node) => {
|
|
114
|
+
const children = node.isLeaf ? [] : matches(node.children ?? [], depth + 1);
|
|
115
|
+
if (!reactNodeText(node.label).toLowerCase().includes(q) && children.length === 0) return [];
|
|
116
|
+
return [{ node, depth, hasChildren: children.length > 0 }, ...children];
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return matches(options, 0);
|
|
120
|
+
}
|
|
121
|
+
function collectAllExpandableKeys(options) {
|
|
122
|
+
const keys = [];
|
|
123
|
+
for (const node of options) {
|
|
124
|
+
if ((node.children?.length ?? 0) > 0 && node.isLeaf !== true) {
|
|
125
|
+
keys.push(node.value);
|
|
126
|
+
keys.push(...collectAllExpandableKeys(node.children));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return keys;
|
|
130
|
+
}
|
|
131
|
+
function findNodeByValue(options, value) {
|
|
132
|
+
for (const node of options) {
|
|
133
|
+
if (node.value === value) return node;
|
|
134
|
+
const nested = node.children ? findNodeByValue(node.children, value) : void 0;
|
|
135
|
+
if (nested) return nested;
|
|
136
|
+
}
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
collectAllExpandableKeys,
|
|
141
|
+
collectAllPaths,
|
|
142
|
+
collectLeafPaths,
|
|
143
|
+
filterTreeOptions,
|
|
144
|
+
filterVisibleTree,
|
|
145
|
+
findNodeByValue,
|
|
146
|
+
flattenVisibleTree,
|
|
147
|
+
formatPathLabels,
|
|
148
|
+
getDescendantValues,
|
|
149
|
+
getNodeByPath,
|
|
150
|
+
getOptionsAtPath,
|
|
151
|
+
normalizeTreeOptions,
|
|
152
|
+
pathKey,
|
|
153
|
+
pathsEqual,
|
|
154
|
+
reactNodeText
|
|
155
|
+
};
|
|
@@ -28,7 +28,8 @@ export type ProseProp = {
|
|
|
28
28
|
className?: ClassNameProp;
|
|
29
29
|
children?: ChildrenProp;
|
|
30
30
|
};
|
|
31
|
-
import type { ActionProp, ClassNameProp, DescriptionProp, IconProp, TitleProp, ColumnDefProp, GetRowIdProp, OnRowClickProp, OnSelectChangeProp, OnSortChangeProp, OnTableDensityChangeProp, SelectedIdsProp, SortStateProp, TableDensityProp, TablePresetProp, BreakpointProp, DensityProp, ChildrenProp, ToneProp, AvatarShapeProp, HeadingLevelProp, HandlerProp, SizeProp, LabelProp, IdProp, DescriptionsLayoutProp, DescriptionsColumnProp, DescriptionsSpanProp, DescriptionsItemsProp, SortDirectionProp, OnColumnFilterChangeProp, OnRowProp, TableExpandableProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp } from "../vocabulary/index.js";
|
|
31
|
+
import type { ActionProp, ClassNameProp, DescriptionProp, IconProp, TitleProp, ColumnDefProp, GetRowIdProp, OnRowClickProp, OnSelectChangeProp, OnSortChangeProp, OnTableDensityChangeProp, SelectedIdsProp, SortStateProp, TableDensityProp, TablePresetProp, BreakpointProp, DensityProp, ChildrenProp, PendingProp, ToneProp, AvatarShapeProp, HeadingLevelProp, HandlerProp, SizeProp, LabelProp, IdProp, DescriptionsLayoutProp, DescriptionsColumnProp, DescriptionsSpanProp, DescriptionsItemsProp, SortDirectionProp, OnColumnFilterChangeProp, OnRowProp, TableExpandableProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp, DisabledProp, ValueProp, DefaultValueProp, OnValueChangeProp } from "../vocabulary/index.js";
|
|
32
|
+
import type { TreeFieldNamesProp, TreeOptionProp } from "./data-entry.prop.js";
|
|
32
33
|
/**
|
|
33
34
|
* One key in a `Legend`: a tone, and the words that tone stands for.
|
|
34
35
|
*
|
|
@@ -493,3 +494,188 @@ export type TimelineGridProp = {
|
|
|
493
494
|
className?: ClassNameProp;
|
|
494
495
|
id?: IdProp;
|
|
495
496
|
};
|
|
497
|
+
/**
|
|
498
|
+
* @see Tree — one node of the standalone, on-page tree view.
|
|
499
|
+
*
|
|
500
|
+
* Structurally the same node `TreeSelect`/`Cascader` take (one tree model, two surfaces — the
|
|
501
|
+
* normalizer lives in `src/lib/tree.ts`), plus the per-node glyph the ON-PAGE surface needs and a
|
|
502
|
+
* dropdown does not.
|
|
503
|
+
*/
|
|
504
|
+
export type TreeNodeProp = TreeOptionProp & {
|
|
505
|
+
/** Glyph drawn before the label when `showIcon` is on. Decorative — the label carries the name. */
|
|
506
|
+
icon?: React.ReactNode;
|
|
507
|
+
children?: TreeNodeProp[];
|
|
508
|
+
};
|
|
509
|
+
/**
|
|
510
|
+
* @see Tree — the WAI-ARIA APG "Tree View" on a page (Ant's `Tree` / `DirectoryTree`).
|
|
511
|
+
*
|
|
512
|
+
* `TreeSelect` is the same hierarchy inside a Popover. A flat indented list only LOOKS like a tree
|
|
513
|
+
* (that was `TreeList`, removed in 21.0.0 with `Tree` as its replacement), so reach for `Tree`
|
|
514
|
+
* whenever nodes expand, collapse, or are navigated by keyboard.
|
|
515
|
+
*
|
|
516
|
+
* Ant's key-shaped names are mapped onto this package's controlled vocabulary and NOT re-spelled:
|
|
517
|
+
* selection is `value`/`defaultValue`/`onValueChange`, expansion is
|
|
518
|
+
* `expandedValues`/`defaultExpandedValues`/`onExpandedValuesChange`, checks are
|
|
519
|
+
* `checkedValues`/`defaultCheckedValues`/`onCheckedValuesChange`.
|
|
520
|
+
* @see docs/roadmap/tree-components.md §3
|
|
521
|
+
*/
|
|
522
|
+
export type TreeProp = {
|
|
523
|
+
/** The hierarchy (antd `treeData`). Use `fieldNames` to remap keys off an API response. */
|
|
524
|
+
treeData: readonly TreeNodeProp[];
|
|
525
|
+
/** Remap the data's own key names (antd `fieldNames`). */
|
|
526
|
+
fieldNames?: TreeFieldNamesProp;
|
|
527
|
+
/** Controlled selection. `string` while single, `string[]` once `multiple` is on. */
|
|
528
|
+
value?: ValueProp<string | string[]>;
|
|
529
|
+
/** Uncontrolled initial selection (controlled-triad rule). */
|
|
530
|
+
defaultValue?: DefaultValueProp<string | string[]>;
|
|
531
|
+
/** Selection change. Emits `string | undefined` while single, `string[]` once `multiple` is on. */
|
|
532
|
+
onValueChange?: OnValueChangeProp<string | string[] | undefined>;
|
|
533
|
+
/** Allow more than one selected node (antd `multiple`). Also sets `aria-multiselectable`. */
|
|
534
|
+
multiple?: boolean;
|
|
535
|
+
/** Draw a checkbox on every node (antd `checkable`). Checks are a SEPARATE axis from selection. */
|
|
536
|
+
checkable?: boolean;
|
|
537
|
+
/** Parent and child checks are independent (antd `checkStrictly`) — no cascade, no `mixed`. */
|
|
538
|
+
checkStrictly?: boolean;
|
|
539
|
+
/** Controlled checked nodes (antd `checkedKeys`). */
|
|
540
|
+
checkedValues?: readonly string[];
|
|
541
|
+
/** Uncontrolled initial checked nodes (antd `defaultCheckedKeys`). */
|
|
542
|
+
defaultCheckedValues?: readonly string[];
|
|
543
|
+
/** Checked-set change (antd `onCheck`). Fires for controlled and uncontrolled trees alike. */
|
|
544
|
+
onCheckedValuesChange?: (values: string[]) => void;
|
|
545
|
+
/** Controlled expanded branches (antd `expandedKeys`). */
|
|
546
|
+
expandedValues?: readonly string[];
|
|
547
|
+
/** Uncontrolled initial expanded branches (antd `defaultExpandedKeys`). */
|
|
548
|
+
defaultExpandedValues?: readonly string[];
|
|
549
|
+
/** Expansion change (antd `onExpand`). Fires for controlled and uncontrolled trees alike. */
|
|
550
|
+
onExpandedValuesChange?: (values: string[]) => void;
|
|
551
|
+
/** Start with every branch open (antd `defaultExpandAll`). Seeded once, never re-applied. */
|
|
552
|
+
defaultExpandAll?: boolean;
|
|
553
|
+
/**
|
|
554
|
+
* Lazy children (antd `loadData`). Called ONCE per node the first time a branch with no
|
|
555
|
+
* `children` and `isLeaf !== true` is expanded; push the fetched children into `treeData`.
|
|
556
|
+
*/
|
|
557
|
+
loadData?: (node: TreeNodeProp) => void | Promise<void>;
|
|
558
|
+
/** Render a node's title (antd `titleRender`). */
|
|
559
|
+
titleRender?: (node: TreeNodeProp) => React.ReactNode;
|
|
560
|
+
/** Draw the connector rails between a parent and its children (antd `showLine`). */
|
|
561
|
+
showLine?: boolean;
|
|
562
|
+
/** Draw each node's `icon` (antd `showIcon`). `variant="directory"` supplies folder/file glyphs. */
|
|
563
|
+
showIcon?: boolean;
|
|
564
|
+
/** `directory` is antd's `<DirectoryTree>`: folder/file glyphs and a full-row selected band. */
|
|
565
|
+
variant?: "default" | "directory";
|
|
566
|
+
/** Row height tier — the shared `--control-height` ladder. Default `md`. */
|
|
567
|
+
size?: SizeProp;
|
|
568
|
+
/** Disable the whole tree: nothing selects, checks or expands; nodes stay readable. */
|
|
569
|
+
disabled?: DisabledProp;
|
|
570
|
+
className?: ClassNameProp;
|
|
571
|
+
id?: IdProp;
|
|
572
|
+
/** Accessible name of the `role="tree"` container. Required unless `aria-labelledby` is given. */
|
|
573
|
+
"aria-label"?: string;
|
|
574
|
+
"aria-labelledby"?: string;
|
|
575
|
+
};
|
|
576
|
+
/**
|
|
577
|
+
* @see ChatBubble — which side of the conversation the message sits on. LOGICAL, not physical:
|
|
578
|
+
* `start` is the inline start of the reading direction (left in `ltr`, right in `rtl`), so a feed
|
|
579
|
+
* flips correctly under `dir="rtl"` with no per-locale branch. `start` = the other party,
|
|
580
|
+
* `end` = the reader's own messages, which is the convention every chat client shares.
|
|
581
|
+
*/
|
|
582
|
+
export type ChatBubblePlacementProp = "start" | "end";
|
|
583
|
+
/**
|
|
584
|
+
* @see ChatBubble — STRUCTURAL treatment of the message body, orthogonal to `tone` (which owns
|
|
585
|
+
* colour). Ant Design X's `shadow` is deliberately absent: this design system is a 1px-border
|
|
586
|
+
* system with no drop shadows (docs/TOKENS.md).
|
|
587
|
+
*
|
|
588
|
+
* - `filled` (default) — the quiet `--chat-bubble-background` wash.
|
|
589
|
+
* - `outlined` — transparent body inside a 1px `--chat-bubble-border-color` edge.
|
|
590
|
+
* - `borderless` — no fill and no edge; long assistant prose that should read as page copy.
|
|
591
|
+
*/
|
|
592
|
+
export type ChatBubbleVariantProp = "filled" | "borderless" | "outlined";
|
|
593
|
+
/**
|
|
594
|
+
* @see ChatBubble — status intent for a message that is not ordinary conversation (a failed send,
|
|
595
|
+
* a rate-limit warning, a tool result). A subset of `ToneProp`: `muted` and `neutral` are not
|
|
596
|
+
* offered because a bubble is already the quiet surface, so they would name no distinct state.
|
|
597
|
+
*
|
|
598
|
+
* Never colour alone (WCAG 1.4.1): a toned bubble also renders a localized `sr-only` tone word.
|
|
599
|
+
*/
|
|
600
|
+
export type ChatBubbleToneProp = Extract<ToneProp, "default" | "info" | "success" | "warning" | "destructive">;
|
|
601
|
+
/**
|
|
602
|
+
* @see ChatBubble — the streaming type-on animation. `true` takes the defaults (1 character every
|
|
603
|
+
* 50ms); the object form retunes them per instance.
|
|
604
|
+
*
|
|
605
|
+
* It animates only when `children` is a plain string — a `ReactNode` has no character count to
|
|
606
|
+
* reveal — and it is DROPPED ENTIRELY under `prefers-reduced-motion: reduce`, which renders the
|
|
607
|
+
* full text immediately (WCAG 2.2 SC 2.3.3).
|
|
608
|
+
*/
|
|
609
|
+
export type ChatBubbleTypingProp = boolean | {
|
|
610
|
+
step?: number;
|
|
611
|
+
interval?: number;
|
|
612
|
+
};
|
|
613
|
+
/** @see ChatBubble — one message in a conversation. */
|
|
614
|
+
export type ChatBubbleProp = {
|
|
615
|
+
/** The message. A `ReactNode`; pass a plain string to make `typing` animatable. */
|
|
616
|
+
children?: ChildrenProp;
|
|
617
|
+
/** Which side of the feed the message sits on. Default `start`. */
|
|
618
|
+
placement?: ChatBubblePlacementProp;
|
|
619
|
+
/** Structural treatment of the body. Default `filled`. */
|
|
620
|
+
variant?: ChatBubbleVariantProp;
|
|
621
|
+
/**
|
|
622
|
+
* The author's mark — a real `<Avatar>` node, never a styled div. It is decorative when the
|
|
623
|
+
* `header` already names the author, so mark it `aria-hidden` at the call site.
|
|
624
|
+
*/
|
|
625
|
+
avatar?: ChildrenProp;
|
|
626
|
+
/**
|
|
627
|
+
* Line above the body — the author, and anything that identifies the turn. When it is present
|
|
628
|
+
* the bubble takes its ACCESSIBLE NAME from it, so keep it text.
|
|
629
|
+
*/
|
|
630
|
+
header?: ChildrenProp;
|
|
631
|
+
/** Line below the body — timestamps, per-message actions, token counts. */
|
|
632
|
+
footer?: ChildrenProp;
|
|
633
|
+
/** The reply has been requested and has not arrived: renders `Skeleton`, sets `aria-busy`. */
|
|
634
|
+
loading?: PendingProp;
|
|
635
|
+
/** Stream the text in character by character. Honours `prefers-reduced-motion`. */
|
|
636
|
+
typing?: ChatBubbleTypingProp;
|
|
637
|
+
/** Type step and inner inset. Default `md`. */
|
|
638
|
+
size?: SizeProp;
|
|
639
|
+
/** Status intent. Default `default`. */
|
|
640
|
+
tone?: ChatBubbleToneProp;
|
|
641
|
+
className?: ClassNameProp;
|
|
642
|
+
id?: IdProp;
|
|
643
|
+
};
|
|
644
|
+
/** @see ChatBubbleList — one entry of the feed: a `ChatBubble`'s own props plus its identity. */
|
|
645
|
+
export type ChatMessageProp = Omit<ChatBubbleProp, "children" | "id"> & {
|
|
646
|
+
/** Stable message id. Also the React key and the rendered `<article>`'s DOM id. */
|
|
647
|
+
id: IdProp;
|
|
648
|
+
/** Key into the list's `roles` map — `"user"`, `"assistant"`, `"system"`, or your own. */
|
|
649
|
+
role?: string;
|
|
650
|
+
/** The message body. Named `content` here because `children` is not a data field. */
|
|
651
|
+
content?: ChildrenProp;
|
|
652
|
+
};
|
|
653
|
+
/**
|
|
654
|
+
* @see ChatBubbleList — the message feed.
|
|
655
|
+
*
|
|
656
|
+
* It scrolls inside itself, so it needs a DEFINITE height from the caller (`className="h-96"`, or
|
|
657
|
+
* a flex/grid parent that gives it a track). Without one it grows to its content and nothing ever
|
|
658
|
+
* overflows, which silently disables `autoScroll` and the jump-to-latest affordance.
|
|
659
|
+
*/
|
|
660
|
+
export type ChatBubbleListProp = {
|
|
661
|
+
/** Messages in conversation order, oldest first. */
|
|
662
|
+
items: readonly ChatMessageProp[];
|
|
663
|
+
/**
|
|
664
|
+
* Per-role bubble defaults, merged UNDER each message's own props. This is what keeps a feed
|
|
665
|
+
* consistent: `{ assistant: { placement: "start", variant: "filled" }, user: { placement:
|
|
666
|
+
* "end" } }` is written once instead of on every message.
|
|
667
|
+
*/
|
|
668
|
+
roles?: Record<string, Partial<ChatBubbleProp>>;
|
|
669
|
+
/**
|
|
670
|
+
* Keep the newest message in view while the reader is already at the bottom. Default `true`.
|
|
671
|
+
*
|
|
672
|
+
* It is NOT "scroll to the bottom when content arrives": the moment the reader scrolls up the
|
|
673
|
+
* pin is revoked and a focusable "jump to latest" affordance appears instead, because yanking a
|
|
674
|
+
* reader back mid-sentence is a change of context they did not request (WCAG 3.2.5).
|
|
675
|
+
*/
|
|
676
|
+
autoScroll?: boolean;
|
|
677
|
+
/** Accessible name of the feed. A plain string — it lands on `aria-label`. Defaults via `t()`. */
|
|
678
|
+
label?: Extract<LabelProp, string>;
|
|
679
|
+
className?: ClassNameProp;
|
|
680
|
+
id?: IdProp;
|
|
681
|
+
};
|
|
@@ -1376,3 +1376,130 @@ export type BranchScopePickerProp = FieldA11yProps & {
|
|
|
1376
1376
|
id?: IdProp;
|
|
1377
1377
|
className?: ClassNameProp;
|
|
1378
1378
|
};
|
|
1379
|
+
/**
|
|
1380
|
+
* Which keystroke SENDS and which one breaks the line — Ant Design X `Sender.submitType`.
|
|
1381
|
+
*
|
|
1382
|
+
* `enter` (default) is the chat convention: `Enter` sends, `Shift+Enter` inserts a newline.
|
|
1383
|
+
* `shiftEnter` is the inverse, for composers that hold long, deliberately multi-line drafts.
|
|
1384
|
+
* Neither ever fires while an IME conversion is in flight.
|
|
1385
|
+
* @see ChatComposer
|
|
1386
|
+
*/
|
|
1387
|
+
export type ChatComposerSubmitTypeProp = "enter" | "shiftEnter";
|
|
1388
|
+
/**
|
|
1389
|
+
* @see ChatComposer — the message input of a conversation (Ant Design X `Sender`; the industry
|
|
1390
|
+
* calls the control a *composer*, so that is what it is named).
|
|
1391
|
+
*
|
|
1392
|
+
* Built on the real `Textarea` (auto-growing between `--chat-composer-min-height` and
|
|
1393
|
+
* `--chat-composer-max-height`, both derived from the `--control-height` tier) plus real
|
|
1394
|
+
* `Button`s. The `<textarea>` is the semantic focus target: `ref`, `id`, `name` and the whole
|
|
1395
|
+
* `FormField` label/helper/error contract land on it, so a ChatComposer inside a `FormField`
|
|
1396
|
+
* behaves exactly like an `Input` does.
|
|
1397
|
+
*/
|
|
1398
|
+
export type ChatComposerProp = Omit<React.HTMLAttributes<HTMLDivElement>, "onSubmit" | "defaultValue" | "prefix" | "onChange" | "onKeyDown"> & FieldA11yProps & {
|
|
1399
|
+
/** Controlled draft text. Pair with `onValueChange` or the box freezes. */
|
|
1400
|
+
value?: ValueProp<string>;
|
|
1401
|
+
/** Uncontrolled initial draft text. */
|
|
1402
|
+
defaultValue?: DefaultValueProp<string>;
|
|
1403
|
+
/** Draft-text change handler — fires on every keystroke, including during an IME conversion. */
|
|
1404
|
+
onValueChange?: OnValueChangeProp<string>;
|
|
1405
|
+
/**
|
|
1406
|
+
* Send the draft. Receives the text as typed; never fires for an empty or whitespace-only
|
|
1407
|
+
* draft, and never while `loading`, `disabled` or `readOnly`.
|
|
1408
|
+
*/
|
|
1409
|
+
onSubmit?: (value: string) => void;
|
|
1410
|
+
/** Stop the in-flight response. Only reachable while `loading`. */
|
|
1411
|
+
onCancel?: () => void;
|
|
1412
|
+
/**
|
|
1413
|
+
* A response is streaming. The trailing action becomes CANCEL — exactly one trailing action
|
|
1414
|
+
* exists at a time, the same discipline as the picker trailing-action rule.
|
|
1415
|
+
*/
|
|
1416
|
+
loading?: PendingProp;
|
|
1417
|
+
/** Which keystroke sends and which breaks the line. Default `enter`. */
|
|
1418
|
+
submitType?: ChatComposerSubmitTypeProp;
|
|
1419
|
+
/** Empty-state text of the draft box; pass it through `t()` at the call site. */
|
|
1420
|
+
placeholder?: PlaceholderProp;
|
|
1421
|
+
/** Disable the whole composer (draft box and every action). */
|
|
1422
|
+
disabled?: DisabledProp;
|
|
1423
|
+
/** Show the draft without allowing an edit; still focusable and selectable. */
|
|
1424
|
+
readOnly?: boolean;
|
|
1425
|
+
/** Slot ABOVE the draft row — attachments, a reply-to banner, a model picker. */
|
|
1426
|
+
header?: React.ReactNode;
|
|
1427
|
+
/** Slot at the inline START of the draft row — an attach button, an avatar. */
|
|
1428
|
+
prefix?: React.ReactNode;
|
|
1429
|
+
/** Slot BELOW the draft row — a hint line, a token counter. */
|
|
1430
|
+
footer?: React.ReactNode;
|
|
1431
|
+
/** Extra trailing actions, rendered BEFORE the send/cancel action. */
|
|
1432
|
+
actions?: React.ReactNode;
|
|
1433
|
+
/** Control height tier on the shared `--control-height` ladder. Default `md`. */
|
|
1434
|
+
size?: SizeProp;
|
|
1435
|
+
/** Hard ceiling on the draft length, forwarded to the textarea. */
|
|
1436
|
+
maxLength?: number;
|
|
1437
|
+
/** Validation state the composer paints. `error` also reports `aria-invalid`. */
|
|
1438
|
+
status?: ControlStatusProp;
|
|
1439
|
+
/** Accessible name override for the send action (localized default otherwise). */
|
|
1440
|
+
submitLabel?: string;
|
|
1441
|
+
/** Accessible name override for the cancel action (localized default otherwise). */
|
|
1442
|
+
cancelLabel?: string;
|
|
1443
|
+
/** Keydown on the draft box — how `ChatSuggestion` drives its list from the composer. */
|
|
1444
|
+
onKeyDown?: React.KeyboardEventHandler<HTMLTextAreaElement>;
|
|
1445
|
+
name?: NameProp;
|
|
1446
|
+
id?: IdProp;
|
|
1447
|
+
className?: ClassNameProp;
|
|
1448
|
+
};
|
|
1449
|
+
/** @see ChatSuggestion — one row of the trigger-character list. One level of `children` is honoured. */
|
|
1450
|
+
export type ChatSuggestionItemProp = {
|
|
1451
|
+
/** Stable value handed to `onValueChange`, and the search haystack when there is no `label`. */
|
|
1452
|
+
value: string;
|
|
1453
|
+
/** Human row label. Falls back to `value`. */
|
|
1454
|
+
label?: string;
|
|
1455
|
+
/** Secondary line under the label. */
|
|
1456
|
+
description?: string;
|
|
1457
|
+
/** Decorative leading node (an icon). */
|
|
1458
|
+
icon?: React.ReactNode;
|
|
1459
|
+
/** Keep the row visible but unselectable. */
|
|
1460
|
+
disabled?: DisabledProp;
|
|
1461
|
+
/** One level of sub-rows; picking the parent drills into them instead of emitting a value. */
|
|
1462
|
+
children?: readonly ChatSuggestionItemProp[];
|
|
1463
|
+
};
|
|
1464
|
+
/** The render-prop bag `ChatSuggestion` hands to the composer it wraps. */
|
|
1465
|
+
export type ChatSuggestionRenderProp = {
|
|
1466
|
+
/**
|
|
1467
|
+
* Re-read the caret and decide whether the list belongs open — call it from the composer's
|
|
1468
|
+
* `onValueChange`. `false` force-closes; no argument force-opens with an empty query.
|
|
1469
|
+
*/
|
|
1470
|
+
onTrigger: (value?: string | false) => void;
|
|
1471
|
+
/** Forward to the composer's `onKeyDown`: arrows move, Enter/Tab pick, Escape closes. */
|
|
1472
|
+
onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement>;
|
|
1473
|
+
};
|
|
1474
|
+
/**
|
|
1475
|
+
* @see ChatSuggestion — trigger-character autocomplete over a `ChatComposer` (Ant Design X
|
|
1476
|
+
* `Suggestion`).
|
|
1477
|
+
*
|
|
1478
|
+
* The list itself is the existing `Command` (cmdk) inside a `Popover`, so the listbox ARIA comes
|
|
1479
|
+
* from a primitive that already ships it. What this component owns is the part `Command` does not:
|
|
1480
|
+
* spotting the trigger character at the caret in a textarea, tracking the query as the caret
|
|
1481
|
+
* moves, and closing on `Escape` / blur / a word break — `Escape` returns focus to the textarea
|
|
1482
|
+
* and leaves the typed text exactly as it was.
|
|
1483
|
+
*/
|
|
1484
|
+
export type ChatSuggestionProp = {
|
|
1485
|
+
/** The rows to offer. One level of `children` is honoured. */
|
|
1486
|
+
items: readonly ChatSuggestionItemProp[];
|
|
1487
|
+
/** Fires with the picked row's `value`. The caller owns what that does to the draft text. */
|
|
1488
|
+
onValueChange?: OnValueChangeProp<string>;
|
|
1489
|
+
/** The character that opens the list when typed at a word boundary. Default `/`. */
|
|
1490
|
+
triggerCharacter?: string;
|
|
1491
|
+
/** Controlled open state. */
|
|
1492
|
+
open?: OpenProp;
|
|
1493
|
+
/** Uncontrolled initial open state. */
|
|
1494
|
+
defaultOpen?: DefaultOpenProp;
|
|
1495
|
+
/** Open-state change handler. */
|
|
1496
|
+
onOpenChange?: OnOpenChangeProp;
|
|
1497
|
+
/** The composer to wrap — receives `{ onTrigger, onKeyDown }`. */
|
|
1498
|
+
children: (props: ChatSuggestionRenderProp) => React.ReactNode;
|
|
1499
|
+
/** Text shown when the query matches nothing (localized default otherwise). */
|
|
1500
|
+
emptyMessage?: EmptyMessageProp;
|
|
1501
|
+
/** Accessible name of the suggestion listbox — a plain string (localized default otherwise). */
|
|
1502
|
+
listLabel?: string;
|
|
1503
|
+
id?: IdProp;
|
|
1504
|
+
className?: ClassNameProp;
|
|
1505
|
+
};
|