@weasel-js/react 1.15.1 → 1.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +19 -4
- package/dist/index.js +24 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Ref, RefObject, ReactNode } from 'react';
|
|
2
|
-
export { isDev } from '@weasel-js/registry';
|
|
2
|
+
export { isDev, sameList } from '@weasel-js/registry';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Write `value` into a ref a caller passed in, whichever shape it has: call a
|
|
@@ -102,6 +102,23 @@ declare function prefersReducedMotion(): boolean;
|
|
|
102
102
|
/** {@link prefersReducedMotion}, re-rendering when the preference changes. */
|
|
103
103
|
declare function usePrefersReducedMotion(): boolean;
|
|
104
104
|
|
|
105
|
+
/** A color scheme the system can ask for. */
|
|
106
|
+
type ColorScheme = 'light' | 'dark';
|
|
107
|
+
/**
|
|
108
|
+
* The scheme where nothing says which: no `matchMedia`, a server render, or no
|
|
109
|
+
* stated preference. Dark, the mode weasel's tokens paint at `:root`.
|
|
110
|
+
*/
|
|
111
|
+
declare const DEFAULT_COLOR_SCHEME: ColorScheme;
|
|
112
|
+
/** The color scheme the system asks for, {@link DEFAULT_COLOR_SCHEME} where it says nothing. */
|
|
113
|
+
declare function systemColorScheme(): ColorScheme;
|
|
114
|
+
/** Call `fn` whenever the system color scheme changes. Returns the stop. */
|
|
115
|
+
declare function watchColorScheme(fn: () => void): () => void;
|
|
116
|
+
/**
|
|
117
|
+
* {@link systemColorScheme}, re-rendering when it changes. With `follow` false
|
|
118
|
+
* nothing is subscribed: for a caller whose user picked a scheme of their own.
|
|
119
|
+
*/
|
|
120
|
+
declare function useSystemColorScheme(follow?: boolean): ColorScheme;
|
|
121
|
+
|
|
105
122
|
/**
|
|
106
123
|
* The one place weasel decides whether a frame may run, so that "a loop does no
|
|
107
124
|
* work nobody can see" holds by construction rather than by every loop author
|
|
@@ -164,8 +181,6 @@ declare function useVisibleRaf(frame: (time: number, loop: VisibleRaf) => void,
|
|
|
164
181
|
* a render React throws away never becomes the one the next render keeps.
|
|
165
182
|
*/
|
|
166
183
|
declare function useStableByContent<T>(value: T, same: (a: T, b: T) => boolean): T;
|
|
167
|
-
/** Same length, and the same element at every index. */
|
|
168
|
-
declare function sameList<T>(a: readonly T[], b: readonly T[]): boolean;
|
|
169
184
|
|
|
170
185
|
/** A value a settings leaf can hold. */
|
|
171
186
|
type KitSettingLeaf = number | string | boolean;
|
|
@@ -248,4 +263,4 @@ interface KitSettings {
|
|
|
248
263
|
interface KitTargetSettings {
|
|
249
264
|
}
|
|
250
265
|
|
|
251
|
-
export { type AdoptedCss, type CanvasFocusReturn, type KitSettingAt, type KitSettingLeaf, type KitSettingPath, type KitSettings, type KitSettingsOverrides, KitSettingsProvider, type KitSettingsStore, type KitTargetSettings, NO_KIT_SETTINGS, type StyleRoot, type UseCanvasFocusOptions, type VisibleRaf, type VisibleRafOptions, type VisibleRafTarget, type WritableKitSettingsStore, __resetAdoptedCss, adoptCss, adoptCssOnce, assignRef, createKitSettingsStore, prefersReducedMotion,
|
|
266
|
+
export { type AdoptedCss, type CanvasFocusReturn, type ColorScheme, DEFAULT_COLOR_SCHEME, type KitSettingAt, type KitSettingLeaf, type KitSettingPath, type KitSettings, type KitSettingsOverrides, KitSettingsProvider, type KitSettingsStore, type KitTargetSettings, NO_KIT_SETTINGS, type StyleRoot, type UseCanvasFocusOptions, type VisibleRaf, type VisibleRafOptions, type VisibleRafTarget, type WritableKitSettingsStore, __resetAdoptedCss, adoptCss, adoptCssOnce, assignRef, createKitSettingsStore, prefersReducedMotion, styleRootOf, systemColorScheme, useCanvasFocus, useKitSetting, useKitSettings, useLatest, useNearViewport, usePrefersReducedMotion, useStableByContent, useSystemColorScheme, useVisibleRaf, watchColorScheme };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { createEmitter } from '@weasel-js/registry';
|
|
2
|
+
export { isDev, sameList } from '@weasel-js/registry';
|
|
2
3
|
import { createContext, useState, useRef, useCallback, useMemo, useInsertionEffect, useEffect, useSyncExternalStore, useLayoutEffect, useContext, createElement } from 'react';
|
|
3
4
|
|
|
4
5
|
// src/assignRef.ts
|
|
@@ -142,6 +143,23 @@ function subscribe(onChange) {
|
|
|
142
143
|
function usePrefersReducedMotion() {
|
|
143
144
|
return useSyncExternalStore(subscribe, prefersReducedMotion, () => false);
|
|
144
145
|
}
|
|
146
|
+
var DEFAULT_COLOR_SCHEME = "dark";
|
|
147
|
+
var QUERY2 = "(prefers-color-scheme: light)";
|
|
148
|
+
var query = () => typeof matchMedia === "function" ? matchMedia(QUERY2) : null;
|
|
149
|
+
function systemColorScheme() {
|
|
150
|
+
return query()?.matches ? "light" : DEFAULT_COLOR_SCHEME;
|
|
151
|
+
}
|
|
152
|
+
function watchColorScheme(fn) {
|
|
153
|
+
const list = query();
|
|
154
|
+
list?.addEventListener?.("change", fn);
|
|
155
|
+
return () => list?.removeEventListener?.("change", fn);
|
|
156
|
+
}
|
|
157
|
+
var still = () => () => {
|
|
158
|
+
};
|
|
159
|
+
var serverScheme = () => DEFAULT_COLOR_SCHEME;
|
|
160
|
+
function useSystemColorScheme(follow = true) {
|
|
161
|
+
return useSyncExternalStore(follow ? watchColorScheme : still, systemColorScheme, serverScheme);
|
|
162
|
+
}
|
|
145
163
|
var documentHidden = () => typeof document !== "undefined" && document.hidden;
|
|
146
164
|
var resolveTarget = (target) => typeof target === "function" ? target() : target?.current ?? null;
|
|
147
165
|
function useVisibleRaf(frame, options = {}) {
|
|
@@ -246,11 +264,6 @@ function useStableByContent(value, same) {
|
|
|
246
264
|
});
|
|
247
265
|
return out;
|
|
248
266
|
}
|
|
249
|
-
function sameList(a, b) {
|
|
250
|
-
return a.length === b.length && a.every((t, i) => t === b[i]);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// src/kitSettings.ts
|
|
254
267
|
function leafAt(tree, path) {
|
|
255
268
|
let at = tree;
|
|
256
269
|
for (const key of path.split(".")) {
|
|
@@ -273,10 +286,8 @@ var sameLeaves = (a, b) => a.size === b.size && [...a].every(([path, value]) =>
|
|
|
273
286
|
function createKitSettingsStore(overrides = {}, parent = null) {
|
|
274
287
|
let own = overrides;
|
|
275
288
|
let leaves = flatten(own, "", /* @__PURE__ */ new Map());
|
|
276
|
-
const listeners =
|
|
277
|
-
const notify = () =>
|
|
278
|
-
for (const listener of [...listeners]) listener();
|
|
279
|
-
};
|
|
289
|
+
const listeners = createEmitter();
|
|
290
|
+
const notify = () => listeners.emit();
|
|
280
291
|
let unhookParent = null;
|
|
281
292
|
return {
|
|
282
293
|
get(path) {
|
|
@@ -284,10 +295,10 @@ function createKitSettingsStore(overrides = {}, parent = null) {
|
|
|
284
295
|
return value !== void 0 ? value : parent?.get(path);
|
|
285
296
|
},
|
|
286
297
|
subscribe(listener) {
|
|
287
|
-
listeners.
|
|
298
|
+
const off = listeners.subscribe(listener);
|
|
288
299
|
if (parent && unhookParent === null) unhookParent = parent.subscribe(notify);
|
|
289
300
|
return () => {
|
|
290
|
-
|
|
301
|
+
off();
|
|
291
302
|
if (listeners.size === 0 && unhookParent) {
|
|
292
303
|
unhookParent();
|
|
293
304
|
unhookParent = null;
|
|
@@ -323,6 +334,6 @@ function useKitSetting(path) {
|
|
|
323
334
|
return useSyncExternalStore(store.subscribe, read, read);
|
|
324
335
|
}
|
|
325
336
|
|
|
326
|
-
export { KitSettingsProvider, NO_KIT_SETTINGS, __resetAdoptedCss, adoptCss, adoptCssOnce, assignRef, createKitSettingsStore, prefersReducedMotion,
|
|
337
|
+
export { DEFAULT_COLOR_SCHEME, KitSettingsProvider, NO_KIT_SETTINGS, __resetAdoptedCss, adoptCss, adoptCssOnce, assignRef, createKitSettingsStore, prefersReducedMotion, styleRootOf, systemColorScheme, useCanvasFocus, useKitSetting, useKitSettings, useLatest, useNearViewport, usePrefersReducedMotion, useStableByContent, useSystemColorScheme, useVisibleRaf, watchColorScheme };
|
|
327
338
|
//# sourceMappingURL=index.js.map
|
|
328
339
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/assignRef.ts","../src/adoptCss.ts","../src/useCanvasFocus.ts","../src/useLatest.ts","../src/useNearViewport.ts","../src/reducedMotion.ts","../src/useVisibleRaf.ts","../src/useStableByContent.ts","../src/kitSettings.ts","../src/useKitSetting.ts"],"names":["useRef","useState","useCallback","useMemo","held","useInsertionEffect","useLayoutEffect","useSyncExternalStore"],"mappings":"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","file":"index.js","sourcesContent":["import type { Ref } from 'react';\n\n/**\n * Write `value` into a ref a caller passed in, whichever shape it has: call a\n * callback ref, set an object ref's `current`, ignore `null` or `undefined`.\n *\n * For a component that keeps the element (or handle) for itself and also\n * forwards it to a ref prop — call this from the component's own callback ref.\n */\nexport function assignRef<T>(ref: Ref<T> | undefined, value: T | null): void {\n if (typeof ref === 'function') ref(value);\n else if (ref) (ref as { current: T | null }).current = value;\n}\n","/** Where an element's styles come from. */\nexport type StyleRoot = Document | ShadowRoot;\n\n/** The root `el`'s styles come from: its shadow root, else its document. */\nexport function styleRootOf(el: Element): StyleRoot {\n const root = el.getRootNode();\n return root instanceof (el.ownerDocument.defaultView?.ShadowRoot ?? ShadowRoot) ? (root as ShadowRoot) : el.ownerDocument;\n}\n\n/** A sheet {@link adoptCss} put in a root. */\nexport interface AdoptedCss {\n /** Replace the sheet's text. */\n write: (css: string) => void;\n /** Take the sheet out of its root. */\n release: () => void;\n}\n\n/**\n * A sheet of its own in `root`, holding `css`: an adopted constructed sheet\n * where the root takes one, else a `<style>`, with `styleId` as its id when\n * one is passed.\n */\nexport function adoptCss(root: StyleRoot, css: string, styleId?: string): AdoptedCss {\n const doc = 'host' in root ? root.ownerDocument : root;\n const Sheet = doc.defaultView?.CSSStyleSheet;\n if (Sheet && 'replaceSync' in Sheet.prototype && Array.isArray(root.adoptedStyleSheets)) {\n // From the root's own window: a constructed sheet cannot be adopted across documents.\n const sheet = new Sheet();\n sheet.replaceSync(css);\n root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];\n return {\n write: (next) => sheet.replaceSync(next),\n release: () => {\n root.adoptedStyleSheets = root.adoptedStyleSheets.filter((s) => s !== sheet);\n },\n };\n }\n const style = doc.createElement('style');\n if (styleId !== undefined) style.id = styleId;\n style.textContent = css;\n ('host' in root ? root : doc.head).appendChild(style);\n return {\n write: (next) => {\n style.textContent = next;\n },\n release: () => style.remove(),\n };\n}\n\nlet held = new WeakMap<StyleRoot, Set<string>>();\n\n/**\n * Hold `css` in the root `el`'s styles come from, once per root however many\n * elements ask, and never take it back out: for a component's fixed rules.\n */\nexport function adoptCssOnce(el: Element, css: string): void {\n const root = styleRootOf(el);\n let rules = held.get(root);\n if (rules === undefined) held.set(root, (rules = new Set()));\n if (rules.has(css)) return;\n rules.add(css);\n adoptCss(root, css);\n}\n\n/** Test seam: forgets which roots hold which rules. */\nexport function __resetAdoptedCss(): void {\n held = new WeakMap();\n}\n","/**\n * Track DOM focus on a canvas (or any focusable host element). Returns the\n * live `focused` boolean, props to spread onto the focusable element, and a\n * stable `getFocused()` getter for use inside per-frame closures (notably\n * `RenderLayer.draw`, which is constructed once but called every frame).\n *\n * No Canvas/SceneCanvas integration. Consumers compose the hook with core's\n * `gateLayer` (or their own gate) to hide affordances when blurred.\n */\nimport { useCallback, useMemo, useRef, useState } from 'react';\n\n/** Options for `useCanvasFocus`. */\nexport interface UseCanvasFocusOptions {\n /** Initial focus state. Default `false` — handles hidden until first click. */\n initial?: boolean;\n /** `tabIndex` to set on the focusable element. Default `0` (focusable in\n * document tab order). Set to `-1` for programmatic-only focus. */\n tabIndex?: number;\n}\n\n/** What `useCanvasFocus` hands back: the focus state, the props that\n * maintain it, and a getter for reading it from per-frame code. */\nexport interface CanvasFocusReturn {\n /** Live boolean — true when the element has DOM focus. Triggers re-renders\n * when the focus state changes (set via React state). */\n focused: boolean;\n /** Spread onto the focusable element (e.g. the canvas). Includes\n * `tabIndex`, `onFocus`, `onBlur`. */\n focusProps: {\n tabIndex: number;\n onFocus: () => void;\n onBlur: () => void;\n };\n /** Stable getter for the live focused state. Use inside per-frame closures\n * like `RenderLayer.draw`, where the layer is constructed once and\n * reading `focused` directly would capture the value at construction\n * time. The getter always returns the current value. */\n getFocused: () => boolean;\n /** Imperative focus / blur. Useful for tests and programmatic control. */\n setFocused: (next: boolean) => void;\n}\n\n/** Track DOM focus on the canvas, so chrome that should only show while the\n * canvas is focused can be gated on it. */\nexport function useCanvasFocus(options: UseCanvasFocusOptions = {}): CanvasFocusReturn {\n const { initial = false, tabIndex = 0 } = options;\n const [focused, setFocusedState] = useState<boolean>(initial);\n // Every state change goes through a handler below that writes this first.\n const focusedRef = useRef<boolean>(initial);\n\n const onFocus = useCallback(() => {\n if (!focusedRef.current) {\n focusedRef.current = true;\n setFocusedState(true);\n }\n }, []);\n const onBlur = useCallback(() => {\n if (focusedRef.current) {\n focusedRef.current = false;\n setFocusedState(false);\n }\n }, []);\n const getFocused = useCallback(() => focusedRef.current, []);\n const setFocused = useCallback((next: boolean) => {\n if (focusedRef.current === next) return;\n focusedRef.current = next;\n setFocusedState(next);\n }, []);\n\n // Stable until tabIndex changes, so it can be spread onto memoized elements\n // without retriggering downstream effects.\n const focusProps = useMemo(() => ({ tabIndex, onFocus, onBlur }), [tabIndex, onFocus, onBlur]);\n\n return {\n focused,\n focusProps,\n getFocused,\n setFocused,\n };\n}\n","import { useInsertionEffect, useRef } from 'react';\n\n/**\n * A ref holding the `value` of the last **committed** render, for event\n * handlers, frame loops and other code that runs outside render and must not\n * re-subscribe every time `value` changes.\n *\n * Writing `ref.current = value` in a render body is not the same thing: React\n * may start a render and throw it away (a transition that suspends, a render\n * interrupted by a more urgent update), and the value that render computed\n * would stay behind for the next event to read. This writes from\n * `useInsertionEffect`, so only a render that commits ever lands, and it lands\n * before any layout effect in that commit runs.\n *\n * Don't read `.current` during render expecting this render's value: until the\n * commit it holds the previous committed one (on mount, this first `value`).\n * Render code should use `value` itself.\n */\nexport function useLatest<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useInsertionEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n","import { type RefObject, useEffect, useState } from 'react';\n\n/**\n * Whether `ref`'s element lies within `margin` (a CSS `rootMargin`) of its document's viewport; true where the\n * browser cannot say. It follows the element both ways: it turns false again once the element moves away.\n * Decided by where the element is, not by whether any of it shows: `isIntersecting` is false too for an element an\n * `overflow` ancestor clips away, as a narrow tile does to its content while the tiling changes. `initial` is\n * the answer before the browser has given one: false for content too costly to mount everywhere at once.\n */\nexport function useNearViewport(ref: RefObject<Element | null>, margin: string, initial = true): boolean {\n const [near, setNear] = useState(() => initial || typeof IntersectionObserver === 'undefined');\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === 'undefined') return;\n // A fresh observe always reports, wherever the element is.\n const recheck = () => {\n observer.unobserve(el);\n observer.observe(el);\n };\n let watching = false;\n const watch = (on: boolean) => {\n if (on === watching) return;\n watching = on;\n if (on) {\n document.addEventListener('scroll', recheck, { capture: true, passive: true });\n window.addEventListener('resize', recheck);\n } else {\n document.removeEventListener('scroll', recheck, { capture: true });\n window.removeEventListener('resize', recheck);\n }\n };\n const observer = new IntersectionObserver(\n (entries) => {\n const last = entries.at(-1);\n if (!last) return;\n const root = last.rootBounds;\n const box = last.boundingClientRect;\n setNear(\n root\n ? box.bottom >= root.top && box.top <= root.bottom && box.right >= root.left && box.left <= root.right\n : last.isIntersecting,\n );\n // While nothing of it intersects, a clip can hide every move from the observer, so ask again on each scroll.\n watch(Boolean(root) && !last.isIntersecting);\n },\n { root: document, rootMargin: margin },\n );\n observer.observe(el);\n return () => {\n observer.disconnect();\n watch(false);\n };\n }, [ref, margin]);\n return near;\n}\n","import { useSyncExternalStore } from 'react';\n\nconst QUERY = '(prefers-reduced-motion: reduce)';\n\n/** Whether the reader has asked for less motion. False where there is no `matchMedia` to ask. */\nexport function prefersReducedMotion(): boolean {\n return typeof matchMedia === 'function' && matchMedia(QUERY).matches;\n}\n\nfunction subscribe(onChange: () => void): () => void {\n if (typeof matchMedia !== 'function') return () => {};\n const list = matchMedia(QUERY);\n list.addEventListener?.('change', onChange);\n return () => list.removeEventListener?.('change', onChange);\n}\n\n/** {@link prefersReducedMotion}, re-rendering when the preference changes. */\nexport function usePrefersReducedMotion(): boolean {\n return useSyncExternalStore(subscribe, prefersReducedMotion, () => false);\n}\n","/**\n * The one place weasel decides whether a frame may run, so that \"a loop does no\n * work nobody can see\" holds by construction rather than by every loop author\n * remembering. Nothing runs while the document is hidden; a loop that names an\n * element also stops while that element is outside the viewport.\n *\n * A request made while suspended is held, not dropped, and re-armed when the\n * surface comes back — so a loop never needs to poll visibility itself.\n */\n\nimport { type RefObject, useCallback, useLayoutEffect, useMemo, useRef } from 'react';\nimport { useLatest } from './useLatest';\n\n/** What a loop names as its element, resolved every time the gate is consulted\n * so a ref filled in after mount still starts the observer. */\nexport type VisibleRafTarget = RefObject<Element | null> | (() => Element | null);\n\n/** Options for {@link useVisibleRaf}. */\nexport interface VisibleRafOptions {\n /** Also gate on this element's intersection with the viewport. Omit it and\n * the loop is gated on document visibility alone. */\n target?: VisibleRafTarget;\n /**\n * Run frames regardless of visibility. The escape hatch for a loop that is\n * not painting for a viewer — an offscreen recording, an export driving its\n * own frames. It is the wrong fix for a loop that stalls after a tab switch:\n * that is a missing `request()`, and setting this hides it.\n */\n dangerouslyRunWhenHidden?: boolean;\n /** Frame clock. Read live, so a test may inject one after mount. */\n requestFrame?: (cb: FrameRequestCallback) => number;\n cancelFrame?: (handle: number) => void;\n /**\n * Runs once when the loop leaves suspension, before the frame that follows\n * it. Where a loop that measures elapsed time drops the interval it spent\n * suspended — without it, an hour hidden arrives as one hour-long frame.\n */\n onResume?: () => void;\n}\n\n/** The handle {@link useVisibleRaf} returns. Nothing runs until `request()` is called. */\nexport interface VisibleRaf {\n /** Ask for a frame. Idempotent while one is outstanding, and held rather than\n * dropped while suspended. Identity is stable for the component's lifetime. */\n request(): void;\n /** Drop the outstanding request, held or scheduled. */\n cancel(): void;\n /** Whether a frame would run right now. */\n isVisible(): boolean;\n}\n\nconst documentHidden = (): boolean => typeof document !== 'undefined' && document.hidden;\n\nconst resolveTarget = (target: VisibleRafTarget | undefined): Element | null =>\n typeof target === 'function' ? target() : (target?.current ?? null);\n\n/**\n * A `requestAnimationFrame` loop that runs only while someone can see it: never while the document is hidden,\n * and, given a `target`, never while that element is outside the viewport. Nothing runs until `request()`.\n *\n * @param frame Runs on the frame, with the timestamp the clock supplied and the\n * loop's own handle. A continuous loop calls `loop.request()` from inside it;\n * a one-shot loop does not.\n */\nexport function useVisibleRaf(\n frame: (time: number, loop: VisibleRaf) => void,\n options: VisibleRafOptions = {},\n): VisibleRaf {\n const frameRef = useLatest(frame);\n const optsRef = useLatest(options);\n\n const rafRef = useRef<number | null>(null);\n const pendingRef = useRef(false);\n const suspendedRef = useRef(false);\n const aliveRef = useRef(true);\n const intersectingRef = useRef(true);\n const observerRef = useRef<IntersectionObserver | null>(null);\n const observedRef = useRef<Element | null>(null);\n const handleRef = useRef<VisibleRaf | null>(null);\n\n const isVisible = useCallback(\n (): boolean =>\n optsRef.current.dangerouslyRunWhenHidden === true ||\n (!documentHidden() && intersectingRef.current),\n [optsRef],\n );\n\n const cancelRaf = useCallback(() => {\n if (rafRef.current === null) return;\n (optsRef.current.cancelFrame ?? cancelAnimationFrame)(rafRef.current);\n rafRef.current = null;\n }, [optsRef]);\n\n const scheduleRaf = useCallback(() => {\n if (rafRef.current !== null || !aliveRef.current) return;\n rafRef.current = (optsRef.current.requestFrame ?? requestAnimationFrame)((time) => {\n // Released before the frame runs, so a `request()` issued from inside it\n // schedules the next one instead of being swallowed.\n rafRef.current = null;\n if (!aliveRef.current || !pendingRef.current || !isVisible()) return;\n pendingRef.current = false;\n frameRef.current(time, handleRef.current!);\n });\n }, [frameRef, isVisible, optsRef]);\n\n /** Attaches the observer once the named element exists. Ref callbacks and\n * lazily-mounted hosts fill their refs after this hook's effect has run. */\n const syncObserver = useCallback(() => {\n const io = observerRef.current;\n if (!io) return;\n const el = resolveTarget(optsRef.current.target);\n if (el === observedRef.current) return;\n if (observedRef.current) io.unobserve(observedRef.current);\n observedRef.current = el;\n // An element that has gone away stops gating rather than stopping the loop.\n intersectingRef.current = true;\n if (el) io.observe(el);\n }, [optsRef]);\n\n /** Re-reads the gate after something that could have changed it. Suspending\n * drops the outstanding frame but keeps the request; resuming hands the\n * loop its rebase before re-arming. */\n const syncGate = useCallback(() => {\n if (!isVisible()) {\n if (suspendedRef.current) return;\n suspendedRef.current = true;\n cancelRaf();\n return;\n }\n if (!suspendedRef.current) return;\n suspendedRef.current = false;\n if (!aliveRef.current) return;\n optsRef.current.onResume?.();\n if (pendingRef.current) scheduleRaf();\n }, [cancelRaf, isVisible, optsRef, scheduleRaf]);\n\n const request = useCallback(() => {\n if (!aliveRef.current) return;\n syncObserver();\n pendingRef.current = true;\n if (!isVisible()) {\n suspendedRef.current = true;\n return;\n }\n scheduleRaf();\n }, [isVisible, scheduleRaf, syncObserver]);\n\n const cancel = useCallback(() => {\n pendingRef.current = false;\n cancelRaf();\n }, [cancelRaf]);\n\n // Re-armed in setup because the refs survive StrictMode's simulated remount, and\n // a layout effect so the arming precedes the host's own layout effects — a loop\n // driven from one of those would otherwise be declined on the second mount.\n useLayoutEffect(() => {\n aliveRef.current = true;\n document.addEventListener('visibilitychange', syncGate);\n if (typeof IntersectionObserver !== 'undefined') {\n observerRef.current = new IntersectionObserver((entries) => {\n intersectingRef.current = entries.some((e) => e.isIntersecting);\n syncGate();\n });\n syncObserver();\n }\n return () => {\n aliveRef.current = false;\n document.removeEventListener('visibilitychange', syncGate);\n observerRef.current?.disconnect();\n observerRef.current = null;\n observedRef.current = null;\n intersectingRef.current = true;\n cancelRaf();\n pendingRef.current = false;\n suspendedRef.current = false;\n };\n }, [cancelRaf, syncGate, syncObserver]);\n\n // Stable identity: `requestRedraw` and friends outlive the render that made\n // them, and consumers capture the handle.\n const handle = useMemo(() => ({ request, cancel, isVisible }), [request, cancel, isVisible]);\n useLayoutEffect(() => { handleRef.current = handle; }, [handle]);\n return handle;\n}\n","import { useInsertionEffect, useRef } from 'react';\n\n/**\n * `value`, or the one the last committed render returned when `same` says the\n * two are equal — so a caller rebuilding an equivalent object every render\n * hands its readers one identity. Compared against the committed value only:\n * a render React throws away never becomes the one the next render keeps.\n */\nexport function useStableByContent<T>(value: T, same: (a: T, b: T) => boolean): T {\n const held = useRef(value);\n const out = held.current === value || same(held.current, value) ? held.current : value;\n useInsertionEffect(() => {\n held.current = out;\n });\n return out;\n}\n\n/** Same length, and the same element at every index. */\nexport function sameList<T>(a: readonly T[], b: readonly T[]): boolean {\n return a.length === b.length && a.every((t, i) => t === b[i]);\n}\n","import type { KitSettings } from './index';\n\n/** A value a settings leaf can hold. */\nexport type KitSettingLeaf = number | string | boolean;\n\n/** `T` with every area and leaf optional: what a provider sets. */\nexport type KitSettingsOverrides<T = KitSettings> = {\n [K in keyof T]?: T[K] extends KitSettingLeaf ? T[K] : KitSettingsOverrides<T[K]>;\n};\n\n/** The dotted path of every leaf in `T`: `'tree.typeaheadMs'`. */\nexport type KitSettingPath<T = KitSettings> = {\n [K in keyof T & string]: T[K] extends KitSettingLeaf ? K : `${K}.${KitSettingPath<T[K]>}`;\n}[keyof T & string];\n\n/** The type of the leaf at `P` in `T`. */\nexport type KitSettingAt<P extends string, T = KitSettings> = P extends `${infer H}.${infer R}`\n ? H extends keyof T ? KitSettingAt<R, T[H]> : never\n : P extends keyof T ? T[P] : never;\n\n/** Reads settings by path and says when any of them changed. */\nexport interface KitSettingsStore<T = KitSettings> {\n /** The value at `path`: this store's own, else the nearest ancestor's, else `undefined`. */\n get<P extends KitSettingPath<T>>(path: P): KitSettingAt<P, T> | undefined;\n subscribe(listener: () => void): () => void;\n}\n\n/** A store whose own overrides can be replaced. */\nexport interface WritableKitSettingsStore<T = KitSettings> extends KitSettingsStore<T> {\n /** Replace this store's own overrides, notifying only when some leaf's value changed. */\n set(overrides: KitSettingsOverrides<T>): void;\n}\n\nfunction leafAt(tree: unknown, path: string): unknown {\n let at = tree;\n for (const key of path.split('.')) {\n if (at === null || typeof at !== 'object' || !Object.hasOwn(at, key)) return undefined;\n at = (at as Record<string, unknown>)[key];\n }\n return at;\n}\n\nfunction flatten(tree: unknown, prefix: string, out: Map<string, unknown>): Map<string, unknown> {\n if (tree === null || typeof tree !== 'object') return out;\n for (const [key, value] of Object.entries(tree)) {\n if (value === undefined) continue;\n const path = prefix === '' ? key : `${prefix}.${key}`;\n if (value !== null && typeof value === 'object') flatten(value, path, out);\n else out.set(path, value);\n }\n return out;\n}\n\nconst sameLeaves = (a: Map<string, unknown>, b: Map<string, unknown>): boolean =>\n a.size === b.size && [...a].every(([path, value]) => b.has(path) && Object.is(b.get(path), value));\n\n/**\n * A settings store holding `overrides`, reading through to `parent` for any\n * leaf it does not set. A change in the parent notifies this store's\n * listeners too.\n */\nexport function createKitSettingsStore<T = KitSettings>(\n overrides: KitSettingsOverrides<T> = {},\n parent: KitSettingsStore<T> | null = null,\n): WritableKitSettingsStore<T> {\n let own: unknown = overrides;\n let leaves = flatten(own, '', new Map());\n const listeners = new Set<() => void>();\n const notify = (): void => {\n for (const listener of [...listeners]) listener();\n };\n let unhookParent: (() => void) | null = null;\n\n return {\n get(path) {\n const value = leafAt(own, path);\n return (value !== undefined ? value : parent?.get(path)) as never;\n },\n subscribe(listener) {\n listeners.add(listener);\n if (parent && unhookParent === null) unhookParent = parent.subscribe(notify);\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0 && unhookParent) {\n unhookParent();\n unhookParent = null;\n }\n };\n },\n set(next) {\n const nextLeaves = flatten(next, '', new Map());\n own = next;\n if (sameLeaves(leaves, nextLeaves)) return;\n leaves = nextLeaves;\n notify();\n },\n };\n}\n\n/** The store read where no provider is mounted: it sets nothing. */\nexport const NO_KIT_SETTINGS: KitSettingsStore = createKitSettingsStore();\n","import { createContext, createElement, useCallback, useContext, useLayoutEffect, useState, useSyncExternalStore, type ReactNode } from 'react';\nimport {\n createKitSettingsStore,\n NO_KIT_SETTINGS,\n type KitSettingAt,\n type KitSettingPath,\n type KitSettingsOverrides,\n type KitSettingsStore,\n} from './kitSettings';\n\nconst KitSettingsContext = createContext<KitSettingsStore>(NO_KIT_SETTINGS);\n\n/**\n * Set kit settings for everything inside. Nested providers merge: a leaf this\n * one leaves unset reads through to the one above.\n */\nexport function KitSettingsProvider({ value, children }: { value: KitSettingsOverrides; children?: ReactNode }): ReactNode {\n const parent = useContext(KitSettingsContext);\n const [store, setStore] = useState(() => ({ parent, store: createKitSettingsStore(value, parent) }));\n if (store.parent !== parent) setStore({ parent, store: createKitSettingsStore(value, parent) });\n useLayoutEffect(() => {\n store.store.set(value);\n }, [store, value]);\n return createElement(KitSettingsContext.Provider, { value: store.store }, children);\n}\n\n/**\n * The store the nearest provider holds, for code outside React — a gesture\n * recognizer, a canvas action — to read with `get(path)` at the moment it\n * needs a value.\n */\nexport function useKitSettings(): KitSettingsStore {\n return useContext(KitSettingsContext);\n}\n\n/**\n * The setting at `path`, or `undefined` where no provider sets it; the caller\n * falls back to its own constant. Re-renders only when this path's value changes.\n */\nexport function useKitSetting<P extends KitSettingPath>(path: P): KitSettingAt<P> | undefined {\n const store = useKitSettings();\n const read = useCallback(() => store.get(path), [store, path]);\n return useSyncExternalStore(store.subscribe, read, read);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/assignRef.ts","../src/adoptCss.ts","../src/useCanvasFocus.ts","../src/useLatest.ts","../src/useNearViewport.ts","../src/reducedMotion.ts","../src/colorScheme.ts","../src/useVisibleRaf.ts","../src/useStableByContent.ts","../src/kitSettings.ts","../src/useKitSetting.ts"],"names":["useRef","useState","QUERY","useSyncExternalStore","useCallback","useMemo","held","useInsertionEffect","useLayoutEffect"],"mappings":"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","file":"index.js","sourcesContent":["import type { Ref } from 'react';\n\n/**\n * Write `value` into a ref a caller passed in, whichever shape it has: call a\n * callback ref, set an object ref's `current`, ignore `null` or `undefined`.\n *\n * For a component that keeps the element (or handle) for itself and also\n * forwards it to a ref prop — call this from the component's own callback ref.\n */\nexport function assignRef<T>(ref: Ref<T> | undefined, value: T | null): void {\n if (typeof ref === 'function') ref(value);\n else if (ref) (ref as { current: T | null }).current = value;\n}\n","/** Where an element's styles come from. */\nexport type StyleRoot = Document | ShadowRoot;\n\n/** The root `el`'s styles come from: its shadow root, else its document. */\nexport function styleRootOf(el: Element): StyleRoot {\n const root = el.getRootNode();\n return root instanceof (el.ownerDocument.defaultView?.ShadowRoot ?? ShadowRoot) ? (root as ShadowRoot) : el.ownerDocument;\n}\n\n/** A sheet {@link adoptCss} put in a root. */\nexport interface AdoptedCss {\n /** Replace the sheet's text. */\n write: (css: string) => void;\n /** Take the sheet out of its root. */\n release: () => void;\n}\n\n/**\n * A sheet of its own in `root`, holding `css`: an adopted constructed sheet\n * where the root takes one, else a `<style>`, with `styleId` as its id when\n * one is passed.\n */\nexport function adoptCss(root: StyleRoot, css: string, styleId?: string): AdoptedCss {\n const doc = 'host' in root ? root.ownerDocument : root;\n const Sheet = doc.defaultView?.CSSStyleSheet;\n if (Sheet && 'replaceSync' in Sheet.prototype && Array.isArray(root.adoptedStyleSheets)) {\n // From the root's own window: a constructed sheet cannot be adopted across documents.\n const sheet = new Sheet();\n sheet.replaceSync(css);\n root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];\n return {\n write: (next) => sheet.replaceSync(next),\n release: () => {\n root.adoptedStyleSheets = root.adoptedStyleSheets.filter((s) => s !== sheet);\n },\n };\n }\n const style = doc.createElement('style');\n if (styleId !== undefined) style.id = styleId;\n style.textContent = css;\n ('host' in root ? root : doc.head).appendChild(style);\n return {\n write: (next) => {\n style.textContent = next;\n },\n release: () => style.remove(),\n };\n}\n\nlet held = new WeakMap<StyleRoot, Set<string>>();\n\n/**\n * Hold `css` in the root `el`'s styles come from, once per root however many\n * elements ask, and never take it back out: for a component's fixed rules.\n */\nexport function adoptCssOnce(el: Element, css: string): void {\n const root = styleRootOf(el);\n let rules = held.get(root);\n if (rules === undefined) held.set(root, (rules = new Set()));\n if (rules.has(css)) return;\n rules.add(css);\n adoptCss(root, css);\n}\n\n/** Test seam: forgets which roots hold which rules. */\nexport function __resetAdoptedCss(): void {\n held = new WeakMap();\n}\n","/**\n * Track DOM focus on a canvas (or any focusable host element). Returns the\n * live `focused` boolean, props to spread onto the focusable element, and a\n * stable `getFocused()` getter for use inside per-frame closures (notably\n * `RenderLayer.draw`, which is constructed once but called every frame).\n *\n * No Canvas/SceneCanvas integration. Consumers compose the hook with core's\n * `gateLayer` (or their own gate) to hide affordances when blurred.\n */\nimport { useCallback, useMemo, useRef, useState } from 'react';\n\n/** Options for `useCanvasFocus`. */\nexport interface UseCanvasFocusOptions {\n /** Initial focus state. Default `false` — handles hidden until first click. */\n initial?: boolean;\n /** `tabIndex` to set on the focusable element. Default `0` (focusable in\n * document tab order). Set to `-1` for programmatic-only focus. */\n tabIndex?: number;\n}\n\n/** What `useCanvasFocus` hands back: the focus state, the props that\n * maintain it, and a getter for reading it from per-frame code. */\nexport interface CanvasFocusReturn {\n /** Live boolean — true when the element has DOM focus. Triggers re-renders\n * when the focus state changes (set via React state). */\n focused: boolean;\n /** Spread onto the focusable element (e.g. the canvas). Includes\n * `tabIndex`, `onFocus`, `onBlur`. */\n focusProps: {\n tabIndex: number;\n onFocus: () => void;\n onBlur: () => void;\n };\n /** Stable getter for the live focused state. Use inside per-frame closures\n * like `RenderLayer.draw`, where the layer is constructed once and\n * reading `focused` directly would capture the value at construction\n * time. The getter always returns the current value. */\n getFocused: () => boolean;\n /** Imperative focus / blur. Useful for tests and programmatic control. */\n setFocused: (next: boolean) => void;\n}\n\n/** Track DOM focus on the canvas, so chrome that should only show while the\n * canvas is focused can be gated on it. */\nexport function useCanvasFocus(options: UseCanvasFocusOptions = {}): CanvasFocusReturn {\n const { initial = false, tabIndex = 0 } = options;\n const [focused, setFocusedState] = useState<boolean>(initial);\n // Every state change goes through a handler below that writes this first.\n const focusedRef = useRef<boolean>(initial);\n\n const onFocus = useCallback(() => {\n if (!focusedRef.current) {\n focusedRef.current = true;\n setFocusedState(true);\n }\n }, []);\n const onBlur = useCallback(() => {\n if (focusedRef.current) {\n focusedRef.current = false;\n setFocusedState(false);\n }\n }, []);\n const getFocused = useCallback(() => focusedRef.current, []);\n const setFocused = useCallback((next: boolean) => {\n if (focusedRef.current === next) return;\n focusedRef.current = next;\n setFocusedState(next);\n }, []);\n\n // Stable until tabIndex changes, so it can be spread onto memoized elements\n // without retriggering downstream effects.\n const focusProps = useMemo(() => ({ tabIndex, onFocus, onBlur }), [tabIndex, onFocus, onBlur]);\n\n return {\n focused,\n focusProps,\n getFocused,\n setFocused,\n };\n}\n","import { useInsertionEffect, useRef } from 'react';\n\n/**\n * A ref holding the `value` of the last **committed** render, for event\n * handlers, frame loops and other code that runs outside render and must not\n * re-subscribe every time `value` changes.\n *\n * Writing `ref.current = value` in a render body is not the same thing: React\n * may start a render and throw it away (a transition that suspends, a render\n * interrupted by a more urgent update), and the value that render computed\n * would stay behind for the next event to read. This writes from\n * `useInsertionEffect`, so only a render that commits ever lands, and it lands\n * before any layout effect in that commit runs.\n *\n * Don't read `.current` during render expecting this render's value: until the\n * commit it holds the previous committed one (on mount, this first `value`).\n * Render code should use `value` itself.\n */\nexport function useLatest<T>(value: T): { readonly current: T } {\n const ref = useRef(value);\n useInsertionEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n","import { type RefObject, useEffect, useState } from 'react';\n\n/**\n * Whether `ref`'s element lies within `margin` (a CSS `rootMargin`) of its document's viewport; true where the\n * browser cannot say. It follows the element both ways: it turns false again once the element moves away.\n * Decided by where the element is, not by whether any of it shows: `isIntersecting` is false too for an element an\n * `overflow` ancestor clips away, as a narrow tile does to its content while the tiling changes. `initial` is\n * the answer before the browser has given one: false for content too costly to mount everywhere at once.\n */\nexport function useNearViewport(ref: RefObject<Element | null>, margin: string, initial = true): boolean {\n const [near, setNear] = useState(() => initial || typeof IntersectionObserver === 'undefined');\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === 'undefined') return;\n // A fresh observe always reports, wherever the element is.\n const recheck = () => {\n observer.unobserve(el);\n observer.observe(el);\n };\n let watching = false;\n const watch = (on: boolean) => {\n if (on === watching) return;\n watching = on;\n if (on) {\n document.addEventListener('scroll', recheck, { capture: true, passive: true });\n window.addEventListener('resize', recheck);\n } else {\n document.removeEventListener('scroll', recheck, { capture: true });\n window.removeEventListener('resize', recheck);\n }\n };\n const observer = new IntersectionObserver(\n (entries) => {\n const last = entries.at(-1);\n if (!last) return;\n const root = last.rootBounds;\n const box = last.boundingClientRect;\n setNear(\n root\n ? box.bottom >= root.top && box.top <= root.bottom && box.right >= root.left && box.left <= root.right\n : last.isIntersecting,\n );\n // While nothing of it intersects, a clip can hide every move from the observer, so ask again on each scroll.\n watch(Boolean(root) && !last.isIntersecting);\n },\n { root: document, rootMargin: margin },\n );\n observer.observe(el);\n return () => {\n observer.disconnect();\n watch(false);\n };\n }, [ref, margin]);\n return near;\n}\n","import { useSyncExternalStore } from 'react';\n\nconst QUERY = '(prefers-reduced-motion: reduce)';\n\n/** Whether the reader has asked for less motion. False where there is no `matchMedia` to ask. */\nexport function prefersReducedMotion(): boolean {\n return typeof matchMedia === 'function' && matchMedia(QUERY).matches;\n}\n\nfunction subscribe(onChange: () => void): () => void {\n if (typeof matchMedia !== 'function') return () => {};\n const list = matchMedia(QUERY);\n list.addEventListener?.('change', onChange);\n return () => list.removeEventListener?.('change', onChange);\n}\n\n/** {@link prefersReducedMotion}, re-rendering when the preference changes. */\nexport function usePrefersReducedMotion(): boolean {\n return useSyncExternalStore(subscribe, prefersReducedMotion, () => false);\n}\n","import { useSyncExternalStore } from 'react';\n\n/** A color scheme the system can ask for. */\nexport type ColorScheme = 'light' | 'dark';\n\n/**\n * The scheme where nothing says which: no `matchMedia`, a server render, or no\n * stated preference. Dark, the mode weasel's tokens paint at `:root`.\n */\nexport const DEFAULT_COLOR_SCHEME: ColorScheme = 'dark';\n\n// Asking for light makes \"no preference\" fall to the default, as no `matchMedia` does.\nconst QUERY = '(prefers-color-scheme: light)';\n\nconst query = (): MediaQueryList | null => (typeof matchMedia === 'function' ? matchMedia(QUERY) : null);\n\n/** The color scheme the system asks for, {@link DEFAULT_COLOR_SCHEME} where it says nothing. */\nexport function systemColorScheme(): ColorScheme {\n return query()?.matches ? 'light' : DEFAULT_COLOR_SCHEME;\n}\n\n/** Call `fn` whenever the system color scheme changes. Returns the stop. */\nexport function watchColorScheme(fn: () => void): () => void {\n const list = query();\n list?.addEventListener?.('change', fn);\n return () => list?.removeEventListener?.('change', fn);\n}\n\nconst still = () => () => {};\nconst serverScheme = (): ColorScheme => DEFAULT_COLOR_SCHEME;\n\n/**\n * {@link systemColorScheme}, re-rendering when it changes. With `follow` false\n * nothing is subscribed: for a caller whose user picked a scheme of their own.\n */\nexport function useSystemColorScheme(follow = true): ColorScheme {\n return useSyncExternalStore(follow ? watchColorScheme : still, systemColorScheme, serverScheme);\n}\n","/**\n * The one place weasel decides whether a frame may run, so that \"a loop does no\n * work nobody can see\" holds by construction rather than by every loop author\n * remembering. Nothing runs while the document is hidden; a loop that names an\n * element also stops while that element is outside the viewport.\n *\n * A request made while suspended is held, not dropped, and re-armed when the\n * surface comes back — so a loop never needs to poll visibility itself.\n */\n\nimport { type RefObject, useCallback, useLayoutEffect, useMemo, useRef } from 'react';\nimport { useLatest } from './useLatest';\n\n/** What a loop names as its element, resolved every time the gate is consulted\n * so a ref filled in after mount still starts the observer. */\nexport type VisibleRafTarget = RefObject<Element | null> | (() => Element | null);\n\n/** Options for {@link useVisibleRaf}. */\nexport interface VisibleRafOptions {\n /** Also gate on this element's intersection with the viewport. Omit it and\n * the loop is gated on document visibility alone. */\n target?: VisibleRafTarget;\n /**\n * Run frames regardless of visibility. The escape hatch for a loop that is\n * not painting for a viewer — an offscreen recording, an export driving its\n * own frames. It is the wrong fix for a loop that stalls after a tab switch:\n * that is a missing `request()`, and setting this hides it.\n */\n dangerouslyRunWhenHidden?: boolean;\n /** Frame clock. Read live, so a test may inject one after mount. */\n requestFrame?: (cb: FrameRequestCallback) => number;\n cancelFrame?: (handle: number) => void;\n /**\n * Runs once when the loop leaves suspension, before the frame that follows\n * it. Where a loop that measures elapsed time drops the interval it spent\n * suspended — without it, an hour hidden arrives as one hour-long frame.\n */\n onResume?: () => void;\n}\n\n/** The handle {@link useVisibleRaf} returns. Nothing runs until `request()` is called. */\nexport interface VisibleRaf {\n /** Ask for a frame. Idempotent while one is outstanding, and held rather than\n * dropped while suspended. Identity is stable for the component's lifetime. */\n request(): void;\n /** Drop the outstanding request, held or scheduled. */\n cancel(): void;\n /** Whether a frame would run right now. */\n isVisible(): boolean;\n}\n\nconst documentHidden = (): boolean => typeof document !== 'undefined' && document.hidden;\n\nconst resolveTarget = (target: VisibleRafTarget | undefined): Element | null =>\n typeof target === 'function' ? target() : (target?.current ?? null);\n\n/**\n * A `requestAnimationFrame` loop that runs only while someone can see it: never while the document is hidden,\n * and, given a `target`, never while that element is outside the viewport. Nothing runs until `request()`.\n *\n * @param frame Runs on the frame, with the timestamp the clock supplied and the\n * loop's own handle. A continuous loop calls `loop.request()` from inside it;\n * a one-shot loop does not.\n */\nexport function useVisibleRaf(\n frame: (time: number, loop: VisibleRaf) => void,\n options: VisibleRafOptions = {},\n): VisibleRaf {\n const frameRef = useLatest(frame);\n const optsRef = useLatest(options);\n\n const rafRef = useRef<number | null>(null);\n const pendingRef = useRef(false);\n const suspendedRef = useRef(false);\n const aliveRef = useRef(true);\n const intersectingRef = useRef(true);\n const observerRef = useRef<IntersectionObserver | null>(null);\n const observedRef = useRef<Element | null>(null);\n const handleRef = useRef<VisibleRaf | null>(null);\n\n const isVisible = useCallback(\n (): boolean =>\n optsRef.current.dangerouslyRunWhenHidden === true ||\n (!documentHidden() && intersectingRef.current),\n [optsRef],\n );\n\n const cancelRaf = useCallback(() => {\n if (rafRef.current === null) return;\n (optsRef.current.cancelFrame ?? cancelAnimationFrame)(rafRef.current);\n rafRef.current = null;\n }, [optsRef]);\n\n const scheduleRaf = useCallback(() => {\n if (rafRef.current !== null || !aliveRef.current) return;\n rafRef.current = (optsRef.current.requestFrame ?? requestAnimationFrame)((time) => {\n // Released before the frame runs, so a `request()` issued from inside it\n // schedules the next one instead of being swallowed.\n rafRef.current = null;\n if (!aliveRef.current || !pendingRef.current || !isVisible()) return;\n pendingRef.current = false;\n frameRef.current(time, handleRef.current!);\n });\n }, [frameRef, isVisible, optsRef]);\n\n /** Attaches the observer once the named element exists. Ref callbacks and\n * lazily-mounted hosts fill their refs after this hook's effect has run. */\n const syncObserver = useCallback(() => {\n const io = observerRef.current;\n if (!io) return;\n const el = resolveTarget(optsRef.current.target);\n if (el === observedRef.current) return;\n if (observedRef.current) io.unobserve(observedRef.current);\n observedRef.current = el;\n // An element that has gone away stops gating rather than stopping the loop.\n intersectingRef.current = true;\n if (el) io.observe(el);\n }, [optsRef]);\n\n /** Re-reads the gate after something that could have changed it. Suspending\n * drops the outstanding frame but keeps the request; resuming hands the\n * loop its rebase before re-arming. */\n const syncGate = useCallback(() => {\n if (!isVisible()) {\n if (suspendedRef.current) return;\n suspendedRef.current = true;\n cancelRaf();\n return;\n }\n if (!suspendedRef.current) return;\n suspendedRef.current = false;\n if (!aliveRef.current) return;\n optsRef.current.onResume?.();\n if (pendingRef.current) scheduleRaf();\n }, [cancelRaf, isVisible, optsRef, scheduleRaf]);\n\n const request = useCallback(() => {\n if (!aliveRef.current) return;\n syncObserver();\n pendingRef.current = true;\n if (!isVisible()) {\n suspendedRef.current = true;\n return;\n }\n scheduleRaf();\n }, [isVisible, scheduleRaf, syncObserver]);\n\n const cancel = useCallback(() => {\n pendingRef.current = false;\n cancelRaf();\n }, [cancelRaf]);\n\n // Re-armed in setup because the refs survive StrictMode's simulated remount, and\n // a layout effect so the arming precedes the host's own layout effects — a loop\n // driven from one of those would otherwise be declined on the second mount.\n useLayoutEffect(() => {\n aliveRef.current = true;\n document.addEventListener('visibilitychange', syncGate);\n if (typeof IntersectionObserver !== 'undefined') {\n observerRef.current = new IntersectionObserver((entries) => {\n intersectingRef.current = entries.some((e) => e.isIntersecting);\n syncGate();\n });\n syncObserver();\n }\n return () => {\n aliveRef.current = false;\n document.removeEventListener('visibilitychange', syncGate);\n observerRef.current?.disconnect();\n observerRef.current = null;\n observedRef.current = null;\n intersectingRef.current = true;\n cancelRaf();\n pendingRef.current = false;\n suspendedRef.current = false;\n };\n }, [cancelRaf, syncGate, syncObserver]);\n\n // Stable identity: `requestRedraw` and friends outlive the render that made\n // them, and consumers capture the handle.\n const handle = useMemo(() => ({ request, cancel, isVisible }), [request, cancel, isVisible]);\n useLayoutEffect(() => { handleRef.current = handle; }, [handle]);\n return handle;\n}\n","import { useInsertionEffect, useRef } from 'react';\n\n/**\n * `value`, or the one the last committed render returned when `same` says the\n * two are equal — so a caller rebuilding an equivalent object every render\n * hands its readers one identity. Compared against the committed value only:\n * a render React throws away never becomes the one the next render keeps.\n */\nexport function useStableByContent<T>(value: T, same: (a: T, b: T) => boolean): T {\n const held = useRef(value);\n const out = held.current === value || same(held.current, value) ? held.current : value;\n useInsertionEffect(() => {\n held.current = out;\n });\n return out;\n}\n","import { createEmitter } from '@weasel-js/registry';\nimport type { KitSettings } from './index';\n\n/** A value a settings leaf can hold. */\nexport type KitSettingLeaf = number | string | boolean;\n\n/** `T` with every area and leaf optional: what a provider sets. */\nexport type KitSettingsOverrides<T = KitSettings> = {\n [K in keyof T]?: T[K] extends KitSettingLeaf ? T[K] : KitSettingsOverrides<T[K]>;\n};\n\n/** The dotted path of every leaf in `T`: `'tree.typeaheadMs'`. */\nexport type KitSettingPath<T = KitSettings> = {\n [K in keyof T & string]: T[K] extends KitSettingLeaf ? K : `${K}.${KitSettingPath<T[K]>}`;\n}[keyof T & string];\n\n/** The type of the leaf at `P` in `T`. */\nexport type KitSettingAt<P extends string, T = KitSettings> = P extends `${infer H}.${infer R}`\n ? H extends keyof T ? KitSettingAt<R, T[H]> : never\n : P extends keyof T ? T[P] : never;\n\n/** Reads settings by path and says when any of them changed. */\nexport interface KitSettingsStore<T = KitSettings> {\n /** The value at `path`: this store's own, else the nearest ancestor's, else `undefined`. */\n get<P extends KitSettingPath<T>>(path: P): KitSettingAt<P, T> | undefined;\n subscribe(listener: () => void): () => void;\n}\n\n/** A store whose own overrides can be replaced. */\nexport interface WritableKitSettingsStore<T = KitSettings> extends KitSettingsStore<T> {\n /** Replace this store's own overrides, notifying only when some leaf's value changed. */\n set(overrides: KitSettingsOverrides<T>): void;\n}\n\nfunction leafAt(tree: unknown, path: string): unknown {\n let at = tree;\n for (const key of path.split('.')) {\n if (at === null || typeof at !== 'object' || !Object.hasOwn(at, key)) return undefined;\n at = (at as Record<string, unknown>)[key];\n }\n return at;\n}\n\nfunction flatten(tree: unknown, prefix: string, out: Map<string, unknown>): Map<string, unknown> {\n if (tree === null || typeof tree !== 'object') return out;\n for (const [key, value] of Object.entries(tree)) {\n if (value === undefined) continue;\n const path = prefix === '' ? key : `${prefix}.${key}`;\n if (value !== null && typeof value === 'object') flatten(value, path, out);\n else out.set(path, value);\n }\n return out;\n}\n\nconst sameLeaves = (a: Map<string, unknown>, b: Map<string, unknown>): boolean =>\n a.size === b.size && [...a].every(([path, value]) => b.has(path) && Object.is(b.get(path), value));\n\n/**\n * A settings store holding `overrides`, reading through to `parent` for any\n * leaf it does not set. A change in the parent notifies this store's\n * listeners too.\n */\nexport function createKitSettingsStore<T = KitSettings>(\n overrides: KitSettingsOverrides<T> = {},\n parent: KitSettingsStore<T> | null = null,\n): WritableKitSettingsStore<T> {\n let own: unknown = overrides;\n let leaves = flatten(own, '', new Map());\n const listeners = createEmitter();\n const notify = (): void => listeners.emit();\n let unhookParent: (() => void) | null = null;\n\n return {\n get(path) {\n const value = leafAt(own, path);\n return (value !== undefined ? value : parent?.get(path)) as never;\n },\n subscribe(listener) {\n const off = listeners.subscribe(listener);\n if (parent && unhookParent === null) unhookParent = parent.subscribe(notify);\n return () => {\n off();\n if (listeners.size === 0 && unhookParent) {\n unhookParent();\n unhookParent = null;\n }\n };\n },\n set(next) {\n const nextLeaves = flatten(next, '', new Map());\n own = next;\n if (sameLeaves(leaves, nextLeaves)) return;\n leaves = nextLeaves;\n notify();\n },\n };\n}\n\n/** The store read where no provider is mounted: it sets nothing. */\nexport const NO_KIT_SETTINGS: KitSettingsStore = createKitSettingsStore();\n","import { createContext, createElement, useCallback, useContext, useLayoutEffect, useState, useSyncExternalStore, type ReactNode } from 'react';\nimport {\n createKitSettingsStore,\n NO_KIT_SETTINGS,\n type KitSettingAt,\n type KitSettingPath,\n type KitSettingsOverrides,\n type KitSettingsStore,\n} from './kitSettings';\n\nconst KitSettingsContext = createContext<KitSettingsStore>(NO_KIT_SETTINGS);\n\n/**\n * Set kit settings for everything inside. Nested providers merge: a leaf this\n * one leaves unset reads through to the one above.\n */\nexport function KitSettingsProvider({ value, children }: { value: KitSettingsOverrides; children?: ReactNode }): ReactNode {\n const parent = useContext(KitSettingsContext);\n const [store, setStore] = useState(() => ({ parent, store: createKitSettingsStore(value, parent) }));\n if (store.parent !== parent) setStore({ parent, store: createKitSettingsStore(value, parent) });\n useLayoutEffect(() => {\n store.store.set(value);\n }, [store, value]);\n return createElement(KitSettingsContext.Provider, { value: store.store }, children);\n}\n\n/**\n * The store the nearest provider holds, for code outside React — a gesture\n * recognizer, a canvas action — to read with `get(path)` at the moment it\n * needs a value.\n */\nexport function useKitSettings(): KitSettingsStore {\n return useContext(KitSettingsContext);\n}\n\n/**\n * The setting at `path`, or `undefined` where no provider sets it; the caller\n * falls back to its own constant. Re-renders only when this path's value changes.\n */\nexport function useKitSetting<P extends KitSettingPath>(path: P): KitSettingAt<P> | undefined {\n const store = useKitSettings();\n const read = useCallback(() => store.get(path), [store, path]);\n return useSyncExternalStore(store.subscribe, read, read);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/react",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.2",
|
|
4
4
|
"description": "Generic React hooks the weasel packages share: a committed-value ref, content-stable identity, and the stylesheet a component adopts into its root. No weasel domain in it.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"react": { "optional": true }
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@weasel-js/registry": "1.15.
|
|
25
|
+
"@weasel-js/registry": "1.15.2"
|
|
26
26
|
},
|
|
27
27
|
"author": "orochi235",
|
|
28
28
|
"homepage": "https://orochi235.github.io/weasel/",
|