import { Injectable } from '@angular/core'; import { SpriteLoaderService } from '../sprite-loader/sprite-loader.service'; import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; @Injectable() export class SvgService { private svgSprite: SVGSVGElement; private readonly isSpriteInitialized$ = new BehaviorSubject(false); constructor(private spriteLoaderService: SpriteLoaderService) { if (!this.svgSprite) { this.initializeSprite(); } } get isInitialized$(): Observable { return this.isSpriteInitialized$; } getSvg(iconName: string): SVGSVGElement { const svgElement = this.getSvgElement(iconName); if (!svgElement) { throw new Error(`Icon with name ${ iconName } not found`); } return svgElement; } private async initializeSprite(): Promise { const svgSprite = await firstValueFrom(this.spriteLoaderService.getSvgSprite()); const div = document.createElement('div'); div.innerHTML = svgSprite; this.svgSprite = div.children[0].cloneNode(true) as SVGSVGElement; this.isSpriteInitialized$.next(true); } private getSvgElement(iconName: string): SVGSVGElement { const svgElement = this.svgSprite.getElementById(iconName) as SVGSVGElement; return this.makeUniqueIds(svgElement); } private makeUniqueIds(svgElement: SVGSVGElement): SVGSVGElement { if (!svgElement) { return svgElement; } const clonedSvg = svgElement.cloneNode(true) as SVGSVGElement; const elementsWithIds = clonedSvg.querySelectorAll('[id]'); if (elementsWithIds.length) { const uniqueSuffix = uuidv4(); elementsWithIds.forEach((element) => { const oldId = element.id; const newId = oldId + uniqueSuffix; element.id = newId; this.replaceUrlReferences(clonedSvg, oldId, newId); }); } return clonedSvg; } private replaceUrlReferences(svgElement: SVGSVGElement, oldId: string, newId: string): void { const elements = svgElement.querySelectorAll('[*|href], [style], [fill], [stroke], [filter], [clip-path], [mask], [marker-start], [marker-mid], [marker-end]'); elements.forEach((element) => { Array.from(element.attributes).forEach((attr) => { if (attr.value.includes(`url(#${oldId})`)) { element.setAttribute(attr.name, attr.value.replace(`url(#${oldId})`, `url(#${newId})`)); } }); const hrefAttr = element.getAttribute('href') || element.getAttribute('xlink:href'); if (hrefAttr && hrefAttr.includes(`#${oldId}`)) { element.setAttribute(hrefAttr.startsWith('xlink:') ? 'xlink:href' : 'href', hrefAttr.replace(`#${oldId}`, `#${newId}`)); } }); } }