@fullcalendar/timeline 7.0.0-beta.4 → 7.0.0-beta.5
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/LICENSE.md +2 -2
- package/{index.cjs → cjs/index.cjs} +14 -7
- package/cjs/internal.cjs +1224 -0
- package/esm/index.d.ts +25 -0
- package/{index.js → esm/index.js} +13 -6
- package/{internal.d.ts → esm/internal.d.ts} +30 -45
- package/{internal.js → esm/internal.js} +434 -471
- package/{index.global.js → global.js} +443 -476
- package/global.min.js +6 -0
- package/package.json +24 -21
- package/index.d.ts +0 -8
- package/index.global.min.js +0 -6
- package/internal.cjs +0 -1258
package/internal.cjs
DELETED
|
@@ -1,1258 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var internal_cjs = require('@fullcalendar/core/internal.cjs');
|
|
6
|
-
var preact_cjs = require('@fullcalendar/core/preact.cjs');
|
|
7
|
-
var internal_cjs$1 = require('@fullcalendar/scrollgrid/internal.cjs');
|
|
8
|
-
|
|
9
|
-
const MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
|
|
10
|
-
const MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
|
|
11
|
-
const MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
|
|
12
|
-
internal_cjs.config.MAX_TIMELINE_SLOTS = 1000;
|
|
13
|
-
// potential nice values for slot-duration and interval-duration
|
|
14
|
-
const STOCK_SUB_DURATIONS = [
|
|
15
|
-
{ years: 1 },
|
|
16
|
-
{ months: 1 },
|
|
17
|
-
{ days: 1 },
|
|
18
|
-
{ hours: 1 },
|
|
19
|
-
{ minutes: 30 },
|
|
20
|
-
{ minutes: 15 },
|
|
21
|
-
{ minutes: 10 },
|
|
22
|
-
{ minutes: 5 },
|
|
23
|
-
{ minutes: 1 },
|
|
24
|
-
{ seconds: 30 },
|
|
25
|
-
{ seconds: 15 },
|
|
26
|
-
{ seconds: 10 },
|
|
27
|
-
{ seconds: 5 },
|
|
28
|
-
{ seconds: 1 },
|
|
29
|
-
{ milliseconds: 500 },
|
|
30
|
-
{ milliseconds: 100 },
|
|
31
|
-
{ milliseconds: 10 },
|
|
32
|
-
{ milliseconds: 1 },
|
|
33
|
-
];
|
|
34
|
-
function buildTimelineDateProfile(dateProfile, dateEnv, allOptions, dateProfileGenerator) {
|
|
35
|
-
let tDateProfile = {
|
|
36
|
-
labelInterval: allOptions.slotLabelInterval,
|
|
37
|
-
slotDuration: allOptions.slotDuration,
|
|
38
|
-
};
|
|
39
|
-
validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
|
|
40
|
-
ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
|
|
41
|
-
ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
|
|
42
|
-
let input = allOptions.slotLabelFormat;
|
|
43
|
-
let rawFormats = Array.isArray(input) ? input :
|
|
44
|
-
(input != null) ? [input] :
|
|
45
|
-
computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions);
|
|
46
|
-
tDateProfile.headerFormats = rawFormats.map((rawFormat) => internal_cjs.createFormatter(rawFormat));
|
|
47
|
-
tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
|
|
48
|
-
let largeUnit = null;
|
|
49
|
-
if (!tDateProfile.isTimeScale) {
|
|
50
|
-
const slotUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
|
|
51
|
-
if (/year|month|week/.test(slotUnit)) {
|
|
52
|
-
largeUnit = slotUnit;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
tDateProfile.largeUnit = largeUnit;
|
|
56
|
-
tDateProfile.emphasizeWeeks =
|
|
57
|
-
internal_cjs.asCleanDays(tDateProfile.slotDuration) === 1 &&
|
|
58
|
-
currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
|
|
59
|
-
!allOptions.businessHours;
|
|
60
|
-
/*
|
|
61
|
-
console.log('label interval =', timelineView.labelInterval.humanize())
|
|
62
|
-
console.log('slot duration =', timelineView.slotDuration.humanize())
|
|
63
|
-
console.log('header formats =', timelineView.headerFormats)
|
|
64
|
-
console.log('isTimeScale', timelineView.isTimeScale)
|
|
65
|
-
console.log('largeUnit', timelineView.largeUnit)
|
|
66
|
-
*/
|
|
67
|
-
let rawSnapDuration = allOptions.snapDuration;
|
|
68
|
-
let snapDuration;
|
|
69
|
-
let snapsPerSlot;
|
|
70
|
-
if (rawSnapDuration) {
|
|
71
|
-
snapDuration = internal_cjs.createDuration(rawSnapDuration);
|
|
72
|
-
snapsPerSlot = internal_cjs.wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
|
|
73
|
-
// ^ TODO: warning if not whole?
|
|
74
|
-
}
|
|
75
|
-
if (snapsPerSlot == null) {
|
|
76
|
-
snapDuration = tDateProfile.slotDuration;
|
|
77
|
-
snapsPerSlot = 1;
|
|
78
|
-
}
|
|
79
|
-
tDateProfile.snapDuration = snapDuration;
|
|
80
|
-
tDateProfile.snapsPerSlot = snapsPerSlot;
|
|
81
|
-
// more...
|
|
82
|
-
let timeWindowMs = internal_cjs.asRoughMs(dateProfile.slotMaxTime) - internal_cjs.asRoughMs(dateProfile.slotMinTime);
|
|
83
|
-
// TODO: why not use normalizeRange!?
|
|
84
|
-
let normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
|
|
85
|
-
let normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
|
|
86
|
-
// apply slotMinTime/slotMaxTime
|
|
87
|
-
// TODO: View should be responsible.
|
|
88
|
-
if (tDateProfile.isTimeScale) {
|
|
89
|
-
normalizedStart = dateEnv.add(normalizedStart, dateProfile.slotMinTime);
|
|
90
|
-
normalizedEnd = dateEnv.add(internal_cjs.addDays(normalizedEnd, -1), dateProfile.slotMaxTime);
|
|
91
|
-
}
|
|
92
|
-
tDateProfile.timeWindowMs = timeWindowMs;
|
|
93
|
-
tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
|
|
94
|
-
let slotDates = [];
|
|
95
|
-
let date = normalizedStart;
|
|
96
|
-
while (date < normalizedEnd) {
|
|
97
|
-
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
|
|
98
|
-
slotDates.push(date);
|
|
99
|
-
}
|
|
100
|
-
date = dateEnv.add(date, tDateProfile.slotDuration);
|
|
101
|
-
}
|
|
102
|
-
tDateProfile.slotDates = slotDates;
|
|
103
|
-
// more...
|
|
104
|
-
let snapIndex = -1;
|
|
105
|
-
let snapDiff = 0; // index of the diff :(
|
|
106
|
-
const snapDiffToIndex = [];
|
|
107
|
-
const snapIndexToDiff = [];
|
|
108
|
-
date = normalizedStart;
|
|
109
|
-
while (date < normalizedEnd) {
|
|
110
|
-
if (isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator)) {
|
|
111
|
-
snapIndex += 1;
|
|
112
|
-
snapDiffToIndex.push(snapIndex);
|
|
113
|
-
snapIndexToDiff.push(snapDiff);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
snapDiffToIndex.push(snapIndex + 0.5);
|
|
117
|
-
}
|
|
118
|
-
date = dateEnv.add(date, tDateProfile.snapDuration);
|
|
119
|
-
snapDiff += 1;
|
|
120
|
-
}
|
|
121
|
-
tDateProfile.snapDiffToIndex = snapDiffToIndex;
|
|
122
|
-
tDateProfile.snapIndexToDiff = snapIndexToDiff;
|
|
123
|
-
tDateProfile.snapCnt = snapIndex + 1; // is always one behind
|
|
124
|
-
tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
|
|
125
|
-
// more...
|
|
126
|
-
tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
|
|
127
|
-
tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv);
|
|
128
|
-
tDateProfile.slotsPerLabel = internal_cjs.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
|
|
129
|
-
return tDateProfile;
|
|
130
|
-
}
|
|
131
|
-
/*
|
|
132
|
-
snaps to appropriate unit
|
|
133
|
-
*/
|
|
134
|
-
function normalizeDate(date, tDateProfile, dateEnv) {
|
|
135
|
-
let normalDate = date;
|
|
136
|
-
if (!tDateProfile.isTimeScale) {
|
|
137
|
-
normalDate = internal_cjs.startOfDay(normalDate);
|
|
138
|
-
if (tDateProfile.largeUnit) {
|
|
139
|
-
normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return normalDate;
|
|
143
|
-
}
|
|
144
|
-
/*
|
|
145
|
-
snaps to appropriate unit
|
|
146
|
-
*/
|
|
147
|
-
function normalizeRange(range, tDateProfile, dateEnv) {
|
|
148
|
-
if (!tDateProfile.isTimeScale) {
|
|
149
|
-
range = internal_cjs.computeVisibleDayRange(range);
|
|
150
|
-
if (tDateProfile.largeUnit) {
|
|
151
|
-
let dayRange = range; // preserve original result
|
|
152
|
-
range = {
|
|
153
|
-
start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
|
|
154
|
-
end: dateEnv.startOf(range.end, tDateProfile.largeUnit),
|
|
155
|
-
};
|
|
156
|
-
// if date is partially through the interval, or is in the same interval as the start,
|
|
157
|
-
// make the exclusive end be the *next* interval
|
|
158
|
-
if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
|
|
159
|
-
range = {
|
|
160
|
-
start: range.start,
|
|
161
|
-
end: dateEnv.add(range.end, tDateProfile.slotDuration),
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return range;
|
|
167
|
-
}
|
|
168
|
-
function isValidDate(date, tDateProfile, dateProfile, dateProfileGenerator) {
|
|
169
|
-
if (dateProfileGenerator.isHiddenDay(date)) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
if (tDateProfile.isTimeScale) {
|
|
173
|
-
// determine if the time is within slotMinTime/slotMaxTime, which may have wacky values
|
|
174
|
-
let day = internal_cjs.startOfDay(date);
|
|
175
|
-
let timeMs = date.valueOf() - day.valueOf();
|
|
176
|
-
let ms = timeMs - internal_cjs.asRoughMs(dateProfile.slotMinTime); // milliseconds since slotMinTime
|
|
177
|
-
ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
|
|
178
|
-
return ms < tDateProfile.timeWindowMs; // before the slotMaxTime?
|
|
179
|
-
}
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
|
|
183
|
-
const { currentRange } = dateProfile;
|
|
184
|
-
// make sure labelInterval doesn't exceed the max number of cells
|
|
185
|
-
if (tDateProfile.labelInterval) {
|
|
186
|
-
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
|
|
187
|
-
if (labelCnt > internal_cjs.config.MAX_TIMELINE_SLOTS) {
|
|
188
|
-
console.warn('slotLabelInterval results in too many cells');
|
|
189
|
-
tDateProfile.labelInterval = null;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
// make sure slotDuration doesn't exceed the maximum number of cells
|
|
193
|
-
if (tDateProfile.slotDuration) {
|
|
194
|
-
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
|
|
195
|
-
if (slotCnt > internal_cjs.config.MAX_TIMELINE_SLOTS) {
|
|
196
|
-
console.warn('slotDuration results in too many cells');
|
|
197
|
-
tDateProfile.slotDuration = null;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
// make sure labelInterval is a multiple of slotDuration
|
|
201
|
-
if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
|
|
202
|
-
const slotsPerLabel = internal_cjs.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
|
|
203
|
-
if (slotsPerLabel === null || slotsPerLabel < 1) {
|
|
204
|
-
console.warn('slotLabelInterval must be a multiple of slotDuration');
|
|
205
|
-
tDateProfile.slotDuration = null;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
|
|
210
|
-
const { currentRange } = dateProfile;
|
|
211
|
-
let { labelInterval } = tDateProfile;
|
|
212
|
-
if (!labelInterval) {
|
|
213
|
-
// compute based off the slot duration
|
|
214
|
-
// find the largest label interval with an acceptable slots-per-label
|
|
215
|
-
let input;
|
|
216
|
-
if (tDateProfile.slotDuration) {
|
|
217
|
-
for (input of STOCK_SUB_DURATIONS) {
|
|
218
|
-
const tryLabelInterval = internal_cjs.createDuration(input);
|
|
219
|
-
const slotsPerLabel = internal_cjs.wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
|
|
220
|
-
if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
|
|
221
|
-
labelInterval = tryLabelInterval;
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// use the slot duration as a last resort
|
|
226
|
-
if (!labelInterval) {
|
|
227
|
-
labelInterval = tDateProfile.slotDuration;
|
|
228
|
-
}
|
|
229
|
-
// compute based off the view's duration
|
|
230
|
-
// find the largest label interval that yields the minimum number of labels
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
for (input of STOCK_SUB_DURATIONS) {
|
|
234
|
-
labelInterval = internal_cjs.createDuration(input);
|
|
235
|
-
const labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
|
|
236
|
-
if (labelCnt >= MIN_AUTO_LABELS) {
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
tDateProfile.labelInterval = labelInterval;
|
|
242
|
-
}
|
|
243
|
-
return labelInterval;
|
|
244
|
-
}
|
|
245
|
-
function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
|
|
246
|
-
const { currentRange } = dateProfile;
|
|
247
|
-
let { slotDuration } = tDateProfile;
|
|
248
|
-
if (!slotDuration) {
|
|
249
|
-
const labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
|
|
250
|
-
// compute based off the label interval
|
|
251
|
-
// find the largest slot duration that is different from labelInterval, but still acceptable
|
|
252
|
-
for (let input of STOCK_SUB_DURATIONS) {
|
|
253
|
-
const trySlotDuration = internal_cjs.createDuration(input);
|
|
254
|
-
const slotsPerLabel = internal_cjs.wholeDivideDurations(labelInterval, trySlotDuration);
|
|
255
|
-
if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
|
|
256
|
-
slotDuration = trySlotDuration;
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
// only allow the value if it won't exceed the view's # of slots limit
|
|
261
|
-
if (slotDuration) {
|
|
262
|
-
const slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
|
|
263
|
-
if (slotCnt > MAX_AUTO_CELLS) {
|
|
264
|
-
slotDuration = null;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
// use the label interval as a last resort
|
|
268
|
-
if (!slotDuration) {
|
|
269
|
-
slotDuration = labelInterval;
|
|
270
|
-
}
|
|
271
|
-
tDateProfile.slotDuration = slotDuration;
|
|
272
|
-
}
|
|
273
|
-
return slotDuration;
|
|
274
|
-
}
|
|
275
|
-
function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, allOptions) {
|
|
276
|
-
let format1;
|
|
277
|
-
let format2;
|
|
278
|
-
const { labelInterval } = tDateProfile;
|
|
279
|
-
let unit = internal_cjs.greatestDurationDenominator(labelInterval).unit;
|
|
280
|
-
const weekNumbersVisible = allOptions.weekNumbers;
|
|
281
|
-
let format0 = (format1 = (format2 = null));
|
|
282
|
-
// NOTE: weekNumber computation function wont work
|
|
283
|
-
if ((unit === 'week') && !weekNumbersVisible) {
|
|
284
|
-
unit = 'day';
|
|
285
|
-
}
|
|
286
|
-
switch (unit) {
|
|
287
|
-
case 'year':
|
|
288
|
-
format0 = { year: 'numeric' }; // '2015'
|
|
289
|
-
break;
|
|
290
|
-
case 'month':
|
|
291
|
-
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
|
|
292
|
-
format0 = { year: 'numeric' }; // '2015'
|
|
293
|
-
}
|
|
294
|
-
format1 = { month: 'short' }; // 'Jan'
|
|
295
|
-
break;
|
|
296
|
-
case 'week':
|
|
297
|
-
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
|
|
298
|
-
format0 = { year: 'numeric' }; // '2015'
|
|
299
|
-
}
|
|
300
|
-
format1 = { week: 'narrow' }; // 'Wk4'
|
|
301
|
-
break;
|
|
302
|
-
case 'day':
|
|
303
|
-
if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
|
|
304
|
-
format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
|
|
305
|
-
}
|
|
306
|
-
else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
|
|
307
|
-
format0 = { month: 'long' }; // 'January'
|
|
308
|
-
}
|
|
309
|
-
if (weekNumbersVisible) {
|
|
310
|
-
format1 = { week: 'short' }; // 'Wk 4'
|
|
311
|
-
}
|
|
312
|
-
format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
|
|
313
|
-
break;
|
|
314
|
-
case 'hour':
|
|
315
|
-
if (weekNumbersVisible) {
|
|
316
|
-
format0 = { week: 'short' }; // 'Wk 4'
|
|
317
|
-
}
|
|
318
|
-
if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
|
|
319
|
-
format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
|
|
320
|
-
}
|
|
321
|
-
format2 = {
|
|
322
|
-
hour: 'numeric',
|
|
323
|
-
minute: '2-digit',
|
|
324
|
-
omitZeroMinute: true,
|
|
325
|
-
meridiem: 'short',
|
|
326
|
-
};
|
|
327
|
-
break;
|
|
328
|
-
case 'minute':
|
|
329
|
-
// sufficiently large number of different minute cells?
|
|
330
|
-
if ((internal_cjs.asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
|
|
331
|
-
format0 = {
|
|
332
|
-
hour: 'numeric',
|
|
333
|
-
meridiem: 'short',
|
|
334
|
-
};
|
|
335
|
-
format1 = (params) => (':' + internal_cjs.padStart(params.date.minute, 2) // ':30'
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
format0 = {
|
|
340
|
-
hour: 'numeric',
|
|
341
|
-
minute: 'numeric',
|
|
342
|
-
meridiem: 'short',
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
break;
|
|
346
|
-
case 'second':
|
|
347
|
-
// sufficiently large number of different second cells?
|
|
348
|
-
if ((internal_cjs.asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
|
|
349
|
-
format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
|
|
350
|
-
format1 = (params) => (':' + internal_cjs.padStart(params.date.second, 2) // ':30'
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
|
|
355
|
-
}
|
|
356
|
-
break;
|
|
357
|
-
case 'millisecond':
|
|
358
|
-
format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
|
|
359
|
-
format1 = (params) => ('.' + internal_cjs.padStart(params.millisecond, 3));
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
return [].concat(format0 || [], format1 || [], format2 || []);
|
|
363
|
-
}
|
|
364
|
-
// Compute the number of the give units in the "current" range.
|
|
365
|
-
// Won't go more precise than days.
|
|
366
|
-
// Will return `0` if there's not a clean whole interval.
|
|
367
|
-
function currentRangeAs(unit, dateProfile, dateEnv) {
|
|
368
|
-
let range = dateProfile.currentRange;
|
|
369
|
-
let res = null;
|
|
370
|
-
if (unit === 'years') {
|
|
371
|
-
res = dateEnv.diffWholeYears(range.start, range.end);
|
|
372
|
-
}
|
|
373
|
-
else if (unit === 'months') {
|
|
374
|
-
res = dateEnv.diffWholeMonths(range.start, range.end);
|
|
375
|
-
}
|
|
376
|
-
else if (unit === 'weeks') {
|
|
377
|
-
res = dateEnv.diffWholeMonths(range.start, range.end);
|
|
378
|
-
}
|
|
379
|
-
else if (unit === 'days') {
|
|
380
|
-
res = internal_cjs.diffWholeDays(range.start, range.end);
|
|
381
|
-
}
|
|
382
|
-
return res || 0;
|
|
383
|
-
}
|
|
384
|
-
function buildIsWeekStarts(tDateProfile, dateEnv) {
|
|
385
|
-
let { slotDates, emphasizeWeeks } = tDateProfile;
|
|
386
|
-
let prevWeekNumber = null;
|
|
387
|
-
let isWeekStarts = [];
|
|
388
|
-
for (let slotDate of slotDates) {
|
|
389
|
-
let weekNumber = dateEnv.computeWeekNumber(slotDate);
|
|
390
|
-
let isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
|
|
391
|
-
prevWeekNumber = weekNumber;
|
|
392
|
-
isWeekStarts.push(isWeekStart);
|
|
393
|
-
}
|
|
394
|
-
return isWeekStarts;
|
|
395
|
-
}
|
|
396
|
-
function buildCellRows(tDateProfile, dateEnv) {
|
|
397
|
-
let slotDates = tDateProfile.slotDates;
|
|
398
|
-
let formats = tDateProfile.headerFormats;
|
|
399
|
-
let cellRows = formats.map(() => []); // indexed by row,col
|
|
400
|
-
let slotAsDays = internal_cjs.asCleanDays(tDateProfile.slotDuration);
|
|
401
|
-
let guessedSlotUnit = slotAsDays === 7 ? 'week' :
|
|
402
|
-
slotAsDays === 1 ? 'day' :
|
|
403
|
-
null;
|
|
404
|
-
// specifically for navclicks
|
|
405
|
-
let rowUnitsFromFormats = formats.map((format) => (format.getLargestUnit ? format.getLargestUnit() : null));
|
|
406
|
-
// builds cellRows and slotCells
|
|
407
|
-
for (let i = 0; i < slotDates.length; i += 1) {
|
|
408
|
-
let date = slotDates[i];
|
|
409
|
-
let isWeekStart = tDateProfile.isWeekStarts[i];
|
|
410
|
-
for (let row = 0; row < formats.length; row += 1) {
|
|
411
|
-
let format = formats[row];
|
|
412
|
-
let rowCells = cellRows[row];
|
|
413
|
-
let leadingCell = rowCells[rowCells.length - 1];
|
|
414
|
-
let isLastRow = row === formats.length - 1;
|
|
415
|
-
let isSuperRow = formats.length > 1 && !isLastRow; // more than one row and not the last
|
|
416
|
-
let newCell = null;
|
|
417
|
-
let rowUnit = rowUnitsFromFormats[row] || (isLastRow ? guessedSlotUnit : null);
|
|
418
|
-
if (isSuperRow) {
|
|
419
|
-
let text = dateEnv.format(date, format);
|
|
420
|
-
if (!leadingCell || (leadingCell.text !== text)) {
|
|
421
|
-
newCell = buildCellObject(date, text, rowUnit);
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
leadingCell.colspan += 1;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
else if (!leadingCell ||
|
|
428
|
-
internal_cjs.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
|
|
429
|
-
let text = dateEnv.format(date, format);
|
|
430
|
-
newCell = buildCellObject(date, text, rowUnit);
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
leadingCell.colspan += 1;
|
|
434
|
-
}
|
|
435
|
-
if (newCell) {
|
|
436
|
-
newCell.weekStart = isWeekStart;
|
|
437
|
-
rowCells.push(newCell);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
return cellRows;
|
|
442
|
-
}
|
|
443
|
-
function buildCellObject(date, text, rowUnit) {
|
|
444
|
-
return { date, text, rowUnit, colspan: 1, isWeekStart: false };
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
class TimelineSlatCell extends internal_cjs.BaseComponent {
|
|
448
|
-
constructor() {
|
|
449
|
-
super(...arguments);
|
|
450
|
-
// ref
|
|
451
|
-
this.innerElRef = preact_cjs.createRef();
|
|
452
|
-
}
|
|
453
|
-
render() {
|
|
454
|
-
let { props, context } = this;
|
|
455
|
-
let { dateEnv, options } = context;
|
|
456
|
-
let { date, tDateProfile, isEm } = props;
|
|
457
|
-
let dateMeta = internal_cjs.getDateMeta(props.date, props.todayRange, props.nowDate, props.dateProfile);
|
|
458
|
-
let renderProps = Object.assign(Object.assign({ date: dateEnv.toDate(props.date) }, dateMeta), { view: context.viewApi });
|
|
459
|
-
return (preact_cjs.createElement(internal_cjs.ContentContainer, { tag: "div",
|
|
460
|
-
// fc-align-start shrinks width of InnerContent
|
|
461
|
-
// TODO: document this semantic className fc-timeline-slot-em
|
|
462
|
-
className: internal_cjs.joinClassNames('fc-timeline-slot', isEm && 'fc-timeline-slot-em', tDateProfile.isTimeScale && (internal_cjs.isInt(dateEnv.countDurationsBetween(// best to do this here?
|
|
463
|
-
tDateProfile.normalizedRange.start, props.date, tDateProfile.labelInterval)) ?
|
|
464
|
-
'fc-timeline-slot-major' :
|
|
465
|
-
'fc-timeline-slot-minor'), 'fc-timeline-slot-lane fc-cell fc-flex-col fc-align-start', props.borderStart && 'fc-border-s', props.isDay ?
|
|
466
|
-
internal_cjs.getDayClassName(dateMeta) :
|
|
467
|
-
internal_cjs.getSlotClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(date, {
|
|
468
|
-
omitTimeZoneOffset: true,
|
|
469
|
-
omitTime: !tDateProfile.isTimeScale,
|
|
470
|
-
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
|
|
471
|
-
width: props.width,
|
|
472
|
-
}, renderProps: renderProps, generatorName: "slotLaneContent", customGenerator: options.slotLaneContent, classNameGenerator: options.slotLaneClassNames, didMount: options.slotLaneDidMount, willUnmount: options.slotLaneWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "div", className: 'fc-cell-inner', elRef: this.innerElRef }))));
|
|
473
|
-
}
|
|
474
|
-
componentDidMount() {
|
|
475
|
-
const innerEl = this.innerElRef.current;
|
|
476
|
-
this.disconnectInnerWidth = internal_cjs.watchWidth(innerEl, (width) => {
|
|
477
|
-
internal_cjs.setRef(this.props.innerWidthRef, width);
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
componentWillUnmount() {
|
|
481
|
-
this.disconnectInnerWidth();
|
|
482
|
-
internal_cjs.setRef(this.props.innerWidthRef, null);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
class TimelineSlats extends internal_cjs.BaseComponent {
|
|
487
|
-
constructor() {
|
|
488
|
-
super(...arguments);
|
|
489
|
-
this.innerWidthRefMap = new internal_cjs.RefMap(() => {
|
|
490
|
-
internal_cjs.afterSize(this.handleInnerWidths);
|
|
491
|
-
});
|
|
492
|
-
this.handleInnerWidths = () => {
|
|
493
|
-
const innerWidthMap = this.innerWidthRefMap.current;
|
|
494
|
-
let max = 0;
|
|
495
|
-
for (const innerWidth of innerWidthMap.values()) {
|
|
496
|
-
max = Math.max(max, innerWidth);
|
|
497
|
-
}
|
|
498
|
-
// TODO: check to see if changed before firing ref!? YES. do in other places too
|
|
499
|
-
internal_cjs.setRef(this.props.innerWidthRef, max);
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
render() {
|
|
503
|
-
let { props, innerWidthRefMap } = this;
|
|
504
|
-
let { tDateProfile, slotWidth } = props;
|
|
505
|
-
let { slotDates, isWeekStarts } = tDateProfile;
|
|
506
|
-
let isDay = !tDateProfile.isTimeScale && !tDateProfile.largeUnit;
|
|
507
|
-
return (preact_cjs.createElement("div", { "aria-hidden": true, className: "fc-timeline-slots fc-fill fc-flex-row", style: { height: props.height } }, slotDates.map((slotDate, i) => {
|
|
508
|
-
let key = slotDate.toISOString();
|
|
509
|
-
return (preact_cjs.createElement(TimelineSlatCell, { key: key, date: slotDate, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isEm: isWeekStarts[i], isDay: isDay, borderStart: Boolean(i),
|
|
510
|
-
// ref
|
|
511
|
-
innerWidthRef: innerWidthRefMap.createRef(key),
|
|
512
|
-
// dimensions
|
|
513
|
-
width: slotWidth }));
|
|
514
|
-
})));
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/*
|
|
519
|
-
TODO: rename this file!
|
|
520
|
-
*/
|
|
521
|
-
// returned value is between 0 and the number of snaps
|
|
522
|
-
function computeDateSnapCoverage$1(date, tDateProfile, dateEnv) {
|
|
523
|
-
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
|
|
524
|
-
if (snapDiff < 0) {
|
|
525
|
-
return 0;
|
|
526
|
-
}
|
|
527
|
-
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
|
|
528
|
-
return tDateProfile.snapCnt;
|
|
529
|
-
}
|
|
530
|
-
let snapDiffInt = Math.floor(snapDiff);
|
|
531
|
-
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
|
|
532
|
-
if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
|
|
533
|
-
snapCoverage += snapDiff - snapDiffInt; // add the remainder
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
// a fractional value, meaning the date is not visible
|
|
537
|
-
// always round up in this case. works for start AND end dates in a range.
|
|
538
|
-
snapCoverage = Math.ceil(snapCoverage);
|
|
539
|
-
}
|
|
540
|
-
return snapCoverage;
|
|
541
|
-
}
|
|
542
|
-
/*
|
|
543
|
-
TODO: DRY up with elsewhere?
|
|
544
|
-
*/
|
|
545
|
-
function horizontalsToCss(hcoord, isRtl) {
|
|
546
|
-
if (!hcoord) {
|
|
547
|
-
return {};
|
|
548
|
-
}
|
|
549
|
-
if (isRtl) {
|
|
550
|
-
return { right: hcoord.start, width: hcoord.size };
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
return { left: hcoord.start, width: hcoord.size };
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
function horizontalCoordToCss(start, isRtl) {
|
|
557
|
-
if (isRtl) {
|
|
558
|
-
return { right: start };
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
return { left: start };
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
function createVerticalStyle(props) {
|
|
566
|
-
if (props) {
|
|
567
|
-
return {
|
|
568
|
-
top: props.start,
|
|
569
|
-
height: props.size,
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
function createHorizontalStyle(// TODO: DRY up?
|
|
574
|
-
props, isRtl) {
|
|
575
|
-
if (props) {
|
|
576
|
-
return {
|
|
577
|
-
[isRtl ? 'right' : 'left']: props.start,
|
|
578
|
-
width: props.size,
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
// Timeline-specific
|
|
583
|
-
// -------------------------------------------------------------------------------------------------
|
|
584
|
-
const MIN_SLOT_WIDTH = 30; // for real
|
|
585
|
-
/*
|
|
586
|
-
TODO: DRY with computeSlatHeight?
|
|
587
|
-
*/
|
|
588
|
-
function computeSlotWidth(slatCnt, slatsPerLabel, slatMinWidth, labelInnerWidth, viewportWidth) {
|
|
589
|
-
if (labelInnerWidth == null || viewportWidth == null) {
|
|
590
|
-
return [undefined, undefined, false];
|
|
591
|
-
}
|
|
592
|
-
slatMinWidth = Math.max(slatMinWidth || 0, (labelInnerWidth + 1) / slatsPerLabel, MIN_SLOT_WIDTH);
|
|
593
|
-
const slatTryWidth = viewportWidth / slatCnt;
|
|
594
|
-
let slatLiquid;
|
|
595
|
-
let slatWidth;
|
|
596
|
-
if (slatTryWidth >= slatMinWidth) {
|
|
597
|
-
slatLiquid = true;
|
|
598
|
-
slatWidth = slatTryWidth;
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
slatLiquid = false;
|
|
602
|
-
slatWidth = Math.max(slatMinWidth, slatTryWidth);
|
|
603
|
-
}
|
|
604
|
-
return [slatWidth * slatCnt, slatWidth, slatLiquid];
|
|
605
|
-
}
|
|
606
|
-
function timeToCoord(// pixels
|
|
607
|
-
time, dateEnv, dateProfile, tDateProfile, slowWidth) {
|
|
608
|
-
let date = dateEnv.add(dateProfile.activeRange.start, time);
|
|
609
|
-
if (!tDateProfile.isTimeScale) {
|
|
610
|
-
date = internal_cjs.startOfDay(date);
|
|
611
|
-
}
|
|
612
|
-
return dateToCoord(date, dateEnv, tDateProfile, slowWidth);
|
|
613
|
-
}
|
|
614
|
-
function dateToCoord(// pixels
|
|
615
|
-
date, dateEnv, tDateProfile, slotWidth) {
|
|
616
|
-
let snapCoverage = computeDateSnapCoverage(date, tDateProfile, dateEnv);
|
|
617
|
-
let slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
|
|
618
|
-
return slotCoverage * slotWidth;
|
|
619
|
-
}
|
|
620
|
-
/*
|
|
621
|
-
returned value is between 0 and the number of snaps
|
|
622
|
-
*/
|
|
623
|
-
function computeDateSnapCoverage(date, tDateProfile, dateEnv) {
|
|
624
|
-
let snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
|
|
625
|
-
if (snapDiff < 0) {
|
|
626
|
-
return 0;
|
|
627
|
-
}
|
|
628
|
-
if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
|
|
629
|
-
return tDateProfile.snapCnt;
|
|
630
|
-
}
|
|
631
|
-
let snapDiffInt = Math.floor(snapDiff);
|
|
632
|
-
let snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
|
|
633
|
-
if (internal_cjs.isInt(snapCoverage)) { // not an in-between value
|
|
634
|
-
snapCoverage += snapDiff - snapDiffInt; // add the remainder
|
|
635
|
-
}
|
|
636
|
-
else {
|
|
637
|
-
// a fractional value, meaning the date is not visible
|
|
638
|
-
// always round up in this case. works for start AND end dates in a range.
|
|
639
|
-
snapCoverage = Math.ceil(snapCoverage);
|
|
640
|
-
}
|
|
641
|
-
return snapCoverage;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
function computeManySegHorizontals(segs, segMinWidth, dateEnv, tDateProfile, slotWidth) {
|
|
645
|
-
const res = {};
|
|
646
|
-
for (const seg of segs) {
|
|
647
|
-
res[internal_cjs.getEventKey(seg)] = computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth);
|
|
648
|
-
}
|
|
649
|
-
return res;
|
|
650
|
-
}
|
|
651
|
-
function computeSegHorizontals(seg, segMinWidth, dateEnv, tDateProfile, slotWidth) {
|
|
652
|
-
const startCoord = dateToCoord(seg.startDate, dateEnv, tDateProfile, slotWidth);
|
|
653
|
-
const endCoord = dateToCoord(seg.endDate, dateEnv, tDateProfile, slotWidth);
|
|
654
|
-
let size = endCoord - startCoord;
|
|
655
|
-
if (segMinWidth) {
|
|
656
|
-
size = Math.max(size, segMinWidth);
|
|
657
|
-
}
|
|
658
|
-
return { start: startCoord, size };
|
|
659
|
-
}
|
|
660
|
-
function computeFgSegPlacements(// mostly horizontals
|
|
661
|
-
segs, segHorizontals, // TODO: use Map
|
|
662
|
-
segHeights, // keyed by instanceId
|
|
663
|
-
hiddenGroupHeights, strictOrder, maxDepth) {
|
|
664
|
-
const segRanges = [];
|
|
665
|
-
// isn't it true that there will either be ALL hcoords or NONE? can optimize
|
|
666
|
-
for (const seg of segs) {
|
|
667
|
-
const hcoords = segHorizontals[internal_cjs.getEventKey(seg)];
|
|
668
|
-
if (hcoords) {
|
|
669
|
-
segRanges.push(Object.assign(Object.assign({}, seg), { start: hcoords.start, end: hcoords.start + hcoords.size }));
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
const hierarchy = new internal_cjs.SegHierarchy(segRanges, (seg) => segHeights.get(internal_cjs.getEventKey(seg)), strictOrder, undefined, // maxCoord
|
|
673
|
-
maxDepth);
|
|
674
|
-
const segTops = new Map();
|
|
675
|
-
hierarchy.traverseSegs((seg, segTop) => {
|
|
676
|
-
segTops.set(internal_cjs.getEventKey(seg), segTop);
|
|
677
|
-
});
|
|
678
|
-
const { hiddenSegs } = hierarchy;
|
|
679
|
-
let totalHeight = 0;
|
|
680
|
-
for (const segRange of segRanges) {
|
|
681
|
-
const segKey = internal_cjs.getEventKey(segRange);
|
|
682
|
-
const segHeight = segHeights.get(segKey);
|
|
683
|
-
const segTop = segTops.get(segKey);
|
|
684
|
-
if (segHeight != null) {
|
|
685
|
-
if (segTop != null) {
|
|
686
|
-
totalHeight = Math.max(totalHeight, segTop + segHeight);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
const hiddenGroups = internal_cjs.groupIntersectingSegs(hiddenSegs);
|
|
691
|
-
const hiddenGroupTops = new Map();
|
|
692
|
-
// HACK for hiddenGroup findInsertion() call
|
|
693
|
-
hierarchy.strictOrder = true;
|
|
694
|
-
for (const hiddenGroup of hiddenGroups) {
|
|
695
|
-
const { levelCoord: top } = hierarchy.findInsertion(hiddenGroup, 0);
|
|
696
|
-
const hiddenGroupHeight = hiddenGroupHeights.get(hiddenGroup.key) || 0;
|
|
697
|
-
hiddenGroupTops.set(hiddenGroup.key, top);
|
|
698
|
-
totalHeight = Math.max(totalHeight, top + hiddenGroupHeight);
|
|
699
|
-
}
|
|
700
|
-
return [
|
|
701
|
-
segTops,
|
|
702
|
-
hiddenGroups,
|
|
703
|
-
hiddenGroupTops,
|
|
704
|
-
totalHeight,
|
|
705
|
-
];
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
class TimelineLaneBg extends internal_cjs.BaseComponent {
|
|
709
|
-
render() {
|
|
710
|
-
let { props } = this;
|
|
711
|
-
let highlightSeg = [].concat(props.eventResizeSegs, props.dateSelectionSegs);
|
|
712
|
-
return (preact_cjs.createElement(preact_cjs.Fragment, null,
|
|
713
|
-
this.renderSegs(props.businessHourSegs || [], 'non-business'),
|
|
714
|
-
this.renderSegs(props.bgEventSegs || [], 'bg-event'),
|
|
715
|
-
this.renderSegs(highlightSeg, 'highlight')));
|
|
716
|
-
}
|
|
717
|
-
renderSegs(segs, fillType) {
|
|
718
|
-
let { tDateProfile, todayRange, nowDate, slotWidth } = this.props;
|
|
719
|
-
let { dateEnv, isRtl } = this.context;
|
|
720
|
-
return (preact_cjs.createElement(preact_cjs.Fragment, null, segs.map((seg) => {
|
|
721
|
-
let hStyle; // TODO
|
|
722
|
-
if (slotWidth != null) {
|
|
723
|
-
let segHorizontal = computeSegHorizontals(seg, undefined, dateEnv, tDateProfile, slotWidth);
|
|
724
|
-
hStyle = horizontalsToCss(segHorizontal, isRtl);
|
|
725
|
-
}
|
|
726
|
-
return (preact_cjs.createElement("div", { key: internal_cjs.buildEventRangeKey(seg.eventRange), className: "fc-fill-y", style: hStyle }, fillType === 'bg-event' ?
|
|
727
|
-
preact_cjs.createElement(internal_cjs.BgEvent, Object.assign({ eventRange: seg.eventRange, isStart: seg.isStart, isEnd: seg.isEnd }, internal_cjs.getEventRangeMeta(seg.eventRange, todayRange, nowDate))) : (internal_cjs.renderFill(fillType))));
|
|
728
|
-
})));
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
class TimelineLaneSlicer extends internal_cjs.Slicer {
|
|
733
|
-
sliceRange(origRange, dateProfile, dateProfileGenerator, tDateProfile, dateEnv) {
|
|
734
|
-
let normalRange = normalizeRange(origRange, tDateProfile, dateEnv);
|
|
735
|
-
let segs = [];
|
|
736
|
-
// protect against when the span is entirely in an invalid date region
|
|
737
|
-
if (computeDateSnapCoverage$1(normalRange.start, tDateProfile, dateEnv)
|
|
738
|
-
< computeDateSnapCoverage$1(normalRange.end, tDateProfile, dateEnv)) {
|
|
739
|
-
// intersect the footprint's range with the grid's range
|
|
740
|
-
let slicedRange = internal_cjs.intersectRanges(normalRange, tDateProfile.normalizedRange);
|
|
741
|
-
if (slicedRange) {
|
|
742
|
-
segs.push({
|
|
743
|
-
startDate: slicedRange.start,
|
|
744
|
-
endDate: slicedRange.end,
|
|
745
|
-
isStart: slicedRange.start.valueOf() === normalRange.start.valueOf()
|
|
746
|
-
&& isValidDate(slicedRange.start, tDateProfile, dateProfile, dateProfileGenerator),
|
|
747
|
-
isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf()
|
|
748
|
-
&& isValidDate(internal_cjs.addMs(slicedRange.end, -1), tDateProfile, dateProfile, dateProfileGenerator),
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
return segs;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
const DEFAULT_TIME_FORMAT = internal_cjs.createFormatter({
|
|
757
|
-
hour: 'numeric',
|
|
758
|
-
minute: '2-digit',
|
|
759
|
-
omitZeroMinute: true,
|
|
760
|
-
meridiem: 'narrow',
|
|
761
|
-
});
|
|
762
|
-
class TimelineEvent extends internal_cjs.BaseComponent {
|
|
763
|
-
render() {
|
|
764
|
-
let { props, context } = this;
|
|
765
|
-
let { options } = context;
|
|
766
|
-
return (preact_cjs.createElement(internal_cjs.StandardEvent, Object.assign({}, props, { className: internal_cjs.joinClassNames('fc-timeline-event', options.eventOverlap === false // TODO: fix bad default
|
|
767
|
-
&& 'fc-timeline-event-spacious', 'fc-h-event'), defaultTimeFormat: DEFAULT_TIME_FORMAT, defaultDisplayEventTime: !props.isTimeScale })));
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
class TimelineLaneMoreLink extends internal_cjs.BaseComponent {
|
|
772
|
-
render() {
|
|
773
|
-
let { props } = this;
|
|
774
|
-
let { hiddenSegs, resourceId, forcedInvisibleMap } = props;
|
|
775
|
-
let dateSpanProps = resourceId ? { resourceId } : {};
|
|
776
|
-
return (preact_cjs.createElement(internal_cjs.MoreLinkContainer, { className: 'fc-timeline-more-link', allDayDate: null, segs: hiddenSegs, hiddenSegs: hiddenSegs, dateProfile: props.dateProfile, todayRange: props.todayRange, dateSpanProps: dateSpanProps, popoverContent: () => (preact_cjs.createElement(preact_cjs.Fragment, null, hiddenSegs.map((seg) => {
|
|
777
|
-
let { eventRange } = seg;
|
|
778
|
-
let instanceId = eventRange.instance.instanceId;
|
|
779
|
-
return (preact_cjs.createElement("div", { key: instanceId, style: { visibility: forcedInvisibleMap[instanceId] ? 'hidden' : '' } },
|
|
780
|
-
preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection }, internal_cjs.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
|
|
781
|
-
}))) }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: "div", className: 'fc-timeline-more-link-inner fc-sticky-s' }))));
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/*
|
|
786
|
-
TODO: make DRY with other Event Harnesses
|
|
787
|
-
*/
|
|
788
|
-
class TimelineEventHarness extends preact_cjs.Component {
|
|
789
|
-
constructor() {
|
|
790
|
-
super(...arguments);
|
|
791
|
-
// ref
|
|
792
|
-
this.rootElRef = preact_cjs.createRef();
|
|
793
|
-
}
|
|
794
|
-
render() {
|
|
795
|
-
const { props } = this;
|
|
796
|
-
return (preact_cjs.createElement("div", { className: "fc-abs", style: props.style, ref: this.rootElRef }, props.children));
|
|
797
|
-
}
|
|
798
|
-
componentDidMount() {
|
|
799
|
-
const rootEl = this.rootElRef.current; // TODO: make dynamic with useEffect
|
|
800
|
-
this.disconnectHeight = internal_cjs.watchHeight(rootEl, (height) => {
|
|
801
|
-
internal_cjs.setRef(this.props.heightRef, height);
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
componentWillUnmount() {
|
|
805
|
-
this.disconnectHeight();
|
|
806
|
-
internal_cjs.setRef(this.props.heightRef, null);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
/*
|
|
811
|
-
TODO: split TimelineLaneBg and TimelineLaneFg?
|
|
812
|
-
*/
|
|
813
|
-
class TimelineLane extends internal_cjs.BaseComponent {
|
|
814
|
-
constructor() {
|
|
815
|
-
super(...arguments);
|
|
816
|
-
// memo
|
|
817
|
-
this.sortEventSegs = internal_cjs.memoize(internal_cjs.sortEventSegs);
|
|
818
|
-
// refs
|
|
819
|
-
this.segHeightRefMap = new internal_cjs.RefMap(() => {
|
|
820
|
-
internal_cjs.afterSize(this.handleSegHeights);
|
|
821
|
-
});
|
|
822
|
-
this.moreLinkHeightRefMap = new internal_cjs.RefMap(() => {
|
|
823
|
-
internal_cjs.afterSize(this.handleMoreLinkHeights);
|
|
824
|
-
});
|
|
825
|
-
// internal
|
|
826
|
-
this.slicer = new TimelineLaneSlicer();
|
|
827
|
-
this.handleMoreLinkHeights = () => {
|
|
828
|
-
this.setState({ moreLinkHeightRev: this.moreLinkHeightRefMap.rev }); // will trigger rerender
|
|
829
|
-
};
|
|
830
|
-
this.handleSegHeights = () => {
|
|
831
|
-
this.setState({ segHeightRev: this.segHeightRefMap.rev }); // will trigger rerender
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
/*
|
|
835
|
-
TODO: lots of memoization needed here!
|
|
836
|
-
*/
|
|
837
|
-
render() {
|
|
838
|
-
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
|
|
839
|
-
let { options } = context;
|
|
840
|
-
let { dateProfile, tDateProfile } = props;
|
|
841
|
-
let slicedProps = this.slicer.sliceProps(props, dateProfile, tDateProfile.isTimeScale ? null : props.nextDayThreshold, context, // wish we didn't have to pass in the rest of the args...
|
|
842
|
-
dateProfile, context.dateProfileGenerator, tDateProfile, context.dateEnv);
|
|
843
|
-
let mirrorSegs = (slicedProps.eventDrag ? slicedProps.eventDrag.segs : null) ||
|
|
844
|
-
(slicedProps.eventResize ? slicedProps.eventResize.segs : null) ||
|
|
845
|
-
[];
|
|
846
|
-
let fgSegs = this.sortEventSegs(slicedProps.fgEventSegs, options.eventOrder);
|
|
847
|
-
let fgSegHorizontals = props.slotWidth != null
|
|
848
|
-
? computeManySegHorizontals(fgSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
|
|
849
|
-
: {};
|
|
850
|
-
let [fgSegTops, hiddenGroups, hiddenGroupTops, totalHeight] = computeFgSegPlacements(fgSegs, fgSegHorizontals, segHeightRefMap.current, moreLinkHeightRefMap.current, options.eventOrderStrict, options.eventMaxStack);
|
|
851
|
-
let forcedInvisibleMap = // TODO: more convenient/DRY
|
|
852
|
-
(slicedProps.eventDrag ? slicedProps.eventDrag.affectedInstances : null) ||
|
|
853
|
-
(slicedProps.eventResize ? slicedProps.eventResize.affectedInstances : null) ||
|
|
854
|
-
{};
|
|
855
|
-
return (preact_cjs.createElement(preact_cjs.Fragment, null,
|
|
856
|
-
preact_cjs.createElement(TimelineLaneBg, { tDateProfile: tDateProfile, nowDate: props.nowDate, todayRange: props.todayRange,
|
|
857
|
-
// content
|
|
858
|
-
bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventResizeSegs: slicedProps.eventResize ? slicedProps.eventResize.segs : [] /* bad new empty array? */,
|
|
859
|
-
// dimensions
|
|
860
|
-
slotWidth: props.slotWidth }),
|
|
861
|
-
preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-timeline-events', options.eventOverlap === false // TODO: fix bad default
|
|
862
|
-
? 'fc-timeline-events-overlap-disabled'
|
|
863
|
-
: 'fc-timeline-events-overlap-enabled', 'fc-content-box'), style: { height: totalHeight } },
|
|
864
|
-
this.renderFgSegs(fgSegs, fgSegHorizontals, fgSegTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, false, // isDragging
|
|
865
|
-
false, // isResizing
|
|
866
|
-
false),
|
|
867
|
-
this.renderFgSegs(mirrorSegs, props.slotWidth // TODO: memoize
|
|
868
|
-
? computeManySegHorizontals(mirrorSegs, options.eventMinWidth, context.dateEnv, tDateProfile, props.slotWidth)
|
|
869
|
-
: {}, fgSegTops, {}, // forcedInvisibleMap
|
|
870
|
-
[], // hiddenGroups
|
|
871
|
-
new Map(), // hiddenGroupTops
|
|
872
|
-
Boolean(slicedProps.eventDrag), Boolean(slicedProps.eventResize), false))));
|
|
873
|
-
}
|
|
874
|
-
renderFgSegs(segs, segHorizontals, segTops, forcedInvisibleMap, hiddenGroups, hiddenGroupTops, isDragging, isResizing, isDateSelecting) {
|
|
875
|
-
let { props, context, segHeightRefMap, moreLinkHeightRefMap } = this;
|
|
876
|
-
let isMirror = isDragging || isResizing || isDateSelecting;
|
|
877
|
-
return (preact_cjs.createElement(preact_cjs.Fragment, null,
|
|
878
|
-
segs.map((seg) => {
|
|
879
|
-
const { eventRange } = seg;
|
|
880
|
-
const { instanceId } = eventRange.instance;
|
|
881
|
-
const segTop = segTops.get(instanceId);
|
|
882
|
-
const segHorizontal = segHorizontals[instanceId];
|
|
883
|
-
const isVisible = isMirror ||
|
|
884
|
-
(segHorizontal && segTop != null && !forcedInvisibleMap[instanceId]);
|
|
885
|
-
return (preact_cjs.createElement(TimelineEventHarness, { key: instanceId, style: Object.assign({ visibility: isVisible ? '' : 'hidden', top: segTop || 0 }, horizontalsToCss(segHorizontal, context.isRtl)), heightRef: isMirror ? undefined : segHeightRefMap.createRef(instanceId) },
|
|
886
|
-
preact_cjs.createElement(TimelineEvent, Object.assign({ isTimeScale: props.tDateProfile.isTimeScale, eventRange: eventRange, isStart: seg.isStart, isEnd: seg.isEnd, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === props.eventSelection /* TODO: bad for mirror? */ }, internal_cjs.getEventRangeMeta(eventRange, props.todayRange, props.nowDate)))));
|
|
887
|
-
}),
|
|
888
|
-
hiddenGroups.map((hiddenGroup) => (preact_cjs.createElement(TimelineEventHarness, { key: hiddenGroup.key, style: Object.assign({ top: hiddenGroupTops.get(hiddenGroup.key) || 0 }, horizontalsToCss({
|
|
889
|
-
start: hiddenGroup.start,
|
|
890
|
-
size: hiddenGroup.end - hiddenGroup.start
|
|
891
|
-
}, context.isRtl)), heightRef: moreLinkHeightRefMap.createRef(hiddenGroup.key) },
|
|
892
|
-
preact_cjs.createElement(TimelineLaneMoreLink, { hiddenSegs: hiddenGroup.segs, dateProfile: props.dateProfile, nowDate: props.nowDate, todayRange: props.todayRange, isTimeScale: props.tDateProfile.isTimeScale, eventSelection: props.eventSelection, resourceId: props.resourceId, forcedInvisibleMap: forcedInvisibleMap }))))));
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
class TimelineHeaderCell extends internal_cjs.BaseComponent {
|
|
897
|
-
constructor() {
|
|
898
|
-
super(...arguments);
|
|
899
|
-
// memo
|
|
900
|
-
this.refineRenderProps = internal_cjs.memoizeObjArg(refineRenderProps);
|
|
901
|
-
// ref
|
|
902
|
-
this.innerElRef = preact_cjs.createRef();
|
|
903
|
-
}
|
|
904
|
-
render() {
|
|
905
|
-
let { props, context } = this;
|
|
906
|
-
let { dateEnv, options } = context;
|
|
907
|
-
let { cell, dateProfile, tDateProfile } = props;
|
|
908
|
-
// the cell.rowUnit is f'd
|
|
909
|
-
// giving 'month' for a 3-day view
|
|
910
|
-
// workaround: to infer day, do NOT time
|
|
911
|
-
let dateMeta = internal_cjs.getDateMeta(cell.date, props.todayRange, props.nowDate, dateProfile);
|
|
912
|
-
let renderProps = this.refineRenderProps({
|
|
913
|
-
level: props.rowLevel,
|
|
914
|
-
dateMarker: cell.date,
|
|
915
|
-
text: cell.text,
|
|
916
|
-
dateEnv: context.dateEnv,
|
|
917
|
-
viewApi: context.viewApi,
|
|
918
|
-
});
|
|
919
|
-
let isNavLink = !dateMeta.isDisabled && (cell.rowUnit && cell.rowUnit !== 'time');
|
|
920
|
-
return (preact_cjs.createElement(internal_cjs.ContentContainer, { tag: "div", className: internal_cjs.joinClassNames('fc-timeline-slot-label fc-timeline-slot', cell.isWeekStart && 'fc-timeline-slot-em', // TODO: document this semantic className
|
|
921
|
-
'fc-header-cell fc-cell fc-flex-col fc-justify-center', props.borderStart && 'fc-border-s', props.isCentered ? 'fc-align-center' : 'fc-align-start',
|
|
922
|
-
// TODO: so slot classnames for week/month/bigger. see note above about rowUnit
|
|
923
|
-
cell.rowUnit === 'time' ?
|
|
924
|
-
internal_cjs.getSlotClassName(dateMeta) :
|
|
925
|
-
internal_cjs.getDayClassName(dateMeta)), attrs: Object.assign({ 'data-date': dateEnv.formatIso(cell.date, {
|
|
926
|
-
omitTime: !tDateProfile.isTimeScale,
|
|
927
|
-
omitTimeZoneOffset: true,
|
|
928
|
-
}) }, (dateMeta.isToday ? { 'aria-current': 'date' } : {})), style: {
|
|
929
|
-
width: props.slotWidth != null
|
|
930
|
-
? props.slotWidth * cell.colspan
|
|
931
|
-
: undefined,
|
|
932
|
-
}, renderProps: renderProps, generatorName: "slotLabelContent", customGenerator: options.slotLabelContent, defaultGenerator: renderInnerContent, classNameGenerator: options.slotLabelClassNames, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, (InnerContent) => (preact_cjs.createElement(InnerContent, { tag: 'div', attrs: isNavLink
|
|
933
|
-
// not tabbable because parent is aria-hidden
|
|
934
|
-
? internal_cjs.buildNavLinkAttrs(context, cell.date, cell.rowUnit, undefined, /* isTabbable = */ false)
|
|
935
|
-
: {} // don't bother with aria-hidden because parent already hidden
|
|
936
|
-
, className: internal_cjs.joinClassNames('fc-cell-inner fc-padding-md', props.isSticky && 'fc-sticky-s'), elRef: this.innerElRef }))));
|
|
937
|
-
}
|
|
938
|
-
componentDidMount() {
|
|
939
|
-
const { props } = this;
|
|
940
|
-
const innerEl = this.innerElRef.current; // TODO: make dynamic with useEffect
|
|
941
|
-
this.detachSize = internal_cjs.watchSize(innerEl, (width, height) => {
|
|
942
|
-
internal_cjs.setRef(props.innerWidthRef, width);
|
|
943
|
-
internal_cjs.setRef(props.innerHeightRef, height);
|
|
944
|
-
// HACK for sticky-centering
|
|
945
|
-
innerEl.style.left = innerEl.style.right =
|
|
946
|
-
(props.isCentered && props.isSticky)
|
|
947
|
-
? `calc(50% - ${width / 2}px)`
|
|
948
|
-
: '';
|
|
949
|
-
});
|
|
950
|
-
}
|
|
951
|
-
componentWillUnmount() {
|
|
952
|
-
const { props } = this;
|
|
953
|
-
this.detachSize();
|
|
954
|
-
internal_cjs.setRef(props.innerWidthRef, null);
|
|
955
|
-
internal_cjs.setRef(props.innerHeightRef, null);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
// Utils
|
|
959
|
-
// -------------------------------------------------------------------------------------------------
|
|
960
|
-
function renderInnerContent(renderProps) {
|
|
961
|
-
return renderProps.text;
|
|
962
|
-
}
|
|
963
|
-
function refineRenderProps(input) {
|
|
964
|
-
return {
|
|
965
|
-
level: input.level,
|
|
966
|
-
date: input.dateEnv.toDate(input.dateMarker),
|
|
967
|
-
view: input.viewApi,
|
|
968
|
-
text: input.text,
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
class TimelineHeaderRow extends internal_cjs.BaseComponent {
|
|
973
|
-
constructor() {
|
|
974
|
-
super(...arguments);
|
|
975
|
-
// refs
|
|
976
|
-
this.innerWidthRefMap = new internal_cjs.RefMap(() => {
|
|
977
|
-
internal_cjs.afterSize(this.handleInnerWidths);
|
|
978
|
-
});
|
|
979
|
-
this.innerHeightRefMap = new internal_cjs.RefMap(() => {
|
|
980
|
-
internal_cjs.afterSize(this.handleInnerHeights);
|
|
981
|
-
});
|
|
982
|
-
this.handleInnerWidths = () => {
|
|
983
|
-
const innerWidthMap = this.innerWidthRefMap.current;
|
|
984
|
-
let max = 0;
|
|
985
|
-
for (const innerWidth of innerWidthMap.values()) {
|
|
986
|
-
max = Math.max(max, innerWidth);
|
|
987
|
-
}
|
|
988
|
-
// TODO: ensure not equal?
|
|
989
|
-
internal_cjs.setRef(this.props.innerWidthRef, max);
|
|
990
|
-
};
|
|
991
|
-
this.handleInnerHeights = () => {
|
|
992
|
-
const innerHeightMap = this.innerHeightRefMap.current;
|
|
993
|
-
let max = 0;
|
|
994
|
-
for (const innerHeight of innerHeightMap.values()) {
|
|
995
|
-
max = Math.max(max, innerHeight);
|
|
996
|
-
}
|
|
997
|
-
// TODO: ensure not equal?
|
|
998
|
-
internal_cjs.setRef(this.props.innerHeighRef, max);
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
render() {
|
|
1002
|
-
const { props, innerWidthRefMap, innerHeightRefMap } = this;
|
|
1003
|
-
const isCentered = !(props.tDateProfile.isTimeScale && props.isLastRow);
|
|
1004
|
-
const isSticky = !props.isLastRow;
|
|
1005
|
-
return (preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-flex-row fc-grow', // TODO: move fc-grow to parent?
|
|
1006
|
-
!props.isLastRow && 'fc-border-b') }, props.cells.map((cell, cellI) => {
|
|
1007
|
-
// TODO: make this part of the cell obj?
|
|
1008
|
-
// TODO: rowUnit seems wrong sometimes. says 'month' when it should be day
|
|
1009
|
-
// TODO: rowUnit is relevant to whole row. put it on a row object, not the cells
|
|
1010
|
-
// TODO: use rowUnit to key the Row itself?
|
|
1011
|
-
const key = cell.rowUnit + ':' + cell.date.toISOString();
|
|
1012
|
-
return (preact_cjs.createElement(TimelineHeaderCell, { key: key, cell: cell, rowLevel: props.rowLevel, dateProfile: props.dateProfile, tDateProfile: props.tDateProfile, todayRange: props.todayRange, nowDate: props.nowDate, isCentered: isCentered, isSticky: isSticky, borderStart: Boolean(cellI),
|
|
1013
|
-
// refs
|
|
1014
|
-
innerWidthRef: innerWidthRefMap.createRef(key), innerHeightRef: innerHeightRefMap.createRef(key),
|
|
1015
|
-
// dimensions
|
|
1016
|
-
slotWidth: props.slotWidth }));
|
|
1017
|
-
})));
|
|
1018
|
-
}
|
|
1019
|
-
componentWillUnmount() {
|
|
1020
|
-
internal_cjs.setRef(this.props.innerWidthRef, null);
|
|
1021
|
-
internal_cjs.setRef(this.props.innerHeighRef, null);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
class TimelineNowIndicatorLine extends internal_cjs.BaseComponent {
|
|
1026
|
-
render() {
|
|
1027
|
-
const { props, context } = this;
|
|
1028
|
-
return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
|
|
1029
|
-
preact_cjs.createElement(internal_cjs.NowIndicatorContainer // TODO: make separate component?
|
|
1030
|
-
, { className: 'fc-timeline-now-indicator-line', style: props.slotWidth != null
|
|
1031
|
-
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
|
|
1032
|
-
: {}, isAxis: false, date: props.nowDate })));
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
class TimelineNowIndicatorArrow extends internal_cjs.BaseComponent {
|
|
1037
|
-
render() {
|
|
1038
|
-
const { props, context } = this;
|
|
1039
|
-
return (preact_cjs.createElement("div", { className: "fc-timeline-now-indicator-container" },
|
|
1040
|
-
preact_cjs.createElement(internal_cjs.NowIndicatorContainer, { className: 'fc-timeline-now-indicator-arrow', style: props.slotWidth != null
|
|
1041
|
-
? horizontalCoordToCss(dateToCoord(props.nowDate, context.dateEnv, props.tDateProfile, props.slotWidth), context.isRtl)
|
|
1042
|
-
: {}, isAxis: true, date: props.nowDate })));
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
class TimelineView extends internal_cjs.DateComponent {
|
|
1047
|
-
constructor() {
|
|
1048
|
-
super(...arguments);
|
|
1049
|
-
// memoized
|
|
1050
|
-
this.buildTimelineDateProfile = internal_cjs.memoize(buildTimelineDateProfile);
|
|
1051
|
-
this.computeSlotWidth = internal_cjs.memoize(computeSlotWidth);
|
|
1052
|
-
// refs
|
|
1053
|
-
this.headerScrollerRef = preact_cjs.createRef();
|
|
1054
|
-
this.bodyScrollerRef = preact_cjs.createRef();
|
|
1055
|
-
this.footerScrollerRef = preact_cjs.createRef();
|
|
1056
|
-
this.headerRowInnerWidthMap = new internal_cjs.RefMap(() => {
|
|
1057
|
-
internal_cjs.afterSize(this.handleSlotInnerWidths);
|
|
1058
|
-
});
|
|
1059
|
-
this.scrollTime = null;
|
|
1060
|
-
// Sizing
|
|
1061
|
-
// -----------------------------------------------------------------------------------------------
|
|
1062
|
-
this.handleBodySlotInnerWidth = (innerWidth) => {
|
|
1063
|
-
this.bodySlotInnerWidth = innerWidth;
|
|
1064
|
-
internal_cjs.afterSize(this.handleSlotInnerWidths);
|
|
1065
|
-
};
|
|
1066
|
-
this.handleSlotInnerWidths = () => {
|
|
1067
|
-
const { state } = this;
|
|
1068
|
-
const slotInnerWidth = Math.max(this.headerRowInnerWidthMap.current.get(this.tDateProfile.cellRows.length - 1) || 0, this.bodySlotInnerWidth);
|
|
1069
|
-
if (state.slotInnerWidth !== slotInnerWidth) {
|
|
1070
|
-
this.setState({ slotInnerWidth });
|
|
1071
|
-
}
|
|
1072
|
-
};
|
|
1073
|
-
this.handleClientWidth = (clientWidth) => {
|
|
1074
|
-
this.setState({
|
|
1075
|
-
clientWidth,
|
|
1076
|
-
});
|
|
1077
|
-
};
|
|
1078
|
-
this.handleEndScrollbarWidth = (endScrollbarWidth) => {
|
|
1079
|
-
this.setState({
|
|
1080
|
-
endScrollbarWidth
|
|
1081
|
-
});
|
|
1082
|
-
};
|
|
1083
|
-
this.handleTimeScrollRequest = (scrollTime) => {
|
|
1084
|
-
this.scrollTime = scrollTime;
|
|
1085
|
-
this.applyTimeScroll();
|
|
1086
|
-
};
|
|
1087
|
-
this.handleTimeScrollEnd = ({ isUser }) => {
|
|
1088
|
-
if (isUser) {
|
|
1089
|
-
this.scrollTime = null;
|
|
1090
|
-
}
|
|
1091
|
-
};
|
|
1092
|
-
// Hit System
|
|
1093
|
-
// -----------------------------------------------------------------------------------------------
|
|
1094
|
-
this.handeBodyEl = (el) => {
|
|
1095
|
-
this.bodyEl = el;
|
|
1096
|
-
if (el) {
|
|
1097
|
-
this.context.registerInteractiveComponent(this, { el });
|
|
1098
|
-
}
|
|
1099
|
-
else {
|
|
1100
|
-
this.context.unregisterInteractiveComponent(this);
|
|
1101
|
-
}
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1104
|
-
render() {
|
|
1105
|
-
const { props, state, context } = this;
|
|
1106
|
-
const { options } = context;
|
|
1107
|
-
/* date */
|
|
1108
|
-
const tDateProfile = this.tDateProfile = this.buildTimelineDateProfile(props.dateProfile, context.dateEnv, options, context.dateProfileGenerator);
|
|
1109
|
-
const { cellRows } = tDateProfile;
|
|
1110
|
-
const timerUnit = internal_cjs.greatestDurationDenominator(tDateProfile.slotDuration).unit;
|
|
1111
|
-
/* table settings */
|
|
1112
|
-
const verticalScrolling = !props.forPrint && !internal_cjs.getIsHeightAuto(options);
|
|
1113
|
-
const stickyHeaderDates = !props.forPrint && internal_cjs.getStickyHeaderDates(options);
|
|
1114
|
-
const stickyFooterScrollbar = !props.forPrint && internal_cjs.getStickyFooterScrollbar(options);
|
|
1115
|
-
/* table positions */
|
|
1116
|
-
const [canvasWidth, slotWidth] = this.computeSlotWidth(tDateProfile.slotCnt, tDateProfile.slotsPerLabel, options.slotMinWidth, state.slotInnerWidth, // is ACTUALLY the label width. rename?
|
|
1117
|
-
state.clientWidth);
|
|
1118
|
-
this.slotWidth = slotWidth;
|
|
1119
|
-
return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: timerUnit }, (nowDate, todayRange) => {
|
|
1120
|
-
const enableNowIndicator = // TODO: DRY
|
|
1121
|
-
options.nowIndicator &&
|
|
1122
|
-
slotWidth != null &&
|
|
1123
|
-
internal_cjs.rangeContainsMarker(props.dateProfile.currentRange, nowDate);
|
|
1124
|
-
return (preact_cjs.createElement(internal_cjs.ViewContainer, { viewSpec: context.viewSpec, className: internal_cjs.joinClassNames('fc-timeline fc-border',
|
|
1125
|
-
// HACK for Safari print-mode, where fc-scroller-no-bars won't take effect for
|
|
1126
|
-
// the below Scrollers if they have liquid flex height
|
|
1127
|
-
!props.forPrint && 'fc-flex-col') },
|
|
1128
|
-
preact_cjs.createElement(internal_cjs.Scroller, { horizontal: true, hideScrollbars: true, className: internal_cjs.joinClassNames('fc-timeline-header fc-flex-row fc-border-b', stickyHeaderDates && 'fc-table-header-sticky'), ref: this.headerScrollerRef },
|
|
1129
|
-
preact_cjs.createElement("div", {
|
|
1130
|
-
// TODO: DRY
|
|
1131
|
-
className: internal_cjs.joinClassNames('fc-rel', // origin for now-indicator
|
|
1132
|
-
canvasWidth == null && 'fc-liquid'), style: { width: canvasWidth } },
|
|
1133
|
-
cellRows.map((cells, rowLevel) => {
|
|
1134
|
-
const isLast = rowLevel === cellRows.length - 1;
|
|
1135
|
-
return (preact_cjs.createElement(TimelineHeaderRow, { key: rowLevel, dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, rowLevel: rowLevel, isLastRow: isLast, cells: cells, slotWidth: slotWidth, innerWidthRef: this.headerRowInnerWidthMap.createRef(rowLevel) }));
|
|
1136
|
-
}),
|
|
1137
|
-
enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorArrow, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth }))),
|
|
1138
|
-
Boolean(state.endScrollbarWidth) && (preact_cjs.createElement("div", { className: 'fc-border-s fc-filler', style: { minWidth: state.endScrollbarWidth } }))),
|
|
1139
|
-
preact_cjs.createElement(internal_cjs.Scroller, { vertical: verticalScrolling, horizontal: true, hideScrollbars: props.forPrint, className: internal_cjs.joinClassNames('fc-timeline-body fc-flex-col', verticalScrolling && 'fc-liquid'), ref: this.bodyScrollerRef, clientWidthRef: this.handleClientWidth, endScrollbarWidthRef: this.handleEndScrollbarWidth },
|
|
1140
|
-
preact_cjs.createElement("div", { "aria-label": options.eventsHint, className: "fc-rel fc-grow", style: { width: canvasWidth }, ref: this.handeBodyEl },
|
|
1141
|
-
preact_cjs.createElement(TimelineSlats, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange,
|
|
1142
|
-
// ref
|
|
1143
|
-
innerWidthRef: this.handleBodySlotInnerWidth,
|
|
1144
|
-
// dimensions
|
|
1145
|
-
slotWidth: slotWidth }),
|
|
1146
|
-
preact_cjs.createElement(TimelineLane, { dateProfile: props.dateProfile, tDateProfile: tDateProfile, nowDate: nowDate, todayRange: todayRange, nextDayThreshold: options.nextDayThreshold, eventStore: props.eventStore, eventUiBases: props.eventUiBases, businessHours: props.businessHours, dateSelection: props.dateSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, eventSelection: props.eventSelection, slotWidth: slotWidth }),
|
|
1147
|
-
enableNowIndicator && (preact_cjs.createElement(TimelineNowIndicatorLine, { tDateProfile: tDateProfile, nowDate: nowDate, slotWidth: slotWidth })))),
|
|
1148
|
-
stickyFooterScrollbar && (preact_cjs.createElement(internal_cjs.Scroller, { ref: this.footerScrollerRef, horizontal: true },
|
|
1149
|
-
preact_cjs.createElement("div", { style: { width: canvasWidth } })))));
|
|
1150
|
-
}));
|
|
1151
|
-
}
|
|
1152
|
-
// Lifecycle
|
|
1153
|
-
// -----------------------------------------------------------------------------------------------
|
|
1154
|
-
componentDidMount() {
|
|
1155
|
-
this.syncedScroller = new internal_cjs$1.ScrollerSyncer(true); // horizontal=true
|
|
1156
|
-
this.updateSyncedScroller();
|
|
1157
|
-
this.resetScroll();
|
|
1158
|
-
this.context.emitter.on('_timeScrollRequest', this.handleTimeScrollRequest);
|
|
1159
|
-
this.syncedScroller.addScrollEndListener(this.handleTimeScrollEnd);
|
|
1160
|
-
}
|
|
1161
|
-
componentDidUpdate(prevProps) {
|
|
1162
|
-
this.updateSyncedScroller();
|
|
1163
|
-
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
|
|
1164
|
-
this.resetScroll();
|
|
1165
|
-
}
|
|
1166
|
-
else {
|
|
1167
|
-
// TODO: inefficient to update so often
|
|
1168
|
-
this.applyTimeScroll();
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
componentWillUnmount() {
|
|
1172
|
-
this.syncedScroller.destroy();
|
|
1173
|
-
this.context.emitter.off('_timeScrollRequest', this.handleTimeScrollRequest);
|
|
1174
|
-
this.syncedScroller.removeScrollEndListener(this.handleTimeScrollEnd);
|
|
1175
|
-
}
|
|
1176
|
-
// Scrolling
|
|
1177
|
-
// -----------------------------------------------------------------------------------------------
|
|
1178
|
-
updateSyncedScroller() {
|
|
1179
|
-
this.syncedScroller.handleChildren([
|
|
1180
|
-
this.headerScrollerRef.current,
|
|
1181
|
-
this.bodyScrollerRef.current,
|
|
1182
|
-
this.footerScrollerRef.current
|
|
1183
|
-
]);
|
|
1184
|
-
}
|
|
1185
|
-
resetScroll() {
|
|
1186
|
-
this.handleTimeScrollRequest(this.context.options.scrollTime);
|
|
1187
|
-
}
|
|
1188
|
-
applyTimeScroll() {
|
|
1189
|
-
const { props, context, tDateProfile, scrollTime, slotWidth } = this;
|
|
1190
|
-
if (scrollTime != null && slotWidth != null) {
|
|
1191
|
-
let x = timeToCoord(scrollTime, context.dateEnv, props.dateProfile, tDateProfile, slotWidth);
|
|
1192
|
-
if (x) {
|
|
1193
|
-
x += 1; // overcome border. TODO: DRY this up
|
|
1194
|
-
}
|
|
1195
|
-
this.syncedScroller.scrollTo({ x });
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
queryHit(positionLeft, positionTop, elWidth, elHeight) {
|
|
1199
|
-
const { props, context, tDateProfile, slotWidth } = this;
|
|
1200
|
-
const { dateEnv } = context;
|
|
1201
|
-
if (slotWidth) {
|
|
1202
|
-
const x = context.isRtl ? elWidth - positionLeft : positionLeft;
|
|
1203
|
-
const slatIndex = Math.floor(x / slotWidth);
|
|
1204
|
-
const slatX = slatIndex * slotWidth;
|
|
1205
|
-
const partial = (x - slatX) / slotWidth; // floating point number between 0 and 1
|
|
1206
|
-
const localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot); // the snap # relative to start of slat
|
|
1207
|
-
let startDate = dateEnv.add(tDateProfile.slotDates[slatIndex], internal_cjs.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
|
|
1208
|
-
let endDate = dateEnv.add(startDate, tDateProfile.snapDuration);
|
|
1209
|
-
// TODO: generalize this coord stuff to TimeGrid?
|
|
1210
|
-
let snapWidth = slotWidth / tDateProfile.snapsPerSlot;
|
|
1211
|
-
let startCoord = slatIndex * slotWidth + (snapWidth * localSnapIndex);
|
|
1212
|
-
let endCoord = startCoord + snapWidth;
|
|
1213
|
-
let left, right;
|
|
1214
|
-
if (context.isRtl) {
|
|
1215
|
-
left = elWidth - endCoord;
|
|
1216
|
-
right = elWidth - startCoord;
|
|
1217
|
-
}
|
|
1218
|
-
else {
|
|
1219
|
-
left = startCoord;
|
|
1220
|
-
right = endCoord;
|
|
1221
|
-
}
|
|
1222
|
-
return {
|
|
1223
|
-
dateProfile: props.dateProfile,
|
|
1224
|
-
dateSpan: {
|
|
1225
|
-
range: { start: startDate, end: endDate },
|
|
1226
|
-
allDay: !tDateProfile.isTimeScale,
|
|
1227
|
-
},
|
|
1228
|
-
rect: {
|
|
1229
|
-
left,
|
|
1230
|
-
right,
|
|
1231
|
-
top: 0,
|
|
1232
|
-
bottom: elHeight,
|
|
1233
|
-
},
|
|
1234
|
-
// HACK. TODO: This is expensive to do every hit-query
|
|
1235
|
-
dayEl: this.bodyEl.querySelectorAll('.fc-timeline-slot')[slatIndex],
|
|
1236
|
-
layer: 0,
|
|
1237
|
-
};
|
|
1238
|
-
}
|
|
1239
|
-
return null;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
var css_248z = ".fc-timeline-slots{z-index:1}.fc-timeline-events{position:relative;z-index:2}.fc-timeline-slot-minor{border-style:dotted}.fc-timeline-events-overlap-enabled{padding-bottom:10px}.fc-timeline-event{border-radius:0;font-size:var(--fc-small-font-size);margin-bottom:1px}.fc-direction-ltr .fc-timeline-event.fc-event-end{margin-right:1px}.fc-direction-rtl .fc-timeline-event.fc-event-end{margin-left:1px}.fc-timeline-event .fc-event-inner{align-items:center;display:flex;flex-direction:row;padding:2px 1px}.fc-timeline-event-spacious .fc-event-inner{padding-bottom:5px;padding-top:5px}.fc-timeline-event .fc-event-time{font-weight:700}.fc-timeline-event .fc-event-time,.fc-timeline-event .fc-event-title{padding:0 2px}.fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-color:transparent #000;border-style:solid;border-width:5px;content:\"\";flex-grow:0;flex-shrink:0;height:0;margin:0 1px;opacity:.5;width:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before,.fc-direction-rtl .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after{border-left:0}.fc-direction-ltr .fc-timeline-event:not(.fc-event-end) .fc-event-inner:after,.fc-direction-rtl .fc-timeline-event:not(.fc-event-start) .fc-event-inner:before{border-right:0}.fc-timeline-more-link{align-items:flex-start;background:var(--fc-more-link-bg-color);color:var(--fc-more-link-text-color);cursor:pointer;display:flex;flex-direction:column;font-size:var(--fc-small-font-size);padding:1px}.fc-direction-ltr .fc-timeline-more-link{margin-right:1px}.fc-direction-rtl .fc-timeline-more-link{margin-left:1px}.fc-timeline-more-link-inner{padding:2px}.fc-timeline-now-indicator-container{bottom:0;left:0;overflow:hidden;pointer-events:none;position:absolute;right:0;top:0;z-index:4}.fc-timeline-now-indicator-arrow{border-bottom-style:solid;border-bottom-width:0;border-color:var(--fc-now-indicator-color);border-left:5px solid transparent;border-right:5px solid transparent;border-top-style:solid;border-top-width:6px;height:0;margin:0 -5px;position:absolute;top:0;width:0}.fc-timeline-now-indicator-line{border-left:1px solid var(--fc-now-indicator-color);bottom:0;position:absolute;top:0}";
|
|
1244
|
-
internal_cjs.injectStyles(css_248z);
|
|
1245
|
-
|
|
1246
|
-
exports.TimelineHeaderRow = TimelineHeaderRow;
|
|
1247
|
-
exports.TimelineLane = TimelineLane;
|
|
1248
|
-
exports.TimelineLaneBg = TimelineLaneBg;
|
|
1249
|
-
exports.TimelineLaneSlicer = TimelineLaneSlicer;
|
|
1250
|
-
exports.TimelineNowIndicatorArrow = TimelineNowIndicatorArrow;
|
|
1251
|
-
exports.TimelineNowIndicatorLine = TimelineNowIndicatorLine;
|
|
1252
|
-
exports.TimelineSlats = TimelineSlats;
|
|
1253
|
-
exports.TimelineView = TimelineView;
|
|
1254
|
-
exports.buildTimelineDateProfile = buildTimelineDateProfile;
|
|
1255
|
-
exports.computeSlotWidth = computeSlotWidth;
|
|
1256
|
-
exports.createHorizontalStyle = createHorizontalStyle;
|
|
1257
|
-
exports.createVerticalStyle = createVerticalStyle;
|
|
1258
|
-
exports.timeToCoord = timeToCoord;
|