react-big-schedule 8.0.0 → 9.0.0

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 (109) hide show
  1. package/README.md +20 -1129
  2. package/dist/calendar/Calendar.d.ts +28 -0
  3. package/dist/calendar/Calendar.js +95 -0
  4. package/dist/calendar/Toolbar.d.ts +18 -0
  5. package/dist/calendar/Toolbar.js +6 -0
  6. package/dist/calendar/recurrence.check.d.ts +1 -0
  7. package/dist/calendar/recurrence.check.js +49 -0
  8. package/dist/calendar/recurrence.d.ts +8 -0
  9. package/dist/calendar/recurrence.js +90 -0
  10. package/dist/calendar/timezone.d.ts +6 -0
  11. package/dist/calendar/timezone.js +14 -0
  12. package/dist/calendar/types.d.ts +187 -0
  13. package/dist/calendar/types.js +1 -0
  14. package/dist/calendar/useEventSources.d.ts +11 -0
  15. package/dist/calendar/useEventSources.js +89 -0
  16. package/dist/calendar/useRecurrence.d.ts +4 -0
  17. package/dist/calendar/useRecurrence.js +15 -0
  18. package/dist/calendar/utils.d.ts +38 -0
  19. package/dist/calendar/utils.js +143 -0
  20. package/dist/calendar/views/DayGridView.d.ts +3 -0
  21. package/dist/calendar/views/DayGridView.js +375 -0
  22. package/dist/calendar/views/ListView.d.ts +4 -0
  23. package/dist/calendar/views/ListView.js +56 -0
  24. package/dist/calendar/views/MultiMonthView.d.ts +4 -0
  25. package/dist/calendar/views/MultiMonthView.js +36 -0
  26. package/dist/calendar/views/TimeGridView.d.ts +3 -0
  27. package/dist/calendar/views/TimeGridView.js +334 -0
  28. package/dist/calendar/views/daygrid/DayCell.d.ts +19 -0
  29. package/dist/calendar/views/daygrid/DayCell.js +17 -0
  30. package/dist/calendar/views/daygrid/EventChip.d.ts +19 -0
  31. package/dist/calendar/views/daygrid/EventChip.js +40 -0
  32. package/dist/calendar/views/daygrid/layout.d.ts +30 -0
  33. package/dist/calendar/views/daygrid/layout.js +63 -0
  34. package/dist/calendar/views/list/bucket.d.ts +8 -0
  35. package/dist/calendar/views/list/bucket.js +37 -0
  36. package/dist/calendar/views/multimonth/Cell.d.ts +12 -0
  37. package/dist/calendar/views/multimonth/Cell.js +44 -0
  38. package/dist/calendar/views/multimonth/Continuous.d.ts +8 -0
  39. package/dist/calendar/views/multimonth/Continuous.js +28 -0
  40. package/dist/calendar/views/multimonth/MonthGrid.d.ts +10 -0
  41. package/dist/calendar/views/multimonth/MonthGrid.js +36 -0
  42. package/dist/calendar/views/multimonth/shared.d.ts +27 -0
  43. package/dist/calendar/views/multimonth/shared.js +40 -0
  44. package/dist/calendar/views/timegrid/DayColumn.d.ts +49 -0
  45. package/dist/calendar/views/timegrid/DayColumn.js +86 -0
  46. package/dist/calendar/views/timegrid/model.d.ts +55 -0
  47. package/dist/calendar/views/timegrid/model.js +102 -0
  48. package/dist/components/AddMore.d.ts +13 -0
  49. package/dist/components/AddMore.js +9 -42
  50. package/dist/components/AddMorePopover.d.ts +13 -0
  51. package/dist/components/AddMorePopover.js +24 -95
  52. package/dist/components/AgendaEventItem.d.ts +10 -0
  53. package/dist/components/AgendaEventItem.js +33 -81
  54. package/dist/components/AgendaResourceEvents.d.ts +10 -0
  55. package/dist/components/AgendaResourceEvents.js +38 -90
  56. package/dist/components/AgendaView.d.ts +9 -0
  57. package/dist/components/AgendaView.js +10 -69
  58. package/dist/components/BodyView.d.ts +11 -0
  59. package/dist/components/BodyView.js +36 -67
  60. package/dist/components/DnDContext.d.ts +48 -0
  61. package/dist/components/DnDContext.js +200 -221
  62. package/dist/components/DnDSource.d.ts +49 -0
  63. package/dist/components/DnDSource.js +125 -120
  64. package/dist/components/EventItem.d.ts +41 -0
  65. package/dist/components/EventItem.js +585 -787
  66. package/dist/components/EventItemPopover.d.ts +12 -0
  67. package/dist/components/EventItemPopover.js +19 -143
  68. package/dist/components/HeaderView.d.ts +14 -0
  69. package/dist/components/HeaderView.js +123 -186
  70. package/dist/components/ResourceEvents.d.ts +54 -0
  71. package/dist/components/ResourceEvents.js +534 -703
  72. package/dist/components/ResourceView.d.ts +26 -0
  73. package/dist/components/ResourceView.js +64 -184
  74. package/dist/components/SchedulerData.d.ts +136 -0
  75. package/dist/components/SchedulerData.js +1296 -1226
  76. package/dist/components/SchedulerHeader.d.ts +25 -0
  77. package/dist/components/SchedulerHeader.js +50 -150
  78. package/dist/components/SelectedArea.d.ts +8 -0
  79. package/dist/components/SelectedArea.js +11 -26
  80. package/dist/components/Summary.d.ts +11 -0
  81. package/dist/components/Summary.js +21 -50
  82. package/dist/components/WrapperFun.d.ts +2 -0
  83. package/dist/components/WrapperFun.js +5 -10
  84. package/dist/components/index.d.ts +44 -0
  85. package/dist/components/index.js +428 -654
  86. package/dist/components/ui/Icons.d.ts +8 -0
  87. package/dist/components/ui/Icons.js +7 -0
  88. package/dist/components/ui/MiniCalendar.d.ts +9 -0
  89. package/dist/components/ui/MiniCalendar.js +17 -0
  90. package/dist/components/ui/Popover.d.ts +13 -0
  91. package/dist/components/ui/Popover.js +80 -0
  92. package/dist/config/default.d.ts +36 -0
  93. package/dist/config/default.js +17 -26
  94. package/dist/config/i18n.d.ts +27 -0
  95. package/dist/config/i18n.js +21 -37
  96. package/dist/config/scheduler.d.ts +12 -0
  97. package/dist/config/scheduler.js +126 -119
  98. package/dist/css/style.css +2 -316
  99. package/dist/helper/behaviors.d.ts +14 -0
  100. package/dist/helper/behaviors.js +75 -75
  101. package/dist/helper/utility.d.ts +26 -0
  102. package/dist/helper/utility.js +23 -30
  103. package/dist/index.d.ts +6 -477
  104. package/dist/index.js +4 -2
  105. package/dist/sample-data/sample1.d.ts +11 -0
  106. package/dist/sample-data/sample1.js +427 -372
  107. package/dist/types.d.ts +267 -0
  108. package/dist/types.js +1 -0
  109. package/package.json +35 -26
@@ -1,730 +1,561 @@
1
- import { PropTypes } from 'prop-types';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { PureComponent } from 'react';
3
3
  import { useDrop } from 'react-dnd';
4
4
  import { CellUnit, DATETIME_FORMAT, DnDTypes, SummaryPos } from '../config/default';
5
+ import { toDate } from '../helper/behaviors';
5
6
  import { getPos, normalizeEventEnd, normalizeEventStart } from '../helper/utility';
6
7
  import AddMore from './AddMore';
7
8
  import EventItem from './EventItem';
8
9
  import SelectedArea from './SelectedArea';
9
10
  import Summary from './Summary';
10
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  const DEFAULT_ROW_HEIGHT = 50;
12
+ // Drag handlers take the mouse/touch union; DOM listeners are typed for plain Event.
13
+ const asListener = (fn) => fn;
12
14
  class ResourceEvents extends PureComponent {
13
- static propTypes = {
14
- resourceEvents: PropTypes.object.isRequired,
15
- schedulerData: PropTypes.object.isRequired,
16
- schedulerDataVersion: PropTypes.number,
17
- dndSource: PropTypes.object.isRequired,
18
- onSetAddMoreState: PropTypes.func,
19
- updateEventStart: PropTypes.func,
20
- updateEventEnd: PropTypes.func,
21
- moveEvent: PropTypes.func,
22
- movingEvent: PropTypes.func,
23
- conflictOccurred: PropTypes.func,
24
- subtitleGetter: PropTypes.func,
25
- eventItemClick: PropTypes.func,
26
- viewEventClick: PropTypes.func,
27
- viewEventText: PropTypes.string,
28
- viewEvent2Click: PropTypes.func,
29
- viewEvent2Text: PropTypes.string,
30
- newEvent: PropTypes.func,
31
- eventItemTemplateResolver: PropTypes.func,
32
- eventItemPopoverTemplateResolver: PropTypes.func,
33
- onSelectionChange: PropTypes.func,
34
- isRowSelected: PropTypes.bool,
35
- selectionPreview: PropTypes.shape({
36
- isSelecting: PropTypes.bool,
37
- left: PropTypes.number,
38
- width: PropTypes.number
39
- })
40
- };
41
- constructor(props) {
42
- super(props);
43
- this.state = {
44
- isSelecting: false,
45
- left: 0,
46
- width: 0,
47
- // Add vertical selection tracking
48
- originalStartRowIndex: -1,
49
- startRowIndex: -1,
50
- endRowIndex: -1,
51
- // where the dragged item would land (set from the drop target's hover)
52
- dropPreview: null
53
- };
54
- this.supportTouch = false; // 'ontouchstart' in window;
55
- }
56
- componentDidMount() {
57
- const {
58
- schedulerData
59
- } = this.props;
60
- const {
61
- config
62
- } = schedulerData;
63
- this.supportTouch = 'ontouchstart' in window;
64
- if (config.creatable === true) {
65
- this.supportTouchHelper();
66
- }
67
- }
68
- componentDidUpdate(prevProps) {
69
- const prevCreatable = prevProps.schedulerData?.config?.creatable;
70
- const currentCreatable = this.props.schedulerData?.config?.creatable;
71
- if (prevCreatable !== currentCreatable) {
72
- this.supportTouchHelper('remove');
73
- if (currentCreatable) {
74
- this.supportTouchHelper();
75
- }
76
- }
77
- }
78
- componentWillUnmount() {
79
- this.cleanupDragInteraction();
80
- this.supportTouchHelper('remove');
81
- this.emitSelectionChange(false, [], {
82
- left: 0,
83
- width: 0
84
- });
85
- }
86
- setDropPreview = dropPreview => {
87
- if (!Number.isFinite(dropPreview.left) || !Number.isFinite(dropPreview.width)) return;
88
- const current = this.state.dropPreview;
89
- if (current?.left === dropPreview.left && current?.width === dropPreview.width) return;
90
- this.setState({
91
- dropPreview
92
- });
93
- };
94
- cleanupDragInteraction = () => {
95
- document.documentElement.removeEventListener('touchmove', this.doDrag, false);
96
- document.documentElement.removeEventListener('touchend', this.stopDrag, false);
97
- document.documentElement.removeEventListener('touchcancel', this.cancelDrag, false);
98
- document.documentElement.removeEventListener('mousemove', this.doDrag, false);
99
- document.documentElement.removeEventListener('mouseup', this.stopDrag, false);
100
- document.onselectstart = null;
101
- document.ondragstart = null;
102
- };
103
- supportTouchHelper = (evType = 'add') => {
104
- const ev = evType === 'add' ? this.eventContainer.addEventListener : this.eventContainer.removeEventListener;
105
- if (this.supportTouch) {
106
- // ev('touchstart', this.initDrag, false);
107
- } else {
108
- ev('mousedown', this.initDrag, false);
109
- }
110
- };
111
- initDrag = ev => {
112
- const {
113
- isSelecting
114
- } = this.state;
115
- if (isSelecting) return;
116
- if ((ev.srcElement || ev.target) !== this.eventContainer) return;
117
- ev.stopPropagation();
118
- const {
119
- resourceEvents
120
- } = this.props;
121
- if (resourceEvents.groupOnly) return;
122
- const [clientX, toReturn] = this.dragHelper(ev, 'init');
123
- if (toReturn) {
124
- return;
125
- }
126
- const {
127
- schedulerData
128
- } = this.props;
129
- const cellWidth = schedulerData.getContentCellWidth();
130
- const pos = getPos(this.eventContainer);
131
- const startX = clientX - pos.x;
132
- const leftIndex = Math.floor(startX / cellWidth);
133
- const left = leftIndex * cellWidth;
134
- const rightIndex = Math.ceil(startX / cellWidth);
135
- const width = (rightIndex - leftIndex) * cellWidth;
136
-
137
- // Get the row index of this resource
138
- const startRowIndex = this.getResourceRowIndex(resourceEvents.slotId);
139
- this.setState({
140
- startX,
141
- left,
142
- leftIndex,
143
- width,
144
- rightIndex,
145
- isSelecting: true,
146
- originalStartRowIndex: startRowIndex,
147
- startRowIndex,
148
- endRowIndex: startRowIndex // Initially same as start
149
- });
150
- this.emitSelectionChange(true, this.getSelectedResourceIds(startRowIndex, startRowIndex), {
151
- left,
152
- width
153
- });
154
- if (this.supportTouch) {
155
- document.documentElement.addEventListener('touchmove', this.doDrag, false);
156
- document.documentElement.addEventListener('touchend', this.stopDrag, false);
157
- document.documentElement.addEventListener('touchcancel', this.cancelDrag, false);
158
- } else {
159
- document.documentElement.addEventListener('mousemove', this.doDrag, false);
160
- document.documentElement.addEventListener('mouseup', this.stopDrag, false);
161
- }
162
- document.onselectstart = () => false;
163
- document.ondragstart = () => false;
164
- };
165
- doDrag = ev => {
166
- ev.stopPropagation();
167
- const [clientX, toReturn] = this.dragHelper(ev, 'do');
168
- if (toReturn) {
169
- return;
170
- }
171
- const {
172
- startX,
173
- originalStartRowIndex
174
- } = this.state;
175
- const {
176
- schedulerData
177
- } = this.props;
178
- const {
179
- headers
180
- } = schedulerData;
181
- const cellWidth = schedulerData.getContentCellWidth();
182
- const pos = getPos(this.eventContainer);
183
- const currentX = clientX - pos.x;
184
- let leftIndex = Math.floor(Math.min(startX, currentX) / cellWidth);
185
- leftIndex = leftIndex < 0 ? 0 : leftIndex;
186
- const left = leftIndex * cellWidth;
187
- let rightIndex = Math.ceil(Math.max(startX, currentX) / cellWidth);
188
- rightIndex = rightIndex > headers.length ? headers.length : rightIndex;
189
- const width = (rightIndex - leftIndex) * cellWidth;
190
-
191
- // Calculate current row based on per-row heights (not a uniform row height assumption).
192
- let clientY = ev.clientY;
193
- if (this.supportTouch && ev.changedTouches && ev.changedTouches.length > 0) {
194
- clientY = ev.changedTouches[0].clientY;
195
- }
196
- const currentY = clientY - pos.y;
197
- const displayRenderData = this.getDisplayRenderData();
198
- if (displayRenderData.length === 0) {
199
- this.setState({
200
- leftIndex,
201
- left,
202
- rightIndex,
203
- width,
204
- isSelecting: true,
205
- startRowIndex: -1,
206
- endRowIndex: -1
207
- });
208
- this.emitSelectionChange(true, [], {
209
- left,
210
- width
211
- });
212
- return;
15
+ eventContainer = null;
16
+ supportTouch;
17
+ constructor(props) {
18
+ super(props);
19
+ this.state = {
20
+ isSelecting: false,
21
+ left: 0,
22
+ width: 0,
23
+ // Add vertical selection tracking
24
+ originalStartRowIndex: -1,
25
+ startRowIndex: -1,
26
+ endRowIndex: -1,
27
+ // where the dragged item would land (set from the drop target's hover)
28
+ dropPreview: null,
29
+ };
30
+ this.supportTouch = false; // 'ontouchstart' in window;
213
31
  }
214
- const rowHeights = displayRenderData.map(row => row?.rowHeight || DEFAULT_ROW_HEIGHT);
215
- const startRowTop = rowHeights.slice(0, Math.max(0, originalStartRowIndex)).reduce((sum, h) => sum + h, 0);
216
- const absoluteY = startRowTop + currentY;
217
- let cumulativeHeight = 0;
218
- let currentRowIndex = rowHeights.length - 1;
219
- for (let i = 0; i < rowHeights.length; i += 1) {
220
- cumulativeHeight += rowHeights[i];
221
- if (absoluteY < cumulativeHeight) {
222
- currentRowIndex = i;
223
- break;
224
- }
32
+ componentDidMount() {
33
+ const { schedulerData } = this.props;
34
+ const { config } = schedulerData;
35
+ this.supportTouch = 'ontouchstart' in window;
36
+ if (config.creatable === true) {
37
+ this.supportTouchHelper();
38
+ }
225
39
  }
226
- if (absoluteY < 0) {
227
- currentRowIndex = 0;
40
+ componentDidUpdate(prevProps) {
41
+ const prevCreatable = prevProps.schedulerData?.config?.creatable;
42
+ const currentCreatable = this.props.schedulerData?.config?.creatable;
43
+ if (prevCreatable !== currentCreatable) {
44
+ this.supportTouchHelper('remove');
45
+ if (currentCreatable) {
46
+ this.supportTouchHelper();
47
+ }
48
+ }
228
49
  }
229
- const minRowIndex = Math.min(originalStartRowIndex, currentRowIndex);
230
- const maxRowIndex = Math.max(originalStartRowIndex, currentRowIndex);
231
-
232
- // Clamp to valid row indices
233
- const clampedMinRow = Math.max(0, minRowIndex);
234
- const clampedMaxRow = Math.min(displayRenderData.length - 1, maxRowIndex);
235
- this.setState({
236
- leftIndex,
237
- left,
238
- rightIndex,
239
- width,
240
- isSelecting: true,
241
- startRowIndex: clampedMinRow,
242
- endRowIndex: clampedMaxRow
243
- });
244
- this.emitSelectionChange(true, this.getSelectedResourceIds(clampedMinRow, clampedMaxRow), {
245
- left,
246
- width
247
- });
248
- };
249
- dragHelper = (ev, dragType) => {
250
- let clientX = 0;
251
- if (this.supportTouch) {
252
- if (ev.changedTouches.length === 0) return [clientX, true];
253
- const touch = ev.changedTouches[0];
254
- clientX = touch.pageX;
255
- } else if (dragType === 'init') {
256
- if (ev.buttons !== undefined && ev.buttons !== 1) return [clientX, true];
257
- clientX = ev.clientX;
258
- } else {
259
- clientX = ev.clientX;
50
+ componentWillUnmount() {
51
+ this.cleanupDragInteraction();
52
+ this.supportTouchHelper('remove');
53
+ this.emitSelectionChange(false, [], { left: 0, width: 0 });
260
54
  }
261
- return [clientX, false];
262
- };
263
- stopDrag = ev => {
264
- if (ev?.stopPropagation) ev.stopPropagation();
265
- const {
266
- schedulerData,
267
- newEvent,
268
- resourceEvents
269
- } = this.props;
270
- const {
271
- headers,
272
- events,
273
- config,
274
- cellUnit,
275
- localeDayjs
276
- } = schedulerData;
277
- const {
278
- leftIndex,
279
- rightIndex
280
- } = this.state;
281
- this.cleanupDragInteraction();
282
- const resetSelection = () => {
283
- this.setState({
284
- startX: 0,
285
- leftIndex: 0,
286
- left: 0,
287
- rightIndex: 0,
288
- width: 0,
289
- isSelecting: false,
290
- originalStartRowIndex: -1,
291
- startRowIndex: -1,
292
- endRowIndex: -1
293
- });
294
- this.emitSelectionChange(false, [], {
295
- left: 0,
296
- width: 0
297
- });
55
+ setDropPreview = (dropPreview) => {
56
+ if (!Number.isFinite(dropPreview.left) || !Number.isFinite(dropPreview.width))
57
+ return;
58
+ const current = this.state.dropPreview;
59
+ if (current?.left === dropPreview.left && current?.width === dropPreview.width)
60
+ return;
61
+ this.setState({ dropPreview });
298
62
  };
299
- if (headers.length === 0 || !resourceEvents.headerItems || resourceEvents.headerItems.length === 0) {
300
- resetSelection();
301
- return;
302
- }
303
-
304
- // a cell width of 0 (container not measured yet) makes the drag indexes NaN or Infinity
305
- const toIndex = (value, fallback) => Number.isFinite(value) ? value : fallback;
306
- const maxLeftIndex = headers.length - 1;
307
- const safeLeftIndex = Math.max(0, Math.min(toIndex(leftIndex, 0), maxLeftIndex));
308
- const maxRightIndex = Math.min(headers.length, resourceEvents.headerItems.length);
309
- let safeRightIndex = Math.max(1, toIndex(rightIndex, 1), safeLeftIndex + 1);
310
- safeRightIndex = Math.min(safeRightIndex, maxRightIndex);
311
-
312
- // headers and headerItems can be out of step while the view rebuilds; drop the selection instead of throwing
313
- if (!headers[safeLeftIndex] || !resourceEvents.headerItems[safeRightIndex - 1]) {
314
- resetSelection();
315
- return;
316
- }
317
- const isVertical = schedulerData.isVerticalResourceView();
318
- // vertical view: rows are time slots and columns are resources, so the roles swap
319
- const selectedRowIds = this.getSelectedResourceIds();
320
- let startTime, endTime;
321
- if (isVertical) {
322
- startTime = selectedRowIds[0] ?? resourceEvents.slotId;
323
- const lastRowId = selectedRowIds[selectedRowIds.length - 1] ?? startTime;
324
- endTime = localeDayjs(new Date(lastRowId)).add(config.minuteStep, 'minutes').format(DATETIME_FORMAT);
325
- } else {
326
- startTime = headers[safeLeftIndex].time;
327
- endTime = resourceEvents.headerItems[safeRightIndex - 1].end;
328
- if (cellUnit !== CellUnit.Hour) {
329
- endTime = localeDayjs(new Date(resourceEvents.headerItems[safeRightIndex - 1].start)).hour(23).minute(59).second(59).format(DATETIME_FORMAT);
330
- }
331
- }
332
-
333
- // Get selected resource IDs
334
- const selectedResourceIds = isVertical ? headers.slice(safeLeftIndex, safeRightIndex).map(header => header.id) : selectedRowIds;
335
- const slotId = selectedResourceIds.length > 0 ? selectedResourceIds[0] : resourceEvents.slotId;
336
- const slotName = selectedResourceIds.length > 0 ? schedulerData.getResourceById(slotId)?.name || slotId : resourceEvents.slotName;
337
- this.setState({
338
- startX: 0,
339
- leftIndex: 0,
340
- left: 0,
341
- rightIndex: 0,
342
- width: 0,
343
- isSelecting: false,
344
- originalStartRowIndex: -1,
345
- startRowIndex: -1,
346
- endRowIndex: -1
347
- });
348
- this.emitSelectionChange(false, [], {
349
- left: 0,
350
- width: 0
351
- });
352
- let hasConflict = false;
353
- if (config.checkConflict) {
354
- const start = localeDayjs(new Date(startTime));
355
- const end = localeDayjs(endTime);
356
- events.forEach(e => {
357
- const eventResourceIds = e.resourceIds || [e.resourceId];
358
- const hasOverlap = selectedResourceIds.some(selectedId => eventResourceIds.includes(selectedId));
359
- if (hasOverlap) {
360
- const eStart = localeDayjs(e.start);
361
- const eEnd = localeDayjs(e.end);
362
- if (start >= eStart && start < eEnd || end > eStart && end <= eEnd || eStart >= start && eStart < end || eEnd > start && eEnd <= end) hasConflict = true;
63
+ cleanupDragInteraction = () => {
64
+ document.documentElement.removeEventListener('touchmove', asListener(this.doDrag), false);
65
+ document.documentElement.removeEventListener('touchend', asListener(this.stopDrag), false);
66
+ document.documentElement.removeEventListener('touchcancel', asListener(this.cancelDrag), false);
67
+ document.documentElement.removeEventListener('mousemove', asListener(this.doDrag), false);
68
+ document.documentElement.removeEventListener('mouseup', asListener(this.stopDrag), false);
69
+ document.onselectstart = null;
70
+ document.ondragstart = null;
71
+ };
72
+ supportTouchHelper = (evType = 'add') => {
73
+ const container = this.eventContainer;
74
+ if (!container)
75
+ return;
76
+ const ev = evType === 'add' ? container.addEventListener : container.removeEventListener;
77
+ if (this.supportTouch) {
78
+ // ev('touchstart', this.initDrag, false);
363
79
  }
364
- });
365
- }
366
- if (hasConflict) {
367
- const {
368
- conflictOccurred
369
- } = this.props;
370
- if (conflictOccurred !== undefined) {
371
- conflictOccurred(schedulerData, 'New', {
372
- id: undefined,
373
- start: startTime,
374
- end: endTime,
375
- resourceId: slotId,
376
- // Keep for backward compatibility
377
- resourceIds: selectedResourceIds,
378
- // Add multi-resource support
379
- slotId,
380
- slotName,
381
- title: undefined
382
- }, DnDTypes.EVENT, slotId, slotName, startTime, endTime);
383
- } else {
384
- console.log('Conflict occurred, set conflictOccurred func in Scheduler to handle it');
385
- }
386
- } else if (newEvent !== undefined) {
387
- // Pass resourceIds for multi-resource events
388
- newEvent(schedulerData, slotId, slotName, startTime, endTime, undefined, {
389
- resourceIds: selectedResourceIds.length > 1 ? selectedResourceIds : undefined
390
- });
391
- }
392
- };
393
- cancelDrag = ev => {
394
- if (ev?.stopPropagation) ev.stopPropagation();
395
- const {
396
- isSelecting
397
- } = this.state;
398
- if (isSelecting) {
399
- this.cleanupDragInteraction();
400
- this.setState({
401
- startX: 0,
402
- leftIndex: 0,
403
- left: 0,
404
- rightIndex: 0,
405
- width: 0,
406
- isSelecting: false,
407
- originalStartRowIndex: -1,
408
- startRowIndex: -1,
409
- endRowIndex: -1
410
- });
411
- this.emitSelectionChange(false, [], {
412
- left: 0,
413
- width: 0
414
- });
415
- }
416
- };
417
- emitSelectionChange = (isSelecting, selectedResourceIds, preview = {}) => {
418
- const {
419
- onSelectionChange
420
- } = this.props;
421
- if (onSelectionChange) {
422
- onSelectionChange(isSelecting, selectedResourceIds, preview);
423
- }
424
- };
425
- onAddMoreClick = headerItem => {
426
- const {
427
- onSetAddMoreState,
428
- resourceEvents,
429
- schedulerData
430
- } = this.props;
431
- if (onSetAddMoreState) {
432
- const {
433
- config
434
- } = schedulerData;
435
- const cellWidth = schedulerData.getContentCellWidth();
436
- const index = resourceEvents.headerItems.indexOf(headerItem);
437
- if (index !== -1) {
438
- let left = index * (cellWidth - 1);
80
+ else {
81
+ // keeps the original unbound call, as ev is not invoked on the container
82
+ ev.call(container, 'mousedown', asListener(this.initDrag), false);
83
+ }
84
+ };
85
+ initDrag = (ev) => {
86
+ const { isSelecting } = this.state;
87
+ if (isSelecting)
88
+ return;
89
+ if (ev.target !== this.eventContainer)
90
+ return;
91
+ ev.stopPropagation();
92
+ const { resourceEvents } = this.props;
93
+ if (resourceEvents.groupOnly)
94
+ return;
95
+ const [clientX, toReturn] = this.dragHelper(ev, 'init');
96
+ if (toReturn) {
97
+ return;
98
+ }
99
+ const { schedulerData } = this.props;
100
+ const cellWidth = schedulerData.getContentCellWidth();
439
101
  const pos = getPos(this.eventContainer);
440
- left += pos.x;
441
- const top = pos.y;
442
- const height = (headerItem.count + 1) * config.eventItemLineHeight + 20;
443
- onSetAddMoreState({
444
- headerItem,
445
- left,
446
- top,
447
- height
102
+ const startX = clientX - pos.x;
103
+ const leftIndex = Math.floor(startX / cellWidth);
104
+ const left = leftIndex * cellWidth;
105
+ const rightIndex = Math.ceil(startX / cellWidth);
106
+ const width = (rightIndex - leftIndex) * cellWidth;
107
+ // Get the row index of this resource
108
+ const startRowIndex = this.getResourceRowIndex(resourceEvents.slotId);
109
+ this.setState({
110
+ startX,
111
+ left,
112
+ leftIndex,
113
+ width,
114
+ rightIndex,
115
+ isSelecting: true,
116
+ originalStartRowIndex: startRowIndex,
117
+ startRowIndex,
118
+ endRowIndex: startRowIndex, // Initially same as start
448
119
  });
449
- }
450
- }
451
- };
452
- eventContainerRef = element => {
453
- this.eventContainer = element;
454
- // Also set the drop ref if it exists
455
- const {
456
- dropRef
457
- } = this.props;
458
- if (dropRef) {
459
- dropRef(element);
460
- }
461
- };
462
- getResourceRowIndex = slotId => {
463
- return this.getDisplayRenderData().findIndex(row => row.slotId === slotId);
464
- };
465
- getDisplayRenderData = () => {
466
- const {
467
- schedulerData
468
- } = this.props;
469
- return schedulerData.renderData.filter(row => row.render);
470
- };
471
- getSelectedResourceIds = (startOverride, endOverride) => {
472
- const {
473
- startRowIndex,
474
- endRowIndex
475
- } = this.state;
476
- const displayRenderData = this.getDisplayRenderData();
477
- const from = startOverride !== undefined ? startOverride : startRowIndex;
478
- const to = endOverride !== undefined ? endOverride : endRowIndex;
479
- if (from === -1 || to === -1) {
480
- return [];
481
- }
482
- const selectedResourceIds = [];
483
- for (let i = from; i <= to; i++) {
484
- const row = displayRenderData[i];
485
- if (row && !row.groupOnly) {
486
- selectedResourceIds.push(row.slotId);
487
- }
488
- }
489
- return selectedResourceIds;
490
- };
491
- render() {
492
- const {
493
- resourceEvents,
494
- schedulerData,
495
- dndSource
496
- } = this.props;
497
- const {
498
- cellUnit,
499
- startDate,
500
- endDate,
501
- config,
502
- localeDayjs
503
- } = schedulerData;
504
- const {
505
- isSelecting,
506
- left,
507
- width,
508
- startRowIndex,
509
- endRowIndex
510
- } = this.state;
511
- const cellWidth = schedulerData.getContentCellWidth();
512
- const cellMaxEvents = schedulerData.getCellMaxEvents();
513
- const rowWidth = schedulerData.getContentTableWidth();
514
- const selectedArea = isSelecting ? /*#__PURE__*/_jsx(SelectedArea, {
515
- schedulerData: schedulerData,
516
- left: left,
517
- width: width
518
- }) : /*#__PURE__*/_jsx("div", {});
519
- const sharedSelecting = this.props.selectionPreview?.isSelecting;
520
- const isSharedSelectedRow = sharedSelecting && this.props.isRowSelected;
521
- const sharedLeft = this.props.selectionPreview?.left || 0;
522
- const sharedWidth = this.props.selectionPreview?.width || 0;
523
- const sharedSelectedArea = !isSelecting && isSharedSelectedRow ? /*#__PURE__*/_jsx(SelectedArea, {
524
- schedulerData: schedulerData,
525
- left: sharedLeft,
526
- width: sharedWidth
527
- }) : null;
528
-
529
- // Add vertical selection overlay
530
- const verticalSelectionOverlay = isSelecting && startRowIndex !== endRowIndex ? /*#__PURE__*/_jsx("div", {
531
- className: "vertical-selection-overlay"
532
- }) : null;
533
- const eventList = [];
534
- resourceEvents.headerItems.forEach((headerItem, index) => {
535
- if (headerItem.count > 0 || headerItem.summary !== undefined) {
536
- const isTop = config.summaryPos === SummaryPos.TopRight || config.summaryPos === SummaryPos.Top || config.summaryPos === SummaryPos.TopLeft;
537
- const marginTop = resourceEvents.hasSummary && isTop ? 1 + config.eventItemLineHeight : 1;
538
- const renderEventsMaxIndex = headerItem.addMore === 0 ? cellMaxEvents : headerItem.addMoreIndex;
539
- headerItem.events.forEach((evt, idx) => {
540
- if (idx < renderEventsMaxIndex && evt !== undefined && evt.render) {
541
- let durationStart = localeDayjs(new Date(startDate));
542
- let durationEnd = localeDayjs(endDate);
543
- if (cellUnit === CellUnit.Hour) {
544
- durationStart = localeDayjs(new Date(startDate)).add(config.dayStartFrom, 'hours');
545
- durationEnd = localeDayjs(endDate).add(config.dayStopTo + 1, 'hours');
546
- }
547
- const eventStart = normalizeEventStart(evt.eventItem.start);
548
- const eventEnd = normalizeEventEnd(evt.eventItem.end);
549
- const isStart = eventStart >= durationStart;
550
- const isEnd = eventEnd <= durationEnd;
551
- let left = index * cellWidth + (index > 0 ? 2 : 3);
552
- let width = evt.span * cellWidth - (index > 0 ? 5 : 6) > 0 ? evt.span * cellWidth - (index > 0 ? 5 : 6) : 0;
553
- if (cellUnit === CellUnit.Day || cellUnit === CellUnit.Hour) {
554
- // Clamp the event to the cells it covers, then position it proportionally inside the
555
- // first and last cell (each cell's own duration, so DST days and sub-slot times are handled).
556
- const lastHeaderItem = resourceEvents.headerItems[index + evt.span - 1] ?? headerItem;
557
- const firstCellStart = localeDayjs(new Date(headerItem.start));
558
- const firstCellEnd = localeDayjs(new Date(headerItem.end));
559
- const lastCellStart = localeDayjs(new Date(lastHeaderItem.start));
560
- const lastCellEnd = localeDayjs(new Date(lastHeaderItem.end));
561
- const clampedStart = eventStart.isAfter(firstCellStart) ? eventStart : firstCellStart;
562
- const clampedEnd = eventEnd.isBefore(lastCellEnd) ? eventEnd : lastCellEnd;
563
- const startFraction = clampedStart.diff(firstCellStart) / firstCellEnd.diff(firstCellStart);
564
- const endFraction = clampedEnd.diff(lastCellStart) / lastCellEnd.diff(lastCellStart);
565
- const usableWidth = evt.span * cellWidth - (index > 0 ? 5 : 6);
566
- left = index * cellWidth + (index > 0 ? 2 : 3) + usableWidth * startFraction / evt.span;
567
- width = Math.max(1, usableWidth * (evt.span - 1 + endFraction - startFraction) / evt.span);
568
- } else {
569
- width = evt.span * cellWidth - (index > 0 ? 5 : 6) > 0 ? evt.span * cellWidth - (index > 0 ? 5 : 6) : 0;
120
+ this.emitSelectionChange(true, this.getSelectedResourceIds(startRowIndex, startRowIndex), { left, width });
121
+ if (this.supportTouch) {
122
+ document.documentElement.addEventListener('touchmove', asListener(this.doDrag), false);
123
+ document.documentElement.addEventListener('touchend', asListener(this.stopDrag), false);
124
+ document.documentElement.addEventListener('touchcancel', asListener(this.cancelDrag), false);
125
+ }
126
+ else {
127
+ document.documentElement.addEventListener('mousemove', asListener(this.doDrag), false);
128
+ document.documentElement.addEventListener('mouseup', asListener(this.stopDrag), false);
129
+ }
130
+ document.onselectstart = () => false;
131
+ document.ondragstart = () => false;
132
+ };
133
+ doDrag = (ev) => {
134
+ ev.stopPropagation();
135
+ const [clientX, toReturn] = this.dragHelper(ev, 'do');
136
+ if (toReturn) {
137
+ return;
138
+ }
139
+ const { originalStartRowIndex } = this.state;
140
+ const startX = this.state.startX ?? 0;
141
+ const { schedulerData } = this.props;
142
+ const { headers } = schedulerData;
143
+ const cellWidth = schedulerData.getContentCellWidth();
144
+ const pos = getPos(this.eventContainer);
145
+ const currentX = clientX - pos.x;
146
+ let leftIndex = Math.floor(Math.min(startX, currentX) / cellWidth);
147
+ leftIndex = leftIndex < 0 ? 0 : leftIndex;
148
+ const left = leftIndex * cellWidth;
149
+ let rightIndex = Math.ceil(Math.max(startX, currentX) / cellWidth);
150
+ rightIndex = rightIndex > headers.length ? headers.length : rightIndex;
151
+ const width = (rightIndex - leftIndex) * cellWidth;
152
+ // Calculate current row based on per-row heights (not a uniform row height assumption).
153
+ let clientY = ev.clientY;
154
+ const { changedTouches } = ev;
155
+ if (this.supportTouch && changedTouches && changedTouches.length > 0) {
156
+ clientY = changedTouches[0].clientY;
157
+ }
158
+ const currentY = clientY - pos.y;
159
+ const displayRenderData = this.getDisplayRenderData();
160
+ if (displayRenderData.length === 0) {
161
+ this.setState({
162
+ leftIndex,
163
+ left,
164
+ rightIndex,
165
+ width,
166
+ isSelecting: true,
167
+ startRowIndex: -1,
168
+ endRowIndex: -1,
169
+ });
170
+ this.emitSelectionChange(true, [], { left, width });
171
+ return;
172
+ }
173
+ const rowHeights = displayRenderData.map(row => row?.rowHeight || DEFAULT_ROW_HEIGHT);
174
+ const startRowTop = rowHeights.slice(0, Math.max(0, originalStartRowIndex)).reduce((sum, h) => sum + h, 0);
175
+ const absoluteY = startRowTop + currentY;
176
+ let cumulativeHeight = 0;
177
+ let currentRowIndex = rowHeights.length - 1;
178
+ for (let i = 0; i < rowHeights.length; i += 1) {
179
+ cumulativeHeight += rowHeights[i];
180
+ if (absoluteY < cumulativeHeight) {
181
+ currentRowIndex = i;
182
+ break;
570
183
  }
571
- const top = schedulerData.isVerticalResourceView() ? 0 : marginTop + idx * config.eventItemLineHeight;
572
- const height = schedulerData.isVerticalResourceView() ? resourceEvents.rowHeight : undefined;
573
- if (schedulerData.isVerticalResourceView()) {
574
- const eventCount = headerItem.count;
575
- const itemWidth = (cellWidth - (index > 0 ? 5 : 6)) / eventCount;
576
- left = index * cellWidth + (index > 0 ? 2 : 3) + idx * itemWidth;
577
- width = itemWidth;
184
+ }
185
+ if (absoluteY < 0) {
186
+ currentRowIndex = 0;
187
+ }
188
+ const minRowIndex = Math.min(originalStartRowIndex, currentRowIndex);
189
+ const maxRowIndex = Math.max(originalStartRowIndex, currentRowIndex);
190
+ // Clamp to valid row indices
191
+ const clampedMinRow = Math.max(0, minRowIndex);
192
+ const clampedMaxRow = Math.min(displayRenderData.length - 1, maxRowIndex);
193
+ this.setState({
194
+ leftIndex,
195
+ left,
196
+ rightIndex,
197
+ width,
198
+ isSelecting: true,
199
+ startRowIndex: clampedMinRow,
200
+ endRowIndex: clampedMaxRow,
201
+ });
202
+ this.emitSelectionChange(true, this.getSelectedResourceIds(clampedMinRow, clampedMaxRow), { left, width });
203
+ };
204
+ dragHelper = (ev, dragType) => {
205
+ let clientX = 0;
206
+ if (this.supportTouch) {
207
+ const { changedTouches } = ev;
208
+ if (changedTouches.length === 0)
209
+ return [clientX, true];
210
+ const touch = changedTouches[0];
211
+ clientX = touch.pageX;
212
+ }
213
+ else if (dragType === 'init') {
214
+ const { buttons } = ev;
215
+ if (buttons !== undefined && buttons !== 1)
216
+ return [clientX, true];
217
+ clientX = ev.clientX;
218
+ }
219
+ else {
220
+ clientX = ev.clientX;
221
+ }
222
+ return [clientX, false];
223
+ };
224
+ stopDrag = (ev) => {
225
+ if (ev?.stopPropagation)
226
+ ev.stopPropagation();
227
+ const { schedulerData, newEvent, resourceEvents } = this.props;
228
+ const { headers, events, config, cellUnit, localeDayjs } = schedulerData;
229
+ const { leftIndex, rightIndex } = this.state;
230
+ this.cleanupDragInteraction();
231
+ const resetSelection = () => {
232
+ this.setState({
233
+ startX: 0,
234
+ leftIndex: 0,
235
+ left: 0,
236
+ rightIndex: 0,
237
+ width: 0,
238
+ isSelecting: false,
239
+ originalStartRowIndex: -1,
240
+ startRowIndex: -1,
241
+ endRowIndex: -1,
242
+ });
243
+ this.emitSelectionChange(false, [], { left: 0, width: 0 });
244
+ };
245
+ if (headers.length === 0 || !resourceEvents.headerItems || resourceEvents.headerItems.length === 0) {
246
+ resetSelection();
247
+ return;
248
+ }
249
+ // a cell width of 0 (container not measured yet) makes the drag indexes NaN or Infinity
250
+ const toIndex = (value, fallback) => value !== undefined && Number.isFinite(value) ? value : fallback;
251
+ const maxLeftIndex = headers.length - 1;
252
+ const safeLeftIndex = Math.max(0, Math.min(toIndex(leftIndex, 0), maxLeftIndex));
253
+ const maxRightIndex = Math.min(headers.length, resourceEvents.headerItems.length);
254
+ let safeRightIndex = Math.max(1, toIndex(rightIndex, 1), safeLeftIndex + 1);
255
+ safeRightIndex = Math.min(safeRightIndex, maxRightIndex);
256
+ // headers and headerItems can be out of step while the view rebuilds; drop the selection instead of throwing
257
+ if (!headers[safeLeftIndex] || !resourceEvents.headerItems[safeRightIndex - 1]) {
258
+ resetSelection();
259
+ return;
260
+ }
261
+ const isVertical = schedulerData.isVerticalResourceView();
262
+ // vertical view: rows are time slots and columns are resources, so the roles swap
263
+ const selectedRowIds = this.getSelectedResourceIds();
264
+ let startTime;
265
+ let endTime;
266
+ if (isVertical) {
267
+ // in the vertical view slot ids are the row's time string
268
+ startTime = (selectedRowIds[0] ?? resourceEvents.slotId);
269
+ const lastRowId = selectedRowIds[selectedRowIds.length - 1] ?? startTime;
270
+ endTime = localeDayjs(new Date(lastRowId)).add(config.minuteStep, 'minutes').format(DATETIME_FORMAT);
271
+ }
272
+ else {
273
+ startTime = headers[safeLeftIndex].time;
274
+ endTime = resourceEvents.headerItems[safeRightIndex - 1].end;
275
+ if (cellUnit !== CellUnit.Hour) {
276
+ endTime = localeDayjs(new Date(resourceEvents.headerItems[safeRightIndex - 1].start))
277
+ .hour(23)
278
+ .minute(59)
279
+ .second(59)
280
+ .format(DATETIME_FORMAT);
578
281
  }
579
- const eventKey = `${evt.eventItem.id}_${resourceEvents.slotId}_${index}`;
580
- const eventItem = /*#__PURE__*/_jsx(EventItem, {
581
- schedulerData: schedulerData,
582
- eventItem: evt.eventItem,
583
- dndSource: dndSource,
584
- isStart: isStart,
585
- isEnd: isEnd,
586
- isInPopover: false,
587
- slotId: resourceEvents.slotId,
588
- left: left,
589
- width: width,
590
- top: top,
591
- height: height,
592
- leftIndex: index,
593
- rightIndex: index + evt.span
594
- // Passing through functional props
595
- ,
596
- eventItemClick: this.props.eventItemClick,
597
- viewEventClick: this.props.viewEventClick,
598
- viewEventText: this.props.viewEventText,
599
- viewEvent2Click: this.props.viewEvent2Click,
600
- viewEvent2Text: this.props.viewEvent2Text,
601
- eventItemTemplateResolver: this.props.eventItemTemplateResolver,
602
- eventItemPopoverTemplateResolver: this.props.eventItemPopoverTemplateResolver,
603
- subtitleGetter: this.props.subtitleGetter,
604
- updateEventStart: this.props.updateEventStart,
605
- updateEventEnd: this.props.updateEventEnd,
606
- moveEvent: this.props.moveEvent,
607
- conflictOccurred: this.props.conflictOccurred
608
- }, eventKey);
609
- eventList.push(eventItem);
610
- }
282
+ }
283
+ // Get selected resource IDs
284
+ const selectedResourceIds = isVertical
285
+ ? headers
286
+ .slice(safeLeftIndex, safeRightIndex)
287
+ .map(header => header.id)
288
+ .filter((id) => id !== undefined)
289
+ : selectedRowIds;
290
+ const slotId = selectedResourceIds.length > 0 ? selectedResourceIds[0] : resourceEvents.slotId;
291
+ const slotName = selectedResourceIds.length > 0
292
+ ? String(schedulerData.getResourceById(slotId)?.name || slotId)
293
+ : resourceEvents.slotName;
294
+ this.setState({
295
+ startX: 0,
296
+ leftIndex: 0,
297
+ left: 0,
298
+ rightIndex: 0,
299
+ width: 0,
300
+ isSelecting: false,
301
+ originalStartRowIndex: -1,
302
+ startRowIndex: -1,
303
+ endRowIndex: -1,
611
304
  });
612
- if (headerItem.addMore > 0) {
613
- const left = index * cellWidth + (index > 0 ? 2 : 3);
614
- const width = cellWidth - (index > 0 ? 5 : 6);
615
- const top = marginTop + headerItem.addMoreIndex * config.eventItemLineHeight;
616
- const addMoreItem = /*#__PURE__*/_jsx(AddMore, {
617
- schedulerData: schedulerData,
618
- headerItem: headerItem,
619
- number: headerItem.addMore,
620
- left: left,
621
- width: width,
622
- top: top,
623
- clickAction: this.onAddMoreClick
624
- }, `add-more-${headerItem.time}`);
625
- eventList.push(addMoreItem);
305
+ this.emitSelectionChange(false, [], { left: 0, width: 0 });
306
+ let hasConflict = false;
307
+ if (config.checkConflict) {
308
+ const start = localeDayjs(new Date(startTime));
309
+ const end = localeDayjs(endTime);
310
+ events.forEach(e => {
311
+ const eventResourceIds = e.resourceIds || [e.resourceId];
312
+ const hasOverlap = selectedResourceIds.some(selectedId => eventResourceIds.includes(selectedId));
313
+ if (hasOverlap) {
314
+ const eStart = localeDayjs(e.start);
315
+ const eEnd = localeDayjs(e.end);
316
+ if ((start >= eStart && start < eEnd) ||
317
+ (end > eStart && end <= eEnd) ||
318
+ (eStart >= start && eStart < end) ||
319
+ (eEnd > start && eEnd <= end))
320
+ hasConflict = true;
321
+ }
322
+ });
323
+ }
324
+ if (hasConflict) {
325
+ const { conflictOccurred } = this.props;
326
+ if (conflictOccurred !== undefined) {
327
+ conflictOccurred(schedulerData, 'New', {
328
+ id: undefined,
329
+ start: startTime,
330
+ end: endTime,
331
+ resourceId: slotId, // Keep for backward compatibility
332
+ resourceIds: selectedResourceIds, // Add multi-resource support
333
+ slotId,
334
+ slotName,
335
+ title: undefined,
336
+ }, DnDTypes.EVENT, slotId, slotName, startTime, endTime);
337
+ }
338
+ else {
339
+ console.log('Conflict occurred, set conflictOccurred func in Scheduler to handle it');
340
+ }
341
+ }
342
+ else if (newEvent !== undefined) {
343
+ // Pass resourceIds for multi-resource events
344
+ newEvent(schedulerData, slotId, slotName, startTime, endTime, undefined, {
345
+ resourceIds: selectedResourceIds.length > 1 ? selectedResourceIds : undefined,
346
+ });
347
+ }
348
+ };
349
+ cancelDrag = (ev) => {
350
+ if (ev?.stopPropagation)
351
+ ev.stopPropagation();
352
+ const { isSelecting } = this.state;
353
+ if (isSelecting) {
354
+ this.cleanupDragInteraction();
355
+ this.setState({
356
+ startX: 0,
357
+ leftIndex: 0,
358
+ left: 0,
359
+ rightIndex: 0,
360
+ width: 0,
361
+ isSelecting: false,
362
+ originalStartRowIndex: -1,
363
+ startRowIndex: -1,
364
+ endRowIndex: -1,
365
+ });
366
+ this.emitSelectionChange(false, [], { left: 0, width: 0 });
367
+ }
368
+ };
369
+ emitSelectionChange = (isSelecting, selectedResourceIds, preview = {}) => {
370
+ const { onSelectionChange } = this.props;
371
+ if (onSelectionChange) {
372
+ onSelectionChange(isSelecting, selectedResourceIds, preview);
373
+ }
374
+ };
375
+ onAddMoreClick = (headerItem) => {
376
+ const { onSetAddMoreState, resourceEvents, schedulerData } = this.props;
377
+ if (onSetAddMoreState) {
378
+ const { config } = schedulerData;
379
+ const cellWidth = schedulerData.getContentCellWidth();
380
+ const index = resourceEvents.headerItems.indexOf(headerItem);
381
+ if (index !== -1) {
382
+ let left = index * (cellWidth - 1);
383
+ const pos = getPos(this.eventContainer);
384
+ left += pos.x;
385
+ const top = pos.y;
386
+ const height = ((headerItem.count ?? 0) + 1) * config.eventItemLineHeight + 20;
387
+ onSetAddMoreState({
388
+ headerItem,
389
+ left,
390
+ top,
391
+ height,
392
+ });
393
+ }
394
+ }
395
+ };
396
+ eventContainerRef = (element) => {
397
+ this.eventContainer = element;
398
+ // Also set the drop ref if it exists
399
+ const { dropRef } = this.props;
400
+ if (dropRef) {
401
+ dropRef(element);
402
+ }
403
+ };
404
+ getResourceRowIndex = (slotId) => {
405
+ return this.getDisplayRenderData().findIndex(row => row.slotId === slotId);
406
+ };
407
+ getDisplayRenderData = () => {
408
+ const { schedulerData } = this.props;
409
+ return schedulerData.renderData.filter(row => row.render);
410
+ };
411
+ getSelectedResourceIds = (startOverride, endOverride) => {
412
+ const { startRowIndex, endRowIndex } = this.state;
413
+ const displayRenderData = this.getDisplayRenderData();
414
+ const from = startOverride !== undefined ? startOverride : startRowIndex;
415
+ const to = endOverride !== undefined ? endOverride : endRowIndex;
416
+ if (from === -1 || to === -1) {
417
+ return [];
626
418
  }
627
- if (headerItem.summary !== undefined) {
628
- const top = isTop ? 1 : resourceEvents.rowHeight - config.eventItemLineHeight + 1;
629
- const left = index * cellWidth + (index > 0 ? 2 : 3);
630
- const width = cellWidth - (index > 0 ? 5 : 6);
631
- const key = `${resourceEvents.slotId}_${headerItem.time}`;
632
- const summary = /*#__PURE__*/_jsx(Summary, {
633
- schedulerData: schedulerData,
634
- summary: headerItem.summary,
635
- left: left,
636
- width: width,
637
- top: top
638
- }, key);
639
- eventList.push(summary);
419
+ const selectedResourceIds = [];
420
+ for (let i = from; i <= to; i++) {
421
+ const row = displayRenderData[i];
422
+ if (row && !row.groupOnly) {
423
+ selectedResourceIds.push(row.slotId);
424
+ }
640
425
  }
641
- }
642
- });
643
-
644
- // drop preview: styled box where the item being dragged over this row would be placed
645
- const {
646
- dropPreview
647
- } = this.state;
648
- const dropPreviewBox = this.props.isOver && this.props.canDrop && config.dropPreviewEnabled !== false && dropPreview ? /*#__PURE__*/_jsx("div", {
649
- className: `drop-preview${config.dropPreviewClassName ? ` ${config.dropPreviewClassName}` : ''}`,
650
- style: {
651
- left: dropPreview.left,
652
- width: dropPreview.width,
653
- ...config.dropPreviewStyle
654
- }
655
- }) : null;
656
- const eventContainer = /*#__PURE__*/_jsxs("div", {
657
- ref: this.eventContainerRef,
658
- className: "event-container",
659
- "data-slot-id": resourceEvents.slotId,
660
- style: {
661
- height: resourceEvents.rowHeight
662
- },
663
- children: [selectedArea, sharedSelectedArea, verticalSelectionOverlay, dropPreviewBox, eventList]
664
- });
665
- return /*#__PURE__*/_jsx("tr", {
666
- children: /*#__PURE__*/_jsx("td", {
667
- style: {
668
- width: rowWidth
669
- },
670
- children: eventContainer
671
- })
672
- });
673
- }
426
+ return selectedResourceIds;
427
+ };
428
+ render() {
429
+ const { resourceEvents, schedulerData, dndSource } = this.props;
430
+ const { cellUnit, startDate, endDate, config, localeDayjs } = schedulerData;
431
+ const { isSelecting, left, width, startRowIndex, endRowIndex } = this.state;
432
+ const cellWidth = schedulerData.getContentCellWidth();
433
+ const cellMaxEvents = schedulerData.getCellMaxEvents();
434
+ const rowWidth = schedulerData.getContentTableWidth();
435
+ const selectedArea = isSelecting ? (_jsx(SelectedArea, { schedulerData: schedulerData, left: left, width: width })) : (_jsx("div", {}));
436
+ const sharedSelecting = this.props.selectionPreview?.isSelecting;
437
+ const isSharedSelectedRow = sharedSelecting && this.props.isRowSelected;
438
+ const sharedLeft = this.props.selectionPreview?.left || 0;
439
+ const sharedWidth = this.props.selectionPreview?.width || 0;
440
+ const sharedSelectedArea = !isSelecting && isSharedSelectedRow ? (_jsx(SelectedArea, { schedulerData: schedulerData, left: sharedLeft, width: sharedWidth })) : null;
441
+ // Add vertical selection overlay
442
+ const verticalSelectionOverlay = isSelecting && startRowIndex !== endRowIndex ? _jsx("div", { className: "vertical-selection-overlay" }) : null;
443
+ const eventList = [];
444
+ resourceEvents.headerItems.forEach((headerItem, index) => {
445
+ if ((headerItem.count ?? 0) > 0 || headerItem.summary !== undefined) {
446
+ const isTop = config.summaryPos === SummaryPos.TopRight ||
447
+ config.summaryPos === SummaryPos.Top ||
448
+ config.summaryPos === SummaryPos.TopLeft;
449
+ const marginTop = resourceEvents.hasSummary && isTop ? 1 + config.eventItemLineHeight : 1;
450
+ const renderEventsMaxIndex = headerItem.addMore === 0 ? cellMaxEvents : (headerItem.addMoreIndex ?? 0);
451
+ headerItem.events.forEach((evt, idx) => {
452
+ if (idx < renderEventsMaxIndex && evt !== undefined && evt.render) {
453
+ let durationStart = localeDayjs(toDate(startDate));
454
+ let durationEnd = localeDayjs(endDate);
455
+ if (cellUnit === CellUnit.Hour) {
456
+ durationStart = localeDayjs(toDate(startDate)).add(config.dayStartFrom, 'hours');
457
+ durationEnd = localeDayjs(endDate).add(config.dayStopTo + 1, 'hours');
458
+ }
459
+ const eventStart = normalizeEventStart(evt.eventItem.start);
460
+ const eventEnd = normalizeEventEnd(evt.eventItem.end);
461
+ const isStart = eventStart >= durationStart;
462
+ const isEnd = eventEnd <= durationEnd;
463
+ let left = index * cellWidth + (index > 0 ? 2 : 3);
464
+ let width = evt.span * cellWidth - (index > 0 ? 5 : 6) > 0 ? evt.span * cellWidth - (index > 0 ? 5 : 6) : 0;
465
+ if (cellUnit === CellUnit.Day || cellUnit === CellUnit.Hour) {
466
+ // Clamp the event to the cells it covers, then position it proportionally inside the
467
+ // first and last cell (each cell's own duration, so DST days and sub-slot times are handled).
468
+ const lastHeaderItem = resourceEvents.headerItems[index + evt.span - 1] ?? headerItem;
469
+ const firstCellStart = localeDayjs(new Date(headerItem.start));
470
+ const firstCellEnd = localeDayjs(new Date(headerItem.end));
471
+ const lastCellStart = localeDayjs(new Date(lastHeaderItem.start));
472
+ const lastCellEnd = localeDayjs(new Date(lastHeaderItem.end));
473
+ const clampedStart = eventStart.isAfter(firstCellStart) ? eventStart : firstCellStart;
474
+ const clampedEnd = eventEnd.isBefore(lastCellEnd) ? eventEnd : lastCellEnd;
475
+ const startFraction = clampedStart.diff(firstCellStart) / firstCellEnd.diff(firstCellStart);
476
+ const endFraction = clampedEnd.diff(lastCellStart) / lastCellEnd.diff(lastCellStart);
477
+ const usableWidth = evt.span * cellWidth - (index > 0 ? 5 : 6);
478
+ left = index * cellWidth + (index > 0 ? 2 : 3) + (usableWidth * startFraction) / evt.span;
479
+ width = Math.max(1, (usableWidth * (evt.span - 1 + endFraction - startFraction)) / evt.span);
480
+ }
481
+ else {
482
+ width = evt.span * cellWidth - (index > 0 ? 5 : 6) > 0 ? evt.span * cellWidth - (index > 0 ? 5 : 6) : 0;
483
+ }
484
+ const top = schedulerData.isVerticalResourceView() ? 0 : marginTop + idx * config.eventItemLineHeight;
485
+ const height = schedulerData.isVerticalResourceView() ? resourceEvents.rowHeight : undefined;
486
+ if (schedulerData.isVerticalResourceView()) {
487
+ const eventCount = headerItem.count ?? 0;
488
+ const itemWidth = (cellWidth - (index > 0 ? 5 : 6)) / eventCount;
489
+ left = index * cellWidth + (index > 0 ? 2 : 3) + idx * itemWidth;
490
+ width = itemWidth;
491
+ }
492
+ const eventKey = `${evt.eventItem.id}_${resourceEvents.slotId}_${index}`;
493
+ const eventItem = (_jsx(EventItem, { schedulerData: schedulerData, eventItem: evt.eventItem, dndSource: dndSource, isStart: isStart, isEnd: isEnd, isInPopover: false, slotId: resourceEvents.slotId, left: left, width: width, top: top, height: height, leftIndex: index, rightIndex: index + evt.span,
494
+ // Passing through functional props
495
+ eventItemClick: this.props.eventItemClick, viewEventClick: this.props.viewEventClick, viewEventText: this.props.viewEventText, viewEvent2Click: this.props.viewEvent2Click, viewEvent2Text: this.props.viewEvent2Text, eventItemTemplateResolver: this.props.eventItemTemplateResolver, eventItemPopoverTemplateResolver: this.props.eventItemPopoverTemplateResolver, subtitleGetter: this.props.subtitleGetter, updateEventStart: this.props.updateEventStart, updateEventEnd: this.props.updateEventEnd, moveEvent: this.props.moveEvent, conflictOccurred: this.props.conflictOccurred }, eventKey));
496
+ eventList.push(eventItem);
497
+ }
498
+ });
499
+ if (headerItem.addMore !== undefined && headerItem.addMore > 0) {
500
+ const left = index * cellWidth + (index > 0 ? 2 : 3);
501
+ const width = cellWidth - (index > 0 ? 5 : 6);
502
+ const top = marginTop + (headerItem.addMoreIndex ?? 0) * config.eventItemLineHeight;
503
+ const addMoreItem = (_jsx(AddMore, { schedulerData: schedulerData, headerItem: headerItem, number: headerItem.addMore, left: left, width: width, top: top, clickAction: this.onAddMoreClick }, `add-more-${headerItem.time}`));
504
+ eventList.push(addMoreItem);
505
+ }
506
+ if (headerItem.summary !== undefined) {
507
+ const top = isTop ? 1 : resourceEvents.rowHeight - config.eventItemLineHeight + 1;
508
+ const left = index * cellWidth + (index > 0 ? 2 : 3);
509
+ const width = cellWidth - (index > 0 ? 5 : 6);
510
+ const key = `${resourceEvents.slotId}_${headerItem.time}`;
511
+ const summary = (_jsx(Summary, { schedulerData: schedulerData, summary: headerItem.summary, left: left, width: width, top: top }, key));
512
+ eventList.push(summary);
513
+ }
514
+ }
515
+ });
516
+ // drop preview: styled box where the item being dragged over this row would be placed
517
+ const { dropPreview } = this.state;
518
+ const dropPreviewBox = this.props.isOver && this.props.canDrop && config.dropPreviewEnabled !== false && dropPreview ? (_jsx("div", { className: `drop-preview${config.dropPreviewClassName ? ` ${config.dropPreviewClassName}` : ''}`, style: { left: dropPreview.left, width: dropPreview.width, ...config.dropPreviewStyle } })) : null;
519
+ const eventContainer = (_jsxs("div", { ref: this.eventContainerRef, className: "event-container", "data-slot-id": resourceEvents.slotId, style: { height: resourceEvents.rowHeight }, children: [selectedArea, sharedSelectedArea, verticalSelectionOverlay, dropPreviewBox, eventList] }));
520
+ return (_jsx("tr", { children: _jsx("td", { style: { width: rowWidth }, children: eventContainer }) }));
521
+ }
674
522
  }
675
-
676
523
  // Wrapper component to use useDrop hook
677
- const ResourceEventsWithDnD = props => {
678
- const {
679
- schedulerData,
680
- dndContext
681
- } = props;
682
- const {
683
- config
684
- } = schedulerData;
685
- const componentRef = React.useRef(null);
686
- const propsRef = React.useRef(props);
687
-
688
- // Keep propsRef up to date
689
- React.useEffect(() => {
690
- propsRef.current = props;
691
- }, [props]);
692
-
693
- // Always call useDrop unconditionally (Rules of Hooks)
694
- // Disable functionality when drag and drop is not enabled
695
- const [{
696
- isOver,
697
- canDrop
698
- }, dropRef] = useDrop(() => {
699
- // If drag and drop is disabled, return a no-op spec
700
- if (!config.dragAndDropEnabled || !dndContext) {
701
- return {
702
- accept: [],
703
- collect: () => ({
704
- isOver: false,
705
- canDrop: false
706
- })
707
- };
708
- }
709
- const spec = dndContext.getDropSpec();
710
- return {
711
- accept: [...dndContext.sourceMap.keys()],
712
- drop: (_item, monitor) => spec.drop(propsRef.current, monitor, componentRef.current),
713
- hover: (_item, monitor) => spec.hover(propsRef.current, monitor, componentRef.current),
714
- canDrop: (_item, monitor) => spec.canDrop(propsRef.current, monitor),
715
- collect: monitor => ({
716
- isOver: monitor.isOver(),
717
- canDrop: monitor.canDrop()
718
- })
719
- };
720
- }, [dndContext, config.dragAndDropEnabled]);
721
- return /*#__PURE__*/_jsx(ResourceEvents, {
722
- ref: componentRef,
723
- ...props,
724
- dropRef: dropRef,
725
- isOver: isOver,
726
- canDrop: canDrop
727
- });
524
+ const ResourceEventsWithDnD = (props) => {
525
+ const { schedulerData, dndContext } = props;
526
+ const { config } = schedulerData;
527
+ const componentRef = React.useRef(null);
528
+ const propsRef = React.useRef(props);
529
+ // Keep propsRef up to date
530
+ React.useEffect(() => {
531
+ propsRef.current = props;
532
+ }, [props]);
533
+ // Always call useDrop unconditionally (Rules of Hooks)
534
+ // Disable functionality when drag and drop is not enabled
535
+ const [{ isOver, canDrop }, dropRef] = useDrop(() => {
536
+ // If drag and drop is disabled, return a no-op spec
537
+ if (!config.dragAndDropEnabled || !dndContext) {
538
+ return {
539
+ accept: [],
540
+ collect: () => ({ isOver: false, canDrop: false }),
541
+ };
542
+ }
543
+ const spec = dndContext.getDropSpec();
544
+ return {
545
+ accept: [...dndContext.sourceMap.keys()],
546
+ drop: (_item, monitor) => componentRef.current ? spec.drop(propsRef.current, monitor, componentRef.current) : undefined,
547
+ hover: (_item, monitor) => {
548
+ if (componentRef.current)
549
+ spec.hover(propsRef.current, monitor, componentRef.current);
550
+ },
551
+ canDrop: (_item, monitor) => spec.canDrop(propsRef.current, monitor),
552
+ collect: monitor => ({
553
+ isOver: monitor.isOver(),
554
+ canDrop: monitor.canDrop(),
555
+ }),
556
+ };
557
+ }, [dndContext, config.dragAndDropEnabled]);
558
+ return _jsx(ResourceEvents, { ref: componentRef, ...props, dropRef: dropRef, isOver: isOver, canDrop: canDrop });
728
559
  };
729
560
  ResourceEventsWithDnD.displayName = 'ResourceEventsWithDnD';
730
- export default /*#__PURE__*/React.memo(ResourceEventsWithDnD);
561
+ export default React.memo(ResourceEventsWithDnD);