@juniorxsound/react-three-components 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -41
- package/dist/components/CircularCarousel/CircularCarousel.d.ts +1 -4
- package/dist/components/CircularCarousel/context.d.ts +4 -3
- package/dist/components/CircularCarousel/types.d.ts +2 -23
- package/dist/components/CircularCarousel/utils.d.ts +0 -1
- package/dist/components/LinearCarousel/LinearCarousel.d.ts +1 -4
- package/dist/components/LinearCarousel/context.d.ts +3 -3
- package/dist/components/LinearCarousel/types.d.ts +5 -23
- package/dist/components/LinearCarousel/utils.d.ts +0 -1
- package/dist/components/StorybookUtils/index.d.ts +9 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useCarouselContext.d.ts +20 -0
- package/dist/hooks/useCarouselDrag.d.ts +85 -0
- package/dist/react-three-components.js +494 -375
- package/dist/utils/index.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
[](https://github.com/juniorxsound/react-three-components/actions/workflows/ci.yml)
|
|
5
5
|
[](https://github.com/juniorxsound/react-three-components/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A personal set of reusable components for React Three Fiber 🪄
|
|
8
|
+
|
|
9
|
+
> This library is a work in progress - if you find any issues, please report them [here](https://github.com/juniorxsound/react-three-components/issues). Please proceed with caution if you use this library in production.
|
|
8
10
|
|
|
9
11
|
## Installation
|
|
10
12
|
|
|
@@ -28,12 +30,15 @@ A 3D carousel that arranges items in a circle and rotates around an axis.
|
|
|
28
30
|
|
|
29
31
|
```tsx
|
|
30
32
|
import { Canvas } from "@react-three/fiber";
|
|
31
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
CircularCarousel,
|
|
35
|
+
useCarouselContext,
|
|
36
|
+
} from "@juniorxsound/react-three-components";
|
|
32
37
|
|
|
33
38
|
function Item({ index }: { index: number }) {
|
|
34
39
|
const { activeIndex } = useCarouselContext();
|
|
35
40
|
const isActive = index === activeIndex;
|
|
36
|
-
|
|
41
|
+
|
|
37
42
|
return (
|
|
38
43
|
<mesh>
|
|
39
44
|
<boxGeometry args={[1, 1, 1]} />
|
|
@@ -58,27 +63,27 @@ function App() {
|
|
|
58
63
|
|
|
59
64
|
#### Props
|
|
60
65
|
|
|
61
|
-
| Prop
|
|
62
|
-
|
|
63
|
-
| `children`
|
|
64
|
-
| `radius`
|
|
65
|
-
| `axis`
|
|
66
|
-
| `index`
|
|
67
|
-
| `defaultIndex`
|
|
68
|
-
| `onIndexChange`
|
|
69
|
-
| `dragEnabled`
|
|
70
|
-
| `dragSensitivity` | `number`
|
|
71
|
-
| `dragAxis`
|
|
72
|
-
| `dragConfig`
|
|
66
|
+
| Prop | Type | Default | Description |
|
|
67
|
+
| ----------------- | ------------------------- | -------- | ----------------------------- |
|
|
68
|
+
| `children` | `ReactNode` | required | Carousel items |
|
|
69
|
+
| `radius` | `number` | `3` | Distance from center to items |
|
|
70
|
+
| `axis` | `"x" \| "y" \| "z"` | `"y"` | Rotation axis |
|
|
71
|
+
| `index` | `number` | - | Controlled active index |
|
|
72
|
+
| `defaultIndex` | `number` | `0` | Initial index (uncontrolled) |
|
|
73
|
+
| `onIndexChange` | `(index: number) => void` | - | Called when index changes |
|
|
74
|
+
| `dragEnabled` | `boolean` | `true` | Enable drag gestures |
|
|
75
|
+
| `dragSensitivity` | `number` | auto | Drag sensitivity |
|
|
76
|
+
| `dragAxis` | `"x" \| "y"` | `"x"` | Drag gesture axis |
|
|
77
|
+
| `dragConfig` | `DragConfig` | - | Additional drag options |
|
|
73
78
|
|
|
74
79
|
#### Ref Methods
|
|
75
80
|
|
|
76
81
|
```tsx
|
|
77
82
|
const ref = useRef<CircularCarouselRef>(null);
|
|
78
83
|
|
|
79
|
-
ref.current.next();
|
|
80
|
-
ref.current.prev();
|
|
81
|
-
ref.current.goTo(2);
|
|
84
|
+
ref.current.next(); // Go to next item
|
|
85
|
+
ref.current.prev(); // Go to previous item
|
|
86
|
+
ref.current.goTo(2); // Go to specific index
|
|
82
87
|
```
|
|
83
88
|
|
|
84
89
|
#### With Navigation Triggers
|
|
@@ -88,13 +93,19 @@ ref.current.goTo(2); // Go to specific index
|
|
|
88
93
|
<Item index={0} />
|
|
89
94
|
<Item index={1} />
|
|
90
95
|
<Item index={2} />
|
|
91
|
-
|
|
96
|
+
|
|
92
97
|
<CircularCarousel.PrevTrigger position={[-2, 0, 0]}>
|
|
93
|
-
<mesh
|
|
98
|
+
<mesh>
|
|
99
|
+
<boxGeometry />
|
|
100
|
+
<meshBasicMaterial color="blue" />
|
|
101
|
+
</mesh>
|
|
94
102
|
</CircularCarousel.PrevTrigger>
|
|
95
|
-
|
|
103
|
+
|
|
96
104
|
<CircularCarousel.NextTrigger position={[2, 0, 0]}>
|
|
97
|
-
<mesh
|
|
105
|
+
<mesh>
|
|
106
|
+
<boxGeometry />
|
|
107
|
+
<meshBasicMaterial color="red" />
|
|
108
|
+
</mesh>
|
|
98
109
|
</CircularCarousel.NextTrigger>
|
|
99
110
|
</CircularCarousel>
|
|
100
111
|
```
|
|
@@ -107,12 +118,15 @@ A carousel that slides items linearly (horizontally or vertically).
|
|
|
107
118
|
|
|
108
119
|
```tsx
|
|
109
120
|
import { Canvas } from "@react-three/fiber";
|
|
110
|
-
import {
|
|
121
|
+
import {
|
|
122
|
+
LinearCarousel,
|
|
123
|
+
useLinearCarouselContext,
|
|
124
|
+
} from "@juniorxsound/react-three-components";
|
|
111
125
|
|
|
112
126
|
function Item({ index }: { index: number }) {
|
|
113
127
|
const { activeIndex } = useLinearCarouselContext();
|
|
114
128
|
const isActive = index === activeIndex;
|
|
115
|
-
|
|
129
|
+
|
|
116
130
|
return (
|
|
117
131
|
<mesh scale={isActive ? 1.2 : 1}>
|
|
118
132
|
<planeGeometry args={[2, 1.5]} />
|
|
@@ -137,27 +151,27 @@ function App() {
|
|
|
137
151
|
|
|
138
152
|
#### Props
|
|
139
153
|
|
|
140
|
-
| Prop
|
|
141
|
-
|
|
142
|
-
| `children`
|
|
143
|
-
| `gap`
|
|
144
|
-
| `direction`
|
|
145
|
-
| `index`
|
|
146
|
-
| `defaultIndex`
|
|
147
|
-
| `onIndexChange`
|
|
148
|
-
| `dragEnabled`
|
|
149
|
-
| `dragSensitivity` | `number`
|
|
150
|
-
| `dragAxis`
|
|
151
|
-
| `dragConfig`
|
|
154
|
+
| Prop | Type | Default | Description |
|
|
155
|
+
| ----------------- | ---------------------------- | -------------- | ---------------------------------- |
|
|
156
|
+
| `children` | `ReactNode` | required | Carousel items |
|
|
157
|
+
| `gap` | `number` | `0.2` | Space between items |
|
|
158
|
+
| `direction` | `"horizontal" \| "vertical"` | `"horizontal"` | Slide direction |
|
|
159
|
+
| `index` | `number` | - | Controlled active index |
|
|
160
|
+
| `defaultIndex` | `number` | `0` | Initial index (uncontrolled) |
|
|
161
|
+
| `onIndexChange` | `(index: number) => void` | - | Called when index changes |
|
|
162
|
+
| `dragEnabled` | `boolean` | `true` | Enable drag gestures |
|
|
163
|
+
| `dragSensitivity` | `number` | `150` | Drag sensitivity |
|
|
164
|
+
| `dragAxis` | `"x" \| "y"` | auto | Drag axis (derived from direction) |
|
|
165
|
+
| `dragConfig` | `DragConfig` | - | Additional drag options |
|
|
152
166
|
|
|
153
167
|
#### Ref Methods
|
|
154
168
|
|
|
155
169
|
```tsx
|
|
156
170
|
const ref = useRef<LinearCarouselRef>(null);
|
|
157
171
|
|
|
158
|
-
ref.current.next();
|
|
159
|
-
ref.current.prev();
|
|
160
|
-
ref.current.goTo(2);
|
|
172
|
+
ref.current.next(); // Go to next item (bounded)
|
|
173
|
+
ref.current.prev(); // Go to previous item (bounded)
|
|
174
|
+
ref.current.goTo(2); // Go to specific index
|
|
161
175
|
```
|
|
162
176
|
|
|
163
177
|
> Note: LinearCarousel is bounded (doesn't wrap around), unlike CircularCarousel which loops infinitely.
|
|
@@ -169,11 +183,11 @@ ref.current.goTo(2); // Go to specific index
|
|
|
169
183
|
<Item index={0} />
|
|
170
184
|
<Item index={1} />
|
|
171
185
|
<Item index={2} />
|
|
172
|
-
|
|
186
|
+
|
|
173
187
|
<LinearCarousel.PrevTrigger position={[-3, 0, 0]}>
|
|
174
188
|
<PrevButton />
|
|
175
189
|
</LinearCarousel.PrevTrigger>
|
|
176
|
-
|
|
190
|
+
|
|
177
191
|
<LinearCarousel.NextTrigger position={[3, 0, 0]}>
|
|
178
192
|
<NextButton />
|
|
179
193
|
</LinearCarousel.NextTrigger>
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { type ForwardRefExoticComponent, type RefAttributes } from "react";
|
|
2
1
|
import { CircularCarouselNextTrigger, CircularCarouselPrevTrigger } from "./CircularCarouselTriggers";
|
|
3
2
|
import type { CircularCarouselProps, CircularCarouselRef } from "./types";
|
|
4
|
-
|
|
3
|
+
export declare const CircularCarousel: import("react").ForwardRefExoticComponent<CircularCarouselProps & import("react").RefAttributes<CircularCarouselRef>> & {
|
|
5
4
|
NextTrigger: typeof CircularCarouselNextTrigger;
|
|
6
5
|
PrevTrigger: typeof CircularCarouselPrevTrigger;
|
|
7
6
|
};
|
|
8
|
-
export declare const CircularCarousel: CircularCarouselComponent;
|
|
9
|
-
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
export
|
|
3
|
-
export declare function
|
|
1
|
+
import { CarouselContext } from "../../hooks";
|
|
2
|
+
export { CarouselContext };
|
|
3
|
+
export declare function useCircularCarouselContext(): import("./types").CarouselContextValue;
|
|
4
|
+
export { useCircularCarouselContext as useCarouselContext };
|
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
axis?: "x" | "y";
|
|
5
|
-
/** Pointer options for drag gesture */
|
|
6
|
-
pointer?: {
|
|
7
|
-
touch?: boolean;
|
|
8
|
-
capture?: boolean;
|
|
9
|
-
keys?: boolean;
|
|
10
|
-
};
|
|
11
|
-
/** Touch action CSS property. Default: "none" */
|
|
12
|
-
touchAction?: string;
|
|
13
|
-
/** Threshold in pixels before drag starts */
|
|
14
|
-
threshold?: number;
|
|
15
|
-
/** Rubberband effect when dragging past bounds (0-1) */
|
|
16
|
-
rubberband?: boolean | number;
|
|
17
|
-
};
|
|
2
|
+
import type { DragConfig, CarouselContextValue } from "../../hooks";
|
|
3
|
+
export type { DragConfig, CarouselContextValue };
|
|
18
4
|
export type CircularCarouselProps = {
|
|
19
5
|
children: ReactNode;
|
|
20
6
|
radius?: number;
|
|
@@ -34,10 +20,3 @@ export type CircularCarouselRef = {
|
|
|
34
20
|
prev(): void;
|
|
35
21
|
goTo(index: number): void;
|
|
36
22
|
};
|
|
37
|
-
export type CarouselContextValue = {
|
|
38
|
-
activeIndex: number;
|
|
39
|
-
count: number;
|
|
40
|
-
next: () => void;
|
|
41
|
-
prev: () => void;
|
|
42
|
-
goTo: (index: number) => void;
|
|
43
|
-
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare function clamp(value: number, min: number, max: number): number;
|
|
2
1
|
export declare function getItemTransform(index: number, count: number, radius: number, axis: "x" | "y" | "z"): {
|
|
3
2
|
position: readonly [number, 0, number] | readonly [0, number, number] | readonly [number, number, 0];
|
|
4
3
|
rotation: readonly [0, number, 0] | readonly [number, 0, 0] | readonly [0, 0, number];
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { type ForwardRefExoticComponent, type RefAttributes } from "react";
|
|
2
1
|
import { LinearCarouselNextTrigger, LinearCarouselPrevTrigger } from "./LinearCarouselTriggers";
|
|
3
2
|
import type { LinearCarouselProps, LinearCarouselRef } from "./types";
|
|
4
|
-
|
|
3
|
+
export declare const LinearCarousel: import("react").ForwardRefExoticComponent<LinearCarouselProps & import("react").RefAttributes<LinearCarouselRef>> & {
|
|
5
4
|
NextTrigger: typeof LinearCarouselNextTrigger;
|
|
6
5
|
PrevTrigger: typeof LinearCarouselPrevTrigger;
|
|
7
6
|
};
|
|
8
|
-
export declare const LinearCarousel: LinearCarouselComponent;
|
|
9
|
-
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
export
|
|
3
|
-
export declare function useLinearCarouselContext(): LinearCarouselContextValue;
|
|
1
|
+
import { CarouselContext } from "../../hooks";
|
|
2
|
+
export { CarouselContext as LinearCarouselContext };
|
|
3
|
+
export declare function useLinearCarouselContext(): import("./types").LinearCarouselContextValue;
|
|
@@ -1,20 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/** Pointer options for drag gesture */
|
|
6
|
-
pointer?: {
|
|
7
|
-
touch?: boolean;
|
|
8
|
-
capture?: boolean;
|
|
9
|
-
keys?: boolean;
|
|
10
|
-
};
|
|
11
|
-
/** Touch action CSS property. Default: "none" */
|
|
12
|
-
touchAction?: string;
|
|
13
|
-
/** Threshold in pixels before drag starts */
|
|
14
|
-
threshold?: number;
|
|
15
|
-
/** Rubberband effect when dragging past bounds (0-1) */
|
|
16
|
-
rubberband?: boolean | number;
|
|
17
|
-
};
|
|
2
|
+
import type { DragConfig, CarouselContextValue } from "../../hooks";
|
|
3
|
+
export type { DragConfig };
|
|
4
|
+
export type { CarouselContextValue as LinearCarouselContextValue };
|
|
18
5
|
export type LinearCarouselProps = {
|
|
19
6
|
children: ReactNode;
|
|
20
7
|
gap?: number;
|
|
@@ -28,16 +15,11 @@ export type LinearCarouselProps = {
|
|
|
28
15
|
dragAxis?: "x" | "y";
|
|
29
16
|
/** Additional drag gesture configuration */
|
|
30
17
|
dragConfig?: DragConfig;
|
|
18
|
+
/** When true, navigation wraps from last item to first and vice versa. Default: false */
|
|
19
|
+
infinite?: boolean;
|
|
31
20
|
};
|
|
32
21
|
export type LinearCarouselRef = {
|
|
33
22
|
next(): void;
|
|
34
23
|
prev(): void;
|
|
35
24
|
goTo(index: number): void;
|
|
36
25
|
};
|
|
37
|
-
export type LinearCarouselContextValue = {
|
|
38
|
-
activeIndex: number;
|
|
39
|
-
count: number;
|
|
40
|
-
next: () => void;
|
|
41
|
-
prev: () => void;
|
|
42
|
-
goTo: (index: number) => void;
|
|
43
|
-
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* A floating 3D shape for use in carousel stories.
|
|
4
|
+
* Pass `isActive` to control the float animation.
|
|
5
|
+
*/
|
|
6
|
+
export declare function FloatingShape({ type, isActive, }: {
|
|
7
|
+
type: "box" | "sphere" | "cone" | "plane";
|
|
8
|
+
isActive: boolean;
|
|
9
|
+
}): React.ReactNode;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type CarouselContextValue = {
|
|
2
|
+
/** Currently active item index */
|
|
3
|
+
activeIndex: number;
|
|
4
|
+
/** Total number of items */
|
|
5
|
+
count: number;
|
|
6
|
+
/** Navigate to the next item */
|
|
7
|
+
next: () => void;
|
|
8
|
+
/** Navigate to the previous item */
|
|
9
|
+
prev: () => void;
|
|
10
|
+
/** Navigate to a specific item by index */
|
|
11
|
+
goTo: (index: number) => void;
|
|
12
|
+
};
|
|
13
|
+
export declare const CarouselContext: import("react").Context<CarouselContextValue | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Hook to access carousel context from within a carousel component.
|
|
16
|
+
* @param componentName - Name of the carousel component for error messages
|
|
17
|
+
* @returns The carousel context value
|
|
18
|
+
* @throws If used outside of a carousel component
|
|
19
|
+
*/
|
|
20
|
+
export declare function useCarouselContext(componentName: string): CarouselContextValue;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type SpringRef } from "@react-spring/web";
|
|
2
|
+
export type DragConfig = {
|
|
3
|
+
/** Axis to constrain drag movement */
|
|
4
|
+
axis?: "x" | "y";
|
|
5
|
+
/** Pointer options for drag gesture */
|
|
6
|
+
pointer?: {
|
|
7
|
+
touch?: boolean;
|
|
8
|
+
capture?: boolean;
|
|
9
|
+
keys?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Touch action CSS property. Default: "none" */
|
|
12
|
+
touchAction?: string;
|
|
13
|
+
/** Threshold in pixels before drag starts. Default: 5 */
|
|
14
|
+
threshold?: number;
|
|
15
|
+
/** Rubberband effect when dragging past bounds (0-1) */
|
|
16
|
+
rubberband?: boolean | number;
|
|
17
|
+
/** Filter out taps (clicks) from drags. Default: true */
|
|
18
|
+
filterTaps?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type SpringConfig = {
|
|
21
|
+
tension: number;
|
|
22
|
+
friction: number;
|
|
23
|
+
};
|
|
24
|
+
export type UseCarouselDragOptions = {
|
|
25
|
+
/** Number of items in the carousel */
|
|
26
|
+
count: number;
|
|
27
|
+
/** Whether drag is enabled */
|
|
28
|
+
dragEnabled: boolean;
|
|
29
|
+
/** Axis for drag gesture ("x" or "y") */
|
|
30
|
+
dragAxis: "x" | "y";
|
|
31
|
+
/** Sensitivity for drag movement (pixels per unit) */
|
|
32
|
+
dragSensitivity: number;
|
|
33
|
+
/** Maximum drag amount per gesture (e.g., itemSpacing or anglePerItem) */
|
|
34
|
+
maxDragAmount: number;
|
|
35
|
+
/** Additional drag configuration */
|
|
36
|
+
dragConfig?: DragConfig;
|
|
37
|
+
/** Spring configuration for settling animations */
|
|
38
|
+
springConfig: SpringConfig;
|
|
39
|
+
/** Spring configuration for drag animations */
|
|
40
|
+
dragSpringConfig: SpringConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Calculate the nearest index from an offset value.
|
|
43
|
+
* @param offset - The current offset value
|
|
44
|
+
* @returns The nearest index
|
|
45
|
+
*/
|
|
46
|
+
calculateIndexFromOffset: (offset: number) => number;
|
|
47
|
+
/**
|
|
48
|
+
* Calculate the final target index after drag ends.
|
|
49
|
+
* @param currentOffset - The offset at drag end
|
|
50
|
+
* @param startIndex - The index when drag started
|
|
51
|
+
* @param dragAmount - The clamped drag amount
|
|
52
|
+
* @returns The target index to navigate to
|
|
53
|
+
*/
|
|
54
|
+
calculateTargetIndex: (currentOffset: number, startIndex: number, dragAmount: number) => number;
|
|
55
|
+
/**
|
|
56
|
+
* Calculate the target offset for an index.
|
|
57
|
+
* @param index - The target index
|
|
58
|
+
* @param currentOffset - The current offset (useful for shortest path calculations)
|
|
59
|
+
* @returns The target offset value
|
|
60
|
+
*/
|
|
61
|
+
calculateTargetOffset: (index: number, currentOffset: number) => number;
|
|
62
|
+
/**
|
|
63
|
+
* Called when navigation completes with the final index.
|
|
64
|
+
*/
|
|
65
|
+
onNavigate: (index: number) => void;
|
|
66
|
+
};
|
|
67
|
+
export type UseCarouselDragReturn = {
|
|
68
|
+
/** Spring offset value */
|
|
69
|
+
offset: {
|
|
70
|
+
get: () => number;
|
|
71
|
+
};
|
|
72
|
+
/** Spring API for manual control */
|
|
73
|
+
springApi: SpringRef<{
|
|
74
|
+
offset: number;
|
|
75
|
+
}>;
|
|
76
|
+
/** Ref tracking if currently dragging */
|
|
77
|
+
isDraggingRef: React.MutableRefObject<boolean>;
|
|
78
|
+
/** Ref tracking current offset value (updated each frame) */
|
|
79
|
+
currentOffsetRef: React.MutableRefObject<number>;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Shared hook for carousel drag behavior.
|
|
83
|
+
* Handles spring animation, gesture recognition, and cursor management.
|
|
84
|
+
*/
|
|
85
|
+
export declare function useCarouselDrag(options: UseCarouselDragOptions): UseCarouselDragReturn;
|