Skip to content

Commit e37ee3b

Browse files
committed
auto cursor scroll
1 parent f88b8f4 commit e37ee3b

File tree

4 files changed

+86
-53
lines changed

4 files changed

+86
-53
lines changed

examples/src/example.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const htmlIcon = require('./assets/html.png');
4949
class Example extends React.Component {
5050
richText = React.createRef();
5151
linkModal = React.createRef();
52+
scrollRef = React.createRef();
5253

5354
constructor(props) {
5455
super(props);
@@ -87,7 +88,8 @@ class Example extends React.Component {
8788
Keyboard.removeListener('keyboardDidHide', this.onKeyHide);
8889
}
8990

90-
onKeyHide = () => {};
91+
onKeyHide = () => {
92+
};
9193

9294
onKeyShow = () => {
9395
TextInput.State.currentlyFocusedInput() && this.setState({emojiVisible: false});
@@ -158,11 +160,11 @@ class Example extends React.Component {
158160
this.richText.current?.setFontSize(size[XMath.random(size.length - 1)]);
159161
};
160162

161-
foreColor = ()=> {
163+
foreColor = () => {
162164
this.richText.current?.setForeColor('blue');
163165
}
164166

165-
hiliteColor = ()=> {
167+
hiliteColor = () => {
166168
this.richText.current?.setHiliteColor('red');
167169
}
168170

@@ -266,14 +268,19 @@ class Example extends React.Component {
266268
this.editorFocus = false;
267269
};
268270

271+
handleCursorPosition = (scrollY) => {
272+
// Positioning scroll bar
273+
this.scrollRef.current.scrollTo({y: scrollY - 30, duration: 100, animated: true});
274+
}
275+
269276
render() {
270277
let that = this;
271278
const {contentStyle, theme, emojiVisible, disabled} = that.state;
272279
const {backgroundColor, color, placeholderColor} = contentStyle;
273280
const dark = theme === 'dark';
274281
return (
275282
<SafeAreaView style={[styles.container, dark && styles.darkBack]}>
276-
<StatusBar barStyle={theme !== 'dark' ? 'dark-content' : 'light-content'} />
283+
<StatusBar barStyle={theme !== 'dark' ? 'dark-content' : 'light-content'}/>
277284
<InsertLinkModal
278285
placeholderColor={placeholderColor}
279286
color={color}
@@ -282,10 +289,11 @@ class Example extends React.Component {
282289
ref={that.linkModal}
283290
/>
284291
<View style={styles.nav}>
285-
<Button title={'HOME'} onPress={that.onHome} />
286-
<Button title="Preview" onPress={that.save} />
292+
<Button title={'HOME'} onPress={that.onHome}/>
293+
<Button title="Preview" onPress={that.save}/>
287294
</View>
288-
<ScrollView style={[styles.scroll, dark && styles.scrollDark]} keyboardDismissMode={'none'}>
295+
<ScrollView style={[styles.scroll, dark && styles.scrollDark]} keyboardDismissMode={'none'}
296+
ref={that.scrollRef} scrollEventThrottle={20}>
289297
<View style={[styles.topVi, dark && styles.darkBack]}>
290298
<View style={styles.item}>
291299
<Text style={{color}}>To: </Text>
@@ -306,8 +314,8 @@ class Example extends React.Component {
306314
/>
307315
</View>
308316
<View style={styles.item}>
309-
<Button title={theme} onPress={that.onTheme} />
310-
<Button title={disabled ? 'enable' : 'disable'} onPress={that.onDisabled} />
317+
<Button title={theme} onPress={that.onTheme}/>
318+
<Button title={disabled ? 'enable' : 'disable'} onPress={that.onDisabled}/>
311319
</View>
312320
</View>
313321
<RichToolbar
@@ -339,6 +347,7 @@ class Example extends React.Component {
339347
onMessage={that.handleMessage}
340348
onFocus={that.handleFocus}
341349
onBlur={that.handleBlur}
350+
onCursorPosition={that.handleCursorPosition}
342351
pasteAsPlainText={true}
343352
/>
344353
</ScrollView>
@@ -401,7 +410,7 @@ class Example extends React.Component {
401410
foreColor={that.foreColor}
402411
hiliteColor={that.hiliteColor}
403412
/>
404-
{emojiVisible && <EmojiView onSelect={that.insertEmoji} />}
413+
{emojiVisible && <EmojiView onSelect={that.insertEmoji}/>}
405414
</KeyboardAvoidingView>
406415
</SafeAreaView>
407416
);

src/RichEditor.js

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ export default class RichTextEditor extends Component {
4545
that._onKeyboardWillHide = that._onKeyboardWillHide.bind(that);
4646
that.init = that.init.bind(that);
4747
that.setRef = that.setRef.bind(that);
48+
that.onViewLayout = that.onViewLayout.bind(that);
4849
that._keyOpen = false;
49-
this._focus = false;
50+
that._focus = false;
51+
that.layout = {};
5052
that.selectionChangeListeners = [];
5153
const {
5254
editorStyle: {backgroundColor, color, placeholderColor, cssText, contentCSSText} = {},
@@ -133,72 +135,74 @@ export default class RichTextEditor extends Component {
133135
}*/
134136

135137
onMessage(event) {
138+
const that = this;
139+
const {
140+
onFocus, onBlur, onChange, onPaste,
141+
onKeyUp, onKeyDown, onMessage,
142+
onCursorPosition,
143+
} = that.props;
136144
try {
137-
const props = this.props;
138145
const message = JSON.parse(event.nativeEvent.data);
139146
const data = message.data;
140147
switch (message.type) {
141148
case messages.CONTENT_HTML_RESPONSE:
142-
if (this.contentResolve) {
143-
this.contentResolve(message.data);
144-
this.contentResolve = undefined;
145-
this.contentReject = undefined;
146-
if (this.pendingContentHtml) {
147-
clearTimeout(this.pendingContentHtml);
148-
this.pendingContentHtml = undefined;
149+
if (that.contentResolve) {
150+
that.contentResolve(message.data);
151+
that.contentResolve = undefined;
152+
that.contentReject = undefined;
153+
if (that.pendingContentHtml) {
154+
clearTimeout(that.pendingContentHtml);
155+
that.pendingContentHtml = undefined;
149156
}
150157
}
151158
break;
152159
case messages.LOG:
153160
console.log('FROM EDIT:', ...data);
154161
break;
155-
case messages.SELECTION_CHANGE: {
162+
case messages.SELECTION_CHANGE:
156163
const items = message.data;
157-
this.selectionChangeListeners.map((listener) => {
164+
that.selectionChangeListeners.map((listener) => {
158165
listener(items);
159166
});
160167
break;
161-
}
162-
case messages.CONTENT_FOCUSED: {
163-
this._focus = true;
164-
this.focusListeners.map((da) => da()); // Subsequent versions will be deleted
165-
props.onFocus && props.onFocus();
168+
case messages.CONTENT_FOCUSED:
169+
that._focus = true;
170+
that.focusListeners.map((da) => da()); // Subsequent versions will be deleted
171+
onFocus?.();
166172
break;
167-
}
168-
case messages.CONTENT_BLUR: {
169-
this._focus = false;
170-
props.onBlur && props.onBlur();
173+
case messages.CONTENT_BLUR:
174+
that._focus = false;
175+
onBlur?.();
171176
break;
172-
}
173-
case messages.CONTENT_CHANGE: {
174-
props.onChange && props.onChange(data);
177+
case messages.CONTENT_CHANGE:
178+
onChange?.(data);
175179
break;
176-
}
177-
case messages.CONTENT_PASTED: {
178-
props.onPaste && props.onPaste(data);
180+
case messages.CONTENT_PASTED:
181+
onPaste?.(data);
179182
break;
180-
}
181-
case messages.CONTENT_KEYUP: {
182-
props.onKeyUp && props.onKeyUp(data);
183+
case messages.CONTENT_KEYUP:
184+
onKeyUp?.(data);
183185
break;
184-
}
185-
case messages.CONTENT_KEYDOWN: {
186-
props.onKeyDown && props.onKeyDown(data);
186+
case messages.CONTENT_KEYDOWN:
187+
onKeyDown?.(data);
187188
break;
188-
}
189189
case messages.OFFSET_HEIGHT:
190-
this.setWebHeight(data);
190+
that.setWebHeight(data);
191+
break;
192+
case messages.OFFSET_Y:
193+
let offsetY = Number.parseInt(Number.parseInt(data) + that.layout.y);
194+
offsetY > 0 && onCursorPosition(offsetY);
191195
break;
192196
default:
193-
props.onMessage && props.onMessage(message);
197+
onMessage?.(message);
194198
break;
195199
}
196200
} catch (e) {
197201
//alert('NON JSON MESSAGE');
198202
}
199203
}
200204

201-
setWebHeight = (height) => {
205+
setWebHeight (height) {
202206
// console.log(height);
203207
const {onHeightChange, useContainer} = this.props;
204208
if (height !== this.state.height) {
@@ -260,11 +264,16 @@ export default class RichTextEditor extends Component {
260264
opacity={opacity}
261265
onLoad={that.init}
262266
/>
263-
{Platform.OS === 'android' && <TextInput ref={(ref) => (that._input = ref)} style={styles._input} />}
267+
{Platform.OS === 'android' && <TextInput ref={(ref) => (that._input = ref)} style={styles._input}/>}
264268
</>
265269
);
266270
}
267271

272+
onViewLayout ({nativeEvent: { layout}}) {
273+
// const {x, y, width, height} = layout;
274+
this.layout = layout;
275+
}
276+
268277
render() {
269278
let {height} = this.state;
270279

@@ -273,7 +282,7 @@ export default class RichTextEditor extends Component {
273282
// If set to false, it will not use a View wrapper
274283
const {useContainer, style, initialHeight = 0} = this.props;
275284
return useContainer ? (
276-
<View style={[style, {height: height || initialHeight}]}>{this.renderWebView()}</View>
285+
<View style={[style, {height: height || initialHeight}]} onLayout={this.onViewLayout}>{this.renderWebView()}</View>
277286
) : (
278287
this.renderWebView()
279288
);
@@ -367,11 +376,11 @@ export default class RichTextEditor extends Component {
367376
this.sendAction(actions.fontSize, 'result', size);
368377
}
369378

370-
setForeColor (color){
379+
setForeColor(color) {
371380
this.sendAction(actions.foreColor, 'result', color);
372381
}
373382

374-
setHiliteColor (color) {
383+
setHiliteColor(color) {
375384
this.sendAction(actions.hiliteColor, 'result', color);
376385
}
377386

src/const.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ export const messages = {
7373
LINK_TOUCHED: 'LINK_TOUCHED',
7474
SELECTED_TEXT_CHANGED: 'SELECTED_TEXT_CHANGED',
7575
OFFSET_HEIGHT: 'OFFSET_HEIGHT',
76+
OFFSET_Y: 'OFFSET_Y',
7677
};

src/editor.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function createHTML(options = {}) {
4545
html, body { margin: 0; padding: 0;font-family: Arial, Helvetica, sans-serif; font-size:1em;}
4646
body { overflow-y: hidden; -webkit-overflow-scrolling: touch;height: 100%;background-color: ${backgroundColor};}
4747
.content {font-family: Arial, Helvetica, sans-serif;color: ${color}; width: 100%;height: 100%;-webkit-overflow-scrolling: touch;padding-left: 0;padding-right: 0;}
48-
.pell { height: 100%;background-color: red;} .pell-content { outline: 0; overflow-y: auto;padding: 10px;height: 100%;${contentCSSText}}
48+
.pell { height: 100%;} .pell-content { outline: 0; overflow-y: auto;padding: 10px;height: 100%;${contentCSSText}}
4949
</style>
5050
<style>
5151
[placeholder]:empty:before { content: attr(placeholder); color: ${placeholderColor};}
@@ -70,7 +70,7 @@ function createHTML(options = {}) {
7070
var body = document.body, docEle = document.documentElement;
7171
var defaultParagraphSeparatorString = 'defaultParagraphSeparator';
7272
var formatBlock = 'formatBlock';
73-
var editor = null, o_height = 0;
73+
var editor = null, editorFoucs = false, o_height = 0;
7474
function addEventListener(parent, type, listener) {
7575
return parent.addEventListener(type, listener);
7676
};
@@ -215,6 +215,8 @@ function createHTML(options = {}) {
215215
var _keyDown = false;
216216
function handleChange (event){
217217
var node = anchorNode;
218+
Actions.UPDATE_HEIGHT();
219+
Actions.UPDATE_OFFSET_Y();
218220
if (_keyDown){
219221
if(_checkboxFlag === 1 && checkboxNode(node)){
220222
_checkboxFlag = 0;
@@ -374,7 +376,7 @@ function createHTML(options = {}) {
374376
375377
init: function (){
376378
if (${useContainer}){
377-
setInterval(Actions.UPDATE_HEIGHT, 150);
379+
// setInterval(Actions.UPDATE_HEIGHT, 150);
378380
Actions.UPDATE_HEIGHT();
379381
} else {
380382
body.style.height = docEle.clientHeight + 'px';
@@ -388,6 +390,16 @@ function createHTML(options = {}) {
388390
if (o_height !== height){
389391
_postMessage({type: 'OFFSET_HEIGHT', data: o_height = height});
390392
}
393+
},
394+
395+
UPDATE_OFFSET_Y: function (){
396+
if (!${useContainer}) return;
397+
if (anchorNode){
398+
var offsetY = anchorNode.offsetTop || anchorNode.parentNode.offsetTop;
399+
if (offsetY){
400+
_postMessage({type: 'OFFSET_Y', data: offsetY});
401+
}
402+
}
391403
}
392404
};
393405
@@ -480,9 +492,11 @@ function createHTML(options = {}) {
480492
${keyDownListener} && postKeyAction(event, "CONTENT_KEYDOWN");
481493
}
482494
function handleFocus (){
495+
editorFoucs = true;
483496
postAction({type: 'CONTENT_FOCUSED'});
484497
}
485498
function handleBlur (){
499+
editorFoucs = false;
486500
postAction({type: 'SELECTION_CHANGE', data: []});
487501
postAction({type: 'CONTENT_BLUR'});
488502
}

0 commit comments

Comments
 (0)