@visns-studio/visns-components 5.8.2 → 5.8.3

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
@@ -4,10 +4,10 @@
4
4
  "@fontsource/barlow": "^5.2.5",
5
5
  "@inovua/reactdatagrid-community": "^5.10.2",
6
6
  "@inovua/reactdatagrid-enterprise": "^5.10.2",
7
- "@nivo/bar": "^0.88.0",
8
- "@nivo/core": "^0.88.0",
9
- "@nivo/line": "^0.88.0",
10
- "@nivo/pie": "^0.88.0",
7
+ "@nivo/bar": "^0.95.0",
8
+ "@nivo/core": "^0.95.0",
9
+ "@nivo/line": "^0.95.0",
10
+ "@nivo/pie": "^0.95.0",
11
11
  "@tinymce/tinymce-react": "^6.1.0",
12
12
  "@visns-studio/visns-datagrid-community": "^1.0.14",
13
13
  "@visns-studio/visns-datagrid-enterprise": "^1.0.14",
@@ -18,14 +18,14 @@
18
18
  "awesome-debounce-promise": "^2.1.0",
19
19
  "browser-image-compression": "^2.0.2",
20
20
  "dayjs": "^1.11.13",
21
- "fabric": "^6.6.4",
21
+ "fabric": "^6.6.5",
22
22
  "file-saver": "^2.0.5",
23
- "framer-motion": "^12.9.7",
24
- "html-react-parser": "^5.2.3",
23
+ "framer-motion": "^12.11.0",
24
+ "html-react-parser": "^5.2.5",
25
25
  "lodash": "^4.17.21",
26
26
  "lodash.debounce": "^4.0.8",
27
27
  "moment": "^2.30.1",
28
- "motion": "^12.9.7",
28
+ "motion": "^12.11.0",
29
29
  "numeral": "^2.0.6",
30
30
  "pluralize": "^8.0.0",
31
31
  "qrcode.react": "^4.2.0",
@@ -60,12 +60,12 @@
60
60
  "validator": "^13.15.0",
61
61
  "vite": "^6.3.5",
62
62
  "yarn": "^1.22.22",
63
- "yet-another-react-lightbox": "^3.23.0"
63
+ "yet-another-react-lightbox": "^3.23.1"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@babel/core": "^7.27.1",
67
67
  "@babel/plugin-transform-runtime": "^7.27.1",
68
- "@babel/preset-env": "^7.27.1",
68
+ "@babel/preset-env": "^7.27.2",
69
69
  "@babel/preset-react": "^7.27.1",
70
70
  "babel-loader": "^10.0.0",
71
71
  "copy-webpack-plugin": "^13.0.0",
@@ -74,7 +74,7 @@
74
74
  "mini-css-extract-plugin": "^2.9.2",
75
75
  "react": "^18.3.1",
76
76
  "react-dom": "^18.3.1",
77
- "sass": "^1.87.0",
77
+ "sass": "^1.88.0",
78
78
  "sass-loader": "^16.0.5"
79
79
  },
80
80
  "peerDependencies": {
@@ -82,7 +82,7 @@
82
82
  "react-dom": "^17.0.0 || ^18.0.0"
83
83
  },
84
84
  "name": "@visns-studio/visns-components",
85
- "version": "5.8.2",
85
+ "version": "5.8.3",
86
86
  "description": "Various packages to assist in the development of our Custom Applications.",
87
87
  "main": "src/index.js",
88
88
  "files": [
@@ -8,12 +8,13 @@ function TableFilter({
8
8
  settings,
9
9
  type,
10
10
  onFilterChange,
11
+ tabs,
11
12
  }) {
12
13
  const [visibleChildren, setVisibleChildren] = useState({});
13
14
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
14
15
  const isFirstRender = useRef(true);
15
16
 
16
- // Initialize visible children for all parent categories
17
+ // Initialize visible children for all parent categories and set checkbox function availability
17
18
  useEffect(() => {
18
19
  if (isFirstRender.current && filters.length > 0) {
19
20
  const initialVisibleState = {};
@@ -21,26 +22,96 @@ function TableFilter({
21
22
  // Find the first parent with isParent=true and set only it to visible
22
23
  let foundFirstParent = false;
23
24
 
24
- for (const filter of filters) {
25
+ // Initialize filters with checkbox function availability
26
+ const updatedFilters = filters.map((filter) => {
27
+ // Find the tab configuration for this filter
28
+ const tabConfig = tabs?.find((tab) => tab.id === filter.id);
29
+
30
+ // Check if the tab has the checkboxUpdate and checkboxDelete functions
31
+ const hasCheckboxUpdate =
32
+ tabConfig?.functions?.checkboxUpdate !== undefined;
33
+ const hasCheckboxDelete =
34
+ tabConfig?.functions?.checkboxDelete !== undefined;
35
+
36
+ // Add checkbox function availability to the filter
37
+ const updatedFilter = {
38
+ ...filter,
39
+ hasCheckboxUpdate,
40
+ hasCheckboxDelete,
41
+ };
42
+
43
+ // If this filter has children, also add the checkbox function info to each child
25
44
  if (
26
- filter.isParent &&
27
- filter.children &&
28
- filter.children.length > 0
45
+ updatedFilter.isParent &&
46
+ updatedFilter.children &&
47
+ updatedFilter.children.length > 0
29
48
  ) {
30
- // Only show the first parent's children by default
31
- // and ensure all others are explicitly set to false
49
+ updatedFilter.children = updatedFilter.children.map(
50
+ (child, index) => {
51
+ // Find the tab configuration for this child
52
+ const childTabConfig = tabs?.find(
53
+ (tab) => tab.id === child.id
54
+ );
55
+
56
+ // Check if the tab has the checkboxUpdate and checkboxDelete functions
57
+ const childHasCheckboxUpdate =
58
+ childTabConfig?.functions?.checkboxUpdate !==
59
+ undefined;
60
+ const childHasCheckboxDelete =
61
+ childTabConfig?.functions?.checkboxDelete !==
62
+ undefined;
63
+
64
+ // If this is the first child and the parent is active but no child is active,
65
+ // this child will be automatically selected
66
+ const isFirstChild = index === 0;
67
+ const isParentActive = updatedFilter.show === true;
68
+ const noChildActive = !updatedFilter.children.some(
69
+ (c) => c.show === true
70
+ );
71
+
72
+ // If this is the first child and will be auto-selected, use its function availability
73
+ if (
74
+ isFirstChild &&
75
+ isParentActive &&
76
+ noChildActive
77
+ ) {
78
+ // Update the parent's function availability based on the first child
79
+ updatedFilter.hasCheckboxUpdate =
80
+ childHasCheckboxUpdate;
81
+ updatedFilter.hasCheckboxDelete =
82
+ childHasCheckboxDelete;
83
+ }
84
+
85
+ return {
86
+ ...child,
87
+ hasCheckboxUpdate: childHasCheckboxUpdate,
88
+ hasCheckboxDelete: childHasCheckboxDelete,
89
+ };
90
+ }
91
+ );
92
+
93
+ // Track which parent should be expanded by default
32
94
  initialVisibleState[filter.id] = !foundFirstParent;
33
95
 
34
96
  if (!foundFirstParent) {
35
97
  foundFirstParent = true;
36
98
  }
37
99
  }
100
+
101
+ return updatedFilter;
102
+ });
103
+
104
+ // Update the filters with the checkbox function availability
105
+ if (onFilterChange) {
106
+ onFilterChange(updatedFilters, null);
107
+ } else if (setFilters) {
108
+ setFilters(updatedFilters);
38
109
  }
39
110
 
40
111
  setVisibleChildren(initialVisibleState);
41
112
  isFirstRender.current = false;
42
113
  }
43
- }, [filters]);
114
+ }, [filters, tabs, onFilterChange, setFilters]);
44
115
 
45
116
  // Toggle visibility of children when clicking on a parent
46
117
  // Only one parent can be open at a time
@@ -74,11 +145,41 @@ function TableFilter({
74
145
  // If clicking on a parent category, toggle its children visibility
75
146
  if (filtertype === 'parent') {
76
147
  const clickedFilter = filters.find((f) => f.id === id);
77
- if (clickedFilter && clickedFilter.isParent) {
148
+ if (
149
+ clickedFilter &&
150
+ clickedFilter.isParent &&
151
+ clickedFilter.children &&
152
+ clickedFilter.children.length > 0
153
+ ) {
78
154
  toggleChildren(id);
79
155
 
80
- // Don't activate the parent as a filter, but keep processing to update settings
81
- // This allows the parent to expand/collapse while still triggering content updates
156
+ // When clicking on a parent, we need to explicitly select the first child
157
+ // This ensures the correct tab is activated and the correct buttons are shown
158
+ const firstChildId = clickedFilter.children[0].id;
159
+ const firstChildValue =
160
+ clickedFilter.children[0].hasOwnProperty('value')
161
+ ? clickedFilter.children[0].value
162
+ : '';
163
+
164
+ // Create a synthetic event for the first child
165
+ const childEvent = {
166
+ preventDefault: () => {},
167
+ target: {
168
+ dataset: {
169
+ id: firstChildId,
170
+ parent: id,
171
+ filtertype: 'children',
172
+ url: clickedFilter.children[0].url || '',
173
+ value: firstChildValue,
174
+ },
175
+ },
176
+ };
177
+
178
+ // Process the synthetic child event
179
+ filterTable(childEvent);
180
+
181
+ // Return early to prevent further processing of the parent click
182
+ return;
82
183
  }
83
184
  }
84
185
 
@@ -97,23 +198,25 @@ function TableFilter({
97
198
 
98
199
  // For parent filters
99
200
  if (filter.isParent) {
100
- // If this is the clicked parent, expand/collapse it
201
+ // If this is the clicked parent, expand/collapse it and activate the first child
101
202
  if (filter.id === id && filtertype === 'parent') {
102
- // Update children - don't change their active state
203
+ // Update children - activate the first child
103
204
  const updatedChildren = filter.children
104
- ? filter.children.map((child) => ({
205
+ ? filter.children.map((child, index) => ({
105
206
  ...child,
106
- // Preserve existing active state
107
- class: child.class,
108
- show: child.show,
207
+ // Set the first child as active
208
+ class: index === 0 ? 'subactivechildren' : '',
209
+ show: index === 0, // Only the first child is shown
210
+ active: index === 0, // Only the first child is active
109
211
  }))
110
212
  : [];
111
213
 
112
214
  return {
113
215
  ...filter,
114
- // Don't change parent's active state when just expanding/collapsing
115
- class: filter.class,
116
- show: filter.show,
216
+ // Set the parent as active
217
+ class: 'subactive',
218
+ show: true,
219
+ active: true,
117
220
  children: updatedChildren,
118
221
  };
119
222
  }
@@ -201,7 +304,63 @@ function TableFilter({
201
304
  };
202
305
 
203
306
  // Calculate the updated filters
204
- const updatedFilters = filters.map(updateFilter);
307
+ const updatedFilters = filters.map((filter) => {
308
+ const updatedFilter = updateFilter(filter);
309
+
310
+ // If this filter has children, process each child
311
+ if (updatedFilter.children && updatedFilter.children.length > 0) {
312
+ updatedFilter.children = updatedFilter.children.map(
313
+ (child, index) => {
314
+ // Find the tab configuration for this child
315
+ const tabConfig = tabs?.find(
316
+ (tab) => tab.id === child.id
317
+ );
318
+
319
+ // Check if the tab has the checkboxUpdate and checkboxDelete functions
320
+ const hasCheckboxUpdate =
321
+ tabConfig?.functions?.checkboxUpdate !== undefined;
322
+ const hasCheckboxDelete =
323
+ tabConfig?.functions?.checkboxDelete !== undefined;
324
+
325
+ // If this is the first child and the parent is active but no child is active,
326
+ // this child will be automatically selected
327
+ const isFirstChild = index === 0;
328
+ const isParentActive = updatedFilter.show === true;
329
+ const noChildActive = !updatedFilter.children.some(
330
+ (c) => c.show === true
331
+ );
332
+
333
+ // If this is the first child and will be auto-selected, use its function availability
334
+ if (isFirstChild && isParentActive && noChildActive) {
335
+ // Update the parent's function availability based on the first child
336
+ updatedFilter.hasCheckboxUpdate = hasCheckboxUpdate;
337
+ updatedFilter.hasCheckboxDelete = hasCheckboxDelete;
338
+ }
339
+
340
+ return {
341
+ ...child,
342
+ hasCheckboxUpdate,
343
+ hasCheckboxDelete,
344
+ };
345
+ }
346
+ );
347
+ }
348
+
349
+ // Find the tab configuration for this filter
350
+ const tabConfig = tabs?.find((tab) => tab.id === updatedFilter.id);
351
+
352
+ // Check if the tab has the checkboxUpdate and checkboxDelete functions
353
+ const hasCheckboxUpdate =
354
+ tabConfig?.functions?.checkboxUpdate !== undefined;
355
+ const hasCheckboxDelete =
356
+ tabConfig?.functions?.checkboxDelete !== undefined;
357
+
358
+ return {
359
+ ...updatedFilter,
360
+ hasCheckboxUpdate,
361
+ hasCheckboxDelete,
362
+ };
363
+ });
205
364
 
206
365
  // Only navigate to URL if it's provided and not a parent toggle action
207
366
  if (
@@ -623,6 +623,7 @@ function GenericIndex({
623
623
  settings={config}
624
624
  type={layout}
625
625
  onFilterChange={handleFilterChange}
626
+ tabs={setting.tabs}
626
627
  />
627
628
  </div>
628
629
  <div className={styles.tabcontent__main}>
@@ -646,6 +647,7 @@ function GenericIndex({
646
647
  settings={config}
647
648
  type={layout}
648
649
  onFilterChange={handleFilterChange}
650
+ tabs={setting.tabs}
649
651
  />
650
652
  </div>
651
653
  <div className={styles.grid__subcontent}>
@@ -692,6 +694,54 @@ function GenericIndex({
692
694
  }
693
695
  }, []);
694
696
 
697
+ // Add an effect to update the config when the active tab changes
698
+ useEffect(() => {
699
+ // Find the active filter and its child
700
+ const activeFilter = subnav?.find((filter) => {
701
+ if (filter.isParent) {
702
+ return (
703
+ filter.children &&
704
+ filter.children.some((child) => child.show)
705
+ );
706
+ }
707
+ return filter.show === true;
708
+ });
709
+
710
+ if (
711
+ activeFilter?.isParent &&
712
+ activeFilter.children &&
713
+ activeFilter.children.length > 0
714
+ ) {
715
+ // Find the active child
716
+ const activeChild = activeFilter.children.find(
717
+ (child) => child.show
718
+ );
719
+
720
+ // If no child is active but the parent is, activate the first child
721
+ if (!activeChild && activeFilter.show) {
722
+ const firstChild = activeFilter.children[0];
723
+ const firstChildId = firstChild.id;
724
+
725
+ // Find the tab config for the first child
726
+ const firstChildTabConfig = setting.tabs?.find(
727
+ (tab) => tab.id === firstChildId
728
+ );
729
+
730
+ if (firstChildTabConfig) {
731
+ // Update the config with the first child's tab config
732
+ setConfig((prevConfig) => ({
733
+ ...prevConfig,
734
+ id: firstChildTabConfig.id,
735
+ functions:
736
+ firstChildTabConfig.functions ||
737
+ prevConfig.functions ||
738
+ {},
739
+ }));
740
+ }
741
+ }
742
+ }
743
+ }, [subnav, setting.tabs, setConfig]);
744
+
695
745
  return (
696
746
  <>
697
747
  <div className={styles.grid}>
@@ -728,40 +778,142 @@ function GenericIndex({
728
778
  <Outlet />
729
779
 
730
780
  <div className={styles.polActions}>
731
- {/* Render Update button if checkboxUpdate function is configured in any location */}
732
- {getFunctionConfig('checkboxUpdate') && (
733
- <button
734
- className={styles.btn}
735
- onClick={handleCheckboxUpdate}
736
- >
737
- {getFunctionConfig('checkboxUpdate').label || 'Update'}
738
- </button>
739
- )}
781
+ {/* Find active filter to check if buttons should be shown */}
782
+ {(() => {
783
+ // Find the active item and its configuration
784
+ const activeItemInfo = (() => {
785
+ // First check if there's an active filter
786
+ const activeFilter = subnav?.find((filter) => {
787
+ if (filter.isParent) {
788
+ return (
789
+ filter.children &&
790
+ filter.children.some((child) => child.show)
791
+ );
792
+ }
793
+ return filter.show === true;
794
+ });
740
795
 
741
- {/* Render Delete button if checkboxDelete function is configured in any location */}
742
- {getFunctionConfig('checkboxDelete') && (
743
- <button
744
- className={styles.btn}
745
- onClick={handleCheckboxDelete}
746
- >
747
- {getFunctionConfig('checkboxDelete').label || 'Delete'}
748
- </button>
749
- )}
750
-
751
- {/* Render Custom Action button if customAction function is configured in any location */}
752
- {getFunctionConfig('customAction') && (
753
- <button className={styles.btn} onClick={handleCustomAction}>
754
- {getFunctionConfig('customAction').label ||
755
- 'Custom Action'}
756
- aa
757
- </button>
758
- )}
796
+ if (!activeFilter) {
797
+ return { activeTabId: undefined };
798
+ }
759
799
 
760
- {setting.export?.label && (
761
- <button className={styles.btn} onClick={handleExport}>
762
- {setting.export.label}
763
- </button>
764
- )}
800
+ // If the active filter is a parent
801
+ if (
802
+ activeFilter.isParent &&
803
+ activeFilter.children &&
804
+ activeFilter.children.length > 0
805
+ ) {
806
+ // Find the active child
807
+ const activeChild = activeFilter.children.find(
808
+ (child) => child.show
809
+ );
810
+
811
+ // If there's an active child, use its ID
812
+ if (activeChild) {
813
+ const childTabConfig = setting.tabs?.find(
814
+ (tab) => tab.id === activeChild.id
815
+ );
816
+ return {
817
+ activeTabId: activeChild.id,
818
+ activeTabConfig: childTabConfig,
819
+ };
820
+ }
821
+
822
+ // If no child is active but the parent is, use the first child's ID
823
+ // This handles the case when clicking on a parent automatically selects the first child
824
+ const firstChild = activeFilter.children[0];
825
+ const firstChildTabConfig = setting.tabs?.find(
826
+ (tab) => tab.id === firstChild.id
827
+ );
828
+
829
+ // Log for debugging
830
+ console.log(
831
+ 'No active child found, using first child:',
832
+ firstChild.id
833
+ );
834
+ console.log(
835
+ 'First child tab config:',
836
+ firstChildTabConfig
837
+ );
838
+
839
+ return {
840
+ activeTabId: firstChild.id,
841
+ activeTabConfig: firstChildTabConfig,
842
+ };
843
+ }
844
+
845
+ // For non-parent filters
846
+ const tabConfig = setting.tabs?.find(
847
+ (tab) => tab.id === activeFilter.id
848
+ );
849
+ return {
850
+ activeTabId: activeFilter.id,
851
+ activeTabConfig: tabConfig,
852
+ };
853
+ })();
854
+
855
+ const { activeTabId, activeTabConfig } = activeItemInfo;
856
+
857
+ // Check if the active tab has the checkboxUpdate function
858
+ const hasCheckboxUpdate =
859
+ activeTabConfig?.functions?.checkboxUpdate !==
860
+ undefined;
861
+
862
+ // Check if the active tab has the checkboxDelete function
863
+ const hasCheckboxDelete =
864
+ activeTabConfig?.functions?.checkboxDelete !==
865
+ undefined;
866
+
867
+ return (
868
+ <>
869
+ {/* Render Update button if checkboxUpdate function is configured and the active tab allows it */}
870
+ {getFunctionConfig('checkboxUpdate') &&
871
+ (activeTabId === undefined ||
872
+ hasCheckboxUpdate) && (
873
+ <button
874
+ className={styles.btn}
875
+ onClick={handleCheckboxUpdate}
876
+ >
877
+ {getFunctionConfig('checkboxUpdate')
878
+ .label || 'Update'}
879
+ </button>
880
+ )}
881
+
882
+ {/* Render Delete button if checkboxDelete function is configured and the active tab allows it */}
883
+ {getFunctionConfig('checkboxDelete') &&
884
+ (activeTabId === undefined ||
885
+ hasCheckboxDelete) && (
886
+ <button
887
+ className={styles.btn}
888
+ onClick={handleCheckboxDelete}
889
+ >
890
+ {getFunctionConfig('checkboxDelete')
891
+ .label || 'Delete'}
892
+ </button>
893
+ )}
894
+
895
+ {/* Render Custom Action button if customAction function is configured in any location */}
896
+ {getFunctionConfig('customAction') && (
897
+ <button
898
+ className={styles.btn}
899
+ onClick={handleCustomAction}
900
+ >
901
+ {getFunctionConfig('customAction').label ||
902
+ 'Custom Action'}
903
+ </button>
904
+ )}
905
+
906
+ {setting.export?.label && (
907
+ <button
908
+ className={styles.btn}
909
+ onClick={handleExport}
910
+ >
911
+ {setting.export.label}
912
+ </button>
913
+ )}
914
+ </>
915
+ );
916
+ })()}
765
917
  </div>
766
918
  </>
767
919
  );