@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.
Files changed (56) hide show
  1. package/dist/Camera/index.d.ts +31 -0
  2. package/dist/Camera/index.js +96 -0
  3. package/dist/Map/index.d.ts +86 -0
  4. package/dist/Map/index.js +83 -0
  5. package/dist/Progress/index.d.ts +19 -0
  6. package/dist/Progress/index.js +82 -0
  7. package/dist/RichText/index.d.ts +7 -0
  8. package/dist/RichText/index.js +56 -0
  9. package/dist/Slider/index.d.ts +22 -0
  10. package/dist/Slider/index.js +58 -0
  11. package/dist/Swiper/carousel.d.ts +17 -0
  12. package/dist/Swiper/carousel.js +39 -0
  13. package/dist/Swiper/index.d.ts +19 -0
  14. package/dist/Swiper/index.js +15 -0
  15. package/dist/Swiper/pagination.d.ts +11 -0
  16. package/dist/Swiper/pagination.js +50 -0
  17. package/dist/Video/index.d.ts +42 -0
  18. package/dist/Video/index.js +168 -0
  19. package/dist/Video/utils.d.ts +3 -0
  20. package/dist/Video/utils.js +13 -0
  21. package/dist/WebView/index.d.ts +9 -0
  22. package/dist/WebView/index.js +6 -0
  23. package/dist/assets/loading.png +0 -0
  24. package/dist/assets/video/full.png +0 -0
  25. package/dist/assets/video/mute.png +0 -0
  26. package/dist/assets/video/pause.png +0 -0
  27. package/dist/assets/video/play.png +0 -0
  28. package/dist/assets/video/shrink.png +0 -0
  29. package/dist/assets/video/unmute.png +0 -0
  30. package/dist/assets/video/volume.png +0 -0
  31. package/dist/index.d.ts +8 -0
  32. package/dist/index.js +10 -0
  33. package/dist/utils/index.d.ts +4 -0
  34. package/dist/utils/index.js +8 -0
  35. package/package.json +58 -0
  36. package/src/Camera/index.tsx +179 -0
  37. package/src/Map/index.tsx +275 -0
  38. package/src/Progress/index.tsx +142 -0
  39. package/src/RichText/index.tsx +82 -0
  40. package/src/Slider/index.tsx +118 -0
  41. package/src/Swiper/carousel.tsx +119 -0
  42. package/src/Swiper/index.tsx +64 -0
  43. package/src/Swiper/pagination.tsx +70 -0
  44. package/src/Video/index.tsx +303 -0
  45. package/src/Video/utils.ts +14 -0
  46. package/src/WebView/index.tsx +25 -0
  47. package/src/assets/loading.png +0 -0
  48. package/src/assets/video/full.png +0 -0
  49. package/src/assets/video/mute.png +0 -0
  50. package/src/assets/video/pause.png +0 -0
  51. package/src/assets/video/play.png +0 -0
  52. package/src/assets/video/shrink.png +0 -0
  53. package/src/assets/video/unmute.png +0 -0
  54. package/src/assets/video/volume.png +0 -0
  55. package/src/index.ts +10 -0
  56. 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
+ });