main
js 79 lines 3.32 KB
Raw
1 /**
2 * Detects the input type: 'pointer' (e.g., mouse, supports hover) or 'touch' (e.g., finger, no reliable hover).
3 * On hybrids, resolves based on first user interaction (mouse vs. touch).
4 * @returns {Promise<string>} Resolves to 'pointer' or 'touch'.
5 */
6 // Module variable to store detected input type
7 let detectedInputType;
8
9 // Detects and stores the input type: 'pointer' or 'touch'. On hybrids, resolves on first user interaction.
10 export function determineInputType() {
11 return new Promise((resolve) => {
12 let inputType = "pointer"; // Default (overridden below)
13 let resolved = false;
14
15 // Helper to resolve and clean up listeners
16 const resolveType = (type) => {
17 if (resolved) return;
18 resolved = true;
19 inputType = type;
20 detectedInputType = type; // store in module variable
21 resolve(inputType);
22 // Remove listeners to avoid memory leaks
23 document.removeEventListener("touchstart", onTouch, { passive: true });
24 document.removeEventListener("mousemove", onMouse, { passive: true });
25 document.removeEventListener("mouseenter", onMouse, { passive: true });
26 };
27
28 // Dynamic listeners for hybrids (detect first interaction)
29 const onTouch = () => resolveType("touch");
30 const onMouse = () => resolveType("pointer");
31
32 // Static detection (inspired by detect-it: https://github.com/rafgraph/detect-it)
33 // Step 1: Check for touch capability (touchOnly or hybrid)
34 const hasTouch = () => {
35 if ("maxTouchPoints" in navigator) return navigator.maxTouchPoints > 0;
36 if (window.matchMedia)
37 return window.matchMedia("(any-pointer: coarse)").matches;
38 return "ontouchstart" in window || navigator.msMaxTouchPoints > 0;
39 };
40
41 // Step 2: Check for pointer/hover capability (pointerOnly or hybrid)
42 const hasPointer = () => {
43 if (window.matchMedia) {
44 const finePointer = window.matchMedia("(any-pointer: fine)").matches;
45 const hover = window.matchMedia("(any-hover: hover)").matches;
46 return finePointer || hover;
47 }
48 return false; // Fallback: Assume no pointer if media queries unavailable
49 };
50
51 const touchSupported = hasTouch();
52 const pointerSupported = hasPointer();
53
54 if (touchSupported && !pointerSupported) {
55 // Touch-only (e.g., phones)
56 resolveType("touch");
57 } else if (!touchSupported && pointerSupported) {
58 // Pointer-only (e.g., desktops)
59 resolveType("pointer");
60 } else if (touchSupported && pointerSupported) {
61 // Hybrid: Wait for first interaction to determine usageDit is the user's active input
62 // Default to pointer, but add listeners if hybrid
63 inputType = "pointer"; // Default for hybrids until interaction
64 document.addEventListener("touchstart", onTouch, { passive: true });
65 document.addEventListener("mousemove", onMouse, { passive: true });
66 document.addEventListener("mouseenter", onMouse, { passive: true });
67 // Optional: Timeout fallback (e.g., after 10s, assume pointer for hybrids)
68 setTimeout(() => resolveType("pointer"), 10000);
69 } else {
70 // Rare fallback: No touch or pointer detected (assume pointer)
71 resolveType("pointer");
72 }
73 });
74 }
75
76 // Exported function to get the detected input type (defaults to 'pointer' if undetermined)
77 export function getInputType() {
78 return detectedInputType || "pointer";
79 }