🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@fullcalendar/timeline

Package Overview
Dependencies
Maintainers
1
Versions
72
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fullcalendar/timeline - npm Package Compare versions

Comparing version
7.0.0-beta.4
to
7.0.0-beta.5
+53
cjs/index.cjs
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var core = require('@fullcalendar/core');
var premiumCommonPlugin = require('@fullcalendar/premium-common');
var internalCommon = require('./internal.cjs');
var internal = require('@fullcalendar/core/internal');
require('@fullcalendar/core/internal-classnames');
require('@fullcalendar/core/preact');
require('@fullcalendar/scrollgrid/internal');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var premiumCommonPlugin__default = /*#__PURE__*/_interopDefaultLegacy(premiumCommonPlugin);
const OPTION_REFINERS = {
timelineTopClass: internal.identity,
timelineBottomClass: internal.identity,
};
var index = core.createPlugin({
name: '@fullcalendar/timeline',
premiumReleaseDate: '2025-12-20',
deps: [premiumCommonPlugin__default["default"]],
initialView: 'timelineDay',
optionRefiners: OPTION_REFINERS,
views: {
timeline: {
component: internalCommon.TimelineView,
usesMinMaxTime: true,
eventResizableFromStart: true, // how is this consumed for TimelineView tho?
},
timelineDay: {
type: 'timeline',
duration: { days: 1 },
},
timelineWeek: {
type: 'timeline',
duration: { weeks: 1 },
},
timelineMonth: {
type: 'timeline',
duration: { months: 1 },
},
timelineYear: {
type: 'timeline',
duration: { years: 1 },
},
},
});
exports["default"] = index;
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var core = require('@fullcalendar/core');
var internal = require('@fullcalendar/core/internal');
var classNames = require('@fullcalendar/core/internal-classnames');
var preact = require('@fullcalendar/core/preact');
var internal$1 = require('@fullcalendar/scrollgrid/internal');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var classNames__default = /*#__PURE__*/_interopDefaultLegacy(classNames);
const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
const MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
internal.config.MAX_TIMELINE_SLOTS = 1000;
// potential nice values for slot-duration and interval-duration
const STOCK_SUB_DURATIONS = [
{ years: 1 },
{ months: 1 },
{ days: 1 },
{ hours: 1 },
{ minutes: 30 },
{ minutes: 15 },
{ minutes: 10 },
{ minutes: 5 },
{ minutes: 1 },
{ seconds: 30 },
{ seconds: 15 },
{ seconds: 10 },
{ seconds: 5 },
{ seconds: 1 },
{ milliseconds: 500 },
{ milliseconds: 100 },
{ milliseconds: 10 },
{ milliseconds: 1 },
];
function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
let tDateProfile = {
labelInterval: allOptions.slotHeaderInterval,
slotDuration: allOptions.slotDuration,
};
validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
let input = allOptions.slotHeaderFormat;
let rawFormats = Array.isArray(input) ? input :
(input != null) ? [input] :
computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
tDateProfile.headerFormats = rawFormats.map((rawFormat) => internal.createFormatter(rawFormat));
tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
let largeUnit = null;
if (!tDateProfile.isTimeScale) {
const slotUnit = internal.greatestDurationDenominator(tDateProfile.slotDuration).unit;
if (/year|month|week/.test(slotUnit)) {
largeUnit = slotUnit;
}
}
tDateProfile.largeUnit = largeUnit;
/*
console.log('label interval =', timelineView.labelInterval.humanize())
console.log('slot duration =', timelineView.slotDuration.humanize())
console.log('header formats =', timelineView.headerFormats)
console.log('isTimeScale', timelineView.isTimeScale)
console.log('largeUnit', timelineView.largeUnit)
*/
let rawSnapDuration = allOptions.snapDuration;
let snapDuration;
let snapsPerSlot;
if (rawSnapDuration) {
snapDuration = internal.createDuration(rawSnapDuration);
snapsPerSlot = internal.wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
// ^ TODO: warning if not whole?
}
if (snapsPerSlot == null) {
snapDuration = tDateProfile.slotDuration;
snapsPerSlot = 1;
}
tDateProfile.snapDuration = snapDuration;
tDateProfile.snapsPerSlot = snapsPerSlot;
// more...
let timeWindowMs = internal.asRoughMs(dateProfile.slotMaxTime) - internal.asRoughMs(dateProfile.slotMinTime);
// TODO: why not use normalizeRange!?
let normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
let normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
// apply slotMinTime/slotMaxTime
// TODO: View should be responsible.
if (tDateProfile.isTimeScale) {
normalizedStart = dateEnv.add(normalizedStart, dateProfile.slotMinTime);
normalizedEnd = dateEnv.add(internal.addDays(normalizedEnd, -1), dateProfile.slotMaxTime);
}
tDateProfile.timeWindowMs = timeWindowMs;
tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
let slotDates = [];
let slotDatesMajor = [];
let date = normalizedStart;
let majorUnit = internal.computeMajorUnit(dateProfile, dateEnv);
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
slotDates.push(date);
slotDatesMajor.push(internal.isMajorUnit(date, majorUnit, dateEnv));
}
date = dateEnv.add(date, tDateProfile.slotDuration);
}
tDateProfile.slotDates = slotDates;
tDateProfile.slotDatesMajor = slotDatesMajor;
// more...
let snapIndex = -1;
let snapDiff = 0; // index of the diff :(
const snapDiffToIndex = [];
const snapIndexToDiff = [];
date = normalizedStart;
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
snapIndex += 1;
snapDiffToIndex.push(snapIndex);
snapIndexToDiff.push(snapDiff);
}
else {
snapDiffToIndex.push(snapIndex + 0.5);
}
date = dateEnv.add(date, tDateProfile.snapDuration);
snapDiff += 1;
}
tDateProfile.snapDiffToIndex = snapDiffToIndex;
tDateProfile.snapIndexToDiff = snapIndexToDiff;
tDateProfile.snapCnt = snapIndex + 1; // is always one behind
tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
// more...
tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv, majorUnit);
tDateProfile.slotsPerLabel = internal.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
return tDateProfile;
}
/*
snaps to appropriate unit
*/
function normalizeDate(date, tDateProfile, dateEnv) {
let normalDate = date;
if (!tDateProfile.isTimeScale) {
normalDate = internal.startOfDay(normalDate);
if (tDateProfile.largeUnit) {
normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
}
}
return normalDate;
}
/*
snaps to appropriate unit
*/
function normalizeRange(range, tDateProfile, dateEnv) {
if (!tDateProfile.isTimeScale) {
range = internal.computeVisibleDayRange(range);
if (tDateProfile.largeUnit) {
let dayRange = range; // preserve original result
range = {
start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
end: dateEnv.startOf(range.end, tDateProfile.largeUnit),
};
// if date is partially through the interval, or is in the same interval as the start,
// make the exclusive end be the *next* interval
if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
range = {
start: range.start,
end: dateEnv.add(range.end, tDateProfile.slotDuration),
};
}
}
}
return range;
}
function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
if (dateProfileGenerator.isHiddenDay(date)) {
return false;
}
if (tDateProfile.isTimeScale) {
// determine if the time is within slotMinTime/slotMaxTime, which may have wacky values
let day = internal.startOfDay(date);
let timeMs = date.valueOf() - day.valueOf();
let ms = timeMs - internal.asRoughMs(dateProfile.slotMinTime); // milliseconds since slotMinTime
ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
return ms < tDateProfile.timeWindowMs; // before the slotMaxTime?
}
return true;
}
function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
// make sure labelInterval doesn't exceed the max number of cells
if (tDateProfile.labelInterval) {
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
if (labelCnt > internal.config.MAX_TIMELINE_SLOTS) {
console.warn('slotHeaderInterval results in too many cells');
tDateProfile.labelInterval = null;
}
}
// make sure slotDuration doesn't exceed the maximum number of cells
if (tDateProfile.slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
if (slotCnt > internal.config.MAX_TIMELINE_SLOTS) {
console.warn('slotDuration results in too many cells');
tDateProfile.slotDuration = null;
}
}
// make sure labelInterval is a multiple of slotDuration
if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
const slotsPerLabel = internal.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
if (slotsPerLabel === null || slotsPerLabel < 1) {
console.warn('slotHeaderInterval must be a multiple of slotDuration');
tDateProfile.slotDuration = null;
}
}
}
function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { labelInterval } = tDateProfile;
if (!labelInterval) {
// compute based off the slot duration
// find the largest label interval with an acceptable slots-per-label
let input;
if (tDateProfile.slotDuration) {
for (input of STOCK_SUB_DURATIONS) {
const tryLabelInterval = internal.createDuration(input);
const slotsPerLabel = internal.wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
labelInterval = tryLabelInterval;
break;
}
}
// use the slot duration as a last resort
if (!labelInterval) {
labelInterval = tDateProfile.slotDuration;
}
// compute based off the view's duration
// find the largest label interval that yields the minimum number of labels
}
else {
for (input of STOCK_SUB_DURATIONS) {
labelInterval = internal.createDuration(input);
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
if (labelCnt >= MIN_AUTO_LABELS) {
break;
}
}
}
tDateProfile.labelInterval = labelInterval;
}
return labelInterval;
}
function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { slotDuration } = tDateProfile;
if (!slotDuration) {
const labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
// compute based off the label interval
// find the largest slot duration that is different from labelInterval, but still acceptable
for (let input of STOCK_SUB_DURATIONS) {
const trySlotDuration = internal.createDuration(input);
const slotsPerLabel = internal.wholeDivideDurations(labelInterval, trySlotDuration);
if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
slotDuration = trySlotDuration;
break;
}
}
// only allow the value if it won't exceed the view's # of slots limit
if (slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
if (slotCnt > MAX_AUTO_CELLS) {
slotDuration = null;
}
}
// use the label interval as a last resort
if (!slotDuration) {
slotDuration = labelInterval;
}
tDateProfile.slotDuration = slotDuration;
}
return slotDuration;
}
function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
let format1;
let format2;
const { labelInterval } = tDateProfile;
const { currentRange } = dateProfile;
let unit = internal.greatestDurationDenominator(labelInterval).unit;
const weekNumbersVisible = allOptions.weekNumbers;
let format0 = (format1 = (format2 = null));
// NOTE: weekNumber computation function wont work
if ((unit === 'week') && !weekNumbersVisible) {
unit = 'day';
}
switch (unit) {
case 'year':
format0 = { year: 'numeric' }; // '2015'
break;
case 'month':
if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { month: 'short' }; // 'Jan'
break;
case 'week':
if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { week: 'narrow' }; // 'Wk4'
break;
case 'day':
if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
}
else if (dateEnv.diffWholeMonths(currentRange.start, currentRange.end) > 1) {
format0 = { month: 'long' }; // 'January'
}
if (weekNumbersVisible) {
format1 = { week: 'short' }; // 'Wk 4'
}
format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
break;
case 'hour':
if (weekNumbersVisible) {
format0 = { week: 'short' }; // 'Wk 4'
}
if (internal.diffWholeDays(currentRange.start, currentRange.end) > 1) {
format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
}
format2 = {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'short',
};
break;
case 'minute':
// sufficiently large number of different minute cells?
if ((internal.asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = {
hour: 'numeric',
meridiem: 'short',
};
format1 = (params) => (':' + internal.padStart(params.date.minute, 2) // ':30'
);
}
else {
format0 = {
hour: 'numeric',
minute: 'numeric',
meridiem: 'short',
};
}
break;
case 'second':
// sufficiently large number of different second cells?
if ((internal.asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
format1 = (params) => (':' + internal.padStart(params.date.second, 2) // ':30'
);
}
else {
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
}
break;
case 'millisecond':
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
format1 = (params) => ('.' + internal.padStart(params.millisecond, 3));
break;
}
return [].concat(format0 || [], format1 || [], format2 || []);
}
function buildCellRows(tDateProfile, dateEnv, majorUnit) {
let slotDates = tDateProfile.slotDates;
let formats = tDateProfile.headerFormats;
let cellRows = formats.map(() => []); // indexed by row,col
let slotAsDays = internal.asCleanDays(tDateProfile.slotDuration);
let guessedSlotUnit = slotAsDays === 7 ? 'week' :
slotAsDays === 1 ? 'day' :
null;
// specifically for navclicks
let rowUnitsFromFormats = formats.map((format) => (format.getSmallestUnit ? format.getSmallestUnit() : null));
// builds cellRows and slotCells
for (let i = 0; i < slotDates.length; i += 1) {
let date = slotDates[i];
for (let row = 0; row < formats.length; row += 1) {
let format = formats[row];
let rowCells = cellRows[row];
let leadingCell = rowCells[rowCells.length - 1];
let isLastRow = row === formats.length - 1;
let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
let isMajor = internal.isMajorUnit(date, majorUnit, dateEnv);
let newCell = null;
let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
if (isSuperRow) {
let [text] = dateEnv.format(date, format);
if (!leadingCell || (leadingCell.text !== text)) {
newCell = buildCellObject(date, isMajor, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
}
else if (!leadingCell ||
internal.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
let [text] = dateEnv.format(date, format);
newCell = buildCellObject(date, isMajor, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
if (newCell) {
rowCells.push(newCell);
}
}
}
return cellRows;
}
function buildCellObject(date, isMajor, text, rowUnit) {
return { date, isMajor, text, rowUnit, colspan: 1 }; // colspan mutated later
}
class TimelineSlatCell extends internal.BaseComponent {
constructor() {
super(...arguments);
// memo
this.getDateMeta = internal.memoize(internal.getDateMeta);
}
render() {
let { props, context } = this;
let { dateEnv, options } = context;
let { date, tDateProfile, isMajor } = props;
let dateMeta = this.getDateMeta(props.date, dateEnv, props.dateProfile, props.todayRange, props.nowDate);
let isMinor = tDateProfile.isTimeScale &&
!internal.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval));
let renderProps = Object.assign(Object.assign({}, dateMeta), { isMajor,
isMinor, view: context.viewApi });
return (preact.createElement(internal.ContentContainer, { tag: "div", className: core.joinClassNames(classNames__default["default"].tight, classNames__default["default"].alignStart, // shrinks width of InnerContent
props.borderStart ? classNames__default["default"].borderOnlyS : classNames__default["default"].borderNone, classNames__default["default"].internalTimelineSlot), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
omitTimeZoneOffset: true,
omitTime: !tDateProfile.isTimeScale,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.width,
}, renderProps: renderProps, generatorName: undefined, classNameGenerator: options.slotLaneClass, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }));
}
}
class TimelineSlats extends internal.BaseComponent {
render() {
let { props } = this;
let { tDateProfile, slotWidth } = props;
let { slotDates, slotDatesMajor } = tDateProfile;
return (preact.createElement("div", { "aria-hidden": true, className: core.joinClassNames(classNames__default["default"].flexRow, classNames__default["default"].fill), style: { height: props.height } }, slotDates.map((slotDate, i) => {
let key = slotDate.toISOString();
return (preact.createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isMajor: slotDatesMajor[i], borderStart: Boolean(i),
// dimensions
width: slotWidth }));
})));
}
}
class TimelineHeaderCell extends internal.BaseComponent {
constructor() {
super(...arguments);
// memo
this.getDateMeta = internal.memoize(internal.getDateMeta);
// ref
this.innerWrapperElRef = preact.createRef();
}
render() {
let { props, state, context } = this;
let { dateEnv, options } = context;
let { cell, dateProfile, tDateProfile } = props;
// the cell.rowUnit is f'd
// giving 'month' for a 3-day view
// workaround: to infer day, do NOT time
let dateMeta = this.getDateMeta(cell.date, dateEnv, dateProfile, props.todayRange, props.nowDate);
let hasNavLink = options.navLinks && !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
let isTime = tDateProfile.isTimeScale && !props.rowLevel; // HACK: faulty way of determining this
let renderProps = Object.assign(Object.assign({}, dateMeta), { level: props.rowLevel, isMajor: cell.isMajor, isMinor: false, isNarrow: false, isTime,
hasNavLink, text: cell.text, isFirst: props.isFirst, view: context.viewApi });
const { slotHeaderAlign } = options;
const align = this.align =
typeof slotHeaderAlign === 'function'
? slotHeaderAlign({ level: props.rowLevel, isTime })
: slotHeaderAlign;
const isSticky = this.isSticky =
props.rowLevel && options.slotHeaderSticky !== false;
let edgeCoord;
if (isSticky) {
if (align === 'center') {
if (state.innerWidth != null) {
edgeCoord = `calc(50% - ${state.innerWidth / 2}px)`;
}
}
else {
edgeCoord = (typeof options.slotHeaderSticky === 'number' ||
typeof options.slotHeaderSticky === 'string') ? options.slotHeaderSticky : 0;
}
}
return (preact.createElement(internal.ContentContainer, { tag: "div", className: internal.joinArrayishClassNames(classNames__default["default"].tight, classNames__default["default"].flexCol, props.isFirst ? classNames__default["default"].borderNone : classNames__default["default"].borderOnlyS, align === 'center' ? classNames__default["default"].alignCenter :
align === 'end' ? classNames__default["default"].alignEnd :
classNames__default["default"].alignStart, classNames__default["default"].internalTimelineSlot), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
omitTime: !tDateProfile.isTimeScale,
omitTimeZoneOffset: true,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.slotWidth != null
? props.slotWidth * cell.colspan
: undefined,
}, renderProps: renderProps, generatorName: "slotHeaderContent", customGenerator: options.slotHeaderContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotHeaderClass, didMount: options.slotHeaderDidMount, willUnmount: options.slotHeaderWillUnmount }, (InnerContent) => (preact.createElement("div", { ref: this.innerWrapperElRef, className: core.joinClassNames(classNames__default["default"].flexCol, classNames__default["default"].rigid, isSticky && classNames__default["default"].sticky), style: {
left: edgeCoord,
right: edgeCoord,
} },
preact.createElement(InnerContent, { tag: 'div', attrs: hasNavLink
// not tabbable because parent is aria-hidden
? internal.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
: {} // don't bother with aria-hidden because parent already hidden
, className: internal.generateClassName(options.slotHeaderInnerClass, renderProps) })))));
}
componentDidMount() {
const { props } = this;
const innerWrapperEl = this.innerWrapperElRef.current; // TODO: make dynamic with useEffect
this.disconnectSize = internal.watchSize(innerWrapperEl, (width, height) => {
internal.setRef(props.innerWidthRef, width);
internal.setRef(props.innerHeightRef, height);
if (this.align === 'center' && this.isSticky) {
this.setState({ innerWidth: width });
}
});
}
componentWillUnmount() {
const { props } = this;
this.disconnectSize();
internal.setRef(props.innerWidthRef, null);
internal.setRef(props.innerHeightRef, null);
}
}
// Utils
// -------------------------------------------------------------------------------------------------
function renderInnerContent(renderProps) {
return renderProps.text;
}
class TimelineHeaderRow extends internal.BaseComponent {
constructor() {
super(...arguments);
// refs
this.innerWidthRefMap = new internal.RefMap(() => {
internal.afterSize(this.handleInnerWidths);
});
this.innerHeightRefMap = new internal.RefMap(() => {
internal.afterSize(this.handleInnerHeights);
});
this.handleInnerWidths = () => {
const innerWidthMap = this.innerWidthRefMap.current;
let max = 0;
for (const innerWidth of innerWidthMap.values()) {
max = Math.max(max, innerWidth);
}
// TODO: ensure not equal?
internal.setRef(this.props.innerWidthRef, max);
};
this.handleInnerHeights = () => {
const innerHeightMap = this.innerHeightRefMap.current;
let max = 0;
for (const innerHeight of innerHeightMap.values()) {
max = Math.max(max, innerHeight);
}
// TODO: ensure not equal?
internal.setRef(this.props.innerHeighRef, max);
this.setState({ innerHeight: max });
};
}
render() {
const { props, innerWidthRefMap, innerHeightRefMap, state, context } = this;
const { options } = context;
return (preact.createElement("div", { className: internal.joinArrayishClassNames(options.slotHeaderRowClass, classNames__default["default"].flexRow, classNames__default["default"].grow, props.rowLevel // not the last row?
? classNames__default["default"].borderOnlyB
: classNames__default["default"].borderNone), style: {
// we assign height because we allow cells to have distorted heights for visual effect
// but we still want to keep the overall extrenal mass
height: state.innerHeight,
} }, props.cells.map((cell, cellI) => {
// TODO: make this part of the cell obj?
// TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
// TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
// TODO: use rowUnit to key the Row itself?
const key = cell.rowUnit + ':' + cell.date.toISOString();
return (preact.createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isFirst: cellI === 0,
// refs
innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
// dimensions
slotWidth: props.slotWidth }));
})));
}
componentWillUnmount() {
internal.setRef(this.props.innerWidthRef, null);
internal.setRef(this.props.innerHeighRef, null);
}
}
// Timeline-specific
// -------------------------------------------------------------------------------------------------
const MIN_SLOT_WIDTH = 30; // for real
/*
TODO: DRY with computeSlatHeight?
*/
function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
if (labelInnerWidth == null || viewportWidth == null) {
return [undefined, undefined, false];
}
slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
const slatTryWidth = viewportWidth / slatCnt;
let slotLiquid;
let slatWidth;
if (slatTryWidth >= slatMinWidth) {
slotLiquid = true;
slatWidth = slatTryWidth;
}
else {
slotLiquid = false;
slatWidth = Math.max(slatMinWidth, slatTryWidth);
}
return [slatWidth * slatCnt, slatWidth, slotLiquid];
}
function timeToCoord(// pixels
time, dateEnv, dateProfile, tDateProfile, slowWidth) {
let date = dateEnv.add(dateProfile.activeRange.start, time);
if (!tDateProfile.isTimeScale) {
date = internal.startOfDay(date);
}
return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
}
function dateToCoord(// pixels
date, dateEnv, tDateProfile, slotWidth) {
let snapCoverage = computeDateSnapCoverage$1(date, tDateProfile, dateEnv);
let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
return slotCoverage * slotWidth;
}
/*
returned value is between 0 and the number of snaps
*/
function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (internal.isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
class TimelineNowIndicatorLine extends internal.BaseComponent {
render() {
const { props, context } = this;
const xStyle = props.slotWidth == null
? {}
: {
insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth)
};
return (preact.createElement("div", { className: classNames__default["default"].fill, style: {
zIndex: 2,
pointerEvents: 'none', // TODO: className
} },
preact.createElement(internal.NowIndicatorLineContainer, { className: core.joinClassNames(classNames__default["default"].fillY, classNames__default["default"].noMarginY, classNames__default["default"].borderlessY), style: xStyle, date: props.nowDate }),
preact.createElement("div", { className: core.joinClassNames(classNames__default["default"].flexCol, // better for negative margins
classNames__default["default"].fillY), style: xStyle },
preact.createElement("div", {
// stickiness on NowIndicatorDot misbehaves b/c of negative marginss
className: classNames__default["default"].stickyT },
preact.createElement(internal.NowIndicatorDot, null)))));
}
}
/*
TODO: DRY with other NowIndicator components
*/
class TimelineNowIndicatorArrow extends internal.BaseComponent {
render() {
const { props, context } = this;
const xStyle = props.slotWidth == null
? {}
: {
insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth)
};
return (preact.createElement("div", {
// crop any overflow that the arrow/line might cause
// TODO: just do this on the entire canvas within the scroller
className: core.joinClassNames(classNames__default["default"].fill, classNames__default["default"].crop), style: {
zIndex: 2,
pointerEvents: 'none', // TODO: className
} },
preact.createElement(internal.NowIndicatorHeaderContainer, { className: classNames__default["default"].abs, style: xStyle, date: props.nowDate })));
}
}
function getTimelineSlotEl(parentEl, index) {
return parentEl.querySelectorAll(`.${classNames__default["default"].internalTimelineSlot}`)[index];
}
/*
TODO: rename this file!
*/
// returned value is between 0 and the number of snaps
function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (internal.isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
class TimelineLaneSlicer extends internal.Slicer {
sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
let segs = [];
// protect against when the span is entirely in an invalid date region
if (computeDateSnapCoverage(normalRange.start, tDateProfile, dateEnv)
< computeDateSnapCoverage(normalRange.end, tDateProfile, dateEnv)) {
// intersect the footprint's range with the grid's range
let slicedRange = internal.intersectRanges(normalRange, tDateProfile.normalizedRange);
if (slicedRange) {
segs.push({
startDate: slicedRange.start,
endDate: slicedRange.end,
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
&& isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
&& isValidDate(internal.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
});
}
}
return segs;
}
}
const DEFAULT_TIME_FORMAT = internal.createFormatter({
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'narrow',
});
class TimelineEvent extends internal.BaseComponent {
render() {
let { props } = this;
return (preact.createElement(internal.StandardEvent, Object.assign({}, props, { display: 'row', defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
}
}
class TimelineLaneMoreLink extends internal.BaseComponent {
render() {
let { props } = this;
let { hiddenSegs, resourceId } = props;
let dateSpanProps = resourceId ? { resourceId } : {};
return (preact.createElement(internal.MoreLinkContainer, { display: 'row', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, isNarrow: false, isMicro: false, popoverContent: () => (preact.createElement(preact.Fragment, null, hiddenSegs.map((seg) => {
let { eventRange } = seg;
let { instanceId } = eventRange.instance;
let isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
let isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
let isInvisible = isDragging || isResizing;
return (preact.createElement("div", { key: instanceId, style: { visibility: isInvisible ? 'hidden' : undefined } },
preact.createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: false, isSelected: instanceId === props.eventSelection }, internal.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
}))) }));
}
}
function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const res = {};
for (const seg of segs) {
res[internal.getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
}
return res;
}
function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
let size = endCoord - startCoord;
if (segMinWidth) {
size = Math.max(size, segMinWidth);
}
return { start: startCoord, size };
}
function computeFgSegPlacements(// mostly horizontals
segs, segHorizontals, // TODO: use Map
segHeights, // keyed by instanceId
hiddenGroupHeights, strictOrder, maxDepth) {
const segRanges = [];
// isn't it true that there will either be ALL hcoords or NONE? can optimize
for (const seg of segs) {
const hcoords = segHorizontals[internal.getEventKey(seg)];
if (hcoords) {
segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
}
}
const hierarchy = new internal.SegHierarchy(segRanges, (seg) => segHeights.get(internal.getEventKey(seg)), strictOrder, undefined, // maxCoord
maxDepth);
const segTops = new Map();
hierarchy.traverseSegs((seg, segTop) => {
segTops.set(internal.getEventKey(seg), segTop);
});
const { hiddenSegs } = hierarchy;
let totalHeight = 0;
for (const segRange of segRanges) {
const segKey = internal.getEventKey(segRange);
const segHeight = segHeights.get(segKey);
const segTop = segTops.get(segKey);
if (segHeight != null) {
if (segTop != null) {
totalHeight = Math.max(totalHeight, segTop + segHeight);
}
}
}
const hiddenGroups = internal.groupIntersectingSegs(hiddenSegs);
const hiddenGroupTops = new Map();
// HACK for hiddenGroup findInsertion() call
hierarchy.strictOrder = true;
for (const hiddenGroup of hiddenGroups) {
const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
hiddenGroupTops.set(hiddenGroup.key, top);
totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
}
return [
segTops,
hiddenGroups,
hiddenGroupTops,
totalHeight,
];
}
/*
TODO: make DRY with other Event Harnesses
*/
class TimelineEventHarness extends preact.Component {
constructor() {
super(...arguments);
// ref
this.rootElRef = preact.createRef();
}
render() {
const { props } = this;
return (preact.createElement("div", { className: classNames__default["default"].abs, style: props.style, ref: this.rootElRef }, props.children));
}
componentDidMount() {
const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
this.disconnectHeight = internal.watchHeight(rootEl, (height) => {
internal.setRef(this.props.heightRef, height);
});
}
componentWillUnmount() {
this.disconnectHeight();
internal.setRef(this.props.heightRef, null);
}
}
class TimelineFg extends internal.BaseComponent {
constructor() {
super(...arguments);
// memo
this.sortEventSegs = internal.memoize(internal.sortEventSegs);
// refs
this.segHeightRefMap = new internal.RefMap(() => {
internal.afterSize(this.handleSegHeights);
});
this.moreLinkHeightRefMap = new internal.RefMap(() => {
internal.afterSize(this.handleMoreLinkHeights);
});
this.handleMoreLinkHeights = () => {
this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
};
this.handleSegHeights = () => {
this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
};
}
/*
TODO: lots of memoization needed here!
*/
render() {
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
let { options } = context;
let { tDateProfile } = props;
let mirrorSegs = (props.eventDrag ? props.eventDrag.segs : null) ||
(props.eventResize ? props.eventResize.segs : null) ||
[];
let fgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
let fgSegHorizontals = props.slotWidth != null
? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {};
let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
this.totalHeight = totalHeight;
return (preact.createElement("div", { className: core.joinClassNames(classNames__default["default"].rel, classNames__default["default"].noShrink), style: {
height: totalHeight,
} },
this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, hiddenGroups, hiddenGroupTops,
/* isMirror = */ false),
this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {}, fgSegTops,
/* hiddenGroups = */ [],
/* hiddenGroupTops = */ new Map(),
/* isMirror = */ true)));
}
renderFgSegs(segs, segHorizontals, segTops, hiddenGroups, hiddenGroupTops, isMirror) {
let { props, segHeightRefMap, moreLinkHeightRefMap } = this;
return (preact.createElement(preact.Fragment, null,
segs.map((seg) => {
const { eventRange } = seg;
const { instanceId } = eventRange.instance;
const segTop = segTops.get(instanceId);
const segHorizontalMaybe = segHorizontals[instanceId];
const segHorizontal = segHorizontalMaybe || {};
const isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
const isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
const isInvisible = !isMirror && (isDragging || isResizing || !segHorizontalMaybe || segTop == null);
return (preact.createElement(TimelineEventHarness, { key: instanceId, style: {
visibility: isInvisible ? 'hidden' : undefined,
zIndex: 1,
top: segTop || 0,
insetInlineStart: segHorizontal.start,
width: segHorizontal.size,
}, heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
preact.createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: isMirror, isSelected: instanceId === props.eventSelection }, internal.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
}),
hiddenGroups.map((hiddenGroup) => (preact.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: {
top: hiddenGroupTops.get(hiddenGroup.key) || 0,
insetInlineStart: hiddenGroup.start,
width: hiddenGroup.end - hiddenGroup.start,
}, heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
preact.createElement(TimelineLaneMoreLink, { hiddenSegs: hiddenGroup.segs, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection, resourceId: props.resourceId }))))));
}
/*
componentDidMount(): void {
// might want to do firedTotalHeight, but won't be ready on first render
}
*/
componentDidUpdate() {
if (this.totalHeight !== this.firedTotalHeight) {
this.firedTotalHeight = this.totalHeight;
internal.setRef(this.props.heightRef, this.totalHeight);
}
}
componentWillUnmount() {
internal.setRef(this.props.heightRef, null);
}
}
class TimelineBg extends internal.BaseComponent {
render() {
let { props } = this;
let highlightSeg = [].concat(props.eventResizeSegs || [], props.dateSelectionSegs);
return (preact.createElement(preact.Fragment, null,
this.renderSegs(props.businessHourSegs || [], 'non-business'),
this.renderSegs(props.bgEventSegs || [], 'bg-event'),
this.renderSegs(highlightSeg, 'highlight')));
}
renderSegs(segs, fillType) {
let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
let { dateEnv, options } = this.context;
return (preact.createElement(preact.Fragment, null, segs.map((seg) => {
let hStyle = {};
if (slotWidth != null) {
let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
hStyle = { insetInlineStart: segHorizontal.start, width: segHorizontal.size };
}
return (preact.createElement("div", { key: internal.buildEventRangeKey(seg.eventRange), className: classNames__default["default"].fillY, style: hStyle }, fillType === 'bg-event' ? (preact.createElement(internal.BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isVertical: false }, internal.getEventRangeMeta(seg.eventRange, todayRange, nowDate)))) : (internal.renderFill(fillType, options))));
})));
}
}
class TimelineView extends internal.DateComponent {
constructor() {
super(...arguments);
// memoized
this.buildTimelineDateProfile = internal.memoize(buildTimelineDateProfile);
this.computeSlotWidth = internal.memoize(computeSlotWidth);
// refs
this.headerScrollerRef = preact.createRef();
this.bodyScrollerRef = preact.createRef();
this.footerScrollerRef = preact.createRef();
this.headerRowInnerWidthMap = new internal.RefMap(() => {
internal.afterSize(this.handleSlotInnerWidths);
});
this.scrollTime = null;
this.slicer = new TimelineLaneSlicer();
// Sizing
// -----------------------------------------------------------------------------------------------
this.handleSlotInnerWidths = () => {
const headerSlotInnerWidth = this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1);
if (headerSlotInnerWidth != null && headerSlotInnerWidth !== this.state.slotInnerWidth) {
this.setState({ slotInnerWidth: headerSlotInnerWidth });
}
};
this.handleTotalWidth = (totalWidth) => {
this.setState({
totalWidth,
});
};
this.handleClientWidth = (clientWidth) => {
this.setState({
clientWidth,
});
};
this.handleTimeScrollRequest = (scrollTime) => {
this.scrollTime = scrollTime;
this.applyTimeScroll();
};
this.handleTimeScrollEnd = (isUser) => {
if (isUser) {
this.scrollTime = null;
}
};
// Hit System
// -----------------------------------------------------------------------------------------------
this.handeBodyEl = (el) => {
this.bodyEl = el;
if (el) {
this.context.registerInteractiveComponent(this, { el });
}
else {
this.context.unregisterInteractiveComponent(this);
}
};
}
render() {
const { props, state, context } = this;
const { options } = context;
const { totalWidth, clientWidth } = state;
const endScrollbarWidth = (totalWidth != null && clientWidth != null)
? totalWidth - clientWidth
: undefined;
/* date */
const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
const { cellRows } = tDateProfile;
const timerUnit = internal.greatestDurationDenominator(tDateProfile.slotDuration).unit;
/* table settings */
const verticalScrolling = !props.forPrint && !internal.getIsHeightAuto(options);
const stickyHeaderDates = !props.forPrint && internal.getStickyHeaderDates(options);
const stickyFooterScrollbar = !props.forPrint && internal.getStickyFooterScrollbar(options);
/* table positions */
const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
clientWidth);
this.slotWidth = slotWidth;
/* sliced */
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...
props.dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
return (preact.createElement(internal.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
const enableNowIndicator = // TODO: DRY
options.nowIndicator &&
slotWidth != null &&
internal.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
return (preact.createElement(internal.ViewContainer, { viewSpec: context.viewSpec, className: internal.joinArrayishClassNames(
// HACK for Safari print-mode, where noScrollbars won't take effect for
// the below Scrollers if they have liquid flex height
!props.forPrint && classNames__default["default"].flexCol, props.className, options.tableClass, classNames__default["default"].isolate), borderlessX: props.borderlessX, borderlessTop: props.borderlessTop, borderlessBottom: props.borderlessBottom, noEdgeEffects: props.noEdgeEffects },
preact.createElement("div", { className: core.joinClassNames(internal.generateClassName(options.tableHeaderClass, {
isSticky: stickyHeaderDates,
}), props.borderlessX && classNames__default["default"].borderlessX, classNames__default["default"].flexCol, stickyHeaderDates && classNames__default["default"].tableHeaderSticky), style: {
zIndex: 1,
} },
preact.createElement(internal.Scroller, { horizontal: true, hideScrollbars: true, className: classNames__default["default"].flexRow, ref: this.headerScrollerRef },
preact.createElement("div", {
// TODO: DRY
className: core.joinClassNames(classNames__default["default"].rel, // origin for now-indicator
canvasWidth == null && classNames__default["default"].liquid), style: { width: canvasWidth } },
cellRows.map((cells, rowIndex) => {
const rowLevel = cellRows.length - rowIndex - 1;
return (preact.createElement(TimelineHeaderRow, { key: rowIndex, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, cells: cells, slotWidth: slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowIndex) }));
}),
enableNowIndicator && (preact.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
Boolean(endScrollbarWidth) && (preact.createElement("div", { className: internal.joinArrayishClassNames(internal.generateClassName(options.fillerClass, { isHeader: true }), classNames__default["default"].borderOnlyS), style: { minWidth: endScrollbarWidth } }))),
preact.createElement("div", { className: internal.generateClassName(options.slotHeaderDividerClass, {
isHeader: true,
options: { dayMinWidth: options.dayMinWidth },
}) })),
preact.createElement(internal.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: stickyFooterScrollbar ||
props.forPrint // prevents blank space in print-view on Safari
, className: internal.joinArrayishClassNames(options.tableBodyClass, props.borderlessX && classNames__default["default"].borderlessX, stickyHeaderDates && classNames__default["default"].borderlessTop, (stickyHeaderDates || props.noEdgeEffects) && classNames__default["default"].noEdgeEffects, classNames__default["default"].flexCol, verticalScrolling && classNames__default["default"].liquid), style: {
zIndex: 0,
}, ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth },
preact.createElement("div", { "aria-label": options.eventsHint, className: core.joinClassNames(classNames__default["default"].rel, // for canvas origin?
classNames__default["default"].grow), style: { width: canvasWidth }, ref: this.handeBodyEl },
preact.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// dimensions
slotWidth: slotWidth }),
preact.createElement(TimelineBg, { tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// content
bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : null,
// dimensions
slotWidth: slotWidth }),
preact.createElement("div", { className: internal.joinArrayishClassNames(options.timelineTopClass) }),
preact.createElement(TimelineFg, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// content
fgEventSegs: slicedProps.fgEventSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
// dimensions
slotWidth: slotWidth }),
preact.createElement("div", { className: internal.joinArrayishClassNames(options.timelineBottomClass) }),
enableNowIndicator && (preact.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
Boolean(stickyFooterScrollbar) && (preact.createElement(internal.FooterScrollbar, { isSticky: true, canvasWidth: canvasWidth, scrollerRef: this.footerScrollerRef })),
preact.createElement(internal.Ruler, { widthRef: this.handleTotalWidth })));
}));
}
// Lifecycle
// -----------------------------------------------------------------------------------------------
componentDidMount() {
this.syncedScroller = new internal$1.ScrollerSyncer(true); // horizontal=true
this.updateSyncedScroller();
this.resetScroll();
this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
}
componentDidUpdate(prevProps) {
this.updateSyncedScroller();
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
this.resetScroll();
}
else {
// TODO: inefficient to update so often
this.applyTimeScroll();
}
}
componentWillUnmount() {
this.syncedScroller.destroy();
this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
}
// Scrolling
// -----------------------------------------------------------------------------------------------
updateSyncedScroller() {
this.syncedScroller.handleChildren([
this.headerScrollerRef.current,
this.bodyScrollerRef.current,
this.footerScrollerRef.current
]);
}
resetScroll() {
this.handleTimeScrollRequest(this.context.options.scrollTime);
}
applyTimeScroll() {
const { props, context, tDateProfile, scrollTime, slotWidth } = this;
if (scrollTime != null && slotWidth != null) {
let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
if (x) {
x += 1; // overcome border. TODO: DRY this up
}
this.syncedScroller.scrollTo({ x });
}
}
queryHit(isRtl, positionLeft, positionTop, elWidth, elHeight) {
const { props, context, tDateProfile, slotWidth } = this;
const { dateEnv } = context;
if (slotWidth) {
const x = isRtl ? elWidth - positionLeft : positionLeft;
const slatIndex = Math.floor(x / slotWidth);
const slatX = slatIndex * slotWidth;
const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], internal.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
// TODO: generalize this coord stuff to TimeGrid?
let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
let endCoord = startCoord + snapWidth;
let left, right;
if (isRtl) {
left = elWidth - endCoord;
right = elWidth - startCoord;
}
else {
left = startCoord;
right = endCoord;
}
return {
dateProfile: props.dateProfile,
dateSpan: {
range: { start: startDate, end: endDate },
allDay: !tDateProfile.isTimeScale,
},
rect: {
left,
right,
top: 0,
bottom: elHeight,
},
getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
layer: 0,
};
}
return null;
}
}
exports.TimelineBg = TimelineBg;
exports.TimelineFg = TimelineFg;
exports.TimelineHeaderRow = TimelineHeaderRow;
exports.TimelineLaneSlicer = TimelineLaneSlicer;
exports.TimelineNowIndicatorArrow = TimelineNowIndicatorArrow;
exports.TimelineNowIndicatorLine = TimelineNowIndicatorLine;
exports.TimelineSlats = TimelineSlats;
exports.TimelineView = TimelineView;
exports.buildTimelineDateProfile = buildTimelineDateProfile;
exports.computeSlotWidth = computeSlotWidth;
exports.getTimelineSlotEl = getTimelineSlotEl;
exports.timeToCoord = timeToCoord;
import { PluginDef } from '@fullcalendar/core';
import '@fullcalendar/premium-common';
import '@fullcalendar/scrollgrid';
import { RawOptionsFromRefiners, RefinedOptionsFromRefiners, Identity } from '@fullcalendar/core/internal';
declare const OPTION_REFINERS: {
timelineTopClass: Identity<string>;
timelineBottomClass: Identity<string>;
};
type TimelineOptionRefiners = typeof OPTION_REFINERS;
type TimelineOptions = RawOptionsFromRefiners<TimelineOptionRefiners>;
type TimelineOptionsRefined = RefinedOptionsFromRefiners<TimelineOptionRefiners>;
declare module '@fullcalendar/core/internal' {
interface BaseOptions extends TimelineOptions {
}
interface BaseOptionsRefined extends TimelineOptionsRefined {
}
}
//# sourceMappingURL=ambient.d.ts.map
declare const _default: PluginDef;
//# sourceMappingURL=index.d.ts.map
export { TimelineOptions, _default as default };
import { createPlugin } from '@fullcalendar/core';
import premiumCommonPlugin from '@fullcalendar/premium-common';
import { TimelineView } from './internal.js';
import { identity } from '@fullcalendar/core/internal';
import '@fullcalendar/core/internal-classnames';
import '@fullcalendar/core/preact';
import '@fullcalendar/scrollgrid/internal';
const OPTION_REFINERS = {
timelineTopClass: identity,
timelineBottomClass: identity,
};
var index = createPlugin({
name: '@fullcalendar/timeline',
premiumReleaseDate: '2025-12-20',
deps: [premiumCommonPlugin],
initialView: 'timelineDay',
optionRefiners: OPTION_REFINERS,
views: {
timeline: {
component: TimelineView,
usesMinMaxTime: true,
eventResizableFromStart: true, // how is this consumed for TimelineView tho?
},
timelineDay: {
type: 'timeline',
duration: { days: 1 },
},
timelineWeek: {
type: 'timeline',
duration: { weeks: 1 },
},
timelineMonth: {
type: 'timeline',
duration: { months: 1 },
},
timelineYear: {
type: 'timeline',
duration: { years: 1 },
},
},
});
export { index as default };
import { DateComponent, ViewProps, Hit, DateRange, DateMarker, DateProfile, DateEnv, BaseOptionsRefined, DateProfileGenerator, Slicer, CoordRange, BaseComponent, EventRangeProps, CoordSpan, SegGroup, EventSegUiInteractionState } from '@fullcalendar/core/internal';
import { createElement, Ref } from '@fullcalendar/core/preact';
import { Duration } from '@fullcalendar/core';
interface TimelineViewState {
totalWidth?: number;
clientWidth?: number;
slotInnerWidth?: number;
}
declare class TimelineView extends DateComponent<ViewProps, TimelineViewState> {
private buildTimelineDateProfile;
private computeSlotWidth;
private headerScrollerRef;
private bodyScrollerRef;
private footerScrollerRef;
private tDateProfile?;
private bodyEl?;
private slotWidth?;
private headerRowInnerWidthMap;
private syncedScroller;
private scrollTime;
private slicer;
render(): createElement.JSX.Element;
componentDidMount(): void;
componentDidUpdate(prevProps: ViewProps): void;
componentWillUnmount(): void;
handleSlotInnerWidths: () => void;
handleTotalWidth: (totalWidth: number) => void;
handleClientWidth: (clientWidth: number) => void;
private updateSyncedScroller;
private resetScroll;
private handleTimeScrollRequest;
private handleTimeScrollEnd;
private applyTimeScroll;
handeBodyEl: (el: HTMLElement | null) => void;
queryHit(isRtl: boolean, positionLeft: number, positionTop: number, elWidth: number, elHeight: number): Hit;
}
interface TimelineDateProfile {
labelInterval: Duration;
slotDuration: Duration;
slotsPerLabel: number;
headerFormats: any;
isTimeScale: boolean;
largeUnit: string;
snapDuration: Duration;
snapsPerSlot: number;
normalizedRange: DateRange;
timeWindowMs: number;
slotDates: DateMarker[];
slotDatesMajor: boolean[];
snapDiffToIndex: number[];
snapIndexToDiff: number[];
snapCnt: number;
slotCnt: number;
cellRows: TimelineHeaderCellData[][];
}
interface TimelineHeaderCellData {
date: DateMarker;
isMajor: boolean;
text: string;
rowUnit: string;
colspan: number;
}
declare function buildTimelineDateProfile(dateProfile: DateProfile, dateEnv: DateEnv, allOptions: BaseOptionsRefined, dateProfileGenerator: DateProfileGenerator): TimelineDateProfile;
interface TimelineRange {
startDate: DateMarker;
endDate: DateMarker;
isStart: boolean;
isEnd: boolean;
}
type TimelineCoordRange = TimelineRange & CoordRange;
declare class TimelineLaneSlicer extends Slicer<TimelineRange, [
DateProfile,
DateProfileGenerator,
TimelineDateProfile,
DateEnv
]> {
sliceRange(origRange: DateRange, dateProfile: DateProfile, dateProfileGenerator: DateProfileGenerator, tDateProfile: TimelineDateProfile, dateEnv: DateEnv): TimelineRange[];
}
interface TimelineFgProps {
dateProfile: DateProfile;
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
fgEventSegs: (TimelineRange & EventRangeProps)[];
eventDrag: EventSegUiInteractionState<TimelineRange> | null;
eventResize: EventSegUiInteractionState<TimelineRange> | null;
eventSelection: string;
resourceId?: string;
slotWidth: number | undefined;
heightRef?: Ref<number>;
}
interface TimelineFgState {
segHeightRev?: string;
moreLinkHeightRev?: string;
}
declare class TimelineFg extends BaseComponent<TimelineFgProps, TimelineFgState> {
private sortEventSegs;
private segHeightRefMap;
private moreLinkHeightRefMap;
private totalHeight?;
private firedTotalHeight?;
render(): createElement.JSX.Element;
renderFgSegs(segs: (TimelineRange & EventRangeProps)[], segHorizontals: {
[instanceId: string]: CoordSpan;
}, segTops: Map<string, number>, hiddenGroups: SegGroup<TimelineCoordRange>[], hiddenGroupTops: Map<string, number>, isMirror: boolean): createElement.JSX.Element;
private handleMoreLinkHeights;
private handleSegHeights;
componentDidUpdate(): void;
componentWillUnmount(): void;
}
interface TimelineBgProps {
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
bgEventSegs: (TimelineRange & EventRangeProps)[] | null;
businessHourSegs: (TimelineRange & EventRangeProps)[] | null;
dateSelectionSegs: (TimelineRange & EventRangeProps)[];
eventResizeSegs: (TimelineRange & EventRangeProps)[] | null;
slotWidth: number | undefined;
}
declare class TimelineBg extends BaseComponent<TimelineBgProps> {
render(): createElement.JSX.Element;
renderSegs(segs: (TimelineRange & EventRangeProps)[], fillType: string): createElement.JSX.Element;
}
interface TimelineSlatsProps {
dateProfile: DateProfile;
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
height?: number;
slotWidth: number | undefined;
}
declare class TimelineSlats extends BaseComponent<TimelineSlatsProps> {
render(): createElement.JSX.Element;
}
declare function computeSlotWidth(slatCnt: number, slatsPerLabel: number, slatMinWidth: number | undefined, labelInnerWidth: number | undefined, viewportWidth: number | undefined): [
canvasWidth: number | undefined,
slatWidth: number | undefined,
slotLiquid: boolean
];
declare function timeToCoord(// pixels
time: Duration, dateEnv: DateEnv, dateProfile: DateProfile, tDateProfile: TimelineDateProfile, slowWidth: number): number;
interface TimelineHeaderRowProps {
dateProfile: DateProfile;
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
rowLevel: number;
cells: TimelineHeaderCellData[];
innerHeighRef?: Ref<number>;
innerWidthRef?: Ref<number>;
slotWidth: number | undefined;
}
interface TimelineHeaderRowState {
innerHeight?: number;
}
declare class TimelineHeaderRow extends BaseComponent<TimelineHeaderRowProps, TimelineHeaderRowState> {
private innerWidthRefMap;
private innerHeightRefMap;
render(): createElement.JSX.Element;
handleInnerWidths: () => void;
handleInnerHeights: () => void;
componentWillUnmount(): void;
}
interface TimelineNowIndicatorArrowProps {
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
slotWidth: number | undefined;
}
declare class TimelineNowIndicatorArrow extends BaseComponent<TimelineNowIndicatorArrowProps> {
render(): createElement.JSX.Element;
}
interface TimelineNowIndicatorLineProps {
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
slotWidth: number | undefined;
}
declare class TimelineNowIndicatorLine extends BaseComponent<TimelineNowIndicatorLineProps> {
render(): createElement.JSX.Element;
}
declare function getTimelineSlotEl(parentEl: HTMLElement, index: number): HTMLElement;
export { TimelineBg, TimelineDateProfile, TimelineFg, TimelineHeaderRow, TimelineLaneSlicer, TimelineNowIndicatorArrow, TimelineNowIndicatorLine, TimelineRange, TimelineSlats, TimelineView, buildTimelineDateProfile, computeSlotWidth, getTimelineSlotEl, timeToCoord };
import { joinClassNames } from '@fullcalendar/core';
import { config, createFormatter, greatestDurationDenominator, createDuration, wholeDivideDurations, asRoughMs, addDays, computeMajorUnit, isMajorUnit, startOfDay, asRoughSeconds, asRoughMinutes, diffWholeDays, asCleanDays, isInt, computeVisibleDayRange, padStart, BaseComponent, memoize, getDateMeta, ContentContainer, joinArrayishClassNames, buildNavLinkAttrs, generateClassName, watchSize, setRef, RefMap, afterSize, NowIndicatorLineContainer, NowIndicatorDot, NowIndicatorHeaderContainer, 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 } from '@fullcalendar/core/internal';
import classNames from '@fullcalendar/core/internal-classnames';
import { createElement, createRef, Fragment, Component } from '@fullcalendar/core/preact';
import { ScrollerSyncer } from '@fullcalendar/scrollgrid/internal';
const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
const MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
config.MAX_TIMELINE_SLOTS = 1000;
// potential nice values for slot-duration and interval-duration
const STOCK_SUB_DURATIONS = [
{ years: 1 },
{ months: 1 },
{ days: 1 },
{ hours: 1 },
{ minutes: 30 },
{ minutes: 15 },
{ minutes: 10 },
{ minutes: 5 },
{ minutes: 1 },
{ seconds: 30 },
{ seconds: 15 },
{ seconds: 10 },
{ seconds: 5 },
{ seconds: 1 },
{ milliseconds: 500 },
{ milliseconds: 100 },
{ milliseconds: 10 },
{ milliseconds: 1 },
];
function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
let tDateProfile = {
labelInterval: allOptions.slotHeaderInterval,
slotDuration: allOptions.slotDuration,
};
validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
let input = allOptions.slotHeaderFormat;
let rawFormats = Array.isArray(input) ? input :
(input != null) ? [input] :
computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
tDateProfile.headerFormats = rawFormats.map((rawFormat) => createFormatter(rawFormat));
tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
let largeUnit = null;
if (!tDateProfile.isTimeScale) {
const slotUnit = greatestDurationDenominator(tDateProfile.slotDuration).unit;
if (/year|month|week/.test(slotUnit)) {
largeUnit = slotUnit;
}
}
tDateProfile.largeUnit = largeUnit;
/*
console.log('label interval =', timelineView.labelInterval.humanize())
console.log('slot duration =', timelineView.slotDuration.humanize())
console.log('header formats =', timelineView.headerFormats)
console.log('isTimeScale', timelineView.isTimeScale)
console.log('largeUnit', timelineView.largeUnit)
*/
let rawSnapDuration = allOptions.snapDuration;
let snapDuration;
let snapsPerSlot;
if (rawSnapDuration) {
snapDuration = createDuration(rawSnapDuration);
snapsPerSlot = wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
// ^ TODO: warning if not whole?
}
if (snapsPerSlot == null) {
snapDuration = tDateProfile.slotDuration;
snapsPerSlot = 1;
}
tDateProfile.snapDuration = snapDuration;
tDateProfile.snapsPerSlot = snapsPerSlot;
// more...
let timeWindowMs = asRoughMs(dateProfile.slotMaxTime) - asRoughMs(dateProfile.slotMinTime);
// TODO: why not use normalizeRange!?
let normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
let normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
// apply slotMinTime/slotMaxTime
// TODO: View should be responsible.
if (tDateProfile.isTimeScale) {
normalizedStart = dateEnv.add(normalizedStart, dateProfile.slotMinTime);
normalizedEnd = dateEnv.add(addDays(normalizedEnd, -1), dateProfile.slotMaxTime);
}
tDateProfile.timeWindowMs = timeWindowMs;
tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
let slotDates = [];
let slotDatesMajor = [];
let date = normalizedStart;
let majorUnit = computeMajorUnit(dateProfile, dateEnv);
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
slotDates.push(date);
slotDatesMajor.push(isMajorUnit(date, majorUnit, dateEnv));
}
date = dateEnv.add(date, tDateProfile.slotDuration);
}
tDateProfile.slotDates = slotDates;
tDateProfile.slotDatesMajor = slotDatesMajor;
// more...
let snapIndex = -1;
let snapDiff = 0; // index of the diff :(
const snapDiffToIndex = [];
const snapIndexToDiff = [];
date = normalizedStart;
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
snapIndex += 1;
snapDiffToIndex.push(snapIndex);
snapIndexToDiff.push(snapDiff);
}
else {
snapDiffToIndex.push(snapIndex + 0.5);
}
date = dateEnv.add(date, tDateProfile.snapDuration);
snapDiff += 1;
}
tDateProfile.snapDiffToIndex = snapDiffToIndex;
tDateProfile.snapIndexToDiff = snapIndexToDiff;
tDateProfile.snapCnt = snapIndex + 1; // is always one behind
tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
// more...
tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv, majorUnit);
tDateProfile.slotsPerLabel = wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
return tDateProfile;
}
/*
snaps to appropriate unit
*/
function normalizeDate(date, tDateProfile, dateEnv) {
let normalDate = date;
if (!tDateProfile.isTimeScale) {
normalDate = startOfDay(normalDate);
if (tDateProfile.largeUnit) {
normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
}
}
return normalDate;
}
/*
snaps to appropriate unit
*/
function normalizeRange(range, tDateProfile, dateEnv) {
if (!tDateProfile.isTimeScale) {
range = computeVisibleDayRange(range);
if (tDateProfile.largeUnit) {
let dayRange = range; // preserve original result
range = {
start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
end: dateEnv.startOf(range.end, tDateProfile.largeUnit),
};
// if date is partially through the interval, or is in the same interval as the start,
// make the exclusive end be the *next* interval
if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
range = {
start: range.start,
end: dateEnv.add(range.end, tDateProfile.slotDuration),
};
}
}
}
return range;
}
function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
if (dateProfileGenerator.isHiddenDay(date)) {
return false;
}
if (tDateProfile.isTimeScale) {
// determine if the time is within slotMinTime/slotMaxTime, which may have wacky values
let day = startOfDay(date);
let timeMs = date.valueOf() - day.valueOf();
let ms = timeMs - asRoughMs(dateProfile.slotMinTime); // milliseconds since slotMinTime
ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
return ms < tDateProfile.timeWindowMs; // before the slotMaxTime?
}
return true;
}
function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
// make sure labelInterval doesn't exceed the max number of cells
if (tDateProfile.labelInterval) {
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
if (labelCnt > config.MAX_TIMELINE_SLOTS) {
console.warn('slotHeaderInterval results in too many cells');
tDateProfile.labelInterval = null;
}
}
// make sure slotDuration doesn't exceed the maximum number of cells
if (tDateProfile.slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
if (slotCnt > config.MAX_TIMELINE_SLOTS) {
console.warn('slotDuration results in too many cells');
tDateProfile.slotDuration = null;
}
}
// make sure labelInterval is a multiple of slotDuration
if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
const slotsPerLabel = wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
if (slotsPerLabel === null || slotsPerLabel < 1) {
console.warn('slotHeaderInterval must be a multiple of slotDuration');
tDateProfile.slotDuration = null;
}
}
}
function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { labelInterval } = tDateProfile;
if (!labelInterval) {
// compute based off the slot duration
// find the largest label interval with an acceptable slots-per-label
let input;
if (tDateProfile.slotDuration) {
for (input of STOCK_SUB_DURATIONS) {
const tryLabelInterval = createDuration(input);
const slotsPerLabel = wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
labelInterval = tryLabelInterval;
break;
}
}
// use the slot duration as a last resort
if (!labelInterval) {
labelInterval = tDateProfile.slotDuration;
}
// compute based off the view's duration
// find the largest label interval that yields the minimum number of labels
}
else {
for (input of STOCK_SUB_DURATIONS) {
labelInterval = createDuration(input);
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
if (labelCnt >= MIN_AUTO_LABELS) {
break;
}
}
}
tDateProfile.labelInterval = labelInterval;
}
return labelInterval;
}
function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { slotDuration } = tDateProfile;
if (!slotDuration) {
const labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
// compute based off the label interval
// find the largest slot duration that is different from labelInterval, but still acceptable
for (let input of STOCK_SUB_DURATIONS) {
const trySlotDuration = createDuration(input);
const slotsPerLabel = wholeDivideDurations(labelInterval, trySlotDuration);
if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
slotDuration = trySlotDuration;
break;
}
}
// only allow the value if it won't exceed the view's # of slots limit
if (slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
if (slotCnt > MAX_AUTO_CELLS) {
slotDuration = null;
}
}
// use the label interval as a last resort
if (!slotDuration) {
slotDuration = labelInterval;
}
tDateProfile.slotDuration = slotDuration;
}
return slotDuration;
}
function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
let format1;
let format2;
const { labelInterval } = tDateProfile;
const { currentRange } = dateProfile;
let unit = greatestDurationDenominator(labelInterval).unit;
const weekNumbersVisible = allOptions.weekNumbers;
let format0 = (format1 = (format2 = null));
// NOTE: weekNumber computation function wont work
if ((unit === 'week') && !weekNumbersVisible) {
unit = 'day';
}
switch (unit) {
case 'year':
format0 = { year: 'numeric' }; // '2015'
break;
case 'month':
if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { month: 'short' }; // 'Jan'
break;
case 'week':
if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { week: 'narrow' }; // 'Wk4'
break;
case 'day':
if (dateEnv.diffWholeYears(currentRange.start, currentRange.end) > 1) {
format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
}
else if (dateEnv.diffWholeMonths(currentRange.start, currentRange.end) > 1) {
format0 = { month: 'long' }; // 'January'
}
if (weekNumbersVisible) {
format1 = { week: 'short' }; // 'Wk 4'
}
format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
break;
case 'hour':
if (weekNumbersVisible) {
format0 = { week: 'short' }; // 'Wk 4'
}
if (diffWholeDays(currentRange.start, currentRange.end) > 1) {
format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
}
format2 = {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'short',
};
break;
case 'minute':
// sufficiently large number of different minute cells?
if ((asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = {
hour: 'numeric',
meridiem: 'short',
};
format1 = (params) => (':' + padStart(params.date.minute, 2) // ':30'
);
}
else {
format0 = {
hour: 'numeric',
minute: 'numeric',
meridiem: 'short',
};
}
break;
case 'second':
// sufficiently large number of different second cells?
if ((asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
format1 = (params) => (':' + padStart(params.date.second, 2) // ':30'
);
}
else {
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
}
break;
case 'millisecond':
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
format1 = (params) => ('.' + padStart(params.millisecond, 3));
break;
}
return [].concat(format0 || [], format1 || [], format2 || []);
}
function buildCellRows(tDateProfile, dateEnv, majorUnit) {
let slotDates = tDateProfile.slotDates;
let formats = tDateProfile.headerFormats;
let cellRows = formats.map(() => []); // indexed by row,col
let slotAsDays = asCleanDays(tDateProfile.slotDuration);
let guessedSlotUnit = slotAsDays === 7 ? 'week' :
slotAsDays === 1 ? 'day' :
null;
// specifically for navclicks
let rowUnitsFromFormats = formats.map((format) => (format.getSmallestUnit ? format.getSmallestUnit() : null));
// builds cellRows and slotCells
for (let i = 0; i < slotDates.length; i += 1) {
let date = slotDates[i];
for (let row = 0; row < formats.length; row += 1) {
let format = formats[row];
let rowCells = cellRows[row];
let leadingCell = rowCells[rowCells.length - 1];
let isLastRow = row === formats.length - 1;
let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
let isMajor = isMajorUnit(date, majorUnit, dateEnv);
let newCell = null;
let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
if (isSuperRow) {
let [text] = dateEnv.format(date, format);
if (!leadingCell || (leadingCell.text !== text)) {
newCell = buildCellObject(date, isMajor, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
}
else if (!leadingCell ||
isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
let [text] = dateEnv.format(date, format);
newCell = buildCellObject(date, isMajor, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
if (newCell) {
rowCells.push(newCell);
}
}
}
return cellRows;
}
function buildCellObject(date, isMajor, text, rowUnit) {
return { date, isMajor, text, rowUnit, colspan: 1 }; // colspan mutated later
}
class TimelineSlatCell extends BaseComponent {
constructor() {
super(...arguments);
// memo
this.getDateMeta = memoize(getDateMeta);
}
render() {
let { props, context } = this;
let { dateEnv, options } = context;
let { date, tDateProfile, isMajor } = props;
let dateMeta = this.getDateMeta(props.date, dateEnv, props.dateProfile, props.todayRange, props.nowDate);
let isMinor = tDateProfile.isTimeScale &&
!isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval));
let renderProps = Object.assign(Object.assign({}, dateMeta), { isMajor,
isMinor, view: context.viewApi });
return (createElement(ContentContainer, { tag: "div", className: joinClassNames(classNames.tight, classNames.alignStart, // shrinks width of InnerContent
props.borderStart ? classNames.borderOnlyS : classNames.borderNone, classNames.internalTimelineSlot), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
omitTimeZoneOffset: true,
omitTime: !tDateProfile.isTimeScale,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.width,
}, renderProps: renderProps, generatorName: undefined, classNameGenerator: options.slotLaneClass, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }));
}
}
class TimelineSlats extends BaseComponent {
render() {
let { props } = this;
let { tDateProfile, slotWidth } = props;
let { slotDates, slotDatesMajor } = tDateProfile;
return (createElement("div", { "aria-hidden": true, className: joinClassNames(classNames.flexRow, classNames.fill), style: { height: props.height } }, slotDates.map((slotDate, i) => {
let key = slotDate.toISOString();
return (createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isMajor: slotDatesMajor[i], borderStart: Boolean(i),
// dimensions
width: slotWidth }));
})));
}
}
class TimelineHeaderCell extends BaseComponent {
constructor() {
super(...arguments);
// memo
this.getDateMeta = memoize(getDateMeta);
// ref
this.innerWrapperElRef = createRef();
}
render() {
let { props, state, context } = this;
let { dateEnv, options } = context;
let { cell, dateProfile, tDateProfile } = props;
// the cell.rowUnit is f'd
// giving 'month' for a 3-day view
// workaround: to infer day, do NOT time
let dateMeta = this.getDateMeta(cell.date, dateEnv, dateProfile, props.todayRange, props.nowDate);
let hasNavLink = options.navLinks && !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
let isTime = tDateProfile.isTimeScale && !props.rowLevel; // HACK: faulty way of determining this
let renderProps = Object.assign(Object.assign({}, dateMeta), { level: props.rowLevel, isMajor: cell.isMajor, isMinor: false, isNarrow: false, isTime,
hasNavLink, text: cell.text, isFirst: props.isFirst, view: context.viewApi });
const { slotHeaderAlign } = options;
const align = this.align =
typeof slotHeaderAlign === 'function'
? slotHeaderAlign({ level: props.rowLevel, isTime })
: slotHeaderAlign;
const isSticky = this.isSticky =
props.rowLevel && options.slotHeaderSticky !== false;
let edgeCoord;
if (isSticky) {
if (align === 'center') {
if (state.innerWidth != null) {
edgeCoord = `calc(50% - ${state.innerWidth / 2}px)`;
}
}
else {
edgeCoord = (typeof options.slotHeaderSticky === 'number' ||
typeof options.slotHeaderSticky === 'string') ? options.slotHeaderSticky : 0;
}
}
return (createElement(ContentContainer, { tag: "div", className: joinArrayishClassNames(classNames.tight, classNames.flexCol, props.isFirst ? classNames.borderNone : classNames.borderOnlyS, align === 'center' ? classNames.alignCenter :
align === 'end' ? classNames.alignEnd :
classNames.alignStart, classNames.internalTimelineSlot), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
omitTime: !tDateProfile.isTimeScale,
omitTimeZoneOffset: true,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.slotWidth != null
? props.slotWidth * cell.colspan
: undefined,
}, renderProps: renderProps, generatorName: "slotHeaderContent", customGenerator: options.slotHeaderContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotHeaderClass, didMount: options.slotHeaderDidMount, willUnmount: options.slotHeaderWillUnmount }, (InnerContent) => (createElement("div", { ref: this.innerWrapperElRef, className: joinClassNames(classNames.flexCol, classNames.rigid, isSticky && classNames.sticky), style: {
left: edgeCoord,
right: edgeCoord,
} },
createElement(InnerContent, { tag: 'div', attrs: hasNavLink
// not tabbable because parent is aria-hidden
? buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
: {} // don't bother with aria-hidden because parent already hidden
, className: generateClassName(options.slotHeaderInnerClass, renderProps) })))));
}
componentDidMount() {
const { props } = this;
const innerWrapperEl = this.innerWrapperElRef.current; // TODO: make dynamic with useEffect
this.disconnectSize = watchSize(innerWrapperEl, (width, height) => {
setRef(props.innerWidthRef, width);
setRef(props.innerHeightRef, height);
if (this.align === 'center' && this.isSticky) {
this.setState({ innerWidth: width });
}
});
}
componentWillUnmount() {
const { props } = this;
this.disconnectSize();
setRef(props.innerWidthRef, null);
setRef(props.innerHeightRef, null);
}
}
// Utils
// -------------------------------------------------------------------------------------------------
function renderInnerContent(renderProps) {
return renderProps.text;
}
class TimelineHeaderRow extends BaseComponent {
constructor() {
super(...arguments);
// refs
this.innerWidthRefMap = new RefMap(() => {
afterSize(this.handleInnerWidths);
});
this.innerHeightRefMap = new RefMap(() => {
afterSize(this.handleInnerHeights);
});
this.handleInnerWidths = () => {
const innerWidthMap = this.innerWidthRefMap.current;
let max = 0;
for (const innerWidth of innerWidthMap.values()) {
max = Math.max(max, innerWidth);
}
// TODO: ensure not equal?
setRef(this.props.innerWidthRef, max);
};
this.handleInnerHeights = () => {
const innerHeightMap = this.innerHeightRefMap.current;
let max = 0;
for (const innerHeight of innerHeightMap.values()) {
max = Math.max(max, innerHeight);
}
// TODO: ensure not equal?
setRef(this.props.innerHeighRef, max);
this.setState({ innerHeight: max });
};
}
render() {
const { props, innerWidthRefMap, innerHeightRefMap, state, context } = this;
const { options } = context;
return (createElement("div", { className: joinArrayishClassNames(options.slotHeaderRowClass, classNames.flexRow, classNames.grow, props.rowLevel // not the last row?
? classNames.borderOnlyB
: classNames.borderNone), style: {
// we assign height because we allow cells to have distorted heights for visual effect
// but we still want to keep the overall extrenal mass
height: state.innerHeight,
} }, props.cells.map((cell, cellI) => {
// TODO: make this part of the cell obj?
// TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
// TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
// TODO: use rowUnit to key the Row itself?
const key = cell.rowUnit + ':' + cell.date.toISOString();
return (createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isFirst: cellI === 0,
// refs
innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
// dimensions
slotWidth: props.slotWidth }));
})));
}
componentWillUnmount() {
setRef(this.props.innerWidthRef, null);
setRef(this.props.innerHeighRef, null);
}
}
// Timeline-specific
// -------------------------------------------------------------------------------------------------
const MIN_SLOT_WIDTH = 30; // for real
/*
TODO: DRY with computeSlatHeight?
*/
function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
if (labelInnerWidth == null || viewportWidth == null) {
return [undefined, undefined, false];
}
slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
const slatTryWidth = viewportWidth / slatCnt;
let slotLiquid;
let slatWidth;
if (slatTryWidth >= slatMinWidth) {
slotLiquid = true;
slatWidth = slatTryWidth;
}
else {
slotLiquid = false;
slatWidth = Math.max(slatMinWidth, slatTryWidth);
}
return [slatWidth * slatCnt, slatWidth, slotLiquid];
}
function timeToCoord(// pixels
time, dateEnv, dateProfile, tDateProfile, slowWidth) {
let date = dateEnv.add(dateProfile.activeRange.start, time);
if (!tDateProfile.isTimeScale) {
date = startOfDay(date);
}
return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
}
function dateToCoord(// pixels
date, dateEnv, tDateProfile, slotWidth) {
let snapCoverage = computeDateSnapCoverage$1(date, tDateProfile, dateEnv);
let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
return slotCoverage * slotWidth;
}
/*
returned value is between 0 and the number of snaps
*/
function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
class TimelineNowIndicatorLine extends BaseComponent {
render() {
const { props, context } = this;
const xStyle = props.slotWidth == null
? {}
: {
insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth)
};
return (createElement("div", { className: classNames.fill, style: {
zIndex: 2,
pointerEvents: 'none', // TODO: className
} },
createElement(NowIndicatorLineContainer, { className: joinClassNames(classNames.fillY, classNames.noMarginY, classNames.borderlessY), style: xStyle, date: props.nowDate }),
createElement("div", { className: joinClassNames(classNames.flexCol, // better for negative margins
classNames.fillY), style: xStyle },
createElement("div", {
// stickiness on NowIndicatorDot misbehaves b/c of negative marginss
className: classNames.stickyT },
createElement(NowIndicatorDot, null)))));
}
}
/*
TODO: DRY with other NowIndicator components
*/
class TimelineNowIndicatorArrow extends BaseComponent {
render() {
const { props, context } = this;
const xStyle = props.slotWidth == null
? {}
: {
insetInlineStart: dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth)
};
return (createElement("div", {
// crop any overflow that the arrow/line might cause
// TODO: just do this on the entire canvas within the scroller
className: joinClassNames(classNames.fill, classNames.crop), style: {
zIndex: 2,
pointerEvents: 'none', // TODO: className
} },
createElement(NowIndicatorHeaderContainer, { className: classNames.abs, style: xStyle, date: props.nowDate })));
}
}
function getTimelineSlotEl(parentEl, index) {
return parentEl.querySelectorAll(`.${classNames.internalTimelineSlot}`)[index];
}
/*
TODO: rename this file!
*/
// returned value is between 0 and the number of snaps
function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
class TimelineLaneSlicer extends Slicer {
sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
let segs = [];
// protect against when the span is entirely in an invalid date region
if (computeDateSnapCoverage(normalRange.start, tDateProfile, dateEnv)
< computeDateSnapCoverage(normalRange.end, tDateProfile, dateEnv)) {
// intersect the footprint's range with the grid's range
let slicedRange = intersectRanges(normalRange, tDateProfile.normalizedRange);
if (slicedRange) {
segs.push({
startDate: slicedRange.start,
endDate: slicedRange.end,
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
&& isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
&& isValidDate(addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
});
}
}
return segs;
}
}
const DEFAULT_TIME_FORMAT = createFormatter({
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'narrow',
});
class TimelineEvent extends BaseComponent {
render() {
let { props } = this;
return (createElement(StandardEvent, Object.assign({}, props, { display: 'row', defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
}
}
class TimelineLaneMoreLink extends BaseComponent {
render() {
let { props } = this;
let { hiddenSegs, resourceId } = props;
let dateSpanProps = resourceId ? { resourceId } : {};
return (createElement(MoreLinkContainer, { display: 'row', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, isNarrow: false, isMicro: false, popoverContent: () => (createElement(Fragment, null, hiddenSegs.map((seg) => {
let { eventRange } = seg;
let { instanceId } = eventRange.instance;
let isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
let isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
let isInvisible = isDragging || isResizing;
return (createElement("div", { key: instanceId, style: { visibility: isInvisible ? 'hidden' : undefined } },
createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: false, isSelected: instanceId === props.eventSelection }, getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
}))) }));
}
}
function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const res = {};
for (const seg of segs) {
res[getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
}
return res;
}
function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
let size = endCoord - startCoord;
if (segMinWidth) {
size = Math.max(size, segMinWidth);
}
return { start: startCoord, size };
}
function computeFgSegPlacements(// mostly horizontals
segs, segHorizontals, // TODO: use Map
segHeights, // keyed by instanceId
hiddenGroupHeights, strictOrder, maxDepth) {
const segRanges = [];
// isn't it true that there will either be ALL hcoords or NONE? can optimize
for (const seg of segs) {
const hcoords = segHorizontals[getEventKey(seg)];
if (hcoords) {
segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
}
}
const hierarchy = new SegHierarchy(segRanges, (seg) => segHeights.get(getEventKey(seg)), strictOrder, undefined, // maxCoord
maxDepth);
const segTops = new Map();
hierarchy.traverseSegs((seg, segTop) => {
segTops.set(getEventKey(seg), segTop);
});
const { hiddenSegs } = hierarchy;
let totalHeight = 0;
for (const segRange of segRanges) {
const segKey = getEventKey(segRange);
const segHeight = segHeights.get(segKey);
const segTop = segTops.get(segKey);
if (segHeight != null) {
if (segTop != null) {
totalHeight = Math.max(totalHeight, segTop + segHeight);
}
}
}
const hiddenGroups = groupIntersectingSegs(hiddenSegs);
const hiddenGroupTops = new Map();
// HACK for hiddenGroup findInsertion() call
hierarchy.strictOrder = true;
for (const hiddenGroup of hiddenGroups) {
const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
hiddenGroupTops.set(hiddenGroup.key, top);
totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
}
return [
segTops,
hiddenGroups,
hiddenGroupTops,
totalHeight,
];
}
/*
TODO: make DRY with other Event Harnesses
*/
class TimelineEventHarness extends Component {
constructor() {
super(...arguments);
// ref
this.rootElRef = createRef();
}
render() {
const { props } = this;
return (createElement("div", { className: classNames.abs, style: props.style, ref: this.rootElRef }, props.children));
}
componentDidMount() {
const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
this.disconnectHeight = watchHeight(rootEl, (height) => {
setRef(this.props.heightRef, height);
});
}
componentWillUnmount() {
this.disconnectHeight();
setRef(this.props.heightRef, null);
}
}
class TimelineFg extends BaseComponent {
constructor() {
super(...arguments);
// memo
this.sortEventSegs = memoize(sortEventSegs);
// refs
this.segHeightRefMap = new RefMap(() => {
afterSize(this.handleSegHeights);
});
this.moreLinkHeightRefMap = new RefMap(() => {
afterSize(this.handleMoreLinkHeights);
});
this.handleMoreLinkHeights = () => {
this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
};
this.handleSegHeights = () => {
this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
};
}
/*
TODO: lots of memoization needed here!
*/
render() {
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
let { options } = context;
let { tDateProfile } = props;
let mirrorSegs = (props.eventDrag ? props.eventDrag.segs : null) ||
(props.eventResize ? props.eventResize.segs : null) ||
[];
let fgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
let fgSegHorizontals = props.slotWidth != null
? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {};
let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
this.totalHeight = totalHeight;
return (createElement("div", { className: joinClassNames(classNames.rel, classNames.noShrink), style: {
height: totalHeight,
} },
this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, hiddenGroups, hiddenGroupTops,
/* isMirror = */ false),
this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {}, fgSegTops,
/* hiddenGroups = */ [],
/* hiddenGroupTops = */ new Map(),
/* isMirror = */ true)));
}
renderFgSegs(segs, segHorizontals, segTops, hiddenGroups, hiddenGroupTops, isMirror) {
let { props, segHeightRefMap, moreLinkHeightRefMap } = this;
return (createElement(Fragment, null,
segs.map((seg) => {
const { eventRange } = seg;
const { instanceId } = eventRange.instance;
const segTop = segTops.get(instanceId);
const segHorizontalMaybe = segHorizontals[instanceId];
const segHorizontal = segHorizontalMaybe || {};
const isDragging = Boolean(props.eventDrag && props.eventDrag.affectedInstances[instanceId]);
const isResizing = Boolean(props.eventResize && props.eventResize.affectedInstances[instanceId]);
const isInvisible = !isMirror && (isDragging || isResizing || !segHorizontalMaybe || segTop == null);
return (createElement(TimelineEventHarness, { key: instanceId, style: {
visibility: isInvisible ? 'hidden' : undefined,
zIndex: 1,
top: segTop || 0,
insetInlineStart: segHorizontal.start,
width: segHorizontal.size,
}, heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isMirror: isMirror, isSelected: instanceId === props.eventSelection }, getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
}),
hiddenGroups.map((hiddenGroup) => (createElement(TimelineEventHarness, { key: hiddenGroup.key, style: {
top: hiddenGroupTops.get(hiddenGroup.key) || 0,
insetInlineStart: hiddenGroup.start,
width: hiddenGroup.end - hiddenGroup.start,
}, heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
createElement(TimelineLaneMoreLink, { hiddenSegs: hiddenGroup.segs, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection, resourceId: props.resourceId }))))));
}
/*
componentDidMount(): void {
// might want to do firedTotalHeight, but won't be ready on first render
}
*/
componentDidUpdate() {
if (this.totalHeight !== this.firedTotalHeight) {
this.firedTotalHeight = this.totalHeight;
setRef(this.props.heightRef, this.totalHeight);
}
}
componentWillUnmount() {
setRef(this.props.heightRef, null);
}
}
class TimelineBg extends BaseComponent {
render() {
let { props } = this;
let highlightSeg = [].concat(props.eventResizeSegs || [], props.dateSelectionSegs);
return (createElement(Fragment, null,
this.renderSegs(props.businessHourSegs || [], 'non-business'),
this.renderSegs(props.bgEventSegs || [], 'bg-event'),
this.renderSegs(highlightSeg, 'highlight')));
}
renderSegs(segs, fillType) {
let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
let { dateEnv, options } = this.context;
return (createElement(Fragment, null, segs.map((seg) => {
let hStyle = {};
if (slotWidth != null) {
let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
hStyle = { insetInlineStart: segHorizontal.start, width: segHorizontal.size };
}
return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: classNames.fillY, style: hStyle }, fillType === 'bg-event' ? (createElement(BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isVertical: false }, getEventRangeMeta(seg.eventRange, todayRange, nowDate)))) : (renderFill(fillType, options))));
})));
}
}
class TimelineView extends DateComponent {
constructor() {
super(...arguments);
// memoized
this.buildTimelineDateProfile = memoize(buildTimelineDateProfile);
this.computeSlotWidth = memoize(computeSlotWidth);
// refs
this.headerScrollerRef = createRef();
this.bodyScrollerRef = createRef();
this.footerScrollerRef = createRef();
this.headerRowInnerWidthMap = new RefMap(() => {
afterSize(this.handleSlotInnerWidths);
});
this.scrollTime = null;
this.slicer = new TimelineLaneSlicer();
// Sizing
// -----------------------------------------------------------------------------------------------
this.handleSlotInnerWidths = () => {
const headerSlotInnerWidth = this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1);
if (headerSlotInnerWidth != null && headerSlotInnerWidth !== this.state.slotInnerWidth) {
this.setState({ slotInnerWidth: headerSlotInnerWidth });
}
};
this.handleTotalWidth = (totalWidth) => {
this.setState({
totalWidth,
});
};
this.handleClientWidth = (clientWidth) => {
this.setState({
clientWidth,
});
};
this.handleTimeScrollRequest = (scrollTime) => {
this.scrollTime = scrollTime;
this.applyTimeScroll();
};
this.handleTimeScrollEnd = (isUser) => {
if (isUser) {
this.scrollTime = null;
}
};
// Hit System
// -----------------------------------------------------------------------------------------------
this.handeBodyEl = (el) => {
this.bodyEl = el;
if (el) {
this.context.registerInteractiveComponent(this, { el });
}
else {
this.context.unregisterInteractiveComponent(this);
}
};
}
render() {
const { props, state, context } = this;
const { options } = context;
const { totalWidth, clientWidth } = state;
const endScrollbarWidth = (totalWidth != null && clientWidth != null)
? totalWidth - clientWidth
: undefined;
/* date */
const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
const { cellRows } = tDateProfile;
const timerUnit = greatestDurationDenominator(tDateProfile.slotDuration).unit;
/* table settings */
const verticalScrolling = !props.forPrint && !getIsHeightAuto(options);
const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
const stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(options);
/* table positions */
const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
clientWidth);
this.slotWidth = slotWidth;
/* sliced */
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...
props.dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
return (createElement(NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
const enableNowIndicator = // TODO: DRY
options.nowIndicator &&
slotWidth != null &&
rangeContainsMarker(props.dateProfile.currentRange, nowDate);
return (createElement(ViewContainer, { viewSpec: context.viewSpec, className: joinArrayishClassNames(
// HACK for Safari print-mode, where noScrollbars won't take effect for
// the below Scrollers if they have liquid flex height
!props.forPrint && classNames.flexCol, props.className, options.tableClass, classNames.isolate), borderlessX: props.borderlessX, borderlessTop: props.borderlessTop, borderlessBottom: props.borderlessBottom, noEdgeEffects: props.noEdgeEffects },
createElement("div", { className: joinClassNames(generateClassName(options.tableHeaderClass, {
isSticky: stickyHeaderDates,
}), props.borderlessX && classNames.borderlessX, classNames.flexCol, stickyHeaderDates && classNames.tableHeaderSticky), style: {
zIndex: 1,
} },
createElement(Scroller, { horizontal: true, hideScrollbars: true, className: classNames.flexRow, ref: this.headerScrollerRef },
createElement("div", {
// TODO: DRY
className: joinClassNames(classNames.rel, // origin for now-indicator
canvasWidth == null && classNames.liquid), style: { width: canvasWidth } },
cellRows.map((cells, rowIndex) => {
const rowLevel = cellRows.length - rowIndex - 1;
return (createElement(TimelineHeaderRow, { key: rowIndex, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, cells: cells, slotWidth: slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowIndex) }));
}),
enableNowIndicator && (createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
Boolean(endScrollbarWidth) && (createElement("div", { className: joinArrayishClassNames(generateClassName(options.fillerClass, { isHeader: true }), classNames.borderOnlyS), style: { minWidth: endScrollbarWidth } }))),
createElement("div", { className: generateClassName(options.slotHeaderDividerClass, {
isHeader: true,
options: { dayMinWidth: options.dayMinWidth },
}) })),
createElement(Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: stickyFooterScrollbar ||
props.forPrint // prevents blank space in print-view on Safari
, className: joinArrayishClassNames(options.tableBodyClass, props.borderlessX && classNames.borderlessX, stickyHeaderDates && classNames.borderlessTop, (stickyHeaderDates || props.noEdgeEffects) && classNames.noEdgeEffects, classNames.flexCol, verticalScrolling && classNames.liquid), style: {
zIndex: 0,
}, ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth },
createElement("div", { "aria-label": options.eventsHint, className: joinClassNames(classNames.rel, // for canvas origin?
classNames.grow), style: { width: canvasWidth }, ref: this.handeBodyEl },
createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// dimensions
slotWidth: slotWidth }),
createElement(TimelineBg, { tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// content
bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : null,
// dimensions
slotWidth: slotWidth }),
createElement("div", { className: joinArrayishClassNames(options.timelineTopClass) }),
createElement(TimelineFg, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// content
fgEventSegs: slicedProps.fgEventSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
// dimensions
slotWidth: slotWidth }),
createElement("div", { className: joinArrayishClassNames(options.timelineBottomClass) }),
enableNowIndicator && (createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
Boolean(stickyFooterScrollbar) && (createElement(FooterScrollbar, { isSticky: true, canvasWidth: canvasWidth, scrollerRef: this.footerScrollerRef })),
createElement(Ruler, { widthRef: this.handleTotalWidth })));
}));
}
// Lifecycle
// -----------------------------------------------------------------------------------------------
componentDidMount() {
this.syncedScroller = new ScrollerSyncer(true); // horizontal=true
this.updateSyncedScroller();
this.resetScroll();
this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
}
componentDidUpdate(prevProps) {
this.updateSyncedScroller();
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
this.resetScroll();
}
else {
// TODO: inefficient to update so often
this.applyTimeScroll();
}
}
componentWillUnmount() {
this.syncedScroller.destroy();
this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
}
// Scrolling
// -----------------------------------------------------------------------------------------------
updateSyncedScroller() {
this.syncedScroller.handleChildren([
this.headerScrollerRef.current,
this.bodyScrollerRef.current,
this.footerScrollerRef.current
]);
}
resetScroll() {
this.handleTimeScrollRequest(this.context.options.scrollTime);
}
applyTimeScroll() {
const { props, context, tDateProfile, scrollTime, slotWidth } = this;
if (scrollTime != null && slotWidth != null) {
let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
if (x) {
x += 1; // overcome border. TODO: DRY this up
}
this.syncedScroller.scrollTo({ x });
}
}
queryHit(isRtl, positionLeft, positionTop, elWidth, elHeight) {
const { props, context, tDateProfile, slotWidth } = this;
const { dateEnv } = context;
if (slotWidth) {
const x = isRtl ? elWidth - positionLeft : positionLeft;
const slatIndex = Math.floor(x / slotWidth);
const slatX = slatIndex * slotWidth;
const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
// TODO: generalize this coord stuff to TimeGrid?
let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
let endCoord = startCoord + snapWidth;
let left, right;
if (isRtl) {
left = elWidth - endCoord;
right = elWidth - startCoord;
}
else {
left = startCoord;
right = endCoord;
}
return {
dateProfile: props.dateProfile,
dateSpan: {
range: { start: startDate, end: endDate },
allDay: !tDateProfile.isTimeScale,
},
rect: {
left,
right,
top: 0,
bottom: elHeight,
},
getDayEl: () => getTimelineSlotEl(this.bodyEl, slatIndex),
layer: 0,
};
}
return null;
}
}
export { TimelineBg, TimelineFg, TimelineHeaderRow, TimelineLaneSlicer, TimelineNowIndicatorArrow, TimelineNowIndicatorLine, TimelineSlats, TimelineView, buildTimelineDateProfile, computeSlotWidth, getTimelineSlotEl, timeToCoord };

Sorry, the diff of this file is too big to display

/*!
FullCalendar Timeline Plugin v7.0.0-beta.5
Docs & License: https://fullcalendar.io/docs/timeline-view-no-resources
(c) 2025 Adam Shaw
*/
FullCalendar.Timeline=function(e,t,n,i,a,s,r){"use strict";function l(e){return e&&e.__esModule?e:{default:e}}var o=l(n),d=l(a);i.config.MAX_TIMELINE_SLOTS=1e3;const c=[{years:1},{months:1},{days:1},{hours:1},{minutes:30},{minutes:15},{minutes:10},{minutes:5},{minutes:1},{seconds:30},{seconds:15},{seconds:10},{seconds:5},{seconds:1},{milliseconds:500},{milliseconds:100},{milliseconds:10},{milliseconds:1}];function u(e,t,n,a){let s={labelInterval:n.slotHeaderInterval,slotDuration:n.slotDuration};!function(e,t,n){const{currentRange:a}=t;if(e.labelInterval){n.countDurationsBetween(a.start,a.end,e.labelInterval)>i.config.MAX_TIMELINE_SLOTS&&(console.warn("slotHeaderInterval results in too many cells"),e.labelInterval=null)}if(e.slotDuration){n.countDurationsBetween(a.start,a.end,e.slotDuration)>i.config.MAX_TIMELINE_SLOTS&&(console.warn("slotDuration results in too many cells"),e.slotDuration=null)}if(e.labelInterval&&e.slotDuration){const t=i.wholeDivideDurations(e.labelInterval,e.slotDuration);(null===t||t<1)&&(console.warn("slotHeaderInterval must be a multiple of slotDuration"),e.slotDuration=null)}}(s,e,t),f(s,e,t),function(e,t,n){const{currentRange:a}=t;let{slotDuration:s}=e;if(!s){const r=f(e,t,n);for(let e of c){const t=i.createDuration(e),n=i.wholeDivideDurations(r,t);if(null!==n&&n>1&&n<=6){s=t;break}}if(s){n.countDurationsBetween(a.start,a.end,s)>200&&(s=null)}s||(s=r),e.slotDuration=s}}(s,e,t);let r=n.slotHeaderFormat,l=Array.isArray(r)?r:null!=r?[r]:function(e,t,n,a){let s,r;const{labelInterval:l}=e,{currentRange:o}=t;let d=i.greatestDurationDenominator(l).unit;const c=a.weekNumbers;let u=s=r=null;"week"!==d||c||(d="day");switch(d){case"year":u={year:"numeric"};break;case"month":n.diffWholeYears(o.start,o.end)>1&&(u={year:"numeric"}),s={month:"short"};break;case"week":n.diffWholeYears(o.start,o.end)>1&&(u={year:"numeric"}),s={week:"narrow"};break;case"day":n.diffWholeYears(o.start,o.end)>1?u={year:"numeric",month:"long"}:n.diffWholeMonths(o.start,o.end)>1&&(u={month:"long"}),c&&(s={week:"short"}),r={weekday:"narrow",day:"numeric"};break;case"hour":c&&(u={week:"short"}),i.diffWholeDays(o.start,o.end)>1&&(s={weekday:"short",day:"numeric",month:"numeric",omitCommas:!0}),r={hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"short"};break;case"minute":i.asRoughMinutes(l)/60>=6?(u={hour:"numeric",meridiem:"short"},s=e=>":"+i.padStart(e.date.minute,2)):u={hour:"numeric",minute:"numeric",meridiem:"short"};break;case"second":i.asRoughSeconds(l)/60>=6?(u={hour:"numeric",minute:"2-digit",meridiem:"lowercase"},s=e=>":"+i.padStart(e.date.second,2)):u={hour:"numeric",minute:"2-digit",second:"2-digit",meridiem:"lowercase"};break;case"millisecond":u={hour:"numeric",minute:"2-digit",second:"2-digit",meridiem:"lowercase"},s=e=>"."+i.padStart(e.millisecond,3)}return[].concat(u||[],s||[],r||[])}(s,e,t,n);s.headerFormats=l.map(e=>i.createFormatter(e)),s.isTimeScale=Boolean(s.slotDuration.milliseconds);let o=null;if(!s.isTimeScale){const e=i.greatestDurationDenominator(s.slotDuration).unit;/year|month|week/.test(e)&&(o=e)}s.largeUnit=o;let d,u,p=n.snapDuration;p&&(d=i.createDuration(p),u=i.wholeDivideDurations(s.slotDuration,d)),null==u&&(d=s.slotDuration,u=1),s.snapDuration=d,s.snapsPerSlot=u;let S=i.asRoughMs(e.slotMaxTime)-i.asRoughMs(e.slotMinTime),v=h(e.renderRange.start,s,t),D=h(e.renderRange.end,s,t);s.isTimeScale&&(v=t.add(v,e.slotMinTime),D=t.add(i.addDays(D,-1),e.slotMaxTime)),s.timeWindowMs=S,s.normalizedRange={start:v,end:D};let R=[],y=[],w=v,E=i.computeMajorUnit(e,t);for(;w<D;)m(w,s,e,a)&&(R.push(w),y.push(i.isMajorUnit(w,E,t))),w=t.add(w,s.slotDuration);s.slotDates=R,s.slotDatesMajor=y;let M=-1,b=0;const T=[],C=[];for(w=v;w<D;)m(w,s,e,a)?(M+=1,T.push(M),C.push(b)):T.push(M+.5),w=t.add(w,s.snapDuration),b+=1;return s.snapDiffToIndex=T,s.snapIndexToDiff=C,s.snapCnt=M+1,s.slotCnt=s.snapCnt/s.snapsPerSlot,s.cellRows=function(e,t,n){let a=e.slotDates,s=e.headerFormats,r=s.map(()=>[]),l=i.asCleanDays(e.slotDuration),o=7===l?"week":1===l?"day":null,d=s.map(e=>e.getSmallestUnit?e.getSmallestUnit():null);for(let l=0;l<a.length;l+=1){let c=a[l];for(let a=0;a<s.length;a+=1){let l=s[a],u=r[a],h=u[u.length-1],m=a===s.length-1,f=s.length>1&&!m,p=i.isMajorUnit(c,n,t),S=null,v=d[a]||(m?o:null);if(f){let[e]=t.format(c,l);h&&h.text===e?h.colspan+=1:S=g(c,p,e,v)}else if(!h||i.isInt(t.countDurationsBetween(e.normalizedRange.start,c,e.labelInterval))){let[e]=t.format(c,l);S=g(c,p,e,v)}else h.colspan+=1;S&&u.push(S)}}return r}(s,t,E),s.slotsPerLabel=i.wholeDivideDurations(s.labelInterval,s.slotDuration),s}function h(e,t,n){let a=e;return t.isTimeScale||(a=i.startOfDay(a),t.largeUnit&&(a=n.startOf(a,t.largeUnit))),a}function m(e,t,n,a){if(a.isHiddenDay(e))return!1;if(t.isTimeScale){let a=i.startOfDay(e),s=e.valueOf()-a.valueOf()-i.asRoughMs(n.slotMinTime);return s=(s%864e5+864e5)%864e5,s<t.timeWindowMs}return!0}function f(e,t,n){const{currentRange:a}=t;let{labelInterval:s}=e;if(!s){let t;if(e.slotDuration){for(t of c){const n=i.createDuration(t),a=i.wholeDivideDurations(n,e.slotDuration);if(null!==a&&a<=6){s=n;break}}s||(s=e.slotDuration)}else for(t of c){s=i.createDuration(t);if(n.countDurationsBetween(a.start,a.end,s)>=18)break}e.labelInterval=s}return s}function g(e,t,n,i){return{date:e,isMajor:t,text:n,rowUnit:i,colspan:1}}class p extends i.BaseComponent{constructor(){super(...arguments),this.getDateMeta=i.memoize(i.getDateMeta)}render(){let{props:e,context:n}=this,{dateEnv:a,options:r}=n,{date:l,tDateProfile:o,isMajor:c}=e,u=this.getDateMeta(e.date,a,e.dateProfile,e.todayRange,e.nowDate),h=o.isTimeScale&&!i.isInt(a.countDurationsBetween(o.normalizedRange.start,e.date,o.labelInterval)),m=Object.assign(Object.assign({},u),{isMajor:c,isMinor:h,view:n.viewApi});return s.createElement(i.ContentContainer,{tag:"div",className:t.joinClassNames(d.default.tight,d.default.alignStart,e.borderStart?d.default.borderOnlyS:d.default.borderNone,d.default.internalTimelineSlot),attrs:Object.assign({"data-date":a.formatIso(l,{omitTimeZoneOffset:!0,omitTime:!o.isTimeScale})},u.isToday?{"aria-current":"date"}:{}),style:{width:e.width},renderProps:m,generatorName:void 0,classNameGenerator:r.slotLaneClass,didMount:r.slotLaneDidMount,willUnmount:r.slotLaneWillUnmount})}}class S extends i.BaseComponent{render(){let{props:e}=this,{tDateProfile:n,slotWidth:i}=e,{slotDates:a,slotDatesMajor:r}=n;return s.createElement("div",{"aria-hidden":!0,className:t.joinClassNames(d.default.flexRow,d.default.fill),style:{height:e.height}},a.map((t,a)=>{let l=t.toISOString();return s.createElement(p,{key:l,date:t,dateProfile:e.dateProfile,tDateProfile:n,nowDate:e.nowDate,todayRange:e.todayRange,isMajor:r[a],borderStart:Boolean(a),width:i})}))}}class v extends i.BaseComponent{constructor(){super(...arguments),this.getDateMeta=i.memoize(i.getDateMeta),this.innerWrapperElRef=s.createRef()}render(){let{props:e,state:n,context:a}=this,{dateEnv:r,options:l}=a,{cell:o,dateProfile:c,tDateProfile:u}=e,h=this.getDateMeta(o.date,r,c,e.todayRange,e.nowDate),m=l.navLinks&&!h.isDisabled&&o.rowUnit&&"time"!==o.rowUnit,f=u.isTimeScale&&!e.rowLevel,g=Object.assign(Object.assign({},h),{level:e.rowLevel,isMajor:o.isMajor,isMinor:!1,isNarrow:!1,isTime:f,hasNavLink:m,text:o.text,isFirst:e.isFirst,view:a.viewApi});const{slotHeaderAlign:p}=l,S=this.align="function"==typeof p?p({level:e.rowLevel,isTime:f}):p,v=this.isSticky=e.rowLevel&&!1!==l.slotHeaderSticky;let R;return v&&("center"===S?null!=n.innerWidth&&(R=`calc(50% - ${n.innerWidth/2}px)`):R="number"==typeof l.slotHeaderSticky||"string"==typeof l.slotHeaderSticky?l.slotHeaderSticky:0),s.createElement(i.ContentContainer,{tag:"div",className:i.joinArrayishClassNames(d.default.tight,d.default.flexCol,e.isFirst?d.default.borderNone:d.default.borderOnlyS,"center"===S?d.default.alignCenter:"end"===S?d.default.alignEnd:d.default.alignStart,d.default.internalTimelineSlot),attrs:Object.assign({"data-date":r.formatIso(o.date,{omitTime:!u.isTimeScale,omitTimeZoneOffset:!0})},h.isToday?{"aria-current":"date"}:{}),style:{width:null!=e.slotWidth?e.slotWidth*o.colspan:void 0},renderProps:g,generatorName:"slotHeaderContent",customGenerator:l.slotHeaderContent,defaultGenerator:D,classNameGenerator:l.slotHeaderClass,didMount:l.slotHeaderDidMount,willUnmount:l.slotHeaderWillUnmount},e=>s.createElement("div",{ref:this.innerWrapperElRef,className:t.joinClassNames(d.default.flexCol,d.default.rigid,v&&d.default.sticky),style:{left:R,right:R}},s.createElement(e,{tag:"div",attrs:m?i.buildNavLinkAttrs(a,o.date,o.rowUnit,void 0,!1):{},className:i.generateClassName(l.slotHeaderInnerClass,g)})))}componentDidMount(){const{props:e}=this,t=this.innerWrapperElRef.current;this.disconnectSize=i.watchSize(t,(t,n)=>{i.setRef(e.innerWidthRef,t),i.setRef(e.innerHeightRef,n),"center"===this.align&&this.isSticky&&this.setState({innerWidth:t})})}componentWillUnmount(){const{props:e}=this;this.disconnectSize(),i.setRef(e.innerWidthRef,null),i.setRef(e.innerHeightRef,null)}}function D(e){return e.text}class R extends i.BaseComponent{constructor(){super(...arguments),this.innerWidthRefMap=new i.RefMap(()=>{i.afterSize(this.handleInnerWidths)}),this.innerHeightRefMap=new i.RefMap(()=>{i.afterSize(this.handleInnerHeights)}),this.handleInnerWidths=()=>{const e=this.innerWidthRefMap.current;let t=0;for(const n of e.values())t=Math.max(t,n);i.setRef(this.props.innerWidthRef,t)},this.handleInnerHeights=()=>{const e=this.innerHeightRefMap.current;let t=0;for(const n of e.values())t=Math.max(t,n);i.setRef(this.props.innerHeighRef,t),this.setState({innerHeight:t})}}render(){const{props:e,innerWidthRefMap:t,innerHeightRefMap:n,state:a,context:r}=this,{options:l}=r;return s.createElement("div",{className:i.joinArrayishClassNames(l.slotHeaderRowClass,d.default.flexRow,d.default.grow,e.rowLevel?d.default.borderOnlyB:d.default.borderNone),style:{height:a.innerHeight}},e.cells.map((i,a)=>{const r=i.rowUnit+":"+i.date.toISOString();return s.createElement(v,{key:r,cell:i,rowLevel:e.rowLevel,dateProfile:e.dateProfile,tDateProfile:e.tDateProfile,todayRange:e.todayRange,nowDate:e.nowDate,isFirst:0===a,innerWidthRef:t.createRef(r),innerHeightRef:n.createRef(r),slotWidth:e.slotWidth})}))}componentWillUnmount(){i.setRef(this.props.innerWidthRef,null),i.setRef(this.props.innerHeighRef,null)}}function y(e,t,n,i,a){if(null==i||null==a)return[void 0,void 0,!1];const s=a/e;let r,l;return s>=(n=Math.max(n||0,(i+1)/t,30))?(r=!0,l=s):(r=!1,l=Math.max(n,s)),[l*e,l,r]}function w(e,t,n,a,s){let r=t.add(n.activeRange.start,e);return a.isTimeScale||(r=i.startOfDay(r)),E(r,t,a,s)}function E(e,t,n,a){return function(e,t,n){let a=n.countDurationsBetween(t.normalizedRange.start,e,t.snapDuration);if(a<0)return 0;if(a>=t.snapDiffToIndex.length)return t.snapCnt;let s=Math.floor(a),r=t.snapDiffToIndex[s];i.isInt(r)?r+=a-s:r=Math.ceil(r);return r}(e,n,t)/n.snapsPerSlot*a}class M extends i.BaseComponent{render(){const{props:e,context:n}=this,a=null==e.slotWidth?{}:{insetInlineStart:E(e.nowDate,n.dateEnv,e.tDateProfile,e.slotWidth)};return s.createElement("div",{className:d.default.fill,style:{zIndex:2,pointerEvents:"none"}},s.createElement(i.NowIndicatorLineContainer,{className:t.joinClassNames(d.default.fillY,d.default.noMarginY,d.default.borderlessY),style:a,date:e.nowDate}),s.createElement("div",{className:t.joinClassNames(d.default.flexCol,d.default.fillY),style:a},s.createElement("div",{className:d.default.stickyT},s.createElement(i.NowIndicatorDot,null))))}}class b extends i.BaseComponent{render(){const{props:e,context:n}=this,a=null==e.slotWidth?{}:{insetInlineStart:E(e.nowDate,n.dateEnv,e.tDateProfile,e.slotWidth)};return s.createElement("div",{className:t.joinClassNames(d.default.fill,d.default.crop),style:{zIndex:2,pointerEvents:"none"}},s.createElement(i.NowIndicatorHeaderContainer,{className:d.default.abs,style:a,date:e.nowDate}))}}function T(e,t){return e.querySelectorAll("."+d.default.internalTimelineSlot)[t]}function C(e,t,n){let a=n.countDurationsBetween(t.normalizedRange.start,e,t.snapDuration);if(a<0)return 0;if(a>=t.snapDiffToIndex.length)return t.snapCnt;let s=Math.floor(a),r=t.snapDiffToIndex[s];return i.isInt(r)?r+=a-s:r=Math.ceil(r),r}class I extends i.Slicer{sliceRange(e,t,n,a,s){let r=function(e,t,n){if(!t.isTimeScale&&(e=i.computeVisibleDayRange(e),t.largeUnit)){let i=e;((e={start:n.startOf(e.start,t.largeUnit),end:n.startOf(e.end,t.largeUnit)}).end.valueOf()!==i.end.valueOf()||e.end<=e.start)&&(e={start:e.start,end:n.add(e.end,t.slotDuration)})}return e}(e,a,s),l=[];if(C(r.start,a,s)<C(r.end,a,s)){let e=i.intersectRanges(r,a.normalizedRange);e&&l.push({startDate:e.start,endDate:e.end,isStart:e.start.valueOf()===r.start.valueOf()&&m(e.start,a,t,n),isEnd:e.end.valueOf()===r.end.valueOf()&&m(i.addMs(e.end,-1),a,t,n)})}return l}}const W=i.createFormatter({hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"narrow"});class P extends i.BaseComponent{render(){let{props:e}=this;return s.createElement(i.StandardEvent,Object.assign({},e,{display:"row",defaultTimeFormat:W,defaultDisplayEventTime:!e.isTimeScale}))}}class N extends i.BaseComponent{render(){let{props:e}=this,{hiddenSegs:t,resourceId:n}=e,a=n?{resourceId:n}:{};return s.createElement(i.MoreLinkContainer,{display:"row",allDayDate:null,segs:t,hiddenSegs:t,dateProfile:e.dateProfile,todayRange:e.todayRange,dateSpanProps:a,isNarrow:!1,isMicro:!1,popoverContent:()=>s.createElement(s.Fragment,null,t.map(t=>{let{eventRange:n}=t,{instanceId:a}=n.instance,r=Boolean(e.eventDrag&&e.eventDrag.affectedInstances[a]),l=Boolean(e.eventResize&&e.eventResize.affectedInstances[a]),o=r||l;return s.createElement("div",{key:a,style:{visibility:o?"hidden":void 0}},s.createElement(P,Object.assign({isTimeScale:e.isTimeScale,eventRange:n,isStart:t.isStart,isEnd:t.isEnd,isDragging:r,isResizing:l,isMirror:!1,isSelected:a===e.eventSelection},i.getEventRangeMeta(n,e.todayRange,e.nowDate))))}))})}}function x(e,t,n,a,s){const r={};for(const l of e)r[i.getEventKey(l)]=H(l,t,n,a,s);return r}function H(e,t,n,i,a){const s=E(e.startDate,n,i,a);let r=E(e.endDate,n,i,a)-s;return t&&(r=Math.max(r,t)),{start:s,size:r}}class k extends s.Component{constructor(){super(...arguments),this.rootElRef=s.createRef()}render(){const{props:e}=this;return s.createElement("div",{className:d.default.abs,style:e.style,ref:this.rootElRef},e.children)}componentDidMount(){const e=this.rootElRef.current;this.disconnectHeight=i.watchHeight(e,e=>{i.setRef(this.props.heightRef,e)})}componentWillUnmount(){this.disconnectHeight(),i.setRef(this.props.heightRef,null)}}class z extends i.BaseComponent{constructor(){super(...arguments),this.sortEventSegs=i.memoize(i.sortEventSegs),this.segHeightRefMap=new i.RefMap(()=>{i.afterSize(this.handleSegHeights)}),this.moreLinkHeightRefMap=new i.RefMap(()=>{i.afterSize(this.handleMoreLinkHeights)}),this.handleMoreLinkHeights=()=>{this.setState({moreLinkHeightRev:this.moreLinkHeightRefMap.rev})},this.handleSegHeights=()=>{this.setState({segHeightRev:this.segHeightRefMap.rev})}}render(){let{props:e,context:n,segHeightRefMap:a,moreLinkHeightRefMap:r}=this,{options:l}=n,{tDateProfile:o}=e,c=(e.eventDrag?e.eventDrag.segs:null)||(e.eventResize?e.eventResize.segs:null)||[],u=this.sortEventSegs(e.fgEventSegs,l.eventOrder),h=null!=e.slotWidth?x(u,l.eventMinWidth,n.dateEnv,o,e.slotWidth):{},[m,f,g,p]=function(e,t,n,a,s,r){const l=[];for(const n of e){const e=t[i.getEventKey(n)];e&&l.push(Object.assign(Object.assign({},n),{start:e.start,end:e.start+e.size}))}const o=new i.SegHierarchy(l,e=>n.get(i.getEventKey(e)),s,void 0,r),d=new Map;o.traverseSegs((e,t)=>{d.set(i.getEventKey(e),t)});const{hiddenSegs:c}=o;let u=0;for(const e of l){const t=i.getEventKey(e),a=n.get(t),s=d.get(t);null!=a&&null!=s&&(u=Math.max(u,s+a))}const h=i.groupIntersectingSegs(c),m=new Map;o.strictOrder=!0;for(const e of h){const{levelCoord:t}=o.findInsertion(e,0),n=a.get(e.key)||0;m.set(e.key,t),u=Math.max(u,t+n)}return[d,h,m,u]}(u,h,a.current,r.current,l.eventOrderStrict,l.eventMaxStack);return this.totalHeight=p,s.createElement("div",{className:t.joinClassNames(d.default.rel,d.default.noShrink),style:{height:p}},this.renderFgSegs(u,h,m,f,g,!1),this.renderFgSegs(c,e.slotWidth?x(c,l.eventMinWidth,n.dateEnv,o,e.slotWidth):{},m,[],new Map,!0))}renderFgSegs(e,t,n,a,r,l){let{props:o,segHeightRefMap:d,moreLinkHeightRefMap:c}=this;return s.createElement(s.Fragment,null,e.map(e=>{const{eventRange:a}=e,{instanceId:r}=a.instance,c=n.get(r),u=t[r],h=u||{},m=Boolean(o.eventDrag&&o.eventDrag.affectedInstances[r]),f=Boolean(o.eventResize&&o.eventResize.affectedInstances[r]),g=!l&&(m||f||!u||null==c);return s.createElement(k,{key:r,style:{visibility:g?"hidden":void 0,zIndex:1,top:c||0,insetInlineStart:h.start,width:h.size},heightRef:l?void 0:d.createRef(r)},s.createElement(P,Object.assign({isTimeScale:o.tDateProfile.isTimeScale,eventRange:a,isStart:e.isStart,isEnd:e.isEnd,isDragging:m,isResizing:f,isMirror:l,isSelected:r===o.eventSelection},i.getEventRangeMeta(a,o.todayRange,o.nowDate))))}),a.map(e=>s.createElement(k,{key:e.key,style:{top:r.get(e.key)||0,insetInlineStart:e.start,width:e.end-e.start},heightRef:c.createRef(e.key)},s.createElement(N,{hiddenSegs:e.segs,dateProfile:o.dateProfile,nowDate:o.nowDate,todayRange:o.todayRange,isTimeScale:o.tDateProfile.isTimeScale,eventDrag:o.eventDrag,eventResize:o.eventResize,eventSelection:o.eventSelection,resourceId:o.resourceId}))))}componentDidUpdate(){this.totalHeight!==this.firedTotalHeight&&(this.firedTotalHeight=this.totalHeight,i.setRef(this.props.heightRef,this.totalHeight))}componentWillUnmount(){i.setRef(this.props.heightRef,null)}}class j extends i.BaseComponent{render(){let{props:e}=this,t=[].concat(e.eventResizeSegs||[],e.dateSelectionSegs);return s.createElement(s.Fragment,null,this.renderSegs(e.businessHourSegs||[],"non-business"),this.renderSegs(e.bgEventSegs||[],"bg-event"),this.renderSegs(t,"highlight"))}renderSegs(e,t){let{tDateProfile:n,todayRange:a,nowDate:r,slotWidth:l}=this.props,{dateEnv:o,options:c}=this.context;return s.createElement(s.Fragment,null,e.map(e=>{let u={};if(null!=l){let t=H(e,void 0,o,n,l);u={insetInlineStart:t.start,width:t.size}}return s.createElement("div",{key:i.buildEventRangeKey(e.eventRange),className:d.default.fillY,style:u},"bg-event"===t?s.createElement(i.BgEvent,Object.assign({eventRange:e.eventRange,isStart:e.isStart,isEnd:e.isEnd,isVertical:!1},i.getEventRangeMeta(e.eventRange,a,r))):i.renderFill(t,c))}))}}class O extends i.DateComponent{constructor(){super(...arguments),this.buildTimelineDateProfile=i.memoize(u),this.computeSlotWidth=i.memoize(y),this.headerScrollerRef=s.createRef(),this.bodyScrollerRef=s.createRef(),this.footerScrollerRef=s.createRef(),this.headerRowInnerWidthMap=new i.RefMap(()=>{i.afterSize(this.handleSlotInnerWidths)}),this.scrollTime=null,this.slicer=new I,this.handleSlotInnerWidths=()=>{const e=this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length-1);null!=e&&e!==this.state.slotInnerWidth&&this.setState({slotInnerWidth:e})},this.handleTotalWidth=e=>{this.setState({totalWidth:e})},this.handleClientWidth=e=>{this.setState({clientWidth:e})},this.handleTimeScrollRequest=e=>{this.scrollTime=e,this.applyTimeScroll()},this.handleTimeScrollEnd=e=>{e&&(this.scrollTime=null)},this.handeBodyEl=e=>{this.bodyEl=e,e?this.context.registerInteractiveComponent(this,{el:e}):this.context.unregisterInteractiveComponent(this)}}render(){const{props:e,state:n,context:a}=this,{options:r}=a,{totalWidth:l,clientWidth:o}=n,c=null!=l&&null!=o?l-o:void 0,u=this.tDateProfile=this.buildTimelineDateProfile(e.dateProfile,a.dateEnv,r,a.dateProfileGenerator),{cellRows:h}=u,m=i.greatestDurationDenominator(u.slotDuration).unit,f=!e.forPrint&&!i.getIsHeightAuto(r),g=!e.forPrint&&i.getStickyHeaderDates(r),p=!e.forPrint&&i.getStickyFooterScrollbar(r),[v,D]=this.computeSlotWidth(u.slotCnt,u.slotsPerLabel,r.slotMinWidth,n.slotInnerWidth,o);this.slotWidth=D;let y=this.slicer.sliceProps(e,e.dateProfile,u.isTimeScale?null:r.nextDayThreshold,a,e.dateProfile,a.dateProfileGenerator,u,a.dateEnv);return s.createElement(i.NowTimer,{unit:m},(n,l)=>{const o=r.nowIndicator&&null!=D&&i.rangeContainsMarker(e.dateProfile.currentRange,n);return s.createElement(i.ViewContainer,{viewSpec:a.viewSpec,className:i.joinArrayishClassNames(!e.forPrint&&d.default.flexCol,e.className,r.tableClass,d.default.isolate),borderlessX:e.borderlessX,borderlessTop:e.borderlessTop,borderlessBottom:e.borderlessBottom,noEdgeEffects:e.noEdgeEffects},s.createElement("div",{className:t.joinClassNames(i.generateClassName(r.tableHeaderClass,{isSticky:g}),e.borderlessX&&d.default.borderlessX,d.default.flexCol,g&&d.default.tableHeaderSticky),style:{zIndex:1}},s.createElement(i.Scroller,{horizontal:!0,hideScrollbars:!0,className:d.default.flexRow,ref:this.headerScrollerRef},s.createElement("div",{className:t.joinClassNames(d.default.rel,null==v&&d.default.liquid),style:{width:v}},h.map((t,i)=>{const a=h.length-i-1;return s.createElement(R,{key:i,dateProfile:e.dateProfile,tDateProfile:u,nowDate:n,todayRange:l,rowLevel:a,cells:t,slotWidth:D,innerWidthRef:this.headerRowInnerWidthMap.createRef(i)})}),o&&s.createElement(b,{tDateProfile:u,nowDate:n,slotWidth:D})),Boolean(c)&&s.createElement("div",{className:i.joinArrayishClassNames(i.generateClassName(r.fillerClass,{isHeader:!0}),d.default.borderOnlyS),style:{minWidth:c}})),s.createElement("div",{className:i.generateClassName(r.slotHeaderDividerClass,{isHeader:!0,options:{dayMinWidth:r.dayMinWidth}})})),s.createElement(i.Scroller,{vertical:f,horizontal:!0,hideScrollbars:p||e.forPrint,className:i.joinArrayishClassNames(r.tableBodyClass,e.borderlessX&&d.default.borderlessX,g&&d.default.borderlessTop,(g||e.noEdgeEffects)&&d.default.noEdgeEffects,d.default.flexCol,f&&d.default.liquid),style:{zIndex:0},ref:this.bodyScrollerRef,clientWidthRef:this.handleClientWidth},s.createElement("div",{"aria-label":r.eventsHint,className:t.joinClassNames(d.default.rel,d.default.grow),style:{width:v},ref:this.handeBodyEl},s.createElement(S,{dateProfile:e.dateProfile,tDateProfile:u,nowDate:n,todayRange:l,slotWidth:D}),s.createElement(j,{tDateProfile:u,nowDate:n,todayRange:l,bgEventSegs:y.bgEventSegs,businessHourSegs:y.businessHourSegs,dateSelectionSegs:y.dateSelectionSegs,eventResizeSegs:y.eventResize?y.eventResize.segs:null,slotWidth:D}),s.createElement("div",{className:i.joinArrayishClassNames(r.timelineTopClass)}),s.createElement(z,{dateProfile:e.dateProfile,tDateProfile:u,nowDate:n,todayRange:l,fgEventSegs:y.fgEventSegs,eventDrag:y.eventDrag,eventResize:y.eventResize,eventSelection:y.eventSelection,slotWidth:D}),s.createElement("div",{className:i.joinArrayishClassNames(r.timelineBottomClass)}),o&&s.createElement(M,{tDateProfile:u,nowDate:n,slotWidth:D}))),Boolean(p)&&s.createElement(i.FooterScrollbar,{isSticky:!0,canvasWidth:v,scrollerRef:this.footerScrollerRef}),s.createElement(i.Ruler,{widthRef:this.handleTotalWidth}))})}componentDidMount(){this.syncedScroller=new r.ScrollerSyncer(!0),this.updateSyncedScroller(),this.resetScroll(),this.context.emitter.on("_timeScrollRequest",this.handleTimeScrollRequest),this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd)}componentDidUpdate(e){this.updateSyncedScroller(),e.dateProfile!==this.props.dateProfile&&this.context.options.scrollTimeReset?this.resetScroll():this.applyTimeScroll()}componentWillUnmount(){this.syncedScroller.destroy(),this.context.emitter.off("_timeScrollRequest",this.handleTimeScrollRequest),this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd)}updateSyncedScroller(){this.syncedScroller.handleChildren([this.headerScrollerRef.current,this.bodyScrollerRef.current,this.footerScrollerRef.current])}resetScroll(){this.handleTimeScrollRequest(this.context.options.scrollTime)}applyTimeScroll(){const{props:e,context:t,tDateProfile:n,scrollTime:i,slotWidth:a}=this;if(null!=i&&null!=a){let s=w(i,t.dateEnv,e.dateProfile,n,a);s&&(s+=1),this.syncedScroller.scrollTo({x:s})}}queryHit(e,t,n,a,s){const{props:r,context:l,tDateProfile:o,slotWidth:d}=this,{dateEnv:c}=l;if(d){const n=e?a-t:t,l=Math.floor(n/d),u=(n-l*d)/d,h=Math.floor(u*o.snapsPerSlot);let m,f,g=c.add(o.slotDates[l],i.multiplyDuration(o.snapDuration,h)),p=c.add(g,o.snapDuration),S=d/o.snapsPerSlot,v=l*d+S*h,D=v+S;return e?(m=a-D,f=a-v):(m=v,f=D),{dateProfile:r.dateProfile,dateSpan:{range:{start:g,end:p},allDay:!o.isTimeScale},rect:{left:m,right:f,top:0,bottom:s},getDayEl:()=>T(this.bodyEl,l),layer:0}}return null}}const B={timelineTopClass:i.identity,timelineBottomClass:i.identity};var L=t.createPlugin({name:"@fullcalendar/timeline",premiumReleaseDate:"2025-12-20",deps:[o.default],initialView:"timelineDay",optionRefiners:B,views:{timeline:{component:O,usesMinMaxTime:!0,eventResizableFromStart:!0},timelineDay:{type:"timeline",duration:{days:1}},timelineWeek:{type:"timeline",duration:{weeks:1}},timelineMonth:{type:"timeline",duration:{months:1}},timelineYear:{type:"timeline",duration:{years:1}}}}),F={__proto__:null,TimelineView:O,TimelineFg:z,TimelineBg:j,TimelineSlats:S,buildTimelineDateProfile:u,computeSlotWidth:y,timeToCoord:w,TimelineLaneSlicer:I,TimelineHeaderRow:R,TimelineNowIndicatorArrow:b,TimelineNowIndicatorLine:M,getTimelineSlotEl:T};return t.globalPlugins.push(L),e.Internal=F,e.default=L,Object.defineProperty(e,"__esModule",{value:!0}),e}({},FullCalendar,FullCalendar.PremiumCommon,FullCalendar.Internal,FullCalendar.InternalClassNames,FullCalendar.Preact,FullCalendar.ScrollGrid.Internal);
+2
-2

@@ -16,4 +16,4 @@

- GPLv3 License
- AGPLv3 License
(intended for open-source projects)
http://www.gnu.org/licenses/gpl-3.0.en.html
https://www.gnu.org/licenses/agpl-3.0.en.html
{
"name": "@fullcalendar/timeline",
"version": "7.0.0-beta.4",
"version": "7.0.0-beta.5",
"title": "FullCalendar Timeline Plugin",

@@ -18,7 +18,7 @@ "description": "Display events on a horizontal time axis (without resources)",

"dependencies": {
"@fullcalendar/premium-common": "7.0.0-beta.4",
"@fullcalendar/scrollgrid": "7.0.0-beta.4"
"@fullcalendar/premium-common": "7.0.0-beta.5",
"@fullcalendar/scrollgrid": "7.0.0-beta.5"
},
"peerDependencies": {
"@fullcalendar/core": "7.0.0-beta.4"
"@fullcalendar/core": "7.0.0-beta.5"
},

@@ -38,26 +38,29 @@ "type": "module",

},
"copyright": "2024 Adam Shaw",
"types": "./index.d.ts",
"main": "./index.cjs",
"module": "./index.js",
"unpkg": "./index.global.min.js",
"jsdelivr": "./index.global.min.js",
"copyright": "2025 Adam Shaw",
"types": "./esm/index.d.ts",
"module": "./esm/index.js",
"main": "./cjs/index.cjs",
"unpkg": "./global.min.js",
"jsdelivr": "./global.min.js",
"exports": {
"./package.json": "./package.json",
"./index.cjs": "./index.cjs",
"./index.js": "./index.js",
".": {
"types": "./index.d.ts",
"require": "./index.cjs",
"import": "./index.js"
"import": {
"types": "./esm/index.d.ts",
"default": "./esm/index.js"
},
"require": "./cjs/index.cjs"
},
"./internal.cjs": "./internal.cjs",
"./internal.js": "./internal.js",
"./internal": {
"types": "./internal.d.ts",
"require": "./internal.cjs",
"import": "./internal.js"
"import": {
"types": "./esm/internal.d.ts",
"default": "./esm/internal.js"
},
"require": "./cjs/internal.cjs"
}
},
"sideEffects": false
"sideEffects": [
"./global.js",
"./global.min.js"
]
}
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var index_cjs = require('@fullcalendar/core/index.cjs');
var premiumCommonPlugin = require('@fullcalendar/premium-common/index.cjs');
var internalCommon = require('./internal.cjs');
require('@fullcalendar/core/internal.cjs');
require('@fullcalendar/core/preact.cjs');
require('@fullcalendar/scrollgrid/internal.cjs');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var premiumCommonPlugin__default = /*#__PURE__*/_interopDefaultLegacy(premiumCommonPlugin);
var index = index_cjs.createPlugin({
name: '@fullcalendar/timeline',
premiumReleaseDate: '2025-01-09',
deps: [premiumCommonPlugin__default["default"]],
initialView: 'timelineDay',
views: {
timeline: {
component: internalCommon.TimelineView,
usesMinMaxTime: true,
eventResizableFromStart: true, // how is this consumed for TimelineView tho?
},
timelineDay: {
type: 'timeline',
duration: { days: 1 },
},
timelineWeek: {
type: 'timeline',
duration: { weeks: 1 },
},
timelineMonth: {
type: 'timeline',
duration: { months: 1 },
},
timelineYear: {
type: 'timeline',
duration: { years: 1 },
},
},
});
exports["default"] = index;
import { PluginDef } from '@fullcalendar/core';
import '@fullcalendar/premium-common';
import '@fullcalendar/scrollgrid';
declare const _default: PluginDef;
//# sourceMappingURL=index.d.ts.map
export { _default as default };

Sorry, the diff of this file is too big to display

/*!
FullCalendar Timeline Plugin v7.0.0-beta.4
Docs & License: https://fullcalendar.io/docs/timeline-view-no-resources
(c) 2024 Adam Shaw
*/
FullCalendar.Timeline=function(e,t,n,i,r,s){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}var l=a(n);i.config.MAX_TIMELINE_SLOTS=1e3;const o=[{years:1},{months:1},{days:1},{hours:1},{minutes:30},{minutes:15},{minutes:10},{minutes:5},{minutes:1},{seconds:30},{seconds:15},{seconds:10},{seconds:5},{seconds:1},{milliseconds:500},{milliseconds:100},{milliseconds:10},{milliseconds:1}];function c(e,t,n,r){let s={labelInterval:n.slotLabelInterval,slotDuration:n.slotDuration};!function(e,t,n){const{currentRange:r}=t;if(e.labelInterval){n.countDurationsBetween(r.start,r.end,e.labelInterval)>i.config.MAX_TIMELINE_SLOTS&&(console.warn("slotLabelInterval results in too many cells"),e.labelInterval=null)}if(e.slotDuration){n.countDurationsBetween(r.start,r.end,e.slotDuration)>i.config.MAX_TIMELINE_SLOTS&&(console.warn("slotDuration results in too many cells"),e.slotDuration=null)}if(e.labelInterval&&e.slotDuration){const t=i.wholeDivideDurations(e.labelInterval,e.slotDuration);(null===t||t<1)&&(console.warn("slotLabelInterval must be a multiple of slotDuration"),e.slotDuration=null)}}(s,e,t),h(s,e,t),function(e,t,n){const{currentRange:r}=t;let{slotDuration:s}=e;if(!s){const a=h(e,t,n);for(let e of o){const t=i.createDuration(e),n=i.wholeDivideDurations(a,t);if(null!==n&&n>1&&n<=6){s=t;break}}if(s){n.countDurationsBetween(r.start,r.end,s)>200&&(s=null)}s||(s=a),e.slotDuration=s}}(s,e,t);let a=n.slotLabelFormat,l=Array.isArray(a)?a:null!=a?[a]:function(e,t,n,r){let s,a;const{labelInterval:l}=e;let o=i.greatestDurationDenominator(l).unit;const c=r.weekNumbers;let d=s=a=null;"week"!==o||c||(o="day");switch(o){case"year":d={year:"numeric"};break;case"month":m("years",t,n)>1&&(d={year:"numeric"}),s={month:"short"};break;case"week":m("years",t,n)>1&&(d={year:"numeric"}),s={week:"narrow"};break;case"day":m("years",t,n)>1?d={year:"numeric",month:"long"}:m("months",t,n)>1&&(d={month:"long"}),c&&(s={week:"short"}),a={weekday:"narrow",day:"numeric"};break;case"hour":c&&(d={week:"short"}),m("days",t,n)>1&&(s={weekday:"short",day:"numeric",month:"numeric",omitCommas:!0}),a={hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"short"};break;case"minute":i.asRoughMinutes(l)/60>=6?(d={hour:"numeric",meridiem:"short"},s=e=>":"+i.padStart(e.date.minute,2)):d={hour:"numeric",minute:"numeric",meridiem:"short"};break;case"second":i.asRoughSeconds(l)/60>=6?(d={hour:"numeric",minute:"2-digit",meridiem:"lowercase"},s=e=>":"+i.padStart(e.date.second,2)):d={hour:"numeric",minute:"2-digit",second:"2-digit",meridiem:"lowercase"};break;case"millisecond":d={hour:"numeric",minute:"2-digit",second:"2-digit",meridiem:"lowercase"},s=e=>"."+i.padStart(e.millisecond,3)}return[].concat(d||[],s||[],a||[])}(s,e,t,n);s.headerFormats=l.map(e=>i.createFormatter(e)),s.isTimeScale=Boolean(s.slotDuration.milliseconds);let c=null;if(!s.isTimeScale){const e=i.greatestDurationDenominator(s.slotDuration).unit;/year|month|week/.test(e)&&(c=e)}s.largeUnit=c,s.emphasizeWeeks=1===i.asCleanDays(s.slotDuration)&&m("weeks",e,t)>=2&&!n.businessHours;let g,p,v=n.snapDuration;v&&(g=i.createDuration(v),p=i.wholeDivideDurations(s.slotDuration,g)),null==p&&(g=s.slotDuration,p=1),s.snapDuration=g,s.snapsPerSlot=p;let S=i.asRoughMs(e.slotMaxTime)-i.asRoughMs(e.slotMinTime),R=d(e.renderRange.start,s,t),D=d(e.renderRange.end,s,t);s.isTimeScale&&(R=t.add(R,e.slotMinTime),D=t.add(i.addDays(D,-1),e.slotMaxTime)),s.timeWindowMs=S,s.normalizedRange={start:R,end:D};let y=[],w=R;for(;w<D;)f(w,s,e,r)&&y.push(w),w=t.add(w,s.slotDuration);s.slotDates=y;let b=-1,E=0;const x=[],W=[];for(w=R;w<D;)f(w,s,e,r)?(b+=1,x.push(b),W.push(E)):x.push(b+.5),w=t.add(w,s.snapDuration),E+=1;return s.snapDiffToIndex=x,s.snapIndexToDiff=W,s.snapCnt=b+1,s.slotCnt=s.snapCnt/s.snapsPerSlot,s.isWeekStarts=function(e,t){let{slotDates:n,emphasizeWeeks:i}=e,r=null,s=[];for(let e of n){let n=t.computeWeekNumber(e),a=i&&null!==r&&r!==n;r=n,s.push(a)}return s}(s,t),s.cellRows=function(e,t){let n=e.slotDates,r=e.headerFormats,s=r.map(()=>[]),a=i.asCleanDays(e.slotDuration),l=7===a?"week":1===a?"day":null,o=r.map(e=>e.getLargestUnit?e.getLargestUnit():null);for(let a=0;a<n.length;a+=1){let c=n[a],d=e.isWeekStarts[a];for(let n=0;n<r.length;n+=1){let a=r[n],f=s[n],h=f[f.length-1],m=n===r.length-1,g=r.length>1&&!m,p=null,v=o[n]||(m?l:null);if(g){let e=t.format(c,a);h&&h.text===e?h.colspan+=1:p=u(c,e,v)}else if(!h||i.isInt(t.countDurationsBetween(e.normalizedRange.start,c,e.labelInterval))){let e=t.format(c,a);p=u(c,e,v)}else h.colspan+=1;p&&(p.weekStart=d,f.push(p))}}return s}(s,t),s.slotsPerLabel=i.wholeDivideDurations(s.labelInterval,s.slotDuration),s}function d(e,t,n){let r=e;return t.isTimeScale||(r=i.startOfDay(r),t.largeUnit&&(r=n.startOf(r,t.largeUnit))),r}function f(e,t,n,r){if(r.isHiddenDay(e))return!1;if(t.isTimeScale){let r=i.startOfDay(e),s=e.valueOf()-r.valueOf()-i.asRoughMs(n.slotMinTime);return s=(s%864e5+864e5)%864e5,s<t.timeWindowMs}return!0}function h(e,t,n){const{currentRange:r}=t;let{labelInterval:s}=e;if(!s){let t;if(e.slotDuration){for(t of o){const n=i.createDuration(t),r=i.wholeDivideDurations(n,e.slotDuration);if(null!==r&&r<=6){s=n;break}}s||(s=e.slotDuration)}else for(t of o){s=i.createDuration(t);if(n.countDurationsBetween(r.start,r.end,s)>=18)break}e.labelInterval=s}return s}function m(e,t,n){let r=t.currentRange,s=null;return"years"===e?s=n.diffWholeYears(r.start,r.end):"months"===e||"weeks"===e?s=n.diffWholeMonths(r.start,r.end):"days"===e&&(s=i.diffWholeDays(r.start,r.end)),s||0}function u(e,t,n){return{date:e,text:t,rowUnit:n,colspan:1,isWeekStart:!1}}class g extends i.BaseComponent{constructor(){super(...arguments),this.innerElRef=r.createRef()}render(){let{props:e,context:t}=this,{dateEnv:n,options:s}=t,{date:a,tDateProfile:l,isEm:o}=e,c=i.getDateMeta(e.date,e.todayRange,e.nowDate,e.dateProfile),d=Object.assign(Object.assign({date:n.toDate(e.date)},c),{view:t.viewApi});return r.createElement(i.ContentContainer,{tag:"div",className:i.joinClassNames("fc-timeline-slot",o&&"fc-timeline-slot-em",l.isTimeScale&&(i.isInt(n.countDurationsBetween(l.normalizedRange.start,e.date,l.labelInterval))?"fc-timeline-slot-major":"fc-timeline-slot-minor"),"fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start",e.borderStart&&"fc-border-s",e.isDay?i.getDayClassName(c):i.getSlotClassName(c)),attrs:Object.assign({"data-date":n.formatIso(a,{omitTimeZoneOffset:!0,omitTime:!l.isTimeScale})},c.isToday?{"aria-current":"date"}:{}),style:{width:e.width},renderProps:d,generatorName:"slotLaneContent",customGenerator:s.slotLaneContent,classNameGenerator:s.slotLaneClassNames,didMount:s.slotLaneDidMount,willUnmount:s.slotLaneWillUnmount},e=>r.createElement(e,{tag:"div",className:"fc-cell-inner",elRef:this.innerElRef}))}componentDidMount(){const e=this.innerElRef.current;this.disconnectInnerWidth=i.watchWidth(e,e=>{i.setRef(this.props.innerWidthRef,e)})}componentWillUnmount(){this.disconnectInnerWidth(),i.setRef(this.props.innerWidthRef,null)}}class p extends i.BaseComponent{constructor(){super(...arguments),this.innerWidthRefMap=new i.RefMap(()=>{i.afterSize(this.handleInnerWidths)}),this.handleInnerWidths=()=>{const e=this.innerWidthRefMap.current;let t=0;for(const n of e.values())t=Math.max(t,n);i.setRef(this.props.innerWidthRef,t)}}render(){let{props:e,innerWidthRefMap:t}=this,{tDateProfile:n,slotWidth:i}=e,{slotDates:s,isWeekStarts:a}=n,l=!n.isTimeScale&&!n.largeUnit;return r.createElement("div",{"aria-hidden":!0,className:"fc-timeline-slots fc-fill fc-flex-row",style:{height:e.height}},s.map((s,o)=>{let c=s.toISOString();return r.createElement(g,{key:c,date:s,dateProfile:e.dateProfile,tDateProfile:n,nowDate:e.nowDate,todayRange:e.todayRange,isEm:a[o],isDay:l,borderStart:Boolean(o),innerWidthRef:t.createRef(c),width:i})}))}}function v(e,t,n){let r=n.countDurationsBetween(t.normalizedRange.start,e,t.snapDuration);if(r<0)return 0;if(r>=t.snapDiffToIndex.length)return t.snapCnt;let s=Math.floor(r),a=t.snapDiffToIndex[s];return i.isInt(a)?a+=r-s:a=Math.ceil(a),a}function S(e,t){return e?t?{right:e.start,width:e.size}:{left:e.start,width:e.size}:{}}function R(e,t){return t?{right:e}:{left:e}}function D(e,t,n,i,r){if(null==i||null==r)return[void 0,void 0,!1];const s=r/e;let a,l;return s>=(n=Math.max(n||0,(i+1)/t,30))?(a=!0,l=s):(a=!1,l=Math.max(n,s)),[l*e,l,a]}function y(e,t,n,r,s){let a=t.add(n.activeRange.start,e);return r.isTimeScale||(a=i.startOfDay(a)),w(a,t,r,s)}function w(e,t,n,r){return function(e,t,n){let r=n.countDurationsBetween(t.normalizedRange.start,e,t.snapDuration);if(r<0)return 0;if(r>=t.snapDiffToIndex.length)return t.snapCnt;let s=Math.floor(r),a=t.snapDiffToIndex[s];i.isInt(a)?a+=r-s:a=Math.ceil(a);return a}(e,n,t)/n.snapsPerSlot*r}function b(e,t,n,r,s){const a={};for(const l of e)a[i.getEventKey(l)]=E(l,t,n,r,s);return a}function E(e,t,n,i,r){const s=w(e.startDate,n,i,r);let a=w(e.endDate,n,i,r)-s;return t&&(a=Math.max(a,t)),{start:s,size:a}}class x extends i.BaseComponent{render(){let{props:e}=this,t=[].concat(e.eventResizeSegs,e.dateSelectionSegs);return r.createElement(r.Fragment,null,this.renderSegs(e.businessHourSegs||[],"non-business"),this.renderSegs(e.bgEventSegs||[],"bg-event"),this.renderSegs(t,"highlight"))}renderSegs(e,t){let{tDateProfile:n,todayRange:s,nowDate:a,slotWidth:l}=this.props,{dateEnv:o,isRtl:c}=this.context;return r.createElement(r.Fragment,null,e.map(e=>{let d;if(null!=l){d=S(E(e,void 0,o,n,l),c)}return r.createElement("div",{key:i.buildEventRangeKey(e.eventRange),className:"fc-fill-y",style:d},"bg-event"===t?r.createElement(i.BgEvent,Object.assign({eventRange:e.eventRange,isStart:e.isStart,isEnd:e.isEnd},i.getEventRangeMeta(e.eventRange,s,a))):i.renderFill(t))}))}}class W extends i.Slicer{sliceRange(e,t,n,r,s){let a=function(e,t,n){if(!t.isTimeScale&&(e=i.computeVisibleDayRange(e),t.largeUnit)){let i=e;((e={start:n.startOf(e.start,t.largeUnit),end:n.startOf(e.end,t.largeUnit)}).end.valueOf()!==i.end.valueOf()||e.end<=e.start)&&(e={start:e.start,end:n.add(e.end,t.slotDuration)})}return e}(e,r,s),l=[];if(v(a.start,r,s)<v(a.end,r,s)){let e=i.intersectRanges(a,r.normalizedRange);e&&l.push({startDate:e.start,endDate:e.end,isStart:e.start.valueOf()===a.start.valueOf()&&f(e.start,r,t,n),isEnd:e.end.valueOf()===a.end.valueOf()&&f(i.addMs(e.end,-1),r,t,n)})}return l}}const M=i.createFormatter({hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"narrow"});class T extends i.BaseComponent{render(){let{props:e,context:t}=this,{options:n}=t;return r.createElement(i.StandardEvent,Object.assign({},e,{className:i.joinClassNames("fc-timeline-event",!1===n.eventOverlap&&"fc-timeline-event-spacious","fc-h-event"),defaultTimeFormat:M,defaultDisplayEventTime:!e.isTimeScale}))}}class I extends i.BaseComponent{render(){let{props:e}=this,{hiddenSegs:t,resourceId:n,forcedInvisibleMap:s}=e,a=n?{resourceId:n}:{};return r.createElement(i.MoreLinkContainer,{className:"fc-timeline-more-link",allDayDate:null,segs:t,hiddenSegs:t,dateProfile:e.dateProfile,todayRange:e.todayRange,dateSpanProps:a,popoverContent:()=>r.createElement(r.Fragment,null,t.map(t=>{let{eventRange:n}=t,a=n.instance.instanceId;return r.createElement("div",{key:a,style:{visibility:s[a]?"hidden":""}},r.createElement(T,Object.assign({isTimeScale:e.isTimeScale,eventRange:n,isStart:t.isStart,isEnd:t.isEnd,isDragging:!1,isResizing:!1,isDateSelecting:!1,isSelected:a===e.eventSelection},i.getEventRangeMeta(n,e.todayRange,e.nowDate))))}))},e=>r.createElement(e,{tag:"div",className:"fc-timeline-more-link-inner fc-sticky-s"}))}}class k extends r.Component{constructor(){super(...arguments),this.rootElRef=r.createRef()}render(){const{props:e}=this;return r.createElement("div",{className:"fc-abs",style:e.style,ref:this.rootElRef},e.children)}componentDidMount(){const e=this.rootElRef.current;this.disconnectHeight=i.watchHeight(e,e=>{i.setRef(this.props.heightRef,e)})}componentWillUnmount(){this.disconnectHeight(),i.setRef(this.props.heightRef,null)}}class P extends i.BaseComponent{constructor(){super(...arguments),this.sortEventSegs=i.memoize(i.sortEventSegs),this.segHeightRefMap=new i.RefMap(()=>{i.afterSize(this.handleSegHeights)}),this.moreLinkHeightRefMap=new i.RefMap(()=>{i.afterSize(this.handleMoreLinkHeights)}),this.slicer=new W,this.handleMoreLinkHeights=()=>{this.setState({moreLinkHeightRev:this.moreLinkHeightRefMap.rev})},this.handleSegHeights=()=>{this.setState({segHeightRev:this.segHeightRefMap.rev})}}render(){let{props:e,context:t,segHeightRefMap:n,moreLinkHeightRefMap:s}=this,{options:a}=t,{dateProfile:l,tDateProfile:o}=e,c=this.slicer.sliceProps(e,l,o.isTimeScale?null:e.nextDayThreshold,t,l,t.dateProfileGenerator,o,t.dateEnv),d=(c.eventDrag?c.eventDrag.segs:null)||(c.eventResize?c.eventResize.segs:null)||[],f=this.sortEventSegs(c.fgEventSegs,a.eventOrder),h=null!=e.slotWidth?b(f,a.eventMinWidth,t.dateEnv,o,e.slotWidth):{},[m,u,g,p]=function(e,t,n,r,s,a){const l=[];for(const n of e){const e=t[i.getEventKey(n)];e&&l.push(Object.assign(Object.assign({},n),{start:e.start,end:e.start+e.size}))}const o=new i.SegHierarchy(l,e=>n.get(i.getEventKey(e)),s,void 0,a),c=new Map;o.traverseSegs((e,t)=>{c.set(i.getEventKey(e),t)});const{hiddenSegs:d}=o;let f=0;for(const e of l){const t=i.getEventKey(e),r=n.get(t),s=c.get(t);null!=r&&null!=s&&(f=Math.max(f,s+r))}const h=i.groupIntersectingSegs(d),m=new Map;o.strictOrder=!0;for(const e of h){const{levelCoord:t}=o.findInsertion(e,0),n=r.get(e.key)||0;m.set(e.key,t),f=Math.max(f,t+n)}return[c,h,m,f]}(f,h,n.current,s.current,a.eventOrderStrict,a.eventMaxStack),v=(c.eventDrag?c.eventDrag.affectedInstances:null)||(c.eventResize?c.eventResize.affectedInstances:null)||{};return r.createElement(r.Fragment,null,r.createElement(x,{tDateProfile:o,nowDate:e.nowDate,todayRange:e.todayRange,bgEventSegs:c.bgEventSegs,businessHourSegs:c.businessHourSegs,dateSelectionSegs:c.dateSelectionSegs,eventResizeSegs:c.eventResize?c.eventResize.segs:[],slotWidth:e.slotWidth}),r.createElement("div",{className:i.joinClassNames("fc-timeline-events",!1===a.eventOverlap?"fc-timeline-events-overlap-disabled":"fc-timeline-events-overlap-enabled","fc-content-box"),style:{height:p}},this.renderFgSegs(f,h,m,v,u,g,!1,!1,!1),this.renderFgSegs(d,e.slotWidth?b(d,a.eventMinWidth,t.dateEnv,o,e.slotWidth):{},m,{},[],new Map,Boolean(c.eventDrag),Boolean(c.eventResize),!1)))}renderFgSegs(e,t,n,s,a,l,o,c,d){let{props:f,context:h,segHeightRefMap:m,moreLinkHeightRefMap:u}=this,g=o||c||d;return r.createElement(r.Fragment,null,e.map(e=>{const{eventRange:a}=e,{instanceId:l}=a.instance,u=n.get(l),p=t[l],v=g||p&&null!=u&&!s[l];return r.createElement(k,{key:l,style:Object.assign({visibility:v?"":"hidden",top:u||0},S(p,h.isRtl)),heightRef:g?void 0:m.createRef(l)},r.createElement(T,Object.assign({isTimeScale:f.tDateProfile.isTimeScale,eventRange:a,isStart:e.isStart,isEnd:e.isEnd,isDragging:o,isResizing:c,isDateSelecting:d,isSelected:l===f.eventSelection},i.getEventRangeMeta(a,f.todayRange,f.nowDate))))}),a.map(e=>r.createElement(k,{key:e.key,style:Object.assign({top:l.get(e.key)||0},S({start:e.start,size:e.end-e.start},h.isRtl)),heightRef:u.createRef(e.key)},r.createElement(I,{hiddenSegs:e.segs,dateProfile:f.dateProfile,nowDate:f.nowDate,todayRange:f.todayRange,isTimeScale:f.tDateProfile.isTimeScale,eventSelection:f.eventSelection,resourceId:f.resourceId,forcedInvisibleMap:s}))))}}class C extends i.BaseComponent{constructor(){super(...arguments),this.refineRenderProps=i.memoizeObjArg(N),this.innerElRef=r.createRef()}render(){let{props:e,context:t}=this,{dateEnv:n,options:s}=t,{cell:a,dateProfile:l,tDateProfile:o}=e,c=i.getDateMeta(a.date,e.todayRange,e.nowDate,l),d=this.refineRenderProps({level:e.rowLevel,dateMarker:a.date,text:a.text,dateEnv:t.dateEnv,viewApi:t.viewApi}),f=!c.isDisabled&&a.rowUnit&&"time"!==a.rowUnit;return r.createElement(i.ContentContainer,{tag:"div",className:i.joinClassNames("fc-timeline-slot-label fc-timeline-slot",a.isWeekStart&&"fc-timeline-slot-em","fc-header-cell fc-cell fc-flex-col fc-justify-center",e.borderStart&&"fc-border-s",e.isCentered?"fc-align-center":"fc-align-start","time"===a.rowUnit?i.getSlotClassName(c):i.getDayClassName(c)),attrs:Object.assign({"data-date":n.formatIso(a.date,{omitTime:!o.isTimeScale,omitTimeZoneOffset:!0})},c.isToday?{"aria-current":"date"}:{}),style:{width:null!=e.slotWidth?e.slotWidth*a.colspan:void 0},renderProps:d,generatorName:"slotLabelContent",customGenerator:s.slotLabelContent,defaultGenerator:z,classNameGenerator:s.slotLabelClassNames,didMount:s.slotLabelDidMount,willUnmount:s.slotLabelWillUnmount},n=>r.createElement(n,{tag:"div",attrs:f?i.buildNavLinkAttrs(t,a.date,a.rowUnit,void 0,!1):{},className:i.joinClassNames("fc-cell-inner fc-padding-md",e.isSticky&&"fc-sticky-s"),elRef:this.innerElRef}))}componentDidMount(){const{props:e}=this,t=this.innerElRef.current;this.detachSize=i.watchSize(t,(n,r)=>{i.setRef(e.innerWidthRef,n),i.setRef(e.innerHeightRef,r),t.style.left=t.style.right=e.isCentered&&e.isSticky?`calc(50% - ${n/2}px)`:""})}componentWillUnmount(){const{props:e}=this;this.detachSize(),i.setRef(e.innerWidthRef,null),i.setRef(e.innerHeightRef,null)}}function z(e){return e.text}function N(e){return{level:e.level,date:e.dateEnv.toDate(e.dateMarker),view:e.viewApi,text:e.text}}class L extends i.BaseComponent{constructor(){super(...arguments),this.innerWidthRefMap=new i.RefMap(()=>{i.afterSize(this.handleInnerWidths)}),this.innerHeightRefMap=new i.RefMap(()=>{i.afterSize(this.handleInnerHeights)}),this.handleInnerWidths=()=>{const e=this.innerWidthRefMap.current;let t=0;for(const n of e.values())t=Math.max(t,n);i.setRef(this.props.innerWidthRef,t)},this.handleInnerHeights=()=>{const e=this.innerHeightRefMap.current;let t=0;for(const n of e.values())t=Math.max(t,n);i.setRef(this.props.innerHeighRef,t)}}render(){const{props:e,innerWidthRefMap:t,innerHeightRefMap:n}=this,s=!(e.tDateProfile.isTimeScale&&e.isLastRow),a=!e.isLastRow;return r.createElement("div",{className:i.joinClassNames("fc-flex-row fc-grow",!e.isLastRow&&"fc-border-b")},e.cells.map((i,l)=>{const o=i.rowUnit+":"+i.date.toISOString();return r.createElement(C,{key:o,cell:i,rowLevel:e.rowLevel,dateProfile:e.dateProfile,tDateProfile:e.tDateProfile,todayRange:e.todayRange,nowDate:e.nowDate,isCentered:s,isSticky:a,borderStart:Boolean(l),innerWidthRef:t.createRef(o),innerHeightRef:n.createRef(o),slotWidth:e.slotWidth})}))}componentWillUnmount(){i.setRef(this.props.innerWidthRef,null),i.setRef(this.props.innerHeighRef,null)}}class H extends i.BaseComponent{render(){const{props:e,context:t}=this;return r.createElement("div",{className:"fc-timeline-now-indicator-container"},r.createElement(i.NowIndicatorContainer,{className:"fc-timeline-now-indicator-line",style:null!=e.slotWidth?R(w(e.nowDate,t.dateEnv,e.tDateProfile,e.slotWidth),t.isRtl):{},isAxis:!1,date:e.nowDate}))}}class O extends i.BaseComponent{render(){const{props:e,context:t}=this;return r.createElement("div",{className:"fc-timeline-now-indicator-container"},r.createElement(i.NowIndicatorContainer,{className:"fc-timeline-now-indicator-arrow",style:null!=e.slotWidth?R(w(e.nowDate,t.dateEnv,e.tDateProfile,e.slotWidth),t.isRtl):{},isAxis:!0,date:e.nowDate}))}}class B extends i.DateComponent{constructor(){super(...arguments),this.buildTimelineDateProfile=i.memoize(c),this.computeSlotWidth=i.memoize(D),this.headerScrollerRef=r.createRef(),this.bodyScrollerRef=r.createRef(),this.footerScrollerRef=r.createRef(),this.headerRowInnerWidthMap=new i.RefMap(()=>{i.afterSize(this.handleSlotInnerWidths)}),this.scrollTime=null,this.handleBodySlotInnerWidth=e=>{this.bodySlotInnerWidth=e,i.afterSize(this.handleSlotInnerWidths)},this.handleSlotInnerWidths=()=>{const{state:e}=this,t=Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length-1)||0,this.bodySlotInnerWidth);e.slotInnerWidth!==t&&this.setState({slotInnerWidth:t})},this.handleClientWidth=e=>{this.setState({clientWidth:e})},this.handleEndScrollbarWidth=e=>{this.setState({endScrollbarWidth:e})},this.handleTimeScrollRequest=e=>{this.scrollTime=e,this.applyTimeScroll()},this.handleTimeScrollEnd=({isUser:e})=>{e&&(this.scrollTime=null)},this.handeBodyEl=e=>{this.bodyEl=e,e?this.context.registerInteractiveComponent(this,{el:e}):this.context.unregisterInteractiveComponent(this)}}render(){const{props:e,state:t,context:n}=this,{options:s}=n,a=this.tDateProfile=this.buildTimelineDateProfile(e.dateProfile,n.dateEnv,s,n.dateProfileGenerator),{cellRows:l}=a,o=i.greatestDurationDenominator(a.slotDuration).unit,c=!e.forPrint&&!i.getIsHeightAuto(s),d=!e.forPrint&&i.getStickyHeaderDates(s),f=!e.forPrint&&i.getStickyFooterScrollbar(s),[h,m]=this.computeSlotWidth(a.slotCnt,a.slotsPerLabel,s.slotMinWidth,t.slotInnerWidth,t.clientWidth);return this.slotWidth=m,r.createElement(i.NowTimer,{unit:o},(o,u)=>{const g=s.nowIndicator&&null!=m&&i.rangeContainsMarker(e.dateProfile.currentRange,o);return r.createElement(i.ViewContainer,{viewSpec:n.viewSpec,className:i.joinClassNames("fc-timeline fc-border",!e.forPrint&&"fc-flex-col")},r.createElement(i.Scroller,{horizontal:!0,hideScrollbars:!0,className:i.joinClassNames("fc-timeline-header fc-flex-row fc-border-b",d&&"fc-table-header-sticky"),ref:this.headerScrollerRef},r.createElement("div",{className:i.joinClassNames("fc-rel",null==h&&"fc-liquid"),style:{width:h}},l.map((t,n)=>{const i=n===l.length-1;return r.createElement(L,{key:n,dateProfile:e.dateProfile,tDateProfile:a,nowDate:o,todayRange:u,rowLevel:n,isLastRow:i,cells:t,slotWidth:m,innerWidthRef:this.headerRowInnerWidthMap.createRef(n)})}),g&&r.createElement(O,{tDateProfile:a,nowDate:o,slotWidth:m})),Boolean(t.endScrollbarWidth)&&r.createElement("div",{className:"fc-border-s fc-filler",style:{minWidth:t.endScrollbarWidth}})),r.createElement(i.Scroller,{vertical:c,horizontal:!0,hideScrollbars:e.forPrint,className:i.joinClassNames("fc-timeline-body fc-flex-col",c&&"fc-liquid"),ref:this.bodyScrollerRef,clientWidthRef:this.handleClientWidth,endScrollbarWidthRef:this.handleEndScrollbarWidth},r.createElement("div",{"aria-label":s.eventsHint,className:"fc-rel fc-grow",style:{width:h},ref:this.handeBodyEl},r.createElement(p,{dateProfile:e.dateProfile,tDateProfile:a,nowDate:o,todayRange:u,innerWidthRef:this.handleBodySlotInnerWidth,slotWidth:m}),r.createElement(P,{dateProfile:e.dateProfile,tDateProfile:a,nowDate:o,todayRange:u,nextDayThreshold:s.nextDayThreshold,eventStore:e.eventStore,eventUiBases:e.eventUiBases,businessHours:e.businessHours,dateSelection:e.dateSelection,eventDrag:e.eventDrag,eventResize:e.eventResize,eventSelection:e.eventSelection,slotWidth:m}),g&&r.createElement(H,{tDateProfile:a,nowDate:o,slotWidth:m}))),f&&r.createElement(i.Scroller,{ref:this.footerScrollerRef,horizontal:!0},r.createElement("div",{style:{width:h}})))})}componentDidMount(){this.syncedScroller=new s.ScrollerSyncer(!0),this.updateSyncedScroller(),this.resetScroll(),this.context.emitter.on("_timeScrollRequest",this.handleTimeScrollRequest),this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd)}componentDidUpdate(e){this.updateSyncedScroller(),e.dateProfile!==this.props.dateProfile&&this.context.options.scrollTimeReset?this.resetScroll():this.applyTimeScroll()}componentWillUnmount(){this.syncedScroller.destroy(),this.context.emitter.off("_timeScrollRequest",this.handleTimeScrollRequest),this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd)}updateSyncedScroller(){this.syncedScroller.handleChildren([this.headerScrollerRef.current,this.bodyScrollerRef.current,this.footerScrollerRef.current])}resetScroll(){this.handleTimeScrollRequest(this.context.options.scrollTime)}applyTimeScroll(){const{props:e,context:t,tDateProfile:n,scrollTime:i,slotWidth:r}=this;if(null!=i&&null!=r){let s=y(i,t.dateEnv,e.dateProfile,n,r);s&&(s+=1),this.syncedScroller.scrollTo({x:s})}}queryHit(e,t,n,r){const{props:s,context:a,tDateProfile:l,slotWidth:o}=this,{dateEnv:c}=a;if(o){const t=a.isRtl?n-e:e,d=Math.floor(t/o),f=(t-d*o)/o,h=Math.floor(f*l.snapsPerSlot);let m,u,g=c.add(l.slotDates[d],i.multiplyDuration(l.snapDuration,h)),p=c.add(g,l.snapDuration),v=o/l.snapsPerSlot,S=d*o+v*h,R=S+v;return a.isRtl?(m=n-R,u=n-S):(m=S,u=R),{dateProfile:s.dateProfile,dateSpan:{range:{start:g,end:p},allDay:!l.isTimeScale},rect:{left:m,right:u,top:0,bottom:r},dayEl:this.bodyEl.querySelectorAll(".fc-timeline-slot")[d],layer:0}}return null}}i.injectStyles('.fc-timeline-slots{z-index:1}.fc-timeline-events{position:relative;z-index:2}.fc-timeline-slot-minor{border-style:dotted}.fc-timeline-events-overlap-enabled{padding-bottom:10px}.fc-timeline-event{border-radius:0;font-size:var(--fc-small-font-size);margin-bottom:1px}.fc-direction-ltr .fc-timeline-event.fc-event-end{margin-right:1px}.fc-direction-rtl .fc-timeline-event.fc-event-end{margin-left:1px}.fc-timeline-event .fc-event-inner{align-items:center;display:flex;flex-direction:row;padding:2px 1px}.fc-timeline-event-spacious .fc-event-inner{padding-bottom:5px;padding-top:5px}.fc-timeline-event .fc-event-time{font-weight:700}.fc-timeline-event .fc-event-time,.fc-timeline-event .fc-event-title{padding:0 2px}.fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-color:transparent #000;border-style:solid;border-width:5px;content:"";flex-grow:0;flex-shrink:0;height:0;margin:0 1px;opacity:.5;width:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before,.fc-direction-rtl .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after{border-left:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-direction-rtl .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-right:0}.fc-timeline-more-link{align-items:flex-start;background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;display:flex;flex-direction:column;font-size:var(--fc-small-font-size);padding:1px}.fc-direction-ltr .fc-timeline-more-link{margin-right:1px}.fc-direction-rtl .fc-timeline-more-link{margin-left:1px}.fc-timeline-more-link-inner{padding:2px}.fc-timeline-now-indicator-container{bottom:0;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;top:0;z-index:4}.fc-timeline-now-indicator-arrow{border-bottom-style:solid;border-bottom-width:0;border-color:var(--fc-now-indicator-color);border-left:5px solid transparent;border-right:5px solid transparent;border-top-style:solid;border-top-width:6px;height:0;margin:0 -5px;position:absolute;top:0;width:0}.fc-timeline-now-indicator-line{border-left:1px solid var(--fc-now-indicator-color);bottom:0;position:absolute;top:0}');var U=t.createPlugin({name:"@fullcalendar/timeline",premiumReleaseDate:"2025-01-09",deps:[l.default],initialView:"timelineDay",views:{timeline:{component:B,usesMinMaxTime:!0,eventResizableFromStart:!0},timelineDay:{type:"timeline",duration:{days:1}},timelineWeek:{type:"timeline",duration:{weeks:1}},timelineMonth:{type:"timeline",duration:{months:1}},timelineYear:{type:"timeline",duration:{years:1}}}}),j={__proto__:null,TimelineView:B,TimelineLane:P,TimelineLaneBg:x,TimelineSlats:p,buildTimelineDateProfile:c,createVerticalStyle:function(e){if(e)return{top:e.start,height:e.size}},createHorizontalStyle:function(e,t){if(e)return{[t?"right":"left"]:e.start,width:e.size}},computeSlotWidth:D,timeToCoord:y,TimelineLaneSlicer:W,TimelineHeaderRow:L,TimelineNowIndicatorArrow:O,TimelineNowIndicatorLine:H};return t.globalPlugins.push(U),e.Internal=j,e.default=U,Object.defineProperty(e,"__esModule",{value:!0}),e}({},FullCalendar,FullCalendar.PremiumCommon,FullCalendar.Internal,FullCalendar.Preact,FullCalendar.ScrollGrid.Internal);
import { createPlugin } from '@fullcalendar/core/index.js';
import premiumCommonPlugin from '@fullcalendar/premium-common/index.js';
import { TimelineView } from './internal.js';
import '@fullcalendar/core/internal.js';
import '@fullcalendar/core/preact.js';
import '@fullcalendar/scrollgrid/internal.js';
var index = createPlugin({
name: '@fullcalendar/timeline',
premiumReleaseDate: '2025-01-09',
deps: [premiumCommonPlugin],
initialView: 'timelineDay',
views: {
timeline: {
component: TimelineView,
usesMinMaxTime: true,
eventResizableFromStart: true, // how is this consumed for TimelineView tho?
},
timelineDay: {
type: 'timeline',
duration: { days: 1 },
},
timelineWeek: {
type: 'timeline',
duration: { weeks: 1 },
},
timelineMonth: {
type: 'timeline',
duration: { months: 1 },
},
timelineYear: {
type: 'timeline',
duration: { years: 1 },
},
},
});
export { index as default };
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var internal_cjs = require('@fullcalendar/core/internal.cjs');
var preact_cjs = require('@fullcalendar/core/preact.cjs');
var internal_cjs$1 = require('@fullcalendar/scrollgrid/internal.cjs');
const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
const MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
internal_cjs.config.MAX_TIMELINE_SLOTS = 1000;
// potential nice values for slot-duration and interval-duration
const STOCK_SUB_DURATIONS = [
{ years: 1 },
{ months: 1 },
{ days: 1 },
{ hours: 1 },
{ minutes: 30 },
{ minutes: 15 },
{ minutes: 10 },
{ minutes: 5 },
{ minutes: 1 },
{ seconds: 30 },
{ seconds: 15 },
{ seconds: 10 },
{ seconds: 5 },
{ seconds: 1 },
{ milliseconds: 500 },
{ milliseconds: 100 },
{ milliseconds: 10 },
{ milliseconds: 1 },
];
function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
let tDateProfile = {
labelInterval: allOptions.slotLabelInterval,
slotDuration: allOptions.slotDuration,
};
validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
let input = allOptions.slotLabelFormat;
let rawFormats = Array.isArray(input) ? input :
(input != null) ? [input] :
computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
tDateProfile.headerFormats = rawFormats.map((rawFormat) => internal_cjs.createFormatter(rawFormat));
tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
let largeUnit = null;
if (!tDateProfile.isTimeScale) {
const slotUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
if (/year|month|week/.test(slotUnit)) {
largeUnit = slotUnit;
}
}
tDateProfile.largeUnit = largeUnit;
tDateProfile.emphasizeWeeks =
internal_cjs.asCleanDays(tDateProfile.slotDuration) === 1 &&
currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
!allOptions.businessHours;
/*
console.log('label interval =', timelineView.labelInterval.humanize())
console.log('slot duration =', timelineView.slotDuration.humanize())
console.log('header formats =', timelineView.headerFormats)
console.log('isTimeScale', timelineView.isTimeScale)
console.log('largeUnit', timelineView.largeUnit)
*/
let rawSnapDuration = allOptions.snapDuration;
let snapDuration;
let snapsPerSlot;
if (rawSnapDuration) {
snapDuration = internal_cjs.createDuration(rawSnapDuration);
snapsPerSlot = internal_cjs.wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
// ^ TODO: warning if not whole?
}
if (snapsPerSlot == null) {
snapDuration = tDateProfile.slotDuration;
snapsPerSlot = 1;
}
tDateProfile.snapDuration = snapDuration;
tDateProfile.snapsPerSlot = snapsPerSlot;
// more...
let timeWindowMs = internal_cjs.asRoughMs(dateProfile.slotMaxTime) - internal_cjs.asRoughMs(dateProfile.slotMinTime);
// TODO: why not use normalizeRange!?
let normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
let normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
// apply slotMinTime/slotMaxTime
// TODO: View should be responsible.
if (tDateProfile.isTimeScale) {
normalizedStart = dateEnv.add(normalizedStart, dateProfile.slotMinTime);
normalizedEnd = dateEnv.add(internal_cjs.addDays(normalizedEnd, -1), dateProfile.slotMaxTime);
}
tDateProfile.timeWindowMs = timeWindowMs;
tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
let slotDates = [];
let date = normalizedStart;
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
slotDates.push(date);
}
date = dateEnv.add(date, tDateProfile.slotDuration);
}
tDateProfile.slotDates = slotDates;
// more...
let snapIndex = -1;
let snapDiff = 0; // index of the diff :(
const snapDiffToIndex = [];
const snapIndexToDiff = [];
date = normalizedStart;
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
snapIndex += 1;
snapDiffToIndex.push(snapIndex);
snapIndexToDiff.push(snapDiff);
}
else {
snapDiffToIndex.push(snapIndex + 0.5);
}
date = dateEnv.add(date, tDateProfile.snapDuration);
snapDiff += 1;
}
tDateProfile.snapDiffToIndex = snapDiffToIndex;
tDateProfile.snapIndexToDiff = snapIndexToDiff;
tDateProfile.snapCnt = snapIndex + 1; // is always one behind
tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
// more...
tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv);
tDateProfile.slotsPerLabel = internal_cjs.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
return tDateProfile;
}
/*
snaps to appropriate unit
*/
function normalizeDate(date, tDateProfile, dateEnv) {
let normalDate = date;
if (!tDateProfile.isTimeScale) {
normalDate = internal_cjs.startOfDay(normalDate);
if (tDateProfile.largeUnit) {
normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
}
}
return normalDate;
}
/*
snaps to appropriate unit
*/
function normalizeRange(range, tDateProfile, dateEnv) {
if (!tDateProfile.isTimeScale) {
range = internal_cjs.computeVisibleDayRange(range);
if (tDateProfile.largeUnit) {
let dayRange = range; // preserve original result
range = {
start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
end: dateEnv.startOf(range.end, tDateProfile.largeUnit),
};
// if date is partially through the interval, or is in the same interval as the start,
// make the exclusive end be the *next* interval
if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
range = {
start: range.start,
end: dateEnv.add(range.end, tDateProfile.slotDuration),
};
}
}
}
return range;
}
function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
if (dateProfileGenerator.isHiddenDay(date)) {
return false;
}
if (tDateProfile.isTimeScale) {
// determine if the time is within slotMinTime/slotMaxTime, which may have wacky values
let day = internal_cjs.startOfDay(date);
let timeMs = date.valueOf() - day.valueOf();
let ms = timeMs - internal_cjs.asRoughMs(dateProfile.slotMinTime); // milliseconds since slotMinTime
ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
return ms < tDateProfile.timeWindowMs; // before the slotMaxTime?
}
return true;
}
function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
// make sure labelInterval doesn't exceed the max number of cells
if (tDateProfile.labelInterval) {
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
if (labelCnt > internal_cjs.config.MAX_TIMELINE_SLOTS) {
console.warn('slotLabelInterval results in too many cells');
tDateProfile.labelInterval = null;
}
}
// make sure slotDuration doesn't exceed the maximum number of cells
if (tDateProfile.slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
if (slotCnt > internal_cjs.config.MAX_TIMELINE_SLOTS) {
console.warn('slotDuration results in too many cells');
tDateProfile.slotDuration = null;
}
}
// make sure labelInterval is a multiple of slotDuration
if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
const slotsPerLabel = internal_cjs.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
if (slotsPerLabel === null || slotsPerLabel < 1) {
console.warn('slotLabelInterval must be a multiple of slotDuration');
tDateProfile.slotDuration = null;
}
}
}
function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { labelInterval } = tDateProfile;
if (!labelInterval) {
// compute based off the slot duration
// find the largest label interval with an acceptable slots-per-label
let input;
if (tDateProfile.slotDuration) {
for (input of STOCK_SUB_DURATIONS) {
const tryLabelInterval = internal_cjs.createDuration(input);
const slotsPerLabel = internal_cjs.wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
labelInterval = tryLabelInterval;
break;
}
}
// use the slot duration as a last resort
if (!labelInterval) {
labelInterval = tDateProfile.slotDuration;
}
// compute based off the view's duration
// find the largest label interval that yields the minimum number of labels
}
else {
for (input of STOCK_SUB_DURATIONS) {
labelInterval = internal_cjs.createDuration(input);
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
if (labelCnt >= MIN_AUTO_LABELS) {
break;
}
}
}
tDateProfile.labelInterval = labelInterval;
}
return labelInterval;
}
function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { slotDuration } = tDateProfile;
if (!slotDuration) {
const labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
// compute based off the label interval
// find the largest slot duration that is different from labelInterval, but still acceptable
for (let input of STOCK_SUB_DURATIONS) {
const trySlotDuration = internal_cjs.createDuration(input);
const slotsPerLabel = internal_cjs.wholeDivideDurations(labelInterval, trySlotDuration);
if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
slotDuration = trySlotDuration;
break;
}
}
// only allow the value if it won't exceed the view's # of slots limit
if (slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
if (slotCnt > MAX_AUTO_CELLS) {
slotDuration = null;
}
}
// use the label interval as a last resort
if (!slotDuration) {
slotDuration = labelInterval;
}
tDateProfile.slotDuration = slotDuration;
}
return slotDuration;
}
function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
let format1;
let format2;
const { labelInterval } = tDateProfile;
let unit = internal_cjs.greatestDurationDenominator(labelInterval).unit;
const weekNumbersVisible = allOptions.weekNumbers;
let format0 = (format1 = (format2 = null));
// NOTE: weekNumber computation function wont work
if ((unit === 'week') && !weekNumbersVisible) {
unit = 'day';
}
switch (unit) {
case 'year':
format0 = { year: 'numeric' }; // '2015'
break;
case 'month':
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { month: 'short' }; // 'Jan'
break;
case 'week':
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { week: 'narrow' }; // 'Wk4'
break;
case 'day':
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
}
else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
format0 = { month: 'long' }; // 'January'
}
if (weekNumbersVisible) {
format1 = { week: 'short' }; // 'Wk 4'
}
format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
break;
case 'hour':
if (weekNumbersVisible) {
format0 = { week: 'short' }; // 'Wk 4'
}
if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
}
format2 = {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'short',
};
break;
case 'minute':
// sufficiently large number of different minute cells?
if ((internal_cjs.asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = {
hour: 'numeric',
meridiem: 'short',
};
format1 = (params) => (':' + internal_cjs.padStart(params.date.minute, 2) // ':30'
);
}
else {
format0 = {
hour: 'numeric',
minute: 'numeric',
meridiem: 'short',
};
}
break;
case 'second':
// sufficiently large number of different second cells?
if ((internal_cjs.asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
format1 = (params) => (':' + internal_cjs.padStart(params.date.second, 2) // ':30'
);
}
else {
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
}
break;
case 'millisecond':
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
format1 = (params) => ('.' + internal_cjs.padStart(params.millisecond, 3));
break;
}
return [].concat(format0 || [], format1 || [], format2 || []);
}
// Compute the number of the give units in the "current" range.
// Won't go more precise than days.
// Will return `0` if there's not a clean whole interval.
function currentRangeAs(unit, dateProfile, dateEnv) {
let range = dateProfile.currentRange;
let res = null;
if (unit === 'years') {
res = dateEnv.diffWholeYears(range.start, range.end);
}
else if (unit === 'months') {
res = dateEnv.diffWholeMonths(range.start, range.end);
}
else if (unit === 'weeks') {
res = dateEnv.diffWholeMonths(range.start, range.end);
}
else if (unit === 'days') {
res = internal_cjs.diffWholeDays(range.start, range.end);
}
return res || 0;
}
function buildIsWeekStarts(tDateProfile, dateEnv) {
let { slotDates, emphasizeWeeks } = tDateProfile;
let prevWeekNumber = null;
let isWeekStarts = [];
for (let slotDate of slotDates) {
let weekNumber = dateEnv.computeWeekNumber(slotDate);
let isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
prevWeekNumber = weekNumber;
isWeekStarts.push(isWeekStart);
}
return isWeekStarts;
}
function buildCellRows(tDateProfile, dateEnv) {
let slotDates = tDateProfile.slotDates;
let formats = tDateProfile.headerFormats;
let cellRows = formats.map(() => []); // indexed by row,col
let slotAsDays = internal_cjs.asCleanDays(tDateProfile.slotDuration);
let guessedSlotUnit = slotAsDays === 7 ? 'week' :
slotAsDays === 1 ? 'day' :
null;
// specifically for navclicks
let rowUnitsFromFormats = formats.map((format) => (format.getLargestUnit ? format.getLargestUnit() : null));
// builds cellRows and slotCells
for (let i = 0; i < slotDates.length; i += 1) {
let date = slotDates[i];
let isWeekStart = tDateProfile.isWeekStarts[i];
for (let row = 0; row < formats.length; row += 1) {
let format = formats[row];
let rowCells = cellRows[row];
let leadingCell = rowCells[rowCells.length - 1];
let isLastRow = row === formats.length - 1;
let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
let newCell = null;
let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
if (isSuperRow) {
let text = dateEnv.format(date, format);
if (!leadingCell || (leadingCell.text !== text)) {
newCell = buildCellObject(date, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
}
else if (!leadingCell ||
internal_cjs.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
let text = dateEnv.format(date, format);
newCell = buildCellObject(date, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
if (newCell) {
newCell.weekStart = isWeekStart;
rowCells.push(newCell);
}
}
}
return cellRows;
}
function buildCellObject(date, text, rowUnit) {
return { date, text, rowUnit, colspan: 1, isWeekStart: false };
}
class TimelineSlatCell extends internal_cjs.BaseComponent {
constructor() {
super(...arguments);
// ref
this.innerElRef = preact_cjs.createRef();
}
render() {
let { props, context } = this;
let { dateEnv, options } = context;
let { date, tDateProfile, isEm } = props;
let dateMeta = internal_cjs.getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
return (preact_cjs.createElement(internal_cjs.ContentContainer, { tag: "div",
// fc-align-start shrinks width of InnerContent
// TODO: document this semantic className fc-timeline-slot-em
className: internal_cjs.joinClassNames('fc-timeline-slot', isEm && 'fc-timeline-slot-em', tDateProfile.isTimeScale && (internal_cjs.isInt(dateEnv.countDurationsBetween(// best to do this here?
tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
'fc-timeline-slot-major' :
'fc-timeline-slot-minor'), 'fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start', props.borderStart && 'fc-border-s', props.isDay ?
internal_cjs.getDayClassName(dateMeta) :
internal_cjs.getSlotClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
omitTimeZoneOffset: true,
omitTime: !tDateProfile.isTimeScale,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.width,
}, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "div", className: 'fc-cell-inner', elRef: this.innerElRef }))));
}
componentDidMount() {
const innerEl = this.innerElRef.current;
this.disconnectInnerWidth = internal_cjs.watchWidth(innerEl, (width) => {
internal_cjs.setRef(this.props.innerWidthRef, width);
});
}
componentWillUnmount() {
this.disconnectInnerWidth();
internal_cjs.setRef(this.props.innerWidthRef, null);
}
}
class TimelineSlats extends internal_cjs.BaseComponent {
constructor() {
super(...arguments);
this.innerWidthRefMap = new internal_cjs.RefMap(() => {
internal_cjs.afterSize(this.handleInnerWidths);
});
this.handleInnerWidths = () => {
const innerWidthMap = this.innerWidthRefMap.current;
let max = 0;
for (const innerWidth of innerWidthMap.values()) {
max = Math.max(max, innerWidth);
}
// TODO: check to see if changed before firing ref!? YES. do in other places too
internal_cjs.setRef(this.props.innerWidthRef, max);
};
}
render() {
let { props, innerWidthRefMap } = this;
let { tDateProfile, slotWidth } = props;
let { slotDates, isWeekStarts } = tDateProfile;
let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
return (preact_cjs.createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
let key = slotDate.toISOString();
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, borderStart: Boolean(i),
// ref
innerWidthRef: innerWidthRefMap.createRef(key),
// dimensions
width: slotWidth }));
})));
}
}
/*
TODO: rename this file!
*/
// returned value is between 0 and the number of snaps
function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
/*
TODO: DRY up with elsewhere?
*/
function horizontalsToCss(hcoord, isRtl) {
if (!hcoord) {
return {};
}
if (isRtl) {
return { right: hcoord.start, width: hcoord.size };
}
else {
return { left: hcoord.start, width: hcoord.size };
}
}
function horizontalCoordToCss(start, isRtl) {
if (isRtl) {
return { right: start };
}
else {
return { left: start };
}
}
function createVerticalStyle(props) {
if (props) {
return {
top: props.start,
height: props.size,
};
}
}
function createHorizontalStyle(// TODO: DRY up?
props, isRtl) {
if (props) {
return {
[isRtl ? 'right' : 'left']: props.start,
width: props.size,
};
}
}
// Timeline-specific
// -------------------------------------------------------------------------------------------------
const MIN_SLOT_WIDTH = 30; // for real
/*
TODO: DRY with computeSlatHeight?
*/
function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
if (labelInnerWidth == null || viewportWidth == null) {
return [undefined, undefined, false];
}
slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
const slatTryWidth = viewportWidth / slatCnt;
let slatLiquid;
let slatWidth;
if (slatTryWidth >= slatMinWidth) {
slatLiquid = true;
slatWidth = slatTryWidth;
}
else {
slatLiquid = false;
slatWidth = Math.max(slatMinWidth, slatTryWidth);
}
return [slatWidth * slatCnt, slatWidth, slatLiquid];
}
function timeToCoord(// pixels
time, dateEnv, dateProfile, tDateProfile, slowWidth) {
let date = dateEnv.add(dateProfile.activeRange.start, time);
if (!tDateProfile.isTimeScale) {
date = internal_cjs.startOfDay(date);
}
return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
}
function dateToCoord(// pixels
date, dateEnv, tDateProfile, slotWidth) {
let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
return slotCoverage * slotWidth;
}
/*
returned value is between 0 and the number of snaps
*/
function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const res = {};
for (const seg of segs) {
res[internal_cjs.getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
}
return res;
}
function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
let size = endCoord - startCoord;
if (segMinWidth) {
size = Math.max(size, segMinWidth);
}
return { start: startCoord, size };
}
function computeFgSegPlacements(// mostly horizontals
segs, segHorizontals, // TODO: use Map
segHeights, // keyed by instanceId
hiddenGroupHeights, strictOrder, maxDepth) {
const segRanges = [];
// isn't it true that there will either be ALL hcoords or NONE? can optimize
for (const seg of segs) {
const hcoords = segHorizontals[internal_cjs.getEventKey(seg)];
if (hcoords) {
segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
}
}
const hierarchy = new internal_cjs.SegHierarchy(segRanges, (seg) => segHeights.get(internal_cjs.getEventKey(seg)), strictOrder, undefined, // maxCoord
maxDepth);
const segTops = new Map();
hierarchy.traverseSegs((seg, segTop) => {
segTops.set(internal_cjs.getEventKey(seg), segTop);
});
const { hiddenSegs } = hierarchy;
let totalHeight = 0;
for (const segRange of segRanges) {
const segKey = internal_cjs.getEventKey(segRange);
const segHeight = segHeights.get(segKey);
const segTop = segTops.get(segKey);
if (segHeight != null) {
if (segTop != null) {
totalHeight = Math.max(totalHeight, segTop + segHeight);
}
}
}
const hiddenGroups = internal_cjs.groupIntersectingSegs(hiddenSegs);
const hiddenGroupTops = new Map();
// HACK for hiddenGroup findInsertion() call
hierarchy.strictOrder = true;
for (const hiddenGroup of hiddenGroups) {
const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
hiddenGroupTops.set(hiddenGroup.key, top);
totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
}
return [
segTops,
hiddenGroups,
hiddenGroupTops,
totalHeight,
];
}
class TimelineLaneBg extends internal_cjs.BaseComponent {
render() {
let { props } = this;
let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
return (preact_cjs.createElement(preact_cjs.Fragment, null,
this.renderSegs(props.businessHourSegs || [], 'non-business'),
this.renderSegs(props.bgEventSegs || [], 'bg-event'),
this.renderSegs(highlightSeg, 'highlight')));
}
renderSegs(segs, fillType) {
let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
let { dateEnv, isRtl } = this.context;
return (preact_cjs.createElement(preact_cjs.Fragment, null, segs.map((seg) => {
let hStyle; // TODO
if (slotWidth != null) {
let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
hStyle = horizontalsToCss(segHorizontal, isRtl);
}
return (preact_cjs.createElement("div", { key: internal_cjs.buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
preact_cjs.createElement(internal_cjs.BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, internal_cjs.getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (internal_cjs.renderFill(fillType))));
})));
}
}
class TimelineLaneSlicer extends internal_cjs.Slicer {
sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
let segs = [];
// protect against when the span is entirely in an invalid date region
if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
< computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
// intersect the footprint's range with the grid's range
let slicedRange = internal_cjs.intersectRanges(normalRange, tDateProfile.normalizedRange);
if (slicedRange) {
segs.push({
startDate: slicedRange.start,
endDate: slicedRange.end,
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
&& isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
&& isValidDate(internal_cjs.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
});
}
}
return segs;
}
}
const DEFAULT_TIME_FORMAT = internal_cjs.createFormatter({
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'narrow',
});
class TimelineEvent extends internal_cjs.BaseComponent {
render() {
let { props, context } = this;
let { options } = context;
return (preact_cjs.createElement(internal_cjs.StandardEvent, Object.assign({}, props, { className: internal_cjs.joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
&& 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
}
}
class TimelineLaneMoreLink extends internal_cjs.BaseComponent {
render() {
let { props } = this;
let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
let dateSpanProps = resourceId ? { resourceId } : {};
return (preact_cjs.createElement(internal_cjs.MoreLinkContainer, { className: 'fc-timeline-more-link', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, popoverContent: () => (preact_cjs.createElement(preact_cjs.Fragment, null, hiddenSegs.map((seg) => {
let { eventRange } = seg;
let instanceId = eventRange.instance.instanceId;
return (preact_cjs.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection }, internal_cjs.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
}))) }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
}
}
/*
TODO: make DRY with other Event Harnesses
*/
class TimelineEventHarness extends preact_cjs.Component {
constructor() {
super(...arguments);
// ref
this.rootElRef = preact_cjs.createRef();
}
render() {
const { props } = this;
return (preact_cjs.createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
}
componentDidMount() {
const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
this.disconnectHeight = internal_cjs.watchHeight(rootEl, (height) => {
internal_cjs.setRef(this.props.heightRef, height);
});
}
componentWillUnmount() {
this.disconnectHeight();
internal_cjs.setRef(this.props.heightRef, null);
}
}
/*
TODO: split TimelineLaneBg and TimelineLaneFg?
*/
class TimelineLane extends internal_cjs.BaseComponent {
constructor() {
super(...arguments);
// memo
this.sortEventSegs = internal_cjs.memoize(internal_cjs.sortEventSegs);
// refs
this.segHeightRefMap = new internal_cjs.RefMap(() => {
internal_cjs.afterSize(this.handleSegHeights);
});
this.moreLinkHeightRefMap = new internal_cjs.RefMap(() => {
internal_cjs.afterSize(this.handleMoreLinkHeights);
});
// internal
this.slicer = new TimelineLaneSlicer();
this.handleMoreLinkHeights = () => {
this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
};
this.handleSegHeights = () => {
this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
};
}
/*
TODO: lots of memoization needed here!
*/
render() {
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
let { options } = context;
let { dateProfile, tDateProfile } = props;
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...
dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
(slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
[];
let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
let fgSegHorizontals = props.slotWidth != null
? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {};
let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
let forcedInvisibleMap = // TODO: more convenient/DRY
(slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
(slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
{};
return (preact_cjs.createElement(preact_cjs.Fragment, null,
preact_cjs.createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
// content
bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
// dimensions
slotWidth: props.slotWidth }),
preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
? 'fc-timeline-events-overlap-disabled'
: 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
false, // isResizing
false),
this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {}, fgSegTops, {}, // forcedInvisibleMap
[], // hiddenGroups
new Map(), // hiddenGroupTops
Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
}
renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
let isMirror = isDragging || isResizing || isDateSelecting;
return (preact_cjs.createElement(preact_cjs.Fragment, null,
segs.map((seg) => {
const { eventRange } = seg;
const { instanceId } = eventRange.instance;
const segTop = segTops.get(instanceId);
const segHorizontal = segHorizontals[instanceId];
const isVisible = isMirror ||
(segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
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) },
preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection /* TODO: bad for mirror? */ }, internal_cjs.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
}),
hiddenGroups.map((hiddenGroup) => (preact_cjs.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
start: hiddenGroup.start,
size: hiddenGroup.end - hiddenGroup.start
}, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
preact_cjs.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 }))))));
}
}
class TimelineHeaderCell extends internal_cjs.BaseComponent {
constructor() {
super(...arguments);
// memo
this.refineRenderProps = internal_cjs.memoizeObjArg(refineRenderProps);
// ref
this.innerElRef = preact_cjs.createRef();
}
render() {
let { props, context } = this;
let { dateEnv, options } = context;
let { cell, dateProfile, tDateProfile } = props;
// the cell.rowUnit is f'd
// giving 'month' for a 3-day view
// workaround: to infer day, do NOT time
let dateMeta = internal_cjs.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
let renderProps = this.refineRenderProps({
level: props.rowLevel,
dateMarker: cell.date,
text: cell.text,
dateEnv: context.dateEnv,
viewApi: context.viewApi,
});
let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
return (preact_cjs.createElement(internal_cjs.ContentContainer, { tag: "div", className: internal_cjs.joinClassNames('fc-timeline-slot-label fc-timeline-slot', cell.isWeekStart && 'fc-timeline-slot-em', // TODO: document this semantic className
'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
// TODO: so slot classnames for week/month/bigger. see note above about rowUnit
cell.rowUnit === 'time' ?
internal_cjs.getSlotClassName(dateMeta) :
internal_cjs.getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
omitTime: !tDateProfile.isTimeScale,
omitTimeZoneOffset: true,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.slotWidth != null
? props.slotWidth * cell.colspan
: undefined,
}, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: 'div', attrs: isNavLink
// not tabbable because parent is aria-hidden
? internal_cjs.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
: {} // don't bother with aria-hidden because parent already hidden
, className: internal_cjs.joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
}
componentDidMount() {
const { props } = this;
const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
this.detachSize = internal_cjs.watchSize(innerEl, (width, height) => {
internal_cjs.setRef(props.innerWidthRef, width);
internal_cjs.setRef(props.innerHeightRef, height);
// HACK for sticky-centering
innerEl.style.left = innerEl.style.right =
(props.isCentered && props.isSticky)
? `calc(50% - ${width / 2}px)`
: '';
});
}
componentWillUnmount() {
const { props } = this;
this.detachSize();
internal_cjs.setRef(props.innerWidthRef, null);
internal_cjs.setRef(props.innerHeightRef, null);
}
}
// Utils
// -------------------------------------------------------------------------------------------------
function renderInnerContent(renderProps) {
return renderProps.text;
}
function refineRenderProps(input) {
return {
level: input.level,
date: input.dateEnv.toDate(input.dateMarker),
view: input.viewApi,
text: input.text,
};
}
class TimelineHeaderRow extends internal_cjs.BaseComponent {
constructor() {
super(...arguments);
// refs
this.innerWidthRefMap = new internal_cjs.RefMap(() => {
internal_cjs.afterSize(this.handleInnerWidths);
});
this.innerHeightRefMap = new internal_cjs.RefMap(() => {
internal_cjs.afterSize(this.handleInnerHeights);
});
this.handleInnerWidths = () => {
const innerWidthMap = this.innerWidthRefMap.current;
let max = 0;
for (const innerWidth of innerWidthMap.values()) {
max = Math.max(max, innerWidth);
}
// TODO: ensure not equal?
internal_cjs.setRef(this.props.innerWidthRef, max);
};
this.handleInnerHeights = () => {
const innerHeightMap = this.innerHeightRefMap.current;
let max = 0;
for (const innerHeight of innerHeightMap.values()) {
max = Math.max(max, innerHeight);
}
// TODO: ensure not equal?
internal_cjs.setRef(this.props.innerHeighRef, max);
};
}
render() {
const { props, innerWidthRefMap, innerHeightRefMap } = this;
const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
const isSticky = !props.isLastRow;
return (preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
!props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
// TODO: make this part of the cell obj?
// TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
// TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
// TODO: use rowUnit to key the Row itself?
const key = cell.rowUnit + ':' + cell.date.toISOString();
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, borderStart: Boolean(cellI),
// refs
innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
// dimensions
slotWidth: props.slotWidth }));
})));
}
componentWillUnmount() {
internal_cjs.setRef(this.props.innerWidthRef, null);
internal_cjs.setRef(this.props.innerHeighRef, null);
}
}
class TimelineNowIndicatorLine extends internal_cjs.BaseComponent {
render() {
const { props, context } = this;
return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
preact_cjs.createElement(internal_cjs.NowIndicatorContainer // TODO: make separate component?
, { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
: {}, isAxis: false, date: props.nowDate })));
}
}
class TimelineNowIndicatorArrow extends internal_cjs.BaseComponent {
render() {
const { props, context } = this;
return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
preact_cjs.createElement(internal_cjs.NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
: {}, isAxis: true, date: props.nowDate })));
}
}
class TimelineView extends internal_cjs.DateComponent {
constructor() {
super(...arguments);
// memoized
this.buildTimelineDateProfile = internal_cjs.memoize(buildTimelineDateProfile);
this.computeSlotWidth = internal_cjs.memoize(computeSlotWidth);
// refs
this.headerScrollerRef = preact_cjs.createRef();
this.bodyScrollerRef = preact_cjs.createRef();
this.footerScrollerRef = preact_cjs.createRef();
this.headerRowInnerWidthMap = new internal_cjs.RefMap(() => {
internal_cjs.afterSize(this.handleSlotInnerWidths);
});
this.scrollTime = null;
// Sizing
// -----------------------------------------------------------------------------------------------
this.handleBodySlotInnerWidth = (innerWidth) => {
this.bodySlotInnerWidth = innerWidth;
internal_cjs.afterSize(this.handleSlotInnerWidths);
};
this.handleSlotInnerWidths = () => {
const { state } = this;
const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
if (state.slotInnerWidth !== slotInnerWidth) {
this.setState({ slotInnerWidth });
}
};
this.handleClientWidth = (clientWidth) => {
this.setState({
clientWidth,
});
};
this.handleEndScrollbarWidth = (endScrollbarWidth) => {
this.setState({
endScrollbarWidth
});
};
this.handleTimeScrollRequest = (scrollTime) => {
this.scrollTime = scrollTime;
this.applyTimeScroll();
};
this.handleTimeScrollEnd = ({ isUser }) => {
if (isUser) {
this.scrollTime = null;
}
};
// Hit System
// -----------------------------------------------------------------------------------------------
this.handeBodyEl = (el) => {
this.bodyEl = el;
if (el) {
this.context.registerInteractiveComponent(this, { el });
}
else {
this.context.unregisterInteractiveComponent(this);
}
};
}
render() {
const { props, state, context } = this;
const { options } = context;
/* date */
const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
const { cellRows } = tDateProfile;
const timerUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
/* table settings */
const verticalScrolling = !props.forPrint && !internal_cjs.getIsHeightAuto(options);
const stickyHeaderDates = !props.forPrint && internal_cjs.getStickyHeaderDates(options);
const stickyFooterScrollbar = !props.forPrint && internal_cjs.getStickyFooterScrollbar(options);
/* table positions */
const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
state.clientWidth);
this.slotWidth = slotWidth;
return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
const enableNowIndicator = // TODO: DRY
options.nowIndicator &&
slotWidth != null &&
internal_cjs.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
return (preact_cjs.createElement(internal_cjs.ViewContainer, { viewSpec: context.viewSpec, className: internal_cjs.joinClassNames('fc-timeline fc-border',
// HACK for Safari print-mode, where fc-scroller-no-bars won't take effect for
// the below Scrollers if they have liquid flex height
!props.forPrint && 'fc-flex-col') },
preact_cjs.createElement(internal_cjs.Scroller, { horizontal: true, hideScrollbars: true, className: internal_cjs.joinClassNames('fc-timeline-header fc-flex-row fc-border-b', stickyHeaderDates && 'fc-table-header-sticky'), ref: this.headerScrollerRef },
preact_cjs.createElement("div", {
// TODO: DRY
className: internal_cjs.joinClassNames('fc-rel', // origin for now-indicator
canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
cellRows.map((cells, rowLevel) => {
const isLast = rowLevel === cellRows.length - 1;
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: this.headerRowInnerWidthMap.createRef(rowLevel) }));
}),
enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
Boolean(state.endScrollbarWidth) && (preact_cjs.createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
preact_cjs.createElement(internal_cjs.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: props.forPrint, className: internal_cjs.joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth },
preact_cjs.createElement("div", { "aria-label": options.eventsHint, className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
preact_cjs.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// ref
innerWidthRef: this.handleBodySlotInnerWidth,
// dimensions
slotWidth: slotWidth }),
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 }),
enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
stickyFooterScrollbar && (preact_cjs.createElement(internal_cjs.Scroller, { ref: this.footerScrollerRef, horizontal: true },
preact_cjs.createElement("div", { style: { width: canvasWidth } })))));
}));
}
// Lifecycle
// -----------------------------------------------------------------------------------------------
componentDidMount() {
this.syncedScroller = new internal_cjs$1.ScrollerSyncer(true); // horizontal=true
this.updateSyncedScroller();
this.resetScroll();
this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
}
componentDidUpdate(prevProps) {
this.updateSyncedScroller();
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
this.resetScroll();
}
else {
// TODO: inefficient to update so often
this.applyTimeScroll();
}
}
componentWillUnmount() {
this.syncedScroller.destroy();
this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
}
// Scrolling
// -----------------------------------------------------------------------------------------------
updateSyncedScroller() {
this.syncedScroller.handleChildren([
this.headerScrollerRef.current,
this.bodyScrollerRef.current,
this.footerScrollerRef.current
]);
}
resetScroll() {
this.handleTimeScrollRequest(this.context.options.scrollTime);
}
applyTimeScroll() {
const { props, context, tDateProfile, scrollTime, slotWidth } = this;
if (scrollTime != null && slotWidth != null) {
let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
if (x) {
x += 1; // overcome border. TODO: DRY this up
}
this.syncedScroller.scrollTo({ x });
}
}
queryHit(positionLeft, positionTop, elWidth, elHeight) {
const { props, context, tDateProfile, slotWidth } = this;
const { dateEnv } = context;
if (slotWidth) {
const x = context.isRtl ? elWidth - positionLeft : positionLeft;
const slatIndex = Math.floor(x / slotWidth);
const slatX = slatIndex * slotWidth;
const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], internal_cjs.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
// TODO: generalize this coord stuff to TimeGrid?
let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
let endCoord = startCoord + snapWidth;
let left, right;
if (context.isRtl) {
left = elWidth - endCoord;
right = elWidth - startCoord;
}
else {
left = startCoord;
right = endCoord;
}
return {
dateProfile: props.dateProfile,
dateSpan: {
range: { start: startDate, end: endDate },
allDay: !tDateProfile.isTimeScale,
},
rect: {
left,
right,
top: 0,
bottom: elHeight,
},
// HACK. TODO: This is expensive to do every hit-query
dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
layer: 0,
};
}
return null;
}
}
var css_248z = ".fc-timeline-slots{z-index:1}.fc-timeline-events{position:relative;z-index:2}.fc-timeline-slot-minor{border-style:dotted}.fc-timeline-events-overlap-enabled{padding-bottom:10px}.fc-timeline-event{border-radius:0;font-size:var(--fc-small-font-size);margin-bottom:1px}.fc-direction-ltr .fc-timeline-event.fc-event-end{margin-right:1px}.fc-direction-rtl .fc-timeline-event.fc-event-end{margin-left:1px}.fc-timeline-event .fc-event-inner{align-items:center;display:flex;flex-direction:row;padding:2px 1px}.fc-timeline-event-spacious .fc-event-inner{padding-bottom:5px;padding-top:5px}.fc-timeline-event .fc-event-time{font-weight:700}.fc-timeline-event .fc-event-time,.fc-timeline-event .fc-event-title{padding:0 2px}.fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-color:transparent #000;border-style:solid;border-width:5px;content:\"\";flex-grow:0;flex-shrink:0;height:0;margin:0 1px;opacity:.5;width:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before,.fc-direction-rtl .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after{border-left:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-direction-rtl .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-right:0}.fc-timeline-more-link{align-items:flex-start;background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;display:flex;flex-direction:column;font-size:var(--fc-small-font-size);padding:1px}.fc-direction-ltr .fc-timeline-more-link{margin-right:1px}.fc-direction-rtl .fc-timeline-more-link{margin-left:1px}.fc-timeline-more-link-inner{padding:2px}.fc-timeline-now-indicator-container{bottom:0;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;top:0;z-index:4}.fc-timeline-now-indicator-arrow{border-bottom-style:solid;border-bottom-width:0;border-color:var(--fc-now-indicator-color);border-left:5px solid transparent;border-right:5px solid transparent;border-top-style:solid;border-top-width:6px;height:0;margin:0 -5px;position:absolute;top:0;width:0}.fc-timeline-now-indicator-line{border-left:1px solid var(--fc-now-indicator-color);bottom:0;position:absolute;top:0}";
internal_cjs.injectStyles(css_248z);
exports.TimelineHeaderRow = TimelineHeaderRow;
exports.TimelineLane = TimelineLane;
exports.TimelineLaneBg = TimelineLaneBg;
exports.TimelineLaneSlicer = TimelineLaneSlicer;
exports.TimelineNowIndicatorArrow = TimelineNowIndicatorArrow;
exports.TimelineNowIndicatorLine = TimelineNowIndicatorLine;
exports.TimelineSlats = TimelineSlats;
exports.TimelineView = TimelineView;
exports.buildTimelineDateProfile = buildTimelineDateProfile;
exports.computeSlotWidth = computeSlotWidth;
exports.createHorizontalStyle = createHorizontalStyle;
exports.createVerticalStyle = createVerticalStyle;
exports.timeToCoord = timeToCoord;
import { DateComponent, ViewProps, Hit, DateRange, DateMarker, DateProfile, DateEnv, BaseOptionsRefined, DateProfileGenerator, Slicer, CoordRange, EventStore, EventUiHash, DateSpan, EventInteractionState, BaseComponent, EventRangeProps, CoordSpan, SegGroup } from '@fullcalendar/core/internal';
import { createElement, Ref } from '@fullcalendar/core/preact';
import { Duration } from '@fullcalendar/core';
interface TimelineViewState {
clientWidth?: number;
endScrollbarWidth?: number;
slotInnerWidth?: number;
}
declare class TimelineView extends DateComponent<ViewProps, TimelineViewState> {
private buildTimelineDateProfile;
private computeSlotWidth;
private headerScrollerRef;
private bodyScrollerRef;
private footerScrollerRef;
private tDateProfile?;
private bodyEl?;
private slotWidth?;
private headerRowInnerWidthMap;
private bodySlotInnerWidth?;
private syncedScroller;
private scrollTime;
render(): createElement.JSX.Element;
componentDidMount(): void;
componentDidUpdate(prevProps: ViewProps): void;
componentWillUnmount(): void;
handleBodySlotInnerWidth: (innerWidth: number) => void;
handleSlotInnerWidths: () => void;
handleClientWidth: (clientWidth: number) => void;
handleEndScrollbarWidth: (endScrollbarWidth: number) => void;
private updateSyncedScroller;
private resetScroll;
private handleTimeScrollRequest;
private handleTimeScrollEnd;
private applyTimeScroll;
handeBodyEl: (el: HTMLElement | null) => void;
queryHit(positionLeft: number, positionTop: number, elWidth: number, elHeight: number): Hit;
}
interface TimelineDateProfile {
labelInterval: Duration;
slotDuration: Duration;
slotsPerLabel: number;
headerFormats: any;
isTimeScale: boolean;
largeUnit: string;
emphasizeWeeks: boolean;
snapDuration: Duration;
snapsPerSlot: number;
normalizedRange: DateRange;
timeWindowMs: number;
slotDates: DateMarker[];
isWeekStarts: boolean[];
snapDiffToIndex: number[];
snapIndexToDiff: number[];
snapCnt: number;
slotCnt: number;
cellRows: TimelineHeaderCellData[][];
}
interface TimelineHeaderCellData {
date: DateMarker;
text: string;
rowUnit: string;
colspan: number;
isWeekStart: boolean;
}
declare function buildTimelineDateProfile(dateProfile: DateProfile, dateEnv: DateEnv, allOptions: BaseOptionsRefined, dateProfileGenerator: DateProfileGenerator): TimelineDateProfile;
interface TimelineRange {
startDate: DateMarker;
endDate: DateMarker;
isStart: boolean;
isEnd: boolean;
}
type TimelineCoordRange = TimelineRange & CoordRange;
declare class TimelineLaneSlicer extends Slicer<TimelineRange, [
DateProfile,
DateProfileGenerator,
TimelineDateProfile,
DateEnv
]> {
sliceRange(origRange: DateRange, dateProfile: DateProfile, dateProfileGenerator: DateProfileGenerator, tDateProfile: TimelineDateProfile, dateEnv: DateEnv): TimelineRange[];
}
interface TimelineLaneProps {
dateProfile: DateProfile;
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
nextDayThreshold: Duration;
eventStore: EventStore | null;
eventUiBases: EventUiHash;
businessHours: EventStore | null;
dateSelection: DateSpan | null;
eventDrag: EventInteractionState | null;
eventResize: EventInteractionState | null;
eventSelection: string;
resourceId?: string;
slotWidth: number | undefined;
}
interface TimelineLaneState {
segHeightRev?: string;
moreLinkHeightRev?: string;
}
declare class TimelineLane extends BaseComponent<TimelineLaneProps, TimelineLaneState> {
private sortEventSegs;
private segHeightRefMap;
private moreLinkHeightRefMap;
private slicer;
render(): createElement.JSX.Element;
renderFgSegs(segs: (TimelineRange & EventRangeProps)[], segHorizontals: {
[instanceId: string]: CoordSpan;
}, segTops: Map<string, number>, forcedInvisibleMap: {
[instanceId: string]: any;
}, hiddenGroups: SegGroup<TimelineCoordRange>[], hiddenGroupTops: Map<string, number>, isDragging: boolean, isResizing: boolean, isDateSelecting: boolean): createElement.JSX.Element;
private handleMoreLinkHeights;
private handleSegHeights;
}
interface TimelineLaneBgProps {
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
bgEventSegs: (TimelineRange & EventRangeProps)[] | null;
businessHourSegs: (TimelineRange & EventRangeProps)[] | null;
dateSelectionSegs: (TimelineRange & EventRangeProps)[];
eventResizeSegs: (TimelineRange & EventRangeProps)[];
slotWidth: number | undefined;
}
declare class TimelineLaneBg extends BaseComponent<TimelineLaneBgProps> {
render(): createElement.JSX.Element;
renderSegs(segs: (TimelineRange & EventRangeProps)[], fillType: string): createElement.JSX.Element;
}
interface TimelineSlatsProps {
dateProfile: DateProfile;
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
height?: number;
slotWidth: number | undefined;
innerWidthRef?: Ref<number>;
}
declare class TimelineSlats extends BaseComponent<TimelineSlatsProps> {
private innerWidthRefMap;
render(): createElement.JSX.Element;
handleInnerWidths: () => void;
}
declare function createVerticalStyle(props: CoordSpan | undefined): {
top: number;
height: number;
} | undefined;
declare function createHorizontalStyle(// TODO: DRY up?
props: CoordSpan | undefined, isRtl: boolean): {
left: number;
width: number;
} | {
right: number;
width: number;
} | undefined;
declare function computeSlotWidth(slatCnt: number, slatsPerLabel: number, slatMinWidth: number | undefined, labelInnerWidth: number | undefined, viewportWidth: number | undefined): [
canvasWidth: number | undefined,
slatWidth: number | undefined,
slatLiquid: boolean
];
declare function timeToCoord(// pixels
time: Duration, dateEnv: DateEnv, dateProfile: DateProfile, tDateProfile: TimelineDateProfile, slowWidth: number): number;
interface TimelineHeaderRowProps {
dateProfile: DateProfile;
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
todayRange: DateRange;
rowLevel: number;
isLastRow: boolean;
cells: TimelineHeaderCellData[];
innerHeighRef?: Ref<number>;
innerWidthRef?: Ref<number>;
slotWidth: number | undefined;
}
declare class TimelineHeaderRow extends BaseComponent<TimelineHeaderRowProps> {
private innerWidthRefMap;
private innerHeightRefMap;
render(): createElement.JSX.Element;
handleInnerWidths: () => void;
handleInnerHeights: () => void;
componentWillUnmount(): void;
}
interface TimelineNowIndicatorArrowProps {
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
slotWidth: number | undefined;
}
declare class TimelineNowIndicatorArrow extends BaseComponent<TimelineNowIndicatorArrowProps> {
render(): createElement.JSX.Element;
}
interface TimelineNowIndicatorLineProps {
tDateProfile: TimelineDateProfile;
nowDate: DateMarker;
slotWidth: number | undefined;
}
declare class TimelineNowIndicatorLine extends BaseComponent<TimelineNowIndicatorLineProps> {
render(): createElement.JSX.Element;
}
export { TimelineDateProfile, TimelineHeaderRow, TimelineLane, TimelineLaneBg, TimelineLaneProps, TimelineLaneSlicer, TimelineNowIndicatorArrow, TimelineNowIndicatorLine, TimelineRange, TimelineSlats, TimelineView, buildTimelineDateProfile, computeSlotWidth, createHorizontalStyle, createVerticalStyle, timeToCoord };
import { config, createFormatter, greatestDurationDenominator, asCleanDays, createDuration, wholeDivideDurations, asRoughMs, addDays, startOfDay, asRoughSeconds, asRoughMinutes, diffWholeDays, isInt, computeVisibleDayRange, padStart, BaseComponent, getDateMeta, ContentContainer, joinClassNames, getDayClassName, getSlotClassName, watchWidth, setRef, RefMap, afterSize, getEventKey, SegHierarchy, groupIntersectingSegs, buildEventRangeKey, BgEvent, getEventRangeMeta, renderFill, Slicer, intersectRanges, addMs, StandardEvent, MoreLinkContainer, watchHeight, memoize, sortEventSegs, memoizeObjArg, buildNavLinkAttrs, watchSize, NowIndicatorContainer, DateComponent, getIsHeightAuto, getStickyHeaderDates, getStickyFooterScrollbar, NowTimer, rangeContainsMarker, ViewContainer, Scroller, multiplyDuration, injectStyles } from '@fullcalendar/core/internal.js';
import { createRef, createElement, Fragment, Component } from '@fullcalendar/core/preact.js';
import { ScrollerSyncer } from '@fullcalendar/scrollgrid/internal.js';
const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
const MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
config.MAX_TIMELINE_SLOTS = 1000;
// potential nice values for slot-duration and interval-duration
const STOCK_SUB_DURATIONS = [
{ years: 1 },
{ months: 1 },
{ days: 1 },
{ hours: 1 },
{ minutes: 30 },
{ minutes: 15 },
{ minutes: 10 },
{ minutes: 5 },
{ minutes: 1 },
{ seconds: 30 },
{ seconds: 15 },
{ seconds: 10 },
{ seconds: 5 },
{ seconds: 1 },
{ milliseconds: 500 },
{ milliseconds: 100 },
{ milliseconds: 10 },
{ milliseconds: 1 },
];
function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
let tDateProfile = {
labelInterval: allOptions.slotLabelInterval,
slotDuration: allOptions.slotDuration,
};
validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
let input = allOptions.slotLabelFormat;
let rawFormats = Array.isArray(input) ? input :
(input != null) ? [input] :
computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
tDateProfile.headerFormats = rawFormats.map((rawFormat) => createFormatter(rawFormat));
tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
let largeUnit = null;
if (!tDateProfile.isTimeScale) {
const slotUnit = greatestDurationDenominator(tDateProfile.slotDuration).unit;
if (/year|month|week/.test(slotUnit)) {
largeUnit = slotUnit;
}
}
tDateProfile.largeUnit = largeUnit;
tDateProfile.emphasizeWeeks =
asCleanDays(tDateProfile.slotDuration) === 1 &&
currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
!allOptions.businessHours;
/*
console.log('label interval =', timelineView.labelInterval.humanize())
console.log('slot duration =', timelineView.slotDuration.humanize())
console.log('header formats =', timelineView.headerFormats)
console.log('isTimeScale', timelineView.isTimeScale)
console.log('largeUnit', timelineView.largeUnit)
*/
let rawSnapDuration = allOptions.snapDuration;
let snapDuration;
let snapsPerSlot;
if (rawSnapDuration) {
snapDuration = createDuration(rawSnapDuration);
snapsPerSlot = wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
// ^ TODO: warning if not whole?
}
if (snapsPerSlot == null) {
snapDuration = tDateProfile.slotDuration;
snapsPerSlot = 1;
}
tDateProfile.snapDuration = snapDuration;
tDateProfile.snapsPerSlot = snapsPerSlot;
// more...
let timeWindowMs = asRoughMs(dateProfile.slotMaxTime) - asRoughMs(dateProfile.slotMinTime);
// TODO: why not use normalizeRange!?
let normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
let normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
// apply slotMinTime/slotMaxTime
// TODO: View should be responsible.
if (tDateProfile.isTimeScale) {
normalizedStart = dateEnv.add(normalizedStart, dateProfile.slotMinTime);
normalizedEnd = dateEnv.add(addDays(normalizedEnd, -1), dateProfile.slotMaxTime);
}
tDateProfile.timeWindowMs = timeWindowMs;
tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
let slotDates = [];
let date = normalizedStart;
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
slotDates.push(date);
}
date = dateEnv.add(date, tDateProfile.slotDuration);
}
tDateProfile.slotDates = slotDates;
// more...
let snapIndex = -1;
let snapDiff = 0; // index of the diff :(
const snapDiffToIndex = [];
const snapIndexToDiff = [];
date = normalizedStart;
while (date < normalizedEnd) {
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
snapIndex += 1;
snapDiffToIndex.push(snapIndex);
snapIndexToDiff.push(snapDiff);
}
else {
snapDiffToIndex.push(snapIndex + 0.5);
}
date = dateEnv.add(date, tDateProfile.snapDuration);
snapDiff += 1;
}
tDateProfile.snapDiffToIndex = snapDiffToIndex;
tDateProfile.snapIndexToDiff = snapIndexToDiff;
tDateProfile.snapCnt = snapIndex + 1; // is always one behind
tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
// more...
tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv);
tDateProfile.slotsPerLabel = wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
return tDateProfile;
}
/*
snaps to appropriate unit
*/
function normalizeDate(date, tDateProfile, dateEnv) {
let normalDate = date;
if (!tDateProfile.isTimeScale) {
normalDate = startOfDay(normalDate);
if (tDateProfile.largeUnit) {
normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
}
}
return normalDate;
}
/*
snaps to appropriate unit
*/
function normalizeRange(range, tDateProfile, dateEnv) {
if (!tDateProfile.isTimeScale) {
range = computeVisibleDayRange(range);
if (tDateProfile.largeUnit) {
let dayRange = range; // preserve original result
range = {
start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
end: dateEnv.startOf(range.end, tDateProfile.largeUnit),
};
// if date is partially through the interval, or is in the same interval as the start,
// make the exclusive end be the *next* interval
if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
range = {
start: range.start,
end: dateEnv.add(range.end, tDateProfile.slotDuration),
};
}
}
}
return range;
}
function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
if (dateProfileGenerator.isHiddenDay(date)) {
return false;
}
if (tDateProfile.isTimeScale) {
// determine if the time is within slotMinTime/slotMaxTime, which may have wacky values
let day = startOfDay(date);
let timeMs = date.valueOf() - day.valueOf();
let ms = timeMs - asRoughMs(dateProfile.slotMinTime); // milliseconds since slotMinTime
ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
return ms < tDateProfile.timeWindowMs; // before the slotMaxTime?
}
return true;
}
function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
// make sure labelInterval doesn't exceed the max number of cells
if (tDateProfile.labelInterval) {
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
if (labelCnt > config.MAX_TIMELINE_SLOTS) {
console.warn('slotLabelInterval results in too many cells');
tDateProfile.labelInterval = null;
}
}
// make sure slotDuration doesn't exceed the maximum number of cells
if (tDateProfile.slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
if (slotCnt > config.MAX_TIMELINE_SLOTS) {
console.warn('slotDuration results in too many cells');
tDateProfile.slotDuration = null;
}
}
// make sure labelInterval is a multiple of slotDuration
if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
const slotsPerLabel = wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
if (slotsPerLabel === null || slotsPerLabel < 1) {
console.warn('slotLabelInterval must be a multiple of slotDuration');
tDateProfile.slotDuration = null;
}
}
}
function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { labelInterval } = tDateProfile;
if (!labelInterval) {
// compute based off the slot duration
// find the largest label interval with an acceptable slots-per-label
let input;
if (tDateProfile.slotDuration) {
for (input of STOCK_SUB_DURATIONS) {
const tryLabelInterval = createDuration(input);
const slotsPerLabel = wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
labelInterval = tryLabelInterval;
break;
}
}
// use the slot duration as a last resort
if (!labelInterval) {
labelInterval = tDateProfile.slotDuration;
}
// compute based off the view's duration
// find the largest label interval that yields the minimum number of labels
}
else {
for (input of STOCK_SUB_DURATIONS) {
labelInterval = createDuration(input);
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
if (labelCnt >= MIN_AUTO_LABELS) {
break;
}
}
}
tDateProfile.labelInterval = labelInterval;
}
return labelInterval;
}
function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
const { currentRange } = dateProfile;
let { slotDuration } = tDateProfile;
if (!slotDuration) {
const labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
// compute based off the label interval
// find the largest slot duration that is different from labelInterval, but still acceptable
for (let input of STOCK_SUB_DURATIONS) {
const trySlotDuration = createDuration(input);
const slotsPerLabel = wholeDivideDurations(labelInterval, trySlotDuration);
if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
slotDuration = trySlotDuration;
break;
}
}
// only allow the value if it won't exceed the view's # of slots limit
if (slotDuration) {
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
if (slotCnt > MAX_AUTO_CELLS) {
slotDuration = null;
}
}
// use the label interval as a last resort
if (!slotDuration) {
slotDuration = labelInterval;
}
tDateProfile.slotDuration = slotDuration;
}
return slotDuration;
}
function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
let format1;
let format2;
const { labelInterval } = tDateProfile;
let unit = greatestDurationDenominator(labelInterval).unit;
const weekNumbersVisible = allOptions.weekNumbers;
let format0 = (format1 = (format2 = null));
// NOTE: weekNumber computation function wont work
if ((unit === 'week') && !weekNumbersVisible) {
unit = 'day';
}
switch (unit) {
case 'year':
format0 = { year: 'numeric' }; // '2015'
break;
case 'month':
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { month: 'short' }; // 'Jan'
break;
case 'week':
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
format0 = { year: 'numeric' }; // '2015'
}
format1 = { week: 'narrow' }; // 'Wk4'
break;
case 'day':
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
}
else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
format0 = { month: 'long' }; // 'January'
}
if (weekNumbersVisible) {
format1 = { week: 'short' }; // 'Wk 4'
}
format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
break;
case 'hour':
if (weekNumbersVisible) {
format0 = { week: 'short' }; // 'Wk 4'
}
if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
}
format2 = {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'short',
};
break;
case 'minute':
// sufficiently large number of different minute cells?
if ((asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = {
hour: 'numeric',
meridiem: 'short',
};
format1 = (params) => (':' + padStart(params.date.minute, 2) // ':30'
);
}
else {
format0 = {
hour: 'numeric',
minute: 'numeric',
meridiem: 'short',
};
}
break;
case 'second':
// sufficiently large number of different second cells?
if ((asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
format1 = (params) => (':' + padStart(params.date.second, 2) // ':30'
);
}
else {
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
}
break;
case 'millisecond':
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
format1 = (params) => ('.' + padStart(params.millisecond, 3));
break;
}
return [].concat(format0 || [], format1 || [], format2 || []);
}
// Compute the number of the give units in the "current" range.
// Won't go more precise than days.
// Will return `0` if there's not a clean whole interval.
function currentRangeAs(unit, dateProfile, dateEnv) {
let range = dateProfile.currentRange;
let res = null;
if (unit === 'years') {
res = dateEnv.diffWholeYears(range.start, range.end);
}
else if (unit === 'months') {
res = dateEnv.diffWholeMonths(range.start, range.end);
}
else if (unit === 'weeks') {
res = dateEnv.diffWholeMonths(range.start, range.end);
}
else if (unit === 'days') {
res = diffWholeDays(range.start, range.end);
}
return res || 0;
}
function buildIsWeekStarts(tDateProfile, dateEnv) {
let { slotDates, emphasizeWeeks } = tDateProfile;
let prevWeekNumber = null;
let isWeekStarts = [];
for (let slotDate of slotDates) {
let weekNumber = dateEnv.computeWeekNumber(slotDate);
let isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
prevWeekNumber = weekNumber;
isWeekStarts.push(isWeekStart);
}
return isWeekStarts;
}
function buildCellRows(tDateProfile, dateEnv) {
let slotDates = tDateProfile.slotDates;
let formats = tDateProfile.headerFormats;
let cellRows = formats.map(() => []); // indexed by row,col
let slotAsDays = asCleanDays(tDateProfile.slotDuration);
let guessedSlotUnit = slotAsDays === 7 ? 'week' :
slotAsDays === 1 ? 'day' :
null;
// specifically for navclicks
let rowUnitsFromFormats = formats.map((format) => (format.getLargestUnit ? format.getLargestUnit() : null));
// builds cellRows and slotCells
for (let i = 0; i < slotDates.length; i += 1) {
let date = slotDates[i];
let isWeekStart = tDateProfile.isWeekStarts[i];
for (let row = 0; row < formats.length; row += 1) {
let format = formats[row];
let rowCells = cellRows[row];
let leadingCell = rowCells[rowCells.length - 1];
let isLastRow = row === formats.length - 1;
let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
let newCell = null;
let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
if (isSuperRow) {
let text = dateEnv.format(date, format);
if (!leadingCell || (leadingCell.text !== text)) {
newCell = buildCellObject(date, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
}
else if (!leadingCell ||
isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
let text = dateEnv.format(date, format);
newCell = buildCellObject(date, text, rowUnit);
}
else {
leadingCell.colspan += 1;
}
if (newCell) {
newCell.weekStart = isWeekStart;
rowCells.push(newCell);
}
}
}
return cellRows;
}
function buildCellObject(date, text, rowUnit) {
return { date, text, rowUnit, colspan: 1, isWeekStart: false };
}
class TimelineSlatCell extends BaseComponent {
constructor() {
super(...arguments);
// ref
this.innerElRef = createRef();
}
render() {
let { props, context } = this;
let { dateEnv, options } = context;
let { date, tDateProfile, isEm } = props;
let dateMeta = getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
return (createElement(ContentContainer, { tag: "div",
// fc-align-start shrinks width of InnerContent
// TODO: document this semantic className fc-timeline-slot-em
className: joinClassNames('fc-timeline-slot', isEm && 'fc-timeline-slot-em', tDateProfile.isTimeScale && (isInt(dateEnv.countDurationsBetween(// best to do this here?
tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
'fc-timeline-slot-major' :
'fc-timeline-slot-minor'), 'fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start', props.borderStart && 'fc-border-s', props.isDay ?
getDayClassName(dateMeta) :
getSlotClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
omitTimeZoneOffset: true,
omitTime: !tDateProfile.isTimeScale,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.width,
}, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-cell-inner', elRef: this.innerElRef }))));
}
componentDidMount() {
const innerEl = this.innerElRef.current;
this.disconnectInnerWidth = watchWidth(innerEl, (width) => {
setRef(this.props.innerWidthRef, width);
});
}
componentWillUnmount() {
this.disconnectInnerWidth();
setRef(this.props.innerWidthRef, null);
}
}
class TimelineSlats extends BaseComponent {
constructor() {
super(...arguments);
this.innerWidthRefMap = new RefMap(() => {
afterSize(this.handleInnerWidths);
});
this.handleInnerWidths = () => {
const innerWidthMap = this.innerWidthRefMap.current;
let max = 0;
for (const innerWidth of innerWidthMap.values()) {
max = Math.max(max, innerWidth);
}
// TODO: check to see if changed before firing ref!? YES. do in other places too
setRef(this.props.innerWidthRef, max);
};
}
render() {
let { props, innerWidthRefMap } = this;
let { tDateProfile, slotWidth } = props;
let { slotDates, isWeekStarts } = tDateProfile;
let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
return (createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
let key = slotDate.toISOString();
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),
// ref
innerWidthRef: innerWidthRefMap.createRef(key),
// dimensions
width: slotWidth }));
})));
}
}
/*
TODO: rename this file!
*/
// returned value is between 0 and the number of snaps
function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
/*
TODO: DRY up with elsewhere?
*/
function horizontalsToCss(hcoord, isRtl) {
if (!hcoord) {
return {};
}
if (isRtl) {
return { right: hcoord.start, width: hcoord.size };
}
else {
return { left: hcoord.start, width: hcoord.size };
}
}
function horizontalCoordToCss(start, isRtl) {
if (isRtl) {
return { right: start };
}
else {
return { left: start };
}
}
function createVerticalStyle(props) {
if (props) {
return {
top: props.start,
height: props.size,
};
}
}
function createHorizontalStyle(// TODO: DRY up?
props, isRtl) {
if (props) {
return {
[isRtl ? 'right' : 'left']: props.start,
width: props.size,
};
}
}
// Timeline-specific
// -------------------------------------------------------------------------------------------------
const MIN_SLOT_WIDTH = 30; // for real
/*
TODO: DRY with computeSlatHeight?
*/
function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
if (labelInnerWidth == null || viewportWidth == null) {
return [undefined, undefined, false];
}
slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
const slatTryWidth = viewportWidth / slatCnt;
let slatLiquid;
let slatWidth;
if (slatTryWidth >= slatMinWidth) {
slatLiquid = true;
slatWidth = slatTryWidth;
}
else {
slatLiquid = false;
slatWidth = Math.max(slatMinWidth, slatTryWidth);
}
return [slatWidth * slatCnt, slatWidth, slatLiquid];
}
function timeToCoord(// pixels
time, dateEnv, dateProfile, tDateProfile, slowWidth) {
let date = dateEnv.add(dateProfile.activeRange.start, time);
if (!tDateProfile.isTimeScale) {
date = startOfDay(date);
}
return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
}
function dateToCoord(// pixels
date, dateEnv, tDateProfile, slotWidth) {
let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
return slotCoverage * slotWidth;
}
/*
returned value is between 0 and the number of snaps
*/
function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
if (snapDiff < 0) {
return 0;
}
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
return tDateProfile.snapCnt;
}
let snapDiffInt = Math.floor(snapDiff);
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
if (isInt(snapCoverage)) { // not an in-between value
snapCoverage += snapDiff - snapDiffInt; // add the remainder
}
else {
// a fractional value, meaning the date is not visible
// always round up in this case. works for start AND end dates in a range.
snapCoverage = Math.ceil(snapCoverage);
}
return snapCoverage;
}
function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const res = {};
for (const seg of segs) {
res[getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
}
return res;
}
function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
let size = endCoord - startCoord;
if (segMinWidth) {
size = Math.max(size, segMinWidth);
}
return { start: startCoord, size };
}
function computeFgSegPlacements(// mostly horizontals
segs, segHorizontals, // TODO: use Map
segHeights, // keyed by instanceId
hiddenGroupHeights, strictOrder, maxDepth) {
const segRanges = [];
// isn't it true that there will either be ALL hcoords or NONE? can optimize
for (const seg of segs) {
const hcoords = segHorizontals[getEventKey(seg)];
if (hcoords) {
segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
}
}
const hierarchy = new SegHierarchy(segRanges, (seg) => segHeights.get(getEventKey(seg)), strictOrder, undefined, // maxCoord
maxDepth);
const segTops = new Map();
hierarchy.traverseSegs((seg, segTop) => {
segTops.set(getEventKey(seg), segTop);
});
const { hiddenSegs } = hierarchy;
let totalHeight = 0;
for (const segRange of segRanges) {
const segKey = getEventKey(segRange);
const segHeight = segHeights.get(segKey);
const segTop = segTops.get(segKey);
if (segHeight != null) {
if (segTop != null) {
totalHeight = Math.max(totalHeight, segTop + segHeight);
}
}
}
const hiddenGroups = groupIntersectingSegs(hiddenSegs);
const hiddenGroupTops = new Map();
// HACK for hiddenGroup findInsertion() call
hierarchy.strictOrder = true;
for (const hiddenGroup of hiddenGroups) {
const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
hiddenGroupTops.set(hiddenGroup.key, top);
totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
}
return [
segTops,
hiddenGroups,
hiddenGroupTops,
totalHeight,
];
}
class TimelineLaneBg extends BaseComponent {
render() {
let { props } = this;
let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
return (createElement(Fragment, null,
this.renderSegs(props.businessHourSegs || [], 'non-business'),
this.renderSegs(props.bgEventSegs || [], 'bg-event'),
this.renderSegs(highlightSeg, 'highlight')));
}
renderSegs(segs, fillType) {
let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
let { dateEnv, isRtl } = this.context;
return (createElement(Fragment, null, segs.map((seg) => {
let hStyle; // TODO
if (slotWidth != null) {
let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
hStyle = horizontalsToCss(segHorizontal, isRtl);
}
return (createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
createElement(BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (renderFill(fillType))));
})));
}
}
class TimelineLaneSlicer extends Slicer {
sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
let segs = [];
// protect against when the span is entirely in an invalid date region
if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
< computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
// intersect the footprint's range with the grid's range
let slicedRange = intersectRanges(normalRange, tDateProfile.normalizedRange);
if (slicedRange) {
segs.push({
startDate: slicedRange.start,
endDate: slicedRange.end,
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
&& isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
&& isValidDate(addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
});
}
}
return segs;
}
}
const DEFAULT_TIME_FORMAT = createFormatter({
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'narrow',
});
class TimelineEvent extends BaseComponent {
render() {
let { props, context } = this;
let { options } = context;
return (createElement(StandardEvent, Object.assign({}, props, { className: joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
&& 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
}
}
class TimelineLaneMoreLink extends BaseComponent {
render() {
let { props } = this;
let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
let dateSpanProps = resourceId ? { resourceId } : {};
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) => {
let { eventRange } = seg;
let instanceId = eventRange.instance.instanceId;
return (createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
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)))));
}))) }, (InnerContent) => (createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
}
}
/*
TODO: make DRY with other Event Harnesses
*/
class TimelineEventHarness extends Component {
constructor() {
super(...arguments);
// ref
this.rootElRef = createRef();
}
render() {
const { props } = this;
return (createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
}
componentDidMount() {
const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
this.disconnectHeight = watchHeight(rootEl, (height) => {
setRef(this.props.heightRef, height);
});
}
componentWillUnmount() {
this.disconnectHeight();
setRef(this.props.heightRef, null);
}
}
/*
TODO: split TimelineLaneBg and TimelineLaneFg?
*/
class TimelineLane extends BaseComponent {
constructor() {
super(...arguments);
// memo
this.sortEventSegs = memoize(sortEventSegs);
// refs
this.segHeightRefMap = new RefMap(() => {
afterSize(this.handleSegHeights);
});
this.moreLinkHeightRefMap = new RefMap(() => {
afterSize(this.handleMoreLinkHeights);
});
// internal
this.slicer = new TimelineLaneSlicer();
this.handleMoreLinkHeights = () => {
this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
};
this.handleSegHeights = () => {
this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
};
}
/*
TODO: lots of memoization needed here!
*/
render() {
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
let { options } = context;
let { dateProfile, tDateProfile } = props;
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...
dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
(slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
[];
let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
let fgSegHorizontals = props.slotWidth != null
? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {};
let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
let forcedInvisibleMap = // TODO: more convenient/DRY
(slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
(slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
{};
return (createElement(Fragment, null,
createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
// content
bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
// dimensions
slotWidth: props.slotWidth }),
createElement("div", { className: joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
? 'fc-timeline-events-overlap-disabled'
: 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
false, // isResizing
false),
this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
: {}, fgSegTops, {}, // forcedInvisibleMap
[], // hiddenGroups
new Map(), // hiddenGroupTops
Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
}
renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
let isMirror = isDragging || isResizing || isDateSelecting;
return (createElement(Fragment, null,
segs.map((seg) => {
const { eventRange } = seg;
const { instanceId } = eventRange.instance;
const segTop = segTops.get(instanceId);
const segHorizontal = segHorizontals[instanceId];
const isVisible = isMirror ||
(segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
return (createElement(TimelineEventHarness, { key: instanceId, style: Object.assign({ visibility: isVisible ? '' : 'hidden', top: segTop || 0 }, horizontalsToCss(segHorizontal, context.isRtl)), heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection /* TODO: bad for mirror? */ }, getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
}),
hiddenGroups.map((hiddenGroup) => (createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
start: hiddenGroup.start,
size: hiddenGroup.end - hiddenGroup.start
}, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
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 }))))));
}
}
class TimelineHeaderCell extends BaseComponent {
constructor() {
super(...arguments);
// memo
this.refineRenderProps = memoizeObjArg(refineRenderProps);
// ref
this.innerElRef = createRef();
}
render() {
let { props, context } = this;
let { dateEnv, options } = context;
let { cell, dateProfile, tDateProfile } = props;
// the cell.rowUnit is f'd
// giving 'month' for a 3-day view
// workaround: to infer day, do NOT time
let dateMeta = getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
let renderProps = this.refineRenderProps({
level: props.rowLevel,
dateMarker: cell.date,
text: cell.text,
dateEnv: context.dateEnv,
viewApi: context.viewApi,
});
let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
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
'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
// TODO: so slot classnames for week/month/bigger. see note above about rowUnit
cell.rowUnit === 'time' ?
getSlotClassName(dateMeta) :
getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
omitTime: !tDateProfile.isTimeScale,
omitTimeZoneOffset: true,
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
width: props.slotWidth != null
? props.slotWidth * cell.colspan
: undefined,
}, 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
// not tabbable because parent is aria-hidden
? buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
: {} // don't bother with aria-hidden because parent already hidden
, className: joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
}
componentDidMount() {
const { props } = this;
const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
this.detachSize = watchSize(innerEl, (width, height) => {
setRef(props.innerWidthRef, width);
setRef(props.innerHeightRef, height);
// HACK for sticky-centering
innerEl.style.left = innerEl.style.right =
(props.isCentered && props.isSticky)
? `calc(50% - ${width / 2}px)`
: '';
});
}
componentWillUnmount() {
const { props } = this;
this.detachSize();
setRef(props.innerWidthRef, null);
setRef(props.innerHeightRef, null);
}
}
// Utils
// -------------------------------------------------------------------------------------------------
function renderInnerContent(renderProps) {
return renderProps.text;
}
function refineRenderProps(input) {
return {
level: input.level,
date: input.dateEnv.toDate(input.dateMarker),
view: input.viewApi,
text: input.text,
};
}
class TimelineHeaderRow extends BaseComponent {
constructor() {
super(...arguments);
// refs
this.innerWidthRefMap = new RefMap(() => {
afterSize(this.handleInnerWidths);
});
this.innerHeightRefMap = new RefMap(() => {
afterSize(this.handleInnerHeights);
});
this.handleInnerWidths = () => {
const innerWidthMap = this.innerWidthRefMap.current;
let max = 0;
for (const innerWidth of innerWidthMap.values()) {
max = Math.max(max, innerWidth);
}
// TODO: ensure not equal?
setRef(this.props.innerWidthRef, max);
};
this.handleInnerHeights = () => {
const innerHeightMap = this.innerHeightRefMap.current;
let max = 0;
for (const innerHeight of innerHeightMap.values()) {
max = Math.max(max, innerHeight);
}
// TODO: ensure not equal?
setRef(this.props.innerHeighRef, max);
};
}
render() {
const { props, innerWidthRefMap, innerHeightRefMap } = this;
const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
const isSticky = !props.isLastRow;
return (createElement("div", { className: joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
!props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
// TODO: make this part of the cell obj?
// TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
// TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
// TODO: use rowUnit to key the Row itself?
const key = cell.rowUnit + ':' + cell.date.toISOString();
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),
// refs
innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
// dimensions
slotWidth: props.slotWidth }));
})));
}
componentWillUnmount() {
setRef(this.props.innerWidthRef, null);
setRef(this.props.innerHeighRef, null);
}
}
class TimelineNowIndicatorLine extends BaseComponent {
render() {
const { props, context } = this;
return (createElement("div", { className: "fc-timeline-now-indicator-container" },
createElement(NowIndicatorContainer // TODO: make separate component?
, { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
: {}, isAxis: false, date: props.nowDate })));
}
}
class TimelineNowIndicatorArrow extends BaseComponent {
render() {
const { props, context } = this;
return (createElement("div", { className: "fc-timeline-now-indicator-container" },
createElement(NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
: {}, isAxis: true, date: props.nowDate })));
}
}
class TimelineView extends DateComponent {
constructor() {
super(...arguments);
// memoized
this.buildTimelineDateProfile = memoize(buildTimelineDateProfile);
this.computeSlotWidth = memoize(computeSlotWidth);
// refs
this.headerScrollerRef = createRef();
this.bodyScrollerRef = createRef();
this.footerScrollerRef = createRef();
this.headerRowInnerWidthMap = new RefMap(() => {
afterSize(this.handleSlotInnerWidths);
});
this.scrollTime = null;
// Sizing
// -----------------------------------------------------------------------------------------------
this.handleBodySlotInnerWidth = (innerWidth) => {
this.bodySlotInnerWidth = innerWidth;
afterSize(this.handleSlotInnerWidths);
};
this.handleSlotInnerWidths = () => {
const { state } = this;
const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
if (state.slotInnerWidth !== slotInnerWidth) {
this.setState({ slotInnerWidth });
}
};
this.handleClientWidth = (clientWidth) => {
this.setState({
clientWidth,
});
};
this.handleEndScrollbarWidth = (endScrollbarWidth) => {
this.setState({
endScrollbarWidth
});
};
this.handleTimeScrollRequest = (scrollTime) => {
this.scrollTime = scrollTime;
this.applyTimeScroll();
};
this.handleTimeScrollEnd = ({ isUser }) => {
if (isUser) {
this.scrollTime = null;
}
};
// Hit System
// -----------------------------------------------------------------------------------------------
this.handeBodyEl = (el) => {
this.bodyEl = el;
if (el) {
this.context.registerInteractiveComponent(this, { el });
}
else {
this.context.unregisterInteractiveComponent(this);
}
};
}
render() {
const { props, state, context } = this;
const { options } = context;
/* date */
const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
const { cellRows } = tDateProfile;
const timerUnit = greatestDurationDenominator(tDateProfile.slotDuration).unit;
/* table settings */
const verticalScrolling = !props.forPrint && !getIsHeightAuto(options);
const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
const stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(options);
/* table positions */
const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
state.clientWidth);
this.slotWidth = slotWidth;
return (createElement(NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
const enableNowIndicator = // TODO: DRY
options.nowIndicator &&
slotWidth != null &&
rangeContainsMarker(props.dateProfile.currentRange, nowDate);
return (createElement(ViewContainer, { viewSpec: context.viewSpec, className: joinClassNames('fc-timeline fc-border',
// HACK for Safari print-mode, where fc-scroller-no-bars won't take effect for
// the below Scrollers if they have liquid flex height
!props.forPrint && 'fc-flex-col') },
createElement(Scroller, { horizontal: true, hideScrollbars: true, className: joinClassNames('fc-timeline-header fc-flex-row fc-border-b', stickyHeaderDates && 'fc-table-header-sticky'), ref: this.headerScrollerRef },
createElement("div", {
// TODO: DRY
className: joinClassNames('fc-rel', // origin for now-indicator
canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
cellRows.map((cells, rowLevel) => {
const isLast = rowLevel === cellRows.length - 1;
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) }));
}),
enableNowIndicator && (createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
Boolean(state.endScrollbarWidth) && (createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
createElement(Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: props.forPrint, className: joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth },
createElement("div", { "aria-label": options.eventsHint, className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
// ref
innerWidthRef: this.handleBodySlotInnerWidth,
// dimensions
slotWidth: slotWidth }),
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 }),
enableNowIndicator && (createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
stickyFooterScrollbar && (createElement(Scroller, { ref: this.footerScrollerRef, horizontal: true },
createElement("div", { style: { width: canvasWidth } })))));
}));
}
// Lifecycle
// -----------------------------------------------------------------------------------------------
componentDidMount() {
this.syncedScroller = new ScrollerSyncer(true); // horizontal=true
this.updateSyncedScroller();
this.resetScroll();
this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
}
componentDidUpdate(prevProps) {
this.updateSyncedScroller();
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
this.resetScroll();
}
else {
// TODO: inefficient to update so often
this.applyTimeScroll();
}
}
componentWillUnmount() {
this.syncedScroller.destroy();
this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
}
// Scrolling
// -----------------------------------------------------------------------------------------------
updateSyncedScroller() {
this.syncedScroller.handleChildren([
this.headerScrollerRef.current,
this.bodyScrollerRef.current,
this.footerScrollerRef.current
]);
}
resetScroll() {
this.handleTimeScrollRequest(this.context.options.scrollTime);
}
applyTimeScroll() {
const { props, context, tDateProfile, scrollTime, slotWidth } = this;
if (scrollTime != null && slotWidth != null) {
let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
if (x) {
x += 1; // overcome border. TODO: DRY this up
}
this.syncedScroller.scrollTo({ x });
}
}
queryHit(positionLeft, positionTop, elWidth, elHeight) {
const { props, context, tDateProfile, slotWidth } = this;
const { dateEnv } = context;
if (slotWidth) {
const x = context.isRtl ? elWidth - positionLeft : positionLeft;
const slatIndex = Math.floor(x / slotWidth);
const slatX = slatIndex * slotWidth;
const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
// TODO: generalize this coord stuff to TimeGrid?
let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
let endCoord = startCoord + snapWidth;
let left, right;
if (context.isRtl) {
left = elWidth - endCoord;
right = elWidth - startCoord;
}
else {
left = startCoord;
right = endCoord;
}
return {
dateProfile: props.dateProfile,
dateSpan: {
range: { start: startDate, end: endDate },
allDay: !tDateProfile.isTimeScale,
},
rect: {
left,
right,
top: 0,
bottom: elHeight,
},
// HACK. TODO: This is expensive to do every hit-query
dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
layer: 0,
};
}
return null;
}
}
var css_248z = ".fc-timeline-slots{z-index:1}.fc-timeline-events{position:relative;z-index:2}.fc-timeline-slot-minor{border-style:dotted}.fc-timeline-events-overlap-enabled{padding-bottom:10px}.fc-timeline-event{border-radius:0;font-size:var(--fc-small-font-size);margin-bottom:1px}.fc-direction-ltr .fc-timeline-event.fc-event-end{margin-right:1px}.fc-direction-rtl .fc-timeline-event.fc-event-end{margin-left:1px}.fc-timeline-event .fc-event-inner{align-items:center;display:flex;flex-direction:row;padding:2px 1px}.fc-timeline-event-spacious .fc-event-inner{padding-bottom:5px;padding-top:5px}.fc-timeline-event .fc-event-time{font-weight:700}.fc-timeline-event .fc-event-time,.fc-timeline-event .fc-event-title{padding:0 2px}.fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-color:transparent #000;border-style:solid;border-width:5px;content:\"\";flex-grow:0;flex-shrink:0;height:0;margin:0 1px;opacity:.5;width:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before,.fc-direction-rtl .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after{border-left:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-direction-rtl .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-right:0}.fc-timeline-more-link{align-items:flex-start;background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;display:flex;flex-direction:column;font-size:var(--fc-small-font-size);padding:1px}.fc-direction-ltr .fc-timeline-more-link{margin-right:1px}.fc-direction-rtl .fc-timeline-more-link{margin-left:1px}.fc-timeline-more-link-inner{padding:2px}.fc-timeline-now-indicator-container{bottom:0;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;top:0;z-index:4}.fc-timeline-now-indicator-arrow{border-bottom-style:solid;border-bottom-width:0;border-color:var(--fc-now-indicator-color);border-left:5px solid transparent;border-right:5px solid transparent;border-top-style:solid;border-top-width:6px;height:0;margin:0 -5px;position:absolute;top:0;width:0}.fc-timeline-now-indicator-line{border-left:1px solid var(--fc-now-indicator-color);bottom:0;position:absolute;top:0}";
injectStyles(css_248z);
export { TimelineHeaderRow, TimelineLane, TimelineLaneBg, TimelineLaneSlicer, TimelineNowIndicatorArrow, TimelineNowIndicatorLine, TimelineSlats, TimelineView, buildTimelineDateProfile, computeSlotWidth, createHorizontalStyle, createVerticalStyle, timeToCoord };