@oscarpalmer/tabela 0.7.0 → 0.9.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 (42) hide show
  1. package/dist/components/body.component.js +1 -1
  2. package/dist/components/column.component.js +29 -9
  3. package/dist/components/header.component.js +1 -1
  4. package/dist/helpers/dom.helpers.js +6 -4
  5. package/dist/managers/column.manager.js +6 -1
  6. package/dist/managers/data.manager.js +15 -10
  7. package/dist/managers/event.manager.js +30 -0
  8. package/dist/managers/filter.manager.js +92 -0
  9. package/dist/managers/row.manager.js +9 -2
  10. package/dist/managers/sort.manager.js +94 -0
  11. package/dist/managers/virtualization.manager.js +4 -2
  12. package/dist/models/filter.model.js +0 -0
  13. package/dist/models/sort.model.js +0 -0
  14. package/dist/tabela.full.js +1441 -108
  15. package/dist/tabela.js +16 -0
  16. package/package.json +1 -1
  17. package/src/components/body.component.ts +1 -0
  18. package/src/components/column.component.ts +58 -19
  19. package/src/components/header.component.ts +1 -1
  20. package/src/helpers/dom.helpers.ts +6 -0
  21. package/src/managers/column.manager.ts +8 -0
  22. package/src/managers/data.manager.ts +25 -15
  23. package/src/managers/event.manager.ts +55 -0
  24. package/src/managers/filter.manager.ts +154 -0
  25. package/src/managers/row.manager.ts +16 -2
  26. package/src/managers/sort.manager.ts +149 -0
  27. package/src/managers/virtualization.manager.ts +6 -3
  28. package/src/models/filter.model.ts +17 -0
  29. package/src/models/sort.model.ts +6 -0
  30. package/src/models/tabela.model.ts +24 -0
  31. package/src/tabela.ts +25 -1
  32. package/types/components/column.component.d.ts +9 -2
  33. package/types/helpers/dom.helpers.d.ts +1 -1
  34. package/types/managers/data.manager.d.ts +10 -2
  35. package/types/managers/event.manager.d.ts +10 -0
  36. package/types/managers/filter.manager.d.ts +19 -0
  37. package/types/managers/sort.manager.d.ts +26 -0
  38. package/types/managers/virtualization.manager.d.ts +2 -2
  39. package/types/models/filter.model.d.ts +6 -0
  40. package/types/models/sort.model.d.ts +5 -0
  41. package/types/models/tabela.model.d.ts +22 -0
  42. package/types/tabela.d.ts +3 -1
@@ -3,14 +3,25 @@
3
3
  * @param value Original value
4
4
  * @returns String representation of the value
5
5
  */
6
- function getString(value) {
6
+ function getString$1(value) {
7
7
  if (typeof value === "string") return value;
8
8
  if (value == null) return "";
9
- if (typeof value === "function") return getString(value());
9
+ if (typeof value === "function") return getString$1(value());
10
10
  if (typeof value !== "object") return String(value);
11
11
  const asString = String(value.valueOf?.() ?? value);
12
12
  return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
13
13
  }
14
+ /**
15
+ * Is the value a plain object?
16
+ * @param value Value to check
17
+ * @returns `true` if the value is a plain object, otherwise `false`
18
+ */
19
+ function isPlainObject$1(value) {
20
+ if (value === null || typeof value !== "object") return false;
21
+ if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
22
+ const prototype = Object.getPrototypeOf(value);
23
+ return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
24
+ }
14
25
  function getBoolean(value, defaultValue) {
15
26
  return typeof value === "boolean" ? value : defaultValue ?? false;
16
27
  }
@@ -43,10 +54,80 @@ new Set([
43
54
  * @returns `true` if the value is nullable or a whitespace-only string, otherwise `false`
44
55
  */
45
56
  function isNullableOrWhitespace(value) {
46
- return value == null || EXPRESSION_WHITESPACE.test(getString(value));
57
+ return value == null || EXPRESSION_WHITESPACE$1.test(getString$1(value));
58
+ }
59
+ var EXPRESSION_WHITESPACE$1 = /^\s*$/;
60
+ function badAttributeHandler(name, value) {
61
+ if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
62
+ if (EXPRESSION_CLOBBERED_NAME.test(name) && (value in document || value in formElement) || EXPRESSION_EVENT_NAME.test(name)) return true;
63
+ if (EXPRESSION_SKIP_NAME.test(name) || EXPRESSION_URI_VALUE.test(value) || isValidSourceAttribute(name, value)) return false;
64
+ return EXPRESSION_DATA_OR_SCRIPT.test(value);
65
+ }
66
+ function booleanAttributeHandler(name, value) {
67
+ if (typeof name !== "string" || name.trim().length === 0 || typeof value !== "string") return true;
68
+ const normalizedName = name.toLowerCase();
69
+ if (!booleanAttributesSet.has(normalizedName)) return false;
70
+ const normalized = value.toLowerCase();
71
+ return !(normalized.length === 0 || normalized === normalizedName);
72
+ }
73
+ function decodeAttribute(value) {
74
+ textArea ??= document.createElement("textarea");
75
+ textArea.innerHTML = value;
76
+ return decodeURIComponent(textArea.value);
77
+ }
78
+ function handleAttribute(callback, decode, first, second) {
79
+ let name;
80
+ let value;
81
+ if (isAttribute(first)) {
82
+ name = first.name;
83
+ value = String(first.value);
84
+ } else if (typeof first === "string" && typeof second === "string") {
85
+ name = first;
86
+ value = second;
87
+ }
88
+ if (decode && value != null) value = decodeAttribute(value);
89
+ return callback(name, value?.replace(EXPRESSION_WHITESPACE, ""));
90
+ }
91
+ function isAttribute(value) {
92
+ return value instanceof Attr || isPlainObject$1(value) && typeof value.name === "string" && "value" in value;
93
+ }
94
+ function _isBadAttribute(first, second, decode) {
95
+ return handleAttribute(badAttributeHandler, decode, first, second);
96
+ }
97
+ function _isEmptyNonBooleanAttribute(first, second, decode) {
98
+ return handleAttribute((name, value) => name != null && value != null && !booleanAttributesSet.has(name) && value.trim().length === 0, decode, first, second);
99
+ }
100
+ function _isInvalidBooleanAttribute(first, second, decode) {
101
+ return handleAttribute(booleanAttributeHandler, decode, first, second);
102
+ }
103
+ function isValidSourceAttribute(name, value) {
104
+ return EXPRESSION_SOURCE_NAME.test(name) && EXPRESSION_SOURCE_VALUE.test(value);
105
+ }
106
+ function updateAttribute(element, name, value, dispatch) {
107
+ const normalizedName = name.toLowerCase();
108
+ const isBoolean = booleanAttributesSet.has(normalizedName);
109
+ const next = isBoolean ? value === true || typeof value === "string" && (value === "" || value.toLowerCase() === normalizedName) : value == null ? "" : value;
110
+ if (name in element) updateProperty(element, normalizedName, next, dispatch);
111
+ updateElementValue(element, name, isBoolean ? next ? "" : null : value, element.setAttribute, element.removeAttribute, isBoolean, false);
112
+ }
113
+ function updateProperty(element, name, value, dispatch) {
114
+ if (Object.is(element[name], value)) return;
115
+ element[name] = value;
116
+ const event = dispatch !== false && elementEvents[element.tagName]?.[name];
117
+ if (typeof event === "string") element.dispatchEvent(new Event(event, { bubbles: true }));
47
118
  }
48
- var EXPRESSION_WHITESPACE = /^\s*$/;
49
- Object.freeze([
119
+ var EXPRESSION_CLOBBERED_NAME = /^(id|name)$/i;
120
+ var EXPRESSION_DATA_OR_SCRIPT = /^(?:data|\w+script):/i;
121
+ var EXPRESSION_EVENT_NAME = /^on/i;
122
+ var EXPRESSION_SKIP_NAME = /^(aria-[-\w]+|data-[-\w.\u00B7-\uFFFF]+)$/i;
123
+ var EXPRESSION_SOURCE_NAME = /^src$/i;
124
+ var EXPRESSION_SOURCE_VALUE = /^data:/i;
125
+ 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;
127
+ /**
128
+ * List of boolean attributes
129
+ */
130
+ const booleanAttributes = Object.freeze([
50
131
  "async",
51
132
  "autofocus",
52
133
  "autoplay",
@@ -72,7 +153,23 @@ Object.freeze([
72
153
  "reversed",
73
154
  "selected"
74
155
  ]);
75
- document.createElement("form");
156
+ var booleanAttributesSet = new Set(booleanAttributes);
157
+ var elementEvents = {
158
+ DETAILS: { open: "toggle" },
159
+ INPUT: {
160
+ checked: "change",
161
+ value: "input"
162
+ },
163
+ SELECT: { value: "change" },
164
+ TEXTAREA: { value: "input" }
165
+ };
166
+ var formElement = document.createElement("form");
167
+ 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);
172
+ }
76
173
  function setElementValues(element, first, second, third, callback) {
77
174
  if (!isHTMLOrSVGElement(element)) return;
78
175
  if (typeof first === "string") {
@@ -110,18 +207,25 @@ function updateStyleProperty(element, key, value) {
110
207
  this.style[property] = "";
111
208
  }, false, false);
112
209
  }
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
+ }
113
216
  function createCell(width, body) {
114
217
  const cell = createElement("div", {
115
218
  className: "tabela__cell",
116
219
  role: "cell"
117
- }, { width: `${width}px` });
220
+ }, {}, { width: `${width}px` });
118
221
  if (body ?? true) cell.classList.add("tabela__cell-body");
119
222
  return cell;
120
223
  }
121
- function createElement(tagName, properties, style) {
224
+ function createElement(tagName, properties, attributes, style) {
122
225
  const element = document.createElement(tagName);
123
226
  const keys = Object.keys(properties);
124
227
  for (const key of keys) element[key] = properties[key];
228
+ setAttributes(element, attributes);
125
229
  setStyles(element, style);
126
230
  return element;
127
231
  }
@@ -129,7 +233,7 @@ function createRowGroup(withRow) {
129
233
  const group = createElement("div", {
130
234
  className: "tabela__rowgroup",
131
235
  role: "rowgroup"
132
- }, {});
236
+ }, {}, {});
133
237
  if (!(withRow ?? true)) return group;
134
238
  const row = createRow(false);
135
239
  group.append(row);
@@ -142,7 +246,7 @@ function createRow(withStyle) {
142
246
  const row = createElement("div", {
143
247
  className: "tabela__row",
144
248
  role: "row"
145
- }, {});
249
+ }, {}, {});
146
250
  if (withStyle ?? true) setStyles(row, {
147
251
  inset: "0 auto auto 0",
148
252
  position: "absolute"
@@ -150,7 +254,7 @@ function createRow(withStyle) {
150
254
  return row;
151
255
  }
152
256
  function createFaker() {
153
- return createElement("div", {}, {
257
+ return createElement("div", {}, {}, {
154
258
  height: "0",
155
259
  inset: "0 auto auto 0",
156
260
  opacity: "0",
@@ -229,18 +333,11 @@ var HeaderComponent = class {
229
333
  }
230
334
  update(columns) {
231
335
  this.elements.row.innerHTML = "";
232
- this.elements.row.append(...columns.map((column) => column.element));
336
+ this.elements.row.append(...columns.map((column) => column.elements.wrapper));
233
337
  }
234
338
  };
235
- function createHeading(title, width) {
236
- return createElement("div", {
237
- className: "tabela__heading",
238
- role: "columnheader",
239
- textContent: title
240
- }, { width: `${width}px` });
241
- }
242
339
  var ColumnComponent = class {
243
- element;
340
+ elements;
244
341
  options;
245
342
  constructor(options) {
246
343
  const width = Number.parseInt(getComputedStyle(document.body).fontSize, 10) * (options.width ?? options.title.length * 1.5);
@@ -248,9 +345,36 @@ var ColumnComponent = class {
248
345
  ...options,
249
346
  width
250
347
  };
251
- this.element = createHeading(options.title, width);
348
+ this.elements = createHeading(options.field, options.title, width);
349
+ }
350
+ destroy() {
351
+ this.elements.content.remove();
352
+ this.elements.wrapper.remove();
353
+ this.elements.sorter.remove();
354
+ this.elements = void 0;
355
+ this.options = void 0;
252
356
  }
253
357
  };
358
+ function createHeading(field, title, width) {
359
+ const wrapper = createElement("div", {
360
+ className: "tabela__heading",
361
+ role: "columnheader"
362
+ }, {
363
+ "data-event": "heading",
364
+ "data-field": field
365
+ }, { width: `${width}px` });
366
+ const content = createElement("div", {
367
+ className: "tabela__heading__content",
368
+ textContent: title
369
+ }, {}, {});
370
+ const sorter = createElement("div", { className: "tabela__heading__sorter" }, {}, {});
371
+ wrapper.append(content, sorter);
372
+ return {
373
+ content,
374
+ sorter,
375
+ wrapper
376
+ };
377
+ }
254
378
  var ColumnManager = class {
255
379
  items = [];
256
380
  constructor(managers, components, columns) {
@@ -259,6 +383,8 @@ var ColumnManager = class {
259
383
  this.set(columns);
260
384
  }
261
385
  destroy() {
386
+ const { length } = this.items;
387
+ for (let index = 0; index < length; index += 1) this.items[index].destroy();
262
388
  this.items.length = 0;
263
389
  }
264
390
  remove(value) {
@@ -268,7 +394,10 @@ var ColumnManager = class {
268
394
  if (length === 0) return;
269
395
  for (let fieldIndex = 0; fieldIndex < length; fieldIndex += 1) {
270
396
  const itemIndex = items.findIndex((component) => component.options.field === fields[fieldIndex]);
271
- if (itemIndex > -1) items.splice(itemIndex, 1);
397
+ if (itemIndex > -1) {
398
+ items[itemIndex].destroy();
399
+ items.splice(itemIndex, 1);
400
+ }
272
401
  }
273
402
  components.header.update(items);
274
403
  components.footer.update(items);
@@ -297,6 +426,408 @@ function getArrayCallbacks(bool, key, value) {
297
426
  value: getArrayCallback(value)
298
427
  };
299
428
  }
429
+ function findValues(type, array, parameters, mapper) {
430
+ const result = {
431
+ matched: [],
432
+ notMatched: []
433
+ };
434
+ if (!Array.isArray(array) || array.length === 0) return result;
435
+ const { length } = array;
436
+ const { bool, key, value } = getParameters(parameters);
437
+ const callbacks = getArrayCallbacks(bool, key);
438
+ if (type === "unique" && callbacks?.keyed == null && length >= UNIQUE_THRESHOLD) {
439
+ result.matched = [...new Set(array)];
440
+ return result;
441
+ }
442
+ const mapCallback = typeof mapper === "function" ? mapper : void 0;
443
+ if (callbacks?.bool != null || type === "all" && key == null) {
444
+ const callback = callbacks?.bool ?? ((item) => Object.is(item, value));
445
+ for (let index = 0; index < length; index += 1) {
446
+ const item = array[index];
447
+ if (callback(item, index, array)) result.matched.push(mapCallback?.(item, index, array) ?? item);
448
+ else result.notMatched.push(item);
449
+ }
450
+ return result;
451
+ }
452
+ const keys = /* @__PURE__ */ new Set();
453
+ for (let index = 0; index < length; index += 1) {
454
+ const item = array[index];
455
+ const keyed = callbacks?.keyed?.(item, index, array) ?? item;
456
+ if (type === "all" && Object.is(keyed, value) || type === "unique" && !keys.has(keyed)) {
457
+ keys.add(keyed);
458
+ result.matched.push(mapCallback?.(item, index, array) ?? item);
459
+ } else result.notMatched.push(item);
460
+ }
461
+ return result;
462
+ }
463
+ function getParameters(original) {
464
+ const { length } = original;
465
+ return {
466
+ bool: length === 1 && typeof original[0] === "function" ? original[0] : void 0,
467
+ key: length === 2 ? original[0] : void 0,
468
+ value: length === 1 && typeof original[0] !== "function" ? original[0] : original[1]
469
+ };
470
+ }
471
+ var UNIQUE_THRESHOLD = 100;
472
+ function filter(array, ...parameters) {
473
+ return findValues("all", array, parameters).matched;
474
+ }
475
+ filter.remove = removeFiltered;
476
+ function removeFiltered(array, ...parameters) {
477
+ return findValues("all", array, parameters).notMatched;
478
+ }
479
+ /**
480
+ * Is the value a constructor function?
481
+ * @param value Value to check
482
+ * @returns `true` if the value is a constructor function, otherwise `false`
483
+ */
484
+ function isConstructor(value) {
485
+ return typeof value === "function" && value.prototype?.constructor === value;
486
+ }
487
+ /**
488
+ * Is the value a number?
489
+ * @param value Value to check
490
+ * @returns `true` if the value is a `number`, otherwise `false`
491
+ */
492
+ function isNumber(value) {
493
+ return typeof value === "number" && !Number.isNaN(value);
494
+ }
495
+ /**
496
+ * Is the value a plain object?
497
+ * @param value Value to check
498
+ * @returns `true` if the value is a plain object, otherwise `false`
499
+ */
500
+ function isPlainObject(value) {
501
+ if (value === null || typeof value !== "object") return false;
502
+ if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
503
+ const prototype = Object.getPrototypeOf(value);
504
+ return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
505
+ }
506
+ /**
507
+ * Is the value a typed array?
508
+ * @param value Value to check
509
+ * @returns `true` if the value is a typed array, otherwise `false`
510
+ */
511
+ function isTypedArray(value) {
512
+ TYPED_ARRAYS ??= new Set([
513
+ Int8Array,
514
+ Uint8Array,
515
+ Uint8ClampedArray,
516
+ Int16Array,
517
+ Uint16Array,
518
+ Int32Array,
519
+ Uint32Array,
520
+ Float32Array,
521
+ Float64Array,
522
+ BigInt64Array,
523
+ BigUint64Array
524
+ ]);
525
+ return TYPED_ARRAYS.has(value?.constructor);
526
+ }
527
+ var TYPED_ARRAYS;
528
+ /**
529
+ * Chunk an array into smaller arrays
530
+ * @param array Array to chunk
531
+ * @param size Size of each chunk _(minimum is `1`, maximum is `5000`; defaults to `5000`)_
532
+ * @returns Array of arrays
533
+ */
534
+ function chunk(array, size) {
535
+ if (!Array.isArray(array)) return [];
536
+ if (array.length === 0) return [];
537
+ const { length } = array;
538
+ const actualSize = typeof size === "number" && size > 0 && size <= MAX_SIZE ? size : MAX_SIZE;
539
+ if (length <= actualSize) return [array];
540
+ const chunks = [];
541
+ let index = 0;
542
+ while (index < length) {
543
+ chunks.push(array.slice(index, index + actualSize));
544
+ index += actualSize;
545
+ }
546
+ return chunks;
547
+ }
548
+ var MAX_SIZE = 5e3;
549
+ function compact(array, strict) {
550
+ if (!Array.isArray(array)) return [];
551
+ if (strict === true) return array.filter(Boolean);
552
+ const { length } = array;
553
+ const compacted = [];
554
+ for (let index = 0; index < length; index += 1) {
555
+ const item = array[index];
556
+ if (item != null) compacted.push(item);
557
+ }
558
+ return compacted;
559
+ }
560
+ function insertChunkedValues(type, array, items, start, deleteCount) {
561
+ const actualDeleteCount = deleteCount < 0 ? 0 : deleteCount;
562
+ const actualStart = Math.min(Math.max(0, start), array.length);
563
+ const chunked = chunk(items);
564
+ const lastIndex = chunked.length - 1;
565
+ let index = Number(chunked.length);
566
+ let returned;
567
+ while (index > 0) {
568
+ index -= 1;
569
+ const spliced = array.splice(actualStart, index === lastIndex ? actualDeleteCount : 0, ...chunked[index]);
570
+ if (returned == null) returned = spliced;
571
+ else returned.push(...spliced);
572
+ }
573
+ if (type === "insert") return array;
574
+ return type === "splice" ? returned : array.length;
575
+ }
576
+ function insertValues(type, array, items, start, deleteCount) {
577
+ const spliceArray = type === "insert" || type === "splice";
578
+ if (!Array.isArray(array) || typeof start !== "number" || !Array.isArray(items) || items.length === 0) return spliceArray ? [] : 0;
579
+ return insertChunkedValues(type, array, items, start, spliceArray ? deleteCount : 0);
580
+ }
581
+ /**
582
+ * Push items into an array _(at the end)_
583
+ * @param array Original array
584
+ * @param pushed Pushed items
585
+ * @returns New length of the array
586
+ */
587
+ function push(array, pushed) {
588
+ return insertValues("push", array, pushed, array.length, 0);
589
+ }
590
+ function aggregate(type, array, key) {
591
+ const length = Array.isArray(array) ? array.length : 0;
592
+ if (length === 0) return {
593
+ count: 0,
594
+ value: NaN
595
+ };
596
+ const aggregator = aggregators[type];
597
+ const callback = getAggregateCallback(key);
598
+ let counted = 0;
599
+ let aggregated = NaN;
600
+ let notNumber = true;
601
+ for (let index = 0; index < length; index += 1) {
602
+ const item = array[index];
603
+ const value = callback == null ? item : callback(item, index, array);
604
+ if (!isNumber(value)) continue;
605
+ aggregated = aggregator(aggregated, value, notNumber);
606
+ counted += 1;
607
+ notNumber = false;
608
+ }
609
+ return {
610
+ count: counted,
611
+ value: aggregated
612
+ };
613
+ }
614
+ function getAggregateCallback(key) {
615
+ if (key == null) return;
616
+ return typeof key === "function" ? key : (item) => item[key];
617
+ }
618
+ function max(array, key) {
619
+ return getAggregated("max", array, key);
620
+ }
621
+ function calculateSum(current, value, notNumber) {
622
+ return notNumber ? value : current + value;
623
+ }
624
+ function getAggregated(type, array, key) {
625
+ const aggregated = aggregate(type, array, key);
626
+ return aggregated.count > 0 ? aggregated.value : NaN;
627
+ }
628
+ var aggregators = {
629
+ average: calculateSum,
630
+ max: (current, value, notNumber) => notNumber || value > current ? value : current,
631
+ min: (current, value, notNumber) => notNumber || value < current ? value : current,
632
+ sum: calculateSum
633
+ };
634
+ /**
635
+ * Get the string value from any value
636
+ * @param value Original value
637
+ * @returns String representation of the value
638
+ */
639
+ function getString(value) {
640
+ if (typeof value === "string") return value;
641
+ if (value == null) return "";
642
+ if (typeof value === "function") return getString(value());
643
+ if (typeof value !== "object") return String(value);
644
+ const asString = String(value.valueOf?.() ?? value);
645
+ return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
646
+ }
647
+ /**
648
+ * Join an array of values into a string
649
+ * @param value Array of values
650
+ * @param delimiter Delimiter to use between values
651
+ * @returns Joined string
652
+ */
653
+ function join(value, delimiter) {
654
+ return compact(value).map(getString).join(typeof delimiter === "string" ? delimiter : "");
655
+ }
656
+ /**
657
+ * Split a string into words _(and other readable parts)_
658
+ * @param value Original string
659
+ * @returns Array of words found in the string
660
+ */
661
+ function words(value) {
662
+ return typeof value === "string" ? value.match(EXPRESSION_WORDS) ?? [] : [];
663
+ }
664
+ var EXPRESSION_WORDS = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
665
+ function getCompareHandlers(owner, options) {
666
+ const { get, register, unregister } = getHandlers(owner, options);
667
+ return {
668
+ register,
669
+ unregister,
670
+ handle(first, second, ...parameters) {
671
+ const handler = get(first, second);
672
+ if (handler == null) return options.callback(first, second, ...parameters);
673
+ return typeof handler === "function" ? handler(first, second) : first[handler](second);
674
+ }
675
+ };
676
+ }
677
+ function getHandlers(owner, options) {
678
+ const handlers = /* @__PURE__ */ new WeakMap();
679
+ return {
680
+ get(first, second) {
681
+ if (isConstructable(first) && isConstructable(second) && first.constructor === second.constructor) return handlers.get(first.constructor);
682
+ },
683
+ register(constructor, handler) {
684
+ if (!isConstructor(constructor) || handler === owner) return;
685
+ let actual = handler ?? options.method;
686
+ if (typeof actual !== "function" && typeof actual !== "string") return;
687
+ if (typeof actual === "string") actual = typeof constructor.prototype[actual] === "function" ? actual : void 0;
688
+ if (actual != null) handlers.set(constructor, actual);
689
+ },
690
+ unregister(constructor) {
691
+ handlers.delete(constructor);
692
+ }
693
+ };
694
+ }
695
+ function isConstructable(value) {
696
+ return typeof value === "object" && value !== null;
697
+ }
698
+ /**
699
+ * Compare two values _(for sorting purposes)_
700
+ * @param first First value
701
+ * @param second Second value
702
+ * @returns `0` if equal; `-1` first comes before second; `1` first comes after second
703
+ */
704
+ function compare(first, second) {
705
+ if (Object.is(first, second)) return 0;
706
+ if (first == null) return -1;
707
+ if (second == null) return 1;
708
+ let comparison = compareValue(first, second, false);
709
+ if (comparison != null) return comparison;
710
+ const firstParts = getComparisonParts(first);
711
+ const secondParts = getComparisonParts(second);
712
+ const length = max([firstParts.length, secondParts.length]);
713
+ const lastIndex = length - 1;
714
+ for (let index = 0; index < length; index += 1) {
715
+ const firstPart = firstParts[index];
716
+ const secondPart = secondParts[index];
717
+ comparison = compareValue(firstPart, secondPart, true);
718
+ if (comparison === 0) {
719
+ if (index === lastIndex) break;
720
+ continue;
721
+ }
722
+ return comparison;
723
+ }
724
+ return 0;
725
+ }
726
+ compare.handlers = getCompareHandlers(compare, {
727
+ callback: (first, second, compareStrings) => {
728
+ if (compareStrings) return getString(first).localeCompare(getString(second));
729
+ },
730
+ method: "compare"
731
+ });
732
+ compare.register = registerComparator;
733
+ compare.unregister = unregisterComparator;
734
+ /**
735
+ * Register a custom comparison handler for a class
736
+ * @param constructor Class constructor
737
+ * @param handler Method name or comparison function _(defaults to `compare`)_
738
+ */
739
+ function registerComparator(constructor, handler) {
740
+ compare.handlers.register(constructor, handler);
741
+ }
742
+ /**
743
+ * Unregister a custom comparison handler for a class
744
+ * @param constructor Class constructor
745
+ */
746
+ function unregisterComparator(constructor) {
747
+ compare.handlers.unregister(constructor);
748
+ }
749
+ function compareNumbers(first, second) {
750
+ const firstNumber = Number(first);
751
+ const secondNumber = Number(second);
752
+ if (firstNumber === secondNumber) return 0;
753
+ return firstNumber > secondNumber ? 1 : -1;
754
+ }
755
+ function compareSymbols(first, second) {
756
+ return getString(first.description ?? first).localeCompare(getString(second.description ?? second));
757
+ }
758
+ function compareValue(first, second, compareStrings) {
759
+ const firstType = typeof first;
760
+ if (firstType === typeof second && firstType in comparators$1) return comparators$1[firstType](first, second);
761
+ if (first instanceof Date && second instanceof Date) return compareNumbers(first.getTime(), second.getTime());
762
+ return compare.handlers.handle(first, second, compareStrings);
763
+ }
764
+ function getComparisonParts(value) {
765
+ if (Array.isArray(value)) return value;
766
+ return typeof value === "object" ? [value] : words(getString(value));
767
+ }
768
+ var comparators$1 = {
769
+ bigint: compareNumbers,
770
+ boolean: compareNumbers,
771
+ number: compareNumbers,
772
+ symbol: compareSymbols
773
+ };
774
+ function getCallback(value, key, forObject) {
775
+ if (key != null) return;
776
+ if (forObject && typeof value.value === "function") return value.value;
777
+ return typeof value === "function" ? value : void 0;
778
+ }
779
+ function getKey(value, forObject) {
780
+ if (forObject && typeof value.key === "string") return value.key;
781
+ return typeof value === "string" ? value : void 0;
782
+ }
783
+ function getModifier(value, modifier, forObject) {
784
+ if (!forObject || typeof value.direction !== "string") return modifier;
785
+ if (value.direction === "ascending") return 1;
786
+ return value.direction === "descending" ? -1 : modifier;
787
+ }
788
+ function getSorter(value, modifier) {
789
+ const forObject = isPlainObject(value);
790
+ const sorter = {
791
+ identifier: "",
792
+ modifier
793
+ };
794
+ sorter.compare = forObject && typeof value.compare === "function" ? value.compare : void 0;
795
+ sorter.key = getKey(value, forObject);
796
+ sorter.modifier = getModifier(value, modifier, forObject);
797
+ sorter.callback = getCallback(value, sorter.key, forObject);
798
+ if (sorter.key != null || sorter.callback != null) {
799
+ sorter.identifier = `${sorter.key ?? sorter.callback}`;
800
+ return sorter;
801
+ }
802
+ }
803
+ function sort(array, first, second) {
804
+ if (!Array.isArray(array)) return [];
805
+ if (array.length < 2) return array;
806
+ const modifier = (first === true || second === true ? "desc" : "asc") === "asc" ? 1 : -1;
807
+ const sorters = (Array.isArray(first) ? first : [first]).map((item) => getSorter(item, modifier)).filter((sorter) => sorter != null).filter((current, index, filtered) => filtered.findIndex((next) => next.identifier === current.identifier) === index);
808
+ const { length } = sorters;
809
+ if (length === 0) return array.sort((firstItem, secondItem) => compare(firstItem, secondItem) * modifier);
810
+ if (length === 1) {
811
+ const sorter = sorters[0];
812
+ const { callback, key } = sorter;
813
+ return array.sort((firstItem, secondItem) => {
814
+ const firstValue = key == null ? callback?.(firstItem) : firstItem[key];
815
+ const secondValue = key == null ? callback?.(secondItem) : secondItem[key];
816
+ return (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ?? compare(firstValue, secondValue)) * sorter.modifier;
817
+ });
818
+ }
819
+ return array.sort((firstItem, secondItem) => {
820
+ for (let index = 0; index < length; index += 1) {
821
+ const sorter = sorters[index];
822
+ const { callback, key } = sorter;
823
+ const firstValue = key == null ? callback?.(firstItem) : firstItem[key];
824
+ const secondValue = key == null ? callback?.(secondItem) : secondItem[key];
825
+ const compared = (sorter.compare?.(firstItem, firstValue, secondItem, secondValue) ?? compare(firstValue, secondValue)) * sorter.modifier;
826
+ if (compared !== 0) return compared;
827
+ }
828
+ return 0;
829
+ });
830
+ }
300
831
  function getMapValues(array, first, second, arrays) {
301
832
  if (!Array.isArray(array)) return /* @__PURE__ */ new Map();
302
833
  const { length } = array;
@@ -321,17 +852,6 @@ toMap.arrays = toMapArrays;
321
852
  function toMapArrays(array, first, second) {
322
853
  return getMapValues(array, first, second, true);
323
854
  }
324
- /**
325
- * Is the value a plain object?
326
- * @param value Value to check
327
- * @returns `true` if the value is a plain object, otherwise `false`
328
- */
329
- function isPlainObject(value) {
330
- if (value === null || typeof value !== "object") return false;
331
- if (Symbol.toStringTag in value || Symbol.iterator in value) return false;
332
- const prototype = Object.getPrototypeOf(value);
333
- return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
334
- }
335
855
  var DataManager = class {
336
856
  handlers = Object.freeze({
337
857
  add: (data) => void this.add(data, true),
@@ -358,10 +878,9 @@ var DataManager = class {
358
878
  }
359
879
  async add(data, render) {
360
880
  const { field, values } = this;
361
- values.objects.array.push(...data);
881
+ push(values.objects.array, data);
362
882
  values.objects.mapped = toMap(values.objects.array, field);
363
- values.keys.original = [...values.objects.mapped.keys()];
364
- if (render) this.managers.virtualization.update(true);
883
+ if (render) this.render();
365
884
  }
366
885
  clear() {
367
886
  if (this.values.objects.array.length > 0) this.set([]);
@@ -372,6 +891,7 @@ var DataManager = class {
372
891
  values.keys.active = void 0;
373
892
  values.keys.original.length = 0;
374
893
  values.objects.array.length = 0;
894
+ this.handlers = void 0;
375
895
  }
376
896
  get(active) {
377
897
  const { values } = this;
@@ -390,16 +910,20 @@ var DataManager = class {
390
910
  values.keys.original.splice(values.keys.original.indexOf(key), 1);
391
911
  managers.row.remove(key);
392
912
  }
393
- if (render) this.managers.virtualization.update(true);
913
+ if (render) this.render();
914
+ }
915
+ 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);
394
921
  }
395
922
  set(data) {
396
923
  const { field, values } = this;
397
- const mapped = toMap(data, field);
398
- values.keys.active = void 0;
399
- values.keys.original = [...mapped.keys()];
400
- values.objects.mapped = mapped;
924
+ values.objects.mapped = toMap(data, field);
401
925
  values.objects.array = data;
402
- this.managers.virtualization.update(true);
926
+ this.render();
403
927
  }
404
928
  async synchronize(data, remove) {
405
929
  const { field, values } = this;
@@ -421,7 +945,7 @@ var DataManager = class {
421
945
  }
422
946
  await this.update(updated);
423
947
  if (add.length > 0) await this.add(add, false);
424
- if (add.length > 0 || (remove ?? false)) this.managers.virtualization.update(true);
948
+ if (add.length > 0 || (remove ?? false)) this.render();
425
949
  }
426
950
  async update(data) {
427
951
  const { field, managers, values } = this;
@@ -440,69 +964,6 @@ var DataManager = class {
440
964
  }
441
965
  }
442
966
  };
443
- function removeRow(pool, row) {
444
- if (row.element != null) {
445
- row.element.innerHTML = "";
446
- pool.rows.push(row.element);
447
- row.element.remove();
448
- row.element = void 0;
449
- }
450
- row.cells = {};
451
- }
452
- function renderRow(managers, row) {
453
- const element = row.element ?? managers.virtualization.pool.rows.shift() ?? createRow();
454
- row.element = element;
455
- element.dataset.key = String(row.key);
456
- element.innerHTML = "";
457
- const columns = managers.column.items;
458
- const { length } = columns;
459
- const data = managers.data.values.objects.mapped.get(row.key);
460
- if (data == null) return;
461
- for (let index = 0; index < length; index += 1) {
462
- const { options } = columns[index];
463
- managers.virtualization.pool.cells[options.field] ??= [];
464
- const cell = managers.virtualization.pool.cells[columns[index].options.field].shift() ?? createCell(options.width);
465
- cell.textContent = String(data[options.field]);
466
- row.cells[options.field] = cell;
467
- element.append(cell);
468
- }
469
- }
470
- var RowComponent = class {
471
- cells = {};
472
- element;
473
- constructor(key) {
474
- this.key = key;
475
- }
476
- };
477
- var RowManager = class {
478
- components = /* @__PURE__ */ new Map();
479
- height;
480
- constructor(managers, rowHeight) {
481
- this.managers = managers;
482
- this.height = rowHeight;
483
- }
484
- destroy() {
485
- this.components.clear();
486
- }
487
- get(key) {
488
- let row = this.components.get(key);
489
- if (row == null) {
490
- row = new RowComponent(key);
491
- this.components.set(key, row);
492
- }
493
- return row;
494
- }
495
- has(key) {
496
- return this.components.has(key);
497
- }
498
- remove(key) {
499
- this.components.delete(key);
500
- }
501
- update(key) {
502
- const row = this.components.get(key);
503
- if (row != null) renderRow(this.managers, row);
504
- }
505
- };
506
967
  function addDelegatedHandler(doc, type, name, passive) {
507
968
  if (DELEGATED.has(name)) return;
508
969
  DELEGATED.add(name);
@@ -607,6 +1068,863 @@ function on(target, type, listener, options) {
607
1068
  target.removeEventListener(type, listener, extended);
608
1069
  };
609
1070
  }
1071
+ function findAncestor(origin, selector) {
1072
+ const element = getElement(origin);
1073
+ if (element == null || selector == null) return null;
1074
+ if (typeof selector === "string") {
1075
+ if (Element.prototype.matches.call(element, selector)) return element;
1076
+ return Element.prototype.closest.call(element, selector);
1077
+ }
1078
+ if (typeof selector !== "function") return null;
1079
+ if (selector(element)) return element;
1080
+ let parent = element.parentElement;
1081
+ while (parent != null && !selector(parent)) {
1082
+ if (parent === document.body) return null;
1083
+ parent = parent.parentElement;
1084
+ }
1085
+ return parent;
1086
+ }
1087
+ function getElement(origin) {
1088
+ if (origin instanceof Element) return origin;
1089
+ return origin instanceof Event && origin.target instanceof Element ? origin.target : void 0;
1090
+ }
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
+ 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 });
1264
+ }
1265
+ 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
+ }
1276
+ }
1277
+ onSort(event, target) {
1278
+ const { managers } = this;
1279
+ const direction = target.getAttribute("data-sort-direction");
1280
+ const field = target.getAttribute("data-field");
1281
+ if (field != null) managers.sort.toggle(event, field, direction);
1282
+ }
1283
+ };
1284
+ /**
1285
+ * Clamp a number between a minimum and maximum value
1286
+ * @param value Value to clamp
1287
+ * @param minimum Minimum value
1288
+ * @param maximum Maximum value
1289
+ * @param loop If `true`, the value will loop around when smaller than the minimum or larger than the maximum _(defaults to `false`)_
1290
+ * @returns Clamped value
1291
+ */
1292
+ function clamp(value, minimum, maximum, loop) {
1293
+ if (![
1294
+ value,
1295
+ minimum,
1296
+ maximum
1297
+ ].every(isNumber)) return NaN;
1298
+ if (value < minimum) return loop === true ? maximum : minimum;
1299
+ return value > maximum ? loop === true ? minimum : maximum : value;
1300
+ }
1301
+ /**
1302
+ * Get the number value from an unknown value _(based on Lodash)_
1303
+ * @param value Original value
1304
+ * @returns Original value as a number, or `NaN` if the value is unable to be parsed
1305
+ */
1306
+ function getNumber(value) {
1307
+ if (typeof value === "number") return value;
1308
+ if (typeof value === "bigint" || typeof value === "boolean") return Number(value);
1309
+ if (value == null || typeof value === "symbol") return NaN;
1310
+ if (typeof value === "function") return getNumber(value());
1311
+ let parsed = value.valueOf();
1312
+ if (typeof parsed === "object") parsed = parsed.toString();
1313
+ if (typeof parsed !== "string") return getNumber(parsed);
1314
+ const trimmed = parsed.trim();
1315
+ if (trimmed.length === 0) return NaN;
1316
+ if (EXPRESSION_ZEROISH.test(parsed)) return 0;
1317
+ const isBinary = EXPRESSION_BINARY.test(trimmed);
1318
+ if (isBinary || EXPRESSION_OCTAL.test(trimmed)) return Number.parseInt(trimmed.slice(2), isBinary ? 2 : OCTAL_VALUE);
1319
+ return Number(EXPRESSION_HEX.test(trimmed) ? trimmed : trimmed.replace(EXPRESSION_UNDERSCORE, ""));
1320
+ }
1321
+ var EXPRESSION_BINARY = /^0b[01]+$/i;
1322
+ var EXPRESSION_HEX = /^0x[0-9a-f]+$/i;
1323
+ var EXPRESSION_OCTAL = /^0o[0-7]+$/i;
1324
+ var EXPRESSION_UNDERSCORE = /_/g;
1325
+ var EXPRESSION_ZEROISH = /^\s*0+\s*$/;
1326
+ var OCTAL_VALUE = 8;
1327
+ function getSizedMaximum(first, second) {
1328
+ let actual;
1329
+ if (typeof first === "number") actual = first;
1330
+ else actual = typeof second === "number" ? second : MAXIMUM_DEFAULT;
1331
+ return clamp(actual, 1, MAXIMUM_ABSOLUTE);
1332
+ }
1333
+ var MAXIMUM_ABSOLUTE = 16777216;
1334
+ var MAXIMUM_DEFAULT = 1048576;
1335
+ /**
1336
+ * A Map with a maximum size
1337
+ *
1338
+ * Behavior is similar to a _LRU_-cache, where the least recently used entries are removed
1339
+ */
1340
+ var SizedMap = class extends Map {
1341
+ /**
1342
+ * The maximum size of the Map
1343
+ */
1344
+ #maximumSize;
1345
+ /**
1346
+ * Is the Map full?
1347
+ */
1348
+ get full() {
1349
+ return this.size >= this.#maximumSize;
1350
+ }
1351
+ get maximum() {
1352
+ return this.#maximumSize;
1353
+ }
1354
+ constructor(first, second) {
1355
+ const maximum = getSizedMaximum(first, second);
1356
+ super();
1357
+ this.#maximumSize = maximum;
1358
+ if (Array.isArray(first)) {
1359
+ const { length } = first;
1360
+ if (length <= maximum) for (let index = 0; index < length; index += 1) this.set(...first[index]);
1361
+ else for (let index = 0; index < maximum; index += 1) this.set(...first[length - maximum + index]);
1362
+ }
1363
+ }
1364
+ /**
1365
+ * @inheritdoc
1366
+ */
1367
+ get(key) {
1368
+ const value = super.get(key);
1369
+ if (value !== void 0 || this.has(key)) this.set(key, value);
1370
+ return value;
1371
+ }
1372
+ /**
1373
+ * @inheritdoc
1374
+ */
1375
+ set(key, value) {
1376
+ if (this.has(key)) this.delete(key);
1377
+ else if (this.size >= this.#maximumSize) this.delete(this.keys().next().value);
1378
+ return super.set(key, value);
1379
+ }
1380
+ };
1381
+ var Memoized = class {
1382
+ #state;
1383
+ /**
1384
+ * Maximum cache size
1385
+ */
1386
+ get maximum() {
1387
+ return this.#state.cache?.maximum ?? NaN;
1388
+ }
1389
+ /**
1390
+ * Current cache size
1391
+ */
1392
+ get size() {
1393
+ return this.#state.cache?.size ?? NaN;
1394
+ }
1395
+ constructor(callback, options) {
1396
+ const cache = new SizedMap(options.cacheSize);
1397
+ const getter = (...parameters) => {
1398
+ const key = options.cacheKey?.(...parameters) ?? (parameters.length === 1 ? parameters[0] : join(parameters.map(getString), "_"));
1399
+ if (cache.has(key)) return cache.get(key);
1400
+ const value = callback(...parameters);
1401
+ cache.set(key, value);
1402
+ return value;
1403
+ };
1404
+ this.#state = {
1405
+ cache,
1406
+ getter
1407
+ };
1408
+ }
1409
+ /**
1410
+ * Clear the cache
1411
+ */
1412
+ clear() {
1413
+ this.#state.cache?.clear();
1414
+ }
1415
+ /**
1416
+ * Delete a result from the cache
1417
+ * @param key Key to delete
1418
+ * @returns `true` if the key existed and was removed, otherwise `false`
1419
+ */
1420
+ delete(key) {
1421
+ return this.#state.cache?.delete(key) ?? false;
1422
+ }
1423
+ /**
1424
+ * Destroy the instance _(clearing its cache and removing its callback)_
1425
+ */
1426
+ destroy() {
1427
+ this.#state.cache?.clear();
1428
+ this.#state.cache = void 0;
1429
+ this.#state.getter = void 0;
1430
+ }
1431
+ /**
1432
+ * Get a result from the cache
1433
+ * @param key Key to get
1434
+ * @returns Cached result or `undefined` if it does not exist
1435
+ */
1436
+ get(key) {
1437
+ return this.#state.cache?.get(key);
1438
+ }
1439
+ /**
1440
+ * Does the result exist?
1441
+ * @param key Key to check
1442
+ * @returns `true` if the result exists, otherwise `false`
1443
+ */
1444
+ has(key) {
1445
+ return this.#state.cache?.has(key) ?? false;
1446
+ }
1447
+ /**
1448
+ * Run the callback with the provided parameters
1449
+ * @param parameters Parameters to pass to the callback
1450
+ * @returns Cached or computed _(then cached)_ result
1451
+ */
1452
+ run(...parameters) {
1453
+ if (this.#state.cache == null || this.#state.getter == null) throw new Error("The Memoized instance has been destroyed");
1454
+ return this.#state.getter(...parameters);
1455
+ }
1456
+ };
1457
+ function getMemoizationOptions(input) {
1458
+ const { cacheKey, cacheSize } = isPlainObject(input) ? input : {};
1459
+ return {
1460
+ cacheKey: typeof cacheKey === "function" ? cacheKey : void 0,
1461
+ cacheSize: typeof cacheSize === "number" && cacheSize > 0 ? cacheSize : DEFAULT_CACHE_SIZE
1462
+ };
1463
+ }
1464
+ /**
1465
+ * Memoize a function, caching and retrieving results based on the first parameter
1466
+ * @param callback Callback to memoize
1467
+ * @param options Memoization options
1468
+ * @returns Memoized instance
1469
+ */
1470
+ function memoize(callback, options) {
1471
+ return new Memoized(callback, getMemoizationOptions(options));
1472
+ }
1473
+ var DEFAULT_CACHE_SIZE = 1024;
1474
+ /**
1475
+ * Check if a string ends with a specified substring
1476
+ * @param haystack String to look in
1477
+ * @param needle String to look for
1478
+ * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
1479
+ * @returns `true` if the string ends with the given substring, otherwise `false`
1480
+ */
1481
+ function endsWith(haystack, needle, ignoreCase) {
1482
+ return match("endsWith", haystack, needle, ignoreCase === true);
1483
+ }
1484
+ /**
1485
+ * Check if a string includes a specified substring
1486
+ * @param haystack String to look in
1487
+ * @param needle String to look for
1488
+ * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
1489
+ * @returns `true` if the string includes the given substring, otherwise `false`
1490
+ */
1491
+ function includes(haystack, needle, ignoreCase) {
1492
+ return match("includes", haystack, needle, ignoreCase === true);
1493
+ }
1494
+ function match(type, haystack, needle, ignoreCase) {
1495
+ if (typeof haystack !== "string" || typeof needle !== "string") return false;
1496
+ matchMemoizers[type] ??= memoize(matchCallback.bind(type));
1497
+ return matchMemoizers[type].run(haystack, needle, ignoreCase);
1498
+ }
1499
+ function matchCallback(haystack, needle, ignoreCase) {
1500
+ return (ignoreCase ? haystack.toLocaleLowerCase() : haystack)[this](ignoreCase ? needle.toLocaleLowerCase() : needle);
1501
+ }
1502
+ /**
1503
+ * Check if a string starts with a specified substring
1504
+ * @param haystack String to look in
1505
+ * @param needle String to look for
1506
+ * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
1507
+ * @returns `true` if the string starts with the given substring, otherwise `false`
1508
+ */
1509
+ function startsWith(haystack, needle, ignoreCase) {
1510
+ return match("startsWith", haystack, needle, ignoreCase === true);
1511
+ }
1512
+ var matchMemoizers = {};
1513
+ function equal(first, second, options) {
1514
+ return equalValue(first, second, getEqualOptions(options));
1515
+ }
1516
+ equal.handlers = getCompareHandlers(equal, { callback: Object.is });
1517
+ equal.initialize = initializeEqualizer;
1518
+ equal.register = registerEqualizer;
1519
+ equal.unregister = unregisterEqualizer;
1520
+ function equalArray(first, second, options) {
1521
+ const { length } = first;
1522
+ if (length !== second.length) return false;
1523
+ let offset = 0;
1524
+ if (length >= ARRAY_THRESHOLD) {
1525
+ offset = Math.round(length / ARRAY_PEEK_PERCENTAGE);
1526
+ offset = offset > ARRAY_THRESHOLD ? ARRAY_THRESHOLD : offset;
1527
+ for (let index = 0; index < offset; index += 1) if (!(equalValue(first[index], second[index], options) && equalValue(first[length - index - 1], second[length - index - 1], options))) return false;
1528
+ }
1529
+ const firstChunks = chunk(first.slice(offset, length - offset), ARRAY_THRESHOLD);
1530
+ const secondChunks = chunk(second.slice(offset, length - offset), ARRAY_THRESHOLD);
1531
+ const chunksLength = firstChunks.length;
1532
+ for (let chunkIndex = 0; chunkIndex < chunksLength; chunkIndex += 1) {
1533
+ const firstChunk = firstChunks[chunkIndex];
1534
+ const secondChunk = secondChunks[chunkIndex];
1535
+ const chunkLength = firstChunk.length;
1536
+ for (let index = 0; index < chunkLength; index += 1) if (!equalValue(firstChunk[index], secondChunk[index], options)) return false;
1537
+ }
1538
+ return true;
1539
+ }
1540
+ function equalArrayBuffer(first, second, options) {
1541
+ return first.byteLength === second.byteLength ? equalArray(new Uint8Array(first), new Uint8Array(second), options) : false;
1542
+ }
1543
+ function equalDataView(first, second, options) {
1544
+ return first.byteOffset === second.byteOffset ? equalArrayBuffer(first.buffer, second.buffer, options) : false;
1545
+ }
1546
+ function equalMap(first, second, options) {
1547
+ const { size } = first;
1548
+ if (size !== second.size) return false;
1549
+ const firstKeys = [...first.keys()];
1550
+ const secondKeys = [...second.keys()];
1551
+ if (firstKeys.some((key) => !secondKeys.includes(key))) return false;
1552
+ for (let index = 0; index < size; index += 1) {
1553
+ const key = firstKeys[index];
1554
+ if (!equalValue(first.get(key), second.get(key), options)) return false;
1555
+ }
1556
+ return true;
1557
+ }
1558
+ function equalPlainObject(first, second, options) {
1559
+ let firstKeys = [...Object.keys(first), ...Object.getOwnPropertySymbols(first)];
1560
+ let secondKeys = [...Object.keys(second), ...Object.getOwnPropertySymbols(second)];
1561
+ if (options.ignoreKeys.enabled || options.ignoreExpressions.enabled) {
1562
+ firstKeys = firstKeys.filter((key) => filterKey(key, options));
1563
+ secondKeys = secondKeys.filter((key) => filterKey(key, options));
1564
+ }
1565
+ const { length } = firstKeys;
1566
+ if (length !== secondKeys.length || firstKeys.some((key) => !secondKeys.includes(key))) return false;
1567
+ for (let index = 0; index < length; index += 1) {
1568
+ const key = firstKeys[index];
1569
+ if (!equalValue(first[key], second[key], options)) return false;
1570
+ }
1571
+ return true;
1572
+ }
1573
+ function equalProperties(first, second, properties, options) {
1574
+ const { length } = properties;
1575
+ for (let index = 0; index < length; index += 1) {
1576
+ const property = properties[index];
1577
+ if (!equalValue(first[property], second[property], options)) return false;
1578
+ }
1579
+ return true;
1580
+ }
1581
+ function equalSet(first, second, options) {
1582
+ const { size } = first;
1583
+ if (size !== second.size) return false;
1584
+ const firstValues = [...first];
1585
+ const secondValues = [...second];
1586
+ for (let index = 0; index < size; index += 1) {
1587
+ const firstValue = firstValues[index];
1588
+ if (!secondValues.some((secondValue) => equalValue(firstValue, secondValue, options))) return false;
1589
+ }
1590
+ return true;
1591
+ }
1592
+ function equalTypedArray(first, second) {
1593
+ if (first.constructor !== second.constructor) return false;
1594
+ if (first.byteLength !== second.byteLength) return false;
1595
+ const { length } = first;
1596
+ for (let index = 0; index < length; index += 1) if (first[index] !== second[index]) return false;
1597
+ return true;
1598
+ }
1599
+ function equalValue(first, second, options) {
1600
+ if (options.relaxedNullish && first == null && second == null) return true;
1601
+ switch (true) {
1602
+ case Object.is(first, second): return true;
1603
+ case first == null || second == null: return first === second;
1604
+ case typeof first !== typeof second: return false;
1605
+ case typeof first === "string" && options.ignoreCase === true: return Object.is(first.toLocaleLowerCase(), second.toLocaleLowerCase());
1606
+ case first instanceof ArrayBuffer && second instanceof ArrayBuffer: return equalArrayBuffer(first, second, options);
1607
+ case first instanceof Date && second instanceof Date: return Object.is(Number(first), Number(second));
1608
+ case first instanceof DataView && second instanceof DataView: return equalDataView(first, second, options);
1609
+ case first instanceof Error && second instanceof Error: return equalProperties(first, second, ["name", "message"], options);
1610
+ case first instanceof Map && second instanceof Map: return equalMap(first, second, options);
1611
+ case first instanceof RegExp && second instanceof RegExp: return equalProperties(first, second, ["source", "flags"], options);
1612
+ case first instanceof Set && second instanceof Set: return equalSet(first, second, options);
1613
+ case Array.isArray(first) && Array.isArray(second): return equalArray(first, second, options);
1614
+ case isPlainObject(first) && isPlainObject(second): return equalPlainObject(first, second, options);
1615
+ case isTypedArray(first) && isTypedArray(second): return equalTypedArray(first, second);
1616
+ default: return equal.handlers.handle(first, second, options);
1617
+ }
1618
+ }
1619
+ /**
1620
+ * Create an equalizer with predefined options
1621
+ * @param options Comparison options
1622
+ * @returns Equalizer function
1623
+ */
1624
+ function initializeEqualizer(options) {
1625
+ const actual = getEqualOptions(options);
1626
+ const equalizer = (first, second) => equalValue(first, second, actual);
1627
+ equalizer.register = registerEqualizer;
1628
+ equalizer.unregister = unregisterEqualizer;
1629
+ return equalizer;
1630
+ }
1631
+ /**
1632
+ * Register a equality comparison function for a specific class
1633
+ * @param constructor Class constructor
1634
+ * @param fn Comparison function
1635
+ */
1636
+ function registerEqualizer(constructor, handler) {
1637
+ equal.handlers.register(constructor, handler);
1638
+ }
1639
+ /**
1640
+ * Unregister a equality comparison handler for a specific class
1641
+ * @param constructor Class constructor
1642
+ */
1643
+ function unregisterEqualizer(constructor) {
1644
+ equal.handlers.unregister(constructor);
1645
+ }
1646
+ function filterKey(key, options) {
1647
+ if (typeof key !== "string") return true;
1648
+ if (options.ignoreExpressions.enabled && options.ignoreExpressions.values.some((expression) => expression.test(key))) return false;
1649
+ if (options.ignoreKeys.enabled && options.ignoreKeys.values.has(key)) return false;
1650
+ return true;
1651
+ }
1652
+ function getEqualOptions(input) {
1653
+ const options = {
1654
+ ignoreCase: false,
1655
+ ignoreExpressions: {
1656
+ enabled: false,
1657
+ values: []
1658
+ },
1659
+ ignoreKeys: {
1660
+ enabled: false,
1661
+ values: /* @__PURE__ */ new Set()
1662
+ },
1663
+ relaxedNullish: false
1664
+ };
1665
+ if (typeof input === "boolean") {
1666
+ options.ignoreCase = input;
1667
+ return options;
1668
+ }
1669
+ if (!isPlainObject(input)) return options;
1670
+ options.ignoreCase = typeof input.ignoreCase === "boolean" ? input.ignoreCase : false;
1671
+ options.ignoreExpressions.values = (Array.isArray(input.ignoreKeys) ? input.ignoreKeys : [input.ignoreKeys]).filter((key) => key instanceof RegExp);
1672
+ options.ignoreKeys.values = new Set((Array.isArray(input.ignoreKeys) ? input.ignoreKeys : [input.ignoreKeys]).filter((key) => typeof key === "string"));
1673
+ options.ignoreExpressions.enabled = options.ignoreExpressions.values.length > 0;
1674
+ options.ignoreKeys.enabled = options.ignoreKeys.values.size > 0;
1675
+ options.relaxedNullish = input.relaxedNullish === true;
1676
+ return options;
1677
+ }
1678
+ var ARRAY_PEEK_PERCENTAGE = 10;
1679
+ var ARRAY_THRESHOLD = 100;
1680
+ var FilterManager = class {
1681
+ handlers = Object.freeze({
1682
+ add: (item) => this.add(item),
1683
+ clear: () => this.clear(),
1684
+ remove: (value) => this.remove(value),
1685
+ set: (items) => this.set(items)
1686
+ });
1687
+ items = {};
1688
+ constructor(managers) {
1689
+ this.managers = managers;
1690
+ }
1691
+ add(item) {
1692
+ if (this.items[item.field] == null) this.items[item.field] = [];
1693
+ else if (this.items[item.field].findIndex((existing) => equal(existing, item)) !== -1) return;
1694
+ this.items[item.field].push(item);
1695
+ this.filter();
1696
+ }
1697
+ clear() {
1698
+ if (Object.keys(this.items).length > 0) {
1699
+ this.items = {};
1700
+ this.filter();
1701
+ }
1702
+ }
1703
+ destroy() {
1704
+ this.handlers = void 0;
1705
+ this.items = {};
1706
+ }
1707
+ filter() {
1708
+ const { managers } = this;
1709
+ const filtered = [];
1710
+ const filters = Object.entries(this.items);
1711
+ const keysLength = managers.data.values.keys.original.length;
1712
+ 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);
1715
+ if (row == null) continue;
1716
+ filterLoop: for (let filterIndex = 0; filterIndex < filters.length; filterIndex += 1) {
1717
+ const [field, items] = filters[filterIndex];
1718
+ const value = row[field];
1719
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
1720
+ const filter = items[itemIndex];
1721
+ if (comparators[filter.comparison](value, filter.value)) continue filterLoop;
1722
+ }
1723
+ continue rowLoop;
1724
+ }
1725
+ filtered.push(key);
1726
+ }
1727
+ managers.data.values.keys.active = filtered;
1728
+ if (managers.sort.items.length > 0) managers.sort.sort();
1729
+ else managers.virtualization.update(true, true);
1730
+ }
1731
+ remove(value) {
1732
+ if (typeof value === "string") {
1733
+ if (this.items[value] == null) return;
1734
+ this.items = {};
1735
+ } else {
1736
+ const { field } = value;
1737
+ if (this.items[field] == null) return;
1738
+ if (this.items[field].findIndex((item) => equal(item, value)) === -1) return;
1739
+ }
1740
+ this.filter();
1741
+ }
1742
+ set(items) {
1743
+ const keyed = {};
1744
+ const { length } = items;
1745
+ for (let index = 0; index < length; index += 1) {
1746
+ const item = items[index];
1747
+ keyed[item.field] ??= [];
1748
+ keyed[item.field].push(item);
1749
+ }
1750
+ this.items = keyed;
1751
+ this.filter();
1752
+ }
1753
+ };
1754
+ const comparators = {
1755
+ contains: (row, filter) => includes(getString(row), getString(filter), true),
1756
+ "ends-with": (row, filter) => endsWith(getString(row), getString(filter), true),
1757
+ equals: (row, filter) => equalizer(row, filter),
1758
+ "greater-than": (row, filter) => getNumber(row) > getNumber(filter),
1759
+ "greater-than-or-equal": (row, filter) => getNumber(row) >= getNumber(filter),
1760
+ "less-than": (row, filter) => getNumber(row) < getNumber(filter),
1761
+ "less-than-or-equal": (row, filter) => getNumber(row) <= getNumber(filter),
1762
+ "not-contains": (row, filter) => !includes(getString(row), getString(filter), true),
1763
+ "not-equals": (row, filter) => !equalizer(row, filter),
1764
+ "starts-with": (row, filter) => startsWith(getString(row), getString(filter), true)
1765
+ };
1766
+ const equalizer = equal.initialize({ ignoreCase: true });
1767
+ function removeRow(pool, row) {
1768
+ if (row.element != null) {
1769
+ row.element.innerHTML = "";
1770
+ pool.rows.push(row.element);
1771
+ row.element.remove();
1772
+ row.element = void 0;
1773
+ }
1774
+ row.cells = {};
1775
+ }
1776
+ function renderRow(managers, row) {
1777
+ const element = row.element ?? managers.virtualization.pool.rows.shift() ?? createRow();
1778
+ row.element = element;
1779
+ element.dataset.key = String(row.key);
1780
+ element.innerHTML = "";
1781
+ const columns = managers.column.items;
1782
+ const { length } = columns;
1783
+ const data = managers.data.values.objects.mapped.get(row.key);
1784
+ if (data == null) return;
1785
+ for (let index = 0; index < length; index += 1) {
1786
+ 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);
1789
+ cell.textContent = String(data[options.field]);
1790
+ row.cells[options.field] = cell;
1791
+ element.append(cell);
1792
+ }
1793
+ }
1794
+ var RowComponent = class {
1795
+ cells = {};
1796
+ element;
1797
+ constructor(key) {
1798
+ this.key = key;
1799
+ }
1800
+ };
1801
+ var RowManager = class {
1802
+ components = /* @__PURE__ */ new Map();
1803
+ height;
1804
+ constructor(managers, rowHeight) {
1805
+ this.managers = managers;
1806
+ this.height = rowHeight;
1807
+ }
1808
+ 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();
1813
+ }
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);
1819
+ }
1820
+ return row;
1821
+ }
1822
+ has(key) {
1823
+ return this.components.has(key);
1824
+ }
1825
+ remove(key) {
1826
+ const row = this.components.get(key);
1827
+ if (row != null) {
1828
+ removeRow(this.managers.virtualization.pool, row);
1829
+ this.components.delete(key);
1830
+ }
1831
+ }
1832
+ update(key) {
1833
+ const row = this.components.get(key);
1834
+ if (row != null) renderRow(this.managers, row);
1835
+ }
1836
+ };
1837
+ var SortManager = class {
1838
+ handlers = Object.freeze({
1839
+ add: (field, direction) => this.add(field, direction),
1840
+ flip: (field) => this.flip(field),
1841
+ clear: () => this.clear(),
1842
+ remove: (field) => this.remove(field),
1843
+ set: (items) => this.set(items)
1844
+ });
1845
+ items = [];
1846
+ constructor(managers) {
1847
+ this.managers = managers;
1848
+ }
1849
+ add(field, direction) {
1850
+ if (this.items.findIndex((item) => item.key === field) > -1) return;
1851
+ this.items.push({
1852
+ key: field,
1853
+ direction: direction ?? "ascending"
1854
+ });
1855
+ this.sort();
1856
+ }
1857
+ addOrSet(event, field) {
1858
+ if (event.ctrlKey || event.metaKey) this.add(field);
1859
+ else this.set([{
1860
+ field,
1861
+ direction: "ascending"
1862
+ }]);
1863
+ }
1864
+ clear() {
1865
+ if (this.items.length > 0) {
1866
+ this.items.length = 0;
1867
+ this.sort();
1868
+ }
1869
+ }
1870
+ destroy() {
1871
+ this.handlers = void 0;
1872
+ this.items.length = 0;
1873
+ }
1874
+ flip(field) {
1875
+ const item = this.items.find((item) => item.key === field);
1876
+ if (item == null) return;
1877
+ item.direction = item.direction === "ascending" ? "descending" : "ascending";
1878
+ this.sort();
1879
+ }
1880
+ remove(field) {
1881
+ const index = this.items.findIndex((item) => item.key === field);
1882
+ if (index > -1) {
1883
+ this.items.splice(index, 1);
1884
+ this.sort();
1885
+ }
1886
+ }
1887
+ removeOrClear(event, field) {
1888
+ if (event.ctrlKey || event.metaKey) this.remove(field);
1889
+ else this.clear();
1890
+ }
1891
+ set(items) {
1892
+ this.items.splice(0, this.items.length, ...items.map((item) => ({
1893
+ key: item.field,
1894
+ direction: item.direction
1895
+ })));
1896
+ this.sort();
1897
+ }
1898
+ sort() {
1899
+ const { items, managers } = this;
1900
+ const { length } = managers.column.items;
1901
+ for (let index = 0; index < length; index += 1) {
1902
+ const column = managers.column.items[index];
1903
+ const sorterIndex = items.findIndex((item) => item.key === column.options.field);
1904
+ const sorterItem = items[sorterIndex];
1905
+ setAttributes(column.elements.wrapper, {
1906
+ "aria-sort": sorterItem == null ? "none" : items.length > 1 ? "other" : sorterItem.direction,
1907
+ "data-sort-direction": sorterItem == null ? void 0 : sorterItem.direction
1908
+ });
1909
+ setAttribute(column.elements.sorter, "data-sort-position", sorterIndex > -1 && items.length > 1 ? sorterIndex + 1 : void 0);
1910
+ }
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);
1913
+ }
1914
+ toggle(event, field, direction) {
1915
+ switch (direction) {
1916
+ case "ascending":
1917
+ this.flip(field);
1918
+ return;
1919
+ case "descending":
1920
+ this.removeOrClear(event, field);
1921
+ return;
1922
+ default:
1923
+ this.addOrSet(event, field);
1924
+ return;
1925
+ }
1926
+ }
1927
+ };
610
1928
  function getRange(down) {
611
1929
  const { components, managers } = this;
612
1930
  const { clientHeight, scrollTop } = components.body.elements.group;
@@ -656,6 +1974,8 @@ var VirtualizationManager = class {
656
1974
  }
657
1975
  this.pool.cells = {};
658
1976
  this.pool.rows = [];
1977
+ this.listener = void 0;
1978
+ this.visible = void 0;
659
1979
  }
660
1980
  removeCells(fields) {
661
1981
  const { length } = fields;
@@ -671,13 +1991,13 @@ var VirtualizationManager = class {
671
1991
  this.fragment.replaceChildren();
672
1992
  return this.fragment;
673
1993
  }
674
- update(down) {
1994
+ update(down, rerender) {
675
1995
  const { components, managers, pool, visible } = this;
676
1996
  components.body.elements.faker.style.height = `${managers.data.size * managers.row.height}px`;
677
1997
  const indices = /* @__PURE__ */ new Set();
678
1998
  const range = getRange.call(this, down);
679
1999
  for (let index = range.start; index <= range.end; index += 1) indices.add(index);
680
- let remove = false;
2000
+ let remove = rerender ?? false;
681
2001
  for (const [index, row] of visible) {
682
2002
  if (!managers.row.has(row.key) || !indices.has(index)) remove = true;
683
2003
  if (remove) {
@@ -714,10 +2034,15 @@ var Tabela = class {
714
2034
  #managers = {
715
2035
  column: void 0,
716
2036
  data: void 0,
2037
+ event: void 0,
2038
+ filter: void 0,
717
2039
  row: void 0,
2040
+ sort: void 0,
718
2041
  virtualization: void 0
719
2042
  };
720
2043
  data;
2044
+ filter;
2045
+ sort;
721
2046
  get key() {
722
2047
  return this.#key;
723
2048
  }
@@ -733,11 +2058,16 @@ var Tabela = class {
733
2058
  this.#components.footer = new FooterComponent();
734
2059
  this.#managers.column = new ColumnManager(this.#managers, this.#components, options.columns);
735
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);
736
2063
  this.#managers.row = new RowManager(this.#managers, options.rowHeight);
2064
+ this.#managers.sort = new SortManager(this.#managers);
737
2065
  this.#managers.virtualization = new VirtualizationManager(this.#managers, this.#components);
738
2066
  element.append(this.#components.header.elements.group, this.#components.body.elements.group, this.#components.footer.elements.group);
739
2067
  this.#managers.data.set(options.data);
740
2068
  this.data = this.#managers.data.handlers;
2069
+ this.filter = this.#managers.filter.handlers;
2070
+ this.sort = this.#managers.sort.handlers;
741
2071
  }
742
2072
  destroy() {
743
2073
  const components = this.#components;
@@ -748,7 +2078,10 @@ var Tabela = class {
748
2078
  components.header.destroy();
749
2079
  managers.column.destroy();
750
2080
  managers.data.destroy();
2081
+ managers.event.destroy();
2082
+ managers.filter.destroy();
751
2083
  managers.row.destroy();
2084
+ managers.sort.destroy();
752
2085
  managers.virtualization.destroy();
753
2086
  element.innerHTML = "";
754
2087
  element.role = "";