Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Slider] Allow disabling the left and right thumbs swap #25547

Merged
merged 6 commits into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/pages/api-docs/slider-unstyled.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"type": { "name": "union", "description": "Array&lt;number&gt;<br>&#124;&nbsp;number" }
},
"disabled": { "type": { "name": "bool" } },
"disableSwap": { "type": { "name": "bool" } },
"getAriaLabel": { "type": { "name": "func" } },
"getAriaValueText": { "type": { "name": "func" } },
"isRtl": { "type": { "name": "bool" } },
Expand Down
1 change: 1 addition & 0 deletions docs/pages/api-docs/slider.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"type": { "name": "union", "description": "Array&lt;number&gt;<br>&#124;&nbsp;number" }
},
"disabled": { "type": { "name": "bool" } },
"disableSwap": { "type": { "name": "bool" } },
"getAriaLabel": { "type": { "name": "func" } },
"getAriaValueText": { "type": { "name": "func" } },
"isRtl": { "type": { "name": "bool" } },
Expand Down
46 changes: 46 additions & 0 deletions docs/src/pages/components/slider/RangeSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@ export default function RangeSlider() {
setValue(newValue);
};

const [value2, setValue2] = React.useState([20, 37]);

const handleChange2 = (event, newValue) => {
if (typeof newValue !== 'number' && newValue[1] - newValue[0] >= 10) {
setValue2(newValue);
}
};

const [value3, setValue3] = React.useState([20, 37]);

const handleChange3 = (event, newValue, activeThumb) => {
if (typeof newValue !== 'number') {
if (newValue[1] - newValue[0] < 10) {
if (activeThumb === 0) {
setValue3([newValue[0], newValue[0] + 10]);
} else {
setValue3([newValue[1] - 10, newValue[1]]);
}
} else {
setValue3(newValue);
}
}
};

return (
<Box sx={{ width: 300 }}>
<Typography id="range-slider-demo" gutterBottom>
Expand All @@ -26,6 +50,28 @@ export default function RangeSlider() {
aria-labelledby="range-slider-demo"
getAriaValueText={valuetext}
/>
<Typography id="range-slider-demo" gutterBottom>
Minimum distance
</Typography>
<Slider
value={value2}
onChange={handleChange2}
valueLabelDisplay="auto"
aria-labelledby="range-slider-demo"
getAriaValueText={valuetext}
disableSwap
/>
<Typography id="range-slider-demo" gutterBottom>
Minimum distance shift
</Typography>
<Slider
value={value3}
onChange={handleChange3}
valueLabelDisplay="auto"
aria-labelledby="range-slider-demo"
getAriaValueText={valuetext}
disableSwap
/>
</Box>
);
}
50 changes: 50 additions & 0 deletions docs/src/pages/components/slider/RangeSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,34 @@ export default function RangeSlider() {
setValue(newValue as number[]);
};

const [value2, setValue2] = React.useState<number[]>([20, 37]);

const handleChange2 = (event: Event, newValue: number | number[]) => {
if (typeof newValue !== 'number' && newValue[1] - newValue[0] >= 10) {
setValue2(newValue as number[]);
}
};

const [value3, setValue3] = React.useState<number[]>([20, 37]);

const handleChange3 = (
event: Event,
newValue: number | number[],
activeThumb: number,
) => {
if (typeof newValue !== 'number') {
if (newValue[1] - newValue[0] < 10) {
if (activeThumb === 0) {
setValue3([newValue[0], newValue[0] + 10]);
} else {
setValue3([newValue[1] - 10, newValue[1]]);
}
} else {
setValue3(newValue as number[]);
}
}
};

return (
<Box sx={{ width: 300 }}>
<Typography id="range-slider-demo" gutterBottom>
Expand All @@ -26,6 +54,28 @@ export default function RangeSlider() {
aria-labelledby="range-slider-demo"
getAriaValueText={valuetext}
/>
<Typography id="range-slider-demo" gutterBottom>
Minimum distance
</Typography>
<Slider
value={value2}
onChange={handleChange2}
valueLabelDisplay="auto"
aria-labelledby="range-slider-demo"
getAriaValueText={valuetext}
disableSwap
/>
<Typography id="range-slider-demo" gutterBottom>
Minimum distance shift
</Typography>
<Slider
value={value3}
onChange={handleChange3}
valueLabelDisplay="auto"
aria-labelledby="range-slider-demo"
getAriaValueText={valuetext}
disableSwap
/>
</Box>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
"componentsProps": "The props used for each slot inside the Slider.",
"defaultValue": "The default value. Use when the component is not controlled.",
"disabled": "If <code>true</code>, the component is disabled.",
"disableSwap": "If <code>true</code>, the active thumb doesn&#39;t swap.",
"getAriaLabel": "Accepts a function which returns a string value that provides a user-friendly name for the thumb labels of the slider.<br><br><strong>Signature:</strong><br><code>function(index: number) =&gt; string</code><br><em>index:</em> The thumb label&#39;s index to format.",
"getAriaValueText": "Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider.<br><br><strong>Signature:</strong><br><code>function(value: number, index: number) =&gt; string</code><br><em>value:</em> The thumb label&#39;s value to format.<br><em>index:</em> The thumb label&#39;s index to format.",
"isRtl": "Indicates whether the theme context has rtl direction. It is set automatically.",
"marks": "Marks indicate predetermined values to which the user can move the slider. If <code>true</code> the marks are spaced according the value of the <code>step</code> prop. If an array, it should contain objects with <code>value</code> and an optional <code>label</code> keys.",
"max": "The maximum allowed value of the slider. Should not be equal to min.",
"min": "The minimum allowed value of the slider. Should not be equal to max.",
"name": "Name attribute of the hidden <code>input</code> element.",
"onChange": "Callback function that is fired when the slider&#39;s value changed.<br><br><strong>Signature:</strong><br><code>function(event: object, value: number \\| number[]) =&gt; void</code><br><em>event:</em> The event source of the callback. You can pull out the new value by accessing <code>event.target.value</code> (any). <strong>Warning</strong>: This is a generic event not a change event.<br><em>value:</em> The new value.",
"onChange": "Callback function that is fired when the slider&#39;s value changed.<br><br><strong>Signature:</strong><br><code>function(event: object, value: number \\| number[], activeThumb: number) =&gt; void</code><br><em>event:</em> The event source of the callback. You can pull out the new value by accessing <code>event.target.value</code> (any). <strong>Warning</strong>: This is a generic event not a change event.<br><em>value:</em> The new value.<br><em>activeThumb:</em> Index of the currently moved thumb.",
"onChangeCommitted": "Callback function that is fired when the <code>mouseup</code> is triggered.<br><br><strong>Signature:</strong><br><code>function(event: object, value: number \\| number[]) =&gt; void</code><br><em>event:</em> The event source of the callback. <strong>Warning</strong>: This is a generic event not a change event.<br><em>value:</em> The new value.",
"orientation": "The component orientation.",
"scale": "A transformation function, to change the scale of the slider.",
Expand Down
3 changes: 2 additions & 1 deletion docs/translations/api-docs/slider/slider.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
"componentsProps": "The props used for each slot inside the Slider.",
"defaultValue": "The default value. Use when the component is not controlled.",
"disabled": "If <code>true</code>, the component is disabled.",
"disableSwap": "If <code>true</code>, the active thumb doesn&#39;t swap.",
"getAriaLabel": "Accepts a function which returns a string value that provides a user-friendly name for the thumb labels of the slider.<br><br><strong>Signature:</strong><br><code>function(index: number) =&gt; string</code><br><em>index:</em> The thumb label&#39;s index to format.",
"getAriaValueText": "Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider.<br><br><strong>Signature:</strong><br><code>function(value: number, index: number) =&gt; string</code><br><em>value:</em> The thumb label&#39;s value to format.<br><em>index:</em> The thumb label&#39;s index to format.",
"isRtl": "Indicates whether the theme context has rtl direction. It is set automatically.",
"marks": "Marks indicate predetermined values to which the user can move the slider. If <code>true</code> the marks are spaced according the value of the <code>step</code> prop. If an array, it should contain objects with <code>value</code> and an optional <code>label</code> keys.",
"max": "The maximum allowed value of the slider. Should not be equal to min.",
"min": "The minimum allowed value of the slider. Should not be equal to max.",
"name": "Name attribute of the hidden <code>input</code> element.",
"onChange": "Callback function that is fired when the slider&#39;s value changed.<br><br><strong>Signature:</strong><br><code>function(event: object, value: number \\| number[]) =&gt; void</code><br><em>event:</em> The event source of the callback. You can pull out the new value by accessing <code>event.target.value</code> (any). <strong>Warning</strong>: This is a generic event not a change event.<br><em>value:</em> The new value.",
"onChange": "Callback function that is fired when the slider&#39;s value changed.<br><br><strong>Signature:</strong><br><code>function(event: object, value: number \\| number[], activeThumb: number) =&gt; void</code><br><em>event:</em> The event source of the callback. You can pull out the new value by accessing <code>event.target.value</code> (any). <strong>Warning</strong>: This is a generic event not a change event.<br><em>value:</em> The new value.<br><em>activeThumb:</em> Index of the currently moved thumb.",
"onChangeCommitted": "Callback function that is fired when the <code>mouseup</code> is triggered.<br><br><strong>Signature:</strong><br><code>function(event: object, value: number \\| number[]) =&gt; void</code><br><em>event:</em> The event source of the callback. <strong>Warning</strong>: This is a generic event not a change event.<br><em>value:</em> The new value.",
"orientation": "The component orientation.",
"scale": "A transformation function, to change the scale of the slider.",
Expand Down
5 changes: 5 additions & 0 deletions framer/Material-UI.framerfx/code/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import MuiSlider from '@material-ui/core/Slider';
interface Props {
color: 'primary' | 'secondary';
disabled?: boolean;
disableSwap?: boolean;
max?: number;
min?: number;
orientation?: 'horizontal' | 'vertical';
Expand Down Expand Up @@ -37,6 +38,10 @@ addPropertyControls(Slider, {
type: ControlType.Boolean,
title: 'Disabled',
},
disableSwap: {
type: ControlType.Boolean,
title: 'Disable swap',
},
max: {
type: ControlType.Number,
title: 'Max',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ export interface SliderUnstyledTypeMap<P = {}, D extends React.ElementType = 'sp
* @default false
*/
disabled?: boolean;
/**
* If `true`, the active thumb doesn't swap.
* @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.
*
Expand Down Expand Up @@ -184,8 +189,9 @@ export interface SliderUnstyledTypeMap<P = {}, D extends React.ElementType = 'sp
* 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[]) => void;
onChange?: (event: Event, value: number | number[], activeThumb: number) => void;
/**
* Callback function that is fired when the `mouseup` is triggered.
*
Expand Down
33 changes: 25 additions & 8 deletions packages/material-ui-unstyled/src/SliderUnstyled/SliderUnstyled.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {
component = 'span',
classes: classesProp,
defaultValue,
disableSwap = false,
disabled = false,
getAriaLabel,
getAriaValueText,
Expand Down Expand Up @@ -235,7 +236,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {

const handleChange =
onChange &&
((event, value) => {
((event, value, thumbIndex) => {
// Redefine target to allow name and value to be read.
// This allows seamless integration with the most popular form libraries.
// https://github.com/mui-org/material-ui/issues/13485#issuecomment-676048492
Expand All @@ -248,7 +249,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {
value: { value, name },
});

onChange(clonedEvent, value);
onChange(clonedEvent, value, thumbIndex);
});

const range = Array.isArray(valueDerived);
Expand Down Expand Up @@ -351,7 +352,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {
setFocusVisible(index);

if (handleChange) {
handleChange(event, newValue);
handleChange(event, newValue, index);
}

if (onChangeCommitted) {
Expand Down Expand Up @@ -400,15 +401,25 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {
activeIndex = previousIndex.current;
}

if (disableSwap) {
newValue = clamp(
newValue,
values2[activeIndex - 1] || -Infinity,
values2[activeIndex + 1] || Infinity,
);
}

const previousValue = newValue;
newValue = setValueIndex({
values: values2,
source,
newValue,
index: activeIndex,
}).sort(asc);
activeIndex = newValue.indexOf(previousValue);
previousIndex.current = activeIndex;
if (!(disableSwap && move)) {
activeIndex = newValue.indexOf(previousValue);
previousIndex.current = activeIndex;
}
}

return { newValue, activeIndex };
Expand Down Expand Up @@ -445,7 +456,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {
}

if (handleChange) {
handleChange(nativeEvent, newValue);
handleChange(nativeEvent, newValue, activeIndex);
}
});

Expand Down Expand Up @@ -492,7 +503,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {
setValueState(newValue);

if (handleChange) {
handleChange(nativeEvent, newValue);
handleChange(nativeEvent, newValue, activeIndex);
}

moveCount.current = 0;
Expand Down Expand Up @@ -549,7 +560,7 @@ const SliderUnstyled = React.forwardRef(function SliderUnstyled(props, ref) {
setValueState(newValue);

if (handleChange) {
handleChange(event, newValue);
handleChange(event, newValue, activeIndex);
}

moveCount.current = 0;
Expand Down Expand Up @@ -856,6 +867,11 @@ SliderUnstyled.propTypes /* remove-proptypes */ = {
* @default false
*/
disabled: PropTypes.bool,
/**
* If `true`, the active thumb doesn't swap.
* @default false
*/
disableSwap: PropTypes.bool,
/**
* Accepts a function which returns a string value that provides a user-friendly name for the thumb labels of the slider.
*
Expand Down Expand Up @@ -914,6 +930,7 @@ SliderUnstyled.propTypes /* remove-proptypes */ = {
* 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: PropTypes.func,
/**
Expand Down
6 changes: 6 additions & 0 deletions packages/material-ui/src/Slider/Slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,11 @@ Slider.propTypes /* remove-proptypes */ = {
* @default false
*/
disabled: PropTypes.bool,
/**
* If `true`, the active thumb doesn't swap.
* @default false
*/
disableSwap: PropTypes.bool,
/**
* Accepts a function which returns a string value that provides a user-friendly name for the thumb labels of the slider.
*
Expand Down Expand Up @@ -560,6 +565,7 @@ Slider.propTypes /* remove-proptypes */ = {
* 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: PropTypes.func,
/**
Expand Down