Skip to content

Commit

Permalink
Merge pull request #282 from royeden/fix/slider
Browse files Browse the repository at this point in the history
fix(slider): fix `Home/End/PageDown/PageUp` keys
  • Loading branch information
fabien-ml authored Nov 3, 2023
2 parents 8e187e9 + 8ac24b2 commit c783342
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 55 deletions.
6 changes: 3 additions & 3 deletions apps/docs/src/examples/slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function MultipleThumbsExample() {
<Slider.Thumb class={style["SliderThumb"]}>
<Slider.Input />
</Slider.Thumb>
<Slider.Thumb class={`${style["SliderThumb"]} ${style["SliderThumb"]}`}>
<Slider.Thumb class={style["SliderThumb"]}>
<Slider.Input />
</Slider.Thumb>
</Slider.Track>
Expand Down Expand Up @@ -94,7 +94,7 @@ export function MinStepsBetweenExample() {
<Slider.Thumb class={style["SliderThumb"]}>
<Slider.Input />
</Slider.Thumb>
<Slider.Thumb class={`${style["SliderThumb"]} ${style["SliderThumb"]}`}>
<Slider.Thumb class={style["SliderThumb"]}>
<Slider.Input />
</Slider.Thumb>
</Slider.Track>
Expand Down Expand Up @@ -137,7 +137,7 @@ export function CustomValueLabelExample() {
<Slider.Thumb class={style["SliderThumb"]}>
<Slider.Input />
</Slider.Thumb>
<Slider.Thumb class={`${style["SliderThumb"]} ${style["SliderThumb"]}`}>
<Slider.Thumb class={style["SliderThumb"]}>
<Slider.Input />
</Slider.Thumb>
</Slider.Track>
Expand Down
27 changes: 13 additions & 14 deletions packages/core/src/slider/create-slider-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,15 @@ export function createSliderState(props: StateOpts): SliderState {
};

const getThumbMinValue = (index: number) => {
return index === 0 ? props.minValue!() : values()[index - 1];
return index === 0
? props.minValue!()
: values()[index - 1] + props.minStepsBetweenThumbs!() * props.step!();
};

const getThumbMaxValue = (index: number) => {
return index === values().length - 1 ? props.maxValue!() : values()[index + 1];
return index === values().length - 1
? props.maxValue!()
: values()[index + 1] - props.minStepsBetweenThumbs!() * props.step!();
};

const isThumbEditable = (index: number) => {
Expand Down Expand Up @@ -182,9 +186,8 @@ export function createSliderState(props: StateOpts): SliderState {
return clamp(getRoundedValue(val), props.minValue!(), props.maxValue!());
};

const incrementThumb = (index: number, stepSize = 1) => {
const s = Math.max(stepSize, props.step!());
const nextValue = values()[index] + s;
const snapThumbValue = (index: number, value: number) => {
const nextValue = values()[index] + value;
const nextValues = getNextSortedValues(values(), nextValue, index);
if (hasMinStepsBetweenValues(nextValues, props.minStepsBetweenThumbs!() * props.step!())) {
updateValue(
Expand All @@ -194,16 +197,12 @@ export function createSliderState(props: StateOpts): SliderState {
}
};

const incrementThumb = (index: number, stepSize = 1) => {
snapThumbValue(index, Math.max(stepSize, props.step!()));
};

const decrementThumb = (index: number, stepSize = 1) => {
const s = Math.max(stepSize, props.step!());
const nextValue = values()[index] - s;
const nextValues = getNextSortedValues(values(), nextValue, index);
if (hasMinStepsBetweenValues(nextValues, props.minStepsBetweenThumbs!() * props.step!())) {
updateValue(
index,
snapValueToStep(nextValue, props.minValue!(), props.maxValue!(), props.step!()),
);
}
snapThumbValue(index, -Math.max(stepSize, props.step!()));
};

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/slider/slider-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function SliderInput(props: SliderInputProps) {
type="range"
id={fieldProps.id()}
name={formControlContext.name()}
tabIndex={!context.state.isDisabled() ? 0 : undefined}
tabIndex={context.state.isDisabled() ? undefined : -1}
min={context.state.getThumbMinValue(thumb.index())}
max={context.state.getThumbMaxValue(thumb.index())}
step={context.state.step()}
Expand Down
64 changes: 28 additions & 36 deletions packages/core/src/slider/slider-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import { CollectionItemWithRef, createFormResetListener } from "../primitives";
import { createDomCollection } from "../primitives/create-dom-collection";
import { createSliderState } from "./create-slider-state";
import { SliderContext, SliderContextValue, SliderDataSet } from "./slider-context";
import { getClosestValueIndex, getNextSortedValues, hasMinStepsBetweenValues } from "./utils";
import {
getNextSortedValues,
hasMinStepsBetweenValues,
stopEventDefaultAndPropagation,
} from "./utils";

export interface GetValueLabelParams {
values: number[];
Expand Down Expand Up @@ -247,31 +251,36 @@ export function SliderRoot(props: SliderRootProps) {
if (activeThumb !== undefined) {
state.setThumbDragging(activeThumb, false);
local.onChangeEnd?.(state.values());
(thumbs()[activeThumb].ref() as HTMLElement).focus();
}
};

const onHomeKeyDown = () => {
!formControlContext.isDisabled() &&
state.focusedThumb() !== undefined &&
state.setThumbValue(0, state.getThumbMinValue(0));
const onHomeKeyDown = (event: KeyboardEvent) => {
const focusedThumb = state.focusedThumb();

if (!formControlContext.isDisabled() && focusedThumb !== undefined) {
stopEventDefaultAndPropagation(event);
state.setThumbValue(focusedThumb, state.getThumbMinValue(focusedThumb));
}
};

const onEndKeyDown = () => {
!formControlContext.isDisabled() &&
state.focusedThumb() !== undefined &&
state.setThumbValue(
state.values().length - 1,
state.getThumbMaxValue(state.values().length - 1),
);
const onEndKeyDown = (event: KeyboardEvent) => {
const focusedThumb = state.focusedThumb();

if (!formControlContext.isDisabled() && focusedThumb !== undefined) {
stopEventDefaultAndPropagation(event);
state.setThumbValue(focusedThumb, state.getThumbMaxValue(focusedThumb));
}
};

const onStepKeyDown = (event: KeyboardEvent, index: number) => {
if (!formControlContext.isDisabled()) {
switch (event.key) {
case "Left":
case "ArrowLeft":
event.preventDefault();
event.stopPropagation();
case "Down":
case "ArrowDown":
stopEventDefaultAndPropagation(event);
if (!isLTR()) {
state.incrementThumb(index, event.shiftKey ? state.pageSize() : state.step());
} else {
Expand All @@ -280,44 +289,27 @@ export function SliderRoot(props: SliderRootProps) {
break;
case "Right":
case "ArrowRight":
event.preventDefault();
event.stopPropagation();
if (!isLTR()) {
state.decrementThumb(index, event.shiftKey ? state.pageSize() : state.step());
} else {
state.incrementThumb(index, event.shiftKey ? state.pageSize() : state.step());
}
break;
case "Up":
case "ArrowUp":
event.preventDefault();
event.stopPropagation();
stopEventDefaultAndPropagation(event);
if (!isLTR()) {
state.decrementThumb(index, event.shiftKey ? state.pageSize() : state.step());
} else {
state.incrementThumb(index, event.shiftKey ? state.pageSize() : state.step());
}
break;
case "Down":
case "ArrowDown":
event.preventDefault();
event.stopPropagation();
if (!isLTR()) {
state.incrementThumb(index, event.shiftKey ? state.pageSize() : state.step());
} else {
state.decrementThumb(index, event.shiftKey ? state.pageSize() : state.step());
}
break;
case "Home":
onHomeKeyDown();
onHomeKeyDown(event);
break;
case "End":
onEndKeyDown();
onEndKeyDown(event);
break;
case "PageUp":
stopEventDefaultAndPropagation(event);
state.incrementThumb(index, state.pageSize());
break;
case "PageDown":
stopEventDefaultAndPropagation(event);
state.decrementThumb(index, state.pageSize());
break;
}
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/slider/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export function getClosestValueIndex(values: number[], nextValue: number) {
if (values.length === 1) return 0;
const distances = values.map(value => Math.abs(value - nextValue));
const closestDistance = Math.min(...distances);
return distances.indexOf(closestDistance);
const closestIndex = distances.indexOf(closestDistance);

return nextValue < values[closestIndex] ? closestIndex : distances.lastIndexOf(closestDistance);
}

/**
Expand Down Expand Up @@ -68,3 +70,8 @@ export function linearScale(input: readonly [number, number], output: readonly [
return output[0] + ratio * (value - input[0]);
};
}

export function stopEventDefaultAndPropagation(event: Event) {
event.preventDefault();
event.stopPropagation();
}

0 comments on commit c783342

Please sign in to comment.