@fullcalendar/scrollgrid 6.1.14 → 7.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/internal.cjs CHANGED
@@ -3,238 +3,6 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var internal_cjs = require('@fullcalendar/core/internal.cjs');
6
- var preact_cjs = require('@fullcalendar/core/preact.cjs');
7
-
8
- // TODO: assume the el has no borders?
9
- function getScrollCanvasOrigin(scrollEl) {
10
- let rect = scrollEl.getBoundingClientRect();
11
- let edges = internal_cjs.computeEdges(scrollEl); // TODO: pass in isRtl?
12
- return {
13
- left: rect.left + edges.borderLeft + edges.scrollbarLeft - getScrollFromLeftEdge(scrollEl),
14
- top: rect.top + edges.borderTop - scrollEl.scrollTop,
15
- };
16
- }
17
- function getScrollFromLeftEdge(el) {
18
- let scrollLeft = el.scrollLeft;
19
- let computedStyles = window.getComputedStyle(el); // TODO: pass in isRtl instead?
20
- if (computedStyles.direction === 'rtl') {
21
- switch (getRtlScrollSystem()) {
22
- case 'negative':
23
- scrollLeft *= -1; // convert to 'reverse'. fall through...
24
- case 'reverse': // scrollLeft is distance between scrollframe's right edge scrollcanvas's right edge
25
- scrollLeft = el.scrollWidth - scrollLeft - el.clientWidth;
26
- }
27
- }
28
- return scrollLeft;
29
- }
30
- function setScrollFromLeftEdge(el, scrollLeft) {
31
- let computedStyles = window.getComputedStyle(el); // TODO: pass in isRtl instead?
32
- if (computedStyles.direction === 'rtl') {
33
- switch (getRtlScrollSystem()) {
34
- case 'reverse':
35
- scrollLeft = el.scrollWidth - scrollLeft;
36
- break;
37
- case 'negative':
38
- scrollLeft = -(el.scrollWidth - scrollLeft);
39
- break;
40
- }
41
- }
42
- el.scrollLeft = scrollLeft;
43
- }
44
- // Horizontal Scroll System Detection
45
- // ----------------------------------------------------------------------------------------------
46
- let _rtlScrollSystem;
47
- function getRtlScrollSystem() {
48
- return _rtlScrollSystem || (_rtlScrollSystem = detectRtlScrollSystem());
49
- }
50
- function detectRtlScrollSystem() {
51
- let el = document.createElement('div');
52
- el.style.position = 'absolute';
53
- el.style.top = '-1000px';
54
- el.style.width = '100px'; // must be at least the side of scrollbars or you get inaccurate values (#7335)
55
- el.style.height = '100px'; // "
56
- el.style.overflow = 'scroll';
57
- el.style.direction = 'rtl';
58
- let innerEl = document.createElement('div');
59
- innerEl.style.width = '200px';
60
- innerEl.style.height = '200px';
61
- el.appendChild(innerEl);
62
- document.body.appendChild(el);
63
- let system;
64
- if (el.scrollLeft > 0) {
65
- system = 'positive'; // scroll is a positive number from the left edge
66
- }
67
- else {
68
- el.scrollLeft = 1;
69
- if (el.scrollLeft > 0) {
70
- system = 'reverse'; // scroll is a positive number from the right edge
71
- }
72
- else {
73
- system = 'negative'; // scroll is a negative number from the right edge
74
- }
75
- }
76
- internal_cjs.removeElement(el);
77
- return system;
78
- }
79
-
80
- const STICKY_SELECTOR = '.fc-sticky';
81
- /*
82
- Goes beyond mere position:sticky, allows horizontal centering
83
-
84
- REQUIREMENT: fc-sticky elements, if the fc-sticky className is taken away, should NOT have relative or absolute positioning.
85
- This is because we attach the coords with JS, and the VDOM might take away the fc-sticky class but doesn't know kill the positioning.
86
-
87
- TODO: don't query text-align:center. isn't compatible with flexbox centering. instead, check natural X coord within parent container
88
- */
89
- class StickyScrolling {
90
- constructor(scrollEl, isRtl) {
91
- this.scrollEl = scrollEl;
92
- this.isRtl = isRtl;
93
- this.updateSize = () => {
94
- let { scrollEl } = this;
95
- let els = internal_cjs.findElements(scrollEl, STICKY_SELECTOR);
96
- let elGeoms = this.queryElGeoms(els);
97
- let viewportWidth = scrollEl.clientWidth;
98
- assignStickyPositions(els, elGeoms, viewportWidth);
99
- };
100
- }
101
- queryElGeoms(els) {
102
- let { scrollEl, isRtl } = this;
103
- let canvasOrigin = getScrollCanvasOrigin(scrollEl);
104
- let elGeoms = [];
105
- for (let el of els) {
106
- let parentBound = internal_cjs.translateRect(internal_cjs.computeInnerRect(el.parentNode, true, true), // weird way to call this!!!
107
- -canvasOrigin.left, -canvasOrigin.top);
108
- let elRect = el.getBoundingClientRect();
109
- let computedStyles = window.getComputedStyle(el);
110
- let textAlign = window.getComputedStyle(el.parentNode).textAlign; // ask the parent
111
- let naturalBound = null;
112
- if (textAlign === 'start') {
113
- textAlign = isRtl ? 'right' : 'left';
114
- }
115
- else if (textAlign === 'end') {
116
- textAlign = isRtl ? 'left' : 'right';
117
- }
118
- if (computedStyles.position !== 'sticky') {
119
- naturalBound = internal_cjs.translateRect(elRect, -canvasOrigin.left - (parseFloat(computedStyles.left) || 0), // could be 'auto'
120
- -canvasOrigin.top - (parseFloat(computedStyles.top) || 0));
121
- }
122
- elGeoms.push({
123
- parentBound,
124
- naturalBound,
125
- elWidth: elRect.width,
126
- elHeight: elRect.height,
127
- textAlign,
128
- });
129
- }
130
- return elGeoms;
131
- }
132
- }
133
- function assignStickyPositions(els, elGeoms, viewportWidth) {
134
- els.forEach((el, i) => {
135
- let { textAlign, elWidth, parentBound } = elGeoms[i];
136
- let parentWidth = parentBound.right - parentBound.left;
137
- let left;
138
- if (textAlign === 'center' &&
139
- parentWidth > viewportWidth) {
140
- left = (viewportWidth - elWidth) / 2;
141
- }
142
- else { // if parent container can be completely in view, we don't need stickiness
143
- left = '';
144
- }
145
- internal_cjs.applyStyle(el, {
146
- left,
147
- right: left,
148
- top: 0,
149
- });
150
- });
151
- }
152
-
153
- class ClippedScroller extends internal_cjs.BaseComponent {
154
- constructor() {
155
- super(...arguments);
156
- this.elRef = preact_cjs.createRef();
157
- this.state = {
158
- xScrollbarWidth: 0,
159
- yScrollbarWidth: 0,
160
- };
161
- this.handleScroller = (scroller) => {
162
- this.scroller = scroller;
163
- internal_cjs.setRef(this.props.scrollerRef, scroller);
164
- };
165
- this.handleSizing = () => {
166
- let { props } = this;
167
- if (props.overflowY === 'scroll-hidden') {
168
- this.setState({ yScrollbarWidth: this.scroller.getYScrollbarWidth() });
169
- }
170
- if (props.overflowX === 'scroll-hidden') {
171
- this.setState({ xScrollbarWidth: this.scroller.getXScrollbarWidth() });
172
- }
173
- };
174
- }
175
- render() {
176
- let { props, state, context } = this;
177
- let isScrollbarOnLeft = context.isRtl && internal_cjs.getIsRtlScrollbarOnLeft();
178
- let overcomeLeft = 0;
179
- let overcomeRight = 0;
180
- let overcomeBottom = 0;
181
- let { overflowX, overflowY } = props;
182
- if (props.forPrint) {
183
- overflowX = 'visible';
184
- overflowY = 'visible';
185
- }
186
- if (overflowX === 'scroll-hidden') {
187
- overcomeBottom = state.xScrollbarWidth;
188
- }
189
- if (overflowY === 'scroll-hidden') {
190
- if (state.yScrollbarWidth != null) {
191
- if (isScrollbarOnLeft) {
192
- overcomeLeft = state.yScrollbarWidth;
193
- }
194
- else {
195
- overcomeRight = state.yScrollbarWidth;
196
- }
197
- }
198
- }
199
- return (preact_cjs.createElement("div", { ref: this.elRef, className: 'fc-scroller-harness' + (props.liquid ? ' fc-scroller-harness-liquid' : '') },
200
- preact_cjs.createElement(internal_cjs.Scroller, { ref: this.handleScroller, elRef: this.props.scrollerElRef, overflowX: overflowX === 'scroll-hidden' ? 'scroll' : overflowX, overflowY: overflowY === 'scroll-hidden' ? 'scroll' : overflowY, overcomeLeft: overcomeLeft, overcomeRight: overcomeRight, overcomeBottom: overcomeBottom, maxHeight: typeof props.maxHeight === 'number'
201
- ? (props.maxHeight + (overflowX === 'scroll-hidden' ? state.xScrollbarWidth : 0))
202
- : '', liquid: props.liquid, liquidIsAbsolute: true }, props.children)));
203
- }
204
- componentDidMount() {
205
- this.handleSizing();
206
- this.context.addResizeHandler(this.handleSizing);
207
- }
208
- getSnapshotBeforeUpdate(prevProps) {
209
- if (this.props.forPrint && !prevProps.forPrint) {
210
- return { simulateScrollLeft: this.scroller.el.scrollLeft };
211
- }
212
- return {};
213
- }
214
- componentDidUpdate(prevProps, prevState, snapshot) {
215
- const { props, scroller: { el: scrollerEl } } = this;
216
- if (!internal_cjs.isPropsEqual(prevProps, props)) { // an external change?
217
- this.handleSizing();
218
- }
219
- if (snapshot.simulateScrollLeft !== undefined) {
220
- scrollerEl.style.left = -snapshot.simulateScrollLeft + 'px';
221
- }
222
- else if (!props.forPrint && prevProps.forPrint) {
223
- const restoredScrollLeft = -parseInt(scrollerEl.style.left);
224
- scrollerEl.style.left = '';
225
- scrollerEl.scrollLeft = restoredScrollLeft;
226
- }
227
- }
228
- componentWillUnmount() {
229
- this.context.removeResizeHandler(this.handleSizing);
230
- }
231
- needsXScrolling() {
232
- return this.scroller.needsXScrolling();
233
- }
234
- needsYScrolling() {
235
- return this.scroller.needsYScrolling();
236
- }
237
- }
238
6
 
239
7
  const WHEEL_EVENT_NAMES = 'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ');
240
8
  /*
@@ -279,7 +47,7 @@ class ScrollListener {
279
47
  el.addEventListener('touchstart', this.handleTouchStart, { passive: true });
280
48
  el.addEventListener('touchend', this.handleTouchEnd);
281
49
  for (let eventName of WHEEL_EVENT_NAMES) {
282
- el.addEventListener(eventName, this.handleWheel);
50
+ el.addEventListener(eventName, this.handleWheel, { passive: true });
283
51
  }
284
52
  }
285
53
  destroy() {
@@ -288,7 +56,7 @@ class ScrollListener {
288
56
  el.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
289
57
  el.removeEventListener('touchend', this.handleTouchEnd);
290
58
  for (let eventName of WHEEL_EVENT_NAMES) {
291
- el.removeEventListener(eventName, this.handleWheel);
59
+ el.removeEventListener(eventName, this.handleWheel, { passive: true });
292
60
  }
293
61
  }
294
62
  // Start / Stop
@@ -322,20 +90,59 @@ class ScrollListener {
322
90
  }
323
91
  }
324
92
 
325
- class ScrollSyncer {
326
- constructor(isVertical, scrollEls) {
327
- this.isVertical = isVertical;
328
- this.scrollEls = scrollEls;
93
+ class ScrollerSyncer {
94
+ constructor(isHorizontal = false) {
95
+ this.isHorizontal = isHorizontal;
96
+ this.scrollers = []; // TODO: move away from requiring Scroller
97
+ this.scrollListeners = [];
329
98
  this.isPaused = false;
330
- this.scrollListeners = scrollEls.map((el) => this.bindScroller(el));
99
+ }
100
+ handleChildren(scrollers, isRtl) {
101
+ if (!internal_cjs.isArraysEqual(this.scrollers, scrollers)) {
102
+ this.destroy();
103
+ this.scrollers = scrollers;
104
+ const scrollListeners = [];
105
+ for (const scroller of scrollers) {
106
+ if (scroller) { // could be null
107
+ scrollListeners.push(this.bindScroller(scroller.el));
108
+ }
109
+ }
110
+ this.scrollListeners = scrollListeners;
111
+ }
112
+ this.isRtl = isRtl;
331
113
  }
332
114
  destroy() {
333
115
  for (let scrollListener of this.scrollListeners) {
334
116
  scrollListener.destroy();
335
117
  }
336
118
  }
119
+ get x() {
120
+ const { scrollListeners, masterEl, isRtl } = this;
121
+ const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
122
+ return internal_cjs.getNormalizedScrollX(el, isRtl);
123
+ }
124
+ get y() {
125
+ const { scrollListeners, masterEl } = this;
126
+ const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
127
+ return el.scrollTop;
128
+ }
129
+ scrollTo({ x, y }) {
130
+ this.isPaused = true;
131
+ const { scrollListeners, isRtl } = this;
132
+ if (y != null) {
133
+ for (let scrollListener of scrollListeners) {
134
+ scrollListener.el.scrollTop = y;
135
+ }
136
+ }
137
+ if (x != null) {
138
+ for (let scrollListener of scrollListeners) {
139
+ internal_cjs.setNormalizedScrollX(scrollListener.el, isRtl, x);
140
+ }
141
+ }
142
+ this.isPaused = false;
143
+ }
337
144
  bindScroller(el) {
338
- let { scrollEls, isVertical } = this;
145
+ let { isHorizontal } = this;
339
146
  let scrollListener = new ScrollListener(el);
340
147
  const onScroll = (isWheel, isTouch) => {
341
148
  if (!this.isPaused) {
@@ -343,13 +150,14 @@ class ScrollSyncer {
343
150
  this.assignMaster(el);
344
151
  }
345
152
  if (this.masterEl === el) { // dealing with current
346
- for (let otherEl of scrollEls) {
153
+ for (let scrollListener of this.scrollListeners) {
154
+ const otherEl = scrollListener.el;
347
155
  if (otherEl !== el) {
348
- if (isVertical) {
349
- otherEl.scrollTop = el.scrollTop;
156
+ if (isHorizontal) {
157
+ otherEl.scrollLeft = el.scrollLeft;
350
158
  }
351
159
  else {
352
- otherEl.scrollLeft = el.scrollLeft;
160
+ otherEl.scrollTop = el.scrollTop;
353
161
  }
354
162
  }
355
163
  }
@@ -373,484 +181,6 @@ class ScrollSyncer {
373
181
  }
374
182
  }
375
183
  }
376
- /*
377
- will normalize the scrollLeft value
378
- */
379
- forceScrollLeft(scrollLeft) {
380
- this.isPaused = true;
381
- for (let listener of this.scrollListeners) {
382
- setScrollFromLeftEdge(listener.el, scrollLeft);
383
- }
384
- this.isPaused = false;
385
- }
386
- forceScrollTop(top) {
387
- this.isPaused = true;
388
- for (let listener of this.scrollListeners) {
389
- listener.el.scrollTop = top;
390
- }
391
- this.isPaused = false;
392
- }
393
- }
394
-
395
- internal_cjs.config.SCROLLGRID_RESIZE_INTERVAL = 500;
396
- /*
397
- TODO: make <ScrollGridSection> subcomponent
398
- NOTE: doesn't support collapsibleWidth (which is sortof a hack anyway)
399
- */
400
- class ScrollGrid extends internal_cjs.BaseComponent {
401
- constructor() {
402
- super(...arguments);
403
- this.compileColGroupStats = internal_cjs.memoizeArraylike(compileColGroupStat, isColGroupStatsEqual);
404
- this.renderMicroColGroups = internal_cjs.memoizeArraylike(internal_cjs.renderMicroColGroup); // yucky to memoize VNodes, but much more efficient for consumers
405
- this.clippedScrollerRefs = new internal_cjs.RefMap();
406
- // doesn't hold non-scrolling els used just for padding
407
- this.scrollerElRefs = new internal_cjs.RefMap(this._handleScrollerEl.bind(this));
408
- this.chunkElRefs = new internal_cjs.RefMap(this._handleChunkEl.bind(this));
409
- this.scrollSyncersBySection = {};
410
- this.scrollSyncersByColumn = {};
411
- // for row-height-syncing
412
- this.rowUnstableMap = new Map(); // no need to groom. always self-cancels
413
- this.rowInnerMaxHeightMap = new Map();
414
- this.anyRowHeightsChanged = false;
415
- this.recentSizingCnt = 0;
416
- this.state = {
417
- shrinkWidths: [],
418
- forceYScrollbars: false,
419
- forceXScrollbars: false,
420
- scrollerClientWidths: {},
421
- scrollerClientHeights: {},
422
- sectionRowMaxHeights: [],
423
- };
424
- this.handleSizing = (isForcedResize, sectionRowMaxHeightsChanged) => {
425
- if (!this.allowSizing()) {
426
- return;
427
- }
428
- if (!sectionRowMaxHeightsChanged) { // something else changed, probably external
429
- this.anyRowHeightsChanged = true;
430
- }
431
- let otherState = {};
432
- // if reacting to self-change of sectionRowMaxHeightsChanged, or not stable, don't do anything
433
- if (isForcedResize || (!sectionRowMaxHeightsChanged && !this.rowUnstableMap.size)) {
434
- otherState.sectionRowMaxHeights = this.computeSectionRowMaxHeights();
435
- }
436
- this.setState(Object.assign(Object.assign({ shrinkWidths: this.computeShrinkWidths() }, this.computeScrollerDims()), otherState), () => {
437
- if (!this.rowUnstableMap.size) {
438
- this.updateStickyScrolling(); // needs to happen AFTER final positioning committed to DOM
439
- }
440
- });
441
- };
442
- this.handleRowHeightChange = (rowEl, isStable) => {
443
- let { rowUnstableMap, rowInnerMaxHeightMap } = this;
444
- if (!isStable) {
445
- rowUnstableMap.set(rowEl, true);
446
- }
447
- else {
448
- rowUnstableMap.delete(rowEl);
449
- let innerMaxHeight = getRowInnerMaxHeight(rowEl);
450
- if (!rowInnerMaxHeightMap.has(rowEl) || rowInnerMaxHeightMap.get(rowEl) !== innerMaxHeight) {
451
- rowInnerMaxHeightMap.set(rowEl, innerMaxHeight);
452
- this.anyRowHeightsChanged = true;
453
- }
454
- if (!rowUnstableMap.size && this.anyRowHeightsChanged) {
455
- this.anyRowHeightsChanged = false;
456
- this.setState({
457
- sectionRowMaxHeights: this.computeSectionRowMaxHeights(),
458
- });
459
- }
460
- }
461
- };
462
- }
463
- render() {
464
- let { props, state, context } = this;
465
- let { shrinkWidths } = state;
466
- let colGroupStats = this.compileColGroupStats(props.colGroups.map((colGroup) => [colGroup]));
467
- let microColGroupNodes = this.renderMicroColGroups(colGroupStats.map((stat, i) => [stat.cols, shrinkWidths[i]]));
468
- let classNames = internal_cjs.getScrollGridClassNames(props.liquid, context);
469
- this.getDims();
470
- // TODO: make DRY
471
- let sectionConfigs = props.sections;
472
- let configCnt = sectionConfigs.length;
473
- let configI = 0;
474
- let currentConfig;
475
- let headSectionNodes = [];
476
- let bodySectionNodes = [];
477
- let footSectionNodes = [];
478
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'header') {
479
- headSectionNodes.push(this.renderSection(currentConfig, configI, colGroupStats, microColGroupNodes, state.sectionRowMaxHeights, true));
480
- configI += 1;
481
- }
482
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'body') {
483
- bodySectionNodes.push(this.renderSection(currentConfig, configI, colGroupStats, microColGroupNodes, state.sectionRowMaxHeights, false));
484
- configI += 1;
485
- }
486
- while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'footer') {
487
- footSectionNodes.push(this.renderSection(currentConfig, configI, colGroupStats, microColGroupNodes, state.sectionRowMaxHeights, true));
488
- configI += 1;
489
- }
490
- const isBuggy = !internal_cjs.getCanVGrowWithinCell(); // see NOTE in SimpleScrollGrid
491
- const roleAttrs = { role: 'rowgroup' };
492
- return preact_cjs.createElement('table', {
493
- ref: props.elRef,
494
- role: 'grid',
495
- className: classNames.join(' '),
496
- }, renderMacroColGroup(colGroupStats, shrinkWidths), Boolean(!isBuggy && headSectionNodes.length) && preact_cjs.createElement('thead', roleAttrs, ...headSectionNodes), Boolean(!isBuggy && bodySectionNodes.length) && preact_cjs.createElement('tbody', roleAttrs, ...bodySectionNodes), Boolean(!isBuggy && footSectionNodes.length) && preact_cjs.createElement('tfoot', roleAttrs, ...footSectionNodes), isBuggy && preact_cjs.createElement('tbody', roleAttrs, ...headSectionNodes, ...bodySectionNodes, ...footSectionNodes));
497
- }
498
- renderSection(sectionConfig, sectionIndex, colGroupStats, microColGroupNodes, sectionRowMaxHeights, isHeader) {
499
- if ('outerContent' in sectionConfig) {
500
- return (preact_cjs.createElement(preact_cjs.Fragment, { key: sectionConfig.key }, sectionConfig.outerContent));
501
- }
502
- return (preact_cjs.createElement("tr", { key: sectionConfig.key, role: "presentation", className: internal_cjs.getSectionClassNames(sectionConfig, this.props.liquid).join(' ') }, sectionConfig.chunks.map((chunkConfig, i) => this.renderChunk(sectionConfig, sectionIndex, colGroupStats[i], microColGroupNodes[i], chunkConfig, i, (sectionRowMaxHeights[sectionIndex] || [])[i] || [], isHeader))));
503
- }
504
- renderChunk(sectionConfig, sectionIndex, colGroupStat, microColGroupNode, chunkConfig, chunkIndex, rowHeights, isHeader) {
505
- if ('outerContent' in chunkConfig) {
506
- return (preact_cjs.createElement(preact_cjs.Fragment, { key: chunkConfig.key }, chunkConfig.outerContent));
507
- }
508
- let { state } = this;
509
- let { scrollerClientWidths, scrollerClientHeights } = state;
510
- let [sectionCnt, chunksPerSection] = this.getDims();
511
- let index = sectionIndex * chunksPerSection + chunkIndex;
512
- let sideScrollIndex = (!this.context.isRtl || internal_cjs.getIsRtlScrollbarOnLeft()) ? chunksPerSection - 1 : 0;
513
- let isVScrollSide = chunkIndex === sideScrollIndex;
514
- let isLastSection = sectionIndex === sectionCnt - 1;
515
- let forceXScrollbars = isLastSection && state.forceXScrollbars; // NOOOO can result in `null`
516
- let forceYScrollbars = isVScrollSide && state.forceYScrollbars; // NOOOO can result in `null`
517
- let allowXScrolling = colGroupStat && colGroupStat.allowXScrolling; // rename?
518
- let allowYScrolling = internal_cjs.getAllowYScrolling(this.props, sectionConfig); // rename? do in section func?
519
- let chunkVGrow = internal_cjs.getSectionHasLiquidHeight(this.props, sectionConfig); // do in section func?
520
- let expandRows = sectionConfig.expandRows && chunkVGrow;
521
- let tableMinWidth = (colGroupStat && colGroupStat.totalColMinWidth) || '';
522
- let content = internal_cjs.renderChunkContent(sectionConfig, chunkConfig, {
523
- tableColGroupNode: microColGroupNode,
524
- tableMinWidth,
525
- clientWidth: scrollerClientWidths[index] !== undefined ? scrollerClientWidths[index] : null,
526
- clientHeight: scrollerClientHeights[index] !== undefined ? scrollerClientHeights[index] : null,
527
- expandRows,
528
- syncRowHeights: Boolean(sectionConfig.syncRowHeights),
529
- rowSyncHeights: rowHeights,
530
- reportRowHeightChange: this.handleRowHeightChange,
531
- }, isHeader);
532
- let overflowX = forceXScrollbars ? (isLastSection ? 'scroll' : 'scroll-hidden') :
533
- !allowXScrolling ? 'hidden' :
534
- (isLastSection ? 'auto' : 'scroll-hidden');
535
- let overflowY = forceYScrollbars ? (isVScrollSide ? 'scroll' : 'scroll-hidden') :
536
- !allowYScrolling ? 'hidden' :
537
- (isVScrollSide ? 'auto' : 'scroll-hidden');
538
- // it *could* be possible to reduce DOM wrappers by only doing a ClippedScroller when allowXScrolling or allowYScrolling,
539
- // but if these values were to change, the inner components would be unmounted/remounted because of the parent change.
540
- content = (preact_cjs.createElement(ClippedScroller, { ref: this.clippedScrollerRefs.createRef(index), scrollerElRef: this.scrollerElRefs.createRef(index), overflowX: overflowX, overflowY: overflowY, forPrint: this.props.forPrint, liquid: chunkVGrow, maxHeight: sectionConfig.maxHeight }, content));
541
- return preact_cjs.createElement(isHeader ? 'th' : 'td', {
542
- key: chunkConfig.key,
543
- ref: this.chunkElRefs.createRef(index),
544
- role: 'presentation',
545
- }, content);
546
- }
547
- componentDidMount() {
548
- this.getStickyScrolling = internal_cjs.memoizeArraylike(initStickyScrolling);
549
- this.getScrollSyncersBySection = internal_cjs.memoizeHashlike(initScrollSyncer.bind(this, true), null, destroyScrollSyncer);
550
- this.getScrollSyncersByColumn = internal_cjs.memoizeHashlike(initScrollSyncer.bind(this, false), null, destroyScrollSyncer);
551
- this.updateScrollSyncers();
552
- this.handleSizing(false);
553
- this.context.addResizeHandler(this.handleSizing);
554
- }
555
- componentDidUpdate(prevProps, prevState) {
556
- this.updateScrollSyncers();
557
- // TODO: need better solution when state contains non-sizing things
558
- this.handleSizing(false, prevState.sectionRowMaxHeights !== this.state.sectionRowMaxHeights);
559
- }
560
- componentWillUnmount() {
561
- this.context.removeResizeHandler(this.handleSizing);
562
- this.destroyScrollSyncers();
563
- }
564
- allowSizing() {
565
- let now = new Date();
566
- if (!this.lastSizingDate ||
567
- now.valueOf() > this.lastSizingDate.valueOf() + internal_cjs.config.SCROLLGRID_RESIZE_INTERVAL) {
568
- this.lastSizingDate = now;
569
- this.recentSizingCnt = 0;
570
- return true;
571
- }
572
- return (this.recentSizingCnt += 1) <= 10;
573
- }
574
- computeShrinkWidths() {
575
- let colGroupStats = this.compileColGroupStats(this.props.colGroups.map((colGroup) => [colGroup]));
576
- let [sectionCnt, chunksPerSection] = this.getDims();
577
- let cnt = sectionCnt * chunksPerSection;
578
- let shrinkWidths = [];
579
- colGroupStats.forEach((colGroupStat, i) => {
580
- if (colGroupStat.hasShrinkCol) {
581
- let chunkEls = this.chunkElRefs.collect(i, cnt, chunksPerSection); // in one col
582
- shrinkWidths[i] = internal_cjs.computeShrinkWidth(chunkEls);
583
- }
584
- });
585
- return shrinkWidths;
586
- }
587
- // has the side effect of grooming rowInnerMaxHeightMap
588
- // TODO: somehow short-circuit if there are no new height changes
589
- computeSectionRowMaxHeights() {
590
- let newHeightMap = new Map();
591
- let [sectionCnt, chunksPerSection] = this.getDims();
592
- let sectionRowMaxHeights = [];
593
- for (let sectionI = 0; sectionI < sectionCnt; sectionI += 1) {
594
- let sectionConfig = this.props.sections[sectionI];
595
- let assignableHeights = []; // chunk, row
596
- if (sectionConfig && sectionConfig.syncRowHeights) {
597
- let rowHeightsByChunk = [];
598
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) {
599
- let index = sectionI * chunksPerSection + chunkI;
600
- let rowHeights = [];
601
- let chunkEl = this.chunkElRefs.currentMap[index];
602
- if (chunkEl) {
603
- rowHeights = internal_cjs.findElements(chunkEl, '.fc-scrollgrid-sync-table tr').map((rowEl) => {
604
- let max = getRowInnerMaxHeight(rowEl);
605
- newHeightMap.set(rowEl, max);
606
- return max;
607
- });
608
- }
609
- else {
610
- rowHeights = [];
611
- }
612
- rowHeightsByChunk.push(rowHeights);
613
- }
614
- let rowCnt = rowHeightsByChunk[0].length;
615
- let isEqualRowCnt = true;
616
- for (let chunkI = 1; chunkI < chunksPerSection; chunkI += 1) {
617
- let isOuterContent = sectionConfig.chunks[chunkI] && sectionConfig.chunks[chunkI].outerContent !== undefined; // can be null
618
- if (!isOuterContent && rowHeightsByChunk[chunkI].length !== rowCnt) { // skip outer content
619
- isEqualRowCnt = false;
620
- break;
621
- }
622
- }
623
- if (!isEqualRowCnt) {
624
- let chunkHeightSums = [];
625
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) {
626
- chunkHeightSums.push(sumNumbers(rowHeightsByChunk[chunkI]) + rowHeightsByChunk[chunkI].length);
627
- }
628
- let maxTotalSum = Math.max(...chunkHeightSums);
629
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) {
630
- let rowInChunkCnt = rowHeightsByChunk[chunkI].length;
631
- let rowInChunkTotalHeight = maxTotalSum - rowInChunkCnt; // subtract border
632
- // height of non-first row. we do this to avoid rounding, because it's unreliable within a table
633
- let rowInChunkHeightOthers = Math.floor(rowInChunkTotalHeight / rowInChunkCnt);
634
- // whatever is leftover goes to the first row
635
- let rowInChunkHeightFirst = rowInChunkTotalHeight - rowInChunkHeightOthers * (rowInChunkCnt - 1);
636
- let rowInChunkHeights = [];
637
- let row = 0;
638
- if (row < rowInChunkCnt) {
639
- rowInChunkHeights.push(rowInChunkHeightFirst);
640
- row += 1;
641
- }
642
- while (row < rowInChunkCnt) {
643
- rowInChunkHeights.push(rowInChunkHeightOthers);
644
- row += 1;
645
- }
646
- assignableHeights.push(rowInChunkHeights);
647
- }
648
- }
649
- else {
650
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) {
651
- assignableHeights.push([]);
652
- }
653
- for (let row = 0; row < rowCnt; row += 1) {
654
- let rowHeightsAcrossChunks = [];
655
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) {
656
- let h = rowHeightsByChunk[chunkI][row];
657
- if (h != null) { // protect against outerContent
658
- rowHeightsAcrossChunks.push(h);
659
- }
660
- }
661
- let maxHeight = Math.max(...rowHeightsAcrossChunks);
662
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) {
663
- assignableHeights[chunkI].push(maxHeight);
664
- }
665
- }
666
- }
667
- }
668
- sectionRowMaxHeights.push(assignableHeights);
669
- }
670
- this.rowInnerMaxHeightMap = newHeightMap;
671
- return sectionRowMaxHeights;
672
- }
673
- computeScrollerDims() {
674
- let scrollbarWidth = internal_cjs.getScrollbarWidths();
675
- let [sectionCnt, chunksPerSection] = this.getDims();
676
- let sideScrollI = (!this.context.isRtl || internal_cjs.getIsRtlScrollbarOnLeft()) ? chunksPerSection - 1 : 0;
677
- let lastSectionI = sectionCnt - 1;
678
- let currentScrollers = this.clippedScrollerRefs.currentMap;
679
- let scrollerEls = this.scrollerElRefs.currentMap;
680
- let forceYScrollbars = false;
681
- let forceXScrollbars = false;
682
- let scrollerClientWidths = {};
683
- let scrollerClientHeights = {};
684
- for (let sectionI = 0; sectionI < sectionCnt; sectionI += 1) { // along edge
685
- let index = sectionI * chunksPerSection + sideScrollI;
686
- let scroller = currentScrollers[index];
687
- if (scroller && scroller.needsYScrolling()) {
688
- forceYScrollbars = true;
689
- break;
690
- }
691
- }
692
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) { // along last row
693
- let index = lastSectionI * chunksPerSection + chunkI;
694
- let scroller = currentScrollers[index];
695
- if (scroller && scroller.needsXScrolling()) {
696
- forceXScrollbars = true;
697
- break;
698
- }
699
- }
700
- for (let sectionI = 0; sectionI < sectionCnt; sectionI += 1) {
701
- for (let chunkI = 0; chunkI < chunksPerSection; chunkI += 1) {
702
- let index = sectionI * chunksPerSection + chunkI;
703
- let scrollerEl = scrollerEls[index];
704
- if (scrollerEl) {
705
- // TODO: weird way to get this. need harness b/c doesn't include table borders
706
- let harnessEl = scrollerEl.parentNode;
707
- scrollerClientWidths[index] = Math.floor(harnessEl.getBoundingClientRect().width - ((chunkI === sideScrollI && forceYScrollbars)
708
- ? scrollbarWidth.y // use global because scroller might not have scrollbars yet but will need them in future
709
- : 0));
710
- scrollerClientHeights[index] = Math.floor(harnessEl.getBoundingClientRect().height - ((sectionI === lastSectionI && forceXScrollbars)
711
- ? scrollbarWidth.x // use global because scroller might not have scrollbars yet but will need them in future
712
- : 0));
713
- }
714
- }
715
- }
716
- return { forceYScrollbars, forceXScrollbars, scrollerClientWidths, scrollerClientHeights };
717
- }
718
- updateStickyScrolling() {
719
- let { isRtl } = this.context;
720
- let argsByKey = this.scrollerElRefs.getAll().map((scrollEl) => [scrollEl, isRtl]);
721
- this.getStickyScrolling(argsByKey)
722
- .forEach((stickyScrolling) => stickyScrolling.updateSize());
723
- }
724
- updateScrollSyncers() {
725
- let [sectionCnt, chunksPerSection] = this.getDims();
726
- let cnt = sectionCnt * chunksPerSection;
727
- let scrollElsBySection = {};
728
- let scrollElsByColumn = {};
729
- let scrollElMap = this.scrollerElRefs.currentMap;
730
- for (let sectionI = 0; sectionI < sectionCnt; sectionI += 1) {
731
- let startIndex = sectionI * chunksPerSection;
732
- let endIndex = startIndex + chunksPerSection;
733
- scrollElsBySection[sectionI] = internal_cjs.collectFromHash(scrollElMap, startIndex, endIndex, 1); // use the filtered
734
- }
735
- for (let col = 0; col < chunksPerSection; col += 1) {
736
- scrollElsByColumn[col] = this.scrollerElRefs.collect(col, cnt, chunksPerSection); // DON'T use the filtered
737
- }
738
- this.scrollSyncersBySection = this.getScrollSyncersBySection(scrollElsBySection);
739
- this.scrollSyncersByColumn = this.getScrollSyncersByColumn(scrollElsByColumn);
740
- }
741
- destroyScrollSyncers() {
742
- internal_cjs.mapHash(this.scrollSyncersBySection, destroyScrollSyncer);
743
- internal_cjs.mapHash(this.scrollSyncersByColumn, destroyScrollSyncer);
744
- }
745
- getChunkConfigByIndex(index) {
746
- let chunksPerSection = this.getDims()[1];
747
- let sectionI = Math.floor(index / chunksPerSection);
748
- let chunkI = index % chunksPerSection;
749
- let sectionConfig = this.props.sections[sectionI];
750
- return sectionConfig && sectionConfig.chunks[chunkI];
751
- }
752
- forceScrollLeft(col, scrollLeft) {
753
- let scrollSyncer = this.scrollSyncersByColumn[col];
754
- if (scrollSyncer) {
755
- scrollSyncer.forceScrollLeft(scrollLeft);
756
- }
757
- }
758
- forceScrollTop(sectionI, scrollTop) {
759
- let scrollSyncer = this.scrollSyncersBySection[sectionI];
760
- if (scrollSyncer) {
761
- scrollSyncer.forceScrollTop(scrollTop);
762
- }
763
- }
764
- _handleChunkEl(chunkEl, key) {
765
- let chunkConfig = this.getChunkConfigByIndex(parseInt(key, 10));
766
- if (chunkConfig) { // null if section disappeared. bad, b/c won't null-set the elRef
767
- internal_cjs.setRef(chunkConfig.elRef, chunkEl);
768
- }
769
- }
770
- _handleScrollerEl(scrollerEl, key) {
771
- let chunkConfig = this.getChunkConfigByIndex(parseInt(key, 10));
772
- if (chunkConfig) { // null if section disappeared. bad, b/c won't null-set the elRef
773
- internal_cjs.setRef(chunkConfig.scrollerElRef, scrollerEl);
774
- }
775
- }
776
- getDims() {
777
- let sectionCnt = this.props.sections.length;
778
- let chunksPerSection = sectionCnt ? this.props.sections[0].chunks.length : 0;
779
- return [sectionCnt, chunksPerSection];
780
- }
781
- }
782
- ScrollGrid.addStateEquality({
783
- shrinkWidths: internal_cjs.isArraysEqual,
784
- scrollerClientWidths: internal_cjs.isPropsEqual,
785
- scrollerClientHeights: internal_cjs.isPropsEqual,
786
- });
787
- function sumNumbers(numbers) {
788
- let sum = 0;
789
- for (let n of numbers) {
790
- sum += n;
791
- }
792
- return sum;
793
- }
794
- function getRowInnerMaxHeight(rowEl) {
795
- let innerHeights = internal_cjs.findElements(rowEl, '.fc-scrollgrid-sync-inner').map(getElHeight);
796
- if (innerHeights.length) {
797
- return Math.max(...innerHeights);
798
- }
799
- return 0;
800
- }
801
- function getElHeight(el) {
802
- return el.offsetHeight; // better to deal with integers, for rounding, for PureComponent
803
- }
804
- function renderMacroColGroup(colGroupStats, shrinkWidths) {
805
- let children = colGroupStats.map((colGroupStat, i) => {
806
- let width = colGroupStat.width;
807
- if (width === 'shrink') {
808
- width = colGroupStat.totalColWidth + internal_cjs.sanitizeShrinkWidth(shrinkWidths[i]) + 1; // +1 for border :(
809
- }
810
- return ( // eslint-disable-next-line react/jsx-key
811
- preact_cjs.createElement("col", { style: { width } }));
812
- });
813
- return preact_cjs.createElement('colgroup', {}, ...children);
814
- }
815
- function compileColGroupStat(colGroupConfig) {
816
- let totalColWidth = sumColProp(colGroupConfig.cols, 'width'); // excludes "shrink"
817
- let totalColMinWidth = sumColProp(colGroupConfig.cols, 'minWidth');
818
- let hasShrinkCol = internal_cjs.hasShrinkWidth(colGroupConfig.cols);
819
- let allowXScrolling = colGroupConfig.width !== 'shrink' && Boolean(totalColWidth || totalColMinWidth || hasShrinkCol);
820
- return {
821
- hasShrinkCol,
822
- totalColWidth,
823
- totalColMinWidth,
824
- allowXScrolling,
825
- cols: colGroupConfig.cols,
826
- width: colGroupConfig.width,
827
- };
828
- }
829
- function sumColProp(cols, propName) {
830
- let total = 0;
831
- for (let col of cols) {
832
- let val = col[propName];
833
- if (typeof val === 'number') {
834
- total += val * (col.span || 1);
835
- }
836
- }
837
- return total;
838
- }
839
- const COL_GROUP_STAT_EQUALITY = {
840
- cols: internal_cjs.isColPropsEqual,
841
- };
842
- function isColGroupStatsEqual(stat0, stat1) {
843
- return internal_cjs.compareObjs(stat0, stat1, COL_GROUP_STAT_EQUALITY);
844
- }
845
- // for memoizers...
846
- function initScrollSyncer(isVertical, ...scrollEls) {
847
- return new ScrollSyncer(isVertical, scrollEls);
848
- }
849
- function destroyScrollSyncer(scrollSyncer) {
850
- scrollSyncer.destroy();
851
- }
852
- function initStickyScrolling(scrollEl, isRtl) {
853
- return new StickyScrolling(scrollEl, isRtl);
854
184
  }
855
185
 
856
- exports.ScrollGrid = ScrollGrid;
186
+ exports.ScrollerSyncer = ScrollerSyncer;