From 48730bca0506120bca4bf3a23545d59f2b1a9009 Mon Sep 17 00:00:00 2001 From: undergroundwires Date: Wed, 11 Oct 2023 18:38:19 +0200 Subject: [PATCH] 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` --- package-lock.json | 116 +------------ package.json | 5 - .../assets/icons/battery-full.svg | 1 + .../assets/icons/battery-half.svg | 1 + src/presentation/assets/icons/circle-info.svg | 1 + src/presentation/assets/icons/copy.svg | 1 + src/presentation/assets/icons/desktop.svg | 1 + src/presentation/assets/icons/face-smile.svg | 1 + .../assets/icons/file-arrow-down.svg | 1 + src/presentation/assets/icons/floppy-disk.svg | 1 + src/presentation/assets/icons/folder-open.svg | 1 + src/presentation/assets/icons/folder.svg | 1 + src/presentation/assets/icons/github.svg | 1 + src/presentation/assets/icons/globe.svg | 1 + src/presentation/assets/icons/left-right.svg | 1 + .../assets/icons/magnifying-glass.svg | 1 + src/presentation/assets/icons/play.svg | 1 + src/presentation/assets/icons/tag.svg | 1 + src/presentation/assets/icons/user-secret.svg | 1 + src/presentation/assets/icons/xmark.svg | 1 + .../bootstrapping/ApplicationBootstrapper.ts | 2 - .../bootstrapping/Modules/IconBootstrapper.ts | 38 ----- .../Code/CodeButtons/IconButton.vue | 22 ++- .../Instructions/CodeInstruction.vue | 6 +- .../Instructions/InstructionList.vue | 12 +- .../Code/CodeButtons/TheCodeButtons.vue | 5 +- .../Scripts/Slider/SliderHandle.vue | 8 +- .../Scripts/View/Cards/CardListItem.vue | 18 +- .../Scripts/View/TheScriptsView.vue | 4 +- .../ToggleDocumentationButton.vue | 7 +- .../components/Shared/Icon/AppIcon.vue | 40 +++++ .../components/Shared/Icon/IconName.ts | 22 +++ .../components/Shared/Icon/UseSvgLoader.ts | 92 ++++++++++ .../components/Shared/Modal/ModalDialog.vue | 6 +- .../components/TheFooter/DownloadUrlList.vue | 4 +- .../components/TheFooter/TheFooter.vue | 12 +- src/presentation/components/TheSearchBar.vue | 4 +- .../Tree/Shared/Icon/UseSvgLoader.spec.ts | 20 +++ tests/shared/WaitForValueChange.ts | 17 ++ .../components/Shared/Icon/AppIcon.spec.ts | 100 +++++++++++ .../Shared/Icon/UseSvgLoader.spec.ts | 160 ++++++++++++++++++ .../shared/Assertions/ExpectThrowsAsync.ts | 3 +- tests/unit/shared/Stubs/UseSvgLoaderStub.ts | 31 ++++ 43 files changed, 568 insertions(+), 204 deletions(-) create mode 100644 src/presentation/assets/icons/battery-full.svg create mode 100644 src/presentation/assets/icons/battery-half.svg create mode 100644 src/presentation/assets/icons/circle-info.svg create mode 100644 src/presentation/assets/icons/copy.svg create mode 100644 src/presentation/assets/icons/desktop.svg create mode 100644 src/presentation/assets/icons/face-smile.svg create mode 100644 src/presentation/assets/icons/file-arrow-down.svg create mode 100644 src/presentation/assets/icons/floppy-disk.svg create mode 100644 src/presentation/assets/icons/folder-open.svg create mode 100644 src/presentation/assets/icons/folder.svg create mode 100644 src/presentation/assets/icons/github.svg create mode 100644 src/presentation/assets/icons/globe.svg create mode 100644 src/presentation/assets/icons/left-right.svg create mode 100644 src/presentation/assets/icons/magnifying-glass.svg create mode 100644 src/presentation/assets/icons/play.svg create mode 100644 src/presentation/assets/icons/tag.svg create mode 100644 src/presentation/assets/icons/user-secret.svg create mode 100644 src/presentation/assets/icons/xmark.svg delete mode 100644 src/presentation/bootstrapping/Modules/IconBootstrapper.ts create mode 100644 src/presentation/components/Shared/Icon/AppIcon.vue create mode 100644 src/presentation/components/Shared/Icon/IconName.ts create mode 100644 src/presentation/components/Shared/Icon/UseSvgLoader.ts create mode 100644 tests/integration/presentation/components/Scripts/View/Tree/Shared/Icon/UseSvgLoader.spec.ts create mode 100644 tests/shared/WaitForValueChange.ts create mode 100644 tests/unit/presentation/components/Shared/Icon/AppIcon.spec.ts create mode 100644 tests/unit/presentation/components/Shared/Icon/UseSvgLoader.spec.ts create mode 100644 tests/unit/shared/Stubs/UseSvgLoaderStub.ts diff --git a/package-lock.json b/package-lock.json index 46137264..15858f21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,15 +6,10 @@ "packages": { "": { "name": "privacy.sexy", - "version": "0.12.3", + "version": "0.12.4", "hasInstallScript": true, "dependencies": { "@floating-ui/vue": "^1.0.2", - "@fortawesome/fontawesome-svg-core": "^6.4.0", - "@fortawesome/free-brands-svg-icons": "^6.4.0", - "@fortawesome/free-regular-svg-icons": "^6.4.0", - "@fortawesome/free-solid-svg-icons": "^6.4.0", - "@fortawesome/vue-fontawesome": "^2.0.9", "@juggle/resize-observer": "^3.4.0", "ace-builds": "^1.23.4", "cross-fetch": "^4.0.0", @@ -2578,72 +2573,6 @@ } } }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", - "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==", - "hasInstallScript": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", - "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-brands-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz", - "integrity": "sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz", - "integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", - "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", - "hasInstallScript": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/vue-fontawesome": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.10.tgz", - "integrity": "sha512-OTETSXz+3ygD2OK2/vy82cmUBpuJqeOAg4gfnnv+f2Rir1tDIhQg026Q3NQxznq83ZLz8iNqGG9XJm26inpDeg==", - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "vue": "~2" - } - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -23015,49 +22944,6 @@ } } }, - "@fortawesome/fontawesome-common-types": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", - "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==" - }, - "@fortawesome/fontawesome-svg-core": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", - "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.2" - } - }, - "@fortawesome/free-brands-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz", - "integrity": "sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.2" - } - }, - "@fortawesome/free-regular-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz", - "integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.2" - } - }, - "@fortawesome/free-solid-svg-icons": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", - "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", - "requires": { - "@fortawesome/fontawesome-common-types": "6.4.2" - } - }, - "@fortawesome/vue-fontawesome": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-2.0.10.tgz", - "integrity": "sha512-OTETSXz+3ygD2OK2/vy82cmUBpuJqeOAg4gfnnv+f2Rir1tDIhQg026Q3NQxznq83ZLz8iNqGG9XJm26inpDeg==", - "requires": {} - }, "@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", diff --git a/package.json b/package.json index 5a0f4ead..22a6261b 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,6 @@ }, "dependencies": { "@floating-ui/vue": "^1.0.2", - "@fortawesome/fontawesome-svg-core": "^6.4.0", - "@fortawesome/free-brands-svg-icons": "^6.4.0", - "@fortawesome/free-regular-svg-icons": "^6.4.0", - "@fortawesome/free-solid-svg-icons": "^6.4.0", - "@fortawesome/vue-fontawesome": "^2.0.9", "@juggle/resize-observer": "^3.4.0", "ace-builds": "^1.23.4", "cross-fetch": "^4.0.0", diff --git a/src/presentation/assets/icons/battery-full.svg b/src/presentation/assets/icons/battery-full.svg new file mode 100644 index 00000000..7ceede8b --- /dev/null +++ b/src/presentation/assets/icons/battery-full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/battery-half.svg b/src/presentation/assets/icons/battery-half.svg new file mode 100644 index 00000000..840f8f76 --- /dev/null +++ b/src/presentation/assets/icons/battery-half.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/circle-info.svg b/src/presentation/assets/icons/circle-info.svg new file mode 100644 index 00000000..82514fa4 --- /dev/null +++ b/src/presentation/assets/icons/circle-info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/copy.svg b/src/presentation/assets/icons/copy.svg new file mode 100644 index 00000000..2244213e --- /dev/null +++ b/src/presentation/assets/icons/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/desktop.svg b/src/presentation/assets/icons/desktop.svg new file mode 100644 index 00000000..1b4172b5 --- /dev/null +++ b/src/presentation/assets/icons/desktop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/face-smile.svg b/src/presentation/assets/icons/face-smile.svg new file mode 100644 index 00000000..742c43f6 --- /dev/null +++ b/src/presentation/assets/icons/face-smile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/file-arrow-down.svg b/src/presentation/assets/icons/file-arrow-down.svg new file mode 100644 index 00000000..9464e290 --- /dev/null +++ b/src/presentation/assets/icons/file-arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/floppy-disk.svg b/src/presentation/assets/icons/floppy-disk.svg new file mode 100644 index 00000000..8d33c1af --- /dev/null +++ b/src/presentation/assets/icons/floppy-disk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/folder-open.svg b/src/presentation/assets/icons/folder-open.svg new file mode 100644 index 00000000..296b3c88 --- /dev/null +++ b/src/presentation/assets/icons/folder-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/folder.svg b/src/presentation/assets/icons/folder.svg new file mode 100644 index 00000000..cb984cb9 --- /dev/null +++ b/src/presentation/assets/icons/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/github.svg b/src/presentation/assets/icons/github.svg new file mode 100644 index 00000000..004eedbf --- /dev/null +++ b/src/presentation/assets/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/globe.svg b/src/presentation/assets/icons/globe.svg new file mode 100644 index 00000000..da08a85a --- /dev/null +++ b/src/presentation/assets/icons/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/left-right.svg b/src/presentation/assets/icons/left-right.svg new file mode 100644 index 00000000..15dca761 --- /dev/null +++ b/src/presentation/assets/icons/left-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/magnifying-glass.svg b/src/presentation/assets/icons/magnifying-glass.svg new file mode 100644 index 00000000..0886e247 --- /dev/null +++ b/src/presentation/assets/icons/magnifying-glass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/play.svg b/src/presentation/assets/icons/play.svg new file mode 100644 index 00000000..1baf7c6e --- /dev/null +++ b/src/presentation/assets/icons/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/tag.svg b/src/presentation/assets/icons/tag.svg new file mode 100644 index 00000000..62e52162 --- /dev/null +++ b/src/presentation/assets/icons/tag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/user-secret.svg b/src/presentation/assets/icons/user-secret.svg new file mode 100644 index 00000000..78516b5a --- /dev/null +++ b/src/presentation/assets/icons/user-secret.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/assets/icons/xmark.svg b/src/presentation/assets/icons/xmark.svg new file mode 100644 index 00000000..68838153 --- /dev/null +++ b/src/presentation/assets/icons/xmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/presentation/bootstrapping/ApplicationBootstrapper.ts b/src/presentation/bootstrapping/ApplicationBootstrapper.ts index 97b34fa9..4635f108 100644 --- a/src/presentation/bootstrapping/ApplicationBootstrapper.ts +++ b/src/presentation/bootstrapping/ApplicationBootstrapper.ts @@ -1,4 +1,3 @@ -import { IconBootstrapper } from './Modules/IconBootstrapper'; import { VueConstructor, IVueBootstrapper } from './IVueBootstrapper'; import { VueBootstrapper } from './Modules/VueBootstrapper'; import { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator'; @@ -14,7 +13,6 @@ export class ApplicationBootstrapper implements IVueBootstrapper { private static getAllBootstrappers(): IVueBootstrapper[] { return [ - new IconBootstrapper(), new VueBootstrapper(), new RuntimeSanityValidator(), new AppInitializationLogger(), diff --git a/src/presentation/bootstrapping/Modules/IconBootstrapper.ts b/src/presentation/bootstrapping/Modules/IconBootstrapper.ts deleted file mode 100644 index 11c3b270..00000000 --- a/src/presentation/bootstrapping/Modules/IconBootstrapper.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faGithub } from '@fortawesome/free-brands-svg-icons'; -/** BRAND ICONS (PREFIX: fab) */ -import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; -/** REGULAR ICONS (PREFIX: far) */ -import { faFolderOpen, faFolder, faSmile } from '@fortawesome/free-regular-svg-icons'; -/** SOLID ICONS (PREFIX: fas (default)) */ -import { - faTimes, faFileDownload, faCopy, faSearch, faInfoCircle, faUserSecret, faDesktop, faTag, faGlobe, - faSave, faBatteryFull, faBatteryHalf, faPlay, faArrowsAltH, -} from '@fortawesome/free-solid-svg-icons'; -import { IVueBootstrapper, VueConstructor } from '../IVueBootstrapper'; - -export class IconBootstrapper implements IVueBootstrapper { - public bootstrap(vue: VueConstructor): void { - library.add( - faGithub, - faUserSecret, - faSmile, - faDesktop, - faGlobe, - faTag, - faFolderOpen, - faFolder, - faTimes, - faFileDownload, - faSave, - faCopy, - faPlay, - faSearch, - faBatteryFull, - faBatteryHalf, - faInfoCircle, - faArrowsAltH, - ); - vue.component('font-awesome-icon', FontAwesomeIcon); - } -} diff --git a/src/presentation/components/Code/CodeButtons/IconButton.vue b/src/presentation/components/Code/CodeButtons/IconButton.vue index 9cceb9b0..f404be32 100644 --- a/src/presentation/components/Code/CodeButtons/IconButton.vue +++ b/src/presentation/components/Code/CodeButtons/IconButton.vue @@ -4,30 +4,30 @@ type="button" @click="onClicked" > -
{{text}}
+ + diff --git a/src/presentation/components/Shared/Icon/IconName.ts b/src/presentation/components/Shared/Icon/IconName.ts new file mode 100644 index 00000000..3d6450ca --- /dev/null +++ b/src/presentation/components/Shared/Icon/IconName.ts @@ -0,0 +1,22 @@ +export const IconNames = [ + 'magnifying-glass', + 'copy', + 'circle-info', + 'user-secret', + 'tag', + 'github', + 'face-smile', + 'globe', + 'desktop', + 'xmark', + 'battery-half', + 'battery-full', + 'folder', + 'folder-open', + 'left-right', + 'file-arrow-down', + 'floppy-disk', + 'play', +] as const; + +export type IconName = typeof IconNames[number]; diff --git a/src/presentation/components/Shared/Icon/UseSvgLoader.ts b/src/presentation/components/Shared/Icon/UseSvgLoader.ts new file mode 100644 index 00000000..2fef9716 --- /dev/null +++ b/src/presentation/components/Shared/Icon/UseSvgLoader.ts @@ -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, + loaders: FileLoaders = RawSvgLoaders, +) { + const svgContent = ref(''); + + 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 Promise>; + +const LazyIconCache = new Map>(); + +async function lazyLoadSvg(name: IconName, loaders: FileLoaders): Promise { + let iconLoader = LazyIconCache.get(name); + if (!iconLoader) { + iconLoader = new AsyncLazy(() => loadSvg(name, loaders)); + LazyIconCache.set(name, iconLoader); + } + const icon = await iconLoader.getValue(); + return icon; +} + +async function loadSvg(name: IconName, loaders: FileLoaders): Promise { + 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; +} diff --git a/src/presentation/components/Shared/Modal/ModalDialog.vue b/src/presentation/components/Shared/Modal/ModalDialog.vue index 2f9bf380..6bb44f2a 100644 --- a/src/presentation/components/Shared/Modal/ModalDialog.vue +++ b/src/presentation/components/Shared/Modal/ModalDialog.vue @@ -10,9 +10,7 @@ class="dialog__close-button" @click="hide" > - + @@ -20,11 +18,13 @@