diff --git a/docs/pages/material-ui/api/button-group.json b/docs/pages/material-ui/api/button-group.json index ef1eb072c05e1b..a89d57e49e857b 100644 --- a/docs/pages/material-ui/api/button-group.json +++ b/docs/pages/material-ui/api/button-group.json @@ -54,6 +54,7 @@ "text", "disableElevation", "disabled", + "firstButton", "fullWidth", "vertical", "grouped", @@ -73,7 +74,9 @@ "groupedContainedHorizontal", "groupedContainedVertical", "groupedContainedPrimary", - "groupedContainedSecondary" + "groupedContainedSecondary", + "lastButton", + "middleButton" ], "globalClasses": { "disabled": "Mui-disabled" }, "name": "MuiButtonGroup" diff --git a/docs/translations/api-docs/button-group/button-group.json b/docs/translations/api-docs/button-group/button-group.json index 4f813acc0ba716..d2479a7bb3dee2 100644 --- a/docs/translations/api-docs/button-group/button-group.json +++ b/docs/translations/api-docs/button-group/button-group.json @@ -56,6 +56,10 @@ "nodeName": "the child elements", "conditions": "disabled={true}" }, + "firstButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the first button in the button group" + }, "fullWidth": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the root element", @@ -151,6 +155,14 @@ "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the children", "conditions": "variant=\"contained\" and color=\"secondary\"" + }, + "lastButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the last button in the button group" + }, + "middleButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "buttons in the middle of the button group" } } } diff --git a/packages/mui-material/src/Button/Button.js b/packages/mui-material/src/Button/Button.js index 5c1915b27e34c5..b287f9e603d545 100644 --- a/packages/mui-material/src/Button/Button.js +++ b/packages/mui-material/src/Button/Button.js @@ -296,17 +296,20 @@ const ButtonEndIcon = styled('span', { ...commonIconStyles(ownerState), })); -const addPositionDataAttributes = (buttonGroupButtonContext) => { - if (buttonGroupButtonContext.isFirstButton && buttonGroupButtonContext.isLastButton) { - return {}; +const addClassNameBasedOnPosition = (buttonGroupButtonContext) => { + if ( + buttonGroupButtonContext.firstButtonClassName && + buttonGroupButtonContext.lastButtonClassName + ) { + return ''; } - if (buttonGroupButtonContext.isFirstButton) { - return { 'data-first-child': '' }; + if (buttonGroupButtonContext.firstButtonClassName) { + return buttonGroupButtonContext.firstButtonClassName; } - if (buttonGroupButtonContext.isLastButton) { - return { 'data-last-child': '' }; + if (buttonGroupButtonContext.lastButtonClassName) { + return buttonGroupButtonContext.lastButtonClassName; } - return { 'data-middle-child': '' }; + return buttonGroupButtonContext.middleButtonClassName; }; const Button = React.forwardRef(function Button(inProps, ref) { @@ -360,22 +363,21 @@ const Button = React.forwardRef(function Button(inProps, ref) { ); - let dataAttributes = {}; + let positionClassName = ''; if (buttonGroupButtonContext) { - dataAttributes = addPositionDataAttributes(buttonGroupButtonContext); + positionClassName = addClassNameBasedOnPosition(buttonGroupButtonContext); } return ( diff --git a/packages/mui-material/src/ButtonGroup/ButtonGroup.js b/packages/mui-material/src/ButtonGroup/ButtonGroup.js index acb36d5a84ecd7..3046786bd3cb22 100644 --- a/packages/mui-material/src/ButtonGroup/ButtonGroup.js +++ b/packages/mui-material/src/ButtonGroup/ButtonGroup.js @@ -28,6 +28,15 @@ const overridesResolver = (props, styles) => { [`& .${buttonGroupClasses.grouped}`]: styles[`grouped${capitalize(ownerState.variant)}${capitalize(ownerState.color)}`], }, + { + [`& .${buttonGroupClasses.firstButton}`]: styles.firstButton, + }, + { + [`& .${buttonGroupClasses.lastButton}`]: styles.lastButton, + }, + { + [`& .${buttonGroupClasses.middleButton}`]: styles.middleButton, + }, styles.root, styles[ownerState.variant], ownerState.disableElevation === true && styles.disableElevation, @@ -56,6 +65,9 @@ const useUtilityClasses = (ownerState) => { `grouped${capitalize(variant)}${capitalize(color)}`, disabled && 'disabled', ], + firstButton: ['firstButton'], + lastButton: ['lastButton'], + middleButton: ['middleButton'], }; return composeClasses(slots, getButtonGroupUtilityClass, classes); @@ -82,106 +94,106 @@ const ButtonGroupRoot = styled('div', { }), [`& .${buttonGroupClasses.grouped}`]: { minWidth: 40, - '&[data-last-child],&[data-middle-child]': { - ...(ownerState.orientation === 'horizontal' && { - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }), - ...(ownerState.orientation === 'vertical' && { - borderTopRightRadius: 0, - borderTopLeftRadius: 0, + '&:hover': { + ...(ownerState.variant === 'contained' && { + boxShadow: 'none', }), - ...(ownerState.variant === 'outlined' && - ownerState.orientation === 'horizontal' && { - marginLeft: -1, - }), - ...(ownerState.variant === 'outlined' && - ownerState.orientation === 'vertical' && { - marginTop: -1, - }), }, - '&[data-first-child],&[data-middle-child]': { - ...(ownerState.orientation === 'horizontal' && { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, + ...(ownerState.variant === 'contained' && { + boxShadow: 'none', + }), + }, + [`& .${buttonGroupClasses.firstButton},& .${buttonGroupClasses.middleButton}`]: { + ...(ownerState.orientation === 'horizontal' && { + borderTopRightRadius: 0, + borderBottomRightRadius: 0, + }), + ...(ownerState.orientation === 'vertical' && { + borderBottomRightRadius: 0, + borderBottomLeftRadius: 0, + }), + ...(ownerState.variant === 'text' && + ownerState.orientation === 'horizontal' && { + borderRight: theme.vars + ? `1px solid rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.23)` + : `1px solid ${ + theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)' + }`, + [`&.${buttonGroupClasses.disabled}`]: { + borderRight: `1px solid ${(theme.vars || theme).palette.action.disabled}`, + }, }), - ...(ownerState.orientation === 'vertical' && { - borderBottomRightRadius: 0, - borderBottomLeftRadius: 0, + ...(ownerState.variant === 'text' && + ownerState.orientation === 'vertical' && { + borderBottom: theme.vars + ? `1px solid rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.23)` + : `1px solid ${ + theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)' + }`, + [`&.${buttonGroupClasses.disabled}`]: { + borderBottom: `1px solid ${(theme.vars || theme).palette.action.disabled}`, + }, }), - ...(ownerState.variant === 'text' && - ownerState.orientation === 'horizontal' && { - borderRight: theme.vars - ? `1px solid rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.23)` - : `1px solid ${ - theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)' - }`, - [`&.${buttonGroupClasses.disabled}`]: { - borderRight: `1px solid ${(theme.vars || theme).palette.action.disabled}`, - }, - }), - ...(ownerState.variant === 'text' && - ownerState.orientation === 'vertical' && { - borderBottom: theme.vars - ? `1px solid rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.23)` - : `1px solid ${ - theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)' - }`, - [`&.${buttonGroupClasses.disabled}`]: { - borderBottom: `1px solid ${(theme.vars || theme).palette.action.disabled}`, - }, - }), - ...(ownerState.variant === 'text' && - ownerState.color !== 'inherit' && { - borderColor: theme.vars - ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.5)` - : alpha(theme.palette[ownerState.color].main, 0.5), - }), + ...(ownerState.variant === 'text' && + ownerState.color !== 'inherit' && { + borderColor: theme.vars + ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.5)` + : alpha(theme.palette[ownerState.color].main, 0.5), + }), + ...(ownerState.variant === 'outlined' && + ownerState.orientation === 'horizontal' && { + borderRightColor: 'transparent', + }), + ...(ownerState.variant === 'outlined' && + ownerState.orientation === 'vertical' && { + borderBottomColor: 'transparent', + }), + ...(ownerState.variant === 'contained' && + ownerState.orientation === 'horizontal' && { + borderRight: `1px solid ${(theme.vars || theme).palette.grey[400]}`, + [`&.${buttonGroupClasses.disabled}`]: { + borderRight: `1px solid ${(theme.vars || theme).palette.action.disabled}`, + }, + }), + ...(ownerState.variant === 'contained' && + ownerState.orientation === 'vertical' && { + borderBottom: `1px solid ${(theme.vars || theme).palette.grey[400]}`, + [`&.${buttonGroupClasses.disabled}`]: { + borderBottom: `1px solid ${(theme.vars || theme).palette.action.disabled}`, + }, + }), + ...(ownerState.variant === 'contained' && + ownerState.color !== 'inherit' && { + borderColor: (theme.vars || theme).palette[ownerState.color].dark, + }), + '&:hover': { ...(ownerState.variant === 'outlined' && ownerState.orientation === 'horizontal' && { - borderRightColor: 'transparent', + borderRightColor: 'currentColor', }), ...(ownerState.variant === 'outlined' && ownerState.orientation === 'vertical' && { - borderBottomColor: 'transparent', - }), - ...(ownerState.variant === 'contained' && - ownerState.orientation === 'horizontal' && { - borderRight: `1px solid ${(theme.vars || theme).palette.grey[400]}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderRight: `1px solid ${(theme.vars || theme).palette.action.disabled}`, - }, - }), - ...(ownerState.variant === 'contained' && - ownerState.orientation === 'vertical' && { - borderBottom: `1px solid ${(theme.vars || theme).palette.grey[400]}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderBottom: `1px solid ${(theme.vars || theme).palette.action.disabled}`, - }, + borderBottomColor: 'currentColor', }), - ...(ownerState.variant === 'contained' && - ownerState.color !== 'inherit' && { - borderColor: (theme.vars || theme).palette[ownerState.color].dark, - }), - '&:hover': { - ...(ownerState.variant === 'outlined' && - ownerState.orientation === 'horizontal' && { - borderRightColor: 'currentColor', - }), - ...(ownerState.variant === 'outlined' && - ownerState.orientation === 'vertical' && { - borderBottomColor: 'currentColor', - }), - }, - }, - '&:hover': { - ...(ownerState.variant === 'contained' && { - boxShadow: 'none', - }), }, - ...(ownerState.variant === 'contained' && { - boxShadow: 'none', + }, + [`& .${buttonGroupClasses.lastButton},& .${buttonGroupClasses.middleButton}`]: { + ...(ownerState.orientation === 'horizontal' && { + borderTopLeftRadius: 0, + borderBottomLeftRadius: 0, }), + ...(ownerState.orientation === 'vertical' && { + borderTopRightRadius: 0, + borderTopLeftRadius: 0, + }), + ...(ownerState.variant === 'outlined' && + ownerState.orientation === 'horizontal' && { + marginLeft: -1, + }), + ...(ownerState.variant === 'outlined' && + ownerState.orientation === 'vertical' && { + marginTop: -1, + }), }, })); @@ -245,8 +257,10 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(inProps, ref) { ); const buttonGroupPositionContext = (index, childrenParam) => ({ - isFirstButton: index === 0, - isLastButton: index === React.Children.count(childrenParam) - 1, + firstButtonClassName: index === 0 ? classes.firstButton : '', + lastButtonClassName: + index === React.Children.count(childrenParam) - 1 ? classes.lastButton : '', + middleButtonClassName: classes.middleButton, }); return ( diff --git a/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts b/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts index 5d3b96c2da914b..dc5809a7c16cb5 100644 --- a/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts +++ b/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts @@ -1,8 +1,9 @@ import * as React from 'react'; interface ButtonGroupButtonContextType { - isFirstButton?: boolean; - isLastButton?: boolean; + firstButtonClassName?: string; + lastButtonClassName?: string; + middleButtonClassName?: string; } /** diff --git a/packages/mui-material/src/ButtonGroup/buttonGroupClasses.ts b/packages/mui-material/src/ButtonGroup/buttonGroupClasses.ts index 55dec11fdfe720..433083e3ae20f9 100644 --- a/packages/mui-material/src/ButtonGroup/buttonGroupClasses.ts +++ b/packages/mui-material/src/ButtonGroup/buttonGroupClasses.ts @@ -14,6 +14,8 @@ export interface ButtonGroupClasses { disableElevation: string; /** State class applied to the child elements if `disabled={true}`. */ disabled: string; + /** Styles applied to the first button in the button group. */ + firstButton: string; /** Styles applied to the root element if `fullWidth={true}`. */ fullWidth: string; /** Styles applied to the root element if `orientation="vertical"`. */ @@ -54,6 +56,10 @@ export interface ButtonGroupClasses { groupedContainedPrimary: string; /** Styles applied to the children if `variant="contained"` and `color="secondary"`. */ groupedContainedSecondary: string; + /** Styles applied to the last button in the button group. */ + lastButton: string; + /** Styles applied to buttons in the middle of the button group. */ + middleButton: string; } export type ButtonGroupClassKey = keyof ButtonGroupClasses; @@ -69,6 +75,7 @@ const buttonGroupClasses: ButtonGroupClasses = generateUtilityClasses('MuiButton 'text', 'disableElevation', 'disabled', + 'firstButton', 'fullWidth', 'vertical', 'grouped', @@ -89,6 +96,8 @@ const buttonGroupClasses: ButtonGroupClasses = generateUtilityClasses('MuiButton 'groupedContainedVertical', 'groupedContainedPrimary', 'groupedContainedSecondary', + 'lastButton', + 'middleButton', ]); export default buttonGroupClasses;