Skip to content

Commit

Permalink
UI-287: Tooltip > Improvement (#300)
Browse files Browse the repository at this point in the history
  • Loading branch information
mosarabi committed Mar 13, 2024
1 parent 72a422d commit dd743c9
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 172 deletions.
90 changes: 8 additions & 82 deletions packages/vue3/src/_dev/App.vue
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
<script setup lang="ts">
import 'uno.css';
import '../assets/main.css';
import { ref } from 'vue';
import { Drawer, PrimaryButton } from '~/components';
const showDrawer = ref(false);
import { SecondaryButton, Tooltip } from '~/components';
</script>

<template>
<Drawer v-model:visible="showDrawer">
<div class="drawer-content">
<img class="banner" src="https://developer.youcan.shop/banner.webp" alt="">
<div class="content">
<h1 class="title">
Get started with YouCan UI
</h1>
<p class="description">
Create your app using our UI library to insure seemless integration and user experience
</p>
<ul class="features">
<li>👔 Tailored for you</li>
<li>🏢 Internal and External Use</li>
<li>⚙️ Vue.js and CSS</li>
</ul>
</div>
<PrimaryButton class="cta" @click="showDrawer = false">
<span>Get Started</span>
</PrimaryButton>
</div>
</Drawer>
<div class="container">
<PrimaryButton @click="showDrawer = true">
<span>Open Drawer</span>
</PrimaryButton>
<Tooltip label="Add to favorites" position="top">
<SecondaryButton>
Hover
</SecondaryButton>
</Tooltip>
</div>
</template>

Expand All @@ -40,60 +19,7 @@ const showDrawer = ref(false);
display: flex;
align-items: center;
justify-content: center;
min-width: 50%;
padding: 16px;
}
.drawer-content {
display: flex;
flex-direction: column;
max-width: 992px;
height: 100%;
}
.drawer-content .banner {
width: 100%;
height: 60%;
border: 1px solid var(--gray-200);
border-radius: 12px;
object-fit: cover;
}
.drawer-content .content {
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
}
.content .title {
text-align: center;
}
.content .description {
margin: 0;
color: var(--gray-500);
text-align: center;
}
.content .features {
display: grid;
grid-template-columns: repeat(3, 1fr);
margin-top: 50px;
list-style: none;
gap: 16px;
}
.content .features li {
margin: 0;
padding: 16px;
border: 1px solid var(--gray-200);
border-radius: 8px;
}
.drawer-content .cta {
width: 100%;
min-height: 55px;
width: 100vw;
height: 100vh;
}
</style>
2 changes: 1 addition & 1 deletion packages/vue3/src/components/DropdownMenu/DropdownMenu.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, ref } from 'vue';
import { onClickOutside } from '@vueuse/core';
import { setPosition } from '../Tooltip/utils';
import { setPosition } from '~/helpers';
import type { DropdownMenuProps } from '~/types';
const props = withDefaults(defineProps<DropdownMenuProps>(), { position: 'bottom' });
Expand Down
2 changes: 1 addition & 1 deletion packages/vue3/src/components/Popover/Popover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { onClickOutside } from '@vueuse/core';
import { onMounted, onUnmounted, ref, useSlots } from 'vue';
import type { PopoverProps } from '~/types';
import { setPosition } from '~/components/Tooltip/utils';
import { setPosition } from '~/helpers';
const props = withDefaults(defineProps<PopoverProps>(), {
position: 'left',
objectFit: 'cover',
Expand Down
72 changes: 35 additions & 37 deletions packages/vue3/src/components/Tooltip/Tooltip.vue
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import { setPosition } from './utils';
import { setPosition } from '~/helpers';
import type { TooltipProps } from '~/types';
const props = withDefaults(defineProps<TooltipProps>(), {
position: 'top',
});
const tooltipTrigger = ref<HTMLElement >();
const triggeredElement = ref<HTMLElement >();
const tooltip = ref<HTMLElement>();
const tooltipPosition = () => {
if (tooltipTrigger.value && tooltip.value) {
const { left, top } = setPosition(tooltipTrigger.value, tooltip.value, props.position);
tooltipTrigger.value?.setAttribute('style', `top:${top}px;left:${left}px`);
if (triggeredElement.value && tooltip.value) {
const { left, top } = setPosition(triggeredElement.value, tooltip.value, props.position);
triggeredElement.value?.setAttribute('style', `top:${top}px;left:${left}px`);
}
};
const handleScroll = () => {
if (!tooltipTrigger.value?.classList.contains('tooltip-trigger-hide')) {
tooltipTrigger.value?.classList.add('tooltip-trigger-hide');
if (!triggeredElement.value?.classList.contains('triggered-element-hide')) {
triggeredElement.value?.classList.add('triggered-element-hide');
}
};
const handleMouseEnter = () => {
tooltipTrigger.value?.classList.remove('tooltip-trigger-hide');
triggeredElement.value?.classList.remove('triggered-element-hide');
tooltipPosition();
};
onMounted(() => {
tooltipTrigger.value?.classList.remove('tooltip-trigger-hide');
triggeredElement.value?.classList.remove('triggered-element-hide');
tooltipPosition();
window.addEventListener('scroll', handleScroll);
});
Expand All @@ -40,45 +40,43 @@ onUnmounted(() => {

<template>
<div v-if="label" ref="tooltip" class="tooltip" :class="position" @mouseenter="handleMouseEnter">
<span ref="tooltipTrigger" class="tooltip-trigger">{{ label }}</span>
<span ref="triggeredElement" class="triggered-element">{{ label }}</span>

<slot />
</div>
<slot v-else />
</template>

<style scoped lang="scss">
<style scoped>
.tooltip {
display: inline-block;
position: relative;
}
.tooltip-trigger {
visibility: hidden;
position: fixed;
z-index: 9999999999;
width: max-content;
max-width: 200px;
padding: 8px 12px;
transition: opacity 0.3s ease-in-out;
transition-delay: 0.1s;
border-radius: 4px;
opacity: 0;
background-color: var(--base-black);
color: var(--base-white);
font: var(--text-xs-regular);
.tooltip .triggered-element {
visibility: hidden;
position: fixed;
z-index: 9999999999;
width: max-content;
max-width: 200px;
padding: 8px 12px;
transition: opacity 0.3s ease-in-out;
transition-delay: 0.1s;
border-radius: 4px;
opacity: 0;
background-color: var(--base-black);
color: var(--base-white);
font: var(--text-xs-regular);
}
&-hide {
visibility: hidden !important;
opacity: 0 !important;
}
}
.tooltip .triggered-element-hide {
visibility: hidden !important;
opacity: 0 !important;
}
&:hover {
.tooltip-trigger {
visibility: visible;
transition-delay: 0.2s;
opacity: 1;
}
}
.tooltip:hover .triggered-element {
visibility: visible;
transition-delay: 0.2s;
opacity: 1;
}
</style>
50 changes: 0 additions & 50 deletions packages/vue3/src/components/Tooltip/utils.ts

This file was deleted.

56 changes: 55 additions & 1 deletion packages/vue3/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,58 @@ const toast = {
},
};

export { toast };
const setPosition = (triggeredElement: HTMLElement, trigger: HTMLElement, position: string, gap = 6) => {
const xy = { top: 0, left: 0, currentPosition: position };
if (triggeredElement && trigger) {
const offset = trigger?.getBoundingClientRect();
const triggerHeight = triggeredElement?.clientHeight || 0;
const triggerWidth = triggeredElement?.clientWidth || 0;
const tooltipHeight = trigger?.clientHeight || 0;
const tooltipWidth = trigger?.clientWidth || 0;
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;

const disableHorizontal = triggerWidth + tooltipWidth + gap > windowWidth;
if (offset) {
// set position top as default
let centerTooltip = (triggerWidth - tooltipWidth) / 2;
let top = offset.top - (triggerHeight + gap);
let left = offset.left - centerTooltip;

left = left < 0 ? gap / 2 : left + triggerWidth > windowWidth ? windowWidth - (triggerWidth + gap / 2) : left;

const bottom = offset.bottom + triggerHeight + gap;
const right = offset.right + triggerWidth + gap;
xy.currentPosition = 'top';

if ((position === 'left' || position === 'right') && !disableHorizontal) {
centerTooltip = (triggerHeight - tooltipHeight) / 2;
top = offset.top - centerTooltip;
top = top < 0 ? gap / 2 : top + triggerHeight > windowHeight ? windowHeight - (triggerHeight + gap / 2) : top;
left = offset.left - (triggerWidth + gap);
xy.currentPosition = 'left';
if ((position === 'right' || left < 0) && right < windowWidth) {
left = offset.left + (tooltipWidth + gap);
xy.currentPosition = 'right';
}
}
else if ((position === 'bottom' || top < 0) && bottom < windowHeight) {
top = offset.top + (tooltipHeight + gap);
xy.currentPosition = 'bottom';
}
xy.top = top;
xy.left = left;

return xy;
}

return xy;
}

return xy;
};

export {
toast,
setPosition,
};

0 comments on commit dd743c9

Please sign in to comment.