Adds facelist for addon content
- Redesign of addon cards - Addon settings moved to modals - Fixes error with extra title in PS - Fixes error with pagination in PS - Fixes several issues with detached windows
This commit is contained in:
parent
67c29bbce5
commit
5b8f2f2de3
|
@ -33,7 +33,6 @@ export default [
|
||||||
shown: false,
|
shown: false,
|
||||||
settings: [
|
settings: [
|
||||||
{type: "switch", id: "addonErrors", value: true},
|
{type: "switch", id: "addonErrors", value: true},
|
||||||
{type: "switch", id: "autoScroll", value: true},
|
|
||||||
{type: "switch", id: "autoReload", value: true},
|
{type: "switch", id: "autoReload", value: true},
|
||||||
{type: "dropdown", id: "editAction", value: "detached", options: [{value: "detached"}, {value: "system"}]}
|
{type: "dropdown", id: "editAction", value: "detached", options: [{value: "detached"}, {value: "system"}]}
|
||||||
]
|
]
|
||||||
|
|
|
@ -71,10 +71,6 @@ export default {
|
||||||
name: "Show Addon Errors",
|
name: "Show Addon Errors",
|
||||||
note: "Shows a modal with plugin/theme errors"
|
note: "Shows a modal with plugin/theme errors"
|
||||||
},
|
},
|
||||||
autoScroll: {
|
|
||||||
name: "Scroll To Settings",
|
|
||||||
note: "Auto-scrolls to a plugin's settings when the button is clicked (only if out of view)"
|
|
||||||
},
|
|
||||||
autoReload: {
|
autoReload: {
|
||||||
name: "Automatic Loading",
|
name: "Automatic Loading",
|
||||||
note: "Automatically loads, reloads, and unloads plugins and themes"
|
note: "Automatically loads, reloads, and unloads plugins and themes"
|
||||||
|
@ -223,6 +219,7 @@ export default {
|
||||||
couldNotDisable: "{{name}} could not be disabled.",
|
couldNotDisable: "{{name}} could not be disabled.",
|
||||||
couldNotStart: "{{name}} could not be started.",
|
couldNotStart: "{{name}} could not be started.",
|
||||||
couldNotStop: "{{name}} could not be stopped.",
|
couldNotStop: "{{name}} could not be stopped.",
|
||||||
|
settingsError: "Could not open settings for {{name}}",
|
||||||
methodError: "{{method}} could not be fired.",
|
methodError: "{{method}} could not be fired.",
|
||||||
unknownAuthor: "Unknown Author",
|
unknownAuthor: "Unknown Author",
|
||||||
noDescription: "Description not provided.",
|
noDescription: "Description not provided.",
|
||||||
|
@ -276,6 +273,7 @@ export default {
|
||||||
Modals: {
|
Modals: {
|
||||||
confirmAction: "Are You Sure?",
|
confirmAction: "Are You Sure?",
|
||||||
okay: "Okay",
|
okay: "Okay",
|
||||||
|
done: "Done",
|
||||||
cancel: "Cancel",
|
cancel: "Cancel",
|
||||||
nevermind: "Nevermind",
|
nevermind: "Nevermind",
|
||||||
close: "Close",
|
close: "Close",
|
||||||
|
|
|
@ -46,6 +46,7 @@ export default class AddonManager {
|
||||||
this.timeCache = {};
|
this.timeCache = {};
|
||||||
this.addonList = [];
|
this.addonList = [];
|
||||||
this.state = {};
|
this.state = {};
|
||||||
|
this.windows = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
|
@ -343,9 +344,12 @@ export default class AddonManager {
|
||||||
const fullPath = path.resolve(this.addonFolder, addon.filename);
|
const fullPath = path.resolve(this.addonFolder, addon.filename);
|
||||||
const content = fs.readFileSync(fullPath).toString();
|
const content = fs.readFileSync(fullPath).toString();
|
||||||
|
|
||||||
|
if (this.windows.has(fullPath)) return;
|
||||||
|
this.windows.add(fullPath);
|
||||||
|
|
||||||
const editorRef = React.createRef();
|
const editorRef = React.createRef();
|
||||||
const editor = React.createElement(AddonEditor, {
|
const editor = React.createElement(AddonEditor, {
|
||||||
id: "bd-floating-editor-" + addon.name,
|
id: "bd-floating-editor-" + addon.id,
|
||||||
ref: editorRef,
|
ref: editorRef,
|
||||||
content: content,
|
content: content,
|
||||||
save: this.saveAddon.bind(this, addon),
|
save: this.saveAddon.bind(this, addon),
|
||||||
|
@ -355,14 +359,14 @@ export default class AddonManager {
|
||||||
|
|
||||||
FloatingWindows.open({
|
FloatingWindows.open({
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
this.isDetached = false;
|
this.windows.delete(fullPath);
|
||||||
},
|
},
|
||||||
onResize: () => {
|
onResize: () => {
|
||||||
if (!editorRef || !editorRef.current || !editorRef.current.resize) return;
|
if (!editorRef || !editorRef.current || !editorRef.current.resize) return;
|
||||||
editorRef.current.resize();
|
editorRef.current.resize();
|
||||||
},
|
},
|
||||||
title: addon.name,
|
title: addon.name,
|
||||||
id: content.id,
|
id: "bd-floating-window-" + addon.id,
|
||||||
className: "floating-addon-window",
|
className: "floating-addon-window",
|
||||||
height: 470,
|
height: 470,
|
||||||
width: 410,
|
width: 410,
|
||||||
|
|
|
@ -60,7 +60,7 @@ export default new class DataStore {
|
||||||
const newSettings = {
|
const newSettings = {
|
||||||
general: {publicServers: oldSettings["bda-gs-1"], voiceDisconnect: oldSettings["bda-dc-0"], classNormalizer: oldSettings["fork-ps-4"], showToasts: oldSettings["fork-ps-2"]},
|
general: {publicServers: oldSettings["bda-gs-1"], voiceDisconnect: oldSettings["bda-dc-0"], classNormalizer: oldSettings["fork-ps-4"], showToasts: oldSettings["fork-ps-2"]},
|
||||||
appearance: {twentyFourHour: oldSettings["bda-gs-6"], voiceMode: oldSettings["bda-gs-4"], minimalMode: oldSettings["bda-gs-2"], hideChannels: oldSettings["bda-gs-3"], darkMode: oldSettings["bda-gs-5"], coloredText: oldSettings["bda-gs-7"]},
|
appearance: {twentyFourHour: oldSettings["bda-gs-6"], voiceMode: oldSettings["bda-gs-4"], minimalMode: oldSettings["bda-gs-2"], hideChannels: oldSettings["bda-gs-3"], darkMode: oldSettings["bda-gs-5"], coloredText: oldSettings["bda-gs-7"]},
|
||||||
addons: {addonErrors: oldSettings["fork-ps-1"], autoScroll: oldSettings["fork-ps-3"], autoReload: oldSettings["fork-ps-5"]},
|
addons: {addonErrors: oldSettings["fork-ps-1"], autoReload: oldSettings["fork-ps-5"]},
|
||||||
developer: {debuggerHotkey: oldSettings["bda-gs-8"], copySelector: oldSettings["fork-dm-1"], reactDevTools: oldSettings.reactDevTools}
|
developer: {debuggerHotkey: oldSettings["bda-gs-8"], copySelector: oldSettings["fork-dm-1"], reactDevTools: oldSettings.reactDevTools}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -241,4 +241,8 @@
|
||||||
.bd-pagination button[disabled] {
|
.bd-pagination button[disabled] {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-pagination + .bd-settings-title {
|
||||||
|
margin-top: 20px;
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 3px 6px;
|
padding: 3px 6px;
|
||||||
|
transition: opacity 250ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-button:hover {
|
.bd-button:hover {
|
||||||
|
@ -23,4 +24,24 @@
|
||||||
|
|
||||||
.bd-button.bd-button-success:active {
|
.bd-button.bd-button-success:active {
|
||||||
background-color: rgb(46, 154, 74);
|
background-color: rgb(46, 154, 74);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-button.bd-button-danger {
|
||||||
|
background-color: #f04747;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-button.bd-button-danger:hover {
|
||||||
|
background-color: rgb(237, 42, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-button.bd-button-danger:active {
|
||||||
|
background-color: rgb(230, 18, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-button-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-button-disabled:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
@import "./ui/*";
|
@import "./ui/*";
|
||||||
@import "./buttons.css";
|
@import "./buttons.css";
|
||||||
@import "./spinner.css";
|
@import "./spinner.css";
|
||||||
|
@import "./search.css";
|
||||||
|
|
||||||
.bd-chat-badge {
|
.bd-chat-badge {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
.bd-search-wrapper {
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
outline: none;
|
||||||
|
border: 0;
|
||||||
|
background-color: var(--background-tertiary);
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-search {
|
||||||
|
padding: 2px 3px;
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
color: var(--text-normal);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-search::-webkit-input-placeholder {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-search-wrapper > svg {
|
||||||
|
margin-right: 2px;
|
||||||
|
fill: var(--interactive-normal);
|
||||||
|
}
|
|
@ -27,8 +27,7 @@
|
||||||
.bd-addon-list .bd-addon-card {
|
.bd-addon-list .bd-addon-card {
|
||||||
max-height: 175px;
|
max-height: 175px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding: 5px 8px;
|
padding: 12px;
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
@ -36,13 +35,11 @@
|
||||||
.theme-dark .bd-addon-list .bd-addon-card {
|
.theme-dark .bd-addon-list .bd-addon-card {
|
||||||
background-color: rgba(32, 34, 37, 0.6);
|
background-color: rgba(32, 34, 37, 0.6);
|
||||||
color: #f6f6f7;
|
color: #f6f6f7;
|
||||||
border-color: #202225;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-light .bd-addon-list .bd-addon-card {
|
.theme-light .bd-addon-list .bd-addon-card {
|
||||||
background-color: #f8f9f9;
|
background-color: #f8f9f9;
|
||||||
color: #4f545c;
|
color: #4f545c;
|
||||||
border-color: #dcddde;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-addon-list .bd-addon-card.settings-open {
|
.bd-addon-list .bd-addon-card.settings-open {
|
||||||
|
@ -51,24 +48,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-addon-list .bd-addon-header {
|
.bd-addon-list .bd-addon-header {
|
||||||
font-size: 12px;
|
font-size: 14px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-bottom: 5px;
|
|
||||||
border-bottom: 1px solid transparent;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-dark .bd-addon-list .bd-addon-header {
|
.theme-dark .bd-addon-list .bd-addon-header {
|
||||||
color: #f6f6f7;
|
color: #f6f6f7;
|
||||||
border-bottom-color: rgba(114, 118, 125, 0.3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-light .bd-addon-list .bd-addon-header {
|
.theme-light .bd-addon-list .bd-addon-header {
|
||||||
color: #4f545c;
|
color: #4f545c;
|
||||||
border-bottom-color: rgba(185, 187, 190, 0.3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-addon-list .bd-description {
|
.bd-addon-list .bd-description {
|
||||||
|
@ -77,6 +70,7 @@
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
line-height: 1.125em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-dark .bd-addon-list .bd-description {
|
.theme-dark .bd-addon-list .bd-description {
|
||||||
|
@ -99,7 +93,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-top: 5px;
|
padding-top: 8px;
|
||||||
border-top: 1px solid transparent;
|
border-top: 1px solid transparent;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
@ -112,15 +106,6 @@
|
||||||
border-top-color: rgba(185, 187, 190, 0.3);
|
border-top-color: rgba(185, 187, 190, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-addon-list .bd-footer button {
|
|
||||||
padding: 3px 16px;
|
|
||||||
transition: opacity 250ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bd-addon-list .bd-footer button:disabled {
|
|
||||||
opacity: 0.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bd-addon-list .bd-footer a {
|
.bd-addon-list .bd-footer a {
|
||||||
color: #3e82e5;
|
color: #3e82e5;
|
||||||
}
|
}
|
||||||
|
@ -129,44 +114,48 @@
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-controls + .bd-addon-list {
|
.bd-controls > .bd-addon-button {
|
||||||
margin-top: 10px;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-addon-button {
|
.bd-links .bd-addon-button + .bd-addon-button {
|
||||||
cursor: pointer;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-addon-button + .bd-addon-button {
|
.bd-controls > .bd-addon-button svg {
|
||||||
margin-left: 5px;
|
fill: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-search-wrapper {
|
.bd-controls > .bd-addon-button:first-of-type {
|
||||||
padding: 3px;
|
border-radius: 5px 0 0 5px;
|
||||||
border-radius: 3px;
|
}
|
||||||
outline: none;
|
|
||||||
border: 0;
|
.bd-controls > .bd-addon-button:last-of-type {
|
||||||
background-color: var(--background-tertiary);
|
border-radius: 0 5px 5px 0;
|
||||||
color: var(--text-muted);
|
}
|
||||||
|
|
||||||
|
.bd-addon-list .bd-footer .bd-links,
|
||||||
|
.bd-addon-list .bd-footer .bd-links a,
|
||||||
|
.bd-addon-list .bd-footer .bd-addon-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-search {
|
.bd-addon-list .bd-footer .bd-links .bd-addon-button {
|
||||||
padding: 2px 3px;
|
height: 24px;
|
||||||
background: none;
|
|
||||||
border: 0;
|
|
||||||
color: var(--text-normal);
|
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-search::-webkit-input-placeholder {
|
.bd-controls + .bd-addon-list {
|
||||||
color: var(--text-muted);
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-search-wrapper > svg {
|
.bd-links .bd-addon-button svg {
|
||||||
margin-right: 2px;
|
opacity: 0.7;
|
||||||
fill: var(--interactive-normal);
|
}
|
||||||
|
|
||||||
|
.bd-links .bd-addon-button:active svg,
|
||||||
|
.bd-links .bd-addon-button:hover svg {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bd-addon-controls {
|
.bd-addon-controls {
|
||||||
|
@ -196,4 +185,28 @@
|
||||||
.settings-open .bd-close {
|
.settings-open .bd-close {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
float: right;
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-addon-modal {
|
||||||
|
min-height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-addon-modal-settings {
|
||||||
|
/* padding: 16px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-addon-modal-footer .bd-button {
|
||||||
|
background-color: #3e82e5;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 3px 6px;
|
||||||
|
transition: opacity 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-addon-modal-footer .bd-button:hover {
|
||||||
|
background-color: rgb(56, 117, 206);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-addon-modal-footer .bd-button:active {
|
||||||
|
background-color: rgb(50, 104, 183);
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import {React, Logger} from "modules";
|
import {React, Logger} from "modules";
|
||||||
import {remote} from "electron";
|
import {remote} from "electron";
|
||||||
|
|
||||||
export default class ErrorBoundary extends React.Component {
|
export default class ErrorBoundary extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
|
@ -17,24 +17,27 @@ class FloatingWindowContainer extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.state.windows.map(window =>
|
return this.state.windows.map(window =>
|
||||||
<FloatingWindow {...window} close={this.close.bind(this, window.id)} minY={this.minY}>
|
<FloatingWindow {...window} close={this.close.bind(this, window.id)} minY={this.minY} key={window.id}>
|
||||||
{window.children}
|
{window.children}
|
||||||
</FloatingWindow>
|
</FloatingWindow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
open(window) {
|
open(window) {
|
||||||
this.setState({
|
this.setState(state => {
|
||||||
windows: [...this.state.windows, window]
|
state.windows.push(window);
|
||||||
|
return {windows: state.windows};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
close(id) {
|
close(id) {
|
||||||
this.setState({
|
this.setState(state => {
|
||||||
windows: this.state.windows.filter(w => {
|
return {
|
||||||
if (w.id == id && w.onClose) w.onClose();
|
windows: state.windows.filter(w => {
|
||||||
return w.id != id;
|
if (w.id == id && w.onClose) w.onClose();
|
||||||
})
|
return w.id != id;
|
||||||
|
})
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {React} from "modules";
|
||||||
|
|
||||||
|
export default class DollarSign extends React.Component {
|
||||||
|
render() {
|
||||||
|
const size = this.props.size || "18px";
|
||||||
|
return <svg viewBox="2 2 20 20" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.41 16.09V20h-2.67v-1.93c-1.71-.36-3.16-1.46-3.27-3.4h1.96c.1 1.05.82 1.87 2.65 1.87 1.96 0 2.4-.98 2.4-1.59 0-.83-.44-1.61-2.67-2.14-2.48-.6-4.18-1.62-4.18-3.67 0-1.72 1.39-2.84 3.11-3.21V4h2.67v1.95c1.86.45 2.79 1.86 2.85 3.39H14.3c-.05-1.11-.64-1.87-2.22-1.87-1.5 0-2.4.68-2.4 1.64 0 .84.65 1.39 2.67 1.91s4.18 1.39 4.18 3.91c-.01 1.83-1.38 2.83-3.12 3.16z"/>
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {React} from "modules";
|
||||||
|
|
||||||
|
export default class GitHub extends React.Component {
|
||||||
|
render() {
|
||||||
|
const size = this.props.size || "18px";
|
||||||
|
return <svg viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
|
<path d="m12 .5c-6.63 0-12 5.28-12 11.792 0 5.211 3.438 9.63 8.205 11.188.6.111.82-.254.82-.567 0-.28-.01-1.022-.015-2.005-3.338.711-4.042-1.582-4.042-1.582-.546-1.361-1.335-1.725-1.335-1.725-1.087-.731.084-.716.084-.716 1.205.082 1.838 1.215 1.838 1.215 1.07 1.803 2.809 1.282 3.495.981.108-.763.417-1.282.76-1.577-2.665-.295-5.466-1.309-5.466-5.827 0-1.287.465-2.339 1.235-3.164-.135-.298-.54-1.497.105-3.121 0 0 1.005-.316 3.3 1.209.96-.262 1.98-.392 3-.398 1.02.006 2.04.136 3 .398 2.28-1.525 3.285-1.209 3.285-1.209.645 1.624.24 2.823.12 3.121.765.825 1.23 1.877 1.23 3.164 0 4.53-2.805 5.527-5.475 5.817.42.354.81 1.077.81 2.182 0 1.578-.015 2.846-.015 3.229 0 .309.21.678.825.56 4.801-1.548 8.236-5.97 8.236-11.173 0-6.512-5.373-11.792-12-11.792z" />
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {React} from "modules";
|
||||||
|
|
||||||
|
export default class Globe extends React.Component {
|
||||||
|
render() {
|
||||||
|
const size = this.props.size || "18px";
|
||||||
|
return <svg viewBox="2 2 20 20" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {React} from "modules";
|
||||||
|
|
||||||
|
export default class Patreon extends React.Component {
|
||||||
|
render() {
|
||||||
|
const size = this.props.size || "18px";
|
||||||
|
return <svg viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
|
<path d="m0 .5h4.219v23h-4.219z"/>
|
||||||
|
<path d="m15.384.5c-4.767 0-8.644 3.873-8.644 8.633 0 4.75 3.877 8.61 8.644 8.61 4.754 0 8.616-3.865 8.616-8.61 0-4.759-3.863-8.633-8.616-8.633z"/>
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {React} from "modules";
|
||||||
|
|
||||||
|
export default class Support extends React.Component {
|
||||||
|
render() {
|
||||||
|
const size = this.props.size || "18px";
|
||||||
|
return <svg viewBox="2 2 20 20" fill="#FFFFFF" style={{width: size, height: size}} onClick={this.props.onClick}>
|
||||||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/>
|
||||||
|
</svg>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import {Config} from "data";
|
import {Config} from "data";
|
||||||
import {Logger, WebpackModules, React, Settings, Strings, DOM, DiscordModules} from "modules";
|
import {Logger, WebpackModules, React, Settings, Strings, DOM, DiscordModules} from "modules";
|
||||||
import FormattableString from "../structs/string";
|
import FormattableString from "../structs/string";
|
||||||
|
import ErrorBoundary from "./errorboundary";
|
||||||
|
|
||||||
export default class Modals {
|
export default class Modals {
|
||||||
|
|
||||||
|
@ -8,10 +9,15 @@ export default class Modals {
|
||||||
|
|
||||||
static get ModalActions() {return WebpackModules.getByProps("openModal", "updateModal");}
|
static get ModalActions() {return WebpackModules.getByProps("openModal", "updateModal");}
|
||||||
static get ModalStack() {return WebpackModules.getByProps("push", "update", "pop", "popWithKey");}
|
static get ModalStack() {return WebpackModules.getByProps("push", "update", "pop", "popWithKey");}
|
||||||
|
static get ModalComponents() {return WebpackModules.getByProps("ModalRoot");}
|
||||||
|
static get ModalClasses() {return WebpackModules.getByProps("modal", "content");}
|
||||||
static get AlertModal() {return WebpackModules.getByPrototypes("handleCancel", "handleSubmit", "handleMinorConfirm");}
|
static get AlertModal() {return WebpackModules.getByPrototypes("handleCancel", "handleSubmit", "handleMinorConfirm");}
|
||||||
|
static get FlexElements() {return WebpackModules.getByProps("Child", "Align");}
|
||||||
|
static get FormTitle() {return WebpackModules.findByDisplayName("FormTitle");}
|
||||||
static get TextElement() {return WebpackModules.getByProps("Sizes", "Weights");}
|
static get TextElement() {return WebpackModules.getByProps("Sizes", "Weights");}
|
||||||
static get ConfirmationModal() {return WebpackModules.findByDisplayName("ConfirmModal");}
|
static get ConfirmationModal() {return WebpackModules.findByDisplayName("ConfirmModal");}
|
||||||
static get Markdown() {return WebpackModules.findByDisplayName("Markdown");}
|
static get Markdown() {return WebpackModules.findByDisplayName("Markdown");}
|
||||||
|
static get Buttons() {return WebpackModules.getByProps("ButtonColors");}
|
||||||
|
|
||||||
static default(title, content) {
|
static default(title, content) {
|
||||||
const modal = DOM.createElement(`<div class="bd-modal-wrapper theme-dark">
|
const modal = DOM.createElement(`<div class="bd-modal-wrapper theme-dark">
|
||||||
|
@ -78,7 +84,7 @@ export default class Modals {
|
||||||
return ModalActions.openModal(props => {
|
return ModalActions.openModal(props => {
|
||||||
return React.createElement(ConfirmationModal, Object.assign({
|
return React.createElement(ConfirmationModal, Object.assign({
|
||||||
header: title,
|
header: title,
|
||||||
red: danger,
|
confirmButtonColor: danger ? this.Buttons.ButtonColors.RED : this.Buttons.ButtonColors.PRIMARY,
|
||||||
confirmText: confirmText,
|
confirmText: confirmText,
|
||||||
cancelText: cancelText,
|
cancelText: cancelText,
|
||||||
onConfirm: onConfirm,
|
onConfirm: onConfirm,
|
||||||
|
@ -230,4 +236,55 @@ export default class Modals {
|
||||||
};
|
};
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static showAddonSettingsModal(name, panel) {
|
||||||
|
|
||||||
|
let child = panel;
|
||||||
|
if (panel instanceof Node || typeof(panel) === "string") {
|
||||||
|
child = class ReactWrapper extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.elementRef = React.createRef();
|
||||||
|
this.element = panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.element instanceof Node) this.elementRef.current.appendChild(this.element);
|
||||||
|
// if (typeof(this.element) === "string") this.elementRef.current.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const props = {
|
||||||
|
className: "bd-addon-settings-wrap",
|
||||||
|
ref: this.elementRef
|
||||||
|
};
|
||||||
|
if (typeof(this.element) === "string") props.dangerouslySetInnerHTML = {__html: this.element};
|
||||||
|
return React.createElement("div", props);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (typeof(child) === "function") child = React.createElement(child);
|
||||||
|
|
||||||
|
const mc = this.ModalComponents;
|
||||||
|
const modal = props => {
|
||||||
|
return React.createElement(mc.ModalRoot, Object.assign({size: mc.ModalSize.MEDIUM, className: "bd-addon-modal"}, props),
|
||||||
|
React.createElement(mc.ModalHeader, {separator: false, className: "bd-addon-modal-header"},
|
||||||
|
React.createElement(this.FormTitle, {tag: "h4"}, `${name} Settings`),
|
||||||
|
React.createElement(this.FlexElements.Child, {grow: 0},
|
||||||
|
React.createElement(mc.ModalCloseButton, {onClick: props.onClose})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
React.createElement(mc.ModalContent, {className: "bd-addon-modal-settings"},
|
||||||
|
React.createElement(ErrorBoundary, {}, child)
|
||||||
|
),
|
||||||
|
React.createElement(mc.ModalFooter, {className: "bd-addon-modal-footer"},
|
||||||
|
React.createElement(this.Buttons.default, {onClick: props.onClose, className: "bd-button"}, Strings.Modals.done)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.ModalActions.openModal(props => {
|
||||||
|
return React.createElement(modal, props);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -164,10 +164,10 @@ export default class PublicServers extends React.Component {
|
||||||
else if (this.state.results.total) content = React.createElement("div", {className: "bd-card-list"}, servers);
|
else if (this.state.results.total) content = React.createElement("div", {className: "bd-card-list"}, servers);
|
||||||
|
|
||||||
return [React.createElement(SettingsTitle, {text: this.title, button: connectButton}),
|
return [React.createElement(SettingsTitle, {text: this.title, button: connectButton}),
|
||||||
(this.state.tab !== "Featured" && this.state.tab !== "Popular") && this.pagination,
|
this.state.results.numPages > 1 && this.pagination,
|
||||||
content,
|
content,
|
||||||
(this.state.tab !== "Featured" && this.state.tab !== "Popular") && this.pagination,
|
this.state.results.numPages > 1 && this.pagination,
|
||||||
this.state.results.servers.length > 0 && React.createElement(SettingsTitle, {text: this.title})
|
this.state.results.numPages > 1 && this.state.query && React.createElement(SettingsTitle, {text: this.title})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,25 @@
|
||||||
import {React, Logger, Strings, WebpackModules, DOM, DiscordModules} from "modules";
|
import {React, Logger, Strings, WebpackModules, DiscordModules} from "modules";
|
||||||
import CloseButton from "../icons/close";
|
|
||||||
import ReloadIcon from "../icons/reload";
|
import ReloadIcon from "../icons/reload";
|
||||||
import EditIcon from "../icons/edit";
|
import EditIcon from "../icons/edit";
|
||||||
import DeleteIcon from "../icons/delete";
|
import DeleteIcon from "../icons/delete";
|
||||||
|
import CogIcon from "../icons/cog";
|
||||||
import Switch from "./components/switch";
|
import Switch from "./components/switch";
|
||||||
import ErrorBoundary from "../errorboundary";
|
|
||||||
|
import GitHubIcon from "../icons/github";
|
||||||
|
import MoneyIcon from "../icons/dollarsign";
|
||||||
|
import WebIcon from "../icons/globe";
|
||||||
|
import PatreonIcon from "../icons/patreon";
|
||||||
|
import SupportIcon from "../icons/support";
|
||||||
|
import Modals from "../modals";
|
||||||
|
import Toasts from "../toasts";
|
||||||
|
|
||||||
|
const LinkIcons = {
|
||||||
|
website: WebIcon,
|
||||||
|
source: GitHubIcon,
|
||||||
|
invite: SupportIcon,
|
||||||
|
donate: MoneyIcon,
|
||||||
|
patreon: PatreonIcon
|
||||||
|
};
|
||||||
|
|
||||||
const Tooltip = WebpackModules.getByDisplayName("Tooltip");
|
const Tooltip = WebpackModules.getByDisplayName("Tooltip");
|
||||||
|
|
||||||
|
@ -22,7 +37,18 @@ export default class AddonCard extends React.Component {
|
||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
this.reload = this.reload.bind(this);
|
this.reload = this.reload.bind(this);
|
||||||
this.showSettings = this.showSettings.bind(this);
|
this.showSettings = this.showSettings.bind(this);
|
||||||
this.closeSettings = this.closeSettings.bind(this);
|
}
|
||||||
|
|
||||||
|
showSettings() {
|
||||||
|
if (!this.props.hasSettings || !this.props.enabled) return;
|
||||||
|
const name = this.getString(this.props.addon.name);
|
||||||
|
try {
|
||||||
|
Modals.showAddonSettingsModal(name, this.props.getSettingsPanel());
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
Toasts.show(Strings.Addons.settingsError.format({name}), {type: "error"});
|
||||||
|
Logger.stacktrace("Addon Settings", "Unable to get settings panel for " + name + ".", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
|
@ -31,36 +57,6 @@ export default class AddonCard extends React.Component {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
if (!this.state.settingsOpen) return;
|
|
||||||
if (this.settingsPanel instanceof Node) this.panelRef.current.appendChild(this.settingsPanel);
|
|
||||||
|
|
||||||
setImmediate(() => {
|
|
||||||
const isHidden = (container, element) => {
|
|
||||||
const cTop = container.scrollTop;
|
|
||||||
const cBottom = cTop + container.clientHeight;
|
|
||||||
const eTop = element.offsetTop;
|
|
||||||
const eBottom = eTop + element.clientHeight;
|
|
||||||
return (eTop < cTop || eBottom > cBottom);
|
|
||||||
};
|
|
||||||
|
|
||||||
const thisNode = this.panelRef.current;
|
|
||||||
const container = thisNode.closest(".scrollerBase-289Jih");
|
|
||||||
if (!container || !isHidden(container, thisNode)) return;
|
|
||||||
const thisNodeOffset = DOM.offset(thisNode);
|
|
||||||
const containerOffset = DOM.offset(container);
|
|
||||||
const original = container.scrollTop;
|
|
||||||
const endPoint = thisNodeOffset.top - containerOffset.top + container.scrollTop - 30;
|
|
||||||
DOM.animate({
|
|
||||||
duration: 300,
|
|
||||||
update: function(progress) {
|
|
||||||
if (endPoint > original) container.scrollTop = original + (progress * (endPoint - original));
|
|
||||||
else container.scrollTop = original - (progress * (original - endPoint));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getString(value) {return typeof value == "string" ? value : value.toString();}
|
getString(value) {return typeof value == "string" ? value : value.toString();}
|
||||||
|
|
||||||
onChange() {
|
onChange() {
|
||||||
|
@ -69,16 +65,6 @@ export default class AddonCard extends React.Component {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
showSettings() {
|
|
||||||
if (!this.props.hasSettings) return;
|
|
||||||
this.setState({settingsOpen: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSettings() {
|
|
||||||
if (this.settingsPanel instanceof Node) this.panelRef.current.innerHTML = "";
|
|
||||||
this.setState({settingsOpen: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTitle(name, version, author) {
|
buildTitle(name, version, author) {
|
||||||
const title = Strings.Addons.title.split(/({{[A-Za-z]+}})/);
|
const title = Strings.Addons.title.split(/({{[A-Za-z]+}})/);
|
||||||
const nameIndex = title.findIndex(s => s == "{{name}}");
|
const nameIndex = title.findIndex(s => s == "{{name}}");
|
||||||
|
@ -90,35 +76,11 @@ export default class AddonCard extends React.Component {
|
||||||
return title.flat();
|
return title.flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
get settingsComponent() {
|
|
||||||
const addon = this.props.addon;
|
|
||||||
const name = this.getString(addon.name);
|
|
||||||
try {this.settingsPanel = this.props.getSettingsPanel();}
|
|
||||||
catch (err) {Logger.stacktrace("Addon Settings", "Unable to get settings panel for " + name + ".", err);}
|
|
||||||
|
|
||||||
const props = {id: `${name}-settings`, className: "addon-settings", ref: this.panelRef};
|
|
||||||
if (typeof(this.settingsPanel) == "string") {
|
|
||||||
Logger.warn("Addon Settings", "Using a DOMString is officially deprecated.");
|
|
||||||
props.dangerouslySetInnerHTML = {__html: this.settingsPanel};
|
|
||||||
}
|
|
||||||
|
|
||||||
let child;
|
|
||||||
if (typeof(this.settingsPanel) === "function") child = <this.settingsPanel />;
|
|
||||||
if (this.settingsPanel.$$typeof && this.settingsPanel.$$typeof === Symbol.for("react.element")) child = this.settingsPanel;
|
|
||||||
if (child) child = <ErrorBoundary>{child}</ErrorBoundary>;
|
|
||||||
|
|
||||||
return <div className="bd-addon-card settings-open bd-switch-item">
|
|
||||||
<div className="bd-close" onClick={this.closeSettings}><CloseButton /></div>
|
|
||||||
<div {...props}>
|
|
||||||
{child}
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildLink(which) {
|
buildLink(which) {
|
||||||
const url = this.props.addon[which];
|
const url = this.props.addon[which];
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
const link = <a className="bd-link bd-link-website" href={url} target="_blank" rel="noopener noreferrer">{Strings.Addons[which]}</a>;
|
const icon = React.createElement(LinkIcons[which]);
|
||||||
|
const link = <a className="bd-link bd-link-website" href={url} target="_blank" rel="noopener noreferrer">{icon}</a>;
|
||||||
if (which == "invite") {
|
if (which == "invite") {
|
||||||
link.props.onClick = function(event) {
|
link.props.onClick = function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -130,16 +92,24 @@ export default class AddonCard extends React.Component {
|
||||||
DiscordModules.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
DiscordModules.InviteActions.acceptInviteAndTransitionToInviteChannel(code);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return link;
|
return this.makeButton(Strings.Addons[which], link);
|
||||||
|
}
|
||||||
|
|
||||||
|
get controls() { // {this.props.hasSettings && <button onClick={this.showSettings} className="bd-button bd-button-addon-settings" disabled={!this.props.enabled}>{Strings.Addons.addonSettings}</button>}
|
||||||
|
return <div className="bd-controls">
|
||||||
|
{this.props.hasSettings && this.makeControlButton(Strings.Addons.addonSettings, <CogIcon size={"24px"} />, this.showSettings, {disabled: !this.props.enabled})}
|
||||||
|
{this.props.showReloadIcon && this.makeControlButton(Strings.Addons.reload, <ReloadIcon />, this.reload)}
|
||||||
|
{this.props.editAddon && this.makeControlButton(Strings.Addons.editAddon, <EditIcon />, this.props.editAddon)}
|
||||||
|
{this.props.deleteAddon && this.makeControlButton(Strings.Addons.deleteAddon, <DeleteIcon />, this.props.deleteAddon, {danger: true})}
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
get footer() {
|
get footer() {
|
||||||
const links = ["website", "source", "invite", "donate", "patreon"];
|
const links = ["website", "source", "invite", "donate", "patreon"];
|
||||||
if (!links.some(l => this.props.addon[l]) && !this.props.hasSettings) return null;
|
const linkComponents = links.map(this.buildLink.bind(this)).filter(c => c);// linkComponents.map((comp, i) => i < linkComponents.length - 1 ? [comp, " | "] : comp).flat()
|
||||||
const linkComponents = links.map(this.buildLink.bind(this)).filter(c => c);
|
|
||||||
return <div className="bd-footer">
|
return <div className="bd-footer">
|
||||||
<span className="bd-links">{linkComponents.map((comp, i) => i < linkComponents.length - 1 ? [comp, " | "] : comp).flat()}</span>
|
<span className="bd-links">{linkComponents}</span>
|
||||||
{this.props.hasSettings && <button onClick={this.showSettings} className="bd-button bd-button-addon-settings" disabled={!this.props.enabled}>{Strings.Addons.addonSettings}</button>}
|
{this.controls}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,9 +121,15 @@ export default class AddonCard extends React.Component {
|
||||||
</Tooltip>;
|
</Tooltip>;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
makeControlButton(title, children, action, {danger = false, disabled = false} = {}) {
|
||||||
if (this.state.settingsOpen) return this.settingsComponent;
|
return <Tooltip color="black" position="top" text={title}>
|
||||||
|
{(props) => {
|
||||||
|
return <button {...props} className={"bd-button bd-addon-button" + (danger ? " bd-button-danger" : "") + (disabled ? " bd-button-disabled" : "")} onClick={action}>{children}</button>;
|
||||||
|
}}
|
||||||
|
</Tooltip>;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
const addon = this.props.addon;
|
const addon = this.props.addon;
|
||||||
const name = this.getString(addon.name);
|
const name = this.getString(addon.name);
|
||||||
const author = this.getString(addon.author);
|
const author = this.getString(addon.author);
|
||||||
|
@ -163,12 +139,7 @@ export default class AddonCard extends React.Component {
|
||||||
return <div id={`${addon.id}-card`} className="bd-addon-card settings-closed">
|
return <div id={`${addon.id}-card`} className="bd-addon-card settings-closed">
|
||||||
<div className="bd-addon-header">
|
<div className="bd-addon-header">
|
||||||
<span className="bd-title">{this.buildTitle(name, version, author)}</span>
|
<span className="bd-title">{this.buildTitle(name, version, author)}</span>
|
||||||
<div className="bd-controls">
|
<Switch checked={this.props.enabled} onChange={this.onChange} />
|
||||||
{this.props.editAddon && this.makeButton(Strings.Addons.editAddon, <EditIcon />, this.props.editAddon)}
|
|
||||||
{this.props.deleteAddon && this.makeButton(Strings.Addons.deleteAddon, <DeleteIcon />, this.props.deleteAddon)}
|
|
||||||
{this.props.showReloadIcon && this.makeButton(Strings.Addons.reload, <ReloadIcon className="bd-reload bd-reload-card" />, this.reload)}
|
|
||||||
<Switch checked={this.props.enabled} onChange={this.onChange} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="bd-description-wrap scroller-wrap fade"><div className="bd-description scroller">{description}</div></div>
|
<div className="bd-description-wrap scroller-wrap fade"><div className="bd-description scroller">{description}</div></div>
|
||||||
{this.footer}
|
{this.footer}
|
||||||
|
|
Loading…
Reference in New Issue