@visns-studio/visns-components 5.16.2 → 5.18.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/package.json CHANGED
@@ -94,7 +94,7 @@
94
94
  "react-dom": "^17.0.0 || ^18.0.0"
95
95
  },
96
96
  "name": "@visns-studio/visns-components",
97
- "version": "5.16.2",
97
+ "version": "5.18.0",
98
98
  "description": "Various packages to assist in the development of our Custom Applications.",
99
99
  "main": "src/index.js",
100
100
  "files": [
@@ -921,6 +921,36 @@ const DataGrid = forwardRef(
921
921
  [selected, ajaxSetting]
922
922
  );
923
923
 
924
+ const isGroupPartiallySelected = useCallback(
925
+ (groupValue) => {
926
+ if (!dataRef.current || !ajaxSetting?.groupBy) {
927
+ return false;
928
+ }
929
+
930
+ const groupField = ajaxSetting.groupBy[0];
931
+ const validRows = dataRef.current.filter(
932
+ (row) =>
933
+ row &&
934
+ row[groupField] === groupValue &&
935
+ row.id !== undefined &&
936
+ row.id !== null
937
+ );
938
+
939
+ if (validRows.length === 0) {
940
+ return false;
941
+ }
942
+
943
+ const selectedCount = validRows.filter(
944
+ (row) =>
945
+ selected[row.id] &&
946
+ !selected[row.id].__group
947
+ ).length;
948
+
949
+ return selectedCount > 0 && selectedCount < validRows.length;
950
+ },
951
+ [selected, ajaxSetting]
952
+ );
953
+
924
954
  const findUpdatedFilter = (filters, currentValues) => {
925
955
  for (const filter of filters) {
926
956
  const matchingCurrentValue = currentValues.find(
@@ -1275,6 +1305,11 @@ const DataGrid = forwardRef(
1275
1305
  case 'update':
1276
1306
  modalOpen('update', d.id);
1277
1307
  break;
1308
+ case 'navigate':
1309
+ if (s.url && s.key && d[s.key] != null) {
1310
+ navigate(`${s.url}${d[s.key]}`);
1311
+ }
1312
+ break;
1278
1313
  default:
1279
1314
  confirmDialog({
1280
1315
  title: s.title,
@@ -1393,11 +1428,21 @@ const DataGrid = forwardRef(
1393
1428
  // Check if all show conditions are met
1394
1429
  return iconConfig.show.every((condition) => {
1395
1430
  const { id, value } = condition;
1431
+ const hasValueProp = condition.hasOwnProperty('value');
1396
1432
 
1397
1433
  // Check if any row in the group matches the condition
1398
1434
  return groupRows.some((row) => {
1399
1435
  const rowValue = row[id];
1400
1436
 
1437
+ // No value prop → field must be truthy (set/non-empty)
1438
+ if (!hasValueProp) {
1439
+ return (
1440
+ rowValue !== null &&
1441
+ rowValue !== undefined &&
1442
+ rowValue !== ''
1443
+ );
1444
+ }
1445
+
1401
1446
  // Handle null value condition
1402
1447
  if (value === null) {
1403
1448
  return (
@@ -2533,6 +2578,8 @@ const DataGrid = forwardRef(
2533
2578
  return getIconComponent(RotateCcw);
2534
2579
  case 'update':
2535
2580
  return getIconComponent(Edit);
2581
+ case 'navigate':
2582
+ return getIconComponent(Inbox);
2536
2583
  case 'romComplete':
2537
2584
  return getIconComponent(CheckCircle);
2538
2585
  default:
@@ -3525,6 +3572,7 @@ const DataGrid = forwardRef(
3525
3572
 
3526
3573
  {/* DataGrid container with its own border */}
3527
3574
  <div className={styles.dataGridContainer}>
3575
+
3528
3576
  {limit && limit > 0 ? (
3529
3577
  <ReactDataGrid
3530
3578
  key={`datagrid-${ajaxSetting?.url || ''}-${
@@ -3747,6 +3795,37 @@ const DataGrid = forwardRef(
3747
3795
  styles.groupActionContainer
3748
3796
  }
3749
3797
  >
3798
+ {tableSetting?.checkboxColumn && (
3799
+ <input
3800
+ type="checkbox"
3801
+ className={
3802
+ styles.groupHeaderCheckbox
3803
+ }
3804
+ checked={isGroupSelected(
3805
+ groupValue
3806
+ )}
3807
+ ref={(el) => {
3808
+ if (el) {
3809
+ el.indeterminate =
3810
+ isGroupPartiallySelected(
3811
+ groupValue
3812
+ );
3813
+ }
3814
+ }}
3815
+ onClick={(e) =>
3816
+ e.stopPropagation()
3817
+ }
3818
+ onChange={(e) => {
3819
+ e.stopPropagation();
3820
+ handleGroupSelectionChange(
3821
+ groupValue,
3822
+ e.target
3823
+ .checked
3824
+ );
3825
+ }}
3826
+ aria-label="Select all items in this group"
3827
+ />
3828
+ )}
3750
3829
  <button
3751
3830
  className={`group-expand-btn ${styles.groupExpandBtn}`}
3752
3831
  onClick={(e) => {
@@ -2229,6 +2229,30 @@ function Form({
2229
2229
  }
2230
2230
  }, [tableData.dataSource]);
2231
2231
 
2232
+ // Auto-submit on mount when formSettings.autoSubmit is true.
2233
+ // Used for "report" tabs that should show their results immediately instead of
2234
+ // requiring the user to click "View Report" with an empty filter form.
2235
+ // We trigger an actual click on the rendered Save button so the path is
2236
+ // identical to the user clicking it manually.
2237
+ const autoSubmittedRef = useRef(false);
2238
+ useEffect(() => {
2239
+ if (formSettings?.autoSubmit && !autoSubmittedRef.current) {
2240
+ autoSubmittedRef.current = true;
2241
+ const timer = setTimeout(() => {
2242
+ const saveBtn = document.querySelector('[data-type="save"]');
2243
+ if (saveBtn) {
2244
+ saveBtn.click();
2245
+ } else {
2246
+ handleSubmit({
2247
+ preventDefault: () => {},
2248
+ target: { dataset: { type: 'save' } },
2249
+ });
2250
+ }
2251
+ }, 150);
2252
+ return () => clearTimeout(timer);
2253
+ }
2254
+ }, [formSettings?.autoSubmit]);
2255
+
2232
2256
  useEffect(() => {
2233
2257
  formSettings.fields.forEach((field) => {
2234
2258
  // showIfEmpty: only show field when its own value is empty/null/undefined/[]
@@ -241,6 +241,20 @@ function GenericIndex({
241
241
  const [customActionFormData, setCustomActionFormData] = useState({});
242
242
  const [customActionConfig, setCustomActionConfig] = useState(null);
243
243
  const { windowHeight, windowWidth } = useWindowDimensions();
244
+
245
+ // Toggle a body-level "tablet-mode" class when this view opts in via
246
+ // setting.tabletMode. Lets consumer projects style the whole UI
247
+ // (including app-level chrome) for tablet use without touching this lib.
248
+ useEffect(() => {
249
+ if (!setting?.tabletMode) {
250
+ return undefined;
251
+ }
252
+ document.body.classList.add('tablet-mode');
253
+ return () => {
254
+ document.body.classList.remove('tablet-mode');
255
+ };
256
+ }, [setting?.tabletMode]);
257
+
244
258
  const { config, setConfig, subnav, setSubnav } = useConfig(
245
259
  setting,
246
260
  setting.tabs,
@@ -879,6 +879,21 @@
879
879
  position: relative;
880
880
  }
881
881
 
882
+ .groupHeaderCheckbox {
883
+ width: 22px;
884
+ height: 22px;
885
+ margin-right: 12px;
886
+ cursor: pointer;
887
+ accent-color: var(--primary-color, #3b82f6);
888
+ flex-shrink: 0;
889
+ border-radius: 4px;
890
+ transition: transform 0.15s ease;
891
+
892
+ &:hover {
893
+ transform: scale(1.08);
894
+ }
895
+ }
896
+
882
897
  .groupExpandBtn {
883
898
  background: var(--primary-color, #3b82f6);
884
899
  border: none;
@@ -1200,3 +1215,5 @@
1200
1215
  transform: scale(1.2);
1201
1216
  }
1202
1217
  }
1218
+
1219
+