@oscarpalmer/tabela 0.4.0 → 0.6.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/dist/components/body.component.js +33 -55
- package/dist/components/column.component.js +20 -25
- package/dist/components/footer.component.js +33 -34
- package/dist/components/header.component.js +21 -26
- package/dist/components/row.component.js +34 -36
- package/dist/helpers/dom.helpers.js +35 -30
- package/dist/index.js +5 -5
- package/dist/managers/column.manager.js +32 -0
- package/dist/managers/data.manager.js +30 -0
- package/dist/managers/row.manager.js +21 -0
- package/dist/managers/virtualization.manager.js +89 -85
- package/dist/models/body.model.js +0 -0
- package/dist/models/column.model.js +0 -1
- package/dist/models/data.model.js +0 -0
- package/dist/models/footer.model.js +0 -0
- package/dist/models/header.model.js +0 -0
- package/dist/models/tabela.options.js +0 -1
- package/dist/models/virtualization.model.js +0 -0
- package/dist/tabela.full.js +645 -0
- package/dist/tabela.js +45 -43
- package/package.json +22 -24
- package/src/components/body.component.ts +22 -51
- package/src/components/column.component.ts +14 -8
- package/src/components/footer.component.ts +18 -16
- package/src/components/header.component.ts +9 -15
- package/src/components/row.component.ts +39 -32
- package/src/helpers/dom.helpers.ts +50 -15
- package/src/index.ts +8 -1
- package/src/managers/column.manager.ts +65 -0
- package/src/managers/data.manager.ts +45 -0
- package/src/managers/row.manager.ts +32 -0
- package/src/managers/virtualization.manager.ts +101 -72
- package/src/models/body.model.ts +4 -0
- package/src/models/column.model.ts +1 -7
- package/src/models/data.model.ts +16 -0
- package/src/models/footer.model.ts +5 -0
- package/src/models/header.model.ts +4 -0
- package/src/models/tabela.options.ts +2 -0
- package/src/models/virtualization.model.ts +14 -0
- package/src/tabela.ts +65 -36
- package/types/components/body.component.d.ts +2 -13
- package/types/components/footer.component.d.ts +4 -7
- package/types/components/header.component.d.ts +4 -8
- package/types/components/row.component.d.ts +7 -7
- package/types/helpers/dom.helpers.d.ts +1 -0
- package/types/index.d.ts +4 -1
- package/types/managers/column.manager.d.ts +12 -0
- package/types/managers/data.manager.d.ts +11 -0
- package/types/managers/row.manager.d.ts +11 -0
- package/types/managers/virtualization.manager.d.ts +12 -12
- package/types/models/body.model.d.ts +4 -0
- package/types/models/data.model.d.ts +14 -0
- package/types/models/footer.model.d.ts +5 -0
- package/types/models/header.model.d.ts +4 -0
- package/types/models/tabela.options.d.ts +2 -0
- package/types/models/virtualization.model.d.ts +12 -0
- package/types/tabela.d.ts +19 -5
- package/dist/components/body.component.cjs +0 -58
- package/dist/components/column.component.cjs +0 -27
- package/dist/components/footer.component.cjs +0 -35
- package/dist/components/header.component.cjs +0 -28
- package/dist/components/row.component.cjs +0 -38
- package/dist/helpers/dom.helpers.cjs +0 -36
- package/dist/index.cjs +0 -5
- package/dist/managers/virtualization.manager.cjs +0 -88
- package/dist/models/column.model.cjs +0 -1
- package/dist/models/tabela.options.cjs +0 -1
- package/dist/tabela.cjs +0 -47
- package/types/components/body.component.d.cts +0 -98
- package/types/components/column.component.d.cts +0 -98
- package/types/components/footer.component.d.cts +0 -98
- package/types/components/header.component.d.cts +0 -98
- package/types/components/row.component.d.cts +0 -98
- package/types/helpers/dom.helpers.d.cts +0 -12
- package/types/index.d.cts +0 -99
- package/types/managers/virtualization.manager.d.cts +0 -98
- package/types/models/column.model.d.cts +0 -17
- package/types/models/tabela.options.d.cts +0 -18
- package/types/tabela.d.cts +0 -99
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the string value from any value
|
|
3
|
+
* @param value Original value
|
|
4
|
+
* @returns String representation of the value
|
|
5
|
+
*/
|
|
6
|
+
function getString(value) {
|
|
7
|
+
if (typeof value === "string") return value;
|
|
8
|
+
if (value == null) return "";
|
|
9
|
+
if (typeof value === "function") return getString(value());
|
|
10
|
+
if (typeof value !== "object") return String(value);
|
|
11
|
+
const asString = String(value.valueOf?.() ?? value);
|
|
12
|
+
return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
|
|
13
|
+
}
|
|
14
|
+
function getBoolean(value, defaultValue) {
|
|
15
|
+
return typeof value === "boolean" ? value : defaultValue ?? false;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Is the value an event target?
|
|
19
|
+
* @param value Value to check
|
|
20
|
+
* @returns `true` if it's an event target, otherwise `false`
|
|
21
|
+
*/
|
|
22
|
+
function isEventTarget(value) {
|
|
23
|
+
return typeof value === "object" && value != null && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function" && typeof value.dispatchEvent === "function";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Is the value an HTML or SVG element?
|
|
27
|
+
* @param value Value to check
|
|
28
|
+
* @returns `true` if it's an HTML or SVG element, otherwise `false`
|
|
29
|
+
*/
|
|
30
|
+
function isHTMLOrSVGElement(value) {
|
|
31
|
+
return value instanceof HTMLElement || value instanceof SVGElement;
|
|
32
|
+
}
|
|
33
|
+
new Set([
|
|
34
|
+
Node.ELEMENT_NODE,
|
|
35
|
+
Node.TEXT_NODE,
|
|
36
|
+
Node.PROCESSING_INSTRUCTION_NODE,
|
|
37
|
+
Node.COMMENT_NODE,
|
|
38
|
+
Node.DOCUMENT_TYPE_NODE
|
|
39
|
+
]);
|
|
40
|
+
/**
|
|
41
|
+
* Is the value `undefined`, `null`, or a whitespace-only string?
|
|
42
|
+
* @param value Value to check
|
|
43
|
+
* @returns `true` if the value is nullable or a whitespace-only string, otherwise `false`
|
|
44
|
+
*/
|
|
45
|
+
function isNullableOrWhitespace(value) {
|
|
46
|
+
return value == null || EXPRESSION_WHITESPACE.test(getString(value));
|
|
47
|
+
}
|
|
48
|
+
var EXPRESSION_WHITESPACE = /^\s*$/;
|
|
49
|
+
Object.freeze([
|
|
50
|
+
"async",
|
|
51
|
+
"autofocus",
|
|
52
|
+
"autoplay",
|
|
53
|
+
"checked",
|
|
54
|
+
"controls",
|
|
55
|
+
"default",
|
|
56
|
+
"defer",
|
|
57
|
+
"disabled",
|
|
58
|
+
"formnovalidate",
|
|
59
|
+
"hidden",
|
|
60
|
+
"inert",
|
|
61
|
+
"ismap",
|
|
62
|
+
"itemscope",
|
|
63
|
+
"loop",
|
|
64
|
+
"multiple",
|
|
65
|
+
"muted",
|
|
66
|
+
"nomodule",
|
|
67
|
+
"novalidate",
|
|
68
|
+
"open",
|
|
69
|
+
"playsinline",
|
|
70
|
+
"readonly",
|
|
71
|
+
"required",
|
|
72
|
+
"reversed",
|
|
73
|
+
"selected"
|
|
74
|
+
]);
|
|
75
|
+
document.createElement("form");
|
|
76
|
+
function setElementValues(element, first, second, third, callback) {
|
|
77
|
+
if (!isHTMLOrSVGElement(element)) return;
|
|
78
|
+
if (typeof first === "string") {
|
|
79
|
+
callback(element, first, second, third);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const isArray = Array.isArray(first);
|
|
83
|
+
if (!isArray && !(typeof first === "object" && first !== null)) return;
|
|
84
|
+
const entries = isArray ? first : Object.entries(first).map(([name, value]) => ({
|
|
85
|
+
name,
|
|
86
|
+
value
|
|
87
|
+
}));
|
|
88
|
+
const { length } = entries;
|
|
89
|
+
for (let index = 0; index < length; index += 1) {
|
|
90
|
+
const entry = entries[index];
|
|
91
|
+
if (typeof entry === "object" && typeof entry?.name === "string") callback(element, entry.name, entry.value, third);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function updateElementValue(element, key, value, set, remove, isBoolean, json) {
|
|
95
|
+
if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
|
|
96
|
+
else set.call(element, key, json ? JSON.stringify(value) : String(value));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Set styles on an element
|
|
100
|
+
* @param element Element to set the styles on
|
|
101
|
+
* @param styles Styles to set
|
|
102
|
+
*/
|
|
103
|
+
function setStyles(element, styles) {
|
|
104
|
+
setElementValues(element, styles, null, null, updateStyleProperty);
|
|
105
|
+
}
|
|
106
|
+
function updateStyleProperty(element, key, value) {
|
|
107
|
+
updateElementValue(element, key, value, function(property, style) {
|
|
108
|
+
this.style[property] = style;
|
|
109
|
+
}, function(property) {
|
|
110
|
+
this.style[property] = "";
|
|
111
|
+
}, false, false);
|
|
112
|
+
}
|
|
113
|
+
function createCell(width, body) {
|
|
114
|
+
const cell = createElement("div", {
|
|
115
|
+
className: "tabela__cell",
|
|
116
|
+
role: "cell"
|
|
117
|
+
}, { width: `${width}px` });
|
|
118
|
+
if (body ?? true) cell.classList.add("tabela__cell-body");
|
|
119
|
+
return cell;
|
|
120
|
+
}
|
|
121
|
+
function createElement(tagName, properties, style) {
|
|
122
|
+
const element = document.createElement(tagName);
|
|
123
|
+
const keys = Object.keys(properties);
|
|
124
|
+
for (const key of keys) element[key] = properties[key];
|
|
125
|
+
setStyles(element, style);
|
|
126
|
+
return element;
|
|
127
|
+
}
|
|
128
|
+
function createRowGroup(withRow) {
|
|
129
|
+
const group = createElement("div", {
|
|
130
|
+
className: "tabela__rowgroup",
|
|
131
|
+
role: "rowgroup"
|
|
132
|
+
}, {});
|
|
133
|
+
if (!(withRow ?? true)) return group;
|
|
134
|
+
const row = createRow(false);
|
|
135
|
+
group.append(row);
|
|
136
|
+
return {
|
|
137
|
+
group,
|
|
138
|
+
row
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function createRow(withStyle) {
|
|
142
|
+
const row = createElement("div", {
|
|
143
|
+
className: "tabela__row",
|
|
144
|
+
role: "row"
|
|
145
|
+
}, {});
|
|
146
|
+
if (withStyle ?? true) setStyles(row, {
|
|
147
|
+
inset: "0 auto auto 0",
|
|
148
|
+
position: "absolute"
|
|
149
|
+
});
|
|
150
|
+
return row;
|
|
151
|
+
}
|
|
152
|
+
function createFaker() {
|
|
153
|
+
return createElement("div", {}, {
|
|
154
|
+
height: "0",
|
|
155
|
+
inset: "0 auto auto 0",
|
|
156
|
+
opacity: "0",
|
|
157
|
+
pointerEvents: "none",
|
|
158
|
+
position: "absolute",
|
|
159
|
+
width: "1px"
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
var BodyComponent = class {
|
|
163
|
+
elements = {
|
|
164
|
+
faker: createFaker(),
|
|
165
|
+
group: void 0
|
|
166
|
+
};
|
|
167
|
+
constructor(tabela) {
|
|
168
|
+
this.tabela = tabela;
|
|
169
|
+
const group = createRowGroup(false);
|
|
170
|
+
this.elements.group = group;
|
|
171
|
+
group.className += " tabela__rowgroup-body";
|
|
172
|
+
group.tabIndex = 0;
|
|
173
|
+
setStyles(group, {
|
|
174
|
+
height: "100%",
|
|
175
|
+
overflow: "auto",
|
|
176
|
+
position: "relative"
|
|
177
|
+
});
|
|
178
|
+
group.append(this.elements.faker);
|
|
179
|
+
}
|
|
180
|
+
destroy() {
|
|
181
|
+
this.elements.faker = void 0;
|
|
182
|
+
this.elements.group = void 0;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var FooterComponent = class {
|
|
186
|
+
elements;
|
|
187
|
+
constructor(tabela) {
|
|
188
|
+
this.tabela = tabela;
|
|
189
|
+
const { group, row } = createRowGroup();
|
|
190
|
+
this.elements = {
|
|
191
|
+
group,
|
|
192
|
+
row,
|
|
193
|
+
cells: []
|
|
194
|
+
};
|
|
195
|
+
group.className += " tabela__rowgroup-footer";
|
|
196
|
+
row.className += " tabela__row-footer";
|
|
197
|
+
}
|
|
198
|
+
destroy() {
|
|
199
|
+
this.elements.cells.length = 0;
|
|
200
|
+
this.elements.group = void 0;
|
|
201
|
+
this.elements.row = void 0;
|
|
202
|
+
}
|
|
203
|
+
update(columns) {
|
|
204
|
+
const { elements } = this;
|
|
205
|
+
const { length } = columns;
|
|
206
|
+
elements.cells.length = 0;
|
|
207
|
+
elements.row.innerHTML = "";
|
|
208
|
+
for (let index = 0; index < length; index += 1) {
|
|
209
|
+
const cell = createCell(columns[index].options.width ?? 4, false);
|
|
210
|
+
cell.className += " tabela__cell-footer";
|
|
211
|
+
cell.innerHTML = " ";
|
|
212
|
+
elements.cells.push(cell);
|
|
213
|
+
elements.row.append(cell);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
var HeaderComponent = class {
|
|
218
|
+
elements;
|
|
219
|
+
constructor(tabela) {
|
|
220
|
+
this.tabela = tabela;
|
|
221
|
+
const { group, row } = createRowGroup();
|
|
222
|
+
this.elements = {
|
|
223
|
+
group,
|
|
224
|
+
row
|
|
225
|
+
};
|
|
226
|
+
group.className += " tabela__rowgroup-header";
|
|
227
|
+
row.className += " tabela__row-header";
|
|
228
|
+
}
|
|
229
|
+
destroy() {
|
|
230
|
+
this.elements.group = void 0;
|
|
231
|
+
this.elements.row = void 0;
|
|
232
|
+
}
|
|
233
|
+
update(columns) {
|
|
234
|
+
this.elements.row.innerHTML = "";
|
|
235
|
+
this.elements.row.append(...columns.map((column) => column.element));
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
function createHeading(title, width) {
|
|
239
|
+
return createElement("div", {
|
|
240
|
+
className: "tabela__heading",
|
|
241
|
+
role: "columnheader",
|
|
242
|
+
textContent: title
|
|
243
|
+
}, { width: `${width}px` });
|
|
244
|
+
}
|
|
245
|
+
var ColumnComponent = class {
|
|
246
|
+
element;
|
|
247
|
+
options;
|
|
248
|
+
constructor(tabela, options) {
|
|
249
|
+
this.tabela = tabela;
|
|
250
|
+
const width = Number.parseInt(getComputedStyle(tabela.element).fontSize, 10) * (options.width ?? options.title.length * 1.5);
|
|
251
|
+
this.options = {
|
|
252
|
+
...options,
|
|
253
|
+
width
|
|
254
|
+
};
|
|
255
|
+
this.element = createHeading(options.title, width);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
var ColumnManager = class {
|
|
259
|
+
components = [];
|
|
260
|
+
constructor(tabela, columns) {
|
|
261
|
+
this.tabela = tabela;
|
|
262
|
+
this.set(columns);
|
|
263
|
+
}
|
|
264
|
+
destroy() {
|
|
265
|
+
this.components.length = 0;
|
|
266
|
+
}
|
|
267
|
+
remove(value) {
|
|
268
|
+
const { components, tabela } = this;
|
|
269
|
+
const fields = (Array.isArray(value) ? value : [value]).filter((item) => typeof item === "string");
|
|
270
|
+
const { length } = fields;
|
|
271
|
+
if (length === 0) return;
|
|
272
|
+
for (let fieldIndex = 0; fieldIndex < length; fieldIndex += 1) {
|
|
273
|
+
const componentIndex = components.findIndex((component) => component.options.field === fields[fieldIndex]);
|
|
274
|
+
if (componentIndex > -1) components.splice(componentIndex, 1);
|
|
275
|
+
}
|
|
276
|
+
tabela.components.header.update(components);
|
|
277
|
+
tabela.components.footer.update(components);
|
|
278
|
+
tabela.managers.virtualization.removeCells(fields);
|
|
279
|
+
}
|
|
280
|
+
set(columns) {
|
|
281
|
+
const { components, tabela } = this;
|
|
282
|
+
const { footer, header } = tabela.components;
|
|
283
|
+
components.splice(0, components.length, ...columns.map((column) => new ColumnComponent(tabela, column)));
|
|
284
|
+
header.update(components);
|
|
285
|
+
footer.update(components);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
function getArrayCallback(value) {
|
|
289
|
+
switch (typeof value) {
|
|
290
|
+
case "function": return value;
|
|
291
|
+
case "number":
|
|
292
|
+
case "string": return typeof value === "string" && value.includes(".") ? void 0 : (obj) => obj[value];
|
|
293
|
+
default: break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function getArrayCallbacks(bool, key, value) {
|
|
297
|
+
if (typeof bool === "function") return { bool };
|
|
298
|
+
return {
|
|
299
|
+
keyed: getArrayCallback(key),
|
|
300
|
+
value: getArrayCallback(value)
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function getMapValues(array, first, second, arrays) {
|
|
304
|
+
if (!Array.isArray(array)) return /* @__PURE__ */ new Map();
|
|
305
|
+
const { length } = array;
|
|
306
|
+
const callbacks = getArrayCallbacks(void 0, first, second);
|
|
307
|
+
const map = /* @__PURE__ */ new Map();
|
|
308
|
+
for (let index = 0; index < length; index += 1) {
|
|
309
|
+
const item = array[index];
|
|
310
|
+
const key = callbacks?.keyed?.(item, index, array) ?? index;
|
|
311
|
+
const value = callbacks?.value?.(item, index, array) ?? item;
|
|
312
|
+
if (arrays) {
|
|
313
|
+
const existing = map.get(key);
|
|
314
|
+
if (existing == null) map.set(key, [value]);
|
|
315
|
+
else existing.push(value);
|
|
316
|
+
} else map.set(key, value);
|
|
317
|
+
}
|
|
318
|
+
return map;
|
|
319
|
+
}
|
|
320
|
+
function toMap(array, first, second) {
|
|
321
|
+
return getMapValues(array, first, second, false);
|
|
322
|
+
}
|
|
323
|
+
toMap.arrays = toMapArrays;
|
|
324
|
+
function toMapArrays(array, first, second) {
|
|
325
|
+
return getMapValues(array, first, second, true);
|
|
326
|
+
}
|
|
327
|
+
var DataManager = class {
|
|
328
|
+
values;
|
|
329
|
+
get length() {
|
|
330
|
+
return this.values.keys.active?.length ?? this.values.keys.original.length;
|
|
331
|
+
}
|
|
332
|
+
constructor(tabela, values) {
|
|
333
|
+
this.tabela = tabela;
|
|
334
|
+
const mapped = toMap(values, tabela.key);
|
|
335
|
+
this.values = {
|
|
336
|
+
keys: { original: [...mapped.keys()] },
|
|
337
|
+
objects: {
|
|
338
|
+
mapped,
|
|
339
|
+
array: values
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
destroy() {
|
|
344
|
+
const { values } = this;
|
|
345
|
+
values.objects.mapped.clear();
|
|
346
|
+
values.keys.active = void 0;
|
|
347
|
+
values.keys.original.length = 0;
|
|
348
|
+
values.objects.array.length = 0;
|
|
349
|
+
}
|
|
350
|
+
update() {
|
|
351
|
+
this.tabela.components.body.elements.faker.style.height = `${this.length * this.tabela.managers.rows.height}px`;
|
|
352
|
+
this.tabela.managers.virtualization.update(true);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
function removeRow(row, pool) {
|
|
356
|
+
if (row.element != null) {
|
|
357
|
+
row.element.innerHTML = "";
|
|
358
|
+
pool.rows.push(row.element);
|
|
359
|
+
row.element.remove();
|
|
360
|
+
row.element = void 0;
|
|
361
|
+
}
|
|
362
|
+
row.cells = {};
|
|
363
|
+
}
|
|
364
|
+
function renderRow(tabela, pool, row) {
|
|
365
|
+
const element = row.element ?? pool.rows.shift() ?? createRow();
|
|
366
|
+
row.element = element;
|
|
367
|
+
element.dataset.key = String(row.key);
|
|
368
|
+
element.innerHTML = "";
|
|
369
|
+
const columns = tabela.managers.columns.components;
|
|
370
|
+
const { length } = columns;
|
|
371
|
+
const data = tabela.managers.data.values.objects.mapped.get(row.key);
|
|
372
|
+
if (data == null) return;
|
|
373
|
+
for (let index = 0; index < length; index += 1) {
|
|
374
|
+
const { options } = columns[index];
|
|
375
|
+
pool.cells[options.field] ??= [];
|
|
376
|
+
const cell = pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
|
|
377
|
+
cell.textContent = String(data[options.field]);
|
|
378
|
+
row.cells[options.field] = cell;
|
|
379
|
+
element.append(cell);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
var RowComponent = class {
|
|
383
|
+
cells = {};
|
|
384
|
+
element;
|
|
385
|
+
constructor(key) {
|
|
386
|
+
this.key = key;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
var RowManager = class {
|
|
390
|
+
components = /* @__PURE__ */ new Map();
|
|
391
|
+
height;
|
|
392
|
+
constructor(tabela, rowHeight) {
|
|
393
|
+
this.tabela = tabela;
|
|
394
|
+
this.height = rowHeight;
|
|
395
|
+
}
|
|
396
|
+
destroy() {
|
|
397
|
+
this.components.clear();
|
|
398
|
+
}
|
|
399
|
+
get(key) {
|
|
400
|
+
let row = this.components.get(key);
|
|
401
|
+
if (row == null) {
|
|
402
|
+
row = new RowComponent(key);
|
|
403
|
+
this.components.set(key, row);
|
|
404
|
+
}
|
|
405
|
+
return row;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
function addDelegatedHandler(doc, type, name, passive) {
|
|
409
|
+
if (DELEGATED.has(name)) return;
|
|
410
|
+
DELEGATED.add(name);
|
|
411
|
+
doc.addEventListener(type, passive ? HANDLER_PASSIVE : HANDLER_ACTIVE, { passive });
|
|
412
|
+
}
|
|
413
|
+
function addDelegatedListener(target, type, name, listener, passive) {
|
|
414
|
+
target[name] ??= /* @__PURE__ */ new Set();
|
|
415
|
+
target[name].add(listener);
|
|
416
|
+
addDelegatedHandler(document, type, name, passive);
|
|
417
|
+
return () => {
|
|
418
|
+
removeDelegatedListener(target, name, listener);
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function delegatedEventHandler(event) {
|
|
422
|
+
const key = `${EVENT_PREFIX}${event.type}${this ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
|
|
423
|
+
const items = event.composedPath();
|
|
424
|
+
const { length } = items;
|
|
425
|
+
let target = items[0];
|
|
426
|
+
Object.defineProperties(event, {
|
|
427
|
+
currentTarget: {
|
|
428
|
+
configurable: true,
|
|
429
|
+
get() {
|
|
430
|
+
return target;
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
target: {
|
|
434
|
+
configurable: true,
|
|
435
|
+
value: target
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
for (let index = 0; index < length; index += 1) {
|
|
439
|
+
const item = items[index];
|
|
440
|
+
const listeners = item[key];
|
|
441
|
+
if (item.disabled || listeners == null) continue;
|
|
442
|
+
target = item;
|
|
443
|
+
for (const listener of listeners) {
|
|
444
|
+
listener.call(item, event);
|
|
445
|
+
if (event.cancelBubble) return;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function getDelegatedName(target, type, options) {
|
|
450
|
+
if (isEventTarget(target) && EVENT_TYPES.has(type) && !options.capture && !options.once && options.signal == null) return `${EVENT_PREFIX}${type}${options.passive ? EVENT_SUFFIX_PASSIVE : EVENT_SUFFIX_ACTIVE}`;
|
|
451
|
+
}
|
|
452
|
+
function removeDelegatedListener(target, name, listener) {
|
|
453
|
+
const handlers = target[name];
|
|
454
|
+
if (handlers == null || !handlers.has(listener)) return false;
|
|
455
|
+
handlers.delete(listener);
|
|
456
|
+
if (handlers.size === 0) target[name] = void 0;
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
var DELEGATED = /* @__PURE__ */ new Set();
|
|
460
|
+
var EVENT_PREFIX = "@";
|
|
461
|
+
var EVENT_SUFFIX_ACTIVE = ":active";
|
|
462
|
+
var EVENT_SUFFIX_PASSIVE = ":passive";
|
|
463
|
+
var EVENT_TYPES = new Set([
|
|
464
|
+
"beforeinput",
|
|
465
|
+
"click",
|
|
466
|
+
"dblclick",
|
|
467
|
+
"contextmenu",
|
|
468
|
+
"focusin",
|
|
469
|
+
"focusout",
|
|
470
|
+
"input",
|
|
471
|
+
"keydown",
|
|
472
|
+
"keyup",
|
|
473
|
+
"mousedown",
|
|
474
|
+
"mousemove",
|
|
475
|
+
"mouseout",
|
|
476
|
+
"mouseover",
|
|
477
|
+
"mouseup",
|
|
478
|
+
"pointerdown",
|
|
479
|
+
"pointermove",
|
|
480
|
+
"pointerout",
|
|
481
|
+
"pointerover",
|
|
482
|
+
"pointerup",
|
|
483
|
+
"touchend",
|
|
484
|
+
"touchmove",
|
|
485
|
+
"touchstart"
|
|
486
|
+
]);
|
|
487
|
+
var HANDLER_ACTIVE = delegatedEventHandler.bind(false);
|
|
488
|
+
var HANDLER_PASSIVE = delegatedEventHandler.bind(true);
|
|
489
|
+
/**
|
|
490
|
+
* A function that does nothing, which can be useful, I guess…
|
|
491
|
+
*/
|
|
492
|
+
function noop() {}
|
|
493
|
+
function createEventOptions(options) {
|
|
494
|
+
return {
|
|
495
|
+
capture: getBoolean(options?.capture),
|
|
496
|
+
once: getBoolean(options?.once),
|
|
497
|
+
passive: getBoolean(options?.passive, true),
|
|
498
|
+
signal: options?.signal instanceof AbortSignal ? options.signal : void 0
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function on(target, type, listener, options) {
|
|
502
|
+
if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return noop;
|
|
503
|
+
const extended = createEventOptions(options);
|
|
504
|
+
const delegated = getDelegatedName(target, type, extended);
|
|
505
|
+
if (delegated != null) return addDelegatedListener(target, type, delegated, listener, extended.passive);
|
|
506
|
+
target.addEventListener(type, listener, extended);
|
|
507
|
+
if (extended.once) return noop;
|
|
508
|
+
return () => {
|
|
509
|
+
target.removeEventListener(type, listener, extended);
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function getRange(tabela, down) {
|
|
513
|
+
const { components, managers } = tabela;
|
|
514
|
+
const { body } = components;
|
|
515
|
+
const { data, rows } = managers;
|
|
516
|
+
const { clientHeight, scrollTop } = body.elements.group;
|
|
517
|
+
const first = Math.floor(scrollTop / rows.height);
|
|
518
|
+
const last = Math.min(data.length - 1, Math.ceil((scrollTop + clientHeight) / rows.height) - 1);
|
|
519
|
+
const before = Math.ceil(clientHeight / rows.height) * (down ? 1 : 2);
|
|
520
|
+
const after = Math.ceil(clientHeight / rows.height) * (down ? 2 : 1);
|
|
521
|
+
const start = Math.max(0, first - before);
|
|
522
|
+
return {
|
|
523
|
+
end: Math.min(data.length - 1, last + after),
|
|
524
|
+
start
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function onScroll() {
|
|
528
|
+
if (!this.state.active) {
|
|
529
|
+
requestAnimationFrame(() => {
|
|
530
|
+
const top = this.tabela.components.body.elements.group.scrollTop;
|
|
531
|
+
this.update(top > this.state.top);
|
|
532
|
+
this.state.active = false;
|
|
533
|
+
this.state.top = top;
|
|
534
|
+
});
|
|
535
|
+
this.state.active = true;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
var VirtualizationManager = class {
|
|
539
|
+
fragment;
|
|
540
|
+
listener;
|
|
541
|
+
pool = {
|
|
542
|
+
cells: {},
|
|
543
|
+
rows: []
|
|
544
|
+
};
|
|
545
|
+
state = {
|
|
546
|
+
active: false,
|
|
547
|
+
top: 0
|
|
548
|
+
};
|
|
549
|
+
visible = /* @__PURE__ */ new Map();
|
|
550
|
+
constructor(tabela) {
|
|
551
|
+
this.tabela = tabela;
|
|
552
|
+
this.listener = on(tabela.components.body.elements.group, "scroll", onScroll.bind(this));
|
|
553
|
+
}
|
|
554
|
+
destroy() {
|
|
555
|
+
this.listener();
|
|
556
|
+
for (const [index, row] of this.visible) {
|
|
557
|
+
removeRow(row, this.pool);
|
|
558
|
+
this.visible.delete(index);
|
|
559
|
+
}
|
|
560
|
+
this.pool.cells = {};
|
|
561
|
+
this.pool.rows = [];
|
|
562
|
+
}
|
|
563
|
+
removeCells(fields) {
|
|
564
|
+
const { length } = fields;
|
|
565
|
+
for (let index = 0; index < length; index += 1) delete this.pool.cells[fields[index]];
|
|
566
|
+
for (const [, row] of this.visible) for (let index = 0; index < length; index += 1) {
|
|
567
|
+
row.cells[fields[index]].innerHTML = "";
|
|
568
|
+
row.cells[fields[index]].remove();
|
|
569
|
+
delete row.cells[fields[index]];
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
update(down) {
|
|
573
|
+
const { tabela } = this;
|
|
574
|
+
const { rows } = tabela.managers;
|
|
575
|
+
const indices = /* @__PURE__ */ new Set();
|
|
576
|
+
const range = getRange(tabela, down);
|
|
577
|
+
for (let index = range.start; index <= range.end; index += 1) indices.add(index);
|
|
578
|
+
for (const [index, row] of this.visible) if (!indices.has(index)) {
|
|
579
|
+
this.visible.delete(index);
|
|
580
|
+
removeRow(row, this.pool);
|
|
581
|
+
}
|
|
582
|
+
this.fragment ??= document.createDocumentFragment();
|
|
583
|
+
this.fragment.replaceChildren();
|
|
584
|
+
const keys = tabela.managers.data.values.keys.active ?? tabela.managers.data.values.keys.original;
|
|
585
|
+
let count = 0;
|
|
586
|
+
for (let index = range.start; index <= range.end; index += 1) {
|
|
587
|
+
if (this.visible.has(index)) continue;
|
|
588
|
+
const row = tabela.managers.rows.get(keys[index]);
|
|
589
|
+
if (row == null) continue;
|
|
590
|
+
count += 1;
|
|
591
|
+
renderRow(tabela, this.pool, row);
|
|
592
|
+
this.visible.set(index, row);
|
|
593
|
+
if (row.element != null) {
|
|
594
|
+
row.element.style.transform = `translateY(${index * rows.height}px)`;
|
|
595
|
+
this.fragment.append(row.element);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (count > 0) tabela.components.body.elements.group[down ? "append" : "prepend"](this.fragment);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
var Tabela = class {
|
|
602
|
+
components;
|
|
603
|
+
key;
|
|
604
|
+
managers;
|
|
605
|
+
constructor(element, options) {
|
|
606
|
+
this.element = element;
|
|
607
|
+
element.innerHTML = "";
|
|
608
|
+
element.role = "table";
|
|
609
|
+
element.classList.add("tabela");
|
|
610
|
+
element.setAttribute("aria-label", options.label);
|
|
611
|
+
this.key = options.key;
|
|
612
|
+
this.components = {
|
|
613
|
+
header: new HeaderComponent(this),
|
|
614
|
+
body: new BodyComponent(this),
|
|
615
|
+
footer: new FooterComponent(this)
|
|
616
|
+
};
|
|
617
|
+
this.managers = {
|
|
618
|
+
columns: new ColumnManager(this, options.columns),
|
|
619
|
+
data: new DataManager(this, options.data),
|
|
620
|
+
rows: new RowManager(this, options.rowHeight),
|
|
621
|
+
virtualization: new VirtualizationManager(this)
|
|
622
|
+
};
|
|
623
|
+
element.append(this.components.header.elements.group, this.components.body.elements.group, this.components.footer.elements.group);
|
|
624
|
+
this.managers.data.update();
|
|
625
|
+
}
|
|
626
|
+
destroy() {
|
|
627
|
+
const { components, element, managers } = this;
|
|
628
|
+
components.body.destroy();
|
|
629
|
+
components.footer.destroy();
|
|
630
|
+
components.header.destroy();
|
|
631
|
+
managers.columns.destroy();
|
|
632
|
+
managers.data.destroy();
|
|
633
|
+
managers.rows.destroy();
|
|
634
|
+
element.innerHTML = "";
|
|
635
|
+
element.role = "";
|
|
636
|
+
element.classList.remove("tabela");
|
|
637
|
+
element.removeAttribute("aria-label");
|
|
638
|
+
element.removeAttribute("role");
|
|
639
|
+
this.element = void 0;
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
function tabela(element, options) {
|
|
643
|
+
return new Tabela(element, options);
|
|
644
|
+
}
|
|
645
|
+
export { tabela };
|