Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 69 additions & 20 deletions packages/react-core/src/components/Slider/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,18 @@ export interface SliderProps extends Omit<React.HTMLProps<HTMLDivElement>, 'onCh
inputAriaLabel?: string;
/** Text label that is place after the input field. */
inputLabel?: string | number;
/** Position of the input. */
inputPosition?: 'aboveThumb' | 'right';
/** Position of the input. Note "right" is deprecated. Use "end" instead*/
inputPosition?: 'aboveThumb' | 'right' | 'end';
/** Value displayed in the input field. */
inputValue?: number;
/** Adds disabled styling, and disables the slider and the input component if present. */
isDisabled?: boolean;
/** Flag to show value input field. */
isInputVisible?: boolean;
/** Actions placed to the left of the slider. */
/** @deprecated Use startActions instead. Actions placed at the start of the slider. */
leftActions?: React.ReactNode;
/** Actions placed at the start of the slider. */
startActions?: React.ReactNode;
/** The maximum permitted value. */
max?: number;
/** The minimum permitted value. */
Expand All @@ -67,8 +69,10 @@ export interface SliderProps extends Omit<React.HTMLProps<HTMLDivElement>, 'onCh
inputValue?: number,
setLocalInputValue?: React.Dispatch<React.SetStateAction<number>>
) => void;
/** Actions placed to the right of the slider. */
/** @deprecated Use endActions instead. Actions placed to the right of the slider. */
rightActions?: React.ReactNode;
/** Actions placed at the end of the slider. */
endActions?: React.ReactNode;
/** Flag to indicate if boundaries should be shown for slider that does not have custom steps. */
showBoundaries?: boolean;
/** Flag to indicate if ticks should be shown for slider that does not have custom steps. */
Expand All @@ -95,10 +99,12 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
inputAriaLabel = 'Slider value input',
thumbAriaLabel = 'Value',
hasTooltipOverThumb = false,
inputPosition = 'right',
inputPosition = 'end',
onChange,
leftActions,
startActions,
rightActions,
endActions,
step = 1,
min = 0,
max = 100,
Expand All @@ -114,6 +120,12 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
const [localValue, setValue] = useState(value);
const [localInputValue, setLocalInputValue] = useState(inputValue);

let isRTL: boolean;

React.useEffect(() => {
isRTL = window.getComputedStyle(sliderRailRef.current).getPropertyValue('direction') === 'rtl';
});

React.useEffect(() => {
setValue(value);
}, [value]);
Expand Down Expand Up @@ -182,7 +194,11 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
e.stopPropagation();
e.preventDefault();

diff = e.clientX - thumbRef.current.getBoundingClientRect().left;
if (isRTL) {
diff = thumbRef.current.getBoundingClientRect().right - e.clientX;
} else {
diff = e.clientX - thumbRef.current.getBoundingClientRect().left;
}

document.addEventListener('mousemove', callbackThumbMove);
document.addEventListener('mouseup', callbackThumbUp);
Expand All @@ -191,7 +207,11 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
const handleTouchStart = (e: React.TouchEvent) => {
e.stopPropagation();

diff = e.touches[0].clientX - thumbRef.current.getBoundingClientRect().left;
if (isRTL) {
diff = thumbRef.current.getBoundingClientRect().right - e.touches[0].clientX;
} else {
diff = e.touches[0].clientX - thumbRef.current.getBoundingClientRect().left;
}

document.addEventListener('touchmove', callbackThumbMove, { passive: false });
document.addEventListener('touchend', callbackThumbUp);
Expand All @@ -216,8 +236,13 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
}

const clientPosition = e.touches && e.touches.length ? e.touches[0].clientX : e.clientX;
let newPosition;

let newPosition = clientPosition - diff - sliderRailRef.current.getBoundingClientRect().left;
if (isRTL) {
newPosition = sliderRailRef.current.getBoundingClientRect().right - clientPosition - diff;
} else {
newPosition = clientPosition - diff - sliderRailRef.current.getBoundingClientRect().left;
}

const end = sliderRailRef.current.offsetWidth - thumbRef.current.offsetWidth;

Expand Down Expand Up @@ -288,21 +313,43 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
if (!areCustomStepsContinuous && customSteps) {
const stepIndex = customSteps.findIndex((stepObj) => stepObj.value === localValue);
if (key === 'ArrowRight') {
if (stepIndex + 1 < customSteps.length) {
{
newValue = customSteps[stepIndex + 1].value;
if (isRTL) {
if (stepIndex - 1 >= 0) {
newValue = customSteps[stepIndex - 1].value;
}
} else {
if (stepIndex + 1 < customSteps.length) {
{
newValue = customSteps[stepIndex + 1].value;
}
}
}
} else if (key === 'ArrowLeft') {
if (stepIndex - 1 >= 0) {
newValue = customSteps[stepIndex - 1].value;
if (isRTL) {
if (stepIndex + 1 < customSteps.length) {
{
newValue = customSteps[stepIndex + 1].value;
}
}
} else {
if (stepIndex - 1 >= 0) {
newValue = customSteps[stepIndex - 1].value;
}
}
}
} else {
if (key === 'ArrowRight') {
newValue = localValue + step <= max ? localValue + step : max;
if (isRTL) {
newValue = localValue - step >= min ? localValue - step : min;
} else {
newValue = localValue + step <= max ? localValue + step : max;
}
} else if (key === 'ArrowLeft') {
newValue = localValue - step >= min ? localValue - step : min;
if (isRTL) {
newValue = localValue + step <= max ? localValue + step : max;
} else {
newValue = localValue - step >= min ? localValue - step : min;
}
}
}

Expand Down Expand Up @@ -347,8 +394,8 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
for (let i = min; i <= max; i = i + step) {
const stepValue = getStepValue(i, min, max);

// If we boundaries but not ticks just generate the needed steps
// so that we don't pullute them DOM with empty divs
// If boundaries but not ticks just generate the needed steps
// so that we don't pollute them DOM with empty divs
if (!showTicks && showBoundaries && i !== min && i !== max) {
continue;
}
Expand Down Expand Up @@ -394,7 +441,7 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
style={{ ...style, ...inputStyle }}
{...props}
>
{leftActions && <div className={css(styles.sliderActions)}>{leftActions}</div>}
{(leftActions || startActions) && <div className={css(styles.sliderActions)}>{leftActions || startActions}</div>}
<div className={css(styles.sliderMain)}>
<div className={css(styles.sliderRail)} ref={sliderRailRef} onClick={!isDisabled ? onSliderRailClick : null}>
<div className={css(styles.sliderRailTrack)} />
Expand Down Expand Up @@ -434,8 +481,10 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
<div className={css(styles.sliderValue, styles.modifiers.floating)}>{displayInput()}</div>
)}
</div>
{isInputVisible && inputPosition === 'right' && <div className={css(styles.sliderValue)}>{displayInput()}</div>}
{rightActions && <div className={css(styles.sliderActions)}>{rightActions}</div>}
{isInputVisible && (inputPosition === 'right' || inputPosition === 'end') && (
<div className={css(styles.sliderValue)}>{displayInput()}</div>
)}
{(rightActions || endActions) && <div className={css(styles.sliderActions)}>{rightActions || endActions}</div>}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('slider', () => {

test('renders slider with input', () => {
const { asFragment } = render(
<Slider value={50} isInputVisible inputValue={50} inputLabel="%" inputPosition="right" />
<Slider value={50} isInputVisible inputValue={50} inputLabel="%" inputPosition="end" />
);
expect(asFragment()).toMatchSnapshot();
});
Expand All @@ -62,8 +62,8 @@ describe('slider', () => {
const { asFragment } = render(
<Slider
value={50}
leftActions={<Button variant="plain" aria-label="Minus" />}
rightActions={<Button variant="plain" aria-label="Plus" />}
startActions={<Button variant="plain" aria-label="Minus" />}
endActions={<Button variant="plain" aria-label="Plus" />}
/>
);
expect(asFragment()).toMatchSnapshot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ export const SliderActions: React.FunctionComponent = () => {
<Slider
value={value1}
onChange={onChange1}
leftActions={
startActions={
<Button variant="plain" aria-label="Minus" onClick={onMinusClick}>
<MinusIcon />
</Button>
}
rightActions={
endActions={
<Button variant="plain" aria-label="Plus" onClick={onPlusClick}>
<PlusIcon />
</Button>
Expand All @@ -85,7 +85,7 @@ export const SliderActions: React.FunctionComponent = () => {
inputLabel="%"
isInputVisible
isDisabled={isDisabled}
rightActions={buildAction(isDisabled)}
endActions={buildAction(isDisabled)}
/>
</>
);
Expand Down