« get me outta code hell

TextInput.js « form « ui - tui-lib - Pure Node.js library for making visual command-line programs (ala vim, ncdu)
about summary refs log tree commit diff
path: root/ui/form/TextInput.js
blob: fc59cbbce097facb49b422151d8c9e4124311ccf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
const ansi = require('../../util/ansi')
const unic = require('../../util/unichars')
const telc = require('../../util/telchars')

const FocusElement = require('./FocusElement')

module.exports = class TextInput extends FocusElement {
  // An element that the user can type in.

  constructor() {
    super()

    this.value = ''
    this.cursorIndex = 0
    this.scrollChars = 0
  }

  drawTo(writable) {
    // There should be room for the cursor so move the "right edge" left a
    // single character.

    const startRange = this.scrollChars
    const endRange = this.scrollChars + this.w - 3

    let str = this.value.slice(startRange, endRange)

    writable.write(ansi.moveCursor(this.absTop, this.absLeft + 1))
    writable.write(str)

    // Ellipsis on left side, if there's more characters behind the visible
    // area.
    if (startRange > 0) {
      writable.write(ansi.moveCursor(this.absTop, this.absLeft))
      writable.write(unic.ELLIPSIS)
    }

    // Ellipsis on the right side, if there's more characters ahead of the
    // visible area.
    if (endRange < this.value.length) {
      writable.write(ansi.moveCursor(this.absTop, this.absRight - 1))
      writable.write(unic.ELLIPSIS.repeat(2))
    }

    this.cursorX = this.cursorIndex - this.scrollChars + 1

    super.drawTo(writable)
  }

  keyPressed(keyBuf) {
    if (keyBuf[0] === 127) {
      this.value = (
        this.value.slice(0, this.cursorIndex - 1) +
        this.value.slice(this.cursorIndex)
      )
      this.cursorIndex--
      this.root.cursorMoved()
    } else if (keyBuf[0] === 13) {
      this.emit('value', this.value)
    } else if (keyBuf[0] === 0x1b && keyBuf[1] === 0x5b) {
      // Keyboard navigation
      if (keyBuf[2] === 0x44) {
        this.cursorIndex--
        this.root.cursorMoved()
      } else if (keyBuf[2] === 0x43) {
        this.cursorIndex++
        this.root.cursorMoved()
      }
    } else if (telc.isEscape(keyBuf)) {
      // ESC is bad and we don't want that in the text input!
      return
    } else {
      // console.log(keyBuf, keyBuf[0], keyBuf[1], keyBuf[2])
      this.value = (
        this.value.slice(0, this.cursorIndex) + keyBuf.toString() +
        this.value.slice(this.cursorIndex)
      )
      this.cursorIndex++
      this.root.cursorMoved()
    }

    this.keepCursorInRange()
  }

  keepCursorInRange() {
    // Keep the cursor inside or at the end of the input value.

    if (this.cursorIndex < 0) {
      this.cursorIndex = 0
    }

    if (this.cursorIndex > this.value.length) {
      this.cursorIndex = this.value.length
    }

    // Scroll right, if the cursor is past the right edge of where text is
    // displayed.
    if (this.cursorIndex - this.scrollChars > this.w - 3) {
      this.scrollChars++
    }

    // Scroll left, if the cursor is behind the left edge of where text is
    // displayed.
    if (this.cursorIndex - this.scrollChars < 0) {
      this.scrollChars--
    }

    // Scroll left, if we can see past the end of the text.
    if (this.scrollChars > 0 && (
      this.scrollChars + this.w - 3 > this.value.length)
    ) {
      this.scrollChars--
    }
  }
}