@timlassiter11/yatl 0.3.22 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1933 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // src/events.ts
13
+ var YatlEvent = class extends CustomEvent {
14
+ constructor(name, detail, options = {}) {
15
+ super(name, {
16
+ bubbles: true,
17
+ composed: true,
18
+ cancelable: false,
19
+ ...options,
20
+ detail
21
+ });
22
+ }
23
+ };
24
+ var _YatlRowClickEvent = class _YatlRowClickEvent extends YatlEvent {
25
+ constructor(row, index, field, originalEvent) {
26
+ super(_YatlRowClickEvent.EVENT_NAME, {
27
+ row,
28
+ index,
29
+ field,
30
+ originalEvent
31
+ });
32
+ }
33
+ };
34
+ _YatlRowClickEvent.EVENT_NAME = "yatl-row-click";
35
+ var YatlRowClickEvent = _YatlRowClickEvent;
36
+ var _YatlChangeEvent = class _YatlChangeEvent extends YatlEvent {
37
+ constructor(data) {
38
+ super(_YatlChangeEvent.EVENT_NAME, { data });
39
+ }
40
+ };
41
+ _YatlChangeEvent.EVENT_NAME = "yatl-change";
42
+ var YatlChangeEvent = _YatlChangeEvent;
43
+ var _YatlSortEvent = class _YatlSortEvent extends YatlEvent {
44
+ constructor(field, order) {
45
+ super(
46
+ _YatlSortEvent.EVENT_NAME,
47
+ {
48
+ field,
49
+ order
50
+ },
51
+ {
52
+ cancelable: true
53
+ }
54
+ );
55
+ }
56
+ };
57
+ _YatlSortEvent.EVENT_NAME = "yatl-sort";
58
+ var YatlSortEvent = _YatlSortEvent;
59
+ var _YatlColumnToggleEvent = class _YatlColumnToggleEvent extends YatlEvent {
60
+ constructor(field, visible) {
61
+ super(
62
+ _YatlColumnToggleEvent.EVENT_NAME,
63
+ {
64
+ field,
65
+ visible
66
+ },
67
+ {
68
+ cancelable: true
69
+ }
70
+ );
71
+ }
72
+ };
73
+ _YatlColumnToggleEvent.EVENT_NAME = "yatl-column-toggle";
74
+ var YatlColumnToggleEvent = _YatlColumnToggleEvent;
75
+ var _YatlColumnResizeEvent = class _YatlColumnResizeEvent extends YatlEvent {
76
+ constructor(field, width) {
77
+ super(_YatlColumnResizeEvent.EVENT_NAME, {
78
+ field,
79
+ width
80
+ });
81
+ }
82
+ };
83
+ _YatlColumnResizeEvent.EVENT_NAME = "yatl-column-resize";
84
+ var YatlColumnResizeEvent = _YatlColumnResizeEvent;
85
+ var _YatlColumnReorderEvent = class _YatlColumnReorderEvent extends YatlEvent {
86
+ constructor(draggedColumn, droppedColumn, order) {
87
+ super(
88
+ _YatlColumnReorderEvent.EVENT_NAME,
89
+ {
90
+ draggedColumn,
91
+ droppedColumn,
92
+ order
93
+ },
94
+ {
95
+ cancelable: true
96
+ }
97
+ );
98
+ }
99
+ };
100
+ _YatlColumnReorderEvent.EVENT_NAME = "yatl-column-reorder";
101
+ var YatlColumnReorderEvent = _YatlColumnReorderEvent;
102
+ var _YatlSearchEvent = class _YatlSearchEvent extends YatlEvent {
103
+ constructor(query2) {
104
+ super(_YatlSearchEvent.EVENT_NAME, { query: query2 });
105
+ }
106
+ };
107
+ _YatlSearchEvent.EVENT_NAME = "yatl-search";
108
+ var YatlSearchEvent = _YatlSearchEvent;
109
+ var _YatlStateChangeEvent = class _YatlStateChangeEvent extends YatlEvent {
110
+ constructor(state2, triggers) {
111
+ super(_YatlStateChangeEvent.EVENT_NAME, { state: state2, triggers });
112
+ this.triggers = triggers;
113
+ }
114
+ };
115
+ _YatlStateChangeEvent.EVENT_NAME = "yatl-state-change";
116
+ var YatlStateChangeEvent = _YatlStateChangeEvent;
117
+
118
+ // src/utils.ts
119
+ import { html } from "lit";
120
+ var toHumanReadable = (str) => {
121
+ return str.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (char) => char.toUpperCase());
122
+ };
123
+ var createRegexTokenizer = (exp = "\\S+") => {
124
+ const regex = new RegExp(`"[^"]*"|${exp}`, "g");
125
+ return (value) => {
126
+ const matches = value.match(regex) || [];
127
+ return matches.map((token) => {
128
+ token = token.toLocaleLowerCase().trim();
129
+ if (token.startsWith('"') && token.endsWith('"')) {
130
+ return { value: token.slice(1, -1), quoted: true };
131
+ }
132
+ return { value: token, quoted: false };
133
+ });
134
+ };
135
+ };
136
+ var whitespaceTokenizer = createRegexTokenizer();
137
+ function isValidKey(key, obj) {
138
+ return key in obj;
139
+ }
140
+ function getNestedValue(obj, path) {
141
+ const keys = path.split(".");
142
+ let current = obj;
143
+ for (const key of keys) {
144
+ if (current && isValidKey(key, current)) {
145
+ current = current[key];
146
+ } else {
147
+ return void 0;
148
+ }
149
+ }
150
+ return current;
151
+ }
152
+ function findColumn(field, columns) {
153
+ return columns.find((c) => c.field === field);
154
+ }
155
+ function highlightText(text, ranges) {
156
+ if (!text || !ranges || ranges.length === 0) {
157
+ return text;
158
+ }
159
+ const sortedRanges = [...ranges].sort((a, b) => a[0] - b[0]);
160
+ const mergedRanges = [];
161
+ let currentRange = sortedRanges[0];
162
+ for (let i = 1; i < sortedRanges.length; i++) {
163
+ const nextRange = sortedRanges[i];
164
+ if (nextRange[0] < currentRange[1]) {
165
+ currentRange[1] = Math.max(currentRange[1], nextRange[1]);
166
+ } else {
167
+ mergedRanges.push(currentRange);
168
+ currentRange = nextRange;
169
+ }
170
+ }
171
+ mergedRanges.push(currentRange);
172
+ const result = [];
173
+ let lastIndex = 0;
174
+ for (const [start, end] of mergedRanges) {
175
+ const safeStart = Math.max(0, Math.min(start, text.length));
176
+ const safeEnd = Math.max(0, Math.min(end, text.length));
177
+ if (safeStart > lastIndex) {
178
+ result.push(text.slice(lastIndex, safeStart));
179
+ }
180
+ result.push(
181
+ html`<mark class="highlight">${text.slice(safeStart, safeEnd)}</mark>`
182
+ );
183
+ lastIndex = safeEnd;
184
+ }
185
+ if (lastIndex < text.length) {
186
+ result.push(text.slice(lastIndex));
187
+ }
188
+ return html`${result}`;
189
+ }
190
+ function widthsToGridTemplates(widths, defaultWidth = "1fr") {
191
+ return widths.map((width) => width ? `${width}px` : defaultWidth);
192
+ }
193
+ function isCompareable(value) {
194
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
195
+ }
196
+
197
+ // src/yatl-table.ts
198
+ import { html as html2, LitElement, nothing } from "lit";
199
+ import { customElement, property, query, state } from "lit/decorators.js";
200
+ import { classMap } from "lit/directives/class-map.js";
201
+ import { ifDefined } from "lit/directives/if-defined.js";
202
+ import { repeat } from "lit/directives/repeat.js";
203
+ import { styleMap } from "lit/directives/style-map.js";
204
+ import "@lit-labs/virtualizer";
205
+
206
+ // src/yatl-table.styles.ts
207
+ import { css } from "lit";
208
+ var yatl_table_styles_default = css`
209
+ /* Style declarations */
210
+ :host {
211
+ /* Typography */
212
+ --yatl-font-family: var(
213
+ --yatl-table-font,
214
+ -apple-system,
215
+ BlinkMacSystemFont,
216
+ 'Segoe UI',
217
+ Roboto,
218
+ Helvetica,
219
+ Arial,
220
+ sans-serif,
221
+ 'Apple Color Emoji',
222
+ 'Segoe UI Emoji'
223
+ );
224
+ --yatl-font-size: var(--yatl-table-font-size, 0.875rem);
225
+ --yatl-line-height: var(--yatl-table-line-height, 1.25rem);
226
+
227
+ /* Spacing */
228
+ --yatl-cell-padding: var(--yatl-table-cell-padding, 10px 16px);
229
+ --yatl-header-padding: var(--yatl-table-header-padding, 12px 16px);
230
+
231
+ /* Colors */
232
+ --yatl-bg: var(--yatl-table-bg, #ffffff);
233
+ --yatl-text: var(--yatl-table-text, #0f172a);
234
+ --yatl-text-muted: var(--yatl-table-text-muted, #64748b);
235
+ --yatl-border-color: var(--yatl-table-border-color, #e2e8f0);
236
+
237
+ --yatl-header-bg: var(--yatl-table-header-bg, #f8fafc);
238
+ --yatl-header-text: var(--yatl-table-header-text, #475569);
239
+
240
+ --yatl-row-hover-bg: var(--yatl-table-row-hover-bg, #f1f5f9);
241
+ --yatl-row-selected-bg: var(--yatl-table-row-selected-bg, #e0f2fe);
242
+
243
+ /* Resize grab handle width */
244
+ --yatl-resizer-width: 10px;
245
+ /* z-index for the header */
246
+ --header-z-index: 2;
247
+ /* Drop target background color */
248
+ --header-drop-color: rgba(255, 255, 255, 0.1);
249
+
250
+ font-family: var(--yatl-font-family);
251
+ font-size: var(--yatl-font-size);
252
+ color: var(--yatl-text);
253
+ }
254
+
255
+ :host(.dark) {
256
+ --yatl-table-bg: #1e293b;
257
+ --yatl-table-text: #f1f5f9;
258
+ --yatl-table-text-muted: #94a3b8;
259
+ --yatl-table-border-color: #334155;
260
+
261
+ --yatl-table-header-bg: #0f172a;
262
+ --yatl-table-header-text: #cbd5e1;
263
+
264
+ --yatl-table-row-hover-bg: #334155;
265
+ --yatl-table-row-selected-bg: #1e3a8a;
266
+ }
267
+
268
+ @media (prefers-color-scheme: dark) {
269
+ :host {
270
+ --yatl-bg: var(--yatl-table-bg, #1e293b);
271
+ --yatl-text: var(--yatl-table-text, #f1f5f9);
272
+ --yatl-text-muted: var(--yatl-table-text-muted, #94a3b8);
273
+ --yatl-border-color: var(--yatl-table-border-color, #334155);
274
+
275
+ --yatl-header-bg: var(--yatl-table-header-bg, #0f172a);
276
+ --yatl-header-text: var(--yatl-table-header-text, #cbd5e1);
277
+
278
+ --yatl-row-hover-bg: var(--yatl-table-row-hover-bg, #334155);
279
+ --yatl-row-selected-bg: var(--yatl-table-row-selected-bg, #1e3a8a);
280
+ }
281
+ }
282
+
283
+ :host {
284
+ font-family: system-ui, sans-serif;
285
+ }
286
+
287
+ .table {
288
+ background-color: var(--yatl-bg);
289
+ border: 1px solid var(--yatl-border-color);
290
+ border-radius: 6px;
291
+ }
292
+
293
+ .header.row {
294
+ background-color: var(--yatl-header-bg);
295
+ border-bottom: 1px solid var(--yatl-border-color);
296
+ font-weight: 600;
297
+ color: var(--yatl-header-text);
298
+ }
299
+
300
+ .row {
301
+ background-color: var(--yatl-bg);
302
+ border-bottom: 1px solid var(--yatl-border-color);
303
+ transition: background-color 50ms;
304
+ position: relative;
305
+ }
306
+
307
+ .row:last-child {
308
+ border-bottom: none;
309
+ }
310
+
311
+ .header .cell::after,
312
+ .row:not(.header)::after {
313
+ content: '';
314
+ position: absolute;
315
+ inset: 0;
316
+ pointer-events: none;
317
+ background-color: transparent;
318
+ transition: background-color 50ms;
319
+ z-index: 1;
320
+ }
321
+
322
+ .header .cell:hover::after,
323
+ .row:not(.header):hover::after {
324
+ background-color: rgba(0, 0, 0, 0.2);
325
+ }
326
+
327
+ .cell {
328
+ align-items: center;
329
+ padding: var(--yatl-cell-padding);
330
+ }
331
+
332
+ .header .cell {
333
+ padding: var(--yatl-header-padding);
334
+ }
335
+
336
+ .footer {
337
+ padding: 8px 12px;
338
+ background-color: var(--yatl-header-bg);
339
+ border-top: 1px solid var(--yatl-border-color);
340
+ color: var(--yatl-text-muted);
341
+ font-size: 0.8em;
342
+ }
343
+
344
+ .resizer::after {
345
+ height: 60%;
346
+ width: 1px;
347
+ background-color: color-mix(in srgb, currentColor 30%, transparent);
348
+ transition: background-color 0.2s;
349
+ }
350
+
351
+ .resizer:hover::after {
352
+ background-color: currentColor;
353
+ width: 2px;
354
+ }
355
+
356
+ .drop-indicator {
357
+ background: rgba(0, 0, 0, 0.4);
358
+ }
359
+
360
+ .message {
361
+ font-size: large;
362
+ }
363
+
364
+ /* Layout stuff
365
+ * Most of this is functional and needed
366
+ * for the table to work properly.
367
+ * Modify with caution!
368
+ */
369
+ :host {
370
+ display: block;
371
+ height: 100%;
372
+ width: 100%;
373
+ }
374
+
375
+ .table {
376
+ display: flex;
377
+ flex-direction: column;
378
+ height: 100%;
379
+ width: 100%;
380
+ min-height: 0;
381
+ overflow: auto;
382
+ box-sizing: border-box;
383
+ }
384
+
385
+ .header {
386
+ z-index: var(--header-z-index);
387
+ flex-shrink: 0;
388
+ position: sticky;
389
+ top: 0;
390
+ }
391
+
392
+ .header-content {
393
+ position: relative;
394
+ width: 100%;
395
+ display: flex;
396
+ flex-direction: row;
397
+ align-items: baseline;
398
+ gap: 0.5rem;
399
+ }
400
+
401
+ .sort-icon {
402
+ position: relative;
403
+ width: 1ch;
404
+ align-self: stretch;
405
+ padding: 0;
406
+ overflow: hidden;
407
+ flex-shrink: 0;
408
+ }
409
+
410
+ .sort-icon::after {
411
+ content: '';
412
+ position: absolute;
413
+ }
414
+
415
+ .sort-icon.descending::after {
416
+ content: '\\2191';
417
+ }
418
+
419
+ .sort-icon.ascending::after {
420
+ content: '\\2193';
421
+ }
422
+
423
+ .resizer {
424
+ position: absolute;
425
+ top: 0;
426
+ bottom: 0;
427
+ right: 0;
428
+ width: var(--yatl-resizer-width);
429
+ cursor: col-resize;
430
+ display: flex;
431
+ justify-content: center;
432
+ align-items: center;
433
+ }
434
+
435
+ .resizer::after {
436
+ content: '';
437
+ display: block;
438
+ }
439
+
440
+ .drop-indicator {
441
+ display: none;
442
+ position: absolute;
443
+ top: 0;
444
+ left: 0;
445
+ right: 0;
446
+ bottom: 0;
447
+ pointer-events: none;
448
+ z-index: calc(var(--header-z-index) + 1);
449
+ }
450
+
451
+ .drop-indicator.active {
452
+ display: block;
453
+ }
454
+
455
+ .sortable {
456
+ cursor: pointer;
457
+ }
458
+
459
+ /* Footer */
460
+ .footer {
461
+ display: flex;
462
+ align-items: center;
463
+ justify-content: space-between;
464
+ flex-shrink: 0;
465
+
466
+ position: sticky;
467
+ bottom: 0;
468
+ z-index: var(--header-z-index);
469
+ }
470
+
471
+ /* Generic table parts */
472
+ .row {
473
+ display: grid;
474
+ grid-template-columns: var(--grid-template);
475
+ min-width: 100%;
476
+ width: fit-content;
477
+ }
478
+
479
+ .cell {
480
+ white-space: nowrap;
481
+ overflow: hidden;
482
+ text-overflow: ellipsis;
483
+ position: relative;
484
+ display: flex;
485
+ align-items: center;
486
+ }
487
+
488
+ .message {
489
+ width: 100%;
490
+ height: 100%;
491
+ text-align: center;
492
+ pointer-events: none;
493
+ display: flex;
494
+ align-items: center;
495
+ justify-content: center;
496
+ }
497
+
498
+ .truncate {
499
+ display: block;
500
+ white-space: nowrap;
501
+ overflow: hidden;
502
+ text-overflow: ellipsis;
503
+ }
504
+ `;
505
+
506
+ // src/yatl-table.ts
507
+ var STATE_SAVE_DEBOUNCE = 1e3;
508
+ var DEFAULT_STORAGE_OPTIONS = {
509
+ storage: "local",
510
+ saveColumnSortOrders: true,
511
+ saveColumnVisibility: true,
512
+ saveColumnWidths: true,
513
+ saveColumnOrder: true
514
+ };
515
+ var SAVE_TRIGGERS = /* @__PURE__ */ new Set([
516
+ "searchQuery",
517
+ "filters",
518
+ "columns",
519
+ "columnSort",
520
+ "columnOrder",
521
+ "columnVisibility",
522
+ "columnWidths",
523
+ "storageOptions"
524
+ ]);
525
+ var MATCH_WEIGHTS = {
526
+ EXACT: 100,
527
+ PREFIX: 50,
528
+ SUBSTRING: 10
529
+ };
530
+ var YatlTable = class extends LitElement {
531
+ constructor() {
532
+ super(...arguments);
533
+ // #region --- State Data ---
534
+ // Property data
535
+ this._enableSearchTokenization = false;
536
+ this._enableSearchScoring = false;
537
+ this._columns = [];
538
+ this._columnStates = [];
539
+ this._columnOrder = [];
540
+ this._storageOptions = null;
541
+ this._data = [];
542
+ this._searchQuery = "";
543
+ this._searchIncludedFields = [];
544
+ this._searchTokenizer = whitespaceTokenizer;
545
+ this._filters = null;
546
+ this._filteredData = [];
547
+ // Flag if we have already restored the state or not.
548
+ this.hasRestoredState = false;
549
+ // save state debounce timer
550
+ this.saveTimer = 0;
551
+ // Flags set when something changes that
552
+ // requires the filter or sort logic to re-run.
553
+ this.filterDirty = false;
554
+ this.sortDirty = false;
555
+ // The last time the data was updated.
556
+ // For displaying in the footer only.
557
+ this.dataLastUpdate = null;
558
+ // Maps rows to their metadata
559
+ this.rowMetadata = /* @__PURE__ */ new WeakMap();
560
+ // List of tokens created from the current query
561
+ this.queryTokens = null;
562
+ // Column resize state
563
+ this.resizeState = null;
564
+ // Column drag & drop state
565
+ this.dragColumn = null;
566
+ this.enableVirtualScroll = false;
567
+ this.enableSearchHighlight = true;
568
+ this.enableColumnReorder = true;
569
+ this.enableFooter = false;
570
+ this.nullValuePlaceholder = "-";
571
+ this.emptyMessage = "No records to display";
572
+ this.noResultsMessage = "No matching records found";
573
+ this.rowParts = null;
574
+ // #endregion
575
+ // #region --- Event Handlers ---
576
+ this.handleHeaderClicked = (event, column) => {
577
+ const target = event.target;
578
+ if (!column.sortable || target.classList.contains("resizer")) {
579
+ return;
580
+ }
581
+ const multiSort = event.shiftKey;
582
+ const state2 = this.getColumnState(column.field);
583
+ if (!state2?.sort) {
584
+ this.sort(column.field, "asc", !multiSort);
585
+ } else if (state2.sort.order === "asc") {
586
+ this.sort(column.field, "desc", !multiSort);
587
+ } else if (state2.sort.order) {
588
+ this.sort(column.field, null, !multiSort);
589
+ }
590
+ };
591
+ this.handleCellClick = (event, row, field) => {
592
+ if (window.getSelection()?.toString()) return;
593
+ const rowIndex = this.rowMetadata.get(row).index;
594
+ this.dispatchEvent(new YatlRowClickEvent(row, rowIndex, field, event));
595
+ };
596
+ this.handleResizeMouseMove = (event) => {
597
+ if (!this.resizeState?.active) return;
598
+ requestAnimationFrame(() => {
599
+ if (!this.resizeState?.active) return;
600
+ const deltaX = event.pageX - this.resizeState.startX;
601
+ const newWidth = Math.max(50, this.resizeState.startWidth + deltaX);
602
+ this.resizeState.currentWidths[this.resizeState.columnIndex] = `${newWidth}px`;
603
+ this.tableElement.style.setProperty(
604
+ "--grid-template",
605
+ this.resizeState.currentWidths.join(" ")
606
+ );
607
+ });
608
+ };
609
+ this.handleResizeMouseUp = (_event) => {
610
+ window.removeEventListener("mousemove", this.handleResizeMouseMove);
611
+ window.removeEventListener("mouseup", this.handleResizeMouseUp);
612
+ document.body.style.cursor = "";
613
+ if (this.resizeState?.active) {
614
+ const finalWidth = parseFloat(
615
+ this.resizeState.currentWidths[this.resizeState.columnIndex]
616
+ );
617
+ const columnWidths = this.columnWidths;
618
+ const state2 = findColumn(this.resizeState.columnField, columnWidths);
619
+ state2.width = finalWidth;
620
+ this.columnWidths = columnWidths;
621
+ this.dispatchEvent(new YatlColumnResizeEvent(state2.field, state2.width));
622
+ }
623
+ this.resizeState = null;
624
+ };
625
+ this.handleDragColumnStart = (event, field) => {
626
+ const target = event.target;
627
+ if (target?.classList.contains("resizer")) {
628
+ event.preventDefault();
629
+ return;
630
+ }
631
+ if (event.dataTransfer) {
632
+ event.dataTransfer.effectAllowed = "move";
633
+ event.dataTransfer.setData("text/plain", field);
634
+ this.dragColumn = field;
635
+ }
636
+ };
637
+ this.handleDragColumnEnter = (event) => {
638
+ const cell = event.currentTarget;
639
+ cell.querySelector(".drop-indicator")?.classList.add("active");
640
+ };
641
+ this.handleDragColumnLeave = (event) => {
642
+ const cell = event.currentTarget;
643
+ const enteringElement = event.relatedTarget;
644
+ if (cell.contains(enteringElement)) {
645
+ return;
646
+ }
647
+ cell.querySelector(".drop-indicator")?.classList.remove("active");
648
+ };
649
+ this.handleDragColumnOver = (event) => {
650
+ event.preventDefault();
651
+ if (event.dataTransfer) {
652
+ event.dataTransfer.dropEffect = "move";
653
+ }
654
+ };
655
+ this.handleDragColumnDrop = (event, field) => {
656
+ if (!this.dragColumn || this.dragColumn === field) {
657
+ return;
658
+ }
659
+ event.preventDefault();
660
+ event.stopPropagation();
661
+ const newColumnOrder = [...this.columnOrder];
662
+ const dragIndex = newColumnOrder.findIndex((col) => col === this.dragColumn);
663
+ const dropIndex = newColumnOrder.findIndex((col) => col === field);
664
+ if (dragIndex > -1 && dropIndex > -1) {
665
+ const [draggedColumn] = newColumnOrder.splice(dragIndex, 1);
666
+ const droppedColumn = findColumn(field, this.columns);
667
+ if (!droppedColumn) return;
668
+ newColumnOrder.splice(dropIndex, 0, draggedColumn);
669
+ const reorderEvent = new YatlColumnReorderEvent(
670
+ draggedColumn,
671
+ droppedColumn.field,
672
+ newColumnOrder
673
+ );
674
+ if (!this.dispatchEvent(reorderEvent)) {
675
+ return;
676
+ }
677
+ this.columnOrder = [...newColumnOrder];
678
+ }
679
+ };
680
+ this.handleDragColumnEnd = () => {
681
+ this.dragColumn = null;
682
+ this.tableElement.querySelectorAll(".drop-indicator.active").forEach((element) => element.classList.remove("active"));
683
+ };
684
+ }
685
+ get enableSearchTokenization() {
686
+ return this._enableSearchTokenization;
687
+ }
688
+ set enableSearchTokenization(enable) {
689
+ if (this._enableSearchTokenization === enable) {
690
+ return;
691
+ }
692
+ const oldValue = this._enableSearchTokenization;
693
+ this._enableSearchTokenization = enable;
694
+ this.updateInternalQuery();
695
+ this.filterDirty = true;
696
+ this.requestUpdate("enableSearchTokenization", oldValue);
697
+ }
698
+ get enableSearchScoring() {
699
+ return this._enableSearchScoring;
700
+ }
701
+ set enableSearchScoring(enable) {
702
+ if (this._enableSearchScoring === enable) {
703
+ return;
704
+ }
705
+ const oldValue = this._enableSearchScoring;
706
+ this._enableSearchScoring = enable;
707
+ this.filterDirty = true;
708
+ this.requestUpdate("enableSearchScoring", oldValue);
709
+ }
710
+ get columns() {
711
+ return this._columns;
712
+ }
713
+ set columns(columns) {
714
+ if (this._columns === columns) {
715
+ return;
716
+ }
717
+ const oldValue = this._columns;
718
+ this._columns = columns;
719
+ this.filterDirty = true;
720
+ this.requestUpdate("columns", oldValue);
721
+ }
722
+ get columnOrder() {
723
+ const finalOrder = /* @__PURE__ */ new Set();
724
+ for (const field of this._columnOrder) {
725
+ const col = findColumn(field, this.columns);
726
+ if (col) {
727
+ finalOrder.add(field);
728
+ }
729
+ }
730
+ for (const col of this.columns) {
731
+ if (!finalOrder.has(col.field)) {
732
+ finalOrder.add(col.field);
733
+ }
734
+ }
735
+ return [...finalOrder];
736
+ }
737
+ set columnOrder(columns) {
738
+ if (this._columnOrder === columns) {
739
+ return;
740
+ }
741
+ const oldValue = this._columnOrder;
742
+ this._columnOrder = [...columns];
743
+ this.requestUpdate("columnOrder", oldValue);
744
+ }
745
+ get columnVisibility() {
746
+ return this.columnOrder.map((field) => ({
747
+ field,
748
+ visible: this.getColumnState(field).visible
749
+ }));
750
+ }
751
+ set columnVisibility(columns) {
752
+ const oldValue = this.columnVisibility;
753
+ let changed = false;
754
+ for (const column of columns) {
755
+ const state2 = this.getColumnState(column.field);
756
+ if (state2 && state2.visible !== column.visible) {
757
+ changed = true;
758
+ state2.visible = column.visible;
759
+ }
760
+ }
761
+ if (!changed) {
762
+ return;
763
+ }
764
+ this.requestUpdate("columnVisibility", oldValue);
765
+ }
766
+ get columnSort() {
767
+ return this.columnOrder.map((field) => ({
768
+ field,
769
+ sort: this.getColumnState(field).sort
770
+ }));
771
+ }
772
+ set columnSort(columns) {
773
+ const oldValue = this.columnSort;
774
+ let changed = false;
775
+ for (const column of columns) {
776
+ const state2 = this.getColumnState(column.field);
777
+ if (state2 && (state2.sort?.order !== column.sort?.order || state2.sort?.priority !== column.sort?.priority)) {
778
+ changed = true;
779
+ state2.sort = column.sort;
780
+ }
781
+ }
782
+ if (!changed) {
783
+ return;
784
+ }
785
+ this.sortDirty = true;
786
+ this.requestUpdate("columnSort", oldValue);
787
+ }
788
+ get columnWidths() {
789
+ return this.columnOrder.map((field) => ({
790
+ field,
791
+ width: this.getColumnState(field).width
792
+ }));
793
+ }
794
+ set columnWidths(columns) {
795
+ const oldValue = this.columnWidths;
796
+ let changed = false;
797
+ for (const column of columns) {
798
+ const state2 = this.getColumnState(column.field);
799
+ if (state2 && state2.width !== column.width) {
800
+ changed = true;
801
+ state2.width = column.width;
802
+ }
803
+ }
804
+ if (!changed) {
805
+ return;
806
+ }
807
+ this.requestUpdate("columnWidths", oldValue);
808
+ }
809
+ get searchQuery() {
810
+ return this._searchQuery;
811
+ }
812
+ set searchQuery(query2) {
813
+ if (this._searchQuery === query2) {
814
+ return;
815
+ }
816
+ const oldValue = this._searchQuery;
817
+ this._searchQuery = query2;
818
+ this.updateInternalQuery();
819
+ this.filterDirty = true;
820
+ this.requestUpdate("searchQuery", oldValue);
821
+ }
822
+ get searchIncludedFields() {
823
+ return this._searchIncludedFields;
824
+ }
825
+ set searchIncludedFields(fields) {
826
+ if (this._searchIncludedFields === fields) {
827
+ return;
828
+ }
829
+ const oldValue = this._searchIncludedFields;
830
+ this._searchIncludedFields = fields;
831
+ this.filterDirty = true;
832
+ this.requestUpdate("searchIncludedFields", oldValue);
833
+ }
834
+ get searchTokenizer() {
835
+ return this._searchTokenizer;
836
+ }
837
+ set searchTokenizer(tokenizer) {
838
+ if (this._searchTokenizer === tokenizer) {
839
+ return;
840
+ }
841
+ const oldValue = this._searchTokenizer;
842
+ this._searchTokenizer = tokenizer;
843
+ this.filterDirty = true;
844
+ this.requestUpdate("searchTokenizer", oldValue);
845
+ }
846
+ get filters() {
847
+ return this._filters;
848
+ }
849
+ set filters(filters) {
850
+ if (this._filters === filters) {
851
+ return;
852
+ }
853
+ const oldValue = this._filters;
854
+ this._filters = filters;
855
+ this.filterDirty = true;
856
+ this.requestUpdate("filters", oldValue);
857
+ }
858
+ get storageOptions() {
859
+ return this._storageOptions;
860
+ }
861
+ set storageOptions(options) {
862
+ if (this._storageOptions === options) {
863
+ return;
864
+ }
865
+ const oldValue = this._storageOptions;
866
+ this._storageOptions = options;
867
+ if (!this.hasRestoredState) {
868
+ this.loadStateFromStorage();
869
+ }
870
+ this.requestUpdate("storageOptions", oldValue);
871
+ }
872
+ get data() {
873
+ return this._data;
874
+ }
875
+ set data(value) {
876
+ const oldValue = this._data;
877
+ this._data = value;
878
+ this.dataLastUpdate = /* @__PURE__ */ new Date();
879
+ this.createMetadata();
880
+ this.filterDirty = true;
881
+ this.requestUpdate("data", oldValue);
882
+ }
883
+ get filteredData() {
884
+ if (this.filterDirty) {
885
+ this.filterRows();
886
+ } else if (this.sortDirty) {
887
+ this.sortRows();
888
+ }
889
+ this.filterDirty = false;
890
+ this.sortDirty = false;
891
+ return [...this._filteredData];
892
+ }
893
+ // #endregion
894
+ // #region --- Public Methods ---
895
+ /**
896
+ * Gets a copy of the current state of the table.
897
+ */
898
+ getState() {
899
+ return {
900
+ searchQuery: this.searchQuery,
901
+ filters: this.filters,
902
+ columnOrder: this.columnOrder,
903
+ columns: this.columns.map((column) => {
904
+ const state2 = this.getColumnState(column.field);
905
+ return {
906
+ field: column.field,
907
+ visible: state2.visible,
908
+ sort: state2.sort,
909
+ width: state2.width
910
+ };
911
+ })
912
+ };
913
+ }
914
+ /**
915
+ * Restores the table to the provided state.
916
+ * @param state - The state to restore the table to.
917
+ */
918
+ restoreState(state2) {
919
+ if ("searchQuery" in state2 && state2.searchQuery !== void 0) {
920
+ this.searchQuery = state2.searchQuery;
921
+ }
922
+ if ("filters" in state2 && state2.filters !== void 0) {
923
+ this.filters = state2.filters;
924
+ }
925
+ if ("columnOrder" in state2 && state2.columnOrder !== void 0) {
926
+ this.columnOrder = state2.columnOrder;
927
+ }
928
+ if ("columns" in state2 && state2.columns !== void 0) {
929
+ const newColumnStates = [];
930
+ for (const newState of state2.columns) {
931
+ const currentState = this.getColumnState(newState.field);
932
+ newColumnStates.push(currentState);
933
+ if (!newState) {
934
+ continue;
935
+ }
936
+ if ("visible" in newState && newState.visible !== void 0) {
937
+ currentState.visible = newState.visible;
938
+ }
939
+ if ("sort" in newState && newState.sort !== void 0) {
940
+ currentState.sort = newState.sort;
941
+ }
942
+ if ("width" in newState && newState.width !== void 0) {
943
+ currentState.width = newState.width;
944
+ }
945
+ }
946
+ this._columnStates = newColumnStates;
947
+ this.requestUpdate();
948
+ }
949
+ }
950
+ /**
951
+ * Sorts the table by a specified column and order.
952
+ * If `order` is `null`, the sort on this column is removed.
953
+ * @param field - The field name of the column to sort by.
954
+ * @param order - The sort order: 'asc', 'desc', or `null` to remove sorting for this column.
955
+ * @param clear - Clear all other sorting
956
+ */
957
+ sort(field, order, clear = true) {
958
+ const sortStates = this.columnSort;
959
+ const state2 = findColumn(field, sortStates);
960
+ if (!state2) {
961
+ throw new Error(`Cannot get options for non-existent column "${field}"`);
962
+ }
963
+ if (order === state2.sort?.order) {
964
+ return;
965
+ }
966
+ if (!this.dispatchEvent(new YatlSortEvent(field, order))) {
967
+ return;
968
+ }
969
+ if (order && !state2.sort) {
970
+ const priorities = sortStates.map((col) => col.sort?.priority).filter((priority2) => priority2 !== void 0);
971
+ const maxPriority = this.columns.length + 1;
972
+ const priority = Math.min(maxPriority, ...priorities) - 1;
973
+ state2.sort = { order, priority };
974
+ } else if (order && state2.sort) {
975
+ state2.sort.order = order;
976
+ } else {
977
+ state2.sort = null;
978
+ }
979
+ if (clear) {
980
+ for (const state3 of sortStates) {
981
+ if (state3.field !== field) {
982
+ state3.sort = null;
983
+ }
984
+ }
985
+ }
986
+ this.columnSort = sortStates;
987
+ }
988
+ /**
989
+ * Sets the visibility of a specified column.
990
+ * @param field - The field name of the column.
991
+ * @param visible - `true` to show the column, `false` to hide it.
992
+ */
993
+ setColumnVisibility(field, visible) {
994
+ const visibilityStates = this.columnVisibility;
995
+ const state2 = findColumn(field, visibilityStates);
996
+ if (!state2) {
997
+ throw new Error(`Cannot get options for non-existent column "${field}"`);
998
+ }
999
+ if (state2.visible === visible) {
1000
+ return;
1001
+ }
1002
+ if (!this.dispatchEvent(new YatlColumnToggleEvent(field, visible))) {
1003
+ return;
1004
+ }
1005
+ state2.visible = visible;
1006
+ this.columnVisibility = visibilityStates;
1007
+ }
1008
+ /**
1009
+ * Toggles the visibility of hte specified column
1010
+ * @param field - The field name of the column to toggle.
1011
+ */
1012
+ toggleColumnVisibility(field) {
1013
+ const state2 = this.getColumnState(field);
1014
+ this.setColumnVisibility(field, !state2);
1015
+ }
1016
+ /**
1017
+ * Shows the specified column
1018
+ * @param field - The field name of the column to show.
1019
+ */
1020
+ showColumn(field) {
1021
+ this.setColumnVisibility(field, true);
1022
+ }
1023
+ /**
1024
+ * Hides the specified column
1025
+ * @param field - The field name of the column to hide.
1026
+ */
1027
+ hideColumn(field) {
1028
+ this.setColumnVisibility(field, false);
1029
+ }
1030
+ /**
1031
+ * Export the current visible table data to a CSV file.
1032
+ * @param filename - The name of the file to save.
1033
+ * @param all - If `true`, exports all original data (ignoring filters). If `false` (default), exports only the currently visible (filtered and sorted) rows.
1034
+ */
1035
+ export(filename, all = false) {
1036
+ const data = all ? this.data : this.filteredData;
1037
+ const rows = [...data.values()];
1038
+ const columnData = this.columnData;
1039
+ const csvHeaders = columnData.filter((col) => all || col.state?.visible).map((col) => `"${col.options.title}"`).join(",");
1040
+ const csvRows = rows.map((row) => {
1041
+ const list = [];
1042
+ for (const col of columnData) {
1043
+ let value = getNestedValue(row, col.field);
1044
+ if (all || col.state.visible) {
1045
+ if (typeof col.options.valueFormatter === "function") {
1046
+ value = col.options.valueFormatter(value, row);
1047
+ }
1048
+ value = String(value).replace('"', '""');
1049
+ list.push(`"${value}"`);
1050
+ }
1051
+ }
1052
+ return list.join(",");
1053
+ }).join("\n");
1054
+ const csvContent = csvHeaders + "\n" + csvRows;
1055
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8," });
1056
+ const a = document.createElement("a");
1057
+ a.style.display = "none";
1058
+ a.href = URL.createObjectURL(blob);
1059
+ a.download = `${filename}.csv`;
1060
+ document.body.append(a);
1061
+ a.click();
1062
+ a.remove();
1063
+ }
1064
+ scrollToRow(row) {
1065
+ const index = this.data.findIndex((v) => v === row);
1066
+ if (typeof index === "number") {
1067
+ return this.scrollToOriginalIndex(index);
1068
+ } else {
1069
+ throw new Error("Row not in table");
1070
+ }
1071
+ }
1072
+ /**
1073
+ * Scrolls the table to bring the row at the specified original index into view.
1074
+ * @param index - The original index of the row (from the initial dataset).
1075
+ */
1076
+ scrollToOriginalIndex(index) {
1077
+ const rowData = this.data[index];
1078
+ if (rowData) {
1079
+ const filteredIndex = this.filteredData.indexOf(rowData);
1080
+ if (filteredIndex >= 0) {
1081
+ return this.scrollToFilteredIndex(filteredIndex);
1082
+ } else {
1083
+ throw new Error("Cannot scroll to filtered out row");
1084
+ }
1085
+ } else {
1086
+ throw new RangeError(`Row index ${index} out of range`);
1087
+ }
1088
+ }
1089
+ async scrollToFilteredIndex(index) {
1090
+ const rowData = this.filteredData[index];
1091
+ if (!rowData) {
1092
+ throw new RangeError(`Row index ${index} out of range`);
1093
+ }
1094
+ await this.updateComplete;
1095
+ if (this.virtualizer) {
1096
+ this.virtualizer.element(index)?.scrollIntoView({
1097
+ block: "start",
1098
+ behavior: "instant"
1099
+ });
1100
+ } else {
1101
+ const row = this.tableElement.querySelector(
1102
+ `.row[data-filtered-index="${index}"]`
1103
+ );
1104
+ row?.scrollIntoView({
1105
+ block: "start",
1106
+ behavior: "smooth"
1107
+ });
1108
+ }
1109
+ }
1110
+ async scrollToPx(px) {
1111
+ await this.updateComplete;
1112
+ if (this.virtualizer) {
1113
+ this.virtualizer.scrollTop = px;
1114
+ } else {
1115
+ this.tableElement.scrollTop = px;
1116
+ }
1117
+ }
1118
+ /**
1119
+ * Finds the first row
1120
+ * @param field
1121
+ * @param value
1122
+ * @returns
1123
+ */
1124
+ findRow(field, value) {
1125
+ return this.data.find((row) => {
1126
+ const rowValue = getNestedValue(row, field);
1127
+ return rowValue === value;
1128
+ });
1129
+ }
1130
+ /**
1131
+ * Finds the original index of the first row where the specified field matches the given value.
1132
+ * This searches through the original, unfiltered dataset.
1133
+ * @param field - The field name within the row data to search.
1134
+ * @param value - The value to match against the field's content.
1135
+ * @returns The original index of the found row, or -1 if no match is found.
1136
+ * @example
1137
+ * ```ts
1138
+ * const index = dataTable.indexOf('id', 12345);
1139
+ * if (index >= 0) {
1140
+ * dataTable.updateRow({description: "Updated description"}, index);
1141
+ * }
1142
+ * ```
1143
+ */
1144
+ findRowIndex(field, value) {
1145
+ const row = this.findRow(field, value);
1146
+ if (row) {
1147
+ return this.rowMetadata.get(row).index;
1148
+ }
1149
+ return -1;
1150
+ }
1151
+ /**
1152
+ * Updates the data of a row at a specific original index.
1153
+ * @param index - The original index of the row to update.
1154
+ * @param data - An object containing the new data to assign to the row. Existing fields will be updated, and new fields will be added.
1155
+ *
1156
+ * @example
1157
+ * ```ts
1158
+ * const index = dataTable.indexOf('id', 12345);
1159
+ * if (index >= 0) {
1160
+ * dataTable.updateRow(index, {description: "Updated description"});
1161
+ * }
1162
+ * ```
1163
+ */
1164
+ updateRow(index, data) {
1165
+ const current_row = this.data[index];
1166
+ if (current_row) {
1167
+ Object.assign(current_row, data);
1168
+ this.requestUpdate("data");
1169
+ }
1170
+ }
1171
+ /**
1172
+ * Deletes a row at a specific original index from the table.
1173
+ * @param index - The original index of the row to delete.
1174
+ */
1175
+ deleteRow(index) {
1176
+ this.data = this.data.toSpliced(index, 1);
1177
+ }
1178
+ // #endregion
1179
+ // #region --- Render Methods ---
1180
+ renderColumnSortIcon(column, state2) {
1181
+ return column.sortable ? html2`<div
1182
+ part="header-sort-icon"
1183
+ class=${classMap({
1184
+ "sort-icon": true,
1185
+ ascending: state2.sort?.order === "asc",
1186
+ descending: state2.sort?.order === "desc"
1187
+ })}
1188
+ ></div>` : nothing;
1189
+ }
1190
+ renderColumnResizer(column, _state) {
1191
+ return column.resizable ? html2`<div
1192
+ part="header-resizer"
1193
+ class="resizer"
1194
+ @click=${(event) => event.stopPropagation()}
1195
+ @mousedown=${(event) => this.handleResizeMouseDown(event, column.field)}
1196
+ ></div>` : nothing;
1197
+ }
1198
+ renderHeaderCell(field) {
1199
+ const column = findColumn(field, this.columns);
1200
+ const state2 = this.getColumnState(column.field);
1201
+ if (state2.visible == false) {
1202
+ return nothing;
1203
+ }
1204
+ return html2`
1205
+ <div
1206
+ part="cell header-cell"
1207
+ class=${classMap({
1208
+ cell: true,
1209
+ sortable: column.sortable ?? false
1210
+ })}
1211
+ draggable=${ifDefined(this.enableColumnReorder ? true : void 0)}
1212
+ data-field=${column.field}
1213
+ @dragstart=${(event) => this.handleDragColumnStart(event, column.field)}
1214
+ @dragenter=${this.handleDragColumnEnter}
1215
+ @dragleave=${this.handleDragColumnLeave}
1216
+ @dragover=${this.handleDragColumnOver}
1217
+ @drop=${(event) => this.handleDragColumnDrop(event, column.field)}
1218
+ @dragend=${this.handleDragColumnEnd}
1219
+ @click=${(event) => this.handleHeaderClicked(event, column)}
1220
+ >
1221
+ <div class="header-content">
1222
+ <span class="header-title truncate" part="header-title">
1223
+ ${column.title ?? toHumanReadable(column.field)}
1224
+ </span>
1225
+ ${this.renderColumnSortIcon(column, state2)}
1226
+ </div>
1227
+ ${this.renderColumnResizer(column, state2)}
1228
+ <div part="drop-indicator" class="drop-indicator"></div>
1229
+ </div>
1230
+ `;
1231
+ }
1232
+ renderHeader() {
1233
+ return html2`
1234
+ <div part="header" class="header row">
1235
+ ${this.columnOrder.map((field) => this.renderHeaderCell(field))}
1236
+ </div>
1237
+ `;
1238
+ }
1239
+ renderCellContents(value, column, row) {
1240
+ if (column.cellRenderer) {
1241
+ return column.cellRenderer(value, column.field, row);
1242
+ }
1243
+ if (value == null) {
1244
+ return this.nullValuePlaceholder;
1245
+ }
1246
+ const indices = this.rowMetadata.get(row).highlightIndices;
1247
+ return this.enableSearchHighlight && indices ? highlightText(String(value), indices[column.field]) : value;
1248
+ }
1249
+ renderCell(field, row) {
1250
+ const column = findColumn(field, this.columns);
1251
+ const state2 = this.getColumnState(column.field);
1252
+ if (!state2.visible) {
1253
+ return nothing;
1254
+ }
1255
+ let value = getNestedValue(row, column.field);
1256
+ let userParts = column.cellParts?.call(this, value, column.field, row);
1257
+ if (Array.isArray(userParts)) {
1258
+ userParts = userParts.join(" ");
1259
+ }
1260
+ if (typeof column.valueFormatter === "function") {
1261
+ value = column.valueFormatter(value, row);
1262
+ }
1263
+ return html2`
1264
+ <div
1265
+ part="cell body-cell cell-${column.field} ${userParts}"
1266
+ data-field=${column.field}
1267
+ class="cell"
1268
+ title=${ifDefined(value ? String(value) : void 0)}
1269
+ @click=${(event) => this.handleCellClick(event, row, column.field)}
1270
+ >
1271
+ <span class="truncate">
1272
+ ${this.renderCellContents(value, column, row)}
1273
+ </span>
1274
+ </div>
1275
+ `;
1276
+ }
1277
+ renderRow(row, index) {
1278
+ const metadata = this.rowMetadata.get(row);
1279
+ let userParts = this.rowParts?.(row) ?? "";
1280
+ if (Array.isArray(userParts)) {
1281
+ userParts = userParts.join(" ");
1282
+ }
1283
+ return html2`
1284
+ <div
1285
+ part=${"row " + userParts}
1286
+ class="row"
1287
+ data-index=${metadata.index}
1288
+ data-filtered-index=${index}
1289
+ >
1290
+ ${this.columnOrder.map((field) => this.renderCell(field, row))}
1291
+ </div>
1292
+ `;
1293
+ }
1294
+ renderBody() {
1295
+ if (this.columnWidths.length === 0) {
1296
+ return html2`
1297
+ <div part="message" class="message">No visible columns.</div>
1298
+ `;
1299
+ }
1300
+ if (this.data.length === 0) {
1301
+ return html2`<div part="message" class="message">
1302
+ ${this.emptyMessage}
1303
+ </div>`;
1304
+ }
1305
+ if (this.filteredData.length === 0) {
1306
+ return html2`<div part="message" class="message">
1307
+ ${this.noResultsMessage}
1308
+ </div>`;
1309
+ }
1310
+ if (this.enableVirtualScroll) {
1311
+ return html2`
1312
+ <lit-virtualizer
1313
+ .items=${this.filteredData}
1314
+ .renderItem=${(item, index) => this.renderRow(item, index)}
1315
+ ></lit-virtualizer>
1316
+ `;
1317
+ }
1318
+ return html2`
1319
+ ${repeat(
1320
+ this.filteredData,
1321
+ (item) => this.rowMetadata.get(item).index,
1322
+ (item, index) => this.renderRow(item, index)
1323
+ )}
1324
+ `;
1325
+ }
1326
+ renderFooter() {
1327
+ if (!this.enableFooter) {
1328
+ return nothing;
1329
+ }
1330
+ const total = this.data.length;
1331
+ const filtered = this.filteredData.length;
1332
+ const fmt = new Intl.NumberFormat(void 0);
1333
+ const totalStr = fmt.format(total);
1334
+ const filteredStr = fmt.format(filtered);
1335
+ const rowCountText = total !== filtered ? `Showing ${filteredStr} of ${totalStr} records` : `Total records: ${totalStr}`;
1336
+ const formatter = Intl.DateTimeFormat(void 0, {
1337
+ dateStyle: "short",
1338
+ timeStyle: "short"
1339
+ });
1340
+ const lastUpdateText = this.dataLastUpdate ? formatter.format(this.dataLastUpdate) : "Never";
1341
+ return html2`
1342
+ <div part="footer" class="footer">
1343
+ <slot name="footer">
1344
+ <span part="row-count">${rowCountText}</span>
1345
+ <span part="timestamp">${lastUpdateText}</span>
1346
+ </slot>
1347
+ </div>
1348
+ `;
1349
+ }
1350
+ render() {
1351
+ const gridWidths = this.getGridWidths();
1352
+ const gridTemplate = widthsToGridTemplates(gridWidths).join(" ");
1353
+ return html2`
1354
+ <div
1355
+ part="table"
1356
+ class="table"
1357
+ style=${styleMap({ "--grid-template": gridTemplate })}
1358
+ >
1359
+ ${this.renderHeader()} ${this.renderBody()} ${this.renderFooter()}
1360
+ </div>
1361
+ `;
1362
+ }
1363
+ // #endregion
1364
+ // #region --- Lifecycle Methods ---
1365
+ updated(changedProperties) {
1366
+ super.updated(changedProperties);
1367
+ super.updated(changedProperties);
1368
+ const stateProps = [
1369
+ "columnOrder",
1370
+ "columnVisibility",
1371
+ "columnSort",
1372
+ "columnWidths",
1373
+ "searchQuery",
1374
+ "filters"
1375
+ ];
1376
+ const changedStateProp = stateProps.filter(
1377
+ (prop) => changedProperties.has(prop)
1378
+ );
1379
+ if (changedStateProp.length) {
1380
+ this.dispatchEvent(
1381
+ new YatlStateChangeEvent(this.getState(), changedStateProp)
1382
+ );
1383
+ }
1384
+ if (this.storageOptions?.key) {
1385
+ const shouldSave = Array.from(changedProperties.keys()).some(
1386
+ (prop) => SAVE_TRIGGERS.has(prop)
1387
+ );
1388
+ if (shouldSave) {
1389
+ this.scheduleSave();
1390
+ }
1391
+ }
1392
+ }
1393
+ disconnectedCallback() {
1394
+ super.disconnectedCallback();
1395
+ window.addEventListener("mousemove", this.handleResizeMouseMove);
1396
+ window.addEventListener("mouseup", this.handleResizeMouseUp);
1397
+ }
1398
+ // #endregion
1399
+ // #region --- Filter Methods ---
1400
+ /**
1401
+ * Calculates a relevance score for a given query against a target string.
1402
+ *
1403
+ * This function implements a tiered matching strategy:
1404
+ * 1. **Exact Match**: The query exactly matches the target. This yields the highest score.
1405
+ * 2. **Prefix Match**: The target starts with the query. This is the next most relevant.
1406
+ * 3. **Substring Match**: The target contains the query somewhere. This is the least relevant.
1407
+ *
1408
+ * The final score is weighted and adjusted by the length difference between the query and the target
1409
+ * to ensure that more specific matches (e.g., "apple" vs "application" for the query "apple") rank higher.
1410
+ *
1411
+ * @param query The search term (e.g., "app").
1412
+ * @param target The string to be searched (e.g., "Apple" or "Application").
1413
+ * @returns A numerical score representing the relevance of the match. Higher is better. Returns 0 if no match is found.
1414
+ */
1415
+ calculateSearchScore(query2, target) {
1416
+ const results = { score: 0, ranges: [] };
1417
+ if (!query2 || !target) {
1418
+ return results;
1419
+ }
1420
+ let baseScore = 0;
1421
+ let matchTypeWeight = 0;
1422
+ if (target === query2) {
1423
+ matchTypeWeight = MATCH_WEIGHTS.EXACT;
1424
+ baseScore = query2.length;
1425
+ results.ranges.push([0, target.length]);
1426
+ } else if (target.startsWith(query2)) {
1427
+ matchTypeWeight = MATCH_WEIGHTS.PREFIX;
1428
+ baseScore = query2.length;
1429
+ results.ranges.push([0, query2.length]);
1430
+ } else {
1431
+ const index = target.indexOf(query2);
1432
+ if (index !== -1) {
1433
+ matchTypeWeight = MATCH_WEIGHTS.SUBSTRING;
1434
+ baseScore = query2.length;
1435
+ let cursor = index;
1436
+ while (cursor !== -1) {
1437
+ results.ranges.push([cursor, cursor + query2.length]);
1438
+ cursor = target.indexOf(query2, cursor + 1);
1439
+ }
1440
+ } else {
1441
+ return results;
1442
+ }
1443
+ }
1444
+ const lengthDifference = target.length - query2.length;
1445
+ const specificityBonus = 1 / (1 + lengthDifference);
1446
+ results.score = baseScore * matchTypeWeight * specificityBonus;
1447
+ return results;
1448
+ }
1449
+ searchField(query2, value, tokens) {
1450
+ const result = { score: 0, ranges: [] };
1451
+ const addRangesFromValue = (searchTerm) => {
1452
+ let idx = value.indexOf(searchTerm);
1453
+ while (idx !== -1) {
1454
+ result.ranges.push([idx, idx + searchTerm.length]);
1455
+ idx = value.indexOf(searchTerm, idx + 1);
1456
+ }
1457
+ };
1458
+ if (query2.quoted || !tokens) {
1459
+ if (!this.enableSearchScoring) {
1460
+ if (value.includes(query2.value)) {
1461
+ result.score = 1;
1462
+ addRangesFromValue(query2.value);
1463
+ }
1464
+ } else {
1465
+ const calculation = this.calculateSearchScore(query2.value, value);
1466
+ result.score = calculation.score;
1467
+ result.ranges = calculation.ranges;
1468
+ }
1469
+ return result;
1470
+ }
1471
+ if (!this.enableSearchScoring) {
1472
+ const isMatch = tokens.some((token) => token.includes(query2.value));
1473
+ if (isMatch) {
1474
+ result.score = 1;
1475
+ addRangesFromValue(query2.value);
1476
+ }
1477
+ return result;
1478
+ }
1479
+ for (const token of tokens) {
1480
+ const calculation = this.calculateSearchScore(query2.value, token);
1481
+ if (calculation.score > 0) {
1482
+ result.score += calculation.score;
1483
+ addRangesFromValue(query2.value);
1484
+ }
1485
+ }
1486
+ return result;
1487
+ }
1488
+ filterField(value, filter, filterFunction = null) {
1489
+ if (Array.isArray(filter)) {
1490
+ if (filter.length === 0) {
1491
+ return true;
1492
+ }
1493
+ return filter.some(
1494
+ (element) => this.filterField(value, element, filterFunction)
1495
+ );
1496
+ }
1497
+ if (Array.isArray(value)) {
1498
+ if (value.length === 0) {
1499
+ return false;
1500
+ }
1501
+ return value.some(
1502
+ (element) => this.filterField(element, filter, filterFunction)
1503
+ );
1504
+ }
1505
+ if (typeof filterFunction === "function") {
1506
+ return filterFunction(value, filter);
1507
+ }
1508
+ if (filter instanceof RegExp) {
1509
+ return filter.test(String(value));
1510
+ }
1511
+ return filter === value;
1512
+ }
1513
+ filterRow(row, index) {
1514
+ if (!this.filters) {
1515
+ return true;
1516
+ }
1517
+ if (typeof this.filters === "function") {
1518
+ return this.filters(row, index);
1519
+ }
1520
+ for (const field in this.filters) {
1521
+ const filter = getNestedValue(this.filters, field);
1522
+ const value = getNestedValue(row, field);
1523
+ if (typeof filter === "function") {
1524
+ if (!filter(value)) {
1525
+ return false;
1526
+ }
1527
+ } else {
1528
+ const col = findColumn(field, this.columns);
1529
+ const filterCallback = col ? col.filter : void 0;
1530
+ if (!this.filterField(value, filter, filterCallback)) {
1531
+ return false;
1532
+ }
1533
+ }
1534
+ }
1535
+ return true;
1536
+ }
1537
+ filterRows() {
1538
+ const searchableFields = [...this.columnData.values()].filter((col) => col.options.searchable).map((c) => c.field);
1539
+ const fields = [...searchableFields, ...this.searchIncludedFields];
1540
+ this._filteredData = this.data.filter((row, index) => {
1541
+ const metadata = this.rowMetadata.get(row);
1542
+ metadata.searchScore = 0;
1543
+ metadata.highlightIndices = {};
1544
+ if (!this.filterRow(row, index)) {
1545
+ return false;
1546
+ }
1547
+ if (!this.queryTokens) {
1548
+ return true;
1549
+ }
1550
+ for (const field of fields) {
1551
+ const originalValue = getNestedValue(row, field);
1552
+ const compareValue = metadata.searchValues[field];
1553
+ const columnTokens = metadata.searchTokens[field];
1554
+ if (typeof originalValue !== "string" || typeof compareValue !== "string") {
1555
+ continue;
1556
+ }
1557
+ const fieldResults = { score: 0, ranges: [] };
1558
+ for (const token of this.queryTokens) {
1559
+ const results = this.searchField(token, compareValue, columnTokens);
1560
+ fieldResults.score += results.score;
1561
+ fieldResults.ranges.push(...results.ranges);
1562
+ }
1563
+ if (fieldResults.score > 0) {
1564
+ metadata.searchScore += fieldResults.score;
1565
+ metadata.highlightIndices[field] = fieldResults.ranges;
1566
+ }
1567
+ }
1568
+ return metadata.searchScore > 0;
1569
+ });
1570
+ this.filterDirty = false;
1571
+ this.sortRows();
1572
+ this.dispatchEvent(new YatlChangeEvent(this.data));
1573
+ }
1574
+ // #endregion
1575
+ // #region --- Sort Methods ---
1576
+ compareRows(a, b, field) {
1577
+ let aValue, bValue;
1578
+ const columnData = findColumn(field, this.columnData);
1579
+ if (!columnData.state.sort) {
1580
+ return 0;
1581
+ }
1582
+ const aMetadata = this.rowMetadata.get(a);
1583
+ const bMetadata = this.rowMetadata.get(b);
1584
+ if (columnData.state.sort?.order === "asc") {
1585
+ aValue = aMetadata.sortValues[columnData.field];
1586
+ bValue = bMetadata.sortValues[columnData.field];
1587
+ } else {
1588
+ aValue = bMetadata.sortValues[columnData.field];
1589
+ bValue = aMetadata.sortValues[columnData.field];
1590
+ }
1591
+ if (typeof columnData.options.sorter === "function") {
1592
+ const ret = columnData.options.sorter(aValue, bValue);
1593
+ if (ret !== 0) return ret;
1594
+ }
1595
+ const aIsNull = aValue == null;
1596
+ const bIsNull = bValue == null;
1597
+ if (aIsNull && !bIsNull) return -1;
1598
+ if (bIsNull && !aIsNull) return 1;
1599
+ if (aValue < bValue) return -1;
1600
+ if (aValue > bValue) return 1;
1601
+ return 0;
1602
+ }
1603
+ sortRows() {
1604
+ if (this.filterDirty) {
1605
+ this.filterRows();
1606
+ return;
1607
+ }
1608
+ const sortedColumns = this.columnData.filter((col) => col.state.visible && col.state.sort).sort((a, b) => b.state.sort.priority - a.state.sort.priority);
1609
+ this._filteredData = this._filteredData.toSorted((a, b) => {
1610
+ const aMetadata = this.rowMetadata.get(a);
1611
+ const bMetadata = this.rowMetadata.get(b);
1612
+ if (this.enableSearchScoring && this.queryTokens) {
1613
+ const aValue = aMetadata.searchScore || 0;
1614
+ const bValue = bMetadata.searchScore || 0;
1615
+ if (aValue > bValue) return -1;
1616
+ if (aValue < bValue) return 1;
1617
+ }
1618
+ for (const col of sortedColumns) {
1619
+ const comp = this.compareRows(a, b, col.field);
1620
+ if (comp !== 0) {
1621
+ return comp;
1622
+ }
1623
+ }
1624
+ return aMetadata.index - bMetadata.index;
1625
+ });
1626
+ this.sortDirty = false;
1627
+ }
1628
+ // #endregion
1629
+ // #region --- Utilities ---
1630
+ createMetadata() {
1631
+ this.rowMetadata = /* @__PURE__ */ new WeakMap();
1632
+ let index = 0;
1633
+ for (const row of this.data) {
1634
+ const metadata = {
1635
+ index: index++,
1636
+ searchTokens: {},
1637
+ searchValues: {},
1638
+ sortValues: {}
1639
+ };
1640
+ this.rowMetadata.set(row, metadata);
1641
+ for (const column of this.columns) {
1642
+ const value = getNestedValue(row, column.field);
1643
+ if (typeof column.sortValue === "function") {
1644
+ metadata.sortValues[column.field] = column.sortValue(value);
1645
+ } else if (typeof value === "string") {
1646
+ metadata.sortValues[column.field] = value.toLocaleLowerCase();
1647
+ } else if (isCompareable(value)) {
1648
+ metadata.sortValues[column.field] = value;
1649
+ } else {
1650
+ metadata.sortValues[column.field] = String(value);
1651
+ }
1652
+ if (typeof value === "string") {
1653
+ metadata.searchValues[column.field] = value.toLocaleLowerCase();
1654
+ }
1655
+ if (column.searchable && column.tokenize && value) {
1656
+ const tokenizer = column.searchTokenizer ?? this.searchTokenizer;
1657
+ metadata.searchTokens[column.field] = tokenizer(String(value)).map(
1658
+ (token) => token.value
1659
+ );
1660
+ }
1661
+ }
1662
+ for (const field of this.searchIncludedFields) {
1663
+ const value = getNestedValue(row, field);
1664
+ if (typeof value === "string") {
1665
+ metadata.searchValues[field] = value.toLocaleLowerCase();
1666
+ }
1667
+ }
1668
+ }
1669
+ }
1670
+ updateInternalQuery() {
1671
+ if (this.searchQuery.length === 0) {
1672
+ this.queryTokens = null;
1673
+ return;
1674
+ }
1675
+ this.queryTokens = [
1676
+ { value: this.searchQuery.toLocaleLowerCase(), quoted: true }
1677
+ ];
1678
+ if (this.enableSearchTokenization) {
1679
+ this.queryTokens.push(...this.searchTokenizer(this.searchQuery));
1680
+ }
1681
+ }
1682
+ get columnData() {
1683
+ return this.columns.map((column) => ({
1684
+ field: column.field,
1685
+ options: column,
1686
+ state: this.getColumnState(column.field)
1687
+ }));
1688
+ }
1689
+ /**
1690
+ * Gets the width of each column in the
1691
+ * order they will appear in the grid.
1692
+ */
1693
+ getGridWidths() {
1694
+ return this.columnOrder.map((field) => this.getColumnState(field)).filter((state2) => state2.visible).map((state2) => state2.width);
1695
+ }
1696
+ scheduleSave() {
1697
+ window.clearTimeout(this.saveTimer);
1698
+ this.saveTimer = window.setTimeout(() => {
1699
+ this.saveStateToStorage();
1700
+ }, STATE_SAVE_DEBOUNCE);
1701
+ }
1702
+ getColumnState(field) {
1703
+ let state2 = findColumn(field, this._columnStates);
1704
+ if (!state2) {
1705
+ state2 = {
1706
+ field,
1707
+ visible: true,
1708
+ sort: null,
1709
+ width: null
1710
+ };
1711
+ this._columnStates.push(state2);
1712
+ }
1713
+ return state2;
1714
+ }
1715
+ // #endregion
1716
+ // #region --- Storage Methods ---
1717
+ saveStateToStorage() {
1718
+ if (!this.storageOptions) {
1719
+ return;
1720
+ }
1721
+ const options = { ...DEFAULT_STORAGE_OPTIONS, ...this.storageOptions };
1722
+ const savedTableState = {
1723
+ columns: []
1724
+ };
1725
+ const tableState = this.getState();
1726
+ if (options.saveColumnOrder) {
1727
+ savedTableState.columnOrder = tableState.columnOrder;
1728
+ }
1729
+ for (const columnState of tableState.columns) {
1730
+ const savedColumnState = {
1731
+ field: columnState.field
1732
+ };
1733
+ if (options.saveColumnSortOrders) {
1734
+ savedColumnState.sort = columnState.sort;
1735
+ }
1736
+ if (options.saveColumnVisibility) {
1737
+ savedColumnState.visible = columnState.visible;
1738
+ }
1739
+ if (options.saveColumnWidths) {
1740
+ savedColumnState.width = columnState.width;
1741
+ }
1742
+ savedTableState.columns?.push(savedColumnState);
1743
+ }
1744
+ const storage = options.storage === "session" ? sessionStorage : localStorage;
1745
+ try {
1746
+ storage.setItem(options.key, JSON.stringify(savedTableState));
1747
+ } catch (error) {
1748
+ console.warn("Failed to save table state", error);
1749
+ }
1750
+ }
1751
+ loadStateFromStorage() {
1752
+ if (!this.storageOptions) {
1753
+ return;
1754
+ }
1755
+ const options = { ...DEFAULT_STORAGE_OPTIONS, ...this.storageOptions };
1756
+ const json = localStorage.getItem(options.key);
1757
+ if (!json) {
1758
+ return;
1759
+ }
1760
+ try {
1761
+ const savedTableState = JSON.parse(json);
1762
+ const tableStateToRestore = {};
1763
+ if (options.saveColumnOrder) {
1764
+ tableStateToRestore.columnOrder = savedTableState.columnOrder;
1765
+ }
1766
+ if (savedTableState.columns) {
1767
+ tableStateToRestore.columns = [];
1768
+ for (const savedColumnState of savedTableState.columns) {
1769
+ const columnStateToRestore = {
1770
+ field: savedColumnState.field
1771
+ };
1772
+ if (options.saveColumnVisibility) {
1773
+ columnStateToRestore.visible = savedColumnState.visible;
1774
+ }
1775
+ if (options.saveColumnWidths) {
1776
+ columnStateToRestore.width = savedColumnState.width;
1777
+ }
1778
+ if (options.saveColumnSortOrders) {
1779
+ columnStateToRestore.sort = savedColumnState.sort;
1780
+ }
1781
+ tableStateToRestore.columns.push(columnStateToRestore);
1782
+ }
1783
+ }
1784
+ this.restoreState(tableStateToRestore);
1785
+ this.hasRestoredState = true;
1786
+ } catch (error) {
1787
+ console.error("Failed to restore DataTable state:", error);
1788
+ }
1789
+ }
1790
+ handleResizeMouseDown(event, field) {
1791
+ event.preventDefault();
1792
+ event.stopPropagation();
1793
+ const target = event.target;
1794
+ const header = target.closest(".cell");
1795
+ if (!header) {
1796
+ return;
1797
+ }
1798
+ const columnIndex = this.columnOrder.findIndex((col) => col === field);
1799
+ if (columnIndex < 0) {
1800
+ return;
1801
+ }
1802
+ this.tableElement.querySelectorAll(".header .cell").forEach((element) => {
1803
+ const field2 = element.dataset.field;
1804
+ if (field2) {
1805
+ const state2 = this.getColumnState(field2);
1806
+ if (state2) {
1807
+ state2.width = element.getBoundingClientRect().width;
1808
+ }
1809
+ }
1810
+ });
1811
+ this.resizeState = {
1812
+ active: true,
1813
+ startX: event.pageX,
1814
+ startWidth: header.getBoundingClientRect().width,
1815
+ columnIndex,
1816
+ columnField: field,
1817
+ currentWidths: widthsToGridTemplates(this.getGridWidths())
1818
+ };
1819
+ this.tableElement.style.setProperty(
1820
+ "--grid-template",
1821
+ this.resizeState.currentWidths.join(" ")
1822
+ );
1823
+ window.addEventListener("mousemove", this.handleResizeMouseMove);
1824
+ window.addEventListener("mouseup", this.handleResizeMouseUp);
1825
+ document.body.style.cursor = "col-resize";
1826
+ }
1827
+ addEventListener(type, listener, options) {
1828
+ super.addEventListener(
1829
+ type,
1830
+ listener,
1831
+ options
1832
+ );
1833
+ }
1834
+ removeEventListener(type, listener, options) {
1835
+ super.removeEventListener(type, listener, options);
1836
+ }
1837
+ dispatchEvent(event) {
1838
+ return super.dispatchEvent(event);
1839
+ }
1840
+ // #endregion
1841
+ };
1842
+ YatlTable.styles = [yatl_table_styles_default];
1843
+ __decorateClass([
1844
+ query(".table")
1845
+ ], YatlTable.prototype, "tableElement", 2);
1846
+ __decorateClass([
1847
+ query("lit-virtualizer")
1848
+ ], YatlTable.prototype, "virtualizer", 2);
1849
+ __decorateClass([
1850
+ state()
1851
+ ], YatlTable.prototype, "_filteredData", 2);
1852
+ __decorateClass([
1853
+ property({ type: Boolean, attribute: "enable-virtual-scroll" })
1854
+ ], YatlTable.prototype, "enableVirtualScroll", 2);
1855
+ __decorateClass([
1856
+ property({ type: Boolean, attribute: "enable-search-highlight" })
1857
+ ], YatlTable.prototype, "enableSearchHighlight", 2);
1858
+ __decorateClass([
1859
+ property({ type: Boolean, attribute: "enable-search-tokenization" })
1860
+ ], YatlTable.prototype, "enableSearchTokenization", 1);
1861
+ __decorateClass([
1862
+ property({ type: Boolean, attribute: "enable-search-scoring" })
1863
+ ], YatlTable.prototype, "enableSearchScoring", 1);
1864
+ __decorateClass([
1865
+ property({ type: Boolean, attribute: "enable-column-reorder" })
1866
+ ], YatlTable.prototype, "enableColumnReorder", 2);
1867
+ __decorateClass([
1868
+ property({ type: Boolean, attribute: "enable-footer" })
1869
+ ], YatlTable.prototype, "enableFooter", 2);
1870
+ __decorateClass([
1871
+ property({ type: String, attribute: "null-value-placeholder" })
1872
+ ], YatlTable.prototype, "nullValuePlaceholder", 2);
1873
+ __decorateClass([
1874
+ property({ type: String, attribute: "empty-message" })
1875
+ ], YatlTable.prototype, "emptyMessage", 2);
1876
+ __decorateClass([
1877
+ property({ type: String, attribute: "no-results-message" })
1878
+ ], YatlTable.prototype, "noResultsMessage", 2);
1879
+ __decorateClass([
1880
+ property({ attribute: false })
1881
+ ], YatlTable.prototype, "columns", 1);
1882
+ __decorateClass([
1883
+ property({ attribute: false })
1884
+ ], YatlTable.prototype, "columnOrder", 1);
1885
+ __decorateClass([
1886
+ property({ attribute: false })
1887
+ ], YatlTable.prototype, "columnVisibility", 1);
1888
+ __decorateClass([
1889
+ property({ attribute: false })
1890
+ ], YatlTable.prototype, "columnSort", 1);
1891
+ __decorateClass([
1892
+ property({ attribute: false })
1893
+ ], YatlTable.prototype, "columnWidths", 1);
1894
+ __decorateClass([
1895
+ property({ type: String, attribute: "search-query" })
1896
+ ], YatlTable.prototype, "searchQuery", 1);
1897
+ __decorateClass([
1898
+ property({ type: Array, attribute: "search-included-fields" })
1899
+ ], YatlTable.prototype, "searchIncludedFields", 1);
1900
+ __decorateClass([
1901
+ property({ attribute: false })
1902
+ ], YatlTable.prototype, "searchTokenizer", 1);
1903
+ __decorateClass([
1904
+ property({ attribute: false })
1905
+ ], YatlTable.prototype, "filters", 1);
1906
+ __decorateClass([
1907
+ property({ attribute: false })
1908
+ ], YatlTable.prototype, "rowParts", 2);
1909
+ __decorateClass([
1910
+ property({ type: Object, attribute: "storage-options" })
1911
+ ], YatlTable.prototype, "storageOptions", 1);
1912
+ __decorateClass([
1913
+ property({ attribute: false })
1914
+ ], YatlTable.prototype, "data", 1);
1915
+ YatlTable = __decorateClass([
1916
+ customElement("yatl-table")
1917
+ ], YatlTable);
1918
+ export {
1919
+ YatlChangeEvent,
1920
+ YatlColumnReorderEvent,
1921
+ YatlColumnResizeEvent,
1922
+ YatlColumnToggleEvent,
1923
+ YatlEvent,
1924
+ YatlRowClickEvent,
1925
+ YatlSearchEvent,
1926
+ YatlSortEvent,
1927
+ YatlStateChangeEvent,
1928
+ YatlTable,
1929
+ createRegexTokenizer,
1930
+ findColumn,
1931
+ whitespaceTokenizer
1932
+ };
1933
+ //# sourceMappingURL=index.mjs.map