Implement new UI component for icons #230
- Introduce `AppIcon.vue`, offering improved performance over the previous `fort-awesome` dependency. This implementation reduces bundle size by 67.31KB (tested for web using `npm run build -- --mode prod`). - Migrate Font Awesome 5 icons to Font Awesome 6. This commit facilitates migration to Vue 3.0 (#230) and ensures no Vue component remains tightly bound to a specific Vue version, enhancing code portability. Font Awesome license is not included because Font Awesome revokes its right: > "Attribution is no longer required as of Font Awesome 3.0" > > Sources: > > - https://fontawesome.com/v4/license/ (archived: https://web.archive.org/web/20231003213441/https://fontawesome.com/v4/license/, https://archive.ph/Yy9j5) > - https://github.com/FortAwesome/Font-Awesome/wiki (archived: https://web.archive.org/web/20231003214646/https://github.com/FortAwesome/Font-Awesome/wiki, https://archive.ph/C6sXv) This commit removes following third-party production dependencies: - `@fortawesome/vue-fontawesome` - `@fortawesome/free-solid-svg-icons` - `@fortawesome/free-regular-svg-icons` - `@fortawesome/free-brands-svg-icons` - `@fortawesome/fontawesome-svg-core`
This commit is contained in:
92
src/presentation/components/Shared/Icon/UseSvgLoader.ts
Normal file
92
src/presentation/components/Shared/Icon/UseSvgLoader.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import {
|
||||
WatchSource, readonly, ref, watch,
|
||||
} from 'vue';
|
||||
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||
import { IconName } from './IconName';
|
||||
|
||||
export function useSvgLoader(
|
||||
iconWatcher: WatchSource<IconName>,
|
||||
loaders: FileLoaders = RawSvgLoaders,
|
||||
) {
|
||||
const svgContent = ref<string>('');
|
||||
|
||||
watch(iconWatcher, async (iconName) => {
|
||||
svgContent.value = await lazyLoadSvg(iconName, loaders);
|
||||
}, { immediate: true });
|
||||
|
||||
return {
|
||||
svgContent: readonly(svgContent),
|
||||
};
|
||||
}
|
||||
|
||||
export function clearIconCache() {
|
||||
LazyIconCache.clear();
|
||||
}
|
||||
|
||||
export type FileLoaders = Record<string, () => Promise<string>>;
|
||||
|
||||
const LazyIconCache = new Map<IconName, AsyncLazy<string>>();
|
||||
|
||||
async function lazyLoadSvg(name: IconName, loaders: FileLoaders): Promise<string> {
|
||||
let iconLoader = LazyIconCache.get(name);
|
||||
if (!iconLoader) {
|
||||
iconLoader = new AsyncLazy<string>(() => loadSvg(name, loaders));
|
||||
LazyIconCache.set(name, iconLoader);
|
||||
}
|
||||
const icon = await iconLoader.getValue();
|
||||
return icon;
|
||||
}
|
||||
|
||||
async function loadSvg(name: IconName, loaders: FileLoaders): Promise<string> {
|
||||
const iconPath = `/assets/icons/${name}.svg`;
|
||||
const loader = loaders[iconPath];
|
||||
if (!loader) {
|
||||
throw new Error(`missing icon for "${name}" in "${iconPath}"`);
|
||||
}
|
||||
const svgContent = await loader();
|
||||
const modifiedContent = modifySvg(svgContent);
|
||||
return modifiedContent;
|
||||
}
|
||||
|
||||
const RawSvgLoaders = import.meta.glob('@/presentation/assets/icons/**/*.svg', {
|
||||
as: 'raw', // This will load the SVG file content as a string.
|
||||
/*
|
||||
Using `eager: true` to preload all icons.
|
||||
Pros:
|
||||
- Speed: Icons are instantly accessible post-initial load.
|
||||
Cons:
|
||||
- Increased initial load time due to preloading of all icons.
|
||||
- Increased bundle size.
|
||||
*/
|
||||
eager: false,
|
||||
});
|
||||
|
||||
function modifySvg(svgSource: string): string {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(svgSource, 'image/svg+xml');
|
||||
let svgRoot = doc.documentElement;
|
||||
svgRoot = removeSvgComments(svgRoot);
|
||||
svgRoot = fillSvgCurrentColor(svgRoot);
|
||||
return new XMLSerializer()
|
||||
.serializeToString(svgRoot);
|
||||
}
|
||||
|
||||
function removeSvgComments(svgRoot: HTMLElement): HTMLElement {
|
||||
const comments = Array.from(svgRoot.childNodes).filter(
|
||||
(node) => node.nodeType === Node.COMMENT_NODE,
|
||||
);
|
||||
for (const comment of comments) {
|
||||
svgRoot.removeChild(comment);
|
||||
}
|
||||
Array.from(svgRoot.children).forEach((child) => {
|
||||
removeSvgComments(child as HTMLElement);
|
||||
});
|
||||
return svgRoot;
|
||||
}
|
||||
|
||||
function fillSvgCurrentColor(svgRoot: HTMLElement): HTMLElement {
|
||||
svgRoot.querySelectorAll('path').forEach((el: Element) => {
|
||||
el.setAttribute('fill', 'currentColor');
|
||||
});
|
||||
return svgRoot;
|
||||
}
|
||||
Reference in New Issue
Block a user