react-big-schedule 8.1.0 → 9.0.1

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 +24 -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 +37 -91
  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 +30 -27
@@ -1,1271 +1,1341 @@
1
1
  import dayjs from 'dayjs';
2
2
  import isoWeek from 'dayjs/plugin/isoWeek';
3
3
  import quarterOfYear from 'dayjs/plugin/quarterOfYear';
4
+ import timezone from 'dayjs/plugin/timezone';
4
5
  import utc from 'dayjs/plugin/utc';
5
6
  import weekday from 'dayjs/plugin/weekday';
6
7
  import { RRuleSet, rrulestr } from 'rrule';
7
8
  import { CellUnit, DATE_FORMAT, DATETIME_FORMAT, ViewType } from '../config/default';
8
9
  import { getDefaultLabels, getLabel } from '../config/i18n';
9
10
  import config from '../config/scheduler';
10
- import behaviors from '../helper/behaviors';
11
+ import behaviors, { toDate } from '../helper/behaviors';
11
12
  import { normalizeEventEnd, normalizeEventStart } from '../helper/utility';
13
+ // dayjs's quarterOfYear plugin adds 'quarters' to add() but not to the ManipulateType the base overload accepts.
14
+ const addUnit = (d, amount, unit) => d.add(amount, unit);
15
+ const isPercent = (value) => typeof value === 'string' && value.endsWith('%');
16
+ const percentOf = (base, percent) => Math.trunc((base * Number(String(percent).slice(0, -1))) / 100);
17
+ // Non-percent widths are pixel numbers by contract.
18
+ const asPx = (value) => value;
12
19
  export default class SchedulerData {
13
- constructor(date = dayjs(), viewType = ViewType.Week, showAgenda = false, isEventPerspective = false, newConfig = undefined, newBehaviors = undefined) {
14
- this.resources = [];
15
- this.events = [];
16
- this.eventGroups = [];
17
- this.eventGroupsAutoGenerated = true;
18
- this.viewType = viewType;
19
- this.cellUnit = viewType === ViewType.Day || viewType === ViewType.VerticalResource ? CellUnit.Hour : CellUnit.Day;
20
- this.showAgenda = showAgenda;
21
- this.isEventPerspective = isEventPerspective;
22
- this.resizing = false;
23
- this.scrollToSpecialDayjs = false;
24
- this.documentWidth = 0;
25
- this.documentHeight = 0;
26
- this.schedulerHeaderHeight = 0;
27
- this._shouldReloadViewType = false;
28
- this.version = 0;
29
- this._batchCount = 0;
30
- this._versionChangeCallback = null;
31
- this.calendarPopoverLocale = undefined;
32
- dayjs.extend(quarterOfYear);
33
- dayjs.extend(weekday);
34
- dayjs.extend(utc);
35
- dayjs.extend(isoWeek);
36
- this.localeDayjs = this._createLocaleDayjs();
37
- this.config = newConfig === undefined ? config : {
38
- ...config,
39
- ...newConfig
40
- };
41
- this._updateLabelsFromI18n();
42
- this._validateMinuteStep(this.config.minuteStep);
43
- this.behaviors = newBehaviors === undefined ? behaviors : {
44
- ...behaviors,
45
- ...newBehaviors
46
- };
47
- this._resolveDate(0, date);
48
- this._createHeaders();
49
- this._createRenderData();
50
- }
51
-
52
- // Per-instance dayjs: the locale is applied to each created date instead of the global dayjs
53
- // singleton, so schedulers (and the host app) don't overwrite each other's locale (#146).
54
- _createLocaleDayjs() {
55
- let locale;
56
- const localeDayjs = (...args) => {
57
- const d = dayjs(...args);
58
- return locale ? d.locale(locale) : d;
59
- };
60
- localeDayjs.locale = preset => {
61
- if (preset === undefined) return locale || dayjs.locale();
62
- // dayjs accepts a locale name or a locale object; resolve (and register) it once to a name
63
- locale = dayjs().locale(preset).locale();
64
- return localeDayjs;
65
- };
66
- return localeDayjs;
67
- }
68
-
69
- /**
70
- * Update user-facing labels from the i18n provider
71
- * This allows apps to provide localized labels via setLabelsProvider()
72
- * @private
73
- */
74
- _updateLabelsFromI18n(locale) {
75
- // Get labels from the i18n provider if available
76
- const defaults = getDefaultLabels();
77
- const applyIfProvided = (key, current) => {
78
- const value = getLabel(key, locale);
79
- if (value === undefined || value === null) return current;
80
- return value !== defaults[key] ? value : current;
81
- };
82
- this.config.resourceName = applyIfProvided('resourceName', this.config.resourceName);
83
- this.config.taskName = applyIfProvided('taskName', this.config.taskName);
84
- this.config.agendaViewHeader = applyIfProvided('agendaViewHeader', this.config.agendaViewHeader);
85
- this.config.weekNumberLabel = applyIfProvided('weekNumberLabel', this.config.weekNumberLabel);
86
- }
87
- setVersionChangeNotifier(callback) {
88
- this._versionChangeCallback = typeof callback === 'function' ? callback : null;
89
- }
90
- beginBatch() {
91
- this._batchCount = (this._batchCount || 0) + 1;
92
- }
93
- endBatch() {
94
- if (this._batchCount === undefined || this._batchCount <= 0) {
95
- this._batchCount = 0;
96
- console.warn('SchedulerData.endBatch called without a matching beginBatch');
97
- return;
98
- }
99
- if (this._batchCount > 0) {
100
- this._batchCount -= 1;
101
- if (this._batchCount === 0 && this._pendingVersion !== undefined) {
102
- const pendingVersion = this._pendingVersion;
103
- this._pendingVersion = undefined;
104
- if (typeof this._versionChangeCallback === 'function') {
105
- this._versionChangeCallback(pendingVersion);
106
- }
107
- }
108
- }
109
- }
110
- bumpVersion() {
111
- this.version += 1;
112
- if (this._batchCount > 0) {
113
- // Defer callback until batch ends
114
- this._pendingVersion = this.version;
115
- } else if (typeof this._versionChangeCallback === 'function') {
116
- this._versionChangeCallback(this.version);
117
- }
118
- }
119
- getVersion() {
120
- return this.version;
121
- }
122
- isVerticalResourceView() {
123
- return this.viewType === ViewType.VerticalResource;
124
- }
125
- setSchedulerLocale(preset) {
126
- if (!preset) return;
127
- this.localeDayjs.locale(preset);
128
- this._updateLabelsFromI18n(preset);
129
- this._shouldReloadViewType = true;
130
- this.setViewType(this.viewType, this.showAgenda, this.isEventPerspective);
131
- }
132
- setCalendarPopoverLocale(lang) {
133
- if (lang) {
134
- this.calendarPopoverLocale = lang;
135
- }
136
- }
137
- setResources(resources) {
138
- this._validateResource(resources);
139
- this.resources = Array.from(new Set(resources));
140
- // vertical view builds its headers from the resources
141
- if (this.isVerticalResourceView()) {
142
- this._createHeaders();
143
- }
144
- this._createRenderData();
145
- this.setScrollToSpecialDayjs(true);
146
- this.bumpVersion();
147
- }
148
- setEventGroupsAutoGenerated(autoGenerated) {
149
- this.eventGroupsAutoGenerated = autoGenerated;
150
- }
151
-
152
- // optional
153
- setEventGroups(eventGroups) {
154
- this._validateEventGroups(eventGroups);
155
- this.eventGroups = Array.from(new Set(eventGroups));
156
- this.eventGroupsAutoGenerated = false;
157
- this._createRenderData();
158
- this.setScrollToSpecialDayjs(true);
159
- this.bumpVersion();
160
- }
161
- setMinuteStep(minuteStep) {
162
- if (this.config.minuteStep !== minuteStep) {
163
- this._validateMinuteStep(minuteStep);
164
- this.config.minuteStep = minuteStep;
165
- this._createHeaders();
166
- this._createRenderData();
167
- this.bumpVersion();
168
- }
169
- }
170
- setUnderneathHeight(underneathHeight) {
171
- if (underneathHeight >= 0 && this.config.underneathHeight !== underneathHeight) {
172
- this.config.underneathHeight = underneathHeight;
173
- this.bumpVersion();
174
- }
175
- }
176
- getMinuteStepsInHour() {
177
- return 60 / this.config.minuteStep;
178
- }
179
- addResource(resource) {
180
- const existedResources = this.resources.filter(x => x.id === resource.id);
181
- if (existedResources.length === 0) {
182
- this.resources.push(resource);
183
- if (this.isVerticalResourceView()) {
184
- this._createHeaders();
185
- }
186
- this._createRenderData();
187
- this.bumpVersion();
188
- }
189
- }
190
- addEventGroup(eventGroup) {
191
- const existedEventGroups = this.eventGroups.filter(x => x.id === eventGroup.id);
192
- if (existedEventGroups.length === 0) {
193
- this.eventGroups.push(eventGroup);
194
- this._createRenderData();
195
- this.bumpVersion();
196
- }
197
- }
198
- removeEventGroupById(eventGroupId) {
199
- let index = -1;
200
- this.eventGroups.forEach((item, idx) => {
201
- if (item.id === eventGroupId) index = idx;
202
- });
203
- if (index !== -1) {
204
- this.eventGroups.splice(index, 1);
205
- this._createRenderData();
206
- this.bumpVersion();
207
- }
208
- }
209
- containsEventGroupId(eventGroupId) {
210
- let index = -1;
211
- this.eventGroups.forEach((item, idx) => {
212
- if (item.id === eventGroupId) index = idx;
213
- });
214
- return index !== -1;
215
- }
216
- setEvents(events) {
217
- this._validateEvents(events);
218
- this.events = Array.from(events);
219
- if (this.eventGroupsAutoGenerated) this._generateEventGroups();
220
- if (this.config.recurringEventsEnabled) this._handleRecurringEvents();
221
- this._createRenderData();
222
- this.bumpVersion();
223
- }
224
- setScrollToSpecialDayjs(scrollToSpecialDayjs) {
225
- if (this.config.scrollToSpecialDayjsEnabled) this.scrollToSpecialDayjs = scrollToSpecialDayjs;
226
- }
227
- prev() {
228
- this._resolveDate(-1);
229
- this.events = [];
230
- this._createHeaders();
231
- this._createRenderData();
232
- this.bumpVersion();
233
- }
234
- next() {
235
- this._resolveDate(1);
236
- this.events = [];
237
- this._createHeaders();
238
- this._createRenderData();
239
- this.bumpVersion();
240
- }
241
- setDate(date = dayjs(new Date())) {
242
- this._resolveDate(0, date);
243
- this.events = [];
244
- this._createHeaders();
245
- this._createRenderData();
246
- this.bumpVersion();
247
- }
248
- setViewType(viewType = ViewType.Week, showAgenda = false, isEventPerspective = false) {
249
- const previousShowAgenda = this.showAgenda;
250
- const previousIsEventPerspective = this.isEventPerspective;
251
- this.showAgenda = showAgenda;
252
- this.isEventPerspective = isEventPerspective;
253
- const isCustomView = viewType === ViewType.Custom || viewType === ViewType.Custom1 || viewType === ViewType.Custom2;
254
- const isVerticalView = viewType === ViewType.VerticalResource;
255
- if (!isCustomView && viewType !== ViewType.Day && !isVerticalView) {
256
- this.cellUnit = CellUnit.Day;
257
- } else if (viewType === ViewType.Day || isVerticalView) {
258
- this.cellUnit = CellUnit.Hour;
259
- }
260
-
261
- // If showAgenda or isEventPerspective changed, force reload
262
- if (previousShowAgenda !== showAgenda || previousIsEventPerspective !== isEventPerspective) {
263
- this._shouldReloadViewType = true;
264
- }
265
- if (this.viewType !== viewType || this._shouldReloadViewType) {
266
- let date = this.startDate;
267
- if (viewType === ViewType.Custom || viewType === ViewType.Custom1 || viewType === ViewType.Custom2) {
20
+ resources;
21
+ events;
22
+ eventGroups;
23
+ eventGroupsAutoGenerated;
24
+ viewType;
25
+ cellUnit;
26
+ showAgenda;
27
+ isEventPerspective;
28
+ resizing;
29
+ scrollToSpecialDayjs;
30
+ documentWidth;
31
+ documentHeight;
32
+ schedulerHeaderHeight;
33
+ version;
34
+ localeDayjs;
35
+ config;
36
+ behaviors;
37
+ selectDate;
38
+ startDate;
39
+ endDate;
40
+ timeSlots;
41
+ headers;
42
+ renderData;
43
+ _shouldReloadViewType;
44
+ _batchCount;
45
+ _versionChangeCallback;
46
+ _pendingVersion;
47
+ constructor(date = dayjs(), viewType = ViewType.Week, showAgenda = false, isEventPerspective = false, newConfig = undefined, newBehaviors = undefined) {
48
+ this.resources = [];
49
+ this.events = [];
50
+ this.eventGroups = [];
51
+ this.eventGroupsAutoGenerated = true;
268
52
  this.viewType = viewType;
53
+ this.cellUnit = viewType === ViewType.Day || viewType === ViewType.VerticalResource ? CellUnit.Hour : CellUnit.Day;
54
+ this.showAgenda = showAgenda;
55
+ this.isEventPerspective = isEventPerspective;
56
+ this.resizing = false;
57
+ this.scrollToSpecialDayjs = false;
58
+ this.documentWidth = 0;
59
+ this.documentHeight = 0;
60
+ this.schedulerHeaderHeight = 0;
61
+ this._shouldReloadViewType = false;
62
+ this.version = 0;
63
+ this._batchCount = 0;
64
+ this._versionChangeCallback = null;
65
+ dayjs.extend(quarterOfYear);
66
+ dayjs.extend(weekday);
67
+ dayjs.extend(utc);
68
+ dayjs.extend(timezone);
69
+ dayjs.extend(isoWeek);
70
+ this.localeDayjs = this._createLocaleDayjs();
71
+ this.config = newConfig === undefined ? config : { ...config, ...newConfig };
72
+ this._updateLabelsFromI18n();
73
+ this._validateMinuteStep(this.config.minuteStep);
74
+ this.behaviors = newBehaviors === undefined ? behaviors : { ...behaviors, ...newBehaviors };
269
75
  this._resolveDate(0, date);
270
- } else {
271
- if (this.viewType < viewType) {
272
- if (viewType === ViewType.Week) {
273
- this.startDate = this.localeDayjs(new Date(date)).startOf('isoWeek');
274
- this.endDate = this.localeDayjs(new Date(this.startDate)).endOf('isoWeek');
275
- } else if (viewType === ViewType.Month) {
276
- this.startDate = this.localeDayjs(new Date(date)).startOf('month');
277
- this.endDate = this.localeDayjs(new Date(this.startDate)).endOf('month');
278
- } else if (viewType === ViewType.Quarter) {
279
- this.startDate = this.localeDayjs(new Date(date)).startOf('quarter');
280
- this.endDate = this.localeDayjs(new Date(this.startDate)).endOf('quarter');
281
- } else if (viewType === ViewType.Year) {
282
- this.startDate = this.localeDayjs(new Date(date)).startOf('year');
283
- this.endDate = this.localeDayjs(new Date(this.startDate)).endOf('year');
284
- }
285
- } else {
286
- const start = this.localeDayjs(new Date(this.startDate));
287
- const end = this.localeDayjs(new Date(this.endDate)).add(1, 'days');
288
- if (this.selectDate !== undefined) {
289
- const selectDate = this.localeDayjs(new Date(this.selectDate));
290
- if (selectDate >= start && selectDate < end) {
291
- date = this.selectDate;
76
+ this._createHeaders();
77
+ this._createRenderData();
78
+ }
79
+ // Per-instance dayjs: the locale is applied to each created date instead of the global dayjs
80
+ // singleton, so schedulers (and the host app) don't overwrite each other's locale (#146).
81
+ _createLocaleDayjs() {
82
+ let locale;
83
+ const create = (date, format, strict) => {
84
+ const d = dayjs(date, format, strict);
85
+ return locale ? d.locale(locale) : d;
86
+ };
87
+ function setLocale(preset) {
88
+ if (preset === undefined)
89
+ return locale || dayjs.locale();
90
+ // dayjs accepts a locale name or a locale object; resolve (and register) it once to a name
91
+ locale = dayjs().locale(preset).locale();
92
+ return localeDayjs;
93
+ }
94
+ // wall-clock `date` interpreted in IANA zone `zone` (instants / Dates / Dayjs are converted instead)
95
+ const tz = (date, zone) => {
96
+ const d = dayjs.tz(date, zone);
97
+ return locale ? d.locale(locale) : d;
98
+ };
99
+ const localeDayjs = Object.assign(create, { locale: setLocale, utc: dayjs.utc, tz });
100
+ return localeDayjs;
101
+ }
102
+ /**
103
+ * Update user-facing labels from the i18n provider
104
+ * This allows apps to provide localized labels via setLabelsProvider()
105
+ * @private
106
+ */
107
+ _updateLabelsFromI18n(locale) {
108
+ // Get labels from the i18n provider if available
109
+ const defaults = getDefaultLabels();
110
+ const applyIfProvided = (key, current) => {
111
+ const value = getLabel(key, locale);
112
+ if (value === undefined || value === null)
113
+ return current;
114
+ return value !== defaults[key] ? value : current;
115
+ };
116
+ this.config.resourceName = applyIfProvided('resourceName', this.config.resourceName);
117
+ this.config.taskName = applyIfProvided('taskName', this.config.taskName);
118
+ this.config.agendaViewHeader = applyIfProvided('agendaViewHeader', this.config.agendaViewHeader);
119
+ this.config.weekNumberLabel = applyIfProvided('weekNumberLabel', this.config.weekNumberLabel);
120
+ }
121
+ setVersionChangeNotifier(callback) {
122
+ this._versionChangeCallback = typeof callback === 'function' ? callback : null;
123
+ }
124
+ beginBatch() {
125
+ this._batchCount = (this._batchCount || 0) + 1;
126
+ }
127
+ endBatch() {
128
+ if (this._batchCount === undefined || this._batchCount <= 0) {
129
+ this._batchCount = 0;
130
+ console.warn('SchedulerData.endBatch called without a matching beginBatch');
131
+ return;
132
+ }
133
+ if (this._batchCount > 0) {
134
+ this._batchCount -= 1;
135
+ if (this._batchCount === 0 && this._pendingVersion !== undefined) {
136
+ const pendingVersion = this._pendingVersion;
137
+ this._pendingVersion = undefined;
138
+ if (typeof this._versionChangeCallback === 'function') {
139
+ this._versionChangeCallback(pendingVersion);
140
+ }
141
+ }
142
+ }
143
+ }
144
+ bumpVersion() {
145
+ this.version += 1;
146
+ if (this._batchCount > 0) {
147
+ // Defer callback until batch ends
148
+ this._pendingVersion = this.version;
149
+ }
150
+ else if (typeof this._versionChangeCallback === 'function') {
151
+ this._versionChangeCallback(this.version);
152
+ }
153
+ }
154
+ getVersion() {
155
+ return this.version;
156
+ }
157
+ isVerticalResourceView() {
158
+ return this.viewType === ViewType.VerticalResource;
159
+ }
160
+ setSchedulerLocale(preset) {
161
+ if (!preset)
162
+ return;
163
+ this.localeDayjs.locale(preset);
164
+ this._updateLabelsFromI18n(preset);
165
+ this._shouldReloadViewType = true;
166
+ this.setViewType(this.viewType, this.showAgenda, this.isEventPerspective);
167
+ }
168
+ setResources(resources) {
169
+ this._validateResource(resources);
170
+ this.resources = Array.from(new Set(resources));
171
+ // vertical view builds its headers from the resources
172
+ if (this.isVerticalResourceView()) {
173
+ this._createHeaders();
174
+ }
175
+ this._createRenderData();
176
+ this.setScrollToSpecialDayjs(true);
177
+ this.bumpVersion();
178
+ }
179
+ setEventGroupsAutoGenerated(autoGenerated) {
180
+ this.eventGroupsAutoGenerated = autoGenerated;
181
+ }
182
+ // optional
183
+ setEventGroups(eventGroups) {
184
+ this._validateEventGroups(eventGroups);
185
+ this.eventGroups = Array.from(new Set(eventGroups));
186
+ this.eventGroupsAutoGenerated = false;
187
+ this._createRenderData();
188
+ this.setScrollToSpecialDayjs(true);
189
+ this.bumpVersion();
190
+ }
191
+ setMinuteStep(minuteStep) {
192
+ if (this.config.minuteStep !== minuteStep) {
193
+ this._validateMinuteStep(minuteStep);
194
+ this.config.minuteStep = minuteStep;
195
+ this._createHeaders();
196
+ this._createRenderData();
197
+ this.bumpVersion();
198
+ }
199
+ }
200
+ setUnderneathHeight(underneathHeight) {
201
+ if (underneathHeight >= 0 && this.config.underneathHeight !== underneathHeight) {
202
+ this.config.underneathHeight = underneathHeight;
203
+ this.bumpVersion();
204
+ }
205
+ }
206
+ getMinuteStepsInHour() {
207
+ return 60 / this.config.minuteStep;
208
+ }
209
+ addResource(resource) {
210
+ const existedResources = this.resources.filter(x => x.id === resource.id);
211
+ if (existedResources.length === 0) {
212
+ this.resources.push(resource);
213
+ if (this.isVerticalResourceView()) {
214
+ this._createHeaders();
292
215
  }
293
- }
294
- const now = this.localeDayjs();
295
- if (now >= start && now < end) {
296
- date = now.startOf('day');
297
- }
298
- if (viewType === ViewType.Day || viewType === ViewType.VerticalResource) {
299
- this.startDate = this.localeDayjs(new Date(date)).startOf('day');
300
- this.endDate = this.startDate;
216
+ this._createRenderData();
217
+ this.bumpVersion();
218
+ }
219
+ }
220
+ addEventGroup(eventGroup) {
221
+ const existedEventGroups = this.eventGroups.filter(x => x.id === eventGroup.id);
222
+ if (existedEventGroups.length === 0) {
223
+ this.eventGroups.push(eventGroup);
224
+ this._createRenderData();
225
+ this.bumpVersion();
226
+ }
227
+ }
228
+ removeEventGroupById(eventGroupId) {
229
+ let index = -1;
230
+ this.eventGroups.forEach((item, idx) => {
231
+ if (item.id === eventGroupId)
232
+ index = idx;
233
+ });
234
+ if (index !== -1) {
235
+ this.eventGroups.splice(index, 1);
236
+ this._createRenderData();
237
+ this.bumpVersion();
238
+ }
239
+ }
240
+ containsEventGroupId(eventGroupId) {
241
+ let index = -1;
242
+ this.eventGroups.forEach((item, idx) => {
243
+ if (item.id === eventGroupId)
244
+ index = idx;
245
+ });
246
+ return index !== -1;
247
+ }
248
+ setEvents(events) {
249
+ this._validateEvents(events);
250
+ this.events = Array.from(events);
251
+ if (this.eventGroupsAutoGenerated)
252
+ this._generateEventGroups();
253
+ if (this.config.recurringEventsEnabled)
254
+ this._handleRecurringEvents();
255
+ this._createRenderData();
256
+ this.bumpVersion();
257
+ }
258
+ setScrollToSpecialDayjs(scrollToSpecialDayjs) {
259
+ if (this.config.scrollToSpecialDayjsEnabled)
260
+ this.scrollToSpecialDayjs = scrollToSpecialDayjs;
261
+ }
262
+ prev() {
263
+ this._resolveDate(-1);
264
+ this.events = [];
265
+ this._createHeaders();
266
+ this._createRenderData();
267
+ this.bumpVersion();
268
+ }
269
+ next() {
270
+ this._resolveDate(1);
271
+ this.events = [];
272
+ this._createHeaders();
273
+ this._createRenderData();
274
+ this.bumpVersion();
275
+ }
276
+ setDate(date = dayjs(new Date())) {
277
+ this._resolveDate(0, date);
278
+ this.events = [];
279
+ this._createHeaders();
280
+ this._createRenderData();
281
+ this.bumpVersion();
282
+ }
283
+ setViewType(viewType = ViewType.Week, showAgenda = false, isEventPerspective = false) {
284
+ const previousShowAgenda = this.showAgenda;
285
+ const previousIsEventPerspective = this.isEventPerspective;
286
+ this.showAgenda = showAgenda;
287
+ this.isEventPerspective = isEventPerspective;
288
+ const isCustomView = viewType === ViewType.Custom || viewType === ViewType.Custom1 || viewType === ViewType.Custom2;
289
+ const isVerticalView = viewType === ViewType.VerticalResource;
290
+ if (!isCustomView && viewType !== ViewType.Day && !isVerticalView) {
291
+ this.cellUnit = CellUnit.Day;
292
+ }
293
+ else if (viewType === ViewType.Day || isVerticalView) {
301
294
  this.cellUnit = CellUnit.Hour;
302
- } else if (viewType === ViewType.Week) {
303
- this.startDate = this.localeDayjs(new Date(date)).startOf('isoWeek');
304
- this.endDate = this.localeDayjs(new Date(this.startDate)).endOf('isoWeek');
305
- } else if (viewType === ViewType.Month) {
306
- this.startDate = this.localeDayjs(new Date(date)).startOf('month');
307
- this.endDate = this.localeDayjs(new Date(this.startDate)).endOf('month');
308
- } else if (viewType === ViewType.Quarter) {
309
- this.startDate = this.localeDayjs(new Date(date)).startOf('quarter');
310
- this.endDate = this.localeDayjs(new Date(this.startDate)).endOf('quarter');
311
- }
312
295
  }
313
- this.viewType = viewType;
314
- }
315
- this._shouldReloadViewType = false;
316
- this.events = [];
317
- this._createHeaders();
318
- this._createRenderData();
319
- this.setScrollToSpecialDayjs(true);
320
- this.bumpVersion();
321
- }
322
- }
323
- setSchedulerMaxHeight(newSchedulerMaxHeight) {
324
- if (newSchedulerMaxHeight < 0) {
325
- return;
326
- }
327
- if (this.config.schedulerMaxHeight !== newSchedulerMaxHeight) {
328
- this.config.schedulerMaxHeight = newSchedulerMaxHeight;
329
- this.bumpVersion();
330
- }
331
- }
332
- isSchedulerResponsive() {
333
- return !!this.config.schedulerWidth.endsWith && this.config.schedulerWidth.endsWith('%');
334
- }
335
- toggleExpandStatus(slotId) {
336
- let slotEntered = false;
337
- let slotIndent = -1;
338
- let isExpanded = false;
339
- let changed = false;
340
- const expandedMap = new Map();
341
- this.renderData.forEach(item => {
342
- if (slotEntered === false) {
343
- if (item.slotId === slotId && item.hasChildren) {
344
- slotEntered = true;
345
- isExpanded = !item.expanded;
346
- item.expanded = isExpanded;
347
- changed = true;
348
- slotIndent = item.indent;
349
- expandedMap.set(item.indent, {
350
- expanded: item.expanded,
351
- render: item.render
352
- });
296
+ // If showAgenda or isEventPerspective changed, force reload
297
+ if (previousShowAgenda !== showAgenda || previousIsEventPerspective !== isEventPerspective) {
298
+ this._shouldReloadViewType = true;
299
+ }
300
+ if (this.viewType !== viewType || this._shouldReloadViewType) {
301
+ let date = this.startDate;
302
+ if (viewType === ViewType.Custom || viewType === ViewType.Custom1 || viewType === ViewType.Custom2) {
303
+ this.viewType = viewType;
304
+ this._resolveDate(0, date);
305
+ }
306
+ else {
307
+ if (this.viewType < viewType) {
308
+ if (viewType === ViewType.Week) {
309
+ this.startDate = this.localeDayjs(toDate(date)).startOf('isoWeek');
310
+ this.endDate = this.localeDayjs(toDate(this.startDate)).endOf('isoWeek');
311
+ }
312
+ else if (viewType === ViewType.Month) {
313
+ this.startDate = this.localeDayjs(toDate(date)).startOf('month');
314
+ this.endDate = this.localeDayjs(toDate(this.startDate)).endOf('month');
315
+ }
316
+ else if (viewType === ViewType.Quarter) {
317
+ this.startDate = this.localeDayjs(toDate(date)).startOf('quarter');
318
+ this.endDate = this.localeDayjs(toDate(this.startDate)).endOf('quarter');
319
+ }
320
+ else if (viewType === ViewType.Year) {
321
+ this.startDate = this.localeDayjs(toDate(date)).startOf('year');
322
+ this.endDate = this.localeDayjs(toDate(this.startDate)).endOf('year');
323
+ }
324
+ }
325
+ else {
326
+ const start = this.localeDayjs(toDate(this.startDate));
327
+ const end = this.localeDayjs(toDate(this.endDate)).add(1, 'days');
328
+ if (this.selectDate !== undefined) {
329
+ const selectDate = this.localeDayjs(toDate(this.selectDate));
330
+ if (selectDate >= start && selectDate < end) {
331
+ date = this.selectDate;
332
+ }
333
+ }
334
+ const now = this.localeDayjs();
335
+ if (now >= start && now < end) {
336
+ date = now.startOf('day');
337
+ }
338
+ if (viewType === ViewType.Day || viewType === ViewType.VerticalResource) {
339
+ this.startDate = this.localeDayjs(toDate(date)).startOf('day');
340
+ this.endDate = this.startDate;
341
+ this.cellUnit = CellUnit.Hour;
342
+ }
343
+ else if (viewType === ViewType.Week) {
344
+ this.startDate = this.localeDayjs(toDate(date)).startOf('isoWeek');
345
+ this.endDate = this.localeDayjs(toDate(this.startDate)).endOf('isoWeek');
346
+ }
347
+ else if (viewType === ViewType.Month) {
348
+ this.startDate = this.localeDayjs(toDate(date)).startOf('month');
349
+ this.endDate = this.localeDayjs(toDate(this.startDate)).endOf('month');
350
+ }
351
+ else if (viewType === ViewType.Quarter) {
352
+ this.startDate = this.localeDayjs(toDate(date)).startOf('quarter');
353
+ this.endDate = this.localeDayjs(toDate(this.startDate)).endOf('quarter');
354
+ }
355
+ }
356
+ this.viewType = viewType;
357
+ }
358
+ this._shouldReloadViewType = false;
359
+ this.events = [];
360
+ this._createHeaders();
361
+ this._createRenderData();
362
+ this.setScrollToSpecialDayjs(true);
363
+ this.bumpVersion();
353
364
  }
354
- } else if (item.indent > slotIndent) {
355
- const expandStatus = expandedMap.get(item.indent - 1);
356
- const newRender = expandStatus.expanded && expandStatus.render;
357
- if (item.render !== newRender) {
358
- item.render = newRender;
359
- changed = true;
365
+ }
366
+ setSchedulerMaxHeight(newSchedulerMaxHeight) {
367
+ if (newSchedulerMaxHeight < 0) {
368
+ return;
360
369
  }
361
- if (item.hasChildren) {
362
- expandedMap.set(item.indent, {
363
- expanded: item.expanded,
364
- render: item.render
365
- });
370
+ if (this.config.schedulerMaxHeight !== newSchedulerMaxHeight) {
371
+ this.config.schedulerMaxHeight = newSchedulerMaxHeight;
372
+ this.bumpVersion();
366
373
  }
367
- } else {
368
- slotEntered = false;
369
- }
370
- });
371
- if (changed) {
372
- this.bumpVersion();
373
- }
374
- }
375
- isResourceViewResponsive() {
376
- const resourceTableWidth = this.getResourceTableConfigWidth();
377
- return !!resourceTableWidth.endsWith && resourceTableWidth.endsWith('%');
378
- }
379
- isContentViewResponsive() {
380
- const contentCellWidth = this.getContentCellConfigWidth();
381
- return !!contentCellWidth.endsWith && contentCellWidth.endsWith('%');
382
- }
383
- getSchedulerWidth() {
384
- const baseWidth = this.documentWidth > 0 ? this.documentWidth : 0;
385
- return this.isSchedulerResponsive() ? parseInt(baseWidth * Number(this.config.schedulerWidth.slice(0, -1)) / 100, 10) : this.config.schedulerWidth;
386
- }
387
- getSchedulerHeight() {
388
- // Use the header height tracked in SchedulerData (set via _setSchedulerHeaderHeight)
389
- const headerHeight = this.schedulerHeaderHeight || 0;
390
- const underneathHeight = this.config.underneathHeight || 0;
391
- const baseHeight = this.documentHeight - headerHeight - this.config.tableHeaderHeight - underneathHeight;
392
- const validBaseHeight = baseHeight > 0 ? baseHeight : 0;
393
- if (this.isSchedulerResponsive() && this.config.schedulerHeight?.endsWith?.('%')) {
394
- return parseInt(validBaseHeight * Number(this.config.schedulerHeight.slice(0, -1)) / 100, 10);
395
- }
396
- // fallback to max height if set, else just use base height
397
- return this.config.schedulerMaxHeight || validBaseHeight;
398
- }
399
-
400
- // configured resource column width in px (config may be a percentage of the scheduler width)
401
- _getConfiguredResourceTableWidth() {
402
- const resourceTableConfigWidth = this.getResourceTableConfigWidth();
403
- return this.isResourceViewResponsive() ? parseInt(this.getSchedulerWidth() * Number(resourceTableConfigWidth.slice(0, -1)) / 100, 10) : resourceTableConfigWidth;
404
- }
405
- getResourceTableWidth() {
406
- const schedulerWidth = this.getSchedulerWidth();
407
- let resourceTableWidth = this._getConfiguredResourceTableWidth();
408
- // only the few px left after the cells are stretched go to the resource column
409
- if (this.isSchedulerResponsive() && this.getContentTableWidth() + resourceTableWidth < schedulerWidth) resourceTableWidth = schedulerWidth - this.getContentTableWidth();
410
- return resourceTableWidth;
411
- }
412
- getContentCellWidth() {
413
- const contentCellConfigWidth = this.getContentCellConfigWidth();
414
- const schedulerWidth = this.getSchedulerWidth();
415
- const cellWidth = this.isContentViewResponsive() ? parseInt(schedulerWidth * Number(contentCellConfigWidth.slice(0, -1)) / 100, 10) : contentCellConfigWidth;
416
-
417
- // stretch the cells to fill the container instead of widening the resource column
418
- if (this.isSchedulerResponsive() && !this.showAgenda && this.headers?.length) {
419
- const resourceWidth = this.config.resourceViewEnabled === false ? 0 : this._getConfiguredResourceTableWidth();
420
- const fillWidth = Math.floor((schedulerWidth - resourceWidth) / this.headers.length);
421
- return Math.max(cellWidth, fillWidth);
422
- }
423
- return cellWidth;
424
- }
425
- getContentTableWidth() {
426
- return this.headers.length * this.getContentCellWidth();
427
- }
428
- getScrollToSpecialDayjs() {
429
- if (this.config.scrollToSpecialDayjsEnabled) return this.scrollToSpecialDayjs;
430
- return false;
431
- }
432
- getSlots() {
433
- return this.isEventPerspective ? this.eventGroups : this.resources;
434
- }
435
- getSlotById(slotId) {
436
- const slots = this.getSlots();
437
- let slot;
438
- slots.forEach(item => {
439
- if (item.id === slotId) slot = item;
440
- });
441
- return slot;
442
- }
443
- getResourceById(resourceId) {
444
- let resource;
445
- this.resources.forEach(item => {
446
- if (item.id === resourceId) resource = item;
447
- });
448
- return resource;
449
- }
450
-
451
- // height of the optional group rows (month / week number) rendered above the main header row
452
- getHeaderGroupRowsHeight() {
453
- const {
454
- showMonthRow,
455
- monthRowHeight = 24,
456
- showWeekNumber,
457
- weekNumberRowHeight = 24
458
- } = this.config;
459
- return (showMonthRow ? monthRowHeight : 0) + (showWeekNumber ? weekNumberRowHeight : 0);
460
- }
461
- getTableHeaderHeight() {
462
- return this.config.tableHeaderHeight;
463
- }
464
- getSchedulerContentDesiredHeight() {
465
- let height = 0;
466
- this.renderData.forEach(item => {
467
- if (item.render) height += item.rowHeight;
468
- });
469
- return height;
470
- }
471
- getCellMaxEvents() {
472
- const viewConfigMap = {
473
- [ViewType.Week]: 'weekMaxEvents',
474
- [ViewType.Day]: 'dayMaxEvents',
475
- [ViewType.Month]: 'monthMaxEvents',
476
- [ViewType.Year]: 'yearMaxEvents',
477
- [ViewType.Quarter]: 'quarterMaxEvents'
478
- };
479
- const configProperty = viewConfigMap[this.viewType] || 'customMaxEvents';
480
- return this.config[configProperty];
481
- }
482
- getCalendarPopoverLocale() {
483
- return this.calendarPopoverLocale;
484
- }
485
- getSelectedDate() {
486
- return this.selectDate.format(DATE_FORMAT);
487
- }
488
- getViewStartDate() {
489
- return this.startDate;
490
- }
491
- getViewEndDate() {
492
- return this.endDate;
493
- }
494
- getViewDates() {
495
- return {
496
- startDate: this.startDate,
497
- endDate: this.endDate
498
- };
499
- }
500
- getDateLabel() {
501
- const start = this.localeDayjs(new Date(this.startDate));
502
- const end = this.localeDayjs(new Date(this.endDate));
503
- let dateLabel = start.format('LL');
504
- if (start !== end) dateLabel = `${start.format('LL')}-${end.format('LL')}`;
505
- if (this.behaviors.getDateLabelFunc) dateLabel = this.behaviors.getDateLabelFunc(this, this.viewType, this.startDate, this.endDate);
506
- return dateLabel;
507
- }
508
- addEvent(newEvent) {
509
- this._attachEvent(newEvent);
510
- if (this.eventGroupsAutoGenerated) this._generateEventGroups();
511
- this._createRenderData();
512
- this.bumpVersion();
513
- }
514
- updateEventStart(event, newStart) {
515
- this._detachEvent(event);
516
- event.start = newStart;
517
- this._attachEvent(event);
518
- this._createRenderData();
519
- this.bumpVersion();
520
- }
521
- updateEventEnd(event, newEnd) {
522
- event.end = newEnd;
523
- this._createRenderData();
524
- this.bumpVersion();
525
- }
526
- swapEvent(eventSource, eventDest) {
527
- // Swap group or resource IDs
528
- if (this.isEventPerspective) {
529
- [eventSource.groupId, eventDest.groupId] = [eventDest.groupId, eventSource.groupId];
530
- [eventSource.groupName, eventDest.groupName] = [eventDest.groupName, eventSource.groupName];
531
- } else {
532
- [eventSource.resourceId, eventDest.resourceId] = [eventDest.resourceId, eventSource.resourceId];
533
- }
534
-
535
- // Swap start and end times
536
- [eventSource.start, eventDest.start] = [eventDest.start, eventSource.start];
537
- [eventSource.end, eventDest.end] = [eventDest.end, eventSource.end];
538
-
539
- // Update the events
540
- this._detachEvent(eventSource);
541
- this._detachEvent(eventDest);
542
- this._attachEvent(eventSource);
543
- this._attachEvent(eventDest);
544
- this._createRenderData();
545
- this.bumpVersion();
546
- }
547
- swapEvent2(eventSource, eventDest) {
548
- const tempEventSource = {
549
- ...eventSource
550
- };
551
- const tempEventDest = {
552
- ...eventDest
553
- };
554
- this._detachEvent(eventSource);
555
- this._detachEvent(eventDest);
556
- if (this.isEventPerspective) {
557
- tempEventSource.groupId = eventDest.groupId;
558
- tempEventSource.groupName = eventDest.groupName;
559
- tempEventDest.groupId = eventSource.groupId;
560
- tempEventDest.groupName = eventSource.groupName;
561
- } else {
562
- tempEventSource.resourceId = eventDest.resourceId;
563
- tempEventDest.resourceId = eventSource.resourceId;
564
- }
565
- tempEventSource.end = eventDest.end;
566
- tempEventSource.start = eventDest.start;
567
- tempEventDest.end = eventSource.end;
568
- tempEventDest.start = eventSource.start;
569
- this._attachEvent(tempEventSource);
570
- this._attachEvent(tempEventDest);
571
- this._createRenderData();
572
- this.bumpVersion();
573
- }
574
- moveEvent(event, newSlotId, newSlotName, newStart, newEnd) {
575
- this._detachEvent(event);
576
- if (this.isEventPerspective) {
577
- event.groupId = newSlotId;
578
- event.groupName = newSlotName;
579
- } else event.resourceId = newSlotId;
580
- event.end = newEnd;
581
- event.start = newStart;
582
- this._attachEvent(event);
583
- this._createRenderData();
584
- this.bumpVersion();
585
- }
586
- isEventInTimeWindow(eventStart, eventEnd, windowStart, windowEnd) {
587
- return eventStart < windowEnd && eventEnd > windowStart;
588
- }
589
- removeEvent(event) {
590
- const index = this.events.indexOf(event);
591
- if (index !== -1) {
592
- this.events.splice(index, 1);
593
- this._createRenderData();
594
- this.bumpVersion();
595
- }
596
- }
597
- removeEventById(eventId) {
598
- let index = -1;
599
- this.events.forEach((item, idx) => {
600
- if (item.id === eventId) index = idx;
601
- });
602
- if (index !== -1) {
603
- this.events.splice(index, 1);
604
- this._createRenderData();
605
- this.bumpVersion();
606
- }
607
- }
608
- getResourceTableConfigWidth() {
609
- if (this.showAgenda) {
610
- return this.config.agendaResourceTableWidth;
611
- }
612
- const viewConfigMap = {
613
- [ViewType.Week]: 'weekResourceTableWidth',
614
- [ViewType.Day]: 'dayResourceTableWidth',
615
- [ViewType.Month]: 'monthResourceTableWidth',
616
- [ViewType.Year]: 'yearResourceTableWidth',
617
- [ViewType.Quarter]: 'quarterResourceTableWidth',
618
- [ViewType.VerticalResource]: 'customResourceTableWidth'
619
- };
620
- const configProperty = viewConfigMap[this.viewType] || 'customResourceTableWidth';
621
- return this.config[configProperty];
622
- }
623
- getContentCellConfigWidth() {
624
- const viewConfigMap = {
625
- [ViewType.Week]: 'weekCellWidth',
626
- [ViewType.Day]: 'dayCellWidth',
627
- [ViewType.Month]: 'monthCellWidth',
628
- [ViewType.Year]: 'yearCellWidth',
629
- [ViewType.Quarter]: 'quarterCellWidth',
630
- [ViewType.VerticalResource]: 'customCellWidth'
631
- };
632
- const configProperty = viewConfigMap[this.viewType] || 'customCellWidth';
633
- return this.config[configProperty];
634
- }
635
- _setDocumentWidth(documentWidth) {
636
- if (documentWidth >= 0 && documentWidth !== this.documentWidth) {
637
- this.documentWidth = documentWidth;
638
- this.bumpVersion();
639
- }
640
- }
641
- _setDocumentHeight(documentHeight) {
642
- if (documentHeight >= 0 && documentHeight !== this.documentHeight) {
643
- this.documentHeight = documentHeight;
644
- this.bumpVersion();
645
- }
646
- }
647
- _setSchedulerHeaderHeight(schedulerHeaderHeight) {
648
- if (schedulerHeaderHeight >= 0 && schedulerHeaderHeight !== this.schedulerHeaderHeight) {
649
- this.schedulerHeaderHeight = schedulerHeaderHeight;
650
- this.bumpVersion();
651
- }
652
- }
653
- _detachEvent(event) {
654
- const index = this.events.indexOf(event);
655
- if (index !== -1) this.events.splice(index, 1);
656
- }
657
- _attachEvent(event) {
658
- let pos = 0;
659
- const eventStart = this.localeDayjs(new Date(event.start));
660
- this.events.forEach((item, index) => {
661
- const start = this.localeDayjs(new Date(item.start));
662
- if (eventStart >= start) pos = index + 1;
663
- });
664
- this.events.splice(pos, 0, event);
665
- }
666
- _handleRecurringEvents() {
667
- const recurringEvents = this.events.filter(x => !!x.rrule);
668
- recurringEvents.forEach(item => {
669
- this._detachEvent(item);
670
- });
671
- recurringEvents.forEach(item => {
672
- const windowStart = this.startDate;
673
- const windowEnd = this.endDate.add(1, 'days');
674
- const oldStart = this.localeDayjs(new Date(item.start));
675
- const oldEnd = this.localeDayjs(new Date(item.end));
676
- let rule = rrulestr(item.rrule);
677
- let oldDtstart;
678
- const oldUntil = rule.origOptions.until || windowEnd.toDate();
679
- if (rule.origOptions.dtstart) {
680
- oldDtstart = this.localeDayjs(new Date(rule.origOptions.dtstart));
681
- }
682
- // rule.origOptions.dtstart = oldStart.toDate();
683
- if (windowEnd < oldUntil) {
684
- rule.origOptions.until = windowEnd.toDate();
685
- }
686
-
687
- // reload
688
- rule = rrulestr(rule.toString());
689
- if (item.exdates || item.exrule) {
690
- const rruleSet = new RRuleSet();
691
- rruleSet.rrule(rule);
692
- if (item.exrule) {
693
- rruleSet.exrule(rrulestr(item.exrule));
374
+ }
375
+ isSchedulerResponsive() {
376
+ return isPercent(this.config.schedulerWidth);
377
+ }
378
+ toggleExpandStatus(slotId) {
379
+ let slotEntered = false;
380
+ let slotIndent = -1;
381
+ let isExpanded = false;
382
+ let changed = false;
383
+ const expandedMap = new Map();
384
+ this.renderData.forEach(item => {
385
+ if (slotEntered === false) {
386
+ if (item.slotId === slotId && item.hasChildren) {
387
+ slotEntered = true;
388
+ isExpanded = !item.expanded;
389
+ item.expanded = isExpanded;
390
+ changed = true;
391
+ slotIndent = item.indent;
392
+ expandedMap.set(item.indent, {
393
+ expanded: item.expanded,
394
+ render: item.render,
395
+ });
396
+ }
397
+ }
398
+ else if (item.indent > slotIndent) {
399
+ const expandStatus = expandedMap.get(item.indent - 1);
400
+ const newRender = expandStatus.expanded && expandStatus.render;
401
+ if (item.render !== newRender) {
402
+ item.render = newRender;
403
+ changed = true;
404
+ }
405
+ if (item.hasChildren) {
406
+ expandedMap.set(item.indent, {
407
+ expanded: item.expanded,
408
+ render: item.render,
409
+ });
410
+ }
411
+ }
412
+ else {
413
+ slotEntered = false;
414
+ }
415
+ });
416
+ if (changed) {
417
+ this.bumpVersion();
694
418
  }
695
- if (item.exdates) {
696
- item.exdates.forEach(exdate => {
697
- rruleSet.exdate(this.localeDayjs(exdate).toDate());
698
- });
419
+ }
420
+ isResourceViewResponsive() {
421
+ return isPercent(this.getResourceTableConfigWidth());
422
+ }
423
+ isContentViewResponsive() {
424
+ return isPercent(this.getContentCellConfigWidth());
425
+ }
426
+ getSchedulerWidth() {
427
+ const baseWidth = this.documentWidth > 0 ? this.documentWidth : 0;
428
+ return this.isSchedulerResponsive()
429
+ ? percentOf(baseWidth, this.config.schedulerWidth)
430
+ : asPx(this.config.schedulerWidth);
431
+ }
432
+ getSchedulerHeight() {
433
+ // Use the header height tracked in SchedulerData (set via _setSchedulerHeaderHeight)
434
+ const headerHeight = this.schedulerHeaderHeight || 0;
435
+ const underneathHeight = this.config.underneathHeight || 0;
436
+ const baseHeight = this.documentHeight - headerHeight - this.config.tableHeaderHeight - underneathHeight;
437
+ const validBaseHeight = baseHeight > 0 ? baseHeight : 0;
438
+ const { schedulerHeight } = this.config;
439
+ if (this.isSchedulerResponsive() && schedulerHeight !== undefined && isPercent(schedulerHeight)) {
440
+ return percentOf(validBaseHeight, schedulerHeight);
699
441
  }
700
- rule = rruleSet;
701
- }
702
- const all = rule.between(new Date(windowStart), new Date(windowEnd));
703
- all.forEach((time, index) => {
704
- const newEvent = {
705
- ...item,
706
- recurringEventId: item.id,
707
- recurringEventStart: item.start,
708
- recurringEventEnd: item.end,
709
- id: `${item.id}-${index}`,
710
- start: rule.origOptions.tzid ? this.localeDayjs.utc(time).utcOffset(this.localeDayjs(new Date()).utcOffset(), true).format(DATETIME_FORMAT) : this.localeDayjs(new Date(time)).format(DATETIME_FORMAT),
711
- end: rule.origOptions.tzid ? this.localeDayjs.utc(time).utcOffset(this.localeDayjs(new Date()).utcOffset(), true).add(oldEnd.diff(oldStart), 'ms').add(this.localeDayjs(new Date(oldUntil)).utcOffset() - this.localeDayjs(new Date(item.start)).utcOffset(), 'm').format(DATETIME_FORMAT) : this.localeDayjs(new Date(time)).add(oldEnd.diff(oldStart), 'ms').format(DATETIME_FORMAT)
442
+ // fallback to max height if set, else just use base height
443
+ return this.config.schedulerMaxHeight || validBaseHeight;
444
+ }
445
+ // configured resource column width in px (config may be a percentage of the scheduler width)
446
+ _getConfiguredResourceTableWidth() {
447
+ const resourceTableConfigWidth = this.getResourceTableConfigWidth();
448
+ return this.isResourceViewResponsive()
449
+ ? percentOf(this.getSchedulerWidth(), resourceTableConfigWidth)
450
+ : asPx(resourceTableConfigWidth);
451
+ }
452
+ getResourceTableWidth() {
453
+ const schedulerWidth = this.getSchedulerWidth();
454
+ let resourceTableWidth = this._getConfiguredResourceTableWidth();
455
+ // only the few px left after the cells are stretched go to the resource column
456
+ if (this.isSchedulerResponsive() && this.getContentTableWidth() + resourceTableWidth < schedulerWidth)
457
+ resourceTableWidth = schedulerWidth - this.getContentTableWidth();
458
+ return resourceTableWidth;
459
+ }
460
+ getContentCellWidth() {
461
+ const contentCellConfigWidth = this.getContentCellConfigWidth();
462
+ const schedulerWidth = this.getSchedulerWidth();
463
+ const cellWidth = this.isContentViewResponsive()
464
+ ? percentOf(schedulerWidth, contentCellConfigWidth)
465
+ : asPx(contentCellConfigWidth);
466
+ // stretch the cells to fill the container instead of widening the resource column
467
+ if (this.isSchedulerResponsive() && !this.showAgenda && this.headers?.length) {
468
+ const resourceWidth = this.config.resourceViewEnabled === false ? 0 : this._getConfiguredResourceTableWidth();
469
+ const fillWidth = Math.floor((schedulerWidth - resourceWidth) / this.headers.length);
470
+ return Math.max(cellWidth, fillWidth);
471
+ }
472
+ return cellWidth;
473
+ }
474
+ getContentTableWidth() {
475
+ return this.headers.length * this.getContentCellWidth();
476
+ }
477
+ getScrollToSpecialDayjs() {
478
+ if (this.config.scrollToSpecialDayjsEnabled)
479
+ return this.scrollToSpecialDayjs;
480
+ return false;
481
+ }
482
+ getSlots() {
483
+ return this.isEventPerspective ? this.eventGroups : this.resources;
484
+ }
485
+ getSlotById(slotId) {
486
+ const slots = this.getSlots();
487
+ let slot = undefined;
488
+ slots.forEach(item => {
489
+ if (item.id === slotId)
490
+ slot = item;
491
+ });
492
+ return slot;
493
+ }
494
+ getResourceById(resourceId) {
495
+ let resource = undefined;
496
+ this.resources.forEach(item => {
497
+ if (item.id === resourceId)
498
+ resource = item;
499
+ });
500
+ return resource;
501
+ }
502
+ // height of the optional group rows (month / week number) rendered above the main header row
503
+ getHeaderGroupRowsHeight() {
504
+ const { showMonthRow, monthRowHeight = 24, showWeekNumber, weekNumberRowHeight = 24 } = this.config;
505
+ return (showMonthRow ? monthRowHeight : 0) + (showWeekNumber ? weekNumberRowHeight : 0);
506
+ }
507
+ getTableHeaderHeight() {
508
+ return this.config.tableHeaderHeight;
509
+ }
510
+ getSchedulerContentDesiredHeight() {
511
+ let height = 0;
512
+ this.renderData.forEach(item => {
513
+ if (item.render)
514
+ height += item.rowHeight;
515
+ });
516
+ return height;
517
+ }
518
+ getCellMaxEvents() {
519
+ const viewConfigMap = {
520
+ [ViewType.Week]: 'weekMaxEvents',
521
+ [ViewType.Day]: 'dayMaxEvents',
522
+ [ViewType.Month]: 'monthMaxEvents',
523
+ [ViewType.Year]: 'yearMaxEvents',
524
+ [ViewType.Quarter]: 'quarterMaxEvents',
712
525
  };
713
- const eventStart = normalizeEventStart(newEvent.start);
714
- const eventEnd = normalizeEventEnd(newEvent.end);
715
- if (this.isEventInTimeWindow(eventStart, eventEnd, windowStart, windowEnd) && (!oldDtstart || eventStart >= oldDtstart)) {
716
- this._attachEvent(newEvent);
526
+ const configProperty = viewConfigMap[this.viewType] || 'customMaxEvents';
527
+ return this.config[configProperty];
528
+ }
529
+ getSelectedDate() {
530
+ return this.selectDate.format(DATE_FORMAT);
531
+ }
532
+ getViewStartDate() {
533
+ return this.startDate;
534
+ }
535
+ getViewEndDate() {
536
+ return this.endDate;
537
+ }
538
+ getViewDates() {
539
+ return {
540
+ startDate: this.startDate,
541
+ endDate: this.endDate,
542
+ };
543
+ }
544
+ getDateLabel() {
545
+ const start = this.localeDayjs(toDate(this.startDate));
546
+ const end = this.localeDayjs(toDate(this.endDate));
547
+ let dateLabel = start.format('LL');
548
+ if (start !== end)
549
+ dateLabel = `${start.format('LL')}-${end.format('LL')}`;
550
+ if (this.behaviors.getDateLabelFunc)
551
+ dateLabel = this.behaviors.getDateLabelFunc(this, this.viewType, this.startDate, this.endDate);
552
+ return dateLabel;
553
+ }
554
+ addEvent(newEvent) {
555
+ this._attachEvent(newEvent);
556
+ if (this.eventGroupsAutoGenerated)
557
+ this._generateEventGroups();
558
+ this._createRenderData();
559
+ this.bumpVersion();
560
+ }
561
+ updateEventStart(event, newStart) {
562
+ this._detachEvent(event);
563
+ event.start = newStart;
564
+ this._attachEvent(event);
565
+ this._createRenderData();
566
+ this.bumpVersion();
567
+ }
568
+ updateEventEnd(event, newEnd) {
569
+ event.end = newEnd;
570
+ this._createRenderData();
571
+ this.bumpVersion();
572
+ }
573
+ swapEvent(eventSource, eventDest) {
574
+ // Swap group or resource IDs
575
+ if (this.isEventPerspective) {
576
+ [eventSource.groupId, eventDest.groupId] = [eventDest.groupId, eventSource.groupId];
577
+ [eventSource.groupName, eventDest.groupName] = [eventDest.groupName, eventSource.groupName];
717
578
  }
718
- });
719
- });
720
- }
721
- _resolveDate(num, date = undefined) {
722
- if (date !== undefined) {
723
- this.selectDate = this.localeDayjs(date);
724
- }
725
- const setStartAndEndDates = unit => {
726
- this.startDate = date !== undefined ? this.selectDate.startOf(unit) : this.startDate.add(num, `${unit}s`);
727
- this.endDate = this.startDate.endOf(unit);
728
- };
729
- switch (this.viewType) {
730
- case ViewType.Week:
731
- if (date !== undefined) {
732
- this.startDate = this.selectDate.startOf('isoWeek');
733
- } else {
734
- this.startDate = this.startDate.add(num, 'weeks');
579
+ else {
580
+ [eventSource.resourceId, eventDest.resourceId] = [eventDest.resourceId, eventSource.resourceId];
735
581
  }
736
- this.endDate = this.startDate.endOf('isoWeek');
737
- break;
738
- case ViewType.Day:
739
- case ViewType.VerticalResource:
740
- this.startDate = (date !== undefined ? this.selectDate : this.startDate.add(num, 'days')).startOf('day');
741
- this.endDate = this.startDate;
742
- break;
743
- case ViewType.Month:
744
- setStartAndEndDates('month');
745
- break;
746
- case ViewType.Quarter:
747
- setStartAndEndDates('quarter');
748
- break;
749
- case ViewType.Year:
750
- setStartAndEndDates('year');
751
- break;
752
- case ViewType.Custom:
753
- case ViewType.Custom1:
754
- case ViewType.Custom2:
755
- if (this.behaviors.getCustomDateFunc !== undefined) {
756
- const customDate = this.behaviors.getCustomDateFunc(this, num, date);
757
- this.startDate = this.localeDayjs(customDate.startDate);
758
- this.endDate = this.localeDayjs(customDate.endDate);
759
- if (customDate.cellUnit) {
760
- this.cellUnit = customDate.cellUnit;
761
- }
762
- } else {
763
- throw new Error('This is a custom view type, set behaviors.getCustomDateFunc func ' + 'to resolve the time window (startDate and endDate) yourself');
582
+ // Swap start and end times
583
+ [eventSource.start, eventDest.start] = [eventDest.start, eventSource.start];
584
+ [eventSource.end, eventDest.end] = [eventDest.end, eventSource.end];
585
+ // Update the events
586
+ this._detachEvent(eventSource);
587
+ this._detachEvent(eventDest);
588
+ this._attachEvent(eventSource);
589
+ this._attachEvent(eventDest);
590
+ this._createRenderData();
591
+ this.bumpVersion();
592
+ }
593
+ swapEvent2(eventSource, eventDest) {
594
+ const tempEventSource = { ...eventSource };
595
+ const tempEventDest = { ...eventDest };
596
+ this._detachEvent(eventSource);
597
+ this._detachEvent(eventDest);
598
+ if (this.isEventPerspective) {
599
+ tempEventSource.groupId = eventDest.groupId;
600
+ tempEventSource.groupName = eventDest.groupName;
601
+ tempEventDest.groupId = eventSource.groupId;
602
+ tempEventDest.groupName = eventSource.groupName;
764
603
  }
765
- break;
766
- default:
767
- break;
768
- }
769
- }
770
- _createHeaders() {
771
- const timeSlots = [];
772
- let start = this.localeDayjs(new Date(this.startDate));
773
- let end = this.localeDayjs(new Date(this.endDate));
774
- let header = start;
775
- const width = this.getContentCellWidth();
776
- if (this.showAgenda) {
777
- timeSlots.push({
778
- time: header.format(DATETIME_FORMAT),
779
- nonWorkingTime: false
780
- });
781
- } else if (this.cellUnit === CellUnit.Hour) {
782
- if (start.hour() === 0) {
783
- start = start.hour(this.config.dayStartFrom);
784
- }
785
- if (end.hour() === 0) {
786
- end = end.hour(this.config.dayStopTo);
787
- }
788
- header = start;
789
- let prevHour = -1;
790
- while (header >= start && header <= end) {
791
- // prevent doubled hours on time change
792
- if (header.hour() === prevHour) {
793
- header = header.add(1, 'hours');
794
- continue;
604
+ else {
605
+ tempEventSource.resourceId = eventDest.resourceId;
606
+ tempEventDest.resourceId = eventSource.resourceId;
795
607
  }
796
- prevHour = header.hour();
797
- const minuteSteps = this.getMinuteStepsInHour();
798
- for (let i = 0; i < minuteSteps; i += 1) {
799
- const hour = header.hour();
800
- if (hour >= this.config.dayStartFrom && hour <= this.config.dayStopTo) {
801
- const time = header.format(DATETIME_FORMAT);
802
- const nonWorkingTime = this.behaviors.isNonWorkingTimeFunc(this, time);
803
- timeSlots.push({
804
- time,
805
- nonWorkingTime
806
- });
807
- }
808
- header = header.add(this.config.minuteStep, 'minutes');
608
+ tempEventSource.end = eventDest.end;
609
+ tempEventSource.start = eventDest.start;
610
+ tempEventDest.end = eventSource.end;
611
+ tempEventDest.start = eventSource.start;
612
+ this._attachEvent(tempEventSource);
613
+ this._attachEvent(tempEventDest);
614
+ this._createRenderData();
615
+ this.bumpVersion();
616
+ }
617
+ moveEvent(event, newSlotId, newSlotName, newStart, newEnd) {
618
+ this._detachEvent(event);
619
+ if (this.isEventPerspective) {
620
+ event.groupId = newSlotId;
621
+ event.groupName = newSlotName;
622
+ }
623
+ else
624
+ event.resourceId = newSlotId;
625
+ event.end = newEnd;
626
+ event.start = newStart;
627
+ this._attachEvent(event);
628
+ this._createRenderData();
629
+ this.bumpVersion();
630
+ }
631
+ isEventInTimeWindow(eventStart, eventEnd, windowStart, windowEnd) {
632
+ return eventStart < windowEnd && eventEnd > windowStart;
633
+ }
634
+ removeEvent(event) {
635
+ const index = this.events.indexOf(event);
636
+ if (index !== -1) {
637
+ this.events.splice(index, 1);
638
+ this._createRenderData();
639
+ this.bumpVersion();
809
640
  }
810
- }
811
- } else if (this.cellUnit === CellUnit.Day) {
812
- while (header >= start && header <= end) {
813
- const time = header.format(DATETIME_FORMAT);
814
- const style = {
815
- width,
816
- minWidth: width
641
+ }
642
+ removeEventById(eventId) {
643
+ let index = -1;
644
+ this.events.forEach((item, idx) => {
645
+ if (item.id === eventId)
646
+ index = idx;
647
+ });
648
+ if (index !== -1) {
649
+ this.events.splice(index, 1);
650
+ this._createRenderData();
651
+ this.bumpVersion();
652
+ }
653
+ }
654
+ getResourceTableConfigWidth() {
655
+ if (this.showAgenda) {
656
+ return this.config.agendaResourceTableWidth;
657
+ }
658
+ const viewConfigMap = {
659
+ [ViewType.Week]: 'weekResourceTableWidth',
660
+ [ViewType.Day]: 'dayResourceTableWidth',
661
+ [ViewType.Month]: 'monthResourceTableWidth',
662
+ [ViewType.Year]: 'yearResourceTableWidth',
663
+ [ViewType.Quarter]: 'quarterResourceTableWidth',
664
+ [ViewType.VerticalResource]: 'customResourceTableWidth',
817
665
  };
818
- const dayOfWeek = header.day();
819
- if (this.config.displayWeekend || dayOfWeek !== 0 && dayOfWeek !== 6) {
820
- const nonWorkingTime = this.behaviors.isNonWorkingTimeFunc(this, time);
821
- timeSlots.push({
822
- time,
823
- nonWorkingTime,
824
- style
825
- });
666
+ const configProperty = viewConfigMap[this.viewType] || 'customResourceTableWidth';
667
+ return this.config[configProperty];
668
+ }
669
+ getContentCellConfigWidth() {
670
+ const viewConfigMap = {
671
+ [ViewType.Week]: 'weekCellWidth',
672
+ [ViewType.Day]: 'dayCellWidth',
673
+ [ViewType.Month]: 'monthCellWidth',
674
+ [ViewType.Year]: 'yearCellWidth',
675
+ [ViewType.Quarter]: 'quarterCellWidth',
676
+ [ViewType.VerticalResource]: 'customCellWidth',
677
+ };
678
+ const configProperty = viewConfigMap[this.viewType] || 'customCellWidth';
679
+ return this.config[configProperty];
680
+ }
681
+ _setDocumentWidth(documentWidth) {
682
+ if (documentWidth >= 0 && documentWidth !== this.documentWidth) {
683
+ this.documentWidth = documentWidth;
684
+ this.bumpVersion();
685
+ }
686
+ }
687
+ _setDocumentHeight(documentHeight) {
688
+ if (documentHeight >= 0 && documentHeight !== this.documentHeight) {
689
+ this.documentHeight = documentHeight;
690
+ this.bumpVersion();
691
+ }
692
+ }
693
+ _setSchedulerHeaderHeight(schedulerHeaderHeight) {
694
+ if (schedulerHeaderHeight >= 0 && schedulerHeaderHeight !== this.schedulerHeaderHeight) {
695
+ this.schedulerHeaderHeight = schedulerHeaderHeight;
696
+ this.bumpVersion();
826
697
  }
827
- header = header.add(1, 'days');
828
- }
829
- } else if (this.cellUnit === CellUnit.Week) {
830
- while (header >= start && header <= end) {
831
- const time = header.format(DATE_FORMAT);
832
- timeSlots.push({
833
- time
698
+ }
699
+ _detachEvent(event) {
700
+ const index = this.events.indexOf(event);
701
+ if (index !== -1)
702
+ this.events.splice(index, 1);
703
+ }
704
+ _attachEvent(event) {
705
+ let pos = 0;
706
+ const eventStart = this.localeDayjs(new Date(event.start));
707
+ this.events.forEach((item, index) => {
708
+ const start = this.localeDayjs(new Date(item.start));
709
+ if (eventStart >= start)
710
+ pos = index + 1;
834
711
  });
835
- header = header.add(1, 'weeks').startOf('isoWeek');
836
- }
837
- } else if (this.cellUnit === CellUnit.Month) {
838
- while (header >= start && header <= end) {
839
- const time = header.format(DATE_FORMAT);
840
- timeSlots.push({
841
- time
712
+ this.events.splice(pos, 0, event);
713
+ }
714
+ _handleRecurringEvents() {
715
+ const recurringEvents = this.events.filter((x) => !!x.rrule);
716
+ recurringEvents.forEach(item => {
717
+ this._detachEvent(item);
842
718
  });
843
- header = header.add(1, 'months').startOf('month');
844
- }
845
- } else if (this.cellUnit === CellUnit.Quarter) {
846
- while (header >= start && header <= end) {
847
- const time = header.format(DATE_FORMAT);
848
- timeSlots.push({
849
- time
719
+ recurringEvents.forEach(item => {
720
+ const windowStart = this.startDate;
721
+ const windowEnd = this.endDate.add(1, 'days');
722
+ const oldStart = this.localeDayjs(new Date(item.start));
723
+ const oldEnd = this.localeDayjs(new Date(item.end));
724
+ let rule = rrulestr(item.rrule);
725
+ let oldDtstart;
726
+ const oldUntil = rule.origOptions.until || windowEnd.toDate();
727
+ if (rule.origOptions.dtstart) {
728
+ oldDtstart = this.localeDayjs(new Date(rule.origOptions.dtstart));
729
+ }
730
+ // rule.origOptions.dtstart = oldStart.toDate();
731
+ if (windowEnd.valueOf() < oldUntil.valueOf()) {
732
+ rule.origOptions.until = windowEnd.toDate();
733
+ }
734
+ // reload
735
+ rule = rrulestr(rule.toString());
736
+ if (item.exdates || item.exrule) {
737
+ const rruleSet = new RRuleSet();
738
+ rruleSet.rrule(rule);
739
+ if (item.exrule) {
740
+ rruleSet.exrule(rrulestr(item.exrule));
741
+ }
742
+ if (item.exdates) {
743
+ item.exdates.forEach(exdate => {
744
+ rruleSet.exdate(this.localeDayjs(exdate).toDate());
745
+ });
746
+ }
747
+ rule = rruleSet;
748
+ }
749
+ const all = rule.between(toDate(windowStart), toDate(windowEnd));
750
+ all.forEach((time, index) => {
751
+ const newEvent = {
752
+ ...item,
753
+ recurringEventId: item.id,
754
+ recurringEventStart: item.start,
755
+ recurringEventEnd: item.end,
756
+ id: `${item.id}-${index}`,
757
+ start: rule.origOptions.tzid
758
+ ? this.localeDayjs
759
+ .utc(time)
760
+ .utcOffset(this.localeDayjs(new Date()).utcOffset(), true)
761
+ .format(DATETIME_FORMAT)
762
+ : this.localeDayjs(new Date(time)).format(DATETIME_FORMAT),
763
+ end: rule.origOptions.tzid
764
+ ? this.localeDayjs
765
+ .utc(time)
766
+ .utcOffset(this.localeDayjs(new Date()).utcOffset(), true)
767
+ .add(oldEnd.diff(oldStart), 'ms')
768
+ .add(this.localeDayjs(new Date(oldUntil)).utcOffset() - this.localeDayjs(new Date(item.start)).utcOffset(), 'm')
769
+ .format(DATETIME_FORMAT)
770
+ : this.localeDayjs(new Date(time)).add(oldEnd.diff(oldStart), 'ms').format(DATETIME_FORMAT),
771
+ };
772
+ const eventStart = normalizeEventStart(newEvent.start);
773
+ const eventEnd = normalizeEventEnd(newEvent.end);
774
+ if (this.isEventInTimeWindow(eventStart, eventEnd, windowStart, windowEnd) &&
775
+ (!oldDtstart || eventStart >= oldDtstart)) {
776
+ this._attachEvent(newEvent);
777
+ }
778
+ });
850
779
  });
851
- header = header.add(1, 'quarters').startOf('quarter');
852
- }
853
- } else if (this.cellUnit === CellUnit.Year) {
854
- while (header >= start && header <= end) {
855
- const time = header.format(DATE_FORMAT);
856
- timeSlots.push({
857
- time
780
+ }
781
+ _resolveDate(num, date = undefined) {
782
+ if (date !== undefined) {
783
+ this.selectDate = this.localeDayjs(date);
784
+ }
785
+ const setStartAndEndDates = (unit) => {
786
+ this.startDate = date !== undefined ? this.selectDate.startOf(unit) : this.startDate.add(num, `${unit}s`);
787
+ this.endDate = this.startDate.endOf(unit);
788
+ };
789
+ switch (this.viewType) {
790
+ case ViewType.Week:
791
+ if (date !== undefined) {
792
+ this.startDate = this.selectDate.startOf('isoWeek');
793
+ }
794
+ else {
795
+ this.startDate = this.startDate.add(num, 'weeks');
796
+ }
797
+ this.endDate = this.startDate.endOf('isoWeek');
798
+ break;
799
+ case ViewType.Day:
800
+ case ViewType.VerticalResource:
801
+ this.startDate = (date !== undefined ? this.selectDate : this.startDate.add(num, 'days')).startOf('day');
802
+ this.endDate = this.startDate;
803
+ break;
804
+ case ViewType.Month:
805
+ setStartAndEndDates('month');
806
+ break;
807
+ case ViewType.Quarter:
808
+ setStartAndEndDates('quarter');
809
+ break;
810
+ case ViewType.Year:
811
+ setStartAndEndDates('year');
812
+ break;
813
+ case ViewType.Custom:
814
+ case ViewType.Custom1:
815
+ case ViewType.Custom2:
816
+ if (this.behaviors.getCustomDateFunc !== undefined) {
817
+ const customDate = this.behaviors.getCustomDateFunc(this, num, date);
818
+ this.startDate = this.localeDayjs(customDate.startDate);
819
+ this.endDate = this.localeDayjs(customDate.endDate);
820
+ if (customDate.cellUnit) {
821
+ this.cellUnit = customDate.cellUnit;
822
+ }
823
+ }
824
+ else {
825
+ throw new Error('This is a custom view type, set behaviors.getCustomDateFunc func ' +
826
+ 'to resolve the time window (startDate and endDate) yourself');
827
+ }
828
+ break;
829
+ default:
830
+ break;
831
+ }
832
+ }
833
+ _createHeaders() {
834
+ const timeSlots = [];
835
+ let start = this.localeDayjs(toDate(this.startDate));
836
+ let end = this.localeDayjs(toDate(this.endDate));
837
+ let header = start;
838
+ const width = this.getContentCellWidth();
839
+ if (this.showAgenda) {
840
+ timeSlots.push({
841
+ time: header.format(DATETIME_FORMAT),
842
+ nonWorkingTime: false,
843
+ });
844
+ }
845
+ else if (this.cellUnit === CellUnit.Hour) {
846
+ if (start.hour() === 0) {
847
+ start = start.hour(this.config.dayStartFrom);
848
+ }
849
+ if (end.hour() === 0) {
850
+ end = end.hour(this.config.dayStopTo);
851
+ }
852
+ header = start;
853
+ let prevHour = -1;
854
+ while (header >= start && header <= end) {
855
+ // prevent doubled hours on time change
856
+ if (header.hour() === prevHour) {
857
+ header = header.add(1, 'hours');
858
+ continue;
859
+ }
860
+ prevHour = header.hour();
861
+ const minuteSteps = this.getMinuteStepsInHour();
862
+ for (let i = 0; i < minuteSteps; i += 1) {
863
+ const hour = header.hour();
864
+ if (hour >= this.config.dayStartFrom && hour <= this.config.dayStopTo) {
865
+ const time = header.format(DATETIME_FORMAT);
866
+ const nonWorkingTime = this.behaviors.isNonWorkingTimeFunc(this, time);
867
+ timeSlots.push({ time, nonWorkingTime });
868
+ }
869
+ header = header.add(this.config.minuteStep, 'minutes');
870
+ }
871
+ }
872
+ }
873
+ else if (this.cellUnit === CellUnit.Day) {
874
+ while (header >= start && header <= end) {
875
+ const time = header.format(DATETIME_FORMAT);
876
+ const style = { width, minWidth: width };
877
+ const dayOfWeek = header.day();
878
+ if (this.config.displayWeekend || (dayOfWeek !== 0 && dayOfWeek !== 6)) {
879
+ const nonWorkingTime = this.behaviors.isNonWorkingTimeFunc(this, time);
880
+ timeSlots.push({ time, nonWorkingTime, style });
881
+ }
882
+ header = header.add(1, 'days');
883
+ }
884
+ }
885
+ else if (this.cellUnit === CellUnit.Week) {
886
+ while (header >= start && header <= end) {
887
+ const time = header.format(DATE_FORMAT);
888
+ timeSlots.push({ time });
889
+ header = header.add(1, 'weeks').startOf('isoWeek');
890
+ }
891
+ }
892
+ else if (this.cellUnit === CellUnit.Month) {
893
+ while (header >= start && header <= end) {
894
+ const time = header.format(DATE_FORMAT);
895
+ timeSlots.push({ time });
896
+ header = header.add(1, 'months').startOf('month');
897
+ }
898
+ }
899
+ else if (this.cellUnit === CellUnit.Quarter) {
900
+ while (header >= start && header <= end) {
901
+ const time = header.format(DATE_FORMAT);
902
+ timeSlots.push({ time });
903
+ header = header.add(1, 'quarters').startOf('quarter');
904
+ }
905
+ }
906
+ else if (this.cellUnit === CellUnit.Year) {
907
+ while (header >= start && header <= end) {
908
+ const time = header.format(DATE_FORMAT);
909
+ timeSlots.push({ time });
910
+ header = header.add(1, 'years').startOf('year');
911
+ }
912
+ }
913
+ this.timeSlots = timeSlots;
914
+ if (this.isVerticalResourceView()) {
915
+ const currentViewDate = this.localeDayjs(toDate(this.startDate)).format(DATETIME_FORMAT);
916
+ this.headers = this.resources.map(r => ({
917
+ ...r,
918
+ time: currentViewDate,
919
+ nonWorkingTime: false,
920
+ }));
921
+ }
922
+ else {
923
+ this.headers = timeSlots;
924
+ }
925
+ }
926
+ _createInitHeaderEvents(header) {
927
+ const start = this.localeDayjs(new Date(header.time));
928
+ const startValue = start.format(DATETIME_FORMAT);
929
+ let endValue;
930
+ if (this.showAgenda) {
931
+ const incrementUnits = {
932
+ [ViewType.Day]: 'days',
933
+ [ViewType.Week]: 'weeks',
934
+ [ViewType.Month]: 'months',
935
+ [ViewType.Year]: 'years',
936
+ [ViewType.Quarter]: 'quarters',
937
+ };
938
+ const incrementUnit = incrementUnits[this.viewType] || 'days';
939
+ if (incrementUnit === 'days') {
940
+ endValue = this.localeDayjs(toDate(this.endDate)).add(1, 'days').format(DATETIME_FORMAT);
941
+ }
942
+ else {
943
+ endValue = addUnit(start, 1, incrementUnit).format(DATETIME_FORMAT);
944
+ }
945
+ }
946
+ else if (this.isVerticalResourceView()) {
947
+ // In vertical view, headers are resources. They don't have a time span.
948
+ // But we need a start/end for the headerEvent structure.
949
+ endValue = startValue;
950
+ }
951
+ else {
952
+ const { amount, unit } = this._getCellStep();
953
+ // Keep the full timestamp so cell bounds match _getSpan/normalizeEventEnd in every time zone.
954
+ endValue = addUnit(start, amount, unit).format(DATETIME_FORMAT);
955
+ }
956
+ return {
957
+ time: header.time,
958
+ nonWorkingTime: header.nonWorkingTime,
959
+ start: startValue,
960
+ end: endValue,
961
+ count: 0,
962
+ addMore: 0,
963
+ addMoreIndex: 0,
964
+ events: [],
965
+ id: header.id || header.time,
966
+ style: header.style || { width: this.getContentCellWidth(), minWidth: this.getContentCellWidth() },
967
+ };
968
+ }
969
+ _createHeaderEvent(render, span, eventItem) {
970
+ return { render, span, eventItem };
971
+ }
972
+ _getEventSlotId(event) {
973
+ if (this.isEventPerspective) {
974
+ return this._getEventGroupId(event);
975
+ }
976
+ // Support for multi-resource events
977
+ if (event.resourceIds && event.resourceIds.length > 0) {
978
+ // For multi-resource events, return the first resourceId for backward compatibility
979
+ // The rendering logic will handle showing on multiple rows
980
+ return event.resourceIds[0];
981
+ }
982
+ // Fallback to single resourceId
983
+ return event.resourceId;
984
+ }
985
+ _getEventGroupId(event) {
986
+ return event.groupId ? event.groupId.toString() : event.id.toString();
987
+ }
988
+ _getEventGroupName(event) {
989
+ return event.groupName ? event.groupName : event.title;
990
+ }
991
+ _generateEventGroups() {
992
+ const eventGroups = [];
993
+ const set = new Set();
994
+ this.events.forEach(item => {
995
+ const groupId = this._getEventGroupId(item);
996
+ const groupName = this._getEventGroupName(item);
997
+ if (!set.has(groupId)) {
998
+ eventGroups.push({
999
+ id: groupId,
1000
+ name: groupName,
1001
+ state: item,
1002
+ });
1003
+ set.add(groupId);
1004
+ }
858
1005
  });
859
- header = header.add(1, 'years').startOf('year');
860
- }
861
- }
862
- this.timeSlots = timeSlots;
863
- if (this.isVerticalResourceView()) {
864
- const currentViewDate = this.localeDayjs(new Date(this.startDate)).format(DATETIME_FORMAT);
865
- this.headers = this.resources.map(r => ({
866
- ...r,
867
- time: currentViewDate,
868
- nonWorkingTime: false
869
- }));
870
- } else {
871
- this.headers = timeSlots;
872
- }
873
- }
874
- _createInitHeaderEvents(header) {
875
- const start = this.localeDayjs(new Date(header.time));
876
- const startValue = start.format(DATETIME_FORMAT);
877
- let endValue;
878
- if (this.showAgenda) {
879
- const incrementUnit = {
880
- [ViewType.Day]: 'days',
881
- [ViewType.Week]: 'weeks',
882
- [ViewType.Month]: 'months',
883
- [ViewType.Year]: 'years',
884
- [ViewType.Quarter]: 'quarters'
885
- }[this.viewType] || 'days';
886
- if (incrementUnit === 'days') {
887
- endValue = this.localeDayjs(new Date(this.endDate)).add(1, 'days').format(DATETIME_FORMAT);
888
- } else {
889
- endValue = start.add(1, incrementUnit).format(DATETIME_FORMAT);
890
- }
891
- } else if (this.isVerticalResourceView()) {
892
- // In vertical view, headers are resources. They don't have a time span.
893
- // But we need a start/end for the headerEvent structure.
894
- endValue = startValue;
895
- } else {
896
- const {
897
- amount,
898
- unit
899
- } = this._getCellStep();
900
-
901
- // Keep the full timestamp so cell bounds match _getSpan/normalizeEventEnd in every time zone.
902
- endValue = start.add(amount, unit).format(DATETIME_FORMAT);
903
- }
904
- return {
905
- time: header.time,
906
- nonWorkingTime: header.nonWorkingTime,
907
- start: startValue,
908
- end: endValue,
909
- count: 0,
910
- addMore: 0,
911
- addMoreIndex: 0,
912
- events: [],
913
- id: header.id || header.time,
914
- style: header.style || {
915
- width: this.getContentCellWidth(),
916
- minWidth: this.getContentCellWidth()
917
- }
918
- };
919
- }
920
- _createHeaderEvent(render, span, eventItem) {
921
- return {
922
- render,
923
- span,
924
- eventItem
925
- };
926
- }
927
- _getEventSlotId(event) {
928
- if (this.isEventPerspective) {
929
- return this._getEventGroupId(event);
930
- }
931
- // Support for multi-resource events
932
- if (event.resourceIds && event.resourceIds.length > 0) {
933
- // For multi-resource events, return the first resourceId for backward compatibility
934
- // The rendering logic will handle showing on multiple rows
935
- return event.resourceIds[0];
936
- }
937
- // Fallback to single resourceId
938
- return event.resourceId;
939
- }
940
- _getEventGroupId(event) {
941
- return event.groupId ? event.groupId.toString() : event.id.toString();
942
- }
943
- _getEventGroupName(event) {
944
- return event.groupName ? event.groupName : event.title;
945
- }
946
- _generateEventGroups() {
947
- const eventGroups = [];
948
- const set = new Set();
949
- this.events.forEach(item => {
950
- const groupId = this._getEventGroupId(item);
951
- const groupName = this._getEventGroupName(item);
952
- if (!set.has(groupId)) {
953
- eventGroups.push({
954
- id: groupId,
955
- name: groupName,
956
- state: item
1006
+ this.eventGroups = eventGroups;
1007
+ }
1008
+ _createInitRenderData(isEventPerspective, eventGroups, resources, headers) {
1009
+ const slots = this.isVerticalResourceView()
1010
+ ? this.timeSlots.map(ts => ({
1011
+ ...ts,
1012
+ id: ts.time,
1013
+ name: ts.time,
1014
+ slotId: ts.time,
1015
+ slotName: ts.time,
1016
+ }))
1017
+ : isEventPerspective
1018
+ ? eventGroups
1019
+ : resources;
1020
+ const slotTree = [];
1021
+ const slotMap = new Map();
1022
+ slots.forEach(slot => {
1023
+ const headerEvents = headers.map(header => this._createInitHeaderEvents(header));
1024
+ const slotRenderData = {
1025
+ ...slot,
1026
+ slotId: slot.id,
1027
+ slotName: slot.name,
1028
+ slotTitle: slot.title,
1029
+ parentId: slot.parentId,
1030
+ groupOnly: slot.groupOnly,
1031
+ hasSummary: false,
1032
+ rowMaxCount: 0,
1033
+ rowHeight: this.config.nonAgendaSlotMinHeight !== 0
1034
+ ? this.config.nonAgendaSlotMinHeight
1035
+ : this.config.eventItemLineHeight + 2,
1036
+ headerItems: headerEvents,
1037
+ indent: 0,
1038
+ hasChildren: false,
1039
+ expanded: true,
1040
+ render: true,
1041
+ };
1042
+ const { id } = slot;
1043
+ let value;
1044
+ if (slotMap.has(id)) {
1045
+ value = slotMap.get(id);
1046
+ value.data = slotRenderData;
1047
+ }
1048
+ else {
1049
+ value = {
1050
+ data: slotRenderData,
1051
+ children: [],
1052
+ };
1053
+ slotMap.set(id, value);
1054
+ }
1055
+ const { parentId } = slot;
1056
+ if (!parentId || parentId === id) {
1057
+ slotTree.push(value);
1058
+ }
1059
+ else {
1060
+ let parentValue;
1061
+ if (slotMap.has(parentId)) {
1062
+ parentValue = slotMap.get(parentId);
1063
+ }
1064
+ else {
1065
+ parentValue = {
1066
+ data: undefined,
1067
+ children: [],
1068
+ };
1069
+ slotMap.set(parentId, parentValue);
1070
+ }
1071
+ parentValue.children.push(value);
1072
+ }
957
1073
  });
958
- set.add(groupId);
959
- }
960
- });
961
- this.eventGroups = eventGroups;
962
- }
963
- _createInitRenderData(isEventPerspective, eventGroups, resources, headers) {
964
- const slots = this.isVerticalResourceView() ? this.timeSlots.map(ts => ({
965
- ...ts,
966
- id: ts.time,
967
- name: ts.time,
968
- slotId: ts.time,
969
- slotName: ts.time
970
- })) : isEventPerspective ? eventGroups : resources;
971
- const slotTree = [];
972
- const slotMap = new Map();
973
- slots.forEach(slot => {
974
- const headerEvents = headers.map(header => this._createInitHeaderEvents(header));
975
- const slotRenderData = {
976
- ...slot,
977
- slotId: slot.id,
978
- slotName: slot.name,
979
- slotTitle: slot.title,
980
- parentId: slot.parentId,
981
- groupOnly: slot.groupOnly,
982
- hasSummary: false,
983
- rowMaxCount: 0,
984
- rowHeight: this.config.nonAgendaSlotMinHeight !== 0 ? this.config.nonAgendaSlotMinHeight : this.config.eventItemLineHeight + 2,
985
- headerItems: headerEvents,
986
- indent: 0,
987
- hasChildren: false,
988
- expanded: true,
989
- render: true
990
- };
991
- const {
992
- id
993
- } = slot;
994
- let value;
995
- if (slotMap.has(id)) {
996
- value = slotMap.get(id);
997
- value.data = slotRenderData;
998
- } else {
999
- value = {
1000
- data: slotRenderData,
1001
- children: []
1074
+ const slotStack = [];
1075
+ let i;
1076
+ for (i = slotTree.length - 1; i >= 0; i -= 1) {
1077
+ slotStack.push(slotTree[i]);
1078
+ }
1079
+ const initRenderData = [];
1080
+ while (slotStack.length > 0) {
1081
+ const currentNode = slotStack.pop();
1082
+ // every node reached from the tree roots has its data set (placeholders only exist for missing parents)
1083
+ const data = currentNode.data;
1084
+ if (data.indent > 0) {
1085
+ data.render = this.config.defaultExpanded;
1086
+ }
1087
+ if (currentNode.children.length > 0) {
1088
+ data.hasChildren = true;
1089
+ data.expanded = this.config.defaultExpanded;
1090
+ }
1091
+ initRenderData.push(data);
1092
+ for (i = currentNode.children.length - 1; i >= 0; i -= 1) {
1093
+ currentNode.children[i].data.indent = data.indent + 1;
1094
+ slotStack.push(currentNode.children[i]);
1095
+ }
1096
+ }
1097
+ return initRenderData;
1098
+ }
1099
+ _getCellStep() {
1100
+ const units = {
1101
+ [CellUnit.Hour]: 'minutes',
1102
+ [CellUnit.Week]: 'weeks',
1103
+ [CellUnit.Month]: 'months',
1104
+ [CellUnit.Year]: 'years',
1105
+ [CellUnit.Quarter]: 'quarters',
1002
1106
  };
1003
- slotMap.set(id, value);
1004
- }
1005
- const {
1006
- parentId
1007
- } = slot;
1008
- if (!parentId || parentId === id) {
1009
- slotTree.push(value);
1010
- } else {
1011
- let parentValue;
1012
- if (slotMap.has(parentId)) {
1013
- parentValue = slotMap.get(parentId);
1014
- } else {
1015
- parentValue = {
1016
- data: undefined,
1017
- children: []
1018
- };
1019
- slotMap.set(parentId, parentValue);
1107
+ const unit = units[this.cellUnit] || 'days';
1108
+ return { amount: unit === 'minutes' ? this.config.minuteStep : 1, unit };
1109
+ }
1110
+ // Span = number of visible header cells the event overlaps, using the same overlap rule as
1111
+ // _createRenderData. Headers only cover the visible window (and visible days), so the event is
1112
+ // clamped consistently across views, and calendar-aware dayjs math keeps DST days at one cell.
1113
+ _getSpan(startTime, endTime, headers) {
1114
+ if (this.showAgenda || this.isVerticalResourceView())
1115
+ return 1;
1116
+ const eventStart = normalizeEventStart(startTime);
1117
+ const eventEnd = normalizeEventEnd(endTime);
1118
+ const { amount, unit } = this._getCellStep();
1119
+ return headers.filter(header => {
1120
+ const cellStart = this.localeDayjs(new Date(header.time));
1121
+ return cellStart.isBefore(eventEnd) && addUnit(cellStart, amount, unit).isAfter(eventStart);
1122
+ }).length;
1123
+ }
1124
+ _validateResource(resources) {
1125
+ if (Object.prototype.toString.call(resources) !== '[object Array]') {
1126
+ throw new Error('Resources should be Array object');
1020
1127
  }
1021
- parentValue.children.push(value);
1022
- }
1023
- });
1024
- const slotStack = [];
1025
- let i;
1026
- for (i = slotTree.length - 1; i >= 0; i -= 1) {
1027
- slotStack.push(slotTree[i]);
1028
- }
1029
- const initRenderData = [];
1030
- let currentNode;
1031
- while (slotStack.length > 0) {
1032
- currentNode = slotStack.pop();
1033
- if (currentNode.data.indent > 0) {
1034
- currentNode.data.render = this.config.defaultExpanded;
1035
- }
1036
- if (currentNode.children.length > 0) {
1037
- currentNode.data.hasChildren = true;
1038
- currentNode.data.expanded = this.config.defaultExpanded;
1039
- }
1040
- initRenderData.push(currentNode.data);
1041
- for (i = currentNode.children.length - 1; i >= 0; i -= 1) {
1042
- currentNode.children[i].data.indent = currentNode.data.indent + 1;
1043
- slotStack.push(currentNode.children[i]);
1044
- }
1045
- }
1046
- return initRenderData;
1047
- }
1048
- _getCellStep() {
1049
- const unit = {
1050
- [CellUnit.Hour]: 'minutes',
1051
- [CellUnit.Week]: 'weeks',
1052
- [CellUnit.Month]: 'months',
1053
- [CellUnit.Year]: 'years',
1054
- [CellUnit.Quarter]: 'quarters'
1055
- }[this.cellUnit] || 'days';
1056
- return {
1057
- amount: unit === 'minutes' ? this.config.minuteStep : 1,
1058
- unit
1059
- };
1060
- }
1061
-
1062
- // Span = number of visible header cells the event overlaps, using the same overlap rule as
1063
- // _createRenderData. Headers only cover the visible window (and visible days), so the event is
1064
- // clamped consistently across views, and calendar-aware dayjs math keeps DST days at one cell.
1065
- _getSpan(startTime, endTime, headers) {
1066
- if (this.showAgenda || this.isVerticalResourceView()) return 1;
1067
- const eventStart = normalizeEventStart(startTime);
1068
- const eventEnd = normalizeEventEnd(endTime);
1069
- const {
1070
- amount,
1071
- unit
1072
- } = this._getCellStep();
1073
- return headers.filter(header => {
1074
- const cellStart = this.localeDayjs(new Date(header.time));
1075
- return cellStart.isBefore(eventEnd) && cellStart.add(amount, unit).isAfter(eventStart);
1076
- }).length;
1077
- }
1078
- _validateResource(resources) {
1079
- if (Object.prototype.toString.call(resources) !== '[object Array]') {
1080
- throw new Error('Resources should be Array object');
1081
- }
1082
- resources.forEach((item, index) => {
1083
- if (item === undefined) {
1084
- console.error(`Resource undefined: ${index}`);
1085
- throw new Error(`Resource undefined: ${index}`);
1086
- }
1087
- if (item.id === undefined || item.name === undefined) {
1088
- console.error('Resource property missed', index, item);
1089
- throw new Error(`Resource property undefined: ${index}`);
1090
- }
1091
- });
1092
- }
1093
- _validateEventGroups(eventGroups) {
1094
- if (Object.prototype.toString.call(eventGroups) !== '[object Array]') {
1095
- throw new Error('Event groups should be Array object');
1096
- }
1097
- eventGroups.forEach((item, index) => {
1098
- if (item === undefined) {
1099
- console.error(`Event group undefined: ${index}`);
1100
- throw new Error(`Event group undefined: ${index}`);
1101
- }
1102
- if (item.id === undefined || item.name === undefined) {
1103
- console.error('Event group property missed', index, item);
1104
- throw new Error(`Event group property undefined: ${index}`);
1105
- }
1106
- });
1107
- }
1108
- _validateEvents(events) {
1109
- if (Object.prototype.toString.call(events) !== '[object Array]') {
1110
- throw new Error('Events should be Array object');
1111
- }
1112
- events.forEach((e, index) => {
1113
- if (e === undefined) {
1114
- console.error(`Event undefined: ${index}`);
1115
- throw new Error(`Event undefined: ${index}`);
1116
- }
1117
- const hasResourceTarget = e.resourceId !== undefined || Array.isArray(e.resourceIds) && e.resourceIds.length > 0;
1118
- if (e.id === undefined || !hasResourceTarget || e.title === undefined || e.start === undefined || e.end === undefined) {
1119
- console.error('Event property missed (id, resourceId/resourceIds, title, start, end)', index, e);
1120
- throw new Error(`Event property undefined (id/resourceId|resourceIds/title/start/end): ${index}`);
1121
- }
1122
- });
1123
- }
1124
- _validateMinuteStep(minuteStep) {
1125
- if (60 % minuteStep !== 0) {
1126
- console.error('Minute step is not set properly - 60 minutes must be divisible without remainder by this number');
1127
- throw new Error('Minute step is not set properly - 60 minutes must be divisible without remainder by this number');
1128
- }
1129
- }
1130
- _compare(event1, event2) {
1131
- const start1 = this.localeDayjs(event1.start);
1132
- const start2 = this.localeDayjs(event2.start);
1133
- if (start1 !== start2) return start1 < start2 ? -1 : 1;
1134
- const end1 = this.localeDayjs(event1.end);
1135
- const end2 = this.localeDayjs(event2.end);
1136
- if (end1 !== end2) return end1 < end2 ? -1 : 1;
1137
- return event1.id < event2.id ? -1 : 1;
1138
- }
1139
- _createRenderData() {
1140
- const initRenderData = this._createInitRenderData(this.isEventPerspective, this.eventGroups, this.resources, this.headers);
1141
- // this.events.sort(this._compare);
1142
- const cellMaxEventsCount = this.getCellMaxEvents();
1143
- const cellMaxEventsCountValue = 30;
1144
- this.events.forEach(item => {
1145
- let targetSlotIds = [];
1146
- if (this.isVerticalResourceView()) {
1147
- const eventStart = new Date(item.start);
1148
- const eventEnd = new Date(item.end);
1149
- this.timeSlots.forEach(ts => {
1150
- // Check if the time slot overlaps with the event's time
1151
- const tsStart = new Date(ts.time);
1152
- const tsEnd = this.localeDayjs(ts.time).add(this.config.minuteStep, 'minutes').toDate();
1153
- if (tsEnd > eventStart && tsStart < eventEnd) {
1154
- targetSlotIds.push(ts.time);
1155
- }
1128
+ resources.forEach((item, index) => {
1129
+ if (item === undefined) {
1130
+ console.error(`Resource undefined: ${index}`);
1131
+ throw new Error(`Resource undefined: ${index}`);
1132
+ }
1133
+ if (item.id === undefined || item.name === undefined) {
1134
+ console.error('Resource property missed', index, item);
1135
+ throw new Error(`Resource property undefined: ${index}`);
1136
+ }
1156
1137
  });
1157
- } else if (this.isEventPerspective) {
1158
- targetSlotIds = [this._getEventSlotId(item)];
1159
- } else if (item.resourceIds && item.resourceIds.length > 0) {
1160
- // Multi-resource event
1161
- targetSlotIds = item.resourceIds;
1162
- } else if (item.resourceId) {
1163
- // Single resource event (backward compatibility)
1164
- targetSlotIds = [item.resourceId];
1165
- }
1166
- targetSlotIds.forEach(slotId => {
1167
- const resourceEventsList = initRenderData.filter(x => x.slotId === slotId);
1168
- if (resourceEventsList.length > 0) {
1169
- const resourceEvents = resourceEventsList[0];
1170
- const span = this._getSpan(item.start, item.end, this.headers);
1171
- const eventStart = normalizeEventStart(item.start).toDate();
1172
- const eventEnd = normalizeEventEnd(item.end).toDate();
1173
- let pos = -1;
1174
- resourceEvents.headerItems.forEach((header, index) => {
1175
- let isOverlapping = false;
1176
- if (this.isVerticalResourceView()) {
1177
- // In vertical view, headers are resources. Check if event belongs to this resource.
1178
- const itemResourceIds = item.resourceIds || [item.resourceId];
1179
- const targetId = header.id;
1180
- isOverlapping = itemResourceIds.includes(targetId);
1181
- } else {
1182
- const headerStart = new Date(header.start);
1183
- const headerEnd = new Date(header.end);
1184
- isOverlapping = headerEnd > eventStart && headerStart < eventEnd;
1138
+ }
1139
+ _validateEventGroups(eventGroups) {
1140
+ if (Object.prototype.toString.call(eventGroups) !== '[object Array]') {
1141
+ throw new Error('Event groups should be Array object');
1142
+ }
1143
+ eventGroups.forEach((item, index) => {
1144
+ if (item === undefined) {
1145
+ console.error(`Event group undefined: ${index}`);
1146
+ throw new Error(`Event group undefined: ${index}`);
1185
1147
  }
1186
- if (isOverlapping) {
1187
- header.count += 1;
1188
- if (header.count > resourceEvents.rowMaxCount) {
1189
- resourceEvents.rowMaxCount = header.count;
1190
- const rowsCount = cellMaxEventsCount <= cellMaxEventsCountValue && resourceEvents.rowMaxCount > cellMaxEventsCount ? cellMaxEventsCount : resourceEvents.rowMaxCount;
1191
- const newRowHeight = rowsCount * this.config.eventItemLineHeight + (this.config.creatable && this.config.checkConflict === false ? 20 : 2);
1192
- if (newRowHeight > resourceEvents.rowHeight) resourceEvents.rowHeight = newRowHeight;
1193
- }
1194
- if (pos === -1) {
1195
- let tmp = 0;
1196
- while (header.events[tmp] !== undefined) tmp += 1;
1197
- pos = tmp;
1198
- }
1199
- let render = false;
1200
- if (this.isVerticalResourceView()) {
1201
- render = true;
1202
- } else {
1203
- const headerStart = new Date(header.start);
1204
- render = headerStart <= eventStart || index === 0;
1205
- if (render === false) {
1206
- const previousHeader = resourceEvents.headerItems[index - 1];
1207
- const previousHeaderStart = new Date(previousHeader.start);
1208
- const previousHeaderEnd = new Date(previousHeader.end);
1209
- if (previousHeaderEnd <= eventStart || previousHeaderStart >= eventEnd) render = true;
1210
- }
1211
- }
1212
- header.events[pos] = this._createHeaderEvent(render, span, item);
1148
+ if (item.id === undefined || item.name === undefined) {
1149
+ console.error('Event group property missed', index, item);
1150
+ throw new Error(`Event group property undefined: ${index}`);
1213
1151
  }
1214
- });
1152
+ });
1153
+ }
1154
+ _validateEvents(events) {
1155
+ if (Object.prototype.toString.call(events) !== '[object Array]') {
1156
+ throw new Error('Events should be Array object');
1157
+ }
1158
+ events.forEach((e, index) => {
1159
+ if (e === undefined) {
1160
+ console.error(`Event undefined: ${index}`);
1161
+ throw new Error(`Event undefined: ${index}`);
1162
+ }
1163
+ const hasResourceTarget = e.resourceId !== undefined || (Array.isArray(e.resourceIds) && e.resourceIds.length > 0);
1164
+ if (e.id === undefined ||
1165
+ !hasResourceTarget ||
1166
+ e.title === undefined ||
1167
+ e.start === undefined ||
1168
+ e.end === undefined) {
1169
+ console.error('Event property missed (id, resourceId/resourceIds, title, start, end)', index, e);
1170
+ throw new Error(`Event property undefined (id/resourceId|resourceIds/title/start/end): ${index}`);
1171
+ }
1172
+ });
1173
+ }
1174
+ _validateMinuteStep(minuteStep) {
1175
+ if (60 % minuteStep !== 0) {
1176
+ console.error('Minute step is not set properly - 60 minutes must be divisible without remainder by this number');
1177
+ throw new Error('Minute step is not set properly - 60 minutes must be divisible without remainder by this number');
1215
1178
  }
1216
- });
1217
- });
1218
- if (cellMaxEventsCount <= cellMaxEventsCountValue || this.behaviors.getSummaryFunc !== undefined) {
1219
- initRenderData.forEach(resourceEvents => {
1220
- let hasSummary = false;
1221
- resourceEvents.headerItems.forEach(headerItem => {
1222
- if (cellMaxEventsCount <= cellMaxEventsCountValue) {
1223
- let renderItemsCount = 0;
1224
- let addMoreIndex = 0;
1225
- let index = 0;
1226
- while (index < cellMaxEventsCount - 1) {
1227
- if (headerItem.events[index] !== undefined) {
1228
- renderItemsCount += 1;
1229
- addMoreIndex = index + 1;
1230
- }
1231
- index += 1;
1179
+ }
1180
+ _compare(event1, event2) {
1181
+ const start1 = this.localeDayjs(event1.start);
1182
+ const start2 = this.localeDayjs(event2.start);
1183
+ if (start1 !== start2)
1184
+ return start1 < start2 ? -1 : 1;
1185
+ const end1 = this.localeDayjs(event1.end);
1186
+ const end2 = this.localeDayjs(event2.end);
1187
+ if (end1 !== end2)
1188
+ return end1 < end2 ? -1 : 1;
1189
+ return event1.id < event2.id ? -1 : 1;
1190
+ }
1191
+ _createRenderData() {
1192
+ const initRenderData = this._createInitRenderData(this.isEventPerspective, this.eventGroups, this.resources, this.headers);
1193
+ // this.events.sort(this._compare);
1194
+ const cellMaxEventsCount = this.getCellMaxEvents();
1195
+ const cellMaxEventsCountValue = 30;
1196
+ this.events.forEach(item => {
1197
+ let targetSlotIds = [];
1198
+ if (this.isVerticalResourceView()) {
1199
+ const eventStart = new Date(item.start);
1200
+ const eventEnd = new Date(item.end);
1201
+ this.timeSlots.forEach(ts => {
1202
+ // Check if the time slot overlaps with the event's time
1203
+ const tsStart = new Date(ts.time);
1204
+ const tsEnd = this.localeDayjs(ts.time).add(this.config.minuteStep, 'minutes').toDate();
1205
+ if (tsEnd > eventStart && tsStart < eventEnd) {
1206
+ targetSlotIds.push(ts.time);
1207
+ }
1208
+ });
1232
1209
  }
1233
- if (headerItem.events[index] !== undefined) {
1234
- if (renderItemsCount + 1 < headerItem.count) {
1235
- headerItem.addMore = headerItem.count - renderItemsCount;
1236
- headerItem.addMoreIndex = addMoreIndex;
1237
- }
1238
- } else if (renderItemsCount < headerItem.count) {
1239
- headerItem.addMore = headerItem.count - renderItemsCount;
1240
- headerItem.addMoreIndex = addMoreIndex;
1210
+ else if (this.isEventPerspective) {
1211
+ targetSlotIds = [this._getEventSlotId(item)];
1241
1212
  }
1242
- }
1243
- if (this.behaviors.getSummaryFunc !== undefined) {
1244
- const events = [];
1245
- headerItem.events.forEach(e => {
1246
- if (e?.eventItem) events.push(e.eventItem);
1213
+ else if (item.resourceIds && item.resourceIds.length > 0) {
1214
+ // Multi-resource event
1215
+ targetSlotIds = item.resourceIds;
1216
+ }
1217
+ else if (item.resourceId) {
1218
+ // Single resource event (backward compatibility)
1219
+ targetSlotIds = [item.resourceId];
1220
+ }
1221
+ targetSlotIds.forEach(slotId => {
1222
+ const resourceEventsList = initRenderData.filter(x => x.slotId === slotId);
1223
+ if (resourceEventsList.length > 0) {
1224
+ const resourceEvents = resourceEventsList[0];
1225
+ const span = this._getSpan(item.start, item.end, this.headers);
1226
+ const eventStart = normalizeEventStart(item.start).toDate();
1227
+ const eventEnd = normalizeEventEnd(item.end).toDate();
1228
+ let pos = -1;
1229
+ resourceEvents.headerItems.forEach((header, index) => {
1230
+ let isOverlapping = false;
1231
+ if (this.isVerticalResourceView()) {
1232
+ // In vertical view, headers are resources. Check if event belongs to this resource.
1233
+ const itemResourceIds = item.resourceIds || [item.resourceId];
1234
+ const targetId = header.id;
1235
+ isOverlapping = itemResourceIds.includes(targetId);
1236
+ }
1237
+ else {
1238
+ const headerStart = new Date(header.start);
1239
+ const headerEnd = new Date(header.end);
1240
+ isOverlapping = headerEnd > eventStart && headerStart < eventEnd;
1241
+ }
1242
+ if (isOverlapping) {
1243
+ header.count += 1;
1244
+ if (header.count > resourceEvents.rowMaxCount) {
1245
+ resourceEvents.rowMaxCount = header.count;
1246
+ const rowsCount = cellMaxEventsCount <= cellMaxEventsCountValue && resourceEvents.rowMaxCount > cellMaxEventsCount
1247
+ ? cellMaxEventsCount
1248
+ : resourceEvents.rowMaxCount;
1249
+ const newRowHeight = rowsCount * this.config.eventItemLineHeight +
1250
+ (this.config.creatable && this.config.checkConflict === false ? 20 : 2);
1251
+ if (newRowHeight > resourceEvents.rowHeight)
1252
+ resourceEvents.rowHeight = newRowHeight;
1253
+ }
1254
+ if (pos === -1) {
1255
+ let tmp = 0;
1256
+ while (header.events[tmp] !== undefined)
1257
+ tmp += 1;
1258
+ pos = tmp;
1259
+ }
1260
+ let render = false;
1261
+ if (this.isVerticalResourceView()) {
1262
+ render = true;
1263
+ }
1264
+ else {
1265
+ const headerStart = new Date(header.start);
1266
+ render = headerStart <= eventStart || index === 0;
1267
+ if (render === false) {
1268
+ const previousHeader = resourceEvents.headerItems[index - 1];
1269
+ const previousHeaderStart = new Date(previousHeader.start);
1270
+ const previousHeaderEnd = new Date(previousHeader.end);
1271
+ if (previousHeaderEnd <= eventStart || previousHeaderStart >= eventEnd)
1272
+ render = true;
1273
+ }
1274
+ }
1275
+ header.events[pos] = this._createHeaderEvent(render, span, item);
1276
+ }
1277
+ });
1278
+ }
1247
1279
  });
1248
- headerItem.summary = this.behaviors.getSummaryFunc(this, events, resourceEvents.slotId, resourceEvents.slotName, headerItem.start, headerItem.end);
1249
- if (headerItem.summary && headerItem.summary.text !== undefined) hasSummary = true;
1250
- }
1251
1280
  });
1252
- resourceEvents.hasSummary = hasSummary;
1253
- if (hasSummary) {
1254
- const rowsCount = cellMaxEventsCount <= cellMaxEventsCountValue && resourceEvents.rowMaxCount > cellMaxEventsCount ? cellMaxEventsCount : resourceEvents.rowMaxCount;
1255
- const newRowHeight = (rowsCount + 1) * this.config.eventItemLineHeight + (this.config.creatable && this.config.checkConflict === false ? 20 : 2);
1256
- if (newRowHeight > resourceEvents.rowHeight) resourceEvents.rowHeight = newRowHeight;
1281
+ if (cellMaxEventsCount <= cellMaxEventsCountValue || this.behaviors.getSummaryFunc !== undefined) {
1282
+ initRenderData.forEach(resourceEvents => {
1283
+ let hasSummary = false;
1284
+ resourceEvents.headerItems.forEach(headerItem => {
1285
+ if (cellMaxEventsCount <= cellMaxEventsCountValue) {
1286
+ let renderItemsCount = 0;
1287
+ let addMoreIndex = 0;
1288
+ let index = 0;
1289
+ while (index < cellMaxEventsCount - 1) {
1290
+ if (headerItem.events[index] !== undefined) {
1291
+ renderItemsCount += 1;
1292
+ addMoreIndex = index + 1;
1293
+ }
1294
+ index += 1;
1295
+ }
1296
+ if (headerItem.events[index] !== undefined) {
1297
+ if (renderItemsCount + 1 < headerItem.count) {
1298
+ headerItem.addMore = headerItem.count - renderItemsCount;
1299
+ headerItem.addMoreIndex = addMoreIndex;
1300
+ }
1301
+ }
1302
+ else if (renderItemsCount < headerItem.count) {
1303
+ headerItem.addMore = headerItem.count - renderItemsCount;
1304
+ headerItem.addMoreIndex = addMoreIndex;
1305
+ }
1306
+ }
1307
+ if (this.behaviors.getSummaryFunc !== undefined) {
1308
+ const events = [];
1309
+ headerItem.events.forEach(e => {
1310
+ if (e?.eventItem)
1311
+ events.push(e.eventItem);
1312
+ });
1313
+ headerItem.summary = this.behaviors.getSummaryFunc(this, events, resourceEvents.slotId, resourceEvents.slotName, headerItem.start, headerItem.end);
1314
+ if (headerItem.summary && headerItem.summary.text !== undefined)
1315
+ hasSummary = true;
1316
+ }
1317
+ });
1318
+ resourceEvents.hasSummary = hasSummary;
1319
+ if (hasSummary) {
1320
+ const rowsCount = cellMaxEventsCount <= cellMaxEventsCountValue && resourceEvents.rowMaxCount > cellMaxEventsCount
1321
+ ? cellMaxEventsCount
1322
+ : resourceEvents.rowMaxCount;
1323
+ const newRowHeight = (rowsCount + 1) * this.config.eventItemLineHeight +
1324
+ (this.config.creatable && this.config.checkConflict === false ? 20 : 2);
1325
+ if (newRowHeight > resourceEvents.rowHeight)
1326
+ resourceEvents.rowHeight = newRowHeight;
1327
+ }
1328
+ });
1257
1329
  }
1258
- });
1259
- }
1260
- this.renderData = initRenderData;
1261
- }
1262
- _startResizing() {
1263
- this.resizing = true;
1264
- }
1265
- _stopResizing() {
1266
- this.resizing = false;
1267
- }
1268
- _isResizing() {
1269
- return this.resizing;
1270
- }
1271
- }
1330
+ this.renderData = initRenderData;
1331
+ }
1332
+ _startResizing() {
1333
+ this.resizing = true;
1334
+ }
1335
+ _stopResizing() {
1336
+ this.resizing = false;
1337
+ }
1338
+ _isResizing() {
1339
+ return this.resizing;
1340
+ }
1341
+ }