UNPKG

@teaui/core

Version:

A high-level terminal UI library for Node

151 lines 4.87 kB
import { Point, Size } from '../geometry.js'; import { Style } from '../Style.js'; import { View } from '../View.js'; import { isMouseClicked, } from '../events/index.js'; export class Toggle extends View { #value = false; #sliderLocation; #onChange; constructor(props = {}) { super(props); this.#update(props); this.#sliderLocation = this.#targetLocation(); } get value() { return this.#value; } set value(value) { if (value === this.#value) { return; } this.#value = value; this.invalidateRender(); } update(props) { this.#update(props); super.update(props); } #update({ value, onChange }) { if (value !== undefined) { this.#value = value; } this.#onChange = onChange; } naturalSize(_available) { return TOGGLE_SIZE; } receiveKey(event) { switch (event.name) { case 'return': case 'space': this.#toggle(); break; } } receiveMouse(event, system) { super.receiveMouse(event, system); if (isMouseClicked(event)) { this.#toggle(); } } receiveTick(dt) { const target = this.#targetLocation(); this.#sliderLocation ??= target; if (this.#sliderLocation === target) { return false; } const dx = dt / ANIMATION_MS; if (target > this.#sliderLocation) { this.#sliderLocation = Math.min(target, this.#sliderLocation + dx); } else { this.#sliderLocation = Math.max(target, this.#sliderLocation - dx); } return true; } render(viewport) { viewport.registerFocus({ isDefault: false }); viewport.registerMouse(['mouse.button.left', 'mouse.move']); viewport.registerTick(); if (viewport.isEmpty) { return; } const sliderX = this.#sliderX(viewport.contentSize.height); const trackStyle = this.#trackStyle(); const sliderStyle = this.#sliderStyle(); if (viewport.contentSize.height <= COMPACT_HEIGHT) { this.#renderCompact(viewport, sliderX, trackStyle, sliderStyle); } else { this.#renderNormal(viewport, sliderX, trackStyle, sliderStyle); } } #toggle() { this.value = !this.#value; this.#onChange?.(this.#value); } #renderNormal(viewport, sliderX, trackStyle, sliderStyle) { viewport.write(TRACK_TOP, Point.zero, trackStyle); viewport.write(TRACK_BOTTOM, new Point(0, 1), trackStyle); viewport.write(this.#sliderTop(), new Point(sliderX, 0), sliderStyle); viewport.write(this.#sliderBottom(), new Point(sliderX, 1), sliderStyle); } #renderCompact(viewport, sliderX, trackStyle, sliderStyle) { if (sliderX <= COMPACT_SLIDER_RANGE[0]) { viewport.write(COMPACT_TRACK, new Point(COMPACT_TRACK_OFF_X, 0), trackStyle); } else { viewport.write(COMPACT_TRACK, Point.zero, trackStyle); } viewport.write(this.#compactSlider(), new Point(sliderX, 0), sliderStyle); } #sliderX(height) { const location = this.#sliderLocation ?? this.#targetLocation(); const [left, right] = height <= COMPACT_HEIGHT ? COMPACT_SLIDER_RANGE : SLIDER_RANGE; return Math.round(left + (right - left) * location); } #targetLocation() { return this.#value ? ON_LOCATION : OFF_LOCATION; } #sliderTop() { return this.#value ? SLIDER_ON_TOP : SLIDER_OFF_TOP; } #sliderBottom() { return this.#value ? SLIDER_ON_BOTTOM : SLIDER_OFF_BOTTOM; } #compactSlider() { return this.#value ? COMPACT_SLIDER_ON : COMPACT_SLIDER_OFF; } #trackStyle() { return new Style({ foreground: this.#value ? ON_TRACK_COLOR : OFF_TRACK_COLOR, }); } #sliderStyle() { return new Style({ foreground: SLIDER_COLOR, bold: true, }); } } const TOGGLE_SIZE = new Size(6, 2); const COMPACT_HEIGHT = 1; const TRACK_TOP = '┌────┐'; const TRACK_BOTTOM = '└────┘'; const SLIDER_OFF_TOP = '┏━┓'; const SLIDER_OFF_BOTTOM = '┗━┛'; const SLIDER_ON_TOP = '▗▄▖'; const SLIDER_ON_BOTTOM = '▝▀▘'; const COMPACT_TRACK = '╶──╴'; const COMPACT_SLIDER_OFF = '⬜︎'; const COMPACT_SLIDER_ON = '⬛︎'; const SLIDER_RANGE = [0, 3]; const COMPACT_SLIDER_RANGE = [0, 4]; const COMPACT_TRACK_OFF_X = 2; const OFF_LOCATION = 0; const ON_LOCATION = 1; const ANIMATION_MS = 120; const OFF_TRACK_COLOR = '#808080(244)'; const ON_TRACK_COLOR = '#34C759(40)'; const SLIDER_COLOR = '#FFFFFF(15)'; //# sourceMappingURL=Toggle.js.map