2021-03-11 05:43:45 +01:00
|
|
|
import $ from "jquery";
|
|
|
|
|
2021-02-28 21:33:50 +01:00
|
|
|
import render_dropdown_list from "../templates/settings/dropdown_list.hbs";
|
2020-08-01 03:43:15 +02:00
|
|
|
|
2021-03-16 23:38:59 +01:00
|
|
|
import * as blueslip from "./blueslip";
|
2021-02-28 21:33:50 +01:00
|
|
|
import * as ListWidget from "./list_widget";
|
2020-09-24 04:45:12 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
export function DropdownListWidget({
|
2021-03-24 21:44:43 +01:00
|
|
|
widget_name,
|
|
|
|
data,
|
|
|
|
default_text,
|
|
|
|
render_text = (item_name) => item_name,
|
|
|
|
null_value = null,
|
2021-03-28 15:24:00 +02:00
|
|
|
include_current_item = true,
|
2021-03-24 21:44:43 +01:00
|
|
|
value,
|
|
|
|
on_update = () => {},
|
|
|
|
}) {
|
2021-07-27 14:05:22 +02:00
|
|
|
// Initializing values
|
|
|
|
this.widget_name = widget_name;
|
|
|
|
this.data = data;
|
|
|
|
this.default_text = default_text;
|
|
|
|
this.render_text = render_text;
|
|
|
|
this.null_value = null_value;
|
|
|
|
this.include_current_item = include_current_item;
|
|
|
|
this.initial_value = value;
|
|
|
|
this.on_update = on_update;
|
|
|
|
|
|
|
|
this.container_id = `${widget_name}_widget`;
|
|
|
|
this.value_id = `id_${widget_name}`;
|
|
|
|
|
2021-03-24 21:44:43 +01:00
|
|
|
if (value === undefined) {
|
2021-07-27 14:05:22 +02:00
|
|
|
this.initial_value = null_value;
|
2021-03-24 21:44:43 +01:00
|
|
|
blueslip.warn("dropdown-list-widget: Called without a default value; using null value");
|
|
|
|
}
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
// Setting up dropdown_list_widget
|
|
|
|
this.setup();
|
|
|
|
}
|
2021-02-12 23:49:22 +01:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
DropdownListWidget.prototype.render_default_text = function (elem) {
|
|
|
|
elem.text(this.default_text);
|
|
|
|
elem.addClass("text-warning");
|
|
|
|
elem.closest(".input-group").find(".dropdown_list_reset_button:enabled").hide();
|
|
|
|
};
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
DropdownListWidget.prototype.render = function (value) {
|
|
|
|
$(`#${CSS.escape(this.container_id)} #${CSS.escape(this.value_id)}`).data("value", value);
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
const elem = $(`#${CSS.escape(this.container_id)} #${CSS.escape(this.widget_name)}_name`);
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
if (!value || value === this.null_value) {
|
|
|
|
this.render_default_text(elem);
|
|
|
|
return;
|
|
|
|
}
|
2021-02-12 23:49:22 +01:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
// Happy path
|
|
|
|
const item = this.data.find((x) => x.value === value.toString());
|
2021-02-12 23:49:22 +01:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
if (item === undefined) {
|
|
|
|
this.render_default_text(elem);
|
|
|
|
return;
|
|
|
|
}
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
const text = this.render_text(item.name);
|
|
|
|
elem.text(text);
|
|
|
|
elem.removeClass("text-warning");
|
|
|
|
elem.closest(".input-group").find(".dropdown_list_reset_button:enabled").show();
|
|
|
|
};
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
DropdownListWidget.prototype.update = function (value) {
|
|
|
|
this.render(value);
|
|
|
|
this.on_update(value);
|
|
|
|
};
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
DropdownListWidget.prototype.register_event_handlers = function () {
|
|
|
|
$(`#${CSS.escape(this.container_id)} .dropdown-list-body`).on(
|
|
|
|
"click keypress",
|
|
|
|
".list_item",
|
|
|
|
(e) => {
|
|
|
|
const setting_elem = $(e.currentTarget).closest(
|
|
|
|
`.${CSS.escape(this.widget_name)}_setting`,
|
|
|
|
);
|
|
|
|
if (e.type === "keypress") {
|
|
|
|
if (e.key === "Enter") {
|
|
|
|
setting_elem.find(".dropdown-menu").dropdown("toggle");
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2021-03-28 15:24:00 +02:00
|
|
|
}
|
2021-07-27 14:05:22 +02:00
|
|
|
const value = $(e.currentTarget).attr("data-value");
|
|
|
|
this.update(value);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
$(`#${CSS.escape(this.container_id)} .dropdown_list_reset_button`).on("click", (e) => {
|
|
|
|
this.update(this.null_value);
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
};
|
2021-03-28 15:24:00 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
DropdownListWidget.prototype.setup_dropdown_widget = function (data) {
|
|
|
|
const dropdown_list_body = $(
|
|
|
|
`#${CSS.escape(this.container_id)} .dropdown-list-body`,
|
|
|
|
).expectOne();
|
|
|
|
const search_input = $(`#${CSS.escape(this.container_id)} .dropdown-search > input[type=text]`);
|
|
|
|
const get_data = () => {
|
|
|
|
if (this.include_current_item) {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
return data.filter((x) => x.value !== this.value.toString());
|
|
|
|
};
|
|
|
|
|
|
|
|
ListWidget.create(dropdown_list_body, get_data(data), {
|
|
|
|
name: `${CSS.escape(this.widget_name)}_list`,
|
|
|
|
modifier(item) {
|
|
|
|
return render_dropdown_list({item});
|
|
|
|
},
|
|
|
|
filter: {
|
|
|
|
element: search_input,
|
|
|
|
predicate(item, value) {
|
|
|
|
return item.name.toLowerCase().includes(value);
|
2020-04-15 04:29:15 +02:00
|
|
|
},
|
2021-07-27 14:05:22 +02:00
|
|
|
},
|
|
|
|
simplebar_container: $(`#${CSS.escape(this.container_id)} .dropdown-list-wrapper`),
|
|
|
|
});
|
|
|
|
};
|
2020-04-15 22:55:05 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
// Sets the focus to the ListWidget input once the dropdown button is clicked.
|
|
|
|
DropdownListWidget.prototype.dropdown_toggle_click_handler = function () {
|
|
|
|
const dropdown_toggle = $(`#${CSS.escape(this.container_id)} .dropdown-toggle`);
|
|
|
|
const search_input = $(`#${CSS.escape(this.container_id)} .dropdown-search > input[type=text]`);
|
2020-04-15 04:29:15 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
dropdown_toggle.on("click", () => {
|
|
|
|
search_input.val("").trigger("input");
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
DropdownListWidget.prototype.setup = function () {
|
|
|
|
// populate the dropdown
|
|
|
|
const dropdown_list_body = $(
|
|
|
|
`#${CSS.escape(this.container_id)} .dropdown-list-body`,
|
|
|
|
).expectOne();
|
|
|
|
const search_input = $(`#${CSS.escape(this.container_id)} .dropdown-search > input[type=text]`);
|
|
|
|
const dropdown_toggle = $(`#${CSS.escape(this.container_id)} .dropdown-toggle`);
|
|
|
|
|
|
|
|
this.setup_dropdown_widget(this.data);
|
|
|
|
|
|
|
|
$(`#${CSS.escape(this.container_id)} .dropdown-search`).on("click", (e) => {
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
|
|
|
this.dropdown_toggle_click_handler();
|
|
|
|
|
|
|
|
dropdown_toggle.on("focus", (e) => {
|
|
|
|
// On opening a Bootstrap Dropdown, the parent element receives focus.
|
|
|
|
// Here, we want our search input to have focus instead.
|
|
|
|
e.preventDefault();
|
|
|
|
// This function gets called twice when focusing the
|
|
|
|
// dropdown, and only in the second call is the input
|
|
|
|
// field visible in the DOM; so the following visibility
|
|
|
|
// check ensures we wait for the second call to focus.
|
|
|
|
if (dropdown_list_body.is(":visible")) {
|
|
|
|
search_input.trigger("focus");
|
2020-04-14 10:43:30 +02:00
|
|
|
}
|
2021-07-27 14:05:22 +02:00
|
|
|
});
|
2020-04-14 10:43:30 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
search_input.on("keydown", (e) => {
|
|
|
|
const {key, keyCode, which} = e;
|
|
|
|
const navigation_keys = ["ArrowUp", "ArrowDown", "Escape"];
|
|
|
|
if (!navigation_keys.includes(key)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
// We pass keyCode instead of key here because the outdated
|
|
|
|
// bootstrap library we have at static/third/ still uses the
|
|
|
|
// deprecated keyCode & which properties.
|
|
|
|
const custom_event = new $.Event("keydown.dropdown.data-api", {keyCode, which});
|
|
|
|
dropdown_toggle.trigger(custom_event);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.render(this.initial_value);
|
|
|
|
this.register_event_handlers();
|
|
|
|
};
|
2020-04-15 04:29:15 +02:00
|
|
|
|
2021-07-27 14:05:22 +02:00
|
|
|
// Returns the updated value
|
|
|
|
DropdownListWidget.prototype.value = function () {
|
|
|
|
let val = $(`#${CSS.escape(this.container_id)} #${CSS.escape(this.value_id)}`).data("value");
|
|
|
|
if (val === null) {
|
|
|
|
val = "";
|
|
|
|
}
|
|
|
|
return val;
|
2020-04-14 10:43:30 +02:00
|
|
|
};
|