@neveranyart/weaver 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Weaver
2
2
 
3
+ We are in a progress to expand the package to cover more cases, especially `SceneSync`.
4
+
3
5
  An in-house core package by neveranyart for making performant React CSR.
4
6
 
5
7
  <sub>Almost feels like a framework, but it's not.</sub>
package/dist/scene.d.mts CHANGED
@@ -72,24 +72,23 @@ interface SyncProps {
72
72
  /**
73
73
  * Use `Hud` for this scene or not.
74
74
  *
75
- * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.
75
+ * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on top of each other.
76
76
  *
77
- * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera
78
- * to avoid unwanted behavior.
77
+ * To use a custom camera, pass a Drei's camera to `camera` variable, ensure that it has `makeDefault` set to true.
79
78
  *
80
- * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,
81
- * the camera is also change, ruining the effect.
79
+ * To use the parent's default camera, pass `null` to `camera` variable.
82
80
  *
83
- * Example:
81
+ * Example with a custom `OrthographicCamera`:
84
82
  * ```tsx
85
83
  * const control = useRef<Group>(null);
86
84
  *
87
85
  * return (
88
- * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>
89
- * <PerspectiveCamera makeDefault position={[0, 0, 5]} />
90
- * <group ref={control}>
91
- * <Box />
92
- * </group>
86
+ * <SceneSync
87
+ * hud
88
+ * renderPriority={1}
89
+ * camera={<OrthographicCamera makeDefault zoom={100} position={[0, 0, 5]} />}
90
+ * >
91
+ * <Box />
93
92
  * </SceneSync>
94
93
  * );
95
94
  * ```
@@ -148,30 +147,9 @@ interface SyncProps {
148
147
  interface HudProps extends SyncProps {
149
148
  hud: true;
150
149
  /**
151
- * Use the parent's camera instead of providing your own in the scene.
152
- * ```tsx
153
- * return (
154
- * <SceneSync hud renderPriority={1} rootCamera={true}>
155
- * <Box />
156
- * </SceneSync>
157
- * );
158
- * ```
159
- *
160
- * If you want to provide your own camera:
161
- * ```tsx
162
- * const control = useRef<Group>(null);
163
- *
164
- * return (
165
- * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>
166
- * <PerspectiveCamera makeDefault position={[0, 0, 5]} />
167
- * <group ref={control}>
168
- * <Box />
169
- * </group>
170
- * </SceneSync>
171
- * );
172
- * ```
150
+ * Provide your own camera, set to null to use the parent's camera.
173
151
  */
174
- rootCamera: true;
152
+ camera: ReactNode | null;
175
153
  /**
176
154
  * Set the `renderPriority` to render things for `Hud`.
177
155
  *
@@ -179,36 +157,6 @@ interface HudProps extends SyncProps {
179
157
  */
180
158
  renderPriority: number;
181
159
  }
182
- interface HudWithoutCameraProps extends SyncProps {
183
- hud: true;
184
- /**
185
- * Use the parent's camera instead of providing your own in the scene.
186
- * ```tsx
187
- * return (
188
- * <SceneSync hud renderPriority={1} rootCamera={true}>
189
- * <Box />
190
- * </SceneSync>
191
- * );
192
- * ```
193
- *
194
- * If you want to provide your own camera:
195
- * ```tsx
196
- * const control = useRef<Group>(null);
197
- *
198
- * return (
199
- * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>
200
- * <PerspectiveCamera makeDefault position={[0, 0, 5]} />
201
- * <group ref={control}>
202
- * <Box />
203
- * </group>
204
- * </SceneSync>
205
- * );
206
- * ```
207
- */
208
- rootCamera: false;
209
- renderPriority: number;
210
- control: RefObject<Basic3DTransforms | null>;
211
- }
212
160
  interface NormalProps extends SyncProps {
213
161
  hud?: false;
214
162
  }
@@ -220,6 +168,6 @@ interface NormalProps extends SyncProps {
220
168
  * The component uses `<Hud />` under the "hud", so if you want to use more than one `<SceneSync />`,
221
169
  * you must set `renderPriority`. If not, the component will render the last scene pushed through React.
222
170
  */
223
- declare function SceneSync(props: NormalProps | HudProps | HudWithoutCameraProps): React.JSX.Element;
171
+ declare function SceneSync(props: NormalProps | HudProps): React.JSX.Element;
224
172
 
225
173
  export { BakeScene, SceneSync };
package/dist/scene.d.ts CHANGED
@@ -72,24 +72,23 @@ interface SyncProps {
72
72
  /**
73
73
  * Use `Hud` for this scene or not.
74
74
  *
75
- * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on each other.
75
+ * This is useful when you want to apply custom camera for this scene, or renders multiple scenes on top of each other.
76
76
  *
77
- * NOTE: When setting a custom camera, the `control` variable must also be set and mount to your scene, not related to the camera
78
- * to avoid unwanted behavior.
77
+ * To use a custom camera, pass a Drei's camera to `camera` variable, ensure that it has `makeDefault` set to true.
79
78
  *
80
- * `<SceneSync />` groups children passed to it by default, so the camera is also in the group, when syncer updates the group,
81
- * the camera is also change, ruining the effect.
79
+ * To use the parent's default camera, pass `null` to `camera` variable.
82
80
  *
83
- * Example:
81
+ * Example with a custom `OrthographicCamera`:
84
82
  * ```tsx
85
83
  * const control = useRef<Group>(null);
86
84
  *
87
85
  * return (
88
- * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>
89
- * <PerspectiveCamera makeDefault position={[0, 0, 5]} />
90
- * <group ref={control}>
91
- * <Box />
92
- * </group>
86
+ * <SceneSync
87
+ * hud
88
+ * renderPriority={1}
89
+ * camera={<OrthographicCamera makeDefault zoom={100} position={[0, 0, 5]} />}
90
+ * >
91
+ * <Box />
93
92
  * </SceneSync>
94
93
  * );
95
94
  * ```
@@ -148,30 +147,9 @@ interface SyncProps {
148
147
  interface HudProps extends SyncProps {
149
148
  hud: true;
150
149
  /**
151
- * Use the parent's camera instead of providing your own in the scene.
152
- * ```tsx
153
- * return (
154
- * <SceneSync hud renderPriority={1} rootCamera={true}>
155
- * <Box />
156
- * </SceneSync>
157
- * );
158
- * ```
159
- *
160
- * If you want to provide your own camera:
161
- * ```tsx
162
- * const control = useRef<Group>(null);
163
- *
164
- * return (
165
- * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>
166
- * <PerspectiveCamera makeDefault position={[0, 0, 5]} />
167
- * <group ref={control}>
168
- * <Box />
169
- * </group>
170
- * </SceneSync>
171
- * );
172
- * ```
150
+ * Provide your own camera, set to null to use the parent's camera.
173
151
  */
174
- rootCamera: true;
152
+ camera: ReactNode | null;
175
153
  /**
176
154
  * Set the `renderPriority` to render things for `Hud`.
177
155
  *
@@ -179,36 +157,6 @@ interface HudProps extends SyncProps {
179
157
  */
180
158
  renderPriority: number;
181
159
  }
182
- interface HudWithoutCameraProps extends SyncProps {
183
- hud: true;
184
- /**
185
- * Use the parent's camera instead of providing your own in the scene.
186
- * ```tsx
187
- * return (
188
- * <SceneSync hud renderPriority={1} rootCamera={true}>
189
- * <Box />
190
- * </SceneSync>
191
- * );
192
- * ```
193
- *
194
- * If you want to provide your own camera:
195
- * ```tsx
196
- * const control = useRef<Group>(null);
197
- *
198
- * return (
199
- * <SceneSync control={control} hud renderPriority={1} rootCamera={false}>
200
- * <PerspectiveCamera makeDefault position={[0, 0, 5]} />
201
- * <group ref={control}>
202
- * <Box />
203
- * </group>
204
- * </SceneSync>
205
- * );
206
- * ```
207
- */
208
- rootCamera: false;
209
- renderPriority: number;
210
- control: RefObject<Basic3DTransforms | null>;
211
- }
212
160
  interface NormalProps extends SyncProps {
213
161
  hud?: false;
214
162
  }
@@ -220,6 +168,6 @@ interface NormalProps extends SyncProps {
220
168
  * The component uses `<Hud />` under the "hud", so if you want to use more than one `<SceneSync />`,
221
169
  * you must set `renderPriority`. If not, the component will render the last scene pushed through React.
222
170
  */
223
- declare function SceneSync(props: NormalProps | HudProps | HudWithoutCameraProps): React.JSX.Element;
171
+ declare function SceneSync(props: NormalProps | HudProps): React.JSX.Element;
224
172
 
225
173
  export { BakeScene, SceneSync };
package/dist/scene.js CHANGED
@@ -245,7 +245,7 @@ function SceneSync(props) {
245
245
  );
246
246
  }
247
247
  if (props.hud) {
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 })));
248
+ return /* @__PURE__ */ import_react11.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react11.default.createElement(import_drei2.Hud, { key: unique, renderPriority: props.renderPriority }, props.camera !== null ? props.camera : /* @__PURE__ */ import_react11.default.createElement(HudCameraHandler, null), /* @__PURE__ */ import_react11.default.createElement(SyncInternal, { ...props })));
249
249
  }
250
250
  return /* @__PURE__ */ import_react11.default.createElement(TunnelIn, null, /* @__PURE__ */ import_react11.default.createElement(SyncInternal, { key: unique, ...props }));
251
251
  }
@@ -367,7 +367,7 @@ function AggressiveUpdate(props) {
367
367
  }, [updatePosition]);
368
368
  return null;
369
369
  }
370
- function HudProjectionHandler() {
370
+ function HudCameraHandler() {
371
371
  const getHudState = (0, import_fiber3.useThree)((state) => state.get);
372
372
  const { previousRoot } = (0, import_fiber3.useThree)();
373
373
  const { camera: rootCamera } = previousRoot();
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 { 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 /**\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: true;\n\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\ninterface HudWithoutCameraProps extends SyncProps {\n hud: true;\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: false;\n\n renderPriority: number;\n control: RefObject<Basic3DTransforms | null>;\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(\n props: NormalProps | HudProps | HudWithoutCameraProps\n) {\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"]}
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 top of each other.\n *\n * To use a custom camera, pass a Drei's camera to `camera` variable, ensure that it has `makeDefault` set to true.\n *\n * To use the parent's default camera, pass `null` to `camera` variable.\n *\n * Example with a custom `OrthographicCamera`:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync\n * hud\n * renderPriority={1}\n * camera={<OrthographicCamera makeDefault zoom={100} position={[0, 0, 5]} />}\n * >\n * <Box />\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 /**\n * Provide your own camera, set to null to use the parent's camera.\n */\n camera: ReactNode | null;\n\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\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.camera !== null ? props.camera : <HudCameraHandler />}\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 HudCameraHandler() {\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA4B;AAC5B,mBAAyB;AACzB,mBAQO;;;ACAP,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA,EAIhB,IAAI,iBAAoC;AACtC,WAAO,WAAW;AAAA,EACpB;AAAA,EACA,IAAI,eAAe,KAAwB;AACzC,eAAW,gBAAgB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,qBAAgD;AAClD,WAAO,WAAW;AAAA,EACpB;AAAA,EACA,IAAI,mBAAmB,KAAgC;AACrD,eAAW,mBAAmB;AAAA,EAChC;AAAA,EAEA,iBAAiB,UAAiB;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA,EACA,YAAY,UAAyB;AACnC,SAAK,qBAAqB;AAAA,EAC5B;AACF;AAEO,IAAM,cAAc,IAAI,YAAY;;;ADV5B,SAAR,UAA2B,OAK/B;AACD,QAAM,aAAS,oBAAM;AACrB,QAAM,kBAAc,oBAAM;AAE1B,QAAM,WAAW,MAAM,YAAY,YAAY;AAE/C,MAAI,CAAC,UAAU;AACb,UAAM;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,SACE,6BAAAA,QAAA,cAAC,gBACC,6BAAAA,QAAA,cAAC,yBAAS,KAAK,eAAc,MAAM,QAAS,GAC5C,6BAAAA,QAAA;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,cAAc,MAAM;AAAA,MACpB,eAAe,MAAM;AAAA;AAAA,EACvB,CACF;AAEJ;AAEA,SAAS,oBAAoB,OAG1B;AACD,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAKlD,SACE,CAAC,cAAc,6BAAAA,QAAA,cAAC,kBAAgB,GAAG,OAAO,eAA8B;AAE5E;AAEA,SAAS,eAAe,OAIrB;AACD,QAAM,EAAE,SAAS,QAAI,yBAAY;AACjC,QAAM,2BAAuB,qBAAO,KAAK;AAEzC,6BAAS,MAAM;AACb,QAAI,CAAC,qBAAqB,WAAW,aAAa,KAAK;AACrD,2BAAqB,UAAU;AAC/B,iBAAW,MAAM;AACf,cAAM,cAAc,IAAI;AACxB,cAAM,aAAa;AAAA,MACrB,GAAG,MAAM,iBAAiB,EAAE;AAAA,IAC9B;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;AE3FA,IAAAC,eAAoB;AACpB,IAAAC,gBAAyB;AACzB,IAAAC,iBAAmC;AACnC,IAAAA,iBAQO;;;ACXP,IAAAC,gBAAsC;;;ACAtC,IAAAC,gBAAgC;;;ACAhC,IAAAC,gBAA2C;AAqBpC,SAAS,oBAAoB,UAAgC;AAElE,qCAAgB,UAAU,CAAC,CAAC;AAC9B;;;ACxBA,IAAAC,gBAA6D;;;ACA7D,IAAAC,gBAAgD;AASzC,SAAS,SAAS,SAOtB;AACD,QAAM,EAAE,UAAU,YAAY,IAAI,QAAQ;AAC1C,QAAM,EAAE,aAAa,kBAAkB,IAAI;AAE3C,qCAAgB,MAAM;AACpB,QAAI,CAAC,QAAQ,OAAQ;AACrB,QAAI,CAAC,QAAQ,OAAO,QAAS;AAE7B,QAAI,cAAc;AAClB,QAAI,UAAU;AACZ,oBAAc,IAAI,eAAe,CAAC,YAAY,SAAS,QAAQ,CAAC,CAAC,CAAC;AAClE,kBAAY,QAAQ,QAAQ,OAAO,OAAO;AAAA,IAC5C;AACA,QAAI,iBAAiB;AACrB,QAAI,aAAa;AACf,uBAAiB,IAAI;AAAA,QACnB,CAAC,YAAY,YAAY,QAAQ,CAAC,CAAC;AAAA,QACnC,EAAE,WAAW;AAAA,MACf;AACA,qBAAe,QAAQ,QAAQ,OAAO,OAAO;AAAA,IAC/C;AAEA,WAAO,MAAM;AACX,mBAAa,WAAW;AACxB,sBAAgB,WAAW;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,aAAa,UAAU,YAAY,QAAQ,MAAM,CAAC;AACxD;;;ADrBO,SAAS,iBACd,UACA,SACA;AACA,MAAI,CAAC,YAAY,gBAAgB;AAC/B,UAAM;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,QAAM,yBAAqB;AAAA,IACzB,MAAM,SAAS,YAAY,eAAgB,cAAc,QAAQ;AAAA,IACjE,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,yBAAqB;AAAA,IACzB,MAAM,SAAS,YAAY,eAAgB,cAAc,QAAQ;AAAA,IACjE,CAAC,QAAQ;AAAA,EACX;AAEA,WAAS;AAAA,IACP,QAAQ,SAAS;AAAA,IACjB,QAAQ;AAAA,MACN,YAAY,OAAO;AACjB,YAAI,MAAM,gBAAgB;AACxB,sBAAY,eAAgB,GAAG,UAAU,kBAAkB;AAC3D,iBAAO,iBAAiB,UAAU,kBAAkB;AAAA,QACtD,OAAO;AACL,sBAAY,eAAgB,IAAI,UAAU,kBAAkB;AAC5D,iBAAO,oBAAoB,UAAU,kBAAkB;AAAA,QACzD;AAAA,MACF;AAAA,IACF;AAAA,IACA,YAAY;AAAA,EACd,CAAC;AAED,qCAAgB,MAAM;AACpB,QAAI,CAAC,SAAS,aAAa;AACzB,kBAAY,eAAgB,GAAG,UAAU,kBAAkB;AAC3D,aAAO,iBAAiB,UAAU,kBAAkB;AAAA,IACtD;AAEA,QAAI,SAAS,aAAa;AACxB,eAAS,YAAY,eAAgB,cAAc,YAAY;AAAA,IACjE;AAEA,WAAO,MAAM;AACX,kBAAY,eAAgB,IAAI,UAAU,kBAAkB;AAC5D,aAAO,oBAAoB,UAAU,kBAAkB;AAAA,IACzD;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACH;;;AE9EA,IAAAC,gBAA6D;;;ACA7D,IAAAC,gBAA4B;AAC5B,0BAA4B;;;ACD5B,IAAAC,uBAA4B;;;ACA5B,IAAAC,gBAAuD;;;ACAvD,IAAAC,gBAAyB;AAMlB,SAAS,YACd,cACA;AACA,QAAM,EAAE,UAAU,OAAO,QAAI,wBAAS;AAEtC,SAAO;AAAA,IACL,OAAO,SAAS,mBAAmB,gBAAgB,MAAM,EAAE;AAAA,IAC3D,QAAQ,SAAS,mBAAmB,gBAAgB,MAAM,EAAE;AAAA,EAC9D;AACF;;;AVgKe,SAAR,UAA2B,OAA+B;AAC/D,MAAI,MAAM,iBAAiB,aAAa,CAAC,YAAY,gBAAgB;AACnE,UAAM;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAS,sBAAM;AAErB,QAAM,WAAW,MAAM,YAAY,YAAY;AAE/C,MAAI,CAAC,UAAU;AACb,UAAM;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,KAAK;AACb,WACE,+BAAAC,QAAA,cAAC,gBACC,+BAAAA,QAAA,cAAC,oBAAI,KAAK,QAAQ,gBAAgB,MAAM,kBACrC,MAAM,WAAW,OAAO,MAAM,SAAS,+BAAAA,QAAA,cAAC,sBAAiB,GAC1D,+BAAAA,QAAA,cAAC,gBAAc,GAAG,OAAO,CAC3B,CACF;AAAA,EAEJ;AAEA,SACE,+BAAAA,QAAA,cAAC,gBACC,+BAAAA,QAAA,cAAC,gBAAa,KAAK,QAAS,GAAG,OAAO,CACxC;AAEJ;AAEA,SAAS,aAAa,OAAkB;AACtC,QAAM,WAAW,YAAY;AAE7B,QAAM,qBAAiB,uBAAc,IAAI;AACzC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,qBAAiB,4BAAY,MAAM;AACvC,UAAM,gBAAgB,WAAW;AAEjC,QAAI,CAAC,cAAc,WAAW,CAAC,OAAO,QAAS;AAE/C,UAAM,UAAU,OAAO,QAAQ,sBAAsB;AACrD,UAAM,UAAU,OAAO;AACvB,UAAM,UAAU,OAAO;AACvB,UAAM,SAAS,YAAY,gBAAgB,gBAAgB,OAAO;AAElE,UAAM,eAAe,SAAS,QAAQ;AACtC,UAAM,gBAAgB,SAAS,SAAS;AAExC,UAAM,eAAgB,SAAS,UAAW,SAAS;AAEnD,UAAM,IAAI,QAAQ,QAAQ;AAC1B,UAAM,IAAI,QAAQ,SAAS;AAE3B,UAAM,IAAI,QAAQ,IAAI,eAAe,IAAI,MAAM,SAAS,QAAQ;AAChE,UAAM,IACJ,SAAS,SAAS,OACjB,QAAQ,IAAI,UAAU,gBACvB,IAAI,MACJ;AAEF,QAAI,gBAAgB;AAClB,qBAAe,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAAA,IAC5C;AAEA,UAAM,sBAAsB,eAAe;AAE3C,QAAI,CAAC,gBAAgB;AACnB,UAAI,CAAC,SAAS;AACZ,cAAM,WAAW,KAAK,IAAI,GAAG,CAAC,IAAI;AAClC,sBAAc,QAAQ,MAAM,IAAI,UAAU,UAAU,QAAQ;AAAA,MAC9D,OAAO;AACL,sBAAc,QAAQ,MAAM;AAAA,UAC1B,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,KAAK,IAAI,GAAG,CAAC,IAAI;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAGA,kBAAc,QAAS,SAAS,IAAI;AACpC,kBAAc,QAAS,SAAS,IAAI;AAAA,EACtC,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AAKD,sCAAgB,MAAM;AACpB,mBAAe;AAAA,EACjB,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,OAAO;AAAA,IACX,SACE,+BAAAA,QAAA,cAAC,iBAAc,QAAQ,MAAM,QAAQ,gBAAgC;AAAA,IAEvE,UACE,+BAAAA,QAAA,cAAC,kBAAe,QAAQ,MAAM,QAAQ,gBAAgC;AAAA,IAExE,YAAY,+BAAAA,QAAA,cAAC,oBAAiB,gBAAgC;AAAA,EAChE;AAEA,MAAI,MAAM,SAAS;AACjB,WACE,+BAAAA,QAAA,6BAAAA,QAAA,gBACG,MAAM,UACN,KAAK,MAAM,YAAY,CAC1B;AAAA,EAEJ;AAEA,SACE,+BAAAA,QAAA,cAAC,WAAM,KAAK,kBACT,MAAM,UACN,KAAK,MAAM,YAAY,CAC1B;AAEJ;AAEA,SAAS,cAAc,OAGpB;AACD,QAAM,EAAE,eAAe,IAAI;AAK3B,mBAAiB,gBAAgB;AAAA,IAC/B,aAAa;AAAA,IACb,aAAa,MAAM;AAAA,EACrB,CAAC;AAKD,WAAS;AAAA,IACP,QAAQ,MAAM;AAAA,IACd,QAAQ;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF,CAAC;AAED,sBAAoB,cAAc;AAClC,SAAO;AACT;AAEA,SAAS,eAAe,OAGrB;AACD,QAAM,EAAE,eAAe,IAAI;AAE3B,QAAM,CAAC,cAAc,eAAe,QAAI,yBAAS,KAAK;AAEtD,WAAS;AAAA,IACP,QAAQ,MAAM;AAAA,IACd,QAAQ;AAAA,MACN,YAAY,OAAO;AACjB,wBAAgB,MAAM,cAAc;AAAA,MACtC;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,EACd,CAAC;AAED,sCAAgB,MAAM;AACpB,QAAI,cAAc;AAChB,2BAAM,KAAK,gBAAgB,IAAI;AAAA,IACjC,OAAO;AACL,sCAAY,cAAc;AAAA,IAC5B;AAEA,WAAO,MAAM;AACX,sCAAY,cAAc;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,gBAAgB,YAAY,CAAC;AAEjC,sBAAoB,cAAc;AAClC,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAuC;AAC/D,QAAM,EAAE,eAAe,IAAI;AAE3B,sCAAgB,MAAM;AACpB,yBAAM,KAAK,gBAAgB,IAAI;AAE/B,WAAO,MAAM;AACX,sCAAY,cAAc;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAEnB,SAAO;AACT;AAEA,SAAS,mBAAmB;AAC1B,QAAM,kBAAc,wBAAS,CAAC,UAAU,MAAM,GAAG;AACjD,QAAM,EAAE,aAAa,QAAI,wBAAS;AAClC,QAAM,EAAE,QAAQ,WAAW,IAAI,aAAc;AAE7C,sCAAgB,MAAM;AACpB,gBAAY,EAAE,IAAI,EAAE,QAAQ,WAAW,CAAC;AAAA,EAC1C,GAAG,CAAC,aAAa,UAAU,CAAC;AAE5B,SAAO;AACT;","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
@@ -219,7 +219,7 @@ function SceneSync(props) {
219
219
  );
220
220
  }
221
221
  if (props.hud) {
222
- return /* @__PURE__ */ React2.createElement(TunnelIn, null, /* @__PURE__ */ React2.createElement(Hud, { key: unique, renderPriority: props.renderPriority }, props.rootCamera && /* @__PURE__ */ React2.createElement(HudProjectionHandler, null), /* @__PURE__ */ React2.createElement(SyncInternal, { ...props })));
222
+ return /* @__PURE__ */ React2.createElement(TunnelIn, null, /* @__PURE__ */ React2.createElement(Hud, { key: unique, renderPriority: props.renderPriority }, props.camera !== null ? props.camera : /* @__PURE__ */ React2.createElement(HudCameraHandler, null), /* @__PURE__ */ React2.createElement(SyncInternal, { ...props })));
223
223
  }
224
224
  return /* @__PURE__ */ React2.createElement(TunnelIn, null, /* @__PURE__ */ React2.createElement(SyncInternal, { key: unique, ...props }));
225
225
  }
@@ -341,7 +341,7 @@ function AggressiveUpdate(props) {
341
341
  }, [updatePosition]);
342
342
  return null;
343
343
  }
344
- function HudProjectionHandler() {
344
+ function HudCameraHandler() {
345
345
  const getHudState = useThree2((state) => state.get);
346
346
  const { previousRoot } = useThree2();
347
347
  const { camera: rootCamera } = previousRoot();
@@ -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 { 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 /**\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: true;\n\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\ninterface HudWithoutCameraProps extends SyncProps {\n hud: true;\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: false;\n\n renderPriority: number;\n control: RefObject<Basic3DTransforms | null>;\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(\n props: NormalProps | HudProps | HudWithoutCameraProps\n) {\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"]}
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 top of each other.\n *\n * To use a custom camera, pass a Drei's camera to `camera` variable, ensure that it has `makeDefault` set to true.\n *\n * To use the parent's default camera, pass `null` to `camera` variable.\n *\n * Example with a custom `OrthographicCamera`:\n * ```tsx\n * const control = useRef<Group>(null);\n *\n * return (\n * <SceneSync\n * hud\n * renderPriority={1}\n * camera={<OrthographicCamera makeDefault zoom={100} position={[0, 0, 5]} />}\n * >\n * <Box />\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 /**\n * Provide your own camera, set to null to use the parent's camera.\n */\n camera: ReactNode | null;\n\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\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.camera !== null ? props.camera : <HudCameraHandler />}\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 HudCameraHandler() {\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/docs/SCENE.md CHANGED
@@ -70,7 +70,9 @@ that DOM element, there are multiple ways for you to decide on how a scene shoul
70
70
  The component works by creating a group, containing your scene, and position the group along with user scroll, or frame-based positioning.
71
71
 
72
72
  > [!CAUTION]
73
- > The only limitation is to never touch the camera, you can initialize a custom camera, but in no way you should move/rotate it.
73
+ > Limitations:
74
+ > - You can initialize any type of camera, but in no way you should move/rotate it.
75
+ > - `SceneSync` assumes all objects are at `z=0`, so if you move the `control` group/mesh, it will look off.
74
76
 
75
77
  There are 3 modes: `relaxed`, `balanced` and `aggressive`:
76
78
  - `relaxed`: Uses IntersectionObserver paired with lenis hook, together with ResizeObserver.
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.1.2",
8
+ "version": "1.1.3",
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": {