@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.js
CHANGED
|
@@ -162,7 +162,11 @@ class FTableDOMHelper {
|
|
|
162
162
|
if (options.className) {
|
|
163
163
|
element.className = options.className;
|
|
164
164
|
}
|
|
165
|
-
|
|
165
|
+
|
|
166
|
+
if (options.style) {
|
|
167
|
+
element.style.cssText = options.style;
|
|
168
|
+
}
|
|
169
|
+
|
|
166
170
|
if (options.attributes) {
|
|
167
171
|
Object.entries(options.attributes).forEach(([key, value]) => {
|
|
168
172
|
element.setAttribute(key, value);
|
|
@@ -1498,6 +1502,50 @@ class FTable extends FTableEventEmitter {
|
|
|
1498
1502
|
|
|
1499
1503
|
// Add essential CSS if not already present
|
|
1500
1504
|
//this.addEssentialCSS();
|
|
1505
|
+
|
|
1506
|
+
// now make sure all tables have a % width
|
|
1507
|
+
this.initColumnWidths();
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
initColumnWidths() {
|
|
1511
|
+
const visibleFields = this.columnList.filter(fieldName => {
|
|
1512
|
+
const field = this.options.fields[fieldName];
|
|
1513
|
+
return field.visibility !== 'hidden';
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
const count = visibleFields.length;
|
|
1517
|
+
visibleFields.forEach(fieldName => {
|
|
1518
|
+
const field = this.options.fields[fieldName];
|
|
1519
|
+
// Use configured width or equal distribution
|
|
1520
|
+
//field.width = field.width || `${(100 / count).toFixed(2)}%`;
|
|
1521
|
+
field.width = field.width || `${(100 / count)}%`;
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
normalizeColumnWidths() {
|
|
1526
|
+
const container = this.elements.mainContainer;
|
|
1527
|
+
const visibleHeaders = this.columnList
|
|
1528
|
+
.map(fieldName => ({
|
|
1529
|
+
th: this.elements.table.querySelector(`[data-field-name="${fieldName}"]`),
|
|
1530
|
+
field: this.options.fields[fieldName]
|
|
1531
|
+
}))
|
|
1532
|
+
.filter(item => item.th && item.field.visibility !== 'hidden');
|
|
1533
|
+
|
|
1534
|
+
if (visibleHeaders.length === 0) return;
|
|
1535
|
+
|
|
1536
|
+
const totalContainerWidth = container.offsetWidth;
|
|
1537
|
+
let totalPercent = 0;
|
|
1538
|
+
|
|
1539
|
+
visibleHeaders.forEach(item => {
|
|
1540
|
+
const widthPct = (item.th.offsetWidth / totalContainerWidth) * 100;
|
|
1541
|
+
//item.field.width = `${widthPct.toFixed(2)}%`;
|
|
1542
|
+
item.field.width = `${widthPct}%`;
|
|
1543
|
+
item.th.style.width = item.field.width;
|
|
1544
|
+
totalPercent += widthPct;
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
// Optional: adjust for rounding drift
|
|
1548
|
+
// (not critical, but can help)
|
|
1501
1549
|
}
|
|
1502
1550
|
|
|
1503
1551
|
parseDefaultSorting(sortStr) {
|
|
@@ -1660,25 +1708,13 @@ class FTable extends FTableEventEmitter {
|
|
|
1660
1708
|
field.edit = true;
|
|
1661
1709
|
field.type = 'hidden';
|
|
1662
1710
|
}
|
|
1663
|
-
|
|
1664
|
-
field.visibility = 'hidden';
|
|
1665
|
-
}
|
|
1711
|
+
field.visibility = field.visibility ?? 'visible';
|
|
1666
1712
|
} else {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
}
|
|
1673
|
-
if (field.list === undefined) {
|
|
1674
|
-
field.list = true;
|
|
1675
|
-
}
|
|
1676
|
-
if (field.sorting === undefined) {
|
|
1677
|
-
field.sorting = true;
|
|
1678
|
-
}
|
|
1679
|
-
if (!field.hasOwnProperty('visibility')) {
|
|
1680
|
-
field.visibility = 'visible';
|
|
1681
|
-
}
|
|
1713
|
+
field.create = field.create ?? true;
|
|
1714
|
+
field.edit = field.edit ?? true;
|
|
1715
|
+
field.list = field.list ?? true;
|
|
1716
|
+
field.sorting = field.sorting ?? true;
|
|
1717
|
+
field.visibility = field.visibility ?? 'visible';
|
|
1682
1718
|
}
|
|
1683
1719
|
});
|
|
1684
1720
|
|
|
@@ -1691,7 +1727,7 @@ class FTable extends FTableEventEmitter {
|
|
|
1691
1727
|
// Find key field
|
|
1692
1728
|
this.keyField = this.fieldList.find(name => this.options.fields[name].key === true);
|
|
1693
1729
|
if (!this.keyField) {
|
|
1694
|
-
this.logger.
|
|
1730
|
+
this.logger.info('No key field defined');
|
|
1695
1731
|
}
|
|
1696
1732
|
}
|
|
1697
1733
|
|
|
@@ -1867,7 +1903,7 @@ class FTable extends FTableEventEmitter {
|
|
|
1867
1903
|
}
|
|
1868
1904
|
|
|
1869
1905
|
// Hide column if needed
|
|
1870
|
-
if (field.visibility === 'hidden') {
|
|
1906
|
+
if (field.visibility === 'hidden' || field.visibility === 'separator') {
|
|
1871
1907
|
FTableDOMHelper.hide(th);
|
|
1872
1908
|
}
|
|
1873
1909
|
});
|
|
@@ -1910,7 +1946,10 @@ class FTable extends FTableEventEmitter {
|
|
|
1910
1946
|
|
|
1911
1947
|
// Add empty cell for selecting column if enabled
|
|
1912
1948
|
if (this.options.selecting && this.options.selectingCheckboxes) {
|
|
1913
|
-
FTableDOMHelper.create('th', {
|
|
1949
|
+
FTableDOMHelper.create('th', {
|
|
1950
|
+
className: 'ftable-toolbarsearch-column-header',
|
|
1951
|
+
parent: searchRow
|
|
1952
|
+
});
|
|
1914
1953
|
}
|
|
1915
1954
|
|
|
1916
1955
|
// Add search input cells for data columns
|
|
@@ -2045,7 +2084,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2045
2084
|
}
|
|
2046
2085
|
|
|
2047
2086
|
// Hide search cell if column is hidden
|
|
2048
|
-
if (field.visibility === 'hidden') {
|
|
2087
|
+
if (field.visibility === 'hidden' || field.visibility === 'separator') {
|
|
2049
2088
|
FTableDOMHelper.hide(th);
|
|
2050
2089
|
}
|
|
2051
2090
|
}
|
|
@@ -2053,7 +2092,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2053
2092
|
if (this.options.toolbarsearch && this.options.toolbarreset) {
|
|
2054
2093
|
// Add reset button cell
|
|
2055
2094
|
const resetTh = FTableDOMHelper.create('th', {
|
|
2056
|
-
className: 'ftable-toolbarsearch-reset',
|
|
2095
|
+
className: 'ftable-toolbarsearch-column-header ftable-toolbarsearch-reset',
|
|
2057
2096
|
parent: searchRow
|
|
2058
2097
|
});
|
|
2059
2098
|
|
|
@@ -2170,8 +2209,18 @@ class FTable extends FTableEventEmitter {
|
|
|
2170
2209
|
this.load();
|
|
2171
2210
|
}
|
|
2172
2211
|
|
|
2212
|
+
getNextVisibleHeader(th) {
|
|
2213
|
+
const headers = Array.from(this.elements.table.querySelectorAll('thead th:not(.ftable-command-column-header, .ftable-toolbarsearch-column-header)'));
|
|
2214
|
+
const index = headers.indexOf(th);
|
|
2215
|
+
for (let i = index + 1; i < headers.length; i++) {
|
|
2216
|
+
if (headers[i].offsetParent !== null) { // visible
|
|
2217
|
+
return headers[i];
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2173
2223
|
makeColumnResizable(th, container) {
|
|
2174
|
-
// Create resize bar if it doesn't exist
|
|
2175
2224
|
if (!this.elements.resizeBar) {
|
|
2176
2225
|
this.elements.resizeBar = FTableDOMHelper.create('div', {
|
|
2177
2226
|
className: 'ftable-column-resize-bar',
|
|
@@ -2188,59 +2237,88 @@ class FTable extends FTableEventEmitter {
|
|
|
2188
2237
|
let isResizing = false;
|
|
2189
2238
|
let startX = 0;
|
|
2190
2239
|
let startWidth = 0;
|
|
2240
|
+
let containerRect;
|
|
2241
|
+
let nextTh = null;
|
|
2242
|
+
let nextStartWidth = 0;
|
|
2243
|
+
let nextField = null;
|
|
2191
2244
|
|
|
2192
2245
|
resizeHandler.addEventListener('mousedown', (e) => {
|
|
2193
2246
|
e.preventDefault();
|
|
2194
2247
|
e.stopPropagation();
|
|
2195
|
-
|
|
2248
|
+
|
|
2196
2249
|
isResizing = true;
|
|
2250
|
+
|
|
2251
|
+
// Capture layout
|
|
2252
|
+
containerRect = this.elements.mainContainer.getBoundingClientRect();
|
|
2197
2253
|
startX = e.clientX;
|
|
2198
2254
|
startWidth = th.offsetWidth;
|
|
2199
|
-
|
|
2200
|
-
//
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2255
|
+
|
|
2256
|
+
// Find next visible column
|
|
2257
|
+
nextTh = this.getNextVisibleHeader(th);
|
|
2258
|
+
if (nextTh) {
|
|
2259
|
+
nextStartWidth = nextTh.offsetWidth;
|
|
2260
|
+
const fieldName = nextTh.dataset.fieldName;
|
|
2261
|
+
nextField = this.options.fields[fieldName];
|
|
2262
|
+
} else {
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
// Position resize bar
|
|
2267
|
+
const thRect = th.getBoundingClientRect();
|
|
2268
|
+
this.elements.resizeBar.style.left = (thRect.right - containerRect.left) + 'px';
|
|
2269
|
+
this.elements.resizeBar.style.top = (thRect.top - containerRect.top) + 'px';
|
|
2206
2270
|
this.elements.resizeBar.style.height = this.elements.table.offsetHeight + 'px';
|
|
2271
|
+
|
|
2207
2272
|
FTableDOMHelper.show(this.elements.resizeBar);
|
|
2208
|
-
|
|
2273
|
+
|
|
2209
2274
|
document.addEventListener('mousemove', handleMouseMove);
|
|
2210
2275
|
document.addEventListener('mouseup', handleMouseUp);
|
|
2211
2276
|
});
|
|
2212
2277
|
|
|
2213
2278
|
const handleMouseMove = (e) => {
|
|
2214
2279
|
if (!isResizing) return;
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
const newWidth = Math.max(50, startWidth + diff); // Minimum 50px width
|
|
2218
|
-
|
|
2219
|
-
// Update resize bar position
|
|
2220
|
-
const containerRect = this.elements.mainContainer.getBoundingClientRect();
|
|
2280
|
+
|
|
2281
|
+
// Move resize bar with mouse
|
|
2221
2282
|
this.elements.resizeBar.style.left = (e.clientX - containerRect.left) + 'px';
|
|
2222
2283
|
};
|
|
2223
2284
|
|
|
2224
2285
|
const handleMouseUp = (e) => {
|
|
2225
2286
|
if (!isResizing) return;
|
|
2226
|
-
|
|
2227
2287
|
isResizing = false;
|
|
2228
|
-
|
|
2229
|
-
const
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2288
|
+
|
|
2289
|
+
const diff = e.clientX - startX; // px
|
|
2290
|
+
const totalWidth = containerRect.width;
|
|
2291
|
+
|
|
2292
|
+
// Current column new width in px
|
|
2293
|
+
const newCurrentPx = Math.max(50, startWidth + diff);
|
|
2294
|
+
const newCurrentPct = (newCurrentPx / totalWidth) * 100;
|
|
2295
|
+
|
|
2296
|
+
// Next column adjustment
|
|
2297
|
+
if (nextTh) {
|
|
2298
|
+
const newNextPx = Math.max(50, nextStartWidth - diff); // opposite delta
|
|
2299
|
+
const newNextPct = (newNextPx / totalWidth) * 100;
|
|
2300
|
+
|
|
2301
|
+
// Apply to next
|
|
2302
|
+
nextField.width = `${newNextPct.toFixed(2)}%`;
|
|
2303
|
+
nextTh.style.width = nextField.width;
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
// Apply to current
|
|
2307
|
+
const field = this.options.fields[th.dataset.fieldName];
|
|
2308
|
+
field.width = `${newCurrentPct.toFixed(2)}%`;
|
|
2309
|
+
th.style.width = field.width;
|
|
2310
|
+
|
|
2311
|
+
// Final normalization (optional, but safe)
|
|
2312
|
+
this.normalizeColumnWidths();
|
|
2313
|
+
|
|
2314
|
+
// Save
|
|
2241
2315
|
if (this.options.saveUserPreferences) {
|
|
2242
2316
|
this.saveColumnSettings();
|
|
2243
2317
|
}
|
|
2318
|
+
|
|
2319
|
+
FTableDOMHelper.hide(this.elements.resizeBar);
|
|
2320
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
2321
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
2244
2322
|
};
|
|
2245
2323
|
}
|
|
2246
2324
|
|
|
@@ -2471,8 +2549,8 @@ class FTable extends FTableEventEmitter {
|
|
|
2471
2549
|
this.subscribeOptionEvents();
|
|
2472
2550
|
|
|
2473
2551
|
// Add toolbar buttons
|
|
2474
|
-
this.createToolbarButtons();
|
|
2475
2552
|
this.createCustomToolbarItems();
|
|
2553
|
+
this.createToolbarButtons();
|
|
2476
2554
|
|
|
2477
2555
|
// Keyboard shortcuts
|
|
2478
2556
|
this.bindKeyboardEvents();
|
|
@@ -2569,6 +2647,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2569
2647
|
const field = this.options.fields[fieldName];
|
|
2570
2648
|
const isVisible = field.visibility !== 'hidden';
|
|
2571
2649
|
const isFixed = field.visibility === 'fixed';
|
|
2650
|
+
const isSeparator = field.visibility === 'separator';
|
|
2572
2651
|
const isSorted = this.isFieldSorted(fieldName);
|
|
2573
2652
|
|
|
2574
2653
|
const listItem = FTableDOMHelper.create('li', {
|
|
@@ -2581,24 +2660,32 @@ class FTable extends FTableEventEmitter {
|
|
|
2581
2660
|
parent: listItem
|
|
2582
2661
|
});
|
|
2583
2662
|
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2663
|
+
if (!isSeparator) {
|
|
2664
|
+
const checkbox = FTableDOMHelper.create('input', {
|
|
2665
|
+
attributes: {
|
|
2666
|
+
type: 'checkbox',
|
|
2667
|
+
id: `column-${fieldName}`
|
|
2668
|
+
},
|
|
2669
|
+
parent: label
|
|
2670
|
+
});
|
|
2671
|
+
checkbox.checked = isVisible;
|
|
2593
2672
|
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2673
|
+
// Disable checkbox if column is fixed or currently sorted
|
|
2674
|
+
if (isFixed || (isSorted && isVisible)) {
|
|
2675
|
+
checkbox.disabled = true;
|
|
2676
|
+
listItem.style.opacity = '0.6';
|
|
2677
|
+
}
|
|
2678
|
+
// Handle checkbox change
|
|
2679
|
+
if (!checkbox.disabled) {
|
|
2680
|
+
checkbox.addEventListener('change', () => {
|
|
2681
|
+
this.setColumnVisibility(fieldName, checkbox.checked);
|
|
2682
|
+
});
|
|
2683
|
+
}
|
|
2598
2684
|
}
|
|
2599
2685
|
|
|
2600
2686
|
const labelText = FTableDOMHelper.create('span', {
|
|
2601
2687
|
text: field.title || fieldName,
|
|
2688
|
+
style: isSeparator ? 'font-weight: bold;' : null,
|
|
2602
2689
|
parent: label
|
|
2603
2690
|
});
|
|
2604
2691
|
|
|
@@ -2613,12 +2700,6 @@ class FTable extends FTableEventEmitter {
|
|
|
2613
2700
|
sortIndicator.style.color = '#666';
|
|
2614
2701
|
}
|
|
2615
2702
|
|
|
2616
|
-
// Handle checkbox change
|
|
2617
|
-
if (!checkbox.disabled) {
|
|
2618
|
-
checkbox.addEventListener('change', () => {
|
|
2619
|
-
this.setColumnVisibility(fieldName, checkbox.checked);
|
|
2620
|
-
});
|
|
2621
|
-
}
|
|
2622
2703
|
});
|
|
2623
2704
|
}
|
|
2624
2705
|
|
|
@@ -2726,7 +2807,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2726
2807
|
if (!this.options.toolbar || !this.options.toolbar.items) return;
|
|
2727
2808
|
|
|
2728
2809
|
this.options.toolbar.items.forEach(item => {
|
|
2729
|
-
const button = FTableDOMHelper.create('
|
|
2810
|
+
const button = FTableDOMHelper.create('span', {
|
|
2730
2811
|
className: `ftable-toolbar-item ftable-toolbar-item-custom ${item.buttonClass || ''}`,
|
|
2731
2812
|
parent: this.elements.toolbarDiv
|
|
2732
2813
|
});
|
|
@@ -2754,6 +2835,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2754
2835
|
if (item.text) {
|
|
2755
2836
|
FTableDOMHelper.create('span', {
|
|
2756
2837
|
text: item.text,
|
|
2838
|
+
className: `ftable-toolbar-item-text ftable-toolbar-item-custom-text ${item.buttonTextClass || ''}`,
|
|
2757
2839
|
parent: button
|
|
2758
2840
|
});
|
|
2759
2841
|
}
|
|
@@ -2823,6 +2905,7 @@ class FTable extends FTableEventEmitter {
|
|
|
2823
2905
|
// Update sorting display
|
|
2824
2906
|
this.renderSortingInfo();
|
|
2825
2907
|
|
|
2908
|
+
this.normalizeColumnWidths();
|
|
2826
2909
|
}
|
|
2827
2910
|
|
|
2828
2911
|
buildLoadParams() {
|
|
@@ -3512,7 +3595,7 @@ class FTable extends FTableEventEmitter {
|
|
|
3512
3595
|
if (this.options.selectOnRowClick !== false) {
|
|
3513
3596
|
row.addEventListener('click', (e) => {
|
|
3514
3597
|
// input elements can't select the row, nor norowselectonclick class
|
|
3515
|
-
if (!['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(e.target.tagName) &&
|
|
3598
|
+
if (!['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA', 'A'].includes(e.target.tagName) &&
|
|
3516
3599
|
!e.target.classList.contains('norowselectonclick')) {
|
|
3517
3600
|
this.toggleRowSelection(row);
|
|
3518
3601
|
}
|
|
@@ -3905,11 +3988,8 @@ class FTable extends FTableEventEmitter {
|
|
|
3905
3988
|
}
|
|
3906
3989
|
|
|
3907
3990
|
// Public API Methods
|
|
3908
|
-
reload(
|
|
3909
|
-
|
|
3910
|
-
// Clear list cache
|
|
3911
|
-
this.clearListCache();
|
|
3912
|
-
}
|
|
3991
|
+
reload() {
|
|
3992
|
+
this.clearListCache();
|
|
3913
3993
|
return this.load();
|
|
3914
3994
|
}
|
|
3915
3995
|
|