react-orbit-component
Version:
A Orbit component for React.
1 lines • 8.62 kB
Source Map (JSON)
{"version":3,"sources":["../src/components/orbit.path.tsx","../src/hooks/useWindowSize.tsx","../src/components/orbit.item.tsx","../src/utils/geometry.utils.ts"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { useWindowSize } from 'src/hooks/useWindowSize';\n\nexport type OrbitPathTypes = 'circle';\n\ninterface OrbitPathProps {\n className?: string;\n children?: React.ReactNode;\n type: OrbitPathTypes;\n style?: React.CSSProperties;\n}\n\n/**\n * @description\n * OrbitPath is a component that creates a circular path for OrbitItems to follow.\n * @property width & height - Should be the same size.\n * @param className - Optional class name to add to the component.\n * @param children - The OrbitItems to be placed on the path.\n * @param type - The type of path to create.\n * @example <caption>Creating a circular path</caption>\n * <OrbitPath type=\"circle\">\n * <OrbitItem></OrbitItem>\n * </OrbitPath>\n */\nexport const OrbitPath: React.FC<OrbitPathProps> = ({ className, children, type, style }) => {\n const pathRef = useRef<HTMLDivElement>(null);\n const [newChildren, setNewChildren] = useState<React.ReactNode[]>([]);\n const size = useWindowSize();\n\n useEffect(() => {\n if (!pathRef.current) return;\n\n const { offsetHeight: height, offsetWidth: width } = pathRef.current;\n\n if (width !== height) {\n console.error('OrbitPath must be a circle with equal height and width.');\n return;\n }\n\n const radius = height / 2;\n const childrenArray = React.Children.toArray(children);\n\n const updatedChildren = childrenArray.map((child, index) => {\n const childElement = child as React.ReactElement;\n const newProps = { ...childElement.props, radius };\n\n return React.cloneElement(childElement, { ...newProps, key: index });\n });\n\n setNewChildren(updatedChildren);\n }, [children, size]);\n\n return (\n <div\n style={{\n pointerEvents: 'none',\n } && style}\n className={className}\n ref={pathRef}\n >\n {newChildren}\n </div>\n );\n};\n","import { useEffect, useState } from 'react';\n\ninterface WindowSize {\n width: number | undefined;\n height: number | undefined;\n}\n\nexport const useWindowSize = (): WindowSize => {\n const [windowSize, setWindowSize] = useState<WindowSize>({\n width: undefined,\n height: undefined,\n });\n\n const handleResize = () => {\n setWindowSize({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n };\n\n useEffect(() => {\n window.addEventListener('resize', handleResize);\n handleResize();\n return () => window.removeEventListener('resize', handleResize);\n }, []);\n\n return windowSize;\n};\n","import React, { useEffect, useRef, forwardRef, useState } from 'react';\nimport { calculateCoordinatesOnCircle } from '../utils';\n\ntype OrbitItemDirection = 'clockwise' | 'counter-clockwise';\nexport type OrbitItemProps = {\n className?: string;\n children?: React.ReactNode;\n radius?: number;\n anglePerStep?: number;\n timeBetweenSteps?: number;\n startAngle?: number;\n direction?: OrbitItemDirection;\n style?: React.CSSProperties;\n angle?: number;\n} & React.HTMLAttributes<HTMLDivElement>;\n\n/**\n * OrbitItem is a component that moves around a circle path.\n * @param className - The class name of the component.\n * @param children - The children of the component.\n * @param radius - The radius of the circle used by the parent component.\n * @param anglePerStep - The angle per step in degrees.\n * @param timeBetweenSteps - The time between steps in milliseconds.\n * @param startAngle - The start angle.\n * @param direction - The direction of the orbit.\n * @param style - The style of the component.\n * @param angle - The angle of the component.\n * @param ref - The ref of the component.\n * @returns The component.\n * @example\n * ```tsx\n * <OrbitItem direction=\"clockwise\" startAngle={120} step={0.2} className={SHARED_CLASSNAME}>\n * 😀\n * </OrbitItem>\n */\nexport const OrbitItem = forwardRef<HTMLDivElement, OrbitItemProps>(\n (\n {\n className,\n children,\n radius = 0,\n anglePerStep = 0.1,\n timeBetweenSteps = 10,\n startAngle = 0,\n direction = 'clockwise',\n style,\n angle,\n ...rest\n },\n ref,\n ) => {\n const [angleState, setAngle] = useState(startAngle);\n const effectiveAngle = angle ?? angleState;\n const internalRef = useRef<HTMLDivElement>(null);\n const actualRef = (ref ?? internalRef) as React.MutableRefObject<HTMLDivElement>;\n\n useEffect(() => {\n const interval = setInterval(() => {\n setAngle((prevAngle) => (direction === 'clockwise' ? prevAngle + (anglePerStep % 360) : prevAngle - (anglePerStep % 360)));\n }, timeBetweenSteps);\n\n return () => clearInterval(interval);\n }, [anglePerStep, timeBetweenSteps]);\n\n const { x, y } = calculateCoordinatesOnCircle(radius, effectiveAngle, actualRef.current?.getBoundingClientRect() ?? new DOMRect());\n\n return (\n <div\n style={{\n position: 'absolute',\n top: `${y}px`,\n left: `${x}px`,\n pointerEvents: 'all',\n ...style,\n }}\n className={className}\n ref={actualRef}\n {...rest}\n >\n {children}\n </div>\n );\n },\n);\n","const DEGREE_TO_RADIAN_FACTOR = Math.PI / 180;\n\nexport const calculateCoordinatesOnCircle = (radius: number, angle: number, boundingClientRect: DOMRect): { x: number; y: number } => {\n const angleInRadians = angle * DEGREE_TO_RADIAN_FACTOR;\n const { width, height } = boundingClientRect;\n const x = radius + (radius * Math.cos(angleInRadians));\n const y = radius + (radius * Math.sin(angleInRadians));\n const centerX = x - width / 2;\n const centerY = y - height / 2;\n return {\n x: centerX,\n y: centerY,\n };\n};\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","React","useEffect","useRef","useState","_a","_b","useState","useRef","useEffect","React"]}