@liedekef/ftable 1.1.11 → 1.1.13
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 +161 -81
- package/ftable.js +161 -81
- package/ftable.min.js +3 -3
- package/ftable.umd.js +161 -81
- package/package.json +1 -1
- package/themes/basic/ftable_basic.css +1 -0
- package/themes/basic/ftable_basic.min.css +1 -1
- package/themes/ftable_theme_base.less +1 -0
- package/themes/lightcolor/blue/ftable.css +1 -0
- package/themes/lightcolor/blue/ftable.min.css +1 -1
- package/themes/lightcolor/gray/ftable.css +1 -0
- package/themes/lightcolor/gray/ftable.min.css +1 -1
- package/themes/lightcolor/green/ftable.css +1 -0
- package/themes/lightcolor/green/ftable.min.css +1 -1
- package/themes/lightcolor/orange/ftable.css +1 -0
- package/themes/lightcolor/orange/ftable.min.css +1 -1
- package/themes/lightcolor/red/ftable.css +1 -0
- package/themes/lightcolor/red/ftable.min.css +1 -1
- package/themes/metro/blue/ftable.css +1 -0
- package/themes/metro/blue/ftable.min.css +1 -1
- package/themes/metro/brown/ftable.css +1 -0
- package/themes/metro/brown/ftable.min.css +1 -1
- package/themes/metro/crimson/ftable.css +1 -0
- package/themes/metro/crimson/ftable.min.css +1 -1
- package/themes/metro/darkgray/ftable.css +1 -0
- package/themes/metro/darkgray/ftable.min.css +1 -1
- package/themes/metro/darkorange/ftable.css +1 -0
- package/themes/metro/darkorange/ftable.min.css +1 -1
- package/themes/metro/green/ftable.css +1 -0
- package/themes/metro/green/ftable.min.css +1 -1
- package/themes/metro/lightgray/ftable.css +1 -0
- package/themes/metro/lightgray/ftable.min.css +1 -1
- package/themes/metro/pink/ftable.css +1 -0
- package/themes/metro/pink/ftable.min.css +1 -1
- package/themes/metro/purple/ftable.css +1 -0
- package/themes/metro/purple/ftable.min.css +1 -1
- package/themes/metro/red/ftable.css +1 -0
- package/themes/metro/red/ftable.min.css +1 -1
package/ftable.esm.js
CHANGED
|
@@ -157,7 +157,11 @@ class FTableDOMHelper {
|
|
|
157
157
|
if (options.className) {
|
|
158
158
|
element.className = options.className;
|
|
159
159
|
}
|
|
160
|
-
|
|
160
|
+
|
|
161
|
+
if (options.style) {
|
|
162
|
+
element.style.cssText = options.style;
|
|
163
|
+
}
|
|
164
|
+
|
|
161
165
|
if (options.attributes) {
|
|
162
166
|
Object.entries(options.attributes).forEach(([key, value]) => {
|
|
163
167
|
element.setAttribute(key, value);
|
|
@@ -1493,6 +1497,50 @@ class FTable extends FTableEventEmitter {
|
|
|
1493
1497
|
|
|
1494
1498
|
// Add essential CSS if not already present
|
|
1495
1499
|
//this.addEssentialCSS();
|
|
1500
|
+
|
|
1501
|
+
// now make sure all tables have a % width
|
|
1502
|
+
this.initColumnWidths();
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
initColumnWidths() {
|
|
1506
|
+
const visibleFields = this.columnList.filter(fieldName => {
|
|
1507
|
+
const field = this.options.fields[fieldName];
|
|
1508
|
+
return field.visibility !== 'hidden';
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
const count = visibleFields.length;
|
|
1512
|
+
visibleFields.forEach(fieldName => {
|
|
1513
|
+
const field = this.options.fields[fieldName];
|
|
1514
|
+
// Use configured width or equal distribution
|
|
1515
|
+
//field.width = field.width || `${(100 / count).toFixed(2)}%`;
|
|
1516
|
+
field.width = field.width || `${(100 / count)}%`;
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
normalizeColumnWidths() {
|
|
1521
|
+
const container = this.elements.mainContainer;
|
|
1522
|
+
const visibleHeaders = this.columnList
|
|
1523
|
+
.map(fieldName => ({
|
|
1524
|
+
th: this.elements.table.querySelector(`[data-field-name="${fieldName}"]`),
|
|
1525
|
+
field: this.options.fields[fieldName]
|
|
1526
|
+
}))
|
|
1527
|
+
.filter(item => item.th && item.field.visibility !== 'hidden');
|
|
1528
|
+
|
|
1529
|
+
if (visibleHeaders.length === 0) return;
|
|
1530
|
+
|
|
1531
|
+
const totalContainerWidth = container.offsetWidth;
|
|
1532
|
+
let totalPercent = 0;
|
|
1533
|
+
|
|
1534
|
+
visibleHeaders.forEach(item => {
|
|
1535
|
+
const widthPct = (item.th.offsetWidth / totalContainerWidth) * 100;
|
|
1536
|
+
//item.field.width = `${widthPct.toFixed(2)}%`;
|
|
1537
|
+
item.field.width = `${widthPct}%`;
|
|
1538
|
+
item.th.style.width = item.field.width;
|
|
1539
|
+
totalPercent += widthPct;
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1542
|
+
// Optional: adjust for rounding drift
|
|
1543
|
+
// (not critical, but can help)
|
|
1496
1544
|
}
|
|
1497
1545
|
|
|
1498
1546
|
parseDefaultSorting(sortStr) {
|
|
@@ -1655,25 +1703,13 @@ class FTable extends FTableEventEmitter {
|
|
|
1655
1703
|
field.edit = true;
|
|
1656
1704
|
field.type = 'hidden';
|
|
1657
1705
|
}
|
|
1658
|
-
|
|
1659
|
-
field.visibility = 'hidden';
|
|
1660
|
-
}
|
|
1706
|
+
field.visibility = field.visibility ?? 'visible';
|
|
1661
1707
|
} else {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
}
|
|
1668
|
-
if (field.list === undefined) {
|
|
1669
|
-
field.list = true;
|
|
1670
|
-
}
|
|
1671
|
-
if (field.sorting === undefined) {
|
|
1672
|
-
field.sorting = true;
|
|
1673
|
-
}
|
|
1674
|
-
if (!field.hasOwnProperty('visibility')) {
|
|
1675
|
-
field.visibility = 'visible';
|
|
1676
|
-
}
|
|
1708
|
+
field.create = field.create ?? true;
|
|
1709
|
+
field.edit = field.edit ?? true;
|
|
1710
|
+
field.list = field.list ?? true;
|
|
1711
|
+
field.sorting = field.sorting ?? true;
|
|
1712
|
+
field.visibility = field.visibility ?? 'visible';
|
|
1677
1713
|
}
|
|
1678
1714
|
});
|
|
1679
1715
|
|
|
@@ -1686,7 +1722,7 @@ class FTable extends FTableEventEmitter {
|
|
|
1686
1722
|
// Find key field
|
|
1687
1723
|
this.keyField = this.fieldList.find(name => this.options.fields[name].key === true);
|
|
1688
1724
|
if (!this.keyField) {
|
|
1689
|
-
this.logger.
|
|
1725
|
+
this.logger.info('No key field defined');
|
|
1690
1726
|
}
|
|
1691
1727
|
}
|
|
1692
1728
|
|
|
@@ -1862,7 +1898,7 @@ class FTable extends FTableEventEmitter {
|
|
|
1862
1898
|
}
|
|
1863
1899
|
|
|
1864
1900
|
// Hide column if needed
|
|
1865
|
-
if (field.visibility === 'hidden') {
|
|
1901
|
+
if (field.visibility === 'hidden' || field.visibility === 'separator') {
|
|
1866
1902
|
FTableDOMHelper.hide(th);
|
|
1867
1903
|
}
|
|
1868
1904
|
});
|
|
@@ -1905,7 +1941,10 @@ class FTable extends FTableEventEmitter {
|
|
|
1905
1941
|
|
|
1906
1942
|
// Add empty cell for selecting column if enabled
|
|
1907
1943
|
if (this.options.selecting && this.options.selectingCheckboxes) {
|
|
1908
|
-
FTableDOMHelper.create('th', {
|
|
1944
|
+
FTableDOMHelper.create('th', {
|
|
1945
|
+
className: 'ftable-toolbarsearch-column-header',
|
|
1946
|
+
parent: searchRow
|
|
1947
|
+
});
|
|
1909
1948
|
}
|
|
1910
1949
|
|
|
1911
1950
|
// Add search input cells for data columns
|
|
@@ -2040,7 +2079,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2040
2079
|
}
|
|
2041
2080
|
|
|
2042
2081
|
// Hide search cell if column is hidden
|
|
2043
|
-
if (field.visibility === 'hidden') {
|
|
2082
|
+
if (field.visibility === 'hidden' || field.visibility === 'separator') {
|
|
2044
2083
|
FTableDOMHelper.hide(th);
|
|
2045
2084
|
}
|
|
2046
2085
|
}
|
|
@@ -2048,7 +2087,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2048
2087
|
if (this.options.toolbarsearch && this.options.toolbarreset) {
|
|
2049
2088
|
// Add reset button cell
|
|
2050
2089
|
const resetTh = FTableDOMHelper.create('th', {
|
|
2051
|
-
className: 'ftable-toolbarsearch-reset',
|
|
2090
|
+
className: 'ftable-toolbarsearch-column-header ftable-toolbarsearch-reset',
|
|
2052
2091
|
parent: searchRow
|
|
2053
2092
|
});
|
|
2054
2093
|
|
|
@@ -2165,8 +2204,18 @@ class FTable extends FTableEventEmitter {
|
|
|
2165
2204
|
this.load();
|
|
2166
2205
|
}
|
|
2167
2206
|
|
|
2207
|
+
getNextVisibleHeader(th) {
|
|
2208
|
+
const headers = Array.from(this.elements.table.querySelectorAll('thead th:not(.ftable-command-column-header, .ftable-toolbarsearch-column-header)'));
|
|
2209
|
+
const index = headers.indexOf(th);
|
|
2210
|
+
for (let i = index + 1; i < headers.length; i++) {
|
|
2211
|
+
if (headers[i].offsetParent !== null) { // visible
|
|
2212
|
+
return headers[i];
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
return null;
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2168
2218
|
makeColumnResizable(th, container) {
|
|
2169
|
-
// Create resize bar if it doesn't exist
|
|
2170
2219
|
if (!this.elements.resizeBar) {
|
|
2171
2220
|
this.elements.resizeBar = FTableDOMHelper.create('div', {
|
|
2172
2221
|
className: 'ftable-column-resize-bar',
|
|
@@ -2183,59 +2232,88 @@ class FTable extends FTableEventEmitter {
|
|
|
2183
2232
|
let isResizing = false;
|
|
2184
2233
|
let startX = 0;
|
|
2185
2234
|
let startWidth = 0;
|
|
2235
|
+
let containerRect;
|
|
2236
|
+
let nextTh = null;
|
|
2237
|
+
let nextStartWidth = 0;
|
|
2238
|
+
let nextField = null;
|
|
2186
2239
|
|
|
2187
2240
|
resizeHandler.addEventListener('mousedown', (e) => {
|
|
2188
2241
|
e.preventDefault();
|
|
2189
2242
|
e.stopPropagation();
|
|
2190
|
-
|
|
2243
|
+
|
|
2191
2244
|
isResizing = true;
|
|
2245
|
+
|
|
2246
|
+
// Capture layout
|
|
2247
|
+
containerRect = this.elements.mainContainer.getBoundingClientRect();
|
|
2192
2248
|
startX = e.clientX;
|
|
2193
2249
|
startWidth = th.offsetWidth;
|
|
2194
|
-
|
|
2195
|
-
//
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2250
|
+
|
|
2251
|
+
// Find next visible column
|
|
2252
|
+
nextTh = this.getNextVisibleHeader(th);
|
|
2253
|
+
if (nextTh) {
|
|
2254
|
+
nextStartWidth = nextTh.offsetWidth;
|
|
2255
|
+
const fieldName = nextTh.dataset.fieldName;
|
|
2256
|
+
nextField = this.options.fields[fieldName];
|
|
2257
|
+
} else {
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// Position resize bar
|
|
2262
|
+
const thRect = th.getBoundingClientRect();
|
|
2263
|
+
this.elements.resizeBar.style.left = (thRect.right - containerRect.left) + 'px';
|
|
2264
|
+
this.elements.resizeBar.style.top = (thRect.top - containerRect.top) + 'px';
|
|
2201
2265
|
this.elements.resizeBar.style.height = this.elements.table.offsetHeight + 'px';
|
|
2266
|
+
|
|
2202
2267
|
FTableDOMHelper.show(this.elements.resizeBar);
|
|
2203
|
-
|
|
2268
|
+
|
|
2204
2269
|
document.addEventListener('mousemove', handleMouseMove);
|
|
2205
2270
|
document.addEventListener('mouseup', handleMouseUp);
|
|
2206
2271
|
});
|
|
2207
2272
|
|
|
2208
2273
|
const handleMouseMove = (e) => {
|
|
2209
2274
|
if (!isResizing) return;
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
const newWidth = Math.max(50, startWidth + diff); // Minimum 50px width
|
|
2213
|
-
|
|
2214
|
-
// Update resize bar position
|
|
2215
|
-
const containerRect = this.elements.mainContainer.getBoundingClientRect();
|
|
2275
|
+
|
|
2276
|
+
// Move resize bar with mouse
|
|
2216
2277
|
this.elements.resizeBar.style.left = (e.clientX - containerRect.left) + 'px';
|
|
2217
2278
|
};
|
|
2218
2279
|
|
|
2219
2280
|
const handleMouseUp = (e) => {
|
|
2220
2281
|
if (!isResizing) return;
|
|
2221
|
-
|
|
2222
2282
|
isResizing = false;
|
|
2223
|
-
|
|
2224
|
-
const
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2283
|
+
|
|
2284
|
+
const diff = e.clientX - startX; // px
|
|
2285
|
+
const totalWidth = containerRect.width;
|
|
2286
|
+
|
|
2287
|
+
// Current column new width in px
|
|
2288
|
+
const newCurrentPx = Math.max(50, startWidth + diff);
|
|
2289
|
+
const newCurrentPct = (newCurrentPx / totalWidth) * 100;
|
|
2290
|
+
|
|
2291
|
+
// Next column adjustment
|
|
2292
|
+
if (nextTh) {
|
|
2293
|
+
const newNextPx = Math.max(50, nextStartWidth - diff); // opposite delta
|
|
2294
|
+
const newNextPct = (newNextPx / totalWidth) * 100;
|
|
2295
|
+
|
|
2296
|
+
// Apply to next
|
|
2297
|
+
nextField.width = `${newNextPct.toFixed(2)}%`;
|
|
2298
|
+
nextTh.style.width = nextField.width;
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// Apply to current
|
|
2302
|
+
const field = this.options.fields[th.dataset.fieldName];
|
|
2303
|
+
field.width = `${newCurrentPct.toFixed(2)}%`;
|
|
2304
|
+
th.style.width = field.width;
|
|
2305
|
+
|
|
2306
|
+
// Final normalization (optional, but safe)
|
|
2307
|
+
this.normalizeColumnWidths();
|
|
2308
|
+
|
|
2309
|
+
// Save
|
|
2236
2310
|
if (this.options.saveUserPreferences) {
|
|
2237
2311
|
this.saveColumnSettings();
|
|
2238
2312
|
}
|
|
2313
|
+
|
|
2314
|
+
FTableDOMHelper.hide(this.elements.resizeBar);
|
|
2315
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
2316
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
2239
2317
|
};
|
|
2240
2318
|
}
|
|
2241
2319
|
|
|
@@ -2466,8 +2544,8 @@ class FTable extends FTableEventEmitter {
|
|
|
2466
2544
|
this.subscribeOptionEvents();
|
|
2467
2545
|
|
|
2468
2546
|
// Add toolbar buttons
|
|
2469
|
-
this.createToolbarButtons();
|
|
2470
2547
|
this.createCustomToolbarItems();
|
|
2548
|
+
this.createToolbarButtons();
|
|
2471
2549
|
|
|
2472
2550
|
// Keyboard shortcuts
|
|
2473
2551
|
this.bindKeyboardEvents();
|
|
@@ -2564,6 +2642,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2564
2642
|
const field = this.options.fields[fieldName];
|
|
2565
2643
|
const isVisible = field.visibility !== 'hidden';
|
|
2566
2644
|
const isFixed = field.visibility === 'fixed';
|
|
2645
|
+
const isSeparator = field.visibility === 'separator';
|
|
2567
2646
|
const isSorted = this.isFieldSorted(fieldName);
|
|
2568
2647
|
|
|
2569
2648
|
const listItem = FTableDOMHelper.create('li', {
|
|
@@ -2576,24 +2655,32 @@ class FTable extends FTableEventEmitter {
|
|
|
2576
2655
|
parent: listItem
|
|
2577
2656
|
});
|
|
2578
2657
|
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2658
|
+
if (!isSeparator) {
|
|
2659
|
+
const checkbox = FTableDOMHelper.create('input', {
|
|
2660
|
+
attributes: {
|
|
2661
|
+
type: 'checkbox',
|
|
2662
|
+
id: `column-${fieldName}`
|
|
2663
|
+
},
|
|
2664
|
+
parent: label
|
|
2665
|
+
});
|
|
2666
|
+
checkbox.checked = isVisible;
|
|
2588
2667
|
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2668
|
+
// Disable checkbox if column is fixed or currently sorted
|
|
2669
|
+
if (isFixed || (isSorted && isVisible)) {
|
|
2670
|
+
checkbox.disabled = true;
|
|
2671
|
+
listItem.style.opacity = '0.6';
|
|
2672
|
+
}
|
|
2673
|
+
// Handle checkbox change
|
|
2674
|
+
if (!checkbox.disabled) {
|
|
2675
|
+
checkbox.addEventListener('change', () => {
|
|
2676
|
+
this.setColumnVisibility(fieldName, checkbox.checked);
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2593
2679
|
}
|
|
2594
2680
|
|
|
2595
2681
|
const labelText = FTableDOMHelper.create('span', {
|
|
2596
2682
|
text: field.title || fieldName,
|
|
2683
|
+
style: isSeparator ? 'font-weight: bold;' : null,
|
|
2597
2684
|
parent: label
|
|
2598
2685
|
});
|
|
2599
2686
|
|
|
@@ -2608,12 +2695,6 @@ class FTable extends FTableEventEmitter {
|
|
|
2608
2695
|
sortIndicator.style.color = '#666';
|
|
2609
2696
|
}
|
|
2610
2697
|
|
|
2611
|
-
// Handle checkbox change
|
|
2612
|
-
if (!checkbox.disabled) {
|
|
2613
|
-
checkbox.addEventListener('change', () => {
|
|
2614
|
-
this.setColumnVisibility(fieldName, checkbox.checked);
|
|
2615
|
-
});
|
|
2616
|
-
}
|
|
2617
2698
|
});
|
|
2618
2699
|
}
|
|
2619
2700
|
|
|
@@ -2721,7 +2802,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2721
2802
|
if (!this.options.toolbar || !this.options.toolbar.items) return;
|
|
2722
2803
|
|
|
2723
2804
|
this.options.toolbar.items.forEach(item => {
|
|
2724
|
-
const button = FTableDOMHelper.create('
|
|
2805
|
+
const button = FTableDOMHelper.create('span', {
|
|
2725
2806
|
className: `ftable-toolbar-item ftable-toolbar-item-custom ${item.buttonClass || ''}`,
|
|
2726
2807
|
parent: this.elements.toolbarDiv
|
|
2727
2808
|
});
|
|
@@ -2749,6 +2830,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2749
2830
|
if (item.text) {
|
|
2750
2831
|
FTableDOMHelper.create('span', {
|
|
2751
2832
|
text: item.text,
|
|
2833
|
+
className: `ftable-toolbar-item-text ftable-toolbar-item-custom-text ${item.buttonTextClass || ''}`,
|
|
2752
2834
|
parent: button
|
|
2753
2835
|
});
|
|
2754
2836
|
}
|
|
@@ -2818,6 +2900,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2818
2900
|
// Update sorting display
|
|
2819
2901
|
this.renderSortingInfo();
|
|
2820
2902
|
|
|
2903
|
+
this.normalizeColumnWidths();
|
|
2821
2904
|
}
|
|
2822
2905
|
|
|
2823
2906
|
buildLoadParams() {
|
|
@@ -3507,7 +3590,7 @@ class FTable extends FTableEventEmitter {
|
|
|
3507
3590
|
if (this.options.selectOnRowClick !== false) {
|
|
3508
3591
|
row.addEventListener('click', (e) => {
|
|
3509
3592
|
// input elements can't select the row, nor norowselectonclick class
|
|
3510
|
-
if (!['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(e.target.tagName) &&
|
|
3593
|
+
if (!['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA', 'A'].includes(e.target.tagName) &&
|
|
3511
3594
|
!e.target.classList.contains('norowselectonclick')) {
|
|
3512
3595
|
this.toggleRowSelection(row);
|
|
3513
3596
|
}
|
|
@@ -3900,11 +3983,8 @@ class FTable extends FTableEventEmitter {
|
|
|
3900
3983
|
}
|
|
3901
3984
|
|
|
3902
3985
|
// Public API Methods
|
|
3903
|
-
reload(
|
|
3904
|
-
|
|
3905
|
-
// Clear list cache
|
|
3906
|
-
this.clearListCache();
|
|
3907
|
-
}
|
|
3986
|
+
reload() {
|
|
3987
|
+
this.clearListCache();
|
|
3908
3988
|
return this.load();
|
|
3909
3989
|
}
|
|
3910
3990
|
|