Files
silo-server/web/src/components/sidebarItemNavigation.ts
T

63 lines
2.1 KiB
TypeScript

export const SIDEBAR_COLLAPSE_DURATION_MS = 300;
export const SIDEBAR_TRANSITION_FALLBACK_MS = SIDEBAR_COLLAPSE_DURATION_MS + 80;
// Hover expansion and hidden-tab rAF suspension must never hold the detail
// shell indefinitely. Settling is preferred, but this is the absolute cap.
export const SIDEBAR_DETAILS_REVEAL_DEADLINE_MS = SIDEBAR_TRANSITION_FALLBACK_MS * 2;
export function sidebarDetailsRevealDelay(reduceMotion: boolean): number {
return reduceMotion ? 0 : SIDEBAR_TRANSITION_FALLBACK_MS;
}
export function isCollapsedSidebarSurface(target: EventTarget | null): target is HTMLElement {
return (
target instanceof HTMLElement &&
target.classList.contains("sidebar-surface") &&
target.dataset.collapsed === "true"
);
}
export function hasRunningSidebarTransition(surface: HTMLElement): boolean {
if (typeof surface.getAnimations !== "function") return false;
const Transition = globalThis.CSSTransition;
if (typeof Transition === "undefined") return false;
return surface
.getAnimations()
.some(
(animation) =>
animation instanceof Transition &&
animation.transitionProperty === "transform" &&
animation.playState === "running",
);
}
export function parseOptionalLibraryId(rawLibraryId: string | null): number | undefined {
if (!rawLibraryId) return undefined;
const parsedLibraryId = Number(rawLibraryId);
return Number.isFinite(parsedLibraryId) ? parsedLibraryId : undefined;
}
export interface SidebarItemNavigationRequest {
href: string;
replace?: boolean;
state?: unknown;
}
export function parseItemNavigationHref(
href: string,
origin: string,
): { contentId: string; libraryId?: number } | null {
try {
const destination = new URL(href, origin);
if (destination.origin !== origin || !destination.pathname.startsWith("/item/")) return null;
const contentId = decodeURIComponent(destination.pathname.slice("/item/".length));
if (!contentId) return null;
return {
contentId,
libraryId: parseOptionalLibraryId(destination.searchParams.get("libraryId")),
};
} catch {
return null;
}
}