useDrawerForm
useModalForm hook also allows you to manage a form inside a drawer component, such as a modal component. It provides some useful methods to handle the form drawer.
info
useModalForm hook based on useForm hook provided by @pankod/refine-mantine.
Usage
We'll show two examples, one for creating and one for editing a post. Let's see how useModalForm is used in both.
Create Drawer
First, we'll create a list page for posts. We'll use the useTable hook to manage the table and the useModalForm hook as a useDrawerForm to manage the form.
src/pages/posts/list.tsx
import React from "react";
import { useTable, ColumnDef, flexRender } from "@pankod/refine-react-table";
import {
List,
ScrollArea,
Table,
Pagination,
useModalForm as useDrawerForm,
} from "@pankod/refine-mantine";
import { CreatePostDrawer } from "../../components";
import { IPost } from "../../interfaces";
export const PostList: React.FC = () => {
const createDrawerForm = useDrawerForm({
refineCoreProps: { action: "create" },
initialValues: {
title: "",
status: "",
category: {
id: "",
},
content: "",
},
validate: {
title: (value) => (value.length < 2 ? "Too short title" : null),
status: (value) =>
value.length <= 0 ? "Status is required" : null,
category: {
id: (value) =>
value.length <= 0 ? "Category is required" : null,
},
content: (value) =>
value.length < 10 ? "Too short content" : null,
},
});
const {
modal: { show: showCreateDrawer },
} = createDrawerForm;
const columns = React.useMemo<ColumnDef<IPost>[]>(
() => [
{
id: "id",
header: "ID",
accessorKey: "id",
},
{
id: "title",
header: "Title",
accessorKey: "title",
},
{
id: "status",
header: "Status",
accessorKey: "status",
},
],
[],
);
const {
getHeaderGroups,
getRowModel,
refineCore: { setCurrent, pageCount, current },
} = useTable({
columns,
});
return (
<>
<CreatePostDrawer {...createDrawerForm} />
<ScrollArea>
<List createButtonProps={{ onClick: () => showCreateDrawer() }}>
<Table highlightOnHover>
<thead>
{getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th key={header.id}>
{!header.isPlaceholder && (
<div>
{flexRender(
header.column
.columnDef
.header,
header.getContext(),
)}
</div>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{getRowModel().rows.map((row) => {
return (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef
.cell,
cell.getContext(),
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</Table>
<br />
<Pagination
position="right"
total={pageCount}
page={current}
onChange={setCurrent}
/>
</List>
</ScrollArea>
</>
);
};
export interface IPost {
id: number;
title: string;
content: string;
status: "published" | "draft" | "rejected";
category: { id: number };
}
Now, let's see how the CreatePostDrawer component is implemented.
src/components/createPostDrawer.tsx
import { BaseRecord, HttpError } from "@pankod/refine-core";
import {
UseModalFormReturnType as UseDrawerFormReturnType,
Drawer,
TextInput,
RichTextEditor,
Select,
useSelect,
SaveButton,
Box,
Text,
} from "@pankod/refine-mantine";
interface FormValues {
title: string;
content: string;
status: string;
category: { id: string };
}
export const CreatePostDrawer: React.FC<
UseDrawerFormReturnType<BaseRecord, HttpError, FormValues>
> = ({
getInputProps,
errors,
modal: { visible, close, title },
saveButtonProps,
}) => {
const { selectProps } = useSelect({
resource: "categories",
});
return (
<Drawer
opened={visible}
onClose={close}
title={title}
padding="xl"
size="xl"
position="right"
>
<TextInput
mt={8}
label="Title"
placeholder="Title"
{...getInputProps("title")}
/>
<Select
mt={8}
label="Status"
placeholder="Pick one"
{...getInputProps("status")}
data={[
{ label: "Published", value: "published" },
{ label: "Draft", value: "draft" },
{ label: "Rejected", value: "rejected" },
]}
/>
<Select
mt={8}
label="Category"
placeholder="Pick one"
{...getInputProps("category.id")}
{...selectProps}
/>
<Text mt={8} weight={500} size="sm" color="#212529">
Content
</Text>
<RichTextEditor
sx={{ minHeight: 300 }}
{...getInputProps("content")}
/>
{errors.content && (
<Text mt={2} weight={500} size="xs" color="red">
{errors.content}
</Text>
)}
<Box mt={8} sx={{ display: "flex", justifyContent: "flex-end" }}>
<SaveButton {...saveButtonProps} />
</Box>
</Drawer>
);
};
Edit Drawer
Now, let's add the edit drawer to the PostList component.
src/pages/posts/list.tsx
import React from "react";
import { useTable, ColumnDef, flexRender } from "@pankod/refine-react-table";
import {
List,
ScrollArea,
Table,
Pagination,
EditButton,
useModalForm as useDrawerForm,
} from "@pankod/refine-mantine";
import { CreatePostDrawer, EditPostDrawer } from "../../components";
import { IPost } from "../../interfaces";
export const PostList: React.FC = () => {
const createDrawerForm = useDrawerForm({
refineCoreProps: { action: "create" },
initialValues: {
title: "",
status: "",
category: {
id: "",
},
content: "",
},
validate: {
title: (value) => (value.length < 2 ? "Too short title" : null),
status: (value) =>
value.length <= 0 ? "Status is required" : null,
category: {
id: (value) =>
value.length <= 0 ? "Category is required" : null,
},
content: (value) =>
value.length < 10 ? "Too short content" : null,
},
});
const {
modal: { show: showCreateDrawer },
} = createDrawerForm;
const editDrawerForm = useDrawerForm({
refineCoreProps: { action: "edit" },
initialValues: {
title: "",
status: "",
category: {
id: "",
},
content: "",
},
validate: {
title: (value) => (value.length < 2 ? "Too short title" : null),
status: (value) =>
value.length <= 0 ? "Status is required" : null,
category: {
id: (value) =>
value.length <= 0 ? "Category is required" : null,
},
content: (value) =>
value.length < 10 ? "Too short content" : null,
},
});
const {
modal: { show: showEditDrawer },
} = editDrawerForm;
const columns = React.useMemo<ColumnDef<IPost>[]>(
() => [
{
id: "id",
header: "ID",
accessorKey: "id",
},
{
id: "title",
header: "Title",
accessorKey: "title",
},
{
id: "status",
header: "Status",
accessorKey: "status",
},
{
id: "actions",
header: "Actions",
accessorKey: "id",
cell: function render({ getValue }) {
return (
<EditButton
hideText
size="xs"
onClick={() => showEditDrawer(getValue() as number)}
/>
);
},
},
],
[],
);
const {
getHeaderGroups,
getRowModel,
refineCore: { setCurrent, pageCount, current },
} = useTable({
columns,
});
return (
<>
<CreatePostDrawer {...createDrawerForm} />
<EditPostDrawer {...editDrawerForm} />
<ScrollArea>
<List createButtonProps={{ onClick: () => showCreateDrawer() }}>
<Table highlightOnHover>
<thead>
{getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th key={header.id}>
{!header.isPlaceholder && (
<div>
{flexRender(
header.column
.columnDef
.header,
header.getContext(),
)}
</div>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{getRowModel().rows.map((row) => {
return (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef
.cell,
cell.getContext(),
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</Table>
<br />
<Pagination
position="right"
total={pageCount}
page={current}
onChange={setCurrent}
/>
</List>
</ScrollArea>
</>
);
};
Finally, let's see how the EditPostDrawer component is implemented.
src/components/editPostDrawer.tsx
import { BaseRecord, HttpError } from "@pankod/refine-core";
import {
UseModalFormReturnType as UseDrawerFormReturnType,
Drawer,
TextInput,
RichTextEditor,
Select,
useSelect,
SaveButton,
Box,
Text,
} from "@pankod/refine-mantine";
interface FormValues {
title: string;
content: string;
status: string;
category: { id: string };
}
export const EditPostDrawer: React.FC<
UseDrawerFormReturnType<BaseRecord, HttpError, FormValues>
> = ({
getInputProps,
errors,
modal: { visible, close, title },
refineCore: { queryResult },
saveButtonProps,
}) => {
const { selectProps } = useSelect({
resource: "categories",
defaultValue: queryResult?.data?.data.category.id,
});
return (
<Drawer
opened={visible}
onClose={close}
title={title}
padding="xl"
size="xl"
position="right"
>
<TextInput
mt={8}
label="Title"
placeholder="Title"
{...getInputProps("title")}
/>
<Select
mt={8}
label="Status"
placeholder="Pick one"
{...getInputProps("status")}
data={[
{ label: "Published", value: "published" },
{ label: "Draft", value: "draft" },
{ label: "Rejected", value: "rejected" },
]}
/>
<Select
mt={8}
label="Category"
placeholder="Pick one"
{...getInputProps("category.id")}
{...selectProps}
/>
<Text mt={8} weight={500} size="sm" color="#212529">
Content
</Text>
<RichTextEditor {...getInputProps("content")} />
{errors.content && (
<Text mt={2} weight={500} size="xs" color="red">
{errors.content}
</Text>
)}
<Box mt={8} sx={{ display: "flex", justifyContent: "flex-end" }}>
<SaveButton {...saveButtonProps} />
</Box>
</Drawer>
);
};
API Reference
Properties
| Property | Description | Type |
|---|---|---|
| modalProps | Configuration object for the modal or drawer | ModalPropsType |
| refineCoreProps | Configuration object for the core of the useForm | UseFormProps |
@mantine/form's useForm properties | See useForm documentation |
ModalPropsType
Property Description Type Default defaultVisible Initial visibility state of the modal or drawer booleanfalseautoSubmitClose Whether the form should be submitted when the modal or drawer is closed booleantrueautoResetForm Whether the form should be reset when the form is submitted booleantrue
Return values
| Property | Description | Type |
|---|---|---|
| modal | Relevant states and methods to control the modal or drawer | ModalReturnValues |
| refineCore | The return values of the useForm in the core | UseFormReturnValues |
@mantine/form's useForm return values | See useForm documentation |
ModalReturnValues
Property Description Type visible State of modal of drawer visibility booleanshow Sets the visible state to true (id?: BaseKey) => voidclose Sets the visible state to false () => voidsubmit Submits the form (values: TVariables) => voidtitle Modal or drawer title based on resource and action value stringsaveButtonProps Props for a submit button { disabled: boolean, onClick: (e: React.FormEvent<HTMLFormElement>) => void; }