From 68f4922724f7aeb5d651dc6cc833f6e98e9263e7 Mon Sep 17 00:00:00 2001 From: Maksim Ivanov Date: Fri, 21 Apr 2023 20:54:16 +0300 Subject: [PATCH] feat(kit): support `fallback` for `avatar` when img not found by `avatarUrl` (#4243) --- .../components/avatar/avatar.component.ts | 16 ++++++++ .../components/avatar/avatar.module.ts | 2 + .../components/avatar/avatar.template.html | 20 +++++++++- .../components/avatar/examples/6/index.html | 12 ++++++ .../components/avatar/examples/6/index.less | 16 ++++++++ .../components/avatar/examples/6/index.ts | 15 ++++++++ .../kit/components/avatar/avatar.component.ts | 37 +++++++++++++------ .../components/avatar/avatar.template.html | 5 +++ 8 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 projects/demo/src/modules/components/avatar/examples/6/index.html create mode 100644 projects/demo/src/modules/components/avatar/examples/6/index.less create mode 100644 projects/demo/src/modules/components/avatar/examples/6/index.ts diff --git a/projects/demo/src/modules/components/avatar/avatar.component.ts b/projects/demo/src/modules/components/avatar/avatar.component.ts index 071d59b86c65..36db180ec980 100644 --- a/projects/demo/src/modules/components/avatar/avatar.component.ts +++ b/projects/demo/src/modules/components/avatar/avatar.component.ts @@ -1,4 +1,5 @@ import {Component} from '@angular/core'; +import {SafeHtml} from '@angular/platform-browser'; import {changeDetection} from '@demo/emulate/change-detection'; import {RawLoaderContent, TuiDocExample} from '@taiga-ui/addon-doc'; import {TuiSizeXXL, TuiSizeXXS} from '@taiga-ui/core'; @@ -47,12 +48,27 @@ export class ExampleTuiAvatarComponent { HTML: import('./examples/5/index.html?raw'), }; + readonly example6: TuiDocExample = { + TypeScript: import('./examples/6/index.ts?raw'), + HTML: import('./examples/6/index.html?raw'), + LESS: import('./examples/6/index.less?raw'), + }; + readonly avatarUrlVariants: readonly string[] = [ 'https://ng-web-apis.github.io/dist/assets/images/web-api.svg', + 'https://taiga-ui.dev/assets/images/test-not-found.png', + ]; + + readonly fallbackVariants: ReadonlyArray = [ + 'tuiIconUserLarge', + '', ]; avatarUrl: string | null = null; + fallback: SafeHtml | string | null = this.fallbackVariants[0]; + text = 'daenerys targaryen'; rounded = false; diff --git a/projects/demo/src/modules/components/avatar/avatar.module.ts b/projects/demo/src/modules/components/avatar/avatar.module.ts index 10a27a852681..24a8136fd1f6 100644 --- a/projects/demo/src/modules/components/avatar/avatar.module.ts +++ b/projects/demo/src/modules/components/avatar/avatar.module.ts @@ -11,6 +11,7 @@ import {TuiAvatarExample2} from './examples/2'; import {TuiAvatarExample3} from './examples/3'; import {TuiAvatarExample4} from './examples/4'; import {TuiAvatarExample5} from './examples/5'; +import {TuiAvatarExample6} from './examples/6'; @NgModule({ imports: [ @@ -27,6 +28,7 @@ import {TuiAvatarExample5} from './examples/5'; TuiAvatarExample3, TuiAvatarExample4, TuiAvatarExample5, + TuiAvatarExample6, ], exports: [ExampleTuiAvatarComponent], }) diff --git a/projects/demo/src/modules/components/avatar/avatar.template.html b/projects/demo/src/modules/components/avatar/avatar.template.html index 82187d735749..a74fb7579491 100644 --- a/projects/demo/src/modules/components/avatar/avatar.template.html +++ b/projects/demo/src/modules/components/avatar/avatar.template.html @@ -62,6 +62,14 @@ + + + + @@ -73,6 +81,7 @@ [rounded]="rounded" [size]="size" [autoColor]="autoColor" + [fallback]="fallback" [style.--tui-avatar-color]="color" [style.--tui-avatar-background]="background" > @@ -84,7 +93,7 @@ documentationPropertyMode="input" [(documentationPropertyValue)]="autoColor" > - Autocolor if there is no image + Auto color if there is no image Avatar URL or Taiga UI icon name + + Avatar URL or Taiga UI icon name + + + diff --git a/projects/demo/src/modules/components/avatar/examples/6/index.less b/projects/demo/src/modules/components/avatar/examples/6/index.less new file mode 100644 index 000000000000..d7a27e336d40 --- /dev/null +++ b/projects/demo/src/modules/components/avatar/examples/6/index.less @@ -0,0 +1,16 @@ +@import 'taiga-ui-local'; + +:host { + display: flex; +} + +.avatar { + .shadow(1); + + background: transparent; + border: 1px solid; + + &:hover { + .shadow(5); + } +} diff --git a/projects/demo/src/modules/components/avatar/examples/6/index.ts b/projects/demo/src/modules/components/avatar/examples/6/index.ts new file mode 100644 index 000000000000..1eeae25b5399 --- /dev/null +++ b/projects/demo/src/modules/components/avatar/examples/6/index.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; +import {changeDetection} from '@demo/emulate/change-detection'; +import {encapsulation} from '@demo/emulate/encapsulation'; + +@Component({ + selector: 'tui-avatar-example-6', + templateUrl: './index.html', + styleUrls: ['./index.less'], + changeDetection, + encapsulation, +}) +export class TuiAvatarExample6 { + readonly waterplea = 'https://avatars.githubusercontent.com/u/11832552?v=4'; + readonly wrongUrl = 'https://taiga-ui.dev/assets/images/test-not-found.png'; +} diff --git a/projects/kit/components/avatar/avatar.component.ts b/projects/kit/components/avatar/avatar.component.ts index 87b811d5dd60..f92ddbf2234e 100644 --- a/projects/kit/components/avatar/avatar.component.ts +++ b/projects/kit/components/avatar/avatar.component.ts @@ -5,9 +5,9 @@ import { Inject, Input, } from '@angular/core'; -import {SafeResourceUrl} from '@angular/platform-browser'; -import {tuiDefaultProp, tuiIsString, tuiRequiredSetter} from '@taiga-ui/cdk'; -import {tuiSizeBigger} from '@taiga-ui/core'; +import {SafeHtml, SafeResourceUrl} from '@angular/platform-browser'; +import {tuiDefaultProp, tuiIsString, tuiPure, tuiRequiredSetter} from '@taiga-ui/cdk'; +import {tuiSizeBigger, TuiSizeXXL, TuiSizeXXS} from '@taiga-ui/core'; import {tuiStringHashToHsl} from '@taiga-ui/kit/utils/format'; import {TUI_AVATAR_OPTIONS, TuiAvatarOptions} from './avatar-options'; @@ -35,6 +35,10 @@ export class TuiAvatarComponent { @tuiDefaultProp() text = ''; + @Input() + @tuiDefaultProp() + fallback: SafeHtml | string | null = null; + @Input() @tuiDefaultProp() autoColor: boolean = this.options.autoColor; @@ -64,16 +68,16 @@ export class TuiAvatarComponent { return tuiIsString(this.avatarUrl) && !!this.avatarUrl?.startsWith('tuiIcon'); } - get computedText(): string { - if (this.hasAvatar || this.iconAvatar || this.text === '') { - return ''; - } - - const words = this.text.split(' '); + get useFallback(): boolean { + return ( + !!this.fallback && !!this.avatarUrl && !this.isUrlValid && !this.text.length + ); + } - return words.length > 1 && tuiSizeBigger(this.size) - ? words[0].slice(0, 1) + words[1].slice(0, 1) - : words[0].slice(0, 1); + get computedText(): string { + return this.hasAvatar || this.iconAvatar || this.text === '' + ? '' + : this.getSlicedText(this.text, this.size); } get stringAvatar(): string { @@ -83,4 +87,13 @@ export class TuiAvatarComponent { onError(): void { this.isUrlValid = false; } + + @tuiPure + private getSlicedText(text: string, size: TuiSizeXXL | TuiSizeXXS): string { + const words = text.split(' '); + + return words.length > 1 && tuiSizeBigger(size) + ? words[0].slice(0, 1) + words[1].slice(0, 1) + : words[0].slice(0, 1); + } } diff --git a/projects/kit/components/avatar/avatar.template.html b/projects/kit/components/avatar/avatar.template.html index 904a6622e891..f32433aeee00 100644 --- a/projects/kit/components/avatar/avatar.template.html +++ b/projects/kit/components/avatar/avatar.template.html @@ -7,6 +7,11 @@ [src]="avatarUrl || ''" (error)="onError()" /> +