Skip to content
3 changes: 2 additions & 1 deletion docs/pages/api-docs/fab.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
},
"default": "'large'"
},
"sx": { "type": { "name": "object" } },
"variant": {
"type": {
"name": "union",
Expand Down Expand Up @@ -52,6 +53,6 @@
"filename": "/packages/material-ui/src/Fab/Fab.js",
"inheritance": { "component": "ButtonBase", "pathname": "/api/button-base/" },
"demos": "<ul><li><a href=\"/components/floating-action-button/\">Floating Action Button</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
1 change: 1 addition & 0 deletions docs/translations/api-docs/fab/fab.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"disableRipple": "If <code>true</code>, the ripple effect is disabled.",
"href": "The URL to link to when the button is clicked. If defined, an <code>a</code> element will be used as the root node.",
"size": "The size of the component. <code>small</code> is equivalent to the dense button styling.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/basics/#the-sx-prop\">`sx` page</a> for more details.",
"variant": "The variant to use."
},
"classDescriptions": {
Expand Down
1 change: 1 addition & 0 deletions framer/scripts/framerConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export const componentSettings = {
'disableFocusRipple',
// FIXME: `Union`
'variant',
'sx',
],
propValues: {
icon: "'add'",
Expand Down
7 changes: 6 additions & 1 deletion packages/material-ui/src/Fab/Fab.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { OverridableStringUnion } from '@material-ui/types';
import { PropTypes } from '..';
import { SxProps } from '@material-ui/system';
import { PropTypes, Theme } from '..';
import { ExtendButtonBase, ExtendButtonBaseTypeMap } from '../ButtonBase';
import { OverrideProps } from '../OverridableComponent';

Expand Down Expand Up @@ -74,6 +75,10 @@ export type FabTypeMap<P = {}, D extends React.ElementType = 'button'> = ExtendB
* @default 'circular'
*/
variant?: OverridableStringUnion<FabVariantDefaults, FabPropsVariantOverrides>;
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
};
defaultComponent: D;
}>;
Expand Down
262 changes: 152 additions & 110 deletions packages/material-ui/src/Fab/Fab.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,56 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { useThemeVariants } from '@material-ui/styles';
import withStyles from '../styles/withStyles';
import { deepmerge } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import ButtonBase from '../ButtonBase';
import capitalize from '../utils/capitalize';
import useThemeProps from '../styles/useThemeProps';
import fabClasses, { getFabUtilityClass } from './fabClasses';
import experimentalStyled from '../styles/experimentalStyled';

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
const overridesResolver = (props, styles) => {
const { styleProps } = props;

return deepmerge(styles.root || {}, {
...styles[styleProps.variant],
...styles[`size${capitalize(styleProps.size)}`],
...(styleProps.color === 'inherit' && styles.colorInherit),
...(styleProps.color === 'primary' && styles.primary),
...(styleProps.color === 'secondary' && styles.secondary),
[`& .${fabClasses.label}`]: styles.label,
});
};

const useUtilityClasses = (styleProps) => {
const { color, variant, classes, size } = styleProps;

const slots = {
root: [
'root',
variant,
`size${capitalize(size)}`,
color === 'inherit' && 'colorInherit',
color === 'primary' && 'primary',
color === 'secondary' && 'secondary',
],
label: ['label'],
};

return composeClasses(slots, getFabUtilityClass, classes);
};

const FabRoot = experimentalStyled(
ButtonBase,
{},
{
name: 'MuiFab',
slot: 'Root',
overridesResolver,
},
)(
({ theme, styleProps }) => ({
/* Styles applied to the root element. */
...theme.typography.button,
minHeight: 36,
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color'], {
Expand All @@ -33,95 +75,101 @@ export const styles = (theme) => ({
},
textDecoration: 'none',
},
'&$focusVisible': {
'&.Mui-focusVisible': {
boxShadow: theme.shadows[6],
},
'&$disabled': {
'&.Mui-disabled': {
color: theme.palette.action.disabled,
boxShadow: theme.shadows[0],
backgroundColor: theme.palette.action.disabledBackground,
},
},
/* Styles applied to the span element that wraps the children. */
label: {
width: '100%', // assure the correct width for iOS Safari
display: 'inherit',
alignItems: 'inherit',
justifyContent: 'inherit',
},
/* Styles applied to the root element if `color="primary"`. */
primary: {
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.primary.main,
'&:hover': {
backgroundColor: theme.palette.primary.dark,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: theme.palette.primary.main,
/* Styles applied to the root element if `size="small"``. */
...(styleProps.size === 'small' && {
width: 40,
height: 40,
}),
/* Styles applied to the root element if `size="medium"``. */
...(styleProps.size === 'medium' && {
width: 48,
height: 48,
}),
/* Styles applied to the root element if `variant="extended"`. */
...(styleProps.variant === 'extended' && {
borderRadius: 48 / 2,
padding: '0 16px',
width: 'auto',
minHeight: 'auto',
minWidth: 48,
height: 48,
}),
...(styleProps.variant === 'extended' &&
styleProps.size === 'small' && {
width: 'auto',
padding: '0 8px',
borderRadius: 34 / 2,
minWidth: 34,
height: 34,
}),
...(styleProps.variant === 'extended' &&
styleProps.size === 'medium' && {
width: 'auto',
padding: '0 16px',
borderRadius: 40 / 2,
minWidth: 40,
height: 40,
}),
/* Styles applied to the root element if `color="inherit"`. */
...(styleProps.color === 'inherit' && {
color: 'inherit',
}),
}),
({ theme, styleProps }) => ({
/* Styles applied to the root element if `color="primary"`. */
...(styleProps.color === 'primary' && {
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.primary.main,
'&:hover': {
backgroundColor: theme.palette.primary.dark,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: theme.palette.primary.main,
},
},
},
},
/* Styles applied to the root element if `color="secondary"`. */
secondary: {
color: theme.palette.secondary.contrastText,
backgroundColor: theme.palette.secondary.main,
'&:hover': {
backgroundColor: theme.palette.secondary.dark,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: theme.palette.secondary.main,
}),
/* Styles applied to the root element if `color="secondary"`. */
...(styleProps.color === 'secondary' && {
color: theme.palette.secondary.contrastText,
backgroundColor: theme.palette.secondary.main,
'&:hover': {
backgroundColor: theme.palette.secondary.dark,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: theme.palette.secondary.main,
},
},
},
},
/* Styles applied to the root element if `variant="extended"`. */
extended: {
borderRadius: 48 / 2,
padding: '0 16px',
width: 'auto',
minHeight: 'auto',
minWidth: 48,
height: 48,
'&$sizeSmall': {
width: 'auto',
padding: '0 8px',
borderRadius: 34 / 2,
minWidth: 34,
height: 34,
},
'&$sizeMedium': {
width: 'auto',
padding: '0 16px',
borderRadius: 40 / 2,
minWidth: 40,
height: 40,
},
},
/* Styles applied to the root element if `variant="circular"`. */
circular: {},
/* Pseudo-class applied to the ButtonBase root element if the button is keyboard focused. */
focusVisible: {},
/* Pseudo-class applied to the root element if `disabled={true}`. */
disabled: {},
/* Styles applied to the root element if `color="inherit"`. */
colorInherit: {
color: 'inherit',
},
/* Styles applied to the root element if `size="small"``. */
sizeSmall: {
width: 40,
height: 40,
},
/* Styles applied to the root element if `size="medium"``. */
sizeMedium: {
width: 48,
height: 48,
}),
}),
);

const FabLabel = experimentalStyled(
'span',
{},
{
name: 'MuiFab',
slot: 'Label',
},
)({
/* Styles applied to the span element that wraps the children. */
width: '100%', // assure the correct width for iOS Safari
display: 'inherit',
alignItems: 'inherit',
justifyContent: 'inherit',
});

const Fab = React.forwardRef(function Fab(props, ref) {
const Fab = React.forwardRef(function Fab(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiFab' });
const {
children,
classes,
className,
color = 'default',
component = 'button',
Expand All @@ -133,43 +181,33 @@ const Fab = React.forwardRef(function Fab(props, ref) {
...other
} = props;

const themeVariantsClasses = useThemeVariants(
{
...props,
color,
component,
disabled,
disableFocusRipple,
size,
variant,
},
'MuiFab',
);
const styleProps = {
...props,
color,
component,
disabled,
disableFocusRipple,
size,
variant,
};

const classes = useUtilityClasses(styleProps);

return (
<ButtonBase
className={clsx(
classes.root,
classes[variant],
{
[classes.primary]: color === 'primary',
[classes.secondary]: color === 'secondary',
[classes[`size${capitalize(size)}`]]: size !== 'large',
[classes.disabled]: disabled,
[classes.colorInherit]: color === 'inherit',
},
themeVariantsClasses,
className,
)}
<FabRoot
className={clsx(classes.root, className)}
component={component}
disabled={disabled}
focusRipple={!disableFocusRipple}
focusVisibleClassName={clsx(classes.focusVisible, focusVisibleClassName)}
styleProps={styleProps}
ref={ref}
{...other}
>
<span className={classes.label}>{children}</span>
</ButtonBase>
<FabLabel className={classes.label} styleProps={styleProps}>
{children}
</FabLabel>
</FabRoot>
);
});

Expand Down Expand Up @@ -229,6 +267,10 @@ Fab.propTypes = {
* @default 'large'
*/
size: PropTypes.oneOf(['large', 'medium', 'small']),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* The variant to use.
* @default 'circular'
Expand All @@ -239,4 +281,4 @@ Fab.propTypes = {
]),
};

export default withStyles(styles, { name: 'MuiFab' })(Fab);
export default Fab;
Loading