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

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/index.global.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- FullCalendar Timeline Plugin v7.0.0-beta.3
2
+ FullCalendar Timeline Plugin v7.0.0-rc.0
3
3
  Docs & License: https://fullcalendar.io/docs/timeline-view-no-resources
4
4
  (c) 2024 Adam Shaw
5
5
  */
@@ -451,6 +451,8 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
451
451
  class TimelineSlatCell extends internal$1.BaseComponent {
452
452
  constructor() {
453
453
  super(...arguments);
454
+ // memo
455
+ this.getPublicDate = internal$1.memoize((dateEnv, date) => dateEnv.toDate(date));
454
456
  // ref
455
457
  this.innerElRef = preact.createRef();
456
458
  }
@@ -459,7 +461,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
459
461
  let { dateEnv, options } = context;
460
462
  let { date, tDateProfile, isEm } = props;
461
463
  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 });
464
+ let renderProps = Object.assign(Object.assign({ date: this.getPublicDate(dateEnv, props.date) }, dateMeta), { view: context.viewApi });
463
465
  return (preact.createElement(internal$1.ContentContainer, { tag: "div",
464
466
  // fc-align-start shrinks width of InnerContent
465
467
  // TODO: document this semantic className fc-timeline-slot-em
@@ -468,14 +470,16 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
468
470
  'fc-timeline-slot-major' :
469
471
  'fc-timeline-slot-minor'), 'fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start', props.borderStart && 'fc-border-s', props.isDay ?
470
472
  internal$1.getDayClassName(dateMeta) :
471
- internal$1.getSlotClassName(dateMeta)), attrs: {
472
- 'data-date': dateEnv.formatIso(date, {
473
+ internal$1.getSlotClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
473
474
  omitTimeZoneOffset: true,
474
475
  omitTime: !tDateProfile.isTimeScale,
475
- }),
476
- }, style: {
476
+ }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
477
477
  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 }))));
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', style: {
479
+ // HACK for Safari 16.4,
480
+ // which can't use ResizeObserver on elements with natural width 0
481
+ minWidth: 1,
482
+ }, elRef: this.innerElRef }))));
479
483
  }
480
484
  componentDidMount() {
481
485
  const innerEl = this.innerElRef.current;
@@ -510,7 +514,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
510
514
  let { tDateProfile, slotWidth } = props;
511
515
  let { slotDates, isWeekStarts } = tDateProfile;
512
516
  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) => {
517
+ return (preact.createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-flex-row fc-fill", style: { height: props.height } }, slotDates.map((slotDate, i) => {
514
518
  let key = slotDate.toISOString();
515
519
  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
520
  // ref
@@ -521,50 +525,132 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
521
525
  }
522
526
  }
523
527
 
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;
528
+ class TimelineHeaderCell extends internal$1.BaseComponent {
529
+ constructor() {
530
+ super(...arguments);
531
+ // memo
532
+ this.refineRenderProps = internal$1.memoizeObjArg(refineRenderProps);
533
+ // ref
534
+ this.innerElRef = preact.createRef();
532
535
  }
533
- if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
534
- return tDateProfile.snapCnt;
536
+ render() {
537
+ let { props, context } = this;
538
+ let { dateEnv, options } = context;
539
+ let { cell, dateProfile, tDateProfile } = props;
540
+ // the cell.rowUnit is f'd
541
+ // giving 'month' for a 3-day view
542
+ // workaround: to infer day, do NOT time
543
+ let dateMeta = internal$1.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
544
+ let renderProps = this.refineRenderProps({
545
+ level: props.rowLevel,
546
+ dateMarker: cell.date,
547
+ text: cell.text,
548
+ dateEnv: context.dateEnv,
549
+ viewApi: context.viewApi,
550
+ });
551
+ let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
552
+ 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
553
+ 'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
554
+ // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
555
+ cell.rowUnit === 'time' ?
556
+ internal$1.getSlotClassName(dateMeta) :
557
+ internal$1.getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
558
+ omitTime: !tDateProfile.isTimeScale,
559
+ omitTimeZoneOffset: true,
560
+ }) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
561
+ width: props.slotWidth != null
562
+ ? props.slotWidth * cell.colspan
563
+ : undefined,
564
+ }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact.createElement(InnerContent, { tag: 'div', attrs: isNavLink
565
+ // not tabbable because parent is aria-hidden
566
+ ? internal$1.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
567
+ : {} // don't bother with aria-hidden because parent already hidden
568
+ , className: internal$1.joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
535
569
  }
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
570
+ componentDidMount() {
571
+ const { props } = this;
572
+ const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
573
+ this.detachSize = internal$1.watchSize(innerEl, (width, height) => {
574
+ internal$1.setRef(props.innerWidthRef, width);
575
+ internal$1.setRef(props.innerHeightRef, height);
576
+ // HACK for sticky-centering
577
+ innerEl.style.left = innerEl.style.right =
578
+ (props.isCentered && props.isSticky)
579
+ ? `calc(50% - ${width / 2}px)`
580
+ : '';
581
+ });
540
582
  }
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);
583
+ componentWillUnmount() {
584
+ const { props } = this;
585
+ this.detachSize();
586
+ internal$1.setRef(props.innerWidthRef, null);
587
+ internal$1.setRef(props.innerHeightRef, null);
545
588
  }
546
- return snapCoverage;
547
589
  }
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
- }
590
+ // Utils
591
+ // -------------------------------------------------------------------------------------------------
592
+ function renderInnerContent(renderProps) {
593
+ return renderProps.text;
561
594
  }
562
- function horizontalCoordToCss(start, isRtl) {
563
- if (isRtl) {
564
- return { right: start };
595
+ function refineRenderProps(input) {
596
+ return {
597
+ level: input.level,
598
+ date: input.dateEnv.toDate(input.dateMarker),
599
+ view: input.viewApi,
600
+ text: input.text,
601
+ };
602
+ }
603
+
604
+ class TimelineHeaderRow extends internal$1.BaseComponent {
605
+ constructor() {
606
+ super(...arguments);
607
+ // refs
608
+ this.innerWidthRefMap = new internal$1.RefMap(() => {
609
+ internal$1.afterSize(this.handleInnerWidths);
610
+ });
611
+ this.innerHeightRefMap = new internal$1.RefMap(() => {
612
+ internal$1.afterSize(this.handleInnerHeights);
613
+ });
614
+ this.handleInnerWidths = () => {
615
+ const innerWidthMap = this.innerWidthRefMap.current;
616
+ let max = 0;
617
+ for (const innerWidth of innerWidthMap.values()) {
618
+ max = Math.max(max, innerWidth);
619
+ }
620
+ // TODO: ensure not equal?
621
+ internal$1.setRef(this.props.innerWidthRef, max);
622
+ };
623
+ this.handleInnerHeights = () => {
624
+ const innerHeightMap = this.innerHeightRefMap.current;
625
+ let max = 0;
626
+ for (const innerHeight of innerHeightMap.values()) {
627
+ max = Math.max(max, innerHeight);
628
+ }
629
+ // TODO: ensure not equal?
630
+ internal$1.setRef(this.props.innerHeighRef, max);
631
+ };
565
632
  }
566
- else {
567
- return { left: start };
633
+ render() {
634
+ const { props, innerWidthRefMap, innerHeightRefMap } = this;
635
+ const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
636
+ const isSticky = !props.isLastRow;
637
+ return (preact.createElement("div", { className: internal$1.joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
638
+ !props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
639
+ // TODO: make this part of the cell obj?
640
+ // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
641
+ // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
642
+ // TODO: use rowUnit to key the Row itself?
643
+ const key = cell.rowUnit + ':' + cell.date.toISOString();
644
+ 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),
645
+ // refs
646
+ innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
647
+ // dimensions
648
+ slotWidth: props.slotWidth }));
649
+ })));
650
+ }
651
+ componentWillUnmount() {
652
+ internal$1.setRef(this.props.innerWidthRef, null);
653
+ internal$1.setRef(this.props.innerHeighRef, null);
568
654
  }
569
655
  }
570
656
 
@@ -619,13 +705,38 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
619
705
  }
620
706
  function dateToCoord(// pixels
621
707
  date, dateEnv, tDateProfile, slotWidth) {
622
- let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
708
+ let snapCoverage = computeDateSnapCoverage$1(date, tDateProfile, dateEnv);
623
709
  let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
624
710
  return slotCoverage * slotWidth;
625
711
  }
626
712
  /*
627
713
  returned value is between 0 and the number of snaps
628
714
  */
715
+ function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
716
+ let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
717
+ if (snapDiff < 0) {
718
+ return 0;
719
+ }
720
+ if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
721
+ return tDateProfile.snapCnt;
722
+ }
723
+ let snapDiffInt = Math.floor(snapDiff);
724
+ let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
725
+ if (internal$1.isInt(snapCoverage)) { // not an in-between value
726
+ snapCoverage += snapDiff - snapDiffInt; // add the remainder
727
+ }
728
+ else {
729
+ // a fractional value, meaning the date is not visible
730
+ // always round up in this case. works for start AND end dates in a range.
731
+ snapCoverage = Math.ceil(snapCoverage);
732
+ }
733
+ return snapCoverage;
734
+ }
735
+
736
+ /*
737
+ TODO: rename this file!
738
+ */
739
+ // returned value is between 0 and the number of snaps
629
740
  function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
630
741
  let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
631
742
  if (snapDiff < 0) {
@@ -644,7 +755,107 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
644
755
  // always round up in this case. works for start AND end dates in a range.
645
756
  snapCoverage = Math.ceil(snapCoverage);
646
757
  }
647
- return snapCoverage;
758
+ return snapCoverage;
759
+ }
760
+ /*
761
+ TODO: DRY up with elsewhere?
762
+ */
763
+ function horizontalsToCss(hcoord, isRtl) {
764
+ if (!hcoord) {
765
+ return {};
766
+ }
767
+ if (isRtl) {
768
+ return { right: hcoord.start, width: hcoord.size };
769
+ }
770
+ else {
771
+ return { left: hcoord.start, width: hcoord.size };
772
+ }
773
+ }
774
+ function horizontalCoordToCss(start, isRtl) {
775
+ if (isRtl) {
776
+ return { right: start };
777
+ }
778
+ else {
779
+ return { left: start };
780
+ }
781
+ }
782
+
783
+ class TimelineNowIndicatorLine extends internal$1.BaseComponent {
784
+ render() {
785
+ const { props, context } = this;
786
+ return (preact.createElement("div", { className: "fc-timeline-now-indicator-container" },
787
+ preact.createElement(internal$1.NowIndicatorContainer // TODO: make separate component?
788
+ , { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
789
+ ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
790
+ : {}, isAxis: false, date: props.nowDate })));
791
+ }
792
+ }
793
+
794
+ class TimelineNowIndicatorArrow extends internal$1.BaseComponent {
795
+ render() {
796
+ const { props, context } = this;
797
+ return (preact.createElement("div", { className: "fc-timeline-now-indicator-container" },
798
+ preact.createElement(internal$1.NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
799
+ ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
800
+ : {}, isAxis: true, date: props.nowDate })));
801
+ }
802
+ }
803
+
804
+ function getTimelineSlotEl(parentEl, index) {
805
+ return parentEl.querySelectorAll('.fc-timeline-slot')[index];
806
+ }
807
+
808
+ class TimelineLaneSlicer extends internal$1.Slicer {
809
+ sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
810
+ let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
811
+ let segs = [];
812
+ // protect against when the span is entirely in an invalid date region
813
+ if (computeDateSnapCoverage(normalRange.start, tDateProfile, dateEnv)
814
+ < computeDateSnapCoverage(normalRange.end, tDateProfile, dateEnv)) {
815
+ // intersect the footprint's range with the grid's range
816
+ let slicedRange = internal$1.intersectRanges(normalRange, tDateProfile.normalizedRange);
817
+ if (slicedRange) {
818
+ segs.push({
819
+ startDate: slicedRange.start,
820
+ endDate: slicedRange.end,
821
+ isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
822
+ && isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
823
+ isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
824
+ && isValidDate(internal$1.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
825
+ });
826
+ }
827
+ }
828
+ return segs;
829
+ }
830
+ }
831
+
832
+ const DEFAULT_TIME_FORMAT = internal$1.createFormatter({
833
+ hour: 'numeric',
834
+ minute: '2-digit',
835
+ omitZeroMinute: true,
836
+ meridiem: 'narrow',
837
+ });
838
+ class TimelineEvent extends internal$1.BaseComponent {
839
+ render() {
840
+ let { props, context } = this;
841
+ let { options } = context;
842
+ return (preact.createElement(internal$1.StandardEvent, Object.assign({}, props, { className: internal$1.joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
843
+ && 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
844
+ }
845
+ }
846
+
847
+ class TimelineLaneMoreLink extends internal$1.BaseComponent {
848
+ render() {
849
+ let { props } = this;
850
+ let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
851
+ let dateSpanProps = resourceId ? { resourceId } : {};
852
+ 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) => {
853
+ let { eventRange } = seg;
854
+ let instanceId = eventRange.instance.instanceId;
855
+ return (preact.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
856
+ 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)))));
857
+ }))) }, (InnerContent) => (preact.createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
858
+ }
648
859
  }
649
860
 
650
861
  function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
@@ -711,83 +922,6 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
711
922
  ];
712
923
  }
713
924
 
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
925
  /*
792
926
  TODO: make DRY with other Event Harnesses
793
927
  */
@@ -813,10 +947,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
813
947
  }
814
948
  }
815
949
 
816
- /*
817
- TODO: split TimelineLaneBg and TimelineLaneFg?
818
- */
819
- class TimelineLane extends internal$1.BaseComponent {
950
+ class TimelineFg extends internal$1.BaseComponent {
820
951
  constructor() {
821
952
  super(...arguments);
822
953
  // memo
@@ -828,8 +959,6 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
828
959
  this.moreLinkHeightRefMap = new internal$1.RefMap(() => {
829
960
  internal$1.afterSize(this.handleMoreLinkHeights);
830
961
  });
831
- // internal
832
- this.slicer = new TimelineLaneSlicer();
833
962
  this.handleMoreLinkHeights = () => {
834
963
  this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
835
964
  };
@@ -843,39 +972,30 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
843
972
  render() {
844
973
  let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
845
974
  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) ||
975
+ let { tDateProfile } = props;
976
+ let mirrorSegs = (props.eventDrag ? props.eventDrag.segs : null) ||
977
+ (props.eventResize ? props.eventResize.segs : null) ||
851
978
  [];
852
- let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
979
+ let fgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
853
980
  let fgSegHorizontals = props.slotWidth != null
854
981
  ? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
855
982
  : {};
856
983
  let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
984
+ this.totalHeight = totalHeight;
857
985
  let forcedInvisibleMap = // TODO: more convenient/DRY
858
- (slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
859
- (slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
986
+ (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
987
+ (props.eventResize ? props.eventResize.affectedInstances : null) ||
860
988
  {};
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))));
989
+ return (preact.createElement("div", { className: 'fc-timeline-events fc-rel', style: { height: totalHeight } },
990
+ this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
991
+ false, // isResizing
992
+ false),
993
+ this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
994
+ ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
995
+ : {}, fgSegTops, {}, // forcedInvisibleMap
996
+ [], // hiddenGroups
997
+ new Map(), // hiddenGroupTops
998
+ Boolean(props.eventDrag), Boolean(props.eventResize), false)));
879
999
  }
880
1000
  renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
881
1001
  let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
@@ -897,158 +1017,43 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
897
1017
  }, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
898
1018
  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
1019
  }
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 }))));
941
- }
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
- };
1020
+ /*
1021
+ componentDidMount(): void {
1022
+ // might want to do firedTotalHeight, but won't be ready on first render
1009
1023
  }
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
- })));
1024
+ */
1025
+ componentDidUpdate() {
1026
+ if (this.totalHeight !== this.firedTotalHeight) {
1027
+ this.firedTotalHeight = this.totalHeight;
1028
+ internal$1.setRef(this.props.heightRef, this.totalHeight);
1029
+ }
1027
1030
  }
1028
1031
  componentWillUnmount() {
1029
- internal$1.setRef(this.props.innerWidthRef, null);
1030
- internal$1.setRef(this.props.innerHeighRef, null);
1032
+ internal$1.setRef(this.props.heightRef, null);
1031
1033
  }
1032
1034
  }
1033
1035
 
1034
- class TimelineNowIndicatorLine extends internal$1.BaseComponent {
1036
+ class TimelineBg extends internal$1.BaseComponent {
1035
1037
  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 })));
1038
+ let { props } = this;
1039
+ let highlightSeg = [].concat(props.eventResizeSegs || [], props.dateSelectionSegs);
1040
+ return (preact.createElement(preact.Fragment, null,
1041
+ this.renderSegs(props.businessHourSegs || [], 'non-business'),
1042
+ this.renderSegs(props.bgEventSegs || [], 'bg-event'),
1043
+ this.renderSegs(highlightSeg, 'highlight')));
1042
1044
  }
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 })));
1045
+ renderSegs(segs, fillType) {
1046
+ let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
1047
+ let { dateEnv, isRtl } = this.context;
1048
+ return (preact.createElement(preact.Fragment, null, segs.map((seg) => {
1049
+ let hStyle; // TODO
1050
+ if (slotWidth != null) {
1051
+ let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
1052
+ hStyle = horizontalsToCss(segHorizontal, isRtl);
1053
+ }
1054
+ return (preact.createElement("div", { key: internal$1.buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
1055
+ 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))));
1056
+ })));
1052
1057
  }
1053
1058
  }
1054
1059
 
@@ -1066,6 +1071,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1066
1071
  internal$1.afterSize(this.handleSlotInnerWidths);
1067
1072
  });
1068
1073
  this.scrollTime = null;
1074
+ this.slicer = new TimelineLaneSlicer();
1069
1075
  // Sizing
1070
1076
  // -----------------------------------------------------------------------------------------------
1071
1077
  this.handleBodySlotInnerWidth = (innerWidth) => {
@@ -1073,39 +1079,34 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1073
1079
  internal$1.afterSize(this.handleSlotInnerWidths);
1074
1080
  };
1075
1081
  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 });
1082
+ const headerSlotInnerWidth = this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1);
1083
+ const { bodySlotInnerWidth } = this;
1084
+ if (headerSlotInnerWidth != null && bodySlotInnerWidth != null) {
1085
+ const slotInnerWidth = Math.max(headerSlotInnerWidth, bodySlotInnerWidth);
1086
+ if (slotInnerWidth !== this.state.slotInnerWidth) {
1087
+ this.setState({ slotInnerWidth });
1088
+ }
1080
1089
  }
1081
1090
  };
1082
- this.handleClientWidth = (clientWidth) => {
1091
+ this.handleTotalWidth = (totalWidth) => {
1083
1092
  this.setState({
1084
- clientWidth,
1093
+ totalWidth,
1085
1094
  });
1086
1095
  };
1087
- this.handleEndScrollbarWidth = (endScrollbarWidth) => {
1096
+ this.handleClientWidth = (clientWidth) => {
1088
1097
  this.setState({
1089
- endScrollbarWidth
1098
+ clientWidth,
1090
1099
  });
1091
1100
  };
1092
- this.handleTimeScroll = (scrollTime) => {
1101
+ this.handleTimeScrollRequest = (scrollTime) => {
1093
1102
  this.scrollTime = scrollTime;
1094
- this.updateScroll();
1103
+ this.applyTimeScroll();
1095
1104
  };
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 });
1105
+ this.handleTimeScrollEnd = (isUser) => {
1106
+ if (isUser) {
1107
+ this.scrollTime = null;
1104
1108
  }
1105
1109
  };
1106
- this.clearScroll = () => {
1107
- this.scrollTime = null;
1108
- };
1109
1110
  // Hit System
1110
1111
  // -----------------------------------------------------------------------------------------------
1111
1112
  this.handeBodyEl = (el) => {
@@ -1121,6 +1122,10 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1121
1122
  render() {
1122
1123
  const { props, state, context } = this;
1123
1124
  const { options } = context;
1125
+ const { totalWidth, clientWidth } = state;
1126
+ const endScrollbarWidth = (totalWidth != null && clientWidth != null)
1127
+ ? totalWidth - clientWidth
1128
+ : undefined;
1124
1129
  /* date */
1125
1130
  const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
1126
1131
  const { cellRows } = tDateProfile;
@@ -1131,8 +1136,11 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1131
1136
  const stickyFooterScrollbar = !props.forPrint && internal$1.getStickyFooterScrollbar(options);
1132
1137
  /* table positions */
1133
1138
  const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
1134
- state.clientWidth);
1139
+ clientWidth);
1135
1140
  this.slotWidth = slotWidth;
1141
+ /* sliced */
1142
+ 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...
1143
+ props.dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
1136
1144
  return (preact.createElement(internal$1.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
1137
1145
  const enableNowIndicator = // TODO: DRY
1138
1146
  options.nowIndicator &&
@@ -1152,18 +1160,30 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1152
1160
  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
1161
  }),
1154
1162
  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 },
1163
+ Boolean(endScrollbarWidth) && (preact.createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: endScrollbarWidth } }))),
1164
+ preact.createElement(internal$1.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: stickyFooterScrollbar ||
1165
+ props.forPrint // prevents blank space in print-view on Safari
1166
+ , className: internal$1.joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth },
1167
+ preact.createElement("div", { "aria-label": options.eventsHint, className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
1158
1168
  preact.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1159
1169
  // ref
1160
1170
  innerWidthRef: this.handleBodySlotInnerWidth,
1161
1171
  // dimensions
1162
1172
  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 }),
1173
+ preact.createElement(TimelineBg, { tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1174
+ // content
1175
+ bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : null,
1176
+ // dimensions
1177
+ slotWidth: slotWidth }),
1178
+ preact.createElement(TimelineFg, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1179
+ // content
1180
+ fgEventSegs: slicedProps.fgEventSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
1181
+ // dimensions
1182
+ slotWidth: slotWidth }),
1183
+ preact.createElement("div", { className: 'fc-timeline-lane-footer' }),
1164
1184
  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 } })))));
1185
+ Boolean(stickyFooterScrollbar) && (preact.createElement(internal$1.FooterScrollbar, { isSticky: true, canvasWidth: canvasWidth, scrollerRef: this.footerScrollerRef })),
1186
+ preact.createElement(internal$1.Ruler, { widthRef: this.handleTotalWidth })));
1167
1187
  }));
1168
1188
  }
1169
1189
  // Lifecycle
@@ -1172,8 +1192,8 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1172
1192
  this.syncedScroller = new internal$2.ScrollerSyncer(true); // horizontal=true
1173
1193
  this.updateSyncedScroller();
1174
1194
  this.resetScroll();
1175
- this.context.emitter.on('_timeScrollRequest', this.handleTimeScroll);
1176
- this.syncedScroller.addScrollEndListener(this.clearScroll);
1195
+ this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
1196
+ this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
1177
1197
  }
1178
1198
  componentDidUpdate(prevProps) {
1179
1199
  this.updateSyncedScroller();
@@ -1182,13 +1202,13 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1182
1202
  }
1183
1203
  else {
1184
1204
  // TODO: inefficient to update so often
1185
- this.updateScroll();
1205
+ this.applyTimeScroll();
1186
1206
  }
1187
1207
  }
1188
1208
  componentWillUnmount() {
1189
1209
  this.syncedScroller.destroy();
1190
- this.context.emitter.off('_timeScrollRequest', this.handleTimeScroll);
1191
- this.syncedScroller.removeScrollEndListener(this.clearScroll);
1210
+ this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
1211
+ this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
1192
1212
  }
1193
1213
  // Scrolling
1194
1214
  // -----------------------------------------------------------------------------------------------
@@ -1200,7 +1220,17 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1200
1220
  ]);
1201
1221
  }
1202
1222
  resetScroll() {
1203
- this.handleTimeScroll(this.context.options.scrollTime);
1223
+ this.handleTimeScrollRequest(this.context.options.scrollTime);
1224
+ }
1225
+ applyTimeScroll() {
1226
+ const { props, context, tDateProfile, scrollTime, slotWidth } = this;
1227
+ if (scrollTime != null && slotWidth != null) {
1228
+ let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
1229
+ if (x) {
1230
+ x += 1; // overcome border. TODO: DRY this up
1231
+ }
1232
+ this.syncedScroller.scrollTo({ x });
1233
+ }
1204
1234
  }
1205
1235
  queryHit(positionLeft, positionTop, elWidth, elHeight) {
1206
1236
  const { props, context, tDateProfile, slotWidth } = this;
@@ -1238,8 +1268,7 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1238
1268
  top: 0,
1239
1269
  bottom: elHeight,
1240
1270
  },
1241
- // HACK. TODO: This is expensive to do every hit-query
1242
- dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
1271
+ getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
1243
1272
  layer: 0,
1244
1273
  };
1245
1274
  }
@@ -1247,12 +1276,12 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1247
1276
  }
1248
1277
  }
1249
1278
 
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}";
1279
+ var css_248z = ".fc-timeline-lane-footer{padding-bottom:10px}.fc-timeline-overlap-disabled .fc-timeline-lane-footer{padding-bottom:0}.fc-timeline-slot-minor{border-style:dotted}.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:2}.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
1280
  internal$1.injectStyles(css_248z);
1252
1281
 
1253
1282
  var plugin = core.createPlugin({
1254
1283
  name: '@fullcalendar/timeline',
1255
- premiumReleaseDate: '2024-12-19',
1284
+ premiumReleaseDate: '2025-02-21',
1256
1285
  deps: [premiumCommonPlugin__default["default"]],
1257
1286
  initialView: 'timelineDay',
1258
1287
  views: {
@@ -1283,8 +1312,8 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1283
1312
  var internal = {
1284
1313
  __proto__: null,
1285
1314
  TimelineView: TimelineView,
1286
- TimelineLane: TimelineLane,
1287
- TimelineLaneBg: TimelineLaneBg,
1315
+ TimelineFg: TimelineFg,
1316
+ TimelineBg: TimelineBg,
1288
1317
  TimelineSlats: TimelineSlats,
1289
1318
  buildTimelineDateProfile: buildTimelineDateProfile,
1290
1319
  createVerticalStyle: createVerticalStyle,
@@ -1294,7 +1323,8 @@ FullCalendar.Timeline = (function (exports, core, premiumCommonPlugin, internal$
1294
1323
  TimelineLaneSlicer: TimelineLaneSlicer,
1295
1324
  TimelineHeaderRow: TimelineHeaderRow,
1296
1325
  TimelineNowIndicatorArrow: TimelineNowIndicatorArrow,
1297
- TimelineNowIndicatorLine: TimelineNowIndicatorLine
1326
+ TimelineNowIndicatorLine: TimelineNowIndicatorLine,
1327
+ getTimelineSlotEl: getTimelineSlotEl
1298
1328
  };
1299
1329
 
1300
1330
  core.globalPlugins.push(plugin);