diff --git a/jslib b/jslib index c71b9703f3..e9228a7888 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit c71b9703f3a7ca6f791e5f813c418c8ed23855b7 +Subproject commit e9228a788813b8da22c71d0b8ddeda5e08702a14 diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index b7ff61700f..4c804e8ba0 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -491,6 +491,20 @@ "disableContextMenuItemDesc": { "message": "Context menu options provide quick access to password generation and logins for the website in your current tab." }, + "theme": { + "message": "Theme" + }, + "themeDesc": { + "message": "Change the application's color theme. You must re-open the window." + }, + "dark": { + "message": "Dark", + "description": "Dark color" + }, + "light": { + "message": "Light", + "description": "Light color" + }, "exportVault": { "message": "Export Vault" }, @@ -1036,5 +1050,8 @@ }, "remove": { "message": "Remove" + }, + "default": { + "message": "Default" } } diff --git a/src/popup/scss/buttons.scss b/src/popup/scss/buttons.scss index 3bc6e05e2f..20e4277bd7 100644 --- a/src/popup/scss/buttons.scss +++ b/src/popup/scss/buttons.scss @@ -1,7 +1,7 @@ @import "variables.scss"; .btn { - background-color: $button-backgound-color; + background-color: $button-background-color; border-radius: $border-radius; padding: 7px 15px; border: 1px solid $button-border-color; @@ -21,7 +21,7 @@ &:hover:not([disabled]) { cursor: pointer; - background-color: darken($button-backgound-color, 1.5%); + background-color: darken($button-background-color, 1.5%); border-color: darken($button-border-color, 17%); color: darken($button-color, 10%); @@ -36,7 +36,7 @@ &:focus:not([disabled]) { cursor: pointer; - background-color: darken($button-backgound-color, 6%); + background-color: darken($button-background-color, 6%); border-color: darken($button-border-color, 25%); outline: 0; } diff --git a/src/popup/scss/variables.scss b/src/popup/scss/variables.scss index e04bf4a826..1a9c1f5d4f 100644 --- a/src/popup/scss/variables.scss +++ b/src/popup/scss/variables.scss @@ -31,7 +31,112 @@ $box-background-hover-color: $list-item-hover; $box-border-color: $border-color; $button-border-color: darken($border-color-dark, 12%); -$button-backgound-color: white; +$button-background-color: white; $button-color: lighten($text-color, 40%); $button-color-primary: darken($brand-primary, 8%); $button-color-danger: darken($brand-danger, 10%); + +$themes: ( + light: ( + textColor: $text-color, + borderColor: $border-color, + borderColorDark: $border-color-dark, + backgroundColor: $background-color, + backgroundColorAlt: $background-color-alt, + headerBackgroundColor: $brand-primary, + headerBorderColor: darken($brand-primary, 7%), + headerInputBackgroundColor: darken($brand-primary, 8%), + headerInputBackgroundFocusColor: darken($brand-primary, 10%), + headerInputColor: #ffffff, + headerInputPlaceholderColor: lighten($brand-primary, 35%), + listItemBackgroundColor: $background-color, + listItemBackgroundHoverColor: $list-item-hover, + boxBackgroundColor: $box-background-color, + boxBackgroundHoverColor: $box-background-hover-color, + boxBorderColor: $box-border-color, + groupingsActiveColor: darken($background-color-alt, 5%), + disabledIconColor: $list-icon-color, + headingColor: $gray-light, + headingButtonColor: lighten($gray-light, 30%), + headingButtonHoverColor: $gray-light, + labelColor: $gray-light, + mutedColor: $text-muted, + totpStrokeColor: $brand-primary, + boxRowButtonColor: $brand-primary, + boxRowButtonHoverColor: darken($brand-primary, 10%), + inputBorderColor: darken($border-color-dark, 7%), + inputBackgroundColor: #ffffff, + inputPlaceholderColor: lighten($gray-light, 35%), + buttonBackgroundColor: $button-background-color, + buttonBorderColor: $button-border-color, + buttonColor: $button-color, + buttonPrimaryColor: $button-color-primary, + buttonDangerColor: $button-color-danger, + primaryColor: $brand-primary, + primaryAccentColor: $brand-primary-accent, + dangerColor: $brand-danger, + successColor: $brand-success, + infoColor: $brand-info, + warningColor: $brand-warning, + ), + dark: ( + textColor: #ffffff, + borderColor: #2f2f2f, + borderColorDark: #2f2f2f, + backgroundColor: #363636, + backgroundColorAlt: #3d3d3d, + boxBackgroundColor: #363636, + boxBackgroundHoverColor: #3f3f3f, + boxBorderColor: #2f2f2f, + headerBackgroundColor: #363636, + headerBorderColor: #272727, + headerInputBackgroundColor: #222222, + headerInputBackgroundFocusColor: #1d1d1d, + headerInputColor: #ffffff, + headerInputPlaceholderColor: #707070, + listItemBackgroundColor: #363636, + listItemBackgroundHoverColor: #464646, + groupingsActiveColor: #292929, + disabledIconColor: #c7c7cd, + headingColor: #a3a3a3, + headingButtonColor: #a3a3a3, + headingButtonHoverColor: #ffffff, + labelColor: #a3a3a3, + mutedColor: #a3a3a3, + totpStrokeColor: #cacaca, + boxRowButtonColor: #cacaca, + boxRowButtonHoverColor: #ffffff, + inputBorderColor: #222222, + inputBackgroundColor: #363636, + inputPlaceholderColor: #707070, + buttonBackgroundColor: #363636, + buttonBorderColor: #1f1f1f, + buttonColor: #ffffff, + buttonPrimaryColor: #46ace7, + buttonDangerColor: #ff3e24, + primaryColor: #52bdfb, + primaryAccentColor: #3ea1da, + dangerColor: #ff3e24, + successColor: $brand-success, + infoColor: $brand-info, + warningColor: $brand-warning, + ), +); + +@mixin themify($themes: $themes) { + @each $theme, $map in $themes { + html.theme_#{$theme} & { + $theme-map: () !global; + @each $key, $submap in $map { + $value: map-get(map-get($themes, $theme), '#{$key}'); + $theme-map: map-merge($theme-map, ($key: $value)) !global; + } + @content; + $theme-map: null !global; + } + } +} + +@function themed($key) { + @return map-get($theme-map, $key); +} diff --git a/src/popup/services/services.module.ts b/src/popup/services/services.module.ts index e874615df9..ffeb993aa5 100644 --- a/src/popup/services/services.module.ts +++ b/src/popup/services/services.module.ts @@ -77,7 +77,13 @@ export function initFactory(i18nService: I18nService, storageService: StorageSer stateService.save(ConstantsService.disableFaviconKey, await storageService.get(ConstantsService.disableFaviconKey)); + let theme = await storageService.get(ConstantsService.themeKey); + if (theme == null) { + theme = 'light'; + } window.document.documentElement.classList.add('locale_' + i18nService.translationLocale); + window.document.documentElement.classList.add('theme_' + theme); + authService.init(); const analytics = new Analytics(window, () => BrowserApi.gaFilter(), null, null, null, () => { diff --git a/src/popup/settings/options.component.html b/src/popup/settings/options.component.html index 961ac649ea..326467b3be 100644 --- a/src/popup/settings/options.component.html +++ b/src/popup/settings/options.component.html @@ -71,4 +71,15 @@ +
+
+
+ + +
+
+ +
diff --git a/src/popup/settings/options.component.ts b/src/popup/settings/options.component.ts index 452ac12fda..7e6e9a6ff4 100644 --- a/src/popup/settings/options.component.ts +++ b/src/popup/settings/options.component.ts @@ -5,6 +5,7 @@ import { import { Angulartics2 } from 'angulartics2'; +import { I18nService } from 'jslib/abstractions/i18n.service'; import { MessagingService } from 'jslib/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { StateService } from 'jslib/abstractions/state.service'; @@ -25,10 +26,19 @@ export class OptionsComponent implements OnInit { disableAddLoginNotification = false; showDisableContextMenu = true; disableGa = false; + theme: string; + themeOptions: any[]; constructor(private analytics: Angulartics2, private messagingService: MessagingService, private platformUtilsService: PlatformUtilsService, private storageService: StorageService, - private stateService: StateService, private totpService: TotpService) { } + private stateService: StateService, private totpService: TotpService, + private i18nService: I18nService) { + this.themeOptions = [ + { name: i18nService.t('default'), value: null }, + { name: i18nService.t('light'), value: 'light' }, + { name: i18nService.t('dark'), value: 'dark' }, + ]; + } async ngOnInit() { this.showDisableContextMenu = !this.platformUtilsService.isSafari(); @@ -49,6 +59,8 @@ export class OptionsComponent implements OnInit { this.disableAutoTotpCopy = !await this.totpService.isAutoCopyEnabled(); this.disableFavicon = await this.storageService.get(ConstantsService.disableFaviconKey); + + this.theme = await this.storageService.get(ConstantsService.themeKey); } async saveGa() { @@ -90,6 +102,11 @@ export class OptionsComponent implements OnInit { this.callAnalytics('Favicon', !this.disableFavicon); } + async saveTheme() { + await this.storageService.save(ConstantsService.themeKey, this.theme); + this.analytics.eventTrack.next({ action: 'Set Theme ' + this.theme }); + } + private callAnalytics(name: string, enabled: boolean) { const status = enabled ? 'Enabled' : 'Disabled'; this.analytics.eventTrack.next({ action: `${status} ${name}` });