react-native-signature-pad
Version:
React Native wrapper around szimek's Canvas based Signature Pad
133 lines (106 loc) • 4.47 kB
JavaScript
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {StyleSheet, View, ViewPropTypes} from 'react-native';
import {WebView} from 'react-native-webview';
import htmlContent from './injectedHtml';
import injectedSignaturePad from './injectedJavaScript/signaturePad';
import injectedApplication from './injectedJavaScript/application';
import injectedErrorHandler from './injectedJavaScript/errorHandler';
import injectedExecuteNativeFunction from './injectedJavaScript/executeNativeFunction';
export default class SignaturePad extends Component {
static propTypes = {
onChange: PropTypes.func,
onError: PropTypes.func,
style: ViewPropTypes.style,
penColor: PropTypes.string,
dataURL: PropTypes.string
};
static defaultProps = {
onChange: () => {
},
onError: () => {
},
style: {}
};
constructor(props) {
super(props);
this.state = {base64DataUrl: props.dataURL || null};
const { backgroundColor } = StyleSheet.flatten(props.style);
var injectedJavaScript = injectedExecuteNativeFunction
+ injectedErrorHandler
+ injectedSignaturePad
+ injectedApplication(props.penColor, backgroundColor, props.dataURL);
var html = htmlContent(injectedJavaScript);
this.source = {html}; //We don't use WebView's injectedJavaScript because on Android, the WebView re-injects the JavaScript upon every url change. Given that we use url changes to communicate signature changes to the React Native app, the JS is re-injected every time a stroke is drawn.
}
_onNavigationChange = (args) => {
this._parseMessageFromWebViewNavigationChange(unescape(args.url));
};
_parseMessageFromWebViewNavigationChange = (newUrl) => {
//Example input:
//applewebdata://4985ECDA-4C2B-4E37-87ED-0070D14EB985#executeFunction=jsError&arguments=%7B%22message%22:%22ReferenceError:%20Can't%20find%20variable:%20WHADDUP%22,%22url%22:%22applewebdata://4985ECDA-4C2B-4E37-87ED-0070D14EB985%22,%22line%22:340,%22column%22:10%7D"
//All parameters to the native world are passed via a hash url where every parameter is passed as &[ParameterName]<-[Content]&
var hashUrlIndex = newUrl.lastIndexOf('#');
if(hashUrlIndex === -1) {
return;
}
var hashUrl = newUrl.substring(hashUrlIndex);
hashUrl = decodeURIComponent(hashUrl);
var regexFindAllSubmittedParameters = /&(.*?)&/g;
var parameters = {};
var parameterMatch = regexFindAllSubmittedParameters.exec(hashUrl);
if(!parameterMatch) {
return;
}
while(parameterMatch) {
var parameterPair = parameterMatch[1]; //For example executeFunction=jsError or arguments=...
var parameterPairSplit = parameterPair.split('<-');
if(parameterPairSplit.length === 2) {
parameters[parameterPairSplit[0]] = parameterPairSplit[1];
}
parameterMatch = regexFindAllSubmittedParameters.exec(hashUrl);
}
if(!this._attemptToExecuteNativeFunctionFromWebViewMessage(parameters)) {
logger.warn({parameters, hashUrl}, 'Received an unknown set of parameters from WebView');
}
};
_attemptToExecuteNativeFunctionFromWebViewMessage = (message) => {
if(message.executeFunction && message.arguments) {
var parsedArguments = JSON.parse(message.arguments);
var referencedFunction = this['_bridged_' + message.executeFunction];
if(typeof(referencedFunction) === 'function') {
referencedFunction.apply(this, [parsedArguments]);
return true;
}
}
return false;
};
_bridged_jsError = (args) => {
this.props.onError({details: args});
};
_bridged_finishedStroke = ({base64DataUrl}) => {
this.props.onChange({base64DataUrl});
this.setState({base64DataUrl});
};
_renderError = (args) => {
this.props.onError({details: args});
};
_renderLoading = (args) => {
};
onMessage = (event) => {
var base64DataUrl = JSON.parse(event.nativeEvent.data);
this._bridged_finishedStroke(base64DataUrl);
}
render = () => {
return (
<WebView automaticallyAdjustContentInsets={false}
onNavigationStateChange={this._onNavigationChange}
onMessage={this.onMessage}
renderError={this._renderError}
renderLoading={this._renderLoading}
source={this.source}
javaScriptEnabled={true}
style={this.props.style}/>
)
};
}