Skip to content

Instantly share code, notes, and snippets.

@aryanshridhar
Created June 19, 2021 12:32
Show Gist options
  • Save aryanshridhar/cac0a951ffe6de12db164faa5e4e14b4 to your computer and use it in GitHub Desktop.
Save aryanshridhar/cac0a951ffe6de12db164faa5e4e14b4 to your computer and use it in GitHub Desktop.
import $ from "jquery";
import render_dropdown_list from "../templates/settings/dropdown_list.hbs";
import * as blueslip from "./blueslip";
import * as ListWidget from "./list_widget";
import _ from 'lodash';
export function DropdownListWidget({
widget_name,
data,
default_text,
render_text = (item_name) => item_name,
null_value = null,
include_current_item = true,
value,
on_update = () => {},
}) {
this.widget_name = widget_name;
this.data = data;
this.default_text = default_text;
this.render_text = render_text;
this.null_value = null;
this.include_current_item = include_current_item;
this.value = value;
this.on_update = on_update;
this.container_id = `${widget_name}_widget`;
this.value_id = `id_${widget_name}`;
if (value === undefined) {
this.value = null_value;
blueslip.warn("dropdown-list-widget: Called without a default value; using null value");
}
this.setup();
}
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();
};
DropdownListWidget.prototype.render = function (value) {
$(`#${CSS.escape(this.container_id)} #${CSS.escape(this.value_id)}`).data("value", value);
const elem = $(`#${CSS.escape(this.container_id)} #${CSS.escape(this.widget_name)}_name`);
if (!value || value === this.null_value) {
this.render_default_text(elem);
return;
}
// Happy path
const item = this.data.find((x) => x.value === value.toString());
if (item === undefined) {
this.render_default_text(elem);
return;
}
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();
};
DropdownListWidget.prototype.update = function (value) {
this.render(value);
this.on_update(value);
};
DropdownListWidget.prototype.register_event_handlers = function () {
$(`#${CSS.escape(this.container_id)} .dropdown-list-body`).on(
"click keypress",
".list_item",
(e) => {
const setting_elem = $(this).closest(`.${CSS.escape(this.widget_name)}_setting`);
if (e.type === "keypress") {
if (e.key === "Enter") {
setting_elem.find(".dropdown-menu").dropdown("toggle");
} else {
return;
}
}
const value = $(e.target.closest("li")).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();
});
};
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);
},
},
simplebar_container: $(`#${CSS.escape(this.container_id)} .dropdown-list-wrapper`),
});
};
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();
});
dropdown_toggle.on("click", () => {
search_input.val("").trigger("input");
});
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");
}
});
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.value);
this.register_event_handlers();
};
DropdownListWidget.prototype.get_value = function () {
let val = $(`#${CSS.escape(this.container_id)} #${CSS.escape(this.value_id)}`).data("value");
if (val === null) {
val = "";
}
return val;
};
export function MultiSelectDropdownListWidget({
widget_name,
data,
default_text,
null_value = null,
on_update = () => {},
value,
limit,
}) {
this.limit = limit;
this.selected_data = []; // Populate the data enetered by the user.
DropdownListWidget.call(this, {
widget_name,
data,
default_text,
null_value,
on_update,
value,
});
if (this.limit === undefined) {
this.limit = 2;
blueslip.warn(
"Multiselect dropdown-list-widget: Called without limit value; using 2 as the value",
);
}
if(value){
this.selected_data = [...value];
}
this.initialize_intial_values(value);
}
MultiSelectDropdownListWidget.prototype = Object.create(DropdownListWidget.prototype);
MultiSelectDropdownListWidget.prototype.set_text = function (elem, limit) {
const items_selected = this.selected_data.length;
let text = "";
if(items_selected === 0){
this.render_default_text(elem);
return;
}
else if (limit >= items_selected) {
const data_selected = this.data.filter((data) => this.selected_data.includes(data.value));
text = data_selected.map((data) => data.name).toString();
} else {
text = `${items_selected} selected`;
}
elem.text(text);
elem.removeClass("text-warning");
elem.closest(".input-group").find(".dropdown_list_reset_button:enabled").show();
};
MultiSelectDropdownListWidget.prototype.render = function(value){
$(`#${CSS.escape(this.container_id)} #${CSS.escape(this.value_id)}`).data("value", value);
const elem = $(`#${CSS.escape(this.container_id)} #${CSS.escape(this.widget_name)}_name`);
if (!value || value === this.null_value) {
this.render_default_text(elem);
return;
}
this.set_text(elem, this.limit);
};
MultiSelectDropdownListWidget.prototype.initialize_intial_values = function(values){
if(values === undefined){
return;
}
const dropdown_list_body = $(
`#${CSS.escape(this.container_id)} .dropdown-list-body`,
).expectOne();
for(const value of values){
console.log(typeof value);
const element = dropdown_list_body.find(`li[data-value = "${value}"]`)
this.add_check_mark(element, value);
}
}
MultiSelectDropdownListWidget.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]`);
ListWidget.create(dropdown_list_body, 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);
},
multiselect: {
selected_items: this.selected_data,
},
},
simplebar_container: $(`#${CSS.escape(this.container_id)} .dropdown-list-wrapper`),
});
};
MultiSelectDropdownListWidget.prototype.reset_dropdown_items = function(){
const dropdown_list_body = $(
`#${CSS.escape(this.container_id)} .dropdown-list-body`,
).expectOne();
if(this.checked_items === undefined){
$(`#${CSS.escape(this.container_id)} .dropdown-list-body`).find("li").each((e) =>{
const item = $($(`#${CSS.escape(this.container_id)} .dropdown-list-body`).find("li")[e]);
const value = item.attr("data-value");
if(item.hasClass("checked")){
console.log("Haha")
this.remove_check_mark(item,value);
}
})
}
else{
if(!(_.isEqual(this.selected_data,this.checked_items))){
const diff_values = _.difference(this.selected_data, this.checked_items).concat(_.difference(this.checked_items, this.selected_data));
for(const value of diff_values){
const element = dropdown_list_body.find(`li[data-value = "${value}"]`)
// User unselected the current value.
if(this.checked_items.includes(value)){
this.add_check_mark(element, value);
}
// User selected a bunch of more values.
else{
this.remove_check_mark(element, value);
}
}
}
}
}
MultiSelectDropdownListWidget.prototype.add_check_mark = function(element,value){
const link_elem = element.find("a").expectOne();
element.addClass("checked");
link_elem.prepend('<i class="fa fa-check" aria-hidden="true"></i>');
this.selected_data.push(value);
}
MultiSelectDropdownListWidget.prototype.remove_check_mark = function(element,value){
const icon = element.find("i").expectOne();
const index = this.selected_data.indexOf(value);
console.log(typeof value);
if (index > -1) {
icon.remove();
element.removeClass("checked");
this.selected_data.splice(index, 1);
}
}
MultiSelectDropdownListWidget.prototype.register_event_handlers = function () {
const dropdown_toggle = $(`#${CSS.escape(this.container_id)} .dropdown-toggle`);
const search_input = $(`#${CSS.escape(this.container_id)} .dropdown-search > input[type=text]`);
const dropdown_list_body = $(
`#${CSS.escape(this.container_id)} .dropdown-list-body`,
).expectOne();
dropdown_toggle.on("click", () => {
this.reset_dropdown_items();
search_input.val("").trigger("input");
});
dropdown_list_body.on("click keypress", ".list_item", (e) => {
const item_pressed = $(e.target.closest("li"));
const value = item_pressed.attr("data-value");
if (item_pressed.hasClass("checked")) {
console.log("chekced");
this.remove_check_mark(item_pressed, value);
} else {
console.log("lol");
this.add_check_mark(item_pressed, value);
}
e.stopPropagation();
});
$(`#${CSS.escape(this.container_id)} .dropdown_list_reset_button`).on("click", (e) => {
// Default back the value.
this.checked_items = undefined;
this.update(this.null_value);
e.preventDefault();
});
$(`#${CSS.escape(this.container_id)} .multiselect_btn`).on("click", (e) => {
e.preventDefault();
this.checked_items = [...this.selected_data];
this.update(this.selected_data);
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment