@applica-software-guru/react-admin 1.5.335 → 1.5.337

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.335"
111
+ "version": "1.5.337"
112
112
  }
@@ -0,0 +1,68 @@
1
+ import { Tab, Tabs } from '@mui/material';
2
+ import { useListContext } from 'react-admin';
3
+ import { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { useListViewContext } from './ListViewProvider';
5
+
6
+ type ListTabToolbarConfig = {
7
+ label: string;
8
+ key: string;
9
+ icon?: ReactElement;
10
+ filter: Record<string, any>;
11
+ default?: boolean;
12
+ };
13
+
14
+ interface ListTabsToolbarProps {
15
+ tabs?: ListTabToolbarConfig[];
16
+ }
17
+
18
+ function ListTabsToolbar(props: ListTabsToolbarProps) {
19
+ const { tabs = [] } = props;
20
+ const { setFilters, filterValues } = useListContext();
21
+ const defaultTabIndex = useMemo(() => tabs.findIndex((tab) => tab.default) ?? 0, [tabs]);
22
+ const [currentTab, setCurrentTab] = useState(defaultTabIndex);
23
+ const initialized = useRef(false);
24
+ const { setCurrentTabKey } = useListViewContext();
25
+
26
+ if (tabs.length > 0 && tabs.some((tab) => !tab.key)) {
27
+ throw new Error('ListTabsToolbar: Each tab must have a unique key.');
28
+ }
29
+
30
+ const buildFilters = useCallback(
31
+ (index: number, debounce: boolean) => {
32
+ if (setCurrentTabKey) {
33
+ setCurrentTabKey(tabs[index].key);
34
+ }
35
+ const newFilters = {
36
+ ...filterValues,
37
+ ...tabs[index].filter
38
+ };
39
+ setFilters(newFilters, {}, debounce);
40
+ },
41
+ [filterValues, setFilters, setCurrentTabKey, tabs]
42
+ );
43
+
44
+ useEffect(() => {
45
+ if (!initialized.current) {
46
+ buildFilters(defaultTabIndex, false);
47
+ initialized.current = true;
48
+ }
49
+ }, [defaultTabIndex, buildFilters]);
50
+
51
+ const handleChange = useCallback(
52
+ (_: any, newIndex: number) => {
53
+ setCurrentTab(newIndex);
54
+ buildFilters(newIndex, true);
55
+ },
56
+ [buildFilters]
57
+ );
58
+
59
+ return tabs.length > 0 ? (
60
+ <Tabs value={currentTab} onChange={handleChange}>
61
+ {tabs.map((tab) => (
62
+ <Tab icon={tab.icon} key={tab.label} label={tab.label} />
63
+ ))}
64
+ </Tabs>
65
+ ) : null;
66
+ }
67
+
68
+ export { type ListTabToolbarConfig, ListTabsToolbar };
@@ -17,6 +17,7 @@ import { ListToolbar } from './ListToolbar';
17
17
  import { FilterSidebar } from './FilterSidebar';
18
18
  import { MainCard } from '@/components/MainCard';
19
19
  import { Empty } from '@/components/ra-lists/Empty';
20
+ import { type ListTabToolbarConfig, ListTabsToolbar } from './ListTabsToolbar';
20
21
 
21
22
  const defaultActions = <DefaultActions />;
22
23
  const defaultPagination = <DefaultPagination />;
@@ -37,6 +38,7 @@ function ListView<RecordType extends RaRecord = any>(props: ListViewProps): Reac
37
38
  component: Content = DefaultComponent,
38
39
  title,
39
40
  empty = defaultEmpty,
41
+ tabs,
40
42
  ...rest
41
43
  } = props;
42
44
  const { defaultTitle, data, error, isLoading, filterValues, resource } = useListContext<RecordType>(props);
@@ -63,6 +65,7 @@ function ListView<RecordType extends RaRecord = any>(props: ListViewProps): Reac
63
65
  backgroundColor: hasAsideSidebar ? theme.palette.background.paper : 'transparent'
64
66
  }}
65
67
  >
68
+ {tabs ? <ListTabsToolbar tabs={tabs} /> : null}
66
69
  {filters || actions ? (
67
70
  <ListToolbar
68
71
  className={ListClasses.actions}
@@ -199,6 +202,23 @@ const ListClasses = {
199
202
  };
200
203
 
201
204
  interface ListViewProps {
205
+ /**
206
+ * Tabs to display in the toolbar. Each tab is a filter object with a label, key, icon, filter, and an optional default property.
207
+ *
208
+ * @example
209
+ * import { List, ListTabToolbarConfig } from 'react-admin';
210
+ * const tabs: ListTabToolbarConfig[] = [
211
+ * { label: 'Published', key: 'published', icon: <IconPublished />, filter: { status: 'published' }, default: true },
212
+ * { label: 'Drafts', key: 'drafts', icon: <IconDrafts />, filter: { status: 'draft' } },
213
+ * ];
214
+ * const PostList = () => (
215
+ * <List tabs={tabs}>
216
+ * ...
217
+ * </List>
218
+ * );
219
+ */
220
+ tabs?: ListTabToolbarConfig[];
221
+
202
222
  /**
203
223
  * The actions to display in the toolbar. defaults to Filter + Create + Export.
204
224
  *
@@ -1,19 +1,31 @@
1
1
  import React, { createContext, useContext } from 'react';
2
2
 
3
3
  interface ListViewContextType {
4
+ /* FilterSidebar props */
4
5
  sidebarOpen?: boolean;
5
6
  setSidebarOpen?: (open: boolean) => void;
7
+
8
+ /* ListTabsToolbar props */
9
+ currentTabKey?: string;
10
+ setCurrentTabKey?: (key: string) => void;
6
11
  }
7
12
 
8
13
  const ListViewContext = createContext<ListViewContextType>({
9
14
  sidebarOpen: false,
10
- setSidebarOpen: () => {}
15
+ setSidebarOpen: () => {},
16
+ currentTabKey: '',
17
+ setCurrentTabKey: () => {}
11
18
  });
12
19
 
13
20
  function ListViewProvider(props: { children: React.ReactNode }) {
14
21
  const [sidebarOpen, setSidebarOpen] = React.useState(false);
22
+ const [currentTabKey, setCurrentTabKey] = React.useState('');
15
23
 
16
- return <ListViewContext.Provider value={{ sidebarOpen, setSidebarOpen }}>{props.children}</ListViewContext.Provider>;
24
+ return (
25
+ <ListViewContext.Provider value={{ currentTabKey, setCurrentTabKey, sidebarOpen, setSidebarOpen }}>
26
+ {props.children}
27
+ </ListViewContext.Provider>
28
+ );
17
29
  }
18
30
 
19
31
  function useListViewContext() {
@@ -4,6 +4,7 @@ export * from './Datagrid';
4
4
  export * from './Empty';
5
5
  export * from './FilterSidebar';
6
6
  export * from './List';
7
+ export * from './ListTabsToolbar';
7
8
  export * from './ListToolbar';
8
9
  export * from './ListViewProvider';
9
10
  export * from './NotificationList';
package/src/index.ts CHANGED
@@ -46,6 +46,7 @@ export {
46
46
  ValidationError,
47
47
  WithListContext,
48
48
  choices,
49
+ downloadCSV,
49
50
  email,
50
51
  maxLength,
51
52
  maxValue,