@firecms/core 3.0.0-canary.61 → 3.0.0-canary.63
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/dist/app/AppBar.d.ts +12 -0
- package/dist/app/Drawer.d.ts +17 -0
- package/dist/app/Scaffold.d.ts +30 -0
- package/dist/app/index.d.ts +4 -0
- package/dist/app/useApp.d.ts +16 -0
- package/dist/components/index.d.ts +1 -1
- package/dist/{components/FireCMSAppBar.d.ts → core/DefaultAppBar.d.ts} +2 -7
- package/dist/core/DefaultDrawer.d.ts +19 -0
- package/dist/core/NavigationRoutes.d.ts +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +2054 -2037
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/package.json +4 -4
- package/src/app/AppBar.tsx +18 -0
- package/src/app/Drawer.tsx +25 -0
- package/src/{core → app}/Scaffold.tsx +61 -107
- package/src/app/index.ts +4 -0
- package/src/app/useApp.tsx +32 -0
- package/src/components/index.tsx +1 -1
- package/src/{components/FireCMSAppBar.tsx → core/DefaultAppBar.tsx} +17 -25
- package/src/core/DefaultDrawer.tsx +177 -0
- package/src/core/NavigationRoutes.tsx +3 -3
- package/src/core/index.tsx +2 -2
- package/src/index.ts +1 -0
- package/dist/core/Drawer.d.ts +0 -16
- package/dist/core/Scaffold.d.ts +0 -51
- package/src/core/Drawer.tsx +0 -139
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-canary.
|
|
4
|
+
"version": "3.0.0-canary.63",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"./package.json": "./package.json"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@firecms/formex": "^3.0.0-canary.
|
|
50
|
-
"@firecms/ui": "^3.0.0-canary.
|
|
49
|
+
"@firecms/formex": "^3.0.0-canary.63",
|
|
50
|
+
"@firecms/ui": "^3.0.0-canary.63",
|
|
51
51
|
"@fontsource/jetbrains-mono": "^5.0.20",
|
|
52
52
|
"@hello-pangea/dnd": "^16.6.0",
|
|
53
53
|
"@radix-ui/react-portal": "^1.0.4",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"dist",
|
|
112
112
|
"src"
|
|
113
113
|
],
|
|
114
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "ebf432414a10a3e316be86d4b6ad4aa7be2786ad",
|
|
115
115
|
"publishConfig": {
|
|
116
116
|
"access": "public"
|
|
117
117
|
},
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DefaultAppBar, DefaultAppBarProps } from "../core/DefaultAppBar";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This component renders the main app bar of FireCMS.
|
|
5
|
+
*/
|
|
6
|
+
export function AppBar({
|
|
7
|
+
children,
|
|
8
|
+
...props
|
|
9
|
+
}: {
|
|
10
|
+
children?: React.ReactNode,
|
|
11
|
+
className?: string,
|
|
12
|
+
style?: React.CSSProperties
|
|
13
|
+
} & DefaultAppBarProps) {
|
|
14
|
+
const usedChildren = children ?? <DefaultAppBar {...props}/>;
|
|
15
|
+
return <>{usedChildren}</>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
AppBar.componentType = "AppBar";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DefaultDrawer } from "../core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This component is in charge of rendering the drawer.
|
|
5
|
+
* If you add this component under your {@link Scaffold}, it will be rendered
|
|
6
|
+
* as a drawer, and the open and close functionality will be handled automatically.
|
|
7
|
+
* If you want to customise the drawer, you can create your own component and pass it as a child.
|
|
8
|
+
* For custom drawers, you can use the {@link useApp} to open and close the drawer.
|
|
9
|
+
*
|
|
10
|
+
* @constructor
|
|
11
|
+
*/
|
|
12
|
+
export function Drawer({
|
|
13
|
+
children,
|
|
14
|
+
className,
|
|
15
|
+
style
|
|
16
|
+
}: {
|
|
17
|
+
children?: React.ReactNode,
|
|
18
|
+
className?: string,
|
|
19
|
+
style?: React.CSSProperties
|
|
20
|
+
}) {
|
|
21
|
+
const usedChildren = children ?? <DefaultDrawer className={className} style={style}/>;
|
|
22
|
+
return <>{usedChildren}</>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Drawer.componentType = "Drawer";
|
|
@@ -1,55 +1,16 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useCallback } from "react";
|
|
2
|
-
import equal from "react-fast-compare"
|
|
3
|
-
import { Link } from "react-router-dom";
|
|
4
|
-
|
|
5
|
-
import { Drawer as DefaultDrawer, DrawerProps } from "./Drawer";
|
|
6
|
-
import { useLargeLayout, useNavigationController } from "../hooks";
|
|
7
|
-
import { ErrorBoundary, FireCMSAppBar as DefaultFireCMSAppBar, FireCMSAppBarProps, FireCMSLogo } from "../components";
|
|
8
2
|
import { ChevronLeftIcon, cls, defaultBorderMixin, IconButton, MenuIcon, Sheet, Tooltip } from "@firecms/ui";
|
|
3
|
+
import equal from "react-fast-compare"
|
|
4
|
+
import { useLargeLayout } from "../hooks";
|
|
5
|
+
import { ErrorBoundary } from "../components";
|
|
6
|
+
import { AppContext } from "./useApp";
|
|
9
7
|
|
|
10
8
|
export const DRAWER_WIDTH = 280;
|
|
11
9
|
|
|
12
|
-
const DrawerContext = React.createContext<DrawerProps>({
|
|
13
|
-
hovered: false,
|
|
14
|
-
drawerOpen: false,
|
|
15
|
-
openDrawer: () => {
|
|
16
|
-
throw new Error("openDrawer not implemented");
|
|
17
|
-
},
|
|
18
|
-
closeDrawer: () => {
|
|
19
|
-
throw new Error("closeDrawer not implemented");
|
|
20
|
-
},
|
|
21
|
-
autoOpenDrawer: false
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
export function useDrawer() {
|
|
25
|
-
return React.useContext(DrawerContext);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
10
|
/**
|
|
29
11
|
* @group Core
|
|
30
12
|
*/
|
|
31
|
-
export interface ScaffoldProps
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Name of the app, displayed as the main title and in the tab title
|
|
35
|
-
*/
|
|
36
|
-
name?: React.ReactNode;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Logo to be displayed in the drawer of the CMS
|
|
40
|
-
*/
|
|
41
|
-
logo?: string;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Whether to include the drawer in the scaffold
|
|
45
|
-
*/
|
|
46
|
-
includeDrawer?: boolean;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* You can define a custom drawer to be displayed in the scaffold.
|
|
50
|
-
* Use the hook `useDrawer` to access the context values.
|
|
51
|
-
*/
|
|
52
|
-
drawer?: React.ReactNode;
|
|
13
|
+
export interface ScaffoldProps {
|
|
53
14
|
|
|
54
15
|
/**
|
|
55
16
|
* Open the drawer on hover
|
|
@@ -57,15 +18,14 @@ export interface ScaffoldProps<ExtraAppbarProps = object> {
|
|
|
57
18
|
autoOpenDrawer?: boolean;
|
|
58
19
|
|
|
59
20
|
/**
|
|
60
|
-
*
|
|
21
|
+
* Logo to be displayed in the top bar and drawer.
|
|
22
|
+
* Note that this has no effect if you are using a custom AppBar or Drawer.
|
|
61
23
|
*/
|
|
62
|
-
|
|
24
|
+
logo?: string;
|
|
63
25
|
|
|
64
|
-
|
|
65
|
-
* Additional props passed to the custom AppBar
|
|
66
|
-
*/
|
|
67
|
-
fireCMSAppBarProps?: Partial<FireCMSAppBarProps> & ExtraAppbarProps;
|
|
26
|
+
className?: string;
|
|
68
27
|
|
|
28
|
+
style?: React.CSSProperties;
|
|
69
29
|
}
|
|
70
30
|
|
|
71
31
|
/**
|
|
@@ -84,15 +44,23 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
|
|
|
84
44
|
|
|
85
45
|
const {
|
|
86
46
|
children,
|
|
87
|
-
name,
|
|
88
|
-
logo,
|
|
89
|
-
includeDrawer = true,
|
|
90
47
|
autoOpenDrawer,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
48
|
+
logo,
|
|
49
|
+
className,
|
|
50
|
+
style
|
|
94
51
|
} = props;
|
|
95
52
|
|
|
53
|
+
const drawerChildren = React.Children.toArray(children).filter((child: any) => child.type.componentType === "Drawer");
|
|
54
|
+
if (drawerChildren.length > 1) {
|
|
55
|
+
throw Error("Only one Drawer component is allowed in Scaffold");
|
|
56
|
+
}
|
|
57
|
+
const appBarChildren = React.Children.toArray(children).filter((child: any) => child.type.componentType === "AppBar");
|
|
58
|
+
if (appBarChildren.length > 1) {
|
|
59
|
+
throw Error("Only one AppBar component is allowed in Scaffold");
|
|
60
|
+
}
|
|
61
|
+
const otherChildren = React.Children.toArray(children)
|
|
62
|
+
.filter((child: any) => child.type.componentType !== "Drawer" && child.type.componentType !== "AppBar");
|
|
63
|
+
const includeDrawer = drawerChildren.length > 0;
|
|
96
64
|
const largeLayout = useLargeLayout();
|
|
97
65
|
|
|
98
66
|
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
|
@@ -112,38 +80,37 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
|
|
|
112
80
|
const computedDrawerOpen: boolean = drawerOpen || Boolean(largeLayout && autoOpenDrawer && onHover);
|
|
113
81
|
|
|
114
82
|
return (
|
|
115
|
-
<
|
|
116
|
-
|
|
83
|
+
<AppContext.Provider value={{
|
|
84
|
+
logo,
|
|
85
|
+
hasDrawer: includeDrawer,
|
|
86
|
+
drawerHovered: onHover,
|
|
117
87
|
drawerOpen: computedDrawerOpen,
|
|
118
88
|
closeDrawer: handleDrawerClose,
|
|
119
89
|
openDrawer: handleDrawerOpen,
|
|
120
90
|
autoOpenDrawer
|
|
121
91
|
}}>
|
|
122
92
|
<div
|
|
123
|
-
className="flex h-screen w-screen bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white overflow-hidden"
|
|
93
|
+
className={cls("flex h-screen w-screen bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white overflow-hidden", className)}
|
|
124
94
|
style={{
|
|
125
95
|
paddingTop: "env(safe-area-inset-top)",
|
|
126
96
|
paddingLeft: "env(safe-area-inset-left)",
|
|
127
97
|
paddingRight: "env(safe-area-inset-right)",
|
|
128
98
|
paddingBottom: "env(safe-area-inset-bottom)",
|
|
129
|
-
height: "100dvh"
|
|
99
|
+
height: "100dvh",
|
|
100
|
+
...style
|
|
130
101
|
}}>
|
|
131
102
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
logo={logo}
|
|
135
|
-
drawerOpen={computedDrawerOpen}
|
|
136
|
-
{...fireCMSAppBarProps}/>
|
|
103
|
+
{appBarChildren}
|
|
104
|
+
|
|
137
105
|
<DrawerWrapper
|
|
138
106
|
displayed={includeDrawer}
|
|
139
107
|
onMouseEnter={setOnHoverTrue}
|
|
140
108
|
onMouseMove={setOnHoverTrue}
|
|
141
109
|
onMouseLeave={setOnHoverFalse}
|
|
142
110
|
open={computedDrawerOpen}
|
|
143
|
-
logo={logo}
|
|
144
111
|
hovered={onHover}
|
|
145
112
|
setDrawerOpen={setDrawerOpen}>
|
|
146
|
-
{includeDrawer &&
|
|
113
|
+
{includeDrawer && drawerChildren}
|
|
147
114
|
</DrawerWrapper>
|
|
148
115
|
|
|
149
116
|
<main
|
|
@@ -153,13 +120,13 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
|
|
|
153
120
|
className={cls(defaultBorderMixin, "flex-grow overflow-auto lg:m-0 lg:mx-4 lg:mb-4 lg:rounded-lg lg:border lg:border-solid m-0 mt-1")}>
|
|
154
121
|
|
|
155
122
|
<ErrorBoundary>
|
|
156
|
-
{
|
|
123
|
+
{otherChildren}
|
|
157
124
|
</ErrorBoundary>
|
|
158
125
|
|
|
159
126
|
</div>
|
|
160
127
|
</main>
|
|
161
128
|
</div>
|
|
162
|
-
</
|
|
129
|
+
</AppContext.Provider>
|
|
163
130
|
);
|
|
164
131
|
},
|
|
165
132
|
equal
|
|
@@ -183,8 +150,6 @@ function DrawerWrapper(props: {
|
|
|
183
150
|
onMouseLeave: () => void
|
|
184
151
|
}) {
|
|
185
152
|
|
|
186
|
-
const navigation = useNavigationController();
|
|
187
|
-
|
|
188
153
|
const width = !props.displayed ? 0 : (props.open ? DRAWER_WIDTH : 72);
|
|
189
154
|
const innerDrawer = <div
|
|
190
155
|
className={"relative h-full no-scrollbar overflow-y-auto overflow-x-hidden"}
|
|
@@ -198,7 +163,7 @@ function DrawerWrapper(props: {
|
|
|
198
163
|
<Tooltip title="Open menu"
|
|
199
164
|
side="right"
|
|
200
165
|
sideOffset={12}
|
|
201
|
-
className="fixed top-2 left-3 !bg-gray-50 dark:!bg-gray-900 rounded-full w-fit"
|
|
166
|
+
className="fixed top-2 left-3 !bg-gray-50 dark:!bg-gray-900 rounded-full w-fit z-20"
|
|
202
167
|
>
|
|
203
168
|
<IconButton
|
|
204
169
|
color="inherit"
|
|
@@ -212,30 +177,19 @@ function DrawerWrapper(props: {
|
|
|
212
177
|
</Tooltip>
|
|
213
178
|
)}
|
|
214
179
|
|
|
215
|
-
<div
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
<Link
|
|
227
|
-
to={navigation.basePath}>
|
|
228
|
-
{props.logo
|
|
229
|
-
? <img src={props.logo}
|
|
230
|
-
alt="Logo"
|
|
231
|
-
className={cls("max-w-full max-h-full",
|
|
232
|
-
props.open ?? "w-[112px] h-[112px]")}/>
|
|
233
|
-
: <FireCMSLogo/>}
|
|
234
|
-
|
|
235
|
-
</Link>
|
|
236
|
-
</Tooltip>
|
|
237
|
-
</div>
|
|
180
|
+
<div
|
|
181
|
+
className={`z-20 absolute right-0 top-4 ${
|
|
182
|
+
props.open ? "opacity-100" : "opacity-0 invisible"
|
|
183
|
+
} transition-opacity duration-200 ease-in-out`}>
|
|
184
|
+
<IconButton
|
|
185
|
+
aria-label="Close drawer"
|
|
186
|
+
onClick={() => props.setDrawerOpen(false)}
|
|
187
|
+
>
|
|
188
|
+
<ChevronLeftIcon/>
|
|
189
|
+
</IconButton>
|
|
190
|
+
</div>
|
|
238
191
|
|
|
192
|
+
<div className={"flex flex-col h-full"}>
|
|
239
193
|
{props.children}
|
|
240
194
|
</div>
|
|
241
195
|
|
|
@@ -267,7 +221,7 @@ function DrawerWrapper(props: {
|
|
|
267
221
|
|
|
268
222
|
return (
|
|
269
223
|
<div
|
|
270
|
-
className="relative"
|
|
224
|
+
className="z-20 relative"
|
|
271
225
|
onMouseEnter={props.onMouseEnter}
|
|
272
226
|
onMouseMove={props.onMouseMove}
|
|
273
227
|
onMouseLeave={props.onMouseLeave}
|
|
@@ -278,17 +232,17 @@ function DrawerWrapper(props: {
|
|
|
278
232
|
|
|
279
233
|
{innerDrawer}
|
|
280
234
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
235
|
+
{/*<div*/}
|
|
236
|
+
{/* className={`z-20 absolute right-0 top-4 ${*/}
|
|
237
|
+
{/* props.open ? "opacity-100" : "opacity-0 invisible"*/}
|
|
238
|
+
{/* } transition-opacity duration-1000 ease-in-out`}>*/}
|
|
239
|
+
{/* <IconButton*/}
|
|
240
|
+
{/* aria-label="Close drawer"*/}
|
|
241
|
+
{/* onClick={() => props.setDrawerOpen(false)}*/}
|
|
242
|
+
{/* >*/}
|
|
243
|
+
{/* <ChevronLeftIcon/>*/}
|
|
244
|
+
{/* </IconButton>*/}
|
|
245
|
+
{/*</div>*/}
|
|
292
246
|
|
|
293
247
|
</div>
|
|
294
248
|
);
|
package/src/app/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This context represents the state of the app in terms of layout.
|
|
5
|
+
* @group Core
|
|
6
|
+
*/
|
|
7
|
+
export type AppState = {
|
|
8
|
+
hasDrawer: boolean,
|
|
9
|
+
drawerHovered: boolean,
|
|
10
|
+
drawerOpen: boolean,
|
|
11
|
+
openDrawer: () => void,
|
|
12
|
+
closeDrawer: () => void,
|
|
13
|
+
autoOpenDrawer?: boolean,
|
|
14
|
+
logo?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const AppContext = React.createContext<AppState>({
|
|
18
|
+
hasDrawer: false,
|
|
19
|
+
drawerHovered: false,
|
|
20
|
+
drawerOpen: false,
|
|
21
|
+
openDrawer: () => {
|
|
22
|
+
throw new Error("openDrawer not implemented");
|
|
23
|
+
},
|
|
24
|
+
closeDrawer: () => {
|
|
25
|
+
throw new Error("closeDrawer not implemented");
|
|
26
|
+
},
|
|
27
|
+
autoOpenDrawer: false
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export function useApp() {
|
|
31
|
+
return React.useContext(AppContext);
|
|
32
|
+
}
|
package/src/components/index.tsx
CHANGED
|
@@ -16,8 +16,9 @@ import {
|
|
|
16
16
|
} from "@firecms/ui";
|
|
17
17
|
import { useAuthController, useLargeLayout, useModeController, useNavigationController } from "../hooks";
|
|
18
18
|
import { User } from "../types";
|
|
19
|
+
import { useApp } from "../app/useApp";
|
|
19
20
|
|
|
20
|
-
export type
|
|
21
|
+
export type DefaultAppBarProps<ADDITIONAL_PROPS = object> = {
|
|
21
22
|
|
|
22
23
|
title?: React.ReactNode;
|
|
23
24
|
/**
|
|
@@ -29,12 +30,8 @@ export type FireCMSAppBarProps<ADDITIONAL_PROPS = object> = {
|
|
|
29
30
|
|
|
30
31
|
dropDownActions?: React.ReactNode;
|
|
31
32
|
|
|
32
|
-
includeDrawer?: boolean;
|
|
33
|
-
|
|
34
33
|
includeModeToggle?: boolean;
|
|
35
34
|
|
|
36
|
-
drawerOpen: boolean;
|
|
37
|
-
|
|
38
35
|
className?: string;
|
|
39
36
|
|
|
40
37
|
style?: React.CSSProperties;
|
|
@@ -48,24 +45,24 @@ export type FireCMSAppBarProps<ADDITIONAL_PROPS = object> = {
|
|
|
48
45
|
* This component renders the main app bar of FireCMS.
|
|
49
46
|
* You will likely not need to use this component directly.
|
|
50
47
|
*
|
|
51
|
-
* @param title
|
|
52
|
-
* @param toolbarExtraWidget
|
|
53
|
-
* @param drawerOpen
|
|
54
48
|
* @constructor
|
|
55
49
|
*/
|
|
56
|
-
export const
|
|
50
|
+
export const DefaultAppBar = function DefaultAppBar({
|
|
57
51
|
title,
|
|
58
52
|
endAdornment,
|
|
59
53
|
startAdornment,
|
|
60
|
-
drawerOpen,
|
|
61
54
|
dropDownActions,
|
|
62
|
-
includeDrawer,
|
|
63
55
|
includeModeToggle = true,
|
|
64
56
|
className,
|
|
65
57
|
style,
|
|
66
|
-
logo,
|
|
67
58
|
user: userProp
|
|
68
|
-
}:
|
|
59
|
+
}: DefaultAppBarProps) {
|
|
60
|
+
|
|
61
|
+
const {
|
|
62
|
+
hasDrawer,
|
|
63
|
+
drawerOpen,
|
|
64
|
+
logo
|
|
65
|
+
} = useApp();
|
|
69
66
|
const navigation = useNavigationController();
|
|
70
67
|
|
|
71
68
|
const authController = useAuthController();
|
|
@@ -96,20 +93,15 @@ export const FireCMSAppBar = function FireCMSAppBar({
|
|
|
96
93
|
return (
|
|
97
94
|
<div
|
|
98
95
|
style={style}
|
|
99
|
-
className={cls("pr-2",
|
|
96
|
+
className={cls("pr-2 w-full h-16 transition-all ease-in duration-75 fixed",
|
|
100
97
|
{
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"h-16": true,
|
|
98
|
+
"pl-[17rem]": drawerOpen && largeLayout,
|
|
99
|
+
"pl-20": hasDrawer && !(drawerOpen && largeLayout),
|
|
104
100
|
"z-10": largeLayout,
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"w-full": !includeDrawer,
|
|
109
|
-
"w-[calc(100%-64px)]": includeDrawer && !(drawerOpen && largeLayout),
|
|
110
|
-
"w-[calc(100%-17rem)]": includeDrawer && (drawerOpen && largeLayout),
|
|
101
|
+
// "w-full": !hasDrawer,
|
|
102
|
+
// "w-[calc(100%-64px)]": hasDrawer && !(drawerOpen && largeLayout),
|
|
103
|
+
// "w-[calc(100%-17rem)]": hasDrawer && (drawerOpen && largeLayout),
|
|
111
104
|
"duration-150": drawerOpen && largeLayout,
|
|
112
|
-
fixed: true
|
|
113
105
|
},
|
|
114
106
|
className)}>
|
|
115
107
|
|
|
@@ -121,7 +113,7 @@ export const FireCMSAppBar = function FireCMSAppBar({
|
|
|
121
113
|
to={navigation?.basePath ?? "/"}
|
|
122
114
|
>
|
|
123
115
|
<div className={"flex flex-row gap-4"}>
|
|
124
|
-
{!
|
|
116
|
+
{!hasDrawer && (logo
|
|
125
117
|
? <img src={logo}
|
|
126
118
|
alt="Logo"
|
|
127
119
|
className={cls("w-[32px] h-[32px]")}/>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
import { useLargeLayout, useNavigationController } from "../hooks";
|
|
4
|
+
|
|
5
|
+
import { Link, useNavigate } from "react-router-dom";
|
|
6
|
+
import { CMSAnalyticsEvent, TopNavigationEntry, TopNavigationResult } from "../types";
|
|
7
|
+
import { IconForView } from "../util";
|
|
8
|
+
import { cls, IconButton, Menu, MenuItem, MoreVertIcon, Tooltip, Typography } from "@firecms/ui";
|
|
9
|
+
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
10
|
+
import { DrawerNavigationItem } from "./DrawerNavigationItem";
|
|
11
|
+
import { FireCMSLogo } from "../components";
|
|
12
|
+
import { useApp } from "../app/useApp";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Default drawer used in the CMS
|
|
16
|
+
* @group Core
|
|
17
|
+
*/
|
|
18
|
+
export function DefaultDrawer({
|
|
19
|
+
className,
|
|
20
|
+
style,
|
|
21
|
+
}: {
|
|
22
|
+
className?: string
|
|
23
|
+
style?: React.CSSProperties,
|
|
24
|
+
}) {
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
drawerHovered,
|
|
28
|
+
drawerOpen,
|
|
29
|
+
closeDrawer,
|
|
30
|
+
logo
|
|
31
|
+
} = useApp();
|
|
32
|
+
|
|
33
|
+
const analyticsController = useAnalyticsController();
|
|
34
|
+
const navigation = useNavigationController();
|
|
35
|
+
|
|
36
|
+
const tooltipsOpen = drawerHovered && !drawerOpen;
|
|
37
|
+
const largeLayout = useLargeLayout();
|
|
38
|
+
const navigate = useNavigate();
|
|
39
|
+
|
|
40
|
+
const [adminMenuOpen, setAdminMenuOpen] = React.useState(false);
|
|
41
|
+
|
|
42
|
+
if (!navigation.topLevelNavigation)
|
|
43
|
+
throw Error("Navigation not ready in Drawer");
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
navigationEntries,
|
|
47
|
+
groups
|
|
48
|
+
}: TopNavigationResult = navigation.topLevelNavigation;
|
|
49
|
+
|
|
50
|
+
const adminViews = navigationEntries.filter(e => e.type === "admin") ?? [];
|
|
51
|
+
const groupsWithoutAdmin = groups.filter(g => g !== "Admin");
|
|
52
|
+
|
|
53
|
+
const buildGroupHeader = useCallback((group?: string) => {
|
|
54
|
+
if (!drawerOpen) return <div className="h-12 w-full"/>;
|
|
55
|
+
return <div
|
|
56
|
+
className="pt-8 pl-6 pr-8 pb-2 flex flex-row items-center">
|
|
57
|
+
<Typography variant={"caption"}
|
|
58
|
+
color={"secondary"}
|
|
59
|
+
className="font-medium flex-grow line-clamp-1">
|
|
60
|
+
{group ? group.toUpperCase() : "Views".toUpperCase()}
|
|
61
|
+
</Typography>
|
|
62
|
+
|
|
63
|
+
</div>;
|
|
64
|
+
}, [drawerOpen]);
|
|
65
|
+
|
|
66
|
+
const onClick = (view: TopNavigationEntry) => {
|
|
67
|
+
const eventName: CMSAnalyticsEvent = view.type === "collection"
|
|
68
|
+
? "drawer_navigate_to_collection"
|
|
69
|
+
: (view.type === "view" ? "drawer_navigate_to_view" : "unmapped_event");
|
|
70
|
+
analyticsController.onAnalyticsEvent?.(eventName, { url: view.url });
|
|
71
|
+
if (!largeLayout)
|
|
72
|
+
closeDrawer();
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<>
|
|
77
|
+
<div className={cls("flex flex-col h-full relative flex-grow w-full", className)} style={style}>
|
|
78
|
+
|
|
79
|
+
<DrawerLogo logo={logo}/>
|
|
80
|
+
|
|
81
|
+
<div className={"overflow-scroll no-scrollbar"}>
|
|
82
|
+
|
|
83
|
+
{groupsWithoutAdmin.map((group) => (
|
|
84
|
+
<React.Fragment
|
|
85
|
+
key={`drawer_group_${group}`}>
|
|
86
|
+
{buildGroupHeader(group)}
|
|
87
|
+
{Object.values(navigationEntries)
|
|
88
|
+
.filter(e => e.group === group)
|
|
89
|
+
.map((view, index) =>
|
|
90
|
+
<DrawerNavigationItem
|
|
91
|
+
key={`navigation_${index}`}
|
|
92
|
+
icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
|
|
93
|
+
tooltipsOpen={tooltipsOpen}
|
|
94
|
+
drawerOpen={drawerOpen}
|
|
95
|
+
onClick={() => onClick(view)}
|
|
96
|
+
url={view.url}
|
|
97
|
+
name={view.name}/>)}
|
|
98
|
+
</React.Fragment>
|
|
99
|
+
))}
|
|
100
|
+
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
{adminViews.length > 0 && <Menu
|
|
104
|
+
open={adminMenuOpen}
|
|
105
|
+
onOpenChange={setAdminMenuOpen}
|
|
106
|
+
trigger={
|
|
107
|
+
<IconButton
|
|
108
|
+
shape={"square"}
|
|
109
|
+
className={"m-4 text-gray-900 dark:text-white w-fit"}>
|
|
110
|
+
<Tooltip title={"Admin"}
|
|
111
|
+
open={tooltipsOpen}
|
|
112
|
+
side={"right"} sideOffset={28}>
|
|
113
|
+
<MoreVertIcon/>
|
|
114
|
+
</Tooltip>
|
|
115
|
+
{drawerOpen && <div
|
|
116
|
+
className={cls(
|
|
117
|
+
drawerOpen ? "opacity-100" : "opacity-0 hidden",
|
|
118
|
+
"mx-4 font-inherit text-inherit"
|
|
119
|
+
)}>
|
|
120
|
+
ADMIN
|
|
121
|
+
</div>}
|
|
122
|
+
</IconButton>}
|
|
123
|
+
>
|
|
124
|
+
{adminViews.map((entry, index) =>
|
|
125
|
+
<MenuItem
|
|
126
|
+
onClick={(event) => {
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
navigate(entry.path);
|
|
129
|
+
}}
|
|
130
|
+
key={`navigation_${index}`}>
|
|
131
|
+
{<IconForView collectionOrView={entry.view}/>}
|
|
132
|
+
{entry.name}
|
|
133
|
+
</MenuItem>)}
|
|
134
|
+
|
|
135
|
+
</Menu>}
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
</>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* This is the logo displayed in the drawer
|
|
144
|
+
* It expands when the drawer is open.
|
|
145
|
+
*
|
|
146
|
+
* @param logo
|
|
147
|
+
* @constructor
|
|
148
|
+
*/
|
|
149
|
+
export function DrawerLogo({ logo }: {
|
|
150
|
+
logo?: string;
|
|
151
|
+
}) {
|
|
152
|
+
|
|
153
|
+
const navigation = useNavigationController();
|
|
154
|
+
const { drawerOpen } = useApp();
|
|
155
|
+
return <div
|
|
156
|
+
style={{
|
|
157
|
+
transition: "padding 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms",
|
|
158
|
+
padding: drawerOpen ? "32px 144px 0px 24px" : "72px 16px 0px"
|
|
159
|
+
}}
|
|
160
|
+
className={cls("cursor-pointer")}>
|
|
161
|
+
|
|
162
|
+
<Tooltip title={"Home"}
|
|
163
|
+
sideOffset={20}
|
|
164
|
+
side="right">
|
|
165
|
+
<Link
|
|
166
|
+
to={navigation.basePath}>
|
|
167
|
+
{logo
|
|
168
|
+
? <img src={logo}
|
|
169
|
+
alt="Logo"
|
|
170
|
+
className={cls("max-w-full max-h-full",
|
|
171
|
+
drawerOpen ?? "w-[112px] h-[112px]")}/>
|
|
172
|
+
: <FireCMSLogo/>}
|
|
173
|
+
|
|
174
|
+
</Link>
|
|
175
|
+
</Tooltip>
|
|
176
|
+
</div>;
|
|
177
|
+
}
|