From b0c5f6ab200696156262b6240fb10d0e29877344 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Tue, 3 Nov 2020 19:45:36 -0500 Subject: [PATCH] Replaces Ace with Monaco - Replaces Ace editor with the Monaco editor (the one from VSCode) - Adds the option for listview vs gridview in addon lists - Addon descriptions can use markdown again - Added blankslates for no addon results and no addons installed (Thanks Tropical) - Replaces BBD logos (social links and loading icon) with BD logos --- src/builtins/customcss.js | 36 ++++++- src/data/strings.js | 4 +- src/loadingicon.js | 2 +- src/modules/dommanager.js | 5 +- src/styles/blankslates/emptyimage.css | 21 +++++ src/styles/builtins/customcss.css | 3 +- src/styles/buttons.css | 7 +- src/styles/index.css | 1 + src/styles/ui/addonlist.css | 53 +++++++++-- src/styles/ui/bdsettings.css | 8 ++ src/styles/ui/floatingwindow.css | 6 +- src/ui/blankslates/emptyimage.jsx | 19 ++++ .../noresults.jsx | 0 src/ui/customcss/csseditor.jsx | 2 - src/ui/customcss/editor.jsx | 52 +++------- src/ui/icons/bdlogo.jsx | 12 +-- src/ui/icons/grid.jsx | 11 +++ src/ui/icons/list.jsx | 11 +++ src/ui/misc/addoneditor.jsx | 4 +- src/ui/publicservers/menu.js | 2 +- src/ui/settings/addoncard.jsx | 3 +- src/ui/settings/addonlist.jsx | 94 ++++++++++++++----- 22 files changed, 256 insertions(+), 100 deletions(-) create mode 100644 src/styles/blankslates/emptyimage.css create mode 100644 src/ui/blankslates/emptyimage.jsx rename src/ui/{publicservers => blankslates}/noresults.jsx (100%) create mode 100644 src/ui/icons/grid.jsx create mode 100644 src/ui/icons/list.jsx diff --git a/src/builtins/customcss.js b/src/builtins/customcss.js index db35b8c9..b0290431 100644 --- a/src/builtins/customcss.js +++ b/src/builtins/customcss.js @@ -26,11 +26,37 @@ export default new class CustomCSS extends Builtin { } async enabled() { - if (!window.ace) { - DOMManager.injectScript("ace-script", "https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js").then(() => { - if (window.require.original) window.require = window.require.original; - }); - } + // if (!window.ace) { + // DOMManager.injectScript("ace-script", "https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js").then(() => { + // if (window.require.original) window.require = window.require.original; + // }); + // } + + + Object.defineProperty(window, "MonacoEnvironment", { + value: { + getWorkerUrl: function() { + return `data:text/javascript;charset=utf-8,${encodeURIComponent(` + self.MonacoEnvironment = { + baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min' + }; + importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/base/worker/workerMain.min.js');` + )}`; + } + } + }); + + const commonjsLoader = window.require; + delete window.module; // Make monaco think this isn't a local node script or else it freaks out + DOMManager.linkStyle("monaco-style", "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.min.css", {documentHead: true}); + DOMManager.injectScript("monaco-script", "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/loader.min.js").then(() => { + const amdLoader = window.require; // Grab Monaco's amd loader + window.require = commonjsLoader; // Revert to commonjs + amdLoader.config({paths: {vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs"}}); + amdLoader(["vs/editor/editor.main"], () => {}); // exposes the monaco global + }); + + Settings.registerPanel(this.id, Strings.Panels.customcss, { order: 2, element: () => [, React.createElement(CSSEditor, { diff --git a/src/data/strings.js b/src/data/strings.js index 44b8af51..0f91fa6e 100644 --- a/src/data/strings.js +++ b/src/data/strings.js @@ -229,7 +229,9 @@ export default { missingNameData: "META missing name data.", metaNotFound: "META was not found.", compileError: "Could not be compiled.", - wasUnloaded: "{{name}} was unloaded." + wasUnloaded: "{{name}} was unloaded.", + blankSlateHeader: "You don't have any {{type}}!", + blankSlateMessage: "Grab some from [this website]({{link}}) and add them to your {{type}} folder." }, CustomCSS: { confirmationText: "You have unsaved changes to your Custom CSS. Closing this window will lose all those changes.", diff --git a/src/loadingicon.js b/src/loadingicon.js index e904c716..41d06bd2 100644 --- a/src/loadingicon.js +++ b/src/loadingicon.js @@ -2,7 +2,7 @@ const css = `/* BEGIN V2 LOADER */ /* =============== */ #bd-loading-icon { - background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+IDwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyBoZWlnaHQ9IjEwMCUiIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiB3aWR0aD0iMTAwJSIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIj4gICAgPG1ldGFkYXRhIC8+ICAgIDxkZWZzPiAgICAgICAgPGZpbHRlciBpZD0ic2hhZG93MSI+ICAgICAgICA8ZmVEcm9wU2hhZG93IGR4PSIyMCIgZHk9IjAiIHN0ZERldmlhdGlvbj0iMjAiIGZsb29kLWNvbG9yPSJyZ2JhKDAsMCwwLDAuMzUpIi8+ICAgICAgICA8L2ZpbHRlcj4gICAgICAgIDxmaWx0ZXIgaWQ9InNoYWRvdzIiPiAgICAgICAgPGZlRHJvcFNoYWRvdyBkeD0iMTUiIGR5PSIwIiBzdGREZXZpYXRpb249IjIwIiBmbG9vZC1jb2xvcj0icmdiYSgyNTUsMjU1LDI1NSwwLjE1KSIvPiAgICAgICAgPC9maWx0ZXI+ICAgICAgICA8ZmlsdGVyIGlkPSJzaGFkb3czIj4gICAgICAgIDxmZURyb3BTaGFkb3cgZHg9IjEwIiBkeT0iMCIgc3RkRGV2aWF0aW9uPSIyMCIgZmxvb2QtY29sb3I9InJnYmEoMCwwLDAsMC4zNSkiLz4gICAgICAgIDwvZmlsdGVyPiAgICA8L2RlZnM+ICAgIDxnPiAgICAgICAgPHBhdGggc3R5bGU9ImZpbHRlcjogdXJsKCNzaGFkb3czKSIgZD0iTTExOTUuNDQrMTM1LjQ0MkwxMTk1LjQ0KzEzNS40NDJMOTk3LjYrMTM2LjQ0MkMxMDI0LjIrMTQ5Ljc0MisxMTcwLjM0KzE2My41NDIrMTE5My42NCsxNzkuNzQyQzEyNjQuMzQrMjI4Ljg0MisxMzE5Ljc0KzI5MS4yNDIrMTM1OC4yNCszNjUuMDQyQzEzOTguMTQrNDQxLjY0MisxNDE5Ljc0KzUzMC42NDIrMTQyMi41NCs2MjkuNjQyTDE0MjIuNTQrNjMwLjg0MkwxNDIyLjU0KzYzMi4wNDJDMTQyMi41NCs3NzMuMTQyKzE0MjIuNTQrMTIyOC4xNCsxNDIyLjU0KzEzNjkuMTRMMTQyMi41NCsxMzcwLjM0TDE0MjIuNTQrMTM3MS41NEMxNDE5Ljg0KzE0NzAuNTQrMTM5OC4yNCsxNTU5LjU0KzEzNTguMjQrMTYzNi4xNEMxMzE5Ljc0KzE3MDkuOTQrMTI2NC40NCsxNzcyLjM0KzExOTMuNjQrMTgyMS40NEMxMTcxLjA0KzE4MzcuMTQrMTAyNS43KzE4NTAuNTQrMTAwMCsxODYzLjU0TDExOTMuNTQrMTg2NC41NEMxNTM5Ljc0KzE4NjYuNDQrMTg2NC41NCsxNjkzLjM0KzE4NjQuNTQrMTI5Ni42NEwxODY0LjU0KzcxNi45NDJDMTg2Ni40NCszMTIuNDQyKzE1NDEuNjQrMTM1LjQ0MisxMTk1LjQ0KzEzNS40NDJaIiBmaWxsPSIjMTcxNzE3IiBvcGFjaXR5PSIxIi8+ICAgICAgICA8cGF0aCBzdHlsZT0iZmlsdGVyOiB1cmwoI3NoYWRvdzIpIiBkPSJNMTY5NS41NCs2MzEuNDQyQzE2ODUuODQrMjc4LjA0MisxNDA5LjM0KzEzNS40NDIrMTA1Mi45NCsxMzUuNDQyTDM2MS43NCsxMzYuNDQyTDgwMy43NCs0OTAuNDQyTDEwNjAuNzQrNDkwLjQ0MkMxMzM1LjI0KzQ5MC40NDIrMTMzNS4yNCs4MzUuMzQyKzEwNjAuNzQrODM1LjM0MkwxMDYwLjc0KzExNjQuODRDMTE1MC4yMisxMTY0Ljg0KzEyMTAuNTMrMTIwMS40OCsxMjQxLjY4KzEyNTAuODdDMTMwNi4wNysxMzUzKzEyNDUuNzYrMTUwOS42NCsxMDYwLjc0KzE1MDkuNjRMMzYxLjc0KzE4NjMuNTRMMTA1Mi45NCsxODY0LjU0QzE0MDkuMjQrMTg2NC41NCsxNjg1Ljc0KzE3MjEuOTQrMTY5NS41NCsxMzY4LjU0QzE2OTUuNTQrMTIwNS45NCsxNjUxLjA0KzEwODQuNDQrMTU3Mi42NCs5OTkuOTQyQzE2NTEuMDQrOTE1LjU0MisxNjk1LjU0Kzc5NC4wNDIrMTY5NS41NCs2MzEuNDQyWiIgZmlsbD0iIzNFODJFNSIgb3BhY2l0eT0iMSIvPiAgICAgICAgPHBhdGggc3R5bGU9ImZpbHRlcjogdXJsKCNzaGFkb3cxKSIgZD0iTTE0NjkuMjUrNjMxLjQ0MkMxNDU5LjU1KzI3OC4wNDIrMTE4My4wNSsxMzUuNDQyKzgyNi42NSsxMzUuNDQyTDEzNS40NSsxMzUuNDQyTDEzNS40NSsxMDA0QzEzNS40NSsxMDA0KzEzNS40MjcrMTI1NS4yMSszNTUuNjI2KzEyNTUuMjFDNTc1LjgyNSsxMjU1LjIxKzU3NS44NDgrMTAwNCs1NzUuODQ4KzEwMDRMNTc3LjQ1KzQ5MC40NDJMODM0LjQ1KzQ5MC40NDJDMTEwOC45NSs0OTAuNDQyKzExMDguOTUrODM1LjM0Mis4MzQuNDUrODM1LjM0Mkw2NjQuNjUrODM1LjM0Mkw2NjQuNjUrMTE2NC44NEw4MzQuNDUrMTE2NC44NEM5MjMuOTMyKzExNjQuODQrOTg0LjI0NCsxMjAxLjQ4KzEwMTUuMzkrMTI1MC44N0MxMDc5Ljc4KzEzNTMrMTAxOS40NysxNTA5LjY0KzgzNC40NSsxNTA5LjY0TDEzNS40NSsxNTA5LjY0TDEzNS40NSsxODY0LjU0TDgyNi42NSsxODY0LjU0QzExODIuOTUrMTg2NC41NCsxNDU5LjQ1KzE3MjEuOTQrMTQ2OS4yNSsxMzY4LjU0QzE0NjkuMjUrMTIwNS45NCsxNDI0Ljc1KzEwODQuNDQrMTM0Ni4zNSs5OTkuOTQyQzE0MjQuNzUrOTE1LjU0MisxNDY5LjI1Kzc5NC4wNDIrMTQ2OS4yNSs2MzEuNDQyWiIgZmlsbD0iI0ZGRkZGRiIgb3BhY2l0eT0iMSIvPiAgICA8L2c+PC9zdmc+); + background-image: url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjAwMCAyMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyMDAwIDIwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGZpbGw9IiMzRTgyRTUiIGQ9Ik0xNDAyLjIsNjMxLjdjLTkuNy0zNTMuNC0yODYuMi00OTYtNjQyLjYtNDk2SDY4LjR2NzE0LjFsNDQyLDM5OFY0OTAuN2gyNTdjMjc0LjUsMCwyNzQuNSwzNDQuOSwwLDM0NC45SDU5Ny42djMyOS41aDE2OS44YzI3NC41LDAsMjc0LjUsMzQ0LjgsMCwzNDQuOGgtNjk5djM1NC45aDY5MS4yYzM1Ni4zLDAsNjMyLjgtMTQyLjYsNjQyLjYtNDk2YzAtMTYyLjYtNDQuNS0yODQuMS0xMjIuOS0zNjguNkMxMzU3LjcsOTE1LjgsMTQwMi4yLDc5NC4zLDE0MDIuMiw2MzEuN3oiLz48cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMTI2Mi41LDEzNS4yTDEyNjIuNSwxMzUuMmwtNzYuOCwwYzI2LjYsMTMuMyw1MS43LDI4LjEsNzUsNDQuM2M3MC43LDQ5LjEsMTI2LjEsMTExLjUsMTY0LjYsMTg1LjNjMzkuOSw3Ni42LDYxLjUsMTY1LjYsNjQuMywyNjQuNmwwLDEuMnYxLjJjMCwxNDEuMSwwLDU5Ni4xLDAsNzM3LjF2MS4ybDAsMS4yYy0yLjcsOTktMjQuMywxODgtNjQuMywyNjQuNmMtMzguNSw3My44LTkzLjgsMTM2LjItMTY0LjYsMTg1LjNjLTIyLjYsMTUuNy00Ni45LDMwLjEtNzIuNiw0My4xaDcyLjVjMzQ2LjIsMS45LDY3MS0xNzEuMiw2NzEtNTY3LjlWNzE2LjdDMTkzMy41LDMxMi4yLDE2MDguNywxMzUuMiwxMjYyLjUsMTM1LjJ6Ii8+PC9nPjwvc3ZnPg==); } #bd-loading-icon { position: fixed; diff --git a/src/modules/dommanager.js b/src/modules/dommanager.js index c5d3aaa6..4f7e260d 100644 --- a/src/modules/dommanager.js +++ b/src/modules/dommanager.js @@ -54,14 +54,15 @@ export default class DOMManager { return this.removeStyle(id); } - static linkStyle(id, url) { + static linkStyle(id, url, {documentHead = false} = {}) { id = this.escapeID(id); return new Promise(resolve => { const link = this.getElement(`#${id}`, this.bdStyles) || this.createElement("link", {id}); link.rel = "stylesheet"; link.href = url; link.onload = resolve; - this.bdStyles.append(link); + const target = documentHead ? document.head : this.bdStyles; + target.append(link); }); } diff --git a/src/styles/blankslates/emptyimage.css b/src/styles/blankslates/emptyimage.css new file mode 100644 index 00000000..edcd48c5 --- /dev/null +++ b/src/styles/blankslates/emptyimage.css @@ -0,0 +1,21 @@ +.bd-empty-image-container { + background: transparent; +} + +.bd-empty-image-header { + color: var(--header-primary); + font-size: 24px; + font-weight: 600; + margin-bottom: 8px; +} + +.bd-empty-image-message { + color: var(--header-secondary); + margin-bottom: 8px; +} + +.bd-empty-image-container .bd-button { + margin-top: 10px; + font-size: 16px; + padding: 10px 16px; +} \ No newline at end of file diff --git a/src/styles/builtins/customcss.css b/src/styles/builtins/customcss.css index 519da384..bc94a98f 100644 --- a/src/styles/builtins/customcss.css +++ b/src/styles/builtins/customcss.css @@ -43,9 +43,8 @@ display: flex; } +.editor, .ace_editor { - line-height: normal; - font-family: Consolas, monospace; box-sizing: border-box; height: calc(100vh - 250px); font-size: 14px; diff --git a/src/styles/buttons.css b/src/styles/buttons.css index c878aa80..53206c9d 100644 --- a/src/styles/buttons.css +++ b/src/styles/buttons.css @@ -1,4 +1,7 @@ .bd-button { + display: inline-flex; + justify-content: center; + align-items: center; background-color: #3e82e5; color: #fff; border-radius: 3px; @@ -31,11 +34,11 @@ } .bd-button.bd-button-danger:hover { - background-color: rgb(237, 42, 42); + background-color: #d84040; } .bd-button.bd-button-danger:active { - background-color: rgb(230, 18, 18); + background-color: #c03939; } .bd-button-disabled { diff --git a/src/styles/index.css b/src/styles/index.css index ab429b84..0680b82f 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -2,6 +2,7 @@ @import "./builtins/*"; @import "./ui/*"; +@import "./blankslates/*"; @import "./buttons.css"; @import "./spinner.css"; @import "./search.css"; diff --git a/src/styles/ui/addonlist.css b/src/styles/ui/addonlist.css index 7a8dd332..6587a307 100644 --- a/src/styles/ui/addonlist.css +++ b/src/styles/ui/addonlist.css @@ -16,7 +16,8 @@ margin-right: 5px; } -.bd-controls { +.bd-controls, +.bd-controls-advanced { display: flex; } @@ -24,18 +25,26 @@ user-select: text; } +.bd-addon-list.bd-grid-view { + display: grid; + grid-template-columns: auto auto; + column-gap: 10px; + row-gap: 10px; +} + .bd-addon-list .bd-addon-card { - max-height: 175px; + display: flex; + flex-direction: column; margin-bottom: 20px; - padding: 12px; + padding: 16px; border-radius: 5px; overflow: hidden; background: var(--background-secondary-alt); + border: 1px solid var(--background-tertiary); } -.bd-addon-list .bd-addon-card.settings-open { - max-height: 800px; - overflow-y: auto; +.bd-addon-list.bd-grid-view .bd-addon-card { + margin-bottom: 0; } .bd-addon-list .bd-addon-header { @@ -49,11 +58,16 @@ overflow: hidden; } +.bd-description-wrap { + flex: 1; +} + .bd-addon-list .bd-description { word-break: break-word; margin-bottom: 5px; padding: 5px 0; overflow-y: auto; + max-height: 175px; font-size: 14px; line-height: 18px; -webkit-line-clamp: 3; @@ -161,6 +175,7 @@ .bd-addon-modal-settings { /* padding: 16px; */ + padding: 0 16px 16px 16px; } .bd-addon-modal-footer .bd-button { @@ -177,4 +192,30 @@ .bd-addon-modal-footer .bd-button:active { background-color: rgb(50, 104, 183); +} + +.bd-addon-views { + display: flex; + margin-left: 10px; +} + +.bd-addon-views .bd-view-button { + background-color: transparent; + padding: 3px 4px; +} + +.bd-addon-views .bd-view-button:hover { + background-color: var(--background-modifier-selected); +} + +.bd-addon-views .bd-view-button:active { + background-color: var(--background-modifier-accent); +} + +.bd-addon-views .bd-view-button.selected { + background-color: #3e82e5; +} + +.bd-addon-views .bd-view-button + .bd-view-button { + margin-left: 5px; } \ No newline at end of file diff --git a/src/styles/ui/bdsettings.css b/src/styles/ui/bdsettings.css index f53ebabf..d36566b8 100644 --- a/src/styles/ui/bdsettings.css +++ b/src/styles/ui/bdsettings.css @@ -2,10 +2,18 @@ opacity: 0.6; } +.bd-social-logo path { + fill: white; +} + .bd-social-link:hover .bd-social-logo { opacity: 1; } +.bd-social-link:hover .bd-social-logo path:first-of-type { + fill: #3e82e5; +} + .standardSidebarView-3F1I7i .bd-versioninfo-wrapper { bottom: 0; left: 0; diff --git a/src/styles/ui/floatingwindow.css b/src/styles/ui/floatingwindow.css index b0e81367..75a8db31 100644 --- a/src/styles/ui/floatingwindow.css +++ b/src/styles/ui/floatingwindow.css @@ -48,6 +48,7 @@ background: var(--background-secondary); color: #fff; flex: 1; + overflow: hidden; } .floating-window-titlebar .title { @@ -95,13 +96,16 @@ display: flex; flex-direction: column; flex: 1; + height: 100%; + overflow: hidden; } .floating-window .editor-wrapper { flex: 1; + overflow: hidden; } -.floating-window .ace_editor { +.floating-window .editor { height: auto; flex: 1; } diff --git a/src/ui/blankslates/emptyimage.jsx b/src/ui/blankslates/emptyimage.jsx new file mode 100644 index 00000000..36debf51 --- /dev/null +++ b/src/ui/blankslates/emptyimage.jsx @@ -0,0 +1,19 @@ +import {React, WebpackModules} from "modules"; + +const EmptyImageClasses = WebpackModules.getByProps("emptyImage") || {}; +const MarkdownParser = WebpackModules.getByProps("markdownToReact"); + +export default class EmptyImage extends React.Component { + render() { + return
+
+
+ {this.props.title || "You don't have anything!"} +
+
+ {MarkdownParser.markdownToReact(this.props.message || "You should probably get something.")} +
+ {this.props.children} +
; + } +} \ No newline at end of file diff --git a/src/ui/publicservers/noresults.jsx b/src/ui/blankslates/noresults.jsx similarity index 100% rename from src/ui/publicservers/noresults.jsx rename to src/ui/blankslates/noresults.jsx diff --git a/src/ui/customcss/csseditor.jsx b/src/ui/customcss/csseditor.jsx index 7e000fe8..c9ddc2cf 100644 --- a/src/ui/customcss/csseditor.jsx +++ b/src/ui/customcss/csseditor.jsx @@ -5,7 +5,6 @@ import Editor from "./editor"; import Refresh from "../icons/reload"; import Save from "../icons/save"; import Edit from "../icons/edit"; -import Cog from "../icons/cog"; import Detach from "../icons/detach"; export default class CssEditor extends React.Component { @@ -27,7 +26,6 @@ export default class CssEditor extends React.Component { {label: React.createElement(Refresh, {size: "18px"}), tooltip: Strings.CustomCSS.update, onClick: this.updateCss}, {label: React.createElement(Save, {size: "18px"}), tooltip: Strings.CustomCSS.save, onClick: this.saveCss}, {label: React.createElement(Edit, {size: "18px"}), tooltip: Strings.CustomCSS.openNative, onClick: this.openNative}, - {label: React.createElement(Cog, {size: "18px"}), tooltip: Strings.CustomCSS.settings, onClick: "showSettings"}, {label: Strings.Collections.settings.customcss.liveUpdate.name, type: "checkbox", onChange: this.toggleLiveUpdate, checked: Settings.get("settings", "customcss", "liveUpdate"), side: "right"} ]; if (this.openDetached) this.controls.push({label: React.createElement(Detach, {size: "18px"}), tooltip: Strings.CustomCSS.openDetached, onClick: this.openDetached, side: "right"}); diff --git a/src/ui/customcss/editor.jsx b/src/ui/customcss/editor.jsx index 8487f099..71737647 100644 --- a/src/ui/customcss/editor.jsx +++ b/src/ui/customcss/editor.jsx @@ -1,4 +1,4 @@ -import {React, WebpackModules} from "modules"; +import {React, WebpackModules, DiscordModules} from "modules"; import Checkbox from "./checkbox"; @@ -12,11 +12,6 @@ export default class CodeEditor extends React.Component { constructor(props) { super(props); - - for (const control of this.props.controls) { - if (control.type == "checkbox") continue; - if (control.onClick == "showSettings") control.onClick = this.showSettings.bind(this); - } this.props.theme = this.props.theme.toLowerCase().replace(/ /g, "_"); if (!themes.includes(this.props.theme)) this.props.theme = CodeEditor.defaultProps.theme; @@ -24,6 +19,7 @@ export default class CodeEditor extends React.Component { this.props.language = this.props.language.toLowerCase().replace(/ /g, "_"); if (!languages.includes(this.props.language)) this.props.language = CodeEditor.defaultProps.language; + this.bindings = []; this.onChange = this.onChange.bind(this); } @@ -40,47 +36,29 @@ export default class CodeEditor extends React.Component { static get themes() {return themes;} componentDidMount() { - this.editor = ace.edit(this.props.id); + this.editor = window.monaco.editor.create(document.getElementById(this.props.id), { + value: this.props.value, + language: this.props.language, + theme: DiscordModules.UserSettingsStore.theme == "light" ? "vs" : "vs-dark" + }); - // Add id to the ace menu container - const originalShow = this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec; - this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec = function() { - originalShow.apply(this, arguments); - const observer = new MutationObserver(mutations => { - for (const mutation of mutations) { - if (!mutation.addedNodes.length || !(mutation.addedNodes[0] instanceof Element)) continue; - const node = mutation.addedNodes[0]; - if (node.parentElement !== document.body || !node.querySelector("#ace_settingsmenu")) continue; - node.id = "ace_settingsmenu_container"; - observer.disconnect(); - } - }); - observer.observe(document.body, {childList: true}); - }; - - const theme = this.props.theme == CodeEditor.defaultProps.theme ? this.props.theme.split("-")[1] : this.props.theme; - this.editor.setTheme(`ace/theme/${theme}`); - this.editor.session.setMode(`ace/mode/${this.props.language}`); - this.editor.setShowPrintMargin(false); - this.editor.setFontSize(this.props.fontSize); - this.editor.on("change", this.onChange); + this.bindings.push(this.editor.onDidChangeModelContent(this.onChange)); } componentWillUnmount() { - this.editor.destroy(); + for (const binding of this.bindings) binding.dispose(); + this.editor.dispose(); } - get value() {return this.editor.session.getValue();} - set value(newValue) { - this.editor.setValue(newValue); - } + get value() {return this.editor.getValue();} + set value(newValue) {this.editor.setValue(newValue);} onChange() { if (this.props.onChange) this.props.onChange(this.value); } showSettings() {return this.editor.keyBinding.$defaultHandler.commands.showSettingsMenu.exec(this.editor);} - resize() {return this.editor.resize();} + resize() {return this.editor.layout();} buildControl(control) { if (control.type == "checkbox") return this.makeCheckbox(control); @@ -100,7 +78,7 @@ export default class CodeEditor extends React.Component { } render() { - if (this.editor && this.editor.resize) this.editor.resize(); + if (this.editor && this.editor.resize) this.editor.layout(); const controlsLeft = this.props.controls.filter(c => c.side != "right").map(this.buildControl.bind(this)); const controlsRight = this.props.controls.filter(c => c.side == "right").map(this.buildControl.bind(this)); @@ -115,7 +93,7 @@ export default class CodeEditor extends React.Component {
-
{this.props.value}
+
; } diff --git a/src/ui/icons/bdlogo.jsx b/src/ui/icons/bdlogo.jsx index d93592c6..e10c56dd 100644 --- a/src/ui/icons/bdlogo.jsx +++ b/src/ui/icons/bdlogo.jsx @@ -2,17 +2,9 @@ import {React} from "modules"; export default class BDLogo extends React.Component { render() { - return - - - - - - + return - - - + ; } diff --git a/src/ui/icons/grid.jsx b/src/ui/icons/grid.jsx new file mode 100644 index 00000000..89f9940b --- /dev/null +++ b/src/ui/icons/grid.jsx @@ -0,0 +1,11 @@ +import {React} from "modules"; + +export default class Grid extends React.Component { + render() { + const size = this.props.size || "20px"; + return + + + ; + } +} \ No newline at end of file diff --git a/src/ui/icons/list.jsx b/src/ui/icons/list.jsx new file mode 100644 index 00000000..9e6f3167 --- /dev/null +++ b/src/ui/icons/list.jsx @@ -0,0 +1,11 @@ +import {React} from "modules"; + +export default class List extends React.Component { + render() { + const size = this.props.size || "20px"; + return + + + ; + } +} \ No newline at end of file diff --git a/src/ui/misc/addoneditor.jsx b/src/ui/misc/addoneditor.jsx index 348b0801..eeca49a0 100644 --- a/src/ui/misc/addoneditor.jsx +++ b/src/ui/misc/addoneditor.jsx @@ -3,7 +3,6 @@ import {React, Strings} from "modules"; import Editor from "../customcss/editor"; import Save from "../icons/save"; import Edit from "../icons/edit"; -import Cog from "../icons/cog"; export default class AddonEditor extends React.Component { @@ -18,8 +17,7 @@ export default class AddonEditor extends React.Component { this.controls = [ {label: React.createElement(Save, {size: "18px"}), tooltip: Strings.CustomCSS.save, onClick: this.save}, - {label: React.createElement(Edit, {size: "18px"}), tooltip: Strings.CustomCSS.openNative, onClick: this.openNative}, - {label: React.createElement(Cog, {size: "18px"}), tooltip: Strings.CustomCSS.settings, onClick: "showSettings"} + {label: React.createElement(Edit, {size: "18px"}), tooltip: Strings.CustomCSS.openNative, onClick: this.openNative} ]; } diff --git a/src/ui/publicservers/menu.js b/src/ui/publicservers/menu.js index 777240b2..3f32e18c 100644 --- a/src/ui/publicservers/menu.js +++ b/src/ui/publicservers/menu.js @@ -2,7 +2,7 @@ import {React, WebpackModules, Strings} from "modules"; import Modals from "../modals"; import SettingsTitle from "../settings/title"; import ServerCard from "./card"; -import EmptyResults from "./noresults"; +import EmptyResults from "../blankslates/noresults"; import Connection from "../../structs/psconnection"; import Search from "../settings/components/search"; import Previous from "../icons/previous"; diff --git a/src/ui/settings/addoncard.jsx b/src/ui/settings/addoncard.jsx index 6b051b65..d865d2c7 100644 --- a/src/ui/settings/addoncard.jsx +++ b/src/ui/settings/addoncard.jsx @@ -22,6 +22,7 @@ const LinkIcons = { }; const Tooltip = WebpackModules.getByDisplayName("Tooltip"); +const MarkdownParser = WebpackModules.getByProps("markdownToReact"); export default class AddonCard extends React.Component { @@ -141,7 +142,7 @@ export default class AddonCard extends React.Component { {this.buildTitle(name, version, author)} -
{description}
+
{MarkdownParser.markdownToReact(description)}
{this.footer} ; } diff --git a/src/ui/settings/addonlist.jsx b/src/ui/settings/addonlist.jsx index c3609a7c..0c6cd10f 100644 --- a/src/ui/settings/addonlist.jsx +++ b/src/ui/settings/addonlist.jsx @@ -1,4 +1,4 @@ -import {React, Settings, Strings, Events, Logger} from "modules"; +import {React, Settings, Strings, Events, Logger, WebpackModules} from "modules"; import Modals from "../modals"; import SettingsTitle from "./title"; @@ -8,15 +8,25 @@ import Dropdown from "./components/dropdown"; import Search from "./components/search"; import ErrorBoundary from "../errorboundary"; +import ListIcon from "../icons/list"; +import GridIcon from "../icons/grid"; +import NoResults from "../blankslates/noresults"; +import EmptyImage from "../blankslates/emptyimage"; + +const Tooltip = WebpackModules.getByDisplayName("Tooltip"); + export default class AddonList extends React.Component { constructor(props) { super(props); - this.state = {sort: "name", ascending: true, query: ""}; + this.state = {sort: "name", ascending: true, query: "", view: "list"}; this.sort = this.sort.bind(this); this.reverse = this.reverse.bind(this); this.search = this.search.bind(this); this.update = this.update.bind(this); + this.listView = this.listView.bind(this); + this.gridView = this.gridView.bind(this); + this.openFolder = this.openFolder.bind(this); } componentDidMount() { @@ -33,6 +43,14 @@ export default class AddonList extends React.Component { this.forceUpdate(); } + listView() { + this.setState({view: "list"}); + } + + gridView() { + this.setState({view: "grid"}); + } + reload() { if (this.props.refreshList) this.props.refreshList(); this.forceUpdate(); @@ -50,6 +68,12 @@ export default class AddonList extends React.Component { this.setState({query: event.target.value.toLocaleLowerCase()}); } + openFolder() { + const shell = require("electron").shell; + const open = shell.openItem || shell.openPath; + open(this.props.folder); + } + get sortOptions() { return [ {label: Strings.Addons.name, value: "name"}, @@ -67,17 +91,25 @@ export default class AddonList extends React.Component { ]; } + get emptyImage() { + return + + ; + } + + makeControlButton(title, children, action, selected = false) { + return + {(props) => { + return ; + }} + ; + } + render() { const {title, folder, addonList, addonState, onChange, reload} = this.props; const showReloadIcon = !Settings.get("settings", "addons", "autoReload"); - const button = folder ? { - title: Strings.Addons.openFolder.format({type: title}), - onClick: () => { - const shell = require("electron").shell; - const open = shell.openItem || shell.openPath; - open(folder); - }} : null; - const sortedAddons = addonList.sort((a, b) => { + const button = folder ? {title: Strings.Addons.openFolder.format({type: title}), onClick: this.openFolder} : null; + let sortedAddons = addonList.sort((a, b) => { const first = a[this.state.sort]; const second = b[this.state.sort]; if (typeof(first) == "string") return first.toLocaleLowerCase().localeCompare(second.toLocaleLowerCase()); @@ -86,34 +118,44 @@ export default class AddonList extends React.Component { return 0; }); if (!this.state.ascending) sortedAddons.reverse(); + if (this.state.query) { + sortedAddons = sortedAddons.filter(addon => { + let matches = addon.name.toLocaleLowerCase().includes(this.state.query); + matches = matches || addon.author.toLocaleLowerCase().includes(this.state.query); + matches = matches || addon.description.toLocaleLowerCase().includes(this.state.query); + if (!matches) return false; + return true; + }); + } return [ } />, -
+
-
-
- - +
+
+
+ + +
+
+ + +
-
- - +
+ {this.makeControlButton("List View", , this.listView, this.state.view === "list")} + {this.makeControlButton("Grid View", , this.gridView, this.state.view === "grid")}
-
, -
+
{sortedAddons.map(addon => { - if (this.state.query) { - let matches = addon.name.toLocaleLowerCase().includes(this.state.query); - matches = matches || addon.author.toLocaleLowerCase().includes(this.state.query); - matches = matches || addon.description.toLocaleLowerCase().includes(this.state.query); - if (!matches) return null; - } const hasSettings = addon.instance && typeof(addon.instance.getSettingsPanel) === "function"; const getSettings = hasSettings && addon.instance.getSettingsPanel.bind(addon.instance); return ; })} + {this.props.addonList.length === 0 && this.emptyImage} + {this.state.query && sortedAddons.length == 0 && this.props.addonList.length !== 0 && }
]; }