import * as React from 'react'; import ContentAdd from '@mui/icons-material/Add'; import { Fab, Theme, useMediaQuery } from '@mui/material'; import { styled } from '@mui/material/styles'; import clsx from 'clsx'; import isEqual from 'lodash/isEqual'; import merge from 'lodash/merge'; import PropTypes from 'prop-types'; import { useCreatePath, useResourceContext, useTranslate } from 'ra-core'; import { Link, To } from 'react-router-dom'; import { Button, ButtonProps, LocationDescriptor } from './Button'; /** * Opens the Create view of a given resource * * Renders as a regular button on desktop, and a Floating Action Button * on mobile. * * @example // basic usage * import { CreateButton } from 'react-admin'; * * const CommentCreateButton = () => ( * * ); */ function CreateBtn(props: CreateButtonProps) { const { className, icon = defaultIcon, label = 'ra.action.create', resource: resourceProp, scrollToTop = true, variant, to: locationDescriptor, state: initialState = {}, disableFloatingButton = false, ...rest } = props; const resource = useResourceContext(props); const createPath = useCreatePath(); const translate = useTranslate(); const isSmall = useMediaQuery((theme: Theme) => theme.breakpoints.down('md')); const state = merge({}, scrollStates.get(String(scrollToTop)), initialState); // Duplicated behaviour of Button component (legacy use) which will be removed in v5. const linkParams = getLinkParams(locationDescriptor); return !disableFloatingButton && isSmall ? ( {icon} ) : ( {icon} ); } const defaultIcon = ; // avoids using useMemo to get a constant value for the link state const scrollStates = new Map([ ['true', { _scrollToTop: true }], ['false', {}] ]); interface Props { resource?: string; icon?: React.ReactElement; scrollToTop?: boolean; to?: LocationDescriptor | To; state?: any; } interface DisableFloatingButtonProps { disableFloatingButton?: boolean; } type CreateButtonProps = DisableFloatingButtonProps & Props & Omit; CreateBtn.propTypes = { resource: PropTypes.string, className: PropTypes.string, icon: PropTypes.element, label: PropTypes.string }; const PREFIX = 'RaCreateButton'; const CreateButtonClasses = { root: `${PREFIX}-root`, floating: `${PREFIX}-floating` }; const StyledFab = styled(Fab, { name: PREFIX, overridesResolver: (_props, styles) => styles.root })(({ theme }) => ({ [`&.${CreateButtonClasses.floating}`]: { color: theme.palette.getContrastText(theme.palette.primary.main), margin: 0, top: 'auto', right: 20, bottom: 60, left: 'auto', position: 'fixed', zIndex: 1000 } })) as unknown as typeof Fab; const StyledButton = styled(Button, { name: PREFIX, overridesResolver: (_props, styles) => styles.root })({}); function arePropsEqual(prevProps: CreateButtonProps, nextProps: CreateButtonProps) { return ( prevProps.resource === nextProps.resource && prevProps.label === nextProps.label && prevProps.translate === nextProps.translate && prevProps.disabled === nextProps.disabled && isEqual(prevProps.to, nextProps.to) && isEqual(prevProps.state, nextProps.state) ); } const CreateButton = React.memo(CreateBtn, arePropsEqual); function getLinkParams(locationDescriptor?: LocationDescriptor | string) { // eslint-disable-next-line eqeqeq if (locationDescriptor == undefined) { return undefined; } if (typeof locationDescriptor === 'string') { return { to: locationDescriptor }; } const { redirect, replace, state, ...to } = locationDescriptor; return { to, redirect, replace, state }; } export { CreateButton, CreateButtonClasses }; export type { CreateButtonProps };