useModalForm
useModalForm hook also allows you to manage a form inside a modal component. It provides some useful methods to handle the form modal.
信息
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 Modal
First, we'll create a list page for posts. We'll use the useTable hook to manage the table and the useModalForm hook 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,
} from "@pankod/refine-mantine";
import { CreatePostModal } from "../../components";
import { IPost } from "../../interfaces";
export const PostList: React.FC = () => {
const createModalForm = useModalForm({
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: showCreateModal },
} = createModalForm;
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 (
<>
<CreatePostModal {...createModalForm} />
<ScrollArea>
<List createButtonProps={{ onClick: () => showCreateModal() }}>
<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 CreatePostModal component is implemented.
src/components/createPostModal.tsx
import { BaseRecord, HttpError } from "@pankod/refine-core";
import {
UseModalFormReturnType,
Modal,
TextInput,
RichTextEditor,
Select,
useSelect,
Box,
SaveButton,
Text,
} from "@pankod/refine-mantine";
interface FormValues {
title: string;
content: string;
status: string;
category: { id: string };
}
export const CreatePostModal: React.FC<
UseModalFormReturnType<BaseRecord, HttpError, FormValues>
> = ({
getInputProps,
errors,
modal: { visible, close, title },
saveButtonProps,
}) => {
const { selectProps } = useSelect({
resource: "categories",
});
return (
<Modal opened={visible} onClose={close} title={title}>
<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>
</Modal>
);
};
Edit Modal
Now, let's add the edit modal 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,
} from "@pankod/refine-mantine";
import { CreatePostModal, EditPostModal } from "../../components";
import { IPost } from "../../interfaces";
export const PostList: React.FC = () => {
const createModalForm = useModalForm({
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: showCreateModal },
} = createModalForm;
const editModalForm = useModalForm({
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: showEditModal },
} = editModalForm;
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={() => showEditModal(getValue() as number)}
/>
);
},
},
],
[],
);
const {
getHeaderGroups,
getRowModel,
refineCore: { setCurrent, pageCount, current },
} = useTable({
columns,
});
return (
<>
<CreatePostModal {...createModalForm} />
<EditPostModal {...editModalForm} />
<ScrollArea>
<List createButtonProps={{ onClick: () => showCreateModal() }}>
<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 EditPostModal component is implemented.
src/components/editPostModal.tsx
import { BaseRecord, HttpError } from "@pankod/refine-core";
import {
UseModalFormReturnType,
Modal,
TextInput,
RichTextEditor,
Select,
useSelect,
SaveButton,
Box,
Text,
} from "@pankod/refine-mantine";
interface FormValues {
title: string;
content: string;
status: string;
category: { id: string };
}
export const EditPostModal: React.FC<
UseModalFormReturnType<BaseRecord, HttpError, FormValues>
> = ({
getInputProps,
errors,
modal: { visible, close, title },
refineCore: { queryResult },
saveButtonProps,
}) => {
const { selectProps } = useSelect({
resource: "categories",
defaultValue: queryResult?.data?.data.category.id,
});
return (
<Modal opened={visible} onClose={close} title={title}>
<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>
</Modal>
);
};
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 booleanfalseautoSubmitClose Whether the form should be submitted when the modal 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 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 title based on resource and action value stringsaveButtonProps Props for a submit button { disabled: boolean, onClick: (e: React.FormEvent<HTMLFormElement>) => void; }