diff --git a/docs/data/base/components/slider/CustomSubcomponents.js b/docs/data/base/components/slider/CustomSubcomponents.js new file mode 100644 index 0000000000..111e80079b --- /dev/null +++ b/docs/data/base/components/slider/CustomSubcomponents.js @@ -0,0 +1,301 @@ +import * as React from 'react'; +import { useTheme } from '@mui/system'; +import * as Slider from '@base_ui/react/Slider'; +import * as Tooltip from '@base_ui/react/Tooltip'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function CustomSubcomponents() { + const [valueLabelOpen, setValueLabelOpen] = React.useState(false); + + const handleGlobalPointerUp = () => { + if (valueLabelOpen) { + setValueLabelOpen(false); + } + }; + + return ( +
+ + + + {Array.from(Array(10), (_, x) => x).map((v) => { + return ; + })} + + { + if (!valueLabelOpen) { + setValueLabelOpen(true); + } + }} + onBlur={() => { + if (valueLabelOpen) { + setValueLabelOpen(false); + } + }} + onPointerOver={() => { + if (!valueLabelOpen) { + setValueLabelOpen(true); + } + }} + onPointerLeave={(event) => { + if (event.buttons !== 1) { + setValueLabelOpen(false); + } else { + document.addEventListener('pointerup', handleGlobalPointerUp, { + once: true, + }); + } + }} + > + + + + + + + + + + + + +
+ ); +} + +const SliderMark = React.forwardRef(function SliderMark(props, ref) { + const { index, style, ...otherProps } = props; + const { percentageValues } = Slider.useSliderContext(); + const isFilled = percentageValues[0] >= index * 10; + return ( + + ); +}); + +const SliderTrackFill = React.forwardRef(function SliderTrackFill(props, ref) { + const { style, ...otherProps } = props; + + const { axis, disabled, isRtl, orientation, percentageValues } = + Slider.useSliderContext(); + + const isRange = percentageValues.length > 1; + + let internalStyles; + + if (isRange) { + const trackOffset = percentageValues[0]; + const trackLeap = percentageValues[percentageValues.length - 1] - trackOffset; + internalStyles = getRangeStyles(axis, trackOffset, trackLeap); + } else if (orientation === 'vertical') { + internalStyles = { + bottom: 0, + height: `${percentageValues[0]}%`, + }; + } else { + internalStyles = { + width: `${percentageValues[0]}%`, + [isRtl ? 'right' : 'left']: 0, + }; + } + + return ( + + ); +}); + +function getRangeStyles(axis, trackOffset, trackLeap) { + const axisProps = { + horizontal: { + offset: (percent) => ({ left: `${percent}%` }), + leap: (percent) => ({ width: `${percent}%` }), + }, + 'horizontal-reverse': { + offset: (percent) => ({ right: `${percent}%` }), + leap: (percent) => ({ width: `${percent}%` }), + }, + vertical: { + offset: (percent) => ({ bottom: `${percent}%` }), + leap: (percent) => ({ height: `${percent}%` }), + }, + }; + + return { + ...axisProps[axis].offset(trackOffset), + ...axisProps[axis].leap(trackLeap), + }; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + return ( + + ); +} diff --git a/docs/data/base/components/slider/CustomSubcomponents.tsx b/docs/data/base/components/slider/CustomSubcomponents.tsx new file mode 100644 index 0000000000..4e576425c9 --- /dev/null +++ b/docs/data/base/components/slider/CustomSubcomponents.tsx @@ -0,0 +1,314 @@ +import * as React from 'react'; +import { useTheme } from '@mui/system'; +import * as Slider from '@base_ui/react/Slider'; +import * as Tooltip from '@base_ui/react/Tooltip'; +import { type Axis } from '@base_ui/react/Slider'; + +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +export default function CustomSubcomponents() { + const [valueLabelOpen, setValueLabelOpen] = React.useState(false); + + const handleGlobalPointerUp = () => { + if (valueLabelOpen) { + setValueLabelOpen(false); + } + }; + + return ( +
+ + + + {Array.from(Array(10), (_, x) => x).map((v) => { + return ; + })} + + { + if (!valueLabelOpen) { + setValueLabelOpen(true); + } + }} + onBlur={() => { + if (valueLabelOpen) { + setValueLabelOpen(false); + } + }} + onPointerOver={() => { + if (!valueLabelOpen) { + setValueLabelOpen(true); + } + }} + onPointerLeave={(event) => { + if (event.buttons !== 1) { + setValueLabelOpen(false); + } else { + document.addEventListener('pointerup', handleGlobalPointerUp, { + once: true, + }); + } + }} + > + + + + + + + + + + + + +
+ ); +} + +const SliderMark = React.forwardRef(function SliderMark( + props: any, + ref: React.ForwardedRef, +) { + const { index, style, ...otherProps } = props; + const { percentageValues } = Slider.useSliderContext(); + const isFilled = percentageValues[0] >= index * 10; + return ( + + ); +}); + +const SliderTrackFill = React.forwardRef(function SliderTrackFill( + props: any, + ref: React.ForwardedRef, +) { + const { style, ...otherProps } = props; + + const { axis, disabled, isRtl, orientation, percentageValues } = + Slider.useSliderContext(); + + const isRange = percentageValues.length > 1; + + let internalStyles; + + if (isRange) { + const trackOffset = percentageValues[0]; + const trackLeap = percentageValues[percentageValues.length - 1] - trackOffset; + internalStyles = getRangeStyles(axis, trackOffset, trackLeap); + } else if (orientation === 'vertical') { + internalStyles = { + bottom: 0, + height: `${percentageValues[0]}%`, + }; + } else { + internalStyles = { + width: `${percentageValues[0]}%`, + [isRtl ? 'right' : 'left']: 0, + }; + } + + return ( + + ); +}); + +function getRangeStyles(axis: Axis, trackOffset: number, trackLeap: number) { + const axisProps: Record< + Axis, + { + offset: (percent: number) => Record; + leap: (percent: number) => Record; + } + > = { + horizontal: { + offset: (percent: number) => ({ left: `${percent}%` }), + leap: (percent: number) => ({ width: `${percent}%` }), + }, + 'horizontal-reverse': { + offset: (percent: number) => ({ right: `${percent}%` }), + leap: (percent: number) => ({ width: `${percent}%` }), + }, + vertical: { + offset: (percent: number) => ({ bottom: `${percent}%` }), + leap: (percent: number) => ({ height: `${percent}%` }), + }, + }; + + return { + ...axisProps[axis].offset(trackOffset), + ...axisProps[axis].leap(trackLeap), + }; +} + +function Styles() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); + return ( + + ); +} diff --git a/docs/data/base/components/slider/DiscreteSlider.js b/docs/data/base/components/slider/DiscreteSlider.js deleted file mode 100644 index 77113b3adc..0000000000 --- a/docs/data/base/components/slider/DiscreteSlider.js +++ /dev/null @@ -1,163 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function DiscreteSlider() { - return ( - - - - ); -} - -function SliderValueLabel({ children }) { - return {children}; -} - -SliderValueLabel.propTypes = { - children: PropTypes.element.isRequired, -}; - -function valuetext(value) { - return `${value}°C`; -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .${sliderClasses.mark} { - position: absolute; - width: 10px; - height: 10px; - border-radius: 99%; - background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[700]}; - transform: translateX(-50%); - } - - & .${sliderClasses.markActive} { - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - } - - & .valueLabel { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 12px; - position: relative; - top: -2em; - text-align: center; - align-self: center; - } -`, -); diff --git a/docs/data/base/components/slider/DiscreteSlider.tsx b/docs/data/base/components/slider/DiscreteSlider.tsx deleted file mode 100644 index 54d3605561..0000000000 --- a/docs/data/base/components/slider/DiscreteSlider.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import * as React from 'react'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function DiscreteSlider() { - return ( - - - - ); -} - -interface SliderValueLabelProps { - children: React.ReactElement; -} - -function SliderValueLabel({ children }: SliderValueLabelProps) { - return {children}; -} - -function valuetext(value: number) { - return `${value}°C`; -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .${sliderClasses.mark} { - position: absolute; - width: 10px; - height: 10px; - border-radius: 99%; - background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[700]}; - transform: translateX(-50%); - } - - & .${sliderClasses.markActive} { - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - } - - & .valueLabel { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 12px; - position: relative; - top: -2em; - text-align: center; - align-self: center; - } -`, -); diff --git a/docs/data/base/components/slider/DiscreteSlider.tsx.preview b/docs/data/base/components/slider/DiscreteSlider.tsx.preview deleted file mode 100644 index 1d0ad0d7b2..0000000000 --- a/docs/data/base/components/slider/DiscreteSlider.tsx.preview +++ /dev/null @@ -1,11 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/base/components/slider/DiscreteSliderMarks.js b/docs/data/base/components/slider/DiscreteSliderMarks.js deleted file mode 100644 index a1ea9df512..0000000000 --- a/docs/data/base/components/slider/DiscreteSliderMarks.js +++ /dev/null @@ -1,168 +0,0 @@ -import * as React from 'react'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function DiscreteSliderMarks() { - return ( - - - - ); -} - -const marks = [ - { - value: 0, - label: '0°C', - }, - { - value: 20, - label: '20°C', - }, - { - value: 37, - label: '37°C', - }, - { - value: 100, - label: '100°C', - }, -]; - -function valuetext(value) { - return `${value}°C`; -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .${sliderClasses.mark} { - position: absolute; - width: 10px; - height: 10px; - border-radius: 99%; - background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]}; - transform: translateX(-50%); - } - - & .${sliderClasses.markActive} { - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - } - - & .${sliderClasses.markLabel} { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 12px; - position: absolute; - top: 24px; - transform: translateX(-50%); - margin-top: 8px; - } -`, -); diff --git a/docs/data/base/components/slider/DiscreteSliderMarks.tsx b/docs/data/base/components/slider/DiscreteSliderMarks.tsx deleted file mode 100644 index 973e349951..0000000000 --- a/docs/data/base/components/slider/DiscreteSliderMarks.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import * as React from 'react'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function DiscreteSliderMarks() { - return ( - - - - ); -} - -const marks = [ - { - value: 0, - label: '0°C', - }, - { - value: 20, - label: '20°C', - }, - { - value: 37, - label: '37°C', - }, - { - value: 100, - label: '100°C', - }, -]; - -function valuetext(value: number) { - return `${value}°C`; -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .${sliderClasses.mark} { - position: absolute; - width: 10px; - height: 10px; - border-radius: 99%; - background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]}; - transform: translateX(-50%); - } - - & .${sliderClasses.markActive} { - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - } - - & .${sliderClasses.markLabel} { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 12px; - position: absolute; - top: 24px; - transform: translateX(-50%); - margin-top: 8px; - } -`, -); diff --git a/docs/data/base/components/slider/DiscreteSliderMarks.tsx.preview b/docs/data/base/components/slider/DiscreteSliderMarks.tsx.preview deleted file mode 100644 index 32bef2b878..0000000000 --- a/docs/data/base/components/slider/DiscreteSliderMarks.tsx.preview +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/base/components/slider/DiscreteSliderValues.js b/docs/data/base/components/slider/DiscreteSliderValues.js deleted file mode 100644 index ac0cb96228..0000000000 --- a/docs/data/base/components/slider/DiscreteSliderValues.js +++ /dev/null @@ -1,169 +0,0 @@ -import * as React from 'react'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function DiscreteSliderValues() { - return ( - - - - ); -} - -const marks = [ - { - value: 0, - label: '0°C', - }, - { - value: 20, - label: '20°C', - }, - { - value: 37, - label: '37°C', - }, - { - value: 100, - label: '100°C', - }, -]; - -function valuetext(value) { - return `${value}°C`; -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .${sliderClasses.mark} { - position: absolute; - width: 10px; - height: 10px; - border-radius: 99%; - background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]}; - transform: translateX(-50%); - } - - & .${sliderClasses.markActive} { - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - } - - & .${sliderClasses.markLabel} { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 12px; - position: absolute; - top: 24px; - transform: translateX(-50%); - margin-top: 8px; - } -`, -); diff --git a/docs/data/base/components/slider/DiscreteSliderValues.tsx b/docs/data/base/components/slider/DiscreteSliderValues.tsx deleted file mode 100644 index 3da423aaee..0000000000 --- a/docs/data/base/components/slider/DiscreteSliderValues.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import * as React from 'react'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function DiscreteSliderValues() { - return ( - - - - ); -} - -const marks = [ - { - value: 0, - label: '0°C', - }, - { - value: 20, - label: '20°C', - }, - { - value: 37, - label: '37°C', - }, - { - value: 100, - label: '100°C', - }, -]; - -function valuetext(value: number) { - return `${value}°C`; -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .${sliderClasses.mark} { - position: absolute; - width: 10px; - height: 10px; - border-radius: 99%; - background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]}; - transform: translateX(-50%); - } - - & .${sliderClasses.markActive} { - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - } - - & .${sliderClasses.markLabel} { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 12px; - position: absolute; - top: 24px; - transform: translateX(-50%); - margin-top: 8px; - } -`, -); diff --git a/docs/data/base/components/slider/DiscreteSliderValues.tsx.preview b/docs/data/base/components/slider/DiscreteSliderValues.tsx.preview deleted file mode 100644 index f5e963865b..0000000000 --- a/docs/data/base/components/slider/DiscreteSliderValues.tsx.preview +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/base/components/slider/LabeledValuesSlider.js b/docs/data/base/components/slider/LabeledValuesSlider.js deleted file mode 100644 index 17c2fd45ff..0000000000 --- a/docs/data/base/components/slider/LabeledValuesSlider.js +++ /dev/null @@ -1,161 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function LabeledValuesSlider() { - return ( - - - - ); -} - -function SliderValueLabel({ children }) { - return ( - -
{children}
-
- ); -} - -SliderValueLabel.propTypes = { - children: PropTypes.element.isRequired, -}; - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .label { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 14px; - background: unset; - background-color: ${theme.palette.mode === 'light' ? blue[600] : blue[900]}; - width: 32px; - height: 32px; - padding: 0px; - visibility: hidden; - color: #fff; - border-radius: 50% 50% 50% 0; - position: absolute; - transform: translate(0%, -140%) rotate(-45deg) scale(0); - transition: transform 0.2s ease; - display: flex; - align-items: center; - justify-content: center; - } - - :hover .label { - visibility: visible; - transform: translate(0%, -140%) rotate(-45deg) scale(1); - } - - :hover .value { - transform: rotate(45deg); - text-align: center; - } - } -`, -); diff --git a/docs/data/base/components/slider/LabeledValuesSlider.tsx b/docs/data/base/components/slider/LabeledValuesSlider.tsx deleted file mode 100644 index fe5a7462a8..0000000000 --- a/docs/data/base/components/slider/LabeledValuesSlider.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import * as React from 'react'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; - -export default function LabeledValuesSlider() { - return ( - - - - ); -} - -interface SliderValueLabelProps { - children: React.ReactElement; -} - -function SliderValueLabel({ children }: SliderValueLabelProps) { - return ( - -
{children}
-
- ); -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - -const grey = { - 50: '#F3F6F9', - 100: '#E5EAF2', - 200: '#DAE2ED', - 300: '#C7D0DD', - 400: '#B0B8C4', - 500: '#9DA8B7', - 600: '#6B7A90', - 700: '#434D5B', - 800: '#303740', - 900: '#1C2025', -}; - -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; - width: 100%; - padding: 16px 0; - display: inline-flex; - align-items: center; - position: relative; - cursor: pointer; - touch-action: none; - -webkit-tap-highlight-color: transparent; - - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; - } - - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; - } - - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; - } - - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } - } - - & .label { - font-family: IBM Plex Sans; - font-weight: 600; - font-size: 14px; - background: unset; - background-color: ${theme.palette.mode === 'light' ? blue[600] : blue[900]}; - width: 32px; - height: 32px; - padding: 0px; - visibility: hidden; - color: #fff; - border-radius: 50% 50% 50% 0; - position: absolute; - transform: translate(0%, -140%) rotate(-45deg) scale(0); - transition: transform 0.2s ease; - display: flex; - align-items: center; - justify-content: center; - } - - :hover .label { - visibility: visible; - transform: translate(0%, -140%) rotate(-45deg) scale(1); - } - - :hover .value { - transform: rotate(45deg); - text-align: center; - } - } -`, -); diff --git a/docs/data/base/components/slider/LabeledValuesSlider.tsx.preview b/docs/data/base/components/slider/LabeledValuesSlider.tsx.preview deleted file mode 100644 index ab77eaafc1..0000000000 --- a/docs/data/base/components/slider/LabeledValuesSlider.tsx.preview +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/base/components/slider/RangeSlider.js b/docs/data/base/components/slider/RangeSlider.js index dc8b7d413e..2fcc7926fe 100644 --- a/docs/data/base/components/slider/RangeSlider.js +++ b/docs/data/base/components/slider/RangeSlider.js @@ -1,52 +1,43 @@ import * as React from 'react'; -import { styled, alpha, Box } from '@mui/system'; -import { Slider as BaseSlider, sliderClasses } from '@base_ui/react/v5_Slider'; +import { styled, useTheme, Box } from '@mui/system'; +import * as BaseSlider from '@base_ui/react/Slider'; export default function RangeSlider() { + // Replace this with your app logic for determining dark mode + const isDarkMode = useIsDarkMode(); const [value, setValue] = React.useState([20, 37]); - const handleChange = (event, newValue) => { - setValue(newValue); - }; - return ( - + {/* controlled: */} 'Temperature range'} - getAriaValueText={valuetext} - min={0} - max={100} - /> + onValueChange={setValue} + aria-labelledby="ControlledRangeLabel" + > + + + + + + + {/* uncontrolled: */} - 'Temperature range'} - getAriaValueText={valuetext} - min={0} - max={100} - /> + + + + + + + + ); } -function valuetext(value) { - return `${value}°C`; -} - -const blue = { - 100: '#DAECFF', - 200: '#99CCF3', - 400: '#3399FF', - 300: '#66B2FF', - 500: '#007FFF', - 600: '#0072E5', - 700: '#0059B3', - 900: '#003A75', -}; - const grey = { 50: '#F3F6F9', 100: '#E5EAF2', @@ -60,98 +51,102 @@ const grey = { 900: '#1C2025', }; -const Slider = styled(BaseSlider)( - ({ theme }) => ` - color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - height: 6px; +function useIsDarkMode() { + const theme = useTheme(); + return theme.palette.mode === 'dark'; +} + +const Slider = styled(BaseSlider.Root)` + font-family: 'IBM Plex Sans', sans-serif; + font-size: 1rem; width: 100%; - padding: 16px 0; - display: inline-flex; align-items: center; position: relative; - cursor: pointer; - touch-action: none; -webkit-tap-highlight-color: transparent; + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 1rem; +`; + +const SliderOutput = styled(BaseSlider.Output)` + text-align: right; +`; + +const SliderTrack = styled(BaseSlider.Track)` + grid-column: 1/3; + display: flex; + align-items: center; + position: relative; + width: 100%; + height: 16px; + border-radius: 9999px; + touch-action: none; - &.${sliderClasses.disabled} { - pointer-events: none; - cursor: default; - color: ${theme.palette.mode === 'light' ? grey[300] : grey[600]}; - opacity: 0.4; + &::before { + content: ''; + width: 100%; + height: 2px; + border-radius: 9999px; + background-color: ${grey[400]}; + touch-action: none; } - & .${sliderClasses.rail} { - display: block; - position: absolute; - width: 100%; - height: 4px; - border-radius: 6px; - background-color: currentColor; - opacity: 0.3; + &[data-disabled] { + cursor: not-allowed; + } + + .dark &::before { + background-color: ${grey[700]}; + } +`; + +const SliderThumb = styled(BaseSlider.Thumb)` + position: absolute; + width: 16px; + height: 16px; + box-sizing: border-box; + border-radius: 50%; + background-color: black; + transform: translateX(-50%); + touch-action: none; + + &:focus-visible { + outline: 2px solid black; + outline-offset: 2px; + } + + .dark & { + background-color: ${grey[300]}; } - & .${sliderClasses.track} { - display: block; - position: absolute; - height: 4px; - border-radius: 6px; - background-color: currentColor; + .dark &:focus-visible { + outline-color: ${grey[300]}; + outline-width: 1px; + outline-offset: 3px; } - & .${sliderClasses.thumb} { - display: flex; - align-items: center; - justify-content: center; - position: absolute; - margin-left: -6px; - width: 20px; - height: 20px; - box-sizing: border-box; - border-radius: 50%; - outline: 0; - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; - transition-property: box-shadow, transform; - transition-timing-function: ease; - transition-duration: 120ms; - transform-origin: center; - - &:hover { - box-shadow: 0 0 0 6px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[300], - 0.3, - )}; - } - - &.${sliderClasses.focusVisible} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - } - - &.${sliderClasses.active} { - box-shadow: 0 0 0 8px ${alpha( - theme.palette.mode === 'light' ? blue[200] : blue[400], - 0.5, - )}; - outline: none; - transform: scale(1.2); - } + &[data-dragging='true'] { + background-color: pink; } - & .${sliderClasses.mark} { - position: absolute; - width: 10px; - height: 10px; - border-radius: 99%; - background-color: ${theme.palette.mode === 'light' ? blue[200] : blue[900]}; - top: 44%; - transform: translateX(-50%); + &[data-disabled], + .dark &[data-disabled] { + background-color: ${grey[600]}; } - & .${sliderClasses.markActive} { - background-color: ${theme.palette.mode === 'light' ? blue[500] : blue[400]}; + .dark &[data-dragging='true'] { + background-color: pink; + } +`; + +// we can't use a