gd-bs
Version:
Bootstrap JavaScript, TypeScript and Web Components library.
830 lines (829 loc) • 34.6 kB
JavaScript
"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;