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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,15 @@
1
1
  /*!
2
- FullCalendar Timeline Plugin v7.0.0-beta.3
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,161 +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: {
472
- '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, {
473
436
  omitTimeZoneOffset: true,
474
437
  omitTime: !tDateProfile.isTimeScale,
475
- }),
476
- }, style: {
438
+ }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
477
439
  width: props.width,
478
- }, 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) })))));
479
515
  }
480
516
  componentDidMount() {
481
- const innerEl = this.innerElRef.current;
482
- this.disconnectInnerWidth = internal$1.watchWidth(innerEl, (width) => {
483
- 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
+ }
484
525
  });
485
526
  }
486
527
  componentWillUnmount() {
487
- this.disconnectInnerWidth();
488
- 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);
489
532
  }
490
533
  }
534
+ // Utils
535
+ // -------------------------------------------------------------------------------------------------
536
+ function renderInnerContent(renderProps) {
537
+ return renderProps.text;
538
+ }
491
539
 
492
- class TimelineSlats extends internal$1.BaseComponent {
540
+ class TimelineHeaderRow extends internal$1.BaseComponent {
493
541
  constructor() {
494
542
  super(...arguments);
543
+ // refs
495
544
  this.innerWidthRefMap = new internal$1.RefMap(() => {
496
545
  internal$1.afterSize(this.handleInnerWidths);
497
546
  });
547
+ this.innerHeightRefMap = new internal$1.RefMap(() => {
548
+ internal$1.afterSize(this.handleInnerHeights);
549
+ });
498
550
  this.handleInnerWidths = () => {
499
551
  const innerWidthMap = this.innerWidthRefMap.current;
500
552
  let max = 0;
501
553
  for (const innerWidth of innerWidthMap.values()) {
502
554
  max = Math.max(max, innerWidth);
503
555
  }
504
- // TODO: check to see if changed before firing ref!? YES. do in other places too
556
+ // TODO: ensure not equal?
505
557
  internal$1.setRef(this.props.innerWidthRef, max);
506
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
+ };
507
569
  }
508
570
  render() {
509
- let { props, innerWidthRefMap } = this;
510
- let { tDateProfile, slotWidth } = props;
511
- let { slotDates, isWeekStarts } = tDateProfile;
512
- let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
513
- return (preact.createElement("div", { className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
514
- let key = slotDate.toISOString();
515
- 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),
516
- // ref
517
- 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),
518
588
  // dimensions
519
- width: slotWidth }));
589
+ slotWidth: props.slotWidth }));
520
590
  })));
521
591
  }
522
- }
523
-
524
- /*
525
- TODO: rename this file!
526
- */
527
- // returned value is between 0 and the number of snaps
528
- function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
529
- let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
530
- if (snapDiff < 0) {
531
- return 0;
532
- }
533
- if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
534
- return tDateProfile.snapCnt;
535
- }
536
- let snapDiffInt = Math.floor(snapDiff);
537
- let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
538
- if (internal$1.isInt(snapCoverage)) { // not an in-between value
539
- snapCoverage += snapDiff - snapDiffInt; // add the remainder
540
- }
541
- else {
542
- // a fractional value, meaning the date is not visible
543
- // always round up in this case. works for start AND end dates in a range.
544
- snapCoverage = Math.ceil(snapCoverage);
545
- }
546
- return snapCoverage;
547
- }
548
- /*
549
- TODO: DRY up with elsewhere?
550
- */
551
- function horizontalsToCss(hcoord, isRtl) {
552
- if (!hcoord) {
553
- return {};
554
- }
555
- if (isRtl) {
556
- return { right: hcoord.start, width: hcoord.size };
557
- }
558
- else {
559
- return { left: hcoord.start, width: hcoord.size };
560
- }
561
- }
562
- function horizontalCoordToCss(start, isRtl) {
563
- if (isRtl) {
564
- return { right: start };
565
- }
566
- else {
567
- return { left: start };
592
+ componentWillUnmount() {
593
+ internal$1.setRef(this.props.innerWidthRef, null);
594
+ internal$1.setRef(this.props.innerHeighRef, null);
568
595
  }
569
596
  }
570
597
 
571
- function createVerticalStyle(props) {
572
- if (props) {
573
- return {
574
- top: props.start,
575
- height: props.size,
576
- };
577
- }
578
- }
579
- function createHorizontalStyle(// TODO: DRY up?
580
- props, isRtl) {
581
- if (props) {
582
- return {
583
- [isRtl ? 'right' : 'left']: props.start,
584
- width: props.size,
585
- };
586
- }
587
- }
588
598
  // Timeline-specific
589
599
  // -------------------------------------------------------------------------------------------------
590
600
  const MIN_SLOT_WIDTH = 30; // for real
@@ -597,17 +607,17 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
597
607
  }
598
608
  slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
599
609
  const slatTryWidth = viewportWidth / slatCnt;
600
- let slatLiquid;
610
+ let slotLiquid;
601
611
  let slatWidth;
602
612
  if (slatTryWidth >= slatMinWidth) {
603
- slatLiquid = true;
613
+ slotLiquid = true;
604
614
  slatWidth = slatTryWidth;
605
615
  }
606
616
  else {
607
- slatLiquid = false;
617
+ slotLiquid = false;
608
618
  slatWidth = Math.max(slatMinWidth, slatTryWidth);
609
619
  }
610
- return [slatWidth * slatCnt, slatWidth, slatLiquid];
620
+ return [slatWidth * slatCnt, slatWidth, slotLiquid];
611
621
  }
612
622
  function timeToCoord(// pixels
613
623
  time, dateEnv, dateProfile, tDateProfile, slowWidth) {
@@ -619,13 +629,86 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
619
629
  }
620
630
  function dateToCoord(// pixels
621
631
  date, dateEnv, tDateProfile, slotWidth) {
622
- let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
632
+ let snapCoverage = computeDateSnapCoverage$1(date, tDateProfile, dateEnv);
623
633
  let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
624
634
  return slotCoverage * slotWidth;
625
635
  }
626
636
  /*
627
637
  returned value is between 0 and the number of snaps
628
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
629
712
  function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
630
713
  let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
631
714
  if (snapDiff < 0) {
@@ -647,6 +730,60 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
647
730
  return snapCoverage;
648
731
  }
649
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
+
650
787
  function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
651
788
  const res = {};
652
789
  for (const seg of segs) {
@@ -711,83 +848,6 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
711
848
  ];
712
849
  }
713
850
 
714
- class TimelineLaneBg extends internal$1.BaseComponent {
715
- render() {
716
- let { props } = this;
717
- let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
718
- return (preact.createElement(preact.Fragment, null,
719
- this.renderSegs(props.businessHourSegs || [], 'non-business'),
720
- this.renderSegs(props.bgEventSegs || [], 'bg-event'),
721
- this.renderSegs(highlightSeg, 'highlight')));
722
- }
723
- renderSegs(segs, fillType) {
724
- let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
725
- let { dateEnv, isRtl } = this.context;
726
- return (preact.createElement(preact.Fragment, null, segs.map((seg) => {
727
- let hStyle; // TODO
728
- if (slotWidth != null) {
729
- let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
730
- hStyle = horizontalsToCss(segHorizontal, isRtl);
731
- }
732
- return (preact.createElement("div", { key: internal$1.buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
733
- 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))));
734
- })));
735
- }
736
- }
737
-
738
- class TimelineLaneSlicer extends internal$1.Slicer {
739
- sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
740
- let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
741
- let segs = [];
742
- // protect against when the span is entirely in an invalid date region
743
- if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
744
- < computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
745
- // intersect the footprint's range with the grid's range
746
- let slicedRange = internal$1.intersectRanges(normalRange, tDateProfile.normalizedRange);
747
- if (slicedRange) {
748
- segs.push({
749
- startDate: slicedRange.start,
750
- endDate: slicedRange.end,
751
- isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
752
- && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
753
- isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
754
- && isValidDate(internal$1.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
755
- });
756
- }
757
- }
758
- return segs;
759
- }
760
- }
761
-
762
- const DEFAULT_TIME_FORMAT = internal$1.createFormatter({
763
- hour: 'numeric',
764
- minute: '2-digit',
765
- omitZeroMinute: true,
766
- meridiem: 'narrow',
767
- });
768
- class TimelineEvent extends internal$1.BaseComponent {
769
- render() {
770
- let { props, context } = this;
771
- let { options } = context;
772
- return (preact.createElement(internal$1.StandardEvent, Object.assign({}, props, { className: internal$1.joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
773
- && 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
774
- }
775
- }
776
-
777
- class TimelineLaneMoreLink extends internal$1.BaseComponent {
778
- render() {
779
- let { props } = this;
780
- let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
781
- let dateSpanProps = resourceId ? { resourceId } : {};
782
- 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) => {
783
- let { eventRange } = seg;
784
- let instanceId = eventRange.instance.instanceId;
785
- return (preact.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
786
- 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)))));
787
- }))) }, (InnerContent) => (preact.createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
788
- }
789
- }
790
-
791
851
  /*
792
852
  TODO: make DRY with other Event Harnesses
793
853
  */
@@ -799,7 +859,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
799
859
  }
800
860
  render() {
801
861
  const { props } = this;
802
- 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));
803
863
  }
804
864
  componentDidMount() {
805
865
  const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
@@ -813,10 +873,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
813
873
  }
814
874
  }
815
875
 
816
- /*
817
- TODO: split TimelineLaneBg and TimelineLaneFg?
818
- */
819
- class TimelineLane extends internal$1.BaseComponent {
876
+ class TimelineFg extends internal$1.BaseComponent {
820
877
  constructor() {
821
878
  super(...arguments);
822
879
  // memo
@@ -828,8 +885,6 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
828
885
  this.moreLinkHeightRefMap = new internal$1.RefMap(() => {
829
886
  internal$1.afterSize(this.handleMoreLinkHeights);
830
887
  });
831
- // internal
832
- this.slicer = new TimelineLaneSlicer();
833
888
  this.handleMoreLinkHeights = () => {
834
889
  this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
835
890
  };
@@ -843,212 +898,92 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
843
898
  render() {
844
899
  let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
845
900
  let { options } = context;
846
- let { dateProfile, tDateProfile } = props;
847
- 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...
848
- dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
849
- let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
850
- (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) ||
851
904
  [];
852
- let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
905
+ let fgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
853
906
  let fgSegHorizontals = props.slotWidth != null
854
907
  ? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
855
908
  : {};
856
909
  let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
857
- let forcedInvisibleMap = // TODO: more convenient/DRY
858
- (slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
859
- (slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
860
- {};
861
- return (preact.createElement(preact.Fragment, null,
862
- preact.createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
863
- // content
864
- bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
865
- // dimensions
866
- slotWidth: props.slotWidth }),
867
- preact.createElement("div", { className: internal$1.joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
868
- ? 'fc-timeline-events-overlap-disabled'
869
- : 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
870
- this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
871
- false, // isResizing
872
- false),
873
- this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
874
- ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
875
- : {}, fgSegTops, {}, // forcedInvisibleMap
876
- [], // hiddenGroups
877
- new Map(), // hiddenGroupTops
878
- Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
879
- }
880
- renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
881
- let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
882
- 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;
883
925
  return (preact.createElement(preact.Fragment, null,
884
926
  segs.map((seg) => {
885
927
  const { eventRange } = seg;
886
928
  const { instanceId } = eventRange.instance;
887
929
  const segTop = segTops.get(instanceId);
888
- const segHorizontal = segHorizontals[instanceId];
889
- const isVisible = isMirror ||
890
- (segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
891
- 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) },
892
- 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)))));
893
943
  }),
894
- hiddenGroups.map((hiddenGroup) => (preact.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
895
- start: hiddenGroup.start,
896
- size: hiddenGroup.end - hiddenGroup.start
897
- }, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
898
- 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 }))))));
899
- }
900
- }
901
-
902
- class TimelineHeaderCell extends internal$1.BaseComponent {
903
- constructor() {
904
- super(...arguments);
905
- // memo
906
- this.refineRenderProps = internal$1.memoizeObjArg(refineRenderProps);
907
- this.buildCellNavLinkAttrs = internal$1.memoize(buildCellNavLinkAttrs);
908
- // ref
909
- this.innerElRef = preact.createRef();
910
- }
911
- render() {
912
- let { props, context } = this;
913
- let { dateEnv, options } = context;
914
- let { cell, dateProfile, tDateProfile } = props;
915
- // the cell.rowUnit is f'd
916
- // giving 'month' for a 3-day view
917
- // workaround: to infer day, do NOT time
918
- let dateMeta = internal$1.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
919
- let renderProps = this.refineRenderProps({
920
- level: props.rowLevel,
921
- dateMarker: cell.date,
922
- text: cell.text,
923
- dateEnv: context.dateEnv,
924
- viewApi: context.viewApi,
925
- });
926
- 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
927
- 'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
928
- // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
929
- cell.rowUnit === 'time' ?
930
- internal$1.getSlotClassName(dateMeta) :
931
- internal$1.getDayClassName(dateMeta)), attrs: {
932
- 'data-date': dateEnv.formatIso(cell.date, {
933
- omitTime: !tDateProfile.isTimeScale,
934
- omitTimeZoneOffset: true,
935
- }),
936
- }, style: {
937
- width: props.slotWidth != null
938
- ? props.slotWidth * cell.colspan
939
- : undefined,
940
- }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact.createElement(InnerContent, { tag: "a", attrs: this.buildCellNavLinkAttrs(context, cell.date, cell.rowUnit), 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 buildCellNavLinkAttrs(context, cellDate, rowUnit) {
965
- return (rowUnit && rowUnit !== 'time')
966
- ? internal$1.buildNavLinkAttrs(context, cellDate, rowUnit)
967
- : {};
968
- }
969
- function renderInnerContent(renderProps) {
970
- return renderProps.text;
971
- }
972
- function refineRenderProps(input) {
973
- return {
974
- level: input.level,
975
- date: input.dateEnv.toDate(input.dateMarker),
976
- view: input.viewApi,
977
- text: input.text,
978
- };
979
- }
980
-
981
- class TimelineHeaderRow extends internal$1.BaseComponent {
982
- constructor() {
983
- super(...arguments);
984
- // refs
985
- this.innerWidthRefMap = new internal$1.RefMap(() => {
986
- internal$1.afterSize(this.handleInnerWidths);
987
- });
988
- this.innerHeightRefMap = new internal$1.RefMap(() => {
989
- internal$1.afterSize(this.handleInnerHeights);
990
- });
991
- this.handleInnerWidths = () => {
992
- const innerWidthMap = this.innerWidthRefMap.current;
993
- let max = 0;
994
- for (const innerWidth of innerWidthMap.values()) {
995
- max = Math.max(max, innerWidth);
996
- }
997
- // TODO: ensure not equal?
998
- internal$1.setRef(this.props.innerWidthRef, max);
999
- };
1000
- this.handleInnerHeights = () => {
1001
- const innerHeightMap = this.innerHeightRefMap.current;
1002
- let max = 0;
1003
- for (const innerHeight of innerHeightMap.values()) {
1004
- max = Math.max(max, innerHeight);
1005
- }
1006
- // TODO: ensure not equal?
1007
- internal$1.setRef(this.props.innerHeighRef, max);
1008
- };
951
+ /*
952
+ componentDidMount(): void {
953
+ // might want to do firedTotalHeight, but won't be ready on first render
1009
954
  }
1010
- render() {
1011
- const { props, innerWidthRefMap, innerHeightRefMap } = this;
1012
- const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
1013
- const isSticky = !props.isLastRow;
1014
- return (preact.createElement("div", { className: internal$1.joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
1015
- !props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
1016
- // TODO: make this part of the cell obj?
1017
- // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
1018
- // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
1019
- // TODO: use rowUnit to key the Row itself?
1020
- const key = cell.rowUnit + ':' + cell.date.toISOString();
1021
- 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),
1022
- // refs
1023
- innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
1024
- // dimensions
1025
- slotWidth: props.slotWidth }));
1026
- })));
955
+ */
956
+ componentDidUpdate() {
957
+ if (this.totalHeight !== this.firedTotalHeight) {
958
+ this.firedTotalHeight = this.totalHeight;
959
+ internal$1.setRef(this.props.heightRef, this.totalHeight);
960
+ }
1027
961
  }
1028
962
  componentWillUnmount() {
1029
- internal$1.setRef(this.props.innerWidthRef, null);
1030
- internal$1.setRef(this.props.innerHeighRef, null);
963
+ internal$1.setRef(this.props.heightRef, null);
1031
964
  }
1032
965
  }
1033
966
 
1034
- class TimelineNowIndicatorLine extends internal$1.BaseComponent {
967
+ class TimelineBg extends internal$1.BaseComponent {
1035
968
  render() {
1036
- const { props, context } = this;
1037
- return (preact.createElement("div", { className: "fc-timeline-now-indicator-container" },
1038
- preact.createElement(internal$1.NowIndicatorContainer // TODO: make separate component?
1039
- , { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
1040
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1041
- : {}, 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')));
1042
975
  }
1043
- }
1044
-
1045
- class TimelineNowIndicatorArrow extends internal$1.BaseComponent {
1046
- render() {
1047
- const { props, context } = this;
1048
- return (preact.createElement("div", { className: "fc-timeline-now-indicator-container" },
1049
- preact.createElement(internal$1.NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
1050
- ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1051
- : {}, 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
+ })));
1052
987
  }
1053
988
  }
1054
989
 
@@ -1066,46 +1001,34 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1066
1001
  internal$1.afterSize(this.handleSlotInnerWidths);
1067
1002
  });
1068
1003
  this.scrollTime = null;
1004
+ this.slicer = new TimelineLaneSlicer();
1069
1005
  // Sizing
1070
1006
  // -----------------------------------------------------------------------------------------------
1071
- this.handleBodySlotInnerWidth = (innerWidth) => {
1072
- this.bodySlotInnerWidth = innerWidth;
1073
- internal$1.afterSize(this.handleSlotInnerWidths);
1074
- };
1075
1007
  this.handleSlotInnerWidths = () => {
1076
- const { state } = this;
1077
- const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
1078
- if (state.slotInnerWidth !== slotInnerWidth) {
1079
- 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 });
1080
1011
  }
1081
1012
  };
1082
- this.handleClientWidth = (clientWidth) => {
1013
+ this.handleTotalWidth = (totalWidth) => {
1083
1014
  this.setState({
1084
- clientWidth,
1015
+ totalWidth,
1085
1016
  });
1086
1017
  };
1087
- this.handleEndScrollbarWidth = (endScrollbarWidth) => {
1018
+ this.handleClientWidth = (clientWidth) => {
1088
1019
  this.setState({
1089
- endScrollbarWidth
1020
+ clientWidth,
1090
1021
  });
1091
1022
  };
1092
- this.handleTimeScroll = (scrollTime) => {
1023
+ this.handleTimeScrollRequest = (scrollTime) => {
1093
1024
  this.scrollTime = scrollTime;
1094
- this.updateScroll();
1025
+ this.applyTimeScroll();
1095
1026
  };
1096
- this.updateScroll = () => {
1097
- const { props, context, tDateProfile, scrollTime, slotWidth } = this;
1098
- if (scrollTime != null && slotWidth != null) {
1099
- let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
1100
- if (x) {
1101
- x += 1; // overcome border. TODO: DRY this up
1102
- }
1103
- this.syncedScroller.scrollTo({ x });
1027
+ this.handleTimeScrollEnd = (isUser) => {
1028
+ if (isUser) {
1029
+ this.scrollTime = null;
1104
1030
  }
1105
1031
  };
1106
- this.clearScroll = () => {
1107
- this.scrollTime = null;
1108
- };
1109
1032
  // Hit System
1110
1033
  // -----------------------------------------------------------------------------------------------
1111
1034
  this.handeBodyEl = (el) => {
@@ -1121,6 +1044,10 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1121
1044
  render() {
1122
1045
  const { props, state, context } = this;
1123
1046
  const { options } = context;
1047
+ const { totalWidth, clientWidth } = state;
1048
+ const endScrollbarWidth = (totalWidth != null && clientWidth != null)
1049
+ ? totalWidth - clientWidth
1050
+ : undefined;
1124
1051
  /* date */
1125
1052
  const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
1126
1053
  const { cellRows } = tDateProfile;
@@ -1131,39 +1058,65 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1131
1058
  const stickyFooterScrollbar = !props.forPrint && internal$1.getStickyFooterScrollbar(options);
1132
1059
  /* table positions */
1133
1060
  const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
1134
- state.clientWidth);
1061
+ clientWidth);
1135
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);
1136
1066
  return (preact.createElement(internal$1.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
1137
1067
  const enableNowIndicator = // TODO: DRY
1138
1068
  options.nowIndicator &&
1139
1069
  slotWidth != null &&
1140
1070
  internal$1.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
1141
- return (preact.createElement(internal$1.ViewContainer, { viewSpec: context.viewSpec, className: internal$1.joinClassNames('fc-timeline fc-border',
1142
- // 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
1143
1073
  // the below Scrollers if they have liquid flex height
1144
- !props.forPrint && 'fc-flex-col') },
1145
- 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 },
1146
- preact.createElement("div", {
1147
- // TODO: DRY
1148
- className: internal$1.joinClassNames('fc-rel', // origin for now-indicator
1149
- canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
1150
- cellRows.map((cells, rowLevel) => {
1151
- const isLast = rowLevel === cellRows.length - 1;
1152
- 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) }));
1153
- }),
1154
- enableNowIndicator && (preact.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
1155
- Boolean(state.endScrollbarWidth) && (preact.createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
1156
- 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 },
1157
- preact.createElement("div", { 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 },
1158
1102
  preact.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1159
- // ref
1160
- innerWidthRef: this.handleBodySlotInnerWidth,
1161
1103
  // dimensions
1162
1104
  slotWidth: slotWidth }),
1163
- 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) }),
1164
1117
  enableNowIndicator && (preact.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
1165
- stickyFooterScrollbar && (preact.createElement(internal$1.Scroller, { ref: this.footerScrollerRef, horizontal: true },
1166
- 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 })));
1167
1120
  }));
1168
1121
  }
1169
1122
  // Lifecycle
@@ -1172,8 +1125,8 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1172
1125
  this.syncedScroller = new internal$2.ScrollerSyncer(true); // horizontal=true
1173
1126
  this.updateSyncedScroller();
1174
1127
  this.resetScroll();
1175
- this.context.emitter.on('_timeScrollRequest', this.handleTimeScroll);
1176
- this.syncedScroller.addScrollEndListener(this.clearScroll);
1128
+ this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
1129
+ this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
1177
1130
  }
1178
1131
  componentDidUpdate(prevProps) {
1179
1132
  this.updateSyncedScroller();
@@ -1182,13 +1135,13 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1182
1135
  }
1183
1136
  else {
1184
1137
  // TODO: inefficient to update so often
1185
- this.updateScroll();
1138
+ this.applyTimeScroll();
1186
1139
  }
1187
1140
  }
1188
1141
  componentWillUnmount() {
1189
1142
  this.syncedScroller.destroy();
1190
- this.context.emitter.off('_timeScrollRequest', this.handleTimeScroll);
1191
- this.syncedScroller.removeScrollEndListener(this.clearScroll);
1143
+ this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
1144
+ this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
1192
1145
  }
1193
1146
  // Scrolling
1194
1147
  // -----------------------------------------------------------------------------------------------
@@ -1200,13 +1153,23 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1200
1153
  ]);
1201
1154
  }
1202
1155
  resetScroll() {
1203
- this.handleTimeScroll(this.context.options.scrollTime);
1156
+ this.handleTimeScrollRequest(this.context.options.scrollTime);
1157
+ }
1158
+ applyTimeScroll() {
1159
+ const { props, context, tDateProfile, scrollTime, slotWidth } = this;
1160
+ if (scrollTime != null && slotWidth != null) {
1161
+ let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
1162
+ if (x) {
1163
+ x += 1; // overcome border. TODO: DRY this up
1164
+ }
1165
+ this.syncedScroller.scrollTo({ x });
1166
+ }
1204
1167
  }
1205
- queryHit(positionLeft, positionTop, elWidth, elHeight) {
1168
+ queryHit(isRtl, positionLeft, positionTop, elWidth, elHeight) {
1206
1169
  const { props, context, tDateProfile, slotWidth } = this;
1207
1170
  const { dateEnv } = context;
1208
1171
  if (slotWidth) {
1209
- const x = context.isRtl ? elWidth - positionLeft : positionLeft;
1172
+ const x = isRtl ? elWidth - positionLeft : positionLeft;
1210
1173
  const slatIndex = Math.floor(x / slotWidth);
1211
1174
  const slatX = slatIndex * slotWidth;
1212
1175
  const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
@@ -1218,7 +1181,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1218
1181
  let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
1219
1182
  let endCoord = startCoord + snapWidth;
1220
1183
  let left, right;
1221
- if (context.isRtl) {
1184
+ if (isRtl) {
1222
1185
  left = elWidth - endCoord;
1223
1186
  right = elWidth - startCoord;
1224
1187
  }
@@ -1238,8 +1201,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1238
1201
  top: 0,
1239
1202
  bottom: elHeight,
1240
1203
  },
1241
- // HACK. TODO: This is expensive to do every hit-query
1242
- dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
1204
+ getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
1243
1205
  layer: 0,
1244
1206
  };
1245
1207
  }
@@ -1247,14 +1209,17 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1247
1209
  }
1248
1210
  }
1249
1211
 
1250
- 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}";
1251
- internal$1.injectStyles(css_248z);
1212
+ const OPTION_REFINERS = {
1213
+ timelineTopClass: internal$1.identity,
1214
+ timelineBottomClass: internal$1.identity,
1215
+ };
1252
1216
 
1253
1217
  var plugin = core.createPlugin({
1254
1218
  name: '@fullcalendar/timeline',
1255
- premiumReleaseDate: '2024-12-19',
1219
+ premiumReleaseDate: '2025-12-20',
1256
1220
  deps: [premiumCommonPlugin__default["default"]],
1257
1221
  initialView: 'timelineDay',
1222
+ optionRefiners: OPTION_REFINERS,
1258
1223
  views: {
1259
1224
  timeline: {
1260
1225
  component: TimelineView,
@@ -1283,18 +1248,17 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1283
1248
  var internal = {
1284
1249
  __proto__: null,
1285
1250
  TimelineView: TimelineView,
1286
- TimelineLane: TimelineLane,
1287
- TimelineLaneBg: TimelineLaneBg,
1251
+ TimelineFg: TimelineFg,
1252
+ TimelineBg: TimelineBg,
1288
1253
  TimelineSlats: TimelineSlats,
1289
1254
  buildTimelineDateProfile: buildTimelineDateProfile,
1290
- createVerticalStyle: createVerticalStyle,
1291
- createHorizontalStyle: createHorizontalStyle,
1292
1255
  computeSlotWidth: computeSlotWidth,
1293
1256
  timeToCoord: timeToCoord,
1294
1257
  TimelineLaneSlicer: TimelineLaneSlicer,
1295
1258
  TimelineHeaderRow: TimelineHeaderRow,
1296
1259
  TimelineNowIndicatorArrow: TimelineNowIndicatorArrow,
1297
- TimelineNowIndicatorLine: TimelineNowIndicatorLine
1260
+ TimelineNowIndicatorLine: TimelineNowIndicatorLine,
1261
+ getTimelineSlotEl: getTimelineSlotEl
1298
1262
  };
1299
1263
 
1300
1264
  core.globalPlugins.push(plugin);
@@ -1306,4 +1270,4 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1306
1270
 
1307
1271
  return exports;
1308
1272
 
1309
- })({}, FullCalendar, FullCalendar.PremiumCommon, FullCalendar.Internal, FullCalendar.Preact, FullCalendar.ScrollGrid.Internal);
1273
+ })({}, FullCalendar, FullCalendar.PremiumCommon, FullCalendar.Internal, FullCalendar.InternalClassNames, FullCalendar.Preact, FullCalendar.ScrollGrid.Internal);