@pisell/materials 1.8.54 → 1.8.55

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 (139) 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/meta.js +3 -3
  5. package/build/lowcode/render/default/view.css +1 -1
  6. package/build/lowcode/render/default/view.js +35 -35
  7. package/build/lowcode/view.css +1 -1
  8. package/build/lowcode/view.js +35 -35
  9. package/es/components/buttonGroupPreview/index.js +8 -4
  10. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.js +207 -49
  11. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.less +33 -2
  12. package/es/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +50 -2
  13. package/es/components/pisellFloorMapLayout/components/EdgeLayer.js +252 -0
  14. package/es/components/pisellFloorMapLayout/components/EdgeLayer.less +11 -0
  15. package/es/components/pisellFloorMapLayout/components/EditableItemLayer.js +179 -29
  16. package/es/components/pisellFloorMapLayout/components/EditableItemLayer.less +0 -28
  17. package/es/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.js +131 -0
  18. package/es/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.less +65 -0
  19. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +5 -4
  20. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +12 -1
  21. package/es/components/pisellFloorMapLayout/components/FloorMapEditQuickActions.js +8 -1
  22. package/es/components/pisellFloorMapLayout/components/FloorMapOverview.js +16 -8
  23. package/es/components/pisellFloorMapLayout/components/FloorMapOverview.less +69 -17
  24. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.js +290 -147
  25. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.less +210 -11
  26. package/es/components/pisellFloorMapLayout/components/FloorMapToolbarViewAllModal.js +81 -0
  27. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.d.ts +8 -0
  28. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.js +21 -0
  29. package/es/components/pisellFloorMapLayout/components/FloorMapZoneElement.less +74 -0
  30. package/es/components/pisellFloorMapLayout/components/ItemLayer.less +3 -1
  31. package/es/components/pisellFloorMapLayout/components/NodePortMarkers.js +70 -0
  32. package/es/components/pisellFloorMapLayout/components/NodePortMarkers.less +33 -0
  33. package/es/components/pisellFloorMapLayout/components/ViewControls.js +13 -5
  34. package/es/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +2 -0
  35. package/es/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -1
  36. package/es/components/pisellFloorMapLayout/floorMapLayoutConstants.js +1 -1
  37. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.js +340 -0
  38. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEdgeViewModel.js +102 -0
  39. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +244 -84
  40. package/es/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +54 -0
  41. package/es/components/pisellFloorMapLayout/hooks/useShopFloorPlanSubscription.js +7 -4
  42. package/es/components/pisellFloorMapLayout/index.d.ts +5 -1
  43. package/es/components/pisellFloorMapLayout/locales-ja.js +14 -2
  44. package/es/components/pisellFloorMapLayout/locales-pt.js +14 -2
  45. package/es/components/pisellFloorMapLayout/locales.js +161 -8
  46. package/es/components/pisellFloorMapLayout/types.d.ts +86 -4
  47. package/es/components/pisellFloorMapLayout/types.js +5 -1
  48. package/es/components/pisellFloorMapLayout/utils/alignSnap.js +170 -0
  49. package/es/components/pisellFloorMapLayout/utils/batchScenePlacementLayout.js +86 -0
  50. package/es/components/pisellFloorMapLayout/utils/edgeRouting.js +942 -0
  51. package/es/components/pisellFloorMapLayout/utils/floorMapCanvasDisplay.d.ts +7 -0
  52. package/es/components/pisellFloorMapLayout/utils/floorMapCanvasStage.js +23 -1
  53. package/es/components/pisellFloorMapLayout/utils/floorMapConfigMerge.js +11 -2
  54. package/es/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +20 -3
  55. package/es/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +11 -5
  56. package/es/components/pisellFloorMapLayout/utils/floorMapOverviewLayout.js +5 -3
  57. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.d.ts +20 -0
  58. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.js +62 -0
  59. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.d.ts +83 -0
  60. package/es/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.js +29 -0
  61. package/es/components/pisellFloorMapLayout/utils/floorMapToolbarPalette.js +51 -0
  62. package/es/components/productCard/cartSkuCard/components/BookingItem/index.js +14 -0
  63. package/es/components/productCard/cartSkuCard/components/BookingItem/index.less +32 -0
  64. package/es/components/productCard/cartSkuCard/components/timeRange/index.js +1 -2
  65. package/es/components/productCard/cartSkuCard/index.js +17 -11
  66. package/es/components/productCard/lineItem/BookingLineItem.js +6 -4
  67. package/es/components/productCard/lineItem/components/Resources/index.js +45 -0
  68. package/es/components/productCard/lineItem/components/TimeRange/index.js +64 -0
  69. package/es/components/productCard/lineItem/index.js +2 -1
  70. package/es/components/productCard/lineItem/index.less +21 -10
  71. package/es/index.d.ts +6 -2
  72. package/es/index.js +6 -2
  73. package/es/pisell-materials.tw.css +3 -0
  74. package/lib/components/buttonGroupPreview/index.js +8 -4
  75. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.js +206 -48
  76. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.less +33 -2
  77. package/lib/components/pisellFloorMapLayout/appearance/floorMapAppearance.js +50 -2
  78. package/lib/components/pisellFloorMapLayout/components/EdgeLayer.js +255 -0
  79. package/lib/components/pisellFloorMapLayout/components/EdgeLayer.less +11 -0
  80. package/lib/components/pisellFloorMapLayout/components/EditableItemLayer.js +179 -29
  81. package/lib/components/pisellFloorMapLayout/components/EditableItemLayer.less +0 -28
  82. package/lib/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.js +134 -0
  83. package/lib/components/pisellFloorMapLayout/components/FloorMapEdgeEditPanel.less +65 -0
  84. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +4 -3
  85. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +12 -1
  86. package/lib/components/pisellFloorMapLayout/components/FloorMapEditQuickActions.js +8 -1
  87. package/lib/components/pisellFloorMapLayout/components/FloorMapOverview.js +16 -8
  88. package/lib/components/pisellFloorMapLayout/components/FloorMapOverview.less +69 -17
  89. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.js +287 -144
  90. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.less +210 -11
  91. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbarViewAllModal.js +84 -0
  92. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.d.ts +8 -0
  93. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.js +23 -0
  94. package/lib/components/pisellFloorMapLayout/components/FloorMapZoneElement.less +74 -0
  95. package/lib/components/pisellFloorMapLayout/components/ItemLayer.less +3 -1
  96. package/lib/components/pisellFloorMapLayout/components/NodePortMarkers.js +73 -0
  97. package/lib/components/pisellFloorMapLayout/components/NodePortMarkers.less +33 -0
  98. package/lib/components/pisellFloorMapLayout/components/ViewControls.js +12 -4
  99. package/lib/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +2 -0
  100. package/lib/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -1
  101. package/lib/components/pisellFloorMapLayout/floorMapLayoutConstants.js +1 -1
  102. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.js +341 -0
  103. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEdgeViewModel.js +103 -0
  104. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +242 -82
  105. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +54 -0
  106. package/lib/components/pisellFloorMapLayout/hooks/useShopFloorPlanSubscription.js +7 -4
  107. package/lib/components/pisellFloorMapLayout/index.d.ts +5 -1
  108. package/lib/components/pisellFloorMapLayout/locales-ja.js +14 -2
  109. package/lib/components/pisellFloorMapLayout/locales-pt.js +14 -2
  110. package/lib/components/pisellFloorMapLayout/locales.js +161 -8
  111. package/lib/components/pisellFloorMapLayout/types.d.ts +86 -4
  112. package/lib/components/pisellFloorMapLayout/types.js +5 -0
  113. package/lib/components/pisellFloorMapLayout/utils/alignSnap.js +170 -0
  114. package/lib/components/pisellFloorMapLayout/utils/batchScenePlacementLayout.js +87 -0
  115. package/lib/components/pisellFloorMapLayout/utils/edgeRouting.js +956 -0
  116. package/lib/components/pisellFloorMapLayout/utils/floorMapCanvasDisplay.d.ts +7 -0
  117. package/lib/components/pisellFloorMapLayout/utils/floorMapCanvasStage.js +23 -0
  118. package/lib/components/pisellFloorMapLayout/utils/floorMapConfigMerge.js +11 -2
  119. package/lib/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +21 -2
  120. package/lib/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +11 -5
  121. package/lib/components/pisellFloorMapLayout/utils/floorMapOverviewLayout.js +5 -3
  122. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.d.ts +20 -0
  123. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerDisplay.js +64 -0
  124. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.d.ts +83 -0
  125. package/lib/components/pisellFloorMapLayout/utils/floorMapResourcePickerTypes.js +29 -0
  126. package/lib/components/pisellFloorMapLayout/utils/floorMapToolbarPalette.js +54 -0
  127. package/lib/components/productCard/cartSkuCard/components/BookingItem/index.js +17 -0
  128. package/lib/components/productCard/cartSkuCard/components/BookingItem/index.less +32 -0
  129. package/lib/components/productCard/cartSkuCard/components/timeRange/index.js +1 -2
  130. package/lib/components/productCard/cartSkuCard/index.js +17 -11
  131. package/lib/components/productCard/lineItem/BookingLineItem.js +6 -4
  132. package/lib/components/productCard/lineItem/components/Resources/index.js +47 -0
  133. package/lib/components/productCard/lineItem/components/TimeRange/index.js +67 -0
  134. package/lib/components/productCard/lineItem/index.js +2 -1
  135. package/lib/components/productCard/lineItem/index.less +21 -10
  136. package/lib/index.d.ts +6 -2
  137. package/lib/index.js +11 -0
  138. package/lib/pisell-materials.tw.css +3 -0
  139. package/package.json +4 -1
@@ -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 };