UNPKG

@teaui/core

Version:

A high-level terminal UI library for Node

128 lines 3.87 kB
import { Container } from '../Container.js'; import { Text } from './Text.js'; import { Rect, Point } from '../geometry.js'; import { isMouseClicked, styleTextForHotKey, hotKeyToString, } from '../events/index.js'; import { childPalette } from '../UI.js'; import { Style } from '../Style.js'; export class Checkbox extends Container { #value = false; #hotKey; #onChange; #textView; #hasFocus = false; constructor(props) { super(props); this.#textView = new Text({ alignment: 'center' }); this.add(this.#textView); this.#update(props); } get value() { return this.#value; } set value(value) { if (value === this.#value) { return; } this.#value = value; this.invalidateRender(); } childPalette(view) { return childPalette(super.childPalette(view), this.isPressed, this.isHover || this.#hasFocus); } update(props) { this.#update(props); super.update(props); } #update({ title, hotKey, value, onChange }) { const styledText = hotKey ? styleTextForHotKey(title ?? '', hotKey) : title; this.#textView.text = styledText ?? ''; this.#value = value; this.#hotKey = hotKey; this.#onChange = onChange; } get title() { return this.#textView?.text; } set title(value) { const styledText = this.#hotKey ? styleTextForHotKey(value ?? '', this.#hotKey) : value; this.#textView.text = styledText ?? ''; this.invalidateSize(); } legendItems() { if (!this.#hotKey) { return []; } return [{ key: hotKeyToString(this.#hotKey), label: this.title ?? '' }]; } naturalSize(available) { return super.naturalSize(available).grow(CHECKBOX_WIDTH, 0); } receiveMouse(event, system) { super.receiveMouse(event, system); if (isMouseClicked(event)) { this.#value = !this.#value; this.#onChange?.(this.#value); } } receiveKey(_) { this.#value = !this.#value; this.#onChange?.(this.#value); } render(viewport) { const hasFocus = viewport.registerFocus({ isDefault: false }); this.#hasFocus = hasFocus; if (viewport.isEmpty) { return super.render(viewport); } viewport.registerMouse(['mouse.button.left', 'mouse.move']); const uiStyle = this.purpose.ui({ isPressed: this.isPressed, isHover: this.isHover || hasFocus, }); viewport.paint(uiStyle); const boxWidth = CHECKBOX_WIDTH; const naturalSize = super.naturalSize(viewport.contentSize.shrink(boxWidth, 0)); const offset = new Point(boxWidth, Math.round((viewport.contentSize.height - naturalSize.height) / 2)); const chars = hasFocus ? BOX_FOCUS : BOX; const box = chars[this.boxStyle()][this.#value ? 'checked' : 'unchecked']; const textStyle = hasFocus ? uiStyle.merge(new Style({ bold: true })) : uiStyle; viewport.write(box, new Point(0, offset.y), textStyle); viewport.clipped(new Rect(offset, naturalSize), textStyle, inside => { super.render(inside); }); } boxStyle() { return 'checkbox'; } } export class Radio extends Checkbox { boxStyle() { return 'radio'; } } const BOX = { checkbox: { unchecked: '☐ ', checked: '◼︎ ', }, radio: { unchecked: '◯ ', checked: '⦿ ', }, }; const BOX_FOCUS = { checkbox: { unchecked: '🞐 ', checked: '🞕 ', }, radio: { unchecked: '◎ ', checked: '🞋 ', }, }; const CHECKBOX_WIDTH = 2; //# sourceMappingURL=Checkbox.js.map