diff --git a/docs/data/slider-unstyled.json b/docs/data/slider-unstyled.json new file mode 100644 index 00000000000000..fce0b51669b0e1 --- /dev/null +++ b/docs/data/slider-unstyled.json @@ -0,0 +1,84 @@ +{ + "css": { + "root": { + "class": ".MuiSlider-root", + "description": "Class name applied to the root element." + }, + "colorPrimary": { + "class": ".MuiSlider-colorPrimary", + "description": "Class name applied to the root element if `color=\"primary\"`." + }, + "colorSecondary": { + "class": ".MuiSlider-colorSecondary", + "description": "Class name applied to the root element if `color=\"secondary\"`." + }, + "marked": { + "class": ".MuiSlider-marked", + "description": "Class name applied to the root element if `marks` is provided with at least one label." + }, + "vertical": { + "class": ".MuiSlider-vertical", + "description": "Class name applied to the root element if `orientation=\"vertical\"`." + }, + "disabled": { + "class": ".Mui-disabled", + "description": "Pseudo-class applied to the root and thumb element if `disabled={true}`." + }, + "rail": { + "class": ".MuiSlider-rail", + "description": "Class name applied to the rail element." + }, + "track": { + "class": ".MuiSlider-track", + "description": "Class name applied to the track element." + }, + "trackFalse": { + "class": ".MuiSlider-trackFalse", + "description": "Class name applied to the track element if `track={false}`." + }, + "trackInverted": { + "class": ".MuiSlider-trackInverted", + "description": "Class name applied to the track element if `track=\"inverted\"`." + }, + "thumb": { + "class": ".MuiSlider-thumb", + "description": "Class name applied to the thumb element." + }, + "thumbColorPrimary": { + "class": ".MuiSlider-thumbColorPrimary", + "description": "Class name applied to the thumb element if `color=\"primary\"`." + }, + "thumbColorSecondary": { + "class": ".MuiSlider-thumbColorSecondary", + "description": "Class name applied to the thumb element if `color=\"secondary\"`." + }, + "active": { + "class": ".MuiSlider-active", + "description": "Pseudo-class applied to the thumb element if it's active." + }, + "focusVisible": { + "class": ".Mui-focusVisible", + "description": "Pseudo-class applied to the thumb element if keyboard focused." + }, + "valueLabel": { + "class": ".MuiSlider-valueLabel", + "description": "Class name applied to the thumb label element." + }, + "mark": { + "class": ".MuiSlider-mark", + "description": "Class name applied to the mark element." + }, + "markActive": { + "class": ".MuiSlider-markActive", + "description": "Class name applied to the mark element if active (depending on the value)." + }, + "markLabel": { + "class": ".MuiSlider-markLabel", + "description": "Class name applied to the mark label element." + }, + "markLabelActive": { + "class": ".MuiSlider-markLabelActive", + "description": "Class name applied to the mark label element if active (depending on the value)." + } + } +} diff --git a/docs/pages/api-docs/slider-unstyled.md b/docs/pages/api-docs/slider-unstyled.md index 583f664a09a7d7..a221a13588f369 100644 --- a/docs/pages/api-docs/slider-unstyled.md +++ b/docs/pages/api-docs/slider-unstyled.md @@ -29,6 +29,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | aria-label | string | | The label of the slider. | | aria-labelledby | string | | The id of the element containing a label for the slider. | | aria-valuetext | string | | A string value that provides a user-friendly name for the current value of the slider. | +| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. | | color | 'primary'
| 'secondary'
| 'primary' | The color of the component. It supports those theme colors that make sense for this component. | | component | elementType | 'span' | The component used for the root node. Either a string to use a HTML element or a component. | | components | { Mark?: elementType, MarkLabel?: elementType, Rail?: elementType, Root?: elementType, Thumb?: elementType, Track?: elementType, ValueLabel?: elementType } | {} | The components used for each slot inside the Slider. Either a string to use a HTML element or a component. | @@ -56,6 +57,36 @@ The `ref` is forwarded to the root element. Any other props supplied will be provided to the root element (native element). +## CSS + +| Rule name | Global class | Description | +|:-----|:-------------|:------------| +| root | .root-1 | Class name applied to the root element. +| colorPrimary | .colorPrimary-2 | Class name applied to the root element if `color="primary"`. +| colorSecondary | .colorSecondary-3 | Class name applied to the root element if `color="secondary"`. +| marked | .marked-4 | Class name applied to the root element if `marks` is provided with at least one label. +| vertical | .vertical-5 | Class name applied to the root element if `orientation="vertical"`. +| disabled | .disabled-6 | Pseudo-class applied to the root and thumb element if `disabled={true}`. +| rail | .rail-7 | Class name applied to the rail element. +| track | .track-8 | Class name applied to the track element. +| trackFalse | .trackFalse-9 | Class name applied to the track element if `track={false}`. +| trackInverted | .trackInverted-10 | Class name applied to the track element if `track="inverted"`. +| thumb | .thumb-11 | Class name applied to the thumb element. +| thumbColorPrimary | .thumbColorPrimary-12 | Class name applied to the thumb element if `color="primary"`. +| thumbColorSecondary | .thumbColorSecondary-13 | Class name applied to the thumb element if `color="secondary"`. +| active | .active-14 | Pseudo-class applied to the thumb element if it's active. +| focusVisible | .focusVisible-15 | Pseudo-class applied to the thumb element if keyboard focused. +| valueLabel | .valueLabel-16 | Class name applied to the thumb label element. +| mark | .mark-17 | Class name applied to the mark element. +| markActive | .markActive-18 | Class name applied to the mark element if active (depending on the value). +| markLabel | .markLabel-19 | Class name applied to the mark label element. +| markLabelActive | .markLabelActive-20 | Class name applied to the mark label element if active (depending on the value). + +You can override the style of the component thanks to one of these customization points: + +- With a [global class name](/guides/interoperability/#global-css). +- With a rule name as part of the component's [`styleOverrides` property](/customization/components/#global-theme-override) in a custom theme. + ## Demos - [Slider](/components/slider/) diff --git a/docs/pages/api-docs/slider.md b/docs/pages/api-docs/slider.md index c6bddb4314aaa7..671ec19ad7bc80 100644 --- a/docs/pages/api-docs/slider.md +++ b/docs/pages/api-docs/slider.md @@ -31,6 +31,7 @@ The `MuiSlider` name can be used for providing [default props](/customization/gl | aria-label | string | | The label of the slider. | | aria-labelledby | string | | The id of the element containing a label for the slider. | | aria-valuetext | string | | A string value that provides a user-friendly name for the current value of the slider. | +| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. | | color | 'primary'
| 'secondary'
| | The color of the component. It supports those theme colors that make sense for this component. | | components | { Mark?: elementType, MarkLabel?: elementType, Rail?: elementType, Root?: elementType, Thumb?: elementType, Track?: elementType, ValueLabel?: elementType } | {} | The components used for each slot inside the Slider. Either a string to use a HTML element or a component. | | componentsProps | object | | The props used for each slot inside the Slider. | diff --git a/docs/src/modules/utils/generateMarkdown.ts b/docs/src/modules/utils/generateMarkdown.ts index 0e8da04b383ba1..c95019ed6f9c94 100644 --- a/docs/src/modules/utils/generateMarkdown.ts +++ b/docs/src/modules/utils/generateMarkdown.ts @@ -287,7 +287,11 @@ function generatePropType(type: PropTypeDescriptor): string | undefined { } function generateName(reactAPI: ReactApi) { - if (reactAPI.styles.classes.length && !reactAPI.styles.name) { + if ( + reactAPI.styles.classes.length && + !reactAPI.styles.name && + reactAPI.name.indexOf('Unstyled') === -1 + ) { throw new Error(`Missing styles name on ${reactAPI.name} component`); } @@ -445,7 +449,7 @@ function generateClasses(reactAPI: ReactApi, styledComponent: boolean) { return ''; } - if (!reactAPI.styles.name) { + if (!reactAPI.styles.name && reactAPI.name.indexOf('Unstyled') === -1) { throw new Error(`Missing styles name on ${reactAPI.name} component`); } diff --git a/docs/src/pages/components/slider/CustomizedSlider.js b/docs/src/pages/components/slider/CustomizedSlider.js index 108b5236a27b09..7fce104c4515c8 100644 --- a/docs/src/pages/components/slider/CustomizedSlider.js +++ b/docs/src/pages/components/slider/CustomizedSlider.js @@ -1,7 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import Slider, { SliderThumb } from '@material-ui/core/Slider'; import { experimentalStyled as styled } from '@material-ui/core/styles'; -import Slider from '@material-ui/core/Slider'; import Typography from '@material-ui/core/Typography'; import Tooltip from '@material-ui/core/Tooltip'; @@ -162,11 +162,11 @@ const AirbnbSlider = styled(Slider)({ function AirbnbThumbComponent(props) { return ( - + - + ); } diff --git a/docs/src/pages/components/slider/CustomizedSlider.tsx b/docs/src/pages/components/slider/CustomizedSlider.tsx index adf3374e20967a..8eb717bb020abb 100644 --- a/docs/src/pages/components/slider/CustomizedSlider.tsx +++ b/docs/src/pages/components/slider/CustomizedSlider.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; +import Slider, { SliderThumb } from '@material-ui/core/Slider'; import { experimentalStyled as styled } from '@material-ui/core/styles'; -import Slider from '@material-ui/core/Slider'; import Typography from '@material-ui/core/Typography'; import Tooltip from '@material-ui/core/Tooltip'; @@ -161,11 +161,11 @@ const AirbnbSlider = styled(Slider)({ function AirbnbThumbComponent(props: any) { return ( - + - + ); } diff --git a/docs/src/pages/components/slider/UnstyledSlider.js b/docs/src/pages/components/slider/UnstyledSlider.js index 8c93ea3f4c3a72..a33367f3c4b490 100644 --- a/docs/src/pages/components/slider/UnstyledSlider.js +++ b/docs/src/pages/components/slider/UnstyledSlider.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import { experimentalStyled as styled } from '@material-ui/core/styles'; +import { experimentalStyled as styled, alpha } from '@material-ui/core/styles'; import SliderUnstyled from '@material-ui/unstyled/SliderUnstyled'; import Box from '@material-ui/core/Box'; @@ -56,6 +56,15 @@ const StyledSlider = styled(SliderUnstyled)` right: -15px; bottom: -15px; } + + :hover, + &.Mui-focusVisible { + box-shadow: 0 0 0 8px ${alpha('#000', 0.16)}; + } + + &.Mui-active { + box-shadow: 0 0 0 14px ${alpha('#000', 0.16)}; + } } `; diff --git a/docs/src/pages/components/slider/UnstyledSlider.tsx b/docs/src/pages/components/slider/UnstyledSlider.tsx index 8c93ea3f4c3a72..a33367f3c4b490 100644 --- a/docs/src/pages/components/slider/UnstyledSlider.tsx +++ b/docs/src/pages/components/slider/UnstyledSlider.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { experimentalStyled as styled } from '@material-ui/core/styles'; +import { experimentalStyled as styled, alpha } from '@material-ui/core/styles'; import SliderUnstyled from '@material-ui/unstyled/SliderUnstyled'; import Box from '@material-ui/core/Box'; @@ -56,6 +56,15 @@ const StyledSlider = styled(SliderUnstyled)` right: -15px; bottom: -15px; } + + :hover, + &.Mui-focusVisible { + box-shadow: 0 0 0 8px ${alpha('#000', 0.16)}; + } + + &.Mui-active { + box-shadow: 0 0 0 14px ${alpha('#000', 0.16)}; + } } `; diff --git a/docs/src/pages/guides/migration-v4/migration-v4.md b/docs/src/pages/guides/migration-v4/migration-v4.md index 6af38d2e143dc9..2dab4bb855cd2d 100644 --- a/docs/src/pages/guides/migration-v4/migration-v4.md +++ b/docs/src/pages/guides/migration-v4/migration-v4.md @@ -751,44 +751,6 @@ const classes = makeStyles(theme => ({ + ``` -- The `classes` prop is no longer available for styling the component. You can fix this by one of these options: - - 1. Move these overrides to the theme's components overrides section - - ```diff - - const useStyles = makeStyles({ - - root: { - - margin: '10px', - - }, - - colorPrimary: { - - backgroundColor: '#232323', - - } - - }); - + const theme = createMuiTheme({ - + components: { - + MuiSlider: { - + styleOverrides: { - + root: { - + margin: '10px', - + }, - + colorPrimary: { - + backgroundColor: '#232323', - + }, - + }, - + }, - + }, - + }); - // ... - - const classes = useStyles(); - // ... - + - // ... - - - + - ``` - - or 2. you can follow one of the overrides approach described on the ([Style Library Interoperability](/guides/interoperability/)) page. The class names for the Slider component can be find on the [API page](/api/slider/#css). - ### Snackbar - The notification now displays at the bottom left on large screens. diff --git a/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.d.ts b/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.d.ts index 22c536085850dc..95c123362ae5d3 100644 --- a/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.d.ts +++ b/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.d.ts @@ -1,4 +1,4 @@ -import { OverridableComponent, OverridableTypeMap } from '../OverridableComponent'; +import { OverridableComponent, OverridableTypeMap, OverrideProps } from '../OverridableComponent'; export interface Mark { value: number; @@ -26,6 +26,51 @@ export interface SliderUnstyledTypeMap

; as: React.ElementType; + styleProps?: Omit['props'], 'components' | 'componentsProps'>; }; track?: { as?: React.ElementType; + styleProps?: Omit['props'], 'components' | 'componentsProps'>; }; rail?: { as?: React.ElementType; + styleProps?: Omit['props'], 'components' | 'componentsProps'>; }; thumb?: { as?: React.ElementType; + styleProps?: Omit['props'], 'components' | 'componentsProps'>; }; mark?: { as?: React.ElementType; + styleProps?: Omit['props'], 'components' | 'componentsProps'>; }; markLabel?: { as?: React.ElementType; + styleProps?: Omit['props'], 'components' | 'componentsProps'>; }; valueLabel?: { as?: React.ElementType; + styleProps?: Omit['props'], 'components' | 'componentsProps'>; }; }; /** @@ -219,4 +270,11 @@ export type ExtendSliderUnstyled = OverridableComp */ declare const SliderUnstyled: OverridableComponent; +export type SliderUnstyledProps< + D extends React.ElementType = SliderUnstyledTypeMap['defaultComponent'], + P = {} +> = OverrideProps, D>; + +export type SliderUnstyledClassKey = keyof NonNullable; + export default SliderUnstyled; diff --git a/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.js b/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.js index 3c84b47eacd6e3..17d204b9bb73eb 100644 --- a/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.js +++ b/packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.js @@ -149,24 +149,41 @@ const getUtilityClass = (name) => { }; const useSliderClasses = (props) => { - const { color, disabled, marked, orientation, track } = props; + const { color, disabled, marked, orientation, track, classes = {} } = props; const utilityClasses = { - root: clsx(getUtilityClass('root'), getUtilityClass(`color${capitalize(color)}`), { - 'Mui-disabled': disabled, - [getUtilityClass('marked')]: marked, - [getUtilityClass('vertical')]: orientation === 'vertical', - [getUtilityClass('trackInverted')]: track === 'inverted', - [getUtilityClass('trackFalse')]: track === false, - }), - rail: getUtilityClass('rail'), - track: getUtilityClass('track'), - mark: getUtilityClass('mark'), - markLabel: getUtilityClass('markLabel'), - valueLabel: getUtilityClass('valueLabel'), - thumb: clsx(getUtilityClass('thumb'), getUtilityClass(`thumbColor${capitalize(color)}`), { - 'Mui-disabled': disabled, - }), + root: clsx( + getUtilityClass('root'), + classes['root'], + getUtilityClass(`color${capitalize(color)}`), + classes[`color${capitalize(color)}`], + { + 'Mui-disabled': disabled, + [getUtilityClass('marked')]: marked, + [classes['marked']]: marked, + [getUtilityClass('vertical')]: orientation === 'vertical', + [classes['vertical']]: orientation === 'vertical', + [getUtilityClass('trackInverted')]: track === 'inverted', + [classes['trackInverted']]: track === 'inverted', + [getUtilityClass('trackFalse')]: track === false, + [classes['trackFalse']]: track === false, + }, + ), + rail: clsx(getUtilityClass('rail'), classes['rail']), + track: clsx(getUtilityClass('track'), classes['track']), + mark: clsx(getUtilityClass('mark'), classes['mark']), + markLabel: clsx(getUtilityClass('markLabel'), classes['markLabel']), + valueLabel: clsx(getUtilityClass('valueLabel'), classes['valueLabel']), + thumb: clsx( + getUtilityClass('thumb'), + classes['thumb'], + getUtilityClass(`thumbColor${capitalize(color)}`), + classes[`thumbColor${capitalize(color)}`], + { + 'Mui-disabled': disabled, + [getUtilityClass('vertical')]: orientation === 'vertical', + }, + ), }; return utilityClasses; @@ -599,6 +616,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) { // consider extracting to hook an reusing the lint rule for the varints const stateAndProps = { ...props, + classes: {}, color, disabled, max, @@ -627,9 +645,20 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) { {...other} className={clsx(utilityClasses.root, rootProps.className, className)} > - + @@ -658,6 +687,10 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) { = ExtendSliderUnstyledTypeMap<{ props: P & { + /** + * Override or extend the styles applied to the component. + */ + classes?: { + /** Class name applied to the root element. */ + root?: string; + /** Class name applied to the root element if `color="primary"`. */ + colorPrimary?: string; + /** Class name applied to the root element if `color="secondary"`. */ + colorSecondary?: string; + /** Class name applied to the root element if `marks` is provided with at least one label. */ + marked?: string; + /** Class name applied to the root element if `orientation="vertical"`. */ + vertical?: string; + /** Pseudo-class applied to the root and thumb element if `disabled={true}`. */ + disabled?: string; + /** Class name applied to the rail element. */ + rail?: string; + /** Class name applied to the track element. */ + track?: string; + /** Class name applied to the track element if `track={false}`. */ + trackFalse?: string; + /** Class name applied to the track element if `track="inverted"`. */ + trackInverted?: string; + /** Class name applied to the thumb element. */ + thumb?: string; + /** Class name applied to the thumb element if `color="primary"`. */ + thumbColorPrimary?: string; + /** Class name applied to the thumb element if `color="secondary"`. */ + thumbColorSecondary?: string; + /** Pseudo-class applied to the thumb element if it's active. */ + active?: string; + /** Pseudo-class applied to the thumb element if keyboard focused. */ + focusVisible?: string; + /** Class name applied to the thumb label element. */ + valueLabel?: string; + /** Class name applied to the mark element. */ + mark?: string; + /** Class name applied to the mark element if active (depending on the value). */ + markActive?: string; + /** Class name applied to the mark label element. */ + markLabel?: string; + /** Class name applied to the mark label element if active (depending on the value). */ + markLabelActive?: string; + }; /** * The system prop that allows defining system overrides as well as additional CSS styles. */ @@ -17,7 +62,20 @@ export type SliderTypeMap< }>; type SliderRootProps = NonNullable['root']; +type SliderMarkProps = NonNullable['mark']; +type SliderMarkLabelProps = NonNullable['markLabel']; +type SliderRailProps = NonNullable['rail']; +type SliderTrackProps = NonNullable['track']; +type SliderThumbProps = NonNullable['thumb']; +type SliderValueLabel = NonNullable['valueLabel']; + export const SliderRoot: React.FC; +export const SliderMark: React.FC; +export const SliderMarkLabel: React.FC; +export const SliderRail: React.FC; +export const SliderTrack: React.FC; +export const SliderThumb: React.FC; +export const SliderValueLabel: React.FC; /** * diff --git a/packages/material-ui/src/Slider/Slider.js b/packages/material-ui/src/Slider/Slider.js index 1c9ecba32bfe1f..1333b6825ef62f 100644 --- a/packages/material-ui/src/Slider/Slider.js +++ b/packages/material-ui/src/Slider/Slider.js @@ -1,9 +1,15 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { chainPropTypes } from '@material-ui/utils'; +import { + useThemeProps, + experimentalStyled, + alpha, + lighten, + darken, +} from '@material-ui/core/styles'; +import { capitalize } from '@material-ui/core/utils'; import SliderUnstyled, { SliderValueLabelUnstyled } from '@material-ui/unstyled/SliderUnstyled'; -import { useThemeProps, experimentalStyled, alpha, lighten, darken } from '../styles'; -import capitalize from '../utils/capitalize'; const overridesResolver = (props, styles, name) => { const { @@ -48,7 +54,7 @@ const overridesResolver = (props, styles, name) => { return styleOverrides; }; -const SliderRoot = experimentalStyled( +export const SliderRoot = experimentalStyled( 'span', {}, { muiName: 'MuiSlider', overridesResolver }, @@ -94,125 +100,138 @@ const SliderRoot = experimentalStyled( marginRight: 20, }), }), - '& .MuiSlider-rail': { - display: 'block', +})); + +export const SliderRail = experimentalStyled( + 'span', + {}, + { muiName: 'MuiSlider-rail' }, +)((props) => ({ + display: 'block', + position: 'absolute', + width: '100%', + height: 2, + borderRadius: 1, + backgroundColor: 'currentColor', + opacity: 0.38, + ...(props.styleProps.orientation === 'vertical' && { + height: '100%', + width: 2, + }), + ...(props.styleProps.track === 'inverted' && { + opacity: 1, + }), +})); + +export const SliderTrack = experimentalStyled( + 'span', + {}, + { muiName: 'MuiSlider-track' }, +)((props) => ({ + display: 'block', + position: 'absolute', + height: 2, + borderRadius: 1, + backgroundColor: 'currentColor', + ...(props.styleProps.orientation === 'vertical' && { + width: 2, + }), + ...(props.styleProps.track === false && { + display: 'none', + }), + ...(props.styleProps.track === 'inverted' && { + backgroundColor: + // Same logic as the LinearProgress track color + props.theme.palette.mode === 'light' + ? lighten(props.theme.palette.primary.main, 0.62) + : darken(props.theme.palette.primary.main, 0.5), + }), +})); + +export const SliderThumb = experimentalStyled( + 'span', + {}, + { muiName: 'MuiSlider-thumb' }, +)((props) => ({ + position: 'absolute', + width: 12, + height: 12, + marginLeft: -6, + marginTop: -5, + boxSizing: 'border-box', + borderRadius: '50%', + outline: 0, + backgroundColor: 'currentColor', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + transition: props.theme.transitions.create(['box-shadow'], { + duration: props.theme.transitions.duration.shortest, + }), + '&::after': { position: 'absolute', - width: '100%', - height: 2, - borderRadius: 1, - backgroundColor: 'currentColor', - opacity: 0.38, - ...(props.styleProps.orientation === 'vertical' && { - height: '100%', - width: 2, - }), - ...(props.styleProps.track === 'inverted' && { - opacity: 1, - }), + content: '""', + borderRadius: '50%', + // reach 42px hit target (2 * 15 + thumb diameter) + left: -15, + top: -15, + right: -15, + bottom: -15, }, - '& .MuiSlider-track': { - display: 'block', - position: 'absolute', - height: 2, - borderRadius: 1, - backgroundColor: 'currentColor', - ...(props.styleProps.orientation === 'vertical' && { - width: 2, - }), - ...(props.styleProps.track === false && { - display: 'none', - }), - ...(props.styleProps.track === 'inverted' && { - backgroundColor: - // Same logic as the LinearProgress track color - props.theme.palette.mode === 'light' - ? lighten(props.theme.palette.primary.main, 0.62) - : darken(props.theme.palette.primary.main, 0.5), - }), + '&:hover, &.Mui-focusVisible': { + boxShadow: `0px 0px 0px 8px ${alpha(props.theme.palette.primary.main, 0.16)}`, + '@media (hover: none)': { + boxShadow: 'none', + }, }, - '& .MuiSlider-thumb': { - position: 'absolute', - width: 12, - height: 12, - marginLeft: -6, - marginTop: -5, - boxSizing: 'border-box', - borderRadius: '50%', - outline: 0, - backgroundColor: 'currentColor', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - transition: props.theme.transitions.create(['box-shadow'], { - duration: props.theme.transitions.duration.shortest, - }), - '::after': { - position: 'absolute', - content: '""', - borderRadius: '50%', - // reach 42px hit target (2 * 15 + thumb diameter) - left: -15, - top: -15, - right: -15, - bottom: -15, + '&.Mui-active': { + boxShadow: `0px 0px 0px 14px ${alpha(props.theme.palette.primary.main, 0.16)}`, + }, + '&.Mui-disabled': { + width: 8, + height: 8, + marginLeft: -4, + marginTop: -3, + '&:hover': { + boxShadow: 'none', }, - ':hover, &.Mui-focusVisible': { - boxShadow: `0px 0px 0px 8px ${alpha(props.theme.palette.primary.main, 0.16)}`, - '@media (hover: none)': { - boxShadow: 'none', - }, + }, + '&.MuiSlider-vertical': { + marginLeft: -5, + marginBottom: -6, + }, + '&.MuiSlider-vertical&.Mui-disabled': { + marginLeft: -3, + marginBottom: -4, + }, + ...(props.styleProps.color === 'secondary' && { + '&:hover, &.Mui-focusVisible': { + boxShadow: `0px 0px 0px 8px ${alpha(props.theme.palette.secondary.main, 0.16)}`, }, '&.Mui-active': { - boxShadow: `0px 0px 0px 14px ${alpha(props.theme.palette.primary.main, 0.16)}`, + boxShadow: `0px 0px 0px 14px ${alpha(props.theme.palette.secondary.main, 0.16)}`, }, - '&.Mui-disabled': { - width: 8, - height: 8, - marginLeft: -4, - marginTop: -3, - ':hover': { - boxShadow: 'none', - }, - ...(props.styleProps.orientation === 'vertical' && { - marginLeft: -3, - marginBottom: -4, - }), + }), +})); + +export const SliderValueLabel = experimentalStyled(SliderValueLabelUnstyled)((props) => ({ + // IE 11 centering bug, to remove from the customization demos once no longer supported + left: 'calc(-50% - 4px)', + '&.MuiSlider-valueLabelOffset': { + '&.MuiSlider-valueLabelOpen': { + transform: 'scale(1) translateY(-10px)', }, - ...(props.styleProps.orientation === 'vertical' && { - marginLeft: -5, - marginBottom: -6, - }), - ...(props.styleProps.color === 'secondary' && { - ':hover': { - boxShadow: `0px 0px 0px 8px ${alpha(props.theme.palette.secondary.main, 0.16)}`, - }, - '&.Mui-focusVisible': { - boxShadow: `0px 0px 0px 8px ${alpha(props.theme.palette.secondary.main, 0.16)}`, - }, - '&.Mui-active': { - boxShadow: `0px 0px 0px 14px ${alpha(props.theme.palette.secondary.main, 0.16)}`, - }, + zIndex: 1, + ...props.theme.typography.body2, + fontSize: props.theme.typography.pxToRem(12), + lineHeight: 1.2, + transition: props.theme.transitions.create(['transform'], { + duration: props.theme.transitions.duration.shortest, }), - }, - '& .MuiSlider-valueLabel': { - // IE11 centering bug, to remove from the customization demos once no longer supported - left: 'calc(-50% - 4px)', - '&.MuiSlider-valueLabelOffset': { - '&.MuiSlider-valueLabelOpen': { - transform: 'scale(1) translateY(-10px)', - }, - zIndex: 1, - ...props.theme.typography.body2, - fontSize: props.theme.typography.pxToRem(12), - lineHeight: 1.2, - transition: props.theme.transitions.create(['transform'], { - duration: props.theme.transitions.duration.shortest, - }), - top: -34, - transformOrigin: 'bottom center', - transform: 'scale(0)', - position: 'absolute', - }, + top: -34, + transformOrigin: 'bottom center', + transform: 'scale(0)', + position: 'absolute', }, '& .MuiSlider-valueLabelCircle': { display: 'flex', @@ -229,42 +248,98 @@ const SliderRoot = experimentalStyled( transform: 'rotate(45deg)', textAlign: 'center', }, - '& .MuiSlider-mark': { - position: 'absolute', - width: 2, - height: 2, - borderRadius: 1, - backgroundColor: 'currentColor', - '&.MuiSlider-markActive': { - backgroundColor: props.theme.palette.background.paper, - opacity: 0.8, - }, +})); + +export const SliderMark = experimentalStyled( + 'span', + {}, + { muiName: 'MuiSlider-mark' }, +)((props) => ({ + position: 'absolute', + width: 2, + height: 2, + borderRadius: 1, + backgroundColor: 'currentColor', + '&.MuiSlider-markActive': { + backgroundColor: props.theme.palette.background.paper, + opacity: 0.8, }, - '& .MuiSlider-markLabel': { - ...props.theme.typography.body2, - color: props.theme.palette.text.secondary, - position: 'absolute', - top: 26, - transform: 'translateX(-50%)', - whiteSpace: 'nowrap', +})); + +export const SliderMarkLabel = experimentalStyled( + 'span', + {}, + { muiName: 'MuiSlider-markLabel' }, +)((props) => ({ + ...props.theme.typography.body2, + color: props.theme.palette.text.secondary, + position: 'absolute', + top: 26, + transform: 'translateX(-50%)', + whiteSpace: 'nowrap', + ...(props.styleProps.orientation === 'vertical' && { + top: 'auto', + left: 26, + transform: 'translateY(50%)', + }), + '@media (pointer: coarse)': { + top: 40, ...(props.styleProps.orientation === 'vertical' && { - top: 'auto', - left: 26, - transform: 'translateY(50%)', + left: 31, }), - '@media (pointer: coarse)': { - top: 40, - ...(props.styleProps.orientation === 'vertical' && { - top: 'auto', - left: 31, - }), - }, - '&.MuiSlider-markLabelActive': { - color: props.theme.palette.text.primary, - }, + }, + '&.MuiSlider-markLabelActive': { + color: props.theme.palette.text.primary, }, })); +SliderRoot.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the d.ts file and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * @ignore + */ + children: PropTypes.node, + /** + * @ignore + */ + styleProps: PropTypes.shape({ + 'aria-label': PropTypes.string, + 'aria-labelledby': PropTypes.string, + 'aria-valuetext': PropTypes.string, + classes: PropTypes.object, + color: PropTypes.oneOf(['primary', 'secondary']), + defaultValue: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), + disabled: PropTypes.bool, + getAriaLabel: PropTypes.func, + getAriaValueText: PropTypes.func, + isRtl: PropTypes.bool, + marks: PropTypes.oneOfType([ + PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.node, + value: PropTypes.number.isRequired, + }), + ), + PropTypes.bool, + ]), + max: PropTypes.number, + min: PropTypes.number, + name: PropTypes.string, + onChange: PropTypes.func, + onChangeCommitted: PropTypes.func, + orientation: PropTypes.oneOf(['horizontal', 'vertical']), + scale: PropTypes.func, + step: PropTypes.number, + track: PropTypes.oneOf(['inverted', 'normal', false]), + value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), + valueLabelDisplay: PropTypes.oneOf(['auto', 'off', 'on']), + valueLabelFormat: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + }), +}; + const Slider = React.forwardRef(function Slider(inputProps, ref) { const props = useThemeProps({ props: inputProps, name: 'MuiSlider' }); const { components = {}, ...other } = props; @@ -273,7 +348,12 @@ const Slider = React.forwardRef(function Slider(inputProps, ref) { {...other} components={{ Root: SliderRoot, - ValueLabel: SliderValueLabelUnstyled, + Rail: SliderRail, + Track: SliderTrack, + Thumb: SliderThumb, + ValueLabel: SliderValueLabel, + Mark: SliderMark, + MarkLabel: SliderMarkLabel, ...components, }} ref={ref} @@ -322,6 +402,10 @@ Slider.propTypes = { * @ignore */ children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, /** * The color of the component. It supports those theme colors that make sense for this component. * @default 'primary' diff --git a/packages/material-ui/src/Slider/index.js b/packages/material-ui/src/Slider/index.js index 9898d6a85d1d01..006f966fe2404f 100644 --- a/packages/material-ui/src/Slider/index.js +++ b/packages/material-ui/src/Slider/index.js @@ -1 +1,2 @@ export { default } from './Slider'; +export * from './Slider';