@neveranyart/weaver 1.0.26 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/scene.d.mts CHANGED
@@ -7,8 +7,8 @@ import 'lenis';
7
7
  *
8
8
  * `BakeScene`: This component will notifiy when the global 3D scene is ready.
9
9
  *
10
- * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire
11
- * with ease, the component takes advantage of that, and because `useLoader` is unreliable.
10
+ * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire normally again
11
+ * This component takes advantage of that, combined with the `useLoader` for external resourses loading to hold back.
12
12
  *
13
13
  * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.
14
14
  * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also
package/dist/scene.d.ts CHANGED
@@ -7,8 +7,8 @@ import 'lenis';
7
7
  *
8
8
  * `BakeScene`: This component will notifiy when the global 3D scene is ready.
9
9
  *
10
- * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire
11
- * with ease, the component takes advantage of that, and because `useLoader` is unreliable.
10
+ * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire normally again
11
+ * This component takes advantage of that, combined with the `useLoader` for external resourses loading to hold back.
12
12
  *
13
13
  * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.
14
14
  * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also
package/dist/scene.js CHANGED
@@ -36,6 +36,7 @@ __export(scene_exports, {
36
36
  module.exports = __toCommonJS(scene_exports);
37
37
 
38
38
  // src/scene/BakeScene.tsx
39
+ var import_drei = require("@react-three/drei");
39
40
  var import_fiber = require("@react-three/fiber");
40
41
  var import_react = __toESM(require("react"));
41
42
 
@@ -92,9 +93,10 @@ function NotificationHandler(props) {
92
93
  return !sceneReady && /* @__PURE__ */ import_react.default.createElement(RenderNotifier, { ...props, setSceneReady });
93
94
  }
94
95
  function RenderNotifier(props) {
96
+ const { progress } = (0, import_drei.useProgress)();
95
97
  const scheduledForCallback = (0, import_react.useRef)(false);
96
98
  (0, import_fiber.useFrame)(() => {
97
- if (!scheduledForCallback.current) {
99
+ if (!scheduledForCallback.current && progress === 100) {
98
100
  scheduledForCallback.current = true;
99
101
  setTimeout(() => {
100
102
  props.setSceneReady(true);
@@ -106,7 +108,7 @@ function RenderNotifier(props) {
106
108
  }
107
109
 
108
110
  // src/scene/SceneSync.tsx
109
- var import_drei = require("@react-three/drei");
111
+ var import_drei2 = require("@react-three/drei");
110
112
  var import_fiber3 = require("@react-three/fiber");
111
113
  var import_react10 = require("motion/react");
112
114
  var import_react11 = __toESM(require("react"));
@@ -243,7 +245,7 @@ function SceneSync(props) {
243
245
  );
244
246
  }
245
247
  if (props.hud) {
246
- return /* @__PURE__ */ import_react11.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react11.default.createElement(import_drei.Hud, { key: unique, renderPriority: props.renderPriority }, props.rootCamera && /* @__PURE__ */ import_react11.default.createElement(HudProjectionHandler, null), /* @__PURE__ */ import_react11.default.createElement(SyncInternal, { ...props })));
248
+ return /* @__PURE__ */ import_react11.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react11.default.createElement(import_drei2.Hud, { key: unique, renderPriority: props.renderPriority }, props.rootCamera && /* @__PURE__ */ import_react11.default.createElement(HudProjectionHandler, null), /* @__PURE__ */ import_react11.default.createElement(SyncInternal, { ...props })));
247
249
  }
248
250
  return /* @__PURE__ */ import_react11.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react11.default.createElement(SyncInternal, { key: unique, ...props }));
249
251
  }
@@ -260,16 +262,18 @@ function SyncInternal(props) {
260
262
  } = props;
261
263
  const updatePosition = (0, import_react11.useCallback)(() => {
262
264
  const activeControl = control ?? defaultControl;
265
+ if (!activeControl.current || !attach.current) return;
263
266
  const domRect = attach.current.getBoundingClientRect();
264
267
  const screenH = window.innerHeight;
265
268
  const screenW = window.innerWidth;
269
+ const scroll = weaverSetup._lenisInstance?.actualScroll ?? window.scrollY;
266
270
  const vpWidthRatio = viewport.width / screenW;
267
271
  const vpHeightRatio = viewport.height / screenH;
268
- const scrollOffset = weaverSetup._lenisInstance.actualScroll / screenH * viewport.height;
272
+ const scrollOffset = scroll / screenH * viewport.height;
269
273
  const w = domRect.width * vpWidthRatio;
270
274
  const h = domRect.height * vpHeightRatio;
271
275
  const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;
272
- const y = viewport.height * 0.5 - (domRect.y + weaverSetup._lenisInstance.actualScroll) * vpHeightRatio - h * 0.5 + scrollOffset;
276
+ const y = viewport.height * 0.5 - (domRect.y + scroll) * vpHeightRatio - h * 0.5 + scrollOffset;
273
277
  if (onLayoutUpdate) {
274
278
  onLayoutUpdate(domRect, { w, h }, { x, y });
275
279
  }
@@ -298,19 +302,13 @@ function SyncInternal(props) {
298
302
  viewport.height,
299
303
  viewport.width
300
304
  ]);
301
- const graceUpdate = (0, import_react11.useCallback)(() => {
302
- try {
303
- updatePosition();
304
- } catch {
305
- }
306
- }, [updatePosition]);
307
305
  (0, import_react11.useLayoutEffect)(() => {
308
- graceUpdate();
309
- }, [graceUpdate]);
306
+ updatePosition();
307
+ }, [updatePosition]);
310
308
  const mode = {
311
- relaxed: /* @__PURE__ */ import_react11.default.createElement(RelaxedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
312
- balanced: /* @__PURE__ */ import_react11.default.createElement(BalancedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
313
- aggressive: /* @__PURE__ */ import_react11.default.createElement(AggressiveUpdate, { updatePosition: graceUpdate })
309
+ relaxed: /* @__PURE__ */ import_react11.default.createElement(RelaxedUpdate, { attach: props.attach, updatePosition }),
310
+ balanced: /* @__PURE__ */ import_react11.default.createElement(BalancedUpdate, { attach: props.attach, updatePosition }),
311
+ aggressive: /* @__PURE__ */ import_react11.default.createElement(AggressiveUpdate, { updatePosition })
314
312
  };
315
313
  if (props.control) {
316
314
  return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, props.children, mode[props.trackingMode]);
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/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import BakeScene from './BakeScene';\nimport SceneSync from './SceneSync';\n\nexport { BakeScene, SceneSync };\n","import { useFrame } from '@react-three/fiber';\nimport React, {\n Dispatch,\n Fragment,\n type ReactNode,\n SetStateAction,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `BakeScene`: This component will notifiy when the global 3D scene is ready.\n *\n * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire\n * with ease, the component takes advantage of that, and because `useLoader` is unreliable.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notifier as soon as the job is done.\n */\n return (\n !sceneReady && (\n <RenderNotifier {...props} setSceneReady={setSceneReady} />\n )\n );\n}\n\nfunction RenderNotifier(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n props.setSceneReady(true);\n props.onSceneReady();\n }, props.loadTaskDelay ?? 50);\n }\n });\n\n return null;\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { Hud } from '@react-three/drei';\nimport { useThree } 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 { useViewport } from '../hooks';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n *\n * Example:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the part of the scene that did not fully moved out of view will stay there.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n renderPriority: number;\n\n /**\n * Use the parent's camera instead of providing your own in the scene.\n * ```tsx\n * return (\n * <SceneSync hud renderPriority={1} rootCamera={true}>\n * <Box />\n * </SceneSync>\n * );\n * ```\n *\n * If you want to provide your own camera:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n rootCamera: boolean;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (props.trackingMode === 'relaxed' && !weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.renderPriority}>\n {props.rootCamera && <HudProjectionHandler />}\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const viewport = useViewport();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n const domRect = attach.current!.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n\n const vpWidthRatio = viewport.width / screenW;\n const vpHeightRatio = viewport.height / screenH;\n\n const scrollOffset =\n (weaverSetup._lenisInstance!.actualScroll / screenH) * viewport.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;\n const y =\n viewport.height * 0.5 -\n (domRect.y + weaverSetup._lenisInstance!.actualScroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current!.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current!.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [\n attach,\n control,\n disableScaling,\n onLayoutUpdate,\n scaleFactor,\n stretch,\n viewport.height,\n viewport.width,\n ]);\n\n const graceUpdate = useCallback(() => {\n try {\n updatePosition();\n } catch {\n /* empty */\n }\n }, [updatePosition]);\n\n /**\n * Update position when function changes.\n */\n useLayoutEffect(() => {\n graceUpdate();\n }, [graceUpdate]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n aggressive: <AggressiveUpdate updatePosition={graceUpdate} />,\n };\n\n if (props.control) {\n return (\n <>\n {props.children}\n {mode[props.trackingMode]}\n </>\n );\n }\n\n return (\n <group ref={defaultControl}>\n {props.children}\n {mode[props.trackingMode]}\n </group>\n );\n}\n\nfunction RelaxedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n /**\n * Scroll hook to update object correctly to the current HTML scroll position.\n */\n useLenisCallback(updatePosition, {\n initialCall: true,\n intersectOn: props.attach,\n });\n\n /**\n * Allows the element to resize too.\n */\n useOrbit({\n target: props.attach,\n events: {\n onIntersect: updatePosition,\n onResize: updatePosition,\n },\n });\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction BalancedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n const [shouldUpdate, setShouldUpdate] = useState(false);\n\n useOrbit({\n target: props.attach,\n events: {\n onIntersect(entry) {\n setShouldUpdate(entry.isIntersecting);\n },\n onResize: updatePosition,\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (shouldUpdate) {\n frame.read(updatePosition, true);\n } else {\n cancelFrame(updatePosition);\n }\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition, shouldUpdate]);\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction AggressiveUpdate(props: { updatePosition: () => void }) {\n const { updatePosition } = props;\n\n useLayoutEffect(() => {\n frame.read(updatePosition, true);\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition]);\n\n return null;\n}\n\nfunction HudProjectionHandler() {\n const getHudState = useThree((state) => state.get);\n const { previousRoot } = useThree();\n const { camera: rootCamera } = previousRoot!();\n\n useLayoutEffect(() => {\n getHudState().set({ camera: rootCamera });\n }, [getHudState, rootCamera]);\n\n return null;\n}\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\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 return {\n width: viewport.getCurrentViewport(customCamera ?? camera).width,\n height: viewport.getCurrentViewport(customCamera ?? camera).height,\n };\n}\n"],"mappings":"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","names":["React","import_fiber","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react_router","import_react","import_fiber","React"]}
1
+ {"version":3,"sources":["../src/scene/index.ts","../src/scene/BakeScene.tsx","../src/setup.ts","../src/scene/SceneSync.tsx","../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import BakeScene from './BakeScene';\nimport SceneSync from './SceneSync';\n\nexport { BakeScene, SceneSync };\n","import { useProgress } from '@react-three/drei';\nimport { 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 normally again\n * This component takes advantage of that, combined with the `useLoader` for external resourses loading to hold back.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notifier as soon as the job is done.\n */\n return (\n !sceneReady && <RenderNotifier {...props} setSceneReady={setSceneReady} />\n );\n}\n\nfunction RenderNotifier(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const { progress } = useProgress();\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current && progress === 100) {\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 } 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 { useViewport } from '../hooks';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n *\n * Example:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the part of the scene that did not fully moved out of view will stay there.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n renderPriority: number;\n\n /**\n * Use the parent's camera instead of providing your own in the scene.\n * ```tsx\n * return (\n * <SceneSync hud renderPriority={1} rootCamera={true}>\n * <Box />\n * </SceneSync>\n * );\n * ```\n *\n * If you want to provide your own camera:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n rootCamera: boolean;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (props.trackingMode === 'relaxed' && !weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.renderPriority}>\n {props.rootCamera && <HudProjectionHandler />}\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const viewport = useViewport();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n if (!activeControl.current || !attach.current) return;\n\n const domRect = attach.current.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n const scroll = weaverSetup._lenisInstance?.actualScroll ?? window.scrollY;\n\n const vpWidthRatio = viewport.width / screenW;\n const vpHeightRatio = viewport.height / screenH;\n\n const scrollOffset = (scroll / screenH) * viewport.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;\n const y =\n viewport.height * 0.5 -\n (domRect.y + scroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [\n attach,\n control,\n disableScaling,\n onLayoutUpdate,\n scaleFactor,\n stretch,\n viewport.height,\n viewport.width,\n ]);\n\n /**\n * Update position when function changes.\n */\n useLayoutEffect(() => {\n updatePosition();\n }, [updatePosition]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={updatePosition} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={updatePosition} />\n ),\n aggressive: <AggressiveUpdate updatePosition={updatePosition} />,\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\nfunction HudProjectionHandler() {\n const getHudState = useThree((state) => state.get);\n const { previousRoot } = useThree();\n const { camera: rootCamera } = previousRoot!();\n\n useLayoutEffect(() => {\n getHudState().set({ camera: rootCamera });\n }, [getHudState, rootCamera]);\n\n return null;\n}\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\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 return {\n width: viewport.getCurrentViewport(customCamera ?? camera).width,\n height: viewport.getCurrentViewport(customCamera ?? camera).height,\n };\n}\n"],"mappings":"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","names":["React","import_drei","import_fiber","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react_router","import_react","import_fiber","React"]}
package/dist/scene.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/scene/BakeScene.tsx
2
+ import { useProgress } from "@react-three/drei";
2
3
  import { useFrame } from "@react-three/fiber";
3
4
  import React, {
4
5
  Fragment,
@@ -60,9 +61,10 @@ function NotificationHandler(props) {
60
61
  return !sceneReady && /* @__PURE__ */ React.createElement(RenderNotifier, { ...props, setSceneReady });
61
62
  }
62
63
  function RenderNotifier(props) {
64
+ const { progress } = useProgress();
63
65
  const scheduledForCallback = useRef(false);
64
66
  useFrame(() => {
65
- if (!scheduledForCallback.current) {
67
+ if (!scheduledForCallback.current && progress === 100) {
66
68
  scheduledForCallback.current = true;
67
69
  setTimeout(() => {
68
70
  props.setSceneReady(true);
@@ -234,16 +236,18 @@ function SyncInternal(props) {
234
236
  } = props;
235
237
  const updatePosition = useCallback6(() => {
236
238
  const activeControl = control ?? defaultControl;
239
+ if (!activeControl.current || !attach.current) return;
237
240
  const domRect = attach.current.getBoundingClientRect();
238
241
  const screenH = window.innerHeight;
239
242
  const screenW = window.innerWidth;
243
+ const scroll = weaverSetup._lenisInstance?.actualScroll ?? window.scrollY;
240
244
  const vpWidthRatio = viewport.width / screenW;
241
245
  const vpHeightRatio = viewport.height / screenH;
242
- const scrollOffset = weaverSetup._lenisInstance.actualScroll / screenH * viewport.height;
246
+ const scrollOffset = scroll / screenH * viewport.height;
243
247
  const w = domRect.width * vpWidthRatio;
244
248
  const h = domRect.height * vpHeightRatio;
245
249
  const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;
246
- const y = viewport.height * 0.5 - (domRect.y + weaverSetup._lenisInstance.actualScroll) * vpHeightRatio - h * 0.5 + scrollOffset;
250
+ const y = viewport.height * 0.5 - (domRect.y + scroll) * vpHeightRatio - h * 0.5 + scrollOffset;
247
251
  if (onLayoutUpdate) {
248
252
  onLayoutUpdate(domRect, { w, h }, { x, y });
249
253
  }
@@ -272,19 +276,13 @@ function SyncInternal(props) {
272
276
  viewport.height,
273
277
  viewport.width
274
278
  ]);
275
- const graceUpdate = useCallback6(() => {
276
- try {
277
- updatePosition();
278
- } catch {
279
- }
280
- }, [updatePosition]);
281
279
  useLayoutEffect7(() => {
282
- graceUpdate();
283
- }, [graceUpdate]);
280
+ updatePosition();
281
+ }, [updatePosition]);
284
282
  const mode = {
285
- relaxed: /* @__PURE__ */ React2.createElement(RelaxedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
286
- balanced: /* @__PURE__ */ React2.createElement(BalancedUpdate, { attach: props.attach, updatePosition: graceUpdate }),
287
- aggressive: /* @__PURE__ */ React2.createElement(AggressiveUpdate, { updatePosition: graceUpdate })
283
+ relaxed: /* @__PURE__ */ React2.createElement(RelaxedUpdate, { attach: props.attach, updatePosition }),
284
+ balanced: /* @__PURE__ */ React2.createElement(BalancedUpdate, { attach: props.attach, updatePosition }),
285
+ aggressive: /* @__PURE__ */ React2.createElement(AggressiveUpdate, { updatePosition })
288
286
  };
289
287
  if (props.control) {
290
288
  return /* @__PURE__ */ React2.createElement(React2.Fragment, null, props.children, mode[props.trackingMode]);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/scene/BakeScene.tsx","../src/setup.ts","../src/scene/SceneSync.tsx","../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import { useFrame } from '@react-three/fiber';\nimport React, {\n Dispatch,\n Fragment,\n type ReactNode,\n SetStateAction,\n useId,\n useRef,\n useState,\n} from 'react';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * `BakeScene`: This component will notifiy when the global 3D scene is ready.\n *\n * It works by tune in the `useFrame` from `@react-three/fiber`. When the scene is loaded, `useFreame` will fire\n * with ease, the component takes advantage of that, and because `useLoader` is unreliable.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notifier as soon as the job is done.\n */\n return (\n !sceneReady && (\n <RenderNotifier {...props} setSceneReady={setSceneReady} />\n )\n );\n}\n\nfunction RenderNotifier(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current) {\n scheduledForCallback.current = true;\n setTimeout(() => {\n props.setSceneReady(true);\n props.onSceneReady();\n }, props.loadTaskDelay ?? 50);\n }\n });\n\n return null;\n}\n","import Lenis from 'lenis';\nimport { ReactNode } from 'react';\n\nexport type BasicTunnelIn = ({ children }: { children: ReactNode }) => null;\n\ndeclare global {\n var __weaverLenis: Lenis | undefined;\n var __weaver3DTunnel: BasicTunnelIn | undefined;\n}\n\nclass WeaverSetup {\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _lenisInstance(): Lenis | undefined {\n return globalThis.__weaverLenis;\n }\n set _lenisInstance(val: Lenis | undefined) {\n globalThis.__weaverLenis = val;\n }\n\n /**\n * This variable is handled internally by weaver. **Do not use**.\n */\n get _Default3DTunnelIn(): BasicTunnelIn | undefined {\n return globalThis.__weaver3DTunnel;\n }\n set _Default3DTunnelIn(val: BasicTunnelIn | undefined) {\n globalThis.__weaver3DTunnel = val;\n }\n\n setLenisInstance(instance: Lenis) {\n this._lenisInstance = instance;\n }\n set3DTunnel(tunnelIn: BasicTunnelIn) {\n this._Default3DTunnelIn = tunnelIn;\n }\n}\n\nexport const weaverSetup = new WeaverSetup();\n","import { Hud } from '@react-three/drei';\nimport { useThree } 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 { useViewport } from '../hooks';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n *\n * Example:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the part of the scene that did not fully moved out of view will stay there.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n renderPriority: number;\n\n /**\n * Use the parent's camera instead of providing your own in the scene.\n * ```tsx\n * return (\n * <SceneSync hud renderPriority={1} rootCamera={true}>\n * <Box />\n * </SceneSync>\n * );\n * ```\n *\n * If you want to provide your own camera:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n rootCamera: boolean;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (props.trackingMode === 'relaxed' && !weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.renderPriority}>\n {props.rootCamera && <HudProjectionHandler />}\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const viewport = useViewport();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n const domRect = attach.current!.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n\n const vpWidthRatio = viewport.width / screenW;\n const vpHeightRatio = viewport.height / screenH;\n\n const scrollOffset =\n (weaverSetup._lenisInstance!.actualScroll / screenH) * viewport.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;\n const y =\n viewport.height * 0.5 -\n (domRect.y + weaverSetup._lenisInstance!.actualScroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current!.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current!.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [\n attach,\n control,\n disableScaling,\n onLayoutUpdate,\n scaleFactor,\n stretch,\n viewport.height,\n viewport.width,\n ]);\n\n const graceUpdate = useCallback(() => {\n try {\n updatePosition();\n } catch {\n /* empty */\n }\n }, [updatePosition]);\n\n /**\n * Update position when function changes.\n */\n useLayoutEffect(() => {\n graceUpdate();\n }, [graceUpdate]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={graceUpdate} />\n ),\n aggressive: <AggressiveUpdate updatePosition={graceUpdate} />,\n };\n\n if (props.control) {\n return (\n <>\n {props.children}\n {mode[props.trackingMode]}\n </>\n );\n }\n\n return (\n <group ref={defaultControl}>\n {props.children}\n {mode[props.trackingMode]}\n </group>\n );\n}\n\nfunction RelaxedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n /**\n * Scroll hook to update object correctly to the current HTML scroll position.\n */\n useLenisCallback(updatePosition, {\n initialCall: true,\n intersectOn: props.attach,\n });\n\n /**\n * Allows the element to resize too.\n */\n useOrbit({\n target: props.attach,\n events: {\n onIntersect: updatePosition,\n onResize: updatePosition,\n },\n });\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction BalancedUpdate(props: {\n attach: RefObject<HTMLElement | null>;\n updatePosition: () => void;\n}) {\n const { updatePosition } = props;\n\n const [shouldUpdate, setShouldUpdate] = useState(false);\n\n useOrbit({\n target: props.attach,\n events: {\n onIntersect(entry) {\n setShouldUpdate(entry.isIntersecting);\n },\n onResize: updatePosition,\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (shouldUpdate) {\n frame.read(updatePosition, true);\n } else {\n cancelFrame(updatePosition);\n }\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition, shouldUpdate]);\n\n useLayoutEffectOnce(updatePosition);\n return null;\n}\n\nfunction AggressiveUpdate(props: { updatePosition: () => void }) {\n const { updatePosition } = props;\n\n useLayoutEffect(() => {\n frame.read(updatePosition, true);\n\n return () => {\n cancelFrame(updatePosition);\n };\n }, [updatePosition]);\n\n return null;\n}\n\nfunction HudProjectionHandler() {\n const getHudState = useThree((state) => state.get);\n const { previousRoot } = useThree();\n const { camera: rootCamera } = previousRoot!();\n\n useLayoutEffect(() => {\n getHudState().set({ camera: rootCamera });\n }, [getHudState, rootCamera]);\n\n return null;\n}\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\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 return {\n width: viewport.getCurrentViewport(customCamera ?? camera).width,\n height: viewport.getCurrentViewport(customCamera ?? camera).height,\n };\n}\n"],"mappings":"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","names":["useThree","React","useCallback","useId","useLayoutEffect","useRef","useState","useState","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useCallback","useLayoutEffect","useState","useId","React","useRef","useCallback","useLayoutEffect","useState","useThree"]}
1
+ {"version":3,"sources":["../src/scene/BakeScene.tsx","../src/setup.ts","../src/scene/SceneSync.tsx","../src/hooks/breakpoints.ts","../src/hooks/screenCallback.ts","../src/hooks/effectOnce.ts","../src/hooks/lenisCallback.ts","../src/hooks/orbit.ts","../src/hooks/mouseCallback.ts","../src/hooks/navigateAnchor.ts","../src/hooks/rawParams.ts","../src/hooks/screen.ts","../src/hooks/viewport.ts"],"sourcesContent":["import { useProgress } from '@react-three/drei';\nimport { 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 normally again\n * This component takes advantage of that, combined with the `useLoader` for external resourses loading to hold back.\n *\n * This component also accepts 3D elements `children` to be rendered directly to the canvas with some camera options.\n * But you don't have to put every 3D components inside the baker, for example, `SceneSync`s in the page are also\n * being watched by this component.\n *\n * The route renderer **CAN'T** detect if the page has 3D elements or not, so if a page uses any sort of 3D rendering,\n * this component **MUST** be a children iniside `Pipeline` (`index.tsx`), then pass the state value that bake changes\n * to `Pipeline`'s `contentReady` in order for the `BakeScene` to work behind loading fallback screen.\n */\nexport default function BakeScene(props: {\n children?: ReactNode;\n tunnelIn?: BasicTunnelIn;\n loadTaskDelay?: number;\n onSceneReady: () => void;\n}) {\n const unique = useId();\n const pipeObjects = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n return (\n <TunnelIn>\n <Fragment key={pipeObjects}>{props.children}</Fragment>\n <NotificationHandler\n key={unique}\n onSceneReady={props.onSceneReady}\n loadTaskDelay={props.loadTaskDelay}\n />\n </TunnelIn>\n );\n}\n\nfunction NotificationHandler(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n}) {\n const [sceneReady, setSceneReady] = useState(false);\n /**\n * `useFrame` is expensive for something that only triggers once, so yea,\n * we'll remove the notifier as soon as the job is done.\n */\n return (\n !sceneReady && <RenderNotifier {...props} setSceneReady={setSceneReady} />\n );\n}\n\nfunction RenderNotifier(props: {\n onSceneReady: () => void;\n loadTaskDelay?: number;\n setSceneReady: Dispatch<SetStateAction<boolean>>;\n}) {\n const { progress } = useProgress();\n const scheduledForCallback = useRef(false);\n\n useFrame(() => {\n if (!scheduledForCallback.current && progress === 100) {\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 } 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 { useViewport } from '../hooks';\nimport { useLayoutEffectOnce } from '../hooks/effectOnce';\nimport { useLenisCallback } from '../hooks/lenisCallback';\nimport { useOrbit } from '../hooks/orbit';\nimport { BasicTunnelIn, weaverSetup } from '../setup';\n\nexport type Basic3DTransforms = {\n scale: {\n set: (x: number, y: number, z: number) => void;\n };\n position: {\n x: number;\n y: number;\n };\n};\n\ninterface SyncProps {\n /**\n * HTML element ref that `<SceneSync />` will use to sync with the scene.\n *\n * ```tsx\n * <div ref={container} />\n * <SceneSync attach={container}>\n * <group />\n * </SceneSync>\n * ```\n */\n attach: RefObject<HTMLElement | null>;\n\n /**\n * This variable allows fine-grain control over your scene when passed to `<SceneSync />`.\n *\n * `<SceneSync />` will use its own ref and group when creating your scene to control its scale and position.\n * Setting this variable will disable the internal ref, and you can decide on which object gets controlled.\n *\n * This variable is needed for `hud` if you wanted to add a custom camera.\n *\n * For listening to change details, use `onLayoutChange` instead.\n */\n control?: RefObject<Basic3DTransforms | null>;\n\n /**\n * When this variable is set, `<SceneSync />` will send updates when the scene update its positions.\n *\n * The function return the calculated DOM rect, with dimension and position in 3D measurements.\n */\n onLayoutUpdate?: (\n rect: DOMRect,\n dimension: { w: number; h: number },\n position: { x: number; y: number }\n ) => void;\n\n /**\n * Use `Hud` for this scene or not.\n *\n * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.\n *\n * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera\n * to avoid unwanted behavior.\n *\n * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,\n * the camera is also change, ruining the effect.\n *\n * Example:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n hud?: boolean;\n\n /**\n * Control the scene's scaling when positioning.\n */\n scaleFactor?: number;\n\n /**\n * `<SceneSync />` avoid stretching the object by default by using the smallest dimension of the DOM element.\n *\n * This variable will tell `<SceneSync />` to stretch it anyways.\n */\n stretch?: boolean;\n\n /**\n * Disable automatic scaling on the object.\n *\n * This variable will also disable any scaling settings like `stretch` and `scaleFactor`.\n */\n disableScaling?: boolean;\n\n /**\n * `<SceneSync />` will depend on this variable to adjust how it should update.\n *\n * There are 3 modes: `relaxed`, `balanced` and `aggressive`.\n *\n * For each mode, there will be some very distinct trade-offs\n *\n * - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.\n * - (+): Minimal update calls, best performance.\n * - (-): The scene get desynced the moment DOM element moves without changing its sizes.\n * When the scene bleeds out of the DOM element too much, if IntersectionObserver reported that the DOM element\n * is out of view, the part of the scene that did not fully moved out of view will stay there.\n * - `balanced`: Uses IntersectionObserver paired with frame-based update, together with ResizeObserver.\n * - (+): Just enough update calls to allow the DOM element to move freely while maintain aceptable performance.\n * - (-): It will update on every frame when the object gets into view as reported by IntersectionObserver. And the same\n * problem with `relaxed` mode when the scene bleeds out too much.\n * - `aggressive`: Frame-based update only. This mode is like how `<View />` from `@react-three/drei` kepts track of DOM elements.\n * - (+): Designed for precise element <-> scene updates. Can't be desynced, if desynced, that's a bug.\n * - (-): This is frame-based. It will fire updates as long as the scene is still mounted. Too many scenes with this\n * mode enabled is not a good idea. Acceptable amount would be 3 scenes with this mode.\n *\n * Best of both worlds is `balanced` mode, for simpler scenes that doesn't change its position, `relaxed` should be used.\n */\n trackingMode: 'relaxed' | 'balanced' | 'aggressive';\n\n /**\n * Set a custom tunnel for `<SceneSync />` send the components to for this scene only.\n *\n * Which is useful for example, put the objects inside a container in the scene.\n *\n * To set a default tunnel, pass it to `setDefaulTunnel` before use.\n */\n tunnelIn?: BasicTunnelIn;\n\n children: ReactNode;\n}\n\ninterface HudProps extends SyncProps {\n hud: true;\n /**\n * Set the `renderPriority` to render things for `Hud`.\n *\n * This variable is ignored when `hud` is not `true`.\n */\n renderPriority: number;\n\n /**\n * Use the parent's camera instead of providing your own in the scene.\n * ```tsx\n * return (\n * <SceneSync hud renderPriority={1} rootCamera={true}>\n * <Box />\n * </SceneSync>\n * );\n * ```\n *\n * If you want to provide your own camera:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>\n * <PerspectiveCamera makeDefault position={[0, 0, 5]} />\n * <group ref={control}>\n * <Box />\n * </group>\n * </SceneSync>\n * );\n * ```\n */\n rootCamera: boolean;\n}\n\ninterface NormalProps extends SyncProps {\n hud?: false;\n}\n\n/**\n * A core part of an in-house tool called `weaver`.\n *\n * A component to allow three objects to track and sync with DOM element.\n *\n * The component uses `<Hud />` under the \"hud\", so if you want to use more than one `<SceneSync />`,\n * you must set `renderPriority`. If not, the component will render the last scene pushed through React.\n */\nexport default function SceneSync(props: NormalProps | HudProps) {\n if (props.trackingMode === 'relaxed' && !weaverSetup._lenisInstance) {\n throw Error(\n \"SceneSync's relaxed mode won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const unique = useId();\n\n const TunnelIn = props.tunnelIn ?? weaverSetup._Default3DTunnelIn;\n\n if (!TunnelIn) {\n throw Error(\n 'Failed to find a tunnel to use. Consider setting a default tunnel.'\n );\n }\n\n if (props.hud) {\n return (\n <TunnelIn>\n <Hud key={unique} renderPriority={props.renderPriority}>\n {props.rootCamera && <HudProjectionHandler />}\n <SyncInternal {...props} />\n </Hud>\n </TunnelIn>\n );\n }\n\n return (\n <TunnelIn>\n <SyncInternal key={unique} {...props} />\n </TunnelIn>\n );\n}\n\nfunction SyncInternal(props: SyncProps) {\n const viewport = useViewport();\n\n const defaultControl = useRef<Group>(null);\n const {\n attach,\n control,\n scaleFactor,\n stretch,\n disableScaling,\n onLayoutUpdate,\n } = props;\n\n const updatePosition = useCallback(() => {\n const activeControl = control ?? defaultControl;\n\n if (!activeControl.current || !attach.current) return;\n\n const domRect = attach.current.getBoundingClientRect();\n const screenH = window.innerHeight;\n const screenW = window.innerWidth;\n const scroll = weaverSetup._lenisInstance?.actualScroll ?? window.scrollY;\n\n const vpWidthRatio = viewport.width / screenW;\n const vpHeightRatio = viewport.height / screenH;\n\n const scrollOffset = (scroll / screenH) * viewport.height;\n\n const w = domRect.width * vpWidthRatio;\n const h = domRect.height * vpHeightRatio;\n\n const x = domRect.x * vpWidthRatio + w * 0.5 - viewport.width * 0.5;\n const y =\n viewport.height * 0.5 -\n (domRect.y + scroll) * vpHeightRatio -\n h * 0.5 +\n scrollOffset;\n\n if (onLayoutUpdate) {\n onLayoutUpdate(domRect, { w, h }, { x, y });\n }\n\n const unwrapedScaleFactor = scaleFactor ?? 1;\n\n if (!disableScaling) {\n if (!stretch) {\n const minScale = Math.min(w, h) * unwrapedScaleFactor;\n activeControl.current.scale.set(minScale, minScale, minScale);\n } else {\n activeControl.current.scale.set(\n w * unwrapedScaleFactor,\n h * unwrapedScaleFactor,\n Math.min(w, h) * unwrapedScaleFactor\n );\n }\n }\n\n // eslint-disable-next-line react-hooks/immutability\n activeControl.current!.position.x = x;\n activeControl.current!.position.y = y;\n }, [\n attach,\n control,\n disableScaling,\n onLayoutUpdate,\n scaleFactor,\n stretch,\n viewport.height,\n viewport.width,\n ]);\n\n /**\n * Update position when function changes.\n */\n useLayoutEffect(() => {\n updatePosition();\n }, [updatePosition]);\n\n const mode = {\n relaxed: (\n <RelaxedUpdate attach={props.attach} updatePosition={updatePosition} />\n ),\n balanced: (\n <BalancedUpdate attach={props.attach} updatePosition={updatePosition} />\n ),\n aggressive: <AggressiveUpdate updatePosition={updatePosition} />,\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\nfunction HudProjectionHandler() {\n const getHudState = useThree((state) => state.get);\n const { previousRoot } = useThree();\n const { camera: rootCamera } = previousRoot!();\n\n useLayoutEffect(() => {\n getHudState().set({ camera: rootCamera });\n }, [getHudState, rootCamera]);\n\n return null;\n}\n","import { useCallback, useState } from 'react';\nimport { useScreenCallback } from './screenCallback';\n\n/**\n * A screen size hook to change components when media-query isn't viable. For example, swap out\n * components when screen gets too small, changing layout of a 3D scene to match the size.\n *\n * The value passed in must be sorted in ascending order.\n *\n * The hooks return where the screen size belong inbetween, for example:\n *\n * ```\n * Input: \" 640 768 1024 1280 1536 \"\n * | | | | | |\n * Returns: 0 1 2 3 4 5\n * ```\n *\n * @param breakpoints Default value is TailwindCSS's screen sizes:\n * `[640, 768, 1024, 1280, 1536]`\n *\n * @returns A number from `0` to `breakpoints.length + 1` depends on screen sizes.\n */\nexport function useBreakpoints(\n breakpoints: number[] = [640, 768, 1024, 1280, 1536]\n) {\n const getBreakpoint = useCallback(\n (width: number) => {\n let result = breakpoints!.length;\n for (let index = 0; index < breakpoints!.length; index++) {\n if (width < breakpoints![index]) {\n result = index;\n break;\n }\n }\n\n return result;\n },\n [breakpoints]\n );\n\n const [breakAt, setBreakAt] = useState<number>(\n getBreakpoint(window.innerWidth)\n );\n\n const breakpointCheck = useCallback(\n (latest: { width: number; height: number }) => {\n const result = getBreakpoint(latest.width);\n if (result !== breakAt) {\n setBreakAt(result);\n }\n },\n [breakAt, getBreakpoint]\n );\n useScreenCallback(breakpointCheck);\n\n return breakAt;\n}\n","import { useLayoutEffect } from 'react';\n\nexport interface ScreenCallbackValues {\n width: number;\n height: number;\n}\n\ntype Callback = (props: ScreenCallbackValues) => void;\n\n/**\n * A callback-based screen hook. Recommended.\n */\nexport function useScreenCallback(\n callback: Callback,\n options?: { initialCall?: boolean }\n) {\n useLayoutEffect(() => {\n const reportScreen = () =>\n callback({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n\n // Call it first time when the hook was initialized.\n if (options?.initialCall) {\n reportScreen();\n }\n\n window.addEventListener('resize', reportScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', reportScreen);\n };\n }, [callback, options?.initialCall]);\n}\n","import { useEffect, useLayoutEffect } from 'react';\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(callback, []);\n}\n\n/**\n * Effect to run once on mount/unmount, ignore all cautions.\n *\n * Strict mode still make this effect runs twice, but never by a state change.\n *\n * Used for initialization, clean up.\n */\nexport function useLayoutEffectOnce(callback: React.EffectCallback) {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(callback, []);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { weaverSetup } from '../setup';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A lenis scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n */\nexport function useLenisCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n if (!weaverSetup._lenisInstance) {\n throw Error(\n \"useLenisCallback won't work without a lenis instance. Provide one via weaverSetup.setLenisInstance\"\n );\n }\n\n const callbackWrapScroll = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(weaverSetup._lenisInstance!.actualScroll, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n weaverSetup._lenisInstance!.on('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(weaverSetup._lenisInstance!.actualScroll, 'initialize');\n }\n\n return () => {\n weaverSetup._lenisInstance!.off('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { type RefObject, useLayoutEffect } from 'react';\n\n/**\n * A simple Orbit hook for ResizeObserver and IntersectionObserver.\n *\n * @param target HTML element ref to attach to.\n * @param events Specify which events should the orbit tracks.\n * @param rootMargin Adjust `rootMargin` option for `IntersectionObserver`.\n */\nexport function useOrbit(options: {\n target?: RefObject<HTMLElement | null>;\n events: {\n onResize?: (entry: ResizeObserverEntry) => void;\n onIntersect?: (entry: IntersectionObserverEntry) => void;\n };\n rootMargin?: string;\n}) {\n const { onResize, onIntersect } = options.events;\n const { rootMargin = '25% 0px 25% 0px' } = options;\n\n useLayoutEffect(() => {\n if (!options.target) return;\n if (!options.target.current) return;\n\n let orbitResize = undefined;\n if (onResize) {\n orbitResize = new ResizeObserver((entries) => onResize(entries[0]));\n orbitResize.observe(options.target.current);\n }\n let orbitIntersect = undefined;\n if (onIntersect) {\n orbitIntersect = new IntersectionObserver(\n (entries) => onIntersect(entries[0]),\n { rootMargin }\n );\n orbitIntersect.observe(options.target.current);\n }\n\n return () => {\n orbitResize?.disconnect();\n orbitIntersect?.disconnect();\n };\n }, [onIntersect, onResize, rootMargin, options.target]);\n}\n","import { type RefObject, useCallback, useLayoutEffect } from 'react';\nimport { useOrbit } from './orbit';\n\ninterface HookOptions {\n /**\n * Hook will report when first initialized without waiting for scroll event to actually happens.\n */\n initialCall?: boolean;\n /**\n * Set an element to only call when the element is actually entering the viewport (with 25% `rootMargin`).\n */\n intersectOn?: RefObject<HTMLOrSVGElement | null>;\n}\n\nexport type ScrollCallbackReason = 'resize' | 'scroll' | 'initialize';\n\n/**\n * A DOM scroll hook.\n *\n * This hook calls many time and repeated. Update states inside this hook carefully to avoid performance issues.\n *\n * For manipulating elements matches closely with the actual scroll offet, consider use `lenis` and utilize `useLenisCallback`.\n */\nexport function useMouseCallback(\n callback: (latest: number, reason: ScrollCallbackReason) => void,\n options?: HookOptions\n) {\n const callbackWrapScroll = useCallback(\n () => callback(window.scrollY, 'scroll'),\n [callback]\n );\n const callbackWrapResize = useCallback(\n () => callback(window.scrollY, 'resize'),\n [callback]\n );\n\n useOrbit({\n target: options?.intersectOn as RefObject<HTMLElement | null> | undefined,\n events: {\n onIntersect(entry) {\n if (entry.isIntersecting) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n } else {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n }\n },\n },\n rootMargin: '50% 0px 50% 0px',\n });\n\n useLayoutEffect(() => {\n if (!options?.intersectOn) {\n window.addEventListener('scroll', callbackWrapScroll);\n window.addEventListener('resize', callbackWrapResize);\n }\n\n if (options?.initialCall) {\n callback(window.scrollY, 'initialize');\n }\n\n return () => {\n window.removeEventListener('scroll', callbackWrapScroll);\n window.removeEventListener('resize', callbackWrapResize);\n };\n }, [\n callback,\n callbackWrapResize,\n callbackWrapScroll,\n options?.initialCall,\n options?.intersectOn,\n ]);\n}\n","import { useCallback } from 'react';\nimport { useNavigate } from 'react-router';\n\n/**\n * A hook to replace `<Link>` from `react-router`, attach the function to\n * `onClick` event of an anchor tag to overwrites its behavior.\n *\n * Example usage:\n * ```tsx\n * const navigator = useNavigateAnchor();\n *\n * return (\n * <a href=\"/\" onClick={navigator}>\n * Navigate\n * </a>\n * );\n * ```\n *\n * @param onNavigate Calls when a navigation event happens.\n * @param onSameRoute Calls when user is on the same route, no navigation happens.\n * @returns\n */\nexport function useNavigateAnchor(\n onNavigate?: () => void,\n onSameRoute?: () => void\n) {\n const navigate = useNavigate();\n\n return useCallback(\n (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {\n event.preventDefault();\n const href = event.currentTarget.getAttribute('href');\n if (href) {\n if (onNavigate) {\n onNavigate();\n }\n\n if (href !== window.location.pathname) {\n navigate(event.currentTarget.getAttribute('href') ?? '');\n } else {\n if (onSameRoute) {\n onSameRoute();\n }\n }\n }\n },\n [onNavigate, navigate, onSameRoute]\n );\n}\n","import { useLocation } from 'react-router';\n\n/**\n * Routing hook. This hook updates and splits pathname on location change.\n *\n * Great for creating custom routes on the fly under the same parent `Pipeline`.\n */\nexport function useRawParams(): (string | undefined)[] {\n const { pathname } = useLocation();\n\n return pathname.split('/').filter((param) => param !== '');\n}\n","import { useCallback, useLayoutEffect, useState } from 'react';\n\n/**\n * A state-based screen hook. It will change its state on resize.\n */\nexport function useScreen() {\n const [width, setWidth] = useState(window.innerWidth);\n const [height, setHeight] = useState(window.innerHeight);\n\n const setScreen = useCallback(() => {\n setWidth(window.innerWidth);\n setHeight(window.innerHeight);\n }, []);\n\n useLayoutEffect(() => {\n window.addEventListener('resize', setScreen, { passive: true });\n\n return () => {\n window.removeEventListener('resize', setScreen);\n };\n }, [setScreen]);\n\n return { width: width, height: height };\n}\n","import { useThree } from '@react-three/fiber';\nimport { OrthographicCamera, PerspectiveCamera } from 'three';\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 return {\n width: viewport.getCurrentViewport(customCamera ?? camera).width,\n height: viewport.getCurrentViewport(customCamera ?? camera).height,\n };\n}\n"],"mappings":"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","names":["useThree","React","useCallback","useId","useLayoutEffect","useRef","useState","useState","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useLayoutEffect","useCallback","useCallback","useLayoutEffect","useState","useId","React","useRef","useCallback","useLayoutEffect","useState","useThree"]}
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.26",
8
+ "version": "1.1.0",
9
9
  "description": "A collection of React CSR components, hooks for handling many moving part of a creative web with ease. ",
10
10
  "packageManager": "yarn@4.10.3",
11
11
  "publishConfig": {