@liedekef/ftable 1.1.49 → 1.1.50
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 +233 -87
- package/ftable.js +233 -87
- package/ftable.min.js +2 -24
- package/ftable.umd.js +233 -87
- package/package.json +1 -1
- package/themes/basic/ftable_basic.css +15 -8
- package/themes/basic/ftable_basic.min.css +1 -1
- package/themes/ftable_theme_base.less +17 -11
- package/themes/lightcolor/blue/ftable.css +15 -8
- package/themes/lightcolor/blue/ftable.min.css +1 -1
- package/themes/lightcolor/gray/ftable.css +15 -8
- package/themes/lightcolor/gray/ftable.min.css +1 -1
- package/themes/lightcolor/green/ftable.css +15 -8
- package/themes/lightcolor/green/ftable.min.css +1 -1
- package/themes/lightcolor/orange/ftable.css +15 -8
- package/themes/lightcolor/orange/ftable.min.css +1 -1
- package/themes/lightcolor/red/ftable.css +15 -8
- package/themes/lightcolor/red/ftable.min.css +1 -1
- package/themes/metro/blue/ftable.css +15 -8
- package/themes/metro/blue/ftable.min.css +1 -1
- package/themes/metro/brown/ftable.css +15 -8
- package/themes/metro/brown/ftable.min.css +1 -1
- package/themes/metro/crimson/ftable.css +15 -8
- package/themes/metro/crimson/ftable.min.css +1 -1
- package/themes/metro/darkgray/ftable.css +15 -8
- package/themes/metro/darkgray/ftable.min.css +1 -1
- package/themes/metro/darkorange/ftable.css +15 -8
- package/themes/metro/darkorange/ftable.min.css +1 -1
- package/themes/metro/green/ftable.css +15 -8
- package/themes/metro/green/ftable.min.css +1 -1
- package/themes/metro/lightgray/ftable.css +15 -8
- package/themes/metro/lightgray/ftable.min.css +1 -1
- package/themes/metro/pink/ftable.css +15 -8
- package/themes/metro/pink/ftable.min.css +1 -1
- package/themes/metro/purple/ftable.css +15 -8
- package/themes/metro/purple/ftable.min.css +1 -1
- package/themes/metro/red/ftable.css +15 -8
- package/themes/metro/red/ftable.min.css +1 -1
package/ftable.esm.js
CHANGED
|
@@ -1452,12 +1452,9 @@ class FTableFormBuilder {
|
|
|
1452
1452
|
parent: display
|
|
1453
1453
|
});
|
|
1454
1454
|
|
|
1455
|
-
//
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
parent: container,
|
|
1459
|
-
style: 'display: none;'
|
|
1460
|
-
});
|
|
1455
|
+
// Dropdown and overlay will be created on demand and appended to body
|
|
1456
|
+
let dropdown = null;
|
|
1457
|
+
let dropdownOverlay = null;
|
|
1461
1458
|
|
|
1462
1459
|
// Store selected values and checkbox references
|
|
1463
1460
|
const selectedValues = new Set(
|
|
@@ -1525,9 +1522,53 @@ class FTableFormBuilder {
|
|
|
1525
1522
|
hiddenInput.value = Array.from(selectedValues).join(',');
|
|
1526
1523
|
};
|
|
1527
1524
|
|
|
1525
|
+
// Function to close dropdown
|
|
1526
|
+
const closeDropdown = () => {
|
|
1527
|
+
if (dropdown) {
|
|
1528
|
+
dropdown.remove();
|
|
1529
|
+
dropdown = null;
|
|
1530
|
+
}
|
|
1531
|
+
if (dropdownOverlay) {
|
|
1532
|
+
dropdownOverlay.remove();
|
|
1533
|
+
dropdownOverlay = null;
|
|
1534
|
+
}
|
|
1535
|
+
if (container._cleanupHandlers) {
|
|
1536
|
+
container._cleanupHandlers();
|
|
1537
|
+
container._cleanupHandlers = null;
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
|
|
1541
|
+
// Function to position dropdown
|
|
1542
|
+
const positionDropdown = () => {
|
|
1543
|
+
if (!dropdown) return;
|
|
1544
|
+
|
|
1545
|
+
const rect = display.getBoundingClientRect();
|
|
1546
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
1547
|
+
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
1548
|
+
|
|
1549
|
+
let left = rect.left + scrollLeft;
|
|
1550
|
+
let top = rect.bottom + scrollTop + 4; // 4px gap
|
|
1551
|
+
|
|
1552
|
+
dropdown.style.position = 'absolute';
|
|
1553
|
+
dropdown.style.left = `${left}px`;
|
|
1554
|
+
dropdown.style.top = `${top}px`;
|
|
1555
|
+
dropdown.style.width = `${rect.width}px`;
|
|
1556
|
+
dropdown.style.minWidth = `${rect.width}px`;
|
|
1557
|
+
dropdown.style.boxSizing = 'border-box';
|
|
1558
|
+
dropdown.style.zIndex = '10000';
|
|
1559
|
+
|
|
1560
|
+
// Adjust horizontal position if needed
|
|
1561
|
+
const dropdownRect = dropdown.getBoundingClientRect();
|
|
1562
|
+
const viewportWidth = window.innerWidth;
|
|
1563
|
+
if (dropdownRect.right > viewportWidth) {
|
|
1564
|
+
left = Math.max(10, viewportWidth - dropdownRect.width - 10);
|
|
1565
|
+
dropdown.style.left = `${left}px`;
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1528
1569
|
// Populate options
|
|
1529
1570
|
const populateOptions = () => {
|
|
1530
|
-
if (!field.options) return;
|
|
1571
|
+
if (!field.options || !dropdown) return;
|
|
1531
1572
|
|
|
1532
1573
|
const options = Array.isArray(field.options) ? field.options :
|
|
1533
1574
|
Object.entries(field.options).map(([k, v]) => ({Value: k, DisplayText: v}));
|
|
@@ -1584,26 +1625,74 @@ class FTableFormBuilder {
|
|
|
1584
1625
|
// Toggle dropdown
|
|
1585
1626
|
const toggleDropdown = (e) => {
|
|
1586
1627
|
if (e) e.stopPropagation();
|
|
1587
|
-
const isVisible = dropdown.style.display !== 'none';
|
|
1588
|
-
dropdown.style.display = isVisible ? 'none' : 'block';
|
|
1589
1628
|
|
|
1590
|
-
if (
|
|
1591
|
-
//
|
|
1592
|
-
|
|
1593
|
-
|
|
1629
|
+
if (dropdown) {
|
|
1630
|
+
// Dropdown is open, close it
|
|
1631
|
+
closeDropdown();
|
|
1632
|
+
} else {
|
|
1633
|
+
// Close any other open multiselect dropdowns
|
|
1634
|
+
document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => dd.remove());
|
|
1635
|
+
document.querySelectorAll('.ftable-multiselect-overlay').forEach(ov => ov.remove());
|
|
1636
|
+
|
|
1637
|
+
// Create overlay
|
|
1638
|
+
dropdownOverlay = FTableDOMHelper.create('div', {
|
|
1639
|
+
className: 'ftable-multiselect-overlay',
|
|
1640
|
+
parent: document.body
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
// Create dropdown
|
|
1644
|
+
dropdown = FTableDOMHelper.create('div', {
|
|
1645
|
+
className: 'ftable-multiselect-dropdown',
|
|
1646
|
+
parent: document.body
|
|
1594
1647
|
});
|
|
1648
|
+
|
|
1649
|
+
// Populate options
|
|
1650
|
+
populateOptions();
|
|
1651
|
+
|
|
1652
|
+
// Position dropdown
|
|
1653
|
+
positionDropdown();
|
|
1654
|
+
|
|
1655
|
+
// Handle clicks outside
|
|
1656
|
+
dropdownOverlay.addEventListener('click', (event) => {
|
|
1657
|
+
if (event.target === dropdownOverlay) {
|
|
1658
|
+
closeDropdown();
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
// Reposition on scroll/resize
|
|
1663
|
+
const repositionHandler = () => positionDropdown();
|
|
1664
|
+
window.addEventListener('scroll', repositionHandler, true);
|
|
1665
|
+
window.addEventListener('resize', repositionHandler);
|
|
1666
|
+
|
|
1667
|
+
// Store cleanup function
|
|
1668
|
+
container._cleanupHandlers = () => {
|
|
1669
|
+
window.removeEventListener('scroll', repositionHandler, true);
|
|
1670
|
+
window.removeEventListener('resize', repositionHandler);
|
|
1671
|
+
};
|
|
1595
1672
|
}
|
|
1596
1673
|
};
|
|
1597
1674
|
|
|
1598
1675
|
display.addEventListener('click', toggleDropdown);
|
|
1599
1676
|
toggleBtn.addEventListener('click', toggleDropdown);
|
|
1600
1677
|
|
|
1601
|
-
//
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1678
|
+
// Clean up when container is removed from DOM
|
|
1679
|
+
const observer = new MutationObserver((mutations) => {
|
|
1680
|
+
mutations.forEach((mutation) => {
|
|
1681
|
+
mutation.removedNodes.forEach((node) => {
|
|
1682
|
+
if (node === container || node.contains && node.contains(container)) {
|
|
1683
|
+
closeDropdown();
|
|
1684
|
+
observer.disconnect();
|
|
1685
|
+
}
|
|
1686
|
+
});
|
|
1687
|
+
});
|
|
1606
1688
|
});
|
|
1689
|
+
|
|
1690
|
+
// Start observing once container is in the DOM
|
|
1691
|
+
setTimeout(() => {
|
|
1692
|
+
if (container.parentNode) {
|
|
1693
|
+
observer.observe(container.parentNode, { childList: true, subtree: true });
|
|
1694
|
+
}
|
|
1695
|
+
}, 0);
|
|
1607
1696
|
|
|
1608
1697
|
// Initialize
|
|
1609
1698
|
populateOptions();
|
|
@@ -1931,9 +2020,6 @@ class FTable extends FTableEventEmitter {
|
|
|
1931
2020
|
this.updateSortingHeaders();
|
|
1932
2021
|
this.renderSortingInfo();
|
|
1933
2022
|
|
|
1934
|
-
// Add essential CSS if not already present
|
|
1935
|
-
//this.addEssentialCSS();
|
|
1936
|
-
|
|
1937
2023
|
// now make sure all tables have a % width
|
|
1938
2024
|
this.initColumnWidths();
|
|
1939
2025
|
}
|
|
@@ -2013,40 +2099,6 @@ class FTable extends FTableEventEmitter {
|
|
|
2013
2099
|
return result;
|
|
2014
2100
|
}
|
|
2015
2101
|
|
|
2016
|
-
addEssentialCSS() {
|
|
2017
|
-
// Check if our CSS is already added
|
|
2018
|
-
if (document.querySelector('#ftable-essential-css')) return;
|
|
2019
|
-
|
|
2020
|
-
const css = `
|
|
2021
|
-
.ftable-row-animation {
|
|
2022
|
-
transition: background-color 0.3s ease;
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
.ftable-row-added {
|
|
2026
|
-
background-color: #d4edda !important;
|
|
2027
|
-
}
|
|
2028
|
-
|
|
2029
|
-
.ftable-row-edited {
|
|
2030
|
-
background-color: #d1ecf1 !important;
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
.ftable-row-deleted {
|
|
2034
|
-
opacity: 0;
|
|
2035
|
-
transform: translateY(-10px);
|
|
2036
|
-
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
.ftable-toolbarsearch {
|
|
2040
|
-
width: 90%;
|
|
2041
|
-
}
|
|
2042
|
-
`;
|
|
2043
|
-
|
|
2044
|
-
const style = document.createElement('style');
|
|
2045
|
-
style.id = 'ftable-essential-css';
|
|
2046
|
-
style.textContent = css;
|
|
2047
|
-
document.head.appendChild(style);
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2050
2102
|
createPagingUI() {
|
|
2051
2103
|
this.elements.bottomPanel = FTableDOMHelper.create('div', {
|
|
2052
2104
|
className: 'ftable-bottom-panel',
|
|
@@ -2703,12 +2755,9 @@ class FTable extends FTableEventEmitter {
|
|
|
2703
2755
|
parent: display
|
|
2704
2756
|
});
|
|
2705
2757
|
|
|
2706
|
-
//
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
parent: container,
|
|
2710
|
-
style: 'display: none;'
|
|
2711
|
-
});
|
|
2758
|
+
// Dropdown and overlay will be created on demand and appended to body
|
|
2759
|
+
let dropdown = null;
|
|
2760
|
+
let dropdownOverlay = null;
|
|
2712
2761
|
|
|
2713
2762
|
// Store selected values and checkbox references
|
|
2714
2763
|
const selectedValues = new Set();
|
|
@@ -2775,18 +2824,53 @@ class FTable extends FTableEventEmitter {
|
|
|
2775
2824
|
}
|
|
2776
2825
|
};
|
|
2777
2826
|
|
|
2778
|
-
//
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
}
|
|
2784
|
-
|
|
2827
|
+
// Function to close dropdown
|
|
2828
|
+
const closeDropdown = () => {
|
|
2829
|
+
if (dropdown) {
|
|
2830
|
+
dropdown.remove();
|
|
2831
|
+
dropdown = null;
|
|
2832
|
+
}
|
|
2833
|
+
if (dropdownOverlay) {
|
|
2834
|
+
dropdownOverlay.remove();
|
|
2835
|
+
dropdownOverlay = null;
|
|
2836
|
+
}
|
|
2837
|
+
if (container._cleanupHandlers) {
|
|
2838
|
+
container._cleanupHandlers();
|
|
2839
|
+
container._cleanupHandlers = null;
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
|
|
2843
|
+
// Function to position dropdown
|
|
2844
|
+
const positionDropdown = () => {
|
|
2845
|
+
if (!dropdown) return;
|
|
2846
|
+
|
|
2847
|
+
const rect = display.getBoundingClientRect();
|
|
2848
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
2849
|
+
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
2850
|
+
|
|
2851
|
+
let left = rect.left + scrollLeft;
|
|
2852
|
+
let top = rect.bottom + scrollTop + 4; // 4px gap
|
|
2853
|
+
|
|
2854
|
+
dropdown.style.position = 'absolute';
|
|
2855
|
+
dropdown.style.left = `${left}px`;
|
|
2856
|
+
dropdown.style.top = `${top}px`;
|
|
2857
|
+
dropdown.style.width = `${rect.width}px`;
|
|
2858
|
+
dropdown.style.minWidth = `${rect.width}px`;
|
|
2859
|
+
dropdown.style.boxSizing = 'border-box';
|
|
2860
|
+
dropdown.style.zIndex = '10000';
|
|
2861
|
+
|
|
2862
|
+
// Adjust horizontal position if needed
|
|
2863
|
+
const dropdownRect = dropdown.getBoundingClientRect();
|
|
2864
|
+
const viewportWidth = window.innerWidth;
|
|
2865
|
+
if (dropdownRect.right > viewportWidth) {
|
|
2866
|
+
left = Math.max(10, viewportWidth - dropdownRect.width - 10);
|
|
2867
|
+
dropdown.style.left = `${left}px`;
|
|
2868
|
+
}
|
|
2785
2869
|
};
|
|
2786
2870
|
|
|
2787
2871
|
// Populate options in both hidden select and dropdown
|
|
2788
2872
|
const populateOptions = () => {
|
|
2789
|
-
if (!optionsSource) return;
|
|
2873
|
+
if (!optionsSource || !dropdown) return;
|
|
2790
2874
|
|
|
2791
2875
|
const options = Array.isArray(optionsSource) ? optionsSource :
|
|
2792
2876
|
Object.entries(optionsSource).map(([k, v]) => ({Value: k, DisplayText: v}));
|
|
@@ -2802,12 +2886,14 @@ class FTable extends FTableEventEmitter {
|
|
|
2802
2886
|
|
|
2803
2887
|
const optText = option.DisplayText || option.text || option;
|
|
2804
2888
|
|
|
2805
|
-
// Add to hidden select
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2889
|
+
// Add to hidden select (only once)
|
|
2890
|
+
if (!hiddenSelect.querySelector(`option[value="${optValue}"]`)) {
|
|
2891
|
+
FTableDOMHelper.create('option', {
|
|
2892
|
+
value: optValue,
|
|
2893
|
+
textContent: optText,
|
|
2894
|
+
parent: hiddenSelect
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2811
2897
|
|
|
2812
2898
|
// Add to visual dropdown
|
|
2813
2899
|
const optionDiv = FTableDOMHelper.create('div', {
|
|
@@ -2821,6 +2907,9 @@ class FTable extends FTableEventEmitter {
|
|
|
2821
2907
|
parent: optionDiv
|
|
2822
2908
|
});
|
|
2823
2909
|
|
|
2910
|
+
// Set initial checked state
|
|
2911
|
+
checkbox.checked = selectedValues.has(optValue.toString());
|
|
2912
|
+
|
|
2824
2913
|
// Store checkbox reference
|
|
2825
2914
|
checkboxMap.set(optValue.toString(), checkbox);
|
|
2826
2915
|
|
|
@@ -2850,29 +2939,86 @@ class FTable extends FTableEventEmitter {
|
|
|
2850
2939
|
// Toggle dropdown
|
|
2851
2940
|
const toggleDropdown = (e) => {
|
|
2852
2941
|
if (e) e.stopPropagation();
|
|
2853
|
-
const isVisible = dropdown.style.display !== 'none';
|
|
2854
|
-
dropdown.style.display = isVisible ? 'none' : 'block';
|
|
2855
2942
|
|
|
2856
|
-
if (
|
|
2857
|
-
//
|
|
2858
|
-
|
|
2859
|
-
|
|
2943
|
+
if (dropdown) {
|
|
2944
|
+
// Dropdown is open, close it
|
|
2945
|
+
closeDropdown();
|
|
2946
|
+
} else {
|
|
2947
|
+
// Close any other open multiselect dropdowns
|
|
2948
|
+
document.querySelectorAll('.ftable-multiselect-dropdown').forEach(dd => dd.remove());
|
|
2949
|
+
document.querySelectorAll('.ftable-multiselect-overlay').forEach(ov => ov.remove());
|
|
2950
|
+
|
|
2951
|
+
// Create overlay
|
|
2952
|
+
dropdownOverlay = FTableDOMHelper.create('div', {
|
|
2953
|
+
className: 'ftable-multiselect-overlay',
|
|
2954
|
+
parent: document.body
|
|
2860
2955
|
});
|
|
2956
|
+
|
|
2957
|
+
// Create dropdown
|
|
2958
|
+
dropdown = FTableDOMHelper.create('div', {
|
|
2959
|
+
className: 'ftable-multiselect-dropdown',
|
|
2960
|
+
parent: document.body
|
|
2961
|
+
});
|
|
2962
|
+
|
|
2963
|
+
// Populate options
|
|
2964
|
+
populateOptions();
|
|
2965
|
+
|
|
2966
|
+
// Position dropdown
|
|
2967
|
+
positionDropdown();
|
|
2968
|
+
|
|
2969
|
+
// Handle clicks outside
|
|
2970
|
+
dropdownOverlay.addEventListener('click', (event) => {
|
|
2971
|
+
if (event.target === dropdownOverlay) {
|
|
2972
|
+
closeDropdown();
|
|
2973
|
+
}
|
|
2974
|
+
});
|
|
2975
|
+
|
|
2976
|
+
// Reposition on scroll/resize
|
|
2977
|
+
const repositionHandler = () => positionDropdown();
|
|
2978
|
+
window.addEventListener('scroll', repositionHandler, true);
|
|
2979
|
+
window.addEventListener('resize', repositionHandler);
|
|
2980
|
+
|
|
2981
|
+
// Store cleanup function
|
|
2982
|
+
container._cleanupHandlers = () => {
|
|
2983
|
+
window.removeEventListener('scroll', repositionHandler, true);
|
|
2984
|
+
window.removeEventListener('resize', repositionHandler);
|
|
2985
|
+
};
|
|
2861
2986
|
}
|
|
2862
2987
|
};
|
|
2863
2988
|
|
|
2864
2989
|
display.addEventListener('click', toggleDropdown);
|
|
2865
2990
|
toggleBtn.addEventListener('click', toggleDropdown);
|
|
2866
2991
|
|
|
2867
|
-
//
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2992
|
+
// Add reset method to container
|
|
2993
|
+
container.resetMultiSelect = () => {
|
|
2994
|
+
selectedValues.clear();
|
|
2995
|
+
checkboxMap.forEach(checkbox => {
|
|
2996
|
+
checkbox.checked = false;
|
|
2997
|
+
});
|
|
2998
|
+
closeDropdown();
|
|
2999
|
+
updateDisplay();
|
|
3000
|
+
};
|
|
3001
|
+
|
|
3002
|
+
// Clean up when container is removed from DOM
|
|
3003
|
+
const observer = new MutationObserver((mutations) => {
|
|
3004
|
+
mutations.forEach((mutation) => {
|
|
3005
|
+
mutation.removedNodes.forEach((node) => {
|
|
3006
|
+
if (node === container || node.contains && node.contains(container)) {
|
|
3007
|
+
closeDropdown();
|
|
3008
|
+
observer.disconnect();
|
|
3009
|
+
}
|
|
3010
|
+
});
|
|
3011
|
+
});
|
|
2872
3012
|
});
|
|
3013
|
+
|
|
3014
|
+
// Start observing once container is in the DOM
|
|
3015
|
+
setTimeout(() => {
|
|
3016
|
+
if (container.parentNode) {
|
|
3017
|
+
observer.observe(container.parentNode, { childList: true, subtree: true });
|
|
3018
|
+
}
|
|
3019
|
+
}, 0);
|
|
2873
3020
|
|
|
2874
3021
|
// Initialize
|
|
2875
|
-
populateOptions();
|
|
2876
3022
|
updateDisplay();
|
|
2877
3023
|
|
|
2878
3024
|
return container;
|