@hunterchen/canvas 0.9.0 → 0.10.1
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 +6 -2
- package/dist/components/canvas/canvas.d.ts +4 -0
- package/dist/components/canvas/canvas.d.ts.map +1 -1
- package/dist/components/canvas/canvas.js +29 -15
- package/dist/components/canvas/canvas.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +6 -1
- package/dist/lib/constants.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/canvas/canvas.tsx +39 -12
- package/src/index.ts +1 -0
- package/src/lib/constants.ts +6 -0
- package/src/types/index.ts +24 -0
package/README.md
CHANGED
|
@@ -573,7 +573,9 @@ The library exports default gradient values you can use as a starting point:
|
|
|
573
573
|
import {
|
|
574
574
|
DEFAULT_CANVAS_GRADIENT, // Default canvas background gradient
|
|
575
575
|
DEFAULT_INTRO_GRADIENT, // Default intro background gradient
|
|
576
|
-
DEFAULT_CANVAS_BOX_GRADIENT // Default blur mask gradient
|
|
576
|
+
DEFAULT_CANVAS_BOX_GRADIENT, // Default blur mask gradient
|
|
577
|
+
FADE_TRANSITION, // Default scene fade-in transition
|
|
578
|
+
STAGE2_TRANSITION, // Default pan-to-home transition
|
|
577
579
|
} from '@hunterchen/canvas';
|
|
578
580
|
```
|
|
579
581
|
|
|
@@ -687,13 +689,15 @@ The `Canvas` component accepts the following props:
|
|
|
687
689
|
| `canvasWidth` | `number` | No | `6000` | Total canvas width in pixels |
|
|
688
690
|
| `canvasHeight` | `number` | No | `4000` | Total canvas height in pixels |
|
|
689
691
|
| `navItems` | `NavItem[]` | No | - | Navigation items for navbar |
|
|
690
|
-
| `skipIntro` | `boolean` | No | `false` | Skip intro
|
|
692
|
+
| `skipIntro` | `boolean` | No | `false` | Skip all intro animations (starts at home position) |
|
|
691
693
|
| `introContent` | `ReactNode` | No | - | Custom intro content during loading |
|
|
692
694
|
| `loadingText` | `string` | No | - | Custom loading text |
|
|
693
695
|
| `introBackgroundGradient` | `string` | No | - | Background gradient for intro |
|
|
694
696
|
| `canvasBoxGradient` | `string` | No | - | Canvas box gradient during intro |
|
|
695
697
|
| `growTransition` | `Transition` | No | - | Custom grow transition (Framer Motion) |
|
|
696
698
|
| `blurTransition` | `Transition` | No | - | Custom blur transition (Framer Motion) |
|
|
699
|
+
| `panTransition` | `Transition` | No | - | Custom pan-to-home transition (Framer Motion) |
|
|
700
|
+
| `fadeTransition` | `Transition` | No | - | Custom scene fade-in transition (Framer Motion) |
|
|
697
701
|
| `canvasBackground` | `ReactNode` | No | `<DefaultCanvasBackground />` | Custom canvas background |
|
|
698
702
|
| `wrapperBackground` | `ReactNode` | No | - | Custom wrapper/intro background |
|
|
699
703
|
| `toolbarConfig` | `ToolbarConfig` | No | - | Toolbar customization options |
|
|
@@ -23,6 +23,10 @@ interface Props {
|
|
|
23
23
|
growTransition?: Transition;
|
|
24
24
|
/** Custom blur transition */
|
|
25
25
|
blurTransition?: Transition;
|
|
26
|
+
/** Custom pan-to-home transition (stage 2) */
|
|
27
|
+
panTransition?: Transition;
|
|
28
|
+
/** Custom fade-in transition for the canvas scene */
|
|
29
|
+
fadeTransition?: Transition;
|
|
26
30
|
/** Custom canvas background. If not provided, uses DefaultCanvasBackground. */
|
|
27
31
|
canvasBackground?: ReactNode;
|
|
28
32
|
/** Custom wrapper/intro background. If not provided, uses introBackgroundGradient. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/components/canvas/canvas.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,EAIZ,KAAK,EAAE,EAIR,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/components/canvas/canvas.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,EAIZ,KAAK,EAAE,EAIR,MAAM,OAAO,CAAC;AA2Bf,OAAO,KAAK,EAEV,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACd,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,UAAU,KAAK;IACb,eAAe,EAAE,kBAAkB,CAAC;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAG1B,WAAW,CAAC,EAAC,MAAM,CAAC;IACpB,YAAY,CAAC,EAAC,MAAM,CAAC;IAGrB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IAGrB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,6BAA6B;IAC7B,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,8CAA8C;IAC9C,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,qDAAqD;IACrD,cAAc,CAAC,EAAE,UAAU,CAAC;IAG5B,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAG9B,oCAAoC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;IAG9B,mCAAmC;IACnC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAYD,QAAA,MAAM,MAAM,EAAE,EAAE,CAAC,KAAK,CAwnBrB,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CanvasProvider } from "../../contexts/CanvasContext.js";
|
|
2
|
-
import { DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, INTERACTIVE_SELECTOR, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, STAGE2_TRANSITION, TRACKPAD_ZOOM_SENSITIVITY, ZOOM_BOUND } from "../../lib/constants.js";
|
|
2
|
+
import { DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, FADE_TRANSITION, INTERACTIVE_SELECTOR, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, STAGE2_TRANSITION, TRACKPAD_ZOOM_SENSITIVITY, ZOOM_BOUND } from "../../lib/constants.js";
|
|
3
3
|
import { calcInitialBoxWidth, getDistance, getMidpoint, getScreenSizeEnum, getSectionPanCoordinates, panToOffsetScene } from "../../lib/canvas.js";
|
|
4
4
|
import useWindowDimensions_default from "../../hooks/useWindowDimensions.js";
|
|
5
5
|
import { usePerformanceMode } from "../../hooks/usePerformanceMode.js";
|
|
@@ -17,7 +17,7 @@ const stopAllMotion = (x, y, scale) => {
|
|
|
17
17
|
y.stop();
|
|
18
18
|
scale.stop();
|
|
19
19
|
};
|
|
20
|
-
const Canvas = ({ children, homeCoordinates, navItems, skipIntro = false, introContent, loadingText, introBackgroundGradient, canvasBoxGradient, growTransition, blurTransition, canvasBackground, wrapperBackground, toolbarConfig, navbarConfig, canvasHeight, canvasWidth }) => {
|
|
20
|
+
const Canvas = ({ children, homeCoordinates, navItems, skipIntro = false, introContent, loadingText, introBackgroundGradient, canvasBoxGradient, growTransition, blurTransition, panTransition, fadeTransition, canvasBackground, wrapperBackground, toolbarConfig, navbarConfig, canvasHeight, canvasWidth }) => {
|
|
21
21
|
const { height: windowHeight, width: windowWidth } = useWindowDimensions_default();
|
|
22
22
|
const { mode } = usePerformanceMode();
|
|
23
23
|
const hasNavbar = Boolean(navItems && navItems.length > 0);
|
|
@@ -36,13 +36,10 @@ const Canvas = ({ children, homeCoordinates, navItems, skipIntro = false, introC
|
|
|
36
36
|
});
|
|
37
37
|
const [isResetting, setIsResetting] = useState(false);
|
|
38
38
|
const [maxZIndex, setMaxZIndex] = useState(50);
|
|
39
|
-
const [animationStage, setAnimationStage] = useState(0);
|
|
39
|
+
const [animationStage, setAnimationStage] = useState(skipIntro ? 2 : 0);
|
|
40
40
|
const [nextTargetSection, setNextTargetSection] = useState(null);
|
|
41
|
-
const isIntroAnimatingRef = useRef(
|
|
41
|
+
const isIntroAnimatingRef = useRef(!skipIntro);
|
|
42
42
|
const initialBoxWidth = useMemo(() => calcInitialBoxWidth(windowWidth, windowHeight), [windowWidth, windowHeight]);
|
|
43
|
-
const x = useMotionValue(0);
|
|
44
|
-
const y = useMotionValue(0);
|
|
45
|
-
const scale = useMotionValue(initialBoxWidth);
|
|
46
43
|
const offsetHomeCoordinates = useMemo(() => getSectionPanCoordinates({
|
|
47
44
|
windowDimensions: {
|
|
48
45
|
width: windowWidth,
|
|
@@ -55,6 +52,9 @@ const Canvas = ({ children, homeCoordinates, navItems, skipIntro = false, introC
|
|
|
55
52
|
windowWidth,
|
|
56
53
|
windowHeight
|
|
57
54
|
]);
|
|
55
|
+
const x = useMotionValue(skipIntro ? offsetHomeCoordinates.x : 0);
|
|
56
|
+
const y = useMotionValue(skipIntro ? offsetHomeCoordinates.y : 0);
|
|
57
|
+
const scale = useMotionValue(skipIntro ? 1 : initialBoxWidth);
|
|
58
58
|
const onResetViewAndItems = useCallback((onComplete) => {
|
|
59
59
|
setIsResetting(true);
|
|
60
60
|
panToOffsetScene(offsetHomeCoordinates, x, y, scale, 1).then(() => {
|
|
@@ -108,12 +108,27 @@ const Canvas = ({ children, homeCoordinates, navItems, skipIntro = false, introC
|
|
|
108
108
|
x,
|
|
109
109
|
y
|
|
110
110
|
]);
|
|
111
|
+
const effectivePanTransition = useMemo(() => {
|
|
112
|
+
if (!panTransition) return STAGE2_TRANSITION;
|
|
113
|
+
return {
|
|
114
|
+
...STAGE2_TRANSITION,
|
|
115
|
+
...panTransition
|
|
116
|
+
};
|
|
117
|
+
}, [panTransition]);
|
|
111
118
|
const startStage2 = useCallback(() => {
|
|
119
|
+
if (skipIntro) {
|
|
120
|
+
x.set(offsetHomeCoordinates.x);
|
|
121
|
+
y.set(offsetHomeCoordinates.y);
|
|
122
|
+
scale.set(1);
|
|
123
|
+
setAnimationStage(2);
|
|
124
|
+
isIntroAnimatingRef.current = false;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
112
127
|
setAnimationStage(1);
|
|
113
128
|
Promise.all([
|
|
114
|
-
animate(x, offsetHomeCoordinates.x,
|
|
115
|
-
animate(y, offsetHomeCoordinates.y,
|
|
116
|
-
animate(scale, 1,
|
|
129
|
+
animate(x, offsetHomeCoordinates.x, effectivePanTransition),
|
|
130
|
+
animate(y, offsetHomeCoordinates.y, effectivePanTransition),
|
|
131
|
+
animate(scale, 1, effectivePanTransition)
|
|
117
132
|
]).then(() => {
|
|
118
133
|
setAnimationStage(2);
|
|
119
134
|
isIntroAnimatingRef.current = false;
|
|
@@ -124,7 +139,9 @@ const Canvas = ({ children, homeCoordinates, navItems, skipIntro = false, introC
|
|
|
124
139
|
offsetHomeCoordinates,
|
|
125
140
|
x,
|
|
126
141
|
y,
|
|
127
|
-
scale
|
|
142
|
+
scale,
|
|
143
|
+
effectivePanTransition,
|
|
144
|
+
skipIntro
|
|
128
145
|
]);
|
|
129
146
|
const viewportRef = useRef(null);
|
|
130
147
|
const sceneRef = useRef(null);
|
|
@@ -422,10 +439,7 @@ const Canvas = ({ children, homeCoordinates, navItems, skipIntro = false, introC
|
|
|
422
439
|
className: "absolute z-20 origin-top-left",
|
|
423
440
|
initial: { opacity: 0 },
|
|
424
441
|
animate: { opacity: 1 },
|
|
425
|
-
transition:
|
|
426
|
-
duration: .3,
|
|
427
|
-
ease: "easeIn"
|
|
428
|
-
},
|
|
442
|
+
transition: fadeTransition ?? FADE_TRANSITION,
|
|
429
443
|
style: {
|
|
430
444
|
width: `${sceneWidth}px`,
|
|
431
445
|
height: `${sceneHeight}px`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.js","names":["useWindowDimensions","Toolbar"],"sources":["../../../src/components/canvas/canvas.tsx"],"sourcesContent":["import {\n motion,\n type MotionValue,\n type Point,\n useMotionValue,\n animate,\n useTransform,\n type Transition,\n} from \"framer-motion\";\nimport React, {\n useState,\n useRef,\n type PointerEvent,\n type FC,\n useEffect,\n useCallback,\n useMemo,\n} from \"react\";\nimport { CanvasProvider } from \"../../contexts/CanvasContext\";\nimport {\n calcInitialBoxWidth,\n canvasHeight,\n canvasWidth,\n getDistance,\n getMidpoint,\n getScreenSizeEnum,\n getSectionPanCoordinates,\n INTERACTIVE_SELECTOR,\n MAX_ZOOM,\n MIN_ZOOMS,\n panToOffsetScene,\n ZOOM_BOUND,\n} from \"../../lib/canvas\";\nimport {\n STAGE2_TRANSITION,\n MOUSE_WHEEL_ZOOM_SENSITIVITY,\n TRACKPAD_ZOOM_SENSITIVITY,\n DEFAULT_CANVAS_WIDTH,\n DEFAULT_CANVAS_HEIGHT,\n} from \"../../lib/constants\";\nimport useWindowDimensions from \"../../hooks/useWindowDimensions\";\nimport Navbar from \"./navbar\";\nimport Toolbar from \"./toolbar\";\nimport type {\n CanvasSection,\n NavItem,\n NavbarConfig,\n SectionCoordinates,\n ToolbarConfig,\n} from \"../../types\";\nimport { CanvasWrapper } from \"./wrapper\";\nimport { usePerformanceMode } from \"../../hooks/usePerformanceMode\";\nimport type { ReactNode } from \"react\";\nimport { DefaultCanvasBackground } from \"./backgrounds\";\n\ninterface Props {\n homeCoordinates: SectionCoordinates;\n children: React.ReactNode;\n\n // Optional height and with params, if omitted sizing will be 6000x4000\n canvasWidth?:number;\n canvasHeight?:number;\n\n // Navbar data (optional). If omitted, navbar is hidden.\n /** Array of navigation items for the navbar. If omitted, navbar is hidden. */\n navItems?: NavItem[];\n\n // ============== Intro Animation Customization ==============\n /** Disable intro animation entirely */\n skipIntro?: boolean;\n /** Custom intro content during loading */\n introContent?: ReactNode;\n /** Custom loading text */\n loadingText?: string;\n /** Background gradient for intro */\n introBackgroundGradient?: string;\n /** Canvas box gradient */\n canvasBoxGradient?: string;\n /** Custom grow transition */\n growTransition?: Transition;\n /** Custom blur transition */\n blurTransition?: Transition;\n\n // ============== Background Customization ==============\n /** Custom canvas background. If not provided, uses DefaultCanvasBackground. */\n canvasBackground?: ReactNode;\n /** Custom wrapper/intro background. If not provided, uses introBackgroundGradient. */\n wrapperBackground?: ReactNode;\n\n // ============== Toolbar Customization ==============\n /** Toolbar customization options */\n toolbarConfig?: ToolbarConfig;\n\n // ============== Navbar Customization ==============\n /** Navbar customization options */\n navbarConfig?: NavbarConfig;\n}\n\nconst stopAllMotion = (\n x: MotionValue<number>,\n y: MotionValue<number>,\n scale: MotionValue<number>\n) => {\n x.stop();\n y.stop();\n scale.stop();\n};\n\nconst Canvas: FC<Props> = ({\n children,\n homeCoordinates,\n navItems,\n skipIntro = false,\n introContent,\n loadingText,\n introBackgroundGradient,\n canvasBoxGradient,\n growTransition,\n blurTransition,\n canvasBackground,\n wrapperBackground,\n toolbarConfig,\n navbarConfig,\n canvasHeight,\n canvasWidth,\n}) => {\n const { height: windowHeight, width: windowWidth } = useWindowDimensions();\n\n const { mode } = usePerformanceMode();\n\n const hasNavbar = Boolean(navItems && navItems.length > 0);\n\n const sceneWidth = canvasWidth ?? DEFAULT_CANVAS_WIDTH;\n const sceneHeight = canvasHeight ?? DEFAULT_CANVAS_HEIGHT;\n\n const MIN_ZOOM = MIN_ZOOMS[getScreenSizeEnum(windowWidth)];\n\n // tracks if user is panning the screen\n const [isPanning, setIsPanning] = useState<boolean>(false);\n // this one is moving from scene control, not from user\n const [isSceneMoving, setIsSceneMoving] = useState<boolean>(false);\n const [panStartPoint, setPanStartPoint] = useState<Point>({ x: 0, y: 0 });\n const [initialPanOffsetOnDrag, setInitialPanOffsetOnDrag] = useState<Point>({\n x: 0,\n y: 0,\n });\n const [isResetting, setIsResetting] = useState<boolean>(false);\n const [maxZIndex, setMaxZIndex] = useState<number>(50);\n const [animationStage, setAnimationStage] = useState<number>(0); // 0: initial, 1: finish grow, 2: pan to home\n const [nextTargetSection, setNextTargetSection] =\n useState<CanvasSection | null>(null);\n // Track if the intro (stage1 + stage2) is still running, to avoid accidental cancellation\n const isIntroAnimatingRef = useRef(true);\n\n const initialBoxWidth = useMemo(\n () => calcInitialBoxWidth(windowWidth, windowHeight),\n [windowWidth, windowHeight]\n );\n\n // somewhere near the middle-ish\n const x = useMotionValue(0);\n const y = useMotionValue(0);\n const scale = useMotionValue(initialBoxWidth);\n\n const offsetHomeCoordinates = useMemo(\n () =>\n getSectionPanCoordinates({\n windowDimensions: { width: windowWidth, height: windowHeight },\n coords: homeCoordinates,\n targetZoom: 1,\n }),\n [homeCoordinates, windowWidth, windowHeight]\n );\n\n const onResetViewAndItems = useCallback(\n (onComplete?: () => void): void => {\n setIsResetting(true);\n\n void panToOffsetScene(offsetHomeCoordinates, x, y, scale, 1).then(() => {\n setIsResetting(false);\n if (onComplete) onComplete();\n });\n },\n [offsetHomeCoordinates, x, y, scale]\n );\n\n // Shared intro progress (0->1) driven by CanvasWrapper\n const introProgress = useMotionValue(0);\n\n // Precompute final stage1 scale and offsets (snapshot dimensions once on mount)\n const stage1Targets = useMemo(() => {\n const finalScale = Math.max(\n (windowWidth || 0) / sceneWidth,\n (windowHeight || 0) / sceneHeight\n );\n const endX = (windowWidth - sceneWidth * finalScale) / 2;\n const endY = (windowHeight - sceneHeight * finalScale) / 2;\n return { finalScale, endX, endY };\n }, [windowWidth, windowHeight, sceneWidth, sceneHeight]);\n\n // Replace direct motion values with derived transforms during stage1\n const derivedScale = useTransform(\n introProgress,\n [0, 1],\n [initialBoxWidth, stage1Targets.finalScale]\n );\n const derivedX = useTransform(introProgress, [0, 1], [0, stage1Targets.endX]);\n const derivedY = useTransform(introProgress, [0, 1], [0, stage1Targets.endY]);\n\n // While intro (stage1) is running, bind x/y/scale to derived versions.\n useEffect(() => {\n const unsubscribeScale = derivedScale.on(\"change\", (v) => {\n if (animationStage === 0) scale.set(v);\n });\n const unsubscribeX = derivedX.on(\"change\", (v) => {\n if (animationStage === 0) x.set(v);\n });\n const unsubscribeY = derivedY.on(\"change\", (v) => {\n if (animationStage === 0) y.set(v);\n });\n return () => {\n unsubscribeScale();\n unsubscribeX();\n unsubscribeY();\n };\n }, [derivedScale, derivedX, derivedY, animationStage, scale, x, y]);\n\n // Kick off stage2 (pan to home) when grow completes (introProgress hits 1)\n const startStage2 = useCallback(() => {\n setAnimationStage(1);\n\n Promise.all([\n animate(x, offsetHomeCoordinates.x, STAGE2_TRANSITION),\n animate(y, offsetHomeCoordinates.y, STAGE2_TRANSITION),\n animate(scale, 1, STAGE2_TRANSITION),\n ])\n .then(() => {\n setAnimationStage(2);\n isIntroAnimatingRef.current = false;\n })\n .catch(() => {\n isIntroAnimatingRef.current = false;\n });\n }, [offsetHomeCoordinates, x, y, scale]);\n\n const viewportRef = useRef<HTMLDivElement>(null);\n const sceneRef = useRef<HTMLDivElement>(null);\n\n // Stable wheel listener wrapper that always calls the latest handler via ref\n const wheelHandlerRef = useRef<((e: WheelEvent) => void) | null>(null);\n const wheelWrapper = useCallback((e: WheelEvent) => {\n wheelHandlerRef.current?.(e);\n }, []);\n\n // Ensure wheel listener attaches when the element actually mounts (wrapper delays child mount)\n const setViewportRef = useCallback(\n (node: HTMLDivElement | null) => {\n // Clean up old listener if ref changes/unmounts\n if (viewportRef.current) {\n viewportRef.current.removeEventListener(\"wheel\", wheelWrapper);\n }\n viewportRef.current = node;\n if (node) {\n node.addEventListener(\"wheel\", wheelWrapper, { passive: false });\n }\n },\n [wheelWrapper]\n );\n\n const activePointersRef = useRef<Map<number, PointerEvent<HTMLDivElement>>>(\n new Map()\n );\n const initialPinchStateRef = useRef<{\n distance: number;\n midpoint: Point;\n zoom: number;\n panOffset: Point;\n } | null>(null);\n\n const panToOffset = useCallback(\n (\n offset: Point,\n viewportRef: React.RefObject<HTMLDivElement | null>,\n onComplete?: () => void,\n zoom?: number\n ): void => {\n if (!viewportRef.current) return;\n setIsSceneMoving(true);\n\n // Calculate bounds based on scene and viewport dimensions\n const viewportWidth = viewportRef.current.offsetWidth;\n const viewportHeight = viewportRef.current.offsetHeight;\n\n const minPanX = viewportWidth - sceneWidth * (zoom ?? 1);\n const maxPanX = 0;\n const minPanY = viewportHeight - sceneHeight * (zoom ?? 1);\n const maxPanY = 0;\n\n // Clamp the offset to keep the scene within bounds, shouldn't be needed but still implemented\n const clampedX = Math.min(Math.max(offset.x, minPanX), maxPanX);\n const clampedY = Math.min(Math.max(offset.y, minPanY), maxPanY);\n\n void panToOffsetScene(\n { x: clampedX, y: clampedY },\n x,\n y,\n scale,\n zoom\n ).then(() => {\n setIsSceneMoving(false);\n if (onComplete) onComplete();\n });\n },\n [sceneWidth, sceneHeight, x, y, scale]\n );\n\n // Guarded stop that ignores attempts during intro animations\n const stopAllSceneMotion = useCallback(() => {\n if (isIntroAnimatingRef.current) return; // ignore stops while intro runs\n stopAllMotion(x, y, scale);\n }, [x, y, scale]);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>): void => {\n if (animationStage < 2) return; // ignore during intro animations\n activePointersRef.current.set(event.pointerId, event);\n (event.target as HTMLElement).setPointerCapture(event.pointerId);\n if (isResetting || isSceneMoving) return;\n stopAllSceneMotion();\n // pan with 1 pointer, pinch with 2 pointers\n if (activePointersRef.current.size === 1) {\n // do not pan from interactive elements\n const targetElement = event.target as HTMLElement;\n if (targetElement.closest(INTERACTIVE_SELECTOR)) {\n activePointersRef.current.delete(event.pointerId);\n (event.target as HTMLElement).releasePointerCapture(event.pointerId);\n return;\n }\n\n setIsPanning(true);\n setPanStartPoint({ x: event.clientX, y: event.clientY });\n setInitialPanOffsetOnDrag({ x: x.get(), y: y.get() });\n if (viewportRef.current) viewportRef.current.style.cursor = \"grabbing\";\n } else if (activePointersRef.current.size === 2) {\n setIsPanning(false);\n const pointers = Array.from(activePointersRef.current.values());\n initialPinchStateRef.current = {\n distance: getDistance(pointers[0]!, pointers[1]!),\n midpoint: getMidpoint(pointers[0]!, pointers[1]!),\n zoom: scale.get(),\n panOffset: { x: x.get(), y: y.get() },\n };\n }\n },\n [\n isResetting,\n isSceneMoving,\n setIsPanning,\n setPanStartPoint,\n setInitialPanOffsetOnDrag,\n x,\n y,\n scale,\n viewportRef,\n animationStage,\n stopAllSceneMotion,\n ]\n );\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLDivElement>): void => {\n if (animationStage < 2) return;\n if (isPanning || activePointersRef.current.size >= 2) {\n stopAllSceneMotion();\n }\n if (!activePointersRef.current.has(event.pointerId) || isResetting)\n return;\n activePointersRef.current.set(event.pointerId, event);\n\n if (isPanning && activePointersRef.current.size === 1) {\n event.preventDefault();\n const deltaX = event.clientX - panStartPoint.x;\n const deltaY = event.clientY - panStartPoint.y;\n\n // UPDATE to use motion value\n const minPanX = windowWidth - sceneWidth * scale.get();\n const maxPanX = 0;\n const minPanY = windowHeight - sceneHeight * scale.get();\n const maxPanY = 0;\n\n const newX = Math.min(\n Math.max(initialPanOffsetOnDrag.x + deltaX, minPanX),\n maxPanX\n );\n const newY = Math.min(\n Math.max(initialPanOffsetOnDrag.y + deltaY, minPanY),\n maxPanY\n );\n x.set(newX);\n y.set(newY);\n } else if (\n activePointersRef.current.size >= 2 &&\n initialPinchStateRef.current\n ) {\n event.preventDefault();\n const pointers = Array.from(activePointersRef.current.values());\n const p1 = pointers[0]!;\n const p2 = pointers[1]!;\n\n const currentDistance = getDistance(p1, p2);\n const currentMidpoint = getMidpoint(p1, p2);\n\n const {\n distance: initialDistance,\n zoom: initialZoom,\n panOffset: initialPanOffsetPinch,\n } = initialPinchStateRef.current;\n\n if (initialDistance === 0) return;\n\n let newZoom = initialZoom * (currentDistance / initialDistance);\n newZoom = Math.max(\n (window.innerWidth / sceneWidth) * ZOOM_BOUND, // Ensure zoom is at least the width of the canvas\n (window.innerHeight / sceneHeight) * ZOOM_BOUND, // Ensure zoom is at least the height of the canvas\n Math.min(newZoom, 10),\n MIN_ZOOM // Ensure zoom is not less than MIN_ZOOM\n );\n\n const mx = currentMidpoint.x;\n const my = currentMidpoint.y;\n\n const minPanX = windowWidth - sceneWidth * newZoom;\n const maxPanX = 0;\n const minPanY = windowHeight - sceneHeight * newZoom;\n const maxPanY = 0;\n\n let newPanX =\n mx - ((mx - initialPanOffsetPinch.x) / initialZoom) * newZoom;\n let newPanY =\n my - ((my - initialPanOffsetPinch.y) / initialZoom) * newZoom;\n\n // Clamp pan to prevent leaving bounds\n newPanX = Math.min(Math.max(newPanX, minPanX), maxPanX);\n newPanY = Math.min(Math.max(newPanY, minPanY), maxPanY);\n\n scale.set(newZoom);\n x.set(newPanX);\n y.set(newPanY);\n }\n },\n [\n isPanning,\n isResetting,\n x,\n y,\n scale,\n panStartPoint.x,\n panStartPoint.y,\n windowWidth,\n sceneWidth,\n windowHeight,\n sceneHeight,\n initialPanOffsetOnDrag.x,\n initialPanOffsetOnDrag.y,\n MIN_ZOOM,\n animationStage,\n stopAllSceneMotion,\n ]\n );\n\n const handlePointerUpOrCancel = useCallback(\n (event: PointerEvent<HTMLDivElement>): void => {\n if (animationStage < 2) {\n event.preventDefault();\n return; // ignore pointer up during intro\n }\n stopAllSceneMotion();\n event.preventDefault();\n if ((event.target as HTMLElement).hasPointerCapture(event.pointerId)) {\n (event.target as HTMLElement).releasePointerCapture(event.pointerId);\n }\n activePointersRef.current.delete(event.pointerId);\n\n if (isPanning && activePointersRef.current.size < 1) {\n setIsPanning(false);\n if (viewportRef.current)\n viewportRef.current.style.cursor = \"url('/customcursor.svg'), grab\";\n }\n\n if (initialPinchStateRef.current && activePointersRef.current.size < 2) {\n initialPinchStateRef.current = null;\n }\n\n if (\n !isPanning &&\n activePointersRef.current.size === 1 &&\n !initialPinchStateRef.current\n ) {\n const lastPointer = Array.from(activePointersRef.current.values())[0]!;\n setIsPanning(true);\n setPanStartPoint({ x: lastPointer.clientX, y: lastPointer.clientY });\n setInitialPanOffsetOnDrag({ x: x.get(), y: y.get() });\n }\n },\n [x, y, isPanning, animationStage, stopAllSceneMotion]\n );\n\n const handleWheelZoom = useCallback(\n (event: WheelEvent) => {\n if (animationStage < 2) {\n event.preventDefault();\n return; // block wheel interaction during intro animations\n }\n event.preventDefault();\n // pinch gesture on track\n const isPinch = event.ctrlKey || event.metaKey;\n const isMouseWheelZoom =\n event.deltaMode === WheelEvent.DOM_DELTA_LINE ||\n Math.abs(event.deltaY) >= 100;\n\n // mouse wheel zoom and track pad zoom have different sensitivities\n const ZOOM_SENSITIVITY = isMouseWheelZoom\n ? MOUSE_WHEEL_ZOOM_SENSITIVITY\n : TRACKPAD_ZOOM_SENSITIVITY;\n\n if (isPinch) {\n const currentZoom = scale.get();\n const nextZoom = Math.max(\n Math.min(\n currentZoom * (1 - event.deltaY * ZOOM_SENSITIVITY),\n MAX_ZOOM\n ),\n MIN_ZOOM,\n (window.innerWidth / sceneWidth) * ZOOM_BOUND, // Ensure zoom is at least the width of the canvas\n (window.innerHeight / sceneHeight) * ZOOM_BOUND // Ensure zoom is at least the height of the canvas\n );\n\n const rect = viewportRef.current?.getBoundingClientRect();\n\n if (!rect) return;\n\n const vpLeft = rect.left;\n const vpTop = rect.top;\n const viewportWidth = rect.width;\n const viewportHeight = rect.height;\n\n const cursorSceneX = (event.clientX - vpLeft - x.get()) / currentZoom;\n const cursorSceneY = (event.clientY - vpTop - y.get()) / currentZoom;\n\n let newPanX = event.clientX - vpLeft - cursorSceneX * nextZoom;\n let newPanY = event.clientY - vpTop - cursorSceneY * nextZoom;\n\n const minPanX = viewportWidth - sceneWidth * nextZoom;\n const minPanY = viewportHeight - sceneHeight * nextZoom;\n const maxPanX = 0;\n const maxPanY = 0;\n\n newPanX = Math.min(maxPanX, Math.max(minPanX, newPanX));\n newPanY = Math.min(maxPanY, Math.max(minPanY, newPanY));\n\n x.set(newPanX);\n y.set(newPanY);\n scale.set(nextZoom);\n } else {\n stopAllSceneMotion();\n\n const scrollSpeed = 1;\n const newPanX = x.get() - event.deltaX * scrollSpeed;\n const newPanY = y.get() - event.deltaY * scrollSpeed;\n\n const minPanX = windowWidth - sceneWidth * scale.get();\n const maxPanX = 0;\n const minPanY = windowHeight - sceneHeight * scale.get();\n const maxPanY = 0;\n\n const clampedPanX = Math.min(Math.max(newPanX, minPanX), maxPanX);\n const clampedPanY = Math.min(Math.max(newPanY, minPanY), maxPanY);\n\n x.set(clampedPanX);\n y.set(clampedPanY);\n }\n },\n [\n scale,\n MIN_ZOOM,\n x,\n y,\n sceneWidth,\n sceneHeight,\n windowWidth,\n windowHeight,\n animationStage,\n stopAllSceneMotion,\n ]\n );\n\n // Keep the wheel handler ref pointing to the latest implementation\n useEffect(() => {\n wheelHandlerRef.current = handleWheelZoom;\n }, [handleWheelZoom]);\n\n const handlePanToOffset = useCallback(\n (\n offset: { x: number; y: number },\n onComplete?: () => void,\n zoom?: number\n ) => {\n panToOffset(\n {\n x: -offset.x,\n y: -offset.y,\n },\n viewportRef,\n onComplete,\n zoom\n );\n },\n [panToOffset, viewportRef]\n );\n\n return (\n <CanvasWrapper\n introProgress={introProgress}\n onIntroGrowComplete={startStage2}\n skipIntro={skipIntro}\n introContent={introContent}\n loadingText={loadingText}\n introBackgroundGradient={introBackgroundGradient}\n wrapperBackground={wrapperBackground}\n canvasBoxGradient={canvasBoxGradient}\n growTransition={growTransition}\n blurTransition={blurTransition}\n >\n <CanvasProvider\n x={x}\n y={y}\n scale={scale}\n isResetting={isResetting}\n maxZIndex={maxZIndex}\n setMaxZIndex={setMaxZIndex}\n animationStage={animationStage}\n nextTargetSection={nextTargetSection}\n setNextTargetSection={setNextTargetSection}\n >\n {animationStage >= 2 && (\n <>\n {!toolbarConfig?.hidden && (\n <Toolbar\n homeCoordinates={offsetHomeCoordinates}\n config={toolbarConfig}\n />\n )}\n {hasNavbar && navItems && !navbarConfig?.hidden && (\n <Navbar\n panToOffset={handlePanToOffset}\n onReset={onResetViewAndItems}\n items={navItems}\n config={navbarConfig}\n />\n )}\n </>\n )}\n <div\n ref={setViewportRef}\n className=\"relative h-full w-full touch-none select-none overflow-hidden\"\n style={{\n touchAction: \"none\",\n pointerEvents: animationStage >= 2 ? \"auto\" : \"none\",\n overscrollBehavior: \"contain\",\n }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUpOrCancel}\n onPointerLeave={handlePointerUpOrCancel}\n onPointerCancel={handlePointerUpOrCancel}\n >\n <motion.div\n ref={sceneRef}\n className=\"absolute z-20 origin-top-left\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ duration: 0.3, ease: \"easeIn\" }}\n style={{\n width: `${sceneWidth}px`,\n height: `${sceneHeight}px`,\n x,\n y,\n scale,\n willChange:\n mode !== \"high\" && (animationStage < 2 || isPanning)\n ? \"transform\"\n : \"auto\",\n }}\n >\n {/* Canvas Background - customizable or default */}\n {canvasBackground !== undefined ? (\n canvasBackground\n ) : (\n <>\n {animationStage >= 1 && mode === \"high\" ? (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ duration: 0.5, ease: \"easeIn\" }}\n >\n <DefaultCanvasBackground />\n </motion.div>\n ) : (\n <DefaultCanvasBackground />\n )}\n </>\n )}\n {children}\n </motion.div>\n </div>\n </CanvasProvider>\n </CanvasWrapper>\n );\n};\n\nexport default Canvas;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"canvas.js","names":["useWindowDimensions","Toolbar"],"sources":["../../../src/components/canvas/canvas.tsx"],"sourcesContent":["import {\n motion,\n type MotionValue,\n type Point,\n useMotionValue,\n animate,\n useTransform,\n type Transition,\n} from \"framer-motion\";\nimport React, {\n useState,\n useRef,\n type PointerEvent,\n type FC,\n useEffect,\n useCallback,\n useMemo,\n} from \"react\";\nimport { CanvasProvider } from \"../../contexts/CanvasContext\";\nimport {\n calcInitialBoxWidth,\n canvasHeight,\n canvasWidth,\n getDistance,\n getMidpoint,\n getScreenSizeEnum,\n getSectionPanCoordinates,\n INTERACTIVE_SELECTOR,\n MAX_ZOOM,\n MIN_ZOOMS,\n panToOffsetScene,\n ZOOM_BOUND,\n} from \"../../lib/canvas\";\nimport {\n STAGE2_TRANSITION,\n FADE_TRANSITION,\n MOUSE_WHEEL_ZOOM_SENSITIVITY,\n TRACKPAD_ZOOM_SENSITIVITY,\n DEFAULT_CANVAS_WIDTH,\n DEFAULT_CANVAS_HEIGHT,\n} from \"../../lib/constants\";\nimport useWindowDimensions from \"../../hooks/useWindowDimensions\";\nimport Navbar from \"./navbar\";\nimport Toolbar from \"./toolbar\";\nimport type {\n CanvasSection,\n NavItem,\n NavbarConfig,\n SectionCoordinates,\n ToolbarConfig,\n} from \"../../types\";\nimport { CanvasWrapper } from \"./wrapper\";\nimport { usePerformanceMode } from \"../../hooks/usePerformanceMode\";\nimport type { ReactNode } from \"react\";\nimport { DefaultCanvasBackground } from \"./backgrounds\";\n\ninterface Props {\n homeCoordinates: SectionCoordinates;\n children: React.ReactNode;\n\n // Optional height and with params, if omitted sizing will be 6000x4000\n canvasWidth?:number;\n canvasHeight?:number;\n\n // Navbar data (optional). If omitted, navbar is hidden.\n /** Array of navigation items for the navbar. If omitted, navbar is hidden. */\n navItems?: NavItem[];\n\n // ============== Intro Animation Customization ==============\n /** Disable intro animation entirely */\n skipIntro?: boolean;\n /** Custom intro content during loading */\n introContent?: ReactNode;\n /** Custom loading text */\n loadingText?: string;\n /** Background gradient for intro */\n introBackgroundGradient?: string;\n /** Canvas box gradient */\n canvasBoxGradient?: string;\n /** Custom grow transition */\n growTransition?: Transition;\n /** Custom blur transition */\n blurTransition?: Transition;\n /** Custom pan-to-home transition (stage 2) */\n panTransition?: Transition;\n /** Custom fade-in transition for the canvas scene */\n fadeTransition?: Transition;\n\n // ============== Background Customization ==============\n /** Custom canvas background. If not provided, uses DefaultCanvasBackground. */\n canvasBackground?: ReactNode;\n /** Custom wrapper/intro background. If not provided, uses introBackgroundGradient. */\n wrapperBackground?: ReactNode;\n\n // ============== Toolbar Customization ==============\n /** Toolbar customization options */\n toolbarConfig?: ToolbarConfig;\n\n // ============== Navbar Customization ==============\n /** Navbar customization options */\n navbarConfig?: NavbarConfig;\n}\n\nconst stopAllMotion = (\n x: MotionValue<number>,\n y: MotionValue<number>,\n scale: MotionValue<number>\n) => {\n x.stop();\n y.stop();\n scale.stop();\n};\n\nconst Canvas: FC<Props> = ({\n children,\n homeCoordinates,\n navItems,\n skipIntro = false,\n introContent,\n loadingText,\n introBackgroundGradient,\n canvasBoxGradient,\n growTransition,\n blurTransition,\n panTransition,\n fadeTransition,\n canvasBackground,\n wrapperBackground,\n toolbarConfig,\n navbarConfig,\n canvasHeight,\n canvasWidth,\n}) => {\n const { height: windowHeight, width: windowWidth } = useWindowDimensions();\n\n const { mode } = usePerformanceMode();\n\n const hasNavbar = Boolean(navItems && navItems.length > 0);\n\n const sceneWidth = canvasWidth ?? DEFAULT_CANVAS_WIDTH;\n const sceneHeight = canvasHeight ?? DEFAULT_CANVAS_HEIGHT;\n\n const MIN_ZOOM = MIN_ZOOMS[getScreenSizeEnum(windowWidth)];\n\n // tracks if user is panning the screen\n const [isPanning, setIsPanning] = useState<boolean>(false);\n // this one is moving from scene control, not from user\n const [isSceneMoving, setIsSceneMoving] = useState<boolean>(false);\n const [panStartPoint, setPanStartPoint] = useState<Point>({ x: 0, y: 0 });\n const [initialPanOffsetOnDrag, setInitialPanOffsetOnDrag] = useState<Point>({\n x: 0,\n y: 0,\n });\n const [isResetting, setIsResetting] = useState<boolean>(false);\n const [maxZIndex, setMaxZIndex] = useState<number>(50);\n const [animationStage, setAnimationStage] = useState<number>(\n skipIntro ? 2 : 0\n ); // 0: initial, 1: finish grow, 2: pan to home\n const [nextTargetSection, setNextTargetSection] =\n useState<CanvasSection | null>(null);\n // Track if the intro (stage1 + stage2) is still running, to avoid accidental cancellation\n const isIntroAnimatingRef = useRef(!skipIntro);\n\n const initialBoxWidth = useMemo(\n () => calcInitialBoxWidth(windowWidth, windowHeight),\n [windowWidth, windowHeight]\n );\n\n const offsetHomeCoordinates = useMemo(\n () =>\n getSectionPanCoordinates({\n windowDimensions: { width: windowWidth, height: windowHeight },\n coords: homeCoordinates,\n targetZoom: 1,\n }),\n [homeCoordinates, windowWidth, windowHeight]\n );\n\n // When skipIntro, initialize at home position; otherwise start at origin for intro animation\n const x = useMotionValue(skipIntro ? offsetHomeCoordinates.x : 0);\n const y = useMotionValue(skipIntro ? offsetHomeCoordinates.y : 0);\n const scale = useMotionValue(skipIntro ? 1 : initialBoxWidth);\n\n const onResetViewAndItems = useCallback(\n (onComplete?: () => void): void => {\n setIsResetting(true);\n\n void panToOffsetScene(offsetHomeCoordinates, x, y, scale, 1).then(() => {\n setIsResetting(false);\n if (onComplete) onComplete();\n });\n },\n [offsetHomeCoordinates, x, y, scale]\n );\n\n // Shared intro progress (0->1) driven by CanvasWrapper\n const introProgress = useMotionValue(0);\n\n // Precompute final stage1 scale and offsets (snapshot dimensions once on mount)\n const stage1Targets = useMemo(() => {\n const finalScale = Math.max(\n (windowWidth || 0) / sceneWidth,\n (windowHeight || 0) / sceneHeight\n );\n const endX = (windowWidth - sceneWidth * finalScale) / 2;\n const endY = (windowHeight - sceneHeight * finalScale) / 2;\n return { finalScale, endX, endY };\n }, [windowWidth, windowHeight, sceneWidth, sceneHeight]);\n\n // Replace direct motion values with derived transforms during stage1\n const derivedScale = useTransform(\n introProgress,\n [0, 1],\n [initialBoxWidth, stage1Targets.finalScale]\n );\n const derivedX = useTransform(introProgress, [0, 1], [0, stage1Targets.endX]);\n const derivedY = useTransform(introProgress, [0, 1], [0, stage1Targets.endY]);\n\n // While intro (stage1) is running, bind x/y/scale to derived versions.\n useEffect(() => {\n const unsubscribeScale = derivedScale.on(\"change\", (v) => {\n if (animationStage === 0) scale.set(v);\n });\n const unsubscribeX = derivedX.on(\"change\", (v) => {\n if (animationStage === 0) x.set(v);\n });\n const unsubscribeY = derivedY.on(\"change\", (v) => {\n if (animationStage === 0) y.set(v);\n });\n return () => {\n unsubscribeScale();\n unsubscribeX();\n unsubscribeY();\n };\n }, [derivedScale, derivedX, derivedY, animationStage, scale, x, y]);\n\n // Merge custom panTransition with defaults\n const effectivePanTransition: Transition = useMemo(() => {\n if (!panTransition) return STAGE2_TRANSITION;\n return {\n ...STAGE2_TRANSITION,\n ...panTransition,\n };\n }, [panTransition]);\n\n // Kick off stage2 (pan to home) when grow completes (introProgress hits 1)\n const startStage2 = useCallback(() => {\n if (skipIntro) {\n x.set(offsetHomeCoordinates.x);\n y.set(offsetHomeCoordinates.y);\n scale.set(1);\n setAnimationStage(2);\n isIntroAnimatingRef.current = false;\n return;\n }\n\n setAnimationStage(1);\n\n Promise.all([\n animate(x, offsetHomeCoordinates.x, effectivePanTransition),\n animate(y, offsetHomeCoordinates.y, effectivePanTransition),\n animate(scale, 1, effectivePanTransition),\n ])\n .then(() => {\n setAnimationStage(2);\n isIntroAnimatingRef.current = false;\n })\n .catch(() => {\n isIntroAnimatingRef.current = false;\n });\n }, [offsetHomeCoordinates, x, y, scale, effectivePanTransition, skipIntro]);\n\n const viewportRef = useRef<HTMLDivElement>(null);\n const sceneRef = useRef<HTMLDivElement>(null);\n\n // Stable wheel listener wrapper that always calls the latest handler via ref\n const wheelHandlerRef = useRef<((e: WheelEvent) => void) | null>(null);\n const wheelWrapper = useCallback((e: WheelEvent) => {\n wheelHandlerRef.current?.(e);\n }, []);\n\n // Ensure wheel listener attaches when the element actually mounts (wrapper delays child mount)\n const setViewportRef = useCallback(\n (node: HTMLDivElement | null) => {\n // Clean up old listener if ref changes/unmounts\n if (viewportRef.current) {\n viewportRef.current.removeEventListener(\"wheel\", wheelWrapper);\n }\n viewportRef.current = node;\n if (node) {\n node.addEventListener(\"wheel\", wheelWrapper, { passive: false });\n }\n },\n [wheelWrapper]\n );\n\n const activePointersRef = useRef<Map<number, PointerEvent<HTMLDivElement>>>(\n new Map()\n );\n const initialPinchStateRef = useRef<{\n distance: number;\n midpoint: Point;\n zoom: number;\n panOffset: Point;\n } | null>(null);\n\n const panToOffset = useCallback(\n (\n offset: Point,\n viewportRef: React.RefObject<HTMLDivElement | null>,\n onComplete?: () => void,\n zoom?: number\n ): void => {\n if (!viewportRef.current) return;\n setIsSceneMoving(true);\n\n // Calculate bounds based on scene and viewport dimensions\n const viewportWidth = viewportRef.current.offsetWidth;\n const viewportHeight = viewportRef.current.offsetHeight;\n\n const minPanX = viewportWidth - sceneWidth * (zoom ?? 1);\n const maxPanX = 0;\n const minPanY = viewportHeight - sceneHeight * (zoom ?? 1);\n const maxPanY = 0;\n\n // Clamp the offset to keep the scene within bounds, shouldn't be needed but still implemented\n const clampedX = Math.min(Math.max(offset.x, minPanX), maxPanX);\n const clampedY = Math.min(Math.max(offset.y, minPanY), maxPanY);\n\n void panToOffsetScene(\n { x: clampedX, y: clampedY },\n x,\n y,\n scale,\n zoom\n ).then(() => {\n setIsSceneMoving(false);\n if (onComplete) onComplete();\n });\n },\n [sceneWidth, sceneHeight, x, y, scale]\n );\n\n // Guarded stop that ignores attempts during intro animations\n const stopAllSceneMotion = useCallback(() => {\n if (isIntroAnimatingRef.current) return; // ignore stops while intro runs\n stopAllMotion(x, y, scale);\n }, [x, y, scale]);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>): void => {\n if (animationStage < 2) return; // ignore during intro animations\n activePointersRef.current.set(event.pointerId, event);\n (event.target as HTMLElement).setPointerCapture(event.pointerId);\n if (isResetting || isSceneMoving) return;\n stopAllSceneMotion();\n // pan with 1 pointer, pinch with 2 pointers\n if (activePointersRef.current.size === 1) {\n // do not pan from interactive elements\n const targetElement = event.target as HTMLElement;\n if (targetElement.closest(INTERACTIVE_SELECTOR)) {\n activePointersRef.current.delete(event.pointerId);\n (event.target as HTMLElement).releasePointerCapture(event.pointerId);\n return;\n }\n\n setIsPanning(true);\n setPanStartPoint({ x: event.clientX, y: event.clientY });\n setInitialPanOffsetOnDrag({ x: x.get(), y: y.get() });\n if (viewportRef.current) viewportRef.current.style.cursor = \"grabbing\";\n } else if (activePointersRef.current.size === 2) {\n setIsPanning(false);\n const pointers = Array.from(activePointersRef.current.values());\n initialPinchStateRef.current = {\n distance: getDistance(pointers[0]!, pointers[1]!),\n midpoint: getMidpoint(pointers[0]!, pointers[1]!),\n zoom: scale.get(),\n panOffset: { x: x.get(), y: y.get() },\n };\n }\n },\n [\n isResetting,\n isSceneMoving,\n setIsPanning,\n setPanStartPoint,\n setInitialPanOffsetOnDrag,\n x,\n y,\n scale,\n viewportRef,\n animationStage,\n stopAllSceneMotion,\n ]\n );\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLDivElement>): void => {\n if (animationStage < 2) return;\n if (isPanning || activePointersRef.current.size >= 2) {\n stopAllSceneMotion();\n }\n if (!activePointersRef.current.has(event.pointerId) || isResetting)\n return;\n activePointersRef.current.set(event.pointerId, event);\n\n if (isPanning && activePointersRef.current.size === 1) {\n event.preventDefault();\n const deltaX = event.clientX - panStartPoint.x;\n const deltaY = event.clientY - panStartPoint.y;\n\n // UPDATE to use motion value\n const minPanX = windowWidth - sceneWidth * scale.get();\n const maxPanX = 0;\n const minPanY = windowHeight - sceneHeight * scale.get();\n const maxPanY = 0;\n\n const newX = Math.min(\n Math.max(initialPanOffsetOnDrag.x + deltaX, minPanX),\n maxPanX\n );\n const newY = Math.min(\n Math.max(initialPanOffsetOnDrag.y + deltaY, minPanY),\n maxPanY\n );\n x.set(newX);\n y.set(newY);\n } else if (\n activePointersRef.current.size >= 2 &&\n initialPinchStateRef.current\n ) {\n event.preventDefault();\n const pointers = Array.from(activePointersRef.current.values());\n const p1 = pointers[0]!;\n const p2 = pointers[1]!;\n\n const currentDistance = getDistance(p1, p2);\n const currentMidpoint = getMidpoint(p1, p2);\n\n const {\n distance: initialDistance,\n zoom: initialZoom,\n panOffset: initialPanOffsetPinch,\n } = initialPinchStateRef.current;\n\n if (initialDistance === 0) return;\n\n let newZoom = initialZoom * (currentDistance / initialDistance);\n newZoom = Math.max(\n (window.innerWidth / sceneWidth) * ZOOM_BOUND, // Ensure zoom is at least the width of the canvas\n (window.innerHeight / sceneHeight) * ZOOM_BOUND, // Ensure zoom is at least the height of the canvas\n Math.min(newZoom, 10),\n MIN_ZOOM // Ensure zoom is not less than MIN_ZOOM\n );\n\n const mx = currentMidpoint.x;\n const my = currentMidpoint.y;\n\n const minPanX = windowWidth - sceneWidth * newZoom;\n const maxPanX = 0;\n const minPanY = windowHeight - sceneHeight * newZoom;\n const maxPanY = 0;\n\n let newPanX =\n mx - ((mx - initialPanOffsetPinch.x) / initialZoom) * newZoom;\n let newPanY =\n my - ((my - initialPanOffsetPinch.y) / initialZoom) * newZoom;\n\n // Clamp pan to prevent leaving bounds\n newPanX = Math.min(Math.max(newPanX, minPanX), maxPanX);\n newPanY = Math.min(Math.max(newPanY, minPanY), maxPanY);\n\n scale.set(newZoom);\n x.set(newPanX);\n y.set(newPanY);\n }\n },\n [\n isPanning,\n isResetting,\n x,\n y,\n scale,\n panStartPoint.x,\n panStartPoint.y,\n windowWidth,\n sceneWidth,\n windowHeight,\n sceneHeight,\n initialPanOffsetOnDrag.x,\n initialPanOffsetOnDrag.y,\n MIN_ZOOM,\n animationStage,\n stopAllSceneMotion,\n ]\n );\n\n const handlePointerUpOrCancel = useCallback(\n (event: PointerEvent<HTMLDivElement>): void => {\n if (animationStage < 2) {\n event.preventDefault();\n return; // ignore pointer up during intro\n }\n stopAllSceneMotion();\n event.preventDefault();\n if ((event.target as HTMLElement).hasPointerCapture(event.pointerId)) {\n (event.target as HTMLElement).releasePointerCapture(event.pointerId);\n }\n activePointersRef.current.delete(event.pointerId);\n\n if (isPanning && activePointersRef.current.size < 1) {\n setIsPanning(false);\n if (viewportRef.current)\n viewportRef.current.style.cursor = \"url('/customcursor.svg'), grab\";\n }\n\n if (initialPinchStateRef.current && activePointersRef.current.size < 2) {\n initialPinchStateRef.current = null;\n }\n\n if (\n !isPanning &&\n activePointersRef.current.size === 1 &&\n !initialPinchStateRef.current\n ) {\n const lastPointer = Array.from(activePointersRef.current.values())[0]!;\n setIsPanning(true);\n setPanStartPoint({ x: lastPointer.clientX, y: lastPointer.clientY });\n setInitialPanOffsetOnDrag({ x: x.get(), y: y.get() });\n }\n },\n [x, y, isPanning, animationStage, stopAllSceneMotion]\n );\n\n const handleWheelZoom = useCallback(\n (event: WheelEvent) => {\n if (animationStage < 2) {\n event.preventDefault();\n return; // block wheel interaction during intro animations\n }\n event.preventDefault();\n // pinch gesture on track\n const isPinch = event.ctrlKey || event.metaKey;\n const isMouseWheelZoom =\n event.deltaMode === WheelEvent.DOM_DELTA_LINE ||\n Math.abs(event.deltaY) >= 100;\n\n // mouse wheel zoom and track pad zoom have different sensitivities\n const ZOOM_SENSITIVITY = isMouseWheelZoom\n ? MOUSE_WHEEL_ZOOM_SENSITIVITY\n : TRACKPAD_ZOOM_SENSITIVITY;\n\n if (isPinch) {\n const currentZoom = scale.get();\n const nextZoom = Math.max(\n Math.min(\n currentZoom * (1 - event.deltaY * ZOOM_SENSITIVITY),\n MAX_ZOOM\n ),\n MIN_ZOOM,\n (window.innerWidth / sceneWidth) * ZOOM_BOUND, // Ensure zoom is at least the width of the canvas\n (window.innerHeight / sceneHeight) * ZOOM_BOUND // Ensure zoom is at least the height of the canvas\n );\n\n const rect = viewportRef.current?.getBoundingClientRect();\n\n if (!rect) return;\n\n const vpLeft = rect.left;\n const vpTop = rect.top;\n const viewportWidth = rect.width;\n const viewportHeight = rect.height;\n\n const cursorSceneX = (event.clientX - vpLeft - x.get()) / currentZoom;\n const cursorSceneY = (event.clientY - vpTop - y.get()) / currentZoom;\n\n let newPanX = event.clientX - vpLeft - cursorSceneX * nextZoom;\n let newPanY = event.clientY - vpTop - cursorSceneY * nextZoom;\n\n const minPanX = viewportWidth - sceneWidth * nextZoom;\n const minPanY = viewportHeight - sceneHeight * nextZoom;\n const maxPanX = 0;\n const maxPanY = 0;\n\n newPanX = Math.min(maxPanX, Math.max(minPanX, newPanX));\n newPanY = Math.min(maxPanY, Math.max(minPanY, newPanY));\n\n x.set(newPanX);\n y.set(newPanY);\n scale.set(nextZoom);\n } else {\n stopAllSceneMotion();\n\n const scrollSpeed = 1;\n const newPanX = x.get() - event.deltaX * scrollSpeed;\n const newPanY = y.get() - event.deltaY * scrollSpeed;\n\n const minPanX = windowWidth - sceneWidth * scale.get();\n const maxPanX = 0;\n const minPanY = windowHeight - sceneHeight * scale.get();\n const maxPanY = 0;\n\n const clampedPanX = Math.min(Math.max(newPanX, minPanX), maxPanX);\n const clampedPanY = Math.min(Math.max(newPanY, minPanY), maxPanY);\n\n x.set(clampedPanX);\n y.set(clampedPanY);\n }\n },\n [\n scale,\n MIN_ZOOM,\n x,\n y,\n sceneWidth,\n sceneHeight,\n windowWidth,\n windowHeight,\n animationStage,\n stopAllSceneMotion,\n ]\n );\n\n // Keep the wheel handler ref pointing to the latest implementation\n useEffect(() => {\n wheelHandlerRef.current = handleWheelZoom;\n }, [handleWheelZoom]);\n\n const handlePanToOffset = useCallback(\n (\n offset: { x: number; y: number },\n onComplete?: () => void,\n zoom?: number\n ) => {\n panToOffset(\n {\n x: -offset.x,\n y: -offset.y,\n },\n viewportRef,\n onComplete,\n zoom\n );\n },\n [panToOffset, viewportRef]\n );\n\n return (\n <CanvasWrapper\n introProgress={introProgress}\n onIntroGrowComplete={startStage2}\n skipIntro={skipIntro}\n introContent={introContent}\n loadingText={loadingText}\n introBackgroundGradient={introBackgroundGradient}\n wrapperBackground={wrapperBackground}\n canvasBoxGradient={canvasBoxGradient}\n growTransition={growTransition}\n blurTransition={blurTransition}\n >\n <CanvasProvider\n x={x}\n y={y}\n scale={scale}\n isResetting={isResetting}\n maxZIndex={maxZIndex}\n setMaxZIndex={setMaxZIndex}\n animationStage={animationStage}\n nextTargetSection={nextTargetSection}\n setNextTargetSection={setNextTargetSection}\n >\n {animationStage >= 2 && (\n <>\n {!toolbarConfig?.hidden && (\n <Toolbar\n homeCoordinates={offsetHomeCoordinates}\n config={toolbarConfig}\n />\n )}\n {hasNavbar && navItems && !navbarConfig?.hidden && (\n <Navbar\n panToOffset={handlePanToOffset}\n onReset={onResetViewAndItems}\n items={navItems}\n config={navbarConfig}\n />\n )}\n </>\n )}\n <div\n ref={setViewportRef}\n className=\"relative h-full w-full touch-none select-none overflow-hidden\"\n style={{\n touchAction: \"none\",\n pointerEvents: animationStage >= 2 ? \"auto\" : \"none\",\n overscrollBehavior: \"contain\",\n }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUpOrCancel}\n onPointerLeave={handlePointerUpOrCancel}\n onPointerCancel={handlePointerUpOrCancel}\n >\n <motion.div\n ref={sceneRef}\n className=\"absolute z-20 origin-top-left\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={fadeTransition ?? FADE_TRANSITION}\n style={{\n width: `${sceneWidth}px`,\n height: `${sceneHeight}px`,\n x,\n y,\n scale,\n willChange:\n mode !== \"high\" && (animationStage < 2 || isPanning)\n ? \"transform\"\n : \"auto\",\n }}\n >\n {/* Canvas Background - customizable or default */}\n {canvasBackground !== undefined ? (\n canvasBackground\n ) : (\n <>\n {animationStage >= 1 && mode === \"high\" ? (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ duration: 0.5, ease: \"easeIn\" }}\n >\n <DefaultCanvasBackground />\n </motion.div>\n ) : (\n <DefaultCanvasBackground />\n )}\n </>\n )}\n {children}\n </motion.div>\n </div>\n </CanvasProvider>\n </CanvasWrapper>\n );\n};\n\nexport default Canvas;\n"],"mappings":";;;;;;;;;;;;;;AAuGA,MAAM,iBACJ,GACA,GACA,UACG;AACH,GAAE,MAAM;AACR,GAAE,MAAM;AACR,OAAM,MAAM;;AAGd,MAAM,UAAqB,EACzB,UACA,iBACA,UACA,YAAY,OACZ,cACA,aACA,yBACA,mBACA,gBACA,gBACA,eACA,gBACA,kBACA,mBACA,eACA,cACA,cACA,kBACI;CACJ,MAAM,EAAE,QAAQ,cAAc,OAAO,gBAAgBA,6BAAqB;CAE1E,MAAM,EAAE,SAAS,oBAAoB;CAErC,MAAM,YAAY,QAAQ,YAAY,SAAS,SAAS,EAAE;CAE1D,MAAM,aAAa,eAAe;CAClC,MAAM,cAAc,gBAAgB;CAEpC,MAAM,WAAW,UAAU,kBAAkB,YAAY;CAGzD,MAAM,CAAC,WAAW,gBAAgB,SAAkB,MAAM;CAE1D,MAAM,CAAC,eAAe,oBAAoB,SAAkB,MAAM;CAClE,MAAM,CAAC,eAAe,oBAAoB,SAAgB;EAAE,GAAG;EAAG,GAAG;EAAG,CAAC;CACzE,MAAM,CAAC,wBAAwB,6BAA6B,SAAgB;EAC1E,GAAG;EACH,GAAG;EACJ,CAAC;CACF,MAAM,CAAC,aAAa,kBAAkB,SAAkB,MAAM;CAC9D,MAAM,CAAC,WAAW,gBAAgB,SAAiB,GAAG;CACtD,MAAM,CAAC,gBAAgB,qBAAqB,SAC1C,YAAY,IAAI,EACjB;CACD,MAAM,CAAC,mBAAmB,wBACxB,SAA+B,KAAK;CAEtC,MAAM,sBAAsB,OAAO,CAAC,UAAU;CAE9C,MAAM,kBAAkB,cAChB,oBAAoB,aAAa,aAAa,EACpD,CAAC,aAAa,aAAa,CAC5B;CAED,MAAM,wBAAwB,cAE1B,yBAAyB;EACvB,kBAAkB;GAAE,OAAO;GAAa,QAAQ;GAAc;EAC9D,QAAQ;EACR,YAAY;EACb,CAAC,EACJ;EAAC;EAAiB;EAAa;EAAa,CAC7C;CAGD,MAAM,IAAI,eAAe,YAAY,sBAAsB,IAAI,EAAE;CACjE,MAAM,IAAI,eAAe,YAAY,sBAAsB,IAAI,EAAE;CACjE,MAAM,QAAQ,eAAe,YAAY,IAAI,gBAAgB;CAE7D,MAAM,sBAAsB,aACzB,eAAkC;AACjC,iBAAe,KAAK;AAEpB,EAAK,iBAAiB,uBAAuB,GAAG,GAAG,OAAO,EAAE,CAAC,WAAW;AACtE,kBAAe,MAAM;AACrB,OAAI,WAAY,aAAY;IAC5B;IAEJ;EAAC;EAAuB;EAAG;EAAG;EAAM,CACrC;CAGD,MAAM,gBAAgB,eAAe,EAAE;CAGvC,MAAM,gBAAgB,cAAc;EAClC,MAAM,aAAa,KAAK,KACrB,eAAe,KAAK,aACpB,gBAAgB,KAAK,YACvB;AAGD,SAAO;GAAE;GAAY,OAFP,cAAc,aAAa,cAAc;GAE5B,OADb,eAAe,cAAc,cAAc;GACxB;IAChC;EAAC;EAAa;EAAc;EAAY;EAAY,CAAC;CAGxD,MAAM,eAAe,aACnB,eACA,CAAC,GAAG,EAAE,EACN,CAAC,iBAAiB,cAAc,WAAW,CAC5C;CACD,MAAM,WAAW,aAAa,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,cAAc,KAAK,CAAC;CAC7E,MAAM,WAAW,aAAa,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,cAAc,KAAK,CAAC;AAG7E,iBAAgB;EACd,MAAM,mBAAmB,aAAa,GAAG,WAAW,MAAM;AACxD,OAAI,mBAAmB,EAAG,OAAM,IAAI,EAAE;IACtC;EACF,MAAM,eAAe,SAAS,GAAG,WAAW,MAAM;AAChD,OAAI,mBAAmB,EAAG,GAAE,IAAI,EAAE;IAClC;EACF,MAAM,eAAe,SAAS,GAAG,WAAW,MAAM;AAChD,OAAI,mBAAmB,EAAG,GAAE,IAAI,EAAE;IAClC;AACF,eAAa;AACX,qBAAkB;AAClB,iBAAc;AACd,iBAAc;;IAEf;EAAC;EAAc;EAAU;EAAU;EAAgB;EAAO;EAAG;EAAE,CAAC;CAGnE,MAAM,yBAAqC,cAAc;AACvD,MAAI,CAAC,cAAe,QAAO;AAC3B,SAAO;GACL,GAAG;GACH,GAAG;GACJ;IACA,CAAC,cAAc,CAAC;CAGnB,MAAM,cAAc,kBAAkB;AACpC,MAAI,WAAW;AACb,KAAE,IAAI,sBAAsB,EAAE;AAC9B,KAAE,IAAI,sBAAsB,EAAE;AAC9B,SAAM,IAAI,EAAE;AACZ,qBAAkB,EAAE;AACpB,uBAAoB,UAAU;AAC9B;;AAGF,oBAAkB,EAAE;AAEpB,UAAQ,IAAI;GACV,QAAQ,GAAG,sBAAsB,GAAG,uBAAuB;GAC3D,QAAQ,GAAG,sBAAsB,GAAG,uBAAuB;GAC3D,QAAQ,OAAO,GAAG,uBAAuB;GAC1C,CAAC,CACC,WAAW;AACV,qBAAkB,EAAE;AACpB,uBAAoB,UAAU;IAC9B,CACD,YAAY;AACX,uBAAoB,UAAU;IAC9B;IACH;EAAC;EAAuB;EAAG;EAAG;EAAO;EAAwB;EAAU,CAAC;CAE3E,MAAM,cAAc,OAAuB,KAAK;CAChD,MAAM,WAAW,OAAuB,KAAK;CAG7C,MAAM,kBAAkB,OAAyC,KAAK;CACtE,MAAM,eAAe,aAAa,MAAkB;AAClD,kBAAgB,UAAU,EAAE;IAC3B,EAAE,CAAC;CAGN,MAAM,iBAAiB,aACpB,SAAgC;AAE/B,MAAI,YAAY,QACd,aAAY,QAAQ,oBAAoB,SAAS,aAAa;AAEhE,cAAY,UAAU;AACtB,MAAI,KACF,MAAK,iBAAiB,SAAS,cAAc,EAAE,SAAS,OAAO,CAAC;IAGpE,CAAC,aAAa,CACf;CAED,MAAM,oBAAoB,uBACxB,IAAI,KAAK,CACV;CACD,MAAM,uBAAuB,OAKnB,KAAK;CAEf,MAAM,cAAc,aAEhB,QACA,aACA,YACA,SACS;AACT,MAAI,CAAC,YAAY,QAAS;AAC1B,mBAAiB,KAAK;EAGtB,MAAM,gBAAgB,YAAY,QAAQ;EAC1C,MAAM,iBAAiB,YAAY,QAAQ;EAE3C,MAAM,UAAU,gBAAgB,cAAc,QAAQ;EACtD,MAAM,UAAU;EAChB,MAAM,UAAU,iBAAiB,eAAe,QAAQ;AAOxD,EAAK,iBACH;GAAE,GAJa,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,QAAQ,EAAE,QAAQ;GAI9C,GAHA,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,QAAQ,EAJrC,EAI+C;GAGjC,EAC5B,GACA,GACA,OACA,KACD,CAAC,WAAW;AACX,oBAAiB,MAAM;AACvB,OAAI,WAAY,aAAY;IAC5B;IAEJ;EAAC;EAAY;EAAa;EAAG;EAAG;EAAM,CACvC;CAGD,MAAM,qBAAqB,kBAAkB;AAC3C,MAAI,oBAAoB,QAAS;AACjC,gBAAc,GAAG,GAAG,MAAM;IACzB;EAAC;EAAG;EAAG;EAAM,CAAC;CAEjB,MAAM,oBAAoB,aACvB,UAA8C;AAC7C,MAAI,iBAAiB,EAAG;AACxB,oBAAkB,QAAQ,IAAI,MAAM,WAAW,MAAM;AACrD,EAAC,MAAM,OAAuB,kBAAkB,MAAM,UAAU;AAChE,MAAI,eAAe,cAAe;AAClC,sBAAoB;AAEpB,MAAI,kBAAkB,QAAQ,SAAS,GAAG;AAGxC,OADsB,MAAM,OACV,QAAQ,qBAAqB,EAAE;AAC/C,sBAAkB,QAAQ,OAAO,MAAM,UAAU;AACjD,IAAC,MAAM,OAAuB,sBAAsB,MAAM,UAAU;AACpE;;AAGF,gBAAa,KAAK;AAClB,oBAAiB;IAAE,GAAG,MAAM;IAAS,GAAG,MAAM;IAAS,CAAC;AACxD,6BAA0B;IAAE,GAAG,EAAE,KAAK;IAAE,GAAG,EAAE,KAAK;IAAE,CAAC;AACrD,OAAI,YAAY,QAAS,aAAY,QAAQ,MAAM,SAAS;aACnD,kBAAkB,QAAQ,SAAS,GAAG;AAC/C,gBAAa,MAAM;GACnB,MAAM,WAAW,MAAM,KAAK,kBAAkB,QAAQ,QAAQ,CAAC;AAC/D,wBAAqB,UAAU;IAC7B,UAAU,YAAY,SAAS,IAAK,SAAS,GAAI;IACjD,UAAU,YAAY,SAAS,IAAK,SAAS,GAAI;IACjD,MAAM,MAAM,KAAK;IACjB,WAAW;KAAE,GAAG,EAAE,KAAK;KAAE,GAAG,EAAE,KAAK;KAAE;IACtC;;IAGL;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,oBAAoB,aACvB,UAA8C;AAC7C,MAAI,iBAAiB,EAAG;AACxB,MAAI,aAAa,kBAAkB,QAAQ,QAAQ,EACjD,qBAAoB;AAEtB,MAAI,CAAC,kBAAkB,QAAQ,IAAI,MAAM,UAAU,IAAI,YACrD;AACF,oBAAkB,QAAQ,IAAI,MAAM,WAAW,MAAM;AAErD,MAAI,aAAa,kBAAkB,QAAQ,SAAS,GAAG;AACrD,SAAM,gBAAgB;GACtB,MAAM,SAAS,MAAM,UAAU,cAAc;GAC7C,MAAM,SAAS,MAAM,UAAU,cAAc;GAG7C,MAAM,UAAU,cAAc,aAAa,MAAM,KAAK;GACtD,MAAM,UAAU;GAChB,MAAM,UAAU,eAAe,cAAc,MAAM,KAAK;GACxD,MAAM,UAAU;GAEhB,MAAM,OAAO,KAAK,IAChB,KAAK,IAAI,uBAAuB,IAAI,QAAQ,QAAQ,EACpD,QACD;GACD,MAAM,OAAO,KAAK,IAChB,KAAK,IAAI,uBAAuB,IAAI,QAAQ,QAAQ,EACpD,QACD;AACD,KAAE,IAAI,KAAK;AACX,KAAE,IAAI,KAAK;aAEX,kBAAkB,QAAQ,QAAQ,KAClC,qBAAqB,SACrB;AACA,SAAM,gBAAgB;GACtB,MAAM,WAAW,MAAM,KAAK,kBAAkB,QAAQ,QAAQ,CAAC;GAC/D,MAAM,KAAK,SAAS;GACpB,MAAM,KAAK,SAAS;GAEpB,MAAM,kBAAkB,YAAY,IAAI,GAAG;GAC3C,MAAM,kBAAkB,YAAY,IAAI,GAAG;GAE3C,MAAM,EACJ,UAAU,iBACV,MAAM,aACN,WAAW,0BACT,qBAAqB;AAEzB,OAAI,oBAAoB,EAAG;GAE3B,IAAI,UAAU,eAAe,kBAAkB;AAC/C,aAAU,KAAK,IACZ,OAAO,aAAa,aAAc,YAClC,OAAO,cAAc,cAAe,YACrC,KAAK,IAAI,SAAS,GAAG,EACrB,SACD;GAED,MAAM,KAAK,gBAAgB;GAC3B,MAAM,KAAK,gBAAgB;GAE3B,MAAM,UAAU,cAAc,aAAa;GAC3C,MAAM,UAAU;GAChB,MAAM,UAAU,eAAe,cAAc;GAC7C,MAAM,UAAU;GAEhB,IAAI,UACF,MAAO,KAAK,sBAAsB,KAAK,cAAe;GACxD,IAAI,UACF,MAAO,KAAK,sBAAsB,KAAK,cAAe;AAGxD,aAAU,KAAK,IAAI,KAAK,IAAI,SAAS,QAAQ,EAAE,QAAQ;AACvD,aAAU,KAAK,IAAI,KAAK,IAAI,SAAS,QAAQ,EAAE,QAAQ;AAEvD,SAAM,IAAI,QAAQ;AAClB,KAAE,IAAI,QAAQ;AACd,KAAE,IAAI,QAAQ;;IAGlB;EACE;EACA;EACA;EACA;EACA;EACA,cAAc;EACd,cAAc;EACd;EACA;EACA;EACA;EACA,uBAAuB;EACvB,uBAAuB;EACvB;EACA;EACA;EACD,CACF;CAED,MAAM,0BAA0B,aAC7B,UAA8C;AAC7C,MAAI,iBAAiB,GAAG;AACtB,SAAM,gBAAgB;AACtB;;AAEF,sBAAoB;AACpB,QAAM,gBAAgB;AACtB,MAAK,MAAM,OAAuB,kBAAkB,MAAM,UAAU,CAClE,CAAC,MAAM,OAAuB,sBAAsB,MAAM,UAAU;AAEtE,oBAAkB,QAAQ,OAAO,MAAM,UAAU;AAEjD,MAAI,aAAa,kBAAkB,QAAQ,OAAO,GAAG;AACnD,gBAAa,MAAM;AACnB,OAAI,YAAY,QACd,aAAY,QAAQ,MAAM,SAAS;;AAGvC,MAAI,qBAAqB,WAAW,kBAAkB,QAAQ,OAAO,EACnE,sBAAqB,UAAU;AAGjC,MACE,CAAC,aACD,kBAAkB,QAAQ,SAAS,KACnC,CAAC,qBAAqB,SACtB;GACA,MAAM,cAAc,MAAM,KAAK,kBAAkB,QAAQ,QAAQ,CAAC,CAAC;AACnE,gBAAa,KAAK;AAClB,oBAAiB;IAAE,GAAG,YAAY;IAAS,GAAG,YAAY;IAAS,CAAC;AACpE,6BAA0B;IAAE,GAAG,EAAE,KAAK;IAAE,GAAG,EAAE,KAAK;IAAE,CAAC;;IAGzD;EAAC;EAAG;EAAG;EAAW;EAAgB;EAAmB,CACtD;CAED,MAAM,kBAAkB,aACrB,UAAsB;AACrB,MAAI,iBAAiB,GAAG;AACtB,SAAM,gBAAgB;AACtB;;AAEF,QAAM,gBAAgB;EAEtB,MAAM,UAAU,MAAM,WAAW,MAAM;EAMvC,MAAM,mBAJJ,MAAM,cAAc,WAAW,kBAC/B,KAAK,IAAI,MAAM,OAAO,IAAI,MAIxB,+BACA;AAEJ,MAAI,SAAS;GACX,MAAM,cAAc,MAAM,KAAK;GAC/B,MAAM,WAAW,KAAK,IACpB,KAAK,IACH,eAAe,IAAI,MAAM,SAAS,mBAClC,SACD,EACD,UACC,OAAO,aAAa,aAAc,YAClC,OAAO,cAAc,cAAe,WACtC;GAED,MAAM,OAAO,YAAY,SAAS,uBAAuB;AAEzD,OAAI,CAAC,KAAM;GAEX,MAAM,SAAS,KAAK;GACpB,MAAM,QAAQ,KAAK;GACnB,MAAM,gBAAgB,KAAK;GAC3B,MAAM,iBAAiB,KAAK;GAE5B,MAAM,gBAAgB,MAAM,UAAU,SAAS,EAAE,KAAK,IAAI;GAC1D,MAAM,gBAAgB,MAAM,UAAU,QAAQ,EAAE,KAAK,IAAI;GAEzD,IAAI,UAAU,MAAM,UAAU,SAAS,eAAe;GACtD,IAAI,UAAU,MAAM,UAAU,QAAQ,eAAe;GAErD,MAAM,UAAU,gBAAgB,aAAa;GAC7C,MAAM,UAAU,iBAAiB,cAAc;GAC/C,MAAM,UAAU;GAChB,MAAM,UAAU;AAEhB,aAAU,KAAK,IAAI,SAAS,KAAK,IAAI,SAAS,QAAQ,CAAC;AACvD,aAAU,KAAK,IAAI,SAAS,KAAK,IAAI,SAAS,QAAQ,CAAC;AAEvD,KAAE,IAAI,QAAQ;AACd,KAAE,IAAI,QAAQ;AACd,SAAM,IAAI,SAAS;SACd;AACL,uBAAoB;GAEpB,MAAM,cAAc;GACpB,MAAM,UAAU,EAAE,KAAK,GAAG,MAAM,SAAS;GACzC,MAAM,UAAU,EAAE,KAAK,GAAG,MAAM,SAAS;GAEzC,MAAM,UAAU,cAAc,aAAa,MAAM,KAAK;GACtD,MAAM,UAAU;GAChB,MAAM,UAAU,eAAe,cAAc,MAAM,KAAK;GACxD,MAAM,UAAU;GAEhB,MAAM,cAAc,KAAK,IAAI,KAAK,IAAI,SAAS,QAAQ,EAAE,QAAQ;GACjE,MAAM,cAAc,KAAK,IAAI,KAAK,IAAI,SAAS,QAAQ,EAAE,QAAQ;AAEjE,KAAE,IAAI,YAAY;AAClB,KAAE,IAAI,YAAY;;IAGtB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAGD,iBAAgB;AACd,kBAAgB,UAAU;IACzB,CAAC,gBAAgB,CAAC;CAErB,MAAM,oBAAoB,aAEtB,QACA,YACA,SACG;AACH,cACE;GACE,GAAG,CAAC,OAAO;GACX,GAAG,CAAC,OAAO;GACZ,EACD,aACA,YACA,KACD;IAEH,CAAC,aAAa,YAAY,CAC3B;AAED,QACE,oBAAC;EACgB;EACf,qBAAqB;EACV;EACG;EACD;EACY;EACN;EACA;EACH;EACA;YAEhB,qBAAC;GACI;GACA;GACI;GACM;GACF;GACG;GACE;GACG;GACG;cAErB,kBAAkB,KACjB,4CACG,CAAC,eAAe,UACf,oBAACC;IACC,iBAAiB;IACjB,QAAQ;KACR,EAEH,aAAa,YAAY,CAAC,cAAc,UACvC,oBAAC;IACC,aAAa;IACb,SAAS;IACT,OAAO;IACP,QAAQ;KACR,IAEH,EAEL,oBAAC;IACC,KAAK;IACL,WAAU;IACV,OAAO;KACL,aAAa;KACb,eAAe,kBAAkB,IAAI,SAAS;KAC9C,oBAAoB;KACrB;IACD,eAAe;IACf,eAAe;IACf,aAAa;IACb,gBAAgB;IAChB,iBAAiB;cAEjB,qBAAC,OAAO;KACN,KAAK;KACL,WAAU;KACV,SAAS,EAAE,SAAS,GAAG;KACvB,SAAS,EAAE,SAAS,GAAG;KACvB,YAAY,kBAAkB;KAC9B,OAAO;MACL,OAAO,GAAG,WAAW;MACrB,QAAQ,GAAG,YAAY;MACvB;MACA;MACA;MACA,YACE,SAAS,WAAW,iBAAiB,KAAK,aACtC,cACA;MACP;gBAGA,qBAAqB,SACpB,mBAEA,0CACG,kBAAkB,KAAK,SAAS,SAC/B,oBAAC,OAAO;MACN,SAAS,EAAE,SAAS,GAAG;MACvB,SAAS,EAAE,SAAS,GAAG;MACvB,YAAY;OAAE,UAAU;OAAK,MAAM;OAAU;gBAE7C,oBAAC,4BAA0B;OAChB,GAEb,oBAAC,4BAA0B,GAE5B,EAEJ;MACU;KACT;IACS;GACH;;AAIpB,qBAAe"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,5 +16,5 @@ export * from "./lib/canvas";
|
|
|
16
16
|
export * from "./lib/constants";
|
|
17
17
|
export * from "./lib/utils";
|
|
18
18
|
export * from "./utils/performance";
|
|
19
|
-
export type { SectionCoordinates, NavItem, CanvasSection, ToolbarConfig, ToolbarPosition, ToolbarDisplayMode, NavbarConfig, NavbarPosition, NavbarDisplayMode, NavbarButtonConfig, NavbarTooltipConfig, } from "./types";
|
|
19
|
+
export type { SectionCoordinates, NavItem, CanvasSection, ToolbarConfig, ToolbarPosition, ToolbarDisplayMode, NavbarConfig, NavbarPosition, NavbarDisplayMode, NavbarButtonConfig, NavbarTooltipConfig, AnimationTimingConfig, } from "./types";
|
|
20
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,iCAAiC,CAAC;AACzC,YAAY,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,GACf,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,eAAe,EACf,iBAAiB,GAClB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,kBAAkB,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAG5F,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AAGpC,YAAY,EACV,kBAAkB,EAClB,OAAO,EACP,aAAa,EACb,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,iCAAiC,CAAC;AACzC,YAAY,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,GACf,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,eAAe,EACf,iBAAiB,GAClB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,kBAAkB,IAAI,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAG5F,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AAGpC,YAAY,EACV,kBAAkB,EAClB,OAAO,EACP,aAAa,EACb,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CanvasContext, CanvasProvider, useCanvasContext } from "./contexts/CanvasContext.js";
|
|
2
|
-
import { BLUR_TRANSITION, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, GROW_TRANSITION, IMAGE_FALLBACK_WIDTH_THRESHOLD, INTERACTIVE_SELECTOR, INTRO_ASPECT_RATIO, MAX_DIM_RATIO, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, NAVBAR_DEBOUNCE_MS, PAN_SPRING, RESPONSIVE_ZOOM_MAP, STAGE2_TRANSITION, ScreenSizeEnum, TOOLBAR_OPACITY_POS_EPS, TOOLBAR_OPACITY_SCALE_EPS, TRACKPAD_ZOOM_SENSITIVITY, VIEWPORT_HYSTERESIS_BUFFER, ZOOM_BOUND } from "./lib/constants.js";
|
|
2
|
+
import { BLUR_TRANSITION, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, FADE_TRANSITION, GROW_TRANSITION, IMAGE_FALLBACK_WIDTH_THRESHOLD, INTERACTIVE_SELECTOR, INTRO_ASPECT_RATIO, MAX_DIM_RATIO, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, NAVBAR_DEBOUNCE_MS, PAN_SPRING, RESPONSIVE_ZOOM_MAP, STAGE2_TRANSITION, ScreenSizeEnum, TOOLBAR_OPACITY_POS_EPS, TOOLBAR_OPACITY_SCALE_EPS, TRACKPAD_ZOOM_SENSITIVITY, VIEWPORT_HYSTERESIS_BUFFER, ZOOM_BOUND } from "./lib/constants.js";
|
|
3
3
|
import { calcInitialBoxWidth, canvasHeight, canvasWidth, getDistance, getMidpoint, getScreenSizeEnum, getSectionPanCoordinates, panToOffsetScene, useMemoPoint } from "./lib/canvas.js";
|
|
4
4
|
import useWindowDimensions_default from "./hooks/useWindowDimensions.js";
|
|
5
5
|
import { cn } from "./lib/utils.js";
|
|
@@ -14,4 +14,4 @@ import { CanvasComponent } from "./components/canvas/component.js";
|
|
|
14
14
|
import { Draggable, DraggableImage } from "./components/canvas/draggable.js";
|
|
15
15
|
import { PerformanceProvider, usePerformance, usePerformanceMode } from "./contexts/PerformanceContext.js";
|
|
16
16
|
|
|
17
|
-
export { BLUR_TRANSITION, canvas_default as Canvas, CanvasComponent, CanvasContext, Navbar as CanvasNavbar, CanvasProvider, toolbar_default as CanvasToolbar, CanvasWrapper, DEFAULT_CANVAS_BOX_GRADIENT, DEFAULT_CANVAS_GRADIENT, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_INTRO_GRADIENT, DefaultCanvasBackground, DefaultIntroContent, DefaultWrapperBackground, Draggable, DraggableImage, GROW_TRANSITION, IMAGE_FALLBACK_WIDTH_THRESHOLD, INTERACTIVE_SELECTOR, INTRO_ASPECT_RATIO, MAX_DIM_RATIO, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, NAVBAR_DEBOUNCE_MS, PAN_SPRING, PerformanceProvider, RESPONSIVE_ZOOM_MAP, STAGE2_TRANSITION, ScreenSizeEnum, TOOLBAR_OPACITY_POS_EPS, TOOLBAR_OPACITY_SCALE_EPS, TRACKPAD_ZOOM_SENSITIVITY, VIEWPORT_HYSTERESIS_BUFFER, ZOOM_BOUND, calcInitialBoxWidth, canvasHeight, canvasWidth, cn, getDistance, getMidpoint, getScreenSizeEnum, getSectionPanCoordinates, getWillChange, GROW_TRANSITION as growTransition, isIOS, isMobile, panToOffsetScene, prefersReducedMotion, useCanvasContext, useMemoPoint, usePerformance, usePerformanceMode, usePerformanceMode$1 as usePerformanceModeLegacy, useWindowDimensions_default as useWindowDimensions };
|
|
17
|
+
export { BLUR_TRANSITION, canvas_default as Canvas, CanvasComponent, CanvasContext, Navbar as CanvasNavbar, CanvasProvider, toolbar_default as CanvasToolbar, CanvasWrapper, DEFAULT_CANVAS_BOX_GRADIENT, DEFAULT_CANVAS_GRADIENT, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, DEFAULT_INTRO_GRADIENT, DefaultCanvasBackground, DefaultIntroContent, DefaultWrapperBackground, Draggable, DraggableImage, FADE_TRANSITION, GROW_TRANSITION, IMAGE_FALLBACK_WIDTH_THRESHOLD, INTERACTIVE_SELECTOR, INTRO_ASPECT_RATIO, MAX_DIM_RATIO, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, NAVBAR_DEBOUNCE_MS, PAN_SPRING, PerformanceProvider, RESPONSIVE_ZOOM_MAP, STAGE2_TRANSITION, ScreenSizeEnum, TOOLBAR_OPACITY_POS_EPS, TOOLBAR_OPACITY_SCALE_EPS, TRACKPAD_ZOOM_SENSITIVITY, VIEWPORT_HYSTERESIS_BUFFER, ZOOM_BOUND, calcInitialBoxWidth, canvasHeight, canvasWidth, cn, getDistance, getMidpoint, getScreenSizeEnum, getSectionPanCoordinates, getWillChange, GROW_TRANSITION as growTransition, isIOS, isMobile, panToOffsetScene, prefersReducedMotion, useCanvasContext, useMemoPoint, usePerformance, usePerformanceMode, usePerformanceMode$1 as usePerformanceModeLegacy, useWindowDimensions_default as useWindowDimensions };
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -40,6 +40,11 @@ export declare const STAGE2_TRANSITION: {
|
|
|
40
40
|
readonly duration: 0.96;
|
|
41
41
|
readonly ease: readonly [0.37, 0.1, 0.6, 1];
|
|
42
42
|
};
|
|
43
|
+
/** Canvas scene fade-in transition config */
|
|
44
|
+
export declare const FADE_TRANSITION: {
|
|
45
|
+
readonly duration: 0.3;
|
|
46
|
+
readonly ease: Easing;
|
|
47
|
+
};
|
|
43
48
|
/** Maximum zoom level */
|
|
44
49
|
export declare const MAX_ZOOM = 3;
|
|
45
50
|
/** Minimum zoom bound multiplier to prevent zooming out past canvas edges */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/lib/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;GAGG;AAMH,oBAAY,cAAc;IACtB,YAAY,iBAAiB;IAC7B,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,aAAa,kBAAkB;IAC/B,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,YAAY,iBAAiB;CAChC;AAMD,qCAAqC;AACrC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,sCAAsC;AACtC,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAM1C,sEAAsE;AACtE,eAAO,MAAM,aAAa;;;CAGhB,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,uCAAuC;AACvC,eAAO,MAAM,eAAe;;;mBAGK,MAAM;CAC7B,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,eAAe;;;mBAGN,MAAM;CAClB,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAMX,yBAAyB;AACzB,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,6EAA6E;AAC7E,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,0CAA0C;AAC1C,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAQ3C,CAAC;AAEX,kCAAkC;AAClC,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,6CAA6C;AAC7C,eAAO,MAAM,4BAA4B,SAAS,CAAC;AAEnD,0CAA0C;AAC1C,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAM/C,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,QAEe,CAAC;AAMjD,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,8DAA8D;AAC9D,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAMnD,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAQrD,CAAC;AAMX,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAMX,mEAAmE;AACnE,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,OAAO,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/lib/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;;GAGG;AAMH,oBAAY,cAAc;IACtB,YAAY,iBAAiB;IAC7B,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,aAAa,kBAAkB;IAC/B,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,YAAY,iBAAiB;CAChC;AAMD,qCAAqC;AACrC,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,sCAAsC;AACtC,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAM1C,sEAAsE;AACtE,eAAO,MAAM,aAAa;;;CAGhB,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAExC,uCAAuC;AACvC,eAAO,MAAM,eAAe;;;mBAGK,MAAM;CAC7B,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,eAAe;;;mBAGN,MAAM;CAClB,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,6CAA6C;AAC7C,eAAO,MAAM,eAAe;;mBAEN,MAAM;CAClB,CAAC;AAMX,yBAAyB;AACzB,eAAO,MAAM,QAAQ,IAAI,CAAC;AAE1B,6EAA6E;AAC7E,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,0CAA0C;AAC1C,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAQ3C,CAAC;AAEX,kCAAkC;AAClC,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,6CAA6C;AAC7C,eAAO,MAAM,4BAA4B,SAAS,CAAC;AAEnD,0CAA0C;AAC1C,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAM/C,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,QAEe,CAAC;AAMjD,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,8DAA8D;AAC9D,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAMnD,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAQrD,CAAC;AAMX,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAMX,mEAAmE;AACnE,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,OAAO,CAAC"}
|
package/dist/lib/constants.js
CHANGED
|
@@ -51,6 +51,11 @@ const STAGE2_TRANSITION = {
|
|
|
51
51
|
1
|
|
52
52
|
]
|
|
53
53
|
};
|
|
54
|
+
/** Canvas scene fade-in transition config */
|
|
55
|
+
const FADE_TRANSITION = {
|
|
56
|
+
duration: .3,
|
|
57
|
+
ease: "easeIn"
|
|
58
|
+
};
|
|
54
59
|
/** Maximum zoom level */
|
|
55
60
|
const MAX_ZOOM = 3;
|
|
56
61
|
/** Minimum zoom bound multiplier to prevent zooming out past canvas edges */
|
|
@@ -104,5 +109,5 @@ const TOOLBAR_OPACITY_POS_EPS = 1;
|
|
|
104
109
|
const TOOLBAR_OPACITY_SCALE_EPS = .01;
|
|
105
110
|
|
|
106
111
|
//#endregion
|
|
107
|
-
export { BLUR_TRANSITION, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, GROW_TRANSITION, IMAGE_FALLBACK_WIDTH_THRESHOLD, INTERACTIVE_SELECTOR, INTRO_ASPECT_RATIO, MAX_DIM_RATIO, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, NAVBAR_DEBOUNCE_MS, PAN_SPRING, RESPONSIVE_ZOOM_MAP, STAGE2_TRANSITION, ScreenSizeEnum, TOOLBAR_OPACITY_POS_EPS, TOOLBAR_OPACITY_SCALE_EPS, TRACKPAD_ZOOM_SENSITIVITY, VIEWPORT_HYSTERESIS_BUFFER, ZOOM_BOUND };
|
|
112
|
+
export { BLUR_TRANSITION, DEFAULT_CANVAS_HEIGHT, DEFAULT_CANVAS_WIDTH, FADE_TRANSITION, GROW_TRANSITION, IMAGE_FALLBACK_WIDTH_THRESHOLD, INTERACTIVE_SELECTOR, INTRO_ASPECT_RATIO, MAX_DIM_RATIO, MAX_ZOOM, MIN_ZOOMS, MOUSE_WHEEL_ZOOM_SENSITIVITY, NAVBAR_DEBOUNCE_MS, PAN_SPRING, RESPONSIVE_ZOOM_MAP, STAGE2_TRANSITION, ScreenSizeEnum, TOOLBAR_OPACITY_POS_EPS, TOOLBAR_OPACITY_SCALE_EPS, TRACKPAD_ZOOM_SENSITIVITY, VIEWPORT_HYSTERESIS_BUFFER, ZOOM_BOUND };
|
|
108
113
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../../src/lib/constants.ts"],"sourcesContent":["import { type Easing } from \"framer-motion\";\n\n/**\n * Canvas Library Constants\n * All configurable constants consolidated in one place\n */\n\n// ============================================================================\n// SCREEN SIZE BREAKPOINTS\n// ============================================================================\n\nexport enum ScreenSizeEnum {\n SMALL_MOBILE = \"small-mobile\",\n MOBILE = \"mobile\",\n TABLET = \"tablet\",\n SMALL_DESKTOP = \"small-desktop\",\n MEDIUM_DESKTOP = \"medium-desktop\",\n LARGE_DESKTOP = \"large-desktop\",\n HUGE_DESKTOP = \"huge-desktop\",\n}\n\n// ============================================================================\n// CANVAS DIMENSIONS\n// ============================================================================\n\n/** Default canvas width in pixels */\nexport const DEFAULT_CANVAS_WIDTH = 6000;\n\n/** Default canvas height in pixels */\nexport const DEFAULT_CANVAS_HEIGHT = 4000;\n\n// ============================================================================\n// INTRO ANIMATION\n// ============================================================================\n\n/** Maximum dimensions ratio for the intro box relative to viewport */\nexport const MAX_DIM_RATIO = {\n width: 0.8,\n height: 0.5,\n} as const;\n\n/** Intro box aspect ratio (width:height) */\nexport const INTRO_ASPECT_RATIO = 3 / 2;\n\n/** Grow animation transition config */\nexport const GROW_TRANSITION = {\n duration: 0.96,\n delay: 3.14,\n ease: [0.35, 0.1, 0.8, 1] as Easing,\n} as const;\n\n/** Blur mask animation transition config */\nexport const BLUR_TRANSITION = {\n duration: 0.85,\n delay: 1.25,\n ease: \"easeIn\" as Easing,\n} as const;\n\n/** Stage 2 pan-to-home transition config */\nexport const STAGE2_TRANSITION = {\n duration: 0.96,\n ease: [0.37, 0.1, 0.6, 1],\n} as const;\n\n// ============================================================================\n// ZOOM & PAN\n// ============================================================================\n\n/** Maximum zoom level */\nexport const MAX_ZOOM = 3;\n\n/** Minimum zoom bound multiplier to prevent zooming out past canvas edges */\nexport const ZOOM_BOUND = 1.05;\n\n/** Minimum zoom levels per screen size */\nexport const MIN_ZOOMS: Record<ScreenSizeEnum, number> = {\n [ScreenSizeEnum.SMALL_MOBILE]: 0.3,\n [ScreenSizeEnum.MOBILE]: 0.35,\n [ScreenSizeEnum.TABLET]: 0.25,\n [ScreenSizeEnum.SMALL_DESKTOP]: 0.15,\n [ScreenSizeEnum.MEDIUM_DESKTOP]: 0.1,\n [ScreenSizeEnum.LARGE_DESKTOP]: 0.1,\n [ScreenSizeEnum.HUGE_DESKTOP]: 0.1,\n} as const;\n\n/** Pan animation spring config */\nexport const PAN_SPRING = {\n visualDuration: 0.34,\n type: \"spring\",\n stiffness: 200,\n damping: 25,\n} as const;\n\n/** Wheel zoom sensitivity for mouse wheel */\nexport const MOUSE_WHEEL_ZOOM_SENSITIVITY = 0.0015;\n\n/** Wheel zoom sensitivity for trackpad */\nexport const TRACKPAD_ZOOM_SENSITIVITY = 0.015;\n\n// ============================================================================\n// INTERACTIONS\n// ============================================================================\n\n/** CSS selector for interactive elements that should not trigger pan */\nexport const INTERACTIVE_SELECTOR =\n \"button,[role='button'],input,textarea,[contenteditable='true'],\" +\n \"[data-toolbar-button],[data-navbar-button]\";\n\n// ============================================================================\n// VIEWPORT CULLING\n// ============================================================================\n\n/** Buffer zone in pixels for hysteresis visibility detection */\nexport const VIEWPORT_HYSTERESIS_BUFFER = 120;\n\n/** Threshold for showing image fallback on smaller screens */\nexport const IMAGE_FALLBACK_WIDTH_THRESHOLD = 2000;\n\n// ============================================================================\n// NAVBAR\n// ============================================================================\n\n/** Responsive zoom levels for navbar navigation per screen size */\nexport const RESPONSIVE_ZOOM_MAP: Record<ScreenSizeEnum, number> = {\n [ScreenSizeEnum.SMALL_MOBILE]: 0.5,\n [ScreenSizeEnum.MOBILE]: 0.6,\n [ScreenSizeEnum.TABLET]: 0.8,\n [ScreenSizeEnum.SMALL_DESKTOP]: 0.9,\n [ScreenSizeEnum.MEDIUM_DESKTOP]: 1,\n [ScreenSizeEnum.LARGE_DESKTOP]: 1.25,\n [ScreenSizeEnum.HUGE_DESKTOP]: 1.5,\n} as const;\n\n// ============================================================================\n// PERFORMANCE\n// ============================================================================\n\n/** Debounce duration in ms for navbar clicks based on performance mode */\nexport const NAVBAR_DEBOUNCE_MS = {\n high: 0,\n medium: 100,\n low: 400,\n} as const;\n\n// ============================================================================\n// TOOLBAR\n// ============================================================================\n\n/** Epsilon for position comparison to hide toolbar when at home */\nexport const TOOLBAR_OPACITY_POS_EPS = 1; // px\n\n/** Epsilon for scale comparison to hide toolbar when at 1x */\nexport const TOOLBAR_OPACITY_SCALE_EPS = 0.01; // scale delta\n"],"mappings":";;;;;AAWA,IAAY,0DAAL;AACH;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAQJ,MAAa,uBAAuB;;AAGpC,MAAa,wBAAwB;;AAOrC,MAAa,gBAAgB;CACzB,OAAO;CACP,QAAQ;CACX;;AAGD,MAAa,qBAAqB,IAAI;;AAGtC,MAAa,kBAAkB;CAC3B,UAAU;CACV,OAAO;CACP,MAAM;EAAC;EAAM;EAAK;EAAK;EAAE;CAC5B;;AAGD,MAAa,kBAAkB;CAC3B,UAAU;CACV,OAAO;CACP,MAAM;CACT;;AAGD,MAAa,oBAAoB;CAC7B,UAAU;CACV,MAAM;EAAC;EAAM;EAAK;EAAK;EAAE;CAC5B;;AAOD,MAAa,WAAW;;AAGxB,MAAa,aAAa;;AAG1B,MAAa,YAA4C;EACpD,eAAe,eAAe;EAC9B,eAAe,SAAS;EACxB,eAAe,SAAS;EACxB,eAAe,gBAAgB;EAC/B,eAAe,iBAAiB;EAChC,eAAe,gBAAgB;EAC/B,eAAe,eAAe;CAClC;;AAGD,MAAa,aAAa;CACtB,gBAAgB;CAChB,MAAM;CACN,WAAW;CACX,SAAS;CACZ;;AAGD,MAAa,+BAA+B;;AAG5C,MAAa,4BAA4B;;AAOzC,MAAa,uBACT;;AAQJ,MAAa,6BAA6B;;AAG1C,MAAa,iCAAiC;;AAO9C,MAAa,sBAAsD;EAC9D,eAAe,eAAe;EAC9B,eAAe,SAAS;EACxB,eAAe,SAAS;EACxB,eAAe,gBAAgB;EAC/B,eAAe,iBAAiB;EAChC,eAAe,gBAAgB;EAC/B,eAAe,eAAe;CAClC;;AAOD,MAAa,qBAAqB;CAC9B,MAAM;CACN,QAAQ;CACR,KAAK;CACR;;AAOD,MAAa,0BAA0B;;AAGvC,MAAa,4BAA4B"}
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../src/lib/constants.ts"],"sourcesContent":["import { type Easing } from \"framer-motion\";\n\n/**\n * Canvas Library Constants\n * All configurable constants consolidated in one place\n */\n\n// ============================================================================\n// SCREEN SIZE BREAKPOINTS\n// ============================================================================\n\nexport enum ScreenSizeEnum {\n SMALL_MOBILE = \"small-mobile\",\n MOBILE = \"mobile\",\n TABLET = \"tablet\",\n SMALL_DESKTOP = \"small-desktop\",\n MEDIUM_DESKTOP = \"medium-desktop\",\n LARGE_DESKTOP = \"large-desktop\",\n HUGE_DESKTOP = \"huge-desktop\",\n}\n\n// ============================================================================\n// CANVAS DIMENSIONS\n// ============================================================================\n\n/** Default canvas width in pixels */\nexport const DEFAULT_CANVAS_WIDTH = 6000;\n\n/** Default canvas height in pixels */\nexport const DEFAULT_CANVAS_HEIGHT = 4000;\n\n// ============================================================================\n// INTRO ANIMATION\n// ============================================================================\n\n/** Maximum dimensions ratio for the intro box relative to viewport */\nexport const MAX_DIM_RATIO = {\n width: 0.8,\n height: 0.5,\n} as const;\n\n/** Intro box aspect ratio (width:height) */\nexport const INTRO_ASPECT_RATIO = 3 / 2;\n\n/** Grow animation transition config */\nexport const GROW_TRANSITION = {\n duration: 0.96,\n delay: 3.14,\n ease: [0.35, 0.1, 0.8, 1] as Easing,\n} as const;\n\n/** Blur mask animation transition config */\nexport const BLUR_TRANSITION = {\n duration: 0.85,\n delay: 1.25,\n ease: \"easeIn\" as Easing,\n} as const;\n\n/** Stage 2 pan-to-home transition config */\nexport const STAGE2_TRANSITION = {\n duration: 0.96,\n ease: [0.37, 0.1, 0.6, 1],\n} as const;\n\n/** Canvas scene fade-in transition config */\nexport const FADE_TRANSITION = {\n duration: 0.3,\n ease: \"easeIn\" as Easing,\n} as const;\n\n// ============================================================================\n// ZOOM & PAN\n// ============================================================================\n\n/** Maximum zoom level */\nexport const MAX_ZOOM = 3;\n\n/** Minimum zoom bound multiplier to prevent zooming out past canvas edges */\nexport const ZOOM_BOUND = 1.05;\n\n/** Minimum zoom levels per screen size */\nexport const MIN_ZOOMS: Record<ScreenSizeEnum, number> = {\n [ScreenSizeEnum.SMALL_MOBILE]: 0.3,\n [ScreenSizeEnum.MOBILE]: 0.35,\n [ScreenSizeEnum.TABLET]: 0.25,\n [ScreenSizeEnum.SMALL_DESKTOP]: 0.15,\n [ScreenSizeEnum.MEDIUM_DESKTOP]: 0.1,\n [ScreenSizeEnum.LARGE_DESKTOP]: 0.1,\n [ScreenSizeEnum.HUGE_DESKTOP]: 0.1,\n} as const;\n\n/** Pan animation spring config */\nexport const PAN_SPRING = {\n visualDuration: 0.34,\n type: \"spring\",\n stiffness: 200,\n damping: 25,\n} as const;\n\n/** Wheel zoom sensitivity for mouse wheel */\nexport const MOUSE_WHEEL_ZOOM_SENSITIVITY = 0.0015;\n\n/** Wheel zoom sensitivity for trackpad */\nexport const TRACKPAD_ZOOM_SENSITIVITY = 0.015;\n\n// ============================================================================\n// INTERACTIONS\n// ============================================================================\n\n/** CSS selector for interactive elements that should not trigger pan */\nexport const INTERACTIVE_SELECTOR =\n \"button,[role='button'],input,textarea,[contenteditable='true'],\" +\n \"[data-toolbar-button],[data-navbar-button]\";\n\n// ============================================================================\n// VIEWPORT CULLING\n// ============================================================================\n\n/** Buffer zone in pixels for hysteresis visibility detection */\nexport const VIEWPORT_HYSTERESIS_BUFFER = 120;\n\n/** Threshold for showing image fallback on smaller screens */\nexport const IMAGE_FALLBACK_WIDTH_THRESHOLD = 2000;\n\n// ============================================================================\n// NAVBAR\n// ============================================================================\n\n/** Responsive zoom levels for navbar navigation per screen size */\nexport const RESPONSIVE_ZOOM_MAP: Record<ScreenSizeEnum, number> = {\n [ScreenSizeEnum.SMALL_MOBILE]: 0.5,\n [ScreenSizeEnum.MOBILE]: 0.6,\n [ScreenSizeEnum.TABLET]: 0.8,\n [ScreenSizeEnum.SMALL_DESKTOP]: 0.9,\n [ScreenSizeEnum.MEDIUM_DESKTOP]: 1,\n [ScreenSizeEnum.LARGE_DESKTOP]: 1.25,\n [ScreenSizeEnum.HUGE_DESKTOP]: 1.5,\n} as const;\n\n// ============================================================================\n// PERFORMANCE\n// ============================================================================\n\n/** Debounce duration in ms for navbar clicks based on performance mode */\nexport const NAVBAR_DEBOUNCE_MS = {\n high: 0,\n medium: 100,\n low: 400,\n} as const;\n\n// ============================================================================\n// TOOLBAR\n// ============================================================================\n\n/** Epsilon for position comparison to hide toolbar when at home */\nexport const TOOLBAR_OPACITY_POS_EPS = 1; // px\n\n/** Epsilon for scale comparison to hide toolbar when at 1x */\nexport const TOOLBAR_OPACITY_SCALE_EPS = 0.01; // scale delta\n"],"mappings":";;;;;AAWA,IAAY,0DAAL;AACH;AACA;AACA;AACA;AACA;AACA;AACA;;;;AAQJ,MAAa,uBAAuB;;AAGpC,MAAa,wBAAwB;;AAOrC,MAAa,gBAAgB;CACzB,OAAO;CACP,QAAQ;CACX;;AAGD,MAAa,qBAAqB,IAAI;;AAGtC,MAAa,kBAAkB;CAC3B,UAAU;CACV,OAAO;CACP,MAAM;EAAC;EAAM;EAAK;EAAK;EAAE;CAC5B;;AAGD,MAAa,kBAAkB;CAC3B,UAAU;CACV,OAAO;CACP,MAAM;CACT;;AAGD,MAAa,oBAAoB;CAC7B,UAAU;CACV,MAAM;EAAC;EAAM;EAAK;EAAK;EAAE;CAC5B;;AAGD,MAAa,kBAAkB;CAC3B,UAAU;CACV,MAAM;CACT;;AAOD,MAAa,WAAW;;AAGxB,MAAa,aAAa;;AAG1B,MAAa,YAA4C;EACpD,eAAe,eAAe;EAC9B,eAAe,SAAS;EACxB,eAAe,SAAS;EACxB,eAAe,gBAAgB;EAC/B,eAAe,iBAAiB;EAChC,eAAe,gBAAgB;EAC/B,eAAe,eAAe;CAClC;;AAGD,MAAa,aAAa;CACtB,gBAAgB;CAChB,MAAM;CACN,WAAW;CACX,SAAS;CACZ;;AAGD,MAAa,+BAA+B;;AAG5C,MAAa,4BAA4B;;AAOzC,MAAa,uBACT;;AAQJ,MAAa,6BAA6B;;AAG1C,MAAa,iCAAiC;;AAO9C,MAAa,sBAAsD;EAC9D,eAAe,eAAe;EAC9B,eAAe,SAAS;EACxB,eAAe,SAAS;EACxB,eAAe,gBAAgB;EAC/B,eAAe,iBAAiB;EAChC,eAAe,gBAAgB;EAC/B,eAAe,eAAe;CAClC;;AAOD,MAAa,qBAAqB;CAC9B,MAAM;CACN,QAAQ;CACR,KAAK;CACR;;AAOD,MAAa,0BAA0B;;AAGvC,MAAa,4BAA4B"}
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--canvas-beige:#f5f5f5;--canvas-coral:#d4d4d4;--canvas-lilac:#e5e5e5;--canvas-salmon:#a3a3a3;--canvas-heavy:#171717;--canvas-emphasis:#262626;--canvas-active:#525252;--canvas-tinted:#a3a3a3;--canvas-medium:#737373;--canvas-light:#a3a3a3;--canvas-faint-lilac:#fafafa;--canvas-offwhite:#fff;--canvas-highlight:#f5f5f5;--canvas-pushed:#e5e5e5;--canvas-border-light:0 0% 89%}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1600px){.container{max-width:1600px}}@media (min-width:2000px){.container{max-width:2000px}}@media (min-width:3000px){.container{max-width:3000px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-top-10{top:-2.5rem}.bottom-12{bottom:3rem}.bottom-24{bottom:6rem}.bottom-6{bottom:1.5rem}.left-1\/2{left:50%}.left-4{left:1rem}.left-full{left:100%}.right-4{right:1rem}.top-1\/2{top:50%}.top-12{top:3rem}.top-24{top:6rem}.top-6{top:1.5rem}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.z-\[1000\]{z-index:1000}.m-auto{margin:auto}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.hidden{display:none}.h-auto{height:auto}.h-full{height:100%}.w-full{width:100%}.flex-shrink-0{flex-shrink:0}.grow{flex-grow:1}.origin-center{transform-origin:center}.origin-top-left{transform-origin:top left}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.overflow-hidden{overflow:hidden}.whitespace-nowrap{white-space:nowrap}.rounded-\[10px\]{border-radius:10px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-border{border-color:hsl(var(--border))}.border-neutral-200{--tw-border-opacity:1;border-color:rgb(229 229 229/var(--tw-border-opacity,1))}.bg-canvas-offwhite{background-color:var(--canvas-offwhite)}.bg-neutral-100{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity,1))}.bg-neutral-200{--tw-bg-opacity:1;background-color:rgb(229 229 229/var(--tw-bg-opacity,1))}.bg-neutral-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.bg-none{background-image:none}.from-black\/10{--tw-gradient-from:rgba(0,0,0,.1) var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.object-contain{-o-object-fit:contain;object-fit:contain}.p-1{padding:.25rem}.p-2{padding:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-\[1px\]{padding-left:1px;padding-right:1px}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-\[2\.5px\]{padding-bottom:2.5px}.pt-\[1px\]{padding-top:1px}.text-center{text-align:center}.font-canvas-figtree{font-family:var(--font-figtree)}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-canvas-heavy{color:var(--canvas-heavy)}.text-canvas-light{color:var(--canvas-light)}.text-neutral-500{--tw-text-opacity:1;color:rgb(115 115 115/var(--tw-text-opacity,1))}.text-neutral-600{--tw-text-opacity:1;color:rgb(82 82 82/var(--tw-text-opacity,1))}.text-neutral-700{--tw-text-opacity:1;color:rgb(64 64 64/var(--tw-text-opacity,1))}.shadow-\[0_20px_40px_rgba\(103\2c 86\2c 86\2c 0\.15\)\]{--tw-shadow:0 20px 40px hsla(0,9%,37%,.15);--tw-shadow-colored:0 20px 40px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-\[0_6px_12px_rgba\(0\2c 0\2c 0\2c 0\.08\)\]{--tw-shadow:0 6px 12px rgba(0,0,0,.08);--tw-shadow-colored:0 6px 12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-\[0_6px_12px_rgba\(0\2c 0\2c 0\2c 0\.10\)\]{--tw-shadow:0 6px 12px rgba(0,0,0,.1);--tw-shadow-colored:0 6px 12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.blur{--tw-blur:blur(8px)}.blur,.drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.duration-200{animation-duration:.2s}.running{animation-play-state:running}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}@media (min-width:640px){.sm\:bottom-4{bottom:1rem}.sm\:top-4{top:1rem}}@media (min-width:768px){.md\:bottom-4{bottom:1rem}.md\:top-4{top:1rem}.md\:inline{display:inline}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:py-8{padding-top:2rem;padding-bottom:2rem}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}
|
|
1
|
+
*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--canvas-beige:#f5f5f5;--canvas-coral:#d4d4d4;--canvas-lilac:#e5e5e5;--canvas-salmon:#a3a3a3;--canvas-heavy:#171717;--canvas-emphasis:#262626;--canvas-active:#525252;--canvas-tinted:#a3a3a3;--canvas-medium:#737373;--canvas-light:#a3a3a3;--canvas-faint-lilac:#fafafa;--canvas-offwhite:#fff;--canvas-highlight:#f5f5f5;--canvas-pushed:#e5e5e5;--canvas-border-light:0 0% 89%}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1600px){.container{max-width:1600px}}@media (min-width:2000px){.container{max-width:2000px}}@media (min-width:3000px){.container{max-width:3000px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-top-10{top:-2.5rem}.bottom-12{bottom:3rem}.bottom-24{bottom:6rem}.bottom-6{bottom:1.5rem}.left-1\/2{left:50%}.left-4{left:1rem}.left-full{left:100%}.right-4{right:1rem}.top-1\/2{top:50%}.top-12{top:3rem}.top-24{top:6rem}.top-6{top:1.5rem}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.z-\[1000\]{z-index:1000}.m-auto{margin:auto}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.hidden{display:none}.h-auto{height:auto}.h-full{height:100%}.w-full{width:100%}.flex-shrink-0{flex-shrink:0}.grow{flex-grow:1}.origin-center{transform-origin:center}.origin-top-left{transform-origin:top left}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.overflow-hidden{overflow:hidden}.whitespace-nowrap{white-space:nowrap}.rounded-\[10px\]{border-radius:10px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-border{border-color:hsl(var(--border))}.border-neutral-200{--tw-border-opacity:1;border-color:rgb(229 229 229/var(--tw-border-opacity,1))}.bg-canvas-offwhite{background-color:var(--canvas-offwhite)}.bg-neutral-100{--tw-bg-opacity:1;background-color:rgb(245 245 245/var(--tw-bg-opacity,1))}.bg-neutral-200{--tw-bg-opacity:1;background-color:rgb(229 229 229/var(--tw-bg-opacity,1))}.bg-neutral-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.bg-none{background-image:none}.from-black\/10{--tw-gradient-from:rgba(0,0,0,.1) var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.object-contain{-o-object-fit:contain;object-fit:contain}.p-1{padding:.25rem}.p-2{padding:.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.px-\[1px\]{padding-left:1px;padding-right:1px}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-\[2\.5px\]{padding-bottom:2.5px}.pt-\[1px\]{padding-top:1px}.text-center{text-align:center}.font-canvas-figtree{font-family:var(--font-figtree)}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-canvas-heavy{color:var(--canvas-heavy)}.text-canvas-light{color:var(--canvas-light)}.text-neutral-500{--tw-text-opacity:1;color:rgb(115 115 115/var(--tw-text-opacity,1))}.text-neutral-600{--tw-text-opacity:1;color:rgb(82 82 82/var(--tw-text-opacity,1))}.text-neutral-700{--tw-text-opacity:1;color:rgb(64 64 64/var(--tw-text-opacity,1))}.shadow-\[0_20px_40px_rgba\(103\2c 86\2c 86\2c 0\.15\)\]{--tw-shadow:0 20px 40px hsla(0,9%,37%,.15);--tw-shadow-colored:0 20px 40px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-\[0_6px_12px_rgba\(0\2c 0\2c 0\2c 0\.08\)\]{--tw-shadow:0 6px 12px rgba(0,0,0,.08);--tw-shadow-colored:0 6px 12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-\[0_6px_12px_rgba\(0\2c 0\2c 0\2c 0\.10\)\]{--tw-shadow:0 6px 12px rgba(0,0,0,.1);--tw-shadow-colored:0 6px 12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.blur{--tw-blur:blur(8px)}.blur,.drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.fade-in{--tw-enter-opacity:0}.duration-200{animation-duration:.2s}.running{animation-play-state:running}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}@media (min-width:640px){.sm\:bottom-4{bottom:1rem}.sm\:top-4{top:1rem}}@media (min-width:768px){.md\:bottom-4{bottom:1rem}.md\:top-4{top:1rem}.md\:inline{display:inline}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:py-8{padding-top:2rem;padding-bottom:2rem}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Generic types for the canvas library
|
|
3
3
|
* Apps should extend these types with their specific enums and constants
|
|
4
4
|
*/
|
|
5
|
+
import type { Transition } from "framer-motion";
|
|
5
6
|
export interface SectionCoordinates {
|
|
6
7
|
x: number;
|
|
7
8
|
y: number;
|
|
@@ -126,6 +127,27 @@ export interface NavbarButtonConfig {
|
|
|
126
127
|
/** Label inline styles */
|
|
127
128
|
labelStyle?: React.CSSProperties;
|
|
128
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Animation timing configuration for intro animations.
|
|
132
|
+
* Re-exports Framer Motion's Transition type for full flexibility.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Simple duration-based transition
|
|
136
|
+
* const timing: AnimationTimingConfig = {
|
|
137
|
+
* duration: 1.5,
|
|
138
|
+
* delay: 0.5,
|
|
139
|
+
* ease: "easeInOut"
|
|
140
|
+
* };
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // Custom bezier curve
|
|
144
|
+
* const timing: AnimationTimingConfig = {
|
|
145
|
+
* duration: 0.96,
|
|
146
|
+
* delay: 3.14,
|
|
147
|
+
* ease: [0.35, 0.1, 0.8, 1]
|
|
148
|
+
* };
|
|
149
|
+
*/
|
|
150
|
+
export type AnimationTimingConfig = Transition;
|
|
129
151
|
/**
|
|
130
152
|
* Configuration options for the canvas navbar
|
|
131
153
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from "../../lib/canvas";
|
|
34
34
|
import {
|
|
35
35
|
STAGE2_TRANSITION,
|
|
36
|
+
FADE_TRANSITION,
|
|
36
37
|
MOUSE_WHEEL_ZOOM_SENSITIVITY,
|
|
37
38
|
TRACKPAD_ZOOM_SENSITIVITY,
|
|
38
39
|
DEFAULT_CANVAS_WIDTH,
|
|
@@ -80,6 +81,10 @@ interface Props {
|
|
|
80
81
|
growTransition?: Transition;
|
|
81
82
|
/** Custom blur transition */
|
|
82
83
|
blurTransition?: Transition;
|
|
84
|
+
/** Custom pan-to-home transition (stage 2) */
|
|
85
|
+
panTransition?: Transition;
|
|
86
|
+
/** Custom fade-in transition for the canvas scene */
|
|
87
|
+
fadeTransition?: Transition;
|
|
83
88
|
|
|
84
89
|
// ============== Background Customization ==============
|
|
85
90
|
/** Custom canvas background. If not provided, uses DefaultCanvasBackground. */
|
|
@@ -117,6 +122,8 @@ const Canvas: FC<Props> = ({
|
|
|
117
122
|
canvasBoxGradient,
|
|
118
123
|
growTransition,
|
|
119
124
|
blurTransition,
|
|
125
|
+
panTransition,
|
|
126
|
+
fadeTransition,
|
|
120
127
|
canvasBackground,
|
|
121
128
|
wrapperBackground,
|
|
122
129
|
toolbarConfig,
|
|
@@ -146,22 +153,19 @@ const Canvas: FC<Props> = ({
|
|
|
146
153
|
});
|
|
147
154
|
const [isResetting, setIsResetting] = useState<boolean>(false);
|
|
148
155
|
const [maxZIndex, setMaxZIndex] = useState<number>(50);
|
|
149
|
-
const [animationStage, setAnimationStage] = useState<number>(
|
|
156
|
+
const [animationStage, setAnimationStage] = useState<number>(
|
|
157
|
+
skipIntro ? 2 : 0
|
|
158
|
+
); // 0: initial, 1: finish grow, 2: pan to home
|
|
150
159
|
const [nextTargetSection, setNextTargetSection] =
|
|
151
160
|
useState<CanvasSection | null>(null);
|
|
152
161
|
// Track if the intro (stage1 + stage2) is still running, to avoid accidental cancellation
|
|
153
|
-
const isIntroAnimatingRef = useRef(
|
|
162
|
+
const isIntroAnimatingRef = useRef(!skipIntro);
|
|
154
163
|
|
|
155
164
|
const initialBoxWidth = useMemo(
|
|
156
165
|
() => calcInitialBoxWidth(windowWidth, windowHeight),
|
|
157
166
|
[windowWidth, windowHeight]
|
|
158
167
|
);
|
|
159
168
|
|
|
160
|
-
// somewhere near the middle-ish
|
|
161
|
-
const x = useMotionValue(0);
|
|
162
|
-
const y = useMotionValue(0);
|
|
163
|
-
const scale = useMotionValue(initialBoxWidth);
|
|
164
|
-
|
|
165
169
|
const offsetHomeCoordinates = useMemo(
|
|
166
170
|
() =>
|
|
167
171
|
getSectionPanCoordinates({
|
|
@@ -172,6 +176,11 @@ const Canvas: FC<Props> = ({
|
|
|
172
176
|
[homeCoordinates, windowWidth, windowHeight]
|
|
173
177
|
);
|
|
174
178
|
|
|
179
|
+
// When skipIntro, initialize at home position; otherwise start at origin for intro animation
|
|
180
|
+
const x = useMotionValue(skipIntro ? offsetHomeCoordinates.x : 0);
|
|
181
|
+
const y = useMotionValue(skipIntro ? offsetHomeCoordinates.y : 0);
|
|
182
|
+
const scale = useMotionValue(skipIntro ? 1 : initialBoxWidth);
|
|
183
|
+
|
|
175
184
|
const onResetViewAndItems = useCallback(
|
|
176
185
|
(onComplete?: () => void): void => {
|
|
177
186
|
setIsResetting(true);
|
|
@@ -225,14 +234,32 @@ const Canvas: FC<Props> = ({
|
|
|
225
234
|
};
|
|
226
235
|
}, [derivedScale, derivedX, derivedY, animationStage, scale, x, y]);
|
|
227
236
|
|
|
237
|
+
// Merge custom panTransition with defaults
|
|
238
|
+
const effectivePanTransition: Transition = useMemo(() => {
|
|
239
|
+
if (!panTransition) return STAGE2_TRANSITION;
|
|
240
|
+
return {
|
|
241
|
+
...STAGE2_TRANSITION,
|
|
242
|
+
...panTransition,
|
|
243
|
+
};
|
|
244
|
+
}, [panTransition]);
|
|
245
|
+
|
|
228
246
|
// Kick off stage2 (pan to home) when grow completes (introProgress hits 1)
|
|
229
247
|
const startStage2 = useCallback(() => {
|
|
248
|
+
if (skipIntro) {
|
|
249
|
+
x.set(offsetHomeCoordinates.x);
|
|
250
|
+
y.set(offsetHomeCoordinates.y);
|
|
251
|
+
scale.set(1);
|
|
252
|
+
setAnimationStage(2);
|
|
253
|
+
isIntroAnimatingRef.current = false;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
230
257
|
setAnimationStage(1);
|
|
231
258
|
|
|
232
259
|
Promise.all([
|
|
233
|
-
animate(x, offsetHomeCoordinates.x,
|
|
234
|
-
animate(y, offsetHomeCoordinates.y,
|
|
235
|
-
animate(scale, 1,
|
|
260
|
+
animate(x, offsetHomeCoordinates.x, effectivePanTransition),
|
|
261
|
+
animate(y, offsetHomeCoordinates.y, effectivePanTransition),
|
|
262
|
+
animate(scale, 1, effectivePanTransition),
|
|
236
263
|
])
|
|
237
264
|
.then(() => {
|
|
238
265
|
setAnimationStage(2);
|
|
@@ -241,7 +268,7 @@ const Canvas: FC<Props> = ({
|
|
|
241
268
|
.catch(() => {
|
|
242
269
|
isIntroAnimatingRef.current = false;
|
|
243
270
|
});
|
|
244
|
-
}, [offsetHomeCoordinates, x, y, scale]);
|
|
271
|
+
}, [offsetHomeCoordinates, x, y, scale, effectivePanTransition, skipIntro]);
|
|
245
272
|
|
|
246
273
|
const viewportRef = useRef<HTMLDivElement>(null);
|
|
247
274
|
const sceneRef = useRef<HTMLDivElement>(null);
|
|
@@ -679,7 +706,7 @@ const Canvas: FC<Props> = ({
|
|
|
679
706
|
className="absolute z-20 origin-top-left"
|
|
680
707
|
initial={{ opacity: 0 }}
|
|
681
708
|
animate={{ opacity: 1 }}
|
|
682
|
-
transition={
|
|
709
|
+
transition={fadeTransition ?? FADE_TRANSITION}
|
|
683
710
|
style={{
|
|
684
711
|
width: `${sceneWidth}px`,
|
|
685
712
|
height: `${sceneHeight}px`,
|
package/src/index.ts
CHANGED
package/src/lib/constants.ts
CHANGED
|
@@ -62,6 +62,12 @@ export const STAGE2_TRANSITION = {
|
|
|
62
62
|
ease: [0.37, 0.1, 0.6, 1],
|
|
63
63
|
} as const;
|
|
64
64
|
|
|
65
|
+
/** Canvas scene fade-in transition config */
|
|
66
|
+
export const FADE_TRANSITION = {
|
|
67
|
+
duration: 0.3,
|
|
68
|
+
ease: "easeIn" as Easing,
|
|
69
|
+
} as const;
|
|
70
|
+
|
|
65
71
|
// ============================================================================
|
|
66
72
|
// ZOOM & PAN
|
|
67
73
|
// ============================================================================
|
package/src/types/index.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Apps should extend these types with their specific enums and constants
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { Transition } from "framer-motion";
|
|
7
|
+
|
|
6
8
|
export interface SectionCoordinates {
|
|
7
9
|
x: number;
|
|
8
10
|
y: number;
|
|
@@ -152,6 +154,28 @@ export interface NavbarButtonConfig {
|
|
|
152
154
|
labelStyle?: React.CSSProperties;
|
|
153
155
|
}
|
|
154
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Animation timing configuration for intro animations.
|
|
159
|
+
* Re-exports Framer Motion's Transition type for full flexibility.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* // Simple duration-based transition
|
|
163
|
+
* const timing: AnimationTimingConfig = {
|
|
164
|
+
* duration: 1.5,
|
|
165
|
+
* delay: 0.5,
|
|
166
|
+
* ease: "easeInOut"
|
|
167
|
+
* };
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* // Custom bezier curve
|
|
171
|
+
* const timing: AnimationTimingConfig = {
|
|
172
|
+
* duration: 0.96,
|
|
173
|
+
* delay: 3.14,
|
|
174
|
+
* ease: [0.35, 0.1, 0.8, 1]
|
|
175
|
+
* };
|
|
176
|
+
*/
|
|
177
|
+
export type AnimationTimingConfig = Transition;
|
|
178
|
+
|
|
155
179
|
/**
|
|
156
180
|
* Configuration options for the canvas navbar
|
|
157
181
|
*/
|