@fullcalendar/timeline 6.1.14 → 7.0.0-beta.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/internal.cjs CHANGED
@@ -4,7 +4,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var internal_cjs = require('@fullcalendar/core/internal.cjs');
6
6
  var preact_cjs = require('@fullcalendar/core/preact.cjs');
7
- var internal_cjs$1 = require('@fullcalendar/scrollgrid/internal.cjs');
7
+
8
+ 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}";
9
+ internal_cjs.injectStyles(css_248z);
8
10
 
9
11
  const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
10
12
  const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
@@ -444,152 +446,90 @@ function buildCellObject(date, text, rowUnit) {
444
446
  return { date, text, rowUnit, colspan: 1, isWeekStart: false };
445
447
  }
446
448
 
447
- class TimelineHeaderTh extends internal_cjs.BaseComponent {
449
+ class TimelineSlatCell extends internal_cjs.BaseComponent {
448
450
  constructor() {
449
451
  super(...arguments);
450
- this.refineRenderProps = internal_cjs.memoizeObjArg(refineRenderProps);
451
- this.buildCellNavLinkAttrs = internal_cjs.memoize(buildCellNavLinkAttrs);
452
+ // ref
453
+ this.innerElRef = preact_cjs.createRef();
452
454
  }
453
455
  render() {
454
456
  let { props, context } = this;
455
- let { dateEnv, options } = context;
456
- let { cell, dateProfile, tDateProfile } = props;
457
- // the cell.rowUnit is f'd
458
- // giving 'month' for a 3-day view
459
- // workaround: to infer day, do NOT time
460
- let dateMeta = internal_cjs.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
461
- let renderProps = this.refineRenderProps({
462
- level: props.rowLevel,
463
- dateMarker: cell.date,
464
- text: cell.text,
465
- dateEnv: context.dateEnv,
466
- viewApi: context.viewApi,
467
- });
468
- return (preact_cjs.createElement(internal_cjs.ContentContainer, { elTag: "th", elClasses: [
457
+ let { dateEnv, options, theme } = context;
458
+ let { date, tDateProfile, isEm } = props;
459
+ let dateMeta = internal_cjs.getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
460
+ let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
461
+ return (preact_cjs.createElement(internal_cjs.ContentContainer, { elTag: "div", elClasses: [
462
+ 'fc-flex-column',
463
+ 'fc-align-start',
464
+ 'fc-cell',
469
465
  'fc-timeline-slot',
470
- 'fc-timeline-slot-label',
471
- cell.isWeekStart && 'fc-timeline-slot-em',
472
- ...( // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
473
- cell.rowUnit === 'time' ?
474
- internal_cjs.getSlotClassNames(dateMeta, context.theme) :
475
- internal_cjs.getDayClassNames(dateMeta, context.theme)),
466
+ 'fc-timeline-slot-lane',
467
+ isEm ? 'fc-timeline-slot-em' : '',
468
+ tDateProfile.isTimeScale ? (internal_cjs.isInt(dateEnv.countDurationsBetween(// best to do this here?
469
+ tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
470
+ 'fc-timeline-slot-major' :
471
+ 'fc-timeline-slot-minor') : '',
472
+ ...(props.isDay ?
473
+ internal_cjs.getDayClassNames(dateMeta, theme) :
474
+ internal_cjs.getSlotClassNames(dateMeta, theme)),
476
475
  ], elAttrs: {
477
- colSpan: cell.colspan,
478
- 'data-date': dateEnv.formatIso(cell.date, {
479
- omitTime: !tDateProfile.isTimeScale,
476
+ 'data-date': dateEnv.formatIso(date, {
480
477
  omitTimeZoneOffset: true,
478
+ omitTime: !tDateProfile.isTimeScale,
481
479
  }),
482
- }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact_cjs.createElement("div", { className: "fc-timeline-slot-frame", style: { height: props.rowInnerHeight } },
483
- preact_cjs.createElement(InnerContent, { elTag: "a", elClasses: [
484
- 'fc-timeline-slot-cushion',
485
- 'fc-scrollgrid-sync-inner',
486
- props.isSticky && 'fc-sticky',
487
- ], elAttrs: this.buildCellNavLinkAttrs(context, cell.date, cell.rowUnit) })))));
480
+ }, elStyle: {
481
+ width: props.width,
482
+ }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (preact_cjs.createElement("div", { ref: this.innerElRef, className: 'fc-flex-column' },
483
+ preact_cjs.createElement(InnerContent, { elTag: "div", elClasses: ['fc-cell-inner'] })))));
488
484
  }
489
- }
490
- function buildCellNavLinkAttrs(context, cellDate, rowUnit) {
491
- return (rowUnit && rowUnit !== 'time')
492
- ? internal_cjs.buildNavLinkAttrs(context, cellDate, rowUnit)
493
- : {};
494
- }
495
- function renderInnerContent(renderProps) {
496
- return renderProps.text;
497
- }
498
- function refineRenderProps(input) {
499
- return {
500
- level: input.level,
501
- date: input.dateEnv.toDate(input.dateMarker),
502
- view: input.viewApi,
503
- text: input.text,
504
- };
505
- }
506
-
507
- class TimelineHeaderRows extends internal_cjs.BaseComponent {
508
- render() {
509
- let { dateProfile, tDateProfile, rowInnerHeights, todayRange, nowDate } = this.props;
510
- let { cellRows } = tDateProfile;
511
- return (preact_cjs.createElement(preact_cjs.Fragment, null, cellRows.map((rowCells, rowLevel) => {
512
- let isLast = rowLevel === cellRows.length - 1;
513
- let isChrono = tDateProfile.isTimeScale && isLast; // the final row, with times?
514
- let classNames = [
515
- 'fc-timeline-header-row',
516
- isChrono ? 'fc-timeline-header-row-chrono' : '',
517
- ];
518
- return ( // eslint-disable-next-line react/no-array-index-key
519
- preact_cjs.createElement("tr", { key: rowLevel, className: classNames.join(' ') }, rowCells.map((cell) => (preact_cjs.createElement(TimelineHeaderTh, { key: cell.date.toISOString(), cell: cell, rowLevel: rowLevel, dateProfile: dateProfile, tDateProfile: tDateProfile, todayRange: todayRange, nowDate: nowDate, rowInnerHeight: rowInnerHeights && rowInnerHeights[rowLevel], isSticky: !isLast })))));
520
- })));
485
+ componentDidMount() {
486
+ const innerEl = this.innerElRef.current;
487
+ this.detachWidth = internal_cjs.watchWidth(innerEl, (width) => {
488
+ internal_cjs.setRef(this.props.innerWidthRef, width);
489
+ });
490
+ }
491
+ componentWillUnmount() {
492
+ this.detachWidth();
521
493
  }
522
494
  }
523
495
 
524
- class TimelineCoords {
525
- constructor(slatRootEl, // okay to expose?
526
- slatEls, dateProfile, tDateProfile, dateEnv, isRtl) {
527
- this.slatRootEl = slatRootEl;
528
- this.dateProfile = dateProfile;
529
- this.tDateProfile = tDateProfile;
530
- this.dateEnv = dateEnv;
531
- this.isRtl = isRtl;
532
- this.outerCoordCache = new internal_cjs.PositionCache(slatRootEl, slatEls, true, // isHorizontal
533
- false);
534
- // for the inner divs within the slats
535
- // used for event rendering and scrollTime, to disregard slat border
536
- this.innerCoordCache = new internal_cjs.PositionCache(slatRootEl, internal_cjs.findDirectChildren(slatEls, 'div'), true, // isHorizontal
537
- false);
538
- }
539
- isDateInRange(date) {
540
- return internal_cjs.rangeContainsMarker(this.dateProfile.currentRange, date);
541
- }
542
- // results range from negative width of area to 0
543
- dateToCoord(date) {
544
- let { tDateProfile } = this;
545
- let snapCoverage = this.computeDateSnapCoverage(date);
546
- let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
547
- let slotIndex = Math.floor(slotCoverage);
548
- slotIndex = Math.min(slotIndex, tDateProfile.slotCnt - 1);
549
- let partial = slotCoverage - slotIndex;
550
- let { innerCoordCache, outerCoordCache } = this;
551
- if (this.isRtl) {
552
- return outerCoordCache.originClientRect.width - (outerCoordCache.rights[slotIndex] -
553
- (innerCoordCache.getWidth(slotIndex) * partial));
554
- }
555
- return (outerCoordCache.lefts[slotIndex] +
556
- (innerCoordCache.getWidth(slotIndex) * partial));
557
- }
558
- rangeToCoords(range) {
559
- return {
560
- start: this.dateToCoord(range.start),
561
- end: this.dateToCoord(range.end),
562
- };
563
- }
564
- durationToCoord(duration) {
565
- let { dateProfile, tDateProfile, dateEnv, isRtl } = this;
566
- let coord = 0;
567
- if (dateProfile) {
568
- let date = dateEnv.add(dateProfile.activeRange.start, duration);
569
- if (!tDateProfile.isTimeScale) {
570
- date = internal_cjs.startOfDay(date);
571
- }
572
- coord = this.dateToCoord(date);
573
- // hack to overcome the left borders of non-first slat
574
- if (!isRtl && coord) {
575
- coord += 1;
496
+ class TimelineSlats extends internal_cjs.BaseComponent {
497
+ constructor() {
498
+ super(...arguments);
499
+ this.innerWidthRefMap = new internal_cjs.RefMap(() => {
500
+ internal_cjs.afterSize(this.handleInnerWidths);
501
+ });
502
+ this.handleInnerWidths = () => {
503
+ const innerWidthMap = this.innerWidthRefMap.current;
504
+ let max = 0;
505
+ for (const innerWidth of innerWidthMap.values()) {
506
+ max = Math.max(max, innerWidth);
576
507
  }
577
- }
578
- return coord;
579
- }
580
- coordFromLeft(coord) {
581
- if (this.isRtl) {
582
- return this.outerCoordCache.originClientRect.width - coord;
583
- }
584
- return coord;
508
+ // TODO: check to see if changed before firing ref!? YES. do in other places too
509
+ internal_cjs.setRef(this.props.innerWidthRef, max);
510
+ };
585
511
  }
586
- // returned value is between 0 and the number of snaps
587
- computeDateSnapCoverage(date) {
588
- return computeDateSnapCoverage(date, this.tDateProfile, this.dateEnv);
512
+ render() {
513
+ let { props, innerWidthRefMap } = this;
514
+ let { tDateProfile, slotWidth } = props;
515
+ let { slotDates, isWeekStarts } = tDateProfile;
516
+ let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
517
+ return (preact_cjs.createElement("div", { className: "fc-timeline-slots fc-fill fc-flex-row" }, slotDates.map((slotDate, i) => {
518
+ let key = slotDate.toISOString();
519
+ return (preact_cjs.createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isEm: isWeekStarts[i], isDay: isDay,
520
+ // ref
521
+ innerWidthRef: innerWidthRefMap.createRef(key),
522
+ // dimensions
523
+ width: slotWidth }));
524
+ })));
589
525
  }
590
526
  }
527
+
528
+ /*
529
+ TODO: rename this file!
530
+ */
591
531
  // returned value is between 0 and the number of snaps
592
- function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
532
+ function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
593
533
  let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
594
534
  if (snapDiff < 0) {
595
535
  return 0;
@@ -609,6 +549,9 @@ function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
609
549
  }
610
550
  return snapCoverage;
611
551
  }
552
+ /*
553
+ TODO: audit!!!
554
+ */
612
555
  function coordToCss(hcoord, isRtl) {
613
556
  if (hcoord === null) {
614
557
  return { left: '', right: '' };
@@ -618,6 +561,9 @@ function coordToCss(hcoord, isRtl) {
618
561
  }
619
562
  return { left: hcoord, right: '' };
620
563
  }
564
+ /*
565
+ TODO: audit!!!
566
+ */
621
567
  function coordsToCss(hcoords, isRtl) {
622
568
  if (!hcoords) {
623
569
  return { left: '', right: '' };
@@ -627,216 +573,141 @@ function coordsToCss(hcoords, isRtl) {
627
573
  }
628
574
  return { left: hcoords.start, right: -hcoords.end };
629
575
  }
630
-
631
- class TimelineHeader extends internal_cjs.BaseComponent {
632
- constructor() {
633
- super(...arguments);
634
- this.rootElRef = preact_cjs.createRef();
635
- }
636
- render() {
637
- let { props, context } = this;
638
- // TODO: very repetitive
639
- // TODO: make part of tDateProfile?
640
- let timerUnit = internal_cjs.greatestDurationDenominator(props.tDateProfile.slotDuration).unit;
641
- // WORKAROUND: make ignore slatCoords when out of sync with dateProfile
642
- let slatCoords = props.slatCoords && props.slatCoords.dateProfile === props.dateProfile ? props.slatCoords : null;
643
- return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => (preact_cjs.createElement("div", { className: "fc-timeline-header", ref: this.rootElRef },
644
- preact_cjs.createElement("table", { "aria-hidden": true, className: "fc-scrollgrid-sync-table", style: { minWidth: props.tableMinWidth, width: props.clientWidth } },
645
- props.tableColGroupNode,
646
- preact_cjs.createElement("tbody", null,
647
- preact_cjs.createElement(TimelineHeaderRows, { dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, nowDate: nowDate, todayRange: todayRange, rowInnerHeights: props.rowInnerHeights }))),
648
- context.options.nowIndicator && (
649
- // need to have a container regardless of whether the current view has a visible now indicator
650
- // because apparently removal of the element resets the scroll for some reasons (issue #5351).
651
- // this issue doesn't happen for the timeline body however (
652
- preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" }, (slatCoords && slatCoords.isDateInRange(nowDate)) && (preact_cjs.createElement(internal_cjs.NowIndicatorContainer, { elClasses: ['fc-timeline-now-indicator-arrow'], elStyle: coordToCss(slatCoords.dateToCoord(nowDate), context.isRtl), isAxis: true, date: nowDate }))))))));
576
+ /*
577
+ TODO: DRY up with elsewhere?
578
+ */
579
+ function horizontalsToCss(hcoord, isRtl) {
580
+ if (!hcoord) {
581
+ return {};
653
582
  }
654
- componentDidMount() {
655
- this.updateSize();
583
+ if (isRtl) {
584
+ return { right: hcoord.start, width: hcoord.size };
656
585
  }
657
- componentDidUpdate() {
658
- this.updateSize();
586
+ else {
587
+ return { left: hcoord.start, width: hcoord.size };
659
588
  }
660
- updateSize() {
661
- if (this.props.onMaxCushionWidth) {
662
- this.props.onMaxCushionWidth(this.computeMaxCushionWidth());
663
- }
589
+ }
590
+ function horizontalCoordToCss(start, isRtl) {
591
+ if (isRtl) {
592
+ return { right: start };
664
593
  }
665
- computeMaxCushionWidth() {
666
- return Math.max(...internal_cjs.findElements(this.rootElRef.current, '.fc-timeline-header-row:last-child .fc-timeline-slot-cushion').map((el) => el.getBoundingClientRect().width));
594
+ else {
595
+ return { left: start };
667
596
  }
668
597
  }
669
598
 
670
- class TimelineSlatCell extends internal_cjs.BaseComponent {
671
- render() {
672
- let { props, context } = this;
673
- let { dateEnv, options, theme } = context;
674
- let { date, tDateProfile, isEm } = props;
675
- let dateMeta = internal_cjs.getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
676
- let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
677
- return (preact_cjs.createElement(internal_cjs.ContentContainer, { elTag: "td", elRef: props.elRef, elClasses: [
678
- 'fc-timeline-slot',
679
- 'fc-timeline-slot-lane',
680
- isEm && 'fc-timeline-slot-em',
681
- tDateProfile.isTimeScale ? (internal_cjs.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
682
- 'fc-timeline-slot-major' :
683
- 'fc-timeline-slot-minor') : '',
684
- ...(props.isDay ?
685
- internal_cjs.getDayClassNames(dateMeta, theme) :
686
- internal_cjs.getSlotClassNames(dateMeta, theme)),
687
- ], elAttrs: {
688
- 'data-date': dateEnv.formatIso(date, {
689
- omitTimeZoneOffset: true,
690
- omitTime: !tDateProfile.isTimeScale,
691
- }),
692
- }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { elTag: "div" }))));
599
+ function createVerticalStyle(props) {
600
+ if (props) {
601
+ return {
602
+ top: props.start,
603
+ height: props.size,
604
+ };
693
605
  }
694
606
  }
695
-
696
- class TimelineSlatsBody extends internal_cjs.BaseComponent {
697
- render() {
698
- let { props } = this;
699
- let { tDateProfile, cellElRefs } = props;
700
- let { slotDates, isWeekStarts } = tDateProfile;
701
- let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
702
- return (preact_cjs.createElement("tbody", null,
703
- preact_cjs.createElement("tr", null, slotDates.map((slotDate, i) => {
704
- let key = slotDate.toISOString();
705
- return (preact_cjs.createElement(TimelineSlatCell, { key: key, elRef: cellElRefs.createRef(key), date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isEm: isWeekStarts[i], isDay: isDay }));
706
- }))));
607
+ function createHorizontalStyle(props, isRtl) {
608
+ if (props) {
609
+ return {
610
+ [isRtl ? 'right' : 'left']: props.start,
611
+ width: props.size,
612
+ };
707
613
  }
708
614
  }
709
-
710
- class TimelineSlats extends internal_cjs.BaseComponent {
711
- constructor() {
712
- super(...arguments);
713
- this.rootElRef = preact_cjs.createRef();
714
- this.cellElRefs = new internal_cjs.RefMap();
715
- this.handleScrollRequest = (request) => {
716
- let { onScrollLeftRequest } = this.props;
717
- let { coords } = this;
718
- if (onScrollLeftRequest && coords) {
719
- if (request.time) {
720
- let scrollLeft = coords.coordFromLeft(coords.durationToCoord(request.time));
721
- onScrollLeftRequest(scrollLeft);
722
- }
723
- return true;
724
- }
725
- return null; // best?
726
- };
615
+ // Timeline-specific
616
+ // -------------------------------------------------------------------------------------------------
617
+ const MIN_SLOT_WIDTH = 30; // for real
618
+ /*
619
+ TODO: DRY with computeSlatHeight?
620
+ */
621
+ function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
622
+ if (labelInnerWidth == null || viewportWidth == null) {
623
+ return [undefined, undefined, false];
624
+ }
625
+ slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
626
+ const slatTryWidth = viewportWidth / slatCnt;
627
+ let slatLiquid;
628
+ let slatWidth;
629
+ if (slatTryWidth >= slatMinWidth) {
630
+ slatLiquid = true;
631
+ slatWidth = slatTryWidth;
727
632
  }
728
- render() {
729
- let { props, context } = this;
730
- return (preact_cjs.createElement("div", { className: "fc-timeline-slots", ref: this.rootElRef },
731
- preact_cjs.createElement("table", { "aria-hidden": true, className: context.theme.getClass('table'), style: {
732
- minWidth: props.tableMinWidth,
733
- width: props.clientWidth,
734
- } },
735
- props.tableColGroupNode,
736
- preact_cjs.createElement(TimelineSlatsBody, { cellElRefs: this.cellElRefs, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange }))));
633
+ else {
634
+ slatLiquid = false;
635
+ slatWidth = Math.max(slatMinWidth, slatTryWidth);
737
636
  }
738
- componentDidMount() {
739
- this.updateSizing();
740
- this.scrollResponder = this.context.createScrollResponder(this.handleScrollRequest);
637
+ return [slatWidth * slatCnt, slatWidth, slatLiquid];
638
+ }
639
+ function timeToCoord(// pixels
640
+ time, dateEnv, dateProfile, tDateProfile, slowWidth) {
641
+ let date = dateEnv.add(dateProfile.activeRange.start, time);
642
+ if (!tDateProfile.isTimeScale) {
643
+ date = internal_cjs.startOfDay(date);
741
644
  }
742
- componentDidUpdate(prevProps) {
743
- this.updateSizing();
744
- this.scrollResponder.update(prevProps.dateProfile !== this.props.dateProfile);
645
+ return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
646
+ }
647
+ function dateToCoord(// pixels
648
+ date, dateEnv, tDateProfile, slotWidth) {
649
+ let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
650
+ let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
651
+ return slotCoverage * slotWidth;
652
+ }
653
+ /*
654
+ returned value is between 0 and the number of snaps
655
+ */
656
+ function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
657
+ let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
658
+ if (snapDiff < 0) {
659
+ return 0;
745
660
  }
746
- componentWillUnmount() {
747
- this.scrollResponder.detach();
748
- if (this.props.onCoords) {
749
- this.props.onCoords(null);
750
- }
661
+ if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
662
+ return tDateProfile.snapCnt;
751
663
  }
752
- updateSizing() {
753
- let { props, context } = this;
754
- if (props.clientWidth !== null && // is sizing stable?
755
- this.scrollResponder
756
- // ^it's possible to have clientWidth immediately after mount (when returning from print view), but w/o scrollResponder
757
- ) {
758
- let rootEl = this.rootElRef.current;
759
- if (rootEl.offsetWidth) { // not hidden by css
760
- this.coords = new TimelineCoords(this.rootElRef.current, collectCellEls(this.cellElRefs.currentMap, props.tDateProfile.slotDates), props.dateProfile, props.tDateProfile, context.dateEnv, context.isRtl);
761
- if (props.onCoords) {
762
- props.onCoords(this.coords);
763
- }
764
- this.scrollResponder.update(false); // TODO: wouldn't have to do this if coords were in state
765
- }
766
- }
664
+ let snapDiffInt = Math.floor(snapDiff);
665
+ let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
666
+ if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
667
+ snapCoverage += snapDiff - snapDiffInt; // add the remainder
767
668
  }
768
- positionToHit(leftPosition) {
769
- let { outerCoordCache } = this.coords;
770
- let { dateEnv, isRtl } = this.context;
771
- let { tDateProfile } = this.props;
772
- let slatIndex = outerCoordCache.leftToIndex(leftPosition);
773
- if (slatIndex != null) {
774
- // somewhat similar to what TimeGrid does. consolidate?
775
- let slatWidth = outerCoordCache.getWidth(slatIndex);
776
- let partial = isRtl ?
777
- (outerCoordCache.rights[slatIndex] - leftPosition) / slatWidth :
778
- (leftPosition - outerCoordCache.lefts[slatIndex]) / slatWidth;
779
- let localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot);
780
- let start = dateEnv.add(tDateProfile.slotDates[slatIndex], internal_cjs.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
781
- let end = dateEnv.add(start, tDateProfile.snapDuration);
782
- return {
783
- dateSpan: {
784
- range: { start, end },
785
- allDay: !this.props.tDateProfile.isTimeScale,
786
- },
787
- dayEl: this.cellElRefs.currentMap[slatIndex],
788
- left: outerCoordCache.lefts[slatIndex],
789
- right: outerCoordCache.rights[slatIndex],
790
- };
791
- }
792
- return null;
669
+ else {
670
+ // a fractional value, meaning the date is not visible
671
+ // always round up in this case. works for start AND end dates in a range.
672
+ snapCoverage = Math.ceil(snapCoverage);
793
673
  }
794
- }
795
- function collectCellEls(elMap, slotDates) {
796
- return slotDates.map((slotDate) => {
797
- let key = slotDate.toISOString();
798
- return elMap[key];
799
- });
674
+ return snapCoverage;
800
675
  }
801
676
 
802
- function computeSegHCoords(segs, minWidth, timelineCoords) {
803
- let hcoords = [];
804
- if (timelineCoords) {
805
- for (let seg of segs) {
806
- let res = timelineCoords.rangeToCoords(seg);
807
- let start = Math.round(res.start); // for barely-overlapping collisions
808
- let end = Math.round(res.end); //
809
- if (end - start < minWidth) {
810
- end = start + minWidth;
811
- }
812
- hcoords.push({ start, end });
813
- }
677
+ function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
678
+ const res = {};
679
+ for (const seg of segs) {
680
+ res[seg.eventRange.instance.instanceId] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
814
681
  }
815
- return hcoords;
682
+ return res;
683
+ }
684
+ function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
685
+ const startCoord = dateToCoord(seg.start, dateEnv, tDateProfile, slotWidth);
686
+ const endCoord = dateToCoord(seg.end, dateEnv, tDateProfile, slotWidth);
687
+ let size = endCoord - startCoord;
688
+ if (segMinWidth) {
689
+ size = Math.max(size, segMinWidth);
690
+ }
691
+ return { start: startCoord, size };
816
692
  }
817
- function computeFgSegPlacements(segs, segHCoords, // might not have for every seg
818
- eventInstanceHeights, // might not have for every seg
819
- moreLinkHeights, // might not have for every more-link
693
+ function computeFgSegPlacements(// mostly horizontals
694
+ segs, segHorizontals, segHeights, // keyed by instanceId
820
695
  strictOrder, maxStackCnt) {
821
- let segInputs = [];
822
- let crudePlacements = []; // when we don't know dims
696
+ let segEntries = [];
823
697
  for (let i = 0; i < segs.length; i += 1) {
824
698
  let seg = segs[i];
825
699
  let instanceId = seg.eventRange.instance.instanceId;
826
- let height = eventInstanceHeights[instanceId];
827
- let hcoords = segHCoords[i];
828
- if (height && hcoords) {
829
- segInputs.push({
700
+ let height = segHeights.get(instanceId);
701
+ let hcoords = segHorizontals[instanceId];
702
+ if (height != null && hcoords != null) {
703
+ segEntries.push({
830
704
  index: i,
831
- span: hcoords,
832
- thickness: height,
833
- });
834
- }
835
- else {
836
- crudePlacements.push({
837
705
  seg,
838
- hcoords,
839
- top: null,
706
+ span: {
707
+ start: hcoords.start,
708
+ end: hcoords.start + hcoords.size,
709
+ },
710
+ thickness: height,
840
711
  });
841
712
  }
842
713
  }
@@ -847,80 +718,85 @@ strictOrder, maxStackCnt) {
847
718
  if (maxStackCnt != null) {
848
719
  hierarchy.maxStackCnt = maxStackCnt;
849
720
  }
850
- let hiddenEntries = hierarchy.addSegs(segInputs);
851
- let hiddenPlacements = hiddenEntries.map((entry) => ({
852
- seg: segs[entry.index],
853
- hcoords: entry.span,
854
- top: null,
855
- }));
721
+ let hiddenEntries = hierarchy.addSegs(segEntries);
856
722
  let hiddenGroups = internal_cjs.groupIntersectingEntries(hiddenEntries);
857
- let moreLinkInputs = [];
858
- let moreLinkCrudePlacements = [];
859
- const extractSeg = (entry) => segs[entry.index];
860
- for (let i = 0; i < hiddenGroups.length; i += 1) {
861
- let hiddenGroup = hiddenGroups[i];
862
- let sortedSegs = hiddenGroup.entries.map(extractSeg);
863
- let height = moreLinkHeights[internal_cjs.buildIsoString(internal_cjs.computeEarliestSegStart(sortedSegs))]; // not optimal :(
864
- if (height != null) {
865
- // NOTE: the hiddenGroup's spanStart/spanEnd are already computed by rangeToCoords. computed during input.
866
- moreLinkInputs.push({
867
- index: segs.length + i,
868
- thickness: height,
869
- span: hiddenGroup.span,
870
- });
871
- }
872
- else {
873
- moreLinkCrudePlacements.push({
874
- seg: sortedSegs,
875
- hcoords: hiddenGroup.span,
876
- top: null,
877
- });
878
- }
879
- }
723
+ let hiddenGroupEntries = hiddenGroups.map((hiddenGroup, index) => ({
724
+ index: segs.length + index,
725
+ segGroup: hiddenGroup,
726
+ span: hiddenGroup.span,
727
+ thickness: 1, // HACK to ensure it's placed
728
+ }));
880
729
  // add more-links into the hierarchy, but don't limit
881
730
  hierarchy.maxStackCnt = -1;
882
- hierarchy.addSegs(moreLinkInputs);
731
+ hierarchy.addSegs(hiddenGroupEntries);
883
732
  let visibleRects = hierarchy.toRects();
884
- let visiblePlacements = [];
885
- let maxHeight = 0;
733
+ let segTops = {};
734
+ let hiddenGroupTops = {};
886
735
  for (let rect of visibleRects) {
887
- let segIndex = rect.index;
888
- visiblePlacements.push({
889
- seg: segIndex < segs.length
890
- ? segs[segIndex] // a real seg
891
- : hiddenGroups[segIndex - segs.length].entries.map(extractSeg),
892
- hcoords: rect.span,
893
- top: rect.levelCoord,
894
- });
895
- maxHeight = Math.max(maxHeight, rect.levelCoord + rect.thickness);
736
+ const { seg, segGroup } = rect;
737
+ if (seg) { // regular seg
738
+ segTops[seg.eventRange.instance.instanceId] = rect.levelCoord;
739
+ }
740
+ else { // hiddenGroup
741
+ hiddenGroupTops[segGroup.key] = rect.levelCoord;
742
+ }
896
743
  }
897
744
  return [
898
- visiblePlacements.concat(crudePlacements, hiddenPlacements, moreLinkCrudePlacements),
899
- maxHeight,
745
+ segTops,
746
+ computeMaxBottom(segs, segTops, segHeights),
747
+ hiddenGroups,
748
+ hiddenGroupTops,
900
749
  ];
901
750
  }
751
+ function computeMaxBottom(segs, segTops, segHeights) {
752
+ let max = 0;
753
+ for (const seg of segs) {
754
+ const { instanceId } = seg.eventRange.instance;
755
+ const top = segTops[instanceId];
756
+ const height = segHeights.get(instanceId);
757
+ if (top != null && height != null) {
758
+ max = Math.max(max, top + height);
759
+ }
760
+ }
761
+ return max;
762
+ }
763
+ /*
764
+ TODO: converge with computeMaxBottom, but keys are different
765
+ */
766
+ function computeMoreLinkMaxBottom(hiddenGroups, hiddenGroupTops, hiddenGroupHeights) {
767
+ let max = 0;
768
+ for (const hiddenGroup of hiddenGroups) {
769
+ const top = hiddenGroupTops[hiddenGroup.key];
770
+ const height = hiddenGroupHeights.get(hiddenGroup.key);
771
+ if (top != null && height != null) {
772
+ max = Math.max(max, top + height);
773
+ }
774
+ }
775
+ return max;
776
+ }
902
777
 
903
778
  class TimelineLaneBg extends internal_cjs.BaseComponent {
904
779
  render() {
905
780
  let { props } = this;
906
781
  let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
907
- return props.timelineCoords && (preact_cjs.createElement("div", { className: "fc-timeline-bg" },
908
- this.renderSegs(props.businessHourSegs || [], props.timelineCoords, 'non-business'),
909
- this.renderSegs(props.bgEventSegs || [], props.timelineCoords, 'bg-event'),
910
- this.renderSegs(highlightSeg, props.timelineCoords, 'highlight')));
911
- }
912
- renderSegs(segs, timelineCoords, fillType) {
913
- let { todayRange, nowDate } = this.props;
914
- let { isRtl } = this.context;
915
- let segHCoords = computeSegHCoords(segs, 0, timelineCoords);
916
- let children = segs.map((seg, i) => {
917
- let hcoords = segHCoords[i];
918
- let hStyle = coordsToCss(hcoords, isRtl);
782
+ return (preact_cjs.createElement("div", { className: "fc-timeline-bg" },
783
+ this.renderSegs(props.businessHourSegs || [], 'non-business'),
784
+ this.renderSegs(props.bgEventSegs || [], 'bg-event'),
785
+ this.renderSegs(highlightSeg, 'highlight')));
786
+ }
787
+ renderSegs(segs, fillType) {
788
+ let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
789
+ let { dateEnv, isRtl } = this.context;
790
+ return (preact_cjs.createElement(preact_cjs.Fragment, null, segs.map((seg) => {
791
+ let hStyle; // TODO
792
+ if (slotWidth != null) {
793
+ let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
794
+ hStyle = horizontalsToCss(segHorizontal, isRtl);
795
+ }
919
796
  return (preact_cjs.createElement("div", { key: internal_cjs.buildEventRangeKey(seg.eventRange), className: "fc-timeline-bg-harness", style: hStyle }, fillType === 'bg-event' ?
920
797
  preact_cjs.createElement(internal_cjs.BgEvent, Object.assign({ seg: seg }, internal_cjs.getSegMeta(seg, todayRange, nowDate))) :
921
798
  internal_cjs.renderFill(fillType)));
922
- });
923
- return preact_cjs.createElement(preact_cjs.Fragment, null, children);
799
+ })));
924
800
  }
925
801
  }
926
802
 
@@ -929,8 +805,8 @@ class TimelineLaneSlicer extends internal_cjs.Slicer {
929
805
  let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
930
806
  let segs = [];
931
807
  // protect against when the span is entirely in an invalid date region
932
- if (computeDateSnapCoverage(normalRange.start, tDateProfile, dateEnv)
933
- < computeDateSnapCoverage(normalRange.end, tDateProfile, dateEnv)) {
808
+ if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
809
+ < computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
934
810
  // intersect the footprint's range with the grid's range
935
811
  let slicedRange = internal_cjs.intersectRanges(normalRange, tDateProfile.normalizedRange);
936
812
  if (slicedRange) {
@@ -956,48 +832,84 @@ const DEFAULT_TIME_FORMAT = internal_cjs.createFormatter({
956
832
  });
957
833
  class TimelineEvent extends internal_cjs.BaseComponent {
958
834
  render() {
959
- let { props } = this;
960
- return (preact_cjs.createElement(internal_cjs.StandardEvent, Object.assign({}, props, { elClasses: ['fc-timeline-event', 'fc-h-event'], defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
835
+ let { props, context } = this;
836
+ let { options } = context;
837
+ return (preact_cjs.createElement(internal_cjs.StandardEvent, Object.assign({}, props, { elClasses: [
838
+ 'fc-timeline-event',
839
+ 'fc-h-event',
840
+ options.eventOverlap === false // TODO: fix bad default
841
+ ? 'fc-timeline-event-spacious'
842
+ : ''
843
+ ], defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
961
844
  }
962
845
  }
963
846
 
964
847
  class TimelineLaneMoreLink extends internal_cjs.BaseComponent {
965
848
  render() {
966
- let { props, context } = this;
967
- let { hiddenSegs, placement, resourceId } = props;
968
- let { top, hcoords } = placement;
969
- let isVisible = hcoords && top !== null;
970
- let hStyle = coordsToCss(hcoords, context.isRtl);
849
+ let { props } = this;
850
+ let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
971
851
  let extraDateSpan = resourceId ? { resourceId } : {};
972
- return (preact_cjs.createElement(internal_cjs.MoreLinkContainer, { elRef: props.elRef, elClasses: ['fc-timeline-more-link'], elStyle: Object.assign({ visibility: isVisible ? '' : 'hidden', top: top || 0 }, hStyle), allDayDate: null, moreCnt: hiddenSegs.length, allSegs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, extraDateSpan: extraDateSpan, popoverContent: () => (preact_cjs.createElement(preact_cjs.Fragment, null, hiddenSegs.map((seg) => {
852
+ return (preact_cjs.createElement(internal_cjs.MoreLinkContainer, { elClasses: ['fc-timeline-more-link'], allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, extraDateSpan: extraDateSpan, popoverContent: () => (preact_cjs.createElement(preact_cjs.Fragment, null, hiddenSegs.map((seg) => {
973
853
  let instanceId = seg.eventRange.instance.instanceId;
974
- return (preact_cjs.createElement("div", { key: instanceId, style: { visibility: props.isForcedInvisible[instanceId] ? 'hidden' : '' } },
854
+ return (preact_cjs.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
975
855
  preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection }, internal_cjs.getSegMeta(seg, props.todayRange, props.nowDate)))));
976
- }))) }, (InnerContent) => (preact_cjs.createElement(InnerContent, { elTag: "div", elClasses: ['fc-timeline-more-link-inner', 'fc-sticky'] }))));
856
+ }))) }, (InnerContent) => (preact_cjs.createElement(InnerContent, { elTag: "div", elClasses: ['fc-timeline-more-link-inner', 'fc-sticky-x'] }))));
857
+ }
858
+ }
859
+
860
+ /*
861
+ TODO: make DRY with other Event Harnesses
862
+ */
863
+ class TimelineEventHarness extends preact_cjs.Component {
864
+ constructor() {
865
+ super(...arguments);
866
+ // ref
867
+ this.rootElRef = preact_cjs.createRef();
868
+ }
869
+ render() {
870
+ const { props } = this;
871
+ return (preact_cjs.createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
872
+ }
873
+ componentDidMount() {
874
+ const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
875
+ this.detachHeight = internal_cjs.watchHeight(rootEl, (height) => {
876
+ internal_cjs.setRef(this.props.heightRef, height);
877
+ });
878
+ }
879
+ componentWillUnmount() {
880
+ this.detachHeight();
977
881
  }
978
882
  }
979
883
 
884
+ /*
885
+ TODO: split TimelineLaneBg and TimelineLaneFg?
886
+ */
980
887
  class TimelineLane extends internal_cjs.BaseComponent {
981
888
  constructor() {
982
889
  super(...arguments);
983
- this.slicer = new TimelineLaneSlicer();
890
+ // memo
984
891
  this.sortEventSegs = internal_cjs.memoize(internal_cjs.sortEventSegs);
985
- this.harnessElRefs = new internal_cjs.RefMap();
986
- this.moreElRefs = new internal_cjs.RefMap();
987
- this.innerElRef = preact_cjs.createRef();
988
- // TODO: memoize event positioning
989
- this.state = {
990
- eventInstanceHeights: {},
991
- moreLinkHeights: {},
892
+ // refs
893
+ this.segHeightRefMap = new internal_cjs.RefMap(() => {
894
+ internal_cjs.afterSize(this.handleSegHeights);
895
+ });
896
+ this.moreLinkHeightRefMap = new internal_cjs.RefMap(() => {
897
+ internal_cjs.afterSize(this.handleMoreLinkHeights);
898
+ });
899
+ // internal
900
+ this.slicer = new TimelineLaneSlicer();
901
+ this.handleMoreLinkHeights = () => {
902
+ this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
992
903
  };
993
- this.handleResize = (isForced) => {
994
- if (isForced) {
995
- this.updateSize();
996
- }
904
+ this.handleSegHeights = () => {
905
+ this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
997
906
  };
998
907
  }
908
+ /*
909
+ TODO: lots of memoization needed here!
910
+ */
999
911
  render() {
1000
- let { props, state, context } = this;
912
+ let { props, context, segHeightRefMap } = this;
1001
913
  let { options } = context;
1002
914
  let { dateProfile, tDateProfile } = props;
1003
915
  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...
@@ -1006,106 +918,275 @@ class TimelineLane extends internal_cjs.BaseComponent {
1006
918
  (slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
1007
919
  [];
1008
920
  let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
1009
- let fgSegHCoords = computeSegHCoords(fgSegs, options.eventMinWidth, props.timelineCoords);
1010
- let [fgPlacements, fgHeight] = computeFgSegPlacements(fgSegs, fgSegHCoords, state.eventInstanceHeights, state.moreLinkHeights, options.eventOrderStrict, options.eventMaxStack);
1011
- let isForcedInvisible = // TODO: more convenient
921
+ let fgSegHorizontals = props.slotWidth != null
922
+ ? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
923
+ : {};
924
+ let [fgSegTops, fgSegsBottom, hiddenGroups, hiddenGroupTops] = computeFgSegPlacements(// verticals
925
+ fgSegs, fgSegHorizontals, segHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
926
+ let moreLinksBottom = computeMoreLinkMaxBottom(hiddenGroups, hiddenGroupTops, this.moreLinkHeightRefMap.current);
927
+ let innerHeight = Math.max(moreLinksBottom, fgSegsBottom);
928
+ let forcedInvisibleMap = // TODO: more convenient/DRY
1012
929
  (slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
1013
930
  (slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
1014
931
  {};
1015
932
  return (preact_cjs.createElement(preact_cjs.Fragment, null,
1016
- preact_cjs.createElement(TimelineLaneBg, { businessHourSegs: slicedProps.businessHourSegs, bgEventSegs: slicedProps.bgEventSegs, timelineCoords: props.timelineCoords, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */, dateSelectionSegs: slicedProps.dateSelectionSegs, nowDate: props.nowDate, todayRange: props.todayRange }),
1017
- preact_cjs.createElement("div", { className: "fc-timeline-events fc-scrollgrid-sync-inner", ref: this.innerElRef, style: { height: fgHeight } },
1018
- this.renderFgSegs(fgPlacements, isForcedInvisible, false, false, false),
1019
- this.renderFgSegs(buildMirrorPlacements(mirrorSegs, props.timelineCoords, fgPlacements), {}, Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
933
+ preact_cjs.createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
934
+ // content
935
+ bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
936
+ // dimensions
937
+ slotWidth: props.slotWidth }),
938
+ preact_cjs.createElement("div", { className: [
939
+ 'fc-timeline-events',
940
+ 'fc-content-box',
941
+ options.eventOverlap === false // TODO: fix bad default
942
+ ? 'fc-timeline-events-overlap-disabled'
943
+ : 'fc-timeline-events-overlap-enabled'
944
+ ].join(' '), style: { height: innerHeight } },
945
+ this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
946
+ false, // isResizing
947
+ false),
948
+ this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
949
+ ? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
950
+ : {}, fgSegTops, {}, // forcedInvisibleMap
951
+ [], {}, Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
952
+ }
953
+ renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
954
+ let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
955
+ let isMirror = isDragging || isResizing || isDateSelecting;
956
+ return (preact_cjs.createElement(preact_cjs.Fragment, null,
957
+ segs.map((seg) => {
958
+ const { instanceId } = seg.eventRange.instance;
959
+ const segTop = segTops[instanceId];
960
+ const segHorizontal = segHorizontals[instanceId];
961
+ const isVisible = isMirror ||
962
+ (segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
963
+ return (preact_cjs.createElement(TimelineEventHarness, { key: instanceId, style: Object.assign({ visibility: isVisible ? '' : 'hidden', top: segTop || 0 }, horizontalsToCss(segHorizontal, context.isRtl)), heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
964
+ preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection /* TODO: bad for mirror? */ }, internal_cjs.getSegMeta(seg, props.todayRange, props.nowDate)))));
965
+ }),
966
+ hiddenGroups.map((hiddenGroup) => (preact_cjs.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops[hiddenGroup.key] || 0 }, horizontalsToCss({
967
+ start: hiddenGroup.span.start,
968
+ size: hiddenGroup.span.end - hiddenGroup.span.start
969
+ }, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
970
+ preact_cjs.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 }))))));
971
+ }
972
+ }
973
+
974
+ class TimelineHeaderCell extends internal_cjs.BaseComponent {
975
+ constructor() {
976
+ super(...arguments);
977
+ // memo
978
+ this.refineRenderProps = internal_cjs.memoizeObjArg(refineRenderProps);
979
+ this.buildCellNavLinkAttrs = internal_cjs.memoize(buildCellNavLinkAttrs);
980
+ // ref
981
+ this.innerElRef = preact_cjs.createRef();
982
+ // TODO: unset width/height ref on unmount?
983
+ }
984
+ render() {
985
+ let { props, context } = this;
986
+ let { dateEnv, options } = context;
987
+ let { cell, dateProfile, tDateProfile } = props;
988
+ // the cell.rowUnit is f'd
989
+ // giving 'month' for a 3-day view
990
+ // workaround: to infer day, do NOT time
991
+ let dateMeta = internal_cjs.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
992
+ let renderProps = this.refineRenderProps({
993
+ level: props.rowLevel,
994
+ dateMarker: cell.date,
995
+ text: cell.text,
996
+ dateEnv: context.dateEnv,
997
+ viewApi: context.viewApi,
998
+ });
999
+ return (preact_cjs.createElement(internal_cjs.ContentContainer, { elTag: "div", elClasses: [
1000
+ 'fc-timeline-slot-label',
1001
+ 'fc-timeline-slot',
1002
+ cell.isWeekStart ? 'fc-timeline-slot-em' : '',
1003
+ 'fc-header-cell',
1004
+ 'fc-cell',
1005
+ 'fc-flex-column',
1006
+ 'fc-justify-center',
1007
+ props.isCentered ? 'fc-align-center' : 'fc-align-start',
1008
+ ...( // TODO: so slot classnames for week/month/bigger. see note above about rowUnit
1009
+ cell.rowUnit === 'time' ?
1010
+ internal_cjs.getSlotClassNames(dateMeta, context.theme) :
1011
+ internal_cjs.getDayClassNames(dateMeta, context.theme)),
1012
+ ], elAttrs: {
1013
+ 'data-date': dateEnv.formatIso(cell.date, {
1014
+ omitTime: !tDateProfile.isTimeScale,
1015
+ omitTimeZoneOffset: true,
1016
+ }),
1017
+ }, elStyle: {
1018
+ width: props.slotWidth != null
1019
+ ? props.slotWidth * cell.colspan
1020
+ : undefined,
1021
+ }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact_cjs.createElement("div", { ref: this.innerElRef, className: [
1022
+ 'fc-flex-column',
1023
+ props.isSticky ? 'fc-sticky-x' : '',
1024
+ ].join(' ') },
1025
+ preact_cjs.createElement(InnerContent, { elTag: "a", elClasses: [
1026
+ 'fc-cell-inner',
1027
+ 'fc-padding-md',
1028
+ ], elAttrs: this.buildCellNavLinkAttrs(context, cell.date, cell.rowUnit) })))));
1020
1029
  }
1021
1030
  componentDidMount() {
1022
- this.updateSize();
1023
- this.context.addResizeHandler(this.handleResize);
1024
- }
1025
- componentDidUpdate(prevProps, prevState) {
1026
- if (prevProps.eventStore !== this.props.eventStore || // external thing changed?
1027
- prevProps.timelineCoords !== this.props.timelineCoords || // external thing changed?
1028
- prevState.moreLinkHeights !== this.state.moreLinkHeights // HACK. see addStateEquality
1029
- ) {
1030
- this.updateSize();
1031
- }
1031
+ const { props } = this;
1032
+ const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
1033
+ this.detachSize = internal_cjs.watchSize(innerEl, (width, height) => {
1034
+ internal_cjs.setRef(props.innerWidthRef, width);
1035
+ internal_cjs.setRef(props.innerHeightRef, height);
1036
+ // HACK for sticky-centering
1037
+ innerEl.style.left = innerEl.style.right =
1038
+ (props.isCentered && props.isSticky)
1039
+ ? `calc(50% - ${width / 2}px)`
1040
+ : '';
1041
+ });
1032
1042
  }
1033
1043
  componentWillUnmount() {
1034
- this.context.removeResizeHandler(this.handleResize);
1035
- }
1036
- updateSize() {
1037
- let { props } = this;
1038
- let { timelineCoords } = props;
1039
- const innerEl = this.innerElRef.current;
1040
- if (props.onHeightChange) {
1041
- props.onHeightChange(innerEl, false);
1042
- }
1043
- if (timelineCoords) {
1044
- this.setState({
1045
- eventInstanceHeights: internal_cjs.mapHash(this.harnessElRefs.currentMap, (harnessEl) => (Math.round(harnessEl.getBoundingClientRect().height))),
1046
- moreLinkHeights: internal_cjs.mapHash(this.moreElRefs.currentMap, (moreEl) => (Math.round(moreEl.getBoundingClientRect().height))),
1047
- }, () => {
1048
- if (props.onHeightChange) {
1049
- props.onHeightChange(innerEl, true);
1050
- }
1051
- });
1052
- }
1053
- // hack
1054
- if (props.syncParentMinHeight) {
1055
- innerEl.parentElement.style.minHeight = innerEl.style.height;
1056
- }
1044
+ this.detachSize();
1057
1045
  }
1058
- renderFgSegs(segPlacements, isForcedInvisible, isDragging, isResizing, isDateSelecting) {
1059
- let { harnessElRefs, moreElRefs, props, context } = this;
1060
- let isMirror = isDragging || isResizing || isDateSelecting;
1061
- return (preact_cjs.createElement(preact_cjs.Fragment, null, segPlacements.map((segPlacement) => {
1062
- let { seg, hcoords, top } = segPlacement;
1063
- if (Array.isArray(seg)) { // a more-link
1064
- let isoStr = internal_cjs.buildIsoString(internal_cjs.computeEarliestSegStart(seg));
1065
- return (preact_cjs.createElement(TimelineLaneMoreLink, { key: 'm:' + isoStr /* "m" for "more" */, elRef: moreElRefs.createRef(isoStr), hiddenSegs: seg, placement: segPlacement, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventSelection: props.eventSelection, resourceId: props.resourceId, isForcedInvisible: isForcedInvisible }));
1046
+ }
1047
+ // Utils
1048
+ // -------------------------------------------------------------------------------------------------
1049
+ function buildCellNavLinkAttrs(context, cellDate, rowUnit) {
1050
+ return (rowUnit && rowUnit !== 'time')
1051
+ ? internal_cjs.buildNavLinkAttrs(context, cellDate, rowUnit)
1052
+ : {};
1053
+ }
1054
+ function renderInnerContent(renderProps) {
1055
+ return renderProps.text;
1056
+ }
1057
+ function refineRenderProps(input) {
1058
+ return {
1059
+ level: input.level,
1060
+ date: input.dateEnv.toDate(input.dateMarker),
1061
+ view: input.viewApi,
1062
+ text: input.text,
1063
+ };
1064
+ }
1065
+
1066
+ class TimelineHeaderRow extends internal_cjs.BaseComponent {
1067
+ constructor() {
1068
+ super(...arguments);
1069
+ // refs
1070
+ this.innerWidthRefMap = new internal_cjs.RefMap(() => {
1071
+ internal_cjs.afterSize(this.handleInnerWidths);
1072
+ });
1073
+ this.innerHeightRefMap = new internal_cjs.RefMap(() => {
1074
+ internal_cjs.afterSize(this.handleInnerHeights);
1075
+ });
1076
+ this.handleInnerWidths = () => {
1077
+ const innerWidthMap = this.innerWidthRefMap.current;
1078
+ let max = 0;
1079
+ for (const innerWidth of innerWidthMap.values()) {
1080
+ max = Math.max(max, innerWidth);
1081
+ }
1082
+ // TODO: ensure not equal?
1083
+ internal_cjs.setRef(this.props.innerWidthRef, max);
1084
+ };
1085
+ this.handleInnerHeights = () => {
1086
+ const innerHeightMap = this.innerHeightRefMap.current;
1087
+ let max = 0;
1088
+ for (const innerHeight of innerHeightMap.values()) {
1089
+ max = Math.max(max, innerHeight);
1066
1090
  }
1067
- let instanceId = seg.eventRange.instance.instanceId;
1068
- let isVisible = isMirror || Boolean(!isForcedInvisible[instanceId] && hcoords && top !== null);
1069
- let hStyle = coordsToCss(hcoords, context.isRtl);
1070
- return (preact_cjs.createElement("div", { key: 'e:' + instanceId /* "e" for "event" */, ref: isMirror ? null : harnessElRefs.createRef(instanceId), className: "fc-timeline-event-harness", style: Object.assign({ visibility: isVisible ? '' : 'hidden', top: top || 0 }, hStyle) },
1071
- preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection /* TODO: bad for mirror? */ }, internal_cjs.getSegMeta(seg, props.todayRange, props.nowDate)))));
1091
+ // TODO: ensure not equal?
1092
+ internal_cjs.setRef(this.props.innerHeighRef, max);
1093
+ };
1094
+ }
1095
+ render() {
1096
+ const { props, innerWidthRefMap, innerHeightRefMap } = this;
1097
+ const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
1098
+ const isSticky = !props.isLastRow;
1099
+ return (preact_cjs.createElement("div", { className: 'fc-row', style: { height: props.height } }, props.cells.map((cell) => {
1100
+ // TODO: make this part of the cell obj?
1101
+ // TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
1102
+ // TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
1103
+ const key = cell.rowUnit + ':' + cell.date.toISOString();
1104
+ return (preact_cjs.createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isCentered: isCentered, isSticky: isSticky,
1105
+ // refs
1106
+ innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
1107
+ // dimensions
1108
+ slotWidth: props.slotWidth }));
1072
1109
  })));
1073
1110
  }
1074
1111
  }
1075
- TimelineLane.addStateEquality({
1076
- eventInstanceHeights: internal_cjs.isPropsEqual,
1077
- moreLinkHeights: internal_cjs.isPropsEqual,
1078
- });
1079
- function buildMirrorPlacements(mirrorSegs, timelineCoords, fgPlacements) {
1080
- if (!mirrorSegs.length || !timelineCoords) {
1081
- return [];
1082
- }
1083
- let topsByInstanceId = buildAbsoluteTopHash(fgPlacements); // TODO: cache this at first render?
1084
- return mirrorSegs.map((seg) => ({
1085
- seg,
1086
- hcoords: timelineCoords.rangeToCoords(seg),
1087
- top: topsByInstanceId[seg.eventRange.instance.instanceId],
1088
- }));
1112
+
1113
+ class TimelineNowIndicatorLine extends internal_cjs.BaseComponent {
1114
+ render() {
1115
+ const { props, context } = this;
1116
+ return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
1117
+ preact_cjs.createElement(internal_cjs.NowIndicatorContainer // TODO: make separate component?
1118
+ , { elClasses: ['fc-timeline-now-indicator-line'], elStyle: props.slotWidth != null
1119
+ ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1120
+ : {}, isAxis: false, date: props.nowDate })));
1121
+ }
1089
1122
  }
1090
- function buildAbsoluteTopHash(placements) {
1091
- let topsByInstanceId = {};
1092
- for (let placement of placements) {
1093
- let { seg } = placement;
1094
- if (!Array.isArray(seg)) { // doesn't represent a more-link
1095
- topsByInstanceId[seg.eventRange.instance.instanceId] = placement.top;
1096
- }
1123
+
1124
+ class TimelineNowIndicatorArrow extends internal_cjs.BaseComponent {
1125
+ render() {
1126
+ const { props, context } = this;
1127
+ return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
1128
+ preact_cjs.createElement(internal_cjs.NowIndicatorContainer, { elClasses: ['fc-timeline-now-indicator-arrow'], elStyle: props.slotWidth != null
1129
+ ? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
1130
+ : {}, isAxis: true, date: props.nowDate })));
1097
1131
  }
1098
- return topsByInstanceId;
1099
1132
  }
1100
1133
 
1101
- class TimelineGrid extends internal_cjs.DateComponent {
1134
+ class TimelineView extends internal_cjs.DateComponent {
1102
1135
  constructor() {
1103
1136
  super(...arguments);
1104
- this.slatsRef = preact_cjs.createRef();
1105
- this.state = {
1106
- coords: null,
1137
+ // memoized
1138
+ this.buildTimelineDateProfile = internal_cjs.memoize(buildTimelineDateProfile);
1139
+ this.computeSlotWidth = internal_cjs.memoize(computeSlotWidth);
1140
+ // refs
1141
+ this.headerScrollerRef = preact_cjs.createRef();
1142
+ this.bodyScrollerRef = preact_cjs.createRef();
1143
+ this.footerScrollerRef = preact_cjs.createRef();
1144
+ // Sizing
1145
+ // -----------------------------------------------------------------------------------------------
1146
+ this.handleHeaderSlotInnerWidth = (innerWidth) => {
1147
+ this.headerSlotInnerWidth = innerWidth;
1148
+ internal_cjs.afterSize(this.handleSlotInnerWidths);
1149
+ };
1150
+ this.handleBodySlotInnerWidth = (innerWidth) => {
1151
+ this.bodySlotInnerWidth = innerWidth;
1152
+ internal_cjs.afterSize(this.handleSlotInnerWidths);
1153
+ };
1154
+ this.handleSlotInnerWidths = () => {
1155
+ const { state } = this;
1156
+ const slotInnerWidth = Math.max(this.headerSlotInnerWidth, this.bodySlotInnerWidth);
1157
+ if (state.slotInnerWidth !== slotInnerWidth) {
1158
+ this.setState({ slotInnerWidth });
1159
+ }
1160
+ };
1161
+ this.handleScrollerWidth = (scrollerWidth) => {
1162
+ this.setState({
1163
+ scrollerWidth,
1164
+ });
1165
+ };
1166
+ this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
1167
+ this.setState({
1168
+ leftScrollbarWidth
1169
+ });
1170
+ };
1171
+ this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
1172
+ this.setState({
1173
+ rightScrollbarWidth
1174
+ });
1107
1175
  };
1108
- this.handeEl = (el) => {
1176
+ this.timeScrollResponder = new internal_cjs.ScrollResponder((time) => {
1177
+ const { props, context, tDateProfile, slotWidth } = this;
1178
+ if (slotWidth != null) {
1179
+ const x = timeToCoord(time, context.dateEnv, props.dateProfile, tDateProfile, slotWidth) +
1180
+ (context.isRtl ? -1 : 1); // overcome border. TODO: DRY this up
1181
+ this.syncedScroller.scrollTo({ x });
1182
+ return true;
1183
+ }
1184
+ return false;
1185
+ });
1186
+ // Hit System
1187
+ // -----------------------------------------------------------------------------------------------
1188
+ this.handeBodyEl = (el) => {
1189
+ this.bodyEl = el;
1109
1190
  if (el) {
1110
1191
  this.context.registerInteractiveComponent(this, { el });
1111
1192
  }
@@ -1113,45 +1194,141 @@ class TimelineGrid extends internal_cjs.DateComponent {
1113
1194
  this.context.unregisterInteractiveComponent(this);
1114
1195
  }
1115
1196
  };
1116
- this.handleCoords = (coords) => {
1117
- this.setState({ coords });
1118
- if (this.props.onSlatCoords) {
1119
- this.props.onSlatCoords(coords);
1120
- }
1121
- };
1122
1197
  }
1123
1198
  render() {
1124
- let { props, state, context } = this;
1125
- let { options } = context;
1126
- let { dateProfile, tDateProfile } = props;
1127
- let timerUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
1128
- return (preact_cjs.createElement("div", { className: "fc-timeline-body", ref: this.handeEl, style: {
1129
- minWidth: props.tableMinWidth,
1130
- height: props.clientHeight,
1131
- width: props.clientWidth,
1132
- } },
1133
- preact_cjs.createElement(internal_cjs.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => (preact_cjs.createElement(preact_cjs.Fragment, null,
1134
- preact_cjs.createElement(TimelineSlats, { ref: this.slatsRef, dateProfile: dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, clientWidth: props.clientWidth, tableColGroupNode: props.tableColGroupNode, tableMinWidth: props.tableMinWidth, onCoords: this.handleCoords, onScrollLeftRequest: props.onScrollLeftRequest }),
1135
- preact_cjs.createElement(TimelineLane, { dateProfile: dateProfile, tDateProfile: props.tDateProfile, nowDate: nowDate, todayRange: todayRange, nextDayThreshold: options.nextDayThreshold, businessHours: props.businessHours, eventStore: props.eventStore, eventUiBases: props.eventUiBases, dateSelection: props.dateSelection, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, timelineCoords: state.coords, syncParentMinHeight: true }),
1136
- (options.nowIndicator && state.coords && state.coords.isDateInRange(nowDate)) && (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
1137
- preact_cjs.createElement(internal_cjs.NowIndicatorContainer, { elClasses: ['fc-timeline-now-indicator-line'], elStyle: coordToCss(state.coords.dateToCoord(nowDate), context.isRtl), isAxis: false, date: nowDate }))))))));
1138
- }
1139
- // Hit System
1140
- // ------------------------------------------------------------------------------------------
1199
+ const { props, state, context } = this;
1200
+ const { options } = context;
1201
+ /* date */
1202
+ const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
1203
+ const { cellRows } = tDateProfile;
1204
+ const timerUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
1205
+ /* table settings */
1206
+ const verticalScrolling = !props.forPrint && !internal_cjs.getIsHeightAuto(options);
1207
+ const stickyHeaderDates = !props.forPrint && internal_cjs.getStickyHeaderDates(options);
1208
+ const stickyFooterScrollbar = !props.forPrint && internal_cjs.getStickyFooterScrollbar(options);
1209
+ /* table positions */
1210
+ const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
1211
+ state.scrollerWidth);
1212
+ this.slotWidth = slotWidth;
1213
+ return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
1214
+ const enableNowIndicator = // TODO: DRY
1215
+ options.nowIndicator &&
1216
+ slotWidth != null &&
1217
+ internal_cjs.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
1218
+ return (preact_cjs.createElement(internal_cjs.ViewContainer, { viewSpec: context.viewSpec, elClasses: [
1219
+ 'fc-timeline-view',
1220
+ 'fc-flex-column',
1221
+ 'fc-border',
1222
+ ] },
1223
+ preact_cjs.createElement(internal_cjs.Scroller, { horizontal: true, hideScrollbars: true, elClassNames: [
1224
+ 'fc-timeline-header',
1225
+ 'fc-rowgroup',
1226
+ stickyHeaderDates ? 'fc-sticky-header' : '',
1227
+ ], ref: this.headerScrollerRef },
1228
+ preact_cjs.createElement("div", { className: 'fc-rel fc-content-box' // origin for now-indicator
1229
+ , style: {
1230
+ width: canvasWidth,
1231
+ paddingLeft: state.leftScrollbarWidth,
1232
+ paddingRight: state.rightScrollbarWidth,
1233
+ } },
1234
+ preact_cjs.createElement("div", null, cellRows.map((cells, rowLevel) => {
1235
+ const isLast = rowLevel === cellRows.length - 1;
1236
+ return (preact_cjs.createElement(TimelineHeaderRow, { key: rowLevel, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, isLastRow: isLast, cells: cells, slotWidth: slotWidth, innerWidthRef: isLast ? this.handleHeaderSlotInnerWidth : undefined }));
1237
+ })),
1238
+ enableNowIndicator && (
1239
+ // TODO: make this positioned WITHIN padding?
1240
+ preact_cjs.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
1241
+ preact_cjs.createElement(internal_cjs.Scroller, { vertical: verticalScrolling, horizontal: true, elClassNames: [
1242
+ 'fc-timeline-body',
1243
+ 'fc-rowgroup',
1244
+ verticalScrolling ? 'fc-liquid' : '',
1245
+ ], ref: this.bodyScrollerRef, widthRef: this.handleScrollerWidth, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth },
1246
+ preact_cjs.createElement("div", { className: "fc-rel fc-grow", style: {
1247
+ width: canvasWidth,
1248
+ }, ref: this.handeBodyEl },
1249
+ preact_cjs.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
1250
+ // ref
1251
+ innerWidthRef: this.handleBodySlotInnerWidth,
1252
+ // dimensions
1253
+ slotWidth: slotWidth }),
1254
+ preact_cjs.createElement(TimelineLane, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, nextDayThreshold: options.nextDayThreshold, eventStore: props.eventStore, eventUiBases: props.eventUiBases, businessHours: props.businessHours, dateSelection: props.dateSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection, slotWidth: slotWidth }),
1255
+ enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
1256
+ stickyFooterScrollbar && (preact_cjs.createElement(internal_cjs.Scroller, { ref: this.footerScrollerRef, horizontal: true },
1257
+ preact_cjs.createElement("div", { style: { width: canvasWidth } })))));
1258
+ }));
1259
+ }
1260
+ // Lifecycle
1261
+ // -----------------------------------------------------------------------------------------------
1262
+ componentDidMount() {
1263
+ const { context } = this;
1264
+ const { options } = context;
1265
+ const ScrollerSyncer = internal_cjs.getScrollerSyncerClass(this.context.pluginHooks);
1266
+ this.syncedScroller = new ScrollerSyncer(true); // horizontal=true
1267
+ this.updateSyncedScroller();
1268
+ context.emitter.on('_timeScrollRequest', this.timeScrollResponder.handleScroll);
1269
+ this.timeScrollResponder.handleScroll(options.scrollTime);
1270
+ }
1271
+ componentDidUpdate(prevProps) {
1272
+ const { options } = this.context;
1273
+ this.updateSyncedScroller();
1274
+ if (prevProps.dateProfile !== this.props.dateProfile && options.scrollTimeReset) {
1275
+ this.timeScrollResponder.handleScroll(options.scrollTime);
1276
+ }
1277
+ else {
1278
+ this.timeScrollResponder.drain();
1279
+ }
1280
+ }
1281
+ componentWillUnmount() {
1282
+ this.syncedScroller.destroy();
1283
+ this.context.emitter.off('_timeScrollRequest', this.timeScrollResponder.handleScroll);
1284
+ }
1285
+ // Scrolling
1286
+ // -----------------------------------------------------------------------------------------------
1287
+ updateSyncedScroller() {
1288
+ this.syncedScroller.handleChildren([
1289
+ this.headerScrollerRef.current,
1290
+ this.bodyScrollerRef.current,
1291
+ this.footerScrollerRef.current
1292
+ ], this.context.isRtl);
1293
+ }
1141
1294
  queryHit(positionLeft, positionTop, elWidth, elHeight) {
1142
- let slats = this.slatsRef.current;
1143
- let slatHit = slats.positionToHit(positionLeft);
1144
- if (slatHit) {
1295
+ const { props, context, tDateProfile, slotWidth } = this;
1296
+ const { dateEnv } = context;
1297
+ if (slotWidth) {
1298
+ const x = context.isRtl ? elWidth - positionLeft : positionLeft;
1299
+ const slatIndex = Math.floor(x / slotWidth);
1300
+ const slatX = slatIndex * slotWidth;
1301
+ const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
1302
+ const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
1303
+ let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], internal_cjs.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
1304
+ let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
1305
+ // TODO: generalize this coord stuff to TimeGrid?
1306
+ let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
1307
+ let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
1308
+ let endCoord = startCoord + snapWidth;
1309
+ let left, right;
1310
+ if (context.isRtl) {
1311
+ left = elWidth - endCoord;
1312
+ right = elWidth - startCoord;
1313
+ }
1314
+ else {
1315
+ left = startCoord;
1316
+ right = endCoord;
1317
+ }
1145
1318
  return {
1146
- dateProfile: this.props.dateProfile,
1147
- dateSpan: slatHit.dateSpan,
1319
+ dateProfile: props.dateProfile,
1320
+ dateSpan: {
1321
+ range: { start: startDate, end: endDate },
1322
+ allDay: !tDateProfile.isTimeScale,
1323
+ },
1148
1324
  rect: {
1149
- left: slatHit.left,
1150
- right: slatHit.right,
1325
+ left,
1326
+ right,
1151
1327
  top: 0,
1152
1328
  bottom: elHeight,
1153
1329
  },
1154
- dayEl: slatHit.dayEl,
1330
+ // HACK. TODO: This is expensive to do every hit-query
1331
+ dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
1155
1332
  layer: 0,
1156
1333
  };
1157
1334
  }
@@ -1159,100 +1336,18 @@ class TimelineGrid extends internal_cjs.DateComponent {
1159
1336
  }
1160
1337
  }
1161
1338
 
1162
- class TimelineView extends internal_cjs.DateComponent {
1163
- constructor() {
1164
- super(...arguments);
1165
- this.buildTimelineDateProfile = internal_cjs.memoize(buildTimelineDateProfile);
1166
- this.scrollGridRef = preact_cjs.createRef();
1167
- this.state = {
1168
- slatCoords: null,
1169
- slotCushionMaxWidth: null,
1170
- };
1171
- this.handleSlatCoords = (slatCoords) => {
1172
- this.setState({ slatCoords });
1173
- };
1174
- this.handleScrollLeftRequest = (scrollLeft) => {
1175
- let scrollGrid = this.scrollGridRef.current;
1176
- scrollGrid.forceScrollLeft(0, scrollLeft);
1177
- };
1178
- this.handleMaxCushionWidth = (slotCushionMaxWidth) => {
1179
- this.setState({
1180
- slotCushionMaxWidth: Math.ceil(slotCushionMaxWidth), // for less rerendering TODO: DRY
1181
- });
1182
- };
1183
- }
1184
- render() {
1185
- let { props, state, context } = this;
1186
- let { options } = context;
1187
- let stickyHeaderDates = !props.forPrint && internal_cjs.getStickyHeaderDates(options);
1188
- let stickyFooterScrollbar = !props.forPrint && internal_cjs.getStickyFooterScrollbar(options);
1189
- let tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
1190
- let { slotMinWidth } = options;
1191
- let slatCols = buildSlatCols(tDateProfile, slotMinWidth || this.computeFallbackSlotMinWidth(tDateProfile));
1192
- let sections = [
1193
- {
1194
- type: 'header',
1195
- key: 'header',
1196
- isSticky: stickyHeaderDates,
1197
- chunks: [{
1198
- key: 'timeline',
1199
- content: (contentArg) => (preact_cjs.createElement(TimelineHeader, { dateProfile: props.dateProfile, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, tableMinWidth: contentArg.tableMinWidth, tableColGroupNode: contentArg.tableColGroupNode, tDateProfile: tDateProfile, slatCoords: state.slatCoords, onMaxCushionWidth: slotMinWidth ? null : this.handleMaxCushionWidth })),
1200
- }],
1201
- },
1202
- {
1203
- type: 'body',
1204
- key: 'body',
1205
- liquid: true,
1206
- chunks: [{
1207
- key: 'timeline',
1208
- content: (contentArg) => (preact_cjs.createElement(TimelineGrid, Object.assign({}, props, { clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, tableMinWidth: contentArg.tableMinWidth, tableColGroupNode: contentArg.tableColGroupNode, tDateProfile: tDateProfile, onSlatCoords: this.handleSlatCoords, onScrollLeftRequest: this.handleScrollLeftRequest }))),
1209
- }],
1210
- },
1211
- ];
1212
- if (stickyFooterScrollbar) {
1213
- sections.push({
1214
- type: 'footer',
1215
- key: 'footer',
1216
- isSticky: true,
1217
- chunks: [{
1218
- key: 'timeline',
1219
- content: internal_cjs.renderScrollShim,
1220
- }],
1221
- });
1222
- }
1223
- return (preact_cjs.createElement(internal_cjs.ViewContainer, { elClasses: [
1224
- 'fc-timeline',
1225
- options.eventOverlap === false ?
1226
- 'fc-timeline-overlap-disabled' :
1227
- '',
1228
- ], viewSpec: context.viewSpec },
1229
- preact_cjs.createElement(internal_cjs$1.ScrollGrid, { ref: this.scrollGridRef, liquid: !props.isHeightAuto && !props.forPrint, forPrint: props.forPrint, collapsibleWidth: false, colGroups: [
1230
- { cols: slatCols },
1231
- ], sections: sections })));
1232
- }
1233
- computeFallbackSlotMinWidth(tDateProfile) {
1234
- return Math.max(30, ((this.state.slotCushionMaxWidth || 0) / tDateProfile.slotsPerLabel));
1235
- }
1236
- }
1237
- function buildSlatCols(tDateProfile, slotMinWidth) {
1238
- return [{
1239
- span: tDateProfile.slotCnt,
1240
- minWidth: slotMinWidth || 1, // needs to be a non-zero number to trigger horizontal scrollbars!??????
1241
- }];
1242
- }
1243
-
1244
- var css_248z = ".fc .fc-timeline-body{min-height:100%;position:relative;z-index:1}.fc .fc-timeline-slots{bottom:0;position:absolute;top:0;z-index:1}.fc .fc-timeline-slots>table{height:100%}.fc .fc-timeline-slot-minor{border-style:dotted}.fc .fc-timeline-slot-frame{align-items:center;display:flex;justify-content:center}.fc .fc-timeline-header-row-chrono .fc-timeline-slot-frame{justify-content:flex-start}.fc .fc-timeline-header-row:last-child .fc-timeline-slot-frame{overflow:hidden}.fc .fc-timeline-slot-cushion{padding:4px 5px;white-space:nowrap}.fc-direction-ltr .fc-timeline-slot{border-right:0!important}.fc-direction-rtl .fc-timeline-slot{border-left:0!important}.fc .fc-timeline-now-indicator-container{bottom:0;left:0;position:absolute;right:0;top:0;width:0;z-index:4}.fc .fc-timeline-now-indicator-arrow,.fc .fc-timeline-now-indicator-line{border-color:var(--fc-now-indicator-color);border-style:solid;position:absolute;top:0}.fc .fc-timeline-now-indicator-arrow{border-left-color:transparent;border-right-color:transparent;border-width:6px 5px 0;margin:0 -6px}.fc .fc-timeline-now-indicator-line{border-width:0 0 0 1px;bottom:0;margin:0 -1px}.fc .fc-timeline-events{position:relative;width:0;z-index:3}.fc .fc-timeline-event-harness,.fc .fc-timeline-more-link{position:absolute;top:0}.fc-timeline-event{z-index:1}.fc-timeline-event.fc-event-mirror{z-index:2}.fc-timeline-event{align-items:center;border-radius:0;display:flex;font-size:var(--fc-small-font-size);margin-bottom:1px;padding:2px 1px;position:relative}.fc-timeline-event .fc-event-main{flex-grow:1;flex-shrink:1;min-width: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;white-space:nowrap}.fc-direction-ltr .fc-timeline-event.fc-event-end,.fc-direction-ltr .fc-timeline-more-link{margin-right:1px}.fc-direction-rtl .fc-timeline-event.fc-event-end,.fc-direction-rtl .fc-timeline-more-link{margin-left:1px}.fc-timeline-overlap-disabled .fc-timeline-event{margin-bottom:0;padding-bottom:5px;padding-top:5px}.fc-timeline-event:not(.fc-event-end):after,.fc-timeline-event:not(.fc-event-start):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):before,.fc-direction-rtl .fc-timeline-event:not(.fc-event-end):after{border-left:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-end):after,.fc-direction-rtl .fc-timeline-event:not(.fc-event-start):before{border-right:0}.fc-timeline-more-link{background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;font-size:var(--fc-small-font-size);padding:1px}.fc-timeline-more-link-inner{display:inline-block;left:0;padding:2px;right:0}.fc .fc-timeline-bg{bottom:0;left:0;position:absolute;right:0;top:0;width:0;z-index:2}.fc .fc-timeline-bg .fc-non-business{z-index:1}.fc .fc-timeline-bg .fc-bg-event{z-index:2}.fc .fc-timeline-bg .fc-highlight{z-index:3}.fc .fc-timeline-bg-harness{bottom:0;position:absolute;top:0}";
1245
- internal_cjs.injectStyles(css_248z);
1246
-
1247
- exports.TimelineCoords = TimelineCoords;
1248
- exports.TimelineHeader = TimelineHeader;
1249
- exports.TimelineHeaderRows = TimelineHeaderRows;
1339
+ exports.TimelineHeaderRow = TimelineHeaderRow;
1250
1340
  exports.TimelineLane = TimelineLane;
1251
1341
  exports.TimelineLaneBg = TimelineLaneBg;
1252
1342
  exports.TimelineLaneSlicer = TimelineLaneSlicer;
1343
+ exports.TimelineNowIndicatorArrow = TimelineNowIndicatorArrow;
1344
+ exports.TimelineNowIndicatorLine = TimelineNowIndicatorLine;
1253
1345
  exports.TimelineSlats = TimelineSlats;
1254
1346
  exports.TimelineView = TimelineView;
1255
- exports.buildSlatCols = buildSlatCols;
1256
1347
  exports.buildTimelineDateProfile = buildTimelineDateProfile;
1348
+ exports.computeSlotWidth = computeSlotWidth;
1257
1349
  exports.coordToCss = coordToCss;
1258
1350
  exports.coordsToCss = coordsToCss;
1351
+ exports.createHorizontalStyle = createHorizontalStyle;
1352
+ exports.createVerticalStyle = createVerticalStyle;
1353
+ exports.timeToCoord = timeToCoord;