Add Notices API. (#1022)

* Add notices API.

* Change to spreading syntax

* Misc

* Fix typo & add Node type

* Remove unnecessary check & set default timeout to 0
This commit is contained in:
Strencher 2022-01-23 19:58:17 +01:00 committed by GitHub
parent 1dbf3e90a4
commit 8176425039
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 195 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import DiscordModules from "./discordmodules";
import DataStore from "./datastore";
import DOMManager from "./dommanager";
import Toasts from "../ui/toasts";
import Notices from "../ui/notices";
import Modals from "../ui/modals";
import PluginManager from "./pluginmanager";
import ThemeManager from "./thememanager";
@ -113,6 +114,19 @@ BdApi.showToast = function(content, options = {}) {
Toasts.show(content, options);
};
/**
* Show a notice above discord's chat layer.
* @param {string|Node} content Content of the notice
* @param {object} options Options for the notice.
* @param {string} [options.type="info" | "error" | "warning" | "success"] Type for the notice. Will affect the color.
* @param {Array<{label: string, onClick: (immediately?: boolean = false) => void}>} [options.buttons] Buttons that should be added next to the notice text.
* @param {number} [options.timeout=10000] Timeout until the notice is closed. Won't fire if it's set to 0;
* @returns {(immediately?: boolean = false) => void}
*/
BdApi.showNotice = function (content, options = {}) {
return Notices.show(content, options);
};
// Finds module
BdApi.findModule = function(filter) {
return WebpackModules.getModule(filter);

View File

@ -0,0 +1,94 @@
.bd-notice-success {
--color: #3ba55d;
}
.bd-notice-error {
--color: #ED4245;
}
.bd-notice-info {
--color: #4A8FE1;
}
.bd-notice-warning {
--color: #FAA81A;
}
.bd-notice-closing {
transition: height 400ms ease;
height: 0 !important;
}
@keyframes bd-open-notice {
from {
height: 0;
}
}
.bd-notice {
animation: bd-open-notice 400ms ease;
overflow: hidden;
height: 36px;
font-size: 14px;
line-height: 36px;
font-weight: 500;
text-align: center;
position: relative;
padding-left: 4px;
padding-right: 28px;
z-index: 101;
flex-shrink: 0;
flex-grow: 0;
box-shadow: var(--elevation-low);
color: #fff;
background: var(--color, var(--brand-experiment-600, #3C45A5));
}
.bd-notice:first-child {
border-radius: 8px 0 0;
}
.bd-notice-close {
position: absolute;
top: 0;
right: 0;
width: 36px;
height: 36px;
background: url(https://discord.com/assets/7731c77d99babca1a8faec204d98c380.svg) no-repeat;
background-position: 50% 55%;
background-size: 10px 10px;
opacity: .5;
transition: opacity .2s;
cursor: pointer;
-webkit-app-region: no-drag;
}
.bd-notice-button {
font-size: 14px;
font-weight: 500;
position: relative;
top: 6px;
border: 1px solid;
color: #fff;
border-radius: 3px;
height: 24px;
padding: 0 10px;
box-sizing: border-box;
display: inline-block;
vertical-align: top;
margin-left: 10px;
line-height: 22px;
transition: background-color .2s ease,color .2s ease,border-color .2s ease;
-webkit-app-region: no-drag;
border-color: #fff;
background: transparent;
}
.bd-notice-button:hover {
color: var(--color, var(--background-mobile-primary));
background: #fff;
}
.bd-notice-close:hover {
opacity: 1;
}

View File

@ -0,0 +1,87 @@
import {WebpackModules} from "modules";
export default class Notices {
static get baseClass() {return this.__baseClass || (this.__baseClass = WebpackModules.getByProps("container", "base")?.base);}
/** Shorthand for `type = "info"` for {@link module:Notices.show} */
static info(content, options = {}) {return this.show(content, Object.assign({}, options, {type: "info"}));}
/** Shorthand for `type = "warning"` for {@link module:Notices.show} */
static warn(content, options = {}) {return this.show(content, Object.assign({}, options, {type: "warning"}));}
/** Shorthand for `type = "error"` for {@link module:Notices.show} */
static error(content, options = {}) {return this.show(content, Object.assign({}, options, {type: "error"}));}
/** Shorthand for `type = "success"` for {@link module:Notices.show} */
static success(content, options = {}) {return this.show(content, Object.assign({}, options, {type: "success"}));}
static createElement(type, options = {}, ...children) {
const element = document.createElement(type);
Object.assign(element, options);
const filteredChildren = children.filter((n) => n);
if (filteredChildren.length > 0) element.append(...filteredChildren);
return element;
}
static joinClassNames(...classNames) {
return classNames.filter((n) => n).join(" ");
}
/**
* Show a notice above discord's chat layer.
* @param {string} content Content of the notice
* @param {object} options Options for the notice.
* @param {string} [options.type="info" | "error" | "warning" | "success"] Type for the notice. Will affect the color.
* @param {Array<{label: string, onClick: (immediately?: boolean = false) => void}>} [options.buttons] Buttons that should be added next to the notice text.
* @param {number} [options.timeout=10000] Timeout until the toast is closed. Won't fire if it's set to 0;
* @returns {(immediately?: boolean = false) => void}
*/
static show(content, options = {}) {
const {type, buttons = [], timeout = 0} = options;
const haveContainer = this.ensureContainer();
if (!haveContainer) return;
const closeNotification = function (immediately = false) {
// Immediately remove the notice without adding the closing class.
if (immediately) return noticeElement.remove();
noticeElement.classList.add("bd-notice-closing");
setTimeout(() => {noticeElement.remove();}, 300);
};
const noticeElement = this.createElement("div", {
className: this.joinClassNames("bd-notice", type && `bd-notice-${type}`),
}, this.createElement("div", {
className: "bd-notice-close",
onclick: closeNotification.bind(null, false)
}), this.createElement("span", {
className: "bd-notice-content"
}, content), ...buttons.map((button) => {
if (!button || !button.label || typeof(button.onClick) !== "function") return null;
return this.createElement("button", {
className: "bd-notice-button",
onclick: button.onClick.bind(null, closeNotification)
}, button.label);
}));
document.getElementById("bd-notices").appendChild(noticeElement);
if (timeout > 0) {
setTimeout(closeNotification, timeout);
}
return closeNotification;
}
static ensureContainer() {
if (document.getElementById("bd-notices")) return true;
const container = document.querySelector(`.${this.baseClass}`);
if (!container) return false;
const noticeContainer = this.createElement("div", {
id: "bd-notices"
});
container.prepend(noticeContainer);
return true;
}
}