;
+ /**
+ * The components used for each slot inside the Slider.
+ * Either a string to use a HTML element or a component.
+ * @default {}
+ */
+ components?: {
+ Root?: React.ElementType;
+ Track?: React.ElementType;
+ Rail?: React.ElementType;
+ Thumb?: React.ElementType;
+ Mark?: React.ElementType;
+ MarkLabel?: React.ElementType;
+ ValueLabel?: React.ElementType;
+ Input?: React.ElementType;
+ };
+ /**
+ * The props used for each slot inside the Slider.
+ * @default {}
+ */
+ componentsProps?: {
+ root?: SlotComponentProps<
+ 'span',
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
+ track?: SlotComponentProps<
+ 'span',
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
+ rail?: SlotComponentProps<
+ 'span',
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
+ thumb?: SlotComponentProps<
+ 'span',
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
+ mark?: SlotComponentProps<
+ 'span',
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
+ markLabel?: SlotComponentProps<
+ 'span',
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
+ valueLabel?: SlotComponentProps<
+ typeof SliderValueLabelUnstyled,
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
+
+ input?: SlotComponentProps<
+ 'input',
+ SliderUnstyledComponentsPropsOverrides,
+ SliderUnstyledOwnerState
+ >;
};
+ /**
+ * The default value. Use when the component is not controlled.
+ */
+ defaultValue?: number | number[];
+ /**
+ * If `true`, the component is disabled.
+ * @default false
+ */
+ disabled?: boolean;
+ /**
+ * If `true`, the active thumb doesn't swap when moving pointer over a thumb while dragging another thumb.
+ * @default false
+ */
+ disableSwap?: boolean;
+ /**
+ * Accepts a function which returns a string value that provides a user-friendly name for the thumb labels of the slider.
+ * This is important for screen reader users.
+ * @param {number} index The thumb label's index to format.
+ * @returns {string}
+ */
+ getAriaLabel?: (index: number) => string;
+ /**
+ * Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider.
+ * This is important for screen reader users.
+ * @param {number} value The thumb label's value to format.
+ * @param {number} index The thumb label's index to format.
+ * @returns {string}
+ */
+ getAriaValueText?: (value: number, index: number) => string;
+ /**
+ * Indicates whether the theme context has rtl direction. It is set automatically.
+ * @default false
+ */
+ isRtl?: boolean;
+ /**
+ * Marks indicate predetermined values to which the user can move the slider.
+ * If `true` the marks are spaced according the value of the `step` prop.
+ * If an array, it should contain objects with `value` and an optional `label` keys.
+ * @default false
+ */
+ marks?: boolean | Mark[];
+ /**
+ * The maximum allowed value of the slider.
+ * Should not be equal to min.
+ * @default 100
+ */
+ max?: number;
+ /**
+ * The minimum allowed value of the slider.
+ * Should not be equal to max.
+ * @default 0
+ */
+ min?: number;
+ /**
+ * Name attribute of the hidden `input` element.
+ */
+ name?: string;
+ /**
+ * Callback function that is fired when the slider's value changed.
+ *
+ * @param {Event} event The event source of the callback.
+ * You can pull out the new value by accessing `event.target.value` (any).
+ * **Warning**: This is a generic event not a change event.
+ * @param {number | number[]} value The new value.
+ * @param {number} activeThumb Index of the currently moved thumb.
+ */
+ onChange?: (event: Event, value: number | number[], activeThumb: number) => void;
+ /**
+ * Callback function that is fired when the `mouseup` is triggered.
+ *
+ * @param {React.SyntheticEvent | Event} event The event source of the callback. **Warning**: This is a generic event not a change event.
+ * @param {number | number[]} value The new value.
+ */
+ onChangeCommitted?: (event: React.SyntheticEvent | Event, value: number | number[]) => void;
+ /**
+ * The component orientation.
+ * @default 'horizontal'
+ */
+ orientation?: 'horizontal' | 'vertical';
+ /**
+ * A transformation function, to change the scale of the slider.
+ * @default (x) => x
+ */
+ scale?: (value: number) => number;
+ /**
+ * The granularity with which the slider can step through values. (A "discrete" slider.)
+ * The `min` prop serves as the origin for the valid values.
+ * We recommend (max - min) to be evenly divisible by the step.
+ *
+ * When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop.
+ * @default 1
+ */
+ step?: number | null;
+ /**
+ * Tab index attribute of the hidden `input` element.
+ */
+ tabIndex?: number;
+ /**
+ * The track presentation:
+ *
+ * - `normal` the track will render a bar representing the slider value.
+ * - `inverted` the track will render a bar representing the remaining slider value.
+ * - `false` the track will render without a bar.
+ * @default 'normal'
+ */
+ track?: 'normal' | false | 'inverted';
+ /**
+ * The value of the slider.
+ * For ranged sliders, provide an array with two values.
+ */
+ value?: number | number[];
+ /**
+ * Controls when the value label is displayed:
+ *
+ * - `auto` the value label will display when the thumb is hovered or focused.
+ * - `on` will display persistently.
+ * - `off` will never display.
+ * @default 'off'
+ */
+ valueLabelDisplay?: 'on' | 'auto' | 'off';
+ /**
+ * The format function the value label's value.
+ *
+ * When a function is provided, it should have the following signature:
+ *
+ * - {number} value The value label's value to format
+ * - {number} index The value label's index to format
+ * @default (x) => x
+ */
+ valueLabelFormat?: string | ((value: number, index: number) => React.ReactNode);
+}
+
+export interface SliderUnstyledTypeMap {
+ props: P & SliderUnstyledOwnProps;
defaultComponent: D;
}
@@ -250,6 +286,7 @@ export type SliderUnstyledRailSlotProps = {
export type SliderUnstyledThumbSlotProps = UseSliderThumbSlotProps & {
'data-index': number;
+ 'data-focusvisible': boolean;
children: React.ReactNode;
className?: string;
ownerState: SliderUnstyledOwnerState;
diff --git a/packages/mui-base/src/SliderUnstyled/useSlider.ts b/packages/mui-base/src/SliderUnstyled/useSlider.ts
index 6622a043d93282..c60319d32c643e 100644
--- a/packages/mui-base/src/SliderUnstyled/useSlider.ts
+++ b/packages/mui-base/src/SliderUnstyled/useSlider.ts
@@ -251,7 +251,7 @@ export default function useSlider(parameters: UseSliderParameters) {
onFocus: handleFocusVisible,
ref: focusVisibleRef,
} = useIsFocusVisible();
- const [focusVisible, setFocusVisible] = React.useState(-1);
+ const [focusedThumbIndex, setFocusedThumbIndex] = React.useState(-1);
const sliderRef = React.useRef();
const handleFocusRef = useForkRef(focusVisibleRef, sliderRef);
@@ -262,7 +262,7 @@ export default function useSlider(parameters: UseSliderParameters) {
const index = Number(event.currentTarget.getAttribute('data-index'));
handleFocusVisible(event);
if (isFocusVisibleRef.current === true) {
- setFocusVisible(index);
+ setFocusedThumbIndex(index);
}
setOpen(index);
otherHandlers?.onFocus?.(event);
@@ -271,7 +271,7 @@ export default function useSlider(parameters: UseSliderParameters) {
(otherHandlers: Record>) => (event: React.FocusEvent) => {
handleBlurVisible(event);
if (isFocusVisibleRef.current === false) {
- setFocusVisible(-1);
+ setFocusedThumbIndex(-1);
}
setOpen(-1);
otherHandlers?.onBlur?.(event);
@@ -290,8 +290,8 @@ export default function useSlider(parameters: UseSliderParameters) {
if (disabled && active !== -1) {
setActive(-1);
}
- if (disabled && focusVisible !== -1) {
- setFocusVisible(-1);
+ if (disabled && focusedThumbIndex !== -1) {
+ setFocusedThumbIndex(-1);
}
const createHandleHiddenInputChange =
@@ -344,7 +344,7 @@ export default function useSlider(parameters: UseSliderParameters) {
}
setValueState(newValue);
- setFocusVisible(index);
+ setFocusedThumbIndex(index);
if (handleChange) {
handleChange(event, newValue, index);
@@ -633,13 +633,10 @@ export default function useSlider(parameters: UseSliderParameters) {
onMouseLeave: createHandleMouseLeave(otherHandlers || {}),
};
- const mergedEventHandlers = {
+ return {
...otherHandlers,
...ownEventHandlers,
};
- return {
- ...mergedEventHandlers,
- };
};
const getHiddenInputProps = (
@@ -666,7 +663,7 @@ export default function useSlider(parameters: UseSliderParameters) {
type: 'range',
min: parameters.min,
max: parameters.max,
- step: parameters.step,
+ step: parameters.step ?? undefined,
disabled,
...mergedEventHandlers,
style: {
@@ -684,7 +681,7 @@ export default function useSlider(parameters: UseSliderParameters) {
axis: axis as keyof typeof axisProps,
axisProps,
dragging,
- focusVisible,
+ focusedThumbIndex,
getHiddenInputProps,
getRootProps,
getThumbProps,
diff --git a/packages/mui-base/src/SliderUnstyled/useSlider.types.ts b/packages/mui-base/src/SliderUnstyled/useSlider.types.ts
index 324b781fb3621f..2742b4c5795cf1 100644
--- a/packages/mui-base/src/SliderUnstyled/useSlider.types.ts
+++ b/packages/mui-base/src/SliderUnstyled/useSlider.types.ts
@@ -51,7 +51,7 @@ type UseSliderHiddenInputOwnProps = {
onBlur: React.FocusEventHandler;
onChange: React.ChangeEventHandler;
onFocus: React.FocusEventHandler;
- step?: number | null;
+ step?: number;
style: React.CSSProperties;
tabIndex?: number;
type?: React.InputHTMLAttributes['type'];
diff --git a/packages/mui-base/src/utils/index.ts b/packages/mui-base/src/utils/index.ts
index 628e03491d72b9..0823837f41e0de 100644
--- a/packages/mui-base/src/utils/index.ts
+++ b/packages/mui-base/src/utils/index.ts
@@ -2,5 +2,6 @@ export { default as appendOwnerState } from './appendOwnerState';
export { default as areArraysEqual } from './areArraysEqual';
export { default as extractEventHandlers } from './extractEventHandlers';
export { default as isHostComponent } from './isHostComponent';
+export { default as resolveComponentProps } from './resolveComponentProps';
export { default as useSlotProps } from './useSlotProps';
export * from './types';
diff --git a/packages/mui-base/src/utils/mergeSlotProps.test.ts b/packages/mui-base/src/utils/mergeSlotProps.test.ts
index 41f2af10344e7e..d2cb7270ba6338 100644
--- a/packages/mui-base/src/utils/mergeSlotProps.test.ts
+++ b/packages/mui-base/src/utils/mergeSlotProps.test.ts
@@ -75,6 +75,49 @@ describe('mergeSlotProps', () => {
expect(merged.props.className).to.contain('externalSlot');
});
+ it('merges the style props', () => {
+ const getSlotProps = () => ({
+ style: {
+ fontSize: '12px',
+ textAlign: 'center' as const,
+ },
+ });
+
+ const additionalProps = {
+ style: {
+ fontSize: '14px',
+ color: 'red',
+ },
+ };
+
+ const externalForwardedProps = {
+ style: {
+ fontWeight: 500,
+ },
+ };
+
+ const externalSlotProps = {
+ style: {
+ textDecoration: 'underline',
+ },
+ };
+
+ const merged = mergeSlotProps({
+ getSlotProps,
+ additionalProps,
+ externalForwardedProps,
+ externalSlotProps,
+ });
+
+ expect(merged.props.style).to.deep.equal({
+ textAlign: 'center',
+ color: 'red',
+ fontSize: '14px',
+ fontWeight: 500,
+ textDecoration: 'underline',
+ });
+ });
+
it('returns the ref returned from the getSlotProps function', () => {
const ref = React.createRef();
const getSlotProps = () => ({
diff --git a/packages/mui-base/src/utils/mergeSlotProps.ts b/packages/mui-base/src/utils/mergeSlotProps.ts
index 8092cd32d6fad1..59f7c4205b47f9 100644
--- a/packages/mui-base/src/utils/mergeSlotProps.ts
+++ b/packages/mui-base/src/utils/mergeSlotProps.ts
@@ -4,11 +4,9 @@ import { EventHandlers } from './types';
import extractEventHandlers from './extractEventHandlers';
import omitEventHandlers, { OmitEventHandlers } from './omitEventHandlers';
-export type WithClassName = T & {
+export type WithCommonProps = T & {
className?: string;
-};
-
-export type WithRef = T & {
+ style?: React.CSSProperties;
ref?: React.Ref;
};
@@ -23,20 +21,20 @@ export interface MergeSlotPropsParameters<
* It accepts the event handlers passed into the component by the user
* and is responsible for calling them where appropriate.
*/
- getSlotProps?: (other: EventHandlers) => WithClassName;
+ getSlotProps?: (other: EventHandlers) => WithCommonProps;
/**
* Props provided to the `componentsProps.*` of the unstyled component.
*/
- externalSlotProps?: WithClassName;
+ externalSlotProps?: WithCommonProps;
/**
* Extra props placed on the unstyled component that should be forwarded to the slot.
* This should usually be used only for the root slot.
*/
- externalForwardedProps?: WithClassName;
+ externalForwardedProps?: WithCommonProps;
/**
* Additional props to be placed on the slot.
*/
- additionalProps?: WithClassName;
+ additionalProps?: WithCommonProps;
/**
* Extra class name(s) to be placed on the slot.
*/
@@ -53,7 +51,7 @@ export type MergeSlotPropsResult<
SlotProps &
OmitEventHandlers &
OmitEventHandlers &
- AdditionalProps & { className?: string }
+ AdditionalProps & { className?: string; style?: React.CSSProperties }
>;
internalRef: React.Ref | undefined;
};
@@ -78,7 +76,7 @@ export default function mergeSlotProps<
AdditionalProps,
>(
parameters: MergeSlotPropsParameters<
- WithRef,
+ SlotProps,
ExternalForwardedProps,
ExternalSlotProps,
AdditionalProps
@@ -97,20 +95,29 @@ export default function mergeSlotProps<
additionalProps?.className,
);
+ const mergedStyle = {
+ ...additionalProps?.style,
+ ...externalForwardedProps?.style,
+ ...externalSlotProps?.style,
+ };
+
const props = {
...additionalProps,
...externalForwardedProps,
...externalSlotProps,
- className: joinedClasses,
- } as Simplify<
- SlotProps &
- ExternalForwardedProps &
- ExternalSlotProps &
- AdditionalProps & { className?: string }
- >;
-
- if (joinedClasses.length === 0) {
- delete props.className;
+ } as MergeSlotPropsResult<
+ SlotProps,
+ ExternalForwardedProps,
+ ExternalSlotProps,
+ AdditionalProps
+ >['props'];
+
+ if (joinedClasses.length > 0) {
+ props.className = joinedClasses;
+ }
+
+ if (Object.keys(mergedStyle).length > 0) {
+ props.style = mergedStyle;
}
return {
@@ -135,20 +142,31 @@ export default function mergeSlotProps<
internalSlotProps?.className,
);
+ const mergedStyle = {
+ ...internalSlotProps?.style,
+ ...additionalProps?.style,
+ ...externalForwardedProps?.style,
+ ...externalSlotProps?.style,
+ };
+
const props = {
...internalSlotProps,
...additionalProps,
...otherPropsWithoutEventHandlers,
...componentsPropsWithoutEventHandlers,
- className: joinedClasses,
- } as Simplify<
- SlotProps &
- OmitEventHandlers &
- OmitEventHandlers &
- AdditionalProps & { className?: string }
- >;
- if (joinedClasses.length === 0) {
- delete props.className;
+ } as MergeSlotPropsResult<
+ SlotProps,
+ ExternalForwardedProps,
+ ExternalSlotProps,
+ AdditionalProps
+ >['props'];
+
+ if (joinedClasses.length > 0) {
+ props.className = joinedClasses;
+ }
+
+ if (Object.keys(mergedStyle).length > 0) {
+ props.style = mergedStyle;
}
return {
diff --git a/packages/mui-base/src/utils/useSlotProps.ts b/packages/mui-base/src/utils/useSlotProps.ts
index e544b6ae089646..f219be2eff284a 100644
--- a/packages/mui-base/src/utils/useSlotProps.ts
+++ b/packages/mui-base/src/utils/useSlotProps.ts
@@ -1,7 +1,7 @@
import * as React from 'react';
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import appendOwnerState, { AppendOwnerStateReturnType } from './appendOwnerState';
-import mergeSlotProps, { MergeSlotPropsParameters, WithRef } from './mergeSlotProps';
+import mergeSlotProps, { MergeSlotPropsParameters, WithCommonProps } from './mergeSlotProps';
import resolveComponentProps from './resolveComponentProps';
export type UseSlotPropsParameters<
@@ -67,8 +67,8 @@ export default function useSlotProps<
ElementType,
SlotProps,
ExternalForwardedProps,
- WithRef,
- WithRef,
+ WithCommonProps,
+ WithCommonProps,
OwnerState
>,
) {
diff --git a/packages/mui-joy/src/Slider/Slider.tsx b/packages/mui-joy/src/Slider/Slider.tsx
index 7833cc462133c0..8f3cf7570e699b 100644
--- a/packages/mui-joy/src/Slider/Slider.tsx
+++ b/packages/mui-joy/src/Slider/Slider.tsx
@@ -7,21 +7,17 @@ import {
} from '@mui/utils';
import { OverridableComponent } from '@mui/types';
import { useSlider } from '@mui/base/SliderUnstyled';
+import { useSlotProps } from '@mui/base/utils';
import { useThemeProps, styled, Theme } from '../styles';
import sliderClasses, { getSliderUtilityClass } from './sliderClasses';
-import { SliderProps, SliderTypeMap } from './SliderProps';
-
-type OwnerState = SliderProps & {
- dragging: boolean;
- marked: boolean;
-};
+import { SliderProps, SliderTypeMap, SliderOwnerState } from './SliderProps';
const valueToPercent = (value: number, min: number, max: number) =>
((value - min) * 100) / (max - min);
const Identity = (x: any) => x;
-const useUtilityClasses = (ownerState: OwnerState) => {
+const useUtilityClasses = (ownerState: SliderOwnerState) => {
const { disabled, dragging, marked, orientation, track, color, size } = ownerState;
const slots = {
@@ -69,7 +65,7 @@ const SliderRoot = styled('span', {
name: 'JoySlider',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
-})<{ ownerState: SliderProps }>(({ theme, ownerState }) => {
+})<{ ownerState: SliderOwnerState }>(({ theme, ownerState }) => {
const getColorVariables = sliderColorVariables({ theme, ownerState });
return [
{
@@ -138,7 +134,7 @@ const SliderRail = styled('span', {
name: 'JoySlider',
slot: 'Rail',
overridesResolver: (props, styles) => styles.rail,
-})<{ ownerState: SliderProps }>(({ ownerState }) => [
+})<{ ownerState: SliderOwnerState }>(({ ownerState }) => [
{
display: 'block',
position: 'absolute',
@@ -171,7 +167,7 @@ const SliderTrack = styled('span', {
name: 'JoySlider',
slot: 'Track',
overridesResolver: (props, styles) => styles.track,
-})<{ ownerState: SliderProps }>(({ ownerState }) => {
+})<{ ownerState: SliderOwnerState }>(({ ownerState }) => {
return [
{
display: 'block',
@@ -207,7 +203,7 @@ const SliderThumb = styled('span', {
name: 'JoySlider',
slot: 'Thumb',
overridesResolver: (props, styles) => styles.thumb,
-})<{ ownerState: SliderProps }>(({ ownerState, theme }) => ({
+})<{ ownerState: SliderOwnerState }>(({ ownerState, theme }) => ({
position: 'absolute',
boxSizing: 'border-box',
outline: 0,
@@ -240,7 +236,7 @@ const SliderMark = styled('span', {
name: 'JoySlider',
slot: 'Mark',
overridesResolver: (props, styles) => styles.mark,
-})<{ ownerState: SliderProps & { percent: number } }>(({ ownerState }) => {
+})<{ ownerState: SliderOwnerState & { percent: number } }>(({ ownerState }) => {
return {
position: 'absolute',
width: 'var(--Slider-mark-size)',
@@ -274,7 +270,7 @@ const SliderValueLabel = styled('span', {
name: 'JoySlider',
slot: 'ValueLabel',
overridesResolver: (props, styles) => styles.valueLabel,
-})<{ ownerState: SliderProps }>(({ theme, ownerState }) => ({
+})<{ ownerState: SliderOwnerState }>(({ theme, ownerState }) => ({
...(ownerState.size === 'sm' && {
fontSize: theme.fontSize.xs,
lineHeight: theme.lineHeight.md,
@@ -334,7 +330,7 @@ const SliderMarkLabel = styled('span', {
name: 'JoySlider',
slot: 'MarkLabel',
overridesResolver: (props, styles) => styles.markLabel,
-})<{ ownerState: SliderProps }>(({ theme, ownerState }) => ({
+})<{ ownerState: SliderOwnerState }>(({ theme, ownerState }) => ({
fontFamily: theme.vars.fontFamily.body,
...(ownerState.size === 'sm' && {
fontSize: theme.vars.fontSize.xs,
@@ -362,7 +358,7 @@ const SliderInput = styled('input', {
name: 'JoySlider',
slot: 'Input',
overridesResolver: (props, styles) => styles.input,
-})<{ ownerState: SliderProps }>({});
+})<{ ownerState?: SliderOwnerState }>({});
const Slider = React.forwardRef(function Slider(inProps, ref) {
const props = useThemeProps({
@@ -373,7 +369,6 @@ const Slider = React.forwardRef(function Slider(inProps, ref) {
const {
'aria-label': ariaLabel,
'aria-valuetext': ariaValuetext,
- className,
component,
componentsProps = {},
classes: classesProp,
@@ -420,7 +415,7 @@ const Slider = React.forwardRef(function Slider(inProps, ref) {
valueLabelFormat,
color,
size,
- } as OwnerState;
+ } as SliderOwnerState;
const {
axisProps,
@@ -430,8 +425,8 @@ const Slider = React.forwardRef(function Slider(inProps, ref) {
open,
active,
axis,
+ focusedThumbIndex,
range,
- focusVisible,
dragging,
marks,
values,
@@ -447,29 +442,77 @@ const Slider = React.forwardRef(function Slider(inProps, ref) {
...axisProps[axis].leap(trackLeap),
};
- const hiddenInputProps = getHiddenInputProps();
-
const classes = useUtilityClasses(ownerState);
+ const rootProps = useSlotProps({
+ elementType: SliderRoot,
+ getSlotProps: getRootProps,
+ externalSlotProps: componentsProps.root,
+ externalForwardedProps: other,
+ additionalProps: {
+ as: component,
+ },
+ ownerState,
+ className: classes.root,
+ });
+
+ const railProps = useSlotProps({
+ elementType: SliderRail,
+ externalSlotProps: componentsProps.rail,
+ ownerState,
+ className: classes.rail,
+ });
+
+ const trackProps = useSlotProps({
+ elementType: SliderTrack,
+ externalSlotProps: componentsProps.track,
+ additionalProps: {
+ style: trackStyle,
+ },
+ ownerState,
+ className: classes.track,
+ });
+
+ const markProps = useSlotProps({
+ elementType: SliderMark,
+ externalSlotProps: componentsProps.mark,
+ ownerState,
+ className: classes.mark,
+ });
+
+ const markLabelProps = useSlotProps({
+ elementType: SliderMarkLabel,
+ externalSlotProps: componentsProps.markLabel,
+ ownerState,
+ className: classes.markLabel,
+ });
+
+ const thumbProps = useSlotProps({
+ elementType: SliderThumb,
+ getSlotProps: getThumbProps,
+ externalSlotProps: componentsProps.thumb,
+ ownerState,
+ className: classes.thumb,
+ });
+
+ const inputProps = useSlotProps({
+ elementType: SliderInput,
+ getSlotProps: getHiddenInputProps,
+ externalSlotProps: componentsProps.input,
+ ownerState,
+ });
+
+ const valueLabelProps = useSlotProps({
+ elementType: SliderValueLabel,
+ externalSlotProps: componentsProps.valueLabel,
+ ownerState,
+ className: classes.valueLabel,
+ });
+
return (
-
-
-
+
+
+
{marks
.filter((mark) => mark.value >= min && mark.value <= max)
.map((mark, index) => {
@@ -495,10 +538,10 @@ const Slider = React.forwardRef(function Slider(inProps, ref) {
@@ -506,10 +549,10 @@ const Slider = React.forwardRef(function Slider(inProps, ref) {
@@ -526,23 +569,18 @@ const Slider = React.forwardRef(function Slider(inProps, ref) {
- {/* @ts-expect-error TODO: revisit the null type in useSlider */}
{valueLabelDisplay !== 'off' ? (
;
+ track?: SlotComponentProps<'span', SliderComponentsPropsOverrides, SliderOwnerState>;
+ rail?: SlotComponentProps<'span', SliderComponentsPropsOverrides, SliderOwnerState>;
+ thumb?: SlotComponentProps<'span', SliderComponentsPropsOverrides, SliderOwnerState>;
+ mark?: SlotComponentProps<'span', SliderComponentsPropsOverrides, SliderOwnerState>;
+ markLabel?: SlotComponentProps<'span', SliderComponentsPropsOverrides, SliderOwnerState>;
+ valueLabel?: SlotComponentProps<
+ typeof SliderValueLabelUnstyled,
+ SliderComponentsPropsOverrides,
+ SliderOwnerState
+ >;
+
+ input?: SlotComponentProps<'input', SliderComponentsPropsOverrides, SliderOwnerState>;
+ };
+ /**
+ * The color of the component. It supports those theme colors that make sense for this component.
+ * @default 'primary'
+ */
+ color?: OverridableStringUnion;
+ /**
+ * The size of the component.
+ * It accepts theme values between 'sm' and 'lg'.
+ * @default 'md'
+ */
+ size?: OverridableStringUnion<'sm' | 'md' | 'lg', SliderPropsSizeOverrides>;
+ /**
+ * The system prop that allows defining system overrides as well as additional CSS styles.
+ */
+ sx?: SxProps;
+}
+
export type SliderTypeMap<
D extends React.ElementType = 'span',
P = {},
> = ExtendSliderUnstyledTypeMap<{
- props: P & {
- /**
- * The color of the component. It supports those theme colors that make sense for this component.
- * @default 'primary'
- */
- color?: OverridableStringUnion;
- /**
- * The size of the component.
- * It accepts theme values between 'sm' and 'lg'.
- * @default 'md'
- */
- size?: OverridableStringUnion<'sm' | 'md' | 'lg', SliderPropsSizeOverrides>;
- /**
- * The system prop that allows defining system overrides as well as additional CSS styles.
- */
- sx?: SxProps;
- };
+ props: P & SliderOwnProps;
defaultComponent: D;
}>;
@@ -45,3 +69,8 @@ export type SliderProps<
D extends React.ElementType = SliderTypeMap['defaultComponent'],
P = { component?: React.ElementType },
> = OverrideProps, D>;
+
+export type SliderOwnerState = SliderProps & {
+ dragging: boolean;
+ marked: boolean;
+};
diff --git a/packages/mui-material/src/Slider/Slider.js b/packages/mui-material/src/Slider/Slider.js
index 8453781872dff2..be38a6ebf021f2 100644
--- a/packages/mui-material/src/Slider/Slider.js
+++ b/packages/mui-material/src/Slider/Slider.js
@@ -618,24 +618,27 @@ Slider.propTypes /* remove-proptypes */ = {
* @default {}
*/
componentsProps: PropTypes.shape({
- input: PropTypes.object,
- mark: PropTypes.object,
- markLabel: PropTypes.object,
- rail: PropTypes.object,
- root: PropTypes.object,
- thumb: PropTypes.object,
- track: PropTypes.object,
- valueLabel: PropTypes.shape({
- children: PropTypes.element,
- className: PropTypes.string,
- components: PropTypes.shape({
- Root: PropTypes.elementType,
+ input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ mark: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ markLabel: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ rail: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ thumb: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ track: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ valueLabel: PropTypes.oneOfType([
+ PropTypes.func,
+ PropTypes.shape({
+ children: PropTypes.element,
+ className: PropTypes.string,
+ components: PropTypes.shape({
+ Root: PropTypes.elementType,
+ }),
+ open: PropTypes.bool,
+ style: PropTypes.object,
+ value: PropTypes.number,
+ valueLabelDisplay: PropTypes.oneOf(['auto', 'off', 'on']),
}),
- open: PropTypes.bool,
- style: PropTypes.object,
- value: PropTypes.number,
- valueLabelDisplay: PropTypes.oneOf(['auto', 'off', 'on']),
- }),
+ ]),
}),
/**
* The default value. Use when the component is not controlled.