@fullcalendar/timeline 7.0.0-beta.4 → 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,1258 +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: Object.assign({ 'data-date': dateEnv.formatIso(date, {
468
- omitTimeZoneOffset: true,
469
- omitTime: !tDateProfile.isTimeScale,
470
- }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
471
- width: props.width,
472
- }, 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 }))));
473
- }
474
- componentDidMount() {
475
- const innerEl = this.innerElRef.current;
476
- this.disconnectInnerWidth = internal_cjs.watchWidth(innerEl, (width) => {
477
- internal_cjs.setRef(this.props.innerWidthRef, width);
478
- });
479
- }
480
- componentWillUnmount() {
481
- this.disconnectInnerWidth();
482
- internal_cjs.setRef(this.props.innerWidthRef, null);
483
- }
484
- }
485
-
486
- class TimelineSlats extends internal_cjs.BaseComponent {
487
- constructor() {
488
- super(...arguments);
489
- this.innerWidthRefMap = new internal_cjs.RefMap(() => {
490
- internal_cjs.afterSize(this.handleInnerWidths);
491
- });
492
- this.handleInnerWidths = () => {
493
- const innerWidthMap = this.innerWidthRefMap.current;
494
- let max = 0;
495
- for (const innerWidth of innerWidthMap.values()) {
496
- max = Math.max(max, innerWidth);
497
- }
498
- // TODO: check to see if changed before firing ref!? YES. do in other places too
499
- internal_cjs.setRef(this.props.innerWidthRef, max);
500
- };
501
- }
502
- render() {
503
- let { props, innerWidthRefMap } = this;
504
- let { tDateProfile, slotWidth } = props;
505
- let { slotDates, isWeekStarts } = tDateProfile;
506
- let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
507
- return (preact_cjs.createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
508
- let key = slotDate.toISOString();
509
- 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),
510
- // ref
511
- innerWidthRef: innerWidthRefMap.createRef(key),
512
- // dimensions
513
- width: slotWidth }));
514
- })));
515
- }
516
- }
517
-
518
- /*
519
- TODO: rename this file!
520
- */
521
- // returned value is between 0 and the number of snaps
522
- function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
523
- let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
524
- if (snapDiff < 0) {
525
- return 0;
526
- }
527
- if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
528
- return tDateProfile.snapCnt;
529
- }
530
- let snapDiffInt = Math.floor(snapDiff);
531
- let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
532
- if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
533
- snapCoverage += snapDiff - snapDiffInt; // add the remainder
534
- }
535
- else {
536
- // a fractional value, meaning the date is not visible
537
- // always round up in this case. works for start AND end dates in a range.
538
- snapCoverage = Math.ceil(snapCoverage);
539
- }
540
- return snapCoverage;
541
- }
542
- /*
543
- TODO: DRY up with elsewhere?
544
- */
545
- function horizontalsToCss(hcoord, isRtl) {
546
- if (!hcoord) {
547
- return {};
548
- }
549
- if (isRtl) {
550
- return { right: hcoord.start, width: hcoord.size };
551
- }
552
- else {
553
- return { left: hcoord.start, width: hcoord.size };
554
- }
555
- }
556
- function horizontalCoordToCss(start, isRtl) {
557
- if (isRtl) {
558
- return { right: start };
559
- }
560
- else {
561
- return { left: start };
562
- }
563
- }
564
-
565
- function createVerticalStyle(props) {
566
- if (props) {
567
- return {
568
- top: props.start,
569
- height: props.size,
570
- };
571
- }
572
- }
573
- function createHorizontalStyle(// TODO: DRY up?
574
- props, isRtl) {
575
- if (props) {
576
- return {
577
- [isRtl ? 'right' : 'left']: props.start,
578
- width: props.size,
579
- };
580
- }
581
- }
582
- // Timeline-specific
583
- // -------------------------------------------------------------------------------------------------
584
- const MIN_SLOT_WIDTH = 30; // for real
585
- /*
586
- TODO: DRY with computeSlatHeight?
587
- */
588
- function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
589
- if (labelInnerWidth == null || viewportWidth == null) {
590
- return [undefined, undefined, false];
591
- }
592
- slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
593
- const slatTryWidth = viewportWidth / slatCnt;
594
- let slatLiquid;
595
- let slatWidth;
596
- if (slatTryWidth >= slatMinWidth) {
597
- slatLiquid = true;
598
- slatWidth = slatTryWidth;
599
- }
600
- else {
601
- slatLiquid = false;
602
- slatWidth = Math.max(slatMinWidth, slatTryWidth);
603
- }
604
- return [slatWidth * slatCnt, slatWidth, slatLiquid];
605
- }
606
- function timeToCoord(// pixels
607
- time, dateEnv, dateProfile, tDateProfile, slowWidth) {
608
- let date = dateEnv.add(dateProfile.activeRange.start, time);
609
- if (!tDateProfile.isTimeScale) {
610
- date = internal_cjs.startOfDay(date);
611
- }
612
- return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
613
- }
614
- function dateToCoord(// pixels
615
- date, dateEnv, tDateProfile, slotWidth) {
616
- let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
617
- let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
618
- return slotCoverage * slotWidth;
619
- }
620
- /*
621
- returned value is between 0 and the number of snaps
622
- */
623
- function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
624
- let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
625
- if (snapDiff < 0) {
626
- return 0;
627
- }
628
- if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
629
- return tDateProfile.snapCnt;
630
- }
631
- let snapDiffInt = Math.floor(snapDiff);
632
- let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
633
- if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
634
- snapCoverage += snapDiff - snapDiffInt; // add the remainder
635
- }
636
- else {
637
- // a fractional value, meaning the date is not visible
638
- // always round up in this case. works for start AND end dates in a range.
639
- snapCoverage = Math.ceil(snapCoverage);
640
- }
641
- return snapCoverage;
642
- }
643
-
644
- function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
645
- const res = {};
646
- for (const seg of segs) {
647
- res[internal_cjs.getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
648
- }
649
- return res;
650
- }
651
- function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
652
- const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
653
- const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
654
- let size = endCoord - startCoord;
655
- if (segMinWidth) {
656
- size = Math.max(size, segMinWidth);
657
- }
658
- return { start: startCoord, size };
659
- }
660
- function computeFgSegPlacements(// mostly horizontals
661
- segs, segHorizontals, // TODO: use Map
662
- segHeights, // keyed by instanceId
663
- hiddenGroupHeights, strictOrder, maxDepth) {
664
- const segRanges = [];
665
- // isn't it true that there will either be ALL hcoords or NONE? can optimize
666
- for (const seg of segs) {
667
- const hcoords = segHorizontals[internal_cjs.getEventKey(seg)];
668
- if (hcoords) {
669
- segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
670
- }
671
- }
672
- const hierarchy = new internal_cjs.SegHierarchy(segRanges, (seg) => segHeights.get(internal_cjs.getEventKey(seg)), strictOrder, undefined, // maxCoord
673
- maxDepth);
674
- const segTops = new Map();
675
- hierarchy.traverseSegs((seg, segTop) => {
676
- segTops.set(internal_cjs.getEventKey(seg), segTop);
677
- });
678
- const { hiddenSegs } = hierarchy;
679
- let totalHeight = 0;
680
- for (const segRange of segRanges) {
681
- const segKey = internal_cjs.getEventKey(segRange);
682
- const segHeight = segHeights.get(segKey);
683
- const segTop = segTops.get(segKey);
684
- if (segHeight != null) {
685
- if (segTop != null) {
686
- totalHeight = Math.max(totalHeight, segTop + segHeight);
687
- }
688
- }
689
- }
690
- const hiddenGroups = internal_cjs.groupIntersectingSegs(hiddenSegs);
691
- const hiddenGroupTops = new Map();
692
- // HACK for hiddenGroup findInsertion() call
693
- hierarchy.strictOrder = true;
694
- for (const hiddenGroup of hiddenGroups) {
695
- const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
696
- const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
697
- hiddenGroupTops.set(hiddenGroup.key, top);
698
- totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
699
- }
700
- return [
701
- segTops,
702
- hiddenGroups,
703
- hiddenGroupTops,
704
- totalHeight,
705
- ];
706
- }
707
-
708
- class TimelineLaneBg extends internal_cjs.BaseComponent {
709
- render() {
710
- let { props } = this;
711
- let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
712
- return (preact_cjs.createElement(preact_cjs.Fragment, null,
713
- this.renderSegs(props.businessHourSegs || [], 'non-business'),
714
- this.renderSegs(props.bgEventSegs || [], 'bg-event'),
715
- this.renderSegs(highlightSeg, 'highlight')));
716
- }
717
- renderSegs(segs, fillType) {
718
- let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
719
- let { dateEnv, isRtl } = this.context;
720
- return (preact_cjs.createElement(preact_cjs.Fragment, null, segs.map((seg) => {
721
- let hStyle; // TODO
722
- if (slotWidth != null) {
723
- let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
724
- hStyle = horizontalsToCss(segHorizontal, isRtl);
725
- }
726
- return (preact_cjs.createElement("div", { key: internal_cjs.buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
727
- 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))));
728
- })));
729
- }
730
- }
731
-
732
- class TimelineLaneSlicer extends internal_cjs.Slicer {
733
- sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
734
- let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
735
- let segs = [];
736
- // protect against when the span is entirely in an invalid date region
737
- if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
738
- < computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
739
- // intersect the footprint's range with the grid's range
740
- let slicedRange = internal_cjs.intersectRanges(normalRange, tDateProfile.normalizedRange);
741
- if (slicedRange) {
742
- segs.push({
743
- startDate: slicedRange.start,
744
- endDate: slicedRange.end,
745
- isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
746
- && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
747
- isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
748
- && isValidDate(internal_cjs.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
749
- });
750
- }
751
- }
752
- return segs;
753
- }
754
- }
755
-
756
- const DEFAULT_TIME_FORMAT = internal_cjs.createFormatter({
757
- hour: 'numeric',
758
- minute: '2-digit',
759
- omitZeroMinute: true,
760
- meridiem: 'narrow',
761
- });
762
- class TimelineEvent extends internal_cjs.BaseComponent {
763
- render() {
764
- let { props, context } = this;
765
- let { options } = context;
766
- return (preact_cjs.createElement(internal_cjs.StandardEvent, Object.assign({}, props, { className: internal_cjs.joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
767
- && 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
768
- }
769
- }
770
-
771
- class TimelineLaneMoreLink extends internal_cjs.BaseComponent {
772
- render() {
773
- let { props } = this;
774
- let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
775
- let dateSpanProps = resourceId ? { resourceId } : {};
776
- 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) => {
777
- let { eventRange } = seg;
778
- let instanceId = eventRange.instance.instanceId;
779
- return (preact_cjs.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
780
- 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)))));
781
- }))) }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
782
- }
783
- }
784
-
785
- /*
786
- TODO: make DRY with other Event Harnesses
787
- */
788
- class TimelineEventHarness extends preact_cjs.Component {
789
- constructor() {
790
- super(...arguments);
791
- // ref
792
- this.rootElRef = preact_cjs.createRef();
793
- }
794
- render() {
795
- const { props } = this;
796
- return (preact_cjs.createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
797
- }
798
- componentDidMount() {
799
- const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
800
- this.disconnectHeight = internal_cjs.watchHeight(rootEl, (height) => {
801
- internal_cjs.setRef(this.props.heightRef, height);
802
- });
803
- }
804
- componentWillUnmount() {
805
- this.disconnectHeight();
806
- internal_cjs.setRef(this.props.heightRef, null);
807
- }
808
- }
809
-
810
- /*
811
- TODO: split TimelineLaneBg and TimelineLaneFg?
812
- */
813
- class TimelineLane extends internal_cjs.BaseComponent {
814
- constructor() {
815
- super(...arguments);
816
- // memo
817
- this.sortEventSegs = internal_cjs.memoize(internal_cjs.sortEventSegs);
818
- // refs
819
- this.segHeightRefMap = new internal_cjs.RefMap(() => {
820
- internal_cjs.afterSize(this.handleSegHeights);
821
- });
822
- this.moreLinkHeightRefMap = new internal_cjs.RefMap(() => {
823
- internal_cjs.afterSize(this.handleMoreLinkHeights);
824
- });
825
- // internal
826
- this.slicer = new TimelineLaneSlicer();
827
- this.handleMoreLinkHeights = () => {
828
- this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
829
- };
830
- this.handleSegHeights = () => {
831
- this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
832
- };
833
- }
834
- /*
835
- TODO: lots of memoization needed here!
836
- */
837
- render() {
838
- let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
839
- let { options } = context;
840
- let { dateProfile, tDateProfile } = props;
841
- 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...
842
- dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
843
- let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
844
- (slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
845
- [];
846
- let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
847
- let fgSegHorizontals = props.slotWidth != null
848
- ? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
849
- : {};
850
- let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
851
- let forcedInvisibleMap = // TODO: more convenient/DRY
852
- (slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
853
- (slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
854
- {};
855
- return (preact_cjs.createElement(preact_cjs.Fragment, null,
856
- preact_cjs.createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
857
- // content
858
- bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
859
- // dimensions
860
- slotWidth: props.slotWidth }),
861
- preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
862
- ? 'fc-timeline-events-overlap-disabled'
863
- : 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
864
- this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
865
- false, // isResizing
866
- false),
867
- this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
868
- ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
869
- : {}, fgSegTops, {}, // forcedInvisibleMap
870
- [], // hiddenGroups
871
- new Map(), // hiddenGroupTops
872
- Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
873
- }
874
- renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
875
- let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
876
- let isMirror = isDragging || isResizing || isDateSelecting;
877
- return (preact_cjs.createElement(preact_cjs.Fragment, null,
878
- segs.map((seg) => {
879
- const { eventRange } = seg;
880
- const { instanceId } = eventRange.instance;
881
- const segTop = segTops.get(instanceId);
882
- const segHorizontal = segHorizontals[instanceId];
883
- const isVisible = isMirror ||
884
- (segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
885
- 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) },
886
- 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)))));
887
- }),
888
- hiddenGroups.map((hiddenGroup) => (preact_cjs.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
889
- start: hiddenGroup.start,
890
- size: hiddenGroup.end - hiddenGroup.start
891
- }, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
892
- 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 }))))));
893
- }
894
- }
895
-
896
- class TimelineHeaderCell extends internal_cjs.BaseComponent {
897
- constructor() {
898
- super(...arguments);
899
- // memo
900
- this.refineRenderProps = internal_cjs.memoizeObjArg(refineRenderProps);
901
- // ref
902
- this.innerElRef = preact_cjs.createRef();
903
- }
904
- render() {
905
- let { props, context } = this;
906
- let { dateEnv, options } = context;
907
- let { cell, dateProfile, tDateProfile } = props;
908
- // the cell.rowUnit is f'd
909
- // giving 'month' for a 3-day view
910
- // workaround: to infer day, do NOT time
911
- let dateMeta = internal_cjs.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
912
- let renderProps = this.refineRenderProps({
913
- level: props.rowLevel,
914
- dateMarker: cell.date,
915
- text: cell.text,
916
- dateEnv: context.dateEnv,
917
- viewApi: context.viewApi,
918
- });
919
- let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
920
- 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
921
- 'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
922
- // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
923
- cell.rowUnit === 'time' ?
924
- internal_cjs.getSlotClassName(dateMeta) :
925
- internal_cjs.getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
926
- omitTime: !tDateProfile.isTimeScale,
927
- omitTimeZoneOffset: true,
928
- }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
929
- width: props.slotWidth != null
930
- ? props.slotWidth * cell.colspan
931
- : undefined,
932
- }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: 'div', attrs: isNavLink
933
- // not tabbable because parent is aria-hidden
934
- ? internal_cjs.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
935
- : {} // don't bother with aria-hidden because parent already hidden
936
- , 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 renderInnerContent(renderProps) {
961
- return renderProps.text;
962
- }
963
- function refineRenderProps(input) {
964
- return {
965
- level: input.level,
966
- date: input.dateEnv.toDate(input.dateMarker),
967
- view: input.viewApi,
968
- text: input.text,
969
- };
970
- }
971
-
972
- class TimelineHeaderRow extends internal_cjs.BaseComponent {
973
- constructor() {
974
- super(...arguments);
975
- // refs
976
- this.innerWidthRefMap = new internal_cjs.RefMap(() => {
977
- internal_cjs.afterSize(this.handleInnerWidths);
978
- });
979
- this.innerHeightRefMap = new internal_cjs.RefMap(() => {
980
- internal_cjs.afterSize(this.handleInnerHeights);
981
- });
982
- this.handleInnerWidths = () => {
983
- const innerWidthMap = this.innerWidthRefMap.current;
984
- let max = 0;
985
- for (const innerWidth of innerWidthMap.values()) {
986
- max = Math.max(max, innerWidth);
987
- }
988
- // TODO: ensure not equal?
989
- internal_cjs.setRef(this.props.innerWidthRef, max);
990
- };
991
- this.handleInnerHeights = () => {
992
- const innerHeightMap = this.innerHeightRefMap.current;
993
- let max = 0;
994
- for (const innerHeight of innerHeightMap.values()) {
995
- max = Math.max(max, innerHeight);
996
- }
997
- // TODO: ensure not equal?
998
- internal_cjs.setRef(this.props.innerHeighRef, max);
999
- };
1000
- }
1001
- render() {
1002
- const { props, innerWidthRefMap, innerHeightRefMap } = this;
1003
- const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
1004
- const isSticky = !props.isLastRow;
1005
- return (preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
1006
- !props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
1007
- // TODO: make this part of the cell obj?
1008
- // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
1009
- // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
1010
- // TODO: use rowUnit to key the Row itself?
1011
- const key = cell.rowUnit + ':' + cell.date.toISOString();
1012
- 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),
1013
- // refs
1014
- innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
1015
- // dimensions
1016
- slotWidth: props.slotWidth }));
1017
- })));
1018
- }
1019
- componentWillUnmount() {
1020
- internal_cjs.setRef(this.props.innerWidthRef, null);
1021
- internal_cjs.setRef(this.props.innerHeighRef, null);
1022
- }
1023
- }
1024
-
1025
- class TimelineNowIndicatorLine extends internal_cjs.BaseComponent {
1026
- render() {
1027
- const { props, context } = this;
1028
- return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
1029
- preact_cjs.createElement(internal_cjs.NowIndicatorContainer // TODO: make separate component?
1030
- , { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
1031
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1032
- : {}, isAxis: false, date: props.nowDate })));
1033
- }
1034
- }
1035
-
1036
- class TimelineNowIndicatorArrow extends internal_cjs.BaseComponent {
1037
- render() {
1038
- const { props, context } = this;
1039
- return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
1040
- preact_cjs.createElement(internal_cjs.NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
1041
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1042
- : {}, isAxis: true, date: props.nowDate })));
1043
- }
1044
- }
1045
-
1046
- class TimelineView extends internal_cjs.DateComponent {
1047
- constructor() {
1048
- super(...arguments);
1049
- // memoized
1050
- this.buildTimelineDateProfile = internal_cjs.memoize(buildTimelineDateProfile);
1051
- this.computeSlotWidth = internal_cjs.memoize(computeSlotWidth);
1052
- // refs
1053
- this.headerScrollerRef = preact_cjs.createRef();
1054
- this.bodyScrollerRef = preact_cjs.createRef();
1055
- this.footerScrollerRef = preact_cjs.createRef();
1056
- this.headerRowInnerWidthMap = new internal_cjs.RefMap(() => {
1057
- internal_cjs.afterSize(this.handleSlotInnerWidths);
1058
- });
1059
- this.scrollTime = null;
1060
- // Sizing
1061
- // -----------------------------------------------------------------------------------------------
1062
- this.handleBodySlotInnerWidth = (innerWidth) => {
1063
- this.bodySlotInnerWidth = innerWidth;
1064
- internal_cjs.afterSize(this.handleSlotInnerWidths);
1065
- };
1066
- this.handleSlotInnerWidths = () => {
1067
- const { state } = this;
1068
- const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
1069
- if (state.slotInnerWidth !== slotInnerWidth) {
1070
- this.setState({ slotInnerWidth });
1071
- }
1072
- };
1073
- this.handleClientWidth = (clientWidth) => {
1074
- this.setState({
1075
- clientWidth,
1076
- });
1077
- };
1078
- this.handleEndScrollbarWidth = (endScrollbarWidth) => {
1079
- this.setState({
1080
- endScrollbarWidth
1081
- });
1082
- };
1083
- this.handleTimeScrollRequest = (scrollTime) => {
1084
- this.scrollTime = scrollTime;
1085
- this.applyTimeScroll();
1086
- };
1087
- this.handleTimeScrollEnd = ({ isUser }) => {
1088
- if (isUser) {
1089
- this.scrollTime = null;
1090
- }
1091
- };
1092
- // Hit System
1093
- // -----------------------------------------------------------------------------------------------
1094
- this.handeBodyEl = (el) => {
1095
- this.bodyEl = el;
1096
- if (el) {
1097
- this.context.registerInteractiveComponent(this, { el });
1098
- }
1099
- else {
1100
- this.context.unregisterInteractiveComponent(this);
1101
- }
1102
- };
1103
- }
1104
- render() {
1105
- const { props, state, context } = this;
1106
- const { options } = context;
1107
- /* date */
1108
- const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
1109
- const { cellRows } = tDateProfile;
1110
- const timerUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
1111
- /* table settings */
1112
- const verticalScrolling = !props.forPrint && !internal_cjs.getIsHeightAuto(options);
1113
- const stickyHeaderDates = !props.forPrint && internal_cjs.getStickyHeaderDates(options);
1114
- const stickyFooterScrollbar = !props.forPrint && internal_cjs.getStickyFooterScrollbar(options);
1115
- /* table positions */
1116
- const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
1117
- state.clientWidth);
1118
- this.slotWidth = slotWidth;
1119
- return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
1120
- const enableNowIndicator = // TODO: DRY
1121
- options.nowIndicator &&
1122
- slotWidth != null &&
1123
- internal_cjs.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
1124
- return (preact_cjs.createElement(internal_cjs.ViewContainer, { viewSpec: context.viewSpec, className: internal_cjs.joinClassNames('fc-timeline fc-border',
1125
- // HACK for Safari print-mode, where fc-scroller-no-bars won't take effect for
1126
- // the below Scrollers if they have liquid flex height
1127
- !props.forPrint && 'fc-flex-col') },
1128
- 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 },
1129
- preact_cjs.createElement("div", {
1130
- // TODO: DRY
1131
- className: internal_cjs.joinClassNames('fc-rel', // origin for now-indicator
1132
- canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
1133
- cellRows.map((cells, rowLevel) => {
1134
- const isLast = rowLevel === cellRows.length - 1;
1135
- 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) }));
1136
- }),
1137
- enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
1138
- Boolean(state.endScrollbarWidth) && (preact_cjs.createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
1139
- 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 },
1140
- preact_cjs.createElement("div", { "aria-label": options.eventsHint, className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
1141
- preact_cjs.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1142
- // ref
1143
- innerWidthRef: this.handleBodySlotInnerWidth,
1144
- // dimensions
1145
- slotWidth: slotWidth }),
1146
- 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 }),
1147
- enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
1148
- stickyFooterScrollbar && (preact_cjs.createElement(internal_cjs.Scroller, { ref: this.footerScrollerRef, horizontal: true },
1149
- preact_cjs.createElement("div", { style: { width: canvasWidth } })))));
1150
- }));
1151
- }
1152
- // Lifecycle
1153
- // -----------------------------------------------------------------------------------------------
1154
- componentDidMount() {
1155
- this.syncedScroller = new internal_cjs$1.ScrollerSyncer(true); // horizontal=true
1156
- this.updateSyncedScroller();
1157
- this.resetScroll();
1158
- this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
1159
- this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
1160
- }
1161
- componentDidUpdate(prevProps) {
1162
- this.updateSyncedScroller();
1163
- if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
1164
- this.resetScroll();
1165
- }
1166
- else {
1167
- // TODO: inefficient to update so often
1168
- this.applyTimeScroll();
1169
- }
1170
- }
1171
- componentWillUnmount() {
1172
- this.syncedScroller.destroy();
1173
- this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
1174
- this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
1175
- }
1176
- // Scrolling
1177
- // -----------------------------------------------------------------------------------------------
1178
- updateSyncedScroller() {
1179
- this.syncedScroller.handleChildren([
1180
- this.headerScrollerRef.current,
1181
- this.bodyScrollerRef.current,
1182
- this.footerScrollerRef.current
1183
- ]);
1184
- }
1185
- resetScroll() {
1186
- this.handleTimeScrollRequest(this.context.options.scrollTime);
1187
- }
1188
- applyTimeScroll() {
1189
- const { props, context, tDateProfile, scrollTime, slotWidth } = this;
1190
- if (scrollTime != null && slotWidth != null) {
1191
- let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
1192
- if (x) {
1193
- x += 1; // overcome border. TODO: DRY this up
1194
- }
1195
- this.syncedScroller.scrollTo({ x });
1196
- }
1197
- }
1198
- queryHit(positionLeft, positionTop, elWidth, elHeight) {
1199
- const { props, context, tDateProfile, slotWidth } = this;
1200
- const { dateEnv } = context;
1201
- if (slotWidth) {
1202
- const x = context.isRtl ? elWidth - positionLeft : positionLeft;
1203
- const slatIndex = Math.floor(x / slotWidth);
1204
- const slatX = slatIndex * slotWidth;
1205
- const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
1206
- const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
1207
- let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], internal_cjs.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
1208
- let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
1209
- // TODO: generalize this coord stuff to TimeGrid?
1210
- let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
1211
- let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
1212
- let endCoord = startCoord + snapWidth;
1213
- let left, right;
1214
- if (context.isRtl) {
1215
- left = elWidth - endCoord;
1216
- right = elWidth - startCoord;
1217
- }
1218
- else {
1219
- left = startCoord;
1220
- right = endCoord;
1221
- }
1222
- return {
1223
- dateProfile: props.dateProfile,
1224
- dateSpan: {
1225
- range: { start: startDate, end: endDate },
1226
- allDay: !tDateProfile.isTimeScale,
1227
- },
1228
- rect: {
1229
- left,
1230
- right,
1231
- top: 0,
1232
- bottom: elHeight,
1233
- },
1234
- // HACK. TODO: This is expensive to do every hit-query
1235
- dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
1236
- layer: 0,
1237
- };
1238
- }
1239
- return null;
1240
- }
1241
- }
1242
-
1243
- 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}";
1244
- internal_cjs.injectStyles(css_248z);
1245
-
1246
- exports.TimelineHeaderRow = TimelineHeaderRow;
1247
- exports.TimelineLane = TimelineLane;
1248
- exports.TimelineLaneBg = TimelineLaneBg;
1249
- exports.TimelineLaneSlicer = TimelineLaneSlicer;
1250
- exports.TimelineNowIndicatorArrow = TimelineNowIndicatorArrow;
1251
- exports.TimelineNowIndicatorLine = TimelineNowIndicatorLine;
1252
- exports.TimelineSlats = TimelineSlats;
1253
- exports.TimelineView = TimelineView;
1254
- exports.buildTimelineDateProfile = buildTimelineDateProfile;
1255
- exports.computeSlotWidth = computeSlotWidth;
1256
- exports.createHorizontalStyle = createHorizontalStyle;
1257
- exports.createVerticalStyle = createVerticalStyle;
1258
- exports.timeToCoord = timeToCoord;