@fullcalendar/timeline 7.0.0-beta.3 → 7.0.0-beta.5

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.
package/internal.cjs DELETED
@@ -1,1261 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var internal_cjs = require('@fullcalendar/core/internal.cjs');
6
- var preact_cjs = require('@fullcalendar/core/preact.cjs');
7
- var internal_cjs$1 = require('@fullcalendar/scrollgrid/internal.cjs');
8
-
9
- const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
10
- const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
11
- const MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
12
- internal_cjs.config.MAX_TIMELINE_SLOTS = 1000;
13
- // potential nice values for slot-duration and interval-duration
14
- const STOCK_SUB_DURATIONS = [
15
- { years: 1 },
16
- { months: 1 },
17
- { days: 1 },
18
- { hours: 1 },
19
- { minutes: 30 },
20
- { minutes: 15 },
21
- { minutes: 10 },
22
- { minutes: 5 },
23
- { minutes: 1 },
24
- { seconds: 30 },
25
- { seconds: 15 },
26
- { seconds: 10 },
27
- { seconds: 5 },
28
- { seconds: 1 },
29
- { milliseconds: 500 },
30
- { milliseconds: 100 },
31
- { milliseconds: 10 },
32
- { milliseconds: 1 },
33
- ];
34
- function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
35
- let tDateProfile = {
36
- labelInterval: allOptions.slotLabelInterval,
37
- slotDuration: allOptions.slotDuration,
38
- };
39
- validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
40
- ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
41
- ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
42
- let input = allOptions.slotLabelFormat;
43
- let rawFormats = Array.isArray(input) ? input :
44
- (input != null) ? [input] :
45
- computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
46
- tDateProfile.headerFormats = rawFormats.map((rawFormat) => internal_cjs.createFormatter(rawFormat));
47
- tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
48
- let largeUnit = null;
49
- if (!tDateProfile.isTimeScale) {
50
- const slotUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
51
- if (/year|month|week/.test(slotUnit)) {
52
- largeUnit = slotUnit;
53
- }
54
- }
55
- tDateProfile.largeUnit = largeUnit;
56
- tDateProfile.emphasizeWeeks =
57
- internal_cjs.asCleanDays(tDateProfile.slotDuration) === 1 &&
58
- currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
59
- !allOptions.businessHours;
60
- /*
61
- console.log('label interval =', timelineView.labelInterval.humanize())
62
- console.log('slot duration =', timelineView.slotDuration.humanize())
63
- console.log('header formats =', timelineView.headerFormats)
64
- console.log('isTimeScale', timelineView.isTimeScale)
65
- console.log('largeUnit', timelineView.largeUnit)
66
- */
67
- let rawSnapDuration = allOptions.snapDuration;
68
- let snapDuration;
69
- let snapsPerSlot;
70
- if (rawSnapDuration) {
71
- snapDuration = internal_cjs.createDuration(rawSnapDuration);
72
- snapsPerSlot = internal_cjs.wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
73
- // ^ TODO: warning if not whole?
74
- }
75
- if (snapsPerSlot == null) {
76
- snapDuration = tDateProfile.slotDuration;
77
- snapsPerSlot = 1;
78
- }
79
- tDateProfile.snapDuration = snapDuration;
80
- tDateProfile.snapsPerSlot = snapsPerSlot;
81
- // more...
82
- let timeWindowMs = internal_cjs.asRoughMs(dateProfile.slotMaxTime) - internal_cjs.asRoughMs(dateProfile.slotMinTime);
83
- // TODO: why not use normalizeRange!?
84
- let normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
85
- let normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
86
- // apply slotMinTime/slotMaxTime
87
- // TODO: View should be responsible.
88
- if (tDateProfile.isTimeScale) {
89
- normalizedStart = dateEnv.add(normalizedStart, dateProfile.slotMinTime);
90
- normalizedEnd = dateEnv.add(internal_cjs.addDays(normalizedEnd, -1), dateProfile.slotMaxTime);
91
- }
92
- tDateProfile.timeWindowMs = timeWindowMs;
93
- tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
94
- let slotDates = [];
95
- let date = normalizedStart;
96
- while (date < normalizedEnd) {
97
- if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
98
- slotDates.push(date);
99
- }
100
- date = dateEnv.add(date, tDateProfile.slotDuration);
101
- }
102
- tDateProfile.slotDates = slotDates;
103
- // more...
104
- let snapIndex = -1;
105
- let snapDiff = 0; // index of the diff :(
106
- const snapDiffToIndex = [];
107
- const snapIndexToDiff = [];
108
- date = normalizedStart;
109
- while (date < normalizedEnd) {
110
- if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
111
- snapIndex += 1;
112
- snapDiffToIndex.push(snapIndex);
113
- snapIndexToDiff.push(snapDiff);
114
- }
115
- else {
116
- snapDiffToIndex.push(snapIndex + 0.5);
117
- }
118
- date = dateEnv.add(date, tDateProfile.snapDuration);
119
- snapDiff += 1;
120
- }
121
- tDateProfile.snapDiffToIndex = snapDiffToIndex;
122
- tDateProfile.snapIndexToDiff = snapIndexToDiff;
123
- tDateProfile.snapCnt = snapIndex + 1; // is always one behind
124
- tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
125
- // more...
126
- tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
127
- tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv);
128
- tDateProfile.slotsPerLabel = internal_cjs.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
129
- return tDateProfile;
130
- }
131
- /*
132
- snaps to appropriate unit
133
- */
134
- function normalizeDate(date, tDateProfile, dateEnv) {
135
- let normalDate = date;
136
- if (!tDateProfile.isTimeScale) {
137
- normalDate = internal_cjs.startOfDay(normalDate);
138
- if (tDateProfile.largeUnit) {
139
- normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
140
- }
141
- }
142
- return normalDate;
143
- }
144
- /*
145
- snaps to appropriate unit
146
- */
147
- function normalizeRange(range, tDateProfile, dateEnv) {
148
- if (!tDateProfile.isTimeScale) {
149
- range = internal_cjs.computeVisibleDayRange(range);
150
- if (tDateProfile.largeUnit) {
151
- let dayRange = range; // preserve original result
152
- range = {
153
- start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
154
- end: dateEnv.startOf(range.end, tDateProfile.largeUnit),
155
- };
156
- // if date is partially through the interval, or is in the same interval as the start,
157
- // make the exclusive end be the *next* interval
158
- if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
159
- range = {
160
- start: range.start,
161
- end: dateEnv.add(range.end, tDateProfile.slotDuration),
162
- };
163
- }
164
- }
165
- }
166
- return range;
167
- }
168
- function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
169
- if (dateProfileGenerator.isHiddenDay(date)) {
170
- return false;
171
- }
172
- if (tDateProfile.isTimeScale) {
173
- // determine if the time is within slotMinTime/slotMaxTime, which may have wacky values
174
- let day = internal_cjs.startOfDay(date);
175
- let timeMs = date.valueOf() - day.valueOf();
176
- let ms = timeMs - internal_cjs.asRoughMs(dateProfile.slotMinTime); // milliseconds since slotMinTime
177
- ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
178
- return ms < tDateProfile.timeWindowMs; // before the slotMaxTime?
179
- }
180
- return true;
181
- }
182
- function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
183
- const { currentRange } = dateProfile;
184
- // make sure labelInterval doesn't exceed the max number of cells
185
- if (tDateProfile.labelInterval) {
186
- const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
187
- if (labelCnt > internal_cjs.config.MAX_TIMELINE_SLOTS) {
188
- console.warn('slotLabelInterval results in too many cells');
189
- tDateProfile.labelInterval = null;
190
- }
191
- }
192
- // make sure slotDuration doesn't exceed the maximum number of cells
193
- if (tDateProfile.slotDuration) {
194
- const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
195
- if (slotCnt > internal_cjs.config.MAX_TIMELINE_SLOTS) {
196
- console.warn('slotDuration results in too many cells');
197
- tDateProfile.slotDuration = null;
198
- }
199
- }
200
- // make sure labelInterval is a multiple of slotDuration
201
- if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
202
- const slotsPerLabel = internal_cjs.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
203
- if (slotsPerLabel === null || slotsPerLabel < 1) {
204
- console.warn('slotLabelInterval must be a multiple of slotDuration');
205
- tDateProfile.slotDuration = null;
206
- }
207
- }
208
- }
209
- function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
210
- const { currentRange } = dateProfile;
211
- let { labelInterval } = tDateProfile;
212
- if (!labelInterval) {
213
- // compute based off the slot duration
214
- // find the largest label interval with an acceptable slots-per-label
215
- let input;
216
- if (tDateProfile.slotDuration) {
217
- for (input of STOCK_SUB_DURATIONS) {
218
- const tryLabelInterval = internal_cjs.createDuration(input);
219
- const slotsPerLabel = internal_cjs.wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
220
- if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
221
- labelInterval = tryLabelInterval;
222
- break;
223
- }
224
- }
225
- // use the slot duration as a last resort
226
- if (!labelInterval) {
227
- labelInterval = tDateProfile.slotDuration;
228
- }
229
- // compute based off the view's duration
230
- // find the largest label interval that yields the minimum number of labels
231
- }
232
- else {
233
- for (input of STOCK_SUB_DURATIONS) {
234
- labelInterval = internal_cjs.createDuration(input);
235
- const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
236
- if (labelCnt >= MIN_AUTO_LABELS) {
237
- break;
238
- }
239
- }
240
- }
241
- tDateProfile.labelInterval = labelInterval;
242
- }
243
- return labelInterval;
244
- }
245
- function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
246
- const { currentRange } = dateProfile;
247
- let { slotDuration } = tDateProfile;
248
- if (!slotDuration) {
249
- const labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
250
- // compute based off the label interval
251
- // find the largest slot duration that is different from labelInterval, but still acceptable
252
- for (let input of STOCK_SUB_DURATIONS) {
253
- const trySlotDuration = internal_cjs.createDuration(input);
254
- const slotsPerLabel = internal_cjs.wholeDivideDurations(labelInterval, trySlotDuration);
255
- if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
256
- slotDuration = trySlotDuration;
257
- break;
258
- }
259
- }
260
- // only allow the value if it won't exceed the view's # of slots limit
261
- if (slotDuration) {
262
- const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
263
- if (slotCnt > MAX_AUTO_CELLS) {
264
- slotDuration = null;
265
- }
266
- }
267
- // use the label interval as a last resort
268
- if (!slotDuration) {
269
- slotDuration = labelInterval;
270
- }
271
- tDateProfile.slotDuration = slotDuration;
272
- }
273
- return slotDuration;
274
- }
275
- function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
276
- let format1;
277
- let format2;
278
- const { labelInterval } = tDateProfile;
279
- let unit = internal_cjs.greatestDurationDenominator(labelInterval).unit;
280
- const weekNumbersVisible = allOptions.weekNumbers;
281
- let format0 = (format1 = (format2 = null));
282
- // NOTE: weekNumber computation function wont work
283
- if ((unit === 'week') && !weekNumbersVisible) {
284
- unit = 'day';
285
- }
286
- switch (unit) {
287
- case 'year':
288
- format0 = { year: 'numeric' }; // '2015'
289
- break;
290
- case 'month':
291
- if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
292
- format0 = { year: 'numeric' }; // '2015'
293
- }
294
- format1 = { month: 'short' }; // 'Jan'
295
- break;
296
- case 'week':
297
- if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
298
- format0 = { year: 'numeric' }; // '2015'
299
- }
300
- format1 = { week: 'narrow' }; // 'Wk4'
301
- break;
302
- case 'day':
303
- if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
304
- format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
305
- }
306
- else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
307
- format0 = { month: 'long' }; // 'January'
308
- }
309
- if (weekNumbersVisible) {
310
- format1 = { week: 'short' }; // 'Wk 4'
311
- }
312
- format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
313
- break;
314
- case 'hour':
315
- if (weekNumbersVisible) {
316
- format0 = { week: 'short' }; // 'Wk 4'
317
- }
318
- if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
319
- format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
320
- }
321
- format2 = {
322
- hour: 'numeric',
323
- minute: '2-digit',
324
- omitZeroMinute: true,
325
- meridiem: 'short',
326
- };
327
- break;
328
- case 'minute':
329
- // sufficiently large number of different minute cells?
330
- if ((internal_cjs.asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
331
- format0 = {
332
- hour: 'numeric',
333
- meridiem: 'short',
334
- };
335
- format1 = (params) => (':' + internal_cjs.padStart(params.date.minute, 2) // ':30'
336
- );
337
- }
338
- else {
339
- format0 = {
340
- hour: 'numeric',
341
- minute: 'numeric',
342
- meridiem: 'short',
343
- };
344
- }
345
- break;
346
- case 'second':
347
- // sufficiently large number of different second cells?
348
- if ((internal_cjs.asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
349
- format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
350
- format1 = (params) => (':' + internal_cjs.padStart(params.date.second, 2) // ':30'
351
- );
352
- }
353
- else {
354
- format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
355
- }
356
- break;
357
- case 'millisecond':
358
- format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
359
- format1 = (params) => ('.' + internal_cjs.padStart(params.millisecond, 3));
360
- break;
361
- }
362
- return [].concat(format0 || [], format1 || [], format2 || []);
363
- }
364
- // Compute the number of the give units in the "current" range.
365
- // Won't go more precise than days.
366
- // Will return `0` if there's not a clean whole interval.
367
- function currentRangeAs(unit, dateProfile, dateEnv) {
368
- let range = dateProfile.currentRange;
369
- let res = null;
370
- if (unit === 'years') {
371
- res = dateEnv.diffWholeYears(range.start, range.end);
372
- }
373
- else if (unit === 'months') {
374
- res = dateEnv.diffWholeMonths(range.start, range.end);
375
- }
376
- else if (unit === 'weeks') {
377
- res = dateEnv.diffWholeMonths(range.start, range.end);
378
- }
379
- else if (unit === 'days') {
380
- res = internal_cjs.diffWholeDays(range.start, range.end);
381
- }
382
- return res || 0;
383
- }
384
- function buildIsWeekStarts(tDateProfile, dateEnv) {
385
- let { slotDates, emphasizeWeeks } = tDateProfile;
386
- let prevWeekNumber = null;
387
- let isWeekStarts = [];
388
- for (let slotDate of slotDates) {
389
- let weekNumber = dateEnv.computeWeekNumber(slotDate);
390
- let isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
391
- prevWeekNumber = weekNumber;
392
- isWeekStarts.push(isWeekStart);
393
- }
394
- return isWeekStarts;
395
- }
396
- function buildCellRows(tDateProfile, dateEnv) {
397
- let slotDates = tDateProfile.slotDates;
398
- let formats = tDateProfile.headerFormats;
399
- let cellRows = formats.map(() => []); // indexed by row,col
400
- let slotAsDays = internal_cjs.asCleanDays(tDateProfile.slotDuration);
401
- let guessedSlotUnit = slotAsDays === 7 ? 'week' :
402
- slotAsDays === 1 ? 'day' :
403
- null;
404
- // specifically for navclicks
405
- let rowUnitsFromFormats = formats.map((format) => (format.getLargestUnit ? format.getLargestUnit() : null));
406
- // builds cellRows and slotCells
407
- for (let i = 0; i < slotDates.length; i += 1) {
408
- let date = slotDates[i];
409
- let isWeekStart = tDateProfile.isWeekStarts[i];
410
- for (let row = 0; row < formats.length; row += 1) {
411
- let format = formats[row];
412
- let rowCells = cellRows[row];
413
- let leadingCell = rowCells[rowCells.length - 1];
414
- let isLastRow = row === formats.length - 1;
415
- let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
416
- let newCell = null;
417
- let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
418
- if (isSuperRow) {
419
- let text = dateEnv.format(date, format);
420
- if (!leadingCell || (leadingCell.text !== text)) {
421
- newCell = buildCellObject(date, text, rowUnit);
422
- }
423
- else {
424
- leadingCell.colspan += 1;
425
- }
426
- }
427
- else if (!leadingCell ||
428
- internal_cjs.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
429
- let text = dateEnv.format(date, format);
430
- newCell = buildCellObject(date, text, rowUnit);
431
- }
432
- else {
433
- leadingCell.colspan += 1;
434
- }
435
- if (newCell) {
436
- newCell.weekStart = isWeekStart;
437
- rowCells.push(newCell);
438
- }
439
- }
440
- }
441
- return cellRows;
442
- }
443
- function buildCellObject(date, text, rowUnit) {
444
- return { date, text, rowUnit, colspan: 1, isWeekStart: false };
445
- }
446
-
447
- class TimelineSlatCell extends internal_cjs.BaseComponent {
448
- constructor() {
449
- super(...arguments);
450
- // ref
451
- this.innerElRef = preact_cjs.createRef();
452
- }
453
- render() {
454
- let { props, context } = this;
455
- let { dateEnv, options } = context;
456
- let { date, tDateProfile, isEm } = props;
457
- let dateMeta = internal_cjs.getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
458
- let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
459
- return (preact_cjs.createElement(internal_cjs.ContentContainer, { tag: "div",
460
- // fc-align-start shrinks width of InnerContent
461
- // TODO: document this semantic className fc-timeline-slot-em
462
- className: internal_cjs.joinClassNames('fc-timeline-slot', isEm && 'fc-timeline-slot-em', tDateProfile.isTimeScale && (internal_cjs.isInt(dateEnv.countDurationsBetween(// best to do this here?
463
- tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
464
- 'fc-timeline-slot-major' :
465
- 'fc-timeline-slot-minor'), 'fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start', props.borderStart && 'fc-border-s', props.isDay ?
466
- internal_cjs.getDayClassName(dateMeta) :
467
- internal_cjs.getSlotClassName(dateMeta)), attrs: {
468
- 'data-date': dateEnv.formatIso(date, {
469
- omitTimeZoneOffset: true,
470
- omitTime: !tDateProfile.isTimeScale,
471
- }),
472
- }, style: {
473
- width: props.width,
474
- }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "div", className: 'fc-cell-inner', elRef: this.innerElRef }))));
475
- }
476
- componentDidMount() {
477
- const innerEl = this.innerElRef.current;
478
- this.disconnectInnerWidth = internal_cjs.watchWidth(innerEl, (width) => {
479
- internal_cjs.setRef(this.props.innerWidthRef, width);
480
- });
481
- }
482
- componentWillUnmount() {
483
- this.disconnectInnerWidth();
484
- internal_cjs.setRef(this.props.innerWidthRef, null);
485
- }
486
- }
487
-
488
- class TimelineSlats extends internal_cjs.BaseComponent {
489
- constructor() {
490
- super(...arguments);
491
- this.innerWidthRefMap = new internal_cjs.RefMap(() => {
492
- internal_cjs.afterSize(this.handleInnerWidths);
493
- });
494
- this.handleInnerWidths = () => {
495
- const innerWidthMap = this.innerWidthRefMap.current;
496
- let max = 0;
497
- for (const innerWidth of innerWidthMap.values()) {
498
- max = Math.max(max, innerWidth);
499
- }
500
- // TODO: check to see if changed before firing ref!? YES. do in other places too
501
- internal_cjs.setRef(this.props.innerWidthRef, max);
502
- };
503
- }
504
- render() {
505
- let { props, innerWidthRefMap } = this;
506
- let { tDateProfile, slotWidth } = props;
507
- let { slotDates, isWeekStarts } = tDateProfile;
508
- let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
509
- return (preact_cjs.createElement("div", { className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
510
- let key = slotDate.toISOString();
511
- return (preact_cjs.createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isEm: isWeekStarts[i], isDay: isDay, borderStart: Boolean(i),
512
- // ref
513
- innerWidthRef: innerWidthRefMap.createRef(key),
514
- // dimensions
515
- width: slotWidth }));
516
- })));
517
- }
518
- }
519
-
520
- /*
521
- TODO: rename this file!
522
- */
523
- // returned value is between 0 and the number of snaps
524
- function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
525
- let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
526
- if (snapDiff < 0) {
527
- return 0;
528
- }
529
- if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
530
- return tDateProfile.snapCnt;
531
- }
532
- let snapDiffInt = Math.floor(snapDiff);
533
- let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
534
- if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
535
- snapCoverage += snapDiff - snapDiffInt; // add the remainder
536
- }
537
- else {
538
- // a fractional value, meaning the date is not visible
539
- // always round up in this case. works for start AND end dates in a range.
540
- snapCoverage = Math.ceil(snapCoverage);
541
- }
542
- return snapCoverage;
543
- }
544
- /*
545
- TODO: DRY up with elsewhere?
546
- */
547
- function horizontalsToCss(hcoord, isRtl) {
548
- if (!hcoord) {
549
- return {};
550
- }
551
- if (isRtl) {
552
- return { right: hcoord.start, width: hcoord.size };
553
- }
554
- else {
555
- return { left: hcoord.start, width: hcoord.size };
556
- }
557
- }
558
- function horizontalCoordToCss(start, isRtl) {
559
- if (isRtl) {
560
- return { right: start };
561
- }
562
- else {
563
- return { left: start };
564
- }
565
- }
566
-
567
- function createVerticalStyle(props) {
568
- if (props) {
569
- return {
570
- top: props.start,
571
- height: props.size,
572
- };
573
- }
574
- }
575
- function createHorizontalStyle(// TODO: DRY up?
576
- props, isRtl) {
577
- if (props) {
578
- return {
579
- [isRtl ? 'right' : 'left']: props.start,
580
- width: props.size,
581
- };
582
- }
583
- }
584
- // Timeline-specific
585
- // -------------------------------------------------------------------------------------------------
586
- const MIN_SLOT_WIDTH = 30; // for real
587
- /*
588
- TODO: DRY with computeSlatHeight?
589
- */
590
- function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
591
- if (labelInnerWidth == null || viewportWidth == null) {
592
- return [undefined, undefined, false];
593
- }
594
- slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
595
- const slatTryWidth = viewportWidth / slatCnt;
596
- let slatLiquid;
597
- let slatWidth;
598
- if (slatTryWidth >= slatMinWidth) {
599
- slatLiquid = true;
600
- slatWidth = slatTryWidth;
601
- }
602
- else {
603
- slatLiquid = false;
604
- slatWidth = Math.max(slatMinWidth, slatTryWidth);
605
- }
606
- return [slatWidth * slatCnt, slatWidth, slatLiquid];
607
- }
608
- function timeToCoord(// pixels
609
- time, dateEnv, dateProfile, tDateProfile, slowWidth) {
610
- let date = dateEnv.add(dateProfile.activeRange.start, time);
611
- if (!tDateProfile.isTimeScale) {
612
- date = internal_cjs.startOfDay(date);
613
- }
614
- return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
615
- }
616
- function dateToCoord(// pixels
617
- date, dateEnv, tDateProfile, slotWidth) {
618
- let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
619
- let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
620
- return slotCoverage * slotWidth;
621
- }
622
- /*
623
- returned value is between 0 and the number of snaps
624
- */
625
- function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
626
- let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
627
- if (snapDiff < 0) {
628
- return 0;
629
- }
630
- if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
631
- return tDateProfile.snapCnt;
632
- }
633
- let snapDiffInt = Math.floor(snapDiff);
634
- let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
635
- if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
636
- snapCoverage += snapDiff - snapDiffInt; // add the remainder
637
- }
638
- else {
639
- // a fractional value, meaning the date is not visible
640
- // always round up in this case. works for start AND end dates in a range.
641
- snapCoverage = Math.ceil(snapCoverage);
642
- }
643
- return snapCoverage;
644
- }
645
-
646
- function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
647
- const res = {};
648
- for (const seg of segs) {
649
- res[internal_cjs.getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
650
- }
651
- return res;
652
- }
653
- function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
654
- const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
655
- const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
656
- let size = endCoord - startCoord;
657
- if (segMinWidth) {
658
- size = Math.max(size, segMinWidth);
659
- }
660
- return { start: startCoord, size };
661
- }
662
- function computeFgSegPlacements(// mostly horizontals
663
- segs, segHorizontals, // TODO: use Map
664
- segHeights, // keyed by instanceId
665
- hiddenGroupHeights, strictOrder, maxDepth) {
666
- const segRanges = [];
667
- // isn't it true that there will either be ALL hcoords or NONE? can optimize
668
- for (const seg of segs) {
669
- const hcoords = segHorizontals[internal_cjs.getEventKey(seg)];
670
- if (hcoords) {
671
- segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
672
- }
673
- }
674
- const hierarchy = new internal_cjs.SegHierarchy(segRanges, (seg) => segHeights.get(internal_cjs.getEventKey(seg)), strictOrder, undefined, // maxCoord
675
- maxDepth);
676
- const segTops = new Map();
677
- hierarchy.traverseSegs((seg, segTop) => {
678
- segTops.set(internal_cjs.getEventKey(seg), segTop);
679
- });
680
- const { hiddenSegs } = hierarchy;
681
- let totalHeight = 0;
682
- for (const segRange of segRanges) {
683
- const segKey = internal_cjs.getEventKey(segRange);
684
- const segHeight = segHeights.get(segKey);
685
- const segTop = segTops.get(segKey);
686
- if (segHeight != null) {
687
- if (segTop != null) {
688
- totalHeight = Math.max(totalHeight, segTop + segHeight);
689
- }
690
- }
691
- }
692
- const hiddenGroups = internal_cjs.groupIntersectingSegs(hiddenSegs);
693
- const hiddenGroupTops = new Map();
694
- // HACK for hiddenGroup findInsertion() call
695
- hierarchy.strictOrder = true;
696
- for (const hiddenGroup of hiddenGroups) {
697
- const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
698
- const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
699
- hiddenGroupTops.set(hiddenGroup.key, top);
700
- totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
701
- }
702
- return [
703
- segTops,
704
- hiddenGroups,
705
- hiddenGroupTops,
706
- totalHeight,
707
- ];
708
- }
709
-
710
- class TimelineLaneBg extends internal_cjs.BaseComponent {
711
- render() {
712
- let { props } = this;
713
- let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
714
- return (preact_cjs.createElement(preact_cjs.Fragment, null,
715
- this.renderSegs(props.businessHourSegs || [], 'non-business'),
716
- this.renderSegs(props.bgEventSegs || [], 'bg-event'),
717
- this.renderSegs(highlightSeg, 'highlight')));
718
- }
719
- renderSegs(segs, fillType) {
720
- let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
721
- let { dateEnv, isRtl } = this.context;
722
- return (preact_cjs.createElement(preact_cjs.Fragment, null, segs.map((seg) => {
723
- let hStyle; // TODO
724
- if (slotWidth != null) {
725
- let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
726
- hStyle = horizontalsToCss(segHorizontal, isRtl);
727
- }
728
- return (preact_cjs.createElement("div", { key: internal_cjs.buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
729
- preact_cjs.createElement(internal_cjs.BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, internal_cjs.getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (internal_cjs.renderFill(fillType))));
730
- })));
731
- }
732
- }
733
-
734
- class TimelineLaneSlicer extends internal_cjs.Slicer {
735
- sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
736
- let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
737
- let segs = [];
738
- // protect against when the span is entirely in an invalid date region
739
- if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
740
- < computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
741
- // intersect the footprint's range with the grid's range
742
- let slicedRange = internal_cjs.intersectRanges(normalRange, tDateProfile.normalizedRange);
743
- if (slicedRange) {
744
- segs.push({
745
- startDate: slicedRange.start,
746
- endDate: slicedRange.end,
747
- isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
748
- && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
749
- isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
750
- && isValidDate(internal_cjs.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
751
- });
752
- }
753
- }
754
- return segs;
755
- }
756
- }
757
-
758
- const DEFAULT_TIME_FORMAT = internal_cjs.createFormatter({
759
- hour: 'numeric',
760
- minute: '2-digit',
761
- omitZeroMinute: true,
762
- meridiem: 'narrow',
763
- });
764
- class TimelineEvent extends internal_cjs.BaseComponent {
765
- render() {
766
- let { props, context } = this;
767
- let { options } = context;
768
- return (preact_cjs.createElement(internal_cjs.StandardEvent, Object.assign({}, props, { className: internal_cjs.joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
769
- && 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
770
- }
771
- }
772
-
773
- class TimelineLaneMoreLink extends internal_cjs.BaseComponent {
774
- render() {
775
- let { props } = this;
776
- let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
777
- let dateSpanProps = resourceId ? { resourceId } : {};
778
- return (preact_cjs.createElement(internal_cjs.MoreLinkContainer, { className: 'fc-timeline-more-link', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, popoverContent: () => (preact_cjs.createElement(preact_cjs.Fragment, null, hiddenSegs.map((seg) => {
779
- let { eventRange } = seg;
780
- let instanceId = eventRange.instance.instanceId;
781
- return (preact_cjs.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
782
- preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection }, internal_cjs.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
783
- }))) }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
784
- }
785
- }
786
-
787
- /*
788
- TODO: make DRY with other Event Harnesses
789
- */
790
- class TimelineEventHarness extends preact_cjs.Component {
791
- constructor() {
792
- super(...arguments);
793
- // ref
794
- this.rootElRef = preact_cjs.createRef();
795
- }
796
- render() {
797
- const { props } = this;
798
- return (preact_cjs.createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
799
- }
800
- componentDidMount() {
801
- const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
802
- this.disconnectHeight = internal_cjs.watchHeight(rootEl, (height) => {
803
- internal_cjs.setRef(this.props.heightRef, height);
804
- });
805
- }
806
- componentWillUnmount() {
807
- this.disconnectHeight();
808
- internal_cjs.setRef(this.props.heightRef, null);
809
- }
810
- }
811
-
812
- /*
813
- TODO: split TimelineLaneBg and TimelineLaneFg?
814
- */
815
- class TimelineLane extends internal_cjs.BaseComponent {
816
- constructor() {
817
- super(...arguments);
818
- // memo
819
- this.sortEventSegs = internal_cjs.memoize(internal_cjs.sortEventSegs);
820
- // refs
821
- this.segHeightRefMap = new internal_cjs.RefMap(() => {
822
- internal_cjs.afterSize(this.handleSegHeights);
823
- });
824
- this.moreLinkHeightRefMap = new internal_cjs.RefMap(() => {
825
- internal_cjs.afterSize(this.handleMoreLinkHeights);
826
- });
827
- // internal
828
- this.slicer = new TimelineLaneSlicer();
829
- this.handleMoreLinkHeights = () => {
830
- this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
831
- };
832
- this.handleSegHeights = () => {
833
- this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
834
- };
835
- }
836
- /*
837
- TODO: lots of memoization needed here!
838
- */
839
- render() {
840
- let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
841
- let { options } = context;
842
- let { dateProfile, tDateProfile } = props;
843
- let slicedProps = this.slicer.sliceProps(props, dateProfile, tDateProfile.isTimeScale ? null : props.nextDayThreshold, context, // wish we didn't have to pass in the rest of the args...
844
- dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
845
- let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
846
- (slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
847
- [];
848
- let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
849
- let fgSegHorizontals = props.slotWidth != null
850
- ? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
851
- : {};
852
- let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
853
- let forcedInvisibleMap = // TODO: more convenient/DRY
854
- (slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
855
- (slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
856
- {};
857
- return (preact_cjs.createElement(preact_cjs.Fragment, null,
858
- preact_cjs.createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
859
- // content
860
- bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
861
- // dimensions
862
- slotWidth: props.slotWidth }),
863
- preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
864
- ? 'fc-timeline-events-overlap-disabled'
865
- : 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
866
- this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
867
- false, // isResizing
868
- false),
869
- this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
870
- ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
871
- : {}, fgSegTops, {}, // forcedInvisibleMap
872
- [], // hiddenGroups
873
- new Map(), // hiddenGroupTops
874
- Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
875
- }
876
- renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
877
- let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
878
- let isMirror = isDragging || isResizing || isDateSelecting;
879
- return (preact_cjs.createElement(preact_cjs.Fragment, null,
880
- segs.map((seg) => {
881
- const { eventRange } = seg;
882
- const { instanceId } = eventRange.instance;
883
- const segTop = segTops.get(instanceId);
884
- const segHorizontal = segHorizontals[instanceId];
885
- const isVisible = isMirror ||
886
- (segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
887
- return (preact_cjs.createElement(TimelineEventHarness, { key: instanceId, style: Object.assign({ visibility: isVisible ? '' : 'hidden', top: segTop || 0 }, horizontalsToCss(segHorizontal, context.isRtl)), heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
888
- preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection /* TODO: bad for mirror? */ }, internal_cjs.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
889
- }),
890
- hiddenGroups.map((hiddenGroup) => (preact_cjs.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
891
- start: hiddenGroup.start,
892
- size: hiddenGroup.end - hiddenGroup.start
893
- }, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
894
- preact_cjs.createElement(TimelineLaneMoreLink, { hiddenSegs: hiddenGroup.segs, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventSelection: props.eventSelection, resourceId: props.resourceId, forcedInvisibleMap: forcedInvisibleMap }))))));
895
- }
896
- }
897
-
898
- class TimelineHeaderCell extends internal_cjs.BaseComponent {
899
- constructor() {
900
- super(...arguments);
901
- // memo
902
- this.refineRenderProps = internal_cjs.memoizeObjArg(refineRenderProps);
903
- this.buildCellNavLinkAttrs = internal_cjs.memoize(buildCellNavLinkAttrs);
904
- // ref
905
- this.innerElRef = preact_cjs.createRef();
906
- }
907
- render() {
908
- let { props, context } = this;
909
- let { dateEnv, options } = context;
910
- let { cell, dateProfile, tDateProfile } = props;
911
- // the cell.rowUnit is f'd
912
- // giving 'month' for a 3-day view
913
- // workaround: to infer day, do NOT time
914
- let dateMeta = internal_cjs.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
915
- let renderProps = this.refineRenderProps({
916
- level: props.rowLevel,
917
- dateMarker: cell.date,
918
- text: cell.text,
919
- dateEnv: context.dateEnv,
920
- viewApi: context.viewApi,
921
- });
922
- return (preact_cjs.createElement(internal_cjs.ContentContainer, { tag: "div", className: internal_cjs.joinClassNames('fc-timeline-slot-label fc-timeline-slot', cell.isWeekStart && 'fc-timeline-slot-em', // TODO: document this semantic className
923
- 'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
924
- // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
925
- cell.rowUnit === 'time' ?
926
- internal_cjs.getSlotClassName(dateMeta) :
927
- internal_cjs.getDayClassName(dateMeta)), attrs: {
928
- 'data-date': dateEnv.formatIso(cell.date, {
929
- omitTime: !tDateProfile.isTimeScale,
930
- omitTimeZoneOffset: true,
931
- }),
932
- }, style: {
933
- width: props.slotWidth != null
934
- ? props.slotWidth * cell.colspan
935
- : undefined,
936
- }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "a", attrs: this.buildCellNavLinkAttrs(context, cell.date, cell.rowUnit), className: internal_cjs.joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
937
- }
938
- componentDidMount() {
939
- const { props } = this;
940
- const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
941
- this.detachSize = internal_cjs.watchSize(innerEl, (width, height) => {
942
- internal_cjs.setRef(props.innerWidthRef, width);
943
- internal_cjs.setRef(props.innerHeightRef, height);
944
- // HACK for sticky-centering
945
- innerEl.style.left = innerEl.style.right =
946
- (props.isCentered && props.isSticky)
947
- ? `calc(50% - ${width / 2}px)`
948
- : '';
949
- });
950
- }
951
- componentWillUnmount() {
952
- const { props } = this;
953
- this.detachSize();
954
- internal_cjs.setRef(props.innerWidthRef, null);
955
- internal_cjs.setRef(props.innerHeightRef, null);
956
- }
957
- }
958
- // Utils
959
- // -------------------------------------------------------------------------------------------------
960
- function buildCellNavLinkAttrs(context, cellDate, rowUnit) {
961
- return (rowUnit && rowUnit !== 'time')
962
- ? internal_cjs.buildNavLinkAttrs(context, cellDate, rowUnit)
963
- : {};
964
- }
965
- function renderInnerContent(renderProps) {
966
- return renderProps.text;
967
- }
968
- function refineRenderProps(input) {
969
- return {
970
- level: input.level,
971
- date: input.dateEnv.toDate(input.dateMarker),
972
- view: input.viewApi,
973
- text: input.text,
974
- };
975
- }
976
-
977
- class TimelineHeaderRow extends internal_cjs.BaseComponent {
978
- constructor() {
979
- super(...arguments);
980
- // refs
981
- this.innerWidthRefMap = new internal_cjs.RefMap(() => {
982
- internal_cjs.afterSize(this.handleInnerWidths);
983
- });
984
- this.innerHeightRefMap = new internal_cjs.RefMap(() => {
985
- internal_cjs.afterSize(this.handleInnerHeights);
986
- });
987
- this.handleInnerWidths = () => {
988
- const innerWidthMap = this.innerWidthRefMap.current;
989
- let max = 0;
990
- for (const innerWidth of innerWidthMap.values()) {
991
- max = Math.max(max, innerWidth);
992
- }
993
- // TODO: ensure not equal?
994
- internal_cjs.setRef(this.props.innerWidthRef, max);
995
- };
996
- this.handleInnerHeights = () => {
997
- const innerHeightMap = this.innerHeightRefMap.current;
998
- let max = 0;
999
- for (const innerHeight of innerHeightMap.values()) {
1000
- max = Math.max(max, innerHeight);
1001
- }
1002
- // TODO: ensure not equal?
1003
- internal_cjs.setRef(this.props.innerHeighRef, max);
1004
- };
1005
- }
1006
- render() {
1007
- const { props, innerWidthRefMap, innerHeightRefMap } = this;
1008
- const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
1009
- const isSticky = !props.isLastRow;
1010
- return (preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
1011
- !props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
1012
- // TODO: make this part of the cell obj?
1013
- // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
1014
- // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
1015
- // TODO: use rowUnit to key the Row itself?
1016
- const key = cell.rowUnit + ':' + cell.date.toISOString();
1017
- return (preact_cjs.createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isCentered: isCentered, isSticky: isSticky, borderStart: Boolean(cellI),
1018
- // refs
1019
- innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
1020
- // dimensions
1021
- slotWidth: props.slotWidth }));
1022
- })));
1023
- }
1024
- componentWillUnmount() {
1025
- internal_cjs.setRef(this.props.innerWidthRef, null);
1026
- internal_cjs.setRef(this.props.innerHeighRef, null);
1027
- }
1028
- }
1029
-
1030
- class TimelineNowIndicatorLine extends internal_cjs.BaseComponent {
1031
- render() {
1032
- const { props, context } = this;
1033
- return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
1034
- preact_cjs.createElement(internal_cjs.NowIndicatorContainer // TODO: make separate component?
1035
- , { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
1036
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1037
- : {}, isAxis: false, date: props.nowDate })));
1038
- }
1039
- }
1040
-
1041
- class TimelineNowIndicatorArrow extends internal_cjs.BaseComponent {
1042
- render() {
1043
- const { props, context } = this;
1044
- return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
1045
- preact_cjs.createElement(internal_cjs.NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
1046
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1047
- : {}, isAxis: true, date: props.nowDate })));
1048
- }
1049
- }
1050
-
1051
- class TimelineView extends internal_cjs.DateComponent {
1052
- constructor() {
1053
- super(...arguments);
1054
- // memoized
1055
- this.buildTimelineDateProfile = internal_cjs.memoize(buildTimelineDateProfile);
1056
- this.computeSlotWidth = internal_cjs.memoize(computeSlotWidth);
1057
- // refs
1058
- this.headerScrollerRef = preact_cjs.createRef();
1059
- this.bodyScrollerRef = preact_cjs.createRef();
1060
- this.footerScrollerRef = preact_cjs.createRef();
1061
- this.headerRowInnerWidthMap = new internal_cjs.RefMap(() => {
1062
- internal_cjs.afterSize(this.handleSlotInnerWidths);
1063
- });
1064
- this.scrollTime = null;
1065
- // Sizing
1066
- // -----------------------------------------------------------------------------------------------
1067
- this.handleBodySlotInnerWidth = (innerWidth) => {
1068
- this.bodySlotInnerWidth = innerWidth;
1069
- internal_cjs.afterSize(this.handleSlotInnerWidths);
1070
- };
1071
- this.handleSlotInnerWidths = () => {
1072
- const { state } = this;
1073
- const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
1074
- if (state.slotInnerWidth !== slotInnerWidth) {
1075
- this.setState({ slotInnerWidth });
1076
- }
1077
- };
1078
- this.handleClientWidth = (clientWidth) => {
1079
- this.setState({
1080
- clientWidth,
1081
- });
1082
- };
1083
- this.handleEndScrollbarWidth = (endScrollbarWidth) => {
1084
- this.setState({
1085
- endScrollbarWidth
1086
- });
1087
- };
1088
- this.handleTimeScroll = (scrollTime) => {
1089
- this.scrollTime = scrollTime;
1090
- this.updateScroll();
1091
- };
1092
- this.updateScroll = () => {
1093
- const { props, context, tDateProfile, scrollTime, slotWidth } = this;
1094
- if (scrollTime != null && slotWidth != null) {
1095
- let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
1096
- if (x) {
1097
- x += 1; // overcome border. TODO: DRY this up
1098
- }
1099
- this.syncedScroller.scrollTo({ x });
1100
- }
1101
- };
1102
- this.clearScroll = () => {
1103
- this.scrollTime = null;
1104
- };
1105
- // Hit System
1106
- // -----------------------------------------------------------------------------------------------
1107
- this.handeBodyEl = (el) => {
1108
- this.bodyEl = el;
1109
- if (el) {
1110
- this.context.registerInteractiveComponent(this, { el });
1111
- }
1112
- else {
1113
- this.context.unregisterInteractiveComponent(this);
1114
- }
1115
- };
1116
- }
1117
- render() {
1118
- const { props, state, context } = this;
1119
- const { options } = context;
1120
- /* date */
1121
- const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
1122
- const { cellRows } = tDateProfile;
1123
- const timerUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
1124
- /* table settings */
1125
- const verticalScrolling = !props.forPrint && !internal_cjs.getIsHeightAuto(options);
1126
- const stickyHeaderDates = !props.forPrint && internal_cjs.getStickyHeaderDates(options);
1127
- const stickyFooterScrollbar = !props.forPrint && internal_cjs.getStickyFooterScrollbar(options);
1128
- /* table positions */
1129
- const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
1130
- state.clientWidth);
1131
- this.slotWidth = slotWidth;
1132
- return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
1133
- const enableNowIndicator = // TODO: DRY
1134
- options.nowIndicator &&
1135
- slotWidth != null &&
1136
- internal_cjs.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
1137
- return (preact_cjs.createElement(internal_cjs.ViewContainer, { viewSpec: context.viewSpec, className: internal_cjs.joinClassNames('fc-timeline fc-border',
1138
- // HACK for Safari print-mode, where fc-scroller-no-bars won't take effect for
1139
- // the below Scrollers if they have liquid flex height
1140
- !props.forPrint && 'fc-flex-col') },
1141
- preact_cjs.createElement(internal_cjs.Scroller, { horizontal: true, hideScrollbars: true, className: internal_cjs.joinClassNames('fc-timeline-header fc-flex-row fc-border-b', stickyHeaderDates && 'fc-table-header-sticky'), ref: this.headerScrollerRef },
1142
- preact_cjs.createElement("div", {
1143
- // TODO: DRY
1144
- className: internal_cjs.joinClassNames('fc-rel', // origin for now-indicator
1145
- canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
1146
- cellRows.map((cells, rowLevel) => {
1147
- const isLast = rowLevel === cellRows.length - 1;
1148
- return (preact_cjs.createElement(TimelineHeaderRow, { key: rowLevel, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, isLastRow: isLast, cells: cells, slotWidth: slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowLevel) }));
1149
- }),
1150
- enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
1151
- Boolean(state.endScrollbarWidth) && (preact_cjs.createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
1152
- preact_cjs.createElement(internal_cjs.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: props.forPrint, className: internal_cjs.joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth },
1153
- preact_cjs.createElement("div", { className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
1154
- preact_cjs.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1155
- // ref
1156
- innerWidthRef: this.handleBodySlotInnerWidth,
1157
- // dimensions
1158
- slotWidth: slotWidth }),
1159
- preact_cjs.createElement(TimelineLane, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, nextDayThreshold: options.nextDayThreshold, eventStore: props.eventStore, eventUiBases: props.eventUiBases, businessHours: props.businessHours, dateSelection: props.dateSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection, slotWidth: slotWidth }),
1160
- enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
1161
- stickyFooterScrollbar && (preact_cjs.createElement(internal_cjs.Scroller, { ref: this.footerScrollerRef, horizontal: true },
1162
- preact_cjs.createElement("div", { style: { width: canvasWidth } })))));
1163
- }));
1164
- }
1165
- // Lifecycle
1166
- // -----------------------------------------------------------------------------------------------
1167
- componentDidMount() {
1168
- this.syncedScroller = new internal_cjs$1.ScrollerSyncer(true); // horizontal=true
1169
- this.updateSyncedScroller();
1170
- this.resetScroll();
1171
- this.context.emitter.on('_timeScrollRequest', this.handleTimeScroll);
1172
- this.syncedScroller.addScrollEndListener(this.clearScroll);
1173
- }
1174
- componentDidUpdate(prevProps) {
1175
- this.updateSyncedScroller();
1176
- if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
1177
- this.resetScroll();
1178
- }
1179
- else {
1180
- // TODO: inefficient to update so often
1181
- this.updateScroll();
1182
- }
1183
- }
1184
- componentWillUnmount() {
1185
- this.syncedScroller.destroy();
1186
- this.context.emitter.off('_timeScrollRequest', this.handleTimeScroll);
1187
- this.syncedScroller.removeScrollEndListener(this.clearScroll);
1188
- }
1189
- // Scrolling
1190
- // -----------------------------------------------------------------------------------------------
1191
- updateSyncedScroller() {
1192
- this.syncedScroller.handleChildren([
1193
- this.headerScrollerRef.current,
1194
- this.bodyScrollerRef.current,
1195
- this.footerScrollerRef.current
1196
- ]);
1197
- }
1198
- resetScroll() {
1199
- this.handleTimeScroll(this.context.options.scrollTime);
1200
- }
1201
- queryHit(positionLeft, positionTop, elWidth, elHeight) {
1202
- const { props, context, tDateProfile, slotWidth } = this;
1203
- const { dateEnv } = context;
1204
- if (slotWidth) {
1205
- const x = context.isRtl ? elWidth - positionLeft : positionLeft;
1206
- const slatIndex = Math.floor(x / slotWidth);
1207
- const slatX = slatIndex * slotWidth;
1208
- const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
1209
- const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
1210
- let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], internal_cjs.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
1211
- let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
1212
- // TODO: generalize this coord stuff to TimeGrid?
1213
- let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
1214
- let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
1215
- let endCoord = startCoord + snapWidth;
1216
- let left, right;
1217
- if (context.isRtl) {
1218
- left = elWidth - endCoord;
1219
- right = elWidth - startCoord;
1220
- }
1221
- else {
1222
- left = startCoord;
1223
- right = endCoord;
1224
- }
1225
- return {
1226
- dateProfile: props.dateProfile,
1227
- dateSpan: {
1228
- range: { start: startDate, end: endDate },
1229
- allDay: !tDateProfile.isTimeScale,
1230
- },
1231
- rect: {
1232
- left,
1233
- right,
1234
- top: 0,
1235
- bottom: elHeight,
1236
- },
1237
- // HACK. TODO: This is expensive to do every hit-query
1238
- dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
1239
- layer: 0,
1240
- };
1241
- }
1242
- return null;
1243
- }
1244
- }
1245
-
1246
- var css_248z = ".fc-timeline-slots{z-index:1}.fc-timeline-events{position:relative;z-index:2}.fc-timeline-slot-minor{border-style:dotted}.fc-timeline-events-overlap-enabled{padding-bottom:10px}.fc-timeline-event{border-radius:0;font-size:var(--fc-small-font-size);margin-bottom:1px}.fc-direction-ltr .fc-timeline-event.fc-event-end{margin-right:1px}.fc-direction-rtl .fc-timeline-event.fc-event-end{margin-left:1px}.fc-timeline-event .fc-event-inner{align-items:center;display:flex;flex-direction:row;padding:2px 1px}.fc-timeline-event-spacious .fc-event-inner{padding-bottom:5px;padding-top:5px}.fc-timeline-event .fc-event-time{font-weight:700}.fc-timeline-event .fc-event-time,.fc-timeline-event .fc-event-title{padding:0 2px}.fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-color:transparent #000;border-style:solid;border-width:5px;content:\"\";flex-grow:0;flex-shrink:0;height:0;margin:0 1px;opacity:.5;width:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before,.fc-direction-rtl .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after{border-left:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-direction-rtl .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-right:0}.fc-timeline-more-link{align-items:flex-start;background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;display:flex;flex-direction:column;font-size:var(--fc-small-font-size);padding:1px}.fc-direction-ltr .fc-timeline-more-link{margin-right:1px}.fc-direction-rtl .fc-timeline-more-link{margin-left:1px}.fc-timeline-more-link-inner{padding:2px}.fc-timeline-now-indicator-container{bottom:0;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;top:0;z-index:4}.fc-timeline-now-indicator-arrow{border-bottom-style:solid;border-bottom-width:0;border-color:var(--fc-now-indicator-color);border-left:5px solid transparent;border-right:5px solid transparent;border-top-style:solid;border-top-width:6px;height:0;margin:0 -5px;position:absolute;top:0;width:0}.fc-timeline-now-indicator-line{border-left:1px solid var(--fc-now-indicator-color);bottom:0;position:absolute;top:0}";
1247
- internal_cjs.injectStyles(css_248z);
1248
-
1249
- exports.TimelineHeaderRow = TimelineHeaderRow;
1250
- exports.TimelineLane = TimelineLane;
1251
- exports.TimelineLaneBg = TimelineLaneBg;
1252
- exports.TimelineLaneSlicer = TimelineLaneSlicer;
1253
- exports.TimelineNowIndicatorArrow = TimelineNowIndicatorArrow;
1254
- exports.TimelineNowIndicatorLine = TimelineNowIndicatorLine;
1255
- exports.TimelineSlats = TimelineSlats;
1256
- exports.TimelineView = TimelineView;
1257
- exports.buildTimelineDateProfile = buildTimelineDateProfile;
1258
- exports.computeSlotWidth = computeSlotWidth;
1259
- exports.createHorizontalStyle = createHorizontalStyle;
1260
- exports.createVerticalStyle = createVerticalStyle;
1261
- exports.timeToCoord = timeToCoord;