UNPKG

asciitorium

Version:
271 lines (270 loc) 9.89 kB
import { Component } from '../core/Component.js'; class SliderBase extends Component { constructor(options, defaultWidth, defaultHeight) { super({ ...options, width: options.width ?? options.style?.width ?? defaultWidth, height: options.height ?? options.style?.height ?? defaultHeight, border: options.border ?? options.style?.border ?? false }); this.focusable = true; this.hasFocus = false; this.valueState = options.value; this.min = options.min ?? 0; this.max = options.max ?? 100; this.step = options.step ?? 1; this.focusable = !options.readonly; this.bind(this.valueState, () => { }); } clampValue(value) { return Math.max(this.min, Math.min(this.max, value)); } incrementValue() { const newValue = this.clampValue(this.valueState.value + this.step); this.valueState.value = newValue; } decrementValue() { const newValue = this.clampValue(this.valueState.value - this.step); this.valueState.value = newValue; } initializeBuffer() { this.buffer = Array.from({ length: this.height }, () => Array.from({ length: this.width }, () => this.transparentChar)); } drawChar(x, y, char) { if (x >= 0 && x < this.width && y >= 0 && y < this.height) { this.buffer[y][x] = char; } } calculateNormalizedValue() { const range = this.max - this.min; return (this.valueState.value - this.min) / range; } } export class ProgressBarSlider extends SliderBase { constructor(options) { super(options, 'fill', 3); } handleEvent(event) { const prevValue = this.valueState.value; if (event === 'ArrowRight' || event === 'd') { this.incrementValue(); } else if (event === 'ArrowLeft' || event === 'a') { this.decrementValue(); } else { return false; } return this.valueState.value !== prevValue; } draw() { this.initializeBuffer(); const innerWidth = this.width - 2; const normalizedValue = this.calculateNormalizedValue(); const filledLength = Math.round(normalizedValue * innerWidth); const emptyLength = Math.max(0, innerWidth - filledLength); let barContent = '█'.repeat(Math.max(0, filledLength)) + ' '.repeat(Math.max(0, emptyLength)); // Top border this.drawChar(0, 0, '╭'); for (let x = 1; x < this.width - 1; x++) { this.drawChar(x, 0, '─'); } this.drawChar(this.width - 1, 0, '╮'); // Middle with content this.drawChar(0, 1, '│'); for (let x = 0; x < barContent.length && x < innerWidth; x++) { const char = this.hasFocus && x === filledLength - 1 && filledLength > 0 ? '▓' : barContent[x]; this.drawChar(x + 1, 1, char); } this.drawChar(this.width - 1, 1, '│'); // Bottom border this.drawChar(0, 2, '╰'); for (let x = 1; x < this.width - 1; x++) { this.drawChar(x, 2, '─'); } this.drawChar(this.width - 1, 2, '╯'); // Draw hotkey indicator at position (1, 0) if hotkey visibility is on if (this.hotkey && this.isHotkeyVisibilityEnabled()) { const hotkeyDisplay = `[${this.hotkey.toUpperCase()}]`; for (let i = 0; i < hotkeyDisplay.length && i + 1 < this.width - 1; i++) { this.drawChar(i + 1, 0, hotkeyDisplay[i]); } } return this.buffer; } } export class GaugeSlider extends SliderBase { constructor(options) { super(options, 'fill', 1); } handleEvent(event) { const prevValue = this.valueState.value; if (event === 'ArrowRight' || event === 'd') { this.incrementValue(); } else if (event === 'ArrowLeft' || event === 'a') { this.decrementValue(); } else { return false; } return this.valueState.value !== prevValue; } draw() { this.initializeBuffer(); const trackWidth = this.width - 2; const normalizedValue = this.calculateNormalizedValue(); const indicatorPosition = Math.round(normalizedValue * (trackWidth - 1)); const y = Math.floor(this.height / 2); this.drawChar(0, y, '├'); for (let x = 1; x < this.width - 1; x++) { const trackPos = x - 1; if (trackPos === indicatorPosition) { this.drawChar(x, y, this.hasFocus ? '◆' : '◇'); } else { this.drawChar(x, y, '─'); } } this.drawChar(this.width - 1, y, '┤'); // Draw hotkey indicator at position (1, 0) if hotkey visibility is on if (this.hotkey && this.isHotkeyVisibilityEnabled()) { const hotkeyDisplay = `[${this.hotkey.toUpperCase()}]`; for (let i = 0; i < hotkeyDisplay.length && i + 1 < this.width - 1; i++) { this.drawChar(i, 0, hotkeyDisplay[i]); } } return this.buffer; } } export class DotSlider extends SliderBase { constructor(options) { super(options, 'fill', 1); } handleEvent(event) { const prevValue = this.valueState.value; if (event === 'ArrowRight' || event === 'd') { this.incrementValue(); } else if (event === 'ArrowLeft' || event === 'a') { this.decrementValue(); } else { return false; } return this.valueState.value !== prevValue; } draw() { this.initializeBuffer(); const dotCount = Math.floor(this.width / 2); const normalizedValue = this.calculateNormalizedValue(); const activeDots = Math.round(normalizedValue * dotCount); const y = Math.floor(this.height / 2); for (let i = 0; i < dotCount; i++) { const x = i * 2; if (i < activeDots) { this.drawChar(x, y, this.hasFocus ? '◆' : '◆'); } else { this.drawChar(x, y, '◇'); } } // Draw hotkey indicator at position (1, 0) if hotkey visibility is on if (this.hotkey && this.isHotkeyVisibilityEnabled()) { const hotkeyDisplay = `[${this.hotkey.toUpperCase()}]`; for (let i = 0; i < hotkeyDisplay.length && i + 1 < this.width - 1; i++) { this.drawChar(i, 0, hotkeyDisplay[i]); } } return this.buffer; } } export class BarSlider extends SliderBase { constructor(options) { super(options, 'fill', 1); } handleEvent(event) { const prevValue = this.valueState.value; if (event === 'ArrowRight' || event === 'd') { this.incrementValue(); } else if (event === 'ArrowLeft' || event === 'a') { this.decrementValue(); } else { return false; } return this.valueState.value !== prevValue; } draw() { this.initializeBuffer(); const normalizedValue = this.calculateNormalizedValue(); const filledLength = Math.round(normalizedValue * this.width); const y = Math.floor(this.height / 2); // Draw filled portion with █ for (let x = 0; x < filledLength; x++) { this.drawChar(x, y, '█'); } // Draw empty portion with ▒ for (let x = filledLength; x < this.width; x++) { this.drawChar(x, y, '▒'); } // Draw hotkey indicator at position (0, 0) if hotkey visibility is on if (this.hotkey && this.isHotkeyVisibilityEnabled()) { const hotkeyDisplay = `[${this.hotkey.toUpperCase()}]`; for (let i = 0; i < hotkeyDisplay.length && i < this.width; i++) { this.drawChar(i, 0, hotkeyDisplay[i]); } } return this.buffer; } } export class VerticalSlider extends SliderBase { constructor(options) { super(options, 3, 'fill'); } handleEvent(event) { const prevValue = this.valueState.value; if (event === 'ArrowUp') { this.incrementValue(); } else if (event === 'ArrowDown') { this.decrementValue(); } else { return false; } return this.valueState.value !== prevValue; } draw() { this.initializeBuffer(); const trackHeight = this.height - 2; const normalizedValue = this.calculateNormalizedValue(); const filledHeight = Math.round(normalizedValue * trackHeight); this.drawChar(0, 0, '┌'); this.drawChar(1, 0, '─'); this.drawChar(2, 0, '┐'); for (let y = 1; y < this.height - 1; y++) { this.drawChar(0, y, '│'); this.drawChar(2, y, '│'); const trackPos = trackHeight - (y - 1); if (trackPos <= filledHeight) { this.drawChar(1, y, this.hasFocus ? '█' : '▓'); } else { this.drawChar(1, y, '░'); } } this.drawChar(0, this.height - 1, '└'); this.drawChar(1, this.height - 1, '─'); this.drawChar(2, this.height - 1, '┘'); // Draw hotkey indicator at position (0, 0) if hotkey visibility is on if (this.hotkey && this.isHotkeyVisibilityEnabled()) { const hotkeyDisplay = `[${this.hotkey.toUpperCase()}]`; for (let i = 0; i < hotkeyDisplay.length && i < this.width; i++) { this.drawChar(i, 0, hotkeyDisplay[i]); } } return this.buffer; } }