@visns-studio/visns-components 6.6.3 → 6.15.0
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/README.md +1658 -18
- package/package.json +10 -4
- package/src/components/DataGrid.jsx +619 -158
- package/src/components/Fetch.jsx +80 -3
- package/src/components/Form.jsx +9 -0
- package/src/components/Navigation.jsx +109 -50
- package/src/components/Notification.jsx +279 -9
- package/src/components/TableFilter.jsx +14 -1
- package/src/components/auth/AuthBrandPanel.jsx +43 -0
- package/src/components/auth/AuthLoading.jsx +78 -0
- package/src/components/auth/AuthShell.jsx +59 -0
- package/src/components/auth/ClientAuth.jsx +161 -0
- package/src/components/auth/ClientAuthFrame.jsx +59 -0
- package/src/components/auth/ClientLogin.jsx +266 -55
- package/src/components/auth/ClientOTPVerify.jsx +587 -115
- package/src/components/auth/ImpersonateGate.jsx +254 -0
- package/src/components/auth/Login.jsx +134 -41
- package/src/components/auth/LogoutScreen.jsx +112 -0
- package/src/components/auth/Reset.jsx +134 -77
- package/src/components/auth/TwoFactorAuth.jsx +475 -297
- package/src/components/auth/Verify.jsx +237 -126
- package/src/components/auth/authEndpoints.js +105 -0
- package/src/components/auth/authFont.js +23 -0
- package/src/components/auth/authHelpers.js +465 -0
- package/src/components/auth/clientAuthProtocols.js +240 -0
- package/src/components/auth/useOptionalRouter.js +49 -0
- package/src/components/callQueue/CallQueuePop.jsx +1502 -0
- package/src/components/callQueue/CallQueueSettings.jsx +508 -0
- package/src/components/callQueue/callQueueHelpers.js +280 -0
- package/src/components/callQueue/callQueueSettingsHelpers.js +75 -0
- package/src/components/columns/AutoGrowCell.jsx +141 -0
- package/src/components/columns/ColumnRenderers.jsx +53 -7
- package/src/components/generic/GenericAuth.jsx +163 -96
- package/src/components/generic/GenericDetail.jsx +215 -90
- package/src/components/generic/GenericIndex.jsx +20 -47
- package/src/components/generic/GenericMain.jsx +5 -0
- package/src/components/generic/GroupedReportRenderer.jsx +1 -5
- package/src/components/generic/StandardModal.jsx +17 -5
- package/src/components/generic/reportSemanticSteps/SemanticEntityStep.jsx +1 -6
- package/src/components/generic/reportSemanticSteps/SemanticFieldsStep.jsx +4 -11
- package/src/components/generic/reportSemanticSteps/SemanticFiltersStep.jsx +9 -27
- package/src/components/generic/reportSemanticSteps/SemanticGroupingStep.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticParameterPrompt.jsx +5 -13
- package/src/components/generic/reportSemanticSteps/SemanticPreviewStep.jsx +14 -31
- package/src/components/generic/reportSemanticSteps/SemanticRelationsStep.jsx +3 -9
- package/src/components/generic/reportSemanticSteps/SemanticValueInput.jsx +2 -15
- package/src/components/navActive.js +60 -0
- package/src/components/notify/desktopNotifications.js +256 -0
- package/src/components/sketch/SketchField.jsx +12 -2
- package/src/components/sms/SmsComposeModal.jsx +495 -0
- package/src/components/sms/SmsInbox.jsx +596 -0
- package/src/components/sms/SmsInboxBadge.jsx +468 -0
- package/src/components/sms/SmsLineSettings.jsx +854 -0
- package/src/components/sms/SmsThreadPanel.jsx +1184 -0
- package/src/components/sms/smsEndpoints.js +56 -0
- package/src/components/sms/smsHelpers.js +1075 -0
- package/src/components/sms/smsLiveState.js +132 -0
- package/src/components/sms/useSmsLive.js +307 -0
- package/src/components/styles/CallQueuePop.module.scss +646 -0
- package/src/components/styles/CallQueueSettings.module.scss +460 -0
- package/src/components/styles/ClientAuth.module.scss +229 -0
- package/src/components/styles/DataGrid.module.scss +10 -0
- package/src/components/styles/GenericDetail.module.scss +153 -26
- package/src/components/styles/GenericIndex.module.scss +13 -0
- package/src/components/styles/GenericMain.module.scss +19 -2
- package/src/components/styles/ImpersonateGate.module.scss +85 -0
- package/src/components/styles/Login.module.scss +13 -122
- package/src/components/styles/LogoutScreen.module.scss +89 -0
- package/src/components/styles/Navigation.module.scss +509 -223
- package/src/components/styles/Notification.module.scss +103 -0
- package/src/components/styles/Reset.module.scss +52 -186
- package/src/components/styles/Sms.module.scss +2382 -0
- package/src/components/styles/TableFilter.module.scss +118 -98
- package/src/components/styles/TwoFactorAuth.module.scss +115 -176
- package/src/components/styles/Vault.module.scss +1983 -0
- package/src/components/styles/Verify.module.scss +57 -180
- package/src/components/styles/_authBrandPanel.scss +163 -0
- package/src/components/styles/_authShell.scss +369 -0
- package/src/components/styles/global-datagrid.css +29 -0
- package/src/components/utils/buildEnv.js +67 -0
- package/src/components/utils/displayValue.js +94 -0
- package/src/components/utils/useDensity.js +345 -9
- package/src/components/vault/OtpChip.jsx +208 -0
- package/src/components/vault/PasswordGenerator.jsx +145 -0
- package/src/components/vault/QrScanner.jsx +326 -0
- package/src/components/vault/VaultAccessLog.jsx +225 -0
- package/src/components/vault/VaultConfirmPanel.jsx +113 -0
- package/src/components/vault/VaultEntryForm.jsx +740 -0
- package/src/components/vault/VaultManager.jsx +1000 -0
- package/src/components/vault/VaultQuickSearch.jsx +681 -0
- package/src/components/vault/qrDecode.js +257 -0
- package/src/components/vault/useDebouncedValue.js +22 -0
- package/src/components/vault/useVaultReveal.js +160 -0
- package/src/components/vault/vaultClipboard.js +33 -0
- package/src/components/vault/vaultEndpoints.js +40 -0
- package/src/components/vault/vaultFit.js +116 -0
- package/src/components/vault/vaultHelpers.js +547 -0
- package/src/components/vault/vaultNavigation.jsx +58 -0
- package/src/components/vault/vaultOtp.js +105 -0
- package/src/index.js +233 -1
|
@@ -30,6 +30,7 @@ import React, {
|
|
|
30
30
|
useState,
|
|
31
31
|
} from 'react';
|
|
32
32
|
import { useNavigate, useParams } from 'react-router-dom';
|
|
33
|
+
import { buildEnv } from './utils/buildEnv';
|
|
33
34
|
import debounce from 'lodash.debounce';
|
|
34
35
|
import moment from 'moment';
|
|
35
36
|
import 'reactjs-popup/dist/index.css';
|
|
@@ -43,7 +44,11 @@ import NativeDateFilterEditor from './columns/NativeDateFilterEditor';
|
|
|
43
44
|
import { toast } from 'react-toastify';
|
|
44
45
|
import fetchUtil from '../utils/fetchUtil';
|
|
45
46
|
import { confirmDialog } from './utils/ConfirmDialog';
|
|
46
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
useDensity,
|
|
49
|
+
useAvailableHeight,
|
|
50
|
+
rowsForHeight,
|
|
51
|
+
} from './utils/useDensity';
|
|
47
52
|
import { DEFAULT_INTELLIGENT_SORTING_CONFIG } from '../utils/relationshipSortingUtils';
|
|
48
53
|
import {
|
|
49
54
|
extractColumnsMetadata,
|
|
@@ -144,6 +149,7 @@ import {
|
|
|
144
149
|
renderPhoneColumn,
|
|
145
150
|
renderAbnColumn,
|
|
146
151
|
} from './columns/ColumnRenderers.jsx';
|
|
152
|
+
import AutoGrowCell from './columns/AutoGrowCell.jsx';
|
|
147
153
|
import _ from 'lodash';
|
|
148
154
|
|
|
149
155
|
// Enhanced multi-line content support for data cells
|
|
@@ -872,6 +878,22 @@ const DataGrid = forwardRef(
|
|
|
872
878
|
debugLog('[DataGrid] Initial limit set to:', initialLimit);
|
|
873
879
|
return initialLimit;
|
|
874
880
|
});
|
|
881
|
+
|
|
882
|
+
// Set the moment the user picks a size from the pager's "Results per
|
|
883
|
+
// page" control. From then on nothing auto-sizes the page behind their
|
|
884
|
+
// back — not the profile default, not a window resize. Deliberately a
|
|
885
|
+
// ref rather than state: it must not itself trigger a re-render, and
|
|
886
|
+
// the sizing effect reads it without wanting to re-run when it flips.
|
|
887
|
+
const userChoseLimitRef = useRef(false);
|
|
888
|
+
// The current limit, readable from the sizing effect without listing
|
|
889
|
+
// `limit` as a dependency (which would re-run it on every page change).
|
|
890
|
+
const limitRef = useRef(limit);
|
|
891
|
+
limitRef.current = limit;
|
|
892
|
+
|
|
893
|
+
const handleLimitChange = useCallback((next) => {
|
|
894
|
+
userChoseLimitRef.current = true;
|
|
895
|
+
setLimit(next);
|
|
896
|
+
}, []);
|
|
875
897
|
const [search, setSearch] = useState('');
|
|
876
898
|
|
|
877
899
|
// This component instance survives client-side route changes — React Router
|
|
@@ -906,8 +928,12 @@ const DataGrid = forwardRef(
|
|
|
906
928
|
userProfile_settings_rows_per_page: userProfile?.settings?.rows_per_page,
|
|
907
929
|
currentLimit: limit,
|
|
908
930
|
});
|
|
909
|
-
// Only apply the user's preference once, when userProfile first
|
|
910
|
-
|
|
931
|
+
// Only apply the user's preference once, when userProfile first
|
|
932
|
+
// becomes available — and never over a size the user has picked
|
|
933
|
+
// from the pager since. This seeds the limit for the first fetch;
|
|
934
|
+
// the sizing effect below replaces it as soon as the grid has
|
|
935
|
+
// measured itself against the window.
|
|
936
|
+
if (!initialLimitApplied.current && !userChoseLimitRef.current) {
|
|
911
937
|
const userLimit = userProfile?.rows_per_page || userProfile?.settings?.rows_per_page;
|
|
912
938
|
debugLog('[DataGrid] Checking userLimit:', userLimit);
|
|
913
939
|
if (userLimit && userLimit > 0) {
|
|
@@ -959,6 +985,197 @@ const DataGrid = forwardRef(
|
|
|
959
985
|
// prevents action buttons from being clipped.
|
|
960
986
|
const { grid } = useDensity();
|
|
961
987
|
|
|
988
|
+
// ---------------------------------------------------------------
|
|
989
|
+
// Per-column autoGrow
|
|
990
|
+
// ---------------------------------------------------------------
|
|
991
|
+
//
|
|
992
|
+
// A column with `"autoGrow": true` lets the ROWS whose content does
|
|
993
|
+
// not fit the fixed height above grow to fit — and only those rows.
|
|
994
|
+
// Nothing here runs unless such a column exists AND the density gives
|
|
995
|
+
// a numeric `grid.rowHeight`; on desktop (`rowHeight === null`) the
|
|
996
|
+
// grid already measures every row, so autoGrow has nothing to add.
|
|
997
|
+
//
|
|
998
|
+
// Why this drives the grid's RowHeightManager instead of passing
|
|
999
|
+
// `rowHeight` as a function: the datagrid's types advertise
|
|
1000
|
+
// `rowHeight: number | ((rowIndex) => number) | null`, but in this
|
|
1001
|
+
// fork the function form is not implemented. `RowHeightManager`
|
|
1002
|
+
// stores it as `rowHeightFn` and never reads it again, and
|
|
1003
|
+
// `factory.tsx` derives `naturalRowHeight: typeof props.rowHeight !==
|
|
1004
|
+
// 'number'` — so a function rowHeight silently turns on exactly the
|
|
1005
|
+
// lazy per-row rAF measurement the fixed height exists to avoid.
|
|
1006
|
+
//
|
|
1007
|
+
// What IS implemented is the manager's per-index override map: every
|
|
1008
|
+
// row's height comes from `rowHeightManager.getRowHeight(index)`
|
|
1009
|
+
// (packages/react-virtual-list-pro/src/Row.tsx), which reads that map
|
|
1010
|
+
// first and falls back to the fixed height; `setHeights(map)`
|
|
1011
|
+
// re-indexes the offsets and emits `index`, which `VirtualList`
|
|
1012
|
+
// listens for and answers with a full re-layout. The cells follow the
|
|
1013
|
+
// row because Content/Row.tsx passes the row's own height down as the
|
|
1014
|
+
// cell's `initialRowHeight` on unexpanded rows, so a grown row's other
|
|
1015
|
+
// cells and its action buttons stay vertically centred in the taller
|
|
1016
|
+
// box exactly as they are today.
|
|
1017
|
+
//
|
|
1018
|
+
// Heights are keyed by ROW ID (stable across sorting, filtering and
|
|
1019
|
+
// virtualised scrolling) and only converted to the manager's
|
|
1020
|
+
// index-keyed map at flush time, from the grid's own computed data —
|
|
1021
|
+
// which includes group rows, so indexes stay right under groupBy/tree.
|
|
1022
|
+
const autoGrowHeightsRef = useRef(new Map());
|
|
1023
|
+
const autoGrowRowIndexRef = useRef(new Map());
|
|
1024
|
+
const autoGrowAppliedRef = useRef(null);
|
|
1025
|
+
const autoGrowRafRef = useRef(0);
|
|
1026
|
+
|
|
1027
|
+
const flushAutoGrowHeights = useCallback(() => {
|
|
1028
|
+
autoGrowRafRef.current = 0;
|
|
1029
|
+
|
|
1030
|
+
const gridApi = gridRef.current;
|
|
1031
|
+
const manager = gridApi?.rowHeightManager;
|
|
1032
|
+
const baseRowHeight = grid.rowHeight;
|
|
1033
|
+
|
|
1034
|
+
if (!manager || typeof baseRowHeight !== 'number') {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const heights = autoGrowHeightsRef.current;
|
|
1039
|
+
const next = {};
|
|
1040
|
+
|
|
1041
|
+
// The grid's computed data is the authority on index → row: it is
|
|
1042
|
+
// the array the virtual list indexes into, group rows included.
|
|
1043
|
+
// The `onRenderRow` record is only a fallback for the frame
|
|
1044
|
+
// before `handle` has published the computed props.
|
|
1045
|
+
const data = Array.isArray(gridApi?.data) ? gridApi.data : null;
|
|
1046
|
+
|
|
1047
|
+
if (data) {
|
|
1048
|
+
const present = new Set();
|
|
1049
|
+
|
|
1050
|
+
for (let index = 0; index < data.length; index++) {
|
|
1051
|
+
const row = data[index];
|
|
1052
|
+
|
|
1053
|
+
// Group rows keep the default height — they carry a title,
|
|
1054
|
+
// not a column's content.
|
|
1055
|
+
if (!row || row.__group) {
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
const rowId = row.id;
|
|
1060
|
+
|
|
1061
|
+
if (rowId === undefined || rowId === null) {
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const key = String(rowId);
|
|
1066
|
+
|
|
1067
|
+
present.add(key);
|
|
1068
|
+
|
|
1069
|
+
const measured = heights.get(key);
|
|
1070
|
+
|
|
1071
|
+
if (measured > baseRowHeight) {
|
|
1072
|
+
next[index] = measured;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// Rows that left the page cannot be measured again, so drop
|
|
1077
|
+
// their heights rather than letting the map grow forever.
|
|
1078
|
+
heights.forEach((_value, key) => {
|
|
1079
|
+
if (!present.has(key)) {
|
|
1080
|
+
heights.delete(key);
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
} else {
|
|
1084
|
+
autoGrowRowIndexRef.current.forEach((key, index) => {
|
|
1085
|
+
const measured = heights.get(key);
|
|
1086
|
+
|
|
1087
|
+
if (measured > baseRowHeight) {
|
|
1088
|
+
next[index] = measured;
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// setHeights re-indexes and emits, and the re-layout re-renders
|
|
1094
|
+
// the rows — which measures them again. Applying an unchanged map
|
|
1095
|
+
// would therefore loop, so compare before writing.
|
|
1096
|
+
const serialised = JSON.stringify(next);
|
|
1097
|
+
|
|
1098
|
+
if (serialised === autoGrowAppliedRef.current) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
autoGrowAppliedRef.current = serialised;
|
|
1103
|
+
|
|
1104
|
+
if (typeof manager.setHeights === 'function') {
|
|
1105
|
+
manager.setHeights(next);
|
|
1106
|
+
}
|
|
1107
|
+
}, [grid]);
|
|
1108
|
+
|
|
1109
|
+
// One flush per frame: a page of 100 rows measuring on mount is 100
|
|
1110
|
+
// reports and one re-layout.
|
|
1111
|
+
const scheduleAutoGrowFlush = useCallback(() => {
|
|
1112
|
+
if (autoGrowRafRef.current) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
if (typeof requestAnimationFrame === 'undefined') {
|
|
1117
|
+
flushAutoGrowHeights();
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
autoGrowRafRef.current =
|
|
1122
|
+
requestAnimationFrame(flushAutoGrowHeights);
|
|
1123
|
+
}, [flushAutoGrowHeights]);
|
|
1124
|
+
|
|
1125
|
+
/** `AutoGrowCell`'s callback: one row's natural height, in px. */
|
|
1126
|
+
const reportAutoGrowHeight = useCallback(
|
|
1127
|
+
(rowId, height, force) => {
|
|
1128
|
+
if (
|
|
1129
|
+
rowId === undefined ||
|
|
1130
|
+
rowId === null ||
|
|
1131
|
+
!Number.isFinite(height)
|
|
1132
|
+
) {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
const key = String(rowId);
|
|
1137
|
+
const previous = autoGrowHeightsRef.current.get(key);
|
|
1138
|
+
const changed =
|
|
1139
|
+
previous === undefined || Math.abs(previous - height) > 1;
|
|
1140
|
+
|
|
1141
|
+
if (changed) {
|
|
1142
|
+
autoGrowHeightsRef.current.set(key, height);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// `force` covers the mount/row-changed case: the height may be
|
|
1146
|
+
// identical while the INDEX it has to be applied at is not.
|
|
1147
|
+
if (changed || force) {
|
|
1148
|
+
scheduleAutoGrowFlush();
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
[scheduleAutoGrowFlush]
|
|
1152
|
+
);
|
|
1153
|
+
|
|
1154
|
+
useEffect(
|
|
1155
|
+
() => () => {
|
|
1156
|
+
if (
|
|
1157
|
+
autoGrowRafRef.current &&
|
|
1158
|
+
typeof cancelAnimationFrame !== 'undefined'
|
|
1159
|
+
) {
|
|
1160
|
+
cancelAnimationFrame(autoGrowRafRef.current);
|
|
1161
|
+
autoGrowRafRef.current = 0;
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
[]
|
|
1165
|
+
);
|
|
1166
|
+
|
|
1167
|
+
// Leaving the fixed-height densities hands row heights back to the
|
|
1168
|
+
// grid; anything measured under the old density is meaningless.
|
|
1169
|
+
useEffect(() => {
|
|
1170
|
+
if (typeof grid.rowHeight === 'number') {
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
autoGrowHeightsRef.current.clear();
|
|
1175
|
+
autoGrowRowIndexRef.current.clear();
|
|
1176
|
+
autoGrowAppliedRef.current = null;
|
|
1177
|
+
}, [grid]);
|
|
1178
|
+
|
|
962
1179
|
// Effect to handle autoRefresh setting
|
|
963
1180
|
useEffect(() => {
|
|
964
1181
|
if (ajaxSetting && ajaxSetting.autoRefresh !== undefined) {
|
|
@@ -1921,7 +2138,11 @@ const DataGrid = forwardRef(
|
|
|
1921
2138
|
}
|
|
1922
2139
|
|
|
1923
2140
|
// Show confirmation dialog for other actions to prevent accidental clicks
|
|
1924
|
-
const confirmMessage = `Are you sure you want to perform "${
|
|
2141
|
+
const confirmMessage = `Are you sure you want to perform "${
|
|
2142
|
+
iconConfig.label
|
|
2143
|
+
}" action on group "${getGroupDisplayLabel(groupValue)}" (${
|
|
2144
|
+
groupRows.length
|
|
2145
|
+
} rows)?`;
|
|
1925
2146
|
|
|
1926
2147
|
confirmDialog({
|
|
1927
2148
|
title: 'Confirm Group Action',
|
|
@@ -1959,6 +2180,28 @@ const DataGrid = forwardRef(
|
|
|
1959
2180
|
);
|
|
1960
2181
|
};
|
|
1961
2182
|
|
|
2183
|
+
/**
|
|
2184
|
+
* `groupBySetting.labelKey` is display-only: when set, the group row
|
|
2185
|
+
* shows the named field taken from the group's first row instead of
|
|
2186
|
+
* the raw group value (falling back to it when the field is empty).
|
|
2187
|
+
* The group KEY is still `groupValue` — it decides membership,
|
|
2188
|
+
* selection, collapse state, `handleGroupAction`/`shouldShowGroupAction`
|
|
2189
|
+
* and what the backend receives (`groupLabel`).
|
|
2190
|
+
*/
|
|
2191
|
+
const getGroupDisplayLabel = (groupValue) => {
|
|
2192
|
+
const labelKey = ajaxSetting?.groupBySetting?.labelKey;
|
|
2193
|
+
|
|
2194
|
+
if (!labelKey) {
|
|
2195
|
+
return groupValue;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
const label = getGroupRows(groupValue)[0]?.[labelKey];
|
|
2199
|
+
|
|
2200
|
+
return label === null || label === undefined || label === ''
|
|
2201
|
+
? groupValue
|
|
2202
|
+
: label;
|
|
2203
|
+
};
|
|
2204
|
+
|
|
1962
2205
|
/**
|
|
1963
2206
|
* The show-condition grammar shared by group action icons and row
|
|
1964
2207
|
* action icons:
|
|
@@ -2096,7 +2339,9 @@ const DataGrid = forwardRef(
|
|
|
2096
2339
|
iconConfig.fetch.messageFromResponse &&
|
|
2097
2340
|
result?.data?.message
|
|
2098
2341
|
? result.data.message
|
|
2099
|
-
: `${iconConfig.label} - Group: ${
|
|
2342
|
+
: `${iconConfig.label} - Group: ${getGroupDisplayLabel(
|
|
2343
|
+
groupValue
|
|
2344
|
+
)} completed successfully`
|
|
2100
2345
|
);
|
|
2101
2346
|
|
|
2102
2347
|
// Reload grid data if needed
|
|
@@ -2113,7 +2358,9 @@ const DataGrid = forwardRef(
|
|
|
2113
2358
|
} catch (error) {
|
|
2114
2359
|
console.error('Group action fetch error:', error);
|
|
2115
2360
|
toast.error(
|
|
2116
|
-
`${iconConfig.label} - Group: ${
|
|
2361
|
+
`${iconConfig.label} - Group: ${getGroupDisplayLabel(
|
|
2362
|
+
groupValue
|
|
2363
|
+
)} failed: ${error.message}`
|
|
2117
2364
|
);
|
|
2118
2365
|
}
|
|
2119
2366
|
} else if (iconConfig.formModal) {
|
|
@@ -2137,7 +2384,11 @@ const DataGrid = forwardRef(
|
|
|
2137
2384
|
// Fallback to existing switch logic if no fetch config
|
|
2138
2385
|
// Skip toast notification for edit actions
|
|
2139
2386
|
if (iconConfig.id !== 'edit') {
|
|
2140
|
-
toast.info(
|
|
2387
|
+
toast.info(
|
|
2388
|
+
`${iconConfig.label} - Group: ${getGroupDisplayLabel(
|
|
2389
|
+
groupValue
|
|
2390
|
+
)}`
|
|
2391
|
+
);
|
|
2141
2392
|
}
|
|
2142
2393
|
|
|
2143
2394
|
switch (iconConfig.id) {
|
|
@@ -2684,6 +2935,26 @@ const DataGrid = forwardRef(
|
|
|
2684
2935
|
|
|
2685
2936
|
const onRenderRow = useCallback(
|
|
2686
2937
|
(rowProps) => {
|
|
2938
|
+
// Index → row id, the fallback map autoGrow flushes from
|
|
2939
|
+
// before the grid has published its computed data (see the
|
|
2940
|
+
// autoGrow block above). Group rows are skipped: they keep the
|
|
2941
|
+
// default height.
|
|
2942
|
+
const autoGrowIndex =
|
|
2943
|
+
rowProps?.rowIndex ?? rowProps?.realIndex;
|
|
2944
|
+
|
|
2945
|
+
if (
|
|
2946
|
+
typeof autoGrowIndex === 'number' &&
|
|
2947
|
+
rowProps?.data &&
|
|
2948
|
+
!rowProps.data.__group &&
|
|
2949
|
+
rowProps.data.id !== undefined &&
|
|
2950
|
+
rowProps.data.id !== null
|
|
2951
|
+
) {
|
|
2952
|
+
autoGrowRowIndexRef.current.set(
|
|
2953
|
+
autoGrowIndex,
|
|
2954
|
+
String(rowProps.data.id)
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2687
2958
|
// save the original handlers to be called later
|
|
2688
2959
|
const { onClick } = rowProps;
|
|
2689
2960
|
|
|
@@ -2853,10 +3124,10 @@ const DataGrid = forwardRef(
|
|
|
2853
3124
|
|
|
2854
3125
|
const handleCoding = (c, data) => {
|
|
2855
3126
|
const icons = [];
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
3127
|
+
// Read through buildEnv, not import.meta.env directly: the
|
|
3128
|
+
// latter is Vite's interface and throws a TypeError under
|
|
3129
|
+
// webpack, which took the whole grid down in the Next.js portal.
|
|
3130
|
+
const assetPath = buildEnv('REACT_APP_ASSET_PATH', '');
|
|
2860
3131
|
|
|
2861
3132
|
const addIcon = (src, icon, alt, tooltipContent, key) => {
|
|
2862
3133
|
if (src && src !== '') {
|
|
@@ -4594,16 +4865,91 @@ const DataGrid = forwardRef(
|
|
|
4594
4865
|
};
|
|
4595
4866
|
};
|
|
4596
4867
|
|
|
4868
|
+
/**
|
|
4869
|
+
* `"autoGrow": true` on a column definition.
|
|
4870
|
+
*
|
|
4871
|
+
* Opt-in, and only meaningful where the grid runs at a
|
|
4872
|
+
* fixed row height — on desktop (`grid.rowHeight === null`)
|
|
4873
|
+
* rows already measure naturally, so this returns the
|
|
4874
|
+
* column untouched and the code path below is the one that
|
|
4875
|
+
* has always run.
|
|
4876
|
+
*
|
|
4877
|
+
* Outermost decorator on purpose: it has to measure the
|
|
4878
|
+
* FINAL content of the cell, including the tree indent and
|
|
4879
|
+
* any row-action icons the decorators below add.
|
|
4880
|
+
*/
|
|
4881
|
+
const decorateAutoGrow = (gridColumn, columnDef) => {
|
|
4882
|
+
if (
|
|
4883
|
+
columnDef?.autoGrow !== true ||
|
|
4884
|
+
typeof grid.rowHeight !== 'number' ||
|
|
4885
|
+
!gridColumn
|
|
4886
|
+
) {
|
|
4887
|
+
return gridColumn;
|
|
4888
|
+
}
|
|
4889
|
+
|
|
4890
|
+
const originalRender = gridColumn.render;
|
|
4891
|
+
const existingClassName = gridColumn.className;
|
|
4892
|
+
|
|
4893
|
+
// The class relaxes the nowrap/ellipsis clamp that
|
|
4894
|
+
// global-datagrid.css puts on every non-wrapping cell;
|
|
4895
|
+
// without it the content can never take a second line
|
|
4896
|
+
// and so can never be taller than the row.
|
|
4897
|
+
const withAutoGrowClass = (base) =>
|
|
4898
|
+
[base, 'cell-auto-grow'].filter(Boolean).join(' ');
|
|
4899
|
+
|
|
4900
|
+
return {
|
|
4901
|
+
...gridColumn,
|
|
4902
|
+
className:
|
|
4903
|
+
typeof existingClassName === 'function'
|
|
4904
|
+
? (...args) =>
|
|
4905
|
+
withAutoGrowClass(
|
|
4906
|
+
existingClassName(...args)
|
|
4907
|
+
)
|
|
4908
|
+
: withAutoGrowClass(existingClassName),
|
|
4909
|
+
render: (arg) => {
|
|
4910
|
+
const content = originalRender
|
|
4911
|
+
? originalRender(arg)
|
|
4912
|
+
: arg?.value ?? null;
|
|
4913
|
+
|
|
4914
|
+
const row = arg?.data;
|
|
4915
|
+
|
|
4916
|
+
// Group rows keep the default height, and an
|
|
4917
|
+
// empty cell has nothing to grow for.
|
|
4918
|
+
if (
|
|
4919
|
+
!row ||
|
|
4920
|
+
row.__group ||
|
|
4921
|
+
content === null ||
|
|
4922
|
+
content === undefined ||
|
|
4923
|
+
content === false
|
|
4924
|
+
) {
|
|
4925
|
+
return content;
|
|
4926
|
+
}
|
|
4927
|
+
|
|
4928
|
+
return (
|
|
4929
|
+
<AutoGrowCell
|
|
4930
|
+
rowId={row.id}
|
|
4931
|
+
onMeasure={reportAutoGrowHeight}
|
|
4932
|
+
>
|
|
4933
|
+
{content}
|
|
4934
|
+
</AutoGrowCell>
|
|
4935
|
+
);
|
|
4936
|
+
},
|
|
4937
|
+
};
|
|
4938
|
+
};
|
|
4939
|
+
|
|
4597
4940
|
// decorateTreeChildIndent sits INSIDE
|
|
4598
4941
|
// decorateTreeParentActions: the latter draws parent rows
|
|
4599
4942
|
// itself and delegates everything else downward, so the
|
|
4600
4943
|
// member indent has to be the render it delegates to.
|
|
4601
4944
|
const newColumns = columns.map((column) =>
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4945
|
+
decorateAutoGrow(
|
|
4946
|
+
decorateColumnRowActions(
|
|
4947
|
+
decorateTreeParentActions(
|
|
4948
|
+
decorateTreeChildIndent(
|
|
4949
|
+
guardTreeParentRender(
|
|
4950
|
+
renderColumn(column),
|
|
4951
|
+
column
|
|
4952
|
+
),
|
|
4607
4953
|
column
|
|
4608
4954
|
),
|
|
4609
4955
|
column
|
|
@@ -4872,11 +5218,42 @@ const DataGrid = forwardRef(
|
|
|
4872
5218
|
// Data Grid Styling
|
|
4873
5219
|
const [windowHeight, setWindowHeight] = useState(window.innerHeight);
|
|
4874
5220
|
const [dataCount, setDataCount] = useState(0);
|
|
5221
|
+
|
|
5222
|
+
// Pages that mount the grid directly (rather than through GenericIndex)
|
|
5223
|
+
// pass no gridHeight, and fitToRows deliberately never grows past the
|
|
5224
|
+
// rows - so on a tall screen the grid sat at a few rows' height and
|
|
5225
|
+
// scrolled inside itself. The grid now measures what is left between
|
|
5226
|
+
// its own top edge and the bottom of the viewport; fitToRows remains
|
|
5227
|
+
// the first-paint fallback.
|
|
5228
|
+
//
|
|
5229
|
+
// Measured from `.dataGridContainer` rather than from the whole
|
|
5230
|
+
// component, so the search/create bar and the legend above it are part
|
|
5231
|
+
// of `top` instead of being asked for a second time below.
|
|
5232
|
+
// `measureChrome` subtracts what the *page* needs under the grid — the
|
|
5233
|
+
// card's padding and border, the page gutter, any section rendered
|
|
5234
|
+
// after it — which used to be a flat 28px guess that was wrong for any
|
|
5235
|
+
// page nesting its grid differently.
|
|
5236
|
+
const containerRef = useRef(null);
|
|
5237
|
+
const measuredHeight = useAvailableHeight(containerRef, {
|
|
5238
|
+
bottomGutter: 0,
|
|
5239
|
+
measureChrome: true,
|
|
5240
|
+
recomputeKey: windowHeight,
|
|
5241
|
+
});
|
|
5242
|
+
const resolvedHeight =
|
|
5243
|
+
gridHeight && gridHeight > 0
|
|
5244
|
+
? gridHeight
|
|
5245
|
+
: measuredHeight != null && measuredHeight > 0
|
|
5246
|
+
? measuredHeight
|
|
5247
|
+
: null;
|
|
5248
|
+
|
|
5249
|
+
// Both minHeight AND height: with minHeight alone the grid settles at
|
|
5250
|
+
// its content height on a tall screen. An explicit height makes it
|
|
5251
|
+
// take the room measured for it.
|
|
5252
|
+
const initialHeight =
|
|
5253
|
+
resolvedHeight ?? fitToRows(dataCount, windowHeight);
|
|
4875
5254
|
const [gridStyle, setGridStyle] = useState({
|
|
4876
|
-
minHeight:
|
|
4877
|
-
|
|
4878
|
-
? gridHeight
|
|
4879
|
-
: fitToRows(dataCount, windowHeight),
|
|
5255
|
+
minHeight: initialHeight,
|
|
5256
|
+
height: initialHeight,
|
|
4880
5257
|
boxShadow: 'none',
|
|
4881
5258
|
...(hoverColor ? { '--hover-color': hoverColor } : {}),
|
|
4882
5259
|
});
|
|
@@ -4884,39 +5261,116 @@ const DataGrid = forwardRef(
|
|
|
4884
5261
|
style: style ? style : {},
|
|
4885
5262
|
};
|
|
4886
5263
|
|
|
5264
|
+
/**
|
|
5265
|
+
* The part of the grid's height that is not data rows: the column
|
|
5266
|
+
* header, the filter row (only some pages have one), the footer rows
|
|
5267
|
+
* and the pagination toolbar.
|
|
5268
|
+
*
|
|
5269
|
+
* Read off the rendered grid rather than assumed, because it changes
|
|
5270
|
+
* with density, with `enableFiltering`, and with whether the page shows
|
|
5271
|
+
* a footer — and because the pager *is* inside the grid element, so
|
|
5272
|
+
* getting this wrong is exactly what leaves the last row hidden behind
|
|
5273
|
+
* it or the pager stranded under a band of empty body.
|
|
5274
|
+
*
|
|
5275
|
+
* Returns null on the first pass, before the grid is in the DOM; the
|
|
5276
|
+
* caller falls back to GRID_CHROME.
|
|
5277
|
+
*/
|
|
5278
|
+
const measureGridChrome = () => {
|
|
5279
|
+
const root = containerRef.current;
|
|
5280
|
+
|
|
5281
|
+
if (!root || typeof root.querySelector !== 'function') {
|
|
5282
|
+
return null;
|
|
5283
|
+
}
|
|
5284
|
+
|
|
5285
|
+
const gridEl = root.querySelector('.InovuaReactDataGrid');
|
|
5286
|
+
const body = root.querySelector('.InovuaReactDataGrid__body');
|
|
5287
|
+
|
|
5288
|
+
if (!gridEl || !body) {
|
|
5289
|
+
return null;
|
|
5290
|
+
}
|
|
5291
|
+
|
|
5292
|
+
const chrome =
|
|
5293
|
+
gridEl.getBoundingClientRect().height -
|
|
5294
|
+
body.getBoundingClientRect().height;
|
|
5295
|
+
|
|
5296
|
+
return chrome > 0 ? chrome : null;
|
|
5297
|
+
};
|
|
5298
|
+
|
|
4887
5299
|
useEffect(() => {
|
|
4888
5300
|
const getMinimumHeight = () => {
|
|
4889
|
-
if (
|
|
5301
|
+
if (resolvedHeight > 0) return resolvedHeight;
|
|
4890
5302
|
return fitToRows(dataCount, window.innerHeight);
|
|
4891
5303
|
};
|
|
4892
5304
|
|
|
4893
5305
|
const minHeight = getMinimumHeight();
|
|
4894
|
-
setGridStyle((prevState) => ({
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
// 3. Calculated rows based on height (fallback)
|
|
4900
|
-
|
|
4901
|
-
// Only auto-calculate limit if user hasn't set a preference
|
|
4902
|
-
const userPreferredLimit = userProfile?.rows_per_page || userProfile?.settings?.rows_per_page;
|
|
5306
|
+
setGridStyle((prevState) => ({
|
|
5307
|
+
...prevState,
|
|
5308
|
+
minHeight,
|
|
5309
|
+
height: minHeight,
|
|
5310
|
+
}));
|
|
4903
5311
|
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
5312
|
+
// How many rows to ask for.
|
|
5313
|
+
//
|
|
5314
|
+
// The rule used to be "if the profile has a rows_per_page, never
|
|
5315
|
+
// auto-calculate". That is a stored default, not a decision anyone
|
|
5316
|
+
// made — it is 25 for most staff — so every index page on a 1080p
|
|
5317
|
+
// screen drew 25 rows and then several hundred px of empty grid
|
|
5318
|
+
// body under them, with the pager stranded at the bottom of it.
|
|
5319
|
+
//
|
|
5320
|
+
// The profile value now *seeds* the limit (see the initial state
|
|
5321
|
+
// and the effect that applies it once the profile arrives) and
|
|
5322
|
+
// nothing more. Once the grid has measured itself against the
|
|
5323
|
+
// window, the measurement wins — unless the user has picked a size
|
|
5324
|
+
// from the pager's "Results per page" control in this session, in
|
|
5325
|
+
// which case their choice is final for as long as the grid is
|
|
5326
|
+
// mounted. Whatever the pager does about persisting that choice to
|
|
5327
|
+
// the profile is unchanged.
|
|
5328
|
+
//
|
|
5329
|
+
// A page that passes an explicit `gridHeight` is not sized to the
|
|
5330
|
+
// window at all (a grid in a modal, a secondary grid on a detail
|
|
5331
|
+
// page), so it keeps the old order: profile preference, then a
|
|
5332
|
+
// configured `take`, then the arithmetic.
|
|
5333
|
+
const autoSizedToWindow = !(gridHeight && gridHeight > 0);
|
|
5334
|
+
const userPreferredLimit =
|
|
5335
|
+
userProfile?.rows_per_page ||
|
|
5336
|
+
userProfile?.settings?.rows_per_page;
|
|
5337
|
+
|
|
5338
|
+
if (userChoseLimitRef.current) {
|
|
5339
|
+
debugLog(
|
|
5340
|
+
'[DataGrid] Honouring the size the user picked this session:',
|
|
5341
|
+
limitRef.current
|
|
5342
|
+
);
|
|
5343
|
+
} else if (
|
|
5344
|
+
!autoSizedToWindow &&
|
|
5345
|
+
userPreferredLimit &&
|
|
5346
|
+
userPreferredLimit > 0
|
|
5347
|
+
) {
|
|
5348
|
+
debugLog(
|
|
5349
|
+
'[DataGrid] Fixed-height grid, respecting profile preference:',
|
|
5350
|
+
userPreferredLimit
|
|
5351
|
+
);
|
|
5352
|
+
} else if (!autoSizedToWindow && ajaxSetting?.take >= 35) {
|
|
4908
5353
|
setLimit(ajaxSetting.take);
|
|
4909
5354
|
} else {
|
|
4910
|
-
//
|
|
4911
|
-
//
|
|
4912
|
-
//
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
setLimit(
|
|
5355
|
+
// Prefer the chrome the grid is really wearing — the column
|
|
5356
|
+
// header, the filter row if this page has one, and the pager
|
|
5357
|
+
// toolbar all live inside `minHeight`, and they differ by
|
|
5358
|
+
// density and by config. GRID_CHROME is the fallback for the
|
|
5359
|
+
// first pass, before the grid exists to be measured.
|
|
5360
|
+
const chrome = measureGridChrome() ?? GRID_CHROME;
|
|
5361
|
+
|
|
5362
|
+
setLimit(rowsForHeight(minHeight, chrome, grid.rowUnit));
|
|
4918
5363
|
}
|
|
4919
|
-
}, [
|
|
5364
|
+
}, [
|
|
5365
|
+
dataCount,
|
|
5366
|
+
gridHeight,
|
|
5367
|
+
resolvedHeight,
|
|
5368
|
+
window.innerHeight,
|
|
5369
|
+
grid,
|
|
5370
|
+
ajaxSetting?.take,
|
|
5371
|
+
userProfile?.rows_per_page,
|
|
5372
|
+
userProfile?.settings?.rows_per_page,
|
|
5373
|
+
]);
|
|
4920
5374
|
|
|
4921
5375
|
useEffect(() => {
|
|
4922
5376
|
const debouncedHandleResize = debounce(() => {
|
|
@@ -5072,8 +5526,9 @@ const DataGrid = forwardRef(
|
|
|
5072
5526
|
</div>
|
|
5073
5527
|
)}
|
|
5074
5528
|
|
|
5075
|
-
{/* DataGrid container with its own border
|
|
5076
|
-
|
|
5529
|
+
{/* DataGrid container with its own border; its top edge is
|
|
5530
|
+
what the available height is measured from. */}
|
|
5531
|
+
<div className={styles.dataGridContainer} ref={containerRef}>
|
|
5077
5532
|
|
|
5078
5533
|
{limit && limit > 0 ? (
|
|
5079
5534
|
<ReactDataGrid
|
|
@@ -5328,126 +5783,130 @@ const DataGrid = forwardRef(
|
|
|
5328
5783
|
aria-label="Select all items in this group"
|
|
5329
5784
|
/>
|
|
5330
5785
|
)}
|
|
5331
|
-
<
|
|
5332
|
-
className={`group-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5786
|
+
<div
|
|
5787
|
+
className={`group-controls ${styles.groupControls}`}
|
|
5788
|
+
>
|
|
5789
|
+
<button
|
|
5790
|
+
className={`group-expand-btn ${styles.groupExpandBtn}`}
|
|
5791
|
+
onClick={(e) => {
|
|
5792
|
+
e.stopPropagation();
|
|
5793
|
+
if (
|
|
5794
|
+
typeof toggleGroup ===
|
|
5795
|
+
'function'
|
|
5796
|
+
) {
|
|
5797
|
+
toggleGroup();
|
|
5798
|
+
}
|
|
5799
|
+
}}
|
|
5800
|
+
onTouchStart={(e) => {
|
|
5801
|
+
e.stopPropagation();
|
|
5802
|
+
handleTouchStart(
|
|
5803
|
+
e,
|
|
5804
|
+
'group-expand',
|
|
5805
|
+
collapsed
|
|
5806
|
+
? 'Expand group'
|
|
5807
|
+
: 'Collapse group'
|
|
5808
|
+
);
|
|
5809
|
+
}}
|
|
5810
|
+
onTouchEnd={
|
|
5811
|
+
handleTouchEnd
|
|
5340
5812
|
}
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
'group-expand',
|
|
5813
|
+
onTouchMove={
|
|
5814
|
+
handleTouchMove
|
|
5815
|
+
}
|
|
5816
|
+
data-tooltip-id="system-tooltip"
|
|
5817
|
+
data-tooltip-content={
|
|
5347
5818
|
collapsed
|
|
5348
5819
|
? 'Expand group'
|
|
5349
5820
|
: 'Collapse group'
|
|
5350
|
-
);
|
|
5351
|
-
}}
|
|
5352
|
-
onTouchEnd={
|
|
5353
|
-
handleTouchEnd
|
|
5354
|
-
}
|
|
5355
|
-
onTouchMove={
|
|
5356
|
-
handleTouchMove
|
|
5357
|
-
}
|
|
5358
|
-
data-tooltip-id="system-tooltip"
|
|
5359
|
-
data-tooltip-content={
|
|
5360
|
-
collapsed
|
|
5361
|
-
? 'Expand group'
|
|
5362
|
-
: 'Collapse group'
|
|
5363
|
-
}
|
|
5364
|
-
>
|
|
5365
|
-
{collapsed
|
|
5366
|
-
? '+'
|
|
5367
|
-
: '−'}
|
|
5368
|
-
</button>
|
|
5369
|
-
{/* Dynamic group action buttons */}
|
|
5370
|
-
{ajaxSetting?.groupBySetting?.icons?.map(
|
|
5371
|
-
(
|
|
5372
|
-
iconConfig,
|
|
5373
|
-
index
|
|
5374
|
-
) => {
|
|
5375
|
-
// Check if this icon should be shown based on show conditions
|
|
5376
|
-
if (
|
|
5377
|
-
!shouldShowGroupAction(
|
|
5378
|
-
iconConfig,
|
|
5379
|
-
groupValue
|
|
5380
|
-
)
|
|
5381
|
-
) {
|
|
5382
|
-
return null;
|
|
5383
5821
|
}
|
|
5822
|
+
>
|
|
5823
|
+
{collapsed
|
|
5824
|
+
? '+'
|
|
5825
|
+
: '−'}
|
|
5826
|
+
</button>
|
|
5827
|
+
{/* Dynamic group action buttons */}
|
|
5828
|
+
{ajaxSetting?.groupBySetting?.icons?.map(
|
|
5829
|
+
(
|
|
5830
|
+
iconConfig,
|
|
5831
|
+
index
|
|
5832
|
+
) => {
|
|
5833
|
+
// Check if this icon should be shown based on show conditions
|
|
5834
|
+
if (
|
|
5835
|
+
!shouldShowGroupAction(
|
|
5836
|
+
iconConfig,
|
|
5837
|
+
groupValue
|
|
5838
|
+
)
|
|
5839
|
+
) {
|
|
5840
|
+
return null;
|
|
5841
|
+
}
|
|
5384
5842
|
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5843
|
+
const IconComponent =
|
|
5844
|
+
getGroupIconComponent(
|
|
5845
|
+
iconConfig.id
|
|
5846
|
+
);
|
|
5389
5847
|
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
}
|
|
5431
|
-
onTouchMove={
|
|
5432
|
-
handleTouchMove
|
|
5433
|
-
}
|
|
5434
|
-
data-tooltip-id="system-tooltip"
|
|
5435
|
-
data-tooltip-content={
|
|
5436
|
-
iconConfig.label
|
|
5437
|
-
}
|
|
5438
|
-
>
|
|
5439
|
-
<IconComponent
|
|
5440
|
-
strokeWidth={
|
|
5441
|
-
2
|
|
5848
|
+
// Generate different color classes for multiple buttons
|
|
5849
|
+
const colorClasses =
|
|
5850
|
+
[
|
|
5851
|
+
'success',
|
|
5852
|
+
'warning',
|
|
5853
|
+
'info',
|
|
5854
|
+
'purple',
|
|
5855
|
+
'pink',
|
|
5856
|
+
];
|
|
5857
|
+
const colorClass =
|
|
5858
|
+
colorClasses[
|
|
5859
|
+
index %
|
|
5860
|
+
colorClasses.length
|
|
5861
|
+
];
|
|
5862
|
+
|
|
5863
|
+
return (
|
|
5864
|
+
<button
|
|
5865
|
+
key={`group-action-${index}`}
|
|
5866
|
+
className={`group-action-btn group-${iconConfig.id}-btn ${styles.groupActionBtn} ${styles[colorClass]}`}
|
|
5867
|
+
onClick={(
|
|
5868
|
+
e
|
|
5869
|
+
) => {
|
|
5870
|
+
e.stopPropagation();
|
|
5871
|
+
handleGroupAction(
|
|
5872
|
+
groupValue,
|
|
5873
|
+
iconConfig
|
|
5874
|
+
);
|
|
5875
|
+
}}
|
|
5876
|
+
onTouchStart={(
|
|
5877
|
+
e
|
|
5878
|
+
) => {
|
|
5879
|
+
e.stopPropagation();
|
|
5880
|
+
handleTouchStart(
|
|
5881
|
+
e,
|
|
5882
|
+
`group-${iconConfig.id}-${index}`,
|
|
5883
|
+
iconConfig.label
|
|
5884
|
+
);
|
|
5885
|
+
}}
|
|
5886
|
+
onTouchEnd={
|
|
5887
|
+
handleTouchEnd
|
|
5442
5888
|
}
|
|
5443
|
-
|
|
5444
|
-
|
|
5889
|
+
onTouchMove={
|
|
5890
|
+
handleTouchMove
|
|
5445
5891
|
}
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5892
|
+
data-tooltip-id="system-tooltip"
|
|
5893
|
+
data-tooltip-content={
|
|
5894
|
+
iconConfig.label
|
|
5895
|
+
}
|
|
5896
|
+
>
|
|
5897
|
+
<IconComponent
|
|
5898
|
+
strokeWidth={
|
|
5899
|
+
2
|
|
5900
|
+
}
|
|
5901
|
+
size={
|
|
5902
|
+
14
|
|
5903
|
+
}
|
|
5904
|
+
/>
|
|
5905
|
+
</button>
|
|
5906
|
+
);
|
|
5907
|
+
}
|
|
5908
|
+
) || null}
|
|
5909
|
+
</div>
|
|
5451
5910
|
<div
|
|
5452
5911
|
className={
|
|
5453
5912
|
styles.groupInfo
|
|
@@ -5458,7 +5917,9 @@ const DataGrid = forwardRef(
|
|
|
5458
5917
|
styles.groupValue
|
|
5459
5918
|
}
|
|
5460
5919
|
>
|
|
5461
|
-
{
|
|
5920
|
+
{getGroupDisplayLabel(
|
|
5921
|
+
groupValue
|
|
5922
|
+
)}
|
|
5462
5923
|
{/* Important marker */}
|
|
5463
5924
|
{hasImportantMarker && (
|
|
5464
5925
|
<AlertCircle
|
|
@@ -5606,7 +6067,7 @@ const DataGrid = forwardRef(
|
|
|
5606
6067
|
onFilterValueChange={(fv) => {
|
|
5607
6068
|
handleFilterChange(fv, filterValue);
|
|
5608
6069
|
}}
|
|
5609
|
-
onLimitChange={
|
|
6070
|
+
onLimitChange={handleLimitChange}
|
|
5610
6071
|
onSelectionChange={handleSelectionChange}
|
|
5611
6072
|
onSortInfoChange={handleSort}
|
|
5612
6073
|
onRenderRow={onRenderRow}
|