/** * EGroupware eTemplate2 - JS Dropdown Button object * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api * @link https://www.egroupware.org * @author Nathan Gray * @copyright Nathan Gray 2013 */ /*egw:uses /vendor/bower-asset/jquery/dist/jquery.js; /vendor/bower-asset/jquery-ui/jquery-ui.js; et2_baseWidget; */ import { et2_inputWidget } from './et2_core_inputWidget'; import { et2_register_widget } from "./et2_core_widget"; import { ClassWithAttributes } from "./et2_core_inheritance"; import { et2_no_init } from "./et2_core_common"; import { egw } from "../jsapi/egw_global"; /** * A split button - a button with a dropdown list * * There are several parts to the button UI: * - Container: This is what is percieved as the dropdown button, the whole package together * - Button: The part on the left that can be clicked * - Arrow: The button to display the choices * - Menu: The list of choices * * Menu options are passed via the select_options. They are normally ID => Title pairs, * as for a select box, but the title can also be full HTML if needed. * * @augments et2_inputWidget */ export class et2_dropdown_button extends et2_inputWidget { /** * Constructor * * @memberOf et2_dropdown_button */ constructor(_parent, _attrs, _child) { super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_dropdown_button._attributes, _child || {})); this.internal_ids = { div: "", button: "", menu: "" }; this.div = null; this.buttons = null; this.button = null; this.arrow = null; this.menu = null; this.image = null; this.clicked = false; this.label_updates = true; this.value = null; /** * Default menu, so there is something for the widget browser / editor to show */ this.default_menu = '
'; this.clicked = false; let self = this; // Create the individual UI elements // Menu is a UL this.menu = jQuery(this.default_menu).attr("id", this.internal_ids.menu) .hide() .menu({ select: function (event, ui) { self.onselect.call(self, event, ui.item); } }); this.buttons = jQuery(document.createElement("div")) .addClass("et2_dropdown"); // Main "wrapper" div this.div = jQuery(document.createElement("div")) .attr("id", this.internal_ids.div) .append(this.buttons) .append(this.menu); // Left side - activates click action this.button = jQuery(document.createElement("button")) .attr("id", this.internal_ids.button) .attr("type", "button") .addClass("ui-widget ui-corner-left").removeClass("ui-corner-all") .appendTo(this.buttons); // Right side - shows dropdown this.arrow = jQuery(document.createElement("button")) .addClass("ui-widget ui-corner-right").removeClass("ui-corner-all") .attr("type", "button") .click(function () { // ignore click on readonly button if (self.options.readonly) return false; // Clicking it again hides menu if (self.menu.is(":visible")) { self.menu.hide(); return false; } // Show menu dropdown var menu = self.menu.show().position({ my: "left top", at: "left bottom", of: self.buttons }); // Hide menu if clicked elsewhere jQuery(document).one("click", function () { menu.hide(); }); return false; }) // This is the actual down arrow icon .append("") .appendTo(this.buttons); // Common button UI this.buttons.children("button") .addClass("ui-state-default") .hover(function () { jQuery(this).addClass("ui-state-hover"); }, function () { jQuery(this).removeClass("ui-state-hover"); }); // Icon this.image = jQuery(document.createElement("img")); this.setDOMNode(this.div[0]); } destroy() { // Destroy widget if (this.menu && this.menu.data('ui-menu')) this.menu.menu("destroy"); // Null children this.image = null; this.button = null; this.arrow = null; this.buttons = null; this.menu = null; // Remove this.div.empty().remove(); } set_id(_id) { super.set_id(_id); // Update internal IDs - not really needed since we refer by internal // javascript reference, but good to keep up to date this.internal_ids = { div: this.dom_id + "_wrapper", button: this.dom_id, menu: this.dom_id + "_menu" }; for (let key in this.internal_ids) { if (this[key] == null) continue; this[key].attr("id", this.internal_ids[key]); } } /** * Set if the button label changes to match the selected option * * @param updates boolean Turn updating on or off */ set_label_updates(updates) { this.label_updates = updates; } set_accesskey(key) { jQuery(this.node).attr("accesskey", key); } set_ro_image(_image) { if (this.options.readonly) { this.set_image(_image); } } set_image(_image) { if (!this.isInTree() || this.image == null) return; if (!_image.trim()) { this.image.hide(); } else { this.image.show(); } let src = this.egw().image(_image); if (src) { this.image.attr("src", src); } // allow url's too else if (_image[0] == '/' || _image.substr(0, 4) == 'http') { this.image.attr('src', _image); } else { this.image.hide(); } } /** * Overwritten to maintain an internal clicked attribute * * @param _ev * @returns {Boolean} */ click(_ev) { // ignore click on readonly button if (this.options.readonly) return false; this.clicked = true; if (!super.click(_ev)) { this.clicked = false; return false; } this.clicked = false; return true; } onselect(event, selected_node) { this.set_value(selected_node.attr("data-id")); this.change(selected_node); } attachToDOM() { let res = super.attachToDOM(); // Move the parent's handler to the button, or we can't tell the difference between the clicks jQuery(this.node).unbind("click.et2_baseWidget"); this.button.off().bind("click.et2_baseWidget", this, function (e) { return e.data.click.call(e.data, this); }); return res; } set_label(_value) { if (this.button) { this.label = _value; this.button.text(_value) .prepend(this.image); } } /** * Set the options for the dropdown * * @param options Object ID => Label pairs */ set_select_options(options) { this.menu.first().empty(); // Allow more complicated content, if passed if (typeof options == "string") { this.menu.append(options); } else { let add_complex = function (node, options) { for (let key in options) { let item; if (typeof options[key] == "string") { item = jQuery("