mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 22:33:35 +00:00
* [CL-245] Update palette to new light and dark theme colors (#8633) * [CL-245] Add new color swatches to storybook (#8697) * [CL-238] update typography (#8997) * [CL-230] [CL-296] Update button styles (#9345) * [CL-237] Update menu styles for extension refresh (#9525) * [CL-267] Add 100-level color variants and update primary-600 (#9550) * [CL-286] Update badge to use focus-visible instead of focus (#9551) * [CL-250] Update badge styles for extension refresh (#9572) * [CL-234] callout style refresh (#9920) * [CL-233] Update form field styles (#9776) * [CL-239][CL-251][CL-342] dialog style refresh (#10096) * [CL-239] simple dialog style refresh * [CL-342] fix text overflow in dialog; add story * [CL-244] readonly fields (#10164) * [CL-352] Fix Angular errors related to form element changes (#10211) * [CL-273] Update styles for checkbox and form control (#10146) * [CL-274] Update styling for radio button (#10333) * [CL-338] Remove extra space in item content when end slot is empty (#10350) * [CL-377] Fix extension style conflict for input background (#10351) * [CL-271] Update styles for toggle (#10377) * [CL-381] Update spacing around form elements (#10432) * [CL-229] Update icon button styles (#10405) * [CL-380] Remove hover state from disabled form fields (#10639) * [CL-405] Allow toggle group input to be full width (#10658) * [CL-389] Exclude end slot label content from truncation (#10508) * [CL-383] Remove manual focus when password toggle is clicked (#10749) * [CL-278][CL-391] misc bit-item style fixes (#10758) * [CL-391] use pointer cursor on hover when link or button * [CL-210] Change base font size from 14px to 16px (#10779) * [CL-291] Finalize styling for chip select (#10771) * [CL-257] update banner component styles (#10766) * [CL-443] Fix sizing issues (#10893) * [CL-445] Fix small sizing and spacing issues (#10962) * [CL-382] Reduce element shifting on readonly hover (#10956) * [CL-396] Update theme colors to new hexes (#10968) * [CL-395] Remove text headers color (#10997) * [CL-404] Switch to primary-600 for all focus indicators (#11015) * [CL-397] Remove primary-500 (#11036) * [CL-447] Ensure DM Sans displays correctly at all font weights (#11041) * [CL-448] Scrollbar Styles (#11111) * CL-252/update toast (#10996) * [CL-275] Update link styles (#11174) * [CL-446] Update hover state for unselected chip selects (#11172) * [CL-454] Improve color a11y for toast and banner interactive elements (#11200) * [CL-457] Center input text for select and multiselect (#11239) * [CL-455] Do not use responsive margin for sections in dialogs or extension (#11243) * [CL-459] Fix chip behavior when opening menu while item is selected (#11227) * [CL-388] Update vertical nav colors for new palette (#11226) * scope styled scrollbar to only select elements (#11247) * edit radio buttons to be block inputs and update spacing (#11291) * [CL-453] Fix multiselect chip spacing and truncation (#11300) * [PM-11131] Prevent duplicated sr labels on form field icon buttons (#11383) * [CL-303] Prevent chip menu from running offscreen (#11348) * [CL-476] Fix DM Sans font on Windows (#11409) * implements scrollbar styles for firefox/chrome and safari (#11447) * [CL-472] Fix search background color in extension (#11466) * [CL-481] Style updates for bit-item, bit-card, and primary-100 (#11473) * [CL-478] Remove underline on hover for most components (#11477) * [CL-477] Remove focus styles for readonly input (#11510) * [CL-487] Fix vault items virtual scroll height (#11581) * [PM-8625] Increase popup width (#11686) * [CL-494] Wrap long words in toggle group (#11659) * [CL-13820] Add class to remove link underline (#11762) * [CL-435] Prevent Windows extension from shifting (#11851) * [CL-503] Add notification color variables (#11802) * [PM-14043] Update size of toggle group label to fit more content (#11881) * [CL-498] Set chip menu width minimum to chip select width (#11905) --------- Co-authored-by: Will Martin <contact@willmartian.com> Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com> Co-authored-by: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Co-authored-by: Merissa Weinstein <merissa.k.weinstein@gmail.com> Co-authored-by: Danielle Flinn <43477473+danielleflinn@users.noreply.github.com>
134 lines
4.4 KiB
TypeScript
134 lines
4.4 KiB
TypeScript
import { Component, Input, OnChanges } from "@angular/core";
|
|
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
|
|
|
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
|
|
type SizeTypes = "xlarge" | "large" | "default" | "small" | "xsmall";
|
|
|
|
const SizeClasses: Record<SizeTypes, string[]> = {
|
|
xlarge: ["tw-h-24", "tw-w-24"],
|
|
large: ["tw-h-16", "tw-w-16"],
|
|
default: ["tw-h-10", "tw-w-10"],
|
|
small: ["tw-h-7", "tw-w-7"],
|
|
xsmall: ["tw-h-6", "tw-w-6"],
|
|
};
|
|
|
|
@Component({
|
|
selector: "bit-avatar",
|
|
template: `<img *ngIf="src" [src]="src" title="{{ title || text }}" [ngClass]="classList" />`,
|
|
})
|
|
export class AvatarComponent implements OnChanges {
|
|
@Input() border = false;
|
|
@Input() color?: string;
|
|
@Input() id?: string;
|
|
@Input() text?: string;
|
|
@Input() title: string;
|
|
@Input() size: SizeTypes = "default";
|
|
|
|
private svgCharCount = 2;
|
|
private svgFontSize = 20;
|
|
private svgFontWeight = 300;
|
|
private svgSize = 48;
|
|
src: SafeResourceUrl;
|
|
|
|
constructor(public sanitizer: DomSanitizer) {}
|
|
|
|
ngOnChanges() {
|
|
this.generate();
|
|
}
|
|
|
|
get classList() {
|
|
return ["tw-rounded-full"]
|
|
.concat(SizeClasses[this.size] ?? [])
|
|
.concat(this.border ? ["tw-border", "tw-border-solid", "tw-border-secondary-600"] : []);
|
|
}
|
|
|
|
private generate() {
|
|
let chars: string = null;
|
|
const upperCaseText = this.text?.toUpperCase() ?? "";
|
|
|
|
chars = this.getFirstLetters(upperCaseText, this.svgCharCount);
|
|
|
|
if (chars == null) {
|
|
chars = this.unicodeSafeSubstring(upperCaseText, this.svgCharCount);
|
|
}
|
|
|
|
// If the chars contain an emoji, only show it.
|
|
if (chars.match(Utils.regexpEmojiPresentation)) {
|
|
chars = chars.match(Utils.regexpEmojiPresentation)[0];
|
|
}
|
|
|
|
let svg: HTMLElement;
|
|
let hexColor = this.color;
|
|
|
|
if (!Utils.isNullOrWhitespace(this.color)) {
|
|
svg = this.createSvgElement(this.svgSize, hexColor);
|
|
} else if (!Utils.isNullOrWhitespace(this.id)) {
|
|
hexColor = Utils.stringToColor(this.id.toString());
|
|
svg = this.createSvgElement(this.svgSize, hexColor);
|
|
} else {
|
|
hexColor = Utils.stringToColor(upperCaseText);
|
|
svg = this.createSvgElement(this.svgSize, hexColor);
|
|
}
|
|
|
|
const charObj = this.createTextElement(chars, hexColor);
|
|
svg.appendChild(charObj);
|
|
const html = window.document.createElement("div").appendChild(svg).outerHTML;
|
|
const svgHtml = window.btoa(unescape(encodeURIComponent(html)));
|
|
|
|
// This is safe because the only user provided value, chars is set using `textContent`
|
|
this.src = this.sanitizer.bypassSecurityTrustResourceUrl(
|
|
"data:image/svg+xml;base64," + svgHtml,
|
|
);
|
|
}
|
|
|
|
private getFirstLetters(data: string, count: number): string {
|
|
const parts = data.split(" ");
|
|
if (parts.length > 1) {
|
|
let text = "";
|
|
for (let i = 0; i < count; i++) {
|
|
text += this.unicodeSafeSubstring(parts[i], 1);
|
|
}
|
|
return text;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private createSvgElement(size: number, color: string): HTMLElement {
|
|
const svgTag = window.document.createElement("svg");
|
|
svgTag.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
svgTag.setAttribute("pointer-events", "none");
|
|
svgTag.setAttribute("width", size.toString());
|
|
svgTag.setAttribute("height", size.toString());
|
|
svgTag.style.backgroundColor = color;
|
|
svgTag.style.width = size + "px";
|
|
svgTag.style.height = size + "px";
|
|
return svgTag;
|
|
}
|
|
|
|
private createTextElement(character: string, color: string): HTMLElement {
|
|
const textTag = window.document.createElement("text");
|
|
textTag.setAttribute("text-anchor", "middle");
|
|
textTag.setAttribute("y", "50%");
|
|
textTag.setAttribute("x", "50%");
|
|
textTag.setAttribute("dy", "0.35em");
|
|
textTag.setAttribute("pointer-events", "auto");
|
|
textTag.setAttribute("fill", Utils.pickTextColorBasedOnBgColor(color, 135, true));
|
|
textTag.setAttribute(
|
|
"font-family",
|
|
'"DM Sans","Helvetica Neue",Helvetica,Arial,' +
|
|
'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"',
|
|
);
|
|
// Warning do not use innerHTML here, characters are user provided
|
|
textTag.textContent = character;
|
|
textTag.style.fontWeight = this.svgFontWeight.toString();
|
|
textTag.style.fontSize = this.svgFontSize + "px";
|
|
return textTag;
|
|
}
|
|
|
|
private unicodeSafeSubstring(str: string, count: number) {
|
|
const characters = str.match(/./gu);
|
|
return characters != null ? characters.slice(0, count).join("") : "";
|
|
}
|
|
}
|