Improved dropdown button and select widget behavior.

This commit is contained in:
Daan Vanden Bosch 2019-08-30 17:31:46 +02:00
parent 24f0cdb461
commit 4dde973951
7 changed files with 35 additions and 15 deletions

View File

@ -10,6 +10,7 @@
color: var(--control-text-color);
outline: none;
font-size: 13px;
font-family: var(--font-family);
overflow: hidden;
}

View File

@ -24,16 +24,18 @@ export class DropDownButton<T> extends Control {
to_label: (element: T) => string,
options?: DropDownButtonOptions,
) {
const element = el.div({ class: "core_DropDownButton" });
const button = new Button(text, {
icon_left: options && options.icon_left,
icon_right: Icon.TriangleDown,
});
const menu = new Menu<T>(items, to_label);
const menu = new Menu<T>(items, to_label, element);
super(el.div({ class: "core_DropDownButton" }, button.element, menu.element), options);
super(element, options);
this.button = this.disposable(button);
this.menu = this.disposable(menu);
this.element.append(this.button.element, this.menu.element);
this._chosen = emitter();
this.chosen = this._chosen;
@ -41,7 +43,9 @@ export class DropDownButton<T> extends Control {
this.just_opened = false;
this.disposables(
disposable_listener(button.element, "mousedown", e => this.button_mousedown(e)),
disposable_listener(button.element, "mousedown", () => this.button_mousedown(), {
capture: true,
}),
button.mouseup.observe(() => this.button_mouseup()),
@ -59,8 +63,7 @@ export class DropDownButton<T> extends Control {
this.button.enabled.val = enabled;
}
private button_mousedown(e: Event): void {
e.stopPropagation();
private button_mousedown(): void {
this.just_opened = !this.menu.visible.val;
this.menu.visible.val = true;
}

View File

@ -11,12 +11,18 @@ export class Menu<T> extends Widget {
private readonly to_label: (element: T) => string;
private readonly items: Property<T[]>;
private readonly related_element: HTMLElement;
private readonly _selected: WidgetProperty<T | undefined>;
constructor(items: T[] | Property<T[]>, to_label: (element: T) => string) {
constructor(
items: T[] | Property<T[]>,
to_label: (element: T) => string,
related_element: HTMLElement,
) {
super(el.div({ class: "core_Menu" }));
this.element.hidden = true;
this.visible.val = false;
this.element.onmouseup = (e: Event) => this.mouseup(e);
const inner_element = el.div({ class: "core_Menu_inner" });
@ -24,6 +30,7 @@ export class Menu<T> extends Widget {
this.to_label = to_label;
this.items = Array.isArray(items) ? property(items) : items;
this.related_element = related_element;
this._selected = new WidgetProperty<T | undefined>(this, undefined, this.set_selected);
this.selected = this._selected;
@ -41,7 +48,9 @@ export class Menu<T> extends Widget {
{ call_now: true },
),
disposable_listener(document, "mousedown", (e: Event) => this.document_mousedown(e)),
disposable_listener(document, "mousedown", (e: Event) => this.document_mousedown(e), {
capture: true,
}),
);
}
@ -63,7 +72,11 @@ export class Menu<T> extends Widget {
}
private document_mousedown(e: Event): void {
if (this.visible.val && !this.element.contains(e.target as Node)) {
if (
this.visible.val &&
!this.element.contains(e.target as Node) &&
!this.related_element.contains(e.target as Node)
) {
this.visible.val = false;
}
}

View File

@ -1,7 +1,7 @@
.core_Select {
position: relative;
display: inline-flex;
width: 150px;
width: 160px;
}
.core_Select .core_Button {

View File

@ -27,18 +27,20 @@ export class Select<T> extends LabelledControl {
to_label: (element: T) => string,
options?: SelectOptions<T>,
) {
const element = el.div({ class: "core_Select" });
const button = new Button("", {
icon_right: Icon.TriangleDown,
});
const menu = new Menu<T>(items, to_label);
const menu = new Menu<T>(items, to_label, element);
super(el.div({ class: "core_Select" }, button.element, menu.element), options);
super(element, options);
this.preferred_label_position = "left";
this.to_label = to_label;
this.button = this.disposable(button);
this.menu = this.disposable(menu);
this.element.append(this.button.element, this.menu.element);
this._selected = new WidgetProperty<T | undefined>(this, undefined, this.set_selected);
this.selected = this._selected;

View File

@ -129,11 +129,11 @@ export function disposable_listener(
listener: EventListenerOrEventListenerObject,
options?: AddEventListenerOptions,
): Disposable {
element.addEventListener(event, listener);
element.addEventListener(event, listener, options);
return {
dispose(): void {
element.removeEventListener(event, listener, options);
element.removeEventListener(event, listener);
},
};
}

View File

@ -4,6 +4,7 @@
--bg-color: hsl(0, 0%, 15%);
--text-color: hsl(0, 0%, 80%);
--text-color-disabled: hsl(0, 0%, 55%);
--font-family: Verdana, Geneva, sans-serif;
--border-color: hsl(0, 0%, 25%);
/* Scrollbars */
@ -63,7 +64,7 @@ body {
font-size: 13px;
background-color: var(--bg-color);
color: var(--text-color);
font-family: Verdana, Geneva, sans-serif;
font-family: var(--font-family);
}
#root *[hidden] {