UNPKG

68.4 kBJavaScriptView Raw
1/*!
2FullCalendar Time Grid Plugin v6.1.1
3Docs & License: https://fullcalendar.io/docs/timegrid-view
4(c) 2023 Adam Shaw
5*/
6FullCalendar.TimeGrid = (function (exports, core, internal$1, preact, internal$2) {
7 'use strict';
8
9 class AllDaySplitter extends internal$1.Splitter {
10 getKeyInfo() {
11 return {
12 allDay: {},
13 timed: {},
14 };
15 }
16 getKeysForDateSpan(dateSpan) {
17 if (dateSpan.allDay) {
18 return ['allDay'];
19 }
20 return ['timed'];
21 }
22 getKeysForEventDef(eventDef) {
23 if (!eventDef.allDay) {
24 return ['timed'];
25 }
26 if (internal$1.hasBgRendering(eventDef)) {
27 return ['timed', 'allDay'];
28 }
29 return ['allDay'];
30 }
31 }
32
33 const DEFAULT_SLAT_LABEL_FORMAT = internal$1.createFormatter({
34 hour: 'numeric',
35 minute: '2-digit',
36 omitZeroMinute: true,
37 meridiem: 'short',
38 });
39 function TimeColsAxisCell(props) {
40 let classNames = [
41 'fc-timegrid-slot',
42 'fc-timegrid-slot-label',
43 props.isLabeled ? 'fc-scrollgrid-shrink' : 'fc-timegrid-slot-minor',
44 ];
45 return (preact.createElement(internal$1.ViewContextType.Consumer, null, (context) => {
46 if (!props.isLabeled) {
47 return (preact.createElement("td", { className: classNames.join(' '), "data-time": props.isoTimeStr }));
48 }
49 let { dateEnv, options, viewApi } = context;
50 let labelFormat = // TODO: fully pre-parse
51 options.slotLabelFormat == null ? DEFAULT_SLAT_LABEL_FORMAT :
52 Array.isArray(options.slotLabelFormat) ? internal$1.createFormatter(options.slotLabelFormat[0]) :
53 internal$1.createFormatter(options.slotLabelFormat);
54 let renderProps = {
55 level: 0,
56 time: props.time,
57 date: dateEnv.toDate(props.date),
58 view: viewApi,
59 text: dateEnv.format(props.date, labelFormat),
60 };
61 return (preact.createElement(internal$1.ContentContainer, { elTag: "td", elClasses: classNames, elAttrs: {
62 'data-time': props.isoTimeStr,
63 }, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact.createElement("div", { className: "fc-timegrid-slot-label-frame fc-scrollgrid-shrink-frame" },
64 preact.createElement(InnerContent, { elTag: "div", elClasses: [
65 'fc-timegrid-slot-label-cushion',
66 'fc-scrollgrid-shrink-cushion',
67 ] })))));
68 }));
69 }
70 function renderInnerContent(props) {
71 return props.text;
72 }
73
74 class TimeBodyAxis extends internal$1.BaseComponent {
75 render() {
76 return this.props.slatMetas.map((slatMeta) => (preact.createElement("tr", { key: slatMeta.key },
77 preact.createElement(TimeColsAxisCell, Object.assign({}, slatMeta)))));
78 }
79 }
80
81 const DEFAULT_WEEK_NUM_FORMAT = internal$1.createFormatter({ week: 'short' });
82 const AUTO_ALL_DAY_MAX_EVENT_ROWS = 5;
83 class TimeColsView extends internal$1.DateComponent {
84 constructor() {
85 super(...arguments);
86 this.allDaySplitter = new AllDaySplitter(); // for use by subclasses
87 this.headerElRef = preact.createRef();
88 this.rootElRef = preact.createRef();
89 this.scrollerElRef = preact.createRef();
90 this.state = {
91 slatCoords: null,
92 };
93 this.handleScrollTopRequest = (scrollTop) => {
94 let scrollerEl = this.scrollerElRef.current;
95 if (scrollerEl) { // TODO: not sure how this could ever be null. weirdness with the reducer
96 scrollerEl.scrollTop = scrollTop;
97 }
98 };
99 /* Header Render Methods
100 ------------------------------------------------------------------------------------------------------------------*/
101 this.renderHeadAxis = (rowKey, frameHeight = '') => {
102 let { options } = this.context;
103 let { dateProfile } = this.props;
104 let range = dateProfile.renderRange;
105 let dayCnt = internal$1.diffDays(range.start, range.end);
106 // only do in day views (to avoid doing in week views that dont need it)
107 let navLinkAttrs = (dayCnt === 1)
108 ? internal$1.buildNavLinkAttrs(this.context, range.start, 'week')
109 : {};
110 if (options.weekNumbers && rowKey === 'day') {
111 return (preact.createElement(internal$1.WeekNumberContainer, { elTag: "th", elClasses: [
112 'fc-timegrid-axis',
113 'fc-scrollgrid-shrink',
114 ], elAttrs: {
115 'aria-hidden': true,
116 }, date: range.start, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }, (InnerContent) => (preact.createElement("div", { className: [
117 'fc-timegrid-axis-frame',
118 'fc-scrollgrid-shrink-frame',
119 'fc-timegrid-axis-frame-liquid',
120 ].join(' '), style: { height: frameHeight } },
121 preact.createElement(InnerContent, { elTag: "a", elClasses: [
122 'fc-timegrid-axis-cushion',
123 'fc-scrollgrid-shrink-cushion',
124 'fc-scrollgrid-sync-inner',
125 ], elAttrs: navLinkAttrs })))));
126 }
127 return (preact.createElement("th", { "aria-hidden": true, className: "fc-timegrid-axis" },
128 preact.createElement("div", { className: "fc-timegrid-axis-frame", style: { height: frameHeight } })));
129 };
130 /* Table Component Render Methods
131 ------------------------------------------------------------------------------------------------------------------*/
132 // only a one-way height sync. we don't send the axis inner-content height to the DayGrid,
133 // but DayGrid still needs to have classNames on inner elements in order to measure.
134 this.renderTableRowAxis = (rowHeight) => {
135 let { options, viewApi } = this.context;
136 let renderProps = {
137 text: options.allDayText,
138 view: viewApi,
139 };
140 return (
141 // TODO: make reusable hook. used in list view too
142 preact.createElement(internal$1.ContentContainer, { elTag: "td", elClasses: [
143 'fc-timegrid-axis',
144 'fc-scrollgrid-shrink',
145 ], elAttrs: {
146 'aria-hidden': true,
147 }, renderProps: renderProps, generatorName: "allDayContent", customGenerator: options.allDayContent, defaultGenerator: renderAllDayInner, classNameGenerator: options.allDayClassNames, didMount: options.allDayDidMount, willUnmount: options.allDayWillUnmount }, (InnerContent) => (preact.createElement("div", { className: [
148 'fc-timegrid-axis-frame',
149 'fc-scrollgrid-shrink-frame',
150 rowHeight == null ? ' fc-timegrid-axis-frame-liquid' : '',
151 ].join(' '), style: { height: rowHeight } },
152 preact.createElement(InnerContent, { elTag: "span", elClasses: [
153 'fc-timegrid-axis-cushion',
154 'fc-scrollgrid-shrink-cushion',
155 'fc-scrollgrid-sync-inner',
156 ] })))));
157 };
158 this.handleSlatCoords = (slatCoords) => {
159 this.setState({ slatCoords });
160 };
161 }
162 // rendering
163 // ----------------------------------------------------------------------------------------------------
164 renderSimpleLayout(headerRowContent, allDayContent, timeContent) {
165 let { context, props } = this;
166 let sections = [];
167 let stickyHeaderDates = internal$1.getStickyHeaderDates(context.options);
168 if (headerRowContent) {
169 sections.push({
170 type: 'header',
171 key: 'header',
172 isSticky: stickyHeaderDates,
173 chunk: {
174 elRef: this.headerElRef,
175 tableClassName: 'fc-col-header',
176 rowContent: headerRowContent,
177 },
178 });
179 }
180 if (allDayContent) {
181 sections.push({
182 type: 'body',
183 key: 'all-day',
184 chunk: { content: allDayContent },
185 });
186 sections.push({
187 type: 'body',
188 key: 'all-day-divider',
189 outerContent: ( // TODO: rename to cellContent so don't need to define <tr>?
190 preact.createElement("tr", { role: "presentation", className: "fc-scrollgrid-section" },
191 preact.createElement("td", { className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))),
192 });
193 }
194 sections.push({
195 type: 'body',
196 key: 'body',
197 liquid: true,
198 expandRows: Boolean(context.options.expandRows),
199 chunk: {
200 scrollerElRef: this.scrollerElRef,
201 content: timeContent,
202 },
203 });
204 return (preact.createElement(internal$1.ViewContainer, { elRef: this.rootElRef, elClasses: ['fc-timegrid'], viewSpec: context.viewSpec },
205 preact.createElement(internal$1.SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, collapsibleWidth: props.forPrint, cols: [{ width: 'shrink' }], sections: sections })));
206 }
207 renderHScrollLayout(headerRowContent, allDayContent, timeContent, colCnt, dayMinWidth, slatMetas, slatCoords) {
208 let ScrollGrid = this.context.pluginHooks.scrollGridImpl;
209 if (!ScrollGrid) {
210 throw new Error('No ScrollGrid implementation');
211 }
212 let { context, props } = this;
213 let stickyHeaderDates = !props.forPrint && internal$1.getStickyHeaderDates(context.options);
214 let stickyFooterScrollbar = !props.forPrint && internal$1.getStickyFooterScrollbar(context.options);
215 let sections = [];
216 if (headerRowContent) {
217 sections.push({
218 type: 'header',
219 key: 'header',
220 isSticky: stickyHeaderDates,
221 syncRowHeights: true,
222 chunks: [
223 {
224 key: 'axis',
225 rowContent: (arg) => (preact.createElement("tr", { role: "presentation" }, this.renderHeadAxis('day', arg.rowSyncHeights[0]))),
226 },
227 {
228 key: 'cols',
229 elRef: this.headerElRef,
230 tableClassName: 'fc-col-header',
231 rowContent: headerRowContent,
232 },
233 ],
234 });
235 }
236 if (allDayContent) {
237 sections.push({
238 type: 'body',
239 key: 'all-day',
240 syncRowHeights: true,
241 chunks: [
242 {
243 key: 'axis',
244 rowContent: (contentArg) => (preact.createElement("tr", { role: "presentation" }, this.renderTableRowAxis(contentArg.rowSyncHeights[0]))),
245 },
246 {
247 key: 'cols',
248 content: allDayContent,
249 },
250 ],
251 });
252 sections.push({
253 key: 'all-day-divider',
254 type: 'body',
255 outerContent: ( // TODO: rename to cellContent so don't need to define <tr>?
256 preact.createElement("tr", { role: "presentation", className: "fc-scrollgrid-section" },
257 preact.createElement("td", { colSpan: 2, className: 'fc-timegrid-divider ' + context.theme.getClass('tableCellShaded') }))),
258 });
259 }
260 let isNowIndicator = context.options.nowIndicator;
261 sections.push({
262 type: 'body',
263 key: 'body',
264 liquid: true,
265 expandRows: Boolean(context.options.expandRows),
266 chunks: [
267 {
268 key: 'axis',
269 content: (arg) => (
270 // TODO: make this now-indicator arrow more DRY with TimeColsContent
271 preact.createElement("div", { className: "fc-timegrid-axis-chunk" },
272 preact.createElement("table", { "aria-hidden": true, style: { height: arg.expandRows ? arg.clientHeight : '' } },
273 arg.tableColGroupNode,
274 preact.createElement("tbody", null,
275 preact.createElement(TimeBodyAxis, { slatMetas: slatMetas }))),
276 preact.createElement("div", { className: "fc-timegrid-now-indicator-container" },
277 preact.createElement(internal$1.NowTimer, { unit: isNowIndicator ? 'minute' : 'day' /* hacky */ }, (nowDate) => {
278 let nowIndicatorTop = isNowIndicator &&
279 slatCoords &&
280 slatCoords.safeComputeTop(nowDate); // might return void
281 if (typeof nowIndicatorTop === 'number') {
282 return (preact.createElement(internal$1.NowIndicatorContainer, { elClasses: ['fc-timegrid-now-indicator-arrow'], elStyle: { top: nowIndicatorTop }, isAxis: true, date: nowDate }));
283 }
284 return null;
285 })))),
286 },
287 {
288 key: 'cols',
289 scrollerElRef: this.scrollerElRef,
290 content: timeContent,
291 },
292 ],
293 });
294 if (stickyFooterScrollbar) {
295 sections.push({
296 key: 'footer',
297 type: 'footer',
298 isSticky: true,
299 chunks: [
300 {
301 key: 'axis',
302 content: internal$1.renderScrollShim,
303 },
304 {
305 key: 'cols',
306 content: internal$1.renderScrollShim,
307 },
308 ],
309 });
310 }
311 return (preact.createElement(internal$1.ViewContainer, { elRef: this.rootElRef, elClasses: ['fc-timegrid'], viewSpec: context.viewSpec },
312 preact.createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, collapsibleWidth: false, colGroups: [
313 { width: 'shrink', cols: [{ width: 'shrink' }] },
314 { cols: [{ span: colCnt, minWidth: dayMinWidth }] },
315 ], sections: sections })));
316 }
317 /* Dimensions
318 ------------------------------------------------------------------------------------------------------------------*/
319 getAllDayMaxEventProps() {
320 let { dayMaxEvents, dayMaxEventRows } = this.context.options;
321 if (dayMaxEvents === true || dayMaxEventRows === true) { // is auto?
322 dayMaxEvents = undefined;
323 dayMaxEventRows = AUTO_ALL_DAY_MAX_EVENT_ROWS; // make sure "auto" goes to a real number
324 }
325 return { dayMaxEvents, dayMaxEventRows };
326 }
327 }
328 function renderAllDayInner(renderProps) {
329 return renderProps.text;
330 }
331
332 class TimeColsSlatsCoords {
333 constructor(positions, dateProfile, slotDuration) {
334 this.positions = positions;
335 this.dateProfile = dateProfile;
336 this.slotDuration = slotDuration;
337 }
338 safeComputeTop(date) {
339 let { dateProfile } = this;
340 if (internal$1.rangeContainsMarker(dateProfile.currentRange, date)) {
341 let startOfDayDate = internal$1.startOfDay(date);
342 let timeMs = date.valueOf() - startOfDayDate.valueOf();
343 if (timeMs >= internal$1.asRoughMs(dateProfile.slotMinTime) &&
344 timeMs < internal$1.asRoughMs(dateProfile.slotMaxTime)) {
345 return this.computeTimeTop(internal$1.createDuration(timeMs));
346 }
347 }
348 return null;
349 }
350 // Computes the top coordinate, relative to the bounds of the grid, of the given date.
351 // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
352 computeDateTop(when, startOfDayDate) {
353 if (!startOfDayDate) {
354 startOfDayDate = internal$1.startOfDay(when);
355 }
356 return this.computeTimeTop(internal$1.createDuration(when.valueOf() - startOfDayDate.valueOf()));
357 }
358 // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
359 // This is a makeshify way to compute the time-top. Assumes all slatMetas dates are uniform.
360 // Eventually allow computation with arbirary slat dates.
361 computeTimeTop(duration) {
362 let { positions, dateProfile } = this;
363 let len = positions.els.length;
364 // floating-point value of # of slots covered
365 let slatCoverage = (duration.milliseconds - internal$1.asRoughMs(dateProfile.slotMinTime)) / internal$1.asRoughMs(this.slotDuration);
366 let slatIndex;
367 let slatRemainder;
368 // compute a floating-point number for how many slats should be progressed through.
369 // from 0 to number of slats (inclusive)
370 // constrained because slotMinTime/slotMaxTime might be customized.
371 slatCoverage = Math.max(0, slatCoverage);
372 slatCoverage = Math.min(len, slatCoverage);
373 // an integer index of the furthest whole slat
374 // from 0 to number slats (*exclusive*, so len-1)
375 slatIndex = Math.floor(slatCoverage);
376 slatIndex = Math.min(slatIndex, len - 1);
377 // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition.
378 // could be 1.0 if slatCoverage is covering *all* the slots
379 slatRemainder = slatCoverage - slatIndex;
380 return positions.tops[slatIndex] +
381 positions.getHeight(slatIndex) * slatRemainder;
382 }
383 }
384
385 class TimeColsSlatsBody extends internal$1.BaseComponent {
386 render() {
387 let { props, context } = this;
388 let { options } = context;
389 let { slatElRefs } = props;
390 return (preact.createElement("tbody", null, props.slatMetas.map((slatMeta, i) => {
391 let renderProps = {
392 time: slatMeta.time,
393 date: context.dateEnv.toDate(slatMeta.date),
394 view: context.viewApi,
395 };
396 return (preact.createElement("tr", { key: slatMeta.key, ref: slatElRefs.createRef(slatMeta.key) },
397 props.axis && (preact.createElement(TimeColsAxisCell, Object.assign({}, slatMeta))),
398 preact.createElement(internal$1.ContentContainer, { elTag: "td", elClasses: [
399 'fc-timegrid-slot',
400 'fc-timegrid-slot-lane',
401 !slatMeta.isLabeled && 'fc-timegrid-slot-minor',
402 ], elAttrs: {
403 'data-time': slatMeta.isoTimeStr,
404 }, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount })));
405 })));
406 }
407 }
408
409 /*
410 for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL.
411 */
412 class TimeColsSlats extends internal$1.BaseComponent {
413 constructor() {
414 super(...arguments);
415 this.rootElRef = preact.createRef();
416 this.slatElRefs = new internal$1.RefMap();
417 }
418 render() {
419 let { props, context } = this;
420 return (preact.createElement("div", { ref: this.rootElRef, className: "fc-timegrid-slots" },
421 preact.createElement("table", { "aria-hidden": true, className: context.theme.getClass('table'), style: {
422 minWidth: props.tableMinWidth,
423 width: props.clientWidth,
424 height: props.minHeight,
425 } },
426 props.tableColGroupNode /* relies on there only being a single <col> for the axis */,
427 preact.createElement(TimeColsSlatsBody, { slatElRefs: this.slatElRefs, axis: props.axis, slatMetas: props.slatMetas }))));
428 }
429 componentDidMount() {
430 this.updateSizing();
431 }
432 componentDidUpdate() {
433 this.updateSizing();
434 }
435 componentWillUnmount() {
436 if (this.props.onCoords) {
437 this.props.onCoords(null);
438 }
439 }
440 updateSizing() {
441 let { context, props } = this;
442 if (props.onCoords &&
443 props.clientWidth !== null // means sizing has stabilized
444 ) {
445 let rootEl = this.rootElRef.current;
446 if (rootEl.offsetHeight) { // not hidden by css
447 props.onCoords(new TimeColsSlatsCoords(new internal$1.PositionCache(this.rootElRef.current, collectSlatEls(this.slatElRefs.currentMap, props.slatMetas), false, true), this.props.dateProfile, context.options.slotDuration));
448 }
449 }
450 }
451 }
452 function collectSlatEls(elMap, slatMetas) {
453 return slatMetas.map((slatMeta) => elMap[slatMeta.key]);
454 }
455
456 function splitSegsByCol(segs, colCnt) {
457 let segsByCol = [];
458 let i;
459 for (i = 0; i < colCnt; i += 1) {
460 segsByCol.push([]);
461 }
462 if (segs) {
463 for (i = 0; i < segs.length; i += 1) {
464 segsByCol[segs[i].col].push(segs[i]);
465 }
466 }
467 return segsByCol;
468 }
469 function splitInteractionByCol(ui, colCnt) {
470 let byRow = [];
471 if (!ui) {
472 for (let i = 0; i < colCnt; i += 1) {
473 byRow[i] = null;
474 }
475 }
476 else {
477 for (let i = 0; i < colCnt; i += 1) {
478 byRow[i] = {
479 affectedInstances: ui.affectedInstances,
480 isEvent: ui.isEvent,
481 segs: [],
482 };
483 }
484 for (let seg of ui.segs) {
485 byRow[seg.col].segs.push(seg);
486 }
487 }
488 return byRow;
489 }
490
491 class TimeColMoreLink extends internal$1.BaseComponent {
492 render() {
493 let { props } = this;
494 return (preact.createElement(internal$1.MoreLinkContainer, { elClasses: ['fc-timegrid-more-link'], elStyle: {
495 top: props.top,
496 bottom: props.bottom,
497 }, allDayDate: null, moreCnt: props.hiddenSegs.length, allSegs: props.hiddenSegs, hiddenSegs: props.hiddenSegs, extraDateSpan: props.extraDateSpan, dateProfile: props.dateProfile, todayRange: props.todayRange, popoverContent: () => renderPlainFgSegs(props.hiddenSegs, props), defaultGenerator: renderMoreLinkInner }, (InnerContent) => (preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-timegrid-more-link-inner', 'fc-sticky'] }))));
498 }
499 }
500 function renderMoreLinkInner(props) {
501 return props.shortText;
502 }
503
504 // segInputs assumed sorted
505 function buildPositioning(segInputs, strictOrder, maxStackCnt) {
506 let hierarchy = new internal$1.SegHierarchy();
507 if (strictOrder != null) {
508 hierarchy.strictOrder = strictOrder;
509 }
510 if (maxStackCnt != null) {
511 hierarchy.maxStackCnt = maxStackCnt;
512 }
513 let hiddenEntries = hierarchy.addSegs(segInputs);
514 let hiddenGroups = internal$1.groupIntersectingEntries(hiddenEntries);
515 let web = buildWeb(hierarchy);
516 web = stretchWeb(web, 1); // all levelCoords/thickness will have 0.0-1.0
517 let segRects = webToRects(web);
518 return { segRects, hiddenGroups };
519 }
520 function buildWeb(hierarchy) {
521 const { entriesByLevel } = hierarchy;
522 const buildNode = cacheable((level, lateral) => level + ':' + lateral, (level, lateral) => {
523 let siblingRange = findNextLevelSegs(hierarchy, level, lateral);
524 let nextLevelRes = buildNodes(siblingRange, buildNode);
525 let entry = entriesByLevel[level][lateral];
526 return [
527 Object.assign(Object.assign({}, entry), { nextLevelNodes: nextLevelRes[0] }),
528 entry.thickness + nextLevelRes[1], // the pressure builds
529 ];
530 });
531 return buildNodes(entriesByLevel.length
532 ? { level: 0, lateralStart: 0, lateralEnd: entriesByLevel[0].length }
533 : null, buildNode)[0];
534 }
535 function buildNodes(siblingRange, buildNode) {
536 if (!siblingRange) {
537 return [[], 0];
538 }
539 let { level, lateralStart, lateralEnd } = siblingRange;
540 let lateral = lateralStart;
541 let pairs = [];
542 while (lateral < lateralEnd) {
543 pairs.push(buildNode(level, lateral));
544 lateral += 1;
545 }
546 pairs.sort(cmpDescPressures);
547 return [
548 pairs.map(extractNode),
549 pairs[0][1], // first item's pressure
550 ];
551 }
552 function cmpDescPressures(a, b) {
553 return b[1] - a[1];
554 }
555 function extractNode(a) {
556 return a[0];
557 }
558 function findNextLevelSegs(hierarchy, subjectLevel, subjectLateral) {
559 let { levelCoords, entriesByLevel } = hierarchy;
560 let subjectEntry = entriesByLevel[subjectLevel][subjectLateral];
561 let afterSubject = levelCoords[subjectLevel] + subjectEntry.thickness;
562 let levelCnt = levelCoords.length;
563 let level = subjectLevel;
564 // skip past levels that are too high up
565 for (; level < levelCnt && levelCoords[level] < afterSubject; level += 1)
566 ; // do nothing
567 for (; level < levelCnt; level += 1) {
568 let entries = entriesByLevel[level];
569 let entry;
570 let searchIndex = internal$1.binarySearch(entries, subjectEntry.span.start, internal$1.getEntrySpanEnd);
571 let lateralStart = searchIndex[0] + searchIndex[1]; // if exact match (which doesn't collide), go to next one
572 let lateralEnd = lateralStart;
573 while ( // loop through entries that horizontally intersect
574 (entry = entries[lateralEnd]) && // but not past the whole seg list
575 entry.span.start < subjectEntry.span.end) {
576 lateralEnd += 1;
577 }
578 if (lateralStart < lateralEnd) {
579 return { level, lateralStart, lateralEnd };
580 }
581 }
582 return null;
583 }
584 function stretchWeb(topLevelNodes, totalThickness) {
585 const stretchNode = cacheable((node, startCoord, prevThickness) => internal$1.buildEntryKey(node), (node, startCoord, prevThickness) => {
586 let { nextLevelNodes, thickness } = node;
587 let allThickness = thickness + prevThickness;
588 let thicknessFraction = thickness / allThickness;
589 let endCoord;
590 let newChildren = [];
591 if (!nextLevelNodes.length) {
592 endCoord = totalThickness;
593 }
594 else {
595 for (let childNode of nextLevelNodes) {
596 if (endCoord === undefined) {
597 let res = stretchNode(childNode, startCoord, allThickness);
598 endCoord = res[0];
599 newChildren.push(res[1]);
600 }
601 else {
602 let res = stretchNode(childNode, endCoord, 0);
603 newChildren.push(res[1]);
604 }
605 }
606 }
607 let newThickness = (endCoord - startCoord) * thicknessFraction;
608 return [endCoord - newThickness, Object.assign(Object.assign({}, node), { thickness: newThickness, nextLevelNodes: newChildren })];
609 });
610 return topLevelNodes.map((node) => stretchNode(node, 0, 0)[1]);
611 }
612 // not sorted in any particular order
613 function webToRects(topLevelNodes) {
614 let rects = [];
615 const processNode = cacheable((node, levelCoord, stackDepth) => internal$1.buildEntryKey(node), (node, levelCoord, stackDepth) => {
616 let rect = Object.assign(Object.assign({}, node), { levelCoord,
617 stackDepth, stackForward: 0 });
618 rects.push(rect);
619 return (rect.stackForward = processNodes(node.nextLevelNodes, levelCoord + node.thickness, stackDepth + 1) + 1);
620 });
621 function processNodes(nodes, levelCoord, stackDepth) {
622 let stackForward = 0;
623 for (let node of nodes) {
624 stackForward = Math.max(processNode(node, levelCoord, stackDepth), stackForward);
625 }
626 return stackForward;
627 }
628 processNodes(topLevelNodes, 0, 0);
629 return rects; // TODO: sort rects by levelCoord to be consistent with toRects?
630 }
631 // TODO: move to general util
632 function cacheable(keyFunc, workFunc) {
633 const cache = {};
634 return (...args) => {
635 let key = keyFunc(...args);
636 return (key in cache)
637 ? cache[key]
638 : (cache[key] = workFunc(...args));
639 };
640 }
641
642 function computeSegVCoords(segs, colDate, slatCoords = null, eventMinHeight = 0) {
643 let vcoords = [];
644 if (slatCoords) {
645 for (let i = 0; i < segs.length; i += 1) {
646 let seg = segs[i];
647 let spanStart = slatCoords.computeDateTop(seg.start, colDate);
648 let spanEnd = Math.max(spanStart + (eventMinHeight || 0), // :(
649 slatCoords.computeDateTop(seg.end, colDate));
650 vcoords.push({
651 start: Math.round(spanStart),
652 end: Math.round(spanEnd), //
653 });
654 }
655 }
656 return vcoords;
657 }
658 function computeFgSegPlacements(segs, segVCoords, // might not have for every seg
659 eventOrderStrict, eventMaxStack) {
660 let segInputs = [];
661 let dumbSegs = []; // segs without coords
662 for (let i = 0; i < segs.length; i += 1) {
663 let vcoords = segVCoords[i];
664 if (vcoords) {
665 segInputs.push({
666 index: i,
667 thickness: 1,
668 span: vcoords,
669 });
670 }
671 else {
672 dumbSegs.push(segs[i]);
673 }
674 }
675 let { segRects, hiddenGroups } = buildPositioning(segInputs, eventOrderStrict, eventMaxStack);
676 let segPlacements = [];
677 for (let segRect of segRects) {
678 segPlacements.push({
679 seg: segs[segRect.index],
680 rect: segRect,
681 });
682 }
683 for (let dumbSeg of dumbSegs) {
684 segPlacements.push({ seg: dumbSeg, rect: null });
685 }
686 return { segPlacements, hiddenGroups };
687 }
688
689 const DEFAULT_TIME_FORMAT = internal$1.createFormatter({
690 hour: 'numeric',
691 minute: '2-digit',
692 meridiem: false,
693 });
694 class TimeColEvent extends internal$1.BaseComponent {
695 render() {
696 return (preact.createElement(internal$1.StandardEvent, Object.assign({}, this.props, { elClasses: [
697 'fc-timegrid-event',
698 'fc-v-event',
699 this.props.isShort && 'fc-timegrid-event-short',
700 ], defaultTimeFormat: DEFAULT_TIME_FORMAT })));
701 }
702 }
703
704 class TimeCol extends internal$1.BaseComponent {
705 constructor() {
706 super(...arguments);
707 this.sortEventSegs = internal$1.memoize(internal$1.sortEventSegs);
708 }
709 // TODO: memoize event-placement?
710 render() {
711 let { props, context } = this;
712 let { options } = context;
713 let isSelectMirror = options.selectMirror;
714 let mirrorSegs = // yuck
715 (props.eventDrag && props.eventDrag.segs) ||
716 (props.eventResize && props.eventResize.segs) ||
717 (isSelectMirror && props.dateSelectionSegs) ||
718 [];
719 let interactionAffectedInstances = // TODO: messy way to compute this
720 (props.eventDrag && props.eventDrag.affectedInstances) ||
721 (props.eventResize && props.eventResize.affectedInstances) ||
722 {};
723 let sortedFgSegs = this.sortEventSegs(props.fgEventSegs, options.eventOrder);
724 return (preact.createElement(internal$1.DayCellContainer, { elTag: "td", elRef: props.elRef, elClasses: [
725 'fc-timegrid-col',
726 ...(props.extraClassNames || []),
727 ], elAttrs: Object.assign({ role: 'gridcell' }, props.extraDataAttrs), date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, extraRenderProps: props.extraRenderProps }, (InnerContent) => (preact.createElement("div", { className: "fc-timegrid-col-frame" },
728 preact.createElement("div", { className: "fc-timegrid-col-bg" },
729 this.renderFillSegs(props.businessHourSegs, 'non-business'),
730 this.renderFillSegs(props.bgEventSegs, 'bg-event'),
731 this.renderFillSegs(props.dateSelectionSegs, 'highlight')),
732 preact.createElement("div", { className: "fc-timegrid-col-events" }, this.renderFgSegs(sortedFgSegs, interactionAffectedInstances, false, false, false)),
733 preact.createElement("div", { className: "fc-timegrid-col-events" }, this.renderFgSegs(mirrorSegs, {}, Boolean(props.eventDrag), Boolean(props.eventResize), Boolean(isSelectMirror))),
734 preact.createElement("div", { className: "fc-timegrid-now-indicator-container" }, this.renderNowIndicator(props.nowIndicatorSegs)),
735 internal$1.hasCustomDayCellContent(options) && (preact.createElement(InnerContent, { elTag: "div", elClasses: ['fc-timegrid-col-misc'] }))))));
736 }
737 renderFgSegs(sortedFgSegs, segIsInvisible, isDragging, isResizing, isDateSelecting) {
738 let { props } = this;
739 if (props.forPrint) {
740 return renderPlainFgSegs(sortedFgSegs, props);
741 }
742 return this.renderPositionedFgSegs(sortedFgSegs, segIsInvisible, isDragging, isResizing, isDateSelecting);
743 }
744 renderPositionedFgSegs(segs, // if not mirror, needs to be sorted
745 segIsInvisible, isDragging, isResizing, isDateSelecting) {
746 let { eventMaxStack, eventShortHeight, eventOrderStrict, eventMinHeight } = this.context.options;
747 let { date, slatCoords, eventSelection, todayRange, nowDate } = this.props;
748 let isMirror = isDragging || isResizing || isDateSelecting;
749 let segVCoords = computeSegVCoords(segs, date, slatCoords, eventMinHeight);
750 let { segPlacements, hiddenGroups } = computeFgSegPlacements(segs, segVCoords, eventOrderStrict, eventMaxStack);
751 return (preact.createElement(preact.Fragment, null,
752 this.renderHiddenGroups(hiddenGroups, segs),
753 segPlacements.map((segPlacement) => {
754 let { seg, rect } = segPlacement;
755 let instanceId = seg.eventRange.instance.instanceId;
756 let isVisible = isMirror || Boolean(!segIsInvisible[instanceId] && rect);
757 let vStyle = computeSegVStyle(rect && rect.span);
758 let hStyle = (!isMirror && rect) ? this.computeSegHStyle(rect) : { left: 0, right: 0 };
759 let isInset = Boolean(rect) && rect.stackForward > 0;
760 let isShort = Boolean(rect) && (rect.span.end - rect.span.start) < eventShortHeight; // look at other places for this problem
761 return (preact.createElement("div", { className: 'fc-timegrid-event-harness' +
762 (isInset ? ' fc-timegrid-event-harness-inset' : ''), key: instanceId, style: Object.assign(Object.assign({ visibility: isVisible ? '' : 'hidden' }, vStyle), hStyle) },
763 preact.createElement(TimeColEvent, Object.assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, isShort: isShort }, internal$1.getSegMeta(seg, todayRange, nowDate)))));
764 })));
765 }
766 // will already have eventMinHeight applied because segInputs already had it
767 renderHiddenGroups(hiddenGroups, segs) {
768 let { extraDateSpan, dateProfile, todayRange, nowDate, eventSelection, eventDrag, eventResize } = this.props;
769 return (preact.createElement(preact.Fragment, null, hiddenGroups.map((hiddenGroup) => {
770 let positionCss = computeSegVStyle(hiddenGroup.span);
771 let hiddenSegs = compileSegsFromEntries(hiddenGroup.entries, segs);
772 return (preact.createElement(TimeColMoreLink, { key: internal$1.buildIsoString(internal$1.computeEarliestSegStart(hiddenSegs)), hiddenSegs: hiddenSegs, top: positionCss.top, bottom: positionCss.bottom, extraDateSpan: extraDateSpan, dateProfile: dateProfile, todayRange: todayRange, nowDate: nowDate, eventSelection: eventSelection, eventDrag: eventDrag, eventResize: eventResize }));
773 })));
774 }
775 renderFillSegs(segs, fillType) {
776 let { props, context } = this;
777 let segVCoords = computeSegVCoords(segs, props.date, props.slatCoords, context.options.eventMinHeight); // don't assume all populated
778 let children = segVCoords.map((vcoords, i) => {
779 let seg = segs[i];
780 return (preact.createElement("div", { key: internal$1.buildEventRangeKey(seg.eventRange), className: "fc-timegrid-bg-harness", style: computeSegVStyle(vcoords) }, fillType === 'bg-event' ?
781 preact.createElement(internal$1.BgEvent, Object.assign({ seg: seg }, internal$1.getSegMeta(seg, props.todayRange, props.nowDate))) :
782 internal$1.renderFill(fillType)));
783 });
784 return preact.createElement(preact.Fragment, null, children);
785 }
786 renderNowIndicator(segs) {
787 let { slatCoords, date } = this.props;
788 if (!slatCoords) {
789 return null;
790 }
791 return segs.map((seg, i) => (preact.createElement(internal$1.NowIndicatorContainer
792 // key doesn't matter. will only ever be one
793 , {
794 // key doesn't matter. will only ever be one
795 key: i, elClasses: ['fc-timegrid-now-indicator-line'], elStyle: {
796 top: slatCoords.computeDateTop(seg.start, date),
797 }, isAxis: false, date: date })));
798 }
799 computeSegHStyle(segHCoords) {
800 let { isRtl, options } = this.context;
801 let shouldOverlap = options.slotEventOverlap;
802 let nearCoord = segHCoords.levelCoord; // the left side if LTR. the right side if RTL. floating-point
803 let farCoord = segHCoords.levelCoord + segHCoords.thickness; // the right side if LTR. the left side if RTL. floating-point
804 let left; // amount of space from left edge, a fraction of the total width
805 let right; // amount of space from right edge, a fraction of the total width
806 if (shouldOverlap) {
807 // double the width, but don't go beyond the maximum forward coordinate (1.0)
808 farCoord = Math.min(1, nearCoord + (farCoord - nearCoord) * 2);
809 }
810 if (isRtl) {
811 left = 1 - farCoord;
812 right = nearCoord;
813 }
814 else {
815 left = nearCoord;
816 right = 1 - farCoord;
817 }
818 let props = {
819 zIndex: segHCoords.stackDepth + 1,
820 left: left * 100 + '%',
821 right: right * 100 + '%',
822 };
823 if (shouldOverlap && !segHCoords.stackForward) {
824 // add padding to the edge so that forward stacked events don't cover the resizer's icon
825 props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
826 }
827 return props;
828 }
829 }
830 function renderPlainFgSegs(sortedFgSegs, { todayRange, nowDate, eventSelection, eventDrag, eventResize }) {
831 let hiddenInstances = (eventDrag ? eventDrag.affectedInstances : null) ||
832 (eventResize ? eventResize.affectedInstances : null) ||
833 {};
834 return (preact.createElement(preact.Fragment, null, sortedFgSegs.map((seg) => {
835 let instanceId = seg.eventRange.instance.instanceId;
836 return (preact.createElement("div", { key: instanceId, style: { visibility: hiddenInstances[instanceId] ? 'hidden' : '' } },
837 preact.createElement(TimeColEvent, Object.assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === eventSelection, isShort: false }, internal$1.getSegMeta(seg, todayRange, nowDate)))));
838 })));
839 }
840 function computeSegVStyle(segVCoords) {
841 if (!segVCoords) {
842 return { top: '', bottom: '' };
843 }
844 return {
845 top: segVCoords.start,
846 bottom: -segVCoords.end,
847 };
848 }
849 function compileSegsFromEntries(segEntries, allSegs) {
850 return segEntries.map((segEntry) => allSegs[segEntry.index]);
851 }
852
853 class TimeColsContent extends internal$1.BaseComponent {
854 constructor() {
855 super(...arguments);
856 this.splitFgEventSegs = internal$1.memoize(splitSegsByCol);
857 this.splitBgEventSegs = internal$1.memoize(splitSegsByCol);
858 this.splitBusinessHourSegs = internal$1.memoize(splitSegsByCol);
859 this.splitNowIndicatorSegs = internal$1.memoize(splitSegsByCol);
860 this.splitDateSelectionSegs = internal$1.memoize(splitSegsByCol);
861 this.splitEventDrag = internal$1.memoize(splitInteractionByCol);
862 this.splitEventResize = internal$1.memoize(splitInteractionByCol);
863 this.rootElRef = preact.createRef();
864 this.cellElRefs = new internal$1.RefMap();
865 }
866 render() {
867 let { props, context } = this;
868 let nowIndicatorTop = context.options.nowIndicator &&
869 props.slatCoords &&
870 props.slatCoords.safeComputeTop(props.nowDate); // might return void
871 let colCnt = props.cells.length;
872 let fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, colCnt);
873 let bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, colCnt);
874 let businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, colCnt);
875 let nowIndicatorSegsByRow = this.splitNowIndicatorSegs(props.nowIndicatorSegs, colCnt);
876 let dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, colCnt);
877 let eventDragByRow = this.splitEventDrag(props.eventDrag, colCnt);
878 let eventResizeByRow = this.splitEventResize(props.eventResize, colCnt);
879 return (preact.createElement("div", { className: "fc-timegrid-cols", ref: this.rootElRef },
880 preact.createElement("table", { role: "presentation", style: {
881 minWidth: props.tableMinWidth,
882 width: props.clientWidth,
883 } },
884 props.tableColGroupNode,
885 preact.createElement("tbody", { role: "presentation" },
886 preact.createElement("tr", { role: "row" },
887 props.axis && (preact.createElement("td", { "aria-hidden": true, className: "fc-timegrid-col fc-timegrid-axis" },
888 preact.createElement("div", { className: "fc-timegrid-col-frame" },
889 preact.createElement("div", { className: "fc-timegrid-now-indicator-container" }, typeof nowIndicatorTop === 'number' && (preact.createElement(internal$1.NowIndicatorContainer, { elClasses: ['fc-timegrid-now-indicator-arrow'], elStyle: { top: nowIndicatorTop }, isAxis: true, date: props.nowDate })))))),
890 props.cells.map((cell, i) => (preact.createElement(TimeCol, { key: cell.key, elRef: this.cellElRefs.createRef(cell.key), dateProfile: props.dateProfile, date: cell.date, nowDate: props.nowDate, todayRange: props.todayRange, extraRenderProps: cell.extraRenderProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, extraDateSpan: cell.extraDateSpan, fgEventSegs: fgEventSegsByRow[i], bgEventSegs: bgEventSegsByRow[i], businessHourSegs: businessHourSegsByRow[i], nowIndicatorSegs: nowIndicatorSegsByRow[i], dateSelectionSegs: dateSelectionSegsByRow[i], eventDrag: eventDragByRow[i], eventResize: eventResizeByRow[i], slatCoords: props.slatCoords, eventSelection: props.eventSelection, forPrint: props.forPrint }))))))));
891 }
892 componentDidMount() {
893 this.updateCoords();
894 }
895 componentDidUpdate() {
896 this.updateCoords();
897 }
898 updateCoords() {
899 let { props } = this;
900 if (props.onColCoords &&
901 props.clientWidth !== null // means sizing has stabilized
902 ) {
903 props.onColCoords(new internal$1.PositionCache(this.rootElRef.current, collectCellEls(this.cellElRefs.currentMap, props.cells), true, // horizontal
904 false));
905 }
906 }
907 }
908 function collectCellEls(elMap, cells) {
909 return cells.map((cell) => elMap[cell.key]);
910 }
911
912 /* A component that renders one or more columns of vertical time slots
913 ----------------------------------------------------------------------------------------------------------------------*/
914 class TimeCols extends internal$1.DateComponent {
915 constructor() {
916 super(...arguments);
917 this.processSlotOptions = internal$1.memoize(processSlotOptions);
918 this.state = {
919 slatCoords: null,
920 };
921 this.handleRootEl = (el) => {
922 if (el) {
923 this.context.registerInteractiveComponent(this, {
924 el,
925 isHitComboAllowed: this.props.isHitComboAllowed,
926 });
927 }
928 else {
929 this.context.unregisterInteractiveComponent(this);
930 }
931 };
932 this.handleScrollRequest = (request) => {
933 let { onScrollTopRequest } = this.props;
934 let { slatCoords } = this.state;
935 if (onScrollTopRequest && slatCoords) {
936 if (request.time) {
937 let top = slatCoords.computeTimeTop(request.time);
938 top = Math.ceil(top); // zoom can give weird floating-point values. rather scroll a little bit further
939 if (top) {
940 top += 1; // to overcome top border that slots beyond the first have. looks better
941 }
942 onScrollTopRequest(top);
943 }
944 return true;
945 }
946 return false;
947 };
948 this.handleColCoords = (colCoords) => {
949 this.colCoords = colCoords;
950 };
951 this.handleSlatCoords = (slatCoords) => {
952 this.setState({ slatCoords });
953 if (this.props.onSlatCoords) {
954 this.props.onSlatCoords(slatCoords);
955 }
956 };
957 }
958 render() {
959 let { props, state } = this;
960 return (preact.createElement("div", { className: "fc-timegrid-body", ref: this.handleRootEl, style: {
961 // these props are important to give this wrapper correct dimensions for interactions
962 // TODO: if we set it here, can we avoid giving to inner tables?
963 width: props.clientWidth,
964 minWidth: props.tableMinWidth,
965 } },
966 preact.createElement(TimeColsSlats, { axis: props.axis, dateProfile: props.dateProfile, slatMetas: props.slatMetas, clientWidth: props.clientWidth, minHeight: props.expandRows ? props.clientHeight : '', tableMinWidth: props.tableMinWidth, tableColGroupNode: props.axis ? props.tableColGroupNode : null /* axis depends on the colgroup's shrinking */, onCoords: this.handleSlatCoords }),
967 preact.createElement(TimeColsContent, { cells: props.cells, axis: props.axis, dateProfile: props.dateProfile, businessHourSegs: props.businessHourSegs, bgEventSegs: props.bgEventSegs, fgEventSegs: props.fgEventSegs, dateSelectionSegs: props.dateSelectionSegs, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange, nowDate: props.nowDate, nowIndicatorSegs: props.nowIndicatorSegs, clientWidth: props.clientWidth, tableMinWidth: props.tableMinWidth, tableColGroupNode: props.tableColGroupNode, slatCoords: state.slatCoords, onColCoords: this.handleColCoords, forPrint: props.forPrint })));
968 }
969 componentDidMount() {
970 this.scrollResponder = this.context.createScrollResponder(this.handleScrollRequest);
971 }
972 componentDidUpdate(prevProps) {
973 this.scrollResponder.update(prevProps.dateProfile !== this.props.dateProfile);
974 }
975 componentWillUnmount() {
976 this.scrollResponder.detach();
977 }
978 queryHit(positionLeft, positionTop) {
979 let { dateEnv, options } = this.context;
980 let { colCoords } = this;
981 let { dateProfile } = this.props;
982 let { slatCoords } = this.state;
983 let { snapDuration, snapsPerSlot } = this.processSlotOptions(this.props.slotDuration, options.snapDuration);
984 let colIndex = colCoords.leftToIndex(positionLeft);
985 let slatIndex = slatCoords.positions.topToIndex(positionTop);
986 if (colIndex != null && slatIndex != null) {
987 let cell = this.props.cells[colIndex];
988 let slatTop = slatCoords.positions.tops[slatIndex];
989 let slatHeight = slatCoords.positions.getHeight(slatIndex);
990 let partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1
991 let localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
992 let snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
993 let dayDate = this.props.cells[colIndex].date;
994 let time = internal$1.addDurations(dateProfile.slotMinTime, internal$1.multiplyDuration(snapDuration, snapIndex));
995 let start = dateEnv.add(dayDate, time);
996 let end = dateEnv.add(start, snapDuration);
997 return {
998 dateProfile,
999 dateSpan: Object.assign({ range: { start, end }, allDay: false }, cell.extraDateSpan),
1000 dayEl: colCoords.els[colIndex],
1001 rect: {
1002 left: colCoords.lefts[colIndex],
1003 right: colCoords.rights[colIndex],
1004 top: slatTop,
1005 bottom: slatTop + slatHeight,
1006 },
1007 layer: 0,
1008 };
1009 }
1010 return null;
1011 }
1012 }
1013 function processSlotOptions(slotDuration, snapDurationOverride) {
1014 let snapDuration = snapDurationOverride || slotDuration;
1015 let snapsPerSlot = internal$1.wholeDivideDurations(slotDuration, snapDuration);
1016 if (snapsPerSlot === null) {
1017 snapDuration = slotDuration;
1018 snapsPerSlot = 1;
1019 // TODO: say warning?
1020 }
1021 return { snapDuration, snapsPerSlot };
1022 }
1023
1024 class DayTimeColsSlicer extends internal$1.Slicer {
1025 sliceRange(range, dayRanges) {
1026 let segs = [];
1027 for (let col = 0; col < dayRanges.length; col += 1) {
1028 let segRange = internal$1.intersectRanges(range, dayRanges[col]);
1029 if (segRange) {
1030 segs.push({
1031 start: segRange.start,
1032 end: segRange.end,
1033 isStart: segRange.start.valueOf() === range.start.valueOf(),
1034 isEnd: segRange.end.valueOf() === range.end.valueOf(),
1035 col,
1036 });
1037 }
1038 }
1039 return segs;
1040 }
1041 }
1042
1043 class DayTimeCols extends internal$1.DateComponent {
1044 constructor() {
1045 super(...arguments);
1046 this.buildDayRanges = internal$1.memoize(buildDayRanges);
1047 this.slicer = new DayTimeColsSlicer();
1048 this.timeColsRef = preact.createRef();
1049 }
1050 render() {
1051 let { props, context } = this;
1052 let { dateProfile, dayTableModel } = props;
1053 let { nowIndicator, nextDayThreshold } = context.options;
1054 let dayRanges = this.buildDayRanges(dayTableModel, dateProfile, context.dateEnv);
1055 // give it the first row of cells
1056 // TODO: would move this further down hierarchy, but sliceNowDate needs it
1057 return (preact.createElement(internal$1.NowTimer, { unit: nowIndicator ? 'minute' : 'day' }, (nowDate, todayRange) => (preact.createElement(TimeCols, Object.assign({ ref: this.timeColsRef }, this.slicer.sliceProps(props, dateProfile, null, context, dayRanges), { forPrint: props.forPrint, axis: props.axis, dateProfile: dateProfile, slatMetas: props.slatMetas, slotDuration: props.slotDuration, cells: dayTableModel.cells[0], tableColGroupNode: props.tableColGroupNode, tableMinWidth: props.tableMinWidth, clientWidth: props.clientWidth, clientHeight: props.clientHeight, expandRows: props.expandRows, nowDate: nowDate, nowIndicatorSegs: nowIndicator && this.slicer.sliceNowDate(nowDate, dateProfile, nextDayThreshold, context, dayRanges), todayRange: todayRange, onScrollTopRequest: props.onScrollTopRequest, onSlatCoords: props.onSlatCoords })))));
1058 }
1059 }
1060 function buildDayRanges(dayTableModel, dateProfile, dateEnv) {
1061 let ranges = [];
1062 for (let date of dayTableModel.headerDates) {
1063 ranges.push({
1064 start: dateEnv.add(date, dateProfile.slotMinTime),
1065 end: dateEnv.add(date, dateProfile.slotMaxTime),
1066 });
1067 }
1068 return ranges;
1069 }
1070
1071 // potential nice values for the slot-duration and interval-duration
1072 // from largest to smallest
1073 const STOCK_SUB_DURATIONS = [
1074 { hours: 1 },
1075 { minutes: 30 },
1076 { minutes: 15 },
1077 { seconds: 30 },
1078 { seconds: 15 },
1079 ];
1080 function buildSlatMetas(slotMinTime, slotMaxTime, explicitLabelInterval, slotDuration, dateEnv) {
1081 let dayStart = new Date(0);
1082 let slatTime = slotMinTime;
1083 let slatIterator = internal$1.createDuration(0);
1084 let labelInterval = explicitLabelInterval || computeLabelInterval(slotDuration);
1085 let metas = [];
1086 while (internal$1.asRoughMs(slatTime) < internal$1.asRoughMs(slotMaxTime)) {
1087 let date = dateEnv.add(dayStart, slatTime);
1088 let isLabeled = internal$1.wholeDivideDurations(slatIterator, labelInterval) !== null;
1089 metas.push({
1090 date,
1091 time: slatTime,
1092 key: date.toISOString(),
1093 isoTimeStr: internal$1.formatIsoTimeString(date),
1094 isLabeled,
1095 });
1096 slatTime = internal$1.addDurations(slatTime, slotDuration);
1097 slatIterator = internal$1.addDurations(slatIterator, slotDuration);
1098 }
1099 return metas;
1100 }
1101 // Computes an automatic value for slotLabelInterval
1102 function computeLabelInterval(slotDuration) {
1103 let i;
1104 let labelInterval;
1105 let slotsPerLabel;
1106 // find the smallest stock label interval that results in more than one slots-per-label
1107 for (i = STOCK_SUB_DURATIONS.length - 1; i >= 0; i -= 1) {
1108 labelInterval = internal$1.createDuration(STOCK_SUB_DURATIONS[i]);
1109 slotsPerLabel = internal$1.wholeDivideDurations(labelInterval, slotDuration);
1110 if (slotsPerLabel !== null && slotsPerLabel > 1) {
1111 return labelInterval;
1112 }
1113 }
1114 return slotDuration; // fall back
1115 }
1116
1117 class DayTimeColsView extends TimeColsView {
1118 constructor() {
1119 super(...arguments);
1120 this.buildTimeColsModel = internal$1.memoize(buildTimeColsModel);
1121 this.buildSlatMetas = internal$1.memoize(buildSlatMetas);
1122 }
1123 render() {
1124 let { options, dateEnv, dateProfileGenerator } = this.context;
1125 let { props } = this;
1126 let { dateProfile } = props;
1127 let dayTableModel = this.buildTimeColsModel(dateProfile, dateProfileGenerator);
1128 let splitProps = this.allDaySplitter.splitProps(props);
1129 let slatMetas = this.buildSlatMetas(dateProfile.slotMinTime, dateProfile.slotMaxTime, options.slotLabelInterval, options.slotDuration, dateEnv);
1130 let { dayMinWidth } = options;
1131 let hasAttachedAxis = !dayMinWidth;
1132 let hasDetachedAxis = dayMinWidth;
1133 let headerContent = options.dayHeaders && (preact.createElement(internal$1.DayHeader, { dates: dayTableModel.headerDates, dateProfile: dateProfile, datesRepDistinctDays: true, renderIntro: hasAttachedAxis ? this.renderHeadAxis : null }));
1134 let allDayContent = (options.allDaySlot !== false) && ((contentArg) => (preact.createElement(internal$2.DayTable, Object.assign({}, splitProps.allDay, { dateProfile: dateProfile, dayTableModel: dayTableModel, nextDayThreshold: options.nextDayThreshold, tableMinWidth: contentArg.tableMinWidth, colGroupNode: contentArg.tableColGroupNode, renderRowIntro: hasAttachedAxis ? this.renderTableRowAxis : null, showWeekNumbers: false, expandRows: false, headerAlignElRef: this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint }, this.getAllDayMaxEventProps()))));
1135 let timeGridContent = (contentArg) => (preact.createElement(DayTimeCols, Object.assign({}, splitProps.timed, { dayTableModel: dayTableModel, dateProfile: dateProfile, axis: hasAttachedAxis, slotDuration: options.slotDuration, slatMetas: slatMetas, forPrint: props.forPrint, tableColGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, onSlatCoords: this.handleSlatCoords, expandRows: contentArg.expandRows, onScrollTopRequest: this.handleScrollTopRequest })));
1136 return hasDetachedAxis
1137 ? this.renderHScrollLayout(headerContent, allDayContent, timeGridContent, dayTableModel.colCnt, dayMinWidth, slatMetas, this.state.slatCoords)
1138 : this.renderSimpleLayout(headerContent, allDayContent, timeGridContent);
1139 }
1140 }
1141 function buildTimeColsModel(dateProfile, dateProfileGenerator) {
1142 let daySeries = new internal$1.DaySeriesModel(dateProfile.renderRange, dateProfileGenerator);
1143 return new internal$1.DayTableModel(daySeries, false);
1144 }
1145
1146 const OPTION_REFINERS = {
1147 allDaySlot: Boolean,
1148 };
1149
1150 var css_248z = ".fc-v-event{background-color:var(--fc-event-bg-color);border:1px solid var(--fc-event-border-color);display:block}.fc-v-event .fc-event-main{color:var(--fc-event-text-color);height:100%}.fc-v-event .fc-event-main-frame{display:flex;flex-direction:column;height:100%}.fc-v-event .fc-event-time{flex-grow:0;flex-shrink:0;max-height:100%;overflow:hidden}.fc-v-event .fc-event-title-container{flex-grow:1;flex-shrink:1;min-height:0}.fc-v-event .fc-event-title{bottom:0;max-height:100%;overflow:hidden;top:0}.fc-v-event:not(.fc-event-start){border-top-left-radius:0;border-top-right-radius:0;border-top-width:0}.fc-v-event:not(.fc-event-end){border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom-width:0}.fc-v-event.fc-event-selected:before{left:-10px;right:-10px}.fc-v-event .fc-event-resizer-start{cursor:n-resize}.fc-v-event .fc-event-resizer-end{cursor:s-resize}.fc-v-event:not(.fc-event-selected) .fc-event-resizer{height:var(--fc-event-resizer-thickness);left:0;right:0}.fc-v-event:not(.fc-event-selected) .fc-event-resizer-start{top:calc(var(--fc-event-resizer-thickness)/-2)}.fc-v-event:not(.fc-event-selected) .fc-event-resizer-end{bottom:calc(var(--fc-event-resizer-thickness)/-2)}.fc-v-event.fc-event-selected .fc-event-resizer{left:50%;margin-left:calc(var(--fc-event-resizer-dot-total-width)/-2)}.fc-v-event.fc-event-selected .fc-event-resizer-start{top:calc(var(--fc-event-resizer-dot-total-width)/-2)}.fc-v-event.fc-event-selected .fc-event-resizer-end{bottom:calc(var(--fc-event-resizer-dot-total-width)/-2)}.fc .fc-timegrid .fc-daygrid-body{z-index:2}.fc .fc-timegrid-divider{padding:0 0 2px}.fc .fc-timegrid-body{min-height:100%;position:relative;z-index:1}.fc .fc-timegrid-axis-chunk{position:relative}.fc .fc-timegrid-axis-chunk>table,.fc .fc-timegrid-slots{position:relative;z-index:1}.fc .fc-timegrid-slot{border-bottom:0;height:1.5em}.fc .fc-timegrid-slot:empty:before{content:\"\\00a0\"}.fc .fc-timegrid-slot-minor{border-top-style:dotted}.fc .fc-timegrid-slot-label-cushion{display:inline-block;white-space:nowrap}.fc .fc-timegrid-slot-label{vertical-align:middle}.fc .fc-timegrid-axis-cushion,.fc .fc-timegrid-slot-label-cushion{padding:0 4px}.fc .fc-timegrid-axis-frame-liquid{height:100%}.fc .fc-timegrid-axis-frame{align-items:center;display:flex;justify-content:flex-end;overflow:hidden}.fc .fc-timegrid-axis-cushion{flex-shrink:0;max-width:60px}.fc-direction-ltr .fc-timegrid-slot-label-frame{text-align:right}.fc-direction-rtl .fc-timegrid-slot-label-frame{text-align:left}.fc-liquid-hack .fc-timegrid-axis-frame-liquid{bottom:0;height:auto;left:0;position:absolute;right:0;top:0}.fc .fc-timegrid-col.fc-day-today{background-color:var(--fc-today-bg-color)}.fc .fc-timegrid-col-frame{min-height:100%;position:relative}.fc-media-screen.fc-liquid-hack .fc-timegrid-col-frame{bottom:0;height:auto;left:0;position:absolute;right:0;top:0}.fc-media-screen .fc-timegrid-cols{bottom:0;left:0;position:absolute;right:0;top:0}.fc-media-screen .fc-timegrid-cols>table{height:100%}.fc-media-screen .fc-timegrid-col-bg,.fc-media-screen .fc-timegrid-col-events,.fc-media-screen .fc-timegrid-now-indicator-container{left:0;position:absolute;right:0;top:0}.fc .fc-timegrid-col-bg{z-index:2}.fc .fc-timegrid-col-bg .fc-non-business{z-index:1}.fc .fc-timegrid-col-bg .fc-bg-event{z-index:2}.fc .fc-timegrid-col-bg .fc-highlight{z-index:3}.fc .fc-timegrid-bg-harness{left:0;position:absolute;right:0}.fc .fc-timegrid-col-events{z-index:3}.fc .fc-timegrid-now-indicator-container{bottom:0;overflow:hidden}.fc-direction-ltr .fc-timegrid-col-events{margin:0 2.5% 0 2px}.fc-direction-rtl .fc-timegrid-col-events{margin:0 2px 0 2.5%}.fc-timegrid-event-harness{position:absolute}.fc-timegrid-event-harness>.fc-timegrid-event{bottom:0;left:0;position:absolute;right:0;top:0}.fc-timegrid-event-harness-inset .fc-timegrid-event,.fc-timegrid-event.fc-event-mirror,.fc-timegrid-more-link{box-shadow:0 0 0 1px var(--fc-page-bg-color)}.fc-timegrid-event,.fc-timegrid-more-link{border-radius:3px;font-size:var(--fc-small-font-size)}.fc-timegrid-event{margin-bottom:1px}.fc-timegrid-event .fc-event-main{padding:1px 1px 0}.fc-timegrid-event .fc-event-time{font-size:var(--fc-small-font-size);margin-bottom:1px;white-space:nowrap}.fc-timegrid-event-short .fc-event-main-frame{flex-direction:row;overflow:hidden}.fc-timegrid-event-short .fc-event-time:after{content:\"\\00a0-\\00a0\"}.fc-timegrid-event-short .fc-event-title{font-size:var(--fc-small-font-size)}.fc-timegrid-more-link{background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;margin-bottom:1px;position:absolute;z-index:9999}.fc-timegrid-more-link-inner{padding:3px 2px;top:0}.fc-direction-ltr .fc-timegrid-more-link{right:0}.fc-direction-rtl .fc-timegrid-more-link{left:0}.fc .fc-timegrid-now-indicator-line{border-color:var(--fc-now-indicator-color);border-style:solid;border-width:1px 0 0;left:0;position:absolute;right:0;z-index:4}.fc .fc-timegrid-now-indicator-arrow{border-color:var(--fc-now-indicator-color);border-style:solid;margin-top:-5px;position:absolute;z-index:4}.fc-direction-ltr .fc-timegrid-now-indicator-arrow{border-bottom-color:transparent;border-top-color:transparent;border-width:5px 0 5px 6px;left:0}.fc-direction-rtl .fc-timegrid-now-indicator-arrow{border-bottom-color:transparent;border-top-color:transparent;border-width:5px 6px 5px 0;right:0}";
1151 internal$1.injectStyles(css_248z);
1152
1153 var plugin = core.createPlugin({
1154 name: '@fullcalendar/timegrid',
1155 initialView: 'timeGridWeek',
1156 optionRefiners: OPTION_REFINERS,
1157 views: {
1158 timeGrid: {
1159 component: DayTimeColsView,
1160 usesMinMaxTime: true,
1161 allDaySlot: true,
1162 slotDuration: '00:30:00',
1163 slotEventOverlap: true, // a bad name. confused with overlap/constraint system
1164 },
1165 timeGridDay: {
1166 type: 'timeGrid',
1167 duration: { days: 1 },
1168 },
1169 timeGridWeek: {
1170 type: 'timeGrid',
1171 duration: { weeks: 1 },
1172 },
1173 },
1174 });
1175
1176 var internal = {
1177 __proto__: null,
1178 TimeColsView: TimeColsView,
1179 DayTimeColsView: DayTimeColsView,
1180 buildTimeColsModel: buildTimeColsModel,
1181 DayTimeCols: DayTimeCols,
1182 buildDayRanges: buildDayRanges,
1183 DayTimeColsSlicer: DayTimeColsSlicer,
1184 TimeCols: TimeCols,
1185 buildSlatMetas: buildSlatMetas,
1186 TimeColsSlatsCoords: TimeColsSlatsCoords
1187 };
1188
1189 core.globalPlugins.push(plugin);
1190
1191 exports.Internal = internal;
1192 exports["default"] = plugin;
1193
1194 Object.defineProperty(exports, '__esModule', { value: true });
1195
1196 return exports;
1197
1198})({}, FullCalendar, FullCalendar.Internal, FullCalendar.Preact, FullCalendar.DayGrid.Internal);