@rainbow-oh/yee-c 0.6.4 → 0.8.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.
Files changed (145) hide show
  1. package/dist/cjs/Button/style/index.less +5 -4
  2. package/dist/cjs/Drawer/drawer.js +1 -1
  3. package/dist/cjs/Drawer/interface.d.ts +1 -1
  4. package/dist/cjs/Drawer/style/index.less +1 -0
  5. package/dist/cjs/Form/form-store.d.ts +2 -1
  6. package/dist/cjs/Form/form-store.js +32 -17
  7. package/dist/cjs/List/demo/virtual.d.ts +3 -0
  8. package/dist/cjs/List/demo/virtual.js +53 -0
  9. package/dist/cjs/List/hooks/useListKeyboard.d.ts +28 -0
  10. package/dist/cjs/List/hooks/useListKeyboard.js +188 -0
  11. package/dist/cjs/List/interface.d.ts +29 -2
  12. package/dist/cjs/List/list.js +141 -21
  13. package/dist/cjs/List/style/index.less +24 -0
  14. package/dist/cjs/Select/demo/columns.d.ts +3 -0
  15. package/dist/cjs/Select/demo/columns.js +62 -0
  16. package/dist/cjs/Select/hooks/useSelectKeyboard.d.ts +13 -1
  17. package/dist/cjs/Select/hooks/useSelectKeyboard.js +81 -10
  18. package/dist/cjs/Select/interface.d.ts +30 -3
  19. package/dist/cjs/Select/option.js +4 -3
  20. package/dist/cjs/Select/options.js +90 -8
  21. package/dist/cjs/Select/select.js +32 -8
  22. package/dist/cjs/Select/style/index.less +14 -0
  23. package/dist/cjs/Selector/interface.d.ts +4 -0
  24. package/dist/cjs/Selector/selector.js +5 -3
  25. package/dist/cjs/Table/hooks/useColumns.js +1 -1
  26. package/dist/cjs/Table/interface.d.ts +1 -1
  27. package/dist/cjs/Table/style/index.less +35 -11
  28. package/dist/cjs/Table/table.js +57 -7
  29. package/dist/cjs/Table/tbody/cell.js +1 -1
  30. package/dist/cjs/Table/util/header.d.ts +3 -1
  31. package/dist/cjs/Table/util/header.js +30 -3
  32. package/dist/cjs/Tree/demo/draggable.js +2 -0
  33. package/dist/cjs/Tree/interface.d.ts +46 -23
  34. package/dist/cjs/Tree/tree-node.d.ts +1 -1
  35. package/dist/cjs/Tree/tree-node.js +30 -23
  36. package/dist/cjs/Tree/tree.d.ts +13 -12
  37. package/dist/cjs/Tree/tree.js +452 -227
  38. package/dist/cjs/Tree/utils/tree-tools.d.ts +43 -10
  39. package/dist/cjs/Tree/utils/tree-tools.js +129 -91
  40. package/dist/cjs/locale/context.d.ts +5 -4
  41. package/dist/cjs/locale/context.js +15 -8
  42. package/dist/cjs/locale/interface.d.ts +20 -0
  43. package/dist/cjs/locale/util.d.ts +3 -3
  44. package/dist/cjs/style/color.css +3 -0
  45. package/dist/cjs/style/size.css +2 -1
  46. package/dist/cjs/style/themes.css +1 -1
  47. package/dist/cjs/style/variables.css +6 -2
  48. package/dist/cjs/utils/isEqual.js +5 -7
  49. package/dist/cjs/version.d.ts +1 -1
  50. package/dist/cjs/version.js +1 -1
  51. package/dist/esm/Button/style/index.less +5 -4
  52. package/dist/esm/Drawer/drawer.js +2 -1
  53. package/dist/esm/Drawer/interface.d.ts +1 -1
  54. package/dist/esm/Drawer/style/index.less +1 -0
  55. package/dist/esm/Form/form-store.d.ts +2 -1
  56. package/dist/esm/Form/form-store.js +40 -19
  57. package/dist/esm/List/demo/virtual.d.ts +3 -0
  58. package/dist/esm/List/demo/virtual.js +22 -0
  59. package/dist/esm/List/hooks/useListKeyboard.d.ts +28 -0
  60. package/dist/esm/List/hooks/useListKeyboard.js +173 -0
  61. package/dist/esm/List/interface.d.ts +29 -2
  62. package/dist/esm/List/list.js +171 -14
  63. package/dist/esm/List/style/index.less +24 -0
  64. package/dist/esm/Select/demo/columns.d.ts +3 -0
  65. package/dist/esm/Select/demo/columns.js +36 -0
  66. package/dist/esm/Select/hooks/useSelectKeyboard.d.ts +13 -1
  67. package/dist/esm/Select/hooks/useSelectKeyboard.js +91 -14
  68. package/dist/esm/Select/interface.d.ts +30 -3
  69. package/dist/esm/Select/option.js +6 -2
  70. package/dist/esm/Select/options.js +114 -32
  71. package/dist/esm/Select/select.js +40 -11
  72. package/dist/esm/Select/style/index.less +14 -0
  73. package/dist/esm/Selector/interface.d.ts +4 -0
  74. package/dist/esm/Selector/selector.js +10 -4
  75. package/dist/esm/Table/hooks/useColumns.js +1 -1
  76. package/dist/esm/Table/interface.d.ts +1 -1
  77. package/dist/esm/Table/style/index.less +35 -11
  78. package/dist/esm/Table/table.js +54 -2
  79. package/dist/esm/Table/tbody/cell.js +1 -1
  80. package/dist/esm/Table/util/header.d.ts +3 -1
  81. package/dist/esm/Table/util/header.js +30 -2
  82. package/dist/esm/Tree/demo/draggable.js +2 -0
  83. package/dist/esm/Tree/interface.d.ts +46 -23
  84. package/dist/esm/Tree/tree-node.d.ts +1 -1
  85. package/dist/esm/Tree/tree-node.js +26 -31
  86. package/dist/esm/Tree/tree.d.ts +13 -12
  87. package/dist/esm/Tree/tree.js +572 -163
  88. package/dist/esm/Tree/utils/tree-tools.d.ts +43 -10
  89. package/dist/esm/Tree/utils/tree-tools.js +219 -104
  90. package/dist/esm/locale/context.d.ts +5 -4
  91. package/dist/esm/locale/context.js +25 -9
  92. package/dist/esm/locale/interface.d.ts +20 -0
  93. package/dist/esm/locale/util.d.ts +3 -3
  94. package/dist/esm/style/color.css +3 -0
  95. package/dist/esm/style/size.css +2 -1
  96. package/dist/esm/style/themes.css +1 -1
  97. package/dist/esm/style/variables.css +6 -2
  98. package/dist/esm/utils/isEqual.js +5 -7
  99. package/dist/esm/version.d.ts +1 -1
  100. package/dist/esm/version.js +1 -1
  101. package/dist/umd/yee-c.min.css +4 -4
  102. package/dist/umd/yee-c.min.js +1 -1
  103. package/package.json +1 -1
  104. package/src/Button/style/index.less +5 -4
  105. package/src/Drawer/drawer.tsx +1 -1
  106. package/src/Drawer/interface.ts +1 -0
  107. package/src/Drawer/style/index.less +1 -0
  108. package/src/Form/form-store.ts +42 -15
  109. package/src/List/demo/virtual.tsx +20 -0
  110. package/src/List/hooks/useListKeyboard.ts +235 -0
  111. package/src/List/index.en-US.md +6 -0
  112. package/src/List/index.zh-CN.md +6 -0
  113. package/src/List/interface.ts +29 -2
  114. package/src/List/list.tsx +157 -19
  115. package/src/List/style/index.less +24 -0
  116. package/src/Select/demo/columns.tsx +35 -0
  117. package/src/Select/hooks/useSelectKeyboard.ts +112 -15
  118. package/src/Select/index.en-US.md +4 -0
  119. package/src/Select/index.zh-CN.md +4 -0
  120. package/src/Select/interface.ts +31 -4
  121. package/src/Select/option.tsx +9 -3
  122. package/src/Select/options.tsx +119 -26
  123. package/src/Select/select.tsx +41 -8
  124. package/src/Select/style/index.less +14 -0
  125. package/src/Selector/interface.ts +4 -0
  126. package/src/Selector/selector.tsx +8 -3
  127. package/src/Table/hooks/useColumns.ts +1 -1
  128. package/src/Table/interface.ts +1 -1
  129. package/src/Table/style/index.less +35 -11
  130. package/src/Table/table.tsx +60 -1
  131. package/src/Table/tbody/cell.tsx +1 -1
  132. package/src/Table/util/header.ts +48 -2
  133. package/src/Tree/demo/draggable.tsx +2 -0
  134. package/src/Tree/interface.ts +46 -23
  135. package/src/Tree/tree-node.tsx +26 -29
  136. package/src/Tree/tree.tsx +573 -274
  137. package/src/Tree/utils/tree-tools.ts +231 -125
  138. package/src/locale/_README.md +22 -13
  139. package/src/locale/context.tsx +33 -16
  140. package/src/locale/interface.ts +24 -0
  141. package/src/locale/util.ts +6 -6
  142. package/src/style/color.css +3 -0
  143. package/src/style/size.css +2 -1
  144. package/src/utils/isEqual.ts +5 -7
  145. package/src/version.ts +1 -1
@@ -4,6 +4,17 @@ export interface MoveInfo {
4
4
  dragKey: string | number;
5
5
  dropKey: string | number;
6
6
  position: DragPosition;
7
+ /**
8
+ * Full key path from root to the dragged node (inclusive).
9
+ * When provided, enables precise matching even with non-unique keys.
10
+ * Example: ['root', 'parent', 'child']
11
+ */
12
+ dragPath?: Array<string | number>;
13
+ /**
14
+ * Full key path from root to the drop target node (inclusive).
15
+ * When provided, enables precise matching even with non-unique keys.
16
+ */
17
+ dropPath?: Array<string | number>;
7
18
  }
8
19
  /**
9
20
  * Reorder the nested dataSource immutably after a drag.
@@ -13,6 +24,10 @@ export interface MoveInfo {
13
24
  * nodes are reused by reference (data may carry non-cloneable values like
14
25
  * React elements / functions in `icon`).
15
26
  *
27
+ * When `dragPath` / `dropPath` are provided in `info`, matching is done by
28
+ * walking the exact path (supports non-unique keys). Otherwise falls back to
29
+ * first-match by key (backward compatible).
30
+ *
16
31
  * Returns a brand-new root array; returns the input unchanged if dragKey is
17
32
  * not found or drag and drop resolve to the same location.
18
33
  */
@@ -20,20 +35,38 @@ export declare function moveTreeNode<T extends Record<string, unknown>>(tree: T
20
35
  key: keyof T;
21
36
  children: keyof T;
22
37
  }): T[];
23
- export declare function getChildKeys<T extends Record<string, unknown>>(data: T, fieldNames: {
24
- key: keyof T;
25
- children: keyof T;
26
- }): Array<string | number>;
38
+ /**
39
+ * Flatten tree into a flat array with rich metadata.
40
+ *
41
+ * Key design decisions:
42
+ * - Uses `uid` (built from the key path) as the internal identifier, because
43
+ * user-provided `key` values may NOT be globally unique across the tree.
44
+ * - The returned Map is keyed by `uid` for O(1) lookups.
45
+ * - `childUids` is collected bottom-up in a single pass (O(n) total) instead
46
+ * of per-node recursive enumeration (which was O(n²)).
47
+ */
27
48
  export declare function tree2array<T extends Record<string, unknown>>(tree: T | T[], fieldNames: {
28
49
  key: keyof T;
29
50
  label: keyof T;
30
51
  children: keyof T;
31
52
  }): [TreeNode<T>[], Map<string, TreeNode<T>>];
32
- export declare function containsAll<T>(a: Array<T>, b: Array<T>): boolean;
33
- export declare function removeContainsAll<T>(a: Array<T>, b: Array<T>): T[];
34
- export declare function getCheckedKeys({ checked, keys, key, map, }: {
53
+ /**
54
+ * Check whether array `a` contains every element in `b`.
55
+ */
56
+ export declare function containsAll<T>(a: Set<T>, b: Array<T>): boolean;
57
+ /**
58
+ * Remove all elements in `b` from `a`.
59
+ */
60
+ export declare function removeAll<T>(a: Set<T>, b: Array<T>): Set<T>;
61
+ /**
62
+ * Compute new checked uids when a node is toggled.
63
+ * Uses Set-based operations for O(1) membership checks.
64
+ * Disabled nodes are NOT affected by parent/child propagation — their checked
65
+ * state remains unchanged unless they are the directly toggled node.
66
+ */
67
+ export declare function getCheckedKeys({ checked, keys, uid, map, }: {
35
68
  checked: boolean;
36
- keys: Array<string | number>;
37
- key: string | number;
69
+ keys: Set<string>;
70
+ uid: string;
38
71
  map: Map<string, TreeNode<unknown>>;
39
- }): (string | number)[];
72
+ }): Set<string>;
@@ -19,6 +19,10 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
19
19
  * nodes are reused by reference (data may carry non-cloneable values like
20
20
  * React elements / functions in `icon`).
21
21
  *
22
+ * When `dragPath` / `dropPath` are provided in `info`, matching is done by
23
+ * walking the exact path (supports non-unique keys). Otherwise falls back to
24
+ * first-match by key (backward compatible).
25
+ *
22
26
  * Returns a brand-new root array; returns the input unchanged if dragKey is
23
27
  * not found or drag and drop resolve to the same location.
24
28
  */
@@ -28,12 +32,28 @@ export function moveTreeNode(tree, info, fieldNames) {
28
32
  C = fieldNames.children;
29
33
  var dragKey = info.dragKey,
30
34
  dropKey = info.dropKey,
31
- position = info.position;
35
+ position = info.position,
36
+ dragPath = info.dragPath,
37
+ dropPath = info.dropPath;
32
38
  var root = Array.isArray(tree) ? tree : [tree];
33
39
 
40
+ /**
41
+ * Match a node: when a path is provided, match by comparing node key against
42
+ * the expected key at the current depth; otherwise match by key equality.
43
+ */
44
+ var matchNode = function matchNode(node, targetKey, path, depth) {
45
+ if (path) {
46
+ return String(node[K]) === String(path[depth]);
47
+ }
48
+ return node[K] === targetKey;
49
+ };
50
+ var isTargetDepth = function isTargetDepth(path, depth) {
51
+ if (!path) return true; // no path constraint, match at any depth
52
+ return depth === path.length - 1;
53
+ };
54
+
34
55
  // Phase 1: detach the drag node subtree (immutably).
35
- // Returns the rebuilt array (without the drag node) plus the detached node.
36
- var detach = function detach(nodes) {
56
+ var detach = function detach(nodes, depth) {
37
57
  var found = null;
38
58
  var next = [];
39
59
  var _iterator = _createForOfIteratorHelper(nodes),
@@ -41,18 +61,22 @@ export function moveTreeNode(tree, info, fieldNames) {
41
61
  try {
42
62
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
43
63
  var n = _step.value;
44
- if (n[K] === dragKey) {
45
- found = n; // keep the original subtree reference intact
64
+ if (!found && matchNode(n, dragKey, dragPath, depth) && isTargetDepth(dragPath, depth)) {
65
+ found = n;
46
66
  continue;
47
67
  }
48
68
  var kids = n[C];
49
- if (Array.isArray(kids)) {
50
- var r = detach(kids);
69
+ if (!found && dragPath && Array.isArray(kids) && !matchNode(n, dragKey, dragPath, depth)) {
70
+ // A path lets us skip branches that cannot contain the drag node.
71
+ next.push(n);
72
+ } else if (!found && Array.isArray(kids)) {
73
+ // On the drag path (or no path constraint), recurse
74
+ var r = detach(kids, depth + 1);
51
75
  if (r.found) {
52
76
  found = r.found;
53
77
  next.push(_objectSpread(_objectSpread({}, n), {}, _defineProperty({}, C, r.nodes)));
54
78
  } else {
55
- next.push(n); // unchanged branch: reuse original ref
79
+ next.push(n);
56
80
  }
57
81
  } else {
58
82
  next.push(n);
@@ -68,15 +92,13 @@ export function moveTreeNode(tree, info, fieldNames) {
68
92
  nodes: next
69
93
  };
70
94
  };
71
- var _detach = detach(root),
95
+ var _detach = detach(root, 0),
72
96
  dragNode = _detach.found,
73
97
  withoutDrag = _detach.nodes;
74
- if (!dragNode) return root; // dragKey not found: no-op
98
+ if (!dragNode) return root;
75
99
 
76
- // Phase 2: insert per position into the tree that no longer contains the drag node.
77
- // Note: do NOT short-circuit with an early return once inserted — siblings that
78
- // follow the matched subtree must still be copied (by reference) or they'd be lost.
79
- var insert = function insert(nodes) {
100
+ // Phase 2: insert per position.
101
+ var insert = function insert(nodes, depth) {
80
102
  var next = [];
81
103
  var inserted = false;
82
104
  var _iterator2 = _createForOfIteratorHelper(nodes),
@@ -84,28 +106,31 @@ export function moveTreeNode(tree, info, fieldNames) {
84
106
  try {
85
107
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
86
108
  var n = _step2.value;
87
- if (!inserted && position === 'inside' && n[K] === dropKey) {
109
+ var isTarget = !inserted && matchNode(n, dropKey, dropPath, depth) && isTargetDepth(dropPath, depth);
110
+ if (isTarget && position === 'inside') {
88
111
  var oldKids = Array.isArray(n[C]) ? n[C] : [];
89
112
  next.push(_objectSpread(_objectSpread({}, n), {}, _defineProperty({}, C, [].concat(_toConsumableArray(oldKids), [dragNode]))));
90
113
  inserted = true;
91
114
  continue;
92
115
  }
93
- if (!inserted && position === 'before' && n[K] === dropKey) {
116
+ if (isTarget && position === 'before') {
94
117
  next.push(dragNode, n);
95
118
  inserted = true;
96
119
  continue;
97
120
  }
98
- if (!inserted && position === 'after' && n[K] === dropKey) {
121
+ if (isTarget && position === 'after') {
99
122
  next.push(n, dragNode);
100
123
  inserted = true;
101
124
  continue;
102
125
  }
103
126
  var kids = n[C];
104
127
  if (inserted || !Array.isArray(kids)) {
105
- // Already inserted, or a leaf with no subtree to search: keep by ref.
128
+ next.push(n);
129
+ } else if (dropPath && !matchNode(n, dropKey, dropPath, depth)) {
130
+ // Not on the drop path at this level, skip recursion
106
131
  next.push(n);
107
132
  } else {
108
- var r = insert(kids);
133
+ var r = insert(kids, depth + 1);
109
134
  if (r.inserted) {
110
135
  next.push(_objectSpread(_objectSpread({}, n), {}, _defineProperty({}, C, r.nodes)));
111
136
  inserted = true;
@@ -124,28 +149,22 @@ export function moveTreeNode(tree, info, fieldNames) {
124
149
  nodes: next
125
150
  };
126
151
  };
127
- var _insert = insert(withoutDrag),
152
+ var _insert = insert(withoutDrag, 0),
128
153
  inserted = _insert.inserted,
129
154
  result = _insert.nodes;
130
- // If the drop target wasn't found, nothing should change — return the
131
- // original (drag node stays put) rather than silently dropping it.
132
155
  return inserted ? result : root;
133
156
  }
134
- export function getChildKeys(data, fieldNames) {
135
- var key = fieldNames.key,
136
- children = fieldNames.children;
137
- var _children = data[children];
138
- var keys = [];
139
- if (Array.isArray(_children) && _children.length) {
140
- _children.forEach(function (node) {
141
- keys.push(node[key]);
142
- if (Array.isArray(node[children])) {
143
- keys.push.apply(keys, _toConsumableArray(getChildKeys(node, fieldNames)));
144
- }
145
- });
146
- }
147
- return keys;
148
- }
157
+
158
+ /**
159
+ * Flatten tree into a flat array with rich metadata.
160
+ *
161
+ * Key design decisions:
162
+ * - Uses `uid` (built from the key path) as the internal identifier, because
163
+ * user-provided `key` values may NOT be globally unique across the tree.
164
+ * - The returned Map is keyed by `uid` for O(1) lookups.
165
+ * - `childUids` is collected bottom-up in a single pass (O(n) total) instead
166
+ * of per-node recursive enumeration (which was O(n²)).
167
+ */
149
168
  export function tree2array(tree, fieldNames) {
150
169
  if (!tree || Array.isArray(tree) && tree.length === 0) return [[], new Map()];
151
170
  var data = Array.isArray(tree) ? tree : [tree];
@@ -154,118 +173,214 @@ export function tree2array(tree, fieldNames) {
154
173
  var key = fieldNames.key,
155
174
  label = fieldNames.label,
156
175
  children = fieldNames.children;
176
+
177
+ /**
178
+ * Walk returns the list of descendant uids so the parent can collect them
179
+ * without a second traversal.
180
+ * Pushes nodes in PRE-ORDER (parent before children) so that visibleItems
181
+ * filtering can work in a single forward pass.
182
+ */
157
183
  function walk(_ref) {
158
- var data = _ref.data,
159
- pKey = _ref.pKey,
184
+ var node = _ref.data,
185
+ pUid = _ref.pUid,
160
186
  depth = _ref.depth,
161
187
  path = _ref.path,
162
188
  isLast = _ref.isLast,
163
189
  lines = _ref.lines;
164
- var _key = data[key];
165
- var _label = data[label];
166
- var _children = data[children];
167
- var nextPath = [].concat(_toConsumableArray(path), [_key]);
168
- var uid = nextPath.join('_');
169
- var isParent = !!(Array.isArray(_children) && _children.length);
190
+ var rawKey = node[key];
191
+ var _label = node[label];
192
+ var _children = node[children];
193
+ var nextPath = [].concat(_toConsumableArray(path), [rawKey]);
194
+ var uid = pUid ? "".concat(pUid, "\0").concat(String(rawKey)) : String(rawKey);
195
+ var isParent = Array.isArray(_children) && _children.length > 0;
196
+
197
+ // Push parent FIRST (pre-order) so visibleItems can rely on forward scan
170
198
  var wrapperData = {
171
199
  uid: uid,
172
- key: _key,
200
+ key: rawKey,
173
201
  label: _label,
202
+ title: node.title,
174
203
  path: path,
175
- pKey: pKey,
204
+ pUid: pUid,
176
205
  depth: depth,
177
206
  isLeaf: !isParent,
178
207
  isLast: isLast,
179
208
  lines: lines,
180
- disabled: data.disabled,
181
- icon: data.icon,
182
- childKeys: getChildKeys(data, fieldNames),
183
- original: data
209
+ disabled: node.disabled,
210
+ icon: node.icon,
211
+ childUids: undefined,
212
+ // will be filled after recursion
213
+ original: node
184
214
  };
185
215
  res.push(wrapperData);
186
- map.set(_key, wrapperData);
187
- var total = Array.isArray(_children) ? _children.length : 0;
188
- _children === null || _children === void 0 || _children.forEach(function (node, index) {
189
- return walk({
190
- data: node,
191
- pKey: _key,
192
- depth: depth + 1,
193
- path: nextPath,
194
- isLast: index + 1 === total,
195
- lines: isLast ? lines : [].concat(_toConsumableArray(lines), [depth])
216
+ map.set(uid, wrapperData);
217
+
218
+ // Recurse children to collect their uids bottom-up
219
+ var childTotal = isParent ? _children.length : 0;
220
+ var allDescendantUids = [];
221
+ if (isParent) {
222
+ _children.forEach(function (child, index) {
223
+ var childIsLast = index + 1 === childTotal;
224
+ var descendantUids = walk({
225
+ data: child,
226
+ pUid: uid,
227
+ depth: depth + 1,
228
+ path: nextPath,
229
+ isLast: childIsLast,
230
+ lines: isLast ? lines : [].concat(_toConsumableArray(lines), [depth])
231
+ });
232
+ allDescendantUids.push.apply(allDescendantUids, _toConsumableArray(descendantUids));
196
233
  });
197
- });
234
+ }
235
+
236
+ // Now fill in childUids
237
+ if (allDescendantUids.length > 0) {
238
+ wrapperData.childUids = allDescendantUids;
239
+ }
240
+
241
+ // Return this uid + all descendant uids to the caller
242
+ return [uid].concat(allDescendantUids);
198
243
  }
199
- var total = Array.isArray(data) ? data.length : 0;
244
+ var rootTotal = data.length;
200
245
  data.forEach(function (node, index) {
201
- return walk({
246
+ walk({
202
247
  data: node,
203
- pKey: null,
248
+ pUid: null,
204
249
  depth: 0,
205
250
  path: [],
206
- isLast: index + 1 === total,
207
- lines: index + 1 === total ? [] : [0]
251
+ isLast: index + 1 === rootTotal,
252
+ lines: index + 1 === rootTotal ? [] : [0]
208
253
  });
209
254
  });
210
255
  return [res, map];
211
256
  }
257
+
258
+ /**
259
+ * Check whether array `a` contains every element in `b`.
260
+ */
212
261
  export function containsAll(a, b) {
213
- if (b.length > a.length) {
214
- return false;
215
- }
262
+ if (!b || b.length === 0) return true;
216
263
  return b.every(function (i) {
217
- return a.includes(i);
264
+ return a.has(i);
218
265
  });
219
266
  }
220
- export function removeContainsAll(a, b) {
221
- if (!b || b.length === 0) return a;
222
- var toRemove = new Set(b);
223
- return a.filter(function (i) {
224
- return !toRemove.has(i);
267
+
268
+ /**
269
+ * Remove all elements in `b` from `a`.
270
+ */
271
+ export function removeAll(a, b) {
272
+ var result = new Set();
273
+ a.forEach(function (item) {
274
+ return result.add(item);
225
275
  });
276
+ var _iterator3 = _createForOfIteratorHelper(b),
277
+ _step3;
278
+ try {
279
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
280
+ var item = _step3.value;
281
+ result.delete(item);
282
+ }
283
+ } catch (err) {
284
+ _iterator3.e(err);
285
+ } finally {
286
+ _iterator3.f();
287
+ }
288
+ return result;
226
289
  }
290
+
291
+ /**
292
+ * Compute new checked uids when a node is toggled.
293
+ * Uses Set-based operations for O(1) membership checks.
294
+ * Disabled nodes are NOT affected by parent/child propagation — their checked
295
+ * state remains unchanged unless they are the directly toggled node.
296
+ */
227
297
  export function getCheckedKeys(_ref2) {
228
298
  var checked = _ref2.checked,
229
299
  keys = _ref2.keys,
230
- key = _ref2.key,
300
+ uid = _ref2.uid,
231
301
  map = _ref2.map;
232
- var newKeys = _toConsumableArray(keys);
233
- var item = map.get(key);
234
- var childKeys = (item === null || item === void 0 ? void 0 : item.childKeys) || [];
235
- var pKey = item === null || item === void 0 ? void 0 : item.pKey;
302
+ var item = map.get(uid);
303
+ if (!item) return keys;
304
+ var childUids = item.childUids || [];
305
+ var newKeys = new Set();
306
+ keys.forEach(function (k) {
307
+ return newKeys.add(k);
308
+ });
236
309
  if (checked) {
237
- newKeys = [].concat(_toConsumableArray(newKeys), _toConsumableArray(childKeys), [key]);
238
- while (pKey) {
239
- var parent = map.get(pKey);
310
+ // Add this node + non-disabled descendants
311
+ newKeys.add(uid);
312
+ var _iterator4 = _createForOfIteratorHelper(childUids),
313
+ _step4;
314
+ try {
315
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
316
+ var cuid = _step4.value;
317
+ var child = map.get(cuid);
318
+ if (child && !child.disabled) {
319
+ newKeys.add(cuid);
320
+ }
321
+ }
322
+
323
+ // Walk up: if all non-disabled children of an ancestor are checked, check the ancestor
324
+ } catch (err) {
325
+ _iterator4.e(err);
326
+ } finally {
327
+ _iterator4.f();
328
+ }
329
+ var pUid = item.pUid;
330
+ while (pUid) {
331
+ var parent = map.get(pUid);
240
332
  if (parent) {
241
- if (!newKeys.includes(parent.key) && containsAll(newKeys, parent.childKeys)) {
242
- newKeys.push(parent.key);
333
+ if (parent.disabled) {
334
+ // Stop propagation at disabled ancestors
335
+ break;
336
+ }
337
+ if (!newKeys.has(parent.uid) && parent.childUids) {
338
+ // Check if all non-disabled children are checked
339
+ var allNonDisabledChecked = parent.childUids.every(function (cuid) {
340
+ var child = map.get(cuid);
341
+ return child && child.disabled || newKeys.has(cuid);
342
+ });
343
+ if (allNonDisabledChecked) {
344
+ newKeys.add(parent.uid);
345
+ }
243
346
  }
244
- pKey = parent.pKey;
347
+ pUid = parent.pUid;
245
348
  } else {
246
- pKey = null;
349
+ pUid = null;
247
350
  }
248
351
  }
249
352
  } else {
250
- newKeys = newKeys.filter(function (k) {
251
- return k !== key;
252
- });
253
- newKeys = removeContainsAll(newKeys, childKeys);
254
- var _loop = function _loop() {
255
- var parent = map.get(pKey);
256
- if (parent) {
257
- if (newKeys.includes(parent.key)) {
258
- newKeys = newKeys.filter(function (k) {
259
- return k !== parent.key;
260
- });
353
+ // Remove this node + non-disabled descendants
354
+ newKeys.delete(uid);
355
+ var _iterator5 = _createForOfIteratorHelper(childUids),
356
+ _step5;
357
+ try {
358
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
359
+ var _cuid = _step5.value;
360
+ var _child = map.get(_cuid);
361
+ if (_child && !_child.disabled) {
362
+ newKeys.delete(_cuid);
261
363
  }
262
- pKey = parent.pKey;
364
+ }
365
+
366
+ // Walk up: uncheck non-disabled ancestors
367
+ } catch (err) {
368
+ _iterator5.e(err);
369
+ } finally {
370
+ _iterator5.f();
371
+ }
372
+ var _pUid = item.pUid;
373
+ while (_pUid) {
374
+ var _parent = map.get(_pUid);
375
+ if (_parent) {
376
+ if (_parent.disabled) {
377
+ break;
378
+ }
379
+ newKeys.delete(_parent.uid);
380
+ _pUid = _parent.pUid;
263
381
  } else {
264
- pKey = null;
382
+ _pUid = null;
265
383
  }
266
- };
267
- while (pKey) {
268
- _loop();
269
384
  }
270
385
  }
271
386
  return newKeys;
@@ -1,16 +1,17 @@
1
1
  import React from 'react';
2
- import type { Locale, TFunction } from './interface';
2
+ import type { Locale, LocaleConfigInput, TFunction } from './interface';
3
3
  interface LocaleContextType {
4
4
  locale: Locale;
5
5
  lang: string;
6
- setLocale: (locale: Locale | string) => void;
6
+ setLocale: (locale: LocaleConfigInput) => void;
7
7
  t: TFunction;
8
8
  }
9
9
  export declare const LocaleContext: React.Context<LocaleContextType>;
10
10
  interface LocaleProviderProps {
11
11
  children: React.ReactNode;
12
- /** Initial locale pack or language code */
13
- defaultLocale?: Locale | string;
12
+ /** Initial locale: a language code, or a full/partial locale object.
13
+ * Partial objects are merged onto the matching built-in pack. */
14
+ defaultLocale?: LocaleConfigInput;
14
15
  /** Async locale loader function */
15
16
  loadLocale?: (lang: string) => Promise<Locale>;
16
17
  /** Whether to auto-fallback to default for missing fields */
@@ -10,9 +10,9 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
10
10
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
11
11
  // locale/context.tsx
12
12
  import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
13
- import { DEFAULT_LOCALE, LOCALE_CONFIGS } from "./constants";
13
+ import { DEFAULT_LOCALE, DEFAULT_LOCALE_CONFIG, LOCALE_CONFIGS } from "./constants";
14
14
  import defaultLocaleData from "./lang/en_US";
15
- import { isValidLocale, translate } from "./util";
15
+ import { deepMerge, isValidLocale, translate } from "./util";
16
16
 
17
17
  // ============ Default value (fallback when no Provider) ============
18
18
 
@@ -33,6 +33,25 @@ var createDefaultValue = function createDefaultValue() {
33
33
  };
34
34
  var defaultValue = createDefaultValue();
35
35
 
36
+ // ============ Locale input resolution ============
37
+
38
+ /**
39
+ * Resolve a locale input (a language code, or a possibly-partial locale
40
+ * object) into a full `Locale` pack. Partial objects are deep-merged onto the
41
+ * built-in pack matching their `locale` field, so only the overridden keys
42
+ * change and everything else is inherited.
43
+ */
44
+ function resolveLocaleConfigInput(input) {
45
+ if (typeof input === 'string') {
46
+ return LOCALE_CONFIGS[input] || defaultLocaleData;
47
+ }
48
+ if (input && typeof input.locale === 'string') {
49
+ var base = LOCALE_CONFIGS[input.locale] || DEFAULT_LOCALE_CONFIG;
50
+ return deepMerge(base, input);
51
+ }
52
+ return defaultLocaleData;
53
+ }
54
+
36
55
  // ============ Context definition ============
37
56
 
38
57
  export var LocaleContext = /*#__PURE__*/createContext(defaultValue);
@@ -48,11 +67,7 @@ export function LocaleProvider(_ref) {
48
67
  fallbackToDefault = _ref$fallbackToDefaul === void 0 ? true : _ref$fallbackToDefaul;
49
68
  // State management
50
69
  var _useState = useState(function () {
51
- if (typeof initialLocale === 'string') {
52
- // Prefer sync lookup from static locale packs
53
- return LOCALE_CONFIGS[initialLocale] || defaultLocaleData;
54
- }
55
- return isValidLocale(initialLocale) ? initialLocale : defaultLocaleData;
70
+ return resolveLocaleConfigInput(initialLocale);
56
71
  }),
57
72
  _useState2 = _slicedToArray(_useState, 2),
58
73
  locale = _useState2[0],
@@ -152,8 +167,9 @@ export function LocaleProvider(_ref) {
152
167
  }
153
168
  // Otherwise use async loading
154
169
  loadAndSetLocale(target);
155
- } else if (isValidLocale(target)) {
156
- setLocaleState(target);
170
+ } else if (target && typeof target.locale === 'string') {
171
+ // Full or partial object: merge onto the matching built-in pack
172
+ setLocaleState(resolveLocaleConfigInput(target));
157
173
  setLang(target.locale);
158
174
  }
159
175
  }, [loadAndSetLocale]);
@@ -126,4 +126,24 @@ type ExtractParams<T extends string> = T extends `${infer Pre}{${infer P}}${infe
126
126
  } : Record<string, never>;
127
127
  export type LocaleParams<K extends LocaleKey> = ExtractParams<K extends keyof Locale ? Locale[K] : K extends `${infer A}.${infer B}` ? A extends keyof Locale ? B extends keyof Locale[A] ? Locale[A][B] : never : never : never>;
128
128
  export type TFunction = <K extends LocaleKey>(key: K, params?: LocaleParams<K>) => string;
129
+ /**
130
+ * Recursively-optional version of T. Every level is optional; arrays are kept
131
+ * as-is (replaced wholesale rather than element-merged).
132
+ *
133
+ * Used so a locale override only needs to specify the keys it changes:
134
+ * `{ datepicker: { now: 'Now' } }` is a valid `DeepPartial<Locale>`.
135
+ */
136
+ export type DeepPartial<T> = T extends (...args: any[]) => any ? T : T extends object ? T extends readonly any[] ? T : {
137
+ [K in keyof T]?: DeepPartial<T[K]>;
138
+ } : T;
139
+ /**
140
+ * Locale input accepted by `LocaleProvider` / `useLocale().setLocale`:
141
+ * - a language code string (`'zh_CN'`), resolved from the built-in packs, or
142
+ * - a (possibly partial) locale object. A partial object MUST carry `locale`
143
+ * so the provider knows which built-in pack to merge the overrides onto;
144
+ * only the keys you specify are overridden, everything else is inherited.
145
+ */
146
+ export type LocaleConfigInput = string | (DeepPartial<Locale> & {
147
+ locale: string;
148
+ });
129
149
  export {};
@@ -1,4 +1,4 @@
1
- import type { Locale } from './interface';
1
+ import type { DeepPartial, Locale } from './interface';
2
2
  /**
3
3
  * Deep get object value
4
4
  * get(obj, 'a.b.c') => obj.a.b.c
@@ -16,11 +16,11 @@ export declare function isValidLocale(obj: any): obj is Locale;
16
16
  * Deep merge two objects
17
17
  * Used to merge custom config with default config
18
18
  */
19
- export declare function deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T;
19
+ export declare function deepMerge<T extends Record<string, any>>(target: T, source: DeepPartial<NoInfer<T>>): T;
20
20
  /**
21
21
  * Ensure locale pack completeness, fill missing fields with default values
22
22
  */
23
- export declare function normalizeLocale(locale: Partial<Locale>): Locale;
23
+ export declare function normalizeLocale(locale: DeepPartial<Locale>): Locale;
24
24
  /**
25
25
  * Get translation text from locale, supports fallback
26
26
  * @param locale Current locale pack