diff --git a/docs/pages/api-docs/switch.json b/docs/pages/api-docs/switch.json
index 2c9f0422029c13..1da7991e1ec1a6 100644
--- a/docs/pages/api-docs/switch.json
+++ b/docs/pages/api-docs/switch.json
@@ -34,7 +34,11 @@
"default": "'medium'"
},
"sx": { "type": { "name": "object" } },
- "value": { "type": { "name": "any" } }
+ "value": { "type": { "name": "any" } },
+ "variant": {
+ "type": { "name": "enum", "description": "'contained'
| 'normal'" },
+ "default": "'normal'"
+ }
},
"name": "Switch",
"styles": {
diff --git a/docs/src/pages/components/switches/ContainedSwitches.js b/docs/src/pages/components/switches/ContainedSwitches.js
new file mode 100644
index 00000000000000..247f58e2b1d4f7
--- /dev/null
+++ b/docs/src/pages/components/switches/ContainedSwitches.js
@@ -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 (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/docs/src/pages/components/switches/ContainedSwitches.tsx b/docs/src/pages/components/switches/ContainedSwitches.tsx
new file mode 100644
index 00000000000000..247f58e2b1d4f7
--- /dev/null
+++ b/docs/src/pages/components/switches/ContainedSwitches.tsx
@@ -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 (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/docs/src/pages/components/switches/switches.md b/docs/src/pages/components/switches/switches.md
index d22345fba2928d..810d6ab2601e23 100644
--- a/docs/src/pages/components/switches/switches.md
+++ b/docs/src/pages/components/switches/switches.md
@@ -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.
diff --git a/docs/translations/api-docs/switch/switch.json b/docs/translations/api-docs/switch/switch.json
index 29c49a1b0fc182..c8bd19506de975 100644
--- a/docs/translations/api-docs/switch/switch.json
+++ b/docs/translations/api-docs/switch/switch.json
@@ -17,7 +17,8 @@
"required": "If true
, the input
element is required.",
"size": "The size of the component. small
is equivalent to the dense switch styling.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.",
- "value": "The value of the component. The DOM API casts this to a string. The browser uses "on" as the default value."
+ "value": "The value of the component. The DOM API casts this to a string. The browser uses "on" as the default value.",
+ "variant": "The variant to use."
},
"classDescriptions": {
"root": { "description": "Styles applied to the root element." },
diff --git a/framer/Material-UI.framerfx/code/Switch.tsx b/framer/Material-UI.framerfx/code/Switch.tsx
index bb11971f3b7145..49e8c9c6f8219e 100644
--- a/framer/Material-UI.framerfx/code/Switch.tsx
+++ b/framer/Material-UI.framerfx/code/Switch.tsx
@@ -7,6 +7,7 @@ interface Props {
checked: boolean;
defaultChecked?: boolean;
disabled: boolean;
+ variant: 'contained' | 'normal';
label: string;
width: number | string;
height: number;
@@ -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,
@@ -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',
diff --git a/packages/material-ui/src/Switch/Switch.d.ts b/packages/material-ui/src/Switch/Switch.d.ts
index 99fb72bfdb132b..190e06a319ecfd 100644
--- a/packages/material-ui/src/Switch/Switch.d.ts
+++ b/packages/material-ui/src/Switch/Switch.d.ts
@@ -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 {}
@@ -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;
diff --git a/packages/material-ui/src/Switch/Switch.js b/packages/material-ui/src/Switch/Switch.js
index 6cfa60b0f3f6d5..5e8f4171f2b378 100644
--- a/packages/material-ui/src/Switch/Switch.js
+++ b/packages/material-ui/src/Switch/Switch.js
@@ -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';
@@ -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,
@@ -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,
@@ -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,
@@ -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',
{},
@@ -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);
@@ -232,6 +269,12 @@ const Switch = React.forwardRef(function Switch(inProps, ref) {
}}
/>
+ {variant === 'contained' ? (
+
+
+
+
+ ) : null}
);
});
@@ -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;
diff --git a/packages/material-ui/src/internal/svg-icons/Check.js b/packages/material-ui/src/internal/svg-icons/Check.js
new file mode 100644
index 00000000000000..697dc4f1199d2c
--- /dev/null
+++ b/packages/material-ui/src/internal/svg-icons/Check.js
@@ -0,0 +1,10 @@
+import * as React from 'react';
+import createSvgIcon from '../../utils/createSvgIcon';
+
+/**
+ * @ignore - internal component.
+ */
+export default createSvgIcon(
+ ,
+ 'Check',
+);
diff --git a/packages/material-ui/src/internal/svg-icons/Remove.js b/packages/material-ui/src/internal/svg-icons/Remove.js
new file mode 100644
index 00000000000000..b0ccb0e0d3d068
--- /dev/null
+++ b/packages/material-ui/src/internal/svg-icons/Remove.js
@@ -0,0 +1,7 @@
+import * as React from 'react';
+import createSvgIcon from '../../utils/createSvgIcon';
+
+/**
+ * @ignore - internal component.
+ */
+export default createSvgIcon(, 'Remove');