@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.
- package/dist/cjs/Alert/alert.js +10 -4
- package/dist/cjs/Alert/style/index.less +3 -0
- package/dist/cjs/Anchor/anchor-item.js +6 -4
- package/dist/cjs/Anchor/anchor.js +38 -22
- package/dist/cjs/Anchor/demo/auto.d.ts +11 -0
- package/dist/cjs/Anchor/demo/auto.js +85 -0
- package/dist/cjs/Anchor/demo/basic.js +7 -1
- package/dist/cjs/Anchor/demo/controlled.js +42 -30
- package/dist/cjs/Anchor/interface.d.ts +2 -0
- package/dist/cjs/Anchor/style/index.less +2 -2
- package/dist/cjs/Button/button.d.ts +1 -1
- package/dist/cjs/Checkbox/checkbox-group.js +4 -1
- package/dist/cjs/Checkbox/demo/group.js +1 -0
- package/dist/cjs/Checkbox/interface.d.ts +5 -0
- package/dist/cjs/Checkbox/style/index.less +21 -1
- package/dist/cjs/Dropdown/demo/placement.js +2 -1
- package/dist/cjs/Dropdown/dropdown.js +3 -0
- package/dist/cjs/Dropdown/style/index.less +6 -0
- package/dist/cjs/Form/Field.js +33 -3
- package/dist/cjs/Form/Group.js +3 -2
- package/dist/cjs/Form/demo/form-table.js +1 -8
- package/dist/cjs/Form/demo/tooltip.d.ts +3 -0
- package/dist/cjs/Form/demo/tooltip.js +84 -0
- package/dist/cjs/Form/interface.d.ts +34 -1
- package/dist/cjs/Form/style/index.less +22 -7
- package/dist/cjs/Popconfirm/popconfirm.js +1 -1
- package/dist/cjs/Popconfirm/style/index.less +6 -0
- package/dist/cjs/Popover/popover.js +1 -1
- package/dist/cjs/Popover/style/index.less +6 -0
- package/dist/cjs/Progress/progress.js +2 -2
- package/dist/cjs/Radio/style/index.less +13 -6
- package/dist/cjs/Select/option.js +3 -2
- package/dist/cjs/Select/options.js +9 -1
- package/dist/cjs/Select/select.js +1 -0
- package/dist/cjs/Skeleton/demo/basic.js +1 -1
- package/dist/cjs/Space/interface.d.ts +4 -0
- package/dist/cjs/Space/space.js +36 -16
- package/dist/cjs/Splitter/demo/basic.js +1 -1
- package/dist/cjs/Splitter/demo/vertical.js +23 -1
- package/dist/cjs/Splitter/index.d.ts +1 -1
- package/dist/cjs/Splitter/interface.d.ts +9 -3
- package/dist/cjs/Splitter/splitter-handler.d.ts +1 -1
- package/dist/cjs/Splitter/splitter-handler.js +2 -1
- package/dist/cjs/Splitter/splitter.d.ts +2 -9
- package/dist/cjs/Splitter/splitter.js +53 -50
- package/dist/cjs/Splitter/style/index.less +21 -0
- package/dist/cjs/Table/demo/size.js +1 -1
- package/dist/cjs/Tabs/hooks/useTabScroll.d.ts +1 -1
- package/dist/cjs/Tabs/interface.d.ts +1 -1
- package/dist/cjs/Tabs/style/index.less +4 -0
- package/dist/cjs/Tabs/tab-header/index.js +56 -25
- package/dist/cjs/Tooltip/interface.d.ts +4 -0
- package/dist/cjs/Tooltip/style/index.less +8 -8
- package/dist/cjs/Tooltip/tooltip.js +11 -3
- package/dist/cjs/Tree/demo/draggable.d.ts +3 -0
- package/dist/cjs/Tree/demo/draggable.js +88 -0
- package/dist/cjs/Tree/demo/expanded.js +6 -6
- package/dist/cjs/Tree/index.d.ts +1 -0
- package/dist/cjs/Tree/index.js +3 -1
- package/dist/cjs/Tree/interface.d.ts +26 -0
- package/dist/cjs/Tree/style/index.less +25 -0
- package/dist/cjs/Tree/tree-node.js +38 -4
- package/dist/cjs/Tree/tree.d.ts +12 -1
- package/dist/cjs/Tree/tree.js +174 -18
- package/dist/cjs/Tree/utils/tree-tools.d.ts +21 -0
- package/dist/cjs/Tree/utils/tree-tools.js +88 -0
- package/dist/cjs/Trigger/style/index.less +17 -0
- package/dist/cjs/Trigger/trigger.js +3 -1
- package/dist/cjs/hooks/useDebounceFunction.d.ts +1 -1
- package/dist/cjs/hooks/useLayoutEffect.d.ts +1 -1
- package/dist/cjs/style/size.css +13 -4
- package/dist/cjs/style/themes.css +1 -1
- package/dist/cjs/style/variables.css +14 -5
- package/dist/cjs/utils/debounce.d.ts +6 -1
- package/dist/cjs/utils/debounce.js +9 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Alert/alert.js +24 -8
- package/dist/esm/Alert/style/index.less +3 -0
- package/dist/esm/Anchor/anchor-item.js +6 -3
- package/dist/esm/Anchor/anchor.js +38 -19
- package/dist/esm/Anchor/demo/auto.d.ts +11 -0
- package/dist/esm/Anchor/demo/auto.js +62 -0
- package/dist/esm/Anchor/demo/basic.js +3 -1
- package/dist/esm/Anchor/demo/controlled.js +14 -9
- package/dist/esm/Anchor/interface.d.ts +2 -0
- package/dist/esm/Anchor/style/index.less +2 -2
- package/dist/esm/Button/button.d.ts +1 -1
- package/dist/esm/Checkbox/checkbox-group.js +4 -2
- package/dist/esm/Checkbox/demo/group.js +1 -0
- package/dist/esm/Checkbox/interface.d.ts +5 -0
- package/dist/esm/Checkbox/style/index.less +21 -1
- package/dist/esm/Dropdown/demo/placement.js +2 -1
- package/dist/esm/Dropdown/dropdown.js +6 -1
- package/dist/esm/Dropdown/style/index.less +6 -0
- package/dist/esm/Form/Field.js +43 -5
- package/dist/esm/Form/Group.js +4 -2
- package/dist/esm/Form/demo/form-table.js +8 -6
- package/dist/esm/Form/demo/tooltip.d.ts +3 -0
- package/dist/esm/Form/demo/tooltip.js +59 -0
- package/dist/esm/Form/interface.d.ts +34 -1
- package/dist/esm/Form/style/index.less +22 -7
- package/dist/esm/Popconfirm/popconfirm.js +3 -1
- package/dist/esm/Popconfirm/style/index.less +6 -0
- package/dist/esm/Popover/popover.js +3 -1
- package/dist/esm/Popover/style/index.less +6 -0
- package/dist/esm/Progress/progress.js +5 -5
- package/dist/esm/Radio/style/index.less +13 -6
- package/dist/esm/Select/option.js +4 -2
- package/dist/esm/Select/options.js +5 -3
- package/dist/esm/Select/select.js +1 -0
- package/dist/esm/Skeleton/demo/basic.js +2 -8
- package/dist/esm/Space/interface.d.ts +4 -0
- package/dist/esm/Space/space.js +24 -13
- package/dist/esm/Splitter/demo/basic.js +2 -0
- package/dist/esm/Splitter/demo/vertical.js +13 -5
- package/dist/esm/Splitter/index.d.ts +1 -1
- package/dist/esm/Splitter/interface.d.ts +9 -3
- package/dist/esm/Splitter/splitter-handler.d.ts +1 -1
- package/dist/esm/Splitter/splitter-handler.js +3 -1
- package/dist/esm/Splitter/splitter.d.ts +2 -9
- package/dist/esm/Splitter/splitter.js +59 -56
- package/dist/esm/Splitter/style/index.less +21 -0
- package/dist/esm/Table/demo/size.js +2 -7
- package/dist/esm/Tabs/hooks/useTabScroll.d.ts +1 -1
- package/dist/esm/Tabs/interface.d.ts +1 -1
- package/dist/esm/Tabs/style/index.less +4 -0
- package/dist/esm/Tabs/tab-header/index.js +11 -5
- package/dist/esm/Tooltip/interface.d.ts +4 -0
- package/dist/esm/Tooltip/style/index.less +8 -8
- package/dist/esm/Tooltip/tooltip.js +14 -2
- package/dist/esm/Tree/demo/draggable.d.ts +3 -0
- package/dist/esm/Tree/demo/draggable.js +61 -0
- package/dist/esm/Tree/demo/expanded.js +6 -6
- package/dist/esm/Tree/index.d.ts +1 -0
- package/dist/esm/Tree/index.js +1 -0
- package/dist/esm/Tree/interface.d.ts +26 -0
- package/dist/esm/Tree/style/index.less +25 -0
- package/dist/esm/Tree/tree-node.js +29 -4
- package/dist/esm/Tree/tree.d.ts +12 -1
- package/dist/esm/Tree/tree.js +135 -4
- package/dist/esm/Tree/utils/tree-tools.d.ts +21 -0
- package/dist/esm/Tree/utils/tree-tools.js +128 -1
- package/dist/esm/Trigger/style/index.less +17 -0
- package/dist/esm/Trigger/trigger.js +4 -3
- package/dist/esm/hooks/useDebounceFunction.d.ts +1 -1
- package/dist/esm/hooks/useLayoutEffect.d.ts +1 -1
- package/dist/esm/style/size.css +13 -4
- package/dist/esm/style/themes.css +1 -1
- package/dist/esm/style/variables.css +14 -5
- package/dist/esm/utils/debounce.d.ts +6 -1
- package/dist/esm/utils/debounce.js +9 -2
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.css +2 -2
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +21 -23
- package/src/Alert/alert.tsx +10 -4
- package/src/Alert/style/index.less +3 -0
- package/src/Anchor/anchor-item.tsx +7 -2
- package/src/Anchor/anchor.tsx +49 -23
- package/src/Anchor/demo/auto.tsx +55 -0
- package/src/Anchor/demo/basic.tsx +7 -1
- package/src/Anchor/demo/controlled.tsx +18 -14
- package/src/Anchor/index.en-US.md +17 -0
- package/src/Anchor/index.zh-CN.md +17 -0
- package/src/Anchor/interface.ts +2 -0
- package/src/Anchor/style/index.less +2 -2
- package/src/Checkbox/checkbox-group.tsx +5 -1
- package/src/Checkbox/demo/group.tsx +1 -0
- package/src/Checkbox/interface.ts +5 -0
- package/src/Checkbox/style/index.less +21 -1
- package/src/Dropdown/demo/placement.tsx +1 -0
- package/src/Dropdown/dropdown.tsx +2 -0
- package/src/Dropdown/style/index.less +6 -0
- package/src/Form/Field.tsx +45 -3
- package/src/Form/Group.tsx +4 -1
- package/src/Form/demo/form-table.tsx +7 -7
- package/src/Form/demo/tooltip.tsx +72 -0
- package/src/Form/form-store.ts +1 -1
- package/src/Form/index.en-US.md +2 -0
- package/src/Form/index.zh-CN.md +83 -85
- package/src/Form/interface.ts +36 -1
- package/src/Form/style/index.less +22 -7
- package/src/Popconfirm/popconfirm.tsx +1 -1
- package/src/Popconfirm/style/index.less +6 -0
- package/src/Popover/popover.tsx +1 -1
- package/src/Popover/style/index.less +6 -0
- package/src/Progress/progress.tsx +2 -2
- package/src/Radio/style/index.less +13 -6
- package/src/Select/option.tsx +2 -1
- package/src/Select/options.tsx +7 -2
- package/src/Select/select.tsx +1 -0
- package/src/Skeleton/demo/basic.tsx +3 -3
- package/src/Space/interface.ts +4 -0
- package/src/Space/space.tsx +40 -16
- package/src/Splitter/demo/basic.tsx +2 -2
- package/src/Splitter/demo/vertical.tsx +19 -5
- package/src/Splitter/index.ts +5 -1
- package/src/Splitter/interface.ts +10 -3
- package/src/Splitter/splitter-handler.tsx +4 -2
- package/src/Splitter/splitter.tsx +55 -60
- package/src/Splitter/style/index.less +21 -0
- package/src/Table/demo/size.tsx +3 -3
- package/src/Table/index.zh-CN.md +50 -50
- package/src/Tabs/interface.ts +1 -1
- package/src/Tabs/style/index.less +4 -0
- package/src/Tabs/tab-header/index.tsx +26 -5
- package/src/Tooltip/index.en-US.md +1 -0
- package/src/Tooltip/index.zh-CN.md +1 -0
- package/src/Tooltip/interface.ts +4 -0
- package/src/Tooltip/style/index.less +8 -8
- package/src/Tooltip/tooltip.tsx +10 -1
- package/src/Tree/demo/draggable.tsx +58 -0
- package/src/Tree/demo/expanded.tsx +7 -7
- package/src/Tree/index.en-US.md +3 -0
- package/src/Tree/index.ts +1 -0
- package/src/Tree/index.zh-CN.md +3 -0
- package/src/Tree/interface.ts +28 -0
- package/src/Tree/style/index.less +25 -0
- package/src/Tree/tree-node.tsx +26 -0
- package/src/Tree/tree.tsx +183 -2
- package/src/Tree/utils/tree-tools.ts +107 -0
- package/src/Trigger/style/index.less +17 -0
- package/src/Trigger/trigger.tsx +3 -1
- package/src/style/size.css +13 -4
- package/src/utils/debounce.ts +22 -3
- 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
|
|
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 {
|
package/src/Trigger/trigger.tsx
CHANGED
|
@@ -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}
|
package/src/style/size.css
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
772
|
-
--yee-tooltip-arrow-height:
|
|
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;
|
package/src/utils/debounce.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|