@solucx/react-native-solucx-widget 0.1.3 → 0.1.5
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/README.intern.md +1 -4
- package/README.md +3 -6
- package/package.json +4 -3
- package/src/SoluCXWidget.tsx +3 -4
- package/src/components/InlineWidget.tsx +13 -6
- package/src/components/ModalWidget.tsx +14 -6
- package/src/components/OverlayWidget.tsx +26 -17
- package/src/hooks/useWidgetState.ts +7 -1
- package/src/interfaces/WidgetOptions.ts +0 -1
- package/src/styles/widgetStyles.ts +25 -0
package/README.intern.md
CHANGED
|
@@ -85,7 +85,6 @@ export default function MyComponent() {
|
|
|
85
85
|
param_REGIAO: "SUDESTE"
|
|
86
86
|
}}
|
|
87
87
|
options={{
|
|
88
|
-
width: 380,
|
|
89
88
|
height: 400
|
|
90
89
|
}}
|
|
91
90
|
/>
|
|
@@ -145,8 +144,7 @@ interface WidgetData {
|
|
|
145
144
|
|
|
146
145
|
```typescript
|
|
147
146
|
interface WidgetOptions {
|
|
148
|
-
|
|
149
|
-
height?: number; // Altura do widget (padrão: 400)
|
|
147
|
+
height?: number; // Altura
|
|
150
148
|
retry?: { // Configuração de retry
|
|
151
149
|
attempts?: number; // Número de tentativas
|
|
152
150
|
interval?: number; // Intervalo entre tentativas
|
|
@@ -277,7 +275,6 @@ Configurações centralizadas em [`webViewConstants.ts`](src/constants/webViewCo
|
|
|
277
275
|
```typescript
|
|
278
276
|
export const BASE_URL = 'https://survey-link.solucx.com.br/link';
|
|
279
277
|
export const STORAGE_KEY = '@solucxWidgetLog';
|
|
280
|
-
export const DEFAULT_WIDTH = 380;
|
|
281
278
|
export const MIN_HEIGHT = 200;
|
|
282
279
|
export const FIXED_Z_INDEX = 9999;
|
|
283
280
|
export const MODAL_Z_INDEX = 10000;
|
package/README.md
CHANGED
|
@@ -54,8 +54,7 @@ export default function MyScreen() {
|
|
|
54
54
|
store_id: "loja_01",
|
|
55
55
|
amount: 150.00
|
|
56
56
|
}}
|
|
57
|
-
options={{
|
|
58
|
-
width: 380,
|
|
57
|
+
options={{
|
|
59
58
|
height: 400
|
|
60
59
|
}}
|
|
61
60
|
/>
|
|
@@ -138,8 +137,7 @@ interface WidgetData {
|
|
|
138
137
|
|
|
139
138
|
```typescript
|
|
140
139
|
interface WidgetOptions {
|
|
141
|
-
|
|
142
|
-
height?: number; // Altura (padrão: 400)
|
|
140
|
+
height?: number; // Altura
|
|
143
141
|
retry?: {
|
|
144
142
|
attempts?: number; // Tentativas (padrão: 3)
|
|
145
143
|
interval?: number; // Intervalo em ms (padrão: 1000)
|
|
@@ -239,10 +237,9 @@ const handleMessage = (message: string) => {
|
|
|
239
237
|
|
|
240
238
|
```typescript
|
|
241
239
|
// Ajustar dimensões para o dispositivo:
|
|
242
|
-
const {
|
|
240
|
+
const { height } = Dimensions.get('window');
|
|
243
241
|
|
|
244
242
|
const options = {
|
|
245
|
-
width: width * 0.9,
|
|
246
243
|
height: Math.min(height * 0.6, 400)
|
|
247
244
|
};
|
|
248
245
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solucx/react-native-solucx-widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "The React Native SDK for Solucx Widget",
|
|
5
5
|
"main": "src/index",
|
|
6
6
|
"author": " <> ()",
|
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
15
|
-
"react-native-webview": "^13.16.0"
|
|
15
|
+
"react-native-webview": "^13.16.0",
|
|
16
|
+
"react-native-reanimated": "~3.17.4"
|
|
16
17
|
},
|
|
17
18
|
"peerDependencies": {
|
|
18
19
|
"react": ">=18.0.0",
|
|
19
20
|
"react-native": ">=0.72.0"
|
|
20
21
|
}
|
|
21
|
-
}
|
|
22
|
+
}
|
package/src/SoluCXWidget.tsx
CHANGED
|
@@ -36,13 +36,12 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({
|
|
|
36
36
|
open,
|
|
37
37
|
close,
|
|
38
38
|
userId,
|
|
39
|
-
} = useWidgetState(data, type);
|
|
39
|
+
} = useWidgetState(data, options, type);
|
|
40
40
|
|
|
41
41
|
const eventService = new WidgetEventService(setIsWidgetVisible, resize, open, userId, options);
|
|
42
42
|
|
|
43
43
|
const uri = buildWidgetURL(soluCXKey, data);
|
|
44
44
|
const isForm = Boolean(data.form_id);
|
|
45
|
-
const height = options.height ? Number(options.height) : undefined;
|
|
46
45
|
|
|
47
46
|
useEffect(() => {
|
|
48
47
|
loadSavedData();
|
|
@@ -77,7 +76,7 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({
|
|
|
77
76
|
|
|
78
77
|
if (type === 'modal') {
|
|
79
78
|
return (
|
|
80
|
-
<ModalWidget visible={isWidgetVisible} onClose={handleClose}>
|
|
79
|
+
<ModalWidget visible={isWidgetVisible} height={widgetHeight} onClose={handleClose}>
|
|
81
80
|
<WebView
|
|
82
81
|
ref={webviewRef}
|
|
83
82
|
style={webViewStyle}
|
|
@@ -91,7 +90,7 @@ export const SoluCXWidget: React.FC<SoluCXWidgetProps> = ({
|
|
|
91
90
|
|
|
92
91
|
if (type === 'inline') {
|
|
93
92
|
return (
|
|
94
|
-
<InlineWidget visible={isWidgetVisible} onClose={handleClose}>
|
|
93
|
+
<InlineWidget visible={isWidgetVisible} height={widgetHeight} onClose={handleClose}>
|
|
95
94
|
<WebView
|
|
96
95
|
ref={webviewRef}
|
|
97
96
|
style={webViewStyle}
|
|
@@ -1,25 +1,32 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
|
-
import { styles, getWidgetVisibility } from '../styles/widgetStyles';
|
|
3
|
+
import { styles, getWidgetVisibility, useHeightAnimation } from '../styles/widgetStyles';
|
|
4
4
|
import { CloseButton } from './CloseButton';
|
|
5
|
+
import Animated from 'react-native-reanimated';
|
|
5
6
|
|
|
6
7
|
interface InlineWidgetProps {
|
|
7
8
|
visible: boolean;
|
|
9
|
+
height: number;
|
|
8
10
|
children?: React.ReactNode;
|
|
9
11
|
onClose?: () => void;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
export const InlineWidget: React.FC<InlineWidgetProps> = ({ visible, children, onClose }) => {
|
|
14
|
+
export const InlineWidget: React.FC<InlineWidgetProps> = ({ visible, height, children, onClose }) => {
|
|
15
|
+
const { animatedHeightStyle, updateHeight } = useHeightAnimation(height);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
updateHeight(height);
|
|
19
|
+
}, [height, updateHeight]);
|
|
13
20
|
|
|
14
21
|
return (
|
|
15
|
-
<View style={[styles.
|
|
16
|
-
<View style={[styles.inline, getWidgetVisibility(visible)]}>
|
|
22
|
+
<View style={[styles.inlineWrapper, getWidgetVisibility(visible)]}>
|
|
23
|
+
<Animated.View style={[styles.inline, animatedHeightStyle, getWidgetVisibility(visible)]}>
|
|
17
24
|
{children}
|
|
18
25
|
<CloseButton
|
|
19
26
|
visible={visible}
|
|
20
27
|
onPress={onClose || (() => { })}
|
|
21
28
|
/>
|
|
22
|
-
</View>
|
|
29
|
+
</Animated.View>
|
|
23
30
|
</View>
|
|
24
31
|
);
|
|
25
32
|
};
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { Modal, SafeAreaView, View } from 'react-native';
|
|
3
|
-
import { styles, getWidgetVisibility } from '../styles/widgetStyles';
|
|
3
|
+
import { styles, getWidgetVisibility, useHeightAnimation } from '../styles/widgetStyles';
|
|
4
4
|
import { CloseButton } from './CloseButton';
|
|
5
|
+
import Animated from 'react-native-reanimated';
|
|
5
6
|
|
|
6
7
|
interface ModalWidgetProps {
|
|
7
8
|
visible: boolean;
|
|
9
|
+
height: number;
|
|
8
10
|
children?: React.ReactNode;
|
|
9
11
|
onClose?: () => void;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
export const ModalWidget: React.FC<ModalWidgetProps> = ({ visible, children, onClose }) => {
|
|
14
|
+
export const ModalWidget: React.FC<ModalWidgetProps> = ({ visible, height, children, onClose }) => {
|
|
13
15
|
const [isWidgetVisible, setIsWidgetVisible] = useState<boolean>(true);
|
|
14
16
|
|
|
17
|
+
const { animatedHeightStyle, updateHeight } = useHeightAnimation(height);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
updateHeight(height);
|
|
21
|
+
}, [height, updateHeight]);
|
|
22
|
+
|
|
15
23
|
return (
|
|
16
24
|
<SafeAreaView>
|
|
17
|
-
<Modal transparent visible={isWidgetVisible} animationType="
|
|
25
|
+
<Modal transparent visible={isWidgetVisible} animationType="slide" hardwareAccelerated>
|
|
18
26
|
<View style={[styles.modalOverlay, getWidgetVisibility(visible)]}>
|
|
19
|
-
<View style={[styles.modalContent, getWidgetVisibility(visible)]}>
|
|
27
|
+
<Animated.View style={[styles.modalContent, getWidgetVisibility(visible), animatedHeightStyle]}>
|
|
20
28
|
{children}
|
|
21
29
|
<CloseButton
|
|
22
30
|
visible={visible}
|
|
@@ -27,7 +35,7 @@ export const ModalWidget: React.FC<ModalWidgetProps> = ({ visible, children, onC
|
|
|
27
35
|
}
|
|
28
36
|
}}
|
|
29
37
|
/>
|
|
30
|
-
</View>
|
|
38
|
+
</Animated.View>
|
|
31
39
|
</View>
|
|
32
40
|
</Modal>
|
|
33
41
|
</SafeAreaView>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { View, ViewStyle
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { View, ViewStyle } from 'react-native';
|
|
3
3
|
import { initialWindowMetrics } from 'react-native-safe-area-context';
|
|
4
|
-
import { getWidgetStyles, getWidgetVisibility } from '../styles/widgetStyles';
|
|
4
|
+
import { getWidgetStyles, getWidgetVisibility, useHeightAnimation } from '../styles/widgetStyles';
|
|
5
5
|
import { FIXED_Z_INDEX } from '../constants/webViewConstants';
|
|
6
6
|
import { CloseButton } from './CloseButton';
|
|
7
|
+
import Animated from 'react-native-reanimated';
|
|
7
8
|
|
|
8
9
|
interface OverlayWidgetProps {
|
|
9
10
|
visible: boolean;
|
|
@@ -14,10 +15,23 @@ interface OverlayWidgetProps {
|
|
|
14
15
|
onClose?: () => void;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export const OverlayWidget: React.FC<OverlayWidgetProps> = ({
|
|
18
|
+
export const OverlayWidget: React.FC<OverlayWidgetProps> = ({
|
|
19
|
+
visible,
|
|
20
|
+
width,
|
|
21
|
+
height,
|
|
22
|
+
position,
|
|
23
|
+
children,
|
|
24
|
+
onClose,
|
|
25
|
+
}) => {
|
|
18
26
|
const insets = initialWindowMetrics?.insets ?? { top: 0, bottom: 0, left: 0, right: 0 };
|
|
19
27
|
const [isWidgetVisible, setIsWidgetVisible] = useState<boolean>(true);
|
|
20
28
|
|
|
29
|
+
const { animatedHeightStyle, updateHeight } = useHeightAnimation(height);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
updateHeight(height);
|
|
33
|
+
}, [height, updateHeight]);
|
|
34
|
+
|
|
21
35
|
const containerStyle: ViewStyle = {
|
|
22
36
|
position: 'absolute',
|
|
23
37
|
top: 0,
|
|
@@ -27,14 +41,14 @@ export const OverlayWidget: React.FC<OverlayWidgetProps> = ({ visible, width, he
|
|
|
27
41
|
width: '100%',
|
|
28
42
|
height: '100%',
|
|
29
43
|
zIndex: FIXED_Z_INDEX,
|
|
30
|
-
|
|
44
|
+
pointerEvents: 'box-none'
|
|
31
45
|
}
|
|
32
46
|
|
|
33
47
|
const contentStyle = [
|
|
34
48
|
getWidgetStyles(position).content,
|
|
35
49
|
{
|
|
36
50
|
width,
|
|
37
|
-
|
|
51
|
+
pointerEvents: 'auto' as const,
|
|
38
52
|
...(position === 'top' && {
|
|
39
53
|
top: insets.top,
|
|
40
54
|
}),
|
|
@@ -45,15 +59,10 @@ export const OverlayWidget: React.FC<OverlayWidgetProps> = ({ visible, width, he
|
|
|
45
59
|
];
|
|
46
60
|
|
|
47
61
|
return (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
transparent
|
|
51
|
-
visible={isWidgetVisible}
|
|
52
|
-
animationType="fade"
|
|
53
|
-
hardwareAccelerated
|
|
54
|
-
>
|
|
62
|
+
<>
|
|
63
|
+
{isWidgetVisible && (
|
|
55
64
|
<View style={[containerStyle, getWidgetVisibility(visible)]}>
|
|
56
|
-
<View style={[contentStyle, getWidgetVisibility(visible)]}>
|
|
65
|
+
<Animated.View style={[contentStyle, animatedHeightStyle, getWidgetVisibility(visible)]}>
|
|
57
66
|
{children}
|
|
58
67
|
<CloseButton
|
|
59
68
|
visible={visible}
|
|
@@ -64,9 +73,9 @@ export const OverlayWidget: React.FC<OverlayWidgetProps> = ({ visible, width, he
|
|
|
64
73
|
}
|
|
65
74
|
}}
|
|
66
75
|
/>
|
|
67
|
-
</View>
|
|
76
|
+
</Animated.View>
|
|
68
77
|
</View>
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
)}
|
|
79
|
+
</>
|
|
71
80
|
);
|
|
72
81
|
};
|
|
@@ -2,12 +2,13 @@ import { useState, useCallback } from 'react';
|
|
|
2
2
|
import { Dimensions } from 'react-native';
|
|
3
3
|
import { WidgetSamplerLog, WidgetData, WidgetType } from '../interfaces';
|
|
4
4
|
import { StorageService } from '../services/storage';
|
|
5
|
+
import { WidgetOptions } from '@solucx/react-native-solucx-widget';
|
|
5
6
|
|
|
6
7
|
function getUserId(widgetData: WidgetData): string {
|
|
7
8
|
return widgetData.customer_id ?? widgetData.document ?? widgetData.email ?? "";
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export const useWidgetState = (data: WidgetData, type?: WidgetType) => {
|
|
11
|
+
export const useWidgetState = (data: WidgetData, options?: WidgetOptions, type?: WidgetType) => {
|
|
11
12
|
const [savedData, setSavedData] = useState<WidgetSamplerLog | null>(null);
|
|
12
13
|
const [widgetHeight, setWidgetHeight] = useState<number>(0);
|
|
13
14
|
const [isWidgetVisible, setIsWidgetVisible] = useState<boolean>(false);
|
|
@@ -15,6 +16,7 @@ export const useWidgetState = (data: WidgetData, type?: WidgetType) => {
|
|
|
15
16
|
const userId = getUserId(data);
|
|
16
17
|
const storageService = new StorageService(userId);
|
|
17
18
|
const screenHeight = Dimensions.get('screen').height;
|
|
19
|
+
const height = options?.height ? Number(options.height) : undefined;
|
|
18
20
|
|
|
19
21
|
const loadSavedData = useCallback(async () => {
|
|
20
22
|
try {
|
|
@@ -53,6 +55,10 @@ export const useWidgetState = (data: WidgetData, type?: WidgetType) => {
|
|
|
53
55
|
|
|
54
56
|
const resize = useCallback((value: string) => {
|
|
55
57
|
const receivedHeight = Number(value);
|
|
58
|
+
if (height && receivedHeight > height) {
|
|
59
|
+
setWidgetHeight(height);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
56
62
|
setWidgetHeight(receivedHeight);
|
|
57
63
|
}, [screenHeight]);
|
|
58
64
|
|
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
2
|
import { WidgetType } from '../interfaces';
|
|
3
|
+
import {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
withTiming,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
Easing,
|
|
8
|
+
} from 'react-native-reanimated';
|
|
9
|
+
|
|
10
|
+
export const useHeightAnimation = (height: number) => {
|
|
11
|
+
const animatedHeight = useSharedValue(height);
|
|
12
|
+
|
|
13
|
+
const animatedHeightStyle = useAnimatedStyle(() => {
|
|
14
|
+
return {
|
|
15
|
+
height: animatedHeight.value,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const updateHeight = (newHeight: number) => {
|
|
20
|
+
animatedHeight.value = withTiming(newHeight, {
|
|
21
|
+
duration: 300,
|
|
22
|
+
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return { animatedHeightStyle, updateHeight };
|
|
27
|
+
};
|
|
3
28
|
|
|
4
29
|
export const styles = StyleSheet.create({
|
|
5
30
|
wrapper: {
|