@rainbow-oh/yee-c 0.1.0 → 0.2.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 (228) hide show
  1. package/dist/cjs/Alert/alert.js +10 -4
  2. package/dist/cjs/Alert/style/index.less +3 -0
  3. package/dist/cjs/Anchor/anchor-item.js +6 -4
  4. package/dist/cjs/Anchor/anchor.js +38 -22
  5. package/dist/cjs/Anchor/demo/auto.d.ts +11 -0
  6. package/dist/cjs/Anchor/demo/auto.js +85 -0
  7. package/dist/cjs/Anchor/demo/basic.js +7 -1
  8. package/dist/cjs/Anchor/demo/controlled.js +42 -30
  9. package/dist/cjs/Anchor/interface.d.ts +2 -0
  10. package/dist/cjs/Anchor/style/index.less +2 -2
  11. package/dist/cjs/Button/button.d.ts +1 -1
  12. package/dist/cjs/Checkbox/checkbox-group.js +4 -1
  13. package/dist/cjs/Checkbox/demo/group.js +1 -0
  14. package/dist/cjs/Checkbox/interface.d.ts +5 -0
  15. package/dist/cjs/Checkbox/style/index.less +21 -1
  16. package/dist/cjs/Dropdown/demo/placement.js +2 -1
  17. package/dist/cjs/Dropdown/dropdown.js +3 -0
  18. package/dist/cjs/Dropdown/style/index.less +6 -0
  19. package/dist/cjs/Form/Field.js +33 -3
  20. package/dist/cjs/Form/Group.js +3 -2
  21. package/dist/cjs/Form/demo/form-table.js +1 -8
  22. package/dist/cjs/Form/demo/tooltip.d.ts +3 -0
  23. package/dist/cjs/Form/demo/tooltip.js +84 -0
  24. package/dist/cjs/Form/interface.d.ts +34 -1
  25. package/dist/cjs/Form/style/index.less +22 -7
  26. package/dist/cjs/Popconfirm/popconfirm.js +1 -1
  27. package/dist/cjs/Popconfirm/style/index.less +6 -0
  28. package/dist/cjs/Popover/popover.js +1 -1
  29. package/dist/cjs/Popover/style/index.less +6 -0
  30. package/dist/cjs/Progress/progress.js +2 -2
  31. package/dist/cjs/Radio/style/index.less +13 -6
  32. package/dist/cjs/Select/option.js +3 -2
  33. package/dist/cjs/Select/options.js +9 -1
  34. package/dist/cjs/Select/select.js +1 -0
  35. package/dist/cjs/Skeleton/demo/basic.js +1 -1
  36. package/dist/cjs/Space/interface.d.ts +4 -0
  37. package/dist/cjs/Space/space.js +36 -16
  38. package/dist/cjs/Splitter/demo/basic.js +1 -1
  39. package/dist/cjs/Splitter/demo/vertical.js +23 -1
  40. package/dist/cjs/Splitter/index.d.ts +1 -1
  41. package/dist/cjs/Splitter/interface.d.ts +9 -3
  42. package/dist/cjs/Splitter/splitter-handler.d.ts +1 -1
  43. package/dist/cjs/Splitter/splitter-handler.js +2 -1
  44. package/dist/cjs/Splitter/splitter.d.ts +2 -9
  45. package/dist/cjs/Splitter/splitter.js +53 -50
  46. package/dist/cjs/Splitter/style/index.less +21 -0
  47. package/dist/cjs/Table/demo/size.js +1 -1
  48. package/dist/cjs/Tabs/hooks/useTabScroll.d.ts +1 -1
  49. package/dist/cjs/Tabs/interface.d.ts +1 -1
  50. package/dist/cjs/Tabs/style/index.less +4 -0
  51. package/dist/cjs/Tabs/tab-header/index.js +56 -25
  52. package/dist/cjs/Tooltip/interface.d.ts +4 -0
  53. package/dist/cjs/Tooltip/style/index.less +8 -8
  54. package/dist/cjs/Tooltip/tooltip.js +11 -3
  55. package/dist/cjs/Tree/demo/draggable.d.ts +3 -0
  56. package/dist/cjs/Tree/demo/draggable.js +88 -0
  57. package/dist/cjs/Tree/demo/expanded.js +6 -6
  58. package/dist/cjs/Tree/index.d.ts +1 -0
  59. package/dist/cjs/Tree/index.js +3 -1
  60. package/dist/cjs/Tree/interface.d.ts +26 -0
  61. package/dist/cjs/Tree/style/index.less +25 -0
  62. package/dist/cjs/Tree/tree-node.js +38 -4
  63. package/dist/cjs/Tree/tree.d.ts +12 -1
  64. package/dist/cjs/Tree/tree.js +174 -18
  65. package/dist/cjs/Tree/utils/tree-tools.d.ts +21 -0
  66. package/dist/cjs/Tree/utils/tree-tools.js +88 -0
  67. package/dist/cjs/Trigger/style/index.less +17 -0
  68. package/dist/cjs/Trigger/trigger.js +3 -1
  69. package/dist/cjs/hooks/useDebounceFunction.d.ts +1 -1
  70. package/dist/cjs/hooks/useLayoutEffect.d.ts +1 -1
  71. package/dist/cjs/style/size.css +13 -4
  72. package/dist/cjs/style/themes.css +1 -1
  73. package/dist/cjs/style/variables.css +14 -5
  74. package/dist/cjs/utils/debounce.d.ts +6 -1
  75. package/dist/cjs/utils/debounce.js +9 -1
  76. package/dist/cjs/version.d.ts +1 -1
  77. package/dist/cjs/version.js +1 -1
  78. package/dist/esm/Alert/alert.js +24 -8
  79. package/dist/esm/Alert/style/index.less +3 -0
  80. package/dist/esm/Anchor/anchor-item.js +6 -3
  81. package/dist/esm/Anchor/anchor.js +38 -19
  82. package/dist/esm/Anchor/demo/auto.d.ts +11 -0
  83. package/dist/esm/Anchor/demo/auto.js +62 -0
  84. package/dist/esm/Anchor/demo/basic.js +3 -1
  85. package/dist/esm/Anchor/demo/controlled.js +14 -9
  86. package/dist/esm/Anchor/interface.d.ts +2 -0
  87. package/dist/esm/Anchor/style/index.less +2 -2
  88. package/dist/esm/Button/button.d.ts +1 -1
  89. package/dist/esm/Checkbox/checkbox-group.js +4 -2
  90. package/dist/esm/Checkbox/demo/group.js +1 -0
  91. package/dist/esm/Checkbox/interface.d.ts +5 -0
  92. package/dist/esm/Checkbox/style/index.less +21 -1
  93. package/dist/esm/Dropdown/demo/placement.js +2 -1
  94. package/dist/esm/Dropdown/dropdown.js +6 -1
  95. package/dist/esm/Dropdown/style/index.less +6 -0
  96. package/dist/esm/Form/Field.js +43 -5
  97. package/dist/esm/Form/Group.js +4 -2
  98. package/dist/esm/Form/demo/form-table.js +8 -6
  99. package/dist/esm/Form/demo/tooltip.d.ts +3 -0
  100. package/dist/esm/Form/demo/tooltip.js +59 -0
  101. package/dist/esm/Form/interface.d.ts +34 -1
  102. package/dist/esm/Form/style/index.less +22 -7
  103. package/dist/esm/Popconfirm/popconfirm.js +3 -1
  104. package/dist/esm/Popconfirm/style/index.less +6 -0
  105. package/dist/esm/Popover/popover.js +3 -1
  106. package/dist/esm/Popover/style/index.less +6 -0
  107. package/dist/esm/Progress/progress.js +5 -5
  108. package/dist/esm/Radio/style/index.less +13 -6
  109. package/dist/esm/Select/option.js +4 -2
  110. package/dist/esm/Select/options.js +5 -3
  111. package/dist/esm/Select/select.js +1 -0
  112. package/dist/esm/Skeleton/demo/basic.js +2 -8
  113. package/dist/esm/Space/interface.d.ts +4 -0
  114. package/dist/esm/Space/space.js +24 -13
  115. package/dist/esm/Splitter/demo/basic.js +2 -0
  116. package/dist/esm/Splitter/demo/vertical.js +13 -5
  117. package/dist/esm/Splitter/index.d.ts +1 -1
  118. package/dist/esm/Splitter/interface.d.ts +9 -3
  119. package/dist/esm/Splitter/splitter-handler.d.ts +1 -1
  120. package/dist/esm/Splitter/splitter-handler.js +3 -1
  121. package/dist/esm/Splitter/splitter.d.ts +2 -9
  122. package/dist/esm/Splitter/splitter.js +59 -56
  123. package/dist/esm/Splitter/style/index.less +21 -0
  124. package/dist/esm/Table/demo/size.js +2 -7
  125. package/dist/esm/Tabs/hooks/useTabScroll.d.ts +1 -1
  126. package/dist/esm/Tabs/interface.d.ts +1 -1
  127. package/dist/esm/Tabs/style/index.less +4 -0
  128. package/dist/esm/Tabs/tab-header/index.js +11 -5
  129. package/dist/esm/Tooltip/interface.d.ts +4 -0
  130. package/dist/esm/Tooltip/style/index.less +8 -8
  131. package/dist/esm/Tooltip/tooltip.js +14 -2
  132. package/dist/esm/Tree/demo/draggable.d.ts +3 -0
  133. package/dist/esm/Tree/demo/draggable.js +61 -0
  134. package/dist/esm/Tree/demo/expanded.js +6 -6
  135. package/dist/esm/Tree/index.d.ts +1 -0
  136. package/dist/esm/Tree/index.js +1 -0
  137. package/dist/esm/Tree/interface.d.ts +26 -0
  138. package/dist/esm/Tree/style/index.less +25 -0
  139. package/dist/esm/Tree/tree-node.js +29 -4
  140. package/dist/esm/Tree/tree.d.ts +12 -1
  141. package/dist/esm/Tree/tree.js +135 -4
  142. package/dist/esm/Tree/utils/tree-tools.d.ts +21 -0
  143. package/dist/esm/Tree/utils/tree-tools.js +128 -1
  144. package/dist/esm/Trigger/style/index.less +17 -0
  145. package/dist/esm/Trigger/trigger.js +4 -3
  146. package/dist/esm/hooks/useDebounceFunction.d.ts +1 -1
  147. package/dist/esm/hooks/useLayoutEffect.d.ts +1 -1
  148. package/dist/esm/style/size.css +13 -4
  149. package/dist/esm/style/themes.css +1 -1
  150. package/dist/esm/style/variables.css +14 -5
  151. package/dist/esm/utils/debounce.d.ts +6 -1
  152. package/dist/esm/utils/debounce.js +9 -2
  153. package/dist/esm/version.d.ts +1 -1
  154. package/dist/esm/version.js +1 -1
  155. package/dist/umd/yee-c.min.css +2 -2
  156. package/dist/umd/yee-c.min.js +1 -1
  157. package/package.json +21 -23
  158. package/src/Alert/alert.tsx +10 -4
  159. package/src/Alert/style/index.less +3 -0
  160. package/src/Anchor/anchor-item.tsx +7 -2
  161. package/src/Anchor/anchor.tsx +49 -23
  162. package/src/Anchor/demo/auto.tsx +55 -0
  163. package/src/Anchor/demo/basic.tsx +7 -1
  164. package/src/Anchor/demo/controlled.tsx +18 -14
  165. package/src/Anchor/index.en-US.md +17 -0
  166. package/src/Anchor/index.zh-CN.md +17 -0
  167. package/src/Anchor/interface.ts +2 -0
  168. package/src/Anchor/style/index.less +2 -2
  169. package/src/Checkbox/checkbox-group.tsx +5 -1
  170. package/src/Checkbox/demo/group.tsx +1 -0
  171. package/src/Checkbox/interface.ts +5 -0
  172. package/src/Checkbox/style/index.less +21 -1
  173. package/src/Dropdown/demo/placement.tsx +1 -0
  174. package/src/Dropdown/dropdown.tsx +2 -0
  175. package/src/Dropdown/style/index.less +6 -0
  176. package/src/Form/Field.tsx +45 -3
  177. package/src/Form/Group.tsx +4 -1
  178. package/src/Form/demo/form-table.tsx +7 -7
  179. package/src/Form/demo/tooltip.tsx +72 -0
  180. package/src/Form/form-store.ts +1 -1
  181. package/src/Form/index.en-US.md +2 -0
  182. package/src/Form/index.zh-CN.md +83 -85
  183. package/src/Form/interface.ts +36 -1
  184. package/src/Form/style/index.less +22 -7
  185. package/src/Popconfirm/popconfirm.tsx +1 -1
  186. package/src/Popconfirm/style/index.less +6 -0
  187. package/src/Popover/popover.tsx +1 -1
  188. package/src/Popover/style/index.less +6 -0
  189. package/src/Progress/progress.tsx +2 -2
  190. package/src/Radio/style/index.less +13 -6
  191. package/src/Select/option.tsx +2 -1
  192. package/src/Select/options.tsx +7 -2
  193. package/src/Select/select.tsx +1 -0
  194. package/src/Skeleton/demo/basic.tsx +3 -3
  195. package/src/Space/interface.ts +4 -0
  196. package/src/Space/space.tsx +40 -16
  197. package/src/Splitter/demo/basic.tsx +2 -2
  198. package/src/Splitter/demo/vertical.tsx +19 -5
  199. package/src/Splitter/index.ts +5 -1
  200. package/src/Splitter/interface.ts +10 -3
  201. package/src/Splitter/splitter-handler.tsx +4 -2
  202. package/src/Splitter/splitter.tsx +55 -60
  203. package/src/Splitter/style/index.less +21 -0
  204. package/src/Table/demo/size.tsx +3 -3
  205. package/src/Table/index.zh-CN.md +50 -50
  206. package/src/Tabs/interface.ts +1 -1
  207. package/src/Tabs/style/index.less +4 -0
  208. package/src/Tabs/tab-header/index.tsx +26 -5
  209. package/src/Tooltip/index.en-US.md +1 -0
  210. package/src/Tooltip/index.zh-CN.md +1 -0
  211. package/src/Tooltip/interface.ts +4 -0
  212. package/src/Tooltip/style/index.less +8 -8
  213. package/src/Tooltip/tooltip.tsx +10 -1
  214. package/src/Tree/demo/draggable.tsx +58 -0
  215. package/src/Tree/demo/expanded.tsx +7 -7
  216. package/src/Tree/index.en-US.md +3 -0
  217. package/src/Tree/index.ts +1 -0
  218. package/src/Tree/index.zh-CN.md +3 -0
  219. package/src/Tree/interface.ts +28 -0
  220. package/src/Tree/style/index.less +25 -0
  221. package/src/Tree/tree-node.tsx +26 -0
  222. package/src/Tree/tree.tsx +183 -2
  223. package/src/Tree/utils/tree-tools.ts +107 -0
  224. package/src/Trigger/style/index.less +17 -0
  225. package/src/Trigger/trigger.tsx +3 -1
  226. package/src/style/size.css +13 -4
  227. package/src/utils/debounce.ts +22 -3
  228. package/src/version.ts +1 -1
package/src/Tree/tree.tsx CHANGED
@@ -5,11 +5,13 @@ import React, {
5
5
  useCallback,
6
6
  useContext,
7
7
  useMemo,
8
+ useRef,
9
+ useState,
8
10
  } from 'react';
9
11
  import { GlobalContext } from '../Config-Provider';
10
12
  import useMergedState from '../hooks/useMergedState';
11
13
  import mergeContextToProps from '../utils/mergeContextToProps';
12
- import type { TreeProps } from './interface';
14
+ import type { DragPosition, TreeProps } from './interface';
13
15
  import TreeNode from './tree-node';
14
16
  import { getCheckedKeys, tree2array } from './utils/tree-tools';
15
17
 
@@ -21,6 +23,8 @@ const DEFAULTFIELDNAMES = {
21
23
  children: 'children' as const,
22
24
  };
23
25
 
26
+ type DropState = { key: string | number; position: DragPosition } | null;
27
+
24
28
  export const TreeContext = createContext<{
25
29
  prefixCls?: string;
26
30
  checkable?: boolean;
@@ -31,17 +35,31 @@ export const TreeContext = createContext<{
31
35
  selectedKeys: Array<string | number>;
32
36
  checkedKeys: Array<string | number>;
33
37
  expandedKeys: Array<string | number>;
38
+ draggable?: boolean;
39
+ draggingKey: string | number | null;
40
+ dropState: DropState;
34
41
  onCheck: (event: ChangeEvent<HTMLInputElement>) => void;
35
42
  onSelect: (key: string | number) => void;
36
43
  onExpand: (key: string | number) => void;
44
+ onNodeDragStart: (key: string | number, e: React.DragEvent) => void;
45
+ onNodeDragOver: (key: string | number, e: React.DragEvent) => void;
46
+ onNodeDragLeave: (key: string | number, e: React.DragEvent) => void;
47
+ onNodeDrop: (key: string | number, e: React.DragEvent) => void;
37
48
  }>({
38
49
  showIcon: true,
39
50
  selectedKeys: [],
40
51
  checkedKeys: [],
41
52
  expandedKeys: [],
53
+ draggable: false,
54
+ draggingKey: null,
55
+ dropState: null,
42
56
  onCheck: () => {},
43
57
  onSelect: () => {},
44
58
  onExpand: () => {},
59
+ onNodeDragStart: () => {},
60
+ onNodeDragOver: () => {},
61
+ onNodeDragLeave: () => {},
62
+ onNodeDrop: () => {},
45
63
  });
46
64
 
47
65
  const Tree = <T extends Record<string, unknown> = any>(
@@ -70,6 +88,9 @@ const Tree = <T extends Record<string, unknown> = any>(
70
88
  icon,
71
89
  showLine,
72
90
  switcherIcon,
91
+ draggable,
92
+ allowDrop,
93
+ onDrop,
73
94
  onSelect,
74
95
  onCheck,
75
96
  onExpand,
@@ -198,8 +219,161 @@ const Tree = <T extends Record<string, unknown> = any>(
198
219
  [mergedExpandedKeys, itemMap, onExpand],
199
220
  );
200
221
 
222
+ // --- drag & drop (native HTML5) ---
223
+ // dataTransfer is not reliably readable during dragover, so the drag key
224
+ // lives in a ref as the source of truth; a state mirrors it for styling.
225
+ const dragKeyRef = useRef<string | number | null>(null);
226
+ const [draggingKey, setDraggingKey] = useState<string | number | null>(null);
227
+ const [dropState, setDropState] = useState<DropState>(null);
228
+
229
+ // A drop is a cycle/illegal if the target is the dragged node itself or one
230
+ // of its descendants. Covers before/after/inside uniformly.
231
+ const isIllegalMove = useCallback(
232
+ (dragKey: string | number, dropKey: string | number) => {
233
+ if (dragKey === dropKey) return true;
234
+ const dropNode = itemMap.get(dropKey as string);
235
+ return !!dropNode && dropNode.path.includes(dragKey as string);
236
+ },
237
+ [itemMap],
238
+ );
239
+
240
+ const resolvePosition = useCallback(
241
+ (rect: DOMRect, clientY: number): DragPosition => {
242
+ const offset = (clientY - rect.top) / rect.height;
243
+ if (offset < 0.25) return 'before';
244
+ if (offset > 0.75) return 'after';
245
+ return 'inside';
246
+ },
247
+ [],
248
+ );
249
+
250
+ const handleDragStart = useCallback(
251
+ (key: string | number, e: React.DragEvent) => {
252
+ const node = itemMap.get(key as string);
253
+ if (node?.disabled) {
254
+ e.preventDefault();
255
+ return;
256
+ }
257
+ dragKeyRef.current = key;
258
+ setDraggingKey(key);
259
+ if (e.dataTransfer) {
260
+ e.dataTransfer.effectAllowed = 'move';
261
+ try {
262
+ e.dataTransfer.setData('text/plain', String(key));
263
+ } catch {
264
+ /* some browsers throw on setData in certain modes */
265
+ }
266
+ }
267
+ },
268
+ [itemMap],
269
+ );
270
+
271
+ const handleDragOver = useCallback(
272
+ (key: string | number, e: React.DragEvent) => {
273
+ const dragKey = dragKeyRef.current;
274
+ if (dragKey === null) return;
275
+ if (isIllegalMove(dragKey, key)) return;
276
+ const dropNode = itemMap.get(key as string);
277
+ if (!dropNode || dropNode.disabled) return;
278
+
279
+ const position = resolvePosition(
280
+ (e.currentTarget as HTMLElement).getBoundingClientRect(),
281
+ e.clientY,
282
+ );
283
+
284
+ const dragNode = itemMap.get(dragKey as string);
285
+ if (
286
+ allowDrop &&
287
+ dragNode &&
288
+ allowDrop({
289
+ dragNode: dragNode.original,
290
+ dropNode: dropNode.original,
291
+ position,
292
+ }) === false
293
+ ) {
294
+ return; // not calling preventDefault shows the "no-drop" cursor
295
+ }
296
+
297
+ e.preventDefault();
298
+ if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';
299
+ setDropState((prev) =>
300
+ prev && prev.key === key && prev.position === position
301
+ ? prev
302
+ : { key, position },
303
+ );
304
+ },
305
+ [itemMap, isIllegalMove, resolvePosition, allowDrop],
306
+ );
307
+
308
+ const handleDragLeave = useCallback(
309
+ // Intentionally a no-op: dragleave fires spuriously when crossing child
310
+ // elements. Drop state is cleared in handleDragEnd at the Tree root.
311
+ () => {},
312
+ [],
313
+ );
314
+
315
+ const handleDrop = useCallback(
316
+ (key: string | number, e: React.DragEvent) => {
317
+ e.preventDefault();
318
+ const dragKey = dragKeyRef.current;
319
+ if (dragKey === null) return;
320
+
321
+ const position = resolvePosition(
322
+ (e.currentTarget as HTMLElement).getBoundingClientRect(),
323
+ e.clientY,
324
+ );
325
+
326
+ if (isIllegalMove(dragKey, key)) return;
327
+ const dragNode = itemMap.get(dragKey as string);
328
+ const dropNode = itemMap.get(key as string);
329
+ if (!dragNode || !dropNode) return;
330
+ if (
331
+ allowDrop &&
332
+ allowDrop({
333
+ dragNode: dragNode.original,
334
+ dropNode: dropNode.original,
335
+ position,
336
+ }) === false
337
+ ) {
338
+ return;
339
+ }
340
+
341
+ onDrop?.({
342
+ dragNode: dragNode.original,
343
+ dropNode: dropNode.original,
344
+ dragKey,
345
+ dropKey: key,
346
+ position,
347
+ dropToGap: position !== 'inside',
348
+ });
349
+
350
+ // Auto-expand the target after a drop-inside so the dragged node is
351
+ // visible. Only effective in uncontrolled expandedKeys mode.
352
+ if (position === 'inside') {
353
+ setMergedExpandedKeys((prev) =>
354
+ prev.includes(key) ? prev : [...prev, key],
355
+ );
356
+ }
357
+
358
+ dragKeyRef.current = null;
359
+ setDraggingKey(null);
360
+ setDropState(null);
361
+ },
362
+ [itemMap, isIllegalMove, resolvePosition, allowDrop, onDrop],
363
+ );
364
+
365
+ const handleDragEnd = useCallback(() => {
366
+ dragKeyRef.current = null;
367
+ setDraggingKey(null);
368
+ setDropState(null);
369
+ }, []);
370
+
201
371
  return (
202
- <div className={clsx(prefixCls, className)} style={style}>
372
+ <div
373
+ className={clsx(prefixCls, className)}
374
+ style={style}
375
+ onDragEnd={handleDragEnd}
376
+ >
203
377
  <TreeContext.Provider
204
378
  value={{
205
379
  prefixCls,
@@ -211,9 +385,16 @@ const Tree = <T extends Record<string, unknown> = any>(
211
385
  selectedKeys: mergedSelectedKeys,
212
386
  checkedKeys: mergedCheckedKeys,
213
387
  expandedKeys: mergedExpandedKeys,
388
+ draggable,
389
+ draggingKey,
390
+ dropState,
214
391
  onCheck: handleCheck,
215
392
  onSelect: handleSelect,
216
393
  onExpand: handleExpand,
394
+ onNodeDragStart: handleDragStart,
395
+ onNodeDragOver: handleDragOver,
396
+ onNodeDragLeave: handleDragLeave,
397
+ onNodeDrop: handleDrop,
217
398
  }}
218
399
  >
219
400
  {flatItems.map((node, index) => (
@@ -1,5 +1,112 @@
1
1
  import { TreeNode } from '../interface';
2
2
 
3
+ export type DragPosition = 'before' | 'after' | 'inside';
4
+
5
+ export interface MoveInfo {
6
+ dragKey: string | number;
7
+ dropKey: string | number;
8
+ position: DragPosition;
9
+ }
10
+
11
+ /**
12
+ * Reorder the nested dataSource immutably after a drag.
13
+ *
14
+ * Two phases: detach the dragged subtree, then insert it at the drop target.
15
+ * Only the modified path gets new arrays + shallow-cloned wrappers; untouched
16
+ * nodes are reused by reference (data may carry non-cloneable values like
17
+ * React elements / functions in `icon`).
18
+ *
19
+ * Returns a brand-new root array; returns the input unchanged if dragKey is
20
+ * not found or drag and drop resolve to the same location.
21
+ */
22
+ export function moveTreeNode<T extends Record<string, unknown>>(
23
+ tree: T | T[],
24
+ info: MoveInfo,
25
+ fieldNames: { key: keyof T; children: keyof T },
26
+ ): T[] {
27
+ if (!tree || (Array.isArray(tree) && tree.length === 0)) return [];
28
+
29
+ const { key: K, children: C } = fieldNames;
30
+ const { dragKey, dropKey, position } = info;
31
+ const root = Array.isArray(tree) ? tree : [tree];
32
+
33
+ // Phase 1: detach the drag node subtree (immutably).
34
+ // Returns the rebuilt array (without the drag node) plus the detached node.
35
+ const detach = (
36
+ nodes: T[],
37
+ ): { found: T | null; nodes: T[] } => {
38
+ let found: T | null = null;
39
+ const next: T[] = [];
40
+ for (const n of nodes) {
41
+ if (n[K] === dragKey) {
42
+ found = n; // keep the original subtree reference intact
43
+ continue;
44
+ }
45
+ const kids = n[C] as T[] | undefined;
46
+ if (Array.isArray(kids)) {
47
+ const r = detach(kids);
48
+ if (r.found) {
49
+ found = r.found;
50
+ next.push({ ...n, [C]: r.nodes });
51
+ } else {
52
+ next.push(n); // unchanged branch: reuse original ref
53
+ }
54
+ } else {
55
+ next.push(n);
56
+ }
57
+ }
58
+ return { found, nodes: next };
59
+ };
60
+
61
+ const { found: dragNode, nodes: withoutDrag } = detach(root);
62
+ if (!dragNode) return root; // dragKey not found: no-op
63
+
64
+ // Phase 2: insert per position into the tree that no longer contains the drag node.
65
+ // Note: do NOT short-circuit with an early return once inserted — siblings that
66
+ // follow the matched subtree must still be copied (by reference) or they'd be lost.
67
+ const insert = (nodes: T[]): { inserted: boolean; nodes: T[] } => {
68
+ const next: T[] = [];
69
+ let inserted = false;
70
+ for (const n of nodes) {
71
+ if (!inserted && position === 'inside' && n[K] === dropKey) {
72
+ const oldKids = (Array.isArray(n[C]) ? n[C] : []) as T[];
73
+ next.push({ ...n, [C]: [...oldKids, dragNode] });
74
+ inserted = true;
75
+ continue;
76
+ }
77
+ if (!inserted && position === 'before' && n[K] === dropKey) {
78
+ next.push(dragNode, n);
79
+ inserted = true;
80
+ continue;
81
+ }
82
+ if (!inserted && position === 'after' && n[K] === dropKey) {
83
+ next.push(n, dragNode);
84
+ inserted = true;
85
+ continue;
86
+ }
87
+ const kids = n[C] as T[] | undefined;
88
+ if (inserted || !Array.isArray(kids)) {
89
+ // Already inserted, or a leaf with no subtree to search: keep by ref.
90
+ next.push(n);
91
+ } else {
92
+ const r = insert(kids);
93
+ if (r.inserted) {
94
+ next.push({ ...n, [C]: r.nodes });
95
+ inserted = true;
96
+ } else {
97
+ next.push(n);
98
+ }
99
+ }
100
+ }
101
+ return { inserted, nodes: next };
102
+ };
103
+
104
+ const { inserted, nodes: result } = insert(withoutDrag);
105
+ // If the drop target wasn't found, nothing should change — return the
106
+ // original (drag node stays put) rather than silently dropping it.
107
+ return inserted ? result : root;
108
+ }
109
+
3
110
  export function getChildKeys<T extends Record<string, unknown>>(
4
111
  data: T,
5
112
  fieldNames: {
@@ -8,6 +8,23 @@
8
8
  display: none;
9
9
  }
10
10
 
11
+ &-arrow {
12
+ &::after {
13
+ position: absolute;
14
+ bottom: 0;
15
+ z-index: 0;
16
+ width: var(--yee-popup-arrow-width);
17
+ height: var(--yee-popup-arrow-height);
18
+ margin: auto;
19
+ border-radius: var(--yee-popup-arrow-border-radius);
20
+ box-shadow: var(--yee-box-shadow-popover-arrow);
21
+ transform: translateY(var(--yee-popup-arrow-translate-y))
22
+ rotate(var(--yee-popup-arrow-rotate));
23
+ content: '';
24
+ inset-inline: 0;
25
+ }
26
+ }
27
+
11
28
  &-topLeft,
12
29
  &-top,
13
30
  &-topRight {
@@ -26,6 +26,7 @@ const Trigger: FC<TriggerProps> = (baseprops) => {
26
26
  mouseLeaveDelay = 0.2,
27
27
  arrow,
28
28
  forceRender = false,
29
+ popupStyle,
29
30
  onOpenChange,
30
31
  zIndex,
31
32
  ...rest
@@ -130,6 +131,7 @@ const Trigger: FC<TriggerProps> = (baseprops) => {
130
131
  onOpenChange?.(false);
131
132
  }
132
133
  };
134
+
133
135
  return (
134
136
  <RcTrigger
135
137
  {...rest}
@@ -156,7 +158,7 @@ const Trigger: FC<TriggerProps> = (baseprops) => {
156
158
  motionAppear: true,
157
159
  motionEnter: true,
158
160
  }}
159
- popupStyle={{ pointerEvents: 'auto' }}
161
+ popupStyle={{ pointerEvents: 'auto', ...popupStyle }}
160
162
  {...(zIndex !== undefined ? { zIndex } : {})}
161
163
  getPopupClassNameFromAlign={getPopupClassNameFromAlign}
162
164
  onOpenChange={handlePopupOpenChange}
@@ -8,10 +8,17 @@
8
8
  --yee-z-index-message: 1100; /* L6 全局反馈 */
9
9
  --yee-z-index-max: 1110; /* L7 最顶层 */
10
10
 
11
+ /* common size */
12
+ --yee-popup-arrow-width: 8px;
13
+ --yee-popup-arrow-height: 8px;
14
+ --yee-popup-border-radius: 4px;
15
+ --yee-popup-arrow-translate-y: 50%;
16
+ --yee-popup-arrow-rotate: -135deg;
17
+
11
18
  /* --Alert-- */
12
19
  --yee-alert-close-top: 7px;
13
20
  --yee-alert-close-right: 11px;
14
- --yee-alert-icon-margin-right: 6px;
21
+ --yee-alert-icon-margin-right: 12px;
15
22
  --yee-alert-title-font-size: 16px;
16
23
  --yee-alert-message-font-size: 14px;
17
24
  --yee-alert-banner-padding: 8px 12px;
@@ -26,7 +33,7 @@
26
33
  --yee-anchor-static-content-padding: 8px 0;
27
34
  --yee-anchor-static-item-active-width: 2px;
28
35
  --yee-anchor-static-item-active-height: 22px;
29
- --yee-anchor-vertical-line-left: 4px;
36
+ --yee-anchor-vertical-line-left: 0;
30
37
  --yee-anchor-horizontal-item-padding: 6px 12px;
31
38
  --yee-anchor-nav-arrow-left: -8px;
32
39
  --yee-anchor-nav-arrow-height: 80px;
@@ -376,6 +383,8 @@
376
383
  --yee-popover-header-font-weight: 500;
377
384
  --yee-popover-header-font-size: 16px;
378
385
  --yee-popover-header-line-height: 24px;
386
+ --yee-popover-arrow-width: var(--yee-popup-arrow-width);
387
+ --yee-popover-arrow-height: var(--yee-popup-arrow-height);
379
388
 
380
389
  /* --Progress-- */
381
390
  --yee-progress-text-width: 48px;
@@ -768,8 +777,8 @@
768
777
  --yee-tooltip-z-index: var(--yee-z-index-tooltip);
769
778
  --yee-tooltip-padding: 8px 12px;
770
779
  --yee-tooltip-border-radius: 4px;
771
- --yee-tooltip-arrow-width: 8px;
772
- --yee-tooltip-arrow-height: 8px;
780
+ --yee-tooltip-arrow-width: var(--yee-popup-arrow-width);
781
+ --yee-tooltip-arrow-height: var(--yee-popup-arrow-height);
773
782
  --yee-tooltip-arrow-border-radius: 0 0 2px 0;
774
783
  --yee-tooltip-arrow-translate-y: 50%;
775
784
  --yee-tooltip-arrow-rotate: -135deg;
@@ -1,4 +1,13 @@
1
- function debounce<T extends (...args: any[]) => any>(fn: T, delay: number) {
1
+ type AnyFn = (...args: any[]) => any;
2
+
3
+ export type Debounced<T extends AnyFn> = ((
4
+ ...args: Parameters<T>
5
+ ) => void) & {
6
+ /** Cancel any pending invocation. */
7
+ cancel: () => void;
8
+ };
9
+
10
+ function debounce<T extends AnyFn>(fn: T, delay: number): Debounced<T> {
2
11
  if (typeof fn !== 'function') {
3
12
  throw new Error('Expected fn to be a function');
4
13
  }
@@ -7,15 +16,25 @@ function debounce<T extends (...args: any[]) => any>(fn: T, delay: number) {
7
16
  }
8
17
 
9
18
  let timer: ReturnType<typeof setTimeout> | undefined = undefined;
10
- return function (...args: Parameters<T>) {
19
+
20
+ const debounced = function (this: unknown, ...args: Parameters<T>) {
11
21
  if (timer !== undefined) {
12
22
  clearTimeout(timer);
13
23
  }
14
24
  timer = setTimeout(() => {
15
- // @ts-ignore
25
+ timer = undefined;
16
26
  fn.apply(this, args);
17
27
  }, delay);
28
+ } as Debounced<T>;
29
+
30
+ debounced.cancel = () => {
31
+ if (timer !== undefined) {
32
+ clearTimeout(timer);
33
+ timer = undefined;
34
+ }
18
35
  };
36
+
37
+ return debounced;
19
38
  }
20
39
 
21
40
  export default debounce;
package/src/version.ts CHANGED
@@ -4,7 +4,7 @@ declare global {
4
4
  }
5
5
  }
6
6
 
7
- export const version = "0.1.0";
7
+ export const version = "0.2.0";
8
8
 
9
9
  if (typeof window !== 'undefined') {
10
10
  window.__yee_c_version__ = version;