Route Soma navigation menu timers

active-uix
dev 5 months ago
parent 8f4be6d425
commit 88be88f210

@ -23,6 +23,7 @@ import { NAVIGATION_MENU_LANGS } from './langs';
import { navigationMenuMorfo } from '../../../morfo/components/navigation-menu';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
import type { TimerHandle } from '$timer';
const attrs = createAttrs(navigationMenuMorfo);
@ -62,9 +63,9 @@ export class NavigationMenuProvider {
return new NavigationMenuProvider(opts);
}
private openTimer: ReturnType<typeof setTimeout> | null = null;
private closeTimer: ReturnType<typeof setTimeout> | null = null;
private skipDelayTimer: ReturnType<typeof setTimeout> | null = null;
private openTimer: TimerHandle | null = null;
private closeTimer: TimerHandle | null = null;
private skipDelayTimer: TimerHandle | null = null;
/** True while the "skip delay" window is open — i.e. a submenu was just closed
* and the user is likely hovering another trigger right after. During this
* window, `scheduleOpen` uses the shorter `skipDelayDuration`. */
@ -107,9 +108,8 @@ export class NavigationMenuProvider {
$effect(() => {
return () => {
if (this.openTimer) clearTimeout(this.openTimer);
if (this.closeTimer) clearTimeout(this.closeTimer);
if (this.skipDelayTimer) clearTimeout(this.skipDelayTimer);
this.clearTimers();
this.clearSkipDelayTimer();
};
});
}
@ -129,11 +129,11 @@ export class NavigationMenuProvider {
private clearTimers() {
if (this.openTimer) {
clearTimeout(this.openTimer);
this.openTimer.cancel();
this.openTimer = null;
}
if (this.closeTimer) {
clearTimeout(this.closeTimer);
this.closeTimer.cancel();
this.closeTimer = null;
}
}
@ -158,12 +158,18 @@ export class NavigationMenuProvider {
private armSkipDelay() {
this.inSkipDelayWindow = true;
if (this.skipDelayTimer) clearTimeout(this.skipDelayTimer);
this.clearSkipDelayTimer();
// Skip window lasts the length of `openDelay` — a reasonable "grace
// period" during which the user is presumed to still be navigating.
this.skipDelayTimer = setTimeout(() => {
this.skipDelayTimer = this.soma.uix.timers.schedule(
this.skipDelayTimerKey,
this.opts.openDelay.current,
() => {
this.skipDelayTimer = null;
this.inSkipDelayWindow = false;
}, this.opts.openDelay.current);
},
{ replace: true, meta: { component: 'navigation-menu', action: 'skip-delay' } }
);
}
/**
@ -186,23 +192,58 @@ export class NavigationMenuProvider {
this.openNow(value);
return;
}
this.openTimer = setTimeout(() => this.openNow(value), delay);
this.openTimer = this.soma.uix.timers.schedule(
this.openTimerKey,
delay,
() => {
this.openTimer = null;
this.openNow(value);
},
{ replace: true, meta: { component: 'navigation-menu', action: 'open-delay' } }
);
}
/** Schedule a close (grace period). */
scheduleClose() {
this.clearTimers();
this.closeTimer = setTimeout(() => this.closeNow(), this.opts.closeDelay.current);
this.closeTimer = this.soma.uix.timers.schedule(
this.closeTimerKey,
this.opts.closeDelay.current,
() => {
this.closeTimer = null;
this.closeNow();
},
{ replace: true, meta: { component: 'navigation-menu', action: 'close-delay' } }
);
}
/** Cancel a pending close (e.g. pointer entered the content). */
cancelClose() {
if (this.closeTimer) {
clearTimeout(this.closeTimer);
this.closeTimer.cancel();
this.closeTimer = null;
}
}
private clearSkipDelayTimer() {
if (this.skipDelayTimer) {
this.skipDelayTimer.cancel();
this.skipDelayTimer = null;
}
}
private get openTimerKey() {
return `soma:navigation-menu:${this.opts.id.current}:open`;
}
private get closeTimerKey() {
return `soma:navigation-menu:${this.opts.id.current}:close`;
}
private get skipDelayTimerKey() {
return `soma:navigation-menu:${this.opts.id.current}:skip-delay`;
}
registerTrigger(value: string, el: HTMLElement) {
this.triggerRefs.set(value, el);
return () => {

Loading…
Cancel
Save

Powered by TurnKey Linux.