Skip to content

Commit

Permalink
[Slider] Allow disabling the left and right thumbs swap (#25547)
Browse files Browse the repository at this point in the history
  • Loading branch information
michal-perlakowski committed Mar 31, 2021
1 parent fa475e2 commit 7729b92
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 14 deletions.
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
73 changes: 73 additions & 0 deletions docs/src/pages/components/slider/MinimumDistanceSlider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as React from 'react';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import Slider from '@material-ui/core/Slider';

function valuetext(value) {
return `${value}°C`;
}

const minDistance = 10;

export default function MinimumDistanceSlider() {
const [value1, setValue1] = React.useState([20, 37]);

const handleChange1 = (event, newValue, activeThumb) => {
if (!Array.isArray(newValue)) {
return;
}

if (activeThumb === 0) {
setValue1([Math.min(newValue[0], value1[1] - minDistance), value1[1]]);
} else {
setValue1([value1[0], Math.max(newValue[1], value1[0] + minDistance)]);
}
};

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

const handleChange2 = (event, newValue, activeThumb) => {
if (!Array.isArray(newValue)) {
return;
}

if (newValue[1] - newValue[0] < minDistance) {
if (activeThumb === 0) {
const clamped = Math.min(newValue[0], 100 - minDistance);
setValue2([clamped, clamped + minDistance]);
} else {
const clamped = Math.max(newValue[1], minDistance);
setValue2([clamped - minDistance, clamped]);
}
} else {
setValue2(newValue);
}
};

return (
<Box sx={{ width: 300 }}>
<Typography id="minimum-distance-demo" gutterBottom>
Minimum distance
</Typography>
<Slider
value={value1}
onChange={handleChange1}
valueLabelDisplay="auto"
aria-labelledby="minimum-distance-demo"
getAriaValueText={valuetext}
disableSwap
/>
<Typography id="minimum-distance-shift-demo" gutterBottom>
Minimum distance shift
</Typography>
<Slider
value={value2}
onChange={handleChange2}
valueLabelDisplay="auto"
aria-labelledby="minimum-distance-shift-demo"
getAriaValueText={valuetext}
disableSwap
/>
</Box>
);
}
81 changes: 81 additions & 0 deletions docs/src/pages/components/slider/MinimumDistanceSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as React from 'react';
import Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import Slider from '@material-ui/core/Slider';

function valuetext(value: number) {
return `${value}°C`;
}

const minDistance = 10;

export default function MinimumDistanceSlider() {
const [value1, setValue1] = React.useState<number[]>([20, 37]);

const handleChange1 = (
event: Event,
newValue: number | number[],
activeThumb: number,
) => {
if (!Array.isArray(newValue)) {
return;
}

if (activeThumb === 0) {
setValue1([Math.min(newValue[0], value1[1] - minDistance), value1[1]]);
} else {
setValue1([value1[0], Math.max(newValue[1], value1[0] + minDistance)]);
}
};

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

const handleChange2 = (
event: Event,
newValue: number | number[],
activeThumb: number,
) => {
if (!Array.isArray(newValue)) {
return;
}

if (newValue[1] - newValue[0] < minDistance) {
if (activeThumb === 0) {
const clamped = Math.min(newValue[0], 100 - minDistance);
setValue2([clamped, clamped + minDistance]);
} else {
const clamped = Math.max(newValue[1], minDistance);
setValue2([clamped - minDistance, clamped]);
}
} else {
setValue2(newValue as number[]);
}
};

return (
<Box sx={{ width: 300 }}>
<Typography id="minimum-distance-demo" gutterBottom>
Minimum distance
</Typography>
<Slider
value={value1}
onChange={handleChange1}
valueLabelDisplay="auto"
aria-labelledby="minimum-distance-demo"
getAriaValueText={valuetext}
disableSwap
/>
<Typography id="minimum-distance-shift-demo" gutterBottom>
Minimum distance shift
</Typography>
<Slider
value={value2}
onChange={handleChange2}
valueLabelDisplay="auto"
aria-labelledby="minimum-distance-shift-demo"
getAriaValueText={valuetext}
disableSwap
/>
</Box>
);
}
10 changes: 9 additions & 1 deletion docs/src/pages/components/slider/slider.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ The slider can be used to set the start and end of a range by supplying an array

{{"demo": "pages/components/slider/RangeSlider.js"}}

### Minimum distance

You can enforce a minimum distance between values in the `onChange` event handler.
By default, when you move the pointer over a thumb while dragging another thumb, the active thumb will swap to the hovered thumb. You can disable this behavior with the `disableSwap` prop.
If you want the range to shift when reaching minimum distance, you can utilize the `activeThumb` parameter in `onChange`.

{{"demo": "pages/components/slider/MinimumDistanceSlider.js"}}

## Slider with input field

In this example an input allows a discrete value to be set.
In this example, an input allows a discrete value to be set.

{{"demo": "pages/components/slider/InputSlider.js"}}

Expand Down
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 when moving pointer over a thumb while dragging another thumb.",
"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 when moving pointer over a thumb while dragging another thumb.",
"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 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.
*
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

0 comments on commit 7729b92

Please sign in to comment.