add vueuse hooks

This commit is contained in:
Georges-Antoine Assi
2025-08-25 14:52:16 -04:00
parent 9145cd5e93
commit 8f109508cb
3 changed files with 63 additions and 60 deletions

View File

@@ -10,6 +10,7 @@
"license": "AGPL-3.0-only",
"dependencies": {
"@mdi/font": "7.4.47",
"@vueuse/core": "^13.7.0",
"axios": "^1.8.4",
"cronstrue": "^2.57.0",
"date-fns": "^4.1.0",
@@ -3604,6 +3605,11 @@
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"dev": true
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.28.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz",
@@ -4085,6 +4091,41 @@
"vuetify": "^3.0.0"
}
},
"node_modules/@vueuse/core": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.7.0.tgz",
"integrity": "sha512-myagn09+c6BmS6yHc1gTwwsdZilAovHslMjyykmZH3JNyzI5HoWhv114IIdytXiPipdHJ2gDUx0PB93jRduJYg==",
"dependencies": {
"@types/web-bluetooth": "^0.0.21",
"@vueuse/metadata": "13.7.0",
"@vueuse/shared": "13.7.0"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0"
}
},
"node_modules/@vueuse/metadata": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.7.0.tgz",
"integrity": "sha512-8okFhS/1ite8EwUdZZfvTYowNTfXmVCOrBFlA31O0HD8HKXhY+WtTRyF0LwbpJfoFPc+s9anNJIXMVrvP7UTZg==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.7.0.tgz",
"integrity": "sha512-Wi2LpJi4UA9kM0OZ0FCZslACp92HlVNw1KPaDY6RAzvQ+J1s7seOtcOpmkfbD5aBSmMn9NvOakc8ZxMxmDXTIg==",
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0"
}
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",

View File

@@ -28,6 +28,7 @@
},
"dependencies": {
"@mdi/font": "7.4.47",
"@vueuse/core": "^13.7.0",
"axios": "^1.8.4",
"cronstrue": "^2.57.0",
"date-fns": "^4.1.0",

View File

@@ -1,12 +1,14 @@
<script setup lang="ts">
import { onMounted, onUnmounted, provide, ref, watch } from "vue";
import { useRoute, type RouteLocationNormalized } from "vue-router";
import { onMounted, onUnmounted, provide, watch } from "vue";
import { type RouteLocationNormalized } from "vue-router";
import { InputBus, InputBusSymbol } from "@/console/input/bus";
import { attachKeyboard } from "@/console/input/keyboard";
import { attachGamepad } from "@/console/input/gamepad";
import { ROUTES } from "@/plugins/router";
import { useRouter } from "vue-router";
import { useIdle } from "@vueuse/core";
const route = useRoute();
const router = useRouter();
const bus = new InputBus();
provide(InputBusSymbol, bus);
@@ -19,18 +21,16 @@ const routeHierarchy = {
[ROUTES.CONSOLE_PLAY]: 3,
};
let previousRoute: string | null = null;
// Determine transition based on navigation direction and route type
function getTransitionName(route: RouteLocationNormalized): string {
const currentName = route.name as string;
const currentLevel =
routeHierarchy[currentName as keyof typeof routeHierarchy] ?? 1;
const previousRoute = router.options.history.state.back;
const previousLevel = previousRoute
? (routeHierarchy[previousRoute as keyof typeof routeHierarchy] ?? 1)
: 0;
// Special case for play mode (slide up/down)
if (
currentName === ROUTES.CONSOLE_PLAY ||
previousRoute === ROUTES.CONSOLE_PLAY
@@ -38,72 +38,35 @@ function getTransitionName(route: RouteLocationNormalized): string {
return currentLevel > previousLevel ? "slide-up" : "slide-down";
}
// General navigation (slide left/right)
if (currentLevel > previousLevel) {
return "slide-left"; // Going deeper (forward)
} else if (currentLevel < previousLevel) {
return "slide-right"; // Going back
} else {
return "fade"; // Same level or first load
}
if (currentLevel > previousLevel) return "slide-left";
return currentLevel < previousLevel ? "slide-right" : "fade";
}
// Track route changes for transition direction
watch(
() => route.name,
(newName, oldName) => {
if (oldName) {
previousRoute = oldName as string;
}
},
);
const { idle: mouseIdle } = useIdle(100, {
events: ["mousemove", "mousedown", "wheel", "touchstart"],
});
watch(mouseIdle, (idle) => {
document
.querySelector("#application")
?.classList.toggle("mouse-hidden", idle);
});
let detachKeyboard: (() => void) | null = null;
let detachGamepad: (() => void) | null = null;
const mouseHidden = ref(false);
let idleTimer: number | undefined;
const HIDE_DELAY_MS = 100;
const onMouseActivity = () => {
// Show cursor (remove shield) then schedule hide
if (mouseHidden.value) mouseHidden.value = false;
window.clearTimeout(idleTimer);
idleTimer = window.setTimeout(() => {
mouseHidden.value = true;
}, HIDE_DELAY_MS);
};
// Toggle class for global cursor hiding (covers any nested explicit cursor styles)
watch(mouseHidden, (hidden) => {
const app = document.querySelector("#application");
if (app) app.classList.toggle("mouse-hidden", hidden);
});
onMounted(() => {
// Establish a root input scope so child views can subscribe safely
bus.pushScope();
detachKeyboard = attachKeyboard(bus);
detachGamepad = attachGamepad(bus);
// Mouse idle/hide across all console views
onMouseActivity();
document.addEventListener("mousemove", onMouseActivity, { passive: true });
document.addEventListener("mousedown", onMouseActivity, { passive: true });
document.addEventListener("wheel", onMouseActivity, { passive: true });
document.addEventListener("touchstart", onMouseActivity, { passive: true });
});
onUnmounted(() => {
const app = document.querySelector("#application");
if (app) app.classList.remove("mouse-hidden");
detachKeyboard?.();
detachGamepad?.();
document.removeEventListener("mousemove", onMouseActivity as EventListener);
document.removeEventListener("mousedown", onMouseActivity as EventListener);
document.removeEventListener("wheel", onMouseActivity as EventListener);
document.removeEventListener("touchstart", onMouseActivity as EventListener);
window.clearTimeout(idleTimer);
document.querySelector("#application")?.classList.remove("mouse-hidden");
});
</script>
@@ -111,11 +74,9 @@ onUnmounted(() => {
<div class="min-h-screen text-white console-root relative">
<!-- Shield overlay to neutralize mouse input while hidden; movement wakes it -->
<div
v-if="mouseHidden"
v-if="!mouseIdle"
class="fixed inset-0 z-50 cursor-none"
aria-hidden="true"
@mousemove="onMouseActivity"
@pointermove="onMouseActivity"
@mousedown.prevent
@mouseup.prevent
@click.prevent