Skip to content

Commit

Permalink
fix(avatar): supported SSR
Browse files Browse the repository at this point in the history
  • Loading branch information
pimenovoleg committed Sep 17, 2024
1 parent 3371ed5 commit c50ce18
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 18 deletions.
1 change: 1 addition & 0 deletions .commitlintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const config = {
[
'primitives',
'accordion',
'avatar',
'collapsible',
'dialog',
'radio',
Expand Down
4 changes: 2 additions & 2 deletions apps/radix-docs/src/components/mdx/PropsTable.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ const { name } = Astro.props;
const directive = getDirectiveByName(name);
const { inputsClass = [], propertiesClass = [] } = (directive ?? getComponentByName(name)) || {};
const { inputsClass = [], propertiesClass = [], outputsClass = [] } = (directive ?? getComponentByName(name)) || {};
const data = [...inputsClass, ...propertiesClass];
const data = [...inputsClass, ...propertiesClass, ...outputsClass];
---

<div class="rt-TableRoot rt-r-size-2 rt-variant-surface rt-Box rt-r-my-5">
Expand Down
16 changes: 16 additions & 0 deletions apps/radix-docs/src/content/primitives/components/avatar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,20 @@ Import the component.

## API Reference

### Image
`RdxAvatarImageDirective`

The image to render.
By default it will only render when it has loaded. You can use the `onLoadingStatusChange` handler if you need more control.

<PropsTable name="RdxAvatarImageDirective" />

### Fallback
`RdxAvatarFallbackDirective`

An element that renders when the image hasn't loaded. This means whilst it's loading, or if there was an error.
If you notice a flash during loading, you can provide a `delayMs` prop to delay its rendering
so it only renders for those with slower connections.

For more control, use the `onLoadingStatusChange` handler on `rdxAvatarImage`.
<PropsTable name="RdxAvatarFallbackDirective" />
25 changes: 16 additions & 9 deletions packages/primitives/avatar/src/avatar-fallback.directive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Directive, inject, Input, NgZone, numberAttribute, OnDestroy, OnInit } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { Directive, inject, Input, NgZone, numberAttribute, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
import { injectAvatar } from './avatar-root.directive';
import { injectAvatarConfig } from './avatar.config';

Expand All @@ -21,6 +22,8 @@ export class RdxAvatarFallbackDirective implements RdxAvatarFallbackProps, OnIni

private readonly ngZone = inject(NgZone);

private readonly platformId = inject(PLATFORM_ID);

/**
* Define a delay before the fallback is shown.
* This is useful to only show the fallback for those with slower connections.
Expand All @@ -40,16 +43,20 @@ export class RdxAvatarFallbackDirective implements RdxAvatarFallbackProps, OnIni
private timeoutId: number | null = null;

ngOnInit(): void {
this.ngZone.runOutsideAngular(() => {
this.timeoutId = window.setTimeout(() => (this.delayElapsed = true), this.delayMs);
});
if (isPlatformBrowser(this.platformId)) {
this.ngZone.runOutsideAngular(() => {
this.timeoutId = globalThis.setTimeout(() => {
this.ngZone.run(() => {
this.delayElapsed = true;
});
}, this.delayMs);
});
}
}

ngOnDestroy(): void {
this.ngZone.run(() => {
if (this.timeoutId) {
window.clearTimeout(this.timeoutId);
}
});
if (isPlatformBrowser(this.platformId) && this.timeoutId !== null) {
globalThis.clearTimeout(this.timeoutId);
}
}
}
19 changes: 12 additions & 7 deletions packages/primitives/avatar/src/avatar-image.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,46 @@ export interface RdxAvatarImageProps {
exportAs: 'rdxAvatarImage',
standalone: true,
host: {
'(load)': '_onLoad()',
'(error)': '_onError()'
'(load)': 'onLoad()',
'(error)': 'onError()'
}
})
export class RdxAvatarImageDirective implements RdxAvatarImageProps, OnInit {
private readonly avatar = injectAvatar();

private readonly elementRef = inject<ElementRef<HTMLImageElement>>(ElementRef);

/* By default, it will only render when it has loaded.
/**
* By default, it will only render when it has loaded.
* You can use the `onLoadingStatusChange` handler if you need more control.
*/
@Output() onLoadingStatusChange = new EventEmitter<RdxImageLoadingStatus>();

ngOnInit(): void {
this.avatar._setState('loading');

if (!this.elementRef.nativeElement.src) {
if (!this.nativeElement.src) {
this.avatar._setState('error');
}

if (this.elementRef.nativeElement.complete) {
if (this.nativeElement.complete) {
this.avatar._setState('loaded');
}

this.onLoadingStatusChange.emit(this.avatar._state());
}

_onLoad(): void {
protected onLoad(): void {
this.avatar._setState('loaded');
this.onLoadingStatusChange.emit('loaded');
}

_onError(): void {
protected onError(): void {
this.avatar._setState('error');
this.onLoadingStatusChange.emit('error');
}

get nativeElement() {
return this.elementRef.nativeElement;
}
}

0 comments on commit c50ce18

Please sign in to comment.