@timlassiter11/yatl 1.2.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -20,6 +20,8 @@ var YatlEvent = class extends Event {
20
20
  });
21
21
  }
22
22
  };
23
+ var YatlTableControllerEvent = class extends YatlEvent {
24
+ };
23
25
  var YatlRowClickEvent = class _YatlRowClickEvent extends YatlEvent {
24
26
  constructor(row, rowId, index, field, originalEvent) {
25
27
  super(_YatlRowClickEvent.EVENT_NAME);
@@ -32,15 +34,6 @@ var YatlRowClickEvent = class _YatlRowClickEvent extends YatlEvent {
32
34
  static {
33
35
  this.EVENT_NAME = "yatl-row-click";
34
36
  }
35
- clone() {
36
- return new _YatlRowClickEvent(
37
- this.row,
38
- this.rowId,
39
- this.index,
40
- this.field,
41
- this.originalEvent
42
- );
43
- }
44
37
  };
45
38
  var YatlRowSelectRequest = class _YatlRowSelectRequest extends YatlEvent {
46
39
  constructor(rowId, selected, currentlySelectedRows) {
@@ -52,15 +45,8 @@ var YatlRowSelectRequest = class _YatlRowSelectRequest extends YatlEvent {
52
45
  static {
53
46
  this.EVENT_NAME = "yatl-row-select-request";
54
47
  }
55
- clone() {
56
- return new _YatlRowSelectRequest(
57
- this.rowId,
58
- this.selected,
59
- this.currentlySelectedRows
60
- );
61
- }
62
48
  };
63
- var YatlRowSelectEvent = class _YatlRowSelectEvent extends YatlEvent {
49
+ var YatlRowSelectEvent = class _YatlRowSelectEvent extends YatlTableControllerEvent {
64
50
  constructor(selectedIds, previouslySelectedRows) {
65
51
  super(_YatlRowSelectEvent.EVENT_NAME);
66
52
  this.selectedIds = selectedIds;
@@ -86,11 +72,8 @@ var YatlColumnSortRequest = class _YatlColumnSortRequest extends YatlEvent {
86
72
  static {
87
73
  this.EVENT_NAME = "yatl-column-sort-request";
88
74
  }
89
- clone() {
90
- return new _YatlColumnSortRequest(this.field, this.order, this.multisort);
91
- }
92
75
  };
93
- var YatlColumnSortEvent = class _YatlColumnSortEvent extends YatlEvent {
76
+ var YatlColumnSortEvent = class _YatlColumnSortEvent extends YatlTableControllerEvent {
94
77
  constructor(field, order, multisort) {
95
78
  super(_YatlColumnSortEvent.EVENT_NAME);
96
79
  this.field = field;
@@ -104,7 +87,7 @@ var YatlColumnSortEvent = class _YatlColumnSortEvent extends YatlEvent {
104
87
  return new _YatlColumnSortEvent(this.field, this.order, this.multisort);
105
88
  }
106
89
  };
107
- var YatlColumnToggleEvent = class _YatlColumnToggleEvent extends YatlEvent {
90
+ var YatlColumnToggleEvent = class _YatlColumnToggleEvent extends YatlTableControllerEvent {
108
91
  constructor(field, visible) {
109
92
  super(_YatlColumnToggleEvent.EVENT_NAME);
110
93
  this.field = field;
@@ -117,7 +100,7 @@ var YatlColumnToggleEvent = class _YatlColumnToggleEvent extends YatlEvent {
117
100
  return new _YatlColumnToggleEvent(this.field, this.visible);
118
101
  }
119
102
  };
120
- var YatlColumnResizeEvent = class _YatlColumnResizeEvent extends YatlEvent {
103
+ var YatlColumnResizeEvent = class _YatlColumnResizeEvent extends YatlTableControllerEvent {
121
104
  constructor(field, width) {
122
105
  super(_YatlColumnResizeEvent.EVENT_NAME);
123
106
  this.field = field;
@@ -140,15 +123,8 @@ var YatlColumnReorderRequest = class _YatlColumnReorderRequest extends YatlEvent
140
123
  static {
141
124
  this.EVENT_NAME = "yatl-column-reorder-request";
142
125
  }
143
- clone() {
144
- return new _YatlColumnReorderRequest(
145
- this.movedColumn,
146
- this.originalIndex,
147
- this.newIndex
148
- );
149
- }
150
126
  };
151
- var YatlColumnReorderEvent = class _YatlColumnReorderEvent extends YatlEvent {
127
+ var YatlColumnReorderEvent = class _YatlColumnReorderEvent extends YatlTableControllerEvent {
152
128
  constructor(order) {
153
129
  super(_YatlColumnReorderEvent.EVENT_NAME);
154
130
  this.order = order;
@@ -160,7 +136,20 @@ var YatlColumnReorderEvent = class _YatlColumnReorderEvent extends YatlEvent {
160
136
  return new _YatlColumnReorderEvent(this.order);
161
137
  }
162
138
  };
163
- var YatlTableSearchEvent = class _YatlTableSearchEvent extends YatlEvent {
139
+ var YatlCellEditEvent = class _YatlCellEditEvent extends YatlEvent {
140
+ constructor(row, rowId, field, originalValue, currentValue) {
141
+ super(_YatlCellEditEvent.EVENT_NAME);
142
+ this.row = row;
143
+ this.rowId = rowId;
144
+ this.field = field;
145
+ this.originalValue = originalValue;
146
+ this.currentValue = currentValue;
147
+ }
148
+ static {
149
+ this.EVENT_NAME = "yatl-cell-edit";
150
+ }
151
+ };
152
+ var YatlTableSearchEvent = class _YatlTableSearchEvent extends YatlTableControllerEvent {
164
153
  constructor(query2) {
165
154
  super(_YatlTableSearchEvent.EVENT_NAME);
166
155
  this.query = query2;
@@ -172,7 +161,7 @@ var YatlTableSearchEvent = class _YatlTableSearchEvent extends YatlEvent {
172
161
  return new _YatlTableSearchEvent(this.query);
173
162
  }
174
163
  };
175
- var YatlTableViewChangeEvent = class _YatlTableViewChangeEvent extends YatlEvent {
164
+ var YatlTableViewChangeEvent = class _YatlTableViewChangeEvent extends YatlTableControllerEvent {
176
165
  constructor(data) {
177
166
  super(_YatlTableViewChangeEvent.EVENT_NAME);
178
167
  this.data = data;
@@ -184,7 +173,7 @@ var YatlTableViewChangeEvent = class _YatlTableViewChangeEvent extends YatlEvent
184
173
  return new _YatlTableViewChangeEvent(this.data);
185
174
  }
186
175
  };
187
- var YatlTableStateChangeEvent = class _YatlTableStateChangeEvent extends YatlEvent {
176
+ var YatlTableStateChangeEvent = class _YatlTableStateChangeEvent extends YatlTableControllerEvent {
188
177
  constructor(state2, triggers) {
189
178
  super(_YatlTableStateChangeEvent.EVENT_NAME);
190
179
  this.state = state2;
@@ -198,6 +187,139 @@ var YatlTableStateChangeEvent = class _YatlTableStateChangeEvent extends YatlEve
198
187
  }
199
188
  };
200
189
 
190
+ // src/editors/input-editor.ts
191
+ import { html } from "lit";
192
+ import { ifDefined } from "lit/directives/if-defined.js";
193
+ import { live } from "lit/directives/live.js";
194
+
195
+ // src/editors/base.ts
196
+ var BaseEditor = class {
197
+ constructor(options) {
198
+ this.options = options;
199
+ }
200
+ reset() {
201
+ this.currentValue = void 0;
202
+ }
203
+ canEdit(field, row) {
204
+ return this.options?.canEdit?.(field, row) ?? true;
205
+ }
206
+ save(originalValue, field, row, _controller) {
207
+ if (this.currentValue === void 0 || this.currentValue === originalValue) {
208
+ return;
209
+ }
210
+ if (!this.options?.onSave) {
211
+ return this.currentValue;
212
+ }
213
+ return this.options.onSave(originalValue, this.currentValue, field, row);
214
+ }
215
+ };
216
+
217
+ // src/editors/input-editor.ts
218
+ var InputEditor = class extends BaseEditor {
219
+ constructor(options) {
220
+ super(options);
221
+ this.options = options;
222
+ this.handleChange = (event) => {
223
+ const target = event.target;
224
+ const { type, max, min } = this.options ?? {};
225
+ if (type === "checkbox") {
226
+ this.currentValue = target.checked;
227
+ } else if (type === "date" || type === "datetime-local") {
228
+ this.currentValue = target.valueAsDate;
229
+ } else if (type === "number") {
230
+ let value = target.valueAsNumber;
231
+ if (isNaN(value)) {
232
+ value = null;
233
+ } else {
234
+ if (typeof max === "number" && value > max) {
235
+ target.valueAsNumber = max;
236
+ value = max;
237
+ } else if (typeof min === "number" && value < min) {
238
+ target.valueAsNumber = min;
239
+ value = min;
240
+ }
241
+ this.currentValue = value;
242
+ }
243
+ } else {
244
+ this.currentValue = target.value;
245
+ }
246
+ };
247
+ }
248
+ render(value, _field, _row, _controller) {
249
+ return html`
250
+ <input
251
+ .value=${live(String(value ?? ""))}
252
+ type=${ifDefined(this.options?.type)}
253
+ minlength=${ifDefined(this.options?.minlength)}
254
+ maxlength=${ifDefined(this.options?.maxlength)}
255
+ min=${ifDefined(this.options?.min)}
256
+ max=${ifDefined(this.options?.max)}
257
+ step=${ifDefined(this.options?.step)}
258
+ pattern=${ifDefined(this.options?.pattern)}
259
+ placeholder=${ifDefined(this.options?.placeholder)}
260
+ autofocus
261
+ @input=${this.handleChange}
262
+ />
263
+ `;
264
+ }
265
+ };
266
+
267
+ // src/editors/number-editor.ts
268
+ var NumberEditor = class extends InputEditor {
269
+ constructor(options) {
270
+ super({
271
+ type: "number",
272
+ ...options
273
+ });
274
+ }
275
+ };
276
+
277
+ // src/editors/select-editor.ts
278
+ import { html as html2 } from "lit";
279
+ import { live as live2 } from "lit/directives/live.js";
280
+ import { repeat } from "lit/directives/repeat.js";
281
+ var SelectEditor = class extends BaseEditor {
282
+ constructor(options) {
283
+ super(options);
284
+ this.options = options;
285
+ this.handleChange = (event) => {
286
+ const target = event.target;
287
+ this.currentValue = target.value;
288
+ };
289
+ }
290
+ render(value, field, row, controller) {
291
+ const values = controller.getColumnFilterValues(field, false);
292
+ return html2`
293
+ <select .value=${live2(String(value))} @change=${this.handleChange}>
294
+ ${repeat(
295
+ values.keys(),
296
+ (option) => option,
297
+ (option) => this.renderOption(option, option === value)
298
+ )}
299
+ </select>
300
+ `;
301
+ }
302
+ renderOption(option, select) {
303
+ const [value, display] = this.options?.labelRenderer?.(option) ?? [
304
+ String(option),
305
+ String(option)
306
+ ];
307
+ return html2`
308
+ <option value=${value} ?selected=${select}>${display}</option>
309
+ `;
310
+ }
311
+ };
312
+
313
+ // src/editors/text-editor.ts
314
+ var TextEditor = class extends InputEditor {
315
+ constructor(options) {
316
+ super({
317
+ type: "text",
318
+ ...options
319
+ });
320
+ }
321
+ };
322
+
201
323
  // src/utils/columns.ts
202
324
  function findColumn(columns, field) {
203
325
  return columns.find((c) => c.field === field);
@@ -310,7 +432,7 @@ var TypedEventTarget = class extends EventTarget {
310
432
  };
311
433
 
312
434
  // src/table/utils.ts
313
- import { html } from "lit";
435
+ import { html as html3 } from "lit";
314
436
  function highlightText(text, ranges) {
315
437
  if (!text || !ranges || ranges.length === 0) {
316
438
  return text;
@@ -337,25 +459,25 @@ function highlightText(text, ranges) {
337
459
  result.push(text.slice(lastIndex, safeStart));
338
460
  }
339
461
  result.push(
340
- html`<mark class="highlight">${text.slice(safeStart, safeEnd)}</mark>`
462
+ html3`<mark class="highlight">${text.slice(safeStart, safeEnd)}</mark>`
341
463
  );
342
464
  lastIndex = safeEnd;
343
465
  }
344
466
  if (lastIndex < text.length) {
345
467
  result.push(text.slice(lastIndex));
346
468
  }
347
- return html`${result}`;
469
+ return html3`${result}`;
348
470
  }
349
471
  var toHumanReadable = (str) => {
350
472
  return str.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (char) => char.toUpperCase());
351
473
  };
352
474
 
353
475
  // src/table/table.ts
354
- import { html as html2, LitElement, nothing } from "lit";
476
+ import { html as html4, LitElement, nothing } from "lit";
355
477
  import { customElement, property, query, state } from "lit/decorators.js";
356
478
  import { classMap } from "lit/directives/class-map.js";
357
- import { ifDefined } from "lit/directives/if-defined.js";
358
- import { repeat } from "lit/directives/repeat.js";
479
+ import { ifDefined as ifDefined2 } from "lit/directives/if-defined.js";
480
+ import { repeat as repeat2 } from "lit/directives/repeat.js";
359
481
  import { styleMap } from "lit/directives/style-map.js";
360
482
  import "@lit-labs/virtualizer";
361
483
  import { virtualizerRef } from "@lit-labs/virtualizer/virtualize.js";
@@ -399,7 +521,7 @@ function createRankMap(values, locale) {
399
521
  // src/table-controller/table-controller.ts
400
522
  var STATE_SAVE_DEBOUNCE = 1e3;
401
523
  var DEFAULT_STORAGE_OPTIONS = {
402
- storage: "local",
524
+ storage: window.localStorage,
403
525
  saveColumnSortOrders: true,
404
526
  saveColumnVisibility: true,
405
527
  saveColumnWidths: true,
@@ -419,8 +541,8 @@ var YatlTableController = class extends TypedEventTarget {
419
541
  // #region State Data
420
542
  // Property data
421
543
  this.hosts = /* @__PURE__ */ new Set();
422
- this._enableSearchTokenization = false;
423
- this._enableSearchScoring = false;
544
+ this._tokenizedSearch = false;
545
+ this._scoredSearch = false;
424
546
  // Original options passed by the user
425
547
  this._columns = [];
426
548
  // Options mapped by field for faster lookup
@@ -464,10 +586,10 @@ var YatlTableController = class extends TypedEventTarget {
464
586
  this.attach(host);
465
587
  }
466
588
  if (options) {
467
- if (options.enableSearchScoring !== void 0)
468
- this.enableSearchScoring = options.enableSearchScoring;
469
- if (options.enableSearchTokenization !== void 0)
470
- this.enableSearchTokenization = options.enableSearchTokenization;
589
+ if (options.scoredSearch !== void 0)
590
+ this.scoredSearch = options.scoredSearch;
591
+ if (options.tokenizedSearch !== void 0)
592
+ this.tokenizedSearch = options.tokenizedSearch;
471
593
  if (options.searchTokenizer !== void 0)
472
594
  this.searchTokenizer = options.searchTokenizer;
473
595
  if (options.rowIdCallback !== void 0)
@@ -482,46 +604,6 @@ var YatlTableController = class extends TypedEventTarget {
482
604
  }
483
605
  // #endregion
484
606
  // #region Properties
485
- /**
486
- * Enables tokenized search behavior.
487
- * When enabled, the search query is split into individual tokens using the
488
- * `searchTokenizer` function (defaults to splitting on whitespace).
489
- * A row is considered a match if **ANY** of the tokens appear in the searchable fields.
490
- * @default false
491
- */
492
- get enableSearchTokenization() {
493
- return this._enableSearchTokenization;
494
- }
495
- set enableSearchTokenization(enable) {
496
- if (this._enableSearchTokenization === enable) {
497
- return;
498
- }
499
- this._enableSearchTokenization = enable;
500
- this.updateInternalQuery();
501
- this.filterDirty = true;
502
- this.requestUpdate("enableSearchTokenization");
503
- }
504
- /**
505
- * Enables weighted relevance scoring for search results.
506
- * When enabled, exact matches and prefix matches are ranked higher than substring matches.
507
- * Rows are sorted by their relevance score descending.
508
- * @default false
509
- */
510
- get enableSearchScoring() {
511
- return this._enableSearchScoring;
512
- }
513
- set enableSearchScoring(enable) {
514
- if (this._enableSearchScoring === enable) {
515
- return;
516
- }
517
- this._enableSearchScoring = enable;
518
- this.filterDirty = true;
519
- this.requestUpdate("enableSearchScoring");
520
- }
521
- /**
522
- * The definitions for the columns to be rendered.
523
- * This defines the field mapping, titles, sortability, and other static options.
524
- */
525
607
  get columns() {
526
608
  return [...this._columns];
527
609
  }
@@ -563,10 +645,43 @@ var YatlTableController = class extends TypedEventTarget {
563
645
  }
564
646
  this.requestUpdate("columnStates");
565
647
  }
566
- /**
567
- * The current text string used to filter the table data.
568
- * Setting this property triggers a new search and render cycle.
569
- */
648
+ get data() {
649
+ return [...this._data];
650
+ }
651
+ set data(data) {
652
+ if (this._data === data) {
653
+ return;
654
+ }
655
+ this._data = [...data];
656
+ this.createMetadata();
657
+ this._dataUpdateTimestamp = /* @__PURE__ */ new Date();
658
+ this.filterDirty = true;
659
+ this.requestUpdate("data");
660
+ }
661
+ get filteredData() {
662
+ if (this.filterDirty) {
663
+ this.filterRows();
664
+ } else if (this.sortDirty) {
665
+ this.sortRows();
666
+ }
667
+ this.filterDirty = false;
668
+ this.sortDirty = false;
669
+ return [...this._filteredData];
670
+ }
671
+ get dataUpdateTimestamp() {
672
+ return this._dataUpdateTimestamp;
673
+ }
674
+ get filters() {
675
+ return this._filters;
676
+ }
677
+ set filters(filters) {
678
+ if (this._filters === filters) {
679
+ return;
680
+ }
681
+ this._filters = filters;
682
+ this.filterDirty = true;
683
+ this.requestUpdate("filters");
684
+ }
570
685
  get searchQuery() {
571
686
  return this._searchQuery;
572
687
  }
@@ -579,11 +694,29 @@ var YatlTableController = class extends TypedEventTarget {
579
694
  this.filterDirty = true;
580
695
  this.requestUpdate("searchQuery");
581
696
  }
582
- /**
583
- * A function that splits the search query into tokens.
584
- * Only used if `enableSearchTokenization` is true.
585
- * @default whitespaceTokenizer
586
- */
697
+ get tokenizedSearch() {
698
+ return this._tokenizedSearch;
699
+ }
700
+ set tokenizedSearch(enable) {
701
+ if (this._tokenizedSearch === enable) {
702
+ return;
703
+ }
704
+ this._tokenizedSearch = enable;
705
+ this.updateInternalQuery();
706
+ this.filterDirty = true;
707
+ this.requestUpdate("tokenizedSearch");
708
+ }
709
+ get scoredSearch() {
710
+ return this._scoredSearch;
711
+ }
712
+ set scoredSearch(enable) {
713
+ if (this._scoredSearch === enable) {
714
+ return;
715
+ }
716
+ this._scoredSearch = enable;
717
+ this.filterDirty = true;
718
+ this.requestUpdate("scoredSearch");
719
+ }
587
720
  get searchTokenizer() {
588
721
  return this._searchTokenizer;
589
722
  }
@@ -595,38 +728,6 @@ var YatlTableController = class extends TypedEventTarget {
595
728
  this.filterDirty = true;
596
729
  this.requestUpdate("searchTokenizer");
597
730
  }
598
- /**
599
- * An optional set of criteria to filter the visible rows.
600
- * This runs **before** the global search query is applied.
601
- * * You can provide:
602
- * 1. A **Partial Object**: matches rows where specific keys equal specific values (AND logic).
603
- * 2. A **Callback Function**: returns `true` to keep the row, `false` to hide it.
604
- * * @example
605
- * // 1. Object Syntax (Simple Exact Match)
606
- * // Shows rows where status is 'active' AND role is 'admin'
607
- * table.filters = { status: 'active', role: 'admin' };
608
- * * @example
609
- * // 2. Callback Syntax (Complex Logic)
610
- * // Shows rows where age is over 21 OR they are a VIP
611
- * table.filters = (row) => row.age > 21 || row.isVip;
612
- */
613
- get filters() {
614
- return this._filters;
615
- }
616
- set filters(filters) {
617
- if (this._filters === filters) {
618
- return;
619
- }
620
- this._filters = filters;
621
- this.filterDirty = true;
622
- this.requestUpdate("filters");
623
- }
624
- /**
625
- * The row selection method to use.
626
- * * single - Only a single row can be selected at a time
627
- * * multi - Multiple rows can be selected at a time
628
- * * null - Disable row selection
629
- */
630
731
  get rowSelectionMethod() {
631
732
  return this._rowSelectionMethod;
632
733
  }
@@ -637,11 +738,6 @@ var YatlTableController = class extends TypedEventTarget {
637
738
  this._rowSelectionMethod = selection;
638
739
  this.requestUpdate("rowSelectionMethod");
639
740
  }
640
- /**
641
- * List of currently selected row indexes.
642
- * * **NOTE**: These indexes are based off the of
643
- * the original data array index, *not* the filtered data.
644
- */
645
741
  get selectedRowIds() {
646
742
  let selectedRows = [...this._selectedRowIds];
647
743
  if (this.rowSelectionMethod === "single") {
@@ -658,23 +754,6 @@ var YatlTableController = class extends TypedEventTarget {
658
754
  this._selectedRowIds = new Set(rows);
659
755
  this.requestUpdate("selectedRowIds");
660
756
  }
661
- /**
662
- * Configuration options for automatically saving and restoring table state
663
- * (column width, order, visibility, etc.) to browser storage.
664
- */
665
- get storageOptions() {
666
- return this._storageOptions ? { ...this._storageOptions } : null;
667
- }
668
- set storageOptions(options) {
669
- if (this._storageOptions === options) {
670
- return;
671
- }
672
- this._storageOptions = options ? { ...options } : null;
673
- if (!this.hasRestoredState) {
674
- this.loadStateFromStorage();
675
- this.requestUpdate("storageOptions");
676
- }
677
- }
678
757
  get rowIdCallback() {
679
758
  return this._rowIdCallback;
680
759
  }
@@ -689,32 +768,18 @@ var YatlTableController = class extends TypedEventTarget {
689
768
  }
690
769
  this.requestUpdate("rowIdCallback");
691
770
  }
692
- /**
693
- * The array of data objects to be displayed.
694
- * Objects must satisfy the `WeakKey` constraint (objects only, no primitives).
695
- */
696
- get data() {
697
- return [...this._data];
698
- }
699
- set data(data) {
700
- this._data = [...data];
701
- this.createMetadata();
702
- this._dataUpdateTimestamp = /* @__PURE__ */ new Date();
703
- this.filterDirty = true;
704
- this.requestUpdate("data");
771
+ get storageOptions() {
772
+ return this._storageOptions ? { ...this._storageOptions } : null;
705
773
  }
706
- get filteredData() {
707
- if (this.filterDirty) {
708
- this.filterRows();
709
- } else if (this.sortDirty) {
710
- this.sortRows();
774
+ set storageOptions(options) {
775
+ if (this._storageOptions === options) {
776
+ return;
777
+ }
778
+ this._storageOptions = options ? { ...options } : null;
779
+ if (!this.hasRestoredState) {
780
+ this.loadStateFromStorage();
781
+ this.requestUpdate("storageOptions");
711
782
  }
712
- this.filterDirty = false;
713
- this.sortDirty = false;
714
- return [...this._filteredData];
715
- }
716
- get dataUpdateTimestamp() {
717
- return this._dataUpdateTimestamp;
718
783
  }
719
784
  attach(host) {
720
785
  this.hosts.add(host);
@@ -791,9 +856,14 @@ var YatlTableController = class extends TypedEventTarget {
791
856
  if (column?.valueFormatter) {
792
857
  value = column.valueFormatter(value, row);
793
858
  }
794
- if (value != null || includeNull) {
795
- const valueCount = values.get(value) ?? 0;
796
- values.set(value, valueCount + 1);
859
+ if (!Array.isArray(value)) {
860
+ value = [value];
861
+ }
862
+ for (const item of value) {
863
+ if (item != null || includeNull) {
864
+ const valueCount = values.get(item) ?? 0;
865
+ values.set(item, valueCount + 1);
866
+ }
797
867
  }
798
868
  }
799
869
  return values;
@@ -1028,8 +1098,7 @@ var YatlTableController = class extends TypedEventTarget {
1028
1098
  updateRow(rowId, data) {
1029
1099
  const row = this.idToRowMap.get(rowId);
1030
1100
  if (row) {
1031
- Object.assign(row, data);
1032
- this.requestUpdate("data");
1101
+ this.updateRowData(row, data);
1033
1102
  }
1034
1103
  }
1035
1104
  /**
@@ -1048,8 +1117,7 @@ var YatlTableController = class extends TypedEventTarget {
1048
1117
  updateRowAtIndex(index, data) {
1049
1118
  const row = this.data[index];
1050
1119
  if (row) {
1051
- Object.assign(row, data);
1052
- this.requestUpdate("data");
1120
+ this.updateRowData(row, data);
1053
1121
  }
1054
1122
  }
1055
1123
  /**
@@ -1128,6 +1196,9 @@ var YatlTableController = class extends TypedEventTarget {
1128
1196
  new YatlTableStateChangeEvent(this.getTableState(), triggers)
1129
1197
  );
1130
1198
  }
1199
+ if (props.includes("data")) {
1200
+ this.filterDirty = true;
1201
+ }
1131
1202
  for (const host of this.hosts) {
1132
1203
  host.requestUpdate();
1133
1204
  }
@@ -1193,7 +1264,7 @@ var YatlTableController = class extends TypedEventTarget {
1193
1264
  }
1194
1265
  };
1195
1266
  if (query2.quoted || !tokens) {
1196
- if (!this.enableSearchScoring) {
1267
+ if (!this.scoredSearch) {
1197
1268
  if (value.includes(query2.value)) {
1198
1269
  result.score = 1;
1199
1270
  addRangesFromValue(query2.value);
@@ -1205,7 +1276,7 @@ var YatlTableController = class extends TypedEventTarget {
1205
1276
  }
1206
1277
  return result;
1207
1278
  }
1208
- if (!this.enableSearchScoring) {
1279
+ if (!this.scoredSearch) {
1209
1280
  const isMatch = tokens.some((token) => token.includes(query2.value));
1210
1281
  if (isMatch) {
1211
1282
  result.score = 1;
@@ -1340,7 +1411,7 @@ var YatlTableController = class extends TypedEventTarget {
1340
1411
  this._filteredData = this._filteredData.toSorted((a, b) => {
1341
1412
  const aMetadata = this.rowMetadata.get(a);
1342
1413
  const bMetadata = this.rowMetadata.get(b);
1343
- if (this.enableSearchScoring && this.queryTokens) {
1414
+ if (this.scoredSearch && this.queryTokens) {
1344
1415
  const aValue = aMetadata.searchScore || 0;
1345
1416
  const bValue = bMetadata.searchScore || 0;
1346
1417
  if (aValue > bValue) return -1;
@@ -1403,6 +1474,11 @@ var YatlTableController = class extends TypedEventTarget {
1403
1474
  }
1404
1475
  }
1405
1476
  }
1477
+ updateRowData(row, data) {
1478
+ Object.assign(row, data);
1479
+ this.createMetadata();
1480
+ this.requestUpdate("data");
1481
+ }
1406
1482
  updateInternalQuery() {
1407
1483
  if (this.searchQuery.length === 0) {
1408
1484
  this.queryTokens = null;
@@ -1411,7 +1487,7 @@ var YatlTableController = class extends TypedEventTarget {
1411
1487
  this.queryTokens = [
1412
1488
  { value: this.searchQuery.toLocaleLowerCase(), quoted: true }
1413
1489
  ];
1414
- if (this.enableSearchTokenization) {
1490
+ if (this.tokenizedSearch) {
1415
1491
  this.queryTokens.push(...this.searchTokenizer(this.searchQuery));
1416
1492
  }
1417
1493
  }
@@ -1453,7 +1529,7 @@ var YatlTableController = class extends TypedEventTarget {
1453
1529
  }
1454
1530
  savedTableState.columns?.push(savedColumnState);
1455
1531
  }
1456
- const storage = options.storage === "session" ? sessionStorage : localStorage;
1532
+ const storage = options.storage ?? window.localStorage;
1457
1533
  try {
1458
1534
  storage.setItem(options.key, JSON.stringify(savedTableState));
1459
1535
  } catch (error) {
@@ -1465,7 +1541,8 @@ var YatlTableController = class extends TypedEventTarget {
1465
1541
  return;
1466
1542
  }
1467
1543
  const options = { ...DEFAULT_STORAGE_OPTIONS, ...this.storageOptions };
1468
- const json = localStorage.getItem(options.key);
1544
+ const storage = options.storage ?? window.localStorage;
1545
+ const json = storage.getItem(options.key);
1469
1546
  if (!json) {
1470
1547
  return;
1471
1548
  }
@@ -1554,7 +1631,7 @@ var table_styles_default = css`
1554
1631
  );
1555
1632
 
1556
1633
  --table-header-text: var(--yatl-table-header-text, var(--yatl-text-1));
1557
- --table-header-bg: var(--yatl-table-header-bg, var(--yatl-surface-lowered));
1634
+ --table-header-bg: var(--yatl-table-header-bg, var(--yatl-surface-2));
1558
1635
  --table-header-hover-bg: var(
1559
1636
  --yatl-table-header-hover-bg,
1560
1637
  color-mix(in srgb, var(--yatl-color-mix) 4%, var(--table-header-bg))
@@ -1656,6 +1733,20 @@ var table_styles_default = css`
1656
1733
  justify-content: flex-end;
1657
1734
  }
1658
1735
 
1736
+ .cell.is-editing {
1737
+ padding: 0;
1738
+ }
1739
+
1740
+ .cell.is-editing > * {
1741
+ width: 100%;
1742
+ height: 100%;
1743
+ box-sizing: border-box;
1744
+ outline: none;
1745
+ padding: var(--yatl-spacing-m);
1746
+ border: 1px solid var(--yatl-color-brand);
1747
+ border-radius: var(--yatl-radius-xs);
1748
+ }
1749
+
1659
1750
  .table.resizing * {
1660
1751
  cursor: col-resize !important;
1661
1752
  }
@@ -1683,8 +1774,17 @@ var table_styles_default = css`
1683
1774
  transition: background-color 0.2s;
1684
1775
  }
1685
1776
 
1777
+ .cell-wrapper:has(.cell-index) {
1778
+ overflow: visible;
1779
+ background-color: var(--table-header-bg);
1780
+ height: calc(100% + var(--table-border-width));
1781
+ }
1782
+
1686
1783
  .row-number-cell {
1687
1784
  background-color: var(--table-header-bg);
1785
+ border-right-width: var(--table-border-width);
1786
+ border-right-color: var(--table-border-color);
1787
+ border-right-style: solid;
1688
1788
  }
1689
1789
 
1690
1790
  .message {
@@ -1731,11 +1831,10 @@ var table_styles_default = css`
1731
1831
  }
1732
1832
 
1733
1833
  /* Layout stuff
1734
- * Most of this is functional and needed
1735
- * for the table to work properly.
1736
- * Modify with caution!
1737
- */
1738
- /* stylelint-disable-next-line no-duplicate-selectors */
1834
+ * Most of this is functional and needed
1835
+ * for the table to work properly.
1836
+ * Modify with caution!
1837
+ */
1739
1838
  :host {
1740
1839
  display: block;
1741
1840
  width: 100%;
@@ -1916,6 +2015,21 @@ var table_styles_default = css`
1916
2015
  white-space: nowrap;
1917
2016
  overflow: hidden;
1918
2017
  }
2018
+
2019
+ @media print {
2020
+ .table {
2021
+ color: black;
2022
+ width: 100%;
2023
+ }
2024
+
2025
+ .row {
2026
+ break-inside: avoid;
2027
+ }
2028
+
2029
+ .cell {
2030
+ border: 1px solid black;
2031
+ }
2032
+ }
1919
2033
  `;
1920
2034
 
1921
2035
  // src/table/table.ts
@@ -1923,22 +2037,20 @@ var YatlTable = class extends LitElement {
1923
2037
  constructor() {
1924
2038
  super(...arguments);
1925
2039
  // #region --- State Data ---
1926
- // Property data
1927
2040
  this.resizeState = null;
1928
2041
  // Column drag & drop state
1929
2042
  this.dragColumn = null;
1930
2043
  this.useYatlUi = false;
1931
- // #endregion
1932
- // #region --- Properties ---
2044
+ this.editingState = null;
1933
2045
  this._controller = new YatlTableController(this);
1934
2046
  this.striped = false;
1935
- this.sortable = true;
1936
- this.resizable = true;
1937
- this.enableVirtualScroll = false;
1938
- this.enableSearchHighlight = true;
1939
- this.enableColumnReorder = true;
1940
- this.enableRowNumberColumn = true;
1941
- this.enableFooter = true;
2047
+ this.sortable = false;
2048
+ this.resizable = false;
2049
+ this.reorderable = false;
2050
+ this.rowNumbers = false;
2051
+ this.virtualScroll = false;
2052
+ this.hideFooter = false;
2053
+ this.disableEditing = false;
1942
2054
  this.nullValuePlaceholder = "-";
1943
2055
  this.emptyMessage = "No records to display";
1944
2056
  this.noResultsMessage = "No matching records found";
@@ -1956,7 +2068,7 @@ var YatlTable = class extends LitElement {
1956
2068
  "yatl-table-view-change"
1957
2069
  ];
1958
2070
  this.redispatchControllerEvent = (event) => {
1959
- if (event instanceof YatlEvent) {
2071
+ if (event instanceof YatlTableControllerEvent) {
1960
2072
  this.dispatchEvent(event.clone());
1961
2073
  }
1962
2074
  };
@@ -1986,7 +2098,13 @@ var YatlTable = class extends LitElement {
1986
2098
  this.sort(column.field, sortOrder, !multiSort);
1987
2099
  };
1988
2100
  this.handleCellClick = (event, row, field) => {
1989
- if (window.getSelection()?.toString()) return;
2101
+ if (this.editingState || window.getSelection()?.toString()) {
2102
+ return;
2103
+ }
2104
+ const target = event.target;
2105
+ if (target.tagName === "A" || target.tagName === "BUTTON" || target.tagName === "YATL-BUTTON") {
2106
+ return;
2107
+ }
1990
2108
  const rowId = this.controller.getRowId(row);
1991
2109
  const rowIndex = this.controller.getRowIndex(row);
1992
2110
  this.dispatchEvent(
@@ -2100,6 +2218,8 @@ var YatlTable = class extends LitElement {
2100
2218
  return this.virtualizer[virtualizerRef];
2101
2219
  }
2102
2220
  }
2221
+ // #endregion
2222
+ // #region --- Properties ---
2103
2223
  get controller() {
2104
2224
  return this._controller;
2105
2225
  }
@@ -2115,36 +2235,12 @@ var YatlTable = class extends LitElement {
2115
2235
  oldController.detach(this);
2116
2236
  controller.attach(this);
2117
2237
  this._controller = controller;
2118
- this.requestUpdate("controller", oldController);
2119
- }
2120
- get enableSearchTokenization() {
2121
- return this.controller.enableSearchTokenization;
2122
- }
2123
- set enableSearchTokenization(enable) {
2124
- const oldValue = this.enableSearchTokenization;
2125
- if (oldValue === enable) {
2126
- return;
2127
- }
2128
- this.controller.enableSearchTokenization = enable;
2129
- this.requestUpdate("enableSearchTokenization", oldValue);
2130
- }
2131
- get enableSearchScoring() {
2132
- return this.controller.enableSearchScoring;
2133
- }
2134
- set enableSearchScoring(enable) {
2135
- const oldValue = this.enableSearchScoring;
2136
- if (oldValue === enable) {
2137
- return;
2138
- }
2139
- this.controller.enableSearchScoring = enable;
2140
- this.requestUpdate("enableSearchScoring", oldValue);
2141
2238
  }
2142
2239
  get columns() {
2143
2240
  return this.controller.columns;
2144
2241
  }
2145
2242
  set columns(columns) {
2146
- const oldValue = this.columns;
2147
- if (oldValue === columns) {
2243
+ if (this.columns === columns) {
2148
2244
  return;
2149
2245
  }
2150
2246
  for (const column of columns) {
@@ -2153,7 +2249,6 @@ var YatlTable = class extends LitElement {
2153
2249
  }
2154
2250
  }
2155
2251
  this.controller.columns = columns;
2156
- this.requestUpdate("columns", oldValue);
2157
2252
  }
2158
2253
  /**
2159
2254
  * Gets a list of columns with the display role
@@ -2166,7 +2261,6 @@ var YatlTable = class extends LitElement {
2166
2261
  return this.controller.columnStates;
2167
2262
  }
2168
2263
  set columnStates(states) {
2169
- const oldValue = this.columnStates;
2170
2264
  let changed = false;
2171
2265
  for (const state2 of states) {
2172
2266
  const oldState = this.getColumnState(state2.field);
@@ -2180,51 +2274,76 @@ var YatlTable = class extends LitElement {
2180
2274
  return;
2181
2275
  }
2182
2276
  this.controller.columnStates = states;
2183
- this.requestUpdate("columnStates", oldValue);
2184
2277
  }
2278
+ get data() {
2279
+ return this.controller.data;
2280
+ }
2281
+ set data(value) {
2282
+ this.controller.data = value;
2283
+ }
2284
+ get filteredData() {
2285
+ return this.controller.filteredData;
2286
+ }
2287
+ get dataUpdateTimestamp() {
2288
+ return this.controller.dataUpdateTimestamp;
2289
+ }
2290
+ get filters() {
2291
+ return this.controller.filters;
2292
+ }
2293
+ set filters(filters) {
2294
+ if (this.filters === filters) {
2295
+ return;
2296
+ }
2297
+ this.controller.filters = filters;
2298
+ }
2299
+ /** @attr search-query */
2185
2300
  get searchQuery() {
2186
2301
  return this.controller.searchQuery;
2187
2302
  }
2188
2303
  set searchQuery(query2) {
2189
- const oldValue = this.searchQuery;
2190
- if (oldValue === query2) {
2304
+ if (this.searchQuery === query2) {
2191
2305
  return;
2192
2306
  }
2193
2307
  this.controller.searchQuery = query2;
2194
- this.requestUpdate("searchQuery", oldValue);
2195
2308
  }
2196
- get searchTokenizer() {
2197
- return this.controller.searchTokenizer;
2309
+ /** @attr tokenized-search */
2310
+ get tokenizedSearch() {
2311
+ return this.controller.tokenizedSearch;
2198
2312
  }
2199
- set searchTokenizer(tokenizer) {
2200
- const oldValue = this.searchTokenizer;
2201
- if (oldValue === tokenizer) {
2313
+ set tokenizedSearch(enable) {
2314
+ if (this.tokenizedSearch === enable) {
2202
2315
  return;
2203
2316
  }
2204
- this.controller.searchTokenizer = tokenizer;
2205
- this.requestUpdate("searchTokenizer", oldValue);
2317
+ this.controller.tokenizedSearch = enable;
2206
2318
  }
2207
- get filters() {
2208
- return this.controller.filters;
2319
+ /** @attr scored-search */
2320
+ get scoredSearch() {
2321
+ return this.controller.scoredSearch;
2209
2322
  }
2210
- set filters(filters) {
2211
- const oldValue = this.filters;
2212
- if (oldValue === filters) {
2323
+ set scoredSearch(enable) {
2324
+ if (this.scoredSearch === enable) {
2213
2325
  return;
2214
2326
  }
2215
- this.controller.filters = filters;
2216
- this.requestUpdate("filters", oldValue);
2327
+ this.controller.scoredSearch = enable;
2328
+ }
2329
+ get searchTokenizer() {
2330
+ return this.controller.searchTokenizer;
2217
2331
  }
2332
+ set searchTokenizer(tokenizer) {
2333
+ if (this.searchTokenizer === tokenizer) {
2334
+ return;
2335
+ }
2336
+ this.controller.searchTokenizer = tokenizer;
2337
+ }
2338
+ /** @attr row-selection-method */
2218
2339
  get rowSelectionMethod() {
2219
2340
  return this.controller.rowSelectionMethod;
2220
2341
  }
2221
2342
  set rowSelectionMethod(selection) {
2222
- const oldValue = this.rowSelectionMethod;
2223
- if (oldValue === selection) {
2343
+ if (this.rowSelectionMethod === selection) {
2224
2344
  return;
2225
2345
  }
2226
2346
  this.controller.rowSelectionMethod = selection;
2227
- this.requestUpdate("rowSelectionMethod", oldValue);
2228
2347
  }
2229
2348
  get selectedRowIds() {
2230
2349
  return this.controller.selectedRowIds;
@@ -2235,40 +2354,21 @@ var YatlTable = class extends LitElement {
2235
2354
  return;
2236
2355
  }
2237
2356
  this.controller.selectedRowIds = rows;
2238
- this.requestUpdate("selectedRows", [...oldValue]);
2239
2357
  }
2240
2358
  get storageOptions() {
2241
2359
  return this.controller.storageOptions;
2242
2360
  }
2243
2361
  set storageOptions(options) {
2244
- const oldValue = this.storageOptions;
2245
2362
  this.controller.storageOptions = options;
2246
- this.requestUpdate("storageOptions", oldValue);
2247
2363
  }
2248
2364
  get rowIdCallback() {
2249
2365
  return this.controller.rowIdCallback;
2250
2366
  }
2251
2367
  set rowIdCallback(callback) {
2252
- const oldValue = this.rowIdCallback;
2253
- if (oldValue === callback) {
2368
+ if (this.rowIdCallback === callback) {
2254
2369
  return;
2255
2370
  }
2256
2371
  this.controller.rowIdCallback = callback;
2257
- this.requestUpdate("rowIdCallback", oldValue);
2258
- }
2259
- get data() {
2260
- return this.controller.data;
2261
- }
2262
- set data(value) {
2263
- const oldValue = this.data;
2264
- this.controller.data = value;
2265
- this.requestUpdate("data", oldValue);
2266
- }
2267
- get filteredData() {
2268
- return this.controller.filteredData;
2269
- }
2270
- get dataUpdateTimestamp() {
2271
- return this.controller.dataUpdateTimestamp;
2272
2372
  }
2273
2373
  // #endregion
2274
2374
  // #region --- Public Methods ---
@@ -2396,6 +2496,38 @@ var YatlTable = class extends LitElement {
2396
2496
  a.click();
2397
2497
  a.remove();
2398
2498
  }
2499
+ async print(title) {
2500
+ if (title) {
2501
+ document.title = title;
2502
+ }
2503
+ const printSheet = new CSSStyleSheet();
2504
+ printSheet.replaceSync(`
2505
+ @media print {
2506
+ body > *:not(.yatl-printable) {
2507
+ display: none !important;
2508
+ }
2509
+
2510
+ body {
2511
+ height: auto !important;
2512
+ overflow: visible !important;
2513
+ background: white !important;
2514
+ }
2515
+ }
2516
+ `);
2517
+ const documentStyleSheets = document.adoptedStyleSheets;
2518
+ document.adoptedStyleSheets = [...documentStyleSheets, printSheet];
2519
+ const printTable = document.createElement("yatl-table");
2520
+ printTable.classList.add("yatl-printable");
2521
+ printTable.controller = this.controller;
2522
+ printTable.virtualScroll = false;
2523
+ document.body.append(printTable);
2524
+ await printTable.updateComplete;
2525
+ requestAnimationFrame(() => {
2526
+ window.print();
2527
+ printTable.remove();
2528
+ document.adoptedStyleSheets = documentStyleSheets;
2529
+ });
2530
+ }
2399
2531
  scrollToRow(row) {
2400
2532
  const index = this.data.findIndex((v) => v === row);
2401
2533
  if (typeof index === "number") {
@@ -2537,7 +2669,7 @@ var YatlTable = class extends LitElement {
2537
2669
  // #endregion
2538
2670
  // #region Render Methods
2539
2671
  renderColumnSortIcon(column, state2) {
2540
- return column.sortable ?? this.sortable ? html2`<div
2672
+ return column.sortable ?? this.sortable ? html4`<div
2541
2673
  part="header-sort-icon"
2542
2674
  class=${classMap({
2543
2675
  "sort-icon": true,
@@ -2547,7 +2679,7 @@ var YatlTable = class extends LitElement {
2547
2679
  ></div>` : nothing;
2548
2680
  }
2549
2681
  renderColumnResizer(column, _state) {
2550
- return column.resizable ?? this.resizable ? html2`
2682
+ return column.resizable ?? this.resizable ? html4`
2551
2683
  <div
2552
2684
  part="header-resizer"
2553
2685
  class="resizer"
@@ -2571,15 +2703,16 @@ var YatlTable = class extends LitElement {
2571
2703
  cell: true,
2572
2704
  sortable: column.sortable ?? this.sortable
2573
2705
  };
2574
- return this.renderCellWrapper(html2`
2706
+ return this.renderCellWrapper(html4`
2575
2707
  <div
2576
- role=${ifDefined(role)}
2577
- aria-hidden=${ifDefined(hidden)}
2708
+ role=${ifDefined2(role)}
2709
+ aria-hidden=${ifDefined2(hidden)}
2578
2710
  aria-sort=${ariaSort}
2579
2711
  aria-label=${title}
2580
2712
  part="cell header-cell"
2581
2713
  class=${classMap(classes)}
2582
- draggable=${ifDefined(this.enableColumnReorder ? true : void 0)}
2714
+ title=${title}
2715
+ draggable=${ifDefined2(this.reorderable ? true : void 0)}
2583
2716
  data-field=${column.field}
2584
2717
  @dragstart=${(event) => this.handleDragColumnStart(event, column.field)}
2585
2718
  @dragenter=${this.handleDragColumnEnter}
@@ -2602,20 +2735,20 @@ var YatlTable = class extends LitElement {
2602
2735
  }
2603
2736
  renderRowNumberHeader() {
2604
2737
  return this.renderCellWrapper(
2605
- html2`<div part="cell-index" class="cell-index"></div>`
2738
+ html4`<div part="cell-index" class="cell-index"></div>`
2606
2739
  );
2607
2740
  }
2608
2741
  renderSelectionHeader() {
2609
2742
  return this.renderCellWrapper(
2610
- html2`<div part="cell-selector" class="cell-selector"></div>`
2743
+ html4`<div part="cell-selector" class="cell-selector"></div>`
2611
2744
  );
2612
2745
  }
2613
2746
  renderHeader() {
2614
2747
  const classes = {
2615
2748
  header: true,
2616
- reorderable: this.enableColumnReorder
2749
+ reorderable: this.reorderable
2617
2750
  };
2618
- return html2`
2751
+ return html4`
2619
2752
  <div role="rowgroup" part="header" class=${classMap(classes)}>
2620
2753
  <div role="row" class="row header-row" part="row header-row">
2621
2754
  ${this.renderRowNumberHeader()} ${this.renderSelectionHeader()}
@@ -2632,7 +2765,7 @@ var YatlTable = class extends LitElement {
2632
2765
  return this.nullValuePlaceholder;
2633
2766
  }
2634
2767
  const indices = this.controller.getRowHighlightIndicies(row);
2635
- return this.enableSearchHighlight && indices ? highlightText(String(value), indices[column.field]) : value;
2768
+ return indices ? highlightText(String(value), indices[column.field]) : value;
2636
2769
  }
2637
2770
  renderCell(column, row) {
2638
2771
  let value = getNestedValue(row, column.field);
@@ -2640,22 +2773,37 @@ var YatlTable = class extends LitElement {
2640
2773
  if (Array.isArray(userParts)) {
2641
2774
  userParts = userParts.join(" ");
2642
2775
  }
2643
- const isNumber = typeof value === "bigint" || typeof value === "number";
2776
+ const inputType = this.getInputTypeFromValue(value);
2644
2777
  const classes = {
2645
2778
  cell: true,
2646
- "is-number": isNumber
2779
+ "is-number": inputType === "number"
2647
2780
  };
2781
+ const rowId = this.controller.getRowId(row);
2782
+ const field = column.field;
2783
+ if (column.editor && !this.disableEditing && this.editingState && this.editingState.id === rowId && this.editingState.field === field) {
2784
+ return this.renderCellWrapper(html4`
2785
+ <div
2786
+ role="cell"
2787
+ part="cell body-cell cell-${column.field}"
2788
+ class=${classMap({ ...classes, "is-editing": true })}
2789
+ data-field=${column.field}
2790
+ >
2791
+ ${column.editor.render(value, field, row, this.controller)}
2792
+ </div>
2793
+ `);
2794
+ }
2648
2795
  if (typeof column.valueFormatter === "function") {
2649
2796
  value = column.valueFormatter(value, row);
2650
2797
  }
2651
- return this.renderCellWrapper(html2`
2798
+ return this.renderCellWrapper(html4`
2652
2799
  <div
2653
2800
  role="cell"
2654
2801
  part="cell body-cell cell-${column.field} ${userParts}"
2655
2802
  data-field=${column.field}
2656
2803
  class=${classMap(classes)}
2657
- title=${ifDefined(value ? String(value) : void 0)}
2658
- @click=${(event) => this.handleCellClick(event, row, column.field)}
2804
+ title=${ifDefined2(value ? String(value) : void 0)}
2805
+ @click=${(event) => this.handleCellClick(event, row, field)}
2806
+ @dblclick=${() => this.handleCellDoubleClick(row, field)}
2659
2807
  >
2660
2808
  <span class="truncate">
2661
2809
  ${this.renderCellContents(value, column, row)}
@@ -2664,12 +2812,12 @@ var YatlTable = class extends LitElement {
2664
2812
  `);
2665
2813
  }
2666
2814
  renderCheckbox(row, selected) {
2667
- return this.useYatlUi ? html2`<yatl-checkbox
2815
+ return this.useYatlUi ? html4`<yatl-checkbox
2668
2816
  part="row-checkbox"
2669
2817
  class="row-checkbox"
2670
2818
  .checked=${selected}
2671
2819
  @change=${(event) => this.handleRowSelectionClicked(event, row)}
2672
- ></yatl-checkbox>` : html2`<input
2820
+ ></yatl-checkbox>` : html4`<input
2673
2821
  part="row-checkbox"
2674
2822
  class="row-checkbox"
2675
2823
  type="checkbox"
@@ -2678,7 +2826,7 @@ var YatlTable = class extends LitElement {
2678
2826
  />`;
2679
2827
  }
2680
2828
  renderRowSelectorCell(row, selected) {
2681
- return this.renderCellWrapper(html2`
2829
+ return this.renderCellWrapper(html4`
2682
2830
  <div part="cell body-cell" class="cell body-cell">
2683
2831
  <div part="row-selector-cell" class="row-selector-cell">
2684
2832
  <label> ${this.renderCheckbox(row, selected)} </label>
@@ -2687,7 +2835,7 @@ var YatlTable = class extends LitElement {
2687
2835
  `);
2688
2836
  }
2689
2837
  renderRowNumberCell(rowNumber) {
2690
- return this.renderCellWrapper(html2`
2838
+ return this.renderCellWrapper(html4`
2691
2839
  <div part="cell body-cell" class="cell body-cell">
2692
2840
  <div part="row-number-cell" class="row-number-cell">${rowNumber}</div>
2693
2841
  </div>
@@ -2706,7 +2854,7 @@ var YatlTable = class extends LitElement {
2706
2854
  "row-odd": renderIndex % 2 !== 0
2707
2855
  };
2708
2856
  const rowIndex = renderIndex + 1;
2709
- return html2`
2857
+ return html4`
2710
2858
  <div
2711
2859
  role="row"
2712
2860
  aria-rowindex=${rowIndex}
@@ -2722,22 +2870,22 @@ var YatlTable = class extends LitElement {
2722
2870
  }
2723
2871
  renderBodyContents() {
2724
2872
  if (!this.hasVisibleColumn()) {
2725
- return html2`
2873
+ return html4`
2726
2874
  <div part="message" class="message">No visible columns.</div>
2727
2875
  `;
2728
2876
  }
2729
2877
  if (this.data.length === 0) {
2730
- return html2`
2878
+ return html4`
2731
2879
  <div part="message" class="message">${this.emptyMessage}</div>
2732
2880
  `;
2733
2881
  }
2734
2882
  if (this.filteredData.length === 0) {
2735
- return html2`
2883
+ return html4`
2736
2884
  <div part="message" class="message">${this.noResultsMessage}</div>
2737
2885
  `;
2738
2886
  }
2739
- if (this.enableVirtualScroll) {
2740
- return html2`
2887
+ if (this.virtualScroll) {
2888
+ return html4`
2741
2889
  <lit-virtualizer
2742
2890
  .items=${this.filteredData}
2743
2891
  .renderItem=${(item, index) => this.renderRow(item, index)}
@@ -2745,8 +2893,8 @@ var YatlTable = class extends LitElement {
2745
2893
  </lit-virtualizer>
2746
2894
  `;
2747
2895
  }
2748
- return html2`
2749
- ${repeat(
2896
+ return html4`
2897
+ ${repeat2(
2750
2898
  this.filteredData,
2751
2899
  (item) => this.controller.getRowId(item),
2752
2900
  (item, index) => this.renderRow(item, index)
@@ -2754,7 +2902,7 @@ var YatlTable = class extends LitElement {
2754
2902
  `;
2755
2903
  }
2756
2904
  renderFooter() {
2757
- if (!this.enableFooter) {
2905
+ if (this.hideFooter) {
2758
2906
  return nothing;
2759
2907
  }
2760
2908
  const total = this.data.length;
@@ -2768,7 +2916,7 @@ var YatlTable = class extends LitElement {
2768
2916
  timeStyle: "short"
2769
2917
  });
2770
2918
  const lastUpdateText = this.dataUpdateTimestamp ? formatter.format(this.dataUpdateTimestamp) : "Never";
2771
- return html2`
2919
+ return html4`
2772
2920
  <div part="footer" class="footer">
2773
2921
  <slot name="footer">
2774
2922
  <span part="row-count">${rowCountText}</span>
@@ -2782,7 +2930,7 @@ var YatlTable = class extends LitElement {
2782
2930
  const style = {
2783
2931
  "--grid-template": gridTemplate
2784
2932
  };
2785
- return html2`
2933
+ return html4`
2786
2934
  <div
2787
2935
  role="table"
2788
2936
  aria-label="Data Table"
@@ -2802,13 +2950,15 @@ var YatlTable = class extends LitElement {
2802
2950
  `;
2803
2951
  }
2804
2952
  renderCellWrapper(content) {
2805
- return html2` <div class="cell-wrapper">${content}</div> `;
2953
+ return html4` <div class="cell-wrapper">${content}</div> `;
2806
2954
  }
2807
2955
  // #endregion
2808
2956
  // #region --- Lifecycle Methods ---
2809
2957
  connectedCallback() {
2810
2958
  super.connectedCallback();
2811
2959
  this.addControllerListeners(this.controller);
2960
+ this.addEventListener("mousedown", this.handleMouseDown);
2961
+ this.addEventListener("keydown", this.handleCellInputKeypress);
2812
2962
  if (!this.useYatlUi) {
2813
2963
  customElements.whenDefined("yatl-checkbox").then(() => {
2814
2964
  this.useYatlUi = true;
@@ -2818,8 +2968,21 @@ var YatlTable = class extends LitElement {
2818
2968
  disconnectedCallback() {
2819
2969
  super.disconnectedCallback();
2820
2970
  this.removeControllerListeners(this.controller);
2821
- window.addEventListener("mousemove", this.handleResizeMouseMove);
2822
- window.addEventListener("mouseup", this.handleResizeMouseUp);
2971
+ this.removeEventListener("mousedown", this.handleMouseDown);
2972
+ this.removeEventListener("keydown", this.handleCellInputKeypress);
2973
+ window.removeEventListener("mousemove", this.handleResizeMouseMove);
2974
+ window.removeEventListener("mouseup", this.handleResizeMouseUp);
2975
+ }
2976
+ updated(changedProperties) {
2977
+ super.updated(changedProperties);
2978
+ if (this.editor && document.activeElement !== this.editor) {
2979
+ setTimeout(() => {
2980
+ this.editor?.focus();
2981
+ if (this.editor instanceof HTMLInputElement) {
2982
+ this.editor.select();
2983
+ }
2984
+ });
2985
+ }
2823
2986
  }
2824
2987
  addControllerListeners(controller) {
2825
2988
  for (const name of this.eventNames) {
@@ -2840,7 +3003,7 @@ var YatlTable = class extends LitElement {
2840
3003
  */
2841
3004
  getGridWidths() {
2842
3005
  const widths = [];
2843
- if (this.enableRowNumberColumn) {
3006
+ if (this.rowNumbers) {
2844
3007
  widths.push("var(--yatl-row-number-column-width, 48px)");
2845
3008
  } else {
2846
3009
  widths.push("0");
@@ -2857,7 +3020,7 @@ var YatlTable = class extends LitElement {
2857
3020
  if (hasPixelWidth) {
2858
3021
  widths.push(`${state2.width}px`);
2859
3022
  } else {
2860
- widths.push("minmax(0, 1fr)");
3023
+ widths.push("minmax(50px, 1fr)");
2861
3024
  }
2862
3025
  } else {
2863
3026
  if (hasPixelWidth) {
@@ -2869,6 +3032,121 @@ var YatlTable = class extends LitElement {
2869
3032
  }
2870
3033
  return widths;
2871
3034
  }
3035
+ getInputTypeFromValue(value) {
3036
+ const valueType = typeof value;
3037
+ if (valueType === "bigint" || valueType === "number") {
3038
+ return "number";
3039
+ }
3040
+ if (valueType === "boolean") {
3041
+ return "boolean";
3042
+ }
3043
+ if (value instanceof Date) {
3044
+ return "date";
3045
+ }
3046
+ return "text";
3047
+ }
3048
+ getNextEditableField(row, currentField) {
3049
+ let index = 0;
3050
+ if (currentField) {
3051
+ index = this.displayColumns.findIndex((c) => c.field === currentField);
3052
+ if (index < 0) {
3053
+ index = 0;
3054
+ } else {
3055
+ index++;
3056
+ }
3057
+ }
3058
+ for (const col of this.displayColumns.slice(index)) {
3059
+ if (col.editor && col.editor.canEdit(col.field, row)) {
3060
+ return col.field;
3061
+ }
3062
+ }
3063
+ }
3064
+ async saveEdits() {
3065
+ if (!this.editingState) {
3066
+ return;
3067
+ }
3068
+ const { id: rowId, field, originalValue } = this.editingState;
3069
+ const column = this.getDisplayColumn(field);
3070
+ const row = this.getRow(rowId);
3071
+ if (!column?.editor || !row) {
3072
+ return;
3073
+ }
3074
+ let value;
3075
+ const result = column.editor.save(
3076
+ originalValue,
3077
+ field,
3078
+ row,
3079
+ this.controller
3080
+ );
3081
+ if (result instanceof Promise) {
3082
+ value = await result;
3083
+ } else {
3084
+ value = result;
3085
+ }
3086
+ if (value !== void 0) {
3087
+ const updateData = {};
3088
+ setNestedValue(updateData, field, value);
3089
+ this.updateRow(rowId, updateData);
3090
+ this.dispatchEvent(
3091
+ new YatlCellEditEvent(row, rowId, field, originalValue, value)
3092
+ );
3093
+ }
3094
+ }
3095
+ handleCellDoubleClick(row, field) {
3096
+ const column = this.getDisplayColumn(field);
3097
+ if (!column || !column.editor || !column.editor.canEdit(field, row)) {
3098
+ return;
3099
+ }
3100
+ column.editor.reset();
3101
+ const value = getNestedValue(row, field);
3102
+ this.editingState = {
3103
+ id: this.controller.getRowId(row),
3104
+ field,
3105
+ originalValue: value
3106
+ };
3107
+ }
3108
+ handleCellInputKeypress(event) {
3109
+ if (!this.editingState) {
3110
+ return;
3111
+ }
3112
+ if (event.key === "Escape") {
3113
+ this.editingState = null;
3114
+ } else if (event.key === "Enter") {
3115
+ this.saveEdits();
3116
+ this.editingState = null;
3117
+ } else if (event.key === "Tab") {
3118
+ this.saveEdits();
3119
+ const { id: rowId } = this.editingState;
3120
+ const row = this.getRow(rowId);
3121
+ let nextColumn = this.getNextEditableField(row, this.editingState.field);
3122
+ if (nextColumn) {
3123
+ return this.handleCellDoubleClick(row, nextColumn);
3124
+ }
3125
+ let rowIndex = this.filteredData.indexOf(row);
3126
+ if (rowIndex < 0) {
3127
+ return;
3128
+ }
3129
+ rowIndex++;
3130
+ if (rowIndex >= this.filteredData.length) {
3131
+ rowIndex = 0;
3132
+ }
3133
+ const nextRow = this.filteredData[rowIndex];
3134
+ nextColumn = this.getNextEditableField(nextRow);
3135
+ if (!nextColumn) {
3136
+ return;
3137
+ }
3138
+ this.handleCellDoubleClick(nextRow, nextColumn);
3139
+ event.preventDefault();
3140
+ }
3141
+ }
3142
+ handleMouseDown(event) {
3143
+ const containsEditing = event.composedPath().filter((e) => e instanceof HTMLElement).some((e) => e.classList.contains("is-editing"));
3144
+ if (this.editingState && !containsEditing) {
3145
+ console.log("saving");
3146
+ this.saveEdits();
3147
+ this.editingState = null;
3148
+ }
3149
+ }
2872
3150
  handleResizeMouseDown(event, field) {
2873
3151
  event.preventDefault();
2874
3152
  event.stopPropagation();
@@ -2918,89 +3196,100 @@ __decorateClass([
2918
3196
  __decorateClass([
2919
3197
  query("lit-virtualizer")
2920
3198
  ], YatlTable.prototype, "virtualizer", 2);
3199
+ __decorateClass([
3200
+ query(".cell.is-editing > *")
3201
+ ], YatlTable.prototype, "editor", 2);
2921
3202
  __decorateClass([
2922
3203
  state()
2923
3204
  ], YatlTable.prototype, "useYatlUi", 2);
3205
+ __decorateClass([
3206
+ state()
3207
+ ], YatlTable.prototype, "editingState", 2);
2924
3208
  __decorateClass([
2925
3209
  property({ attribute: false })
2926
3210
  ], YatlTable.prototype, "controller", 1);
2927
- __decorateClass([
2928
- property({ type: Boolean, reflect: true })
2929
- ], YatlTable.prototype, "striped", 2);
2930
- __decorateClass([
2931
- property({ type: Boolean, attribute: "sortable" })
2932
- ], YatlTable.prototype, "sortable", 2);
2933
- __decorateClass([
2934
- property({ type: Boolean, attribute: "resizable" })
2935
- ], YatlTable.prototype, "resizable", 2);
2936
- __decorateClass([
2937
- property({ type: Boolean, attribute: "enable-virtual-scroll" })
2938
- ], YatlTable.prototype, "enableVirtualScroll", 2);
2939
- __decorateClass([
2940
- property({ type: Boolean, attribute: "enable-search-highlight" })
2941
- ], YatlTable.prototype, "enableSearchHighlight", 2);
2942
- __decorateClass([
2943
- property({ type: Boolean, attribute: "enable-search-tokenization" })
2944
- ], YatlTable.prototype, "enableSearchTokenization", 1);
2945
- __decorateClass([
2946
- property({ type: Boolean, attribute: "enable-search-scoring" })
2947
- ], YatlTable.prototype, "enableSearchScoring", 1);
2948
- __decorateClass([
2949
- property({ type: Boolean, attribute: "enable-column-reorder" })
2950
- ], YatlTable.prototype, "enableColumnReorder", 2);
2951
- __decorateClass([
2952
- property({ type: Boolean, attribute: "enable-row-number-column" })
2953
- ], YatlTable.prototype, "enableRowNumberColumn", 2);
2954
- __decorateClass([
2955
- property({ type: Boolean, attribute: "enable-footer" })
2956
- ], YatlTable.prototype, "enableFooter", 2);
2957
- __decorateClass([
2958
- property({ type: String, attribute: "null-value-placeholder" })
2959
- ], YatlTable.prototype, "nullValuePlaceholder", 2);
2960
- __decorateClass([
2961
- property({ type: String, attribute: "empty-message" })
2962
- ], YatlTable.prototype, "emptyMessage", 2);
2963
- __decorateClass([
2964
- property({ type: String, attribute: "no-results-message" })
2965
- ], YatlTable.prototype, "noResultsMessage", 2);
2966
3211
  __decorateClass([
2967
3212
  property({ attribute: false })
2968
3213
  ], YatlTable.prototype, "columns", 1);
2969
3214
  __decorateClass([
2970
3215
  property({ attribute: false })
2971
3216
  ], YatlTable.prototype, "columnStates", 1);
2972
- __decorateClass([
2973
- property({ type: String, attribute: "search-query" })
2974
- ], YatlTable.prototype, "searchQuery", 1);
2975
3217
  __decorateClass([
2976
3218
  property({ attribute: false })
2977
- ], YatlTable.prototype, "searchTokenizer", 1);
3219
+ ], YatlTable.prototype, "data", 1);
2978
3220
  __decorateClass([
2979
3221
  property({ attribute: false })
2980
3222
  ], YatlTable.prototype, "filters", 1);
3223
+ __decorateClass([
3224
+ property({ type: String, attribute: "search-query" })
3225
+ ], YatlTable.prototype, "searchQuery", 1);
3226
+ __decorateClass([
3227
+ property({ type: Boolean, attribute: "tokenized-search" })
3228
+ ], YatlTable.prototype, "tokenizedSearch", 1);
3229
+ __decorateClass([
3230
+ property({ type: Boolean, attribute: "scored-search" })
3231
+ ], YatlTable.prototype, "scoredSearch", 1);
2981
3232
  __decorateClass([
2982
3233
  property({ attribute: false })
2983
- ], YatlTable.prototype, "rowParts", 2);
3234
+ ], YatlTable.prototype, "searchTokenizer", 1);
2984
3235
  __decorateClass([
2985
- property({ type: String })
3236
+ property({ type: String, attribute: "row-selection-method" })
2986
3237
  ], YatlTable.prototype, "rowSelectionMethod", 1);
2987
3238
  __decorateClass([
2988
3239
  property({ attribute: false })
2989
3240
  ], YatlTable.prototype, "selectedRowIds", 1);
2990
3241
  __decorateClass([
2991
- property({ type: Object, attribute: "storage-options" })
3242
+ property({ attribute: false })
2992
3243
  ], YatlTable.prototype, "storageOptions", 1);
2993
3244
  __decorateClass([
2994
3245
  property({ attribute: false })
2995
3246
  ], YatlTable.prototype, "rowIdCallback", 1);
3247
+ __decorateClass([
3248
+ property({ type: Boolean, reflect: true })
3249
+ ], YatlTable.prototype, "striped", 2);
3250
+ __decorateClass([
3251
+ property({ type: Boolean })
3252
+ ], YatlTable.prototype, "sortable", 2);
3253
+ __decorateClass([
3254
+ property({ type: Boolean })
3255
+ ], YatlTable.prototype, "resizable", 2);
3256
+ __decorateClass([
3257
+ property({ type: Boolean })
3258
+ ], YatlTable.prototype, "reorderable", 2);
3259
+ __decorateClass([
3260
+ property({ type: Boolean, attribute: "row-numbers" })
3261
+ ], YatlTable.prototype, "rowNumbers", 2);
3262
+ __decorateClass([
3263
+ property({ type: Boolean, attribute: "virtual-scroll" })
3264
+ ], YatlTable.prototype, "virtualScroll", 2);
3265
+ __decorateClass([
3266
+ property({ type: Boolean, attribute: "hide-footer" })
3267
+ ], YatlTable.prototype, "hideFooter", 2);
3268
+ __decorateClass([
3269
+ property({ type: Boolean, attribute: "disable-editing" })
3270
+ ], YatlTable.prototype, "disableEditing", 2);
3271
+ __decorateClass([
3272
+ property({ type: String, attribute: "null-value-placeholder" })
3273
+ ], YatlTable.prototype, "nullValuePlaceholder", 2);
3274
+ __decorateClass([
3275
+ property({ type: String, attribute: "empty-message" })
3276
+ ], YatlTable.prototype, "emptyMessage", 2);
3277
+ __decorateClass([
3278
+ property({ type: String, attribute: "no-results-message" })
3279
+ ], YatlTable.prototype, "noResultsMessage", 2);
2996
3280
  __decorateClass([
2997
3281
  property({ attribute: false })
2998
- ], YatlTable.prototype, "data", 1);
3282
+ ], YatlTable.prototype, "rowParts", 2);
2999
3283
  YatlTable = __decorateClass([
3000
3284
  customElement("yatl-table")
3001
3285
  ], YatlTable);
3002
3286
  export {
3287
+ InputEditor,
3288
+ NumberEditor,
3289
+ SelectEditor,
3290
+ TextEditor,
3003
3291
  TypedEventTarget,
3292
+ YatlCellEditEvent,
3004
3293
  YatlColumnReorderEvent,
3005
3294
  YatlColumnReorderRequest,
3006
3295
  YatlColumnResizeEvent,
@@ -3013,6 +3302,7 @@ export {
3013
3302
  YatlRowSelectRequest,
3014
3303
  YatlTable,
3015
3304
  YatlTableController,
3305
+ YatlTableControllerEvent,
3016
3306
  YatlTableSearchEvent,
3017
3307
  YatlTableStateChangeEvent,
3018
3308
  YatlTableViewChangeEvent,