@proteinjs/ui 2.0.2 → 2.0.4

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.
Files changed (80) hide show
  1. package/.eslintrc.js +20 -0
  2. package/.prettierignore +4 -0
  3. package/.prettierrc +8 -0
  4. package/CHANGELOG.md +16 -14
  5. package/LICENSE +21 -0
  6. package/dist/generated/index.js +1 -1
  7. package/dist/generated/index.js.map +1 -1
  8. package/dist/src/container/AccountIconButton.d.ts +1 -1
  9. package/dist/src/container/AccountIconButton.d.ts.map +1 -1
  10. package/dist/src/container/AccountIconButton.js +13 -6
  11. package/dist/src/container/AccountIconButton.js.map +1 -1
  12. package/dist/src/container/NavMenu.d.ts.map +1 -1
  13. package/dist/src/container/NavMenu.js +9 -9
  14. package/dist/src/container/NavMenu.js.map +1 -1
  15. package/dist/src/container/PageContainer.d.ts +1 -1
  16. package/dist/src/container/PageContainer.d.ts.map +1 -1
  17. package/dist/src/container/PageContainer.js +12 -7
  18. package/dist/src/container/PageContainer.js.map +1 -1
  19. package/dist/src/form/Field.d.ts.map +1 -1
  20. package/dist/src/form/Field.js +4 -2
  21. package/dist/src/form/Field.js.map +1 -1
  22. package/dist/src/form/Form.d.ts.map +1 -1
  23. package/dist/src/form/Form.js +75 -40
  24. package/dist/src/form/Form.js.map +1 -1
  25. package/dist/src/form/FormButton.d.ts.map +1 -1
  26. package/dist/src/form/FormButton.js +1 -1
  27. package/dist/src/form/FormButton.js.map +1 -1
  28. package/dist/src/form/container/FormPage.d.ts.map +1 -1
  29. package/dist/src/form/container/FormPage.js.map +1 -1
  30. package/dist/src/form/container/FormPaper.js +1 -1
  31. package/dist/src/form/container/FormPaper.js.map +1 -1
  32. package/dist/src/form/fields/TextField.d.ts.map +1 -1
  33. package/dist/src/form/fields/TextField.js +8 -6
  34. package/dist/src/form/fields/TextField.js.map +1 -1
  35. package/dist/src/list/NestedList.d.ts.map +1 -1
  36. package/dist/src/list/NestedList.js +10 -10
  37. package/dist/src/list/NestedList.js.map +1 -1
  38. package/dist/src/router/Page.d.ts.map +1 -1
  39. package/dist/src/router/Router.d.ts.map +1 -1
  40. package/dist/src/router/Router.js +11 -7
  41. package/dist/src/router/Router.js.map +1 -1
  42. package/dist/src/router/createUrlParams.d.ts.map +1 -1
  43. package/dist/src/router/createUrlParams.js +2 -1
  44. package/dist/src/router/createUrlParams.js.map +1 -1
  45. package/dist/src/router/withRouter.d.ts.map +1 -1
  46. package/dist/src/router/withRouter.js +1 -1
  47. package/dist/src/router/withRouter.js.map +1 -1
  48. package/dist/src/table/Table.d.ts +1 -1
  49. package/dist/src/table/Table.d.ts.map +1 -1
  50. package/dist/src/table/Table.js +31 -23
  51. package/dist/src/table/Table.js.map +1 -1
  52. package/dist/src/table/TableButton.d.ts.map +1 -1
  53. package/dist/src/table/TableLoader.d.ts.map +1 -1
  54. package/dist/src/table/TableToolbar.d.ts.map +1 -1
  55. package/dist/src/table/TableToolbar.js +10 -7
  56. package/dist/src/table/TableToolbar.js.map +1 -1
  57. package/generated/index.ts +1 -1
  58. package/index.ts +1 -1
  59. package/jest.config.js +8 -17
  60. package/package.json +56 -50
  61. package/src/container/AccountIconButton.tsx +103 -102
  62. package/src/container/NavMenu.tsx +20 -21
  63. package/src/container/PageContainer.tsx +110 -80
  64. package/src/form/Field.tsx +42 -35
  65. package/src/form/Form.tsx +370 -351
  66. package/src/form/FormButton.tsx +24 -22
  67. package/src/form/FunctionalForm.tsx +15 -15
  68. package/src/form/container/FormPage.tsx +16 -18
  69. package/src/form/container/FormPaper.tsx +12 -12
  70. package/src/form/fields/TextField.tsx +63 -61
  71. package/src/list/NestedList.tsx +18 -21
  72. package/src/router/Page.ts +18 -18
  73. package/src/router/Router.tsx +66 -52
  74. package/src/router/createUrlParams.ts +8 -7
  75. package/src/router/withRouter.tsx +6 -11
  76. package/src/table/Table.tsx +96 -80
  77. package/src/table/TableButton.ts +9 -9
  78. package/src/table/TableLoader.ts +6 -6
  79. package/src/table/TableToolbar.tsx +52 -59
  80. package/tsconfig.json +17 -17
@@ -7,139 +7,140 @@ import { Page } from '../router/Page';
7
7
  import LogoutIcon from '@mui/icons-material/Logout';
8
8
 
9
9
  export type AccountIconButtonProps = {
10
- loginClicked: boolean,
11
- setLoginClicked: React.Dispatch<React.SetStateAction<boolean>>,
10
+ loginClicked: boolean;
11
+ setLoginClicked: React.Dispatch<React.SetStateAction<boolean>>;
12
12
  auth?: {
13
- isLoggedIn: boolean,
14
- canViewPage: (page: Page) => boolean,
13
+ isLoggedIn: boolean;
14
+ canViewPage: (page: Page) => boolean;
15
15
  /** Either a dialog component, or a path to be redirected to */
16
- login: LinkOrDialog,
17
- logout: () => Promise<string>
18
- },
19
- profileMenuItems?: { menuItemChildren: React.ReactNode, action: LinkOrDialog | (() => void) }[],
20
- }
16
+ login: LinkOrDialog;
17
+ logout: () => Promise<string>;
18
+ };
19
+ profileMenuItems?: { menuItemChildren: React.ReactNode; action: LinkOrDialog | (() => void) }[];
20
+ };
21
21
 
22
- export const AccountIconButton = ({ loginClicked, setLoginClicked, auth, profileMenuItems }: AccountIconButtonProps) => {
22
+ export const AccountIconButton = ({
23
+ loginClicked,
24
+ setLoginClicked,
25
+ auth,
26
+ profileMenuItems,
27
+ }: AccountIconButtonProps) => {
23
28
  const navigate = useNavigate();
24
29
  const [selectedProfileMenuItem, setSelectedProfileMenuItem] = React.useState<number>(-1);
25
30
  const [selectedIndex, setSelectedIndex] = React.useState<number>(1);
26
- const [anchorEl, setAccountMenuAnchorEl] = React.useState<null|HTMLElement>(null);
31
+ const [anchorEl, setAccountMenuAnchorEl] = React.useState<null | HTMLElement>(null);
27
32
  const open = Boolean(anchorEl);
28
33
 
29
- if (!auth)
30
- return null;
34
+ if (!auth) {
35
+ return null;
36
+ }
31
37
 
32
38
  if (auth.isLoggedIn) {
33
- return (
34
- <div>
35
- <IconButton
36
- sx={{
37
- '&:hover': {
38
- color: '#fff',
39
- backgroundColor: 'transparent',
40
- },
41
- backgroundColor: 'transparent',
42
- }}
43
- onClick={event => setAccountMenuAnchorEl(event.currentTarget)}
44
- >
45
- <AccountCircle />
46
- </IconButton>
47
- <Menu
48
- anchorEl={anchorEl}
49
- keepMounted
50
- open={open}
51
- onClose={() => setAccountMenuAnchorEl(null)}
52
- >
53
- {profileMenuItems &&
54
- profileMenuItems.map((profileMenuItem, index) => (
55
- <MenuItem
56
- key={index}
57
- onClick={event => {
58
- if (typeof profileMenuItem.action === 'string') {
59
- navigate(qualifiedPath(profileMenuItem.action));
60
- return;
61
- }
62
-
63
- else if (isFunction(profileMenuItem.action)) {
64
- setAccountMenuAnchorEl(null);
65
- profileMenuItem.action();
66
- }
67
-
68
- else {
69
- setSelectedIndex(index);
70
- }
71
- }}
72
- selected={selectedIndex === index}
73
- >
74
- {profileMenuItem.menuItemChildren}
75
- </MenuItem>
76
- ))
39
+ return (
40
+ <div>
41
+ <IconButton
42
+ sx={{
43
+ '&:hover': {
44
+ color: '#fff',
45
+ backgroundColor: 'transparent',
46
+ },
47
+ backgroundColor: 'transparent',
48
+ }}
49
+ onClick={(event) => setAccountMenuAnchorEl(event.currentTarget)}
50
+ >
51
+ <AccountCircle />
52
+ </IconButton>
53
+ <Menu anchorEl={anchorEl} keepMounted open={open} onClose={() => setAccountMenuAnchorEl(null)}>
54
+ {profileMenuItems &&
55
+ profileMenuItems.map((profileMenuItem, index) => (
56
+ <MenuItem
57
+ key={index}
58
+ onClick={(event) => {
59
+ if (typeof profileMenuItem.action === 'string') {
60
+ navigate(qualifiedPath(profileMenuItem.action));
61
+ return;
62
+ } else if (isFunction(profileMenuItem.action)) {
63
+ setAccountMenuAnchorEl(null);
64
+ profileMenuItem.action();
65
+ } else {
66
+ setSelectedIndex(index);
77
67
  }
78
- <MenuItem
79
- key='logout'
80
- onClick={async (event) => {
81
- const redirectPath = await auth.logout();
82
- navigate(qualifiedPath(redirectPath));
83
- }}
84
- >
85
-
86
- <ListItemIcon>
87
- <LogoutIcon />
88
- </ListItemIcon>
89
- <Typography>Logout</Typography>
90
- </MenuItem>
91
- </Menu>
92
- <ProfileMenuItemAction />
93
- </div>
94
- );
68
+ }}
69
+ selected={selectedIndex === index}
70
+ >
71
+ {profileMenuItem.menuItemChildren}
72
+ </MenuItem>
73
+ ))}
74
+ <MenuItem
75
+ key='logout'
76
+ onClick={async (event) => {
77
+ const redirectPath = await auth.logout();
78
+ navigate(qualifiedPath(redirectPath));
79
+ }}
80
+ >
81
+ <ListItemIcon>
82
+ <LogoutIcon />
83
+ </ListItemIcon>
84
+ <Typography>Logout</Typography>
85
+ </MenuItem>
86
+ </Menu>
87
+ <ProfileMenuItemAction />
88
+ </div>
89
+ );
95
90
  }
96
91
 
97
92
  return (
98
- <div>
99
- <Button
100
- color='inherit'
101
- onClick={event => auth && typeof auth.login === 'string' ? navigate(qualifiedPath(auth.login)) : setLoginClicked(!loginClicked)}
102
- >
103
- Login
104
- </Button>
105
- <Login />
106
- </div>
93
+ <div>
94
+ <Button
95
+ color='inherit'
96
+ onClick={(event) =>
97
+ auth && typeof auth.login === 'string' ? navigate(qualifiedPath(auth.login)) : setLoginClicked(!loginClicked)
98
+ }
99
+ >
100
+ Login
101
+ </Button>
102
+ <Login />
103
+ </div>
107
104
  );
108
105
 
109
106
  function ProfileMenuItemAction() {
110
- if (selectedProfileMenuItem == -1 || !auth || !profileMenuItems)
111
- return null;
112
-
107
+ if (selectedProfileMenuItem == -1 || !auth || !profileMenuItems) {
108
+ return null;
109
+ }
110
+
113
111
  const menuItem = profileMenuItems[selectedProfileMenuItem];
114
- if (typeof menuItem.action === 'string')
115
- return null;
116
-
112
+ if (typeof menuItem.action === 'string') {
113
+ return null;
114
+ }
115
+
117
116
  if (isDialog(menuItem.action)) {
118
- return <menuItem.action onClose={() => setSelectedProfileMenuItem(-1)} />;
117
+ return <menuItem.action onClose={() => setSelectedProfileMenuItem(-1)} />;
119
118
  }
120
119
 
121
120
  return null;
122
121
  }
123
-
122
+
124
123
  function Login() {
125
- if (!loginClicked || !auth || typeof auth.login === 'string')
126
- return null;
127
-
124
+ if (!loginClicked || !auth || typeof auth.login === 'string') {
125
+ return null;
126
+ }
127
+
128
128
  return <auth.login onClose={() => setLoginClicked(false)} />;
129
129
  }
130
-
130
+
131
131
  function qualifiedPath(path: string) {
132
- if (path.startsWith('/'))
133
- return path;
134
-
132
+ if (path.startsWith('/')) {
133
+ return path;
134
+ }
135
+
135
136
  return `/${path}`;
136
137
  }
137
- }
138
+ };
138
139
 
139
140
  function isDialog(prop: any): prop is React.ComponentType<{ onClose: () => void }> {
140
- return typeof prop === 'function' && 'onClose' in prop.prototype;
141
+ return typeof prop === 'function' && 'onClose' in prop.prototype;
141
142
  }
142
143
 
143
144
  function isFunction(prop: any): prop is () => void {
144
- return typeof prop === 'function';
145
- }
145
+ return typeof prop === 'function';
146
+ }
@@ -1,6 +1,6 @@
1
- import React from 'react'
2
- import { Drawer, Toolbar, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'
3
- import { Navigate } from 'react-router-dom'
1
+ import React from 'react';
2
+ import { Drawer, Toolbar, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
3
+ import { Navigate } from 'react-router-dom';
4
4
 
5
5
  export type LinkOrDialog = string | React.ComponentType<{ onClose: () => void }>;
6
6
 
@@ -9,15 +9,15 @@ export type NavMenuItem = {
9
9
  action: LinkOrDialog;
10
10
  icon?: React.ComponentType;
11
11
  /** Use to make this item a divider */
12
- // isDivider?: boolean,
12
+ // isDivider?: boolean,
13
13
  /** Use to make this item a nested menu */
14
- // children?: NavMenuItems
14
+ // children?: NavMenuItems
15
15
  };
16
16
 
17
17
  export type NavMenuProps = {
18
- navMenuItems?: NavMenuItem[],
19
- navMenuOpen: boolean,
20
- setNavMenuOpen: (open: boolean) => void,
18
+ navMenuItems?: NavMenuItem[];
19
+ navMenuOpen: boolean;
20
+ setNavMenuOpen: (open: boolean) => void;
21
21
  };
22
22
 
23
23
  const drawerWidth = 240;
@@ -31,8 +31,9 @@ export const NavMenu: React.FC<NavMenuProps> = ({ navMenuItems, navMenuOpen, set
31
31
  };
32
32
 
33
33
  const NavMenuItemAction = () => {
34
- if (selectedNavMenuItem == -1 || !navMenuItems)
34
+ if (selectedNavMenuItem == -1 || !navMenuItems) {
35
35
  return null;
36
+ }
36
37
 
37
38
  const menuItem = navMenuItems[selectedNavMenuItem];
38
39
 
@@ -40,16 +41,18 @@ export const NavMenu: React.FC<NavMenuProps> = ({ navMenuItems, navMenuOpen, set
40
41
  setSelectedNavMenuItem(-1);
41
42
  }, [selectedNavMenuItem]);
42
43
 
43
- if (typeof menuItem.action === 'string')
44
+ if (typeof menuItem.action === 'string') {
44
45
  return <Navigate to={qualifiedPath(menuItem.action)} />;
46
+ }
45
47
 
46
48
  return <menuItem.action onClose={() => setSelectedNavMenuItem(-1)} />;
47
49
  };
48
50
 
49
51
  const qualifiedPath = (path: string) => {
50
- if (path.startsWith('/'))
52
+ if (path.startsWith('/')) {
51
53
  return path;
52
-
54
+ }
55
+
53
56
  return `/${path}`;
54
57
  };
55
58
 
@@ -63,22 +66,18 @@ export const NavMenu: React.FC<NavMenuProps> = ({ navMenuItems, navMenuOpen, set
63
66
  variant='temporary'
64
67
  anchor='left'
65
68
  open={navMenuOpen}
66
- onClose={(_, reason) =>
67
- reason === 'backdropClick' && setNavMenuOpen(false)
68
- }
69
+ onClose={(_, reason) => reason === 'backdropClick' && setNavMenuOpen(false)}
69
70
  >
70
71
  <Toolbar />
71
72
  <List>
72
73
  {navMenuItems?.map((navMenuItem, index) => (
73
74
  <ListItem key={index}>
74
- <ListItemButton
75
- onClick={() => handleNavMenuItemClick(index)}
76
- >
77
- {navMenuItem.icon &&
75
+ <ListItemButton onClick={() => handleNavMenuItemClick(index)}>
76
+ {navMenuItem.icon && (
78
77
  <ListItemIcon>
79
78
  <navMenuItem.icon />
80
79
  </ListItemIcon>
81
- }
80
+ )}
82
81
  <ListItemText primary={navMenuItem.name} />
83
82
  </ListItemButton>
84
83
  </ListItem>
@@ -88,4 +87,4 @@ export const NavMenu: React.FC<NavMenuProps> = ({ navMenuItems, navMenuOpen, set
88
87
  <NavMenuItemAction />
89
88
  </div>
90
89
  );
91
- }
90
+ };
@@ -8,100 +8,130 @@ import { LinkOrDialog, NavMenu, NavMenuItem } from './NavMenu';
8
8
  import { AccountIconButton } from './AccountIconButton';
9
9
 
10
10
  function qualifiedPath(path: string) {
11
- if (path.startsWith('/'))
12
- return path;
11
+ if (path.startsWith('/')) {
12
+ return path;
13
+ }
13
14
 
14
- return `/${path}`;
15
+ return `/${path}`;
15
16
  }
16
17
 
17
18
  export type PageContainerProps = {
18
- page: Page,
19
- auth?: {
20
- isLoggedIn: boolean,
21
- canViewPage: (page: Page) => boolean,
22
- login: LinkOrDialog,
23
- logout: () => Promise<string>
24
- },
25
- appName?: string,
26
- toolbarChildren?: React.ReactNode,
27
- /** An array of menu items, each containing a React node and an action triggered when selected, either a string, dialog component, or a function. */
28
- profileMenuItems?: { menuItemChildren: React.ReactNode, action: LinkOrDialog | (() => void) }[],
29
- navMenuItems?: NavMenuItem[],
30
- appBarProps?: AppBarProps,
31
- toolbarProps?: ToolbarProps,
32
- }
19
+ page: Page;
20
+ auth?: {
21
+ isLoggedIn: boolean;
22
+ canViewPage: (page: Page) => boolean;
23
+ login: LinkOrDialog;
24
+ logout: () => Promise<string>;
25
+ };
26
+ appName?: string;
27
+ toolbarChildren?: React.ReactNode;
28
+ /** An array of menu items, each containing a React node and an action triggered when selected, either a string, dialog component, or a function. */
29
+ profileMenuItems?: { menuItemChildren: React.ReactNode; action: LinkOrDialog | (() => void) }[];
30
+ navMenuItems?: NavMenuItem[];
31
+ appBarProps?: AppBarProps;
32
+ toolbarProps?: ToolbarProps;
33
+ };
33
34
 
34
- const Page = React.memo(({ auth, page, navigate, loginClicked, setLoginClicked }: { auth: PageContainerProps['auth'], page: PageContainerProps['page'], navigate: NavigateFunction, loginClicked: boolean, setLoginClicked: (loginClicked: boolean) => void }) => {
35
- if (auth?.canViewPage(page))
36
- return <page.component urlParams={createUrlParams()} navigate={navigate} />;
35
+ const Page = React.memo(
36
+ ({
37
+ auth,
38
+ page,
39
+ navigate,
40
+ loginClicked,
41
+ setLoginClicked,
42
+ }: {
43
+ auth: PageContainerProps['auth'];
44
+ page: PageContainerProps['page'];
45
+ navigate: NavigateFunction;
46
+ loginClicked: boolean;
47
+ setLoginClicked: (loginClicked: boolean) => void;
48
+ }) => {
49
+ if (auth?.canViewPage(page)) {
50
+ return <page.component urlParams={createUrlParams()} navigate={navigate} />;
51
+ }
37
52
 
38
53
  if (!auth?.isLoggedIn) {
39
- if (!loginClicked)
40
- setLoginClicked(true);
41
-
42
- return null;
54
+ if (!loginClicked) {
55
+ setLoginClicked(true);
56
+ }
57
+
58
+ return null;
43
59
  }
44
60
 
45
61
  navigate('/');
46
62
  return null;
47
- });
63
+ }
64
+ );
48
65
 
49
66
  export function PageContainer(props: PageContainerProps) {
50
- const navigate = useNavigate();
51
- const { page, auth, navMenuItems, appName, toolbarChildren, profileMenuItems, appBarProps, toolbarProps } = props;
52
- const [loginClicked, setLoginClicked] = React.useState(false);
53
- const [navMenuOpen, setNavMenuOpen] = React.useState(false);
54
-
55
- React.useEffect(() => {
56
- if (auth?.canViewPage(page))
57
- return;
67
+ const navigate = useNavigate();
68
+ const { page, auth, navMenuItems, appName, toolbarChildren, profileMenuItems, appBarProps, toolbarProps } = props;
69
+ const [loginClicked, setLoginClicked] = React.useState(false);
70
+ const [navMenuOpen, setNavMenuOpen] = React.useState(false);
58
71
 
59
- if (!auth?.isLoggedIn) {
60
- console.log(`User not logged in, redirecting to login`);
61
- if (typeof auth?.login === 'string') {
62
- const p = qualifiedPath(auth.login);
63
- navigate(p);
64
- }
65
- }
66
- }, [page]);
72
+ React.useEffect(() => {
73
+ if (auth?.canViewPage(page)) {
74
+ return;
75
+ }
67
76
 
68
- return (
69
- <Box sx={(theme) => {
70
- const defaultStyles = {};
71
- if (!page.pageContainerSxProps)
72
- return defaultStyles;
77
+ if (!auth?.isLoggedIn) {
78
+ console.log(`User not logged in, redirecting to login`);
79
+ if (typeof auth?.login === 'string') {
80
+ const p = qualifiedPath(auth.login);
81
+ navigate(p);
82
+ }
83
+ }
84
+ }, [page]);
73
85
 
74
- let resolvedStyles = Object.assign({}, defaultStyles, page.pageContainerSxProps(theme));
86
+ return (
87
+ <Box
88
+ sx={(theme) => {
89
+ const defaultStyles = {};
90
+ if (!page.pageContainerSxProps) {
91
+ return defaultStyles;
92
+ }
75
93
 
76
- return resolvedStyles;
77
- }}>
78
- <AppBar position='static' {...appBarProps}>
79
- <Toolbar {...toolbarProps}>
80
- {navMenuItems && (
81
- <IconButton
82
- aria-label='menu'
83
- onClick={() => setNavMenuOpen(!navMenuOpen)}
84
- sx={(theme) => ({
85
- marginRight: theme.spacing(2),
86
- '&:hover': {
87
- color: '#fff',
88
- },
89
- })}
90
- style={{ backgroundColor: 'transparent' }}
91
- >
92
- <MenuIcon />
93
- </IconButton>
94
- )}
95
- {appName && <Typography variant='h5' sx={{ flexGrow: 1, color: 'common.white' }}>
96
- {appName}
97
- </Typography>}
98
- {toolbarChildren}
99
- <div style={{ flexGrow: 1 }}></div>
100
- <AccountIconButton loginClicked={loginClicked} setLoginClicked={setLoginClicked} auth={auth} {...(profileMenuItems ? { profileMenuItems } : {})} />
101
- </Toolbar>
102
- </AppBar>
103
- {navMenuItems && <NavMenu navMenuItems={navMenuItems} navMenuOpen={navMenuOpen} setNavMenuOpen={setNavMenuOpen} />}
104
- <Page auth={auth} page={page} navigate={navigate} loginClicked={loginClicked} setLoginClicked={setLoginClicked} />
105
- </Box>
106
- );
107
- }
94
+ const resolvedStyles = Object.assign({}, defaultStyles, page.pageContainerSxProps(theme));
95
+
96
+ return resolvedStyles;
97
+ }}
98
+ >
99
+ <AppBar position='static' {...appBarProps}>
100
+ <Toolbar {...toolbarProps}>
101
+ {navMenuItems && (
102
+ <IconButton
103
+ aria-label='menu'
104
+ onClick={() => setNavMenuOpen(!navMenuOpen)}
105
+ sx={(theme) => ({
106
+ marginRight: theme.spacing(2),
107
+ '&:hover': {
108
+ color: '#fff',
109
+ },
110
+ })}
111
+ style={{ backgroundColor: 'transparent' }}
112
+ >
113
+ <MenuIcon />
114
+ </IconButton>
115
+ )}
116
+ {appName && (
117
+ <Typography variant='h5' sx={{ flexGrow: 1, color: 'common.white' }}>
118
+ {appName}
119
+ </Typography>
120
+ )}
121
+ {toolbarChildren}
122
+ <div style={{ flexGrow: 1 }}></div>
123
+ <AccountIconButton
124
+ loginClicked={loginClicked}
125
+ setLoginClicked={setLoginClicked}
126
+ auth={auth}
127
+ {...(profileMenuItems ? { profileMenuItems } : {})}
128
+ />
129
+ </Toolbar>
130
+ </AppBar>
131
+ {navMenuItems && (
132
+ <NavMenu navMenuItems={navMenuItems} navMenuOpen={navMenuOpen} setNavMenuOpen={setNavMenuOpen} />
133
+ )}
134
+ <Page auth={auth} page={page} navigate={navigate} loginClicked={loginClicked} setLoginClicked={setLoginClicked} />
135
+ </Box>
136
+ );
137
+ }
@@ -2,50 +2,57 @@ import React from 'react';
2
2
  import S from 'string';
3
3
 
4
4
  export type Field<T, F extends Fields> = {
5
- name: string,
6
- label?: string,
7
- description?: string,
8
- accessibility?: {
9
- required?: boolean,
10
- hidden?: boolean,
11
- readonly?: boolean
12
- },
13
- layout?: {
14
- row: number,
15
- column?: number,
16
- width: 1|2|3|4|5|6|7|8|9|10|11|12
17
- },
18
- value?: T,
19
- displayValue?: (value: T) => string,
20
- onLoad?: (fields: F) => Promise<void>,
21
- onChange?: (value: T, fields: F, setFieldStatus: (message: string, isError: boolean) => void) => Promise<void>
22
- }
5
+ name: string;
6
+ label?: string;
7
+ description?: string;
8
+ accessibility?: {
9
+ required?: boolean;
10
+ hidden?: boolean;
11
+ readonly?: boolean;
12
+ };
13
+ layout?: {
14
+ row: number;
15
+ column?: number;
16
+ width: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
17
+ };
18
+ value?: T;
19
+ displayValue?: (value: T) => string;
20
+ onLoad?: (fields: F) => Promise<void>;
21
+ onChange?: (value: T, fields: F, setFieldStatus: (message: string, isError: boolean) => void) => Promise<void>;
22
+ };
23
23
 
24
24
  export type FieldComponentProps<T, F extends Fields> = {
25
+ field: Field<T, F>;
26
+ onChange: (
25
27
  field: Field<T, F>,
26
- onChange: (field: Field<T, F>, value: T, setFieldStatus: (message: string, isError: boolean) => void) => Promise<void>,
27
- // focused: boolean
28
- }
28
+ value: T,
29
+ setFieldStatus: (message: string, isError: boolean) => void
30
+ ) => Promise<void>;
31
+ // focused: boolean
32
+ };
29
33
 
30
34
  export type FieldComponent<T, F extends Fields> = {
31
- field: Field<T, F>,
32
- component: (props: FieldComponentProps<T, F>) => JSX.Element,
33
- }
35
+ field: Field<T, F>;
36
+ component: (props: FieldComponentProps<T, F>) => JSX.Element;
37
+ };
34
38
 
35
- export abstract class Fields { [name: string]: FieldComponent<any, any> }
39
+ export abstract class Fields {
40
+ [name: string]: FieldComponent<any, any>;
41
+ }
36
42
 
37
43
  export function fieldLabel(field: Field<any, any>) {
38
- return field.label ? field.label : S(field.name).humanize().capitalize().s;
44
+ return field.label ? field.label : S(field.name).humanize().capitalize().s;
39
45
  }
40
46
 
41
47
  export function fieldDisplayValue(field: Field<any, any>) {
42
- let value = '';
43
- if (field.value) {
44
- if (field.displayValue)
45
- value = field.displayValue(field.value);
46
- else
47
- value = field.value;
48
- }
48
+ let value = '';
49
+ if (field.value) {
50
+ if (field.displayValue) {
51
+ value = field.displayValue(field.value);
52
+ } else {
53
+ value = field.value;
54
+ }
55
+ }
49
56
 
50
- return value;
51
- }
57
+ return value;
58
+ }