Let's head now to the part where i don't really admire, working with UI, let's create our FileManager
component, but firstly let's define what props it accepts.
For encapsulation behavior, i'll add the FileManager types file inside the component directory.
Create file-manager/components/FileManager
directory then create FileManager.types.ts
inside it
// file-manager/components/FileManager/FileManager.types.ts
import { Node } from "../../types/FileManager.types";
export type FileManagerProps = {
/**
* Open State for the file manager
*/
open: boolean;
/**
* Callback for when the file manager is closed
*/
onClose: () => void;
/**
* Root path to open in the file manager
*
* @default "/"
*/
rootPath?: string;
/**
* Callback for when a file/directory is selected
*/
onSelect?: (node: Node) => void;
/**
* Callback for when a file/directory is double clicked
*/
onDoubleClick?: (node: Node) => void;
/**
* Callback for when a file/directory is right clicked
*/
onRightClick?: (node: Node) => void;
/**
* Callback for when a file/directory is copied
*/
onCopy?: (node: Node) => void;
/**
* Callback for when a file/directory is cut
*/
onCut?: (node: Node) => void;
/**
* Callback for when a file/directory is pasted
* The old node will contain the old path
* and the new node will contain the new path
*/
onPaste?: (node: Node, oldNode: Node) => void;
/**
* Callback for when a file/directory is deleted
*/
onDelete?: (node: Node) => void;
/**
* Callback for when a file/directory is renamed
* The old node will contain the old path/name
* and the new node will contain the new path/name
*/
onRename?: (node: Node, oldNode: Node) => void;
/**
* Callback for when a directory is created
*/
onCreateDirectory?: (directory: Node) => void;
/**
* Callback for when file(s) is uploaded
*/
onUpload?: (files: Node[]) => void;
/**
* Callback for when a file is downloaded
*/
onDownload?: (node: Node) => void;
};
wooohoo, don't freak out, i'm just trying to make you imagine the big picture, try to read the component props slowly and carefully and the comments above each prop.
For the time being we'll just need only two props, open
and onClose
which are the only required props.
// file-manager/components/FileManager/FileManager.types.ts
export type FileManagerProps = {
/**
* Open State for the file manager
*/
open: boolean;
/**
* Root path to open in the file manager
*
* @default "/"
*/
rootPath?: string;
/**
* Callback for when the file manager is closed
*/
onClose: () => void;
};
These two props are responsible for opening and closing the file manager, as we will open it up in a modal.
Now let's create our FileManager
Component.
// file-manager/components/FileManager/FileManager.ts
import { FileManagerProps } from "./FileManager.types";
export default function FileManager({ open, onClose }: FileManagerProps) {
return <div>FileManager</div>;
}
Before we proceed to the next step, we'll create an index
file to encapsulate the files imports from it directly.
// file-manager/components/FileManager/index.ts
export { default } from "./FileManager";
export * from "./FileManager.types";
Now let's go to our HomePage
and call our newly created component, the FileManager
.
Small modification, we'll also create a component file
HomePage
for the home page rather than sitting it in the index file, then we'll export it from the index as we did in the file manager.
// home/components/HomePage/HomePage.tsx
import Helmet from "@mongez/react-helmet";
import FileManager from "app/file-manager/components/FileManager";
import { useState } from "react";
export default function HomePage() {
const [openFileManager, setOpenFileManager] = useState(false);
return (
<>
<Helmet title="home" appendAppName={false} />
<h1>Welcome To Home Page</h1>
<FileManager
open={openFileManager}
onClose={() => setOpenFileManager(false)}
/>
</>
);
}
We created a state to manage the open/close state for the file manager we set it to false
as default, then we called our component in the function return.
Now let's see what will be in the browser, you should have something like this:
Setting up Mantine Styles
I won't stop here too long, we'll just do what the docs says and import Mantine Theme Provider in top of our application, which can be set in the Root
component.
Let's go to src/apps/front-office/design-system/layouts/Root.tsx
and update it with the following:
import { AppShell, MantineProvider } from "@mantine/core";
import { BasicComponentProps } from "../../utils/types";
/**
* The root should be used with react-router's configuration for rootComponent.
* So it will wrap the entire app.
* It can be useful for single operations as this component will only render once in the entire application life cycle.
* You may for instance fetch settings from the server before loading the app or a Bearer token to work with the API.
*/
export default function Root({ children }: BasicComponentProps) {
return (
<>
<MantineProvider withGlobalStyles withNormalizeCSS>
<AppShell>{children}</AppShell>
</MantineProvider>
</>
);
}
All what we did is we added the theme provider to be wrapping our children, which is literally the entire application.
The AppShell Component will just make some paddings around the content, you can skip it if you like.
Now you should see something like this:
Bonus Tip
You can change the color scheme of Mantine by setting colorScheme
to dark
.
import { AppShell, MantineProvider } from "@mantine/core";
import { BasicComponentProps } from "../../utils/types";
/**
* The root should be used with react-router's configuration for rootComponent.
* So it will wrap the entire app.
* It can be useful for single operations as this component will only render once in the entire application life cycle.
* You may for instance fetch settings from the server before loading the app or a Bearer token to work with the API.
*/
export default function Root({ children }: BasicComponentProps) {
return (
<>
<MantineProvider
theme={{
colorScheme: "dark",
}}
withGlobalStyles
withNormalizeCSS>
<AppShell>{children}</AppShell>
</MantineProvider>
</>
);
}
Now it will look like this:
You can smartly detect if the user (like me) prefers dark mode and is setting it in his/her device using userPrefersDarkMode
utility from Mongez Dom.
import { AppShell, MantineProvider } from "@mantine/core";
import { userPrefersDarkMode } from "@mongez/dom";
import { BasicComponentProps } from "../../utils/types";
/**
* The root should be used with react-router's configuration for rootComponent.
* So it will wrap the entire app.
* It can be useful for single operations as this component will only render once in the entire application life cycle.
* You may for instance fetch settings from the server before loading the app or a Bearer token to work with the API.
*/
export default function Root({ children }: BasicComponentProps) {
return (
<>
<MantineProvider
theme={{
colorScheme: userPrefersDarkMode() ? "dark" : "light",
}}
withGlobalStyles
withNormalizeCSS>
<AppShell>{children}</AppShell>
</MantineProvider>
</>
);
}
Now it will appear based on the user's device default theme mode.
For the demo purpose, we'll stick to the light mode for now, maybe we change it later.
Heading back to our file manager component.
File Manager In Modal
Now we'll add wrap our file manager in a Modal so we can open and close it from the parent component.
// FileManager.tsx
import { Modal } from "@mantine/core";
import { FileManagerProps } from "./FileManager.types";
export default function FileManager({ open, onClose }: FileManagerProps) {
return (
<>
<Modal size="xl" opened={open} onClose={onClose}>
<h1>File Manager</h1>
</Modal>
</>
);
}
I just added the modal around simple content which pops with the File Manager
keywords and set the size to be xl
.
Now let's add a button in the home page to open the file manager.
// HomePage.tsx
import { Button } from "@mantine/core";
import Helmet from "@mongez/react-helmet";
import FileManager from "app/file-manager/components/FileManager";
import { useState } from "react";
export default function HomePage() {
const [openFileManager, setOpenFileManager] = useState(false);
return (
<>
<Helmet title="home" appendAppName={false} />
<h1>Welcome To Home Page</h1>
<Button
onClick={() => setOpenFileManager(true)}
variant="gradient"
gradient={{ from: "red", to: "orange" }}>
Open File Manager
</Button>
<FileManager
open={openFileManager}
onClose={() => setOpenFileManager(false)}
/>
</>
);
}
I used a nice Gradient Button to open the file manager.
Now you should see this in your browser
And when you click on the button, you should see this
We are now ready to establish our file manager skelton, which will be in our next tutorial.
Article Repository
You can see chapter files in Github Repository
Don't forget the
main
branch has the latest updated code.
Salam.
Top comments (0)