@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,252 @@
1
+ import { getPortLabelPosition, resolveEdgeCenterLabelPositions } from "../utils/edgeRouting.js";
2
+ import React, { useMemo } from "react";
3
+ import classNames from "classnames";
4
+ import "./EdgeLayer.less";
5
+ //#region src/components/pisellFloorMapLayout/components/EdgeLayer.tsx
6
+ /**
7
+ * EdgeLayer - 拓扑连线 SVG 层(正交折线 + 端口标签)
8
+ */
9
+ const PREFIX = "pisell-floor-map-layout";
10
+ /** 中段手柄基础视觉尺寸(px),实际渲染会按 viewportScale 反向缩放 */
11
+ const SEGMENT_HANDLE_BASE_SIZE = 12;
12
+ /** 段长 < 该值(px)时不渲染手柄,避免极短段误触 */
13
+ const SEGMENT_HANDLE_MIN_LENGTH = 16;
14
+ function resolveEdgeStroke(edge, appearance, selected) {
15
+ var _edge$status, _edge$strokeWidth3;
16
+ if (selected) {
17
+ var _edge$strokeWidth;
18
+ return {
19
+ stroke: appearance.edgeSelectedStroke,
20
+ strokeWidth: ((_edge$strokeWidth = edge.strokeWidth) !== null && _edge$strokeWidth !== void 0 ? _edge$strokeWidth : 1.5) + 1
21
+ };
22
+ }
23
+ if (edge.color) {
24
+ var _edge$strokeWidth2;
25
+ return {
26
+ stroke: edge.color,
27
+ strokeWidth: (_edge$strokeWidth2 = edge.strokeWidth) !== null && _edge$strokeWidth2 !== void 0 ? _edge$strokeWidth2 : 1.5,
28
+ strokeDasharray: edge.dashed ? "6 4" : void 0
29
+ };
30
+ }
31
+ const status = (_edge$status = edge.status) !== null && _edge$status !== void 0 ? _edge$status : "unknown";
32
+ const style = appearance.edgeStatusStyles[status];
33
+ return {
34
+ stroke: style.stroke,
35
+ strokeWidth: (_edge$strokeWidth3 = edge.strokeWidth) !== null && _edge$strokeWidth3 !== void 0 ? _edge$strokeWidth3 : 1.5,
36
+ strokeDasharray: edge.dashed || style.dashed ? "6 4" : void 0
37
+ };
38
+ }
39
+ function PortLabel(props) {
40
+ const { x, y, text, appearance } = props;
41
+ const estW = Math.max(32, text.length * 6.5 + 12);
42
+ const h = 18;
43
+ return /* @__PURE__ */ React.createElement("g", { className: `${PREFIX}-edge-port-label` }, /* @__PURE__ */ React.createElement("rect", {
44
+ x: x - estW / 2,
45
+ y: y - h / 2,
46
+ width: estW,
47
+ height: h,
48
+ rx: 4,
49
+ fill: appearance.edgePortLabelBackground,
50
+ stroke: appearance.edgePortLabelBorder,
51
+ strokeWidth: 1
52
+ }), /* @__PURE__ */ React.createElement("text", {
53
+ x,
54
+ y: y + 4,
55
+ textAnchor: "middle",
56
+ fontSize: 11,
57
+ fill: appearance.edgePortLabelText
58
+ }, text));
59
+ }
60
+ function EdgeLayer(props) {
61
+ const { width, height, contentOffsetX = 0, contentOffsetY = 0, edges, routes, rectsById, appearance, mode, selectedEdgeId, ghostPathD, onEdgeClick, viewportScale = 1, onSegmentDragStart, className } = props;
62
+ const safeScale = Math.max(viewportScale, .01);
63
+ const handleSize = SEGMENT_HANDLE_BASE_SIZE / safeScale;
64
+ const handleHalf = handleSize / 2;
65
+ const edgeById = useMemo(() => {
66
+ const m = /* @__PURE__ */ new Map();
67
+ for (const e of edges) m.set(e.id, e);
68
+ return m;
69
+ }, [edges]);
70
+ const routeById = useMemo(() => {
71
+ const m = /* @__PURE__ */ new Map();
72
+ for (const r of routes) m.set(r.edgeId, r);
73
+ return m;
74
+ }, [routes]);
75
+ /** 同目标多边时错开 connection_type 标签,避免叠在一起 */
76
+ const centerLabelByEdgeId = useMemo(() => resolveEdgeCenterLabelPositions(routes.map((route) => {
77
+ const edge = edgeById.get(route.edgeId);
78
+ if (!edge) return null;
79
+ return {
80
+ edgeId: route.edgeId,
81
+ sourceId: edge.sourceId,
82
+ targetId: edge.targetId,
83
+ points: route.points,
84
+ label: edge.label
85
+ };
86
+ }).filter((row) => row != null), {
87
+ placement: "target-tail",
88
+ rectsById
89
+ }), [
90
+ routes,
91
+ edgeById,
92
+ rectsById
93
+ ]);
94
+ return /* @__PURE__ */ React.createElement("svg", {
95
+ className: classNames(`${PREFIX}-edge-layer`, className),
96
+ width,
97
+ height,
98
+ style: {
99
+ position: "absolute",
100
+ left: contentOffsetX,
101
+ top: contentOffsetY,
102
+ pointerEvents: "none",
103
+ overflow: "visible"
104
+ },
105
+ "aria-hidden": mode === "read"
106
+ }, /* @__PURE__ */ React.createElement("g", { className: `${PREFIX}-edge-layer-inner` }, routes.map((route) => {
107
+ const edge = edgeById.get(route.edgeId);
108
+ if (!edge) return null;
109
+ const selected = selectedEdgeId === edge.id;
110
+ const stroke = resolveEdgeStroke(edge, appearance, selected);
111
+ return /* @__PURE__ */ React.createElement("g", {
112
+ key: edge.id,
113
+ className: `${PREFIX}-edge-group`
114
+ }, /* @__PURE__ */ React.createElement("path", {
115
+ d: route.pathD,
116
+ fill: "none",
117
+ stroke: stroke.stroke,
118
+ strokeWidth: stroke.strokeWidth,
119
+ strokeDasharray: stroke.strokeDasharray,
120
+ strokeLinecap: "round",
121
+ strokeLinejoin: "round",
122
+ className: classNames(`${PREFIX}-edge-path`, {
123
+ [`${PREFIX}-edge-path--selected`]: selected,
124
+ [`${PREFIX}-edge-path--interactive`]: mode === "edit"
125
+ }),
126
+ style: {
127
+ pointerEvents: mode === "edit" ? "stroke" : "none",
128
+ cursor: mode === "edit" ? "pointer" : void 0
129
+ },
130
+ onClick: mode === "edit" && onEdgeClick ? (ev) => {
131
+ ev.stopPropagation();
132
+ onEdgeClick(edge.id, ev);
133
+ } : void 0
134
+ }), edge.label && route.points.length >= 2 ? (() => {
135
+ const labelPos = centerLabelByEdgeId.get(edge.id);
136
+ if (!labelPos) return null;
137
+ return /* @__PURE__ */ React.createElement(PortLabel, {
138
+ x: labelPos.x,
139
+ y: labelPos.y,
140
+ text: edge.label,
141
+ appearance
142
+ });
143
+ })() : null);
144
+ }), edges.map((edge) => {
145
+ var _edge$sourceAnchor, _edge$targetAnchor;
146
+ const route = routeById.get(edge.id);
147
+ const srcRect = rectsById.get(edge.sourceId);
148
+ const tgtRect = rectsById.get(edge.targetId);
149
+ if (!route || !srcRect || !tgtRect) return null;
150
+ const sourceAnchor = (_edge$sourceAnchor = edge.sourceAnchor) !== null && _edge$sourceAnchor !== void 0 ? _edge$sourceAnchor : "right";
151
+ const targetAnchor = (_edge$targetAnchor = edge.targetAnchor) !== null && _edge$targetAnchor !== void 0 ? _edge$targetAnchor : "left";
152
+ const spLabel = getPortLabelPosition(srcRect, sourceAnchor);
153
+ const tpLabel = getPortLabelPosition(tgtRect, targetAnchor);
154
+ return /* @__PURE__ */ React.createElement("g", { key: `${edge.id}-labels` }, edge.sourceLabel ? /* @__PURE__ */ React.createElement(PortLabel, {
155
+ x: spLabel.x,
156
+ y: spLabel.y,
157
+ text: edge.sourceLabel,
158
+ appearance
159
+ }) : null, edge.targetLabel ? /* @__PURE__ */ React.createElement(PortLabel, {
160
+ x: tpLabel.x,
161
+ y: tpLabel.y,
162
+ text: edge.targetLabel,
163
+ appearance
164
+ }) : null);
165
+ }), mode === "edit" && selectedEdgeId && onSegmentDragStart ? (() => {
166
+ const route = routeById.get(selectedEdgeId);
167
+ if (!route) return null;
168
+ const pts = route.points;
169
+ if (pts.length < 2) return null;
170
+ const lastSegIdx = pts.length - 2;
171
+ const handles = [];
172
+ for (let i = 0; i <= lastSegIdx; i++) {
173
+ const role = i === 0 ? "first" : i === lastSegIdx ? "last" : "inner";
174
+ const [x1, y1] = pts[i];
175
+ const [x2, y2] = pts[i + 1];
176
+ let axis;
177
+ let position;
178
+ let length;
179
+ let midX;
180
+ let midY;
181
+ if (y1 === y2 && x1 !== x2) {
182
+ axis = "h";
183
+ position = y1;
184
+ length = Math.abs(x2 - x1);
185
+ midX = (x1 + x2) / 2;
186
+ midY = y1;
187
+ } else if (x1 === x2 && y1 !== y2) {
188
+ axis = "v";
189
+ position = x1;
190
+ length = Math.abs(y2 - y1);
191
+ midX = x1;
192
+ midY = (y1 + y2) / 2;
193
+ } else continue;
194
+ if (length < SEGMENT_HANDLE_MIN_LENGTH) continue;
195
+ handles.push({
196
+ key: `${role}-${i}`,
197
+ role,
198
+ segmentIndex: i,
199
+ axis,
200
+ position,
201
+ length,
202
+ midX,
203
+ midY
204
+ });
205
+ }
206
+ if (handles.length === 0) return null;
207
+ return /* @__PURE__ */ React.createElement("g", {
208
+ className: `${PREFIX}-edge-segment-handles`,
209
+ pointerEvents: "auto"
210
+ }, handles.map((h) => /* @__PURE__ */ React.createElement("rect", {
211
+ key: h.key,
212
+ x: h.midX - handleHalf,
213
+ y: h.midY - handleHalf,
214
+ width: handleSize,
215
+ height: handleSize,
216
+ rx: handleHalf,
217
+ ry: handleHalf,
218
+ className: classNames(`${PREFIX}-edge-segment-handle`, `${PREFIX}-edge-segment-handle--${h.axis}`, `${PREFIX}-edge-segment-handle--${h.role}`),
219
+ fill: appearance.edgeSelectedStroke,
220
+ stroke: "#fff",
221
+ strokeWidth: 1.5 / safeScale,
222
+ style: {
223
+ cursor: h.axis === "h" ? "ns-resize" : "ew-resize",
224
+ touchAction: "none"
225
+ },
226
+ onPointerDown: (ev) => {
227
+ ev.stopPropagation();
228
+ ev.preventDefault();
229
+ onSegmentDragStart({
230
+ edgeId: selectedEdgeId,
231
+ role: h.role,
232
+ segmentIndex: h.segmentIndex,
233
+ axis: h.axis,
234
+ startPosition: h.position,
235
+ clientX: ev.clientX,
236
+ clientY: ev.clientY,
237
+ segmentLength: h.length
238
+ });
239
+ }
240
+ })));
241
+ })() : null, ghostPathD ? /* @__PURE__ */ React.createElement("path", {
242
+ d: ghostPathD,
243
+ fill: "none",
244
+ stroke: appearance.edgeGhostStroke,
245
+ strokeWidth: 1.5,
246
+ strokeDasharray: "4 4",
247
+ strokeLinecap: "round",
248
+ pointerEvents: "none"
249
+ }) : null));
250
+ }
251
+ //#endregion
252
+ export { EdgeLayer };
@@ -0,0 +1,11 @@
1
+ .pisell-floor-map-layout-edge-layer {
2
+ z-index: 2;
3
+ }
4
+
5
+ .pisell-floor-map-layout-edge-path--interactive:hover {
6
+ filter: brightness(1.05);
7
+ }
8
+
9
+ .pisell-floor-map-layout-edge-path--selected {
10
+ filter: drop-shadow(0 0 2px rgba(21, 112, 239, 0.35));
11
+ }
@@ -1,5 +1,6 @@
1
1
  import { _objectSpread2 } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
2
2
  import { snapRect } from "../utils/snap.js";
3
+ import { snapRectToOthers } from "../utils/alignSnap.js";
3
4
  import { FloorMapEditQuickActions } from "./FloorMapEditQuickActions.js";
4
5
  import React, { useCallback, useMemo, useRef, useState } from "react";
5
6
  import classNames from "classnames";
@@ -16,6 +17,12 @@ const PREFIX = "pisell-floor-map-layout";
16
17
  const DRAG_THRESHOLD = 4;
17
18
  /** 选中图元抬升到该层级,保证快捷栏与缩放点不被其它图元(任意 item.zIndex)遮住 */
18
19
  const SELECTED_ITEM_STACK_Z = 1e6;
20
+ const RESIZE_CORNER_HANDLES = [
21
+ "nw",
22
+ "ne",
23
+ "se",
24
+ "sw"
25
+ ];
19
26
  function getItemKey(item, index, rowKey) {
20
27
  var _rowKey;
21
28
  if (!rowKey) return item.id;
@@ -30,11 +37,40 @@ function getShapeStyle(shape = "rect") {
30
37
  }
31
38
  }
32
39
  function EditableItemLayer(props) {
33
- const { items, renderItem, rowKey, cellSize, itemUnit = "pixel", itemGap = 0, contentWidth, contentHeight, contentOffsetX = 0, contentOffsetY = 0, selectedId, onSelect, onUpdateItem, snapStep = 0, scale = 1, onInteractionChange, getMergedItemWrapperStyle, className, editQuickBar } = props;
40
+ const { items, renderItem, rowKey, cellSize, itemUnit = "pixel", itemGap = 0, contentWidth, contentHeight, contentOffsetX = 0, contentOffsetY = 0, selectedId, onSelect, onUpdateItem, snapStep = 0, scale = 1, onInteractionChange, onLiveRectChange, alignSnapThresholdPx = 6, onAlignGuidesChange, alignGuideStroke = "#1570EF", getMergedItemWrapperStyle, className, editQuickBar } = props;
34
41
  const contentRef = useRef(null);
35
42
  const liveRectRef = useRef(null);
36
43
  const [interaction, setInteraction] = useState(null);
37
44
  const [liveRect, setLiveRect] = useState(null);
45
+ /** 拖动/缩放过程中的对齐辅助线(红色虚线) */
46
+ const [alignGuides, setAlignGuides] = useState([]);
47
+ /** 基础像素矩形(与 liveRect 无关),供对齐吸附排除自身后参考 */
48
+ const getBaseRect = useCallback((item) => {
49
+ var _item$width2, _item$height2;
50
+ const isPixel = itemUnit === "pixel";
51
+ const g = itemGap !== null && itemGap !== void 0 ? itemGap : 0;
52
+ if (isPixel) {
53
+ var _item$width, _item$height;
54
+ return {
55
+ left: item.x,
56
+ top: item.y,
57
+ width: (_item$width = item.width) !== null && _item$width !== void 0 ? _item$width : cellSize,
58
+ height: (_item$height = item.height) !== null && _item$height !== void 0 ? _item$height : cellSize
59
+ };
60
+ }
61
+ const w = (_item$width2 = item.width) !== null && _item$width2 !== void 0 ? _item$width2 : 1;
62
+ const h = (_item$height2 = item.height) !== null && _item$height2 !== void 0 ? _item$height2 : 1;
63
+ return {
64
+ left: g > 0 ? item.x * (cellSize + g) + g : item.x * cellSize,
65
+ top: g > 0 ? item.y * (cellSize + g) + g : item.y * cellSize,
66
+ width: g > 0 ? w * cellSize + (w - 1) * g : w * cellSize,
67
+ height: g > 0 ? h * cellSize + (h - 1) * g : h * cellSize
68
+ };
69
+ }, [
70
+ cellSize,
71
+ itemGap,
72
+ itemUnit
73
+ ]);
38
74
  const getItemStyle = useCallback((item, index) => {
39
75
  const isPixel = itemUnit === "pixel";
40
76
  const g = itemGap !== null && itemGap !== void 0 ? itemGap : 0;
@@ -43,15 +79,15 @@ function EditableItemLayer(props) {
43
79
  let width;
44
80
  let height;
45
81
  if (isPixel) {
46
- var _item$width, _item$height;
47
- width = (_item$width = item.width) !== null && _item$width !== void 0 ? _item$width : cellSize;
48
- height = (_item$height = item.height) !== null && _item$height !== void 0 ? _item$height : cellSize;
82
+ var _item$width3, _item$height3;
83
+ width = (_item$width3 = item.width) !== null && _item$width3 !== void 0 ? _item$width3 : cellSize;
84
+ height = (_item$height3 = item.height) !== null && _item$height3 !== void 0 ? _item$height3 : cellSize;
49
85
  left = item.x;
50
86
  top = item.y;
51
87
  } else {
52
- var _item$width2, _item$height2;
53
- const w = (_item$width2 = item.width) !== null && _item$width2 !== void 0 ? _item$width2 : 1;
54
- const h = (_item$height2 = item.height) !== null && _item$height2 !== void 0 ? _item$height2 : 1;
88
+ var _item$width4, _item$height4;
89
+ const w = (_item$width4 = item.width) !== null && _item$width4 !== void 0 ? _item$width4 : 1;
90
+ const h = (_item$height4 = item.height) !== null && _item$height4 !== void 0 ? _item$height4 : 1;
55
91
  left = g > 0 ? item.x * (cellSize + g) + g : item.x * cellSize;
56
92
  top = g > 0 ? item.y * (cellSize + g) + g : item.y * cellSize;
57
93
  width = g > 0 ? w * cellSize + (w - 1) * g : w * cellSize;
@@ -122,18 +158,46 @@ function EditableItemLayer(props) {
122
158
  ]);
123
159
  React.useEffect(() => {
124
160
  if (!interaction) return;
161
+ /** 阈值按 scale 折算到内容坐标,让它在屏幕上恒定 */
162
+ const threshold = Math.max(0, alignSnapThresholdPx) / Math.max(scale, .01);
163
+ /** 内部计算 snap 目标:排除自身,使用基础 rect(避开 liveRect 自吸) */
164
+ const otherRects = items.filter((it) => it.id !== interaction.itemId).map((it) => _objectSpread2({ id: it.id }, getBaseRect(it)));
125
165
  const onMove = (e) => {
126
166
  if (interaction.type === "drag") {
127
167
  const dx = (e.clientX - interaction.startX) / scale;
128
168
  const dy = (e.clientY - interaction.startY) / scale;
129
- const next = {
169
+ const rawNext = {
130
170
  x: interaction.itemX + dx,
131
171
  y: interaction.itemY + dy,
132
172
  width: interaction.itemW,
133
173
  height: interaction.itemH
134
174
  };
175
+ const snap = snapRectToOthers({
176
+ id: interaction.itemId,
177
+ left: rawNext.x,
178
+ top: rawNext.y,
179
+ width: rawNext.width,
180
+ height: rawNext.height
181
+ }, otherRects, {
182
+ mode: "drag",
183
+ threshold
184
+ });
185
+ const next = {
186
+ x: snap.rect.left,
187
+ y: snap.rect.top,
188
+ width: snap.rect.width,
189
+ height: snap.rect.height
190
+ };
135
191
  liveRectRef.current = next;
136
192
  setLiveRect(_objectSpread2({ id: interaction.itemId }, next));
193
+ onLiveRectChange === null || onLiveRectChange === void 0 || onLiveRectChange(interaction.itemId, {
194
+ left: next.x,
195
+ top: next.y,
196
+ width: next.width,
197
+ height: next.height
198
+ });
199
+ setAlignGuides(snap.guides);
200
+ onAlignGuidesChange === null || onAlignGuidesChange === void 0 || onAlignGuidesChange(snap.guides);
137
201
  } else {
138
202
  const dx = (e.clientX - interaction.startX) / scale;
139
203
  const dy = (e.clientY - interaction.startY) / scale;
@@ -152,14 +216,36 @@ function EditableItemLayer(props) {
152
216
  h = Math.max(20, startH - dy);
153
217
  y = startItemY + dy;
154
218
  }
155
- const next = {
156
- x,
157
- y,
219
+ const movableEdgesX = handle.includes("w") ? ["left"] : handle.includes("e") ? ["right"] : [];
220
+ const movableEdgesY = handle.includes("n") ? ["top"] : handle.includes("s") ? ["bottom"] : [];
221
+ const snap = snapRectToOthers({
222
+ id: interaction.itemId,
223
+ left: x,
224
+ top: y,
158
225
  width: w,
159
226
  height: h
227
+ }, otherRects, {
228
+ mode: "resize",
229
+ threshold,
230
+ movableEdgesX,
231
+ movableEdgesY
232
+ });
233
+ const next = {
234
+ x: snap.rect.left,
235
+ y: snap.rect.top,
236
+ width: Math.max(20, snap.rect.width),
237
+ height: Math.max(20, snap.rect.height)
160
238
  };
161
239
  liveRectRef.current = next;
162
240
  setLiveRect(_objectSpread2({ id: interaction.itemId }, next));
241
+ onLiveRectChange === null || onLiveRectChange === void 0 || onLiveRectChange(interaction.itemId, {
242
+ left: next.x,
243
+ top: next.y,
244
+ width: next.width,
245
+ height: next.height
246
+ });
247
+ setAlignGuides(snap.guides);
248
+ onAlignGuidesChange === null || onAlignGuidesChange === void 0 || onAlignGuidesChange(snap.guides);
163
249
  }
164
250
  };
165
251
  const onUp = () => {
@@ -167,6 +253,9 @@ function EditableItemLayer(props) {
167
253
  if (!current) return;
168
254
  const last = liveRectRef.current;
169
255
  onInteractionChange === null || onInteractionChange === void 0 || onInteractionChange(false);
256
+ onLiveRectChange === null || onLiveRectChange === void 0 || onLiveRectChange(null, null);
257
+ setAlignGuides([]);
258
+ onAlignGuidesChange === null || onAlignGuidesChange === void 0 || onAlignGuidesChange([]);
170
259
  setLiveRect(null);
171
260
  liveRectRef.current = null;
172
261
  setInteraction(null);
@@ -202,7 +291,12 @@ function EditableItemLayer(props) {
202
291
  scale,
203
292
  snapStep,
204
293
  onUpdateItem,
205
- onInteractionChange
294
+ onInteractionChange,
295
+ onLiveRectChange,
296
+ items,
297
+ getBaseRect,
298
+ alignSnapThresholdPx,
299
+ onAlignGuidesChange
206
300
  ]);
207
301
  const handleItemClick = useCallback((e, item) => {
208
302
  if (interaction) return;
@@ -238,26 +332,31 @@ function EditableItemLayer(props) {
238
332
  }, shapeStyle), wrapperStyle), {}, { zIndex: isSelected ? SELECTED_ITEM_STACK_Z + ((_item$zIndex2 = item.zIndex) !== null && _item$zIndex2 !== void 0 ? _item$zIndex2 : 0) : baseZ }),
239
333
  onPointerDown: (e) => handlePointerDown(e, item, index),
240
334
  onClick: (e) => handleItemClick(e, item)
241
- }, /* @__PURE__ */ React.createElement("div", { className: `${PREFIX}-editable-item-inner` }, renderItem(item, index, { viewportScale: scale })), isSelected && !isSceneLayoutTransformLocked(merged) && /* @__PURE__ */ React.createElement(React.Fragment, null, [
242
- "nw",
243
- "n",
244
- "ne",
245
- "e",
246
- "se",
247
- "s",
248
- "sw",
249
- "w"
250
- ].map((h) => /* @__PURE__ */ React.createElement("div", {
251
- key: h,
252
- className: classNames(`${PREFIX}-editable-resize-handle`, `${PREFIX}-editable-resize-handle-${h}`),
253
- onPointerDown: (e) => handleResizePointerDown(e, item.id, h),
254
- style: { position: "absolute" }
255
- }))), isSelected && editQuickBar ? /* @__PURE__ */ React.createElement(FloorMapEditQuickActions, {
335
+ }, /* @__PURE__ */ React.createElement("div", { className: `${PREFIX}-editable-item-inner` }, renderItem(item, index, { viewportScale: scale })), isSelected && !isSceneLayoutTransformLocked(merged) && /* @__PURE__ */ React.createElement(React.Fragment, null, RESIZE_CORNER_HANDLES.map((h) => {
336
+ /** viewport scale 补偿:保持视觉 20px 不随画布放大变大 */
337
+ const size = 20 / Math.max(scale, .01);
338
+ const half = size / 2;
339
+ return /* @__PURE__ */ React.createElement("div", {
340
+ key: h,
341
+ className: classNames(`${PREFIX}-editable-resize-handle`, `${PREFIX}-editable-resize-handle-${h}`),
342
+ onPointerDown: (e) => handleResizePointerDown(e, item.id, h),
343
+ style: {
344
+ position: "absolute",
345
+ width: size,
346
+ height: size,
347
+ marginTop: -half,
348
+ marginRight: -half,
349
+ marginBottom: -half,
350
+ marginLeft: -half
351
+ }
352
+ });
353
+ })), isSelected && editQuickBar ? /* @__PURE__ */ React.createElement(FloorMapEditQuickActions, {
256
354
  labels: editQuickBar.labels,
257
355
  isLocked: editQuickBar.isLocked,
258
356
  onToggleLock: editQuickBar.onToggleLock,
259
357
  onDuplicate: editQuickBar.onDuplicate,
260
- onDelete: editQuickBar.onDelete
358
+ onDelete: editQuickBar.onDelete,
359
+ viewportScale: scale
261
360
  }) : null);
262
361
  });
263
362
  }, [
@@ -292,7 +391,58 @@ function EditableItemLayer(props) {
292
391
  width: contentWidth,
293
392
  height: contentHeight,
294
393
  transform: `translate(${contentOffsetX}px, ${contentOffsetY}px)`
295
- } }, itemNodes) : itemNodes);
394
+ } }, itemNodes, alignGuides.length > 0 ? /* @__PURE__ */ React.createElement(AlignGuideOverlay, {
395
+ guides: alignGuides,
396
+ width: contentWidth,
397
+ height: contentHeight,
398
+ scale,
399
+ stroke: alignGuideStroke
400
+ }) : null) : /* @__PURE__ */ React.createElement(React.Fragment, null, itemNodes, alignGuides.length > 0 ? /* @__PURE__ */ React.createElement(AlignGuideOverlay, {
401
+ guides: alignGuides,
402
+ width: contentWidth,
403
+ height: contentHeight,
404
+ scale,
405
+ stroke: alignGuideStroke
406
+ }) : null));
407
+ }
408
+ /** 拖动/缩放时的对齐辅助线层;线宽按 scale 反向缩放,保持屏幕恒定 1px */
409
+ function AlignGuideOverlay(props) {
410
+ const { guides, width, height, scale, stroke } = props;
411
+ const safeScale = Math.max(scale, .01);
412
+ const sw = 1 / safeScale;
413
+ return /* @__PURE__ */ React.createElement("svg", {
414
+ width,
415
+ height,
416
+ style: {
417
+ position: "absolute",
418
+ left: 0,
419
+ top: 0,
420
+ pointerEvents: "none",
421
+ overflow: "visible"
422
+ },
423
+ "aria-hidden": true
424
+ }, guides.map((g, i) => {
425
+ if (g.orientation === "v") return /* @__PURE__ */ React.createElement("line", {
426
+ key: `v-${i}-${g.position}`,
427
+ x1: g.position,
428
+ y1: g.start,
429
+ x2: g.position,
430
+ y2: g.end,
431
+ stroke,
432
+ strokeWidth: sw,
433
+ strokeDasharray: `${4 / safeScale} ${3 / safeScale}`
434
+ });
435
+ return /* @__PURE__ */ React.createElement("line", {
436
+ key: `h-${i}-${g.position}`,
437
+ x1: g.start,
438
+ y1: g.position,
439
+ x2: g.end,
440
+ y2: g.position,
441
+ stroke,
442
+ strokeWidth: sw,
443
+ strokeDasharray: `${4 / safeScale} ${3 / safeScale}`
444
+ });
445
+ }));
296
446
  }
297
447
  //#endregion
298
448
  export { EditableItemLayer as default };
@@ -52,51 +52,23 @@
52
52
  cursor: nwse-resize;
53
53
  }
54
54
 
55
- &-n {
56
- left: 50%;
57
- top: 0;
58
- margin-left: -10px;
59
- cursor: ns-resize;
60
- }
61
-
62
55
  &-ne {
63
56
  right: 0;
64
57
  top: 0;
65
58
  cursor: nesw-resize;
66
59
  }
67
60
 
68
- &-e {
69
- right: 0;
70
- top: 50%;
71
- margin-top: -10px;
72
- cursor: ew-resize;
73
- }
74
-
75
61
  &-se {
76
62
  right: 0;
77
63
  bottom: 0;
78
64
  cursor: nwse-resize;
79
65
  }
80
66
 
81
- &-s {
82
- left: 50%;
83
- bottom: 0;
84
- margin-left: -10px;
85
- cursor: ns-resize;
86
- }
87
-
88
67
  &-sw {
89
68
  left: 0;
90
69
  bottom: 0;
91
70
  cursor: nesw-resize;
92
71
  }
93
-
94
- &-w {
95
- left: 0;
96
- top: 50%;
97
- margin-top: -10px;
98
- cursor: ew-resize;
99
- }
100
72
  }
101
73
 
102
74
  .pisell-floor-map-layout-edit-quick-actions {