@fullcalendar/daygrid 7.0.0-beta.1 → 7.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/internal.js CHANGED
@@ -1,8 +1,5 @@
1
- import { injectStyles, Slicer, DateProfileGenerator, addWeeks, addDays, diffWeeks, DaySeriesModel, DayTableModel, fracToCssDim, BaseComponent, getDateMeta, buildNavLinkAttrs, ContentContainer, getDayClassNames, formatDayString, watchHeight, setRef, createFormatter, StandardEvent, buildEventRangeTimeText, EventContainer, getEventRangeAnchorAttrs, MoreLinkContainer, getEventRangeMeta, DateComponent, DayCellContainer, hasCustomDayCellContent, addMs, SegHierarchy, buildEntryKey, intersectSpans, RefMap, afterSize, sortEventSegs, WeekNumberContainer, buildEventRangeKey, BgEvent, renderFill, memoize, getIsHeightAuto, watchWidth, getStickyHeaderDates, Scroller, getStickyFooterScrollbar, getScrollerSyncerClass, ViewContainer, NowTimer } from '@fullcalendar/core/internal.js';
2
- import { createRef, createElement, Fragment, Component } from '@fullcalendar/core/preact.js';
3
-
4
- var css_248z = ":root{--fc-daygrid-event-dot-width:8px}.fc-daygrid-week-number{background-color:var(--fc-neutral-bg-color);color:var(--fc-neutral-text-color);min-width:1.5em;padding:2px;position:absolute;text-align:center;top:0;z-index:5}.fc-daygrid-cell.fc-day-today{background-color:var(--fc-today-bg-color)}.fc-daygrid-row-spacious .fc-daygrid-cell-inner{min-height:3em}.fc-daygrid-cell-header{display:flex;flex-direction:row-reverse}.fc-day-other .fc-daygrid-cell-header{opacity:.3}.fc-daygrid-cell-number{padding:4px;position:relative;z-index:4}.fc-daygrid-month-start{font-size:1.1em;font-weight:700}.fc-daygrid-cell-footer{align-items:flex-start;display:flex;flex-direction:column;font-size:.85em;margin:0 2px}.fc-daygrid-row-spacious .fc-daygrid-cell-footer{margin-bottom:1em!important}.fc-daygrid-row-compact .fc-daygrid-cell-footer{align-items:stretch}.fc-daygrid-more-link{border-radius:3px;cursor:pointer;line-height:1;margin-top:1px;max-width:100%;overflow:hidden;padding:2px;position:relative;white-space:nowrap;z-index:4}.fc-daygrid-more-link:hover{background-color:rgba(0,0,0,.1)}.fc-daygrid-row-compact .fc-daygrid-more-link{border:1px solid var(--fc-event-border-color);padding:1px}.fc-daygrid-cell .fc-non-business{z-index:1}.fc-daygrid-cell .fc-bg-event{z-index:2}.fc-daygrid-cell .fc-highlight{z-index:3}.fc-more-popover .fc-popover-body{min-width:220px;padding:10px}.fc-daygrid-event{border-radius:3px;font-size:var(--fc-small-font-size);margin-top:1px;z-index:6}.fc-daygrid-event.fc-event-mirror{z-index:7}.fc-direction-ltr .fc-daygrid-event.fc-event-start,.fc-direction-rtl .fc-daygrid-event.fc-event-end{margin-left:2px}.fc-direction-ltr .fc-daygrid-event.fc-event-end,.fc-direction-rtl .fc-daygrid-event.fc-event-start{margin-right:2px}.fc-direction-ltr .fc-daygrid-event .fc-event-time{margin-right:3px}.fc-direction-rtl .fc-daygrid-event .fc-event-time{margin-left:3px}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-start),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-end){border-bottom-left-radius:0;border-left-width:0;border-top-left-radius:0}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-end),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-start){border-bottom-right-radius:0;border-right-width:0;border-top-right-radius:0}.fc-daygrid-block-event .fc-event-time{font-weight:700}.fc-daygrid-block-event .fc-event-time,.fc-daygrid-block-event .fc-event-title{padding:1px}.fc-daygrid-dot-event{align-items:center;direction:row;display:flex;padding:2px 0;position:relative}.fc-daygrid-dot-event.fc-event-mirror,.fc-daygrid-dot-event:hover{background:rgba(0,0,0,.1)}.fc-daygrid-event-dot{border:calc(var(--fc-daygrid-event-dot-width)/2) solid var(--fc-event-border-color);border-radius:calc(var(--fc-daygrid-event-dot-width)/2);box-sizing:content-box;height:0;margin:0 4px;width:0}.fc-daygrid-dot-event .fc-event-time,.fc-daygrid-dot-event .fc-event-title{overflow:hidden;white-space:nowrap}.fc-daygrid-dot-event .fc-event-title{flex-basis:0;flex-grow:1;font-weight:700;min-width:0}";
5
- injectStyles(css_248z);
1
+ import { Slicer, createFormatter, getDateMeta, buildDateStr, formatDayString, buildNavLinkAttrs, getDayClassName, addDays, getEventKey, BaseComponent, StandardEvent, buildEventRangeTimeText, getEventTagAndAttrs, EventContainer, MoreLinkContainer, joinClassNames, getEventRangeMeta, DateComponent, setRef, watchSize, isDimsEqual, hasCustomDayCellContent, DayCellContainer, addMs, SegHierarchy, DaySeriesModel, DayTableModel, fracToCssDim, watchHeight, RefMap, afterSize, sortEventSegs, WeekNumberContainer, buildEventRangeKey, BgEvent, renderFill, memoize, getIsHeightAuto, watchWidth, ContentContainer, renderText, getStickyHeaderDates, Scroller, getStickyFooterScrollbar, StickyFooterScrollbar, getScrollerSyncerClass, ViewContainer, NowTimer, DateProfileGenerator, addWeeks, diffWeeks, injectStyles } from '@fullcalendar/core/internal.js';
2
+ import { createElement, Fragment, createRef, Component } from '@fullcalendar/core/preact.js';
6
3
 
7
4
  class DayTableSlicer extends Slicer {
8
5
  constructor() {
@@ -14,193 +11,110 @@ class DayTableSlicer extends Slicer {
14
11
  }
15
12
  }
16
13
 
17
- class TableDateProfileGenerator extends DateProfileGenerator {
18
- // Computes the date range that will be rendered
19
- buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay) {
20
- let renderRange = super.buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay);
21
- let { props } = this;
22
- return buildDayTableRenderRange({
23
- currentRange: renderRange,
24
- snapToWeek: /^(year|month)$/.test(currentRangeUnit),
25
- fixedWeekCount: props.fixedWeekCount,
26
- dateEnv: props.dateEnv,
27
- });
28
- }
29
- }
30
- function buildDayTableRenderRange(props) {
31
- let { dateEnv, currentRange } = props;
32
- let { start, end } = currentRange;
33
- let endOfWeek;
34
- // year and month views should be aligned with weeks. this is already done for week
35
- if (props.snapToWeek) {
36
- start = dateEnv.startOfWeek(start);
37
- // make end-of-week if not already
38
- endOfWeek = dateEnv.startOfWeek(end);
39
- if (endOfWeek.valueOf() !== end.valueOf()) {
40
- end = addWeeks(endOfWeek, 1);
41
- }
42
- }
43
- // ensure 6 weeks
44
- if (props.fixedWeekCount) {
45
- // TODO: instead of these date-math gymnastics (for multimonth view),
46
- // compute dateprofiles of all months, then use start of first and end of last.
47
- let lastMonthRenderStart = dateEnv.startOfWeek(dateEnv.startOfMonth(addDays(currentRange.end, -1)));
48
- let rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
49
- diffWeeks(lastMonthRenderStart, end));
50
- end = addWeeks(end, 6 - rowCnt);
51
- }
52
- return { start, end };
53
- }
54
-
55
- function renderInner(renderProps) {
56
- return renderProps.text;
57
- }
58
- function buildDayTableModel(dateProfile, dateProfileGenerator) {
59
- let daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator);
60
- return new DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
61
- }
62
- function computeColWidth(colCnt, colMinWidth, viewportWidth) {
63
- if (viewportWidth == null) {
64
- return [undefined, undefined];
65
- }
66
- const colTempWidth = viewportWidth / colCnt;
67
- if (colTempWidth < colMinWidth) {
68
- return [colMinWidth * colCnt, colMinWidth];
69
- }
70
- return [viewportWidth, undefined];
71
- }
72
- function buildHeaderTiers(dates, datesRepDistinctDays) {
73
- return [
74
- datesRepDistinctDays
75
- ? dates.map((date) => ({ colSpan: 1, date }))
76
- : dates.map((date) => ({ colSpan: 1, dow: date.getUTCDay() }))
77
- ];
78
- }
79
- // Positioning
14
+ // TODO: converge types with DayTableCell and DayCellContainer (the component) and refineRenderProps
15
+ // the generation of DayTableCell will be distinct (for the BODY cells)
16
+ // but can share some of the same types/utils
17
+ // Date Cells
80
18
  // -------------------------------------------------------------------------------------------------
81
- function computeTopFromDate(date, cellRows, rowHeightMap, adjust = 0) {
82
- let top = 0;
83
- for (const cells of cellRows) {
84
- const start = cells[0].date;
85
- const end = cells[cells.length - 1].date;
86
- const key = start.toISOString();
87
- if (date >= start && date <= end) {
88
- return top;
89
- }
90
- const rowHeight = rowHeightMap.get(key);
91
- if (rowHeight == null) {
92
- return; // denote unknown
93
- }
94
- top += rowHeight + adjust;
95
- }
96
- return top;
97
- }
98
- function computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl) {
99
- let left;
100
- let right;
101
- let width;
102
- if (colWidth != null) {
103
- width = (seg.lastCol - seg.firstCol + 1) * colWidth;
104
- if (isRtl) {
105
- right = seg.firstCol * colWidth;
106
- }
107
- else {
108
- left = seg.firstCol * colWidth;
109
- }
110
- }
111
- else {
112
- const colWidthFrac = 1 / colCnt;
113
- width = fracToCssDim((seg.lastCol - seg.firstCol + 1) * colWidthFrac);
114
- if (isRtl) {
115
- right = fracToCssDim(seg.firstCol * colWidthFrac);
116
- }
117
- else {
118
- left = fracToCssDim(seg.firstCol * colWidthFrac);
119
- }
120
- }
121
- return { left, right, width };
122
- }
123
- function computeColFromPosition(positionLeft, elWidth, colWidth, colCnt, isRtl) {
124
- const realColWidth = colWidth != null ? colWidth : elWidth / colCnt;
125
- const colFromLeft = Math.floor(positionLeft / realColWidth);
126
- const col = isRtl ? (colCnt - colFromLeft - 1) : colFromLeft;
127
- const left = colFromLeft * realColWidth;
128
- const right = left + realColWidth;
129
- return { col, left, right };
130
- }
131
- function computeRowFromPosition(positionTop, cellRows, rowHeightMap) {
132
- let row = 0;
133
- let top = 0;
134
- let bottom = 0;
135
- for (const cells of cellRows) {
136
- const key = cells[0].key;
137
- top = bottom;
138
- bottom = top + rowHeightMap.get(key);
139
- if (positionTop < bottom) {
140
- break;
141
- }
142
- row++;
143
- }
144
- return { row, top, bottom };
19
+ const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
20
+ const firstSunday = new Date(259200000);
21
+ function buildDateRowConfigs(...args) {
22
+ return [buildDateRowConfig(...args)];
145
23
  }
146
- // Hit Element
147
- // -------------------------------------------------------------------------------------------------
148
- function getRowEl(rootEl, row) {
149
- return rootEl.querySelectorAll(':scope > [role=row]')[row];
24
+ /*
25
+ Should this receive resource data attributes?
26
+ Or ResourceApi object itself?
27
+ */
28
+ function buildDateRowConfig(dates, datesRepDistinctDays, dateProfile, todayRange, dayHeaderFormat, // TODO: rename to dateHeaderFormat?
29
+ context, colSpan) {
30
+ return {
31
+ renderConfig: buildDateRenderConfig(context),
32
+ dataConfigs: buildDateDataConfigs(dates, datesRepDistinctDays, dateProfile, todayRange, dayHeaderFormat, context, colSpan)
33
+ };
150
34
  }
151
- function getCellEl(rowEl, col) {
152
- return rowEl.querySelectorAll(':scope > [role=gridcell]')[col];
35
+ /*
36
+ For header cells: how to connect w/ custom rendering
37
+ Applies to all cells in a row
38
+ */
39
+ function buildDateRenderConfig(context) {
40
+ const { options } = context;
41
+ return {
42
+ generatorName: 'dayHeaderContent',
43
+ customGenerator: options.dayHeaderContent,
44
+ classNameGenerator: options.dayHeaderClassNames,
45
+ didMount: options.dayHeaderDidMount,
46
+ willUnmount: options.dayHeaderWillUnmount,
47
+ };
153
48
  }
154
-
155
- class DateHeaderCell extends BaseComponent {
156
- constructor() {
157
- super(...arguments);
158
- // ref
159
- this.innerElRef = createRef();
160
- }
161
- render() {
162
- let { props, context } = this;
163
- let { dateProfile, date, extraRenderProps, extraDataAttrs } = props;
164
- let { dateEnv, options, theme, viewApi } = context;
165
- let dayMeta = getDateMeta(date, props.todayRange, null, dateProfile);
166
- let text = dateEnv.format(date, props.dayHeaderFormat);
167
- let navLinkAttrs = (!dayMeta.isDisabled && props.navLink)
168
- ? buildNavLinkAttrs(context, date)
169
- : {};
170
- let renderProps = Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: viewApi }, extraRenderProps), { text }), dayMeta);
171
- return (createElement(ContentContainer, { elTag: 'div', elClasses: [
172
- ...getDayClassNames(dayMeta, theme),
173
- ...(props.extraClassNames || []),
174
- 'fc-header-cell',
175
- 'fc-cell',
176
- props.colWidth != null ? '' : 'fc-liquid',
177
- 'fc-flex-column',
178
- 'fc-align-center',
179
- ], elAttrs: Object.assign({ 'data-date': !dayMeta.isDisabled ? formatDayString(date) : undefined }, extraDataAttrs), elStyle: {
180
- width: props.colWidth != null // TODO: DRY
181
- ? props.colWidth * (props.colSpan || 1)
182
- : undefined,
183
- }, renderProps: renderProps, generatorName: "dayHeaderContent", customGenerator: options.dayHeaderContent, defaultGenerator: renderInner, classNameGenerator: options.dayHeaderClassNames, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, (InnerContainer) => (createElement("div", { ref: this.innerElRef, className: [
184
- 'fc-flex-column',
185
- props.isSticky ? 'fc-sticky-x' : '',
186
- ].join(' ') }, !dayMeta.isDisabled && (createElement(InnerContainer, { elTag: "a", elAttrs: navLinkAttrs, elClasses: [
187
- 'fc-cell-inner',
188
- 'fc-padding-sm',
189
- ] }))))));
190
- }
191
- componentDidMount() {
192
- const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
193
- // TODO: only attach this if refs props present
194
- this.disconectInnerHeight = watchHeight(innerEl, (height) => {
195
- setRef(this.props.innerHeightRef, height);
49
+ /*
50
+ For header cells: data
51
+ */
52
+ function buildDateDataConfigs(dates, datesRepDistinctDays, dateProfile, todayRange, dayHeaderFormat, // TODO: rename to dateHeaderFormat?
53
+ context, colSpan = 1, keyPrefix = '') {
54
+ const { dateEnv, viewApi, options } = context;
55
+ return datesRepDistinctDays
56
+ ? dates.map((date) => {
57
+ const dateMeta = getDateMeta(date, todayRange, null, dateProfile);
58
+ const text = dateEnv.format(date, dayHeaderFormat);
59
+ const renderProps = Object.assign(Object.assign({}, dateMeta), { date: dateEnv.toDate(date), view: viewApi, text });
60
+ const isNavLink = options.navLinks && !dateMeta.isDisabled;
61
+ const fullDateStr = buildDateStr(context, date);
62
+ // for DayGridHeaderCell
63
+ return {
64
+ key: keyPrefix + date.toUTCString(),
65
+ renderProps,
66
+ attrs: Object.assign(Object.assign({ 'aria-label': fullDateStr }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), { 'data-date': formatDayString(date) }),
67
+ // for navlink
68
+ innerAttrs: isNavLink
69
+ ? buildNavLinkAttrs(context, date, undefined, fullDateStr)
70
+ : { 'aria-hidden': true },
71
+ colSpan,
72
+ isNavLink,
73
+ className: getDayClassName(dateMeta),
74
+ };
75
+ })
76
+ : dates.map((date) => {
77
+ const dow = date.getUTCDay();
78
+ const normDate = addDays(firstSunday, dow);
79
+ const dayMeta = {
80
+ dow,
81
+ isDisabled: false,
82
+ isFuture: false,
83
+ isPast: false,
84
+ isToday: false,
85
+ isOther: false,
86
+ };
87
+ const text = dateEnv.format(normDate, dayHeaderFormat);
88
+ const renderProps = Object.assign(Object.assign({}, dayMeta), { date, view: viewApi, text });
89
+ const fullWeekDayStr = dateEnv.format(normDate, WEEKDAY_FORMAT);
90
+ // for DayGridHeaderCell
91
+ return {
92
+ key: keyPrefix + String(dow),
93
+ renderProps,
94
+ attrs: {
95
+ 'aria-label': fullWeekDayStr,
96
+ },
97
+ // for navlink
98
+ innerAttrs: {
99
+ 'aria-hidden': true, // label already on cell
100
+ },
101
+ colSpan,
102
+ className: getDayClassName(dayMeta),
103
+ };
196
104
  });
197
- }
198
- componentWillUnmount() {
199
- this.disconectInnerHeight();
200
- setRef(this.props.innerHeightRef, null);
201
- }
202
105
  }
203
106
 
107
+ /*
108
+ We need really specific keys because RefMap::createRef() which is then given to heightRef
109
+ unable to change key! As a result, we cannot reuse elements between normal/slice/standin types,
110
+ but that's okay since they render quite differently
111
+ */
112
+ function getEventPartKey(seg) {
113
+ return getEventKey(seg) + ':' + seg.start +
114
+ (seg.standinFor ? ':standin' : seg.isSlice ? ':slice' : '');
115
+ }
116
+ // DayGridRange utils (TODO: move)
117
+ // -------------------------------------------------------------------------------------------------
204
118
  function splitSegsByRow(segs, rowCnt) {
205
119
  const byRow = [];
206
120
  for (let row = 0; row < rowCnt; row++) {
@@ -232,20 +146,8 @@ function splitInteractionByRow(ui, rowCnt) {
232
146
  }
233
147
  return byRow;
234
148
  }
235
- function splitSegsByCol(segs, colCnt) {
236
- let byCol = [];
237
- for (let col = 0; col < colCnt; col++) {
238
- byCol.push([]);
239
- }
240
- for (let seg of segs) {
241
- for (let col = seg.firstCol; col <= seg.lastCol; col++) {
242
- if (seg.firstCol !== col) {
243
- seg = Object.assign(Object.assign({}, seg), { firstCol: col, lastCol: col, isStart: false, isEnd: seg.isEnd && seg.lastCol === col, isStandin: true });
244
- }
245
- byCol[col].push(seg);
246
- }
247
- }
248
- return byCol;
149
+ function sliceSegForCol(seg, col) {
150
+ return Object.assign(Object.assign({}, seg), { start: col, end: col + 1, isStart: seg.isStart && seg.start === col, isEnd: seg.isEnd && seg.end - 1 === col, standinFor: seg });
249
151
  }
250
152
 
251
153
  const DEFAULT_TABLE_EVENT_TIME_FORMAT = createFormatter({
@@ -258,7 +160,7 @@ function hasListItemDisplay(seg) {
258
160
  let { display } = seg.eventRange.ui;
259
161
  return display === 'list-item' || (display === 'auto' &&
260
162
  !seg.eventRange.def.allDay &&
261
- seg.firstCol === seg.lastCol && // can't be multi-day
163
+ (seg.end - seg.start) === 1 && // single-day
262
164
  seg.isStart && // "
263
165
  seg.isEnd // "
264
166
  );
@@ -267,7 +169,7 @@ function hasListItemDisplay(seg) {
267
169
  class DayGridBlockEvent extends BaseComponent {
268
170
  render() {
269
171
  let { props } = this;
270
- return (createElement(StandardEvent, Object.assign({}, props, { elClasses: ['fc-daygrid-event', 'fc-daygrid-block-event', 'fc-h-event'], defaultTimeFormat: DEFAULT_TABLE_EVENT_TIME_FORMAT, defaultDisplayEventEnd: props.defaultDisplayEventEnd, disableResizing: !props.eventRange.def.allDay })));
172
+ return (createElement(StandardEvent, Object.assign({}, props, { className: 'fc-daygrid-block-event fc-daygrid-event fc-h-event', defaultTimeFormat: DEFAULT_TABLE_EVENT_TIME_FORMAT, defaultDisplayEventEnd: props.defaultDisplayEventEnd, disableResizing: !props.eventRange.def.allDay })));
271
173
  }
272
174
  }
273
175
 
@@ -277,8 +179,12 @@ class DayGridListEvent extends BaseComponent {
277
179
  let { options } = context;
278
180
  let { eventRange } = props;
279
181
  let timeFormat = options.eventTimeFormat || DEFAULT_TABLE_EVENT_TIME_FORMAT;
280
- let timeText = buildEventRangeTimeText(eventRange, timeFormat, context, true, props.defaultDisplayEventEnd);
281
- return (createElement(EventContainer, Object.assign({}, props, { elTag: "a", elClasses: ['fc-daygrid-event', 'fc-daygrid-dot-event'], elAttrs: getEventRangeAnchorAttrs(eventRange, context), defaultGenerator: renderInnerContent, timeText: timeText, isResizing: false, isDateSelecting: false })));
182
+ let timeText = buildEventRangeTimeText(timeFormat, eventRange,
183
+ /* slicedStart = */ undefined,
184
+ /* slicedEnd = */ undefined, props.isStart, props.isEnd, context,
185
+ /* defaultDisplayEventTime = */ true, props.defaultDisplayEventEnd);
186
+ let [tag, attrs] = getEventTagAndAttrs(eventRange, context);
187
+ return (createElement(EventContainer, Object.assign({}, props, { tag: tag, attrs: attrs, className: 'fc-daygrid-dot-event fc-daygrid-event', defaultGenerator: renderInnerContent, timeText: timeText, isResizing: false, isDateSelecting: false })));
282
188
  }
283
189
  }
284
190
  function renderInnerContent(renderProps) {
@@ -291,14 +197,16 @@ function renderInnerContent(renderProps) {
291
197
  class DayGridMoreLink extends BaseComponent {
292
198
  render() {
293
199
  let { props } = this;
294
- return (createElement(MoreLinkContainer, { elClasses: ['fc-daygrid-more-link'], dateProfile: props.dateProfile, todayRange: props.todayRange, allDayDate: props.allDayDate, segs: props.segs, hiddenSegs: props.hiddenSegs, alignmentElRef: props.alignmentElRef, alignGridTop: props.alignGridTop, extraDateSpan: props.extraDateSpan, popoverContent: () => {
200
+ return (createElement(MoreLinkContainer, { className: joinClassNames('fc-daygrid-more-link', props.isBlock
201
+ ? 'fc-daygrid-more-link-block'
202
+ : 'fc-daygrid-more-link-button'), dateProfile: props.dateProfile, todayRange: props.todayRange, allDayDate: props.allDayDate, segs: props.segs, hiddenSegs: props.hiddenSegs, alignElRef: props.alignElRef, alignParentTop: props.alignParentTop, dateSpanProps: props.dateSpanProps, popoverContent: () => {
295
203
  let forcedInvisibleMap = // TODO: more convenient/DRY
296
204
  (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
297
205
  (props.eventResize ? props.eventResize.affectedInstances : null) ||
298
206
  {};
299
207
  return (createElement(Fragment, null, props.segs.map((seg) => {
300
208
  let { eventRange } = seg;
301
- let instanceId = eventRange.instance.instanceId;
209
+ let { instanceId } = eventRange.instance;
302
210
  return (createElement("div", { key: instanceId, style: {
303
211
  visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '',
304
212
  } }, hasListItemDisplay(seg) ? (createElement(DayGridListEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getEventRangeMeta(eventRange, props.todayRange)))) : (createElement(DayGridBlockEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getEventRangeMeta(eventRange, props.todayRange))))));
@@ -311,8 +219,31 @@ class DayGridCell extends DateComponent {
311
219
  constructor() {
312
220
  super(...arguments);
313
221
  // ref
314
- this.innerElRef = createRef();
315
- this.headerWrapElRef = createRef();
222
+ this.rootElRef = createRef();
223
+ this.handleBodyEl = (bodyEl) => {
224
+ if (this.disconnectBodyHeight) {
225
+ this.disconnectBodyHeight();
226
+ this.disconnectBodyHeight = undefined;
227
+ setRef(this.props.headerHeightRef, null);
228
+ setRef(this.props.mainHeightRef, null);
229
+ }
230
+ if (bodyEl) {
231
+ // we want to fire on ANY size change, because we do more advanced stuff
232
+ this.disconnectBodyHeight = watchSize(bodyEl, (_bodyWidth, bodyHeight) => {
233
+ const { props } = this;
234
+ const mainRect = bodyEl.getBoundingClientRect();
235
+ const rootRect = this.rootElRef.current.getBoundingClientRect();
236
+ const headerHeight = mainRect.top - rootRect.top;
237
+ if (!isDimsEqual(this.headerHeight, headerHeight)) {
238
+ this.headerHeight = headerHeight;
239
+ setRef(props.headerHeightRef, headerHeight);
240
+ }
241
+ if (props.fgLiquidHeight) {
242
+ setRef(props.mainHeightRef, bodyHeight);
243
+ }
244
+ });
245
+ }
246
+ };
316
247
  }
317
248
  render() {
318
249
  let { props, context } = this;
@@ -320,48 +251,28 @@ class DayGridCell extends DateComponent {
320
251
  // TODO: memoize this
321
252
  const isMonthStart = props.showDayNumber &&
322
253
  shouldDisplayMonthStart(props.date, props.dateProfile.currentRange, dateEnv);
323
- return (createElement(DayCellContainer, { elTag: "div", elClasses: [
324
- 'fc-daygrid-cell',
325
- 'fc-cell',
326
- props.width != null ? '' : 'fc-liquid',
327
- 'fc-flex-column',
328
- ...(props.extraClassNames || []),
329
- ], elAttrs: Object.assign(Object.assign({}, props.extraDataAttrs), { role: 'gridcell' }), elStyle: {
254
+ // TODO: memoize this
255
+ const dateMeta = getDateMeta(props.date, props.todayRange, null, props.dateProfile);
256
+ const baseClassName = joinClassNames('fc-daygrid-day', props.borderStart && 'fc-border-s', props.width != null ? '' : 'fc-liquid', 'fc-flex-col');
257
+ if (dateMeta.isDisabled) {
258
+ return (createElement("div", { role: 'gridcell', "aria-disabled": true, className: joinClassNames(baseClassName, 'fc-day-disabled'), style: {
259
+ width: props.width
260
+ } }));
261
+ }
262
+ const hasDayNumber = props.showDayNumber || hasCustomDayCellContent(options);
263
+ const isNavLink = options.navLinks;
264
+ const fullDateStr = buildDateStr(context, props.date);
265
+ return (createElement(DayCellContainer, { tag: "div", className: joinClassNames(baseClassName, props.className), attrs: Object.assign(Object.assign({}, props.attrs), { role: 'gridcell', 'aria-label': fullDateStr }), style: {
330
266
  width: props.width
331
- }, extraRenderProps: props.extraRenderProps, defaultGenerator: renderTopInner, date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, isMonthStart: isMonthStart }, (InnerContent, renderProps) => (createElement("div", { ref: this.innerElRef, className: [
332
- 'fc-daygrid-cell-inner',
333
- props.fgLiquidHeight ? 'fc-liquid' : ''
334
- ].join(' ') },
335
- createElement("div", { ref: this.headerWrapElRef, className: "fc-flex-column" }, !renderProps.isDisabled && (props.showDayNumber || hasCustomDayCellContent(options)) && (createElement("div", { className: "fc-daygrid-cell-header" },
336
- createElement(InnerContent, { elTag: "a", elClasses: [
337
- 'fc-daygrid-cell-number',
338
- isMonthStart && 'fc-daygrid-month-start',
339
- ], elAttrs: buildNavLinkAttrs(context, props.date) })))),
340
- createElement("div", { className: "fc-daygrid-cell-main", style: {
341
- height: props.fgLiquidHeight ? '' : props.fgHeight
342
- } }, props.fg),
343
- createElement("div", { className: "fc-daygrid-cell-footer", style: props.fgLiquidHeight
344
- ? { position: 'relative', top: props.fgHeight }
345
- : {} },
346
- createElement(DayGridMoreLink, { allDayDate: props.date, segs: props.segs, hiddenSegs: props.hiddenSegs, alignmentElRef: this.innerElRef, alignGridTop: !props.showDayNumber, extraDateSpan: props.extraDateSpan, dateProfile: props.dateProfile, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange })),
347
- props.bg))));
348
- }
349
- componentDidMount() {
350
- const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
351
- const headerWrapEl = this.headerWrapElRef.current; // "
352
- // TODO: only attach this if refs props present
353
- this.detachInnerHeight = watchHeight(innerEl, (height) => {
354
- setRef(this.props.innerHeightRef, height);
355
- });
356
- this.detachHeaderHeight = watchHeight(headerWrapEl, (height) => {
357
- setRef(this.props.headerHeightRef, height);
358
- });
359
- }
360
- componentWillUnmount() {
361
- this.detachInnerHeight();
362
- this.detachHeaderHeight();
363
- setRef(this.props.innerHeightRef, null);
364
- setRef(this.props.headerHeightRef, null);
267
+ }, elRef: this.rootElRef, renderProps: props.renderProps, defaultGenerator: renderTopInner, date: props.date, dateMeta: dateMeta, showDayNumber: props.showDayNumber, isMonthStart: isMonthStart }, (InnerContent) => (createElement(Fragment, null,
268
+ hasDayNumber && (createElement("div", { className: "fc-daygrid-day-header" },
269
+ createElement(InnerContent, { tag: 'div', attrs: isNavLink
270
+ ? buildNavLinkAttrs(context, props.date, undefined, fullDateStr)
271
+ : { 'aria-hidden': true } // label already on cell
272
+ , className: joinClassNames('fc-daygrid-day-number', isMonthStart && 'fc-daygrid-month-start') }))),
273
+ createElement("div", { className: joinClassNames('fc-daygrid-day-body', props.isTall && 'fc-daygrid-day-body-tall', props.fgLiquidHeight ? 'fc-liquid' : 'fc-grow'), ref: this.handleBodyEl },
274
+ createElement("div", { className: 'fc-daygrid-day-events', style: { height: props.fgHeight } }, props.fg),
275
+ createElement(DayGridMoreLink, { isBlock: props.isCompact, allDayDate: props.date, segs: props.segs, hiddenSegs: props.hiddenSegs, alignElRef: this.rootElRef, alignParentTop: props.showDayNumber ? '[role=row]' : '.fc-view', dateSpanProps: props.dateSpanProps, dateProfile: props.dateProfile, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange }))))));
365
276
  }
366
277
  }
367
278
  // Utils
@@ -385,121 +296,232 @@ function shouldDisplayMonthStart(date, currentRange, dateEnv) {
385
296
  (dateEnv.getDay(date) === 1 && date.valueOf() < currentEnd.valueOf()));
386
297
  }
387
298
 
299
+ function computeFgSegVerticals(segs, segHeightMap, cells, maxHeight, strictOrder, allowSlicing = true, dayMaxEvents, dayMaxEventRows) {
300
+ let maxCoord;
301
+ let maxDepth;
302
+ let hiddenConsumes;
303
+ if (dayMaxEvents === true || dayMaxEventRows === true) {
304
+ maxCoord = maxHeight;
305
+ hiddenConsumes = true;
306
+ }
307
+ else if (typeof dayMaxEvents === 'number') {
308
+ maxDepth = dayMaxEvents;
309
+ hiddenConsumes = false;
310
+ }
311
+ else if (typeof dayMaxEventRows === 'number') {
312
+ maxDepth = dayMaxEventRows;
313
+ hiddenConsumes = true;
314
+ }
315
+ // NOTE: visibleSegsMap and hiddenSegMap map NEVER overlap for a given event
316
+ // once a seg has a height, the combined potentially-sliced segs will comprise the entire span of the seg
317
+ // if a seg does not have a height yet, it won't be inserted into either visibleSegsMap/hiddenSegMap
318
+ const visibleSegMap = new Map();
319
+ const hiddenSegMap = new Map();
320
+ const segTops = new Map();
321
+ const isSlicedMap = new Map();
322
+ let hierarchy = new SegHierarchy(segs, (seg) => segHeightMap.get(getEventPartKey(seg)), strictOrder, maxCoord, maxDepth, hiddenConsumes, allowSlicing);
323
+ hierarchy.traverseSegs((seg, segTop) => {
324
+ addToSegMap(visibleSegMap, seg);
325
+ segTops.set(getEventPartKey(seg), segTop);
326
+ if (seg.isSlice) {
327
+ isSlicedMap.set(seg.eventRange, true);
328
+ }
329
+ });
330
+ for (const hiddenSeg of hierarchy.hiddenSegs) {
331
+ addToSegMap(hiddenSegMap, hiddenSeg); // hidden main segs
332
+ }
333
+ // recompute tops while considering slices
334
+ // portions of these slices might be added to hiddenSegMap
335
+ if (isSlicedMap.size) {
336
+ segTops.clear();
337
+ hierarchy = new SegHierarchy(compileSegMap(segs, visibleSegMap), (seg) => segHeightMap.get(getEventPartKey(seg)), strictOrder, maxCoord, maxDepth, hiddenConsumes);
338
+ hierarchy.traverseSegs((seg, segTop) => {
339
+ segTops.set(getEventPartKey(seg), segTop); // newly-hidden main segs and slices
340
+ });
341
+ for (const hiddenSeg of hierarchy.hiddenSegs) {
342
+ addToSegMap(hiddenSegMap, hiddenSeg);
343
+ }
344
+ }
345
+ const segsByCol = [];
346
+ const hiddenSegsByCol = [];
347
+ const renderableSegsByCol = [];
348
+ const heightsByCol = [];
349
+ for (let col = 0; col < cells.length; col++) {
350
+ segsByCol.push([]);
351
+ hiddenSegsByCol.push([]);
352
+ renderableSegsByCol.push([]);
353
+ heightsByCol.push(0);
354
+ }
355
+ for (const seg of segs) {
356
+ const { eventRange } = seg;
357
+ const visibleSegs = visibleSegMap.get(eventRange) || [];
358
+ const hiddenSegs = hiddenSegMap.get(eventRange) || [];
359
+ const isSliced = isSlicedMap.get(eventRange) || false;
360
+ // add orig to renderable
361
+ renderableSegsByCol[seg.start].push(seg);
362
+ // add slices to renderable
363
+ if (isSliced) {
364
+ for (const visibleSeg of visibleSegs) {
365
+ renderableSegsByCol[visibleSeg.start].push(visibleSeg);
366
+ }
367
+ }
368
+ // accumulate segsByCol/heightsByCol for visible segs
369
+ for (const visibleSeg of visibleSegs) {
370
+ for (let col = visibleSeg.start; col < visibleSeg.end; col++) {
371
+ const slice = sliceSegForCol(visibleSeg, col);
372
+ segsByCol[col].push(slice);
373
+ }
374
+ const segKey = getEventPartKey(visibleSeg);
375
+ const segTop = segTops.get(segKey);
376
+ if (segTop != null) { // positioned?
377
+ const segHeight = segHeightMap.get(segKey);
378
+ for (let col = visibleSeg.start; col < visibleSeg.end; col++) {
379
+ heightsByCol[col] = Math.max(heightsByCol[col], segTop + segHeight);
380
+ }
381
+ }
382
+ }
383
+ // accumulate segsByCol/hiddenSegsByCol for hidden segs
384
+ for (const hiddenSeg of hiddenSegs) {
385
+ for (let col = hiddenSeg.start; col < hiddenSeg.end; col++) {
386
+ const slice = sliceSegForCol(hiddenSeg, col);
387
+ segsByCol[col].push(slice);
388
+ hiddenSegsByCol[col].push(slice);
389
+ }
390
+ }
391
+ }
392
+ return [
393
+ segsByCol,
394
+ hiddenSegsByCol,
395
+ renderableSegsByCol,
396
+ segTops,
397
+ heightsByCol,
398
+ ];
399
+ }
400
+ // Utils
401
+ // -------------------------------------------------------------------------------------------------
402
+ function addToSegMap(map, seg) {
403
+ let list = map.get(seg.eventRange);
404
+ if (!list) {
405
+ map.set(seg.eventRange, list = []);
406
+ }
407
+ list.push(seg);
408
+ }
388
409
  /*
389
- Unique per-START-column, good for cataloging by top
410
+ Ensures relative order of DayRowEventRange stays consistent with segs
390
411
  */
391
- function getSegStartId(seg) {
392
- return seg.eventRange.instance.instanceId + ':' + seg.firstCol;
412
+ function compileSegMap(segs, segMap) {
413
+ const res = [];
414
+ for (const seg of segs) {
415
+ res.push(...(segMap.get(seg.eventRange) || []));
416
+ }
417
+ return res;
393
418
  }
419
+
394
420
  /*
395
- Unique per-START-and-END-column, good for cataloging by width/height
421
+ TODO: move this so @fullcalendar/daygrid
396
422
  */
397
- function getSegSpanId(seg) {
398
- return getSegStartId(seg) + ':' + seg.lastCol;
423
+ function buildDayTableModel(dateProfile, dateProfileGenerator) {
424
+ let daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator);
425
+ return new DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
399
426
  }
400
- function computeFgSegVerticals(segs, segHeightMap, // keyed by segSpanId
401
- cells, topOrigin, maxHeight, strictOrder, dayMaxEvents, dayMaxEventRows) {
402
- // initialize column-based arrays
403
- const colCnt = cells.length;
404
- const hiddenSegsByCol = [];
405
- const heightsByCol = [];
406
- for (let col = 0; col < colCnt; col++) {
407
- hiddenSegsByCol.push([]);
408
- heightsByCol.push(0);
409
- }
410
- // create entries to be given to DayGridSegHierarchy
411
- const segEntries = segs.map((seg, index) => ({
412
- index: index,
413
- seg,
414
- span: {
415
- start: seg.firstCol,
416
- end: seg.lastCol + 1,
417
- },
418
- }));
419
- // configure hierarchy position-generator
420
- let hierarchy = new DayGridSegHierarchy((segEntry) => (segHeightMap.get(getSegSpanId(segs[segEntry.index]))));
421
- hierarchy.allowReslicing = false;
422
- hierarchy.strictOrder = strictOrder;
423
- if (dayMaxEvents === true || dayMaxEventRows === true) {
424
- hierarchy.maxCoord = maxHeight;
425
- hierarchy.hiddenConsumes = true;
427
+ function computeColWidth(colCnt, colMinWidth, viewportWidth) {
428
+ if (viewportWidth == null) {
429
+ return [undefined, undefined];
426
430
  }
427
- else if (typeof dayMaxEvents === 'number') {
428
- hierarchy.maxStackCnt = dayMaxEvents;
431
+ const colTempWidth = viewportWidth / colCnt;
432
+ if (colTempWidth < colMinWidth) {
433
+ return [colMinWidth * colCnt, colMinWidth];
429
434
  }
430
- else if (typeof dayMaxEventRows === 'number') {
431
- hierarchy.maxStackCnt = dayMaxEventRows;
432
- hierarchy.hiddenConsumes = true;
433
- }
434
- // compile segTops & heightsByCol
435
- const hiddenSegEntries = hierarchy.addSegs(segEntries);
436
- const segRects = hierarchy.toRects();
437
- const segTops = {};
438
- for (const segRect of segRects) {
439
- const seg = segs[segRect.index];
440
- segTops[getSegStartId(seg)] = topOrigin + segRect.levelCoord;
441
- let { start: col, end: endCol } = segRect.span;
442
- for (; col < endCol; col++) {
443
- heightsByCol[col] = Math.max(heightsByCol[col], segRect.levelCoord + segRect.thickness);
435
+ return [viewportWidth, undefined];
436
+ }
437
+ // Positioning
438
+ // -------------------------------------------------------------------------------------------------
439
+ function computeTopFromDate(date, cellRows, rowHeightMap, adjust = 0) {
440
+ let top = 0;
441
+ for (const cells of cellRows) {
442
+ const start = cells[0].date;
443
+ const end = cells[cells.length - 1].date;
444
+ const key = start.toISOString();
445
+ if (date >= start && date <= end) {
446
+ return top;
447
+ }
448
+ const rowHeight = rowHeightMap.get(key);
449
+ if (rowHeight == null) {
450
+ return; // denote unknown
444
451
  }
452
+ top += rowHeight + adjust;
445
453
  }
446
- // compile # of invisible segs per-column
447
- for (const hiddenSegEntry of hiddenSegEntries) {
448
- const { span } = hiddenSegEntry;
449
- const hiddenSeg = segs[hiddenSegEntry.index];
450
- for (let col = span.start; col < span.end; col++) {
451
- hiddenSegsByCol[col].push(hiddenSeg);
454
+ return top;
455
+ }
456
+ /*
457
+ FYI, `width` is not dependable for aligning completely to farside
458
+ */
459
+ function computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl) {
460
+ let fromStart;
461
+ let fromEnd;
462
+ if (colWidth != null) {
463
+ fromStart = seg.start * colWidth;
464
+ fromEnd = (colCnt - seg.end) * colWidth;
465
+ }
466
+ else {
467
+ const colWidthFrac = 1 / colCnt;
468
+ fromStart = fracToCssDim(seg.start * colWidthFrac);
469
+ fromEnd = fracToCssDim(1 - seg.end * colWidthFrac);
470
+ }
471
+ if (isRtl) {
472
+ return { right: fromStart, left: fromEnd };
473
+ }
474
+ else {
475
+ return { left: fromStart, right: fromEnd };
476
+ }
477
+ }
478
+ function computeColFromPosition(positionLeft, elWidth, colWidth, colCnt, isRtl) {
479
+ const realColWidth = colWidth != null ? colWidth : elWidth / colCnt;
480
+ const colFromLeft = Math.floor(positionLeft / realColWidth);
481
+ const col = isRtl ? (colCnt - colFromLeft - 1) : colFromLeft;
482
+ const left = colFromLeft * realColWidth;
483
+ const right = left + realColWidth;
484
+ return { col, left, right };
485
+ }
486
+ function computeRowFromPosition(positionTop, cellRows, rowHeightMap) {
487
+ let row = 0;
488
+ let top = 0;
489
+ let bottom = 0;
490
+ for (const cells of cellRows) {
491
+ const key = cells[0].key;
492
+ top = bottom;
493
+ bottom = top + rowHeightMap.get(key);
494
+ if (positionTop < bottom) {
495
+ break;
452
496
  }
497
+ row++;
453
498
  }
454
- return [segTops, heightsByCol, hiddenSegsByCol];
499
+ return { row, top, bottom };
455
500
  }
456
- // DayGridSegHierarchy
501
+ // Hit Element
457
502
  // -------------------------------------------------------------------------------------------------
458
- class DayGridSegHierarchy extends SegHierarchy {
459
- constructor() {
460
- super(...arguments);
461
- // config
462
- this.hiddenConsumes = false;
463
- // allows us to keep hidden entries in the hierarchy so they take up space
464
- this.forceHidden = {};
465
- }
466
- addSegs(segInputs) {
467
- const hiddenSegs = super.addSegs(segInputs);
468
- const { entriesByLevel } = this;
469
- const excludeHidden = (entry) => !this.forceHidden[buildEntryKey(entry)];
470
- // remove the forced-hidden segs
471
- for (let level = 0; level < entriesByLevel.length; level += 1) {
472
- entriesByLevel[level] = entriesByLevel[level].filter(excludeHidden);
473
- }
474
- return hiddenSegs;
475
- }
476
- handleInvalidInsertion(insertion, entry, hiddenEntries) {
477
- const { entriesByLevel, forceHidden } = this;
478
- const { touchingEntry, touchingLevel, touchingLateral } = insertion;
479
- // the entry that the new insertion is touching must be hidden
480
- if (this.hiddenConsumes && touchingEntry) {
481
- const touchingEntryId = buildEntryKey(touchingEntry);
482
- if (!forceHidden[touchingEntryId]) {
483
- if (this.allowReslicing) {
484
- // split up the touchingEntry, reinsert it
485
- const hiddenEntry = Object.assign(Object.assign({}, touchingEntry), { span: intersectSpans(touchingEntry.span, entry.span) });
486
- // reinsert the area that turned into a "more" link (so no other entries try to
487
- // occupy the space) but mark it forced-hidden
488
- const hiddenEntryId = buildEntryKey(hiddenEntry);
489
- forceHidden[hiddenEntryId] = true;
490
- entriesByLevel[touchingLevel][touchingLateral] = hiddenEntry;
491
- hiddenEntries.push(hiddenEntry);
492
- this.splitEntry(touchingEntry, entry, hiddenEntries);
493
- }
494
- else {
495
- forceHidden[touchingEntryId] = true;
496
- hiddenEntries.push(touchingEntry);
497
- }
498
- }
499
- }
500
- // will try to reslice...
501
- super.handleInvalidInsertion(insertion, entry, hiddenEntries);
503
+ function getRowEl(rootEl, row) {
504
+ return rootEl.querySelectorAll(':scope > [role=row]')[row];
505
+ }
506
+ function getCellEl(rowEl, col) {
507
+ return rowEl.querySelectorAll(':scope > [role=gridcell]')[col];
508
+ }
509
+ // Header Formatting
510
+ // -------------------------------------------------------------------------------------------------
511
+ function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) {
512
+ return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt);
513
+ }
514
+ // Computes a default column header formatting string if `colFormat` is not explicitly defined
515
+ function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
516
+ // if more than one week row, or if there are a lot of columns with not much space,
517
+ // put just the day numbers will be in each cell
518
+ if (!datesRepDistinctDays || dayCnt > 10) {
519
+ return createFormatter({ weekday: 'short' }); // "Sat"
520
+ }
521
+ if (dayCnt > 1) {
522
+ return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12"
502
523
  }
524
+ return createFormatter({ weekday: 'long' }); // "Saturday"
503
525
  }
504
526
 
505
527
  class DayGridEventHarness extends Component {
@@ -510,129 +532,102 @@ class DayGridEventHarness extends Component {
510
532
  }
511
533
  render() {
512
534
  const { props } = this;
513
- return (createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
535
+ return (createElement("div", { className: joinClassNames(props.className, 'fc-abs'), style: props.style, ref: this.rootElRef }, props.children));
514
536
  }
515
537
  componentDidMount() {
516
538
  const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
517
- this.detachHeight = watchHeight(rootEl, (height) => {
539
+ this.disconnectHeight = watchHeight(rootEl, (height) => {
518
540
  setRef(this.props.heightRef, height);
519
541
  });
520
542
  }
521
543
  componentWillUnmount() {
522
- this.detachHeight();
544
+ this.disconnectHeight();
523
545
  setRef(this.props.heightRef, null);
524
546
  }
525
547
  }
526
548
 
527
549
  const DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' });
528
- const COMPACT_CELL_WIDTH = 80;
529
550
  class DayGridRow extends BaseComponent {
530
551
  constructor() {
531
552
  super(...arguments);
532
- this.cellInnerHeightRefMap = new RefMap(() => {
533
- afterSize(this.handleInnerHeights);
553
+ this.headerHeightRefMap = new RefMap(() => {
554
+ afterSize(this.handleSegPositioning);
534
555
  });
535
- this.cellHeaderHeightRefMap = new RefMap(() => {
536
- afterSize(this.handleHeaderHeights);
556
+ this.mainHeightRefMap = new RefMap(() => {
557
+ const fgLiquidHeight = this.props.dayMaxEvents === true || this.props.dayMaxEventRows === true;
558
+ if (fgLiquidHeight) {
559
+ afterSize(this.handleSegPositioning);
560
+ }
537
561
  });
538
562
  this.segHeightRefMap = new RefMap(() => {
539
- afterSize(this.handleSegHeights);
563
+ afterSize(this.handleSegPositioning);
540
564
  });
541
565
  this.handleRootEl = (rootEl) => {
542
566
  this.rootEl = rootEl;
543
567
  setRef(this.props.rootElRef, rootEl);
544
568
  };
545
- // Sizing
546
- // -----------------------------------------------------------------------------------------------
547
- this.handleHeaderHeights = () => {
548
- const cellHeaderHeightMap = this.cellHeaderHeightRefMap.current;
549
- let max = 0;
550
- for (const height of cellHeaderHeightMap.values()) {
551
- max = Math.max(max, height);
552
- }
553
- if (this.state.headerHeight !== max) {
554
- this.setState({ headerHeight: max });
555
- }
556
- };
557
- this.handleInnerHeights = () => {
558
- const { props } = this;
559
- const fgLiquidHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
560
- const cellInnerHeightMap = this.cellInnerHeightRefMap.current;
561
- let max = 0;
562
- for (const height of cellInnerHeightMap.values()) {
563
- max = Math.max(max, height);
564
- }
565
- if (fgLiquidHeight) {
566
- if (this.state.innerHeight !== max) {
567
- this.setState({ innerHeight: max }); // will trigger event rerender
568
- }
569
- }
570
- else {
571
- setRef(props.innerHeightRef, max);
572
- }
573
- };
574
- this.handleSegHeights = () => {
575
- this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger event rerender
569
+ this.handleSegPositioning = () => {
570
+ this.forceUpdate();
576
571
  };
577
572
  }
578
573
  render() {
579
- const { props, state, context, cellInnerHeightRefMap, cellHeaderHeightRefMap } = this;
574
+ const { props, context, headerHeightRefMap, mainHeightRefMap } = this;
580
575
  const { cells } = props;
581
576
  const { options } = context;
582
577
  const weekDate = props.cells[0].date;
583
- const colCnt = props.cells.length;
584
578
  const fgLiquidHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
585
579
  // TODO: memoize? sort all types of segs?
586
580
  const fgEventSegs = sortEventSegs(props.fgEventSegs, options.eventOrder);
587
581
  // TODO: memoize?
588
- const fgEventSegsByCol = splitSegsByCol(fgEventSegs, colCnt);
589
- const bgEventSegsByCol = splitSegsByCol(props.bgEventSegs, colCnt);
590
- const businessHoursByCol = splitSegsByCol(props.businessHourSegs, colCnt);
591
- const highlightSegsByCol = splitSegsByCol(this.getHighlightSegs(), colCnt); // TODO: doesn't need standins
592
- const mirrorSegsByCol = splitSegsByCol(this.getMirrorSegs(), colCnt); // TODO: doesn't need standins
593
- // TODO: memoize?
594
- const [segTops, heightsByCol, hiddenSegsByCol] = computeFgSegVerticals(fgEventSegs, this.segHeightRefMap.current, cells, state.headerHeight, (fgLiquidHeight && state.innerHeight != null && state.headerHeight != null)
595
- ? state.innerHeight - state.headerHeight
596
- : undefined, options.eventOrderStrict, props.dayMaxEvents, props.dayMaxEventRows);
582
+ const [maxMainTop, minMainHeight] = this.computeFgDims(); // uses headerHeightRefMap/mainHeightRefMap
583
+ const [segsByCol, hiddenSegsByCol, renderableSegsByCol, segTops, simpleHeightsByCol] = computeFgSegVerticals(fgEventSegs, this.segHeightRefMap.current, cells, fgLiquidHeight ? minMainHeight : undefined, // if not defined in first run, will unlimited!?
584
+ options.eventOrderStrict, options.eventSlicing, props.dayMaxEvents, props.dayMaxEventRows);
585
+ const heightsByCol = [];
586
+ if (maxMainTop != null) {
587
+ let col = 0;
588
+ for (const cell of cells) { // uses headerHeightRefMap/maxMainTop/simpleHeightsByCol
589
+ const cellHeaderHeight = headerHeightRefMap.current.get(cell.key);
590
+ const extraFgHeight = maxMainTop - cellHeaderHeight;
591
+ heightsByCol.push(simpleHeightsByCol[col++] + extraFgHeight);
592
+ }
593
+ }
594
+ const highlightSegs = this.getHighlightSegs();
595
+ const mirrorSegs = this.getMirrorSegs();
597
596
  const forcedInvisibleMap = // TODO: more convenient/DRY
598
597
  (props.eventDrag && props.eventDrag.affectedInstances) ||
599
598
  (props.eventResize && props.eventResize.affectedInstances) ||
600
599
  {};
601
- return (createElement("div", { role: props.cellGroup ? undefined : 'row', className: [
602
- 'fc-daygrid-row',
603
- props.forceVSpacing
604
- ? 'fc-daygrid-row-spacious'
605
- : props.compact
606
- ? 'fc-daygrid-row-compact'
607
- : '',
608
- props.cellGroup ? 'fc-flex-row' : 'fc-row',
609
- 'fc-rel',
610
- props.className || '',
611
- ].join(' '), style: {
600
+ const isNavLink = options.navLinks;
601
+ const fullWeekStr = buildDateStr(context, weekDate, 'week');
602
+ return (createElement("div", { role: props.role /* !!! */, "aria-label": props.role === 'row' // HACK
603
+ ? fullWeekStr
604
+ : undefined // can't have label on non-role div
605
+ , className: joinClassNames('fc-flex-row fc-rel', props.className), style: {
612
606
  minHeight: props.minHeight,
613
607
  }, ref: this.handleRootEl },
614
- props.cells.map((cell, col) => {
615
- const normalFgNodes = this.renderFgSegs(fgEventSegsByCol[col], segTops, props.todayRange, forcedInvisibleMap);
616
- const mirrorFgNodes = this.renderFgSegs(mirrorSegsByCol[col], segTops, props.todayRange, {}, // forcedInvisibleMap
617
- Boolean(props.eventDrag), Boolean(props.eventResize), false);
618
- return (createElement(DayGridCell, { key: cell.key, dateProfile: props.dateProfile, todayRange: props.todayRange, date: cell.date, showDayNumber: props.showDayNumbers,
608
+ this.renderFillSegs(props.businessHourSegs, 'non-business'),
609
+ this.renderFillSegs(props.bgEventSegs, 'bg-event'),
610
+ this.renderFillSegs(highlightSegs, 'highlight'),
611
+ createElement("div", { className: 'fc-flex-row fc-liquid fc-rel' }, props.cells.map((cell, col) => {
612
+ const normalFgNodes = this.renderFgSegs(maxMainTop, renderableSegsByCol[col], segTops, props.todayRange, forcedInvisibleMap);
613
+ return (createElement(DayGridCell, { key: cell.key, dateProfile: props.dateProfile, todayRange: props.todayRange, date: cell.date, showDayNumber: props.showDayNumbers, isCompact: props.isCompact, isTall: props.isTall, borderStart: Boolean(col),
619
614
  // content
620
- segs: fgEventSegsByCol[col], hiddenSegs: hiddenSegsByCol[col], fgLiquidHeight: fgLiquidHeight, fg: (createElement(Fragment, null,
621
- createElement(Fragment, null, normalFgNodes),
622
- createElement(Fragment, null, mirrorFgNodes))), bg: (createElement(Fragment, null,
623
- this.renderFillSegs(highlightSegsByCol[col], 'highlight'),
624
- this.renderFillSegs(businessHoursByCol[col], 'non-business'),
625
- this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))), eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
615
+ segs: segsByCol[col], hiddenSegs: hiddenSegsByCol[col], fgLiquidHeight: fgLiquidHeight, fg: (createElement(Fragment, null, normalFgNodes)), eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
626
616
  // render hooks
627
- extraRenderProps: cell.extraRenderProps, extraDateSpan: cell.extraDateSpan, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames,
617
+ renderProps: cell.renderProps, dateSpanProps: cell.dateSpanProps, attrs: cell.attrs, className: cell.className,
628
618
  // dimensions
629
619
  fgHeight: heightsByCol[col], width: props.colWidth,
630
620
  // refs
631
- innerHeightRef: cellInnerHeightRefMap.createRef(cell.key), headerHeightRef: cellHeaderHeightRefMap.createRef(cell.key) }));
632
- }),
633
- props.showWeekNumbers && (createElement(WeekNumberContainer, { elTag: "a", elClasses: ['fc-daygrid-week-number'], elAttrs: buildNavLinkAttrs(context, weekDate, 'week'), date: weekDate, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }))));
634
- }
635
- renderFgSegs(segs, segTops, todayRange, forcedInvisibleMap, isDragging, isResizing, isDateSelecting) {
621
+ headerHeightRef: headerHeightRefMap.createRef(cell.key), mainHeightRef: mainHeightRefMap.createRef(cell.key) }));
622
+ })),
623
+ props.showWeekNumbers && (createElement(WeekNumberContainer, { tag: 'div', attrs: Object.assign(Object.assign({}, (isNavLink
624
+ ? buildNavLinkAttrs(context, weekDate, 'week', fullWeekStr, /* isTabbable = */ false)
625
+ : {})), { 'role': undefined, 'aria-hidden': true }), className: 'fc-daygrid-week-number', date: weekDate, defaultFormat: DEFAULT_WEEK_NUM_FORMAT })),
626
+ this.renderFgSegs(maxMainTop, mirrorSegs, segTops, props.todayRange, {}, // forcedInvisibleMap
627
+ Boolean(props.eventDrag), Boolean(props.eventResize), false)));
628
+ }
629
+ renderFgSegs(headerHeight, segs, segTops, todayRange, forcedInvisibleMap, isDragging, isResizing, isDateSelecting) {
630
+ var _a;
636
631
  const { props, context, segHeightRefMap } = this;
637
632
  const { isRtl } = context;
638
633
  const { colWidth, eventSelection } = props;
@@ -641,30 +636,26 @@ class DayGridRow extends BaseComponent {
641
636
  const isMirror = isDragging || isResizing || isDateSelecting;
642
637
  const nodes = [];
643
638
  for (const seg of segs) {
644
- const { left, right, width } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
645
- // TODO: optimize ID creation? all related
646
- const { eventRange } = seg;
639
+ const key = getEventPartKey(seg);
640
+ const { standinFor, eventRange } = seg;
647
641
  const { instanceId } = eventRange.instance;
648
- const segSpanId = getSegSpanId(seg);
649
- const segStartId = getSegStartId(seg);
650
- const top = segTops[segStartId];
651
- const isVisible = !seg.isStandin &&
652
- top != null &&
653
- !forcedInvisibleMap[instanceId];
654
- /*
655
- TODO: is this comment still relevant? vvvvvvvv
656
- known bug: events that are force to be list-item but span multiple days still take up space in later columns
657
- todo: in print view, for multi-day events, don't display title within non-start/end segs
658
- */
659
- nodes.push(createElement(DayGridEventHarness, { key: segSpanId, style: {
660
- visibility: isVisible ? '' : 'hidden',
642
+ if (standinFor) {
643
+ continue;
644
+ }
645
+ const { left, right } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
646
+ const localTop = (_a = segTops.get(standinFor ? getEventPartKey(standinFor) : key)) !== null && _a !== void 0 ? _a : (isMirror ? 0 : undefined);
647
+ const top = headerHeight != null && localTop != null
648
+ ? headerHeight + localTop
649
+ : undefined;
650
+ const isInvisible = standinFor || forcedInvisibleMap[instanceId] || top == null;
651
+ nodes.push(createElement(DayGridEventHarness, { key: key, className: seg.start ? 'fc-border-transparent fc-border-s' : '', style: {
652
+ visibility: isInvisible ? 'hidden' : '',
661
653
  top,
662
654
  left,
663
655
  right,
664
- width,
665
- }, heightRef: (isMirror || seg.isStandin)
666
- ? null
667
- : segHeightRefMap.createRef(segSpanId) }, hasListItemDisplay(seg) ? (createElement(DayGridListEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getEventRangeMeta(eventRange, todayRange)))) : (createElement(DayGridBlockEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getEventRangeMeta(eventRange, todayRange))))));
656
+ }, heightRef: (!standinFor && !isMirror)
657
+ ? segHeightRefMap.createRef(key)
658
+ : null }, hasListItemDisplay(seg) ? (createElement(DayGridListEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getEventRangeMeta(eventRange, todayRange)))) : (createElement(DayGridBlockEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getEventRangeMeta(eventRange, todayRange))))));
668
659
  }
669
660
  return nodes;
670
661
  }
@@ -675,17 +666,17 @@ class DayGridRow extends BaseComponent {
675
666
  const colCnt = props.cells.length;
676
667
  const nodes = [];
677
668
  for (const seg of segs) {
678
- const { left, right, width } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
679
- const isVisible = !seg.isStandin;
680
- nodes.push(createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: {
669
+ const key = buildEventRangeKey(seg.eventRange); // TODO: use different type of key than fg!?
670
+ const { left, right } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
671
+ const isVisible = !seg.standinFor;
672
+ nodes.push(createElement("div", { key: key, className: "fc-fill-y", style: {
681
673
  visibility: isVisible ? '' : 'hidden',
682
674
  left,
683
675
  right,
684
- width,
685
676
  } }, fillType === 'bg-event' ?
686
677
  createElement(BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, getEventRangeMeta(seg.eventRange, todayRange))) : (renderFill(fillType))));
687
678
  }
688
- return createElement(Fragment, {}, ...nodes);
679
+ return createElement(Fragment, {}, ...nodes); // TODO: shouldn't this be an array, so keyed?
689
680
  }
690
681
  // Sizing
691
682
  // -----------------------------------------------------------------------------------------------
@@ -698,9 +689,36 @@ class DayGridRow extends BaseComponent {
698
689
  componentWillUnmount() {
699
690
  this.disconnectHeight();
700
691
  setRef(this.props.heightRef, null);
701
- setRef(this.props.innerHeightRef, null);
702
692
  }
703
- // Utils
693
+ computeFgDims() {
694
+ const { cells } = this.props;
695
+ const headerHeightMap = this.headerHeightRefMap.current;
696
+ const mainHeightMap = this.mainHeightRefMap.current;
697
+ let maxMainTop;
698
+ let minMainBottom;
699
+ for (const cell of cells) {
700
+ const mainTop = headerHeightMap.get(cell.key);
701
+ const mainHeight = mainHeightMap.get(cell.key);
702
+ if (mainTop != null) {
703
+ if (maxMainTop === undefined || mainTop > maxMainTop) {
704
+ maxMainTop = mainTop;
705
+ }
706
+ if (mainHeight != null) {
707
+ const mainBottom = mainTop + mainHeight;
708
+ if (minMainBottom === undefined || mainBottom < minMainBottom) {
709
+ minMainBottom = mainBottom;
710
+ }
711
+ }
712
+ }
713
+ }
714
+ return [
715
+ maxMainTop,
716
+ minMainBottom != null && maxMainTop != null
717
+ ? minMainBottom - maxMainTop
718
+ : undefined,
719
+ ];
720
+ }
721
+ // Internal Utils
704
722
  // -----------------------------------------------------------------------------------------------
705
723
  getMirrorSegs() {
706
724
  let { props } = this;
@@ -753,7 +771,7 @@ class DayGridRows extends DateComponent {
753
771
  };
754
772
  }
755
773
  render() {
756
- let { props, state, context, rowHeightRefMap } = this;
774
+ let { props, context, rowHeightRefMap } = this;
757
775
  let { options } = context;
758
776
  let rowCnt = props.cellRows.length;
759
777
  let fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, rowCnt);
@@ -762,31 +780,31 @@ class DayGridRows extends DateComponent {
762
780
  let dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt);
763
781
  let eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt);
764
782
  let eventResizeByRow = this.splitEventResize(props.eventResize, rowCnt);
765
- // whether the ROW should expand in height
766
- // (not to be confused with whether the fg events within the row should be molded by height of row)
767
783
  let isHeightAuto = getIsHeightAuto(options);
768
- // maintain at least aspectRatio for cells?
769
- let rowMinHeight = (state.width != null && (rowCnt >= 7 || // TODO: better way to infer if across single-month boundary
770
- isHeightAuto)) ? state.width / context.options.aspectRatio / 6 // okay to hardcode 6 (weeks) ?
771
- : null;
772
- return (createElement("div", { className: 'fc-grow fc-flex-column', style: { width: props.width }, ref: this.handleRootEl }, props.cellRows.map((cells, row) => (createElement(DayGridRow, { key: cells[0].key, dateProfile: props.dateProfile, todayRange: props.todayRange, cells: cells, showDayNumbers: rowCnt > 1, showWeekNumbers: options.weekNumbers, forPrint: props.forPrint, compact: state.width != null && (state.width / cells.length) < COMPACT_CELL_WIDTH,
784
+ let rowHeightsRedistribute = !props.forPrint && !isHeightAuto;
785
+ let [rowMinHeight, isCompact] = computeRowHeight(props.visibleWidth, rowCnt, isHeightAuto, props.forPrint, options);
786
+ return (createElement("div", { role: 'rowgroup', className: joinClassNames(
787
+ // HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
788
+ // https://stackoverflow.com/a/60256345
789
+ !props.forPrint && 'fc-flex-col', props.className), style: { width: props.width }, ref: this.handleRootEl }, props.cellRows.map((cells, row) => (createElement(DayGridRow, { key: cells[0].key, role: 'row', dateProfile: props.dateProfile, todayRange: props.todayRange, cells: cells, showDayNumbers: rowCnt > 1, showWeekNumbers: rowCnt > 1 && options.weekNumbers, forPrint: props.forPrint, isCompact: isCompact,
773
790
  // if not auto-height, distribute height of container somewhat evently to rows
774
791
  // (treat all as zero, distribute height, then ensure min-heights -- the inner content height)
775
- className: isHeightAuto ? '' : 'fc-grow fc-basis0',
792
+ className: joinClassNames(rowHeightsRedistribute && 'fc-grow fc-basis0', rowCnt > 1 && 'fc-break-inside-avoid', // don't avoid breaks for single tall row
793
+ row < rowCnt - 1 && 'fc-border-b'),
776
794
  // content
777
- fgEventSegs: fgEventSegsByRow[row], bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* HACK */, businessHourSegs: businessHourSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventSelection: props.eventSelection, eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows,
795
+ fgEventSegs: fgEventSegsByRow[row], bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* HACK */, businessHourSegs: businessHourSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventSelection: props.eventSelection, eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows,
778
796
  // dimensions
779
797
  colWidth: props.colWidth, minHeight: rowMinHeight,
780
798
  // refs
781
799
  heightRef: rowHeightRefMap.createRef(cells[0].key) })))));
782
800
  }
783
801
  componentDidMount() {
784
- this.detachWidth = watchWidth(this.rootEl, (width) => {
802
+ this.disconnectWidth = watchWidth(this.rootEl, (width) => {
785
803
  this.setState({ width });
786
804
  });
787
805
  }
788
806
  componentWillUnmount() {
789
- this.detachWidth();
807
+ this.disconnectWidth();
790
808
  }
791
809
  // Hit System
792
810
  // -----------------------------------------------------------------------------------------------
@@ -803,7 +821,7 @@ class DayGridRows extends DateComponent {
803
821
  dateSpan: Object.assign({ range: {
804
822
  start: cellStartDate,
805
823
  end: cellEndDate,
806
- }, allDay: true }, cell.extraDateSpan),
824
+ }, allDay: true }, cell.dateSpanProps),
807
825
  // HACK. TODO: This is expensive to do every hit-query
808
826
  dayEl: getCellEl(getRowEl(this.rootEl, row), col),
809
827
  rect: {
@@ -821,28 +839,98 @@ class DayGridRows extends DateComponent {
821
839
  function isSegAllDay(seg) {
822
840
  return seg.eventRange.def.allDay;
823
841
  }
842
+ function computeRowHeight(visibleWidth, // should INCLUDE any scrollbar width to avoid oscillation
843
+ rowCnt, isHeightAuto, forPrint, options) {
844
+ if (visibleWidth != null) {
845
+ // ensure a consistent row min-height modelled after a month with 6 rows respecting aspectRatio
846
+ // will result in same minHeight regardless of weekends, dayMinWidth, height:auto
847
+ const rowMinHeight = visibleWidth / options.aspectRatio / 6;
848
+ return [
849
+ forPrint
850
+ // special-case for print, which condenses whole-page width without notifying
851
+ // this is value that looks natural on paper for portrait/landscape
852
+ ? '6em'
853
+ // don't give minHeight when single-month non-auto-height
854
+ // TODO: better way to detect this with DateProfile?
855
+ : (rowCnt > 6 || isHeightAuto)
856
+ ? rowMinHeight
857
+ : undefined,
858
+ // isCompact?: just before most lone +more links hit bottom of cell
859
+ rowMinHeight < 70,
860
+ ];
861
+ }
862
+ return [undefined, false];
863
+ }
864
+
865
+ class DayGridHeaderCell extends BaseComponent {
866
+ constructor() {
867
+ super(...arguments);
868
+ this.handleInnerEl = (innerEl) => {
869
+ if (this.disconectInnerHeight) {
870
+ this.disconectInnerHeight();
871
+ this.disconectInnerHeight = undefined;
872
+ }
873
+ if (innerEl) {
874
+ this.disconectInnerHeight = watchHeight(innerEl, (height) => {
875
+ setRef(this.props.innerHeightRef, height);
876
+ });
877
+ }
878
+ else {
879
+ setRef(this.props.innerHeightRef, null);
880
+ }
881
+ };
882
+ }
883
+ render() {
884
+ const { props } = this;
885
+ const { renderConfig, dataConfig } = props;
886
+ // HACK
887
+ const isDisabled = dataConfig.renderProps.isDisabled;
888
+ return (createElement(ContentContainer, { tag: 'div', attrs: Object.assign({ role: 'columnheader', 'aria-colspan': dataConfig.colSpan }, dataConfig.attrs), className: joinClassNames('fc-header-cell fc-cell fc-flex-col fc-align-center', props.borderStart && 'fc-border-s', !props.isSticky && 'fc-crop', props.colWidth == null && 'fc-liquid', dataConfig.className), style: {
889
+ width: props.colWidth != null
890
+ ? props.colWidth * (dataConfig.colSpan || 1)
891
+ : undefined,
892
+ }, renderProps: dataConfig.renderProps, generatorName: renderConfig.generatorName, customGenerator: renderConfig.customGenerator, defaultGenerator: renderText, classNameGenerator:
893
+ // don't use custom classNames if disabled
894
+ // TODO: make DRY with DayCellContainer
895
+ isDisabled ? undefined : renderConfig.classNameGenerator, didMount: renderConfig.didMount, willUnmount: renderConfig.willUnmount }, (InnerContainer) => (createElement(InnerContainer, { tag: 'div', attrs: dataConfig.innerAttrs, className: joinClassNames('fc-cell-inner fc-flex-col fc-padding-sm', props.isSticky && 'fc-sticky-s'), elRef: this.handleInnerEl }))));
896
+ }
897
+ }
824
898
 
825
- class HeaderRow extends BaseComponent {
899
+ class DayGridHeaderRow extends BaseComponent {
900
+ constructor() {
901
+ super(...arguments);
902
+ // ref
903
+ this.innerHeightRefMap = new RefMap(() => {
904
+ afterSize(this.handleInnerHeights);
905
+ });
906
+ this.handleInnerHeights = () => {
907
+ const innerHeightMap = this.innerHeightRefMap.current;
908
+ let max = 0;
909
+ for (const innerHeight of innerHeightMap.values()) {
910
+ max = Math.max(max, innerHeight);
911
+ }
912
+ if (this.currentInnerHeight !== max) {
913
+ this.currentInnerHeight = max;
914
+ setRef(this.props.innerHeightRef, max);
915
+ }
916
+ };
917
+ }
826
918
  render() {
827
919
  const { props } = this;
828
- return (createElement("div", { role: props.cellGroup ? undefined : 'row', className: [
829
- props.cellGroup ? 'fc-flex-row' : 'fc-row',
830
- props.className || '',
831
- ].join(' ') }, props.cells.map((cell) => (createElement(Fragment, { key: props.getHeaderModelKey(cell) }, props.renderHeaderContent(cell, props.tierNum, undefined, // innerHeightRef
832
- props.colWidth))))));
920
+ return (createElement("div", { role: props.role /* !!! */, "aria-rowindex": props.rowIndex != null ? 1 + props.rowIndex : undefined, className: joinClassNames('fc-flex-row fc-content-box', props.className), style: { height: props.height } }, props.dataConfigs.map((dataConfig, cellI) => (createElement(DayGridHeaderCell, { key: dataConfig.key, renderConfig: props.renderConfig, dataConfig: dataConfig, isSticky: props.isSticky, borderStart: Boolean(cellI), colWidth: props.colWidth, innerHeightRef: props.innerHeightRef })))));
833
921
  }
834
922
  }
835
923
 
836
- function DayGridHeader(props) {
837
- return (createElement("div", { className: [
838
- 'fc-rowgroup',
839
- 'fc-content-box',
840
- ...(props.extraClassNames || []),
841
- ].join(' '), style: {
842
- width: props.width,
843
- paddingLeft: props.paddingLeft,
844
- paddingRight: props.paddingRight,
845
- } }, props.headerTiers.map((cells, tierNum) => (createElement(HeaderRow, { key: tierNum, tierNum: tierNum, cells: cells, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey, colWidth: props.colWidth })))));
924
+ /*
925
+ TODO: kill this class in favor of DayGridHeaderRows?
926
+ */
927
+ class DayGridHeader extends BaseComponent {
928
+ render() {
929
+ const { props } = this;
930
+ return (createElement("div", { role: 'rowgroup', className: joinClassNames(props.className, 'fc-flex-col', props.width == null && 'fc-liquid'), style: {
931
+ width: props.width
932
+ } }, props.headerTiers.map((rowConfig, tierNum) => (createElement(DayGridHeaderRow, Object.assign({}, rowConfig, { key: tierNum, role: 'row', className: tierNum ? 'fc-border-t' : '', colWidth: props.colWidth }))))));
933
+ }
846
934
  }
847
935
 
848
936
  class DayGridLayoutNormal extends BaseComponent {
@@ -851,11 +939,11 @@ class DayGridLayoutNormal extends BaseComponent {
851
939
  this.handleScroller = (scroller) => {
852
940
  setRef(this.props.scrollerRef, scroller);
853
941
  };
854
- this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
855
- this.setState({ leftScrollbarWidth });
942
+ this.handleClientWidth = (clientWidth) => {
943
+ this.setState({ clientWidth });
856
944
  };
857
- this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
858
- this.setState({ rightScrollbarWidth });
945
+ this.handleEndScrollbarWidth = (endScrollbarWidth) => {
946
+ this.setState({ endScrollbarWidth });
859
947
  };
860
948
  }
861
949
  render() {
@@ -864,24 +952,22 @@ class DayGridLayoutNormal extends BaseComponent {
864
952
  const verticalScrollbars = !props.forPrint && !getIsHeightAuto(options);
865
953
  const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
866
954
  return (createElement(Fragment, null,
867
- options.dayHeaders && (createElement(DayGridHeader, { headerTiers: props.headerTiers, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey,
868
- // render hooks
869
- extraClassNames: [
870
- 'fc-daygrid-header',
871
- stickyHeaderDates ? 'fc-sticky-header' : '',
872
- ],
873
- // dimensions
874
- paddingLeft: state.leftScrollbarWidth, paddingRight: state.rightScrollbarWidth })),
875
- createElement(Scroller, { vertical: verticalScrollbars, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth, elClassNames: [
876
- 'fc-daygrid-body',
877
- 'fc-rowgroup',
878
- 'fc-flex-column',
879
- verticalScrollbars ? 'fc-liquid' : '',
880
- ], ref: this.handleScroller },
881
- createElement(DayGridRows // .fc-grow
882
- , { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed,
955
+ options.dayHeaders && (createElement("div", { className: joinClassNames(props.forPrint ? 'fc-print-header' : 'fc-flex-row', // col for print, row for screen
956
+ 'fc-border-b') },
957
+ createElement(DayGridHeader, { headerTiers: props.headerTiers, className: joinClassNames('fc-daygrid-header', stickyHeaderDates && 'fc-table-header-sticky') }),
958
+ Boolean(state.endScrollbarWidth) && (createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } })))),
959
+ createElement(Scroller, { vertical: verticalScrollbars, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth, className: joinClassNames('fc-daygrid-body',
960
+ // HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
961
+ // https://stackoverflow.com/a/60256345
962
+ !props.forPrint && 'fc-flex-col', verticalScrollbars && 'fc-liquid'), ref: this.handleScroller },
963
+ createElement(DayGridRows, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed, className: 'fc-grow', dayMaxEvents: props.forPrint ? undefined : options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows,
883
964
  // content
884
965
  fgEventSegs: props.fgEventSegs, bgEventSegs: props.bgEventSegs, businessHourSegs: props.businessHourSegs, dateSelectionSegs: props.dateSelectionSegs, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
966
+ // dimensions
967
+ visibleWidth: // TODO: DRY
968
+ state.clientWidth != null && state.endScrollbarWidth != null
969
+ ? state.clientWidth + state.endScrollbarWidth
970
+ : undefined,
885
971
  // refs
886
972
  rowHeightRefMap: props.rowHeightRefMap }))));
887
973
  }
@@ -895,14 +981,11 @@ class DayGridLayoutPannable extends BaseComponent {
895
981
  this.footerScrollerRef = createRef();
896
982
  // Sizing
897
983
  // -----------------------------------------------------------------------------------------------
898
- this.handleWidth = (width) => {
899
- this.setState({ width });
984
+ this.handleClientWidth = (clientWidth) => {
985
+ this.setState({ clientWidth });
900
986
  };
901
- this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
902
- this.setState({ leftScrollbarWidth });
903
- };
904
- this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
905
- this.setState({ rightScrollbarWidth });
987
+ this.handleEndScrollbarWidth = (endScrollbarWidth) => {
988
+ this.setState({ endScrollbarWidth });
906
989
  };
907
990
  }
908
991
  render() {
@@ -912,37 +995,29 @@ class DayGridLayoutPannable extends BaseComponent {
912
995
  const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
913
996
  const stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(options);
914
997
  const colCnt = props.cellRows[0].length;
915
- const [canvasWidth, colWidth] = computeColWidth(colCnt, props.dayMinWidth, state.width);
998
+ const [canvasWidth, colWidth] = computeColWidth(colCnt, props.dayMinWidth, state.clientWidth);
916
999
  return (createElement(Fragment, null,
917
- options.dayHeaders && (createElement(Scroller, { horizontal: true, hideScrollbars: true, elClassNames: [
918
- 'fc-daygrid-header',
919
- 'fc-rowgroup',
920
- stickyHeaderDates ? 'fc-sticky-header' : ''
921
- ], ref: this.headerScrollerRef },
922
- createElement(DayGridHeader, { headerTiers: props.headerTiers, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey,
923
- // dimensions
924
- colWidth: colWidth, width: canvasWidth, paddingLeft: state.leftScrollbarWidth, paddingRight: state.rightScrollbarWidth }))),
925
- createElement(Scroller, { vertical: verticalScrollbars, horizontal: true, hideScrollbars: stickyFooterScrollbar, widthRef: this.handleWidth, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth, elClassNames: [
926
- 'fc-daygrid-body',
927
- 'fc-rowgroup',
928
- 'fc-flex-column',
929
- verticalScrollbars ? 'fc-liquid' : '',
930
- ], ref: this.bodyScrollerRef },
931
- createElement(DayGridRows // .fc-grow
932
- , { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed,
1000
+ options.dayHeaders && (createElement("div", { className: 'fc-print-header' },
1001
+ createElement(Scroller, { horizontal: true, hideScrollbars: true, className: joinClassNames('fc-daygrid-header fc-flex-row fc-border-b', stickyHeaderDates && 'fc-table-header-sticky'), ref: this.headerScrollerRef },
1002
+ createElement(DayGridHeader, { headerTiers: props.headerTiers, colWidth: colWidth, width: canvasWidth }),
1003
+ Boolean(state.endScrollbarWidth) && (createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))))),
1004
+ createElement(Scroller, { vertical: verticalScrollbars, horizontal: true, hideScrollbars: stickyFooterScrollbar ||
1005
+ props.forPrint // prevents blank space in print-view on Safari
1006
+ , className: joinClassNames('fc-daygrid-body',
1007
+ // HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
1008
+ // https://stackoverflow.com/a/60256345
1009
+ !props.forPrint && 'fc-flex-col', verticalScrollbars && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth },
1010
+ createElement(DayGridRows, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed, className: 'fc-grow', dayMaxEvents: props.forPrint ? undefined : options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows,
933
1011
  // content
934
1012
  fgEventSegs: props.fgEventSegs, bgEventSegs: props.bgEventSegs, businessHourSegs: props.businessHourSegs, dateSelectionSegs: props.dateSelectionSegs, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
935
1013
  // dimensions
936
- colWidth: colWidth, width: canvasWidth,
1014
+ colWidth: colWidth, width: canvasWidth, visibleWidth: // TODO: DRY
1015
+ state.clientWidth != null && state.endScrollbarWidth != null
1016
+ ? state.clientWidth + state.endScrollbarWidth
1017
+ : undefined,
937
1018
  // refs
938
1019
  rowHeightRefMap: props.rowHeightRefMap })),
939
- Boolean(stickyFooterScrollbar) && (createElement(Scroller, { ref: this.footerScrollerRef, horizontal: true, elClassNames: ['fc-sticky-footer'], elStyle: {
940
- marginTop: '-1px', // HACK
941
- } },
942
- createElement("div", { style: {
943
- width: canvasWidth,
944
- height: '1px', // HACK
945
- } })))));
1020
+ Boolean(stickyFooterScrollbar) && (createElement(StickyFooterScrollbar, { canvasWidth: canvasWidth, scrollerRef: this.footerScrollerRef }))));
946
1021
  }
947
1022
  // Lifecycle
948
1023
  // -----------------------------------------------------------------------------------------------
@@ -997,21 +1072,29 @@ class DayGridLayout extends BaseComponent {
997
1072
  }
998
1073
  }
999
1074
  };
1000
- this.clearScroll = () => {
1001
- this.scrollDate = null;
1075
+ this.handleScrollEnd = ({ isUser }) => {
1076
+ if (isUser) {
1077
+ this.scrollDate = null;
1078
+ }
1002
1079
  };
1003
1080
  }
1004
1081
  render() {
1005
1082
  const { props, context } = this;
1006
1083
  const { options } = context;
1007
1084
  const commonLayoutProps = Object.assign(Object.assign({}, props), { scrollerRef: this.scrollerRef, rowHeightRefMap: this.rowHeightRefMap });
1008
- return (createElement(ViewContainer, { viewSpec: context.viewSpec, elClasses: [props.className, 'fc-flex-column', 'fc-border'] }, options.dayMinWidth ? (createElement(DayGridLayoutPannable, Object.assign({}, commonLayoutProps, { dayMinWidth: options.dayMinWidth }))) : (createElement(DayGridLayoutNormal, Object.assign({}, commonLayoutProps)))));
1085
+ return (createElement(ViewContainer, { viewSpec: context.viewSpec, attrs: {
1086
+ role: 'grid',
1087
+ 'aria-rowcount': props.headerTiers.length + props.cellRows.length,
1088
+ 'aria-colcount': props.cellRows[0].length,
1089
+ 'aria-labelledby': props.labelId,
1090
+ 'aria-label': props.labelStr,
1091
+ }, className: joinClassNames(props.className, 'fc-print-root fc-border') }, options.dayMinWidth ? (createElement(DayGridLayoutPannable, Object.assign({}, commonLayoutProps, { dayMinWidth: options.dayMinWidth }))) : (createElement(DayGridLayoutNormal, Object.assign({}, commonLayoutProps)))));
1009
1092
  }
1010
1093
  // Lifecycle
1011
1094
  // -----------------------------------------------------------------------------------------------
1012
1095
  componentDidMount() {
1013
1096
  this.resetScroll();
1014
- this.scrollerRef.current.addScrollEndListener(this.clearScroll);
1097
+ this.scrollerRef.current.addScrollEndListener(this.handleScrollEnd);
1015
1098
  }
1016
1099
  componentDidUpdate(prevProps) {
1017
1100
  if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
@@ -1019,98 +1102,24 @@ class DayGridLayout extends BaseComponent {
1019
1102
  }
1020
1103
  }
1021
1104
  componentWillUnmount() {
1022
- this.scrollerRef.current.removeScrollEndListener(this.clearScroll);
1105
+ this.scrollerRef.current.removeScrollEndListener(this.handleScrollEnd);
1023
1106
  }
1024
1107
  // Scrolling
1025
1108
  // -----------------------------------------------------------------------------------------------
1026
1109
  resetScroll() {
1027
1110
  this.scrollDate = this.props.dateProfile.currentDate;
1028
1111
  this.updateScrollY();
1029
- // updateScrollX
1030
1112
  const scroller = this.scrollerRef.current;
1031
1113
  scroller.scrollTo({ x: 0 });
1032
1114
  }
1033
1115
  }
1034
1116
 
1035
- const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
1036
- class DayOfWeekHeaderCell extends BaseComponent {
1037
- constructor() {
1038
- super(...arguments);
1039
- // ref
1040
- this.innerElRef = createRef();
1041
- }
1042
- render() {
1043
- let { props, context } = this;
1044
- let { dateEnv, theme, viewApi, options } = context;
1045
- let date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT
1046
- let dateMeta = {
1047
- dow: props.dow,
1048
- isDisabled: false,
1049
- isFuture: false,
1050
- isPast: false,
1051
- isToday: false,
1052
- isOther: false,
1053
- };
1054
- let text = dateEnv.format(date, props.dayHeaderFormat);
1055
- let renderProps = Object.assign(Object.assign(Object.assign(Object.assign({ date }, dateMeta), { view: viewApi }), props.extraRenderProps), { text });
1056
- return (createElement(ContentContainer, { elTag: 'div', elClasses: [
1057
- ...getDayClassNames(dateMeta, theme),
1058
- ...(props.extraClassNames || []),
1059
- 'fc-header-cell',
1060
- 'fc-cell',
1061
- props.colWidth != null ? '' : 'fc-liquid',
1062
- 'fc-flex-column',
1063
- 'fc-align-center',
1064
- ], elAttrs: props.extraDataAttrs, elStyle: {
1065
- width: props.colWidth != null // TODO: DRY
1066
- ? props.colWidth * (props.colSpan || 1)
1067
- : undefined,
1068
- }, renderProps: renderProps, generatorName: "dayHeaderContent", customGenerator: options.dayHeaderContent, defaultGenerator: renderInner, classNameGenerator: options.dayHeaderClassNames, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, (InnerContent) => (createElement("div", { ref: this.innerElRef, className: [
1069
- 'fc-flex-column',
1070
- props.isSticky ? 'fc-sticky-x' : '',
1071
- ].join(' ') },
1072
- createElement(InnerContent, { elTag: "a", elClasses: [
1073
- 'fc-cell-inner',
1074
- 'fc-padding-sm',
1075
- ], elAttrs: {
1076
- 'aria-label': dateEnv.format(date, WEEKDAY_FORMAT),
1077
- } })))));
1078
- }
1079
- componentDidMount() {
1080
- const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
1081
- // TODO: only attach this if refs props present
1082
- this.disconectInnerHeight = watchHeight(innerEl, (height) => {
1083
- setRef(this.props.innerHeightRef, height);
1084
- });
1085
- }
1086
- componentWillUnmount() {
1087
- this.disconectInnerHeight();
1088
- setRef(this.props.innerHeightRef, null);
1089
- }
1090
- }
1091
-
1092
- function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) {
1093
- return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt);
1094
- }
1095
- // Computes a default column header formatting string if `colFormat` is not explicitly defined
1096
- function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
1097
- // if more than one week row, or if there are a lot of columns with not much space,
1098
- // put just the day numbers will be in each cell
1099
- if (!datesRepDistinctDays || dayCnt > 10) {
1100
- return createFormatter({ weekday: 'short' }); // "Sat"
1101
- }
1102
- if (dayCnt > 1) {
1103
- return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12"
1104
- }
1105
- return createFormatter({ weekday: 'long' }); // "Saturday"
1106
- }
1107
-
1108
1117
  class DayGridView extends BaseComponent {
1109
1118
  constructor() {
1110
1119
  super(...arguments);
1111
1120
  // memo
1112
1121
  this.buildDayTableModel = memoize(buildDayTableModel);
1113
- this.buildHeaderTiers = memoize(buildHeaderTiers);
1122
+ this.buildDateRowConfigs = memoize(buildDateRowConfigs);
1114
1123
  this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
1115
1124
  // internal
1116
1125
  this.slicer = new DayTableSlicer();
@@ -1120,60 +1129,58 @@ class DayGridView extends BaseComponent {
1120
1129
  const { options } = context;
1121
1130
  const dayTableModel = this.buildDayTableModel(props.dateProfile, context.dateProfileGenerator);
1122
1131
  const datesRepDistinctDays = dayTableModel.rowCnt === 1;
1123
- const headerTiers = this.buildHeaderTiers(dayTableModel.headerDates, datesRepDistinctDays);
1124
- const slicedProps = this.slicer.sliceProps(props, props.dateProfile, options.nextDayThreshold, context, dayTableModel);
1125
1132
  const dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dayTableModel.colCnt);
1126
- return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(DayGridLayout, { dateProfile: props.dateProfile, todayRange: todayRange, cellRows: dayTableModel.cellRows, forPrint: props.forPrint, className: 'fc-daygrid-view',
1127
- // header content
1128
- headerTiers: headerTiers, renderHeaderContent: (model, tier, innerHeightRef, colWidth) => {
1129
- if (model.date) {
1130
- return (createElement(DateHeaderCell, Object.assign({}, model, { dateProfile: props.dateProfile, todayRange: todayRange, navLink: dayTableModel.colCnt > 1, dayHeaderFormat: dayHeaderFormat, colSpan: model.colSpan, colWidth: colWidth })));
1131
- }
1132
- else {
1133
- return (createElement(DayOfWeekHeaderCell, Object.assign({}, model, { dayHeaderFormat: dayHeaderFormat, colSpan: model.colSpan, colWidth: colWidth })));
1134
- }
1135
- }, getHeaderModelKey: (model) => {
1136
- // can use model.key???
1137
- if (model.date) {
1138
- return model.date.toUTCString();
1139
- }
1140
- return model.dow;
1141
- },
1142
- // body content
1143
- fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection }))));
1133
+ const slicedProps = this.slicer.sliceProps(props, props.dateProfile, options.nextDayThreshold, context, dayTableModel);
1134
+ return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => {
1135
+ const headerTiers = this.buildDateRowConfigs(dayTableModel.headerDates, datesRepDistinctDays, props.dateProfile, todayRange, dayHeaderFormat, context);
1136
+ return (createElement(DayGridLayout, { labelId: props.labelId, labelStr: props.labelStr, dateProfile: props.dateProfile, todayRange: todayRange, cellRows: dayTableModel.cellRows, forPrint: props.forPrint, className: 'fc-daygrid',
1137
+ // header content
1138
+ headerTiers: headerTiers,
1139
+ // body content
1140
+ fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection }));
1141
+ }));
1144
1142
  }
1145
1143
  }
1146
1144
 
1147
- /*
1148
- TODO: is it even worth doing this "advanced" version?
1149
- */
1150
- class HeaderRowAdvanced extends BaseComponent {
1151
- constructor() {
1152
- super(...arguments);
1153
- // ref
1154
- this.innerHeightRefMap = new RefMap(() => {
1155
- afterSize(this.handleInnerHeights);
1145
+ class TableDateProfileGenerator extends DateProfileGenerator {
1146
+ // Computes the date range that will be rendered
1147
+ buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay) {
1148
+ let renderRange = super.buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay);
1149
+ let { props } = this;
1150
+ return buildDayTableRenderRange({
1151
+ currentRange: renderRange,
1152
+ snapToWeek: /^(year|month)$/.test(currentRangeUnit),
1153
+ fixedWeekCount: props.fixedWeekCount,
1154
+ dateEnv: props.dateEnv,
1156
1155
  });
1157
- this.handleInnerHeights = () => {
1158
- const innerHeightMap = this.innerHeightRefMap.current;
1159
- let max = 0;
1160
- for (const innerHeight of innerHeightMap.values()) {
1161
- max = Math.max(max, innerHeight);
1162
- }
1163
- if (this.currentInnerHeight !== max) {
1164
- this.currentInnerHeight = max;
1165
- setRef(this.props.innerHeightRef, max);
1166
- }
1167
- };
1168
1156
  }
1169
- render() {
1170
- const { props } = this;
1171
- return (createElement("div", { role: 'row', className: 'fc-row', style: { height: props.height } }, props.cells.map((cell) => {
1172
- const key = props.getHeaderModelKey(cell);
1173
- return (createElement(Fragment, { key: props.getHeaderModelKey(cell) }, props.renderHeaderContent(cell, props.tierNum, this.innerHeightRefMap.createRef(key), // innerHeightRef
1174
- props.colWidth)));
1175
- })));
1157
+ }
1158
+ function buildDayTableRenderRange(props) {
1159
+ let { dateEnv, currentRange } = props;
1160
+ let { start, end } = currentRange;
1161
+ let endOfWeek;
1162
+ // year and month views should be aligned with weeks. this is already done for week
1163
+ if (props.snapToWeek) {
1164
+ start = dateEnv.startOfWeek(start);
1165
+ // make end-of-week if not already
1166
+ endOfWeek = dateEnv.startOfWeek(end);
1167
+ if (endOfWeek.valueOf() !== end.valueOf()) {
1168
+ end = addWeeks(endOfWeek, 1);
1169
+ }
1170
+ }
1171
+ // ensure 6 weeks
1172
+ if (props.fixedWeekCount) {
1173
+ // TODO: instead of these date-math gymnastics (for multimonth view),
1174
+ // compute dateprofiles of all months, then use start of first and end of last.
1175
+ let lastMonthRenderStart = dateEnv.startOfWeek(dateEnv.startOfMonth(addDays(currentRange.end, -1)));
1176
+ let rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
1177
+ diffWeeks(lastMonthRenderStart, end));
1178
+ end = addWeeks(end, 6 - rowCnt);
1176
1179
  }
1180
+ return { start, end };
1177
1181
  }
1178
1182
 
1179
- export { COMPACT_CELL_WIDTH, DateHeaderCell, DayGridLayout, DayGridRow, DayGridRows, DayGridView, DayOfWeekHeaderCell, DayTableSlicer, HeaderRow, HeaderRowAdvanced, TableDateProfileGenerator, buildDayTableModel, buildDayTableRenderRange, computeColFromPosition, computeColWidth, createDayHeaderFormatter, getCellEl, getRowEl };
1183
+ var css_248z = ":root{--fc-daygrid-event-dot-width:8px}.fc-daygrid-day.fc-day-today{background-color:var(--fc-today-bg-color)}.fc-daygrid-day-header{display:flex;flex-direction:row-reverse}.fc-day-other .fc-daygrid-day-header{opacity:.3}.fc-daygrid-day-number{padding:4px;position:relative}.fc-daygrid-month-start{font-size:1.1em;font-weight:700}.fc-daygrid-day-body{display:flex;flex-direction:column;margin-bottom:1px}.fc-daygrid-day-body-tall{margin-bottom:1em;min-height:2em}.fc-daygrid-day-body:only-child{margin-top:2px}.fc-daygrid-more-link{border-radius:3px;cursor:pointer;font-size:var(--fc-small-font-size);margin:0 2px 1px;max-width:100%;overflow:hidden;padding:2px;position:relative;white-space:nowrap}.fc-daygrid-more-link:hover{background-color:rgba(0,0,0,.1)}.fc-daygrid-more-link-button{align-self:flex-start}.fc-daygrid-more-link-block{border:1px solid var(--fc-event-border-color);padding:1px}.fc-daygrid-week-number{background-color:var(--fc-neutral-bg-color);color:var(--fc-neutral-text-color);min-width:1.5em;padding:2px;position:absolute;text-align:center;top:0}.fc-more-popover .fc-popover-body{min-width:220px;padding:10px}.fc-daygrid-event{border-radius:3px;font-size:var(--fc-small-font-size);margin-bottom:1px}.fc-media-print .fc-daygrid-event{overflow:hidden!important;white-space:nowrap!important}.fc-direction-ltr .fc-daygrid-event.fc-event-start,.fc-direction-rtl .fc-daygrid-event.fc-event-end{margin-left:2px}.fc-direction-ltr .fc-daygrid-event.fc-event-end,.fc-direction-rtl .fc-daygrid-event.fc-event-start{margin-right:2px}.fc-direction-ltr .fc-daygrid-event .fc-event-time{margin-right:3px}.fc-direction-rtl .fc-daygrid-event .fc-event-time{margin-left:3px}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-start),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-end){border-bottom-left-radius:0;border-left-width:0;border-top-left-radius:0}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-end),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-start){border-bottom-right-radius:0;border-right-width:0;border-top-right-radius:0}.fc-daygrid-block-event .fc-event-time{font-weight:700}.fc-daygrid-block-event .fc-event-time,.fc-daygrid-block-event .fc-event-title{padding:1px}.fc-daygrid-dot-event{align-items:center;direction:row;display:flex;padding:2px 0;position:relative;z-index:2}.fc-daygrid-dot-event.fc-event-mirror,.fc-daygrid-dot-event:hover{background:rgba(0,0,0,.1)}.fc-daygrid-dot-event.fc-event-selected:before{bottom:-10px;top:-10px}.fc-daygrid-event-dot{border:calc(var(--fc-daygrid-event-dot-width)/2) solid var(--fc-event-border-color);border-radius:calc(var(--fc-daygrid-event-dot-width)/2);box-sizing:content-box;height:0;margin:0 4px;width:0}.fc-daygrid-dot-event .fc-event-time,.fc-daygrid-dot-event .fc-event-title{overflow:hidden;white-space:nowrap}.fc-daygrid-dot-event .fc-event-title{flex-basis:0;flex-grow:1;font-weight:700;min-height:0;min-width:0}";
1184
+ injectStyles(css_248z);
1185
+
1186
+ export { DayGridHeaderRow, DayGridLayout, DayGridRow, DayGridRows, DayGridView, DayTableSlicer, TableDateProfileGenerator, buildDateDataConfigs, buildDateRenderConfig, buildDateRowConfig, buildDateRowConfigs, buildDayTableModel, buildDayTableRenderRange, computeColFromPosition, computeColWidth, computeRowHeight, createDayHeaderFormatter, getCellEl, getRowEl };