Files
MyIP/frontend/composables/use-route-active.js
T
Claude 96525d2fc7 Chore: trim the branch — leftovers, redundancy, comments
A pass over every file the branch touched, after the iOS animation hunt
left tries and reverts behind it. Removed what nothing reaches: the
view-transition rule for a transition no code starts, an unreachable
guard in the transition hook, a no-op KeepAlive key branch, a sign-in
guard the menu already enforces, usage-dialog wrappers that only
forwarded to the store, a duplicated modifier-click check and card
filter (isPlainClick / listedTools), a test that hand-rolled
listedTools, a drawer-era fixture parameter. auth-flow.js folded into
auth-redirect.js. Comments cut to what the code cannot say itself —
file headers of a few lines, the measured iOS reasons, non-obvious
orderings — and stale ones fixed (Nav under KeepAlive, Home.vue
reusing DEFAULT_META, "from Nav", enabledCards). AGENTS.md sections
tightened to the code as it is now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
2026-10-05 16:50:16 +00:00

62 lines
2.4 KiB
JavaScript

// "Is my page the one on screen?" for components under the app's KeepAlive
// layers (App.vue caches Home and ToolPage; ToolPage caches each tool). A
// cached page stays mounted while another route shows, so whatever acts on the
// route, the document or window events pauses while it is deactivated.
// onActivated / onDeactivated fire for every cached ancestor, so the same call
// works at a page root and deep inside it. Outside a component, always active.
import {
computed, getCurrentInstance, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref,
} from 'vue';
// An async child can first mount after its page was cached away; it starts
// inactive. `isDeactivated` is the flag KeepAlive sets on the cached root.
const inDeactivatedBranch = (instance) => {
for (let current = instance; current; current = current.parent) {
if (current.isDeactivated) return true;
}
return false;
};
export const useRouteActive = () => {
const instance = getCurrentInstance();
const active = ref(instance ? !inDeactivatedBranch(instance) : true);
if (!instance) return active;
onActivated(() => { active.value = true; });
onDeactivated(() => { active.value = false; });
return active;
};
// A computed that follows `getter` while active and holds its last value while
// hidden. The route changes a render before the old page is deactivated, so
// `pathOf` (the current path) also holds it once the path leaves the page's.
export const useActiveValue = (getter, { pathOf, active = useRouteActive() } = {}) => {
let ownPath = pathOf?.();
if (getCurrentInstance()) onActivated(() => { ownPath = pathOf?.(); });
return computed((previous) => (
active.value && (!pathOf || pathOf() === ownPath) ? getter() : previous
));
};
// Attached only while active (mounted / activated).
export const useActiveEventListener = (target, event, handler, options) => {
const active = useRouteActive();
let attached = false;
const attach = () => {
if (attached) return;
target.addEventListener(event, handler, options);
attached = true;
};
const detach = () => {
if (!attached) return;
target.removeEventListener(event, handler, options);
attached = false;
};
onMounted(() => { if (active.value) attach(); });
onActivated(attach);
onDeactivated(detach);
onBeforeUnmount(detach);
};