@applica-software-guru/react-admin 1.5.362 → 1.5.363

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
@@ -108,5 +108,5 @@
108
108
  "type": "module",
109
109
  "types": "dist/index.d.ts",
110
110
  "typings": "dist/index.d.ts",
111
- "version": "1.5.362"
111
+ "version": "1.5.363"
112
112
  }
@@ -19,147 +19,141 @@ interface ListTabsToolbarProps {
19
19
  tabs?: ListTabToolbarConfig[];
20
20
  }
21
21
 
22
- function ListTabsToolbar({ tabs = [] }: ListTabsToolbarProps) {
23
- const { setFilters, filterValues, displayedFilters } = useListContext();
24
- const { setCurrentTabKey } = useListViewContext();
25
- const resource = useResourceContext();
26
- const theme = useTheme();
27
- const [searchParams, setSearchParams] = useSearchParams();
22
+ interface TabFilterState {
23
+ filterValues: Record<string, any>;
24
+ displayedFilters: Record<string, any>;
25
+ }
28
26
 
29
- if (tabs.length > 0 && tabs.some((tab) => !tab.key)) {
30
- throw new Error('ListTabsToolbar: Each tab must have a unique key.');
31
- }
27
+ function useListTabs(tabs: ListTabToolbarConfig[]) {
28
+ const [searchParams] = useSearchParams();
29
+ const resource = useResourceContext();
30
+ const { setCurrentTabKey } = useListViewContext();
31
+ const { filterValues, displayedFilters, setFilters } = useListContext();
32
32
 
33
- const defaultTabIndex = useMemo(() => tabs.findIndex((tab) => tab.default) ?? 0, [tabs]);
34
33
  const resourceKey = useMemo(() => resource.replace(/^entities\//, ''), [resource]);
35
- const tabGroupKey = useMemo(() => `${resourceKey}-tab-group`, [resourceKey]);
36
- const tabFiltersKey = useMemo(() => `${resourceKey}-tab-filters`, [resourceKey]);
34
+ const tabGroupKey = `${resourceKey}-tab-group`;
35
+ const tabFiltersKey = `${resourceKey}-tab-filters`;
37
36
 
38
- const [currentTab, setCurrentTab] = useSessionStorage(tabGroupKey, defaultTabIndex);
39
- const [tabFilterStates, setTabFilterStates] = useSessionStorage(tabFiltersKey, {});
40
- const initialized = useRef(false);
41
-
42
- const saveCurrentTabFilters = useMemo(
43
- () =>
44
- _.debounce((tabKey: string, filters: any, displayed: any) => {
45
- setTabFilterStates((prev: any) => ({
46
- ...prev,
47
- [tabKey]: {
48
- filterValues: { ...filters },
49
- displayedFilters: { ...displayed }
50
- }
51
- }));
52
- }, 500),
53
- [setTabFilterStates]
54
- );
37
+ const defaultTabIndex = useMemo(() => tabs.findIndex((tab) => tab.default) ?? 0, [tabs]);
55
38
 
56
- const restoreTabFilters = useCallback(
57
- (tabKey: string, tabFilter: Record<string, any>, debounce: boolean) => {
58
- const savedState = (tabFilterStates as any)?.[tabKey];
39
+ const [currentTab, setCurrentTab] = useSessionStorage(tabGroupKey, defaultTabIndex) as [number, (v: number) => void];
40
+ const [tabFilterStates, setTabFilterStates] = useSessionStorage(tabFiltersKey, {}) as [
41
+ Record<string, TabFilterState>,
42
+ (v: any) => void
43
+ ];
59
44
 
60
- if (savedState) {
61
- setFilters(savedState.filterValues, savedState.displayedFilters || {}, debounce);
62
- } else {
63
- setFilters(tabFilter, {}, debounce);
64
- }
65
- },
66
- [tabFilterStates, setFilters]
67
- );
45
+ const initialized = useRef(false);
46
+ const switchingTab = useRef(false);
68
47
 
69
- const buildFilters = useCallback(
70
- (index: number, debounce: boolean) => {
71
- const newTabKey = tabs[index].key;
48
+ const handleTabChange = useCallback(
49
+ (index: number) => {
50
+ const tab = tabs[index];
51
+ if (!tab) return;
72
52
 
73
- setCurrentTabKey?.(newTabKey);
74
- restoreTabFilters(newTabKey, tabs[index].filter, debounce);
75
- },
76
- [tabs, setCurrentTabKey, restoreTabFilters]
77
- );
53
+ setCurrentTab(index);
54
+ setCurrentTabKey?.(tab.key);
78
55
 
79
- const updateUrlParam = useCallback(
80
- (index: number) => {
81
- const newParams = new URLSearchParams(searchParams.toString());
82
- newParams.set(tabGroupKey, String(index));
83
- setSearchParams(newParams, { replace: true });
84
- },
85
- [searchParams, setSearchParams, tabGroupKey]
86
- );
56
+ const savedState = tabFilterStates?.[tab.key];
57
+ const nextFilters = savedState?.filterValues ?? tab.filter;
58
+ const nextDisplayed = savedState?.displayedFilters ?? {};
87
59
 
88
- const handleChange = useCallback(
89
- (_: any, newIndex: number) => {
90
- setCurrentTab(newIndex);
91
- buildFilters(newIndex, true);
60
+ if (!_.isEqual(nextFilters, filterValues) || !_.isEqual(nextDisplayed, displayedFilters)) {
61
+ switchingTab.current = true;
62
+ setFilters(nextFilters, nextDisplayed);
63
+ }
92
64
  },
93
- [buildFilters, setCurrentTab]
65
+ [tabs, tabFilterStates, filterValues, displayedFilters, setCurrentTab, setCurrentTabKey, setFilters]
94
66
  );
95
67
 
96
68
  useEffect(() => {
97
- if (initialized.current && tabs.length > 0) {
98
- const currentTabKey = tabs[Number(currentTab)]?.key;
99
- if (currentTabKey) {
100
- saveCurrentTabFilters(currentTabKey, filterValues, displayedFilters);
101
- }
102
- }
103
- }, [filterValues, displayedFilters, currentTab, tabs, saveCurrentTabFilters]);
69
+ if (initialized.current) return;
70
+ initialized.current = true;
104
71
 
105
- useEffect(() => {
106
72
  const param = searchParams.get(tabGroupKey);
107
- if (param !== String(currentTab)) {
108
- updateUrlParam(Number(currentTab));
73
+ const initialIndex = param != null ? Number(param) : Number(currentTab);
74
+ const hasUrlFilter = searchParams.get('filter') !== null;
75
+
76
+ if (hasUrlFilter) {
77
+ setCurrentTab(initialIndex);
78
+ setCurrentTabKey?.(tabs[initialIndex]?.key);
79
+ } else {
80
+ handleTabChange(initialIndex);
109
81
  }
110
- // eslint-disable-next-line react-hooks/exhaustive-deps
111
- }, [filterValues]);
82
+ }, [searchParams, tabGroupKey, currentTab, setCurrentTab, setCurrentTabKey, handleTabChange, tabs]);
112
83
 
113
84
  useEffect(() => {
114
- if (!initialized.current) {
115
- const param = searchParams.get(tabGroupKey);
116
- const initialIndex = param != null ? Number(param) : Number(currentTab);
85
+ if (!initialized.current || switchingTab.current) {
86
+ switchingTab.current = false;
87
+ return;
88
+ }
89
+
90
+ const currentTabKey = tabs[Number(currentTab)]?.key;
91
+ if (!currentTabKey) return;
117
92
 
118
- if (param != null && initialIndex !== Number(currentTab)) {
119
- setCurrentTab(initialIndex);
93
+ setTabFilterStates((prev: Record<string, TabFilterState>) => {
94
+ const prevState = prev?.[currentTabKey];
95
+ if (
96
+ _.isEqual(prevState?.filterValues, filterValues) &&
97
+ _.isEqual(prevState?.displayedFilters, displayedFilters)
98
+ ) {
99
+ return prev;
120
100
  }
101
+ return {
102
+ ...prev,
103
+ [currentTabKey]: { filterValues, displayedFilters }
104
+ };
105
+ });
106
+ }, [filterValues, displayedFilters, currentTab, tabs, setTabFilterStates]);
107
+
108
+ return {
109
+ currentTab,
110
+ handleTabChange: (_: any, val: number) => handleTabChange(val)
111
+ };
112
+ }
121
113
 
122
- buildFilters(initialIndex, false);
123
- initialized.current = true;
124
- }
125
- // eslint-disable-next-line react-hooks/exhaustive-deps
126
- }, []);
114
+ function ListTabsToolbar({ tabs = [] }: ListTabsToolbarProps) {
115
+ const theme = useTheme();
116
+ const { currentTab, handleTabChange } = useListTabs(tabs);
117
+
118
+ if (tabs.length > 0 && tabs.some((tab) => !tab.key)) {
119
+ throw new Error('ListTabsToolbar: Each tab must have a unique key.');
120
+ }
121
+
122
+ if (tabs.length === 0) return null;
127
123
 
128
124
  return (
129
125
  <Box sx={{ width: '100%' }}>
130
- {tabs.length > 0 ? (
131
- <Tabs
132
- value={Number(currentTab)}
133
- onChange={handleChange}
134
- allowScrollButtonsMobile
135
- variant="scrollable"
136
- scrollButtons="auto"
137
- sx={{
138
- width: '100%',
139
- '.MuiTabs-scrollButtons.Mui-disabled': {
140
- opacity: 0.2
141
- }
142
- }}
143
- >
144
- {tabs.map((tab) => (
145
- <Tab
146
- icon={tab.icon}
147
- key={tab.key}
148
- label={tab.label}
149
- sx={{
150
- flex: '0 0 auto',
151
- lineHeight: 1,
152
- whiteSpace: 'nowrap',
153
- overflow: 'hidden',
154
- textOverflow: 'ellipsis',
155
- '& .MuiTab-iconWrapper': {
156
- marginRight: theme.spacing(0.5)
157
- }
158
- }}
159
- />
160
- ))}
161
- </Tabs>
162
- ) : null}
126
+ <Tabs
127
+ value={Number(currentTab)}
128
+ onChange={handleTabChange}
129
+ allowScrollButtonsMobile
130
+ variant="scrollable"
131
+ scrollButtons="auto"
132
+ sx={{
133
+ width: '100%',
134
+ '.MuiTabs-scrollButtons.Mui-disabled': {
135
+ opacity: 0.2
136
+ }
137
+ }}
138
+ >
139
+ {tabs.map((tab) => (
140
+ <Tab
141
+ icon={tab.icon}
142
+ key={tab.key}
143
+ label={tab.label}
144
+ sx={{
145
+ flex: '0 0 auto',
146
+ lineHeight: 1,
147
+ whiteSpace: 'nowrap',
148
+ overflow: 'hidden',
149
+ textOverflow: 'ellipsis',
150
+ '& .MuiTab-iconWrapper': {
151
+ marginRight: theme.spacing(0.5)
152
+ }
153
+ }}
154
+ />
155
+ ))}
156
+ </Tabs>
163
157
  </Box>
164
158
  );
165
159
  }