Skip to content

Commit 771ff1d

Browse files
authoredApr 1, 2021
fix(text-input): make cursor reflect current position (#300)
1 parent 6e11c76 commit 771ff1d

File tree

2 files changed

+86
-4
lines changed

2 files changed

+86
-4
lines changed
 

‎lib/elements/text.js

+27-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class TextPrompt extends Prompt {
2525
this.value = ``;
2626
this.errorMsg = opts.error || `Please Enter A Valid Value`;
2727
this.cursor = Number(!!this.initial);
28+
this.cursorOffset = 0;
2829
this.clear = clear(``, this.out.columns);
2930
this.render();
3031
}
@@ -48,6 +49,7 @@ class TextPrompt extends Prompt {
4849
reset() {
4950
this.value = ``;
5051
this.cursor = Number(!!this.initial);
52+
this.cursorOffset = 0;
5153
this.fire();
5254
this.render();
5355
}
@@ -78,6 +80,8 @@ class TextPrompt extends Prompt {
7880

7981
async submit() {
8082
this.value = this.value || this.initial;
83+
this.cursorOffset = 0;
84+
this.cursor = this.rendered.length;
8185
await this.validate();
8286
if (this.error) {
8387
this.red = true;
@@ -104,6 +108,7 @@ class TextPrompt extends Prompt {
104108
moveCursor(n) {
105109
if (this.placeholder) return;
106110
this.cursor = this.cursor+n;
111+
this.cursorOffset += n;
107112
}
108113

109114
_(c, key) {
@@ -116,12 +121,17 @@ class TextPrompt extends Prompt {
116121
}
117122

118123
delete() {
119-
if (this.cursor === 0) return this.bell();
124+
if (this.isCursorAtStart()) return this.bell();
120125
let s1 = this.value.slice(0, this.cursor-1);
121126
let s2 = this.value.slice(this.cursor);
122127
this.value = `${s1}${s2}`;
123128
this.red = false;
124-
this.moveCursor(-1);
129+
if (this.isCursorAtStart()) {
130+
this.cursorOffset = 0
131+
} else {
132+
this.cursorOffset++;
133+
this.moveCursor(-1);
134+
}
125135
this.render();
126136
}
127137

@@ -131,6 +141,11 @@ class TextPrompt extends Prompt {
131141
let s2 = this.value.slice(this.cursor+1);
132142
this.value = `${s1}${s2}`;
133143
this.red = false;
144+
if (this.isCursorAtEnd()) {
145+
this.cursorOffset = 0;
146+
} else {
147+
this.cursorOffset++;
148+
}
134149
this.render();
135150
}
136151

@@ -156,6 +171,14 @@ class TextPrompt extends Prompt {
156171
this.render();
157172
}
158173

174+
isCursorAtStart() {
175+
return this.cursor === 0 || (this.placeholder && this.cursor === 1);
176+
}
177+
178+
isCursorAtEnd() {
179+
return this.cursor === this.rendered.length || (this.placeholder && this.cursor === this.rendered.length + 1)
180+
}
181+
159182
render() {
160183
if (this.closed) return;
161184
if (!this.firstRender) {
@@ -178,8 +201,8 @@ class TextPrompt extends Prompt {
178201
.reduce((a, l, i) => a + `\n${i ? ' ' : figures.pointerSmall} ${color.red().italic(l)}`, ``);
179202
}
180203

181-
this.out.write(erase.line + cursor.to(0) + this.outputText + cursor.save + this.outputError + cursor.restore);
204+
this.out.write(erase.line + cursor.to(0) + this.outputText + cursor.save + this.outputError + cursor.restore + cursor.move(this.cursorOffset, 0));
182205
}
183206
}
184207

185-
module.exports = TextPrompt;
208+
module.exports = TextPrompt;

‎test/text.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
3+
const test = require('tape');
4+
const TextPrompt = require('../lib/elements/text');
5+
6+
test('move', (t) => {
7+
t.plan(6);
8+
9+
const textPrompt = new TextPrompt();
10+
textPrompt.value = 'Hello, world!';
11+
textPrompt.last()
12+
textPrompt.render()
13+
14+
t.same(textPrompt.cursorOffset, 0, 'cursorOffset is 0 at start');
15+
t.same(textPrompt.cursor, textPrompt.rendered.length, 'cursor starts at 0')
16+
17+
textPrompt.right();
18+
t.same(textPrompt.cursorOffset, 0, 'cursorOffset is unaffected when moved right from the end');
19+
t.same(textPrompt.cursor, textPrompt.rendered.length, 'cursor is unaffected when moved right from the end')
20+
21+
textPrompt.left();
22+
t.equal(textPrompt.cursorOffset, -1, 'cursorOffset is -1 when moved left');
23+
24+
textPrompt.right();
25+
t.equal(textPrompt.cursorOffset, 0, 'cursorOffset is 0 when moved left');
26+
27+
t.end();
28+
});
29+
30+
test('delete', (t) => {
31+
t.plan(4);
32+
33+
const textPrompt = new TextPrompt();
34+
textPrompt.value = 'Hello, world!';
35+
textPrompt.last();
36+
textPrompt.render();
37+
38+
textPrompt.delete();
39+
t.same(textPrompt.cursorOffset, 0, 'cursorOffset is 0 after delete');
40+
t.same(textPrompt.cursor, textPrompt.rendered.length, 'cursor stays at end of line')
41+
42+
textPrompt.left();
43+
textPrompt.deleteForward()
44+
t.same(textPrompt.cursorOffset, 0, 'cursorOffset is 0 after deleteForward');
45+
t.same(textPrompt.cursor, textPrompt.rendered.length, 'cursor stays at end of line')
46+
47+
textPrompt.submit();
48+
t.end()
49+
});
50+
51+
test('submit', (t) => {
52+
t.plan(2)
53+
const textPrompt = new TextPrompt();
54+
textPrompt.value = 'Hello, world!';
55+
textPrompt.submit()
56+
57+
t.same(textPrompt.cursorOffset, 0, 'cursorOffset is reset on submit')
58+
t.same(textPrompt.cursor, textPrompt.rendered.length, 'cursor is reset to end of line on submit')
59+
})

0 commit comments

Comments
 (0)
Please sign in to comment.