@neveranyart/weaver 1.0.20 → 1.0.22

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/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # Weaver
2
2
 
3
- **WIP DOCS**
4
-
5
3
  An in-house core package by neveranyart for making performant React CSR.
6
4
 
7
5
  <sub>Almost feels like a framework, but it's not.</sub>
@@ -9,12 +7,14 @@ An in-house core package by neveranyart for making performant React CSR.
9
7
  ![neverany branding](https://github.com/user-attachments/assets/8c09234f-d592-4adc-8ab1-284b1fca3f7c)
10
8
 
11
9
  ## Introduction
12
- This package is a collection of sub-packages for creating a creative web with ease.
10
+ This package is a collection of sub-packages for handling many moving part of a creative web with ease.
13
11
 
14
12
  Most components and tools are built with flexibility in mind, providing a balance between abstraction and verbose.
15
13
 
16
14
  But some will have its constrain, requiring a certain packages to work with.
17
15
 
16
+ This is not required, but to fully utilize this package, rour website should already using `react-router`, `lenis`, `motion` and `@react-three/*`.
17
+
18
18
  > [!NOTE]
19
19
  > `React v19+` is required.
20
20
 
@@ -32,12 +32,12 @@ yarn add @neveranyart/weaver
32
32
 
33
33
  ## Documentations
34
34
  1. [Routing](https://github.com/neveranyart/weaver/blob/main/docs/ROUTING.md)
35
- - [`DelayedOutlet`](https://github.com/neveranyart/weaver/blob/main/docs/ROUTING.md#delayedoutlet): Delay `react-router`'s `<Outlet/>` to allow transition animation, loading fallback.
36
- - [`Pipeline`](https://github.com/neveranyart/weaver/blob/main/docs/ROUTING.md#pipeline): Declare parent endpoint to communicate with `DelayedOutlet`.
35
+ - [`DelayedOutlet`](https://github.com/neveranyart/weaver/blob/main/docs/ROUTING.md#delayedoutlet): Delay `react-router`'s `<Outlet/>` to allow transition animation, loading fallback.
36
+ - [`Pipeline`](https://github.com/neveranyart/weaver/blob/main/docs/ROUTING.md#pipeline): Declare parent endpoint to communicate with `DelayedOutlet`.
37
37
  2. [Scene](https://github.com/neveranyart/weaver/blob/main/docs/SCENE.md)
38
- - [`BakeScene`](https://github.com/neveranyart/weaver/blob/main/docs/SCENE.md#bakescene): Put 3D scene to render and notifies back when the scene is ready to be used.
39
- - [`SceneSync`](https://github.com/neveranyart/weaver/blob/main/docs/SCENE.md#scenesync): Fit your 3D scene inside a DOM element
40
- 2. [Hooks](https://github.com/neveranyart/weaver/blob/main/docs/HOOKS.md)
38
+ - [`BakeScene`](https://github.com/neveranyart/weaver/blob/main/docs/SCENE.md#bakescene): Put 3D scene to render and notifies back when the scene is ready to be used.
39
+ - [`SceneSync`](https://github.com/neveranyart/weaver/blob/main/docs/SCENE.md#scenesync): Fit your 3D scene inside a DOM element
40
+ 3. [Hooks](https://github.com/neveranyart/weaver/blob/main/docs/HOOKS.md)
41
41
 
42
42
  ## License
43
43
  MIT
package/dist/hooks.js CHANGED
@@ -316,7 +316,7 @@ function useViewport(customCamera) {
316
316
  setWidth(actualViewport.width);
317
317
  setHeight(actualViewport.height);
318
318
  }, [camera, customCamera, viewport]);
319
- useScreenCallback(viewportUpdate);
319
+ useScreenCallback(viewportUpdate, { initialCall: true });
320
320
  return { width, height };
321
321
  }
322
322
  // Annotate the CommonJS export names for ESM import in node:
package/dist/hooks.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/index.ts","../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/setup.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import { useBreakpoints } from './breakpoints';\nimport { useEffectOnce, useLayoutEffectOnce } from './effectOnce';\nimport { useLenisCallback } from './lenisCallback';\nimport { useMouseCallback } from './mouseCallback';\nimport { useNavigateAnchor } from './navigateAnchor';\nimport { useOrbit } from './orbit';\nimport { useRawParams } from './rawParams';\nimport { useScreen } from './screen';\nimport { useScreenCallback } from './screenCallback';\nimport { useViewport } from './viewport';\n\nexport {\n useBreakpoints,\n useEffectOnce,\n useLayoutEffectOnce,\n useLenisCallback,\n useMouseCallback,\n useNavigateAnchor,\n useOrbit,\n useRawParams,\n useScreen,\n useScreenCallback,\n useViewport,\n};\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { useCallback, useState } from 'react';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * Get current threejs viewport with the actual current.\n */\nexport function useViewport(\n customCamera?: OrthographicCamera | PerspectiveCamera\n) {\n const { viewport, camera } = useThree();\n\n const [width, setWidth] = useState(viewport.getCurrentViewport().width);\n const [height, setHeight] = useState(viewport.getCurrentViewport().height);\n\n const viewportUpdate = useCallback(() => {\n const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);\n setWidth(actualViewport.width);\n setHeight(actualViewport.height);\n }, [camera, customCamera, viewport]);\n useScreenCallback(viewportUpdate);\n\n return { width: width, height: height };\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_react","import_react","import_react","import_react_router","import_react","import_react"]}
1
+ {"version":3,"sources":["../src/hooks/index.ts","../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/setup.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import { useBreakpoints } from './breakpoints';\nimport { useEffectOnce, useLayoutEffectOnce } from './effectOnce';\nimport { useLenisCallback } from './lenisCallback';\nimport { useMouseCallback } from './mouseCallback';\nimport { useNavigateAnchor } from './navigateAnchor';\nimport { useOrbit } from './orbit';\nimport { useRawParams } from './rawParams';\nimport { useScreen } from './screen';\nimport { useScreenCallback } from './screenCallback';\nimport { useViewport } from './viewport';\n\nexport {\n useBreakpoints,\n useEffectOnce,\n useLayoutEffectOnce,\n useLenisCallback,\n useMouseCallback,\n useNavigateAnchor,\n useOrbit,\n useRawParams,\n useScreen,\n useScreenCallback,\n useViewport,\n};\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { useCallback, useState } from 'react';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * Get current threejs viewport with the actual current.\n */\nexport function useViewport(\n customCamera?: OrthographicCamera | PerspectiveCamera\n) {\n const { viewport, camera } = useThree();\n\n const [width, setWidth] = useState(viewport.getCurrentViewport().width);\n const [height, setHeight] = useState(viewport.getCurrentViewport().height);\n\n const viewportUpdate = useCallback(() => {\n const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);\n setWidth(actualViewport.width);\n setHeight(actualViewport.height);\n }, [camera, customCamera, viewport]);\n useScreenCallback(viewportUpdate, { initialCall: true });\n\n return { width: width, height: height };\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_react","import_react","import_react","import_react_router","import_react","import_react"]}
package/dist/hooks.mjs CHANGED
@@ -280,7 +280,7 @@ function useViewport(customCamera) {
280
280
  setWidth(actualViewport.width);
281
281
  setHeight(actualViewport.height);
282
282
  }, [camera, customCamera, viewport]);
283
- useScreenCallback(viewportUpdate);
283
+ useScreenCallback(viewportUpdate, { initialCall: true });
284
284
  return { width, height };
285
285
  }
286
286
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/setup.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { useCallback, useState } from 'react';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * Get current threejs viewport with the actual current.\n */\nexport function useViewport(\n customCamera?: OrthographicCamera | PerspectiveCamera\n) {\n const { viewport, camera } = useThree();\n\n const [width, setWidth] = useState(viewport.getCurrentViewport().width);\n const [height, setHeight] = useState(viewport.getCurrentViewport().height);\n\n const viewportUpdate = useCallback(() => {\n const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);\n setWidth(actualViewport.width);\n setHeight(actualViewport.height);\n }, [camera, customCamera, viewport]);\n useScreenCallback(viewportUpdate);\n\n return { width: width, height: height };\n}\n"],"mappings":"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","names":["useLayoutEffect","useCallback","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useCallback","useLayoutEffect","useState","useCallback","useState","useState","useCallback"]}
1
+ {"version":3,"sources":["../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/setup.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { useCallback, useState } from 'react';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * Get current threejs viewport with the actual current.\n */\nexport function useViewport(\n customCamera?: OrthographicCamera | PerspectiveCamera\n) {\n const { viewport, camera } = useThree();\n\n const [width, setWidth] = useState(viewport.getCurrentViewport().width);\n const [height, setHeight] = useState(viewport.getCurrentViewport().height);\n\n const viewportUpdate = useCallback(() => {\n const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);\n setWidth(actualViewport.width);\n setHeight(actualViewport.height);\n }, [camera, customCamera, viewport]);\n useScreenCallback(viewportUpdate, { initialCall: true });\n\n return { width: width, height: height };\n}\n"],"mappings":"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","names":["useLayoutEffect","useCallback","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useCallback","useLayoutEffect","useState","useCallback","useState","useState","useCallback"]}
package/dist/scene.d.mts CHANGED
@@ -104,11 +104,11 @@ interface SyncProps {
104
104
  *
105
105
  * For each mode, there will be some very distinct trade-offs
106
106
  *
107
- * - `relaxed`: Uses IntersectionObserver paired with a scroll hook, together with ResizeObserver.
107
+ * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.
108
108
  * - (+): Minimal update calls, best performance.
109
109
  * - (-): The scene get desynced the moment DOM element moves without changing its sizes.
110
110
  * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element
111
- * is out of view, the scene that did not fully moved out of view will stay there, as scroll hook will be disabled.
111
+ * is out of view, the part of the scene that did not fully moved out of view will stay there.
112
112
  * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.
113
113
  * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.
114
114
  * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same
package/dist/scene.d.ts CHANGED
@@ -104,11 +104,11 @@ interface SyncProps {
104
104
  *
105
105
  * For each mode, there will be some very distinct trade-offs
106
106
  *
107
- * - `relaxed`: Uses IntersectionObserver paired with a scroll hook, together with ResizeObserver.
107
+ * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.
108
108
  * - (+): Minimal update calls, best performance.
109
109
  * - (-): The scene get desynced the moment DOM element moves without changing its sizes.
110
110
  * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element
111
- * is out of view, the scene that did not fully moved out of view will stay there, as scroll hook will be disabled.
111
+ * is out of view, the part of the scene that did not fully moved out of view will stay there.
112
112
  * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.
113
113
  * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.
114
114
  * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same
package/dist/scene.js CHANGED
@@ -89,9 +89,9 @@ function BakeScene(props) {
89
89
  }
90
90
  function NotificationHandler(props) {
91
91
  const [sceneReady, setSceneReady] = (0, import_react.useState)(false);
92
- return !sceneReady && /* @__PURE__ */ import_react.default.createElement(RenderNotification, { ...props, setSceneReady });
92
+ return !sceneReady && /* @__PURE__ */ import_react.default.createElement(RenderNotifier, { ...props, setSceneReady });
93
93
  }
94
- function RenderNotification(props) {
94
+ function RenderNotifier(props) {
95
95
  const scheduledForCallback = (0, import_react.useRef)(false);
96
96
  (0, import_fiber.useFrame)(() => {
97
97
  if (!scheduledForCallback.current) {
@@ -107,25 +107,45 @@ function RenderNotification(props) {
107
107
 
108
108
  // src/scene/SceneSync.tsx
109
109
  var import_drei = require("@react-three/drei");
110
- var import_fiber2 = require("@react-three/fiber");
111
- var import_react5 = require("motion/react");
112
- var import_react6 = __toESM(require("react"));
110
+ var import_react11 = require("motion/react");
111
+ var import_react12 = __toESM(require("react"));
113
112
 
114
- // src/hooks/effectOnce.ts
113
+ // src/hooks/breakpoints.ts
114
+ var import_react3 = require("react");
115
+
116
+ // src/hooks/screenCallback.ts
115
117
  var import_react2 = require("react");
118
+ function useScreenCallback(callback, options) {
119
+ (0, import_react2.useLayoutEffect)(() => {
120
+ const reportScreen = () => callback({
121
+ width: window.innerWidth,
122
+ height: window.innerHeight
123
+ });
124
+ if (options?.initialCall) {
125
+ reportScreen();
126
+ }
127
+ window.addEventListener("resize", reportScreen, { passive: true });
128
+ return () => {
129
+ window.removeEventListener("resize", reportScreen);
130
+ };
131
+ }, [callback, options?.initialCall]);
132
+ }
133
+
134
+ // src/hooks/effectOnce.ts
135
+ var import_react4 = require("react");
116
136
  function useLayoutEffectOnce(callback) {
117
- (0, import_react2.useLayoutEffect)(callback, []);
137
+ (0, import_react4.useLayoutEffect)(callback, []);
118
138
  }
119
139
 
120
140
  // src/hooks/lenisCallback.ts
121
- var import_react4 = require("react");
141
+ var import_react6 = require("react");
122
142
 
123
143
  // src/hooks/orbit.ts
124
- var import_react3 = require("react");
144
+ var import_react5 = require("react");
125
145
  function useOrbit(options) {
126
146
  const { onResize, onIntersect } = options.events;
127
147
  const { rootMargin = "25% 0px 25% 0px" } = options;
128
- (0, import_react3.useLayoutEffect)(() => {
148
+ (0, import_react5.useLayoutEffect)(() => {
129
149
  if (!options.target) return;
130
150
  if (!options.target.current) return;
131
151
  let orbitResize = void 0;
@@ -155,11 +175,11 @@ function useLenisCallback(callback, options) {
155
175
  "useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance"
156
176
  );
157
177
  }
158
- const callbackWrapScroll = (0, import_react4.useCallback)(
178
+ const callbackWrapScroll = (0, import_react6.useCallback)(
159
179
  () => callback(weaverSetup._lenisInstance.actualScroll, "scroll"),
160
180
  [callback]
161
181
  );
162
- const callbackWrapResize = (0, import_react4.useCallback)(
182
+ const callbackWrapResize = (0, import_react6.useCallback)(
163
183
  () => callback(weaverSetup._lenisInstance.actualScroll, "resize"),
164
184
  [callback]
165
185
  );
@@ -178,7 +198,7 @@ function useLenisCallback(callback, options) {
178
198
  },
179
199
  rootMargin: "50% 0px 50% 0px"
180
200
  });
181
- (0, import_react4.useLayoutEffect)(() => {
201
+ (0, import_react6.useLayoutEffect)(() => {
182
202
  if (!options?.intersectOn) {
183
203
  weaverSetup._lenisInstance.on("scroll", callbackWrapScroll);
184
204
  window.addEventListener("resize", callbackWrapResize);
@@ -199,14 +219,43 @@ function useLenisCallback(callback, options) {
199
219
  ]);
200
220
  }
201
221
 
222
+ // src/hooks/mouseCallback.ts
223
+ var import_react7 = require("react");
224
+
225
+ // src/hooks/navigateAnchor.ts
226
+ var import_react8 = require("react");
227
+ var import_react_router = require("react-router");
228
+
229
+ // src/hooks/rawParams.ts
230
+ var import_react_router2 = require("react-router");
231
+
232
+ // src/hooks/screen.ts
233
+ var import_react9 = require("react");
234
+
235
+ // src/hooks/viewport.ts
236
+ var import_fiber2 = require("@react-three/fiber");
237
+ var import_react10 = require("react");
238
+ function useViewport(customCamera) {
239
+ const { viewport, camera } = (0, import_fiber2.useThree)();
240
+ const [width, setWidth] = (0, import_react10.useState)(viewport.getCurrentViewport().width);
241
+ const [height, setHeight] = (0, import_react10.useState)(viewport.getCurrentViewport().height);
242
+ const viewportUpdate = (0, import_react10.useCallback)(() => {
243
+ const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);
244
+ setWidth(actualViewport.width);
245
+ setHeight(actualViewport.height);
246
+ }, [camera, customCamera, viewport]);
247
+ useScreenCallback(viewportUpdate, { initialCall: true });
248
+ return { width, height };
249
+ }
250
+
202
251
  // src/scene/SceneSync.tsx
203
252
  function SceneSync(props) {
204
- if (!weaverSetup._lenisInstance) {
253
+ if (props.trackingMode === "relaxed" && !weaverSetup._lenisInstance) {
205
254
  throw Error(
206
- "SceneSync won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance"
255
+ "SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance"
207
256
  );
208
257
  }
209
- const unique = (0, import_react6.useId)();
258
+ const unique = (0, import_react12.useId)();
210
259
  const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;
211
260
  if (!TunnelIn) {
212
261
  throw Error(
@@ -214,13 +263,13 @@ function SceneSync(props) {
214
263
  );
215
264
  }
216
265
  if (props.hud) {
217
- return /* @__PURE__ */ import_react6.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react6.default.createElement(import_drei.Hud, { key: unique, renderPriority: props.hudRenderPriority }, /* @__PURE__ */ import_react6.default.createElement(SyncInternal, { ...props })));
266
+ return /* @__PURE__ */ import_react12.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react12.default.createElement(import_drei.Hud, { key: unique, renderPriority: props.hudRenderPriority }, /* @__PURE__ */ import_react12.default.createElement(SyncInternal, { ...props })));
218
267
  }
219
- return /* @__PURE__ */ import_react6.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react6.default.createElement(SyncInternal, { key: unique, ...props }));
268
+ return /* @__PURE__ */ import_react12.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react12.default.createElement(SyncInternal, { key: unique, ...props }));
220
269
  }
221
270
  function SyncInternal(props) {
222
- const { viewport, camera } = (0, import_fiber2.useThree)();
223
- const defaultControl = (0, import_react6.useRef)(null);
271
+ const viewport = useViewport();
272
+ const defaultControl = (0, import_react12.useRef)(null);
224
273
  const {
225
274
  attach,
226
275
  control,
@@ -229,23 +278,18 @@ function SyncInternal(props) {
229
278
  disableScaling,
230
279
  onLayoutUpdate
231
280
  } = props;
232
- const properties = (0, import_react6.useRef)({
233
- viewport: void 0
234
- });
235
- const updatePosition = (0, import_react6.useCallback)(() => {
281
+ const updatePosition = (0, import_react12.useCallback)(() => {
236
282
  const activeControl = control ?? defaultControl;
237
- const vp = properties.current.viewport;
238
- if (!vp) return;
239
283
  const domRect = attach.current.getBoundingClientRect();
240
284
  const screenH = window.innerHeight;
241
285
  const screenW = window.innerWidth;
242
- const vpWidthRatio = vp.width / screenW;
243
- const vpHeightRatio = vp.height / screenH;
244
- const scrollOffset = weaverSetup._lenisInstance.actualScroll / screenH * vp.height;
286
+ const vpWidthRatio = viewport.width / screenW;
287
+ const vpHeightRatio = viewport.height / screenH;
288
+ const scrollOffset = weaverSetup._lenisInstance.actualScroll / screenH * viewport.height;
245
289
  const w = domRect.width * vpWidthRatio;
246
290
  const h = domRect.height * vpHeightRatio;
247
- const x = domRect.x * vpWidthRatio + w * 0.5 - vp.width * 0.5;
248
- const y = vp.height * 0.5 - (domRect.y + weaverSetup._lenisInstance.actualScroll) * vpHeightRatio - h * 0.5 + scrollOffset;
291
+ const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;
292
+ const y = viewport.height * 0.5 - (domRect.y + weaverSetup._lenisInstance.actualScroll) * vpHeightRatio - h * 0.5 + scrollOffset;
249
293
  if (onLayoutUpdate) {
250
294
  onLayoutUpdate(domRect, { w, h }, { x, y });
251
295
  }
@@ -264,26 +308,34 @@ function SyncInternal(props) {
264
308
  }
265
309
  activeControl.current.position.x = x;
266
310
  activeControl.current.position.y = y;
267
- }, [attach, onLayoutUpdate, control, scaleFactor, disableScaling, stretch]);
268
- (0, import_react6.useLayoutEffect)(() => {
269
- properties.current.viewport = viewport.getCurrentViewport(camera);
270
- updatePosition();
271
- }, [camera, updatePosition, viewport]);
272
- const graceUpdate = (0, import_react6.useCallback)(() => {
311
+ }, [
312
+ attach,
313
+ control,
314
+ disableScaling,
315
+ onLayoutUpdate,
316
+ scaleFactor,
317
+ stretch,
318
+ viewport.height,
319
+ viewport.width
320
+ ]);
321
+ const graceUpdate = (0, import_react12.useCallback)(() => {
273
322
  try {
274
323
  updatePosition();
275
324
  } catch {
276
325
  }
277
326
  }, [updatePosition]);
327
+ (0, import_react12.useLayoutEffect)(() => {
328
+ graceUpdate();
329
+ }, [graceUpdate]);
278
330
  const mode = {
279
- relaxed: /* @__PURE__ */ import_react6.default.createElement(RelaxedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
280
- balanced: /* @__PURE__ */ import_react6.default.createElement(BalancedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
281
- aggressive: /* @__PURE__ */ import_react6.default.createElement(AggressiveUpdate, { updatePosition: graceUpdate })
331
+ relaxed: /* @__PURE__ */ import_react12.default.createElement(RelaxedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
332
+ balanced: /* @__PURE__ */ import_react12.default.createElement(BalancedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
333
+ aggressive: /* @__PURE__ */ import_react12.default.createElement(AggressiveUpdate, { updatePosition: graceUpdate })
282
334
  };
283
335
  if (props.control) {
284
- return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, null, props.children, mode[props.trackingMode]);
336
+ return /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, props.children, mode[props.trackingMode]);
285
337
  }
286
- return /* @__PURE__ */ import_react6.default.createElement("group", { ref: defaultControl }, props.children, mode[props.trackingMode]);
338
+ return /* @__PURE__ */ import_react12.default.createElement("group", { ref: defaultControl }, props.children, mode[props.trackingMode]);
287
339
  }
288
340
  function RelaxedUpdate(props) {
289
341
  const { updatePosition } = props;
@@ -303,7 +355,7 @@ function RelaxedUpdate(props) {
303
355
  }
304
356
  function BalancedUpdate(props) {
305
357
  const { updatePosition } = props;
306
- const [shouldUpdate, setShouldUpdate] = (0, import_react6.useState)(false);
358
+ const [shouldUpdate, setShouldUpdate] = (0, import_react12.useState)(false);
307
359
  useOrbit({
308
360
  target: props.attach,
309
361
  events: {
@@ -314,14 +366,14 @@ function BalancedUpdate(props) {
314
366
  },
315
367
  rootMargin: "50% 0px 50% 0px"
316
368
  });
317
- (0, import_react6.useLayoutEffect)(() => {
369
+ (0, import_react12.useLayoutEffect)(() => {
318
370
  if (shouldUpdate) {
319
- import_react5.frame.read(updatePosition, true);
371
+ import_react11.frame.read(updatePosition, true);
320
372
  } else {
321
- (0, import_react5.cancelFrame)(updatePosition);
373
+ (0, import_react11.cancelFrame)(updatePosition);
322
374
  }
323
375
  return () => {
324
- (0, import_react5.cancelFrame)(updatePosition);
376
+ (0, import_react11.cancelFrame)(updatePosition);
325
377
  };
326
378
  }, [updatePosition, shouldUpdate]);
327
379
  useLayoutEffectOnce(updatePosition);
@@ -329,10 +381,10 @@ function BalancedUpdate(props) {
329
381
  }
330
382
  function AggressiveUpdate(props) {
331
383
  const { updatePosition } = props;
332
- (0, import_react6.useLayoutEffect)(() => {
333
- import_react5.frame.read(updatePosition, true);
384
+ (0, import_react12.useLayoutEffect)(() => {
385
+ import_react11.frame.read(updatePosition, true);
334
386
  return () => {
335
- (0, import_react5.cancelFrame)(updatePosition);
387
+ (0, import_react11.cancelFrame)(updatePosition);
336
388
  };
337
389
  }, [updatePosition]);
338
390
  return null;
package/dist/scene.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/scene/index.ts","../src/scene/BakeScene.tsx","../src/setup.ts","../src/scene/SceneSync.tsx","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts"],"sourcesContent":["import BakeScene from './BakeScene';\nimport SceneSync from './SceneSync';\n\nexport { BakeScene, SceneSync };\n","import { useFrame } from '@react-three/fiber';\nimport React, {\n Dispatch,\n Fragment,\n type ReactNode,\n SetStateAction,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `BakeScene`: This component will notifiy when the global 3D scene is ready.\n *\n * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire\n * with ease, the component takes advantage of that, and because `useLoader` is unreliable.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notification as soon as the job is done.\n */\n return (\n !sceneReady && (\n <RenderNotification {...props} setSceneReady={setSceneReady} />\n )\n );\n}\n\nfunction RenderNotification(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n props.setSceneReady(true);\n props.onSceneReady();\n }, props.loadTaskDelay ?? 50);\n }\n });\n\n return null;\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { Hud } from '@react-three/drei';\nimport { useThree, type Viewport } from '@react-three/fiber';\nimport { cancelFrame, frame } from 'motion/react';\nimport React, {\n type ReactNode,\n type RefObject,\n useCallback,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { Group } from 'three';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with a scroll hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the scene that did not fully moved out of view will stay there, as scroll hook will be disabled.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n hudRenderPriority: number;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.hudRenderPriority}>\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const { viewport, camera } = useThree();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const properties: RefObject<{\n viewport?: Omit<Viewport, 'dpr' | 'initialDpr'>;\n }> = useRef({\n viewport: undefined,\n });\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n const vp = properties.current.viewport;\n if (!vp) return;\n\n const domRect = attach.current!.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n\n const vpWidthRatio = vp.width / screenW;\n const vpHeightRatio = vp.height / screenH;\n\n const scrollOffset =\n (weaverSetup._lenisInstance!.actualScroll / screenH) * vp.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - vp.width * 0.5;\n const y =\n vp.height * 0.5 -\n (domRect.y + weaverSetup._lenisInstance!.actualScroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current!.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current!.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [attach, onLayoutUpdate, control, scaleFactor, disableScaling, stretch]);\n\n /**\n * Update position when `camera` changes.\n */\n useLayoutEffect(() => {\n properties.current.viewport = viewport.getCurrentViewport(camera);\n updatePosition();\n }, [camera, updatePosition, viewport]);\n\n const graceUpdate = useCallback(() => {\n try {\n updatePosition();\n } catch {\n /* empty */\n }\n }, [updatePosition]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n aggressive: <AggressiveUpdate updatePosition={graceUpdate} />,\n };\n\n if (props.control) {\n return (\n <>\n {props.children}\n {mode[props.trackingMode]}\n </>\n );\n }\n\n return (\n <group ref={defaultControl}>\n {props.children}\n {mode[props.trackingMode]}\n </group>\n );\n}\n\nfunction RelaxedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n /**\n * Scroll hook to update object correctly to the current HTML scroll position.\n */\n useLenisCallback(updatePosition, {\n initialCall: true,\n intersectOn: props.attach,\n });\n\n /**\n * Allows the element to resize too.\n */\n useOrbit({\n target: props.attach,\n events: {\n onIntersect: updatePosition,\n onResize: updatePosition,\n },\n });\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction BalancedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n const [shouldUpdate, setShouldUpdate] = useState(false);\n\n useOrbit({\n target: props.attach,\n events: {\n onIntersect(entry) {\n setShouldUpdate(entry.isIntersecting);\n },\n onResize: updatePosition,\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (shouldUpdate) {\n frame.read(updatePosition, true);\n } else {\n cancelFrame(updatePosition);\n }\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition, shouldUpdate]);\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction AggressiveUpdate(props: { updatePosition: () => void }) {\n const { updatePosition } = props;\n\n useLayoutEffect(() => {\n frame.read(updatePosition, true);\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition]);\n\n return null;\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n"],"mappings":"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","names":["React","import_fiber","import_react","import_react","import_react","import_react","React"]}
1
+ {"version":3,"sources":["../src/scene/index.ts","../src/scene/BakeScene.tsx","../src/setup.ts","../src/scene/SceneSync.tsx","../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import BakeScene from './BakeScene';\nimport SceneSync from './SceneSync';\n\nexport { BakeScene, SceneSync };\n","import { useFrame } from '@react-three/fiber';\nimport React, {\n Dispatch,\n Fragment,\n type ReactNode,\n SetStateAction,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `BakeScene`: This component will notifiy when the global 3D scene is ready.\n *\n * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire\n * with ease, the component takes advantage of that, and because `useLoader` is unreliable.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notifier as soon as the job is done.\n */\n return (\n !sceneReady && (\n <RenderNotifier {...props} setSceneReady={setSceneReady} />\n )\n );\n}\n\nfunction RenderNotifier(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n props.setSceneReady(true);\n props.onSceneReady();\n }, props.loadTaskDelay ?? 50);\n }\n });\n\n return null;\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { Hud } from '@react-three/drei';\nimport { cancelFrame, frame } from 'motion/react';\nimport React, {\n type ReactNode,\n type RefObject,\n useCallback,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { Group } from 'three';\nimport { useViewport } from '../hooks';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the part of the scene that did not fully moved out of view will stay there.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n hudRenderPriority: number;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (props.trackingMode === 'relaxed' && !weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.hudRenderPriority}>\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const viewport = useViewport();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n const domRect = attach.current!.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n\n const vpWidthRatio = viewport.width / screenW;\n const vpHeightRatio = viewport.height / screenH;\n\n const scrollOffset =\n (weaverSetup._lenisInstance!.actualScroll / screenH) * viewport.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;\n const y =\n viewport.height * 0.5 -\n (domRect.y + weaverSetup._lenisInstance!.actualScroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current!.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current!.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [\n attach,\n control,\n disableScaling,\n onLayoutUpdate,\n scaleFactor,\n stretch,\n viewport.height,\n viewport.width,\n ]);\n\n const graceUpdate = useCallback(() => {\n try {\n updatePosition();\n } catch {\n /* empty */\n }\n }, [updatePosition]);\n\n /**\n * Update position when function changes.\n */\n useLayoutEffect(() => {\n graceUpdate();\n }, [graceUpdate]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n aggressive: <AggressiveUpdate updatePosition={graceUpdate} />,\n };\n\n if (props.control) {\n return (\n <>\n {props.children}\n {mode[props.trackingMode]}\n </>\n );\n }\n\n return (\n <group ref={defaultControl}>\n {props.children}\n {mode[props.trackingMode]}\n </group>\n );\n}\n\nfunction RelaxedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n /**\n * Scroll hook to update object correctly to the current HTML scroll position.\n */\n useLenisCallback(updatePosition, {\n initialCall: true,\n intersectOn: props.attach,\n });\n\n /**\n * Allows the element to resize too.\n */\n useOrbit({\n target: props.attach,\n events: {\n onIntersect: updatePosition,\n onResize: updatePosition,\n },\n });\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction BalancedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n const [shouldUpdate, setShouldUpdate] = useState(false);\n\n useOrbit({\n target: props.attach,\n events: {\n onIntersect(entry) {\n setShouldUpdate(entry.isIntersecting);\n },\n onResize: updatePosition,\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (shouldUpdate) {\n frame.read(updatePosition, true);\n } else {\n cancelFrame(updatePosition);\n }\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition, shouldUpdate]);\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction AggressiveUpdate(props: { updatePosition: () => void }) {\n const { updatePosition } = props;\n\n useLayoutEffect(() => {\n frame.read(updatePosition, true);\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition]);\n\n return null;\n}\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { useCallback, useState } from 'react';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * Get current threejs viewport with the actual current.\n */\nexport function useViewport(\n customCamera?: OrthographicCamera | PerspectiveCamera\n) {\n const { viewport, camera } = useThree();\n\n const [width, setWidth] = useState(viewport.getCurrentViewport().width);\n const [height, setHeight] = useState(viewport.getCurrentViewport().height);\n\n const viewportUpdate = useCallback(() => {\n const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);\n setWidth(actualViewport.width);\n setHeight(actualViewport.height);\n }, [camera, customCamera, viewport]);\n useScreenCallback(viewportUpdate, { initialCall: true });\n\n return { width: width, height: height };\n}\n"],"mappings":"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","names":["React","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react_router","import_react","import_fiber","import_react","React"]}
package/dist/scene.mjs CHANGED
@@ -57,9 +57,9 @@ function BakeScene(props) {
57
57
  }
58
58
  function NotificationHandler(props) {
59
59
  const [sceneReady, setSceneReady] = useState(false);
60
- return !sceneReady && /* @__PURE__ */ React.createElement(RenderNotification, { ...props, setSceneReady });
60
+ return !sceneReady && /* @__PURE__ */ React.createElement(RenderNotifier, { ...props, setSceneReady });
61
61
  }
62
- function RenderNotification(props) {
62
+ function RenderNotifier(props) {
63
63
  const scheduledForCallback = useRef(false);
64
64
  useFrame(() => {
65
65
  if (!scheduledForCallback.current) {
@@ -75,31 +75,51 @@ function RenderNotification(props) {
75
75
 
76
76
  // src/scene/SceneSync.tsx
77
77
  import { Hud } from "@react-three/drei";
78
- import { useThree } from "@react-three/fiber";
79
78
  import { cancelFrame, frame } from "motion/react";
80
79
  import React2, {
81
- useCallback as useCallback2,
80
+ useCallback as useCallback7,
82
81
  useId as useId2,
83
- useLayoutEffect as useLayoutEffect4,
82
+ useLayoutEffect as useLayoutEffect7,
84
83
  useRef as useRef2,
85
- useState as useState2
84
+ useState as useState5
86
85
  } from "react";
87
86
 
87
+ // src/hooks/breakpoints.ts
88
+ import { useCallback, useState as useState2 } from "react";
89
+
90
+ // src/hooks/screenCallback.ts
91
+ import { useLayoutEffect } from "react";
92
+ function useScreenCallback(callback, options) {
93
+ useLayoutEffect(() => {
94
+ const reportScreen = () => callback({
95
+ width: window.innerWidth,
96
+ height: window.innerHeight
97
+ });
98
+ if (options?.initialCall) {
99
+ reportScreen();
100
+ }
101
+ window.addEventListener("resize", reportScreen, { passive: true });
102
+ return () => {
103
+ window.removeEventListener("resize", reportScreen);
104
+ };
105
+ }, [callback, options?.initialCall]);
106
+ }
107
+
88
108
  // src/hooks/effectOnce.ts
89
- import { useEffect, useLayoutEffect } from "react";
109
+ import { useEffect, useLayoutEffect as useLayoutEffect2 } from "react";
90
110
  function useLayoutEffectOnce(callback) {
91
- useLayoutEffect(callback, []);
111
+ useLayoutEffect2(callback, []);
92
112
  }
93
113
 
94
114
  // src/hooks/lenisCallback.ts
95
- import { useCallback, useLayoutEffect as useLayoutEffect3 } from "react";
115
+ import { useCallback as useCallback2, useLayoutEffect as useLayoutEffect4 } from "react";
96
116
 
97
117
  // src/hooks/orbit.ts
98
- import { useLayoutEffect as useLayoutEffect2 } from "react";
118
+ import { useLayoutEffect as useLayoutEffect3 } from "react";
99
119
  function useOrbit(options) {
100
120
  const { onResize, onIntersect } = options.events;
101
121
  const { rootMargin = "25% 0px 25% 0px" } = options;
102
- useLayoutEffect2(() => {
122
+ useLayoutEffect3(() => {
103
123
  if (!options.target) return;
104
124
  if (!options.target.current) return;
105
125
  let orbitResize = void 0;
@@ -129,11 +149,11 @@ function useLenisCallback(callback, options) {
129
149
  "useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance"
130
150
  );
131
151
  }
132
- const callbackWrapScroll = useCallback(
152
+ const callbackWrapScroll = useCallback2(
133
153
  () => callback(weaverSetup._lenisInstance.actualScroll, "scroll"),
134
154
  [callback]
135
155
  );
136
- const callbackWrapResize = useCallback(
156
+ const callbackWrapResize = useCallback2(
137
157
  () => callback(weaverSetup._lenisInstance.actualScroll, "resize"),
138
158
  [callback]
139
159
  );
@@ -152,7 +172,7 @@ function useLenisCallback(callback, options) {
152
172
  },
153
173
  rootMargin: "50% 0px 50% 0px"
154
174
  });
155
- useLayoutEffect3(() => {
175
+ useLayoutEffect4(() => {
156
176
  if (!options?.intersectOn) {
157
177
  weaverSetup._lenisInstance.on("scroll", callbackWrapScroll);
158
178
  window.addEventListener("resize", callbackWrapResize);
@@ -173,11 +193,40 @@ function useLenisCallback(callback, options) {
173
193
  ]);
174
194
  }
175
195
 
196
+ // src/hooks/mouseCallback.ts
197
+ import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect5 } from "react";
198
+
199
+ // src/hooks/navigateAnchor.ts
200
+ import { useCallback as useCallback4 } from "react";
201
+ import { useNavigate } from "react-router";
202
+
203
+ // src/hooks/rawParams.ts
204
+ import { useLocation } from "react-router";
205
+
206
+ // src/hooks/screen.ts
207
+ import { useCallback as useCallback5, useLayoutEffect as useLayoutEffect6, useState as useState3 } from "react";
208
+
209
+ // src/hooks/viewport.ts
210
+ import { useThree } from "@react-three/fiber";
211
+ import { useCallback as useCallback6, useState as useState4 } from "react";
212
+ function useViewport(customCamera) {
213
+ const { viewport, camera } = useThree();
214
+ const [width, setWidth] = useState4(viewport.getCurrentViewport().width);
215
+ const [height, setHeight] = useState4(viewport.getCurrentViewport().height);
216
+ const viewportUpdate = useCallback6(() => {
217
+ const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);
218
+ setWidth(actualViewport.width);
219
+ setHeight(actualViewport.height);
220
+ }, [camera, customCamera, viewport]);
221
+ useScreenCallback(viewportUpdate, { initialCall: true });
222
+ return { width, height };
223
+ }
224
+
176
225
  // src/scene/SceneSync.tsx
177
226
  function SceneSync(props) {
178
- if (!weaverSetup._lenisInstance) {
227
+ if (props.trackingMode === "relaxed" && !weaverSetup._lenisInstance) {
179
228
  throw Error(
180
- "SceneSync won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance"
229
+ "SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance"
181
230
  );
182
231
  }
183
232
  const unique = useId2();
@@ -193,7 +242,7 @@ function SceneSync(props) {
193
242
  return /* @__PURE__ */ React2.createElement(TunnelIn, null, /* @__PURE__ */ React2.createElement(SyncInternal, { key: unique, ...props }));
194
243
  }
195
244
  function SyncInternal(props) {
196
- const { viewport, camera } = useThree();
245
+ const viewport = useViewport();
197
246
  const defaultControl = useRef2(null);
198
247
  const {
199
248
  attach,
@@ -203,23 +252,18 @@ function SyncInternal(props) {
203
252
  disableScaling,
204
253
  onLayoutUpdate
205
254
  } = props;
206
- const properties = useRef2({
207
- viewport: void 0
208
- });
209
- const updatePosition = useCallback2(() => {
255
+ const updatePosition = useCallback7(() => {
210
256
  const activeControl = control ?? defaultControl;
211
- const vp = properties.current.viewport;
212
- if (!vp) return;
213
257
  const domRect = attach.current.getBoundingClientRect();
214
258
  const screenH = window.innerHeight;
215
259
  const screenW = window.innerWidth;
216
- const vpWidthRatio = vp.width / screenW;
217
- const vpHeightRatio = vp.height / screenH;
218
- const scrollOffset = weaverSetup._lenisInstance.actualScroll / screenH * vp.height;
260
+ const vpWidthRatio = viewport.width / screenW;
261
+ const vpHeightRatio = viewport.height / screenH;
262
+ const scrollOffset = weaverSetup._lenisInstance.actualScroll / screenH * viewport.height;
219
263
  const w = domRect.width * vpWidthRatio;
220
264
  const h = domRect.height * vpHeightRatio;
221
- const x = domRect.x * vpWidthRatio + w * 0.5 - vp.width * 0.5;
222
- const y = vp.height * 0.5 - (domRect.y + weaverSetup._lenisInstance.actualScroll) * vpHeightRatio - h * 0.5 + scrollOffset;
265
+ const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;
266
+ const y = viewport.height * 0.5 - (domRect.y + weaverSetup._lenisInstance.actualScroll) * vpHeightRatio - h * 0.5 + scrollOffset;
223
267
  if (onLayoutUpdate) {
224
268
  onLayoutUpdate(domRect, { w, h }, { x, y });
225
269
  }
@@ -238,17 +282,25 @@ function SyncInternal(props) {
238
282
  }
239
283
  activeControl.current.position.x = x;
240
284
  activeControl.current.position.y = y;
241
- }, [attach, onLayoutUpdate, control, scaleFactor, disableScaling, stretch]);
242
- useLayoutEffect4(() => {
243
- properties.current.viewport = viewport.getCurrentViewport(camera);
244
- updatePosition();
245
- }, [camera, updatePosition, viewport]);
246
- const graceUpdate = useCallback2(() => {
285
+ }, [
286
+ attach,
287
+ control,
288
+ disableScaling,
289
+ onLayoutUpdate,
290
+ scaleFactor,
291
+ stretch,
292
+ viewport.height,
293
+ viewport.width
294
+ ]);
295
+ const graceUpdate = useCallback7(() => {
247
296
  try {
248
297
  updatePosition();
249
298
  } catch {
250
299
  }
251
300
  }, [updatePosition]);
301
+ useLayoutEffect7(() => {
302
+ graceUpdate();
303
+ }, [graceUpdate]);
252
304
  const mode = {
253
305
  relaxed: /* @__PURE__ */ React2.createElement(RelaxedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
254
306
  balanced: /* @__PURE__ */ React2.createElement(BalancedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
@@ -277,7 +329,7 @@ function RelaxedUpdate(props) {
277
329
  }
278
330
  function BalancedUpdate(props) {
279
331
  const { updatePosition } = props;
280
- const [shouldUpdate, setShouldUpdate] = useState2(false);
332
+ const [shouldUpdate, setShouldUpdate] = useState5(false);
281
333
  useOrbit({
282
334
  target: props.attach,
283
335
  events: {
@@ -288,7 +340,7 @@ function BalancedUpdate(props) {
288
340
  },
289
341
  rootMargin: "50% 0px 50% 0px"
290
342
  });
291
- useLayoutEffect4(() => {
343
+ useLayoutEffect7(() => {
292
344
  if (shouldUpdate) {
293
345
  frame.read(updatePosition, true);
294
346
  } else {
@@ -303,7 +355,7 @@ function BalancedUpdate(props) {
303
355
  }
304
356
  function AggressiveUpdate(props) {
305
357
  const { updatePosition } = props;
306
- useLayoutEffect4(() => {
358
+ useLayoutEffect7(() => {
307
359
  frame.read(updatePosition, true);
308
360
  return () => {
309
361
  cancelFrame(updatePosition);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/scene/BakeScene.tsx","../src/setup.ts","../src/scene/SceneSync.tsx","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts"],"sourcesContent":["import { useFrame } from '@react-three/fiber';\nimport React, {\n Dispatch,\n Fragment,\n type ReactNode,\n SetStateAction,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `BakeScene`: This component will notifiy when the global 3D scene is ready.\n *\n * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire\n * with ease, the component takes advantage of that, and because `useLoader` is unreliable.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notification as soon as the job is done.\n */\n return (\n !sceneReady && (\n <RenderNotification {...props} setSceneReady={setSceneReady} />\n )\n );\n}\n\nfunction RenderNotification(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n props.setSceneReady(true);\n props.onSceneReady();\n }, props.loadTaskDelay ?? 50);\n }\n });\n\n return null;\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { Hud } from '@react-three/drei';\nimport { useThree, type Viewport } from '@react-three/fiber';\nimport { cancelFrame, frame } from 'motion/react';\nimport React, {\n type ReactNode,\n type RefObject,\n useCallback,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { Group } from 'three';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with a scroll hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the scene that did not fully moved out of view will stay there, as scroll hook will be disabled.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n hudRenderPriority: number;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.hudRenderPriority}>\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const { viewport, camera } = useThree();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const properties: RefObject<{\n viewport?: Omit<Viewport, 'dpr' | 'initialDpr'>;\n }> = useRef({\n viewport: undefined,\n });\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n const vp = properties.current.viewport;\n if (!vp) return;\n\n const domRect = attach.current!.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n\n const vpWidthRatio = vp.width / screenW;\n const vpHeightRatio = vp.height / screenH;\n\n const scrollOffset =\n (weaverSetup._lenisInstance!.actualScroll / screenH) * vp.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - vp.width * 0.5;\n const y =\n vp.height * 0.5 -\n (domRect.y + weaverSetup._lenisInstance!.actualScroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current!.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current!.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [attach, onLayoutUpdate, control, scaleFactor, disableScaling, stretch]);\n\n /**\n * Update position when `camera` changes.\n */\n useLayoutEffect(() => {\n properties.current.viewport = viewport.getCurrentViewport(camera);\n updatePosition();\n }, [camera, updatePosition, viewport]);\n\n const graceUpdate = useCallback(() => {\n try {\n updatePosition();\n } catch {\n /* empty */\n }\n }, [updatePosition]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n aggressive: <AggressiveUpdate updatePosition={graceUpdate} />,\n };\n\n if (props.control) {\n return (\n <>\n {props.children}\n {mode[props.trackingMode]}\n </>\n );\n }\n\n return (\n <group ref={defaultControl}>\n {props.children}\n {mode[props.trackingMode]}\n </group>\n );\n}\n\nfunction RelaxedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n /**\n * Scroll hook to update object correctly to the current HTML scroll position.\n */\n useLenisCallback(updatePosition, {\n initialCall: true,\n intersectOn: props.attach,\n });\n\n /**\n * Allows the element to resize too.\n */\n useOrbit({\n target: props.attach,\n events: {\n onIntersect: updatePosition,\n onResize: updatePosition,\n },\n });\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction BalancedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n const [shouldUpdate, setShouldUpdate] = useState(false);\n\n useOrbit({\n target: props.attach,\n events: {\n onIntersect(entry) {\n setShouldUpdate(entry.isIntersecting);\n },\n onResize: updatePosition,\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (shouldUpdate) {\n frame.read(updatePosition, true);\n } else {\n cancelFrame(updatePosition);\n }\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition, shouldUpdate]);\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction AggressiveUpdate(props: { updatePosition: () => void }) {\n const { updatePosition } = props;\n\n useLayoutEffect(() => {\n frame.read(updatePosition, true);\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition]);\n\n return null;\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n"],"mappings":"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","names":["React","useCallback","useId","useLayoutEffect","useRef","useState","useLayoutEffect","useLayoutEffect","useLayoutEffect","useId","React","useRef","useCallback","useLayoutEffect","useState"]}
1
+ {"version":3,"sources":["../src/scene/BakeScene.tsx","../src/setup.ts","../src/scene/SceneSync.tsx","../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import { useFrame } from '@react-three/fiber';\nimport React, {\n Dispatch,\n Fragment,\n type ReactNode,\n SetStateAction,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `BakeScene`: This component will notifiy when the global 3D scene is ready.\n *\n * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire\n * with ease, the component takes advantage of that, and because `useLoader` is unreliable.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notifier as soon as the job is done.\n */\n return (\n !sceneReady && (\n <RenderNotifier {...props} setSceneReady={setSceneReady} />\n )\n );\n}\n\nfunction RenderNotifier(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n props.setSceneReady(true);\n props.onSceneReady();\n }, props.loadTaskDelay ?? 50);\n }\n });\n\n return null;\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { Hud } from '@react-three/drei';\nimport { cancelFrame, frame } from 'motion/react';\nimport React, {\n type ReactNode,\n type RefObject,\n useCallback,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { Group } from 'three';\nimport { useViewport } from '../hooks';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the part of the scene that did not fully moved out of view will stay there.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n hudRenderPriority: number;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (props.trackingMode === 'relaxed' && !weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.hudRenderPriority}>\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const viewport = useViewport();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n const domRect = attach.current!.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n\n const vpWidthRatio = viewport.width / screenW;\n const vpHeightRatio = viewport.height / screenH;\n\n const scrollOffset =\n (weaverSetup._lenisInstance!.actualScroll / screenH) * viewport.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;\n const y =\n viewport.height * 0.5 -\n (domRect.y + weaverSetup._lenisInstance!.actualScroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current!.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current!.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [\n attach,\n control,\n disableScaling,\n onLayoutUpdate,\n scaleFactor,\n stretch,\n viewport.height,\n viewport.width,\n ]);\n\n const graceUpdate = useCallback(() => {\n try {\n updatePosition();\n } catch {\n /* empty */\n }\n }, [updatePosition]);\n\n /**\n * Update position when function changes.\n */\n useLayoutEffect(() => {\n graceUpdate();\n }, [graceUpdate]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n aggressive: <AggressiveUpdate updatePosition={graceUpdate} />,\n };\n\n if (props.control) {\n return (\n <>\n {props.children}\n {mode[props.trackingMode]}\n </>\n );\n }\n\n return (\n <group ref={defaultControl}>\n {props.children}\n {mode[props.trackingMode]}\n </group>\n );\n}\n\nfunction RelaxedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n /**\n * Scroll hook to update object correctly to the current HTML scroll position.\n */\n useLenisCallback(updatePosition, {\n initialCall: true,\n intersectOn: props.attach,\n });\n\n /**\n * Allows the element to resize too.\n */\n useOrbit({\n target: props.attach,\n events: {\n onIntersect: updatePosition,\n onResize: updatePosition,\n },\n });\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction BalancedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n const [shouldUpdate, setShouldUpdate] = useState(false);\n\n useOrbit({\n target: props.attach,\n events: {\n onIntersect(entry) {\n setShouldUpdate(entry.isIntersecting);\n },\n onResize: updatePosition,\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (shouldUpdate) {\n frame.read(updatePosition, true);\n } else {\n cancelFrame(updatePosition);\n }\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition, shouldUpdate]);\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction AggressiveUpdate(props: { updatePosition: () => void }) {\n const { updatePosition } = props;\n\n useLayoutEffect(() => {\n frame.read(updatePosition, true);\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition]);\n\n return null;\n}\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { useCallback, useState } from 'react';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * Get current threejs viewport with the actual current.\n */\nexport function useViewport(\n customCamera?: OrthographicCamera | PerspectiveCamera\n) {\n const { viewport, camera } = useThree();\n\n const [width, setWidth] = useState(viewport.getCurrentViewport().width);\n const [height, setHeight] = useState(viewport.getCurrentViewport().height);\n\n const viewportUpdate = useCallback(() => {\n const actualViewport = viewport.getCurrentViewport(customCamera ?? camera);\n setWidth(actualViewport.width);\n setHeight(actualViewport.height);\n }, [camera, customCamera, viewport]);\n useScreenCallback(viewportUpdate, { initialCall: true });\n\n return { width: width, height: height };\n}\n"],"mappings":"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","names":["React","useCallback","useId","useLayoutEffect","useRef","useState","useState","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useCallback","useLayoutEffect","useState","useCallback","useState","useState","useCallback","useId","React","useRef","useCallback","useLayoutEffect","useState"]}
package/docs/SCENE.md CHANGED
@@ -64,8 +64,31 @@ function Contact() {
64
64
  ## `SceneSync`
65
65
  Fit your 3D scene inside a DOM element!!!
66
66
 
67
+ Create a DOM element, and pass its ref to `SceneSync`, put your 3D scene inside of `SceneSync`. And that scene will get synced with
68
+ that DOM element, there are multiple ways for you to decide on how a scene should be synced.
69
+
70
+ The component works by creating a group, containing your scene, and position the group along with user scroll, or frame-based positioning.
71
+
67
72
  > [!CAUTION]
68
- > For `SceneSync` to work, you must provide a lenis instance:
73
+ > The only limitation is to never touch the camera, you can initialize a custom camera, but in no way you should move/rotate it.
74
+
75
+ There are 3 modes: `relaxed`, `balanced` and `aggressive`:
76
+ - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.
77
+ - (+): Minimal update calls, best performance.
78
+ - (-): The scene get desynced the moment DOM element moves without changing its sizes.
79
+ When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element
80
+ is out of view, the part of the scene that did not fully moved out of view will stay there.
81
+ - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.
82
+ - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.
83
+ - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same
84
+ problem with `relaxed` mode when the scene bleeds out too much.
85
+ - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.
86
+ - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.
87
+ - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this
88
+ mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.
89
+
90
+ > [!CAUTION]
91
+ > For `SceneSync`'s `relaxed` mode to work, you must provide a lenis instance, other modes doesn't require it:
69
92
  > ```tsx
70
93
  > import { Preload } from '@react-three/drei';
71
94
  > import { Canvas } from '@react-three/fiber';
@@ -108,24 +131,6 @@ Fit your 3D scene inside a DOM element!!!
108
131
  > }
109
132
  > ```
110
133
 
111
- Create a DOM element, and pass its ref to `SceneSync`, put your 3D scene inside of `SceneSync`. And that scene will get synced with
112
- that DOM element, there are multiple ways for you to decide on how a scene should be synced.
113
-
114
- There are 3 modes: `relaxed`, `balanced` and `aggressive`:
115
- - `relaxed`: Uses IntersectionObserver paired with a scroll hook, together with ResizeObserver.
116
- - (+): Minimal update calls, best performance.
117
- - (-): The scene get desynced the moment DOM element moves without changing its sizes.
118
- When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element
119
- is out of view, the scene that did not fully moved out of view will stay there, as scroll hook will be disabled.
120
- - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.
121
- - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.
122
- - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same
123
- problem with `relaxed` mode when the scene bleeds out too much.
124
- - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.
125
- - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.
126
- - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this
127
- mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.
128
-
129
134
  For example, a box inside a DOM element:
130
135
  ```tsx
131
136
  function DOMBox() {
package/package.json CHANGED
@@ -5,8 +5,8 @@
5
5
  "repository": {
6
6
  "url": "git+https://github.com/neveranyart/weaver.git"
7
7
  },
8
- "version": "1.0.20",
9
- "description": "An in-house package by neverany for making interactive React CSR.",
8
+ "version": "1.0.22",
9
+ "description": "A collection of React CSR components, hooks for handling many moving part of a creative web with ease. ",
10
10
  "packageManager": "yarn@4.10.3",
11
11
  "publishConfig": {
12
12
  "access": "public"