[PS-1693] feat(browser): implement theming for notification bar (#3805)
* feat(browser): implement theming for notification bar * refactor(browser): split notification bar function * refactor(browser): use own method for getCurrentTheme * chore(browser): add close.svg file as an asset this file is embedded in apps/browser/src/notification/bar.html * feat(browser): change textContrast color on primary buttons * feat(browser): use dedicated color variable for close button * feat(browser): use textColor for close button * feat(browser): implement styling for select fields * feat(browser): improve close button styling, add hover effect
This commit is contained in:
parent
0cb5ffd6ed
commit
2ffafa1f23
|
@ -7,6 +7,7 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.serv
|
|||
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
|
||||
import { CipherType } from "@bitwarden/common/enums/cipherType";
|
||||
import { PolicyType } from "@bitwarden/common/enums/policyType";
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipher.view";
|
||||
import { LoginUriView } from "@bitwarden/common/models/view/login-uri.view";
|
||||
|
@ -125,13 +126,13 @@ export default class NotificationBackground {
|
|||
}
|
||||
|
||||
if (tab != null) {
|
||||
this.doNotificationQueueCheck(tab);
|
||||
await this.doNotificationQueueCheck(tab);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTab = await BrowserApi.getTabFromCurrentWindow();
|
||||
if (currentTab != null) {
|
||||
this.doNotificationQueueCheck(currentTab);
|
||||
await this.doNotificationQueueCheck(currentTab);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +145,7 @@ export default class NotificationBackground {
|
|||
setTimeout(() => this.cleanupNotificationQueue(), 2 * 60 * 1000); // check every 2 minutes
|
||||
}
|
||||
|
||||
private doNotificationQueueCheck(tab: chrome.tabs.Tab): void {
|
||||
private async doNotificationQueueCheck(tab: chrome.tabs.Tab): Promise<void> {
|
||||
if (tab == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -167,6 +168,7 @@ export default class NotificationBackground {
|
|||
type: "add",
|
||||
typeData: {
|
||||
isVaultLocked: this.notificationQueue[i].wasVaultLocked,
|
||||
theme: await this.getCurrentTheme(),
|
||||
},
|
||||
});
|
||||
} else if (this.notificationQueue[i].type === NotificationQueueMessageType.ChangePassword) {
|
||||
|
@ -174,6 +176,7 @@ export default class NotificationBackground {
|
|||
type: "change",
|
||||
typeData: {
|
||||
isVaultLocked: this.notificationQueue[i].wasVaultLocked,
|
||||
theme: await this.getCurrentTheme(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -181,6 +184,18 @@ export default class NotificationBackground {
|
|||
}
|
||||
}
|
||||
|
||||
private async getCurrentTheme() {
|
||||
const theme = await this.stateService.getTheme();
|
||||
|
||||
if (theme !== ThemeType.System) {
|
||||
return theme;
|
||||
}
|
||||
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? ThemeType.Dark
|
||||
: ThemeType.Light;
|
||||
}
|
||||
|
||||
private removeTabFromNotificationQueue(tab: chrome.tabs.Tab) {
|
||||
for (let i = this.notificationQueue.length - 1; i >= 0; i--) {
|
||||
if (this.notificationQueue[i].tabId === tab.id) {
|
||||
|
|
|
@ -498,17 +498,13 @@ document.addEventListener("DOMContentLoaded", (event) => {
|
|||
}
|
||||
|
||||
function closeExistingAndOpenBar(type: string, typeData: any) {
|
||||
let barPage = "notification/bar.html";
|
||||
switch (type) {
|
||||
case "add":
|
||||
barPage = barPage + "?add=1&isVaultLocked=" + typeData.isVaultLocked;
|
||||
break;
|
||||
case "change":
|
||||
barPage = barPage + "?change=1&isVaultLocked=" + typeData.isVaultLocked;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const barQueryParams = {
|
||||
type,
|
||||
isVaultLocked: typeData.isVaultLocked,
|
||||
theme: typeData.theme,
|
||||
};
|
||||
const barQueryString = new URLSearchParams(barQueryParams).toString();
|
||||
const barPage = "notification/bar.html?" + barQueryString;
|
||||
|
||||
const frame = document.getElementById("bit-notification-bar-iframe") as HTMLIFrameElement;
|
||||
if (frame != null && frame.src.indexOf(barPage) >= 0) {
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 288 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" xmlns:v="https://vecta.io/nano"><path d="M497.72 429.63l-169-174.82L498.11 82.24c6.956-7.168 6.956-18.85 0-26.018L449.934 6.31C446.585 2.859 442.076 1 437.31 1s-9.275 1.991-12.624 5.31l-168.62 172.04L87.196 6.45c-3.349-3.451-7.858-5.31-12.624-5.31s-9.275 1.991-12.624 5.31L13.9 56.362c-6.956 7.168-6.956 18.85 0 26.018l169.39 172.57L14.42 429.64c-3.349 3.451-5.281 8.097-5.281 13.009s1.803 9.558 5.281 13.009l48.176 49.912c3.478 3.584 7.987 5.442 12.624 5.442 4.508 0 9.146-1.726 12.624-5.442l168.23-174.16 168.36 174.03c3.478 3.584 7.986 5.442 12.624 5.442 4.509 0 9.146-1.726 12.624-5.442l48.176-49.912c3.349-3.451 5.281-8.097 5.281-13.009a19.32 19.32 0 0 0-5.41-12.876z"/></svg>
|
After Width: | Height: | Size: 743 B |
|
@ -100,8 +100,7 @@
|
|||
"web_accessible_resources": [
|
||||
"notification/bar.html",
|
||||
"images/icon38.png",
|
||||
"images/icon38_locked.png",
|
||||
"images/close.png"
|
||||
"images/icon38_locked.png"
|
||||
],
|
||||
"applications": {
|
||||
"gecko": {
|
||||
|
|
|
@ -106,12 +106,7 @@
|
|||
},
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"notification/bar.html",
|
||||
"images/icon38.png",
|
||||
"images/icon38_locked.png",
|
||||
"images/close.png"
|
||||
],
|
||||
"resources": ["notification/bar.html", "images/icon38.png", "images/icon38_locked.png"],
|
||||
"matches": ["<all_urls>"]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -15,7 +15,16 @@
|
|||
<div id="content"></div>
|
||||
<div>
|
||||
<button type="button" class="neutral" id="close-button">
|
||||
<img id="close" alt="Close" />
|
||||
<svg
|
||||
id="close"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns:v="https://vecta.io/nano"
|
||||
>
|
||||
<path
|
||||
d="M497.72 429.63l-169-174.82L498.11 82.24c6.956-7.168 6.956-18.85 0-26.018L449.934 6.31C446.585 2.859 442.076 1 437.31 1s-9.275 1.991-12.624 5.31l-168.62 172.04L87.196 6.45c-3.349-3.451-7.858-5.31-12.624-5.31s-9.275 1.991-12.624 5.31L13.9 56.362c-6.956 7.168-6.956 18.85 0 26.018l169.39 172.57L14.42 429.64c-3.349 3.451-5.281 8.097-5.281 13.009s1.803 9.558 5.281 13.009l48.176 49.912c3.478 3.584 7.987 5.442 12.624 5.442 4.508 0 9.146-1.726 12.624-5.442l168.23-174.16 168.36 174.03c3.478 3.584 7.986 5.442 12.624 5.442 4.509 0 9.146-1.726 12.624-5.442l48.176-49.912c3.349-3.451 5.281-8.097 5.281-13.009a19.32 19.32 0 0 0-5.41-12.876z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
require("./bar.scss");
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const theme = getQueryVariable("theme");
|
||||
document.documentElement.classList.add("theme_" + theme);
|
||||
|
||||
let i18n = {};
|
||||
let lang = window.navigator.language;
|
||||
|
||||
|
@ -39,10 +42,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
var changeButton = document.querySelector("#template-change .change-save");
|
||||
changeButton.textContent = i18n.notificationChangeSave;
|
||||
|
||||
var closeIcon = document.getElementById("close");
|
||||
closeIcon.src = chrome.runtime.getURL("images/close.png");
|
||||
closeIcon.alt = i18n.close;
|
||||
|
||||
var closeButton = document.getElementById("close-button");
|
||||
closeButton.title = i18n.close;
|
||||
closeButton.setAttribute("aria-label", i18n.close);
|
||||
|
@ -51,54 +50,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
document.querySelector("#template-change .change-text").textContent =
|
||||
i18n.notificationChangeDesc;
|
||||
|
||||
if (getQueryVariable("add")) {
|
||||
setContent(document.getElementById("template-add"));
|
||||
|
||||
var addButton = document.querySelector("#template-add-clone .add-save"), // eslint-disable-line
|
||||
neverButton = document.querySelector("#template-add-clone .never-save"); // eslint-disable-line
|
||||
|
||||
addButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const folderId = document.querySelector("#template-add-clone .select-folder").value;
|
||||
|
||||
const bgAddSaveMessage = {
|
||||
command: "bgAddSave",
|
||||
folder: folderId,
|
||||
};
|
||||
sendPlatformMessage(bgAddSaveMessage);
|
||||
});
|
||||
|
||||
neverButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
sendPlatformMessage({
|
||||
command: "bgNeverSave",
|
||||
});
|
||||
});
|
||||
|
||||
if (!isVaultLocked) {
|
||||
const responseFoldersCommand = "notificationBarGetFoldersList";
|
||||
chrome.runtime.onMessage.addListener((msg) => {
|
||||
if (msg.command === responseFoldersCommand && msg.data) {
|
||||
fillSelectorWithFolders(msg.data.folders);
|
||||
}
|
||||
});
|
||||
sendPlatformMessage({
|
||||
command: "bgGetDataForTab",
|
||||
responseCommand: responseFoldersCommand,
|
||||
});
|
||||
}
|
||||
} else if (getQueryVariable("change")) {
|
||||
setContent(document.getElementById("template-change"));
|
||||
var changeButton = document.querySelector("#template-change-clone .change-save"); // eslint-disable-line
|
||||
changeButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const bgChangeSaveMessage = {
|
||||
command: "bgChangeSave",
|
||||
};
|
||||
sendPlatformMessage(bgChangeSaveMessage);
|
||||
});
|
||||
if (getQueryVariable("type") === "add") {
|
||||
handleTypeAdd(isVaultLocked);
|
||||
} else if (getQueryVariable("type") === "change") {
|
||||
handleTypeChange();
|
||||
}
|
||||
|
||||
closeButton.addEventListener("click", (e) => {
|
||||
|
@ -126,6 +81,58 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
function handleTypeAdd(isVaultLocked) {
|
||||
setContent(document.getElementById("template-add"));
|
||||
|
||||
var addButton = document.querySelector("#template-add-clone .add-save"), // eslint-disable-line
|
||||
neverButton = document.querySelector("#template-add-clone .never-save"); // eslint-disable-line
|
||||
|
||||
addButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const folderId = document.querySelector("#template-add-clone .select-folder").value;
|
||||
|
||||
const bgAddSaveMessage = {
|
||||
command: "bgAddSave",
|
||||
folder: folderId,
|
||||
};
|
||||
sendPlatformMessage(bgAddSaveMessage);
|
||||
});
|
||||
|
||||
neverButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
sendPlatformMessage({
|
||||
command: "bgNeverSave",
|
||||
});
|
||||
});
|
||||
|
||||
if (!isVaultLocked) {
|
||||
const responseFoldersCommand = "notificationBarGetFoldersList";
|
||||
chrome.runtime.onMessage.addListener((msg) => {
|
||||
if (msg.command === responseFoldersCommand && msg.data) {
|
||||
fillSelectorWithFolders(msg.data.folders);
|
||||
}
|
||||
});
|
||||
sendPlatformMessage({
|
||||
command: "bgGetDataForTab",
|
||||
responseCommand: responseFoldersCommand,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleTypeChange() {
|
||||
setContent(document.getElementById("template-change"));
|
||||
var changeButton = document.querySelector("#template-change-clone .change-save"); // eslint-disable-line
|
||||
changeButton.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const bgChangeSaveMessage = {
|
||||
command: "bgChangeSave",
|
||||
};
|
||||
sendPlatformMessage(bgChangeSaveMessage);
|
||||
});
|
||||
}
|
||||
|
||||
function setContent(element) {
|
||||
const content = document.getElementById("content");
|
||||
while (content.firstChild) {
|
||||
|
|
|
@ -1,22 +1,31 @@
|
|||
body {
|
||||
background-color: #ffffff;
|
||||
@import "variables.scss";
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
color: #333333;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-family: $font-family-sans-serif;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("textColor");
|
||||
background-color: themed("backgroundColor");
|
||||
}
|
||||
}
|
||||
|
||||
.outer-wrapper {
|
||||
padding: 0 10px;
|
||||
border-bottom: 2px solid #175ddc;
|
||||
border-bottom: 2px solid transparent;
|
||||
display: grid;
|
||||
grid-template-columns: 24px auto 55px;
|
||||
grid-column-gap: 10px;
|
||||
box-sizing: border-box;
|
||||
min-height: 42px;
|
||||
|
||||
@include themify($themes) {
|
||||
border-bottom-color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
|
||||
.inner-wrapper {
|
||||
|
@ -41,42 +50,86 @@ img {
|
|||
display: block;
|
||||
}
|
||||
|
||||
#close {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
display: block;
|
||||
padding: 5px 0;
|
||||
#close-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
button:not(.link),
|
||||
button:not(.neutral) {
|
||||
background-color: #175ddc;
|
||||
padding: 5px 15px;
|
||||
border-radius: 3px;
|
||||
color: #ffffff;
|
||||
border: 0;
|
||||
#close {
|
||||
display: block;
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
|
||||
> path {
|
||||
@include themify($themes) {
|
||||
fill: themed("textColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#close-button:hover {
|
||||
@include themify($themes) {
|
||||
border-color: rgba(themed("textColor"), 0.2);
|
||||
background-color: rgba(themed("textColor"), 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.35rem 15px;
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:not(.neutral):not(.link) {
|
||||
@include themify($themes) {
|
||||
background-color: themed("primaryColor");
|
||||
color: themed("textContrast");
|
||||
border-color: themed("primaryColor");
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #1751bd;
|
||||
@include themify($themes) {
|
||||
background-color: darken(themed("primaryColor"), 1.5%);
|
||||
color: darken(themed("textContrast"), 6%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.link,
|
||||
button.neutral {
|
||||
background: none;
|
||||
padding: 5px 15px;
|
||||
color: #175ddc;
|
||||
border: 0;
|
||||
@include themify($themes) {
|
||||
background-color: transparent;
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
text-decoration: underline;
|
||||
|
||||
@include themify($themes) {
|
||||
background-color: transparent;
|
||||
color: darken(themed("primaryColor"), 6%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 0.35rem;
|
||||
border: 1px solid #000000;
|
||||
border-radius: $border-radius;
|
||||
|
||||
@include themify($themes) {
|
||||
color: themed("textColor");
|
||||
background-color: themed("inputBackgroundColor");
|
||||
border-color: themed("inputBorderColor");
|
||||
}
|
||||
}
|
||||
|
||||
select,
|
||||
button {
|
||||
font-size: $font-size-base;
|
||||
font-family: $font-family-sans-serif;
|
||||
}
|
||||
|
||||
.select-folder[isVaultLocked="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
@import "~nord/src/sass/nord.scss";
|
||||
|
||||
$font-family-sans-serif: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
$font-size-base: 14px;
|
||||
$text-color: #333333;
|
||||
$border-color-dark: #ddd;
|
||||
$border-radius: 3px;
|
||||
|
||||
$brand-primary: #175ddc;
|
||||
|
||||
$background-color: #f0f0f0;
|
||||
|
||||
$solarizedDarkBase03: #002b36;
|
||||
$solarizedDarkBase02: #073642;
|
||||
$solarizedDarkBase01: #586e75;
|
||||
$solarizedDarkBase2: #eee8d5;
|
||||
$solarizedDarkCyan: #2aa198;
|
||||
$solarizedDarkGreen: #859900;
|
||||
|
||||
$themes: (
|
||||
light: (
|
||||
textColor: $text-color,
|
||||
backgroundColor: $background-color,
|
||||
primaryColor: $brand-primary,
|
||||
buttonPrimaryColor: $brand-primary,
|
||||
textContrast: $background-color,
|
||||
inputBorderColor: darken($border-color-dark, 7%),
|
||||
inputBackgroundColor: #ffffff,
|
||||
),
|
||||
dark: (
|
||||
textColor: #ffffff,
|
||||
backgroundColor: #2f343d,
|
||||
buttonPrimaryColor: #6f9df1,
|
||||
primaryColor: #6f9df1,
|
||||
textContrast: #2f343d,
|
||||
inputBorderColor: #4c525f,
|
||||
inputBackgroundColor: #2f343d,
|
||||
),
|
||||
nord: (
|
||||
textColor: $nord5,
|
||||
backgroundColor: $nord1,
|
||||
buttonPrimaryColor: $nord8,
|
||||
primaryColor: $nord9,
|
||||
textContrast: $nord2,
|
||||
inputBorderColor: $nord0,
|
||||
inputBackgroundColor: $nord2,
|
||||
),
|
||||
solarizedDark: (
|
||||
textColor: $solarizedDarkBase2,
|
||||
backgroundColor: $solarizedDarkBase03,
|
||||
buttonPrimaryColor: $solarizedDarkCyan,
|
||||
primaryColor: $solarizedDarkGreen,
|
||||
textContrast: $solarizedDarkBase02,
|
||||
inputBorderColor: rgba($solarizedDarkBase2, 0.2),
|
||||
inputBackgroundColor: $solarizedDarkBase01,
|
||||
),
|
||||
);
|
||||
|
||||
@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);
|
||||
}
|
Loading…
Reference in New Issue