UNPKG

gd-bs

Version:

Bootstrap JavaScript, TypeScript and Web Components library.

830 lines (829 loc) 34.6 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Dropdown = exports.DropdownTypes = exports.DropdownPlacements = void 0; var base_1 = require("../base"); var button_1 = require("../button"); var checkboxGroup_1 = require("../checkboxGroup"); var floating_ui_1 = require("../floating-ui"); var formItem_1 = require("./formItem"); var item_1 = require("./item"); var templates_1 = require("./templates"); /** * Dropdown Types */ exports.DropdownPlacements = floating_ui_1.FloatingUIPlacements; exports.DropdownTypes = button_1.ButtonTypes; // Gets the template var GetHTML = function (props) { // See if we are rendering items for a form if (props.formFl) { return templates_1.HTMLForm; } // See if we are rendering for a nav bar if (props.navFl) { return templates_1.HTMLNavItem; } // See if we are rendering a split button dropdown if (props.isSplit) { return templates_1.HTMLSplit; } // Return the default template return templates_1.HTML; }; /** * Dropdown * @property props - The dropdown properties. */ var _Dropdown = /** @class */ (function (_super) { __extends(_Dropdown, _super); // Constructor function _Dropdown(props, template) { if (template === void 0) { template = GetHTML(props); } var _this = _super.call(this, template, props) || this; _this._autoSelect = null; _this._cb = null; _this._floatingUI = null; _this._initFl = false; _this._items = null; // Handles the click event outside of the menu to close it _this.handleClick = function (ev) { // See if we clicked within the menu if (!ev.composedPath().includes(_this._elMenu)) { if (_this.isVisible) { // Hide the menu _this.toggle(); } else { // Remove this event (This shouldn't happen, but to be safe) document.body.removeEventListener("click", _this.handleClick); } } }; // Configure the dropdown _this.configure(); // Configure the events _this.configureEvents(); // Configure search _this.configureSearch(); // Configure the parent _this.configureParent(); // Set the flag _this._initFl = true; return _this; } // Configure the card group _Dropdown.prototype.configure = function () { // See if this is for a form if (this.props.formFl) { // Configure the dropdown for a form this.configureForm(); } // Else, see if this is for a nav bar else if (this.props.navFl) { // Configure the dropdown for a nav bar this.configureNavBar(); } else { // Configure the dropdown this.configureDefault(); } // Render the items this.renderItems(); // Set the menu element this._elMenu = this.el.querySelector(".dropdown-menu"); if (this._elMenu) { // See if we are only rendering a menu if (this.props.menuOnly) { // Update the element this.el = this._elMenu; } } // Set the dark theme this.props.isDark ? this.setTheme(true) : null; }; // Configures the dropdown _Dropdown.prototype.configureDefault = function () { // Set the attributes this.props.title ? this.el.title = this.props.title : null; this.props.dropLeft ? this.el.classList.add("dropstart") : null; this.props.dropRight ? this.el.classList.add("dropend") : null; this.props.dropUp ? this.el.classList.add("dropup") : null; // Set the type var btnType = button_1.ButtonClassNames.getByType(this.props.type) || button_1.ButtonClassNames.getByType(exports.DropdownTypes.Primary); // See if this is a split button if (this.props.isSplit) { // Update a label var label = this.el.querySelector("button"); if (label) { label.classList.add(btnType); label.disabled = this.props.isReadonly ? true : false; label.innerHTML = this.props.label == null ? "" : this.props.label; // Set the click event to disable the postback label.addEventListener("click", function (ev) { ev.preventDefault(); }); } } else { // Update the label var label = this.el.querySelector(".dropdown-toggle"); if (label) { label.innerHTML = this.props.label == null ? "" : this.props.label; } } // Update the dropdown var toggle = this.el.querySelector(".dropdown-toggle"); if (toggle) { toggle.classList.add(btnType); toggle.disabled = this.props.isReadonly ? true : false; toggle.setAttribute("aria-label", this.props.label || ""); } // See if we are rendering the menu only var menu = this.el.querySelector(".dropdown-menu"); if (menu) { // See if we are rendering the menu only if (this.props.menuOnly) { // Update the menu this.props.id ? menu.id = this.props.id : null; this.props.className ? menu.classList.add(this.props.className) : null; } else { // Update the menu this.props.id ? menu.setAttribute("aria-labelledby", this.props.id) : null; } // See if a button class name exists var classNames = (this.props.btnClassName || "").split(' '); for (var i = 0; i < classNames.length; i++) { // Ensure the class name exists var className = classNames[i]; if (className) { // Add the class name (this.props.menuOnly ? menu : toggle).classList.add(className); } } } }; // Configure the events _Dropdown.prototype.configureEvents = function () { var _this = this; // Set the auto select property this._autoSelect = typeof (this.props.autoSelect) === "boolean" ? this.props.autoSelect : true; // See if this is a select element and a change event exists var menu = this.el.querySelector("select"); if (menu) { // Add a change event menu.addEventListener("change", function (ev) { // See if multiple options are allowed if (_this.props.multi == true) { // See if we are selecting the values if (_this._autoSelect) { // Parse the items for (var i = 0; i < _this._items.length; i++) { var item = _this._items[i]; // Update the flag item.isSelected = item.el.selected; } } // Call the change event _this.props.onChange ? _this.props.onChange(_this.getValue(), ev) : null; } else { // Get the selected value var selectedValue = (ev.target.value || "").trim(); // Parse the items for (var i = 0; i < _this._items.length; i++) { var item = _this._items[i]; // Replace special characters var value = (item.props.text || ""); // See if this item was selected if (selectedValue == value) { // Ensure this item is selected if (_this._autoSelect && !item.isSelected) { item.toggle(); } // Call the change event _this.props.onChange ? _this.props.onChange(item.props, ev) : null; } else { // Unselect the other values if (_this._autoSelect && item.isSelected) { item.toggle(); } } } } }); } // Get the toggle var toggle = this.el.querySelector(".dropdown-toggle"); if (toggle && this._elMenu) { // Set the type, based on the current dropdown type var popoverType = floating_ui_1.FloatingUITypes.LightBorder; switch (this.props.type) { case exports.DropdownTypes.Danger: case exports.DropdownTypes.OutlineDanger: popoverType = floating_ui_1.FloatingUITypes.Danger; break; case exports.DropdownTypes.Dark: case exports.DropdownTypes.OutlineDark: popoverType = floating_ui_1.FloatingUITypes.Dark; break; case exports.DropdownTypes.Info: case exports.DropdownTypes.OutlineInfo: popoverType = floating_ui_1.FloatingUITypes.Info; break; case exports.DropdownTypes.Light: case exports.DropdownTypes.OutlineLight: case exports.DropdownTypes.Link: case exports.DropdownTypes.OutlineLink: popoverType = floating_ui_1.FloatingUITypes.Light; break; case exports.DropdownTypes.Primary: case exports.DropdownTypes.OutlinePrimary: popoverType = floating_ui_1.FloatingUITypes.Primary; break; case exports.DropdownTypes.Secondary: case exports.DropdownTypes.OutlineSecondary: popoverType = floating_ui_1.FloatingUITypes.Secondary; break; case exports.DropdownTypes.Success: case exports.DropdownTypes.OutlineSuccess: popoverType = floating_ui_1.FloatingUITypes.Success; break; case exports.DropdownTypes.Warning: case exports.DropdownTypes.OutlineWarning: popoverType = floating_ui_1.FloatingUITypes.Warning; break; } // Create the menu this._floatingUI = (0, floating_ui_1.FloatingUI)({ className: "floating-dropdown", elContent: this._elMenu, elTarget: toggle, placement: typeof (this.props.placement) === "number" ? this.props.placement : floating_ui_1.FloatingUIPlacements.BottomStart, theme: popoverType, onShow: function () { // See if the search element exists if (_this._elSearch) { // Clear the search _this._elSearch.value = ""; // Show all the items for (var i = 0; i < _this._items.length; i++) { _this._items[i].show(); } } }, options: { arrow: false, flip: true, shift: true, trigger: "click" } }); } }; // Configures the dropdown for a form _Dropdown.prototype.configureForm = function () { // Configure the label var elLabel = this.el.querySelector("label"); if (elLabel) { var label = this.props.label == null ? "" : this.props.label; if (label) { // Set the label elLabel.innerHTML = label; } else { // Remove the label elLabel.parentNode.removeChild(elLabel); } } // Update the dropdown var dropdown = this.el.querySelector("select"); if (dropdown) { dropdown.className = this.props.className || ""; dropdown.classList.add("form-select"); dropdown.disabled = this.props.isReadonly ? true : false; dropdown.multiple = this.props.multi ? true : false; dropdown.required = this.props.required ? true : false; this.props.title ? dropdown.title = this.props.title : null; } }; // Configure the item events _Dropdown.prototype.configureItemEvents = function (item) { var _this = this; // Ensure this isn't a header/divider if (item.props.isDivider || item.props.isHeader) { return; } // See if multi selections is not allowed if (this.props.multi != true) { // Add a click event item.el.addEventListener("click", function (ev) { // See if an item was selected, and is disabled if (item.props.isDisabled == true) { // Ignore the click event return; } // Parse the items for (var i = 0; i < _this._items.length; i++) { var selectedItem = _this._items[i]; // Skip this item if (item.el.innerHTML == selectedItem.el.innerHTML) { continue; } // Ensure this item is selected if (selectedItem.isSelected) { // Unselect the item selectedItem.toggle(); } } // See if we are updating the label if (_this.props.updateLabel) { var selectedItem = _this.getValue(); // Set the label var toggle = _this.el.querySelector(".dropdown-toggle"); if (toggle) { toggle.innerHTML = selectedItem ? selectedItem.text : _this.props.label; } } }); } // Add a click event item.el.addEventListener("click", function (ev) { // Prevent other events to occur ev.stopPropagation(); // Ensure this isn't a multi-select if (_this.isMulti != true) { // Toggle the menu if it's visible _this.isVisible ? _this.toggle() : null; } // Else, see if we are updating the label for a multi-dropdown else if (_this.props.updateLabel) { // Set the selected values var selectedItems = _this.getValue(); var selectedValues = []; for (var i = 0; i < selectedItems.length; i++) { // Append the value selectedValues.push(selectedItems[i].text); } // Set the label var toggle = _this.el.querySelector(".dropdown-toggle"); if (toggle) { // Set the label toggle.innerHTML = selectedValues.length == 0 ? _this.props.label : selectedValues.join(', '); } } // Execute the event _this.props.onChange ? _this.props.onChange(_this.getValue(), ev) : null; }); }; // Configures the dropdown for a nav bar _Dropdown.prototype.configureNavBar = function () { // Update the link var link = this.el.querySelector("a"); if (link) { link.id = ("navbarDDL" + (this.props.label == null ? "" : this.props.label)).replace(/ /g, ''); this.props.title ? link.title = this.props.title : null; this.props.isReadonly ? link.setAttribute("aria-disabled", "true") : null; link.innerHTML = this.props.label == null ? "" : this.props.label; } // See if we are rendering the menu only var menu = this.el.querySelector(".dropdown-menu"); if (menu) { if (this.props.menuOnly) { // Update the menu this.props.id ? menu.id = this.props.id : null; menu.className = this.props.className ? this.props.className : ""; menu.classList.add("dropdown-menu"); } else { // Update the menu this.props.id ? menu.setAttribute("aria-labelledby", this.props.id) : null; } } }; // Configures the search option for the dropdown _Dropdown.prototype.configureSearch = function () { var _this = this; // See if search is enabled and the menu exists if (this.props.search != true || this._elMenu == null) { return; } // Create the search textbox this._elSearch = document.createElement("input"); this._elSearch.classList.add("form-control"); this._elSearch.type = "search"; this._elSearch.placeholder = "Search for item..."; // Insert the item as the first element this._elMenu.firstChild ? this._elMenu.insertBefore(this._elSearch, this._elMenu.firstChild) : this._elMenu.appendChild(this._elSearch); // Create the empty text var elEmptyText = document.createElement("h6"); elEmptyText.classList.add("dropdown-header"); elEmptyText.classList.add("d-none"); elEmptyText.innerHTML = "No items were found..."; this._elMenu.appendChild(elEmptyText); // Add the element to the ignore list this._floatingUI.addIgnoreElement(this._elSearch); // Add the event this._elSearch.addEventListener("input", function () { // Get the value var searchText = (_this._elSearch.value || "").toLocaleLowerCase(); // Set the flags var itemsFound = false; var showAll = searchText == ""; // Hide the empty text elEmptyText.classList.add("d-none"); // Parse the items for (var i = 0; i < _this._items.length; i++) { var item = _this._items[i]; // See if we are showing all the items if (showAll) { // Show the item item.show(); } else { // See if the value contains the text if ((item.props.text || "").toLowerCase().indexOf(searchText) >= 0) { // Show the item item.show(); // Set the flag itemsFound = true; } else { // Hide the item item.hide(); } } } // See if no items were found if (!showAll && !itemsFound) { // Show the empty message elEmptyText.classList.remove("d-none"); } }); }; // Generates the checkbox items _Dropdown.prototype.generateCheckboxItems = function () { var cbItems = []; // Parse the items var items = this.props.items || []; for (var i = 0; i < items.length; i++) { var item = items[i]; // Create the checkbox item cbItems.push({ data: item, isDisabled: item.isDisabled, isSelected: item.isSelected, label: item.text, onChange: item.onClick, type: checkboxGroup_1.CheckboxGroupTypes.Checkbox }); } // Return the items return cbItems; }; // Generates the checkbox value _Dropdown.prototype.generateCheckboxValue = function (currentValues) { var _a; var values = []; // Ensure a value exists if (currentValues == null) { return values; } // Ensure it's an array if (typeof (currentValues) === "string") { // Make it an array currentValues = [currentValues]; } var _loop_1 = function (i) { var currentValue = currentValues[i]; var currentItem = {}; // See if this is a string if (typeof (currentValue) == "string") { // Set the text and value properties currentItem.text = currentValue; currentItem.value = currentValue; } else { // Set the item currentItem = currentValue; } // Find the item var item = (_a = this_1.props.items) === null || _a === void 0 ? void 0 : _a.find(function (item) { return item.value == currentValue || item.text == currentValue; }); if (item) { // Add the text property values.push(item.text); } }; var this_1 = this; // Parse the current values for (var i = 0; i < currentValues.length; i++) { _loop_1(i); } // Return the values return values; }; // Renders the items _Dropdown.prototype.renderItems = function () { var _this = this; // Clear the items this._items = []; // Get the menu var menu = this.el.querySelector(".dropdown-menu") || this.el.querySelector("select") || (this.el.classList.contains("dropdown-menu") ? this.el : null); if (menu) { // See if we are creating checkboxes if (this.props.isCheckbox) { // Render the checkbox this._cb = (0, checkboxGroup_1.CheckboxGroup)({ className: "m-2", el: menu, items: this.generateCheckboxItems(), multi: this.props.multi, value: this.generateCheckboxValue(this.props.value), onChange: this.props.onChange ? function (selectedItems, allItems, ev) { // Pass the current values _this.props.onChange(selectedItems, ev); } : null }); } else { var isForm = menu.nodeName == "SELECT"; var values = []; // Parse the items var items = this.props.items || []; for (var i = 0; i < items.length; i++) { // Create the item var item = isForm ? new formItem_1.DropdownFormItem(items[i], this.props) : new item_1.DropdownItem(items[i], this.props); this._items.push(item); // See if this item is selected if (item.isSelected) { values.push(item.props.value || item.props.text); } // See if this isn't for a form if (!isForm) { // Configure the item events this.configureItemEvents(item); } // Add the item to the menu menu.appendChild(item.el); } // Set the value this.setValue(this.props.multi ? values : values[0]); // See if this is a form if (isForm) { // Ensure the selected values match the index var idx = menu.selectedIndex; if (idx >= 0 && idx < this._items.length) { // Parse the items and ensure none are toggled except for the selected index for (var i = 0; i < this._items.length; i++) { // Ensure it's not selected if (i != idx && this._items[i].isSelected) { this._items[i].toggle(); } // Else, toggle it if it's the selected item else if (idx == i && this._items[i].isSelected == false) { this._items[i].toggle(); } } } } } } }; /** * Public Interface */ // Disables the button _Dropdown.prototype.disable = function () { // Get the buttons var buttons = this.el.querySelectorAll("button"); for (var i = 0; i < buttons.length; i++) { // Disable the button buttons[i].disabled = true; } }; // Enables the button _Dropdown.prototype.enable = function () { // Get the buttons var buttons = this.el.querySelectorAll("button"); for (var i = 0; i < buttons.length; i++) { // Enable the button buttons[i].disabled = false; } }; // Gets the value _Dropdown.prototype.getValue = function () { var values = []; // See if the checkboxes exist if (this._cb) { // Get the values var items = (this._cb.getValue().selectedItems); items = typeof (items["length"]) === "number" ? items : [items]; // Parse the items for (var i = 0; i < items.length; i++) { // Add the value values.push(items[i].data); } } else { // Parse the items for (var i = 0; i < this._items.length; i++) { var item = this._items[i]; // Skip disabled items if (item.el.disabled) { continue; } // See if this item is selected if (item.isSelected) { // Add the value values.push(item.props); } } } // Return the value return this.props.multi ? values : values[0]; }; Object.defineProperty(_Dropdown.prototype, "isMulti", { // Returns true if the dropdown allows multiple selections get: function () { return this.props.multi; }, enumerable: false, configurable: true }); Object.defineProperty(_Dropdown.prototype, "isVisible", { // Returns true if the dropdown menu is visible get: function () { return this._floatingUI ? this._floatingUI.isVisible : false; }, enumerable: false, configurable: true }); Object.defineProperty(_Dropdown.prototype, "floatingUI", { // The floating ui menu get: function () { return this._floatingUI; }, enumerable: false, configurable: true }); // Sets the dropdown items _Dropdown.prototype.setItems = function (newItems) { if (newItems === void 0) { newItems = []; } // Update the properties this.props.items = newItems; // See if we are rendering checkboxes if (this._cb) { // Set the items this._cb.setItems(this.generateCheckboxItems()); return; } // Get the menu var menu = this.el.querySelector(".dropdown-menu") || this.el.querySelector("select") || (this.el.classList.contains("dropdown-menu") ? this.el : null); if (menu) { // Clear the menu while (menu.firstChild) { menu.removeChild(menu.firstChild); } // Clear the current value menu.value = ""; // Render the items this.renderItems(); // Parse the items for (var i = 0; i < newItems.length; i++) { var item = newItems[i]; // See if the item is selected if (item.isSelected) { menu.value = item.text; break; } } } // Configure search this.configureSearch(); }; // Sets the label of the dropdown _Dropdown.prototype.setLabel = function (value) { if (value === void 0) { value = ""; } // Get the dropdown var ddl = this.el.querySelector(".dropdown-toggle"); if (ddl) { // Set the inner html ddl.innerHTML = value; ddl.setAttribute("aria-label", value); } }; // Enables/Disables the dark theme _Dropdown.prototype.setTheme = function (isDark) { // Get the menu // See if we are setting the dark theme if (isDark) { // Set the theme this._elMenu.classList.add("dropdown-menu-dark"); } else { // Set the theme this._elMenu.classList.remove("dropdown-menu-dark"); } }; // Sets the button type _Dropdown.prototype.setType = function (ddlType) { // Parse the element types to search for var elTypes = ["button", ".dropdown-toggle"]; var _loop_2 = function (i) { var el = this_2.el.querySelector(elTypes[i]); if (el) { // Parse the class names button_1.ButtonClassNames.parse(function (className) { // Remove the class names el.classList.remove(className); }); // Set the class name var className = button_1.ButtonClassNames.getByType(ddlType); className ? el.classList.add(className) : null; } }; var this_2 = this; for (var i = 0; i < elTypes.length; i++) { _loop_2(i); } }; // Sets the dropdown value _Dropdown.prototype.setValue = function (value) { // Ensure it's an array var values = value == null ? [] : (typeof (value.length) === "number" && typeof (value) !== "string" ? value : [value]); // See if this is a checkbox if (this._cb) { // Set the value this._cb.setValue(this.generateCheckboxValue(value)); return; } // Parse the items for (var i = 0; i < this._items.length; i++) { var item = this._items[i]; // Toggle checked items item.isSelected ? item.toggle() : null; } // Parse the values for (var i = 0; i < values.length; i++) { var value_1 = values[i]; var ddlText = value_1 ? value_1.text || value_1 : null; var ddlValue = value_1 ? value_1.value || value_1 : null; // Parse the items for (var j = 0; j < this._items.length; j++) { var item = this._items[j]; // See if this is the target item if (item.props.value == undefined) { // Select this item if the text matches item.props.text == ddlText ? item.toggle() : null; } else { // Select this item if the value matches item.props.value == ddlValue ? item.toggle() : null; } } } // See if this is a form var ddl = this.el.querySelector("select"); if (ddl) { // Ensure the selected values match the index if (this._items[ddl.selectedIndex] && this._items[ddl.selectedIndex].isSelected == false) { // Select the item this._items[ddl.selectedIndex].toggle(); } } // See if we are updating the label if (this.props.updateLabel) { // See if a value exists if (value && typeof (value) === "string") { // Set the label this.setLabel(value); } // Else, see if label exists else if (this.props.label) { // Set the label this.setLabel(this.props.label); } } // See if a change event exists if (this._initFl && this.props.onChange) { // Execute the change event this.props.onChange(this.getValue()); } }; // Toggles the menu _Dropdown.prototype.toggle = function () { // Toggle the popover this._floatingUI ? this._floatingUI.toggle() : null; }; return _Dropdown; }(base_1.Base)); var Dropdown = function (props, template) { return new _Dropdown(props, template); }; exports.Dropdown = Dropdown;