@bokeh/bokehjs
Version:
Interactive, novel data visualization
82 lines • 3.09 kB
JavaScript
import { select, option } from "../../core/dom";
import { isString } from "../../core/util/types";
import { InputWidget, InputWidgetView } from "./input_widget";
import * as inputs from "../../styles/widgets/inputs.css";
export class MultiSelectView extends InputWidgetView {
static __name__ = "MultiSelectView";
connect_signals() {
super.connect_signals();
this.connect(this.model.properties.value.change, () => this.render_selection());
this.connect(this.model.properties.options.change, () => this.rerender());
this.connect(this.model.properties.name.change, () => this.rerender());
this.connect(this.model.properties.title.change, () => this.rerender());
this.connect(this.model.properties.size.change, () => this.rerender());
this.connect(this.model.properties.disabled.change, () => this.rerender());
}
_render_input() {
const options = this.model.options.map((opt) => {
let value, _label;
if (isString(opt)) {
value = _label = opt;
}
else {
[value, _label] = opt;
}
return option({ value }, _label);
});
this.input_el = select({
multiple: true,
class: inputs.input,
name: this.model.name,
disabled: this.model.disabled,
}, options);
this.input_el.addEventListener("change", () => this.change_input());
return this.input_el;
}
render() {
super.render();
this.render_selection();
}
render_selection() {
const selected = new Set(this.model.value);
for (const el of this.shadow_el.querySelectorAll("option")) {
el.selected = selected.has(el.value);
}
// Note that some browser implementations might not reduce
// the number of visible options for size <= 3.
this.input_el.size = this.model.size;
}
change_input() {
const is_focused = this.shadow_el.querySelector("select:focus") != null;
const values = [];
for (const el of this.shadow_el.querySelectorAll("option")) {
if (el.selected) {
values.push(el.value);
}
}
this.model.value = values;
super.change_input();
// Restore focus back to the <select> afterwards,
// so that even if python on_change callback is invoked,
// focus remains on <select> and one can seamlessly scroll
// up/down.
if (is_focused) {
this.input_el.focus();
}
}
}
export class MultiSelect extends InputWidget {
static __name__ = "MultiSelect";
constructor(attrs) {
super(attrs);
}
static {
this.prototype.default_view = MultiSelectView;
this.define(({ Int, Str, List, Tuple, Or }) => ({
value: [List(Str), []],
options: [List(Or(Str, Tuple(Str, Str))), []],
size: [Int, 4], // 4 is the HTML default
}));
}
}
//# sourceMappingURL=multiselect.js.map