minitel-standalone
Advanced tools
Comparing version 1.1.2 to 1.2.0
@@ -8,3 +8,3 @@ import { MinitelObjectAttributes } from '../types.js'; | ||
disabled: boolean; | ||
focusCursorAt?: [number, number]; | ||
cursorActuallyAt?: [number, number]; | ||
keepElmDesc: true; | ||
@@ -11,0 +11,0 @@ } |
@@ -14,8 +14,11 @@ import { Focusable, FocusableAttributes } from '../abstract/focusable.js'; | ||
keepElmDesc: true; | ||
focusCursorAt: [number, number]; | ||
cursorActuallyAt: [number, number]; | ||
scrollInternal: [number, number]; | ||
lastFocusCursorX: number; | ||
constructor(children: [], attributes: Partial<InputAttributes>, minitel: Minitel); | ||
constrainCursor(): void; | ||
keyEventListener(key: string): void; | ||
unmount(): void; | ||
render(attributes: InputAttributes, inheritMe: Partial<InputAttributes>): RichCharGrid; | ||
get focusCursorAt(): number[]; | ||
} | ||
@@ -22,0 +25,0 @@ export interface InputAttributes extends FocusableAttributes { |
@@ -15,6 +15,14 @@ "use strict"; | ||
this.keepElmDesc = true; | ||
this.focusCursorAt = [0, 0]; | ||
this.cursorActuallyAt = [0, 0]; | ||
this.scrollInternal = [0, 0]; | ||
this.lastFocusCursorX = 0; | ||
this.on('key', this.keyEventListener); | ||
} | ||
constrainCursor() { | ||
this.cursorActuallyAt[0] = Math.min(this.cursorActuallyAt[0], this.value.split('\n').length - 1); | ||
this.cursorActuallyAt[0] = Math.max(this.cursorActuallyAt[0], 0); | ||
const currentLine = this.value.split('\n')[this.cursorActuallyAt[0]]; | ||
this.cursorActuallyAt[1] = Math.min(this.lastFocusCursorX, currentLine.length); | ||
this.cursorActuallyAt[1] = Math.max(this.cursorActuallyAt[1], 0); | ||
} | ||
keyEventListener(key) { | ||
@@ -25,6 +33,4 @@ let currentLine; | ||
if (this.attributes.multiline) { | ||
this.focusCursorAt[0] -= 1; | ||
this.focusCursorAt[0] = Math.max(this.focusCursorAt[0], 0); | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
this.focusCursorAt[1] = Math.min(this.lastFocusCursorX, currentLine.length); | ||
this.cursorActuallyAt[0] -= 1; | ||
this.constrainCursor(); | ||
this.minitel.queueImmediateRenderToStream(); | ||
@@ -35,6 +41,4 @@ } | ||
if (this.attributes.multiline) { | ||
this.focusCursorAt[0] += 1; | ||
this.focusCursorAt[0] = Math.min(this.focusCursorAt[1], this.value.split('\n').length); | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
this.focusCursorAt[1] = Math.min(this.lastFocusCursorX, currentLine.length); | ||
this.cursorActuallyAt[0] += 1; | ||
this.constrainCursor(); | ||
this.minitel.queueImmediateRenderToStream(); | ||
@@ -44,26 +48,41 @@ } | ||
case '\x1b\x5b\x43': // right | ||
this.focusCursorAt[1] += 1; | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
if (this.focusCursorAt[1] > currentLine.length) { | ||
this.focusCursorAt[0] += 1; | ||
this.focusCursorAt[0] = Math.min(this.focusCursorAt[0], this.value.split('\n').length); | ||
this.focusCursorAt[1] = 0; | ||
this.cursorActuallyAt[1] += 1; | ||
currentLine = this.value.split('\n')[this.cursorActuallyAt[0]]; | ||
if (this.cursorActuallyAt[1] > currentLine.length) { | ||
if (this.cursorActuallyAt[0] < this.value.split('\n').length - 1) { | ||
this.cursorActuallyAt[0] += 1; | ||
this.cursorActuallyAt[1] = 0; | ||
} | ||
} | ||
this.lastFocusCursorX = this.focusCursorAt[1]; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
this.constrainCursor(); | ||
this.minitel.queueImmediateRenderToStream(); | ||
break; | ||
case '\x1b\x5b\x44': // left | ||
this.focusCursorAt[1] -= 1; | ||
if (this.focusCursorAt[1] < 0) { | ||
this.focusCursorAt[0] -= 1; | ||
this.focusCursorAt[0] = Math.max(this.focusCursorAt[0], 0); | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
this.focusCursorAt[1] = currentLine.length; | ||
this.cursorActuallyAt[1] -= 1; | ||
if (this.cursorActuallyAt[1] < 0) { | ||
if (this.cursorActuallyAt[0] > 0) { | ||
this.cursorActuallyAt[0] -= 1; | ||
currentLine = this.value.split('\n')[this.cursorActuallyAt[0]]; | ||
this.cursorActuallyAt[1] = currentLine.length; | ||
} | ||
} | ||
this.lastFocusCursorX = this.focusCursorAt[1]; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
this.constrainCursor(); | ||
this.minitel.queueImmediateRenderToStream(); | ||
break; | ||
default: | ||
if (/^[a-zA-Z0-9,\.';\-\:?!"#$%&\(\)\[\]<>@+=*/ ]$/g.test(key)) { | ||
this.value += key; | ||
if (/^[a-zA-Z0-9,\.';\-\:?!"#$%&\(\)\[\]<>@+=*/\x0d ]$/g.test(key)) { | ||
this.cursorActuallyAt[1] += 1; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
const lines = this.value.split('\n'); | ||
let cumulPosition = lines.filter((_, i) => i < this.cursorActuallyAt[0] - 1).reduce((p, v) => p + v.length + 1, 0); | ||
cumulPosition += this.cursorActuallyAt[1]; | ||
const chars = this.value.split(''); | ||
chars.splice(cumulPosition, 0, key === '\x0d' ? '\n' : key); | ||
if (key === '\x0d') { | ||
this.lastFocusCursorX = this.cursorActuallyAt[1] = 0; | ||
this.cursorActuallyAt[0] += 1; | ||
} | ||
this.value = chars.join(''); | ||
if (this.attributes.onChange) | ||
@@ -73,6 +92,16 @@ this.attributes.onChange(this); | ||
else if (key === '\x13\x47') { | ||
this.value = this.value.slice(0, -1); | ||
if (this.cursorActuallyAt[0] !== 0 || this.cursorActuallyAt[1] !== 0) { | ||
this.cursorActuallyAt[1] -= 1; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
const lines = this.value.split('\n'); | ||
let cumulPosition = lines.filter((_, i) => i < this.cursorActuallyAt[0] - 1).reduce((p, v) => p + v.length + 1, 0); | ||
cumulPosition += this.cursorActuallyAt[1]; | ||
const chars = this.value.split(''); | ||
chars.splice(cumulPosition, 1); | ||
this.value = chars.join(''); | ||
} | ||
if (this.attributes.onChange) | ||
this.attributes.onChange(this); | ||
} | ||
this.constrainCursor(); | ||
this.minitel.queueImmediateRenderToStream(); | ||
@@ -87,9 +116,40 @@ } | ||
// TODO: fix types | ||
return richchargrid_js_1.RichCharGrid.fromLine({ | ||
text: this.value, | ||
password: '-'.repeat(this.value.length), | ||
}[attributes.type].slice(-attributes.width), inheritMe).setWidth(attributes.width, 'end', fillChar); | ||
const lines = { | ||
text: this.value.split('\n'), | ||
password: this.value.split('\n').map((line) => '-'.repeat(line.length)), | ||
}[attributes.type]; | ||
const result = new richchargrid_js_1.RichCharGrid([]); | ||
const concreteWidth = Math.max(...lines.map((v) => v.length)); | ||
for (let line of lines) { | ||
result.mergeY(richchargrid_js_1.RichCharGrid.fromLine(line, attributes).setWidth(concreteWidth, attributes.textAlign, fillChar)); | ||
} | ||
if (attributes.height != null) { | ||
if (this.scrollInternal[0] > this.cursorActuallyAt[0]) { | ||
this.scrollInternal[0] = this.cursorActuallyAt[0]; | ||
} | ||
if (this.scrollInternal[0] + attributes.height < this.cursorActuallyAt[0]) { | ||
this.scrollInternal[0] = this.cursorActuallyAt[0] - attributes.height; | ||
} | ||
this.scrollInternal[0] = Math.min(Math.max(this.scrollInternal[0], 0), lines.length); | ||
result.setHeight(this.scrollInternal[0] + attributes.height, 'end', fillChar); | ||
result.setHeight(attributes.height, 'start', fillChar); | ||
} | ||
if (attributes.width != null) { | ||
if (this.scrollInternal[1] > this.cursorActuallyAt[1] - 4) { | ||
this.scrollInternal[1] = this.cursorActuallyAt[1] - 4; | ||
} | ||
if (this.scrollInternal[1] + attributes.width < this.cursorActuallyAt[1] + 2) { | ||
this.scrollInternal[1] = this.cursorActuallyAt[1] - attributes.width + 2; | ||
} | ||
this.scrollInternal[1] = Math.min(Math.max(this.scrollInternal[1], 0), lines[this.cursorActuallyAt[0]].length); | ||
result.setWidth(this.scrollInternal[1] + attributes.width, 'end', fillChar); | ||
result.setWidth(attributes.width, 'start', fillChar); | ||
} | ||
return result; | ||
} | ||
get focusCursorAt() { | ||
return [this.cursorActuallyAt[0] - this.scrollInternal[0], this.cursorActuallyAt[1] - this.scrollInternal[1]]; | ||
} | ||
} | ||
exports.Input = Input; | ||
Input.defaultAttributes = Object.assign(Object.assign({}, minitelobject_js_1.MinitelObject.defaultAttributes), { fillChar: '.', width: 8, height: 1, type: 'text', autofocus: false, multiline: false, onChange: () => { } }); |
@@ -124,5 +124,5 @@ "use strict"; | ||
const locationDescriptor = renderGrid.locationDescriptors.get(this.focusedObj); | ||
if (locationDescriptor && 'focusCursorAt' in this.focusedObj && this.focusedObj.focusCursorAt != null) { | ||
if (locationDescriptor && 'focusCursorAt' in this.focusedObj && this.focusedObj.cursorActuallyAt != null) { | ||
const { x, y, w, h } = locationDescriptor; | ||
const [cursorDeltaY, cursorDeltaX] = this.focusedObj.focusCursorAt; | ||
const [cursorDeltaY, cursorDeltaX] = this.focusedObj.cursorActuallyAt; | ||
outputString.push(this.toCursorMove(Math.min(y + cursorDeltaY, y + h - 1), Math.min(x + cursorDeltaX, x + w))); | ||
@@ -129,0 +129,0 @@ outputString.push('\x11'); |
{ | ||
"name": "minitel-standalone", | ||
"version": "1.1.2", | ||
"version": "1.2.0", | ||
"description": "A standalone package for minitel components", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -7,3 +7,3 @@ import { MinitelObjectAttributes } from '../types.js'; | ||
disabled: boolean; | ||
focusCursorAt?: [number, number]; | ||
cursorActuallyAt?: [number, number]; | ||
keepElmDesc: true; | ||
@@ -10,0 +10,0 @@ } |
@@ -26,3 +26,4 @@ import { Focusable, FocusableAttributes } from '../abstract/focusable.js'; | ||
keepElmDesc = true as const; | ||
focusCursorAt = [0, 0] as [number, number]; | ||
cursorActuallyAt = [0, 0] as [number, number]; | ||
scrollInternal = [0, 0] as [number, number]; | ||
lastFocusCursorX = 0; | ||
@@ -38,2 +39,16 @@ constructor( | ||
} | ||
constrainCursor() { | ||
this.cursorActuallyAt[0] = Math.min( | ||
this.cursorActuallyAt[0], | ||
this.value.split('\n').length - 1, | ||
); | ||
this.cursorActuallyAt[0] = Math.max(this.cursorActuallyAt[0], 0); | ||
const currentLine = this.value.split('\n')[this.cursorActuallyAt[0]]; | ||
this.cursorActuallyAt[1] = Math.min( | ||
this.lastFocusCursorX, | ||
currentLine.length, | ||
); | ||
this.cursorActuallyAt[1] = Math.max(this.cursorActuallyAt[1], 0); | ||
} | ||
keyEventListener(key: string) { | ||
@@ -44,10 +59,12 @@ let currentLine: string; | ||
if (this.attributes.multiline) { | ||
this.focusCursorAt[0] -= 1; | ||
this.focusCursorAt[0] = Math.max(this.focusCursorAt[0], 0); | ||
this.cursorActuallyAt[0] -= 1; | ||
this.constrainCursor(); | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
this.focusCursorAt[1] = Math.min( | ||
this.lastFocusCursorX, | ||
currentLine.length, | ||
); | ||
this.minitel.queueImmediateRenderToStream(); | ||
} | ||
break; | ||
case '\x1b\x5b\x42': // down | ||
if (this.attributes.multiline) { | ||
this.cursorActuallyAt[0] += 1; | ||
this.constrainCursor(); | ||
@@ -57,32 +74,14 @@ this.minitel.queueImmediateRenderToStream(); | ||
break; | ||
case '\x1b\x5b\x42': // down | ||
if (this.attributes.multiline) { | ||
this.focusCursorAt[0] += 1; | ||
this.focusCursorAt[0] = Math.min( | ||
this.focusCursorAt[1], | ||
this.value.split('\n').length, | ||
); | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
this.focusCursorAt[1] = Math.min( | ||
this.lastFocusCursorX, | ||
currentLine.length, | ||
); | ||
this.minitel.queueImmediateRenderToStream(); | ||
} | ||
break; | ||
case '\x1b\x5b\x43': // right | ||
this.focusCursorAt[1] += 1; | ||
this.cursorActuallyAt[1] += 1; | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
if (this.focusCursorAt[1] > currentLine.length) { | ||
this.focusCursorAt[0] += 1; | ||
this.focusCursorAt[0] = Math.min( | ||
this.focusCursorAt[0], | ||
this.value.split('\n').length, | ||
); | ||
this.focusCursorAt[1] = 0; | ||
currentLine = this.value.split('\n')[this.cursorActuallyAt[0]]; | ||
if (this.cursorActuallyAt[1] > currentLine.length) { | ||
if (this.cursorActuallyAt[0] < this.value.split('\n').length - 1) { | ||
this.cursorActuallyAt[0] += 1; | ||
this.cursorActuallyAt[1] = 0; | ||
} | ||
} | ||
this.lastFocusCursorX = this.focusCursorAt[1]; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
this.constrainCursor(); | ||
@@ -92,11 +91,13 @@ this.minitel.queueImmediateRenderToStream(); | ||
case '\x1b\x5b\x44': // left | ||
this.focusCursorAt[1] -= 1; | ||
this.cursorActuallyAt[1] -= 1; | ||
if (this.focusCursorAt[1] < 0) { | ||
this.focusCursorAt[0] -= 1; | ||
this.focusCursorAt[0] = Math.max(this.focusCursorAt[0], 0); | ||
currentLine = this.value.split('\n')[this.focusCursorAt[0]]; | ||
this.focusCursorAt[1] = currentLine.length; | ||
if (this.cursorActuallyAt[1] < 0) { | ||
if (this.cursorActuallyAt[0] > 0) { | ||
this.cursorActuallyAt[0] -= 1; | ||
currentLine = this.value.split('\n')[this.cursorActuallyAt[0]]; | ||
this.cursorActuallyAt[1] = currentLine.length; | ||
} | ||
} | ||
this.lastFocusCursorX = this.focusCursorAt[1]; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
this.constrainCursor(); | ||
@@ -106,28 +107,85 @@ this.minitel.queueImmediateRenderToStream(); | ||
default: | ||
if (/^[a-zA-Z0-9,\.';\-\:?!"#$%&\(\)\[\]<>@+=*/ ]$/g.test(key)) { | ||
this.value += key; | ||
if (/^[a-zA-Z0-9,\.';\-\:?!"#$%&\(\)\[\]<>@+=*/\x0d ]$/g.test(key)) { | ||
this.cursorActuallyAt[1] += 1; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
const lines = this.value.split('\n'); | ||
let cumulPosition = lines.filter((_, i) => i < this.cursorActuallyAt[0] - 1).reduce((p, v) => p + v.length + 1, 0); | ||
cumulPosition += this.cursorActuallyAt[1]; | ||
const chars = this.value.split(''); | ||
chars.splice(cumulPosition, 0, key === '\x0d' ? '\n' : key); | ||
if (key === '\x0d') { | ||
this.lastFocusCursorX = this.cursorActuallyAt[1] = 0; | ||
this.cursorActuallyAt[0] += 1; | ||
} | ||
this.value = chars.join(''); | ||
if (this.attributes.onChange) this.attributes.onChange(this); | ||
} else if (key === '\x13\x47') { | ||
this.value = this.value.slice(0, -1); | ||
if (this.cursorActuallyAt[0] !== 0 || this.cursorActuallyAt[1] !== 0) { | ||
this.cursorActuallyAt[1] -= 1; | ||
this.lastFocusCursorX = this.cursorActuallyAt[1]; | ||
const lines = this.value.split('\n'); | ||
let cumulPosition = lines.filter((_, i) => i < this.cursorActuallyAt[0] - 1).reduce((p, v) => p + v.length + 1, 0); | ||
cumulPosition += this.cursorActuallyAt[1]; | ||
const chars = this.value.split(''); | ||
chars.splice(cumulPosition, 1); | ||
this.value = chars.join(''); | ||
} | ||
if (this.attributes.onChange) this.attributes.onChange(this); | ||
} | ||
this.constrainCursor(); | ||
this.minitel.queueImmediateRenderToStream(); | ||
} | ||
} | ||
unmount() { | ||
this.off('key', this.keyEventListener); | ||
} | ||
render(attributes: InputAttributes, inheritMe: Partial<InputAttributes>) { | ||
const fillChar = new RichChar(attributes.fillChar, attributes).noSize(); | ||
// TODO: fix types | ||
const lines = { | ||
text: this.value.split('\n'), | ||
password: this.value.split('\n').map((line) => '-'.repeat(line.length)), | ||
}[attributes.type]; | ||
const result = new RichCharGrid([]); | ||
const concreteWidth = Math.max(...lines.map((v) => v.length)); | ||
for (let line of lines) { | ||
result.mergeY(RichCharGrid.fromLine(line, attributes).setWidth(concreteWidth, attributes.textAlign, fillChar)); | ||
} | ||
if (attributes.height != null) { | ||
if (this.scrollInternal[0] > this.cursorActuallyAt[0]) { | ||
this.scrollInternal[0] = this.cursorActuallyAt[0]; | ||
} | ||
if (this.scrollInternal[0] + attributes.height < this.cursorActuallyAt[0]) { | ||
this.scrollInternal[0] = this.cursorActuallyAt[0] - attributes.height; | ||
} | ||
this.scrollInternal[0] = Math.min(Math.max(this.scrollInternal[0], 0), lines.length); | ||
result.setHeight(this.scrollInternal[0] + attributes.height, 'end', fillChar); | ||
result.setHeight(attributes.height, 'start', fillChar); | ||
} | ||
if (attributes.width != null) { | ||
if (this.scrollInternal[1] > this.cursorActuallyAt[1] - 4) { | ||
this.scrollInternal[1] = this.cursorActuallyAt[1] - 4; | ||
} | ||
if (this.scrollInternal[1] + attributes.width < this.cursorActuallyAt[1] + 2) { | ||
this.scrollInternal[1] = this.cursorActuallyAt[1] - attributes.width + 2; | ||
} | ||
this.scrollInternal[1] = Math.min(Math.max(this.scrollInternal[1], 0), lines[this.cursorActuallyAt[0]].length); | ||
result.setWidth(this.scrollInternal[1] + attributes.width, 'end', fillChar); | ||
result.setWidth(attributes.width, 'start', fillChar); | ||
} | ||
return result; | ||
} | ||
get focusCursorAt() { | ||
return [this.cursorActuallyAt[0] - this.scrollInternal[0], this.cursorActuallyAt[1] - this.scrollInternal[1]]; | ||
} | ||
} | ||
unmount() { | ||
this.off('key', this.keyEventListener); | ||
} | ||
render(attributes: InputAttributes, inheritMe: Partial<InputAttributes>) { | ||
const fillChar = new RichChar(attributes.fillChar, attributes).noSize(); | ||
// TODO: fix types | ||
return RichCharGrid.fromLine( | ||
{ | ||
text: this.value, | ||
password: '-'.repeat(this.value.length), | ||
}[attributes.type].slice(-attributes.width!), | ||
inheritMe, | ||
).setWidth(attributes.width!, 'end', fillChar); | ||
} | ||
} | ||
@@ -134,0 +192,0 @@ export interface InputAttributes extends FocusableAttributes { |
@@ -180,5 +180,5 @@ import { Duplex } from 'stream'; | ||
const locationDescriptor = renderGrid.locationDescriptors.get(this.focusedObj); | ||
if (locationDescriptor && 'focusCursorAt' in this.focusedObj && this.focusedObj.focusCursorAt != null) { | ||
if (locationDescriptor && 'focusCursorAt' in this.focusedObj && this.focusedObj.cursorActuallyAt != null) { | ||
const { x, y, w, h } = locationDescriptor; | ||
const [cursorDeltaY, cursorDeltaX] = this.focusedObj.focusCursorAt; | ||
const [cursorDeltaY, cursorDeltaX] = this.focusedObj.cursorActuallyAt; | ||
@@ -185,0 +185,0 @@ outputString.push(this.toCursorMove( |
178507
3821