Skip to content

Commit e0eed2f

Browse files
authored
feat: add custom format support (#20)
1 parent 926dbc6 commit e0eed2f

File tree

7 files changed

+77
-30
lines changed

7 files changed

+77
-30
lines changed

src/components/RangeSlider/__stories__/index.stories.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,7 @@ const DemoContainer = styled.div`
1515
padding: 0 5rem;
1616
`
1717

18-
const demoMarkers = [
19-
{ label: '$0', value: 0 },
20-
{ label: '$25', value: 25 },
21-
{ label: '$50', value: 50 },
22-
{ label: '$75', value: 75 },
23-
{ label: '$100', value: 100 },
24-
]
18+
const demoMarkers = [{ value: 0 }, { value: 25 }, { value: 50 }, { value: 75 }, { value: 100 }]
2519

2620
const Basic = () => {
2721
const [range, setRange] = useRangeSliderDemo()
@@ -56,6 +50,24 @@ const WithMarkers = () => {
5650
)
5751
}
5852

53+
const WithCustomValueFormat = () => {
54+
const [range, setRange] = useRangeSliderDemo([30, 70])
55+
56+
return (
57+
<DemoContainer>
58+
<RangeSlider
59+
value={range}
60+
min={0}
61+
max={100}
62+
markers={demoMarkers}
63+
onChange={setRange}
64+
formatValue={(value) => `$${value}`}
65+
/>
66+
<pre>{JSON.stringify({ values: range }, null, 2)}</pre>
67+
</DemoContainer>
68+
)
69+
}
70+
5971
const WithLargeRange = () => {
6072
const [range, setRange] = useRangeSliderDemo([300000, 700000])
6173

@@ -68,4 +80,4 @@ const WithLargeRange = () => {
6880
}
6981

7082
export default { title: 'RangeSlider' }
71-
export { Basic, WithStep, WithMarkers, WithLargeRange }
83+
export { Basic, WithStep, WithMarkers, WithCustomValueFormat, WithLargeRange }

src/components/RangeSlider/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface IRangeSliderProps {
2121

2222
step?: number
2323
markers?: IRangeMarker[]
24+
formatValue?: (value: number) => string
2425
}
2526

2627
const RangeSlider: React.FC<IRangeSliderProps> = ({
@@ -30,6 +31,7 @@ const RangeSlider: React.FC<IRangeSliderProps> = ({
3031
onChange = () => {},
3132
step,
3233
markers,
34+
formatValue,
3335
}) => {
3436
const {
3537
getRailProps,
@@ -54,8 +56,10 @@ const RangeSlider: React.FC<IRangeSliderProps> = ({
5456
const { style, isInRange } = getMarkerProps(marker)
5557

5658
return (
57-
<React.Fragment key={`marker-${marker.label}`}>
58-
<SliderMarkerLabel style={style}>{marker.label}</SliderMarkerLabel>
59+
<React.Fragment key={`marker-${marker.value}`}>
60+
<SliderMarkerLabel style={style}>
61+
{marker.label ?? (formatValue ? formatValue(marker.value) : marker.value)}
62+
</SliderMarkerLabel>
5963
<SliderMarker isInRange={isInRange} style={style} />
6064
</React.Fragment>
6165
)

src/components/Slider/__stories__/index.stories.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,7 @@ const DemoContainer = styled.div`
1414
padding: 0 5rem;
1515
`
1616

17-
const demoMarkers = [
18-
{ label: '$0', value: 0 },
19-
{ label: '$25', value: 25 },
20-
{ label: '$50', value: 50 },
21-
{ label: '$75', value: 75 },
22-
{ label: '$100', value: 100 },
23-
]
17+
const demoMarkers = [{ value: 0 }, { value: 25 }, { value: 50 }, { value: 75 }, { value: 100 }]
2418

2519
const Basic = () => {
2620
const [value, setValue] = useSliderDemo()
@@ -34,7 +28,7 @@ const Basic = () => {
3428
}
3529

3630
const WithStep = () => {
37-
const [value, setValue] = useSliderDemo(50)
31+
const [value, setValue] = useSliderDemo(500)
3832

3933
return (
4034
<DemoContainer>
@@ -55,5 +49,23 @@ const WithMarkers = () => {
5549
)
5650
}
5751

52+
const WithCustomValueFormat = () => {
53+
const [value, setValue] = useSliderDemo(50)
54+
55+
return (
56+
<DemoContainer>
57+
<Slider
58+
value={value}
59+
min={0}
60+
max={100}
61+
markers={demoMarkers}
62+
onChange={setValue}
63+
formatValue={(value) => `$${value}`}
64+
/>
65+
<pre>{JSON.stringify({ values: value }, null, 2)}</pre>
66+
</DemoContainer>
67+
)
68+
}
69+
5870
export default { title: 'Slider' }
59-
export { Basic, WithStep, WithMarkers }
71+
export { Basic, WithStep, WithMarkers, WithCustomValueFormat }

src/components/Slider/index.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,25 @@ export interface ISliderProps {
2121

2222
step?: number
2323
markers?: IRangeMarker[]
24+
formatValue?: (value: number) => string
2425
}
2526

26-
const Slider: React.FC<ISliderProps> = ({ value, min, max, onChange, step, markers }) => {
27+
const Slider: React.FC<ISliderProps> = ({
28+
value,
29+
min,
30+
max,
31+
onChange,
32+
step,
33+
markers,
34+
formatValue,
35+
}) => {
2736
const { getRailProps, getTrackProps, getHandleProps, getMarkerProps } = useSlider({
2837
value,
2938
min,
3039
max,
3140
onChange,
3241
step,
42+
formatValue,
3343
})
3444

3545
return (
@@ -41,8 +51,10 @@ const Slider: React.FC<ISliderProps> = ({ value, min, max, onChange, step, marke
4151
const { style, isInRange } = getMarkerProps(marker)
4252

4353
return (
44-
<React.Fragment key={`marker-${marker.label}`}>
45-
<SliderMarkerLabel style={style}>{marker.label}</SliderMarkerLabel>
54+
<React.Fragment key={`marker-${marker.value}`}>
55+
<SliderMarkerLabel style={style}>
56+
{marker.label ?? (formatValue ? formatValue(marker.value) : marker.value)}
57+
</SliderMarkerLabel>
4658
<SliderMarker isInRange={isInRange} style={style} />
4759
</React.Fragment>
4860
)

src/hooks/useRangeSlider.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface IUseRangeSlider {
2323
* @default 1
2424
*/
2525
step?: number
26+
formatValue?: (value: number) => string
2627
}
2728

2829
const useRangeSlider = ({
@@ -31,6 +32,7 @@ const useRangeSlider = ({
3132
max,
3233
onChange,
3334
step = DEFAULT_STEP,
35+
formatValue,
3436
}: IUseRangeSlider) => {
3537
const railRef = React.useRef<HTMLSpanElement>(null)
3638
const trackRef = React.useRef<HTMLSpanElement>(null)
@@ -201,8 +203,9 @@ const useRangeSlider = ({
201203
ref: minThumbRef,
202204
role: 'slider',
203205
tabIndex: 0,
204-
'aria-valuemin': min,
205206
'aria-valuenow': minValue,
207+
'aria-valuetext': formatValue ? formatValue(minValue) : String(minValue),
208+
'aria-valuemin': min,
206209
'aria-valuemax': maxValue,
207210
onFocus: () => {
208211
activeHandle.current = 'min'
@@ -222,15 +225,16 @@ const useRangeSlider = ({
222225
handleTouchStart(event)
223226
},
224227
}
225-
}, [handleKeyDown, handleMouseDown, handleTouchStart, maxValue, min, minValue])
228+
}, [formatValue, handleKeyDown, handleMouseDown, handleTouchStart, maxValue, min, minValue])
226229

227230
const getMaxHandleProps = React.useCallback(() => {
228231
return {
229232
ref: maxThumbRef,
230233
role: 'slider',
231234
tabIndex: 0,
232-
'aria-valuemin': minValue,
233235
'aria-valuenow': maxValue,
236+
'aria-valuetext': formatValue ? formatValue(maxValue) : String(maxValue),
237+
'aria-valuemin': minValue,
234238
'aria-valuemax': max,
235239
onFocus: () => {
236240
activeHandle.current = 'max'
@@ -250,7 +254,7 @@ const useRangeSlider = ({
250254
handleTouchStart(event)
251255
},
252256
}
253-
}, [handleKeyDown, handleMouseDown, handleTouchStart, max, maxValue, minValue])
257+
}, [formatValue, handleKeyDown, handleMouseDown, handleTouchStart, max, maxValue, minValue])
254258

255259
const getMarkerProps = React.useCallback(
256260
(marker: IRangeMarker) => {

src/hooks/useSlider.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ export interface IUseSlider {
2323
* @default 1
2424
*/
2525
step?: number
26+
formatValue?: (value: number) => string
2627
}
2728

28-
const useSlider = ({ value, min, max, onChange, step = DEFAULT_STEP }: IUseSlider) => {
29+
const useSlider = ({ value, min, max, onChange, step = DEFAULT_STEP, formatValue }: IUseSlider) => {
2930
const railRef = React.useRef<HTMLSpanElement>(null)
3031
const trackRef = React.useRef<HTMLSpanElement>(null)
3132
const thumbRef = React.useRef<HTMLSpanElement>(null)
@@ -182,14 +183,15 @@ const useSlider = ({ value, min, max, onChange, step = DEFAULT_STEP }: IUseSlide
182183
ref: thumbRef,
183184
role: 'slider',
184185
tabIndex: 0,
185-
'aria-valuemin': min,
186186
'aria-valuenow': value,
187+
'aria-valuetext': formatValue ? formatValue(value) : String(value),
188+
'aria-valuemin': min,
187189
'aria-valuemax': max,
188190
onKeyDown: handleKeyDown,
189191
onMouseDown: handleMouseDown,
190192
onTouchStart: handleTouchStart,
191193
}
192-
}, [handleKeyDown, handleMouseDown, handleTouchStart, max, min, value])
194+
}, [formatValue, handleKeyDown, handleMouseDown, handleTouchStart, max, min, value])
193195

194196
const getMarkerProps = React.useCallback(
195197
(marker: IRangeMarker) => {

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
export type TRangeTuple = [number, number]
22

33
export interface IRangeMarker {
4-
label: string
54
value: number
5+
6+
label?: string
67
}
78

89
export enum KeyCodes {

0 commit comments

Comments
 (0)