@neveranyart/weaver 1.0.19 → 1.0.20

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
@@ -2,16 +2,18 @@
2
2
 
3
3
  **WIP DOCS**
4
4
 
5
- An in-house core package by neveranyart for making performant, interactive React CSR.
5
+ An in-house core package by neveranyart for making performant React CSR.
6
6
 
7
7
  <sub>Almost feels like a framework, but it's not.</sub>
8
8
 
9
9
  ![neverany branding](https://github.com/user-attachments/assets/8c09234f-d592-4adc-8ab1-284b1fca3f7c)
10
10
 
11
11
  ## Introduction
12
- This package is published as an in-depth on `neverany` technicals. It is not intended for broad usage so it has a strict choice of dependencies and a specific way of use.
12
+ This package is a collection of sub-packages for creating a creative web with ease.
13
13
 
14
- But, most components and tools are built with flexibility in mind, providing a balance between abstraction and verbose.
14
+ Most components and tools are built with flexibility in mind, providing a balance between abstraction and verbose.
15
+
16
+ But some will have its constrain, requiring a certain packages to work with.
15
17
 
16
18
  > [!NOTE]
17
19
  > `React v19+` is required.
@@ -30,8 +32,12 @@ yarn add @neveranyart/weaver
30
32
 
31
33
  ## Documentations
32
34
  1. [Routing](https://github.com/neveranyart/weaver/blob/main/docs/ROUTING.md)
33
- 2. Hooks
34
- 3. Scene
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
+ 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)
35
41
 
36
42
  ## License
37
43
  MIT
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\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAsC;;;ACAtC,mBAAgC;AAYzB,SAAS,kBACd,UACA,SACA;AACA,oCAAgB,MAAM;AACpB,UAAM,eAAe,MACnB,SAAS;AAAA,MACP,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,IACjB,CAAC;AAGH,QAAI,SAAS,aAAa;AACxB,mBAAa;AAAA,IACf;AAEA,WAAO,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAEjE,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,YAAY;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,UAAU,SAAS,WAAW,CAAC;AACrC;;;ADZO,SAAS,eACd,cAAwB,CAAC,KAAK,KAAK,MAAM,MAAM,IAAI,GACnD;AACA,QAAM,oBAAgB;AAAA,IACpB,CAAC,UAAkB;AACjB,UAAI,SAAS,YAAa;AAC1B,eAAS,QAAQ,GAAG,QAAQ,YAAa,QAAQ,SAAS;AACxD,YAAI,QAAQ,YAAa,KAAK,GAAG;AAC/B,mBAAS;AACT;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,CAAC,SAAS,UAAU,QAAI;AAAA,IAC5B,cAAc,OAAO,UAAU;AAAA,EACjC;AAEA,QAAM,sBAAkB;AAAA,IACtB,CAAC,WAA8C;AAC7C,YAAM,SAAS,cAAc,OAAO,KAAK;AACzC,UAAI,WAAW,SAAS;AACtB,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAAC,SAAS,aAAa;AAAA,EACzB;AACA,oBAAkB,eAAe;AAEjC,SAAO;AACT;;;AExDA,IAAAC,gBAA2C;AASpC,SAAS,cAAc,UAAgC;AAE5D,+BAAU,UAAU,CAAC,CAAC;AACxB;AASO,SAAS,oBAAoB,UAAgC;AAElE,qCAAgB,UAAU,CAAC,CAAC;AAC9B;;;ACxBA,IAAAC,gBAA6D;;;ACK7D,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA,EAIhB,IAAI,iBAAoC;AACtC,WAAQ,WAAmB;AAAA,EAC7B;AAAA,EACA,IAAI,eAAe,KAAwB;AACzC,IAAC,WAAmB,gBAAgB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,qBAAgD;AAClD,WAAQ,WAAmB;AAAA,EAC7B;AAAA,EACA,IAAI,mBAAmB,KAAgC;AACrD,IAAC,WAAmB,mBAAmB;AAAA,EACzC;AAAA,EAEA,iBAAiB,UAAiB;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA,EACA,YAAY,UAAyB;AACnC,SAAK,qBAAqB;AAAA,EAC5B;AACF;AAEO,IAAM,cAAc,IAAI,YAAY;;;AClC3C,IAAAC,gBAAgD;AASzC,SAAS,SAAS,SAOtB;AACD,QAAM,EAAE,UAAU,YAAY,IAAI,QAAQ;AAC1C,QAAM,EAAE,aAAa,kBAAkB,IAAI;AAE3C,qCAAgB,MAAM;AACpB,QAAI,CAAC,QAAQ,OAAQ;AACrB,QAAI,CAAC,QAAQ,OAAO,QAAS;AAE7B,QAAI,cAAc;AAClB,QAAI,UAAU;AACZ,oBAAc,IAAI,eAAe,CAAC,YAAY,SAAS,QAAQ,CAAC,CAAC,CAAC;AAClE,kBAAY,QAAQ,QAAQ,OAAO,OAAO;AAAA,IAC5C;AACA,QAAI,iBAAiB;AACrB,QAAI,aAAa;AACf,uBAAiB,IAAI;AAAA,QACnB,CAAC,YAAY,YAAY,QAAQ,CAAC,CAAC;AAAA,QACnC,EAAE,WAAW;AAAA,MACf;AACA,qBAAe,QAAQ,QAAQ,OAAO,OAAO;AAAA,IAC/C;AAEA,WAAO,MAAM;AACX,mBAAa,WAAW;AACxB,sBAAgB,WAAW;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,aAAa,UAAU,YAAY,QAAQ,MAAM,CAAC;AACxD;;;AFrBO,SAAS,iBACd,UACA,SACA;AACA,MAAI,CAAC,YAAY,gBAAgB;AAC/B,UAAM;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,QAAM,yBAAqB;AAAA,IACzB,MAAM,SAAS,YAAY,eAAgB,cAAc,QAAQ;AAAA,IACjE,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,yBAAqB;AAAA,IACzB,MAAM,SAAS,YAAY,eAAgB,cAAc,QAAQ;AAAA,IACjE,CAAC,QAAQ;AAAA,EACX;AAEA,WAAS;AAAA,IACP,QAAQ,SAAS;AAAA,IACjB,QAAQ;AAAA,MACN,YAAY,OAAO;AACjB,YAAI,MAAM,gBAAgB;AACxB,sBAAY,eAAgB,GAAG,UAAU,kBAAkB;AAC3D,iBAAO,iBAAiB,UAAU,kBAAkB;AAAA,QACtD,OAAO;AACL,sBAAY,eAAgB,IAAI,UAAU,kBAAkB;AAC5D,iBAAO,oBAAoB,UAAU,kBAAkB;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAAA,IACA,YAAY;AAAA,EACd,CAAC;AAED,qCAAgB,MAAM;AACpB,QAAI,CAAC,SAAS,aAAa;AACzB,kBAAY,eAAgB,GAAG,UAAU,kBAAkB;AAC3D,aAAO,iBAAiB,UAAU,kBAAkB;AAAA,IACtD;AAEA,QAAI,SAAS,aAAa;AACxB,eAAS,YAAY,eAAgB,cAAc,YAAY;AAAA,IACjE;AAEA,WAAO,MAAM;AACX,kBAAY,eAAgB,IAAI,UAAU,kBAAkB;AAC5D,aAAO,oBAAoB,UAAU,kBAAkB;AAAA,IACzD;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACH;;;AG9EA,IAAAC,gBAA6D;AAuBtD,SAAS,iBACd,UACA,SACA;AACA,QAAM,yBAAqB;AAAA,IACzB,MAAM,SAAS,OAAO,SAAS,QAAQ;AAAA,IACvC,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,yBAAqB;AAAA,IACzB,MAAM,SAAS,OAAO,SAAS,QAAQ;AAAA,IACvC,CAAC,QAAQ;AAAA,EACX;AAEA,WAAS;AAAA,IACP,QAAQ,SAAS;AAAA,IACjB,QAAQ;AAAA,MACN,YAAY,OAAO;AACjB,YAAI,MAAM,gBAAgB;AACxB,iBAAO,iBAAiB,UAAU,kBAAkB;AACpD,iBAAO,iBAAiB,UAAU,kBAAkB;AAAA,QACtD,OAAO;AACL,iBAAO,oBAAoB,UAAU,kBAAkB;AACvD,iBAAO,oBAAoB,UAAU,kBAAkB;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAAA,IACA,YAAY;AAAA,EACd,CAAC;AAED,qCAAgB,MAAM;AACpB,QAAI,CAAC,SAAS,aAAa;AACzB,aAAO,iBAAiB,UAAU,kBAAkB;AACpD,aAAO,iBAAiB,UAAU,kBAAkB;AAAA,IACtD;AAEA,QAAI,SAAS,aAAa;AACxB,eAAS,OAAO,SAAS,YAAY;AAAA,IACvC;AAEA,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,kBAAkB;AACvD,aAAO,oBAAoB,UAAU,kBAAkB;AAAA,IACzD;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACH;;;ACzEA,IAAAC,gBAA4B;AAC5B,0BAA4B;AAqBrB,SAAS,kBACd,YACA,aACA;AACA,QAAM,eAAW,iCAAY;AAE7B,aAAO;AAAA,IACL,CAAC,UAA2D;AAC1D,YAAM,eAAe;AACrB,YAAM,OAAO,MAAM,cAAc,aAAa,MAAM;AACpD,UAAI,MAAM;AACR,YAAI,YAAY;AACd,qBAAW;AAAA,QACb;AAEA,YAAI,SAAS,OAAO,SAAS,UAAU;AACrC,mBAAS,MAAM,cAAc,aAAa,MAAM,KAAK,EAAE;AAAA,QACzD,OAAO;AACL,cAAI,aAAa;AACf,wBAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,YAAY,UAAU,WAAW;AAAA,EACpC;AACF;;;AChDA,IAAAC,uBAA4B;AAOrB,SAAS,eAAuC;AACrD,QAAM,EAAE,SAAS,QAAI,kCAAY;AAEjC,SAAO,SAAS,MAAM,GAAG,EAAE,OAAO,CAAC,UAAU,UAAU,EAAE;AAC3D;;;ACXA,IAAAC,gBAAuD;AAKhD,SAAS,YAAY;AAC1B,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,OAAO,UAAU;AACpD,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAS,OAAO,WAAW;AAEvD,QAAM,gBAAY,2BAAY,MAAM;AAClC,aAAS,OAAO,UAAU;AAC1B,cAAU,OAAO,WAAW;AAAA,EAC9B,GAAG,CAAC,CAAC;AAEL,qCAAgB,MAAM;AACpB,WAAO,iBAAiB,UAAU,WAAW,EAAE,SAAS,KAAK,CAAC;AAE9D,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,SAAS;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAEd,SAAO,EAAE,OAAc,OAAe;AACxC;;;ACvBA,mBAAyB;AACzB,IAAAC,gBAAsC;AAO/B,SAAS,YACd,cACA;AACA,QAAM,EAAE,UAAU,OAAO,QAAI,uBAAS;AAEtC,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAS,SAAS,mBAAmB,EAAE,KAAK;AACtE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAS,SAAS,mBAAmB,EAAE,MAAM;AAEzE,QAAM,qBAAiB,2BAAY,MAAM;AACvC,UAAM,iBAAiB,SAAS,mBAAmB,gBAAgB,MAAM;AACzE,aAAS,eAAe,KAAK;AAC7B,cAAU,eAAe,MAAM;AAAA,EACjC,GAAG,CAAC,QAAQ,cAAc,QAAQ,CAAC;AACnC,oBAAkB,cAAc;AAEhC,SAAO,EAAE,OAAc,OAAe;AACxC;","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);\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 +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\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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);\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"]}
@@ -4,6 +4,10 @@ import { ReactNode } from 'react';
4
4
  type BasicTunnelIn = ({ children }: {
5
5
  children: ReactNode;
6
6
  }) => null;
7
+ declare global {
8
+ var __weaverLenis: Lenis | undefined;
9
+ var __weaver3DTunnel: BasicTunnelIn | undefined;
10
+ }
7
11
  declare class WeaverSetup {
8
12
  /**
9
13
  * This variable is handled internally by weaver. **Do not use**.
@@ -4,6 +4,10 @@ import { ReactNode } from 'react';
4
4
  type BasicTunnelIn = ({ children }: {
5
5
  children: ReactNode;
6
6
  }) => null;
7
+ declare global {
8
+ var __weaverLenis: Lenis | undefined;
9
+ var __weaver3DTunnel: BasicTunnelIn | undefined;
10
+ }
7
11
  declare class WeaverSetup {
8
12
  /**
9
13
  * This variable is handled internally by weaver. **Do not use**.
package/dist/index.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- export { w as weaverSetup } from './index-JFUcplI8.mjs';
1
+ export { w as weaverSetup } from './index-Bknw3PY_.mjs';
2
2
  import 'lenis';
3
3
  import 'react';
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { w as weaverSetup } from './index-JFUcplI8.js';
1
+ export { w as weaverSetup } from './index-Bknw3PY_.js';
2
2
  import 'lenis';
3
3
  import 'react';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/setup.ts"],"sourcesContent":["import { weaverSetup } from './setup';\nexport { weaverSetup };\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA,EAIhB,IAAI,iBAAoC;AACtC,WAAQ,WAAmB;AAAA,EAC7B;AAAA,EACA,IAAI,eAAe,KAAwB;AACzC,IAAC,WAAmB,gBAAgB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,qBAAgD;AAClD,WAAQ,WAAmB;AAAA,EAC7B;AAAA,EACA,IAAI,mBAAmB,KAAgC;AACrD,IAAC,WAAmB,mBAAmB;AAAA,EACzC;AAAA,EAEA,iBAAiB,UAAiB;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA,EACA,YAAY,UAAyB;AACnC,SAAK,qBAAqB;AAAA,EAC5B;AACF;AAEO,IAAM,cAAc,IAAI,YAAY;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/setup.ts"],"sourcesContent":["import { weaverSetup } from './setup';\nexport { weaverSetup };\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACUA,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA,EAIhB,IAAI,iBAAoC;AACtC,WAAO,WAAW;AAAA,EACpB;AAAA,EACA,IAAI,eAAe,KAAwB;AACzC,eAAW,gBAAgB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,qBAAgD;AAClD,WAAO,WAAW;AAAA,EACpB;AAAA,EACA,IAAI,mBAAmB,KAAgC;AACrD,eAAW,mBAAmB;AAAA,EAChC;AAAA,EAEA,iBAAiB,UAAiB;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA,EACA,YAAY,UAAyB;AACnC,SAAK,qBAAqB;AAAA,EAC5B;AACF;AAEO,IAAM,cAAc,IAAI,YAAY;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/setup.ts"],"sourcesContent":["import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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"],"mappings":";AAKA,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA,EAIhB,IAAI,iBAAoC;AACtC,WAAQ,WAAmB;AAAA,EAC7B;AAAA,EACA,IAAI,eAAe,KAAwB;AACzC,IAAC,WAAmB,gBAAgB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,qBAAgD;AAClD,WAAQ,WAAmB;AAAA,EAC7B;AAAA,EACA,IAAI,mBAAmB,KAAgC;AACrD,IAAC,WAAmB,mBAAmB;AAAA,EACzC;AAAA,EAEA,iBAAiB,UAAiB;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA,EACA,YAAY,UAAyB;AACnC,SAAK,qBAAqB;AAAA,EAC5B;AACF;AAEO,IAAM,cAAc,IAAI,YAAY;","names":[]}
1
+ {"version":3,"sources":["../src/setup.ts"],"sourcesContent":["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"],"mappings":";AAUA,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA,EAIhB,IAAI,iBAAoC;AACtC,WAAO,WAAW;AAAA,EACpB;AAAA,EACA,IAAI,eAAe,KAAwB;AACzC,eAAW,gBAAgB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,qBAAgD;AAClD,WAAO,WAAW;AAAA,EACpB;AAAA,EACA,IAAI,mBAAmB,KAAgC;AACrD,eAAW,mBAAmB;AAAA,EAChC;AAAA,EAEA,iBAAiB,UAAiB;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA,EACA,YAAY,UAAyB;AACnC,SAAK,qBAAqB;AAAA,EAC5B;AACF;AAEO,IAAM,cAAc,IAAI,YAAY;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/routing/index.ts","../src/routing/context.ts","../src/routing/DelayedOutlet.tsx","../src/utils/getParentRoute.ts","../src/routing/Pipeline.tsx","../src/hooks/effectOnce.ts","../src/setup.ts"],"sourcesContent":["import { useWeaverContext, WeaverContextGetter } from './context';\nimport DelayedOutlet from './DelayedOutlet';\nimport Pipeline from './Pipeline';\n\n/// A read-only state for reacting with changes reflected by weaver.\nexport const useWeaverState = (givenState: keyof WeaverContextGetter) => {\n const state = useWeaverContext((state) => state[givenState]);\n return state;\n};\n\nexport { DelayedOutlet, Pipeline };\n","import { create } from 'zustand/react';\n\nexport interface WeaverContextGetter {\n /**\n * Current active parent, **AFTER** Pipeline has finished rendering, so the `activeParent`\n * will be delayed. Example: \"/\", \"/works\",...\n */\n activeParent: string;\n\n /**\n * Current active Pipeline. Example: \"/\", \"/works\",...\n *\n * This is **not based on URL**, it's based on which `Pipeline` has access to the the site.\n */\n activePipeline: string;\n\n /**\n * `DelayedOutlet` special variable, indicating if the route is current transitioning to a new route or not.\n */\n navigating: boolean;\n\n /**\n * When the page is rendered, it will turns this to true,\n * any parent page navigation will causes this to goes false, set to false in `DelayedOulet`.\n *\n * It's set to `false` right after `navigating` is set to `true`. `true` statement will be handled by `Pipeline`.\n */\n pageRendered: boolean;\n}\n\ninterface WeaverContextSetter {\n setActiveParent: (activeParent: string) => void;\n setActivePipeline: (activePipeline: string) => void;\n setNavigating: (navigating: boolean) => void;\n setPageRendered: (pageRendered: boolean) => void;\n}\n\nexport const useWeaverContext = create<\n WeaverContextSetter & WeaverContextGetter\n>()((set) => ({\n activeParent: '',\n setActiveParent: (activeParent) => set({ activeParent }),\n\n activePipeline: '',\n setActivePipeline: (activePipeline) => set({ activePipeline }),\n\n navigating: true,\n setNavigating: (navigating) => set({ navigating }),\n\n pageRendered: false,\n setPageRendered: (pageRendered) => set({ pageRendered }),\n}));\n","import { type ReactNode, useLayoutEffect, useRef, useState } from 'react';\nimport { useOutlet } from 'react-router';\nimport { getParentRoute } from '../utils/getParentRoute';\nimport { useWeaverContext } from './context';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * Delaying the routing process from `react-router`, handles gracefully between `Pipeline`s\n * while allowing any loading fallback component to listen and react with event changes.\n */\nexport default function DelayedOutlet(props: { delay: number }) {\n const activePipeline = useWeaverContext((state) => state.activePipeline);\n const setNavigating = useWeaverContext((state) => state.setNavigating);\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n\n const renderPageTask = useRef(setTimeout(() => {}));\n const routerOutlet = useOutlet();\n\n const [renderer, setRenderer] = useState<ReactNode>(null);\n\n // Handle and update new parent page.\n useLayoutEffect(() => {\n /**\n * `pageRendered` should be outside of `renderPageTask`,\n * we alrady have `Pipeline` to correct errors.\n */\n if ('scrollRestoration' in history) {\n history.scrollRestoration = 'manual';\n }\n\n if (getParentRoute(window.location.pathname) === activePipeline) {\n // Avoid while changing page, cancelling before new page pushed in\n // cause `activePipeline` to not change, making `navigating` softlock.\n setNavigating(false);\n return;\n }\n\n setNavigating(true);\n setPageRendered(false);\n\n clearTimeout(renderPageTask.current);\n renderPageTask.current = setTimeout(() => {\n setRenderer(routerOutlet!);\n setNavigating(false);\n }, props.delay);\n\n return () => {\n /**\n * Clear invalid task that were scheduled last effect.\n */\n clearInterval(renderPageTask.current);\n };\n }, [\n activePipeline,\n props.delay,\n routerOutlet,\n setNavigating,\n setPageRendered,\n ]);\n\n return renderer;\n}\n","export function getParentRoute(pathname: string) {\n let slashes = 0;\n let sliceAt = 0;\n for (sliceAt; sliceAt < pathname.length; sliceAt++) {\n if (pathname[sliceAt] == '/') {\n slashes++;\n }\n if (slashes === 2) {\n break;\n }\n }\n\n return pathname.slice(0, sliceAt);\n}\n","import React, { useLayoutEffect, type ReactNode } from 'react';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { weaverSetup } from '../setup';\nimport { useWeaverContext } from './context';\n\ninterface PipelineProps {\n children?: ReactNode;\n\n /**\n * A state switch to notifies `Pipeline` that the content and elements of the page is ready to be displayed.\n *\n * Usually, you will need to preload some other external sources, or initialize 3D scene, this state\n * make sure that the loading fallback doesn't mess up and show initializing stuff.\n *\n * Using `BakeScene`, you can ensure that the scene is loaded via its callback, you can then pass the state value that\n * `BakeScene` changes to this variable to hide all the lags behind loading screen.\n */\n contentReady?: boolean;\n\n /**\n * Title for the page.\n */\n title: string;\n\n /**\n * This is crucial for different `Pipeline`s to differentiate each other and avoiding conflict.\n *\n * Example for parent path: `/`, `/about`, `/projects`,...\n */\n parentPath: string;\n\n /**\n * If provided, `Pipeline` will log the current phase to console.\n */\n debugName?: string;\n\n /**\n * This is the only feature where weaver will use lenis to directly manipulate scrolling behaviour.\n *\n * By default, this value is `true` when a lenis instance is provided.\n *\n * `Pipeline` will stop and start lenis automatically when this variable is `true`.\n *\n * Disable this feature to gains complete control over lenis when mounting/unmounting to handle however you want.\n */\n lenisUsage?: boolean;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `Pipeline`: Notifies & reflect changes to/from `LoadingFallback`\n * and `DelayedOutlet` about its page loading status.\n *\n * All parent routes must have `Pipeline` in order to work and sync with `DelayedOutlet`.\n */\nexport default function Pipeline(props: PipelineProps) {\n const { children, ...restOfProps } = props;\n\n return (\n <>\n <RouteHandler {...restOfProps} />\n {children}\n </>\n );\n}\n\nfunction RouteHandler(props: Omit<PipelineProps, 'children'>) {\n const navigating = useWeaverContext((state) => state.navigating);\n const pageRendered = useWeaverContext((state) => state.pageRendered);\n const activeParent = useWeaverContext((state) => state.activeParent);\n\n const setActivePipeline = useWeaverContext(\n (state) => state.setActivePipeline\n );\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n const setActiveParent = useWeaverContext((state) => state.setActiveParent);\n\n useLayoutEffect(() => {\n document.title = props.title;\n setActivePipeline(props.parentPath);\n\n if (props.contentReady !== undefined && !props.contentReady) {\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Loading scene`);\n return;\n }\n\n if (!navigating && (!pageRendered || activeParent !== props.parentPath)) {\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.start();\n }\n\n setPageRendered(true);\n setActiveParent(props.parentPath);\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Mounted`);\n }\n }, [\n activeParent,\n setActivePipeline,\n navigating,\n pageRendered,\n props.lenisUsage,\n props.contentReady,\n props.debugName,\n props.parentPath,\n props.title,\n setActiveParent,\n setPageRendered,\n ]);\n\n useLayoutEffectOnce(() => {\n return () => {\n /**\n * When unmounted, stop lenis to pass control to another Pipline instance.\n *\n * The unmount process only happens when the `<LoadingFallback />` kicks in,\n * so any visual glitches can happen in here, we can now set the scroll position to 0.\n */\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.stop();\n weaverSetup._lenisInstance?.scrollTo(0, {\n immediate: true,\n force: true,\n });\n }\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Unmounted`);\n };\n });\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 Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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"],"mappings":"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","names":["import_react","import_react","import_react","React","state"]}
1
+ {"version":3,"sources":["../src/routing/index.ts","../src/routing/context.ts","../src/routing/DelayedOutlet.tsx","../src/utils/getParentRoute.ts","../src/routing/Pipeline.tsx","../src/hooks/effectOnce.ts","../src/setup.ts"],"sourcesContent":["import { useWeaverContext, WeaverContextGetter } from './context';\nimport DelayedOutlet from './DelayedOutlet';\nimport Pipeline from './Pipeline';\n\n/// A read-only state for reacting with changes reflected by weaver.\nexport const useWeaverState = (givenState: keyof WeaverContextGetter) => {\n const state = useWeaverContext((state) => state[givenState]);\n return state;\n};\n\nexport { DelayedOutlet, Pipeline };\n","import { create } from 'zustand/react';\n\nexport interface WeaverContextGetter {\n /**\n * Current active parent, **AFTER** Pipeline has finished rendering, so the `activeParent`\n * will be delayed. Example: \"/\", \"/works\",...\n */\n activeParent: string;\n\n /**\n * Current active Pipeline. Example: \"/\", \"/works\",...\n *\n * This is **not based on URL**, it's based on which `Pipeline` has access to the the site.\n */\n activePipeline: string;\n\n /**\n * `DelayedOutlet` special variable, indicating if the route is current transitioning to a new route or not.\n */\n navigating: boolean;\n\n /**\n * When the page is rendered, it will turns this to true,\n * any parent page navigation will causes this to goes false, set to false in `DelayedOulet`.\n *\n * It's set to `false` right after `navigating` is set to `true`. `true` statement will be handled by `Pipeline`.\n */\n pageRendered: boolean;\n}\n\ninterface WeaverContextSetter {\n setActiveParent: (activeParent: string) => void;\n setActivePipeline: (activePipeline: string) => void;\n setNavigating: (navigating: boolean) => void;\n setPageRendered: (pageRendered: boolean) => void;\n}\n\nexport const useWeaverContext = create<\n WeaverContextSetter & WeaverContextGetter\n>()((set) => ({\n activeParent: '',\n setActiveParent: (activeParent) => set({ activeParent }),\n\n activePipeline: '',\n setActivePipeline: (activePipeline) => set({ activePipeline }),\n\n navigating: true,\n setNavigating: (navigating) => set({ navigating }),\n\n pageRendered: false,\n setPageRendered: (pageRendered) => set({ pageRendered }),\n}));\n","import { type ReactNode, useLayoutEffect, useRef, useState } from 'react';\nimport { useOutlet } from 'react-router';\nimport { getParentRoute } from '../utils/getParentRoute';\nimport { useWeaverContext } from './context';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * Delaying the routing process from `react-router`, handles gracefully between `Pipeline`s\n * while allowing any loading fallback component to listen and react with event changes.\n */\nexport default function DelayedOutlet(props: { delay: number }) {\n const activePipeline = useWeaverContext((state) => state.activePipeline);\n const setNavigating = useWeaverContext((state) => state.setNavigating);\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n\n const renderPageTask = useRef(setTimeout(() => {}));\n const routerOutlet = useOutlet();\n\n const [renderer, setRenderer] = useState<ReactNode>(null);\n\n // Handle and update new parent page.\n useLayoutEffect(() => {\n /**\n * `pageRendered` should be outside of `renderPageTask`,\n * we alrady have `Pipeline` to correct errors.\n */\n if ('scrollRestoration' in history) {\n history.scrollRestoration = 'manual';\n }\n\n if (getParentRoute(window.location.pathname) === activePipeline) {\n // Avoid while changing page, cancelling before new page pushed in\n // cause `activePipeline` to not change, making `navigating` softlock.\n setNavigating(false);\n return;\n }\n\n setNavigating(true);\n setPageRendered(false);\n\n clearTimeout(renderPageTask.current);\n renderPageTask.current = setTimeout(() => {\n setRenderer(routerOutlet!);\n setNavigating(false);\n }, props.delay);\n\n return () => {\n /**\n * Clear invalid task that were scheduled last effect.\n */\n clearInterval(renderPageTask.current);\n };\n }, [\n activePipeline,\n props.delay,\n routerOutlet,\n setNavigating,\n setPageRendered,\n ]);\n\n return renderer;\n}\n","export function getParentRoute(pathname: string) {\n let slashes = 0;\n let sliceAt = 0;\n for (sliceAt; sliceAt < pathname.length; sliceAt++) {\n if (pathname[sliceAt] == '/') {\n slashes++;\n }\n if (slashes === 2) {\n break;\n }\n }\n\n return pathname.slice(0, sliceAt);\n}\n","import React, { useLayoutEffect, type ReactNode } from 'react';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { weaverSetup } from '../setup';\nimport { useWeaverContext } from './context';\n\ninterface PipelineProps {\n children?: ReactNode;\n\n /**\n * A state switch to notifies `Pipeline` that the content and elements of the page is ready to be displayed.\n *\n * Usually, you will need to preload some other external sources, or initialize 3D scene, this state\n * make sure that the loading fallback doesn't mess up and show initializing stuff.\n *\n * Using `BakeScene`, you can ensure that the scene is loaded via its callback, you can then pass the state value that\n * `BakeScene` changes to this variable to hide all the lags behind loading screen.\n */\n contentReady?: boolean;\n\n /**\n * Title for the page.\n */\n title: string;\n\n /**\n * This is crucial for different `Pipeline`s to differentiate each other and avoiding conflict.\n *\n * Example for parent path: `/`, `/about`, `/projects`,...\n */\n parentPath: string;\n\n /**\n * If provided, `Pipeline` will log the current phase to console.\n */\n debugName?: string;\n\n /**\n * This is the only feature where weaver will use lenis to directly manipulate scrolling behaviour.\n *\n * By default, this value is `true` when a lenis instance is provided.\n *\n * `Pipeline` will stop and start lenis automatically when this variable is `true`.\n *\n * Disable this feature to gains complete control over lenis when mounting/unmounting to handle however you want.\n */\n lenisUsage?: boolean;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `Pipeline`: Notifies & reflect changes to/from `LoadingFallback`\n * and `DelayedOutlet` about its page loading status.\n *\n * All parent routes must have `Pipeline` in order to work and sync with `DelayedOutlet`.\n */\nexport default function Pipeline(props: PipelineProps) {\n const { children, ...restOfProps } = props;\n\n return (\n <>\n <RouteHandler {...restOfProps} />\n {children}\n </>\n );\n}\n\nfunction RouteHandler(props: Omit<PipelineProps, 'children'>) {\n const navigating = useWeaverContext((state) => state.navigating);\n const pageRendered = useWeaverContext((state) => state.pageRendered);\n const activeParent = useWeaverContext((state) => state.activeParent);\n\n const setActivePipeline = useWeaverContext(\n (state) => state.setActivePipeline\n );\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n const setActiveParent = useWeaverContext((state) => state.setActiveParent);\n\n useLayoutEffect(() => {\n document.title = props.title;\n setActivePipeline(props.parentPath);\n\n if (props.contentReady !== undefined && !props.contentReady) {\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Loading scene`);\n return;\n }\n\n if (!navigating && (!pageRendered || activeParent !== props.parentPath)) {\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.start();\n }\n\n setPageRendered(true);\n setActiveParent(props.parentPath);\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Mounted`);\n }\n }, [\n activeParent,\n setActivePipeline,\n navigating,\n pageRendered,\n props.lenisUsage,\n props.contentReady,\n props.debugName,\n props.parentPath,\n props.title,\n setActiveParent,\n setPageRendered,\n ]);\n\n useLayoutEffectOnce(() => {\n return () => {\n /**\n * When unmounted, stop lenis to pass control to another Pipline instance.\n *\n * The unmount process only happens when the `<LoadingFallback />` kicks in,\n * so any visual glitches can happen in here, we can now set the scroll position to 0.\n */\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.stop();\n weaverSetup._lenisInstance?.scrollTo(0, {\n immediate: true,\n force: true,\n });\n }\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Unmounted`);\n };\n });\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 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"],"mappings":"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","names":["import_react","import_react","import_react","React","state"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/routing/context.ts","../src/routing/DelayedOutlet.tsx","../src/utils/getParentRoute.ts","../src/routing/Pipeline.tsx","../src/hooks/effectOnce.ts","../src/setup.ts","../src/routing/index.ts"],"sourcesContent":["import { create } from 'zustand/react';\n\nexport interface WeaverContextGetter {\n /**\n * Current active parent, **AFTER** Pipeline has finished rendering, so the `activeParent`\n * will be delayed. Example: \"/\", \"/works\",...\n */\n activeParent: string;\n\n /**\n * Current active Pipeline. Example: \"/\", \"/works\",...\n *\n * This is **not based on URL**, it's based on which `Pipeline` has access to the the site.\n */\n activePipeline: string;\n\n /**\n * `DelayedOutlet` special variable, indicating if the route is current transitioning to a new route or not.\n */\n navigating: boolean;\n\n /**\n * When the page is rendered, it will turns this to true,\n * any parent page navigation will causes this to goes false, set to false in `DelayedOulet`.\n *\n * It's set to `false` right after `navigating` is set to `true`. `true` statement will be handled by `Pipeline`.\n */\n pageRendered: boolean;\n}\n\ninterface WeaverContextSetter {\n setActiveParent: (activeParent: string) => void;\n setActivePipeline: (activePipeline: string) => void;\n setNavigating: (navigating: boolean) => void;\n setPageRendered: (pageRendered: boolean) => void;\n}\n\nexport const useWeaverContext = create<\n WeaverContextSetter & WeaverContextGetter\n>()((set) => ({\n activeParent: '',\n setActiveParent: (activeParent) => set({ activeParent }),\n\n activePipeline: '',\n setActivePipeline: (activePipeline) => set({ activePipeline }),\n\n navigating: true,\n setNavigating: (navigating) => set({ navigating }),\n\n pageRendered: false,\n setPageRendered: (pageRendered) => set({ pageRendered }),\n}));\n","import { type ReactNode, useLayoutEffect, useRef, useState } from 'react';\nimport { useOutlet } from 'react-router';\nimport { getParentRoute } from '../utils/getParentRoute';\nimport { useWeaverContext } from './context';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * Delaying the routing process from `react-router`, handles gracefully between `Pipeline`s\n * while allowing any loading fallback component to listen and react with event changes.\n */\nexport default function DelayedOutlet(props: { delay: number }) {\n const activePipeline = useWeaverContext((state) => state.activePipeline);\n const setNavigating = useWeaverContext((state) => state.setNavigating);\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n\n const renderPageTask = useRef(setTimeout(() => {}));\n const routerOutlet = useOutlet();\n\n const [renderer, setRenderer] = useState<ReactNode>(null);\n\n // Handle and update new parent page.\n useLayoutEffect(() => {\n /**\n * `pageRendered` should be outside of `renderPageTask`,\n * we alrady have `Pipeline` to correct errors.\n */\n if ('scrollRestoration' in history) {\n history.scrollRestoration = 'manual';\n }\n\n if (getParentRoute(window.location.pathname) === activePipeline) {\n // Avoid while changing page, cancelling before new page pushed in\n // cause `activePipeline` to not change, making `navigating` softlock.\n setNavigating(false);\n return;\n }\n\n setNavigating(true);\n setPageRendered(false);\n\n clearTimeout(renderPageTask.current);\n renderPageTask.current = setTimeout(() => {\n setRenderer(routerOutlet!);\n setNavigating(false);\n }, props.delay);\n\n return () => {\n /**\n * Clear invalid task that were scheduled last effect.\n */\n clearInterval(renderPageTask.current);\n };\n }, [\n activePipeline,\n props.delay,\n routerOutlet,\n setNavigating,\n setPageRendered,\n ]);\n\n return renderer;\n}\n","export function getParentRoute(pathname: string) {\n let slashes = 0;\n let sliceAt = 0;\n for (sliceAt; sliceAt < pathname.length; sliceAt++) {\n if (pathname[sliceAt] == '/') {\n slashes++;\n }\n if (slashes === 2) {\n break;\n }\n }\n\n return pathname.slice(0, sliceAt);\n}\n","import React, { useLayoutEffect, type ReactNode } from 'react';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { weaverSetup } from '../setup';\nimport { useWeaverContext } from './context';\n\ninterface PipelineProps {\n children?: ReactNode;\n\n /**\n * A state switch to notifies `Pipeline` that the content and elements of the page is ready to be displayed.\n *\n * Usually, you will need to preload some other external sources, or initialize 3D scene, this state\n * make sure that the loading fallback doesn't mess up and show initializing stuff.\n *\n * Using `BakeScene`, you can ensure that the scene is loaded via its callback, you can then pass the state value that\n * `BakeScene` changes to this variable to hide all the lags behind loading screen.\n */\n contentReady?: boolean;\n\n /**\n * Title for the page.\n */\n title: string;\n\n /**\n * This is crucial for different `Pipeline`s to differentiate each other and avoiding conflict.\n *\n * Example for parent path: `/`, `/about`, `/projects`,...\n */\n parentPath: string;\n\n /**\n * If provided, `Pipeline` will log the current phase to console.\n */\n debugName?: string;\n\n /**\n * This is the only feature where weaver will use lenis to directly manipulate scrolling behaviour.\n *\n * By default, this value is `true` when a lenis instance is provided.\n *\n * `Pipeline` will stop and start lenis automatically when this variable is `true`.\n *\n * Disable this feature to gains complete control over lenis when mounting/unmounting to handle however you want.\n */\n lenisUsage?: boolean;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `Pipeline`: Notifies & reflect changes to/from `LoadingFallback`\n * and `DelayedOutlet` about its page loading status.\n *\n * All parent routes must have `Pipeline` in order to work and sync with `DelayedOutlet`.\n */\nexport default function Pipeline(props: PipelineProps) {\n const { children, ...restOfProps } = props;\n\n return (\n <>\n <RouteHandler {...restOfProps} />\n {children}\n </>\n );\n}\n\nfunction RouteHandler(props: Omit<PipelineProps, 'children'>) {\n const navigating = useWeaverContext((state) => state.navigating);\n const pageRendered = useWeaverContext((state) => state.pageRendered);\n const activeParent = useWeaverContext((state) => state.activeParent);\n\n const setActivePipeline = useWeaverContext(\n (state) => state.setActivePipeline\n );\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n const setActiveParent = useWeaverContext((state) => state.setActiveParent);\n\n useLayoutEffect(() => {\n document.title = props.title;\n setActivePipeline(props.parentPath);\n\n if (props.contentReady !== undefined && !props.contentReady) {\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Loading scene`);\n return;\n }\n\n if (!navigating && (!pageRendered || activeParent !== props.parentPath)) {\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.start();\n }\n\n setPageRendered(true);\n setActiveParent(props.parentPath);\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Mounted`);\n }\n }, [\n activeParent,\n setActivePipeline,\n navigating,\n pageRendered,\n props.lenisUsage,\n props.contentReady,\n props.debugName,\n props.parentPath,\n props.title,\n setActiveParent,\n setPageRendered,\n ]);\n\n useLayoutEffectOnce(() => {\n return () => {\n /**\n * When unmounted, stop lenis to pass control to another Pipline instance.\n *\n * The unmount process only happens when the `<LoadingFallback />` kicks in,\n * so any visual glitches can happen in here, we can now set the scroll position to 0.\n */\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.stop();\n weaverSetup._lenisInstance?.scrollTo(0, {\n immediate: true,\n force: true,\n });\n }\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Unmounted`);\n };\n });\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 Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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 { useWeaverContext, WeaverContextGetter } from './context';\nimport DelayedOutlet from './DelayedOutlet';\nimport Pipeline from './Pipeline';\n\n/// A read-only state for reacting with changes reflected by weaver.\nexport const useWeaverState = (givenState: keyof WeaverContextGetter) => {\n const state = useWeaverContext((state) => state[givenState]);\n return state;\n};\n\nexport { DelayedOutlet, Pipeline };\n"],"mappings":"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","names":["useLayoutEffect","useLayoutEffect","useLayoutEffect","useLayoutEffect","state"]}
1
+ {"version":3,"sources":["../src/routing/context.ts","../src/routing/DelayedOutlet.tsx","../src/utils/getParentRoute.ts","../src/routing/Pipeline.tsx","../src/hooks/effectOnce.ts","../src/setup.ts","../src/routing/index.ts"],"sourcesContent":["import { create } from 'zustand/react';\n\nexport interface WeaverContextGetter {\n /**\n * Current active parent, **AFTER** Pipeline has finished rendering, so the `activeParent`\n * will be delayed. Example: \"/\", \"/works\",...\n */\n activeParent: string;\n\n /**\n * Current active Pipeline. Example: \"/\", \"/works\",...\n *\n * This is **not based on URL**, it's based on which `Pipeline` has access to the the site.\n */\n activePipeline: string;\n\n /**\n * `DelayedOutlet` special variable, indicating if the route is current transitioning to a new route or not.\n */\n navigating: boolean;\n\n /**\n * When the page is rendered, it will turns this to true,\n * any parent page navigation will causes this to goes false, set to false in `DelayedOulet`.\n *\n * It's set to `false` right after `navigating` is set to `true`. `true` statement will be handled by `Pipeline`.\n */\n pageRendered: boolean;\n}\n\ninterface WeaverContextSetter {\n setActiveParent: (activeParent: string) => void;\n setActivePipeline: (activePipeline: string) => void;\n setNavigating: (navigating: boolean) => void;\n setPageRendered: (pageRendered: boolean) => void;\n}\n\nexport const useWeaverContext = create<\n WeaverContextSetter & WeaverContextGetter\n>()((set) => ({\n activeParent: '',\n setActiveParent: (activeParent) => set({ activeParent }),\n\n activePipeline: '',\n setActivePipeline: (activePipeline) => set({ activePipeline }),\n\n navigating: true,\n setNavigating: (navigating) => set({ navigating }),\n\n pageRendered: false,\n setPageRendered: (pageRendered) => set({ pageRendered }),\n}));\n","import { type ReactNode, useLayoutEffect, useRef, useState } from 'react';\nimport { useOutlet } from 'react-router';\nimport { getParentRoute } from '../utils/getParentRoute';\nimport { useWeaverContext } from './context';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * Delaying the routing process from `react-router`, handles gracefully between `Pipeline`s\n * while allowing any loading fallback component to listen and react with event changes.\n */\nexport default function DelayedOutlet(props: { delay: number }) {\n const activePipeline = useWeaverContext((state) => state.activePipeline);\n const setNavigating = useWeaverContext((state) => state.setNavigating);\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n\n const renderPageTask = useRef(setTimeout(() => {}));\n const routerOutlet = useOutlet();\n\n const [renderer, setRenderer] = useState<ReactNode>(null);\n\n // Handle and update new parent page.\n useLayoutEffect(() => {\n /**\n * `pageRendered` should be outside of `renderPageTask`,\n * we alrady have `Pipeline` to correct errors.\n */\n if ('scrollRestoration' in history) {\n history.scrollRestoration = 'manual';\n }\n\n if (getParentRoute(window.location.pathname) === activePipeline) {\n // Avoid while changing page, cancelling before new page pushed in\n // cause `activePipeline` to not change, making `navigating` softlock.\n setNavigating(false);\n return;\n }\n\n setNavigating(true);\n setPageRendered(false);\n\n clearTimeout(renderPageTask.current);\n renderPageTask.current = setTimeout(() => {\n setRenderer(routerOutlet!);\n setNavigating(false);\n }, props.delay);\n\n return () => {\n /**\n * Clear invalid task that were scheduled last effect.\n */\n clearInterval(renderPageTask.current);\n };\n }, [\n activePipeline,\n props.delay,\n routerOutlet,\n setNavigating,\n setPageRendered,\n ]);\n\n return renderer;\n}\n","export function getParentRoute(pathname: string) {\n let slashes = 0;\n let sliceAt = 0;\n for (sliceAt; sliceAt < pathname.length; sliceAt++) {\n if (pathname[sliceAt] == '/') {\n slashes++;\n }\n if (slashes === 2) {\n break;\n }\n }\n\n return pathname.slice(0, sliceAt);\n}\n","import React, { useLayoutEffect, type ReactNode } from 'react';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { weaverSetup } from '../setup';\nimport { useWeaverContext } from './context';\n\ninterface PipelineProps {\n children?: ReactNode;\n\n /**\n * A state switch to notifies `Pipeline` that the content and elements of the page is ready to be displayed.\n *\n * Usually, you will need to preload some other external sources, or initialize 3D scene, this state\n * make sure that the loading fallback doesn't mess up and show initializing stuff.\n *\n * Using `BakeScene`, you can ensure that the scene is loaded via its callback, you can then pass the state value that\n * `BakeScene` changes to this variable to hide all the lags behind loading screen.\n */\n contentReady?: boolean;\n\n /**\n * Title for the page.\n */\n title: string;\n\n /**\n * This is crucial for different `Pipeline`s to differentiate each other and avoiding conflict.\n *\n * Example for parent path: `/`, `/about`, `/projects`,...\n */\n parentPath: string;\n\n /**\n * If provided, `Pipeline` will log the current phase to console.\n */\n debugName?: string;\n\n /**\n * This is the only feature where weaver will use lenis to directly manipulate scrolling behaviour.\n *\n * By default, this value is `true` when a lenis instance is provided.\n *\n * `Pipeline` will stop and start lenis automatically when this variable is `true`.\n *\n * Disable this feature to gains complete control over lenis when mounting/unmounting to handle however you want.\n */\n lenisUsage?: boolean;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `Pipeline`: Notifies & reflect changes to/from `LoadingFallback`\n * and `DelayedOutlet` about its page loading status.\n *\n * All parent routes must have `Pipeline` in order to work and sync with `DelayedOutlet`.\n */\nexport default function Pipeline(props: PipelineProps) {\n const { children, ...restOfProps } = props;\n\n return (\n <>\n <RouteHandler {...restOfProps} />\n {children}\n </>\n );\n}\n\nfunction RouteHandler(props: Omit<PipelineProps, 'children'>) {\n const navigating = useWeaverContext((state) => state.navigating);\n const pageRendered = useWeaverContext((state) => state.pageRendered);\n const activeParent = useWeaverContext((state) => state.activeParent);\n\n const setActivePipeline = useWeaverContext(\n (state) => state.setActivePipeline\n );\n const setPageRendered = useWeaverContext((state) => state.setPageRendered);\n const setActiveParent = useWeaverContext((state) => state.setActiveParent);\n\n useLayoutEffect(() => {\n document.title = props.title;\n setActivePipeline(props.parentPath);\n\n if (props.contentReady !== undefined && !props.contentReady) {\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Loading scene`);\n return;\n }\n\n if (!navigating && (!pageRendered || activeParent !== props.parentPath)) {\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.start();\n }\n\n setPageRendered(true);\n setActiveParent(props.parentPath);\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Mounted`);\n }\n }, [\n activeParent,\n setActivePipeline,\n navigating,\n pageRendered,\n props.lenisUsage,\n props.contentReady,\n props.debugName,\n props.parentPath,\n props.title,\n setActiveParent,\n setPageRendered,\n ]);\n\n useLayoutEffectOnce(() => {\n return () => {\n /**\n * When unmounted, stop lenis to pass control to another Pipline instance.\n *\n * The unmount process only happens when the `<LoadingFallback />` kicks in,\n * so any visual glitches can happen in here, we can now set the scroll position to 0.\n */\n if (props.lenisUsage === undefined || props.lenisUsage) {\n weaverSetup._lenisInstance?.stop();\n weaverSetup._lenisInstance?.scrollTo(0, {\n immediate: true,\n force: true,\n });\n }\n\n if (props.debugName)\n console.log(`[${props.debugName}] Renderer status: Unmounted`);\n };\n });\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 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 { useWeaverContext, WeaverContextGetter } from './context';\nimport DelayedOutlet from './DelayedOutlet';\nimport Pipeline from './Pipeline';\n\n/// A read-only state for reacting with changes reflected by weaver.\nexport const useWeaverState = (givenState: keyof WeaverContextGetter) => {\n const state = useWeaverContext((state) => state[givenState]);\n return state;\n};\n\nexport { DelayedOutlet, Pipeline };\n"],"mappings":"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","names":["useLayoutEffect","useLayoutEffect","useLayoutEffect","useLayoutEffect","state"]}
package/dist/scene.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import React, { ReactNode, Dispatch, SetStateAction, RefObject } from 'react';
2
- import { B as BasicTunnelIn } from './index-JFUcplI8.mjs';
1
+ import React, { ReactNode, RefObject } from 'react';
2
+ import { B as BasicTunnelIn } from './index-Bknw3PY_.mjs';
3
3
  import 'lenis';
4
4
 
5
5
  /**
@@ -20,11 +20,9 @@ import 'lenis';
20
20
  */
21
21
  declare function BakeScene(props: {
22
22
  children?: ReactNode;
23
- sceneReady: boolean;
24
23
  tunnelIn?: BasicTunnelIn;
25
24
  loadTaskDelay?: number;
26
- setSceneReady: Dispatch<SetStateAction<boolean>>;
27
- onReady?: () => void;
25
+ onSceneReady: () => void;
28
26
  }): React.JSX.Element;
29
27
 
30
28
  type Basic3DTransforms = {
package/dist/scene.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import React, { ReactNode, Dispatch, SetStateAction, RefObject } from 'react';
2
- import { B as BasicTunnelIn } from './index-JFUcplI8.js';
1
+ import React, { ReactNode, RefObject } from 'react';
2
+ import { B as BasicTunnelIn } from './index-Bknw3PY_.js';
3
3
  import 'lenis';
4
4
 
5
5
  /**
@@ -20,11 +20,9 @@ import 'lenis';
20
20
  */
21
21
  declare function BakeScene(props: {
22
22
  children?: ReactNode;
23
- sceneReady: boolean;
24
23
  tunnelIn?: BasicTunnelIn;
25
24
  loadTaskDelay?: number;
26
- setSceneReady: Dispatch<SetStateAction<boolean>>;
27
- onReady?: () => void;
25
+ onSceneReady: () => void;
28
26
  }): React.JSX.Element;
29
27
 
30
28
  type Basic3DTransforms = {
package/dist/scene.js CHANGED
@@ -82,25 +82,23 @@ function BakeScene(props) {
82
82
  NotificationHandler,
83
83
  {
84
84
  key: unique,
85
- callback: props.onReady,
86
- sceneReady: props.sceneReady,
87
- setSceneReady: props.setSceneReady
85
+ onSceneReady: props.onSceneReady,
86
+ loadTaskDelay: props.loadTaskDelay
88
87
  }
89
88
  ));
90
89
  }
91
90
  function NotificationHandler(props) {
92
- return !props.sceneReady && /* @__PURE__ */ import_react.default.createElement(RenderNotification, { ...props });
91
+ const [sceneReady, setSceneReady] = (0, import_react.useState)(false);
92
+ return !sceneReady && /* @__PURE__ */ import_react.default.createElement(RenderNotification, { ...props, setSceneReady });
93
93
  }
94
94
  function RenderNotification(props) {
95
95
  const scheduledForCallback = (0, import_react.useRef)(false);
96
96
  (0, import_fiber.useFrame)(() => {
97
- if (!props.sceneReady && !scheduledForCallback.current) {
97
+ if (!scheduledForCallback.current) {
98
98
  scheduledForCallback.current = true;
99
99
  setTimeout(() => {
100
- if (props.callback) {
101
- props.callback(true);
102
- }
103
100
  props.setSceneReady(true);
101
+ props.onSceneReady();
104
102
  }, props.loadTaskDelay ?? 50);
105
103
  }
106
104
  });
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 type Dispatch,\n Fragment,\n type ReactNode,\n type SetStateAction,\n useId,\n useRef,\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 sceneReady: boolean;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n onReady?: () => 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 callback={props.onReady}\n sceneReady={props.sceneReady}\n setSceneReady={props.setSceneReady}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n callback?: (isSceneReady: boolean) => void;\n sceneReady: boolean;\n setSceneReady: (value: boolean) => void;\n}) {\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 !props.sceneReady && <RenderNotification {...props} />;\n}\n\nfunction RenderNotification(props: {\n callback?: (isSceneReady: boolean) => void;\n sceneReady: boolean;\n loadTaskDelay?: number;\n setSceneReady: (value: boolean) => void;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!props.sceneReady && !scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n if (props.callback) {\n props.callback(true);\n }\n props.setSceneReady(true);\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\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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/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"]}
package/dist/scene.mjs CHANGED
@@ -3,7 +3,8 @@ import { useFrame } from "@react-three/fiber";
3
3
  import React, {
4
4
  Fragment,
5
5
  useId,
6
- useRef
6
+ useRef,
7
+ useState
7
8
  } from "react";
8
9
 
9
10
  // src/setup.ts
@@ -49,25 +50,23 @@ function BakeScene(props) {
49
50
  NotificationHandler,
50
51
  {
51
52
  key: unique,
52
- callback: props.onReady,
53
- sceneReady: props.sceneReady,
54
- setSceneReady: props.setSceneReady
53
+ onSceneReady: props.onSceneReady,
54
+ loadTaskDelay: props.loadTaskDelay
55
55
  }
56
56
  ));
57
57
  }
58
58
  function NotificationHandler(props) {
59
- return !props.sceneReady && /* @__PURE__ */ React.createElement(RenderNotification, { ...props });
59
+ const [sceneReady, setSceneReady] = useState(false);
60
+ return !sceneReady && /* @__PURE__ */ React.createElement(RenderNotification, { ...props, setSceneReady });
60
61
  }
61
62
  function RenderNotification(props) {
62
63
  const scheduledForCallback = useRef(false);
63
64
  useFrame(() => {
64
- if (!props.sceneReady && !scheduledForCallback.current) {
65
+ if (!scheduledForCallback.current) {
65
66
  scheduledForCallback.current = true;
66
67
  setTimeout(() => {
67
- if (props.callback) {
68
- props.callback(true);
69
- }
70
68
  props.setSceneReady(true);
69
+ props.onSceneReady();
71
70
  }, props.loadTaskDelay ?? 50);
72
71
  }
73
72
  });
@@ -83,7 +82,7 @@ import React2, {
83
82
  useId as useId2,
84
83
  useLayoutEffect as useLayoutEffect4,
85
84
  useRef as useRef2,
86
- useState
85
+ useState as useState2
87
86
  } from "react";
88
87
 
89
88
  // src/hooks/effectOnce.ts
@@ -278,7 +277,7 @@ function RelaxedUpdate(props) {
278
277
  }
279
278
  function BalancedUpdate(props) {
280
279
  const { updatePosition } = props;
281
- const [shouldUpdate, setShouldUpdate] = useState(false);
280
+ const [shouldUpdate, setShouldUpdate] = useState2(false);
282
281
  useOrbit({
283
282
  target: props.attach,
284
283
  events: {
@@ -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 type Dispatch,\n Fragment,\n type ReactNode,\n type SetStateAction,\n useId,\n useRef,\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 sceneReady: boolean;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n onReady?: () => 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 callback={props.onReady}\n sceneReady={props.sceneReady}\n setSceneReady={props.setSceneReady}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n callback?: (isSceneReady: boolean) => void;\n sceneReady: boolean;\n setSceneReady: (value: boolean) => void;\n}) {\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 !props.sceneReady && <RenderNotification {...props} />;\n}\n\nfunction RenderNotification(props: {\n callback?: (isSceneReady: boolean) => void;\n sceneReady: boolean;\n loadTaskDelay?: number;\n setSceneReady: (value: boolean) => void;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!props.sceneReady && !scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n if (props.callback) {\n props.callback(true);\n }\n props.setSceneReady(true);\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\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return (globalThis as any).__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n (globalThis as any).__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 as any).__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n (globalThis as any).__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","useLayoutEffect","useLayoutEffect","useLayoutEffect","useId","React","useRef","useCallback","useLayoutEffect"]}
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"]}
package/docs/HOOKS.md ADDED
@@ -0,0 +1,14 @@
1
+ # Hooks
2
+ A collection of React hooks ranging from DOM to 3D to lenis scrolling.
3
+
4
+ - [`useBreakpoints`](https://github.com/neveranyart/weaver/blob/main/src/hooks/breakpoints.ts) ⚹ Screen breakpoints just like TailwindCSS in react.
5
+ - [`useDeviceMotion`](https://github.com/neveranyart/weaver/blob/main/src/hooks/deviceMotion.ts) ⚹ Device motion's API.
6
+ - [`useEffectOnce`, `useLayoutEffectOnce`](https://github.com/neveranyart/weaver/blob/main/src/hooks/effectOnce.ts) ⚹ Mount/unmount effect, ignore all states.
7
+ - [`useLenisCallback`](https://github.com/neveranyart/weaver/blob/main/src/hooks/lenisCallback.ts) ⚹ Lenis scroll callback with react to screen changes and only call on intersect mode.
8
+ - [`useMouseCallback`](https://github.com/neveranyart/weaver/blob/main/src/hooks/mouseCallback.ts) ⚹ DOM scroll callback with react to screen changes and only call on intersect mode.
9
+ - [`useNavigateAnchor`](https://github.com/neveranyart/weaver/blob/main/src/hooks/navigateAnchor.ts) ⚹ `<Link />` replacement for highly customizable components.
10
+ - [`useOrbit`](https://github.com/neveranyart/weaver/blob/main/src/hooks/orbit.ts) ⚹ Quick `ResizeObserver` and `IntersectionObserver`.
11
+ - [`useRawParams`](https://github.com/neveranyart/weaver/blob/main/src/hooks/rawParams.ts) ⚹ Determine pathname's depth.
12
+ - [`useScreen`](https://github.com/neveranyart/weaver/blob/main/src/hooks/screen.ts) ⚹ State update when screen size changes.
13
+ - [`useScreenCallback`](https://github.com/neveranyart/weaver/blob/main/src/hooks/screenCallback.ts) ⚹ Callback when screen size changes.
14
+ - [`useViewport`](https://github.com/neveranyart/weaver/blob/main/src/hooks/viewport.ts) ⚹ `@react-three/fiber` specific. State change when camera or screen size changes.
package/docs/ROUTING.md CHANGED
@@ -1,26 +1,60 @@
1
1
  # Routing
2
2
  The package wraps around `react-router` as a base for working with routing because it provides us a way to postpone parent components mounting.
3
3
 
4
- ## `DelayedOutlet`, `Pipeline`
5
- - `DelayedOutlet`:
6
- - Drop-in replacement for `Outlet`.
7
- - `Pipeline`:
8
- - Declares parent for a route, handle route changes and communicates with `DelayedOutlet`.
9
- - Recommended place to put the component is the parent for a route's components, see example below.
10
- - It can't nest itself.
4
+ This sub-package ensures `react-router` usage just like intended. It's has especially tested against going back and forth rapidly and works flawlessly in strict mode, even with throttle the CPU to a good x20 slowdown, so you don't have to suffer like we did developing the package!
5
+
6
+ While the route is delayed, the previous route is still displayed to ensure a smooth tranition to your likings.
11
7
 
12
8
  > [!CAUTION]
13
9
  > The only officially supported router is `BrowserRouter` data mode, we don't test with other methods.
14
- >
15
- > There can only be ONE `DelayedOutlet` per app and ONE `Pipeline` per parent path.
16
- >
17
- > `parentPath` is a URL route and it's must be unique for each `Pipeline`. It's critical for checking ownership of the route.
18
10
 
19
- Other than that, use `react-router` like intended. `weaver/routing` has especially tested against going back and forth rapidly and works flawlessly in strict mode, even with throttle the CPU to a good x20 slowdown, so you don't have to suffer like we did developing the package!
11
+ ## `DelayedOutlet`
12
+ A drop-in replacement component for `Outlet` with a `delay` variable, you can adjust this based on your needs.
20
13
 
21
- While the route is delayed, the previous route is still displayed to ensure a smooth tranition to your likings.
14
+ > [!CAUTION]
15
+ > There can only be ONE `DelayedOutlet` per app.
16
+
17
+ Recommended example usage, delayed for `500ms`:
18
+ ```tsx
19
+ import { DelayedOutlet } from '@neveranyart/weaver/routing';
20
+ import { StrictMode } from 'react';
21
+ import { createRoot } from 'react-dom/client';
22
+ import { createBrowserRouter, RouterProvider } from 'react-router';
23
+
24
+ function Root() {
25
+ return (
26
+ <main>
27
+ {/* Place any other components here, like a navbar and a loading screen for example. */}
28
+ <DelayedOutlet delay={500} />
29
+ </main>
30
+ );
31
+ }
32
+
33
+ const router = createBrowserRouter([
34
+ {
35
+ path: '/',
36
+ element: <Root />,
37
+ },
38
+ ]);
39
+
40
+ createRoot(document.getElementById('root')!).render(
41
+ <StrictMode>
42
+ <RouterProvider router={router} />
43
+ </StrictMode>
44
+ );
45
+ ```
46
+
47
+ ## `Pipeline`
48
+ Declares parent for a route, handle route changes and communicates with `DelayedOutlet`.
49
+
50
+ > [!CAUTION]
51
+ > Only be ONE `Pipeline` per parent path.
52
+ >
53
+ > It can't nest itself.
54
+
55
+ Recommended place to put the component is the parent for a route's components
22
56
 
23
- Example usage with 2 endpoints, router delayed for `500ms`:
57
+ Example usage with 2 endpoints, taken from previous example from `DelayedOutlet`:
24
58
 
25
59
  ```tsx
26
60
  import { DelayedOutlet, Pipeline } from '@neveranyart/weaver/routing';
@@ -31,6 +65,7 @@ import { createBrowserRouter, Link, RouterProvider } from 'react-router';
31
65
  function Root() {
32
66
  return (
33
67
  <main>
68
+ {/* Place any other components here, like a navbar and a loading screen for example. */}
34
69
  <DelayedOutlet delay={500} />
35
70
  </main>
36
71
  );
@@ -80,7 +115,8 @@ createRoot(document.getElementById('root')!).render(
80
115
  );
81
116
  ```
82
117
 
83
- Routing also provides a quick way to react to state changes with a cool hook called `useWeaverState`. Containing 4 states for you to play with:
118
+ ## State hooks
119
+ Routing also provides a quick way to react to state changes with a cool hook called `useWeaverState`. Containing 4 read-only states for you to play with:
84
120
 
85
121
  ```ts
86
122
  // Covers the whole routing process. It is the recommended way of knowing if the content is ready to be displayed or not
package/docs/SCENE.md ADDED
@@ -0,0 +1,146 @@
1
+ # Scene
2
+ A `@react-three/fiber` focused sub-package for syncing, incorporating with DOM elements.
3
+
4
+ ## `BakeScene`
5
+ Put 3D scene to render and notifies back when the scene is ready to be used.
6
+
7
+ > [!CAUTION]
8
+ > For `BakeScene` to work, you must provide a tunnel, you can create a tunnel via `tunnel-rat`.
9
+ > Then pass the `In` component to weaver from `weaverSetup`:
10
+ > ```tsx
11
+ > import { Preload } from '@react-three/drei';
12
+ > import { Canvas } from '@react-three/fiber';
13
+ > import { weaverSetup } from '@neveranyart/weaver';
14
+ > import tunnel from 'tunnel-rat';
15
+ > /**
16
+ > * Set a default tunnel for `weaver/scene`
17
+ > */
18
+ > const WeaverTunnel = tunnel();
19
+ > weaverSetup.set3DTunnel(WeaverTunnel.In);
20
+ >
21
+ > function ThreeCanvas() {
22
+ > return (
23
+ > <Canvas>
24
+ > <WeaverTunnel.Out />
25
+ > <Preload all />
26
+ > </Canvas>
27
+ > );
28
+ > }
29
+ > ```
30
+
31
+ The component accepts your normal 3D scene, it will then sits and wait for the renderer to finish the job.
32
+
33
+ Plus, your scene doesn't have to be inside of `BakeScene`, this will comes in handy later as we talk about `SceneSync`.
34
+
35
+ For example, a `BakeScene` with a box and a default camera:
36
+ ```tsx
37
+ <BakeScene onSceneReady={() => {}}>
38
+ <Box />
39
+ <PerspectiveCamera position={[0, 0, 5]} makeDefault />
40
+ </BakeScene>
41
+ ```
42
+
43
+ From here, we can combine with `Pipeline` to wait for scene loading too:
44
+ ```tsx
45
+ function Contact() {
46
+ const [sceneReady, setSceneReady] = useState(false);
47
+
48
+ return (
49
+ <Pipeline
50
+ title={'Spideration - Contact'}
51
+ debugName={'Contact'}
52
+ parentPath={'/contact'}
53
+ contentReady={sceneReady}
54
+ >
55
+ <BakeScene onSceneReady={() => setSceneReady(true)}>
56
+ <Box />
57
+ <PerspectiveCamera position={[0, 0, 5]} makeDefault />
58
+ </BakeScene>
59
+ </Pipeline>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ## `SceneSync`
65
+ Fit your 3D scene inside a DOM element!!!
66
+
67
+ > [!CAUTION]
68
+ > For `SceneSync` to work, you must provide a lenis instance:
69
+ > ```tsx
70
+ > import { Preload } from '@react-three/drei';
71
+ > import { Canvas } from '@react-three/fiber';
72
+ > import { weaverSetup } from '@neveranyart/weaver';
73
+ > import Lenis from 'lenis';
74
+ > import { frame } from 'motion/react';
75
+ > import tunnel from 'tunnel-rat';
76
+ >
77
+ > export const lenisInstance = new Lenis({
78
+ > syncTouch: false,
79
+ > });
80
+ >
81
+ > export function activateLenis() {
82
+ > function update(data: { timestamp: number }) {
83
+ > lenisInstance.raf(data.timestamp);
84
+ > }
85
+ >
86
+ > frame.update(update, true);
87
+ > }
88
+ >
89
+ > /**
90
+ > * Provide lenis instance.
91
+ > */
92
+ > activateLenis();
93
+ > weaverSetup.setLenisInstance(lenisInstance);
94
+ >
95
+ > /**
96
+ > * Set a default tunnel for `weaver/scene`
97
+ > */
98
+ > const WeaverTunnel = tunnel();
99
+ > weaverSetup.set3DTunnel(WeaverTunnel.In);
100
+ >
101
+ > function ThreeCanvas() {
102
+ > return (
103
+ > <Canvas>
104
+ > <WeaverTunnel.Out />
105
+ > <Preload all />
106
+ > </Canvas>
107
+ > );
108
+ > }
109
+ > ```
110
+
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
+ For example, a box inside a DOM element:
130
+ ```tsx
131
+ function DOMBox() {
132
+ const container = useRef<HTMLDivElement>(null);
133
+
134
+ return (
135
+ <div ref={container}>
136
+ <SceneSync attach={container} trackingMode="relaxed">
137
+ <Box />
138
+ </SceneSync>
139
+ </div>
140
+ );
141
+ }
142
+ ```
143
+
144
+ There are many varible and options for you to play with, including a layout update callback, where it will report back what changes it have made to the scene, you can overlay many scenes together with the hud option, we have documented the details in the component itself, be sure to check it out.
145
+
146
+ `SceneSync` is compatible with `BakeScene`, `BakeScene` works based on the responsiveness of the site, so no worries :D
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "repository": {
6
6
  "url": "git+https://github.com/neveranyart/weaver.git"
7
7
  },
8
- "version": "1.0.19",
8
+ "version": "1.0.20",
9
9
  "description": "An in-house package by neverany for making interactive React CSR.",
10
10
  "packageManager": "yarn@4.10.3",
11
11
  "publishConfig": {