refactor(ui): switched naive tooltip components to custom ones (#661)

This commit is contained in:
Corentin THOMASSET 2023-10-14 18:24:54 +02:00 committed by GitHub
parent 2d2dffb14a
commit 025f556023
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 177 additions and 243 deletions

2
components.d.ts vendored
View file

@ -90,6 +90,8 @@ declare module '@vue/runtime-core' {
IconMdiDownload: typeof import('~icons/mdi/download')['default'] IconMdiDownload: typeof import('~icons/mdi/download')['default']
IconMdiEye: typeof import('~icons/mdi/eye')['default'] IconMdiEye: typeof import('~icons/mdi/eye')['default']
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['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'] IconMdiPause: typeof import('~icons/mdi/pause')['default']
IconMdiPlay: typeof import('~icons/mdi/play')['default'] IconMdiPlay: typeof import('~icons/mdi/play')['default']
IconMdiRecord: typeof import('~icons/mdi/record')['default'] IconMdiRecord: typeof import('~icons/mdi/record')['default']

23
pnpm-lock.yaml generated
View file

@ -3272,7 +3272,7 @@ packages:
dependencies: dependencies:
'@unhead/dom': 0.5.1 '@unhead/dom': 0.5.1
'@unhead/schema': 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 unhead: 0.5.1
vue: 3.3.4 vue: 3.3.4
transitivePeerDependencies: transitivePeerDependencies:
@ -3854,10 +3854,10 @@ packages:
- vue - vue
dev: false dev: false
/@vueuse/shared@10.4.1(vue@3.3.4): /@vueuse/shared@10.5.0(vue@3.3.4):
resolution: {integrity: sha512-vz5hbAM4qA0lDKmcr2y3pPdU+2EVw/yzfRsBdu+6+USGa4PxqSQRYIUC9/NcT06y+ZgaTsyURw2I9qOFaaXHAg==} resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==}
dependencies: dependencies:
vue-demi: 0.14.5(vue@3.3.4) vue-demi: 0.14.6(vue@3.3.4)
transitivePeerDependencies: transitivePeerDependencies:
- '@vue/composition-api' - '@vue/composition-api'
- vue - vue
@ -8790,6 +8790,21 @@ packages:
vue: 3.3.4 vue: 3.3.4
dev: false 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): /vue-eslint-parser@9.3.1(eslint@8.47.0):
resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
engines: {node: ^14.17.0 || >=16.0.0} engines: {node: ^14.17.0 || >=16.0.0}

View file

@ -1,6 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { FavoriteFilled } from '@vicons/material';
import { useToolStore } from '@/tools/tools.store'; import { useToolStore } from '@/tools/tools.store';
import type { Tool } from '@/tools/tools.types'; import type { Tool } from '@/tools/tools.types';
@ -26,18 +24,15 @@ function toggleFavorite(event: MouseEvent) {
</script> </script>
<template> <template>
<n-tooltip trigger="hover"> <c-tooltip :tooltip="isFavorite ? 'Remove from favorites' : 'Add to favorites' ">
<template #trigger> <c-button
<c-button variant="text"
variant="text" circle
circle :type="buttonType"
:type="buttonType" :style="{ opacity: isFavorite ? 1 : 0.2 }"
:style="{ opacity: isFavorite ? 1 : 0.2 }" @click="toggleFavorite"
@click="toggleFavorite" >
> <icon-mdi-heart />
<n-icon :component="FavoriteFilled" /> </c-button>
</c-button> </c-tooltip>
</template>
{{ isFavorite ? 'Remove from favorites' : 'Add to favorites' }}
</n-tooltip>
</template> </template>

View file

@ -13,14 +13,11 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : 'Copy to cli
<template> <template>
<c-input-text v-model:value="value"> <c-input-text v-model:value="value">
<template #suffix> <template #suffix>
<n-tooltip trigger="hover"> <c-tooltip :tooltip="tooltipText">
<template #trigger> <c-button circle variant="text" size="small" @click="copy()">
<c-button circle variant="text" size="small" @click="copy()"> <icon-mdi-content-copy />
<icon-mdi-content-copy /> </c-button>
</c-button> </c-tooltip>
</template>
{{ tooltipText }}
</n-tooltip>
</template> </template>
</c-input-text> </c-input-text>
</template> </template>

View file

@ -7,56 +7,43 @@ const { isDarkTheme } = toRefs(styleStore);
</script> </script>
<template> <template>
<n-tooltip trigger="hover"> <c-tooltip tooltip="Github repository" position="bottom">
<template #trigger> <c-button
<c-button circle
circle variant="text"
variant="text" href="https://github.com/CorentinTh/it-tools"
href="https://github.com/CorentinTh/it-tools" target="_blank"
target="_blank" rel="noopener noreferrer"
rel="noopener noreferrer" aria-label="IT-Tools' GitHub repository"
aria-label="IT-Tools' GitHub repository" >
> <n-icon size="25" :component="BrandGithub" />
<n-icon size="25" :component="BrandGithub" /> </c-button>
</c-button> </c-tooltip>
</template>
Github repository
</n-tooltip>
<n-tooltip trigger="hover"> <c-tooltip tooltip="Twitter account" position="bottom">
<template #trigger> <c-button
<c-button circle
circle variant="text"
variant="text" href="https://twitter.com/ittoolsdottech"
href="https://twitter.com/ittoolsdottech" rel="noopener"
rel="noopener" target="_blank"
target="_blank" aria-label="IT Tools' Twitter account"
aria-label="IT Tools' Twitter account" >
> <n-icon size="25" :component="BrandTwitter" />
<n-icon size="25" :component="BrandTwitter" /> </c-button>
</c-button> </c-tooltip>
</template>
IT Tools' Twitter account
</n-tooltip>
<n-tooltip trigger="hover"> <c-tooltip tooltip="About IT-Tools" position="bottom">
<template #trigger> <c-button circle variant="text" to="/about" aria-label="About">
<c-button circle variant="text" to="/about" aria-label="About"> <n-icon size="25" :component="InfoCircle" />
<n-icon size="25" :component="InfoCircle" /> </c-button>
</c-button> </c-tooltip>
</template> <c-tooltip :tooltip="isDarkTheme ? 'Light mode' : 'Dark mode'" position="bottom">
About <c-button circle variant="text" aria-label="Toggle dark/light mode" @click="() => styleStore.toggleDark()">
</n-tooltip> <n-icon v-if="isDarkTheme" size="25" :component="Sun" />
<n-tooltip trigger="hover"> <n-icon v-else size="25" :component="Moon" />
<template #trigger> </c-button>
<c-button circle variant="text" aria-label="Toggle dark/light mode" @click="() => styleStore.toggleDark()"> </c-tooltip>
<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>
</template> </template>
<style lang="less" scoped> <style lang="less" scoped>

View file

@ -11,17 +11,7 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : initialText)
</script> </script>
<template> <template>
<n-tooltip trigger="hover"> <c-tooltip :tooltip="tooltipText">
<template #trigger> <span cursor-pointer font-mono @click="copy()">{{ value }}</span>
<span class="value" @click="copy()">{{ value }}</span> </c-tooltip>
</template>
{{ tooltipText }}
</n-tooltip>
</template> </template>
<style scoped lang="less">
.value {
cursor: pointer;
font-family: monospace;
}
</style>

View file

@ -40,7 +40,7 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
<template> <template>
<div style="overflow-x: hidden; width: 100%"> <div style="overflow-x: hidden; width: 100%">
<c-card class="result-card"> <c-card relative>
<n-scrollbar <n-scrollbar
x-scrollable x-scrollable
trigger="none" 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-code :code="value" :language="language" :trim="false" data-test-id="area-content" />
</n-config-provider> </n-config-provider>
</n-scrollbar> </n-scrollbar>
<n-tooltip v-if="value" trigger="hover"> <div absolute right-10px top-10px>
<template #trigger> <c-tooltip v-if="value" :tooltip="tooltipText" position="left">
<div class="copy-button" :class="[copyPlacement]"> <c-button circle important:h-10 important:w-10 @click="copy()">
<c-button circle important:h-10 important:w-10 @click="copy()"> <n-icon size="22" :component="Copy" />
<n-icon size="22" :component="Copy" /> </c-button>
</c-button> </c-tooltip>
</div> </div>
</template>
<span>{{ tooltipText }}</span>
</n-tooltip>
</c-card> </c-card>
<div v-if="copyPlacement === 'outside'" mt-4 flex justify-center> <div v-if="copyPlacement === 'outside'" mt-4 flex justify-center>
<c-button @click="copy()"> <c-button @click="copy()">
@ -74,25 +71,4 @@ const tooltipText = computed(() => isJustCopied.value ? 'Copied!' : copyMessage.
padding-bottom: 10px; padding-bottom: 10px;
margin-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> </style>

View file

@ -94,18 +94,17 @@ const tools = computed<ToolCategory[]>(() => [
<NIcon size="25" :component="Menu2" /> <NIcon size="25" :component="Menu2" />
</c-button> </c-button>
<n-tooltip trigger="hover"> <c-tooltip tooltip="Home" position="bottom">
<template #trigger> <c-button to="/" circle variant="text" aria-label="Home">
<c-button to="/" circle variant="text" aria-label="Home"> <NIcon size="25" :component="Home2" />
<NIcon size="25" :component="Home2" /> </c-button>
</c-button> </c-tooltip>
</template>
Home
</n-tooltip>
<c-button v-if="config.app.env === 'development'" to="/c-lib" circle variant="text" aria-label="UI Lib"> <c-tooltip tooltip="UI Lib" position="bottom">
<icon-mdi:brush-variant text-20px /> <c-button v-if="config.app.env === 'development'" to="/c-lib" circle variant="text" aria-label="UI Lib">
</c-button> <icon-mdi:brush-variant text-20px />
</c-button>
</c-tooltip>
<command-palette /> <command-palette />
@ -113,23 +112,20 @@ const tools = computed<ToolCategory[]>(() => [
<NavbarButtons v-if="!styleStore.isSmallScreen" /> <NavbarButtons v-if="!styleStore.isSmallScreen" />
</div> </div>
<n-tooltip trigger="hover"> <c-tooltip position="bottom" tooltip="Support IT Tools development">
<template #trigger> <c-button
<c-button round
round href="https://www.buymeacoffee.com/cthmsst"
href="https://www.buymeacoffee.com/cthmsst" rel="noopener"
rel="noopener" target="_blank"
target="_blank" class="support-button"
class="support-button" :bordered="false"
:bordered="false" @click="() => tracker.trackEvent({ eventName: 'Support button clicked' })"
@click="() => tracker.trackEvent({ eventName: 'Support button clicked' })" >
> Buy me a coffee
Buy me a coffee <NIcon v-if="!styleStore.isSmallScreen" :component="Heart" ml-2 />
<NIcon v-if="!styleStore.isSmallScreen" :component="Heart" ml-2 /> </c-button>
</c-button> </c-tooltip>
</template>
Support IT Tools development !
</n-tooltip>
</div> </div>
<slot /> <slot />
</template> </template>

View file

@ -39,14 +39,11 @@ function onInputEnter(index: number) {
autofocus autofocus
@keydown.enter="onInputEnter(index)" @keydown.enter="onInputEnter(index)"
/> />
<n-tooltip> <c-tooltip tooltip="Delete this value">
<template #trigger> <c-button circle variant="text" @click="values.splice(index, 1)">
<c-button circle variant="text" @click="values.splice(index, 1)"> <n-icon :component="Trash" depth="3" size="18" />
<n-icon :component="Trash" depth="3" size="18" /> </c-button>
</c-button> </c-tooltip>
</template>
Delete value
</n-tooltip>
</div> </div>
<c-button @click="addValue"> <c-button @click="addValue">

View file

@ -6,13 +6,9 @@ const { icon, title, action, isActive } = toRefs(props);
</script> </script>
<template> <template>
<n-tooltip trigger="hover"> <c-tooltip :tooltip="title">
<template #trigger> <c-button circle variant="text" :type="isActive?.() ? 'primary' : 'default'" @click="action">
<c-button circle variant="text" :type="isActive?.() ? 'primary' : 'default'" @click="action"> <n-icon :component="icon" />
<n-icon :component="icon" /> </c-button>
</c-button> </c-tooltip>
</template>
{{ title }}
</n-tooltip>
</template> </template>

View file

@ -61,19 +61,16 @@ const secretValidationRules = [
:validation-rules="secretValidationRules" :validation-rules="secretValidationRules"
> >
<template #suffix> <template #suffix>
<n-tooltip trigger="hover"> <c-tooltip tooltip="Generate a new random secret">
<template #trigger> <c-button circle variant="text" size="small" @click="refreshSecret">
<c-button circle variant="text" size="small" @click="refreshSecret"> <icon-mdi-refresh />
<icon-mdi-refresh /> </c-button>
</c-button> </c-tooltip>
</template>
Generate secret token
</n-tooltip>
</template> </template>
</c-input-text> </c-input-text>
<div> <div>
<TokenDisplay :tokens="tokens" style="margin-top: 2px" /> <TokenDisplay :tokens="tokens" />
<n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" /> <n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" />
<div style="text-align: center"> <div style="text-align: center">

View file

@ -11,7 +11,7 @@ const { tokens } = toRefs(props);
<template> <template>
<div> <div>
<div class="labels" w-full flex items-center> <div mb-5px w-full flex items-center>
<div flex-1 text-left> <div flex-1 text-left>
Previous Previous
</div> </div>
@ -22,60 +22,24 @@ const { tokens } = toRefs(props);
Next Next
</div> </div>
</div> </div>
<n-input-group> <div flex items-center>
<n-tooltip trigger="hover" placement="bottom"> <c-tooltip :tooltip="previousCopied ? 'Copied !' : 'Copy previous OTP'" position="bottom" flex-1>
<template #trigger> <c-button data-test-id="previous-otp" w-full important:h-12 important:rounded-r-none important:font-mono @click.prevent="copyPrevious(tokens.previous)">
<c-button important:h-12 data-test-id="previous-otp" @click.prevent="copyPrevious(tokens.previous)"> {{ tokens.previous }}
{{ tokens.previous }} </c-button>
</c-button> </c-tooltip>
</template> <c-tooltip :tooltip="currentCopied ? 'Copied !' : 'Copy current OTP'" position="bottom" flex-1 flex-basis-5xl>
<div>{{ previousCopied ? 'Copied !' : 'Copy previous OTP' }}</div> <c-button
</n-tooltip> 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)"
<n-tooltip trigger="hover" placement="bottom"> >
<template #trigger> {{ tokens.current }}
<c-button </c-button>
data-test-id="current-otp" </c-tooltip>
class="current-otp" <c-tooltip :tooltip="nextCopied ? 'Copied !' : 'Copy next OTP'" position="bottom" flex-1>
important:h-12 <c-button data-test-id="next-otp" w-full important:h-12 important:rounded-l-none @click.prevent="copyNext(tokens.next)">
@click.prevent="copyCurrent(tokens.current)" {{ tokens.next }}
> </c-button>
{{ tokens.current }} </c-tooltip>
</c-button> </div>
</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> </div>
</template> </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>

View file

@ -25,14 +25,11 @@ const { userAgentInfo, sections } = toRefs(props);
<div mt-5 flex gap-2> <div mt-5 flex gap-2>
<span v-for="{ label, getValue } in content" :key="label"> <span v-for="{ label, getValue } in content" :key="label">
<n-tooltip v-if="getValue(userAgentInfo)" trigger="hover"> <c-tooltip v-if="getValue(userAgentInfo)" :tooltip="label">
<template #trigger> <n-tag type="success" size="large" round :bordered="false">
<n-tag type="success" size="large" round :bordered="false"> {{ getValue(userAgentInfo) }}
{{ getValue(userAgentInfo) }} </n-tag>
</n-tag> </c-tooltip>
</template>
{{ label }}
</n-tooltip>
</span> </span>
</div> </div>
<div flex flex-col> <div flex flex-col>

View file

@ -1,3 +1,7 @@
<script lang="ts" setup>
const positions = ['top', 'bottom', 'left', 'right'] as const;
</script>
<template> <template>
<div> <div>
<c-tooltip> <c-tooltip>
@ -14,4 +18,18 @@
Hover me Hover me
</c-tooltip> </c-tooltip>
</div> </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> </template>

View file

@ -1,23 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
const props = withDefaults(defineProps<{ tooltip?: string }>(), { tooltip: '' }); const props = withDefaults(defineProps<{ tooltip?: string; position?: 'top' | 'bottom' | 'left' | 'right' }>(), {
const { tooltip } = toRefs(props); tooltip: undefined,
position: 'top',
});
const { tooltip, position } = toRefs(props);
const targetRef = ref(); const targetRef = ref();
const isTargetHovered = useElementHover(targetRef); const isTargetHovered = useElementHover(targetRef);
</script> </script>
<template> <template>
<div class="relative" inline-block> <div relative inline-block>
<div ref="targetRef"> <div ref="targetRef">
<slot /> <slot />
</div> </div>
<div <div
v-if="tooltip || $slots.tooltip" 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="{ :class="{
'op-0 scale-0': isTargetHovered === false, 'op-0 scale-0': isTargetHovered === false,
'op-100 scale-100': isTargetHovered, '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 <slot