Skip to content

Commit dab207e

Browse files
committed
fix: update CardRef type to allow null and initialize intervalRef to 0
1 parent b5cbc31 commit dab207e

File tree

4 files changed

+6
-6
lines changed

4 files changed

+6
-6
lines changed

public/r/CardSwap-TS-CSS.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"files": [
1111
{
1212
"path": "public/ts/default/src/ts-default/Components/CardSwap/CardSwap.tsx",
13-
"content": "import React, {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n ReactElement,\n ReactNode,\n RefObject,\n useEffect,\n useMemo,\n useRef\n} from 'react';\nimport gsap from 'gsap';\nimport './CardSwap.css';\n\nexport interface CardSwapProps {\n width?: number | string;\n height?: number | string;\n cardDistance?: number;\n verticalDistance?: number;\n delay?: number;\n pauseOnHover?: boolean;\n onCardClick?: (idx: number) => void;\n skewAmount?: number;\n easing?: 'linear' | 'elastic';\n children: ReactNode;\n}\n\nexport interface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n customClass?: string;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(({ customClass, ...rest }, ref) => (\n <div ref={ref} {...rest} className={`card ${customClass ?? ''} ${rest.className ?? ''}`.trim()} />\n));\nCard.displayName = 'Card';\n\ntype CardRef = RefObject<HTMLDivElement>;\ninterface Slot {\n x: number;\n y: number;\n z: number;\n zIndex: number;\n}\n\nconst makeSlot = (i: number, distX: number, distY: number, total: number): Slot => ({\n x: i * distX,\n y: -i * distY,\n z: -i * distX * 1.5,\n zIndex: total - i\n});\n\nconst placeNow = (el: HTMLElement, slot: Slot, skew: number) =>\n gsap.set(el, {\n x: slot.x,\n y: slot.y,\n z: slot.z,\n xPercent: -50,\n yPercent: -50,\n skewY: skew,\n transformOrigin: 'center center',\n zIndex: slot.zIndex,\n force3D: true\n });\n\nconst CardSwap: React.FC<CardSwapProps> = ({\n width = 500,\n height = 400,\n cardDistance = 60,\n verticalDistance = 70,\n delay = 5000,\n pauseOnHover = false,\n onCardClick,\n skewAmount = 6,\n easing = 'elastic',\n children\n}) => {\n const config =\n easing === 'elastic'\n ? {\n ease: 'elastic.out(0.6,0.9)',\n durDrop: 2,\n durMove: 2,\n durReturn: 2,\n promoteOverlap: 0.9,\n returnDelay: 0.05\n }\n : {\n ease: 'power1.inOut',\n durDrop: 0.8,\n durMove: 0.8,\n durReturn: 0.8,\n promoteOverlap: 0.45,\n returnDelay: 0.2\n };\n\n const childArr = useMemo(() => Children.toArray(children) as ReactElement<CardProps>[], [children]);\n const refs = useMemo<CardRef[]>(() => childArr.map(() => React.createRef<HTMLDivElement>()), [childArr.length]);\n\n const order = useRef<number[]>(Array.from({ length: childArr.length }, (_, i) => i));\n\n const tlRef = useRef<gsap.core.Timeline | null>(null);\n const intervalRef = useRef<number>();\n const container = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const total = refs.length;\n refs.forEach((r, i) => placeNow(r.current!, makeSlot(i, cardDistance, verticalDistance, total), skewAmount));\n\n const swap = () => {\n if (order.current.length < 2) return;\n\n const [front, ...rest] = order.current;\n const elFront = refs[front].current!;\n const tl = gsap.timeline();\n tlRef.current = tl;\n\n tl.to(elFront, {\n y: '+=500',\n duration: config.durDrop,\n ease: config.ease\n });\n\n tl.addLabel('promote', `-=${config.durDrop * config.promoteOverlap}`);\n rest.forEach((idx, i) => {\n const el = refs[idx].current!;\n const slot = makeSlot(i, cardDistance, verticalDistance, refs.length);\n tl.set(el, { zIndex: slot.zIndex }, 'promote');\n tl.to(\n el,\n {\n x: slot.x,\n y: slot.y,\n z: slot.z,\n duration: config.durMove,\n ease: config.ease\n },\n `promote+=${i * 0.15}`\n );\n });\n\n const backSlot = makeSlot(refs.length - 1, cardDistance, verticalDistance, refs.length);\n tl.addLabel('return', `promote+=${config.durMove * config.returnDelay}`);\n tl.call(\n () => {\n gsap.set(elFront, { zIndex: backSlot.zIndex });\n },\n undefined,\n 'return'\n );\n tl.to(\n elFront,\n {\n x: backSlot.x,\n y: backSlot.y,\n z: backSlot.z,\n duration: config.durReturn,\n ease: config.ease\n },\n 'return'\n );\n\n tl.call(() => {\n order.current = [...rest, front];\n });\n };\n\n swap();\n intervalRef.current = window.setInterval(swap, delay);\n\n if (pauseOnHover) {\n const node = container.current!;\n const pause = () => {\n tlRef.current?.pause();\n clearInterval(intervalRef.current);\n };\n const resume = () => {\n tlRef.current?.play();\n intervalRef.current = window.setInterval(swap, delay);\n };\n node.addEventListener('mouseenter', pause);\n node.addEventListener('mouseleave', resume);\n return () => {\n node.removeEventListener('mouseenter', pause);\n node.removeEventListener('mouseleave', resume);\n clearInterval(intervalRef.current);\n };\n }\n return () => clearInterval(intervalRef.current);\n }, [cardDistance, verticalDistance, delay, pauseOnHover, skewAmount, easing]);\n\n const rendered = childArr.map((child, i) =>\n isValidElement<CardProps>(child)\n ? cloneElement(child, {\n key: i,\n ref: refs[i],\n style: { width, height, ...(child.props.style ?? {}) },\n onClick: e => {\n child.props.onClick?.(e as React.MouseEvent<HTMLDivElement>);\n onCardClick?.(i);\n }\n } as CardProps & React.RefAttributes<HTMLDivElement>)\n : child\n );\n\n return (\n <div ref={container} className=\"card-swap-container\" style={{ width, height }}>\n {rendered}\n </div>\n );\n};\n\nexport default CardSwap;\n",
13+
"content": "import React, {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n ReactElement,\n ReactNode,\n RefObject,\n useEffect,\n useMemo,\n useRef\n} from 'react';\nimport gsap from 'gsap';\nimport './CardSwap.css';\n\nexport interface CardSwapProps {\n width?: number | string;\n height?: number | string;\n cardDistance?: number;\n verticalDistance?: number;\n delay?: number;\n pauseOnHover?: boolean;\n onCardClick?: (idx: number) => void;\n skewAmount?: number;\n easing?: 'linear' | 'elastic';\n children: ReactNode;\n}\n\nexport interface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n customClass?: string;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(({ customClass, ...rest }, ref) => (\n <div ref={ref} {...rest} className={`card ${customClass ?? ''} ${rest.className ?? ''}`.trim()} />\n));\nCard.displayName = 'Card';\n\ntype CardRef = RefObject<HTMLDivElement | null>;\ninterface Slot {\n x: number;\n y: number;\n z: number;\n zIndex: number;\n}\n\nconst makeSlot = (i: number, distX: number, distY: number, total: number): Slot => ({\n x: i * distX,\n y: -i * distY,\n z: -i * distX * 1.5,\n zIndex: total - i\n});\n\nconst placeNow = (el: HTMLElement, slot: Slot, skew: number) =>\n gsap.set(el, {\n x: slot.x,\n y: slot.y,\n z: slot.z,\n xPercent: -50,\n yPercent: -50,\n skewY: skew,\n transformOrigin: 'center center',\n zIndex: slot.zIndex,\n force3D: true\n });\n\nconst CardSwap: React.FC<CardSwapProps> = ({\n width = 500,\n height = 400,\n cardDistance = 60,\n verticalDistance = 70,\n delay = 5000,\n pauseOnHover = false,\n onCardClick,\n skewAmount = 6,\n easing = 'elastic',\n children\n}) => {\n const config =\n easing === 'elastic'\n ? {\n ease: 'elastic.out(0.6,0.9)',\n durDrop: 2,\n durMove: 2,\n durReturn: 2,\n promoteOverlap: 0.9,\n returnDelay: 0.05\n }\n : {\n ease: 'power1.inOut',\n durDrop: 0.8,\n durMove: 0.8,\n durReturn: 0.8,\n promoteOverlap: 0.45,\n returnDelay: 0.2\n };\n\n const childArr = useMemo(() => Children.toArray(children) as ReactElement<CardProps>[], [children]);\n const refs = useMemo<CardRef[]>(() => childArr.map(() => React.createRef<HTMLDivElement>()), [childArr.length]);\n\n const order = useRef<number[]>(Array.from({ length: childArr.length }, (_, i) => i));\n\n const tlRef = useRef<gsap.core.Timeline | null>(null);\n const intervalRef = useRef<number>(0);\n const container = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const total = refs.length;\n refs.forEach((r, i) => placeNow(r.current!, makeSlot(i, cardDistance, verticalDistance, total), skewAmount));\n\n const swap = () => {\n if (order.current.length < 2) return;\n\n const [front, ...rest] = order.current;\n const elFront = refs[front].current!;\n const tl = gsap.timeline();\n tlRef.current = tl;\n\n tl.to(elFront, {\n y: '+=500',\n duration: config.durDrop,\n ease: config.ease\n });\n\n tl.addLabel('promote', `-=${config.durDrop * config.promoteOverlap}`);\n rest.forEach((idx, i) => {\n const el = refs[idx].current!;\n const slot = makeSlot(i, cardDistance, verticalDistance, refs.length);\n tl.set(el, { zIndex: slot.zIndex }, 'promote');\n tl.to(\n el,\n {\n x: slot.x,\n y: slot.y,\n z: slot.z,\n duration: config.durMove,\n ease: config.ease\n },\n `promote+=${i * 0.15}`\n );\n });\n\n const backSlot = makeSlot(refs.length - 1, cardDistance, verticalDistance, refs.length);\n tl.addLabel('return', `promote+=${config.durMove * config.returnDelay}`);\n tl.call(\n () => {\n gsap.set(elFront, { zIndex: backSlot.zIndex });\n },\n undefined,\n 'return'\n );\n tl.to(\n elFront,\n {\n x: backSlot.x,\n y: backSlot.y,\n z: backSlot.z,\n duration: config.durReturn,\n ease: config.ease\n },\n 'return'\n );\n\n tl.call(() => {\n order.current = [...rest, front];\n });\n };\n\n swap();\n intervalRef.current = window.setInterval(swap, delay);\n\n if (pauseOnHover) {\n const node = container.current!;\n const pause = () => {\n tlRef.current?.pause();\n clearInterval(intervalRef.current);\n };\n const resume = () => {\n tlRef.current?.play();\n intervalRef.current = window.setInterval(swap, delay);\n };\n node.addEventListener('mouseenter', pause);\n node.addEventListener('mouseleave', resume);\n return () => {\n node.removeEventListener('mouseenter', pause);\n node.removeEventListener('mouseleave', resume);\n clearInterval(intervalRef.current);\n };\n }\n return () => clearInterval(intervalRef.current);\n }, [cardDistance, verticalDistance, delay, pauseOnHover, skewAmount, easing]);\n\n const rendered = childArr.map((child, i) =>\n isValidElement<CardProps>(child)\n ? cloneElement(child, {\n key: i,\n ref: refs[i],\n style: { width, height, ...(child.props.style ?? {}) },\n onClick: e => {\n child.props.onClick?.(e as React.MouseEvent<HTMLDivElement>);\n onCardClick?.(i);\n }\n } as CardProps & React.RefAttributes<HTMLDivElement>)\n : child\n );\n\n return (\n <div ref={container} className=\"card-swap-container\" style={{ width, height }}>\n {rendered}\n </div>\n );\n};\n\nexport default CardSwap;\n",
1414
"type": "registry:component"
1515
},
1616
{

0 commit comments

Comments
 (0)