@solucx/react-native-solucx-widget 0.1.13 → 0.1.15
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 +513 -473
- package/README.md +285 -257
- package/package.json +23 -23
- package/src/SoluCXWidget.tsx +119 -116
- package/src/__tests__/urlUtils.test.ts +56 -56
- package/src/__tests__/useWidgetState.test.ts +188 -188
- package/src/components/CloseButton.tsx +36 -36
- package/src/components/InlineWidget.tsx +36 -36
- package/src/components/ModalWidget.tsx +59 -59
- package/src/components/OverlayWidget.tsx +88 -88
- package/src/constants/webViewConstants.ts +14 -14
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useHeightAnimation.ts +22 -22
- package/src/hooks/useWidgetHeight.ts +38 -0
- package/src/hooks/useWidgetState.ts +101 -77
- package/src/index.ts +8 -8
- package/src/interfaces/WidgetData.ts +19 -19
- package/src/interfaces/WidgetOptions.ts +7 -7
- package/src/interfaces/WidgetResponse.ts +15 -15
- package/src/interfaces/WidgetSamplerLog.ts +5 -5
- package/src/interfaces/index.ts +23 -23
- package/src/services/storage.ts +20 -20
- package/src/services/widgetEventService.ts +111 -111
- package/src/services/widgetValidationService.ts +86 -86
- package/src/styles/widgetStyles.ts +58 -58
- package/src/utils/urlUtils.ts +13 -13
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
import { WidgetEventService } from '../services/widgetEventService';
|
|
2
|
-
import { WidgetOptions } from '../interfaces';
|
|
3
|
-
|
|
4
|
-
const mockWidgetValidationService = {
|
|
5
|
-
shouldDisplayWidget: jest.fn().mockResolvedValue(true)
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
jest.mock('../services/widgetValidationService', () => ({
|
|
9
|
-
WidgetValidationService: jest.fn().mockImplementation(() => mockWidgetValidationService)
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
describe('WidgetEventService', () => {
|
|
13
|
-
let mockSetIsWidgetVisible: jest.Mock;
|
|
14
|
-
let mockResize: jest.Mock;
|
|
15
|
-
let service: WidgetEventService;
|
|
16
|
-
let open: jest.Mock;
|
|
17
|
-
let mockUserId: string;
|
|
18
|
-
let mockWidgetOptions: WidgetOptions;
|
|
19
|
-
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
jest.clearAllMocks();
|
|
22
|
-
|
|
23
|
-
mockSetIsWidgetVisible = jest.fn();
|
|
24
|
-
mockResize = jest.fn();
|
|
25
|
-
open = jest.fn();
|
|
26
|
-
mockUserId = 'test-user-123';
|
|
27
|
-
mockWidgetOptions = {
|
|
28
|
-
height: 400,
|
|
29
|
-
retry: {
|
|
30
|
-
attempts: 5,
|
|
31
|
-
interval: 1
|
|
32
|
-
},
|
|
33
|
-
waitDelayAfterRating: 60
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
mockWidgetValidationService.shouldDisplayWidget.mockResolvedValue(true);
|
|
37
|
-
|
|
38
|
-
service = new WidgetEventService(
|
|
39
|
-
mockSetIsWidgetVisible,
|
|
40
|
-
mockResize,
|
|
41
|
-
open,
|
|
42
|
-
mockUserId,
|
|
43
|
-
mockWidgetOptions
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
jest.clearAllMocks();
|
|
49
|
-
jest.restoreAllMocks();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should handle FORM_OPENED event correctly', async () => {
|
|
53
|
-
const result = await service.handleMessage('FORM_OPENED', true);
|
|
54
|
-
|
|
55
|
-
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
56
|
-
expect(open).toHaveBeenCalled();
|
|
57
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
58
|
-
expect(result).toEqual({ status: 'success' });
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should prevent widget from opening when validation fails', async () => {
|
|
62
|
-
mockWidgetValidationService.shouldDisplayWidget.mockResolvedValueOnce(await Promise.resolve(false));
|
|
63
|
-
|
|
64
|
-
const result = await service.handleMessage('FORM_OPENED', true);
|
|
65
|
-
|
|
66
|
-
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
67
|
-
expect(open).not.toHaveBeenCalled();
|
|
68
|
-
expect(mockSetIsWidgetVisible).not.toHaveBeenCalled();
|
|
69
|
-
expect(result).toEqual({ status: 'error', message: 'Widget not allowed' });
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should handle FORM_CLOSE event correctly', async () => {
|
|
73
|
-
const result = await service.handleMessage('FORM_CLOSE', true);
|
|
74
|
-
|
|
75
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
76
|
-
expect(result).toEqual({ status: 'success' });
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should handle FORM_RESIZE event correctly', async () => {
|
|
80
|
-
const result = await service.handleMessage('FORM_RESIZE-350', true);
|
|
81
|
-
|
|
82
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
83
|
-
expect(mockResize).toHaveBeenCalledWith('350');
|
|
84
|
-
expect(result).toEqual({ status: 'success' });
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should handle FORM_ERROR event correctly', async () => {
|
|
88
|
-
const result = await service.handleMessage('FORM_ERROR-Something went wrong', true);
|
|
89
|
-
|
|
90
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
91
|
-
expect(result).toEqual({ status: 'error', message: 'Something went wrong' });
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('should adapt survey keys to widget keys correctly for non-form widgets', async () => {
|
|
95
|
-
const result = await service.handleMessage('closeSoluCXWidget', false);
|
|
96
|
-
|
|
97
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
98
|
-
expect(result).toEqual({ status: 'success' });
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should return error for unknown events', async () => {
|
|
102
|
-
const result = await service.handleMessage('UNKNOWN_EVENT', true);
|
|
103
|
-
|
|
104
|
-
expect(result).toEqual({ status: 'error', message: 'Unknown event' });
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should handle messages with no value correctly', async () => {
|
|
108
|
-
const result = await service.handleMessage('FORM_OPENED', true);
|
|
109
|
-
|
|
110
|
-
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
111
|
-
expect(open).toHaveBeenCalled();
|
|
112
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
113
|
-
expect(result).toEqual({ status: 'success' });
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should handle FORM_PAGECHANGED event correctly', async () => {
|
|
117
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
118
|
-
const result = await service.handleMessage('FORM_PAGECHANGED-page2', true);
|
|
119
|
-
|
|
120
|
-
expect(consoleSpy).toHaveBeenCalledWith("Page changed:", "page2");
|
|
121
|
-
expect(result).toEqual({ status: 'success' });
|
|
122
|
-
|
|
123
|
-
consoleSpy.mockRestore();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should handle QUESTION_ANSWERED event correctly', async () => {
|
|
127
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
128
|
-
const result = await service.handleMessage('QUESTION_ANSWERED', true);
|
|
129
|
-
expect(consoleSpy).toHaveBeenCalledWith("Question answered");
|
|
130
|
-
expect(result).toEqual({ status: 'success' });
|
|
131
|
-
|
|
132
|
-
consoleSpy.mockRestore();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('should handle FORM_COMPLETED event correctly', async () => {
|
|
136
|
-
const result = await service.handleMessage('FORM_COMPLETED', true);
|
|
137
|
-
|
|
138
|
-
expect(result).toEqual({ status: 'success' });
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should handle FORM_PARTIALCOMPLETED event correctly', async () => {
|
|
142
|
-
const result = await service.handleMessage('FORM_PARTIALCOMPLETED', true);
|
|
143
|
-
|
|
144
|
-
expect(result).toEqual({ status: 'success' });
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should adapt completeSoluCXWidget to FORM_COMPLETED', async () => {
|
|
148
|
-
const result = await service.handleMessage('completeSoluCXWidget', false);
|
|
149
|
-
|
|
150
|
-
expect(result).toEqual({ status: 'success' });
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should adapt partialSoluCXWidget to FORM_PARTIALCOMPLETED', async () => {
|
|
154
|
-
const result = await service.handleMessage('partialSoluCXWidget', false);
|
|
155
|
-
|
|
156
|
-
expect(result).toEqual({ status: 'success' });
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should adapt dismissSoluCXWidget to FORM_CLOSE', async () => {
|
|
160
|
-
const result = await service.handleMessage('dismissSoluCXWidget', false);
|
|
161
|
-
|
|
162
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
163
|
-
expect(result).toEqual({ status: 'success' });
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should adapt openSoluCXWidget to FORM_OPENED', async () => {
|
|
167
|
-
const result = await service.handleMessage('openSoluCXWidget', false);
|
|
168
|
-
|
|
169
|
-
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
170
|
-
expect(open).toHaveBeenCalled();
|
|
171
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
172
|
-
expect(result).toEqual({ status: 'success' });
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('should adapt errorSoluCXWidget to FORM_ERROR', async () => {
|
|
176
|
-
const result = await service.handleMessage('errorSoluCXWidget-Network error', false);
|
|
177
|
-
|
|
178
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
179
|
-
expect(result).toEqual({ status: 'error', message: 'Network error' });
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should adapt resizeSoluCXWidget to FORM_RESIZE', async () => {
|
|
183
|
-
const result = await service.handleMessage('resizeSoluCXWidget-400', false);
|
|
184
|
-
|
|
185
|
-
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
186
|
-
expect(mockResize).toHaveBeenCalledWith('400');
|
|
187
|
-
expect(result).toEqual({ status: 'success' });
|
|
188
|
-
});
|
|
1
|
+
import { WidgetEventService } from '../services/widgetEventService';
|
|
2
|
+
import { WidgetOptions } from '../interfaces';
|
|
3
|
+
|
|
4
|
+
const mockWidgetValidationService = {
|
|
5
|
+
shouldDisplayWidget: jest.fn().mockResolvedValue(true)
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
jest.mock('../services/widgetValidationService', () => ({
|
|
9
|
+
WidgetValidationService: jest.fn().mockImplementation(() => mockWidgetValidationService)
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
describe('WidgetEventService', () => {
|
|
13
|
+
let mockSetIsWidgetVisible: jest.Mock;
|
|
14
|
+
let mockResize: jest.Mock;
|
|
15
|
+
let service: WidgetEventService;
|
|
16
|
+
let open: jest.Mock;
|
|
17
|
+
let mockUserId: string;
|
|
18
|
+
let mockWidgetOptions: WidgetOptions;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
|
|
23
|
+
mockSetIsWidgetVisible = jest.fn();
|
|
24
|
+
mockResize = jest.fn();
|
|
25
|
+
open = jest.fn();
|
|
26
|
+
mockUserId = 'test-user-123';
|
|
27
|
+
mockWidgetOptions = {
|
|
28
|
+
height: 400,
|
|
29
|
+
retry: {
|
|
30
|
+
attempts: 5,
|
|
31
|
+
interval: 1
|
|
32
|
+
},
|
|
33
|
+
waitDelayAfterRating: 60
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
mockWidgetValidationService.shouldDisplayWidget.mockResolvedValue(true);
|
|
37
|
+
|
|
38
|
+
service = new WidgetEventService(
|
|
39
|
+
mockSetIsWidgetVisible,
|
|
40
|
+
mockResize,
|
|
41
|
+
open,
|
|
42
|
+
mockUserId,
|
|
43
|
+
mockWidgetOptions
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
jest.clearAllMocks();
|
|
49
|
+
jest.restoreAllMocks();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should handle FORM_OPENED event correctly', async () => {
|
|
53
|
+
const result = await service.handleMessage('FORM_OPENED', true);
|
|
54
|
+
|
|
55
|
+
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
56
|
+
expect(open).toHaveBeenCalled();
|
|
57
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
58
|
+
expect(result).toEqual({ status: 'success' });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should prevent widget from opening when validation fails', async () => {
|
|
62
|
+
mockWidgetValidationService.shouldDisplayWidget.mockResolvedValueOnce(await Promise.resolve(false));
|
|
63
|
+
|
|
64
|
+
const result = await service.handleMessage('FORM_OPENED', true);
|
|
65
|
+
|
|
66
|
+
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
67
|
+
expect(open).not.toHaveBeenCalled();
|
|
68
|
+
expect(mockSetIsWidgetVisible).not.toHaveBeenCalled();
|
|
69
|
+
expect(result).toEqual({ status: 'error', message: 'Widget not allowed' });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle FORM_CLOSE event correctly', async () => {
|
|
73
|
+
const result = await service.handleMessage('FORM_CLOSE', true);
|
|
74
|
+
|
|
75
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
76
|
+
expect(result).toEqual({ status: 'success' });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle FORM_RESIZE event correctly', async () => {
|
|
80
|
+
const result = await service.handleMessage('FORM_RESIZE-350', true);
|
|
81
|
+
|
|
82
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
83
|
+
expect(mockResize).toHaveBeenCalledWith('350');
|
|
84
|
+
expect(result).toEqual({ status: 'success' });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should handle FORM_ERROR event correctly', async () => {
|
|
88
|
+
const result = await service.handleMessage('FORM_ERROR-Something went wrong', true);
|
|
89
|
+
|
|
90
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
91
|
+
expect(result).toEqual({ status: 'error', message: 'Something went wrong' });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should adapt survey keys to widget keys correctly for non-form widgets', async () => {
|
|
95
|
+
const result = await service.handleMessage('closeSoluCXWidget', false);
|
|
96
|
+
|
|
97
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
98
|
+
expect(result).toEqual({ status: 'success' });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should return error for unknown events', async () => {
|
|
102
|
+
const result = await service.handleMessage('UNKNOWN_EVENT', true);
|
|
103
|
+
|
|
104
|
+
expect(result).toEqual({ status: 'error', message: 'Unknown event' });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should handle messages with no value correctly', async () => {
|
|
108
|
+
const result = await service.handleMessage('FORM_OPENED', true);
|
|
109
|
+
|
|
110
|
+
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
111
|
+
expect(open).toHaveBeenCalled();
|
|
112
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
113
|
+
expect(result).toEqual({ status: 'success' });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should handle FORM_PAGECHANGED event correctly', async () => {
|
|
117
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
118
|
+
const result = await service.handleMessage('FORM_PAGECHANGED-page2', true);
|
|
119
|
+
|
|
120
|
+
expect(consoleSpy).toHaveBeenCalledWith("Page changed:", "page2");
|
|
121
|
+
expect(result).toEqual({ status: 'success' });
|
|
122
|
+
|
|
123
|
+
consoleSpy.mockRestore();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should handle QUESTION_ANSWERED event correctly', async () => {
|
|
127
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
128
|
+
const result = await service.handleMessage('QUESTION_ANSWERED', true);
|
|
129
|
+
expect(consoleSpy).toHaveBeenCalledWith("Question answered");
|
|
130
|
+
expect(result).toEqual({ status: 'success' });
|
|
131
|
+
|
|
132
|
+
consoleSpy.mockRestore();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should handle FORM_COMPLETED event correctly', async () => {
|
|
136
|
+
const result = await service.handleMessage('FORM_COMPLETED', true);
|
|
137
|
+
|
|
138
|
+
expect(result).toEqual({ status: 'success' });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should handle FORM_PARTIALCOMPLETED event correctly', async () => {
|
|
142
|
+
const result = await service.handleMessage('FORM_PARTIALCOMPLETED', true);
|
|
143
|
+
|
|
144
|
+
expect(result).toEqual({ status: 'success' });
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should adapt completeSoluCXWidget to FORM_COMPLETED', async () => {
|
|
148
|
+
const result = await service.handleMessage('completeSoluCXWidget', false);
|
|
149
|
+
|
|
150
|
+
expect(result).toEqual({ status: 'success' });
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should adapt partialSoluCXWidget to FORM_PARTIALCOMPLETED', async () => {
|
|
154
|
+
const result = await service.handleMessage('partialSoluCXWidget', false);
|
|
155
|
+
|
|
156
|
+
expect(result).toEqual({ status: 'success' });
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should adapt dismissSoluCXWidget to FORM_CLOSE', async () => {
|
|
160
|
+
const result = await service.handleMessage('dismissSoluCXWidget', false);
|
|
161
|
+
|
|
162
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
163
|
+
expect(result).toEqual({ status: 'success' });
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should adapt openSoluCXWidget to FORM_OPENED', async () => {
|
|
167
|
+
const result = await service.handleMessage('openSoluCXWidget', false);
|
|
168
|
+
|
|
169
|
+
expect(mockWidgetValidationService.shouldDisplayWidget).toHaveBeenCalledWith(mockWidgetOptions);
|
|
170
|
+
expect(open).toHaveBeenCalled();
|
|
171
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
172
|
+
expect(result).toEqual({ status: 'success' });
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should adapt errorSoluCXWidget to FORM_ERROR', async () => {
|
|
176
|
+
const result = await service.handleMessage('errorSoluCXWidget-Network error', false);
|
|
177
|
+
|
|
178
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(false);
|
|
179
|
+
expect(result).toEqual({ status: 'error', message: 'Network error' });
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should adapt resizeSoluCXWidget to FORM_RESIZE', async () => {
|
|
183
|
+
const result = await service.handleMessage('resizeSoluCXWidget-400', false);
|
|
184
|
+
|
|
185
|
+
expect(mockSetIsWidgetVisible).toHaveBeenCalledWith(true);
|
|
186
|
+
expect(mockResize).toHaveBeenCalledWith('400');
|
|
187
|
+
expect(result).toEqual({ status: 'success' });
|
|
188
|
+
});
|
|
189
189
|
});
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
|
|
3
|
-
|
|
4
|
-
interface CloseButtonProps {
|
|
5
|
-
onPress: () => void;
|
|
6
|
-
visible?: boolean;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const CloseButton: React.FC<CloseButtonProps> = ({ onPress, visible = true }) => {
|
|
10
|
-
if (!visible) return null;
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<TouchableOpacity style={styles.closeButton} onPress={onPress}>
|
|
14
|
-
<Text style={styles.closeButtonText}>✕</Text>
|
|
15
|
-
</TouchableOpacity>
|
|
16
|
-
);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const styles = StyleSheet.create({
|
|
20
|
-
closeButton: {
|
|
21
|
-
position: 'absolute',
|
|
22
|
-
top: 10,
|
|
23
|
-
right: 10,
|
|
24
|
-
width: 30,
|
|
25
|
-
height: 30,
|
|
26
|
-
borderRadius: 15,
|
|
27
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
28
|
-
justifyContent: 'center',
|
|
29
|
-
alignItems: 'center',
|
|
30
|
-
zIndex: 10001,
|
|
31
|
-
},
|
|
32
|
-
closeButtonText: {
|
|
33
|
-
color: 'white',
|
|
34
|
-
fontSize: 16,
|
|
35
|
-
fontWeight: 'bold',
|
|
36
|
-
},
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
interface CloseButtonProps {
|
|
5
|
+
onPress: () => void;
|
|
6
|
+
visible?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const CloseButton: React.FC<CloseButtonProps> = ({ onPress, visible = true }) => {
|
|
10
|
+
if (!visible) return null;
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<TouchableOpacity style={styles.closeButton} onPress={onPress}>
|
|
14
|
+
<Text style={styles.closeButtonText}>✕</Text>
|
|
15
|
+
</TouchableOpacity>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const styles = StyleSheet.create({
|
|
20
|
+
closeButton: {
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
top: 10,
|
|
23
|
+
right: 10,
|
|
24
|
+
width: 30,
|
|
25
|
+
height: 30,
|
|
26
|
+
borderRadius: 15,
|
|
27
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
zIndex: 10001,
|
|
31
|
+
},
|
|
32
|
+
closeButtonText: {
|
|
33
|
+
color: 'white',
|
|
34
|
+
fontSize: 16,
|
|
35
|
+
fontWeight: 'bold',
|
|
36
|
+
},
|
|
37
37
|
});
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
3
|
-
import { styles, getWidgetVisibility } from '../styles/widgetStyles';
|
|
4
|
-
import { CloseButton } from './CloseButton';
|
|
5
|
-
import { Animated } from 'react-native';
|
|
6
|
-
import { useHeightAnimation } from '../hooks/useHeightAnimation';
|
|
7
|
-
|
|
8
|
-
interface InlineWidgetProps {
|
|
9
|
-
visible: boolean;
|
|
10
|
-
height: number;
|
|
11
|
-
children?: React.ReactNode;
|
|
12
|
-
onClose?: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const InlineWidget: React.FC<InlineWidgetProps> = ({
|
|
16
|
-
visible,
|
|
17
|
-
height,
|
|
18
|
-
children,
|
|
19
|
-
onClose,
|
|
20
|
-
}) => {
|
|
21
|
-
const { animatedHeightStyle, updateHeight } = useHeightAnimation(height);
|
|
22
|
-
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
updateHeight(height);
|
|
25
|
-
}, [height, updateHeight]);
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<View style={[styles.inlineWrapper, getWidgetVisibility(visible)]}>
|
|
29
|
-
<Animated.View
|
|
30
|
-
style={[styles.inline, animatedHeightStyle, getWidgetVisibility(visible)]}>
|
|
31
|
-
{children}
|
|
32
|
-
<CloseButton visible={visible} onPress={onClose || (() => { })} />
|
|
33
|
-
</Animated.View>
|
|
34
|
-
</View>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { styles, getWidgetVisibility } from '../styles/widgetStyles';
|
|
4
|
+
import { CloseButton } from './CloseButton';
|
|
5
|
+
import { Animated } from 'react-native';
|
|
6
|
+
import { useHeightAnimation } from '../hooks/useHeightAnimation';
|
|
7
|
+
|
|
8
|
+
interface InlineWidgetProps {
|
|
9
|
+
visible: boolean;
|
|
10
|
+
height: number;
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
onClose?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const InlineWidget: React.FC<InlineWidgetProps> = ({
|
|
16
|
+
visible,
|
|
17
|
+
height,
|
|
18
|
+
children,
|
|
19
|
+
onClose,
|
|
20
|
+
}) => {
|
|
21
|
+
const { animatedHeightStyle, updateHeight } = useHeightAnimation(height);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
updateHeight(height);
|
|
25
|
+
}, [height, updateHeight]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<View style={[styles.inlineWrapper, getWidgetVisibility(visible)]}>
|
|
29
|
+
<Animated.View
|
|
30
|
+
style={[styles.inline, animatedHeightStyle, getWidgetVisibility(visible)]}>
|
|
31
|
+
{children}
|
|
32
|
+
<CloseButton visible={visible} onPress={onClose || (() => { })} />
|
|
33
|
+
</Animated.View>
|
|
34
|
+
</View>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { Modal, SafeAreaView, View, Animated } from 'react-native';
|
|
3
|
-
import { styles, getWidgetVisibility } from '../styles/widgetStyles';
|
|
4
|
-
import { CloseButton } from './CloseButton';
|
|
5
|
-
import { useHeightAnimation } from '../hooks/useHeightAnimation';
|
|
6
|
-
|
|
7
|
-
interface ModalWidgetProps {
|
|
8
|
-
visible: boolean;
|
|
9
|
-
height: number;
|
|
10
|
-
children?: React.ReactNode;
|
|
11
|
-
onClose?: () => void;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const ModalWidget: React.FC<ModalWidgetProps> = ({
|
|
15
|
-
visible,
|
|
16
|
-
height,
|
|
17
|
-
children,
|
|
18
|
-
onClose,
|
|
19
|
-
}) => {
|
|
20
|
-
const [isWidgetVisible, setIsWidgetVisible] = useState<boolean>(true);
|
|
21
|
-
|
|
22
|
-
const { animatedHeightStyle, updateHeight } = useHeightAnimation(height);
|
|
23
|
-
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
updateHeight(height);
|
|
26
|
-
}, [height, updateHeight]);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<SafeAreaView>
|
|
30
|
-
<Modal
|
|
31
|
-
transparent
|
|
32
|
-
visible={isWidgetVisible}
|
|
33
|
-
animationType="slide"
|
|
34
|
-
hardwareAccelerated
|
|
35
|
-
>
|
|
36
|
-
<View style={[styles.modalOverlay, getWidgetVisibility(visible)]}>
|
|
37
|
-
<Animated.View
|
|
38
|
-
style={[
|
|
39
|
-
styles.modalContent,
|
|
40
|
-
getWidgetVisibility(visible),
|
|
41
|
-
animatedHeightStyle,
|
|
42
|
-
]}
|
|
43
|
-
>
|
|
44
|
-
{children}
|
|
45
|
-
<CloseButton
|
|
46
|
-
visible={visible}
|
|
47
|
-
onPress={() => {
|
|
48
|
-
setIsWidgetVisible(false);
|
|
49
|
-
if (onClose) {
|
|
50
|
-
onClose();
|
|
51
|
-
}
|
|
52
|
-
}}
|
|
53
|
-
/>
|
|
54
|
-
</Animated.View>
|
|
55
|
-
</View>
|
|
56
|
-
</Modal>
|
|
57
|
-
</SafeAreaView>
|
|
58
|
-
);
|
|
59
|
-
};
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { Modal, SafeAreaView, View, Animated } from 'react-native';
|
|
3
|
+
import { styles, getWidgetVisibility } from '../styles/widgetStyles';
|
|
4
|
+
import { CloseButton } from './CloseButton';
|
|
5
|
+
import { useHeightAnimation } from '../hooks/useHeightAnimation';
|
|
6
|
+
|
|
7
|
+
interface ModalWidgetProps {
|
|
8
|
+
visible: boolean;
|
|
9
|
+
height: number;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
onClose?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ModalWidget: React.FC<ModalWidgetProps> = ({
|
|
15
|
+
visible,
|
|
16
|
+
height,
|
|
17
|
+
children,
|
|
18
|
+
onClose,
|
|
19
|
+
}) => {
|
|
20
|
+
const [isWidgetVisible, setIsWidgetVisible] = useState<boolean>(true);
|
|
21
|
+
|
|
22
|
+
const { animatedHeightStyle, updateHeight } = useHeightAnimation(height);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
updateHeight(height);
|
|
26
|
+
}, [height, updateHeight]);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<SafeAreaView>
|
|
30
|
+
<Modal
|
|
31
|
+
transparent
|
|
32
|
+
visible={isWidgetVisible}
|
|
33
|
+
animationType="slide"
|
|
34
|
+
hardwareAccelerated
|
|
35
|
+
>
|
|
36
|
+
<View style={[styles.modalOverlay, getWidgetVisibility(visible)]}>
|
|
37
|
+
<Animated.View
|
|
38
|
+
style={[
|
|
39
|
+
styles.modalContent,
|
|
40
|
+
getWidgetVisibility(visible),
|
|
41
|
+
animatedHeightStyle,
|
|
42
|
+
]}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
<CloseButton
|
|
46
|
+
visible={visible}
|
|
47
|
+
onPress={() => {
|
|
48
|
+
setIsWidgetVisible(false);
|
|
49
|
+
if (onClose) {
|
|
50
|
+
onClose();
|
|
51
|
+
}
|
|
52
|
+
}}
|
|
53
|
+
/>
|
|
54
|
+
</Animated.View>
|
|
55
|
+
</View>
|
|
56
|
+
</Modal>
|
|
57
|
+
</SafeAreaView>
|
|
58
|
+
);
|
|
59
|
+
};
|