diff options
author | 2023-10-14 18:24:54 +0200 | |
---|---|---|
committer | 2023-10-14 16:24:54 +0000 | |
commit | 025f556023c29f8f31dc00870c80a9d790bbb2f4 (patch) | |
tree | 2886353373cc17b24f97c31f6e8880fd335c443c | |
parent | 2d2dffb14a2b51f931934055b5a59e51a070ff93 (diff) | |
download | it-tools-025f556023c29f8f31dc00870c80a9d790bbb2f4.tar.gz it-tools-025f556023c29f8f31dc00870c80a9d790bbb2f4.tar.zst it-tools-025f556023c29f8f31dc00870c80a9d790bbb2f4.zip |
refactor(ui): switched naive tooltip components to custom ones (#661)
-rw-r--r-- | components.d.ts | 2 | ||||
-rw-r--r-- | pnpm-lock.yaml | 23 | ||||
-rw-r--r-- | src/components/FavoriteButton.vue | 27 | ||||
-rw-r--r-- | src/components/InputCopyable.vue | 13 | ||||
-rw-r--r-- | src/components/NavbarButtons.vue | 83 | ||||
-rw-r--r-- | src/components/SpanCopyable.vue | 16 | ||||
-rw-r--r-- | src/components/TextareaCopyable.vue | 40 | ||||
-rw-r--r-- | src/layouts/base.layout.vue | 54 | ||||
-rw-r--r-- | src/tools/benchmark-builder/dynamic-values.vue | 13 | ||||
-rw-r--r-- | src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue | 14 | ||||
-rw-r--r-- | src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue | 15 | ||||
-rw-r--r-- | src/tools/otp-code-generator-and-validator/token-display.vue | 76 | ||||
-rw-r--r-- | src/tools/user-agent-parser/user-agent-result-cards.vue | 13 | ||||
-rw-r--r-- | src/ui/c-tooltip/c-tooltip.demo.vue | 18 | ||||
-rw-r--r-- | src/ui/c-tooltip/c-tooltip.vue | 15 |
15 files changed, 178 insertions, 244 deletions
diff --git a/components.d.ts b/components.d.ts index 1a5ce1a..3b7d96e 100644 --- a/components.d.ts +++ b/components.d.ts @@ -90,6 +90,8 @@ declare module '@vue/runtime-core' { IconMdiDownload: typeof import('~icons/mdi/download')['default'] IconMdiEye: typeof import('~icons/mdi/eye')['default'] IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default'] + IconMdiFavoriteFilled: typeof import('~icons/mdi/favorite-filled')['default'] + IconMdiHeart: typeof import('~icons/mdi/heart')['default'] IconMdiPause: typeof import('~icons/mdi/pause')['default'] IconMdiPlay: typeof import('~icons/mdi/play')['default'] IconMdiRecord: typeof import('~icons/mdi/record')['default'] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 65579a3..2428cee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3272,7 +3272,7 @@ packages: dependencies: '@unhead/dom': 0.5.1 '@unhead/schema': 0.5.1 - '@vueuse/shared': 10.4.1(vue@3.3.4) + '@vueuse/shared': 10.5.0(vue@3.3.4) unhead: 0.5.1 vue: 3.3.4 transitivePeerDependencies: @@ -3854,10 +3854,10 @@ packages: - vue dev: false - /@vueuse/shared@10.4.1(vue@3.3.4): - resolution: {integrity: sha512-vz5hbAM4qA0lDKmcr2y3pPdU+2EVw/yzfRsBdu+6+USGa4PxqSQRYIUC9/NcT06y+ZgaTsyURw2I9qOFaaXHAg==} + /@vueuse/shared@10.5.0(vue@3.3.4): + resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==} dependencies: - vue-demi: 0.14.5(vue@3.3.4) + vue-demi: 0.14.6(vue@3.3.4) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -8790,6 +8790,21 @@ packages: vue: 3.3.4 dev: false + /vue-demi@0.14.6(vue@3.3.4): + resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.4 + dev: false + /vue-eslint-parser@9.3.1(eslint@8.47.0): resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} engines: {node: ^14.17.0 || >=16.0.0} diff --git a/src/components/FavoriteButton.vue b/src/components/FavoriteButton.vue index ef180a6..c3f0aaa 100644 --- a/src/components/FavoriteButton.vue +++ b/src/components/FavoriteButton.vue @@ -1,6 +1,4 @@ <script setup lang="ts"> -import { FavoriteFilled } from '@vicons/material'; - import { useToolStore } from '@/tools/tools.store'; import type { Tool } from '@/tools/tools.types'; @@ -26,18 +24,15 @@ function toggleFavorite(event: MouseEvent) { </script> <template> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button - variant="text" - circle - :type="buttonType" - :style="{ opacity: isFavorite ? 1 : 0.2 }" - @click="toggleFavorite" - > - <n-icon :component="FavoriteFilled" /> - </c-button> - </template> - {{ isFavorite ? 'Remove from favorites' : 'Add to favorites' }} - </n-tooltip> + <c-tooltip :tooltip="isFavorite ? 'Remove from favorites' : 'Add to favorites' "> + <c-button + variant="text" + circle + :type="buttonType" + :style="{ opacity: isFavorite ? 1 : 0.2 }" + @click="toggleFavorite" + > + <icon-mdi-heart /> + </c-button> + </c-tooltip> </template> diff --git a/src/components/InputCopyable.vue b/src/components/InputCopyable.vue index ed67895..a69a039 100644 --- a/src/components/InputCopyable.vue +++ b/src/components/InputCopyable.vue @@ -13,14 +13,11 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : 'Copy to cli <template> <c-input-text v-model:value="value"> <template #suffix> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button circle variant="text" size="small" @click="copy()"> - <icon-mdi-content-copy /> - </c-button> - </template> - {{ tooltipText }} - </n-tooltip> + <c-tooltip :tooltip="tooltipText"> + <c-button circle variant="text" size="small" @click="copy()"> + <icon-mdi-content-copy /> + </c-button> + </c-tooltip> </template> </c-input-text> </template> diff --git a/src/components/NavbarButtons.vue b/src/components/NavbarButtons.vue index 5b1a3a4..653afdd 100644 --- a/src/components/NavbarButtons.vue +++ b/src/components/NavbarButtons.vue @@ -7,56 +7,43 @@ const { isDarkTheme } = toRefs(styleStore); </script> <template> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button - circle - variant="text" - href="https://github.com/CorentinTh/it-tools" - target="_blank" - rel="noopener noreferrer" - aria-label="IT-Tools' GitHub repository" - > - <n-icon size="25" :component="BrandGithub" /> - </c-button> - </template> - Github repository - </n-tooltip> + <c-tooltip tooltip="Github repository" position="bottom"> + <c-button + circle + variant="text" + href="https://github.com/CorentinTh/it-tools" + target="_blank" + rel="noopener noreferrer" + aria-label="IT-Tools' GitHub repository" + > + <n-icon size="25" :component="BrandGithub" /> + </c-button> + </c-tooltip> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button - circle - variant="text" - href="https://twitter.com/ittoolsdottech" - rel="noopener" - target="_blank" - aria-label="IT Tools' Twitter account" - > - <n-icon size="25" :component="BrandTwitter" /> - </c-button> - </template> - IT Tools' Twitter account - </n-tooltip> + <c-tooltip tooltip="Twitter account" position="bottom"> + <c-button + circle + variant="text" + href="https://twitter.com/ittoolsdottech" + rel="noopener" + target="_blank" + aria-label="IT Tools' Twitter account" + > + <n-icon size="25" :component="BrandTwitter" /> + </c-button> + </c-tooltip> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button circle variant="text" to="/about" aria-label="About"> - <n-icon size="25" :component="InfoCircle" /> - </c-button> - </template> - About - </n-tooltip> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button circle variant="text" aria-label="Toggle dark/light mode" @click="() => styleStore.toggleDark()"> - <n-icon v-if="isDarkTheme" size="25" :component="Sun" /> - <n-icon v-else size="25" :component="Moon" /> - </c-button> - </template> - <span v-if="isDarkTheme">Light mode</span> - <span v-else>Dark mode</span> - </n-tooltip> + <c-tooltip tooltip="About IT-Tools" position="bottom"> + <c-button circle variant="text" to="/about" aria-label="About"> + <n-icon size="25" :component="InfoCircle" /> + </c-button> + </c-tooltip> + <c-tooltip :tooltip="isDarkTheme ? 'Light mode' : 'Dark mode'" position="bottom"> + <c-button circle variant="text" aria-label="Toggle dark/light mode" @click="() => styleStore.toggleDark()"> + <n-icon v-if="isDarkTheme" size="25" :component="Sun" /> + <n-icon v-else size="25" :component="Moon" /> + </c-button> + </c-tooltip> </template> <style lang="less" scoped> diff --git a/src/components/SpanCopyable.vue b/src/components/SpanCopyable.vue index 7ab3a1d..5643cff 100644 --- a/src/components/SpanCopyable.vue +++ b/src/components/SpanCopyable.vue @@ -11,17 +11,7 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : initialText) </script> <template> - <n-tooltip trigger="hover"> - <template #trigger> - <span class="value" @click="copy()">{{ value }}</span> - </template> - {{ tooltipText }} - </n-tooltip> + <c-tooltip :tooltip="tooltipText"> + <span cursor-pointer font-mono @click="copy()">{{ value }}</span> + </c-tooltip> </template> - -<style scoped lang="less"> -.value { - cursor: pointer; - font-family: monospace; -} -</style> diff --git a/src/components/TextareaCopyable.vue b/src/components/TextareaCopyable.vue index b349d04..8b0aae6 100644 --- a/src/components/TextareaCopyable.vue +++ b/src/components/TextareaCopyable.vue @@ -40,7 +40,7 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage. <template> <div style="overflow-x: hidden; width: 100%"> - <c-card class="result-card"> + <c-card relative> <n-scrollbar x-scrollable trigger="none" @@ -50,16 +50,13 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage. <n-code :code="value" :language="language" :trim="false" data-test-id="area-content" /> </n-config-provider> </n-scrollbar> - <n-tooltip v-if="value" trigger="hover"> - <template #trigger> - <div class="copy-button" :class="[copyPlacement]"> - <c-button circle important:h-10 important:w-10 @click="copy()"> - <n-icon size="22" :component="Copy" /> - </c-button> - </div> - </template> - <span>{{ tooltipText }}</span> - </n-tooltip> + <div absolute right-10px top-10px> + <c-tooltip v-if="value" :tooltip="tooltipText" position="left"> + <c-button circle important:h-10 important:w-10 @click="copy()"> + <n-icon size="22" :component="Copy" /> + </c-button> + </c-tooltip> + </div> </c-card> <div v-if="copyPlacement === 'outside'" mt-4 flex justify-center> <c-button @click="copy()"> @@ -74,25 +71,4 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage. padding-bottom: 10px; margin-bottom: -10px; } -.result-card { - position: relative; - .copy-button { - position: absolute; - opacity: 1; - - &.top-right { - top: 10px; - right: 10px; - } - - &.bottom-right { - bottom: 10px; - right: 10px; - } - &.outside, - &.none { - display: none; - } - } -} </style> diff --git a/src/layouts/base.layout.vue b/src/layouts/base.layout.vue index 2b697cf..4f9e4cd 100644 --- a/src/layouts/base.layout.vue +++ b/src/layouts/base.layout.vue @@ -94,18 +94,17 @@ const tools = computed<ToolCategory[]>(() => [ <NIcon size="25" :component="Menu2" /> </c-button> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button to="/" circle variant="text" aria-label="Home"> - <NIcon size="25" :component="Home2" /> - </c-button> - </template> - Home - </n-tooltip> - - <c-button v-if="config.app.env === 'development'" to="/c-lib" circle variant="text" aria-label="UI Lib"> - <icon-mdi:brush-variant text-20px /> - </c-button> + <c-tooltip tooltip="Home" position="bottom"> + <c-button to="/" circle variant="text" aria-label="Home"> + <NIcon size="25" :component="Home2" /> + </c-button> + </c-tooltip> + + <c-tooltip tooltip="UI Lib" position="bottom"> + <c-button v-if="config.app.env === 'development'" to="/c-lib" circle variant="text" aria-label="UI Lib"> + <icon-mdi:brush-variant text-20px /> + </c-button> + </c-tooltip> <command-palette /> @@ -113,23 +112,20 @@ const tools = computed<ToolCategory[]>(() => [ <NavbarButtons v-if="!styleStore.isSmallScreen" /> </div> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button - round - href="https://www.buymeacoffee.com/cthmsst" - rel="noopener" - target="_blank" - class="support-button" - :bordered="false" - @click="() => tracker.trackEvent({ eventName: 'Support button clicked' })" - > - Buy me a coffee - <NIcon v-if="!styleStore.isSmallScreen" :component="Heart" ml-2 /> - </c-button> - </template> - ❤ Support IT Tools development ! - </n-tooltip> + <c-tooltip position="bottom" tooltip="Support IT Tools development"> + <c-button + round + href="https://www.buymeacoffee.com/cthmsst" + rel="noopener" + target="_blank" + class="support-button" + :bordered="false" + @click="() => tracker.trackEvent({ eventName: 'Support button clicked' })" + > + Buy me a coffee + <NIcon v-if="!styleStore.isSmallScreen" :component="Heart" ml-2 /> + </c-button> + </c-tooltip> </div> <slot /> </template> diff --git a/src/tools/benchmark-builder/dynamic-values.vue b/src/tools/benchmark-builder/dynamic-values.vue index 975a545..e048ef9 100644 --- a/src/tools/benchmark-builder/dynamic-values.vue +++ b/src/tools/benchmark-builder/dynamic-values.vue @@ -39,14 +39,11 @@ function onInputEnter(index: number) { autofocus @keydown.enter="onInputEnter(index)" /> - <n-tooltip> - <template #trigger> - <c-button circle variant="text" @click="values.splice(index, 1)"> - <n-icon :component="Trash" depth="3" size="18" /> - </c-button> - </template> - Delete value - </n-tooltip> + <c-tooltip tooltip="Delete this value"> + <c-button circle variant="text" @click="values.splice(index, 1)"> + <n-icon :component="Trash" depth="3" size="18" /> + </c-button> + </c-tooltip> </div> <c-button @click="addValue"> diff --git a/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue b/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue index 9a4cf1b..5be2329 100644 --- a/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue +++ b/src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue @@ -6,13 +6,9 @@ const { icon, title, action, isActive } = toRefs(props); </script> <template> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button circle variant="text" :type="isActive?.() ? 'primary' : 'default'" @click="action"> - <n-icon :component="icon" /> - </c-button> - </template> - - {{ title }} - </n-tooltip> + <c-tooltip :tooltip="title"> + <c-button circle variant="text" :type="isActive?.() ? 'primary' : 'default'" @click="action"> + <n-icon :component="icon" /> + </c-button> + </c-tooltip> </template> diff --git a/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue b/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue index 7b26a09..6bd881c 100644 --- a/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue +++ b/src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue @@ -61,19 +61,16 @@ const secretValidationRules = [ :validation-rules="secretValidationRules" > <template #suffix> - <n-tooltip trigger="hover"> - <template #trigger> - <c-button circle variant="text" size="small" @click="refreshSecret"> - <icon-mdi-refresh /> - </c-button> - </template> - Generate secret token - </n-tooltip> + <c-tooltip tooltip="Generate a new random secret"> + <c-button circle variant="text" size="small" @click="refreshSecret"> + <icon-mdi-refresh /> + </c-button> + </c-tooltip> </template> </c-input-text> <div> - <TokenDisplay :tokens="tokens" style="margin-top: 2px" /> + <TokenDisplay :tokens="tokens" /> <n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" /> <div style="text-align: center"> diff --git a/src/tools/otp-code-generator-and-validator/token-display.vue b/src/tools/otp-code-generator-and-validator/token-display.vue index 5313b0b..317f083 100644 --- a/src/tools/otp-code-generator-and-validator/token-display.vue +++ b/src/tools/otp-code-generator-and-validator/token-display.vue @@ -11,7 +11,7 @@ const { tokens } = toRefs(props); <template> <div> - <div class="labels" w-full flex items-center> + <div mb-5px w-full flex items-center> <div flex-1 text-left> Previous </div> @@ -22,60 +22,24 @@ const { tokens } = toRefs(props); Next </div> </div> - <n-input-group> - <n-tooltip trigger="hover" placement="bottom"> - <template #trigger> - <c-button important:h-12 data-test-id="previous-otp" @click.prevent="copyPrevious(tokens.previous)"> - {{ tokens.previous }} - </c-button> - </template> - <div>{{ previousCopied ? 'Copied !' : 'Copy previous OTP' }}</div> - </n-tooltip> - <n-tooltip trigger="hover" placement="bottom"> - <template #trigger> - <c-button - data-test-id="current-otp" - class="current-otp" - important:h-12 - @click.prevent="copyCurrent(tokens.current)" - > - {{ tokens.current }} - </c-button> - </template> - <div>{{ currentCopied ? 'Copied !' : 'Copy current OTP' }}</div> - </n-tooltip> - <n-tooltip trigger="hover" placement="bottom"> - <template #trigger> - <c-button important:h-12 data-test-id="next-otp" @click.prevent="copyNext(tokens.next)"> - {{ - tokens.next - }} - </c-button> - </template> - <div>{{ nextCopied ? 'Copied !' : 'Copy next OTP' }}</div> - </n-tooltip> - </n-input-group> + <div flex items-center> + <c-tooltip :tooltip="previousCopied ? 'Copied !' : 'Copy previous OTP'" position="bottom" flex-1> + <c-button data-test-id="previous-otp" w-full important:h-12 important:rounded-r-none important:font-mono @click.prevent="copyPrevious(tokens.previous)"> + {{ tokens.previous }} + </c-button> + </c-tooltip> + <c-tooltip :tooltip="currentCopied ? 'Copied !' : 'Copy current OTP'" position="bottom" flex-1 flex-basis-5xl> + <c-button + data-test-id="current-otp" w-full important:border-x="1px solid gray op-40" important:h-12 important:rounded-0 important:text-22px @click.prevent="copyCurrent(tokens.current)" + > + {{ tokens.current }} + </c-button> + </c-tooltip> + <c-tooltip :tooltip="nextCopied ? 'Copied !' : 'Copy next OTP'" position="bottom" flex-1> + <c-button data-test-id="next-otp" w-full important:h-12 important:rounded-l-none @click.prevent="copyNext(tokens.next)"> + {{ tokens.next }} + </c-button> + </c-tooltip> + </div> </div> </template> - -<style scoped lang="less"> -.current-otp { - font-size: 22px; - flex: 1 0 35% !important; -} - -.n-button { - height: 45px; -} - -.labels { - div { - padding: 0 2px 6px 2px; - line-height: 1.25; - } -} - -.n-input-group > * { - flex: 1 0 0; -} -</style> diff --git a/src/tools/user-agent-parser/user-agent-result-cards.vue b/src/tools/user-agent-parser/user-agent-result-cards.vue index ed4724c..eeea073 100644 --- a/src/tools/user-agent-parser/user-agent-result-cards.vue +++ b/src/tools/user-agent-parser/user-agent-result-cards.vue @@ -25,14 +25,11 @@ const { userAgentInfo, sections } = toRefs(props); <div mt-5 flex gap-2> <span v-for="{ label, getValue } in content" :key="label"> - <n-tooltip v-if="getValue(userAgentInfo)" trigger="hover"> - <template #trigger> - <n-tag type="success" size="large" round :bordered="false"> - {{ getValue(userAgentInfo) }} - </n-tag> - </template> - {{ label }} - </n-tooltip> + <c-tooltip v-if="getValue(userAgentInfo)" :tooltip="label"> + <n-tag type="success" size="large" round :bordered="false"> + {{ getValue(userAgentInfo) }} + </n-tag> + </c-tooltip> </span> </div> <div flex flex-col> diff --git a/src/ui/c-tooltip/c-tooltip.demo.vue b/src/ui/c-tooltip/c-tooltip.demo.vue index d385257..edd1364 100644 --- a/src/ui/c-tooltip/c-tooltip.demo.vue +++ b/src/ui/c-tooltip/c-tooltip.demo.vue @@ -1,3 +1,7 @@ +<script lang="ts" setup> +const positions = ['top', 'bottom', 'left', 'right'] as const; +</script> + <template> <div> <c-tooltip> @@ -14,4 +18,18 @@ Hover me </c-tooltip> </div> + + <div mt-5> + <h2>Tooltip positions</h2> + + <div class="flex flex-wrap gap-4"> + <div v-for="position in positions" :key="position"> + <c-tooltip :position="position" :tooltip="`Tooltip ${position}`"> + <c-button> + {{ position }} + </c-button> + </c-tooltip> + </div> + </div> + </div> </template> diff --git a/src/ui/c-tooltip/c-tooltip.vue b/src/ui/c-tooltip/c-tooltip.vue index 095315f..0f47e71 100644 --- a/src/ui/c-tooltip/c-tooltip.vue +++ b/src/ui/c-tooltip/c-tooltip.vue @@ -1,23 +1,30 @@ <script setup lang="ts"> -const props = withDefaults(defineProps<{ tooltip?: string }>(), { tooltip: '' }); -const { tooltip } = toRefs(props); +const props = withDefaults(defineProps<{ tooltip?: string; position?: 'top' | 'bottom' | 'left' | 'right' }>(), { + tooltip: undefined, + position: 'top', +}); +const { tooltip, position } = toRefs(props); const targetRef = ref(); const isTargetHovered = useElementHover(targetRef); </script> <template> - <div class="relative" inline-block> + <div relative inline-block> <div ref="targetRef"> <slot /> </div> <div v-if="tooltip || $slots.tooltip" - class="absolute bottom-100% left-50% z-10 mb-5px whitespace-nowrap rounded bg-black px-12px py-6px text-sm text-white shadow-lg transition transition transition-duration-0.2s -translate-x-1/2" + class="absolute z-10 whitespace-nowrap rounded bg-black px-12px py-6px text-sm text-white shadow-lg transition transition transition-duration-0.2s" :class="{ 'op-0 scale-0': isTargetHovered === false, 'op-100 scale-100': isTargetHovered, + 'bottom-100% left-50% -translate-x-1/2 mb-5px': position === 'top', + 'top-100% left-50% -translate-x-1/2 mt-5px': position === 'bottom', + 'right-100% top-50% -translate-y-1/2 mr-5px': position === 'left', + 'left-100% top-50% -translate-y-1/2 ml-5px': position === 'right', }" > <slot |