Skip to content

Instantly share code, notes, and snippets.

@minhhungit
Last active August 10, 2020 12:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save minhhungit/15c1f79e262dca67228584badffeba5b to your computer and use it in GitHub Desktop.
Save minhhungit/15c1f79e262dca67228584badffeba5b to your computer and use it in GitHub Desktop.
serenity-toolbar-dropdown-button
//===================================================
// Copyright @ 2020
// Author : Hung Vo (it.minhhung@gmail.com)
// Time : 2020, June 10
// Description : ToolDropdownButton
//===================================================
namespace YourNamespace.Default {
@Serenity.Decorators.registerClass()
export class YourGrid extends Serenity.EntityGrid<YourRow, any> {
protected getColumnsKey() { return 'Default.YourColumn'; }
protected getDialogType() { return YourDialog; }
protected getIdProperty() { return YourRow.idProperty; }
protected getLocalTextPrefix() { return YourRow.localTextPrefix; }
protected getService() { return YourService.baseUrl; }
protected getInsertPermission() { return YourRow.insertPermission; }
private myToolDropdownButton: J.ToolDropdownButton;
constructor(container: JQuery) {
super(container);
let combineButtons: J.ToolDropdownButtonItem[] = [
{
key: null,
isDropdownHeader: true,
dropdownHeaderTitle: "I am a header 🤩",
cssClass: "text-purple"
},
{
key: "demo",
title: 'Demoooo',
icon: "fa fa-home text-green",
cssClass: "text-green",
onClick: e => Q.alert("button DEMO is pressed"),
},
{
key: null,
isSeparator: true
},
{
key: null,
isDropdownHeader: true,
dropdownHeaderTitle: "Another header.... 😜",
cssClass: "text-green"
},
{
key: "excel-key",
title: 'Excel (disabled by configuring)',
hint: 'Export to excel',
disabled: true,
icon: "fa fa-home text-green",
onClick: e => Q.alert("button EXCEL is pressed"),
},
{
key: "word-key",
title: 'Item without icon', /*=> & nbsp; Option without icon*/
onClick: e => Q.alert("button without icon is pressed"),
},
{
key: null,
isSeparator: true
},
{
key: null,
isDropdownHeader: true,
dropdownHeaderTitle: "3rd header 😜",
cssClass: "text-yellow"
},
{
key: "powerpoint-key",
title: 'Powerpoint // and sooooo loooooooooong option (disabled by helper method)',
icon: "fa fa-long-arrow-right text-red",
cssClass: "text-red",
onClick: e => Q.alert("button POWERPOINT is pressed"),
}
];
this.myToolDropdownButton = new J.ToolDropdownButton(this.toolbar.element.find(".buttons-outer"), combineButtons, {
title: "Export",
icon: "fa fa-print text-blue"
});
}
}
}
//===================================================
// Copyright @ 2020
// Author : Hung Vo (it.minhhung@gmail.com)
// Time : 2020, June 10
// Description : ToolDropdownButton
//===================================================
namespace J {
export interface ToolDropdownButtonOptions {
title?: string;
cssClass?: string;
icon?: string;
disabled?: boolean;
dropdownMenuPosition?: "right";
isDropUp?: boolean;
}
export interface ToolDropdownButtonItem {
key: string;
title?: string;
hint?: string;
cssClass?: string;
icon?: string;
onClick?: any;
isSeparator?: boolean;
disabled?: boolean;
isDropdownHeader?: boolean;
dropdownHeaderTitle?: string;
}
export interface ToolDropdownSideButtonItem {
key: string;
title?: string;
hint?: string;
cssClass?: string;
icon?: string;
onClick?: any;
disabled?: boolean;
}
export class ToolDropdownButton {
public element: JQuery = null;
private isDisabled: boolean = false;
private itemDisablingState: { key: string, disabled: boolean }[] = [];
private options: ToolDropdownButtonOptions;
public constructor(container: JQuery, buttons: ToolDropdownButtonItem[], opt?: ToolDropdownButtonOptions) {
this.options = opt || {};
this.isDisabled = this.options.disabled || false;
this.element = this.buildBaseDropdown();
this.addDropdownItems(buttons);
container.append(this.element);
}
private getDisablingStateItem(key: string): boolean {
if (Q.tryFirst(this.itemDisablingState, x => x.key == key) != null) {
return Q.first(this.itemDisablingState, x => x.key == key).disabled || false;
}
return false;
}
private setDisablingStateItem(key: string, value: boolean) {
if (Q.tryFirst(this.itemDisablingState, x => x.key == key) != null) {
Q.first(this.itemDisablingState, x => x.key == key).disabled = (value || false);
return;
}
this.itemDisablingState.push({ key: key, disabled: (value || false) });
console.log(value);
}
private removeDisablingStateItem(key: string) {
this.itemDisablingState.some((item, idx) => {
if (item.key == key) {
this.itemDisablingState.splice(idx, 1);
return true;
}
});
}
private buildBaseDropdown(): JQuery {
// https://www.w3schools.com/bootstrap/bootstrap_dropdowns.asp
let dropdownTemplate = `<div class="buttons-inner" style="overflow: visible">
<div class="j-tool-dropdown-button tool-button icon-tool-button dropdown ${Q.coalesce(this.options.cssClass, "")} ${this.options.isDropUp ? "dropup" : ""} ${this.isDisabled ? "disabled" : ""}" style="cursor: unset;">
<div class="button-outer dropdown-toggle ${this.isDisabled ? "disabled" : ""}" data-toggle="dropdown" style="cursor: pointer;">
<span class="button-inner">${/*style="border-right: solid 1px #ccc; padding-right: 8px"*/""}
<i class='${this.options.icon}'></i>
${Q.coalesce(this.options.title, "")}
</span>
<i class="caret"></i>
</div>
<ul class="dropdown-menu ${this.options.dropdownMenuPosition == "right" ? "dropdown-menu-right" : ""}"></ul>
</div>
</div>`;
return $(dropdownTemplate);
}
public addDropdownItems(buttons: ToolDropdownButtonItem[]) {
if (buttons && buttons.length > 0) {
buttons.forEach((button, idx) => {
this.addDropdownItem(button);
});
}
}
public addDropdownItem(button: ToolDropdownButtonItem, idx?: number) {
if (!Q.isEmptyOrNull(button.key)) {
if (this.itemDisablingState.map(x => x.key).indexOf(button.key) > -1) {
Q.alert(`Dropdown has existed key: ${button.key}`);
return;
}
this.setDisablingStateItem(button.key, (button.disabled || false));
}
let dropdownItemElement: JQuery;
if (button.isDropdownHeader && !Q.isEmptyOrNull(button.dropdownHeaderTitle)) {
dropdownItemElement = $(`<li class="dropdown-header ${Q.coalesce(button.cssClass, "")}">${button.dropdownHeaderTitle}</li>`);
}
else {
if (button.isSeparator) {
dropdownItemElement = $(`<li class="divider"></li>`);
}
else {
dropdownItemElement = $(`<li j-key="${Q.coalesce(button.key, "")}"
class="${button.disabled ? "disabled" : ""}"
title="${Q.coalesce(button.hint, "")}">
<a href="#" class="${Q.coalesce(button.cssClass, "")}">
<i class="${Q.coalesce(button.icon, "")}"></i>
${button.title}
</a>
</li>`);
dropdownItemElement.click((e) => {
e.preventDefault();
if (this.isDisabled) {
return;
}
let buttonIsDisabled = button.disabled;
if (!Q.isEmptyOrNull(button.key)) {
buttonIsDisabled = this.getDisablingStateItem(button.key);
}
if (buttonIsDisabled) {
return;
}
button.onClick(e);
});
}
}
if (idx === null || typeof idx === 'undefined') {
this.element.find(".dropdown-menu").append(dropdownItemElement);
return;
}
if (idx <= 0) {
this.element.find(".dropdown-menu").prepend(dropdownItemElement);
return;
}
let nbrOfButtons = this.element.find(`.dropdown-menu > li`).length;
if (idx > nbrOfButtons) {
idx = nbrOfButtons;
}
this.element.find(`.dropdown-menu > li:nth-child(${idx})`).after(dropdownItemElement);
}
public replaceDropdownItems(buttons: ToolDropdownButtonItem[]) {
this.element.find(".dropdown-menu").empty();
this.addDropdownItems(buttons);
}
public enableDropdown(enable: boolean) {
let drd = this.element.find(".dropdown").first();
if (drd) {
if (enable) {
if (drd.hasClass("disabled")) {
drd.removeClass("disabled");
}
}
else {
if (!drd.hasClass("disabled")) {
drd.addClass("disabled");
}
}
}
let drdToggle = this.element.find(".dropdown-toggle").first();
if (drdToggle) {
if (enable) {
if (drdToggle.hasClass("disabled")) {
drdToggle.removeClass("disabled");
}
}
else {
if (!drdToggle.hasClass("disabled")) {
drdToggle.addClass("disabled");
}
}
}
this.isDisabled = !enable;
}
public enableDropdownItemByKey(key: string, enable: boolean) {
let drdItem = this.element.find(`.dropdown-menu li[j-key="${key}"]`).first();
if (drdItem) {
if (enable) {
if (drdItem.hasClass("disabled")) {
drdItem.removeClass("disabled");
}
}
else {
if (!drdItem.hasClass("disabled")) {
drdItem.addClass("disabled");
}
}
}
this.setDisablingStateItem(key, !enable);
}
public enableSideButtonByKey(key: string, enable: boolean) {
let tButton = this.element.find(`.tool-button[j-key="${key}"]`).first();
if (tButton) {
if (enable) {
if (tButton.hasClass("disabled")) {
tButton.removeClass("disabled");
}
}
else {
if (!tButton.hasClass("disabled")) {
tButton.addClass("disabled");
}
}
}
this.setDisablingStateItem(key, !enable);
}
public removeDropdownItem(key: string) {
this.element.find(`.dropdown-menu li[j-key="${key}"]`).remove();
this.removeDisablingStateItem(key);
}
public removeSideButtonItem(key: string) {
this.element.find(`.tool-button[j-key="${key}"]`).remove();
this.removeDisablingStateItem(key);
}
public addSideButtonItem(button: ToolDropdownSideButtonItem, idx?: number) {
if (!Q.isEmptyOrNull(button.key)) {
if (this.itemDisablingState.map(x => x.key).indexOf(button.key) > -1) {
Q.alert(`Dropdown has existed key: ${button.key}`);
return;
}
this.setDisablingStateItem(button.key, (button.disabled || false));
}
let sideButtonTemplate = `<div class="tool-button add-button icon-tool-button ${Q.coalesce(button.cssClass, "")} ${button.disabled ? "disabled" : ""}" j-key="${Q.coalesce(button.key, "")}" title="${Q.coalesce(button.key, "")}">
<div class="button-outer">
<span class="button-inner">
<i class="${Q.coalesce(button.icon, "")}"></i> ${Q.coalesce(button.title, "")}
</span>
</div>
</div>`;
let sideButton = $(sideButtonTemplate);
sideButton.click(e => {
e.preventDefault();
let buttonIsDisabled = button.disabled;
if (!Q.isEmptyOrNull(button.key)) {
buttonIsDisabled = this.getDisablingStateItem(button.key);
}
if (buttonIsDisabled) {
return;
}
button.onClick(e);
});
if (idx === null || typeof idx === 'undefined') {
this.element.append(sideButton);
return;
}
if (idx <= 0) {
this.element.prepend(sideButton);
return;
}
let nbrOfButtons = this.element.find(`div.tool-button`).length;
if (idx > nbrOfButtons) {
idx = nbrOfButtons;
}
this.element.find(`div.tool-button:nth-child(${idx})`).after(sideButton);
}
}
}
//===================================================
// Copyright @ 2020
// Author : Hung Vo (it.minhhung@gmail.com)
// Time : 2020, June 10
// Description : ToolDropdownButton
//===================================================
/* ToolDropdownButtonGenerator */
.j-tool-dropdown-button {
&:hover {
background-color: unset !important;
}
.disabled {
cursor: default !important;
}
.dropdown-toggle {
.caret {
margin-left: 6px;
margin-right: 4px;
}
}
.dropdown-menu{
border-color: #ccc;
}
.dropdown-menu li {
a {
/*padding: 5px 10px 5px 12px;*/
color: #333;
font-size: 13px;
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
}
.dropdown-menu li.disabled {
a, i {
color: gray !important;
}
}
}
@rrsc
Copy link

rrsc commented Jul 5, 2020

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment