phase 0.0.5 → 0.0.7

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/react.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { clamp01, easeOutCubic } from "./ease.js";
3
- import { a as readDpr, c as createScrollProgress, d as readMediaQuery, f as subscribeMediaQuery, g as invalidDurationError, i as whenIdle, l as createLoop, n as REDUCED_MOTION_QUERY, o as subscribeDpr, p as createSight, r as prefersReducedMotion, s as createRenderState, t as createMutation, u as createLifecycle, v as missingContextError } from "./mutation-BDx6r5D3.js";
3
+ import { a as REDUCED_MOTION_QUERY, c as readDpr, d as createScrollProgress, f as createLoop, g as createSight, h as subscribeMediaQuery, i as createMutation, l as subscribeDpr, m as readMediaQuery, n as observeResize, o as prefersReducedMotion, p as createLifecycle, r as createPointer, s as whenIdle, t as createScroll, u as createRenderState, x as missingContextError, y as invalidDurationError } from "./scroll-DZvWyPQE.js";
4
4
  import { createContext, createElement, use, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/react/use-synced-ref/index.ts
@@ -53,7 +53,7 @@ function degradedConfig(degraded, degradedFps) {
53
53
  }
54
54
  //#endregion
55
55
  //#region src/react/use-loop/index.ts
56
- const INITIAL_STATE$4 = {
56
+ const INITIAL_STATE$6 = {
57
57
  phase: "idle",
58
58
  phaseReason: "initial",
59
59
  quality: "full",
@@ -75,12 +75,12 @@ function useLoop(options) {
75
75
  const onTickRef = useSyncedRef(options.onTick);
76
76
  const internalRef = useRef(null);
77
77
  const ref = options.ref ?? internalRef;
78
- const [state, setState] = useState(INITIAL_STATE$4);
78
+ const [state, setState] = useState(INITIAL_STATE$6);
79
79
  const loopRef = useRef(null);
80
80
  useEffect(() => {
81
81
  const element = ref.current;
82
82
  if (!element || !enabled) {
83
- setState(INITIAL_STATE$4);
83
+ setState(INITIAL_STATE$6);
84
84
  return;
85
85
  }
86
86
  const loop = createLoop({
@@ -119,7 +119,7 @@ function useLoop(options) {
119
119
  }
120
120
  //#endregion
121
121
  //#region src/react/use-lifecycle/index.ts
122
- const INITIAL_STATE$3 = {
122
+ const INITIAL_STATE$5 = {
123
123
  phase: "idle",
124
124
  phaseReason: "initial"
125
125
  };
@@ -148,12 +148,12 @@ function useLifecycle(options) {
148
148
  const onPhaseChangeRef = useSyncedRef(options?.onPhaseChange);
149
149
  const internalRef = useRef(null);
150
150
  const ref = options?.ref ?? internalRef;
151
- const [state, setState] = useState(INITIAL_STATE$3);
151
+ const [state, setState] = useState(INITIAL_STATE$5);
152
152
  const lifecycleRef = useRef(null);
153
153
  useEffect(() => {
154
154
  const element = ref.current;
155
155
  if (!element || !enabled) {
156
- setState(INITIAL_STATE$3);
156
+ setState(INITIAL_STATE$5);
157
157
  return;
158
158
  }
159
159
  const lifecycle = createLifecycle({
@@ -235,12 +235,12 @@ function usePrefersReducedMotion() {
235
235
  }
236
236
  //#endregion
237
237
  //#region src/react/use-sight/index.ts
238
- const INITIAL_STATE$2 = {
238
+ const INITIAL_STATE$4 = {
239
239
  phase: "unknown",
240
240
  phaseReason: "initial"
241
241
  };
242
242
  function useSight(options) {
243
- const [state, setState] = useState(INITIAL_STATE$2);
243
+ const [state, setState] = useState(INITIAL_STATE$4);
244
244
  const observe = options?.observe ?? "continuous";
245
245
  const phaseRef = useRef("unknown");
246
246
  const phaseReasonRef = useRef("initial");
@@ -283,40 +283,6 @@ function useSight(options) {
283
283
  };
284
284
  }
285
285
  //#endregion
286
- //#region src/core/_internal/pool/ro-pool.ts
287
- let observer = null;
288
- const callbacks = /* @__PURE__ */ new Map();
289
- /**
290
- * Observe an element via a singleton ResizeObserver.
291
- * One RO instance for the entire page. Per-element `box` options are
292
- * forwarded to `ResizeObserver.observe()`.
293
- *
294
- * @returns Cleanup function that unobserves the element.
295
- */
296
- function observeResize(element, callback, box) {
297
- callbacks.set(element, callback);
298
- getObserver().observe(element, box ? { box } : void 0);
299
- let disposed = false;
300
- return () => {
301
- if (disposed) return;
302
- disposed = true;
303
- if (callbacks.get(element) === callback) {
304
- callbacks.delete(element);
305
- observer?.unobserve(element);
306
- }
307
- };
308
- }
309
- /** Lazy-created singleton. RO takes zero constructor options, so one instance can observe everything. */
310
- function getObserver() {
311
- if (!observer) observer = new ResizeObserver((entries) => {
312
- for (const entry of entries) {
313
- const cb = callbacks.get(entry.target);
314
- if (cb) cb(entry);
315
- }
316
- });
317
- return observer;
318
- }
319
- //#endregion
320
286
  //#region src/react/use-size/index.ts
321
287
  function useSize(options) {
322
288
  const [size, setSize] = useState(null);
@@ -512,7 +478,7 @@ function useWhenIdle(callback, options) {
512
478
  }
513
479
  //#endregion
514
480
  //#region src/react/use-canvas/index.ts
515
- const INITIAL_STATE$1 = {
481
+ const INITIAL_STATE$3 = {
516
482
  phase: "idle",
517
483
  phaseReason: "initial",
518
484
  quality: "full",
@@ -535,7 +501,7 @@ const INITIAL_STATE$1 = {
535
501
  function useCanvas(options) {
536
502
  const { containerRef, canvasRef, fps, enabled = true, reducedMotion, degraded, degradedFps } = options;
537
503
  const drawRef = useSyncedRef(options.draw);
538
- const [state, setState] = useState(INITIAL_STATE$1);
504
+ const [state, setState] = useState(INITIAL_STATE$3);
539
505
  const [restartNonce, setRestartNonce] = useState(0);
540
506
  const ctxRef = useRef(null);
541
507
  const sizeRef = useRef({
@@ -647,23 +613,32 @@ function useCanvas(options) {
647
613
  }
648
614
  //#endregion
649
615
  //#region src/react/use-mutation/index.ts
650
- const INITIAL_STATE = {
616
+ const INITIAL_STATE$2 = {
651
617
  phase: "paused",
652
618
  phaseReason: "initial"
653
619
  };
620
+ /**
621
+ * Lifecycle-aware MutationObserver with rAF-coalesced callbacks.
622
+ * Auto-pauses off-screen and tears down on unmount.
623
+ *
624
+ * Records are always delivered imperatively via `onMutations` (never state).
625
+ * Phase transitions (observing/paused) are infrequent, so `phase`/`phaseReason`
626
+ * are reactive state; read `phaseRef`/`phaseReasonRef` for the latest value
627
+ * inside `onMutations` without closure staleness. For synchronous phase
628
+ * reactions, use the core `createMutation` primitive.
629
+ */
654
630
  function useMutation(options) {
655
- const [state, setState] = useState(INITIAL_STATE);
631
+ const [state, setState] = useState(INITIAL_STATE$2);
656
632
  const { mutation: mutationInit, visibility = "pause", enabled = true, intersectionOptions } = options;
657
633
  const phaseRef = useRef("paused");
658
634
  const phaseReasonRef = useRef("initial");
659
635
  const onMutationsRef = useSyncedRef(options.onMutations);
660
- const onPhaseChangeRef = useSyncedRef(options.onPhaseChange);
661
636
  const internalRef = useRef(null);
662
637
  const ref = options.ref ?? internalRef;
663
638
  useEffect(() => {
664
639
  const element = ref.current;
665
640
  if (!element || !enabled) {
666
- setState(INITIAL_STATE);
641
+ setState(INITIAL_STATE$2);
667
642
  phaseRef.current = "paused";
668
643
  phaseReasonRef.current = "initial";
669
644
  return;
@@ -675,8 +650,7 @@ function useMutation(options) {
675
650
  onPhaseChange: (phase, reason) => {
676
651
  phaseRef.current = phase;
677
652
  phaseReasonRef.current = reason;
678
- if (onPhaseChangeRef.current) onPhaseChangeRef.current(phase, reason);
679
- else setState({
653
+ setState({
680
654
  phase,
681
655
  phaseReason: reason
682
656
  });
@@ -694,6 +668,159 @@ function useMutation(options) {
694
668
  };
695
669
  }
696
670
  //#endregion
671
+ //#region src/react/use-pointer/index.ts
672
+ const INITIAL_STATE$1 = {
673
+ phase: "idle",
674
+ phaseReason: "initial"
675
+ };
676
+ /**
677
+ * Lifecycle-aware pointer tracker with rAF-batched `getBoundingClientRect`.
678
+ * Auto-pauses off-screen and tears down on unmount.
679
+ *
680
+ * Position is always delivered imperatively via `onPointer` (never state) and
681
+ * mirrored in `stateRef` for on-demand reads (e.g. inside a `useLoop` tick).
682
+ * Phase transitions (tracking/idle) are infrequent, so `phase`/`phaseReason`
683
+ * are reactive state; read `phaseRef`/`phaseReasonRef` for the latest value
684
+ * without closure staleness. For synchronous phase reactions, use the core
685
+ * `createPointer` primitive.
686
+ */
687
+ function usePointer(options) {
688
+ const [state, setState] = useState(INITIAL_STATE$1);
689
+ const { visibility = "pause", enabled = true, intersectionOptions } = options;
690
+ const phaseRef = useRef("idle");
691
+ const phaseReasonRef = useRef("initial");
692
+ const stateRef = useRef({
693
+ x: 0,
694
+ y: 0,
695
+ active: false
696
+ });
697
+ const onPointerRef = useSyncedRef(options.onPointer);
698
+ const internalRef = useRef(null);
699
+ const ref = options.ref ?? internalRef;
700
+ useEffect(() => {
701
+ const element = ref.current;
702
+ if (!element || !enabled) {
703
+ setState(INITIAL_STATE$1);
704
+ phaseRef.current = "idle";
705
+ phaseReasonRef.current = "initial";
706
+ stateRef.current = {
707
+ x: 0,
708
+ y: 0,
709
+ active: false
710
+ };
711
+ return;
712
+ }
713
+ const instance = createPointer({
714
+ element,
715
+ onPointer: (pointerState) => {
716
+ stateRef.current = pointerState;
717
+ onPointerRef.current(pointerState);
718
+ },
719
+ onPhaseChange: (phase, reason) => {
720
+ phaseRef.current = phase;
721
+ phaseReasonRef.current = reason;
722
+ setState({
723
+ phase,
724
+ phaseReason: reason
725
+ });
726
+ },
727
+ visibility,
728
+ intersectionOptions
729
+ });
730
+ return () => instance.stop();
731
+ }, [enabled, visibility]);
732
+ return {
733
+ ref,
734
+ ...state,
735
+ phaseRef,
736
+ phaseReasonRef,
737
+ stateRef
738
+ };
739
+ }
740
+ //#endregion
741
+ //#region src/react/use-scroll/index.ts
742
+ const INITIAL_STATE = {
743
+ phase: "paused",
744
+ phaseReason: "initial"
745
+ };
746
+ function initialState() {
747
+ return {
748
+ x: 0,
749
+ y: 0,
750
+ maxX: 0,
751
+ maxY: 0,
752
+ progressX: 0,
753
+ progressY: 0,
754
+ visibleX: 1,
755
+ visibleY: 1
756
+ };
757
+ }
758
+ /**
759
+ * Lifecycle-aware scroll tracker. Scroll position is delivered imperatively via
760
+ * `onScroll` (never state) and mirrored in `stateRef` for on-demand reads; only
761
+ * the phase (tracking/paused) is reactive, since it flips rarely. This mirrors
762
+ * `usePointer`. Auto-pauses off-screen and tears down on unmount.
763
+ *
764
+ * Reads `scrollLeft`/`scrollTop` once per rAF frame; the reflow-heavy geometry
765
+ * (`scrollWidth`/`clientWidth`) is read only on resize or via `measure()`, never
766
+ * on the scroll path. Call `measure()` after mutating scrollable content.
767
+ */
768
+ function useScroll(options) {
769
+ const [state, setState] = useState(INITIAL_STATE);
770
+ const { visibility = "pause", enabled = true, intersectionOptions } = options;
771
+ const phaseRef = useRef("paused");
772
+ const phaseReasonRef = useRef("initial");
773
+ const stateRef = useRef(initialState());
774
+ const onScrollRef = useSyncedRef(options.onScroll);
775
+ const instanceRef = useRef(null);
776
+ const internalRef = useRef(null);
777
+ const ref = options.ref ?? internalRef;
778
+ const measure = useCallback(() => {
779
+ instanceRef.current?.measure();
780
+ }, []);
781
+ useEffect(() => {
782
+ const element = ref.current;
783
+ if (!element || !enabled) {
784
+ instanceRef.current = null;
785
+ setState(INITIAL_STATE);
786
+ phaseRef.current = "paused";
787
+ phaseReasonRef.current = "initial";
788
+ stateRef.current = initialState();
789
+ return;
790
+ }
791
+ const instance = createScroll({
792
+ element,
793
+ onScroll: (scrollState) => {
794
+ stateRef.current = scrollState;
795
+ onScrollRef.current(scrollState);
796
+ },
797
+ onPhaseChange: (phase, reason) => {
798
+ phaseRef.current = phase;
799
+ phaseReasonRef.current = reason;
800
+ setState({
801
+ phase,
802
+ phaseReason: reason
803
+ });
804
+ },
805
+ visibility,
806
+ intersectionOptions
807
+ });
808
+ instanceRef.current = instance;
809
+ return () => {
810
+ instance.stop();
811
+ instanceRef.current = null;
812
+ };
813
+ }, [enabled, visibility]);
814
+ return {
815
+ ref,
816
+ ...state,
817
+ phaseRef,
818
+ phaseReasonRef,
819
+ stateRef,
820
+ measure
821
+ };
822
+ }
823
+ //#endregion
697
824
  //#region src/react/use-tween/index.ts
698
825
  /**
699
826
  * Animate a value from its current position to `target` over `duration`.
@@ -1093,6 +1220,6 @@ function SwapState({ id, ref: forwardedRef, children, ...divProps }) {
1093
1220
  }
1094
1221
  const Swap = Object.assign(SwapRoot, { State: SwapState });
1095
1222
  //#endregion
1096
- export { Defer, Presence, Swap, WhenIdle, WhenVisible, useCanvas, useContainerQuery, useDevicePixelRatio, useIdle, useLifecycle, useLoop, useMediaQuery, useMutation, usePrefersReducedMotion, usePresence, useRenderState, useScrollProgress, useSight, useSize, useStableCallback, useSyncedRef, useTween, useWhenIdle };
1223
+ export { Defer, Presence, Swap, WhenIdle, WhenVisible, useCanvas, useContainerQuery, useDevicePixelRatio, useIdle, useLifecycle, useLoop, useMediaQuery, useMutation, usePointer, usePrefersReducedMotion, usePresence, useRenderState, useScroll, useScrollProgress, useSight, useSize, useStableCallback, useSyncedRef, useTween, useWhenIdle };
1097
1224
 
1098
1225
  //# sourceMappingURL=react.js.map