@fullcalendar/timeline 7.0.0-beta.4 → 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/LICENSE.md +2 -2
- package/index.cjs +1 -1
- package/index.global.js +348 -315
- package/index.global.min.js +2 -2
- package/index.js +1 -1
- package/internal.cjs +345 -312
- package/internal.d.ts +21 -18
- package/internal.js +344 -312
- package/package.json +4 -4
package/internal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { config, createFormatter, greatestDurationDenominator, asCleanDays, createDuration, wholeDivideDurations, asRoughMs, addDays, startOfDay, asRoughSeconds, asRoughMinutes, diffWholeDays, isInt, computeVisibleDayRange, padStart, BaseComponent, getDateMeta, ContentContainer, joinClassNames, getDayClassName, getSlotClassName, watchWidth, setRef, RefMap, afterSize,
|
|
1
|
+
import { config, createFormatter, greatestDurationDenominator, asCleanDays, createDuration, wholeDivideDurations, asRoughMs, addDays, startOfDay, asRoughSeconds, asRoughMinutes, diffWholeDays, isInt, computeVisibleDayRange, padStart, BaseComponent, memoize, getDateMeta, ContentContainer, joinClassNames, getDayClassName, getSlotClassName, watchWidth, setRef, RefMap, afterSize, memoizeObjArg, buildNavLinkAttrs, watchSize, NowIndicatorContainer, Slicer, intersectRanges, addMs, StandardEvent, MoreLinkContainer, getEventRangeMeta, getEventKey, SegHierarchy, groupIntersectingSegs, watchHeight, sortEventSegs, buildEventRangeKey, BgEvent, renderFill, DateComponent, getIsHeightAuto, getStickyHeaderDates, getStickyFooterScrollbar, NowTimer, rangeContainsMarker, ViewContainer, Scroller, FooterScrollbar, Ruler, multiplyDuration, injectStyles } from '@fullcalendar/core/internal.js';
|
|
2
2
|
import { createRef, createElement, Fragment, Component } from '@fullcalendar/core/preact.js';
|
|
3
3
|
import { ScrollerSyncer } from '@fullcalendar/scrollgrid/internal.js';
|
|
4
4
|
|
|
@@ -443,6 +443,8 @@ function buildCellObject(date, text, rowUnit) {
|
|
|
443
443
|
class TimelineSlatCell extends BaseComponent {
|
|
444
444
|
constructor() {
|
|
445
445
|
super(...arguments);
|
|
446
|
+
// memo
|
|
447
|
+
this.getPublicDate = memoize((dateEnv, date) => dateEnv.toDate(date));
|
|
446
448
|
// ref
|
|
447
449
|
this.innerElRef = createRef();
|
|
448
450
|
}
|
|
@@ -451,7 +453,7 @@ class TimelineSlatCell extends BaseComponent {
|
|
|
451
453
|
let { dateEnv, options } = context;
|
|
452
454
|
let { date, tDateProfile, isEm } = props;
|
|
453
455
|
let dateMeta = getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
|
|
454
|
-
let renderProps = Object.assign(Object.assign({ date:
|
|
456
|
+
let renderProps = Object.assign(Object.assign({ date: this.getPublicDate(dateEnv, props.date) }, dateMeta), { view: context.viewApi });
|
|
455
457
|
return (createElement(ContentContainer, { tag: "div",
|
|
456
458
|
// fc-align-start shrinks width of InnerContent
|
|
457
459
|
// TODO: document this semantic className fc-timeline-slot-em
|
|
@@ -465,7 +467,11 @@ class TimelineSlatCell extends BaseComponent {
|
|
|
465
467
|
omitTime: !tDateProfile.isTimeScale,
|
|
466
468
|
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
|
|
467
469
|
width: props.width,
|
|
468
|
-
}, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-cell-inner',
|
|
470
|
+
}, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-cell-inner', style: {
|
|
471
|
+
// HACK for Safari 16.4,
|
|
472
|
+
// which can't use ResizeObserver on elements with natural width 0
|
|
473
|
+
minWidth: 1,
|
|
474
|
+
}, elRef: this.innerElRef }))));
|
|
469
475
|
}
|
|
470
476
|
componentDidMount() {
|
|
471
477
|
const innerEl = this.innerElRef.current;
|
|
@@ -500,7 +506,7 @@ class TimelineSlats extends BaseComponent {
|
|
|
500
506
|
let { tDateProfile, slotWidth } = props;
|
|
501
507
|
let { slotDates, isWeekStarts } = tDateProfile;
|
|
502
508
|
let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
|
|
503
|
-
return (createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-
|
|
509
|
+
return (createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-flex-row fc-fill", style: { height: props.height } }, slotDates.map((slotDate, i) => {
|
|
504
510
|
let key = slotDate.toISOString();
|
|
505
511
|
return (createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isEm: isWeekStarts[i], isDay: isDay, borderStart: Boolean(i),
|
|
506
512
|
// ref
|
|
@@ -511,50 +517,132 @@ class TimelineSlats extends BaseComponent {
|
|
|
511
517
|
}
|
|
512
518
|
}
|
|
513
519
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
//
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
return 0;
|
|
520
|
+
class TimelineHeaderCell extends BaseComponent {
|
|
521
|
+
constructor() {
|
|
522
|
+
super(...arguments);
|
|
523
|
+
// memo
|
|
524
|
+
this.refineRenderProps = memoizeObjArg(refineRenderProps);
|
|
525
|
+
// ref
|
|
526
|
+
this.innerElRef = createRef();
|
|
522
527
|
}
|
|
523
|
-
|
|
524
|
-
|
|
528
|
+
render() {
|
|
529
|
+
let { props, context } = this;
|
|
530
|
+
let { dateEnv, options } = context;
|
|
531
|
+
let { cell, dateProfile, tDateProfile } = props;
|
|
532
|
+
// the cell.rowUnit is f'd
|
|
533
|
+
// giving 'month' for a 3-day view
|
|
534
|
+
// workaround: to infer day, do NOT time
|
|
535
|
+
let dateMeta = getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
|
|
536
|
+
let renderProps = this.refineRenderProps({
|
|
537
|
+
level: props.rowLevel,
|
|
538
|
+
dateMarker: cell.date,
|
|
539
|
+
text: cell.text,
|
|
540
|
+
dateEnv: context.dateEnv,
|
|
541
|
+
viewApi: context.viewApi,
|
|
542
|
+
});
|
|
543
|
+
let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
|
|
544
|
+
return (createElement(ContentContainer, { tag: "div", className: joinClassNames('fc-timeline-slot-label fc-timeline-slot', cell.isWeekStart && 'fc-timeline-slot-em', // TODO: document this semantic className
|
|
545
|
+
'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
|
|
546
|
+
// TODO: so slot classnames for week/month/bigger. see note above about rowUnit
|
|
547
|
+
cell.rowUnit === 'time' ?
|
|
548
|
+
getSlotClassName(dateMeta) :
|
|
549
|
+
getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
|
|
550
|
+
omitTime: !tDateProfile.isTimeScale,
|
|
551
|
+
omitTimeZoneOffset: true,
|
|
552
|
+
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
|
|
553
|
+
width: props.slotWidth != null
|
|
554
|
+
? props.slotWidth * cell.colspan
|
|
555
|
+
: undefined,
|
|
556
|
+
}, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (createElement(InnerContent, { tag: 'div', attrs: isNavLink
|
|
557
|
+
// not tabbable because parent is aria-hidden
|
|
558
|
+
? buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
|
|
559
|
+
: {} // don't bother with aria-hidden because parent already hidden
|
|
560
|
+
, className: joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
|
|
525
561
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
562
|
+
componentDidMount() {
|
|
563
|
+
const { props } = this;
|
|
564
|
+
const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
|
|
565
|
+
this.detachSize = watchSize(innerEl, (width, height) => {
|
|
566
|
+
setRef(props.innerWidthRef, width);
|
|
567
|
+
setRef(props.innerHeightRef, height);
|
|
568
|
+
// HACK for sticky-centering
|
|
569
|
+
innerEl.style.left = innerEl.style.right =
|
|
570
|
+
(props.isCentered && props.isSticky)
|
|
571
|
+
? `calc(50% - ${width / 2}px)`
|
|
572
|
+
: '';
|
|
573
|
+
});
|
|
530
574
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
575
|
+
componentWillUnmount() {
|
|
576
|
+
const { props } = this;
|
|
577
|
+
this.detachSize();
|
|
578
|
+
setRef(props.innerWidthRef, null);
|
|
579
|
+
setRef(props.innerHeightRef, null);
|
|
535
580
|
}
|
|
536
|
-
return snapCoverage;
|
|
537
581
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (!hcoord) {
|
|
543
|
-
return {};
|
|
544
|
-
}
|
|
545
|
-
if (isRtl) {
|
|
546
|
-
return { right: hcoord.start, width: hcoord.size };
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
return { left: hcoord.start, width: hcoord.size };
|
|
550
|
-
}
|
|
582
|
+
// Utils
|
|
583
|
+
// -------------------------------------------------------------------------------------------------
|
|
584
|
+
function renderInnerContent(renderProps) {
|
|
585
|
+
return renderProps.text;
|
|
551
586
|
}
|
|
552
|
-
function
|
|
553
|
-
|
|
554
|
-
|
|
587
|
+
function refineRenderProps(input) {
|
|
588
|
+
return {
|
|
589
|
+
level: input.level,
|
|
590
|
+
date: input.dateEnv.toDate(input.dateMarker),
|
|
591
|
+
view: input.viewApi,
|
|
592
|
+
text: input.text,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
class TimelineHeaderRow extends BaseComponent {
|
|
597
|
+
constructor() {
|
|
598
|
+
super(...arguments);
|
|
599
|
+
// refs
|
|
600
|
+
this.innerWidthRefMap = new RefMap(() => {
|
|
601
|
+
afterSize(this.handleInnerWidths);
|
|
602
|
+
});
|
|
603
|
+
this.innerHeightRefMap = new RefMap(() => {
|
|
604
|
+
afterSize(this.handleInnerHeights);
|
|
605
|
+
});
|
|
606
|
+
this.handleInnerWidths = () => {
|
|
607
|
+
const innerWidthMap = this.innerWidthRefMap.current;
|
|
608
|
+
let max = 0;
|
|
609
|
+
for (const innerWidth of innerWidthMap.values()) {
|
|
610
|
+
max = Math.max(max, innerWidth);
|
|
611
|
+
}
|
|
612
|
+
// TODO: ensure not equal?
|
|
613
|
+
setRef(this.props.innerWidthRef, max);
|
|
614
|
+
};
|
|
615
|
+
this.handleInnerHeights = () => {
|
|
616
|
+
const innerHeightMap = this.innerHeightRefMap.current;
|
|
617
|
+
let max = 0;
|
|
618
|
+
for (const innerHeight of innerHeightMap.values()) {
|
|
619
|
+
max = Math.max(max, innerHeight);
|
|
620
|
+
}
|
|
621
|
+
// TODO: ensure not equal?
|
|
622
|
+
setRef(this.props.innerHeighRef, max);
|
|
623
|
+
};
|
|
555
624
|
}
|
|
556
|
-
|
|
557
|
-
|
|
625
|
+
render() {
|
|
626
|
+
const { props, innerWidthRefMap, innerHeightRefMap } = this;
|
|
627
|
+
const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
|
|
628
|
+
const isSticky = !props.isLastRow;
|
|
629
|
+
return (createElement("div", { className: joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
|
|
630
|
+
!props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
|
|
631
|
+
// TODO: make this part of the cell obj?
|
|
632
|
+
// TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
|
|
633
|
+
// TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
|
|
634
|
+
// TODO: use rowUnit to key the Row itself?
|
|
635
|
+
const key = cell.rowUnit + ':' + cell.date.toISOString();
|
|
636
|
+
return (createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isCentered: isCentered, isSticky: isSticky, borderStart: Boolean(cellI),
|
|
637
|
+
// refs
|
|
638
|
+
innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
|
|
639
|
+
// dimensions
|
|
640
|
+
slotWidth: props.slotWidth }));
|
|
641
|
+
})));
|
|
642
|
+
}
|
|
643
|
+
componentWillUnmount() {
|
|
644
|
+
setRef(this.props.innerWidthRef, null);
|
|
645
|
+
setRef(this.props.innerHeighRef, null);
|
|
558
646
|
}
|
|
559
647
|
}
|
|
560
648
|
|
|
@@ -609,13 +697,38 @@ time, dateEnv, dateProfile, tDateProfile, slowWidth) {
|
|
|
609
697
|
}
|
|
610
698
|
function dateToCoord(// pixels
|
|
611
699
|
date, dateEnv, tDateProfile, slotWidth) {
|
|
612
|
-
let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
|
|
700
|
+
let snapCoverage = computeDateSnapCoverage$1(date, tDateProfile, dateEnv);
|
|
613
701
|
let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
|
|
614
702
|
return slotCoverage * slotWidth;
|
|
615
703
|
}
|
|
616
704
|
/*
|
|
617
705
|
returned value is between 0 and the number of snaps
|
|
618
706
|
*/
|
|
707
|
+
function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
|
|
708
|
+
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
|
|
709
|
+
if (snapDiff < 0) {
|
|
710
|
+
return 0;
|
|
711
|
+
}
|
|
712
|
+
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
|
|
713
|
+
return tDateProfile.snapCnt;
|
|
714
|
+
}
|
|
715
|
+
let snapDiffInt = Math.floor(snapDiff);
|
|
716
|
+
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
|
|
717
|
+
if (isInt(snapCoverage)) { // not an in-between value
|
|
718
|
+
snapCoverage += snapDiff - snapDiffInt; // add the remainder
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
// a fractional value, meaning the date is not visible
|
|
722
|
+
// always round up in this case. works for start AND end dates in a range.
|
|
723
|
+
snapCoverage = Math.ceil(snapCoverage);
|
|
724
|
+
}
|
|
725
|
+
return snapCoverage;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/*
|
|
729
|
+
TODO: rename this file!
|
|
730
|
+
*/
|
|
731
|
+
// returned value is between 0 and the number of snaps
|
|
619
732
|
function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
|
|
620
733
|
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
|
|
621
734
|
if (snapDiff < 0) {
|
|
@@ -634,7 +747,107 @@ function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
|
|
|
634
747
|
// always round up in this case. works for start AND end dates in a range.
|
|
635
748
|
snapCoverage = Math.ceil(snapCoverage);
|
|
636
749
|
}
|
|
637
|
-
return snapCoverage;
|
|
750
|
+
return snapCoverage;
|
|
751
|
+
}
|
|
752
|
+
/*
|
|
753
|
+
TODO: DRY up with elsewhere?
|
|
754
|
+
*/
|
|
755
|
+
function horizontalsToCss(hcoord, isRtl) {
|
|
756
|
+
if (!hcoord) {
|
|
757
|
+
return {};
|
|
758
|
+
}
|
|
759
|
+
if (isRtl) {
|
|
760
|
+
return { right: hcoord.start, width: hcoord.size };
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
return { left: hcoord.start, width: hcoord.size };
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
function horizontalCoordToCss(start, isRtl) {
|
|
767
|
+
if (isRtl) {
|
|
768
|
+
return { right: start };
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
return { left: start };
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
class TimelineNowIndicatorLine extends BaseComponent {
|
|
776
|
+
render() {
|
|
777
|
+
const { props, context } = this;
|
|
778
|
+
return (createElement("div", { className: "fc-timeline-now-indicator-container" },
|
|
779
|
+
createElement(NowIndicatorContainer // TODO: make separate component?
|
|
780
|
+
, { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
|
|
781
|
+
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
|
|
782
|
+
: {}, isAxis: false, date: props.nowDate })));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
class TimelineNowIndicatorArrow extends BaseComponent {
|
|
787
|
+
render() {
|
|
788
|
+
const { props, context } = this;
|
|
789
|
+
return (createElement("div", { className: "fc-timeline-now-indicator-container" },
|
|
790
|
+
createElement(NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
|
|
791
|
+
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
|
|
792
|
+
: {}, isAxis: true, date: props.nowDate })));
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function getTimelineSlotEl(parentEl, index) {
|
|
797
|
+
return parentEl.querySelectorAll('.fc-timeline-slot')[index];
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
class TimelineLaneSlicer extends Slicer {
|
|
801
|
+
sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
|
|
802
|
+
let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
|
|
803
|
+
let segs = [];
|
|
804
|
+
// protect against when the span is entirely in an invalid date region
|
|
805
|
+
if (computeDateSnapCoverage(normalRange.start, tDateProfile, dateEnv)
|
|
806
|
+
< computeDateSnapCoverage(normalRange.end, tDateProfile, dateEnv)) {
|
|
807
|
+
// intersect the footprint's range with the grid's range
|
|
808
|
+
let slicedRange = intersectRanges(normalRange, tDateProfile.normalizedRange);
|
|
809
|
+
if (slicedRange) {
|
|
810
|
+
segs.push({
|
|
811
|
+
startDate: slicedRange.start,
|
|
812
|
+
endDate: slicedRange.end,
|
|
813
|
+
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
|
|
814
|
+
&& isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
|
|
815
|
+
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
|
|
816
|
+
&& isValidDate(addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return segs;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const DEFAULT_TIME_FORMAT = createFormatter({
|
|
825
|
+
hour: 'numeric',
|
|
826
|
+
minute: '2-digit',
|
|
827
|
+
omitZeroMinute: true,
|
|
828
|
+
meridiem: 'narrow',
|
|
829
|
+
});
|
|
830
|
+
class TimelineEvent extends BaseComponent {
|
|
831
|
+
render() {
|
|
832
|
+
let { props, context } = this;
|
|
833
|
+
let { options } = context;
|
|
834
|
+
return (createElement(StandardEvent, Object.assign({}, props, { className: joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
|
|
835
|
+
&& 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
class TimelineLaneMoreLink extends BaseComponent {
|
|
840
|
+
render() {
|
|
841
|
+
let { props } = this;
|
|
842
|
+
let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
|
|
843
|
+
let dateSpanProps = resourceId ? { resourceId } : {};
|
|
844
|
+
return (createElement(MoreLinkContainer, { className: 'fc-timeline-more-link', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, popoverContent: () => (createElement(Fragment, null, hiddenSegs.map((seg) => {
|
|
845
|
+
let { eventRange } = seg;
|
|
846
|
+
let instanceId = eventRange.instance.instanceId;
|
|
847
|
+
return (createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
|
|
848
|
+
createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection }, getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
|
|
849
|
+
}))) }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
|
|
850
|
+
}
|
|
638
851
|
}
|
|
639
852
|
|
|
640
853
|
function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
|
|
@@ -701,83 +914,6 @@ hiddenGroupHeights, strictOrder, maxDepth) {
|
|
|
701
914
|
];
|
|
702
915
|
}
|
|
703
916
|
|
|
704
|
-
class TimelineLaneBg extends BaseComponent {
|
|
705
|
-
render() {
|
|
706
|
-
let { props } = this;
|
|
707
|
-
let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
|
|
708
|
-
return (createElement(Fragment, null,
|
|
709
|
-
this.renderSegs(props.businessHourSegs || [], 'non-business'),
|
|
710
|
-
this.renderSegs(props.bgEventSegs || [], 'bg-event'),
|
|
711
|
-
this.renderSegs(highlightSeg, 'highlight')));
|
|
712
|
-
}
|
|
713
|
-
renderSegs(segs, fillType) {
|
|
714
|
-
let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
|
|
715
|
-
let { dateEnv, isRtl } = this.context;
|
|
716
|
-
return (createElement(Fragment, null, segs.map((seg) => {
|
|
717
|
-
let hStyle; // TODO
|
|
718
|
-
if (slotWidth != null) {
|
|
719
|
-
let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
|
|
720
|
-
hStyle = horizontalsToCss(segHorizontal, isRtl);
|
|
721
|
-
}
|
|
722
|
-
return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
|
|
723
|
-
createElement(BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (renderFill(fillType))));
|
|
724
|
-
})));
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
class TimelineLaneSlicer extends Slicer {
|
|
729
|
-
sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
|
|
730
|
-
let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
|
|
731
|
-
let segs = [];
|
|
732
|
-
// protect against when the span is entirely in an invalid date region
|
|
733
|
-
if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
|
|
734
|
-
< computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
|
|
735
|
-
// intersect the footprint's range with the grid's range
|
|
736
|
-
let slicedRange = intersectRanges(normalRange, tDateProfile.normalizedRange);
|
|
737
|
-
if (slicedRange) {
|
|
738
|
-
segs.push({
|
|
739
|
-
startDate: slicedRange.start,
|
|
740
|
-
endDate: slicedRange.end,
|
|
741
|
-
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
|
|
742
|
-
&& isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
|
|
743
|
-
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
|
|
744
|
-
&& isValidDate(addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
return segs;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
const DEFAULT_TIME_FORMAT = createFormatter({
|
|
753
|
-
hour: 'numeric',
|
|
754
|
-
minute: '2-digit',
|
|
755
|
-
omitZeroMinute: true,
|
|
756
|
-
meridiem: 'narrow',
|
|
757
|
-
});
|
|
758
|
-
class TimelineEvent extends BaseComponent {
|
|
759
|
-
render() {
|
|
760
|
-
let { props, context } = this;
|
|
761
|
-
let { options } = context;
|
|
762
|
-
return (createElement(StandardEvent, Object.assign({}, props, { className: joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
|
|
763
|
-
&& 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
class TimelineLaneMoreLink extends BaseComponent {
|
|
768
|
-
render() {
|
|
769
|
-
let { props } = this;
|
|
770
|
-
let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
|
|
771
|
-
let dateSpanProps = resourceId ? { resourceId } : {};
|
|
772
|
-
return (createElement(MoreLinkContainer, { className: 'fc-timeline-more-link', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, popoverContent: () => (createElement(Fragment, null, hiddenSegs.map((seg) => {
|
|
773
|
-
let { eventRange } = seg;
|
|
774
|
-
let instanceId = eventRange.instance.instanceId;
|
|
775
|
-
return (createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
|
|
776
|
-
createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection }, getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
|
|
777
|
-
}))) }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
917
|
/*
|
|
782
918
|
TODO: make DRY with other Event Harnesses
|
|
783
919
|
*/
|
|
@@ -803,10 +939,7 @@ class TimelineEventHarness extends Component {
|
|
|
803
939
|
}
|
|
804
940
|
}
|
|
805
941
|
|
|
806
|
-
|
|
807
|
-
TODO: split TimelineLaneBg and TimelineLaneFg?
|
|
808
|
-
*/
|
|
809
|
-
class TimelineLane extends BaseComponent {
|
|
942
|
+
class TimelineFg extends BaseComponent {
|
|
810
943
|
constructor() {
|
|
811
944
|
super(...arguments);
|
|
812
945
|
// memo
|
|
@@ -818,8 +951,6 @@ class TimelineLane extends BaseComponent {
|
|
|
818
951
|
this.moreLinkHeightRefMap = new RefMap(() => {
|
|
819
952
|
afterSize(this.handleMoreLinkHeights);
|
|
820
953
|
});
|
|
821
|
-
// internal
|
|
822
|
-
this.slicer = new TimelineLaneSlicer();
|
|
823
954
|
this.handleMoreLinkHeights = () => {
|
|
824
955
|
this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
|
|
825
956
|
};
|
|
@@ -833,39 +964,30 @@ class TimelineLane extends BaseComponent {
|
|
|
833
964
|
render() {
|
|
834
965
|
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
|
|
835
966
|
let { options } = context;
|
|
836
|
-
let {
|
|
837
|
-
let
|
|
838
|
-
|
|
839
|
-
let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
|
|
840
|
-
(slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
|
|
967
|
+
let { tDateProfile } = props;
|
|
968
|
+
let mirrorSegs = (props.eventDrag ? props.eventDrag.segs : null) ||
|
|
969
|
+
(props.eventResize ? props.eventResize.segs : null) ||
|
|
841
970
|
[];
|
|
842
|
-
let fgSegs = this.sortEventSegs(
|
|
971
|
+
let fgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
|
|
843
972
|
let fgSegHorizontals = props.slotWidth != null
|
|
844
973
|
? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
|
|
845
974
|
: {};
|
|
846
975
|
let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
|
|
976
|
+
this.totalHeight = totalHeight;
|
|
847
977
|
let forcedInvisibleMap = // TODO: more convenient/DRY
|
|
848
|
-
(
|
|
849
|
-
(
|
|
978
|
+
(props.eventDrag ? props.eventDrag.affectedInstances : null) ||
|
|
979
|
+
(props.eventResize ? props.eventResize.affectedInstances : null) ||
|
|
850
980
|
{};
|
|
851
|
-
return (createElement(
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
false, // isResizing
|
|
862
|
-
false),
|
|
863
|
-
this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
|
|
864
|
-
? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
|
|
865
|
-
: {}, fgSegTops, {}, // forcedInvisibleMap
|
|
866
|
-
[], // hiddenGroups
|
|
867
|
-
new Map(), // hiddenGroupTops
|
|
868
|
-
Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
|
|
981
|
+
return (createElement("div", { className: 'fc-timeline-events fc-rel', style: { height: totalHeight } },
|
|
982
|
+
this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
|
|
983
|
+
false, // isResizing
|
|
984
|
+
false),
|
|
985
|
+
this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
|
|
986
|
+
? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
|
|
987
|
+
: {}, fgSegTops, {}, // forcedInvisibleMap
|
|
988
|
+
[], // hiddenGroups
|
|
989
|
+
new Map(), // hiddenGroupTops
|
|
990
|
+
Boolean(props.eventDrag), Boolean(props.eventResize), false)));
|
|
869
991
|
}
|
|
870
992
|
renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
|
|
871
993
|
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
|
|
@@ -887,155 +1009,43 @@ class TimelineLane extends BaseComponent {
|
|
|
887
1009
|
}, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
|
|
888
1010
|
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 }))))));
|
|
889
1011
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
constructor() {
|
|
894
|
-
super(...arguments);
|
|
895
|
-
// memo
|
|
896
|
-
this.refineRenderProps = memoizeObjArg(refineRenderProps);
|
|
897
|
-
// ref
|
|
898
|
-
this.innerElRef = createRef();
|
|
899
|
-
}
|
|
900
|
-
render() {
|
|
901
|
-
let { props, context } = this;
|
|
902
|
-
let { dateEnv, options } = context;
|
|
903
|
-
let { cell, dateProfile, tDateProfile } = props;
|
|
904
|
-
// the cell.rowUnit is f'd
|
|
905
|
-
// giving 'month' for a 3-day view
|
|
906
|
-
// workaround: to infer day, do NOT time
|
|
907
|
-
let dateMeta = getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
|
|
908
|
-
let renderProps = this.refineRenderProps({
|
|
909
|
-
level: props.rowLevel,
|
|
910
|
-
dateMarker: cell.date,
|
|
911
|
-
text: cell.text,
|
|
912
|
-
dateEnv: context.dateEnv,
|
|
913
|
-
viewApi: context.viewApi,
|
|
914
|
-
});
|
|
915
|
-
let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
|
|
916
|
-
return (createElement(ContentContainer, { tag: "div", className: joinClassNames('fc-timeline-slot-label fc-timeline-slot', cell.isWeekStart && 'fc-timeline-slot-em', // TODO: document this semantic className
|
|
917
|
-
'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
|
|
918
|
-
// TODO: so slot classnames for week/month/bigger. see note above about rowUnit
|
|
919
|
-
cell.rowUnit === 'time' ?
|
|
920
|
-
getSlotClassName(dateMeta) :
|
|
921
|
-
getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
|
|
922
|
-
omitTime: !tDateProfile.isTimeScale,
|
|
923
|
-
omitTimeZoneOffset: true,
|
|
924
|
-
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
|
|
925
|
-
width: props.slotWidth != null
|
|
926
|
-
? props.slotWidth * cell.colspan
|
|
927
|
-
: undefined,
|
|
928
|
-
}, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (createElement(InnerContent, { tag: 'div', attrs: isNavLink
|
|
929
|
-
// not tabbable because parent is aria-hidden
|
|
930
|
-
? buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
|
|
931
|
-
: {} // don't bother with aria-hidden because parent already hidden
|
|
932
|
-
, className: joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
|
|
933
|
-
}
|
|
934
|
-
componentDidMount() {
|
|
935
|
-
const { props } = this;
|
|
936
|
-
const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
|
|
937
|
-
this.detachSize = watchSize(innerEl, (width, height) => {
|
|
938
|
-
setRef(props.innerWidthRef, width);
|
|
939
|
-
setRef(props.innerHeightRef, height);
|
|
940
|
-
// HACK for sticky-centering
|
|
941
|
-
innerEl.style.left = innerEl.style.right =
|
|
942
|
-
(props.isCentered && props.isSticky)
|
|
943
|
-
? `calc(50% - ${width / 2}px)`
|
|
944
|
-
: '';
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
componentWillUnmount() {
|
|
948
|
-
const { props } = this;
|
|
949
|
-
this.detachSize();
|
|
950
|
-
setRef(props.innerWidthRef, null);
|
|
951
|
-
setRef(props.innerHeightRef, null);
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
// Utils
|
|
955
|
-
// -------------------------------------------------------------------------------------------------
|
|
956
|
-
function renderInnerContent(renderProps) {
|
|
957
|
-
return renderProps.text;
|
|
958
|
-
}
|
|
959
|
-
function refineRenderProps(input) {
|
|
960
|
-
return {
|
|
961
|
-
level: input.level,
|
|
962
|
-
date: input.dateEnv.toDate(input.dateMarker),
|
|
963
|
-
view: input.viewApi,
|
|
964
|
-
text: input.text,
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
class TimelineHeaderRow extends BaseComponent {
|
|
969
|
-
constructor() {
|
|
970
|
-
super(...arguments);
|
|
971
|
-
// refs
|
|
972
|
-
this.innerWidthRefMap = new RefMap(() => {
|
|
973
|
-
afterSize(this.handleInnerWidths);
|
|
974
|
-
});
|
|
975
|
-
this.innerHeightRefMap = new RefMap(() => {
|
|
976
|
-
afterSize(this.handleInnerHeights);
|
|
977
|
-
});
|
|
978
|
-
this.handleInnerWidths = () => {
|
|
979
|
-
const innerWidthMap = this.innerWidthRefMap.current;
|
|
980
|
-
let max = 0;
|
|
981
|
-
for (const innerWidth of innerWidthMap.values()) {
|
|
982
|
-
max = Math.max(max, innerWidth);
|
|
983
|
-
}
|
|
984
|
-
// TODO: ensure not equal?
|
|
985
|
-
setRef(this.props.innerWidthRef, max);
|
|
986
|
-
};
|
|
987
|
-
this.handleInnerHeights = () => {
|
|
988
|
-
const innerHeightMap = this.innerHeightRefMap.current;
|
|
989
|
-
let max = 0;
|
|
990
|
-
for (const innerHeight of innerHeightMap.values()) {
|
|
991
|
-
max = Math.max(max, innerHeight);
|
|
992
|
-
}
|
|
993
|
-
// TODO: ensure not equal?
|
|
994
|
-
setRef(this.props.innerHeighRef, max);
|
|
995
|
-
};
|
|
1012
|
+
/*
|
|
1013
|
+
componentDidMount(): void {
|
|
1014
|
+
// might want to do firedTotalHeight, but won't be ready on first render
|
|
996
1015
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
// TODO: make this part of the cell obj?
|
|
1004
|
-
// TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
|
|
1005
|
-
// TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
|
|
1006
|
-
// TODO: use rowUnit to key the Row itself?
|
|
1007
|
-
const key = cell.rowUnit + ':' + cell.date.toISOString();
|
|
1008
|
-
return (createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isCentered: isCentered, isSticky: isSticky, borderStart: Boolean(cellI),
|
|
1009
|
-
// refs
|
|
1010
|
-
innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
|
|
1011
|
-
// dimensions
|
|
1012
|
-
slotWidth: props.slotWidth }));
|
|
1013
|
-
})));
|
|
1016
|
+
*/
|
|
1017
|
+
componentDidUpdate() {
|
|
1018
|
+
if (this.totalHeight !== this.firedTotalHeight) {
|
|
1019
|
+
this.firedTotalHeight = this.totalHeight;
|
|
1020
|
+
setRef(this.props.heightRef, this.totalHeight);
|
|
1021
|
+
}
|
|
1014
1022
|
}
|
|
1015
1023
|
componentWillUnmount() {
|
|
1016
|
-
setRef(this.props.
|
|
1017
|
-
setRef(this.props.innerHeighRef, null);
|
|
1024
|
+
setRef(this.props.heightRef, null);
|
|
1018
1025
|
}
|
|
1019
1026
|
}
|
|
1020
1027
|
|
|
1021
|
-
class
|
|
1028
|
+
class TimelineBg extends BaseComponent {
|
|
1022
1029
|
render() {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1030
|
+
let { props } = this;
|
|
1031
|
+
let highlightSeg = [].concat(props.eventResizeSegs || [], props.dateSelectionSegs);
|
|
1032
|
+
return (createElement(Fragment, null,
|
|
1033
|
+
this.renderSegs(props.businessHourSegs || [], 'non-business'),
|
|
1034
|
+
this.renderSegs(props.bgEventSegs || [], 'bg-event'),
|
|
1035
|
+
this.renderSegs(highlightSeg, 'highlight')));
|
|
1029
1036
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1037
|
+
renderSegs(segs, fillType) {
|
|
1038
|
+
let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
|
|
1039
|
+
let { dateEnv, isRtl } = this.context;
|
|
1040
|
+
return (createElement(Fragment, null, segs.map((seg) => {
|
|
1041
|
+
let hStyle; // TODO
|
|
1042
|
+
if (slotWidth != null) {
|
|
1043
|
+
let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
|
|
1044
|
+
hStyle = horizontalsToCss(segHorizontal, isRtl);
|
|
1045
|
+
}
|
|
1046
|
+
return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
|
|
1047
|
+
createElement(BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (renderFill(fillType))));
|
|
1048
|
+
})));
|
|
1039
1049
|
}
|
|
1040
1050
|
}
|
|
1041
1051
|
|
|
@@ -1053,6 +1063,7 @@ class TimelineView extends DateComponent {
|
|
|
1053
1063
|
afterSize(this.handleSlotInnerWidths);
|
|
1054
1064
|
});
|
|
1055
1065
|
this.scrollTime = null;
|
|
1066
|
+
this.slicer = new TimelineLaneSlicer();
|
|
1056
1067
|
// Sizing
|
|
1057
1068
|
// -----------------------------------------------------------------------------------------------
|
|
1058
1069
|
this.handleBodySlotInnerWidth = (innerWidth) => {
|
|
@@ -1060,27 +1071,30 @@ class TimelineView extends DateComponent {
|
|
|
1060
1071
|
afterSize(this.handleSlotInnerWidths);
|
|
1061
1072
|
};
|
|
1062
1073
|
this.handleSlotInnerWidths = () => {
|
|
1063
|
-
const
|
|
1064
|
-
const
|
|
1065
|
-
if (
|
|
1066
|
-
|
|
1074
|
+
const headerSlotInnerWidth = this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1);
|
|
1075
|
+
const { bodySlotInnerWidth } = this;
|
|
1076
|
+
if (headerSlotInnerWidth != null && bodySlotInnerWidth != null) {
|
|
1077
|
+
const slotInnerWidth = Math.max(headerSlotInnerWidth, bodySlotInnerWidth);
|
|
1078
|
+
if (slotInnerWidth !== this.state.slotInnerWidth) {
|
|
1079
|
+
this.setState({ slotInnerWidth });
|
|
1080
|
+
}
|
|
1067
1081
|
}
|
|
1068
1082
|
};
|
|
1069
|
-
this.
|
|
1083
|
+
this.handleTotalWidth = (totalWidth) => {
|
|
1070
1084
|
this.setState({
|
|
1071
|
-
|
|
1085
|
+
totalWidth,
|
|
1072
1086
|
});
|
|
1073
1087
|
};
|
|
1074
|
-
this.
|
|
1088
|
+
this.handleClientWidth = (clientWidth) => {
|
|
1075
1089
|
this.setState({
|
|
1076
|
-
|
|
1090
|
+
clientWidth,
|
|
1077
1091
|
});
|
|
1078
1092
|
};
|
|
1079
1093
|
this.handleTimeScrollRequest = (scrollTime) => {
|
|
1080
1094
|
this.scrollTime = scrollTime;
|
|
1081
1095
|
this.applyTimeScroll();
|
|
1082
1096
|
};
|
|
1083
|
-
this.handleTimeScrollEnd = (
|
|
1097
|
+
this.handleTimeScrollEnd = (isUser) => {
|
|
1084
1098
|
if (isUser) {
|
|
1085
1099
|
this.scrollTime = null;
|
|
1086
1100
|
}
|
|
@@ -1100,6 +1114,10 @@ class TimelineView extends DateComponent {
|
|
|
1100
1114
|
render() {
|
|
1101
1115
|
const { props, state, context } = this;
|
|
1102
1116
|
const { options } = context;
|
|
1117
|
+
const { totalWidth, clientWidth } = state;
|
|
1118
|
+
const endScrollbarWidth = (totalWidth != null && clientWidth != null)
|
|
1119
|
+
? totalWidth - clientWidth
|
|
1120
|
+
: undefined;
|
|
1103
1121
|
/* date */
|
|
1104
1122
|
const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
|
|
1105
1123
|
const { cellRows } = tDateProfile;
|
|
@@ -1110,8 +1128,11 @@ class TimelineView extends DateComponent {
|
|
|
1110
1128
|
const stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(options);
|
|
1111
1129
|
/* table positions */
|
|
1112
1130
|
const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
|
|
1113
|
-
|
|
1131
|
+
clientWidth);
|
|
1114
1132
|
this.slotWidth = slotWidth;
|
|
1133
|
+
/* sliced */
|
|
1134
|
+
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...
|
|
1135
|
+
props.dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
|
|
1115
1136
|
return (createElement(NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
|
|
1116
1137
|
const enableNowIndicator = // TODO: DRY
|
|
1117
1138
|
options.nowIndicator &&
|
|
@@ -1131,18 +1152,30 @@ class TimelineView extends DateComponent {
|
|
|
1131
1152
|
return (createElement(TimelineHeaderRow, { key: rowLevel, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, isLastRow: isLast, cells: cells, slotWidth: slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowLevel) }));
|
|
1132
1153
|
}),
|
|
1133
1154
|
enableNowIndicator && (createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
|
|
1134
|
-
Boolean(
|
|
1135
|
-
createElement(Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars:
|
|
1155
|
+
Boolean(endScrollbarWidth) && (createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: endScrollbarWidth } }))),
|
|
1156
|
+
createElement(Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: stickyFooterScrollbar ||
|
|
1157
|
+
props.forPrint // prevents blank space in print-view on Safari
|
|
1158
|
+
, className: joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth },
|
|
1136
1159
|
createElement("div", { "aria-label": options.eventsHint, className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
|
|
1137
1160
|
createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
|
|
1138
1161
|
// ref
|
|
1139
1162
|
innerWidthRef: this.handleBodySlotInnerWidth,
|
|
1140
1163
|
// dimensions
|
|
1141
1164
|
slotWidth: slotWidth }),
|
|
1142
|
-
createElement(
|
|
1165
|
+
createElement(TimelineBg, { tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
|
|
1166
|
+
// content
|
|
1167
|
+
bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : null,
|
|
1168
|
+
// dimensions
|
|
1169
|
+
slotWidth: slotWidth }),
|
|
1170
|
+
createElement(TimelineFg, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
|
|
1171
|
+
// content
|
|
1172
|
+
fgEventSegs: slicedProps.fgEventSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
|
|
1173
|
+
// dimensions
|
|
1174
|
+
slotWidth: slotWidth }),
|
|
1175
|
+
createElement("div", { className: 'fc-timeline-lane-footer' }),
|
|
1143
1176
|
enableNowIndicator && (createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
|
|
1144
|
-
stickyFooterScrollbar && (createElement(
|
|
1145
|
-
|
|
1177
|
+
Boolean(stickyFooterScrollbar) && (createElement(FooterScrollbar, { isSticky: true, canvasWidth: canvasWidth, scrollerRef: this.footerScrollerRef })),
|
|
1178
|
+
createElement(Ruler, { widthRef: this.handleTotalWidth })));
|
|
1146
1179
|
}));
|
|
1147
1180
|
}
|
|
1148
1181
|
// Lifecycle
|
|
@@ -1227,8 +1260,7 @@ class TimelineView extends DateComponent {
|
|
|
1227
1260
|
top: 0,
|
|
1228
1261
|
bottom: elHeight,
|
|
1229
1262
|
},
|
|
1230
|
-
|
|
1231
|
-
dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
|
|
1263
|
+
getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
|
|
1232
1264
|
layer: 0,
|
|
1233
1265
|
};
|
|
1234
1266
|
}
|
|
@@ -1236,7 +1268,7 @@ class TimelineView extends DateComponent {
|
|
|
1236
1268
|
}
|
|
1237
1269
|
}
|
|
1238
1270
|
|
|
1239
|
-
var css_248z = ".fc-timeline-
|
|
1271
|
+
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}";
|
|
1240
1272
|
injectStyles(css_248z);
|
|
1241
1273
|
|
|
1242
|
-
export {
|
|
1274
|
+
export { TimelineBg, TimelineFg, TimelineHeaderRow, TimelineLaneSlicer, TimelineNowIndicatorArrow, TimelineNowIndicatorLine, TimelineSlats, TimelineView, buildTimelineDateProfile, computeSlotWidth, createHorizontalStyle, createVerticalStyle, getTimelineSlotEl, timeToCoord };
|