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 @@