Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Switch] Added outlined variant #25920

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/pages/api-docs/switch.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@
"default": "'medium'"
},
"sx": { "type": { "name": "object" } },
"value": { "type": { "name": "any" } }
"value": { "type": { "name": "any" } },
"variant": {
"type": { "name": "enum", "description": "'contained'<br>&#124;&nbsp;'normal'" },
"default": "'normal'"
}
},
"name": "Switch",
"styles": {
Expand Down
19 changes: 19 additions & 0 deletions docs/src/pages/components/switches/ContainedSwitches.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';
import Switch from '@material-ui/core/Switch';

const label = { inputProps: { 'aria-label': 'Switch demo' } };

export default function ContainedSwitches() {
return (
<div>
<Switch {...label} defaultChecked variant="contained" />
<Switch {...label} variant="contained" />
<Switch {...label} disabled defaultChecked variant="contained" />
<Switch {...label} disabled variant="contained" />
<Switch {...label} defaultChecked size="small" variant="contained" />
<Switch {...label} size="small" variant="contained" />
<Switch {...label} disabled defaultChecked size="small" variant="contained" />
<Switch {...label} disabled size="small" variant="contained" />
</div>
);
}
19 changes: 19 additions & 0 deletions docs/src/pages/components/switches/ContainedSwitches.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';
import Switch from '@material-ui/core/Switch';

const label = { inputProps: { 'aria-label': 'Switch demo' } };

export default function ContainedSwitches() {
return (
<div>
<Switch {...label} defaultChecked variant="contained" />
<Switch {...label} variant="contained" />
<Switch {...label} disabled defaultChecked variant="contained" />
<Switch {...label} disabled variant="contained" />
<Switch {...label} defaultChecked size="small" variant="contained" />
<Switch {...label} size="small" variant="contained" />
<Switch {...label} disabled defaultChecked size="small" variant="contained" />
<Switch {...label} disabled size="small" variant="contained" />
</div>
);
}
6 changes: 6 additions & 0 deletions docs/src/pages/components/switches/switches.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ should be made clear from the corresponding inline label.

{{"demo": "pages/components/switches/BasicSwitches.js"}}

## Variants

If you need an contained switch, use the `variant` prop.

{{"demo": "pages/components/switches/ContainedSwitches.js"}}

## Label

You can provide a label to the `Switch` thanks to the `FormControlLabel` component.
Expand Down
3 changes: 2 additions & 1 deletion docs/translations/api-docs/switch/switch.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"required": "If <code>true</code>, the <code>input</code> element is required.",
"size": "The size of the component. <code>small</code> is equivalent to the dense switch 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.",
"value": "The value of the component. The DOM API casts this to a string. The browser uses &quot;on&quot; as the default value."
"value": "The value of the component. The DOM API casts this to a string. The browser uses &quot;on&quot; as the default value.",
"variant": "The variant to use."
},
"classDescriptions": {
"root": { "description": "Styles applied to the root element." },
Expand Down
7 changes: 7 additions & 0 deletions framer/Material-UI.framerfx/code/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface Props {
checked: boolean;
defaultChecked?: boolean;
disabled: boolean;
variant: 'contained' | 'normal';
label: string;
width: number | string;
height: number;
Expand Down Expand Up @@ -43,6 +44,7 @@ export function Switch(props: Props) {
Switch.defaultProps = {
checked: false,
disabled: false,
variant: 'normal' as 'normal',
label: 'Switch',
width: 100,
height: 38,
Expand All @@ -61,6 +63,11 @@ addPropertyControls(Switch, {
type: ControlType.Boolean,
title: 'Disabled',
},
variant: {
type: ControlType.Enum,
title: 'Variant',
options: ['contained', 'normal'],
},
label: {
type: ControlType.String,
title: 'Label',
Expand Down
7 changes: 7 additions & 0 deletions packages/material-ui/src/Switch/Switch.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { OverridableStringUnion } from '@material-ui/types';
import { InternalStandardProps as StandardProps, Theme } from '..';
import { SwitchBaseProps } from '../internal/SwitchBase';

export interface SwitchPropsVariantOverrides {}

export interface SwitchPropsSizeOverrides {}

export interface SwitchPropsColorOverrides {}
Expand Down Expand Up @@ -73,6 +75,11 @@ export interface SwitchProps
* The browser uses "on" as the default value.
*/
value?: unknown;
/**
* The variant to use.
* @default 'normal'
*/
variant?: OverridableStringUnion<'normal' | 'contained', SwitchPropsVariantOverrides>;
}

export type SwitchClassKey = keyof NonNullable<SwitchProps['classes']>;
Expand Down
70 changes: 59 additions & 11 deletions packages/material-ui/src/Switch/Switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
import clsx from 'clsx';
import { refType } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import CheckIcon from '../internal/svg-icons/Check';
import MinusIcon from '../internal/svg-icons/Remove';
import { alpha, darken, lighten } from '../styles/colorManipulator';
import capitalize from '../utils/capitalize';
import SwitchBase from '../internal/SwitchBase';
Expand Down Expand Up @@ -57,7 +59,7 @@ const SwitchRoot = experimentalStyled(
width: 34 + 12 * 2,
height: 14 + 12 * 2,
overflow: 'hidden',
padding: 12,
padding: styleProps.variant === 'contained' ? 8 : 12,
boxSizing: 'border-box',
position: 'relative',
flexShrink: 0,
Expand All @@ -77,10 +79,10 @@ const SwitchRoot = experimentalStyled(
...(styleProps.size === 'small' && {
width: 40,
height: 24,
padding: 7,
padding: styleProps.variant === 'contained' ? 3 : 7,
[`& .${switchClasses.thumb}`]: {
width: 16,
height: 16,
width: styleProps.variant === 'contained' ? 12 : 16,
height: styleProps.variant === 'contained' ? 12 : 16,
},
[`& .${switchClasses.switchBase}`]: {
padding: 4,
Expand Down Expand Up @@ -172,11 +174,15 @@ const SwitchTrack = experimentalStyled(
slot: 'Track',
overridesResolver: (props, styles) => styles.track,
},
)(({ theme }) => ({
)(({ theme, styleProps }) => ({
/* Styles applied to the track element. */
height: '100%',
width: '100%',
borderRadius: 14 / 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 4,
borderRadius: styleProps.variant === 'contained' ? 14 : 14 / 2,
zIndex: -1,
transition: theme.transitions.create(['opacity', 'background-color'], {
duration: theme.transitions.duration.shortest,
Expand All @@ -186,6 +192,27 @@ const SwitchTrack = experimentalStyled(
opacity: theme.palette.mode === 'light' ? 0.38 : 0.3,
}));

const SwitchTrackIcons = experimentalStyled(
'div',
{},
{ name: 'MuiSwitch', slot: 'TrackIcons' },
)(({ styleProps }) => ({
/* Styles applied to the track icons element. */
position: 'absolute',
width: '100%',
height: '100%',
left: 0,
top: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: styleProps.size === 'small' ? '5px 4px' : 12,
opacity: styleProps.disabled ? 0.5 : 1,
[`& svg`]: {
height: '100%',
},
}));

const SwitchThumb = experimentalStyled(
'span',
{},
Expand All @@ -194,24 +221,34 @@ const SwitchThumb = experimentalStyled(
slot: 'Thumb',
overridesResolver: (props, styles) => styles.thumb,
},
)(({ theme }) => ({
)(({ theme, styleProps }) => ({
/* Styles used to create the thumb passed to the internal `SwitchBase` component `icon` prop. */
boxShadow: theme.shadows[1],
boxShadow: styleProps.variant === 'contained' ? 'none' : theme.shadows[1],
backgroundColor: 'currentColor',
width: 20,
height: 20,
width: styleProps.variant === 'contained' ? 16 : 20,
height: styleProps.variant === 'contained' ? 16 : 20,
margin: styleProps.variant === 'contained' ? 2 : undefined,
borderRadius: '50%',
}));

const Switch = React.forwardRef(function Switch(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiSwitch' });
const { className, color = 'secondary', edge = false, size = 'medium', sx, ...other } = props;
const {
className,
color = 'secondary',
edge = false,
size = 'medium',
sx,
variant = 'normal',
...other
} = props;

const styleProps = {
...props,
color,
edge,
size,
variant,
};

const classes = useUtilityClasses(styleProps);
Expand All @@ -232,6 +269,12 @@ const Switch = React.forwardRef(function Switch(inProps, ref) {
}}
/>
<SwitchTrack className={classes.track} styleProps={styleProps} />
{variant === 'contained' ? (
<SwitchTrackIcons className={classes.trackIcons} styleProps={styleProps}>
<CheckIcon fontSize="inherit" color="action" />
<MinusIcon fontSize="inherit" color="action" />
</SwitchTrackIcons>
) : null}
</SwitchRoot>
);
});
Expand Down Expand Up @@ -331,6 +374,11 @@ Switch.propTypes /* remove-proptypes */ = {
* The browser uses "on" as the default value.
*/
value: PropTypes.any,
/**
* The variant to use.
* @default 'normal'
*/
variant: PropTypes.oneOf(['contained', 'normal']),
};

export default Switch;
10 changes: 10 additions & 0 deletions packages/material-ui/src/internal/svg-icons/Check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as React from 'react';
import createSvgIcon from '../../utils/createSvgIcon';

/**
* @ignore - internal component.
*/
export default createSvgIcon(
<path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />,
'Check',
);
7 changes: 7 additions & 0 deletions packages/material-ui/src/internal/svg-icons/Remove.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as React from 'react';
import createSvgIcon from '../../utils/createSvgIcon';

/**
* @ignore - internal component.
*/
export default createSvgIcon(<path d="M19,13H5V11H19V13Z" />, 'Remove');