mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 07:18:29 +08:00
Made several widgets more keyboard-friendly.
This commit is contained in:
parent
71433d253f
commit
821b894a52
@ -39,6 +39,10 @@
|
||||
color: hsl(0, 0%, 75%);
|
||||
}
|
||||
|
||||
.core_Button:focus-within .core_Button_inner {
|
||||
border: var(--control-inner-border-focus);
|
||||
}
|
||||
|
||||
.core_Button:disabled .core_Button_inner {
|
||||
background-color: hsl(0, 0%, 15%);
|
||||
border-color: hsl(0, 0%, 25%);
|
||||
|
@ -19,6 +19,8 @@ export class Button extends Control {
|
||||
private readonly _mousedown: Emitter<MouseEvent>;
|
||||
private readonly _mouseup: Emitter<MouseEvent>;
|
||||
private readonly _click: Emitter<MouseEvent>;
|
||||
private readonly _keydown: Emitter<KeyboardEvent>;
|
||||
private readonly _keyup: Emitter<KeyboardEvent>;
|
||||
private readonly _text: WidgetProperty<string>;
|
||||
private readonly center_element: HTMLSpanElement;
|
||||
|
||||
@ -26,6 +28,8 @@ export class Button extends Control {
|
||||
readonly mousedown: Observable<MouseEvent>;
|
||||
readonly mouseup: Observable<MouseEvent>;
|
||||
readonly click: Observable<MouseEvent>;
|
||||
readonly keydown: Observable<KeyboardEvent>;
|
||||
readonly keyup: Observable<KeyboardEvent>;
|
||||
readonly text: WritableProperty<string>;
|
||||
|
||||
constructor(options?: ButtonOptions) {
|
||||
@ -58,6 +62,14 @@ export class Button extends Control {
|
||||
this.click = this._click;
|
||||
this.element.onclick = (e: MouseEvent) => this._click.emit({ value: e });
|
||||
|
||||
this._keydown = emitter<KeyboardEvent>();
|
||||
this.keydown = this._keydown;
|
||||
this.element.onkeydown = (e: KeyboardEvent) => this._keydown.emit({ value: e });
|
||||
|
||||
this._keyup = emitter<KeyboardEvent>();
|
||||
this.keyup = this._keyup;
|
||||
this.element.onkeyup = (e: KeyboardEvent) => this._keyup.emit({ value: e });
|
||||
|
||||
this._text = new WidgetProperty<string>(this, "", this.set_text);
|
||||
this.text = this._text;
|
||||
|
||||
|
@ -35,7 +35,7 @@ export class DropDown<T> extends Control {
|
||||
}),
|
||||
);
|
||||
this.menu = this.disposable(
|
||||
new Menu<T>({
|
||||
new Menu({
|
||||
items: options.items,
|
||||
to_label: options.to_label,
|
||||
related_element: this.element,
|
||||
@ -49,11 +49,13 @@ export class DropDown<T> extends Control {
|
||||
this.just_opened = false;
|
||||
|
||||
this.disposables(
|
||||
disposable_listener(this.button.element, "mousedown", () => this.button_mousedown(), {
|
||||
disposable_listener(this.button.element, "mousedown", this.button_mousedown, {
|
||||
capture: true,
|
||||
}),
|
||||
|
||||
this.button.mouseup.observe(() => this.button_mouseup()),
|
||||
this.button.mouseup.observe(this.button_mouseup),
|
||||
|
||||
this.button.keydown.observe(this.button_keydown),
|
||||
|
||||
this.menu.selected.observe(({ value }) => {
|
||||
if (value !== undefined) {
|
||||
@ -71,12 +73,12 @@ export class DropDown<T> extends Control {
|
||||
this.button.enabled.val = enabled;
|
||||
}
|
||||
|
||||
private button_mousedown(): void {
|
||||
private button_mousedown = (): void => {
|
||||
this.just_opened = !this.menu.visible.val;
|
||||
this.menu.visible.val = true;
|
||||
}
|
||||
};
|
||||
|
||||
private button_mouseup(): void {
|
||||
private button_mouseup = (): void => {
|
||||
if (this.just_opened) {
|
||||
this.menu.focus();
|
||||
} else {
|
||||
@ -84,5 +86,15 @@ export class DropDown<T> extends Control {
|
||||
}
|
||||
|
||||
this.just_opened = false;
|
||||
}
|
||||
};
|
||||
|
||||
private button_keydown = ({ value: evt }: { value: KeyboardEvent }): void => {
|
||||
if (evt.key === "Enter" || evt.key === " ") {
|
||||
evt.preventDefault();
|
||||
this.just_opened = !this.menu.visible.val;
|
||||
this.menu.visible.val = true;
|
||||
this.menu.focus();
|
||||
this.menu.hover_next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ export class Menu<T> extends Widget {
|
||||
private readonly _selected: WidgetProperty<T | undefined>;
|
||||
private hovered_index?: number;
|
||||
private hovered_element?: HTMLElement;
|
||||
private previously_focused_element?: Element;
|
||||
|
||||
constructor(options: MenuOptions<T>) {
|
||||
super();
|
||||
@ -31,6 +32,7 @@ export class Menu<T> extends Widget {
|
||||
|
||||
this.element.onmouseup = this.mouseup;
|
||||
this.element.onkeydown = this.keydown;
|
||||
this.element.onblur = this.blur;
|
||||
|
||||
this.inner_element.onmouseover = this.inner_mouseover;
|
||||
this.element.append(this.inner_element);
|
||||
@ -66,6 +68,11 @@ export class Menu<T> extends Widget {
|
||||
this.finalize_construction();
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.previously_focused_element = document.activeElement ?? undefined;
|
||||
this.element.focus();
|
||||
}
|
||||
|
||||
hover_next(): void {
|
||||
this.visible.set_val(true, { silent: false });
|
||||
this.hover_item(
|
||||
@ -90,6 +97,10 @@ export class Menu<T> extends Widget {
|
||||
if (this.visible.val != visible) {
|
||||
this.hover_item();
|
||||
this.inner_element.scrollTop = 0;
|
||||
|
||||
if (!visible && this.previously_focused_element instanceof HTMLElement) {
|
||||
this.previously_focused_element.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,24 +117,31 @@ export class Menu<T> extends Widget {
|
||||
this.select_item(parseInt(index_str, 10));
|
||||
};
|
||||
|
||||
private keydown = (e: Event): void => {
|
||||
const key = (e as KeyboardEvent).key;
|
||||
private keydown = (evt: Event): void => {
|
||||
const key = (evt as KeyboardEvent).key;
|
||||
|
||||
switch (key) {
|
||||
case "ArrowDown":
|
||||
evt.preventDefault();
|
||||
this.hover_next();
|
||||
break;
|
||||
|
||||
case "ArrowUp":
|
||||
evt.preventDefault();
|
||||
this.hover_prev();
|
||||
break;
|
||||
|
||||
case "Enter":
|
||||
evt.preventDefault();
|
||||
this.select_hovered();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
private blur = (): void => {
|
||||
this.visible.val = false;
|
||||
};
|
||||
|
||||
private inner_mouseover = (e: Event): void => {
|
||||
if (e.target && e.target instanceof HTMLElement) {
|
||||
const index = e.target.dataset.index;
|
||||
|
@ -53,13 +53,15 @@ export class Select<T> extends LabelledControl {
|
||||
this.just_opened = false;
|
||||
|
||||
this.disposables(
|
||||
disposable_listener(this.button.element, "mousedown", e => this.button_mousedown(e)),
|
||||
disposable_listener(this.button.element, "mousedown", this.button_mousedown),
|
||||
|
||||
this.button.mouseup.observe(() => this.button_mouseup()),
|
||||
this.button.mouseup.observe(this.button_mouseup),
|
||||
|
||||
this.menu.selected.observe(({ value }) =>
|
||||
this._selected.set_val(value, { silent: false }),
|
||||
),
|
||||
this.button.keydown.observe(this.button_keydown),
|
||||
|
||||
this.menu.selected.observe(({ value }) => {
|
||||
this._selected.set_val(value, { silent: false });
|
||||
}),
|
||||
);
|
||||
|
||||
if (options) {
|
||||
@ -83,13 +85,13 @@ export class Select<T> extends LabelledControl {
|
||||
this.menu.selected.val = selected;
|
||||
}
|
||||
|
||||
private button_mousedown(e: Event): void {
|
||||
private button_mousedown = (e: Event): void => {
|
||||
e.stopPropagation();
|
||||
this.just_opened = !this.menu.visible.val;
|
||||
this.menu.visible.val = true;
|
||||
}
|
||||
};
|
||||
|
||||
private button_mouseup(): void {
|
||||
private button_mouseup = (): void => {
|
||||
if (this.just_opened) {
|
||||
this.menu.focus();
|
||||
} else {
|
||||
@ -97,5 +99,15 @@ export class Select<T> extends LabelledControl {
|
||||
}
|
||||
|
||||
this.just_opened = false;
|
||||
}
|
||||
};
|
||||
|
||||
private button_keydown = ({ value: evt }: { value: KeyboardEvent }): void => {
|
||||
if (evt.key === "Enter" || evt.key === " ") {
|
||||
evt.preventDefault();
|
||||
this.just_opened = !this.menu.visible.val;
|
||||
this.menu.visible.val = true;
|
||||
this.menu.focus();
|
||||
this.menu.hover_next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
--control-border: solid 1px hsl(0, 0%, 10%);
|
||||
|
||||
--control-inner-border: solid 1px hsl(0, 0%, 35%);
|
||||
--control-inner-border-focus: solid 1px hsl(0, 0%, 50%);
|
||||
|
||||
/* Inputs */
|
||||
|
||||
|
@ -1 +1 @@
|
||||
35
|
||||
36
|
||||
|
Loading…
Reference in New Issue
Block a user