@liedekef/ftable 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ftable.esm.js +110 -34
- package/ftable.js +110 -34
- package/ftable.min.js +2 -2
- package/ftable.umd.js +110 -34
- package/package.json +1 -1
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
|
-
|
|
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,
|
|
@@ -3114,9 +3169,10 @@ class FTable extends FTableEventEmitter {
|
|
|
3114
3169
|
|
|
3115
3170
|
const settings = {};
|
|
3116
3171
|
this.columnList.forEach(fieldName => {
|
|
3172
|
+
const field = this.options.fields[fieldName];
|
|
3173
|
+
if (field.action) return; // Action columns have no persistent state
|
|
3117
3174
|
const th = this.elements.table.querySelector(`[data-field-name="${fieldName}"]`);
|
|
3118
3175
|
if (th) {
|
|
3119
|
-
const field = this.options.fields[fieldName];
|
|
3120
3176
|
settings[fieldName] = {
|
|
3121
3177
|
width: th.style.width || field.width || 'auto',
|
|
3122
3178
|
visibility: field.visibility || 'visible'
|
|
@@ -3148,7 +3204,7 @@ class FTable extends FTableEventEmitter {
|
|
|
3148
3204
|
const settings = JSON.parse(settingsJson);
|
|
3149
3205
|
Object.entries(settings).forEach(([fieldName, config]) => {
|
|
3150
3206
|
const field = this.options.fields[fieldName];
|
|
3151
|
-
if (field) {
|
|
3207
|
+
if (field && !field.action) {
|
|
3152
3208
|
if (config.width) field.width = config.width;
|
|
3153
3209
|
if (config.visibility) field.visibility = config.visibility;
|
|
3154
3210
|
}
|
|
@@ -3450,6 +3506,7 @@ class FTable extends FTableEventEmitter {
|
|
|
3450
3506
|
|
|
3451
3507
|
this.columnList.forEach(fieldName => {
|
|
3452
3508
|
const field = this.options.fields[fieldName];
|
|
3509
|
+
if (field.action) return; // Action columns don't appear in column picker
|
|
3453
3510
|
const isVisible = field.visibility !== 'hidden';
|
|
3454
3511
|
const isFixed = field.visibility === 'fixed';
|
|
3455
3512
|
const isSeparator = field.visibility === 'separator';
|
|
@@ -3862,26 +3919,45 @@ class FTable extends FTableEventEmitter {
|
|
|
3862
3919
|
// Store record data
|
|
3863
3920
|
row.recordData = record;
|
|
3864
3921
|
|
|
3865
|
-
// Add selecting checkbox if enabled
|
|
3866
|
-
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')) {
|
|
3867
3924
|
this.addSelectingCell(row);
|
|
3868
3925
|
}
|
|
3869
3926
|
|
|
3870
|
-
// Add data cells
|
|
3927
|
+
// Add data cells (including user-placed action columns)
|
|
3871
3928
|
this.columnList.forEach(fieldName => {
|
|
3872
|
-
this.
|
|
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
|
+
}
|
|
3873
3949
|
});
|
|
3874
3950
|
|
|
3875
|
-
// Add action cells
|
|
3876
|
-
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')) {
|
|
3877
3953
|
this.addEditCell(row);
|
|
3878
3954
|
}
|
|
3879
3955
|
|
|
3880
|
-
if (this.options.actions.cloneAction) {
|
|
3956
|
+
if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
|
|
3881
3957
|
this.addCloneCell(row);
|
|
3882
3958
|
}
|
|
3883
3959
|
|
|
3884
|
-
if (this.options.actions.deleteAction) {
|
|
3960
|
+
if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
|
|
3885
3961
|
this.addDeleteCell(row);
|
|
3886
3962
|
}
|
|
3887
3963
|
|
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
|
-
|
|
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,
|
|
@@ -3115,9 +3170,10 @@ class FTable extends FTableEventEmitter {
|
|
|
3115
3170
|
|
|
3116
3171
|
const settings = {};
|
|
3117
3172
|
this.columnList.forEach(fieldName => {
|
|
3173
|
+
const field = this.options.fields[fieldName];
|
|
3174
|
+
if (field.action) return; // Action columns have no persistent state
|
|
3118
3175
|
const th = this.elements.table.querySelector(`[data-field-name="${fieldName}"]`);
|
|
3119
3176
|
if (th) {
|
|
3120
|
-
const field = this.options.fields[fieldName];
|
|
3121
3177
|
settings[fieldName] = {
|
|
3122
3178
|
width: th.style.width || field.width || 'auto',
|
|
3123
3179
|
visibility: field.visibility || 'visible'
|
|
@@ -3149,7 +3205,7 @@ class FTable extends FTableEventEmitter {
|
|
|
3149
3205
|
const settings = JSON.parse(settingsJson);
|
|
3150
3206
|
Object.entries(settings).forEach(([fieldName, config]) => {
|
|
3151
3207
|
const field = this.options.fields[fieldName];
|
|
3152
|
-
if (field) {
|
|
3208
|
+
if (field && !field.action) {
|
|
3153
3209
|
if (config.width) field.width = config.width;
|
|
3154
3210
|
if (config.visibility) field.visibility = config.visibility;
|
|
3155
3211
|
}
|
|
@@ -3451,6 +3507,7 @@ class FTable extends FTableEventEmitter {
|
|
|
3451
3507
|
|
|
3452
3508
|
this.columnList.forEach(fieldName => {
|
|
3453
3509
|
const field = this.options.fields[fieldName];
|
|
3510
|
+
if (field.action) return; // Action columns don't appear in column picker
|
|
3454
3511
|
const isVisible = field.visibility !== 'hidden';
|
|
3455
3512
|
const isFixed = field.visibility === 'fixed';
|
|
3456
3513
|
const isSeparator = field.visibility === 'separator';
|
|
@@ -3863,26 +3920,45 @@ class FTable extends FTableEventEmitter {
|
|
|
3863
3920
|
// Store record data
|
|
3864
3921
|
row.recordData = record;
|
|
3865
3922
|
|
|
3866
|
-
// Add selecting checkbox if enabled
|
|
3867
|
-
if (this.options.selecting && this.options.selectingCheckboxes) {
|
|
3923
|
+
// Add selecting checkbox if enabled (only if not user-placed)
|
|
3924
|
+
if (this.options.selecting && this.options.selectingCheckboxes && !this._userPlacedActions.has('select')) {
|
|
3868
3925
|
this.addSelectingCell(row);
|
|
3869
3926
|
}
|
|
3870
3927
|
|
|
3871
|
-
// Add data cells
|
|
3928
|
+
// Add data cells (including user-placed action columns)
|
|
3872
3929
|
this.columnList.forEach(fieldName => {
|
|
3873
|
-
this.
|
|
3930
|
+
const field = this.options.fields[fieldName];
|
|
3931
|
+
if (field.action) {
|
|
3932
|
+
// Render inline action cell
|
|
3933
|
+
switch (field.action) {
|
|
3934
|
+
case 'select':
|
|
3935
|
+
this.addSelectingCell(row);
|
|
3936
|
+
break;
|
|
3937
|
+
case 'update':
|
|
3938
|
+
this.addEditCell(row);
|
|
3939
|
+
break;
|
|
3940
|
+
case 'clone':
|
|
3941
|
+
this.addCloneCell(row);
|
|
3942
|
+
break;
|
|
3943
|
+
case 'delete':
|
|
3944
|
+
this.addDeleteCell(row);
|
|
3945
|
+
break;
|
|
3946
|
+
}
|
|
3947
|
+
} else {
|
|
3948
|
+
this.addDataCell(row, record, fieldName);
|
|
3949
|
+
}
|
|
3874
3950
|
});
|
|
3875
3951
|
|
|
3876
|
-
// Add action cells
|
|
3877
|
-
if (this.options.actions.updateAction) {
|
|
3952
|
+
// Add default action cells only if not user-placed
|
|
3953
|
+
if (this.options.actions.updateAction && !this._userPlacedActions.has('update')) {
|
|
3878
3954
|
this.addEditCell(row);
|
|
3879
3955
|
}
|
|
3880
3956
|
|
|
3881
|
-
if (this.options.actions.cloneAction) {
|
|
3957
|
+
if (this.options.actions.cloneAction && !this._userPlacedActions.has('clone')) {
|
|
3882
3958
|
this.addCloneCell(row);
|
|
3883
3959
|
}
|
|
3884
3960
|
|
|
3885
|
-
if (this.options.actions.deleteAction) {
|
|
3961
|
+
if (this.options.actions.deleteAction && !this._userPlacedActions.has('delete')) {
|
|
3886
3962
|
this.addDeleteCell(row);
|
|
3887
3963
|
}
|
|
3888
3964
|
|