Skip to content

Instantly share code, notes, and snippets.

@jeffwhelpley
Last active October 24, 2024 04:50
Show Gist options
  • Save jeffwhelpley/76aa894c59c56430d4d2cbc1b33c9c8a to your computer and use it in GitHub Desktop.
Save jeffwhelpley/76aa894c59c56430d4d2cbc1b33c9c8a to your computer and use it in GitHub Desktop.
An awesome icon component I created for Angular
import { Component, Input, ElementRef, Renderer2, PLATFORM_ID } from '@angular/core';
import { isPlatformServer } from '@angular/common';
import { RequestResponseAdapter } from '@gethuman/adapters';
import { config } from '@gethuman/config';
import { GhWebState } from '@gethuman/state';
@Component({
selector: 'icon',
standalone: true,
template: ``, // no content for <icon></icon> since we are setting a mask-image
styles: `
:host {
display: inline-block;
background-repeat: no-repeat;
mask-size: cover;
-webkit-mask-size: cover;
background-color: var(--color-icon-default);
/* this is a blank svg to display by default */
mask-image: url("data:image/svg+xml,%3Csvg width='80' height='80' xmlns='http://www.w3.org/2000/svg'%3E%3C/svg%3E%0A");
width: var(--size-icon-default);
height: var(--size-icon-default);
&.xs {
width: var(--size-icon-xs);
height: var(--size-icon-xs);
}
&.small {
width: var(--size-icon-sm);
height: var(--size-icon-sm);
}
&.large {
width: var(--size-icon-large);
height: var(--size-icon-large);
}
&.contrast {
background-color: var(--font-color-contrast);
}
}
`,
})
export class IconComponent {
@Input() name = '';
constructor(
@Inject(PLATFORM_ID) private platformId: Object,
private el: ElementRef,
private renderer: Renderer2,
private req: RequestResponseAdapter
) {}
async ngOnInit() {
const iconUrl = `https://${config.web.domainApp}/img/icon/${this.name}.svg`;
const hostElement = this.el.nativeElement;
if (isPlatformServer(this.platformId)) {
// see comment below for loadServerIcon
const inlineIconSvg = this.req.loadServerIcon(this.name);
if (inlineIconSvg) {
this.renderer.setStyle(hostElement, 'mask-image', `url("data:image/svg+xml,${inlineIconSvg}")`);
}
} else {
const maskImageStyle = getComputedStyle(hostElement).getPropertyValue('mask-image');
if (!maskImageStyle || maskImageStyle.length < 125) {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.renderer.setStyle(hostElement, 'mask-image', `url(${iconUrl})`);
observer.unobserve(hostElement);
}
});
});
observer.observe(hostElement);
}
}
}
}
// in separate file, RequestResponseAdapter has a server only version (i.e. only added to server AppConfig) with this:
// loadServerIcon(iconName: string): string | null {
// const cachedIcon = this.cache?.[iconName];
// if (cachedIcon) {
// return cachedIcon;
// }
// const iconPath = join(process.cwd(), 'src/assets/img/icon', `${iconName}.svg`);
// const icon = encodeURIComponent(readFileSync(iconPath).toString());
// if (this.cache) {
// this.cache[iconName] = icon;
// }
// return icon;
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment