Files
TheGame/packages/web/components/Carousel/Track.tsx
δυς 9e37df6207 Update MyMeta to ECMAScript Modules + Switch to DID:PKH (#1429)
* beginning ESM transition: Ceramic libraries, Next.js, & TypeScript configuration 🇭🇰

* updating Chakra, React, & Next image `import`s 👔

* upgrading `@types/react`, import extensions for Node, & b64 SVG to PNG ⛹🏿‍♀️

* fixing relative import names & upddating @types packages 📻

* removoing WYSIWYG editor, draft-js, & updating express ⛹🏿‍♀️

* updating OpenSea 🚲

* ¡@metafam/utils is building! 📰

* ¡Discord bot is building! 👘

* ¡backend is building! 🛩

* fixed everything but Ceramic DID update 🏍

* switching to DID:PKH 📦

* fixing "only one child allowed" error 🙇🏿‍♀️

* importing `React` as required by tsc's `isolatedModules` 🇲🇰

* disabling testing rather than taking the time to fix jest ⚜

* removing set `types` from `tsconfig` to fix compilation error 🥦

* printing tests disabled warning, hopefully 🙀

* setting file to be copied to the new resolver 👁️‍🗨️

* "paths-resolver" not "paths-resolve" 🦴

* switching back to relative paths rather than trying to fix `paths` 

* `yarn backend:dev` not working, testing GitHub build 🎺

* removing design system build & fixing some images ✊🏿

* fixed "expected function got string" error & trying to address undefined HTMLElement 🐡

* fixing @emotion/react tree shaking by making external 🏏

* including eslint config in Dockerfile 🌾

* fixing more images 🎯

* updating DIDs & switching back to an updated DID:3 ❇

* switching to w3s.link gateway & fixing early termination of storage endpoint 🔭

* switching back to ipfs.io gateway b/c w3s.link serves SVGs as application/xml which are CORB blocked 🥾

* fixing node config name in eslint ignore & shortening some paths 🧰

* fixing ts-node not handling project references 🥁
2022-11-14 11:26:41 -05:00

173 lines
4.4 KiB
TypeScript

import type { PanInfo } from '@metafam/ds';
import {
Flex,
motion,
useAnimation,
useBreakpointValue,
useMotionValue,
VStack,
} from '@metafam/ds';
import type { PropsWithChildren } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useCarouselContext } from './CarouselContext';
const MotionFlex = motion(Flex);
const transitionProps = {
stiffness: 400,
type: 'spring',
damping: 60,
mass: 3,
};
export const Track: React.FC<PropsWithChildren> = ({ children }) => {
const {
setDragging,
setTrackIsActive,
trackIsActive,
isSubmittingProof,
setActiveItem,
activeItem,
constraint,
multiplier,
itemWidth,
positions,
} = useCarouselContext();
const [dragStartPosition, setDragStartPosition] = useState(0);
const controls = useAnimation();
const x = useMotionValue(0);
const node = useRef<HTMLDivElement>(null);
const isMobile = useBreakpointValue({ base: true, lg: false });
const handleDragStart = () => {
setDragging(true);
setDragStartPosition(positions[activeItem]);
};
// 2022-10-25: This method relied on on a type imported from
// Framer that no longer exists.
const handleDragEnd = (_evt: DragEvent, info: PanInfo) => {
const distance = info.offset.x;
const velocity = info.velocity.x * multiplier;
const direction = velocity < 0 || distance < 0 ? 1 : -1;
const extrapolatedPosition =
dragStartPosition +
(direction === 1
? Math.min(velocity, distance)
: Math.max(velocity, distance));
const closestPosition = positions.reduce(
(prev: number, curr: number) =>
Math.abs(curr - extrapolatedPosition) <
Math.abs(prev - extrapolatedPosition)
? curr
: prev,
0,
);
if (!(closestPosition < positions[positions.length - constraint])) {
setActiveItem(positions.indexOf(closestPosition));
controls.start({
x: closestPosition,
transition: {
velocity: info.velocity.x,
...transitionProps,
},
});
} else {
setActiveItem(positions.length - constraint);
controls.start({
x: positions[positions.length - constraint],
transition: {
velocity: info.velocity.x,
...transitionProps,
},
});
}
setDragging(false);
};
const handleResize = useCallback(
() =>
controls.start({
x: positions[activeItem],
transition: {
...transitionProps,
},
}),
[activeItem, controls, positions],
);
const handleClick = useCallback(
(event: MouseEvent) =>
node?.current?.contains(event.target as Node)
? setTrackIsActive(true)
: setTrackIsActive(false),
[setTrackIsActive],
);
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
if (trackIsActive && !isSubmittingProof) {
if (activeItem < positions.length - constraint) {
if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {
event.preventDefault();
setActiveItem((prev: number) => prev + 1);
}
}
if (activeItem > positions.length - positions.length) {
if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {
event.preventDefault();
setActiveItem((prev: number) => prev - 1);
}
}
}
},
[
trackIsActive,
isSubmittingProof,
activeItem,
positions.length,
constraint,
setActiveItem,
],
);
useEffect(() => {
handleResize();
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('mousedown', handleClick);
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('mousedown', handleClick);
};
}, [handleClick, handleResize, handleKeyDown, positions]);
return (
<>
{itemWidth && (
<VStack ref={node} spacing={5} alignItems="stretch">
<MotionFlex
dragConstraints={node}
onDragStart={isMobile ? undefined : handleDragStart}
onDragEnd={isMobile ? undefined : handleDragEnd}
animate={controls}
style={{ x }}
drag="x"
_active={{ cursor: 'grabbing' }}
minWidth="min-content"
flexWrap="nowrap"
cursor="grab"
>
{children}
</MotionFlex>
</VStack>
)}
</>
);
};