@wzyjs/uis 0.3.29 → 0.3.30

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 (221) hide show
  1. package/dist/advanced/Com2Canvas/index.d.ts +8 -0
  2. package/dist/advanced/Com2Canvas/index.js +39 -0
  3. package/dist/advanced/Crud/components/CardList/index.d.ts +2 -0
  4. package/dist/advanced/Crud/components/CardList/index.js +90 -0
  5. package/dist/advanced/Crud/components/CreateUpdate/index.d.ts +2 -0
  6. package/dist/advanced/Crud/components/CreateUpdate/index.js +78 -0
  7. package/dist/advanced/Crud/components/ListTabs/index.d.ts +8 -0
  8. package/dist/advanced/Crud/components/ListTabs/index.js +7 -0
  9. package/dist/advanced/Crud/components/Provider/index.d.ts +7 -0
  10. package/dist/advanced/Crud/components/Provider/index.js +42 -0
  11. package/dist/advanced/Crud/components/QuickFilters/index.d.ts +8 -0
  12. package/dist/advanced/Crud/components/QuickFilters/index.js +20 -0
  13. package/dist/advanced/Crud/components/Remove/index.d.ts +2 -0
  14. package/dist/advanced/Crud/components/Remove/index.js +18 -0
  15. package/dist/advanced/Crud/components/index.d.ts +6 -0
  16. package/dist/advanced/Crud/components/index.js +6 -0
  17. package/dist/advanced/Crud/hooks/index.d.ts +5 -0
  18. package/dist/advanced/Crud/hooks/index.js +5 -0
  19. package/dist/advanced/Crud/hooks/useColumns.d.ts +11 -0
  20. package/dist/advanced/Crud/hooks/useColumns.js +111 -0
  21. package/dist/advanced/Crud/hooks/useList.d.ts +12 -0
  22. package/dist/advanced/Crud/hooks/useList.js +53 -0
  23. package/dist/advanced/Crud/hooks/useListFilters.d.ts +11 -0
  24. package/dist/advanced/Crud/hooks/useListFilters.js +159 -0
  25. package/dist/advanced/Crud/hooks/useOrderable.d.ts +15 -0
  26. package/dist/advanced/Crud/hooks/useOrderable.js +75 -0
  27. package/dist/advanced/Crud/hooks/useRequest.d.ts +13 -0
  28. package/dist/advanced/Crud/hooks/useRequest.js +27 -0
  29. package/dist/advanced/Crud/index.d.ts +3 -0
  30. package/dist/advanced/Crud/index.js +46 -0
  31. package/dist/advanced/Crud/types/index.d.ts +176 -0
  32. package/dist/advanced/Crud/types/index.js +1 -0
  33. package/dist/advanced/Crud/utils/index.d.ts +7 -0
  34. package/dist/advanced/Crud/utils/index.js +80 -0
  35. package/dist/advanced/Crud/utils/query.d.ts +3 -0
  36. package/dist/advanced/Crud/utils/query.js +34 -0
  37. package/dist/advanced/MindMap/context.d.ts +12 -0
  38. package/dist/advanced/MindMap/context.js +12 -0
  39. package/dist/advanced/MindMap/hooks/useAlignmentSnap.d.ts +15 -0
  40. package/dist/advanced/MindMap/hooks/useAlignmentSnap.js +164 -0
  41. package/dist/advanced/MindMap/hooks/useCopyPaste.d.ts +11 -0
  42. package/dist/advanced/MindMap/hooks/useCopyPaste.js +209 -0
  43. package/dist/advanced/MindMap/hooks/useDropToReparent.d.ts +21 -0
  44. package/dist/advanced/MindMap/hooks/useDropToReparent.js +216 -0
  45. package/dist/advanced/MindMap/hooks/useExpandCollapse.d.ts +18 -0
  46. package/dist/advanced/MindMap/hooks/useExpandCollapse.js +108 -0
  47. package/dist/advanced/MindMap/hooks/useMoveDescendants.d.ts +12 -0
  48. package/dist/advanced/MindMap/hooks/useMoveDescendants.js +98 -0
  49. package/dist/advanced/MindMap/hooks/useUndoRedo.d.ts +14 -0
  50. package/dist/advanced/MindMap/hooks/useUndoRedo.js +181 -0
  51. package/dist/advanced/MindMap/index.d.ts +29 -0
  52. package/dist/advanced/MindMap/index.js +52 -0
  53. package/dist/advanced/index.d.ts +5 -0
  54. package/dist/advanced/index.js +5 -0
  55. package/dist/antd/index.d.ts +6 -0
  56. package/dist/antd/index.js +5 -0
  57. package/dist/buttons/ButtonGroup/index.d.ts +8 -0
  58. package/dist/buttons/ButtonGroup/index.js +13 -0
  59. package/dist/buttons/ConfirmButton/index.d.ts +5 -0
  60. package/dist/buttons/ConfirmButton/index.js +9 -0
  61. package/dist/buttons/CopyButton/index.d.ts +6 -0
  62. package/dist/buttons/CopyButton/index.js +26 -0
  63. package/dist/buttons/DrawerButton/index.d.ts +6 -0
  64. package/dist/buttons/DrawerButton/index.js +13 -0
  65. package/dist/buttons/ProgressButton/index.css +63 -0
  66. package/dist/buttons/ProgressButton/index.d.ts +17 -0
  67. package/dist/buttons/ProgressButton/index.js +31 -0
  68. package/dist/buttons/SectorButton/index.d.ts +20 -0
  69. package/dist/buttons/SectorButton/index.js +130 -0
  70. package/dist/buttons/index.d.ts +6 -0
  71. package/dist/buttons/index.js +6 -0
  72. package/dist/display/CodeView/index.d.ts +26 -0
  73. package/dist/display/CodeView/index.js +60 -0
  74. package/dist/display/EnumTag/index.d.ts +12 -0
  75. package/dist/display/EnumTag/index.js +10 -0
  76. package/dist/display/HtmlDataRenderer/index.d.ts +6 -0
  77. package/dist/display/HtmlDataRenderer/index.js +15 -0
  78. package/dist/display/HtmlView/index.d.ts +6 -0
  79. package/dist/display/HtmlView/index.js +6 -0
  80. package/dist/display/IframePro/index.d.ts +8 -0
  81. package/dist/display/IframePro/index.js +24 -0
  82. package/dist/display/JsonSchemaRenderer/index.d.ts +11 -0
  83. package/dist/display/JsonSchemaRenderer/index.js +62 -0
  84. package/dist/display/JsonView/index.d.ts +3 -0
  85. package/dist/display/JsonView/index.js +7 -0
  86. package/dist/display/MarkdownView/index.d.ts +7 -0
  87. package/dist/display/MarkdownView/index.js +80 -0
  88. package/dist/display/MarkdownView/style.d.ts +1 -0
  89. package/{src/components/Markdown/style.ts → dist/display/MarkdownView/style.js} +1 -1
  90. package/dist/display/VideoPro/index.d.ts +9 -0
  91. package/dist/display/VideoPro/index.js +15 -0
  92. package/dist/display/index.d.ts +9 -0
  93. package/dist/display/index.js +9 -0
  94. package/dist/inputs/CheckboxButton/index.css +22 -0
  95. package/dist/inputs/CheckboxButton/index.d.ts +12 -0
  96. package/dist/inputs/CheckboxButton/index.js +9 -0
  97. package/dist/inputs/DateSwitcher/index.css +10 -0
  98. package/dist/inputs/DateSwitcher/index.d.ts +8 -0
  99. package/dist/inputs/DateSwitcher/index.js +29 -0
  100. package/dist/inputs/FetchSelect/index.d.ts +3 -0
  101. package/dist/inputs/FetchSelect/index.js +121 -0
  102. package/dist/inputs/FetchSelect/types.d.ts +33 -0
  103. package/dist/inputs/FetchSelect/types.js +1 -0
  104. package/dist/inputs/FetchSelect/utils.d.ts +21 -0
  105. package/dist/inputs/FetchSelect/utils.js +67 -0
  106. package/dist/inputs/FileUploader/index.d.ts +22 -0
  107. package/dist/inputs/FileUploader/index.js +79 -0
  108. package/dist/inputs/IconSelect/index.d.ts +89 -0
  109. package/dist/inputs/IconSelect/index.js +54 -0
  110. package/dist/inputs/ImageUploader/index.d.ts +12 -0
  111. package/dist/inputs/ImageUploader/index.js +192 -0
  112. package/dist/inputs/RadioButton/index.d.ts +15 -0
  113. package/dist/inputs/RadioButton/index.js +11 -0
  114. package/dist/inputs/RangeInput/index.d.ts +8 -0
  115. package/dist/inputs/RangeInput/index.js +17 -0
  116. package/dist/inputs/TextInput/index.d.ts +6 -0
  117. package/dist/inputs/TextInput/index.js +30 -0
  118. package/dist/inputs/index.d.ts +9 -0
  119. package/dist/inputs/index.js +9 -0
  120. package/dist/layout/DragSort/index.d.ts +16 -0
  121. package/dist/layout/DragSort/index.js +12 -0
  122. package/dist/layout/FoldCard/index.d.ts +9 -0
  123. package/dist/layout/FoldCard/index.js +69 -0
  124. package/dist/layout/PageBase/index.d.ts +6 -0
  125. package/dist/layout/PageBase/index.js +6 -0
  126. package/dist/layout/ResizableGridLayout/index.d.ts +11 -0
  127. package/dist/layout/ResizableGridLayout/index.js +13 -0
  128. package/dist/layout/SideMenu/index.d.ts +27 -0
  129. package/dist/layout/SideMenu/index.js +40 -0
  130. package/dist/layout/TabsPro/index.d.ts +9 -0
  131. package/dist/layout/TabsPro/index.js +87 -0
  132. package/dist/layout/index.d.ts +6 -0
  133. package/dist/layout/index.js +6 -0
  134. package/dist/web.d.ts +6 -0
  135. package/dist/web.js +6 -0
  136. package/package.json +28 -11
  137. package/src/antd/form/CheckboxButton/index.module.scss +0 -24
  138. package/src/antd/form/CheckboxButton/index.tsx +0 -31
  139. package/src/antd/form/FileUploader/index.tsx +0 -163
  140. package/src/antd/form/RadioButton/index.tsx +0 -32
  141. package/src/antd/form/Upload/index.tsx +0 -65
  142. package/src/antd/form/UploadImage/index.tsx +0 -338
  143. package/src/antd/form/index.ts +0 -6
  144. package/src/antd/index.ts +0 -46
  145. package/src/antd/pro/FoldCard/index.tsx +0 -131
  146. package/src/antd/pro/RangeInput/index.tsx +0 -46
  147. package/src/antd/pro/Tabs/index.tsx +0 -135
  148. package/src/antd/pro/Typography/components/String.tsx +0 -72
  149. package/src/antd/pro/Typography/index.tsx +0 -9
  150. package/src/antd/pro/buttons/ButtonGroup.tsx +0 -26
  151. package/src/antd/pro/buttons/ConfirmButton.tsx +0 -24
  152. package/src/antd/pro/buttons/CopyButton.tsx +0 -47
  153. package/src/antd/pro/buttons/DrawerButton.tsx +0 -37
  154. package/src/antd/pro/buttons/index.tsx +0 -4
  155. package/src/antd/pro/index.ts +0 -5
  156. package/src/components/BottomBar/index.tsx +0 -28
  157. package/src/components/CodeView/index.tsx +0 -85
  158. package/src/components/Collapse/index.tsx +0 -26
  159. package/src/components/Com2Canvas/index.tsx +0 -60
  160. package/src/components/CompileHtml/index.tsx +0 -26
  161. package/src/components/Crud/components/CardList/index.tsx +0 -174
  162. package/src/components/Crud/components/CreateUpdate/index.tsx +0 -179
  163. package/src/components/Crud/components/Provider/index.tsx +0 -83
  164. package/src/components/Crud/components/Remove/index.tsx +0 -56
  165. package/src/components/Crud/components/index.ts +0 -4
  166. package/src/components/Crud/hooks/index.ts +0 -4
  167. package/src/components/Crud/hooks/useColumns.tsx +0 -169
  168. package/src/components/Crud/hooks/useList.ts +0 -65
  169. package/src/components/Crud/hooks/useOrderable.tsx +0 -107
  170. package/src/components/Crud/hooks/useRequest.ts +0 -41
  171. package/src/components/Crud/index.tsx +0 -91
  172. package/src/components/Crud/types/index.ts +0 -188
  173. package/src/components/Crud/utils/index.ts +0 -87
  174. package/src/components/DateSwitcher/index.module.scss +0 -10
  175. package/src/components/DateSwitcher/index.tsx +0 -75
  176. package/src/components/DownloadLink/index.tsx +0 -36
  177. package/src/components/DragSort/index.tsx +0 -77
  178. package/src/components/DynamicSelect/index.tsx +0 -76
  179. package/src/components/DynamicSelect/types.ts +0 -8
  180. package/src/components/DynamicSelect/utils.ts +0 -47
  181. package/src/components/EnumTag/index.tsx +0 -24
  182. package/src/components/FetchSelect/index.tsx +0 -57
  183. package/src/components/FormPro/index.tsx +0 -28
  184. package/src/components/GroupLayout/index.tsx +0 -45
  185. package/src/components/HtmlPro/index.tsx +0 -18
  186. package/src/components/IframePro/index.tsx +0 -52
  187. package/src/components/JsonRenderer/index.tsx +0 -114
  188. package/src/components/JsonView/index.tsx +0 -21
  189. package/src/components/Markdown/index.tsx +0 -152
  190. package/src/components/MindMap/context.tsx +0 -29
  191. package/src/components/MindMap/hooks/useAlignmentSnap.ts +0 -220
  192. package/src/components/MindMap/hooks/useCopyPaste.ts +0 -272
  193. package/src/components/MindMap/hooks/useDropToReparent.ts +0 -288
  194. package/src/components/MindMap/hooks/useExpandCollapse.ts +0 -146
  195. package/src/components/MindMap/hooks/useMoveDescendants.ts +0 -136
  196. package/src/components/MindMap/hooks/useUndoRedo.ts +0 -232
  197. package/src/components/MindMap/index.tsx +0 -117
  198. package/src/components/MultiImageDisplay/index.tsx +0 -63
  199. package/src/components/ProgressButton/index.module.scss +0 -65
  200. package/src/components/ProgressButton/index.tsx +0 -96
  201. package/src/components/SectorButton/index.tsx +0 -247
  202. package/src/components/TextInput/index.tsx +0 -61
  203. package/src/components/TimelineBar/components/CurrentWeekHighlight/index.tsx +0 -64
  204. package/src/components/TimelineBar/components/Guides/index.tsx +0 -61
  205. package/src/components/TimelineBar/components/Ticks/index.tsx +0 -56
  206. package/src/components/TimelineBar/components/TodayIndicator/index.tsx +0 -54
  207. package/src/components/TimelineBar/components/index.ts +0 -4
  208. package/src/components/TimelineBar/const.ts +0 -3
  209. package/src/components/TimelineBar/hooks/index.ts +0 -5
  210. package/src/components/TimelineBar/hooks/useHighlightRange.ts +0 -21
  211. package/src/components/TimelineBar/hooks/useMonthGuides.ts +0 -40
  212. package/src/components/TimelineBar/hooks/useTickValues.ts +0 -18
  213. package/src/components/TimelineBar/hooks/useVisibleRange.ts +0 -43
  214. package/src/components/TimelineBar/hooks/useWeekGuides.ts +0 -39
  215. package/src/components/TimelineBar/index.tsx +0 -63
  216. package/src/components/TimelineBar/utils.ts +0 -27
  217. package/src/components/Video/index.tsx +0 -37
  218. package/src/components/index.ts +0 -26
  219. package/src/rn.ts +0 -1
  220. package/src/rns/index.ts +0 -0
  221. package/src/web.ts +0 -2
@@ -0,0 +1,15 @@
1
+ import { type ReactElement } from 'react';
2
+ import type { NodeChange, Node, Viewport } from '@xyflow/react';
3
+ interface UseAlignmentSnapParams<TNodeData extends Record<string, unknown>> {
4
+ enabled?: boolean;
5
+ nodes: Array<Node<TNodeData>>;
6
+ onNodesChange?: (changes: Array<NodeChange<Node<TNodeData>>>) => void;
7
+ snapThreshold?: number;
8
+ }
9
+ interface UseAlignmentSnapResult<TNodeData extends Record<string, unknown>> {
10
+ helperLines: ReactElement | null;
11
+ onMove: (_event: unknown, viewport: Viewport) => void;
12
+ onNodesChange: (changes: Array<NodeChange<Node<TNodeData>>>) => void;
13
+ }
14
+ export declare const useAlignmentSnap: <TNodeData extends Record<string, unknown>>(params: UseAlignmentSnapParams<TNodeData>) => UseAlignmentSnapResult<TNodeData>;
15
+ export {};
@@ -0,0 +1,164 @@
1
+ 'use client';
2
+ import { Fragment, createElement, useCallback, useMemo, useState } from 'react';
3
+ const getNodeSize = (node) => {
4
+ const width = node.measured?.width ?? node.width ?? 0;
5
+ const height = node.measured?.height ?? node.height ?? 0;
6
+ return { width, height };
7
+ };
8
+ export const useAlignmentSnap = (params) => {
9
+ const { enabled = false, nodes, onNodesChange: onNodesChangeExternal, snapThreshold = 6 } = params;
10
+ const [viewport, setViewport] = useState({ x: 0, y: 0, zoom: 1 });
11
+ // 保存对齐线在画布坐标下的位置(未做 viewport 变换)
12
+ const [helperLines, setHelperLines] = useState({ x: null, y: null });
13
+ const onMove = useCallback((_event, nextViewport) => {
14
+ setViewport(nextViewport);
15
+ }, []);
16
+ const onNodesChange = useCallback((changes) => {
17
+ if (!onNodesChangeExternal) {
18
+ return;
19
+ }
20
+ if (!enabled) {
21
+ setHelperLines({ x: null, y: null });
22
+ onNodesChangeExternal(changes);
23
+ return;
24
+ }
25
+ // 只在 position change 上参与吸附,避免对 selection 等变更产生副作用
26
+ const positionChange = changes.find(change => change.type === 'position');
27
+ if (positionChange?.type !== 'position' || !positionChange.position) {
28
+ onNodesChangeExternal(changes);
29
+ return;
30
+ }
31
+ const dragging = positionChange.dragging === true;
32
+ if (!dragging) {
33
+ setHelperLines({ x: null, y: null });
34
+ }
35
+ const baseNode = nodes.find(item => item.id === positionChange.id);
36
+ if (!baseNode) {
37
+ onNodesChangeExternal(changes);
38
+ return;
39
+ }
40
+ const selfSize = getNodeSize(baseNode);
41
+ if (!selfSize.width || !selfSize.height) {
42
+ onNodesChangeExternal(changes);
43
+ return;
44
+ }
45
+ // 以“边/中/边”作为候选锚点,计算与其他节点锚点的最小距离
46
+ const selfX = positionChange.position.x;
47
+ const selfY = positionChange.position.y;
48
+ const selfXAnchors = [
49
+ { point: selfX, shift: 0 },
50
+ { point: selfX + selfSize.width / 2, shift: selfSize.width / 2 },
51
+ { point: selfX + selfSize.width, shift: selfSize.width },
52
+ ];
53
+ const selfYAnchors = [
54
+ { point: selfY, shift: 0 },
55
+ { point: selfY + selfSize.height / 2, shift: selfSize.height / 2 },
56
+ { point: selfY + selfSize.height, shift: selfSize.height },
57
+ ];
58
+ let bestXDiff = Number.POSITIVE_INFINITY;
59
+ let bestYDiff = Number.POSITIVE_INFINITY;
60
+ let bestXLine = null;
61
+ let bestYLine = null;
62
+ let bestXOffset = 0;
63
+ let bestYOffset = 0;
64
+ nodes.forEach(other => {
65
+ if (other.id === positionChange.id) {
66
+ return;
67
+ }
68
+ const otherSize = getNodeSize(other);
69
+ if (!otherSize.width || !otherSize.height) {
70
+ return;
71
+ }
72
+ const otherX = other.position.x;
73
+ const otherY = other.position.y;
74
+ const otherXAnchors = [otherX, otherX + otherSize.width / 2, otherX + otherSize.width];
75
+ const otherYAnchors = [otherY, otherY + otherSize.height / 2, otherY + otherSize.height];
76
+ for (const selfAnchor of selfXAnchors) {
77
+ for (const otherAnchor of otherXAnchors) {
78
+ const diffX = Math.abs(selfAnchor.point - otherAnchor);
79
+ if (diffX <= snapThreshold && diffX < bestXDiff) {
80
+ bestXDiff = diffX;
81
+ bestXLine = otherAnchor;
82
+ bestXOffset = otherAnchor - selfAnchor.point;
83
+ }
84
+ }
85
+ }
86
+ for (const selfAnchor of selfYAnchors) {
87
+ for (const otherAnchor of otherYAnchors) {
88
+ const diffY = Math.abs(selfAnchor.point - otherAnchor);
89
+ if (diffY <= snapThreshold && diffY < bestYDiff) {
90
+ bestYDiff = diffY;
91
+ bestYLine = otherAnchor;
92
+ bestYOffset = otherAnchor - selfAnchor.point;
93
+ }
94
+ }
95
+ }
96
+ });
97
+ if (dragging) {
98
+ setHelperLines({ x: bestXLine, y: bestYLine });
99
+ }
100
+ // 没命中阈值则完全透传,避免抖动
101
+ if (bestXLine === null && bestYLine === null) {
102
+ onNodesChangeExternal(changes);
103
+ return;
104
+ }
105
+ const nextX = bestXLine === null ? selfX : selfX + bestXOffset;
106
+ const nextY = bestYLine === null ? selfY : selfY + bestYOffset;
107
+ // 通过“改写 position change”的方式实现吸附,避免与外部受控状态冲突
108
+ const nextChanges = changes.map(change => {
109
+ if (change.type !== 'position' || change.id !== positionChange.id || !change.position) {
110
+ return change;
111
+ }
112
+ if (change.position.x === nextX && change.position.y === nextY) {
113
+ return change;
114
+ }
115
+ return {
116
+ ...change,
117
+ position: { x: nextX, y: nextY },
118
+ };
119
+ });
120
+ onNodesChangeExternal(nextChanges);
121
+ }, [enabled, nodes, onNodesChangeExternal, snapThreshold]);
122
+ // 将画布坐标转换为屏幕坐标,用于 absolute 定位对齐线
123
+ const helperLineX = useMemo(() => {
124
+ if (helperLines.x === null) {
125
+ return null;
126
+ }
127
+ return helperLines.x * viewport.zoom + viewport.x;
128
+ }, [helperLines.x, viewport.x, viewport.zoom]);
129
+ const helperLineY = useMemo(() => {
130
+ if (helperLines.y === null) {
131
+ return null;
132
+ }
133
+ return helperLines.y * viewport.zoom + viewport.y;
134
+ }, [helperLines.y, viewport.y, viewport.zoom]);
135
+ const helperLinesElement = useMemo(() => {
136
+ if (!enabled) {
137
+ return null;
138
+ }
139
+ const elements = [];
140
+ if (helperLineX !== null) {
141
+ elements.push(createElement('div', {
142
+ key: 'helper-line-x',
143
+ className: 'pointer-events-none absolute top-0 h-full w-px bg-sky-500',
144
+ style: { left: helperLineX },
145
+ }));
146
+ }
147
+ if (helperLineY !== null) {
148
+ elements.push(createElement('div', {
149
+ key: 'helper-line-y',
150
+ className: 'pointer-events-none absolute left-0 w-full h-px bg-sky-500',
151
+ style: { top: helperLineY },
152
+ }));
153
+ }
154
+ if (elements.length === 0) {
155
+ return null;
156
+ }
157
+ return createElement(Fragment, null, ...elements);
158
+ }, [enabled, helperLineX, helperLineY]);
159
+ return {
160
+ helperLines: helperLinesElement,
161
+ onMove,
162
+ onNodesChange,
163
+ };
164
+ };
@@ -0,0 +1,11 @@
1
+ import type { Edge, EdgeChange, Node, NodeChange } from '@xyflow/react';
2
+ interface UseCopyPasteParams<TNodeData extends Record<string, unknown>> {
3
+ enabled?: boolean;
4
+ nodes: Array<Node<TNodeData>>;
5
+ edges: Edge[];
6
+ onNodesChange?: (changes: Array<NodeChange<Node<TNodeData>>>) => void;
7
+ onEdgesChange?: (changes: Array<EdgeChange<Edge>>) => void;
8
+ pasteOffset?: number;
9
+ }
10
+ export declare const useCopyPaste: <TNodeData extends Record<string, unknown>>(params: UseCopyPasteParams<TNodeData>) => void;
11
+ export {};
@@ -0,0 +1,209 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
3
+ const CLIPBOARD_MIME = 'application/x-mindmap+json';
4
+ const getDescendantIdSet = (edges, rootIds) => {
5
+ const sourceToTargets = new Map();
6
+ edges.forEach(edge => {
7
+ const prev = sourceToTargets.get(edge.source) ?? [];
8
+ sourceToTargets.set(edge.source, [...prev, edge.target]);
9
+ });
10
+ const visited = new Set();
11
+ const result = new Set();
12
+ const queue = [...rootIds];
13
+ rootIds.forEach(id => {
14
+ visited.add(id);
15
+ result.add(id);
16
+ });
17
+ while (queue.length > 0) {
18
+ const id = queue.shift();
19
+ if (!id) {
20
+ continue;
21
+ }
22
+ const targets = sourceToTargets.get(id) ?? [];
23
+ for (const targetId of targets) {
24
+ if (visited.has(targetId)) {
25
+ continue;
26
+ }
27
+ visited.add(targetId);
28
+ result.add(targetId);
29
+ queue.push(targetId);
30
+ }
31
+ }
32
+ return result;
33
+ };
34
+ const getCrypto = () => {
35
+ const anyGlobal = globalThis;
36
+ return anyGlobal.crypto;
37
+ };
38
+ const createId = () => {
39
+ const crypto = getCrypto();
40
+ if (crypto?.randomUUID) {
41
+ return crypto.randomUUID();
42
+ }
43
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
44
+ };
45
+ const clone = (value) => {
46
+ const anyGlobal = globalThis;
47
+ if (typeof anyGlobal.structuredClone === 'function') {
48
+ return anyGlobal.structuredClone(value);
49
+ }
50
+ return JSON.parse(JSON.stringify(value));
51
+ };
52
+ const safeParseClipboard = (text) => {
53
+ try {
54
+ const parsed = JSON.parse(text);
55
+ if (parsed?.t !== 'mindmap' || parsed?.v !== 1) {
56
+ return null;
57
+ }
58
+ const payload = parsed.payload;
59
+ if (!payload?.nodes?.length) {
60
+ return null;
61
+ }
62
+ return payload;
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ };
68
+ export const useCopyPaste = (params) => {
69
+ const { enabled = false, nodes, edges, onNodesChange, onEdgesChange, pasteOffset = 24, } = params;
70
+ const selectedNodes = useMemo(() => {
71
+ return nodes.filter(node => node.selected);
72
+ }, [nodes]);
73
+ const copyIdSet = useMemo(() => {
74
+ if (selectedNodes.length === 0) {
75
+ return new Set();
76
+ }
77
+ const rootIds = selectedNodes.map(n => n.id);
78
+ return getDescendantIdSet(edges, rootIds);
79
+ }, [edges, selectedNodes]);
80
+ const nodesToCopy = useMemo(() => {
81
+ if (copyIdSet.size === 0) {
82
+ return [];
83
+ }
84
+ return nodes.filter(node => copyIdSet.has(node.id));
85
+ }, [copyIdSet, nodes]);
86
+ const edgesToCopy = useMemo(() => {
87
+ if (copyIdSet.size === 0) {
88
+ return [];
89
+ }
90
+ return edges.filter(edge => copyIdSet.has(edge.source) && copyIdSet.has(edge.target));
91
+ }, [copyIdSet, edges]);
92
+ const pasteIndexRef = useRef(0);
93
+ const isEditableTarget = useCallback((target) => {
94
+ const el = target;
95
+ if (!el) {
96
+ return false;
97
+ }
98
+ const tag = el.tagName?.toLowerCase();
99
+ if (tag === 'input' || tag === 'textarea') {
100
+ return true;
101
+ }
102
+ return el.isContentEditable;
103
+ }, []);
104
+ const handleCopy = useCallback((event) => {
105
+ if (!enabled) {
106
+ return;
107
+ }
108
+ if (isEditableTarget(event.target)) {
109
+ return;
110
+ }
111
+ if (nodesToCopy.length === 0) {
112
+ return;
113
+ }
114
+ const payload = {
115
+ nodes: clone(nodesToCopy),
116
+ edges: clone(edgesToCopy),
117
+ };
118
+ const clipboardData = {
119
+ t: 'mindmap',
120
+ v: 1,
121
+ payload,
122
+ };
123
+ const text = JSON.stringify(clipboardData);
124
+ if (!event.clipboardData) {
125
+ return;
126
+ }
127
+ event.preventDefault();
128
+ event.clipboardData.setData(CLIPBOARD_MIME, text);
129
+ event.clipboardData.setData('text/plain', text);
130
+ }, [enabled, edgesToCopy, isEditableTarget, nodesToCopy]);
131
+ const pastePayload = useCallback((payload) => {
132
+ if (!onNodesChange) {
133
+ return;
134
+ }
135
+ pasteIndexRef.current += 1;
136
+ const shift = pasteOffset * pasteIndexRef.current;
137
+ const idMap = new Map();
138
+ const nextNodes = payload.nodes.map(node => {
139
+ const nextId = createId();
140
+ idMap.set(node.id, nextId);
141
+ const nextNode = clone(node);
142
+ nextNode.id = nextId;
143
+ nextNode.position = {
144
+ x: nextNode.position.x + shift,
145
+ y: nextNode.position.y + shift,
146
+ };
147
+ nextNode.selected = true;
148
+ delete nextNode.positionAbsolute;
149
+ delete nextNode.measured;
150
+ return nextNode;
151
+ });
152
+ const nodeChanges = nextNodes.map(item => ({
153
+ type: 'add',
154
+ item,
155
+ }));
156
+ onNodesChange(nodeChanges);
157
+ if (!onEdgesChange || payload.edges.length === 0) {
158
+ return;
159
+ }
160
+ const nextEdges = payload.edges
161
+ .map(edge => {
162
+ const nextSource = idMap.get(edge.source);
163
+ const nextTarget = idMap.get(edge.target);
164
+ if (!nextSource || !nextTarget) {
165
+ return null;
166
+ }
167
+ const nextEdge = clone(edge);
168
+ nextEdge.id = createId();
169
+ nextEdge.source = nextSource;
170
+ nextEdge.target = nextTarget;
171
+ return nextEdge;
172
+ })
173
+ .filter((e) => !!e);
174
+ if (nextEdges.length === 0) {
175
+ return;
176
+ }
177
+ const edgeChanges = nextEdges.map(item => ({
178
+ type: 'add',
179
+ item,
180
+ }));
181
+ onEdgesChange(edgeChanges);
182
+ }, [onEdgesChange, onNodesChange, pasteOffset]);
183
+ const handlePaste = useCallback((event) => {
184
+ if (!enabled) {
185
+ return;
186
+ }
187
+ if (isEditableTarget(event.target)) {
188
+ return;
189
+ }
190
+ const text = event.clipboardData?.getData(CLIPBOARD_MIME) || event.clipboardData?.getData('text/plain') || '';
191
+ const payloadFromClipboard = text ? safeParseClipboard(text) : null;
192
+ if (!payloadFromClipboard) {
193
+ return;
194
+ }
195
+ event.preventDefault();
196
+ pastePayload(payloadFromClipboard);
197
+ }, [enabled, isEditableTarget, pastePayload]);
198
+ useEffect(() => {
199
+ if (!enabled) {
200
+ return;
201
+ }
202
+ document.addEventListener('copy', handleCopy);
203
+ document.addEventListener('paste', handlePaste);
204
+ return () => {
205
+ document.removeEventListener('copy', handleCopy);
206
+ document.removeEventListener('paste', handlePaste);
207
+ };
208
+ }, [enabled, handleCopy, handlePaste]);
209
+ };
@@ -0,0 +1,21 @@
1
+ import type { Edge, EdgeChange, Node, NodeChange } from '@xyflow/react';
2
+ interface UseDropToReparentParams<TNodeData extends Record<string, unknown>> {
3
+ enabled?: boolean;
4
+ nodes: Array<Node<TNodeData>>;
5
+ edges: Edge[];
6
+ dropHighlightClassName?: string;
7
+ canDropOnNode?: (params: {
8
+ draggingNode: Node<TNodeData>;
9
+ targetNode: Node<TNodeData>;
10
+ }) => boolean;
11
+ onNodesChange?: (changes: Array<NodeChange<Node<TNodeData>>>) => void;
12
+ onEdgesChange?: (changes: Array<EdgeChange<Edge>>) => void;
13
+ }
14
+ interface UseDropToReparentResult<TNodeData extends Record<string, unknown>> {
15
+ onNodeDrag: (_event: unknown, node: Node<TNodeData>) => void;
16
+ onNodeDragStop: (_event: unknown, node: Node<TNodeData>) => void;
17
+ draggingNodeId: string | null;
18
+ dropTargetNodeId: string | null;
19
+ }
20
+ export declare const useDropToReparent: <TNodeData extends Record<string, unknown>>(params: UseDropToReparentParams<TNodeData>) => UseDropToReparentResult<TNodeData>;
21
+ export {};
@@ -0,0 +1,216 @@
1
+ 'use client';
2
+ import { useCallback, useMemo, useRef, useState } from 'react';
3
+ const defaultDropHighlightClassName = 'ring-2 ring-sky-500';
4
+ const normalizeClassName = (className) => {
5
+ return (className ?? '').split(/\s+/).map(item => item.trim()).filter(Boolean);
6
+ };
7
+ const withClassTokens = (className, tokens, enabled) => {
8
+ const list = normalizeClassName(className);
9
+ const tokenSet = new Set(list);
10
+ if (enabled) {
11
+ tokens.forEach(token => tokenSet.add(token));
12
+ }
13
+ else {
14
+ tokens.forEach(token => tokenSet.delete(token));
15
+ }
16
+ const next = Array.from(tokenSet).join(' ');
17
+ return next.length > 0 ? next : undefined;
18
+ };
19
+ const getNodeSize = (node) => {
20
+ const width = node.measured?.width ?? node.width ?? 0;
21
+ const height = node.measured?.height ?? node.height ?? 0;
22
+ return { width, height };
23
+ };
24
+ const isPointInRect = (point, rect) => {
25
+ return point.x >= rect.x && point.x <= rect.x + rect.w && point.y >= rect.y && point.y <= rect.y + rect.h;
26
+ };
27
+ const getDescendants = (edges, rootId) => {
28
+ const sourceToTargets = new Map();
29
+ edges.forEach(edge => {
30
+ const prev = sourceToTargets.get(edge.source) ?? [];
31
+ sourceToTargets.set(edge.source, [...prev, edge.target]);
32
+ });
33
+ const visited = new Set([rootId]);
34
+ const result = new Set();
35
+ const queue = [rootId];
36
+ while (queue.length > 0) {
37
+ const currentId = queue.shift();
38
+ if (!currentId) {
39
+ continue;
40
+ }
41
+ const targets = sourceToTargets.get(currentId) ?? [];
42
+ for (const targetId of targets) {
43
+ if (visited.has(targetId)) {
44
+ continue;
45
+ }
46
+ visited.add(targetId);
47
+ result.add(targetId);
48
+ queue.push(targetId);
49
+ }
50
+ }
51
+ return result;
52
+ };
53
+ const pickDropTargetId = (nodes, draggingNode, edges, canDropOnNode) => {
54
+ const draggingSize = getNodeSize(draggingNode);
55
+ if (!draggingSize.width || !draggingSize.height) {
56
+ return null;
57
+ }
58
+ const descendants = getDescendants(edges, draggingNode.id);
59
+ const center = {
60
+ x: draggingNode.position.x + draggingSize.width / 2,
61
+ y: draggingNode.position.y + draggingSize.height / 2,
62
+ };
63
+ let bestId = null;
64
+ let bestDist = Number.POSITIVE_INFINITY;
65
+ nodes.forEach(node => {
66
+ if (node.id === draggingNode.id) {
67
+ return;
68
+ }
69
+ if (descendants.has(node.id)) {
70
+ return;
71
+ }
72
+ if (canDropOnNode && !canDropOnNode({ draggingNode, targetNode: node })) {
73
+ return;
74
+ }
75
+ const size = getNodeSize(node);
76
+ if (!size.width || !size.height) {
77
+ return;
78
+ }
79
+ const rect = { x: node.position.x, y: node.position.y, w: size.width, h: size.height };
80
+ if (!isPointInRect(center, rect)) {
81
+ return;
82
+ }
83
+ const nodeCenterX = node.position.x + size.width / 2;
84
+ const nodeCenterY = node.position.y + size.height / 2;
85
+ const dx = center.x - nodeCenterX;
86
+ const dy = center.y - nodeCenterY;
87
+ const dist = (dx * dx) + (dy * dy);
88
+ if (dist < bestDist) {
89
+ bestDist = dist;
90
+ bestId = node.id;
91
+ }
92
+ });
93
+ return bestId;
94
+ };
95
+ export const useDropToReparent = (params) => {
96
+ const { enabled = true, nodes, edges, dropHighlightClassName = defaultDropHighlightClassName, canDropOnNode, onNodesChange, onEdgesChange, } = params;
97
+ const dropTargetIdRef = useRef(null);
98
+ const [draggingNodeId, setDraggingNodeId] = useState(null);
99
+ const [dropTargetNodeId, setDropTargetNodeId] = useState(null);
100
+ const dropHighlightClassTokens = useMemo(() => {
101
+ return normalizeClassName(dropHighlightClassName);
102
+ }, [dropHighlightClassName]);
103
+ const nodeById = useMemo(() => {
104
+ return new Map(nodes.map(node => [node.id, node]));
105
+ }, [nodes]);
106
+ const setDropTargetId = useCallback((nextId) => {
107
+ const prevId = dropTargetIdRef.current;
108
+ if (prevId === nextId) {
109
+ return;
110
+ }
111
+ dropTargetIdRef.current = nextId;
112
+ setDropTargetNodeId(nextId);
113
+ if (!onNodesChange) {
114
+ return;
115
+ }
116
+ const changes = [];
117
+ if (prevId) {
118
+ const prevNode = nodeById.get(prevId);
119
+ if (prevNode) {
120
+ changes.push({
121
+ type: 'replace',
122
+ id: prevId,
123
+ item: {
124
+ ...prevNode,
125
+ className: withClassTokens(prevNode.className, dropHighlightClassTokens, false),
126
+ },
127
+ });
128
+ }
129
+ }
130
+ if (nextId) {
131
+ const nextNode = nodeById.get(nextId);
132
+ if (nextNode) {
133
+ changes.push({
134
+ type: 'replace',
135
+ id: nextId,
136
+ item: {
137
+ ...nextNode,
138
+ className: withClassTokens(nextNode.className, dropHighlightClassTokens, true),
139
+ },
140
+ });
141
+ }
142
+ }
143
+ if (changes.length > 0) {
144
+ onNodesChange(changes);
145
+ }
146
+ }, [dropHighlightClassTokens, nodeById, onNodesChange]);
147
+ const onNodeDrag = useCallback((_event, node) => {
148
+ setDraggingNodeId(node.id);
149
+ if (!enabled) {
150
+ setDropTargetId(null);
151
+ return;
152
+ }
153
+ const targetId = pickDropTargetId(nodes, node, edges, canDropOnNode);
154
+ setDropTargetId(targetId);
155
+ }, [canDropOnNode, edges, enabled, nodes, setDropTargetId]);
156
+ const onNodeDragStop = useCallback((_event, node) => {
157
+ const targetId = dropTargetIdRef.current;
158
+ setDropTargetId(null);
159
+ setDraggingNodeId(null);
160
+ if (!enabled) {
161
+ return;
162
+ }
163
+ if (!targetId) {
164
+ return;
165
+ }
166
+ if (!onEdgesChange) {
167
+ return;
168
+ }
169
+ const childId = node.id;
170
+ if (targetId === childId) {
171
+ return;
172
+ }
173
+ const descendants = getDescendants(edges, childId);
174
+ if (descendants.has(targetId)) {
175
+ return;
176
+ }
177
+ const targetNode = nodeById.get(targetId);
178
+ if (!targetNode) {
179
+ return;
180
+ }
181
+ if (canDropOnNode && !canDropOnNode({ draggingNode: node, targetNode })) {
182
+ return;
183
+ }
184
+ const nextEdgeId = `e-${targetId}-${childId}`;
185
+ const hasSameEdge = edges.some(edge => edge.source === targetId && edge.target === childId);
186
+ const incomingEdges = edges.filter(edge => edge.target === childId);
187
+ const edgeChanges = [];
188
+ incomingEdges.forEach(edge => {
189
+ if (edge.source === targetId && edge.target === childId) {
190
+ return;
191
+ }
192
+ edgeChanges.push({ type: 'remove', id: edge.id });
193
+ });
194
+ if (!hasSameEdge) {
195
+ edgeChanges.push({
196
+ type: 'add',
197
+ item: {
198
+ id: nextEdgeId,
199
+ source: targetId,
200
+ target: childId,
201
+ type: 'smoothstep',
202
+ },
203
+ });
204
+ }
205
+ if (edgeChanges.length === 0) {
206
+ return;
207
+ }
208
+ onEdgesChange(edgeChanges);
209
+ }, [canDropOnNode, edges, enabled, nodeById, onEdgesChange, setDropTargetId]);
210
+ return {
211
+ onNodeDrag,
212
+ onNodeDragStop,
213
+ draggingNodeId,
214
+ dropTargetNodeId,
215
+ };
216
+ };
@@ -0,0 +1,18 @@
1
+ import type { Edge, EdgeChange, Node, NodeChange } from '@xyflow/react';
2
+ interface GetCountsResult {
3
+ childCount: number;
4
+ descendantCount: number;
5
+ }
6
+ interface UseExpandCollapseParams<TNodeData extends Record<string, unknown>> {
7
+ nodes: Array<Node<TNodeData>>;
8
+ edges: Edge[];
9
+ onNodesChange?: (changes: Array<NodeChange<Node<TNodeData>>>) => void;
10
+ onEdgesChange?: (changes: Array<EdgeChange<Edge>>) => void;
11
+ }
12
+ interface UseExpandCollapseResult {
13
+ getCounts: (nodeId: string) => GetCountsResult;
14
+ expand: (nodeId: string) => void;
15
+ collapse: (nodeId: string) => void;
16
+ }
17
+ export declare const useExpandCollapse: <TNodeData extends Record<string, unknown>>(params: UseExpandCollapseParams<TNodeData>) => UseExpandCollapseResult;
18
+ export {};