Fixed mobile tooltip positioning on mobile
This commit is contained in:
@@ -4,12 +4,16 @@
|
|||||||
let showTooltip = false;
|
let showTooltip = false;
|
||||||
let touchTimer: number;
|
let touchTimer: number;
|
||||||
let isLongPress = false;
|
let isLongPress = false;
|
||||||
|
let tooltipElement: HTMLSpanElement;
|
||||||
|
let wrapperElement: HTMLSpanElement;
|
||||||
|
let tooltipPosition = { align: 'center', vertical: 'top' };
|
||||||
|
|
||||||
function handleTouchStart() {
|
function handleTouchStart() {
|
||||||
isLongPress = false;
|
isLongPress = false;
|
||||||
touchTimer = window.setTimeout(() => {
|
touchTimer = window.setTimeout(() => {
|
||||||
showTooltip = true;
|
showTooltip = true;
|
||||||
isLongPress = true;
|
isLongPress = true;
|
||||||
|
updateTooltipPosition();
|
||||||
}, 500); // Show after 500ms press
|
}, 500); // Show after 500ms press
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +33,7 @@
|
|||||||
showTooltip = !showTooltip;
|
showTooltip = !showTooltip;
|
||||||
// Auto-hide after 3 seconds
|
// Auto-hide after 3 seconds
|
||||||
if (showTooltip) {
|
if (showTooltip) {
|
||||||
|
updateTooltipPosition();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showTooltip = false;
|
showTooltip = false;
|
||||||
}, 3000);
|
}, 3000);
|
||||||
@@ -39,14 +44,43 @@
|
|||||||
|
|
||||||
function handleMouseEnter() {
|
function handleMouseEnter() {
|
||||||
showTooltip = true;
|
showTooltip = true;
|
||||||
|
// Use requestAnimationFrame to ensure DOM is updated before positioning
|
||||||
|
requestAnimationFrame(() => updateTooltipPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMouseLeave() {
|
function handleMouseLeave() {
|
||||||
showTooltip = false;
|
showTooltip = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTooltipPosition() {
|
||||||
|
if (!tooltipElement || !wrapperElement) return;
|
||||||
|
|
||||||
|
const tooltipRect = tooltipElement.getBoundingClientRect();
|
||||||
|
const wrapperRect = wrapperElement.getBoundingClientRect();
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
const padding = 10; // Minimum padding from screen edge
|
||||||
|
|
||||||
|
// Check horizontal overflow
|
||||||
|
let align = 'center';
|
||||||
|
if (tooltipRect.left < padding) {
|
||||||
|
align = 'left';
|
||||||
|
} else if (tooltipRect.right > viewportWidth - padding) {
|
||||||
|
align = 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check vertical overflow (if tooltip would go above viewport)
|
||||||
|
let vertical = 'top';
|
||||||
|
if (tooltipRect.top < padding) {
|
||||||
|
vertical = 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltipPosition = { align, vertical };
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
|
bind:this={wrapperElement}
|
||||||
class="tooltip-wrapper"
|
class="tooltip-wrapper"
|
||||||
on:mouseenter={handleMouseEnter}
|
on:mouseenter={handleMouseEnter}
|
||||||
on:mouseleave={handleMouseLeave}
|
on:mouseleave={handleMouseLeave}
|
||||||
@@ -61,7 +95,16 @@
|
|||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
{#if showTooltip}
|
{#if showTooltip}
|
||||||
<span class="tooltip-text" role="status" aria-live="polite">
|
<span
|
||||||
|
bind:this={tooltipElement}
|
||||||
|
class="tooltip-text"
|
||||||
|
class:align-left={tooltipPosition.align === 'left'}
|
||||||
|
class:align-right={tooltipPosition.align === 'right'}
|
||||||
|
class:align-center={tooltipPosition.align === 'center'}
|
||||||
|
class:position-bottom={tooltipPosition.vertical === 'bottom'}
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
{text}
|
{text}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -79,8 +122,6 @@
|
|||||||
.tooltip-text {
|
.tooltip-text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 125%;
|
bottom: 125%;
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
background-color: var(--bg-color);
|
background-color: var(--bg-color);
|
||||||
color: var(--fg-color);
|
color: var(--fg-color);
|
||||||
border: 2px solid var(--border-color);
|
border: 2px solid var(--border-color);
|
||||||
@@ -94,13 +135,37 @@
|
|||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Horizontal alignment variants */
|
||||||
|
.tooltip-text.align-center {
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-text.align-left {
|
||||||
|
left: 0;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-text.align-right {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Vertical position variant (when tooltip would overflow top) */
|
||||||
|
.tooltip-text.position-bottom {
|
||||||
|
bottom: auto;
|
||||||
|
top: 125%;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.tooltip-text {
|
.tooltip-text {
|
||||||
box-shadow: 0 2px 8px rgba(255, 255, 255, 0.2);
|
box-shadow: 0 2px 8px rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-text::after {
|
/* Arrow positioning for center-aligned tooltip */
|
||||||
|
.tooltip-text.align-center::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
@@ -111,9 +176,49 @@
|
|||||||
border-color: var(--border-color) transparent transparent transparent;
|
border-color: var(--border-color) transparent transparent transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tooltip-text.align-center.position-bottom::after {
|
||||||
|
top: auto;
|
||||||
|
bottom: 100%;
|
||||||
|
border-color: transparent transparent var(--border-color) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Arrow positioning for left-aligned tooltip */
|
||||||
|
.tooltip-text.align-left::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 1rem;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--border-color) transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-text.align-left.position-bottom::after {
|
||||||
|
top: auto;
|
||||||
|
bottom: 100%;
|
||||||
|
border-color: transparent transparent var(--border-color) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Arrow positioning for right-aligned tooltip */
|
||||||
|
.tooltip-text.align-right::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 1rem;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--border-color) transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-text.align-right.position-bottom::after {
|
||||||
|
top: auto;
|
||||||
|
bottom: 100%;
|
||||||
|
border-color: transparent transparent var(--border-color) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.tooltip-text {
|
.tooltip-text {
|
||||||
max-width: 250px;
|
max-width: calc(100vw - 20px);
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user