bitwarden-browser/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts

109 lines
3.8 KiB
TypeScript

import "@webcomponents/custom-elements";
import "lit/polyfill-support.js";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EVENTS } from "../../../constants";
import { logoIcon, logoLockedIcon } from "../../../utils/svg-icons";
import { buildSvgDomElement } from "../../../utils/utils";
import {
InitAutofillOverlayButtonMessage,
OverlayButtonWindowMessageHandlers,
} from "../../abstractions/autofill-overlay-button";
import AutofillOverlayPageElement from "../shared/autofill-overlay-page-element";
class AutofillOverlayButton extends AutofillOverlayPageElement {
private authStatus: AuthenticationStatus = AuthenticationStatus.LoggedOut;
private readonly buttonElement: HTMLButtonElement;
private readonly logoIconElement: HTMLElement;
private readonly logoLockedIconElement: HTMLElement;
private readonly overlayButtonWindowMessageHandlers: OverlayButtonWindowMessageHandlers = {
initAutofillOverlayButton: ({ message }) => this.initAutofillOverlayButton(message),
checkAutofillOverlayButtonFocused: () => this.checkButtonFocused(),
updateAutofillOverlayButtonAuthStatus: ({ message }) =>
this.updateAuthStatus(message.authStatus),
};
constructor() {
super();
this.buttonElement = globalThis.document.createElement("button");
this.setupGlobalListeners(this.overlayButtonWindowMessageHandlers);
this.logoIconElement = buildSvgDomElement(logoIcon);
this.logoIconElement.classList.add("overlay-button-svg-icon", "logo-icon");
this.logoLockedIconElement = buildSvgDomElement(logoLockedIcon);
this.logoLockedIconElement.classList.add("overlay-button-svg-icon", "logo-locked-icon");
}
/**
* Initializes the overlay button. Facilitates ensuring that the page
* is set up with the expected styles and translations.
*
* @param authStatus - The authentication status of the user
* @param styleSheetUrl - The URL of the stylesheet to apply to the page
* @param translations - The translations to apply to the page
* @private
*/
private async initAutofillOverlayButton({
authStatus,
styleSheetUrl,
translations,
}: InitAutofillOverlayButtonMessage) {
const linkElement = this.initOverlayPage("button", styleSheetUrl, translations);
this.buttonElement.tabIndex = -1;
this.buttonElement.type = "button";
this.buttonElement.classList.add("overlay-button");
this.buttonElement.setAttribute(
"aria-label",
this.getTranslation("toggleBitwardenVaultOverlay"),
);
this.buttonElement.addEventListener(EVENTS.CLICK, this.handleButtonElementClick);
this.updateAuthStatus(authStatus);
this.shadowDom.append(linkElement, this.buttonElement);
}
/**
* Updates the authentication status of the user. This will update the icon
* displayed on the button.
*
* @param authStatus - The authentication status of the user
*/
private updateAuthStatus(authStatus: AuthenticationStatus) {
this.authStatus = authStatus;
this.buttonElement.innerHTML = "";
const iconElement =
this.authStatus === AuthenticationStatus.Unlocked
? this.logoIconElement
: this.logoLockedIconElement;
this.buttonElement.append(iconElement);
}
/**
* Handles a click event on the button element. Posts a message to the
* parent window indicating that the button was clicked.
*/
private handleButtonElementClick = () => {
this.postMessageToParent({ command: "overlayButtonClicked" });
};
/**
* Checks if the button is focused. If it is not, then it posts a message
* to the parent window indicating that the overlay should be closed.
*/
private checkButtonFocused() {
if (globalThis.document.hasFocus()) {
return;
}
this.postMessageToParent({ command: "closeAutofillOverlay" });
}
}
export default AutofillOverlayButton;