@teaui/core
Version:
A high-level terminal UI library for Node
128 lines • 3.87 kB
JavaScript
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