@oscarpalmer/tabela 0.9.0 → 0.11.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.
Files changed (60) hide show
  1. package/dist/components/body.component.js +3 -15
  2. package/dist/components/footer.component.js +3 -3
  3. package/dist/components/header.component.js +2 -2
  4. package/dist/components/row.component.js +19 -7
  5. package/dist/helpers/dom.helpers.js +5 -10
  6. package/dist/helpers/misc.helpers.js +7 -0
  7. package/dist/helpers/style.helper.js +6 -0
  8. package/dist/managers/column.manager.js +10 -9
  9. package/dist/managers/data.manager.js +26 -22
  10. package/dist/managers/event.manager.js +39 -19
  11. package/dist/managers/filter.manager.js +11 -10
  12. package/dist/managers/navigation.manager.js +73 -0
  13. package/dist/managers/render.manager.js +110 -0
  14. package/dist/managers/row.manager.js +7 -7
  15. package/dist/managers/selection.manager.js +189 -0
  16. package/dist/managers/sort.manager.js +9 -8
  17. package/dist/tabela.full.js +1096 -433
  18. package/dist/tabela.js +34 -11
  19. package/package.json +1 -1
  20. package/src/components/body.component.ts +5 -20
  21. package/src/components/footer.component.ts +3 -3
  22. package/src/components/header.component.ts +2 -2
  23. package/src/components/row.component.ts +30 -10
  24. package/src/helpers/dom.helpers.ts +3 -10
  25. package/src/helpers/misc.helpers.ts +15 -0
  26. package/src/helpers/style.helper.ts +6 -0
  27. package/src/managers/column.manager.ts +12 -14
  28. package/src/managers/data.manager.ts +31 -27
  29. package/src/managers/event.manager.ts +67 -37
  30. package/src/managers/filter.manager.ts +12 -11
  31. package/src/managers/navigation.manager.ts +145 -0
  32. package/src/managers/render.manager.ts +184 -0
  33. package/src/managers/row.manager.ts +9 -14
  34. package/src/managers/selection.manager.ts +332 -0
  35. package/src/managers/sort.manager.ts +14 -14
  36. package/src/models/render.model.ts +16 -0
  37. package/src/models/tabela.model.ts +23 -2
  38. package/src/tabela.ts +42 -10
  39. package/types/components/row.component.d.ts +4 -4
  40. package/types/helpers/dom.helpers.d.ts +1 -1
  41. package/types/helpers/misc.helpers.d.ts +2 -0
  42. package/types/helpers/style.helper.d.ts +1 -0
  43. package/types/managers/column.manager.d.ts +4 -5
  44. package/types/managers/data.manager.d.ts +5 -6
  45. package/types/managers/event.manager.d.ts +3 -6
  46. package/types/managers/filter.manager.d.ts +3 -3
  47. package/types/managers/navigation.manager.d.ts +10 -0
  48. package/types/managers/render.manager.d.ts +16 -0
  49. package/types/managers/row.manager.d.ts +4 -5
  50. package/types/managers/selection.manager.d.ts +18 -0
  51. package/types/managers/sort.manager.d.ts +4 -4
  52. package/types/models/render.model.d.ts +13 -0
  53. package/types/models/tabela.model.d.ts +21 -2
  54. package/types/tabela.d.ts +2 -1
  55. package/dist/managers/virtualization.manager.js +0 -101
  56. package/src/managers/virtualization.manager.ts +0 -176
  57. package/src/models/virtualization.model.ts +0 -14
  58. package/types/managers/virtualization.manager.d.ts +0 -18
  59. package/types/models/virtualization.model.d.ts +0 -12
  60. /package/dist/models/{virtualization.model.js → render.model.js} +0 -0
@@ -1,3 +1,14 @@
1
+ function compact$1(array, strict) {
2
+ if (!Array.isArray(array)) return [];
3
+ if (strict === true) return array.filter(Boolean);
4
+ const { length } = array;
5
+ const compacted = [];
6
+ for (let index = 0; index < length; index += 1) {
7
+ const item = array[index];
8
+ if (item != null) compacted.push(item);
9
+ }
10
+ return compacted;
11
+ }
1
12
  /**
2
13
  * Get the string value from any value
3
14
  * @param value Original value
@@ -12,6 +23,32 @@ function getString$1(value) {
12
23
  return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
13
24
  }
14
25
  /**
26
+ * Join an array of values into a string
27
+ * @param value Array of values
28
+ * @param delimiter Delimiter to use between values
29
+ * @returns Joined string
30
+ */
31
+ function join$1(value, delimiter) {
32
+ return compact$1(value).map(getString$1).join(typeof delimiter === "string" ? delimiter : "");
33
+ }
34
+ /**
35
+ * Split a string into words _(and other readable parts)_
36
+ * @param value Original string
37
+ * @returns Array of words found in the string
38
+ */
39
+ function words$1(value) {
40
+ return typeof value === "string" ? value.match(EXPRESSION_WORDS$1) ?? [] : [];
41
+ }
42
+ var EXPRESSION_WORDS$1 = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
43
+ /**
44
+ * Is the value a number?
45
+ * @param value Value to check
46
+ * @returns `true` if the value is a `number`, otherwise `false`
47
+ */
48
+ function isNumber$1(value) {
49
+ return typeof value === "number" && !Number.isNaN(value);
50
+ }
51
+ /**
15
52
  * Is the value a plain object?
16
53
  * @param value Value to check
17
54
  * @returns `true` if the value is a plain object, otherwise `false`
@@ -22,9 +59,237 @@ function isPlainObject$1(value) {
22
59
  const prototype = Object.getPrototypeOf(value);
23
60
  return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
24
61
  }
62
+ /**
63
+ * Clamp a number between a minimum and maximum value
64
+ * @param value Value to clamp
65
+ * @param minimum Minimum value
66
+ * @param maximum Maximum value
67
+ * @param loop If `true`, the value will loop around when smaller than the minimum or larger than the maximum _(defaults to `false`)_
68
+ * @returns Clamped value
69
+ */
70
+ function clamp$1(value, minimum, maximum, loop) {
71
+ if (![
72
+ value,
73
+ minimum,
74
+ maximum
75
+ ].every(isNumber$1)) return NaN;
76
+ if (value < minimum) return loop === true ? maximum : minimum;
77
+ return value > maximum ? loop === true ? minimum : maximum : value;
78
+ }
79
+ function getSizedMaximum$1(first, second) {
80
+ let actual;
81
+ if (typeof first === "number") actual = first;
82
+ else actual = typeof second === "number" ? second : MAXIMUM_DEFAULT$1;
83
+ return clamp$1(actual, 1, MAXIMUM_ABSOLUTE$1);
84
+ }
85
+ var MAXIMUM_ABSOLUTE$1 = 16777216;
86
+ var MAXIMUM_DEFAULT$1 = 1048576;
87
+ /**
88
+ * A Map with a maximum size
89
+ *
90
+ * Behavior is similar to a _LRU_-cache, where the least recently used entries are removed
91
+ */
92
+ var SizedMap$1 = class extends Map {
93
+ /**
94
+ * The maximum size of the Map
95
+ */
96
+ #maximumSize;
97
+ /**
98
+ * Is the Map full?
99
+ */
100
+ get full() {
101
+ return this.size >= this.#maximumSize;
102
+ }
103
+ get maximum() {
104
+ return this.#maximumSize;
105
+ }
106
+ constructor(first, second) {
107
+ const maximum = getSizedMaximum$1(first, second);
108
+ super();
109
+ this.#maximumSize = maximum;
110
+ if (Array.isArray(first)) {
111
+ const { length } = first;
112
+ if (length <= maximum) for (let index = 0; index < length; index += 1) this.set(...first[index]);
113
+ else for (let index = 0; index < maximum; index += 1) this.set(...first[length - maximum + index]);
114
+ }
115
+ }
116
+ /**
117
+ * @inheritdoc
118
+ */
119
+ get(key) {
120
+ const value = super.get(key);
121
+ if (value !== void 0 || this.has(key)) this.set(key, value);
122
+ return value;
123
+ }
124
+ /**
125
+ * @inheritdoc
126
+ */
127
+ set(key, value) {
128
+ if (this.has(key)) this.delete(key);
129
+ else if (this.size >= this.#maximumSize) this.delete(this.keys().next().value);
130
+ return super.set(key, value);
131
+ }
132
+ };
133
+ var Memoized$1 = class {
134
+ #state;
135
+ /**
136
+ * Maximum cache size
137
+ */
138
+ get maximum() {
139
+ return this.#state.cache?.maximum ?? NaN;
140
+ }
141
+ /**
142
+ * Current cache size
143
+ */
144
+ get size() {
145
+ return this.#state.cache?.size ?? NaN;
146
+ }
147
+ constructor(callback, options) {
148
+ const cache = new SizedMap$1(options.cacheSize);
149
+ const getter = (...parameters) => {
150
+ const key = options.cacheKey?.(...parameters) ?? (parameters.length === 1 ? parameters[0] : join$1(parameters.map(getString$1), "_"));
151
+ if (cache.has(key)) return cache.get(key);
152
+ const value = callback(...parameters);
153
+ cache.set(key, value);
154
+ return value;
155
+ };
156
+ this.#state = {
157
+ cache,
158
+ getter
159
+ };
160
+ }
161
+ /**
162
+ * Clear the cache
163
+ */
164
+ clear() {
165
+ this.#state.cache?.clear();
166
+ }
167
+ /**
168
+ * Delete a result from the cache
169
+ * @param key Key to delete
170
+ * @returns `true` if the key existed and was removed, otherwise `false`
171
+ */
172
+ delete(key) {
173
+ return this.#state.cache?.delete(key) ?? false;
174
+ }
175
+ /**
176
+ * Destroy the instance _(clearing its cache and removing its callback)_
177
+ */
178
+ destroy() {
179
+ this.#state.cache?.clear();
180
+ this.#state.cache = void 0;
181
+ this.#state.getter = void 0;
182
+ }
183
+ /**
184
+ * Get a result from the cache
185
+ * @param key Key to get
186
+ * @returns Cached result or `undefined` if it does not exist
187
+ */
188
+ get(key) {
189
+ return this.#state.cache?.get(key);
190
+ }
191
+ /**
192
+ * Does the result exist?
193
+ * @param key Key to check
194
+ * @returns `true` if the result exists, otherwise `false`
195
+ */
196
+ has(key) {
197
+ return this.#state.cache?.has(key) ?? false;
198
+ }
199
+ /**
200
+ * Run the callback with the provided parameters
201
+ * @param parameters Parameters to pass to the callback
202
+ * @returns Cached or computed _(then cached)_ result
203
+ */
204
+ run(...parameters) {
205
+ if (this.#state.cache == null || this.#state.getter == null) throw new Error("The Memoized instance has been destroyed");
206
+ return this.#state.getter(...parameters);
207
+ }
208
+ };
209
+ function getMemoizationOptions$1(input) {
210
+ const { cacheKey, cacheSize } = isPlainObject$1(input) ? input : {};
211
+ return {
212
+ cacheKey: typeof cacheKey === "function" ? cacheKey : void 0,
213
+ cacheSize: typeof cacheSize === "number" && cacheSize > 0 ? cacheSize : DEFAULT_CACHE_SIZE$1
214
+ };
215
+ }
216
+ /**
217
+ * Memoize a function, caching and retrieving results based on the first parameter
218
+ * @param callback Callback to memoize
219
+ * @param options Memoization options
220
+ * @returns Memoized instance
221
+ */
222
+ function memoize$1(callback, options) {
223
+ return new Memoized$1(callback, getMemoizationOptions$1(options));
224
+ }
225
+ var DEFAULT_CACHE_SIZE$1 = 1024;
226
+ /**
227
+ * Convert a string to camel case _(thisIsCamelCase)_
228
+ * @param value String to convert
229
+ * @returns Camel-cased string
230
+ */
231
+ function camelCase(value) {
232
+ return toCase("camel", value, true, false);
233
+ }
234
+ /**
235
+ * Capitalize the first letter of a string _(and lowercase the rest)_
236
+ * @param value String to capitalize
237
+ * @returns Capitalized string
238
+ */
239
+ function capitalize(value) {
240
+ if (typeof value !== "string" || value.length === 0) return "";
241
+ memoizedCapitalize ??= memoize$1((v) => v.length === 1 ? v.toLocaleUpperCase() : `${v.charAt(0).toLocaleUpperCase()}${v.slice(1).toLocaleLowerCase()}`);
242
+ return memoizedCapitalize.run(value);
243
+ }
244
+ function toCase(type, value, capitalizeAny, capitalizeFirst) {
245
+ caseMemoizers[type] ??= memoize$1(toCaseCallback.bind({
246
+ type,
247
+ capitalizeAny,
248
+ capitalizeFirst
249
+ }));
250
+ return caseMemoizers[type].run(value);
251
+ }
252
+ function toCaseCallback(value) {
253
+ if (typeof value !== "string") return "";
254
+ if (value.length < 1) return value;
255
+ const { capitalizeAny, capitalizeFirst, type } = this;
256
+ const parts = words$1(value);
257
+ const partsLength = parts.length;
258
+ const cased = [];
259
+ for (let partIndex = 0; partIndex < partsLength; partIndex += 1) {
260
+ const items = parts[partIndex].replace(EXPRESSION_ACRONYM, (full, one, two, three) => three === "s" ? full : `${one}-${two}${three}`).replace(EXPRESSION_CAMEL_CASE, REPLACEMENT_CAMEL_CASE).split("-");
261
+ const itemsLength = items.length;
262
+ const partResult = [];
263
+ let itemCount = 0;
264
+ for (let itemIndex = 0; itemIndex < itemsLength; itemIndex += 1) {
265
+ const item = items[itemIndex];
266
+ if (item.length === 0) continue;
267
+ if (!capitalizeAny || itemCount === 0 && partIndex === 0 && !capitalizeFirst) partResult.push(item.toLocaleLowerCase());
268
+ else partResult.push(capitalize(item));
269
+ itemCount += 1;
270
+ }
271
+ cased.push(join$1(partResult, delimiters[type]));
272
+ }
273
+ return join$1(cased, delimiters[type]);
274
+ }
275
+ var caseMemoizers = {};
276
+ var delimiters = {
277
+ camel: "",
278
+ kebab: "-",
279
+ pascal: "",
280
+ snake: "_"
281
+ };
282
+ var EXPRESSION_CAMEL_CASE = /(\p{Ll})(\p{Lu})/gu;
283
+ var EXPRESSION_ACRONYM = /(\p{Lu}*)(\p{Lu})(\p{Ll}+)/gu;
284
+ var REPLACEMENT_CAMEL_CASE = "$1-$2";
285
+ var memoizedCapitalize;
25
286
  function getBoolean(value, defaultValue) {
26
287
  return typeof value === "boolean" ? value : defaultValue ?? false;
27
288
  }
289
+ function getStyleValue(element, property, computed) {
290
+ const name = camelCase(property);
291
+ return computed ? getComputedStyle(element)[name] : element.style[name];
292
+ }
28
293
  /**
29
294
  * Is the value an event target?
30
295
  * @param value Value to check
@@ -53,10 +318,37 @@ new Set([
53
318
  * @param value Value to check
54
319
  * @returns `true` if the value is nullable or a whitespace-only string, otherwise `false`
55
320
  */
56
- function isNullableOrWhitespace(value) {
57
- return value == null || EXPRESSION_WHITESPACE$1.test(getString$1(value));
321
+ function isNullableOrWhitespace$1(value) {
322
+ return value == null || EXPRESSION_WHITESPACE$2.test(getString$1(value));
323
+ }
324
+ var EXPRESSION_WHITESPACE$2 = /^\s*$/;
325
+ function setElementValue(element, first, second, third, callback) {
326
+ if (!isHTMLOrSVGElement(element)) return;
327
+ if (typeof first === "string") setElementValues(element, first, second, third, callback);
328
+ else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback);
329
+ }
330
+ function setElementValues(element, first, second, third, callback) {
331
+ if (!isHTMLOrSVGElement(element)) return;
332
+ if (typeof first === "string") {
333
+ callback(element, first, second, third);
334
+ return;
335
+ }
336
+ const isArray = Array.isArray(first);
337
+ if (!isArray && !(typeof first === "object" && first !== null)) return;
338
+ const entries = isArray ? first : Object.entries(first).map(([name, value]) => ({
339
+ name,
340
+ value
341
+ }));
342
+ const { length } = entries;
343
+ for (let index = 0; index < length; index += 1) {
344
+ const entry = entries[index];
345
+ if (typeof entry === "object" && typeof entry?.name === "string") callback(element, entry.name, entry.value, third);
346
+ }
347
+ }
348
+ function updateElementValue(element, key, value, set, remove, isBoolean, json) {
349
+ if (isBoolean ? value == null : isNullableOrWhitespace$1(value)) remove.call(element, key);
350
+ else set.call(element, key, json ? JSON.stringify(value) : String(value));
58
351
  }
59
- var EXPRESSION_WHITESPACE$1 = /^\s*$/;
60
352
  function badAttributeHandler(name, value) {
61
353
  if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
62
354
  if (EXPRESSION_CLOBBERED_NAME.test(name) && (value in document || value in formElement) || EXPRESSION_EVENT_NAME.test(name)) return true;
@@ -86,7 +378,7 @@ function handleAttribute(callback, decode, first, second) {
86
378
  value = second;
87
379
  }
88
380
  if (decode && value != null) value = decodeAttribute(value);
89
- return callback(name, value?.replace(EXPRESSION_WHITESPACE, ""));
381
+ return callback(name, value?.replace(EXPRESSION_WHITESPACE$1, ""));
90
382
  }
91
383
  function isAttribute(value) {
92
384
  return value instanceof Attr || isPlainObject$1(value) && typeof value.name === "string" && "value" in value;
@@ -123,7 +415,7 @@ var EXPRESSION_SKIP_NAME = /^(aria-[-\w]+|data-[-\w.\u00B7-\uFFFF]+)$/i;
123
415
  var EXPRESSION_SOURCE_NAME = /^src$/i;
124
416
  var EXPRESSION_SOURCE_VALUE = /^data:/i;
125
417
  var EXPRESSION_URI_VALUE = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i;
126
- var EXPRESSION_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
418
+ var EXPRESSION_WHITESPACE$1 = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
127
419
  /**
128
420
  * List of boolean attributes
129
421
  */
@@ -165,32 +457,28 @@ var elementEvents = {
165
457
  };
166
458
  var formElement = document.createElement("form");
167
459
  var textArea;
168
- function setElementValue(element, first, second, third, callback) {
169
- if (!isHTMLOrSVGElement(element)) return;
170
- if (typeof first === "string") setElementValues(element, first, second, third, callback);
171
- else if (isAttribute(first)) setElementValues(element, first.name, first.value, third, callback);
460
+ function setAttribute(element, first, second, third) {
461
+ setElementValue(element, first, second, third, updateAttribute);
172
462
  }
173
- function setElementValues(element, first, second, third, callback) {
174
- if (!isHTMLOrSVGElement(element)) return;
175
- if (typeof first === "string") {
176
- callback(element, first, second, third);
177
- return;
178
- }
179
- const isArray = Array.isArray(first);
180
- if (!isArray && !(typeof first === "object" && first !== null)) return;
181
- const entries = isArray ? first : Object.entries(first).map(([name, value]) => ({
182
- name,
183
- value
184
- }));
185
- const { length } = entries;
463
+ function setAttributes(element, attributes, dispatch) {
464
+ setElementValues(element, attributes, null, dispatch, updateAttribute);
465
+ }
466
+ /**
467
+ * Get styles from an element
468
+ * @param element Element to get the styles from
469
+ * @param properties Styles to get
470
+ * @param computed Get the computed styles? _(defaults to `false`)_
471
+ * @returns Style values
472
+ */
473
+ function getStyles(element, properties, computed) {
474
+ const styles = {};
475
+ if (!(isHTMLOrSVGElement(element) && Array.isArray(properties))) return styles;
476
+ const { length } = properties;
186
477
  for (let index = 0; index < length; index += 1) {
187
- const entry = entries[index];
188
- if (typeof entry === "object" && typeof entry?.name === "string") callback(element, entry.name, entry.value, third);
478
+ const property = properties[index];
479
+ if (typeof property === "string") styles[property] = getStyleValue(element, property, computed === true);
189
480
  }
190
- }
191
- function updateElementValue(element, key, value, set, remove, isBoolean, json) {
192
- if (isBoolean ? value == null : isNullableOrWhitespace(value)) remove.call(element, key);
193
- else set.call(element, key, json ? JSON.stringify(value) : String(value));
481
+ return styles;
194
482
  }
195
483
  /**
196
484
  * Set styles on an element
@@ -200,6 +488,38 @@ function updateElementValue(element, key, value, set, remove, isBoolean, json) {
200
488
  function setStyles(element, styles) {
201
489
  setElementValues(element, styles, null, null, updateStyleProperty);
202
490
  }
491
+ /**
492
+ * Toggle styles for an element
493
+ * @param element Element to style
494
+ * @param styles Styles to be set or removed
495
+ * @returns Style toggler
496
+ */
497
+ function toggleStyles(element, styles) {
498
+ function toggle(set) {
499
+ hasSet = set;
500
+ let next;
501
+ if (set) {
502
+ values = getStyles(element, keys);
503
+ next = styles;
504
+ } else {
505
+ next = { ...values };
506
+ values = {};
507
+ for (const key of keys) values[key] = void 0;
508
+ }
509
+ setStyles(element, next);
510
+ }
511
+ const keys = Object.keys(styles);
512
+ let hasSet = false;
513
+ let values = {};
514
+ return {
515
+ set() {
516
+ if (!hasSet) toggle(true);
517
+ },
518
+ remove() {
519
+ if (hasSet) toggle(false);
520
+ }
521
+ };
522
+ }
203
523
  function updateStyleProperty(element, key, value) {
204
524
  updateElementValue(element, key, value, function(property, style) {
205
525
  this.style[property] = style;
@@ -207,18 +527,12 @@ function updateStyleProperty(element, key, value) {
207
527
  this.style[property] = "";
208
528
  }, false, false);
209
529
  }
210
- function setAttribute(element, first, second, third) {
211
- setElementValue(element, first, second, third, updateAttribute);
212
- }
213
- function setAttributes(element, attributes, dispatch) {
214
- setElementValues(element, attributes, null, dispatch, updateAttribute);
215
- }
216
530
  function createCell(width, body) {
217
531
  const cell = createElement("div", {
218
532
  className: "tabela__cell",
219
533
  role: "cell"
220
534
  }, {}, { width: `${width}px` });
221
- if (body ?? true) cell.classList.add("tabela__cell-body");
535
+ if (body ?? true) cell.classList.add("tabela__cell--body");
222
536
  return cell;
223
537
  }
224
538
  function createElement(tagName, properties, attributes, style) {
@@ -235,33 +549,21 @@ function createRowGroup(withRow) {
235
549
  role: "rowgroup"
236
550
  }, {}, {});
237
551
  if (!(withRow ?? true)) return group;
238
- const row = createRow(false);
552
+ const row = createRow();
239
553
  group.append(row);
240
554
  return {
241
555
  group,
242
556
  row
243
557
  };
244
558
  }
245
- function createRow(withStyle) {
246
- const row = createElement("div", {
559
+ function createRow() {
560
+ return createElement("div", {
247
561
  className: "tabela__row",
248
562
  role: "row"
249
563
  }, {}, {});
250
- if (withStyle ?? true) setStyles(row, {
251
- inset: "0 auto auto 0",
252
- position: "absolute"
253
- });
254
- return row;
255
564
  }
256
565
  function createFaker() {
257
- return createElement("div", {}, {}, {
258
- height: "0",
259
- inset: "0 auto auto 0",
260
- opacity: "0",
261
- pointerEvents: "none",
262
- position: "absolute",
263
- width: "1px"
264
- });
566
+ return createElement("div", { className: "tabela__faker" }, {}, {});
265
567
  }
266
568
  var BodyComponent = class {
267
569
  elements = {
@@ -271,13 +573,9 @@ var BodyComponent = class {
271
573
  constructor() {
272
574
  const group = createRowGroup(false);
273
575
  this.elements.group = group;
274
- group.className += " tabela__rowgroup-body";
576
+ group.className += " tabela__rowgroup--body";
275
577
  group.tabIndex = 0;
276
- setStyles(group, {
277
- height: "100%",
278
- overflow: "auto",
279
- position: "relative"
280
- });
578
+ group.setAttribute("data-event", "body");
281
579
  group.append(this.elements.faker);
282
580
  }
283
581
  destroy() {
@@ -294,8 +592,8 @@ var FooterComponent = class {
294
592
  row,
295
593
  cells: []
296
594
  };
297
- group.className += " tabela__rowgroup-footer";
298
- row.className += " tabela__row-footer";
595
+ group.className += " tabela__rowgroup--footer";
596
+ row.className += " tabela__row--footer";
299
597
  }
300
598
  destroy() {
301
599
  this.elements.cells.length = 0;
@@ -309,7 +607,7 @@ var FooterComponent = class {
309
607
  elements.row.innerHTML = "";
310
608
  for (let index = 0; index < length; index += 1) {
311
609
  const cell = createCell(columns[index].options.width ?? 4, false);
312
- cell.className += " tabela__cell-footer";
610
+ cell.className += " tabela__cell--footer";
313
611
  cell.innerHTML = "&nbsp;";
314
612
  elements.cells.push(cell);
315
613
  elements.row.append(cell);
@@ -324,8 +622,8 @@ var HeaderComponent = class {
324
622
  group,
325
623
  row
326
624
  };
327
- group.className += " tabela__rowgroup-header";
328
- row.className += " tabela__row-header";
625
+ group.className += " tabela__rowgroup--header";
626
+ row.className += " tabela__row--header";
329
627
  }
330
628
  destroy() {
331
629
  this.elements.group = void 0;
@@ -377,18 +675,19 @@ function createHeading(field, title, width) {
377
675
  }
378
676
  var ColumnManager = class {
379
677
  items = [];
380
- constructor(managers, components, columns) {
381
- this.managers = managers;
382
- this.components = components;
383
- this.set(columns);
678
+ constructor(state) {
679
+ this.state = state;
680
+ this.set(state.options.columns);
384
681
  }
385
682
  destroy() {
386
683
  const { length } = this.items;
387
684
  for (let index = 0; index < length; index += 1) this.items[index].destroy();
388
- this.items.length = 0;
685
+ this.items = void 0;
686
+ this.state = void 0;
389
687
  }
390
688
  remove(value) {
391
- const { components, items, managers } = this;
689
+ const { items, state } = this;
690
+ const { components, managers } = state;
392
691
  const fields = (Array.isArray(value) ? value : [value]).filter((item) => typeof item === "string");
393
692
  const { length } = fields;
394
693
  if (length === 0) return;
@@ -401,11 +700,11 @@ var ColumnManager = class {
401
700
  }
402
701
  components.header.update(items);
403
702
  components.footer.update(items);
404
- managers.virtualization.removeCells(fields);
703
+ managers.render.removeCells(fields);
405
704
  }
406
705
  set(columns) {
407
- const { components, items } = this;
408
- const { footer, header } = components;
706
+ const { items, state } = this;
707
+ const { footer, header } = state.components;
409
708
  items.splice(0, items.length, ...columns.map((column) => new ColumnComponent(column)));
410
709
  header.update(items);
411
710
  footer.update(items);
@@ -485,6 +784,14 @@ function isConstructor(value) {
485
784
  return typeof value === "function" && value.prototype?.constructor === value;
486
785
  }
487
786
  /**
787
+ * Is the value a key?
788
+ * @param value Value to check
789
+ * @returns `true` if the value is a `Key` _(`number` or `string`)_, otherwise `false`
790
+ */
791
+ function isKey(value) {
792
+ return typeof value === "number" || typeof value === "string";
793
+ }
794
+ /**
488
795
  * Is the value a number?
489
796
  * @param value Value to check
490
797
  * @returns `true` if the value is a `number`, otherwise `false`
@@ -776,7 +1083,7 @@ function getCallback(value, key, forObject) {
776
1083
  if (forObject && typeof value.value === "function") return value.value;
777
1084
  return typeof value === "function" ? value : void 0;
778
1085
  }
779
- function getKey(value, forObject) {
1086
+ function getKey$1(value, forObject) {
780
1087
  if (forObject && typeof value.key === "string") return value.key;
781
1088
  return typeof value === "string" ? value : void 0;
782
1089
  }
@@ -792,7 +1099,7 @@ function getSorter(value, modifier) {
792
1099
  modifier
793
1100
  };
794
1101
  sorter.compare = forObject && typeof value.compare === "function" ? value.compare : void 0;
795
- sorter.key = getKey(value, forObject);
1102
+ sorter.key = getKey$1(value, forObject);
796
1103
  sorter.modifier = getModifier(value, modifier, forObject);
797
1104
  sorter.callback = getCallback(value, sorter.key, forObject);
798
1105
  if (sorter.key != null || sorter.callback != null) {
@@ -852,6 +1159,15 @@ toMap.arrays = toMapArrays;
852
1159
  function toMapArrays(array, first, second) {
853
1160
  return getMapValues(array, first, second, true);
854
1161
  }
1162
+ /**
1163
+ * Is the value `undefined`, `null`, or a whitespace-only string?
1164
+ * @param value Value to check
1165
+ * @returns `true` if the value is nullable or a whitespace-only string, otherwise `false`
1166
+ */
1167
+ function isNullableOrWhitespace(value) {
1168
+ return value == null || EXPRESSION_WHITESPACE.test(getString(value));
1169
+ }
1170
+ var EXPRESSION_WHITESPACE = /^\s*$/;
855
1171
  var DataManager = class {
856
1172
  handlers = Object.freeze({
857
1173
  add: (data) => void this.add(data, true),
@@ -871,15 +1187,13 @@ var DataManager = class {
871
1187
  get size() {
872
1188
  return this.values.keys.active?.length ?? this.values.keys.original.length;
873
1189
  }
874
- constructor(managers, components, field) {
875
- this.managers = managers;
876
- this.components = components;
877
- this.field = field;
1190
+ constructor(state) {
1191
+ this.state = state;
878
1192
  }
879
1193
  async add(data, render) {
880
- const { field, values } = this;
1194
+ const { state, values } = this;
881
1195
  push(values.objects.array, data);
882
- values.objects.mapped = toMap(values.objects.array, field);
1196
+ values.objects.mapped = toMap(values.objects.array, state.key);
883
1197
  if (render) this.render();
884
1198
  }
885
1199
  clear() {
@@ -892,48 +1206,54 @@ var DataManager = class {
892
1206
  values.keys.original.length = 0;
893
1207
  values.objects.array.length = 0;
894
1208
  this.handlers = void 0;
1209
+ this.state = void 0;
1210
+ this.values = void 0;
895
1211
  }
896
1212
  get(active) {
897
1213
  const { values } = this;
898
1214
  return active ?? false ? values.keys.active?.map((key) => values.objects.mapped.get(key)) ?? [] : values.objects.array;
899
1215
  }
1216
+ getIndex(key) {
1217
+ const { values } = this;
1218
+ return (values.keys.active ?? values.keys.original).indexOf(key);
1219
+ }
900
1220
  async remove(items, render) {
901
- const { field, managers, values } = this;
902
- const keys = items.map((value) => isPlainObject(value) ? value[field] : value).filter((key) => values.objects.mapped.has(key));
1221
+ const { state, values } = this;
1222
+ const keys = items.map((value) => isPlainObject(value) ? value[state.key] : value).filter((key) => values.objects.mapped.has(key));
903
1223
  const { length } = keys;
904
1224
  if (length === 0) return;
905
1225
  for (let keyIndex = 0; keyIndex < length; keyIndex += 1) {
906
1226
  const key = keys[keyIndex];
907
1227
  values.objects.mapped.delete(key);
908
- const arrayIndex = values.objects.array.findIndex((object) => object[field] === key);
1228
+ const arrayIndex = values.objects.array.findIndex((object) => object[state.key] === key);
909
1229
  if (arrayIndex > -1) values.objects.array.splice(arrayIndex, 1);
910
1230
  values.keys.original.splice(values.keys.original.indexOf(key), 1);
911
- managers.row.remove(key);
1231
+ state.managers.row.remove(key);
912
1232
  }
913
1233
  if (render) this.render();
914
1234
  }
915
1235
  render() {
916
- const { field, managers, values } = this;
917
- values.keys.original = sort(values.objects.array.map((item) => item[field]));
918
- if (Object.keys(managers.filter.items).length > 0) managers.filter.filter();
919
- else if (managers.sort.items.length > 0) managers.sort.sort();
920
- else managers.virtualization.update(true);
1236
+ const { state, values } = this;
1237
+ values.keys.original = sort(values.objects.array.map((item) => item[state.key]));
1238
+ if (Object.keys(state.managers.filter.items).length > 0) state.managers.filter.filter();
1239
+ else if (state.managers.sort.items.length > 0) state.managers.sort.sort();
1240
+ else state.managers.render.update(true);
921
1241
  }
922
1242
  set(data) {
923
- const { field, values } = this;
924
- values.objects.mapped = toMap(data, field);
1243
+ const { state, values } = this;
1244
+ values.objects.mapped = toMap(data, state.key);
925
1245
  values.objects.array = data;
926
1246
  this.render();
927
1247
  }
928
1248
  async synchronize(data, remove) {
929
- const { field, values } = this;
1249
+ const { state, values } = this;
930
1250
  const add = [];
931
1251
  const updated = [];
932
1252
  const keys = /* @__PURE__ */ new Set([]);
933
1253
  const { length } = data;
934
1254
  for (let index = 0; index < length; index += 1) {
935
1255
  const object = data[index];
936
- const key = object[field];
1256
+ const key = object[state.key];
937
1257
  if (values.objects.mapped.has(key)) updated.push(object);
938
1258
  else add.push(object);
939
1259
  keys.add(key);
@@ -948,18 +1268,18 @@ var DataManager = class {
948
1268
  if (add.length > 0 || (remove ?? false)) this.render();
949
1269
  }
950
1270
  async update(data) {
951
- const { field, managers, values } = this;
1271
+ const { state, values } = this;
952
1272
  const { length } = data;
953
1273
  for (let index = 0; index < length; index += 1) {
954
1274
  const object = data[index];
955
- const key = object[field];
1275
+ const key = object[state.key];
956
1276
  const value = values.objects.mapped.get(key);
957
1277
  if (value != null) {
958
1278
  values.objects.mapped.set(key, {
959
1279
  ...value,
960
1280
  ...object
961
1281
  });
962
- managers.row.update(key);
1282
+ state.managers.row.update(key);
963
1283
  }
964
1284
  }
965
1285
  }
@@ -1057,6 +1377,26 @@ function createEventOptions(options) {
1057
1377
  signal: options?.signal instanceof AbortSignal ? options.signal : void 0
1058
1378
  };
1059
1379
  }
1380
+ /**
1381
+ * Get the X- and Y-coordinates from a pointer event
1382
+ * @param event Pointer event
1383
+ * @returns X- and Y-coordinates
1384
+ */
1385
+ function getPosition(event) {
1386
+ let x;
1387
+ let y;
1388
+ if (event instanceof MouseEvent) {
1389
+ x = event.clientX;
1390
+ y = event.clientY;
1391
+ } else if (event instanceof TouchEvent) {
1392
+ x = event.touches[0]?.clientX;
1393
+ y = event.touches[0]?.clientY;
1394
+ }
1395
+ return typeof x === "number" && typeof y === "number" ? {
1396
+ x,
1397
+ y
1398
+ } : void 0;
1399
+ }
1060
1400
  function on(target, type, listener, options) {
1061
1401
  if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return noop;
1062
1402
  const extended = createEventOptions(options);
@@ -1088,199 +1428,53 @@ function getElement(origin) {
1088
1428
  if (origin instanceof Element) return origin;
1089
1429
  return origin instanceof Event && origin.target instanceof Element ? origin.target : void 0;
1090
1430
  }
1091
- function isInert(item) {
1092
- return (item.element.inert ?? false) || EXPRESSION_TRUEISH.test(item.element.getAttribute(ATTRIBUTE_INERT)) || item.element.parentElement != null && isInert({
1093
- element: item.element.parentElement,
1094
- tabIndex: TABINDEX_DEFAULT
1095
- });
1096
- }
1097
- var ATTRIBUTE_INERT = "inert";
1098
- var EXPRESSION_TRUEISH = /^(|true)$/i;
1099
- [
1100
- "[contenteditable]:not([contenteditable=\"false\"])",
1101
- "[tabindex]:not(slot)",
1102
- "a[href]",
1103
- "audio[controls]",
1104
- "button",
1105
- "details",
1106
- "details > summary:first-of-type",
1107
- "input",
1108
- "select",
1109
- "textarea",
1110
- "video[controls]"
1111
- ].map((selector) => `${selector}:not([inert])`).join(",");
1112
- var TABINDEX_DEFAULT = -1;
1113
- function handleElement(element, depth) {
1114
- if (depth === 0) {
1115
- const removable = element.querySelectorAll(REMOVE_SELECTOR);
1116
- for (const item of removable) item.remove();
1117
- }
1118
- sanitizeAttributes(element, [...element.attributes]);
1119
- }
1120
- /**
1121
- * Is the element clobbered?
1122
- *
1123
- * Thanks, DOMPurify _(https://github.com/cure53/DOMPurify)_
1124
- */
1125
- function isClobbered(value) {
1126
- return value instanceof HTMLFormElement && (typeof value.nodeName !== "string" || typeof value.textContent !== "string" || typeof value.removeChild !== "function" || !(value.attributes instanceof NamedNodeMap) || typeof value.removeAttribute !== "function" || typeof value.setAttribute !== "function" || typeof value.namespaceURI !== "string" || typeof value.insertBefore !== "function" || typeof value.hasChildNodes !== "function");
1127
- }
1128
- function removeNode(node) {
1129
- if (typeof node.remove === "function") node.remove();
1130
- }
1131
- function sanitizeAttributes(element, attributes) {
1132
- const { length } = attributes;
1133
- for (let index = 0; index < length; index += 1) {
1134
- const { name, value } = attributes[index];
1135
- if (_isBadAttribute(name, value, false) || _isEmptyNonBooleanAttribute(name, value, false)) element.removeAttribute(name);
1136
- else if (_isInvalidBooleanAttribute(name, value, false)) setAttribute(element, name, true);
1137
- }
1138
- }
1139
- function sanitizeNodes(nodes, depth) {
1140
- const actual = nodes.filter((node) => node instanceof Node);
1141
- let { length } = nodes;
1142
- for (let index = 0; index < length; index += 1) {
1143
- const node = actual[index];
1144
- let remove = isClobbered(node);
1145
- if (!remove) switch (node.nodeType) {
1146
- case Node.ELEMENT_NODE:
1147
- handleElement(node, depth);
1148
- break;
1149
- case Node.COMMENT_NODE:
1150
- remove = COMMENT_HARMFUL.test(node.data);
1151
- break;
1152
- case Node.DOCUMENT_TYPE_NODE:
1153
- case Node.PROCESSING_INSTRUCTION_NODE:
1154
- remove = true;
1155
- break;
1156
- }
1157
- if (remove) {
1158
- removeNode(node);
1159
- actual.splice(index, 1);
1160
- index -= 1;
1161
- length -= 1;
1162
- continue;
1163
- }
1164
- if (node.hasChildNodes()) sanitizeNodes([...node.childNodes], depth + 1);
1165
- }
1166
- return nodes;
1167
- }
1168
- var COMMENT_HARMFUL = /<[/\w]/g;
1169
- var REMOVE_SELECTOR = "script, toretto-temporary";
1170
- function createHtml(value) {
1171
- const parsed = getParser().parseFromString(getHtml(value), PARSE_TYPE_HTML);
1172
- parsed.body.normalize();
1173
- sanitizeNodes([parsed.body], 0);
1174
- return parsed.body.innerHTML;
1175
- }
1176
- function createTemplate(value, options) {
1177
- const template = document.createElement(TEMPLATE_TAG);
1178
- template.innerHTML = createHtml(value);
1179
- if (typeof value === "string" && options.cache) templates[value] = template;
1180
- return template;
1181
- }
1182
- function getHtml(value) {
1183
- return `${TEMPORARY_ELEMENT}${typeof value === "string" ? value : value.innerHTML}${TEMPORARY_ELEMENT}`;
1184
- }
1185
- function getNodes(value, options) {
1186
- if (typeof value !== "string" && !(value instanceof HTMLTemplateElement)) return [];
1187
- const template = getTemplate(value, options);
1188
- return template == null ? [] : [...template.content.cloneNode(true).childNodes];
1189
- }
1190
- function getOptions(input) {
1191
- const options = isPlainObject$1(input) ? input : {};
1192
- options.cache = typeof options.cache === "boolean" ? options.cache : true;
1193
- return options;
1194
- }
1195
- function getParser() {
1196
- parser ??= new DOMParser();
1197
- return parser;
1198
- }
1199
- function getTemplate(value, options) {
1200
- if (value instanceof HTMLTemplateElement) return createTemplate(value, options);
1201
- if (value.trim().length === 0) return;
1202
- let template = templates[value];
1203
- if (template != null) return template;
1204
- const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
1205
- return createTemplate(element instanceof HTMLTemplateElement ? element : value, options);
1206
- }
1207
- var html = ((value, options) => {
1208
- return getNodes(value, getOptions(options));
1209
- });
1210
- html.clear = () => {
1211
- templates = {};
1212
- };
1213
- html.remove = (template) => {
1214
- if (typeof template !== "string" || templates[template] == null) return;
1215
- const keys = Object.keys(templates);
1216
- const { length } = keys;
1217
- const updated = {};
1218
- for (let index = 0; index < length; index += 1) {
1219
- const key = keys[index];
1220
- if (key !== template) updated[key] = templates[key];
1221
- }
1222
- templates = updated;
1223
- };
1224
- var EXPRESSION_ID = /^[a-z][\w-]*$/i;
1225
- var PARSE_TYPE_HTML = "text/html";
1226
- var TEMPLATE_TAG = "template";
1227
- var TEMPORARY_ELEMENT = "<toretto-temporary></toretto-temporary>";
1228
- var parser;
1229
- var templates = {};
1230
- function getSupport() {
1231
- if (window == null || navigator == null) return false;
1232
- if ("matchMedia" in window) {
1233
- const media = matchMedia?.("(pointer: coarse)");
1234
- if (typeof media?.matches === "boolean" && media.matches) return true;
1235
- }
1236
- if ("ontouchstart" in window) return true;
1237
- if (typeof navigator.maxTouchPoints === "number" && navigator.maxTouchPoints > 0) return true;
1238
- if (typeof navigator.msMaxTouchPoints === "number" && navigator.msMaxTouchPoints > 0) return true;
1239
- return false;
1240
- }
1241
- (() => {
1242
- let support = getSupport();
1243
- const instance = Object.create({
1244
- get() {
1245
- return support;
1246
- },
1247
- update() {
1248
- support = getSupport();
1249
- return support;
1250
- }
1251
- });
1252
- Object.defineProperty(instance, "value", { get() {
1253
- return support;
1254
- } });
1255
- return instance;
1256
- })();
1257
1431
  var EventManager = class {
1258
- listener;
1259
- constructor(managers, element) {
1260
- this.managers = managers;
1261
- this.listener = on(element, "click", (event) => {
1262
- this.onClick(event);
1263
- }, { passive: false });
1432
+ constructor(state) {
1433
+ this.state = state;
1434
+ mapped$1.set(state.element, this);
1264
1435
  }
1265
1436
  destroy() {
1266
- this.listener();
1267
- }
1268
- onClick(event) {
1269
- const target = findAncestor(event, "[data-event]");
1270
- if (!(target instanceof HTMLElement)) return;
1271
- switch (target?.getAttribute("data-event")) {
1272
- case "heading":
1273
- this.onSort(event, target);
1274
- break;
1275
- }
1437
+ mapped$1.delete(this.state.element);
1438
+ this.state = void 0;
1276
1439
  }
1277
1440
  onSort(event, target) {
1278
- const { managers } = this;
1279
1441
  const direction = target.getAttribute("data-sort-direction");
1280
1442
  const field = target.getAttribute("data-field");
1281
- if (field != null) managers.sort.toggle(event, field, direction);
1443
+ if (field != null) this.state.managers.sort.toggle(event, field, direction);
1282
1444
  }
1283
1445
  };
1446
+ function onClick(event) {
1447
+ const target = findAncestor(event, "[data-event]");
1448
+ const table = findAncestor(event, ".tabela");
1449
+ if (!(target instanceof HTMLElement) || !(table instanceof HTMLElement)) return;
1450
+ const manager = mapped$1.get(table);
1451
+ if (manager == null) return;
1452
+ switch (target?.getAttribute("data-event")) {
1453
+ case "heading":
1454
+ manager.onSort(event, target);
1455
+ break;
1456
+ case "row":
1457
+ manager.state.managers.selection.handle(event, target);
1458
+ break;
1459
+ default: break;
1460
+ }
1461
+ }
1462
+ function onKeydown(event) {
1463
+ const target = findAncestor(event, "[data-event]");
1464
+ const table = findAncestor(event, ".tabela");
1465
+ if (!(target instanceof HTMLElement) || !(table instanceof HTMLElement)) return;
1466
+ const manager = mapped$1.get(table);
1467
+ if (manager == null) return;
1468
+ switch (target?.getAttribute("data-event")) {
1469
+ case "body":
1470
+ manager.state.managers.navigation.handle(event);
1471
+ break;
1472
+ default: break;
1473
+ }
1474
+ }
1475
+ const mapped$1 = /* @__PURE__ */ new WeakMap();
1476
+ on(document, "click", onClick);
1477
+ on(document, "keydown", onKeydown, { passive: false });
1284
1478
  /**
1285
1479
  * Clamp a number between a minimum and maximum value
1286
1480
  * @param value Value to clamp
@@ -1685,8 +1879,8 @@ var FilterManager = class {
1685
1879
  set: (items) => this.set(items)
1686
1880
  });
1687
1881
  items = {};
1688
- constructor(managers) {
1689
- this.managers = managers;
1882
+ constructor(state) {
1883
+ this.state = state;
1690
1884
  }
1691
1885
  add(item) {
1692
1886
  if (this.items[item.field] == null) this.items[item.field] = [];
@@ -1702,16 +1896,17 @@ var FilterManager = class {
1702
1896
  }
1703
1897
  destroy() {
1704
1898
  this.handlers = void 0;
1705
- this.items = {};
1899
+ this.items = void 0;
1900
+ this.state = void 0;
1706
1901
  }
1707
1902
  filter() {
1708
- const { managers } = this;
1903
+ const { state } = this;
1709
1904
  const filtered = [];
1710
1905
  const filters = Object.entries(this.items);
1711
- const keysLength = managers.data.values.keys.original.length;
1906
+ const keysLength = state.managers.data.values.keys.original.length;
1712
1907
  rowLoop: for (let keyIndex = 0; keyIndex < keysLength; keyIndex += 1) {
1713
- const key = managers.data.values.keys.original[keyIndex];
1714
- const row = managers.data.values.objects.mapped.get(key);
1908
+ const key = state.managers.data.values.keys.original[keyIndex];
1909
+ const row = state.managers.data.values.objects.mapped.get(key);
1715
1910
  if (row == null) continue;
1716
1911
  filterLoop: for (let filterIndex = 0; filterIndex < filters.length; filterIndex += 1) {
1717
1912
  const [field, items] = filters[filterIndex];
@@ -1724,9 +1919,9 @@ var FilterManager = class {
1724
1919
  }
1725
1920
  filtered.push(key);
1726
1921
  }
1727
- managers.data.values.keys.active = filtered;
1728
- if (managers.sort.items.length > 0) managers.sort.sort();
1729
- else managers.virtualization.update(true, true);
1922
+ state.managers.data.values.keys.active = filtered;
1923
+ if (state.managers.sort.items.length > 0) state.managers.sort.sort();
1924
+ else state.managers.render.update(true, true);
1730
1925
  }
1731
1926
  remove(value) {
1732
1927
  if (typeof value === "string") {
@@ -1764,6 +1959,81 @@ const comparators = {
1764
1959
  "starts-with": (row, filter) => startsWith(getString(row), getString(filter), true)
1765
1960
  };
1766
1961
  const equalizer = equal.initialize({ ignoreCase: true });
1962
+ function getKey(value) {
1963
+ if (typeof value === "number") return value;
1964
+ if (typeof value !== "string") return;
1965
+ return integerExpression.test(value) ? Number.parseInt(value, 10) : value;
1966
+ }
1967
+ const integerExpression = /^\d+$/;
1968
+ var NavigationManager = class {
1969
+ active;
1970
+ constructor(state) {
1971
+ this.state = state;
1972
+ }
1973
+ destroy() {
1974
+ this.state = void 0;
1975
+ }
1976
+ handle(event) {
1977
+ if (!allKeys.has(event.key)) return;
1978
+ event.preventDefault();
1979
+ const { components, id, managers } = this.state;
1980
+ const activeDescendant = components.body.elements.group.getAttribute("aria-activedescendant");
1981
+ const keys = managers.data.values.keys.active ?? managers.data.values.keys.original;
1982
+ const { length } = keys;
1983
+ let next;
1984
+ if (isNullableOrWhitespace(activeDescendant)) next = getDefaultIndex(event.key, length);
1985
+ else next = getIndex(event, activeDescendant, id, keys);
1986
+ if (next != null) this.setActive(keys.at(next));
1987
+ }
1988
+ setActive(key, scroll) {
1989
+ const { components, managers, options } = this.state;
1990
+ this.active = key;
1991
+ const active = components.body.elements.group.querySelectorAll("[data-active=\"true\"]");
1992
+ for (const item of active) item.setAttribute("data-active", "false");
1993
+ const row = managers.row.get(key);
1994
+ if (row != null) {
1995
+ row.element?.setAttribute("data-active", "true");
1996
+ if (scroll ?? true) if (row.element == null) components.body.elements.group.scrollTo({
1997
+ top: managers.data.getIndex(key) * options.rowHeight,
1998
+ behavior: "smooth"
1999
+ });
2000
+ else row.element.scrollIntoView({ block: "nearest" });
2001
+ }
2002
+ components.body.elements.group.setAttribute("aria-activedescendant", row == null ? "" : `tabela_${this.state.id}_row_${key}`);
2003
+ }
2004
+ };
2005
+ function getDefaultIndex(key, max) {
2006
+ switch (true) {
2007
+ case negativeDefaultKeys.has(key): return -1;
2008
+ case key === "PageDown": return Math.min(9, max - 1);
2009
+ case key === "PageUp": return max < 10 ? 0 : max - 10;
2010
+ default: return 0;
2011
+ }
2012
+ }
2013
+ function getIndex(event, active, id, keys) {
2014
+ const key = getKey(active.replace(`tabela_${id}_row_`, ""));
2015
+ if (key == null) return;
2016
+ if (absoluteKeys.has(event.key)) return event.key === "Home" ? 0 : keys.length - 1;
2017
+ return clamp(keys.indexOf(key) + getOffset(event.key), 0, keys.length - 1, true);
2018
+ }
2019
+ function getOffset(key) {
2020
+ switch (key) {
2021
+ case "ArrowDown": return 1;
2022
+ case "ArrowUp": return -1;
2023
+ case "PageDown": return 10;
2024
+ case "PageUp": return -10;
2025
+ default: return 0;
2026
+ }
2027
+ }
2028
+ const absoluteKeys = new Set(["End", "Home"]);
2029
+ const arrowKeys = new Set(["ArrowDown", "ArrowUp"]);
2030
+ const negativeDefaultKeys = new Set(["ArrowUp", "End"]);
2031
+ const pageKeys = new Set(["PageDown", "PageUp"]);
2032
+ const allKeys = new Set([
2033
+ ...absoluteKeys,
2034
+ ...arrowKeys,
2035
+ ...pageKeys
2036
+ ]);
1767
2037
  function removeRow(pool, row) {
1768
2038
  if (row.element != null) {
1769
2039
  row.element.innerHTML = "";
@@ -1773,19 +2043,30 @@ function removeRow(pool, row) {
1773
2043
  }
1774
2044
  row.cells = {};
1775
2045
  }
1776
- function renderRow(managers, row) {
1777
- const element = row.element ?? managers.virtualization.pool.rows.shift() ?? createRow();
2046
+ function renderRow(state, row) {
2047
+ const element = row.element ?? state.managers.render.pool.rows.shift() ?? createRow();
1778
2048
  row.element = element;
1779
- element.dataset.key = String(row.key);
1780
2049
  element.innerHTML = "";
1781
- const columns = managers.column.items;
2050
+ const selected = state.managers.selection.items.has(row.key);
2051
+ const key = String(row.key);
2052
+ setAttributes(element, {
2053
+ "aria-selected": String(selected),
2054
+ "data-active": String(state.managers.navigation.active === row.key),
2055
+ "data-event": "row",
2056
+ "data-key": key,
2057
+ id: `tabela_${state.id}_row_${key}`
2058
+ });
2059
+ element.classList.add("tabela__row--body");
2060
+ if (selected) element.classList.add("tabela__row--selected");
2061
+ else element.classList.remove("tabela__row--selected");
2062
+ const columns = state.managers.column.items;
1782
2063
  const { length } = columns;
1783
- const data = managers.data.values.objects.mapped.get(row.key);
2064
+ const data = state.managers.data.values.objects.mapped.get(row.key);
1784
2065
  if (data == null) return;
1785
2066
  for (let index = 0; index < length; index += 1) {
1786
2067
  const { options } = columns[index];
1787
- managers.virtualization.pool.cells[options.field] ??= [];
1788
- const cell = managers.virtualization.pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
2068
+ state.managers.render.pool.cells[options.field] ??= [];
2069
+ const cell = state.managers.render.pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
1789
2070
  cell.textContent = String(data[options.field]);
1790
2071
  row.cells[options.field] = cell;
1791
2072
  element.append(cell);
@@ -1798,42 +2079,500 @@ var RowComponent = class {
1798
2079
  this.key = key;
1799
2080
  }
1800
2081
  };
1801
- var RowManager = class {
1802
- components = /* @__PURE__ */ new Map();
1803
- height;
1804
- constructor(managers, rowHeight) {
1805
- this.managers = managers;
1806
- this.height = rowHeight;
2082
+ function getRange(down) {
2083
+ const { components, managers, options } = this.state;
2084
+ const { clientHeight, scrollTop } = components.body.elements.group;
2085
+ const first = Math.floor(scrollTop / options.rowHeight);
2086
+ const last = Math.min((managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length) - 1, Math.ceil((scrollTop + clientHeight) / options.rowHeight) - 1);
2087
+ const before = Math.ceil(clientHeight / options.rowHeight) * (down ? 1 : 2);
2088
+ const after = Math.ceil(clientHeight / options.rowHeight) * (down ? 2 : 1);
2089
+ const start = Math.max(0, first - before);
2090
+ return {
2091
+ end: Math.min((managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length) - 1, last + after),
2092
+ start
2093
+ };
2094
+ }
2095
+ function onScroll() {
2096
+ const { state } = this;
2097
+ if (!state.active) {
2098
+ requestAnimationFrame(() => {
2099
+ const top = state.components.body.elements.group.scrollTop;
2100
+ this.update(top > state.top);
2101
+ state.active = false;
2102
+ state.top = top;
2103
+ });
2104
+ state.active = true;
2105
+ }
2106
+ }
2107
+ var RenderManager = class {
2108
+ fragment;
2109
+ listener;
2110
+ pool = {
2111
+ cells: {},
2112
+ rows: []
2113
+ };
2114
+ state;
2115
+ visible = /* @__PURE__ */ new Map();
2116
+ constructor(state) {
2117
+ this.listener = on(state.components.body.elements.group, "scroll", onScroll.bind(this));
2118
+ this.state = {
2119
+ ...state,
2120
+ active: false,
2121
+ top: 0
2122
+ };
1807
2123
  }
1808
2124
  destroy() {
1809
- const components = [...this.components.values()];
1810
- const { length } = components;
1811
- for (let index = 0; index < length; index += 1) removeRow(this.managers.virtualization.pool, components[index]);
1812
- this.components.clear();
2125
+ const { listener, pool, visible } = this;
2126
+ listener();
2127
+ visible.clear();
2128
+ pool.cells = {};
2129
+ pool.rows = [];
2130
+ this.fragment = void 0;
2131
+ this.listener = void 0;
2132
+ this.pool = void 0;
2133
+ this.state = void 0;
2134
+ this.visible = void 0;
1813
2135
  }
1814
- get(key) {
1815
- let row = this.components.get(key);
1816
- if (row == null) {
1817
- row = new RowComponent(key);
1818
- this.components.set(key, row);
2136
+ removeCells(fields) {
2137
+ const { pool, state, visible } = this;
2138
+ const { length } = fields;
2139
+ for (let index = 0; index < length; index += 1) delete pool.cells[fields[index]];
2140
+ for (const [, key] of visible) {
2141
+ const row = state.managers.row.get(key);
2142
+ if (row == null || row.element == null) continue;
2143
+ for (let index = 0; index < length; index += 1) {
2144
+ row.cells[fields[index]].innerHTML = "";
2145
+ row.cells[fields[index]].remove();
2146
+ delete row.cells[fields[index]];
2147
+ }
1819
2148
  }
1820
- return row;
1821
- }
1822
- has(key) {
1823
- return this.components.has(key);
2149
+ }
2150
+ getFragment() {
2151
+ this.fragment ??= document.createDocumentFragment();
2152
+ this.fragment.replaceChildren();
2153
+ return this.fragment;
2154
+ }
2155
+ update(down, rerender) {
2156
+ const { state, pool, visible } = this;
2157
+ const { components, managers, options } = state;
2158
+ components.body.elements.faker.style.height = `${managers.data.size * options.rowHeight}px`;
2159
+ const indices = /* @__PURE__ */ new Set();
2160
+ const range = getRange.call(this, down);
2161
+ for (let index = range.start; index <= range.end; index += 1) indices.add(index);
2162
+ let remove = rerender ?? false;
2163
+ for (const [index, key] of visible) {
2164
+ const row = managers.row.get(key);
2165
+ if (remove || row == null || !indices.has(index)) {
2166
+ visible.delete(index);
2167
+ if (row != null) removeRow(pool, row);
2168
+ }
2169
+ }
2170
+ const fragment = this.getFragment();
2171
+ const keys = managers.data.values.keys.active ?? managers.data.values.keys.original;
2172
+ let count = 0;
2173
+ for (let index = range.start; index <= range.end; index += 1) {
2174
+ if (visible.has(index)) continue;
2175
+ const key = keys[index];
2176
+ const row = managers.row.get(key);
2177
+ if (row == null) continue;
2178
+ count += 1;
2179
+ renderRow(state, row);
2180
+ visible.set(index, key);
2181
+ if (row.element != null) {
2182
+ row.element.style.transform = `translateY(${index * options.rowHeight}px)`;
2183
+ fragment.append(row.element);
2184
+ }
2185
+ }
2186
+ if (count > 0) components.body.elements.group[down ? "append" : "prepend"](fragment);
2187
+ }
2188
+ };
2189
+ var RowManager = class {
2190
+ components = /* @__PURE__ */ new Map();
2191
+ constructor(state) {
2192
+ this.state = state;
2193
+ }
2194
+ destroy() {
2195
+ const components = [...this.components.values()];
2196
+ const { length } = components;
2197
+ for (let index = 0; index < length; index += 1) removeRow(this.state.managers.render.pool, components[index]);
2198
+ this.components.clear();
2199
+ this.components = void 0;
2200
+ this.state = void 0;
2201
+ }
2202
+ get(key) {
2203
+ let row = this.components.get(key);
2204
+ if (row == null) {
2205
+ row = new RowComponent(key);
2206
+ this.components.set(key, row);
2207
+ }
2208
+ return row;
2209
+ }
2210
+ has(key) {
2211
+ return this.components.has(key);
1824
2212
  }
1825
2213
  remove(key) {
1826
2214
  const row = this.components.get(key);
1827
2215
  if (row != null) {
1828
- removeRow(this.managers.virtualization.pool, row);
2216
+ removeRow(this.state.managers.render.pool, row);
1829
2217
  this.components.delete(key);
1830
2218
  }
1831
2219
  }
1832
2220
  update(key) {
1833
2221
  const row = this.components.get(key);
1834
- if (row != null) renderRow(this.managers, row);
2222
+ if (row != null) renderRow(this.state, row);
1835
2223
  }
1836
2224
  };
2225
+ function isInert(item) {
2226
+ return (item.element.inert ?? false) || EXPRESSION_TRUEISH.test(item.element.getAttribute(ATTRIBUTE_INERT)) || item.element.parentElement != null && isInert({
2227
+ element: item.element.parentElement,
2228
+ tabIndex: TABINDEX_DEFAULT
2229
+ });
2230
+ }
2231
+ var ATTRIBUTE_INERT = "inert";
2232
+ var EXPRESSION_TRUEISH = /^(|true)$/i;
2233
+ [
2234
+ "[contenteditable]:not([contenteditable=\"false\"])",
2235
+ "[tabindex]:not(slot)",
2236
+ "a[href]",
2237
+ "audio[controls]",
2238
+ "button",
2239
+ "details",
2240
+ "details > summary:first-of-type",
2241
+ "input",
2242
+ "select",
2243
+ "textarea",
2244
+ "video[controls]"
2245
+ ].map((selector) => `${selector}:not([inert])`).join(",");
2246
+ var TABINDEX_DEFAULT = -1;
2247
+ function handleElement(element, depth) {
2248
+ if (depth === 0) {
2249
+ const removable = element.querySelectorAll(REMOVE_SELECTOR);
2250
+ for (const item of removable) item.remove();
2251
+ }
2252
+ sanitizeAttributes(element, [...element.attributes]);
2253
+ }
2254
+ /**
2255
+ * Is the element clobbered?
2256
+ *
2257
+ * Thanks, DOMPurify _(https://github.com/cure53/DOMPurify)_
2258
+ */
2259
+ function isClobbered(value) {
2260
+ return value instanceof HTMLFormElement && (typeof value.nodeName !== "string" || typeof value.textContent !== "string" || typeof value.removeChild !== "function" || !(value.attributes instanceof NamedNodeMap) || typeof value.removeAttribute !== "function" || typeof value.setAttribute !== "function" || typeof value.namespaceURI !== "string" || typeof value.insertBefore !== "function" || typeof value.hasChildNodes !== "function");
2261
+ }
2262
+ function removeNode(node) {
2263
+ if (typeof node.remove === "function") node.remove();
2264
+ }
2265
+ function sanitizeAttributes(element, attributes) {
2266
+ const { length } = attributes;
2267
+ for (let index = 0; index < length; index += 1) {
2268
+ const { name, value } = attributes[index];
2269
+ if (_isBadAttribute(name, value, false) || _isEmptyNonBooleanAttribute(name, value, false)) element.removeAttribute(name);
2270
+ else if (_isInvalidBooleanAttribute(name, value, false)) setAttribute(element, name, true);
2271
+ }
2272
+ }
2273
+ function sanitizeNodes(nodes, depth) {
2274
+ const actual = nodes.filter((node) => node instanceof Node);
2275
+ let { length } = nodes;
2276
+ for (let index = 0; index < length; index += 1) {
2277
+ const node = actual[index];
2278
+ let remove = isClobbered(node);
2279
+ if (!remove) switch (node.nodeType) {
2280
+ case Node.ELEMENT_NODE:
2281
+ handleElement(node, depth);
2282
+ break;
2283
+ case Node.COMMENT_NODE:
2284
+ remove = COMMENT_HARMFUL.test(node.data);
2285
+ break;
2286
+ case Node.DOCUMENT_TYPE_NODE:
2287
+ case Node.PROCESSING_INSTRUCTION_NODE:
2288
+ remove = true;
2289
+ break;
2290
+ }
2291
+ if (remove) {
2292
+ removeNode(node);
2293
+ actual.splice(index, 1);
2294
+ index -= 1;
2295
+ length -= 1;
2296
+ continue;
2297
+ }
2298
+ if (node.hasChildNodes()) sanitizeNodes([...node.childNodes], depth + 1);
2299
+ }
2300
+ return nodes;
2301
+ }
2302
+ var COMMENT_HARMFUL = /<[/\w]/g;
2303
+ var REMOVE_SELECTOR = "script, toretto-temporary";
2304
+ function createHtml(value) {
2305
+ const parsed = getParser().parseFromString(getHtml(value), PARSE_TYPE_HTML);
2306
+ parsed.body.normalize();
2307
+ sanitizeNodes([parsed.body], 0);
2308
+ return parsed.body.innerHTML;
2309
+ }
2310
+ function createTemplate(value, options) {
2311
+ const template = document.createElement(TEMPLATE_TAG);
2312
+ template.innerHTML = createHtml(value);
2313
+ if (typeof value === "string" && options.cache) templates[value] = template;
2314
+ return template;
2315
+ }
2316
+ function getHtml(value) {
2317
+ return `${TEMPORARY_ELEMENT}${typeof value === "string" ? value : value.innerHTML}${TEMPORARY_ELEMENT}`;
2318
+ }
2319
+ function getNodes(value, options) {
2320
+ if (typeof value !== "string" && !(value instanceof HTMLTemplateElement)) return [];
2321
+ const template = getTemplate(value, options);
2322
+ return template == null ? [] : [...template.content.cloneNode(true).childNodes];
2323
+ }
2324
+ function getOptions(input) {
2325
+ const options = isPlainObject$1(input) ? input : {};
2326
+ options.cache = typeof options.cache === "boolean" ? options.cache : true;
2327
+ return options;
2328
+ }
2329
+ function getParser() {
2330
+ parser ??= new DOMParser();
2331
+ return parser;
2332
+ }
2333
+ function getTemplate(value, options) {
2334
+ if (value instanceof HTMLTemplateElement) return createTemplate(value, options);
2335
+ if (value.trim().length === 0) return;
2336
+ let template = templates[value];
2337
+ if (template != null) return template;
2338
+ const element = EXPRESSION_ID.test(value) ? document.querySelector(`#${value}`) : null;
2339
+ return createTemplate(element instanceof HTMLTemplateElement ? element : value, options);
2340
+ }
2341
+ var html = ((value, options) => {
2342
+ return getNodes(value, getOptions(options));
2343
+ });
2344
+ html.clear = () => {
2345
+ templates = {};
2346
+ };
2347
+ html.remove = (template) => {
2348
+ if (typeof template !== "string" || templates[template] == null) return;
2349
+ const keys = Object.keys(templates);
2350
+ const { length } = keys;
2351
+ const updated = {};
2352
+ for (let index = 0; index < length; index += 1) {
2353
+ const key = keys[index];
2354
+ if (key !== template) updated[key] = templates[key];
2355
+ }
2356
+ templates = updated;
2357
+ };
2358
+ var EXPRESSION_ID = /^[a-z][\w-]*$/i;
2359
+ var PARSE_TYPE_HTML = "text/html";
2360
+ var TEMPLATE_TAG = "template";
2361
+ var TEMPORARY_ELEMENT = "<toretto-temporary></toretto-temporary>";
2362
+ var parser;
2363
+ var templates = {};
2364
+ function getSupport() {
2365
+ if (window == null || navigator == null) return false;
2366
+ if ("matchMedia" in window) {
2367
+ const media = matchMedia?.("(pointer: coarse)");
2368
+ if (typeof media?.matches === "boolean" && media.matches) return true;
2369
+ }
2370
+ if ("ontouchstart" in window) return true;
2371
+ if (typeof navigator.maxTouchPoints === "number" && navigator.maxTouchPoints > 0) return true;
2372
+ if (typeof navigator.msMaxTouchPoints === "number" && navigator.msMaxTouchPoints > 0) return true;
2373
+ return false;
2374
+ }
2375
+ (() => {
2376
+ let support = getSupport();
2377
+ const instance = Object.create({
2378
+ get() {
2379
+ return support;
2380
+ },
2381
+ update() {
2382
+ support = getSupport();
2383
+ return support;
2384
+ }
2385
+ });
2386
+ Object.defineProperty(instance, "value", { get() {
2387
+ return support;
2388
+ } });
2389
+ return instance;
2390
+ })();
2391
+ const dragStyling = toggleStyles(document.body, {
2392
+ userSelect: "none",
2393
+ webkitUserSelect: "none"
2394
+ });
2395
+ var SelectionManager = class {
2396
+ handlers = Object.freeze({
2397
+ clear: () => this.clear(),
2398
+ deselect: (keys) => this.deselect(keys),
2399
+ select: (keys) => this.select(keys),
2400
+ toggle: () => this.toggle()
2401
+ });
2402
+ items = /* @__PURE__ */ new Set();
2403
+ last;
2404
+ constructor(state) {
2405
+ this.state = state;
2406
+ mapped.set(state.element, this);
2407
+ }
2408
+ clear() {
2409
+ if (this.items.size === 0) return;
2410
+ const removed = [...this.items];
2411
+ this.items.clear();
2412
+ this.update(removed);
2413
+ }
2414
+ deselect(keys) {
2415
+ const { length } = keys;
2416
+ const removed = [];
2417
+ for (let index = 0; index < length; index += 1) {
2418
+ const key = keys[index];
2419
+ if (this.items.delete(key)) removed.push(key);
2420
+ }
2421
+ if (removed.length > 0) this.update(removed);
2422
+ }
2423
+ destroy() {
2424
+ mapped.delete(this.state.element);
2425
+ this.handlers = void 0;
2426
+ this.items = void 0;
2427
+ this.last = void 0;
2428
+ this.state = void 0;
2429
+ }
2430
+ handle(event, target) {
2431
+ const key = getKey(target.getAttribute("data-key"));
2432
+ if (key == null) return;
2433
+ const { items } = this;
2434
+ if (event.shiftKey) {
2435
+ if (this.last == null) this.state.managers.navigation.setActive(key, false);
2436
+ else this.range(this.last, key);
2437
+ return;
2438
+ }
2439
+ this.last = key;
2440
+ this.state.managers.navigation.setActive(key, false);
2441
+ if (event.ctrlKey || event.metaKey) {
2442
+ if (items.has(key)) this.deselect([key]);
2443
+ else this.select([key]);
2444
+ return;
2445
+ }
2446
+ this.set([key]);
2447
+ }
2448
+ range(from, to) {
2449
+ const { state } = this;
2450
+ const keyed = isKey(from) && isKey(to);
2451
+ const fromKey = keyed ? from : getKey(from.getAttribute("data-key"));
2452
+ const toKey = keyed ? to : getKey(to.getAttribute("data-key"));
2453
+ if (fromKey === toKey) return;
2454
+ const keys = state.managers.data.values.keys.active ?? state.managers.data.values.keys.original;
2455
+ const fromIndex = state.managers.data.getIndex(fromKey);
2456
+ const toIndex = state.managers.data.getIndex(toKey);
2457
+ if (fromIndex === -1 || toIndex === -1) return;
2458
+ const [start, end] = fromIndex < toIndex ? [fromIndex, toIndex] : [toIndex, fromIndex];
2459
+ const selected = [];
2460
+ for (let index = start; index <= end; index += 1) selected.push(keys[index]);
2461
+ if (keyed) this.select(selected);
2462
+ else this.set(selected);
2463
+ this.last = toKey;
2464
+ this.state.managers.navigation.setActive(toKey, false);
2465
+ }
2466
+ select(keys) {
2467
+ const { length } = keys;
2468
+ let update = false;
2469
+ for (let index = 0; index < length; index += 1) {
2470
+ const key = keys[index];
2471
+ if (!this.items.has(key)) {
2472
+ this.items.add(key);
2473
+ update = true;
2474
+ }
2475
+ }
2476
+ if (update) this.update([]);
2477
+ }
2478
+ set(keys) {
2479
+ const { items } = this;
2480
+ const removed = [...items].filter((key) => !keys.includes(key));
2481
+ const added = keys.filter((key) => !items.has(key));
2482
+ if (removed.length === 0 && added.length === 0) return;
2483
+ this.items = new Set(keys);
2484
+ this.update(removed);
2485
+ }
2486
+ toggle() {
2487
+ const { items, state } = this;
2488
+ const all = state.managers.data.values.keys.active ?? state.managers.data.values.keys.original;
2489
+ if (items.size === all.length) this.clear();
2490
+ else this.select(all);
2491
+ }
2492
+ update(removed) {
2493
+ const items = [...removed.map((key) => ({
2494
+ key,
2495
+ removed: true
2496
+ })), ...[...this.items].map((key) => ({
2497
+ key,
2498
+ removed: false
2499
+ }))];
2500
+ const { length } = items;
2501
+ for (let index = 0; index < length; index += 1) {
2502
+ const { key, removed } = items[index];
2503
+ const row = this.state.managers.row.get(key);
2504
+ if (row == null || row.element == null) continue;
2505
+ setAttribute(row.element, "aria-selected", String(!removed));
2506
+ if (removed) row.element.classList.remove("tabela__row--selected");
2507
+ else row.element.classList.add("tabela__row--selected");
2508
+ }
2509
+ }
2510
+ };
2511
+ function getPlaceholder() {
2512
+ placeholder ??= createElement("div", { className: "tabela__selection--placeholder" }, {}, {});
2513
+ return placeholder;
2514
+ }
2515
+ function onMouseDown(event) {
2516
+ if (shifted) {
2517
+ const row = findAncestor(event.target, ".tabela__row--body");
2518
+ if (!(row instanceof HTMLElement)) return;
2519
+ startElement = row;
2520
+ startPosition = getPosition(event);
2521
+ dragStyling.set();
2522
+ }
2523
+ }
2524
+ function onMouseMove(event) {
2525
+ if (startElement == null) return;
2526
+ const currentPosition = getPosition(event);
2527
+ if (currentPosition == null || startPosition == null) return;
2528
+ const element = getPlaceholder();
2529
+ if (element.parentElement == null) document.body.append(element);
2530
+ const { x: cX, y: cY } = currentPosition;
2531
+ const { x: sX, y: sY } = startPosition;
2532
+ const top = Math.min(cY, sY);
2533
+ const left = Math.min(cX, sX);
2534
+ const width = Math.abs(cX - sX);
2535
+ const height = Math.abs(cY - sY);
2536
+ element.style.inset = `${top}px ${window.innerWidth - left - width}px ${window.innerHeight - top - height}px ${left}px`;
2537
+ }
2538
+ function onMouseUp(event) {
2539
+ if (startElement == null) return;
2540
+ if (!event.shiftKey) {
2541
+ shifted = false;
2542
+ dragStyling.remove();
2543
+ }
2544
+ getPlaceholder().remove();
2545
+ const row = findAncestor(event.target, ".tabela__row--body");
2546
+ if (row instanceof HTMLElement) {
2547
+ endElement = row;
2548
+ const endTable = findAncestor(endElement, ".tabela");
2549
+ const startTable = findAncestor(startElement, ".tabela");
2550
+ if (startTable != null && startTable === endTable) mapped.get(startTable)?.range(startElement, endElement);
2551
+ }
2552
+ endElement = void 0;
2553
+ startElement = void 0;
2554
+ startPosition = void 0;
2555
+ }
2556
+ function onShift(event, value) {
2557
+ if (event.key === "Shift") shifted = value;
2558
+ }
2559
+ function onShiftDown(event) {
2560
+ onShift(event, true);
2561
+ }
2562
+ function onShiftUp(event) {
2563
+ onShift(event, false);
2564
+ }
2565
+ const mapped = /* @__PURE__ */ new WeakMap();
2566
+ let shifted = false;
2567
+ let endElement;
2568
+ let placeholder;
2569
+ let startPosition;
2570
+ let startElement;
2571
+ on(document, "keydown", onShiftDown);
2572
+ on(document, "keyup", onShiftUp);
2573
+ on(document, "mousedown", onMouseDown);
2574
+ on(document, "mousemove", onMouseMove);
2575
+ on(document, "mouseup", onMouseUp);
1837
2576
  var SortManager = class {
1838
2577
  handlers = Object.freeze({
1839
2578
  add: (field, direction) => this.add(field, direction),
@@ -1843,8 +2582,8 @@ var SortManager = class {
1843
2582
  set: (items) => this.set(items)
1844
2583
  });
1845
2584
  items = [];
1846
- constructor(managers) {
1847
- this.managers = managers;
2585
+ constructor(state) {
2586
+ this.state = state;
1848
2587
  }
1849
2588
  add(field, direction) {
1850
2589
  if (this.items.findIndex((item) => item.key === field) > -1) return;
@@ -1869,7 +2608,8 @@ var SortManager = class {
1869
2608
  }
1870
2609
  destroy() {
1871
2610
  this.handlers = void 0;
1872
- this.items.length = 0;
2611
+ this.items = void 0;
2612
+ this.state = void 0;
1873
2613
  }
1874
2614
  flip(field) {
1875
2615
  const item = this.items.find((item) => item.key === field);
@@ -1896,10 +2636,10 @@ var SortManager = class {
1896
2636
  this.sort();
1897
2637
  }
1898
2638
  sort() {
1899
- const { items, managers } = this;
1900
- const { length } = managers.column.items;
2639
+ const { items, state } = this;
2640
+ const { length } = state.managers.column.items;
1901
2641
  for (let index = 0; index < length; index += 1) {
1902
- const column = managers.column.items[index];
2642
+ const column = state.managers.column.items[index];
1903
2643
  const sorterIndex = items.findIndex((item) => item.key === column.options.field);
1904
2644
  const sorterItem = items[sorterIndex];
1905
2645
  setAttributes(column.elements.wrapper, {
@@ -1908,8 +2648,8 @@ var SortManager = class {
1908
2648
  });
1909
2649
  setAttribute(column.elements.sorter, "data-sort-position", sorterIndex > -1 && items.length > 1 ? sorterIndex + 1 : void 0);
1910
2650
  }
1911
- managers.data.values.keys.active = items.length === 0 ? void 0 : sort(managers.data.values.keys.active?.map((key) => managers.data.values.objects.mapped.get(key)) ?? managers.data.values.objects.array, items).map((row) => row[managers.data.field]);
1912
- managers.virtualization.update(true, true);
2651
+ state.managers.data.values.keys.active = items.length === 0 ? void 0 : sort(state.managers.data.values.keys.active?.map((key) => state.managers.data.values.objects.mapped.get(key)) ?? state.managers.data.values.objects.array, items).map((row) => row[state.key]);
2652
+ state.managers.render.update(true, true);
1913
2653
  }
1914
2654
  toggle(event, field, direction) {
1915
2655
  switch (direction) {
@@ -1925,104 +2665,6 @@ var SortManager = class {
1925
2665
  }
1926
2666
  }
1927
2667
  };
1928
- function getRange(down) {
1929
- const { components, managers } = this;
1930
- const { clientHeight, scrollTop } = components.body.elements.group;
1931
- const first = Math.floor(scrollTop / managers.row.height);
1932
- const last = Math.min(managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length - 1, Math.ceil((scrollTop + clientHeight) / managers.row.height) - 1);
1933
- const before = Math.ceil(clientHeight / managers.row.height) * (down ? 1 : 2);
1934
- const after = Math.ceil(clientHeight / managers.row.height) * (down ? 2 : 1);
1935
- const start = Math.max(0, first - before);
1936
- return {
1937
- end: Math.min(managers.data.values.keys.active?.length ?? managers.data.values.keys.original.length - 1, last + after),
1938
- start
1939
- };
1940
- }
1941
- function onScroll() {
1942
- if (!this.state.active) {
1943
- requestAnimationFrame(() => {
1944
- const top = this.components.body.elements.group.scrollTop;
1945
- this.update(top > this.state.top);
1946
- this.state.active = false;
1947
- this.state.top = top;
1948
- });
1949
- this.state.active = true;
1950
- }
1951
- }
1952
- var VirtualizationManager = class {
1953
- fragment;
1954
- listener;
1955
- pool = {
1956
- cells: {},
1957
- rows: []
1958
- };
1959
- state = {
1960
- active: false,
1961
- top: 0
1962
- };
1963
- visible = /* @__PURE__ */ new Map();
1964
- constructor(managers, components) {
1965
- this.managers = managers;
1966
- this.components = components;
1967
- this.listener = on(components.body.elements.group, "scroll", onScroll.bind(this));
1968
- }
1969
- destroy() {
1970
- this.listener();
1971
- for (const [index, row] of this.visible) {
1972
- removeRow(this.pool, row);
1973
- this.visible.delete(index);
1974
- }
1975
- this.pool.cells = {};
1976
- this.pool.rows = [];
1977
- this.listener = void 0;
1978
- this.visible = void 0;
1979
- }
1980
- removeCells(fields) {
1981
- const { length } = fields;
1982
- for (let index = 0; index < length; index += 1) delete this.pool.cells[fields[index]];
1983
- for (const [, row] of this.visible) for (let index = 0; index < length; index += 1) {
1984
- row.cells[fields[index]].innerHTML = "";
1985
- row.cells[fields[index]].remove();
1986
- delete row.cells[fields[index]];
1987
- }
1988
- }
1989
- getFragment() {
1990
- this.fragment ??= document.createDocumentFragment();
1991
- this.fragment.replaceChildren();
1992
- return this.fragment;
1993
- }
1994
- update(down, rerender) {
1995
- const { components, managers, pool, visible } = this;
1996
- components.body.elements.faker.style.height = `${managers.data.size * managers.row.height}px`;
1997
- const indices = /* @__PURE__ */ new Set();
1998
- const range = getRange.call(this, down);
1999
- for (let index = range.start; index <= range.end; index += 1) indices.add(index);
2000
- let remove = rerender ?? false;
2001
- for (const [index, row] of visible) {
2002
- if (!managers.row.has(row.key) || !indices.has(index)) remove = true;
2003
- if (remove) {
2004
- visible.delete(index);
2005
- removeRow(pool, row);
2006
- }
2007
- }
2008
- const fragment = this.getFragment();
2009
- const keys = managers.data.values.keys.active ?? managers.data.values.keys.original;
2010
- let count = 0;
2011
- for (let index = range.start; index <= range.end; index += 1) {
2012
- if (visible.has(index)) continue;
2013
- const row = managers.row.get(keys[index]);
2014
- if (row == null) continue;
2015
- count += 1;
2016
- renderRow(managers, row);
2017
- visible.set(index, row);
2018
- if (row.element != null) {
2019
- row.element.style.transform = `translateY(${index * managers.row.height}px)`;
2020
- fragment.append(row.element);
2021
- }
2022
- }
2023
- if (count > 0) components.body.elements.group[down ? "append" : "prepend"](fragment);
2024
- }
2025
- };
2026
2668
  var Tabela = class {
2027
2669
  #components = {
2028
2670
  header: void 0,
@@ -2030,18 +2672,22 @@ var Tabela = class {
2030
2672
  footer: void 0
2031
2673
  };
2032
2674
  #element;
2675
+ #id = getId();
2033
2676
  #key;
2034
2677
  #managers = {
2035
2678
  column: void 0,
2036
2679
  data: void 0,
2037
2680
  event: void 0,
2038
2681
  filter: void 0,
2682
+ navigation: void 0,
2683
+ render: void 0,
2039
2684
  row: void 0,
2040
- sort: void 0,
2041
- virtualization: void 0
2685
+ selection: void 0,
2686
+ sort: void 0
2042
2687
  };
2043
2688
  data;
2044
2689
  filter;
2690
+ selection;
2045
2691
  sort;
2046
2692
  get key() {
2047
2693
  return this.#key;
@@ -2056,17 +2702,28 @@ var Tabela = class {
2056
2702
  this.#components.header = new HeaderComponent();
2057
2703
  this.#components.body = new BodyComponent();
2058
2704
  this.#components.footer = new FooterComponent();
2059
- this.#managers.column = new ColumnManager(this.#managers, this.#components, options.columns);
2060
- this.#managers.data = new DataManager(this.#managers, this.#components, options.key);
2061
- this.#managers.event = new EventManager(this.#managers, this.#element);
2062
- this.#managers.filter = new FilterManager(this.#managers);
2063
- this.#managers.row = new RowManager(this.#managers, options.rowHeight);
2064
- this.#managers.sort = new SortManager(this.#managers);
2065
- this.#managers.virtualization = new VirtualizationManager(this.#managers, this.#components);
2705
+ const state = {
2706
+ element,
2707
+ options,
2708
+ components: this.#components,
2709
+ id: this.#id,
2710
+ key: this.#key,
2711
+ managers: this.#managers
2712
+ };
2713
+ this.#managers.column = new ColumnManager(state);
2714
+ this.#managers.data = new DataManager(state);
2715
+ this.#managers.event = new EventManager(state);
2716
+ this.#managers.filter = new FilterManager(state);
2717
+ this.#managers.navigation = new NavigationManager(state);
2718
+ this.#managers.render = new RenderManager(state);
2719
+ this.#managers.row = new RowManager(state);
2720
+ this.#managers.selection = new SelectionManager(state);
2721
+ this.#managers.sort = new SortManager(state);
2066
2722
  element.append(this.#components.header.elements.group, this.#components.body.elements.group, this.#components.footer.elements.group);
2067
2723
  this.#managers.data.set(options.data);
2068
2724
  this.data = this.#managers.data.handlers;
2069
2725
  this.filter = this.#managers.filter.handlers;
2726
+ this.selection = this.#managers.selection.handlers;
2070
2727
  this.sort = this.#managers.sort.handlers;
2071
2728
  }
2072
2729
  destroy() {
@@ -2080,9 +2737,10 @@ var Tabela = class {
2080
2737
  managers.data.destroy();
2081
2738
  managers.event.destroy();
2082
2739
  managers.filter.destroy();
2740
+ managers.render.destroy();
2083
2741
  managers.row.destroy();
2742
+ managers.selection.destroy();
2084
2743
  managers.sort.destroy();
2085
- managers.virtualization.destroy();
2086
2744
  element.innerHTML = "";
2087
2745
  element.role = "";
2088
2746
  element.classList.remove("tabela");
@@ -2091,6 +2749,11 @@ var Tabela = class {
2091
2749
  this.#element = void 0;
2092
2750
  }
2093
2751
  };
2752
+ function getId() {
2753
+ id += 1;
2754
+ return id;
2755
+ }
2756
+ let id = 0;
2094
2757
  function tabela(element, options) {
2095
2758
  return new Tabela(element, options);
2096
2759
  }