@firecms/core 3.0.0-canary.61 → 3.0.0-canary.62
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 +26 -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/index.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +2053 -2036
- 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 +59 -109
- 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/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.62",
|
|
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.62",
|
|
50
|
+
"@firecms/ui": "^3.0.0-canary.62",
|
|
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": "75f1049ecbeaf3ee6d73b52a7432ac199c4a9877",
|
|
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,71 +1,27 @@
|
|
|
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
|
|
56
17
|
*/
|
|
57
18
|
autoOpenDrawer?: boolean;
|
|
58
19
|
|
|
59
|
-
|
|
60
|
-
* The AppBar component to be used in the scaffold.
|
|
61
|
-
*/
|
|
62
|
-
FireCMSAppBar?: React.ComponentType<FireCMSAppBarProps<ExtraAppbarProps>>;
|
|
20
|
+
logo?: string;
|
|
63
21
|
|
|
64
|
-
|
|
65
|
-
* Additional props passed to the custom AppBar
|
|
66
|
-
*/
|
|
67
|
-
fireCMSAppBarProps?: Partial<FireCMSAppBarProps> & ExtraAppbarProps;
|
|
22
|
+
className?: string;
|
|
68
23
|
|
|
24
|
+
style?: React.CSSProperties;
|
|
69
25
|
}
|
|
70
26
|
|
|
71
27
|
/**
|
|
@@ -84,15 +40,23 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
|
|
|
84
40
|
|
|
85
41
|
const {
|
|
86
42
|
children,
|
|
87
|
-
name,
|
|
88
|
-
logo,
|
|
89
|
-
includeDrawer = true,
|
|
90
43
|
autoOpenDrawer,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
44
|
+
logo,
|
|
45
|
+
className,
|
|
46
|
+
style
|
|
94
47
|
} = props;
|
|
95
48
|
|
|
49
|
+
const drawerChildren = React.Children.toArray(children).filter((child: any) => child.type.componentType === "Drawer");
|
|
50
|
+
if (drawerChildren.length > 1) {
|
|
51
|
+
throw Error("Only one Drawer component is allowed in Scaffold");
|
|
52
|
+
}
|
|
53
|
+
const appBarChildren = React.Children.toArray(children).filter((child: any) => child.type.componentType === "AppBar");
|
|
54
|
+
if (appBarChildren.length > 1) {
|
|
55
|
+
throw Error("Only one AppBar component is allowed in Scaffold");
|
|
56
|
+
}
|
|
57
|
+
const otherChildren = React.Children.toArray(children)
|
|
58
|
+
.filter((child: any) => child.type.componentType !== "Drawer" && child.type.componentType !== "AppBar");
|
|
59
|
+
const includeDrawer = drawerChildren.length > 0;
|
|
96
60
|
const largeLayout = useLargeLayout();
|
|
97
61
|
|
|
98
62
|
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
|
@@ -112,38 +76,37 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
|
|
|
112
76
|
const computedDrawerOpen: boolean = drawerOpen || Boolean(largeLayout && autoOpenDrawer && onHover);
|
|
113
77
|
|
|
114
78
|
return (
|
|
115
|
-
<
|
|
116
|
-
|
|
79
|
+
<AppContext.Provider value={{
|
|
80
|
+
logo,
|
|
81
|
+
hasDrawer: includeDrawer,
|
|
82
|
+
drawerHovered: onHover,
|
|
117
83
|
drawerOpen: computedDrawerOpen,
|
|
118
84
|
closeDrawer: handleDrawerClose,
|
|
119
85
|
openDrawer: handleDrawerOpen,
|
|
120
86
|
autoOpenDrawer
|
|
121
87
|
}}>
|
|
122
88
|
<div
|
|
123
|
-
className="flex h-screen w-screen bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white overflow-hidden"
|
|
89
|
+
className={cls("flex h-screen w-screen bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white overflow-hidden", className)}
|
|
124
90
|
style={{
|
|
125
91
|
paddingTop: "env(safe-area-inset-top)",
|
|
126
92
|
paddingLeft: "env(safe-area-inset-left)",
|
|
127
93
|
paddingRight: "env(safe-area-inset-right)",
|
|
128
94
|
paddingBottom: "env(safe-area-inset-bottom)",
|
|
129
|
-
height: "100dvh"
|
|
95
|
+
height: "100dvh",
|
|
96
|
+
...style
|
|
130
97
|
}}>
|
|
131
98
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
logo={logo}
|
|
135
|
-
drawerOpen={computedDrawerOpen}
|
|
136
|
-
{...fireCMSAppBarProps}/>
|
|
99
|
+
{appBarChildren}
|
|
100
|
+
|
|
137
101
|
<DrawerWrapper
|
|
138
102
|
displayed={includeDrawer}
|
|
139
103
|
onMouseEnter={setOnHoverTrue}
|
|
140
104
|
onMouseMove={setOnHoverTrue}
|
|
141
105
|
onMouseLeave={setOnHoverFalse}
|
|
142
106
|
open={computedDrawerOpen}
|
|
143
|
-
logo={logo}
|
|
144
107
|
hovered={onHover}
|
|
145
108
|
setDrawerOpen={setDrawerOpen}>
|
|
146
|
-
{includeDrawer &&
|
|
109
|
+
{includeDrawer && drawerChildren}
|
|
147
110
|
</DrawerWrapper>
|
|
148
111
|
|
|
149
112
|
<main
|
|
@@ -153,13 +116,13 @@ export const Scaffold = React.memo<PropsWithChildren<ScaffoldProps>>(
|
|
|
153
116
|
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
117
|
|
|
155
118
|
<ErrorBoundary>
|
|
156
|
-
{
|
|
119
|
+
{otherChildren}
|
|
157
120
|
</ErrorBoundary>
|
|
158
121
|
|
|
159
122
|
</div>
|
|
160
123
|
</main>
|
|
161
124
|
</div>
|
|
162
|
-
</
|
|
125
|
+
</AppContext.Provider>
|
|
163
126
|
);
|
|
164
127
|
},
|
|
165
128
|
equal
|
|
@@ -183,8 +146,6 @@ function DrawerWrapper(props: {
|
|
|
183
146
|
onMouseLeave: () => void
|
|
184
147
|
}) {
|
|
185
148
|
|
|
186
|
-
const navigation = useNavigationController();
|
|
187
|
-
|
|
188
149
|
const width = !props.displayed ? 0 : (props.open ? DRAWER_WIDTH : 72);
|
|
189
150
|
const innerDrawer = <div
|
|
190
151
|
className={"relative h-full no-scrollbar overflow-y-auto overflow-x-hidden"}
|
|
@@ -198,7 +159,7 @@ function DrawerWrapper(props: {
|
|
|
198
159
|
<Tooltip title="Open menu"
|
|
199
160
|
side="right"
|
|
200
161
|
sideOffset={12}
|
|
201
|
-
className="fixed top-2 left-3 !bg-gray-50 dark:!bg-gray-900 rounded-full w-fit"
|
|
162
|
+
className="fixed top-2 left-3 !bg-gray-50 dark:!bg-gray-900 rounded-full w-fit z-20"
|
|
202
163
|
>
|
|
203
164
|
<IconButton
|
|
204
165
|
color="inherit"
|
|
@@ -212,30 +173,19 @@ function DrawerWrapper(props: {
|
|
|
212
173
|
</Tooltip>
|
|
213
174
|
)}
|
|
214
175
|
|
|
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>
|
|
176
|
+
<div
|
|
177
|
+
className={`absolute right-0 top-4 ${
|
|
178
|
+
props.open ? "opacity-100" : "opacity-0 invisible"
|
|
179
|
+
} transition-opacity duration-200 ease-in-out`}>
|
|
180
|
+
<IconButton
|
|
181
|
+
aria-label="Close drawer"
|
|
182
|
+
onClick={() => props.setDrawerOpen(false)}
|
|
183
|
+
>
|
|
184
|
+
<ChevronLeftIcon/>
|
|
185
|
+
</IconButton>
|
|
186
|
+
</div>
|
|
238
187
|
|
|
188
|
+
<div className={"flex flex-col h-full"}>
|
|
239
189
|
{props.children}
|
|
240
190
|
</div>
|
|
241
191
|
|
|
@@ -267,7 +217,7 @@ function DrawerWrapper(props: {
|
|
|
267
217
|
|
|
268
218
|
return (
|
|
269
219
|
<div
|
|
270
|
-
className="relative"
|
|
220
|
+
className="z-20 relative"
|
|
271
221
|
onMouseEnter={props.onMouseEnter}
|
|
272
222
|
onMouseMove={props.onMouseMove}
|
|
273
223
|
onMouseLeave={props.onMouseLeave}
|
|
@@ -278,17 +228,17 @@ function DrawerWrapper(props: {
|
|
|
278
228
|
|
|
279
229
|
{innerDrawer}
|
|
280
230
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
231
|
+
{/*<div*/}
|
|
232
|
+
{/* className={`z-20 absolute right-0 top-4 ${*/}
|
|
233
|
+
{/* props.open ? "opacity-100" : "opacity-0 invisible"*/}
|
|
234
|
+
{/* } transition-opacity duration-1000 ease-in-out`}>*/}
|
|
235
|
+
{/* <IconButton*/}
|
|
236
|
+
{/* aria-label="Close drawer"*/}
|
|
237
|
+
{/* onClick={() => props.setDrawerOpen(false)}*/}
|
|
238
|
+
{/* >*/}
|
|
239
|
+
{/* <ChevronLeftIcon/>*/}
|
|
240
|
+
{/* </IconButton>*/}
|
|
241
|
+
{/*</div>*/}
|
|
292
242
|
|
|
293
243
|
</div>
|
|
294
244
|
);
|
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 DrawerState = {
|
|
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<DrawerState>({
|
|
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
|
+
}
|
package/src/core/index.tsx
CHANGED