@applica-software-guru/react-admin 1.2.116 → 1.2.118

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applica-software-guru/react-admin",
3
- "version": "1.2.116",
3
+ "version": "1.2.118",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -4,11 +4,11 @@ import { Card, Collapse, List, ListItem, ListItemProps, ListItemIcon, ListItemTe
4
4
  import { useSetActiveItem, useSyncWithLocation } from './Provider';
5
5
  import { IItem } from './types';
6
6
  import { useCallback, useMemo, useState } from 'react';
7
- import { useNavigate } from 'react-router';
8
- import { useChildren, useIsActive } from './hooks';
7
+ import { useChildren, useIsActive, useNavigateForm } from './hooks';
9
8
  import { ExpandLess, ExpandMore } from '@mui/icons-material';
10
9
  import { getLevel } from './utils';
11
10
  import { useThemeConfig } from '../../../hooks';
11
+ import { useTranslate } from 'ra-core';
12
12
 
13
13
  type IBaseNavMenuItemProps = React.PropsWithChildren<{
14
14
  id: string;
@@ -42,7 +42,8 @@ function NavMenu() {
42
42
  }
43
43
 
44
44
  function NavMenuItem(props: INavMenuItemProps) {
45
- const navMenuItemProps = useNavMenuItem(props),
45
+ const translate = useTranslate() ?? _.identity,
46
+ navMenuItemProps = useNavMenuItem(props),
46
47
  { selected, label, icon, onClick: _onClick, badge, items, isGroup, level } = navMenuItemProps,
47
48
  { spacing } = useThemeConfig(),
48
49
  [open, setOpen] = useState(selected),
@@ -61,7 +62,7 @@ function NavMenuItem(props: INavMenuItemProps) {
61
62
  <ListItem {...listItemProps} disablePadding>
62
63
  <ListItemButton selected={selected && !isGroup} onClick={onClick} sx={{ pl: spacing * level }}>
63
64
  {hasIcon && <ListItemIcon>{icon}</ListItemIcon>}
64
- <ListItemText inset={!hasIcon} primary={label} />
65
+ <ListItemText inset={!hasIcon} primary={translate(label)} />
65
66
  {badge && (
66
67
  <ListItemAvatar>
67
68
  <Avatar type="filled" size="xs" color={badge.color ?? 'default'}>
@@ -91,7 +92,7 @@ function useNavMenuItem(
91
92
  const { id } = item,
92
93
  isActive = useIsActive(id),
93
94
  level = getLevel(id),
94
- navigate = useNavigate(),
95
+ navigate = useNavigateForm(),
95
96
  syncWithLocation = useSyncWithLocation(),
96
97
  setActiveItem = useSetActiveItem(),
97
98
  items = useChildren(id),
@@ -1,10 +1,11 @@
1
1
  import { createContext, useCallback, useContext, useEffect, useMemo, useReducer } from 'react';
2
2
  import _ from 'lodash';
3
3
  import { IItem } from './types';
4
- import { useLocation } from 'react-router';
4
+ import { matchPath, useLocation } from 'react-router';
5
5
  import { getItemsIds } from './utils';
6
6
 
7
7
  enum ActionType {
8
+ SET_FORM_ROOT_PATH = 'setFormRootPath',
8
9
  SET_SYNC_WITH_LOCATION = 'setSyncWithLocation',
9
10
  SET_ACTIVE_ITEM = 'setActiveItem',
10
11
  ADD_ITEM = 'addItem',
@@ -13,13 +14,19 @@ enum ActionType {
13
14
 
14
15
  type IProviderProps = React.PropsWithChildren<{
15
16
  syncWithLocation?: boolean;
17
+ rootMatchString?: string;
16
18
  }>;
17
19
  type IState = {
20
+ formRootPath?: string;
18
21
  syncWithLocation: boolean;
19
22
  items: Array<IItem>;
20
23
  activeItem?: string;
21
24
  };
22
25
  type IAction =
26
+ | {
27
+ type: ActionType.SET_FORM_ROOT_PATH;
28
+ payload: string;
29
+ }
23
30
  | {
24
31
  type: ActionType.SET_SYNC_WITH_LOCATION;
25
32
  payload: boolean;
@@ -46,6 +53,8 @@ function reducer(state: IState, action: IAction): IState {
46
53
  const newState = _.clone(state),
47
54
  { type, payload } = action;
48
55
  switch (type) {
56
+ case ActionType.SET_FORM_ROOT_PATH:
57
+ return _.extend(newState, { formRootPath: payload });
49
58
  case ActionType.SET_SYNC_WITH_LOCATION:
50
59
  return _.extend(newState, { syncWithLocation: payload });
51
60
  case ActionType.SET_ACTIVE_ITEM:
@@ -85,11 +94,33 @@ const Context = createContext<IContext | undefined>(undefined);
85
94
 
86
95
  function Provider(props: IProviderProps) {
87
96
  const syncWithLocation = Boolean(props.syncWithLocation ?? true),
97
+ { rootMatchString } = props,
88
98
  { pathname } = useLocation(),
89
99
  [state, dispatch] = useReducer(reducer, _.cloneDeep(DefaultState)),
90
- { items } = state,
100
+ { formRootPath, items } = state,
91
101
  value = useMemo(() => ({ state: state, dispatch: dispatch }), [state, dispatch]);
92
102
 
103
+ useEffect(() => {
104
+ // I possibili match sono ordinati per priorità (è fondamentale).
105
+ const matchStrings = ['entities/:resource/create/*', ':resource/create/*', 'entities/:resource/:id/*', ':resource/:id/*'];
106
+ if (rootMatchString !== undefined) {
107
+ matchStrings.push(rootMatchString);
108
+ }
109
+ const match = _.chain(matchStrings)
110
+ .map((s) => matchPath(s, pathname))
111
+ .reject((m) => m === null)
112
+ .first()
113
+ .value(),
114
+ pathnameBase = match?.pathnameBase;
115
+
116
+ if (pathnameBase !== undefined && pathnameBase !== formRootPath) {
117
+ dispatch({
118
+ type: ActionType.SET_FORM_ROOT_PATH,
119
+ payload: pathnameBase
120
+ });
121
+ }
122
+ }, [pathname, formRootPath, rootMatchString]);
123
+
93
124
  useEffect(() => {
94
125
  dispatch({
95
126
  type: ActionType.SET_SYNC_WITH_LOCATION,
@@ -98,16 +129,14 @@ function Provider(props: IProviderProps) {
98
129
  }, [syncWithLocation, dispatch]);
99
130
 
100
131
  useEffect(() => {
101
- if (syncWithLocation) {
102
- const match = pathname.match(/([^/]+)$/);
103
- if (match && match[0]) {
104
- dispatch({
105
- type: ActionType.SET_ACTIVE_ITEM,
106
- payload: match[0]
107
- });
108
- }
132
+ if (syncWithLocation && formRootPath !== undefined) {
133
+ const locationItem = pathname.replace(`${formRootPath}/`, '');
134
+ dispatch({
135
+ type: ActionType.SET_ACTIVE_ITEM,
136
+ payload: locationItem
137
+ });
109
138
  }
110
- }, [dispatch, pathname, syncWithLocation, items]);
139
+ }, [dispatch, formRootPath, pathname, items]);
111
140
 
112
141
  return <Context.Provider value={value}>{props.children}</Context.Provider>;
113
142
  }
@@ -132,6 +161,11 @@ function useFormDispatch(): IDispatch {
132
161
  return context.dispatch;
133
162
  }
134
163
 
164
+ function useFormRootPath(): string | undefined {
165
+ const state = useFormState();
166
+ return state.formRootPath;
167
+ }
168
+
135
169
  function useActiveItem(): string | undefined {
136
170
  const state = useFormState();
137
171
  return state.activeItem;
@@ -192,5 +226,5 @@ function useRemoveItem(): (item: string | IItem) => void {
192
226
  return removeItem;
193
227
  }
194
228
 
195
- export { Provider, useItems, useSyncWithLocation, useActiveItem, useSetActiveItem, useAddItem, useRemoveItem };
229
+ export { Provider, useItems, useSyncWithLocation, useFormRootPath, useActiveItem, useSetActiveItem, useAddItem, useRemoveItem };
196
230
  export type { IProviderProps };
@@ -1,8 +1,9 @@
1
1
  import _ from 'lodash';
2
- import { useMemo } from 'react';
3
- import { useActiveItem, useItems } from './Provider';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { useActiveItem, useFormRootPath, useItems } from './Provider';
4
4
  import { IItem } from './types';
5
5
  import { getLevel, isChild } from './utils';
6
+ import { useNavigate } from 'react-router';
6
7
 
7
8
  function useIsActive(id: string): boolean {
8
9
  const activeItem = useActiveItem();
@@ -22,4 +23,17 @@ function useChildren(id?: string): Array<IItem> {
22
23
  return children;
23
24
  }
24
25
 
25
- export { useChildren, useIsActive };
26
+ function useNavigateForm(): (id: string) => void {
27
+ const formRootPath = useFormRootPath(),
28
+ navigate = useNavigate(),
29
+ navigateSection = useCallback(
30
+ (id: string) => {
31
+ navigate(`${formRootPath}/${id}`);
32
+ },
33
+ [formRootPath, navigate]
34
+ );
35
+
36
+ return navigateSection;
37
+ }
38
+
39
+ export { useChildren, useIsActive, useNavigateForm };
@@ -1,7 +1,16 @@
1
1
  import { Form } from './Form';
2
2
  import { BaseForm } from './BaseForm';
3
3
  import { Content } from './Content';
4
- import { Provider, useActiveItem, useItems, useSyncWithLocation, useSetActiveItem, useAddItem, useRemoveItem } from './Provider';
4
+ import {
5
+ Provider,
6
+ useActiveItem,
7
+ useItems,
8
+ useSyncWithLocation,
9
+ useSetActiveItem,
10
+ useAddItem,
11
+ useRemoveItem,
12
+ useFormRootPath
13
+ } from './Provider';
5
14
  import { Sidebar, SidebarSection, SidebarSectionPosition, useSidebarChildren } from './Sidebar';
6
15
  import { NavMenu, NavMenuItem, useNavMenuItem } from './NavMenu';
7
16
  import { Tab, Group, useBaseItemChildren } from './Tab';
@@ -12,6 +21,7 @@ type IForm = typeof Form & {
12
21
  Provider: typeof Provider;
13
22
  useItems: typeof useItems;
14
23
  useSyncWithLocation: typeof useSyncWithLocation;
24
+ useFormRootPath: typeof useFormRootPath;
15
25
  useActiveItem: typeof useActiveItem;
16
26
  useSetActiveItem: typeof useSetActiveItem;
17
27
  useAddItem: typeof useAddItem;
@@ -34,6 +44,7 @@ DefaultForm.Content = Content;
34
44
  DefaultForm.Provider = Provider;
35
45
  DefaultForm.useItems = useItems;
36
46
  DefaultForm.useSyncWithLocation = useSyncWithLocation;
47
+ DefaultForm.useFormRootPath = useFormRootPath;
37
48
  DefaultForm.useActiveItem = useActiveItem;
38
49
  DefaultForm.useSetActiveItem = useSetActiveItem;
39
50
  DefaultForm.useAddItem = useAddItem;