You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
33 lines
1.5 KiB
33 lines
1.5 KiB
import { readActivePrefsSlot, type ActivePrefs } from './active-prefs.svelte';
|
|
import type { MotionEffective, MotionSource } from '$libs/motion';
|
|
|
|
/**
|
|
* The `motion` slot of an open `ActivePrefs`, as the `MotionSource` port the
|
|
* animation runtimes consume.
|
|
*
|
|
* This is the ONE place the effective reduced-motion value is decided for
|
|
* everything downstream: the motion engine, the scene engine, sema's haptic
|
|
* channel and every eidos component read it from here, so an explicit
|
|
* `motion: 'allow'` reaches them all and a `reduce` degrades them all. Asking
|
|
* the OS again (`ActiveDom.prefersReducedMotion`) is asking a question prefs
|
|
* has already answered — the media query is an ENVIRONMENT hint, and
|
|
* `resolveMotion` folded it with the user's intent long before this call.
|
|
*
|
|
* The slot is read DEFENSIVELY (the shape `dom-projection.ts` uses): an app is
|
|
* free to compose a schema without a `motion` dimension, and the answer then is
|
|
* `undefined` — no source, so each consumer keeps its own documented default
|
|
* rather than inventing a policy nobody declared.
|
|
*
|
|
* Reads go through `slot.get()` on every call: the slot is `$state`-backed, so
|
|
* a caller inside a `$derived` / `$effect` tracks it, and a caller outside one
|
|
* still sees the current value.
|
|
*/
|
|
export function createMotionSourceFromPrefs(prefs: ActivePrefs): MotionSource | undefined {
|
|
const slot = readActivePrefsSlot<MotionEffective>(prefs, 'motion');
|
|
if (!slot) return undefined;
|
|
return {
|
|
get: () => slot.get(),
|
|
onChange: (handler) => slot.onChange(handler)
|
|
};
|
|
}
|