@pisell/materials 1.0.1060 → 1.0.1062

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 (64) 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 +7 -7
  5. package/build/lowcode/render/default/view.css +1 -1
  6. package/build/lowcode/render/default/view.js +3 -3
  7. package/build/lowcode/view.css +1 -1
  8. package/build/lowcode/view.js +23 -23
  9. package/es/components/PisellLayouts/index.js +14 -14
  10. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.js +9 -4
  11. package/es/components/pisellFloorMapLayout/PisellFloorMapLayout.stories.js +34 -34
  12. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +55 -2
  13. package/es/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +1 -0
  14. package/es/components/pisellFloorMapLayout/components/ItemLayer.js +5 -5
  15. package/es/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +3 -2
  16. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEditState.d.ts +2 -0
  17. package/es/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +4 -2
  18. package/es/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +42 -1
  19. package/es/components/pisellFloorMapLayout/types.d.ts +17 -1
  20. package/es/components/pisellHierarchicalSummaryList/hooks/useExpandedKeys.js +2 -2
  21. package/es/components/pisellHierarchicalSummaryList/index.js +2 -2
  22. package/es/components/pisellRecordBoard/PisellRecordBoard.js +30 -5
  23. package/es/components/pisellRecordBoard/PisellRecordBoard.stories.js +51 -51
  24. package/es/components/pisellRecordBoard/shellFrame/ToolBar/index.js +4 -4
  25. package/es/components/pisellRecordBoard/shellFrame/index.js +4 -2
  26. package/es/components/pisellRecordBoard/types.d.ts +8 -2
  27. package/es/components/pisellReservationScheduleBand/PisellReservationScheduleBand.less +1 -1
  28. package/es/components/pisellTimeNavigator/PisellTimeNavigator.js +148 -51
  29. package/es/components/pisellTimeNavigator/PisellTimeNavigator.less +38 -4
  30. package/es/components/pisellTimeNavigator/components/BackgroundLayer.js +2 -2
  31. package/es/components/pisellTimeNavigator/components/CursorLayer.js +4 -4
  32. package/es/components/pisellTimeNavigator/components/GestureLayer.js +2 -2
  33. package/es/components/pisellTimeNavigator/components/NowButton.js +3 -3
  34. package/es/components/pisellTimeNavigator/components/ScaleLayer.js +3 -3
  35. package/es/components/pisellTimeNavigator/hooks/useDragInertia.js +2 -2
  36. package/es/components/pisellTimeNavigator/hooks/useRealtime.js +2 -2
  37. package/es/components/pisellTimeNavigator/hooks/useTimeAxis.js +8 -8
  38. package/es/components/pisellTimeNavigator/types.d.ts +4 -0
  39. package/es/components/pisellTimeNavigator/utils/index.d.ts +4 -0
  40. package/es/components/pisellTimeNavigator/utils/index.js +28 -14
  41. package/es/index.js +16 -16
  42. package/lib/components/pisellFloorMapLayout/PisellFloorMapLayout.js +7 -4
  43. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.js +231 -157
  44. package/lib/components/pisellFloorMapLayout/components/FloorMapEditPanel.less +1 -0
  45. package/lib/components/pisellFloorMapLayout/context/mergeFloorMapLayoutContext.js +2 -1
  46. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEditState.d.ts +2 -0
  47. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapEditState.js +4 -2
  48. package/lib/components/pisellFloorMapLayout/hooks/useFloorMapTransformMetrics.js +47 -1
  49. package/lib/components/pisellFloorMapLayout/types.d.ts +17 -1
  50. package/lib/components/pisellRecordBoard/PisellRecordBoard.js +32 -4
  51. package/lib/components/pisellRecordBoard/shellFrame/index.js +1 -0
  52. package/lib/components/pisellRecordBoard/types.d.ts +8 -2
  53. package/lib/components/pisellReservationScheduleBand/PisellReservationScheduleBand.less +1 -1
  54. package/lib/components/pisellTimeNavigator/PisellTimeNavigator.js +143 -29
  55. package/lib/components/pisellTimeNavigator/PisellTimeNavigator.less +38 -4
  56. package/lib/components/pisellTimeNavigator/types.d.ts +4 -0
  57. package/lib/components/pisellTimeNavigator/utils/index.d.ts +4 -0
  58. package/lib/components/pisellTimeNavigator/utils/index.js +9 -0
  59. package/lowcode/floor-map-layout-provider/meta.ts +1 -1
  60. package/lowcode/pisell-floor-map-layout/meta.ts +75 -1
  61. package/lowcode/pisell-hierarchical-summary-list/meta.ts +244 -244
  62. package/lowcode/render-figma-style-round-table-card/meta.ts +1 -1
  63. package/lowcode/render-figma-style-table-card/meta.ts +1 -1
  64. package/package.json +4 -4
@@ -37,6 +37,15 @@ var import_utils = require("@pisell/utils");
37
37
  var import_RecordBoardProvider = require("./context/RecordBoardProvider");
38
38
  var import_recordBoardColumns = require("./utils/recordBoardColumns");
39
39
  var defaultPagination = { pageNumber: 1, pageSize: 50 };
40
+ function readBodyViewFromStorage(key, fallback) {
41
+ if (typeof window === "undefined" || !key) return fallback;
42
+ try {
43
+ const raw = localStorage.getItem(key);
44
+ if (raw === "grid" || raw === "floorMap") return raw;
45
+ } catch {
46
+ }
47
+ return fallback;
48
+ }
40
49
  function mergeChildComponentProps(props) {
41
50
  const hasAny = props.grid != null || props.toolBar != null || props.search != null || props.paginationConfig != null || props.batchActionBar != null || props.cardList != null;
42
51
  if (!hasAny) return void 0;
@@ -82,6 +91,7 @@ function getDefaultRecordBoardContext(layoutType, selection, rowKey) {
82
91
  floorMapProps: void 0,
83
92
  floorMapLayoutContext: void 0,
84
93
  defaultBodyView: void 0,
94
+ floorMapLayoutRef: void 0,
85
95
  dataSources: void 0,
86
96
  gridDataSourceKey: void 0
87
97
  };
@@ -104,6 +114,8 @@ var PisellRecordBoard = (props) => {
104
114
  floorMap: floorMapProp,
105
115
  floorMapLayoutContext: floorMapLayoutContextProp,
106
116
  defaultBodyView,
117
+ bodyViewStorageKey,
118
+ floorMapLayoutRef,
107
119
  className: classNameProp,
108
120
  style: styleProp,
109
121
  ...rest
@@ -127,8 +139,21 @@ var PisellRecordBoard = (props) => {
127
139
  const [internalSelectedRows, setInternalSelectedRows] = (0, import_react.useState)([]);
128
140
  const [scrollAreaHeight, setScrollAreaHeight] = (0, import_react.useState)(null);
129
141
  const [filterResetKey, setFilterResetKey] = (0, import_react.useState)(0);
130
- const [bodyView, setBodyView] = (0, import_react.useState)(
131
- () => defaultBodyView === "floorMap" ? "floorMap" : "grid"
142
+ const [bodyView, setBodyViewState] = (0, import_react.useState)(() => {
143
+ const fallback = defaultBodyView === "floorMap" ? "floorMap" : "grid";
144
+ return readBodyViewFromStorage(bodyViewStorageKey, fallback);
145
+ });
146
+ const setBodyView = (0, import_react.useCallback)(
147
+ (v) => {
148
+ setBodyViewState(v);
149
+ if (typeof window !== "undefined" && bodyViewStorageKey) {
150
+ try {
151
+ localStorage.setItem(bodyViewStorageKey, v);
152
+ } catch {
153
+ }
154
+ }
155
+ },
156
+ [bodyViewStorageKey]
132
157
  );
133
158
  const [internalColumnVisibility, setInternalColumnVisibility] = (0, import_react.useState)(() => {
134
159
  var _a, _b;
@@ -259,7 +284,8 @@ var PisellRecordBoard = (props) => {
259
284
  setBodyView,
260
285
  floorMapProps: floorMapProp,
261
286
  floorMapLayoutContext: floorMapLayoutContextProp,
262
- defaultBodyView
287
+ defaultBodyView,
288
+ floorMapLayoutRef
263
289
  };
264
290
  }, [
265
291
  layoutType,
@@ -288,7 +314,9 @@ var PisellRecordBoard = (props) => {
288
314
  floorMapProp,
289
315
  floorMapLayoutContextProp,
290
316
  defaultBodyView,
291
- bodyView
317
+ floorMapLayoutRef,
318
+ bodyView,
319
+ setBodyView
292
320
  ]);
293
321
  const content = /* @__PURE__ */ import_react.default.createElement(import_RecordBoardProvider.RecordBoardProvider, { value: contextValue }, children);
294
322
  return /* @__PURE__ */ import_react.default.createElement(
@@ -163,6 +163,7 @@ var RecordBoardShellFrame = ({ children, className, style }) => {
163
163
  /* @__PURE__ */ import_react.default.createElement(import_pisellFloorMapLayout.FloorMapLayoutProvider, { value: floorMapLayoutProviderValue }, /* @__PURE__ */ import_react.default.createElement(
164
164
  import_pisellFloorMapLayout.PisellFloorMapLayout,
165
165
  {
166
+ ref: ctx.floorMapLayoutRef ?? void 0,
166
167
  ...effectiveFloorMapProps,
167
168
  ...mergedFloorMapDataSources !== void 0 ? { dataSources: mergedFloorMapDataSources } : {},
168
169
  items: effectiveFloorMapProps.items ?? [],
@@ -1,5 +1,5 @@
1
- import type { ReactNode, Key, ComponentType } from 'react';
2
- import type { PisellFloorMapLayoutProps, FloorMapDataSources, FloorMapLayoutContextValue } from '../pisellFloorMapLayout/types';
1
+ import type { ReactNode, Key, ComponentType, Ref } from 'react';
2
+ import type { PisellFloorMapLayoutProps, PisellFloorMapLayoutRef, FloorMapDataSources, FloorMapLayoutContextValue } from '../pisellFloorMapLayout/types';
3
3
  import type { TableProps } from 'antd';
4
4
  /**
5
5
  * 布局类型:一种 layout 对应一种「视图的集合」
@@ -202,6 +202,8 @@ export interface RecordBoardContextValue {
202
202
  floorMapLayoutContext?: FloorMapLayoutContextValue;
203
203
  /** 双视图并存时首屏偏好(根 props,与 PisellRecordBoard.defaultBodyView 一致) */
204
204
  defaultBodyView?: RecordBoardBodyView;
205
+ /** 挂到 ShellFrame 内 PisellFloorMapLayout,供外部 imperative 调用(如 saveLayout / fitBounds) */
206
+ floorMapLayoutRef?: Ref<PisellFloorMapLayoutRef | null>;
205
207
  }
206
208
  /**
207
209
  * 各 layout 下子组件的可选透传配置(类似 ChildComponentProps)
@@ -364,6 +366,10 @@ export interface RecordBoardProps {
364
366
  * 仅声明 FloorMap 而无 Grid/Card 内容时由 ShellFrame 固定为平面图,不出现切换器。
365
367
  */
366
368
  defaultBodyView?: RecordBoardBodyView;
369
+ /** 与 bodyView 同步持久化到 localStorage 的 key(双视图时生效) */
370
+ bodyViewStorageKey?: string;
371
+ /** 平面图 Layout 实例 ref,由 ShellFrame 注入 */
372
+ floorMapLayoutRef?: Ref<PisellFloorMapLayoutRef | null>;
367
373
  /** 其他 */
368
374
  [key: string]: any;
369
375
  }
@@ -17,7 +17,7 @@
17
17
  column-gap: 12px;
18
18
  box-sizing: border-box;
19
19
  padding: 10px 14px;
20
- border-radius: 10px;
20
+ border-radius: 0;
21
21
  background: #141824;
22
22
  border: 1px solid rgba(255, 255, 255, 0.06);
23
23
  }
@@ -45,6 +45,9 @@ var import_PisellTimeNavigator = require("./PisellTimeNavigator.less");
45
45
  var import_useEngineContext = __toESM(require("../../hooks/useEngineContext"));
46
46
  var CELL_SIZE_H = 82;
47
47
  var CELL_SIZE_V = 20;
48
+ var CURSOR_MOVES_NOW_RESERVE_PX = 56;
49
+ var CURSOR_MOVES_H_PADDING_PX = 8;
50
+ var CURSOR_MOVES_MIN_CELL_PX = 8;
48
51
  function PisellTimeNavigator(props) {
49
52
  var _a, _b;
50
53
  const {
@@ -73,7 +76,8 @@ function PisellTimeNavigator(props) {
73
76
  style,
74
77
  locale,
75
78
  timeFormat = "HH:mm",
76
- renderNowButton
79
+ renderNowButton,
80
+ onUserInteraction
77
81
  } = props;
78
82
  const context = (0, import_useEngineContext.default)();
79
83
  import_utils2.locales.init(
@@ -87,7 +91,45 @@ function PisellTimeNavigator(props) {
87
91
  }, [referenceDateProp]);
88
92
  const referenceDate = referenceDateFromProp ?? (0, import_dayjs.default)().startOf("day");
89
93
  const isHorizontal = orientation === "horizontal";
90
- const cellSizePx = isHorizontal ? CELL_SIZE_H : CELL_SIZE_V;
94
+ const isAxisMoves = cursorMode === "axis-moves";
95
+ const [viewportSize, setViewportSize] = (0, import_react.useState)({ w: 0, h: 0 });
96
+ const viewportWrapRef = (0, import_react.useRef)(null);
97
+ (0, import_react.useEffect)(() => {
98
+ const el = viewportWrapRef.current;
99
+ if (!el) return;
100
+ const setSize = () => {
101
+ const w = el.clientWidth;
102
+ const h = el.clientHeight;
103
+ setViewportSize(
104
+ (prev) => prev.w === w && prev.h === h ? prev : { w, h }
105
+ );
106
+ };
107
+ setSize();
108
+ const ro = new ResizeObserver(setSize);
109
+ ro.observe(el);
110
+ return () => ro.disconnect();
111
+ }, []);
112
+ const horizontalSlotCount = (0, import_react.useMemo)(
113
+ () => isHorizontal ? (0, import_utils.getHorizontalAxisSlotCount)(range, referenceDate, stepMinutes) : 0,
114
+ [isHorizontal, range == null ? void 0 : range.start, range == null ? void 0 : range.end, referenceDate, stepMinutes]
115
+ );
116
+ const cellSizePx = (0, import_react.useMemo)(() => {
117
+ if (!isHorizontal) return CELL_SIZE_V;
118
+ if (isAxisMoves) return CELL_SIZE_H;
119
+ const n = horizontalSlotCount;
120
+ if (n <= 0) return CELL_SIZE_H;
121
+ const vpW = Math.max(1, viewportSize.w || 400);
122
+ const reserve = showNowButton ? CURSOR_MOVES_NOW_RESERVE_PX : 0;
123
+ const usable = Math.max(40, vpW - reserve - CURSOR_MOVES_H_PADDING_PX * 2);
124
+ const raw = Math.floor(usable / n);
125
+ return Math.max(CURSOR_MOVES_MIN_CELL_PX, Math.min(CELL_SIZE_H, raw));
126
+ }, [
127
+ isHorizontal,
128
+ isAxisMoves,
129
+ horizontalSlotCount,
130
+ viewportSize.w,
131
+ showNowButton
132
+ ]);
91
133
  const axis = (0, import_hooks.useTimeAxis)({
92
134
  range,
93
135
  stepMinutes,
@@ -103,7 +145,6 @@ function PisellTimeNavigator(props) {
103
145
  const value = isControlled ? valueProp != null ? (0, import_utils.parseValue)(valueProp, referenceDate) : null : internalValue;
104
146
  const effectiveValue = value ?? axis.rangeStart;
105
147
  const scrollRef = (0, import_react.useRef)(null);
106
- const viewportWrapRef = (0, import_react.useRef)(null);
107
148
  const scrollToCenterInProgressRef = (0, import_react.useRef)(false);
108
149
  const valueUpdatedByScrollRef = (0, import_react.useRef)(false);
109
150
  const isPointerDownRef = (0, import_react.useRef)(false);
@@ -111,13 +152,13 @@ function PisellTimeNavigator(props) {
111
152
  scrollLeft: 0,
112
153
  scrollTop: 0
113
154
  });
114
- const [viewportSize, setViewportSize] = (0, import_react.useState)({ w: 0, h: 0 });
115
155
  const [, setNowTick] = (0, import_react.useState)(0);
116
156
  (0, import_react.useEffect)(() => {
117
157
  const id = setInterval(() => setNowTick((n) => n + 1), 60 * 1e3);
118
158
  return () => clearInterval(id);
119
159
  }, []);
120
- const isAxisMoves = cursorMode === "axis-moves";
160
+ const cursorDragActiveRef = (0, import_react.useRef)(false);
161
+ const cursorDragBaseRef = (0, import_react.useRef)({ startClient: 0, startOffset: 0 });
121
162
  const getScrollContainer = (0, import_react.useCallback)(
122
163
  () => {
123
164
  var _a2, _b2;
@@ -125,21 +166,6 @@ function PisellTimeNavigator(props) {
125
166
  },
126
167
  []
127
168
  );
128
- (0, import_react.useEffect)(() => {
129
- const el = viewportWrapRef.current;
130
- if (!el) return;
131
- const setSize = () => {
132
- const w = el.clientWidth;
133
- const h = el.clientHeight;
134
- setViewportSize(
135
- (prev) => prev.w === w && prev.h === h ? prev : { w, h }
136
- );
137
- };
138
- setSize();
139
- const ro = new ResizeObserver(setSize);
140
- ro.observe(el);
141
- return () => ro.disconnect();
142
- }, []);
143
169
  const inertiaHandlers = (0, import_hooks.useDragInertia)({
144
170
  enabled: inertia && !userInteractionLocked,
145
171
  orientation,
@@ -150,7 +176,7 @@ function PisellTimeNavigator(props) {
150
176
  const halfViewportW = effectiveViewportW / 2;
151
177
  const halfViewportH = effectiveViewportH / 2;
152
178
  const dotOffsetPx = isHorizontal ? cellSizePx / 2 : 0;
153
- const axisStartMarginW = Math.max(0, halfViewportW - dotOffsetPx);
179
+ const axisStartMarginW = isHorizontal && !isAxisMoves ? CURSOR_MOVES_H_PADDING_PX : Math.max(0, halfViewportW - dotOffsetPx);
154
180
  const axisStartMarginH = Math.max(0, halfViewportH - dotOffsetPx);
155
181
  const handleScroll = (0, import_react.useCallback)(
156
182
  (data) => {
@@ -307,12 +333,20 @@ function PisellTimeNavigator(props) {
307
333
  const verticalWrapHeight = axisStartMarginH + axis.totalLengthPx + verticalTailPx;
308
334
  const maxScrollPx = isHorizontal ? Math.max(0, axis.totalLengthPx - cellSizePx) : Math.max(0, verticalWrapHeight - effectiveViewportH);
309
335
  (0, import_react.useEffect)(() => {
310
- var _a2, _b2;
336
+ var _a2, _b2, _c, _d;
337
+ if (!isAxisMoves && isHorizontal) {
338
+ const container2 = (_b2 = (_a2 = scrollRef.current) == null ? void 0 : _a2.getScrollContainer) == null ? void 0 : _b2.call(_a2);
339
+ if (container2 && container2.scrollLeft !== 0) {
340
+ container2.scrollTo({ left: 0, behavior: "auto" });
341
+ }
342
+ return;
343
+ }
344
+ if (!isAxisMoves && cursorDragActiveRef.current) return;
311
345
  if (valueUpdatedByScrollRef.current) {
312
346
  valueUpdatedByScrollRef.current = false;
313
347
  return;
314
348
  }
315
- const container = (_b2 = (_a2 = scrollRef.current) == null ? void 0 : _a2.getScrollContainer) == null ? void 0 : _b2.call(_a2);
349
+ const container = (_d = (_c = scrollRef.current) == null ? void 0 : _c.getScrollContainer) == null ? void 0 : _d.call(_c);
316
350
  if (!container) return;
317
351
  const viewportPx = isHorizontal ? container.clientWidth : container.clientHeight;
318
352
  const targetScroll = hasScrollPadding ? Math.max(0, Math.min(cursorOffsetPx - dotOffsetPx, maxScrollPx)) : Math.max(
@@ -339,7 +373,8 @@ function PisellTimeNavigator(props) {
339
373
  hasScrollPadding,
340
374
  dotOffsetPx,
341
375
  cellSizePx,
342
- axis.totalLengthPx
376
+ axis.totalLengthPx,
377
+ isAxisMoves
343
378
  ]);
344
379
  const scrollToCenterTime = (0, import_react.useCallback)(
345
380
  (t) => {
@@ -380,6 +415,75 @@ function PisellTimeNavigator(props) {
380
415
  const clamped = wall.isBefore(rangeStart) ? rangeStart : wall.isAfter(rangeEnd) ? rangeEnd : wall;
381
416
  handleChange(clamped);
382
417
  }, [range, referenceDate, handleChange]);
418
+ const applyOffsetToNavigatorValue = (0, import_react.useCallback)(
419
+ (offsetPx, snap) => {
420
+ const maxO = Math.max(0, axis.totalLengthPx - 1e-3);
421
+ const nextOff = Math.max(0, Math.min(offsetPx, maxO));
422
+ const t = axis.offsetToTimeContinuous(nextOff);
423
+ if (!t) return;
424
+ const out = snap ? snapToStep ? (0, import_utils.snapToStepMinutes)(t, stepMinutes) : t.startOf("minute") : t.startOf("minute");
425
+ if ((0, import_utils.isInDisabledRanges)(out, disabledRanges)) return;
426
+ if (!isControlled) setInternalValue(out);
427
+ onChange == null ? void 0 : onChange(out);
428
+ },
429
+ [
430
+ axis.totalLengthPx,
431
+ axis.offsetToTimeContinuous,
432
+ snapToStep,
433
+ stepMinutes,
434
+ disabledRanges,
435
+ isControlled,
436
+ onChange
437
+ ]
438
+ );
439
+ const handleCursorDragPointerDown = (0, import_react.useCallback)(
440
+ (e) => {
441
+ if (userInteractionLocked || isAxisMoves) return;
442
+ e.preventDefault();
443
+ e.stopPropagation();
444
+ onUserInteraction == null ? void 0 : onUserInteraction();
445
+ cursorDragActiveRef.current = true;
446
+ cursorDragBaseRef.current = {
447
+ startClient: isHorizontal ? e.clientX : e.clientY,
448
+ startOffset: cursorOffsetPx
449
+ };
450
+ valueUpdatedByScrollRef.current = true;
451
+ e.currentTarget.setPointerCapture(e.pointerId);
452
+ },
453
+ [
454
+ userInteractionLocked,
455
+ isAxisMoves,
456
+ isHorizontal,
457
+ cursorOffsetPx,
458
+ onUserInteraction
459
+ ]
460
+ );
461
+ const handleCursorDragPointerMove = (0, import_react.useCallback)(
462
+ (e) => {
463
+ if (!cursorDragActiveRef.current) return;
464
+ const client = isHorizontal ? e.clientX : e.clientY;
465
+ const delta = client - cursorDragBaseRef.current.startClient;
466
+ const nextOff = cursorDragBaseRef.current.startOffset + delta;
467
+ applyOffsetToNavigatorValue(nextOff, false);
468
+ },
469
+ [isHorizontal, applyOffsetToNavigatorValue]
470
+ );
471
+ const handleCursorDragPointerUp = (0, import_react.useCallback)(
472
+ (e) => {
473
+ if (!cursorDragActiveRef.current) return;
474
+ cursorDragActiveRef.current = false;
475
+ try {
476
+ e.currentTarget.releasePointerCapture(e.pointerId);
477
+ } catch {
478
+ }
479
+ const client = isHorizontal ? e.clientX : e.clientY;
480
+ const delta = client - cursorDragBaseRef.current.startClient;
481
+ const nextOff = cursorDragBaseRef.current.startOffset + delta;
482
+ valueUpdatedByScrollRef.current = false;
483
+ applyOffsetToNavigatorValue(nextOff, true);
484
+ },
485
+ [isHorizontal, applyOffsetToNavigatorValue]
486
+ );
383
487
  const businessHoursPx = (0, import_react.useMemo)(() => {
384
488
  if (!businessHours) return void 0;
385
489
  const [s, e] = (0, import_utils.getRangeBounds)(businessHours, referenceDate);
@@ -460,6 +564,7 @@ function PisellTimeNavigator(props) {
460
564
  snapFn,
461
565
  scrollPosition,
462
566
  viewportSize,
567
+ onUserInteraction,
463
568
  onPointerDown: inertiaHandlers.onPointerDown,
464
569
  onPointerMove: inertiaHandlers.onPointerMove,
465
570
  onPointerUp: inertiaHandlers.onPointerUp,
@@ -469,9 +574,12 @@ function PisellTimeNavigator(props) {
469
574
  "div",
470
575
  {
471
576
  className: "pisell-time-navigator-axis-wrap",
472
- style: isHorizontal ? {
577
+ style: isHorizontal ? isAxisMoves ? {
473
578
  width: axis.totalLengthPx + (effectiveViewportW - cellSizePx),
474
579
  minHeight: 48
580
+ } : {
581
+ width: CURSOR_MOVES_H_PADDING_PX + axis.totalLengthPx + CURSOR_MOVES_H_PADDING_PX,
582
+ minHeight: 48
475
583
  } : {
476
584
  width: "100%"
477
585
  /* 不设 height,由轴(marginTop + 内容 + marginBottom)自然撑开,避免固定高度与 margin 的 CSS 盒模型歧义 */
@@ -482,9 +590,9 @@ function PisellTimeNavigator(props) {
482
590
  {
483
591
  className: "pisell-time-navigator-current-gradient pisell-time-navigator-current-gradient--horizontal",
484
592
  style: {
485
- left: axisStartMarginW + Math.max(0, currentTimeOffsetPx - CELL_SIZE_H),
593
+ left: axisStartMarginW + Math.max(0, currentTimeOffsetPx - cellSizePx),
486
594
  top: 0,
487
- width: Math.min(CELL_SIZE_H, currentTimeOffsetPx),
595
+ width: Math.min(cellSizePx, currentTimeOffsetPx),
488
596
  height: 48
489
597
  },
490
598
  "aria-hidden": true
@@ -526,7 +634,8 @@ function PisellTimeNavigator(props) {
526
634
  majorStepMinutes,
527
635
  timeFormat,
528
636
  locale,
529
- style: scaleStyle
637
+ style: scaleStyle,
638
+ horizontalCellSizePx: isHorizontal ? cellSizePx : void 0
530
639
  }
531
640
  ),
532
641
  !isHorizontal && currentTimeOffsetPx != null && currentTimeOffsetPx >= 0 && /* @__PURE__ */ import_react.default.createElement(
@@ -552,7 +661,12 @@ function PisellTimeNavigator(props) {
552
661
  locale,
553
662
  isRealtime: realtime,
554
663
  offsetPx: cursorOffsetPx,
555
- style: cursorStyle
664
+ style: cursorStyle,
665
+ dragInteractive: !userInteractionLocked,
666
+ onDragPointerDown: handleCursorDragPointerDown,
667
+ onDragPointerMove: handleCursorDragPointerMove,
668
+ onDragPointerUp: handleCursorDragPointerUp,
669
+ onDragPointerCancel: handleCursorDragPointerUp
556
670
  }
557
671
  )
558
672
  )
@@ -28,10 +28,6 @@
28
28
  -webkit-user-select: none;
29
29
  }
30
30
 
31
- &--vertical .pisell-time-navigator-viewport-wrap {
32
- min-height: 0;
33
- }
34
-
35
31
  .pisell-time-navigator-cursor-fixed {
36
32
  position: absolute;
37
33
  left: 0;
@@ -65,6 +61,26 @@
65
61
  }
66
62
 
67
63
  &--vertical {
64
+ /* flex 子项内竖向轴须 min-height:0 + overflow,否则轴随内容无限增高、无法内部滚动 */
65
+ display: flex;
66
+ flex-direction: column;
67
+ min-height: 0;
68
+ overflow: hidden;
69
+
70
+ .pisell-time-navigator-viewport-wrap {
71
+ flex: 1;
72
+ min-height: 0;
73
+ display: flex;
74
+ flex-direction: column;
75
+ overflow: hidden;
76
+ }
77
+
78
+ .pisell-time-navigator-scroll-host {
79
+ flex: 1;
80
+ min-height: 0;
81
+ overflow: hidden;
82
+ }
83
+
68
84
  .pisell-time-navigator-axis {
69
85
  flex-direction: column;
70
86
  }
@@ -274,6 +290,24 @@
274
290
  &--realtime .pisell-time-navigator-cursor-bubble {
275
291
  background: #ff692e;
276
292
  }
293
+
294
+ &--draggable {
295
+ pointer-events: auto;
296
+ touch-action: none;
297
+ cursor: grab;
298
+
299
+ &:active {
300
+ cursor: grabbing;
301
+ }
302
+ }
303
+
304
+ &--draggable.pisell-time-navigator-cursor--horizontal {
305
+ width: 36px;
306
+ }
307
+
308
+ &--draggable.pisell-time-navigator-cursor--vertical {
309
+ height: 40px;
310
+ }
277
311
  }
278
312
 
279
313
  .pisell-time-navigator-cursor-line {
@@ -142,4 +142,8 @@ export interface PisellTimeNavigatorProps {
142
142
  * 自定义 Now 按钮内容(覆盖默认文案与图标时使用)
143
143
  */
144
144
  renderNowButton?: () => ReactNode;
145
+ /**
146
+ * 用户开始操作时间轴(拖轴、点刻度、键盘、拖指针等)时回调;用于上层关闭「跟随当前」等。
147
+ */
148
+ onUserInteraction?: () => void;
145
149
  }
@@ -24,3 +24,7 @@ export declare function isInDisabledRanges(t: Dayjs, disabledRanges: TimeRangeOp
24
24
  * 在 [rangeStart, rangeEnd] 内按 stepMinutes 生成刻度时间列表(Dayjs)
25
25
  */
26
26
  export declare function getTicks(rangeStart: Dayjs, rangeEnd: Dayjs, stepMinutes: number): Dayjs[];
27
+ /**
28
+ * 横向时间轴「格」数量,与 useTimeAxis 中 totalLengthPx 算法一致(含尾格)
29
+ */
30
+ export declare function getHorizontalAxisSlotCount(range: TimeRangeOption | undefined, referenceDate: Dayjs, stepMinutes: number): number;
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/components/pisellTimeNavigator/utils/index.ts
30
30
  var utils_exports = {};
31
31
  __export(utils_exports, {
32
+ getHorizontalAxisSlotCount: () => getHorizontalAxisSlotCount,
32
33
  getRangeBounds: () => getRangeBounds,
33
34
  getTicks: () => getTicks,
34
35
  isInDisabledRanges: () => isInDisabledRanges,
@@ -88,8 +89,16 @@ function getTicks(rangeStart, rangeEnd, stepMinutes) {
88
89
  }
89
90
  return out;
90
91
  }
92
+ function getHorizontalAxisSlotCount(range, referenceDate, stepMinutes) {
93
+ const [rangeStart, rangeEnd] = getRangeBounds(range, referenceDate);
94
+ const ticks = getTicks(rangeStart, rangeEnd, stepMinutes);
95
+ const lastTick = ticks.length > 0 ? ticks[ticks.length - 1] : null;
96
+ const needTailCell = lastTick != null && rangeEnd.isAfter(lastTick);
97
+ return ticks.length + (needTailCell ? 1 : 0);
98
+ }
91
99
  // Annotate the CommonJS export names for ESM import in node:
92
100
  0 && (module.exports = {
101
+ getHorizontalAxisSlotCount,
93
102
  getRangeBounds,
94
103
  getTicks,
95
104
  isInDisabledRanges,
@@ -9,7 +9,7 @@ const FloorMapLayoutProviderMeta: ComponentMetadata = {
9
9
  "devMode": "proCode",
10
10
  "npm": {
11
11
  "package": "@pisell/materials",
12
- "version": "1.0.1059",
12
+ "version": "1.0.1060",
13
13
  "exportName": "FloorMapLayoutProvider",
14
14
  "main": "src\\index.tsx",
15
15
  "destructuring": true,
@@ -9,7 +9,7 @@ const PisellFloorMapLayoutMeta: ComponentMetadata = {
9
9
  "devMode": "proCode",
10
10
  "npm": {
11
11
  "package": "@pisell/materials",
12
- "version": "1.0.1059",
12
+ "version": "1.0.1060",
13
13
  "exportName": "PisellFloorMapLayout",
14
14
  "main": "src\\index.tsx",
15
15
  "destructuring": true,
@@ -1098,6 +1098,42 @@ const PisellFloorMapLayoutMeta: ComponentMetadata = {
1098
1098
  ]
1099
1099
  }
1100
1100
  }
1101
+ },
1102
+ {
1103
+ "title": {
1104
+ "label": {
1105
+ "type": "i18n",
1106
+ "en-US": "autoFitOnMount",
1107
+ "zh-CN": "autoFitOnMount"
1108
+ }
1109
+ },
1110
+ "name": "autoFitOnMount",
1111
+ "setter": {
1112
+ "componentName": "MixedSetter",
1113
+ "props": {
1114
+ "setters": [
1115
+ {
1116
+ "componentName": "ObjectSetter",
1117
+ "props": {
1118
+ "config": {
1119
+ "extraSetter": {
1120
+ "componentName": "MixedSetter",
1121
+ "isRequired": false,
1122
+ "props": {}
1123
+ }
1124
+ }
1125
+ },
1126
+ "isRequired": false,
1127
+ "initialValue": {}
1128
+ },
1129
+ {
1130
+ "componentName": "BoolSetter",
1131
+ "isRequired": false,
1132
+ "initialValue": false
1133
+ }
1134
+ ]
1135
+ }
1136
+ }
1101
1137
  }
1102
1138
  ],
1103
1139
  "extraSetter": {
@@ -5301,6 +5337,44 @@ const PisellFloorMapLayoutMeta: ComponentMetadata = {
5301
5337
  }
5302
5338
  }
5303
5339
  },
5340
+ {
5341
+ "title": {
5342
+ "label": {
5343
+ "type": "i18n",
5344
+ "en-US": "showDataSourceRecordFormBlock",
5345
+ "zh-CN": "为 false 时隐"
5346
+ },
5347
+ "tip": "showDataSourceRecordFormBlock | 为 false 时隐藏侧栏「数据源记录」表单区(保留数据绑定选源/选行)。"
5348
+ },
5349
+ "name": "showDataSourceRecordFormBlock",
5350
+ "description": "为 false 时隐藏侧栏「数据源记录」表单区(保留数据绑定选源/选行)。",
5351
+ "setter": {
5352
+ "componentName": "MixedSetter",
5353
+ "props": {
5354
+ "setters": [
5355
+ {
5356
+ "componentName": "ObjectSetter",
5357
+ "props": {
5358
+ "config": {
5359
+ "extraSetter": {
5360
+ "componentName": "MixedSetter",
5361
+ "isRequired": false,
5362
+ "props": {}
5363
+ }
5364
+ }
5365
+ },
5366
+ "isRequired": false,
5367
+ "initialValue": {}
5368
+ },
5369
+ {
5370
+ "componentName": "BoolSetter",
5371
+ "isRequired": false,
5372
+ "initialValue": false
5373
+ }
5374
+ ]
5375
+ }
5376
+ }
5377
+ },
5304
5378
  {
5305
5379
  "title": {
5306
5380
  "label": {