2024-04-01 13:31:16 +02:00
|
|
|
<script>
|
|
|
|
import Portal from "svelte-portal"
|
2024-04-03 10:08:31 +02:00
|
|
|
import { getContext } from "svelte"
|
|
|
|
import Context from "../context"
|
2024-04-01 13:31:16 +02:00
|
|
|
|
|
|
|
export let anchor
|
|
|
|
export let visible = false
|
2024-04-03 00:07:48 +02:00
|
|
|
export let offset = 0;
|
|
|
|
|
2024-04-03 10:08:31 +02:00
|
|
|
$: target = getContext(Context.PopoverRoot) || ".spectrum"
|
|
|
|
|
2024-04-03 00:07:48 +02:00
|
|
|
let hovering = false
|
|
|
|
let wrapper
|
|
|
|
let currentTooltip
|
|
|
|
let previousTooltip
|
2024-04-01 13:31:16 +02:00
|
|
|
|
2024-04-02 17:46:31 +02:00
|
|
|
let initialShow = true;
|
2024-04-02 13:07:11 +02:00
|
|
|
let previousX = 0
|
|
|
|
let previousY = 0
|
|
|
|
let currentX = 0
|
|
|
|
let currentY = 0
|
2024-04-01 13:31:16 +02:00
|
|
|
|
2024-04-02 11:54:20 +02:00
|
|
|
let currentTooltipWidth = 0
|
|
|
|
let currentTooltipHeight = 0
|
|
|
|
|
2024-04-02 17:46:31 +02:00
|
|
|
const handleVisibilityChange = (visible, hovering) => {
|
2024-04-01 13:31:16 +02:00
|
|
|
if (!visible && !hovering) {
|
2024-04-02 17:46:31 +02:00
|
|
|
initialShow = true;
|
|
|
|
}
|
|
|
|
}
|
2024-04-01 13:31:16 +02:00
|
|
|
|
2024-04-04 08:53:30 +02:00
|
|
|
const updatePosition = (anchor, currentTooltip, previousTooltip, wrapper) => {
|
2024-04-02 11:54:20 +02:00
|
|
|
requestAnimationFrame(() => {
|
2024-04-04 08:53:30 +02:00
|
|
|
if (anchor == null || currentTooltip == null || previousTooltip == null || wrapper == null) {
|
2024-04-02 11:54:20 +02:00
|
|
|
return;
|
|
|
|
}
|
2024-04-01 13:31:16 +02:00
|
|
|
|
2024-04-02 11:54:20 +02:00
|
|
|
const rect = anchor.getBoundingClientRect();
|
2024-04-04 08:53:30 +02:00
|
|
|
const previousStyles = window.getComputedStyle(previousTooltip?.firstChild)
|
|
|
|
const currentStyles = window.getComputedStyle(currentTooltip?.firstChild)
|
|
|
|
|
|
|
|
console.log(previousStyles.backgroundColor);
|
|
|
|
console.log(currentStyles.backgroundColor);
|
|
|
|
console.log("")
|
2024-04-01 18:14:18 +02:00
|
|
|
|
2024-04-02 11:54:20 +02:00
|
|
|
currentTooltipWidth = currentTooltip.clientWidth
|
|
|
|
currentTooltipHeight = currentTooltip.clientHeight
|
2024-04-01 18:14:18 +02:00
|
|
|
|
2024-04-02 13:07:11 +02:00
|
|
|
previousX = currentX
|
|
|
|
previousY = currentY
|
2024-04-02 11:54:20 +02:00
|
|
|
|
2024-04-02 13:07:11 +02:00
|
|
|
// - width to align to left side of anchor
|
2024-04-03 00:07:48 +02:00
|
|
|
currentX = rect.x - currentTooltipWidth - offset
|
2024-04-02 13:07:11 +02:00
|
|
|
currentY = rect.y
|
2024-04-03 00:07:48 +02:00
|
|
|
const fadeIn = [{ opacity: "0" }, { opacity: "1" }];
|
|
|
|
const fadeOut = [{ opacity: "1" }, { opacity: "0" }];
|
2024-04-04 09:10:10 +02:00
|
|
|
const color = [{ backgroundColor: previousStyles.backgroundColor }, { backgroundColor: currentStyles.backgroundColor }];
|
2024-04-03 00:07:48 +02:00
|
|
|
|
2024-04-03 11:08:40 +02:00
|
|
|
const fadeInTiming = {
|
2024-04-04 09:10:10 +02:00
|
|
|
duration: 150,
|
|
|
|
delay: 150,
|
2024-04-03 11:08:40 +02:00
|
|
|
iterations: 1,
|
|
|
|
easing: "ease-in",
|
|
|
|
fill: "both"
|
|
|
|
};
|
|
|
|
const fadeOutTiming = {
|
2024-04-04 09:10:10 +02:00
|
|
|
duration: 150,
|
2024-04-03 00:07:48 +02:00
|
|
|
iterations: 1,
|
2024-04-03 08:50:43 +02:00
|
|
|
easing: "ease-in",
|
|
|
|
fill: "forwards"
|
2024-04-03 00:07:48 +02:00
|
|
|
};
|
|
|
|
|
2024-04-04 08:53:30 +02:00
|
|
|
const colorTiming = {
|
|
|
|
duration: 300,
|
|
|
|
iterations: 1,
|
2024-04-04 09:10:10 +02:00
|
|
|
easing: "ease-in"
|
2024-04-04 08:53:30 +02:00
|
|
|
};
|
|
|
|
|
2024-04-03 11:08:40 +02:00
|
|
|
currentTooltip.animate(fadeIn, fadeInTiming);
|
|
|
|
previousTooltip.animate(fadeOut, fadeOutTiming);
|
2024-04-04 08:53:30 +02:00
|
|
|
wrapper.animate(color, colorTiming);
|
2024-04-02 11:54:20 +02:00
|
|
|
|
2024-04-02 17:46:31 +02:00
|
|
|
// Bypass animations if the tooltip has only just been opened
|
|
|
|
if (initialShow) {
|
|
|
|
initialShow = false;
|
2024-04-01 13:31:16 +02:00
|
|
|
|
2024-04-03 08:50:43 +02:00
|
|
|
previousTooltip.style.visibility = "hidden"
|
|
|
|
|
2024-04-02 17:46:31 +02:00
|
|
|
wrapper.style.transition = "none";
|
2024-04-04 08:53:30 +02:00
|
|
|
//wrapper.style.width = `${currentTooltipWidth}px`
|
|
|
|
//wrapper.style.height = `${currentTooltipHeight}px`
|
2024-04-02 17:46:31 +02:00
|
|
|
wrapper.style.top = `${currentY}px`
|
|
|
|
wrapper.style.left = `${currentX}px`
|
2024-04-01 13:31:16 +02:00
|
|
|
|
|
|
|
|
2024-04-02 17:46:31 +02:00
|
|
|
requestAnimationFrame(() => {
|
|
|
|
wrapper.style.removeProperty("transition");
|
|
|
|
})
|
2024-04-03 08:50:43 +02:00
|
|
|
} else {
|
|
|
|
previousTooltip.style.removeProperty("visibility");
|
2024-04-02 17:46:31 +02:00
|
|
|
}
|
|
|
|
})
|
2024-04-01 18:14:18 +02:00
|
|
|
}
|
2024-04-01 13:31:16 +02:00
|
|
|
|
2024-04-04 08:53:30 +02:00
|
|
|
$: updatePosition(anchor, currentTooltip, previousTooltip, wrapper)
|
2024-04-02 17:46:31 +02:00
|
|
|
$: handleVisibilityChange(visible, hovering)
|
2024-04-01 13:31:16 +02:00
|
|
|
|
|
|
|
const handleMouseenter = (e) => {
|
|
|
|
hovering = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const handleMouseleave = (e) => {
|
|
|
|
hovering = false;
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
2024-04-03 10:08:31 +02:00
|
|
|
<Portal {target}>
|
2024-04-01 13:31:16 +02:00
|
|
|
<div
|
2024-04-02 17:46:31 +02:00
|
|
|
bind:this={wrapper}
|
2024-04-01 13:31:16 +02:00
|
|
|
on:mouseenter={handleMouseenter}
|
|
|
|
on:mouseleave={handleMouseleave}
|
2024-04-02 13:07:11 +02:00
|
|
|
style:left={`${currentX}px`}
|
|
|
|
style:top={`${currentY}px`}
|
2024-04-04 08:53:30 +02:00
|
|
|
style:width={`${currentTooltipWidth}px`}
|
|
|
|
style:height={`${currentTooltipHeight}px`}
|
2024-04-01 13:31:16 +02:00
|
|
|
class="tooltip"
|
|
|
|
class:visible={visible || hovering}
|
|
|
|
>
|
2024-04-02 17:46:31 +02:00
|
|
|
<!-- absolutely position element with the opposite positioning, so that the tooltip elements can be positioned
|
|
|
|
using the same values as the root wrapper, while still being occluded by it.
|
|
|
|
-->
|
2024-04-04 08:53:30 +02:00
|
|
|
<div class="screenSizeAbsoluteWrapper"
|
|
|
|
style:left={`${-currentX}px`}
|
|
|
|
style:top={`${-currentY}px`}
|
|
|
|
>
|
2024-04-02 11:54:20 +02:00
|
|
|
<div
|
|
|
|
bind:this={currentTooltip}
|
|
|
|
class="currentContent"
|
2024-04-04 08:53:30 +02:00
|
|
|
style:left={`${currentX}px`}
|
|
|
|
style:top={`${currentY}px`}
|
2024-04-02 11:54:20 +02:00
|
|
|
>
|
|
|
|
<slot />
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
class="previousContent"
|
2024-04-02 13:07:11 +02:00
|
|
|
style:left={`${previousX}px`}
|
|
|
|
style:top={`${previousY}px`}
|
2024-04-03 00:07:48 +02:00
|
|
|
bind:this={previousTooltip}
|
2024-04-02 11:54:20 +02:00
|
|
|
>
|
|
|
|
<slot name="previous"/>
|
|
|
|
</div>
|
|
|
|
</div>
|
2024-04-01 13:31:16 +02:00
|
|
|
</div>
|
|
|
|
</Portal>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
.tooltip {
|
|
|
|
position: absolute;
|
|
|
|
z-index: 9999;
|
|
|
|
pointer-events: none;
|
2024-04-02 17:46:31 +02:00
|
|
|
background-color: var(--spectrum-global-color-gray-200);
|
2024-04-03 00:07:48 +02:00
|
|
|
border-radius: 5px;
|
|
|
|
box-sizing: border-box;
|
2024-04-02 11:54:20 +02:00
|
|
|
opacity: 0;
|
|
|
|
overflow: hidden;
|
2024-04-04 08:53:30 +02:00
|
|
|
|
2024-04-02 13:07:11 +02:00
|
|
|
transition: width 300ms ease-in, height 300ms ease-in, top 300ms ease-in, left 300ms ease-in;
|
2024-04-01 13:31:16 +02:00
|
|
|
}
|
|
|
|
|
2024-04-02 17:46:31 +02:00
|
|
|
.screenSizeAbsoluteWrapper {
|
2024-04-04 08:53:30 +02:00
|
|
|
width: 200vw;
|
|
|
|
height: 200vh;
|
2024-04-02 17:46:31 +02:00
|
|
|
position: absolute;
|
|
|
|
transition: top 300ms ease-in, left 300ms ease-in;
|
|
|
|
}
|
|
|
|
|
2024-04-01 13:31:16 +02:00
|
|
|
.visible {
|
|
|
|
opacity: 1;
|
|
|
|
pointer-events: auto;
|
|
|
|
}
|
2024-04-02 11:54:20 +02:00
|
|
|
|
|
|
|
.currentContent {
|
|
|
|
position: absolute;
|
2024-04-03 00:07:48 +02:00
|
|
|
z-index: 10001;
|
2024-04-02 11:54:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.previousContent {
|
|
|
|
position: absolute;
|
|
|
|
z-index: 10000;
|
|
|
|
}
|
2024-04-01 13:31:16 +02:00
|
|
|
</style>
|