ng-hub-ui-avatar
Version:
A universal avatar component for Angular applications that fetches / generates avatar based on the information you have about the user. Supports initials, Gravatar integration, custom images and styling options. Perfect for user profiles and comment syste
928 lines • 55.9 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Optional, Inject, Injectable, input, booleanAttribute, computed, output, SecurityContext, ViewChild, Component, NgModule, makeEnvironmentProviders } from '@angular/core';
import * as i3 from '@angular/platform-browser';
import { takeWhile, map } from 'rxjs/operators';
import * as i1 from '@angular/common/http';
import { Md5 } from 'ts-md5';
/**
* Token used to inject the AvatarConfig object
*/
const AVATAR_CONFIG = new InjectionToken('avatar.config');
class AvatarConfigService {
userConfig;
constructor(userConfig) {
this.userConfig = userConfig;
}
getAvatarSources(defaultSources) {
if (this.userConfig &&
this.userConfig.sourcePriorityOrder &&
this.userConfig.sourcePriorityOrder.length) {
const uniqueSources = [
...new Set(this.userConfig.sourcePriorityOrder)
];
const validSources = uniqueSources.filter((source) => defaultSources.includes(source));
return [
...validSources,
...defaultSources.filter((source) => !validSources.includes(source))
];
}
return defaultSources;
}
getAvatarColors(defaultColors) {
return ((this.userConfig &&
this.userConfig.colors &&
this.userConfig.colors.length &&
this.userConfig.colors) ||
defaultColors);
}
getDisableSrcCache(defaultDisableSrcCache) {
if (this.userConfig == null ||
this.userConfig.disableSrcCache == null) {
return defaultDisableSrcCache;
}
else {
return this.userConfig.disableSrcCache;
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarConfigService, deps: [{ token: AVATAR_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarConfigService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarConfigService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [AVATAR_CONFIG]
}] }] });
var AvatarSource;
(function (AvatarSource) {
AvatarSource["FACEBOOK"] = "facebook";
AvatarSource["GRAVATAR"] = "gravatar";
AvatarSource["GITHUB"] = "github";
AvatarSource["CUSTOM"] = "custom";
AvatarSource["INITIALS"] = "initials";
AvatarSource["VALUE"] = "value";
})(AvatarSource || (AvatarSource = {}));
/**
* list of Supported avatar sources
*/
const defaultSources = [
AvatarSource.FACEBOOK,
AvatarSource.GRAVATAR,
AvatarSource.GITHUB,
AvatarSource.CUSTOM,
AvatarSource.INITIALS,
AvatarSource.VALUE
];
/**
* list of default colors
*/
const defaultColors = [
'#1abc9c',
'#3498db',
'#f1c40f',
'#8e44ad',
'#e74c3c',
'#d35400',
'#2c3e50',
'#7f8c8d'
];
/**
* Default disable custom source cache settings
*/
const defaultDisableSrcCache = false;
/**
* Provides utilities methods related to Avatar component
*/
class AvatarService {
http;
avatarConfigService;
avatarSources = defaultSources;
avatarColors = defaultColors;
failedSources = new Map();
constructor(http, avatarConfigService) {
this.http = http;
this.avatarConfigService = avatarConfigService;
this.overrideAvatarSources();
this.overrideAvatarColors();
}
fetchAvatar(avatarUrl) {
return this.http.get(avatarUrl);
}
getRandomColor(avatarText) {
if (!avatarText) {
return 'transparent';
}
const asciiCodeSum = this.calculateAsciiCode(avatarText);
return this.avatarColors[asciiCodeSum % this.avatarColors.length];
}
compareSources(sourceType1, sourceType2) {
return (this.getSourcePriority(sourceType1) - this.getSourcePriority(sourceType2));
}
isSource(source) {
return this.avatarSources.includes(source);
}
isTextAvatar(sourceType) {
return [AvatarSource.INITIALS, AvatarSource.VALUE].includes(sourceType);
}
buildSourceKey(source) {
return source.sourceType + '-' + source.sourceId;
}
sourceHasFailedBefore(source) {
return this.failedSources.has(this.buildSourceKey(source));
}
markSourceAsFailed(source) {
this.failedSources.set(this.buildSourceKey(source), source);
}
overrideAvatarSources() {
this.avatarSources = this.avatarConfigService.getAvatarSources(defaultSources);
}
overrideAvatarColors() {
this.avatarColors = this.avatarConfigService.getAvatarColors(defaultColors);
}
calculateAsciiCode(value) {
return value
.split('')
.map(letter => letter.charCodeAt(0))
.reduce((previous, current) => previous + current);
}
getSourcePriority(sourceType) {
return this.avatarSources.indexOf(sourceType);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarService, deps: [{ token: i1.HttpClient }, { token: AvatarConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarService, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: AvatarConfigService }] });
/**
* Contract of all async sources.
* Every async source must implement the processResponse method that extracts the avatar url from the data
*/
class AsyncSource {
sourceId;
constructor(sourceId) {
this.sourceId = sourceId;
}
}
/**
* Normalises an accent colour into a paintable value for a `--hub-*-accent` CSS slot,
* accepting ANY colour. A bareword (a semantic name, a host-registered accent, or a CSS
* named colour) resolves to its design-system token `var(--hub-sys-color-<name>, <name>)`
* — the raw word is the fallback so an unregistered name still paints. A literal `#hex` /
* `rgb()` / `oklch()` / `var(...)` is passed through unchanged. Returns `null` when the
* value is unset or blank, so the SCSS default (or a builtin `@each`) can take over.
*
* @param value The raw accent value to normalise.
* @returns The paintable CSS value, or `null` when unset/blank.
*/
function resolveHubAccent(value) {
const color = value?.trim();
if (!color) {
return null;
}
return /^[a-zA-Z][\w-]*$/.test(color) ? `var(--hub-sys-color-${color}, ${color})` : color;
}
/**
* Facebook source implementation.
* Fetch avatar source based on facebook identifier
* and image size
*/
class Facebook {
sourceId;
sourceType = AvatarSource.FACEBOOK;
constructor(sourceId) {
this.sourceId = sourceId;
}
getAvatar(size) {
return ('https://graph.facebook.com/' +
`${this.sourceId}/picture?width=${size}&height=${size}`);
}
}
/**
* Custom source implementation.
* return custom image as an avatar
*
*/
class Custom {
sourceId;
sourceType = AvatarSource.CUSTOM;
constructor(sourceId) {
this.sourceId = sourceId;
}
getAvatar() {
return this.sourceId;
}
}
/**
* Initials source implementation.
* return the initials of the given value
*/
class Initials {
sourceId;
sourceType = AvatarSource.INITIALS;
constructor(sourceId) {
this.sourceId = sourceId;
}
getAvatar(size) {
return this.getInitials(this.sourceId, size);
}
/**
* Returns the initial letters of a name in a string.
*/
getInitials(name, size) {
name = name.trim();
if (!name) {
return '';
}
const initials = name.split(' ');
if (size && size < initials.length) {
return this.constructInitials(initials.slice(0, size));
}
else {
return this.constructInitials(initials);
}
}
/**
* Iterates a person's name string to get the initials of each word in uppercase.
*/
constructInitials(elements) {
if (!elements || !elements.length) {
return '';
}
return elements
.filter(element => element && element.length > 0)
.map(element => element[0].toUpperCase())
.join('');
}
}
function isRetina() {
if (typeof window !== 'undefined' && window !== null) {
if (window.devicePixelRatio > 1.25) {
return true;
}
const mediaQuery = '(-webkit-min-device-pixel-ratio: 1.25), (min--moz-device-pixel-ratio: 1.25), (-o-min-device-pixel-ratio: 5/4), (min-resolution: 1.25dppx)';
if (window.matchMedia && window.matchMedia(mediaQuery).matches) {
return true;
}
}
return false;
}
/**
* Gravatar source implementation.
* Fetch avatar source based on gravatar email
*/
class Gravatar {
value;
sourceType = AvatarSource.GRAVATAR;
sourceId;
constructor(value) {
this.value = value;
this.sourceId = value.match('^[a-f0-9]{32}$')
? value
: Md5.hashStr(value).toString();
}
getAvatar(size) {
const avatarSize = isRetina() ? size * 2 : size;
return `https://secure.gravatar.com/avatar/${this.sourceId}?s=${avatarSize}&d=404`;
}
}
/**
* Value source implementation.
* return the value as avatar
*/
class Value {
sourceId;
sourceType = AvatarSource.VALUE;
constructor(sourceId) {
this.sourceId = sourceId;
}
getAvatar() {
return this.sourceId;
}
}
/**
* GitHub source implementation.
* Fetch avatar source based on github identifier
*/
class Github extends AsyncSource {
sourceType = AvatarSource.GITHUB;
constructor(sourceId) {
super(sourceId);
}
getAvatar() {
return `https://api.github.com/users/${this.sourceId}`;
}
/**
* extract github avatar from json data
*/
processResponse(data, size) {
if (size) {
return `${data.avatar_url}&s=${size}`;
}
return data.avatar_url;
}
}
/**
* Custom source implementation (with no cache).
* return custom image as an avatar
*
*/
class CustomNoCache {
sourceId;
sourceType = AvatarSource.CUSTOM;
constructor(sourceId) {
this.sourceId = sourceId;
}
getAvatar() {
const urlSuffix = Math.random();
return `${this.sourceId}${this.sourceId.indexOf('?') > -1 ? '&' : '?'}_=${urlSuffix}`;
}
}
/**
* Factory class that implements factory method pattern.
* Used to create Source implementation class based
* on the source Type
*/
class SourceFactory {
sources = {};
constructor(avatarConfigService) {
const disableSrcCache = avatarConfigService.getDisableSrcCache(defaultDisableSrcCache);
this.sources[AvatarSource.FACEBOOK] = Facebook;
this.sources[AvatarSource.GRAVATAR] = Gravatar;
this.sources[AvatarSource.CUSTOM] = disableSrcCache ? CustomNoCache : Custom;
this.sources[AvatarSource.INITIALS] = Initials;
this.sources[AvatarSource.VALUE] = Value;
this.sources[AvatarSource.GITHUB] = Github;
}
newInstance(sourceType, sourceValue) {
return new this.sources[sourceType](sourceValue);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: SourceFactory, deps: [{ token: AvatarConfigService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: SourceFactory, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: SourceFactory, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: AvatarConfigService }] });
/**
* Universal avatar component that
* generates avatar from different sources
*
* export
* class AvatarComponent
* implements {OnChanges}
*/
class AvatarComponent {
sourceFactory;
avatarService;
sanitizer;
round = input(true, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "round" }] : /* istanbul ignore next */ []));
size = input(50, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
textSizeRatio = input(3, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "textSizeRatio" }] : /* istanbul ignore next */ []));
bgColor = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "bgColor" }] : /* istanbul ignore next */ []));
fgColor = input('#FFF', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "fgColor" }] : /* istanbul ignore next */ []));
borderColor = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "borderColor" }] : /* istanbul ignore next */ []));
/**
* When `true` (default) an initials avatar gets a background colour derived from
* a hash of its `name`, applied inline. Set to `false` to suppress that inline
* colour so the avatar can be themed via the `--hub-avatar-bg-color` CSS variable
* without needing `!important`. An explicit `bgColor` always wins over both.
*/
autoColor = input(true, { ...(ngDevMode ? { debugName: "autoColor" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
style = input({}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "style" }] : /* istanbul ignore next */ []));
cornerRadius = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "cornerRadius" }] : /* istanbul ignore next */ []));
facebook = input(undefined, { ...(ngDevMode ? { debugName: "facebook" } : /* istanbul ignore next */ {}), alias: 'facebookId' });
gravatar = input(undefined, { ...(ngDevMode ? { debugName: "gravatar" } : /* istanbul ignore next */ {}), alias: 'gravatarId' });
github = input(undefined, { ...(ngDevMode ? { debugName: "github" } : /* istanbul ignore next */ {}), alias: 'githubId' });
custom = input(undefined, { ...(ngDevMode ? { debugName: "custom" } : /* istanbul ignore next */ {}), alias: 'src' });
customAlt = input(undefined, { ...(ngDevMode ? { debugName: "customAlt" } : /* istanbul ignore next */ {}), alias: 'alt' });
initials = input(undefined, { ...(ngDevMode ? { debugName: "initials" } : /* istanbul ignore next */ {}), alias: 'name' });
value = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
referrerpolicy = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "referrerpolicy" }] : /* istanbul ignore next */ []));
placeholder = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
initialsSize = input(0, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "initialsSize" }] : /* istanbul ignore next */ []));
/**
* Overlay badge at the bottom-end corner. A boolean / empty value renders a plain
* dot (great for a presence indicator); a string or number renders a labelled badge
* (e.g. a count like `"4k"`). `null` / absent (default) renders nothing.
*
* @example <hub-avatar badge badgeColor="success" /> // dot
* @example <hub-avatar badge="4k" badgeColor="danger" /> // labelled
*/
badge = input(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
/**
* Semantic colour of the {@link badge} (and, as a host class, of the avatar itself).
* Maps to a `--hub-sys-color-*` token; any custom string also works (set
* `--hub-avatar-badge-color`). When unset the badge uses a neutral default.
*/
badgeColor = input(null, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "badgeColor" }] : /* istanbul ignore next */ []));
/**
* Normalises {@link badgeColor} into a paintable value for the `--hub-avatar-badge-color`
* accent slot, accepting ANY colour. A bareword (a semantic name, a host-registered accent,
* or a CSS named colour) resolves to its design-system token `var(--hub-sys-color-<name>,
* <name>)` — the raw word is the fallback so an unregistered name still paints. A literal
* `#hex` / `rgb()` / `oklch()` / `var(...)` is passed through unchanged. `null` when unset,
* so the SCSS default (and the builtin `@each` per `data-badge-color`) takes over.
*/
badgeColorVar = computed(() => resolveHubAccent(this.badgeColor()), /* @ts-ignore */
...(ngDevMode ? [{ debugName: "badgeColorVar" }] : /* istanbul ignore next */ []));
/** True when a badge should be rendered (the `badge` input is set to anything but `null` / `false`). */
_hasBadge = computed(() => {
const b = this.badge();
return b !== null && b !== undefined && b !== false;
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_hasBadge" }] : /* istanbul ignore next */ []));
/** The badge's text content; empty for a plain dot (`badge` is `true` or an empty string). */
_badgeText = computed(() => {
const b = this.badge();
if (b === true || b === '' || b === null || b === undefined || b === false) {
return '';
}
return String(b);
}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_badgeText" }] : /* istanbul ignore next */ []));
/** True when the badge is a plain dot (shown, but with no text content). */
_isDot = computed(() => this._hasBadge() && this._badgeText() === '', /* @ts-ignore */
...(ngDevMode ? [{ debugName: "_isDot" }] : /* istanbul ignore next */ []));
clickOnAvatar = output();
/** Wrapper around the projected content (`<ng-content>`), used to detect whether the consumer projected anything. */
customContentRef;
/** True when the consumer projected custom content (an icon, SVG, image, …) into the avatar. */
hasCustomContent = false;
/** Inline style applied to the projected-content slot (honours `bgColor` / `fgColor` / `borderColor` / `style`). */
customContentStyle = {};
isAlive = true;
avatarSrc = null;
avatarAlt = null;
avatarText = null;
avatarStyle = {};
hostStyle = {};
currentIndex = -1;
sources = [];
constructor(sourceFactory, avatarService, sanitizer) {
this.sourceFactory = sourceFactory;
this.avatarService = avatarService;
this.sanitizer = sanitizer;
}
onAvatarClicked() {
this.clickOnAvatar.emit(this.sources[this.currentIndex]);
}
/**
* Detects projected content once it is available and, when present, computes its style.
* Runs after content init so `<ng-content>` nodes are already in place.
*/
ngAfterContentInit() {
const host = this.customContentRef?.nativeElement;
this.hasCustomContent = !!host && this.hasMeaningfulProjectedContent(host);
if (this.hasCustomContent) {
this.customContentStyle = this.getCustomContentStyle();
}
}
/**
* Returns true when the projected slot holds a real element or non-whitespace text,
* so whitespace-only projection does not flip the avatar into custom-content mode.
*
* @param host The element wrapping the projected content.
*/
hasMeaningfulProjectedContent(host) {
return Array.from(host.childNodes).some((node) => node.nodeType === Node.ELEMENT_NODE ||
(node.nodeType === Node.TEXT_NODE && (node.textContent ?? '').trim().length > 0));
}
/**
* Builds the inline style for the projected-content slot. Sensible visible defaults
* (a themed background circle and a readable foreground colour) come from CSS tokens;
* the `bgColor` / `fgColor` / `borderColor` / `style` inputs override them when set.
*/
getCustomContentStyle() {
const borderColor = this.borderColor();
const bgColor = this.bgColor();
const hasCustomFgColor = this.fgColor() !== '#FFF';
return {
backgroundColor: bgColor ? bgColor : undefined,
color: hasCustomFgColor ? this.fgColor() : undefined,
border: borderColor ? '1px solid ' + borderColor : undefined,
...this.getCustomStyleObject()
};
}
/**
* The avatar size as a px string. Exposed on the host as `--hub-avatar-size`
* so the status dot (and any token-driven child) scales with the avatar.
*/
get avatarSizePx() {
return (parseFloat(String(this.size())) || 50) + 'px';
}
/**
* Detect inputs change
*
* param {{ [propKey: string]: SimpleChange }} changes
*
* memberof AvatarComponent
*/
ngOnChanges(changes) {
for (const propName in changes) {
if (this.avatarService.isSource(propName)) {
const sourceType = AvatarSource[propName.toUpperCase()];
const currentValue = changes[propName].currentValue;
if (currentValue && typeof currentValue === 'string') {
this.addSource(sourceType, currentValue);
}
else {
const sanitized = this.sanitizer.sanitize(SecurityContext.URL, currentValue);
if (sanitized) {
this.addSource(sourceType, sanitized);
}
else {
this.removeSource(sourceType);
}
}
}
}
// Reinitialize when any source input changes so fallback order is recalculated.
this.initializeAvatar();
if (this.hasCustomContent) {
this.customContentStyle = this.getCustomContentStyle();
}
}
/**
* Fetch avatar source
*
* memberOf AvatarComponent
*/
fetchAvatarSource() {
const previousSource = this.sources[this.currentIndex];
if (previousSource) {
this.avatarService.markSourceAsFailed(previousSource);
}
const source = this.findNextSource();
if (!source) {
return;
}
if (this.avatarService.isTextAvatar(source.sourceType)) {
this.buildTextAvatar(source);
this.avatarSrc = null;
}
else {
this.buildImageAvatar(source);
}
}
findNextSource() {
while (++this.currentIndex < this.sources.length) {
const source = this.sources[this.currentIndex];
if (source && !this.avatarService.sourceHasFailedBefore(source)) {
return source;
}
}
return null;
}
ngOnDestroy() {
this.isAlive = false;
}
/**
* Initialize the avatar component and its fallback system
*/
initializeAvatar() {
const computedBorderRadius = this.round()
? '50%'
: this.cornerRadius() + 'px';
this.hostStyle = {
width: this.size() + 'px',
height: this.size() + 'px',
borderRadius: computedBorderRadius
};
this.currentIndex = -1;
if (this.sources.length > 0) {
this.sortAvatarSources();
this.fetchAvatarSource();
}
}
sortAvatarSources() {
this.sources.sort((source1, source2) => this.avatarService.compareSources(source1.sourceType, source2.sourceType));
}
buildTextAvatar(avatarSource) {
this.avatarText = avatarSource.getAvatar(+this.initialsSize());
this.avatarStyle = this.getInitialsStyle(avatarSource.sourceId);
}
buildImageAvatar(avatarSource) {
this.avatarStyle = this.getImageStyle();
if (avatarSource instanceof AsyncSource) {
this.fetchAndProcessAsyncAvatar(avatarSource);
}
else {
this.avatarSrc = this.sanitizer.bypassSecurityTrustUrl(avatarSource.getAvatar(+this.size()));
this.avatarAlt = avatarSource.getAvatar(+this.size());
}
}
/**
*
* returns initials style
*
* memberOf AvatarComponent
*/
getInitialsStyle(avatarValue) {
const borderColor = this.borderColor();
const bgColor = this.bgColor();
const hasCornerRadius = !this.round() || +this.cornerRadius() > 0;
const hasCustomFgColor = this.fgColor() !== '#FFF';
return {
textAlign: 'center',
borderRadius: hasCornerRadius ? (this.round() ? '100%' : this.cornerRadius() + 'px') : undefined,
border: borderColor ? '1px solid ' + borderColor : undefined,
textTransform: 'uppercase',
color: hasCustomFgColor ? this.fgColor() : undefined,
// Explicit `bgColor` wins; otherwise the hash colour is applied inline only
// while `autoColor` is on. With `[autoColor]="false"` no inline background is
// emitted, so `.avatar-content { background-color: var(--hub-avatar-bg-color, …) }`
// takes over and the consumer can theme the avatar through the token.
backgroundColor: bgColor ? bgColor : this.autoColor() ? this.avatarService.getRandomColor(avatarValue) : undefined,
// Only the size is set inline (it scales with `size`); the family comes from
// `.avatar-content { font-family: var(--hub-avatar-font-family, …) }` so the
// initials honour the same token as the rest of the avatar (a `font` shorthand
// here would pin Helvetica and shadow it).
fontSize: Math.floor(+this.size() / this.textSizeRatio()) + 'px',
lineHeight: this.size() + 'px',
...this.getCustomStyleObject()
};
}
/**
*
* returns image style
*
* memberOf AvatarComponent
*/
getImageStyle() {
const borderColor = this.borderColor();
const hasCornerRadius = !this.round() || +this.cornerRadius() > 0;
return {
maxWidth: '100%',
borderRadius: hasCornerRadius ? (this.round() ? '50%' : this.cornerRadius() + 'px') : undefined,
border: borderColor ? '1px solid ' + borderColor : undefined,
width: this.size() + 'px',
height: this.size() + 'px',
...this.getCustomStyleObject()
};
}
getCustomStyleObject() {
const customStyle = this.style();
if (!customStyle) {
return {};
}
if (typeof customStyle === 'string') {
return this.parseInlineStyleString(customStyle);
}
return customStyle;
}
parseInlineStyleString(styleString) {
const styleObject = {};
styleString
.split(';')
.map((declaration) => declaration.trim())
.filter((declaration) => declaration.length > 0)
.forEach((declaration) => {
const separatorIndex = declaration.indexOf(':');
if (separatorIndex <= 0) {
return;
}
const property = declaration.slice(0, separatorIndex).trim();
const value = declaration.slice(separatorIndex + 1).trim();
if (property && value) {
styleObject[property] = value;
}
});
return styleObject;
}
/**
* Fetch avatar image asynchronously.
*
* param {Source} source represents avatar source
* memberof AvatarComponent
*/
fetchAndProcessAsyncAvatar(source) {
if (this.avatarService.sourceHasFailedBefore(source)) {
return;
}
this.avatarService
.fetchAvatar(source.getAvatar(+this.size()))
.pipe(takeWhile(() => this.isAlive), map((response) => source.processResponse(response, +this.size())))
.subscribe({
next: (avatarSrc) => (this.avatarSrc = avatarSrc),
error: () => {
this.fetchAvatarSource();
}
});
}
/**
* Add avatar source
*
* param sourceType avatar source type e.g facebook,twitter, etc.
* param sourceValue source value e.g facebookId value, etc.
*/
addSource(sourceType, sourceValue) {
const source = this.sources.find((s) => s.sourceType === sourceType);
if (source) {
source.sourceId = sourceValue;
}
else {
this.sources.push(this.sourceFactory.newInstance(sourceType, sourceValue));
}
}
/**
* Remove avatar source
*
* param sourceType avatar source type e.g facebook,twitter, etc.
*/
removeSource(sourceType) {
this.sources = this.sources.filter((source) => source.sourceType !== sourceType);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarComponent, deps: [{ token: SourceFactory }, { token: AvatarService }, { token: i3.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: AvatarComponent, isStandalone: true, selector: "hub-avatar", inputs: { round: { classPropertyName: "round", publicName: "round", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, textSizeRatio: { classPropertyName: "textSizeRatio", publicName: "textSizeRatio", isSignal: true, isRequired: false, transformFunction: null }, bgColor: { classPropertyName: "bgColor", publicName: "bgColor", isSignal: true, isRequired: false, transformFunction: null }, fgColor: { classPropertyName: "fgColor", publicName: "fgColor", isSignal: true, isRequired: false, transformFunction: null }, borderColor: { classPropertyName: "borderColor", publicName: "borderColor", isSignal: true, isRequired: false, transformFunction: null }, autoColor: { classPropertyName: "autoColor", publicName: "autoColor", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, cornerRadius: { classPropertyName: "cornerRadius", publicName: "cornerRadius", isSignal: true, isRequired: false, transformFunction: null }, facebook: { classPropertyName: "facebook", publicName: "facebookId", isSignal: true, isRequired: false, transformFunction: null }, gravatar: { classPropertyName: "gravatar", publicName: "gravatarId", isSignal: true, isRequired: false, transformFunction: null }, github: { classPropertyName: "github", publicName: "githubId", isSignal: true, isRequired: false, transformFunction: null }, custom: { classPropertyName: "custom", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, customAlt: { classPropertyName: "customAlt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, initials: { classPropertyName: "initials", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, referrerpolicy: { classPropertyName: "referrerpolicy", publicName: "referrerpolicy", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, initialsSize: { classPropertyName: "initialsSize", publicName: "initialsSize", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, badgeColor: { classPropertyName: "badgeColor", publicName: "badgeColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickOnAvatar: "clickOnAvatar" }, host: { properties: { "attr.data-badge-color": "badgeColor() || null", "style.--hub-avatar-badge-color": "badgeColorVar()", "style.--hub-avatar-size": "avatarSizePx" } }, viewQueries: [{ propertyName: "customContentRef", first: true, predicate: ["customContent"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
<div (click)="onAvatarClicked()" class="avatar-container" [class.hub-avatar--custom]="hasCustomContent" [style]="hostStyle">
<span #customContent class="hub-avatar__custom" [style]="customContentStyle"><ng-content></ng-content></span>
@if (!hasCustomContent) {
@if (avatarSrc) {
<img
[src]="avatarSrc"
[alt]="customAlt() ? customAlt() : avatarAlt"
[width]="size()"
[height]="size()"
[style]="avatarStyle"
[referrerPolicy]="referrerpolicy()"
(error)="fetchAvatarSource()"
class="avatar-content"
loading="lazy"
/>
} @else {
@if (avatarText) {
<div class="avatar-content" [style]="avatarStyle">
{{ avatarText }}
</div>
}
}
}
</div>
@if (_hasBadge()) {
<span
class="hub-avatar__badge"
[class.hub-avatar__badge--dot]="_isDot()"
[class.hub-avatar__badge--label]="!_isDot()"
[attr.aria-hidden]="_isDot() ? 'true' : null"
>{{ _badgeText() }}</span
>
}
`, isInline: true, styles: [":root,:host{--hub-avatar-size: 50px;--hub-avatar-overflow: hidden;--hub-avatar-border-radius-round: 50%;--hub-avatar-border-radius-square: var(--hub-ref-radius-sm, .25rem);--hub-avatar-border-radius: var( --hub-avatar-border-radius-round, var(--hub-avatar-border-radius-square, .25rem) );--hub-avatar-border-width-default: var(--hub-ref-border-width, 1px);--hub-avatar-border-width: 0;--hub-avatar-border-color: transparent;--hub-avatar-accent: var(--hub-sys-color-primary, #0d6efd);--hub-avatar-accent-emphasis: color-mix(in oklch, var(--hub-avatar-accent) 80%, var(--hub-sys-color-ink, #212529));--hub-avatar-accent-subtle: color-mix(in oklch, var(--hub-avatar-accent) 12%, var(--hub-sys-surface-page, #fff));--hub-avatar-accent-on: oklch(from var(--hub-avatar-accent) clamp(0, (.62 - l) * 1000, 1) 0 h);--hub-avatar-fg-color: var(--hub-avatar-accent-on, var(--hub-ref-color-white, #fff));--hub-avatar-bg-color: var(--hub-avatar-accent, var(--hub-sys-color-primary, #0d6efd));--hub-avatar-font-family: var( --hub-ref-font-family-base, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif );--hub-avatar-font-weight: var(--hub-ref-font-weight-base, 400);--hub-avatar-font-size: calc(var(--hub-avatar-size, 50px) / 3);--hub-avatar-line-height: var(--hub-avatar-size, 50px);--hub-avatar-text-transform: uppercase;--hub-avatar-text-align: center;--hub-avatar-object-fit: cover;--hub-avatar-content-padding: calc(var(--hub-avatar-size, 50px) * .2);--hub-avatar-content-icon-size: calc(var(--hub-avatar-size, 50px) * .55);--hub-avatar-badge-size: calc(var(--hub-avatar-size, 50px) * .28);--hub-avatar-badge-offset: 0px;--hub-avatar-badge-ring-width: max(2px, calc(var(--hub-avatar-size, 50px) * .05));--hub-avatar-badge-ring-color: var(--hub-sys-surface-page, #fff);--hub-avatar-badge-color: var(--hub-sys-color-secondary, #6c757d);--hub-avatar-badge-text-color: var(--hub-ref-color-white, #fff);--hub-avatar-badge-font-size: calc(var(--hub-avatar-size, 50px) * .22);--hub-avatar-badge-padding: calc(var(--hub-avatar-size, 50px) * .08);--hub-avatar-group-overlap: calc(var(--hub-avatar-size, 50px) * .3);--hub-avatar-group-ring-width: max(2px, calc(var(--hub-avatar-size, 50px) * .04));--hub-avatar-group-ring-color: var(--hub-sys-surface-page, #fff)}:host{display:inline-block;position:relative;border-radius:var(--hub-avatar-border-radius, 50%)}.avatar-container{width:var(--hub-avatar-size, 50px);height:var(--hub-avatar-size, 50px);border-radius:var(--hub-avatar-border-radius, 50%);overflow:var(--hub-avatar-overflow, hidden)}.avatar-content{width:100%;height:100%;max-width:100%;box-sizing:border-box;border-radius:var(--hub-avatar-border-radius, 50%);border:var(--hub-avatar-border-width, 0) solid var(--hub-avatar-border-color, transparent);color:var(--hub-avatar-fg-color, var(--hub-ref-color-white, #fff));background-color:var(--hub-avatar-bg-color, var(--hub-sys-surface-page, #fff));font-family:var(--hub-avatar-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif);font-size:var(--hub-avatar-font-size, calc(var(--hub-avatar-size, 50px) / 3));font-weight:var(--hub-avatar-font-weight, var(--hub-ref-font-weight-base, 400));line-height:var(--hub-avatar-line-height, var(--hub-avatar-size, 50px));text-transform:var(--hub-avatar-text-transform, uppercase);text-align:var(--hub-avatar-text-align, center)}div.avatar-content{display:flex;align-items:center;justify-content:center}img.avatar-content{display:block;object-fit:var(--hub-avatar-object-fit, cover);background-color:transparent}.hub-avatar__custom{display:none}.hub-avatar--custom .hub-avatar__custom{display:flex;align-items:center;justify-content:center;width:100%;height:100%;box-sizing:border-box;padding:var(--hub-avatar-content-padding, calc(var(--hub-avatar-size, 50px) * .2));border-radius:inherit;overflow:hidden;background-color:var(--hub-avatar-bg-color, var(--hub-sys-color-primary, #0d6efd));color:var(--hub-avatar-fg-color, var(--hub-ref-color-white, #fff));font-size:var(--hub-avatar-content-icon-size, calc(var(--hub-avatar-size, 50px) * .55));line-height:1}.hub-avatar--custom .hub-avatar__custom>*{line-height:1}.hub-avatar--custom .hub-avatar__custom ::ng-deep svg,.hub-avatar--custom .hub-avatar__custom ::ng-deep img{display:block;width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain}.hub-avatar__badge{position:absolute;inset-block-end:var(--hub-avatar-badge-offset, 0px);inset-inline-end:var(--hub-avatar-badge-offset, 0px);box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;background:var(--hub-avatar-badge-color, var(--hub-sys-color-secondary, #6c757d));color:var(--hub-avatar-badge-text-color, var(--hub-ref-color-white, #fff));box-shadow:0 0 0 var(--hub-avatar-badge-ring-width, 2px) var(--hub-avatar-badge-ring-color, var(--hub-sys-surface-page, #fff))}.hub-avatar__badge--dot{width:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));height:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));border-radius:50%}.hub-avatar__badge--label{min-width:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));height:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));padding-inline:var(--hub-avatar-badge-padding, calc(var(--hub-avatar-size, 50px) * .08));border-radius:var(--hub-sys-radius-pill, 50rem);font-family:var(--hub-avatar-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif);font-size:var(--hub-avatar-badge-font-size, calc(var(--hub-avatar-size, 50px) * .22));font-weight:var(--hub-ref-font-weight-bold, 700);line-height:1}:host([data-badge-color=primary]){--hub-avatar-badge-color: var(--hub-sys-color-primary)}:host(.hub-avatar--primary){--hub-avatar-accent: var(--hub-sys-color-primary)}:host([data-badge-color=secondary]){--hub-avatar-badge-color: var(--hub-sys-color-secondary)}:host(.hub-avatar--secondary){--hub-avatar-accent: var(--hub-sys-color-secondary)}:host([data-badge-color=success]){--hub-avatar-badge-color: var(--hub-sys-color-success)}:host(.hub-avatar--success){--hub-avatar-accent: var(--hub-sys-color-success)}:host([data-badge-color=danger]){--hub-avatar-badge-color: var(--hub-sys-color-danger)}:host(.hub-avatar--danger){--hub-avatar-accent: var(--hub-sys-color-danger)}:host([data-badge-color=warning]){--hub-avatar-badge-color: var(--hub-sys-color-warning)}:host(.hub-avatar--warning){--hub-avatar-accent: var(--hub-sys-color-warning)}:host([data-badge-color=info]){--hub-avatar-badge-color: var(--hub-sys-color-info)}:host(.hub-avatar--info){--hub-avatar-accent: var(--hub-sys-color-info)}:host([data-badge-color=neutral]){--hub-avatar-badge-color: var(--hub-sys-color-neutral)}:host(.hub-avatar--neutral){--hub-avatar-accent: var(--hub-sys-color-neutral)}:host([data-badge-color=light]){--hub-avatar-badge-color: var(--hub-sys-color-light)}:host(.hub-avatar--light){--hub-avatar-accent: var(--hub-sys-color-light)}:host([data-badge-color=dark]){--hub-avatar-badge-color: var(--hub-sys-color-dark)}:host(.hub-avatar--dark){--hub-avatar-accent: var(--hub-sys-color-dark)}:host([data-badge-color=warning]),:host([data-badge-color=light]){--hub-avatar-badge-text-color: var(--hub-sys-text-primary, #212529)}.hub-avatar-group{display:inline-flex;align-items:center}.hub-avatar-group hub-avatar{box-shadow:0 0 0 var(--hub-avatar-group-ring-width, 2px) var(--hub-avatar-group-ring-color, var(--hub-sys-surface-page, #fff))}.hub-avatar-group hub-avatar+hub-avatar{margin-inline-start:calc(-1 * var(--hub-avatar-group-overlap, 15px))}\n"] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarComponent, decorators: [{
type: Component,
args: [{ selector: 'hub-avatar', standalone: true, template: `
<div (click)="onAvatarClicked()" class="avatar-container" [class.hub-avatar--custom]="hasCustomContent" [style]="hostStyle">
<span #customContent class="hub-avatar__custom" [style]="customContentStyle"><ng-content></ng-content></span>
@if (!hasCustomContent) {
@if (avatarSrc) {
<img
[src]="avatarSrc"
[alt]="customAlt() ? customAlt() : avatarAlt"
[width]="size()"
[height]="size()"
[style]="avatarStyle"
[referrerPolicy]="referrerpolicy()"
(error)="fetchAvatarSource()"
class="avatar-content"
loading="lazy"
/>
} @else {
@if (avatarText) {
<div class="avatar-content" [style]="avatarStyle">
{{ avatarText }}
</div>
}
}
}
</div>
@if (_hasBadge()) {
<span
class="hub-avatar__badge"
[class.hub-avatar__badge--dot]="_isDot()"
[class.hub-avatar__badge--label]="!_isDot()"
[attr.aria-hidden]="_isDot() ? 'true' : null"
>{{ _badgeText() }}</span
>
}
`, host: {
'[attr.data-badge-color]': 'badgeColor() || null',
'[style.--hub-avatar-badge-color]': 'badgeColorVar()',
'[style.--hub-avatar-size]': 'avatarSizePx'
}, styles: [":root,:host{--hub-avatar-size: 50px;--hub-avatar-overflow: hidden;--hub-avatar-border-radius-round: 50%;--hub-avatar-border-radius-square: var(--hub-ref-radius-sm, .25rem);--hub-avatar-border-radius: var( --hub-avatar-border-radius-round, var(--hub-avatar-border-radius-square, .25rem) );--hub-avatar-border-width-default: var(--hub-ref-border-width, 1px);--hub-avatar-border-width: 0;--hub-avatar-border-color: transparent;--hub-avatar-accent: var(--hub-sys-color-primary, #0d6efd);--hub-avatar-accent-emphasis: color-mix(in oklch, var(--hub-avatar-accent) 80%, var(--hub-sys-color-ink, #212529));--hub-avatar-accent-subtle: color-mix(in oklch, var(--hub-avatar-accent) 12%, var(--hub-sys-surface-page, #fff));--hub-avatar-accent-on: oklch(from var(--hub-avatar-accent) clamp(0, (.62 - l) * 1000, 1) 0 h);--hub-avatar-fg-color: var(--hub-avatar-accent-on, var(--hub-ref-color-white, #fff));--hub-avatar-bg-color: var(--hub-avatar-accent, var(--hub-sys-color-primary, #0d6efd));--hub-avatar-font-family: var( --hub-ref-font-family-base, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif );--hub-avatar-font-weight: var(--hub-ref-font-weight-base, 400);--hub-avatar-font-size: calc(var(--hub-avatar-size, 50px) / 3);--hub-avatar-line-height: var(--hub-avatar-size, 50px);--hub-avatar-text-transform: uppercase;--hub-avatar-text-align: center;--hub-avatar-object-fit: cover;--hub-avatar-content-padding: calc(var(--hub-avatar-size, 50px) * .2);--hub-avatar-content-icon-size: calc(var(--hub-avatar-size, 50px) * .55);--hub-avatar-badge-size: calc(var(--hub-avatar-size, 50px) * .28);--hub-avatar-badge-offset: 0px;--hub-avatar-badge-ring-width: max(2px, calc(var(--hub-avatar-size, 50px) * .05));--hub-avatar-badge-ring-color: var(--hub-sys-surface-page, #fff);--hub-avatar-badge-color: var(--hub-sys-color-secondary, #6c757d);--hub-avatar-badge-text-color: var(--hub-ref-color-white, #fff);--hub-avatar-badge-font-size: calc(var(--hub-avatar-size, 50px) * .22);--hub-avatar-badge-padding: calc(var(--hub-avatar-size, 50px) * .08);--hub-avatar-group-overlap: calc(var(--hub-avatar-size, 50px) * .3);--hub-avatar-group-ring-width: max(2px, calc(var(--hub-avatar-size, 50px) * .04));--hub-avatar-group-ring-color: var(--hub-sys-surface-page, #fff)}:host{display:inline-block;position:relative;border-radius:var(--hub-avatar-border-radius, 50%)}.avatar-container{width:var(--hub-avatar-size, 50px);height:var(--hub-avatar-size, 50px);border-radius:var(--hub-avatar-border-radius, 50%);overflow:var(--hub-avatar-overflow, hidden)}.avatar-content{width:100%;height:100%;max-width:100%;box-sizing:border-box;border-radius:var(--hub-avatar-border-radius, 50%);border:var(--hub-avatar-border-width, 0) solid var(--hub-avatar-border-color, transparent);color:var(--hub-avatar-fg-color, var(--hub-ref-color-white, #fff));background-color:var(--hub-avatar-bg-color, var(--hub-sys-surface-page, #fff));font-family:var(--hub-avatar-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif);font-size:var(--hub-avatar-font-size, calc(var(--hub-avatar-size, 50px) / 3));font-weight:var(--hub-avatar-font-weight, var(--hub-ref-font-weight-base, 400));line-height:var(--hub-avatar-line-height, var(--hub-avatar-size, 50px));text-transform:var(--hub-avatar-text-transform, uppercase);text-align:var(--hub-avatar-text-align, center)}div.avatar-content{display:flex;align-items:center;justify-content:center}img.avatar-content{display:block;object-fit:var(--hub-avatar-object-fit, cover);background-color:transparent}.hub-avatar__custom{display:none}.hub-avatar--custom .hub-avatar__custom{display:flex;align-items:center;justify-content:center;width:100%;height:100%;box-sizing:border-box;padding:var(--hub-avatar-content-padding, calc(var(--hub-avatar-size, 50px) * .2));border-radius:inherit;overflow:hidden;background-color:var(--hub-avatar-bg-color, var(--hub-sys-color-primary, #0d6efd));color:var(--hub-avatar-fg-color, var(--hub-ref-color-white, #fff));font-size:var(--hub-avatar-content-icon-size, calc(var(--hub-avatar-size, 50px) * .55));line-height:1}.hub-avatar--custom .hub-avatar__custom>*{line-height:1}.hub-avatar--custom .hub-avatar__custom ::ng-deep svg,.hub-avatar--custom .hub-avatar__custom ::ng-deep img{display:block;width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain}.hub-avatar__badge{position:absolute;inset-block-end:var(--hub-avatar-badge-offset, 0px);inset-inline-end:var(--hub-avatar-badge-offset, 0px);box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;background:var(--hub-avatar-badge-color, var(--hub-sys-color-secondary, #6c757d));color:var(--hub-avatar-badge-text-color, var(--hub-ref-color-white, #fff));box-shadow:0 0 0 var(--hub-avatar-badge-ring-width, 2px) var(--hub-avatar-badge-ring-color, var(--hub-sys-surface-page, #fff))}.hub-avatar__badge--dot{width:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));height:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));border-radius:50%}.hub-avatar__badge--label{min-width:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));height:var(--hub-avatar-badge-size, calc(var(--hub-avatar-size, 50px) * .28));padding-inline:var(--hub-avatar-badge-padding, calc(var(--hub-avatar-size, 50px) * .08));border-radius:var(--hub-sys-radius-pill, 50rem);font-family:var(--hub-avatar-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif);font-size:var(--hub-avatar-badge-font-size, calc(var(--hub-avatar-size, 50px) * .22));font-weight:var(--hub-ref-font-weight-bold, 700);line-height:1}:host([data-badge-color=primary]){--hub-avatar-badge-color: var(--hub-sys-color-primary)}:host(.hub-avatar--primary){--hub-avatar-accent: var(--hub-sys-color-primary)}:host([data-badge-color=secondary]){--hub-avatar-badge-color: var(--hub-sys-color-secondary)}:host(.hub-avatar--secondary){--hub-avatar-accent: var(--hub-sys-color-secondary)}:host([data-badge-color=success]){--hub-avatar-badge-color: var(--hub-sys-color-success)}:host(.hub-avatar--success){--hub-avatar-accent: var(--hub-sys-color-success)}:host([data-badge-color=danger]){--hub-avatar-badge-color: var(--hub-sys-color-danger)}:host(.hub-avatar--danger){--hub-avatar-accent: var(--hub-sys-color-danger)}:host([data-badge-color=warning]){--hub-avatar-badge-color: var(--hub-sys-color-warning)}:host(.hub-avatar--warning){--hub-avatar-accent: var(--hub-sys-color-warning)}:host([data-badge-color=info]){--hub-avatar-badge-color: var(--hub-sys-color-info)}:host(.hub-avatar--info){--hub-avatar-accent: var(--hub-sys-color-info)}:host([data-badge-color=neutral]){--hub-avatar-badge-color: var(--hub-sys-color-neutral)}:host(.hub-avatar--neutral){--hub-avatar-accent: var(--hub-sys-color-neutral)}:host([data-badge-color=light]){--hub-avatar-badge-color: var(--hub-sys-color-light)}:host(.hub-avatar--light){--hub-avatar-accent: var(--hub-sys-color-light)}:host([data-badge-color=dark]){--hub-avatar-badge-color: var(--hub-sys-color-dark)}:host(.hub-avatar--dark){--hub-avatar-accent: var(--hub-sys-color-dark)}:host([data-badge-color=warning]),:host([data-badge-color=light]){--hub-avatar-badge-text-color: var(--hub-sys-text-primary, #212529)}.hub-avatar-group{display:inline-flex;align-items:center}.hub-avatar-group hub-avatar{box-shadow:0 0 0 var(--hub-avatar-group-ring-width, 2px) var(--hub-avatar-group-ring-color, var(--hub-sys-surface-page, #fff))}.hub-avatar-group hub-avatar+hub-avatar{margin-inline-start:calc(-1 * var(--hub-avatar-group-overlap, 15px))}\n"] }]
}], ctorParameters: () => [{ type: SourceFactory }, { type: AvatarService }, { type: i3.DomSanitizer }], propDecorators: { round: [{ type: i0.Input, args: [{ isSignal: true, alias: "round", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], textSizeRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "textSizeRatio", required: false }] }], bgColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "bgColor", required: false }] }], fgColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "fgColor", required: false }] }], borderColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderColor", required: false }] }], autoColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoColor", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], cornerRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "cornerRadius", required: false }] }], facebook: [{ type: i0.Input, args: [{ isSignal: true, alias: "facebookId", required: false }] }], gravatar: [{ type: i0.Input, args: [{ isSignal: true, alias: "gravatarId", required: false }] }], github: [{ type: i0.Input, args: [{ isSignal: true, alias: "githubId", required: false }] }], custom: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], customAlt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], referrerpolicy: [{ type: i0.Input, args: [{ isSignal: true, alias: "referrerpolicy", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], initialsSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialsSize", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], badgeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "badgeColor", required: false }] }], clickOnAvatar: [{ type: i0.Output, args: ["clickOnAvatar"] }], customContentRef: [{
type: ViewChild,
args: ['customContent', { static: true }]
}] } });
/**
* Backward-compatibility module for `<hub-avatar>`.
*
* @deprecated `AvatarComponent` is now a standalone component. Import it directly
* (`imports: [AvatarComponent]`) and, if you need custom configuration, register
* `provideAvatar()` in your application providers. This module only re-exports the
* standalone component and will be removed in a future major version.
*/
class AvatarModule {
/**
* @deprecated Use `provideAvatar(config)` with the standalone APIs instead.
* Kept so existing `AvatarModule.forRoot()` consumers keep working.
*/
static forRoot(avatarConfig) {
return {
ngModule: AvatarModule,
providers: [
{
provide: AVATAR_CONFIG,
useValue: avatarConfig ? avatarConfig : {}
}
]
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: AvatarModule, imports: [AvatarComponent], exports: [AvatarComponent] });
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: AvatarModule, decorators: [{
type: NgModule,
args: [{
imports: [AvatarComponent],
exports: [AvatarComponent]
}]
}] });
/**
* Registers the avatar configuration for standalone applications.
*
* Standalone-friendly replacement for `AvatarModule.forRoot()`. Add it to your
* `bootstrapApplication` providers (or a route's `providers`) to customise the
* avatar source priority, colour palette or src-cache behaviour. Calling it is
* optional — `<hub-avatar>` works out of the box with sensible defaults.
*
* ```ts
* import { provideAvatar } from 'ng-hub-ui-avatar';
*
* bootstrapApplication(AppComponent, {
* providers: [
* provideAvatar({ sourcePriorityOrder: [AvatarSource.GRAVATAR, AvatarSource.INITIALS] })
* ]
* });
* ```
*
* @param config Optional avatar configuration.
* @returns Environment providers to add to the application config.
*/
function provideAvatar(config) {
return makeEnvironmentProviders([
{
provide: AVATAR_CONFIG,
useValue: config ?? {}
}
]);
}
/*
* Public API Surface of ng-hub-ui-avatar
*/
/**
* Generated bundle index. Do not edit.
*/
export { AvatarComponent, AvatarModule, AvatarService, AvatarSource, defaultColors, defaultDisableSrcCache, defaultSources, provideAvatar };
//# sourceMappingURL=ng-hub-ui-avatar.mjs.map