Skip to content

Commit fd1be1e

Browse files
Merge branch 'sliders' into 'main'
Sliders See merge request react-native/react-native-material-components!18
2 parents 576e052 + c77ce6b commit fd1be1e

21 files changed

+966
-1
lines changed

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,70 @@ export const MyScreen = () => {
857857

858858
![left side sheet](https://ik.imagekit.io/Computools/rn-material-components/left-side-sheet.gif?updatedAt=1706170982231)
859859
![right side sheet](https://ik.imagekit.io/Computools/rn-material-components/right-side-sheet.gif?updatedAt=1706171192408)
860+
</details>
861+
</details>
862+
<details><summary>Sliders</summary>
863+
<br />
864+
<details><summary>Slider</summary>
865+
<br />
866+
867+
**Properties**
868+
869+
| name | description | type | default |
870+
| ------ | ------ | ------ | ---- |
871+
| max | required | number | - |
872+
| min | required | number | - |
873+
| value | - | number | 0 |
874+
| step | The slider operates in discrete mode when a step value is provided. | number | - |
875+
| onChangeValue | - | (value: number) => void | - |
876+
| disabled | - | boolean | false |
877+
| centered | The slider operates in centered mode when a centered props is true | number | boolean | false |
878+
| damping | Controls thumb animation when a track point is pressed. | number | 20 |
879+
| valueHeight | - | number | 44 |
880+
| thumbWidthActive | - | number | 2 |
881+
| thumbWidthInactive | - | number | 4 |
882+
| thumbStyle | - | ViewStyle | - |
883+
| valueStyle | - | ViewStyle | - |
884+
| indicatorStyle | - | ViewStyle | - |
885+
| trackPointStyle | - | ViewStyle | - |
886+
| trackPointsStyle | - | ViewStyle | - |
887+
| filledTrackStyle | - | ViewStyle | - |
888+
| remainingTrackStyle | - | ViewStyle | - |
889+
890+
![continuous slider](https://ik.imagekit.io/Computools/rn-material-components/continuous-slider.gif?updatedAt=1734970059184)
891+
![centered slider](https://ik.imagekit.io/Computools/rn-material-components/centered-slider.gif?updatedAt=1734970059052)
892+
![discrete slider](https://ik.imagekit.io/Computools/rn-material-components/discrete-slider.gif?updatedAt=1734970059069)
893+
894+
</details>
895+
<details><summary>Range Slider</summary>
896+
<br />
897+
898+
**Properties**
899+
900+
| name | description | type | default |
901+
| ------ | ------ | ------ | ---- |
902+
| max | required | number | - |
903+
| min | required | number | - |
904+
| range | The first element represents the minimum value, and the second represents the maximum value. The range must have exactly two elements, with the minimum value less than or equal to the maximum value. | number | - |
905+
| step | The slider operates in discrete mode when a step value is provided. | number | - |
906+
| onChangeValue | - | (value: number) => void | - |
907+
| disabled | - | boolean | false |
908+
| centered | The slider operates in centered mode when a centered props is true | number | boolean | false |
909+
| damping | Controls thumb animation when a track point is pressed. | number | 20 |
910+
| valueHeight | - | number | 44 |
911+
| thumbWidthActive | - | number | 2 |
912+
| thumbWidthInactive | - | number | 4 |
913+
| thumbStyle | - | ViewStyle | - |
914+
| valueStyle | - | ViewStyle | - |
915+
| indicatorStyle | - | ViewStyle | - |
916+
| trackPointStyle | - | ViewStyle | - |
917+
| trackPointsStyle | - | ViewStyle | - |
918+
| filledTrackStyle | - | ViewStyle | - |
919+
| remainingTrackStyle | - | ViewStyle | - |
920+
921+
![range slider](https://ik.imagekit.io/Computools/rn-material-components/range-slider.gif?updatedAt=1734970059452)
922+
![discrete range slider](https://ik.imagekit.io/Computools/rn-material-components/continuous-slider.gif?updatedAt=1734970059184)
923+
860924
</details>
861925
</details>
862926
<details><summary>Snackbar</summary>

src/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ export {Badge, type BadgeProps} from './badge/Badge.component';
44

55
export {Divider, type DividerProps} from './divider/Divider.component';
66

7+
export {Slider, type SliderProps} from './sliders/slider /Slider.component';
8+
export {RangeSlider, type RangeSliderProps} from './sliders/range-slider/RangeSlider.component';
9+
710
export {InputChip, type InputChipProps} from './chips/input-chip/InputChip.component';
811
export {FilterChip, type FilterChipProps} from './chips/filter-chip/FilterChip.component';
9-
export {SuggestionChip, type SuggestionChipProps} from './chips/suggestion-chip/SuggestionChip.component';
1012
export {AssistChip, type AssistChipProps, IconType} from './chips/assist-chip/AssistChip.component';
13+
export {SuggestionChip, type SuggestionChipProps} from './chips/suggestion-chip/SuggestionChip.component';
1114

1215
export {
1316
CenterAlignedTopAppBar,
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import {GestureDetector} from 'react-native-gesture-handler';
4+
import {type LayoutChangeEvent, type ViewProps, type StyleProp, type ViewStyle, type TextStyle} from 'react-native';
5+
6+
import {styles} from './range-slider.styles';
7+
import {useRangeSlider} from './useRangeSlider.hook';
8+
import {useSliderColors} from '../use-slider-colors.hook';
9+
import {SliderTrack} from '../ui/slider-track/SliderTrack.component';
10+
import {useRangeSliderTrackPoints} from './useRangeSliderTrackPoints.hook';
11+
import {SliderIndicator} from '../ui/slider-indicator/SliderIndicator.component';
12+
import {SliderTrackPoint} from '../ui/slider-track-point/SliderTrackPoint.component';
13+
14+
export interface RangeSliderProps extends ViewProps {
15+
max: number;
16+
min: number;
17+
18+
range?: number[];
19+
step?: number;
20+
damping?: number;
21+
centered?: boolean;
22+
disabled?: boolean;
23+
24+
valueHeight?: number;
25+
thumbWidthActive?: number;
26+
thumbWidthInactive?: number;
27+
28+
thumbStyle?: StyleProp<ViewStyle>;
29+
valueStyle?: StyleProp<TextStyle>;
30+
indicatorStyle?: StyleProp<ViewStyle>;
31+
trackPointStyle?: StyleProp<ViewStyle>;
32+
trackPointsStyle?: StyleProp<ViewStyle>;
33+
filledTrackStyle?: StyleProp<ViewStyle>;
34+
remainingTrackStyle?: StyleProp<ViewStyle>;
35+
36+
onChangeRange?: (range: number[]) => void;
37+
}
38+
39+
export const RangeSlider: React.FC<RangeSliderProps> = ({
40+
max,
41+
min,
42+
43+
step,
44+
range = [0, 0],
45+
damping = 20,
46+
centered = false,
47+
disabled = false,
48+
49+
thumbStyle,
50+
valueStyle,
51+
indicatorStyle,
52+
trackPointStyle,
53+
trackPointsStyle,
54+
filledTrackStyle,
55+
remainingTrackStyle,
56+
57+
valueHeight,
58+
thumbWidthActive,
59+
thumbWidthInactive,
60+
61+
onChangeRange,
62+
onLayout,
63+
style,
64+
...props
65+
}) => {
66+
const {filledTrackColor, remainingTrackColor} = useSliderColors(disabled, centered);
67+
68+
const trackPoints = useRangeSliderTrackPoints({max, min, step, centered});
69+
const {
70+
gesture,
71+
selectedRange,
72+
thumbMinSliding,
73+
thumbMaxSliding,
74+
animMinValueProps,
75+
animMaxValueProps,
76+
filledTrackAnimatedStyle,
77+
remainingTrackAfterAnimatedStyle,
78+
remainingTrackBeforeAnimatedStyle,
79+
slideToTrackPoint,
80+
setUpSliderLayout,
81+
} = useRangeSlider({max, min, centered, step, damping}, range, onChangeRange);
82+
83+
const handleLayoutChange = (e: LayoutChangeEvent) => {
84+
setUpSliderLayout(e);
85+
86+
if (onLayout) {
87+
onLayout(e);
88+
}
89+
};
90+
91+
const renderTrackPoint = (pointValue: number) => (
92+
<SliderTrackPoint
93+
key={pointValue}
94+
value={pointValue}
95+
disabled={disabled}
96+
selectedValue={selectedRange}
97+
onPress={slideToTrackPoint}
98+
style={trackPointStyle}
99+
/>
100+
);
101+
102+
return (
103+
<GestureDetector gesture={gesture.enabled(!disabled)}>
104+
<View style={[styles.container, style]} onLayout={handleLayoutChange} {...props}>
105+
<SliderTrack
106+
style={[{backgroundColor: remainingTrackColor}, styles.remainingBeforeTrack, remainingTrackBeforeAnimatedStyle, filledTrackStyle]}
107+
/>
108+
<SliderIndicator
109+
animValueProps={animMinValueProps}
110+
sliding={thumbMinSliding}
111+
disabled={disabled}
112+
style={[styles.thumb, indicatorStyle]}
113+
thumbStyle={thumbStyle}
114+
valueStyle={valueStyle}
115+
valueHeight={valueHeight}
116+
thumbWidthActive={thumbWidthActive}
117+
thumbWidthInactive={thumbWidthInactive}
118+
/>
119+
<SliderTrack style={[{backgroundColor: filledTrackColor}, styles.filledTrack, filledTrackAnimatedStyle, filledTrackStyle]} />
120+
<SliderIndicator
121+
animValueProps={animMaxValueProps}
122+
sliding={thumbMaxSliding}
123+
disabled={disabled}
124+
style={[styles.thumb, indicatorStyle]}
125+
thumbStyle={thumbStyle}
126+
valueStyle={valueStyle}
127+
valueHeight={valueHeight}
128+
thumbWidthActive={thumbWidthActive}
129+
thumbWidthInactive={thumbWidthInactive}
130+
/>
131+
<SliderTrack
132+
style={[{backgroundColor: remainingTrackColor}, styles.remainingAfterTrack, remainingTrackAfterAnimatedStyle, remainingTrackStyle]}
133+
/>
134+
<View style={[styles.trackPoints, trackPointsStyle]}>{trackPoints.map(renderTrackPoint)}</View>
135+
</View>
136+
</GestureDetector>
137+
);
138+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {StyleSheet} from 'react-native';
2+
3+
export const styles = StyleSheet.create({
4+
container: {
5+
flexDirection: 'row',
6+
alignItems: 'center',
7+
},
8+
thumb: {
9+
zIndex: 1,
10+
},
11+
trackPoints: {
12+
position: 'absolute',
13+
14+
flexDirection: 'row',
15+
justifyContent: 'space-between',
16+
17+
width: '100%',
18+
paddingHorizontal: 4,
19+
},
20+
filledTrack: {
21+
borderRadius: 2,
22+
},
23+
remainingBeforeTrack: {
24+
borderTopEndRadius: 2,
25+
borderBottomEndRadius: 2,
26+
borderTopStartRadius: 16,
27+
borderBottomStartRadius: 16,
28+
},
29+
remainingAfterTrack: {
30+
justifyContent: 'center',
31+
32+
borderTopEndRadius: 16,
33+
borderBottomEndRadius: 16,
34+
borderTopStartRadius: 2,
35+
borderBottomStartRadius: 2,
36+
},
37+
});

0 commit comments

Comments
 (0)