-
Notifications
You must be signed in to change notification settings - Fork 33
Feat: Handle accessibility for crossing over thumbs #97
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import Thumb from './components/Thumb' | |
| import ResponderView from './components/ResponderView' | ||
| import useDrag from './hooks/useDrag' | ||
| import useCustomMarks from './hooks/useCustomMarks' | ||
| import { useAccessibilityRangeSlider } from './hooks/useAccessibilityRangeSlider' | ||
|
|
||
| export type SliderProps = RN.ViewProps & { | ||
| range?: [number, number]; | ||
|
|
@@ -120,16 +121,27 @@ const Slider = React.forwardRef<RN.View, SliderProps>((props: SliderProps, forwa | |
|
|
||
| const { marks, onLayoutUpdateMarks } = useCustomMarks(CustomMark, { step, minimumValue, maximumValue, activeValues: range, inverted, vertical }) | ||
|
|
||
| const { | ||
| minThumbRef, | ||
| maxThumbRef, | ||
| updateAccessibilityMinValue, | ||
| updateAccessibilityMaxValue, blurThumbs | ||
| } = useAccessibilityRangeSlider({ min, max, updateMaxValue, updateMinValue }) | ||
|
|
||
| return ( | ||
| <RN.View {...others}> | ||
| <ResponderView style={styleSheet[vertical ? 'vertical' : 'horizontal']} ref={forwardedRef} maximumValue={maximumValue} minimumValue={minimumValue} step={step} | ||
| onPress={onPress} onMove={onMove} onRelease={onRelease} | ||
| onPress={(value) => { | ||
| // We need to blur the min/max thumb if it is focused when the user interacts with the slider | ||
| blurThumbs() | ||
| onPress(value) | ||
| }} onMove={onMove} onRelease={onRelease} | ||
| enabled={enabled} vertical={vertical} inverted={inverted} onLayout={onLayoutUpdateMarks} | ||
| > | ||
| <Track color={outboundColor} style={minStyle} length={minTrackPct * 100} vertical={vertical} thickness={trackHeight} /> | ||
| <Thumb key='min' {...thumbProps} updateValue={updateMinValue} value={min} thumb='min' /> | ||
| <Thumb key='min' {...thumbProps} thumbRef={minThumbRef} updateValue={(value) => crossingAllowed ? updateAccessibilityMinValue(value) : updateMinValue(min + value)} value={min} thumb='min' /> | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that a better approach would be to uniquely identify each thumb with their "key", and order them based on their current value. This would require accepting the range to be reverted internally:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Sharcoux If I order them based on their current value with their 'key', how can I focus on the max/min thumbs if I increase/decrease the value beyond them?
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you order them based on their current value, the thumb would not change, so you would not need to handle the focus switch. |
||
| <Track color={inboundColor} style={midStyle} length={(maxTrackPct - minTrackPct) * 100} vertical={vertical} thickness={trackHeight} /> | ||
| <Thumb key='max' {...thumbProps} updateValue={updateMaxValue} value={max} thumb='max' /> | ||
| <Thumb key='max' {...thumbProps} thumbRef={maxThumbRef} updateValue={(value) => crossingAllowed ? updateAccessibilityMaxValue(value) : updateMaxValue(max + value)} value={max} thumb='max' /> | ||
| <Track color={outboundColor} style={maxStyle} length={(1 - maxTrackPct) * 100} vertical={vertical} thickness={trackHeight} /> | ||
| {marks} | ||
| </ResponderView> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import React, { useCallback } from 'react' | ||
| import { View } from 'react-native' | ||
| interface AccessibilityRangeSliderProps { | ||
| min: number; | ||
| max: number; | ||
| updateMaxValue: (value: number) => void; | ||
| updateMinValue: (value: number) => void; | ||
| } | ||
| /** | ||
| * Custom hook to manage accessibility features for a range slider component. | ||
| * It provides methods to update the minimum and maximum values of the range slider, | ||
| * @param props -Props containing the minimum value, maximum value, and functions to update them. | ||
| * @returns An object containing references to the minimum and maximum thumb elements, | ||
| * and functions to update their values. | ||
| */ | ||
| export const useAccessibilityRangeSlider = (props: AccessibilityRangeSliderProps) => { | ||
| const { min, max, updateMaxValue, updateMinValue } = props | ||
|
|
||
| // Refs to hold references to the minimum and maximum thumb elements | ||
| const minThumbRef = React.useRef<View>(null) | ||
| const maxThumbRef = React.useRef<View>(null) | ||
|
|
||
| /** | ||
| * Function to update the minimum value of the range slider | ||
| * If the new value exceeds the maximum value, it updates the maximum value instead. | ||
| * @param value - The value to be added to the current minimum value. | ||
| */ | ||
| const updateAccessibilityMinValue = useCallback((value: number) => { | ||
| const newValue = min + value | ||
| if (newValue > max) { | ||
| // If the new value exceeds the maximum value, update the maximum and min value instead | ||
| updateMinValue(max) | ||
| updateMaxValue(min + value) | ||
| // Then focus on the maximum thumb for accessibility | ||
| maxThumbRef.current?.focus() | ||
| } else { | ||
| updateMinValue(newValue) | ||
| } | ||
| }, [min, max, updateMaxValue, updateMinValue]) | ||
|
|
||
| /** | ||
| * Function to update the maximum value of the range slider | ||
| * If the new value exceeds the minimum value, it updates the maximum value instead. | ||
| * @param value - The value to be added to the current maximum value. | ||
| */ | ||
| const updateAccessibilityMaxValue = useCallback((value: number) => { | ||
| const newValue = max + value | ||
|
|
||
| if (newValue < min) { | ||
| // If the new value is less than the minimum value, update the minimum and maximum value instead | ||
| updateMaxValue(min) | ||
| updateMinValue(max + value) | ||
| // Then focus on the minimum thumb for accessibility | ||
| minThumbRef.current?.focus() | ||
| } else { | ||
| updateMaxValue(newValue) | ||
| } | ||
| }, [min, max, updateMaxValue, updateMinValue]) | ||
|
|
||
| /** | ||
| * Function to blur thumbs | ||
| */ | ||
| const blurThumbs = useCallback(() => { | ||
| minThumbRef.current?.blur() | ||
| maxThumbRef.current?.blur() | ||
| }, []) | ||
|
|
||
| return { | ||
| minThumbRef, | ||
| maxThumbRef, | ||
| updateAccessibilityMinValue, | ||
| updateAccessibilityMaxValue, | ||
| blurThumbs | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this suspicious. What would have the focuse, then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sharcoux In the example, if I focus on the
minthumb, and then drag this thumb over themaxthumb, you will see that theminthumb remains focused while I'm interacting with themaxthumb.Screen.Recording.2024-05-23.at.5.08.06.PM.mov