@liedekef/ftable 1.2.0 → 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/ftable.esm.js CHANGED
@@ -1413,9 +1413,7 @@ class FTableFormBuilder {
1413
1413
  : [];
1414
1414
 
1415
1415
  // Support data-livesearch attribute on the virtual select as well as field.livesearch
1416
- const livesearch = field.livesearch
1417
- ?? (attributes['data-livesearch'] === 'true' || attributes['data-livesearch'] === true)
1418
- ?? false;
1416
+ const livesearch = field.livesearch ?? false;
1419
1417
 
1420
1418
  return this._buildCustomMultiSelect({
1421
1419
  containerId: fieldName,
@@ -2187,7 +2185,7 @@ class FTable extends FTableEventEmitter {
2187
2185
  initColumnWidths() {
2188
2186
  const visibleFields = this.columnList.filter(fieldName => {
2189
2187
  const field = this.options.fields[fieldName];
2190
- return field.visibility !== 'hidden' && field.visibility !== 'separator';
2188
+ return !field.action && field.visibility !== 'hidden' && field.visibility !== 'separator';
2191
2189
  });
2192
2190
 
2193
2191
  const count = visibleFields.length;
@@ -2206,7 +2204,7 @@ class FTable extends FTableEventEmitter {
2206
2204
  th: this.elements.table.querySelector(`[data-field-name="${fieldName}"]`),
2207
2205
  field: this.options.fields[fieldName]
2208
2206
  }))
2209
- .filter(item => item.th && item.field.visibility !== 'hidden' && item.field.visibility !== 'separator');
2207
+ .filter(item => item.th && !item.field.action && item.field.visibility !== 'hidden' && item.field.visibility !== 'separator');
2210
2208
 
2211
2209
  if (visibleHeaders.length === 0) return;
2212
2210
 
@@ -2341,8 +2339,17 @@ class FTable extends FTableEventEmitter {
2341
2339
  this.fieldList.forEach(fieldName => {
2342
2340
  const field = this.options.fields[fieldName];
2343
2341
  const isKeyField = field.key === true;
2344
-
2345
- if (isKeyField) {
2342
+ const isActionField = !!field.action; // action: 'select' | 'update' | 'clone' | 'delete'
2343
+
2344
+ if (isActionField) {
2345
+ // Action columns are always listed but never part of forms or sorting
2346
+ field.list = true;
2347
+ field.create = false;
2348
+ field.edit = false;
2349
+ field.sorting = false;
2350
+ field.searchable = false;
2351
+ field.visibility = field.visibility ?? 'visible';
2352
+ } else if (isKeyField) {
2346
2353
  if (field.create === undefined || !field.create) {
2347
2354
  field.create = true;
2348
2355
  field.type = 'hidden';
@@ -2367,6 +2374,13 @@ class FTable extends FTableEventEmitter {
2367
2374
  return field.list !== false;
2368
2375
  });
2369
2376
 
2377
+ // Track which actions are user-placed (via action columns in fields)
2378
+ this._userPlacedActions = new Set(
2379
+ this.fieldList
2380
+ .filter(name => this.options.fields[name].action)
2381
+ .map(name => this.options.fields[name].action)
2382
+ );
2383
+
2370
2384
  // Find key field
2371
2385
  this.keyField = this.fieldList.find(name => this.options.fields[name].key === true);
2372
2386
  if (!this.keyField) {
@@ -2377,6 +2391,7 @@ class FTable extends FTableEventEmitter {
2377
2391
  async resolveAsyncFieldOptions() {
2378
2392
  const promises = this.columnList.map(async (fieldName) => {
2379
2393
  const field = this.options.fields[fieldName];
2394
+ if (field.action) return; // Skip action columns
2380
2395
  const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName);
2381
2396
 
2382
2397
  if (this.formBuilder.shouldResolveOptions(originalOptions)) {
@@ -2402,7 +2417,7 @@ class FTable extends FTableEventEmitter {
2402
2417
  for (const row of rows) {
2403
2418
  for (const fieldName of this.columnList) {
2404
2419
  const field = this.options.fields[fieldName];
2405
- if (!field.options) continue;
2420
+ if (field.action || !field.options) continue;
2406
2421
 
2407
2422
  const cell = row.querySelector(`td[data-field-name="${fieldName}"]`);
2408
2423
  if (!cell) continue;
@@ -2473,8 +2488,8 @@ class FTable extends FTableEventEmitter {
2473
2488
  parent: thead
2474
2489
  });
2475
2490
 
2476
- // Add selecting column if enabled
2477
- if (this.options.selecting && this.options.selectingCheckboxes) {
2491
+ // Add selecting column if enabled (only if not user-placed)
2492
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
2478
2493
  const selectHeader = FTableDOMHelper.create('th', {
2479
2494
  className: `ftable-command-column-header ftable-column-header-select`,
2480
2495
  parent: headerRow
@@ -2492,9 +2507,41 @@ class FTable extends FTableEventEmitter {
2492
2507
  }
2493
2508
  }
2494
2509
 
2495
- // Add data columns
2510
+ // Add data columns (including any user-placed action columns)
2496
2511
  this.columnList.forEach(fieldName => {
2497
2512
  const field = this.options.fields[fieldName];
2513
+
2514
+ // If this column is a user-placed action column, render an action header
2515
+ if (field.action) {
2516
+ const actionClassMap = {
2517
+ select: 'ftable-column-header-select',
2518
+ update: 'ftable-column-header-edit',
2519
+ clone: 'ftable-column-header-clone',
2520
+ delete: 'ftable-column-header-delete',
2521
+ };
2522
+ const th = FTableDOMHelper.create('th', {
2523
+ className: `ftable-command-column-header ${actionClassMap[field.action] || ''}`,
2524
+ parent: headerRow
2525
+ });
2526
+ if (field.title) {
2527
+ th.textContent = field.title;
2528
+ }
2529
+ // For select action with multiselect, add the select-all checkbox
2530
+ if (field.action === 'select' && this.options.selecting && this.options.selectingCheckboxes && this.options.multiselect) {
2531
+ const selectAllCheckbox = FTableDOMHelper.create('input', {
2532
+ attributes: { type: 'checkbox' },
2533
+ parent: th
2534
+ });
2535
+ selectAllCheckbox.addEventListener('change', () => {
2536
+ this.toggleSelectAll(selectAllCheckbox.checked);
2537
+ });
2538
+ }
2539
+ if (field.width) {
2540
+ th.style.width = field.width;
2541
+ }
2542
+ return;
2543
+ }
2544
+
2498
2545
  const th = FTableDOMHelper.create('th', {
2499
2546
  className: `ftable-column-header ${field.listClass || ''} ${field.listClassHeader || ''}`,
2500
2547
  attributes: { 'data-field-name': fieldName },
@@ -2543,22 +2590,22 @@ class FTable extends FTableEventEmitter {
2543
2590
  }
2544
2591
  });
2545
2592
 
2546
- // Add action columns
2547
- if (this.options.actions.updateAction) {
2593
+ // Add default action columns only if not user-placed
2594
+ if (this.options.actions.updateAction && !this._userPlacedActions.has('update')) {
2548
2595
  FTableDOMHelper.create('th', {
2549
2596
  className: 'ftable-command-column-header ftable-column-header-edit',
2550
2597
  parent: headerRow
2551
2598
  });
2552
2599
  }
2553
2600
 
2554
- if (this.options.actions.cloneAction) {
2601
+ if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
2555
2602
  FTableDOMHelper.create('th', {
2556
2603
  className: 'ftable-command-column-header ftable-column-header-clone',
2557
2604
  parent: headerRow
2558
2605
  });
2559
2606
  }
2560
2607
 
2561
- if (this.options.actions.deleteAction) {
2608
+ if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
2562
2609
  FTableDOMHelper.create('th', {
2563
2610
  className: 'ftable-command-column-header ftable-column-header-delete',
2564
2611
  parent: headerRow
@@ -2579,17 +2626,27 @@ class FTable extends FTableEventEmitter {
2579
2626
  parent: theadParent
2580
2627
  });
2581
2628
 
2582
- // Add empty cell for selecting column if enabled
2583
- if (this.options.selecting && this.options.selectingCheckboxes) {
2629
+ // Add empty cell for selecting column if enabled (only if not user-placed)
2630
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
2584
2631
  FTableDOMHelper.create('th', {
2585
2632
  className: 'ftable-toolbarsearch-column-header',
2586
2633
  parent: searchRow
2587
2634
  });
2588
2635
  }
2589
2636
 
2590
- // Add search input cells for data columns
2637
+ // Add search input cells for data columns (including user-placed action columns)
2591
2638
  for (const fieldName of this.columnList) {
2592
2639
  const field = this.options.fields[fieldName];
2640
+
2641
+ // Action columns get an empty search cell
2642
+ if (field.action) {
2643
+ FTableDOMHelper.create('th', {
2644
+ className: 'ftable-toolbarsearch-column-header ftable-command-column-header',
2645
+ parent: searchRow
2646
+ });
2647
+ continue;
2648
+ }
2649
+
2593
2650
  const isSearchable = field.searchable !== false;
2594
2651
 
2595
2652
  const th = FTableDOMHelper.create('th', {
@@ -2761,9 +2818,9 @@ class FTable extends FTableEventEmitter {
2761
2818
  parent: searchRow
2762
2819
  });
2763
2820
 
2764
- const actionCount = (this.options.actions.updateAction ? 1 : 0) +
2765
- (this.options.actions.deleteAction ? 1 : 0) +
2766
- (this.options.actions.cloneAction ? 1 : 0);
2821
+ const actionCount = (this.options.actions.updateAction && !this._userPlacedActions.has('update') ? 1 : 0) +
2822
+ (this.options.actions.deleteAction && !this._userPlacedActions.has('delete') ? 1 : 0) +
2823
+ (this.options.actions.cloneAction && !this._userPlacedActions.has('clone') ? 1 : 0);
2767
2824
 
2768
2825
  if (actionCount > 0) {
2769
2826
  resetTh.colSpan = actionCount;
@@ -2871,9 +2928,7 @@ class FTable extends FTableEventEmitter {
2871
2928
  }
2872
2929
 
2873
2930
  createCustomMultiSelectForSearch(fieldSearchName, fieldName, field, optionsSource, attributes) {
2874
- const livesearch = field.livesearch
2875
- ?? (attributes['data-livesearch'] === 'true' || attributes['data-livesearch'] === true)
2876
- ?? false;
2931
+ const livesearch = field.livesearch ?? false;
2877
2932
 
2878
2933
  return this.formBuilder._buildCustomMultiSelect({
2879
2934
  hiddenSelectId: fieldSearchName,
@@ -3450,6 +3505,7 @@ class FTable extends FTableEventEmitter {
3450
3505
 
3451
3506
  this.columnList.forEach(fieldName => {
3452
3507
  const field = this.options.fields[fieldName];
3508
+ if (field.action) return; // Action columns don't appear in column picker
3453
3509
  const isVisible = field.visibility !== 'hidden';
3454
3510
  const isFixed = field.visibility === 'fixed';
3455
3511
  const isSeparator = field.visibility === 'separator';
@@ -3862,26 +3918,45 @@ class FTable extends FTableEventEmitter {
3862
3918
  // Store record data
3863
3919
  row.recordData = record;
3864
3920
 
3865
- // Add selecting checkbox if enabled
3866
- if (this.options.selecting && this.options.selectingCheckboxes) {
3921
+ // Add selecting checkbox if enabled (only if not user-placed)
3922
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
3867
3923
  this.addSelectingCell(row);
3868
3924
  }
3869
3925
 
3870
- // Add data cells
3926
+ // Add data cells (including user-placed action columns)
3871
3927
  this.columnList.forEach(fieldName => {
3872
- this.addDataCell(row, record, fieldName);
3928
+ const field = this.options.fields[fieldName];
3929
+ if (field.action) {
3930
+ // Render inline action cell
3931
+ switch (field.action) {
3932
+ case 'select':
3933
+ this.addSelectingCell(row);
3934
+ break;
3935
+ case 'update':
3936
+ this.addEditCell(row);
3937
+ break;
3938
+ case 'clone':
3939
+ this.addCloneCell(row);
3940
+ break;
3941
+ case 'delete':
3942
+ this.addDeleteCell(row);
3943
+ break;
3944
+ }
3945
+ } else {
3946
+ this.addDataCell(row, record, fieldName);
3947
+ }
3873
3948
  });
3874
3949
 
3875
- // Add action cells
3876
- if (this.options.actions.updateAction) {
3950
+ // Add default action cells only if not user-placed
3951
+ if (this.options.actions.updateAction && !this._userPlacedActions.has('update')) {
3877
3952
  this.addEditCell(row);
3878
3953
  }
3879
3954
 
3880
- if (this.options.actions.cloneAction) {
3955
+ if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
3881
3956
  this.addCloneCell(row);
3882
3957
  }
3883
3958
 
3884
- if (this.options.actions.deleteAction) {
3959
+ if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
3885
3960
  this.addDeleteCell(row);
3886
3961
  }
3887
3962
 
package/ftable.js CHANGED
@@ -1414,9 +1414,7 @@ class FTableFormBuilder {
1414
1414
  : [];
1415
1415
 
1416
1416
  // Support data-livesearch attribute on the virtual select as well as field.livesearch
1417
- const livesearch = field.livesearch
1418
- ?? (attributes['data-livesearch'] === 'true' || attributes['data-livesearch'] === true)
1419
- ?? false;
1417
+ const livesearch = field.livesearch ?? false;
1420
1418
 
1421
1419
  return this._buildCustomMultiSelect({
1422
1420
  containerId: fieldName,
@@ -2188,7 +2186,7 @@ class FTable extends FTableEventEmitter {
2188
2186
  initColumnWidths() {
2189
2187
  const visibleFields = this.columnList.filter(fieldName => {
2190
2188
  const field = this.options.fields[fieldName];
2191
- return field.visibility !== 'hidden' && field.visibility !== 'separator';
2189
+ return !field.action && field.visibility !== 'hidden' && field.visibility !== 'separator';
2192
2190
  });
2193
2191
 
2194
2192
  const count = visibleFields.length;
@@ -2207,7 +2205,7 @@ class FTable extends FTableEventEmitter {
2207
2205
  th: this.elements.table.querySelector(`[data-field-name="${fieldName}"]`),
2208
2206
  field: this.options.fields[fieldName]
2209
2207
  }))
2210
- .filter(item => item.th && item.field.visibility !== 'hidden' && item.field.visibility !== 'separator');
2208
+ .filter(item => item.th && !item.field.action && item.field.visibility !== 'hidden' && item.field.visibility !== 'separator');
2211
2209
 
2212
2210
  if (visibleHeaders.length === 0) return;
2213
2211
 
@@ -2342,8 +2340,17 @@ class FTable extends FTableEventEmitter {
2342
2340
  this.fieldList.forEach(fieldName => {
2343
2341
  const field = this.options.fields[fieldName];
2344
2342
  const isKeyField = field.key === true;
2345
-
2346
- if (isKeyField) {
2343
+ const isActionField = !!field.action; // action: 'select' | 'update' | 'clone' | 'delete'
2344
+
2345
+ if (isActionField) {
2346
+ // Action columns are always listed but never part of forms or sorting
2347
+ field.list = true;
2348
+ field.create = false;
2349
+ field.edit = false;
2350
+ field.sorting = false;
2351
+ field.searchable = false;
2352
+ field.visibility = field.visibility ?? 'visible';
2353
+ } else if (isKeyField) {
2347
2354
  if (field.create === undefined || !field.create) {
2348
2355
  field.create = true;
2349
2356
  field.type = 'hidden';
@@ -2368,6 +2375,13 @@ class FTable extends FTableEventEmitter {
2368
2375
  return field.list !== false;
2369
2376
  });
2370
2377
 
2378
+ // Track which actions are user-placed (via action columns in fields)
2379
+ this._userPlacedActions = new Set(
2380
+ this.fieldList
2381
+ .filter(name => this.options.fields[name].action)
2382
+ .map(name => this.options.fields[name].action)
2383
+ );
2384
+
2371
2385
  // Find key field
2372
2386
  this.keyField = this.fieldList.find(name => this.options.fields[name].key === true);
2373
2387
  if (!this.keyField) {
@@ -2378,6 +2392,7 @@ class FTable extends FTableEventEmitter {
2378
2392
  async resolveAsyncFieldOptions() {
2379
2393
  const promises = this.columnList.map(async (fieldName) => {
2380
2394
  const field = this.options.fields[fieldName];
2395
+ if (field.action) return; // Skip action columns
2381
2396
  const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName);
2382
2397
 
2383
2398
  if (this.formBuilder.shouldResolveOptions(originalOptions)) {
@@ -2403,7 +2418,7 @@ class FTable extends FTableEventEmitter {
2403
2418
  for (const row of rows) {
2404
2419
  for (const fieldName of this.columnList) {
2405
2420
  const field = this.options.fields[fieldName];
2406
- if (!field.options) continue;
2421
+ if (field.action || !field.options) continue;
2407
2422
 
2408
2423
  const cell = row.querySelector(`td[data-field-name="${fieldName}"]`);
2409
2424
  if (!cell) continue;
@@ -2474,8 +2489,8 @@ class FTable extends FTableEventEmitter {
2474
2489
  parent: thead
2475
2490
  });
2476
2491
 
2477
- // Add selecting column if enabled
2478
- if (this.options.selecting && this.options.selectingCheckboxes) {
2492
+ // Add selecting column if enabled (only if not user-placed)
2493
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
2479
2494
  const selectHeader = FTableDOMHelper.create('th', {
2480
2495
  className: `ftable-command-column-header ftable-column-header-select`,
2481
2496
  parent: headerRow
@@ -2493,9 +2508,41 @@ class FTable extends FTableEventEmitter {
2493
2508
  }
2494
2509
  }
2495
2510
 
2496
- // Add data columns
2511
+ // Add data columns (including any user-placed action columns)
2497
2512
  this.columnList.forEach(fieldName => {
2498
2513
  const field = this.options.fields[fieldName];
2514
+
2515
+ // If this column is a user-placed action column, render an action header
2516
+ if (field.action) {
2517
+ const actionClassMap = {
2518
+ select: 'ftable-column-header-select',
2519
+ update: 'ftable-column-header-edit',
2520
+ clone: 'ftable-column-header-clone',
2521
+ delete: 'ftable-column-header-delete',
2522
+ };
2523
+ const th = FTableDOMHelper.create('th', {
2524
+ className: `ftable-command-column-header ${actionClassMap[field.action] || ''}`,
2525
+ parent: headerRow
2526
+ });
2527
+ if (field.title) {
2528
+ th.textContent = field.title;
2529
+ }
2530
+ // For select action with multiselect, add the select-all checkbox
2531
+ if (field.action === 'select' && this.options.selecting && this.options.selectingCheckboxes && this.options.multiselect) {
2532
+ const selectAllCheckbox = FTableDOMHelper.create('input', {
2533
+ attributes: { type: 'checkbox' },
2534
+ parent: th
2535
+ });
2536
+ selectAllCheckbox.addEventListener('change', () => {
2537
+ this.toggleSelectAll(selectAllCheckbox.checked);
2538
+ });
2539
+ }
2540
+ if (field.width) {
2541
+ th.style.width = field.width;
2542
+ }
2543
+ return;
2544
+ }
2545
+
2499
2546
  const th = FTableDOMHelper.create('th', {
2500
2547
  className: `ftable-column-header ${field.listClass || ''} ${field.listClassHeader || ''}`,
2501
2548
  attributes: { 'data-field-name': fieldName },
@@ -2544,22 +2591,22 @@ class FTable extends FTableEventEmitter {
2544
2591
  }
2545
2592
  });
2546
2593
 
2547
- // Add action columns
2548
- if (this.options.actions.updateAction) {
2594
+ // Add default action columns only if not user-placed
2595
+ if (this.options.actions.updateAction && !this._userPlacedActions.has('update')) {
2549
2596
  FTableDOMHelper.create('th', {
2550
2597
  className: 'ftable-command-column-header ftable-column-header-edit',
2551
2598
  parent: headerRow
2552
2599
  });
2553
2600
  }
2554
2601
 
2555
- if (this.options.actions.cloneAction) {
2602
+ if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
2556
2603
  FTableDOMHelper.create('th', {
2557
2604
  className: 'ftable-command-column-header ftable-column-header-clone',
2558
2605
  parent: headerRow
2559
2606
  });
2560
2607
  }
2561
2608
 
2562
- if (this.options.actions.deleteAction) {
2609
+ if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
2563
2610
  FTableDOMHelper.create('th', {
2564
2611
  className: 'ftable-command-column-header ftable-column-header-delete',
2565
2612
  parent: headerRow
@@ -2580,17 +2627,27 @@ class FTable extends FTableEventEmitter {
2580
2627
  parent: theadParent
2581
2628
  });
2582
2629
 
2583
- // Add empty cell for selecting column if enabled
2584
- if (this.options.selecting && this.options.selectingCheckboxes) {
2630
+ // Add empty cell for selecting column if enabled (only if not user-placed)
2631
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
2585
2632
  FTableDOMHelper.create('th', {
2586
2633
  className: 'ftable-toolbarsearch-column-header',
2587
2634
  parent: searchRow
2588
2635
  });
2589
2636
  }
2590
2637
 
2591
- // Add search input cells for data columns
2638
+ // Add search input cells for data columns (including user-placed action columns)
2592
2639
  for (const fieldName of this.columnList) {
2593
2640
  const field = this.options.fields[fieldName];
2641
+
2642
+ // Action columns get an empty search cell
2643
+ if (field.action) {
2644
+ FTableDOMHelper.create('th', {
2645
+ className: 'ftable-toolbarsearch-column-header ftable-command-column-header',
2646
+ parent: searchRow
2647
+ });
2648
+ continue;
2649
+ }
2650
+
2594
2651
  const isSearchable = field.searchable !== false;
2595
2652
 
2596
2653
  const th = FTableDOMHelper.create('th', {
@@ -2762,9 +2819,9 @@ class FTable extends FTableEventEmitter {
2762
2819
  parent: searchRow
2763
2820
  });
2764
2821
 
2765
- const actionCount = (this.options.actions.updateAction ? 1 : 0) +
2766
- (this.options.actions.deleteAction ? 1 : 0) +
2767
- (this.options.actions.cloneAction ? 1 : 0);
2822
+ const actionCount = (this.options.actions.updateAction && !this._userPlacedActions.has('update') ? 1 : 0) +
2823
+ (this.options.actions.deleteAction && !this._userPlacedActions.has('delete') ? 1 : 0) +
2824
+ (this.options.actions.cloneAction && !this._userPlacedActions.has('clone') ? 1 : 0);
2768
2825
 
2769
2826
  if (actionCount > 0) {
2770
2827
  resetTh.colSpan = actionCount;
@@ -2872,9 +2929,7 @@ class FTable extends FTableEventEmitter {
2872
2929
  }
2873
2930
 
2874
2931
  createCustomMultiSelectForSearch(fieldSearchName, fieldName, field, optionsSource, attributes) {
2875
- const livesearch = field.livesearch
2876
- ?? (attributes['data-livesearch'] === 'true' || attributes['data-livesearch'] === true)
2877
- ?? false;
2932
+ const livesearch = field.livesearch ?? false;
2878
2933
 
2879
2934
  return this.formBuilder._buildCustomMultiSelect({
2880
2935
  hiddenSelectId: fieldSearchName,
@@ -3451,6 +3506,7 @@ class FTable extends FTableEventEmitter {
3451
3506
 
3452
3507
  this.columnList.forEach(fieldName => {
3453
3508
  const field = this.options.fields[fieldName];
3509
+ if (field.action) return; // Action columns don't appear in column picker
3454
3510
  const isVisible = field.visibility !== 'hidden';
3455
3511
  const isFixed = field.visibility === 'fixed';
3456
3512
  const isSeparator = field.visibility === 'separator';
@@ -3863,26 +3919,45 @@ class FTable extends FTableEventEmitter {
3863
3919
  // Store record data
3864
3920
  row.recordData = record;
3865
3921
 
3866
- // Add selecting checkbox if enabled
3867
- if (this.options.selecting && this.options.selectingCheckboxes) {
3922
+ // Add selecting checkbox if enabled (only if not user-placed)
3923
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
3868
3924
  this.addSelectingCell(row);
3869
3925
  }
3870
3926
 
3871
- // Add data cells
3927
+ // Add data cells (including user-placed action columns)
3872
3928
  this.columnList.forEach(fieldName => {
3873
- this.addDataCell(row, record, fieldName);
3929
+ const field = this.options.fields[fieldName];
3930
+ if (field.action) {
3931
+ // Render inline action cell
3932
+ switch (field.action) {
3933
+ case 'select':
3934
+ this.addSelectingCell(row);
3935
+ break;
3936
+ case 'update':
3937
+ this.addEditCell(row);
3938
+ break;
3939
+ case 'clone':
3940
+ this.addCloneCell(row);
3941
+ break;
3942
+ case 'delete':
3943
+ this.addDeleteCell(row);
3944
+ break;
3945
+ }
3946
+ } else {
3947
+ this.addDataCell(row, record, fieldName);
3948
+ }
3874
3949
  });
3875
3950
 
3876
- // Add action cells
3877
- if (this.options.actions.updateAction) {
3951
+ // Add default action cells only if not user-placed
3952
+ if (this.options.actions.updateAction && !this._userPlacedActions.has('update')) {
3878
3953
  this.addEditCell(row);
3879
3954
  }
3880
3955
 
3881
- if (this.options.actions.cloneAction) {
3956
+ if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
3882
3957
  this.addCloneCell(row);
3883
3958
  }
3884
3959
 
3885
- if (this.options.actions.deleteAction) {
3960
+ if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
3886
3961
  this.addDeleteCell(row);
3887
3962
  }
3888
3963
 
package/ftable.min.js CHANGED
@@ -1,4 +1,4 @@
1
- (e=>{let s={serverCommunicationError:"An error occurred while communicating to the server.",loadingMessage:"Loading records...",noDataAvailable:"No data available!",addNewRecord:"Add new record",editRecord:"Edit record",areYouSure:"Are you sure?",deleteConfirmation:"This record will be deleted. Are you sure?",yes:"Yes",no:"No",save:"Save",saving:"Saving",cancel:"Cancel",deleteText:"Delete",deleting:"Deleting",error:"An error has occured",close:"Close",cannotLoadOptionsFor:"Cannot load options for field {0}!",pagingInfo:"Showing {0}-{1} of {2}",canNotDeletedRecords:"Can not delete {0} of {1} records!",deleteProgress:"Deleting {0} of {1} records, processing...",pageSizeChangeLabel:"Row count",gotoPageLabel:"Go to page",sortingInfoPrefix:"Sorting applied: ",sortingInfoSuffix:"",ascending:"Ascending",descending:"Descending",sortingInfoNone:"No sorting applied",resetSorting:"Reset sorting",csvExport:"CSV",printTable:"🖨️ Print",cloneRecord:"Clone Record",resetTable:"Reset table",resetTableConfirm:"This will reset all columns, pagesize, sorting to their defaults. Do you want to continue?",resetSearch:"Reset"};class t{constructor(){this.cache=new Map,this.pendingRequests=new Map}generateKey(e,t){return e+"?"+Object.keys(t||{}).sort().map(e=>e+"="+t[e]).join("&")}get(e,t){e=this.generateKey(e,t);return this.cache.get(e)}set(e,t,s){e=this.generateKey(e,t);this.cache.set(e,s)}clear(e=null,t=null){if(e)if(t){t=this.generateKey(e,t);this.cache.delete(t)}else{var s,a=e.split("?")[0];for([s]of this.cache)s.startsWith(a)&&this.cache.delete(s)}else this.cache.clear()}async getOrCreate(e,t,s){let a=this.generateKey(e,t);e=this.cache.get(a);return e||(this.pendingRequests.has(a)?this.pendingRequests.get(a):(t=(async()=>{try{var e=await s();return this.cache.set(a,e),e}finally{this.pendingRequests.delete(a)}})(),this.pendingRequests.set(a,t),t))}size(){return this.cache.size}}class a{static LOG_LEVELS={DEBUG:0,INFO:1,WARN:2,ERROR:3,NONE:4};constructor(e=a.LOG_LEVELS.WARN){this.level=e}log(t,e){var s;!window.console||t<this.level||(s=Object.keys(a.LOG_LEVELS).find(e=>a.LOG_LEVELS[e]===t),console.log(`fTable ${s}: `+e))}debug(e){this.log(a.LOG_LEVELS.DEBUG,e)}info(e){this.log(a.LOG_LEVELS.INFO,e)}warn(e){this.log(a.LOG_LEVELS.WARN,e)}error(e){this.log(a.LOG_LEVELS.ERROR,e)}}class O{static PROPERTY_ATTRIBUTES=new Set(["value","checked","selected","disabled","readOnly","name","id","type","placeholder","min","max","step","required","multiple","accept","className","textContent","innerHTML","title"]);static create(e,t={}){let s=document.createElement(e);return void 0!==t.style&&(s.style.cssText=t.style),O.PROPERTY_ATTRIBUTES.forEach(e=>{e in t&&null!==t[e]&&(s[e]=t[e])}),void 0!==t.parent&&t.parent.appendChild(s),t.attributes&&Object.entries(t.attributes).forEach(([e,t])=>{null!==t&&(O.PROPERTY_ATTRIBUTES.has(e)?s[e]=t:s.setAttribute(e,t))}),s}static find(e,t=document){return t.querySelector(e)}static findAll(e,t=document){return Array.from(t.querySelectorAll(e))}static addClass(e,t){e.classList.add(...t.split(" "))}static removeClass(e,t){e.classList.remove(...t.split(" "))}static toggleClass(e,t){e.classList.toggle(t)}static show(e){e.style.display=""}static hide(e){e.style.display="none"}static escapeHtml(e){if(!e)return e;let t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};return e.replace(/[&<>"']/g,e=>t[e])}}class l{static async request(e,t={}){var s={method:"GET",headers:{}},a={...s,...t};t.headers&&(a.headers={...s.headers,...t.headers});try{var i=await fetch(e,a);if(401===i.status)throw new Error("Unauthorized");if(!i.ok)throw new Error("HTTP error! status: "+i.status);var o=i.headers.get("content-type");if(o&&o.includes("application/json"))return await i.json();var r=await i.text();try{return JSON.parse(r)}catch{return{Result:"OK",Message:r}}}catch(e){throw e}}static async get(e,t={}){let a=new URL(e,window.location.href);return Object.entries(t).forEach(([e,s])=>{if(null!=s)if(Array.isArray(s)){let t=e.endsWith("[]")?e:e+"[]";s.forEach(e=>{null!=e&&a.searchParams.append(t,e)})}else a.searchParams.append(e,s)}),this.request(a.toString(),{method:"GET",headers:{"Content-Type":"application/x-www-form-urlencoded"}})}static async post(e,t={}){e=new URL(e,window.location.href);let a=new FormData;return Object.entries(t).forEach(([e,s])=>{if(null!=s)if(Array.isArray(s)){let t=e.endsWith("[]")?e:e+"[]";s.forEach(e=>{null!=e&&a.append(t,e)})}else a.append(e,s)}),this.request(e.toString(),{method:"POST",body:a})}}class i{constructor(e,t="localStorage"){this.prefix=e,this.method=t}set(e,t){var s,e=""+this.prefix+e;"localStorage"===this.method?localStorage.setItem(e,t):((s=new Date).setDate(s.getDate()+30),document.cookie=e+`=${t}; expires=${s.toUTCString()}; path=/`)}get(e){e=""+this.prefix+e;if("localStorage"===this.method)return localStorage.getItem(e);var t,s=e+"=";for(t of decodeURIComponent(document.cookie).split(";")){for(;" "===t.charAt(0);)t=t.substring(1);if(0===t.indexOf(s))return t.substring(s.length,t.length)}return null}remove(e){e=""+this.prefix+e;"localStorage"===this.method?localStorage.removeItem(e):document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"}generatePrefix(e,t){e=e?e+"#":"";return"ftable#"+(t=>{let s=0;if(0!==t.length)for(let e=0;e<t.length;e++){var a=t.charCodeAt(e);s=(s<<5)-s+a,s&=s}return s})(e+=t.join("$")+"#c"+t.length)}}class o{constructor(e={}){this.options={title:"Modal",content:"",buttons:[],className:"ftable-modal",parent:document.body,...e},this.overlay=null,this.modal=null,this.isOpen=!1}create(){this.overlay=O.create("div",{className:"ftable-modal-overlay",parent:this.options.parent}),this.modal=O.create("div",{className:"ftable-modal "+this.options.className,parent:this.overlay});O.create("h2",{className:"ftable-modal-header",textContent:this.options.title,parent:this.modal});O.create("span",{className:"ftable-modal-close",innerHTML:"&times;",parent:this.modal}).addEventListener("click",()=>this.close());var e=O.create("div",{className:"ftable-modal-body",parent:this.modal});if("string"==typeof this.options.content?e.innerHTML=this.options.content:e.appendChild(this.options.content),0<this.options.buttons.length){let s=O.create("div",{className:"ftable-modal-footer",parent:this.modal});this.options.buttons.forEach(e=>{var t=O.create("button",{className:"ftable-dialog-button "+(e.className||""),innerHTML:`<span>${e.text}</span>`,parent:s});e.onClick&&(t._originalOnClick=e.onClick,t.addEventListener("click",this._createWrappedClickHandler(t)))})}return this.options.closeOnOverlayClick&&this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.close()}),this.hide(),this}show(){return this.modal||this.create(),this.overlay.style.display="flex",this.isOpen=!0,this.modal.querySelectorAll(".ftable-dialog-button").forEach(e=>{e.disabled=!1}),this}hide(){return this.overlay&&(this.overlay.style.display="none"),this.isOpen=!1,this}close(){return this.hide(),this.options.onClose&&this.options.onClose(),this}destroy(){return this.overlay&&this.overlay.remove(),this.isOpen=!1,this}setContent(e){this.options.content=e;var t=this.modal.querySelector(".ftable-modal-body");t&&(t.innerHTML="","string"==typeof e?t.innerHTML=e:t.appendChild(e))}_createWrappedClickHandler(a){return async e=>{a.disabled=!0;try{var t,s=a._originalOnClick;"function"==typeof s&&(t=s.call(a,e))instanceof Promise&&await t}catch(e){console.error("Modal button action failed:",e)}finally{a.disabled=!1}}}}class r{constructor(e){this.options=e,this.dependencies=new Map,this.optionsCache=new t,this.originalFieldOptions=new Map,this.resolvedFieldOptions=new Map,Object.keys(this.options.fields||{}).forEach(e=>{this.resolvedFieldOptions.set(e,{})}),Object.entries(this.options.fields).forEach(([e,t])=>{this.originalFieldOptions.set(e,t.options)})}async getFieldOptions(t,s="table",e={}){var a=this.options.fields[t],i=this.originalFieldOptions.get(t);if(!i)return null;var o=this.shouldForceRefreshForContext(a,s,e),r=this.generateOptionsCacheKey(s,e);if(!o&&!e.forceRefresh){var n=this.resolvedFieldOptions.get(t)[r];if(n)return n}try{var l={...a,options:i},c=await this.resolveOptions(l,{...e},s,o);return this.resolvedFieldOptions.get(t)[r]=c}catch(e){return console.error(`Failed to resolve options for ${t} (${s}):`,e),i}}clearResolvedOptions(e=null,s=null){e?this.resolvedFieldOptions.has(e)&&(s?this.resolvedFieldOptions.get(e)[s]=null:this.resolvedFieldOptions.set(e,{table:null,create:null,edit:null})):s?this.resolvedFieldOptions.forEach((e,t)=>{this.resolvedFieldOptions.get(t)[s]=null}):this.resolvedFieldOptions.forEach((e,t)=>{this.resolvedFieldOptions.set(t,{table:null,create:null,edit:null})})}shouldForceRefreshForContext(e,t,s){return!!e.noCache&&("boolean"==typeof e.noCache?e.noCache:"function"==typeof e.noCache?e.noCache({context:t,...s}):"object"==typeof e.noCache&&!0===e.noCache[t])}generateOptionsCacheKey(e,t){let s=[e];return t.dependedValues&&Object.keys(t.dependedValues).sort().forEach(e=>{s.push(e+"="+t.dependedValues[e])}),s.join("|")}shouldIncludeField(e,t){return"create"===t?!1!==e.create&&!(!0===e.key&&!0!==e.create):"edit"!==t||!1!==e.edit}createFieldContainer(e,t,s,a){var i=O.create("div",{className:"hidden"!=t.type?"ftable-input-field-container":"",attributes:{id:"ftable-input-field-container-div-"+e}}),t=("hidden"!=t.type&&O.create("div",{className:"ftable-input-label",textContent:t.inputTitle||t.title,parent:i}),this.createInput(e,t,s[e],a));return i.appendChild(t),i}async createForm(e="create",t={}){this.currentFormRecord=t;var s,a,i,o,r=O.create("form",{className:`ftable-dialog-form ftable-${e}-form`});this.buildDependencyMap();for([s,a]of Object.entries(this.options.fields))this.shouldIncludeField(a,e)&&(i={...a},a.dependsOn?i.options=a.options:(o=await this.getFieldOptions(s,e,{record:t,source:e}),i.options=o),o=this.createFieldContainer(s,i,t,e),r.appendChild(o));return this.setupDependencyListeners(r),r}shouldResolveOptions(e){return e&&("function"==typeof e||"string"==typeof e)&&!Array.isArray(e)&&!("object"==typeof e&&!Array.isArray(e)&&0<Object.keys(e).length)}buildDependencyMap(){this.dependencies.clear(),Object.entries(this.options.fields).forEach(([t,s])=>{if(s.dependsOn){let e;"string"==typeof s.dependsOn&&(e=s.dependsOn.split(",").map(e=>e.trim()).filter(e=>e)).forEach(e=>{this.dependencies.has(e)||this.dependencies.set(e,[]),this.dependencies.get(e).push(t)})}})}setupDependencyListeners(s){Array.from(this.dependencies.keys()).forEach(e=>{var t=s.querySelector(`[name="${e}"]`);t&&t.addEventListener("change",()=>{this.handleDependencyChange(s,e)})}),this.handleDependencyChange(s)}async resolveOptions(e,t={},s="",a=!1){if(!e.options)return[];if(Array.isArray(e.options)||"object"==typeof e.options)return e.options;let i;t={...t,source:s,clearCache:()=>{a=!0,this.updateFieldCacheSetting(e,s,!0)}};if("function"==typeof e.options)i=await e.options(t);else{if("string"!=typeof e.options)return[];i=e.options}t=i&&"object"==typeof i&&i.url;let o=t?i.url:i;if(a=t&&void 0!==i.noCache?i.noCache:a,"string"!=typeof o)return[];if(!a)return this.optionsCache.getOrCreate(o,{},async()=>{try{var e=this.options.forcePost?await l.post(o):await l.get(o);return e.Options||e.options||e||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}});try{var r=this.options.forcePost?await l.post(o):await l.get(o);return r.Options||r.options||r||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}}updateFieldCacheSetting(e,t,s){e.noCache?"boolean"==typeof e.noCache?e.noCache={table:e.noCache,create:e.noCache,edit:e.noCache,[t]:s}:"object"==typeof e.noCache&&(e.noCache[t]=s):e.noCache={[t]:s}}clearOptionsCache(e=null,t=null){this.optionsCache.clear(e,t)}getFormValues(e){var t={},s=e.elements;for(let e=0;e<s.length;e++){var a=s[e],i=a.name;if(i&&!a.disabled)switch(a.type){case"checkbox":t[i]=a.checked?a.value||"1":"0";break;case"radio":a.checked&&(t[i]=a.value);break;case"select-multiple":t[i]=Array.from(a.selectedOptions).map(e=>e.value);break;default:t[i]=a.value}}return t}async handleDependencyChange(e,s=""){var a,i,o=this.getFormValues(e),r=e.classList.contains("ftable-create-form")?"create":"edit",n=this.currentFormRecord||{},l={record:n,source:r,form:e,dependedValues:o};for([a,i]of Object.entries(this.options.fields))if(i.dependsOn){if(""!==s)if(!i.dependsOn.split(",").map(e=>e.trim()).filter(e=>e).includes(s))continue;let t=e.querySelector(`[name="${a}"]`);if(t&&this.shouldIncludeField(i,r))try{"SELECT"===t.tagName?t.innerHTML='<option value="">Loading...</option>':"INPUT"===t.tagName&&t.list&&(c=document.getElementById(t.list.id))&&(c.innerHTML="");var c,d=t.value||n[a]||"",h={...l,dependsOnField:i.dependsOn,dependsOnValue:o[i.dependsOn]},p=await this.getFieldOptions(a,r,h);"SELECT"===t.tagName?this.populateSelectOptions(t,p,d):"INPUT"===t.tagName&&t.list&&(this.populateDatalistOptions(t.list,p),d)&&(t.value=d),setTimeout(()=>{t.dispatchEvent(new Event("change",{bubbles:!0}))},0)}catch(e){console.error(`Error loading options for ${a}:`,e),"SELECT"===t.tagName&&(t.innerHTML='<option value="">Error</option>')}}}parseInputAttributes(e){if("string"!=typeof e)return e||{};for(var t={},s=/(\w+)(?:=("[^"]*"|'[^']*'|\S+))?/g;null!==(i=s.exec(e));){var a=i[1],i=i[2]?i[2].replace(/^["']|["']$/g,""):"";t[a]=""===i?"true":i}return t}createInput(e,t,s,a){var i=O.create("div",{className:`ftable-input ftable-${t.type||"text"}-input`});let o;switch(null===(s=void 0===s?null:s)&&t.defaultValue&&(s=t.defaultValue),!t.type&&t.options&&(t.type="select"),t.type){case"hidden":o=this.createHiddenInput(e,t,s);break;case"textarea":o=this.createTextarea(e,t,s);break;case"select":o=this.createSelect(e,t,s);break;case"checkbox":o=this.createCheckbox(e,t,s);break;case"radio":o=this.createRadioGroup(e,t,s);break;case"datalist":o=this.createDatalistInput(e,t,s);break;case"file":o=this.createFileInput(e,t,s);break;case"date":case"datetime":case"datetime-local":o=this.createDateInput(e,t,s);break;default:o=this.createTypedInput(e,t,s)}return"function"==typeof t.input?(a={field:t,record:this.currentFormRecord,inputField:o,formType:a},"string"==typeof(a=t.input(a))?i.innerHTML=a:a instanceof Node?i.appendChild(a):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement))):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement)),t.explain&&O.create("div",{className:"ftable-field-explain",innerHTML:`<small>${t.explain}</small>`,parent:i}),i}createDateInput(s,a,i){if("undefined"==typeof FDatepicker)return createTypedInput(s,a,i);{let e=a.dateFormat||this.options.defaultDateFormat;var o,r=document.createElement("div"),n=O.create("input",{id:"real-"+s,type:"hidden",value:i,name:s}),i={"data-date":i};a.inputAttributes&&(o=this.parseInputAttributes(a.inputAttributes),Object.assign(i,o));let t=O.create("input",{attributes:i,id:"Edit-"+s,type:"text",placeholder:a.placeholder||null,className:a.inputClass||"datepicker-input",readOnly:!0});switch(r.appendChild(n),r.appendChild(t),a.type){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"real-"+s,altFormat:"Y-m-d"})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"real-"+s,altFormat:"Y-m-d H:i:00"})},0)}return r}}createTypedInput(e,t,s){var a,i=t.type||"text",o={};let r=e,n=(t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(o,a),void 0!==a.multiple&&!1!==a.multiple)&&(r=e+"[]"),O.create("input",{attributes:o,type:i,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s,name:r}));return n.addEventListener("keypress",e=>{if(13===(e.keyCode||e.which))return e.preventDefault(),n.dispatchEvent(new Event("change",{bubbles:!0})),!1}),n}createDatalistInput(e,t,s){var a={list:e+"-datalist"},i=(t.inputAttributes&&(i=this.parseInputAttributes(t.inputAttributes),Object.assign(a,i)),O.create("input",{attributes:a,type:"search",name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s})),a=O.create("datalist",{id:e+"-datalist"});return t.options&&this.populateDatalistOptions(a,t.options),i.datalistElement=a,i}populateDatalistOptions(s,e){s.innerHTML="",Array.isArray(e)?e.forEach(e=>{O.create("option",{value:e.Value||e.value||e,textContent:e.DisplayText||e.text||e,parent:s})}):"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,parent:s})})}createHiddenInput(e,t,s){var a={};return t.inputAttributes&&(t=this.parseInputAttributes(t.inputAttributes),Object.assign(a,t)),O.create("input",{attributes:a,type:"hidden",name:e,id:"Edit-"+e,value:s})}createTextarea(e,t,s){var a,i={};return t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(i,a)),O.create("textarea",{attributes:i,name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s})}createSelect(e,t,s){var a={};let i=e,o=!1;if(t.inputAttributes&&(r=this.parseInputAttributes(t.inputAttributes),Object.assign(a,r),o=void 0!==r.multiple&&!1!==r.multiple)&&(i=e+"[]"),o)return this.createCustomMultiSelect(e,t,s,a,i);a.name=i;var r=O.create("select",{attributes:a,name:e,id:"Edit-"+e,className:t.inputClass||null});return t.options&&this.populateSelectOptions(r,t.options,s),r}createCustomMultiSelect(e,t,s,a,i){var o=Array.isArray(t.options)?t.options:t.options&&"object"==typeof t.options?Object.entries(t.options).map(([e,t])=>({Value:e,DisplayText:t})):[],a=t.livesearch??("true"===a["data-livesearch"]||!0===a["data-livesearch"])??!1;return this._buildCustomMultiSelect({containerId:e,hiddenSelectId:"Edit-"+e,hiddenSelectName:i,extraClasses:"",containerDataFieldName:e,hiddenSelectAttributes:{},optionsSource:o,initialValues:Array.isArray(s)?s:s?s.toString().split(",").filter(e=>e):[],placeholderText:t.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:a,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!0})}_buildCustomMultiSelect(e){let{hiddenSelectId:t,hiddenSelectName:s,extraClasses:a,containerDataFieldName:i,hiddenSelectAttributes:o,optionsSource:r,initialValues:n,placeholderText:l,livesearch:c,onChangeExtra:d,buildHiddenSelectOnUpdate:h}=e;let p=(e=r)?(Array.isArray(e)?e:Object.entries(e).map(([e,t])=>({Value:e,DisplayText:t}))).map(e=>({optValue:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,optText:e.DisplayText||e.text||e,groupLabel:e.Group||e.group||null})).filter(e=>null!=e.optValue&&""!==e.optValue):[],u=new Map(p.map(e=>[e.optValue.toString(),e.optText]));e=["ftable-multiselect-container",...a.split(" ").filter(Boolean)].join(" ");let m=O.create("div",{className:e,attributes:{"data-field-name":i}}),f=O.create("select",{id:t,name:s,multiple:!0,style:"display: none;",attributes:o}),g=(m.appendChild(f),m.hiddenSelect=f,O.create("div",{className:"ftable-multiselect-display",parent:m,attributes:{tabindex:"0"}})),b=O.create("div",{className:"ftable-multiselect-selected",parent:g}),v=O.create("span",{className:"ftable-multiselect-placeholder",textContent:l,parent:b}),y=(O.create("button",{type:"button",className:"ftable-multiselect-toggle",innerHTML:"▼",parent:g,attributes:{tabindex:"-1"}}),null),C=null,w=new Set(n.map(e=>e.toString())),S=new Map,E=(h||p.forEach(({optValue:e,optText:t})=>{O.create("option",{value:e,textContent:t,parent:f})}),()=>{b.innerHTML="",h?(f.innerHTML="",w.forEach(e=>{var t=u.get(e)??e;O.create("option",{value:e,textContent:t,selected:!0,parent:f})})):Array.from(f.options).forEach(e=>{e.selected=w.has(e.value)}),0===w.size?(v.textContent=l,b.appendChild(v)):w.forEach(t=>{var e=O.create("span",{className:"ftable-multiselect-tag",parent:b});O.create("span",{className:"ftable-multiselect-tag-text",textContent:u.get(t)||t,parent:e}),O.create("span",{className:"ftable-multiselect-tag-remove",innerHTML:"×",parent:e}).addEventListener("click",e=>{e.stopPropagation(),w.delete(t);e=S.get(t);e&&(e.checked=!1),E(),d&&d(f)})}),d&&d(f)}),x=()=>{g.focus(),y&&(y.remove(),y=null),C&&(C.remove(),C=null),m._cleanupHandlers&&(m._cleanupHandlers(),m._cleanupHandlers=null)},N=()=>{var e,t,s;y&&(e=g.getBoundingClientRect(),s=window.pageYOffset||document.documentElement.scrollTop,t=window.pageXOffset||document.documentElement.scrollLeft,t=e.left+t,s=e.bottom+s+4,Object.assign(y.style,{position:"absolute",left:t+"px",top:s+"px",width:e.width+"px",minWidth:"fit-content",boxSizing:"border-box",zIndex:"10000"}),(s=y.getBoundingClientRect()).right>window.innerWidth)&&(t=Math.max(10,window.innerWidth-s.width-10),y.style.left=t+"px")},T=(e="")=>{if(y){Array.from(y.querySelectorAll(".ftable-multiselect-option, .ftable-multiselect-optgroup")).forEach(e=>e.remove());let t=e.toLowerCase();e=e?p.filter(e=>e.optText.toLowerCase().includes(t)):p;let i=new Set,o=null;e.forEach(({optValue:t,optText:e,groupLabel:s})=>{s&&s!==i[i.size-1]?i.has(s)||(i.add(s),o=O.create("div",{className:"ftable-multiselect-optgroup",textContent:s,parent:y})):s||(o=null);s=O.create("div",{className:"ftable-multiselect-option",parent:y});let a=O.create("input",{type:"checkbox",className:"ftable-multiselect-checkbox",checked:w.has(t.toString()),parent:s});S.set(t.toString(),a),O.create("label",{className:"ftable-multiselect-label",textContent:e,parent:s}),s.addEventListener("click",e=>{e.stopPropagation();e=t.toString();w.has(e)?(w.delete(e),a.checked=!1):(w.add(e),a.checked=!0),E()})})}},A=a=>{if(a&&a.stopPropagation(),y)x();else{if(document.querySelectorAll(".ftable-multiselect-dropdown").forEach(e=>e.remove()),document.querySelectorAll(".ftable-multiselect-overlay").forEach(e=>e.remove()),C=O.create("div",{className:"ftable-multiselect-overlay",parent:document.body}),y=O.create("div",{className:"ftable-multiselect-dropdown",parent:document.body,attributes:{tabindex:"-1",role:"listbox","aria-multiselectable":"true"}}),c){a=O.create("div",{className:"ftable-multiselect-livesearch-wrap",parent:y});let e=O.create("input",{type:"search",className:"ftable-multiselect-livesearch",placeholder:"Search...",parent:a,attributes:{autocomplete:"off"}});e.addEventListener("input",()=>{T(e.value)}),e.addEventListener("click",e=>e.stopPropagation()),setTimeout(()=>e.focus(),0)}T(),N(),c||y.focus(),y.addEventListener("keydown",e=>{var t,s;"Escape"===e.key?x():"ArrowDown"===e.key||"ArrowUp"===e.key?(e.preventDefault(),s=(t=Array.from(y.querySelectorAll(".ftable-multiselect-checkbox"))).indexOf(document.activeElement),t["ArrowDown"===e.key?s<t.length-1?s+1:0:0<s?s-1:t.length-1]?.focus()):" "!==e.key&&"Enter"!==e.key||(e.preventDefault(),document.activeElement.classList.contains("ftable-multiselect-checkbox")&&document.activeElement.click())}),C.addEventListener("click",e=>{e.target===C&&x()});let e=()=>N(),t=e=>{y&&y.contains(e.target)||N()},s=new ResizeObserver(()=>N());window.addEventListener("scroll",t,!0),window.addEventListener("resize",e),s.observe(b),m._cleanupHandlers=()=>{window.removeEventListener("scroll",t,!0),window.removeEventListener("resize",e),s.disconnect()}}},L=(g.addEventListener("click",A),g.querySelector(".ftable-multiselect-toggle").addEventListener("click",A),g.addEventListener("keydown",e=>{"ArrowDown"!==e.key&&"Enter"!==e.key||(e.preventDefault(),A())}),m.resetMultiSelect=()=>{w.clear(),S.forEach(e=>{e.checked=!1}),x(),E()},new MutationObserver(e=>{e.forEach(e=>{e.removedNodes.forEach(e=>{(e===m||e.contains&&e.contains(m))&&(x(),L.disconnect())})})}));return setTimeout(()=>{m.parentNode&&L.observe(m.parentNode,{childList:!0,subtree:!0})},0),E(),m}createRadioGroup(o,r,n){let l=O.create("div",{className:"ftable-radio-group"});return r.options&&(Array.isArray(r.options)?r.options:"object"==typeof r.options?Object.entries(r.options).map(([e,t])=>({Value:e,DisplayText:t})):[]).forEach((e,t)=>{var s=O.create("div",{className:"ftable-radio-wrapper",parent:l}),t=o+"_"+t,a={},i=(r.inputAttributes&&(i=this.parseInputAttributes(r.inputAttributes),Object.assign(a,i)),void 0!==e.Value?e.Value:void 0!==e.value?e.value:e);O.create("input",{attributes:a,type:"radio",name:o,id:t,value:i,className:r.inputClass||null,checked:i==n,parent:s}),O.create("label",{attributes:{for:t},textContent:e.DisplayText||e.text||e,parent:s})}),l}createCheckbox(e,t,s){var a=O.create("div",{className:"ftable-yesno-check-wrapper"}),s=[1,"1",!0,"true"].includes(s);let i=this.options.messages.no,o=this.options.messages.yes;return t.values&&"object"==typeof t.values&&(void 0!==t.values[0]&&(i=t.values[0]),void 0!==t.values[1])&&(o=t.values[1]),O.create("input",{className:["ftable-yesno-check-input",t.inputClass||""].filter(Boolean).join(" "),type:"checkbox",name:e,id:"Edit-"+e,value:"1",parent:a}).checked=s,t.label?O.create("label",{className:"ftable-yesno-check-fixedlabel",attributes:{for:"Edit-"+e},textContent:t.label,parent:a}):O.create("label",{className:"ftable-yesno-check-text",attributes:{for:"Edit-"+e,"data-yes":o,"data-no":i},parent:a}),a}populateSelectOptions(o,e,r){if(o.innerHTML="",Array.isArray(e)){let s=new Map,a=[],i=(e.forEach(e=>{var t=e.Group||e.group||null;(t?(s.has(t)||s.set(t,[]),s.get(t)):a).push(e)}),(e,t)=>{var s=void 0!==e.Value?e.Value:void 0!==e.value?e.value:e;let a=O.create("option",{value:s,textContent:e.DisplayText||e.text||e,selected:s==r,parent:t});e.Data&&"object"==typeof e.Data&&Object.entries(e.Data).forEach(([e,t])=>{a.setAttribute("data-"+e,t)})});a.forEach(e=>i(e,o)),s.forEach((e,t)=>{let s=O.create("optgroup",{attributes:{label:t},parent:o});e.forEach(e=>i(e,s))})}else"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,selected:e==r,parent:o})})}createFileInput(e,t,s){var a,i={};let o=e;return t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(i,a),void 0!==a.multiple&&!1!==a.multiple)&&(o=e+"[]"),O.create("input",{type:"file",id:"Edit-"+e,name:o,className:t.inputClass||null,attributes:i})}}class n extends class{constructor(){this.events={}}on(e,t){return this.events[e]||(this.events[e]=[]),this.events[e].push(t),this}once(t,s){let a=(...e)=>{this.off(t,a),s.apply(this,e)};return a.fn=s,this.on(t,a),this}emit(e,t={}){return this.events[e]&&this.events[e].forEach(e=>e(t)),this}off(e,t){return this.events[e]&&(this.events[e]=this.events[e].filter(e=>e!==t)),this}}{constructor(e,t={}){if(super(),this.element="string"==typeof e?document.querySelector(e):e,this.element){if(this.element.ftableInstance)return this.element.ftableInstance;this.options=this.mergeOptions(t),this.verifyOptions(),this.logger=new a(this.options.logLevel),this.userPrefs=new i("",this.options.saveUserPreferencesMethod),this.formBuilder=new r(this.options,this),this.state={records:[],totalRecordCount:0,currentPage:1,isLoading:!1,selectedRecords:new Set,sorting:[],searchQueries:{}},this.elements={},this.modals={},this.searchTimeout=null,this.lastSortEvent=null,this._recalculatedOnce=!1,this.shiftKeyDown=!1,this.lastSelectedRow=null,(this.element.ftableInstance=this).init()}}mergeOptions(e){var t={tableId:void 0,logLevel:a.LOG_LEVELS.WARN,actions:{},fields:{},forcePost:!0,closeOnOverlayClick:!0,animationsEnabled:!0,loadingAnimationDelay:1e3,defaultDateLocale:"",defaultDateFormat:"Y-m-d",saveUserPreferences:!0,saveUserPreferencesMethod:"localStorage",defaultSorting:"",tableReset:!1,paging:!1,pageList:"normal",pageSize:10,pageSizes:[10,25,50,100,250,500],gotoPageArea:"combobox",sorting:!1,multiSorting:!1,multiSortingCtrlKey:!0,selecting:!1,multiselect:!1,openChildAsAccordion:!1,toolbarsearch:!1,toolbarreset:!0,searchDebounceMs:300,listCache:3e4,messages:{...s}};return this.deepMerge(t,e)}deepMerge(e,t){var s,a={...e};for(s in t)t[s]&&"object"==typeof t[s]&&!Array.isArray(t[s])?a[s]=this.deepMerge(a[s]||{},t[s]):a[s]=t[s];return a}verifyOptions(){this.options.pageSize&&!this.options.pageSizes.includes(this.options.pageSize)&&(this.options.pageSize=this.options.pageSizes[0])}static setMessages(e){Object.assign(s,e)}init(){this.processFieldDefinitions(),this.createMainStructure(),this.setupFTableUserPreferences(),this.createTable(),this.createModals(),this.options.paging&&this.createPagingUI(),this.resolveAsyncFieldOptions().then(()=>{setTimeout(()=>{this.refreshDisplayValues()},0)}).catch(console.error),this.bindEvents(),this.updateSortingHeaders(),this.renderSortingInfo(),this.initColumnWidths()}initColumnWidths(){var e=this.columnList.filter(e=>{e=this.options.fields[e];return"hidden"!==e.visibility&&"separator"!==e.visibility});let t=e.length;e.forEach(e=>{e=this.options.fields[e];e.width=e.width||100/t+"%"})}normalizeColumnWidths(){var e=this.elements.mainContainer,t=this.columnList.map(e=>({th:this.elements.table.querySelector(`[data-field-name="${e}"]`),field:this.options.fields[e]})).filter(e=>e.th&&"hidden"!==e.field.visibility&&"separator"!==e.field.visibility);if(0!==t.length){let s=e.offsetWidth,a=0;t.forEach(e=>{var t=e.th.offsetWidth/s*100;e.field.width=t+"%",e.th.style.width=e.field.width,a+=t})}}parseDefaultSorting(e){let o=[];return e&&"string"==typeof e&&e.split(",").forEach(s=>{s=s.trim();if(s){var a=s.toUpperCase().indexOf(" DESC"),i=s.toUpperCase().indexOf(" ASC");let e="ASC",t=s;e=0<a?(t=s.slice(0,a).trim(),"DESC"):(t=(0<i?s.slice(0,i):s).trim(),"ASC");a=this.options.fields[t];a&&!1!==a.sorting&&o.push({fieldName:t,direction:e})}}),o}createPagingUI(){this.elements.bottomPanel=O.create("div",{className:"ftable-bottom-panel",parent:this.elements.mainContainer}),this.elements.leftArea=O.create("div",{className:"ftable-left-area",parent:this.elements.bottomPanel}),this.elements.rightArea=O.create("div",{className:"ftable-right-area",parent:this.elements.bottomPanel}),this.elements.pagingListArea=O.create("div",{className:"ftable-page-list",parent:this.elements.leftArea}),this.elements.pagingGotoArea=O.create("div",{className:"ftable-page-goto",parent:this.elements.leftArea}),this.elements.pageInfoSpan=O.create("div",{className:"ftable-page-info",parent:this.elements.rightArea}),!1!==this.options.pageSizeChangeArea&&this.createPageSizeSelector()}createPageSizeSelector(){var e=O.create("span",{className:"ftable-page-size-change",parent:this.elements.leftArea});O.create("span",{textContent:this.options.messages.pageSizeChangeLabel,parent:e});let s=O.create("select",{className:"ftable-page-size-select",parent:e});(this.options.pageSizes||[10,25,50,100,250,500]).forEach(e=>{var t=O.create("option",{attributes:{value:e},textContent:e.toString(),parent:s});e===this.state.pageSize&&(t.selected=!0)}),s.addEventListener("change",e=>{this.changePageSize(parseInt(e.target.value))})}processFieldDefinitions(){this.fieldList=Object.keys(this.options.fields),this.fieldList.forEach(e=>{e=this.options.fields[e];!0===e.key?(void 0!==e.create&&e.create||(e.create=!0,e.type="hidden"),void 0!==e.edit&&e.edit||(e.edit=!0,e.type="hidden")):(e.create=e.create??!0,e.edit=e.edit??!0,e.list=e.list??!0,e.sorting=e.sorting??!0),e.visibility=e.visibility??"visible"}),this.columnList=this.fieldList.filter(e=>!1!==this.options.fields[e].list),this.keyField=this.fieldList.find(e=>!0===this.options.fields[e].key),this.keyField||this.logger.info("No key field defined")}async resolveAsyncFieldOptions(){var e=this.columnList.map(async t=>{this.options.fields[t];var e=this.formBuilder.originalFieldOptions.get(t);if(this.formBuilder.shouldResolveOptions(e))try{var s=this.formBuilder.generateOptionsCacheKey("table",{});this.formBuilder.resolvedFieldOptions.get(t)?.[s]||await this.formBuilder.getFieldOptions(t,"table")}catch(e){console.error(`Failed to resolve table options for ${t}:`,e)}});await Promise.all(e)}async refreshDisplayValues(){var e=this.elements.tableBody.querySelectorAll(".ftable-data-row");if(0!==e.length)for(var t of e)for(var s of this.columnList){var a,i,o=this.options.fields[s];o.options&&(a=t.querySelector(`td[data-field-name="${s}"]`))&&(i=this.formBuilder.generateOptionsCacheKey("table",{}),i=this.formBuilder.resolvedFieldOptions.get(s)?.[i],s=this.getDisplayText(t.recordData,s,i),a.innerHTML=o.listEscapeHTML?O.escapeHtml(s):s)}}createMainStructure(){this.elements.mainContainer=O.create("div",{className:"ftable-main-container",parent:this.element}),this.options.title&&(this.elements.titleDiv=O.create("div",{className:"ftable-title",parent:this.elements.mainContainer}),O.create("div",{className:"ftable-title-text",innerHTML:this.options.title,parent:this.elements.titleDiv})),this.elements.toolbarDiv=O.create("div",{className:"ftable-toolbar",parent:this.elements.titleDiv||this.elements.mainContainer}),this.elements.tableDiv=O.create("div",{className:"ftable-table-div",parent:this.elements.mainContainer})}createTable(){this.elements.table=O.create("table",{className:"ftable",parent:this.elements.tableDiv}),this.options.tableId&&(this.elements.table.id=this.options.tableId),this.createTableHeader(),this.createTableBody(),this.addNoDataRow()}createTableHeader(){var e=O.create("thead",{parent:this.elements.table});let o=O.create("tr",{parent:e});if(this.options.selecting&&this.options.selectingCheckboxes){var t=O.create("th",{className:"ftable-command-column-header ftable-column-header-select",parent:o});if(this.options.multiselect){let e=O.create("input",{attributes:{type:"checkbox"},parent:t});e.addEventListener("change",()=>{this.toggleSelectAll(e.checked)})}}this.columnList.forEach(t=>{var e=this.options.fields[t],s=O.create("th",{className:`ftable-column-header ${e.listClass||""} `+(e.listClassHeader||""),attributes:{"data-field-name":t},parent:o}),a=(e.width&&(s.style.width=e.width),O.create("div",{className:"ftable-column-header-container",parent:s})),i=(e.tooltip&&a.setAttribute("title",e.tooltip),O.create("span",{className:"ftable-column-header-text",innerHTML:e.title||t,parent:a}));this.options.sorting&&!1!==e.sorting&&(O.addClass(i,"ftable-sortable-text"),O.addClass(s,"ftable-column-header-sortable"),s.addEventListener("click",e=>{e.preventDefault();e=e.ctrlKey||e.metaKey;this.sortByColumn(t,e)})),!1!==this.options.columnResizable&&!1!==e.columnResizable&&this.makeColumnResizable(s,a),"hidden"!==e.visibility&&"separator"!==e.visibility||O.hide(s)}),this.options.actions.updateAction&&O.create("th",{className:"ftable-command-column-header ftable-column-header-edit",parent:o}),this.options.actions.cloneAction&&O.create("th",{className:"ftable-command-column-header ftable-column-header-clone",parent:o}),this.options.actions.deleteAction&&O.create("th",{className:"ftable-command-column-header ftable-column-header-delete",parent:o}),this.options.toolbarsearch&&this.createSearchHeaderRow(e).catch(e=>{console.error("Failed to create search header row:",e)})}async createSearchHeaderRow(e){var t,s=O.create("tr",{className:"ftable-toolbarsearch-row",parent:e});this.options.selecting&&this.options.selectingCheckboxes&&O.create("th",{className:"ftable-toolbarsearch-column-header",parent:s});for(let i of this.columnList){var o=this.options.fields[i],r=!1!==o.searchable,n=O.create("th",{className:"ftable-toolbarsearch-column-header",parent:s});if(r){r=O.create("div",{className:"ftable-column-header-container",parent:n});let s,a="text";o.searchType?a=o.searchType:!o.type&&o.options?a="select":o.type&&(a=o.type);var l="ftable-toolbarsearch-"+i;switch(a){case"date":case"datetime":case"datetime-local":if("undefined"!=typeof FDatepicker){let e=o.searchDateFormat||o.dateFormat||this.options.defaultDateFormat;var c=document.createElement("div"),d=O.create("input",{className:"ftable-toolbarsearch-extra",type:"hidden",id:"ftable-toolbarsearch-extra-"+i,attributes:{"data-field-name":i}});let t=O.create("input",{className:"ftable-toolbarsearch",id:"ftable-toolbarsearch-"+i,type:"text",placeholder:o.searchPlaceholder||o.placeholder||"",readOnly:!0});switch(c.appendChild(d),c.appendChild(t),a){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d",autoClose:!0})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d H:i:00"})},0)}s=c}else s=O.create("input",{className:"ftable-toolbarsearch",type:a,id:l,attributes:{"data-field-name":i}});break;case"checkbox":o.values||(o.values={0:this.options.messages.no,1:this.options.messages.yes}),s=await this.createSelectForSearch(i,o,!0);break;case"select":s=o.options?await this.createSelectForSearch(i,o,!1):O.create("input",{className:"ftable-toolbarsearch",type:"text",id:l,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}});break;case"datalist":s=await this.createDatalistForSearch(i,o);break;default:s=O.create("input",{className:"ftable-toolbarsearch",type:"text",id:l,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}})}if(s){r.appendChild(s),s.datalistElement&&s.datalistElement instanceof Node&&r.appendChild(s.datalistElement);let e=s;"SELECT"===(e=s.classList&&s.classList.contains("ftable-multiselect-container")&&s.hiddenSelect?s.hiddenSelect:e).tagName?e.addEventListener("change",e=>{this.handleSearchInputChange(e)}):e.addEventListener("input",e=>{this.handleSearchInputChange(e)})}}"hidden"!==o.visibility&&"separator"!==o.visibility||O.hide(n)}this.options.toolbarsearch&&this.options.toolbarreset&&(e=O.create("th",{className:"ftable-toolbarsearch-column-header ftable-toolbarsearch-reset",parent:s}),0<(t=(this.options.actions.updateAction?1:0)+(this.options.actions.deleteAction?1:0)+(this.options.actions.cloneAction?1:0))?e.colSpan=t:O.addClass(e,"ftable-command-column-header"),O.create("button",{className:"ftable-toolbarsearch-reset-button",textContent:this.options.messages.resetSearch,attributes:{id:"ftable-toolbarsearch-reset-button"},parent:e}).addEventListener("click",()=>this.resetSearch()))}async createSelectForSearch(e,t,s){var a,i="ftable-toolbarsearch-"+e,o={};let r=e,n=!1;t.searchAttributes?(a=this.formBuilder.parseInputAttributes(t.searchAttributes),Object.assign(o,a),n=void 0!==a.multiple&&!1!==a.multiple):t.inputAttributes&&(a=this.formBuilder.parseInputAttributes(t.inputAttributes),Object.assign(o,a),n=void 0!==a.multiple&&!1!==a.multiple),n&&(r=e+"[]"),o["data-field-name"]=r;let l;if(s&&t.values?l=Object.entries(t.values).map(([e,t])=>({Value:e,DisplayText:t})):t.options&&(l=await this.formBuilder.getFieldOptions(e)),n)return this.createCustomMultiSelectForSearch(i,e,t,l,o);let c=O.create("select",{attributes:o,id:i,className:"ftable-toolbarsearch"});return 0<l?.length&&(""===l[0].Value||""===l[0].value||""===l[0]||""===l[0].DisplayText&&null==l[0].Value)||O.create("option",{value:"",innerHTML:"&nbsp;",parent:c}),l&&Array.isArray(l)?l.forEach(e=>{O.create("option",{value:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,textContent:e.DisplayText||e.text||e,parent:c})}):l&&"object"==typeof l&&Object.entries(l).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,parent:c})}),c}createCustomMultiSelectForSearch(e,t,s,a,i){var o=s.livesearch??("true"===i["data-livesearch"]||!0===i["data-livesearch"])??!1;return this.formBuilder._buildCustomMultiSelect({hiddenSelectId:e,hiddenSelectName:i["data-field-name"]||e,extraClasses:"ftable-multiselect-search ftable-toolbarsearch",containerDataFieldName:i["data-field-name"]||e,hiddenSelectAttributes:i,optionsSource:a,initialValues:[],placeholderText:s.searchPlaceholder||s.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:o,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!1})}async createDatalistForSearch(e,t){var s="ftable-toolbarsearch-"+e,a=s+"-datalist",i=O.create("datalist",{attributes:{id:a}}),s=O.create("input",{className:"ftable-toolbarsearch",type:"search",id:s,placeholder:t.searchPlaceholder||t.placeholder||"Type or select...",attributes:{"data-field-name":e,list:a}});s.datalistElement=i;let o;return t.searchOptions?o=t.searchOptions:t.options&&(o=await this.formBuilder.getFieldOptions(e,"table")),o&&this.formBuilder.populateDatalistOptions(i,o),s}handleSearchInputChange(e){var e=e.target,t=e.getAttribute("data-field-name");let s;s=e.multiple&&e.options?Array.from(e.selectedOptions).map(e=>e.value).filter(e=>""!==e.trim()).map(e=>e.trim()):e.value.trim(),this.state.currentPage=1,Array.isArray(s)&&0<s.length||!Array.isArray(s)&&s?this.state.searchQueries[t]=s:delete this.state.searchQueries[t],clearTimeout(this.searchTimeout),this.searchTimeout=setTimeout(()=>{this.load()},this.options.searchDebounceMs)}resetSearch(){this.state.searchQueries={},this.elements.table.querySelectorAll(".ftable-toolbarsearch").forEach(e=>{"SELECT"===e.tagName?e.selectedIndex=0:e.value=""}),this.elements.table.querySelectorAll(".ftable-multiselect-container").forEach(e=>{"function"==typeof e.resetMultiSelect&&e.resetMultiSelect()}),this.load()}getNextResizableHeader(t){var s=Array.from(this.elements.table.querySelectorAll("thead th.ftable-column-header-resizable"));for(let e=s.indexOf(t)+1;e<s.length;e++)if(null!==s[e].offsetParent)return s[e];return null}makeColumnResizable(a,e){O.addClass(a,"ftable-column-header-resizable"),this.elements.resizeBar||(this.elements.resizeBar=O.create("div",{className:"ftable-column-resize-bar",parent:this.elements.mainContainer}),O.hide(this.elements.resizeBar));e=O.create("div",{className:"ftable-column-resize-handler",parent:e});let i=!1,o=0,r=0,n,l=null,c=0,d=null,h=(e.addEventListener("mousedown",e=>{e.preventDefault(),e.stopPropagation(),i=!0,n=this.elements.mainContainer.getBoundingClientRect(),o=e.clientX,r=a.offsetWidth,(l=this.getNextResizableHeader(a))&&(c=l.offsetWidth,e=l.dataset.fieldName,d=this.options.fields[e]);e=a.getBoundingClientRect();this.elements.resizeBar.style.left=e.right-n.left+"px",this.elements.resizeBar.style.top=e.top-n.top+"px",this.elements.resizeBar.style.height=this.elements.table.offsetHeight+"px",O.show(this.elements.resizeBar),document.addEventListener("mousemove",h),document.addEventListener("mouseup",p)}),e=>{i&&(this.elements.resizeBar.style.left=e.clientX-n.left+"px")}),p=e=>{var t,s;i&&(i=!1,e=e.clientX-o,s=n.width,t=Math.max(50,r+e)/s*100,l&&(e=Math.max(50,c-e)/s*100,d.width=e.toFixed(2)+"%",l.style.width=d.width),(s=this.options.fields[a.dataset.fieldName]).width=t.toFixed(2)+"%",a.style.width=s.width,this.normalizeColumnWidths(),this.options.saveUserPreferences&&this.saveColumnSettings(),O.hide(this.elements.resizeBar),document.removeEventListener("mousemove",h),document.removeEventListener("mouseup",p))}}saveColumnSettings(){if(this.options.saveUserPreferences){let a={};this.columnList.forEach(e=>{var t,s=this.elements.table.querySelector(`[data-field-name="${e}"]`);s&&(t=this.options.fields[e],a[e]={width:s.style.width||t.width||"auto",visibility:t.visibility||"visible"})}),this.userPrefs.set("column-settings",JSON.stringify(a))}}saveState(){var e;this.options.saveUserPreferences&&(e={sorting:this.state.sorting,pageSize:this.state.pageSize},this.userPrefs.set("table-state",JSON.stringify(e)))}loadColumnSettings(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("column-settings");if(e)try{var t=JSON.parse(e);Object.entries(t).forEach(([e,t])=>{e=this.options.fields[e];e&&(t.width&&(e.width=t.width),t.visibility)&&(e.visibility=t.visibility)})}catch(e){this.logger.warn("Failed to load column settings:",e)}}}loadState(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("table-state");if(e)try{var t=JSON.parse(e);Array.isArray(t.sorting)&&(this.state.sorting=t.sorting),t.pageSize&&this.options.pageSizes.includes(t.pageSize)&&(this.state.pageSize=t.pageSize)}catch(e){this.logger.warn("Failed to load table state:",e)}}}createTableBody(){this.elements.tableBody=O.create("tbody",{parent:this.elements.table})}addNoDataRow(){var e,t;this.elements.tableBody.querySelector(".ftable-no-data-row")||(e=O.create("tr",{className:"ftable-no-data-row",parent:this.elements.tableBody}),t=this.elements.table.querySelector("thead tr").children.length,O.create("td",{attributes:{colspan:t},textContent:this.options.messages.noDataAvailable,parent:e}))}removeNoDataRow(){var e=this.elements.tableBody.querySelector(".ftable-no-data-row");e&&e.remove()}createModals(){this.options.actions.createAction&&this.createAddRecordModal(),this.options.actions.updateAction&&this.createEditRecordModal(),this.options.actions.deleteAction&&this.createDeleteConfirmModal(),this.createErrorModal(),this.createInfoModal(),this.createLoadingModal(),Object.values(this.modals).forEach(e=>e.create())}createAddRecordModal(){this.modals.addRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.addNewRecord,className:"ftable-add-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.addRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveNewRecord()}]})}createEditRecordModal(){this.modals.editRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.editRecord,className:"ftable-edit-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.editRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"edit",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveEditedRecord()}]})}createDeleteConfirmModal(){this.modals.deleteConfirm=new o({parent:this.elements.mainContainer,title:this.options.messages.areYouSure,className:"ftable-delete-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>this.modals.deleteConfirm.close()},{text:this.options.messages.deleteText,className:"ftable-dialog-deletebutton",onClick:()=>this.confirmDelete()}]})}createErrorModal(){this.modals.error=new o({parent:this.elements.mainContainer,title:this.options.messages.error,className:"ftable-error-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.error.close()}]})}createInfoModal(){this.modals.info=new o({parent:this.elements.mainContainer,title:"",className:"ftable-info-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.info.close()}]})}createLoadingModal(){this.modals.loading=new o({parent:this.elements.mainContainer,title:"",className:"ftable-loading-modal",closeOnOverlayClick:!1,content:`<div class="ftable-loading-message">${this.options.messages.loadingMessage}</div>`})}bindEvents(){this.subscribeOptionEvents(),this.createCustomToolbarItems(),this.createToolbarButtons(),this.bindKeyboardEvents(),!1!==this.options.columnSelectable&&this.createColumnSelectionMenu()}subscribeOptionEvents(){["formCreated","formClosed","recordsLoaded","recordAdded","recordUpdated","recordDeleted","selectionChanged"].forEach(e=>{"function"==typeof this.options[e]&&this.on(e,this.options[e])})}createColumnSelectionMenu(){this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null,this.elements.table.querySelector("thead").addEventListener("contextmenu",e=>{e.preventDefault(),this.showColumnSelectionMenu(e)})}showColumnSelectionMenu(e){this.hideColumnSelectionMenu(),this.elements.columnSelectionOverlay=O.create("div",{className:"ftable-contextmenu-overlay",parent:document.body}),this.elements.columnSelectionMenu=O.create("div",{className:"ftable-column-selection-container",parent:document.body}),this.populateColumnSelectionMenu(),this.positionColumnSelectionMenu(e),this.elements.columnSelectionOverlay.addEventListener("click",e=>{e.target===this.elements.columnSelectionOverlay&&this.hideColumnSelectionMenu()}),this.elements.columnSelectionOverlay.addEventListener("contextmenu",e=>{e.preventDefault(),this.hideColumnSelectionMenu()})}populateColumnSelectionMenu(){let l=O.create("ul",{className:"ftable-column-select-list",parent:this.elements.columnSelectionMenu});this.columnList.forEach(t=>{var e=this.options.fields[t],s="hidden"!==e.visibility,a="fixed"===e.visibility,i="separator"===e.visibility,o=this.isFieldSorted(t),r=O.create("li",{className:"ftable-column-select-item",parent:l}),n=O.create("label",{className:"ftable-column-select-label",parent:r});if(!i){let e=O.create("input",{attributes:{type:"checkbox",id:"column-"+t},parent:n});e.checked=s,(a||o&&s)&&(e.disabled=!0,r.style.opacity="0.6"),e.disabled||e.addEventListener("change",()=>{this.setColumnVisibility(t,e.checked)})}a=O.create("span",{textContent:e.title||t,style:i?"font-weight: bold;":null,parent:n});o&&((s=O.create("span",{className:"ftable-sort-indicator",textContent:" (sorted)",parent:a})).style.fontSize="0.8em",s.style.color="#666")})}positionColumnSelectionMenu(e){var t=this,s=e.pageX,e=e.pageY,e=(t.elements.columnSelectionMenu.style.position="absolute",t.elements.columnSelectionMenu.style.left=s+"px",t.elements.columnSelectionMenu.style.top=e+"px",t.elements.columnSelectionMenu.style.minWidth="100px",t.elements.columnSelectionMenu.style.boxSizing="border-box",t.elements.columnSelectionMenu.offsetWidth),a=window.innerWidth;a<s+e&&(s=Math.max(10,a-e-10),t.elements.columnSelectionMenu.style.left=s+"px")}hideColumnSelectionMenu(){this.elements.columnSelectionOverlay&&(this.elements.columnSelectionOverlay.remove(),this.elements.columnSelectionMenu.remove(),this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null)}isFieldSorted(t){return this.state.sorting.some(e=>e.fieldName===t)}createToolbarButtons(){this.options.csvExport&&this.addToolbarButton({text:this.options.messages.csvExport,className:"ftable-toolbar-item-csv",onClick:()=>{var e=this.options.title?this.options.title.replace(/[^a-z0-9]/gi,"-").toLowerCase()+".csv":"table-export.csv";this.exportToCSV(e)}}),this.options.printTable&&this.addToolbarButton({text:this.options.messages.printTable,className:"ftable-toolbar-item-print",onClick:()=>{this.printTable()}}),this.options.actions.createAction&&this.addToolbarButton({text:this.options.messages.addNewRecord,className:"ftable-toolbar-item-add-record",addIconSpan:!0,onClick:()=>this.showAddRecordForm()})}addToolbarButton(t){var e,s=O.create("button",{className:"ftable-toolbar-item "+(t.className||""),id:t.id||null,title:t.title||null,textContent:t.text||null,type:"button",parent:this.elements.toolbarDiv});return t.addIconSpan&&(e=O.create("span",{className:"ftable-toolbar-item-icon "+(t.className||""),parent:s}),t.text)&&(s.textContent="",s.append(e,t.text||"")),t.icon&&(e=O.create("img",{attributes:{src:t.icon,alt:"",width:16,height:16,style:"margin-right: 6px; vertical-align: middle;"},parent:s}),t.text)&&(s.textContent="",s.append(e,t.text||"")),t.onClick&&s.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),t.onClick(e)}),t.disabled&&(s.disabled=!0),s}createCustomToolbarItems(){this.options.toolbar&&this.options.toolbar.items&&this.options.toolbar.items.forEach((e,t)=>{this.addToolbarButton({text:e.text||"",className:"ftable-toolbar-item-custom "+(e.buttonClass||""),id:e.buttonId||"ftable-toolbar-item-custom-id-"+t,title:e.tooltip||"",icon:e.icon||null,onClick:"function"==typeof e.click?e.click:null})})}bindKeyboardEvents(){this.options.selecting&&(document.addEventListener("keydown",e=>{"Shift"===e.key&&(this.shiftKeyDown=!0)}),document.addEventListener("keyup",e=>{"Shift"===e.key&&(this.shiftKeyDown=!1)}))}setupFTableUserPreferences(){var e;this.options.saveUserPreferences&&(e=this.userPrefs.generatePrefix(this.options.tableId||"",this.fieldList),this.userPrefs=new i(e,this.options.saveUserPreferencesMethod),this.loadState(),this.loadColumnSettings())}async load(e={}){if(!this.state.isLoading){this.state.isLoading=!0,this.showLoadingIndicator();try{var t={...e,...this.buildLoadParams()},s=await this.performLoad(t);this.processLoadedData(s),this.emit("recordsLoaded",{records:s.Records,serverResponse:s})}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Load failed: "+e.message)}finally{this.state.isLoading=!1,this.hideLoadingIndicator()}this.renderSortingInfo(),this.normalizeColumnWidths()}}buildLoadParams(){var e,t={};if(this.options.paging&&(this.state.pageSize||(this.state.pageSize=this.options.pageSize),t.jtStartIndex=(this.state.currentPage-1)*this.state.pageSize,t.jtPageSize=this.state.pageSize),this.options.sorting&&(0<this.state.sorting.length?t.jtSorting=this.state.sorting.map(e=>e.fieldName+" "+e.direction).join(", "):this.options.defaultSorting&&(t.jtSorting=this.parseDefaultSorting(this.options.defaultSorting).map(e=>e.fieldName+" "+e.direction).join(", "))),this.options.toolbarsearch&&0<Object.keys(this.state.searchQueries).length){let s=[],a=[];Object.entries(this.state.searchQueries).forEach(([t,e])=>{Array.isArray(e)?e.forEach(e=>{""!==e&&null!=e&&(s.push(e),a.push(t))}):""!==e&&(s.push(e),a.push(t))}),0<s.length&&(t.q=s,t.opt=a)}return"function"==typeof this.options.listQueryParams&&(e=this.options.listQueryParams(),Object.assign(t,e)),t}isCacheExpired(e,t){return!e||!e.timestamp||t<Date.now()-e.timestamp}async performLoad(e){var t=this.options.actions.listAction;if(this.options.listCache&&"string"==typeof t){var s=this.formBuilder.optionsCache.get(t,e);if(s&&!this.isCacheExpired(s,this.options.listCache))return s.data}let a;if("function"==typeof t)a=await t(e);else{if("string"!=typeof t)throw new Error("No valid listAction provided");a=this.options.forcePost?await l.post(t,e):await l.get(t,e)}if(a&&"OK"===a.Result)return this.options.listCache&&"string"==typeof t&&this.formBuilder.optionsCache.set(t,e,{data:a,timestamp:Date.now()}),a;throw new Error(a?.Message||"Invalid response from server")}processLoadedData(e){"OK"!==e.Result?this.showError(e.Message||"Unknown error occurred"):(this.state.records=e.Records||[],this.state.totalRecordCount=e.TotalRecordCount||this.state.records.length,this.renderTableData(),this.updatePagingInfo())}renderTableData(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>e.remove()),0===this.state.records.length?this.addNoDataRow():(this.removeNoDataRow(),this.state.records.forEach(e=>{e=this.createTableRow(e);this.elements.tableBody.appendChild(e)}),this.refreshRowStyles())}createTableRow(t){let s=O.create("tr",{className:"ftable-data-row",attributes:{"data-record-key":this.getKeyValue(t)}});return s.recordData=t,this.options.selecting&&this.options.selectingCheckboxes&&this.addSelectingCell(s),this.columnList.forEach(e=>{this.addDataCell(s,t,e)}),this.options.actions.updateAction&&this.addEditCell(s),this.options.actions.cloneAction&&this.addCloneCell(s),this.options.actions.deleteAction&&this.addDeleteCell(s),this.options.selecting&&this.makeRowSelectable(s),s}addSelectingCell(t){var e=O.create("td",{className:"ftable-command-column ftable-selecting-column",parent:t});O.create("input",{className:"norowselectonclick",attributes:{type:"checkbox"},parent:e}).addEventListener("change",e=>{this.toggleRowSelection(t)})}addDataCell(e,t,s){var a=this.options.fields[s],i=this.formBuilder.generateOptionsCacheKey("table",{}),i=this.formBuilder.resolvedFieldOptions.get(s)?.[i],t=this.getDisplayText(t,s,i),i=O.create("td",{className:`${a.listClass||""} `+(a.listClassEntry||""),innerHTML:a.listEscapeHTML?O.escapeHtml(t):t,attributes:{"data-field-name":s},parent:e});"fixed"===a.visibility||"hidden"!==a.visibility&&"separator"!==a.visibility||O.hide(i)}addEditCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-edit-command-button",attributes:{title:this.options.messages.editRecord},innerHTML:`<span>${this.options.messages.editRecord}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.editRecord(t)})}addCloneCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-clone-command-button",attributes:{title:this.options.messages.cloneRecord||"Clone"},innerHTML:`<span>${this.options.messages.cloneRecord||"Clone"}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.cloneRecord(t)})}addDeleteCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-delete-command-button",attributes:{title:this.options.messages.deleteText},innerHTML:`<span>${this.options.messages.deleteText}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.deleteRecord(t)})}getDisplayText(e,t,s=null){var a=this.options.fields[t],i=e[t],s=s||a.options;let o=i;return"date"===a.type&&i&&(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),a.dateFormat||this.options.defaultDateFormat):this.formatDate(i,a.dateLocale||this.options.defaultDateLocale)),"datetime-local"!==a.type&&"datetime"!==a.type||!i||(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),a.dateFormat||this.options.defaultDateFormat):this.formatDateTime(i,a.dateLocale||this.options.defaultDateLocale)),"checkbox"===a.type&&(o=this.getCheckboxText(t,i)),s&&(t=this.findOptionByValue(s,i),o=t?t.DisplayText||t.text||t:i),(o=a.display&&"function"==typeof a.display?a.display({record:e,value:i,displayValue:o}):o)||""}_parseDate(e){return e.includes("Date")?new Date(parseInt(e.substr(6),10)):10==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10)):19==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10),parseInt(e.substr(11,2),10),parseInt(e.substr(14,2),10),parseInt(e.substr(17,2),10)):new Date(e)}formatDate(e,t){if(!e)return"";var s=this._parseDate(e);try{return isNaN(s.getTime())?e:s.toLocaleDateString(t,{year:"numeric",month:"2-digit",day:"2-digit"})}catch{return e}}formatDateTime(e,t){if(!e)return"";var s=this._parseDate(e);try{return isNaN(s.getTime())?e:s.toLocaleString(t)}catch{return e}}getCheckboxText(e,t){e=this.options.fields[e];return e.values&&e.values[t]?e.values[t]:t?this.options.messages.yes:this.options.messages.no}findOptionByValue(e,t){return Array.isArray(e)?e.find(e=>(e.Value||e.value)===t||e===t):"object"==typeof e&&null!==e&&e.hasOwnProperty(t)?e[t]:null}refreshRowStyles(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach((e,t)=>{t%2==0?O.addClass(e,"ftable-row-even"):O.removeClass(e,"ftable-row-even")})}getKeyValue(e){return this.keyField?e[this.keyField]:null}async showAddRecordForm(){var e=await this.formBuilder.createForm("create");this.modals.addRecord.setContent(e),this.modals.addRecord.show(),this.currentForm=e,this.emit("formCreated",{form:e,formType:"create",record:null})}async saveNewRecord(){if(this.currentForm)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performCreate(e);"OK"===t.Result?(this.clearListCache(),this.modals.addRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),t.Message&&this.showInfo(t.Message),await this.load(),this.emit("recordAdded",{record:t.Record})):this.showError(t.Message||"Create failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Create failed: "+e.message)}}else this.currentForm.reportValidity()}async editRecord(e){var t=e.recordData,s=await this.formBuilder.createForm("edit",t);this.modals.editRecord.setContent(s),this.modals.editRecord.show(),this.currentForm=s,this.currentEditingRow=e,this.emit("formCreated",{form:s,formType:"edit",record:t})}async saveEditedRecord(){if(this.currentForm&&this.currentEditingRow)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performUpdate(e);"OK"===t.Result?(this.clearListCache(),this.modals.editRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"edit",record:this.currentEditingRow.recordData}),this.updateRowData(this.currentEditingRow,t.Record||e),t.Message&&this.showInfo(t.Message),this.emit("recordUpdated",{record:t.Record||e,row:this.currentEditingRow})):this.showError(t.Message||"Update failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Update failed: "+e.message)}}else this.currentForm.reportValidity()}async cloneRecord(e){var e={...e.recordData},t=(this.keyField&&(e[this.keyField]=""),await this.formBuilder.createForm("create",e));this.modals.addRecord.options.content=t,this.modals.addRecord.setContent(t),this.modals.addRecord.show(),this.currentForm=t,this.emit("formCreated",{form:t,formType:"create",record:e})}async deleteRows(e){if(e.length){var t=this.options.messages.areYouSure;if(confirm(t)){var s,a=[];for(s of e)try{var i=await this.performDelete(s);a.push({key:s,success:"OK"===i.Result,result:i})}catch(e){a.push({key:s,success:!1,error:e.message})}a.filter(e=>e.success).forEach(({key:e})=>{e=this.getRowByKey(e);e&&this.removeRowFromTable(e)});t=a.filter(e=>!e.success).length;0<t&&this.showError(t+` of ${a.length} records could not be deleted`),this.refreshRowStyles(),this.updatePagingInfo()}}}deleteRecord(e){var t=e.recordData;let s=this.options.messages.deleteConfirmation;if("function"==typeof this.options.deleteConfirmation){t={row:e,record:t,deleteConfirmMessage:s,cancel:!1,cancelMessage:this.options.messages.cancel};if(this.options.deleteConfirmation(t),t.cancel)return void(t.cancelMessage&&this.showError(t.cancelMessage));s=t.deleteConfirmMessage}this.modals.deleteConfirm.setContent(`<p>${s}</p>`),this.modals.deleteConfirm.show(),this.currentDeletingRow=e}async confirmDelete(){if(this.currentDeletingRow){var e=this.getKeyValue(this.currentDeletingRow.recordData);try{var t=await this.performDelete(e);"OK"===t.Result?(this.clearListCache(),this.modals.deleteConfirm.close(),this.removeRowFromTable(this.currentDeletingRow),t.Message&&this.showInfo(t.Message),this.emit("recordDeleted",{record:this.currentDeletingRow.recordData})):this.showError(t.Message||"Delete failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Delete failed: "+e.message)}}}async performCreate(e){var t=this.options.actions.createAction;if("function"==typeof t)return t(e);if("string"==typeof t)return l.post(t,e);throw new Error("No valid createAction provided")}async performUpdate(e){var t=this.options.actions.updateAction;if("function"==typeof t)return t(e);if("string"==typeof t)return l.post(t,e);throw new Error("No valid updateAction provided")}async performDelete(e){var t=this.options.actions.deleteAction;let s;if(s=null===e||"object"!=typeof e||Array.isArray(e)?{[this.keyField]:e}:e,"function"==typeof t)return t(s);if("string"==typeof t)return l.post(t,s);throw new Error("No valid deleteAction provided")}getFormData(e){var t,s,a,i={};for([t,s]of new FormData(e).entries())t.endsWith("[]")?(i[a=t.slice(0,-2)]||(i[a]=[]),i[a].push(s)):i.hasOwnProperty(t)?Array.isArray(i[t])?i[t].push(s):i[t]=[i[t],s]:i[t]=s;return i}updateRowData(i,e){i.recordData={...i.recordData,...e},Object.keys(e).forEach(e=>{var t,s,a=this.options.fields[e];a&&(t=i.querySelector(`td[data-field-name="${e}"]`))&&(s=this.formBuilder.generateOptionsCacheKey("table",{}),s=this.formBuilder.resolvedFieldOptions.get(e)?.[s],e=this.getDisplayText(i.recordData,e,s),t.innerHTML=a.listEscapeHTML?O.escapeHtml(e):e,t.className=(`${a.listClass||""} `+(a.listClassEntry||"")).trim())})}removeRowFromTable(e){e.remove(),0===this.elements.tableBody.querySelectorAll(".ftable-data-row").length&&this.addNoDataRow(),this.refreshRowStyles()}makeRowSelectable(t){!1!==this.options.selectOnRowClick&&t.addEventListener("click",e=>{["INPUT","BUTTON","SELECT","TEXTAREA","A"].includes(e.target.tagName)||e.target.classList.contains("norowselectonclick")||this.toggleRowSelection(t)})}toggleRowSelection(e){var t=e.classList.contains("ftable-row-selected");if(this.shiftKeyDown&&this.lastSelectedRow&&this.options.multiselect){this.clearAllSelections();var s=Array.from(this.elements.tableBody.querySelectorAll(".ftable-data-row")),a=s.indexOf(this.lastSelectedRow),i=s.indexOf(e),[i,o]=a<i?[a,i]:[i,a];for(let e=i;e<=o;e++)this.selectRow(s[e])}else this.options.multiselect||this.clearAllSelections(),t?this.deselectRow(e):this.selectRow(e);t&&!this.shiftKeyDown||(this.lastSelectedRow=e),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}selectRow(e){O.addClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!0),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.add(t)}deselectRow(e){O.removeClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!1),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.delete(t)}recalcColumnWidths(){this.columnList.forEach(e=>{var t=this.options.fields[e],e=this.elements.table.querySelector(`[data-field-name="${e}"]`);e&&t.width&&(e.style.width=t.width)}),this.elements.table.offsetHeight}recalcColumnWidthsOnce(){this._recalculatedOnce||(this.recalcColumnWidths(),this._recalculatedOnce=!0)}clearAllSelections(){this.elements.tableBody.querySelectorAll(".ftable-row-selected").forEach(e=>this.deselectRow(e))}toggleSelectAll(t){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{t?this.selectRow(e):this.deselectRow(e)}),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}getSelectedRows(){return Array.from(this.elements.tableBody.querySelectorAll(".ftable-row-selected"))}sortByColumn(s,a=!1){var i=this.options.fields[s];if(i&&!1!==i.sorting){i=this.state.sorting.findIndex(e=>e.fieldName===s);let e=!0,t="ASC";0<=i?"ASC"===this.state.sorting[i].direction?(t="DESC",this.state.sorting[i].direction=t):(this.state.sorting.splice(i,1),e=!1):this.state.sorting.push({fieldName:s,direction:t}),(!this.options.multiSorting||this.options.multiSortingCtrlKey&&!a)&&(this.state.sorting=e?[{fieldName:s,direction:t}]:[]),this.updateSortingHeaders(),this.load(),this.saveState()}}updateSortingHeaders(){this.elements.table.querySelectorAll(".ftable-column-header-sortable").forEach(e=>{O.removeClass(e,"ftable-column-header-sorted-asc ftable-column-header-sorted-desc")}),this.state.sorting.forEach(e=>{var t=this.elements.table.querySelector(`[data-field-name="${e.fieldName}"]`);t&&O.addClass(t,"ftable-column-header-sorted-"+e.direction.toLowerCase())})}updatePagingInfo(){var e,t,s;this.options.paging&&this.elements.pageInfoSpan&&(this.state.totalRecordCount<=0?(this.elements.pageInfoSpan.textContent="",this.elements.pagingListArea.innerHTML=""):(e=(this.state.currentPage-1)*this.state.pageSize+1,t=Math.min(this.state.currentPage*this.state.pageSize,this.state.totalRecordCount),s=this.options.messages.pagingInfo||"Showing {0}-{1} of {2}",this.elements.pageInfoSpan.textContent=s.replace(/\{0\}/g,e).replace(/\{1\}/g,t).replace(/\{2\}/g,this.state.totalRecordCount),this.createPageListNavigation(),this.createPageGotoNavigation()))}createPageListNavigation(){if(this.elements.pagingListArea){this.elements.pagingListArea.innerHTML="";var e=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(!(e<=1)){if(this.createPageButton("&laquo;",1,1===this.state.currentPage,"ftable-page-number-first"),this.createPageButton("&lsaquo;",this.state.currentPage-1,1===this.state.currentPage,"ftable-page-number-previous"),"normal"==this.options.pageList){var s=this.calculatePageNumbers(e);let t=0;s.forEach(e=>{1<e-t&&O.create("span",{className:"ftable-page-number-space",textContent:"...",parent:this.elements.pagingListArea}),this.createPageButton(e.toString(),e,!1,e===this.state.currentPage?"ftable-page-number ftable-page-number-active":"ftable-page-number"),t=e})}this.createPageButton("&rsaquo;",this.state.currentPage+1,this.state.currentPage>=e,"ftable-page-number-next"),this.createPageButton("&raquo;",e,this.state.currentPage>=e,"ftable-page-number-last")}}}createPageGotoNavigation(){if(this.options.paging&&"none"!==this.options.gotoPageArea){let s=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(s<=1)this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML="";else{this.elements.pagingGotoArea.style.display="inline-block",this.elements.pagingGotoArea.innerHTML="";O.create("span",{textContent:this.options.messages.gotoPageLabel+": ",parent:this.elements.pagingGotoArea});var e="ftable-goto-page-"+(this.options.tableId||"default");if("combobox"===this.options.gotoPageArea){this.elements.gotoPageSelect=O.create("select",{id:e,className:"ftable-page-goto-select",parent:this.elements.pagingGotoArea});for(let e=1;e<=s;e++)O.create("option",{attributes:{value:e},textContent:e,parent:this.elements.gotoPageSelect});this.elements.gotoPageSelect.value=this.state.currentPage,this.elements.gotoPageSelect.addEventListener("change",e=>{e=parseInt(e.target.value);1<=e&&e<=s&&this.changePage(e)})}else"textbox"===this.options.gotoPageArea&&(this.elements.gotoPageInput=O.create("input",{attributes:{type:"number",id:e,min:"1",max:s,value:this.state.currentPage,className:"ftable-page-goto-input",style:"width: 65px; margin-left: 4px;"},parent:this.elements.pagingGotoArea}),this.elements.gotoPageInput.addEventListener("change",e=>{var t=parseInt(e.target.value);1<=t&&t<=s?this.changePage(t):e.target.value=this.state.currentPage}))}}else this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML=""}createPageButton(e,t,s,a){a=O.create("span",{className:a+(s?" ftable-page-number-disabled":""),innerHTML:e,parent:this.elements.pagingListArea});s||(a.style.cursor="pointer",a.addEventListener("click",e=>{e.preventDefault(),this.changePage(t)}))}calculatePageNumbers(t){if(t<=7)return Array.from({length:t},(e,t)=>t+1);var s=this.state.currentPage,a=new Set([1,2,t-1,t]);for(let e=Math.max(1,s-1);e<=Math.min(t,s+1);e++)a.add(e);return Array.from(a).sort((e,t)=>e-t)}changePage(e){var t=Math.ceil(this.state.totalRecordCount/this.state.pageSize);(e=Math.max(1,Math.min(e,t)))!==this.state.currentPage&&(this.state.currentPage=e,this.load())}changePageSize(e){this.state.pageSize=e,this.state.currentPage=1,this.load(),this.saveState()}showLoadingIndicator(){0===this.options.loadingAnimationDelay?this.modals.loading&&this.modals.loading.show():this.loadingTimeout=setTimeout(()=>{this.modals.loading&&this.modals.loading.show(),this.loadingShownAt=Date.now()},this.options.loadingAnimationDelay||500)}hideLoadingIndicator(){this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null);var e=this.loadingShownAt?Date.now()-this.loadingShownAt:0;this.modals.loading&&(e<200?setTimeout(()=>{this.modals.loading.hide()},200-e):this.modals.loading.hide()),this.loadingShownAt=null}showError(e){this.modals.error?(this.modals.error.setContent(`<p>${e}</p>`),this.modals.error.show()):alert(e)}showInfo(e){this.modals.info?(this.modals.info.setContent(`<p>${e}</p>`),this.modals.info.show()):alert(e)}reload(e=!1){return this.clearListCache(),e?this.preservedSelections=new Set(this.state.selectedRecords):this.state.selectedRecords.clear(),this.load().then(()=>(e&&this.preservedSelections&&(this.restoreSelections(),this.preservedSelections=null),this))}clearListCache(){this.options.actions.listAction&&"string"==typeof this.options.actions.listAction&&this.formBuilder.optionsCache.clear(this.options.actions.listAction)}restoreSelections(){if(this.preservedSelections)return this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{var t=this.getKeyValue(e.recordData);t&&this.preservedSelections.has(t)&&this.selectRow(e)}),this}getRowByKey(e){return this.elements.tableBody.querySelector(`[data-record-key="${e}"]`)}destroy(){this.element&&this.element.ftableInstance&&(this.element.ftableInstance=null),Object.values(this.modals).forEach(e=>e.destroy()),this.elements.mainContainer&&this.elements.mainContainer.remove(),this.searchTimeout&&clearTimeout(this.searchTimeout),this.loadingTimeout&&clearTimeout(this.loadingTimeout),window.removeEventListener("resize",this.handleResize),this.options=null,this.state=null,this.elements=null,this.formBuilder=null,this.modals=null}setOption(e,t){return this.options[e]=t,this}getState(){return{...this.state}}addFilter(t,e,s="equals"){return this.state.filters||(this.state.filters=[]),this.state.filters=this.state.filters.filter(e=>e.fieldName!==t),null!=e&&""!==e&&this.state.filters.push({fieldName:t,value:e,operator:s}),this}clearFilters(){return this.state.filters=[],this}exportToCSV(e="table-data.csv"){var t,s,a,i=this.elements.table.cloneNode(!0),o=[],r=e=>`"${String(e||"").replace(/"/g,'""')}"`,n=[];for(t of i.querySelectorAll("thead th"))t.classList.contains("ftable-command-column-header")||t.classList.contains("ftable-toolbarsearch-column-header")||"none"===t.style.display||(s=t.textContent.trim(),n.push(r(s)));o.push(n.join(","));for(a of i.querySelectorAll("tbody tr"))if("none"!==a.style.display){var l,c,d,h=[];let e=!1;for(l of a.querySelectorAll("td"))l.classList.contains("ftable-command-column")||"none"===l.style.display||(l.querySelector("img, button, input, select")&&(l.innerHTML=l.textContent),c=l.innerHTML.replace(/<br\s*\/?>/gi,"\n"),(d=document.createElement("div")).innerHTML=c,h.push(r(d.textContent||"")),e=!0);e&&o.push(h.join(","))}var i=o.join("\n"),i=new Blob(["\ufeff"+i],{type:"text/csv;charset=utf-8;"}),p=document.createElement("a");p.href=URL.createObjectURL(i),p.download=e,p.click(),p.remove()}printTable(){var e=window.open("","_blank","width=800,height=600"),t=this.elements.table.outerHTML,t=`
1
+ (e=>{let s={serverCommunicationError:"An error occurred while communicating to the server.",loadingMessage:"Loading records...",noDataAvailable:"No data available!",addNewRecord:"Add new record",editRecord:"Edit record",areYouSure:"Are you sure?",deleteConfirmation:"This record will be deleted. Are you sure?",yes:"Yes",no:"No",save:"Save",saving:"Saving",cancel:"Cancel",deleteText:"Delete",deleting:"Deleting",error:"An error has occured",close:"Close",cannotLoadOptionsFor:"Cannot load options for field {0}!",pagingInfo:"Showing {0}-{1} of {2}",canNotDeletedRecords:"Can not delete {0} of {1} records!",deleteProgress:"Deleting {0} of {1} records, processing...",pageSizeChangeLabel:"Row count",gotoPageLabel:"Go to page",sortingInfoPrefix:"Sorting applied: ",sortingInfoSuffix:"",ascending:"Ascending",descending:"Descending",sortingInfoNone:"No sorting applied",resetSorting:"Reset sorting",csvExport:"CSV",printTable:"🖨️ Print",cloneRecord:"Clone Record",resetTable:"Reset table",resetTableConfirm:"This will reset all columns, pagesize, sorting to their defaults. Do you want to continue?",resetSearch:"Reset"};class t{constructor(){this.cache=new Map,this.pendingRequests=new Map}generateKey(e,t){return e+"?"+Object.keys(t||{}).sort().map(e=>e+"="+t[e]).join("&")}get(e,t){e=this.generateKey(e,t);return this.cache.get(e)}set(e,t,s){e=this.generateKey(e,t);this.cache.set(e,s)}clear(e=null,t=null){if(e)if(t){t=this.generateKey(e,t);this.cache.delete(t)}else{var s,a=e.split("?")[0];for([s]of this.cache)s.startsWith(a)&&this.cache.delete(s)}else this.cache.clear()}async getOrCreate(e,t,s){let a=this.generateKey(e,t);e=this.cache.get(a);return e||(this.pendingRequests.has(a)?this.pendingRequests.get(a):(t=(async()=>{try{var e=await s();return this.cache.set(a,e),e}finally{this.pendingRequests.delete(a)}})(),this.pendingRequests.set(a,t),t))}size(){return this.cache.size}}class a{static LOG_LEVELS={DEBUG:0,INFO:1,WARN:2,ERROR:3,NONE:4};constructor(e=a.LOG_LEVELS.WARN){this.level=e}log(t,e){var s;!window.console||t<this.level||(s=Object.keys(a.LOG_LEVELS).find(e=>a.LOG_LEVELS[e]===t),console.log(`fTable ${s}: `+e))}debug(e){this.log(a.LOG_LEVELS.DEBUG,e)}info(e){this.log(a.LOG_LEVELS.INFO,e)}warn(e){this.log(a.LOG_LEVELS.WARN,e)}error(e){this.log(a.LOG_LEVELS.ERROR,e)}}class O{static PROPERTY_ATTRIBUTES=new Set(["value","checked","selected","disabled","readOnly","name","id","type","placeholder","min","max","step","required","multiple","accept","className","textContent","innerHTML","title"]);static create(e,t={}){let s=document.createElement(e);return void 0!==t.style&&(s.style.cssText=t.style),O.PROPERTY_ATTRIBUTES.forEach(e=>{e in t&&null!==t[e]&&(s[e]=t[e])}),void 0!==t.parent&&t.parent.appendChild(s),t.attributes&&Object.entries(t.attributes).forEach(([e,t])=>{null!==t&&(O.PROPERTY_ATTRIBUTES.has(e)?s[e]=t:s.setAttribute(e,t))}),s}static find(e,t=document){return t.querySelector(e)}static findAll(e,t=document){return Array.from(t.querySelectorAll(e))}static addClass(e,t){e.classList.add(...t.split(" "))}static removeClass(e,t){e.classList.remove(...t.split(" "))}static toggleClass(e,t){e.classList.toggle(t)}static show(e){e.style.display=""}static hide(e){e.style.display="none"}static escapeHtml(e){if(!e)return e;let t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"};return e.replace(/[&<>"']/g,e=>t[e])}}class n{static async request(e,t={}){var s={method:"GET",headers:{}},a={...s,...t};t.headers&&(a.headers={...s.headers,...t.headers});try{var i=await fetch(e,a);if(401===i.status)throw new Error("Unauthorized");if(!i.ok)throw new Error("HTTP error! status: "+i.status);var o=i.headers.get("content-type");if(o&&o.includes("application/json"))return await i.json();var r=await i.text();try{return JSON.parse(r)}catch{return{Result:"OK",Message:r}}}catch(e){throw e}}static async get(e,t={}){let a=new URL(e,window.location.href);return Object.entries(t).forEach(([e,s])=>{if(null!=s)if(Array.isArray(s)){let t=e.endsWith("[]")?e:e+"[]";s.forEach(e=>{null!=e&&a.searchParams.append(t,e)})}else a.searchParams.append(e,s)}),this.request(a.toString(),{method:"GET",headers:{"Content-Type":"application/x-www-form-urlencoded"}})}static async post(e,t={}){e=new URL(e,window.location.href);let a=new FormData;return Object.entries(t).forEach(([e,s])=>{if(null!=s)if(Array.isArray(s)){let t=e.endsWith("[]")?e:e+"[]";s.forEach(e=>{null!=e&&a.append(t,e)})}else a.append(e,s)}),this.request(e.toString(),{method:"POST",body:a})}}class i{constructor(e,t="localStorage"){this.prefix=e,this.method=t}set(e,t){var s,e=""+this.prefix+e;"localStorage"===this.method?localStorage.setItem(e,t):((s=new Date).setDate(s.getDate()+30),document.cookie=e+`=${t}; expires=${s.toUTCString()}; path=/`)}get(e){e=""+this.prefix+e;if("localStorage"===this.method)return localStorage.getItem(e);var t,s=e+"=";for(t of decodeURIComponent(document.cookie).split(";")){for(;" "===t.charAt(0);)t=t.substring(1);if(0===t.indexOf(s))return t.substring(s.length,t.length)}return null}remove(e){e=""+this.prefix+e;"localStorage"===this.method?localStorage.removeItem(e):document.cookie=e+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"}generatePrefix(e,t){e=e?e+"#":"";return"ftable#"+(t=>{let s=0;if(0!==t.length)for(let e=0;e<t.length;e++){var a=t.charCodeAt(e);s=(s<<5)-s+a,s&=s}return s})(e+=t.join("$")+"#c"+t.length)}}class o{constructor(e={}){this.options={title:"Modal",content:"",buttons:[],className:"ftable-modal",parent:document.body,...e},this.overlay=null,this.modal=null,this.isOpen=!1}create(){this.overlay=O.create("div",{className:"ftable-modal-overlay",parent:this.options.parent}),this.modal=O.create("div",{className:"ftable-modal "+this.options.className,parent:this.overlay});O.create("h2",{className:"ftable-modal-header",textContent:this.options.title,parent:this.modal});O.create("span",{className:"ftable-modal-close",innerHTML:"&times;",parent:this.modal}).addEventListener("click",()=>this.close());var e=O.create("div",{className:"ftable-modal-body",parent:this.modal});if("string"==typeof this.options.content?e.innerHTML=this.options.content:e.appendChild(this.options.content),0<this.options.buttons.length){let s=O.create("div",{className:"ftable-modal-footer",parent:this.modal});this.options.buttons.forEach(e=>{var t=O.create("button",{className:"ftable-dialog-button "+(e.className||""),innerHTML:`<span>${e.text}</span>`,parent:s});e.onClick&&(t._originalOnClick=e.onClick,t.addEventListener("click",this._createWrappedClickHandler(t)))})}return this.options.closeOnOverlayClick&&this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.close()}),this.hide(),this}show(){return this.modal||this.create(),this.overlay.style.display="flex",this.isOpen=!0,this.modal.querySelectorAll(".ftable-dialog-button").forEach(e=>{e.disabled=!1}),this}hide(){return this.overlay&&(this.overlay.style.display="none"),this.isOpen=!1,this}close(){return this.hide(),this.options.onClose&&this.options.onClose(),this}destroy(){return this.overlay&&this.overlay.remove(),this.isOpen=!1,this}setContent(e){this.options.content=e;var t=this.modal.querySelector(".ftable-modal-body");t&&(t.innerHTML="","string"==typeof e?t.innerHTML=e:t.appendChild(e))}_createWrappedClickHandler(a){return async e=>{a.disabled=!0;try{var t,s=a._originalOnClick;"function"==typeof s&&(t=s.call(a,e))instanceof Promise&&await t}catch(e){console.error("Modal button action failed:",e)}finally{a.disabled=!1}}}}class r{constructor(e){this.options=e,this.dependencies=new Map,this.optionsCache=new t,this.originalFieldOptions=new Map,this.resolvedFieldOptions=new Map,Object.keys(this.options.fields||{}).forEach(e=>{this.resolvedFieldOptions.set(e,{})}),Object.entries(this.options.fields).forEach(([e,t])=>{this.originalFieldOptions.set(e,t.options)})}async getFieldOptions(t,s="table",e={}){var a=this.options.fields[t],i=this.originalFieldOptions.get(t);if(!i)return null;var o=this.shouldForceRefreshForContext(a,s,e),r=this.generateOptionsCacheKey(s,e);if(!o&&!e.forceRefresh){var l=this.resolvedFieldOptions.get(t)[r];if(l)return l}try{var n={...a,options:i},c=await this.resolveOptions(n,{...e},s,o);return this.resolvedFieldOptions.get(t)[r]=c}catch(e){return console.error(`Failed to resolve options for ${t} (${s}):`,e),i}}clearResolvedOptions(e=null,s=null){e?this.resolvedFieldOptions.has(e)&&(s?this.resolvedFieldOptions.get(e)[s]=null:this.resolvedFieldOptions.set(e,{table:null,create:null,edit:null})):s?this.resolvedFieldOptions.forEach((e,t)=>{this.resolvedFieldOptions.get(t)[s]=null}):this.resolvedFieldOptions.forEach((e,t)=>{this.resolvedFieldOptions.set(t,{table:null,create:null,edit:null})})}shouldForceRefreshForContext(e,t,s){return!!e.noCache&&("boolean"==typeof e.noCache?e.noCache:"function"==typeof e.noCache?e.noCache({context:t,...s}):"object"==typeof e.noCache&&!0===e.noCache[t])}generateOptionsCacheKey(e,t){let s=[e];return t.dependedValues&&Object.keys(t.dependedValues).sort().forEach(e=>{s.push(e+"="+t.dependedValues[e])}),s.join("|")}shouldIncludeField(e,t){return"create"===t?!1!==e.create&&!(!0===e.key&&!0!==e.create):"edit"!==t||!1!==e.edit}createFieldContainer(e,t,s,a){var i=O.create("div",{className:"hidden"!=t.type?"ftable-input-field-container":"",attributes:{id:"ftable-input-field-container-div-"+e}}),t=("hidden"!=t.type&&O.create("div",{className:"ftable-input-label",textContent:t.inputTitle||t.title,parent:i}),this.createInput(e,t,s[e],a));return i.appendChild(t),i}async createForm(e="create",t={}){this.currentFormRecord=t;var s,a,i,o,r=O.create("form",{className:`ftable-dialog-form ftable-${e}-form`});this.buildDependencyMap();for([s,a]of Object.entries(this.options.fields))this.shouldIncludeField(a,e)&&(i={...a},a.dependsOn?i.options=a.options:(o=await this.getFieldOptions(s,e,{record:t,source:e}),i.options=o),o=this.createFieldContainer(s,i,t,e),r.appendChild(o));return this.setupDependencyListeners(r),r}shouldResolveOptions(e){return e&&("function"==typeof e||"string"==typeof e)&&!Array.isArray(e)&&!("object"==typeof e&&!Array.isArray(e)&&0<Object.keys(e).length)}buildDependencyMap(){this.dependencies.clear(),Object.entries(this.options.fields).forEach(([t,s])=>{if(s.dependsOn){let e;"string"==typeof s.dependsOn&&(e=s.dependsOn.split(",").map(e=>e.trim()).filter(e=>e)).forEach(e=>{this.dependencies.has(e)||this.dependencies.set(e,[]),this.dependencies.get(e).push(t)})}})}setupDependencyListeners(s){Array.from(this.dependencies.keys()).forEach(e=>{var t=s.querySelector(`[name="${e}"]`);t&&t.addEventListener("change",()=>{this.handleDependencyChange(s,e)})}),this.handleDependencyChange(s)}async resolveOptions(e,t={},s="",a=!1){if(!e.options)return[];if(Array.isArray(e.options)||"object"==typeof e.options)return e.options;let i;t={...t,source:s,clearCache:()=>{a=!0,this.updateFieldCacheSetting(e,s,!0)}};if("function"==typeof e.options)i=await e.options(t);else{if("string"!=typeof e.options)return[];i=e.options}t=i&&"object"==typeof i&&i.url;let o=t?i.url:i;if(a=t&&void 0!==i.noCache?i.noCache:a,"string"!=typeof o)return[];if(!a)return this.optionsCache.getOrCreate(o,{},async()=>{try{var e=this.options.forcePost?await n.post(o):await n.get(o);return e.Options||e.options||e||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}});try{var r=this.options.forcePost?await n.post(o):await n.get(o);return r.Options||r.options||r||[]}catch(e){return console.error(`Failed to load options from ${o}:`,e),[]}}updateFieldCacheSetting(e,t,s){e.noCache?"boolean"==typeof e.noCache?e.noCache={table:e.noCache,create:e.noCache,edit:e.noCache,[t]:s}:"object"==typeof e.noCache&&(e.noCache[t]=s):e.noCache={[t]:s}}clearOptionsCache(e=null,t=null){this.optionsCache.clear(e,t)}getFormValues(e){var t={},s=e.elements;for(let e=0;e<s.length;e++){var a=s[e],i=a.name;if(i&&!a.disabled)switch(a.type){case"checkbox":t[i]=a.checked?a.value||"1":"0";break;case"radio":a.checked&&(t[i]=a.value);break;case"select-multiple":t[i]=Array.from(a.selectedOptions).map(e=>e.value);break;default:t[i]=a.value}}return t}async handleDependencyChange(e,s=""){var a,i,o=this.getFormValues(e),r=e.classList.contains("ftable-create-form")?"create":"edit",l=this.currentFormRecord||{},n={record:l,source:r,form:e,dependedValues:o};for([a,i]of Object.entries(this.options.fields))if(i.dependsOn){if(""!==s)if(!i.dependsOn.split(",").map(e=>e.trim()).filter(e=>e).includes(s))continue;let t=e.querySelector(`[name="${a}"]`);if(t&&this.shouldIncludeField(i,r))try{"SELECT"===t.tagName?t.innerHTML='<option value="">Loading...</option>':"INPUT"===t.tagName&&t.list&&(c=document.getElementById(t.list.id))&&(c.innerHTML="");var c,d=t.value||l[a]||"",h={...n,dependsOnField:i.dependsOn,dependsOnValue:o[i.dependsOn]},p=await this.getFieldOptions(a,r,h);"SELECT"===t.tagName?this.populateSelectOptions(t,p,d):"INPUT"===t.tagName&&t.list&&(this.populateDatalistOptions(t.list,p),d)&&(t.value=d),setTimeout(()=>{t.dispatchEvent(new Event("change",{bubbles:!0}))},0)}catch(e){console.error(`Error loading options for ${a}:`,e),"SELECT"===t.tagName&&(t.innerHTML='<option value="">Error</option>')}}}parseInputAttributes(e){if("string"!=typeof e)return e||{};for(var t={},s=/(\w+)(?:=("[^"]*"|'[^']*'|\S+))?/g;null!==(i=s.exec(e));){var a=i[1],i=i[2]?i[2].replace(/^["']|["']$/g,""):"";t[a]=""===i?"true":i}return t}createInput(e,t,s,a){var i=O.create("div",{className:`ftable-input ftable-${t.type||"text"}-input`});let o;switch(null===(s=void 0===s?null:s)&&t.defaultValue&&(s=t.defaultValue),!t.type&&t.options&&(t.type="select"),t.type){case"hidden":o=this.createHiddenInput(e,t,s);break;case"textarea":o=this.createTextarea(e,t,s);break;case"select":o=this.createSelect(e,t,s);break;case"checkbox":o=this.createCheckbox(e,t,s);break;case"radio":o=this.createRadioGroup(e,t,s);break;case"datalist":o=this.createDatalistInput(e,t,s);break;case"file":o=this.createFileInput(e,t,s);break;case"date":case"datetime":case"datetime-local":o=this.createDateInput(e,t,s);break;default:o=this.createTypedInput(e,t,s)}return"function"==typeof t.input?(a={field:t,record:this.currentFormRecord,inputField:o,formType:a},"string"==typeof(a=t.input(a))?i.innerHTML=a:a instanceof Node?i.appendChild(a):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement))):(i.appendChild(o),o.datalistElement&&o.datalistElement instanceof Node&&i.appendChild(o.datalistElement)),t.explain&&O.create("div",{className:"ftable-field-explain",innerHTML:`<small>${t.explain}</small>`,parent:i}),i}createDateInput(s,a,i){if("undefined"==typeof FDatepicker)return createTypedInput(s,a,i);{let e=a.dateFormat||this.options.defaultDateFormat;var o,r=document.createElement("div"),l=O.create("input",{id:"real-"+s,type:"hidden",value:i,name:s}),i={"data-date":i};a.inputAttributes&&(o=this.parseInputAttributes(a.inputAttributes),Object.assign(i,o));let t=O.create("input",{attributes:i,id:"Edit-"+s,type:"text",placeholder:a.placeholder||null,className:a.inputClass||"datepicker-input",readOnly:!0});switch(r.appendChild(l),r.appendChild(t),a.type){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"real-"+s,altFormat:"Y-m-d"})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"real-"+s,altFormat:"Y-m-d H:i:00"})},0)}return r}}createTypedInput(e,t,s){var a,i=t.type||"text",o={};let r=e,l=(t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(o,a),void 0!==a.multiple&&!1!==a.multiple)&&(r=e+"[]"),O.create("input",{attributes:o,type:i,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s,name:r}));return l.addEventListener("keypress",e=>{if(13===(e.keyCode||e.which))return e.preventDefault(),l.dispatchEvent(new Event("change",{bubbles:!0})),!1}),l}createDatalistInput(e,t,s){var a={list:e+"-datalist"},i=(t.inputAttributes&&(i=this.parseInputAttributes(t.inputAttributes),Object.assign(a,i)),O.create("input",{attributes:a,type:"search",name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s})),a=O.create("datalist",{id:e+"-datalist"});return t.options&&this.populateDatalistOptions(a,t.options),i.datalistElement=a,i}populateDatalistOptions(s,e){s.innerHTML="",Array.isArray(e)?e.forEach(e=>{O.create("option",{value:e.Value||e.value||e,textContent:e.DisplayText||e.text||e,parent:s})}):"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,parent:s})})}createHiddenInput(e,t,s){var a={};return t.inputAttributes&&(t=this.parseInputAttributes(t.inputAttributes),Object.assign(a,t)),O.create("input",{attributes:a,type:"hidden",name:e,id:"Edit-"+e,value:s})}createTextarea(e,t,s){var a,i={};return t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(i,a)),O.create("textarea",{attributes:i,name:e,id:"Edit-"+e,className:t.inputClass||null,placeholder:t.placeholder||null,value:s})}createSelect(e,t,s){var a={};let i=e,o=!1;if(t.inputAttributes&&(r=this.parseInputAttributes(t.inputAttributes),Object.assign(a,r),o=void 0!==r.multiple&&!1!==r.multiple)&&(i=e+"[]"),o)return this.createCustomMultiSelect(e,t,s,a,i);a.name=i;var r=O.create("select",{attributes:a,name:e,id:"Edit-"+e,className:t.inputClass||null});return t.options&&this.populateSelectOptions(r,t.options,s),r}createCustomMultiSelect(e,t,s,a,i){var o=Array.isArray(t.options)?t.options:t.options&&"object"==typeof t.options?Object.entries(t.options).map(([e,t])=>({Value:e,DisplayText:t})):[],r=t.livesearch??!1;return this._buildCustomMultiSelect({containerId:e,hiddenSelectId:"Edit-"+e,hiddenSelectName:i,extraClasses:"",containerDataFieldName:e,hiddenSelectAttributes:{},optionsSource:o,initialValues:Array.isArray(s)?s:s?s.toString().split(",").filter(e=>e):[],placeholderText:t.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:r,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!0})}_buildCustomMultiSelect(e){let{hiddenSelectId:t,hiddenSelectName:s,extraClasses:a,containerDataFieldName:i,hiddenSelectAttributes:o,optionsSource:r,initialValues:l,placeholderText:n,livesearch:c,onChangeExtra:d,buildHiddenSelectOnUpdate:h}=e;let p=(e=r)?(Array.isArray(e)?e:Object.entries(e).map(([e,t])=>({Value:e,DisplayText:t}))).map(e=>({optValue:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,optText:e.DisplayText||e.text||e,groupLabel:e.Group||e.group||null})).filter(e=>null!=e.optValue&&""!==e.optValue):[],u=new Map(p.map(e=>[e.optValue.toString(),e.optText]));e=["ftable-multiselect-container",...a.split(" ").filter(Boolean)].join(" ");let m=O.create("div",{className:e,attributes:{"data-field-name":i}}),f=O.create("select",{id:t,name:s,multiple:!0,style:"display: none;",attributes:o}),g=(m.appendChild(f),m.hiddenSelect=f,O.create("div",{className:"ftable-multiselect-display",parent:m,attributes:{tabindex:"0"}})),b=O.create("div",{className:"ftable-multiselect-selected",parent:g}),v=O.create("span",{className:"ftable-multiselect-placeholder",textContent:n,parent:b}),y=(O.create("button",{type:"button",className:"ftable-multiselect-toggle",innerHTML:"▼",parent:g,attributes:{tabindex:"-1"}}),null),C=null,w=new Set(l.map(e=>e.toString())),S=new Map,E=(h||p.forEach(({optValue:e,optText:t})=>{O.create("option",{value:e,textContent:t,parent:f})}),()=>{b.innerHTML="",h?(f.innerHTML="",w.forEach(e=>{var t=u.get(e)??e;O.create("option",{value:e,textContent:t,selected:!0,parent:f})})):Array.from(f.options).forEach(e=>{e.selected=w.has(e.value)}),0===w.size?(v.textContent=n,b.appendChild(v)):w.forEach(t=>{var e=O.create("span",{className:"ftable-multiselect-tag",parent:b});O.create("span",{className:"ftable-multiselect-tag-text",textContent:u.get(t)||t,parent:e}),O.create("span",{className:"ftable-multiselect-tag-remove",innerHTML:"×",parent:e}).addEventListener("click",e=>{e.stopPropagation(),w.delete(t);e=S.get(t);e&&(e.checked=!1),E(),d&&d(f)})}),d&&d(f)}),x=()=>{g.focus(),y&&(y.remove(),y=null),C&&(C.remove(),C=null),m._cleanupHandlers&&(m._cleanupHandlers(),m._cleanupHandlers=null)},A=()=>{var e,t,s;y&&(e=g.getBoundingClientRect(),s=window.pageYOffset||document.documentElement.scrollTop,t=window.pageXOffset||document.documentElement.scrollLeft,t=e.left+t,s=e.bottom+s+4,Object.assign(y.style,{position:"absolute",left:t+"px",top:s+"px",width:e.width+"px",minWidth:"fit-content",boxSizing:"border-box",zIndex:"10000"}),(s=y.getBoundingClientRect()).right>window.innerWidth)&&(t=Math.max(10,window.innerWidth-s.width-10),y.style.left=t+"px")},N=(e="")=>{if(y){Array.from(y.querySelectorAll(".ftable-multiselect-option, .ftable-multiselect-optgroup")).forEach(e=>e.remove());let t=e.toLowerCase();e=e?p.filter(e=>e.optText.toLowerCase().includes(t)):p;let i=new Set,o=null;e.forEach(({optValue:t,optText:e,groupLabel:s})=>{s&&s!==i[i.size-1]?i.has(s)||(i.add(s),o=O.create("div",{className:"ftable-multiselect-optgroup",textContent:s,parent:y})):s||(o=null);s=O.create("div",{className:"ftable-multiselect-option",parent:y});let a=O.create("input",{type:"checkbox",className:"ftable-multiselect-checkbox",checked:w.has(t.toString()),parent:s});S.set(t.toString(),a),O.create("label",{className:"ftable-multiselect-label",textContent:e,parent:s}),s.addEventListener("click",e=>{e.stopPropagation();e=t.toString();w.has(e)?(w.delete(e),a.checked=!1):(w.add(e),a.checked=!0),E()})})}},L=a=>{if(a&&a.stopPropagation(),y)x();else{if(document.querySelectorAll(".ftable-multiselect-dropdown").forEach(e=>e.remove()),document.querySelectorAll(".ftable-multiselect-overlay").forEach(e=>e.remove()),C=O.create("div",{className:"ftable-multiselect-overlay",parent:document.body}),y=O.create("div",{className:"ftable-multiselect-dropdown",parent:document.body,attributes:{tabindex:"-1",role:"listbox","aria-multiselectable":"true"}}),c){a=O.create("div",{className:"ftable-multiselect-livesearch-wrap",parent:y});let e=O.create("input",{type:"search",className:"ftable-multiselect-livesearch",placeholder:"Search...",parent:a,attributes:{autocomplete:"off"}});e.addEventListener("input",()=>{N(e.value)}),e.addEventListener("click",e=>e.stopPropagation()),setTimeout(()=>e.focus(),0)}N(),A(),c||y.focus(),y.addEventListener("keydown",e=>{var t,s;"Escape"===e.key?x():"ArrowDown"===e.key||"ArrowUp"===e.key?(e.preventDefault(),s=(t=Array.from(y.querySelectorAll(".ftable-multiselect-checkbox"))).indexOf(document.activeElement),t["ArrowDown"===e.key?s<t.length-1?s+1:0:0<s?s-1:t.length-1]?.focus()):" "!==e.key&&"Enter"!==e.key||(e.preventDefault(),document.activeElement.classList.contains("ftable-multiselect-checkbox")&&document.activeElement.click())}),C.addEventListener("click",e=>{e.target===C&&x()});let e=()=>A(),t=e=>{y&&y.contains(e.target)||A()},s=new ResizeObserver(()=>A());window.addEventListener("scroll",t,!0),window.addEventListener("resize",e),s.observe(b),m._cleanupHandlers=()=>{window.removeEventListener("scroll",t,!0),window.removeEventListener("resize",e),s.disconnect()}}},T=(g.addEventListener("click",L),g.querySelector(".ftable-multiselect-toggle").addEventListener("click",L),g.addEventListener("keydown",e=>{"ArrowDown"!==e.key&&"Enter"!==e.key||(e.preventDefault(),L())}),m.resetMultiSelect=()=>{w.clear(),S.forEach(e=>{e.checked=!1}),x(),E()},new MutationObserver(e=>{e.forEach(e=>{e.removedNodes.forEach(e=>{(e===m||e.contains&&e.contains(m))&&(x(),T.disconnect())})})}));return setTimeout(()=>{m.parentNode&&T.observe(m.parentNode,{childList:!0,subtree:!0})},0),E(),m}createRadioGroup(o,r,l){let n=O.create("div",{className:"ftable-radio-group"});return r.options&&(Array.isArray(r.options)?r.options:"object"==typeof r.options?Object.entries(r.options).map(([e,t])=>({Value:e,DisplayText:t})):[]).forEach((e,t)=>{var s=O.create("div",{className:"ftable-radio-wrapper",parent:n}),t=o+"_"+t,a={},i=(r.inputAttributes&&(i=this.parseInputAttributes(r.inputAttributes),Object.assign(a,i)),void 0!==e.Value?e.Value:void 0!==e.value?e.value:e);O.create("input",{attributes:a,type:"radio",name:o,id:t,value:i,className:r.inputClass||null,checked:i==l,parent:s}),O.create("label",{attributes:{for:t},textContent:e.DisplayText||e.text||e,parent:s})}),n}createCheckbox(e,t,s){var a=O.create("div",{className:"ftable-yesno-check-wrapper"}),s=[1,"1",!0,"true"].includes(s);let i=this.options.messages.no,o=this.options.messages.yes;return t.values&&"object"==typeof t.values&&(void 0!==t.values[0]&&(i=t.values[0]),void 0!==t.values[1])&&(o=t.values[1]),O.create("input",{className:["ftable-yesno-check-input",t.inputClass||""].filter(Boolean).join(" "),type:"checkbox",name:e,id:"Edit-"+e,value:"1",parent:a}).checked=s,t.label?O.create("label",{className:"ftable-yesno-check-fixedlabel",attributes:{for:"Edit-"+e},textContent:t.label,parent:a}):O.create("label",{className:"ftable-yesno-check-text",attributes:{for:"Edit-"+e,"data-yes":o,"data-no":i},parent:a}),a}populateSelectOptions(o,e,r){if(o.innerHTML="",Array.isArray(e)){let s=new Map,a=[],i=(e.forEach(e=>{var t=e.Group||e.group||null;(t?(s.has(t)||s.set(t,[]),s.get(t)):a).push(e)}),(e,t)=>{var s=void 0!==e.Value?e.Value:void 0!==e.value?e.value:e;let a=O.create("option",{value:s,textContent:e.DisplayText||e.text||e,selected:s==r,parent:t});e.Data&&"object"==typeof e.Data&&Object.entries(e.Data).forEach(([e,t])=>{a.setAttribute("data-"+e,t)})});a.forEach(e=>i(e,o)),s.forEach((e,t)=>{let s=O.create("optgroup",{attributes:{label:t},parent:o});e.forEach(e=>i(e,s))})}else"object"==typeof e&&Object.entries(e).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,selected:e==r,parent:o})})}createFileInput(e,t,s){var a,i={};let o=e;return t.inputAttributes&&(a=this.parseInputAttributes(t.inputAttributes),Object.assign(i,a),void 0!==a.multiple&&!1!==a.multiple)&&(o=e+"[]"),O.create("input",{type:"file",id:"Edit-"+e,name:o,className:t.inputClass||null,attributes:i})}}class l extends class{constructor(){this.events={}}on(e,t){return this.events[e]||(this.events[e]=[]),this.events[e].push(t),this}once(t,s){let a=(...e)=>{this.off(t,a),s.apply(this,e)};return a.fn=s,this.on(t,a),this}emit(e,t={}){return this.events[e]&&this.events[e].forEach(e=>e(t)),this}off(e,t){return this.events[e]&&(this.events[e]=this.events[e].filter(e=>e!==t)),this}}{constructor(e,t={}){if(super(),this.element="string"==typeof e?document.querySelector(e):e,this.element){if(this.element.ftableInstance)return this.element.ftableInstance;this.options=this.mergeOptions(t),this.verifyOptions(),this.logger=new a(this.options.logLevel),this.userPrefs=new i("",this.options.saveUserPreferencesMethod),this.formBuilder=new r(this.options,this),this.state={records:[],totalRecordCount:0,currentPage:1,isLoading:!1,selectedRecords:new Set,sorting:[],searchQueries:{}},this.elements={},this.modals={},this.searchTimeout=null,this.lastSortEvent=null,this._recalculatedOnce=!1,this.shiftKeyDown=!1,this.lastSelectedRow=null,(this.element.ftableInstance=this).init()}}mergeOptions(e){var t={tableId:void 0,logLevel:a.LOG_LEVELS.WARN,actions:{},fields:{},forcePost:!0,closeOnOverlayClick:!0,animationsEnabled:!0,loadingAnimationDelay:1e3,defaultDateLocale:"",defaultDateFormat:"Y-m-d",saveUserPreferences:!0,saveUserPreferencesMethod:"localStorage",defaultSorting:"",tableReset:!1,paging:!1,pageList:"normal",pageSize:10,pageSizes:[10,25,50,100,250,500],gotoPageArea:"combobox",sorting:!1,multiSorting:!1,multiSortingCtrlKey:!0,selecting:!1,multiselect:!1,openChildAsAccordion:!1,toolbarsearch:!1,toolbarreset:!0,searchDebounceMs:300,listCache:3e4,messages:{...s}};return this.deepMerge(t,e)}deepMerge(e,t){var s,a={...e};for(s in t)t[s]&&"object"==typeof t[s]&&!Array.isArray(t[s])?a[s]=this.deepMerge(a[s]||{},t[s]):a[s]=t[s];return a}verifyOptions(){this.options.pageSize&&!this.options.pageSizes.includes(this.options.pageSize)&&(this.options.pageSize=this.options.pageSizes[0])}static setMessages(e){Object.assign(s,e)}init(){this.processFieldDefinitions(),this.createMainStructure(),this.setupFTableUserPreferences(),this.createTable(),this.createModals(),this.options.paging&&this.createPagingUI(),this.resolveAsyncFieldOptions().then(()=>{setTimeout(()=>{this.refreshDisplayValues()},0)}).catch(console.error),this.bindEvents(),this.updateSortingHeaders(),this.renderSortingInfo(),this.initColumnWidths()}initColumnWidths(){var e=this.columnList.filter(e=>{e=this.options.fields[e];return!e.action&&"hidden"!==e.visibility&&"separator"!==e.visibility});let t=e.length;e.forEach(e=>{e=this.options.fields[e];e.width=e.width||100/t+"%"})}normalizeColumnWidths(){var e=this.elements.mainContainer,t=this.columnList.map(e=>({th:this.elements.table.querySelector(`[data-field-name="${e}"]`),field:this.options.fields[e]})).filter(e=>e.th&&!e.field.action&&"hidden"!==e.field.visibility&&"separator"!==e.field.visibility);if(0!==t.length){let s=e.offsetWidth,a=0;t.forEach(e=>{var t=e.th.offsetWidth/s*100;e.field.width=t+"%",e.th.style.width=e.field.width,a+=t})}}parseDefaultSorting(e){let o=[];return e&&"string"==typeof e&&e.split(",").forEach(s=>{s=s.trim();if(s){var a=s.toUpperCase().indexOf(" DESC"),i=s.toUpperCase().indexOf(" ASC");let e="ASC",t=s;e=0<a?(t=s.slice(0,a).trim(),"DESC"):(t=(0<i?s.slice(0,i):s).trim(),"ASC");a=this.options.fields[t];a&&!1!==a.sorting&&o.push({fieldName:t,direction:e})}}),o}createPagingUI(){this.elements.bottomPanel=O.create("div",{className:"ftable-bottom-panel",parent:this.elements.mainContainer}),this.elements.leftArea=O.create("div",{className:"ftable-left-area",parent:this.elements.bottomPanel}),this.elements.rightArea=O.create("div",{className:"ftable-right-area",parent:this.elements.bottomPanel}),this.elements.pagingListArea=O.create("div",{className:"ftable-page-list",parent:this.elements.leftArea}),this.elements.pagingGotoArea=O.create("div",{className:"ftable-page-goto",parent:this.elements.leftArea}),this.elements.pageInfoSpan=O.create("div",{className:"ftable-page-info",parent:this.elements.rightArea}),!1!==this.options.pageSizeChangeArea&&this.createPageSizeSelector()}createPageSizeSelector(){var e=O.create("span",{className:"ftable-page-size-change",parent:this.elements.leftArea});O.create("span",{textContent:this.options.messages.pageSizeChangeLabel,parent:e});let s=O.create("select",{className:"ftable-page-size-select",parent:e});(this.options.pageSizes||[10,25,50,100,250,500]).forEach(e=>{var t=O.create("option",{attributes:{value:e},textContent:e.toString(),parent:s});e===this.state.pageSize&&(t.selected=!0)}),s.addEventListener("change",e=>{this.changePageSize(parseInt(e.target.value))})}processFieldDefinitions(){this.fieldList=Object.keys(this.options.fields),this.fieldList.forEach(e=>{var e=this.options.fields[e],t=!0===e.key;!!e.action?(e.list=!0,e.create=!1,e.edit=!1,e.sorting=!1,e.searchable=!1):t?(void 0!==e.create&&e.create||(e.create=!0,e.type="hidden"),void 0!==e.edit&&e.edit||(e.edit=!0,e.type="hidden")):(e.create=e.create??!0,e.edit=e.edit??!0,e.list=e.list??!0,e.sorting=e.sorting??!0),e.visibility=e.visibility??"visible"}),this.columnList=this.fieldList.filter(e=>!1!==this.options.fields[e].list),this._userPlacedActions=new Set(this.fieldList.filter(e=>this.options.fields[e].action).map(e=>this.options.fields[e].action)),this.keyField=this.fieldList.find(e=>!0===this.options.fields[e].key),this.keyField||this.logger.info("No key field defined")}async resolveAsyncFieldOptions(){var e=this.columnList.map(async t=>{var e=this.options.fields[t];if(!e.action){e=this.formBuilder.originalFieldOptions.get(t);if(this.formBuilder.shouldResolveOptions(e))try{var s=this.formBuilder.generateOptionsCacheKey("table",{});this.formBuilder.resolvedFieldOptions.get(t)?.[s]||await this.formBuilder.getFieldOptions(t,"table")}catch(e){console.error(`Failed to resolve table options for ${t}:`,e)}}});await Promise.all(e)}async refreshDisplayValues(){var e=this.elements.tableBody.querySelectorAll(".ftable-data-row");if(0!==e.length)for(var t of e)for(var s of this.columnList){var a,i,o=this.options.fields[s];!o.action&&o.options&&(a=t.querySelector(`td[data-field-name="${s}"]`))&&(i=this.formBuilder.generateOptionsCacheKey("table",{}),i=this.formBuilder.resolvedFieldOptions.get(s)?.[i],s=this.getDisplayText(t.recordData,s,i),a.innerHTML=o.listEscapeHTML?O.escapeHtml(s):s)}}createMainStructure(){this.elements.mainContainer=O.create("div",{className:"ftable-main-container",parent:this.element}),this.options.title&&(this.elements.titleDiv=O.create("div",{className:"ftable-title",parent:this.elements.mainContainer}),O.create("div",{className:"ftable-title-text",innerHTML:this.options.title,parent:this.elements.titleDiv})),this.elements.toolbarDiv=O.create("div",{className:"ftable-toolbar",parent:this.elements.titleDiv||this.elements.mainContainer}),this.elements.tableDiv=O.create("div",{className:"ftable-table-div",parent:this.elements.mainContainer})}createTable(){this.elements.table=O.create("table",{className:"ftable",parent:this.elements.tableDiv}),this.options.tableId&&(this.elements.table.id=this.options.tableId),this.createTableHeader(),this.createTableBody(),this.addNoDataRow()}createTableHeader(){var e=O.create("thead",{parent:this.elements.table});let o=O.create("tr",{parent:e});if(this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")){var t=O.create("th",{className:"ftable-command-column-header ftable-column-header-select",parent:o});if(this.options.multiselect){let e=O.create("input",{attributes:{type:"checkbox"},parent:t});e.addEventListener("change",()=>{this.toggleSelectAll(e.checked)})}}this.columnList.forEach(t=>{var s=this.options.fields[t];if(s.action){var a={select:"ftable-column-header-select",update:"ftable-column-header-edit",clone:"ftable-column-header-clone",delete:"ftable-column-header-delete"};let t=O.create("th",{className:"ftable-command-column-header "+(a[s.action]||""),parent:o});if(s.title&&(t.textContent=s.title),"select"===s.action&&this.options.selecting&&this.options.selectingCheckboxes&&this.options.multiselect){let e=O.create("input",{attributes:{type:"checkbox"},parent:t});e.addEventListener("change",()=>{this.toggleSelectAll(e.checked)})}void(s.width&&(t.style.width=s.width))}else{let e=O.create("th",{className:`ftable-column-header ${s.listClass||""} `+(s.listClassHeader||""),attributes:{"data-field-name":t},parent:o});s.width&&(e.style.width=s.width);var a=O.create("div",{className:"ftable-column-header-container",parent:e}),i=(s.tooltip&&a.setAttribute("title",s.tooltip),O.create("span",{className:"ftable-column-header-text",innerHTML:s.title||t,parent:a}));this.options.sorting&&!1!==s.sorting&&(O.addClass(i,"ftable-sortable-text"),O.addClass(e,"ftable-column-header-sortable"),e.addEventListener("click",e=>{e.preventDefault();e=e.ctrlKey||e.metaKey;this.sortByColumn(t,e)})),!1!==this.options.columnResizable&&!1!==s.columnResizable&&this.makeColumnResizable(e,a),"hidden"!==s.visibility&&"separator"!==s.visibility||O.hide(e)}}),this.options.actions.updateAction&&!this._userPlacedActions.has("update")&&O.create("th",{className:"ftable-command-column-header ftable-column-header-edit",parent:o}),this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")&&O.create("th",{className:"ftable-command-column-header ftable-column-header-clone",parent:o}),this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")&&O.create("th",{className:"ftable-command-column-header ftable-column-header-delete",parent:o}),this.options.toolbarsearch&&this.createSearchHeaderRow(e).catch(e=>{console.error("Failed to create search header row:",e)})}async createSearchHeaderRow(e){var t,s=O.create("tr",{className:"ftable-toolbarsearch-row",parent:e});this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")&&O.create("th",{className:"ftable-toolbarsearch-column-header",parent:s});for(let i of this.columnList){var o=this.options.fields[i];if(o.action)O.create("th",{className:"ftable-toolbarsearch-column-header ftable-command-column-header",parent:s});else{var r=!1!==o.searchable,l=O.create("th",{className:"ftable-toolbarsearch-column-header",parent:s});if(r){r=O.create("div",{className:"ftable-column-header-container",parent:l});let s,a="text";o.searchType?a=o.searchType:!o.type&&o.options?a="select":o.type&&(a=o.type);var n="ftable-toolbarsearch-"+i;switch(a){case"date":case"datetime":case"datetime-local":if("undefined"!=typeof FDatepicker){let e=o.searchDateFormat||o.dateFormat||this.options.defaultDateFormat;var c=document.createElement("div"),d=O.create("input",{className:"ftable-toolbarsearch-extra",type:"hidden",id:"ftable-toolbarsearch-extra-"+i,attributes:{"data-field-name":i}});let t=O.create("input",{className:"ftable-toolbarsearch",id:"ftable-toolbarsearch-"+i,type:"text",placeholder:o.searchPlaceholder||o.placeholder||"",readOnly:!0});switch(c.appendChild(d),c.appendChild(t),a){case"date":setTimeout(()=>{new FDatepicker(t,{format:e,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d",autoClose:!0})},0);break;case"datetime":case"datetime-local":setTimeout(()=>{new FDatepicker(t,{format:e,timepicker:!0,altField:"ftable-toolbarsearch-extra-"+i,altFormat:"Y-m-d H:i:00"})},0)}s=c}else s=O.create("input",{className:"ftable-toolbarsearch",type:a,id:n,attributes:{"data-field-name":i}});break;case"checkbox":o.values||(o.values={0:this.options.messages.no,1:this.options.messages.yes}),s=await this.createSelectForSearch(i,o,!0);break;case"select":s=o.options?await this.createSelectForSearch(i,o,!1):O.create("input",{className:"ftable-toolbarsearch",type:"text",id:n,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}});break;case"datalist":s=await this.createDatalistForSearch(i,o);break;default:s=O.create("input",{className:"ftable-toolbarsearch",type:"text",id:n,placeholder:o.searchPlaceholder||o.placeholder||"Search...",attributes:{"data-field-name":i}})}if(s){r.appendChild(s),s.datalistElement&&s.datalistElement instanceof Node&&r.appendChild(s.datalistElement);let e=s;"SELECT"===(e=s.classList&&s.classList.contains("ftable-multiselect-container")&&s.hiddenSelect?s.hiddenSelect:e).tagName?e.addEventListener("change",e=>{this.handleSearchInputChange(e)}):e.addEventListener("input",e=>{this.handleSearchInputChange(e)})}}"hidden"!==o.visibility&&"separator"!==o.visibility||O.hide(l)}}this.options.toolbarsearch&&this.options.toolbarreset&&(e=O.create("th",{className:"ftable-toolbarsearch-column-header ftable-toolbarsearch-reset",parent:s}),0<(t=(this.options.actions.updateAction&&!this._userPlacedActions.has("update")?1:0)+(this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")?1:0)+(this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")?1:0))?e.colSpan=t:O.addClass(e,"ftable-command-column-header"),O.create("button",{className:"ftable-toolbarsearch-reset-button",textContent:this.options.messages.resetSearch,attributes:{id:"ftable-toolbarsearch-reset-button"},parent:e}).addEventListener("click",()=>this.resetSearch()))}async createSelectForSearch(e,t,s){var a,i="ftable-toolbarsearch-"+e,o={};let r=e,l=!1;t.searchAttributes?(a=this.formBuilder.parseInputAttributes(t.searchAttributes),Object.assign(o,a),l=void 0!==a.multiple&&!1!==a.multiple):t.inputAttributes&&(a=this.formBuilder.parseInputAttributes(t.inputAttributes),Object.assign(o,a),l=void 0!==a.multiple&&!1!==a.multiple),l&&(r=e+"[]"),o["data-field-name"]=r;let n;if(s&&t.values?n=Object.entries(t.values).map(([e,t])=>({Value:e,DisplayText:t})):t.options&&(n=await this.formBuilder.getFieldOptions(e)),l)return this.createCustomMultiSelectForSearch(i,e,t,n,o);let c=O.create("select",{attributes:o,id:i,className:"ftable-toolbarsearch"});return 0<n?.length&&(""===n[0].Value||""===n[0].value||""===n[0]||""===n[0].DisplayText&&null==n[0].Value)||O.create("option",{value:"",innerHTML:"&nbsp;",parent:c}),n&&Array.isArray(n)?n.forEach(e=>{O.create("option",{value:void 0!==e.Value?e.Value:void 0!==e.value?e.value:e,textContent:e.DisplayText||e.text||e,parent:c})}):n&&"object"==typeof n&&Object.entries(n).forEach(([e,t])=>{O.create("option",{value:e,textContent:t,parent:c})}),c}createCustomMultiSelectForSearch(e,t,s,a,i){var o=s.livesearch??!1;return this.formBuilder._buildCustomMultiSelect({hiddenSelectId:e,hiddenSelectName:i["data-field-name"]||e,extraClasses:"ftable-multiselect-search ftable-toolbarsearch",containerDataFieldName:i["data-field-name"]||e,hiddenSelectAttributes:i,optionsSource:a,initialValues:[],placeholderText:s.searchPlaceholder||s.placeholder||this.options.messages?.multiSelectPlaceholder||"Click to select options...",livesearch:o,onChangeExtra:e=>{e.dispatchEvent(new Event("change",{bubbles:!0}))},buildHiddenSelectOnUpdate:!1})}async createDatalistForSearch(e,t){var s="ftable-toolbarsearch-"+e,a=s+"-datalist",i=O.create("datalist",{attributes:{id:a}}),s=O.create("input",{className:"ftable-toolbarsearch",type:"search",id:s,placeholder:t.searchPlaceholder||t.placeholder||"Type or select...",attributes:{"data-field-name":e,list:a}});s.datalistElement=i;let o;return t.searchOptions?o=t.searchOptions:t.options&&(o=await this.formBuilder.getFieldOptions(e,"table")),o&&this.formBuilder.populateDatalistOptions(i,o),s}handleSearchInputChange(e){var e=e.target,t=e.getAttribute("data-field-name");let s;s=e.multiple&&e.options?Array.from(e.selectedOptions).map(e=>e.value).filter(e=>""!==e.trim()).map(e=>e.trim()):e.value.trim(),this.state.currentPage=1,Array.isArray(s)&&0<s.length||!Array.isArray(s)&&s?this.state.searchQueries[t]=s:delete this.state.searchQueries[t],clearTimeout(this.searchTimeout),this.searchTimeout=setTimeout(()=>{this.load()},this.options.searchDebounceMs)}resetSearch(){this.state.searchQueries={},this.elements.table.querySelectorAll(".ftable-toolbarsearch").forEach(e=>{"SELECT"===e.tagName?e.selectedIndex=0:e.value=""}),this.elements.table.querySelectorAll(".ftable-multiselect-container").forEach(e=>{"function"==typeof e.resetMultiSelect&&e.resetMultiSelect()}),this.load()}getNextResizableHeader(t){var s=Array.from(this.elements.table.querySelectorAll("thead th.ftable-column-header-resizable"));for(let e=s.indexOf(t)+1;e<s.length;e++)if(null!==s[e].offsetParent)return s[e];return null}makeColumnResizable(a,e){O.addClass(a,"ftable-column-header-resizable"),this.elements.resizeBar||(this.elements.resizeBar=O.create("div",{className:"ftable-column-resize-bar",parent:this.elements.mainContainer}),O.hide(this.elements.resizeBar));e=O.create("div",{className:"ftable-column-resize-handler",parent:e});let i=!1,o=0,r=0,l,n=null,c=0,d=null,h=(e.addEventListener("mousedown",e=>{e.preventDefault(),e.stopPropagation(),i=!0,l=this.elements.mainContainer.getBoundingClientRect(),o=e.clientX,r=a.offsetWidth,(n=this.getNextResizableHeader(a))&&(c=n.offsetWidth,e=n.dataset.fieldName,d=this.options.fields[e]);e=a.getBoundingClientRect();this.elements.resizeBar.style.left=e.right-l.left+"px",this.elements.resizeBar.style.top=e.top-l.top+"px",this.elements.resizeBar.style.height=this.elements.table.offsetHeight+"px",O.show(this.elements.resizeBar),document.addEventListener("mousemove",h),document.addEventListener("mouseup",p)}),e=>{i&&(this.elements.resizeBar.style.left=e.clientX-l.left+"px")}),p=e=>{var t,s;i&&(i=!1,e=e.clientX-o,s=l.width,t=Math.max(50,r+e)/s*100,n&&(e=Math.max(50,c-e)/s*100,d.width=e.toFixed(2)+"%",n.style.width=d.width),(s=this.options.fields[a.dataset.fieldName]).width=t.toFixed(2)+"%",a.style.width=s.width,this.normalizeColumnWidths(),this.options.saveUserPreferences&&this.saveColumnSettings(),O.hide(this.elements.resizeBar),document.removeEventListener("mousemove",h),document.removeEventListener("mouseup",p))}}saveColumnSettings(){if(this.options.saveUserPreferences){let a={};this.columnList.forEach(e=>{var t,s=this.elements.table.querySelector(`[data-field-name="${e}"]`);s&&(t=this.options.fields[e],a[e]={width:s.style.width||t.width||"auto",visibility:t.visibility||"visible"})}),this.userPrefs.set("column-settings",JSON.stringify(a))}}saveState(){var e;this.options.saveUserPreferences&&(e={sorting:this.state.sorting,pageSize:this.state.pageSize},this.userPrefs.set("table-state",JSON.stringify(e)))}loadColumnSettings(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("column-settings");if(e)try{var t=JSON.parse(e);Object.entries(t).forEach(([e,t])=>{e=this.options.fields[e];e&&(t.width&&(e.width=t.width),t.visibility)&&(e.visibility=t.visibility)})}catch(e){this.logger.warn("Failed to load column settings:",e)}}}loadState(){if(this.options.saveUserPreferences){var e=this.userPrefs.get("table-state");if(e)try{var t=JSON.parse(e);Array.isArray(t.sorting)&&(this.state.sorting=t.sorting),t.pageSize&&this.options.pageSizes.includes(t.pageSize)&&(this.state.pageSize=t.pageSize)}catch(e){this.logger.warn("Failed to load table state:",e)}}}createTableBody(){this.elements.tableBody=O.create("tbody",{parent:this.elements.table})}addNoDataRow(){var e,t;this.elements.tableBody.querySelector(".ftable-no-data-row")||(e=O.create("tr",{className:"ftable-no-data-row",parent:this.elements.tableBody}),t=this.elements.table.querySelector("thead tr").children.length,O.create("td",{attributes:{colspan:t},textContent:this.options.messages.noDataAvailable,parent:e}))}removeNoDataRow(){var e=this.elements.tableBody.querySelector(".ftable-no-data-row");e&&e.remove()}createModals(){this.options.actions.createAction&&this.createAddRecordModal(),this.options.actions.updateAction&&this.createEditRecordModal(),this.options.actions.deleteAction&&this.createDeleteConfirmModal(),this.createErrorModal(),this.createInfoModal(),this.createLoadingModal(),Object.values(this.modals).forEach(e=>e.create())}createAddRecordModal(){this.modals.addRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.addNewRecord,className:"ftable-add-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.addRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveNewRecord()}]})}createEditRecordModal(){this.modals.editRecord=new o({parent:this.elements.mainContainer,title:this.options.messages.editRecord,className:"ftable-edit-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>{this.modals.editRecord.close(),this.emit("formClosed",{form:this.currentForm,formType:"edit",record:null}),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null}},{text:this.options.messages.save,className:"ftable-dialog-savebutton",onClick:()=>this.saveEditedRecord()}]})}createDeleteConfirmModal(){this.modals.deleteConfirm=new o({parent:this.elements.mainContainer,title:this.options.messages.areYouSure,className:"ftable-delete-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.cancel,className:"ftable-dialog-cancelbutton",onClick:()=>this.modals.deleteConfirm.close()},{text:this.options.messages.deleteText,className:"ftable-dialog-deletebutton",onClick:()=>this.confirmDelete()}]})}createErrorModal(){this.modals.error=new o({parent:this.elements.mainContainer,title:this.options.messages.error,className:"ftable-error-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.error.close()}]})}createInfoModal(){this.modals.info=new o({parent:this.elements.mainContainer,title:"",className:"ftable-info-modal",closeOnOverlayClick:this.options.closeOnOverlayClick,buttons:[{text:this.options.messages.close,className:"ftable-dialog-closebutton",onClick:()=>this.modals.info.close()}]})}createLoadingModal(){this.modals.loading=new o({parent:this.elements.mainContainer,title:"",className:"ftable-loading-modal",closeOnOverlayClick:!1,content:`<div class="ftable-loading-message">${this.options.messages.loadingMessage}</div>`})}bindEvents(){this.subscribeOptionEvents(),this.createCustomToolbarItems(),this.createToolbarButtons(),this.bindKeyboardEvents(),!1!==this.options.columnSelectable&&this.createColumnSelectionMenu()}subscribeOptionEvents(){["formCreated","formClosed","recordsLoaded","recordAdded","recordUpdated","recordDeleted","selectionChanged"].forEach(e=>{"function"==typeof this.options[e]&&this.on(e,this.options[e])})}createColumnSelectionMenu(){this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null,this.elements.table.querySelector("thead").addEventListener("contextmenu",e=>{e.preventDefault(),this.showColumnSelectionMenu(e)})}showColumnSelectionMenu(e){this.hideColumnSelectionMenu(),this.elements.columnSelectionOverlay=O.create("div",{className:"ftable-contextmenu-overlay",parent:document.body}),this.elements.columnSelectionMenu=O.create("div",{className:"ftable-column-selection-container",parent:document.body}),this.populateColumnSelectionMenu(),this.positionColumnSelectionMenu(e),this.elements.columnSelectionOverlay.addEventListener("click",e=>{e.target===this.elements.columnSelectionOverlay&&this.hideColumnSelectionMenu()}),this.elements.columnSelectionOverlay.addEventListener("contextmenu",e=>{e.preventDefault(),this.hideColumnSelectionMenu()})}populateColumnSelectionMenu(){let n=O.create("ul",{className:"ftable-column-select-list",parent:this.elements.columnSelectionMenu});this.columnList.forEach(t=>{var e=this.options.fields[t];if(!e.action){var s="hidden"!==e.visibility,a="fixed"===e.visibility,i="separator"===e.visibility,o=this.isFieldSorted(t),r=O.create("li",{className:"ftable-column-select-item",parent:n}),l=O.create("label",{className:"ftable-column-select-label",parent:r});if(!i){let e=O.create("input",{attributes:{type:"checkbox",id:"column-"+t},parent:l});e.checked=s,(a||o&&s)&&(e.disabled=!0,r.style.opacity="0.6"),e.disabled||e.addEventListener("change",()=>{this.setColumnVisibility(t,e.checked)})}a=O.create("span",{textContent:e.title||t,style:i?"font-weight: bold;":null,parent:l});o&&((s=O.create("span",{className:"ftable-sort-indicator",textContent:" (sorted)",parent:a})).style.fontSize="0.8em",s.style.color="#666")}})}positionColumnSelectionMenu(e){var t=this,s=e.pageX,e=e.pageY,e=(t.elements.columnSelectionMenu.style.position="absolute",t.elements.columnSelectionMenu.style.left=s+"px",t.elements.columnSelectionMenu.style.top=e+"px",t.elements.columnSelectionMenu.style.minWidth="100px",t.elements.columnSelectionMenu.style.boxSizing="border-box",t.elements.columnSelectionMenu.offsetWidth),a=window.innerWidth;a<s+e&&(s=Math.max(10,a-e-10),t.elements.columnSelectionMenu.style.left=s+"px")}hideColumnSelectionMenu(){this.elements.columnSelectionOverlay&&(this.elements.columnSelectionOverlay.remove(),this.elements.columnSelectionMenu.remove(),this.elements.columnSelectionOverlay=null,this.elements.columnSelectionMenu=null)}isFieldSorted(t){return this.state.sorting.some(e=>e.fieldName===t)}createToolbarButtons(){this.options.csvExport&&this.addToolbarButton({text:this.options.messages.csvExport,className:"ftable-toolbar-item-csv",onClick:()=>{var e=this.options.title?this.options.title.replace(/[^a-z0-9]/gi,"-").toLowerCase()+".csv":"table-export.csv";this.exportToCSV(e)}}),this.options.printTable&&this.addToolbarButton({text:this.options.messages.printTable,className:"ftable-toolbar-item-print",onClick:()=>{this.printTable()}}),this.options.actions.createAction&&this.addToolbarButton({text:this.options.messages.addNewRecord,className:"ftable-toolbar-item-add-record",addIconSpan:!0,onClick:()=>this.showAddRecordForm()})}addToolbarButton(t){var e,s=O.create("button",{className:"ftable-toolbar-item "+(t.className||""),id:t.id||null,title:t.title||null,textContent:t.text||null,type:"button",parent:this.elements.toolbarDiv});return t.addIconSpan&&(e=O.create("span",{className:"ftable-toolbar-item-icon "+(t.className||""),parent:s}),t.text)&&(s.textContent="",s.append(e,t.text||"")),t.icon&&(e=O.create("img",{attributes:{src:t.icon,alt:"",width:16,height:16,style:"margin-right: 6px; vertical-align: middle;"},parent:s}),t.text)&&(s.textContent="",s.append(e,t.text||"")),t.onClick&&s.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),t.onClick(e)}),t.disabled&&(s.disabled=!0),s}createCustomToolbarItems(){this.options.toolbar&&this.options.toolbar.items&&this.options.toolbar.items.forEach((e,t)=>{this.addToolbarButton({text:e.text||"",className:"ftable-toolbar-item-custom "+(e.buttonClass||""),id:e.buttonId||"ftable-toolbar-item-custom-id-"+t,title:e.tooltip||"",icon:e.icon||null,onClick:"function"==typeof e.click?e.click:null})})}bindKeyboardEvents(){this.options.selecting&&(document.addEventListener("keydown",e=>{"Shift"===e.key&&(this.shiftKeyDown=!0)}),document.addEventListener("keyup",e=>{"Shift"===e.key&&(this.shiftKeyDown=!1)}))}setupFTableUserPreferences(){var e;this.options.saveUserPreferences&&(e=this.userPrefs.generatePrefix(this.options.tableId||"",this.fieldList),this.userPrefs=new i(e,this.options.saveUserPreferencesMethod),this.loadState(),this.loadColumnSettings())}async load(e={}){if(!this.state.isLoading){this.state.isLoading=!0,this.showLoadingIndicator();try{var t={...e,...this.buildLoadParams()},s=await this.performLoad(t);this.processLoadedData(s),this.emit("recordsLoaded",{records:s.Records,serverResponse:s})}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Load failed: "+e.message)}finally{this.state.isLoading=!1,this.hideLoadingIndicator()}this.renderSortingInfo(),this.normalizeColumnWidths()}}buildLoadParams(){var e,t={};if(this.options.paging&&(this.state.pageSize||(this.state.pageSize=this.options.pageSize),t.jtStartIndex=(this.state.currentPage-1)*this.state.pageSize,t.jtPageSize=this.state.pageSize),this.options.sorting&&(0<this.state.sorting.length?t.jtSorting=this.state.sorting.map(e=>e.fieldName+" "+e.direction).join(", "):this.options.defaultSorting&&(t.jtSorting=this.parseDefaultSorting(this.options.defaultSorting).map(e=>e.fieldName+" "+e.direction).join(", "))),this.options.toolbarsearch&&0<Object.keys(this.state.searchQueries).length){let s=[],a=[];Object.entries(this.state.searchQueries).forEach(([t,e])=>{Array.isArray(e)?e.forEach(e=>{""!==e&&null!=e&&(s.push(e),a.push(t))}):""!==e&&(s.push(e),a.push(t))}),0<s.length&&(t.q=s,t.opt=a)}return"function"==typeof this.options.listQueryParams&&(e=this.options.listQueryParams(),Object.assign(t,e)),t}isCacheExpired(e,t){return!e||!e.timestamp||t<Date.now()-e.timestamp}async performLoad(e){var t=this.options.actions.listAction;if(this.options.listCache&&"string"==typeof t){var s=this.formBuilder.optionsCache.get(t,e);if(s&&!this.isCacheExpired(s,this.options.listCache))return s.data}let a;if("function"==typeof t)a=await t(e);else{if("string"!=typeof t)throw new Error("No valid listAction provided");a=this.options.forcePost?await n.post(t,e):await n.get(t,e)}if(a&&"OK"===a.Result)return this.options.listCache&&"string"==typeof t&&this.formBuilder.optionsCache.set(t,e,{data:a,timestamp:Date.now()}),a;throw new Error(a?.Message||"Invalid response from server")}processLoadedData(e){"OK"!==e.Result?this.showError(e.Message||"Unknown error occurred"):(this.state.records=e.Records||[],this.state.totalRecordCount=e.TotalRecordCount||this.state.records.length,this.renderTableData(),this.updatePagingInfo())}renderTableData(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>e.remove()),0===this.state.records.length?this.addNoDataRow():(this.removeNoDataRow(),this.state.records.forEach(e=>{e=this.createTableRow(e);this.elements.tableBody.appendChild(e)}),this.refreshRowStyles())}createTableRow(s){let a=O.create("tr",{className:"ftable-data-row",attributes:{"data-record-key":this.getKeyValue(s)}});return a.recordData=s,this.options.selecting&&this.options.selectingCheckboxes&&!this._userPlacedActions.has("select")&&this.addSelectingCell(a),this.columnList.forEach(e=>{var t=this.options.fields[e];if(t.action)switch(t.action){case"select":this.addSelectingCell(a);break;case"update":this.addEditCell(a);break;case"clone":this.addCloneCell(a);break;case"delete":this.addDeleteCell(a)}else this.addDataCell(a,s,e)}),this.options.actions.updateAction&&!this._userPlacedActions.has("update")&&this.addEditCell(a),this.options.actions.cloneAction&&!this._userPlacedActions.has("clone")&&this.addCloneCell(a),this.options.actions.deleteAction&&!this._userPlacedActions.has("delete")&&this.addDeleteCell(a),this.options.selecting&&this.makeRowSelectable(a),a}addSelectingCell(t){var e=O.create("td",{className:"ftable-command-column ftable-selecting-column",parent:t});O.create("input",{className:"norowselectonclick",attributes:{type:"checkbox"},parent:e}).addEventListener("change",e=>{this.toggleRowSelection(t)})}addDataCell(e,t,s){var a=this.options.fields[s],i=this.formBuilder.generateOptionsCacheKey("table",{}),i=this.formBuilder.resolvedFieldOptions.get(s)?.[i],t=this.getDisplayText(t,s,i),i=O.create("td",{className:`${a.listClass||""} `+(a.listClassEntry||""),innerHTML:a.listEscapeHTML?O.escapeHtml(t):t,attributes:{"data-field-name":s},parent:e});"fixed"===a.visibility||"hidden"!==a.visibility&&"separator"!==a.visibility||O.hide(i)}addEditCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-edit-command-button",attributes:{title:this.options.messages.editRecord},innerHTML:`<span>${this.options.messages.editRecord}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.editRecord(t)})}addCloneCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-clone-command-button",attributes:{title:this.options.messages.cloneRecord||"Clone"},innerHTML:`<span>${this.options.messages.cloneRecord||"Clone"}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.cloneRecord(t)})}addDeleteCell(t){var e=O.create("td",{className:"ftable-command-column",parent:t});O.create("button",{className:"ftable-command-button ftable-delete-command-button",attributes:{title:this.options.messages.deleteText},innerHTML:`<span>${this.options.messages.deleteText}</span>`,parent:e}).addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),this.deleteRecord(t)})}getDisplayText(e,t,s=null){var a=this.options.fields[t],i=e[t],s=s||a.options;let o=i;return"date"===a.type&&i&&(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),a.dateFormat||this.options.defaultDateFormat):this.formatDate(i,a.dateLocale||this.options.defaultDateLocale)),"datetime-local"!==a.type&&"datetime"!==a.type||!i||(o="undefined"!=typeof FDatepicker?FDatepicker.formatDate(this._parseDate(i),a.dateFormat||this.options.defaultDateFormat):this.formatDateTime(i,a.dateLocale||this.options.defaultDateLocale)),"checkbox"===a.type&&(o=this.getCheckboxText(t,i)),s&&(t=this.findOptionByValue(s,i),o=t?t.DisplayText||t.text||t:i),(o=a.display&&"function"==typeof a.display?a.display({record:e,value:i,displayValue:o}):o)||""}_parseDate(e){return e.includes("Date")?new Date(parseInt(e.substr(6),10)):10==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10)):19==e.length?new Date(parseInt(e.substr(0,4),10),parseInt(e.substr(5,2),10)-1,parseInt(e.substr(8,2),10),parseInt(e.substr(11,2),10),parseInt(e.substr(14,2),10),parseInt(e.substr(17,2),10)):new Date(e)}formatDate(e,t){if(!e)return"";var s=this._parseDate(e);try{return isNaN(s.getTime())?e:s.toLocaleDateString(t,{year:"numeric",month:"2-digit",day:"2-digit"})}catch{return e}}formatDateTime(e,t){if(!e)return"";var s=this._parseDate(e);try{return isNaN(s.getTime())?e:s.toLocaleString(t)}catch{return e}}getCheckboxText(e,t){e=this.options.fields[e];return e.values&&e.values[t]?e.values[t]:t?this.options.messages.yes:this.options.messages.no}findOptionByValue(e,t){return Array.isArray(e)?e.find(e=>(e.Value||e.value)===t||e===t):"object"==typeof e&&null!==e&&e.hasOwnProperty(t)?e[t]:null}refreshRowStyles(){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach((e,t)=>{t%2==0?O.addClass(e,"ftable-row-even"):O.removeClass(e,"ftable-row-even")})}getKeyValue(e){return this.keyField?e[this.keyField]:null}async showAddRecordForm(){var e=await this.formBuilder.createForm("create");this.modals.addRecord.setContent(e),this.modals.addRecord.show(),this.currentForm=e,this.emit("formCreated",{form:e,formType:"create",record:null})}async saveNewRecord(){if(this.currentForm)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performCreate(e);"OK"===t.Result?(this.clearListCache(),this.modals.addRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"create",record:null}),t.Message&&this.showInfo(t.Message),await this.load(),this.emit("recordAdded",{record:t.Record})):this.showError(t.Message||"Create failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Create failed: "+e.message)}}else this.currentForm.reportValidity()}async editRecord(e){var t=e.recordData,s=await this.formBuilder.createForm("edit",t);this.modals.editRecord.setContent(s),this.modals.editRecord.show(),this.currentForm=s,this.currentEditingRow=e,this.emit("formCreated",{form:s,formType:"edit",record:t})}async saveEditedRecord(){if(this.currentForm&&this.currentEditingRow)if(this.currentForm.checkValidity()){var e=this.getFormData(this.currentForm);try{var t=await this.performUpdate(e);"OK"===t.Result?(this.clearListCache(),this.modals.editRecord.close(),this.currentForm&&this.currentForm.parentNode&&this.currentForm.remove(),this.currentForm=null,this.emit("formClosed",{form:this.currentForm,formType:"edit",record:this.currentEditingRow.recordData}),this.updateRowData(this.currentEditingRow,t.Record||e),t.Message&&this.showInfo(t.Message),this.emit("recordUpdated",{record:t.Record||e,row:this.currentEditingRow})):this.showError(t.Message||"Update failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Update failed: "+e.message)}}else this.currentForm.reportValidity()}async cloneRecord(e){var e={...e.recordData},t=(this.keyField&&(e[this.keyField]=""),await this.formBuilder.createForm("create",e));this.modals.addRecord.options.content=t,this.modals.addRecord.setContent(t),this.modals.addRecord.show(),this.currentForm=t,this.emit("formCreated",{form:t,formType:"create",record:e})}async deleteRows(e){if(e.length){var t=this.options.messages.areYouSure;if(confirm(t)){var s,a=[];for(s of e)try{var i=await this.performDelete(s);a.push({key:s,success:"OK"===i.Result,result:i})}catch(e){a.push({key:s,success:!1,error:e.message})}a.filter(e=>e.success).forEach(({key:e})=>{e=this.getRowByKey(e);e&&this.removeRowFromTable(e)});t=a.filter(e=>!e.success).length;0<t&&this.showError(t+` of ${a.length} records could not be deleted`),this.refreshRowStyles(),this.updatePagingInfo()}}}deleteRecord(e){var t=e.recordData;let s=this.options.messages.deleteConfirmation;if("function"==typeof this.options.deleteConfirmation){t={row:e,record:t,deleteConfirmMessage:s,cancel:!1,cancelMessage:this.options.messages.cancel};if(this.options.deleteConfirmation(t),t.cancel)return void(t.cancelMessage&&this.showError(t.cancelMessage));s=t.deleteConfirmMessage}this.modals.deleteConfirm.setContent(`<p>${s}</p>`),this.modals.deleteConfirm.show(),this.currentDeletingRow=e}async confirmDelete(){if(this.currentDeletingRow){var e=this.getKeyValue(this.currentDeletingRow.recordData);try{var t=await this.performDelete(e);"OK"===t.Result?(this.clearListCache(),this.modals.deleteConfirm.close(),this.removeRowFromTable(this.currentDeletingRow),t.Message&&this.showInfo(t.Message),this.emit("recordDeleted",{record:this.currentDeletingRow.recordData})):this.showError(t.Message||"Delete failed")}catch(e){this.showError(this.options.messages.serverCommunicationError),this.logger.error("Delete failed: "+e.message)}}}async performCreate(e){var t=this.options.actions.createAction;if("function"==typeof t)return t(e);if("string"==typeof t)return n.post(t,e);throw new Error("No valid createAction provided")}async performUpdate(e){var t=this.options.actions.updateAction;if("function"==typeof t)return t(e);if("string"==typeof t)return n.post(t,e);throw new Error("No valid updateAction provided")}async performDelete(e){var t=this.options.actions.deleteAction;let s;if(s=null===e||"object"!=typeof e||Array.isArray(e)?{[this.keyField]:e}:e,"function"==typeof t)return t(s);if("string"==typeof t)return n.post(t,s);throw new Error("No valid deleteAction provided")}getFormData(e){var t,s,a,i={};for([t,s]of new FormData(e).entries())t.endsWith("[]")?(i[a=t.slice(0,-2)]||(i[a]=[]),i[a].push(s)):i.hasOwnProperty(t)?Array.isArray(i[t])?i[t].push(s):i[t]=[i[t],s]:i[t]=s;return i}updateRowData(i,e){i.recordData={...i.recordData,...e},Object.keys(e).forEach(e=>{var t,s,a=this.options.fields[e];a&&(t=i.querySelector(`td[data-field-name="${e}"]`))&&(s=this.formBuilder.generateOptionsCacheKey("table",{}),s=this.formBuilder.resolvedFieldOptions.get(e)?.[s],e=this.getDisplayText(i.recordData,e,s),t.innerHTML=a.listEscapeHTML?O.escapeHtml(e):e,t.className=(`${a.listClass||""} `+(a.listClassEntry||"")).trim())})}removeRowFromTable(e){e.remove(),0===this.elements.tableBody.querySelectorAll(".ftable-data-row").length&&this.addNoDataRow(),this.refreshRowStyles()}makeRowSelectable(t){!1!==this.options.selectOnRowClick&&t.addEventListener("click",e=>{["INPUT","BUTTON","SELECT","TEXTAREA","A"].includes(e.target.tagName)||e.target.classList.contains("norowselectonclick")||this.toggleRowSelection(t)})}toggleRowSelection(e){var t=e.classList.contains("ftable-row-selected");if(this.shiftKeyDown&&this.lastSelectedRow&&this.options.multiselect){this.clearAllSelections();var s=Array.from(this.elements.tableBody.querySelectorAll(".ftable-data-row")),a=s.indexOf(this.lastSelectedRow),i=s.indexOf(e),[i,o]=a<i?[a,i]:[i,a];for(let e=i;e<=o;e++)this.selectRow(s[e])}else this.options.multiselect||this.clearAllSelections(),t?this.deselectRow(e):this.selectRow(e);t&&!this.shiftKeyDown||(this.lastSelectedRow=e),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}selectRow(e){O.addClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!0),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.add(t)}deselectRow(e){O.removeClass(e,"ftable-row-selected");var t=e.querySelector('input[type="checkbox"]'),t=(t&&(t.checked=!1),this.getKeyValue(e.recordData));t&&this.state.selectedRecords.delete(t)}recalcColumnWidths(){this.columnList.forEach(e=>{var t=this.options.fields[e],e=this.elements.table.querySelector(`[data-field-name="${e}"]`);e&&t.width&&(e.style.width=t.width)}),this.elements.table.offsetHeight}recalcColumnWidthsOnce(){this._recalculatedOnce||(this.recalcColumnWidths(),this._recalculatedOnce=!0)}clearAllSelections(){this.elements.tableBody.querySelectorAll(".ftable-row-selected").forEach(e=>this.deselectRow(e))}toggleSelectAll(t){this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{t?this.selectRow(e):this.deselectRow(e)}),this.emit("selectionChanged",{selectedRows:this.getSelectedRows()})}getSelectedRows(){return Array.from(this.elements.tableBody.querySelectorAll(".ftable-row-selected"))}sortByColumn(s,a=!1){var i=this.options.fields[s];if(i&&!1!==i.sorting){i=this.state.sorting.findIndex(e=>e.fieldName===s);let e=!0,t="ASC";0<=i?"ASC"===this.state.sorting[i].direction?(t="DESC",this.state.sorting[i].direction=t):(this.state.sorting.splice(i,1),e=!1):this.state.sorting.push({fieldName:s,direction:t}),(!this.options.multiSorting||this.options.multiSortingCtrlKey&&!a)&&(this.state.sorting=e?[{fieldName:s,direction:t}]:[]),this.updateSortingHeaders(),this.load(),this.saveState()}}updateSortingHeaders(){this.elements.table.querySelectorAll(".ftable-column-header-sortable").forEach(e=>{O.removeClass(e,"ftable-column-header-sorted-asc ftable-column-header-sorted-desc")}),this.state.sorting.forEach(e=>{var t=this.elements.table.querySelector(`[data-field-name="${e.fieldName}"]`);t&&O.addClass(t,"ftable-column-header-sorted-"+e.direction.toLowerCase())})}updatePagingInfo(){var e,t,s;this.options.paging&&this.elements.pageInfoSpan&&(this.state.totalRecordCount<=0?(this.elements.pageInfoSpan.textContent="",this.elements.pagingListArea.innerHTML=""):(e=(this.state.currentPage-1)*this.state.pageSize+1,t=Math.min(this.state.currentPage*this.state.pageSize,this.state.totalRecordCount),s=this.options.messages.pagingInfo||"Showing {0}-{1} of {2}",this.elements.pageInfoSpan.textContent=s.replace(/\{0\}/g,e).replace(/\{1\}/g,t).replace(/\{2\}/g,this.state.totalRecordCount),this.createPageListNavigation(),this.createPageGotoNavigation()))}createPageListNavigation(){if(this.elements.pagingListArea){this.elements.pagingListArea.innerHTML="";var e=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(!(e<=1)){if(this.createPageButton("&laquo;",1,1===this.state.currentPage,"ftable-page-number-first"),this.createPageButton("&lsaquo;",this.state.currentPage-1,1===this.state.currentPage,"ftable-page-number-previous"),"normal"==this.options.pageList){var s=this.calculatePageNumbers(e);let t=0;s.forEach(e=>{1<e-t&&O.create("span",{className:"ftable-page-number-space",textContent:"...",parent:this.elements.pagingListArea}),this.createPageButton(e.toString(),e,!1,e===this.state.currentPage?"ftable-page-number ftable-page-number-active":"ftable-page-number"),t=e})}this.createPageButton("&rsaquo;",this.state.currentPage+1,this.state.currentPage>=e,"ftable-page-number-next"),this.createPageButton("&raquo;",e,this.state.currentPage>=e,"ftable-page-number-last")}}}createPageGotoNavigation(){if(this.options.paging&&"none"!==this.options.gotoPageArea){let s=Math.ceil(this.state.totalRecordCount/this.state.pageSize);if(s<=1)this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML="";else{this.elements.pagingGotoArea.style.display="inline-block",this.elements.pagingGotoArea.innerHTML="";O.create("span",{textContent:this.options.messages.gotoPageLabel+": ",parent:this.elements.pagingGotoArea});var e="ftable-goto-page-"+(this.options.tableId||"default");if("combobox"===this.options.gotoPageArea){this.elements.gotoPageSelect=O.create("select",{id:e,className:"ftable-page-goto-select",parent:this.elements.pagingGotoArea});for(let e=1;e<=s;e++)O.create("option",{attributes:{value:e},textContent:e,parent:this.elements.gotoPageSelect});this.elements.gotoPageSelect.value=this.state.currentPage,this.elements.gotoPageSelect.addEventListener("change",e=>{e=parseInt(e.target.value);1<=e&&e<=s&&this.changePage(e)})}else"textbox"===this.options.gotoPageArea&&(this.elements.gotoPageInput=O.create("input",{attributes:{type:"number",id:e,min:"1",max:s,value:this.state.currentPage,className:"ftable-page-goto-input",style:"width: 65px; margin-left: 4px;"},parent:this.elements.pagingGotoArea}),this.elements.gotoPageInput.addEventListener("change",e=>{var t=parseInt(e.target.value);1<=t&&t<=s?this.changePage(t):e.target.value=this.state.currentPage}))}}else this.elements.pagingGotoArea.style.display="none",this.elements.pagingGotoArea.innerHTML=""}createPageButton(e,t,s,a){a=O.create("span",{className:a+(s?" ftable-page-number-disabled":""),innerHTML:e,parent:this.elements.pagingListArea});s||(a.style.cursor="pointer",a.addEventListener("click",e=>{e.preventDefault(),this.changePage(t)}))}calculatePageNumbers(t){if(t<=7)return Array.from({length:t},(e,t)=>t+1);var s=this.state.currentPage,a=new Set([1,2,t-1,t]);for(let e=Math.max(1,s-1);e<=Math.min(t,s+1);e++)a.add(e);return Array.from(a).sort((e,t)=>e-t)}changePage(e){var t=Math.ceil(this.state.totalRecordCount/this.state.pageSize);(e=Math.max(1,Math.min(e,t)))!==this.state.currentPage&&(this.state.currentPage=e,this.load())}changePageSize(e){this.state.pageSize=e,this.state.currentPage=1,this.load(),this.saveState()}showLoadingIndicator(){0===this.options.loadingAnimationDelay?this.modals.loading&&this.modals.loading.show():this.loadingTimeout=setTimeout(()=>{this.modals.loading&&this.modals.loading.show(),this.loadingShownAt=Date.now()},this.options.loadingAnimationDelay||500)}hideLoadingIndicator(){this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null);var e=this.loadingShownAt?Date.now()-this.loadingShownAt:0;this.modals.loading&&(e<200?setTimeout(()=>{this.modals.loading.hide()},200-e):this.modals.loading.hide()),this.loadingShownAt=null}showError(e){this.modals.error?(this.modals.error.setContent(`<p>${e}</p>`),this.modals.error.show()):alert(e)}showInfo(e){this.modals.info?(this.modals.info.setContent(`<p>${e}</p>`),this.modals.info.show()):alert(e)}reload(e=!1){return this.clearListCache(),e?this.preservedSelections=new Set(this.state.selectedRecords):this.state.selectedRecords.clear(),this.load().then(()=>(e&&this.preservedSelections&&(this.restoreSelections(),this.preservedSelections=null),this))}clearListCache(){this.options.actions.listAction&&"string"==typeof this.options.actions.listAction&&this.formBuilder.optionsCache.clear(this.options.actions.listAction)}restoreSelections(){if(this.preservedSelections)return this.elements.tableBody.querySelectorAll(".ftable-data-row").forEach(e=>{var t=this.getKeyValue(e.recordData);t&&this.preservedSelections.has(t)&&this.selectRow(e)}),this}getRowByKey(e){return this.elements.tableBody.querySelector(`[data-record-key="${e}"]`)}destroy(){this.element&&this.element.ftableInstance&&(this.element.ftableInstance=null),Object.values(this.modals).forEach(e=>e.destroy()),this.elements.mainContainer&&this.elements.mainContainer.remove(),this.searchTimeout&&clearTimeout(this.searchTimeout),this.loadingTimeout&&clearTimeout(this.loadingTimeout),window.removeEventListener("resize",this.handleResize),this.options=null,this.state=null,this.elements=null,this.formBuilder=null,this.modals=null}setOption(e,t){return this.options[e]=t,this}getState(){return{...this.state}}addFilter(t,e,s="equals"){return this.state.filters||(this.state.filters=[]),this.state.filters=this.state.filters.filter(e=>e.fieldName!==t),null!=e&&""!==e&&this.state.filters.push({fieldName:t,value:e,operator:s}),this}clearFilters(){return this.state.filters=[],this}exportToCSV(e="table-data.csv"){var t,s,a,i=this.elements.table.cloneNode(!0),o=[],r=e=>`"${String(e||"").replace(/"/g,'""')}"`,l=[];for(t of i.querySelectorAll("thead th"))t.classList.contains("ftable-command-column-header")||t.classList.contains("ftable-toolbarsearch-column-header")||"none"===t.style.display||(s=t.textContent.trim(),l.push(r(s)));o.push(l.join(","));for(a of i.querySelectorAll("tbody tr"))if("none"!==a.style.display){var n,c,d,h=[];let e=!1;for(n of a.querySelectorAll("td"))n.classList.contains("ftable-command-column")||"none"===n.style.display||(n.querySelector("img, button, input, select")&&(n.innerHTML=n.textContent),c=n.innerHTML.replace(/<br\s*\/?>/gi,"\n"),(d=document.createElement("div")).innerHTML=c,h.push(r(d.textContent||"")),e=!0);e&&o.push(h.join(","))}var i=o.join("\n"),i=new Blob(["\ufeff"+i],{type:"text/csv;charset=utf-8;"}),p=document.createElement("a");p.href=URL.createObjectURL(i),p.download=e,p.click(),p.remove()}printTable(){var e=window.open("","_blank","width=800,height=600"),t=this.elements.table.outerHTML,t=`
2
2
  <!DOCTYPE html>
3
3
  <html>
4
4
  <head>
@@ -32,4 +32,4 @@
32
32
  </script>
33
33
  </body>
34
34
  </html>
35
- `;e.document.write(t),e.document.close()}async bulkDelete(e="Delete selected records?"){var t=this.getSelectedRows();if(0===t.length)this.showError("No records selected");else if(confirm(e)){var s,a=[];for(s of t.map(e=>this.getKeyValue(e.recordData)))try{var i=await this.performDelete(s);a.push({key:s,success:"OK"===i.Result,result:i})}catch(e){a.push({key:s,success:!1,error:e.message})}a.filter(e=>e.success).forEach(({key:e})=>{e=this.getRowByKey(e);e&&this.removeRowFromTable(e)});e=a.filter(e=>!e.success).length;0<e&&this.showError(e+` of ${a.length} records could not be deleted`)}}showColumn(e){this.setColumnVisibility(e,!0)}hideColumn(e){this.setColumnVisibility(e,!1)}setColumnVisibility(t,s){var a=this.options.fields[t];if(a)if(!s&&this.isFieldSorted(t))this.showError(`Cannot hide column "${a.title||t}" because it is currently sorted`);else if("fixed"!==a.visibility){a.visibility=s?"visible":"hidden";a=this.columnList.indexOf(t);if(0<=a){let e=a+1;this.options.selecting&&this.options.selectingCheckboxes&&(e+=1);t=`th:nth-child(${e}), td:nth-child(${e})`;this.elements.table.querySelectorAll(t).forEach(e=>{s?O.show(e):O.hide(e)})}this.options.saveUserPreferences&&(this.saveColumnSettings(),this.saveState())}}editRecordByKey(e){var t=this.getRowByKey(e);t?this.editRecord(t):this.showError(`Record with key '${e}' not found`)}async editRecordViaAjax(e,t,s={}){try{var a=this.keyField;if(!a)throw new Error("No key field defined in fTable options");var i={[a]:e,...s},o=this.options.forcePost?await l.post(t,i):await l.get(t,i);if(!o||!o.Record)throw new Error("Invalid response or missing Record");var r=o.Record,n=this.getRowByKey(e)||O.create("tr",{className:"ftable-data-row",attributes:{"data-record-key":e}});n.recordData={...r},await this.editRecord(n)}catch(e){this.showError("Failed to load record for editing."),this.logger.error("editRecordViaAjax failed: "+e.message)}}openChildTable(e,t,s){this.options.openChildAsAccordion&&this.closeAllChildTables(),this.closeChildTable(e);var a=O.create("tr",{className:"ftable-child-row"}),i=O.create("td",{attributes:{colspan:this.getVisibleColumnCount()},parent:a}),i=O.create("div",{className:"ftable-child-table-container",parent:i}),a=(e.parentNode.insertBefore(a,e.nextSibling),(e.childRow=a).parentRow=e,new n(i,{...t,paging:void 0===t.paging||t.paging,pageSize:t.pageSize||10,sorting:void 0===t.sorting||t.sorting,selecting:!1,toolbarsearch:!0,messages:{...this.options.messages,...t.messages}}));a.close;return a.close=()=>{this.closeChildTable(e)},a.init(),s&&s(a),e.childTable=a}closeChildTable(e){e.childRow&&(e.childTable&&"function"==typeof e.childTable.destroy&&e.childTable.destroy(),e.childRow.remove(),e.childRow=null,e.childTable=null)}closeAllChildTables(){Object.values(this.elements.tableRows).forEach(e=>{e.childTable&&this.closeChildTable(e)})}getSortingInfo(){let t=this.options.messages||{};return this.state.sorting.map(e=>(this.options.fields[e.fieldName]?.title||e.fieldName)+` (${"ASC"===e.direction?t.ascending||"ascending":t.descending||"descending"})`).join(", ")}renderSortingInfo(){if(this.options.sortingInfoSelector&&this.options.sorting){var e=document.querySelector(this.options.sortingInfoSelector);if(e){e.innerHTML="";let t=this.options.messages||{};var s,a=t.sortingInfoPrefix?`<span class="ftable-sorting-prefix">${t.sortingInfoPrefix}</span> `:"",i=t.sortingInfoSuffix?` <span class="ftable-sorting-suffix">${t.sortingInfoSuffix}</span>`:"";0===this.state.sorting.length?e.innerHTML=t.sortingInfoNone||"":(s=this.getSortingInfo(),e.innerHTML=a+s+i,0<this.state.sorting.length&&((a=document.createElement("button")).textContent=t.resetSorting||"Reset Sorting",a.style.marginLeft="10px",a.classList.add("ftable-sorting-reset-btn"),a.addEventListener("click",e=>{e.preventDefault(),this.state.sorting=[],this.updateSortingHeaders(),this.load(),this.saveState()}),e.appendChild(a)),this.options.tableReset&&((s=document.createElement("button")).textContent=t.resetTable||"Reset Table",s.style.marginLeft="10px",s.classList.add("ftable-table-reset-btn"),s.addEventListener("click",e=>{e.preventDefault();e=t.resetTableConfirm;confirm(e)&&(this.userPrefs.remove("column-settings"),this.userPrefs.remove("table-state"),this.state.sorting=[],this.state.pageSize=this.options.pageSize,this.columnList.forEach(e=>{e=this.options.fields[e];e.visibility="fixed"===e.visibility?"fixed":"visible"}),location.reload())}),e.appendChild(s)))}}}static _waitForFieldReady(s,e,a,i=5e3){if(e){let t=e.querySelector(`[name="${s}"]`);if(t&&"SELECT"===t.tagName)if(1<t.options.length)a();else{let e=new MutationObserver(()=>{1<t.options.length&&(e.disconnect(),a())});e.observe(t,{childList:!0}),0<i&&setTimeout(()=>{e.disconnect(),1<t.options.length?a():console.warn(`FTable: Timeout waiting for field '${s}' to load options`)},i)}else console.warn(`FTable: Field '${s}' not found or not a <select>`)}else console.warn(`FTable: No form provided for waitForFieldReady('${s}')`)}static async waitForFieldReady(t,s,a=5e3){return new Promise(e=>{n._waitForFieldReady(t,s,e,a)})}}e.FTable=n,e.FtableModal=o,e.FTableHttpClient=l,"undefined"!=typeof module&&module.exports&&(module.exports=n,module.exports.FtableModal=o,module.exports.FTableHttpClient=l)})("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:this);
35
+ `;e.document.write(t),e.document.close()}async bulkDelete(e="Delete selected records?"){var t=this.getSelectedRows();if(0===t.length)this.showError("No records selected");else if(confirm(e)){var s,a=[];for(s of t.map(e=>this.getKeyValue(e.recordData)))try{var i=await this.performDelete(s);a.push({key:s,success:"OK"===i.Result,result:i})}catch(e){a.push({key:s,success:!1,error:e.message})}a.filter(e=>e.success).forEach(({key:e})=>{e=this.getRowByKey(e);e&&this.removeRowFromTable(e)});e=a.filter(e=>!e.success).length;0<e&&this.showError(e+` of ${a.length} records could not be deleted`)}}showColumn(e){this.setColumnVisibility(e,!0)}hideColumn(e){this.setColumnVisibility(e,!1)}setColumnVisibility(t,s){var a=this.options.fields[t];if(a)if(!s&&this.isFieldSorted(t))this.showError(`Cannot hide column "${a.title||t}" because it is currently sorted`);else if("fixed"!==a.visibility){a.visibility=s?"visible":"hidden";a=this.columnList.indexOf(t);if(0<=a){let e=a+1;this.options.selecting&&this.options.selectingCheckboxes&&(e+=1);t=`th:nth-child(${e}), td:nth-child(${e})`;this.elements.table.querySelectorAll(t).forEach(e=>{s?O.show(e):O.hide(e)})}this.options.saveUserPreferences&&(this.saveColumnSettings(),this.saveState())}}editRecordByKey(e){var t=this.getRowByKey(e);t?this.editRecord(t):this.showError(`Record with key '${e}' not found`)}async editRecordViaAjax(e,t,s={}){try{var a=this.keyField;if(!a)throw new Error("No key field defined in fTable options");var i={[a]:e,...s},o=this.options.forcePost?await n.post(t,i):await n.get(t,i);if(!o||!o.Record)throw new Error("Invalid response or missing Record");var r=o.Record,l=this.getRowByKey(e)||O.create("tr",{className:"ftable-data-row",attributes:{"data-record-key":e}});l.recordData={...r},await this.editRecord(l)}catch(e){this.showError("Failed to load record for editing."),this.logger.error("editRecordViaAjax failed: "+e.message)}}openChildTable(e,t,s){this.options.openChildAsAccordion&&this.closeAllChildTables(),this.closeChildTable(e);var a=O.create("tr",{className:"ftable-child-row"}),i=O.create("td",{attributes:{colspan:this.getVisibleColumnCount()},parent:a}),i=O.create("div",{className:"ftable-child-table-container",parent:i}),a=(e.parentNode.insertBefore(a,e.nextSibling),(e.childRow=a).parentRow=e,new l(i,{...t,paging:void 0===t.paging||t.paging,pageSize:t.pageSize||10,sorting:void 0===t.sorting||t.sorting,selecting:!1,toolbarsearch:!0,messages:{...this.options.messages,...t.messages}}));a.close;return a.close=()=>{this.closeChildTable(e)},a.init(),s&&s(a),e.childTable=a}closeChildTable(e){e.childRow&&(e.childTable&&"function"==typeof e.childTable.destroy&&e.childTable.destroy(),e.childRow.remove(),e.childRow=null,e.childTable=null)}closeAllChildTables(){Object.values(this.elements.tableRows).forEach(e=>{e.childTable&&this.closeChildTable(e)})}getSortingInfo(){let t=this.options.messages||{};return this.state.sorting.map(e=>(this.options.fields[e.fieldName]?.title||e.fieldName)+` (${"ASC"===e.direction?t.ascending||"ascending":t.descending||"descending"})`).join(", ")}renderSortingInfo(){if(this.options.sortingInfoSelector&&this.options.sorting){var e=document.querySelector(this.options.sortingInfoSelector);if(e){e.innerHTML="";let t=this.options.messages||{};var s,a=t.sortingInfoPrefix?`<span class="ftable-sorting-prefix">${t.sortingInfoPrefix}</span> `:"",i=t.sortingInfoSuffix?` <span class="ftable-sorting-suffix">${t.sortingInfoSuffix}</span>`:"";0===this.state.sorting.length?e.innerHTML=t.sortingInfoNone||"":(s=this.getSortingInfo(),e.innerHTML=a+s+i,0<this.state.sorting.length&&((a=document.createElement("button")).textContent=t.resetSorting||"Reset Sorting",a.style.marginLeft="10px",a.classList.add("ftable-sorting-reset-btn"),a.addEventListener("click",e=>{e.preventDefault(),this.state.sorting=[],this.updateSortingHeaders(),this.load(),this.saveState()}),e.appendChild(a)),this.options.tableReset&&((s=document.createElement("button")).textContent=t.resetTable||"Reset Table",s.style.marginLeft="10px",s.classList.add("ftable-table-reset-btn"),s.addEventListener("click",e=>{e.preventDefault();e=t.resetTableConfirm;confirm(e)&&(this.userPrefs.remove("column-settings"),this.userPrefs.remove("table-state"),this.state.sorting=[],this.state.pageSize=this.options.pageSize,this.columnList.forEach(e=>{e=this.options.fields[e];e.visibility="fixed"===e.visibility?"fixed":"visible"}),location.reload())}),e.appendChild(s)))}}}static _waitForFieldReady(s,e,a,i=5e3){if(e){let t=e.querySelector(`[name="${s}"]`);if(t&&"SELECT"===t.tagName)if(1<t.options.length)a();else{let e=new MutationObserver(()=>{1<t.options.length&&(e.disconnect(),a())});e.observe(t,{childList:!0}),0<i&&setTimeout(()=>{e.disconnect(),1<t.options.length?a():console.warn(`FTable: Timeout waiting for field '${s}' to load options`)},i)}else console.warn(`FTable: Field '${s}' not found or not a <select>`)}else console.warn(`FTable: No form provided for waitForFieldReady('${s}')`)}static async waitForFieldReady(t,s,a=5e3){return new Promise(e=>{l._waitForFieldReady(t,s,e,a)})}}e.FTable=l,e.FtableModal=o,e.FTableHttpClient=n,"undefined"!=typeof module&&module.exports&&(module.exports=l,module.exports.FtableModal=o,module.exports.FTableHttpClient=n)})("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:this);
package/ftable.umd.js CHANGED
@@ -1414,9 +1414,7 @@ class FTableFormBuilder {
1414
1414
  : [];
1415
1415
 
1416
1416
  // Support data-livesearch attribute on the virtual select as well as field.livesearch
1417
- const livesearch = field.livesearch
1418
- ?? (attributes['data-livesearch'] === 'true' || attributes['data-livesearch'] === true)
1419
- ?? false;
1417
+ const livesearch = field.livesearch ?? false;
1420
1418
 
1421
1419
  return this._buildCustomMultiSelect({
1422
1420
  containerId: fieldName,
@@ -2188,7 +2186,7 @@ class FTable extends FTableEventEmitter {
2188
2186
  initColumnWidths() {
2189
2187
  const visibleFields = this.columnList.filter(fieldName => {
2190
2188
  const field = this.options.fields[fieldName];
2191
- return field.visibility !== 'hidden' && field.visibility !== 'separator';
2189
+ return !field.action && field.visibility !== 'hidden' && field.visibility !== 'separator';
2192
2190
  });
2193
2191
 
2194
2192
  const count = visibleFields.length;
@@ -2207,7 +2205,7 @@ class FTable extends FTableEventEmitter {
2207
2205
  th: this.elements.table.querySelector(`[data-field-name="${fieldName}"]`),
2208
2206
  field: this.options.fields[fieldName]
2209
2207
  }))
2210
- .filter(item => item.th && item.field.visibility !== 'hidden' && item.field.visibility !== 'separator');
2208
+ .filter(item => item.th && !item.field.action && item.field.visibility !== 'hidden' && item.field.visibility !== 'separator');
2211
2209
 
2212
2210
  if (visibleHeaders.length === 0) return;
2213
2211
 
@@ -2342,8 +2340,17 @@ class FTable extends FTableEventEmitter {
2342
2340
  this.fieldList.forEach(fieldName => {
2343
2341
  const field = this.options.fields[fieldName];
2344
2342
  const isKeyField = field.key === true;
2345
-
2346
- if (isKeyField) {
2343
+ const isActionField = !!field.action; // action: 'select' | 'update' | 'clone' | 'delete'
2344
+
2345
+ if (isActionField) {
2346
+ // Action columns are always listed but never part of forms or sorting
2347
+ field.list = true;
2348
+ field.create = false;
2349
+ field.edit = false;
2350
+ field.sorting = false;
2351
+ field.searchable = false;
2352
+ field.visibility = field.visibility ?? 'visible';
2353
+ } else if (isKeyField) {
2347
2354
  if (field.create === undefined || !field.create) {
2348
2355
  field.create = true;
2349
2356
  field.type = 'hidden';
@@ -2368,6 +2375,13 @@ class FTable extends FTableEventEmitter {
2368
2375
  return field.list !== false;
2369
2376
  });
2370
2377
 
2378
+ // Track which actions are user-placed (via action columns in fields)
2379
+ this._userPlacedActions = new Set(
2380
+ this.fieldList
2381
+ .filter(name => this.options.fields[name].action)
2382
+ .map(name => this.options.fields[name].action)
2383
+ );
2384
+
2371
2385
  // Find key field
2372
2386
  this.keyField = this.fieldList.find(name => this.options.fields[name].key === true);
2373
2387
  if (!this.keyField) {
@@ -2378,6 +2392,7 @@ class FTable extends FTableEventEmitter {
2378
2392
  async resolveAsyncFieldOptions() {
2379
2393
  const promises = this.columnList.map(async (fieldName) => {
2380
2394
  const field = this.options.fields[fieldName];
2395
+ if (field.action) return; // Skip action columns
2381
2396
  const originalOptions = this.formBuilder.originalFieldOptions.get(fieldName);
2382
2397
 
2383
2398
  if (this.formBuilder.shouldResolveOptions(originalOptions)) {
@@ -2403,7 +2418,7 @@ class FTable extends FTableEventEmitter {
2403
2418
  for (const row of rows) {
2404
2419
  for (const fieldName of this.columnList) {
2405
2420
  const field = this.options.fields[fieldName];
2406
- if (!field.options) continue;
2421
+ if (field.action || !field.options) continue;
2407
2422
 
2408
2423
  const cell = row.querySelector(`td[data-field-name="${fieldName}"]`);
2409
2424
  if (!cell) continue;
@@ -2474,8 +2489,8 @@ class FTable extends FTableEventEmitter {
2474
2489
  parent: thead
2475
2490
  });
2476
2491
 
2477
- // Add selecting column if enabled
2478
- if (this.options.selecting && this.options.selectingCheckboxes) {
2492
+ // Add selecting column if enabled (only if not user-placed)
2493
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
2479
2494
  const selectHeader = FTableDOMHelper.create('th', {
2480
2495
  className: `ftable-command-column-header ftable-column-header-select`,
2481
2496
  parent: headerRow
@@ -2493,9 +2508,41 @@ class FTable extends FTableEventEmitter {
2493
2508
  }
2494
2509
  }
2495
2510
 
2496
- // Add data columns
2511
+ // Add data columns (including any user-placed action columns)
2497
2512
  this.columnList.forEach(fieldName => {
2498
2513
  const field = this.options.fields[fieldName];
2514
+
2515
+ // If this column is a user-placed action column, render an action header
2516
+ if (field.action) {
2517
+ const actionClassMap = {
2518
+ select: 'ftable-column-header-select',
2519
+ update: 'ftable-column-header-edit',
2520
+ clone: 'ftable-column-header-clone',
2521
+ delete: 'ftable-column-header-delete',
2522
+ };
2523
+ const th = FTableDOMHelper.create('th', {
2524
+ className: `ftable-command-column-header ${actionClassMap[field.action] || ''}`,
2525
+ parent: headerRow
2526
+ });
2527
+ if (field.title) {
2528
+ th.textContent = field.title;
2529
+ }
2530
+ // For select action with multiselect, add the select-all checkbox
2531
+ if (field.action === 'select' && this.options.selecting && this.options.selectingCheckboxes && this.options.multiselect) {
2532
+ const selectAllCheckbox = FTableDOMHelper.create('input', {
2533
+ attributes: { type: 'checkbox' },
2534
+ parent: th
2535
+ });
2536
+ selectAllCheckbox.addEventListener('change', () => {
2537
+ this.toggleSelectAll(selectAllCheckbox.checked);
2538
+ });
2539
+ }
2540
+ if (field.width) {
2541
+ th.style.width = field.width;
2542
+ }
2543
+ return;
2544
+ }
2545
+
2499
2546
  const th = FTableDOMHelper.create('th', {
2500
2547
  className: `ftable-column-header ${field.listClass || ''} ${field.listClassHeader || ''}`,
2501
2548
  attributes: { 'data-field-name': fieldName },
@@ -2544,22 +2591,22 @@ class FTable extends FTableEventEmitter {
2544
2591
  }
2545
2592
  });
2546
2593
 
2547
- // Add action columns
2548
- if (this.options.actions.updateAction) {
2594
+ // Add default action columns only if not user-placed
2595
+ if (this.options.actions.updateAction && !this._userPlacedActions.has('update')) {
2549
2596
  FTableDOMHelper.create('th', {
2550
2597
  className: 'ftable-command-column-header ftable-column-header-edit',
2551
2598
  parent: headerRow
2552
2599
  });
2553
2600
  }
2554
2601
 
2555
- if (this.options.actions.cloneAction) {
2602
+ if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
2556
2603
  FTableDOMHelper.create('th', {
2557
2604
  className: 'ftable-command-column-header ftable-column-header-clone',
2558
2605
  parent: headerRow
2559
2606
  });
2560
2607
  }
2561
2608
 
2562
- if (this.options.actions.deleteAction) {
2609
+ if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
2563
2610
  FTableDOMHelper.create('th', {
2564
2611
  className: 'ftable-command-column-header ftable-column-header-delete',
2565
2612
  parent: headerRow
@@ -2580,17 +2627,27 @@ class FTable extends FTableEventEmitter {
2580
2627
  parent: theadParent
2581
2628
  });
2582
2629
 
2583
- // Add empty cell for selecting column if enabled
2584
- if (this.options.selecting && this.options.selectingCheckboxes) {
2630
+ // Add empty cell for selecting column if enabled (only if not user-placed)
2631
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
2585
2632
  FTableDOMHelper.create('th', {
2586
2633
  className: 'ftable-toolbarsearch-column-header',
2587
2634
  parent: searchRow
2588
2635
  });
2589
2636
  }
2590
2637
 
2591
- // Add search input cells for data columns
2638
+ // Add search input cells for data columns (including user-placed action columns)
2592
2639
  for (const fieldName of this.columnList) {
2593
2640
  const field = this.options.fields[fieldName];
2641
+
2642
+ // Action columns get an empty search cell
2643
+ if (field.action) {
2644
+ FTableDOMHelper.create('th', {
2645
+ className: 'ftable-toolbarsearch-column-header ftable-command-column-header',
2646
+ parent: searchRow
2647
+ });
2648
+ continue;
2649
+ }
2650
+
2594
2651
  const isSearchable = field.searchable !== false;
2595
2652
 
2596
2653
  const th = FTableDOMHelper.create('th', {
@@ -2762,9 +2819,9 @@ class FTable extends FTableEventEmitter {
2762
2819
  parent: searchRow
2763
2820
  });
2764
2821
 
2765
- const actionCount = (this.options.actions.updateAction ? 1 : 0) +
2766
- (this.options.actions.deleteAction ? 1 : 0) +
2767
- (this.options.actions.cloneAction ? 1 : 0);
2822
+ const actionCount = (this.options.actions.updateAction && !this._userPlacedActions.has('update') ? 1 : 0) +
2823
+ (this.options.actions.deleteAction && !this._userPlacedActions.has('delete') ? 1 : 0) +
2824
+ (this.options.actions.cloneAction && !this._userPlacedActions.has('clone') ? 1 : 0);
2768
2825
 
2769
2826
  if (actionCount > 0) {
2770
2827
  resetTh.colSpan = actionCount;
@@ -2872,9 +2929,7 @@ class FTable extends FTableEventEmitter {
2872
2929
  }
2873
2930
 
2874
2931
  createCustomMultiSelectForSearch(fieldSearchName, fieldName, field, optionsSource, attributes) {
2875
- const livesearch = field.livesearch
2876
- ?? (attributes['data-livesearch'] === 'true' || attributes['data-livesearch'] === true)
2877
- ?? false;
2932
+ const livesearch = field.livesearch ?? false;
2878
2933
 
2879
2934
  return this.formBuilder._buildCustomMultiSelect({
2880
2935
  hiddenSelectId: fieldSearchName,
@@ -3451,6 +3506,7 @@ class FTable extends FTableEventEmitter {
3451
3506
 
3452
3507
  this.columnList.forEach(fieldName => {
3453
3508
  const field = this.options.fields[fieldName];
3509
+ if (field.action) return; // Action columns don't appear in column picker
3454
3510
  const isVisible = field.visibility !== 'hidden';
3455
3511
  const isFixed = field.visibility === 'fixed';
3456
3512
  const isSeparator = field.visibility === 'separator';
@@ -3863,26 +3919,45 @@ class FTable extends FTableEventEmitter {
3863
3919
  // Store record data
3864
3920
  row.recordData = record;
3865
3921
 
3866
- // Add selecting checkbox if enabled
3867
- if (this.options.selecting && this.options.selectingCheckboxes) {
3922
+ // Add selecting checkbox if enabled (only if not user-placed)
3923
+ if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
3868
3924
  this.addSelectingCell(row);
3869
3925
  }
3870
3926
 
3871
- // Add data cells
3927
+ // Add data cells (including user-placed action columns)
3872
3928
  this.columnList.forEach(fieldName => {
3873
- this.addDataCell(row, record, fieldName);
3929
+ const field = this.options.fields[fieldName];
3930
+ if (field.action) {
3931
+ // Render inline action cell
3932
+ switch (field.action) {
3933
+ case 'select':
3934
+ this.addSelectingCell(row);
3935
+ break;
3936
+ case 'update':
3937
+ this.addEditCell(row);
3938
+ break;
3939
+ case 'clone':
3940
+ this.addCloneCell(row);
3941
+ break;
3942
+ case 'delete':
3943
+ this.addDeleteCell(row);
3944
+ break;
3945
+ }
3946
+ } else {
3947
+ this.addDataCell(row, record, fieldName);
3948
+ }
3874
3949
  });
3875
3950
 
3876
- // Add action cells
3877
- if (this.options.actions.updateAction) {
3951
+ // Add default action cells only if not user-placed
3952
+ if (this.options.actions.updateAction && !this._userPlacedActions.has('update')) {
3878
3953
  this.addEditCell(row);
3879
3954
  }
3880
3955
 
3881
- if (this.options.actions.cloneAction) {
3956
+ if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
3882
3957
  this.addCloneCell(row);
3883
3958
  }
3884
3959
 
3885
- if (this.options.actions.deleteAction) {
3960
+ if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
3886
3961
  this.addDeleteCell(row);
3887
3962
  }
3888
3963
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liedekef/ftable",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Modern, lightweight, jQuery-free CRUD table for dynamic AJAX-powered tables.",
5
5
  "main": "ftable.js",
6
6
  "module": "ftable.esm.js",