@nebula-rn/components 0.0.1
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/dist/Camera/index.d.ts +31 -0
- package/dist/Camera/index.js +96 -0
- package/dist/Map/index.d.ts +86 -0
- package/dist/Map/index.js +83 -0
- package/dist/Progress/index.d.ts +19 -0
- package/dist/Progress/index.js +82 -0
- package/dist/RichText/index.d.ts +7 -0
- package/dist/RichText/index.js +56 -0
- package/dist/Slider/index.d.ts +22 -0
- package/dist/Slider/index.js +58 -0
- package/dist/Swiper/carousel.d.ts +17 -0
- package/dist/Swiper/carousel.js +39 -0
- package/dist/Swiper/index.d.ts +19 -0
- package/dist/Swiper/index.js +15 -0
- package/dist/Swiper/pagination.d.ts +11 -0
- package/dist/Swiper/pagination.js +50 -0
- package/dist/Video/index.d.ts +42 -0
- package/dist/Video/index.js +168 -0
- package/dist/Video/utils.d.ts +3 -0
- package/dist/Video/utils.js +13 -0
- package/dist/WebView/index.d.ts +9 -0
- package/dist/WebView/index.js +6 -0
- package/dist/assets/loading.png +0 -0
- package/dist/assets/video/full.png +0 -0
- package/dist/assets/video/mute.png +0 -0
- package/dist/assets/video/pause.png +0 -0
- package/dist/assets/video/play.png +0 -0
- package/dist/assets/video/shrink.png +0 -0
- package/dist/assets/video/unmute.png +0 -0
- package/dist/assets/video/volume.png +0 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +8 -0
- package/package.json +58 -0
- package/src/Camera/index.tsx +179 -0
- package/src/Map/index.tsx +275 -0
- package/src/Progress/index.tsx +142 -0
- package/src/RichText/index.tsx +82 -0
- package/src/Slider/index.tsx +118 -0
- package/src/Swiper/carousel.tsx +119 -0
- package/src/Swiper/index.tsx +64 -0
- package/src/Swiper/pagination.tsx +70 -0
- package/src/Video/index.tsx +303 -0
- package/src/Video/utils.ts +14 -0
- package/src/WebView/index.tsx +25 -0
- package/src/assets/loading.png +0 -0
- package/src/assets/video/full.png +0 -0
- package/src/assets/video/mute.png +0 -0
- package/src/assets/video/pause.png +0 -0
- package/src/assets/video/play.png +0 -0
- package/src/assets/video/shrink.png +0 -0
- package/src/assets/video/unmute.png +0 -0
- package/src/assets/video/volume.png +0 -0
- package/src/index.ts +10 -0
- package/src/utils/index.ts +12 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import React, { useCallback, FC } from 'react';
|
|
2
|
+
import { Dimensions, StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import MapView, {
|
|
4
|
+
Callout,
|
|
5
|
+
Circle,
|
|
6
|
+
Marker,
|
|
7
|
+
Polygon,
|
|
8
|
+
Polyline,
|
|
9
|
+
Region,
|
|
10
|
+
MapPressEvent,
|
|
11
|
+
Details,
|
|
12
|
+
} from 'react-native-maps';
|
|
13
|
+
|
|
14
|
+
const { width, height } = Dimensions.get('window');
|
|
15
|
+
const ASPECT_RATIO = width / height;
|
|
16
|
+
const LATITUDE_DELTA = 0.0922;
|
|
17
|
+
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
|
|
18
|
+
|
|
19
|
+
export interface CalloutItem {
|
|
20
|
+
content?: string;
|
|
21
|
+
color?: string;
|
|
22
|
+
fontSize?: number;
|
|
23
|
+
borderRadius?: number;
|
|
24
|
+
borderWidth?: number;
|
|
25
|
+
borderColor?: string;
|
|
26
|
+
bgColor?: string;
|
|
27
|
+
padding?: number;
|
|
28
|
+
display?: 'BYCLICK' | 'ALWAYS';
|
|
29
|
+
textAlign?: 'left' | 'right' | 'center';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface MarkerItem {
|
|
33
|
+
id: number;
|
|
34
|
+
latitude: number;
|
|
35
|
+
longitude: number;
|
|
36
|
+
title?: string;
|
|
37
|
+
iconPath: string;
|
|
38
|
+
rotate?: number;
|
|
39
|
+
alpha?: number;
|
|
40
|
+
callout?: CalloutItem;
|
|
41
|
+
anchor?: { x: number; y: number };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface PolylineItem {
|
|
45
|
+
points: Array<{ latitude: number; longitude: number }>;
|
|
46
|
+
color?: string;
|
|
47
|
+
width?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface PolygonItem {
|
|
51
|
+
points: Array<{ latitude: number; longitude: number }>;
|
|
52
|
+
strokeWidth?: number;
|
|
53
|
+
strokeColor?: string;
|
|
54
|
+
fillColor?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CircleItem {
|
|
58
|
+
latitude: number;
|
|
59
|
+
longitude: number;
|
|
60
|
+
color?: string;
|
|
61
|
+
fillColor?: string;
|
|
62
|
+
radius: number;
|
|
63
|
+
strokeWidth?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface Coordinate {
|
|
67
|
+
latitude: number;
|
|
68
|
+
longitude: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface MapProps {
|
|
72
|
+
longitude: number;
|
|
73
|
+
latitude: number;
|
|
74
|
+
scale?: number;
|
|
75
|
+
markers?: Array<MarkerItem>;
|
|
76
|
+
polyline?: Array<PolylineItem>;
|
|
77
|
+
polygons?: Array<PolygonItem>;
|
|
78
|
+
circles?: Array<CircleItem>;
|
|
79
|
+
includePoints?: Array<Coordinate>;
|
|
80
|
+
showLocation?: boolean;
|
|
81
|
+
subkey?: string;
|
|
82
|
+
enable3D?: boolean;
|
|
83
|
+
showCompass?: boolean;
|
|
84
|
+
enableOverlooking?: boolean;
|
|
85
|
+
enableZoom?: boolean;
|
|
86
|
+
enableScroll?: boolean;
|
|
87
|
+
enableRotate?: boolean;
|
|
88
|
+
|
|
89
|
+
onMarkerClick?: (markerId: number) => void;
|
|
90
|
+
onCalloutClick?: (markerId: number) => void;
|
|
91
|
+
onControlClick?: (controlId?: number) => void;
|
|
92
|
+
onRegionChange?: (event: {
|
|
93
|
+
type: 'begin' | 'end';
|
|
94
|
+
timeStamp: number;
|
|
95
|
+
causedBy?: 'scale' | 'drag' | 'update';
|
|
96
|
+
}) => void;
|
|
97
|
+
onClick?: (coordinate: Coordinate) => void;
|
|
98
|
+
onUpdated?: () => void;
|
|
99
|
+
onPoiClick?: (event: any) => void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const Map: FC<MapProps> = props => {
|
|
103
|
+
const {
|
|
104
|
+
latitude = 0,
|
|
105
|
+
longitude = 0,
|
|
106
|
+
markers = [],
|
|
107
|
+
polyline = [],
|
|
108
|
+
polygons = [],
|
|
109
|
+
circles = [],
|
|
110
|
+
showLocation = false,
|
|
111
|
+
showCompass = true,
|
|
112
|
+
enableZoom = true,
|
|
113
|
+
enableScroll = true,
|
|
114
|
+
enableRotate = true,
|
|
115
|
+
onMarkerClick,
|
|
116
|
+
onCalloutClick,
|
|
117
|
+
onRegionChange,
|
|
118
|
+
onClick,
|
|
119
|
+
onUpdated = () => {},
|
|
120
|
+
onPoiClick = () => {},
|
|
121
|
+
} = props;
|
|
122
|
+
|
|
123
|
+
const onRenderCallout = useCallback(
|
|
124
|
+
(marker: MarkerItem) => {
|
|
125
|
+
const { id, callout } = marker;
|
|
126
|
+
if (!callout) return null;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<Callout onPress={() => onCalloutClick?.(id)}>
|
|
130
|
+
<View
|
|
131
|
+
style={[
|
|
132
|
+
styles.calloutContainer,
|
|
133
|
+
{
|
|
134
|
+
borderRadius: callout.borderRadius,
|
|
135
|
+
borderWidth: callout.borderWidth,
|
|
136
|
+
borderColor: callout.borderColor,
|
|
137
|
+
backgroundColor: callout.bgColor,
|
|
138
|
+
padding: callout.padding,
|
|
139
|
+
},
|
|
140
|
+
]}
|
|
141
|
+
>
|
|
142
|
+
<Text
|
|
143
|
+
style={{
|
|
144
|
+
fontSize: callout.fontSize,
|
|
145
|
+
color: callout.color,
|
|
146
|
+
textAlign: callout.textAlign,
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{callout.content}
|
|
150
|
+
</Text>
|
|
151
|
+
</View>
|
|
152
|
+
</Callout>
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
[onCalloutClick],
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const onHandleRegionChange = useCallback(
|
|
159
|
+
(region: Region, details: Details) => {
|
|
160
|
+
onRegionChange?.({
|
|
161
|
+
type: 'begin',
|
|
162
|
+
timeStamp: Date.now(),
|
|
163
|
+
causedBy: details.isGesture ? 'drag' : 'update',
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
[onRegionChange],
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const onHandleRegionChangeComplete = useCallback(
|
|
170
|
+
(region: Region, details: Details) => {
|
|
171
|
+
onRegionChange?.({
|
|
172
|
+
type: 'end',
|
|
173
|
+
timeStamp: Date.now(),
|
|
174
|
+
causedBy: details.isGesture ? 'drag' : 'update',
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
[onRegionChange],
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const onHandleMapPress = useCallback(
|
|
181
|
+
(e: MapPressEvent) => {
|
|
182
|
+
onClick?.(e.nativeEvent.coordinate);
|
|
183
|
+
},
|
|
184
|
+
[onClick],
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const onHandleMarkerPress = useCallback(
|
|
188
|
+
(id: number) => {
|
|
189
|
+
onMarkerClick?.(id);
|
|
190
|
+
},
|
|
191
|
+
[onMarkerClick],
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<MapView
|
|
196
|
+
style={styles.map}
|
|
197
|
+
region={{
|
|
198
|
+
latitude,
|
|
199
|
+
longitude,
|
|
200
|
+
latitudeDelta: LATITUDE_DELTA,
|
|
201
|
+
longitudeDelta: LONGITUDE_DELTA,
|
|
202
|
+
}}
|
|
203
|
+
minZoomLevel={5}
|
|
204
|
+
maxZoomLevel={18}
|
|
205
|
+
showsUserLocation={showLocation}
|
|
206
|
+
showsCompass={showCompass}
|
|
207
|
+
zoomEnabled={enableZoom}
|
|
208
|
+
scrollEnabled={enableScroll}
|
|
209
|
+
rotateEnabled={enableRotate}
|
|
210
|
+
onRegionChange={onHandleRegionChange}
|
|
211
|
+
onRegionChangeComplete={onHandleRegionChangeComplete}
|
|
212
|
+
onPress={onHandleMapPress}
|
|
213
|
+
onMapReady={onUpdated}
|
|
214
|
+
onPoiClick={onPoiClick}
|
|
215
|
+
>
|
|
216
|
+
{markers.map(marker => (
|
|
217
|
+
<Marker
|
|
218
|
+
id={`marker-${marker.id}`}
|
|
219
|
+
coordinate={{
|
|
220
|
+
latitude: marker.latitude,
|
|
221
|
+
longitude: marker.longitude,
|
|
222
|
+
}}
|
|
223
|
+
title={marker.title}
|
|
224
|
+
image={marker.iconPath ? { uri: marker.iconPath } : undefined}
|
|
225
|
+
rotation={marker.rotate}
|
|
226
|
+
opacity={marker.alpha}
|
|
227
|
+
anchor={marker.anchor}
|
|
228
|
+
onPress={() => onHandleMarkerPress(marker.id)}
|
|
229
|
+
>
|
|
230
|
+
{onRenderCallout(marker)}
|
|
231
|
+
</Marker>
|
|
232
|
+
))}
|
|
233
|
+
|
|
234
|
+
{polyline.map((p, index) => (
|
|
235
|
+
<Polyline
|
|
236
|
+
id={`polyline-${index}`}
|
|
237
|
+
coordinates={p.points}
|
|
238
|
+
strokeColor={p.color}
|
|
239
|
+
strokeWidth={p.width}
|
|
240
|
+
/>
|
|
241
|
+
))}
|
|
242
|
+
|
|
243
|
+
{polygons.map((p, index) => (
|
|
244
|
+
<Polygon
|
|
245
|
+
id={`polygon-${index}`}
|
|
246
|
+
coordinates={p.points}
|
|
247
|
+
strokeColor={p.strokeColor}
|
|
248
|
+
strokeWidth={p.strokeWidth}
|
|
249
|
+
fillColor={p.fillColor}
|
|
250
|
+
/>
|
|
251
|
+
))}
|
|
252
|
+
|
|
253
|
+
{circles.map((c, index) => (
|
|
254
|
+
<Circle
|
|
255
|
+
id={`circle-${index}`}
|
|
256
|
+
center={{ latitude: c.latitude, longitude: c.longitude }}
|
|
257
|
+
strokeColor={c.color}
|
|
258
|
+
fillColor={c.fillColor}
|
|
259
|
+
radius={c.radius}
|
|
260
|
+
strokeWidth={c.strokeWidth}
|
|
261
|
+
/>
|
|
262
|
+
))}
|
|
263
|
+
</MapView>
|
|
264
|
+
);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const styles = StyleSheet.create({
|
|
268
|
+
map: {
|
|
269
|
+
flex: 1,
|
|
270
|
+
},
|
|
271
|
+
calloutContainer: {
|
|
272
|
+
backgroundColor: 'white',
|
|
273
|
+
minWidth: 100,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { useEffect, useRef, FC } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
DimensionValue,
|
|
5
|
+
Easing,
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
StyleProp,
|
|
10
|
+
ViewStyle,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
|
|
13
|
+
export interface ProgressActiveEvent {
|
|
14
|
+
percent: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ProgressProps {
|
|
18
|
+
style?: StyleProp<ViewStyle>;
|
|
19
|
+
percent: number;
|
|
20
|
+
showInfo?: boolean;
|
|
21
|
+
borderRadius?: number | string;
|
|
22
|
+
fontSize?: number | string;
|
|
23
|
+
strokeWidth?: number | string;
|
|
24
|
+
activeColor?: string;
|
|
25
|
+
backgroundColor?: string;
|
|
26
|
+
active?: boolean;
|
|
27
|
+
activeMode?: 'backwards' | 'forwards';
|
|
28
|
+
onActiveEnd?: (event: ProgressActiveEvent) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Progress: FC<ProgressProps> = ({
|
|
32
|
+
style,
|
|
33
|
+
percent = 0,
|
|
34
|
+
showInfo,
|
|
35
|
+
borderRadius = 0,
|
|
36
|
+
strokeWidth = 6,
|
|
37
|
+
activeColor = '#09BB07',
|
|
38
|
+
backgroundColor = '#EBEBEB',
|
|
39
|
+
active,
|
|
40
|
+
activeMode = 'backwards',
|
|
41
|
+
onActiveEnd,
|
|
42
|
+
}) => {
|
|
43
|
+
const valve = useRef(new Animated.Value(0)).current;
|
|
44
|
+
const prevPercentRef = useRef(0);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const toValue = percent / 100;
|
|
48
|
+
|
|
49
|
+
if (!active || (activeMode !== 'backwards' && activeMode !== 'forwards')) {
|
|
50
|
+
Animated.timing(valve, {
|
|
51
|
+
toValue,
|
|
52
|
+
duration: 0,
|
|
53
|
+
useNativeDriver: false,
|
|
54
|
+
}).start();
|
|
55
|
+
prevPercentRef.current = percent;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sequence: Animated.CompositeAnimation[] = [];
|
|
60
|
+
const duration =
|
|
61
|
+
((activeMode === 'forwards'
|
|
62
|
+
? Math.abs(percent - prevPercentRef.current)
|
|
63
|
+
: percent) /
|
|
64
|
+
100) *
|
|
65
|
+
1000;
|
|
66
|
+
|
|
67
|
+
if (activeMode === 'backwards') {
|
|
68
|
+
sequence.push(
|
|
69
|
+
Animated.timing(valve, {
|
|
70
|
+
toValue: 0,
|
|
71
|
+
duration: 0,
|
|
72
|
+
useNativeDriver: false,
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
sequence.push(
|
|
78
|
+
Animated.timing(valve, {
|
|
79
|
+
toValue,
|
|
80
|
+
easing: Easing.linear,
|
|
81
|
+
duration,
|
|
82
|
+
useNativeDriver: false,
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const onCompleteAnimation = () => {
|
|
87
|
+
if (onActiveEnd) {
|
|
88
|
+
onActiveEnd({ percent });
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
Animated.sequence(sequence).start(onCompleteAnimation);
|
|
93
|
+
prevPercentRef.current = percent;
|
|
94
|
+
}, [active, activeMode, percent, valve, onActiveEnd]);
|
|
95
|
+
|
|
96
|
+
const width = valve.interpolate({
|
|
97
|
+
inputRange: [0, 1],
|
|
98
|
+
outputRange: ['0%', '100%'],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<View style={[styles.wrapper, style]}>
|
|
103
|
+
<View
|
|
104
|
+
style={[
|
|
105
|
+
styles.bar,
|
|
106
|
+
{
|
|
107
|
+
height: strokeWidth as DimensionValue,
|
|
108
|
+
backgroundColor,
|
|
109
|
+
},
|
|
110
|
+
]}
|
|
111
|
+
>
|
|
112
|
+
<Animated.View
|
|
113
|
+
style={[
|
|
114
|
+
styles.barThumb,
|
|
115
|
+
{
|
|
116
|
+
width,
|
|
117
|
+
height: '100%',
|
|
118
|
+
backgroundColor: activeColor,
|
|
119
|
+
borderBottomRightRadius: Number(borderRadius),
|
|
120
|
+
borderTopRightRadius: Number(borderRadius),
|
|
121
|
+
},
|
|
122
|
+
]}
|
|
123
|
+
/>
|
|
124
|
+
</View>
|
|
125
|
+
{showInfo && <Text style={styles.info}>{percent}%</Text>}
|
|
126
|
+
</View>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const styles = StyleSheet.create({
|
|
131
|
+
wrapper: {
|
|
132
|
+
flexDirection: 'row',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
},
|
|
135
|
+
bar: {
|
|
136
|
+
flexGrow: 1,
|
|
137
|
+
},
|
|
138
|
+
barThumb: {},
|
|
139
|
+
info: {
|
|
140
|
+
marginLeft: 15,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React, { useState, useRef, useCallback, FC } from 'react';
|
|
2
|
+
import { View, StyleProp, ViewStyle, StyleSheet } from 'react-native';
|
|
3
|
+
import { WebView, WebViewMessageEvent } from 'react-native-webview';
|
|
4
|
+
|
|
5
|
+
export interface RichTextProps {
|
|
6
|
+
style?: StyleProp<ViewStyle>;
|
|
7
|
+
html?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const RichText: FC<RichTextProps> = props => {
|
|
11
|
+
const { style, html = '' } = props;
|
|
12
|
+
const [webViewHeight, setWebViewHeight] = useState<number>(0);
|
|
13
|
+
const webviewRef = useRef<WebView>(null);
|
|
14
|
+
|
|
15
|
+
const onMessage = useCallback((event: WebViewMessageEvent): void => {
|
|
16
|
+
const height = Number(event.nativeEvent.data);
|
|
17
|
+
if (height > 0) {
|
|
18
|
+
setWebViewHeight(height);
|
|
19
|
+
}
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
const onLoadEnd = useCallback(() => {
|
|
23
|
+
webviewRef.current?.injectJavaScript(
|
|
24
|
+
'window.ReactNativeWebView.postMessage(document.body.scrollHeight);',
|
|
25
|
+
);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
const injectedJavaScript = `
|
|
29
|
+
(function() {
|
|
30
|
+
document.documentElement.style.padding = '0';
|
|
31
|
+
document.documentElement.style.margin = '0';
|
|
32
|
+
document.body.style.padding = '0';
|
|
33
|
+
document.body.style.margin = '0';
|
|
34
|
+
window.ReactNativeWebView.postMessage(document.body.scrollHeight);
|
|
35
|
+
})();
|
|
36
|
+
true;
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View
|
|
41
|
+
style={[
|
|
42
|
+
{
|
|
43
|
+
height: webViewHeight,
|
|
44
|
+
width: '100%',
|
|
45
|
+
},
|
|
46
|
+
style,
|
|
47
|
+
]}
|
|
48
|
+
>
|
|
49
|
+
<WebView
|
|
50
|
+
ref={webviewRef}
|
|
51
|
+
source={{
|
|
52
|
+
html: `
|
|
53
|
+
<!DOCTYPE html>
|
|
54
|
+
<html>
|
|
55
|
+
<head>
|
|
56
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
|
57
|
+
<style>
|
|
58
|
+
body { font-family: -apple-system, system-ui; overflow: hidden; }
|
|
59
|
+
</style>
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
${html}
|
|
63
|
+
</body>
|
|
64
|
+
</html>
|
|
65
|
+
`,
|
|
66
|
+
}}
|
|
67
|
+
scrollEnabled={false}
|
|
68
|
+
scalesPageToFit={false}
|
|
69
|
+
onMessage={onMessage}
|
|
70
|
+
injectedJavaScript={injectedJavaScript}
|
|
71
|
+
onLoadEnd={onLoadEnd}
|
|
72
|
+
style={styles.webview}
|
|
73
|
+
/>
|
|
74
|
+
</View>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const styles = StyleSheet.create({
|
|
79
|
+
webview: {
|
|
80
|
+
backgroundColor: 'transparent',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { useState, useEffect, useRef, FC } from 'react';
|
|
2
|
+
import { Text, View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import RNSlider from '@react-native-community/slider';
|
|
4
|
+
|
|
5
|
+
export interface SliderChangeEvent {
|
|
6
|
+
value: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SliderProps {
|
|
10
|
+
name?: string;
|
|
11
|
+
style?: StyleProp<ViewStyle>;
|
|
12
|
+
min?: number;
|
|
13
|
+
max?: number;
|
|
14
|
+
step?: number;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
value?: number;
|
|
17
|
+
defaultValue?: number;
|
|
18
|
+
activeColor?: string;
|
|
19
|
+
backgroundColor?: string;
|
|
20
|
+
blockColor?: string;
|
|
21
|
+
showValue?: boolean;
|
|
22
|
+
onChange?: (event: SliderChangeEvent) => void;
|
|
23
|
+
onChanging?: (event: SliderChangeEvent) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Slider: FC<SliderProps> = props => {
|
|
27
|
+
const {
|
|
28
|
+
style,
|
|
29
|
+
min = 0,
|
|
30
|
+
max = 100,
|
|
31
|
+
step = 1,
|
|
32
|
+
disabled,
|
|
33
|
+
activeColor = '#1aad19',
|
|
34
|
+
backgroundColor = '#e9e9e9',
|
|
35
|
+
blockColor = '#fff',
|
|
36
|
+
showValue,
|
|
37
|
+
onChange,
|
|
38
|
+
onChanging,
|
|
39
|
+
value,
|
|
40
|
+
defaultValue,
|
|
41
|
+
} = props;
|
|
42
|
+
|
|
43
|
+
const isControlled = value !== undefined;
|
|
44
|
+
const [currentValue, setCurrentValue] = useState<number>(
|
|
45
|
+
defaultValue ?? (value || 0),
|
|
46
|
+
);
|
|
47
|
+
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (value !== undefined) {
|
|
51
|
+
setCurrentValue(value || 0);
|
|
52
|
+
}
|
|
53
|
+
}, [value]);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!isControlled) return;
|
|
57
|
+
|
|
58
|
+
if (timerRef.current) {
|
|
59
|
+
clearTimeout(timerRef.current);
|
|
60
|
+
}
|
|
61
|
+
timerRef.current = setTimeout(() => {
|
|
62
|
+
if (value !== currentValue) {
|
|
63
|
+
setCurrentValue(value || 0);
|
|
64
|
+
}
|
|
65
|
+
}, 50);
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
if (timerRef.current) {
|
|
69
|
+
clearTimeout(timerRef.current);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}, [currentValue, isControlled, value]);
|
|
73
|
+
|
|
74
|
+
const onSlidingCompleteHandler = (nextValue: number) => {
|
|
75
|
+
if (onChange) {
|
|
76
|
+
onChange({ value: nextValue });
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const onValueChangeHandler = (nextValue: number) => {
|
|
81
|
+
setCurrentValue(nextValue);
|
|
82
|
+
if (onChanging) {
|
|
83
|
+
onChanging({ value: nextValue });
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<View style={styles.wrapper}>
|
|
89
|
+
<RNSlider
|
|
90
|
+
minimumValue={min}
|
|
91
|
+
maximumValue={max}
|
|
92
|
+
step={step}
|
|
93
|
+
disabled={!!disabled}
|
|
94
|
+
value={currentValue}
|
|
95
|
+
minimumTrackTintColor={activeColor}
|
|
96
|
+
maximumTrackTintColor={backgroundColor}
|
|
97
|
+
thumbTintColor={blockColor}
|
|
98
|
+
onSlidingComplete={onSlidingCompleteHandler}
|
|
99
|
+
onValueChange={onValueChangeHandler}
|
|
100
|
+
style={[styles.bar, style as any]}
|
|
101
|
+
/>
|
|
102
|
+
{showValue && <Text style={styles.info}>{currentValue}</Text>}
|
|
103
|
+
</View>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const styles = StyleSheet.create({
|
|
108
|
+
wrapper: {
|
|
109
|
+
flexDirection: 'row',
|
|
110
|
+
alignItems: 'center',
|
|
111
|
+
},
|
|
112
|
+
bar: {
|
|
113
|
+
flexGrow: 1,
|
|
114
|
+
},
|
|
115
|
+
info: {
|
|
116
|
+
marginLeft: 15,
|
|
117
|
+
},
|
|
118
|
+
});
|