react-native-signature-canvas 4.7.2 → 5.0.0
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/CHANGELOG.md +51 -0
- package/CLAUDE.md +88 -0
- package/QUICK_START.md +219 -0
- package/README.md +430 -362
- package/WEBVIEW_PROPS.md +166 -0
- package/h5/js/app.js +127 -39
- package/index.d.ts +44 -20
- package/index.js +255 -171
- package/package.json +11 -4
- package/tea.yaml +0 -6
package/index.js
CHANGED
|
@@ -5,8 +5,9 @@ import React, {
|
|
|
5
5
|
useRef,
|
|
6
6
|
forwardRef,
|
|
7
7
|
useImperativeHandle,
|
|
8
|
+
useCallback,
|
|
8
9
|
} from "react";
|
|
9
|
-
import { View, StyleSheet, ActivityIndicator } from "react-native";
|
|
10
|
+
import { View, StyleSheet, ActivityIndicator, Text } from "react-native";
|
|
10
11
|
|
|
11
12
|
import htmlContent from "./h5/html";
|
|
12
13
|
import injectedSignaturePad from "./h5/js/signature_pad";
|
|
@@ -14,6 +15,20 @@ import injectedApplication from "./h5/js/app";
|
|
|
14
15
|
|
|
15
16
|
import { WebView } from "react-native-webview";
|
|
16
17
|
|
|
18
|
+
// Constants for better maintainability
|
|
19
|
+
const MESSAGE_TYPES = {
|
|
20
|
+
BEGIN: "BEGIN",
|
|
21
|
+
END: "END",
|
|
22
|
+
EMPTY: "EMPTY",
|
|
23
|
+
CLEAR: "CLEAR",
|
|
24
|
+
UNDO: "UNDO",
|
|
25
|
+
REDO: "REDO",
|
|
26
|
+
DRAW: "DRAW",
|
|
27
|
+
ERASE: "ERASE",
|
|
28
|
+
CHANGE_PEN: "CHANGE_PEN",
|
|
29
|
+
CHANGE_PEN_SIZE: "CHANGE_PEN_SIZE"
|
|
30
|
+
};
|
|
31
|
+
|
|
17
32
|
const styles = StyleSheet.create({
|
|
18
33
|
webBg: {
|
|
19
34
|
width: "100%",
|
|
@@ -49,21 +64,23 @@ const SignatureView = forwardRef(
|
|
|
49
64
|
imageType = "",
|
|
50
65
|
minWidth = 0.5,
|
|
51
66
|
maxWidth = 2.5,
|
|
67
|
+
minDistance = 5,
|
|
52
68
|
nestedScrollEnabled = false,
|
|
53
|
-
showsVerticalScrollIndicator= true,
|
|
54
|
-
onOK = () => {},
|
|
55
|
-
onEmpty = () => {},
|
|
56
|
-
onClear = () => {},
|
|
57
|
-
onUndo = () => {},
|
|
58
|
-
onRedo = () => {},
|
|
59
|
-
onDraw = () => {},
|
|
60
|
-
onErase = () => {},
|
|
61
|
-
onGetData = () => {},
|
|
62
|
-
onChangePenColor = () => {},
|
|
63
|
-
onChangePenSize = () => {},
|
|
64
|
-
onBegin = () => {},
|
|
65
|
-
onEnd = () => {},
|
|
66
|
-
onLoadEnd = () => {},
|
|
69
|
+
showsVerticalScrollIndicator = true,
|
|
70
|
+
onOK = () => { },
|
|
71
|
+
onEmpty = () => { },
|
|
72
|
+
onClear = () => { },
|
|
73
|
+
onUndo = () => { },
|
|
74
|
+
onRedo = () => { },
|
|
75
|
+
onDraw = () => { },
|
|
76
|
+
onErase = () => { },
|
|
77
|
+
onGetData = () => { },
|
|
78
|
+
onChangePenColor = () => { },
|
|
79
|
+
onChangePenSize = () => { },
|
|
80
|
+
onBegin = () => { },
|
|
81
|
+
onEnd = () => { },
|
|
82
|
+
onLoadEnd = () => { },
|
|
83
|
+
onError = () => { },
|
|
67
84
|
overlayHeight = 0,
|
|
68
85
|
overlayWidth = 0,
|
|
69
86
|
overlaySrc = null,
|
|
@@ -75,81 +92,69 @@ const SignatureView = forwardRef(
|
|
|
75
92
|
webStyle = "",
|
|
76
93
|
webviewContainerStyle = null,
|
|
77
94
|
androidLayerType = "hardware",
|
|
95
|
+
webviewProps = {},
|
|
78
96
|
},
|
|
79
97
|
ref
|
|
80
98
|
) => {
|
|
81
99
|
const [loading, setLoading] = useState(true);
|
|
100
|
+
|
|
101
|
+
const [hasError, setHasError] = useState(false);
|
|
102
|
+
const [retryCount, setRetryCount] = useState(0);
|
|
103
|
+
const maxRetries = 3;
|
|
82
104
|
const webViewRef = useRef();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
injectedJavaScript = injectedJavaScript.replace(
|
|
100
|
-
/<%penColor%>/g,
|
|
101
|
-
penColor
|
|
102
|
-
);
|
|
103
|
-
injectedJavaScript = injectedJavaScript.replace(
|
|
104
|
-
/<%backgroundColor%>/g,
|
|
105
|
-
backgroundColor
|
|
106
|
-
);
|
|
107
|
-
injectedJavaScript = injectedJavaScript.replace(/<%dotSize%>/g, dotSize);
|
|
108
|
-
injectedJavaScript = injectedJavaScript.replace(
|
|
109
|
-
/<%minWidth%>/g,
|
|
110
|
-
minWidth
|
|
111
|
-
);
|
|
112
|
-
injectedJavaScript = injectedJavaScript.replace(
|
|
113
|
-
/<%maxWidth%>/g,
|
|
114
|
-
maxWidth
|
|
115
|
-
);
|
|
105
|
+
// Split source generation for better performance
|
|
106
|
+
const injectedScript = useMemo(() => {
|
|
107
|
+
let script = injectedSignaturePad + injectedApplication;
|
|
108
|
+
script = script.replace(/<%autoClear%>/g, autoClear);
|
|
109
|
+
script = script.replace(/<%trimWhitespace%>/g, trimWhitespace);
|
|
110
|
+
script = script.replace(/<%imageType%>/g, imageType || "image/png");
|
|
111
|
+
script = script.replace(/<%dataURL%>/g, dataURL || "");
|
|
112
|
+
script = script.replace(/<%penColor%>/g, penColor || "black");
|
|
113
|
+
script = script.replace(/<%backgroundColor%>/g, backgroundColor || "rgba(255,255,255,0)");
|
|
114
|
+
script = script.replace(/<%dotSize%>/g, dotSize || "null");
|
|
115
|
+
script = script.replace(/<%minWidth%>/g, minWidth || 0.5);
|
|
116
|
+
script = script.replace(/<%maxWidth%>/g, maxWidth || 2.5);
|
|
117
|
+
script = script.replace(/<%minDistance%>/g, minDistance || 5);
|
|
118
|
+
script = script.replace(/<%orientation%>/g, rotated || false);
|
|
119
|
+
return script;
|
|
120
|
+
}, [autoClear, trimWhitespace, imageType, dataURL, penColor, backgroundColor, dotSize, minWidth, maxWidth, minDistance, rotated]);
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
html =
|
|
120
|
-
html = html.replace(/<%
|
|
121
|
-
html = html.replace(/<%
|
|
122
|
-
html = html.replace(/<%
|
|
123
|
-
html = html.replace(/<%
|
|
124
|
-
html = html.replace(/<%
|
|
125
|
-
html = html.replace(/<%
|
|
126
|
-
html = html.replace(/<%
|
|
127
|
-
html = html.replace(/<%
|
|
128
|
-
html = html.replace(/<%
|
|
122
|
+
const source = useMemo(() => {
|
|
123
|
+
const htmlContentValue = customHtml || htmlContent;
|
|
124
|
+
let html = htmlContentValue(injectedScript);
|
|
125
|
+
html = html.replace(/<%bgWidth%>/g, bgWidth || 0);
|
|
126
|
+
html = html.replace(/<%bgHeight%>/g, bgHeight || 0);
|
|
127
|
+
html = html.replace(/<%bgSrc%>/g, bgSrc || "null");
|
|
128
|
+
html = html.replace(/<%overlayWidth%>/g, overlayWidth || 0);
|
|
129
|
+
html = html.replace(/<%overlayHeight%>/g, overlayHeight || 0);
|
|
130
|
+
html = html.replace(/<%overlaySrc%>/g, overlaySrc || "null");
|
|
131
|
+
html = html.replace(/<%style%>/g, webStyle || "");
|
|
132
|
+
html = html.replace(/<%description%>/g, descriptionText || "Sign above");
|
|
133
|
+
html = html.replace(/<%confirm%>/g, confirmText || "Confirm");
|
|
134
|
+
html = html.replace(/<%clear%>/g, clearText || "Clear");
|
|
135
|
+
html = html.replace(/<%orientation%>/g, rotated || false);
|
|
129
136
|
|
|
130
137
|
return { html };
|
|
131
|
-
}, [
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
rotated,
|
|
136
|
-
imageType,
|
|
137
|
-
webStyle,
|
|
138
|
-
descriptionText,
|
|
139
|
-
confirmText,
|
|
140
|
-
clearText,
|
|
141
|
-
dataURL,
|
|
142
|
-
bgSrc,
|
|
143
|
-
bgWidth,
|
|
144
|
-
bgHeight,
|
|
145
|
-
]);
|
|
138
|
+
}, [injectedScript, customHtml, bgWidth, bgHeight, bgSrc, overlayWidth, overlayHeight, overlaySrc, webStyle, descriptionText, confirmText, clearText, rotated]);
|
|
139
|
+
|
|
140
|
+
// Optimize WebView reload to prevent excessive reloads
|
|
141
|
+
const [shouldReload, setShouldReload] = useState(false);
|
|
146
142
|
|
|
147
143
|
useEffect(() => {
|
|
148
|
-
|
|
149
|
-
webViewRef.current.reload();
|
|
150
|
-
}
|
|
144
|
+
setShouldReload(true);
|
|
151
145
|
}, [source]);
|
|
152
146
|
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (shouldReload && webViewRef.current) {
|
|
149
|
+
try {
|
|
150
|
+
webViewRef.current.reload();
|
|
151
|
+
setShouldReload(false);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.warn("WebView reload failed:", error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}, [shouldReload]);
|
|
157
|
+
|
|
153
158
|
const isJson = (str) => {
|
|
154
159
|
try {
|
|
155
160
|
JSON.parse(str);
|
|
@@ -159,112 +164,176 @@ const SignatureView = forwardRef(
|
|
|
159
164
|
return true;
|
|
160
165
|
};
|
|
161
166
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
case "END":
|
|
168
|
-
onEnd();
|
|
169
|
-
break;
|
|
170
|
-
case "EMPTY":
|
|
171
|
-
onEmpty();
|
|
172
|
-
break;
|
|
173
|
-
case "CLEAR":
|
|
174
|
-
onClear();
|
|
175
|
-
break;
|
|
176
|
-
case "UNDO":
|
|
177
|
-
onUndo();
|
|
178
|
-
break;
|
|
179
|
-
case "REDO":
|
|
180
|
-
onRedo();
|
|
181
|
-
break;
|
|
182
|
-
case "DRAW":
|
|
183
|
-
onDraw();
|
|
184
|
-
break;
|
|
185
|
-
case "ERASE":
|
|
186
|
-
onErase();
|
|
187
|
-
break;
|
|
188
|
-
case "CHANGE_PEN":
|
|
189
|
-
onChangePenColor();
|
|
190
|
-
break;
|
|
191
|
-
case "CHANGE_PEN_SIZE":
|
|
192
|
-
onChangePenSize();
|
|
193
|
-
break;
|
|
194
|
-
default:
|
|
195
|
-
isJson(e.nativeEvent.data)
|
|
196
|
-
? onGetData(e.nativeEvent.data)
|
|
197
|
-
: onOK(e.nativeEvent.data);
|
|
167
|
+
// Enhanced message handling with error handling
|
|
168
|
+
const getSignature = useCallback((e) => {
|
|
169
|
+
if (!e?.nativeEvent?.data) {
|
|
170
|
+
console.warn("Invalid message received from WebView");
|
|
171
|
+
return;
|
|
198
172
|
}
|
|
199
|
-
|
|
173
|
+
|
|
174
|
+
const data = e.nativeEvent.data;
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
switch (data) {
|
|
178
|
+
case MESSAGE_TYPES.BEGIN:
|
|
179
|
+
onBegin();
|
|
180
|
+
break;
|
|
181
|
+
case MESSAGE_TYPES.END:
|
|
182
|
+
onEnd();
|
|
183
|
+
break;
|
|
184
|
+
case MESSAGE_TYPES.EMPTY:
|
|
185
|
+
onEmpty();
|
|
186
|
+
break;
|
|
187
|
+
case MESSAGE_TYPES.CLEAR:
|
|
188
|
+
onClear();
|
|
189
|
+
break;
|
|
190
|
+
case MESSAGE_TYPES.UNDO:
|
|
191
|
+
onUndo();
|
|
192
|
+
break;
|
|
193
|
+
case MESSAGE_TYPES.REDO:
|
|
194
|
+
onRedo();
|
|
195
|
+
break;
|
|
196
|
+
case MESSAGE_TYPES.DRAW:
|
|
197
|
+
onDraw();
|
|
198
|
+
break;
|
|
199
|
+
case MESSAGE_TYPES.ERASE:
|
|
200
|
+
onErase();
|
|
201
|
+
break;
|
|
202
|
+
case MESSAGE_TYPES.CHANGE_PEN:
|
|
203
|
+
onChangePenColor();
|
|
204
|
+
break;
|
|
205
|
+
case MESSAGE_TYPES.CHANGE_PEN_SIZE:
|
|
206
|
+
onChangePenSize();
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
if (isJson(data)) {
|
|
210
|
+
onGetData(data);
|
|
211
|
+
} else if (typeof data === "string" && data.startsWith("data:")) {
|
|
212
|
+
onOK(data);
|
|
213
|
+
} else {
|
|
214
|
+
console.warn("Unknown message type:", data);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error("Error handling WebView message:", error);
|
|
219
|
+
}
|
|
220
|
+
}, [onBegin, onEnd, onEmpty, onClear, onUndo, onRedo, onDraw, onErase, onChangePenColor, onChangePenSize, onGetData, onOK]);
|
|
221
|
+
|
|
222
|
+
// Enhanced WebView method execution with error handling
|
|
223
|
+
const executeWebViewMethod = useCallback((method, params = []) => {
|
|
224
|
+
if (!webViewRef.current) {
|
|
225
|
+
console.warn(`WebView ref is null when calling ${method}`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const script = params.length > 0
|
|
231
|
+
? `${method}(${params.map(p => typeof p === 'string' ? `'${p}'` : p).join(',')});true;`
|
|
232
|
+
: `${method}();true;`;
|
|
233
|
+
webViewRef.current.injectJavaScript(script);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error(`Error executing WebView method ${method}:`, error);
|
|
236
|
+
}
|
|
237
|
+
}, []);
|
|
200
238
|
|
|
201
239
|
useImperativeHandle(
|
|
202
240
|
ref,
|
|
203
241
|
() => ({
|
|
204
|
-
readSignature: () =>
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (webViewRef.current) {
|
|
211
|
-
webViewRef.current.injectJavaScript("clearSignature();true;");
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
undo: () => {
|
|
215
|
-
if (webViewRef.current) {
|
|
216
|
-
webViewRef.current.injectJavaScript("undo();true;");
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
redo: () => {
|
|
220
|
-
if (webViewRef.current) {
|
|
221
|
-
webViewRef.current.injectJavaScript("redo();true;");
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
draw: () => {
|
|
225
|
-
if (webViewRef.current) {
|
|
226
|
-
webViewRef.current.injectJavaScript("draw();true;");
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
erase: () => {
|
|
230
|
-
if (webViewRef.current) {
|
|
231
|
-
webViewRef.current.injectJavaScript("erase();true;");
|
|
232
|
-
}
|
|
233
|
-
},
|
|
242
|
+
readSignature: () => executeWebViewMethod('readSignature'),
|
|
243
|
+
clearSignature: () => executeWebViewMethod('clearSignature'),
|
|
244
|
+
undo: () => executeWebViewMethod('undo'),
|
|
245
|
+
redo: () => executeWebViewMethod('redo'),
|
|
246
|
+
draw: () => executeWebViewMethod('draw'),
|
|
247
|
+
erase: () => executeWebViewMethod('erase'),
|
|
234
248
|
changePenColor: (color) => {
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
);
|
|
249
|
+
if (typeof color !== 'string') {
|
|
250
|
+
console.warn('changePenColor: color must be a string');
|
|
251
|
+
return;
|
|
239
252
|
}
|
|
253
|
+
executeWebViewMethod('changePenColor', [color]);
|
|
240
254
|
},
|
|
241
255
|
changePenSize: (minW, maxW) => {
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
);
|
|
256
|
+
if (typeof minW !== 'number' || typeof maxW !== 'number') {
|
|
257
|
+
console.warn('changePenSize: minW and maxW must be numbers');
|
|
258
|
+
return;
|
|
246
259
|
}
|
|
260
|
+
executeWebViewMethod('changePenSize', [minW, maxW]);
|
|
247
261
|
},
|
|
248
|
-
getData: () =>
|
|
249
|
-
|
|
250
|
-
|
|
262
|
+
getData: () => executeWebViewMethod('getData'),
|
|
263
|
+
fromData: (pointGroups) => {
|
|
264
|
+
if (!pointGroups) {
|
|
265
|
+
console.warn('fromData: pointGroups must be an array');
|
|
266
|
+
return;
|
|
251
267
|
}
|
|
268
|
+
executeWebViewMethod('fromData', [pointGroups, false]);
|
|
252
269
|
},
|
|
253
270
|
}),
|
|
254
|
-
[
|
|
271
|
+
[executeWebViewMethod]
|
|
255
272
|
);
|
|
256
273
|
|
|
257
|
-
const renderError = ({ nativeEvent }) =>
|
|
258
|
-
console.
|
|
274
|
+
const renderError = useCallback(({ nativeEvent }) => {
|
|
275
|
+
console.error("WebView error: ", nativeEvent);
|
|
276
|
+
setHasError(true);
|
|
259
277
|
|
|
260
|
-
|
|
261
|
-
|
|
278
|
+
// Call user-provided error handler
|
|
279
|
+
try {
|
|
280
|
+
onError(new Error(`WebView error: ${nativeEvent.description || nativeEvent.code}`));
|
|
281
|
+
} catch (err) {
|
|
282
|
+
console.warn('Error in onError callback:', err);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Attempt to recover from error with retry logic
|
|
286
|
+
if (webViewRef.current && nativeEvent.code !== -999 && retryCount < maxRetries) {
|
|
287
|
+
setTimeout(() => {
|
|
288
|
+
try {
|
|
289
|
+
setRetryCount(prev => prev + 1);
|
|
290
|
+
webViewRef.current.reload();
|
|
291
|
+
setHasError(false);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error("Failed to reload WebView after error:", error);
|
|
294
|
+
}
|
|
295
|
+
}, Math.min(1000 * Math.pow(2, retryCount), 5000)); // Exponential backoff
|
|
296
|
+
}
|
|
297
|
+
}, [onError, retryCount, maxRetries]);
|
|
298
|
+
|
|
299
|
+
const handleLoadEnd = useCallback(() => {
|
|
300
|
+
setLoading(false);
|
|
301
|
+
setHasError(false);
|
|
302
|
+
setRetryCount(0);
|
|
303
|
+
|
|
304
|
+
try {
|
|
262
305
|
onLoadEnd();
|
|
263
|
-
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.warn('Error in onLoadEnd callback:', error);
|
|
308
|
+
}
|
|
309
|
+
}, [onLoadEnd]);
|
|
310
|
+
|
|
311
|
+
// Performance monitoring
|
|
312
|
+
const handleLoadStart = useCallback(() => {
|
|
313
|
+
setLoading(true);
|
|
314
|
+
}, []);
|
|
315
|
+
|
|
316
|
+
const handleLoadProgress = useCallback(({ nativeEvent }) => {
|
|
317
|
+
// Optional: Add progress monitoring
|
|
318
|
+
if (nativeEvent.progress === 1) {
|
|
319
|
+
setLoading(false);
|
|
320
|
+
}
|
|
321
|
+
}, []);
|
|
264
322
|
|
|
265
323
|
return (
|
|
266
324
|
<View style={[styles.webBg, style]}>
|
|
267
325
|
<WebView
|
|
326
|
+
// Core functionality props (cannot be overridden)
|
|
327
|
+
ref={webViewRef}
|
|
328
|
+
source={source}
|
|
329
|
+
onMessage={getSignature}
|
|
330
|
+
onError={renderError}
|
|
331
|
+
onLoadEnd={handleLoadEnd}
|
|
332
|
+
onLoadStart={handleLoadStart}
|
|
333
|
+
onLoadProgress={handleLoadProgress}
|
|
334
|
+
javaScriptEnabled={true}
|
|
335
|
+
useWebKit={true}
|
|
336
|
+
// Default component props (can be overridden by webviewProps)
|
|
268
337
|
bounces={false}
|
|
269
338
|
style={[webviewContainerStyle]}
|
|
270
339
|
scrollEnabled={scrollable}
|
|
@@ -272,19 +341,34 @@ const SignatureView = forwardRef(
|
|
|
272
341
|
androidHardwareAccelerationDisabled={
|
|
273
342
|
androidHardwareAccelerationDisabled
|
|
274
343
|
}
|
|
275
|
-
ref={webViewRef}
|
|
276
|
-
useWebKit={true}
|
|
277
|
-
source={source}
|
|
278
|
-
onMessage={getSignature}
|
|
279
|
-
javaScriptEnabled={true}
|
|
280
|
-
onError={renderError}
|
|
281
|
-
onLoadEnd={handleLoadEnd}
|
|
282
344
|
nestedScrollEnabled={nestedScrollEnabled}
|
|
283
345
|
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
|
|
346
|
+
// Default performance optimizations
|
|
347
|
+
cacheEnabled={true}
|
|
348
|
+
allowsInlineMediaPlayback={false}
|
|
349
|
+
mediaPlaybackRequiresUserAction={true}
|
|
350
|
+
allowsBackForwardNavigationGestures={false}
|
|
351
|
+
// Default security enhancements
|
|
352
|
+
allowsLinkPreview={false}
|
|
353
|
+
allowFileAccess={false}
|
|
354
|
+
allowFileAccessFromFileURLs={false}
|
|
355
|
+
allowUniversalAccessFromFileURLs={false}
|
|
356
|
+
mixedContentMode="never"
|
|
357
|
+
originWhitelist={['*']}
|
|
358
|
+
// Default error recovery
|
|
359
|
+
startInLoadingState={true}
|
|
360
|
+
// User-provided WebView props (can override defaults but not core functionality)
|
|
361
|
+
{...webviewProps}
|
|
284
362
|
/>
|
|
285
|
-
{loading && (
|
|
363
|
+
{(loading || hasError) && (
|
|
286
364
|
<View style={styles.loadingOverlayContainer}>
|
|
287
|
-
|
|
365
|
+
{hasError ? (
|
|
366
|
+
<Text style={{ color: '#ff0000', textAlign: 'center', padding: 10 }}>
|
|
367
|
+
Error loading signature pad{retryCount > 0 ? ` (Retry ${retryCount}/${maxRetries})` : ''}
|
|
368
|
+
</Text>
|
|
369
|
+
) : (
|
|
370
|
+
<ActivityIndicator color={"#007AFF"} size="small" />
|
|
371
|
+
)}
|
|
288
372
|
</View>
|
|
289
373
|
)}
|
|
290
374
|
</View>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-signature-canvas",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "React Native
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "A performant, customizable React Native signature canvas with advanced error handling, WebView optimization, and TypeScript support for iOS, Android, and Expo",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"genlog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
|
@@ -17,9 +17,16 @@
|
|
|
17
17
|
"ios",
|
|
18
18
|
"android",
|
|
19
19
|
"signature",
|
|
20
|
-
"pad",
|
|
20
|
+
"signature-pad",
|
|
21
21
|
"canvas",
|
|
22
|
-
"expo"
|
|
22
|
+
"expo",
|
|
23
|
+
"typescript",
|
|
24
|
+
"drawing",
|
|
25
|
+
"webview",
|
|
26
|
+
"performance",
|
|
27
|
+
"error-handling",
|
|
28
|
+
"undo-redo",
|
|
29
|
+
"svg-export"
|
|
23
30
|
],
|
|
24
31
|
"author": "YanYuanFE",
|
|
25
32
|
"license": "MIT",
|