@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,31 @@
|
|
|
1
|
+
import { FC, PropsWithChildren } from 'react';
|
|
2
|
+
import { CodeType } from 'react-native-vision-camera';
|
|
3
|
+
export interface CameraInitEventDetail {
|
|
4
|
+
maxZoom: number;
|
|
5
|
+
}
|
|
6
|
+
export interface CameraScanCodeEventDetail {
|
|
7
|
+
charSet: string;
|
|
8
|
+
rawData: string;
|
|
9
|
+
type: CodeType | 'unknown';
|
|
10
|
+
result: string;
|
|
11
|
+
fullResult: string;
|
|
12
|
+
}
|
|
13
|
+
export interface CameraError {
|
|
14
|
+
message: string;
|
|
15
|
+
code?: string;
|
|
16
|
+
nativeError?: unknown;
|
|
17
|
+
}
|
|
18
|
+
export interface CameraProps {
|
|
19
|
+
id?: string;
|
|
20
|
+
className?: string;
|
|
21
|
+
style?: any;
|
|
22
|
+
mode?: 'normal' | 'scanCode';
|
|
23
|
+
resolution?: 'low' | 'medium' | 'high';
|
|
24
|
+
devicePosition?: 'front' | 'back';
|
|
25
|
+
flash?: 'auto' | 'on' | 'off' | 'torch';
|
|
26
|
+
onInitDone?: (event: CameraInitEventDetail) => void;
|
|
27
|
+
onReady?: (event: CameraInitEventDetail) => void;
|
|
28
|
+
onScanCode?: (event: CameraScanCodeEventDetail) => void;
|
|
29
|
+
onError?: (error: CameraError) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare const Camera: FC<PropsWithChildren<CameraProps>>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import React, { useEffect, useCallback, useMemo, } from 'react';
|
|
2
|
+
import { StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import { Camera as RNCamera, useCameraDevice, useCameraPermission, useCodeScanner, } from 'react-native-vision-camera';
|
|
4
|
+
export const Camera = props => {
|
|
5
|
+
const { devicePosition = 'back', style, mode, flash, onScanCode, onError, onInitDone, } = props;
|
|
6
|
+
const { hasPermission, requestPermission } = useCameraPermission();
|
|
7
|
+
const device = useCameraDevice(devicePosition);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
onHandlePermissionRequest();
|
|
10
|
+
}, []);
|
|
11
|
+
const onHandlePermissionRequest = useCallback(async () => {
|
|
12
|
+
if (!hasPermission) {
|
|
13
|
+
try {
|
|
14
|
+
const granted = await requestPermission();
|
|
15
|
+
if (!granted && onError) {
|
|
16
|
+
onError({
|
|
17
|
+
message: 'Camera permission denied',
|
|
18
|
+
code: 'PERMISSION_DENIED',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (onError) {
|
|
24
|
+
onError({
|
|
25
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
26
|
+
code: 'PERMISSION_ERROR',
|
|
27
|
+
nativeError: error,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}, [hasPermission, requestPermission, onError]);
|
|
33
|
+
const onHandleCodeScanned = useCallback((codes) => {
|
|
34
|
+
if (mode === 'scanCode' && codes.length > 0 && onScanCode) {
|
|
35
|
+
const code = codes[0];
|
|
36
|
+
onScanCode({
|
|
37
|
+
result: code.value || '',
|
|
38
|
+
type: code.type,
|
|
39
|
+
charSet: '',
|
|
40
|
+
rawData: code.value || '',
|
|
41
|
+
fullResult: code.value || '',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}, [mode, onScanCode]);
|
|
45
|
+
const codeScanner = useCodeScanner({
|
|
46
|
+
codeTypes: ['qr', 'ean-13'],
|
|
47
|
+
onCodeScanned: onHandleCodeScanned,
|
|
48
|
+
});
|
|
49
|
+
const onHandleError = useCallback((error) => {
|
|
50
|
+
if (onError) {
|
|
51
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
52
|
+
onError({ message: error.message, nativeError: error });
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
onError({ message: 'Unknown error', nativeError: error });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}, [onError]);
|
|
59
|
+
const onHandleInitialized = useCallback(() => {
|
|
60
|
+
if (onInitDone) {
|
|
61
|
+
onInitDone({ maxZoom: device?.maxZoom ?? 1 });
|
|
62
|
+
}
|
|
63
|
+
}, [onInitDone, device]);
|
|
64
|
+
const torchState = useMemo(() => {
|
|
65
|
+
if (flash === 'on' || flash === 'torch')
|
|
66
|
+
return 'on';
|
|
67
|
+
return 'off';
|
|
68
|
+
}, [flash]);
|
|
69
|
+
if (!hasPermission) {
|
|
70
|
+
return (<View style={styles.center}>
|
|
71
|
+
<Text>No access to camera</Text>
|
|
72
|
+
</View>);
|
|
73
|
+
}
|
|
74
|
+
if (!device) {
|
|
75
|
+
return (<View style={styles.center}>
|
|
76
|
+
<Text>No camera device found</Text>
|
|
77
|
+
</View>);
|
|
78
|
+
}
|
|
79
|
+
return (<View style={[styles.container, style]}>
|
|
80
|
+
<RNCamera style={StyleSheet.absoluteFill} device={device} isActive codeScanner={mode === 'scanCode' ? codeScanner : undefined} torch={torchState} onError={onHandleError} onInitialized={onHandleInitialized}/>
|
|
81
|
+
{props.children}
|
|
82
|
+
</View>);
|
|
83
|
+
};
|
|
84
|
+
const styles = StyleSheet.create({
|
|
85
|
+
container: {
|
|
86
|
+
width: 300,
|
|
87
|
+
height: 300,
|
|
88
|
+
overflow: 'hidden',
|
|
89
|
+
backgroundColor: 'black',
|
|
90
|
+
},
|
|
91
|
+
center: {
|
|
92
|
+
flex: 1,
|
|
93
|
+
justifyContent: 'center',
|
|
94
|
+
alignItems: 'center',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
export interface CalloutItem {
|
|
3
|
+
content?: string;
|
|
4
|
+
color?: string;
|
|
5
|
+
fontSize?: number;
|
|
6
|
+
borderRadius?: number;
|
|
7
|
+
borderWidth?: number;
|
|
8
|
+
borderColor?: string;
|
|
9
|
+
bgColor?: string;
|
|
10
|
+
padding?: number;
|
|
11
|
+
display?: 'BYCLICK' | 'ALWAYS';
|
|
12
|
+
textAlign?: 'left' | 'right' | 'center';
|
|
13
|
+
}
|
|
14
|
+
export interface MarkerItem {
|
|
15
|
+
id: number;
|
|
16
|
+
latitude: number;
|
|
17
|
+
longitude: number;
|
|
18
|
+
title?: string;
|
|
19
|
+
iconPath: string;
|
|
20
|
+
rotate?: number;
|
|
21
|
+
alpha?: number;
|
|
22
|
+
callout?: CalloutItem;
|
|
23
|
+
anchor?: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface PolylineItem {
|
|
29
|
+
points: Array<{
|
|
30
|
+
latitude: number;
|
|
31
|
+
longitude: number;
|
|
32
|
+
}>;
|
|
33
|
+
color?: string;
|
|
34
|
+
width?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface PolygonItem {
|
|
37
|
+
points: Array<{
|
|
38
|
+
latitude: number;
|
|
39
|
+
longitude: number;
|
|
40
|
+
}>;
|
|
41
|
+
strokeWidth?: number;
|
|
42
|
+
strokeColor?: string;
|
|
43
|
+
fillColor?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface CircleItem {
|
|
46
|
+
latitude: number;
|
|
47
|
+
longitude: number;
|
|
48
|
+
color?: string;
|
|
49
|
+
fillColor?: string;
|
|
50
|
+
radius: number;
|
|
51
|
+
strokeWidth?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface Coordinate {
|
|
54
|
+
latitude: number;
|
|
55
|
+
longitude: number;
|
|
56
|
+
}
|
|
57
|
+
export interface MapProps {
|
|
58
|
+
longitude: number;
|
|
59
|
+
latitude: number;
|
|
60
|
+
scale?: number;
|
|
61
|
+
markers?: Array<MarkerItem>;
|
|
62
|
+
polyline?: Array<PolylineItem>;
|
|
63
|
+
polygons?: Array<PolygonItem>;
|
|
64
|
+
circles?: Array<CircleItem>;
|
|
65
|
+
includePoints?: Array<Coordinate>;
|
|
66
|
+
showLocation?: boolean;
|
|
67
|
+
subkey?: string;
|
|
68
|
+
enable3D?: boolean;
|
|
69
|
+
showCompass?: boolean;
|
|
70
|
+
enableOverlooking?: boolean;
|
|
71
|
+
enableZoom?: boolean;
|
|
72
|
+
enableScroll?: boolean;
|
|
73
|
+
enableRotate?: boolean;
|
|
74
|
+
onMarkerClick?: (markerId: number) => void;
|
|
75
|
+
onCalloutClick?: (markerId: number) => void;
|
|
76
|
+
onControlClick?: (controlId?: number) => void;
|
|
77
|
+
onRegionChange?: (event: {
|
|
78
|
+
type: 'begin' | 'end';
|
|
79
|
+
timeStamp: number;
|
|
80
|
+
causedBy?: 'scale' | 'drag' | 'update';
|
|
81
|
+
}) => void;
|
|
82
|
+
onClick?: (coordinate: Coordinate) => void;
|
|
83
|
+
onUpdated?: () => void;
|
|
84
|
+
onPoiClick?: (event: any) => void;
|
|
85
|
+
}
|
|
86
|
+
export declare const Map: FC<MapProps>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { Dimensions, StyleSheet, Text, View } from 'react-native';
|
|
3
|
+
import MapView, { Callout, Circle, Marker, Polygon, Polyline, } from 'react-native-maps';
|
|
4
|
+
const { width, height } = Dimensions.get('window');
|
|
5
|
+
const ASPECT_RATIO = width / height;
|
|
6
|
+
const LATITUDE_DELTA = 0.0922;
|
|
7
|
+
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
|
|
8
|
+
export const Map = props => {
|
|
9
|
+
const { latitude = 0, longitude = 0, markers = [], polyline = [], polygons = [], circles = [], showLocation = false, showCompass = true, enableZoom = true, enableScroll = true, enableRotate = true, onMarkerClick, onCalloutClick, onRegionChange, onClick, onUpdated = () => { }, onPoiClick = () => { }, } = props;
|
|
10
|
+
const onRenderCallout = useCallback((marker) => {
|
|
11
|
+
const { id, callout } = marker;
|
|
12
|
+
if (!callout)
|
|
13
|
+
return null;
|
|
14
|
+
return (<Callout onPress={() => onCalloutClick?.(id)}>
|
|
15
|
+
<View style={[
|
|
16
|
+
styles.calloutContainer,
|
|
17
|
+
{
|
|
18
|
+
borderRadius: callout.borderRadius,
|
|
19
|
+
borderWidth: callout.borderWidth,
|
|
20
|
+
borderColor: callout.borderColor,
|
|
21
|
+
backgroundColor: callout.bgColor,
|
|
22
|
+
padding: callout.padding,
|
|
23
|
+
},
|
|
24
|
+
]}>
|
|
25
|
+
<Text style={{
|
|
26
|
+
fontSize: callout.fontSize,
|
|
27
|
+
color: callout.color,
|
|
28
|
+
textAlign: callout.textAlign,
|
|
29
|
+
}}>
|
|
30
|
+
{callout.content}
|
|
31
|
+
</Text>
|
|
32
|
+
</View>
|
|
33
|
+
</Callout>);
|
|
34
|
+
}, [onCalloutClick]);
|
|
35
|
+
const onHandleRegionChange = useCallback((region, details) => {
|
|
36
|
+
onRegionChange?.({
|
|
37
|
+
type: 'begin',
|
|
38
|
+
timeStamp: Date.now(),
|
|
39
|
+
causedBy: details.isGesture ? 'drag' : 'update',
|
|
40
|
+
});
|
|
41
|
+
}, [onRegionChange]);
|
|
42
|
+
const onHandleRegionChangeComplete = useCallback((region, details) => {
|
|
43
|
+
onRegionChange?.({
|
|
44
|
+
type: 'end',
|
|
45
|
+
timeStamp: Date.now(),
|
|
46
|
+
causedBy: details.isGesture ? 'drag' : 'update',
|
|
47
|
+
});
|
|
48
|
+
}, [onRegionChange]);
|
|
49
|
+
const onHandleMapPress = useCallback((e) => {
|
|
50
|
+
onClick?.(e.nativeEvent.coordinate);
|
|
51
|
+
}, [onClick]);
|
|
52
|
+
const onHandleMarkerPress = useCallback((id) => {
|
|
53
|
+
onMarkerClick?.(id);
|
|
54
|
+
}, [onMarkerClick]);
|
|
55
|
+
return (<MapView style={styles.map} region={{
|
|
56
|
+
latitude,
|
|
57
|
+
longitude,
|
|
58
|
+
latitudeDelta: LATITUDE_DELTA,
|
|
59
|
+
longitudeDelta: LONGITUDE_DELTA,
|
|
60
|
+
}} minZoomLevel={5} maxZoomLevel={18} showsUserLocation={showLocation} showsCompass={showCompass} zoomEnabled={enableZoom} scrollEnabled={enableScroll} rotateEnabled={enableRotate} onRegionChange={onHandleRegionChange} onRegionChangeComplete={onHandleRegionChangeComplete} onPress={onHandleMapPress} onMapReady={onUpdated} onPoiClick={onPoiClick}>
|
|
61
|
+
{markers.map(marker => (<Marker id={`marker-${marker.id}`} coordinate={{
|
|
62
|
+
latitude: marker.latitude,
|
|
63
|
+
longitude: marker.longitude,
|
|
64
|
+
}} title={marker.title} image={marker.iconPath ? { uri: marker.iconPath } : undefined} rotation={marker.rotate} opacity={marker.alpha} anchor={marker.anchor} onPress={() => onHandleMarkerPress(marker.id)}>
|
|
65
|
+
{onRenderCallout(marker)}
|
|
66
|
+
</Marker>))}
|
|
67
|
+
|
|
68
|
+
{polyline.map((p, index) => (<Polyline id={`polyline-${index}`} coordinates={p.points} strokeColor={p.color} strokeWidth={p.width}/>))}
|
|
69
|
+
|
|
70
|
+
{polygons.map((p, index) => (<Polygon id={`polygon-${index}`} coordinates={p.points} strokeColor={p.strokeColor} strokeWidth={p.strokeWidth} fillColor={p.fillColor}/>))}
|
|
71
|
+
|
|
72
|
+
{circles.map((c, index) => (<Circle id={`circle-${index}`} center={{ latitude: c.latitude, longitude: c.longitude }} strokeColor={c.color} fillColor={c.fillColor} radius={c.radius} strokeWidth={c.strokeWidth}/>))}
|
|
73
|
+
</MapView>);
|
|
74
|
+
};
|
|
75
|
+
const styles = StyleSheet.create({
|
|
76
|
+
map: {
|
|
77
|
+
flex: 1,
|
|
78
|
+
},
|
|
79
|
+
calloutContainer: {
|
|
80
|
+
backgroundColor: 'white',
|
|
81
|
+
minWidth: 100,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export interface ProgressActiveEvent {
|
|
4
|
+
percent: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ProgressProps {
|
|
7
|
+
style?: StyleProp<ViewStyle>;
|
|
8
|
+
percent: number;
|
|
9
|
+
showInfo?: boolean;
|
|
10
|
+
borderRadius?: number | string;
|
|
11
|
+
fontSize?: number | string;
|
|
12
|
+
strokeWidth?: number | string;
|
|
13
|
+
activeColor?: string;
|
|
14
|
+
backgroundColor?: string;
|
|
15
|
+
active?: boolean;
|
|
16
|
+
activeMode?: 'backwards' | 'forwards';
|
|
17
|
+
onActiveEnd?: (event: ProgressActiveEvent) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const Progress: FC<ProgressProps>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, Easing, Text, View, StyleSheet, } from 'react-native';
|
|
3
|
+
export const Progress = ({ style, percent = 0, showInfo, borderRadius = 0, strokeWidth = 6, activeColor = '#09BB07', backgroundColor = '#EBEBEB', active, activeMode = 'backwards', onActiveEnd, }) => {
|
|
4
|
+
const valve = useRef(new Animated.Value(0)).current;
|
|
5
|
+
const prevPercentRef = useRef(0);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const toValue = percent / 100;
|
|
8
|
+
if (!active || (activeMode !== 'backwards' && activeMode !== 'forwards')) {
|
|
9
|
+
Animated.timing(valve, {
|
|
10
|
+
toValue,
|
|
11
|
+
duration: 0,
|
|
12
|
+
useNativeDriver: false,
|
|
13
|
+
}).start();
|
|
14
|
+
prevPercentRef.current = percent;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const sequence = [];
|
|
18
|
+
const duration = ((activeMode === 'forwards'
|
|
19
|
+
? Math.abs(percent - prevPercentRef.current)
|
|
20
|
+
: percent) /
|
|
21
|
+
100) *
|
|
22
|
+
1000;
|
|
23
|
+
if (activeMode === 'backwards') {
|
|
24
|
+
sequence.push(Animated.timing(valve, {
|
|
25
|
+
toValue: 0,
|
|
26
|
+
duration: 0,
|
|
27
|
+
useNativeDriver: false,
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
sequence.push(Animated.timing(valve, {
|
|
31
|
+
toValue,
|
|
32
|
+
easing: Easing.linear,
|
|
33
|
+
duration,
|
|
34
|
+
useNativeDriver: false,
|
|
35
|
+
}));
|
|
36
|
+
const onCompleteAnimation = () => {
|
|
37
|
+
if (onActiveEnd) {
|
|
38
|
+
onActiveEnd({ percent });
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
Animated.sequence(sequence).start(onCompleteAnimation);
|
|
42
|
+
prevPercentRef.current = percent;
|
|
43
|
+
}, [active, activeMode, percent, valve, onActiveEnd]);
|
|
44
|
+
const width = valve.interpolate({
|
|
45
|
+
inputRange: [0, 1],
|
|
46
|
+
outputRange: ['0%', '100%'],
|
|
47
|
+
});
|
|
48
|
+
return (<View style={[styles.wrapper, style]}>
|
|
49
|
+
<View style={[
|
|
50
|
+
styles.bar,
|
|
51
|
+
{
|
|
52
|
+
height: strokeWidth,
|
|
53
|
+
backgroundColor,
|
|
54
|
+
},
|
|
55
|
+
]}>
|
|
56
|
+
<Animated.View style={[
|
|
57
|
+
styles.barThumb,
|
|
58
|
+
{
|
|
59
|
+
width,
|
|
60
|
+
height: '100%',
|
|
61
|
+
backgroundColor: activeColor,
|
|
62
|
+
borderBottomRightRadius: Number(borderRadius),
|
|
63
|
+
borderTopRightRadius: Number(borderRadius),
|
|
64
|
+
},
|
|
65
|
+
]}/>
|
|
66
|
+
</View>
|
|
67
|
+
{showInfo && <Text style={styles.info}>{percent}%</Text>}
|
|
68
|
+
</View>);
|
|
69
|
+
};
|
|
70
|
+
const styles = StyleSheet.create({
|
|
71
|
+
wrapper: {
|
|
72
|
+
flexDirection: 'row',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
},
|
|
75
|
+
bar: {
|
|
76
|
+
flexGrow: 1,
|
|
77
|
+
},
|
|
78
|
+
barThumb: {},
|
|
79
|
+
info: {
|
|
80
|
+
marginLeft: 15,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { useState, useRef, useCallback } from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { WebView } from 'react-native-webview';
|
|
4
|
+
export const RichText = props => {
|
|
5
|
+
const { style, html = '' } = props;
|
|
6
|
+
const [webViewHeight, setWebViewHeight] = useState(0);
|
|
7
|
+
const webviewRef = useRef(null);
|
|
8
|
+
const onMessage = useCallback((event) => {
|
|
9
|
+
const height = Number(event.nativeEvent.data);
|
|
10
|
+
if (height > 0) {
|
|
11
|
+
setWebViewHeight(height);
|
|
12
|
+
}
|
|
13
|
+
}, []);
|
|
14
|
+
const onLoadEnd = useCallback(() => {
|
|
15
|
+
webviewRef.current?.injectJavaScript('window.ReactNativeWebView.postMessage(document.body.scrollHeight);');
|
|
16
|
+
}, []);
|
|
17
|
+
const injectedJavaScript = `
|
|
18
|
+
(function() {
|
|
19
|
+
document.documentElement.style.padding = '0';
|
|
20
|
+
document.documentElement.style.margin = '0';
|
|
21
|
+
document.body.style.padding = '0';
|
|
22
|
+
document.body.style.margin = '0';
|
|
23
|
+
window.ReactNativeWebView.postMessage(document.body.scrollHeight);
|
|
24
|
+
})();
|
|
25
|
+
true;
|
|
26
|
+
`;
|
|
27
|
+
return (<View style={[
|
|
28
|
+
{
|
|
29
|
+
height: webViewHeight,
|
|
30
|
+
width: '100%',
|
|
31
|
+
},
|
|
32
|
+
style,
|
|
33
|
+
]}>
|
|
34
|
+
<WebView ref={webviewRef} source={{
|
|
35
|
+
html: `
|
|
36
|
+
<!DOCTYPE html>
|
|
37
|
+
<html>
|
|
38
|
+
<head>
|
|
39
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
|
40
|
+
<style>
|
|
41
|
+
body { font-family: -apple-system, system-ui; overflow: hidden; }
|
|
42
|
+
</style>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
${html}
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|
|
48
|
+
`,
|
|
49
|
+
}} scrollEnabled={false} scalesPageToFit={false} onMessage={onMessage} injectedJavaScript={injectedJavaScript} onLoadEnd={onLoadEnd} style={styles.webview}/>
|
|
50
|
+
</View>);
|
|
51
|
+
};
|
|
52
|
+
const styles = StyleSheet.create({
|
|
53
|
+
webview: {
|
|
54
|
+
backgroundColor: 'transparent',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export interface SliderChangeEvent {
|
|
4
|
+
value: number;
|
|
5
|
+
}
|
|
6
|
+
export interface SliderProps {
|
|
7
|
+
name?: string;
|
|
8
|
+
style?: StyleProp<ViewStyle>;
|
|
9
|
+
min?: number;
|
|
10
|
+
max?: number;
|
|
11
|
+
step?: number;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
value?: number;
|
|
14
|
+
defaultValue?: number;
|
|
15
|
+
activeColor?: string;
|
|
16
|
+
backgroundColor?: string;
|
|
17
|
+
blockColor?: string;
|
|
18
|
+
showValue?: boolean;
|
|
19
|
+
onChange?: (event: SliderChangeEvent) => void;
|
|
20
|
+
onChanging?: (event: SliderChangeEvent) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare const Slider: FC<SliderProps>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { Text, View, StyleSheet } from 'react-native';
|
|
3
|
+
import RNSlider from '@react-native-community/slider';
|
|
4
|
+
export const Slider = props => {
|
|
5
|
+
const { style, min = 0, max = 100, step = 1, disabled, activeColor = '#1aad19', backgroundColor = '#e9e9e9', blockColor = '#fff', showValue, onChange, onChanging, value, defaultValue, } = props;
|
|
6
|
+
const isControlled = value !== undefined;
|
|
7
|
+
const [currentValue, setCurrentValue] = useState(defaultValue ?? (value || 0));
|
|
8
|
+
const timerRef = useRef(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (value !== undefined) {
|
|
11
|
+
setCurrentValue(value || 0);
|
|
12
|
+
}
|
|
13
|
+
}, [value]);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!isControlled)
|
|
16
|
+
return;
|
|
17
|
+
if (timerRef.current) {
|
|
18
|
+
clearTimeout(timerRef.current);
|
|
19
|
+
}
|
|
20
|
+
timerRef.current = setTimeout(() => {
|
|
21
|
+
if (value !== currentValue) {
|
|
22
|
+
setCurrentValue(value || 0);
|
|
23
|
+
}
|
|
24
|
+
}, 50);
|
|
25
|
+
return () => {
|
|
26
|
+
if (timerRef.current) {
|
|
27
|
+
clearTimeout(timerRef.current);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}, [currentValue, isControlled, value]);
|
|
31
|
+
const onSlidingCompleteHandler = (nextValue) => {
|
|
32
|
+
if (onChange) {
|
|
33
|
+
onChange({ value: nextValue });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const onValueChangeHandler = (nextValue) => {
|
|
37
|
+
setCurrentValue(nextValue);
|
|
38
|
+
if (onChanging) {
|
|
39
|
+
onChanging({ value: nextValue });
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
return (<View style={styles.wrapper}>
|
|
43
|
+
<RNSlider minimumValue={min} maximumValue={max} step={step} disabled={!!disabled} value={currentValue} minimumTrackTintColor={activeColor} maximumTrackTintColor={backgroundColor} thumbTintColor={blockColor} onSlidingComplete={onSlidingCompleteHandler} onValueChange={onValueChangeHandler} style={[styles.bar, style]}/>
|
|
44
|
+
{showValue && <Text style={styles.info}>{currentValue}</Text>}
|
|
45
|
+
</View>);
|
|
46
|
+
};
|
|
47
|
+
const styles = StyleSheet.create({
|
|
48
|
+
wrapper: {
|
|
49
|
+
flexDirection: 'row',
|
|
50
|
+
alignItems: 'center',
|
|
51
|
+
},
|
|
52
|
+
bar: {
|
|
53
|
+
flexGrow: 1,
|
|
54
|
+
},
|
|
55
|
+
info: {
|
|
56
|
+
marginLeft: 15,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FC, PropsWithChildren, ReactNode } from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import { PaginationProps } from './pagination';
|
|
4
|
+
export interface CarouselProps {
|
|
5
|
+
infinite?: boolean;
|
|
6
|
+
dots?: boolean;
|
|
7
|
+
autoplay?: boolean;
|
|
8
|
+
autoplayInterval?: number;
|
|
9
|
+
selectedIndex?: number;
|
|
10
|
+
vertical?: boolean;
|
|
11
|
+
pagination?: (props: PaginationProps) => ReactNode;
|
|
12
|
+
dotStyle?: StyleProp<ViewStyle>;
|
|
13
|
+
dotActiveStyle?: StyleProp<ViewStyle>;
|
|
14
|
+
style?: StyleProp<ViewStyle>;
|
|
15
|
+
afterChange?: (index: number) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const Carousel: FC<PropsWithChildren<CarouselProps>>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { Children, useMemo, useRef, useState, } from 'react';
|
|
2
|
+
import { StyleSheet, Text, View, } from 'react-native';
|
|
3
|
+
import CarouselView from 'react-native-reanimated-carousel';
|
|
4
|
+
import { DefaultPagination } from './pagination';
|
|
5
|
+
export const Carousel = props => {
|
|
6
|
+
const { infinite = false, dots = true, autoplay = false, autoplayInterval = 3000, selectedIndex = 0, vertical = false, pagination: Pagination = DefaultPagination, dotStyle = {}, dotActiveStyle = {}, children, style, afterChange, } = props;
|
|
7
|
+
const carouselRef = useRef(null);
|
|
8
|
+
const [layout, setLayout] = useState({ width: 0, height: 0 });
|
|
9
|
+
const data = useMemo(() => Children.toArray(children), [children]);
|
|
10
|
+
const count = data.length;
|
|
11
|
+
const onLayout = (event) => {
|
|
12
|
+
const { width: w, height: h } = event.nativeEvent.layout;
|
|
13
|
+
if (w > 0 && h > 0) {
|
|
14
|
+
setLayout({ width: w, height: h });
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const onSnapToItem = (index) => {
|
|
18
|
+
if (afterChange) {
|
|
19
|
+
afterChange(index);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const width = layout.width;
|
|
23
|
+
const height = layout.height || 200;
|
|
24
|
+
if (!children || count === 0) {
|
|
25
|
+
return (<Text style={{ backgroundColor: 'white' }}>
|
|
26
|
+
You are supposed to add children inside Carousel
|
|
27
|
+
</Text>);
|
|
28
|
+
}
|
|
29
|
+
return (<View onLayout={onLayout} style={[styles.wrapperStyle, style]}>
|
|
30
|
+
{width && (<CarouselView ref={carouselRef} loop={infinite} width={width} height={height} vertical={vertical} autoPlay={autoplay} autoPlayInterval={autoplayInterval} data={data} defaultIndex={selectedIndex} onSnapToItem={onSnapToItem} renderItem={({ item }) => <View style={{ flex: 1 }}>{item}</View>}/>)}
|
|
31
|
+
|
|
32
|
+
{dots && (<Pagination styles={styles} vertical={vertical} current={selectedIndex} count={count} dotStyle={dotStyle} dotActiveStyle={dotActiveStyle}/>)}
|
|
33
|
+
</View>);
|
|
34
|
+
};
|
|
35
|
+
const styles = StyleSheet.create({
|
|
36
|
+
wrapperStyle: {
|
|
37
|
+
overflow: 'hidden',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FC, PropsWithChildren } from 'react';
|
|
2
|
+
import { ViewStyle, StyleProp } from 'react-native';
|
|
3
|
+
export interface SwiperEvent {
|
|
4
|
+
current: number;
|
|
5
|
+
}
|
|
6
|
+
export interface SwiperProps {
|
|
7
|
+
style?: StyleProp<ViewStyle>;
|
|
8
|
+
indicatorDots?: boolean;
|
|
9
|
+
indicatorColor?: string;
|
|
10
|
+
indicatorActiveColor?: string;
|
|
11
|
+
autoplay?: boolean;
|
|
12
|
+
current?: number;
|
|
13
|
+
interval?: number;
|
|
14
|
+
circular?: boolean;
|
|
15
|
+
vertical?: boolean;
|
|
16
|
+
onChange?: (event: SwiperEvent) => void;
|
|
17
|
+
onAnimationFinish?: (event: SwiperEvent) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const Swiper: FC<PropsWithChildren<SwiperProps>>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Carousel } from './carousel';
|
|
2
|
+
export const Swiper = props => {
|
|
3
|
+
const { children, style, indicatorDots, indicatorColor = 'rgba(0,0,0,0.3)', indicatorActiveColor = '#000', autoplay, current = 0, interval = 5000, circular, vertical, onChange, onAnimationFinish, } = props;
|
|
4
|
+
const onAfterChangeHandler = (index) => {
|
|
5
|
+
if (onChange) {
|
|
6
|
+
onChange({ current: index });
|
|
7
|
+
}
|
|
8
|
+
if (onAnimationFinish) {
|
|
9
|
+
onAnimationFinish({ current: index });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
return (<Carousel style={style} dots={indicatorDots} dotStyle={{ backgroundColor: indicatorColor }} dotActiveStyle={{ backgroundColor: indicatorActiveColor }} autoplay={autoplay} selectedIndex={current} autoplayInterval={interval} infinite={circular} vertical={vertical} afterChange={onAfterChangeHandler}>
|
|
13
|
+
{children}
|
|
14
|
+
</Carousel>);
|
|
15
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
export interface PaginationProps {
|
|
4
|
+
vertical?: boolean;
|
|
5
|
+
current: number;
|
|
6
|
+
count: number;
|
|
7
|
+
styles: any;
|
|
8
|
+
dotStyle?: StyleProp<ViewStyle>;
|
|
9
|
+
dotActiveStyle?: StyleProp<ViewStyle>;
|
|
10
|
+
}
|
|
11
|
+
export declare const DefaultPagination: React.FC<PaginationProps>;
|