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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/internal.js CHANGED
@@ -1,8 +1,6 @@
1
- import { injectStyles, config, createFormatter, greatestDurationDenominator, asCleanDays, createDuration, wholeDivideDurations, asRoughMs, addDays, startOfDay, asRoughSeconds, asRoughMinutes, diffWholeDays, isInt, computeVisibleDayRange, padStart, BaseComponent, getDateMeta, ContentContainer, getDayClassNames, getSlotClassNames, watchWidth, setRef, RefMap, afterSize, SegHierarchy, groupIntersectingEntries, buildEventRangeKey, BgEvent, getSegMeta, renderFill, Slicer, intersectRanges, addMs, StandardEvent, MoreLinkContainer, watchHeight, memoize, sortEventSegs, memoizeObjArg, watchSize, buildNavLinkAttrs, NowIndicatorContainer, DateComponent, ScrollResponder, getIsHeightAuto, getStickyHeaderDates, getStickyFooterScrollbar, NowTimer, rangeContainsMarker, ViewContainer, Scroller, getScrollerSyncerClass, multiplyDuration } from '@fullcalendar/core/internal.js';
1
+ import { config, createFormatter, greatestDurationDenominator, asCleanDays, createDuration, wholeDivideDurations, asRoughMs, addDays, startOfDay, asRoughSeconds, asRoughMinutes, diffWholeDays, isInt, computeVisibleDayRange, padStart, BaseComponent, getDateMeta, ContentContainer, joinClassNames, getDayClassName, getSlotClassName, watchWidth, setRef, RefMap, afterSize, getEventKey, SegHierarchy, groupIntersectingSegs, buildEventRangeKey, BgEvent, getEventRangeMeta, renderFill, Slicer, intersectRanges, addMs, StandardEvent, MoreLinkContainer, watchHeight, memoize, sortEventSegs, memoizeObjArg, watchSize, buildNavLinkAttrs, NowIndicatorContainer, DateComponent, getIsHeightAuto, getStickyHeaderDates, getStickyFooterScrollbar, NowTimer, rangeContainsMarker, ViewContainer, Scroller, multiplyDuration, injectStyles } from '@fullcalendar/core/internal.js';
2
2
  import { createRef, createElement, Fragment, Component } from '@fullcalendar/core/preact.js';
3
-
4
- var css_248z = ".fc-timeline-slots{z-index:1}.fc-timeline-slot-minor{border-style:dotted}.fc-timeline-now-indicator-container{bottom:0;left:0;overflow:hidden;position:absolute;right:0;top:0;z-index:4}.fc-timeline-now-indicator-arrow,.fc-timeline-now-indicator-line{border-color:var(--fc-now-indicator-color);border-style:solid;pointer-events:none;position:absolute;top:0}.fc-timeline-now-indicator-arrow{border-left-color:transparent;border-right-color:transparent;border-width:6px 5px 0;margin:0 -5px}.fc-timeline-now-indicator-line{border-width:0 0 0 1px;bottom:0}.fc-timeline-events{z-index:3}.fc-timeline-events-overlap-enabled{padding-bottom:10px}.fc-timeline-event{border-radius:0;font-size:var(--fc-small-font-size);margin-bottom:1px;z-index:1}.fc-timeline-event.fc-event-mirror{z-index:2}.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-spacious{margin-bottom:0;padding-bottom:5px;padding-top:5px}.fc-timeline-event .fc-event-inner{align-items:center;display:flex;flex-direction:row;padding:2px 1px}.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-event .fc-event-time{font-weight:700}.fc-timeline-event .fc-event-time,.fc-timeline-event .fc-event-title{padding:0 2px}.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-bg{bottom:0;left:0;position:absolute;right:0;top:0;z-index:2}.fc-timeline-bg .fc-non-business{z-index:1}.fc-timeline-bg .fc-bg-event{z-index:2}.fc-timeline-bg .fc-highlight{z-index:3}.fc-timeline-bg-harness{bottom:0;position:absolute;top:0}";
5
- injectStyles(css_248z);
3
+ import { ScrollerSyncer } from '@fullcalendar/scrollgrid/internal.js';
6
4
 
7
5
  const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
8
6
  const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
@@ -450,42 +448,36 @@ class TimelineSlatCell extends BaseComponent {
450
448
  }
451
449
  render() {
452
450
  let { props, context } = this;
453
- let { dateEnv, options, theme } = context;
451
+ let { dateEnv, options } = context;
454
452
  let { date, tDateProfile, isEm } = props;
455
453
  let dateMeta = getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
456
454
  let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
457
- return (createElement(ContentContainer, { elTag: "div", elClasses: [
458
- 'fc-flex-column',
459
- 'fc-align-start',
460
- 'fc-cell',
461
- 'fc-timeline-slot',
462
- 'fc-timeline-slot-lane',
463
- isEm ? 'fc-timeline-slot-em' : '',
464
- tDateProfile.isTimeScale ? (isInt(dateEnv.countDurationsBetween(// best to do this here?
465
- tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
466
- 'fc-timeline-slot-major' :
467
- 'fc-timeline-slot-minor') : '',
468
- ...(props.isDay ?
469
- getDayClassNames(dateMeta, theme) :
470
- getSlotClassNames(dateMeta, theme)),
471
- ], elAttrs: {
455
+ return (createElement(ContentContainer, { tag: "div",
456
+ // fc-align-start shrinks width of InnerContent
457
+ // TODO: document this semantic className fc-timeline-slot-em
458
+ className: joinClassNames('fc-timeline-slot', isEm && 'fc-timeline-slot-em', tDateProfile.isTimeScale && (isInt(dateEnv.countDurationsBetween(// best to do this here?
459
+ tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
460
+ 'fc-timeline-slot-major' :
461
+ 'fc-timeline-slot-minor'), 'fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start', props.borderStart && 'fc-border-s', props.isDay ?
462
+ getDayClassName(dateMeta) :
463
+ getSlotClassName(dateMeta)), attrs: {
472
464
  'data-date': dateEnv.formatIso(date, {
473
465
  omitTimeZoneOffset: true,
474
466
  omitTime: !tDateProfile.isTimeScale,
475
467
  }),
476
- }, elStyle: {
468
+ }, style: {
477
469
  width: props.width,
478
- }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (createElement("div", { ref: this.innerElRef, className: 'fc-flex-column' },
479
- createElement(InnerContent, { elTag: "div", elClasses: ['fc-cell-inner'] })))));
470
+ }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-cell-inner', elRef: this.innerElRef }))));
480
471
  }
481
472
  componentDidMount() {
482
473
  const innerEl = this.innerElRef.current;
483
- this.detachWidth = watchWidth(innerEl, (width) => {
474
+ this.disconnectInnerWidth = watchWidth(innerEl, (width) => {
484
475
  setRef(this.props.innerWidthRef, width);
485
476
  });
486
477
  }
487
478
  componentWillUnmount() {
488
- this.detachWidth();
479
+ this.disconnectInnerWidth();
480
+ setRef(this.props.innerWidthRef, null);
489
481
  }
490
482
  }
491
483
 
@@ -510,9 +502,9 @@ class TimelineSlats extends BaseComponent {
510
502
  let { tDateProfile, slotWidth } = props;
511
503
  let { slotDates, isWeekStarts } = tDateProfile;
512
504
  let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
513
- return (createElement("div", { className: "fc-timeline-slots fc-fill fc-flex-row" }, slotDates.map((slotDate, i) => {
505
+ return (createElement("div", { className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
514
506
  let key = slotDate.toISOString();
515
- return (createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isEm: isWeekStarts[i], isDay: isDay,
507
+ return (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
508
  // ref
517
509
  innerWidthRef: innerWidthRefMap.createRef(key),
518
510
  // dimensions
@@ -546,30 +538,6 @@ function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
546
538
  return snapCoverage;
547
539
  }
548
540
  /*
549
- TODO: audit!!!
550
- */
551
- function coordToCss(hcoord, isRtl) {
552
- if (hcoord === null) {
553
- return { left: '', right: '' };
554
- }
555
- if (isRtl) {
556
- return { right: hcoord, left: '' };
557
- }
558
- return { left: hcoord, right: '' };
559
- }
560
- /*
561
- TODO: audit!!!
562
- */
563
- function coordsToCss(hcoords, isRtl) {
564
- if (!hcoords) {
565
- return { left: '', right: '' };
566
- }
567
- if (isRtl) {
568
- return { right: hcoords.start, left: -hcoords.end };
569
- }
570
- return { left: hcoords.start, right: -hcoords.end };
571
- }
572
- /*
573
541
  TODO: DRY up with elsewhere?
574
542
  */
575
543
  function horizontalsToCss(hcoord, isRtl) {
@@ -600,7 +568,8 @@ function createVerticalStyle(props) {
600
568
  };
601
569
  }
602
570
  }
603
- function createHorizontalStyle(props, isRtl) {
571
+ function createHorizontalStyle(// TODO: DRY up?
572
+ props, isRtl) {
604
573
  if (props) {
605
574
  return {
606
575
  [isRtl ? 'right' : 'left']: props.start,
@@ -673,13 +642,13 @@ function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
673
642
  function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
674
643
  const res = {};
675
644
  for (const seg of segs) {
676
- res[seg.eventRange.instance.instanceId] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
645
+ res[getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
677
646
  }
678
647
  return res;
679
648
  }
680
649
  function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
681
- const startCoord = dateToCoord(seg.start, dateEnv, tDateProfile, slotWidth);
682
- const endCoord = dateToCoord(seg.end, dateEnv, tDateProfile, slotWidth);
650
+ const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
651
+ const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
683
652
  let size = endCoord - startCoord;
684
653
  if (segMinWidth) {
685
654
  size = Math.max(size, segMinWidth);
@@ -687,95 +656,58 @@ function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidt
687
656
  return { start: startCoord, size };
688
657
  }
689
658
  function computeFgSegPlacements(// mostly horizontals
690
- segs, segHorizontals, segHeights, // keyed by instanceId
691
- strictOrder, maxStackCnt) {
692
- let segEntries = [];
693
- for (let i = 0; i < segs.length; i += 1) {
694
- let seg = segs[i];
695
- let instanceId = seg.eventRange.instance.instanceId;
696
- let height = segHeights.get(instanceId);
697
- let hcoords = segHorizontals[instanceId];
698
- if (height != null && hcoords != null) {
699
- segEntries.push({
700
- index: i,
701
- seg,
702
- span: {
703
- start: hcoords.start,
704
- end: hcoords.start + hcoords.size,
705
- },
706
- thickness: height,
707
- });
659
+ segs, segHorizontals, // TODO: use Map
660
+ segHeights, // keyed by instanceId
661
+ hiddenGroupHeights, strictOrder, maxDepth) {
662
+ const segRanges = [];
663
+ // isn't it true that there will either be ALL hcoords or NONE? can optimize
664
+ for (const seg of segs) {
665
+ const hcoords = segHorizontals[getEventKey(seg)];
666
+ if (hcoords) {
667
+ segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
708
668
  }
709
669
  }
710
- let hierarchy = new SegHierarchy();
711
- if (strictOrder != null) {
712
- hierarchy.strictOrder = strictOrder;
713
- }
714
- if (maxStackCnt != null) {
715
- hierarchy.maxStackCnt = maxStackCnt;
716
- }
717
- let hiddenEntries = hierarchy.addSegs(segEntries);
718
- let hiddenGroups = groupIntersectingEntries(hiddenEntries);
719
- let hiddenGroupEntries = hiddenGroups.map((hiddenGroup, index) => ({
720
- index: segs.length + index,
721
- segGroup: hiddenGroup,
722
- span: hiddenGroup.span,
723
- thickness: 1, // HACK to ensure it's placed
724
- }));
725
- // add more-links into the hierarchy, but don't limit
726
- hierarchy.maxStackCnt = -1;
727
- hierarchy.addSegs(hiddenGroupEntries);
728
- let visibleRects = hierarchy.toRects();
729
- let segTops = {};
730
- let hiddenGroupTops = {};
731
- for (let rect of visibleRects) {
732
- const { seg, segGroup } = rect;
733
- if (seg) { // regular seg
734
- segTops[seg.eventRange.instance.instanceId] = rect.levelCoord;
735
- }
736
- else { // hiddenGroup
737
- hiddenGroupTops[segGroup.key] = rect.levelCoord;
670
+ const hierarchy = new SegHierarchy(segRanges, (seg) => segHeights.get(getEventKey(seg)), strictOrder, undefined, // maxCoord
671
+ maxDepth);
672
+ const segTops = new Map();
673
+ hierarchy.traverseSegs((seg, segTop) => {
674
+ segTops.set(getEventKey(seg), segTop);
675
+ });
676
+ const { hiddenSegs } = hierarchy;
677
+ let totalHeight = 0;
678
+ for (const segRange of segRanges) {
679
+ const segKey = getEventKey(segRange);
680
+ const segHeight = segHeights.get(segKey);
681
+ const segTop = segTops.get(segKey);
682
+ if (segHeight != null) {
683
+ if (segTop != null) {
684
+ totalHeight = Math.max(totalHeight, segTop + segHeight);
685
+ }
738
686
  }
739
687
  }
688
+ const hiddenGroups = groupIntersectingSegs(hiddenSegs);
689
+ const hiddenGroupTops = new Map();
690
+ // HACK for hiddenGroup findInsertion() call
691
+ hierarchy.strictOrder = true;
692
+ for (const hiddenGroup of hiddenGroups) {
693
+ const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
694
+ const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
695
+ hiddenGroupTops.set(hiddenGroup.key, top);
696
+ totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
697
+ }
740
698
  return [
741
699
  segTops,
742
- computeMaxBottom(segs, segTops, segHeights),
743
700
  hiddenGroups,
744
701
  hiddenGroupTops,
702
+ totalHeight,
745
703
  ];
746
704
  }
747
- function computeMaxBottom(segs, segTops, segHeights) {
748
- let max = 0;
749
- for (const seg of segs) {
750
- const { instanceId } = seg.eventRange.instance;
751
- const top = segTops[instanceId];
752
- const height = segHeights.get(instanceId);
753
- if (top != null && height != null) {
754
- max = Math.max(max, top + height);
755
- }
756
- }
757
- return max;
758
- }
759
- /*
760
- TODO: converge with computeMaxBottom, but keys are different
761
- */
762
- function computeMoreLinkMaxBottom(hiddenGroups, hiddenGroupTops, hiddenGroupHeights) {
763
- let max = 0;
764
- for (const hiddenGroup of hiddenGroups) {
765
- const top = hiddenGroupTops[hiddenGroup.key];
766
- const height = hiddenGroupHeights.get(hiddenGroup.key);
767
- if (top != null && height != null) {
768
- max = Math.max(max, top + height);
769
- }
770
- }
771
- return max;
772
- }
773
705
 
774
706
  class TimelineLaneBg extends BaseComponent {
775
707
  render() {
776
708
  let { props } = this;
777
709
  let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
778
- return (createElement("div", { className: "fc-timeline-bg" },
710
+ return (createElement(Fragment, null,
779
711
  this.renderSegs(props.businessHourSegs || [], 'non-business'),
780
712
  this.renderSegs(props.bgEventSegs || [], 'bg-event'),
781
713
  this.renderSegs(highlightSeg, 'highlight')));
@@ -789,9 +721,8 @@ class TimelineLaneBg extends BaseComponent {
789
721
  let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
790
722
  hStyle = horizontalsToCss(segHorizontal, isRtl);
791
723
  }
792
- return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-timeline-bg-harness", style: hStyle }, fillType === 'bg-event' ?
793
- createElement(BgEvent, Object.assign({ seg: seg }, getSegMeta(seg, todayRange, nowDate))) :
794
- renderFill(fillType)));
724
+ return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
725
+ createElement(BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (renderFill(fillType))));
795
726
  })));
796
727
  }
797
728
  }
@@ -807,8 +738,8 @@ class TimelineLaneSlicer extends Slicer {
807
738
  let slicedRange = intersectRanges(normalRange, tDateProfile.normalizedRange);
808
739
  if (slicedRange) {
809
740
  segs.push({
810
- start: slicedRange.start,
811
- end: slicedRange.end,
741
+ startDate: slicedRange.start,
742
+ endDate: slicedRange.end,
812
743
  isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
813
744
  && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
814
745
  isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
@@ -830,13 +761,8 @@ class TimelineEvent extends BaseComponent {
830
761
  render() {
831
762
  let { props, context } = this;
832
763
  let { options } = context;
833
- return (createElement(StandardEvent, Object.assign({}, props, { elClasses: [
834
- 'fc-timeline-event',
835
- 'fc-h-event',
836
- options.eventOverlap === false // TODO: fix bad default
837
- ? 'fc-timeline-event-spacious'
838
- : ''
839
- ], defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
764
+ return (createElement(StandardEvent, Object.assign({}, props, { className: joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
765
+ && 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
840
766
  }
841
767
  }
842
768
 
@@ -844,12 +770,13 @@ class TimelineLaneMoreLink extends BaseComponent {
844
770
  render() {
845
771
  let { props } = this;
846
772
  let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
847
- let extraDateSpan = resourceId ? { resourceId } : {};
848
- return (createElement(MoreLinkContainer, { elClasses: ['fc-timeline-more-link'], allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, extraDateSpan: extraDateSpan, popoverContent: () => (createElement(Fragment, null, hiddenSegs.map((seg) => {
849
- let instanceId = seg.eventRange.instance.instanceId;
773
+ let dateSpanProps = resourceId ? { resourceId } : {};
774
+ return (createElement(MoreLinkContainer, { className: 'fc-timeline-more-link', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, popoverContent: () => (createElement(Fragment, null, hiddenSegs.map((seg) => {
775
+ let { eventRange } = seg;
776
+ let instanceId = eventRange.instance.instanceId;
850
777
  return (createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
851
- createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection }, getSegMeta(seg, props.todayRange, props.nowDate)))));
852
- }))) }, (InnerContent) => (createElement(InnerContent, { elTag: "div", elClasses: ['fc-timeline-more-link-inner', 'fc-sticky-x'] }))));
778
+ 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 }, getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
779
+ }))) }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
853
780
  }
854
781
  }
855
782
 
@@ -868,12 +795,13 @@ class TimelineEventHarness extends Component {
868
795
  }
869
796
  componentDidMount() {
870
797
  const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
871
- this.detachHeight = watchHeight(rootEl, (height) => {
798
+ this.disconnectHeight = watchHeight(rootEl, (height) => {
872
799
  setRef(this.props.heightRef, height);
873
800
  });
874
801
  }
875
802
  componentWillUnmount() {
876
- this.detachHeight();
803
+ this.disconnectHeight();
804
+ setRef(this.props.heightRef, null);
877
805
  }
878
806
  }
879
807
 
@@ -905,7 +833,7 @@ class TimelineLane extends BaseComponent {
905
833
  TODO: lots of memoization needed here!
906
834
  */
907
835
  render() {
908
- let { props, context, segHeightRefMap } = this;
836
+ let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
909
837
  let { options } = context;
910
838
  let { dateProfile, tDateProfile } = props;
911
839
  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...
@@ -917,10 +845,7 @@ class TimelineLane extends BaseComponent {
917
845
  let fgSegHorizontals = props.slotWidth != null
918
846
  ? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
919
847
  : {};
920
- let [fgSegTops, fgSegsBottom, hiddenGroups, hiddenGroupTops] = computeFgSegPlacements(// verticals
921
- fgSegs, fgSegHorizontals, segHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
922
- let moreLinksBottom = computeMoreLinkMaxBottom(hiddenGroups, hiddenGroupTops, this.moreLinkHeightRefMap.current);
923
- let innerHeight = Math.max(moreLinksBottom, fgSegsBottom);
848
+ let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
924
849
  let forcedInvisibleMap = // TODO: more convenient/DRY
925
850
  (slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
926
851
  (slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
@@ -931,39 +856,38 @@ class TimelineLane extends BaseComponent {
931
856
  bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
932
857
  // dimensions
933
858
  slotWidth: props.slotWidth }),
934
- createElement("div", { className: [
935
- 'fc-timeline-events',
936
- 'fc-content-box',
937
- options.eventOverlap === false // TODO: fix bad default
938
- ? 'fc-timeline-events-overlap-disabled'
939
- : 'fc-timeline-events-overlap-enabled'
940
- ].join(' '), style: { height: innerHeight } },
859
+ createElement("div", { className: joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
860
+ ? 'fc-timeline-events-overlap-disabled'
861
+ : 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
941
862
  this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
942
863
  false, // isResizing
943
864
  false),
944
865
  this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
945
866
  ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
946
867
  : {}, fgSegTops, {}, // forcedInvisibleMap
947
- [], {}, Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
868
+ [], // hiddenGroups
869
+ new Map(), // hiddenGroupTops
870
+ Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
948
871
  }
949
872
  renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
950
873
  let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
951
874
  let isMirror = isDragging || isResizing || isDateSelecting;
952
875
  return (createElement(Fragment, null,
953
876
  segs.map((seg) => {
954
- const { instanceId } = seg.eventRange.instance;
955
- const segTop = segTops[instanceId];
877
+ const { eventRange } = seg;
878
+ const { instanceId } = eventRange.instance;
879
+ const segTop = segTops.get(instanceId);
956
880
  const segHorizontal = segHorizontals[instanceId];
957
881
  const isVisible = isMirror ||
958
882
  (segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
959
883
  return (createElement(TimelineEventHarness, { key: instanceId, style: Object.assign({ visibility: isVisible ? '' : 'hidden', top: segTop || 0 }, horizontalsToCss(segHorizontal, context.isRtl)), heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
960
- createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection /* TODO: bad for mirror? */ }, getSegMeta(seg, props.todayRange, props.nowDate)))));
884
+ 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? */ }, getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
961
885
  }),
962
- hiddenGroups.map((hiddenGroup) => (createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops[hiddenGroup.key] || 0 }, horizontalsToCss({
963
- start: hiddenGroup.span.start,
964
- size: hiddenGroup.span.end - hiddenGroup.span.start
886
+ hiddenGroups.map((hiddenGroup) => (createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
887
+ start: hiddenGroup.start,
888
+ size: hiddenGroup.end - hiddenGroup.start
965
889
  }, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
966
- createElement(TimelineLaneMoreLink, { hiddenSegs: hiddenGroup.segs /* TODO: make SegGroup generic! */, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventSelection: props.eventSelection, resourceId: props.resourceId, forcedInvisibleMap: forcedInvisibleMap }))))));
890
+ 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 }))))));
967
891
  }
968
892
  }
969
893
 
@@ -975,7 +899,6 @@ class TimelineHeaderCell extends BaseComponent {
975
899
  this.buildCellNavLinkAttrs = memoize(buildCellNavLinkAttrs);
976
900
  // ref
977
901
  this.innerElRef = createRef();
978
- // TODO: unset width/height ref on unmount?
979
902
  }
980
903
  render() {
981
904
  let { props, context } = this;
@@ -992,36 +915,21 @@ class TimelineHeaderCell extends BaseComponent {
992
915
  dateEnv: context.dateEnv,
993
916
  viewApi: context.viewApi,
994
917
  });
995
- return (createElement(ContentContainer, { elTag: "div", elClasses: [
996
- 'fc-timeline-slot-label',
997
- 'fc-timeline-slot',
998
- cell.isWeekStart ? 'fc-timeline-slot-em' : '',
999
- 'fc-header-cell',
1000
- 'fc-cell',
1001
- 'fc-flex-column',
1002
- 'fc-justify-center',
1003
- props.isCentered ? 'fc-align-center' : 'fc-align-start',
1004
- ...( // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
1005
- cell.rowUnit === 'time' ?
1006
- getSlotClassNames(dateMeta, context.theme) :
1007
- getDayClassNames(dateMeta, context.theme)),
1008
- ], elAttrs: {
918
+ return (createElement(ContentContainer, { tag: "div", className: joinClassNames('fc-timeline-slot-label fc-timeline-slot', cell.isWeekStart && 'fc-timeline-slot-em', // TODO: document this semantic className
919
+ 'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
920
+ // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
921
+ cell.rowUnit === 'time' ?
922
+ getSlotClassName(dateMeta) :
923
+ getDayClassName(dateMeta)), attrs: {
1009
924
  'data-date': dateEnv.formatIso(cell.date, {
1010
925
  omitTime: !tDateProfile.isTimeScale,
1011
926
  omitTimeZoneOffset: true,
1012
927
  }),
1013
- }, elStyle: {
928
+ }, style: {
1014
929
  width: props.slotWidth != null
1015
930
  ? props.slotWidth * cell.colspan
1016
931
  : undefined,
1017
- }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (createElement("div", { ref: this.innerElRef, className: [
1018
- 'fc-flex-column',
1019
- props.isSticky ? 'fc-sticky-x' : '',
1020
- ].join(' ') },
1021
- createElement(InnerContent, { elTag: "a", elClasses: [
1022
- 'fc-cell-inner',
1023
- 'fc-padding-md',
1024
- ], elAttrs: this.buildCellNavLinkAttrs(context, cell.date, cell.rowUnit) })))));
932
+ }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (createElement(InnerContent, { tag: "a", attrs: this.buildCellNavLinkAttrs(context, cell.date, cell.rowUnit), className: joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
1025
933
  }
1026
934
  componentDidMount() {
1027
935
  const { props } = this;
@@ -1037,7 +945,10 @@ class TimelineHeaderCell extends BaseComponent {
1037
945
  });
1038
946
  }
1039
947
  componentWillUnmount() {
948
+ const { props } = this;
1040
949
  this.detachSize();
950
+ setRef(props.innerWidthRef, null);
951
+ setRef(props.innerHeightRef, null);
1041
952
  }
1042
953
  }
1043
954
  // Utils
@@ -1092,18 +1003,24 @@ class TimelineHeaderRow extends BaseComponent {
1092
1003
  const { props, innerWidthRefMap, innerHeightRefMap } = this;
1093
1004
  const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
1094
1005
  const isSticky = !props.isLastRow;
1095
- return (createElement("div", { className: 'fc-row', style: { height: props.height } }, props.cells.map((cell) => {
1006
+ return (createElement("div", { className: joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
1007
+ !props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
1096
1008
  // TODO: make this part of the cell obj?
1097
1009
  // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
1098
1010
  // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
1011
+ // TODO: use rowUnit to key the Row itself?
1099
1012
  const key = cell.rowUnit + ':' + cell.date.toISOString();
1100
- return (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,
1013
+ return (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),
1101
1014
  // refs
1102
1015
  innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
1103
1016
  // dimensions
1104
1017
  slotWidth: props.slotWidth }));
1105
1018
  })));
1106
1019
  }
1020
+ componentWillUnmount() {
1021
+ setRef(this.props.innerWidthRef, null);
1022
+ setRef(this.props.innerHeighRef, null);
1023
+ }
1107
1024
  }
1108
1025
 
1109
1026
  class TimelineNowIndicatorLine extends BaseComponent {
@@ -1111,7 +1028,7 @@ class TimelineNowIndicatorLine extends BaseComponent {
1111
1028
  const { props, context } = this;
1112
1029
  return (createElement("div", { className: "fc-timeline-now-indicator-container" },
1113
1030
  createElement(NowIndicatorContainer // TODO: make separate component?
1114
- , { elClasses: ['fc-timeline-now-indicator-line'], elStyle: props.slotWidth != null
1031
+ , { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
1115
1032
  ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1116
1033
  : {}, isAxis: false, date: props.nowDate })));
1117
1034
  }
@@ -1121,7 +1038,7 @@ class TimelineNowIndicatorArrow extends BaseComponent {
1121
1038
  render() {
1122
1039
  const { props, context } = this;
1123
1040
  return (createElement("div", { className: "fc-timeline-now-indicator-container" },
1124
- createElement(NowIndicatorContainer, { elClasses: ['fc-timeline-now-indicator-arrow'], elStyle: props.slotWidth != null
1041
+ createElement(NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
1125
1042
  ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1126
1043
  : {}, isAxis: true, date: props.nowDate })));
1127
1044
  }
@@ -1137,48 +1054,50 @@ class TimelineView extends DateComponent {
1137
1054
  this.headerScrollerRef = createRef();
1138
1055
  this.bodyScrollerRef = createRef();
1139
1056
  this.footerScrollerRef = createRef();
1057
+ this.headerRowInnerWidthMap = new RefMap(() => {
1058
+ afterSize(this.handleSlotInnerWidths);
1059
+ });
1060
+ this.scrollTime = null;
1140
1061
  // Sizing
1141
1062
  // -----------------------------------------------------------------------------------------------
1142
- this.handleHeaderSlotInnerWidth = (innerWidth) => {
1143
- this.headerSlotInnerWidth = innerWidth;
1144
- afterSize(this.handleSlotInnerWidths);
1145
- };
1146
1063
  this.handleBodySlotInnerWidth = (innerWidth) => {
1147
1064
  this.bodySlotInnerWidth = innerWidth;
1148
1065
  afterSize(this.handleSlotInnerWidths);
1149
1066
  };
1150
1067
  this.handleSlotInnerWidths = () => {
1151
1068
  const { state } = this;
1152
- const slotInnerWidth = Math.max(this.headerSlotInnerWidth, this.bodySlotInnerWidth);
1069
+ const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
1153
1070
  if (state.slotInnerWidth !== slotInnerWidth) {
1154
1071
  this.setState({ slotInnerWidth });
1155
1072
  }
1156
1073
  };
1157
- this.handleScrollerWidth = (scrollerWidth) => {
1074
+ this.handleClientWidth = (clientWidth) => {
1158
1075
  this.setState({
1159
- scrollerWidth,
1076
+ clientWidth,
1160
1077
  });
1161
1078
  };
1162
- this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
1079
+ this.handleEndScrollbarWidth = (endScrollbarWidth) => {
1163
1080
  this.setState({
1164
- leftScrollbarWidth
1081
+ endScrollbarWidth
1165
1082
  });
1166
1083
  };
1167
- this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
1168
- this.setState({
1169
- rightScrollbarWidth
1170
- });
1084
+ this.handleTimeScroll = (scrollTime) => {
1085
+ this.scrollTime = scrollTime;
1086
+ this.updateScroll();
1171
1087
  };
1172
- this.timeScrollResponder = new ScrollResponder((time) => {
1173
- const { props, context, tDateProfile, slotWidth } = this;
1174
- if (slotWidth != null) {
1175
- const x = timeToCoord(time, context.dateEnv, props.dateProfile, tDateProfile, slotWidth) +
1176
- (context.isRtl ? -1 : 1); // overcome border. TODO: DRY this up
1088
+ this.updateScroll = () => {
1089
+ const { props, context, tDateProfile, scrollTime, slotWidth } = this;
1090
+ if (scrollTime != null && slotWidth != null) {
1091
+ let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
1092
+ if (x) {
1093
+ x += 1; // overcome border. TODO: DRY this up
1094
+ }
1177
1095
  this.syncedScroller.scrollTo({ x });
1178
- return true;
1179
1096
  }
1180
- return false;
1181
- });
1097
+ };
1098
+ this.clearScroll = () => {
1099
+ this.scrollTime = null;
1100
+ };
1182
1101
  // Hit System
1183
1102
  // -----------------------------------------------------------------------------------------------
1184
1103
  this.handeBodyEl = (el) => {
@@ -1204,44 +1123,30 @@ class TimelineView extends DateComponent {
1204
1123
  const stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(options);
1205
1124
  /* table positions */
1206
1125
  const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
1207
- state.scrollerWidth);
1126
+ state.clientWidth);
1208
1127
  this.slotWidth = slotWidth;
1209
1128
  return (createElement(NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
1210
1129
  const enableNowIndicator = // TODO: DRY
1211
1130
  options.nowIndicator &&
1212
1131
  slotWidth != null &&
1213
1132
  rangeContainsMarker(props.dateProfile.currentRange, nowDate);
1214
- return (createElement(ViewContainer, { viewSpec: context.viewSpec, elClasses: [
1215
- 'fc-timeline-view',
1216
- 'fc-flex-column',
1217
- 'fc-border',
1218
- ] },
1219
- createElement(Scroller, { horizontal: true, hideScrollbars: true, elClassNames: [
1220
- 'fc-timeline-header',
1221
- 'fc-rowgroup',
1222
- stickyHeaderDates ? 'fc-sticky-header' : '',
1223
- ], ref: this.headerScrollerRef },
1224
- createElement("div", { className: 'fc-rel fc-content-box' // origin for now-indicator
1225
- , style: {
1226
- width: canvasWidth,
1227
- paddingLeft: state.leftScrollbarWidth,
1228
- paddingRight: state.rightScrollbarWidth,
1229
- } },
1230
- createElement("div", null, cellRows.map((cells, rowLevel) => {
1133
+ return (createElement(ViewContainer, { viewSpec: context.viewSpec, className: joinClassNames('fc-timeline fc-border',
1134
+ // HACK for Safari print-mode, where fc-scroller-no-bars won't take effect for
1135
+ // the below Scrollers if they have liquid flex height
1136
+ !props.forPrint && 'fc-flex-col') },
1137
+ createElement(Scroller, { horizontal: true, hideScrollbars: true, className: joinClassNames('fc-timeline-header fc-flex-row fc-border-b', stickyHeaderDates && 'fc-table-header-sticky'), ref: this.headerScrollerRef },
1138
+ createElement("div", {
1139
+ // TODO: DRY
1140
+ className: joinClassNames('fc-rel', // origin for now-indicator
1141
+ canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
1142
+ cellRows.map((cells, rowLevel) => {
1231
1143
  const isLast = rowLevel === cellRows.length - 1;
1232
- return (createElement(TimelineHeaderRow, { key: rowLevel, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, isLastRow: isLast, cells: cells, slotWidth: slotWidth, innerWidthRef: isLast ? this.handleHeaderSlotInnerWidth : undefined }));
1233
- })),
1234
- enableNowIndicator && (
1235
- // TODO: make this positioned WITHIN padding?
1236
- createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
1237
- createElement(Scroller, { vertical: verticalScrolling, horizontal: true, elClassNames: [
1238
- 'fc-timeline-body',
1239
- 'fc-rowgroup',
1240
- verticalScrolling ? 'fc-liquid' : '',
1241
- ], ref: this.bodyScrollerRef, widthRef: this.handleScrollerWidth, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth },
1242
- createElement("div", { className: "fc-rel fc-grow", style: {
1243
- width: canvasWidth,
1244
- }, ref: this.handeBodyEl },
1144
+ return (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) }));
1145
+ }),
1146
+ enableNowIndicator && (createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
1147
+ Boolean(state.endScrollbarWidth) && (createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
1148
+ createElement(Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: props.forPrint, className: joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth },
1149
+ createElement("div", { className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
1245
1150
  createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1246
1151
  // ref
1247
1152
  innerWidthRef: this.handleBodySlotInnerWidth,
@@ -1256,27 +1161,26 @@ class TimelineView extends DateComponent {
1256
1161
  // Lifecycle
1257
1162
  // -----------------------------------------------------------------------------------------------
1258
1163
  componentDidMount() {
1259
- const { context } = this;
1260
- const { options } = context;
1261
- const ScrollerSyncer = getScrollerSyncerClass(this.context.pluginHooks);
1262
1164
  this.syncedScroller = new ScrollerSyncer(true); // horizontal=true
1263
1165
  this.updateSyncedScroller();
1264
- context.emitter.on('_timeScrollRequest', this.timeScrollResponder.handleScroll);
1265
- this.timeScrollResponder.handleScroll(options.scrollTime);
1166
+ this.resetScroll();
1167
+ this.context.emitter.on('_timeScrollRequest', this.handleTimeScroll);
1168
+ this.syncedScroller.addScrollEndListener(this.clearScroll);
1266
1169
  }
1267
1170
  componentDidUpdate(prevProps) {
1268
- const { options } = this.context;
1269
1171
  this.updateSyncedScroller();
1270
- if (prevProps.dateProfile !== this.props.dateProfile && options.scrollTimeReset) {
1271
- this.timeScrollResponder.handleScroll(options.scrollTime);
1172
+ if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
1173
+ this.resetScroll();
1272
1174
  }
1273
1175
  else {
1274
- this.timeScrollResponder.drain();
1176
+ // TODO: inefficient to update so often
1177
+ this.updateScroll();
1275
1178
  }
1276
1179
  }
1277
1180
  componentWillUnmount() {
1278
1181
  this.syncedScroller.destroy();
1279
- this.context.emitter.off('_timeScrollRequest', this.timeScrollResponder.handleScroll);
1182
+ this.context.emitter.off('_timeScrollRequest', this.handleTimeScroll);
1183
+ this.syncedScroller.removeScrollEndListener(this.clearScroll);
1280
1184
  }
1281
1185
  // Scrolling
1282
1186
  // -----------------------------------------------------------------------------------------------
@@ -1285,7 +1189,10 @@ class TimelineView extends DateComponent {
1285
1189
  this.headerScrollerRef.current,
1286
1190
  this.bodyScrollerRef.current,
1287
1191
  this.footerScrollerRef.current
1288
- ], this.context.isRtl);
1192
+ ]);
1193
+ }
1194
+ resetScroll() {
1195
+ this.handleTimeScroll(this.context.options.scrollTime);
1289
1196
  }
1290
1197
  queryHit(positionLeft, positionTop, elWidth, elHeight) {
1291
1198
  const { props, context, tDateProfile, slotWidth } = this;
@@ -1332,4 +1239,7 @@ class TimelineView extends DateComponent {
1332
1239
  }
1333
1240
  }
1334
1241
 
1335
- export { TimelineHeaderRow, TimelineLane, TimelineLaneBg, TimelineLaneSlicer, TimelineNowIndicatorArrow, TimelineNowIndicatorLine, TimelineSlats, TimelineView, buildTimelineDateProfile, computeSlotWidth, coordToCss, coordsToCss, createHorizontalStyle, createVerticalStyle, timeToCoord };
1242
+ 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}";
1243
+ injectStyles(css_248z);
1244
+
1245
+ export { TimelineHeaderRow, TimelineLane, TimelineLaneBg, TimelineLaneSlicer, TimelineNowIndicatorArrow, TimelineNowIndicatorLine, TimelineSlats, TimelineView, buildTimelineDateProfile, computeSlotWidth, createHorizontalStyle, createVerticalStyle, timeToCoord };