Skip to content

Commit

Permalink
Add Avatar Component (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
GODrums authored Aug 7, 2024
1 parent 601b76d commit 7d9131a
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 1 deletion.
2 changes: 1 addition & 1 deletion webapp/.prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"semi": true,
"bracketSpacing": true,
"trailingComma": "none",
"endOfLine": "lf"
"endOfLine": "auto"
}
3 changes: 3 additions & 0 deletions webapp/src/app/ui/avatar/avatar.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div [class]="computedClass()">
<img [ngSrc]="computedSrc()" [alt]="alt()" [class]="computedImageClass()" (error)="onError()" fill />
</div>
51 changes: 51 additions & 0 deletions webapp/src/app/ui/avatar/avatar.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Component, computed, input, signal } from '@angular/core';
import type { ClassValue } from 'clsx';
import type { VariantProps } from 'class-variance-authority';
import { cn } from 'app/utils';
import { cva } from 'app/storybook.helper';
import { NgOptimizedImage } from '@angular/common';

const [avatarVariants, args, argTypes] = cva('relative flex shrink-0 overflow-hidden rounded-full', {
variants: {
size: {
default: 'h-10 w-10',
sm: 'h-6 w-6 text-xs',
lg: 'h-14 w-14 text-lg'
}
},
defaultVariants: {
size: 'default'
}
});

export { args, argTypes };

interface AvatarVariants extends VariantProps<typeof avatarVariants> {}

@Component({
selector: 'app-avatar',
standalone: true,
imports: [NgOptimizedImage],
templateUrl: './avatar.component.html'
})
export class AppAvatarComponent {
class = input<ClassValue>('');
size = input<AvatarVariants['size']>('default');

src = input.required<string>();
alt = input<string>('');
imageClass = input<string>('');
fallback = input<string>('https://placehold.co/56');

canShow = signal(true);

onError = () => {
this.canShow.set(false);
};

computedClass = computed(() => cn(avatarVariants({ size: this.size() }), this.class()));

computedSrc = computed(() => (this.canShow() ? this.src() : this.fallback()));

computedImageClass = computed(() => cn('aspect-square object-cover h-full w-full', this.imageClass()));
}
95 changes: 95 additions & 0 deletions webapp/src/app/ui/avatar/avatar.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { argsToTemplate, type Meta, type StoryObj } from '@storybook/angular';
import { AppAvatarComponent, args, argTypes } from './avatar.component';

const meta: Meta<AppAvatarComponent> = {
title: 'UI/Avatar',
component: AppAvatarComponent,
tags: ['autodocs'],
args: {
...args
},
argTypes: {
...argTypes
}
};

export default meta;
type Story = StoryObj<AppAvatarComponent>;

export const Default: Story = {
args: {
src: 'https://i.pravatar.cc/40?img=1',
alt: 'avatar',
class: ''
},

render: (args) => ({
props: args,
template: `<app-avatar ${argsToTemplate(args)}></app-avatar>`
})
};

export const Small: Story = {
args: {
size: 'sm',
src: 'https://i.pravatar.cc/24?img=1'
},

render: (args) => ({
props: args,
template: `<app-avatar ${argsToTemplate(args)}></app-avatar>`
})
};

export const Medium: Story = {
args: {
size: 'default',
src: 'https://i.pravatar.cc/40?img=1'
},

render: (args) => ({
props: args,
template: `<app-avatar ${argsToTemplate(args)}></app-avatar>`
})
};

export const Large: Story = {
args: {
size: 'lg',
src: 'https://i.pravatar.cc/56?img=1',
alt: 'avatar',
class: ''
},

render: (args) => ({
props: args,
template: `<app-avatar ${argsToTemplate(args)}></app-avatar>`
})
};

export const WithRandomImage: Story = {
args: {
size: 'lg',
src: 'https://i.pravatar.cc/56',
alt: 'avatar'
},

render: (args) => ({
props: args,
template: `<app-avatar ${argsToTemplate(args)}></app-avatar>`
})
};

export const WithFallback: Story = {
args: {
size: 'default',
src: 'foobar.jpg',
fallback: 'https://placehold.co/40',
alt: 'fallback'
},

render: (args) => ({
props: args,
template: `<app-avatar ${argsToTemplate(args)}></app-avatar>`
})
};

0 comments on commit 7d9131a

Please sign in to comment.