@momo-kits/slider 0.77.2 → 0.77.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Label.tsx +36 -0
- package/helpers.ts +35 -0
- package/hooks.tsx +198 -0
- package/index.tsx +344 -0
- package/package.json +3 -4
- package/publish.sh +1 -1
- package/styles.ts +29 -0
- package/DefaultLabel.js +0 -83
- package/DefaultLabel.web.js +0 -83
- package/DefaultMarker.js +0 -67
- package/DefaultMarker.web.js +0 -75
- package/Slider.js +0 -781
- package/Slider.web.js +0 -761
- package/converters.js +0 -80
- package/index.js +0 -3
package/Label.tsx
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, {PureComponent} from 'react';
|
|
2
|
+
import {View} from 'react-native';
|
|
3
|
+
import {Shadow, Text, Colors, Spacing, Radius} from '@momo-kits/foundation';
|
|
4
|
+
|
|
5
|
+
class Label extends PureComponent {
|
|
6
|
+
state = {
|
|
7
|
+
value: Number.NaN,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
setValue = (value: number) => {
|
|
11
|
+
this.setState({value});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
render() {
|
|
15
|
+
const {...props} = this.props;
|
|
16
|
+
const {value} = this.state;
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<View
|
|
20
|
+
{...props}
|
|
21
|
+
style={[
|
|
22
|
+
{
|
|
23
|
+
backgroundColor: Colors.black_01,
|
|
24
|
+
paddingVertical: Spacing.XS,
|
|
25
|
+
paddingHorizontal: Spacing.S,
|
|
26
|
+
borderRadius: Radius.XS,
|
|
27
|
+
},
|
|
28
|
+
Shadow.Light,
|
|
29
|
+
]}>
|
|
30
|
+
<Text typography={'description_xs'}>{value}</Text>
|
|
31
|
+
</View>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default Label;
|
package/helpers.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const isLowCloser = (
|
|
2
|
+
downX: number,
|
|
3
|
+
lowPosition: number,
|
|
4
|
+
highPosition: number,
|
|
5
|
+
): boolean => {
|
|
6
|
+
if (lowPosition === highPosition) {
|
|
7
|
+
return downX < lowPosition;
|
|
8
|
+
}
|
|
9
|
+
const distanceFromLow = Math.abs(downX - lowPosition);
|
|
10
|
+
const distanceFromHigh = Math.abs(downX - highPosition);
|
|
11
|
+
return distanceFromLow < distanceFromHigh;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const clamp = (value: number, min: number, max: number): number => {
|
|
15
|
+
return Math.min(Math.max(value, min), max);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getValueForPosition = (
|
|
19
|
+
positionInView: number,
|
|
20
|
+
containerWidth: number,
|
|
21
|
+
thumbWidth: number,
|
|
22
|
+
min: number,
|
|
23
|
+
max: number,
|
|
24
|
+
step: number,
|
|
25
|
+
): number => {
|
|
26
|
+
const availableSpace = containerWidth - thumbWidth;
|
|
27
|
+
const relStepUnit = step / (max - min);
|
|
28
|
+
let relPosition = (positionInView - thumbWidth / 2) / availableSpace;
|
|
29
|
+
const relOffset = relPosition % relStepUnit;
|
|
30
|
+
relPosition -= relOffset;
|
|
31
|
+
if (relOffset / relStepUnit >= 0.5) {
|
|
32
|
+
relPosition += relStepUnit;
|
|
33
|
+
}
|
|
34
|
+
return clamp(min + Math.round(relPosition / relStepUnit) * step, min, max);
|
|
35
|
+
};
|
package/hooks.tsx
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useState,
|
|
4
|
+
useRef,
|
|
5
|
+
useMemo,
|
|
6
|
+
MutableRefObject,
|
|
7
|
+
ReactNode,
|
|
8
|
+
} from 'react';
|
|
9
|
+
import {Animated, I18nManager, View} from 'react-native';
|
|
10
|
+
import {clamp} from './helpers';
|
|
11
|
+
import styles from './styles';
|
|
12
|
+
import FollowerContainer from './Label';
|
|
13
|
+
import {Text} from '@momo-kits/foundation';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* low and high state variables are fallbacks for props (props are not required).
|
|
17
|
+
* This hook ensures that current low and high are not out of [min, max] range.
|
|
18
|
+
* It returns an object which contains:
|
|
19
|
+
* - ref containing correct low, high, min, max and step to work with.
|
|
20
|
+
* - setLow and setHigh setters
|
|
21
|
+
* @param lowProp
|
|
22
|
+
* @param highProp
|
|
23
|
+
* @param min
|
|
24
|
+
* @param max
|
|
25
|
+
* @param step
|
|
26
|
+
* @returns {{inPropsRef: React.MutableRefObject<{high: (*|number), low: (*|number)}>, setLow: (function(number): undefined), setHigh: (function(number): undefined)}}
|
|
27
|
+
*/
|
|
28
|
+
export const useLowHigh = (
|
|
29
|
+
lowProp: number | undefined,
|
|
30
|
+
highProp: number | undefined,
|
|
31
|
+
min: number,
|
|
32
|
+
max: number,
|
|
33
|
+
step: number,
|
|
34
|
+
) => {
|
|
35
|
+
const validLowProp = lowProp === undefined ? min : clamp(lowProp, min, max);
|
|
36
|
+
const validHighProp =
|
|
37
|
+
highProp === undefined ? max : clamp(highProp, min, max);
|
|
38
|
+
const inPropsRef = useRef({
|
|
39
|
+
low: validLowProp,
|
|
40
|
+
high: validHighProp,
|
|
41
|
+
step,
|
|
42
|
+
// These 2 fields will be overwritten below.
|
|
43
|
+
min: validLowProp,
|
|
44
|
+
max: validHighProp,
|
|
45
|
+
});
|
|
46
|
+
const {low: lowState, high: highState} = inPropsRef.current;
|
|
47
|
+
const inPropsRefPrev = {lowPrev: lowState, highPrev: highState};
|
|
48
|
+
|
|
49
|
+
// Props have higher priority.
|
|
50
|
+
// If no props are passed, use internal state variables.
|
|
51
|
+
const low = clamp(lowProp === undefined ? lowState : lowProp, min, max);
|
|
52
|
+
const high = clamp(highProp === undefined ? highState : highProp, min, max);
|
|
53
|
+
|
|
54
|
+
// Always update values of refs so pan responder will have updated values
|
|
55
|
+
Object.assign(inPropsRef.current, {low, high, min, max});
|
|
56
|
+
|
|
57
|
+
const setLow = (value: number) => (inPropsRef.current.low = value);
|
|
58
|
+
const setHigh = (value: number) => (inPropsRef.current.high = value);
|
|
59
|
+
return {inPropsRef, inPropsRefPrev, setLow, setHigh};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Sets the current value of widthRef and calls the callback with new width parameter.
|
|
64
|
+
* @param widthRef
|
|
65
|
+
* @param callback
|
|
66
|
+
* @returns {function({nativeEvent: *}): void}
|
|
67
|
+
*/
|
|
68
|
+
export const useWidthLayout = (
|
|
69
|
+
widthRef: MutableRefObject<number>,
|
|
70
|
+
callback?: (width: number) => void,
|
|
71
|
+
) => {
|
|
72
|
+
return useCallback(
|
|
73
|
+
({nativeEvent}) => {
|
|
74
|
+
const {
|
|
75
|
+
layout: {width},
|
|
76
|
+
} = nativeEvent;
|
|
77
|
+
const {current: w} = widthRef;
|
|
78
|
+
if (w !== width) {
|
|
79
|
+
widthRef.current = width;
|
|
80
|
+
if (callback) {
|
|
81
|
+
callback(width);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[callback, widthRef],
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* This hook creates a component which follows the thumb.
|
|
91
|
+
* Content renderer is passed to FollowerContainer which re-renders only it's content with setValue method.
|
|
92
|
+
* This allows to re-render only follower, instead of the whole slider with all children (thumb, rail, etc.).
|
|
93
|
+
* Returned update function should be called every time follower should be updated.
|
|
94
|
+
* @param containerWidthRef
|
|
95
|
+
* @param gestureStateRef
|
|
96
|
+
* @param isPressed
|
|
97
|
+
* @param allowOverflow
|
|
98
|
+
* @returns {[JSX.Element, function(*, *=): void]|*[]}
|
|
99
|
+
*/
|
|
100
|
+
export const useThumbFollower = (
|
|
101
|
+
containerWidthRef: MutableRefObject<number>,
|
|
102
|
+
gestureStateRef: MutableRefObject<{lastValue: number; lastPosition: number}>,
|
|
103
|
+
isPressed: boolean,
|
|
104
|
+
allowOverflow: boolean,
|
|
105
|
+
) => {
|
|
106
|
+
const xRef = useRef(new Animated.Value(0));
|
|
107
|
+
const widthRef = useRef(0);
|
|
108
|
+
const contentContainerRef = useRef<FollowerContainer | null>(null);
|
|
109
|
+
|
|
110
|
+
const {current: x} = xRef;
|
|
111
|
+
|
|
112
|
+
const update = useCallback(
|
|
113
|
+
(thumbPositionInView, value) => {
|
|
114
|
+
const {current: width} = widthRef;
|
|
115
|
+
const {current: containerWidth} = containerWidthRef;
|
|
116
|
+
const position = thumbPositionInView - width / 2;
|
|
117
|
+
xRef.current.setValue(
|
|
118
|
+
allowOverflow ? position : clamp(position, 0, containerWidth - width),
|
|
119
|
+
);
|
|
120
|
+
contentContainerRef.current?.setValue(value);
|
|
121
|
+
},
|
|
122
|
+
[widthRef, containerWidthRef, allowOverflow],
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const handleLayout = useWidthLayout(widthRef, () => {
|
|
126
|
+
update(
|
|
127
|
+
gestureStateRef.current.lastPosition,
|
|
128
|
+
gestureStateRef.current.lastValue,
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const transform = {transform: [{translateX: x}]};
|
|
133
|
+
const follower = (
|
|
134
|
+
<Animated.View style={[transform, {opacity: isPressed ? 1 : 0}]}>
|
|
135
|
+
<FollowerContainer onLayout={handleLayout} ref={contentContainerRef} />
|
|
136
|
+
</Animated.View>
|
|
137
|
+
);
|
|
138
|
+
return [follower, update];
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
interface InProps {
|
|
142
|
+
low: number;
|
|
143
|
+
high: number;
|
|
144
|
+
min: number;
|
|
145
|
+
max: number;
|
|
146
|
+
step: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const useSelectedRail = (
|
|
150
|
+
inPropsRef: MutableRefObject<InProps>,
|
|
151
|
+
containerWidthRef: MutableRefObject<number>,
|
|
152
|
+
thumbWidth: number,
|
|
153
|
+
disableRange: boolean,
|
|
154
|
+
) => {
|
|
155
|
+
const {current: left} = useRef(new Animated.Value(0));
|
|
156
|
+
const {current: right} = useRef(new Animated.Value(0));
|
|
157
|
+
const update = useCallback(() => {
|
|
158
|
+
const {low, high, min, max} = inPropsRef.current;
|
|
159
|
+
const {current: containerWidth} = containerWidthRef;
|
|
160
|
+
const fullScale = (max - min) / (containerWidth - thumbWidth);
|
|
161
|
+
const leftValue = (low - min) / fullScale;
|
|
162
|
+
const rightValue = (max - high) / fullScale;
|
|
163
|
+
left.setValue(disableRange ? 0 : leftValue);
|
|
164
|
+
right.setValue(
|
|
165
|
+
disableRange ? containerWidth - thumbWidth - leftValue : rightValue,
|
|
166
|
+
);
|
|
167
|
+
}, [inPropsRef, containerWidthRef, disableRange, thumbWidth, left, right]);
|
|
168
|
+
const styles = useMemo(
|
|
169
|
+
() => ({
|
|
170
|
+
position: 'absolute',
|
|
171
|
+
left: I18nManager.isRTL ? right : left,
|
|
172
|
+
right: I18nManager.isRTL ? left : right,
|
|
173
|
+
}),
|
|
174
|
+
[left, right],
|
|
175
|
+
);
|
|
176
|
+
return [styles, update];
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param floating
|
|
181
|
+
* @returns {{onLayout: ((function({nativeEvent: *}): void)|undefined), style: [*, {top}]}}
|
|
182
|
+
*/
|
|
183
|
+
export const useLabelContainerProps = (floating: boolean) => {
|
|
184
|
+
const [labelContainerHeight, setLabelContainerHeight] = useState(0);
|
|
185
|
+
const onLayout = useCallback(({nativeEvent}) => {
|
|
186
|
+
const {
|
|
187
|
+
layout: {height},
|
|
188
|
+
} = nativeEvent;
|
|
189
|
+
setLabelContainerHeight(height);
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
192
|
+
const top = floating ? -labelContainerHeight : 0;
|
|
193
|
+
const style = [
|
|
194
|
+
floating ? styles.labelFloatingContainer : styles.labelFixedContainer,
|
|
195
|
+
{top},
|
|
196
|
+
];
|
|
197
|
+
return {style, onLayout: onLayout};
|
|
198
|
+
};
|
package/index.tsx
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
memo,
|
|
3
|
+
ReactNode,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import {
|
|
12
|
+
Animated,
|
|
13
|
+
GestureResponderEvent,
|
|
14
|
+
PanResponder,
|
|
15
|
+
PanResponderGestureState,
|
|
16
|
+
View,
|
|
17
|
+
ViewProps,
|
|
18
|
+
} from 'react-native';
|
|
19
|
+
|
|
20
|
+
import styles from './styles';
|
|
21
|
+
import {
|
|
22
|
+
useLabelContainerProps,
|
|
23
|
+
useLowHigh,
|
|
24
|
+
useSelectedRail,
|
|
25
|
+
useThumbFollower,
|
|
26
|
+
useWidthLayout,
|
|
27
|
+
} from './hooks';
|
|
28
|
+
import {clamp, getValueForPosition, isLowCloser} from './helpers';
|
|
29
|
+
import {ApplicationContext, Radius, Shadow} from '@momo-kits/foundation';
|
|
30
|
+
|
|
31
|
+
const trueFunc = () => true;
|
|
32
|
+
const falseFunc = () => false;
|
|
33
|
+
|
|
34
|
+
export interface SliderProps extends ViewProps {
|
|
35
|
+
min: number;
|
|
36
|
+
max: number;
|
|
37
|
+
minRange?: number;
|
|
38
|
+
step: number;
|
|
39
|
+
low?: number;
|
|
40
|
+
high?: number;
|
|
41
|
+
allowLabelOverflow?: boolean;
|
|
42
|
+
disableRange?: boolean;
|
|
43
|
+
disabled?: boolean;
|
|
44
|
+
floatingLabel?: boolean;
|
|
45
|
+
renderRail: () => ReactNode;
|
|
46
|
+
renderRailSelected: () => ReactNode;
|
|
47
|
+
onValueChanged?: (low: number, high: number, byUser: boolean) => void;
|
|
48
|
+
onSliderTouchStart?: (low: number, high: number) => void;
|
|
49
|
+
onSliderTouchEnd?: (low: number, high: number) => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const Slider: React.FC<SliderProps> = ({
|
|
53
|
+
min,
|
|
54
|
+
max,
|
|
55
|
+
minRange = 0,
|
|
56
|
+
step,
|
|
57
|
+
low: lowProp,
|
|
58
|
+
high: highProp,
|
|
59
|
+
floatingLabel = false,
|
|
60
|
+
allowLabelOverflow = false,
|
|
61
|
+
disableRange = false,
|
|
62
|
+
disabled = false,
|
|
63
|
+
onValueChanged,
|
|
64
|
+
onSliderTouchStart,
|
|
65
|
+
onSliderTouchEnd,
|
|
66
|
+
renderRail,
|
|
67
|
+
renderRailSelected,
|
|
68
|
+
...restProps
|
|
69
|
+
}) => {
|
|
70
|
+
const {theme} = useContext(ApplicationContext);
|
|
71
|
+
|
|
72
|
+
const {inPropsRef, inPropsRefPrev, setLow, setHigh} = useLowHigh(
|
|
73
|
+
lowProp,
|
|
74
|
+
disableRange ? max : highProp,
|
|
75
|
+
min,
|
|
76
|
+
max,
|
|
77
|
+
step,
|
|
78
|
+
);
|
|
79
|
+
const lowThumbXRef = useRef(new Animated.Value(0));
|
|
80
|
+
const highThumbXRef = useRef(new Animated.Value(0));
|
|
81
|
+
const pointerX = useRef(new Animated.Value(0)).current;
|
|
82
|
+
const {current: lowThumbX} = lowThumbXRef;
|
|
83
|
+
const {current: highThumbX} = highThumbXRef;
|
|
84
|
+
|
|
85
|
+
const gestureStateRef = useRef({isLow: true, lastValue: 0, lastPosition: 0});
|
|
86
|
+
const [isPressed, setPressed] = useState(false);
|
|
87
|
+
|
|
88
|
+
const containerWidthRef = useRef(0);
|
|
89
|
+
const [thumbWidth, setThumbWidth] = useState(0);
|
|
90
|
+
|
|
91
|
+
const [selectedRailStyle, updateSelectedRail] = useSelectedRail(
|
|
92
|
+
inPropsRef,
|
|
93
|
+
containerWidthRef,
|
|
94
|
+
thumbWidth,
|
|
95
|
+
disableRange,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const updateThumbs = useCallback(() => {
|
|
99
|
+
const {current: containerWidth} = containerWidthRef;
|
|
100
|
+
if (!thumbWidth || !containerWidth) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const {low, high} = inPropsRef.current;
|
|
104
|
+
if (!disableRange) {
|
|
105
|
+
const {current: highThumbX} = highThumbXRef;
|
|
106
|
+
const highPosition =
|
|
107
|
+
((high - min) / (max - min)) * (containerWidth - thumbWidth);
|
|
108
|
+
highThumbX.setValue(highPosition);
|
|
109
|
+
}
|
|
110
|
+
const {current: lowThumbX} = lowThumbXRef;
|
|
111
|
+
const lowPosition =
|
|
112
|
+
((low - min) / (max - min)) * (containerWidth - thumbWidth);
|
|
113
|
+
lowThumbX.setValue(lowPosition);
|
|
114
|
+
updateSelectedRail();
|
|
115
|
+
onValueChanged?.(low, high, false);
|
|
116
|
+
}, [
|
|
117
|
+
disableRange,
|
|
118
|
+
inPropsRef,
|
|
119
|
+
max,
|
|
120
|
+
min,
|
|
121
|
+
onValueChanged,
|
|
122
|
+
thumbWidth,
|
|
123
|
+
updateSelectedRail,
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
const {lowPrev, highPrev} = inPropsRefPrev;
|
|
128
|
+
if (
|
|
129
|
+
(lowProp !== undefined && lowProp !== lowPrev) ||
|
|
130
|
+
(highProp !== undefined && highProp !== highPrev)
|
|
131
|
+
) {
|
|
132
|
+
updateThumbs();
|
|
133
|
+
}
|
|
134
|
+
}, [highProp, inPropsRefPrev.lowPrev, inPropsRefPrev.highPrev, lowProp]);
|
|
135
|
+
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
updateThumbs();
|
|
138
|
+
}, [updateThumbs]);
|
|
139
|
+
|
|
140
|
+
const handleContainerLayout = useWidthLayout(containerWidthRef, updateThumbs);
|
|
141
|
+
const handleThumbLayout = useCallback(
|
|
142
|
+
({nativeEvent}) => {
|
|
143
|
+
const {
|
|
144
|
+
layout: {width},
|
|
145
|
+
} = nativeEvent;
|
|
146
|
+
if (thumbWidth !== width) {
|
|
147
|
+
setThumbWidth(width);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
[thumbWidth],
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const lowStyles = useMemo(() => {
|
|
154
|
+
return {transform: [{translateX: lowThumbX}]};
|
|
155
|
+
}, [lowThumbX]);
|
|
156
|
+
|
|
157
|
+
const highStyles = useMemo(() => {
|
|
158
|
+
return disableRange
|
|
159
|
+
? null
|
|
160
|
+
: [styles.highThumbContainer, {transform: [{translateX: highThumbX}]}];
|
|
161
|
+
}, [disableRange, highThumbX]);
|
|
162
|
+
|
|
163
|
+
const railContainerStyles = useMemo(() => {
|
|
164
|
+
return [styles.railsContainer, {marginHorizontal: thumbWidth / 2}];
|
|
165
|
+
}, [thumbWidth]);
|
|
166
|
+
|
|
167
|
+
const [labelView, labelUpdate] = useThumbFollower(
|
|
168
|
+
containerWidthRef,
|
|
169
|
+
gestureStateRef,
|
|
170
|
+
isPressed,
|
|
171
|
+
allowLabelOverflow,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const renderThumb = (name: string) => {
|
|
175
|
+
return (
|
|
176
|
+
<View
|
|
177
|
+
style={[
|
|
178
|
+
Shadow.Light,
|
|
179
|
+
{
|
|
180
|
+
width: 20,
|
|
181
|
+
height: 20,
|
|
182
|
+
borderRadius: Radius.M,
|
|
183
|
+
borderWidth: 4,
|
|
184
|
+
borderColor: theme.colors.background.surface,
|
|
185
|
+
backgroundColor: theme.colors.primary,
|
|
186
|
+
},
|
|
187
|
+
]}
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const lowThumb = renderThumb('low');
|
|
193
|
+
const highThumb = renderThumb('high');
|
|
194
|
+
|
|
195
|
+
const labelContainerProps = useLabelContainerProps(floatingLabel);
|
|
196
|
+
|
|
197
|
+
const {panHandlers} = useMemo(
|
|
198
|
+
() =>
|
|
199
|
+
PanResponder.create({
|
|
200
|
+
onStartShouldSetPanResponderCapture: falseFunc,
|
|
201
|
+
onMoveShouldSetPanResponderCapture: falseFunc,
|
|
202
|
+
onPanResponderTerminationRequest: falseFunc,
|
|
203
|
+
onPanResponderTerminate: trueFunc,
|
|
204
|
+
onShouldBlockNativeResponder: trueFunc,
|
|
205
|
+
|
|
206
|
+
onMoveShouldSetPanResponder: (
|
|
207
|
+
evt: GestureResponderEvent,
|
|
208
|
+
gestureState: PanResponderGestureState,
|
|
209
|
+
) => Math.abs(gestureState.dx) > 2 * Math.abs(gestureState.dy),
|
|
210
|
+
|
|
211
|
+
onPanResponderGrant: ({nativeEvent}, gestureState) => {
|
|
212
|
+
if (disabled) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const {numberActiveTouches} = gestureState;
|
|
216
|
+
if (numberActiveTouches > 1) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
setPressed(true);
|
|
220
|
+
const {current: lowThumbX} = lowThumbXRef;
|
|
221
|
+
const {current: highThumbX} = highThumbXRef;
|
|
222
|
+
const {locationX: downX, pageX} = nativeEvent;
|
|
223
|
+
const containerX = pageX - downX;
|
|
224
|
+
|
|
225
|
+
const {low, high, min, max} = inPropsRef.current;
|
|
226
|
+
onSliderTouchStart?.(low, high);
|
|
227
|
+
const containerWidth = containerWidthRef.current;
|
|
228
|
+
|
|
229
|
+
const lowPosition =
|
|
230
|
+
thumbWidth / 2 +
|
|
231
|
+
((low - min) / (max - min)) * (containerWidth - thumbWidth);
|
|
232
|
+
const highPosition =
|
|
233
|
+
thumbWidth / 2 +
|
|
234
|
+
((high - min) / (max - min)) * (containerWidth - thumbWidth);
|
|
235
|
+
|
|
236
|
+
const isLow =
|
|
237
|
+
disableRange || isLowCloser(downX, lowPosition, highPosition);
|
|
238
|
+
gestureStateRef.current.isLow = isLow;
|
|
239
|
+
|
|
240
|
+
const handlePositionChange = (positionInView: number) => {
|
|
241
|
+
const {low, high, min, max, step} = inPropsRef.current;
|
|
242
|
+
const minValue = isLow ? min : low + minRange;
|
|
243
|
+
const maxValue = isLow ? high - minRange : max;
|
|
244
|
+
const value = clamp(
|
|
245
|
+
getValueForPosition(
|
|
246
|
+
positionInView,
|
|
247
|
+
containerWidth,
|
|
248
|
+
thumbWidth,
|
|
249
|
+
min,
|
|
250
|
+
max,
|
|
251
|
+
step,
|
|
252
|
+
),
|
|
253
|
+
minValue,
|
|
254
|
+
maxValue,
|
|
255
|
+
);
|
|
256
|
+
if (gestureStateRef.current.lastValue === value) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const availableSpace = containerWidth - thumbWidth;
|
|
260
|
+
const absolutePosition =
|
|
261
|
+
((value - min) / (max - min)) * availableSpace;
|
|
262
|
+
gestureStateRef.current.lastValue = value;
|
|
263
|
+
gestureStateRef.current.lastPosition =
|
|
264
|
+
absolutePosition + thumbWidth / 2;
|
|
265
|
+
(isLow ? lowThumbX : highThumbX).setValue(absolutePosition);
|
|
266
|
+
onValueChanged?.(isLow ? value : low, isLow ? high : value, true);
|
|
267
|
+
(isLow ? setLow : setHigh)(value);
|
|
268
|
+
labelUpdate &&
|
|
269
|
+
labelUpdate(gestureStateRef.current.lastPosition, value);
|
|
270
|
+
|
|
271
|
+
updateSelectedRail();
|
|
272
|
+
};
|
|
273
|
+
handlePositionChange(downX);
|
|
274
|
+
pointerX.removeAllListeners();
|
|
275
|
+
pointerX.addListener(({value: pointerPosition}) => {
|
|
276
|
+
const positionInView = pointerPosition - containerX;
|
|
277
|
+
handlePositionChange(positionInView);
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
onPanResponderMove: disabled
|
|
282
|
+
? undefined
|
|
283
|
+
: Animated.event([null, {moveX: pointerX}], {useNativeDriver: false}),
|
|
284
|
+
|
|
285
|
+
onPanResponderRelease: () => {
|
|
286
|
+
setPressed(false);
|
|
287
|
+
const {low, high} = inPropsRef.current;
|
|
288
|
+
onSliderTouchEnd?.(low, high);
|
|
289
|
+
},
|
|
290
|
+
}),
|
|
291
|
+
[
|
|
292
|
+
pointerX,
|
|
293
|
+
inPropsRef,
|
|
294
|
+
thumbWidth,
|
|
295
|
+
disableRange,
|
|
296
|
+
disabled,
|
|
297
|
+
onValueChanged,
|
|
298
|
+
setLow,
|
|
299
|
+
setHigh,
|
|
300
|
+
labelUpdate,
|
|
301
|
+
updateSelectedRail,
|
|
302
|
+
],
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const renderTrack = (color: string) => {
|
|
306
|
+
return (
|
|
307
|
+
<View
|
|
308
|
+
style={{
|
|
309
|
+
width: '100%',
|
|
310
|
+
height: 4,
|
|
311
|
+
borderRadius: Radius.L,
|
|
312
|
+
backgroundColor: color,
|
|
313
|
+
}}
|
|
314
|
+
/>
|
|
315
|
+
);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<View {...restProps}>
|
|
320
|
+
<View {...labelContainerProps}>{labelView}</View>
|
|
321
|
+
<View onLayout={handleContainerLayout} style={styles.controlsContainer}>
|
|
322
|
+
<View style={railContainerStyles}>
|
|
323
|
+
{renderTrack(theme.colors.background.default)}
|
|
324
|
+
<Animated.View style={selectedRailStyle}>
|
|
325
|
+
{renderTrack(theme.colors.primary)}
|
|
326
|
+
</Animated.View>
|
|
327
|
+
</View>
|
|
328
|
+
<Animated.View style={lowStyles} onLayout={handleThumbLayout}>
|
|
329
|
+
{lowThumb}
|
|
330
|
+
</Animated.View>
|
|
331
|
+
{!disableRange && (
|
|
332
|
+
<Animated.View style={highStyles}>{highThumb}</Animated.View>
|
|
333
|
+
)}
|
|
334
|
+
<View
|
|
335
|
+
{...panHandlers}
|
|
336
|
+
style={styles.touchableArea}
|
|
337
|
+
collapsable={false}
|
|
338
|
+
/>
|
|
339
|
+
</View>
|
|
340
|
+
</View>
|
|
341
|
+
);
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export default memo(Slider);
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@momo-kits/slider",
|
|
3
|
-
"version": "0.77.
|
|
3
|
+
"version": "0.77.4",
|
|
4
4
|
"private": false,
|
|
5
|
-
"main": "index.
|
|
5
|
+
"main": "index.tsx",
|
|
6
6
|
"dependencies": {},
|
|
7
7
|
"peerDependencies": {
|
|
8
|
-
"@momo-kits/
|
|
9
|
-
"lodash": "^4.17.15",
|
|
8
|
+
"@momo-kits/foundation": "latest",
|
|
10
9
|
"prop-types": "^15.7.2",
|
|
11
10
|
"react": "16.9.0",
|
|
12
11
|
"react-native": ">=0.55"
|
package/publish.sh
CHANGED
|
@@ -26,4 +26,4 @@ cd ..
|
|
|
26
26
|
rm -rf dist
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
##curl -X POST -H 'Content-Type: application/json' 'https://chat.googleapis.com/v1/spaces/AAAAbP8987c/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&token=UGSFRvk_oYb9uGsAgs31bVvMm6jDkmD8zihGm3eyaQA%3D&threadKey=JoaXTEYaNNkl' -d '{"text": "@momo-kits/
|
|
29
|
+
##curl -X POST -H 'Content-Type: application/json' 'https://chat.googleapis.com/v1/spaces/AAAAbP8987c/messages?key=AIzaSyDdI0hCZtE6vySjMm-WEfRq3CPzqKqqsHI&token=UGSFRvk_oYb9uGsAgs31bVvMm6jDkmD8zihGm3eyaQA%3D&threadKey=JoaXTEYaNNkl' -d '{"text": "@momo-kits/slider new version release: '*"$VERSION"*' https://www.npmjs.com/package/@momo-kits/slider"}'
|
package/styles.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {I18nManager, StyleSheet} from 'react-native';
|
|
2
|
+
|
|
3
|
+
export default StyleSheet.create({
|
|
4
|
+
controlsContainer: {
|
|
5
|
+
flexDirection: 'row',
|
|
6
|
+
justifyContent: I18nManager.isRTL ? 'flex-end' : 'flex-start',
|
|
7
|
+
alignItems: 'center',
|
|
8
|
+
},
|
|
9
|
+
highThumbContainer: {
|
|
10
|
+
position: 'absolute',
|
|
11
|
+
},
|
|
12
|
+
railsContainer: {
|
|
13
|
+
...StyleSheet.absoluteFillObject,
|
|
14
|
+
flexDirection: 'row',
|
|
15
|
+
alignItems: 'center',
|
|
16
|
+
},
|
|
17
|
+
labelFixedContainer: {
|
|
18
|
+
alignItems: I18nManager.isRTL ? 'flex-end' : 'flex-start',
|
|
19
|
+
},
|
|
20
|
+
labelFloatingContainer: {
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
left: 0,
|
|
23
|
+
right: 0,
|
|
24
|
+
alignItems: I18nManager.isRTL ? 'flex-end' : 'flex-start',
|
|
25
|
+
},
|
|
26
|
+
touchableArea: {
|
|
27
|
+
...StyleSheet.absoluteFillObject,
|
|
28
|
+
},
|
|
29
|
+
});
|