@fullcalendar/daygrid 6.1.14 → 7.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/internal.js CHANGED
@@ -1,128 +1,251 @@
1
- import { DateComponent, getStickyHeaderDates, ViewContainer, SimpleScrollGrid, getStickyFooterScrollbar, renderScrollShim, createFormatter, BaseComponent, StandardEvent, buildSegTimeText, EventContainer, getSegAnchorAttrs, memoize, MoreLinkContainer, getSegMeta, getUniqueDomId, setRef, DayCellContainer, WeekNumberContainer, buildNavLinkAttrs, hasCustomDayCellContent, addMs, intersectRanges, addDays, SegHierarchy, buildEntryKey, intersectSpans, RefMap, sortEventSegs, isPropsEqual, buildEventRangeKey, BgEvent, renderFill, PositionCache, NowTimer, formatIsoMonthStr, formatDayString, Slicer, DayHeader, DaySeriesModel, DayTableModel, DateProfileGenerator, addWeeks, diffWeeks, injectStyles } from '@fullcalendar/core/internal.js';
2
- import { createRef, createElement, Fragment } from '@fullcalendar/core/preact.js';
1
+ import { injectStyles, Slicer, DateProfileGenerator, addWeeks, addDays, diffWeeks, DaySeriesModel, DayTableModel, fracToCssDim, BaseComponent, getDateMeta, buildNavLinkAttrs, ContentContainer, getDayClassNames, formatDayString, watchHeight, setRef, createFormatter, StandardEvent, buildSegTimeText, EventContainer, getSegAnchorAttrs, MoreLinkContainer, getSegMeta, DateComponent, DayCellContainer, hasCustomDayCellContent, addMs, SegHierarchy, buildEntryKey, intersectSpans, RefMap, afterSize, sortEventSegs, WeekNumberContainer, buildEventRangeKey, BgEvent, renderFill, memoize, getIsHeightAuto, watchWidth, getStickyHeaderDates, Scroller, getStickyFooterScrollbar, getScrollerSyncerClass, ScrollResponder, ViewContainer, NowTimer } from '@fullcalendar/core/internal.js';
2
+ import { createRef, createElement, Fragment, Component } from '@fullcalendar/core/preact.js';
3
3
 
4
- /* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells.
5
- ----------------------------------------------------------------------------------------------------------------------*/
6
- // It is a manager for a Table subcomponent, which does most of the heavy lifting.
7
- // It is responsible for managing width/height.
8
- class TableView extends DateComponent {
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);
6
+
7
+ class DayTableSlicer extends Slicer {
9
8
  constructor() {
10
9
  super(...arguments);
11
- this.headerElRef = createRef();
10
+ this.forceDayIfListItem = true;
12
11
  }
13
- renderSimpleLayout(headerRowContent, bodyContent) {
14
- let { props, context } = this;
15
- let sections = [];
16
- let stickyHeaderDates = getStickyHeaderDates(context.options);
17
- if (headerRowContent) {
18
- sections.push({
19
- type: 'header',
20
- key: 'header',
21
- isSticky: stickyHeaderDates,
22
- chunk: {
23
- elRef: this.headerElRef,
24
- tableClassName: 'fc-col-header',
25
- rowContent: headerRowContent,
26
- },
27
- });
28
- }
29
- sections.push({
30
- type: 'body',
31
- key: 'body',
32
- liquid: true,
33
- chunk: { content: bodyContent },
12
+ sliceRange(dateRange, dayTableModel) {
13
+ return dayTableModel.sliceRange(dateRange);
14
+ }
15
+ }
16
+
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,
34
27
  });
35
- return (createElement(ViewContainer, { elClasses: ['fc-daygrid'], viewSpec: context.viewSpec },
36
- createElement(SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, collapsibleWidth: props.forPrint, cols: [] /* TODO: make optional? */, sections: sections })));
37
28
  }
38
- renderHScrollLayout(headerRowContent, bodyContent, colCnt, dayMinWidth) {
39
- let ScrollGrid = this.context.pluginHooks.scrollGridImpl;
40
- if (!ScrollGrid) {
41
- throw new Error('No ScrollGrid implementation');
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);
42
41
  }
43
- let { props, context } = this;
44
- let stickyHeaderDates = !props.forPrint && getStickyHeaderDates(context.options);
45
- let stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(context.options);
46
- let sections = [];
47
- if (headerRowContent) {
48
- sections.push({
49
- type: 'header',
50
- key: 'header',
51
- isSticky: stickyHeaderDates,
52
- chunks: [{
53
- key: 'main',
54
- elRef: this.headerElRef,
55
- tableClassName: 'fc-col-header',
56
- rowContent: headerRowContent,
57
- }],
58
- });
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
80
+ // -------------------------------------------------------------------------------------------------
81
+ function computeTopFromDate(date, cellRows, rowHeightMap) {
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;
59
89
  }
60
- sections.push({
61
- type: 'body',
62
- key: 'body',
63
- liquid: true,
64
- chunks: [{
65
- key: 'main',
66
- content: bodyContent,
67
- }],
68
- });
69
- if (stickyFooterScrollbar) {
70
- sections.push({
71
- type: 'footer',
72
- key: 'footer',
73
- isSticky: true,
74
- chunks: [{
75
- key: 'main',
76
- content: renderScrollShim,
77
- }],
78
- });
90
+ const rowHeight = rowHeightMap.get(key);
91
+ if (rowHeight == null) {
92
+ return; // denote unknown
93
+ }
94
+ top += rowHeight;
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);
79
119
  }
80
- return (createElement(ViewContainer, { elClasses: ['fc-daygrid'], viewSpec: context.viewSpec },
81
- createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, forPrint: props.forPrint, collapsibleWidth: props.forPrint, colGroups: [{ cols: [{ span: colCnt, minWidth: dayMinWidth }] }], sections: sections })));
82
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 };
145
+ }
146
+ // Hit Element
147
+ // -------------------------------------------------------------------------------------------------
148
+ function getRowEl(rootEl, row) {
149
+ return rootEl.querySelectorAll(':scope > [role=row]')[row];
150
+ }
151
+ function getCellEl(rowEl, col) {
152
+ return rowEl.querySelectorAll(':scope > [role=gridcell]')[col];
83
153
  }
84
154
 
85
- function splitSegsByRow(segs, rowCnt) {
86
- let byRow = [];
87
- for (let i = 0; i < rowCnt; i += 1) {
88
- byRow[i] = [];
155
+ class DateHeaderCell extends BaseComponent {
156
+ constructor() {
157
+ super(...arguments);
158
+ // ref
159
+ this.innerElRef = createRef();
89
160
  }
90
- for (let seg of segs) {
91
- byRow[seg.row].push(seg);
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);
196
+ });
197
+ }
198
+ componentWillUnmount() {
199
+ this.disconectInnerHeight();
92
200
  }
93
- return byRow;
94
201
  }
95
- function splitSegsByFirstCol(segs, colCnt) {
96
- let byCol = [];
97
- for (let i = 0; i < colCnt; i += 1) {
98
- byCol[i] = [];
202
+
203
+ function splitSegsByRow(segs, rowCnt) {
204
+ const byRow = [];
205
+ for (let row = 0; row < rowCnt; row++) {
206
+ byRow[row] = [];
99
207
  }
100
- for (let seg of segs) {
101
- byCol[seg.firstCol].push(seg);
208
+ for (const seg of segs) {
209
+ byRow[seg.row].push(seg);
102
210
  }
103
- return byCol;
211
+ return byRow;
104
212
  }
105
213
  function splitInteractionByRow(ui, rowCnt) {
106
- let byRow = [];
214
+ const byRow = [];
107
215
  if (!ui) {
108
- for (let i = 0; i < rowCnt; i += 1) {
109
- byRow[i] = null;
216
+ for (let row = 0; row < rowCnt; row++) {
217
+ byRow[row] = null;
110
218
  }
111
219
  }
112
220
  else {
113
- for (let i = 0; i < rowCnt; i += 1) {
114
- byRow[i] = {
221
+ for (let row = 0; row < rowCnt; row++) {
222
+ byRow[row] = {
115
223
  affectedInstances: ui.affectedInstances,
116
224
  isEvent: ui.isEvent,
117
225
  segs: [],
118
226
  };
119
227
  }
120
- for (let seg of ui.segs) {
228
+ for (const seg of ui.segs) {
121
229
  byRow[seg.row].segs.push(seg);
122
230
  }
123
231
  }
124
232
  return byRow;
125
233
  }
234
+ function splitSegsByCol(segs, colCnt) {
235
+ let byCol = [];
236
+ for (let col = 0; col < colCnt; col++) {
237
+ byCol.push([]);
238
+ }
239
+ for (let seg of segs) {
240
+ for (let col = seg.firstCol; col <= seg.lastCol; col++) {
241
+ if (seg.firstCol !== col) {
242
+ seg = Object.assign(Object.assign({}, seg), { firstCol: col, lastCol: col, isStart: false, isEnd: seg.isEnd && seg.lastCol === col, isStandin: true });
243
+ }
244
+ byCol[col].push(seg);
245
+ }
246
+ }
247
+ return byCol;
248
+ }
126
249
 
127
250
  const DEFAULT_TABLE_EVENT_TIME_FORMAT = createFormatter({
128
251
  hour: 'numeric',
@@ -140,14 +263,14 @@ function hasListItemDisplay(seg) {
140
263
  );
141
264
  }
142
265
 
143
- class TableBlockEvent extends BaseComponent {
266
+ class DayGridBlockEvent extends BaseComponent {
144
267
  render() {
145
268
  let { props } = this;
146
269
  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.seg.eventRange.def.allDay })));
147
270
  }
148
271
  }
149
272
 
150
- class TableListItemEvent extends BaseComponent {
273
+ class DayGridListEvent extends BaseComponent {
151
274
  render() {
152
275
  let { props, context } = this;
153
276
  let { options } = context;
@@ -164,80 +287,83 @@ function renderInnerContent(renderProps) {
164
287
  createElement("div", { className: "fc-event-title" }, renderProps.event.title || createElement(Fragment, null, "\u00A0"))));
165
288
  }
166
289
 
167
- class TableCellMoreLink extends BaseComponent {
168
- constructor() {
169
- super(...arguments);
170
- this.compileSegs = memoize(compileSegs);
171
- }
290
+ class DayGridMoreLink extends BaseComponent {
172
291
  render() {
173
292
  let { props } = this;
174
- let { allSegs, invisibleSegs } = this.compileSegs(props.singlePlacements);
175
- return (createElement(MoreLinkContainer, { elClasses: ['fc-daygrid-more-link'], dateProfile: props.dateProfile, todayRange: props.todayRange, allDayDate: props.allDayDate, moreCnt: props.moreCnt, allSegs: allSegs, hiddenSegs: invisibleSegs, alignmentElRef: props.alignmentElRef, alignGridTop: props.alignGridTop, extraDateSpan: props.extraDateSpan, popoverContent: () => {
176
- let isForcedInvisible = (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
293
+ 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: () => {
294
+ let forcedInvisibleMap = // TODO: more convenient/DRY
295
+ (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
177
296
  (props.eventResize ? props.eventResize.affectedInstances : null) ||
178
297
  {};
179
- return (createElement(Fragment, null, allSegs.map((seg) => {
298
+ return (createElement(Fragment, null, props.segs.map((seg) => {
180
299
  let instanceId = seg.eventRange.instance.instanceId;
181
- return (createElement("div", { className: "fc-daygrid-event-harness", key: instanceId, style: {
182
- visibility: isForcedInvisible[instanceId] ? 'hidden' : '',
183
- } }, hasListItemDisplay(seg) ? (createElement(TableListItemEvent, Object.assign({ seg: seg, isDragging: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getSegMeta(seg, props.todayRange)))) : (createElement(TableBlockEvent, Object.assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getSegMeta(seg, props.todayRange))))));
300
+ return (createElement("div", { key: instanceId, style: {
301
+ visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '',
302
+ } }, hasListItemDisplay(seg) ? (createElement(DayGridListEvent, Object.assign({ seg: seg, isDragging: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getSegMeta(seg, props.todayRange)))) : (createElement(DayGridBlockEvent, Object.assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getSegMeta(seg, props.todayRange))))));
184
303
  })));
185
304
  } }));
186
305
  }
187
306
  }
188
- function compileSegs(singlePlacements) {
189
- let allSegs = [];
190
- let invisibleSegs = [];
191
- for (let placement of singlePlacements) {
192
- allSegs.push(placement.seg);
193
- if (!placement.isVisible) {
194
- invisibleSegs.push(placement.seg);
195
- }
196
- }
197
- return { allSegs, invisibleSegs };
198
- }
199
307
 
200
- const DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' });
201
- class TableCell extends DateComponent {
308
+ class DayGridCell extends DateComponent {
202
309
  constructor() {
203
310
  super(...arguments);
204
- this.rootElRef = createRef();
205
- this.state = {
206
- dayNumberId: getUniqueDomId(),
207
- };
208
- this.handleRootEl = (el) => {
209
- setRef(this.rootElRef, el);
210
- setRef(this.props.elRef, el);
211
- };
311
+ // ref
312
+ this.innerElRef = createRef();
313
+ this.headerWrapElRef = createRef();
212
314
  }
213
315
  render() {
214
- let { context, props, state, rootElRef } = this;
316
+ let { props, context } = this;
215
317
  let { options, dateEnv } = context;
216
- let { date, dateProfile } = props;
217
- // TODO: memoize this?
318
+ // TODO: memoize this
218
319
  const isMonthStart = props.showDayNumber &&
219
- shouldDisplayMonthStart(date, dateProfile.currentRange, dateEnv);
220
- return (createElement(DayCellContainer, { elTag: "td", elRef: this.handleRootEl, elClasses: [
221
- 'fc-daygrid-day',
320
+ shouldDisplayMonthStart(props.date, props.dateProfile.currentRange, dateEnv);
321
+ return (createElement(DayCellContainer, { elTag: "div", elClasses: [
322
+ 'fc-daygrid-cell',
323
+ 'fc-cell',
324
+ props.width != null ? '' : 'fc-liquid',
325
+ 'fc-flex-column',
222
326
  ...(props.extraClassNames || []),
223
- ], elAttrs: Object.assign(Object.assign(Object.assign({}, props.extraDataAttrs), (props.showDayNumber ? { 'aria-labelledby': state.dayNumberId } : {})), { role: 'gridcell' }), defaultGenerator: renderTopInner, date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, isMonthStart: isMonthStart, extraRenderProps: props.extraRenderProps }, (InnerContent, renderProps) => (createElement("div", { ref: props.innerElRef, className: "fc-daygrid-day-frame fc-scrollgrid-sync-inner", style: { minHeight: props.minHeight } },
224
- props.showWeekNumber && (createElement(WeekNumberContainer, { elTag: "a", elClasses: ['fc-daygrid-week-number'], elAttrs: buildNavLinkAttrs(context, date, 'week'), date: date, defaultFormat: DEFAULT_WEEK_NUM_FORMAT })),
225
- !renderProps.isDisabled &&
226
- (props.showDayNumber || hasCustomDayCellContent(options) || props.forceDayTop) ? (createElement("div", { className: "fc-daygrid-day-top" },
327
+ ], elAttrs: Object.assign(Object.assign({}, props.extraDataAttrs), { role: 'gridcell' }), elStyle: {
328
+ width: props.width
329
+ }, 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: [
330
+ 'fc-daygrid-cell-inner',
331
+ props.fgLiquidHeight ? 'fc-liquid' : ''
332
+ ].join(' ') },
333
+ createElement("div", { ref: this.headerWrapElRef, className: "fc-flex-column" }, !renderProps.isDisabled && (props.showDayNumber || hasCustomDayCellContent(options)) && (createElement("div", { className: "fc-daygrid-cell-header" },
227
334
  createElement(InnerContent, { elTag: "a", elClasses: [
228
- 'fc-daygrid-day-number',
335
+ 'fc-daygrid-cell-number',
229
336
  isMonthStart && 'fc-daygrid-month-start',
230
- ], elAttrs: Object.assign(Object.assign({}, buildNavLinkAttrs(context, date)), { id: state.dayNumberId }) }))) : props.showDayNumber ? (
231
- // for creating correct amount of space (see issue #7162)
232
- createElement("div", { className: "fc-daygrid-day-top", style: { visibility: 'hidden' } },
233
- createElement("a", { className: "fc-daygrid-day-number" }, "\u00A0"))) : undefined,
234
- createElement("div", { className: "fc-daygrid-day-events", ref: props.fgContentElRef },
235
- props.fgContent,
236
- createElement("div", { className: "fc-daygrid-day-bottom", style: { marginTop: props.moreMarginTop } },
237
- createElement(TableCellMoreLink, { allDayDate: date, singlePlacements: props.singlePlacements, moreCnt: props.moreCnt, alignmentElRef: rootElRef, alignGridTop: !props.showDayNumber, extraDateSpan: props.extraDateSpan, dateProfile: props.dateProfile, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange }))),
238
- createElement("div", { className: "fc-daygrid-day-bg" }, props.bgContent)))));
337
+ ], elAttrs: buildNavLinkAttrs(context, props.date) })))),
338
+ createElement("div", { className: "fc-daygrid-cell-main", style: {
339
+ height: props.fgLiquidHeight ? '' : props.fgHeight
340
+ } }, props.fg),
341
+ createElement("div", { className: "fc-daygrid-cell-footer", style: props.fgLiquidHeight
342
+ ? { position: 'relative', top: props.fgHeight }
343
+ : {} },
344
+ 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 })),
345
+ props.bg))));
346
+ }
347
+ componentDidMount() {
348
+ const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
349
+ const headerWrapEl = this.headerWrapElRef.current; // "
350
+ // TODO: only attach this if refs props present
351
+ this.detachInnerHeight = watchHeight(innerEl, (height) => {
352
+ setRef(this.props.innerHeightRef, height);
353
+ });
354
+ this.detachHeaderHeight = watchHeight(headerWrapEl, (height) => {
355
+ setRef(this.props.headerHeightRef, height);
356
+ });
357
+ }
358
+ componentWillUnmount() {
359
+ this.detachInnerHeight();
360
+ this.detachHeaderHeight();
361
+ setRef(this.props.innerHeightRef, null);
362
+ setRef(this.props.headerHeightRef, null);
239
363
  }
240
364
  }
365
+ // Utils
366
+ // -------------------------------------------------------------------------------------------------
241
367
  function renderTopInner(props) {
242
368
  return props.dayNumberText || createElement(Fragment, null, "\u00A0");
243
369
  }
@@ -257,26 +383,43 @@ function shouldDisplayMonthStart(date, currentRange, dateEnv) {
257
383
  (dateEnv.getDay(date) === 1 && date.valueOf() < currentEnd.valueOf()));
258
384
  }
259
385
 
260
- function generateSegKey(seg) {
386
+ /*
387
+ Unique per-START-column, good for cataloging by top
388
+ */
389
+ function getSegStartId(seg) {
261
390
  return seg.eventRange.instance.instanceId + ':' + seg.firstCol;
262
391
  }
263
- function generateSegUid(seg) {
264
- return generateSegKey(seg) + ':' + seg.lastCol;
265
- }
266
- function computeFgSegPlacement(segs, // assumed already sorted
267
- dayMaxEvents, dayMaxEventRows, strictOrder, segHeights, maxContentHeight, cells) {
268
- let hierarchy = new DayGridSegHierarchy((segEntry) => {
269
- // TODO: more DRY with generateSegUid
270
- let segUid = segs[segEntry.index].eventRange.instance.instanceId +
271
- ':' + segEntry.span.start +
272
- ':' + (segEntry.span.end - 1);
273
- // if no thickness known, assume 1 (if 0, so small it always fits)
274
- return segHeights[segUid] || 1;
275
- });
276
- hierarchy.allowReslicing = true;
392
+ /*
393
+ Unique per-START-and-END-column, good for cataloging by width/height
394
+ */
395
+ function getSegSpanId(seg) {
396
+ return getSegStartId(seg) + ':' + seg.lastCol;
397
+ }
398
+ function computeFgSegVerticals(segs, segHeightMap, // keyed by segSpanId
399
+ cells, topOrigin, maxHeight, strictOrder, dayMaxEvents, dayMaxEventRows) {
400
+ // initialize column-based arrays
401
+ const colCnt = cells.length;
402
+ const hiddenSegsByCol = [];
403
+ const heightsByCol = [];
404
+ for (let col = 0; col < colCnt; col++) {
405
+ hiddenSegsByCol.push([]);
406
+ heightsByCol.push(0);
407
+ }
408
+ // create entries to be given to DayGridSegHierarchy
409
+ const segEntries = segs.map((seg, index) => ({
410
+ index: index,
411
+ seg,
412
+ span: {
413
+ start: seg.firstCol,
414
+ end: seg.lastCol + 1,
415
+ },
416
+ }));
417
+ // configure hierarchy position-generator
418
+ let hierarchy = new DayGridSegHierarchy((segEntry) => (segHeightMap.get(getSegSpanId(segs[segEntry.index]))));
419
+ hierarchy.allowReslicing = false;
277
420
  hierarchy.strictOrder = strictOrder;
278
421
  if (dayMaxEvents === true || dayMaxEventRows === true) {
279
- hierarchy.maxCoord = maxContentHeight;
422
+ hierarchy.maxCoord = maxHeight;
280
423
  hierarchy.hiddenConsumes = true;
281
424
  }
282
425
  else if (typeof dayMaxEvents === 'number') {
@@ -286,172 +429,30 @@ dayMaxEvents, dayMaxEventRows, strictOrder, segHeights, maxContentHeight, cells)
286
429
  hierarchy.maxStackCnt = dayMaxEventRows;
287
430
  hierarchy.hiddenConsumes = true;
288
431
  }
289
- // create segInputs only for segs with known heights
290
- let segInputs = [];
291
- let unknownHeightSegs = [];
292
- for (let i = 0; i < segs.length; i += 1) {
293
- let seg = segs[i];
294
- let segUid = generateSegUid(seg);
295
- let eventHeight = segHeights[segUid];
296
- if (eventHeight != null) {
297
- segInputs.push({
298
- index: i,
299
- span: {
300
- start: seg.firstCol,
301
- end: seg.lastCol + 1,
302
- },
303
- });
304
- }
305
- else {
306
- unknownHeightSegs.push(seg);
307
- }
308
- }
309
- let hiddenEntries = hierarchy.addSegs(segInputs);
310
- let segRects = hierarchy.toRects();
311
- let { singleColPlacements, multiColPlacements, leftoverMargins } = placeRects(segRects, segs, cells);
312
- let moreCnts = [];
313
- let moreMarginTops = [];
314
- // add segs with unknown heights
315
- for (let seg of unknownHeightSegs) {
316
- multiColPlacements[seg.firstCol].push({
317
- seg,
318
- isVisible: false,
319
- isAbsolute: true,
320
- absoluteTop: 0,
321
- marginTop: 0,
322
- });
323
- for (let col = seg.firstCol; col <= seg.lastCol; col += 1) {
324
- singleColPlacements[col].push({
325
- seg: resliceSeg(seg, col, col + 1, cells),
326
- isVisible: false,
327
- isAbsolute: false,
328
- absoluteTop: 0,
329
- marginTop: 0,
330
- });
432
+ // compile segTops & heightsByCol
433
+ const hiddenSegEntries = hierarchy.addSegs(segEntries);
434
+ const segRects = hierarchy.toRects();
435
+ const segTops = {};
436
+ for (const segRect of segRects) {
437
+ const seg = segs[segRect.index];
438
+ segTops[getSegStartId(seg)] = topOrigin + segRect.levelCoord;
439
+ let { start: col, end: endCol } = segRect.span;
440
+ for (; col < endCol; col++) {
441
+ heightsByCol[col] = Math.max(heightsByCol[col], segRect.levelCoord + segRect.thickness);
331
442
  }
332
443
  }
333
- // add the hidden entries
334
- for (let col = 0; col < cells.length; col += 1) {
335
- moreCnts.push(0);
336
- }
337
- for (let hiddenEntry of hiddenEntries) {
338
- let seg = segs[hiddenEntry.index];
339
- let hiddenSpan = hiddenEntry.span;
340
- multiColPlacements[hiddenSpan.start].push({
341
- seg: resliceSeg(seg, hiddenSpan.start, hiddenSpan.end, cells),
342
- isVisible: false,
343
- isAbsolute: true,
344
- absoluteTop: 0,
345
- marginTop: 0,
346
- });
347
- for (let col = hiddenSpan.start; col < hiddenSpan.end; col += 1) {
348
- moreCnts[col] += 1;
349
- singleColPlacements[col].push({
350
- seg: resliceSeg(seg, col, col + 1, cells),
351
- isVisible: false,
352
- isAbsolute: false,
353
- absoluteTop: 0,
354
- marginTop: 0,
355
- });
444
+ // compile # of invisible segs per-column
445
+ for (const hiddenSegEntry of hiddenSegEntries) {
446
+ const { span } = hiddenSegEntry;
447
+ const hiddenSeg = segs[hiddenSegEntry.index];
448
+ for (let col = span.start; col < span.end; col++) {
449
+ hiddenSegsByCol[col].push(hiddenSeg);
356
450
  }
357
451
  }
358
- // deal with leftover margins
359
- for (let col = 0; col < cells.length; col += 1) {
360
- moreMarginTops.push(leftoverMargins[col]);
361
- }
362
- return { singleColPlacements, multiColPlacements, moreCnts, moreMarginTops };
363
- }
364
- // rects ordered by top coord, then left
365
- function placeRects(allRects, segs, cells) {
366
- let rectsByEachCol = groupRectsByEachCol(allRects, cells.length);
367
- let singleColPlacements = [];
368
- let multiColPlacements = [];
369
- let leftoverMargins = [];
370
- for (let col = 0; col < cells.length; col += 1) {
371
- let rects = rectsByEachCol[col];
372
- // compute all static segs in singlePlacements
373
- let singlePlacements = [];
374
- let currentHeight = 0;
375
- let currentMarginTop = 0;
376
- for (let rect of rects) {
377
- let seg = segs[rect.index];
378
- singlePlacements.push({
379
- seg: resliceSeg(seg, col, col + 1, cells),
380
- isVisible: true,
381
- isAbsolute: false,
382
- absoluteTop: rect.levelCoord,
383
- marginTop: rect.levelCoord - currentHeight,
384
- });
385
- currentHeight = rect.levelCoord + rect.thickness;
386
- }
387
- // compute mixed static/absolute segs in multiPlacements
388
- let multiPlacements = [];
389
- currentHeight = 0;
390
- currentMarginTop = 0;
391
- for (let rect of rects) {
392
- let seg = segs[rect.index];
393
- let isAbsolute = rect.span.end - rect.span.start > 1; // multi-column?
394
- let isFirstCol = rect.span.start === col;
395
- currentMarginTop += rect.levelCoord - currentHeight; // amount of space since bottom of previous seg
396
- currentHeight = rect.levelCoord + rect.thickness; // height will now be bottom of current seg
397
- if (isAbsolute) {
398
- currentMarginTop += rect.thickness;
399
- if (isFirstCol) {
400
- multiPlacements.push({
401
- seg: resliceSeg(seg, rect.span.start, rect.span.end, cells),
402
- isVisible: true,
403
- isAbsolute: true,
404
- absoluteTop: rect.levelCoord,
405
- marginTop: 0,
406
- });
407
- }
408
- }
409
- else if (isFirstCol) {
410
- multiPlacements.push({
411
- seg: resliceSeg(seg, rect.span.start, rect.span.end, cells),
412
- isVisible: true,
413
- isAbsolute: false,
414
- absoluteTop: rect.levelCoord,
415
- marginTop: currentMarginTop, // claim the margin
416
- });
417
- currentMarginTop = 0;
418
- }
419
- }
420
- singleColPlacements.push(singlePlacements);
421
- multiColPlacements.push(multiPlacements);
422
- leftoverMargins.push(currentMarginTop);
423
- }
424
- return { singleColPlacements, multiColPlacements, leftoverMargins };
425
- }
426
- function groupRectsByEachCol(rects, colCnt) {
427
- let rectsByEachCol = [];
428
- for (let col = 0; col < colCnt; col += 1) {
429
- rectsByEachCol.push([]);
430
- }
431
- for (let rect of rects) {
432
- for (let col = rect.span.start; col < rect.span.end; col += 1) {
433
- rectsByEachCol[col].push(rect);
434
- }
435
- }
436
- return rectsByEachCol;
437
- }
438
- function resliceSeg(seg, spanStart, spanEnd, cells) {
439
- if (seg.firstCol === spanStart && seg.lastCol === spanEnd - 1) {
440
- return seg;
441
- }
442
- let eventRange = seg.eventRange;
443
- let origRange = eventRange.range;
444
- let slicedRange = intersectRanges(origRange, {
445
- start: cells[spanStart].date,
446
- end: addDays(cells[spanEnd - 1].date, 1),
447
- });
448
- return Object.assign(Object.assign({}, seg), { firstCol: spanStart, lastCol: spanEnd - 1, eventRange: {
449
- def: eventRange.def,
450
- ui: Object.assign(Object.assign({}, eventRange.ui), { durationEditable: false }),
451
- instance: eventRange.instance,
452
- range: slicedRange,
453
- }, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() });
452
+ return [segTops, heightsByCol, hiddenSegsByCol];
454
453
  }
454
+ // DayGridSegHierarchy
455
+ // -------------------------------------------------------------------------------------------------
455
456
  class DayGridSegHierarchy extends SegHierarchy {
456
457
  constructor() {
457
458
  super(...arguments);
@@ -499,74 +500,205 @@ class DayGridSegHierarchy extends SegHierarchy {
499
500
  }
500
501
  }
501
502
 
502
- class TableRow extends DateComponent {
503
+ class DayGridEventHarness extends Component {
503
504
  constructor() {
504
505
  super(...arguments);
505
- this.cellElRefs = new RefMap(); // the <td>
506
- this.frameElRefs = new RefMap(); // the fc-daygrid-day-frame
507
- this.fgElRefs = new RefMap(); // the fc-daygrid-day-events
508
- this.segHarnessRefs = new RefMap(); // indexed by "instanceId:firstCol"
506
+ // ref
509
507
  this.rootElRef = createRef();
510
- this.state = {
511
- framePositions: null,
512
- maxContentHeight: null,
513
- segHeights: {},
508
+ }
509
+ render() {
510
+ const { props } = this;
511
+ return (createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
512
+ }
513
+ componentDidMount() {
514
+ const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
515
+ this.detachHeight = watchHeight(rootEl, (height) => {
516
+ setRef(this.props.heightRef, height);
517
+ });
518
+ }
519
+ componentWillUnmount() {
520
+ this.detachHeight();
521
+ }
522
+ }
523
+
524
+ const DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' });
525
+ const COMPACT_CELL_WIDTH = 80;
526
+ class DayGridRow extends BaseComponent {
527
+ constructor() {
528
+ super(...arguments);
529
+ this.cellInnerHeightRefMap = new RefMap(() => {
530
+ afterSize(this.handleInnerHeights);
531
+ });
532
+ this.cellHeaderHeightRefMap = new RefMap(() => {
533
+ afterSize(this.handleHeaderHeights);
534
+ });
535
+ this.segHeightRefMap = new RefMap(() => {
536
+ afterSize(this.handleSegHeights);
537
+ });
538
+ this.handleRootEl = (rootEl) => {
539
+ this.rootEl = rootEl;
540
+ setRef(this.props.rootElRef, rootEl);
541
+ };
542
+ // Sizing
543
+ // -----------------------------------------------------------------------------------------------
544
+ this.handleHeaderHeights = () => {
545
+ const cellHeaderHeightMap = this.cellHeaderHeightRefMap.current;
546
+ let max = 0;
547
+ for (const height of cellHeaderHeightMap.values()) {
548
+ max = Math.max(max, height);
549
+ }
550
+ if (this.state.headerHeight !== max) {
551
+ this.setState({ headerHeight: max });
552
+ }
514
553
  };
515
- this.handleResize = (isForced) => {
516
- if (isForced) {
517
- this.updateSizing(true); // isExternal=true
554
+ this.handleInnerHeights = () => {
555
+ const { props } = this;
556
+ const fgLiquidHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
557
+ const cellInnerHeightMap = this.cellInnerHeightRefMap.current;
558
+ let max = 0;
559
+ for (const height of cellInnerHeightMap.values()) {
560
+ max = Math.max(max, height);
561
+ }
562
+ if (fgLiquidHeight) {
563
+ if (this.state.innerHeight !== max) {
564
+ this.setState({ innerHeight: max }); // will trigger event rerender
565
+ }
566
+ }
567
+ else {
568
+ setRef(props.innerHeightRef, max);
518
569
  }
519
570
  };
571
+ this.handleSegHeights = () => {
572
+ this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger event rerender
573
+ };
520
574
  }
521
575
  render() {
522
- let { props, state, context } = this;
523
- let { options } = context;
524
- let colCnt = props.cells.length;
525
- let businessHoursByCol = splitSegsByFirstCol(props.businessHourSegs, colCnt);
526
- let bgEventSegsByCol = splitSegsByFirstCol(props.bgEventSegs, colCnt);
527
- let highlightSegsByCol = splitSegsByFirstCol(this.getHighlightSegs(), colCnt);
528
- let mirrorSegsByCol = splitSegsByFirstCol(this.getMirrorSegs(), colCnt);
529
- let { singleColPlacements, multiColPlacements, moreCnts, moreMarginTops } = computeFgSegPlacement(sortEventSegs(props.fgEventSegs, options.eventOrder), props.dayMaxEvents, props.dayMaxEventRows, options.eventOrderStrict, state.segHeights, state.maxContentHeight, props.cells);
530
- let isForcedInvisible = // TODO: messy way to compute this
576
+ const { props, state, context, cellInnerHeightRefMap, cellHeaderHeightRefMap } = this;
577
+ const { cells } = props;
578
+ const { options } = context;
579
+ const weekDate = props.cells[0].date;
580
+ const colCnt = props.cells.length;
581
+ const fgLiquidHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
582
+ // TODO: memoize? sort all types of segs?
583
+ const fgEventSegs = sortEventSegs(props.fgEventSegs, options.eventOrder);
584
+ // TODO: memoize?
585
+ const fgEventSegsByCol = splitSegsByCol(fgEventSegs, colCnt);
586
+ const bgEventSegsByCol = splitSegsByCol(props.bgEventSegs, colCnt);
587
+ const businessHoursByCol = splitSegsByCol(props.businessHourSegs, colCnt);
588
+ const highlightSegsByCol = splitSegsByCol(this.getHighlightSegs(), colCnt); // TODO: doesn't need standins
589
+ const mirrorSegsByCol = splitSegsByCol(this.getMirrorSegs(), colCnt); // TODO: doesn't need standins
590
+ // TODO: memoize?
591
+ const [segTops, heightsByCol, hiddenSegsByCol] = computeFgSegVerticals(fgEventSegs, this.segHeightRefMap.current, cells, state.headerHeight, (fgLiquidHeight && state.innerHeight != null && state.headerHeight != null)
592
+ ? state.innerHeight - state.headerHeight
593
+ : undefined, options.eventOrderStrict, props.dayMaxEvents, props.dayMaxEventRows);
594
+ const forcedInvisibleMap = // TODO: more convenient/DRY
531
595
  (props.eventDrag && props.eventDrag.affectedInstances) ||
532
596
  (props.eventResize && props.eventResize.affectedInstances) ||
533
597
  {};
534
- return (createElement("tr", { ref: this.rootElRef, role: "row" },
535
- props.renderIntro && props.renderIntro(),
598
+ return (createElement("div", { role: props.cellGroup ? undefined : 'row', className: [
599
+ 'fc-daygrid-row',
600
+ props.forceVSpacing
601
+ ? 'fc-daygrid-row-spacious'
602
+ : props.compact
603
+ ? 'fc-daygrid-row-compact'
604
+ : '',
605
+ props.cellGroup ? 'fc-flex-row' : 'fc-row',
606
+ 'fc-rel',
607
+ props.className || '',
608
+ ].join(' '), style: {
609
+ minHeight: props.minHeight,
610
+ }, ref: this.handleRootEl },
536
611
  props.cells.map((cell, col) => {
537
- let normalFgNodes = this.renderFgSegs(col, props.forPrint ? singleColPlacements[col] : multiColPlacements[col], props.todayRange, isForcedInvisible);
538
- let mirrorFgNodes = this.renderFgSegs(col, buildMirrorPlacements(mirrorSegsByCol[col], multiColPlacements), props.todayRange, {}, Boolean(props.eventDrag), Boolean(props.eventResize), false);
539
- return (createElement(TableCell, { key: cell.key, elRef: this.cellElRefs.createRef(cell.key), innerElRef: this.frameElRefs.createRef(cell.key) /* FF <td> problem, but okay to use for left/right. TODO: rename prop */, dateProfile: props.dateProfile, date: cell.date, showDayNumber: props.showDayNumbers, showWeekNumber: props.showWeekNumbers && col === 0, forceDayTop: props.showWeekNumbers /* even displaying weeknum for row, not necessarily day */, todayRange: props.todayRange, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, extraRenderProps: cell.extraRenderProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, extraDateSpan: cell.extraDateSpan, moreCnt: moreCnts[col], moreMarginTop: moreMarginTops[col], singlePlacements: singleColPlacements[col], fgContentElRef: this.fgElRefs.createRef(cell.key), fgContent: ( // Fragment scopes the keys
540
- createElement(Fragment, null,
612
+ const normalFgNodes = this.renderFgSegs(fgEventSegsByCol[col], segTops, props.todayRange, forcedInvisibleMap);
613
+ const mirrorFgNodes = this.renderFgSegs(mirrorSegsByCol[col], segTops, props.todayRange, {}, // forcedInvisibleMap
614
+ Boolean(props.eventDrag), Boolean(props.eventResize), false);
615
+ return (createElement(DayGridCell, { key: cell.key, dateProfile: props.dateProfile, todayRange: props.todayRange, date: cell.date, showDayNumber: props.showDayNumbers,
616
+ // content
617
+ segs: fgEventSegsByCol[col], hiddenSegs: hiddenSegsByCol[col], fgLiquidHeight: fgLiquidHeight, fg: (createElement(Fragment, null,
541
618
  createElement(Fragment, null, normalFgNodes),
542
- createElement(Fragment, null, mirrorFgNodes))), bgContent: ( // Fragment scopes the keys
543
- createElement(Fragment, null,
619
+ createElement(Fragment, null, mirrorFgNodes))), bg: (createElement(Fragment, null,
544
620
  this.renderFillSegs(highlightSegsByCol[col], 'highlight'),
545
621
  this.renderFillSegs(businessHoursByCol[col], 'non-business'),
546
- this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))), minHeight: props.cellMinHeight }));
547
- })));
622
+ this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))), eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
623
+ // render hooks
624
+ extraRenderProps: cell.extraRenderProps, extraDateSpan: cell.extraDateSpan, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames,
625
+ // dimensions
626
+ fgHeight: heightsByCol[col], width: props.colWidth,
627
+ // refs
628
+ innerHeightRef: cellInnerHeightRefMap.createRef(cell.key), headerHeightRef: cellHeaderHeightRefMap.createRef(cell.key) }));
629
+ }),
630
+ props.showWeekNumbers && (createElement(WeekNumberContainer, { elTag: "a", elClasses: ['fc-daygrid-week-number'], elAttrs: buildNavLinkAttrs(context, weekDate, 'week'), date: weekDate, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }))));
631
+ }
632
+ renderFgSegs(segs, segTops, todayRange, forcedInvisibleMap, isDragging, isResizing, isDateSelecting) {
633
+ const { props, context, segHeightRefMap } = this;
634
+ const { isRtl } = context;
635
+ const { colWidth, eventSelection } = props;
636
+ const colCnt = props.cells.length;
637
+ const defaultDisplayEventEnd = props.cells.length === 1;
638
+ const isMirror = isDragging || isResizing || isDateSelecting;
639
+ const nodes = [];
640
+ for (const seg of segs) {
641
+ const { left, right, width } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
642
+ // TODO: optimize ID creation? all related
643
+ const { instanceId } = seg.eventRange.instance;
644
+ const segSpanId = getSegSpanId(seg);
645
+ const segStartId = getSegStartId(seg);
646
+ const top = segTops[segStartId];
647
+ const isVisible = !seg.isStandin &&
648
+ top != null &&
649
+ !forcedInvisibleMap[instanceId];
650
+ /*
651
+ TODO: is this comment still relevant? vvvvvvvv
652
+ known bug: events that are force to be list-item but span multiple days still take up space in later columns
653
+ todo: in print view, for multi-day events, don't display title within non-start/end segs
654
+ */
655
+ nodes.push(createElement(DayGridEventHarness, { key: segSpanId, style: {
656
+ visibility: isVisible ? '' : 'hidden',
657
+ top,
658
+ left,
659
+ right,
660
+ width,
661
+ }, heightRef: (isMirror || seg.isStandin)
662
+ ? null
663
+ : segHeightRefMap.createRef(segSpanId) }, hasListItemDisplay(seg) ? (createElement(DayGridListEvent, Object.assign({ seg: seg, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange)))) : (createElement(DayGridBlockEvent, Object.assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange))))));
664
+ }
665
+ return nodes;
548
666
  }
549
- componentDidMount() {
550
- this.updateSizing(true);
551
- this.context.addResizeHandler(this.handleResize);
667
+ renderFillSegs(segs, fillType) {
668
+ const { props, context } = this;
669
+ const { isRtl } = context;
670
+ const { todayRange, colWidth } = props;
671
+ const colCnt = props.cells.length;
672
+ const nodes = [];
673
+ for (const seg of segs) {
674
+ const { left, right, width } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
675
+ const isVisible = !seg.isStandin;
676
+ nodes.push(createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: {
677
+ visibility: isVisible ? '' : 'hidden',
678
+ left,
679
+ right,
680
+ width,
681
+ } }, fillType === 'bg-event' ?
682
+ createElement(BgEvent, Object.assign({ seg: seg }, getSegMeta(seg, todayRange))) :
683
+ renderFill(fillType)));
684
+ }
685
+ return createElement(Fragment, {}, ...nodes);
552
686
  }
553
- componentDidUpdate(prevProps, prevState) {
554
- let currentProps = this.props;
555
- this.updateSizing(!isPropsEqual(prevProps, currentProps));
687
+ // Sizing
688
+ // -----------------------------------------------------------------------------------------------
689
+ componentDidMount() {
690
+ const { rootEl } = this; // TODO: make dynamic with useEffect
691
+ this.disconnectHeight = watchHeight(rootEl, (contentHeight) => {
692
+ setRef(this.props.heightRef, contentHeight);
693
+ });
556
694
  }
557
695
  componentWillUnmount() {
558
- this.context.removeResizeHandler(this.handleResize);
559
- }
560
- getHighlightSegs() {
561
- let { props } = this;
562
- if (props.eventDrag && props.eventDrag.segs.length) { // messy check
563
- return props.eventDrag.segs;
564
- }
565
- if (props.eventResize && props.eventResize.segs.length) { // messy check
566
- return props.eventResize.segs;
567
- }
568
- return props.dateSelectionSegs;
696
+ this.disconnectHeight();
697
+ setRef(this.props.heightRef, null);
698
+ setRef(this.props.innerHeightRef, null);
569
699
  }
700
+ // Utils
701
+ // -----------------------------------------------------------------------------------------------
570
702
  getMirrorSegs() {
571
703
  let { props } = this;
572
704
  if (props.eventResize && props.eventResize.segs.length) { // messy check
@@ -574,412 +706,459 @@ class TableRow extends DateComponent {
574
706
  }
575
707
  return [];
576
708
  }
577
- renderFgSegs(col, segPlacements, todayRange, isForcedInvisible, isDragging, isResizing, isDateSelecting) {
578
- let { context } = this;
579
- let { eventSelection } = this.props;
580
- let { framePositions } = this.state;
581
- let defaultDisplayEventEnd = this.props.cells.length === 1; // colCnt === 1
582
- let isMirror = isDragging || isResizing || isDateSelecting;
583
- let nodes = [];
584
- if (framePositions) {
585
- for (let placement of segPlacements) {
586
- let { seg } = placement;
587
- let { instanceId } = seg.eventRange.instance;
588
- let isVisible = placement.isVisible && !isForcedInvisible[instanceId];
589
- let isAbsolute = placement.isAbsolute;
590
- let left = '';
591
- let right = '';
592
- if (isAbsolute) {
593
- if (context.isRtl) {
594
- right = 0;
595
- left = framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol];
596
- }
597
- else {
598
- left = 0;
599
- right = framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol];
600
- }
601
- }
602
- /*
603
- known bug: events that are force to be list-item but span multiple days still take up space in later columns
604
- todo: in print view, for multi-day events, don't display title within non-start/end segs
605
- */
606
- nodes.push(createElement("div", { className: 'fc-daygrid-event-harness' + (isAbsolute ? ' fc-daygrid-event-harness-abs' : ''), key: generateSegKey(seg), ref: isMirror ? null : this.segHarnessRefs.createRef(generateSegUid(seg)), style: {
607
- visibility: isVisible ? '' : 'hidden',
608
- marginTop: isAbsolute ? '' : placement.marginTop,
609
- top: isAbsolute ? placement.absoluteTop : '',
610
- left,
611
- right,
612
- } }, hasListItemDisplay(seg) ? (createElement(TableListItemEvent, Object.assign({ seg: seg, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange)))) : (createElement(TableBlockEvent, Object.assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange))))));
613
- }
614
- }
615
- return nodes;
616
- }
617
- renderFillSegs(segs, fillType) {
618
- let { isRtl } = this.context;
619
- let { todayRange } = this.props;
620
- let { framePositions } = this.state;
621
- let nodes = [];
622
- if (framePositions) {
623
- for (let seg of segs) {
624
- let leftRightCss = isRtl ? {
625
- right: 0,
626
- left: framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol],
627
- } : {
628
- left: 0,
629
- right: framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol],
630
- };
631
- nodes.push(createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-daygrid-bg-harness", style: leftRightCss }, fillType === 'bg-event' ?
632
- createElement(BgEvent, Object.assign({ seg: seg }, getSegMeta(seg, todayRange))) :
633
- renderFill(fillType)));
634
- }
635
- }
636
- return createElement(Fragment, {}, ...nodes);
637
- }
638
- updateSizing(isExternalSizingChange) {
639
- let { props, state, frameElRefs } = this;
640
- if (!props.forPrint &&
641
- props.clientWidth !== null // positioning ready?
642
- ) {
643
- if (isExternalSizingChange) {
644
- let frameEls = props.cells.map((cell) => frameElRefs.currentMap[cell.key]);
645
- if (frameEls.length) {
646
- let originEl = this.rootElRef.current;
647
- let newPositionCache = new PositionCache(originEl, frameEls, true, // isHorizontal
648
- false);
649
- if (!state.framePositions || !state.framePositions.similarTo(newPositionCache)) {
650
- this.setState({
651
- framePositions: new PositionCache(originEl, frameEls, true, // isHorizontal
652
- false),
653
- });
654
- }
655
- }
656
- }
657
- const oldSegHeights = this.state.segHeights;
658
- const newSegHeights = this.querySegHeights();
659
- const limitByContentHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
660
- this.safeSetState({
661
- // HACK to prevent oscillations of events being shown/hidden from max-event-rows
662
- // Essentially, once you compute an element's height, never null-out.
663
- // TODO: always display all events, as visibility:hidden?
664
- segHeights: Object.assign(Object.assign({}, oldSegHeights), newSegHeights),
665
- maxContentHeight: limitByContentHeight ? this.computeMaxContentHeight() : null,
666
- });
667
- }
668
- }
669
- querySegHeights() {
670
- let segElMap = this.segHarnessRefs.currentMap;
671
- let segHeights = {};
672
- // get the max height amongst instance segs
673
- for (let segUid in segElMap) {
674
- let height = Math.round(segElMap[segUid].getBoundingClientRect().height);
675
- segHeights[segUid] = Math.max(segHeights[segUid] || 0, height);
709
+ getHighlightSegs() {
710
+ let { props } = this;
711
+ if (props.eventDrag && props.eventDrag.segs.length) { // messy check
712
+ return props.eventDrag.segs;
676
713
  }
677
- return segHeights;
678
- }
679
- computeMaxContentHeight() {
680
- let firstKey = this.props.cells[0].key;
681
- let cellEl = this.cellElRefs.currentMap[firstKey];
682
- let fcContainerEl = this.fgElRefs.currentMap[firstKey];
683
- return cellEl.getBoundingClientRect().bottom - fcContainerEl.getBoundingClientRect().top;
684
- }
685
- getCellEls() {
686
- let elMap = this.cellElRefs.currentMap;
687
- return this.props.cells.map((cell) => elMap[cell.key]);
688
- }
689
- }
690
- TableRow.addStateEquality({
691
- segHeights: isPropsEqual,
692
- });
693
- function buildMirrorPlacements(mirrorSegs, colPlacements) {
694
- if (!mirrorSegs.length) {
695
- return [];
696
- }
697
- let topsByInstanceId = buildAbsoluteTopHash(colPlacements); // TODO: cache this at first render?
698
- return mirrorSegs.map((seg) => ({
699
- seg,
700
- isVisible: true,
701
- isAbsolute: true,
702
- absoluteTop: topsByInstanceId[seg.eventRange.instance.instanceId],
703
- marginTop: 0,
704
- }));
705
- }
706
- function buildAbsoluteTopHash(colPlacements) {
707
- let topsByInstanceId = {};
708
- for (let placements of colPlacements) {
709
- for (let placement of placements) {
710
- topsByInstanceId[placement.seg.eventRange.instance.instanceId] = placement.absoluteTop;
714
+ if (props.eventResize && props.eventResize.segs.length) { // messy check
715
+ return props.eventResize.segs;
711
716
  }
717
+ return props.dateSelectionSegs;
712
718
  }
713
- return topsByInstanceId;
714
719
  }
715
720
 
716
- class TableRows extends DateComponent {
721
+ class DayGridRows extends DateComponent {
717
722
  constructor() {
718
723
  super(...arguments);
724
+ // memo
719
725
  this.splitBusinessHourSegs = memoize(splitSegsByRow);
720
726
  this.splitBgEventSegs = memoize(splitSegsByRow);
721
727
  this.splitFgEventSegs = memoize(splitSegsByRow);
722
728
  this.splitDateSelectionSegs = memoize(splitSegsByRow);
723
729
  this.splitEventDrag = memoize(splitInteractionByRow);
724
730
  this.splitEventResize = memoize(splitInteractionByRow);
725
- this.rowRefs = new RefMap();
731
+ // internal
732
+ this.rowHeightRefMap = new RefMap((height, key) => {
733
+ // HACKy way of syncing RefMap results with prop
734
+ const { rowHeightRefMap } = this.props;
735
+ if (rowHeightRefMap) {
736
+ rowHeightRefMap.handleValue(height, key);
737
+ }
738
+ });
739
+ this.handleRootEl = (rootEl) => {
740
+ this.rootEl = rootEl;
741
+ if (rootEl) {
742
+ this.context.registerInteractiveComponent(this, {
743
+ el: rootEl,
744
+ isHitComboAllowed: this.props.isHitComboAllowed,
745
+ });
746
+ }
747
+ else {
748
+ this.context.unregisterInteractiveComponent(this);
749
+ }
750
+ };
726
751
  }
727
752
  render() {
728
- let { props, context } = this;
729
- let rowCnt = props.cells.length;
730
- let businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt);
731
- let bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt);
753
+ let { props, state, context, rowHeightRefMap } = this;
754
+ let { options } = context;
755
+ let rowCnt = props.cellRows.length;
732
756
  let fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, rowCnt);
757
+ let bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt);
758
+ let businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt);
733
759
  let dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt);
734
760
  let eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt);
735
761
  let eventResizeByRow = this.splitEventResize(props.eventResize, rowCnt);
736
- // for DayGrid view with many rows, force a min-height on cells so doesn't appear squished
737
- // choose 7 because a month view will have max 6 rows
738
- let cellMinHeight = (rowCnt >= 7 && props.clientWidth) ?
739
- props.clientWidth / context.options.aspectRatio / 6 :
740
- null;
741
- return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(Fragment, null, props.cells.map((cells, row) => (createElement(TableRow, { ref: this.rowRefs.createRef(row), key: cells.length
742
- ? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */
743
- : row // in case there are no cells (like when resource view is loading)
744
- , showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: props.dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, cellMinHeight: cellMinHeight, forPrint: props.forPrint })))))));
762
+ // whether the ROW should expand in height
763
+ // (not to be confused with whether the fg events within the row should be molded by height of row)
764
+ let isHeightAuto = getIsHeightAuto(options);
765
+ // maintain at least aspectRatio for cells?
766
+ let rowMinHeight = (state.width != null && (rowCnt >= 7 || // TODO: better way to infer if across single-month boundary
767
+ isHeightAuto)) ? state.width / context.options.aspectRatio / 6 // okay to hardcode 6 (weeks) ?
768
+ : null;
769
+ 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,
770
+ // if not auto-height, distribute height of container somewhat evently to rows
771
+ // (treat all as zero, distribute height, then ensure min-heights -- the inner content height)
772
+ className: isHeightAuto ? '' : 'fc-grow fc-basis0',
773
+ // content
774
+ 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,
775
+ // dimensions
776
+ colWidth: props.colWidth, minHeight: rowMinHeight,
777
+ // refs
778
+ heightRef: rowHeightRefMap.createRef(cells[0].key) })))));
745
779
  }
746
780
  componentDidMount() {
747
- this.registerInteractiveComponent();
748
- }
749
- componentDidUpdate() {
750
- // for if started with zero cells
751
- this.registerInteractiveComponent();
752
- }
753
- registerInteractiveComponent() {
754
- if (!this.rootEl) {
755
- // HACK: need a daygrid wrapper parent to do positioning
756
- // NOTE: a daygrid resource view w/o resources can have zero cells
757
- const firstCellEl = this.rowRefs.currentMap[0].getCellEls()[0];
758
- const rootEl = firstCellEl ? firstCellEl.closest('.fc-daygrid-body') : null;
759
- if (rootEl) {
760
- this.rootEl = rootEl;
761
- this.context.registerInteractiveComponent(this, {
762
- el: rootEl,
763
- isHitComboAllowed: this.props.isHitComboAllowed,
764
- });
765
- }
766
- }
781
+ this.unwatchWidth = watchWidth(this.rootEl, (width) => {
782
+ this.setState({ width });
783
+ });
767
784
  }
768
785
  componentWillUnmount() {
769
- if (this.rootEl) {
770
- this.context.unregisterInteractiveComponent(this);
771
- this.rootEl = null;
772
- }
786
+ this.unwatchWidth();
773
787
  }
774
788
  // Hit System
775
- // ----------------------------------------------------------------------------------------------------
776
- prepareHits() {
777
- this.rowPositions = new PositionCache(this.rootEl, this.rowRefs.collect().map((rowObj) => rowObj.getCellEls()[0]), // first cell el in each row. TODO: not optimal
778
- false, true);
779
- this.colPositions = new PositionCache(this.rootEl, this.rowRefs.currentMap[0].getCellEls(), // cell els in first row
780
- true, // horizontal
781
- false);
782
- }
783
- queryHit(positionLeft, positionTop) {
784
- let { colPositions, rowPositions } = this;
785
- let col = colPositions.leftToIndex(positionLeft);
786
- let row = rowPositions.topToIndex(positionTop);
787
- if (row != null && col != null) {
788
- let cell = this.props.cells[row][col];
789
- return {
790
- dateProfile: this.props.dateProfile,
791
- dateSpan: Object.assign({ range: this.getCellRange(row, col), allDay: true }, cell.extraDateSpan),
792
- dayEl: this.getCellEl(row, col),
793
- rect: {
794
- left: colPositions.lefts[col],
795
- right: colPositions.rights[col],
796
- top: rowPositions.tops[row],
797
- bottom: rowPositions.bottoms[row],
798
- },
799
- layer: 0,
800
- };
801
- }
802
- return null;
803
- }
804
- getCellEl(row, col) {
805
- return this.rowRefs.currentMap[row].getCellEls()[col]; // TODO: not optimal
806
- }
807
- getCellRange(row, col) {
808
- let start = this.props.cells[row][col].date;
809
- let end = addDays(start, 1);
810
- return { start, end };
789
+ // -----------------------------------------------------------------------------------------------
790
+ queryHit(positionLeft, positionTop, elWidth) {
791
+ const { props, context } = this;
792
+ const colCnt = props.cellRows[0].length;
793
+ const { col, left, right } = computeColFromPosition(positionLeft, elWidth, props.colWidth, colCnt, context.isRtl);
794
+ const { row, top, bottom } = computeRowFromPosition(positionTop, props.cellRows, this.rowHeightRefMap.current);
795
+ const cell = props.cellRows[row][col];
796
+ const cellStartDate = cell.date;
797
+ const cellEndDate = addDays(cellStartDate, 1);
798
+ return {
799
+ dateProfile: props.dateProfile,
800
+ dateSpan: Object.assign({ range: {
801
+ start: cellStartDate,
802
+ end: cellEndDate,
803
+ }, allDay: true }, cell.extraDateSpan),
804
+ // HACK. TODO: This is expensive to do every hit-query
805
+ dayEl: getCellEl(getRowEl(this.rootEl, row), col),
806
+ rect: {
807
+ left,
808
+ right,
809
+ top,
810
+ bottom,
811
+ },
812
+ layer: 0,
813
+ };
811
814
  }
812
815
  }
816
+ // Utils
817
+ // -------------------------------------------------------------------------------------------------
813
818
  function isSegAllDay(seg) {
814
819
  return seg.eventRange.def.allDay;
815
820
  }
816
821
 
817
- class Table extends DateComponent {
822
+ class HeaderRow extends BaseComponent {
823
+ render() {
824
+ const { props } = this;
825
+ return (createElement("div", { role: props.cellGroup ? undefined : 'row', className: [
826
+ props.cellGroup ? 'fc-flex-row' : 'fc-row',
827
+ props.className || '',
828
+ ].join(' ') }, props.cells.map((cell) => (createElement(Fragment, { key: props.getHeaderModelKey(cell) }, props.renderHeaderContent(cell, props.tierNum, undefined, // innerHeightRef
829
+ props.colWidth))))));
830
+ }
831
+ }
832
+
833
+ function DayGridHeader(props) {
834
+ return (createElement("div", { className: [
835
+ 'fc-rowgroup',
836
+ 'fc-content-box',
837
+ ...(props.extraClassNames || []),
838
+ ].join(' '), style: {
839
+ width: props.width,
840
+ paddingLeft: props.paddingLeft,
841
+ paddingRight: props.paddingRight,
842
+ } }, props.headerTiers.map((cells, tierNum) => (createElement(HeaderRow, { key: tierNum, tierNum: tierNum, cells: cells, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey, colWidth: props.colWidth })))));
843
+ }
844
+
845
+ class DayGridLayoutNormal extends BaseComponent {
818
846
  constructor() {
819
847
  super(...arguments);
820
- this.elRef = createRef();
821
- this.needsScrollReset = false;
848
+ this.handleScroller = (scroller) => {
849
+ setRef(this.props.scrollerRef, scroller);
850
+ };
851
+ this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
852
+ this.setState({ leftScrollbarWidth });
853
+ };
854
+ this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
855
+ this.setState({ rightScrollbarWidth });
856
+ };
822
857
  }
823
858
  render() {
824
- let { props } = this;
825
- let { dayMaxEventRows, dayMaxEvents, expandRows } = props;
826
- let limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true;
827
- // if rows can't expand to fill fixed height, can't do balanced-height event limit
828
- // TODO: best place to normalize these options?
829
- if (limitViaBalanced && !expandRows) {
830
- limitViaBalanced = false;
831
- dayMaxEventRows = null;
832
- dayMaxEvents = null;
833
- }
834
- let classNames = [
835
- 'fc-daygrid-body',
836
- limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced',
837
- expandRows ? '' : 'fc-daygrid-body-natural', // will height of one row depend on the others?
838
- ];
839
- return (createElement("div", { ref: this.elRef, className: classNames.join(' '), style: {
840
- // these props are important to give this wrapper correct dimensions for interactions
841
- // TODO: if we set it here, can we avoid giving to inner tables?
842
- width: props.clientWidth,
843
- minWidth: props.tableMinWidth,
844
- } },
845
- createElement("table", { role: "presentation", className: "fc-scrollgrid-sync-table", style: {
846
- width: props.clientWidth,
847
- minWidth: props.tableMinWidth,
848
- height: expandRows ? props.clientHeight : '',
849
- } },
850
- props.colGroupNode,
851
- createElement("tbody", { role: "presentation" },
852
- createElement(TableRows, { dateProfile: props.dateProfile, cells: props.cells, renderRowIntro: props.renderRowIntro, showWeekNumbers: props.showWeekNumbers, clientWidth: props.clientWidth, clientHeight: props.clientHeight, businessHourSegs: props.businessHourSegs, bgEventSegs: props.bgEventSegs, fgEventSegs: props.fgEventSegs, dateSelectionSegs: props.dateSelectionSegs, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed })))));
859
+ const { props, state, context } = this;
860
+ const { options } = context;
861
+ const verticalScrollbars = !props.forPrint && !getIsHeightAuto(options);
862
+ const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
863
+ return (createElement(Fragment, null,
864
+ options.dayHeaders && (createElement(DayGridHeader, { headerTiers: props.headerTiers, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey,
865
+ // render hooks
866
+ extraClassNames: [
867
+ 'fc-daygrid-header',
868
+ stickyHeaderDates ? 'fc-sticky-header' : '',
869
+ ],
870
+ // dimensions
871
+ paddingLeft: state.leftScrollbarWidth, paddingRight: state.rightScrollbarWidth })),
872
+ createElement(Scroller, { vertical: verticalScrollbars, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth, elClassNames: [
873
+ 'fc-daygrid-body',
874
+ 'fc-rowgroup',
875
+ 'fc-flex-column',
876
+ verticalScrollbars ? 'fc-liquid' : '',
877
+ ], ref: this.handleScroller },
878
+ createElement(DayGridRows // .fc-grow
879
+ , { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed,
880
+ // content
881
+ fgEventSegs: props.fgEventSegs, bgEventSegs: props.bgEventSegs, businessHourSegs: props.businessHourSegs, dateSelectionSegs: props.dateSelectionSegs, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
882
+ // refs
883
+ rowHeightRefMap: props.rowHeightRefMap }))));
853
884
  }
854
- componentDidMount() {
855
- this.requestScrollReset();
885
+ }
886
+
887
+ class DayGridLayoutPannable extends BaseComponent {
888
+ constructor() {
889
+ super(...arguments);
890
+ this.headerScrollerRef = createRef();
891
+ this.bodyScrollerRef = createRef();
892
+ this.footerScrollerRef = createRef();
893
+ // Sizing
894
+ // -----------------------------------------------------------------------------------------------
895
+ this.handleWidth = (width) => {
896
+ this.setState({ width });
897
+ };
898
+ this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
899
+ this.setState({ leftScrollbarWidth });
900
+ };
901
+ this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
902
+ this.setState({ rightScrollbarWidth });
903
+ };
856
904
  }
857
- componentDidUpdate(prevProps) {
858
- if (prevProps.dateProfile !== this.props.dateProfile) {
859
- this.requestScrollReset();
860
- }
861
- else {
862
- this.flushScrollReset();
863
- }
905
+ render() {
906
+ const { props, state, context } = this;
907
+ const { options } = context;
908
+ const verticalScrollbars = !props.forPrint && !getIsHeightAuto(options);
909
+ const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
910
+ const stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(options);
911
+ const colCnt = props.cellRows[0].length;
912
+ const [canvasWidth, colWidth] = computeColWidth(colCnt, props.dayMinWidth, state.width);
913
+ return (createElement(Fragment, null,
914
+ options.dayHeaders && (createElement(Scroller, { horizontal: true, hideScrollbars: true, elClassNames: [
915
+ 'fc-daygrid-header',
916
+ 'fc-rowgroup',
917
+ stickyHeaderDates ? 'fc-sticky-header' : ''
918
+ ], ref: this.headerScrollerRef },
919
+ createElement(DayGridHeader, { headerTiers: props.headerTiers, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey,
920
+ // dimensions
921
+ colWidth: colWidth, width: canvasWidth, paddingLeft: state.leftScrollbarWidth, paddingRight: state.rightScrollbarWidth }))),
922
+ createElement(Scroller, { vertical: verticalScrollbars, horizontal: true, hideScrollbars: stickyFooterScrollbar, widthRef: this.handleWidth, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth, elClassNames: [
923
+ 'fc-daygrid-body',
924
+ 'fc-rowgroup',
925
+ 'fc-flex-column',
926
+ verticalScrollbars ? 'fc-liquid' : '',
927
+ ], ref: this.bodyScrollerRef },
928
+ createElement(DayGridRows // .fc-grow
929
+ , { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed,
930
+ // content
931
+ fgEventSegs: props.fgEventSegs, bgEventSegs: props.bgEventSegs, businessHourSegs: props.businessHourSegs, dateSelectionSegs: props.dateSelectionSegs, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
932
+ // dimensions
933
+ colWidth: colWidth, width: canvasWidth,
934
+ // refs
935
+ rowHeightRefMap: props.rowHeightRefMap })),
936
+ Boolean(stickyFooterScrollbar) && (createElement(Scroller, { ref: this.footerScrollerRef, horizontal: true, elClassNames: ['fc-sticky-footer'], elStyle: {
937
+ marginTop: '-1px', // HACK
938
+ } },
939
+ createElement("div", { style: {
940
+ width: canvasWidth,
941
+ height: '1px', // HACK
942
+ } })))));
864
943
  }
865
- requestScrollReset() {
866
- this.needsScrollReset = true;
867
- this.flushScrollReset();
868
- }
869
- flushScrollReset() {
870
- if (this.needsScrollReset &&
871
- this.props.clientWidth // sizes computed?
872
- ) {
873
- const subjectEl = getScrollSubjectEl(this.elRef.current, this.props.dateProfile);
874
- if (subjectEl) {
875
- const originEl = subjectEl.closest('.fc-daygrid-body');
876
- const scrollEl = originEl.closest('.fc-scroller');
877
- const scrollTop = subjectEl.getBoundingClientRect().top -
878
- originEl.getBoundingClientRect().top;
879
- scrollEl.scrollTop = scrollTop ? (scrollTop + 1) : 0; // overcome border
880
- }
881
- this.needsScrollReset = false;
882
- }
944
+ // Lifecycle
945
+ // -----------------------------------------------------------------------------------------------
946
+ componentDidMount() {
947
+ // scroller
948
+ const ScrollerSyncer = getScrollerSyncerClass(this.context.pluginHooks);
949
+ this.syncedScroller = new ScrollerSyncer(true); // horizontal=true
950
+ setRef(this.props.scrollerRef, this.syncedScroller);
951
+ this.updateSyncedScroller();
883
952
  }
884
- }
885
- function getScrollSubjectEl(containerEl, dateProfile) {
886
- let el;
887
- if (dateProfile.currentRangeUnit.match(/year|month/)) {
888
- el = containerEl.querySelector(`[data-date="${formatIsoMonthStr(dateProfile.currentDate)}-01"]`);
889
- // even if view is month-based, first-of-month might be hidden...
953
+ componentDidUpdate() {
954
+ // scroller
955
+ this.updateSyncedScroller();
890
956
  }
891
- if (!el) {
892
- el = containerEl.querySelector(`[data-date="${formatDayString(dateProfile.currentDate)}"]`);
893
- // could still be hidden if an interior-view hidden day
957
+ componentWillUnmount() {
958
+ // scroller
959
+ this.syncedScroller.destroy();
960
+ }
961
+ // Scrolling
962
+ // -----------------------------------------------------------------------------------------------
963
+ updateSyncedScroller() {
964
+ const { isRtl } = this.context;
965
+ this.syncedScroller.handleChildren([
966
+ this.headerScrollerRef.current,
967
+ this.bodyScrollerRef.current,
968
+ this.footerScrollerRef.current,
969
+ ], isRtl);
894
970
  }
895
- return el;
896
971
  }
897
972
 
898
- class DayTableSlicer extends Slicer {
973
+ class DayGridLayout extends BaseComponent {
899
974
  constructor() {
900
975
  super(...arguments);
901
- this.forceDayIfListItem = true;
976
+ // ref
977
+ this.scrollerRef = createRef();
978
+ this.rowHeightRefMap = new RefMap();
979
+ // Scrolling
980
+ // -----------------------------------------------------------------------------------------------
981
+ this.timeScrollResponder = new ScrollResponder((_time) => {
982
+ // HACK to scroll to day
983
+ const rowHeightMap = this.rowHeightRefMap.current;
984
+ const scroller = this.scrollerRef.current;
985
+ const scrollTop = computeTopFromDate(this.props.dateProfile.currentDate, this.props.cellRows, rowHeightMap);
986
+ if (scrollTop != null) {
987
+ scroller.scrollTo({ y: scrollTop });
988
+ return true;
989
+ }
990
+ return false;
991
+ });
902
992
  }
903
- sliceRange(dateRange, dayTableModel) {
904
- return dayTableModel.sliceRange(dateRange);
993
+ render() {
994
+ const { props, context } = this;
995
+ const { options } = context;
996
+ const commonLayoutProps = Object.assign(Object.assign({}, props), { scrollerRef: this.scrollerRef, rowHeightRefMap: this.rowHeightRefMap });
997
+ 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)))));
998
+ }
999
+ // Lifecycle
1000
+ // -----------------------------------------------------------------------------------------------
1001
+ componentDidMount() {
1002
+ const { context } = this;
1003
+ const { options } = context;
1004
+ context.emitter.on('_timeScrollRequest', this.timeScrollResponder.handleScroll);
1005
+ this.timeScrollResponder.handleScroll(options.scrollTime);
1006
+ }
1007
+ componentDidUpdate(prevProps) {
1008
+ const { options } = this.context;
1009
+ if (prevProps.dateProfile !== this.props.dateProfile && options.scrollTimeReset) {
1010
+ this.timeScrollResponder.handleScroll(options.scrollTime);
1011
+ }
1012
+ else {
1013
+ this.timeScrollResponder.drain();
1014
+ }
1015
+ }
1016
+ componentWillUnmount() {
1017
+ this.context.emitter.off('_timeScrollRequest', this.timeScrollResponder.handleScroll);
905
1018
  }
906
1019
  }
907
1020
 
908
- class DayTable extends DateComponent {
1021
+ const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
1022
+ class DayOfWeekHeaderCell extends BaseComponent {
909
1023
  constructor() {
910
1024
  super(...arguments);
911
- this.slicer = new DayTableSlicer();
912
- this.tableRef = createRef();
1025
+ // ref
1026
+ this.innerElRef = createRef();
913
1027
  }
914
1028
  render() {
915
1029
  let { props, context } = this;
916
- return (createElement(Table, Object.assign({ ref: this.tableRef }, this.slicer.sliceProps(props, props.dateProfile, props.nextDayThreshold, context, props.dayTableModel), { dateProfile: props.dateProfile, cells: props.dayTableModel.cells, colGroupNode: props.colGroupNode, tableMinWidth: props.tableMinWidth, renderRowIntro: props.renderRowIntro, dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows, showWeekNumbers: props.showWeekNumbers, expandRows: props.expandRows, headerAlignElRef: props.headerAlignElRef, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: props.forPrint })));
1030
+ let { dateEnv, theme, viewApi, options } = context;
1031
+ let date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT
1032
+ let dateMeta = {
1033
+ dow: props.dow,
1034
+ isDisabled: false,
1035
+ isFuture: false,
1036
+ isPast: false,
1037
+ isToday: false,
1038
+ isOther: false,
1039
+ };
1040
+ let text = dateEnv.format(date, props.dayHeaderFormat);
1041
+ let renderProps = Object.assign(Object.assign(Object.assign(Object.assign({ date }, dateMeta), { view: viewApi }), props.extraRenderProps), { text });
1042
+ return (createElement(ContentContainer, { elTag: 'div', elClasses: [
1043
+ ...getDayClassNames(dateMeta, theme),
1044
+ ...(props.extraClassNames || []),
1045
+ 'fc-header-cell',
1046
+ 'fc-cell',
1047
+ props.colWidth != null ? '' : 'fc-liquid',
1048
+ 'fc-flex-column',
1049
+ 'fc-align-center',
1050
+ ], elAttrs: props.extraDataAttrs, elStyle: {
1051
+ width: props.colWidth != null // TODO: DRY
1052
+ ? props.colWidth * (props.colSpan || 1)
1053
+ : undefined,
1054
+ }, 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: [
1055
+ 'fc-flex-column',
1056
+ props.isSticky ? 'fc-sticky-x' : '',
1057
+ ].join(' ') },
1058
+ createElement(InnerContent, { elTag: "a", elClasses: [
1059
+ 'fc-cell-inner',
1060
+ 'fc-padding-sm',
1061
+ ], elAttrs: {
1062
+ 'aria-label': dateEnv.format(date, WEEKDAY_FORMAT),
1063
+ } })))));
1064
+ }
1065
+ componentDidMount() {
1066
+ const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
1067
+ // TODO: only attach this if refs props present
1068
+ this.disconectInnerHeight = watchHeight(innerEl, (height) => {
1069
+ setRef(this.props.innerHeightRef, height);
1070
+ });
1071
+ }
1072
+ componentWillUnmount() {
1073
+ this.disconectInnerHeight();
1074
+ }
1075
+ }
1076
+
1077
+ function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) {
1078
+ return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt);
1079
+ }
1080
+ // Computes a default column header formatting string if `colFormat` is not explicitly defined
1081
+ function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
1082
+ // if more than one week row, or if there are a lot of columns with not much space,
1083
+ // put just the day numbers will be in each cell
1084
+ if (!datesRepDistinctDays || dayCnt > 10) {
1085
+ return createFormatter({ weekday: 'short' }); // "Sat"
917
1086
  }
1087
+ if (dayCnt > 1) {
1088
+ return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12"
1089
+ }
1090
+ return createFormatter({ weekday: 'long' }); // "Saturday"
918
1091
  }
919
1092
 
920
- class DayTableView extends TableView {
1093
+ class DayGridView extends BaseComponent {
921
1094
  constructor() {
922
1095
  super(...arguments);
1096
+ // memo
923
1097
  this.buildDayTableModel = memoize(buildDayTableModel);
924
- this.headerRef = createRef();
925
- this.tableRef = createRef();
926
- // can't override any lifecycle methods from parent
1098
+ this.buildHeaderTiers = memoize(buildHeaderTiers);
1099
+ this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
1100
+ // internal
1101
+ this.slicer = new DayTableSlicer();
927
1102
  }
928
1103
  render() {
929
- let { options, dateProfileGenerator } = this.context;
930
- let { props } = this;
931
- let dayTableModel = this.buildDayTableModel(props.dateProfile, dateProfileGenerator);
932
- let headerContent = options.dayHeaders && (createElement(DayHeader, { ref: this.headerRef, dateProfile: props.dateProfile, dates: dayTableModel.headerDates, datesRepDistinctDays: dayTableModel.rowCnt === 1 }));
933
- let bodyContent = (contentArg) => (createElement(DayTable, { ref: this.tableRef, dateProfile: props.dateProfile, dayTableModel: dayTableModel, businessHours: props.businessHours, dateSelection: props.dateSelection, eventStore: props.eventStore, eventUiBases: props.eventUiBases, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, nextDayThreshold: options.nextDayThreshold, colGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, dayMaxEvents: options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows, showWeekNumbers: options.weekNumbers, expandRows: !props.isHeightAuto, headerAlignElRef: this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint }));
934
- return options.dayMinWidth
935
- ? this.renderHScrollLayout(headerContent, bodyContent, dayTableModel.colCnt, options.dayMinWidth)
936
- : this.renderSimpleLayout(headerContent, bodyContent);
1104
+ const { props, context } = this;
1105
+ const { options } = context;
1106
+ const dayTableModel = this.buildDayTableModel(props.dateProfile, context.dateProfileGenerator);
1107
+ const datesRepDistinctDays = dayTableModel.rowCnt === 1;
1108
+ const headerTiers = this.buildHeaderTiers(dayTableModel.headerDates, datesRepDistinctDays);
1109
+ const slicedProps = this.slicer.sliceProps(props, props.dateProfile, options.nextDayThreshold, context, dayTableModel);
1110
+ const dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dayTableModel.colCnt);
1111
+ return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(DayGridLayout, { dateProfile: props.dateProfile, todayRange: todayRange, cellRows: dayTableModel.cellRows, forPrint: props.forPrint, className: 'fc-daygrid-view',
1112
+ // header content
1113
+ headerTiers: headerTiers, renderHeaderContent: (model, tier, innerHeightRef, colWidth) => {
1114
+ if (model.date) {
1115
+ return (createElement(DateHeaderCell, Object.assign({}, model, { dateProfile: props.dateProfile, todayRange: todayRange, navLink: dayTableModel.colCnt > 1, dayHeaderFormat: dayHeaderFormat, colSpan: model.colSpan, colWidth: colWidth })));
1116
+ }
1117
+ else {
1118
+ return (createElement(DayOfWeekHeaderCell, Object.assign({}, model, { dayHeaderFormat: dayHeaderFormat, colSpan: model.colSpan, colWidth: colWidth })));
1119
+ }
1120
+ }, getHeaderModelKey: (model) => {
1121
+ // can use model.key???
1122
+ if (model.date) {
1123
+ return model.date.toUTCString();
1124
+ }
1125
+ return model.dow;
1126
+ },
1127
+ // body content
1128
+ fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection }))));
937
1129
  }
938
1130
  }
939
- function buildDayTableModel(dateProfile, dateProfileGenerator) {
940
- let daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator);
941
- return new DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
942
- }
943
1131
 
944
- class TableDateProfileGenerator extends DateProfileGenerator {
945
- // Computes the date range that will be rendered
946
- buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay) {
947
- let renderRange = super.buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay);
948
- let { props } = this;
949
- return buildDayTableRenderRange({
950
- currentRange: renderRange,
951
- snapToWeek: /^(year|month)$/.test(currentRangeUnit),
952
- fixedWeekCount: props.fixedWeekCount,
953
- dateEnv: props.dateEnv,
1132
+ /*
1133
+ TODO: is it even worth doing this "advanced" version?
1134
+ */
1135
+ class HeaderRowAdvanced extends BaseComponent {
1136
+ constructor() {
1137
+ super(...arguments);
1138
+ // ref
1139
+ this.innerHeightRefMap = new RefMap(() => {
1140
+ afterSize(this.handleInnerHeights);
954
1141
  });
1142
+ this.handleInnerHeights = () => {
1143
+ const innerHeightMap = this.innerHeightRefMap.current;
1144
+ let max = 0;
1145
+ for (const innerHeight of innerHeightMap.values()) {
1146
+ max = Math.max(max, innerHeight);
1147
+ }
1148
+ if (this.currentInnerHeight !== max) {
1149
+ this.currentInnerHeight = max;
1150
+ setRef(this.props.innerHeightRef, max);
1151
+ }
1152
+ };
955
1153
  }
956
- }
957
- function buildDayTableRenderRange(props) {
958
- let { dateEnv, currentRange } = props;
959
- let { start, end } = currentRange;
960
- let endOfWeek;
961
- // year and month views should be aligned with weeks. this is already done for week
962
- if (props.snapToWeek) {
963
- start = dateEnv.startOfWeek(start);
964
- // make end-of-week if not already
965
- endOfWeek = dateEnv.startOfWeek(end);
966
- if (endOfWeek.valueOf() !== end.valueOf()) {
967
- end = addWeeks(endOfWeek, 1);
968
- }
969
- }
970
- // ensure 6 weeks
971
- if (props.fixedWeekCount) {
972
- // TODO: instead of these date-math gymnastics (for multimonth view),
973
- // compute dateprofiles of all months, then use start of first and end of last.
974
- let lastMonthRenderStart = dateEnv.startOfWeek(dateEnv.startOfMonth(addDays(currentRange.end, -1)));
975
- let rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
976
- diffWeeks(lastMonthRenderStart, end));
977
- end = addWeeks(end, 6 - rowCnt);
1154
+ render() {
1155
+ const { props } = this;
1156
+ return (createElement("div", { role: 'row', className: 'fc-row', style: { height: props.height } }, props.cells.map((cell) => {
1157
+ const key = props.getHeaderModelKey(cell);
1158
+ return (createElement(Fragment, { key: props.getHeaderModelKey(cell) }, props.renderHeaderContent(cell, props.tierNum, this.innerHeightRefMap.createRef(key), // innerHeightRef
1159
+ props.colWidth)));
1160
+ })));
978
1161
  }
979
- return { start, end };
980
1162
  }
981
1163
 
982
- var css_248z = ":root{--fc-daygrid-event-dot-width:8px}.fc-daygrid-day-events:after,.fc-daygrid-day-events:before,.fc-daygrid-day-frame:after,.fc-daygrid-day-frame:before,.fc-daygrid-event-harness:after,.fc-daygrid-event-harness:before{clear:both;content:\"\";display:table}.fc .fc-daygrid-body{position:relative;z-index:1}.fc .fc-daygrid-day.fc-day-today{background-color:var(--fc-today-bg-color)}.fc .fc-daygrid-day-frame{min-height:100%;position:relative}.fc .fc-daygrid-day-top{display:flex;flex-direction:row-reverse}.fc .fc-day-other .fc-daygrid-day-top{opacity:.3}.fc .fc-daygrid-day-number{padding:4px;position:relative;z-index:4}.fc .fc-daygrid-month-start{font-size:1.1em;font-weight:700}.fc .fc-daygrid-day-events{margin-top:1px}.fc .fc-daygrid-body-balanced .fc-daygrid-day-events{left:0;position:absolute;right:0}.fc .fc-daygrid-body-unbalanced .fc-daygrid-day-events{min-height:2em;position:relative}.fc .fc-daygrid-body-natural .fc-daygrid-day-events{margin-bottom:1em}.fc .fc-daygrid-event-harness{position:relative}.fc .fc-daygrid-event-harness-abs{left:0;position:absolute;right:0;top:0}.fc .fc-daygrid-bg-harness{bottom:0;position:absolute;top:0}.fc .fc-daygrid-day-bg .fc-non-business{z-index:1}.fc .fc-daygrid-day-bg .fc-bg-event{z-index:2}.fc .fc-daygrid-day-bg .fc-highlight{z-index:3}.fc .fc-daygrid-event{margin-top:1px;z-index:6}.fc .fc-daygrid-event.fc-event-mirror{z-index:7}.fc .fc-daygrid-day-bottom{font-size:.85em;margin:0 2px}.fc .fc-daygrid-day-bottom:after,.fc .fc-daygrid-day-bottom:before{clear:both;content:\"\";display:table}.fc .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 .fc-daygrid-more-link:hover{background-color:rgba(0,0,0,.1)}.fc .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 .fc-more-popover .fc-popover-body{min-width:220px;padding:10px}.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-more-link{float:left}.fc-direction-ltr .fc-daygrid-week-number{border-radius:0 0 3px 0;left:0}.fc-direction-rtl .fc-daygrid-more-link{float:right}.fc-direction-rtl .fc-daygrid-week-number{border-radius:0 0 0 3px;right:0}.fc-liquid-hack .fc-daygrid-day-frame{position:static}.fc-daygrid-event{border-radius:3px;font-size:var(--fc-small-font-size);position:relative;white-space:nowrap}.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;display:flex;padding:2px 0}.fc-daygrid-dot-event .fc-event-title{flex-grow:1;flex-shrink:1;font-weight:700;min-width:0;overflow:hidden}.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-direction-ltr .fc-daygrid-event .fc-event-time{margin-right:3px}.fc-direction-rtl .fc-daygrid-event .fc-event-time{margin-left:3px}";
983
- injectStyles(css_248z);
984
-
985
- export { DayTableView as DayGridView, DayTable, DayTableSlicer, Table, TableDateProfileGenerator, TableRows, TableView, buildDayTableModel, buildDayTableRenderRange };
1164
+ export { COMPACT_CELL_WIDTH, DateHeaderCell, DayGridLayout, DayGridRow, DayGridRows, DayGridView, DayOfWeekHeaderCell, DayTableSlicer, HeaderRow, HeaderRowAdvanced, TableDateProfileGenerator, buildDayTableModel, buildDayTableRenderRange, computeColFromPosition, computeColWidth, createDayHeaderFormatter, getCellEl, getRowEl };