Files
portal/app/resources/js/composables/useDevIndices.ts
T
2026-05-12 11:57:35 +03:00

112 lines
2.9 KiB
TypeScript

import { ref, type Ref } from 'vue';
export interface DevIndicesApi {
currentTarget: Ref<HTMLElement | null>;
currentId: Ref<number | null>;
overlayMode: Ref<boolean>;
hoverEnabled: Ref<boolean>;
setTarget(el: HTMLElement | null): void;
toggleOverlay(): void;
walkToParent(): void;
walkToChild(): void;
pauseHover(ms: number): void;
reset(): void;
}
// Module-level singleton state — shared across all consumers
const currentTarget = ref<HTMLElement | null>(null);
const currentId = ref<number | null>(null);
const overlayMode = ref(false);
const hoverEnabled = ref(true);
let pauseTimer: ReturnType<typeof setTimeout> | null = null;
function parseId(el: HTMLElement | null): number | null {
if (!el) return null;
const raw = el.getAttribute('data-dx');
if (raw == null) return null;
const n = Number(raw);
return Number.isFinite(n) ? n : null;
}
function setTarget(el: HTMLElement | null): void {
if (el == null) {
currentTarget.value = null;
currentId.value = null;
return;
}
const id = parseId(el);
if (id == null) return;
currentTarget.value = el;
currentId.value = id;
}
function toggleOverlay(): void {
overlayMode.value = !overlayMode.value;
}
function findAncestorWithDx(el: HTMLElement | null): HTMLElement | null {
let cur: HTMLElement | null = el?.parentElement ?? null;
while (cur) {
if (cur.hasAttribute('data-dx')) return cur;
cur = cur.parentElement;
}
return null;
}
function findFirstDescendantWithDx(el: HTMLElement | null): HTMLElement | null {
if (!el) return null;
// BFS: find first descendant with data-dx
const queue: HTMLElement[] = Array.from(el.children) as HTMLElement[];
while (queue.length) {
const cur = queue.shift()!;
if (cur.hasAttribute('data-dx')) return cur;
queue.push(...(Array.from(cur.children) as HTMLElement[]));
}
return null;
}
function walkToParent(): void {
const parent = findAncestorWithDx(currentTarget.value);
if (parent) setTarget(parent);
}
function walkToChild(): void {
const child = findFirstDescendantWithDx(currentTarget.value);
if (child) setTarget(child);
}
function pauseHover(ms: number): void {
hoverEnabled.value = false;
if (pauseTimer) clearTimeout(pauseTimer);
pauseTimer = setTimeout(() => {
hoverEnabled.value = true;
pauseTimer = null;
}, ms);
}
function reset(): void {
currentTarget.value = null;
currentId.value = null;
overlayMode.value = false;
hoverEnabled.value = true;
if (pauseTimer) {
clearTimeout(pauseTimer);
pauseTimer = null;
}
}
export function useDevIndices(): DevIndicesApi {
return {
currentTarget,
currentId,
overlayMode,
hoverEnabled,
setTarget,
toggleOverlay,
walkToParent,
walkToChild,
pauseHover,
reset,
};
}