@visns-studio/visns-components 5.9.0 → 5.9.2
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/package.json
CHANGED
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
87
87
|
},
|
|
88
88
|
"name": "@visns-studio/visns-components",
|
|
89
|
-
"version": "5.9.
|
|
89
|
+
"version": "5.9.2",
|
|
90
90
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
91
91
|
"main": "src/index.js",
|
|
92
92
|
"files": [
|
|
@@ -86,6 +86,101 @@ import Download from './Download';
|
|
|
86
86
|
import Form from './Form';
|
|
87
87
|
import _ from 'lodash';
|
|
88
88
|
|
|
89
|
+
// CSS styles to ensure consistent group header heights with compact design and click indicators
|
|
90
|
+
const groupHeaderStyles = `
|
|
91
|
+
.datagrid-fixed-group-headers .group-header-fixed-height {
|
|
92
|
+
height: 34px !important;
|
|
93
|
+
min-height: 34px !important;
|
|
94
|
+
max-height: 34px !important;
|
|
95
|
+
box-sizing: border-box !important;
|
|
96
|
+
line-height: 1.1 !important;
|
|
97
|
+
flex-shrink: 0 !important;
|
|
98
|
+
overflow: hidden !important;
|
|
99
|
+
transition: background-color 0.2s ease, box-shadow 0.2s ease !important;
|
|
100
|
+
cursor: pointer !important;
|
|
101
|
+
position: relative !important;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Hover effect for entire group header */
|
|
105
|
+
.datagrid-fixed-group-headers .group-header-fixed-height:hover {
|
|
106
|
+
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%) !important;
|
|
107
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important;
|
|
108
|
+
transform: translateY(-1px) !important;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Click indicator overlay */
|
|
112
|
+
.datagrid-fixed-group-headers .group-header-fixed-height::after {
|
|
113
|
+
content: '' !important;
|
|
114
|
+
position: absolute !important;
|
|
115
|
+
top: 0 !important;
|
|
116
|
+
left: 0 !important;
|
|
117
|
+
right: 0 !important;
|
|
118
|
+
bottom: 0 !important;
|
|
119
|
+
background: rgba(59, 130, 246, 0.05) !important;
|
|
120
|
+
opacity: 0 !important;
|
|
121
|
+
transition: opacity 0.2s ease !important;
|
|
122
|
+
pointer-events: none !important;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.datagrid-fixed-group-headers .group-header-fixed-height:hover::after {
|
|
126
|
+
opacity: 1 !important;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row {
|
|
130
|
+
height: 34px !important;
|
|
131
|
+
min-height: 34px !important;
|
|
132
|
+
max-height: 34px !important;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row .InovuaReactDataGrid__group-cell {
|
|
136
|
+
height: 34px !important;
|
|
137
|
+
min-height: 34px !important;
|
|
138
|
+
max-height: 34px !important;
|
|
139
|
+
padding: 0 !important;
|
|
140
|
+
overflow: hidden !important;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row .InovuaReactDataGrid__group-cell > div {
|
|
144
|
+
height: 34px !important;
|
|
145
|
+
min-height: 34px !important;
|
|
146
|
+
max-height: 34px !important;
|
|
147
|
+
overflow: hidden !important;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Ensure group headers maintain consistent styling during expand/collapse */
|
|
151
|
+
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row--collapsed .group-header-fixed-height,
|
|
152
|
+
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row--expanded .group-header-fixed-height {
|
|
153
|
+
height: 34px !important;
|
|
154
|
+
min-height: 34px !important;
|
|
155
|
+
max-height: 34px !important;
|
|
156
|
+
transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease !important;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Force consistent height for all group headers regardless of content */
|
|
160
|
+
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row[data-group-by] {
|
|
161
|
+
height: 34px !important;
|
|
162
|
+
min-height: 34px !important;
|
|
163
|
+
max-height: 34px !important;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row[data-group-by] .InovuaReactDataGrid__group-cell {
|
|
167
|
+
height: 34px !important;
|
|
168
|
+
min-height: 34px !important;
|
|
169
|
+
max-height: 34px !important;
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
|
|
173
|
+
// Inject styles into the document head
|
|
174
|
+
if (typeof document !== 'undefined') {
|
|
175
|
+
const styleId = 'datagrid-group-header-styles';
|
|
176
|
+
if (!document.getElementById(styleId)) {
|
|
177
|
+
const style = document.createElement('style');
|
|
178
|
+
style.id = styleId;
|
|
179
|
+
style.textContent = groupHeaderStyles;
|
|
180
|
+
document.head.appendChild(style);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
89
184
|
const loadData = async (
|
|
90
185
|
{ skip, limit, sortInfo, filterValue },
|
|
91
186
|
search,
|
|
@@ -488,6 +583,75 @@ const DataGrid = forwardRef(
|
|
|
488
583
|
/** Table States */
|
|
489
584
|
const dataRef = useRef(null);
|
|
490
585
|
const [dSearch, setDSearch] = useState('');
|
|
586
|
+
|
|
587
|
+
/** Group expansion state */
|
|
588
|
+
const [collapsedGroups, setCollapsedGroups] = useState({});
|
|
589
|
+
|
|
590
|
+
/** Force re-render trigger for group header consistency */
|
|
591
|
+
const [groupRenderKey, setGroupRenderKey] = useState(0);
|
|
592
|
+
|
|
593
|
+
/** Prevent rapid group selection clicks */
|
|
594
|
+
const groupClickTimeoutRef = useRef(null);
|
|
595
|
+
|
|
596
|
+
// Effect to ensure group headers maintain consistent styling after expand/collapse
|
|
597
|
+
useEffect(() => {
|
|
598
|
+
if (ajaxSetting?.groupBy) {
|
|
599
|
+
// Small delay to allow ReactDataGrid to complete its rendering
|
|
600
|
+
const timer = setTimeout(() => {
|
|
601
|
+
// Force a re-render to ensure consistent styling
|
|
602
|
+
setGroupRenderKey((prev) => prev + 1);
|
|
603
|
+
|
|
604
|
+
// Apply styles to any group headers that might have been missed
|
|
605
|
+
const groupHeaders = document.querySelectorAll(
|
|
606
|
+
'.group-header-fixed-height'
|
|
607
|
+
);
|
|
608
|
+
groupHeaders.forEach((header) => {
|
|
609
|
+
header.style.height = '34px';
|
|
610
|
+
header.style.minHeight = '34px';
|
|
611
|
+
header.style.maxHeight = '34px';
|
|
612
|
+
header.style.boxSizing = 'border-box';
|
|
613
|
+
header.style.lineHeight = '1.1';
|
|
614
|
+
header.style.flexShrink = '0';
|
|
615
|
+
header.style.overflow = 'hidden';
|
|
616
|
+
header.style.cursor = 'pointer';
|
|
617
|
+
header.style.position = 'relative';
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// Also apply to ReactDataGrid group rows to fix height inconsistency
|
|
621
|
+
const groupRows = document.querySelectorAll(
|
|
622
|
+
'.datagrid-fixed-group-headers .InovuaReactDataGrid__group-row'
|
|
623
|
+
);
|
|
624
|
+
groupRows.forEach((row) => {
|
|
625
|
+
row.style.height = '34px';
|
|
626
|
+
row.style.minHeight = '34px';
|
|
627
|
+
row.style.maxHeight = '34px';
|
|
628
|
+
|
|
629
|
+
// Apply to group cells within the row
|
|
630
|
+
const groupCells = row.querySelectorAll(
|
|
631
|
+
'.InovuaReactDataGrid__group-cell'
|
|
632
|
+
);
|
|
633
|
+
groupCells.forEach((cell) => {
|
|
634
|
+
cell.style.height = '34px';
|
|
635
|
+
cell.style.minHeight = '34px';
|
|
636
|
+
cell.style.maxHeight = '34px';
|
|
637
|
+
cell.style.padding = '0';
|
|
638
|
+
cell.style.overflow = 'hidden';
|
|
639
|
+
|
|
640
|
+
// Apply to div elements within group cells
|
|
641
|
+
const divs = cell.querySelectorAll('div');
|
|
642
|
+
divs.forEach((div) => {
|
|
643
|
+
div.style.height = '34px';
|
|
644
|
+
div.style.minHeight = '34px';
|
|
645
|
+
div.style.maxHeight = '34px';
|
|
646
|
+
div.style.overflow = 'hidden';
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
}, 50);
|
|
651
|
+
|
|
652
|
+
return () => clearTimeout(timer);
|
|
653
|
+
}
|
|
654
|
+
}, [collapsedGroups, ajaxSetting?.groupBy]);
|
|
491
655
|
const dataSource = useCallback(
|
|
492
656
|
(params) => {
|
|
493
657
|
const sqlResult = loadData(params, dSearch, ajaxSetting);
|
|
@@ -520,7 +684,7 @@ const DataGrid = forwardRef(
|
|
|
520
684
|
|
|
521
685
|
return sqlResult;
|
|
522
686
|
},
|
|
523
|
-
[ajaxSetting, dSearch]
|
|
687
|
+
[ajaxSetting, dSearch, collapsedGroups]
|
|
524
688
|
);
|
|
525
689
|
const [filterDataSource, setFilterDataSource] = useState([]);
|
|
526
690
|
const [filterValue, setFilterValue] = useState([]);
|
|
@@ -677,9 +841,31 @@ const DataGrid = forwardRef(
|
|
|
677
841
|
if (param.selected === true) {
|
|
678
842
|
// Select all rows - this happens when header checkbox is clicked to select all
|
|
679
843
|
const s = {};
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
844
|
+
|
|
845
|
+
// Always prefer dataRef.current as it contains the most reliable data
|
|
846
|
+
// especially when grouping is active
|
|
847
|
+
if (dataRef.current && Array.isArray(dataRef.current)) {
|
|
848
|
+
dataRef.current.forEach((obj) => {
|
|
849
|
+
if (
|
|
850
|
+
obj &&
|
|
851
|
+
obj.id !== undefined &&
|
|
852
|
+
obj.id !== null
|
|
853
|
+
) {
|
|
854
|
+
s[obj.id] = obj;
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
} else if (param.data && Array.isArray(param.data)) {
|
|
858
|
+
// Fallback to param.data if dataRef.current is not available
|
|
859
|
+
param.data.forEach((obj) => {
|
|
860
|
+
if (
|
|
861
|
+
obj &&
|
|
862
|
+
obj.id !== undefined &&
|
|
863
|
+
obj.id !== null
|
|
864
|
+
) {
|
|
865
|
+
s[obj.id] = obj;
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}
|
|
683
869
|
setSelected(s);
|
|
684
870
|
// Update parent component's rowsSelected state
|
|
685
871
|
if (setRowsSelected) {
|
|
@@ -699,17 +885,148 @@ const DataGrid = forwardRef(
|
|
|
699
885
|
} else if (typeof param.selected === 'object') {
|
|
700
886
|
// Individual row selection/deselection - ReactDataGrid sends the complete new selection state
|
|
701
887
|
// ReactDataGrid sends the complete new selection state, not just the changed row
|
|
702
|
-
//
|
|
703
|
-
|
|
888
|
+
// Clean the selection to remove any group objects or invalid entries
|
|
889
|
+
const cleanSelection = {};
|
|
890
|
+
Object.keys(param.selected).forEach((key) => {
|
|
891
|
+
const item = param.selected[key];
|
|
892
|
+
// Only keep items that have valid IDs and are not group objects
|
|
893
|
+
if (
|
|
894
|
+
key !== 'undefined' &&
|
|
895
|
+
key !== 'null' &&
|
|
896
|
+
item &&
|
|
897
|
+
!item.__group &&
|
|
898
|
+
item.id !== undefined
|
|
899
|
+
) {
|
|
900
|
+
cleanSelection[key] = item;
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
setSelected(cleanSelection);
|
|
704
905
|
// Update parent component's rowsSelected state
|
|
705
906
|
if (setRowsSelected) {
|
|
706
|
-
setRowsSelected(
|
|
907
|
+
setRowsSelected(cleanSelection);
|
|
707
908
|
}
|
|
708
909
|
}
|
|
709
910
|
},
|
|
710
911
|
[selected, setRowsSelected]
|
|
711
912
|
);
|
|
712
913
|
|
|
914
|
+
// Group selection functions
|
|
915
|
+
const handleGroupSelectionChange = useCallback(
|
|
916
|
+
(groupValue, isChecked) => {
|
|
917
|
+
if (!dataRef.current || !ajaxSetting?.groupBy) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const groupField = ajaxSetting.groupBy[0];
|
|
922
|
+
const groupRows = dataRef.current.filter(
|
|
923
|
+
(row) => row[groupField] === groupValue
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
// Clean the selected state - remove any invalid entries
|
|
927
|
+
const cleanSelected = {};
|
|
928
|
+
Object.keys(selected).forEach((key) => {
|
|
929
|
+
const item = selected[key];
|
|
930
|
+
// Only keep items that have valid IDs and are not group objects
|
|
931
|
+
if (
|
|
932
|
+
key !== 'undefined' &&
|
|
933
|
+
key !== 'null' &&
|
|
934
|
+
item &&
|
|
935
|
+
!item.__group &&
|
|
936
|
+
item.id !== undefined
|
|
937
|
+
) {
|
|
938
|
+
cleanSelected[key] = item;
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
const newSelected = { ...cleanSelected };
|
|
942
|
+
|
|
943
|
+
if (isChecked) {
|
|
944
|
+
// Select all rows in the group
|
|
945
|
+
groupRows.forEach((row) => {
|
|
946
|
+
if (row && row.id !== undefined && row.id !== null) {
|
|
947
|
+
newSelected[row.id] = row;
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
} else {
|
|
951
|
+
// Deselect all rows in the group
|
|
952
|
+
groupRows.forEach((row) => {
|
|
953
|
+
if (row && row.id !== undefined && row.id !== null) {
|
|
954
|
+
delete newSelected[row.id];
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// Update the selection state
|
|
960
|
+
setSelected(newSelected);
|
|
961
|
+
if (setRowsSelected) {
|
|
962
|
+
setRowsSelected(newSelected);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// Force ReactDataGrid to update its selection using the grid API
|
|
966
|
+
setTimeout(() => {
|
|
967
|
+
if (gridRef.current && gridRef.current.setSelected) {
|
|
968
|
+
gridRef.current.setSelected(newSelected);
|
|
969
|
+
} else if (gridRef.current) {
|
|
970
|
+
// Try alternative methods
|
|
971
|
+
if (gridRef.current.updateSelected) {
|
|
972
|
+
gridRef.current.updateSelected(newSelected);
|
|
973
|
+
} else if (gridRef.current.setSelection) {
|
|
974
|
+
gridRef.current.setSelection(newSelected);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}, 10);
|
|
978
|
+
},
|
|
979
|
+
[selected, setRowsSelected, ajaxSetting]
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
const isGroupSelected = useCallback(
|
|
983
|
+
(groupValue) => {
|
|
984
|
+
if (!dataRef.current || !ajaxSetting?.groupBy) {
|
|
985
|
+
return false;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Clean the selected state first
|
|
989
|
+
const cleanSelected = {};
|
|
990
|
+
Object.keys(selected).forEach((key) => {
|
|
991
|
+
const item = selected[key];
|
|
992
|
+
if (
|
|
993
|
+
key !== 'undefined' &&
|
|
994
|
+
key !== 'null' &&
|
|
995
|
+
item &&
|
|
996
|
+
!item.__group &&
|
|
997
|
+
item.id !== undefined
|
|
998
|
+
) {
|
|
999
|
+
cleanSelected[key] = item;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
const groupField = ajaxSetting.groupBy[0];
|
|
1004
|
+
const groupRows = dataRef.current.filter(
|
|
1005
|
+
(row) => row[groupField] === groupValue
|
|
1006
|
+
);
|
|
1007
|
+
|
|
1008
|
+
if (groupRows.length === 0) {
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// Filter out rows with invalid IDs and check if all valid rows are selected
|
|
1013
|
+
const validRows = groupRows.filter(
|
|
1014
|
+
(row) => row && row.id !== undefined && row.id !== null
|
|
1015
|
+
);
|
|
1016
|
+
|
|
1017
|
+
if (validRows.length === 0) {
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const allSelected = validRows.every(
|
|
1022
|
+
(row) => cleanSelected[row.id]
|
|
1023
|
+
);
|
|
1024
|
+
|
|
1025
|
+
return allSelected;
|
|
1026
|
+
},
|
|
1027
|
+
[selected, ajaxSetting]
|
|
1028
|
+
);
|
|
1029
|
+
|
|
713
1030
|
const findUpdatedFilter = (filters, currentValues) => {
|
|
714
1031
|
for (const filter of filters) {
|
|
715
1032
|
const matchingCurrentValue = currentValues.find(
|
|
@@ -1343,6 +1660,7 @@ const DataGrid = forwardRef(
|
|
|
1343
1660
|
}
|
|
1344
1661
|
} else {
|
|
1345
1662
|
// For non-checkbox columns, handle normal row clicks
|
|
1663
|
+
// But do NOT trigger checkbox selection
|
|
1346
1664
|
onRowClick(rowProps, event);
|
|
1347
1665
|
|
|
1348
1666
|
// Call the original onClick handler
|
|
@@ -1352,7 +1670,7 @@ const DataGrid = forwardRef(
|
|
|
1352
1670
|
}
|
|
1353
1671
|
};
|
|
1354
1672
|
},
|
|
1355
|
-
[selected, setRowsSelected]
|
|
1673
|
+
[selected, setRowsSelected, onRowClick]
|
|
1356
1674
|
);
|
|
1357
1675
|
|
|
1358
1676
|
const exportTrigger = async () => {
|
|
@@ -1802,6 +2120,82 @@ const DataGrid = forwardRef(
|
|
|
1802
2120
|
}
|
|
1803
2121
|
}, [selected]);
|
|
1804
2122
|
|
|
2123
|
+
// Add styling to grouped column header with enhanced debugging
|
|
2124
|
+
useEffect(() => {
|
|
2125
|
+
if (ajaxSetting?.groupBy && ajaxSetting.groupBy.length > 0) {
|
|
2126
|
+
const groupedColumnName = ajaxSetting.groupBy[0];
|
|
2127
|
+
|
|
2128
|
+
// Multiple attempts to find and style the grouped column header
|
|
2129
|
+
const applyGroupedStyling = () => {
|
|
2130
|
+
const headerCells = document.querySelectorAll(
|
|
2131
|
+
'.InovuaReactDataGrid__header-cell'
|
|
2132
|
+
);
|
|
2133
|
+
|
|
2134
|
+
headerCells.forEach((cell) => {
|
|
2135
|
+
const cellContent = cell.textContent?.trim();
|
|
2136
|
+
const dataColumnId =
|
|
2137
|
+
cell.getAttribute('data-column-id');
|
|
2138
|
+
|
|
2139
|
+
// Find the column that matches the grouped field
|
|
2140
|
+
const matchingColumn = gridColumns.find(
|
|
2141
|
+
(col) =>
|
|
2142
|
+
col.name === groupedColumnName ||
|
|
2143
|
+
col.header === cellContent ||
|
|
2144
|
+
col.id === groupedColumnName ||
|
|
2145
|
+
dataColumnId === groupedColumnName
|
|
2146
|
+
);
|
|
2147
|
+
|
|
2148
|
+
if (
|
|
2149
|
+
matchingColumn &&
|
|
2150
|
+
(matchingColumn.name === groupedColumnName ||
|
|
2151
|
+
matchingColumn.id === groupedColumnName ||
|
|
2152
|
+
dataColumnId === groupedColumnName)
|
|
2153
|
+
) {
|
|
2154
|
+
cell.classList.add(
|
|
2155
|
+
'InovuaReactDataGrid__header-cell--grouped'
|
|
2156
|
+
);
|
|
2157
|
+
// Force inline styles as backup
|
|
2158
|
+
cell.style.background =
|
|
2159
|
+
'linear-gradient(135deg, var(--primary-color, #3b82f6) 0%, var(--secondary-color, #2563eb) 100%)';
|
|
2160
|
+
cell.style.color = 'white';
|
|
2161
|
+
cell.style.fontWeight = '800';
|
|
2162
|
+
cell.style.textTransform = 'uppercase';
|
|
2163
|
+
cell.style.letterSpacing = '0.1em';
|
|
2164
|
+
cell.style.borderBottom =
|
|
2165
|
+
'4px solid var(--secondary-color, #2563eb)';
|
|
2166
|
+
}
|
|
2167
|
+
});
|
|
2168
|
+
|
|
2169
|
+
// Also apply class to the main DataGrid container for debugging
|
|
2170
|
+
const dataGrids = document.querySelectorAll(
|
|
2171
|
+
'.InovuaReactDataGrid'
|
|
2172
|
+
);
|
|
2173
|
+
dataGrids.forEach((grid) => {
|
|
2174
|
+
if (
|
|
2175
|
+
grid.classList.contains(
|
|
2176
|
+
'InovuaReactDataGrid--grouped'
|
|
2177
|
+
) ||
|
|
2178
|
+
grid.classList.contains('datagrid-with-grouping')
|
|
2179
|
+
) {
|
|
2180
|
+
// Main checkbox is now enabled for grouped data
|
|
2181
|
+
}
|
|
2182
|
+
});
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
// Apply immediately and with delays
|
|
2186
|
+
applyGroupedStyling();
|
|
2187
|
+
const timer1 = setTimeout(applyGroupedStyling, 100);
|
|
2188
|
+
const timer2 = setTimeout(applyGroupedStyling, 500);
|
|
2189
|
+
const timer3 = setTimeout(applyGroupedStyling, 1000);
|
|
2190
|
+
|
|
2191
|
+
return () => {
|
|
2192
|
+
clearTimeout(timer1);
|
|
2193
|
+
clearTimeout(timer2);
|
|
2194
|
+
clearTimeout(timer3);
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
}, [ajaxSetting?.groupBy, gridColumns]);
|
|
2198
|
+
|
|
1805
2199
|
useEffect(() => {
|
|
1806
2200
|
const delayedSetDSearch = debounce(() => {
|
|
1807
2201
|
setDSearch(search);
|
|
@@ -2609,7 +3003,7 @@ const DataGrid = forwardRef(
|
|
|
2609
3003
|
>
|
|
2610
3004
|
<option value="">
|
|
2611
3005
|
Select{' '}
|
|
2612
|
-
{/^[
|
|
3006
|
+
{/^[aeioAEIO]/.test(
|
|
2613
3007
|
column.label
|
|
2614
3008
|
)
|
|
2615
3009
|
? 'an'
|
|
@@ -3404,7 +3798,22 @@ const DataGrid = forwardRef(
|
|
|
3404
3798
|
);
|
|
3405
3799
|
}
|
|
3406
3800
|
|
|
3407
|
-
|
|
3801
|
+
// Return a placeholder div to maintain consistent row height
|
|
3802
|
+
return (
|
|
3803
|
+
<div
|
|
3804
|
+
style={{
|
|
3805
|
+
width: '28px',
|
|
3806
|
+
height: '28px',
|
|
3807
|
+
display: 'flex',
|
|
3808
|
+
alignItems: 'center',
|
|
3809
|
+
justifyContent:
|
|
3810
|
+
'center',
|
|
3811
|
+
margin: '0 auto',
|
|
3812
|
+
}}
|
|
3813
|
+
>
|
|
3814
|
+
{/* Empty placeholder to maintain layout */}
|
|
3815
|
+
</div>
|
|
3816
|
+
);
|
|
3408
3817
|
}
|
|
3409
3818
|
},
|
|
3410
3819
|
};
|
|
@@ -4179,12 +4588,395 @@ const DataGrid = forwardRef(
|
|
|
4179
4588
|
<ReactDataGrid
|
|
4180
4589
|
key={`datagrid-${ajaxSetting?.url || ''}-${
|
|
4181
4590
|
ajaxSetting?.groupBy ? 'grouped' : 'ungrouped'
|
|
4182
|
-
}`}
|
|
4591
|
+
}-${groupRenderKey}`}
|
|
4183
4592
|
{...tableSetting}
|
|
4184
4593
|
columns={gridColumns}
|
|
4185
4594
|
dataSource={dataSource}
|
|
4186
4595
|
{...(ajaxSetting && ajaxSetting.groupBy
|
|
4187
|
-
? {
|
|
4596
|
+
? {
|
|
4597
|
+
defaultGroupBy: ajaxSetting.groupBy,
|
|
4598
|
+
defaultCollapsed: true,
|
|
4599
|
+
collapsedGroups: collapsedGroups,
|
|
4600
|
+
onGroupToggle: (groupData) => {
|
|
4601
|
+
// Handle group expand/collapse events
|
|
4602
|
+
// Update collapsed groups state
|
|
4603
|
+
const groupValue =
|
|
4604
|
+
typeof groupData === 'string'
|
|
4605
|
+
? groupData
|
|
4606
|
+
: groupData?.value ||
|
|
4607
|
+
groupData?.name ||
|
|
4608
|
+
'';
|
|
4609
|
+
|
|
4610
|
+
setCollapsedGroups((prev) => {
|
|
4611
|
+
const newState = { ...prev };
|
|
4612
|
+
if (newState[groupValue]) {
|
|
4613
|
+
delete newState[groupValue];
|
|
4614
|
+
} else {
|
|
4615
|
+
newState[groupValue] = true;
|
|
4616
|
+
}
|
|
4617
|
+
console.log(
|
|
4618
|
+
'Updated collapsed groups:',
|
|
4619
|
+
newState
|
|
4620
|
+
);
|
|
4621
|
+
return newState;
|
|
4622
|
+
});
|
|
4623
|
+
},
|
|
4624
|
+
renderGroupTitle: (groupData) => {
|
|
4625
|
+
const groupValue =
|
|
4626
|
+
typeof groupData === 'string'
|
|
4627
|
+
? groupData
|
|
4628
|
+
: groupData?.value ||
|
|
4629
|
+
groupData?.name ||
|
|
4630
|
+
'';
|
|
4631
|
+
|
|
4632
|
+
// Use ReactDataGrid's built-in toggle functionality
|
|
4633
|
+
const collapsed =
|
|
4634
|
+
collapsedGroups[groupValue] ||
|
|
4635
|
+
false;
|
|
4636
|
+
const toggleGroup = () => {
|
|
4637
|
+
// Update the collapsed state
|
|
4638
|
+
setCollapsedGroups((prev) => {
|
|
4639
|
+
const newState = { ...prev };
|
|
4640
|
+
if (newState[groupValue]) {
|
|
4641
|
+
delete newState[
|
|
4642
|
+
groupValue
|
|
4643
|
+
];
|
|
4644
|
+
} else {
|
|
4645
|
+
newState[
|
|
4646
|
+
groupValue
|
|
4647
|
+
] = true;
|
|
4648
|
+
}
|
|
4649
|
+
return newState;
|
|
4650
|
+
});
|
|
4651
|
+
};
|
|
4652
|
+
const groupCount =
|
|
4653
|
+
typeof groupData === 'object'
|
|
4654
|
+
? groupData?.count || 0
|
|
4655
|
+
: 0;
|
|
4656
|
+
const hasCheckboxColumn =
|
|
4657
|
+
gridColumns.some(
|
|
4658
|
+
(col) =>
|
|
4659
|
+
col.id ===
|
|
4660
|
+
'__checkbox-column'
|
|
4661
|
+
);
|
|
4662
|
+
|
|
4663
|
+
// Handle group header click for selection
|
|
4664
|
+
const handleGroupHeaderClick = (
|
|
4665
|
+
e
|
|
4666
|
+
) => {
|
|
4667
|
+
// Prevent rapid clicks
|
|
4668
|
+
if (
|
|
4669
|
+
groupClickTimeoutRef.current
|
|
4670
|
+
) {
|
|
4671
|
+
console.log(
|
|
4672
|
+
'🖱️ Ignoring rapid click'
|
|
4673
|
+
);
|
|
4674
|
+
return;
|
|
4675
|
+
}
|
|
4676
|
+
|
|
4677
|
+
// Don't trigger if clicking on the expand button or checkbox
|
|
4678
|
+
if (
|
|
4679
|
+
e.target.closest(
|
|
4680
|
+
'.group-expand-btn'
|
|
4681
|
+
) ||
|
|
4682
|
+
e.target.closest(
|
|
4683
|
+
'input[type="checkbox"]'
|
|
4684
|
+
)
|
|
4685
|
+
) {
|
|
4686
|
+
console.log(
|
|
4687
|
+
'🖱️ Ignoring click on expand button or checkbox'
|
|
4688
|
+
);
|
|
4689
|
+
return;
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4692
|
+
// Set timeout to prevent rapid clicks
|
|
4693
|
+
groupClickTimeoutRef.current =
|
|
4694
|
+
setTimeout(() => {
|
|
4695
|
+
groupClickTimeoutRef.current =
|
|
4696
|
+
null;
|
|
4697
|
+
}, 300);
|
|
4698
|
+
|
|
4699
|
+
// Toggle selection of all items in this group
|
|
4700
|
+
const isCurrentlySelected =
|
|
4701
|
+
isGroupSelected(groupValue);
|
|
4702
|
+
|
|
4703
|
+
handleGroupSelectionChange(
|
|
4704
|
+
groupValue,
|
|
4705
|
+
!isCurrentlySelected
|
|
4706
|
+
);
|
|
4707
|
+
};
|
|
4708
|
+
|
|
4709
|
+
return (
|
|
4710
|
+
<div
|
|
4711
|
+
className="group-header-clean group-header-fixed-height"
|
|
4712
|
+
onClick={
|
|
4713
|
+
handleGroupHeaderClick
|
|
4714
|
+
}
|
|
4715
|
+
style={{
|
|
4716
|
+
display: 'flex',
|
|
4717
|
+
alignItems: 'center',
|
|
4718
|
+
justifyContent:
|
|
4719
|
+
'space-between',
|
|
4720
|
+
width: '100%',
|
|
4721
|
+
height: '34px', // Compact height
|
|
4722
|
+
padding: '6px 12px', // Reduced padding for compact design
|
|
4723
|
+
background:
|
|
4724
|
+
'linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)',
|
|
4725
|
+
borderLeft:
|
|
4726
|
+
'4px solid var(--primary-color, #3b82f6)',
|
|
4727
|
+
position: 'relative',
|
|
4728
|
+
overflow: 'hidden',
|
|
4729
|
+
boxShadow:
|
|
4730
|
+
'0 1px 3px rgba(0,0,0,0.1)',
|
|
4731
|
+
borderRadius:
|
|
4732
|
+
'0 4px 4px 0',
|
|
4733
|
+
cursor: 'pointer',
|
|
4734
|
+
boxSizing: 'border-box',
|
|
4735
|
+
lineHeight: '1.1', // Compact line height
|
|
4736
|
+
flexShrink: 0,
|
|
4737
|
+
transition:
|
|
4738
|
+
'background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease',
|
|
4739
|
+
}}
|
|
4740
|
+
onMouseEnter={(e) => {
|
|
4741
|
+
e.currentTarget.style.background =
|
|
4742
|
+
'linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%)';
|
|
4743
|
+
e.currentTarget.style.boxShadow =
|
|
4744
|
+
'0 2px 8px rgba(0,0,0,0.15)';
|
|
4745
|
+
e.currentTarget.style.transform =
|
|
4746
|
+
'translateY(-1px)';
|
|
4747
|
+
}}
|
|
4748
|
+
onMouseLeave={(e) => {
|
|
4749
|
+
e.currentTarget.style.background =
|
|
4750
|
+
'linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%)';
|
|
4751
|
+
e.currentTarget.style.boxShadow =
|
|
4752
|
+
'0 1px 3px rgba(0,0,0,0.1)';
|
|
4753
|
+
e.currentTarget.style.transform =
|
|
4754
|
+
'translateY(0)';
|
|
4755
|
+
}}
|
|
4756
|
+
>
|
|
4757
|
+
{/* Left side - Group info and expand/collapse */}
|
|
4758
|
+
<div
|
|
4759
|
+
style={{
|
|
4760
|
+
display: 'flex',
|
|
4761
|
+
alignItems: 'center',
|
|
4762
|
+
flex: 1,
|
|
4763
|
+
zIndex: 2,
|
|
4764
|
+
position: 'relative',
|
|
4765
|
+
}}
|
|
4766
|
+
>
|
|
4767
|
+
<button
|
|
4768
|
+
className="group-expand-btn"
|
|
4769
|
+
onClick={(e) => {
|
|
4770
|
+
e.stopPropagation();
|
|
4771
|
+
if (
|
|
4772
|
+
typeof toggleGroup ===
|
|
4773
|
+
'function'
|
|
4774
|
+
) {
|
|
4775
|
+
toggleGroup();
|
|
4776
|
+
} else {
|
|
4777
|
+
console.warn(
|
|
4778
|
+
'toggleGroup is not a function:',
|
|
4779
|
+
toggleGroup
|
|
4780
|
+
);
|
|
4781
|
+
}
|
|
4782
|
+
}}
|
|
4783
|
+
style={{
|
|
4784
|
+
background:
|
|
4785
|
+
'var(--primary-color, #3b82f6)',
|
|
4786
|
+
border: 'none',
|
|
4787
|
+
borderRadius:
|
|
4788
|
+
'3px',
|
|
4789
|
+
color: 'white',
|
|
4790
|
+
width: '20px', // More compact
|
|
4791
|
+
height: '20px', // More compact
|
|
4792
|
+
display: 'flex',
|
|
4793
|
+
alignItems:
|
|
4794
|
+
'center',
|
|
4795
|
+
justifyContent:
|
|
4796
|
+
'center',
|
|
4797
|
+
marginRight:
|
|
4798
|
+
'8px', // Reduced margin
|
|
4799
|
+
cursor: 'pointer',
|
|
4800
|
+
fontSize: '11px', // Smaller font
|
|
4801
|
+
fontWeight:
|
|
4802
|
+
'bold',
|
|
4803
|
+
boxShadow:
|
|
4804
|
+
'0 1px 2px rgba(0,0,0,0.1)',
|
|
4805
|
+
transition:
|
|
4806
|
+
'all 0.15s ease',
|
|
4807
|
+
}}
|
|
4808
|
+
onMouseEnter={(e) => {
|
|
4809
|
+
e.target.style.transform =
|
|
4810
|
+
'scale(1.05)';
|
|
4811
|
+
e.target.style.background =
|
|
4812
|
+
'var(--secondary-color, #2563eb)';
|
|
4813
|
+
}}
|
|
4814
|
+
onMouseLeave={(e) => {
|
|
4815
|
+
e.target.style.transform =
|
|
4816
|
+
'scale(1)';
|
|
4817
|
+
e.target.style.background =
|
|
4818
|
+
'var(--primary-color, #3b82f6)';
|
|
4819
|
+
}}
|
|
4820
|
+
>
|
|
4821
|
+
{collapsed
|
|
4822
|
+
? '+'
|
|
4823
|
+
: '−'}
|
|
4824
|
+
</button>
|
|
4825
|
+
<div
|
|
4826
|
+
style={{
|
|
4827
|
+
display: 'flex',
|
|
4828
|
+
alignItems:
|
|
4829
|
+
'center', // Changed to center for compact layout
|
|
4830
|
+
gap: '8px', // Add gap between elements
|
|
4831
|
+
}}
|
|
4832
|
+
>
|
|
4833
|
+
<strong
|
|
4834
|
+
style={{
|
|
4835
|
+
fontSize:
|
|
4836
|
+
'0.85rem', // Slightly smaller for compact design
|
|
4837
|
+
color: 'var(--secondary-color, #1f2937)',
|
|
4838
|
+
fontWeight:
|
|
4839
|
+
'600',
|
|
4840
|
+
textTransform:
|
|
4841
|
+
'uppercase',
|
|
4842
|
+
letterSpacing:
|
|
4843
|
+
'0.025em',
|
|
4844
|
+
}}
|
|
4845
|
+
>
|
|
4846
|
+
{groupValue}
|
|
4847
|
+
</strong>
|
|
4848
|
+
{groupCount > 0 && (
|
|
4849
|
+
<span
|
|
4850
|
+
style={{
|
|
4851
|
+
color: '#6b7280',
|
|
4852
|
+
fontSize:
|
|
4853
|
+
'0.75rem', // Smaller for compact design
|
|
4854
|
+
fontWeight:
|
|
4855
|
+
'500',
|
|
4856
|
+
}}
|
|
4857
|
+
>
|
|
4858
|
+
({groupCount}{' '}
|
|
4859
|
+
{groupCount ===
|
|
4860
|
+
1
|
|
4861
|
+
? 'item'
|
|
4862
|
+
: 'items'}
|
|
4863
|
+
)
|
|
4864
|
+
</span>
|
|
4865
|
+
)}
|
|
4866
|
+
{/* Click indicator */}
|
|
4867
|
+
<span
|
|
4868
|
+
style={{
|
|
4869
|
+
color: '#9ca3af',
|
|
4870
|
+
fontSize:
|
|
4871
|
+
'0.7rem',
|
|
4872
|
+
fontStyle:
|
|
4873
|
+
'italic',
|
|
4874
|
+
opacity: 0.8,
|
|
4875
|
+
marginLeft:
|
|
4876
|
+
'auto',
|
|
4877
|
+
userSelect:
|
|
4878
|
+
'none',
|
|
4879
|
+
}}
|
|
4880
|
+
>
|
|
4881
|
+
Click to
|
|
4882
|
+
select/unselect
|
|
4883
|
+
all
|
|
4884
|
+
</span>
|
|
4885
|
+
</div>
|
|
4886
|
+
</div>
|
|
4887
|
+
|
|
4888
|
+
{/* Right side - Group selection */}
|
|
4889
|
+
{hasCheckboxColumn && (
|
|
4890
|
+
<div
|
|
4891
|
+
style={{
|
|
4892
|
+
display: 'flex',
|
|
4893
|
+
alignItems:
|
|
4894
|
+
'center',
|
|
4895
|
+
gap: '4px', // Reduced gap for compact design
|
|
4896
|
+
fontSize:
|
|
4897
|
+
'0.75rem', // Smaller font for compact design
|
|
4898
|
+
background:
|
|
4899
|
+
'rgba(255, 255, 255, 0.9)',
|
|
4900
|
+
padding:
|
|
4901
|
+
'3px 8px', // Reduced padding for compact design
|
|
4902
|
+
borderRadius:
|
|
4903
|
+
'12px', // Smaller border radius
|
|
4904
|
+
border: '1px solid rgba(59, 130, 246, 0.3)',
|
|
4905
|
+
zIndex: 2,
|
|
4906
|
+
position:
|
|
4907
|
+
'relative',
|
|
4908
|
+
boxShadow:
|
|
4909
|
+
'0 1px 2px rgba(0,0,0,0.05)',
|
|
4910
|
+
}}
|
|
4911
|
+
>
|
|
4912
|
+
<input
|
|
4913
|
+
type="checkbox"
|
|
4914
|
+
onChange={(e) => {
|
|
4915
|
+
e.stopPropagation();
|
|
4916
|
+
handleGroupSelectionChange(
|
|
4917
|
+
groupValue,
|
|
4918
|
+
e.target
|
|
4919
|
+
.checked
|
|
4920
|
+
);
|
|
4921
|
+
}}
|
|
4922
|
+
checked={isGroupSelected(
|
|
4923
|
+
groupValue
|
|
4924
|
+
)}
|
|
4925
|
+
style={{
|
|
4926
|
+
width: '14px', // Smaller checkbox for compact design
|
|
4927
|
+
height: '14px',
|
|
4928
|
+
cursor: 'pointer',
|
|
4929
|
+
accentColor:
|
|
4930
|
+
'var(--primary-color, #3b82f6)',
|
|
4931
|
+
}}
|
|
4932
|
+
/>
|
|
4933
|
+
<span
|
|
4934
|
+
style={{
|
|
4935
|
+
cursor: 'pointer',
|
|
4936
|
+
userSelect:
|
|
4937
|
+
'none',
|
|
4938
|
+
color: 'var(--secondary-color, #1f2937)',
|
|
4939
|
+
fontWeight:
|
|
4940
|
+
'500',
|
|
4941
|
+
fontSize:
|
|
4942
|
+
'0.7rem', // Smaller font for compact design
|
|
4943
|
+
}}
|
|
4944
|
+
onClick={(e) => {
|
|
4945
|
+
e.stopPropagation();
|
|
4946
|
+
const checkbox =
|
|
4947
|
+
e.target
|
|
4948
|
+
.previousElementSibling;
|
|
4949
|
+
checkbox.checked =
|
|
4950
|
+
!checkbox.checked;
|
|
4951
|
+
handleGroupSelectionChange(
|
|
4952
|
+
groupValue,
|
|
4953
|
+
checkbox.checked
|
|
4954
|
+
);
|
|
4955
|
+
}}
|
|
4956
|
+
>
|
|
4957
|
+
Select All
|
|
4958
|
+
</span>
|
|
4959
|
+
</div>
|
|
4960
|
+
)}
|
|
4961
|
+
|
|
4962
|
+
{/* Background pattern */}
|
|
4963
|
+
<div
|
|
4964
|
+
style={{
|
|
4965
|
+
position: 'absolute',
|
|
4966
|
+
top: 0,
|
|
4967
|
+
left: 0,
|
|
4968
|
+
right: 0,
|
|
4969
|
+
bottom: 0,
|
|
4970
|
+
background:
|
|
4971
|
+
'repeating-linear-gradient(45deg, transparent, transparent 2px, rgba(59, 130, 246, 0.03) 2px, rgba(59, 130, 246, 0.03) 4px)',
|
|
4972
|
+
zIndex: 1,
|
|
4973
|
+
pointerEvents: 'none',
|
|
4974
|
+
}}
|
|
4975
|
+
/>
|
|
4976
|
+
</div>
|
|
4977
|
+
);
|
|
4978
|
+
},
|
|
4979
|
+
}
|
|
4188
4980
|
: {})}
|
|
4189
4981
|
filterValue={filterValue}
|
|
4190
4982
|
limit={limit}
|
|
@@ -4229,6 +5021,11 @@ const DataGrid = forwardRef(
|
|
|
4229
5021
|
borderRadius: 'var(--br, 4px)',
|
|
4230
5022
|
overflow: 'hidden',
|
|
4231
5023
|
}}
|
|
5024
|
+
className={
|
|
5025
|
+
ajaxSetting?.groupBy
|
|
5026
|
+
? 'InovuaReactDataGrid--grouped datagrid-with-grouping datagrid-fixed-group-headers'
|
|
5027
|
+
: 'datagrid-without-grouping'
|
|
5028
|
+
}
|
|
4232
5029
|
checkboxColumnProps={{
|
|
4233
5030
|
width: 60,
|
|
4234
5031
|
minWidth: 60,
|
|
@@ -4237,7 +5034,88 @@ const DataGrid = forwardRef(
|
|
|
4237
5034
|
sortable: false,
|
|
4238
5035
|
headerAlign: 'center',
|
|
4239
5036
|
textAlign: 'center',
|
|
5037
|
+
renderHeader: () => {
|
|
5038
|
+
// Custom header checkbox that works with grouped data
|
|
5039
|
+
const allRowsSelected =
|
|
5040
|
+
dataRef.current &&
|
|
5041
|
+
dataRef.current.length > 0 &&
|
|
5042
|
+
dataRef.current.every(
|
|
5043
|
+
(row) =>
|
|
5044
|
+
row &&
|
|
5045
|
+
row.id !== undefined &&
|
|
5046
|
+
row.id !== null &&
|
|
5047
|
+
selected[row.id]
|
|
5048
|
+
);
|
|
5049
|
+
const someRowsSelected =
|
|
5050
|
+
dataRef.current &&
|
|
5051
|
+
dataRef.current.some(
|
|
5052
|
+
(row) =>
|
|
5053
|
+
row &&
|
|
5054
|
+
row.id !== undefined &&
|
|
5055
|
+
row.id !== null &&
|
|
5056
|
+
selected[row.id]
|
|
5057
|
+
);
|
|
5058
|
+
|
|
5059
|
+
return (
|
|
5060
|
+
<input
|
|
5061
|
+
type="checkbox"
|
|
5062
|
+
checked={allRowsSelected}
|
|
5063
|
+
ref={(checkbox) => {
|
|
5064
|
+
if (checkbox) {
|
|
5065
|
+
checkbox.indeterminate =
|
|
5066
|
+
someRowsSelected &&
|
|
5067
|
+
!allRowsSelected;
|
|
5068
|
+
}
|
|
5069
|
+
}}
|
|
5070
|
+
onChange={(e) => {
|
|
5071
|
+
if (e.target.checked) {
|
|
5072
|
+
// Select all rows
|
|
5073
|
+
const s = {};
|
|
5074
|
+
if (
|
|
5075
|
+
dataRef.current &&
|
|
5076
|
+
Array.isArray(
|
|
5077
|
+
dataRef.current
|
|
5078
|
+
)
|
|
5079
|
+
) {
|
|
5080
|
+
dataRef.current.forEach(
|
|
5081
|
+
(obj) => {
|
|
5082
|
+
if (
|
|
5083
|
+
obj &&
|
|
5084
|
+
obj.id !==
|
|
5085
|
+
undefined &&
|
|
5086
|
+
obj.id !==
|
|
5087
|
+
null
|
|
5088
|
+
) {
|
|
5089
|
+
s[obj.id] =
|
|
5090
|
+
obj;
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
);
|
|
5094
|
+
}
|
|
5095
|
+
setSelected(s);
|
|
5096
|
+
if (setRowsSelected) {
|
|
5097
|
+
setRowsSelected(s);
|
|
5098
|
+
}
|
|
5099
|
+
} else {
|
|
5100
|
+
// Deselect all rows
|
|
5101
|
+
setSelected({});
|
|
5102
|
+
if (setRowsSelected) {
|
|
5103
|
+
setRowsSelected({});
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
}}
|
|
5107
|
+
style={{
|
|
5108
|
+
width: '20px',
|
|
5109
|
+
height: '20px',
|
|
5110
|
+
cursor: 'pointer',
|
|
5111
|
+
accentColor:
|
|
5112
|
+
'var(--primary-color, #3b82f6)',
|
|
5113
|
+
}}
|
|
5114
|
+
/>
|
|
5115
|
+
);
|
|
5116
|
+
},
|
|
4240
5117
|
}}
|
|
5118
|
+
checkboxOnlyRowSelect={true}
|
|
4241
5119
|
/>
|
|
4242
5120
|
) : null}
|
|
4243
5121
|
</div>
|
|
@@ -833,3 +833,53 @@
|
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
835
|
}
|
|
836
|
+
|
|
837
|
+
/* Group header styles */
|
|
838
|
+
.groupHeader {
|
|
839
|
+
display: flex;
|
|
840
|
+
justify-content: space-between;
|
|
841
|
+
align-items: center;
|
|
842
|
+
padding: 8px 12px;
|
|
843
|
+
background-color: #f8fafc;
|
|
844
|
+
border-bottom: 1px solid #e2e8f0;
|
|
845
|
+
font-size: 0.9rem;
|
|
846
|
+
width: 100%;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.groupTitle {
|
|
850
|
+
display: flex;
|
|
851
|
+
align-items: center;
|
|
852
|
+
gap: 8px;
|
|
853
|
+
flex: 1;
|
|
854
|
+
|
|
855
|
+
strong {
|
|
856
|
+
color: var(--secondary-color, #1f2937);
|
|
857
|
+
font-weight: 600;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.groupCount {
|
|
862
|
+
color: var(--paragraph-color, #6b7280);
|
|
863
|
+
font-size: 0.85rem;
|
|
864
|
+
font-weight: normal;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.groupCheckbox {
|
|
868
|
+
display: flex;
|
|
869
|
+
align-items: center;
|
|
870
|
+
gap: 6px;
|
|
871
|
+
font-size: 0.85rem;
|
|
872
|
+
color: var(--secondary-color, #1f2937);
|
|
873
|
+
|
|
874
|
+
input[type='checkbox'] {
|
|
875
|
+
width: 16px;
|
|
876
|
+
height: 16px;
|
|
877
|
+
margin: 0;
|
|
878
|
+
cursor: pointer;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
span {
|
|
882
|
+
cursor: pointer;
|
|
883
|
+
user-select: none;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
@@ -206,3 +206,231 @@
|
|
|
206
206
|
margin: -18px !important;
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
+
|
|
210
|
+
/* Group expand/collapse icon fixes - Enhanced for better visibility */
|
|
211
|
+
.InovuaReactDataGrid__group-expand-icon,
|
|
212
|
+
.InovuaReactDataGrid__group-collapse-icon {
|
|
213
|
+
display: inline-flex !important;
|
|
214
|
+
align-items: center !important;
|
|
215
|
+
justify-content: center !important;
|
|
216
|
+
width: 24px !important;
|
|
217
|
+
height: 24px !important;
|
|
218
|
+
margin-right: 8px !important;
|
|
219
|
+
cursor: pointer !important;
|
|
220
|
+
border-radius: 4px !important;
|
|
221
|
+
background-color: var(--primary-color, #3b82f6) !important;
|
|
222
|
+
transition: all 0.2s ease !important;
|
|
223
|
+
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
|
224
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.InovuaReactDataGrid__group-expand-icon:hover,
|
|
228
|
+
.InovuaReactDataGrid__group-collapse-icon:hover {
|
|
229
|
+
background-color: var(--secondary-color, #2563eb) !important;
|
|
230
|
+
transform: scale(1.05) !important;
|
|
231
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15) !important;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.InovuaReactDataGrid__group-expand-icon svg,
|
|
235
|
+
.InovuaReactDataGrid__group-collapse-icon svg {
|
|
236
|
+
width: 14px !important;
|
|
237
|
+
height: 14px !important;
|
|
238
|
+
fill: white !important;
|
|
239
|
+
color: white !important;
|
|
240
|
+
display: block !important;
|
|
241
|
+
opacity: 1 !important;
|
|
242
|
+
stroke: white !important;
|
|
243
|
+
stroke-width: 2 !important;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Enhanced group row styling */
|
|
247
|
+
.InovuaReactDataGrid__group-row {
|
|
248
|
+
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important;
|
|
249
|
+
border-bottom: 2px solid #e2e8f0 !important;
|
|
250
|
+
border-left: 4px solid var(--primary-color, #3b82f6) !important;
|
|
251
|
+
position: relative !important;
|
|
252
|
+
transition: all 0.2s ease !important;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.InovuaReactDataGrid__group-row:hover {
|
|
256
|
+
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%) !important;
|
|
257
|
+
border-left-color: var(--secondary-color, #2563eb) !important;
|
|
258
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* Enhanced group cell styling */
|
|
262
|
+
.InovuaReactDataGrid__group-cell {
|
|
263
|
+
padding: 12px 16px !important;
|
|
264
|
+
font-weight: 700 !important;
|
|
265
|
+
color: var(--secondary-color, #1f2937) !important;
|
|
266
|
+
font-size: 0.95rem !important;
|
|
267
|
+
letter-spacing: 0.025em !important;
|
|
268
|
+
text-transform: uppercase !important;
|
|
269
|
+
position: relative !important;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* Add a subtle pattern to group cells */
|
|
273
|
+
.InovuaReactDataGrid__group-cell::before {
|
|
274
|
+
content: '' !important;
|
|
275
|
+
position: absolute !important;
|
|
276
|
+
top: 0 !important;
|
|
277
|
+
left: 0 !important;
|
|
278
|
+
right: 0 !important;
|
|
279
|
+
bottom: 0 !important;
|
|
280
|
+
background: repeating-linear-gradient(
|
|
281
|
+
45deg,
|
|
282
|
+
transparent,
|
|
283
|
+
transparent 2px,
|
|
284
|
+
rgba(59, 130, 246, 0.05) 2px,
|
|
285
|
+
rgba(59, 130, 246, 0.05) 4px
|
|
286
|
+
) !important;
|
|
287
|
+
pointer-events: none !important;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* Ensure group expand/collapse icons are visible in all themes */
|
|
291
|
+
.InovuaReactDataGrid__group-expand-icon .InovuaReactDataGrid__icon,
|
|
292
|
+
.InovuaReactDataGrid__group-collapse-icon .InovuaReactDataGrid__icon {
|
|
293
|
+
opacity: 1 !important;
|
|
294
|
+
visibility: visible !important;
|
|
295
|
+
display: block !important;
|
|
296
|
+
color: white !important;
|
|
297
|
+
fill: white !important;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/* Alternative selectors for different library versions */
|
|
301
|
+
.InovuaReactDataGrid .InovuaReactDataGrid__group-expand-icon,
|
|
302
|
+
.InovuaReactDataGrid .InovuaReactDataGrid__group-collapse-icon {
|
|
303
|
+
opacity: 1 !important;
|
|
304
|
+
visibility: visible !important;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* Fix for SVG icons that might be hidden */
|
|
308
|
+
.InovuaReactDataGrid__group-expand-icon > *,
|
|
309
|
+
.InovuaReactDataGrid__group-collapse-icon > * {
|
|
310
|
+
opacity: 1 !important;
|
|
311
|
+
visibility: visible !important;
|
|
312
|
+
display: block !important;
|
|
313
|
+
color: white !important;
|
|
314
|
+
fill: white !important;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Enhanced styling for the grouped column header */
|
|
318
|
+
.InovuaReactDataGrid__header-cell--grouped {
|
|
319
|
+
background: linear-gradient(
|
|
320
|
+
135deg,
|
|
321
|
+
var(--primary-color, #3b82f6) 0%,
|
|
322
|
+
var(--secondary-color, #2563eb) 100%
|
|
323
|
+
) !important;
|
|
324
|
+
color: white !important;
|
|
325
|
+
font-weight: 800 !important;
|
|
326
|
+
text-transform: uppercase !important;
|
|
327
|
+
letter-spacing: 0.1em !important;
|
|
328
|
+
border-bottom: 4px solid var(--secondary-color, #2563eb) !important;
|
|
329
|
+
border-top: 2px solid rgba(255, 255, 255, 0.3) !important;
|
|
330
|
+
position: relative !important;
|
|
331
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15),
|
|
332
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.2) !important;
|
|
333
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) !important;
|
|
334
|
+
font-size: 0.9rem !important;
|
|
335
|
+
padding: 12px 16px !important;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.InovuaReactDataGrid__header-cell--grouped::before {
|
|
339
|
+
content: '🔗' !important;
|
|
340
|
+
position: absolute !important;
|
|
341
|
+
left: 8px !important;
|
|
342
|
+
top: 50% !important;
|
|
343
|
+
transform: translateY(-50%) !important;
|
|
344
|
+
font-size: 16px !important;
|
|
345
|
+
opacity: 0.9 !important;
|
|
346
|
+
filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.3)) !important;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.InovuaReactDataGrid__header-cell--grouped::after {
|
|
350
|
+
content: 'GROUPED BY' !important;
|
|
351
|
+
position: absolute !important;
|
|
352
|
+
right: 8px !important;
|
|
353
|
+
top: 50% !important;
|
|
354
|
+
transform: translateY(-50%) !important;
|
|
355
|
+
font-size: 10px !important;
|
|
356
|
+
opacity: 0.8 !important;
|
|
357
|
+
font-weight: 600 !important;
|
|
358
|
+
letter-spacing: 0.05em !important;
|
|
359
|
+
background: rgba(255, 255, 255, 0.2) !important;
|
|
360
|
+
padding: 2px 6px !important;
|
|
361
|
+
border-radius: 10px !important;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.InovuaReactDataGrid__header-cell--grouped:hover {
|
|
365
|
+
background: linear-gradient(
|
|
366
|
+
135deg,
|
|
367
|
+
var(--secondary-color, #2563eb) 0%,
|
|
368
|
+
#1d4ed8 100%
|
|
369
|
+
) !important;
|
|
370
|
+
transform: translateY(-1px) !important;
|
|
371
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2),
|
|
372
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.3) !important;
|
|
373
|
+
transition: all 0.2s ease !important;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Group selection functionality */
|
|
377
|
+
.group-selection-checkbox {
|
|
378
|
+
position: absolute !important;
|
|
379
|
+
right: 12px !important;
|
|
380
|
+
top: 50% !important;
|
|
381
|
+
transform: translateY(-50%) !important;
|
|
382
|
+
width: 18px !important;
|
|
383
|
+
height: 18px !important;
|
|
384
|
+
cursor: pointer !important;
|
|
385
|
+
z-index: 10 !important;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* Clean up group header rows - hide all column content */
|
|
389
|
+
.InovuaReactDataGrid__group-row .InovuaReactDataGrid__group-cell {
|
|
390
|
+
position: relative !important;
|
|
391
|
+
padding-right: 50px !important;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* PRESERVE ORIGINAL GROUPING: Clean group rows while maintaining ReactDataGrid structure */
|
|
395
|
+
|
|
396
|
+
/* Let ReactDataGrid handle the group cell spanning, but clean up any content that shouldn't be there */
|
|
397
|
+
/* The group cell should naturally span across all columns in ReactDataGrid */
|
|
398
|
+
|
|
399
|
+
/* Clean approach: Only hide content within group cells that shouldn't be there */
|
|
400
|
+
/* This preserves ReactDataGrid's natural group row spanning behavior */
|
|
401
|
+
|
|
402
|
+
/* Hide specific unwanted elements in group rows */
|
|
403
|
+
.InovuaReactDataGrid__group-row select,
|
|
404
|
+
.InovuaReactDataGrid__group-row input:not([type='checkbox']),
|
|
405
|
+
.InovuaReactDataGrid__group-row
|
|
406
|
+
button:not(.InovuaReactDataGrid__group-expand-button),
|
|
407
|
+
.InovuaReactDataGrid__group-row .dropdown,
|
|
408
|
+
.InovuaReactDataGrid__group-row .timer-button,
|
|
409
|
+
.InovuaReactDataGrid__group-row .action-icon,
|
|
410
|
+
.InovuaReactDataGrid__group-row
|
|
411
|
+
.btn:not(.InovuaReactDataGrid__group-expand-button),
|
|
412
|
+
.InovuaReactDataGrid__group-row
|
|
413
|
+
.icon:not(.InovuaReactDataGrid__group-expand-icon),
|
|
414
|
+
.InovuaReactDataGrid__group-row .cell-content,
|
|
415
|
+
.InovuaReactDataGrid__group-row .cell-value,
|
|
416
|
+
.InovuaReactDataGrid__group-row .dropdown-value {
|
|
417
|
+
display: none !important;
|
|
418
|
+
visibility: hidden !important;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* Enhance group cell styling */
|
|
422
|
+
.InovuaReactDataGrid__group-cell {
|
|
423
|
+
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important;
|
|
424
|
+
border-left: 4px solid var(--primary-color, #3b82f6) !important;
|
|
425
|
+
font-weight: 600 !important;
|
|
426
|
+
padding: 12px 16px !important;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* Additional group row cleanup */
|
|
430
|
+
.InovuaReactDataGrid__group-row * {
|
|
431
|
+
background-image: none !important;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.InovuaReactDataGrid__group-toolbar {
|
|
435
|
+
display: none !important;
|
|
436
|
+
}
|