diff --git a/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx b/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx new file mode 100644 index 00000000000000..97fb8108c1cb09 --- /dev/null +++ b/apps/pigment-css-next-app/src/app/material-ui/react-slider/page.tsx @@ -0,0 +1,142 @@ +'use client'; +import * as React from 'react'; +import ColorSlider from '../../../../../../docs/data/material/components/slider/ColorSlider'; +import ContinuousSlider from '../../../../../../docs/data/material/components/slider/ContinuousSlider'; +import CustomMarks from '../../../../../../docs/data/material/components/slider/CustomMarks'; +import CustomizedSlider from '../../../../../../docs/data/material/components/slider/CustomizedSlider'; +import DiscreteSlider from '../../../../../../docs/data/material/components/slider/DiscreteSlider'; +import DiscreteSliderLabel from '../../../../../../docs/data/material/components/slider/DiscreteSliderLabel'; +import DiscreteSliderMarks from '../../../../../../docs/data/material/components/slider/DiscreteSliderMarks'; +import DiscreteSliderSteps from '../../../../../../docs/data/material/components/slider/DiscreteSliderSteps'; +import DiscreteSliderValues from '../../../../../../docs/data/material/components/slider/DiscreteSliderValues'; +import InputSlider from '../../../../../../docs/data/material/components/slider/InputSlider'; +import MinimumDistanceSlider from '../../../../../../docs/data/material/components/slider/MinimumDistanceSlider'; +import MusicPlayerSlider from '../../../../../../docs/data/material/components/slider/MusicPlayerSlider'; +import NonLinearSlider from '../../../../../../docs/data/material/components/slider/NonLinearSlider'; +import RangeSlider from '../../../../../../docs/data/material/components/slider/RangeSlider'; +import SliderSizes from '../../../../../../docs/data/material/components/slider/SliderSizes'; +import TrackFalseSlider from '../../../../../../docs/data/material/components/slider/TrackFalseSlider'; +import TrackInvertedSlider from '../../../../../../docs/data/material/components/slider/TrackInvertedSlider'; +import VerticalAccessibleSlider from '../../../../../../docs/data/material/components/slider/VerticalAccessibleSlider'; +import VerticalSlider from '../../../../../../docs/data/material/components/slider/VerticalSlider'; + +export default function Slider() { + return ( + +
+

Color Slider

+
+ +
+
+
+

Continuous Slider

+
+ +
+
+
+

Custom Marks

+
+ +
+
+
+

Customized Slider

+
+ +
+
+
+

Discrete Slider

+
+ +
+
+
+

Discrete Slider Label

+
+ +
+
+
+

Discrete Slider Marks

+
+ +
+
+
+

Discrete Slider Steps

+
+ +
+
+
+

Discrete Slider Values

+
+ +
+
+
+

Input Slider

+
+ +
+
+
+

Minimum Distance Slider

+
+ +
+
+
+

Music Player Slider

+
+ +
+
+
+

Non Linear Slider

+
+ +
+
+
+

Range Slider

+
+ +
+
+
+

Slider Sizes

+
+ +
+
+
+

Track False Slider

+
+ +
+
+
+

Track Inverted Slider

+
+ +
+
+
+

Vertical Accessible Slider

+
+ +
+
+
+

Vertical Slider

+
+ +
+
+
+ ); +} diff --git a/apps/pigment-css-next-app/src/app/slider/page.tsx b/apps/pigment-css-next-app/src/app/slider/page.tsx deleted file mode 100644 index 07e5fca1c0f2f2..00000000000000 --- a/apps/pigment-css-next-app/src/app/slider/page.tsx +++ /dev/null @@ -1,100 +0,0 @@ -'use client'; -import * as React from 'react'; -import { styled } from '@pigment-css/react'; -import { Button } from 'local-ui-lib'; -import Slider from '@/components/Slider/ZeroSlider'; - -const HalfWidth = styled.div(({ theme }) => ({ - marginLeft: 20, - width: '50%', - maxHeight: 100, - padding: 20, - border: `1px solid ${theme.vars.palette.primary.main}`, -})); - -export default function SliderPage() { - const [value, setValue] = React.useState(50); - const [isColorPrimary, setIsColorPrimary] = React.useState(true); - const [size, setSize] = React.useState('medium'); - const [showMarks, setShowMarks] = React.useState(true); - const [isTrackInverted, setIsTrackInverted] = React.useState(false); - const [disabled, setDisabled] = React.useState(false); - const [isHorizontal, setIsHorizontal] = React.useState(true); - - return ( -
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
- -
- - setValue(val as number)} - /> - -
- ); -} diff --git a/apps/pigment-css-next-app/src/components/Slider/ZeroSlider.tsx b/apps/pigment-css-next-app/src/components/Slider/ZeroSlider.tsx deleted file mode 100644 index cef2f605d9c5cf..00000000000000 --- a/apps/pigment-css-next-app/src/components/Slider/ZeroSlider.tsx +++ /dev/null @@ -1,945 +0,0 @@ -import * as React from 'react'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/utils'; -import { - type SliderOwnerState, - type SliderProps, - sliderClasses, - getSliderUtilityClass, -} from '@mui/material/Slider'; -import { isHostComponent, useSlotProps } from '@mui/base/utils'; -import { styled } from '@pigment-css/react'; -import { capitalize } from '@mui/material/utils'; -import SliderValueLabel from '@mui/material/Slider/SliderValueLabel'; -import { useSlider, valueToPercent } from '@mui/base/useSlider'; -import { alpha, lighten, darken } from '@mui/system/colorManipulator'; -import type { Theme } from '@mui/material/styles'; - -const shouldSpreadAdditionalProps = (Slot?: React.ElementType) => { - return !Slot || !isHostComponent(Slot); -}; - -function Identity(x: T): T { - return x; -} - -type SliderNestedOwnerState = SliderOwnerState & { - ownerState: SliderOwnerState; -}; - -const SliderRoot = styled('span', { - name: 'MuiSlider', - slot: 'Root', - overridesResolver(props, styles) { - const { ownerState } = props; - return [ - styles.root, - styles[`color${capitalize(ownerState.color ?? '')}`], - ownerState.size !== 'medium' && styles[`size${capitalize(ownerState.size ?? '')}`], - ownerState.marked && styles.marked, - ownerState.orientation === 'vertical' && styles.vertical, - ownerState.track === 'inverted' && styles.trackInverted, - ownerState.track === false && styles.trackFalse, - ]; - }, -})(({ theme }) => ({ - borderRadius: '12px', - boxSizing: 'content-box', - display: 'inline-block', - position: 'relative', - cursor: 'pointer', - touchAction: 'none', - WebkitTapHighlightColor: 'transparent', - '@media print': { - printColorAdjust: 'exact', - }, - [`&.${sliderClasses.disabled}`]: { - pointerEvents: 'none', - cursor: 'default', - color: (theme.vars || theme).palette.grey[400], - }, - [`&.${sliderClasses.dragging}`]: { - [`& .${sliderClasses.thumb}, & .${sliderClasses.track}`]: { - transition: 'none', - }, - }, - variants: [ - { - props({ color }) { - return color === 'primary'; - }, - style: { - color: (theme.vars || theme).palette.primary.main, - }, - }, - { - props: { - color: 'secondary', - }, - style: { - color: (theme.vars || theme).palette.secondary.main, - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - height: 4, - width: '100%', - padding: '13px 0', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '20px 0', - }, - }, - }, - { - props: { - orientation: 'horizontal', - size: 'small', - }, - style: { - height: 2, - }, - }, - { - props: { - orientation: 'horizontal', - marked: true, - }, - style: { - marginBottom: 20, - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - height: '100%', - width: 4, - padding: '0 13px', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '0 20px', - }, - }, - }, - { - props: { - orientation: 'vertical', - size: 'small', - }, - style: { - width: 2, - }, - }, - { - props: { - orientation: 'vertical', - marked: true, - }, - style: { - marginRight: 44, - }, - }, - ], -})); - -export { SliderRoot }; - -const SliderRail = styled('span', { - name: 'MuiSlider', - slot: 'Rail', - overridesResolver: (props, styles) => styles.rail, -})({ - display: 'block', - position: 'absolute', - borderRadius: 'inherit', - backgroundColor: 'currentColor', - opacity: 0.38, - variants: [ - { - props: { - orientation: 'horizontal', - }, - style: { - width: '100%', - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - height: '100%', - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }, - }, - { - props: { - track: 'inverted', - }, - style: { - opacity: 1, - }, - }, - ], -}); - -export { SliderRail }; - -const SliderTrack = styled('span', { - name: 'MuiSlider', - slot: 'Track', - overridesResolver: (props, styles) => styles.track, -})(({ theme }) => { - const lightPrimaryColor = theme.vars - ? theme.vars.palette.Slider.primaryTrack - : lighten(theme.palette.primary.main, 0.62); - const lightSecondaryColor = theme.vars - ? theme.vars.palette.Slider.secondaryTrack - : lighten(theme.palette.secondary.main, 0.62); - const darkPrimaryColor = theme.vars - ? theme.vars.palette.Slider.primaryTrack - : darken(theme.palette.primary.main, 0.5); - const darkSecondaryColor = theme.vars - ? theme.vars.palette.Slider.secondaryTrack - : darken(theme.palette.secondary.main, 0.5); - - return { - display: 'block', - position: 'absolute', - borderRadius: 'inherit', - border: '1px solid currentColor', - backgroundColor: 'currentColor', - transition: theme.transitions.create(['left', 'width', 'bottom', 'height'], { - duration: theme.transitions.duration.shortest, - }), - variants: [ - { - props: { - color: 'primary', - }, - style: { - '--slider-track-color': lightPrimaryColor, - ...theme.applyStyles('dark', { - '--slider-track-color': darkPrimaryColor, - }), - }, - }, - { - props: { - color: 'secondary', - }, - style: { - '--slider-track-color': lightSecondaryColor, - ...theme.applyStyles('dark', { - '--slider-track-color': darkSecondaryColor, - }), - }, - }, - { - props: { - size: 'small', - }, - style: { - border: 'none', - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }, - }, - { - props: { - track: false, - }, - style: { - display: 'none', - }, - }, - { - props: { - track: 'inverted', - color: 'primary', - }, - style: { - backgroundColor: (theme.vars ?? theme).palette.Slider?.primaryTrack, - borderColor: (theme.vars ?? theme).palette.Slider?.primaryTrack, - }, - }, - { - props: { - track: 'inverted', - color: 'secondary', - }, - style: { - backgroundColor: (theme.vars ?? theme).palette.Slider?.secondaryTrack, - borderColor: (theme.vars ?? theme).palette.Slider?.secondaryTrack, - }, - }, - ], - }; -}); - -export { SliderTrack }; - -const SliderThumb = styled('span', { - name: 'MuiSlider', - slot: 'Thumb', - overridesResolver: (props, styles) => { - const { ownerState } = props; - return [ - styles.thumb, - styles[`thumbColor${capitalize(ownerState.color ?? '')}`], - ownerState.size !== 'medium' && styles[`thumbSize${capitalize(ownerState.size ?? '')}`], - ]; - }, -})(({ theme }) => ({ - position: 'absolute', - width: 20, - height: 20, - boxSizing: 'border-box', - borderRadius: '50%', - outline: 0, - backgroundColor: 'currentColor', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - transition: theme.transitions.create(['box-shadow', 'left', 'bottom'], { - duration: theme.transitions.duration.shortest, - }), - '&:before': { - position: 'absolute', - content: '""', - borderRadius: 'inherit', - width: '100%', - height: '100%', - boxShadow: (theme.vars || theme).shadows[2], - }, - '&::after': { - position: 'absolute', - content: '""', - borderRadius: '50%', - // 42px is the hit target - width: 42, - height: 42, - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - }, - [`&:hover, &.${sliderClasses.focusVisible}`]: { - boxShadow: `0px 0px 0px 8px var(--slider-thumb-shadow-color)`, - '@media (hover: none)': { - boxShadow: 'none', - }, - }, - [`&.${sliderClasses.active}`]: { - boxShadow: `0px 0px 0px 14px var(--slider-thumb-shadow-color)`, - }, - [`&.${sliderClasses.disabled}`]: { - '&:hover': { - boxShadow: 'none', - }, - }, - variants: [ - { - props: { - color: 'primary', - }, - style: { - '--slider-thumb-shadow-color': theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / 0.16)` - : alpha((theme as Theme).palette.primary.main, 0.16), - }, - }, - { - props: { - color: 'secondary', - }, - style: { - '--slider-thumb-shadow-color': theme.vars - ? `rgba(${theme.vars.palette.secondary.mainChannel} / 0.16)` - : alpha((theme as Theme).palette.secondary.main, 0.16), - }, - }, - { - props: { - size: 'small', - }, - style: { - width: 12, - height: 12, - '&:before': { - boxShadow: 'none', - }, - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - top: '50%', - transform: 'translate(-50%, -50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - left: '50%', - transform: 'translate(-50%, 50%)', - }, - }, - ], -})); - -export { SliderThumb }; - -const StyledSliderValueLabel = styled(SliderValueLabel, { - name: 'MuiSlider', - slot: 'ValueLabel', - overridesResolver: (props, styles) => styles.valueLabel, -})(({ theme }) => ({ - zIndex: 1, - whiteSpace: 'nowrap', - ...theme.typography.body2, - fontWeight: 500, - transition: theme.transitions.create(['transform'], { - duration: theme.transitions.duration.shortest, - }), - position: 'absolute', - backgroundColor: (theme.vars || theme).palette.grey[600], - borderRadius: '2px', - color: (theme.vars || theme).palette.common.white, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - padding: '0.25rem 0.75rem', - variants: [ - { - props: { - size: 'small', - }, - style: { - fontSize: theme.typography.pxToRem(12), - padding: '0.25rem 0.5rem', - }, - }, - { - props: { - orientation: 'horizontal', - }, - style: { - top: '-10px', - transformOrigin: 'bottom center', - transform: 'translateY(-100%) scale(0)', - '&:before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, 50%) rotate(45deg)', - backgroundColor: 'inherit', - bottom: 0, - left: '50%', - }, - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: 'translateY(-100%) scale(1)', - }, - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - top: '50%', - right: '30px', - transform: 'translateY(-50%) scale(0)', - transformOrigin: 'right center', - '&:before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, -50%) rotate(45deg)', - backgroundColor: 'inherit', - right: -8, - top: '50%', - }, - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: 'translateY(-50%) scale(1)', - }, - }, - }, - { - props: { - orientation: 'vertical', - size: 'small', - }, - style: { - right: '20px', - }, - }, - ], -})); - -export { StyledSliderValueLabel as SliderValueLabel }; - -const SliderMark = styled('span', { - name: 'MuiSlider', - slot: 'Mark', - shouldForwardProp: (prop) => prop !== 'markActive', - // @TODO - Support this in `styled` implementation - // shouldForwardProp: (prop) => slotShouldForwardProp(prop) && prop !== 'markActive', - overridesResolver: (props, styles) => { - const { markActive } = props; - - return [styles.mark, markActive && styles.markActive]; - }, -})(({ theme }) => ({ - position: 'absolute', - width: 2, - height: 2, - borderRadius: 1, - backgroundColor: 'currentColor', - variants: [ - { - props: { - orientation: 'horizontal', - }, - style: { - top: '50%', - transform: 'translate(-1px, -50%)', - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - left: '50%', - transform: 'translate(-50%, 1px)', - }, - }, - { - props: { - markActive: true, - }, - style: { - backgroundColor: (theme.vars || theme).palette.background.paper, - opacity: 0.8, - }, - }, - ], -})); - -export { SliderMark }; - -const SliderMarkLabel = styled('span', { - name: 'MuiSlider', - slot: 'MarkLabel', - // @TODO - // shouldForwardProp: (prop) => slotShouldForwardProp(prop) && prop !== 'markLabelActive', - overridesResolver: (props, styles) => styles.markLabel, -})(({ theme }) => ({ - ...theme.typography.body2, - color: (theme.vars || theme).palette.text.secondary, - position: 'absolute', - whiteSpace: 'nowrap', - variants: [ - { - props: { - orientation: 'horizontal', - }, - style: { - top: 30, - transform: 'translateX(-50%)', - '@media (pointer: coarse)': { - top: 40, - }, - }, - }, - { - props: { - orientation: 'vertical', - }, - style: { - left: 36, - transform: 'translateY(50%)', - '@media (pointer: coarse)': { - left: 44, - }, - }, - }, - { - props: { - markLabelActive: true, - }, - style: { - color: (theme.vars || theme).palette.text.primary, - }, - }, - ], -})); - -const useUtilityClasses = (ownerState: SliderOwnerState) => { - const { disabled, dragging, marked, orientation, track, classes, color, size } = ownerState; - - const slots = { - root: [ - 'root', - disabled && 'disabled', - dragging && 'dragging', - marked && 'marked', - orientation === 'vertical' && 'vertical', - track === 'inverted' && 'trackInverted', - track === false && 'trackFalse', - color && `color${capitalize(color)}`, - size && `size${capitalize(size)}`, - ], - rail: ['rail'], - track: ['track'], - mark: ['mark'], - markActive: ['markActive'], - markLabel: ['markLabel'], - markLabelActive: ['markLabelActive'], - valueLabel: ['valueLabel'], - thumb: [ - 'thumb', - disabled && 'disabled', - size && `thumbSize${capitalize(size)}`, - color && `thumbColor${capitalize(color)}`, - ], - active: ['active'], - disabled: ['disabled'], - focusVisible: ['focusVisible'], - }; - - return composeClasses(slots, getSliderUtilityClass, classes); -}; - -export { SliderMarkLabel }; - -const Forward = ({ children }: React.PropsWithChildren) => children; - -const Slider = React.forwardRef(function Slider(props, ref) { - // @TODO - Figure out how to persist this information - const isRtl = false; // theme.direction === 'rtl'; - - const { - 'aria-label': ariaLabel, - 'aria-valuetext': ariaValuetext, - 'aria-labelledby': ariaLabelledby, - component = 'span', - components = {}, - componentsProps = {}, - color = 'primary', - classes: classesProp, - className, - disableSwap = false, - disabled = false, - getAriaLabel, - getAriaValueText, - marks: marksProp = false, - max = 100, - min = 0, - orientation = 'horizontal', - size = 'medium', - step = 1, - scale = Identity, - slotProps, - slots, - track = 'normal', - valueLabelDisplay = 'off', - valueLabelFormat = Identity, - ...other - } = props; - - const ownerState: SliderOwnerState = { - ...props, - isRtl, - max, - min, - classes: classesProp, - disabled, - disableSwap, - orientation, - marks: marksProp, - color, - size, - step, - scale, - track, - valueLabelDisplay, - valueLabelFormat, - }; - - const { - axisProps, - getRootProps, - getHiddenInputProps, - getThumbProps, - open, - active, - axis, - focusedThumbIndex, - range, - dragging, - marks, - values, - trackOffset, - trackLeap, - getThumbStyle, - } = useSlider({ - ...ownerState, - rootRef: ref, - }); - - ownerState.marked = marks.length > 0 && marks.some((mark) => mark.label); - ownerState.dragging = dragging; - ownerState.focusedThumbIndex = focusedThumbIndex; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots?.root ?? components.Root ?? SliderRoot; - const RailSlot = slots?.rail ?? components.Rail ?? SliderRail; - const TrackSlot = slots?.track ?? components.Track ?? SliderTrack; - const ThumbSlot = slots?.thumb ?? components.Thumb ?? SliderThumb; - const ValueLabelSlot = slots?.valueLabel ?? components.ValueLabel ?? StyledSliderValueLabel; - const MarkSlot = slots?.mark ?? components.Mark ?? SliderMark; - const MarkLabelSlot = slots?.markLabel ?? components.MarkLabel ?? SliderMarkLabel; - const InputSlot = slots?.input ?? components.Input ?? 'input'; - - const rootSlotProps = slotProps?.root ?? componentsProps.root; - const railSlotProps = slotProps?.rail ?? componentsProps.rail; - const trackSlotProps = slotProps?.track ?? componentsProps.track; - const thumbSlotProps = slotProps?.thumb ?? componentsProps.thumb; - const valueLabelSlotProps = slotProps?.valueLabel ?? componentsProps.valueLabel; - const markSlotProps = slotProps?.mark ?? componentsProps.mark; - const markLabelSlotProps = slotProps?.markLabel ?? componentsProps.markLabel; - const inputSlotProps = slotProps?.input ?? componentsProps.input; - - const rootProps = useSlotProps({ - elementType: RootSlot, - getSlotProps: getRootProps, - externalSlotProps: rootSlotProps, - externalForwardedProps: other, - additionalProps: { - ...(shouldSpreadAdditionalProps(RootSlot) && { - as: component, - }), - }, - ownerState: { - ...ownerState, - ...rootSlotProps?.ownerState, - }, - className: [classes.root, className], - }); - - const railProps = useSlotProps({ - elementType: RailSlot, - externalSlotProps: railSlotProps, - ownerState, - className: classes.rail, - }); - - const trackProps = useSlotProps({ - elementType: TrackSlot, - externalSlotProps: trackSlotProps, - additionalProps: { - style: { - ...axisProps[axis].offset(trackOffset), - ...axisProps[axis].leap(trackLeap), - }, - }, - ownerState: { - ...ownerState, - ...trackSlotProps?.ownerState, - }, - className: classes.track, - }); - - const thumbProps = useSlotProps({ - elementType: ThumbSlot, - getSlotProps: getThumbProps, - externalSlotProps: thumbSlotProps, - ownerState: { - ...ownerState, - ...thumbSlotProps?.ownerState, - }, - className: classes.thumb, - }); - - const valueLabelProps = useSlotProps({ - elementType: ValueLabelSlot, - externalSlotProps: valueLabelSlotProps, - ownerState: { - ...ownerState, - ...valueLabelSlotProps?.ownerState, - }, - className: classes.valueLabel, - }); - - const markProps = useSlotProps({ - elementType: MarkSlot, - externalSlotProps: markSlotProps, - ownerState, - className: classes.mark, - }); - - const markLabelProps = useSlotProps({ - elementType: MarkLabelSlot, - externalSlotProps: markLabelSlotProps, - ownerState, - className: classes.markLabel, - }); - - const inputSliderProps = useSlotProps({ - elementType: InputSlot, - getSlotProps: getHiddenInputProps, - externalSlotProps: inputSlotProps, - ownerState, - }); - - return ( - - - - {marks - .filter((mark) => mark.value >= min && mark.value <= max) - .map((mark, index) => { - const percent = valueToPercent(mark.value, min, max); - const style = axisProps[axis].offset(percent); - - let markActive; - if (track === false) { - markActive = values.indexOf(mark.value) !== -1; - } else { - markActive = - (track === 'normal' && - (range - ? mark.value >= values[0] && mark.value <= values[values.length - 1] - : mark.value <= values[0])) || - (track === 'inverted' && - (range - ? mark.value <= values[0] || mark.value >= values[values.length - 1] - : mark.value >= values[0])); - } - - return ( - - - {mark.label != null ? ( - - {mark.label} - - ) : null} - - ); - })} - {values.map((value, index) => { - const percent = valueToPercent(value, min, max); - const style = axisProps[axis].offset(percent); - - const ValueLabelComponent = valueLabelDisplay === 'off' ? Forward : ValueLabelSlot; - - return ( - /* TODO v6: Change component structure. It will help in avoiding the complicated React.cloneElement API added in SliderValueLabel component. Should be: Thumb -> Input, ValueLabel. Follow Joy UI's Slider structure. */ - - - - - - ); - })} - - ); -}); - -export default Slider; diff --git a/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx new file mode 100644 index 00000000000000..963612339e2faf --- /dev/null +++ b/apps/pigment-css-vite-app/src/pages/material-ui/react-slider.tsx @@ -0,0 +1,143 @@ +import * as React from 'react'; +import MaterialUILayout from '../../Layout'; +import ColorSlider from '../../../../../docs/data/material/components/slider/ColorSlider.tsx'; +import ContinuousSlider from '../../../../../docs/data/material/components/slider/ContinuousSlider.tsx'; +import CustomMarks from '../../../../../docs/data/material/components/slider/CustomMarks.tsx'; +import CustomizedSlider from '../../../../../docs/data/material/components/slider/CustomizedSlider.tsx'; +import DiscreteSlider from '../../../../../docs/data/material/components/slider/DiscreteSlider.tsx'; +import DiscreteSliderLabel from '../../../../../docs/data/material/components/slider/DiscreteSliderLabel.tsx'; +import DiscreteSliderMarks from '../../../../../docs/data/material/components/slider/DiscreteSliderMarks.tsx'; +import DiscreteSliderSteps from '../../../../../docs/data/material/components/slider/DiscreteSliderSteps.tsx'; +import DiscreteSliderValues from '../../../../../docs/data/material/components/slider/DiscreteSliderValues.tsx'; +import InputSlider from '../../../../../docs/data/material/components/slider/InputSlider.tsx'; +import MinimumDistanceSlider from '../../../../../docs/data/material/components/slider/MinimumDistanceSlider.tsx'; +import MusicPlayerSlider from '../../../../../docs/data/material/components/slider/MusicPlayerSlider.tsx'; +import NonLinearSlider from '../../../../../docs/data/material/components/slider/NonLinearSlider.tsx'; +import RangeSlider from '../../../../../docs/data/material/components/slider/RangeSlider.tsx'; +import SliderSizes from '../../../../../docs/data/material/components/slider/SliderSizes.tsx'; +import TrackFalseSlider from '../../../../../docs/data/material/components/slider/TrackFalseSlider.tsx'; +import TrackInvertedSlider from '../../../../../docs/data/material/components/slider/TrackInvertedSlider.tsx'; +import VerticalAccessibleSlider from '../../../../../docs/data/material/components/slider/VerticalAccessibleSlider.tsx'; +import VerticalSlider from '../../../../../docs/data/material/components/slider/VerticalSlider.tsx'; + +export default function Slider() { + return ( + +

Slider

+
+

Color Slider

+
+ +
+
+
+

Continuous Slider

+
+ +
+
+
+

Custom Marks

+
+ +
+
+
+

Customized Slider

+
+ +
+
+
+

Discrete Slider

+
+ +
+
+
+

Discrete Slider Label

+
+ +
+
+
+

Discrete Slider Marks

+
+ +
+
+
+

Discrete Slider Steps

+
+ +
+
+
+

Discrete Slider Values

+
+ +
+
+
+

Input Slider

+
+ +
+
+
+

Minimum Distance Slider

+
+ +
+
+
+

Music Player Slider

+
+ +
+
+
+

Non Linear Slider

+
+ +
+
+
+

Range Slider

+
+ +
+
+
+

Slider Sizes

+
+ +
+
+
+

Track False Slider

+
+ +
+
+
+

Track Inverted Slider

+
+ +
+
+
+

Vertical Accessible Slider

+
+ +
+
+
+

Vertical Slider

+
+ +
+
+
+ ); +} diff --git a/packages/mui-material/src/Slider/Slider.js b/packages/mui-material/src/Slider/Slider.js index 096e13f1c25555..5b5ed8c9e97f61 100644 --- a/packages/mui-material/src/Slider/Slider.js +++ b/packages/mui-material/src/Slider/Slider.js @@ -8,13 +8,15 @@ import composeClasses from '@mui/utils/composeClasses'; import { useSlider, valueToPercent } from '@mui/base/useSlider'; import { alpha, lighten, darken } from '@mui/system/colorManipulator'; import { useRtl } from '@mui/system/RtlProvider'; -import useThemeProps from '../styles/useThemeProps'; -import styled, { slotShouldForwardProp } from '../styles/styled'; +import { styled, createUseThemeProps } from '../zero-styled'; +import slotShouldForwardProp from '../styles/slotShouldForwardProp'; import shouldSpreadAdditionalProps from '../utils/shouldSpreadAdditionalProps'; import capitalize from '../utils/capitalize'; import BaseSliderValueLabel from './SliderValueLabel'; import sliderClasses, { getSliderUtilityClass } from './sliderClasses'; +const useThemeProps = createUseThemeProps('MuiSlider'); + function Identity(x) { return x; } @@ -35,47 +37,14 @@ export const SliderRoot = styled('span', { ownerState.track === false && styles.trackFalse, ]; }, -})(({ theme, ownerState }) => ({ +})(({ theme }) => ({ borderRadius: 12, boxSizing: 'content-box', display: 'inline-block', position: 'relative', cursor: 'pointer', touchAction: 'none', - color: (theme.vars || theme).palette[ownerState.color].main, WebkitTapHighlightColor: 'transparent', - ...(ownerState.orientation === 'horizontal' && { - height: 4, - width: '100%', - padding: '13px 0', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '20px 0', - }, - ...(ownerState.size === 'small' && { - height: 2, - }), - ...(ownerState.marked && { - marginBottom: 20, - }), - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - width: 4, - padding: '0 13px', - // The primary input mechanism of the device includes a pointing device of limited accuracy. - '@media (pointer: coarse)': { - // Reach 42px touch target, about ~8mm on screen. - padding: '0 20px', - }, - ...(ownerState.size === 'small' && { - width: 2, - }), - ...(ownerState.marked && { - marginRight: 44, - }), - }), '@media print': { colorAdjust: 'exact', }, @@ -89,44 +58,111 @@ export const SliderRoot = styled('span', { transition: 'none', }, }, + variants: [ + ...Object.keys((theme.vars ?? theme).palette) + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color }, + style: { + color: (theme.vars || theme).palette[color].main, + }, + })), + { + props: { orientation: 'horizontal' }, + style: { + height: 4, + width: '100%', + padding: '13px 0', + // The primary input mechanism of the device includes a pointing device of limited accuracy. + '@media (pointer: coarse)': { + // Reach 42px touch target, about ~8mm on screen. + padding: '20px 0', + }, + }, + }, + { + props: { orientation: 'horizontal', size: 'small' }, + style: { + height: 2, + }, + }, + { + props: { orientation: 'horizontal', marked: true }, + style: { + marginBottom: 20, + }, + }, + { + props: { orientation: 'vertical' }, + style: { + height: '100%', + width: 4, + padding: '0 13px', + // The primary input mechanism of the device includes a pointing device of limited accuracy. + '@media (pointer: coarse)': { + // Reach 42px touch target, about ~8mm on screen. + padding: '0 20px', + }, + }, + }, + { + props: { orientation: 'vertical', size: 'small' }, + style: { + width: 2, + }, + }, + { + props: { orientation: 'vertical', marked: true }, + style: { + marginRight: 44, + }, + }, + ], })); export const SliderRail = styled('span', { name: 'MuiSlider', slot: 'Rail', overridesResolver: (props, styles) => styles.rail, -})(({ ownerState }) => ({ +})({ display: 'block', position: 'absolute', borderRadius: 'inherit', backgroundColor: 'currentColor', opacity: 0.38, - ...(ownerState.orientation === 'horizontal' && { - width: '100%', - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }), - ...(ownerState.track === 'inverted' && { - opacity: 1, - }), -})); + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + width: '100%', + height: 'inherit', + top: '50%', + transform: 'translateY(-50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + height: '100%', + width: 'inherit', + left: '50%', + transform: 'translateX(-50%)', + }, + }, + { + props: { track: 'inverted' }, + style: { + opacity: 1, + }, + }, + ], +}); export const SliderTrack = styled('span', { name: 'MuiSlider', slot: 'Track', overridesResolver: (props, styles) => styles.track, -})(({ theme, ownerState }) => { - const color = // Same logic as the LinearProgress track color - theme.palette.mode === 'light' - ? lighten(theme.palette[ownerState.color].main, 0.62) - : darken(theme.palette[ownerState.color].main, 0.5); +})(({ theme }) => { return { display: 'block', position: 'absolute', @@ -136,26 +172,58 @@ export const SliderTrack = styled('span', { transition: theme.transitions.create(['left', 'width', 'bottom', 'height'], { duration: theme.transitions.duration.shortest, }), - ...(ownerState.size === 'small' && { - border: 'none', - }), - ...(ownerState.orientation === 'horizontal' && { - height: 'inherit', - top: '50%', - transform: 'translateY(-50%)', - }), - ...(ownerState.orientation === 'vertical' && { - width: 'inherit', - left: '50%', - transform: 'translateX(-50%)', - }), - ...(ownerState.track === false && { - display: 'none', - }), - ...(ownerState.track === 'inverted' && { - backgroundColor: theme.vars ? theme.vars.palette.Slider[`${ownerState.color}Track`] : color, - borderColor: theme.vars ? theme.vars.palette.Slider[`${ownerState.color}Track`] : color, - }), + variants: [ + { + props: { size: 'small' }, + style: { + border: 'none', + }, + }, + { + props: { orientation: 'horizontal' }, + style: { + height: 'inherit', + top: '50%', + transform: 'translateY(-50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + width: 'inherit', + left: '50%', + transform: 'translateX(-50%)', + }, + }, + { + props: { track: false }, + style: { + display: 'none', + }, + }, + ...Object.keys((theme.vars ?? theme).palette) + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color, track: 'inverted' }, + style: { + ...(theme.vars + ? { + backgroundColor: theme.vars.palette.Slider[`${color}Track`], + borderColor: theme.vars.palette.Slider[`${color}Track`], + } + : { + backgroundColor: lighten(theme.palette[color].main, 0.62), + borderColor: lighten(theme.palette[color].main, 0.62), + ...theme.applyStyles('dark', { + backgroundColor: darken(theme.palette[color].main, 0.5), + }), + ...theme.applyStyles('dark', { + borderColor: darken(theme.palette[color].main, 0.5), + }), + }), + }, + })), + ], }; }); @@ -170,7 +238,7 @@ export const SliderThumb = styled('span', { ownerState.size !== 'medium' && styles[`thumbSize${capitalize(ownerState.size)}`], ]; }, -})(({ theme, ownerState }) => ({ +})(({ theme }) => ({ position: 'absolute', width: 20, height: 20, @@ -184,18 +252,6 @@ export const SliderThumb = styled('span', { transition: theme.transitions.create(['box-shadow', 'left', 'bottom'], { duration: theme.transitions.duration.shortest, }), - ...(ownerState.size === 'small' && { - width: 12, - height: 12, - }), - ...(ownerState.orientation === 'horizontal' && { - top: '50%', - transform: 'translate(-50%, -50%)', - }), - ...(ownerState.orientation === 'vertical' && { - left: '50%', - transform: 'translate(-50%, 50%)', - }), '&::before': { position: 'absolute', content: '""', @@ -203,9 +259,6 @@ export const SliderThumb = styled('span', { width: '100%', height: '100%', boxShadow: (theme.vars || theme).shadows[2], - ...(ownerState.size === 'small' && { - boxShadow: 'none', - }), }, '&::after': { position: 'absolute', @@ -218,40 +271,72 @@ export const SliderThumb = styled('span', { left: '50%', transform: 'translate(-50%, -50%)', }, - [`&:hover, &.${sliderClasses.focusVisible}`]: { - boxShadow: `0px 0px 0px 8px ${ - theme.vars - ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.16)` - : alpha(theme.palette[ownerState.color].main, 0.16) - }`, - '@media (hover: none)': { - boxShadow: 'none', - }, - }, - [`&.${sliderClasses.active}`]: { - boxShadow: `0px 0px 0px 14px ${ - theme.vars - ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.16)` - : alpha(theme.palette[ownerState.color].main, 0.16) - }`, - }, [`&.${sliderClasses.disabled}`]: { '&:hover': { boxShadow: 'none', }, }, + variants: [ + ...Object.keys((theme.vars ?? theme).palette) + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color }, + style: { + [`&:hover, &.${sliderClasses.focusVisible}`]: { + ...(theme.vars + ? { + boxShadow: `0px 0px 0px 8px rgba(${theme.vars.palette[color].mainChannel} / 0.16)`, + } + : { + boxShadow: `0px 0px 0px 8px ${alpha(theme.palette[color].main, 0.16)}`, + }), + '@media (hover: none)': { + boxShadow: 'none', + }, + }, + [`&.${sliderClasses.active}`]: { + ...(theme.vars + ? { + boxShadow: `0px 0px 0px 14px rgba(${theme.vars.palette[color].mainChannel} / 0.16)}`, + } + : { + boxShadow: `0px 0px 0px 14px ${alpha(theme.palette[color].main, 0.16)}`, + }), + }, + }, + })), + { + props: { size: 'small' }, + style: { + width: 12, + height: 12, + '&::before': { + boxShadow: 'none', + }, + }, + }, + { + props: { orientation: 'horizontal' }, + style: { + top: '50%', + transform: 'translate(-50%, -50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + left: '50%', + transform: 'translate(-50%, 50%)', + }, + }, + ], })); export const SliderValueLabel = styled(BaseSliderValueLabel, { name: 'MuiSlider', slot: 'ValueLabel', overridesResolver: (props, styles) => styles.valueLabel, -})(({ theme, ownerState }) => ({ - [`&.${sliderClasses.valueLabelOpen}`]: { - transform: `${ - ownerState.orientation === 'vertical' ? 'translateY(-50%)' : 'translateY(-100%)' - } scale(1)`, - }, +})(({ theme }) => ({ zIndex: 1, whiteSpace: 'nowrap', ...theme.typography.body2, @@ -259,9 +344,6 @@ export const SliderValueLabel = styled(BaseSliderValueLabel, { transition: theme.transitions.create(['transform'], { duration: theme.transitions.duration.shortest, }), - transform: `${ - ownerState.orientation === 'vertical' ? 'translateY(-50%)' : 'translateY(-100%)' - } scale(0)`, position: 'absolute', backgroundColor: (theme.vars || theme).palette.grey[600], borderRadius: 2, @@ -270,39 +352,64 @@ export const SliderValueLabel = styled(BaseSliderValueLabel, { alignItems: 'center', justifyContent: 'center', padding: '0.25rem 0.75rem', - ...(ownerState.orientation === 'horizontal' && { - top: '-10px', - transformOrigin: 'bottom center', - '&::before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, 50%) rotate(45deg)', - backgroundColor: 'inherit', - bottom: 0, - left: '50%', + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + transform: 'translateY(-100%) scale(0)', + top: '-10px', + transformOrigin: 'bottom center', + '&::before': { + position: 'absolute', + content: '""', + width: 8, + height: 8, + transform: 'translate(-50%, 50%) rotate(45deg)', + backgroundColor: 'inherit', + bottom: 0, + left: '50%', + }, + [`&.${sliderClasses.valueLabelOpen}`]: { + transform: 'translateY(-100%) scale(1)', + }, + }, }, - }), - ...(ownerState.orientation === 'vertical' && { - right: ownerState.size === 'small' ? '20px' : '30px', - top: '50%', - transformOrigin: 'right center', - '&::before': { - position: 'absolute', - content: '""', - width: 8, - height: 8, - transform: 'translate(-50%, -50%) rotate(45deg)', - backgroundColor: 'inherit', - right: -8, - top: '50%', + { + props: { orientation: 'vertical' }, + style: { + transform: 'translateY(-50%) scale(0)', + right: '30px', + top: '50%', + transformOrigin: 'right center', + '&::before': { + position: 'absolute', + content: '""', + width: 8, + height: 8, + transform: 'translate(-50%, -50%) rotate(45deg)', + backgroundColor: 'inherit', + right: -8, + top: '50%', + }, + [`&.${sliderClasses.valueLabelOpen}`]: { + transform: 'translateY(-50%) scale(1)', + }, + }, }, - }), - ...(ownerState.size === 'small' && { - fontSize: theme.typography.pxToRem(12), - padding: '0.25rem 0.5rem', - }), + { + props: { size: 'small' }, + style: { + fontSize: theme.typography.pxToRem(12), + padding: '0.25rem 0.5rem', + }, + }, + { + props: { orientation: 'vertical', size: 'small' }, + style: { + right: '20px', + }, + }, + ], })); export const SliderMark = styled('span', { @@ -314,24 +421,35 @@ export const SliderMark = styled('span', { return [styles.mark, markActive && styles.markActive]; }, -})(({ theme, ownerState, markActive }) => ({ +})(({ theme }) => ({ position: 'absolute', width: 2, height: 2, borderRadius: 1, backgroundColor: 'currentColor', - ...(ownerState.orientation === 'horizontal' && { - top: '50%', - transform: 'translate(-1px, -50%)', - }), - ...(ownerState.orientation === 'vertical' && { - left: '50%', - transform: 'translate(-50%, 1px)', - }), - ...(markActive && { - backgroundColor: (theme.vars || theme).palette.background.paper, - opacity: 0.8, - }), + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + top: '50%', + transform: 'translate(-1px, -50%)', + }, + }, + { + props: { orientation: 'vertical' }, + style: { + left: '50%', + transform: 'translate(-50%, 1px)', + }, + }, + { + props: { markActive: true }, + style: { + backgroundColor: (theme.vars || theme).palette.background.paper, + opacity: 0.8, + }, + }, + ], })); export const SliderMarkLabel = styled('span', { @@ -339,28 +457,39 @@ export const SliderMarkLabel = styled('span', { slot: 'MarkLabel', shouldForwardProp: (prop) => slotShouldForwardProp(prop) && prop !== 'markLabelActive', overridesResolver: (props, styles) => styles.markLabel, -})(({ theme, ownerState, markLabelActive }) => ({ +})(({ theme }) => ({ ...theme.typography.body2, color: (theme.vars || theme).palette.text.secondary, position: 'absolute', whiteSpace: 'nowrap', - ...(ownerState.orientation === 'horizontal' && { - top: 30, - transform: 'translateX(-50%)', - '@media (pointer: coarse)': { - top: 40, + variants: [ + { + props: { orientation: 'horizontal' }, + style: { + top: 30, + transform: 'translateX(-50%)', + '@media (pointer: coarse)': { + top: 40, + }, + }, }, - }), - ...(ownerState.orientation === 'vertical' && { - left: 36, - transform: 'translateY(50%)', - '@media (pointer: coarse)': { - left: 44, + { + props: { orientation: 'vertical' }, + style: { + left: 36, + transform: 'translateY(50%)', + '@media (pointer: coarse)': { + left: 44, + }, + }, }, - }), - ...(markLabelActive && { - color: (theme.vars || theme).palette.text.primary, - }), + { + props: { markLabelActive: true }, + style: { + color: (theme.vars || theme).palette.text.primary, + }, + }, + ], })); const useUtilityClasses = (ownerState) => { diff --git a/packages/mui-material/src/styles/rootShouldForwardProp.ts b/packages/mui-material/src/styles/rootShouldForwardProp.ts new file mode 100644 index 00000000000000..92a781135a85a2 --- /dev/null +++ b/packages/mui-material/src/styles/rootShouldForwardProp.ts @@ -0,0 +1,5 @@ +import slotShouldForwardProp from './slotShouldForwardProp'; + +const rootShouldForwardProp = (prop: string) => slotShouldForwardProp(prop) && prop !== 'classes'; + +export default rootShouldForwardProp; diff --git a/packages/mui-material/src/styles/slotShouldForwardProp.ts b/packages/mui-material/src/styles/slotShouldForwardProp.ts new file mode 100644 index 00000000000000..8b674ade2bd0cd --- /dev/null +++ b/packages/mui-material/src/styles/slotShouldForwardProp.ts @@ -0,0 +1,6 @@ +// copied from @mui/system/createStyled +function slotShouldForwardProp(prop: string) { + return prop !== 'ownerState' && prop !== 'theme' && prop !== 'sx' && prop !== 'as'; +} + +export default slotShouldForwardProp; diff --git a/packages/mui-material/src/styles/styled.d.ts b/packages/mui-material/src/styles/styled.d.ts index a5977f53ac9977..3c7fa4938bf0d4 100644 --- a/packages/mui-material/src/styles/styled.d.ts +++ b/packages/mui-material/src/styles/styled.d.ts @@ -1,9 +1,8 @@ import { CreateMUIStyled } from '@mui/system'; import { Theme } from './createTheme'; -export function rootShouldForwardProp(prop: string): boolean; - -export function slotShouldForwardProp(prop: string): boolean; +export { default as slotShouldForwardProp } from './slotShouldForwardProp'; +export { default as rootShouldForwardProp } from './rootShouldForwardProp'; /** * Custom styled utility that has a default MUI theme. diff --git a/packages/mui-material/src/styles/styled.js b/packages/mui-material/src/styles/styled.js index 4e68a6ab48eaef..3d690d470ed672 100644 --- a/packages/mui-material/src/styles/styled.js +++ b/packages/mui-material/src/styles/styled.js @@ -1,11 +1,11 @@ 'use client'; -import createStyled, { shouldForwardProp } from '@mui/system/createStyled'; +import createStyled from '@mui/system/createStyled'; import defaultTheme from './defaultTheme'; import THEME_ID from './identifier'; +import rootShouldForwardProp from './rootShouldForwardProp'; -export const rootShouldForwardProp = (prop) => shouldForwardProp(prop) && prop !== 'classes'; - -export const slotShouldForwardProp = shouldForwardProp; +export { default as slotShouldForwardProp } from './slotShouldForwardProp'; +export { default as rootShouldForwardProp } from './rootShouldForwardProp'; const styled = createStyled({ themeId: THEME_ID,