@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/index.global.js +887 -684
- package/index.global.min.js +2 -2
- package/index.js +2 -2
- package/internal.cjs +921 -718
- package/internal.d.ts +209 -80
- package/internal.js +910 -716
- package/package.json +2 -2
package/internal.js
CHANGED
|
@@ -1,128 +1,252 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
10
|
+
this.forceDayIfListItem = true;
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
155
|
+
class DateHeaderCell extends BaseComponent {
|
|
156
|
+
constructor() {
|
|
157
|
+
super(...arguments);
|
|
158
|
+
// ref
|
|
159
|
+
this.innerElRef = createRef();
|
|
89
160
|
}
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
203
|
+
|
|
204
|
+
function splitSegsByRow(segs, rowCnt) {
|
|
205
|
+
const byRow = [];
|
|
206
|
+
for (let row = 0; row < rowCnt; row++) {
|
|
207
|
+
byRow[row] = [];
|
|
99
208
|
}
|
|
100
|
-
for (
|
|
101
|
-
|
|
209
|
+
for (const seg of segs) {
|
|
210
|
+
byRow[seg.row].push(seg);
|
|
102
211
|
}
|
|
103
|
-
return
|
|
212
|
+
return byRow;
|
|
104
213
|
}
|
|
105
214
|
function splitInteractionByRow(ui, rowCnt) {
|
|
106
|
-
|
|
215
|
+
const byRow = [];
|
|
107
216
|
if (!ui) {
|
|
108
|
-
for (let
|
|
109
|
-
byRow[
|
|
217
|
+
for (let row = 0; row < rowCnt; row++) {
|
|
218
|
+
byRow[row] = null;
|
|
110
219
|
}
|
|
111
220
|
}
|
|
112
221
|
else {
|
|
113
|
-
for (let
|
|
114
|
-
byRow[
|
|
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 (
|
|
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
|
|
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.
|
|
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
|
|
274
|
+
class DayGridListEvent extends BaseComponent {
|
|
151
275
|
render() {
|
|
152
276
|
let { props, context } = this;
|
|
153
277
|
let { options } = context;
|
|
154
|
-
let {
|
|
278
|
+
let { eventRange } = props;
|
|
155
279
|
let timeFormat = options.eventTimeFormat || DEFAULT_TABLE_EVENT_TIME_FORMAT;
|
|
156
|
-
let timeText =
|
|
157
|
-
return (createElement(EventContainer, Object.assign({}, props, { elTag: "a", elClasses: ['fc-daygrid-event', 'fc-daygrid-dot-event'], elAttrs:
|
|
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
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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,
|
|
180
|
-
let
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
201
|
-
class TableCell extends DateComponent {
|
|
310
|
+
class DayGridCell extends DateComponent {
|
|
202
311
|
constructor() {
|
|
203
312
|
super(...arguments);
|
|
204
|
-
|
|
205
|
-
this.
|
|
206
|
-
|
|
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 {
|
|
318
|
+
let { props, context } = this;
|
|
215
319
|
let { options, dateEnv } = context;
|
|
216
|
-
|
|
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: "
|
|
221
|
-
'fc-daygrid-
|
|
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(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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-
|
|
337
|
+
'fc-daygrid-cell-number',
|
|
229
338
|
isMonthStart && 'fc-daygrid-month-start',
|
|
230
|
-
], elAttrs:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
createElement("div", { className: "fc-daygrid-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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 =
|
|
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
|
-
//
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
|
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
|
|
505
|
+
class DayGridEventHarness extends Component {
|
|
503
506
|
constructor() {
|
|
504
507
|
super(...arguments);
|
|
505
|
-
|
|
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
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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("
|
|
535
|
-
|
|
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
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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))),
|
|
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'))),
|
|
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
|
-
|
|
550
|
-
this
|
|
551
|
-
|
|
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
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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.
|
|
559
|
-
|
|
560
|
-
|
|
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
|
-
|
|
578
|
-
let {
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
730
|
-
let
|
|
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
|
-
//
|
|
737
|
-
//
|
|
738
|
-
let
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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.
|
|
748
|
-
|
|
749
|
-
|
|
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
|
-
|
|
770
|
-
this.context.unregisterInteractiveComponent(this);
|
|
771
|
-
this.rootEl = null;
|
|
772
|
-
}
|
|
789
|
+
this.detachWidth();
|
|
773
790
|
}
|
|
774
791
|
// Hit System
|
|
775
|
-
//
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
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
|
|
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.
|
|
821
|
-
|
|
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
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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
|
-
|
|
855
|
-
|
|
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
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
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
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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
|
-
|
|
886
|
-
|
|
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
|
-
|
|
892
|
-
|
|
893
|
-
|
|
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
|
|
975
|
+
class DayGridLayout extends BaseComponent {
|
|
899
976
|
constructor() {
|
|
900
977
|
super(...arguments);
|
|
901
|
-
|
|
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
|
-
|
|
904
|
-
|
|
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
|
-
|
|
1035
|
+
const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
|
|
1036
|
+
class DayOfWeekHeaderCell extends BaseComponent {
|
|
909
1037
|
constructor() {
|
|
910
1038
|
super(...arguments);
|
|
911
|
-
|
|
912
|
-
this.
|
|
1039
|
+
// ref
|
|
1040
|
+
this.innerElRef = createRef();
|
|
913
1041
|
}
|
|
914
1042
|
render() {
|
|
915
1043
|
let { props, context } = this;
|
|
916
|
-
|
|
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
|
-
|
|
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.
|
|
925
|
-
this.
|
|
926
|
-
//
|
|
1113
|
+
this.buildHeaderTiers = memoize(buildHeaderTiers);
|
|
1114
|
+
this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
|
|
1115
|
+
// internal
|
|
1116
|
+
this.slicer = new DayTableSlicer();
|
|
927
1117
|
}
|
|
928
1118
|
render() {
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
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
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
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
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
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
|
-
|
|
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 };
|