@oscarpalmer/tabela 0.8.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.
@@ -383,6 +383,8 @@ var ColumnManager = class {
383
383
  this.set(columns);
384
384
  }
385
385
  destroy() {
386
+ const { length } = this.items;
387
+ for (let index = 0; index < length; index += 1) this.items[index].destroy();
386
388
  this.items.length = 0;
387
389
  }
388
390
  remove(value) {
@@ -392,7 +394,10 @@ var ColumnManager = class {
392
394
  if (length === 0) return;
393
395
  for (let fieldIndex = 0; fieldIndex < length; fieldIndex += 1) {
394
396
  const itemIndex = items.findIndex((component) => component.options.field === fields[fieldIndex]);
395
- if (itemIndex > -1) items.splice(itemIndex, 1);
397
+ if (itemIndex > -1) {
398
+ items[itemIndex].destroy();
399
+ items.splice(itemIndex, 1);
400
+ }
396
401
  }
397
402
  components.header.update(items);
398
403
  components.footer.update(items);
@@ -498,6 +503,90 @@ function isPlainObject(value) {
498
503
  const prototype = Object.getPrototypeOf(value);
499
504
  return prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null;
500
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
+ }
501
590
  function aggregate(type, array, key) {
502
591
  const length = Array.isArray(array) ? array.length : 0;
503
592
  if (length === 0) return {
@@ -556,6 +645,15 @@ function getString(value) {
556
645
  return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
557
646
  }
558
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
+ /**
559
657
  * Split a string into words _(and other readable parts)_
560
658
  * @param value Original string
561
659
  * @returns Array of words found in the string
@@ -659,7 +757,7 @@ function compareSymbols(first, second) {
659
757
  }
660
758
  function compareValue(first, second, compareStrings) {
661
759
  const firstType = typeof first;
662
- if (firstType === typeof second && firstType in comparators) return comparators[firstType](first, second);
760
+ if (firstType === typeof second && firstType in comparators$1) return comparators$1[firstType](first, second);
663
761
  if (first instanceof Date && second instanceof Date) return compareNumbers(first.getTime(), second.getTime());
664
762
  return compare.handlers.handle(first, second, compareStrings);
665
763
  }
@@ -667,7 +765,7 @@ function getComparisonParts(value) {
667
765
  if (Array.isArray(value)) return value;
668
766
  return typeof value === "object" ? [value] : words(getString(value));
669
767
  }
670
- var comparators = {
768
+ var comparators$1 = {
671
769
  bigint: compareNumbers,
672
770
  boolean: compareNumbers,
673
771
  number: compareNumbers,
@@ -780,7 +878,7 @@ var DataManager = class {
780
878
  }
781
879
  async add(data, render) {
782
880
  const { field, values } = this;
783
- values.objects.array.push(...data);
881
+ push(values.objects.array, data);
784
882
  values.objects.mapped = toMap(values.objects.array, field);
785
883
  if (render) this.render();
786
884
  }
@@ -793,6 +891,7 @@ var DataManager = class {
793
891
  values.keys.active = void 0;
794
892
  values.keys.original.length = 0;
795
893
  values.objects.array.length = 0;
894
+ this.handlers = void 0;
796
895
  }
797
896
  get(active) {
798
897
  const { values } = this;
@@ -816,7 +915,8 @@ var DataManager = class {
816
915
  render() {
817
916
  const { field, managers, values } = this;
818
917
  values.keys.original = sort(values.objects.array.map((item) => item[field]));
819
- if (managers.sort.items.length > 0) managers.sort.sort();
918
+ if (Object.keys(managers.filter.items).length > 0) managers.filter.filter();
919
+ else if (managers.sort.items.length > 0) managers.sort.sort();
820
920
  else managers.virtualization.update(true);
821
921
  }
822
922
  set(data) {
@@ -1158,7 +1258,7 @@ var EventManager = class {
1158
1258
  listener;
1159
1259
  constructor(managers, element) {
1160
1260
  this.managers = managers;
1161
- on(element, "click", (event) => {
1261
+ this.listener = on(element, "click", (event) => {
1162
1262
  this.onClick(event);
1163
1263
  }, { passive: false });
1164
1264
  }
@@ -1181,6 +1281,489 @@ var EventManager = class {
1181
1281
  if (field != null) managers.sort.toggle(event, field, direction);
1182
1282
  }
1183
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 });
1184
1767
  function removeRow(pool, row) {
1185
1768
  if (row.element != null) {
1186
1769
  row.element.innerHTML = "";
@@ -1223,6 +1806,9 @@ var RowManager = class {
1223
1806
  this.height = rowHeight;
1224
1807
  }
1225
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]);
1226
1812
  this.components.clear();
1227
1813
  }
1228
1814
  get(key) {
@@ -1237,7 +1823,11 @@ var RowManager = class {
1237
1823
  return this.components.has(key);
1238
1824
  }
1239
1825
  remove(key) {
1240
- this.components.delete(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
+ }
1241
1831
  }
1242
1832
  update(key) {
1243
1833
  const row = this.components.get(key);
@@ -1267,13 +1857,19 @@ var SortManager = class {
1267
1857
  addOrSet(event, field) {
1268
1858
  if (event.ctrlKey || event.metaKey) this.add(field);
1269
1859
  else this.set([{
1270
- key: field,
1860
+ field,
1271
1861
  direction: "ascending"
1272
1862
  }]);
1273
1863
  }
1274
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;
1275
1872
  this.items.length = 0;
1276
- this.sort();
1277
1873
  }
1278
1874
  flip(field) {
1279
1875
  const item = this.items.find((item) => item.key === field);
@@ -1293,14 +1889,17 @@ var SortManager = class {
1293
1889
  else this.clear();
1294
1890
  }
1295
1891
  set(items) {
1296
- this.items.splice(0, this.items.length, ...items);
1892
+ this.items.splice(0, this.items.length, ...items.map((item) => ({
1893
+ key: item.field,
1894
+ direction: item.direction
1895
+ })));
1297
1896
  this.sort();
1298
1897
  }
1299
1898
  sort() {
1300
1899
  const { items, managers } = this;
1301
- managers.data.values.keys.active = items.length === 0 ? void 0 : sort(managers.data.values.objects.array, items).map((row) => row[managers.data.field]);
1302
- managers.virtualization.update(true, true);
1303
- for (const column of managers.column.items) {
1900
+ const { length } = managers.column.items;
1901
+ for (let index = 0; index < length; index += 1) {
1902
+ const column = managers.column.items[index];
1304
1903
  const sorterIndex = items.findIndex((item) => item.key === column.options.field);
1305
1904
  const sorterItem = items[sorterIndex];
1306
1905
  setAttributes(column.elements.wrapper, {
@@ -1309,6 +1908,8 @@ var SortManager = class {
1309
1908
  });
1310
1909
  setAttribute(column.elements.sorter, "data-sort-position", sorterIndex > -1 && items.length > 1 ? sorterIndex + 1 : void 0);
1311
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);
1312
1913
  }
1313
1914
  toggle(event, field, direction) {
1314
1915
  switch (direction) {
@@ -1373,6 +1974,8 @@ var VirtualizationManager = class {
1373
1974
  }
1374
1975
  this.pool.cells = {};
1375
1976
  this.pool.rows = [];
1977
+ this.listener = void 0;
1978
+ this.visible = void 0;
1376
1979
  }
1377
1980
  removeCells(fields) {
1378
1981
  const { length } = fields;
@@ -1432,11 +2035,13 @@ var Tabela = class {
1432
2035
  column: void 0,
1433
2036
  data: void 0,
1434
2037
  event: void 0,
2038
+ filter: void 0,
1435
2039
  row: void 0,
1436
2040
  sort: void 0,
1437
2041
  virtualization: void 0
1438
2042
  };
1439
2043
  data;
2044
+ filter;
1440
2045
  sort;
1441
2046
  get key() {
1442
2047
  return this.#key;
@@ -1454,12 +2059,14 @@ var Tabela = class {
1454
2059
  this.#managers.column = new ColumnManager(this.#managers, this.#components, options.columns);
1455
2060
  this.#managers.data = new DataManager(this.#managers, this.#components, options.key);
1456
2061
  this.#managers.event = new EventManager(this.#managers, this.#element);
2062
+ this.#managers.filter = new FilterManager(this.#managers);
1457
2063
  this.#managers.row = new RowManager(this.#managers, options.rowHeight);
1458
2064
  this.#managers.sort = new SortManager(this.#managers);
1459
2065
  this.#managers.virtualization = new VirtualizationManager(this.#managers, this.#components);
1460
2066
  element.append(this.#components.header.elements.group, this.#components.body.elements.group, this.#components.footer.elements.group);
1461
2067
  this.#managers.data.set(options.data);
1462
2068
  this.data = this.#managers.data.handlers;
2069
+ this.filter = this.#managers.filter.handlers;
1463
2070
  this.sort = this.#managers.sort.handlers;
1464
2071
  }
1465
2072
  destroy() {
@@ -1471,7 +2078,10 @@ var Tabela = class {
1471
2078
  components.header.destroy();
1472
2079
  managers.column.destroy();
1473
2080
  managers.data.destroy();
2081
+ managers.event.destroy();
2082
+ managers.filter.destroy();
1474
2083
  managers.row.destroy();
2084
+ managers.sort.destroy();
1475
2085
  managers.virtualization.destroy();
1476
2086
  element.innerHTML = "";
1477
2087
  element.role = "";