import { Button as RaButton } from './Button'; import { Toolbar } from '@/components/ra-forms'; import { PlusCircleOutlined } from '@ant-design/icons'; import { Breakpoint, Button, Dialog, DialogContent } from '@mui/material'; import { CreateBase, UseCreateMutateParams, useNotify, useResourceContext, useTranslate } from 'ra-core'; import { ButtonProps, CreateButton, CreateButtonProps, CreateProps, SaveButton } from 'ra-ui-materialui'; import React, { Children, PropsWithChildren, useCallback, useState } from 'react'; import { FieldValues, useFormContext } from 'react-hook-form'; import { useQueryClient } from 'react-query'; function updateColl(old: any, data: any) { const id = data.id; if (!old) return [data]; const index = old.findIndex((record: any) => record.id === id); if (index === -1) { return [...old, data]; } return [...old.slice(0, index), { ...old[index], ...data }, ...old.slice(index + 1)]; } function setManyReferenceQueryData(res: any, data: any) { const result = res && res.data ? { data: updateColl(res.data, data), total: res.total + 1 } : res; return result; } function setManyQueryData(coll: any, data: any) { return coll && coll.length > 0 ? updateColl(coll, data) : coll; } function setListQueryData(res: any, data: any) { return res && res.data ? { ...res, data: updateColl(res.data, data), total: res.total + 1 } : res; } type ResponsiveButtonProps = CreateButtonProps & ButtonProps & { label?: string; onClick?: () => void; disableFloatingButton?: boolean; }; function ResponsiveButton(props: ResponsiveButtonProps): JSX.Element { const { disableFloatingButton, label = 'ra.action.create', startIcon = , ...rest } = props; const handleClick = useCallback( (event: any) => { (event as Event).preventDefault(); props?.onClick?.(); }, [props] ); return disableFloatingButton ? ( {startIcon as any} ) : ( {startIcon as any} ); } type CloseDialogFunction = () => void; type SubmitFunction = (values: FieldValues, closeDialog: CloseDialogFunction) => void; type SubmitButtonProps = { onSubmit: SubmitFunction; closeDialog: CloseDialogFunction; }; function SubmitButton(props: SubmitButtonProps) { const { onSubmit, closeDialog } = props; const { handleSubmit } = useFormContext(); const translate = useTranslate(); const handleClick = useCallback( (event: any) => { (event as Event).preventDefault(); (event as Event).stopPropagation(); handleSubmit((fieldValues) => onSubmit(fieldValues, closeDialog))(); }, [handleSubmit, onSubmit, closeDialog] ); return ( ); } type CreateInDialogButtonProps = PropsWithChildren & CreateProps & ResponsiveButtonProps & { maxWidth: Breakpoint; fullWidth?: boolean; fullScreen?: boolean; /** * Indicates whether the floating button should be disabled. * Default is false, this mean that the floating button will be displayed on small screens. * **If you have multiple buttons on the same page, they will be displayed over each other! ** */ disableFloatingButton?: boolean; scroll: 'paper' | 'body' | undefined; /** * Custom handler for the submit event, in this case you are responsible for closing the dialog. * @example * * const handleSubmit = (values, closeDialog) => { * console.log(values); // { title: 'Hello World' } * closeDialog(); * } * * * * * * */ onSubmit?: SubmitFunction; /** * Controlled open state. When provided, the dialog becomes externally controlled * (the internal state is ignored). Enables opening the dialog programmatically. * Omit to keep the default uncontrolled behavior. */ open?: boolean; /** * Called when the dialog should open or close. Use together with `open`. * Receives the next open state. */ onOpenChange?: (open: boolean) => void; /** * When true, the trigger button is not rendered. Useful when the dialog is driven * externally, where only the dialog surface is needed. Defaults to false. */ hideTrigger?: boolean; }; function CreateInDialogButton(props: CreateInDialogButtonProps) { const { children, maxWidth = 'md', fullWidth = true, fullScreen, hideTrigger = false, open: openProp, onOpenChange, onSubmit, mutationOptions, scroll = 'body', ...rest } = props; const queryClient = useQueryClient(); const resource = useResourceContext(); const translate = useTranslate(); const Child = Children.only(children); const isControlled = openProp !== undefined; const [internalOpen, setInternalOpen] = useState(false); const open = isControlled ? openProp : internalOpen; const setOpenState = useCallback( (next: boolean) => { if (!isControlled) setInternalOpen(next); onOpenChange?.(next); }, [isControlled, onOpenChange] ); const notify = useNotify(); const handleOpen = useCallback(() => setOpenState(true), [setOpenState]); const handleClose = useCallback(() => setOpenState(false), [setOpenState]); const handleSuccess = useCallback( (data: any, params: UseCreateMutateParams, context: unknown) => { const now = Date.now(); const updatedAt = now; queryClient.setQueryData([resource, 'getOne', { id: data.id }], data); queryClient.setQueriesData([resource, 'getList'], (res: any) => setListQueryData(res, data), { updatedAt }); queryClient.setQueriesData([resource, 'getMany'], (coll: any) => setManyQueryData(coll, data), { updatedAt }); queryClient.setQueriesData([resource, 'getManyReference'], (res: any) => setManyReferenceQueryData(res, data), { updatedAt }); handleClose(); notify('ra.notification.created', { messageArgs: { smart_count: 1 } }); props?.mutationOptions?.onSuccess?.(data, params, context); }, [handleClose, notify, props.mutationOptions, queryClient, resource] ); return ( <> {!hideTrigger && } {React.isValidElement(Child) ? React.cloneElement(Child, { ...Child.props, modal: true, toolbar: ( {onSubmit ? ( ) : ( )} ) }) : null} ); } export { CreateInDialogButton }; export type { CreateInDialogButtonProps };