Browse Source

Merge pull request #8 from wix/update-offset-and-cursor-calculation-fix

Fix update offset and cursor position calculation
Igor Khudik 8 years ago
parent
commit
e3469110ef
4 changed files with 87 additions and 33 deletions
  1. 44
    16
      src/RichTextEditor.js
  2. 9
    0
      src/WebviewMessageHandler.js
  3. 4
    1
      src/const.js
  4. 30
    16
      src/editor.html

+ 44
- 16
src/RichTextEditor.js View File

2
 import WebViewBridge from 'react-native-webview-bridge-updated';
2
 import WebViewBridge from 'react-native-webview-bridge-updated';
3
 import {InjectedMessageHandler} from './WebviewMessageHandler';
3
 import {InjectedMessageHandler} from './WebviewMessageHandler';
4
 import {actions, messages} from './const';
4
 import {actions, messages} from './const';
5
-import {Modal, View, Text, StyleSheet, TextInput, TouchableOpacity, Platform, PixelRatio, Keyboard} from 'react-native';
5
+import {Modal, View, Text, StyleSheet, TextInput, TouchableOpacity, Platform, PixelRatio, Keyboard, Dimensions} from 'react-native';
6
 
6
 
7
 const injectScript = `
7
 const injectScript = `
8
   (function () {
8
   (function () {
10
   }());
10
   }());
11
 `;
11
 `;
12
 
12
 
13
-const PlatfomIOS = Platform.OS === 'ios';
13
+const PlatformIOS = Platform.OS === 'ios';
14
 
14
 
15
 export default class RichTextEditor extends Component {
15
 export default class RichTextEditor extends Component {
16
   static propTypes = {
16
   static propTypes = {
21
     editorInitializedCallback: PropTypes.func,
21
     editorInitializedCallback: PropTypes.func,
22
     customCSS: PropTypes.string,
22
     customCSS: PropTypes.string,
23
     hiddenTitle: PropTypes.bool,
23
     hiddenTitle: PropTypes.bool,
24
-    enableOnChange: PropTypes.bool
24
+    enableOnChange: PropTypes.bool,
25
+    footerHeight: PropTypes.number
25
   };
26
   };
26
 
27
 
27
   constructor(props) {
28
   constructor(props) {
44
   }
45
   }
45
 
46
 
46
   componentWillMount() {
47
   componentWillMount() {
47
-    if(PlatfomIOS) {
48
+    if(PlatformIOS) {
48
       this.keyboardEventListeners = [
49
       this.keyboardEventListeners = [
49
         Keyboard.addListener('keyboardWillShow', this._onKeyboardWillShow),
50
         Keyboard.addListener('keyboardWillShow', this._onKeyboardWillShow),
50
         Keyboard.addListener('keyboardWillHide', this._onKeyboardWillHide)
51
         Keyboard.addListener('keyboardWillHide', this._onKeyboardWillHide)
67
     if (this.state.keyboardHeight === newKeyboardHeight) {
68
     if (this.state.keyboardHeight === newKeyboardHeight) {
68
       return;
69
       return;
69
     }
70
     }
71
+    if (newKeyboardHeight) {
72
+      this.setEditorAvailableHeightBasedOnKeyboardHeight(newKeyboardHeight);
73
+    }
70
     this.setState({keyboardHeight: newKeyboardHeight});
74
     this.setState({keyboardHeight: newKeyboardHeight});
71
   }
75
   }
72
 
76
 
73
   _onKeyboardWillHide(event) {
77
   _onKeyboardWillHide(event) {
74
     this.setState({keyboardHeight: 0});
78
     this.setState({keyboardHeight: 0});
75
   }
79
   }
80
+
81
+  setEditorAvailableHeightBasedOnKeyboardHeight(keyboardHeight) {
82
+    const {top = 0, bottom = 0} = this.props.contentInset;
83
+    const {marginTop = 0, marginBottom = 0} = this.props.style;
84
+    const spacing = marginTop + marginBottom + top + bottom;
85
+
86
+    const editorAvailableHeight = Dimensions.get('window').height - keyboardHeight - spacing;
87
+    this.setEditorHeight(editorAvailableHeight);
88
+  }
76
   
89
   
77
   onBridgeMessage(str){
90
   onBridgeMessage(str){
78
     try {
91
     try {
189
             onRequestClose={() => this.setState({showLinkDialog: false})}
202
             onRequestClose={() => this.setState({showLinkDialog: false})}
190
         >
203
         >
191
           <View style={styles.modal}>
204
           <View style={styles.modal}>
192
-            <View style={[styles.innerModal, {marginBottom: PlatfomIOS ? this.state.keyboardHeight : 0}]}>
205
+            <View style={[styles.innerModal, {marginBottom: PlatformIOS ? this.state.keyboardHeight : 0}]}>
193
               <Text style={styles.inputTitle}>Title</Text>
206
               <Text style={styles.inputTitle}>Title</Text>
194
               <View style={styles.inputWrapper}>
207
               <View style={styles.inputWrapper}>
195
                 <TextInput
208
                 <TextInput
209
                     autoCorrect={false}
222
                     autoCorrect={false}
210
                 />
223
                 />
211
               </View>
224
               </View>
212
-              {PlatfomIOS && <View style={styles.lineSeparator}/>}
225
+              {PlatformIOS && <View style={styles.lineSeparator}/>}
213
               {this._renderModalButtons()}
226
               {this._renderModalButtons()}
214
             </View>
227
             </View>
215
           </View>
228
           </View>
228
 
241
 
229
   _renderModalButtons() {
242
   _renderModalButtons() {
230
     const insertUpdateDisabled = this.state.linkTitle.trim().length <= 0 || this.state.linkUrl.trim().length <= 0;
243
     const insertUpdateDisabled = this.state.linkTitle.trim().length <= 0 || this.state.linkUrl.trim().length <= 0;
231
-    const containerPlatformStyle = PlatfomIOS ? {justifyContent: 'space-between'} : {paddingTop: 15};
232
-    const buttonPlatformStyle = PlatfomIOS ? {flex: 1, height: 45, justifyContent: 'center'} : {};
244
+    const containerPlatformStyle = PlatformIOS ? {justifyContent: 'space-between'} : {paddingTop: 15};
245
+    const buttonPlatformStyle = PlatformIOS ? {flex: 1, height: 45, justifyContent: 'center'} : {};
233
     return (
246
     return (
234
       <View style={[{alignSelf: 'stretch', flexDirection: 'row'}, containerPlatformStyle]}>
247
       <View style={[{alignSelf: 'stretch', flexDirection: 'row'}, containerPlatformStyle]}>
235
-        {!PlatfomIOS && <View style={{flex: 1}}/>}
248
+        {!PlatformIOS && <View style={{flex: 1}}/>}
236
         <TouchableOpacity
249
         <TouchableOpacity
237
             onPress={() => this._hideModal()}
250
             onPress={() => this._hideModal()}
238
             style={buttonPlatformStyle}
251
             style={buttonPlatformStyle}
266
   }
279
   }
267
 
280
 
268
   _upperCaseButtonTextIfNeeded(buttonText) {
281
   _upperCaseButtonTextIfNeeded(buttonText) {
269
-    return PlatfomIOS ? buttonText : buttonText.toUpperCase();
282
+    return PlatformIOS ? buttonText : buttonText.toUpperCase();
270
   }
283
   }
271
 
284
 
272
   render() {
285
   render() {
273
     //in release build, external html files in Android can't be required, so they must be placed in the assets folder and accessed via uri
286
     //in release build, external html files in Android can't be required, so they must be placed in the assets folder and accessed via uri
274
-    const pageSource = PlatfomIOS ? require('./editor.html') : { uri: 'file:///android_asset/editor.html' };
287
+    const pageSource = PlatformIOS ? require('./editor.html') : { uri: 'file:///android_asset/editor.html' };
275
     return (
288
     return (
276
       <View style={{flex: 1}}>
289
       <View style={{flex: 1}}>
277
         <WebViewBridge
290
         <WebViewBridge
278
-          style={{flex: 1}}
279
           {...this.props}
291
           {...this.props}
280
           hideKeyboardAccessoryView={true}
292
           hideKeyboardAccessoryView={true}
281
           keyboardDisplayRequiresUserAction={false}
293
           keyboardDisplayRequiresUserAction={false}
504
 
516
 
505
   init() {
517
   init() {
506
     this._sendAction(actions.init);
518
     this._sendAction(actions.init);
519
+    this.setPlatform();
520
+    if (this.props.footerHeight) {
521
+      this.setFooterHeight();
522
+    }
523
+  }
524
+
525
+  setEditorHeight(height) {
526
+    this._sendAction(actions.setEditorHeight, height);
527
+  }
528
+
529
+  setFooterHeight() {
530
+    this._sendAction(actions.setFooterHeight, this.props.footerHeight);
531
+  }
532
+
533
+  setPlatform() {
534
+    this._sendAction(actions.setPlatform, Platform.OS);
507
   }
535
   }
508
 
536
 
509
   async getTitleHtml() {
537
   async getTitleHtml() {
587
   innerModal: {
615
   innerModal: {
588
     backgroundColor: 'rgba(255, 255, 255, 0.9)',
616
     backgroundColor: 'rgba(255, 255, 255, 0.9)',
589
     paddingTop: 20,
617
     paddingTop: 20,
590
-    paddingBottom: PlatfomIOS ? 0 : 20,
618
+    paddingBottom: PlatformIOS ? 0 : 20,
591
     paddingLeft: 20,
619
     paddingLeft: 20,
592
     paddingRight: 20,
620
     paddingRight: 20,
593
     alignSelf: 'stretch',
621
     alignSelf: 'stretch',
594
     margin: 40,
622
     margin: 40,
595
-    borderRadius: PlatfomIOS ? 8 : 2
623
+    borderRadius: PlatformIOS ? 8 : 2
596
   },
624
   },
597
   button: {
625
   button: {
598
     fontSize: 16,
626
     fontSize: 16,
603
     marginTop: 5,
631
     marginTop: 5,
604
     marginBottom: 10,
632
     marginBottom: 10,
605
     borderBottomColor: '#4a4a4a',
633
     borderBottomColor: '#4a4a4a',
606
-    borderBottomWidth: PlatfomIOS ? 1 / PixelRatio.get() : 0
634
+    borderBottomWidth: PlatformIOS ? 1 / PixelRatio.get() : 0
607
   },
635
   },
608
   inputTitle: {
636
   inputTitle: {
609
     color: '#4a4a4a'
637
     color: '#4a4a4a'
610
   },
638
   },
611
   input: {
639
   input: {
612
-    height: PlatfomIOS ? 20 : 40,
640
+    height: PlatformIOS ? 20 : 40,
613
     paddingTop: 0
641
     paddingTop: 0
614
   },
642
   },
615
   lineSeparator: {
643
   lineSeparator: {

+ 9
- 0
src/WebviewMessageHandler.js View File

161
         case '${actions.init}':
161
         case '${actions.init}':
162
           zss_editor.init();
162
           zss_editor.init();
163
           break;
163
           break;
164
+        case '${actions.setEditorHeight}':
165
+          zss_editor.setEditorHeight(action.data);
166
+          break;
167
+        case '${actions.setFooterHeight}':
168
+          zss_editor.setFooterHeight(action.data);
169
+          break;
170
+        case '${actions.setPlatform}':
171
+          zss_editor.setPlatform(action.data);
172
+          break;
164
       }
173
       }
165
     };
174
     };
166
   }
175
   }

+ 4
- 1
src/const.js View File

49
   setCustomCSS: 'SET_CUSTOM_CSS',
49
   setCustomCSS: 'SET_CUSTOM_CSS',
50
   setTextColor: 'SET_TEXT_COLOR',
50
   setTextColor: 'SET_TEXT_COLOR',
51
   setBackgroundColor: 'SET_BACKGROUND_COLOR',
51
   setBackgroundColor: 'SET_BACKGROUND_COLOR',
52
-  init: 'ZSSS_INIT'
52
+  init: 'ZSSS_INIT',
53
+  setEditorHeight: 'SET_EDITOR_HEIGHT',
54
+  setFooterHeight: 'SET_FOOTER_HEIGHT',
55
+  setPlatform: 'SET_PLATFORM'
53
 };
56
 };
54
 
57
 
55
 
58
 

+ 30
- 16
src/editor.html View File

808
 			zss_editor.enabledItems = {};
808
 			zss_editor.enabledItems = {};
809
 
809
 
810
 			// Height of content window, will be set by viewController
810
 			// Height of content window, will be set by viewController
811
-			zss_editor.contentHeight = 244;
811
+			zss_editor.editorHeight = 244;
812
 
812
 
813
 			// Sets to true when extra footer gap shows and requires to hide
813
 			// Sets to true when extra footer gap shows and requires to hide
814
 			zss_editor.updateScrollOffset = false;
814
 			zss_editor.updateScrollOffset = false;
902
 				$(window).on('touchstart', function(e) {
902
 				$(window).on('touchstart', function(e) {
903
 					zss_editor.isDragging = false;
903
 					zss_editor.isDragging = false;
904
 				});
904
 				});
905
+				$(window).on('scroll', function(e) {
906
+					if (zss_editor.platform === 'ios') {
907
+						zss_editor.updateOffset();
908
+					}
909
+				});
905
 
910
 
906
 				setupTouchEndFocus('zss_editor_title');
911
 				setupTouchEndFocus('zss_editor_title');
907
 				setupTouchEndFocus('zss_editor_content');
912
 				setupTouchEndFocus('zss_editor_content');
965
 
970
 
966
 				var offsetY = window.document.body.scrollTop;
971
 				var offsetY = window.document.body.scrollTop;
967
 
972
 
968
-				var footer = $('#zss_editor_footer');
973
+				var footer = document.getElementById('zss_editor_footer');
969
 
974
 
970
-				var maxOffsetY = footer.offset().top - zss_editor.contentHeight;
975
+				var maxOffsetY = footer.offsetTop + footer.offsetHeight - zss_editor.editorHeight;
971
 
976
 
972
 				if (maxOffsetY < 0)
977
 				if (maxOffsetY < 0)
973
 					maxOffsetY = 0;
978
 					maxOffsetY = 0;
1033
 			}
1038
 			}
1034
 
1039
 
1035
 			zss_editor.calculateEditorHeightWithCaretPosition = function() {
1040
 			zss_editor.calculateEditorHeightWithCaretPosition = function() {
1036
-
1037
-				var padding = 50;
1038
-				var c = zss_editor.getCaretYPosition();
1039
-
1041
+				var caretYPosition = zss_editor.getCaretYPosition();
1042
+				var lineHeight = 20;
1043
+				var halfLineHeight = lineHeight/2;
1040
 				var offsetY = window.document.body.scrollTop;
1044
 				var offsetY = window.document.body.scrollTop;
1041
-				var height = zss_editor.contentHeight;
1042
-
1043
-				var newPos = window.pageYOffset;
1044
-
1045
-				if (c < offsetY) {
1046
-					newPos = c;
1047
-				} else if (c > (offsetY + height - padding)) {
1048
-					newPos = c - height + padding - 18;
1045
+				var editorHeight = zss_editor.editorHeight;
1046
+				var newPos;
1047
+
1048
+				var futureEditorHeight = caretYPosition + lineHeight;
1049
+				if (caretYPosition < offsetY) {
1050
+					newPos = caretYPosition;
1051
+				} else if (futureEditorHeight > editorHeight + offsetY) {
1052
+					newPos = futureEditorHeight - editorHeight + halfLineHeight;
1049
 				}
1053
 				}
1050
 
1054
 
1051
-				window.scrollTo(0, newPos);
1055
+				if (newPos) {
1056
+					window.scrollTo(0, newPos);
1057
+				}
1052
 			}
1058
 			}
1053
 
1059
 
1054
 			zss_editor.backuprange = function(){
1060
 			zss_editor.backuprange = function(){
1629
 				});
1635
 				});
1630
 			}
1636
 			}
1631
 
1637
 
1638
+			zss_editor.setEditorHeight = function(editorHeight) {
1639
+				zss_editor.editorHeight = editorHeight;
1640
+			}
1641
+
1642
+			zss_editor.setPlatform = function(platform) {
1643
+				zss_editor.platform = platform;
1644
+			}
1645
+
1632
 			//end
1646
 			//end
1633
 		</script>
1647
 		</script>
1634
 
1648