|
|
|
@ -31,7 +31,7 @@ import {
|
|
|
|
import { tooltipMorfo } from '../../../morfo/components/tooltip';
|
|
|
|
import { tooltipMorfo } from '../../../morfo/components/tooltip';
|
|
|
|
import { Soma } from '../../core/soma.svelte';
|
|
|
|
import { Soma } from '../../core/soma.svelte';
|
|
|
|
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
|
|
|
|
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
|
|
|
|
|
|
|
|
import type { TimerHandle } from '$timer';
|
|
|
|
|
|
|
|
|
|
|
|
export type TooltipState = 'closed' | 'delayed-open' | 'instant-open';
|
|
|
|
export type TooltipState = 'closed' | 'delayed-open' | 'instant-open';
|
|
|
|
|
|
|
|
|
|
|
|
@ -112,8 +112,8 @@ export class TooltipProvider {
|
|
|
|
readonly contentPresence: Presence;
|
|
|
|
readonly contentPresence: Presence;
|
|
|
|
|
|
|
|
|
|
|
|
private internalTooltipState = $state<TooltipState>('closed');
|
|
|
|
private internalTooltipState = $state<TooltipState>('closed');
|
|
|
|
private openTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
private openTimer: TimerHandle | null = null;
|
|
|
|
private closeTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
private closeTimer: TimerHandle | null = null;
|
|
|
|
private contentRef = state<HTMLElement | null>(null);
|
|
|
|
private contentRef = state<HTMLElement | null>(null);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@ -167,6 +167,10 @@ export class TooltipProvider {
|
|
|
|
buffer: 2,
|
|
|
|
buffer: 2,
|
|
|
|
transitIntentTimeout: 200
|
|
|
|
transitIntentTimeout: 200
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
|
|
return () => this.clearTimers();
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Effective delay (per-tooltip override → group default). */
|
|
|
|
/** Effective delay (per-tooltip override → group default). */
|
|
|
|
@ -190,28 +194,40 @@ export class TooltipProvider {
|
|
|
|
|
|
|
|
|
|
|
|
handleOpen(instant = false) {
|
|
|
|
handleOpen(instant = false) {
|
|
|
|
if (this.isDisabled) return;
|
|
|
|
if (this.isDisabled) return;
|
|
|
|
this.clearTimeouts();
|
|
|
|
this.clearTimers();
|
|
|
|
|
|
|
|
|
|
|
|
if (instant || this.group?.isWithinSkipDelay()) {
|
|
|
|
if (instant || this.group?.isWithinSkipDelay()) {
|
|
|
|
this.internalTooltipState = 'instant-open';
|
|
|
|
this.internalTooltipState = 'instant-open';
|
|
|
|
this.opts.open.current = true;
|
|
|
|
this.opts.open.current = true;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
this.internalTooltipState = 'delayed-open';
|
|
|
|
this.internalTooltipState = 'delayed-open';
|
|
|
|
this.openTimeout = setTimeout(() => {
|
|
|
|
this.openTimer = this.soma.uix.timers.schedule(
|
|
|
|
this.internalTooltipState = 'instant-open';
|
|
|
|
this.openTimerKey,
|
|
|
|
this.opts.open.current = true;
|
|
|
|
this.delay,
|
|
|
|
}, this.delay);
|
|
|
|
() => {
|
|
|
|
|
|
|
|
this.openTimer = null;
|
|
|
|
|
|
|
|
this.internalTooltipState = 'instant-open';
|
|
|
|
|
|
|
|
this.opts.open.current = true;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{ replace: true, meta: { component: 'tooltip', action: 'open-delay' } }
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
handleClose() {
|
|
|
|
handleClose() {
|
|
|
|
this.clearTimeouts();
|
|
|
|
this.clearTimers();
|
|
|
|
const closeDelay = this.opts.closeDelay.current;
|
|
|
|
const closeDelay = this.opts.closeDelay.current;
|
|
|
|
|
|
|
|
|
|
|
|
if (closeDelay > 0) {
|
|
|
|
if (closeDelay > 0) {
|
|
|
|
this.closeTimeout = setTimeout(() => {
|
|
|
|
this.closeTimer = this.soma.uix.timers.schedule(
|
|
|
|
this.doClose();
|
|
|
|
this.closeTimerKey,
|
|
|
|
}, closeDelay);
|
|
|
|
closeDelay,
|
|
|
|
|
|
|
|
() => {
|
|
|
|
|
|
|
|
this.closeTimer = null;
|
|
|
|
|
|
|
|
this.doClose();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{ replace: true, meta: { component: 'tooltip', action: 'close-delay' } }
|
|
|
|
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
this.doClose();
|
|
|
|
this.doClose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -225,23 +241,31 @@ export class TooltipProvider {
|
|
|
|
|
|
|
|
|
|
|
|
/** Cancel pending close (when pointer enters content). */
|
|
|
|
/** Cancel pending close (when pointer enters content). */
|
|
|
|
cancelClose() {
|
|
|
|
cancelClose() {
|
|
|
|
if (this.closeTimeout !== null) {
|
|
|
|
if (this.closeTimer !== null) {
|
|
|
|
clearTimeout(this.closeTimeout);
|
|
|
|
this.closeTimer.cancel();
|
|
|
|
this.closeTimeout = null;
|
|
|
|
this.closeTimer = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private clearTimeouts() {
|
|
|
|
private clearTimers() {
|
|
|
|
if (this.openTimeout !== null) {
|
|
|
|
if (this.openTimer !== null) {
|
|
|
|
clearTimeout(this.openTimeout);
|
|
|
|
this.openTimer.cancel();
|
|
|
|
this.openTimeout = null;
|
|
|
|
this.openTimer = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this.closeTimeout !== null) {
|
|
|
|
if (this.closeTimer !== null) {
|
|
|
|
clearTimeout(this.closeTimeout);
|
|
|
|
this.closeTimer.cancel();
|
|
|
|
this.closeTimeout = null;
|
|
|
|
this.closeTimer = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private get openTimerKey() {
|
|
|
|
|
|
|
|
return `soma:tooltip:${this.opts.id.current}:open`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private get closeTimerKey() {
|
|
|
|
|
|
|
|
return `soma:tooltip:${this.opts.id.current}:close`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setContentRef(el: HTMLElement | null) {
|
|
|
|
setContentRef(el: HTMLElement | null) {
|
|
|
|
this.contentRef.current = el;
|
|
|
|
this.contentRef.current = el;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|