@pisell/materials 1.8.54 → 1.8.56

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 (151) hide show
  1. package/build/lowcode/assets-daily.json +11 -11
  2. package/build/lowcode/assets-dev.json +2 -2
  3. package/build/lowcode/assets-prod.json +11 -11
  4. package/build/lowcode/async/view.js +1 -1
  5. package/build/lowcode/index.js +1 -1
  6. package/build/lowcode/meta.js +3 -3
  7. package/build/lowcode/preview.js +1 -1
  8. package/build/lowcode/render/default/async/view.js +1 -1
  9. package/build/lowcode/render/default/view.css +1 -1
  10. package/build/lowcode/render/default/view.js +37 -37
  11. package/build/lowcode/view.css +1 -1
  12. package/build/lowcode/view.js +37 -37
  13. package/es/components/buttonGroupPreview/index.js +8 -4
  14. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.js +207 -49
  15. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.less +33 -2
  16. package/es/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +50 -2
  17. package/es/components/pisellFloorMapLayout/components/EdgeLayer.js +252 -0
  18. package/es/components/pisellFloorMapLayout/components/EdgeLayer.less +11 -0
  19. package/es/components/pisellFloorMapLayout/components/EditableItemLayer.js +179 -29
  20. package/es/components/pisellFloorMapLayout/components/EditableItemLayer.less +0 -28
  21. package/es/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.js +131 -0
  22. package/es/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.less +65 -0
  23. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +5 -4
  24. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +12 -1
  25. package/es/components/pisellFloorMapLayout/components/FloorMapEditQuickActions.js +8 -1
  26. package/es/components/pisellFloorMapLayout/components/FloorMapOverview.js +16 -8
  27. package/es/components/pisellFloorMapLayout/components/FloorMapOverview.less +69 -17
  28. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.js +290 -147
  29. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.less +210 -11
  30. package/es/components/pisellFloorMapLayout/components/FloorMapToolbarViewAllModal.js +81 -0
  31. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.d.ts +8 -0
  32. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.js +21 -0
  33. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.less +74 -0
  34. package/es/components/pisellFloorMapLayout/components/ItemLayer.less +3 -1
  35. package/es/components/pisellFloorMapLayout/components/NodePortMarkers.js +70 -0
  36. package/es/components/pisellFloorMapLayout/components/NodePortMarkers.less +33 -0
  37. package/es/components/pisellFloorMapLayout/components/ViewControls.js +13 -5
  38. package/es/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +2 -0
  39. package/es/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -1
  40. package/es/components/pisellFloorMapLayout/floorMapLayoutConstants.js +1 -1
  41. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.js +340 -0
  42. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEdgeViewModel.js +102 -0
  43. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +244 -84
  44. package/es/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +54 -0
  45. package/es/components/pisellFloorMapLayout/hooks/useShopFloorPlanSubscription.js +7 -4
  46. package/es/components/pisellFloorMapLayout/index.d.ts +5 -1
  47. package/es/components/pisellFloorMapLayout/locales-ja.js +14 -2
  48. package/es/components/pisellFloorMapLayout/locales-pt.js +14 -2
  49. package/es/components/pisellFloorMapLayout/locales.js +161 -8
  50. package/es/components/pisellFloorMapLayout/types.d.ts +86 -4
  51. package/es/components/pisellFloorMapLayout/types.js +5 -1
  52. package/es/components/pisellFloorMapLayout/utils/alignSnap.js +170 -0
  53. package/es/components/pisellFloorMapLayout/utils/batchScenePlacementLayout.js +86 -0
  54. package/es/components/pisellFloorMapLayout/utils/edgeRouting.js +942 -0
  55. package/es/components/pisellFloorMapLayout/utils/floorMapCanvasDisplay.d.ts +7 -0
  56. package/es/components/pisellFloorMapLayout/utils/floorMapCanvasStage.js +23 -1
  57. package/es/components/pisellFloorMapLayout/utils/floorMapConfigMerge.js +11 -2
  58. package/es/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +20 -3
  59. package/es/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +11 -5
  60. package/es/components/pisellFloorMapLayout/utils/floorMapOverviewLayout.js +5 -3
  61. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.d.ts +20 -0
  62. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.js +62 -0
  63. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.d.ts +83 -0
  64. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.js +29 -0
  65. package/es/components/pisellFloorMapLayout/utils/floorMapToolbarPalette.js +51 -0
  66. package/es/components/productCard/cartSkuCard/components/BookingItem/index.js +14 -0
  67. package/es/components/productCard/cartSkuCard/components/BookingItem/index.less +32 -0
  68. package/es/components/productCard/cartSkuCard/components/resources/index.less +4 -0
  69. package/es/components/productCard/cartSkuCard/components/timeRange/index.js +1 -2
  70. package/es/components/productCard/cartSkuCard/index.js +17 -11
  71. package/es/components/productCard/lineItem/BookingLineItem.js +17 -6
  72. package/es/components/productCard/lineItem/components/Holders/index.js +51 -0
  73. package/es/components/productCard/lineItem/components/Holders/index.less +39 -0
  74. package/es/components/productCard/lineItem/components/Resources/index.js +45 -0
  75. package/es/components/productCard/lineItem/components/TimeRange/index.js +64 -0
  76. package/es/components/productCard/lineItem/index.js +5 -2
  77. package/es/components/productCard/lineItem/index.less +21 -10
  78. package/es/components/productCard/types.d.ts +1 -0
  79. package/es/index.d.ts +6 -2
  80. package/es/index.js +6 -2
  81. package/es/pisell-materials.tw.css +3 -0
  82. package/lib/components/buttonGroupPreview/index.js +8 -4
  83. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.js +206 -48
  84. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.less +33 -2
  85. package/lib/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +50 -2
  86. package/lib/components/pisellFloorMapLayout/components/EdgeLayer.js +255 -0
  87. package/lib/components/pisellFloorMapLayout/components/EdgeLayer.less +11 -0
  88. package/lib/components/pisellFloorMapLayout/components/EditableItemLayer.js +179 -29
  89. package/lib/components/pisellFloorMapLayout/components/EditableItemLayer.less +0 -28
  90. package/lib/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.js +134 -0
  91. package/lib/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.less +65 -0
  92. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +4 -3
  93. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +12 -1
  94. package/lib/components/pisellFloorMapLayout/components/FloorMapEditQuickActions.js +8 -1
  95. package/lib/components/pisellFloorMapLayout/components/FloorMapOverview.js +16 -8
  96. package/lib/components/pisellFloorMapLayout/components/FloorMapOverview.less +69 -17
  97. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.js +287 -144
  98. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.less +210 -11
  99. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbarViewAllModal.js +84 -0
  100. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.d.ts +8 -0
  101. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.js +23 -0
  102. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.less +74 -0
  103. package/lib/components/pisellFloorMapLayout/components/ItemLayer.less +3 -1
  104. package/lib/components/pisellFloorMapLayout/components/NodePortMarkers.js +73 -0
  105. package/lib/components/pisellFloorMapLayout/components/NodePortMarkers.less +33 -0
  106. package/lib/components/pisellFloorMapLayout/components/ViewControls.js +12 -4
  107. package/lib/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +2 -0
  108. package/lib/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -1
  109. package/lib/components/pisellFloorMapLayout/floorMapLayoutConstants.js +1 -1
  110. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.js +341 -0
  111. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEdgeViewModel.js +103 -0
  112. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +242 -82
  113. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +54 -0
  114. package/lib/components/pisellFloorMapLayout/hooks/useShopFloorPlanSubscription.js +7 -4
  115. package/lib/components/pisellFloorMapLayout/index.d.ts +5 -1
  116. package/lib/components/pisellFloorMapLayout/locales-ja.js +14 -2
  117. package/lib/components/pisellFloorMapLayout/locales-pt.js +14 -2
  118. package/lib/components/pisellFloorMapLayout/locales.js +161 -8
  119. package/lib/components/pisellFloorMapLayout/types.d.ts +86 -4
  120. package/lib/components/pisellFloorMapLayout/types.js +5 -0
  121. package/lib/components/pisellFloorMapLayout/utils/alignSnap.js +170 -0
  122. package/lib/components/pisellFloorMapLayout/utils/batchScenePlacementLayout.js +87 -0
  123. package/lib/components/pisellFloorMapLayout/utils/edgeRouting.js +956 -0
  124. package/lib/components/pisellFloorMapLayout/utils/floorMapCanvasDisplay.d.ts +7 -0
  125. package/lib/components/pisellFloorMapLayout/utils/floorMapCanvasStage.js +23 -0
  126. package/lib/components/pisellFloorMapLayout/utils/floorMapConfigMerge.js +11 -2
  127. package/lib/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +21 -2
  128. package/lib/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +11 -5
  129. package/lib/components/pisellFloorMapLayout/utils/floorMapOverviewLayout.js +5 -3
  130. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.d.ts +20 -0
  131. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.js +64 -0
  132. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.d.ts +83 -0
  133. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.js +29 -0
  134. package/lib/components/pisellFloorMapLayout/utils/floorMapToolbarPalette.js +54 -0
  135. package/lib/components/productCard/cartSkuCard/components/BookingItem/index.js +17 -0
  136. package/lib/components/productCard/cartSkuCard/components/BookingItem/index.less +32 -0
  137. package/lib/components/productCard/cartSkuCard/components/resources/index.less +4 -0
  138. package/lib/components/productCard/cartSkuCard/components/timeRange/index.js +1 -2
  139. package/lib/components/productCard/cartSkuCard/index.js +17 -11
  140. package/lib/components/productCard/lineItem/BookingLineItem.js +17 -6
  141. package/lib/components/productCard/lineItem/components/Holders/index.js +53 -0
  142. package/lib/components/productCard/lineItem/components/Holders/index.less +39 -0
  143. package/lib/components/productCard/lineItem/components/Resources/index.js +47 -0
  144. package/lib/components/productCard/lineItem/components/TimeRange/index.js +67 -0
  145. package/lib/components/productCard/lineItem/index.js +5 -2
  146. package/lib/components/productCard/lineItem/index.less +21 -10
  147. package/lib/components/productCard/types.d.ts +1 -0
  148. package/lib/index.d.ts +6 -2
  149. package/lib/index.js +11 -0
  150. package/lib/pisell-materials.tw.css +3 -0
  151. package/package.json +6 -3
@@ -0,0 +1,340 @@
1
+ import { _objectSpread2 } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
2
+ import { applySourceStub, applyTargetStub, getAnchorPoint, pathFromPoints, pickAutoAnchor, resolveDrawingTargetAtPoint, routeOrthogonal, simplifyCollinearPath } from "../utils/edgeRouting.js";
3
+ import { useFloorMapEdgeViewModel } from "./useFloorMapEdgeViewModel.js";
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
+ //#region src/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.ts
6
+ /**
7
+ * 平面图编辑态:连线选中、拉线、草稿 edges 更新
8
+ */
9
+ function newEdgeId() {
10
+ return `edge_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
11
+ }
12
+ function useFloorMapEdgeEditing(params) {
13
+ var _config$edges, _rectsById$get;
14
+ const { enabled, config, items, mergedItems, cellSize, itemUnit, itemGap, contentOffsetRef, selectedId, setSelectedId, commitDraft, mapContentRef, scale = 1 } = params;
15
+ const [selectedEdgeId, setSelectedEdgeId] = useState(null);
16
+ const [drawing, setDrawing] = useState(null);
17
+ const [ghostEnd, setGhostEnd] = useState(null);
18
+ /** 拉线时当前悬停的目标图元与吸附端口 */
19
+ const [drawingTarget, setDrawingTarget] = useState(null);
20
+ const [pendingRectsById, setPendingRectsById] = useState({});
21
+ /** 拖动中段手柄时的临时 routing 覆盖(pointerup 后写入 edge.routing) */
22
+ const [pendingRoutingByEdge, setPendingRoutingByEdge] = useState({});
23
+ const edges = (_config$edges = config === null || config === void 0 ? void 0 : config.edges) !== null && _config$edges !== void 0 ? _config$edges : [];
24
+ const { routes, rectsById } = useFloorMapEdgeViewModel({
25
+ edges,
26
+ items,
27
+ cellSize,
28
+ itemUnit,
29
+ itemGap,
30
+ pendingRectsById,
31
+ pendingRoutingByEdge
32
+ });
33
+ const clearEdgeSelection = useCallback(() => {
34
+ setSelectedEdgeId(null);
35
+ }, []);
36
+ const selectEdge = useCallback((edgeId) => {
37
+ if (drawing) return;
38
+ setSelectedEdgeId(edgeId);
39
+ if (edgeId) setSelectedId(null);
40
+ }, [drawing, setSelectedId]);
41
+ const selectNode = useCallback((id) => {
42
+ if (drawing) return;
43
+ setSelectedId(id);
44
+ if (id) setSelectedEdgeId(null);
45
+ }, [drawing, setSelectedId]);
46
+ const clientToContent = useCallback((clientX, clientY) => {
47
+ const el = mapContentRef.current;
48
+ if (!el) return null;
49
+ const rect = el.getBoundingClientRect();
50
+ const off = contentOffsetRef.current;
51
+ return {
52
+ x: (clientX - rect.left) / scale - off.x,
53
+ y: (clientY - rect.top) / scale - off.y
54
+ };
55
+ }, [
56
+ mapContentRef,
57
+ contentOffsetRef,
58
+ scale
59
+ ]);
60
+ const ghostPathD = useMemo(() => {
61
+ var _drawingTarget$anchor;
62
+ if (!drawing || !ghostEnd) return null;
63
+ const srcRect = rectsById.get(drawing.fromInstanceId);
64
+ if (!srcRect) return null;
65
+ const sourcePt = getAnchorPoint(srcRect, drawing.fromAnchor);
66
+ const tgtRect = drawingTarget ? rectsById.get(drawingTarget.instanceId) : null;
67
+ const targetSide = (_drawingTarget$anchor = drawingTarget === null || drawingTarget === void 0 ? void 0 : drawingTarget.anchor) !== null && _drawingTarget$anchor !== void 0 ? _drawingTarget$anchor : "left";
68
+ const targetPt = tgtRect ? getAnchorPoint(tgtRect, targetSide) : ghostEnd;
69
+ return pathFromPoints(routeOrthogonal(sourcePt, drawing.fromAnchor, targetPt, targetSide, void 0, srcRect && tgtRect ? {
70
+ sourceRect: srcRect,
71
+ targetRect: tgtRect
72
+ } : void 0));
73
+ }, [
74
+ drawing,
75
+ ghostEnd,
76
+ drawingTarget,
77
+ rectsById
78
+ ]);
79
+ const drawingTargetPreview = useMemo(() => {
80
+ if (!drawing || !drawingTarget) return null;
81
+ const rect = rectsById.get(drawingTarget.instanceId);
82
+ if (!rect) return null;
83
+ return {
84
+ instanceId: drawingTarget.instanceId,
85
+ rect,
86
+ anchor: drawingTarget.anchor
87
+ };
88
+ }, [
89
+ drawing,
90
+ drawingTarget,
91
+ rectsById
92
+ ]);
93
+ const startDrawingFromAnchor = useCallback((instanceId, anchor) => {
94
+ setDrawing({
95
+ fromInstanceId: instanceId,
96
+ fromAnchor: anchor
97
+ });
98
+ setDrawingTarget(null);
99
+ setGhostEnd(null);
100
+ setSelectedEdgeId(null);
101
+ setSelectedId(instanceId);
102
+ }, [setSelectedId]);
103
+ const finishDrawing = useCallback((clientX, clientY) => {
104
+ var _resolved$instanceId;
105
+ if (!drawing || !config) {
106
+ setDrawing(null);
107
+ setGhostEnd(null);
108
+ setDrawingTarget(null);
109
+ return;
110
+ }
111
+ const pt = clientToContent(clientX, clientY);
112
+ if (!pt) {
113
+ setDrawing(null);
114
+ setGhostEnd(null);
115
+ setDrawingTarget(null);
116
+ return;
117
+ }
118
+ const resolved = drawingTarget !== null && drawingTarget !== void 0 ? drawingTarget : resolveDrawingTargetAtPoint(pt, mergedItems.map((m) => m.id), rectsById, drawing.fromInstanceId);
119
+ const targetId = (_resolved$instanceId = resolved === null || resolved === void 0 ? void 0 : resolved.instanceId) !== null && _resolved$instanceId !== void 0 ? _resolved$instanceId : null;
120
+ const targetAnchor = resolved === null || resolved === void 0 ? void 0 : resolved.anchor;
121
+ if (targetId && targetId !== drawing.fromInstanceId) {
122
+ const edge = {
123
+ id: newEdgeId(),
124
+ sourceId: drawing.fromInstanceId,
125
+ targetId,
126
+ sourceAnchor: drawing.fromAnchor,
127
+ targetAnchor,
128
+ status: "online"
129
+ };
130
+ commitDraft((prev) => {
131
+ var _prev$edges;
132
+ return _objectSpread2(_objectSpread2({}, prev), {}, { edges: [...(_prev$edges = prev.edges) !== null && _prev$edges !== void 0 ? _prev$edges : [], edge] });
133
+ });
134
+ setSelectedEdgeId(edge.id);
135
+ setSelectedId(null);
136
+ }
137
+ setDrawing(null);
138
+ setGhostEnd(null);
139
+ setDrawingTarget(null);
140
+ }, [
141
+ drawing,
142
+ drawingTarget,
143
+ config,
144
+ clientToContent,
145
+ mergedItems,
146
+ rectsById,
147
+ commitDraft,
148
+ setSelectedId
149
+ ]);
150
+ useEffect(() => {
151
+ if (!drawing) return;
152
+ const onMove = (ev) => {
153
+ const pt = clientToContent(ev.clientX, ev.clientY);
154
+ if (!pt) return;
155
+ const hit = resolveDrawingTargetAtPoint(pt, mergedItems.map((m) => m.id), rectsById, drawing.fromInstanceId);
156
+ setDrawingTarget(hit);
157
+ if (hit) {
158
+ const rect = rectsById.get(hit.instanceId);
159
+ if (rect) {
160
+ setGhostEnd(getAnchorPoint(rect, hit.anchor));
161
+ return;
162
+ }
163
+ }
164
+ setGhostEnd(pt);
165
+ };
166
+ const onUp = (ev) => {
167
+ finishDrawing(ev.clientX, ev.clientY);
168
+ };
169
+ window.addEventListener("pointermove", onMove);
170
+ window.addEventListener("pointerup", onUp);
171
+ return () => {
172
+ window.removeEventListener("pointermove", onMove);
173
+ window.removeEventListener("pointerup", onUp);
174
+ };
175
+ }, [
176
+ drawing,
177
+ clientToContent,
178
+ finishDrawing,
179
+ mergedItems,
180
+ rectsById
181
+ ]);
182
+ const updateEdge = useCallback((edgeId, patch) => {
183
+ commitDraft((prev) => {
184
+ var _prev$edges2;
185
+ return _objectSpread2(_objectSpread2({}, prev), {}, { edges: ((_prev$edges2 = prev.edges) !== null && _prev$edges2 !== void 0 ? _prev$edges2 : []).map((e) => e.id === edgeId ? _objectSpread2(_objectSpread2({}, e), patch) : e) });
186
+ });
187
+ }, [commitDraft]);
188
+ /**
189
+ * 拖动连线段手柄:所有拖动都直接对「path snapshot」做形变。
190
+ * - `role='inner'`:把指定 segmentIndex 的主轴坐标平移到鼠标位置。
191
+ * - `role='first'`:在 snapshot 前面插入一对新点(短 stub + 新被拖动段),
192
+ * 随后拖动改新被拖动段的主轴。即「拖首段 = 新增一个角」,可以无限叠加。
193
+ * - `role='last'`:对称地在末尾追加一对新点。
194
+ * 释放后把「working snapshot」一次性写入 edge.routing.pathSnapshot。
195
+ */
196
+ const draggingSegmentRef = useRef(null);
197
+ const startDraggingSegment = useCallback((params) => {
198
+ const edge = edges.find((e) => e.id === params.edgeId);
199
+ if (!edge) return;
200
+ const currentRoute = routes.find((r) => r.edgeId === params.edgeId);
201
+ if (!currentRoute) return;
202
+ /** 始终用「当前 view-model 渲染的 path」(已对齐到最新图元锚点)作为 working 起点。
203
+ * 再做一次共线化简,确保 applySourceStub/Target 不会被冗余共线点干扰
204
+ * (否则插入 stub 后改 y/x,后续共线点未跟随会出斜段)。 */
205
+ const baseSnapshot = simplifyCollinearPath(currentRoute.points);
206
+ let workingSnapshot = baseSnapshot;
207
+ let targetSegIndex;
208
+ if (params.role === "first") {
209
+ var _edge$sourceAnchor, _params$segmentLength;
210
+ const srcRect = rectsById.get(edge.sourceId);
211
+ const tgtRect = rectsById.get(edge.targetId);
212
+ const sourceAnchor = (_edge$sourceAnchor = edge.sourceAnchor) !== null && _edge$sourceAnchor !== void 0 ? _edge$sourceAnchor : srcRect && tgtRect ? pickAutoAnchor(srcRect, tgtRect) : "right";
213
+ const segLen = (_params$segmentLength = params.segmentLength) !== null && _params$segmentLength !== void 0 ? _params$segmentLength : 48;
214
+ workingSnapshot = applySourceStub(baseSnapshot, Math.min(24, segLen / 2), sourceAnchor);
215
+ /** stub 后被拖动段 = path 的 segment 2(即 snapshot[2]→snapshot[3]) */
216
+ targetSegIndex = 2;
217
+ } else if (params.role === "last") {
218
+ var _edge$targetAnchor, _params$segmentLength2;
219
+ const srcRect = rectsById.get(edge.sourceId);
220
+ const tgtRect = rectsById.get(edge.targetId);
221
+ const targetAnchor = (_edge$targetAnchor = edge.targetAnchor) !== null && _edge$targetAnchor !== void 0 ? _edge$targetAnchor : srcRect && tgtRect ? pickAutoAnchor(tgtRect, srcRect) : "left";
222
+ const segLen = (_params$segmentLength2 = params.segmentLength) !== null && _params$segmentLength2 !== void 0 ? _params$segmentLength2 : 48;
223
+ workingSnapshot = applyTargetStub(baseSnapshot, Math.min(24, segLen / 2), targetAnchor);
224
+ /** stub 后被拖动段 = path 的 segment (n-3)(即 snapshot[n-4]→snapshot[n-3]) */
225
+ targetSegIndex = workingSnapshot.length - 4;
226
+ } else targetSegIndex = params.segmentIndex;
227
+ draggingSegmentRef.current = {
228
+ edgeId: params.edgeId,
229
+ workingSnapshot,
230
+ targetSegIndex,
231
+ axis: params.axis,
232
+ startClientX: params.clientX,
233
+ startClientY: params.clientY,
234
+ startPosition: params.startPosition
235
+ };
236
+ setPendingRoutingByEdge((prev) => _objectSpread2(_objectSpread2({}, prev), {}, { [params.edgeId]: { pathSnapshot: workingSnapshot } }));
237
+ setSelectedEdgeId(params.edgeId);
238
+ }, [
239
+ edges,
240
+ routes,
241
+ rectsById
242
+ ]);
243
+ useEffect(() => {
244
+ const onMove = (ev) => {
245
+ const drag = draggingSegmentRef.current;
246
+ if (!drag) return;
247
+ const dx = (ev.clientX - drag.startClientX) / scale;
248
+ const dy = (ev.clientY - drag.startClientY) / scale;
249
+ const delta = drag.axis === "h" ? dy : dx;
250
+ const nextPos = drag.startPosition + delta;
251
+ const snap = drag.workingSnapshot.map((p) => [p[0], p[1]]);
252
+ const i = drag.targetSegIndex;
253
+ if (i < 0 || i + 1 >= snap.length) return;
254
+ if (drag.axis === "h") {
255
+ snap[i] = [snap[i][0], nextPos];
256
+ snap[i + 1] = [snap[i + 1][0], nextPos];
257
+ } else {
258
+ snap[i] = [nextPos, snap[i][1]];
259
+ snap[i + 1] = [nextPos, snap[i + 1][1]];
260
+ }
261
+ setPendingRoutingByEdge((prev) => _objectSpread2(_objectSpread2({}, prev), {}, { [drag.edgeId]: { pathSnapshot: snap } }));
262
+ };
263
+ const onUp = () => {
264
+ const drag = draggingSegmentRef.current;
265
+ draggingSegmentRef.current = null;
266
+ if (!drag) return;
267
+ setPendingRoutingByEdge((prev) => {
268
+ const next = _objectSpread2({}, prev);
269
+ const finalRouting = next[drag.edgeId];
270
+ delete next[drag.edgeId];
271
+ if (finalRouting) updateEdge(drag.edgeId, { routing: finalRouting });
272
+ return next;
273
+ });
274
+ };
275
+ window.addEventListener("pointermove", onMove);
276
+ window.addEventListener("pointerup", onUp);
277
+ return () => {
278
+ window.removeEventListener("pointermove", onMove);
279
+ window.removeEventListener("pointerup", onUp);
280
+ };
281
+ }, [scale, updateEdge]);
282
+ const deleteEdge = useCallback((edgeId) => {
283
+ commitDraft((prev) => {
284
+ var _prev$edges3;
285
+ return _objectSpread2(_objectSpread2({}, prev), {}, { edges: ((_prev$edges3 = prev.edges) !== null && _prev$edges3 !== void 0 ? _prev$edges3 : []).filter((e) => e.id !== edgeId) });
286
+ });
287
+ setSelectedEdgeId((id) => id === edgeId ? null : id);
288
+ }, [commitDraft]);
289
+ const handleEdgeClick = useCallback((edgeId) => {
290
+ selectEdge(edgeId);
291
+ }, [selectEdge]);
292
+ const handleMapBackgroundClick = useCallback(() => {
293
+ if (drawing) return;
294
+ setSelectedEdgeId(null);
295
+ setSelectedId(null);
296
+ }, [drawing, setSelectedId]);
297
+ useEffect(() => {
298
+ if (!enabled) return;
299
+ const onKey = (ev) => {
300
+ if (ev.key !== "Delete" && ev.key !== "Backspace") return;
301
+ if (!selectedEdgeId) return;
302
+ const t = ev.target;
303
+ if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
304
+ ev.preventDefault();
305
+ deleteEdge(selectedEdgeId);
306
+ };
307
+ window.addEventListener("keydown", onKey);
308
+ return () => window.removeEventListener("keydown", onKey);
309
+ }, [
310
+ enabled,
311
+ selectedEdgeId,
312
+ deleteEdge
313
+ ]);
314
+ return {
315
+ edges,
316
+ routes,
317
+ rectsById,
318
+ selectedEdgeId,
319
+ selectedEdge: useMemo(() => {
320
+ var _edges$find;
321
+ return (_edges$find = edges.find((e) => e.id === selectedEdgeId)) !== null && _edges$find !== void 0 ? _edges$find : null;
322
+ }, [edges, selectedEdgeId]),
323
+ selectedNodeRect: selectedId ? (_rectsById$get = rectsById.get(selectedId)) !== null && _rectsById$get !== void 0 ? _rectsById$get : null : null,
324
+ drawing,
325
+ drawingTargetPreview,
326
+ ghostPathD,
327
+ selectNode,
328
+ selectEdge,
329
+ clearEdgeSelection,
330
+ startDrawingFromAnchor,
331
+ handleEdgeClick,
332
+ handleMapBackgroundClick,
333
+ updateEdge,
334
+ deleteEdge,
335
+ setPendingRectsById,
336
+ startDraggingSegment
337
+ };
338
+ }
339
+ //#endregion
340
+ export { useFloorMapEdgeEditing };
@@ -0,0 +1,102 @@
1
+ import { buildEdgeRoute, getEdgeChannelOffset, getItemPixelRect } from "../utils/edgeRouting.js";
2
+ import { useMemo } from "react";
3
+ //#region src/components/pisellFloorMapLayout/hooks/useFloorMapEdgeViewModel.ts
4
+ /**
5
+ * 根据 edges + 图元矩形计算连线路由
6
+ */
7
+ function extractRouteSegments(points) {
8
+ const segs = [];
9
+ for (let i = 0; i < points.length - 1; i++) {
10
+ const [x1, y1] = points[i];
11
+ const [x2, y2] = points[i + 1];
12
+ if (y1 === y2 && x1 !== x2) segs.push({
13
+ orientation: "h",
14
+ key: y1,
15
+ a: Math.min(x1, x2),
16
+ b: Math.max(x1, x2)
17
+ });
18
+ else if (x1 === x2 && y1 !== y2) segs.push({
19
+ orientation: "v",
20
+ key: x1,
21
+ a: Math.min(y1, y2),
22
+ b: Math.max(y1, y2)
23
+ });
24
+ }
25
+ return segs;
26
+ }
27
+ function segmentsOverlap(a, b) {
28
+ if (a.orientation !== b.orientation) return false;
29
+ if (a.key !== b.key) return false;
30
+ return Math.max(a.a, b.a) < Math.min(a.b, b.b);
31
+ }
32
+ function routesShareChannel(a, b) {
33
+ const segsA = extractRouteSegments(a);
34
+ const segsB = extractRouteSegments(b);
35
+ for (const sa of segsA) for (const sb of segsB) if (segmentsOverlap(sa, sb)) return true;
36
+ return false;
37
+ }
38
+ /** 同一目标的多条边在末段竖线处必然共线,需主动分配通道错位 */
39
+ function edgesShareTarget(a, b) {
40
+ return a.targetId === b.targetId || a.sourceId === b.sourceId;
41
+ }
42
+ function useFloorMapEdgeViewModel(params) {
43
+ const { edges = [], items, cellSize, itemUnit, itemGap, pendingRectsById, pendingRoutingByEdge } = params;
44
+ return useMemo(() => {
45
+ const rectsById = /* @__PURE__ */ new Map();
46
+ for (const item of items) rectsById.set(item.id, getItemPixelRect(item, {
47
+ cellSize,
48
+ itemUnit,
49
+ itemGap,
50
+ override: pendingRectsById === null || pendingRectsById === void 0 ? void 0 : pendingRectsById[item.id]
51
+ }));
52
+ /** Pass 1:所有 edge 用默认通道(offset = 0)计算一次 */
53
+ const naive = edges.map((edge) => ({
54
+ edge,
55
+ route: buildEdgeRoute(edge, rectsById, { routingOverride: pendingRoutingByEdge === null || pendingRoutingByEdge === void 0 ? void 0 : pendingRoutingByEdge[edge.id] })
56
+ }));
57
+ /**
58
+ * Pass 2:对共享通道的 edges 分配错位 offset。
59
+ * 用 union-find 风格:按 edges 出现顺序遍历,与已选 routes 比对,若冲突则用下一个 offset。
60
+ */
61
+ const routes = [];
62
+ const accepted = [];
63
+ const acceptedEdges = [];
64
+ for (let i = 0; i < naive.length; i++) {
65
+ const { edge, route } = naive[i];
66
+ if (!route) continue;
67
+ let chosen = route;
68
+ let attempt = 0;
69
+ const maxAttempts = 10;
70
+ while (attempt < maxAttempts) {
71
+ if (!accepted.some((r, idx) => {
72
+ const other = acceptedEdges[idx];
73
+ return routesShareChannel(chosen.points, r.points) || other != null && edgesShareTarget(edge, other);
74
+ })) break;
75
+ attempt += 1;
76
+ const re = buildEdgeRoute(edge, rectsById, {
77
+ channelOffset: getEdgeChannelOffset(attempt),
78
+ routingOverride: pendingRoutingByEdge === null || pendingRoutingByEdge === void 0 ? void 0 : pendingRoutingByEdge[edge.id]
79
+ });
80
+ if (!re) break;
81
+ chosen = re;
82
+ }
83
+ accepted.push(chosen);
84
+ acceptedEdges.push(edge);
85
+ routes.push(chosen);
86
+ }
87
+ return {
88
+ routes,
89
+ rectsById
90
+ };
91
+ }, [
92
+ edges,
93
+ items,
94
+ cellSize,
95
+ itemUnit,
96
+ itemGap,
97
+ pendingRectsById,
98
+ pendingRoutingByEdge
99
+ ]);
100
+ }
101
+ //#endregion
102
+ export { useFloorMapEdgeViewModel };