@fullcalendar/daygrid 6.1.15 → 7.0.0-beta.1

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,252 @@
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, buildEventRangeTimeText, EventContainer, getEventRangeAnchorAttrs, MoreLinkContainer, getEventRangeMeta, DateComponent, DayCellContainer, hasCustomDayCellContent, addMs, SegHierarchy, buildEntryKey, intersectSpans, RefMap, afterSize, sortEventSegs, WeekNumberContainer, buildEventRangeKey, BgEvent, renderFill, memoize, getIsHeightAuto, watchWidth, getStickyHeaderDates, Scroller, getStickyFooterScrollbar, getScrollerSyncerClass, ViewContainer, NowTimer } from '@fullcalendar/core/internal.js';
2
+ import { createRef, createElement, Fragment, Component } from '@fullcalendar/core/preact.js';
3
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, adjust = 0) {
82
+ let top = 0;
83
+ for (const cells of cellRows) {
84
+ const start = cells[0].date;
85
+ const end = cells[cells.length - 1].date;
86
+ const key = start.toISOString();
87
+ if (date >= start && date <= end) {
88
+ return top;
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
79
93
  }
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 })));
94
+ top += rowHeight + adjust;
82
95
  }
96
+ return top;
97
+ }
98
+ function computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl) {
99
+ let left;
100
+ let right;
101
+ let width;
102
+ if (colWidth != null) {
103
+ width = (seg.lastCol - seg.firstCol + 1) * colWidth;
104
+ if (isRtl) {
105
+ right = seg.firstCol * colWidth;
106
+ }
107
+ else {
108
+ left = seg.firstCol * colWidth;
109
+ }
110
+ }
111
+ else {
112
+ const colWidthFrac = 1 / colCnt;
113
+ width = fracToCssDim((seg.lastCol - seg.firstCol + 1) * colWidthFrac);
114
+ if (isRtl) {
115
+ right = fracToCssDim(seg.firstCol * colWidthFrac);
116
+ }
117
+ else {
118
+ left = fracToCssDim(seg.firstCol * colWidthFrac);
119
+ }
120
+ }
121
+ return { left, right, width };
122
+ }
123
+ function computeColFromPosition(positionLeft, elWidth, colWidth, colCnt, isRtl) {
124
+ const realColWidth = colWidth != null ? colWidth : elWidth / colCnt;
125
+ const colFromLeft = Math.floor(positionLeft / realColWidth);
126
+ const col = isRtl ? (colCnt - colFromLeft - 1) : colFromLeft;
127
+ const left = colFromLeft * realColWidth;
128
+ const right = left + realColWidth;
129
+ return { col, left, right };
130
+ }
131
+ function computeRowFromPosition(positionTop, cellRows, rowHeightMap) {
132
+ let row = 0;
133
+ let top = 0;
134
+ let bottom = 0;
135
+ for (const cells of cellRows) {
136
+ const key = cells[0].key;
137
+ top = bottom;
138
+ bottom = top + rowHeightMap.get(key);
139
+ if (positionTop < bottom) {
140
+ break;
141
+ }
142
+ row++;
143
+ }
144
+ return { row, top, bottom };
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();
200
+ setRef(this.props.innerHeightRef, null);
92
201
  }
93
- return byRow;
94
202
  }
95
- function splitSegsByFirstCol(segs, colCnt) {
96
- let byCol = [];
97
- for (let i = 0; i < colCnt; i += 1) {
98
- byCol[i] = [];
203
+
204
+ function splitSegsByRow(segs, rowCnt) {
205
+ const byRow = [];
206
+ for (let row = 0; row < rowCnt; row++) {
207
+ byRow[row] = [];
99
208
  }
100
- for (let seg of segs) {
101
- byCol[seg.firstCol].push(seg);
209
+ for (const seg of segs) {
210
+ byRow[seg.row].push(seg);
102
211
  }
103
- return byCol;
212
+ return byRow;
104
213
  }
105
214
  function splitInteractionByRow(ui, rowCnt) {
106
- let byRow = [];
215
+ const byRow = [];
107
216
  if (!ui) {
108
- for (let i = 0; i < rowCnt; i += 1) {
109
- byRow[i] = null;
217
+ for (let row = 0; row < rowCnt; row++) {
218
+ byRow[row] = null;
110
219
  }
111
220
  }
112
221
  else {
113
- for (let i = 0; i < rowCnt; i += 1) {
114
- byRow[i] = {
222
+ for (let row = 0; row < rowCnt; row++) {
223
+ byRow[row] = {
115
224
  affectedInstances: ui.affectedInstances,
116
225
  isEvent: ui.isEvent,
117
226
  segs: [],
118
227
  };
119
228
  }
120
- for (let seg of ui.segs) {
229
+ for (const seg of ui.segs) {
121
230
  byRow[seg.row].segs.push(seg);
122
231
  }
123
232
  }
124
233
  return byRow;
125
234
  }
235
+ function splitSegsByCol(segs, colCnt) {
236
+ let byCol = [];
237
+ for (let col = 0; col < colCnt; col++) {
238
+ byCol.push([]);
239
+ }
240
+ for (let seg of segs) {
241
+ for (let col = seg.firstCol; col <= seg.lastCol; col++) {
242
+ if (seg.firstCol !== col) {
243
+ seg = Object.assign(Object.assign({}, seg), { firstCol: col, lastCol: col, isStart: false, isEnd: seg.isEnd && seg.lastCol === col, isStandin: true });
244
+ }
245
+ byCol[col].push(seg);
246
+ }
247
+ }
248
+ return byCol;
249
+ }
126
250
 
127
251
  const DEFAULT_TABLE_EVENT_TIME_FORMAT = createFormatter({
128
252
  hour: 'numeric',
@@ -140,21 +264,21 @@ function hasListItemDisplay(seg) {
140
264
  );
141
265
  }
142
266
 
143
- class TableBlockEvent extends BaseComponent {
267
+ class DayGridBlockEvent extends BaseComponent {
144
268
  render() {
145
269
  let { props } = this;
146
- 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 })));
270
+ return (createElement(StandardEvent, Object.assign({}, props, { elClasses: ['fc-daygrid-event', 'fc-daygrid-block-event', 'fc-h-event'], defaultTimeFormat: DEFAULT_TABLE_EVENT_TIME_FORMAT, defaultDisplayEventEnd: props.defaultDisplayEventEnd, disableResizing: !props.eventRange.def.allDay })));
147
271
  }
148
272
  }
149
273
 
150
- class TableListItemEvent extends BaseComponent {
274
+ class DayGridListEvent extends BaseComponent {
151
275
  render() {
152
276
  let { props, context } = this;
153
277
  let { options } = context;
154
- let { seg } = props;
278
+ let { eventRange } = props;
155
279
  let timeFormat = options.eventTimeFormat || DEFAULT_TABLE_EVENT_TIME_FORMAT;
156
- let timeText = buildSegTimeText(seg, timeFormat, context, true, props.defaultDisplayEventEnd);
157
- return (createElement(EventContainer, Object.assign({}, props, { elTag: "a", elClasses: ['fc-daygrid-event', 'fc-daygrid-dot-event'], elAttrs: getSegAnchorAttrs(props.seg, context), defaultGenerator: renderInnerContent, timeText: timeText, isResizing: false, isDateSelecting: false })));
280
+ let timeText = buildEventRangeTimeText(eventRange, timeFormat, context, true, props.defaultDisplayEventEnd);
281
+ return (createElement(EventContainer, Object.assign({}, props, { elTag: "a", elClasses: ['fc-daygrid-event', 'fc-daygrid-dot-event'], elAttrs: getEventRangeAnchorAttrs(eventRange, context), defaultGenerator: renderInnerContent, timeText: timeText, isResizing: false, isDateSelecting: false })));
158
282
  }
159
283
  }
160
284
  function renderInnerContent(renderProps) {
@@ -164,80 +288,84 @@ function renderInnerContent(renderProps) {
164
288
  createElement("div", { className: "fc-event-title" }, renderProps.event.title || createElement(Fragment, null, "\u00A0"))));
165
289
  }
166
290
 
167
- class TableCellMoreLink extends BaseComponent {
168
- constructor() {
169
- super(...arguments);
170
- this.compileSegs = memoize(compileSegs);
171
- }
291
+ class DayGridMoreLink extends BaseComponent {
172
292
  render() {
173
293
  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) ||
294
+ return (createElement(MoreLinkContainer, { elClasses: ['fc-daygrid-more-link'], dateProfile: props.dateProfile, todayRange: props.todayRange, allDayDate: props.allDayDate, segs: props.segs, hiddenSegs: props.hiddenSegs, alignmentElRef: props.alignmentElRef, alignGridTop: props.alignGridTop, extraDateSpan: props.extraDateSpan, popoverContent: () => {
295
+ let forcedInvisibleMap = // TODO: more convenient/DRY
296
+ (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
177
297
  (props.eventResize ? props.eventResize.affectedInstances : null) ||
178
298
  {};
179
- return (createElement(Fragment, null, allSegs.map((seg) => {
180
- 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))))));
299
+ return (createElement(Fragment, null, props.segs.map((seg) => {
300
+ let { eventRange } = seg;
301
+ let instanceId = eventRange.instance.instanceId;
302
+ return (createElement("div", { key: instanceId, style: {
303
+ visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '',
304
+ } }, hasListItemDisplay(seg) ? (createElement(DayGridListEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getEventRangeMeta(eventRange, props.todayRange)))) : (createElement(DayGridBlockEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getEventRangeMeta(eventRange, props.todayRange))))));
184
305
  })));
185
306
  } }));
186
307
  }
187
308
  }
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
309
 
200
- const DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' });
201
- class TableCell extends DateComponent {
310
+ class DayGridCell extends DateComponent {
202
311
  constructor() {
203
312
  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
- };
313
+ // ref
314
+ this.innerElRef = createRef();
315
+ this.headerWrapElRef = createRef();
212
316
  }
213
317
  render() {
214
- let { context, props, state, rootElRef } = this;
318
+ let { props, context } = this;
215
319
  let { options, dateEnv } = context;
216
- let { date, dateProfile } = props;
217
- // TODO: memoize this?
320
+ // TODO: memoize this
218
321
  const isMonthStart = props.showDayNumber &&
219
- shouldDisplayMonthStart(date, dateProfile.currentRange, dateEnv);
220
- return (createElement(DayCellContainer, { elTag: "td", elRef: this.handleRootEl, elClasses: [
221
- 'fc-daygrid-day',
322
+ shouldDisplayMonthStart(props.date, props.dateProfile.currentRange, dateEnv);
323
+ return (createElement(DayCellContainer, { elTag: "div", elClasses: [
324
+ 'fc-daygrid-cell',
325
+ 'fc-cell',
326
+ props.width != null ? '' : 'fc-liquid',
327
+ 'fc-flex-column',
222
328
  ...(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" },
329
+ ], elAttrs: Object.assign(Object.assign({}, props.extraDataAttrs), { role: 'gridcell' }), elStyle: {
330
+ width: props.width
331
+ }, extraRenderProps: props.extraRenderProps, defaultGenerator: renderTopInner, date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, isMonthStart: isMonthStart }, (InnerContent, renderProps) => (createElement("div", { ref: this.innerElRef, className: [
332
+ 'fc-daygrid-cell-inner',
333
+ props.fgLiquidHeight ? 'fc-liquid' : ''
334
+ ].join(' ') },
335
+ createElement("div", { ref: this.headerWrapElRef, className: "fc-flex-column" }, !renderProps.isDisabled && (props.showDayNumber || hasCustomDayCellContent(options)) && (createElement("div", { className: "fc-daygrid-cell-header" },
227
336
  createElement(InnerContent, { elTag: "a", elClasses: [
228
- 'fc-daygrid-day-number',
337
+ 'fc-daygrid-cell-number',
229
338
  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)))));
339
+ ], elAttrs: buildNavLinkAttrs(context, props.date) })))),
340
+ createElement("div", { className: "fc-daygrid-cell-main", style: {
341
+ height: props.fgLiquidHeight ? '' : props.fgHeight
342
+ } }, props.fg),
343
+ createElement("div", { className: "fc-daygrid-cell-footer", style: props.fgLiquidHeight
344
+ ? { position: 'relative', top: props.fgHeight }
345
+ : {} },
346
+ createElement(DayGridMoreLink, { allDayDate: props.date, segs: props.segs, hiddenSegs: props.hiddenSegs, alignmentElRef: this.innerElRef, alignGridTop: !props.showDayNumber, extraDateSpan: props.extraDateSpan, dateProfile: props.dateProfile, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange })),
347
+ props.bg))));
348
+ }
349
+ componentDidMount() {
350
+ const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
351
+ const headerWrapEl = this.headerWrapElRef.current; // "
352
+ // TODO: only attach this if refs props present
353
+ this.detachInnerHeight = watchHeight(innerEl, (height) => {
354
+ setRef(this.props.innerHeightRef, height);
355
+ });
356
+ this.detachHeaderHeight = watchHeight(headerWrapEl, (height) => {
357
+ setRef(this.props.headerHeightRef, height);
358
+ });
359
+ }
360
+ componentWillUnmount() {
361
+ this.detachInnerHeight();
362
+ this.detachHeaderHeight();
363
+ setRef(this.props.innerHeightRef, null);
364
+ setRef(this.props.headerHeightRef, null);
239
365
  }
240
366
  }
367
+ // Utils
368
+ // -------------------------------------------------------------------------------------------------
241
369
  function renderTopInner(props) {
242
370
  return props.dayNumberText || createElement(Fragment, null, "\u00A0");
243
371
  }
@@ -257,26 +385,43 @@ function shouldDisplayMonthStart(date, currentRange, dateEnv) {
257
385
  (dateEnv.getDay(date) === 1 && date.valueOf() < currentEnd.valueOf()));
258
386
  }
259
387
 
260
- function generateSegKey(seg) {
388
+ /*
389
+ Unique per-START-column, good for cataloging by top
390
+ */
391
+ function getSegStartId(seg) {
261
392
  return seg.eventRange.instance.instanceId + ':' + seg.firstCol;
262
393
  }
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;
394
+ /*
395
+ Unique per-START-and-END-column, good for cataloging by width/height
396
+ */
397
+ function getSegSpanId(seg) {
398
+ return getSegStartId(seg) + ':' + seg.lastCol;
399
+ }
400
+ function computeFgSegVerticals(segs, segHeightMap, // keyed by segSpanId
401
+ cells, topOrigin, maxHeight, strictOrder, dayMaxEvents, dayMaxEventRows) {
402
+ // initialize column-based arrays
403
+ const colCnt = cells.length;
404
+ const hiddenSegsByCol = [];
405
+ const heightsByCol = [];
406
+ for (let col = 0; col < colCnt; col++) {
407
+ hiddenSegsByCol.push([]);
408
+ heightsByCol.push(0);
409
+ }
410
+ // create entries to be given to DayGridSegHierarchy
411
+ const segEntries = segs.map((seg, index) => ({
412
+ index: index,
413
+ seg,
414
+ span: {
415
+ start: seg.firstCol,
416
+ end: seg.lastCol + 1,
417
+ },
418
+ }));
419
+ // configure hierarchy position-generator
420
+ let hierarchy = new DayGridSegHierarchy((segEntry) => (segHeightMap.get(getSegSpanId(segs[segEntry.index]))));
421
+ hierarchy.allowReslicing = false;
277
422
  hierarchy.strictOrder = strictOrder;
278
423
  if (dayMaxEvents === true || dayMaxEventRows === true) {
279
- hierarchy.maxCoord = maxContentHeight;
424
+ hierarchy.maxCoord = maxHeight;
280
425
  hierarchy.hiddenConsumes = true;
281
426
  }
282
427
  else if (typeof dayMaxEvents === 'number') {
@@ -286,172 +431,30 @@ dayMaxEvents, dayMaxEventRows, strictOrder, segHeights, maxContentHeight, cells)
286
431
  hierarchy.maxStackCnt = dayMaxEventRows;
287
432
  hierarchy.hiddenConsumes = true;
288
433
  }
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
- });
331
- }
332
- }
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
- });
356
- }
357
- }
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
- }
434
+ // compile segTops & heightsByCol
435
+ const hiddenSegEntries = hierarchy.addSegs(segEntries);
436
+ const segRects = hierarchy.toRects();
437
+ const segTops = {};
438
+ for (const segRect of segRects) {
439
+ const seg = segs[segRect.index];
440
+ segTops[getSegStartId(seg)] = topOrigin + segRect.levelCoord;
441
+ let { start: col, end: endCol } = segRect.span;
442
+ for (; col < endCol; col++) {
443
+ heightsByCol[col] = Math.max(heightsByCol[col], segRect.levelCoord + segRect.thickness);
419
444
  }
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
445
  }
431
- for (let rect of rects) {
432
- for (let col = rect.span.start; col < rect.span.end; col += 1) {
433
- rectsByEachCol[col].push(rect);
446
+ // compile # of invisible segs per-column
447
+ for (const hiddenSegEntry of hiddenSegEntries) {
448
+ const { span } = hiddenSegEntry;
449
+ const hiddenSeg = segs[hiddenSegEntry.index];
450
+ for (let col = span.start; col < span.end; col++) {
451
+ hiddenSegsByCol[col].push(hiddenSeg);
434
452
  }
435
453
  }
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() });
454
+ return [segTops, heightsByCol, hiddenSegsByCol];
454
455
  }
456
+ // DayGridSegHierarchy
457
+ // -------------------------------------------------------------------------------------------------
455
458
  class DayGridSegHierarchy extends SegHierarchy {
456
459
  constructor() {
457
460
  super(...arguments);
@@ -499,74 +502,206 @@ class DayGridSegHierarchy extends SegHierarchy {
499
502
  }
500
503
  }
501
504
 
502
- class TableRow extends DateComponent {
505
+ class DayGridEventHarness extends Component {
503
506
  constructor() {
504
507
  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"
508
+ // ref
509
509
  this.rootElRef = createRef();
510
- this.state = {
511
- framePositions: null,
512
- maxContentHeight: null,
513
- segHeights: {},
510
+ }
511
+ render() {
512
+ const { props } = this;
513
+ return (createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
514
+ }
515
+ componentDidMount() {
516
+ const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
517
+ this.detachHeight = watchHeight(rootEl, (height) => {
518
+ setRef(this.props.heightRef, height);
519
+ });
520
+ }
521
+ componentWillUnmount() {
522
+ this.detachHeight();
523
+ setRef(this.props.heightRef, null);
524
+ }
525
+ }
526
+
527
+ const DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'narrow' });
528
+ const COMPACT_CELL_WIDTH = 80;
529
+ class DayGridRow extends BaseComponent {
530
+ constructor() {
531
+ super(...arguments);
532
+ this.cellInnerHeightRefMap = new RefMap(() => {
533
+ afterSize(this.handleInnerHeights);
534
+ });
535
+ this.cellHeaderHeightRefMap = new RefMap(() => {
536
+ afterSize(this.handleHeaderHeights);
537
+ });
538
+ this.segHeightRefMap = new RefMap(() => {
539
+ afterSize(this.handleSegHeights);
540
+ });
541
+ this.handleRootEl = (rootEl) => {
542
+ this.rootEl = rootEl;
543
+ setRef(this.props.rootElRef, rootEl);
514
544
  };
515
- this.handleResize = (isForced) => {
516
- if (isForced) {
517
- this.updateSizing(true); // isExternal=true
545
+ // Sizing
546
+ // -----------------------------------------------------------------------------------------------
547
+ this.handleHeaderHeights = () => {
548
+ const cellHeaderHeightMap = this.cellHeaderHeightRefMap.current;
549
+ let max = 0;
550
+ for (const height of cellHeaderHeightMap.values()) {
551
+ max = Math.max(max, height);
552
+ }
553
+ if (this.state.headerHeight !== max) {
554
+ this.setState({ headerHeight: max });
518
555
  }
519
556
  };
557
+ this.handleInnerHeights = () => {
558
+ const { props } = this;
559
+ const fgLiquidHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
560
+ const cellInnerHeightMap = this.cellInnerHeightRefMap.current;
561
+ let max = 0;
562
+ for (const height of cellInnerHeightMap.values()) {
563
+ max = Math.max(max, height);
564
+ }
565
+ if (fgLiquidHeight) {
566
+ if (this.state.innerHeight !== max) {
567
+ this.setState({ innerHeight: max }); // will trigger event rerender
568
+ }
569
+ }
570
+ else {
571
+ setRef(props.innerHeightRef, max);
572
+ }
573
+ };
574
+ this.handleSegHeights = () => {
575
+ this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger event rerender
576
+ };
520
577
  }
521
578
  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
579
+ const { props, state, context, cellInnerHeightRefMap, cellHeaderHeightRefMap } = this;
580
+ const { cells } = props;
581
+ const { options } = context;
582
+ const weekDate = props.cells[0].date;
583
+ const colCnt = props.cells.length;
584
+ const fgLiquidHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
585
+ // TODO: memoize? sort all types of segs?
586
+ const fgEventSegs = sortEventSegs(props.fgEventSegs, options.eventOrder);
587
+ // TODO: memoize?
588
+ const fgEventSegsByCol = splitSegsByCol(fgEventSegs, colCnt);
589
+ const bgEventSegsByCol = splitSegsByCol(props.bgEventSegs, colCnt);
590
+ const businessHoursByCol = splitSegsByCol(props.businessHourSegs, colCnt);
591
+ const highlightSegsByCol = splitSegsByCol(this.getHighlightSegs(), colCnt); // TODO: doesn't need standins
592
+ const mirrorSegsByCol = splitSegsByCol(this.getMirrorSegs(), colCnt); // TODO: doesn't need standins
593
+ // TODO: memoize?
594
+ const [segTops, heightsByCol, hiddenSegsByCol] = computeFgSegVerticals(fgEventSegs, this.segHeightRefMap.current, cells, state.headerHeight, (fgLiquidHeight && state.innerHeight != null && state.headerHeight != null)
595
+ ? state.innerHeight - state.headerHeight
596
+ : undefined, options.eventOrderStrict, props.dayMaxEvents, props.dayMaxEventRows);
597
+ const forcedInvisibleMap = // TODO: more convenient/DRY
531
598
  (props.eventDrag && props.eventDrag.affectedInstances) ||
532
599
  (props.eventResize && props.eventResize.affectedInstances) ||
533
600
  {};
534
- return (createElement("tr", { ref: this.rootElRef, role: "row" },
535
- props.renderIntro && props.renderIntro(),
601
+ return (createElement("div", { role: props.cellGroup ? undefined : 'row', className: [
602
+ 'fc-daygrid-row',
603
+ props.forceVSpacing
604
+ ? 'fc-daygrid-row-spacious'
605
+ : props.compact
606
+ ? 'fc-daygrid-row-compact'
607
+ : '',
608
+ props.cellGroup ? 'fc-flex-row' : 'fc-row',
609
+ 'fc-rel',
610
+ props.className || '',
611
+ ].join(' '), style: {
612
+ minHeight: props.minHeight,
613
+ }, ref: this.handleRootEl },
536
614
  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,
615
+ const normalFgNodes = this.renderFgSegs(fgEventSegsByCol[col], segTops, props.todayRange, forcedInvisibleMap);
616
+ const mirrorFgNodes = this.renderFgSegs(mirrorSegsByCol[col], segTops, props.todayRange, {}, // forcedInvisibleMap
617
+ Boolean(props.eventDrag), Boolean(props.eventResize), false);
618
+ return (createElement(DayGridCell, { key: cell.key, dateProfile: props.dateProfile, todayRange: props.todayRange, date: cell.date, showDayNumber: props.showDayNumbers,
619
+ // content
620
+ segs: fgEventSegsByCol[col], hiddenSegs: hiddenSegsByCol[col], fgLiquidHeight: fgLiquidHeight, fg: (createElement(Fragment, null,
541
621
  createElement(Fragment, null, normalFgNodes),
542
- createElement(Fragment, null, mirrorFgNodes))), bgContent: ( // Fragment scopes the keys
543
- createElement(Fragment, null,
622
+ createElement(Fragment, null, mirrorFgNodes))), bg: (createElement(Fragment, null,
544
623
  this.renderFillSegs(highlightSegsByCol[col], 'highlight'),
545
624
  this.renderFillSegs(businessHoursByCol[col], 'non-business'),
546
- this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))), minHeight: props.cellMinHeight }));
547
- })));
625
+ this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))), eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
626
+ // render hooks
627
+ extraRenderProps: cell.extraRenderProps, extraDateSpan: cell.extraDateSpan, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames,
628
+ // dimensions
629
+ fgHeight: heightsByCol[col], width: props.colWidth,
630
+ // refs
631
+ innerHeightRef: cellInnerHeightRefMap.createRef(cell.key), headerHeightRef: cellHeaderHeightRefMap.createRef(cell.key) }));
632
+ }),
633
+ props.showWeekNumbers && (createElement(WeekNumberContainer, { elTag: "a", elClasses: ['fc-daygrid-week-number'], elAttrs: buildNavLinkAttrs(context, weekDate, 'week'), date: weekDate, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }))));
634
+ }
635
+ renderFgSegs(segs, segTops, todayRange, forcedInvisibleMap, isDragging, isResizing, isDateSelecting) {
636
+ const { props, context, segHeightRefMap } = this;
637
+ const { isRtl } = context;
638
+ const { colWidth, eventSelection } = props;
639
+ const colCnt = props.cells.length;
640
+ const defaultDisplayEventEnd = props.cells.length === 1;
641
+ const isMirror = isDragging || isResizing || isDateSelecting;
642
+ const nodes = [];
643
+ for (const seg of segs) {
644
+ const { left, right, width } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
645
+ // TODO: optimize ID creation? all related
646
+ const { eventRange } = seg;
647
+ const { instanceId } = eventRange.instance;
648
+ const segSpanId = getSegSpanId(seg);
649
+ const segStartId = getSegStartId(seg);
650
+ const top = segTops[segStartId];
651
+ const isVisible = !seg.isStandin &&
652
+ top != null &&
653
+ !forcedInvisibleMap[instanceId];
654
+ /*
655
+ TODO: is this comment still relevant? vvvvvvvv
656
+ known bug: events that are force to be list-item but span multiple days still take up space in later columns
657
+ todo: in print view, for multi-day events, don't display title within non-start/end segs
658
+ */
659
+ nodes.push(createElement(DayGridEventHarness, { key: segSpanId, style: {
660
+ visibility: isVisible ? '' : 'hidden',
661
+ top,
662
+ left,
663
+ right,
664
+ width,
665
+ }, heightRef: (isMirror || seg.isStandin)
666
+ ? null
667
+ : segHeightRefMap.createRef(segSpanId) }, hasListItemDisplay(seg) ? (createElement(DayGridListEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getEventRangeMeta(eventRange, todayRange)))) : (createElement(DayGridBlockEvent, Object.assign({ eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getEventRangeMeta(eventRange, todayRange))))));
668
+ }
669
+ return nodes;
548
670
  }
549
- componentDidMount() {
550
- this.updateSizing(true);
551
- this.context.addResizeHandler(this.handleResize);
671
+ renderFillSegs(segs, fillType) {
672
+ const { props, context } = this;
673
+ const { isRtl } = context;
674
+ const { todayRange, colWidth } = props;
675
+ const colCnt = props.cells.length;
676
+ const nodes = [];
677
+ for (const seg of segs) {
678
+ const { left, right, width } = computeHorizontalsFromSeg(seg, colWidth, colCnt, isRtl);
679
+ const isVisible = !seg.isStandin;
680
+ nodes.push(createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: {
681
+ visibility: isVisible ? '' : 'hidden',
682
+ left,
683
+ right,
684
+ width,
685
+ } }, fillType === 'bg-event' ?
686
+ createElement(BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, getEventRangeMeta(seg.eventRange, todayRange))) : (renderFill(fillType))));
687
+ }
688
+ return createElement(Fragment, {}, ...nodes);
552
689
  }
553
- componentDidUpdate(prevProps, prevState) {
554
- let currentProps = this.props;
555
- this.updateSizing(!isPropsEqual(prevProps, currentProps));
690
+ // Sizing
691
+ // -----------------------------------------------------------------------------------------------
692
+ componentDidMount() {
693
+ const { rootEl } = this; // TODO: make dynamic with useEffect
694
+ this.disconnectHeight = watchHeight(rootEl, (contentHeight) => {
695
+ setRef(this.props.heightRef, contentHeight);
696
+ });
556
697
  }
557
698
  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;
699
+ this.disconnectHeight();
700
+ setRef(this.props.heightRef, null);
701
+ setRef(this.props.innerHeightRef, null);
569
702
  }
703
+ // Utils
704
+ // -----------------------------------------------------------------------------------------------
570
705
  getMirrorSegs() {
571
706
  let { props } = this;
572
707
  if (props.eventResize && props.eventResize.segs.length) { // messy check
@@ -574,412 +709,471 @@ class TableRow extends DateComponent {
574
709
  }
575
710
  return [];
576
711
  }
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);
712
+ getHighlightSegs() {
713
+ let { props } = this;
714
+ if (props.eventDrag && props.eventDrag.segs.length) { // messy check
715
+ return props.eventDrag.segs;
676
716
  }
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;
717
+ if (props.eventResize && props.eventResize.segs.length) { // messy check
718
+ return props.eventResize.segs;
711
719
  }
720
+ return props.dateSelectionSegs;
712
721
  }
713
- return topsByInstanceId;
714
722
  }
715
723
 
716
- class TableRows extends DateComponent {
724
+ class DayGridRows extends DateComponent {
717
725
  constructor() {
718
726
  super(...arguments);
727
+ // memo
719
728
  this.splitBusinessHourSegs = memoize(splitSegsByRow);
720
729
  this.splitBgEventSegs = memoize(splitSegsByRow);
721
730
  this.splitFgEventSegs = memoize(splitSegsByRow);
722
731
  this.splitDateSelectionSegs = memoize(splitSegsByRow);
723
732
  this.splitEventDrag = memoize(splitInteractionByRow);
724
733
  this.splitEventResize = memoize(splitInteractionByRow);
725
- this.rowRefs = new RefMap();
734
+ // internal
735
+ this.rowHeightRefMap = new RefMap((height, key) => {
736
+ // HACKy way of syncing RefMap results with prop
737
+ const { rowHeightRefMap } = this.props;
738
+ if (rowHeightRefMap) {
739
+ rowHeightRefMap.handleValue(height, key);
740
+ }
741
+ });
742
+ this.handleRootEl = (rootEl) => {
743
+ this.rootEl = rootEl;
744
+ if (rootEl) {
745
+ this.context.registerInteractiveComponent(this, {
746
+ el: rootEl,
747
+ isHitComboAllowed: this.props.isHitComboAllowed,
748
+ });
749
+ }
750
+ else {
751
+ this.context.unregisterInteractiveComponent(this);
752
+ }
753
+ };
726
754
  }
727
755
  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);
756
+ let { props, state, context, rowHeightRefMap } = this;
757
+ let { options } = context;
758
+ let rowCnt = props.cellRows.length;
732
759
  let fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, rowCnt);
760
+ let bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt);
761
+ let businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt);
733
762
  let dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt);
734
763
  let eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt);
735
764
  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 })))))));
765
+ // whether the ROW should expand in height
766
+ // (not to be confused with whether the fg events within the row should be molded by height of row)
767
+ let isHeightAuto = getIsHeightAuto(options);
768
+ // maintain at least aspectRatio for cells?
769
+ let rowMinHeight = (state.width != null && (rowCnt >= 7 || // TODO: better way to infer if across single-month boundary
770
+ isHeightAuto)) ? state.width / context.options.aspectRatio / 6 // okay to hardcode 6 (weeks) ?
771
+ : null;
772
+ return (createElement("div", { className: 'fc-grow fc-flex-column', style: { width: props.width }, ref: this.handleRootEl }, props.cellRows.map((cells, row) => (createElement(DayGridRow, { key: cells[0].key, dateProfile: props.dateProfile, todayRange: props.todayRange, cells: cells, showDayNumbers: rowCnt > 1, showWeekNumbers: options.weekNumbers, forPrint: props.forPrint, compact: state.width != null && (state.width / cells.length) < COMPACT_CELL_WIDTH,
773
+ // if not auto-height, distribute height of container somewhat evently to rows
774
+ // (treat all as zero, distribute height, then ensure min-heights -- the inner content height)
775
+ className: isHeightAuto ? '' : 'fc-grow fc-basis0',
776
+ // content
777
+ fgEventSegs: fgEventSegsByRow[row], bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* HACK */, businessHourSegs: businessHourSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventSelection: props.eventSelection, eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows,
778
+ // dimensions
779
+ colWidth: props.colWidth, minHeight: rowMinHeight,
780
+ // refs
781
+ heightRef: rowHeightRefMap.createRef(cells[0].key) })))));
745
782
  }
746
783
  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
- }
784
+ this.detachWidth = watchWidth(this.rootEl, (width) => {
785
+ this.setState({ width });
786
+ });
767
787
  }
768
788
  componentWillUnmount() {
769
- if (this.rootEl) {
770
- this.context.unregisterInteractiveComponent(this);
771
- this.rootEl = null;
772
- }
789
+ this.detachWidth();
773
790
  }
774
791
  // 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 };
792
+ // -----------------------------------------------------------------------------------------------
793
+ queryHit(positionLeft, positionTop, elWidth) {
794
+ const { props, context } = this;
795
+ const colCnt = props.cellRows[0].length;
796
+ const { col, left, right } = computeColFromPosition(positionLeft, elWidth, props.colWidth, colCnt, context.isRtl);
797
+ const { row, top, bottom } = computeRowFromPosition(positionTop, props.cellRows, this.rowHeightRefMap.current);
798
+ const cell = props.cellRows[row][col];
799
+ const cellStartDate = cell.date;
800
+ const cellEndDate = addDays(cellStartDate, 1);
801
+ return {
802
+ dateProfile: props.dateProfile,
803
+ dateSpan: Object.assign({ range: {
804
+ start: cellStartDate,
805
+ end: cellEndDate,
806
+ }, allDay: true }, cell.extraDateSpan),
807
+ // HACK. TODO: This is expensive to do every hit-query
808
+ dayEl: getCellEl(getRowEl(this.rootEl, row), col),
809
+ rect: {
810
+ left,
811
+ right,
812
+ top,
813
+ bottom,
814
+ },
815
+ layer: 0,
816
+ };
811
817
  }
812
818
  }
819
+ // Utils
820
+ // -------------------------------------------------------------------------------------------------
813
821
  function isSegAllDay(seg) {
814
822
  return seg.eventRange.def.allDay;
815
823
  }
816
824
 
817
- class Table extends DateComponent {
825
+ class HeaderRow extends BaseComponent {
826
+ render() {
827
+ const { props } = this;
828
+ return (createElement("div", { role: props.cellGroup ? undefined : 'row', className: [
829
+ props.cellGroup ? 'fc-flex-row' : 'fc-row',
830
+ props.className || '',
831
+ ].join(' ') }, props.cells.map((cell) => (createElement(Fragment, { key: props.getHeaderModelKey(cell) }, props.renderHeaderContent(cell, props.tierNum, undefined, // innerHeightRef
832
+ props.colWidth))))));
833
+ }
834
+ }
835
+
836
+ function DayGridHeader(props) {
837
+ return (createElement("div", { className: [
838
+ 'fc-rowgroup',
839
+ 'fc-content-box',
840
+ ...(props.extraClassNames || []),
841
+ ].join(' '), style: {
842
+ width: props.width,
843
+ paddingLeft: props.paddingLeft,
844
+ paddingRight: props.paddingRight,
845
+ } }, props.headerTiers.map((cells, tierNum) => (createElement(HeaderRow, { key: tierNum, tierNum: tierNum, cells: cells, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey, colWidth: props.colWidth })))));
846
+ }
847
+
848
+ class DayGridLayoutNormal extends BaseComponent {
818
849
  constructor() {
819
850
  super(...arguments);
820
- this.elRef = createRef();
821
- this.needsScrollReset = false;
851
+ this.handleScroller = (scroller) => {
852
+ setRef(this.props.scrollerRef, scroller);
853
+ };
854
+ this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
855
+ this.setState({ leftScrollbarWidth });
856
+ };
857
+ this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
858
+ this.setState({ rightScrollbarWidth });
859
+ };
822
860
  }
823
861
  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 })))));
862
+ const { props, state, context } = this;
863
+ const { options } = context;
864
+ const verticalScrollbars = !props.forPrint && !getIsHeightAuto(options);
865
+ const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
866
+ return (createElement(Fragment, null,
867
+ options.dayHeaders && (createElement(DayGridHeader, { headerTiers: props.headerTiers, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey,
868
+ // render hooks
869
+ extraClassNames: [
870
+ 'fc-daygrid-header',
871
+ stickyHeaderDates ? 'fc-sticky-header' : '',
872
+ ],
873
+ // dimensions
874
+ paddingLeft: state.leftScrollbarWidth, paddingRight: state.rightScrollbarWidth })),
875
+ createElement(Scroller, { vertical: verticalScrollbars, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth, elClassNames: [
876
+ 'fc-daygrid-body',
877
+ 'fc-rowgroup',
878
+ 'fc-flex-column',
879
+ verticalScrollbars ? 'fc-liquid' : '',
880
+ ], ref: this.handleScroller },
881
+ createElement(DayGridRows // .fc-grow
882
+ , { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed,
883
+ // content
884
+ fgEventSegs: props.fgEventSegs, bgEventSegs: props.bgEventSegs, businessHourSegs: props.businessHourSegs, dateSelectionSegs: props.dateSelectionSegs, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
885
+ // refs
886
+ rowHeightRefMap: props.rowHeightRefMap }))));
853
887
  }
854
- componentDidMount() {
855
- this.requestScrollReset();
888
+ }
889
+
890
+ class DayGridLayoutPannable extends BaseComponent {
891
+ constructor() {
892
+ super(...arguments);
893
+ this.headerScrollerRef = createRef();
894
+ this.bodyScrollerRef = createRef();
895
+ this.footerScrollerRef = createRef();
896
+ // Sizing
897
+ // -----------------------------------------------------------------------------------------------
898
+ this.handleWidth = (width) => {
899
+ this.setState({ width });
900
+ };
901
+ this.handleLeftScrollbarWidth = (leftScrollbarWidth) => {
902
+ this.setState({ leftScrollbarWidth });
903
+ };
904
+ this.handleRightScrollbarWidth = (rightScrollbarWidth) => {
905
+ this.setState({ rightScrollbarWidth });
906
+ };
856
907
  }
857
- componentDidUpdate(prevProps) {
858
- if (prevProps.dateProfile !== this.props.dateProfile) {
859
- this.requestScrollReset();
860
- }
861
- else {
862
- this.flushScrollReset();
863
- }
908
+ render() {
909
+ const { props, state, context } = this;
910
+ const { options } = context;
911
+ const verticalScrollbars = !props.forPrint && !getIsHeightAuto(options);
912
+ const stickyHeaderDates = !props.forPrint && getStickyHeaderDates(options);
913
+ const stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(options);
914
+ const colCnt = props.cellRows[0].length;
915
+ const [canvasWidth, colWidth] = computeColWidth(colCnt, props.dayMinWidth, state.width);
916
+ return (createElement(Fragment, null,
917
+ options.dayHeaders && (createElement(Scroller, { horizontal: true, hideScrollbars: true, elClassNames: [
918
+ 'fc-daygrid-header',
919
+ 'fc-rowgroup',
920
+ stickyHeaderDates ? 'fc-sticky-header' : ''
921
+ ], ref: this.headerScrollerRef },
922
+ createElement(DayGridHeader, { headerTiers: props.headerTiers, renderHeaderContent: props.renderHeaderContent, getHeaderModelKey: props.getHeaderModelKey,
923
+ // dimensions
924
+ colWidth: colWidth, width: canvasWidth, paddingLeft: state.leftScrollbarWidth, paddingRight: state.rightScrollbarWidth }))),
925
+ createElement(Scroller, { vertical: verticalScrollbars, horizontal: true, hideScrollbars: stickyFooterScrollbar, widthRef: this.handleWidth, leftScrollbarWidthRef: this.handleLeftScrollbarWidth, rightScrollbarWidthRef: this.handleRightScrollbarWidth, elClassNames: [
926
+ 'fc-daygrid-body',
927
+ 'fc-rowgroup',
928
+ 'fc-flex-column',
929
+ verticalScrollbars ? 'fc-liquid' : '',
930
+ ], ref: this.bodyScrollerRef },
931
+ createElement(DayGridRows // .fc-grow
932
+ , { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: props.cellRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed,
933
+ // content
934
+ fgEventSegs: props.fgEventSegs, bgEventSegs: props.bgEventSegs, businessHourSegs: props.businessHourSegs, dateSelectionSegs: props.dateSelectionSegs, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection,
935
+ // dimensions
936
+ colWidth: colWidth, width: canvasWidth,
937
+ // refs
938
+ rowHeightRefMap: props.rowHeightRefMap })),
939
+ Boolean(stickyFooterScrollbar) && (createElement(Scroller, { ref: this.footerScrollerRef, horizontal: true, elClassNames: ['fc-sticky-footer'], elStyle: {
940
+ marginTop: '-1px', // HACK
941
+ } },
942
+ createElement("div", { style: {
943
+ width: canvasWidth,
944
+ height: '1px', // HACK
945
+ } })))));
864
946
  }
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
- }
947
+ // Lifecycle
948
+ // -----------------------------------------------------------------------------------------------
949
+ componentDidMount() {
950
+ // scroller
951
+ const ScrollerSyncer = getScrollerSyncerClass(this.context.pluginHooks);
952
+ this.syncedScroller = new ScrollerSyncer(true); // horizontal=true
953
+ setRef(this.props.scrollerRef, this.syncedScroller);
954
+ this.updateSyncedScroller();
883
955
  }
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...
956
+ componentDidUpdate() {
957
+ // scroller
958
+ this.updateSyncedScroller();
890
959
  }
891
- if (!el) {
892
- el = containerEl.querySelector(`[data-date="${formatDayString(dateProfile.currentDate)}"]`);
893
- // could still be hidden if an interior-view hidden day
960
+ componentWillUnmount() {
961
+ // scroller
962
+ this.syncedScroller.destroy();
963
+ }
964
+ // Scrolling
965
+ // -----------------------------------------------------------------------------------------------
966
+ updateSyncedScroller() {
967
+ this.syncedScroller.handleChildren([
968
+ this.headerScrollerRef.current,
969
+ this.bodyScrollerRef.current,
970
+ this.footerScrollerRef.current,
971
+ ]);
894
972
  }
895
- return el;
896
973
  }
897
974
 
898
- class DayTableSlicer extends Slicer {
975
+ class DayGridLayout extends BaseComponent {
899
976
  constructor() {
900
977
  super(...arguments);
901
- this.forceDayIfListItem = true;
978
+ // ref
979
+ this.scrollerRef = createRef();
980
+ this.rowHeightRefMap = new RefMap(() => {
981
+ afterSize(this.updateScrollY);
982
+ });
983
+ // internal
984
+ this.scrollDate = null;
985
+ this.updateScrollY = () => {
986
+ const rowHeightMap = this.rowHeightRefMap.current;
987
+ const scroller = this.scrollerRef.current;
988
+ // Since updateScrollY is called by rowHeightRefMap, could be called with null during cleanup,
989
+ // and the scroller might not exist
990
+ if (scroller && this.scrollDate) {
991
+ let scrollTop = computeTopFromDate(this.scrollDate, this.props.cellRows, rowHeightMap, 1);
992
+ if (scrollTop != null) {
993
+ if (scrollTop) {
994
+ scrollTop++; // clear top border
995
+ }
996
+ scroller.scrollTo({ y: scrollTop });
997
+ }
998
+ }
999
+ };
1000
+ this.clearScroll = () => {
1001
+ this.scrollDate = null;
1002
+ };
902
1003
  }
903
- sliceRange(dateRange, dayTableModel) {
904
- return dayTableModel.sliceRange(dateRange);
1004
+ render() {
1005
+ const { props, context } = this;
1006
+ const { options } = context;
1007
+ const commonLayoutProps = Object.assign(Object.assign({}, props), { scrollerRef: this.scrollerRef, rowHeightRefMap: this.rowHeightRefMap });
1008
+ return (createElement(ViewContainer, { viewSpec: context.viewSpec, elClasses: [props.className, 'fc-flex-column', 'fc-border'] }, options.dayMinWidth ? (createElement(DayGridLayoutPannable, Object.assign({}, commonLayoutProps, { dayMinWidth: options.dayMinWidth }))) : (createElement(DayGridLayoutNormal, Object.assign({}, commonLayoutProps)))));
1009
+ }
1010
+ // Lifecycle
1011
+ // -----------------------------------------------------------------------------------------------
1012
+ componentDidMount() {
1013
+ this.resetScroll();
1014
+ this.scrollerRef.current.addScrollEndListener(this.clearScroll);
1015
+ }
1016
+ componentDidUpdate(prevProps) {
1017
+ if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
1018
+ this.resetScroll();
1019
+ }
1020
+ }
1021
+ componentWillUnmount() {
1022
+ this.scrollerRef.current.removeScrollEndListener(this.clearScroll);
1023
+ }
1024
+ // Scrolling
1025
+ // -----------------------------------------------------------------------------------------------
1026
+ resetScroll() {
1027
+ this.scrollDate = this.props.dateProfile.currentDate;
1028
+ this.updateScrollY();
1029
+ // updateScrollX
1030
+ const scroller = this.scrollerRef.current;
1031
+ scroller.scrollTo({ x: 0 });
905
1032
  }
906
1033
  }
907
1034
 
908
- class DayTable extends DateComponent {
1035
+ const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
1036
+ class DayOfWeekHeaderCell extends BaseComponent {
909
1037
  constructor() {
910
1038
  super(...arguments);
911
- this.slicer = new DayTableSlicer();
912
- this.tableRef = createRef();
1039
+ // ref
1040
+ this.innerElRef = createRef();
913
1041
  }
914
1042
  render() {
915
1043
  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 })));
1044
+ let { dateEnv, theme, viewApi, options } = context;
1045
+ let date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT
1046
+ let dateMeta = {
1047
+ dow: props.dow,
1048
+ isDisabled: false,
1049
+ isFuture: false,
1050
+ isPast: false,
1051
+ isToday: false,
1052
+ isOther: false,
1053
+ };
1054
+ let text = dateEnv.format(date, props.dayHeaderFormat);
1055
+ let renderProps = Object.assign(Object.assign(Object.assign(Object.assign({ date }, dateMeta), { view: viewApi }), props.extraRenderProps), { text });
1056
+ return (createElement(ContentContainer, { elTag: 'div', elClasses: [
1057
+ ...getDayClassNames(dateMeta, theme),
1058
+ ...(props.extraClassNames || []),
1059
+ 'fc-header-cell',
1060
+ 'fc-cell',
1061
+ props.colWidth != null ? '' : 'fc-liquid',
1062
+ 'fc-flex-column',
1063
+ 'fc-align-center',
1064
+ ], elAttrs: props.extraDataAttrs, elStyle: {
1065
+ width: props.colWidth != null // TODO: DRY
1066
+ ? props.colWidth * (props.colSpan || 1)
1067
+ : undefined,
1068
+ }, renderProps: renderProps, generatorName: "dayHeaderContent", customGenerator: options.dayHeaderContent, defaultGenerator: renderInner, classNameGenerator: options.dayHeaderClassNames, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, (InnerContent) => (createElement("div", { ref: this.innerElRef, className: [
1069
+ 'fc-flex-column',
1070
+ props.isSticky ? 'fc-sticky-x' : '',
1071
+ ].join(' ') },
1072
+ createElement(InnerContent, { elTag: "a", elClasses: [
1073
+ 'fc-cell-inner',
1074
+ 'fc-padding-sm',
1075
+ ], elAttrs: {
1076
+ 'aria-label': dateEnv.format(date, WEEKDAY_FORMAT),
1077
+ } })))));
1078
+ }
1079
+ componentDidMount() {
1080
+ const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
1081
+ // TODO: only attach this if refs props present
1082
+ this.disconectInnerHeight = watchHeight(innerEl, (height) => {
1083
+ setRef(this.props.innerHeightRef, height);
1084
+ });
1085
+ }
1086
+ componentWillUnmount() {
1087
+ this.disconectInnerHeight();
1088
+ setRef(this.props.innerHeightRef, null);
917
1089
  }
918
1090
  }
919
1091
 
920
- class DayTableView extends TableView {
1092
+ function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) {
1093
+ return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt);
1094
+ }
1095
+ // Computes a default column header formatting string if `colFormat` is not explicitly defined
1096
+ function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
1097
+ // if more than one week row, or if there are a lot of columns with not much space,
1098
+ // put just the day numbers will be in each cell
1099
+ if (!datesRepDistinctDays || dayCnt > 10) {
1100
+ return createFormatter({ weekday: 'short' }); // "Sat"
1101
+ }
1102
+ if (dayCnt > 1) {
1103
+ return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12"
1104
+ }
1105
+ return createFormatter({ weekday: 'long' }); // "Saturday"
1106
+ }
1107
+
1108
+ class DayGridView extends BaseComponent {
921
1109
  constructor() {
922
1110
  super(...arguments);
1111
+ // memo
923
1112
  this.buildDayTableModel = memoize(buildDayTableModel);
924
- this.headerRef = createRef();
925
- this.tableRef = createRef();
926
- // can't override any lifecycle methods from parent
1113
+ this.buildHeaderTiers = memoize(buildHeaderTiers);
1114
+ this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
1115
+ // internal
1116
+ this.slicer = new DayTableSlicer();
927
1117
  }
928
1118
  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);
1119
+ const { props, context } = this;
1120
+ const { options } = context;
1121
+ const dayTableModel = this.buildDayTableModel(props.dateProfile, context.dateProfileGenerator);
1122
+ const datesRepDistinctDays = dayTableModel.rowCnt === 1;
1123
+ const headerTiers = this.buildHeaderTiers(dayTableModel.headerDates, datesRepDistinctDays);
1124
+ const slicedProps = this.slicer.sliceProps(props, props.dateProfile, options.nextDayThreshold, context, dayTableModel);
1125
+ const dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dayTableModel.colCnt);
1126
+ return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(DayGridLayout, { dateProfile: props.dateProfile, todayRange: todayRange, cellRows: dayTableModel.cellRows, forPrint: props.forPrint, className: 'fc-daygrid-view',
1127
+ // header content
1128
+ headerTiers: headerTiers, renderHeaderContent: (model, tier, innerHeightRef, colWidth) => {
1129
+ if (model.date) {
1130
+ return (createElement(DateHeaderCell, Object.assign({}, model, { dateProfile: props.dateProfile, todayRange: todayRange, navLink: dayTableModel.colCnt > 1, dayHeaderFormat: dayHeaderFormat, colSpan: model.colSpan, colWidth: colWidth })));
1131
+ }
1132
+ else {
1133
+ return (createElement(DayOfWeekHeaderCell, Object.assign({}, model, { dayHeaderFormat: dayHeaderFormat, colSpan: model.colSpan, colWidth: colWidth })));
1134
+ }
1135
+ }, getHeaderModelKey: (model) => {
1136
+ // can use model.key???
1137
+ if (model.date) {
1138
+ return model.date.toUTCString();
1139
+ }
1140
+ return model.dow;
1141
+ },
1142
+ // body content
1143
+ fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection }))));
937
1144
  }
938
1145
  }
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
1146
 
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,
1147
+ /*
1148
+ TODO: is it even worth doing this "advanced" version?
1149
+ */
1150
+ class HeaderRowAdvanced extends BaseComponent {
1151
+ constructor() {
1152
+ super(...arguments);
1153
+ // ref
1154
+ this.innerHeightRefMap = new RefMap(() => {
1155
+ afterSize(this.handleInnerHeights);
954
1156
  });
1157
+ this.handleInnerHeights = () => {
1158
+ const innerHeightMap = this.innerHeightRefMap.current;
1159
+ let max = 0;
1160
+ for (const innerHeight of innerHeightMap.values()) {
1161
+ max = Math.max(max, innerHeight);
1162
+ }
1163
+ if (this.currentInnerHeight !== max) {
1164
+ this.currentInnerHeight = max;
1165
+ setRef(this.props.innerHeightRef, max);
1166
+ }
1167
+ };
955
1168
  }
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);
1169
+ render() {
1170
+ const { props } = this;
1171
+ return (createElement("div", { role: 'row', className: 'fc-row', style: { height: props.height } }, props.cells.map((cell) => {
1172
+ const key = props.getHeaderModelKey(cell);
1173
+ return (createElement(Fragment, { key: props.getHeaderModelKey(cell) }, props.renderHeaderContent(cell, props.tierNum, this.innerHeightRefMap.createRef(key), // innerHeightRef
1174
+ props.colWidth)));
1175
+ })));
978
1176
  }
979
- return { start, end };
980
1177
  }
981
1178
 
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 };
1179
+ export { COMPACT_CELL_WIDTH, DateHeaderCell, DayGridLayout, DayGridRow, DayGridRows, DayGridView, DayOfWeekHeaderCell, DayTableSlicer, HeaderRow, HeaderRowAdvanced, TableDateProfileGenerator, buildDayTableModel, buildDayTableRenderRange, computeColFromPosition, computeColWidth, createDayHeaderFormatter, getCellEl, getRowEl };