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:
parent
1dbf3e90a4
commit
8176425039
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue