@pisell/materials 1.0.1063 → 1.0.1065

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 (112) 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/index.js +1 -1
  5. package/build/lowcode/meta.js +7 -7
  6. package/build/lowcode/preview.js +9 -9
  7. package/build/lowcode/render/default/view.css +1 -1
  8. package/build/lowcode/render/default/view.js +26 -26
  9. package/build/lowcode/view.css +1 -1
  10. package/build/lowcode/view.js +26 -26
  11. package/es/components/dataSourceComponents/dataSourceForm/utils.d.ts +1 -1
  12. package/es/components/dataSourceComponents/dataSourceTable/hooks/useTableProps.d.ts +3 -3
  13. package/es/components/dataSourceComponents/fields/RecordListWrapper/index.d.ts +1 -1
  14. package/es/components/dataSourceComponents/fields/Select/index.d.ts +1 -1
  15. package/es/components/dataSourceComponents/fields/Tabs/index.d.ts +1 -1
  16. package/es/components/dataSourceComponents/fields/index.d.ts +6 -6
  17. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.js +43 -31
  18. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +60 -16
  19. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +8 -0
  20. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.js +22 -12
  21. package/es/components/pisellFloorMapLayout/components/FloorMapToolbar.less +7 -5
  22. package/es/components/pisellFloorMapLayout/components/MapLayer.js +5 -2
  23. package/es/components/pisellFloorMapLayout/components/ViewControls.d.ts +4 -0
  24. package/es/components/pisellFloorMapLayout/components/ViewControls.js +29 -10
  25. package/es/components/pisellFloorMapLayout/components/ViewControls.less +36 -12
  26. package/es/components/pisellFloorMapLayout/components/ViewControlsWithZoom.d.ts +2 -0
  27. package/es/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +3 -1
  28. package/es/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -2
  29. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEditState.d.ts +1 -0
  30. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +166 -43
  31. package/es/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.d.ts +1 -1
  32. package/es/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +90 -13
  33. package/es/components/pisellFloorMapLayout/index.d.ts +2 -0
  34. package/es/components/pisellFloorMapLayout/index.js +2 -0
  35. package/es/components/pisellFloorMapLayout/locales.d.ts +6 -0
  36. package/es/components/pisellFloorMapLayout/locales.js +9 -3
  37. package/es/components/pisellFloorMapLayout/types.d.ts +9 -2
  38. package/es/components/pisellFloorMapLayout/utils/floorMapDropUtils.d.ts +1 -0
  39. package/es/components/pisellFloorMapLayout/utils/floorMapDropUtils.js +10 -1
  40. package/es/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.d.ts +11 -0
  41. package/es/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +86 -0
  42. package/es/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +3 -2
  43. package/es/components/pisellRecordBoard/PisellRecordBoard.js +6 -3
  44. package/es/components/pisellRecordBoard/index.d.ts +1 -0
  45. package/es/components/pisellRecordBoard/index.js +2 -1
  46. package/es/components/pisellRecordBoard/shellFrame/ToolBar/index.d.ts +1 -0
  47. package/es/components/pisellRecordBoard/shellFrame/ToolBar/index.js +21 -10
  48. package/es/components/pisellRecordBoard/shellFrame/ToolBar/recordBoardToolbar.less +23 -0
  49. package/es/components/pisellRecordBoard/shellFrame/index.d.ts +2 -0
  50. package/es/components/pisellRecordBoard/shellFrame/index.js +9 -3
  51. package/es/components/pisellRecordBoard/shellFrame/shellFrameFloorMap.less +25 -0
  52. package/es/components/pisellRecordBoard/types.d.ts +6 -0
  53. package/es/components/pisellReservationScheduleBand/PisellReservationScheduleBand.js +4 -1
  54. package/es/components/pisellReservationScheduleBand/PisellReservationScheduleBand.less +18 -1
  55. package/es/components/pisellReservationScheduleBand/types.d.ts +4 -0
  56. package/es/components/pisellTimeNavigator/PisellTimeNavigator.js +180 -22
  57. package/es/components/pisellTimeNavigator/PisellTimeNavigator.less +11 -0
  58. package/es/components/pisellTimeNavigator/components/CursorLayer.js +4 -4
  59. package/es/components/pisellTimeNavigator/components/ScaleLayer.js +3 -3
  60. package/es/components/pisellTimeNavigator/types.d.ts +11 -0
  61. package/es/hooks/useResponsive.d.ts +1 -1
  62. package/es/index.d.ts +2 -2
  63. package/es/index.js +2 -2
  64. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.js +39 -36
  65. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +56 -12
  66. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +8 -0
  67. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.js +19 -10
  68. package/lib/components/pisellFloorMapLayout/components/FloorMapToolbar.less +7 -5
  69. package/lib/components/pisellFloorMapLayout/components/MapLayer.js +3 -1
  70. package/lib/components/pisellFloorMapLayout/components/ViewControls.d.ts +4 -0
  71. package/lib/components/pisellFloorMapLayout/components/ViewControls.js +28 -6
  72. package/lib/components/pisellFloorMapLayout/components/ViewControls.less +36 -12
  73. package/lib/components/pisellFloorMapLayout/components/ViewControlsWithZoom.d.ts +2 -0
  74. package/lib/components/pisellFloorMapLayout/components/ViewControlsWithZoom.js +3 -1
  75. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEditState.d.ts +1 -0
  76. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +143 -29
  77. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.d.ts +1 -1
  78. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +86 -13
  79. package/lib/components/pisellFloorMapLayout/index.d.ts +2 -0
  80. package/lib/components/pisellFloorMapLayout/index.js +6 -0
  81. package/lib/components/pisellFloorMapLayout/locales.d.ts +6 -0
  82. package/lib/components/pisellFloorMapLayout/locales.js +9 -3
  83. package/lib/components/pisellFloorMapLayout/types.d.ts +9 -2
  84. package/lib/components/pisellFloorMapLayout/utils/floorMapDropUtils.d.ts +1 -0
  85. package/lib/components/pisellFloorMapLayout/utils/floorMapDropUtils.js +7 -1
  86. package/lib/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.d.ts +11 -0
  87. package/lib/components/pisellFloorMapLayout/utils/floorMapElementKindUtils.js +69 -0
  88. package/lib/components/pisellFloorMapLayout/utils/floorMapFallbackRender.js +2 -1
  89. package/lib/components/pisellRecordBoard/PisellRecordBoard.js +5 -1
  90. package/lib/components/pisellRecordBoard/index.d.ts +1 -0
  91. package/lib/components/pisellRecordBoard/index.js +5 -2
  92. package/lib/components/pisellRecordBoard/shellFrame/ToolBar/index.d.ts +1 -0
  93. package/lib/components/pisellRecordBoard/shellFrame/ToolBar/index.js +13 -5
  94. package/lib/components/pisellRecordBoard/shellFrame/ToolBar/recordBoardToolbar.less +23 -0
  95. package/lib/components/pisellRecordBoard/shellFrame/index.d.ts +2 -0
  96. package/lib/components/pisellRecordBoard/shellFrame/index.js +11 -4
  97. package/lib/components/pisellRecordBoard/shellFrame/shellFrameFloorMap.less +25 -0
  98. package/lib/components/pisellRecordBoard/types.d.ts +6 -0
  99. package/lib/components/pisellReservationScheduleBand/PisellReservationScheduleBand.js +2 -1
  100. package/lib/components/pisellReservationScheduleBand/PisellReservationScheduleBand.less +18 -1
  101. package/lib/components/pisellReservationScheduleBand/types.d.ts +4 -0
  102. package/lib/components/pisellTimeNavigator/PisellTimeNavigator.js +177 -12
  103. package/lib/components/pisellTimeNavigator/PisellTimeNavigator.less +11 -0
  104. package/lib/components/pisellTimeNavigator/types.d.ts +11 -0
  105. package/lib/index.d.ts +2 -2
  106. package/lib/index.js +10 -0
  107. package/lowcode/floor-map-image-element/meta.ts +113 -0
  108. package/lowcode/floor-map-layout-provider/meta.ts +1 -1
  109. package/lowcode/pisell-floor-map-layout/meta.ts +37 -1
  110. package/lowcode/render-figma-style-round-table-card/meta.ts +1 -1
  111. package/lowcode/render-figma-style-table-card/meta.ts +1 -1
  112. package/package.json +3 -3
@@ -45,6 +45,28 @@ var import_layoutMeasurements = require("../utils/layoutMeasurements");
45
45
  var import_floorMapCanvasDisplay = require("../utils/floorMapCanvasDisplay");
46
46
  var import_types = require("../types");
47
47
  var IMAGE_PLACE_DEFAULT = { width: 400, height: 240 };
48
+ var GRID_VIS_STORAGE_PREFIX = "pisell-floor-map-layout.grid-visible";
49
+ function getGridVisStorageKey(canvasId) {
50
+ return `${GRID_VIS_STORAGE_PREFIX}:${canvasId}`;
51
+ }
52
+ function readStoredGridVisible(canvasId) {
53
+ try {
54
+ const raw = localStorage.getItem(getGridVisStorageKey(canvasId));
55
+ if (raw == null) return null;
56
+ if (raw === "1") return true;
57
+ if (raw === "0") return false;
58
+ return null;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ function writeStoredGridVisible(canvasId, visible) {
64
+ try {
65
+ localStorage.setItem(getGridVisStorageKey(canvasId), visible ? "1" : "0");
66
+ } catch {
67
+ }
68
+ }
69
+ var BATCH_PLACE_OFFSET = 16;
48
70
  function newInstanceId() {
49
71
  return `elm_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
50
72
  }
@@ -83,13 +105,26 @@ function useFloorMapEditState(params) {
83
105
  const [placeMode, setPlaceMode] = (0, import_react.useState)(null);
84
106
  const [saving, setSaving] = (0, import_react.useState)(false);
85
107
  const [savingRecord, setSavingRecord] = (0, import_react.useState)(false);
108
+ const [savedBaselineConfig, setSavedBaselineConfig] = (0, import_react.useState)(null);
86
109
  const config = isFloorMapView ? draftConfig ?? floorMapConfigProp : null;
87
110
  const configRef = (0, import_react.useRef)(config);
88
111
  configRef.current = config;
89
112
  (0, import_react.useEffect)(() => {
90
113
  if (!floorMapConfigProp) return;
91
- const nextConfig = (0, import_floorMapViewConfigDiff.normalizeFloorMapViewConfigFromProp)(floorMapConfigProp);
114
+ const baseConfig = (0, import_floorMapViewConfigDiff.normalizeFloorMapViewConfigFromProp)(floorMapConfigProp);
115
+ const nextConfig = {
116
+ ...baseConfig,
117
+ canvases: baseConfig.canvases.map((canvas) => {
118
+ const stored = readStoredGridVisible(canvas.id);
119
+ if (stored == null) return canvas;
120
+ return {
121
+ ...canvas,
122
+ mapLayer: { ...canvas.mapLayer ?? {}, showGrid: stored }
123
+ };
124
+ })
125
+ };
92
126
  setDraftConfig(nextConfig);
127
+ setSavedBaselineConfig(null);
93
128
  const enabled = nextConfig.canvases.filter((c) => c.enabled).sort((a, b) => a.order - b.order);
94
129
  setCurrentCanvasId(
95
130
  (prev) => {
@@ -100,9 +135,9 @@ function useFloorMapEditState(params) {
100
135
  }, [floorMapConfigProp]);
101
136
  const isLayoutDirty = (0, import_react.useMemo)(() => {
102
137
  if (!isFloorMapView || !draftConfig || !floorMapConfigProp) return false;
103
- const baseline = (0, import_floorMapViewConfigDiff.normalizeFloorMapViewConfigFromProp)(floorMapConfigProp);
138
+ const baseline = savedBaselineConfig ?? (0, import_floorMapViewConfigDiff.normalizeFloorMapViewConfigFromProp)(floorMapConfigProp);
104
139
  return !(0, import_floorMapViewConfigDiff.floorMapViewConfigsEqual)(draftConfig, baseline);
105
- }, [isFloorMapView, draftConfig, floorMapConfigProp]);
140
+ }, [isFloorMapView, draftConfig, floorMapConfigProp, savedBaselineConfig]);
106
141
  const handleAddCanvas = (0, import_react.useCallback)(() => {
107
142
  (0, import_floorMapCanvasDisplay.initFloorMapLayoutLocales)(engineLocale);
108
143
  const label = import_utils.locales.getText(
@@ -243,6 +278,26 @@ function useFloorMapEditState(params) {
243
278
  const allKeys = Object.keys(dataSources ?? {});
244
279
  const allowedKeys = ((_a2 = kindConfig == null ? void 0 : kindConfig.allowedDataSourceKeys) == null ? void 0 : _a2.length) ? kindConfig.allowedDataSourceKeys.filter((k) => allKeys.includes(k)) : allKeys;
245
280
  const pickKey = scene.dataSourceKey ?? allowedKeys[0] ?? allKeys[0];
281
+ const recordIdsToPlace = scene.recordIds && scene.recordIds.length > 0 ? scene.recordIds : scene.recordId ? [scene.recordId] : [];
282
+ const isStage = scene.elementKind === import_types.FLOOR_MAP_STAGE_ELEMENT_KIND;
283
+ const isImage = (0, import_floorMapElementKindUtils.isFloorMapImageElementKind)(scene.elementKind);
284
+ const w = isImage ? IMAGE_PLACE_DEFAULT.width : isStage ? import_floorMapStageDefaults.STAGE_DECO_DEFAULT.width : defaultPlacementWidth;
285
+ const h = isImage ? IMAGE_PLACE_DEFAULT.height : isStage ? import_floorMapStageDefaults.STAGE_DECO_DEFAULT.height : defaultPlacementHeight;
286
+ const useDecoSize = policy === "none" && scene.elementKind !== import_types.FLOOR_MAP_STAGE_ELEMENT_KIND && !isImage;
287
+ const buildOneElement = (dataBinding2, x, y, name) => ({
288
+ instanceId: newInstanceId(),
289
+ canvasId: currentCanvasId,
290
+ x,
291
+ y,
292
+ name,
293
+ zIndex: isImage ? -5 : 0,
294
+ width: useDecoSize ? defaultDecoSize : w,
295
+ height: useDecoSize ? defaultDecoSize : h,
296
+ elementKind: scene.elementKind,
297
+ shape: isStage || isImage ? "rect" : kindConfig == null ? void 0 : kindConfig.defaultShape,
298
+ dataBinding: dataBinding2,
299
+ ...isImage ? { imageUrl: "" } : {}
300
+ });
246
301
  let dataBinding;
247
302
  if (policy !== "none" && pickKey) {
248
303
  const dsList = (dataSources == null ? void 0 : dataSources[pickKey]) ?? [];
@@ -251,60 +306,99 @@ function useFloorMapEditState(params) {
251
306
  dataSourceKey: pickKey,
252
307
  recordId: `new_${Date.now()}`
253
308
  };
254
- } else if (!scene.recordId) {
309
+ } else if (recordIdsToPlace.length === 0) {
255
310
  if (policy === "required") {
256
311
  import_antd.message.warning("请先在侧栏选择数据源与数据行,再落位");
257
312
  return false;
258
313
  }
259
- } else {
260
- const record = dsList.find((r) => r.id === scene.recordId);
314
+ } else if (recordIdsToPlace.length === 1) {
315
+ const rid = recordIdsToPlace[0];
316
+ const record = dsList.find((r) => r.id === rid);
261
317
  if (!record) {
262
318
  import_antd.message.error("所选记录不存在,请重新选择数据行");
263
319
  return false;
264
320
  }
265
321
  const cand = {
266
322
  dataSourceKey: pickKey,
267
- recordId: scene.recordId
323
+ recordId: rid
268
324
  };
269
325
  if (duplicateBinding(config.sceneElements, cand)) {
270
326
  import_antd.message.warning("该数据记录已在平面图中绑定,请勿重复添加");
271
327
  return false;
272
328
  }
273
329
  dataBinding = cand;
330
+ } else {
331
+ const newEls = [];
332
+ let skippedDup = 0;
333
+ let skippedMissing = 0;
334
+ let placeIndex = 0;
335
+ const seenInBatch = /* @__PURE__ */ new Set();
336
+ recordIdsToPlace.forEach((rid) => {
337
+ const record = dsList.find((r) => r.id === rid);
338
+ if (!record) {
339
+ skippedMissing += 1;
340
+ return;
341
+ }
342
+ const cand = {
343
+ dataSourceKey: pickKey,
344
+ recordId: rid
345
+ };
346
+ if (seenInBatch.has(rid) || duplicateBinding(config.sceneElements, cand)) {
347
+ skippedDup += 1;
348
+ return;
349
+ }
350
+ seenInBatch.add(rid);
351
+ const row = record;
352
+ const placementName2 = String((row == null ? void 0 : row.name) ?? rid);
353
+ newEls.push(
354
+ buildOneElement(
355
+ cand,
356
+ contentX + placeIndex * BATCH_PLACE_OFFSET,
357
+ contentY + placeIndex * BATCH_PLACE_OFFSET,
358
+ placementName2
359
+ )
360
+ );
361
+ placeIndex += 1;
362
+ });
363
+ if (skippedDup > 0) {
364
+ import_antd.message.warning(`已跳过 ${skippedDup} 条已在平面图中绑定的记录`);
365
+ }
366
+ if (skippedMissing > 0) {
367
+ import_antd.message.warning(`有 ${skippedMissing} 条记录不存在,已跳过`);
368
+ }
369
+ if (newEls.length === 0) {
370
+ import_antd.message.warning("没有可落位的记录");
371
+ return false;
372
+ }
373
+ setDraftConfig(
374
+ (prev) => prev ? {
375
+ ...prev,
376
+ sceneElements: [...prev.sceneElements ?? [], ...newEls]
377
+ } : prev
378
+ );
379
+ return true;
274
380
  }
275
381
  }
276
- const isStage = scene.elementKind === import_types.FLOOR_MAP_STAGE_ELEMENT_KIND;
277
- const isImage = scene.elementKind === import_types.FLOOR_MAP_IMAGE_ELEMENT_KIND;
278
- const w = isImage ? IMAGE_PLACE_DEFAULT.width : isStage ? import_floorMapStageDefaults.STAGE_DECO_DEFAULT.width : defaultPlacementWidth;
279
- const h = isImage ? IMAGE_PLACE_DEFAULT.height : isStage ? import_floorMapStageDefaults.STAGE_DECO_DEFAULT.height : defaultPlacementHeight;
280
- const useDecoSize = policy === "none" && scene.elementKind !== import_types.FLOOR_MAP_STAGE_ELEMENT_KIND && !isImage;
281
382
  const placementName = (() => {
282
383
  var _a3, _b2;
283
384
  if (isStage) return import_floorMapStageDefaults.STAGE_DECO_DEFAULT.name;
284
385
  if (isImage) return "底图";
285
386
  const db = dataBinding;
286
- if (db && scene.recordId) {
387
+ const rid = recordIdsToPlace[0];
388
+ if (db && rid) {
287
389
  const row = (_a3 = dataSources == null ? void 0 : dataSources[db.dataSourceKey]) == null ? void 0 : _a3.find(
288
- (r) => r.id === db.recordId
390
+ (r) => r.id === rid
289
391
  );
290
- return String((row == null ? void 0 : row.name) ?? db.recordId);
392
+ return String((row == null ? void 0 : row.name) ?? rid);
291
393
  }
292
394
  return ((_b2 = kindConfig == null ? void 0 : kindConfig.defaultPresentation) == null ? void 0 : _b2.title) ?? "";
293
395
  })();
294
- const newEl = {
295
- instanceId: newInstanceId(),
296
- canvasId: currentCanvasId,
297
- x: contentX,
298
- y: contentY,
299
- name: placementName,
300
- zIndex: isImage ? -5 : 0,
301
- width: useDecoSize ? defaultDecoSize : w,
302
- height: useDecoSize ? defaultDecoSize : h,
303
- elementKind: scene.elementKind,
304
- shape: isStage || isImage ? "rect" : kindConfig == null ? void 0 : kindConfig.defaultShape,
396
+ const newEl = buildOneElement(
305
397
  dataBinding,
306
- ...isImage ? { imageUrl: "" } : {}
307
- };
398
+ contentX,
399
+ contentY,
400
+ placementName
401
+ );
308
402
  setDraftConfig(
309
403
  (prev) => prev ? { ...prev, sceneElements: [...prev.sceneElements ?? [], newEl] } : prev
310
404
  );
@@ -346,10 +440,29 @@ function useFloorMapEditState(params) {
346
440
  setSaving(true);
347
441
  try {
348
442
  onSave(draftConfig);
443
+ setSavedBaselineConfig(draftConfig);
349
444
  } finally {
350
445
  setSaving(false);
351
446
  }
352
447
  }, [draftConfig, onSave]);
448
+ const toggleDraftCanvasShowGrid = (0, import_react.useCallback)(() => {
449
+ setDraftConfig((prev) => {
450
+ if (!prev || !currentCanvasId) return prev;
451
+ return {
452
+ ...prev,
453
+ canvases: prev.canvases.map((c) => {
454
+ var _a2;
455
+ if (c.id !== currentCanvasId) return c;
456
+ const nextShow = !Boolean((_a2 = c.mapLayer) == null ? void 0 : _a2.showGrid);
457
+ writeStoredGridVisible(currentCanvasId, nextShow);
458
+ return {
459
+ ...c,
460
+ mapLayer: { ...c.mapLayer, showGrid: nextShow }
461
+ };
462
+ })
463
+ };
464
+ });
465
+ }, [currentCanvasId]);
353
466
  const handleEditPanelUpdate = (0, import_react.useCallback)(
354
467
  (patch) => {
355
468
  var _a2;
@@ -422,7 +535,8 @@ function useFloorMapEditState(params) {
422
535
  placeSceneElementFromPaletteDrop,
423
536
  editPanelParams,
424
537
  placeOverlayClass,
425
- isLayoutDirty
538
+ isLayoutDirty,
539
+ toggleDraftCanvasShowGrid
426
540
  };
427
541
  }
428
542
  // Annotate the CommonJS export names for ESM import in node:
@@ -56,7 +56,7 @@ export declare function useFloorMapTransformMetrics<T extends FloorMapItemBase>(
56
56
  setScale: import("react").Dispatch<import("react").SetStateAction<number>>;
57
57
  isFullscreen: boolean;
58
58
  toggleFullscreen: () => void;
59
- fitBounds: () => void;
59
+ fitBounds: () => boolean;
60
60
  lastTransformRef: MutableRefObject<{
61
61
  x: number;
62
62
  y: number;
@@ -65,7 +65,7 @@ function useFloorMapTransformMetrics(params) {
65
65
  setMapAreaSize({ width: el.clientWidth, height: el.clientHeight });
66
66
  return () => ro.disconnect();
67
67
  }, []);
68
- const autoFitOnMount = zoom.autoFitOnMount === true;
68
+ const autoFitOnMount = zoom.autoFitOnMount !== false;
69
69
  const zoomConfig = (0, import_react.useMemo)(
70
70
  () => ({
71
71
  initialScale: zoom.defaultScale ?? 1,
@@ -210,45 +210,112 @@ function useFloorMapTransformMetrics(params) {
210
210
  const fitBounds = (0, import_react.useCallback)(() => {
211
211
  const ctx = transformRef.current;
212
212
  const container = containerRef.current;
213
- if (!ctx || !container) return;
213
+ if (!ctx || !container) return false;
214
214
  const wrapper = container.querySelector(".react-transform-wrapper");
215
- if (!wrapper) return;
215
+ if (!wrapper) return false;
216
216
  const wrapperRect = wrapper.getBoundingClientRect();
217
- const nextScale = Math.min(
217
+ if (wrapperRect.width <= 0 || wrapperRect.height <= 0) return false;
218
+ const pixelBox = isFloorMapView && effectiveItemUnit === "pixel" && items.length > 0 ? (0, import_layoutMeasurements.getPixelBoundingBox)(items, cellSize) : null;
219
+ if (pixelBox) {
220
+ const bw = Math.max(1, pixelBox.maxRight - pixelBox.minLeft);
221
+ const bh = Math.max(1, pixelBox.maxBottom - pixelBox.minTop);
222
+ const edgePx = 0;
223
+ const availW = Math.max(1, wrapperRect.width - 2 * edgePx);
224
+ const availH = Math.max(1, wrapperRect.height - 2 * edgePx);
225
+ const raw2 = Math.min(availW / bw, availH / bh);
226
+ const nextScale2 = Math.min(raw2, zoom.max ?? 2);
227
+ const ap = alignPadSingle ?? padSingle;
228
+ const bboxCx = ap + bw / 2;
229
+ const bboxCy = ap + bh / 2;
230
+ const x2 = wrapperRect.width / 2 - bboxCx * nextScale2;
231
+ const y2 = wrapperRect.height / 2 - bboxCy * nextScale2;
232
+ ctx.setTransform(x2, y2, nextScale2);
233
+ return true;
234
+ }
235
+ if (contentWidth <= 0 || contentHeight <= 0) return false;
236
+ const raw = Math.min(
218
237
  wrapperRect.width / contentWidth,
219
- wrapperRect.height / contentHeight,
220
- zoom.max ?? 2
238
+ wrapperRect.height / contentHeight
221
239
  );
240
+ const nextScale = Math.min(raw, zoom.max ?? 2);
222
241
  const padA = alignPadSingle ?? 0;
223
242
  const x = VIEWPORT_TOP_LEFT_MARGIN - padA * nextScale;
224
243
  const y = VIEWPORT_TOP_LEFT_MARGIN - padA * nextScale;
225
244
  ctx.setTransform(x, y, nextScale);
226
- }, [contentWidth, contentHeight, zoom.max, transformRef, alignPadSingle]);
245
+ return true;
246
+ }, [
247
+ contentWidth,
248
+ contentHeight,
249
+ zoom.max,
250
+ transformRef,
251
+ alignPadSingle,
252
+ isFloorMapView,
253
+ effectiveItemUnit,
254
+ items,
255
+ cellSize,
256
+ padSingle
257
+ ]);
227
258
  const autoFitOnceRef = (0, import_react.useRef)({
228
259
  frame: "",
229
260
  done: false
230
261
  });
231
- (0, import_react.useEffect)(() => {
232
- if (!isFloorMapView || !autoFitOnMount || frameResetKey === void 0)
233
- return;
262
+ const prevItemsCountForAutoFitRef = (0, import_react.useRef)(null);
263
+ (0, import_react.useLayoutEffect)(() => {
264
+ if (!isFloorMapView || !autoFitOnMount || frameResetKey === void 0) return;
234
265
  const frame = String(frameResetKey);
235
266
  if (autoFitOnceRef.current.frame !== frame) {
236
267
  autoFitOnceRef.current = { frame, done: false };
268
+ prevItemsCountForAutoFitRef.current = null;
269
+ }
270
+ const itemCount = items.length;
271
+ const prevN = prevItemsCountForAutoFitRef.current;
272
+ if (prevN !== null && prevN === 0 && itemCount > 0) {
273
+ autoFitOnceRef.current.done = false;
237
274
  }
275
+ prevItemsCountForAutoFitRef.current = itemCount;
238
276
  if (autoFitOnceRef.current.done) return;
277
+ if (effectiveItemUnit === "pixel" && itemCount === 0) {
278
+ return;
279
+ }
239
280
  if (contentWidth <= 0 || contentHeight <= 0) return;
240
281
  let cancelled = false;
241
282
  let raf = 0;
283
+ let attempts = 0;
284
+ const MAX_AUTO_FIT_ATTEMPTS = 160;
242
285
  const tick = () => {
243
286
  if (cancelled) return;
287
+ if (attempts >= MAX_AUTO_FIT_ATTEMPTS) return;
244
288
  const ctx = transformRef.current;
245
289
  const container = containerRef.current;
246
- if (!ctx || !(container == null ? void 0 : container.querySelector(".react-transform-wrapper"))) {
290
+ const wrapper = container == null ? void 0 : container.querySelector(
291
+ ".react-transform-wrapper"
292
+ );
293
+ if (!ctx || !wrapper) {
294
+ attempts += 1;
247
295
  raf = requestAnimationFrame(tick);
248
296
  return;
249
297
  }
250
- fitBounds();
251
- autoFitOnceRef.current.done = true;
298
+ const rect = wrapper.getBoundingClientRect();
299
+ if (rect.width <= 0 || rect.height <= 0) {
300
+ attempts += 1;
301
+ raf = requestAnimationFrame(tick);
302
+ return;
303
+ }
304
+ raf = requestAnimationFrame(() => {
305
+ if (cancelled) return;
306
+ raf = requestAnimationFrame(() => {
307
+ if (cancelled) return;
308
+ const ok = fitBounds();
309
+ if (ok) {
310
+ autoFitOnceRef.current.done = true;
311
+ return;
312
+ }
313
+ attempts += 1;
314
+ if (attempts < MAX_AUTO_FIT_ATTEMPTS) {
315
+ raf = requestAnimationFrame(tick);
316
+ }
317
+ });
318
+ });
252
319
  };
253
320
  raf = requestAnimationFrame(tick);
254
321
  return () => {
@@ -259,8 +326,14 @@ function useFloorMapTransformMetrics(params) {
259
326
  isFloorMapView,
260
327
  autoFitOnMount,
261
328
  frameResetKey,
329
+ effectiveItemUnit,
330
+ items.length,
262
331
  contentWidth,
263
332
  contentHeight,
333
+ viewportSize.width,
334
+ viewportSize.height,
335
+ mapAreaSize.width,
336
+ mapAreaSize.height,
264
337
  fitBounds,
265
338
  transformRef
266
339
  ]);
@@ -5,6 +5,8 @@
5
5
  export { PisellFloorMapLayout } from './PisellFloorMapLayout';
6
6
  export type { PisellFloorMapLayoutProps, PisellFloorMapLayoutRef, FloorMapItemBase, FloorMapItemUnit, FloorMapLayerConfig, FloorMapZoomConfig, FloorMapPanConfig, FloorMapControlsConfig, FloorMapItemShape, FloorMapBackgroundType, FloorMapPanBoundary, FloorMapLayoutMode, FloorMapGridLayoutConfig, FloorMapCanvas, FloorMapCanvasNameI18n, FloorMapElement, FloorMapData, FloorMapViewConfig, FloorMapCanvasUiConfig, FloorMapSceneElement, FloorMapSceneElementDataBinding, FloorMapPlaceMode, FloorMapElementKindCategory, FloorMapElementDataBindingPolicy, FloorMapElementDefaultPresentation, FloorMapEdge, FloorMapElementKindConfig, FloorMapElementType, FloorMapTextSlot, FloorMapDataSources, FloorMapRenderOptions, FloorMapEditPanelParams, DataSourceFormColumn, FloorMapMergedItem, FloorMapRecordFormRenderContext, FloorMapDataSourceRecordFormContent, FloorMapFullscreenMode, FloorMapLayoutContextValue, } from './types';
7
7
  export { FLOOR_MAP_STAGE_ELEMENT_KIND, FLOOR_MAP_IMAGE_ELEMENT_KIND, } from './types';
8
+ export { renderFloorMapFallbackPlaceholder } from './utils/floorMapFallbackRender';
9
+ export { isFloorMapImageElementKind } from './utils/floorMapElementKindUtils';
8
10
  export { FloorMapLayoutProvider, useFloorMapLayoutContext, EMPTY_FLOOR_MAP_LAYOUT_CONTEXT, } from './context/FloorMapLayoutContext';
9
11
  export type { FloorMapLayoutProviderProps } from './context/FloorMapLayoutContext';
10
12
  export { mergeFloorMapLayoutPropsFromContext } from './context/mergeFloorMapLayoutContext';
@@ -28,14 +28,18 @@ __export(pisellFloorMapLayout_exports, {
28
28
  getFigmaTableCardFromMerged: () => import_FigmaTableCard.getFigmaTableCardFromMerged,
29
29
  getRenderItemByKindRoundTable: () => import_FigmaTableCard.getRenderItemByKindRoundTable,
30
30
  getRenderItemByKindTable: () => import_FigmaTableCard.getRenderItemByKindTable,
31
+ isFloorMapImageElementKind: () => import_floorMapElementKindUtils.isFloorMapImageElementKind,
31
32
  mergeFloorMapLayoutPropsFromContext: () => import_mergeFloorMapLayoutContext.mergeFloorMapLayoutPropsFromContext,
32
33
  renderFigmaStyleRoundTableCard: () => import_FigmaTableCard.renderFigmaStyleRoundTableCard,
33
34
  renderFigmaStyleTableCard: () => import_FigmaTableCard.renderFigmaStyleTableCard,
35
+ renderFloorMapFallbackPlaceholder: () => import_floorMapFallbackRender.renderFloorMapFallbackPlaceholder,
34
36
  useFloorMapLayoutContext: () => import_FloorMapLayoutContext.useFloorMapLayoutContext
35
37
  });
36
38
  module.exports = __toCommonJS(pisellFloorMapLayout_exports);
37
39
  var import_PisellFloorMapLayout = require("./PisellFloorMapLayout");
38
40
  var import_types = require("./types");
41
+ var import_floorMapFallbackRender = require("./utils/floorMapFallbackRender");
42
+ var import_floorMapElementKindUtils = require("./utils/floorMapElementKindUtils");
39
43
  var import_FloorMapLayoutContext = require("./context/FloorMapLayoutContext");
40
44
  var import_mergeFloorMapLayoutContext = require("./context/mergeFloorMapLayoutContext");
41
45
  var import_FloorMapImageElement = require("./components/FloorMapImageElement");
@@ -51,8 +55,10 @@ var import_FigmaTableCard = require("./components/FigmaTableCard");
51
55
  getFigmaTableCardFromMerged,
52
56
  getRenderItemByKindRoundTable,
53
57
  getRenderItemByKindTable,
58
+ isFloorMapImageElementKind,
54
59
  mergeFloorMapLayoutPropsFromContext,
55
60
  renderFigmaStyleRoundTableCard,
56
61
  renderFigmaStyleTableCard,
62
+ renderFloorMapFallbackPlaceholder,
57
63
  useFloorMapLayoutContext
58
64
  });
@@ -46,6 +46,8 @@ declare const _default: {
46
46
  'pisell-floor-map-layout.edit-panel.extra-image-url': string;
47
47
  'pisell-floor-map-layout.edit-panel.ph-https': string;
48
48
  'pisell-floor-map-layout.edit-panel.label-canvas': string;
49
+ 'pisell-floor-map-layout.edit-panel.label-element-kind': string;
50
+ 'pisell-floor-map-layout.edit-panel.hint-kind-binding-cleared': string;
49
51
  'pisell-floor-map-layout.edit-panel.apply': string;
50
52
  'pisell-floor-map-layout.edit-panel.data-binding': string;
51
53
  'pisell-floor-map-layout.edit-panel.data-binding-note': string;
@@ -106,6 +108,8 @@ declare const _default: {
106
108
  'pisell-floor-map-layout.edit-panel.extra-image-url': string;
107
109
  'pisell-floor-map-layout.edit-panel.ph-https': string;
108
110
  'pisell-floor-map-layout.edit-panel.label-canvas': string;
111
+ 'pisell-floor-map-layout.edit-panel.label-element-kind': string;
112
+ 'pisell-floor-map-layout.edit-panel.hint-kind-binding-cleared': string;
109
113
  'pisell-floor-map-layout.edit-panel.apply': string;
110
114
  'pisell-floor-map-layout.edit-panel.data-binding': string;
111
115
  'pisell-floor-map-layout.edit-panel.data-binding-note': string;
@@ -166,6 +170,8 @@ declare const _default: {
166
170
  'pisell-floor-map-layout.edit-panel.extra-image-url': string;
167
171
  'pisell-floor-map-layout.edit-panel.ph-https': string;
168
172
  'pisell-floor-map-layout.edit-panel.label-canvas': string;
173
+ 'pisell-floor-map-layout.edit-panel.label-element-kind': string;
174
+ 'pisell-floor-map-layout.edit-panel.hint-kind-binding-cleared': string;
169
175
  'pisell-floor-map-layout.edit-panel.apply': string;
170
176
  'pisell-floor-map-layout.edit-panel.data-binding': string;
171
177
  'pisell-floor-map-layout.edit-panel.data-binding-note': string;
@@ -50,7 +50,7 @@ var locales_default = {
50
50
  "pisell-floor-map-layout.edit-panel.element-props": "Element",
51
51
  "pisell-floor-map-layout.edit-panel.close-aria": "Close",
52
52
  "pisell-floor-map-layout.edit-panel.hint-apply": "Position or size changed — click Apply to update the canvas.",
53
- "pisell-floor-map-layout.edit-panel.hint-save": "Layout draft is not saved — click Save at the top.",
53
+ "pisell-floor-map-layout.edit-panel.hint-save": "Layout draft is not saved — click Done editing to save.",
54
54
  "pisell-floor-map-layout.edit-panel.section-layout": "Position · Layer · Size",
55
55
  "pisell-floor-map-layout.edit-panel.lock-on": "Locked: drag/resize disabled on canvas; click to unlock",
56
56
  "pisell-floor-map-layout.edit-panel.lock-off": "Lock to disable drag and resize on canvas",
@@ -67,6 +67,8 @@ var locales_default = {
67
67
  "pisell-floor-map-layout.edit-panel.extra-image-url": "Use a network image URL; lower layer to place under tables.",
68
68
  "pisell-floor-map-layout.edit-panel.ph-https": "https://...",
69
69
  "pisell-floor-map-layout.edit-panel.label-canvas": "Canvas",
70
+ "pisell-floor-map-layout.edit-panel.label-element-kind": "Element type",
71
+ "pisell-floor-map-layout.edit-panel.hint-kind-binding-cleared": "Data binding was cleared because it does not apply to the new type.",
70
72
  "pisell-floor-map-layout.edit-panel.apply": "Apply",
71
73
  "pisell-floor-map-layout.edit-panel.data-binding": "Data binding",
72
74
  "pisell-floor-map-layout.edit-panel.data-binding-note": "Changing data source or row writes to the layout draft; click Save at the top to persist.",
@@ -110,7 +112,7 @@ var locales_default = {
110
112
  "pisell-floor-map-layout.edit-panel.element-props": "图元属性",
111
113
  "pisell-floor-map-layout.edit-panel.close-aria": "关闭",
112
114
  "pisell-floor-map-layout.edit-panel.hint-apply": "位置、尺寸等已修改,请点击「应用」同步到画布。",
113
- "pisell-floor-map-layout.edit-panel.hint-save": "布局草稿未持久化,请点击顶部「保存」。",
115
+ "pisell-floor-map-layout.edit-panel.hint-save": "布局草稿未持久化,请点击「完成编辑」保存。",
114
116
  "pisell-floor-map-layout.edit-panel.section-layout": "位置 · 层级 · 尺寸",
115
117
  "pisell-floor-map-layout.edit-panel.lock-on": "已锁定:画布不可拖拽/缩放;点击解锁",
116
118
  "pisell-floor-map-layout.edit-panel.lock-off": "锁定后画布不可拖拽与改尺寸",
@@ -127,6 +129,8 @@ var locales_default = {
127
129
  "pisell-floor-map-layout.edit-panel.extra-image-url": "可填网络图片地址;将层级调低可铺在桌位下方作底图。",
128
130
  "pisell-floor-map-layout.edit-panel.ph-https": "https://...",
129
131
  "pisell-floor-map-layout.edit-panel.label-canvas": "所属画布",
132
+ "pisell-floor-map-layout.edit-panel.label-element-kind": "图元类型",
133
+ "pisell-floor-map-layout.edit-panel.hint-kind-binding-cleared": "新类型与原先绑定不兼容,已清除数据绑定。",
130
134
  "pisell-floor-map-layout.edit-panel.apply": "应用",
131
135
  "pisell-floor-map-layout.edit-panel.data-binding": "数据绑定",
132
136
  "pisell-floor-map-layout.edit-panel.data-binding-note": "修改数据源或绑定行会立即写入布局草稿;持久化请点击顶部「保存」。",
@@ -170,7 +174,7 @@ var locales_default = {
170
174
  "pisell-floor-map-layout.edit-panel.element-props": "圖元屬性",
171
175
  "pisell-floor-map-layout.edit-panel.close-aria": "關閉",
172
176
  "pisell-floor-map-layout.edit-panel.hint-apply": "位置、尺寸等已修改,請點擊「套用」同步到畫布。",
173
- "pisell-floor-map-layout.edit-panel.hint-save": "佈局草稿未持久化,請點擊頂部「儲存」。",
177
+ "pisell-floor-map-layout.edit-panel.hint-save": "佈局草稿未持久化,請點擊「完成編輯」儲存。",
174
178
  "pisell-floor-map-layout.edit-panel.section-layout": "位置 · 層級 · 尺寸",
175
179
  "pisell-floor-map-layout.edit-panel.lock-on": "已鎖定:畫布不可拖曳/縮放;點擊解鎖",
176
180
  "pisell-floor-map-layout.edit-panel.lock-off": "鎖定後畫布不可拖曳與改尺寸",
@@ -187,6 +191,8 @@ var locales_default = {
187
191
  "pisell-floor-map-layout.edit-panel.extra-image-url": "可填網絡圖片地址;將層級調低可鋪在桌位下方作底圖。",
188
192
  "pisell-floor-map-layout.edit-panel.ph-https": "https://...",
189
193
  "pisell-floor-map-layout.edit-panel.label-canvas": "所屬畫布",
194
+ "pisell-floor-map-layout.edit-panel.label-element-kind": "圖元類型",
195
+ "pisell-floor-map-layout.edit-panel.hint-kind-binding-cleared": "新類型與原先綁定不相容,已清除數據綁定。",
190
196
  "pisell-floor-map-layout.edit-panel.apply": "套用",
191
197
  "pisell-floor-map-layout.edit-panel.data-binding": "數據綁定",
192
198
  "pisell-floor-map-layout.edit-panel.data-binding-note": "修改數據源或綁定行會立即寫入佈局草稿;持久化請點擊頂部「儲存」。",
@@ -82,8 +82,8 @@ export interface FloorMapZoomConfig {
82
82
  /** 缩放步进,默认 0.1 (10%) */
83
83
  step?: number;
84
84
  /**
85
- * 为 true 时:挂载后每个画布(frameResetKey)自动执行一次 fitBounds,使图元落在视口内。
86
- * true 时跳过「首帧 defaultScale 左上对齐」,避免与 fit 闪动叠加。
85
+ * 为 false 时关闭;未传或其它值时默认开启(平面图像素模式且当前画布有图元时,每画布自动 fitBounds 一次,可视区域刚好容纳全部图元)。
86
+ * 开启时跳过「首帧 defaultScale 左上对齐」,避免与 fit 闪动叠加。
87
87
  */
88
88
  autoFitOnMount?: boolean;
89
89
  }
@@ -108,6 +108,10 @@ export interface FloorMapControlsConfig {
108
108
  showFitBounds?: boolean;
109
109
  /** 是否显示全屏按钮,默认 false */
110
110
  showFullscreen?: boolean;
111
+ /**
112
+ * 是否显示网格开关(写入当前画布 draft 的 mapLayer.showGrid),默认 false
113
+ */
114
+ showGridToggle?: boolean;
111
115
  }
112
116
  /**
113
117
  * 布局模式(规则网格 vs 完全自由坐标)
@@ -317,7 +321,10 @@ export declare type FloorMapPlaceMode = null | {
317
321
  type: 'scene';
318
322
  elementKind: string;
319
323
  dataSourceKey?: string;
324
+ /** 单选落一条;与 {@link recordIds} 二选一,多选时优先 {@link recordIds} */
320
325
  recordId?: string;
326
+ /** 多选后在画布一次落多条,按锚点阶梯偏移 */
327
+ recordIds?: string[];
321
328
  };
322
329
  /** 阅读态 renderItem 可选第三参数 */
323
330
  export interface FloorMapRenderOptions {
@@ -7,6 +7,7 @@ export interface FloorMapPaletteDragPayload {
7
7
  elementKind: string;
8
8
  dataSourceKey?: string;
9
9
  recordId?: string;
10
+ recordIds?: string[];
10
11
  }
11
12
  /**
12
13
  * 与 {@link useFloorMapEditState} 落点一致:内容区 drop 的 offset 减去内容区 padding 偏移
@@ -47,7 +47,13 @@ function parsePaletteDragPayload(dt) {
47
47
  if (typeof elementKind !== "string" || !elementKind) return null;
48
48
  const dataSourceKey = typeof rec.dataSourceKey === "string" ? rec.dataSourceKey : void 0;
49
49
  const recordId = typeof rec.recordId === "string" ? rec.recordId : void 0;
50
- return { elementKind, dataSourceKey, recordId };
50
+ let recordIds;
51
+ const rawIds = rec.recordIds;
52
+ if (Array.isArray(rawIds)) {
53
+ recordIds = rawIds.filter((x) => typeof x === "string" && x);
54
+ if (recordIds.length === 0) recordIds = void 0;
55
+ }
56
+ return { elementKind, dataSourceKey, recordId, recordIds };
51
57
  } catch {
52
58
  return null;
53
59
  }
@@ -2,9 +2,20 @@
2
2
  * 图元种类解析与绑定展示前校验(供合并项渲染、工具栏逻辑复用)
3
3
  */
4
4
  import type { FloorMapElementKindConfig, FloorMapViewConfig, FloorMapSceneElement, FloorMapDataSources, FloorMapElementDataBindingPolicy } from '../types';
5
+ /** 与 RecordBoard Story 一致:`floorMapImage` */
6
+ export declare function isFloorMapImageElementKind(kind: string | undefined): boolean;
5
7
  /** dataBindingPolicy 缺省视为 none */
6
8
  export declare function resolveDataBindingPolicy(kind: FloorMapElementKindConfig | undefined): FloorMapElementDataBindingPolicy;
7
9
  export declare function getElementKindConfig(config: Pick<FloorMapViewConfig, 'elementKinds'> | undefined, elementKind: string | undefined): FloorMapElementKindConfig | undefined;
10
+ /**
11
+ * 与调色板分组一致:取与当前图元 **同一 categoryId**(含未填 categoryId 时落到首个分类)下的可切换种类,
12
+ * 排除舞台、底图。若仅有一种则编辑面板可不展示切换器。
13
+ */
14
+ export declare function getSwappableElementKindsInSameCategory(config: FloorMapViewConfig, elementKind: string | undefined): FloorMapElementKindConfig[];
15
+ /**
16
+ * 在右侧编辑面板将图元改为同分类下的另一种 kind 时的草稿 patch(绑定不兼容则清空)
17
+ */
18
+ export declare function patchSceneElementForKindSwitch(config: FloorMapViewConfig, element: FloorMapSceneElement, nextKindValue: string): Partial<FloorMapSceneElement>;
8
19
  /**
9
20
  * @returns 人类可读错误文案;无问题时返回 null
10
21
  */