web: add haptics to toggles & switchers

This commit is contained in:
wukko 2025-03-05 17:21:45 +06:00
parent 66401c6c5f
commit 6b09bd4688
No known key found for this signature in database
GPG Key ID: 3E30B3F26C7B4AA2
4 changed files with 53 additions and 11 deletions

View File

@ -6,6 +6,8 @@
Value extends CobaltSettings[Context][Id] Value extends CobaltSettings[Context][Id]
" "
> >
import { hapticSwitch } from "$lib/haptics";
import settings, { updateSetting } from "$lib/state/settings"; import settings, { updateSetting } from "$lib/state/settings";
import type { CobaltSettings } from "$lib/types/settings"; import type { CobaltSettings } from "$lib/types/settings";
@ -22,12 +24,14 @@
class="button" class="button"
class:active={isActive} class:active={isActive}
aria-pressed={isActive} aria-pressed={isActive}
on:click={() => on:click={() => {
hapticSwitch();
updateSetting({ updateSetting({
[settingContext]: { [settingContext]: {
[settingId]: settingValue, [settingId]: settingValue,
}, },
})} });
}}
> >
<slot></slot> <slot></slot>
</button> </button>

View File

@ -5,6 +5,7 @@
Id extends keyof CobaltSettings[Context] Id extends keyof CobaltSettings[Context]
" "
> >
import { hapticSwitch } from "$lib/haptics";
import settings, { updateSetting } from "$lib/state/settings"; import settings, { updateSetting } from "$lib/state/settings";
import type { CobaltSettings } from "$lib/types/settings"; import type { CobaltSettings } from "$lib/types/settings";
@ -34,14 +35,15 @@
class="button toggle-container" class="button toggle-container"
role="switch" role="switch"
aria-checked={isEnabled} aria-checked={isEnabled}
disabled={disabled} {disabled}
on:click={() => on:click={() => {
hapticSwitch();
updateSetting({ updateSetting({
[settingContext]: { [settingContext]: {
[settingId]: !isEnabled, [settingId]: !isEnabled,
} },
}) });
} }}
> >
<h4 class="toggle-title">{title}</h4> <h4 class="toggle-title">{title}</h4>
<Toggle enabled={isEnabled} /> <Toggle enabled={isEnabled} />
@ -82,10 +84,14 @@
border-radius: var(--border-radius); border-radius: var(--border-radius);
overflow: scroll; overflow: scroll;
transition: background 0.1s, box-shadow 0.1s; transition:
background 0.1s,
box-shadow 0.1s;
} }
.toggle-container:active { .toggle-container:active {
box-shadow: var(--button-box-shadow), 0 0 0 1.5px var(--button-stroke) inset; box-shadow:
var(--button-box-shadow),
0 0 0 1.5px var(--button-stroke) inset;
} }
</style> </style>

View File

@ -11,6 +11,7 @@ const device = {
iPhone: false, iPhone: false,
iPad: false, iPad: false,
iOS: false, iOS: false,
modernIOS: false,
android: false, android: false,
mobile: false, mobile: false,
}, },
@ -35,6 +36,9 @@ if (browser) {
const iPhone = ua.includes("iphone os"); const iPhone = ua.includes("iphone os");
const iPad = !iPhone && ua.includes("mac os") && navigator.maxTouchPoints > 0; const iPad = !iPhone && ua.includes("mac os") && navigator.maxTouchPoints > 0;
const iosVersion = Number(ua.match(/iphone os (\d+)_/)?.[1]);
const modernIOS = iPhone && iosVersion >= 18;
const iOS = iPhone || iPad; const iOS = iPhone || iPad;
const android = ua.includes("android") || ua.includes("diordna"); const android = ua.includes("android") || ua.includes("diordna");
@ -45,11 +49,13 @@ if (browser) {
}; };
device.is = { device.is = {
mobile: iOS || android,
android,
iPhone, iPhone,
iPad, iPad,
iOS, iOS,
android, modernIOS,
mobile: iOS || android,
}; };
device.browser = { device.browser = {

26
web/src/lib/haptics.ts Normal file
View File

@ -0,0 +1,26 @@
import { device } from "$lib/device";
// not sure if vibrations feel the same on android,
// so they're enabled only on ios 18+ for now
const shouldVibrate = device.is.modernIOS;
export const hapticSwitch = () => {
if (!shouldVibrate) return;
try {
const label = document.createElement("label");
label.ariaHidden = "true";
label.style.display = "none";
const input = document.createElement("input");
input.type = "checkbox";
input.setAttribute("switch", "");
label.appendChild(input);
document.head.appendChild(label);
label.click();
document.head.removeChild(label);
} catch {
// ignore
}
}