mirror of
https://github.com/ONLYOFFICE/onlyoffice.github.io.git
synced 2026-04-07 14:04:30 +08:00
525 lines
16 KiB
JavaScript
525 lines
16 KiB
JavaScript
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2025
|
|
*
|
|
* This program is a free software product. You can redistribute it and/or
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
* of any third-party rights.
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
|
* street, Riga, Latvia, EU, LV-1050.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
*
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
* well as technical writing content are licensed under the terms of the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
*/
|
|
|
|
// @ts-check
|
|
|
|
/// <reference path="./types.js" />
|
|
|
|
/**
|
|
* @param {string | HTMLButtonElement} button
|
|
* @param {ButtonOptionsType} [options]
|
|
*/
|
|
function Button(button, options) {
|
|
const self = this;
|
|
if (typeof button === "string") {
|
|
var temp = document.getElementById(button);
|
|
if (temp instanceof HTMLButtonElement) {
|
|
button = temp;
|
|
}
|
|
}
|
|
if (button instanceof HTMLButtonElement) {
|
|
this._button = button;
|
|
} else {
|
|
throw new Error("Invalid button");
|
|
}
|
|
|
|
this._container = document.createElement("div");
|
|
this._options = options || {};
|
|
|
|
this._options.text = this._options.text || button.textContent.trim();
|
|
this._options.type = this._options.type || "button";
|
|
this._options.variant = this._options.variant || "primary";
|
|
this._options.size = this._options.size || "medium";
|
|
this._options.iconPosition = this._options.iconPosition || "left";
|
|
|
|
this.isLoading = false;
|
|
this._originalText = this._options.text;
|
|
/** @type {Function[]} */
|
|
this._subscribers = [];
|
|
/** @type {ButtonBoundHandlesType} */
|
|
this._boundHandles = {
|
|
click: function (e) {
|
|
self._handleClick(e);
|
|
},
|
|
mouseenter: function () {
|
|
self._handleMouseEnter();
|
|
},
|
|
mouseleave: function () {
|
|
self._handleMouseLeave();
|
|
},
|
|
focus: function () {
|
|
self._handleFocus();
|
|
},
|
|
blur: function () {
|
|
self._handleBlur();
|
|
},
|
|
keydown: function (e) {
|
|
self._handleKeydown(e);
|
|
},
|
|
};
|
|
|
|
this._createDOM();
|
|
this._bindEvents();
|
|
this.updateState();
|
|
}
|
|
|
|
Button.prototype = /** @lends Button.prototype */ {
|
|
constructor: Button,
|
|
|
|
/**
|
|
* @type {HTMLButtonElement}
|
|
*/
|
|
// @ts-ignore
|
|
_button: null,
|
|
/**
|
|
* @type {HTMLSpanElement | null}
|
|
* @private
|
|
*/
|
|
_buttonText: null,
|
|
/**
|
|
* @type {HTMLSpanElement | null}
|
|
* @private
|
|
*/
|
|
_spinner: null,
|
|
/**
|
|
* @private
|
|
* @type {HTMLSpanElement | null}
|
|
*/
|
|
_badgeElement: null,
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_createDOM: function () {
|
|
var parent = this._button.parentNode;
|
|
|
|
var fragment = document.createDocumentFragment();
|
|
fragment.appendChild(this._container);
|
|
this._container.className += " custom-button-container";
|
|
|
|
this._button.className += " custom-button";
|
|
this._button.className += " custom-button-" + this._options.variant;
|
|
this._button.className += " custom-button-" + this._options.size;
|
|
|
|
if (this._options.disabled) {
|
|
this._button.className += " custom-button-disabled";
|
|
}
|
|
if (this._options.loading) {
|
|
this._container.className += " custom-button-loading";
|
|
}
|
|
if (this._options.type) {
|
|
this._button.type = this._options.type;
|
|
}
|
|
|
|
if (this._options.tooltip) {
|
|
this._button.title = this._options.tooltip;
|
|
}
|
|
|
|
if (this._options.disabled) {
|
|
this._button.disabled = true;
|
|
}
|
|
|
|
if (this._options.text) {
|
|
this._button.textContent = "";
|
|
this._buttonText = document.createElement("span");
|
|
this._buttonText.className = "custom-button-text";
|
|
this._buttonText.textContent = this._options.text || "";
|
|
|
|
if (this._options.icon) {
|
|
var iconSpan = document.createElement("span");
|
|
iconSpan.className = "custom-button-icon";
|
|
|
|
if (this._options.iconPosition === "left") {
|
|
iconSpan.className += " custom-button-icon-left";
|
|
this._button.appendChild(iconSpan);
|
|
this._button.appendChild(this._buttonText);
|
|
} else {
|
|
iconSpan.className += " custom-button-icon-right";
|
|
this._button.appendChild(this._buttonText);
|
|
this._button.appendChild(iconSpan);
|
|
}
|
|
iconSpan.innerHTML = this._options.icon;
|
|
} else {
|
|
this._button.appendChild(this._buttonText);
|
|
}
|
|
}
|
|
|
|
if (this._options.loading) {
|
|
this._spinner = document.createElement("span");
|
|
this._spinner.className = "custom-button-spinner";
|
|
this._button.appendChild(this._spinner);
|
|
}
|
|
|
|
if (this._options.badge) {
|
|
this._badgeElement = document.createElement("span");
|
|
this._badgeElement.className = "custom-button-badge";
|
|
this._badgeElement.textContent = this._options.badge;
|
|
this._button.appendChild(this._badgeElement);
|
|
}
|
|
|
|
if (parent) {
|
|
parent.insertBefore(fragment, this._button);
|
|
}
|
|
this._container.appendChild(this._button);
|
|
},
|
|
/** @private */
|
|
_bindEvents: function () {
|
|
var self = this;
|
|
|
|
this._button.addEventListener("click", this._boundHandles.click);
|
|
|
|
this._button.addEventListener(
|
|
"mouseenter",
|
|
this._boundHandles.mouseenter
|
|
);
|
|
|
|
this._button.addEventListener(
|
|
"mouseleave",
|
|
this._boundHandles.mouseleave
|
|
);
|
|
|
|
this._button.addEventListener("focus", this._boundHandles.focus);
|
|
|
|
this._button.addEventListener("blur", this._boundHandles.blur);
|
|
|
|
this._button.addEventListener("keydown", this._boundHandles.keydown);
|
|
},
|
|
|
|
/**
|
|
* @param {Event} e
|
|
* @private
|
|
*/
|
|
_handleClick: function (e) {
|
|
if (this._options.disabled || this.isLoading) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return;
|
|
}
|
|
this.triggerClickEvent(e);
|
|
},
|
|
/** @private */
|
|
_handleMouseEnter: function () {
|
|
var classes = this._button.className.split(" ");
|
|
if (classes.indexOf("custom-button-hover") === -1) {
|
|
this._button.className += " custom-button-hover";
|
|
}
|
|
this.triggerEvent("mouseenter");
|
|
},
|
|
/** @private */
|
|
_handleMouseLeave: function () {
|
|
this._button.className = this._button.className
|
|
.split(" ")
|
|
.filter(function (cls) {
|
|
return cls !== "custom-button-hover";
|
|
})
|
|
.join(" ");
|
|
this.triggerEvent("mouseleave");
|
|
},
|
|
/** @private */
|
|
_handleFocus: function () {
|
|
var classes = this._button.className.split(" ");
|
|
if (classes.indexOf("custom-button-focused") === -1) {
|
|
this._button.className += " custom-button-focused";
|
|
}
|
|
this.triggerEvent("focus");
|
|
},
|
|
/** @private */
|
|
_handleBlur: function () {
|
|
this._button.className = this._button.className
|
|
.split(" ")
|
|
.filter(function (cls) {
|
|
return cls !== "custom-button-focused";
|
|
})
|
|
.join(" ");
|
|
this.triggerEvent("blur");
|
|
},
|
|
/**
|
|
* @param {KeyboardEvent} e
|
|
* @private
|
|
*/
|
|
_handleKeydown: function (e) {
|
|
var key = e.key || e.keyCode;
|
|
|
|
if (key === " " || key === "Enter" || key === 32 || key === 13) {
|
|
if (this._button.tagName === "BUTTON") {
|
|
} else {
|
|
e.preventDefault();
|
|
this._button.click();
|
|
}
|
|
} else if (key === "Escape" || key === 27) {
|
|
this._button.blur();
|
|
}
|
|
|
|
this.triggerEvent("keydown", { key: key });
|
|
},
|
|
/** @param {function(InputEventType): void} callback */
|
|
subscribe: function (callback) {
|
|
var self = this;
|
|
this._subscribers.push(callback);
|
|
|
|
return {
|
|
unsubscribe: function () {
|
|
self._subscribers = self._subscribers.filter(function (cb) {
|
|
return cb !== callback;
|
|
});
|
|
},
|
|
};
|
|
},
|
|
/** @param {ButtonOptionsType['text']} text */
|
|
setText: function (text) {
|
|
if (typeof text === "undefined") return;
|
|
this._options.text = text;
|
|
if (!this._buttonText) {
|
|
this._buttonText = document.createElement("span");
|
|
this._buttonText.className = "custom-button-text";
|
|
this._buttonText.textContent = "";
|
|
this._button.appendChild(this._buttonText);
|
|
}
|
|
this._buttonText.textContent = text;
|
|
},
|
|
/**
|
|
* @param {string} icon
|
|
* @param {ButtonOptionsType['iconPosition']} position
|
|
*/
|
|
setIcon: function (icon, position) {
|
|
this._options.icon = icon;
|
|
this._options.iconPosition = position || "left";
|
|
},
|
|
/** @param {ButtonOptionsType['badge']} badge */
|
|
setBadge: function (badge) {
|
|
if (typeof badge === "undefined") return;
|
|
this._options.badge = badge;
|
|
if (this._badgeElement) {
|
|
this._badgeElement.textContent = badge;
|
|
this._badgeElement.style.display = badge ? "flex" : "none";
|
|
} else if (badge) {
|
|
// rebuild()
|
|
}
|
|
},
|
|
/** @param {ButtonOptionsType['variant']} variant */
|
|
setVariant: function (variant) {
|
|
if (typeof variant === "undefined") return;
|
|
|
|
var oldClass = "custom-button-" + this._options.variant;
|
|
var newClass = "custom-button-" + variant;
|
|
|
|
this._button.className =
|
|
this._button.className
|
|
.split(" ")
|
|
.filter(function (cls) {
|
|
return cls !== oldClass;
|
|
})
|
|
.join(" ") +
|
|
" " +
|
|
newClass;
|
|
|
|
this._options.variant = variant;
|
|
},
|
|
|
|
/** @param {ButtonOptionsType['size']} size */
|
|
setSize: function (size) {
|
|
if (typeof size === "undefined") return;
|
|
|
|
var oldClass = "custom-button-" + this._options.size;
|
|
var newClass = "custom-button-" + size;
|
|
|
|
this._button.className =
|
|
this._button.className
|
|
.split(" ")
|
|
.filter(function (cls) {
|
|
return cls !== oldClass;
|
|
})
|
|
.join(" ") +
|
|
" " +
|
|
newClass;
|
|
|
|
this._options.size = size;
|
|
},
|
|
|
|
enable: function () {
|
|
this._options.disabled = false;
|
|
this._button.disabled = false;
|
|
|
|
this._button.className = this._button.className
|
|
.split(" ")
|
|
.filter(function (cls) {
|
|
return cls !== "custom-button-disabled";
|
|
})
|
|
.join(" ");
|
|
},
|
|
|
|
disable: function () {
|
|
this._options.disabled = true;
|
|
this._button.disabled = true;
|
|
|
|
var classes = this._button.className.split(" ");
|
|
if (classes.indexOf("custom-button-disabled") === -1) {
|
|
this._button.className += " custom-button-disabled";
|
|
}
|
|
},
|
|
|
|
startLoading: function () {
|
|
this.isLoading = true;
|
|
if (typeof this._options.text !== "undefined")
|
|
this._originalText = this._options.text;
|
|
|
|
var containerClasses = this._container.className.split(" ");
|
|
if (containerClasses.indexOf("custom-button-loading") === -1) {
|
|
this._container.className += " custom-button-loading";
|
|
}
|
|
|
|
if (this._spinner) {
|
|
this._spinner.style.display = "inline-block";
|
|
}
|
|
|
|
if (this._buttonText) {
|
|
this._buttonText.textContent = "Loading...";
|
|
}
|
|
|
|
this._button.disabled = true;
|
|
},
|
|
|
|
stopLoading: function () {
|
|
this.isLoading = false;
|
|
|
|
this._container.className = this._container.className
|
|
.split(" ")
|
|
.filter(function (cls) {
|
|
return cls !== "custom-button-loading";
|
|
})
|
|
.join(" ");
|
|
|
|
if (this._spinner) {
|
|
this._spinner.style.display = "none";
|
|
}
|
|
|
|
if (this._buttonText) {
|
|
this._buttonText.textContent = this._originalText;
|
|
}
|
|
|
|
this._button.disabled = !!this._options.disabled;
|
|
},
|
|
/** @param {ButtonOptionsType['tooltip']} tooltip */
|
|
setTooltip: function (tooltip) {
|
|
if (typeof tooltip === "undefined") return;
|
|
this._options.tooltip = tooltip;
|
|
this._button.title = tooltip || "";
|
|
},
|
|
/** @param {Event} e */
|
|
triggerClickEvent: function (e) {
|
|
var detail = {
|
|
originalEvent: e,
|
|
button: this,
|
|
};
|
|
|
|
this._subscribers.forEach(function (cb) {
|
|
cb({
|
|
type: "button:click",
|
|
detail: detail,
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @param {"click"|"keydown" | "mouseenter" | "mouseleave" | "focus" | "blur"} eventName
|
|
* @param {any} [detail]
|
|
*/
|
|
triggerEvent: function (eventName, detail) {
|
|
detail = detail || {};
|
|
detail.button = this;
|
|
|
|
this._subscribers.forEach(function (cb) {
|
|
cb({
|
|
type: "button:" + eventName,
|
|
detail: detail,
|
|
});
|
|
});
|
|
},
|
|
|
|
updateState: function () {
|
|
if (this._options.disabled) {
|
|
this.disable();
|
|
} else {
|
|
this.enable();
|
|
}
|
|
|
|
if (this._options.loading) {
|
|
this.startLoading();
|
|
}
|
|
},
|
|
|
|
destroy: function () {
|
|
this._subscribers = [];
|
|
|
|
if (this._boundHandles) {
|
|
try {
|
|
this._button.removeEventListener(
|
|
"click",
|
|
this._boundHandles.click
|
|
);
|
|
this._button.removeEventListener(
|
|
"mouseenter",
|
|
this._boundHandles.mouseenter
|
|
);
|
|
this._button.removeEventListener(
|
|
"mouseleave",
|
|
this._boundHandles.mouseleave
|
|
);
|
|
this._button.removeEventListener(
|
|
"focus",
|
|
this._boundHandles.focus
|
|
);
|
|
this._button.removeEventListener(
|
|
"blur",
|
|
this._boundHandles.blur
|
|
);
|
|
this._button.removeEventListener(
|
|
"keydown",
|
|
this._boundHandles.keydown
|
|
);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
this._container.innerHTML = "";
|
|
|
|
var containerClasses = this._container.className
|
|
.split(" ")
|
|
.filter(function (cls) {
|
|
return cls !== "custom-button-container";
|
|
})
|
|
.join(" ");
|
|
this._container.className = containerClasses;
|
|
},
|
|
};
|