budibase/packages/bbui/src/Tooltip/Context.svelte

211 lines
5.4 KiB
Svelte
Raw Normal View History

2024-04-01 13:31:16 +02:00
<script>
import Portal from "svelte-portal"
2024-04-01 18:14:18 +02:00
import { fade } from 'svelte/transition';
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
export let currentTooltip
export let previousTooltip
2024-04-01 13:31:16 +02:00
export let anchor
export let visible = false
export let hovering = false
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
let previousTooltipWidth = 0
let previousTooltipHeight = 0
2024-04-01 13:31:16 +02:00
const updatePositionOnVisibilityChange = (visible, hovering) => {
if (!visible && !hovering) {
2024-04-02 13:07:11 +02:00
previousX = 0;
previousY = 0;
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
previousTooltipWidth = 0
previousTooltipHeight = 0
} }
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
const updatePosition = (anchor, currentTooltip, previousTooltip) => {
requestAnimationFrame(() => {
if (anchor == null || currentTooltip == null || previousTooltip == null) {
return;
}
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
const rect = anchor.getBoundingClientRect();
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 11:54:20 +02:00
previousTooltipWidth = previousTooltip.clientWidth
previousTooltipHeight = previousTooltip.clientHeight
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
if (previousTooltipWidth === 0) {
previousTooltipWidth = currentTooltipWidth;
}
if (previousTooltipHeight === 0) {
previousTooltipHeight = currentTooltipHeight;
}
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
currentX = rect.x - currentTooltipWidth
currentY = rect.y
2024-04-02 11:54:20 +02:00
2024-04-02 13:07:11 +02:00
if (previousX === 0) {
previousX = currentX
}
2024-04-02 11:54:20 +02:00
2024-04-02 13:07:11 +02:00
if (previousY === 0) {
previousY = currentY
2024-04-02 11:54:20 +02:00
}
})
2024-04-01 13:31:16 +02:00
}
2024-04-02 13:07:11 +02:00
/*
2024-04-01 18:14:18 +02:00
const getNormalizedTime = (startTime, endTime, currentTime) => {
const distanceFromStart = currentTime - startTime;
const timeDiff = endTime - startTime;
2024-04-01 13:31:16 +02:00
2024-04-01 18:14:18 +02:00
return distanceFromStart / timeDiff;
}
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
const cubicBezierInterpolation = (p1, p2, p3, p4, currentTime) => {
return (
(Math.pow(1 - currentTime, 3) * p1) +
(3 * Math.pow(1 - currentTime, 2) * currentTime * p2) +
(3 * (1 - currentTime) * Math.pow(currentTime, 2) * p3) +
(Math.pow(currentTime, 3) * p4)
)
2024-04-01 18:14:18 +02:00
}
2024-04-01 13:31:16 +02:00
2024-04-01 18:14:18 +02:00
// Made to match the interface of the css bezier curve function
const cubicBezierEasing = (a, b, c, d, t) => {
// CSS bezier curve function implicitly provides p1 and p4
const p1 = { x: 0, y: 0 }
const p2 = { x: a, y: b }
const p3 = { x: c, y: d }
const p4 = { x: 1, y: 1 }
return {
x: cubicBezierInterpolation(p1.x, p2.x, p3.x, p4.x, t),
y: cubicBezierInterpolation(p1.y, p2.y, p3.y, p4.y, t)
2024-04-01 13:31:16 +02:00
}
2024-04-01 18:14:18 +02:00
}
2024-04-01 13:31:16 +02:00
2024-04-01 18:14:18 +02:00
const linearInterpolation = (p1, p2, t) => {
return p1 + t * (p2 - p1);
}
2024-04-01 13:31:16 +02:00
2024-04-01 18:14:18 +02:00
const animate = (invokedAnimationStartTime, frameTime) => {
if (invokedAnimationStartTime !== animationStartTime) {
console.log("CANCEL ANIMATION ", invokedAnimationStartTime, " ", animationStartTime);
return;
}
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
const animationDuration = 300
2024-04-01 18:14:18 +02:00
const normalizedTime = getNormalizedTime(invokedAnimationStartTime, invokedAnimationStartTime + animationDuration, frameTime)
2024-04-01 13:31:16 +02:00
2024-04-01 18:14:18 +02:00
if (normalizedTime >= 1) {
console.log("exiting");
return;
2024-04-01 13:31:16 +02:00
}
2024-04-01 18:14:18 +02:00
const easing = cubicBezierEasing(0.25, 0.1, 0.25, 1, normalizedTime)
x = linearInterpolation(startX, endX, easing.x)
y = linearInterpolation(startY, endY, easing.y)
2024-04-02 11:54:20 +02:00
w = linearInterpolation(previousTooltipWidth, currentTooltipWidth, easing.x)
2024-04-01 13:31:16 +02:00
2024-04-01 18:14:18 +02:00
requestAnimationFrame((newFrameTime) => animate(invokedAnimationStartTime, newFrameTime))
2024-04-02 13:07:11 +02:00
}*/
2024-04-01 13:31:16 +02:00
2024-04-02 11:54:20 +02:00
$: updatePosition(anchor, currentTooltip, previousTooltip)
2024-04-01 13:31:16 +02:00
$: updatePositionOnVisibilityChange(visible, hovering)
2024-04-02 13:07:11 +02:00
/*$: requestAnimationFrame((frameTime) => animate(animationStartTime, frameTime))*/
2024-04-01 13:31:16 +02:00
const handleMouseenter = (e) => {
hovering = true;
}
const handleMouseleave = (e) => {
hovering = false;
}
</script>
<Portal target=".spectrum">
<div
on:mouseenter={handleMouseenter}
on:mouseleave={handleMouseleave}
2024-04-02 13:07:11 +02:00
style:width={`${currentTooltipWidth}px`}
2024-04-02 11:54:20 +02:00
style:height={`${currentTooltipHeight}px`}
2024-04-02 13:07:11 +02:00
style:left={`${currentX}px`}
style:top={`${currentY}px`}
2024-04-01 13:31:16 +02:00
class="tooltip"
class:visible={visible || hovering}
>
2024-04-02 13:07:11 +02:00
<div class="screenSize"
style:left={`${-currentX}px`}
style:top={`${-currentY}px`}
>
2024-04-02 11:54:20 +02:00
<div
2024-04-02 13:07:11 +02:00
style:left={`${currentX}px`}
style:top={`${currentY}px`}
2024-04-02 11:54:20 +02:00
bind:this={currentTooltip}
class="currentContent"
>
<slot />
</div>
<div
bind:this={previousTooltip}
class="previousContent"
2024-04-02 13:07:11 +02:00
style:left={`${previousX}px`}
style:top={`${previousY}px`}
2024-04-02 11:54:20 +02:00
>
<slot name="previous"/>
</div>
</div>
2024-04-01 13:31:16 +02:00
</div>
</Portal>
<style>
2024-04-02 13:07:11 +02:00
/* Screen width absolute parent for tooltip content so that'd the applied width and height
to the root doesn't affect their size */
2024-04-02 11:54:20 +02:00
.screenSize {
position: absolute;
width: 100vw;
height: 100vh;
2024-04-02 13:07:11 +02:00
transition: top 300ms ease-in, left 300ms ease-in;
2024-04-02 11:54:20 +02:00
}
2024-04-01 13:31:16 +02:00
.tooltip {
position: absolute;
z-index: 9999;
pointer-events: none;
2024-04-02 11:54:20 +02:00
background-color: red;
opacity: 0;
overflow: hidden;
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
}
.visible {
opacity: 1;
pointer-events: auto;
}
2024-04-02 11:54:20 +02:00
.currentContent {
position: absolute;
z-index: 10000;
}
.previousContent {
position: absolute;
z-index: 10000;
}
2024-04-01 13:31:16 +02:00
</style>