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 all 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
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
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
@@ -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
@@ -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
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
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
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
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
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