Skip to content

Commit

Permalink
Merge pull request #7 from isoteriksoftware/dev
Browse files Browse the repository at this point in the history
Implemented a new GalleryItem rendering algorithm
  • Loading branch information
isoteriksoftware authored Jun 4, 2024
2 parents f964058 + 4157859 commit 717e92e
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 126 deletions.
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-gallery-3d",
"private": false,
"version": "2.1.0",
"version": "2.2.0-beta.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down Expand Up @@ -39,20 +39,24 @@
"module": "./dist/react-gallery-3d.es.js",
"types": "./dist/index.d.ts",
"dependencies": {
"three-csg-ts": "^3.1.13"
"three-csg-ts": "^3.1.13",
"uuid": "^9.0.1"
},
"peerDependencies": {
"react": ">=18.2.0",
"react-dom": ">=18.2.0",
"@react-three/drei": ">=9.88.11",
"@react-three/fiber": ">=8.15.10",
"react": ">=18.2.0",
"react-dom": ">=18.2.0",
"three": ">=0.158.0"
},
"devDependencies": {
"@react-three/drei": "^9.88.11",
"@react-three/fiber": "^8.15.10",
"@types/node": "^20.9.0",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@types/three": "^0.158.0",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.3",
Expand All @@ -67,14 +71,12 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hooks": "^1.0.1",
"three": "^0.158.0",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vite-plugin-dts": "^3.6.3",
"vite-plugin-linter": "^2.0.7",
"vite-tsconfig-paths": "^4.2.1",
"@react-three/drei": "^9.88.11",
"@react-three/fiber": "^8.15.10",
"three": "^0.158.0"
"vite-tsconfig-paths": "^4.2.1"
},
"repository": {
"type": "git",
Expand Down
58 changes: 23 additions & 35 deletions src/components/Gallery/Gallery.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useMemo } from "react";
import GalleryContext from "./GalleryContext";
import GalleryItemContext from "../GalleryItem/GalleryItemContext";
import { AllowedGalleryItemTypes, GalleryProps } from "./Gallery.types";
import React, { useCallback, useMemo, useState } from "react";
import { GalleryContext } from "./GalleryContext";
import { GalleryProps } from "./Gallery.types";
import { Group } from "three";

/**
Expand All @@ -12,9 +11,7 @@ import { Group } from "three";
* @param item The gallery item properties.
*/
export const Gallery = React.forwardRef<Group, GalleryProps>(({ children, item, ...rest }, ref) => {
if (children.length < 3) {
throw new Error("At least 3 Gallery Items are required");
}
const [itemsId, setItemsId] = useState<string[]>([]);

const {
width = 120,
Expand All @@ -24,28 +21,27 @@ export const Gallery = React.forwardRef<Group, GalleryProps>(({ children, item,
innerRadiusPercent = 0.01,
} = item || {};

/**
* Gets the children that are allowed to be rendered.
*/
const validChildren = useMemo(() => {
return children.filter((child) => {
if (!AllowedGalleryItemTypes.includes(child.type)) {
if (Array.isArray(child)) {
return child.every((subChild) => {
return AllowedGalleryItemTypes.includes(subChild.type);
});
}
const registerItem = useCallback((id: string) => {
setItemsId((prevItems) => {
if (prevItems.includes(id)) {
// eslint-disable-next-line no-console
console.warn(`GalleryItem id is already registered: "${id}"`);
return prevItems;
}

return true;
return [...prevItems, id];
});
}, [children]);
}, []);

const unregisterItem = useCallback((id: string) => {
setItemsId((prevItems) => prevItems.filter((i) => i !== id));
}, []);

/**
* Calculates the section angle, outer radius, and inner radius.
*/
const { sectionAngle, outerRadius, innerRadius } = useMemo(() => {
const sides = validChildren.length;
const sides = itemsId.length;
const sectionAngle = (2 * Math.PI) / sides;
const outerRadius = width / 2;
const innerRadius = outerRadius - outerRadius * innerRadiusPercent;
Expand All @@ -55,12 +51,15 @@ export const Gallery = React.forwardRef<Group, GalleryProps>(({ children, item,
outerRadius,
innerRadius,
};
}, [validChildren.length, width, innerRadiusPercent]);
}, [itemsId.length, width, innerRadiusPercent]);

return (
<GalleryContext.Provider
value={{
itemCount: validChildren.length,
itemsId,
registerItem,
unregisterItem,
itemCount: itemsId.length,
item: {
width,
height,
Expand All @@ -74,18 +73,7 @@ export const Gallery = React.forwardRef<Group, GalleryProps>(({ children, item,
}}
>
<group ref={ref} {...rest}>
{validChildren.map((child, index) => {
return (
<GalleryItemContext.Provider
key={index}
value={{
itemIndex: index,
}}
>
{child}
</GalleryItemContext.Provider>
);
})}
{children}
</group>
</GalleryContext.Provider>
);
Expand Down
55 changes: 28 additions & 27 deletions src/components/Gallery/Gallery.types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { GroupProps } from "@react-three/fiber";
import { ReactElement } from "react";
import { GalleryItem, GalleryItemProps } from "../GalleryItem";
import { SolidColorItem, SolidColorItemProps } from "../SolidColorItem";
import { ImageItem, ImageItemProps } from "../ImageItem";
import { VideoItem, VideoItemProps } from "../VideoItem";
import { TransparentItem, TransparentItemProps } from "../TransparentItem";
import { ObjectItem, ObjectItemProps } from "../ObjectItem";

/**
* A flag to indicate that no provider was found.
Expand All @@ -22,6 +16,27 @@ export type GalleryState = {
*/
itemCount: number;

/**
* The gallery items ids.
*/
itemsId: string[];

/**
* Registers a gallery item.
* This function is called by the gallery item component to register itself.
*
* @param id The id of the gallery item.
*/
registerItem: (id: string) => void;

/**
* Unregisters a gallery item.
* This function is called by the gallery item component to unregister itself.
*
* @param id The id of the gallery item.
*/
unregisterItem: (id: string) => void;

/**
* The gallery item properties.
*/
Expand Down Expand Up @@ -75,38 +90,24 @@ export type GalleryState = {
};

/**
* The valid gallery item types.
* These are the types that can be used as children of the Gallery component.
* Any other type will be ignored.
* The useGallery hook return type.
*/
export const AllowedGalleryItemTypes = [
GalleryItem,
SolidColorItem,
ImageItem,
VideoItem,
TransparentItem,
ObjectItem,
];
export type UseGalleryReturnType = Omit<
GalleryState,
"itemsId" | "registerItem" | "unregisterItem"
>;

/**
* The gallery item type.
*/
export type GalleryItemType =
| ReactElement<GalleryItemProps, typeof GalleryItem>
| ReactElement<SolidColorItemProps, typeof SolidColorItem>
| ReactElement<ImageItemProps, typeof ImageItem>
| ReactElement<VideoItemProps, typeof VideoItem>
| ReactElement<TransparentItemProps, typeof TransparentItem>
| ReactElement<ObjectItemProps, typeof ObjectItem>;
export type GalleryItemType = ReactElement;

/**
* The gallery children type.
* This is the type of the children of the Gallery component.
* At least 3 gallery items are required.
*/
export type GalleryChildren =
| [GalleryItemType, GalleryItemType, GalleryItemType, ...GalleryItemType[]]
| GalleryItemType[];
export type GalleryChildren = GalleryItemType | Iterable<GalleryItemType>;

/**
* The Gallery component properties.
Expand Down
4 changes: 1 addition & 3 deletions src/components/Gallery/GalleryContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { GalleryState, GALLERY_NO_PROVIDER_FLAG } from "./Gallery.types";
/**
* This context is used to provide the gallery state to the gallery components.
*/
const GalleryContext = createContext<GalleryState | typeof GALLERY_NO_PROVIDER_FLAG>(
export const GalleryContext = createContext<GalleryState | typeof GALLERY_NO_PROVIDER_FLAG>(
GALLERY_NO_PROVIDER_FLAG,
);

export default GalleryContext;
50 changes: 39 additions & 11 deletions src/components/Gallery/useGallery.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,54 @@
import { useContext } from "react";
import GalleryContext from "./GalleryContext";
import { GALLERY_NO_PROVIDER_FLAG, GalleryState } from "./Gallery.types";
import GalleryItemContext from "../GalleryItem/GalleryItemContext";
import { GalleryContext } from "./GalleryContext";
import { GALLERY_NO_PROVIDER_FLAG, UseGalleryReturnType } from "./Gallery.types";
import { GalleryItemContext } from "../GalleryItem/GalleryItemContext";
import { GALLERY_ITEM_NO_PROVIDER_FLAG } from "../GalleryItem";

/**
* A hook to get the gallery data.
* This hook must be called within a Gallery component.
*
* @returns {GalleryState} The gallery state data.
* @returns {UseGalleryReturnType} The gallery state data.
*/
export const useGallery = (): GalleryState => {
const data = useContext(GalleryContext);
export const useGallery = (): UseGalleryReturnType => {
const galleryState = useContext(GalleryContext);

if (data === GALLERY_NO_PROVIDER_FLAG) {
if (galleryState === GALLERY_NO_PROVIDER_FLAG) {
throw new Error("useGallery must be called within a Gallery");
}

const itemData = useContext(GalleryItemContext);
if (itemData !== GALLERY_ITEM_NO_PROVIDER_FLAG) {
data.item.itemIndex = itemData.itemIndex;
const galleryItemState = useContext(GalleryItemContext);
if (galleryItemState !== GALLERY_ITEM_NO_PROVIDER_FLAG) {
galleryState.item.itemIndex = galleryItemState.itemIndex;
}

return data;
const {
itemCount,
item: {
width,
height,
radialSegments,
heightSegments,
innerRadiusPercent,
sectionAngle,
outerRadius,
innerRadius,
itemIndex,
},
} = galleryState;

return {
itemCount,
item: {
width,
height,
radialSegments,
heightSegments,
innerRadiusPercent,
sectionAngle,
outerRadius,
innerRadius,
itemIndex,
},
};
};
Loading

0 comments on commit 717e92e

Please sign in to comment.