From 4cb8964aa84370d78eeeaf8dece4fe8ac621b622 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 29 Mar 2023 00:13:11 -0400 Subject: [PATCH 1/6] Start moving away from Discord components --- renderer/src/structs/markdown.js | 4 +- renderer/src/styles/buttons.css | 539 +++++++++++++++++++++++- renderer/src/styles/index.css | 18 +- renderer/src/styles/text.css | 86 ++++ renderer/src/styles/ui/bdsettings.css | 1 + renderer/src/styles/ui/changelog.css | 217 ++++++++++ renderer/src/styles/ui/flex.css | 124 ++++++ renderer/src/styles/ui/modal.css | 91 ++++ renderer/src/styles/ui/scroller.css | 34 ++ renderer/src/ui/base/button.jsx | 102 +++++ renderer/src/ui/base/flex.jsx | 75 ++++ renderer/src/ui/base/markdown.jsx | 33 ++ renderer/src/ui/base/text.jsx | 54 +++ renderer/src/ui/modals.js | 114 +++-- renderer/src/ui/modals/close.jsx | 16 + renderer/src/ui/modals/confirmation.jsx | 51 +++ renderer/src/ui/modals/content.jsx | 8 + renderer/src/ui/modals/footer.jsx | 18 + renderer/src/ui/modals/header.jsx | 18 + renderer/src/ui/modals/root.jsx | 40 ++ renderer/src/ui/settings/addoncard.jsx | 2 +- renderer/src/ui/settings/title.jsx | 4 +- 22 files changed, 1563 insertions(+), 86 deletions(-) create mode 100644 renderer/src/styles/text.css create mode 100644 renderer/src/styles/ui/changelog.css create mode 100644 renderer/src/styles/ui/flex.css create mode 100644 renderer/src/styles/ui/scroller.css create mode 100644 renderer/src/ui/base/button.jsx create mode 100644 renderer/src/ui/base/flex.jsx create mode 100644 renderer/src/ui/base/markdown.jsx create mode 100644 renderer/src/ui/base/text.jsx create mode 100644 renderer/src/ui/modals/close.jsx create mode 100644 renderer/src/ui/modals/confirmation.jsx create mode 100644 renderer/src/ui/modals/content.jsx create mode 100644 renderer/src/ui/modals/footer.jsx create mode 100644 renderer/src/ui/modals/header.jsx create mode 100644 renderer/src/ui/modals/root.jsx diff --git a/renderer/src/structs/markdown.js b/renderer/src/structs/markdown.js index fc6464ba..041f366a 100644 --- a/renderer/src/structs/markdown.js +++ b/renderer/src/structs/markdown.js @@ -1,9 +1,9 @@ import {DiscordModules, Utilities} from "modules"; export default class SimpleMarkdownExt { - static parseToReact(str) { + static parseToReact(str, inline = true) { if (!this._parser) this._initialize(); - return this._renderer(this._parse(str, {inline: true})); + return this._renderer(this._parse(str, {inline})); } static _initialize() { diff --git a/renderer/src/styles/buttons.css b/renderer/src/styles/buttons.css index eb06877f..79b0eea0 100644 --- a/renderer/src/styles/buttons.css +++ b/renderer/src/styles/buttons.css @@ -1,4 +1,4 @@ -.bd-button { +/* .bd-button { display: inline-flex; justify-content: center; align-items: center; @@ -59,4 +59,539 @@ .bd-button-disabled:hover { cursor: not-allowed; -} \ No newline at end of file +} */ + + + + + + + + + + + +/* Generic Button Styles */ +.bd-button { + position: relative; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + background: none; + border: none; + border-radius: 3px; + font-size: 14px; + font-weight: 500; + line-height: 16px; + padding: 2px 16px; + user-select: none; +} + +.bd-button:disabled { + cursor: not-allowed; + opacity: 0.5; +} + +.bd-button .bd-button-content { + --button--underline-color: transparent; + background-image: linear-gradient(0deg, transparent, transparent 1px, var(--button--underline-color) 0, var(--button--underline-color) 2px, transparent 0); +} + +.bd-button:disabled .bd-button-content { + background-image: none !important; +} + +.bd-button-outlined:disabled { + background-color: transparent !important; +} + + + +/* Button Sizes */ +.bd-button-tiny { + width: 52px; + height: 24px; + min-width: 52px; + min-height: 24px; +} + +.bd-button-small { + width: 60px; + height: 32px; + min-width: 60px; + min-height: 32px; +} + +.bd-button-medium { + width: 96px; + height: 38px; + min-width: 96px; + min-height: 38px; +} + +.bd-button-large { + width: 130px; + height: 44px; + min-width: 130px; + min-height: 44px; +} + +.bd-button-xlarge { + width: 148px; + height: 50px; + min-width: 148px; + min-height: 50px; + font-size: 16px; + line-height: normal; + padding: 2px 20px; +} + +.bd-button-icon { + height: auto; + padding: 4px; +} + +.bd-button-grow, +.bd-button-icon { + width: auto +} + + + +/* Button Looks */ +.bd-button-filled { + -webkit-transition: background-color .17s ease, color .17s ease; + transition: background-color .17s ease, color .17s ease +} + +.bd-button-outlined { + -webkit-transition: color .17s ease, background-color .17s ease, border-color .17s ease; + transition: color .17s ease, background-color .17s ease, border-color .17s ease; + border-width: 1px; + border-style: solid +} + +.bd-button-blank { + background: transparent; + color: currentColor; + border: 0; + padding: 0; + margin: 0 +} + +.bd-button-filled .bd-button-content, +.bd-button-link .bd-button-content, +.bd-button-outlined .bd-button-content { + margin: 0 auto; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + + +/* COLORS */ + +/* Color BD Brand */ +.bd-button-filled.bd-button-color-brand { + color: var(--white-500); + background-color: #3E82E5; /* BD Blue */ +} + +.bd-button-filled.bd-button-color-brand:hover { + background-color: #3875CE; +} + +.bd-button-filled.bd-button-color-brand:active { + background-color: #3268B7; +} + +.bd-button-filled.bd-button-color-brand:disabled { + background-color: #3E82E5; /* BD Blue */ + opacity: 0.4; +} + +.bd-button-outlined.bd-button-color-brand { + color: var(--button-outline-brand-text); + border-color: var(--button-outline-brand-border); +} + +.bd-button-outlined.bd-button-color-brand:hover { + background-color: var(--button-outline-brand-background-hover); + border-color: var(--button-outline-brand-border-hover); + color: var(--button-outline-brand-text-hover); +} + +.bd-button-outlined.bd-button-color-brand:active { + background-color: var(--button-outline-brand-background-active); + border-color: var(--button-outline-brand-border-active); + color: var(--button-outline-brand-text-active); +} + +.bd-button-link.bd-button-color-brand { + color: #3E82E5; /* BD Blue */ +} + +.bd-button-link.bd-button-color-brand:hover .bd-button-content { + --button--underline-color: #3E82E5; /* BD Blue */ +} + + + +/* Color Blurple */ +.bd-button-filled.bd-button-color-blurple { + color: var(--white-500); + background-color: var(--brand-experiment) +} + +.bd-button-filled.bd-button-color-blurple:hover { + background-color: var(--brand-experiment-560) +} + +.bd-button-filled.bd-button-color-blurple:active { + background-color: var(--brand-experiment-600) +} + +.bd-button-filled.bd-button-color-blurple:disabled { + background-color: var(--brand-experiment) +} + +.bd-button-outlined.bd-button-color-blurple { + color: var(--button-outline-brand-text); + border-color: var(--button-outline-brand-border) +} + +.bd-button-outlined.bd-button-color-blurple:hover { + background-color: var(--button-outline-brand-background-hover); + border-color: var(--button-outline-brand-border-hover); + color: var(--button-outline-brand-text-hover) +} + +.bd-button-outlined.bd-button-color-blurple:active { + background-color: var(--button-outline-brand-background-active); + border-color: var(--button-outline-brand-border-active); + color: var(--button-outline-brand-text-active) +} + +.bd-button-link.bd-button-color-blurple { + color: var(--brand-experiment) +} + +.bd-button-link.bd-button-color-blurple:hover .bd-button-content { + --button--underline-color: var(--brand-experiment) +} + + + +/* Color Yellow/Warn */ +.bd-button-filled.bd-button-color-yellow { + color: var(--white-500); + background-color: var(--status-warning) +} + +.bd-button-filled.bd-button-color-yellow:active, +.bd-button-filled.bd-button-color-yellow:hover { + background-color: null; +} + +.bd-button-filled.bd-button-color-yellow:disabled { + background-color: var(--status-warning) +} + +.bd-button-outlined.bd-button-color-yellow { + color: var(--status-warning); + border-color: var(--status-warning) +} + +.bd-button-outlined.bd-button-color-yellow:active { + background-color: hsl(var(--yellow-300-hsl)/.1) +} + +.bd-button-link.bd-button-color-yellow { + color: var(--status-warning) +} + +.bd-button-link.bd-button-color-yellow:hover .bd-button-content { + --button--underline-color: var(--status-warning) +} + + + +/* Color Link */ +.bd-button-filled.bd-button-color-link { + color: var(--white-500); + background-color: var(--text-link) +} + +.bd-button-filled.bd-button-color-link:active, +.bd-button-filled.bd-button-color-link:hover { + background-color: null +} + +.bd-button-filled.bd-button-color-link:disabled { + background-color: var(--text-link) +} + +.bd-button-outlined.bd-button-color-link { + color: var(--text-link); + border-color: var(--text-link) +} + +.bd-button-outlined.bd-button-color-link:active { + background-color: hsl(var(--text-link-hsl)/.1) +} + +.bd-button-link.bd-button-color-link { + color: var(--text-link) +} + +.bd-button-link.bd-button-color-link:hover .bd-button-content { + --button--underline-color: var(--text-link) +} + + + +/* Color White */ +.bd-button-filled.bd-button-color-white { + color: var(--primary-500); + background-color: var(--white-500) +} + +.bd-button-filled.bd-button-color-white:active, +.bd-button-filled.bd-button-color-white:hover { + background-color: null +} + +.bd-button-filled.bd-button-color-white:disabled { + background-color: var(--white-500) +} + +.bd-button-outlined.bd-button-color-white { + color: var(--white-500); + border-color: var(--white-500) +} + +.bd-button-outlined.bd-button-color-white:active { + background-color: hsl(var(--white-500-hsl)/.1) +} + +.bd-button-link.bd-button-color-white { + color: var(--white-500) +} + +.bd-button-link.bd-button-color-white:hover .bd-button-content { + --button--underline-color: var(--white-500) +} + + + +/* Color Red/Danger/Error */ +.bd-button-filled.bd-button-color-red { + color: var(--white-500); + background-color: var(--button-danger-background) +} + +.bd-button-filled.bd-button-color-red:hover { + background-color: var(--button-danger-background-hover) +} + +.bd-button-filled.bd-button-color-red:active { + background-color: var(--button-danger-background-active) +} + +.bd-button-filled.bd-button-color-red:disabled { + background-color: var(--button-danger-background-disabled) +} + +.bd-button-outlined.bd-button-color-red { + color: var(--button-outline-danger-text); + border-color: var(--button-outline-danger-border) +} + +.bd-button-outlined.bd-button-color-red:hover { + background-color: var(--button-outline-danger-background-hover); + border-color: var(--button-outline-danger-border-hover); + color: var(--button-outline-danger-text-hover) +} + +.bd-button-outlined.bd-button-color-red:active { + background-color: var(--button-outline-danger-background-active); + border-color: var(--button-outline-danger-border-active); + color: var(--button-outline-danger-text-active) +} + +.bd-button-link.bd-button-color-red { + color: var(--text-danger) +} + +.bd-button-link.bd-button-color-red:hover .bd-button-content { + --button--underline-color: var(--text-danger) +} + + +/* Color Green Success */ +.bd-button-filled.bd-button-color-green { + color: var(--white-500); + background-color: var(--button-positive-background) +} + +.bd-button-filled.bd-button-color-green:hover { + background-color: var(--button-positive-background-hover) +} + +.bd-button-filled.bd-button-color-green:active { + background-color: var(--button-positive-background-active) +} + +.bd-button-filled.bd-button-color-green:disabled { + background-color: var(--button-positive-background-disabled) +} + +.bd-button-outlined.bd-button-color-green { + color: var(--button-outline-positive-text); + border-color: var(--button-outline-positive-border) +} + +.bd-button-outlined.bd-button-color-green:hover { + background-color: var(--button-outline-positive-background-hover); + border-color: var(--button-outline-positive-border-hover); + color: var(--button-outline-positive-text-hover) +} + +.bd-button-outlined.bd-button-color-green:active { + background-color: var(--button-outline-positive-background-active); + border-color: var(--button-outline-positive-border-active); + color: var(--button-outline-positive-text-active) +} + +.bd-button-link.bd-button-color-green { + color: var(--green-360) +} + +.bd-button-link.bd-button-color-green:hover .bd-button-content { + --button--underline-color: var(--green-360) +} + + + +/* Color Primary/Grey */ +.bd-button-outlined.bd-button-color-primary { + color: var(--button-outline-primary-text); + border-color: var(--button-outline-primary-border) +} + +.bd-button-outlined.bd-button-color-primary:hover { + background-color: var(--button-outline-primary-background-hover); + border-color: var(--button-outline-primary-border-hover); + color: var(--button-outline-primary-text-hover) +} + +.bd-button-outlined.bd-button-color-primary:active { + background-color: var(--button-outline-primary-background-active); + border-color: var(--button-outline-primary-border-active); + color: var(--button-outline-primary-text-active) +} + +.bd-button-filled.bd-button-color-primary { + color: var(--white-500); + background-color: var(--button-secondary-background) +} + +.bd-button-filled.bd-button-color-primary:hover { + background-color: var(--button-secondary-background-hover) +} + +.bd-button-filled.bd-button-color-primary:active { + background-color: var(--button-secondary-background-active) +} + +.bd-button-filled.bd-button-color-primary:disabled { + background-color: var(--button-secondary-background-disabled) +} + +.theme-dark .bd-button-link.bd-button-color-primary { + color: var(--white-500) +} + +.theme-dark .bd-button-link.bd-button-color-primary:hover .bd-button-content { + --button--underline-color: var(--white-500) +} + +.theme-light .bd-button-link.bd-button-color-primary { + color: var(--primary-400) +} + +.theme-light .bd-button-link.bd-button-color-primary:hover .bd-button-content { + --button--underline-color: var(--primary-400) +} + + + +/* Color Transparent */ +.theme-dark .bd-button-filled.bd-button-color-transparent { + color: var(--primary-100); + background-color: hsl(var(--white-500-hsl)/.1) +} + +.theme-dark .bd-button-filled.bd-button-color-transparent:hover { + background-color: hsl(var(--white-500-hsl)/.05) +} + +.theme-dark .bd-button-filled.bd-button-color-transparent:active { + background-color: hsl(var(--white-500-hsl)/.01) +} + +.theme-dark .bd-button-filled.bd-button-color-transparent:disabled { + background-color: hsl(var(--white-500-hsl)/.1) +} + +.theme-dark .bd-button-outlined.bd-button-color-transparent { + color: var(--primary-200); + border-color: var(--primary-200) +} + +.theme-dark .bd-button-outlined.bd-button-color-transparent:active { + background-color: hsl(var(--primary-200-hsl)/.1) +} + +.theme-dark .bd-button-link.bd-button-color-transparent { + color: var(--primary-200) +} + +.theme-dark .bd-button-link.bd-button-color-transparent:hover .bd-button-content { + --button--underline-color: var(--primary-200) +} + +.theme-light .bd-button-filled.bd-button-color-transparent { + color: var(--primary-400); + background-color: hsl(var(--primary-400-hsl)/.01) +} + +.theme-light .bd-button-filled.bd-button-color-transparent:hover { + background-color: hsl(var(--primary-400-hsl)/.2) +} + +.theme-light .bd-button-filled.bd-button-color-transparent:active { + background-color: hsl(var(--primary-400-hsl)/.25) +} + +.theme-light .bd-button-filled.bd-button-color-transparent:disabled { + background-color: hsl(var(--primary-400-hsl)/.01) +} + +.theme-light .bd-button-outlined.bd-button-color-transparent { + color: var(--primary-400); + border-color: var(--primary-400) +} + +.theme-light .bd-button-outlined.bd-button-color-transparent:active { + background-color: hsl(var(--primary-400-hsl)/.1) +} + +.theme-light .bd-button-link.bd-button-color-transparent { + color: var(--primary-400) +} + +.theme-light .bd-button-link.bd-button-color-transparent:hover .bd-button-content { + --button--underline-color: var(--primary-400) +} diff --git a/renderer/src/styles/index.css b/renderer/src/styles/index.css index fd1bc78b..cf0e3945 100644 --- a/renderer/src/styles/index.css +++ b/renderer/src/styles/index.css @@ -6,6 +6,7 @@ @import "./buttons.css"; @import "./spinner.css"; @import "./search.css"; +@import "./text.css"; .bd-chat-badge { vertical-align: bottom; @@ -19,23 +20,6 @@ margin-left: 4px; } -.bd-changelog-modal video, -.bd-changelog-modal img { - width: 100%; - border-radius: 5px; - outline: none; -} - -.bd-changelog-modal code.inline { - padding: 0.2em; - margin: -0.2em 0; - border-radius: 3px; - font-size: 85%; - line-height: 1.125rem; - white-space: pre-wrap; - background: var(--background-secondary); -} - .bd-link { text-decoration: none; } diff --git a/renderer/src/styles/text.css b/renderer/src/styles/text.css new file mode 100644 index 00000000..e29b9de5 --- /dev/null +++ b/renderer/src/styles/text.css @@ -0,0 +1,86 @@ +.bd-text-normal { + color: var(--text-normal); +} + +.bd-text-muted { + color: var(--text-muted); +} + +.bd-text-error { + color: var(--red-400); +} + +.bd-text-brand { + color: var(--text-brand); +} + +.bd-text-link { + color: var(--text-link); +} + +.bd-header-primary { + color: var(--header-primary); +} + +.bd-header-secondary { + color: var(--header-secondary); +} + +.bd-text-yellow { + color: var(--text-warning); +} + +.bd-text-green { + color: var(--text-positive); +} + +.bd-text-red { + color: var(--status-danger); +} + +.bd-text-white { + color: var(--white-500); +} + +.bd-text-10 { + font-size: 10px; + line-height: 12px; +} + +.bd-text-12 { + font-size: 12px; + line-height: 16px; +} + +.bd-text-14 { + font-size: 14px; + line-height: 18px; +} + +.bd-text-16 { + font-size: 16px; + line-height: 20px; +} + +.bd-text-20 { + font-size: 20px; + line-height: 24px; +} + +.bd-text-24 { + font-size: 24px; + line-height: 30px; +} + +.bd-text-32 { + font-size: 32px; + line-height: 40px; +} + +.bd-text-strong { + font-weight: 600; +} + +.bd-selectable { + user-select: text; +} \ No newline at end of file diff --git a/renderer/src/styles/ui/bdsettings.css b/renderer/src/styles/ui/bdsettings.css index abc9c357..0603206c 100644 --- a/renderer/src/styles/ui/bdsettings.css +++ b/renderer/src/styles/ui/bdsettings.css @@ -166,6 +166,7 @@ .bd-settings-title { color: var(--header-primary, #FFFFFF); + display: flex; font-weight: 600; cursor: default; flex: 1; diff --git a/renderer/src/styles/ui/changelog.css b/renderer/src/styles/ui/changelog.css new file mode 100644 index 00000000..aea4237e --- /dev/null +++ b/renderer/src/styles/ui/changelog.css @@ -0,0 +1,217 @@ +.bd-changelog-modal video, +.bd-changelog-modal img { + width: 100%; + border-radius: 5px; + outline: none; +} + +.bd-changelog-modal code.inline { + padding: 0.2em; + margin: -0.2em 0; + border-radius: 3px; + font-size: 85%; + line-height: 1.125rem; + white-space: pre-wrap; + background: var(--background-secondary); +} + +.bd-changelog-modal .bd-modal-content { + font-size: 16px; + line-height: 20px; + padding-bottom: 20px; +} + +.bd-changelog-modal .bd-modal-content .emoji { + object-fit: contain; + width: 22px; + height: 22px; +} + +.bd-changelog-modal .bd-modal-content h1 { + line-height: 20px; + font-size: 16px; +} + +.bd-changelog-modal .bd-modal-content h1, +.bd-changelog-modal .bd-modal-content h2, +.bd-changelog-modal .bd-modal-content strong { + font-weight: 700; +} + +.bd-changelog-modal .bd-modal-content em, +.bd-changelog-modal .bd-modal-content i { + font-style: italic; +} + +.bd-changelog-modal .bd-modal-content p + p { + margin-top: 10px; +} + +.bd-changelog-modal .bd-modal-content ol { + margin: 16px 0 16px 16px; +} + +.bd-changelog-modal .bd-modal-content ol li { + list-style-type: decimal; + margin-bottom: 8px; + margin-left: 20px; +} + +.bd-changelog-modal .bd-modal-content ul { + margin: 20px 0 8px 20px; +} + +.bd-changelog-modal .bd-modal-content ul ul { + margin-top: 8px; +} + +.bd-changelog-modal .bd-modal-content ul li { + position: relative; + list-style: none; + margin-bottom: 8px; + user-select: text; +} + +.bd-changelog-modal .bd-modal-content ul li:last-child { + margin-bottom: 0; +} + +.bd-changelog-modal .bd-modal-content ul li::before { + content: ""; + position: absolute; + top: 10px; + left: -15px; + width: 6px; + height: 6px; + margin-top: -4px; + margin-left: -3px; + border-radius: 50%; + opacity: 0.3; +} + +.bd-changelog-modal .bd-modal-content ul li li::before { + top: 12px; + height: 2px; + border-radius: 0; +} + +.bd-changelog-modal .bd-modal-content img { + width: 100%; +} + +.bd-changelog-modal .bd-modal-content a { + color: hsl(200, calc(var(--saturation-factor, 1) * 100%), 49.4%); + transition: 0.05s; + text-decoration: none; +} + +.bd-changelog-modal .bd-modal-content a:hover { + text-decoration: underline; +} + +.theme-dark .bd-changelog-modal .bd-modal-content ol, +.theme-dark .bd-changelog-modal .bd-modal-content p, +.theme-dark .bd-changelog-modal .bd-modal-content ul li { + color: hsl(210, calc(var(--saturation-factor, 1) * 9.3%), 78.8%); +} + +.theme-dark .bd-changelog-modal .bd-modal-content ul li::before { + background-color: hsl(216, calc(var(--saturation-factor, 1) * 9.8%), 90%); +} + +.theme-light .bd-changelog-modal .bd-modal-content ol, +.theme-light .bd-changelog-modal .bd-modal-content p, +.theme-light .bd-changelog-modal .bd-modal-content ul li { + color: hsl(223, calc(var(--saturation-factor, 1) * 5.8%), 52.9%); +} + +.theme-light .bd-changelog-modal .bd-modal-content ul li::before { + background-color: hsl(223, calc(var(--saturation-factor, 1) * 5.8%), 52.9%); +} + +.bd-changelog-title { + font-weight: 700; + font-size: 16px; + line-height: 20px; + text-transform: uppercase; +} + +.bd-changelog-title { + display: flex; + align-items: center; + margin-top: 40px; +} + +.bd-changelog-title.bd-changelog-first { + margin-top: 20px; +} + +.bd-changelog-title::after { + content: ""; + height: 1px; + flex: 1 1 auto; + margin-left: 4px; + opacity: .6; +} + +.bd-changelog-added { + color: var(--text-positive); +} + +.bd-changelog-added::after { + background-color: var(--info-positive-foreground); +} + +.bd-changelog-fixed { + color: hsl(359, calc(var(--saturation-factor, 1)*87.3%), 59.8%); +} + +.bd-changelog-fixed::after { + background-color: hsl(359, calc(var(--saturation-factor, 1)*87.3%), 59.8%); +} + +.bd-changelog-progress { + color: var(--text-warning); +} + +.bd-changelog-progress::after { + background-color: var(--info-warning-foreground); +} + +.bd-changelog-improved { + color: hsl(235, calc(var(--saturation-factor, 1)*85.6%), 64.7%); +} + +.bd-changelog-improved::after { + background-color: hsl(235, calc(var(--saturation-factor, 1)*85.6%), 64.7%); +} + +.theme-dark .bd-changelog-improved { + color: hsl(235, calc(var(--saturation-factor, 1)*86.1%), 77.5%); +} + +.theme-dark .bd-changelog-improved::after { + background-color: hsl(235, calc(var(--saturation-factor, 1)*86.1%), 77.5%); +} + +.theme-dark .bd-changelog-modal video, +.theme-dark .bd-changelog-modal img { + box-shadow: 0 2px 10px 0 hsl(var(0, calc(var(--saturation-factor, 1)*0%), 0%-hsl)/.2); +} + +.theme-light .bd-changelog-modal video, +.theme-light .bd-changelog-modal img { + box-shadow: 0 2px 10px 0 hsl(var(0, calc(var(--saturation-factor, 1)*0%), 0%-hsl)/.1); +} + +/* .socialLink-1qjJIk { + margin-right: 16px; +} + +.theme-light .socialLink-1qjJIk { + color: hsl(228, calc(var(--saturation-factor, 1)*6%), 32.5%); +} + +.theme-dark .socialLink-1qjJIk { + color: hsl(210, calc(var(--saturation-factor, 1)*9.3%), 78.8%); +} */ \ No newline at end of file diff --git a/renderer/src/styles/ui/flex.css b/renderer/src/styles/ui/flex.css new file mode 100644 index 00000000..60d92be2 --- /dev/null +++ b/renderer/src/styles/ui/flex.css @@ -0,0 +1,124 @@ +.bd-flex { + display: flex; +} + +.bd-flex-align-start { + align-items: flex-start; +} + +.bd-flex-align-end { + align-items: flex-end; +} + +.bd-flex-align-center { + align-items: center; +} + +.bd-flex-align-stretch { + align-items: stretch; +} + +.bd-flex-align-baseline { + align-items: baseline +} + +.bd-flex-justify-start { + justify-content: flex-start; +} + +.bd-flex-justify-end { + justify-content: flex-end; +} + +.bd-flex-justify-center { + justify-content: center; +} + +.bd-flex-justify-around { + justify-content: space-around; +} + +.bd-flex-justify-between { + justify-content: space-between; +} + +.bd-flex-no-wrap { + flex-wrap: nowrap; +} + +.bd-flex-wrap { + flex-wrap: wrap; +} + +.bd-flex-wrap-reverse { + flex-wrap: wrap-reverse; +} + +.bd-flex-horizontal { + flex-direction: row; +} + +.bd-flex-reverse { + flex-direction: row-reverse; +} + +.bd-flex-vertical { + flex-direction: column; +} + +.spacer-2upayl { + flex: 1; + overflow: hidden; +} + +.bd-flex-vertical {} + +.bd-flex-horizontal {} + +.bd-flex-reverse {} + +.bd-flex-horizontal > .spacer-2upayl, +.bd-flex-reverse > .spacer-2upayl, +.bd-flex-vertical > .spacer-2upayl { + min-height: 1px; +} + +.flexCenter-1Mwsxg {} + +.bd-flex {} + +.bd-flex-horizontal {} + +.bd-flex-reverse {} + +.bd-flex-horizontal > .bd-flex, +.bd-flex-horizontal > .bd-flex-child { + margin-left: 10px; + margin-right: 10px; +} + +.bd-flex-horizontal > .bd-flex:first-child, +.bd-flex-horizontal > .bd-flex-child:first-child { + margin-left: 0; +} + +.bd-flex-horizontal > .bd-flex:last-child, +.bd-flex-horizontal > .bd-flex-child:last-child { + margin-right: 0; +} + +.bd-flex-reverse > .bd-flex, +.bd-flex-reverse > .bd-flex-child { + margin-left: 10px; + margin-right: 10px; +} + +.bd-flex-reverse > .bd-flex:first-child, +.bd-flex-reverse > .bd-flex-child:first-child { + margin-right: 0; +} + +.bd-flex-reverse > .bd-flex:last-child, +.bd-flex-reverse > .bd-flex-child:last-child { + margin-left: 0; +} \ No newline at end of file diff --git a/renderer/src/styles/ui/modal.css b/renderer/src/styles/ui/modal.css index 1421e5c9..524c8269 100644 --- a/renderer/src/styles/ui/modal.css +++ b/renderer/src/styles/ui/modal.css @@ -71,3 +71,94 @@ @keyframes bd-modal-open { from {transform: scale(0.7);} } + + + +.bd-modal-root { + display: flex; + flex-direction: column; + background-color: var(--modal-background); + border-radius: 4px; + margin: 0 auto; + pointer-events: all; + position: relative; + max-height: 100%; +} + +.bd-close-button { + height: 26px; + padding: 4px; + transition: opacity 0.2s ease-in-out; + opacity: 0.5; + cursor: pointer; + border-radius: 3px; + color: var(--interactive-normal); + box-sizing: content-box; +} + +.bd-close-button:hover { + opacity: 1; + color: var(--interactive-hover); +} + +.bd-modal-small { + width: 440px; + max-height: 720px; + min-height: 200px; +} + +.bd-modal-standard { + font-size: 13px; + white-space: pre-wrap; + word-wrap: break-word; + width: 490px; + max-height: 800px; +} + +.bd-modal-medium { + width: 600px; + max-height: 800px; + min-height: 400px; +} + +.bd-modal-large { + width: 800px; + max-height: 960px; + min-height: 400px; +} + +.bd-modal-header, +.bd-modal-footer { + position: relative; + flex: 0 0 auto; + padding: 16px; + z-index: 1; + overflow-x: hidden; +} + +.bd-modal-header { + border-radius: 4px 4px 0 0; + transition: box-shadow 0.1s ease-out; + word-wrap: break-word; +} + +.bd-modal-footer { + border-radius: 0 0 5px 5px; + background-color: var(--modal-footer-background); + overflow: hidden; + box-shadow: inset 0 1px 0 hsl(var(--primary-630-hsl)/0.6); +} + +.bd-modal-content { + position: relative; + z-index: 0; + border-radius: 5px 5px 0 0; + padding-left: 16px; + /* padding-right: 16px; */ + overflow-x: hidden; + font-size: 16px; + line-height: 20px; + padding-bottom: 20px; + overflow: hidden scroll; + padding-right: 8px; +} \ No newline at end of file diff --git a/renderer/src/styles/ui/scroller.css b/renderer/src/styles/ui/scroller.css new file mode 100644 index 00000000..de7c74db --- /dev/null +++ b/renderer/src/styles/ui/scroller.css @@ -0,0 +1,34 @@ +.bd-scroller-base { + position: relative; + box-sizing: border-box; + min-height: 0; + flex: 1 1 auto; +} + +.bd-scroller-thin { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thin-thumb) var(--scrollbar-thin-track); +} + +.bd-scroller-thin::-webkit-scrollbar-track { + border-color: var(--scrollbar-thin-track); + background-color: var(--scrollbar-thin-track); + border: 2px solid var(--scrollbar-thin-track); +} + +.bd-scroller-thin::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.bd-scroller-thin::-webkit-scrollbar-corner { + background-color: transparent; +} + +.bd-scroller-thin::-webkit-scrollbar-thumb { + background-clip: padding-box; + border: 2px solid transparent; + border-radius: 4px; + background-color: var(--scrollbar-thin-thumb); + min-height: 40px; +} \ No newline at end of file diff --git a/renderer/src/ui/base/button.jsx b/renderer/src/ui/base/button.jsx new file mode 100644 index 00000000..22e5021e --- /dev/null +++ b/renderer/src/ui/base/button.jsx @@ -0,0 +1,102 @@ +import {React, Utilities} from "modules"; + +// S.Looks = y; +// S.Colors = I; +// S.BorderColors = O; +// S.Hovers = T; +// S.Sizes = v; + +const {useCallback} = React; + +export const Looks = Object.freeze({ + FILLED: "bd-button-filled", + OUTLINED: "bd-button-outlined", + LINK: "bd-button-link", + BLANK: "bd-button-blank" +}); + +export const Colors = Object.freeze({ + BRAND: "bd-button-color-brand", + BLURPLE: "bd-button-color-blurple", + RED: "bd-button-color-red", + GREEN: "bd-button-color-green", + YELLOW: "bd-button-color-yellow", + PRIMARY: "bd-button-color-primary", + LINK: "bd-button-color-link", + WHITE: "bd-button-color-white", + TRANSPARENT: "bd-button-color-transparent", + CUSTOM: "" +}); + + +export const Sizes = Object.freeze({ + NONE: "", + TINY: "bd-button-tiny", + SMALL: "bd-button-small", + MEDIUM: "bd-button-medium", + LARGE: "bd-button-large", + ICON: "bd-button-icon" +}); + + +export default function Button({ + className, + children, + onClick, + onKeyDown, + buttonRef, + disabled = false, + type = "button", + look = Looks.FILLED, + color = Colors.BRAND, + size = Sizes.MEDIUM, + grow = true +}) { + + const handleClick = useCallback(event => { + event.preventDefault(); + event.stopPropagation(); + onClick?.(event); + }, [onClick]); + + return ; +} + +Button.Looks = Looks; +Button.Colors = Colors; +Button.Sizes = Sizes; +// window.BDButton = Button; +// (() => { +// const buttons = []; +// for (const look in window.BDButton.Looks) { +// if (!window.BDButton.Looks[look] || look === "BLANK") continue; +// for (const color in window.BDButton.Colors) { +// if (!window.BDButton.Colors[color]) continue; +// for (const size in window.BDButton.Sizes) { +// if (!window.BDButton.Sizes[size]) continue; +// buttons.push(window.BdApi.React.createElement(window.BDButton, { +// look: window.BDButton.Looks[look], +// color: window.BDButton.Colors[color], +// size: window.BDButton.Sizes[size] +// }, "Hello World!")); +// buttons.push(window.BdApi.React.createElement("br")); +// } +// } +// } +// window.BdApi.showConfirmationModal("Buttons", buttons); +// })(); \ No newline at end of file diff --git a/renderer/src/ui/base/flex.jsx b/renderer/src/ui/base/flex.jsx new file mode 100644 index 00000000..572afbc4 --- /dev/null +++ b/renderer/src/ui/base/flex.jsx @@ -0,0 +1,75 @@ +import {React, Utilities} from "modules"; + + +export const Direction = Object.freeze({ + VERTICAL: "bd-flex-vertical", + HORIZONTAL: "bd-flex-horizontal", + HORIZONTAL_REVERSE: "bd-flex-reverse" +}); + +export const Justify = Object.freeze({ + START: "bd-flex-justify-start", + END: "bd-flex-justify-end", + CENTER: "bd-flex-justify-center", + BETWEEN: "bd-flex-justify-between", + AROUND: "bd-flex-justify-around" +}); + +export const Align = Object.freeze({ + START: "bd-flex-align-start", + END: "bd-flex-align-end", + CENTER: "bd-flex-align-center", + STRETCH: "bd-flex-align-stretch", + BASELINE: "bd-flex-align-baseline" +}); + +export const Wrap = Object.freeze({ + NO_WRAP: "bd-flex-no-wrap", + WRAP: "bd-flex-wrap", + WRAP_REVERSE: "bd-flex-wrap-reverse" +}); + + +export function Child(props) { + if (!props.className) props.className = ""; + props.className = Utilities.className(props.className, "bd-flex-child"); + return ; +} + + +export default function Flex({ + children, + className, + style, + shrink = 1, + grow = 1, + basis = "auto", + direction = Direction.HORIZONTAL, + align = Align.STRETCH, + justify = Justify.START, + wrap = Wrap.NO_WRAP + }) { + return
+ {children} +
; +} + +Flex.Child = Child; +Flex.Direction = Direction; +Flex.Align = Align; +Flex.Justify = Justify; +Flex.Wrap = Wrap; \ No newline at end of file diff --git a/renderer/src/ui/base/markdown.jsx b/renderer/src/ui/base/markdown.jsx new file mode 100644 index 00000000..dc38494e --- /dev/null +++ b/renderer/src/ui/base/markdown.jsx @@ -0,0 +1,33 @@ +import {React, WebpackModules, DiscordModules} from "modules"; + + +const DiscordMarkdown = WebpackModules.find(m => m?.prototype?.render && m.rules); +let rules = {}; +if (DiscordMarkdown) { + rules = { + ...DiscordMarkdown.rules, + link: DiscordModules.SimpleMarkdown.defaultRules.link + }; + + const originalLink = rules.link.react; + rules.link.react = function() { + const original = Reflect.apply(originalLink, undefined, arguments); + original.props.className = "bd-link"; + original.props.target = "_blank"; + original.props.rel = "noopener noreferrer"; + return original; + }; +} + + +export default function Markdown({className, children}) { + if (!DiscordMarkdown) return
{children}
; + + return + {children} + ; +} \ No newline at end of file diff --git a/renderer/src/ui/base/text.jsx b/renderer/src/ui/base/text.jsx new file mode 100644 index 00000000..693678e0 --- /dev/null +++ b/renderer/src/ui/base/text.jsx @@ -0,0 +1,54 @@ +import {React, Utilities} from "modules"; + + +export const Colors = Object.freeze({ + STANDARD: "bd-text-normal", + MUTED: "bd-text-muted", + ERROR: "bd-text-error", + BRAND: "bd-text-brand", + LINK: "bd-text-link", + HEADER_PRIMARY: "bd-header-primary", + HEADER_SECONDARY: "bd-header-secondary", + STATUS_YELLOW: "bd-text-yellow", + STATUS_GREEN: "bd-text-green", + STATUS_RED: "bd-text-red", + ALWAYS_WHITE: "bd-text-white", + CUSTOM: null +}); + + +export const Sizes = Object.freeze({ + SIZE_10: "bd-text-10", + SIZE_12: "bd-text-12", + SIZE_14: "bd-text-14", + SIZE_16: "bd-text-16", + SIZE_20: "bd-text-20", + SIZE_24: "bd-text-24", + SIZE_32: "bd-text-32" +}); + + +export default function Text({tag: Tag = "div", className, children, color = Colors.STANDARD, size = Sizes.SIZE_14, selectable, strong, style}) { + return + {children} + ; +} + +Text.Colors = Colors; +Text.Sizes = Sizes; + +// te = WebpackModules.getModule(m => m?.Sizes?.SIZE_32 && m.Colors) +// foo = [] +// for (const color in te.Colors) foo.push(BdApi.React.createElement(te, {color: te.Colors[color]}, color)) +// for (const size in te.Sizes) foo.push(BdApi.React.createElement(te, {size: te.Sizes[size]}, size)) +// BdApi.showConfirmationModal("Text Elements", foo) \ No newline at end of file diff --git a/renderer/src/ui/modals.js b/renderer/src/ui/modals.js index da72056d..b96335d2 100644 --- a/renderer/src/ui/modals.js +++ b/renderer/src/ui/modals.js @@ -1,14 +1,27 @@ import {Config} from "data"; import Logger from "common/logger"; -import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager, DiscordModules, DiscordClasses} from "modules"; +import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager, DiscordModules} from "modules"; import FormattableString from "../structs/string"; import AddonErrorModal from "./addonerrormodal"; import ErrorBoundary from "./errorboundary"; +import TextElement from "./base/text"; +import ModalRoot from "./modals/root"; +import Flex from "./base/flex"; +import ModalHeader from "./modals/header"; +import ModalContent from "./modals/content"; +import ModalFooter from "./modals/footer"; +import CloseButton from "./modals/close"; + +import ConfirmationModal from "./modals/confirmation"; +import Button from "./base/button"; +import CustomMarkdown from "./base/markdown"; +import SimpleMarkdownExt from "../structs/markdown"; export default class Modals { static get shouldShowAddonErrors() {return Settings.get("settings", "addons", "addonErrors");} + static get hasModalOpen() {return !!document.getElementsByClassName("bd-modal").length;} static get ModalActions() { return this._ModalActions ??= { @@ -16,21 +29,11 @@ export default class Modals { closeModal: WebpackModules.getModule(m => typeof m === "function" && m?.toString().includes("onCloseCallback()"), {searchExports: true}) }; } - static get ModalStack() {return this._ModalStack ??= WebpackModules.getByProps("push", "update", "pop", "popWithKey");} - static get ModalComponents() {return this._ModalComponents ??= WebpackModules.getByProps("Header", "Footer");} - static get ModalRoot() {return this._ModalRoot ??= WebpackModules.getModule(m => m?.toString?.()?.includes("ENTERING") && m?.toString?.()?.includes("headerId"), {searchExports: true});} - static get ModalClasses() {return this._ModalClasses ??= WebpackModules.getByProps("modal", "content");} - static get FlexElements() {return this._FlexElements ??= WebpackModules.getByProps("Child", "Align");} - static get TextElement() {return this._TextElement ??= WebpackModules.getModule(m => m?.Sizes?.SIZE_32 && m.Colors);} - static get ConfirmationModal() {return this._ConfirmationModal ??= WebpackModules.getModule(m => m?.toString?.()?.includes(".confirmButtonColor"), {searchExports: true});} - static get Markdown() {return this._Markdown ??= WebpackModules.find(m => m?.prototype?.render && m.rules);} - static get Buttons() {return this._Buttons ??= WebpackModules.getModule(m => m.BorderColors, {searchExports: true});} + static get ModalQueue() {return this._ModalQueue ??= [];} - - static get hasModalOpen() {return !!document.getElementsByClassName("bd-modal").length;} static async initialize() { - const names = ["ConfirmationModal", "ModalActions", "Markdown", "ModalRoot", "ModalComponents", "Buttons", "TextElement", "FlexElements"]; + const names = ["ModalActions"]; for (const name of names) { let value = this[name]; @@ -154,8 +157,6 @@ export default class Modals { * @returns {string} - the key used for this modal */ static showConfirmationModal(title, content, options = {}) { - const Markdown = this.Markdown; - const ConfirmationModal = this.ConfirmationModal; const ModalActions = this.ModalActions; if (content instanceof FormattableString) content = content.toString(); @@ -163,7 +164,7 @@ export default class Modals { const emptyFunction = () => {}; const {onConfirm = emptyFunction, onCancel = emptyFunction, confirmText = Strings.Modals.okay, cancelText = Strings.Modals.cancel, danger = false, key = undefined} = options; - if (!this.ModalActions || !this.ConfirmationModal || !this.Markdown) { + if (!this.ModalActions) { return this.default(title, content, [ confirmText && {label: confirmText, action: onConfirm}, cancelText && {label: cancelText, action: onCancel, danger} @@ -171,7 +172,7 @@ export default class Modals { } if (!Array.isArray(content)) content = [content]; - content = content.map(c => typeof(c) === "string" ? React.createElement(Markdown, null, c) : c); + content = content.map(c => typeof(c) === "string" ? React.createElement(CustomMarkdown, null, c) : c); const modalKey = ModalActions.openModal(props => { return React.createElement(ErrorBoundary, { @@ -186,7 +187,7 @@ export default class Modals { } }, React.createElement(ConfirmationModal, Object.assign({ header: title, - confirmButtonColor: danger ? this.Buttons.Colors.RED : this.Buttons.Colors.BRAND, + danger: danger, confirmText: confirmText, cancelText: cancelText, onConfirm: onConfirm, @@ -205,8 +206,8 @@ export default class Modals { } this.addonErrorsRef = React.createRef(); - this.ModalActions.openModal(props => React.createElement(ErrorBoundary, null, React.createElement(this.ModalRoot, Object.assign(props, { - size: "medium", + this.ModalActions.openModal(props => React.createElement(ErrorBoundary, null, React.createElement(ModalRoot, Object.assign(props, { + size: ModalRoot.Sizes.MEDIUM, className: "bd-error-modal", children: [ React.createElement(AddonErrorModal, { @@ -215,43 +216,36 @@ export default class Modals { themeErrors: Array.isArray(themeErrors) ? themeErrors : [], onClose: props.onClose }), - React.createElement(this.ModalComponents.Footer, { + React.createElement(ModalFooter, { className: "bd-error-modal-footer", - }, React.createElement(this.Buttons, { - onClick: props.onClose, - className: "bd-button" + }, React.createElement(Button, { + onClick: props.onClose }, Strings.Modals.okay)) ] })))); } static showChangelogModal(options = {}) { - const OriginalModalClasses = WebpackModules.getByProps("hideOnFullscreen", "root"); - const ChangelogModalClasses = WebpackModules.getModule(m => m.modal && m.maxModalWidth); - const ChangelogClasses = WebpackModules.getByProps("fixed", "improved") ?? {maxModalWidth: "490px",video: "video-8B-TdZ",container: "container-3PVapX",image: "image-ZPv20Y",title: "title-2ftWWc",lead: "lead-2VtcIe",added: "added-mQcv9V title-2ftWWc",fixed: "fixed-cTX7Hp title-2ftWWc",improved: "improved-2SJXHz title-2ftWWc",progress: "progress-1DcfFh title-2ftWWc",marginTop: "marginTop-VGmU1T",footer: "footer-1gMODG",socialLink: "socialLink-1qjJIk",premiumBanner: "premiumBanner-FU1Urp",premiumIcon: "premiumIcon-rhwgnW",date: "date-2tmzZM"}; - const TextElement = this.TextElement; - const FlexChild = this.FlexElements; - const MarkdownParser = WebpackModules.getByProps("defaultRules", "parse"); - - if (!OriginalModalClasses || !ChangelogModalClasses || !ChangelogClasses || !TextElement || !FlexChild || !MarkdownParser) return Logger.warn("Modals", "showChangelogModal missing modules"); - const {image = "https://i.imgur.com/wuh5yMK.png", description = "", changes = [], title = "BetterDiscord", subtitle = `v${Config.version}`, footer} = options; const ce = React.createElement; - const changelogItems = [options.video ? ce("video", {src: options.video, poster: options.poster, controls: true, className: ChangelogClasses.video}) : ce("img", {src: image})]; - if (description) changelogItems.push(ce("p", null, MarkdownParser.parse(description))); + const changelogItems = [options.video ? ce("video", {src: options.video, poster: options.poster, controls: true, className: "bd-changelog-poster"}) : ce("img", {src: image, className: "bd-changelog-poster"})]; + if (description) changelogItems.push(ce("p", null, SimpleMarkdownExt.parseToReact(description))); for (let c = 0; c < changes.length; c++) { const entry = changes[c]; - const type = ChangelogClasses[entry.type] ? ChangelogClasses[entry.type] : ChangelogClasses.added; - const margin = c == 0 ? ChangelogClasses.marginTop : ""; - changelogItems.push(ce("h1", {className: `${type} ${margin}`,}, entry.title)); - if (entry.description) changelogItems.push(ce("p", null, MarkdownParser.parse(entry.description))); - const list = ce("ul", null, entry.items.map(i => ce("li", null, MarkdownParser.parse(i)))); + const type = "bd-changelog-" + entry.type; + const margin = c == 0 ? " bd-changelog-first" : ""; + changelogItems.push(ce("h1", {className: `bd-changelog-title ${type}${margin}`,}, entry.title)); + if (entry.description) changelogItems.push(ce("p", null, SimpleMarkdownExt.parseToReact(entry.description))); + const list = ce("ul", null, entry.items.map(i => ce("li", null, SimpleMarkdownExt.parseToReact(i)))); changelogItems.push(list); } - const renderHeader = function() { - return ce(FlexChild, {className: OriginalModalClasses.header, grow: 0, shrink: 0, direction: FlexChild.Direction.VERTICAL}, - ce(TextElement, {tag: "h1", size: TextElement.Sizes.SIZE_20, strong: true}, title), - ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.STANDARD, className: ChangelogClasses.date}, subtitle) + const renderHeader = function(props) { + return ce(ModalHeader, {justify: Flex.Justify.BETWEEN}, + ce(Flex, {direction: Flex.Direction.VERTICAL}, + ce(TextElement, {tag: "h1", size: TextElement.Sizes.SIZE_20, strong: true}, title), + ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.HEADER_SECONDARY}, subtitle) + ), + ce(CloseButton, {onClick: props.onClose}) ); }; @@ -264,22 +258,17 @@ export default class Modals { }; const supportLink = ce("a", {className: `${AnchorClasses.anchor} ${AnchorClasses.anchorUnderlineOnHover}`, onClick: joinSupportServer}, "Join our Discord Server."); const defaultFooter = ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.STANDARD}, "Need support? ", supportLink); - return ce(FlexChild, {className: OriginalModalClasses.footer + " " + OriginalModalClasses.footerSeparator}, - ce(FlexChild.Child, {grow: 1, shrink: 1}, footer ? footer : defaultFooter) - ); + return ce(ModalFooter, null, ce(Flex.Child, {grow: 1, shrink: 1}, footer ? footer : defaultFooter)); }; - const body = ce("div", { - className: `${OriginalModalClasses.content} ${ChangelogClasses.container} ${ChangelogModalClasses.content} ${DiscordClasses.Scrollers.thin}` - }, changelogItems); + const body = ce(ModalContent, null, changelogItems); const key = this.ModalActions.openModal(props => { - return React.createElement(ErrorBoundary, null, React.createElement(this.ModalRoot, Object.assign({ - className: `bd-changelog-modal ${OriginalModalClasses.root} ${OriginalModalClasses.small} ${ChangelogModalClasses.modal}`, - selectable: true, - onScroll: _ => _, - onClose: _ => _, - }, props), renderHeader(), body, renderFooter())); + return React.createElement(ErrorBoundary, null, React.createElement(ModalRoot, Object.assign({ + className: `bd-changelog-modal`, + size: ModalRoot.Sizes.SMALL, + style: ModalRoot.Styles.STANDARD + }, props), renderHeader(props), body, renderFooter())); }); return key; } @@ -317,17 +306,16 @@ export default class Modals { } if (typeof(child) === "function") child = React.createElement(child); - const mc = this.ModalComponents; const modal = props => { - return React.createElement(ErrorBoundary, {}, React.createElement(this.ModalRoot, Object.assign({size: mc.Sizes.MEDIUM, className: "bd-addon-modal" + " " + mc.Sizes.MEDIUM}, props), - React.createElement(mc.Header, {separator: false, className: "bd-addon-modal-header"}, - React.createElement(this.TextElement, {tag: "h1", size: this.TextElement.Sizes.SIZE_20, strong: true}, `${name} Settings`) + return React.createElement(ErrorBoundary, {}, React.createElement(ModalRoot, Object.assign({size: ModalRoot.Sizes.MEDIUM, className: "bd-addon-modal" + " " + ModalRoot.Sizes.MEDIUM}, props), + React.createElement(ModalHeader, null, + React.createElement(TextElement, {tag: "h1", size: TextElement.Sizes.SIZE_20, strong: true}, `${name} Settings`) ), - React.createElement(mc.Content, {className: "bd-addon-modal-settings"}, + React.createElement(ModalContent, null, React.createElement(ErrorBoundary, {}, child) ), - React.createElement(mc.Footer, {className: "bd-addon-modal-footer"}, - React.createElement(this.Buttons, {onClick: props.onClose, className: "bd-button"}, Strings.Modals.done) + React.createElement(ModalFooter, null, + React.createElement(Button, {onClick: props.onClose}, Strings.Modals.done) ) )); }; diff --git a/renderer/src/ui/modals/close.jsx b/renderer/src/ui/modals/close.jsx new file mode 100644 index 00000000..9703b9c5 --- /dev/null +++ b/renderer/src/ui/modals/close.jsx @@ -0,0 +1,16 @@ +import {React} from "modules"; +import Button from "../base/button"; +import Close from "../icons/close"; + + +export default function CloseButton({onClick}) { + return ; +} \ No newline at end of file diff --git a/renderer/src/ui/modals/confirmation.jsx b/renderer/src/ui/modals/confirmation.jsx new file mode 100644 index 00000000..acfd8912 --- /dev/null +++ b/renderer/src/ui/modals/confirmation.jsx @@ -0,0 +1,51 @@ +import {React} from "modules"; +import Root from "./root"; +import Header from "./header"; +import Footer from "./footer"; +import Content from "./content"; + +import Text from "../base/text"; +import Button from "../base/button"; +import CloseButton from "./close"; + +const {useRef, useEffect} = React; + + +export default function ConfirmationModal({transitionState, onClose, header, children, danger = false, onCancel = () => {}, onConfirm = () => {}, cancelText = "Cancel", confirmText = "Okay"}) { + + useEffect(() => { + setTimeout(() => buttonRef?.current?.focus?.(), 0); + }, []); + + const buttonRef = useRef(null); + + + return +
{header}
+ {children} +
+ {confirmText && } + {cancelText && } +
+
; +} \ No newline at end of file diff --git a/renderer/src/ui/modals/content.jsx b/renderer/src/ui/modals/content.jsx new file mode 100644 index 00000000..999a25e2 --- /dev/null +++ b/renderer/src/ui/modals/content.jsx @@ -0,0 +1,8 @@ +import {React, Utilities} from "modules"; + + +export default function Content({id, className, children, scroller = true}) { + return
+ {children} +
; +} \ No newline at end of file diff --git a/renderer/src/ui/modals/footer.jsx b/renderer/src/ui/modals/footer.jsx new file mode 100644 index 00000000..9da8a9ce --- /dev/null +++ b/renderer/src/ui/modals/footer.jsx @@ -0,0 +1,18 @@ +import {React, Utilities} from "modules"; +import Flex from "../base/flex"; + + +export default function Header({id, className, children}) { + return + {children} + ; +} \ No newline at end of file diff --git a/renderer/src/ui/modals/header.jsx b/renderer/src/ui/modals/header.jsx new file mode 100644 index 00000000..4ba003c1 --- /dev/null +++ b/renderer/src/ui/modals/header.jsx @@ -0,0 +1,18 @@ +import {React, Utilities} from "modules"; +import Flex from "../base/flex"; + + +export default function Header({id, className, children}) { + return + {children} + ; +} \ No newline at end of file diff --git a/renderer/src/ui/modals/root.jsx b/renderer/src/ui/modals/root.jsx new file mode 100644 index 00000000..ff5b7a93 --- /dev/null +++ b/renderer/src/ui/modals/root.jsx @@ -0,0 +1,40 @@ +import {React, Utilities, WebpackModules} from "modules"; + +const Spring = WebpackModules.getByProps("useSpring", "animated"); +const Anims = WebpackModules.getByProps("Easing"); + + +export const Sizes = Object.freeze({ + SMALL: "bd-modal-small", + MEDIUM: "bd-modal-medium", + LARGE: "bd-modal-large", + DYNAMIC: "" +}); + +export const Styles = Object.freeze({ + STANDARD: "bd-modal-standard", + CUSTOM: "" +}); + + +export default function ModalRoot({className, transitionState, children, size = Sizes.DYNAMIC, style = Styles.CUSTOM}) { + const visible = transitionState == 0 || transitionState == 1; + const springStyles = Spring.useSpring({ + opacity: visible ? 1 : 0, + transform: visible ? "scale(1)" : "scale(0.7)", + config: { + duration: visible ? 300 : 100, + easing: visible ? Anims.Easing.inOut(Anims.Easing.back()) : Anims.Easing.quad, + clamp: true + } + }); + return + {children} + ; +} + +ModalRoot.Sizes = Sizes; +ModalRoot.Styles = Styles; \ No newline at end of file diff --git a/renderer/src/ui/settings/addoncard.jsx b/renderer/src/ui/settings/addoncard.jsx index 927877cc..d3566cca 100644 --- a/renderer/src/ui/settings/addoncard.jsx +++ b/renderer/src/ui/settings/addoncard.jsx @@ -57,7 +57,7 @@ function makeButton(title, children, action, {isControl = false, danger = false, const ButtonType = isControl ? "button" : "div"; return {(props) => { - return {children}; + return {children}; }} ; } diff --git a/renderer/src/ui/settings/title.jsx b/renderer/src/ui/settings/title.jsx index cd81f6d7..fa27941b 100644 --- a/renderer/src/ui/settings/title.jsx +++ b/renderer/src/ui/settings/title.jsx @@ -1,5 +1,7 @@ import {React} from "modules"; +import Button from "../base/button"; + const {useCallback} = React; @@ -18,7 +20,7 @@ export default function SettingsTitle({isGroup, className, button, onClick, text const titleClass = className ? `${baseClass} ${className}` : baseClass; return

{onClick?.();}}> {text} - {button && } + {button && } {otherChildren}

; From 815d91e76fd55b862569968cbc49ca202a51220b Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Fri, 31 Mar 2023 02:06:49 -0400 Subject: [PATCH 2/6] Separate changelog modal --- renderer/src/ui/modals.js | 49 ++----------------- renderer/src/ui/modals/changelog.jsx | 63 +++++++++++++++++++++++++ renderer/src/ui/modals/confirmation.jsx | 3 +- 3 files changed, 68 insertions(+), 47 deletions(-) create mode 100644 renderer/src/ui/modals/changelog.jsx diff --git a/renderer/src/ui/modals.js b/renderer/src/ui/modals.js index b96335d2..e458a78f 100644 --- a/renderer/src/ui/modals.js +++ b/renderer/src/ui/modals.js @@ -1,21 +1,19 @@ import {Config} from "data"; import Logger from "common/logger"; -import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager, DiscordModules} from "modules"; +import {WebpackModules, React, ReactDOM, Settings, Strings, DOMManager} from "modules"; import FormattableString from "../structs/string"; import AddonErrorModal from "./addonerrormodal"; import ErrorBoundary from "./errorboundary"; import TextElement from "./base/text"; import ModalRoot from "./modals/root"; -import Flex from "./base/flex"; import ModalHeader from "./modals/header"; import ModalContent from "./modals/content"; import ModalFooter from "./modals/footer"; -import CloseButton from "./modals/close"; import ConfirmationModal from "./modals/confirmation"; import Button from "./base/button"; import CustomMarkdown from "./base/markdown"; -import SimpleMarkdownExt from "../structs/markdown"; +import ChangelogModal from "./modals/changelog"; export default class Modals { @@ -226,49 +224,10 @@ export default class Modals { } static showChangelogModal(options = {}) { - const {image = "https://i.imgur.com/wuh5yMK.png", description = "", changes = [], title = "BetterDiscord", subtitle = `v${Config.version}`, footer} = options; - const ce = React.createElement; - const changelogItems = [options.video ? ce("video", {src: options.video, poster: options.poster, controls: true, className: "bd-changelog-poster"}) : ce("img", {src: image, className: "bd-changelog-poster"})]; - if (description) changelogItems.push(ce("p", null, SimpleMarkdownExt.parseToReact(description))); - for (let c = 0; c < changes.length; c++) { - const entry = changes[c]; - const type = "bd-changelog-" + entry.type; - const margin = c == 0 ? " bd-changelog-first" : ""; - changelogItems.push(ce("h1", {className: `bd-changelog-title ${type}${margin}`,}, entry.title)); - if (entry.description) changelogItems.push(ce("p", null, SimpleMarkdownExt.parseToReact(entry.description))); - const list = ce("ul", null, entry.items.map(i => ce("li", null, SimpleMarkdownExt.parseToReact(i)))); - changelogItems.push(list); - } - const renderHeader = function(props) { - return ce(ModalHeader, {justify: Flex.Justify.BETWEEN}, - ce(Flex, {direction: Flex.Direction.VERTICAL}, - ce(TextElement, {tag: "h1", size: TextElement.Sizes.SIZE_20, strong: true}, title), - ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.HEADER_SECONDARY}, subtitle) - ), - ce(CloseButton, {onClick: props.onClose}) - ); - }; - - const renderFooter = () => { - const AnchorClasses = WebpackModules.getByProps("anchorUnderlineOnHover") || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"}; - const joinSupportServer = (click) => { - click.preventDefault(); - click.stopPropagation(); - DiscordModules.InviteActions.acceptInviteAndTransitionToInviteChannel({inviteKey: "0Tmfo5ZbORCRqbAd"}); - }; - const supportLink = ce("a", {className: `${AnchorClasses.anchor} ${AnchorClasses.anchorUnderlineOnHover}`, onClick: joinSupportServer}, "Join our Discord Server."); - const defaultFooter = ce(TextElement, {size: TextElement.Sizes.SIZE_12, color: TextElement.Colors.STANDARD}, "Need support? ", supportLink); - return ce(ModalFooter, null, ce(Flex.Child, {grow: 1, shrink: 1}, footer ? footer : defaultFooter)); - }; - - const body = ce(ModalContent, null, changelogItems); + options = Object.assign({image: "https://i.imgur.com/wuh5yMK.png", description: "", changes: [], title: "BetterDiscord", subtitle: `v${Config.version}`}, options); const key = this.ModalActions.openModal(props => { - return React.createElement(ErrorBoundary, null, React.createElement(ModalRoot, Object.assign({ - className: `bd-changelog-modal`, - size: ModalRoot.Sizes.SMALL, - style: ModalRoot.Styles.STANDARD - }, props), renderHeader(props), body, renderFooter())); + return React.createElement(ErrorBoundary, null, React.createElement(ChangelogModal, Object.assign(options, props))); }); return key; } diff --git a/renderer/src/ui/modals/changelog.jsx b/renderer/src/ui/modals/changelog.jsx new file mode 100644 index 00000000..3e9fb800 --- /dev/null +++ b/renderer/src/ui/modals/changelog.jsx @@ -0,0 +1,63 @@ +import {React, DiscordModules, WebpackModules} from "modules"; +import Root from "./root"; +import Header from "./header"; +import Footer from "./footer"; +import Content from "./content"; + +import Flex from "../base/flex"; +import Text from "../base/text"; +import CloseButton from "./close"; + +import SimpleMarkdownExt from "../../structs/markdown"; + +const {useMemo} = React; + + +const AnchorClasses = WebpackModules.getByProps("anchorUnderlineOnHover") || {anchor: "anchor-3Z-8Bb", anchorUnderlineOnHover: "anchorUnderlineOnHover-2ESHQB"}; +const joinSupportServer = (click) => { + click.preventDefault(); + click.stopPropagation(); + DiscordModules.InviteActions.acceptInviteAndTransitionToInviteChannel({inviteKey: "0Tmfo5ZbORCRqbAd"}); +}; + +const supportLink = Join our Discord Server.; +const defaultFooter = Need support? {supportLink}; + + +export default function ChangelogModal({transitionState, footer, title, subtitle, onClose, video, poster, image, description, changes}) { + + const ChangelogHeader = useMemo(() =>
+ + {title} + {subtitle} + + +
, [title, subtitle, onClose]); + + const ChangelogFooter = useMemo(() =>
+ + {footer ? footer : defaultFooter} + +
, [footer]); + + const changelogItems = useMemo(() => { + const items = [video ?