From 96338009776ebc60bc608fa98b9e348e432fb5e1 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 21 Jun 2018 11:28:14 -0400 Subject: [PATCH] avatar component --- src/app/app.module.ts | 3 + src/app/components/avatar.component.ts | 113 ++++++++++++++++++ src/app/settings/account.component.html | 3 + .../tools/password-generator.component.html | 4 +- src/scss/styles.scss | 6 + 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 src/app/components/avatar.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 907dc16d2e..d42e3dee5d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,6 +16,8 @@ import { ServicesModule } from './services/services.module'; import { AppComponent } from './app.component'; import { ModalComponent } from './modal.component'; +import { AvatarComponent } from './components/avatar.component'; + import { FooterComponent } from './layouts/footer.component'; import { FrontendLayoutComponent } from './layouts/frontend-layout.component'; import { NavbarComponent } from './layouts/navbar.component'; @@ -87,6 +89,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; AppComponent, AttachmentsComponent, AutofocusDirective, + AvatarComponent, BlurClickDirective, BoxRowDirective, BulkDeleteComponent, diff --git a/src/app/components/avatar.component.ts b/src/app/components/avatar.component.ts new file mode 100644 index 0000000000..bd294db407 --- /dev/null +++ b/src/app/components/avatar.component.ts @@ -0,0 +1,113 @@ +import { + Component, + Input, + OnChanges, + OnInit, +} from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-avatar', + template: '', +}) +export class AvatarComponent implements OnChanges, OnInit { + @Input() data: string; + @Input() width = 45; + @Input() height = 45; + @Input() charCount = 2; + @Input() textColor = '#ffffff'; + @Input() fontSize = 20; + @Input() fontWeight = 300; + @Input() dynamic = false; + + src: string; + + constructor(public sanitizer: DomSanitizer) { } + + ngOnInit() { + if (!this.dynamic) { + this.generate(); + } + } + + ngOnChanges() { + if (this.dynamic) { + this.generate(); + } + } + + private generate() { + let chars: string = null; + const upperData = this.data.toUpperCase(); + + if (this.charCount > 1) { + chars = this.getFirstLetters(upperData, this.charCount); + } + if (chars == null) { + chars = upperData.substr(0, this.charCount); + } + + const charObj = this.getCharText(chars); + const color = this.stringToColor(upperData); + const svg = this.getSvg(this.width, this.height, color); + svg.appendChild(charObj); + const html = window.document.createElement('div').appendChild(svg).outerHTML; + const svgHtml = window.btoa(unescape(encodeURIComponent(html))); + this.src = 'data:image/svg+xml;base64,' + svgHtml; + } + + private stringToColor(str: string): string { + let hash = 0; + for (let i = 0; i < str.length; i++) { + // tslint:disable-next-line + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + let color = '#'; + for (let i = 0; i < 3; i++) { + // tslint:disable-next-line + const value = (hash >> (i * 8)) & 0xFF; + color += ('00' + value.toString(16)).substr(-2); + } + return color; + } + + 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 += parts[i].substr(0, 1); + } + return text; + } + return null; + } + + private getSvg(width: number, height: 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', width.toString()); + svgTag.setAttribute('height', height.toString()); + svgTag.style.backgroundColor = color; + svgTag.style.width = width + 'px'; + svgTag.style.height = height + 'px'; + return svgTag; + } + + private getCharText(character: 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', this.textColor); + textTag.setAttribute('font-family', '"Open Sans","Helvetica Neue",Helvetica,Arial,' + + 'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'); + textTag.textContent = character; + textTag.style.fontWeight = this.fontWeight.toString(); + textTag.style.fontSize = this.fontSize + 'px'; + return textTag; + } +} diff --git a/src/app/settings/account.component.html b/src/app/settings/account.component.html index 33bcbc7d13..81de6d92a9 100644 --- a/src/app/settings/account.component.html +++ b/src/app/settings/account.component.html @@ -20,6 +20,9 @@ +
+ +
-
-
diff --git a/src/scss/styles.scss b/src/scss/styles.scss index f427d88b1f..6ef13f04ad 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -351,6 +351,12 @@ app-password-generator-history { } } +app-avatar { + img { + @extend .rounded; + } +} + #duo-frame { background: url('../images/loading.svg') 0 0 no-repeat; height: 330px;