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