From 8b930fc57c8ee6691ed6165bcb27d97e64a1a0c0 Mon Sep 17 00:00:00 2001 From: undergroundwires Date: Mon, 18 Sep 2023 17:57:50 +0200 Subject: [PATCH] Rewrite tooltip UI for efficiency and Vue 3.0 #230 - Introduce a new UI component for tooltips. - Fix tooltip arrow misalignment issues in code download/execution instructions dialogs. Reasons for dropping `v-tooltip` dependency: - Lack of support for Vue 3.0, which blocks migration to Vue 3.0 (see #230). - Inability to render HTML content that's required for privacy.sexy. - Inefficient, adding an extra 162.48 KB to the production bundle for web distribution (tested using `npm run build -- --mode production`). Advantages of adopting `floating-ui` (Floating UI): - Compatibility across multiple Vue versions including 2.0, 2.7, and 3.0. - Reduced boilerplate resulting in cleaner, more maintainable code. - Efficient position recalculations without reinventing the wheel. --- docs/presentation.md | 1 - package-lock.json | 169 +++++++++++------ package.json | 2 +- src/presentation/assets/styles/main.scss | 2 - .../third-party-extensions/_tooltip.scss | 45 ----- .../bootstrapping/ApplicationBootstrapper.ts | 2 - .../Modules/TooltipBootstrapper.ts | 8 - .../components/Shared/TooltipWrapper.vue | 179 ++++++++++++++---- 8 files changed, 248 insertions(+), 160 deletions(-) delete mode 100644 src/presentation/assets/styles/third-party-extensions/_tooltip.scss delete mode 100644 src/presentation/bootstrapping/Modules/TooltipBootstrapper.ts diff --git a/docs/presentation.md b/docs/presentation.md index aa45debc..0f398edd 100644 --- a/docs/presentation.md +++ b/docs/presentation.md @@ -20,7 +20,6 @@ The presentation layer uses an event-driven architecture for bidirectional react - [**`fonts/`**](./../src/presentation/assets/fonts/): Contains fonts. - [**`styles/`**](./../src/presentation/assets/styles/): Contains shared styles. - [**`components/`**](./../src/presentation/assets/styles/components): Contains styles coupled to Vue components. - - [**`vendors-extensions/`**](./../src/presentation/assets/styles/third-party-extensions): Contains styles for third-party components. - [**`main.scss`**](./../src/presentation/assets/styles/main.scss): Main Sass file, imported by other components as single entrypoint. - [**`main.ts`**](./../src/presentation/main.ts): Starts Vue app. - [**`electron/`**](./../src/presentation/electron/): Contains Electron code. diff --git a/package-lock.json b/package-lock.json index 59ea2f96..8dc5ce4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,9 +6,10 @@ "packages": { "": { "name": "privacy.sexy", - "version": "0.12.2", + "version": "0.12.3", "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", @@ -21,7 +22,6 @@ "file-saver": "^2.0.5", "markdown-it": "^13.0.1", "npm": "^9.8.1", - "v-tooltip": "2.1.3", "vue": "^2.7.14" }, "devDependencies": { @@ -1716,6 +1716,7 @@ "version": "7.22.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1726,7 +1727,8 @@ "node_modules/@babel/runtime/node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true }, "node_modules/@babel/template": { "version": "7.22.5", @@ -2520,6 +2522,62 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dependencies": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.3.tgz", + "integrity": "sha512-uvnFKtPgzLnpzzTRfhDlvXX0kLYi9lDRQbcDmT8iXl71Rx+uwSuaUIQl3DNC7w5OweAQ7XQMDObML+KaYDQfng==" + }, + "node_modules/@floating-ui/vue": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.0.2.tgz", + "integrity": "sha512-sImlAl9mAoCKZLNlwWz2P2ZMJIDlOEDXrRD6aD2sIHAka1LPC+nWtB+D3lPe7IE7FGWSbwBPTnlSdlABa3Fr0A==", + "dependencies": { + "@floating-ui/dom": "^1.4.5", + "vue-demi": ">=0.13.0" + } + }, + "node_modules/@floating-ui/vue/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "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", @@ -10909,7 +10967,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -16098,16 +16157,6 @@ "node": ">=12.13.0" } }, - "node_modules/popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", - "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/postcss": { "version": "8.4.28", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", @@ -20339,17 +20388,6 @@ "node": ">=8" } }, - "node_modules/v-tooltip": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/v-tooltip/-/v-tooltip-2.1.3.tgz", - "integrity": "sha512-xXngyxLQTOx/yUEy50thb8te7Qo4XU6h4LZB6cvEfVd9mnysUxLEoYwGWDdqR+l69liKsy3IPkdYff3J1gAJ5w==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "lodash": "^4.17.21", - "popper.js": "^1.16.1", - "vue-resize": "^1.0.1" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -20750,17 +20788,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/vue-resize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-1.0.1.tgz", - "integrity": "sha512-z5M7lJs0QluJnaoMFTIeGx6dIkYxOwHThlZDeQnWZBizKblb99GSejPnK37ZbNE/rVwDcYcHY+Io+AxdpY952w==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "vue": "^2.6.0" - } - }, "node_modules/vue-template-compiler": { "version": "2.7.14", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", @@ -22436,6 +22463,7 @@ "version": "7.22.10", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", + "dev": true, "requires": { "regenerator-runtime": "^0.14.0" }, @@ -22443,7 +22471,8 @@ "regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true } } }, @@ -22947,6 +22976,45 @@ "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true }, + "@floating-ui/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "requires": { + "@floating-ui/utils": "^0.1.3" + } + }, + "@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "requires": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "@floating-ui/utils": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.3.tgz", + "integrity": "sha512-uvnFKtPgzLnpzzTRfhDlvXX0kLYi9lDRQbcDmT8iXl71Rx+uwSuaUIQl3DNC7w5OweAQ7XQMDObML+KaYDQfng==" + }, + "@floating-ui/vue": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.0.2.tgz", + "integrity": "sha512-sImlAl9mAoCKZLNlwWz2P2ZMJIDlOEDXrRD6aD2sIHAka1LPC+nWtB+D3lPe7IE7FGWSbwBPTnlSdlABa3Fr0A==", + "requires": { + "@floating-ui/dom": "^1.4.5", + "vue-demi": ">=0.13.0" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "requires": {} + } + } + }, "@fortawesome/fontawesome-common-types": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", @@ -29343,7 +29411,8 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "lodash.debounce": { "version": "4.0.8", @@ -32853,11 +32922,6 @@ "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", "dev": true }, - "popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" - }, "postcss": { "version": "8.4.28", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", @@ -36079,17 +36143,6 @@ "sade": "^1.7.3" } }, - "v-tooltip": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/v-tooltip/-/v-tooltip-2.1.3.tgz", - "integrity": "sha512-xXngyxLQTOx/yUEy50thb8te7Qo4XU6h4LZB6cvEfVd9mnysUxLEoYwGWDdqR+l69liKsy3IPkdYff3J1gAJ5w==", - "requires": { - "@babel/runtime": "^7.13.10", - "lodash": "^4.17.21", - "popper.js": "^1.16.1", - "vue-resize": "^1.0.1" - } - }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -36316,14 +36369,6 @@ } } }, - "vue-resize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-1.0.1.tgz", - "integrity": "sha512-z5M7lJs0QluJnaoMFTIeGx6dIkYxOwHThlZDeQnWZBizKblb99GSejPnK37ZbNE/rVwDcYcHY+Io+AxdpY952w==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, "vue-template-compiler": { "version": "2.7.14", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", diff --git a/package.json b/package.json index cf1fe67d..cea4065c 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "postuninstall": "electron-builder install-app-deps" }, "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", @@ -46,7 +47,6 @@ "file-saver": "^2.0.5", "markdown-it": "^13.0.1", "npm": "^9.8.1", - "v-tooltip": "2.1.3", "vue": "^2.7.14" }, "devDependencies": { diff --git a/src/presentation/assets/styles/main.scss b/src/presentation/assets/styles/main.scss index a951f445..d06041f9 100644 --- a/src/presentation/assets/styles/main.scss +++ b/src/presentation/assets/styles/main.scss @@ -7,5 +7,3 @@ @forward "./mixins"; @forward "./components/card"; - -@forward "./third-party-extensions/tooltip.scss"; diff --git a/src/presentation/assets/styles/third-party-extensions/_tooltip.scss b/src/presentation/assets/styles/third-party-extensions/_tooltip.scss deleted file mode 100644 index 49fc1b60..00000000 --- a/src/presentation/assets/styles/third-party-extensions/_tooltip.scss +++ /dev/null @@ -1,45 +0,0 @@ -// Based on https://github.com/Akryum/v-tooltip/blob/83615e394c96ca491a4df04b892ae87e833beb97/demo-src/src/App.vue#L179-L303 -@use "@/presentation/assets/styles/colors" as *; - -.tooltip { - display: block !important; - z-index: 10000; - .tooltip-inner { - background: $color-primary-darkest; - color: $color-on-primary; - border-radius: 16px; - padding: 5px 10px 4px; - } - .tooltip-arrow { - width: 0; - height: 0; - border-style: solid; - position: absolute; - margin: 5px; - border-color: $color-primary-darkest; - z-index: 1; - } - &[x-placement^="top"] { - margin-bottom: 5px; - .tooltip-arrow { - border-width: 5px 5px 0 5px; - border-left-color: transparent !important; - border-right-color: transparent !important; - border-bottom-color: transparent !important; - bottom: -5px; - left: calc(50% - 5px); - margin-top: 0; - margin-bottom: 0; - } - } - &[aria-hidden='true'] { - visibility: hidden; - opacity: 0; - transition: opacity .15s, visibility .15s; - } - &[aria-hidden='false'] { - visibility: visible; - opacity: 1; - transition: opacity .15s; - } - } \ No newline at end of file diff --git a/src/presentation/bootstrapping/ApplicationBootstrapper.ts b/src/presentation/bootstrapping/ApplicationBootstrapper.ts index 44d048dc..97b34fa9 100644 --- a/src/presentation/bootstrapping/ApplicationBootstrapper.ts +++ b/src/presentation/bootstrapping/ApplicationBootstrapper.ts @@ -1,7 +1,6 @@ import { IconBootstrapper } from './Modules/IconBootstrapper'; import { VueConstructor, IVueBootstrapper } from './IVueBootstrapper'; import { VueBootstrapper } from './Modules/VueBootstrapper'; -import { TooltipBootstrapper } from './Modules/TooltipBootstrapper'; import { RuntimeSanityValidator } from './Modules/RuntimeSanityValidator'; import { AppInitializationLogger } from './Modules/AppInitializationLogger'; @@ -17,7 +16,6 @@ export class ApplicationBootstrapper implements IVueBootstrapper { return [ new IconBootstrapper(), new VueBootstrapper(), - new TooltipBootstrapper(), new RuntimeSanityValidator(), new AppInitializationLogger(), ]; diff --git a/src/presentation/bootstrapping/Modules/TooltipBootstrapper.ts b/src/presentation/bootstrapping/Modules/TooltipBootstrapper.ts deleted file mode 100644 index 757481eb..00000000 --- a/src/presentation/bootstrapping/Modules/TooltipBootstrapper.ts +++ /dev/null @@ -1,8 +0,0 @@ -import VTooltip from 'v-tooltip'; -import { VueConstructor, IVueBootstrapper } from '../IVueBootstrapper'; - -export class TooltipBootstrapper implements IVueBootstrapper { - public bootstrap(vue: VueConstructor): void { - vue.use(VTooltip); - } -} diff --git a/src/presentation/components/Shared/TooltipWrapper.vue b/src/presentation/components/Shared/TooltipWrapper.vue index e4b35eec..65319231 100644 --- a/src/presentation/components/Shared/TooltipWrapper.vue +++ b/src/presentation/components/Shared/TooltipWrapper.vue @@ -1,65 +1,166 @@ - -