@full-ui/headless-grid 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 +18 -0
- package/README.md +23 -0
- package/cjs/index.cjs +371 -0
- package/esm/index.d.ts +87 -0
- package/esm/index.js +355 -0
- package/package.json +40 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
For complete licensing information, visit:
|
|
3
|
+
https://fullcalendar.io/license
|
|
4
|
+
|
|
5
|
+
FullCalendar Premium is tri-licensed, meaning you must choose
|
|
6
|
+
one of three licenses to use. Here is a summary of those licenses:
|
|
7
|
+
|
|
8
|
+
- Commercial License
|
|
9
|
+
(a paid license, intended for commercial use)
|
|
10
|
+
https://fullcalendar.io/commercial-license
|
|
11
|
+
|
|
12
|
+
- Creative Commons Non-Commercial No-Derivatives
|
|
13
|
+
(intended for trial and non-commercial use)
|
|
14
|
+
https://creativecommons.org/licenses/by-nc-nd/4.0/
|
|
15
|
+
|
|
16
|
+
- AGPLv3 License
|
|
17
|
+
(intended for open-source projects)
|
|
18
|
+
https://www.gnu.org/licenses/agpl-3.0.en.html
|
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
# Full UI :: Headless Grid
|
|
3
|
+
|
|
4
|
+
Headless datagrid library by the makers of FullCalendar
|
|
5
|
+
|
|
6
|
+
## Description
|
|
7
|
+
|
|
8
|
+
This "headless" library provides a very minimal set of primitives to render a JavaScript data grid using DOM-related optimizations like absolute positioning and scroll-simulation, which are traditionally the thorniest aspects of making a performant grid. It intends to be utility-first, like Tanstack Table, though in some aspects it must perform framework-dependent optimizations to achieve 60 FPS.
|
|
9
|
+
|
|
10
|
+
> [!IMPORTANT]
|
|
11
|
+
> If you are a FullCalendar Premium subscriber, and wish to use this library, please reach out to the tech support email you received upon purchase.
|
|
12
|
+
|
|
13
|
+
In its current form, it's being used internally by the `@fullcalendar/resource-timeline` package, which renders a timeline view alongside a FullCalendar-built datagrid. Our goal is to make the *FullCalendar* datagrid the best in the world, though in the meantime, many users will want to use a datagrid of their choosing and integrate it with Resource-Timeline. This is the dual-purpose of this package: to allow shimming of third-party data grid libraries into FullCalendar's UI.
|
|
14
|
+
|
|
15
|
+
## High-level API
|
|
16
|
+
|
|
17
|
+
- Column absolute positioning
|
|
18
|
+
- Row absolute positioning
|
|
19
|
+
- Scroll-view syncing across unrelated parents
|
|
20
|
+
- Virtual rendering (soon)
|
|
21
|
+
- A little bit of cross-framework magic
|
|
22
|
+
|
|
23
|
+
And more to come!... 👀
|
package/cjs/index.cjs
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
function fracToCssDim(frac) {
|
|
6
|
+
return frac * 100 + '%';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function parseDimConfig(input, minDim = 0) {
|
|
10
|
+
if (input != null) {
|
|
11
|
+
if (typeof input === 'string') {
|
|
12
|
+
let m = input.match(/^(.*)(%|px)$/);
|
|
13
|
+
if (m) {
|
|
14
|
+
const num = parseFloat(m[1]);
|
|
15
|
+
if (!isNaN(num)) {
|
|
16
|
+
if (m[2] === '%') {
|
|
17
|
+
return { pixels: 0, frac: num / 100, min: minDim };
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return { pixels: num, frac: 0, min: minDim };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else if (typeof input === 'number' && !isNaN(input)) {
|
|
26
|
+
return { pixels: input, frac: 0, min: minDim };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function parseSiblingDimConfig(input, grow, // TODO: use (and sanitize)
|
|
31
|
+
minDim) {
|
|
32
|
+
const partialDimConfig = parseDimConfig(input, minDim);
|
|
33
|
+
return partialDimConfig
|
|
34
|
+
? Object.assign(Object.assign({}, partialDimConfig), { grow: grow || 0 }) : { pixels: 0, frac: 0, grow: grow || 1, min: minDim };
|
|
35
|
+
}
|
|
36
|
+
/*
|
|
37
|
+
Ensure at least one column can grow
|
|
38
|
+
Mutates in-place
|
|
39
|
+
*/
|
|
40
|
+
function ensureDimConfigsGrow(dimConfigs) {
|
|
41
|
+
for (const dimConfig of dimConfigs) {
|
|
42
|
+
if (dimConfig.grow) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// make all expand equally
|
|
47
|
+
for (const dimConfig of dimConfigs) {
|
|
48
|
+
dimConfig.grow = 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function pixelizeDimConfigs(dimConfigs, clientDim) {
|
|
52
|
+
const pixelDims = [];
|
|
53
|
+
let preGrowTotal = 0;
|
|
54
|
+
let growDenom = 0;
|
|
55
|
+
for (const dimConfig of dimConfigs) {
|
|
56
|
+
const constrainedPixels = Math.max(dimConfig.pixels + (dimConfig.frac * clientDim), dimConfig.min);
|
|
57
|
+
pixelDims.push(constrainedPixels);
|
|
58
|
+
preGrowTotal += constrainedPixels;
|
|
59
|
+
growDenom += dimConfig.grow;
|
|
60
|
+
}
|
|
61
|
+
if (preGrowTotal < clientDim) {
|
|
62
|
+
const remainder = clientDim - preGrowTotal;
|
|
63
|
+
for (let i = 0; i < dimConfigs.length; i++) {
|
|
64
|
+
pixelDims[i] += remainder * (dimConfigs[i].grow / growDenom);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [pixelDims, preGrowTotal];
|
|
68
|
+
}
|
|
69
|
+
function flexifyDimConfigs(dimConfigs, pixelDims) {
|
|
70
|
+
const flexDims = [];
|
|
71
|
+
const flexGrows = [];
|
|
72
|
+
let pixelTotal = 0;
|
|
73
|
+
let fracTotal = 0;
|
|
74
|
+
for (let i = 0; i < dimConfigs.length; i++) {
|
|
75
|
+
const dimConfig = dimConfigs[i];
|
|
76
|
+
const constrainedPixels = Math.max(dimConfig.pixels, dimConfig.min);
|
|
77
|
+
flexDims.push(constrainedPixels);
|
|
78
|
+
flexGrows.push(pixelDims[i] - constrainedPixels); // a pixel value, but used as a proportion
|
|
79
|
+
pixelTotal += dimConfig.pixels + (dimConfig.grow ? dimConfig.min : 0);
|
|
80
|
+
fracTotal += dimConfig.frac; // not possible to enforce min for percentage here
|
|
81
|
+
}
|
|
82
|
+
const minCanvasDim = serializeDimConfig({
|
|
83
|
+
pixels: pixelTotal,
|
|
84
|
+
frac: fracTotal,
|
|
85
|
+
});
|
|
86
|
+
return [flexDims, flexGrows, minCanvasDim];
|
|
87
|
+
}
|
|
88
|
+
function serializeDimConfig({ pixels, frac }) {
|
|
89
|
+
if (!frac) {
|
|
90
|
+
return pixels;
|
|
91
|
+
}
|
|
92
|
+
if (!pixels) {
|
|
93
|
+
return fracToCssDim(frac);
|
|
94
|
+
}
|
|
95
|
+
return `calc(${fracToCssDim(frac)} + ${pixels}px)`;
|
|
96
|
+
}
|
|
97
|
+
function resizeSiblingDimConfig(dimConfigs, pixelDims, clientDim, resizeIndex, resizeDim) {
|
|
98
|
+
const newDimConfigs = [];
|
|
99
|
+
for (let i = 0; i < resizeIndex; i++) {
|
|
100
|
+
newDimConfigs.push(resizeDimConfig(dimConfigs[i], pixelDims[i], clientDim));
|
|
101
|
+
}
|
|
102
|
+
newDimConfigs.push(resizeDimConfig(dimConfigs[resizeIndex], resizeDim, clientDim));
|
|
103
|
+
const len = dimConfigs.length;
|
|
104
|
+
let anyGrow = false;
|
|
105
|
+
for (let i = resizeIndex + 1; i < len; i++) {
|
|
106
|
+
const dimConfig = dimConfigs[i];
|
|
107
|
+
newDimConfigs.push(dimConfig);
|
|
108
|
+
if (dimConfig.grow) {
|
|
109
|
+
anyGrow = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!anyGrow) {
|
|
113
|
+
for (let i = resizeIndex + 1; i < len; i++) {
|
|
114
|
+
newDimConfigs[i] = Object.assign({}, newDimConfigs[i], { grow: 1 });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return newDimConfigs;
|
|
118
|
+
}
|
|
119
|
+
function resizeDimConfig(dimConfig, newPixels, clientDim) {
|
|
120
|
+
const { min } = dimConfig;
|
|
121
|
+
newPixels = Math.max(min, newPixels);
|
|
122
|
+
if (dimConfig.pixels) {
|
|
123
|
+
return { pixels: newPixels, frac: 0, grow: 0, min };
|
|
124
|
+
}
|
|
125
|
+
return { pixels: 0, frac: newPixels / clientDim, grow: 0, min };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const ROW_BORDER_WIDTH = 1;
|
|
129
|
+
// Resource/Group Verticals
|
|
130
|
+
// -------------------------------------------------------------------------------------------------
|
|
131
|
+
function computeHeights(siblingNodes, getEntityKey, getEntityHeight, minHeight) {
|
|
132
|
+
const heightMap = new Map();
|
|
133
|
+
let [totalHeight, expandableCount] = computeTightHeights(siblingNodes, getEntityKey, getEntityHeight, heightMap);
|
|
134
|
+
if (minHeight != null && minHeight > totalHeight) {
|
|
135
|
+
expandHeights(siblingNodes, getEntityKey, heightMap, (minHeight - totalHeight) / expandableCount);
|
|
136
|
+
totalHeight = minHeight;
|
|
137
|
+
}
|
|
138
|
+
return [heightMap, totalHeight];
|
|
139
|
+
}
|
|
140
|
+
function computeTightHeights(siblingNodes, getEntityKey, getEntityHeight, heightMap) {
|
|
141
|
+
let totalHeight = 0;
|
|
142
|
+
let expandableCount = 0;
|
|
143
|
+
for (const siblingNode of siblingNodes) {
|
|
144
|
+
const entityKey = getEntityKey(siblingNode.entity);
|
|
145
|
+
let ownHeight = getEntityHeight(entityKey) || 0;
|
|
146
|
+
const [childrenHeight, childrenExpandableCount] = computeTightHeights(siblingNode.children, getEntityKey, getEntityHeight, heightMap);
|
|
147
|
+
if (siblingNode.pooledHeight) { // 'own' is side-by-side with children
|
|
148
|
+
if (ownHeight > childrenHeight) {
|
|
149
|
+
// children are smaller. grow them
|
|
150
|
+
expandHeights(siblingNode.children, getEntityKey, heightMap, (ownHeight - childrenHeight) / childrenExpandableCount);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// own-element is smaller. grow to height of children
|
|
154
|
+
ownHeight = childrenHeight;
|
|
155
|
+
}
|
|
156
|
+
totalHeight += ownHeight;
|
|
157
|
+
}
|
|
158
|
+
else { // 'own' is above children
|
|
159
|
+
totalHeight += ownHeight + ROW_BORDER_WIDTH + childrenHeight;
|
|
160
|
+
}
|
|
161
|
+
heightMap.set(entityKey, ownHeight);
|
|
162
|
+
// expandable?
|
|
163
|
+
// vertically stacked, and not a group
|
|
164
|
+
if (siblingNode.pooledHeight === undefined) {
|
|
165
|
+
expandableCount++;
|
|
166
|
+
}
|
|
167
|
+
expandableCount += childrenExpandableCount;
|
|
168
|
+
}
|
|
169
|
+
return [
|
|
170
|
+
totalHeight + ROW_BORDER_WIDTH * (siblingNodes.length - 1),
|
|
171
|
+
expandableCount,
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
function expandHeights(siblingNodes, getEntityKey, heightMap, expansion) {
|
|
175
|
+
for (const siblingNode of siblingNodes) {
|
|
176
|
+
// expandable?
|
|
177
|
+
// vertically stacked, and not a group
|
|
178
|
+
if (siblingNode.pooledHeight === undefined) {
|
|
179
|
+
const entityKey = getEntityKey(siblingNode.entity);
|
|
180
|
+
heightMap.set(entityKey, heightMap.get(entityKey) + expansion);
|
|
181
|
+
}
|
|
182
|
+
expandHeights(siblingNode.children, getEntityKey, heightMap, expansion);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function computeTopsFromHeights(siblingNodes, getEntityKey, heightMap) {
|
|
186
|
+
const topMap = new Map();
|
|
187
|
+
computeTopsStartingAt(siblingNodes, getEntityKey, heightMap, topMap, 0);
|
|
188
|
+
return topMap;
|
|
189
|
+
}
|
|
190
|
+
function computeTopsStartingAt(siblingNodes, getEntityKey, heightMap, topMap, top) {
|
|
191
|
+
for (let i = 0; i < siblingNodes.length; i++) {
|
|
192
|
+
const siblingNode = siblingNodes[i];
|
|
193
|
+
const entityKey = getEntityKey(siblingNode.entity);
|
|
194
|
+
topMap.set(entityKey, top);
|
|
195
|
+
if (!siblingNode.pooledHeight) {
|
|
196
|
+
top += heightMap.get(entityKey) + ROW_BORDER_WIDTH;
|
|
197
|
+
}
|
|
198
|
+
top = computeTopsStartingAt(siblingNode.children, getEntityKey, heightMap, topMap, top);
|
|
199
|
+
}
|
|
200
|
+
return top;
|
|
201
|
+
}
|
|
202
|
+
function findEntityByCoord(siblingNodes, entityTops, // keyed by createEntityId
|
|
203
|
+
entityHeights, // keyed by createEntityId
|
|
204
|
+
coord, // assumed >= 0
|
|
205
|
+
createEntityId) {
|
|
206
|
+
for (const siblingNode of siblingNodes) {
|
|
207
|
+
const entityKey = createEntityId(siblingNode.entity);
|
|
208
|
+
const siblingOwnTop = entityTops.get(entityKey);
|
|
209
|
+
const siblingOwnHeight = entityHeights.get(entityKey);
|
|
210
|
+
// intersection of the sibling's own element?
|
|
211
|
+
if (siblingOwnTop + siblingOwnHeight > coord) {
|
|
212
|
+
if (siblingNode.pooledHeight) {
|
|
213
|
+
// need more specific result
|
|
214
|
+
return findEntityByCoord(siblingNode.children, entityTops, entityHeights, coord, createEntityId);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
return [siblingNode.entity, siblingOwnTop, siblingOwnHeight];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else if (!siblingNode.pooledHeight) {
|
|
221
|
+
// search siblings below
|
|
222
|
+
const childrenRes = findEntityByCoord(siblingNode.children, entityTops, entityHeights, coord, createEntityId);
|
|
223
|
+
if (childrenRes) {
|
|
224
|
+
return childrenRes;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
class ScrollerSyncer {
|
|
231
|
+
constructor(isHorizontal = false) {
|
|
232
|
+
this.isHorizontal = isHorizontal;
|
|
233
|
+
this.scrollers = [];
|
|
234
|
+
this.destroyFuncs = [];
|
|
235
|
+
this.isPaused = false;
|
|
236
|
+
}
|
|
237
|
+
handleChildren(scrollers) {
|
|
238
|
+
if (!isArraysEqual(this.scrollers, scrollers)) {
|
|
239
|
+
this.destroy();
|
|
240
|
+
for (const scroller of scrollers) {
|
|
241
|
+
if (scroller) { // could be null
|
|
242
|
+
this.destroyFuncs.push(this.bindScroller(scroller));
|
|
243
|
+
this.scrollers.push(scroller);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
destroy() {
|
|
249
|
+
for (let destroyFunc of this.destroyFuncs) {
|
|
250
|
+
destroyFunc();
|
|
251
|
+
}
|
|
252
|
+
this.destroyFuncs = [];
|
|
253
|
+
this.scrollers = [];
|
|
254
|
+
}
|
|
255
|
+
get x() {
|
|
256
|
+
const { scrollers, masterScroller } = this;
|
|
257
|
+
return (masterScroller || scrollers[0]).x;
|
|
258
|
+
}
|
|
259
|
+
get y() {
|
|
260
|
+
const { scrollers, masterScroller } = this;
|
|
261
|
+
return (masterScroller || scrollers[0]).y;
|
|
262
|
+
}
|
|
263
|
+
scrollTo(scrollArg) {
|
|
264
|
+
this.isPaused = true;
|
|
265
|
+
const { scrollers } = this;
|
|
266
|
+
for (let scroller of scrollers) {
|
|
267
|
+
scroller.scrollTo(scrollArg);
|
|
268
|
+
}
|
|
269
|
+
this.isPaused = false;
|
|
270
|
+
}
|
|
271
|
+
addScrollEndListener(handler) {
|
|
272
|
+
var _a;
|
|
273
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.on('scrollEnd', handler);
|
|
274
|
+
}
|
|
275
|
+
removeScrollEndListener(handler) {
|
|
276
|
+
var _a;
|
|
277
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.off('scrollEnd', handler);
|
|
278
|
+
}
|
|
279
|
+
bindScroller(scroller) {
|
|
280
|
+
var _a, _b;
|
|
281
|
+
let { isHorizontal } = this;
|
|
282
|
+
const onScroll = (isUser) => {
|
|
283
|
+
if (!this.isPaused) {
|
|
284
|
+
if (!this.masterScroller || (this.masterScroller !== scroller && isUser)) {
|
|
285
|
+
this.assignMaster(scroller);
|
|
286
|
+
}
|
|
287
|
+
if (this.masterScroller === scroller) { // dealing with current
|
|
288
|
+
for (let otherScroller of this.scrollers) {
|
|
289
|
+
if (otherScroller !== scroller) {
|
|
290
|
+
if (isHorizontal) {
|
|
291
|
+
// TODO: user raw scrollLeft for better performance. No normalization necessary
|
|
292
|
+
otherScroller.scrollTo({ x: scroller.x });
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
otherScroller.scrollTo({ y: scroller.y });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
const onScrollEnd = (isUser) => {
|
|
303
|
+
var _a;
|
|
304
|
+
if (this.masterScroller === scroller) {
|
|
305
|
+
this.masterScroller = null;
|
|
306
|
+
const { x, y } = this; // new values
|
|
307
|
+
let isMoved = false;
|
|
308
|
+
if (this.isHorizontal) {
|
|
309
|
+
if (x !== this.prevX) {
|
|
310
|
+
this.prevX = x;
|
|
311
|
+
isMoved = true;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
if (y !== this.prevY) {
|
|
316
|
+
this.prevY = y;
|
|
317
|
+
isMoved = true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (isMoved) {
|
|
321
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.trigger('scrollEnd', isUser);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
(_a = scroller.listener.emitter) === null || _a === void 0 ? void 0 : _a.on('scroll', onScroll);
|
|
326
|
+
(_b = scroller.listener.emitter) === null || _b === void 0 ? void 0 : _b.on('scrollEnd', onScrollEnd);
|
|
327
|
+
return () => {
|
|
328
|
+
var _a, _b;
|
|
329
|
+
(_a = scroller.listener.emitter) === null || _a === void 0 ? void 0 : _a.off('scroll', onScroll);
|
|
330
|
+
(_b = scroller.listener.emitter) === null || _b === void 0 ? void 0 : _b.off('scrollEnd', onScrollEnd);
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
assignMaster(masterScroller) {
|
|
334
|
+
this.masterScroller = masterScroller;
|
|
335
|
+
for (let scroller of this.scrollers) {
|
|
336
|
+
if (scroller !== masterScroller) {
|
|
337
|
+
scroller.endScroll(); // to prevent residual scrolls from reclaiming master
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function isArraysEqual(array0, array1) {
|
|
343
|
+
if (array0 === array1) {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
let len = array0.length;
|
|
347
|
+
let i;
|
|
348
|
+
if (len !== array1.length) { // not array? or not same length?
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
for (i = 0; i < len; i += 1) {
|
|
352
|
+
if (array0[i] === array1[i]) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
exports.ROW_BORDER_WIDTH = ROW_BORDER_WIDTH;
|
|
360
|
+
exports.ScrollerSyncer = ScrollerSyncer;
|
|
361
|
+
exports.computeHeights = computeHeights;
|
|
362
|
+
exports.computeTopsFromHeights = computeTopsFromHeights;
|
|
363
|
+
exports.ensureDimConfigsGrow = ensureDimConfigsGrow;
|
|
364
|
+
exports.findEntityByCoord = findEntityByCoord;
|
|
365
|
+
exports.flexifyDimConfigs = flexifyDimConfigs;
|
|
366
|
+
exports.parseDimConfig = parseDimConfig;
|
|
367
|
+
exports.parseSiblingDimConfig = parseSiblingDimConfig;
|
|
368
|
+
exports.pixelizeDimConfigs = pixelizeDimConfigs;
|
|
369
|
+
exports.resizeDimConfig = resizeDimConfig;
|
|
370
|
+
exports.resizeSiblingDimConfig = resizeSiblingDimConfig;
|
|
371
|
+
exports.serializeDimConfig = serializeDimConfig;
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
type CssDimValue = string | number;
|
|
2
|
+
|
|
3
|
+
interface DimConfig {
|
|
4
|
+
pixels: number;
|
|
5
|
+
frac: number;
|
|
6
|
+
min: number;
|
|
7
|
+
}
|
|
8
|
+
interface SiblingDimConfig extends DimConfig {
|
|
9
|
+
grow: number;
|
|
10
|
+
}
|
|
11
|
+
declare function parseDimConfig(input: CssDimValue | undefined, minDim?: number): DimConfig | undefined;
|
|
12
|
+
declare function parseSiblingDimConfig(input: CssDimValue | undefined, grow: number | undefined, // TODO: use (and sanitize)
|
|
13
|
+
minDim: number | undefined): SiblingDimConfig;
|
|
14
|
+
declare function ensureDimConfigsGrow(dimConfigs: SiblingDimConfig[]): void;
|
|
15
|
+
declare function pixelizeDimConfigs(dimConfigs: SiblingDimConfig[], clientDim: number): [
|
|
16
|
+
pixelDims: number[],
|
|
17
|
+
minCanvasDim: number
|
|
18
|
+
];
|
|
19
|
+
declare function flexifyDimConfigs(dimConfigs: SiblingDimConfig[], pixelDims: number[]): [
|
|
20
|
+
flexDims: number[],
|
|
21
|
+
flexGrows: number[],
|
|
22
|
+
minCanvasDim: CssDimValue
|
|
23
|
+
];
|
|
24
|
+
declare function serializeDimConfig({ pixels, frac }: {
|
|
25
|
+
pixels: number;
|
|
26
|
+
frac: number;
|
|
27
|
+
}): CssDimValue;
|
|
28
|
+
declare function resizeSiblingDimConfig(dimConfigs: SiblingDimConfig[], pixelDims: number[], clientDim: number, resizeIndex: number, resizeDim: number): SiblingDimConfig[];
|
|
29
|
+
declare function resizeDimConfig(dimConfig: DimConfig, newPixels: number, clientDim: number): SiblingDimConfig;
|
|
30
|
+
|
|
31
|
+
interface GenericLayout<Entity> {
|
|
32
|
+
rowIndex: number;
|
|
33
|
+
visibleIndex: number;
|
|
34
|
+
entity: Entity;
|
|
35
|
+
pooledHeight?: boolean;
|
|
36
|
+
children: GenericLayout<Entity>[];
|
|
37
|
+
}
|
|
38
|
+
declare const ROW_BORDER_WIDTH = 1;
|
|
39
|
+
declare function computeHeights<Entity, Key>(siblingNodes: GenericLayout<Entity>[], getEntityKey: (entity: Entity) => Key, getEntityHeight: (entityKey: Key) => number, minHeight?: number): [
|
|
40
|
+
heightMap: Map<Key, number>,
|
|
41
|
+
totalHeight: number
|
|
42
|
+
];
|
|
43
|
+
declare function computeTopsFromHeights<Entity, Key>(siblingNodes: GenericLayout<Entity>[], getEntityKey: (entity: Entity) => Key, heightMap: Map<Key, number>): Map<Key, number>;
|
|
44
|
+
declare function findEntityByCoord<Entity>(siblingNodes: GenericLayout<Entity>[], entityTops: Map<string, number>, // keyed by createEntityId
|
|
45
|
+
entityHeights: Map<string, number>, // keyed by createEntityId
|
|
46
|
+
coord: number, // assumed >= 0
|
|
47
|
+
createEntityId: (entity: Entity) => string): [
|
|
48
|
+
entity: Entity,
|
|
49
|
+
top: number,
|
|
50
|
+
height: number
|
|
51
|
+
] | undefined;
|
|
52
|
+
|
|
53
|
+
interface Scroller {
|
|
54
|
+
scrollTo({ x, y }: {
|
|
55
|
+
x?: number;
|
|
56
|
+
y?: number;
|
|
57
|
+
}): void;
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
listener: any;
|
|
61
|
+
endScroll(): void;
|
|
62
|
+
}
|
|
63
|
+
declare class ScrollerSyncer {
|
|
64
|
+
private isHorizontal;
|
|
65
|
+
private emitter;
|
|
66
|
+
private scrollers;
|
|
67
|
+
private destroyFuncs;
|
|
68
|
+
private masterScroller;
|
|
69
|
+
private isPaused;
|
|
70
|
+
private prevX;
|
|
71
|
+
private prevY;
|
|
72
|
+
constructor(isHorizontal?: boolean);
|
|
73
|
+
handleChildren(scrollers: Scroller[]): void;
|
|
74
|
+
destroy(): void;
|
|
75
|
+
get x(): number;
|
|
76
|
+
get y(): number;
|
|
77
|
+
scrollTo(scrollArg: {
|
|
78
|
+
x?: number;
|
|
79
|
+
y?: number;
|
|
80
|
+
}): void;
|
|
81
|
+
addScrollEndListener(handler: (isUser: boolean) => void): void;
|
|
82
|
+
removeScrollEndListener(handler: (isUser: boolean) => void): void;
|
|
83
|
+
bindScroller(scroller: Scroller): () => void;
|
|
84
|
+
assignMaster(masterScroller: Scroller): void;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { DimConfig, GenericLayout, ROW_BORDER_WIDTH, Scroller, ScrollerSyncer, SiblingDimConfig, computeHeights, computeTopsFromHeights, ensureDimConfigsGrow, findEntityByCoord, flexifyDimConfigs, parseDimConfig, parseSiblingDimConfig, pixelizeDimConfigs, resizeDimConfig, resizeSiblingDimConfig, serializeDimConfig };
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
function fracToCssDim(frac) {
|
|
2
|
+
return frac * 100 + '%';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function parseDimConfig(input, minDim = 0) {
|
|
6
|
+
if (input != null) {
|
|
7
|
+
if (typeof input === 'string') {
|
|
8
|
+
let m = input.match(/^(.*)(%|px)$/);
|
|
9
|
+
if (m) {
|
|
10
|
+
const num = parseFloat(m[1]);
|
|
11
|
+
if (!isNaN(num)) {
|
|
12
|
+
if (m[2] === '%') {
|
|
13
|
+
return { pixels: 0, frac: num / 100, min: minDim };
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
return { pixels: num, frac: 0, min: minDim };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else if (typeof input === 'number' && !isNaN(input)) {
|
|
22
|
+
return { pixels: input, frac: 0, min: minDim };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parseSiblingDimConfig(input, grow, // TODO: use (and sanitize)
|
|
27
|
+
minDim) {
|
|
28
|
+
const partialDimConfig = parseDimConfig(input, minDim);
|
|
29
|
+
return partialDimConfig
|
|
30
|
+
? Object.assign(Object.assign({}, partialDimConfig), { grow: grow || 0 }) : { pixels: 0, frac: 0, grow: grow || 1, min: minDim };
|
|
31
|
+
}
|
|
32
|
+
/*
|
|
33
|
+
Ensure at least one column can grow
|
|
34
|
+
Mutates in-place
|
|
35
|
+
*/
|
|
36
|
+
function ensureDimConfigsGrow(dimConfigs) {
|
|
37
|
+
for (const dimConfig of dimConfigs) {
|
|
38
|
+
if (dimConfig.grow) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// make all expand equally
|
|
43
|
+
for (const dimConfig of dimConfigs) {
|
|
44
|
+
dimConfig.grow = 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function pixelizeDimConfigs(dimConfigs, clientDim) {
|
|
48
|
+
const pixelDims = [];
|
|
49
|
+
let preGrowTotal = 0;
|
|
50
|
+
let growDenom = 0;
|
|
51
|
+
for (const dimConfig of dimConfigs) {
|
|
52
|
+
const constrainedPixels = Math.max(dimConfig.pixels + (dimConfig.frac * clientDim), dimConfig.min);
|
|
53
|
+
pixelDims.push(constrainedPixels);
|
|
54
|
+
preGrowTotal += constrainedPixels;
|
|
55
|
+
growDenom += dimConfig.grow;
|
|
56
|
+
}
|
|
57
|
+
if (preGrowTotal < clientDim) {
|
|
58
|
+
const remainder = clientDim - preGrowTotal;
|
|
59
|
+
for (let i = 0; i < dimConfigs.length; i++) {
|
|
60
|
+
pixelDims[i] += remainder * (dimConfigs[i].grow / growDenom);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return [pixelDims, preGrowTotal];
|
|
64
|
+
}
|
|
65
|
+
function flexifyDimConfigs(dimConfigs, pixelDims) {
|
|
66
|
+
const flexDims = [];
|
|
67
|
+
const flexGrows = [];
|
|
68
|
+
let pixelTotal = 0;
|
|
69
|
+
let fracTotal = 0;
|
|
70
|
+
for (let i = 0; i < dimConfigs.length; i++) {
|
|
71
|
+
const dimConfig = dimConfigs[i];
|
|
72
|
+
const constrainedPixels = Math.max(dimConfig.pixels, dimConfig.min);
|
|
73
|
+
flexDims.push(constrainedPixels);
|
|
74
|
+
flexGrows.push(pixelDims[i] - constrainedPixels); // a pixel value, but used as a proportion
|
|
75
|
+
pixelTotal += dimConfig.pixels + (dimConfig.grow ? dimConfig.min : 0);
|
|
76
|
+
fracTotal += dimConfig.frac; // not possible to enforce min for percentage here
|
|
77
|
+
}
|
|
78
|
+
const minCanvasDim = serializeDimConfig({
|
|
79
|
+
pixels: pixelTotal,
|
|
80
|
+
frac: fracTotal,
|
|
81
|
+
});
|
|
82
|
+
return [flexDims, flexGrows, minCanvasDim];
|
|
83
|
+
}
|
|
84
|
+
function serializeDimConfig({ pixels, frac }) {
|
|
85
|
+
if (!frac) {
|
|
86
|
+
return pixels;
|
|
87
|
+
}
|
|
88
|
+
if (!pixels) {
|
|
89
|
+
return fracToCssDim(frac);
|
|
90
|
+
}
|
|
91
|
+
return `calc(${fracToCssDim(frac)} + ${pixels}px)`;
|
|
92
|
+
}
|
|
93
|
+
function resizeSiblingDimConfig(dimConfigs, pixelDims, clientDim, resizeIndex, resizeDim) {
|
|
94
|
+
const newDimConfigs = [];
|
|
95
|
+
for (let i = 0; i < resizeIndex; i++) {
|
|
96
|
+
newDimConfigs.push(resizeDimConfig(dimConfigs[i], pixelDims[i], clientDim));
|
|
97
|
+
}
|
|
98
|
+
newDimConfigs.push(resizeDimConfig(dimConfigs[resizeIndex], resizeDim, clientDim));
|
|
99
|
+
const len = dimConfigs.length;
|
|
100
|
+
let anyGrow = false;
|
|
101
|
+
for (let i = resizeIndex + 1; i < len; i++) {
|
|
102
|
+
const dimConfig = dimConfigs[i];
|
|
103
|
+
newDimConfigs.push(dimConfig);
|
|
104
|
+
if (dimConfig.grow) {
|
|
105
|
+
anyGrow = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (!anyGrow) {
|
|
109
|
+
for (let i = resizeIndex + 1; i < len; i++) {
|
|
110
|
+
newDimConfigs[i] = Object.assign({}, newDimConfigs[i], { grow: 1 });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return newDimConfigs;
|
|
114
|
+
}
|
|
115
|
+
function resizeDimConfig(dimConfig, newPixels, clientDim) {
|
|
116
|
+
const { min } = dimConfig;
|
|
117
|
+
newPixels = Math.max(min, newPixels);
|
|
118
|
+
if (dimConfig.pixels) {
|
|
119
|
+
return { pixels: newPixels, frac: 0, grow: 0, min };
|
|
120
|
+
}
|
|
121
|
+
return { pixels: 0, frac: newPixels / clientDim, grow: 0, min };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const ROW_BORDER_WIDTH = 1;
|
|
125
|
+
// Resource/Group Verticals
|
|
126
|
+
// -------------------------------------------------------------------------------------------------
|
|
127
|
+
function computeHeights(siblingNodes, getEntityKey, getEntityHeight, minHeight) {
|
|
128
|
+
const heightMap = new Map();
|
|
129
|
+
let [totalHeight, expandableCount] = computeTightHeights(siblingNodes, getEntityKey, getEntityHeight, heightMap);
|
|
130
|
+
if (minHeight != null && minHeight > totalHeight) {
|
|
131
|
+
expandHeights(siblingNodes, getEntityKey, heightMap, (minHeight - totalHeight) / expandableCount);
|
|
132
|
+
totalHeight = minHeight;
|
|
133
|
+
}
|
|
134
|
+
return [heightMap, totalHeight];
|
|
135
|
+
}
|
|
136
|
+
function computeTightHeights(siblingNodes, getEntityKey, getEntityHeight, heightMap) {
|
|
137
|
+
let totalHeight = 0;
|
|
138
|
+
let expandableCount = 0;
|
|
139
|
+
for (const siblingNode of siblingNodes) {
|
|
140
|
+
const entityKey = getEntityKey(siblingNode.entity);
|
|
141
|
+
let ownHeight = getEntityHeight(entityKey) || 0;
|
|
142
|
+
const [childrenHeight, childrenExpandableCount] = computeTightHeights(siblingNode.children, getEntityKey, getEntityHeight, heightMap);
|
|
143
|
+
if (siblingNode.pooledHeight) { // 'own' is side-by-side with children
|
|
144
|
+
if (ownHeight > childrenHeight) {
|
|
145
|
+
// children are smaller. grow them
|
|
146
|
+
expandHeights(siblingNode.children, getEntityKey, heightMap, (ownHeight - childrenHeight) / childrenExpandableCount);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
// own-element is smaller. grow to height of children
|
|
150
|
+
ownHeight = childrenHeight;
|
|
151
|
+
}
|
|
152
|
+
totalHeight += ownHeight;
|
|
153
|
+
}
|
|
154
|
+
else { // 'own' is above children
|
|
155
|
+
totalHeight += ownHeight + ROW_BORDER_WIDTH + childrenHeight;
|
|
156
|
+
}
|
|
157
|
+
heightMap.set(entityKey, ownHeight);
|
|
158
|
+
// expandable?
|
|
159
|
+
// vertically stacked, and not a group
|
|
160
|
+
if (siblingNode.pooledHeight === undefined) {
|
|
161
|
+
expandableCount++;
|
|
162
|
+
}
|
|
163
|
+
expandableCount += childrenExpandableCount;
|
|
164
|
+
}
|
|
165
|
+
return [
|
|
166
|
+
totalHeight + ROW_BORDER_WIDTH * (siblingNodes.length - 1),
|
|
167
|
+
expandableCount,
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
function expandHeights(siblingNodes, getEntityKey, heightMap, expansion) {
|
|
171
|
+
for (const siblingNode of siblingNodes) {
|
|
172
|
+
// expandable?
|
|
173
|
+
// vertically stacked, and not a group
|
|
174
|
+
if (siblingNode.pooledHeight === undefined) {
|
|
175
|
+
const entityKey = getEntityKey(siblingNode.entity);
|
|
176
|
+
heightMap.set(entityKey, heightMap.get(entityKey) + expansion);
|
|
177
|
+
}
|
|
178
|
+
expandHeights(siblingNode.children, getEntityKey, heightMap, expansion);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function computeTopsFromHeights(siblingNodes, getEntityKey, heightMap) {
|
|
182
|
+
const topMap = new Map();
|
|
183
|
+
computeTopsStartingAt(siblingNodes, getEntityKey, heightMap, topMap, 0);
|
|
184
|
+
return topMap;
|
|
185
|
+
}
|
|
186
|
+
function computeTopsStartingAt(siblingNodes, getEntityKey, heightMap, topMap, top) {
|
|
187
|
+
for (let i = 0; i < siblingNodes.length; i++) {
|
|
188
|
+
const siblingNode = siblingNodes[i];
|
|
189
|
+
const entityKey = getEntityKey(siblingNode.entity);
|
|
190
|
+
topMap.set(entityKey, top);
|
|
191
|
+
if (!siblingNode.pooledHeight) {
|
|
192
|
+
top += heightMap.get(entityKey) + ROW_BORDER_WIDTH;
|
|
193
|
+
}
|
|
194
|
+
top = computeTopsStartingAt(siblingNode.children, getEntityKey, heightMap, topMap, top);
|
|
195
|
+
}
|
|
196
|
+
return top;
|
|
197
|
+
}
|
|
198
|
+
function findEntityByCoord(siblingNodes, entityTops, // keyed by createEntityId
|
|
199
|
+
entityHeights, // keyed by createEntityId
|
|
200
|
+
coord, // assumed >= 0
|
|
201
|
+
createEntityId) {
|
|
202
|
+
for (const siblingNode of siblingNodes) {
|
|
203
|
+
const entityKey = createEntityId(siblingNode.entity);
|
|
204
|
+
const siblingOwnTop = entityTops.get(entityKey);
|
|
205
|
+
const siblingOwnHeight = entityHeights.get(entityKey);
|
|
206
|
+
// intersection of the sibling's own element?
|
|
207
|
+
if (siblingOwnTop + siblingOwnHeight > coord) {
|
|
208
|
+
if (siblingNode.pooledHeight) {
|
|
209
|
+
// need more specific result
|
|
210
|
+
return findEntityByCoord(siblingNode.children, entityTops, entityHeights, coord, createEntityId);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
return [siblingNode.entity, siblingOwnTop, siblingOwnHeight];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (!siblingNode.pooledHeight) {
|
|
217
|
+
// search siblings below
|
|
218
|
+
const childrenRes = findEntityByCoord(siblingNode.children, entityTops, entityHeights, coord, createEntityId);
|
|
219
|
+
if (childrenRes) {
|
|
220
|
+
return childrenRes;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
class ScrollerSyncer {
|
|
227
|
+
constructor(isHorizontal = false) {
|
|
228
|
+
this.isHorizontal = isHorizontal;
|
|
229
|
+
this.scrollers = [];
|
|
230
|
+
this.destroyFuncs = [];
|
|
231
|
+
this.isPaused = false;
|
|
232
|
+
}
|
|
233
|
+
handleChildren(scrollers) {
|
|
234
|
+
if (!isArraysEqual(this.scrollers, scrollers)) {
|
|
235
|
+
this.destroy();
|
|
236
|
+
for (const scroller of scrollers) {
|
|
237
|
+
if (scroller) { // could be null
|
|
238
|
+
this.destroyFuncs.push(this.bindScroller(scroller));
|
|
239
|
+
this.scrollers.push(scroller);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
destroy() {
|
|
245
|
+
for (let destroyFunc of this.destroyFuncs) {
|
|
246
|
+
destroyFunc();
|
|
247
|
+
}
|
|
248
|
+
this.destroyFuncs = [];
|
|
249
|
+
this.scrollers = [];
|
|
250
|
+
}
|
|
251
|
+
get x() {
|
|
252
|
+
const { scrollers, masterScroller } = this;
|
|
253
|
+
return (masterScroller || scrollers[0]).x;
|
|
254
|
+
}
|
|
255
|
+
get y() {
|
|
256
|
+
const { scrollers, masterScroller } = this;
|
|
257
|
+
return (masterScroller || scrollers[0]).y;
|
|
258
|
+
}
|
|
259
|
+
scrollTo(scrollArg) {
|
|
260
|
+
this.isPaused = true;
|
|
261
|
+
const { scrollers } = this;
|
|
262
|
+
for (let scroller of scrollers) {
|
|
263
|
+
scroller.scrollTo(scrollArg);
|
|
264
|
+
}
|
|
265
|
+
this.isPaused = false;
|
|
266
|
+
}
|
|
267
|
+
addScrollEndListener(handler) {
|
|
268
|
+
var _a;
|
|
269
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.on('scrollEnd', handler);
|
|
270
|
+
}
|
|
271
|
+
removeScrollEndListener(handler) {
|
|
272
|
+
var _a;
|
|
273
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.off('scrollEnd', handler);
|
|
274
|
+
}
|
|
275
|
+
bindScroller(scroller) {
|
|
276
|
+
var _a, _b;
|
|
277
|
+
let { isHorizontal } = this;
|
|
278
|
+
const onScroll = (isUser) => {
|
|
279
|
+
if (!this.isPaused) {
|
|
280
|
+
if (!this.masterScroller || (this.masterScroller !== scroller && isUser)) {
|
|
281
|
+
this.assignMaster(scroller);
|
|
282
|
+
}
|
|
283
|
+
if (this.masterScroller === scroller) { // dealing with current
|
|
284
|
+
for (let otherScroller of this.scrollers) {
|
|
285
|
+
if (otherScroller !== scroller) {
|
|
286
|
+
if (isHorizontal) {
|
|
287
|
+
// TODO: user raw scrollLeft for better performance. No normalization necessary
|
|
288
|
+
otherScroller.scrollTo({ x: scroller.x });
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
otherScroller.scrollTo({ y: scroller.y });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
const onScrollEnd = (isUser) => {
|
|
299
|
+
var _a;
|
|
300
|
+
if (this.masterScroller === scroller) {
|
|
301
|
+
this.masterScroller = null;
|
|
302
|
+
const { x, y } = this; // new values
|
|
303
|
+
let isMoved = false;
|
|
304
|
+
if (this.isHorizontal) {
|
|
305
|
+
if (x !== this.prevX) {
|
|
306
|
+
this.prevX = x;
|
|
307
|
+
isMoved = true;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
if (y !== this.prevY) {
|
|
312
|
+
this.prevY = y;
|
|
313
|
+
isMoved = true;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (isMoved) {
|
|
317
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.trigger('scrollEnd', isUser);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
(_a = scroller.listener.emitter) === null || _a === void 0 ? void 0 : _a.on('scroll', onScroll);
|
|
322
|
+
(_b = scroller.listener.emitter) === null || _b === void 0 ? void 0 : _b.on('scrollEnd', onScrollEnd);
|
|
323
|
+
return () => {
|
|
324
|
+
var _a, _b;
|
|
325
|
+
(_a = scroller.listener.emitter) === null || _a === void 0 ? void 0 : _a.off('scroll', onScroll);
|
|
326
|
+
(_b = scroller.listener.emitter) === null || _b === void 0 ? void 0 : _b.off('scrollEnd', onScrollEnd);
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
assignMaster(masterScroller) {
|
|
330
|
+
this.masterScroller = masterScroller;
|
|
331
|
+
for (let scroller of this.scrollers) {
|
|
332
|
+
if (scroller !== masterScroller) {
|
|
333
|
+
scroller.endScroll(); // to prevent residual scrolls from reclaiming master
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function isArraysEqual(array0, array1) {
|
|
339
|
+
if (array0 === array1) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
let len = array0.length;
|
|
343
|
+
let i;
|
|
344
|
+
if (len !== array1.length) { // not array? or not same length?
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
for (i = 0; i < len; i += 1) {
|
|
348
|
+
if (array0[i] === array1[i]) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export { ROW_BORDER_WIDTH, ScrollerSyncer, computeHeights, computeTopsFromHeights, ensureDimConfigsGrow, findEntityByCoord, flexifyDimConfigs, parseDimConfig, parseSiblingDimConfig, pixelizeDimConfigs, resizeDimConfig, resizeSiblingDimConfig, serializeDimConfig };
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@full-ui/headless-grid",
|
|
3
|
+
"version": "7.0.0-beta.5",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"headless",
|
|
6
|
+
"grid",
|
|
7
|
+
"datagrid"
|
|
8
|
+
],
|
|
9
|
+
"title": "Headless DataGrid Library",
|
|
10
|
+
"description": "Headless datagrid library by the makers of FullCalendar",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"homepage": "https://fullcalendar.io/docs/premium",
|
|
13
|
+
"bugs": "https://fullcalendar.io/reporting-bugs",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/fullcalendar/fullcalendar-workspace.git",
|
|
17
|
+
"directory": "premium/packages/headless-grid"
|
|
18
|
+
},
|
|
19
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Adam Shaw",
|
|
22
|
+
"email": "arshaw@arshaw.com",
|
|
23
|
+
"url": "http://arshaw.com/"
|
|
24
|
+
},
|
|
25
|
+
"copyright": "2025 Adam Shaw",
|
|
26
|
+
"types": "./esm/index.d.ts",
|
|
27
|
+
"module": "./esm/index.js",
|
|
28
|
+
"main": "./cjs/index.cjs",
|
|
29
|
+
"exports": {
|
|
30
|
+
"./package.json": "./package.json",
|
|
31
|
+
".": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./esm/index.d.ts",
|
|
34
|
+
"default": "./esm/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": "./cjs/index.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"sideEffects": false
|
|
40
|
+
}
|