packages/mui/src/components/crud/edit/index.tsx
import React from "react";
import {
useMutationMode,
useNavigation,
useTranslate,
useUserFriendlyName,
useRefineContext,
useToPath,
useResource,
useRouterType,
useBack,
useGo,
} from "@refinedev/core";
import Card from "@mui/material/Card";
import CardHeader from "@mui/material/CardHeader";
import IconButton from "@mui/material/IconButton";
import CardContent from "@mui/material/CardContent";
import CardActions from "@mui/material/CardActions";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import {
DeleteButton,
RefreshButton,
ListButton,
SaveButton,
Breadcrumb,
ListButtonProps,
RefreshButtonProps,
DeleteButtonProps,
SaveButtonProps,
AutoSaveIndicator,
} from "@components";
import { EditProps } from "../types";
import { RefinePageHeaderClassNames } from "@refinedev/ui-types";
/**
* `<Edit>` provides us a layout for displaying the page.
* It does not contain any logic but adds extra functionalities like a refresh button.
*
* @see {@link https://refine.dev/docs/ui-frameworks/mui/components/basic-views/edit} for more details.
*/
export const Edit: React.FC<EditProps> = ({
title,
saveButtonProps: saveButtonPropsFromProps,
mutationMode: mutationModeProp,
recordItemId,
children,
deleteButtonProps: deleteButtonPropsFromProps,
canDelete,
resource: resourceFromProps,
isLoading = false,
breadcrumb: breadcrumbFromProps,
dataProviderName,
wrapperProps,
headerProps,
contentProps,
headerButtonProps,
headerButtons,
footerButtonProps,
footerButtons,
goBack: goBackFromProps,
autoSaveProps,
}) => {
const translate = useTranslate();
const {
options: { breadcrumb: globalBreadcrumb } = {},
} = useRefineContext();
const { mutationMode: mutationModeContext } = useMutationMode();
const mutationMode = mutationModeProp ?? mutationModeContext;
const routerType = useRouterType();
const back = useBack();
const go = useGo();
const { goBack, list: legacyGoList } = useNavigation();
const getUserFriendlyName = useUserFriendlyName();
const {
resource,
action,
id: idFromParams,
identifier,
} = useResource(resourceFromProps);
const goListPath = useToPath({
resource,
action: "list",
});
const id = recordItemId ?? idFromParams;
const breadcrumb =
typeof breadcrumbFromProps === "undefined"
? globalBreadcrumb
: breadcrumbFromProps;
const hasList = resource?.list && !recordItemId;
const isDeleteButtonVisible =
canDelete ??
((resource?.meta?.canDelete ?? resource?.canDelete) ||
deleteButtonPropsFromProps);
const breadcrumbComponent =
typeof breadcrumb !== "undefined" ? (
<>{breadcrumb}</> ?? undefined
) : (
<Breadcrumb />
);
const listButtonProps: ListButtonProps | undefined = hasList
? {
...(isLoading ? { disabled: true } : {}),
resource: routerType === "legacy" ? resource?.route : identifier,
}
: undefined;
const refreshButtonProps: RefreshButtonProps = {
...(isLoading ? { disabled: true } : {}),
resource: routerType === "legacy" ? resource?.route : identifier,
recordItemId: id,
dataProviderName,
};
const defaultHeaderButtons = (
<Box display="flex" flexDirection="row" alignItems="center">
{autoSaveProps && <AutoSaveIndicator {...autoSaveProps} />}
{hasList && <ListButton {...listButtonProps} />}
<RefreshButton {...refreshButtonProps} />
</Box>
);
const deleteButtonProps: DeleteButtonProps | undefined = isDeleteButtonVisible
? ({
...(isLoading ? { disabled: true } : {}),
resource: routerType === "legacy" ? resource?.route : identifier,
mutationMode,
variant: "outlined",
onSuccess: () => {
if (routerType === "legacy") {
legacyGoList(resource?.route ?? resource?.name ?? "");
} else {
go({ to: goListPath });
}
},
recordItemId: id,
dataProviderName,
...deleteButtonPropsFromProps,
} as const)
: undefined;
const saveButtonProps: SaveButtonProps = {
...(isLoading ? { disabled: true } : {}),
...saveButtonPropsFromProps,
};
const defaultFooterButtons = (
<>
{isDeleteButtonVisible && <DeleteButton {...deleteButtonProps} />}
<SaveButton {...saveButtonProps} />
</>
);
return (
<Card {...(wrapperProps ?? {})}>
{breadcrumbComponent}
<CardHeader
sx={{
display: "flex",
flexWrap: "wrap",
".MuiCardHeader-action": {
margin: 0,
alignSelf: "center",
},
}}
title={
title ?? (
<Typography
variant="h5"
className={RefinePageHeaderClassNames.Title}
>
{translate(
`${identifier}.titles.edit`,
`Edit ${getUserFriendlyName(
resource?.meta?.label ??
resource?.options?.label ??
resource?.label ??
identifier,
"singular",
)}`,
)}
</Typography>
)
}
avatar={
typeof goBackFromProps !== "undefined" ? (
goBackFromProps
) : (
<IconButton
onClick={
action !== "list" && typeof action !== "undefined"
? routerType === "legacy"
? goBack
: back
: undefined
}
>
<ArrowBackIcon />
</IconButton>
)
}
action={
<Box display="flex" gap="16px" {...(headerButtonProps ?? {})}>
{headerButtons
? typeof headerButtons === "function"
? headerButtons({
defaultButtons: defaultHeaderButtons,
listButtonProps,
refreshButtonProps,
})
: headerButtons
: defaultHeaderButtons}
</Box>
}
{...(headerProps ?? {})}
/>
<CardContent {...(contentProps ?? {})}>{children}</CardContent>
<CardActions
sx={{
display: "flex",
justifyContent: "flex-end",
gap: "16px",
padding: "16px",
}}
{...(footerButtonProps ?? {})}
>
{footerButtons
? typeof footerButtons === "function"
? footerButtons({
defaultButtons: defaultFooterButtons,
deleteButtonProps,
saveButtonProps,
})
: footerButtons
: defaultFooterButtons}
</CardActions>
</Card>
);
};