diff --git a/pkg/webui/account/views/app/index.js b/pkg/webui/account/views/app/index.js index 4366fd5243..b556c5edb4 100644 --- a/pkg/webui/account/views/app/index.js +++ b/pkg/webui/account/views/app/index.js @@ -64,29 +64,49 @@ const getScrollRestorationKey = location => { return `${pathname}${page ? `?page=${page}` : ''}` } -const Layout = () => ( - <> - - - - - -
-
-
- - -
-
-
-
-
-
- -) +const Layout = () => { + const darkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches + + const toggleTheme = theme => { + const htmlElement = document.documentElement + + if (theme === 'dark') { + htmlElement.classList.add('dark') + htmlElement.classList.remove('light') + } else { + htmlElement.classList.add('light') + htmlElement.classList.remove('dark') + } + } + + useEffect(() => { + toggleTheme(darkTheme ? 'dark' : 'light') + }, [darkTheme]) + + return ( + <> + + + + + +
+
+
+ + +
+
+
+
+
+
+ + ) +} const AccountRoot = () => { const user = useSelector(selectUser) diff --git a/pkg/webui/assets/misc/console-theme-dark.png b/pkg/webui/assets/misc/console-theme-dark.png new file mode 100644 index 0000000000..ec17b96f9a Binary files /dev/null and b/pkg/webui/assets/misc/console-theme-dark.png differ diff --git a/pkg/webui/assets/misc/console-theme-light.png b/pkg/webui/assets/misc/console-theme-light.png new file mode 100644 index 0000000000..e00d87c2df Binary files /dev/null and b/pkg/webui/assets/misc/console-theme-light.png differ diff --git a/pkg/webui/assets/misc/console-theme-system.png b/pkg/webui/assets/misc/console-theme-system.png new file mode 100644 index 0000000000..d3cc6918a0 Binary files /dev/null and b/pkg/webui/assets/misc/console-theme-system.png differ diff --git a/pkg/webui/assets/misc/logo-LoRa-cloud-default.svg b/pkg/webui/assets/misc/logo-LoRa-cloud-default.svg new file mode 100644 index 0000000000..f1f6ad7041 --- /dev/null +++ b/pkg/webui/assets/misc/logo-LoRa-cloud-default.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pkg/webui/assets/misc/logo-LoRa-cloud-white.svg b/pkg/webui/assets/misc/logo-LoRa-cloud-white.svg new file mode 100644 index 0000000000..8b6f3b1ca9 --- /dev/null +++ b/pkg/webui/assets/misc/logo-LoRa-cloud-white.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pkg/webui/assets/misc/lora-cloud.png b/pkg/webui/assets/misc/lora-cloud.png deleted file mode 100644 index 3bfd08dc91..0000000000 Binary files a/pkg/webui/assets/misc/lora-cloud.png and /dev/null differ diff --git a/pkg/webui/assets/static/logo-tts-horizontal-white.svg b/pkg/webui/assets/static/logo-tts-horizontal-white.svg new file mode 100644 index 0000000000..39b48f6853 --- /dev/null +++ b/pkg/webui/assets/static/logo-tts-horizontal-white.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/pkg/webui/assets/static/tts-logo-icon-white.svg b/pkg/webui/assets/static/tts-logo-icon-white.svg new file mode 100644 index 0000000000..7216bd60cb --- /dev/null +++ b/pkg/webui/assets/static/tts-logo-icon-white.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pkg/webui/components/button/button.styl b/pkg/webui/components/button/button.styl index fade683c19..4b7a7e78cb 100644 --- a/pkg/webui/components/button/button.styl +++ b/pkg/webui/components/button/button.styl @@ -53,7 +53,7 @@ position: absolute top: 4px left: 17px - border: 3px solid var(--c-border-neutral-min) + border: 3px solid light-dark(var(--c-border-neutral-min), var(--c-bg-neutral-light)) border-radius: 100% size: 13px box-sizing: border-box @@ -72,15 +72,16 @@ height: 2.5rem &.primary:not(.naked) - color: var(--c-text-neutral-min) - background-color: var(--c-text-brand-normal) + color: light-dark(var(--c-text-neutral-min), var(--c-text-neutral-heavy)) + background-color: var(--c-bg-brand-normal) &:disabled + color: light-dark(var(--c-text-neutral-min), var(--c-text-neutral-light)) background-color: var(--c-border-neutral-light) &:not(:disabled) &:hover - background-color: var(--c-text-brand-normal-hover) + background-color: var(--c-bg-brand-normal-hover) +focus-visible() background-color: var(--c-text-brand-normal-hover) @@ -105,6 +106,7 @@ background-color: var(--c-bg-warning-normal-disabled) &.danger + color: light-dark(var(--c-text-neutral-min), var(--c-text-neutral-heavy)) background-color: var(--c-bg-error-normal) &:not(:disabled) @@ -118,19 +120,20 @@ background-color: var(--c-bg-error-normal-active) &:disabled + color: light-dark(var(--c-text-neutral-min), $c.error-300) background-color: var(--c-bg-error-normal-disabled) .expand-icon - color: var(--c-text-neutral-min) + color: light-dark(var(--c-text-neutral-min), var(--c-text-neutral-heavy)) &.secondary color: var(--c-text-neutral-heavy) - background-color: var(--c-bg-neutral-min) - border: solid 1px var(--c-border-neutral-light) + background-color: light-dark(var(--c-text-neutral-min), var(--c-bg-neutral-light)) + border: solid 1px light-dark(var(--c-border-neutral-light), var(--c-border-neutral-normal)) &:not(:disabled) &:hover - background-color: var(--c-bg-neutral-light) + background-color: light-dark(var(--c-bg-neutral-light), var(--c-bg-neutral-extralight-hover)) +focus-visible() gradient-border(var(--c-bg-neutral-light), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) @@ -156,22 +159,23 @@ &.danger color: var(--c-text-error-normal) - border-color: var(--c-border-error-normal) + border-color: light-dark(var(--c-text-error-normal), var(--c-border-error-extralight)) + background-color: var(--c-bg-error-bold) &:not(:disabled) &:hover - gradient-border(var(--c-bg-error-extralight), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) + background-color: light-dark(var(--c-bg-error-bold), var(--c-bg-error-heavy)) +focus-visible() - gradient-border(var(--c-bg-error-extralight), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) + background-color: var(--c-bg-error-heavy) &:active - gradient-border(var(--c-bg-error-extralight), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) + background-color: var(--c-bg-error-heavy) box-shadow: none &:disabled - color: var(--c-text-neutral-extralight) - background-color: var(--c-bg-neutral-semilight) + color: light-dark(var(--c-text-neutral-extralight), var(--c-text-neutral-light)) + background-color: light-dark(var(--c-bg-neutral-semilight), var(--c-bg-neutral-light)) border-color: var(--c-border-neutral-light) &.small @@ -184,8 +188,8 @@ height: 1.2rem &.tertiary - color: var(--c-text-brand-normal) - background-color: var(--c-bg-brand-light) + color: light-dark(var(--c-text-brand-normal), var(--c-text-brand-light)) + background-color: light-dark(var(--c-bg-brand-light), var(--c-bg-brand-bold)) &:disabled color: var(--c-text-neutral-light) @@ -193,7 +197,7 @@ &:not(:disabled) &:hover - background-color: var(--c-bg-brand-semilight) + background-color: light-dark(var(--c-bg-brand-semilight), var(--c-bg-brand-heavy)) +focus-visible() background-color: var(--c-bg-brand-light) @@ -220,11 +224,12 @@ &.danger color: var(--c-text-danger-normal) - background-color: var(--c-bg-error-light) + border-color: light-dark(var(--c-border-error-normal)var(--c-border-error-extralight)) + background-color: light-dark(var(--c-bg-error-light), var(--c-bg-error-bold)) &:not(:disabled) &:hover - background-color: var(--c-bg-error-semilight) + background-color: light-dark(var(--c-bg-error-semilight), var(--c-bg-error-heavy)) +focus-visible() background-color: var(--c-bg-error-semilight) @@ -239,25 +244,37 @@ color: var(--c-text-neutral-light) &:disabled - color: var(--c-text-neutral-extralight) + color: var(--c-bg-brand-normal-disabled) &:not(:disabled) &:hover - background-color: rgba(0,0,0,.04) + background-color: light-dark(var(--c-bg-neutral-light), var(--c-bg-neutral-semilight)) color: var(--c-text-neutral-heavy) +focus-visible() - background-color: rgba(0,0,0,.04) + background-color: var(--c-bg-neutral-light) color: var(--c-text-neutral-heavy) &:active background-color: var(--c-bg-neutral-semilight) + &.danger + background-color: unset + color: var(--c-text-error-normal) + + &:hover + background-color: light-dark(var(--c-bg-error-heavy), var(--c-bg-error-bold)) + color: var(--c-text-error-normal) + + &:disabled + color: light-dark(var(--c-text-neutral-extralight), var(--c-text-neutral-light)) + background-color: light-dark(var(--c-bg-neutral-semilight), var(--c-bg-neutral-light)) + &.primary.naked color: var(--c-bg-brand-normal) &:disabled - color: var(--c-bg-brand-normal-disabled) + color: light-dark(var(--c-text-neutral-extralight), var(--c-text-neutral-light)) &:not(:disabled) &:hover @@ -285,7 +302,8 @@ color: var(--c-text-error-normal) &:hover - color: var(--c-text-error-normal-hover) + background-color: light-dark(var(--c-bg-error-heavy), var(--c-bg-error-bold)) + color: var(--c-text-error-normal) &.with-dropdown&.only-icon:not(&.no-dropdown-icon) width: fit-content diff --git a/pkg/webui/components/code-editor/code-editor.styl b/pkg/webui/components/code-editor/code-editor.styl index 82bec8fa7f..6b1b142323 100644 --- a/pkg/webui/components/code-editor/code-editor.styl +++ b/pkg/webui/components/code-editor/code-editor.styl @@ -13,7 +13,7 @@ // limitations under the License. .wrapper - border: 1px solid var(--c-border-neutral-light) + border: 1px solid light-dark(var(--c-border-neutral-light), var(--c-border-neutral-normal)) border-radius: $br.l transition: border-color .2s position: relative diff --git a/pkg/webui/components/code-editor/ttn-theme.js b/pkg/webui/components/code-editor/ttn-theme.js index 9b90fed27f..6846cabfbf 100644 --- a/pkg/webui/components/code-editor/ttn-theme.js +++ b/pkg/webui/components/code-editor/ttn-theme.js @@ -152,13 +152,8 @@ ace.define( exports.cssClass = 'ace-ttn-dark' exports.cssText = ` .ace-ttn-dark .ace_gutter { - background: var(--c-bg-neutral-bold); color: var(--c-text-neutral-light); } -.ace-ttn-dark { - background: var(--c-bg-neutral-heavy); - color: var(--c-text-neutral-min); -} // begin language .ace-ttn-dark .ace_string { color: var(--c-text-info-normal); @@ -217,12 +212,8 @@ ace.define( .ace-ttn-dark .ace_cursor { color: var(--c-text-neutral-min); } -.ace-ttn-dark.ace_focus .ace_marker-layer .ace_active-line { - background: var(--c-bg-brand-extralight); - opacity: 0.2; -} .ace-ttn-dark .ace_marker-layer .ace_active-line { - background: var(--c-bg-neutral-bold); + background: var(--c-bg-neutral-extalight); } .ace-ttn-dark .ace_marker-layer .ace_selection { background: var(--c-bg-info-normal); @@ -244,7 +235,7 @@ ace.define( border: 1px solid var(--c-border-neutral-bold); } .ace-ttn-dark .ace_gutter-active-line { - background-color: var(--c-bg-neutral-semibold); + background-color: var(--c-bg-neutral-normal); } .ace-ttn-dark .ace_marker-layer .ace_selected-word { border: 1px solid var(--c-border-brand-norma); diff --git a/pkg/webui/components/delete-modal-button/index.js b/pkg/webui/components/delete-modal-button/index.js index 07a34ca38c..bf27f06a6b 100644 --- a/pkg/webui/components/delete-modal-button/index.js +++ b/pkg/webui/components/delete-modal-button/index.js @@ -71,7 +71,7 @@ const DeleteModalButton = props => { icon={IconTrash} onApprove={handleDeleteApprove} onCancel={onCancel} - secondary + naked danger small={small} message={onlyIcon ? undefined : message} diff --git a/pkg/webui/components/dropdown/dropdown.styl b/pkg/webui/components/dropdown/dropdown.styl index 33e568e2c6..888a24fa2e 100644 --- a/pkg/webui/components/dropdown/dropdown.styl +++ b/pkg/webui/components/dropdown/dropdown.styl @@ -19,7 +19,7 @@ ul.dropdown border-radius: $br.l border: 1px solid var(--c-border-neutral-light) list-style-type: none - background: var(--c-bg-neutral-extralight) + background: light-dark(var(--c-bg-neutral-extralight), var(--c-bg-neutral-light)) box-shadow: 0px 3px 16px 0px rgba(0, 0, 0, .06) position: absolute padding: $cs.xs @@ -111,7 +111,7 @@ ul.dropdown hr height: 1px - background-color: var(--c-border-neutral-light) + background-color: light-dark(var(--c-border-neutral-light), var(--c-border-neutral-normal)) margin: $cs.xs 0 li.dropdown-header-item @@ -146,10 +146,10 @@ ul.dropdown border-radius: $br.m &-active - color: var(--c-text-neutral-extralight) + color: light-dark(var(--c-text-neutral-extralight), var(--c-text-neutral-light)) &:hover:not(.button-active) - background: var(--c-bg-neutral-light) + background: light-dark(var(--c-bg-neutral-light), var(--c-bg-neutral-normal)) .icon margin-right: $cs.xs diff --git a/pkg/webui/components/header/index.js b/pkg/webui/components/header/index.js index 523e87f9b4..4f9be3c55a 100644 --- a/pkg/webui/components/header/index.js +++ b/pkg/webui/components/header/index.js @@ -14,6 +14,7 @@ import React from 'react' import classnames from 'classnames' +import { useSelector } from 'react-redux' import { IconStar, IconPlus, IconInbox, IconLayoutSidebarLeftExpand } from '@ttn-lw/components/icon' import Button from '@ttn-lw/components/button' @@ -24,6 +25,8 @@ import AppStatusBadge from '@console/containers/app-status-badge' import PropTypes from '@ttn-lw/lib/prop-types' import sharedMessages from '@ttn-lw/lib/shared-messages' +import { selectConsolePreferences } from '@console/store/selectors/user-preferences' + import Link from '../link' import style from './header.styl' @@ -49,6 +52,8 @@ const Header = ({ ...rest }) => { const LinkComponent = safe ? 'a' : Link + const consolePreferences = useSelector(selectConsolePreferences) + const darkTheme = consolePreferences.console_theme === 'CONSOLE_THEME_DARK' return (
- + ) : ( @@ -86,7 +91,7 @@ const Header = ({
diff --git a/pkg/webui/components/link/link.styl b/pkg/webui/components/link/link.styl index af9a6c8ecc..fd1a5db12c 100644 --- a/pkg/webui/components/link/link.styl +++ b/pkg/webui/components/link/link.styl @@ -34,10 +34,10 @@ color: var(--c-text-brand-normal-active) &.secondary - color: var(--c-text-neutral-light) + color: light-dark(var(--c-text-neutral-light), var(--c-text-neutral-semilight)) &:active:not(:disabled), &:hover:not(:disabled) - color: var(--c-text-neutral-heavy) + color: light-dark($c.neutral-500, var(--c-text-neutral-heavy)) // Exception from using tokens. &.link-visited &:visited diff --git a/pkg/webui/components/map/map.styl b/pkg/webui/components/map/map.styl index 51d8436b5e..838e0d9ec3 100644 --- a/pkg/webui/components/map/map.styl +++ b/pkg/webui/components/map/map.styl @@ -297,7 +297,7 @@ display: block text-align: center text-decoration: none - color: var(--c-bg-neutral-max) + color: light-dark(var(--c-bg-neutral-heavy), var(--c-bg-neutral-min)) } .leaflet-bar a, .leaflet-control-layers-toggle { diff --git a/pkg/webui/components/modal/modal.styl b/pkg/webui/components/modal/modal.styl index 272cdc7ec8..90f64ebf11 100644 --- a/pkg/webui/components/modal/modal.styl +++ b/pkg/webui/components/modal/modal.styl @@ -13,10 +13,12 @@ // limitations under the License. .modal - background: var(--c-bg-neutral-min) - border-radius: $br.m + background: light-dark(var(--c-bg-neutral-min), var(--c-bg-neutral-extralight)) + border-radius: $br.xxl + border: 1px solid var(--c-border-neutral-light) display: flex flex-direction: column + box-shadow: var(--shadow-box-modal-normal) &-absolute top: 50% @@ -153,5 +155,5 @@ bottom: 0 left: 0 right:0 - background: rgba(0, 0, 0, .5) + background: rgba(0, 0, 0, .85) z-index: $zi.modal diff --git a/pkg/webui/components/notification/index.js b/pkg/webui/components/notification/index.js index 1f6cbe15fc..88ebb5dcec 100644 --- a/pkg/webui/components/notification/index.js +++ b/pkg/webui/components/notification/index.js @@ -41,6 +41,7 @@ const Notification = ({ success, messageValues = {}, children, + dark, 'data-test-id': dataTestId, }) => { const classname = classnames(style.notification, className, { @@ -49,6 +50,7 @@ const Notification = ({ [style.info]: info, [style.small]: small, [style.success]: success, + [style.dark]: dark, [style.withDetails]: Boolean(details), }) @@ -80,6 +82,7 @@ Notification.propTypes = { children: PropTypes.node, className: PropTypes.string, content: PropTypes.oneOfType([PropTypes.message, PropTypes.error, PropTypes.string]), + dark: PropTypes.bool, 'data-test-id': PropTypes.string, details: PropTypes.error, error: PropTypes.bool, @@ -104,6 +107,7 @@ Notification.defaultProps = { success: false, messageValues: undefined, details: undefined, + dark: false, } export default Notification diff --git a/pkg/webui/components/notification/notification.styl b/pkg/webui/components/notification/notification.styl index 7fa4b01780..984b910c52 100644 --- a/pkg/webui/components/notification/notification.styl +++ b/pkg/webui/components/notification/notification.styl @@ -14,13 +14,13 @@ $border-width = $cs.xxs -left-border($color) - background: linear-gradient(to right, $color, $color 5px, var(--c-bg-neutral-min) 0px) +left-border($color, $bg) + background: linear-gradient(to right, $color, $color 5px, $bg 0px) .notification - left-border(var(--c-border-neutral-normal)) - border-top: 1px solid var(--c-border-neutral-light) - border-right: 1px solid var(--c-border-neutral-light) + left-border(var(--c-border-neutral-normal), transparent) + border-top: 1px solid var(--c-border-neutral-extralight) + border-right: 1px solid var(--c-border-neutral-extralight) border-color: var(--c-border-neutral-extralight) border-radius: $br.m margin-bottom: $ls.xxs @@ -44,20 +44,42 @@ left-border($color) margin-right: 8rem &.error - left-border(var(--c-bg-error-normal)) - color: var(--c-bg-error-normal) + background: linear-gradient(to right, var(--c-border-error-normal), var(--c-border-error-normal) 5px, var(--c-bg-error-bold) 0px) + border-top: 1px solid light-dark(var(--c-border-error-light), var(--c-border-error-extralight)) + border-right: 1px solid light-dark(var(--c-border-error-light), var(--c-border-error-extralight)) + border-bottom: 1px solid light-dark(var(--c-border-error-light), var(--c-border-error-extralight)) + border-color: light-dark(var(--c-border-error-light), var(--c-border-error-extralight)) + color: var(--c-text-error-normal-hover) + + &.dark + background: linear-gradient(to right, var(--c-border-error-light), var(--c-border-error-light) 5px, var(--c-bg-error-heavy) 0px) &.warning - left-border(var(--c-bg-warning-normal)) - color: var(--c-text-warning-normal) + left-border(var(--c-border-warning-light), var(--c-bg-warning-heavy)) + border-top: 1px solid var(--c-border-warning-extralight) + border-right: 1px solid var(--c-border-warning-extralight) + border-bottom: 1px solid var(--c-border-warning-extralight) + border-color: var(--c-border-warning-extralight) + color: var(--c-text-warning-normal-hover) &.info - left-border(var(--c-text-brand-normal)) + left-border(var(--c-text-brand-normal), var(--c-bg-info-heavy)) + border-top: 1px solid var(--c-border-info-extralight) + border-right: 1px solid var(--c-border-info-extralight) + border-bottom: 1px solid var(--c-border-info-extralight) + border-color: var(--c-border-info-extralight) color: var(--c-text-brand-normal) &.success - left-border(var(--c-bg-success-normal)) - color: var(--c-bg-success-normal) + background: linear-gradient(to right, var(--c-border-success-normal), var(--c-border-success-normal) 5px, var(--c-bg-success-heavy) 0px) + border-top: 1px solid var(--c-border-success-extralight) + border-right: 1px solid var(--c-border-success-extralight) + border-bottom: 1px solid var(--c-border-success-extralight) + border-color: var(--c-border-success-extralight) + color: var(--c-text-success-normal-hover) + + &.dark + background: linear-gradient(to right, var(--c-border-success-light), var(--c-border-success-light) 5px, var(--c-bg-success-heavy) 0px) .message display: flex diff --git a/pkg/webui/components/pagination/pagination.styl b/pkg/webui/components/pagination/pagination.styl index 25e8acdc4e..44366f258d 100644 --- a/pkg/webui/components/pagination/pagination.styl +++ b/pkg/webui/components/pagination/pagination.styl @@ -40,16 +40,17 @@ cursor: pointer height: 100% width: 100% + border-radius: $br.m .link:hover - background-color: hoverize(var(--c-bg-neutral-extralight-hover)) + background-color: var(--c-bg-neutral-light) .link +focus-visible() - background-color: hoverize(var(--c-bg-neutral-min)) + background-color: var(--c-bg-neutral-min) .link:active - background-color: activize(var(--c-bg-neutral-min)) + background-color: var(--c-bg-neutral-min) &-disabled @@ -60,7 +61,7 @@ &-active .link background-color: var(--c-text-brand-normal) - color: var(--c-bg-neutral-min) + color: light-dark(var(--c-bg-neutral-min), var(--c-bg-neutral-heavy)) border-radius: $br.m .link:hover diff --git a/pkg/webui/components/panel/panel.styl b/pkg/webui/components/panel/panel.styl index 625d6a0dad..324fba51d4 100644 --- a/pkg/webui/components/panel/panel.styl +++ b/pkg/webui/components/panel/panel.styl @@ -66,9 +66,9 @@ .panel-header-icon font-size: $fs.xl min-width: $cs.l - background-color: var(--c-bg-brand-semilight) + background-color: light-dark(var(--c-bg-brand-semilight), var(--c-bg-neutral-light)) padding: $cs.xs border-radius: $br.l - color: var(--c-bg-brand-normal) + color: light-dark(var(--c-bg-brand-normal), var(--c-icon-brand-light)) font-variation-settings: 'FILL' 1 diff --git a/pkg/webui/components/panel/toggle/toggle.styl b/pkg/webui/components/panel/toggle/toggle.styl index 88d237d00a..9c23218c9f 100644 --- a/pkg/webui/components/panel/toggle/toggle.styl +++ b/pkg/webui/components/panel/toggle/toggle.styl @@ -49,7 +49,7 @@ color: var(--c-text-neutral-heavy) &:hover:not(.toggle-button-active) - background: var(--c-bg-neutral-semilight) + background: light-dark(var(--c-bg-neutral-semilight), var(--c-bg-neutral-extralight-hover)) @container panel (max-width: 560px) .toggle diff --git a/pkg/webui/components/radio-button/group/group.styl b/pkg/webui/components/radio-button/group/group.styl index 4051fbf98b..ff53fb61b2 100644 --- a/pkg/webui/components/radio-button/group/group.styl +++ b/pkg/webui/components/radio-button/group/group.styl @@ -28,3 +28,6 @@ .group-radio:not(:last-child) margin-right: $cs.m + + &.space-between + justify-content: space-between diff --git a/pkg/webui/components/radio-button/group/index.js b/pkg/webui/components/radio-button/group/index.js index 7d8aec33b0..2c05ac21dd 100644 --- a/pkg/webui/components/radio-button/group/index.js +++ b/pkg/webui/components/radio-button/group/index.js @@ -36,7 +36,7 @@ const findCheckedRadio = children => { } const RadioGroup = props => { - const { className, name, disabled, horizontal, onChange, children } = props + const { className, name, disabled, horizontal, spaceBetween, onChange, children } = props const [value, setValue] = useState(() => { if ('value' in props) { return props.value @@ -84,6 +84,7 @@ const RadioGroup = props => { const cls = classnames(className, style.group, { [style.horizontal]: horizontal, + [style.spaceBetween]: spaceBetween, }) return ( @@ -101,6 +102,7 @@ RadioGroup.propTypes = { initialValue: PropTypes.string, name: PropTypes.string.isRequired, onChange: PropTypes.func, + spaceBetween: PropTypes.bool, value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), } @@ -111,6 +113,7 @@ RadioGroup.defaultProps = { value: undefined, horizontal: false, onChange: () => null, + spaceBetween: false, } export default RadioGroup diff --git a/pkg/webui/components/radio-button/radio.js b/pkg/webui/components/radio-button/radio.js index 4e3fe8261f..ba3bea4a6a 100644 --- a/pkg/webui/components/radio-button/radio.js +++ b/pkg/webui/components/radio-button/radio.js @@ -36,6 +36,7 @@ const RadioButton = ({ checked, id, onChange, + children, }) => { const input = useRef() const context = useContext(RadioGroupContext) @@ -94,22 +95,25 @@ const RadioButton = ({ return ( ) } @@ -117,6 +121,7 @@ const RadioButton = ({ RadioButton.propTypes = { autoFocus: PropTypes.bool, checked: PropTypes.bool, + children: PropTypes.node, className: PropTypes.string, disabled: PropTypes.bool, id: PropTypes.string, @@ -142,6 +147,7 @@ RadioButton.defaultProps = { onChange: () => null, onBlur: () => null, onFocus: () => null, + children: null, } export default RadioButton diff --git a/pkg/webui/components/search-panel/search-panel.styl b/pkg/webui/components/search-panel/search-panel.styl index 509ae5d47b..54c68d2a8d 100644 --- a/pkg/webui/components/search-panel/search-panel.styl +++ b/pkg/webui/components/search-panel/search-panel.styl @@ -21,7 +21,7 @@ overflow: hidden position: absolute z-index: $zi.modal - background-color: var(--c-bg-neutral-extralight) + background-color: light-dark(var(--c-bg-neutral-extralight), var(--c-bg-neutral-light)) left: 50% top: 10rem transform: translateX(-50%) @@ -48,7 +48,7 @@ outline: none width: 100% height: 3rem - background-color: var(--c-bg-neutral-extralight) + background-color: light-dark(var(--c-bg-neutral-extralight), var(--c-bg-neutral-light)) &:focus::placeholder visibility: hidden @@ -87,7 +87,7 @@ color: inherit &-focus - background-color: var(--c-bg-neutral-light) + background-color: light-dark(var(--c-bg-neutral-light), var(--c-bg-neutral-normal)) .icon color: var(--c-icon-neutral-light) diff --git a/pkg/webui/components/select/select.styl b/pkg/webui/components/select/select.styl index bcc3d500d2..9cd4a4d97f 100644 --- a/pkg/webui/components/select/select.styl +++ b/pkg/webui/components/select/select.styl @@ -17,12 +17,14 @@ // Overwriting react-select styling in global scope. // stylelint-disable + :global(.select__single-value) + color: light-dark(inherit, var(--c-text-neutral-heavy)) :global(.select__option) padding: $cs.xs $cs.s :global(.select__option--is-selected) background-color: var(--c-text-brand-normal) :global(.select__option--is-focused:not(.select__option--is-selected)) - background-color: var(--c-bg-neutral-semilight) + background-color: light-dark(var(--c-bg-neutral-semilight), var(--c-bg-neutral-normal)) :global(.select__control--is-focused) box-shadow: none border-color: var(--c-text-brand-normal) @@ -32,7 +34,10 @@ width: 100% min-height: $default-input-height border-radius: $br.xs + border: 1px solid var(--c-border-neutral-normal) + background: var(--c-bg-neutral-min) :global(.select__menu) + background: light-dark(var(--c-bg-neutral-min), var(--c-bg-neutral-light)) z-index: $zi.dropdown :global(.select__dropdown-indicator) padding: 4px diff --git a/pkg/webui/components/sidebar/side-header/index.js b/pkg/webui/components/sidebar/side-header/index.js index 858ac8a866..e6e318e1fd 100644 --- a/pkg/webui/components/sidebar/side-header/index.js +++ b/pkg/webui/components/sidebar/side-header/index.js @@ -15,6 +15,7 @@ import React, { useContext } from 'react' import classnames from 'classnames' import { Link } from 'react-router-dom' +import { useSelector } from 'react-redux' import { IconLayoutSidebarLeftCollapse, IconX } from '@ttn-lw/components/icon' import Button from '@ttn-lw/components/button' @@ -24,15 +25,22 @@ import SidebarContext from '@console/containers/sidebar/context' import PropTypes from '@ttn-lw/lib/prop-types' import sharedMessages from '@ttn-lw/lib/shared-messages' +import { selectConsolePreferences } from '@console/store/selectors/user-preferences' + import style from './side-header.styl' const SideHeader = ({ Logo }) => { const { onMinimizeToggle, isMinimized, closeDrawer } = useContext(SidebarContext) + const consolePreferences = useSelector(selectConsolePreferences) + const darkTheme = + consolePreferences.console_theme === 'CONSOLE_THEME_DARK' || + (consolePreferences.console_theme === 'CONSOLE_THEME_SYSTEM' && + window.matchMedia('(prefers-color-scheme: dark)').matches) return (
- + {!isMinimized && (
diff --git a/pkg/webui/console/components/payload-formatters-form/index.js b/pkg/webui/console/components/payload-formatters-form/index.js index c8a4412db4..9932f84bfb 100644 --- a/pkg/webui/console/components/payload-formatters-form/index.js +++ b/pkg/webui/console/components/payload-formatters-form/index.js @@ -110,6 +110,7 @@ const Formatter = ({ type, pasteAppPayloadFormatter, pasteRepoPayloadFormatters, + darkTheme, }) => { const hasRepoFormatter = repoFormatters !== undefined && Object.keys(repoFormatters).length !== 0 const repositoryPayloadFormatters = repoFormatters?.formatter_parameter @@ -130,6 +131,7 @@ const Formatter = ({ height="10rem" minLines={25} maxLines={25} + darkTheme={darkTheme} /> {type === TYPES.JAVASCRIPT && ( @@ -188,6 +190,7 @@ const Formatter = ({ minLines={25} maxLines={25} value={repositoryPayloadFormatters} + darkTheme={darkTheme} /> @@ -200,6 +203,7 @@ const Formatter = ({ } Formatter.propTypes = { + darkTheme: PropTypes.bool, defaultType: PropTypes.string, pasteAppPayloadFormatter: PropTypes.func.isRequired, pasteRepoPayloadFormatters: PropTypes.func.isRequired, @@ -210,6 +214,7 @@ Formatter.propTypes = { } Formatter.defaultProps = { + darkTheme: false, defaultType: undefined, repoFormatters: undefined, } @@ -230,6 +235,7 @@ const PayloadFormattersForm = ({ onSubmitFailure, onTestSubmit, defaultParameter, + darkTheme, }) => { const [type, setType] = useState(initialType) const [isSubmitting, setIsSubmitting] = useState(false) @@ -451,6 +457,7 @@ const PayloadFormattersForm = ({ type={type} pasteAppPayloadFormatter={pasteAppPayloadFormatter} pasteRepoPayloadFormatters={pasteRepoPayloadFormatters} + darkTheme={darkTheme} /> @@ -465,6 +472,7 @@ const PayloadFormattersForm = ({ onSubmit={handleTestSubmit} uplink={uplink} testResult={testResult} + darkTheme={darkTheme} /> @@ -490,6 +498,7 @@ PayloadFormattersForm.propTypes = { allowReset: PropTypes.bool, allowTest: PropTypes.bool, appId: PropTypes.string, + darkTheme: PropTypes.bool, defaultParameter: PropTypes.string, defaultType: PropTypes.string, initialParameter: PropTypes.string, @@ -522,6 +531,7 @@ PayloadFormattersForm.defaultProps = { appId: undefined, isDefaultType: undefined, repoFormatters: undefined, + darkTheme: false, } export default injectIntl(PayloadFormattersForm) diff --git a/pkg/webui/console/components/payload-formatters-form/test-form/index.js b/pkg/webui/console/components/payload-formatters-form/test-form/index.js index 0c9f33537a..22ee2d8156 100644 --- a/pkg/webui/console/components/payload-formatters-form/test-form/index.js +++ b/pkg/webui/console/components/payload-formatters-form/test-form/index.js @@ -96,6 +96,7 @@ const TestForm = props => { normalized_payload_warnings: normalizedPayloadWarnings, frm_payload: framePayload, }, + darkTheme, } = props const { formatMessage } = useIntl() @@ -175,6 +176,7 @@ const TestForm = props => { component={CodeEditor} minLines={15} maxLines={15} + darkTheme={darkTheme} /> )} { maxLines={12} readOnly showGutter={false} + darkTheme={darkTheme} /> )} @@ -225,6 +228,7 @@ const TestForm = props => { maxLines={11} readOnly showGutter={false} + darkTheme={darkTheme} /> @@ -246,6 +250,7 @@ const TestForm = props => { maxLines={showTestError ? 9 : 6} readOnly showGutter={false} + darkTheme={darkTheme} /> )} @@ -276,6 +281,7 @@ const TestForm = props => { TestForm.propTypes = { className: PropTypes.string, + darkTheme: PropTypes.bool, onSubmit: PropTypes.func.isRequired, testResult: PropTypes.shape({ decoded_payload: PropTypes.PropTypes.shape({}), @@ -288,6 +294,7 @@ TestForm.propTypes = { } TestForm.defaultProps = { className: undefined, + darkTheme: false, } export default TestForm diff --git a/pkg/webui/console/containers/application-payload-formatters/downlink.js b/pkg/webui/console/containers/application-payload-formatters/downlink.js index 9bb5e292b3..4724b08096 100644 --- a/pkg/webui/console/containers/application-payload-formatters/downlink.js +++ b/pkg/webui/console/containers/application-payload-formatters/downlink.js @@ -40,6 +40,7 @@ import { selectApplicationLinkError, selectApplicationLinkFormatters, } from '@console/store/selectors/applications' +import { selectConsolePreferences } from '@console/store/selectors/user-preferences' const m = defineMessages({ title: 'Default downlink payload formatter', @@ -50,6 +51,11 @@ const m = defineMessages({ }) const ApplicationPayloadFormatters = () => { + const consolePreferences = useSelector(selectConsolePreferences) + const darkTheme = + consolePreferences.console_theme === 'CONSOLE_THEME_DARK' || + (consolePreferences.console_theme === 'CONSOLE_THEME_SYSTEM' && + window.matchMedia('(prefers-color-scheme: dark)').matches) const { appId } = useParams() const formatters = useSelector(selectApplicationLinkFormatters) || {} const linkError = useSelector(selectApplicationLinkError) @@ -116,6 +122,7 @@ const ApplicationPayloadFormatters = () => { initialType={formatters.down_formatter || PAYLOAD_FORMATTER_TYPES.NONE} initialParameter={formatters.down_formatter_parameter || ''} onTypeChange={onTypeChange} + darkTheme={darkTheme} /> diff --git a/pkg/webui/console/containers/application-payload-formatters/uplink.js b/pkg/webui/console/containers/application-payload-formatters/uplink.js index 60ed1a68c9..e99a3c3219 100644 --- a/pkg/webui/console/containers/application-payload-formatters/uplink.js +++ b/pkg/webui/console/containers/application-payload-formatters/uplink.js @@ -40,6 +40,7 @@ import { selectApplicationLinkError, selectApplicationLinkFormatters, } from '@console/store/selectors/applications' +import { selectConsolePreferences } from '@console/store/selectors/user-preferences' const m = defineMessages({ title: 'Default uplink payload formatter', @@ -54,6 +55,11 @@ const ApplicationPayloadFormatters = () => { const formatters = useSelector(selectApplicationLinkFormatters) || {} const linkError = useSelector(selectApplicationLinkError) const mayViewLink = useSelector(state => checkFromState(mayViewApplicationLink, state)) + const consolePreferences = useSelector(selectConsolePreferences) + const darkTheme = + consolePreferences.console_theme === 'CONSOLE_THEME_DARK' || + (consolePreferences.console_theme === 'CONSOLE_THEME_SYSTEM' && + window.matchMedia('(prefers-color-scheme: dark)').matches) const [type, setType] = useState(formatters.down_formatter || PAYLOAD_FORMATTER_TYPES.NONE) const dispatch = useDispatch() @@ -115,6 +121,7 @@ const ApplicationPayloadFormatters = () => { initialType={formatters.up_formatter || PAYLOAD_FORMATTER_TYPES.NONE} initialParameter={formatters.up_formatter_parameter || ''} onTypeChange={onTypeChange} + darkTheme={darkTheme} /> diff --git a/pkg/webui/console/containers/device-importer/processor.js b/pkg/webui/console/containers/device-importer/processor.js index 643a7145f9..fc53b31aca 100644 --- a/pkg/webui/console/containers/device-importer/processor.js +++ b/pkg/webui/console/containers/device-importer/processor.js @@ -32,6 +32,7 @@ import { isFrontend } from '@ttn-lw/lib/errors/utils' import PropTypes from '@ttn-lw/lib/prop-types' import { selectSelectedApplicationId } from '@console/store/selectors/applications' +import { selectConsolePreferences } from '@console/store/selectors/user-preferences' import m from './messages' @@ -60,6 +61,11 @@ const Processor = ({ handleReset, editorRef, }) => { + const consolePreferences = useSelector(selectConsolePreferences) + const darkTheme = + consolePreferences.console_theme === 'CONSOLE_THEME_DARK' || + (consolePreferences.console_theme === 'CONSOLE_THEME_SYSTEM' && + window.matchMedia('(prefers-color-scheme: dark)').matches) const appId = useSelector(selectSelectedApplicationId) const hasErrored = status === 'error' const operationMessage = step === 'conversion' ? m.converting : m.creating @@ -181,6 +187,7 @@ const Processor = ({ showGutter={false} scrollToBottom editorRef={editorRef} + darkTheme={darkTheme} /> diff --git a/pkg/webui/console/containers/device-payload-formatters/downlink.js b/pkg/webui/console/containers/device-payload-formatters/downlink.js index 5f6a26e8e1..96744abdca 100644 --- a/pkg/webui/console/containers/device-payload-formatters/downlink.js +++ b/pkg/webui/console/containers/device-payload-formatters/downlink.js @@ -44,6 +44,7 @@ import { selectSelectedDevice, } from '@console/store/selectors/devices' import { selectDeviceRepoPayloadFromatters } from '@console/store/selectors/device-repository' +import { selectConsolePreferences } from '@console/store/selectors/user-preferences' import m from './messages' @@ -55,6 +56,11 @@ const DevicePayloadFormatters = () => { const formatters = useSelector(selectSelectedDeviceFormatters) const encodeDownlink = tts.As.encodeDownlink const repositoryPayloadFormatters = useSelector(selectDeviceRepoPayloadFromatters) + const consolePreferences = useSelector(selectConsolePreferences) + const darkTheme = + consolePreferences.console_theme === 'CONSOLE_THEME_DARK' || + (consolePreferences.console_theme === 'CONSOLE_THEME_SYSTEM' && + window.matchMedia('(prefers-color-scheme: dark)').matches) const [type, setType] = useState( Boolean(formatters) ? formatters.down_formatter || PAYLOAD_FORMATTER_TYPES.NONE @@ -169,6 +175,7 @@ const DevicePayloadFormatters = () => { onTypeChange={onTypeChange} isDefaultType={isDefaultType} repoFormatters={repositoryPayloadFormatters} + darkTheme={darkTheme} /> ) diff --git a/pkg/webui/console/containers/device-payload-formatters/uplink.js b/pkg/webui/console/containers/device-payload-formatters/uplink.js index 9f612142e4..6847b8e6b7 100644 --- a/pkg/webui/console/containers/device-payload-formatters/uplink.js +++ b/pkg/webui/console/containers/device-payload-formatters/uplink.js @@ -45,6 +45,7 @@ import { selectSelectedDevice, } from '@console/store/selectors/devices' import { selectDeviceRepoPayloadFromatters } from '@console/store/selectors/device-repository' +import { selectConsolePreferences } from '@console/store/selectors/user-preferences' import m from './messages' @@ -56,6 +57,11 @@ const DevicePayloadFormatters = () => { const formatters = useSelector(selectSelectedDeviceFormatters) const decodeUplink = tts.As.decodeUplink const repositoryPayloadFormatters = useSelector(selectDeviceRepoPayloadFromatters) + const consolePreferences = useSelector(selectConsolePreferences) + const darkTheme = + consolePreferences.console_theme === 'CONSOLE_THEME_DARK' || + (consolePreferences.console_theme === 'CONSOLE_THEME_SYSTEM' && + window.matchMedia('(prefers-color-scheme: dark)').matches) const [type, setType] = useState( Boolean(formatters) ? formatters.down_formatter || PAYLOAD_FORMATTER_TYPES.NONE @@ -179,6 +185,7 @@ const DevicePayloadFormatters = () => { onTypeChange={onTypeChange} isDefaultType={isDefaultType} repoFormatters={repositoryPayloadFormatters} + darkTheme={darkTheme} /> ) diff --git a/pkg/webui/console/containers/email-notifications-form/index.js b/pkg/webui/console/containers/email-notifications-form/index.js index 5fb0666211..33b7fa56de 100644 --- a/pkg/webui/console/containers/email-notifications-form/index.js +++ b/pkg/webui/console/containers/email-notifications-form/index.js @@ -83,7 +83,6 @@ const m = defineMessages({ requiresAdminAction: "Requires admin action, can't be unselected", unsubscribeFromEverything: 'Turn off all email notifications', unsubscribeDescription: 'You will continue to receive notifications in the console.', - discardChanges: 'Discard changes', updateEmailPreferences: 'Updated email preferences', }) @@ -174,7 +173,12 @@ const InnerForm = initialValues => { -