@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.
@@ -1,14 +1,15 @@
1
1
  /*!
2
- FullCalendar Timeline Plugin v7.0.0-beta.4
2
+ FullCalendar Timeline Plugin v7.0.0-beta.5
3
3
  Docs & License: https://fullcalendar.io/docs/timeline-view-no-resources
4
- (c) 2024 Adam Shaw
4
+ (c) 2025 Adam Shaw
5
5
  */
6
- FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$1, preact, internal$2) {
6
+ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$1, classNames, preact, internal$2) {
7
7
  'use strict';
8
8
 
9
9
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
10
10
 
11
11
  var premiumCommonPlugin__default = /*#__PURE__*/_interopDefault(premiumCommonPlugin);
12
+ var classNames__default = /*#__PURE__*/_interopDefault(classNames);
12
13
 
13
14
  const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
14
15
  const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
@@ -37,13 +38,13 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
37
38
  ];
38
39
  function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
39
40
  let tDateProfile = {
40
- labelInterval: allOptions.slotLabelInterval,
41
+ labelInterval: allOptions.slotHeaderInterval,
41
42
  slotDuration: allOptions.slotDuration,
42
43
  };
43
44
  validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
44
45
  ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
45
46
  ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
46
- let input = allOptions.slotLabelFormat;
47
+ let input = allOptions.slotHeaderFormat;
47
48
  let rawFormats = Array.isArray(input) ? input :
48
49
  (input != null) ? [input] :
49
50
  computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
@@ -57,10 +58,6 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
57
58
  }
58
59
  }
59
60
  tDateProfile.largeUnit = largeUnit;
60
- tDateProfile.emphasizeWeeks =
61
- internal$1.asCleanDays(tDateProfile.slotDuration) === 1 &&
62
- currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
63
- !allOptions.businessHours;
64
61
  /*
65
62
  console.log('label interval =', timelineView.labelInterval.humanize())
66
63
  console.log('slot duration =', timelineView.slotDuration.humanize())
@@ -96,14 +93,18 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
96
93
  tDateProfile.timeWindowMs = timeWindowMs;
97
94
  tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
98
95
  let slotDates = [];
96
+ let slotDatesMajor = [];
99
97
  let date = normalizedStart;
98
+ let majorUnit = internal$1.computeMajorUnit(dateProfile, dateEnv);
100
99
  while (date < normalizedEnd) {
101
100
  if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
102
101
  slotDates.push(date);
102
+ slotDatesMajor.push(internal$1.isMajorUnit(date, majorUnit, dateEnv));
103
103
  }
104
104
  date = dateEnv.add(date, tDateProfile.slotDuration);
105
105
  }
106
106
  tDateProfile.slotDates = slotDates;
107
+ tDateProfile.slotDatesMajor = slotDatesMajor;
107
108
  // more...
108
109
  let snapIndex = -1;
109
110
  let snapDiff = 0; // index of the diff :(
@@ -127,8 +128,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
127
128
  tDateProfile.snapCnt = snapIndex + 1; // is always one behind
128
129
  tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
129
130
  // more...
130
- tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
131
- tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv);
131
+ tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv, majorUnit);
132
132
  tDateProfile.slotsPerLabel = internal$1.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
133
133
  return tDateProfile;
134
134
  }
@@ -189,7 +189,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
189
189
  if (tDateProfile.labelInterval) {
190
190
  const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
191
191
  if (labelCnt > internal$1.config.MAX_TIMELINE_SLOTS) {
192
- console.warn('slotLabelInterval results in too many cells');
192
+ console.warn('slotHeaderInterval results in too many cells');
193
193
  tDateProfile.labelInterval = null;
194
194
  }
195
195
  }
@@ -205,7 +205,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
205
205
  if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
206
206
  const slotsPerLabel = internal$1.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
207
207
  if (slotsPerLabel === null || slotsPerLabel < 1) {
208
- console.warn('slotLabelInterval must be a multiple of slotDuration');
208
+ console.warn('slotHeaderInterval must be a multiple of slotDuration');
209
209
  tDateProfile.slotDuration = null;
210
210
  }
211
211
  }
@@ -280,6 +280,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
280
280
  let format1;
281
281
  let format2;
282
282
  const { labelInterval } = tDateProfile;
283
+ const { currentRange } = dateProfile;
283
284
  let unit = internal$1.greatestDurationDenominator(labelInterval).unit;
284
285
  const weekNumbersVisible = allOptions.weekNumbers;
285
286
  let format0 = (format1 = (format2 = null));
@@ -292,22 +293,22 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
292
293
  format0 = { year: 'numeric' }; // '2015'
293
294
  break;
294
295
  case 'month':
295
- if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
296
+ if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
296
297
  format0 = { year: 'numeric' }; // '2015'
297
298
  }
298
299
  format1 = { month: 'short' }; // 'Jan'
299
300
  break;
300
301
  case 'week':
301
- if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
302
+ if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
302
303
  format0 = { year: 'numeric' }; // '2015'
303
304
  }
304
305
  format1 = { week: 'narrow' }; // 'Wk4'
305
306
  break;
306
307
  case 'day':
307
- if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
308
+ if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
308
309
  format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
309
310
  }
310
- else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
311
+ else if (dateEnv.diffWholeMonths(currentRange.start, currentRange.end) > 1) {
311
312
  format0 = { month: 'long' }; // 'January'
312
313
  }
313
314
  if (weekNumbersVisible) {
@@ -319,7 +320,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
319
320
  if (weekNumbersVisible) {
320
321
  format0 = { week: 'short' }; // 'Wk 4'
321
322
  }
322
- if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
323
+ if (internal$1.diffWholeDays(currentRange.start, currentRange.end) > 1) {
323
324
  format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
324
325
  }
325
326
  format2 = {
@@ -365,39 +366,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
365
366
  }
366
367
  return [].concat(format0 || [], format1 || [], format2 || []);
367
368
  }
368
- // Compute the number of the give units in the "current" range.
369
- // Won't go more precise than days.
370
- // Will return `0` if there's not a clean whole interval.
371
- function currentRangeAs(unit, dateProfile, dateEnv) {
372
- let range = dateProfile.currentRange;
373
- let res = null;
374
- if (unit === 'years') {
375
- res = dateEnv.diffWholeYears(range.start, range.end);
376
- }
377
- else if (unit === 'months') {
378
- res = dateEnv.diffWholeMonths(range.start, range.end);
379
- }
380
- else if (unit === 'weeks') {
381
- res = dateEnv.diffWholeMonths(range.start, range.end);
382
- }
383
- else if (unit === 'days') {
384
- res = internal$1.diffWholeDays(range.start, range.end);
385
- }
386
- return res || 0;
387
- }
388
- function buildIsWeekStarts(tDateProfile, dateEnv) {
389
- let { slotDates, emphasizeWeeks } = tDateProfile;
390
- let prevWeekNumber = null;
391
- let isWeekStarts = [];
392
- for (let slotDate of slotDates) {
393
- let weekNumber = dateEnv.computeWeekNumber(slotDate);
394
- let isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
395
- prevWeekNumber = weekNumber;
396
- isWeekStarts.push(isWeekStart);
397
- }
398
- return isWeekStarts;
399
- }
400
- function buildCellRows(tDateProfile, dateEnv) {
369
+ function buildCellRows(tDateProfile, dateEnv, majorUnit) {
401
370
  let slotDates = tDateProfile.slotDates;
402
371
  let formats = tDateProfile.headerFormats;
403
372
  let cellRows = formats.map(() => []); // indexed by row,col
@@ -406,23 +375,23 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
406
375
  slotAsDays === 1 ? 'day' :
407
376
  null;
408
377
  // specifically for navclicks
409
- let rowUnitsFromFormats = formats.map((format) => (format.getLargestUnit ? format.getLargestUnit() : null));
378
+ let rowUnitsFromFormats = formats.map((format) => (format.getSmallestUnit ? format.getSmallestUnit() : null));
410
379
  // builds cellRows and slotCells
411
380
  for (let i = 0; i < slotDates.length; i += 1) {
412
381
  let date = slotDates[i];
413
- let isWeekStart = tDateProfile.isWeekStarts[i];
414
382
  for (let row = 0; row < formats.length; row += 1) {
415
383
  let format = formats[row];
416
384
  let rowCells = cellRows[row];
417
385
  let leadingCell = rowCells[rowCells.length - 1];
418
386
  let isLastRow = row === formats.length - 1;
419
387
  let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
388
+ let isMajor = internal$1.isMajorUnit(date, majorUnit, dateEnv);
420
389
  let newCell = null;
421
390
  let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
422
391
  if (isSuperRow) {
423
- let text = dateEnv.format(date, format);
392
+ let [text] = dateEnv.format(date, format);
424
393
  if (!leadingCell || (leadingCell.text !== text)) {
425
- newCell = buildCellObject(date, text, rowUnit);
394
+ newCell = buildCellObject(date, isMajor, text, rowUnit);
426
395
  }
427
396
  else {
428
397
  leadingCell.colspan += 1;
@@ -430,159 +399,202 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
430
399
  }
431
400
  else if (!leadingCell ||
432
401
  internal$1.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
433
- let text = dateEnv.format(date, format);
434
- newCell = buildCellObject(date, text, rowUnit);
402
+ let [text] = dateEnv.format(date, format);
403
+ newCell = buildCellObject(date, isMajor, text, rowUnit);
435
404
  }
436
405
  else {
437
406
  leadingCell.colspan += 1;
438
407
  }
439
408
  if (newCell) {
440
- newCell.weekStart = isWeekStart;
441
409
  rowCells.push(newCell);
442
410
  }
443
411
  }
444
412
  }
445
413
  return cellRows;
446
414
  }
447
- function buildCellObject(date, text, rowUnit) {
448
- return { date, text, rowUnit, colspan: 1, isWeekStart: false };
415
+ function buildCellObject(date, isMajor, text, rowUnit) {
416
+ return { date, isMajor, text, rowUnit, colspan: 1 }; // colspan mutated later
449
417
  }
450
418
 
451
419
  class TimelineSlatCell extends internal$1.BaseComponent {
452
420
  constructor() {
453
421
  super(...arguments);
454
- // ref
455
- this.innerElRef = preact.createRef();
422
+ // memo
423
+ this.getDateMeta = internal$1.memoize(internal$1.getDateMeta);
456
424
  }
457
425
  render() {
458
426
  let { props, context } = this;
459
427
  let { dateEnv, options } = context;
460
- let { date, tDateProfile, isEm } = props;
461
- let dateMeta = internal$1.getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
462
- let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
463
- return (preact.createElement(internal$1.ContentContainer, { tag: "div",
464
- // fc-align-start shrinks width of InnerContent
465
- // TODO: document this semantic className fc-timeline-slot-em
466
- className: internal$1.joinClassNames('fc-timeline-slot', isEm && 'fc-timeline-slot-em', tDateProfile.isTimeScale && (internal$1.isInt(dateEnv.countDurationsBetween(// best to do this here?
467
- tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
468
- 'fc-timeline-slot-major' :
469
- 'fc-timeline-slot-minor'), 'fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start', props.borderStart && 'fc-border-s', props.isDay ?
470
- internal$1.getDayClassName(dateMeta) :
471
- internal$1.getSlotClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
428
+ let { date, tDateProfile, isMajor } = props;
429
+ let dateMeta = this.getDateMeta(props.date, dateEnv, props.dateProfile, props.todayRange, props.nowDate);
430
+ let isMinor = tDateProfile.isTimeScale &&
431
+ !internal$1.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval));
432
+ let renderProps = Object.assign(Object.assign({}, dateMeta), { isMajor,
433
+ isMinor, view: context.viewApi });
434
+ return (preact.createElement(internal$1.ContentContainer, { tag: "div", className: core.joinClassNames(classNames__default["default"].tight, classNames__default["default"].alignStart, // shrinks width of InnerContent
435
+ props.borderStart ? classNames__default["default"].borderOnlyS : classNames__default["default"].borderNone, classNames__default["default"].internalTimelineSlot), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
472
436
  omitTimeZoneOffset: true,
473
437
  omitTime: !tDateProfile.isTimeScale,
474
438
  }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
475
439
  width: props.width,
476
- }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (preact.createElement(InnerContent, { tag: "div", className: 'fc-cell-inner', elRef: this.innerElRef }))));
440
+ }, renderProps: renderProps, generatorName: undefined, classNameGenerator: options.slotLaneClass, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }));
441
+ }
442
+ }
443
+
444
+ class TimelineSlats extends internal$1.BaseComponent {
445
+ render() {
446
+ let { props } = this;
447
+ let { tDateProfile, slotWidth } = props;
448
+ let { slotDates, slotDatesMajor } = tDateProfile;
449
+ return (preact.createElement("div", { "aria-hidden": true, className: core.joinClassNames(classNames__default["default"].flexRow, classNames__default["default"].fill), style: { height: props.height } }, slotDates.map((slotDate, i) => {
450
+ let key = slotDate.toISOString();
451
+ return (preact.createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isMajor: slotDatesMajor[i], borderStart: Boolean(i),
452
+ // dimensions
453
+ width: slotWidth }));
454
+ })));
455
+ }
456
+ }
457
+
458
+ class TimelineHeaderCell extends internal$1.BaseComponent {
459
+ constructor() {
460
+ super(...arguments);
461
+ // memo
462
+ this.getDateMeta = internal$1.memoize(internal$1.getDateMeta);
463
+ // ref
464
+ this.innerWrapperElRef = preact.createRef();
465
+ }
466
+ render() {
467
+ let { props, state, context } = this;
468
+ let { dateEnv, options } = context;
469
+ let { cell, dateProfile, tDateProfile } = props;
470
+ // the cell.rowUnit is f'd
471
+ // giving 'month' for a 3-day view
472
+ // workaround: to infer day, do NOT time
473
+ let dateMeta = this.getDateMeta(cell.date, dateEnv, dateProfile, props.todayRange, props.nowDate);
474
+ let hasNavLink = options.navLinks && !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
475
+ let isTime = tDateProfile.isTimeScale && !props.rowLevel; // HACK: faulty way of determining this
476
+ let renderProps = Object.assign(Object.assign({}, dateMeta), { level: props.rowLevel, isMajor: cell.isMajor, isMinor: false, isNarrow: false, isTime,
477
+ hasNavLink, text: cell.text, isFirst: props.isFirst, view: context.viewApi });
478
+ const { slotHeaderAlign } = options;
479
+ const align = this.align =
480
+ typeof slotHeaderAlign === 'function'
481
+ ? slotHeaderAlign({ level: props.rowLevel, isTime })
482
+ : slotHeaderAlign;
483
+ const isSticky = this.isSticky =
484
+ props.rowLevel && options.slotHeaderSticky !== false;
485
+ let edgeCoord;
486
+ if (isSticky) {
487
+ if (align === 'center') {
488
+ if (state.innerWidth != null) {
489
+ edgeCoord = `calc(50% - ${state.innerWidth / 2}px)`;
490
+ }
491
+ }
492
+ else {
493
+ edgeCoord = (typeof options.slotHeaderSticky === 'number' ||
494
+ typeof options.slotHeaderSticky === 'string') ? options.slotHeaderSticky : 0;
495
+ }
496
+ }
497
+ return (preact.createElement(internal$1.ContentContainer, { tag: "div", className: internal$1.joinArrayishClassNames(classNames__default["default"].tight, classNames__default["default"].flexCol, props.isFirst ? classNames__default["default"].borderNone : classNames__default["default"].borderOnlyS, align === 'center' ? classNames__default["default"].alignCenter :
498
+ align === 'end' ? classNames__default["default"].alignEnd :
499
+ classNames__default["default"].alignStart, classNames__default["default"].internalTimelineSlot), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
500
+ omitTime: !tDateProfile.isTimeScale,
501
+ omitTimeZoneOffset: true,
502
+ }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
503
+ width: props.slotWidth != null
504
+ ? props.slotWidth * cell.colspan
505
+ : undefined,
506
+ }, renderProps: renderProps, generatorName: "slotHeaderContent", customGenerator: options.slotHeaderContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotHeaderClass, didMount: options.slotHeaderDidMount, willUnmount: options.slotHeaderWillUnmount }, (InnerContent) => (preact.createElement("div", { ref: this.innerWrapperElRef, className: core.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].rigid, isSticky && classNames__default["default"].sticky), style: {
507
+ left: edgeCoord,
508
+ right: edgeCoord,
509
+ } },
510
+ preact.createElement(InnerContent, { tag: 'div', attrs: hasNavLink
511
+ // not tabbable because parent is aria-hidden
512
+ ? internal$1.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
513
+ : {} // don't bother with aria-hidden because parent already hidden
514
+ , className: internal$1.generateClassName(options.slotHeaderInnerClass, renderProps) })))));
477
515
  }
478
516
  componentDidMount() {
479
- const innerEl = this.innerElRef.current;
480
- this.disconnectInnerWidth = internal$1.watchWidth(innerEl, (width) => {
481
- internal$1.setRef(this.props.innerWidthRef, width);
517
+ const { props } = this;
518
+ const innerWrapperEl = this.innerWrapperElRef.current; // TODO: make dynamic with useEffect
519
+ this.disconnectSize = internal$1.watchSize(innerWrapperEl, (width, height) => {
520
+ internal$1.setRef(props.innerWidthRef, width);
521
+ internal$1.setRef(props.innerHeightRef, height);
522
+ if (this.align === 'center' && this.isSticky) {
523
+ this.setState({ innerWidth: width });
524
+ }
482
525
  });
483
526
  }
484
527
  componentWillUnmount() {
485
- this.disconnectInnerWidth();
486
- internal$1.setRef(this.props.innerWidthRef, null);
528
+ const { props } = this;
529
+ this.disconnectSize();
530
+ internal$1.setRef(props.innerWidthRef, null);
531
+ internal$1.setRef(props.innerHeightRef, null);
487
532
  }
488
533
  }
534
+ // Utils
535
+ // -------------------------------------------------------------------------------------------------
536
+ function renderInnerContent(renderProps) {
537
+ return renderProps.text;
538
+ }
489
539
 
490
- class TimelineSlats extends internal$1.BaseComponent {
540
+ class TimelineHeaderRow extends internal$1.BaseComponent {
491
541
  constructor() {
492
542
  super(...arguments);
543
+ // refs
493
544
  this.innerWidthRefMap = new internal$1.RefMap(() => {
494
545
  internal$1.afterSize(this.handleInnerWidths);
495
546
  });
547
+ this.innerHeightRefMap = new internal$1.RefMap(() => {
548
+ internal$1.afterSize(this.handleInnerHeights);
549
+ });
496
550
  this.handleInnerWidths = () => {
497
551
  const innerWidthMap = this.innerWidthRefMap.current;
498
552
  let max = 0;
499
553
  for (const innerWidth of innerWidthMap.values()) {
500
554
  max = Math.max(max, innerWidth);
501
555
  }
502
- // TODO: check to see if changed before firing ref!? YES. do in other places too
556
+ // TODO: ensure not equal?
503
557
  internal$1.setRef(this.props.innerWidthRef, max);
504
558
  };
559
+ this.handleInnerHeights = () => {
560
+ const innerHeightMap = this.innerHeightRefMap.current;
561
+ let max = 0;
562
+ for (const innerHeight of innerHeightMap.values()) {
563
+ max = Math.max(max, innerHeight);
564
+ }
565
+ // TODO: ensure not equal?
566
+ internal$1.setRef(this.props.innerHeighRef, max);
567
+ this.setState({ innerHeight: max });
568
+ };
505
569
  }
506
570
  render() {
507
- let { props, innerWidthRefMap } = this;
508
- let { tDateProfile, slotWidth } = props;
509
- let { slotDates, isWeekStarts } = tDateProfile;
510
- let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
511
- return (preact.createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
512
- let key = slotDate.toISOString();
513
- return (preact.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),
514
- // ref
515
- innerWidthRef: innerWidthRefMap.createRef(key),
571
+ const { props, innerWidthRefMap, innerHeightRefMap, state, context } = this;
572
+ const { options } = context;
573
+ return (preact.createElement("div", { className: internal$1.joinArrayishClassNames(options.slotHeaderRowClass, classNames__default["default"].flexRow, classNames__default["default"].grow, props.rowLevel // not the last row?
574
+ ? classNames__default["default"].borderOnlyB
575
+ : classNames__default["default"].borderNone), style: {
576
+ // we assign height because we allow cells to have distorted heights for visual effect
577
+ // but we still want to keep the overall extrenal mass
578
+ height: state.innerHeight,
579
+ } }, props.cells.map((cell, cellI) => {
580
+ // TODO: make this part of the cell obj?
581
+ // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
582
+ // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
583
+ // TODO: use rowUnit to key the Row itself?
584
+ const key = cell.rowUnit + ':' + cell.date.toISOString();
585
+ return (preact.createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isFirst: cellI === 0,
586
+ // refs
587
+ innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
516
588
  // dimensions
517
- width: slotWidth }));
589
+ slotWidth: props.slotWidth }));
518
590
  })));
519
591
  }
520
- }
521
-
522
- /*
523
- TODO: rename this file!
524
- */
525
- // returned value is between 0 and the number of snaps
526
- function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
527
- let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
528
- if (snapDiff < 0) {
529
- return 0;
530
- }
531
- if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
532
- return tDateProfile.snapCnt;
533
- }
534
- let snapDiffInt = Math.floor(snapDiff);
535
- let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
536
- if (internal$1.isInt(snapCoverage)) { // not an in-between value
537
- snapCoverage += snapDiff - snapDiffInt; // add the remainder
538
- }
539
- else {
540
- // a fractional value, meaning the date is not visible
541
- // always round up in this case. works for start AND end dates in a range.
542
- snapCoverage = Math.ceil(snapCoverage);
543
- }
544
- return snapCoverage;
545
- }
546
- /*
547
- TODO: DRY up with elsewhere?
548
- */
549
- function horizontalsToCss(hcoord, isRtl) {
550
- if (!hcoord) {
551
- return {};
552
- }
553
- if (isRtl) {
554
- return { right: hcoord.start, width: hcoord.size };
555
- }
556
- else {
557
- return { left: hcoord.start, width: hcoord.size };
558
- }
559
- }
560
- function horizontalCoordToCss(start, isRtl) {
561
- if (isRtl) {
562
- return { right: start };
563
- }
564
- else {
565
- return { left: start };
592
+ componentWillUnmount() {
593
+ internal$1.setRef(this.props.innerWidthRef, null);
594
+ internal$1.setRef(this.props.innerHeighRef, null);
566
595
  }
567
596
  }
568
597
 
569
- function createVerticalStyle(props) {
570
- if (props) {
571
- return {
572
- top: props.start,
573
- height: props.size,
574
- };
575
- }
576
- }
577
- function createHorizontalStyle(// TODO: DRY up?
578
- props, isRtl) {
579
- if (props) {
580
- return {
581
- [isRtl ? 'right' : 'left']: props.start,
582
- width: props.size,
583
- };
584
- }
585
- }
586
598
  // Timeline-specific
587
599
  // -------------------------------------------------------------------------------------------------
588
600
  const MIN_SLOT_WIDTH = 30; // for real
@@ -595,17 +607,17 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
595
607
  }
596
608
  slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
597
609
  const slatTryWidth = viewportWidth / slatCnt;
598
- let slatLiquid;
610
+ let slotLiquid;
599
611
  let slatWidth;
600
612
  if (slatTryWidth >= slatMinWidth) {
601
- slatLiquid = true;
613
+ slotLiquid = true;
602
614
  slatWidth = slatTryWidth;
603
615
  }
604
616
  else {
605
- slatLiquid = false;
617
+ slotLiquid = false;
606
618
  slatWidth = Math.max(slatMinWidth, slatTryWidth);
607
619
  }
608
- return [slatWidth * slatCnt, slatWidth, slatLiquid];
620
+ return [slatWidth * slatCnt, slatWidth, slotLiquid];
609
621
  }
610
622
  function timeToCoord(// pixels
611
623
  time, dateEnv, dateProfile, tDateProfile, slowWidth) {
@@ -617,13 +629,86 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
617
629
  }
618
630
  function dateToCoord(// pixels
619
631
  date, dateEnv, tDateProfile, slotWidth) {
620
- let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
632
+ let snapCoverage = computeDateSnapCoverage$1(date, tDateProfile, dateEnv);
621
633
  let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
622
634
  return slotCoverage * slotWidth;
623
635
  }
624
636
  /*
625
637
  returned value is between 0 and the number of snaps
626
638
  */
639
+ function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
640
+ let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
641
+ if (snapDiff < 0) {
642
+ return 0;
643
+ }
644
+ if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
645
+ return tDateProfile.snapCnt;
646
+ }
647
+ let snapDiffInt = Math.floor(snapDiff);
648
+ let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
649
+ if (internal$1.isInt(snapCoverage)) { // not an in-between value
650
+ snapCoverage += snapDiff - snapDiffInt; // add the remainder
651
+ }
652
+ else {
653
+ // a fractional value, meaning the date is not visible
654
+ // always round up in this case. works for start AND end dates in a range.
655
+ snapCoverage = Math.ceil(snapCoverage);
656
+ }
657
+ return snapCoverage;
658
+ }
659
+
660
+ class TimelineNowIndicatorLine extends internal$1.BaseComponent {
661
+ render() {
662
+ const { props, context } = this;
663
+ const xStyle = props.slotWidth == null
664
+ ? {}
665
+ : {
666
+ insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth)
667
+ };
668
+ return (preact.createElement("div", { className: classNames__default["default"].fill, style: {
669
+ zIndex: 2,
670
+ pointerEvents: 'none', // TODO: className
671
+ } },
672
+ preact.createElement(internal$1.NowIndicatorLineContainer, { className: core.joinClassNames(classNames__default["default"].fillY, classNames__default["default"].noMarginY, classNames__default["default"].borderlessY), style: xStyle, date: props.nowDate }),
673
+ preact.createElement("div", { className: core.joinClassNames(classNames__default["default"].flexCol, // better for negative margins
674
+ classNames__default["default"].fillY), style: xStyle },
675
+ preact.createElement("div", {
676
+ // stickiness on NowIndicatorDot misbehaves b/c of negative marginss
677
+ className: classNames__default["default"].stickyT },
678
+ preact.createElement(internal$1.NowIndicatorDot, null)))));
679
+ }
680
+ }
681
+
682
+ /*
683
+ TODO: DRY with other NowIndicator components
684
+ */
685
+ class TimelineNowIndicatorArrow extends internal$1.BaseComponent {
686
+ render() {
687
+ const { props, context } = this;
688
+ const xStyle = props.slotWidth == null
689
+ ? {}
690
+ : {
691
+ insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth)
692
+ };
693
+ return (preact.createElement("div", {
694
+ // crop any overflow that the arrow/line might cause
695
+ // TODO: just do this on the entire canvas within the scroller
696
+ className: core.joinClassNames(classNames__default["default"].fill, classNames__default["default"].crop), style: {
697
+ zIndex: 2,
698
+ pointerEvents: 'none', // TODO: className
699
+ } },
700
+ preact.createElement(internal$1.NowIndicatorHeaderContainer, { className: classNames__default["default"].abs, style: xStyle, date: props.nowDate })));
701
+ }
702
+ }
703
+
704
+ function getTimelineSlotEl(parentEl, index) {
705
+ return parentEl.querySelectorAll(`.${classNames__default["default"].internalTimelineSlot}`)[index];
706
+ }
707
+
708
+ /*
709
+ TODO: rename this file!
710
+ */
711
+ // returned value is between 0 and the number of snaps
627
712
  function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
628
713
  let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
629
714
  if (snapDiff < 0) {
@@ -645,6 +730,60 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
645
730
  return snapCoverage;
646
731
  }
647
732
 
733
+ class TimelineLaneSlicer extends internal$1.Slicer {
734
+ sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
735
+ let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
736
+ let segs = [];
737
+ // protect against when the span is entirely in an invalid date region
738
+ if (computeDateSnapCoverage(normalRange.start, tDateProfile, dateEnv)
739
+ < computeDateSnapCoverage(normalRange.end, tDateProfile, dateEnv)) {
740
+ // intersect the footprint's range with the grid's range
741
+ let slicedRange = internal$1.intersectRanges(normalRange, tDateProfile.normalizedRange);
742
+ if (slicedRange) {
743
+ segs.push({
744
+ startDate: slicedRange.start,
745
+ endDate: slicedRange.end,
746
+ isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
747
+ && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
748
+ isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
749
+ && isValidDate(internal$1.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
750
+ });
751
+ }
752
+ }
753
+ return segs;
754
+ }
755
+ }
756
+
757
+ const DEFAULT_TIME_FORMAT = internal$1.createFormatter({
758
+ hour: 'numeric',
759
+ minute: '2-digit',
760
+ omitZeroMinute: true,
761
+ meridiem: 'narrow',
762
+ });
763
+ class TimelineEvent extends internal$1.BaseComponent {
764
+ render() {
765
+ let { props } = this;
766
+ return (preact.createElement(internal$1.StandardEvent, Object.assign({}, props, { display: 'row', defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
767
+ }
768
+ }
769
+
770
+ class TimelineLaneMoreLink extends internal$1.BaseComponent {
771
+ render() {
772
+ let { props } = this;
773
+ let { hiddenSegs, resourceId } = props;
774
+ let dateSpanProps = resourceId ? { resourceId } : {};
775
+ return (preact.createElement(internal$1.MoreLinkContainer, { display: 'row', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, isNarrow: false, isMicro: false, popoverContent: () => (preact.createElement(preact.Fragment, null, hiddenSegs.map((seg) => {
776
+ let { eventRange } = seg;
777
+ let { instanceId } = eventRange.instance;
778
+ let isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
779
+ let isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
780
+ let isInvisible = isDragging || isResizing;
781
+ return (preact.createElement("div", { key: instanceId, style: { visibility: isInvisible ? 'hidden' : undefined } },
782
+ preact.createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: false, isSelected: instanceId === props.eventSelection }, internal$1.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
783
+ }))) }));
784
+ }
785
+ }
786
+
648
787
  function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
649
788
  const res = {};
650
789
  for (const seg of segs) {
@@ -709,83 +848,6 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
709
848
  ];
710
849
  }
711
850
 
712
- class TimelineLaneBg extends internal$1.BaseComponent {
713
- render() {
714
- let { props } = this;
715
- let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
716
- return (preact.createElement(preact.Fragment, null,
717
- this.renderSegs(props.businessHourSegs || [], 'non-business'),
718
- this.renderSegs(props.bgEventSegs || [], 'bg-event'),
719
- this.renderSegs(highlightSeg, 'highlight')));
720
- }
721
- renderSegs(segs, fillType) {
722
- let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
723
- let { dateEnv, isRtl } = this.context;
724
- return (preact.createElement(preact.Fragment, null, segs.map((seg) => {
725
- let hStyle; // TODO
726
- if (slotWidth != null) {
727
- let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
728
- hStyle = horizontalsToCss(segHorizontal, isRtl);
729
- }
730
- return (preact.createElement("div", { key: internal$1.buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
731
- preact.createElement(internal$1.BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, internal$1.getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (internal$1.renderFill(fillType))));
732
- })));
733
- }
734
- }
735
-
736
- class TimelineLaneSlicer extends internal$1.Slicer {
737
- sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
738
- let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
739
- let segs = [];
740
- // protect against when the span is entirely in an invalid date region
741
- if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
742
- < computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
743
- // intersect the footprint's range with the grid's range
744
- let slicedRange = internal$1.intersectRanges(normalRange, tDateProfile.normalizedRange);
745
- if (slicedRange) {
746
- segs.push({
747
- startDate: slicedRange.start,
748
- endDate: slicedRange.end,
749
- isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
750
- && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
751
- isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
752
- && isValidDate(internal$1.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
753
- });
754
- }
755
- }
756
- return segs;
757
- }
758
- }
759
-
760
- const DEFAULT_TIME_FORMAT = internal$1.createFormatter({
761
- hour: 'numeric',
762
- minute: '2-digit',
763
- omitZeroMinute: true,
764
- meridiem: 'narrow',
765
- });
766
- class TimelineEvent extends internal$1.BaseComponent {
767
- render() {
768
- let { props, context } = this;
769
- let { options } = context;
770
- return (preact.createElement(internal$1.StandardEvent, Object.assign({}, props, { className: internal$1.joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
771
- && 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
772
- }
773
- }
774
-
775
- class TimelineLaneMoreLink extends internal$1.BaseComponent {
776
- render() {
777
- let { props } = this;
778
- let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
779
- let dateSpanProps = resourceId ? { resourceId } : {};
780
- return (preact.createElement(internal$1.MoreLinkContainer, { className: 'fc-timeline-more-link', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, popoverContent: () => (preact.createElement(preact.Fragment, null, hiddenSegs.map((seg) => {
781
- let { eventRange } = seg;
782
- let instanceId = eventRange.instance.instanceId;
783
- return (preact.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
784
- preact.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$1.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
785
- }))) }, (InnerContent) => (preact.createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
786
- }
787
- }
788
-
789
851
  /*
790
852
  TODO: make DRY with other Event Harnesses
791
853
  */
@@ -797,7 +859,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
797
859
  }
798
860
  render() {
799
861
  const { props } = this;
800
- return (preact.createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
862
+ return (preact.createElement("div", { className: classNames__default["default"].abs, style: props.style, ref: this.rootElRef }, props.children));
801
863
  }
802
864
  componentDidMount() {
803
865
  const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
@@ -811,10 +873,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
811
873
  }
812
874
  }
813
875
 
814
- /*
815
- TODO: split TimelineLaneBg and TimelineLaneFg?
816
- */
817
- class TimelineLane extends internal$1.BaseComponent {
876
+ class TimelineFg extends internal$1.BaseComponent {
818
877
  constructor() {
819
878
  super(...arguments);
820
879
  // memo
@@ -826,8 +885,6 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
826
885
  this.moreLinkHeightRefMap = new internal$1.RefMap(() => {
827
886
  internal$1.afterSize(this.handleMoreLinkHeights);
828
887
  });
829
- // internal
830
- this.slicer = new TimelineLaneSlicer();
831
888
  this.handleMoreLinkHeights = () => {
832
889
  this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
833
890
  };
@@ -841,209 +898,92 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
841
898
  render() {
842
899
  let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
843
900
  let { options } = context;
844
- let { dateProfile, tDateProfile } = props;
845
- 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...
846
- dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
847
- let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
848
- (slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
901
+ let { tDateProfile } = props;
902
+ let mirrorSegs = (props.eventDrag ? props.eventDrag.segs : null) ||
903
+ (props.eventResize ? props.eventResize.segs : null) ||
849
904
  [];
850
- let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
905
+ let fgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
851
906
  let fgSegHorizontals = props.slotWidth != null
852
907
  ? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
853
908
  : {};
854
909
  let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
855
- let forcedInvisibleMap = // TODO: more convenient/DRY
856
- (slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
857
- (slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
858
- {};
859
- return (preact.createElement(preact.Fragment, null,
860
- preact.createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
861
- // content
862
- bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
863
- // dimensions
864
- slotWidth: props.slotWidth }),
865
- preact.createElement("div", { className: internal$1.joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
866
- ? 'fc-timeline-events-overlap-disabled'
867
- : 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
868
- this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
869
- false, // isResizing
870
- false),
871
- this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
872
- ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
873
- : {}, fgSegTops, {}, // forcedInvisibleMap
874
- [], // hiddenGroups
875
- new Map(), // hiddenGroupTops
876
- Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
877
- }
878
- renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
879
- let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
880
- let isMirror = isDragging || isResizing || isDateSelecting;
910
+ this.totalHeight = totalHeight;
911
+ return (preact.createElement("div", { className: core.joinClassNames(classNames__default["default"].rel, classNames__default["default"].noShrink), style: {
912
+ height: totalHeight,
913
+ } },
914
+ this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, hiddenGroups, hiddenGroupTops,
915
+ /* isMirror = */ false),
916
+ this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
917
+ ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
918
+ : {}, fgSegTops,
919
+ /* hiddenGroups = */ [],
920
+ /* hiddenGroupTops = */ new Map(),
921
+ /* isMirror = */ true)));
922
+ }
923
+ renderFgSegs(segs, segHorizontals, segTops, hiddenGroups, hiddenGroupTops, isMirror) {
924
+ let { props, segHeightRefMap, moreLinkHeightRefMap } = this;
881
925
  return (preact.createElement(preact.Fragment, null,
882
926
  segs.map((seg) => {
883
927
  const { eventRange } = seg;
884
928
  const { instanceId } = eventRange.instance;
885
929
  const segTop = segTops.get(instanceId);
886
- const segHorizontal = segHorizontals[instanceId];
887
- const isVisible = isMirror ||
888
- (segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
889
- return (preact.createElement(TimelineEventHarness, { key: instanceId, style: Object.assign({ visibility: isVisible ? '' : 'hidden', top: segTop || 0 }, horizontalsToCss(segHorizontal, context.isRtl)), heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
890
- preact.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$1.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
930
+ const segHorizontalMaybe = segHorizontals[instanceId];
931
+ const segHorizontal = segHorizontalMaybe || {};
932
+ const isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
933
+ const isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
934
+ const isInvisible = !isMirror && (isDragging || isResizing || !segHorizontalMaybe || segTop == null);
935
+ return (preact.createElement(TimelineEventHarness, { key: instanceId, style: {
936
+ visibility: isInvisible ? 'hidden' : undefined,
937
+ zIndex: 1,
938
+ top: segTop || 0,
939
+ insetInlineStart: segHorizontal.start,
940
+ width: segHorizontal.size,
941
+ }, heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
942
+ preact.createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: isMirror, isSelected: instanceId === props.eventSelection }, internal$1.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
891
943
  }),
892
- hiddenGroups.map((hiddenGroup) => (preact.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
893
- start: hiddenGroup.start,
894
- size: hiddenGroup.end - hiddenGroup.start
895
- }, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
896
- preact.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 }))))));
897
- }
898
- }
899
-
900
- class TimelineHeaderCell extends internal$1.BaseComponent {
901
- constructor() {
902
- super(...arguments);
903
- // memo
904
- this.refineRenderProps = internal$1.memoizeObjArg(refineRenderProps);
905
- // ref
906
- this.innerElRef = preact.createRef();
907
- }
908
- render() {
909
- let { props, context } = this;
910
- let { dateEnv, options } = context;
911
- let { cell, dateProfile, tDateProfile } = props;
912
- // the cell.rowUnit is f'd
913
- // giving 'month' for a 3-day view
914
- // workaround: to infer day, do NOT time
915
- let dateMeta = internal$1.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
916
- let renderProps = this.refineRenderProps({
917
- level: props.rowLevel,
918
- dateMarker: cell.date,
919
- text: cell.text,
920
- dateEnv: context.dateEnv,
921
- viewApi: context.viewApi,
922
- });
923
- let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
924
- return (preact.createElement(internal$1.ContentContainer, { tag: "div", className: internal$1.joinClassNames('fc-timeline-slot-label fc-timeline-slot', cell.isWeekStart && 'fc-timeline-slot-em', // TODO: document this semantic className
925
- 'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
926
- // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
927
- cell.rowUnit === 'time' ?
928
- internal$1.getSlotClassName(dateMeta) :
929
- internal$1.getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
930
- omitTime: !tDateProfile.isTimeScale,
931
- omitTimeZoneOffset: true,
932
- }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), 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.createElement(InnerContent, { tag: 'div', attrs: isNavLink
937
- // not tabbable because parent is aria-hidden
938
- ? internal$1.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
939
- : {} // don't bother with aria-hidden because parent already hidden
940
- , className: internal$1.joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
944
+ hiddenGroups.map((hiddenGroup) => (preact.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: {
945
+ top: hiddenGroupTops.get(hiddenGroup.key) || 0,
946
+ insetInlineStart: hiddenGroup.start,
947
+ width: hiddenGroup.end - hiddenGroup.start,
948
+ }, heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
949
+ preact.createElement(TimelineLaneMoreLink, { hiddenSegs: hiddenGroup.segs, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection, resourceId: props.resourceId }))))));
941
950
  }
942
- componentDidMount() {
943
- const { props } = this;
944
- const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
945
- this.detachSize = internal$1.watchSize(innerEl, (width, height) => {
946
- internal$1.setRef(props.innerWidthRef, width);
947
- internal$1.setRef(props.innerHeightRef, height);
948
- // HACK for sticky-centering
949
- innerEl.style.left = innerEl.style.right =
950
- (props.isCentered && props.isSticky)
951
- ? `calc(50% - ${width / 2}px)`
952
- : '';
953
- });
954
- }
955
- componentWillUnmount() {
956
- const { props } = this;
957
- this.detachSize();
958
- internal$1.setRef(props.innerWidthRef, null);
959
- internal$1.setRef(props.innerHeightRef, null);
960
- }
961
- }
962
- // Utils
963
- // -------------------------------------------------------------------------------------------------
964
- function renderInnerContent(renderProps) {
965
- return renderProps.text;
966
- }
967
- function refineRenderProps(input) {
968
- return {
969
- level: input.level,
970
- date: input.dateEnv.toDate(input.dateMarker),
971
- view: input.viewApi,
972
- text: input.text,
973
- };
974
- }
975
-
976
- class TimelineHeaderRow extends internal$1.BaseComponent {
977
- constructor() {
978
- super(...arguments);
979
- // refs
980
- this.innerWidthRefMap = new internal$1.RefMap(() => {
981
- internal$1.afterSize(this.handleInnerWidths);
982
- });
983
- this.innerHeightRefMap = new internal$1.RefMap(() => {
984
- internal$1.afterSize(this.handleInnerHeights);
985
- });
986
- this.handleInnerWidths = () => {
987
- const innerWidthMap = this.innerWidthRefMap.current;
988
- let max = 0;
989
- for (const innerWidth of innerWidthMap.values()) {
990
- max = Math.max(max, innerWidth);
991
- }
992
- // TODO: ensure not equal?
993
- internal$1.setRef(this.props.innerWidthRef, max);
994
- };
995
- this.handleInnerHeights = () => {
996
- const innerHeightMap = this.innerHeightRefMap.current;
997
- let max = 0;
998
- for (const innerHeight of innerHeightMap.values()) {
999
- max = Math.max(max, innerHeight);
1000
- }
1001
- // TODO: ensure not equal?
1002
- internal$1.setRef(this.props.innerHeighRef, max);
1003
- };
951
+ /*
952
+ componentDidMount(): void {
953
+ // might want to do firedTotalHeight, but won't be ready on first render
1004
954
  }
1005
- render() {
1006
- const { props, innerWidthRefMap, innerHeightRefMap } = this;
1007
- const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
1008
- const isSticky = !props.isLastRow;
1009
- return (preact.createElement("div", { className: internal$1.joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
1010
- !props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
1011
- // TODO: make this part of the cell obj?
1012
- // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
1013
- // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
1014
- // TODO: use rowUnit to key the Row itself?
1015
- const key = cell.rowUnit + ':' + cell.date.toISOString();
1016
- return (preact.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),
1017
- // refs
1018
- innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
1019
- // dimensions
1020
- slotWidth: props.slotWidth }));
1021
- })));
955
+ */
956
+ componentDidUpdate() {
957
+ if (this.totalHeight !== this.firedTotalHeight) {
958
+ this.firedTotalHeight = this.totalHeight;
959
+ internal$1.setRef(this.props.heightRef, this.totalHeight);
960
+ }
1022
961
  }
1023
962
  componentWillUnmount() {
1024
- internal$1.setRef(this.props.innerWidthRef, null);
1025
- internal$1.setRef(this.props.innerHeighRef, null);
963
+ internal$1.setRef(this.props.heightRef, null);
1026
964
  }
1027
965
  }
1028
966
 
1029
- class TimelineNowIndicatorLine extends internal$1.BaseComponent {
967
+ class TimelineBg extends internal$1.BaseComponent {
1030
968
  render() {
1031
- const { props, context } = this;
1032
- return (preact.createElement("div", { className: "fc-timeline-now-indicator-container" },
1033
- preact.createElement(internal$1.NowIndicatorContainer // TODO: make separate component?
1034
- , { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
1035
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1036
- : {}, isAxis: false, date: props.nowDate })));
969
+ let { props } = this;
970
+ let highlightSeg = [].concat(props.eventResizeSegs || [], props.dateSelectionSegs);
971
+ return (preact.createElement(preact.Fragment, null,
972
+ this.renderSegs(props.businessHourSegs || [], 'non-business'),
973
+ this.renderSegs(props.bgEventSegs || [], 'bg-event'),
974
+ this.renderSegs(highlightSeg, 'highlight')));
1037
975
  }
1038
- }
1039
-
1040
- class TimelineNowIndicatorArrow extends internal$1.BaseComponent {
1041
- render() {
1042
- const { props, context } = this;
1043
- return (preact.createElement("div", { className: "fc-timeline-now-indicator-container" },
1044
- preact.createElement(internal$1.NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
1045
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1046
- : {}, isAxis: true, date: props.nowDate })));
976
+ renderSegs(segs, fillType) {
977
+ let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
978
+ let { dateEnv, options } = this.context;
979
+ return (preact.createElement(preact.Fragment, null, segs.map((seg) => {
980
+ let hStyle = {};
981
+ if (slotWidth != null) {
982
+ let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
983
+ hStyle = { insetInlineStart: segHorizontal.start, width: segHorizontal.size };
984
+ }
985
+ return (preact.createElement("div", { key: internal$1.buildEventRangeKey(seg.eventRange), className: classNames__default["default"].fillY, style: hStyle }, fillType === 'bg-event' ? (preact.createElement(internal$1.BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isVertical: false }, internal$1.getEventRangeMeta(seg.eventRange, todayRange, nowDate)))) : (internal$1.renderFill(fillType, options))));
986
+ })));
1047
987
  }
1048
988
  }
1049
989
 
@@ -1061,34 +1001,30 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1061
1001
  internal$1.afterSize(this.handleSlotInnerWidths);
1062
1002
  });
1063
1003
  this.scrollTime = null;
1004
+ this.slicer = new TimelineLaneSlicer();
1064
1005
  // Sizing
1065
1006
  // -----------------------------------------------------------------------------------------------
1066
- this.handleBodySlotInnerWidth = (innerWidth) => {
1067
- this.bodySlotInnerWidth = innerWidth;
1068
- internal$1.afterSize(this.handleSlotInnerWidths);
1069
- };
1070
1007
  this.handleSlotInnerWidths = () => {
1071
- const { state } = this;
1072
- const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
1073
- if (state.slotInnerWidth !== slotInnerWidth) {
1074
- this.setState({ slotInnerWidth });
1008
+ const headerSlotInnerWidth = this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1);
1009
+ if (headerSlotInnerWidth != null && headerSlotInnerWidth !== this.state.slotInnerWidth) {
1010
+ this.setState({ slotInnerWidth: headerSlotInnerWidth });
1075
1011
  }
1076
1012
  };
1077
- this.handleClientWidth = (clientWidth) => {
1013
+ this.handleTotalWidth = (totalWidth) => {
1078
1014
  this.setState({
1079
- clientWidth,
1015
+ totalWidth,
1080
1016
  });
1081
1017
  };
1082
- this.handleEndScrollbarWidth = (endScrollbarWidth) => {
1018
+ this.handleClientWidth = (clientWidth) => {
1083
1019
  this.setState({
1084
- endScrollbarWidth
1020
+ clientWidth,
1085
1021
  });
1086
1022
  };
1087
1023
  this.handleTimeScrollRequest = (scrollTime) => {
1088
1024
  this.scrollTime = scrollTime;
1089
1025
  this.applyTimeScroll();
1090
1026
  };
1091
- this.handleTimeScrollEnd = ({ isUser }) => {
1027
+ this.handleTimeScrollEnd = (isUser) => {
1092
1028
  if (isUser) {
1093
1029
  this.scrollTime = null;
1094
1030
  }
@@ -1108,6 +1044,10 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1108
1044
  render() {
1109
1045
  const { props, state, context } = this;
1110
1046
  const { options } = context;
1047
+ const { totalWidth, clientWidth } = state;
1048
+ const endScrollbarWidth = (totalWidth != null && clientWidth != null)
1049
+ ? totalWidth - clientWidth
1050
+ : undefined;
1111
1051
  /* date */
1112
1052
  const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
1113
1053
  const { cellRows } = tDateProfile;
@@ -1118,39 +1058,65 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1118
1058
  const stickyFooterScrollbar = !props.forPrint && internal$1.getStickyFooterScrollbar(options);
1119
1059
  /* table positions */
1120
1060
  const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
1121
- state.clientWidth);
1061
+ clientWidth);
1122
1062
  this.slotWidth = slotWidth;
1063
+ /* sliced */
1064
+ let slicedProps = this.slicer.sliceProps(props, props.dateProfile, tDateProfile.isTimeScale ? null : options.nextDayThreshold, context, // wish we didn't have to pass in the rest of the args...
1065
+ props.dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
1123
1066
  return (preact.createElement(internal$1.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
1124
1067
  const enableNowIndicator = // TODO: DRY
1125
1068
  options.nowIndicator &&
1126
1069
  slotWidth != null &&
1127
1070
  internal$1.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
1128
- return (preact.createElement(internal$1.ViewContainer, { viewSpec: context.viewSpec, className: internal$1.joinClassNames('fc-timeline fc-border',
1129
- // HACK for Safari print-mode, where fc-scroller-no-bars won't take effect for
1071
+ return (preact.createElement(internal$1.ViewContainer, { viewSpec: context.viewSpec, className: internal$1.joinArrayishClassNames(
1072
+ // HACK for Safari print-mode, where noScrollbars won't take effect for
1130
1073
  // the below Scrollers if they have liquid flex height
1131
- !props.forPrint && 'fc-flex-col') },
1132
- preact.createElement(internal$1.Scroller, { horizontal: true, hideScrollbars: true, className: internal$1.joinClassNames('fc-timeline-header fc-flex-row fc-border-b', stickyHeaderDates && 'fc-table-header-sticky'), ref: this.headerScrollerRef },
1133
- preact.createElement("div", {
1134
- // TODO: DRY
1135
- className: internal$1.joinClassNames('fc-rel', // origin for now-indicator
1136
- canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
1137
- cellRows.map((cells, rowLevel) => {
1138
- const isLast = rowLevel === cellRows.length - 1;
1139
- return (preact.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) }));
1140
- }),
1141
- enableNowIndicator && (preact.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
1142
- Boolean(state.endScrollbarWidth) && (preact.createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
1143
- preact.createElement(internal$1.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: props.forPrint, className: internal$1.joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth },
1144
- preact.createElement("div", { "aria-label": options.eventsHint, className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
1074
+ !props.forPrint && classNames__default["default"].flexCol, props.className, options.tableClass, classNames__default["default"].isolate), borderlessX: props.borderlessX, borderlessTop: props.borderlessTop, borderlessBottom: props.borderlessBottom, noEdgeEffects: props.noEdgeEffects },
1075
+ preact.createElement("div", { className: core.joinClassNames(internal$1.generateClassName(options.tableHeaderClass, {
1076
+ isSticky: stickyHeaderDates,
1077
+ }), props.borderlessX && classNames__default["default"].borderlessX, classNames__default["default"].flexCol, stickyHeaderDates && classNames__default["default"].tableHeaderSticky), style: {
1078
+ zIndex: 1,
1079
+ } },
1080
+ preact.createElement(internal$1.Scroller, { horizontal: true, hideScrollbars: true, className: classNames__default["default"].flexRow, ref: this.headerScrollerRef },
1081
+ preact.createElement("div", {
1082
+ // TODO: DRY
1083
+ className: core.joinClassNames(classNames__default["default"].rel, // origin for now-indicator
1084
+ canvasWidth == null && classNames__default["default"].liquid), style: { width: canvasWidth } },
1085
+ cellRows.map((cells, rowIndex) => {
1086
+ const rowLevel = cellRows.length - rowIndex - 1;
1087
+ return (preact.createElement(TimelineHeaderRow, { key: rowIndex, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, cells: cells, slotWidth: slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowIndex) }));
1088
+ }),
1089
+ enableNowIndicator && (preact.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
1090
+ Boolean(endScrollbarWidth) && (preact.createElement("div", { className: internal$1.joinArrayishClassNames(internal$1.generateClassName(options.fillerClass, { isHeader: true }), classNames__default["default"].borderOnlyS), style: { minWidth: endScrollbarWidth } }))),
1091
+ preact.createElement("div", { className: internal$1.generateClassName(options.slotHeaderDividerClass, {
1092
+ isHeader: true,
1093
+ options: { dayMinWidth: options.dayMinWidth },
1094
+ }) })),
1095
+ preact.createElement(internal$1.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: stickyFooterScrollbar ||
1096
+ props.forPrint // prevents blank space in print-view on Safari
1097
+ , className: internal$1.joinArrayishClassNames(options.tableBodyClass, props.borderlessX && classNames__default["default"].borderlessX, stickyHeaderDates && classNames__default["default"].borderlessTop, (stickyHeaderDates || props.noEdgeEffects) && classNames__default["default"].noEdgeEffects, classNames__default["default"].flexCol, verticalScrolling && classNames__default["default"].liquid), style: {
1098
+ zIndex: 0,
1099
+ }, ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth },
1100
+ preact.createElement("div", { "aria-label": options.eventsHint, className: core.joinClassNames(classNames__default["default"].rel, // for canvas origin?
1101
+ classNames__default["default"].grow), style: { width: canvasWidth }, ref: this.handeBodyEl },
1145
1102
  preact.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1146
- // ref
1147
- innerWidthRef: this.handleBodySlotInnerWidth,
1148
1103
  // dimensions
1149
1104
  slotWidth: slotWidth }),
1150
- preact.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 }),
1105
+ preact.createElement(TimelineBg, { tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1106
+ // content
1107
+ bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : null,
1108
+ // dimensions
1109
+ slotWidth: slotWidth }),
1110
+ preact.createElement("div", { className: internal$1.joinArrayishClassNames(options.timelineTopClass) }),
1111
+ preact.createElement(TimelineFg, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1112
+ // content
1113
+ fgEventSegs: slicedProps.fgEventSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
1114
+ // dimensions
1115
+ slotWidth: slotWidth }),
1116
+ preact.createElement("div", { className: internal$1.joinArrayishClassNames(options.timelineBottomClass) }),
1151
1117
  enableNowIndicator && (preact.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
1152
- stickyFooterScrollbar && (preact.createElement(internal$1.Scroller, { ref: this.footerScrollerRef, horizontal: true },
1153
- preact.createElement("div", { style: { width: canvasWidth } })))));
1118
+ Boolean(stickyFooterScrollbar) && (preact.createElement(internal$1.FooterScrollbar, { isSticky: true, canvasWidth: canvasWidth, scrollerRef: this.footerScrollerRef })),
1119
+ preact.createElement(internal$1.Ruler, { widthRef: this.handleTotalWidth })));
1154
1120
  }));
1155
1121
  }
1156
1122
  // Lifecycle
@@ -1199,11 +1165,11 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1199
1165
  this.syncedScroller.scrollTo({ x });
1200
1166
  }
1201
1167
  }
1202
- queryHit(positionLeft, positionTop, elWidth, elHeight) {
1168
+ queryHit(isRtl, positionLeft, positionTop, elWidth, elHeight) {
1203
1169
  const { props, context, tDateProfile, slotWidth } = this;
1204
1170
  const { dateEnv } = context;
1205
1171
  if (slotWidth) {
1206
- const x = context.isRtl ? elWidth - positionLeft : positionLeft;
1172
+ const x = isRtl ? elWidth - positionLeft : positionLeft;
1207
1173
  const slatIndex = Math.floor(x / slotWidth);
1208
1174
  const slatX = slatIndex * slotWidth;
1209
1175
  const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
@@ -1215,7 +1181,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1215
1181
  let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
1216
1182
  let endCoord = startCoord + snapWidth;
1217
1183
  let left, right;
1218
- if (context.isRtl) {
1184
+ if (isRtl) {
1219
1185
  left = elWidth - endCoord;
1220
1186
  right = elWidth - startCoord;
1221
1187
  }
@@ -1235,8 +1201,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1235
1201
  top: 0,
1236
1202
  bottom: elHeight,
1237
1203
  },
1238
- // HACK. TODO: This is expensive to do every hit-query
1239
- dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
1204
+ getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
1240
1205
  layer: 0,
1241
1206
  };
1242
1207
  }
@@ -1244,14 +1209,17 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1244
1209
  }
1245
1210
  }
1246
1211
 
1247
- 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}";
1248
- internal$1.injectStyles(css_248z);
1212
+ const OPTION_REFINERS = {
1213
+ timelineTopClass: internal$1.identity,
1214
+ timelineBottomClass: internal$1.identity,
1215
+ };
1249
1216
 
1250
1217
  var plugin = core.createPlugin({
1251
1218
  name: '@fullcalendar/timeline',
1252
- premiumReleaseDate: '2025-01-09',
1219
+ premiumReleaseDate: '2025-12-20',
1253
1220
  deps: [premiumCommonPlugin__default["default"]],
1254
1221
  initialView: 'timelineDay',
1222
+ optionRefiners: OPTION_REFINERS,
1255
1223
  views: {
1256
1224
  timeline: {
1257
1225
  component: TimelineView,
@@ -1280,18 +1248,17 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1280
1248
  var internal = {
1281
1249
  __proto__: null,
1282
1250
  TimelineView: TimelineView,
1283
- TimelineLane: TimelineLane,
1284
- TimelineLaneBg: TimelineLaneBg,
1251
+ TimelineFg: TimelineFg,
1252
+ TimelineBg: TimelineBg,
1285
1253
  TimelineSlats: TimelineSlats,
1286
1254
  buildTimelineDateProfile: buildTimelineDateProfile,
1287
- createVerticalStyle: createVerticalStyle,
1288
- createHorizontalStyle: createHorizontalStyle,
1289
1255
  computeSlotWidth: computeSlotWidth,
1290
1256
  timeToCoord: timeToCoord,
1291
1257
  TimelineLaneSlicer: TimelineLaneSlicer,
1292
1258
  TimelineHeaderRow: TimelineHeaderRow,
1293
1259
  TimelineNowIndicatorArrow: TimelineNowIndicatorArrow,
1294
- TimelineNowIndicatorLine: TimelineNowIndicatorLine
1260
+ TimelineNowIndicatorLine: TimelineNowIndicatorLine,
1261
+ getTimelineSlotEl: getTimelineSlotEl
1295
1262
  };
1296
1263
 
1297
1264
  core.globalPlugins.push(plugin);
@@ -1303,4 +1270,4 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1303
1270
 
1304
1271
  return exports;
1305
1272
 
1306
- })({}, FullCalendar, FullCalendar.PremiumCommon, FullCalendar.Internal, FullCalendar.Preact, FullCalendar.ScrollGrid.Internal);
1273
+ })({}, FullCalendar, FullCalendar.PremiumCommon, FullCalendar.Internal, FullCalendar.InternalClassNames, FullCalendar.Preact, FullCalendar.ScrollGrid.Internal);