Add species-face picker to item page previews
Note that it doesn't do any compatibility checking, graying out, hiding unneeded faces, etc. They just exist now is all!
This commit is contained in:
parent
7092d86b76
commit
f50de9b11e
1 changed files with 528 additions and 75 deletions
|
@ -14,6 +14,10 @@ import {
|
|||
useColorModeValue,
|
||||
useTheme,
|
||||
useToast,
|
||||
useToken,
|
||||
Stack,
|
||||
Wrap,
|
||||
WrapItem,
|
||||
} from "@chakra-ui/react";
|
||||
import {
|
||||
CheckIcon,
|
||||
|
@ -27,7 +31,7 @@ import { useQuery, useMutation } from "@apollo/client";
|
|||
import { Link, useParams } from "react-router-dom";
|
||||
|
||||
import ItemPageLayout, { SubtleSkeleton } from "./ItemPageLayout";
|
||||
import { Delay, usePageTitle } from "./util";
|
||||
import { Delay, ErrorMessage, usePageTitle } from "./util";
|
||||
import {
|
||||
itemAppearanceFragment,
|
||||
petAppearanceFragment,
|
||||
|
@ -601,90 +605,539 @@ function ItemPageOutfitPreview({ itemId }) {
|
|||
const isIncompatible = Array.isArray(layers) && layers.length === 0;
|
||||
|
||||
return (
|
||||
<VStack spacing="3" width="100%">
|
||||
<AspectRatio
|
||||
width="300px"
|
||||
maxWidth="100%"
|
||||
ratio="1"
|
||||
border="1px"
|
||||
borderColor={borderColor}
|
||||
transition="border-color 0.2s"
|
||||
borderRadius="lg"
|
||||
boxShadow="lg"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Box>
|
||||
<OutfitPreview
|
||||
<Stack direction={{ base: "column", md: "row" }} spacing="8">
|
||||
<VStack spacing="3" width="100%">
|
||||
<AspectRatio
|
||||
width="300px"
|
||||
maxWidth="100%"
|
||||
ratio="1"
|
||||
border="1px"
|
||||
borderColor={borderColor}
|
||||
transition="border-color 0.2s"
|
||||
borderRadius="lg"
|
||||
boxShadow="lg"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Box>
|
||||
<OutfitPreview
|
||||
speciesId={petState.speciesId}
|
||||
colorId={petState.colorId}
|
||||
pose={petState.pose}
|
||||
appearanceId={petState.appearanceId}
|
||||
wornItemIds={[itemId]}
|
||||
isLoading={loading}
|
||||
spinnerVariant="corner"
|
||||
loadingDelayMs={2000}
|
||||
engine="canvas"
|
||||
onChangeHasAnimations={setHasAnimations}
|
||||
/>
|
||||
{hasAnimations && (
|
||||
<PlayPauseButton
|
||||
isPaused={isPaused}
|
||||
onClick={() => setIsPaused(!isPaused)}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</AspectRatio>
|
||||
<Box display="flex" width="100%" alignItems="center">
|
||||
<Box
|
||||
// This empty box grows at the same rate as the box on the right, so
|
||||
// the middle box will be centered, if there's space!
|
||||
flex="1 0 0"
|
||||
/>
|
||||
<SpeciesColorPicker
|
||||
speciesId={petState.speciesId}
|
||||
colorId={petState.colorId}
|
||||
pose={petState.pose}
|
||||
appearanceId={petState.appearanceId}
|
||||
wornItemIds={[itemId]}
|
||||
isLoading={loading}
|
||||
spinnerVariant="corner"
|
||||
loadingDelayMs={2000}
|
||||
engine="canvas"
|
||||
onChangeHasAnimations={setHasAnimations}
|
||||
idealPose={idealPose}
|
||||
onChange={(species, color, _, closestPose) => {
|
||||
setPetState({
|
||||
speciesId: species.id,
|
||||
colorId: color.id,
|
||||
pose: closestPose,
|
||||
appearanceId: null,
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
showPlaceholders
|
||||
// This is just a UX affordance: while we could handle invalid states
|
||||
// from a UI perspective, we figure that, if a pet preview is already
|
||||
// visible and responsive to changes, it feels better to treat the
|
||||
// changes as atomic and always-valid.
|
||||
stateMustAlwaysBeValid
|
||||
/>
|
||||
{hasAnimations && (
|
||||
<IconButton
|
||||
icon={isPaused ? <MdPlayArrow /> : <MdPause />}
|
||||
aria-label={isPaused ? "Play" : "Pause"}
|
||||
onClick={() => setIsPaused(!isPaused)}
|
||||
borderRadius="full"
|
||||
boxShadow="md"
|
||||
color="gray.50"
|
||||
backgroundColor="blackAlpha.700"
|
||||
position="absolute"
|
||||
bottom="2"
|
||||
left="2"
|
||||
_hover={{ backgroundColor: "blackAlpha.900" }}
|
||||
_focus={{ backgroundColor: "blackAlpha.900" }}
|
||||
/>
|
||||
)}
|
||||
<Box flex="1 0 0" lineHeight="1">
|
||||
{isIncompatible && (
|
||||
<Tooltip label="No data yet" placement="top">
|
||||
<WarningIcon
|
||||
color={errorColor}
|
||||
transition="color 0.2"
|
||||
marginLeft="2"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</AspectRatio>
|
||||
<Box display="flex" width="100%" alignItems="center">
|
||||
<Box
|
||||
// This empty box grows at the same rate as the box on the right, so
|
||||
// the middle box will be centered, if there's space!
|
||||
flex="1 0 0"
|
||||
/>
|
||||
<SpeciesColorPicker
|
||||
speciesId={petState.speciesId}
|
||||
colorId={petState.colorId}
|
||||
pose={petState.pose}
|
||||
idealPose={idealPose}
|
||||
onChange={(species, color, _, closestPose) => {
|
||||
</VStack>
|
||||
<Box maxWidth="400px">
|
||||
<SpeciesFacesPicker
|
||||
itemId={itemId}
|
||||
selectedSpeciesId={petState.speciesId}
|
||||
onChange={({ speciesId, colorId }) =>
|
||||
setPetState({
|
||||
speciesId: species.id,
|
||||
colorId: color.id,
|
||||
pose: closestPose,
|
||||
speciesId,
|
||||
colorId,
|
||||
pose: idealPose,
|
||||
appearanceId: null,
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
showPlaceholders
|
||||
// This is just a UX affordance: while we could handle invalid states
|
||||
// from a UI perspective, we figure that, if a pet preview is already
|
||||
// visible and responsive to changes, it feels better to treat the
|
||||
// changes as atomic and always-valid.
|
||||
stateMustAlwaysBeValid
|
||||
})
|
||||
}
|
||||
isLoading={loading}
|
||||
/>
|
||||
<Box flex="1 0 0" lineHeight="1">
|
||||
{isIncompatible && (
|
||||
<Tooltip label="No data yet" placement="top">
|
||||
<WarningIcon
|
||||
color={errorColor}
|
||||
transition="color 0.2"
|
||||
marginLeft="2"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</VStack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function PlayPauseButton({ isPaused, onClick }) {
|
||||
return (
|
||||
<IconButton
|
||||
icon={isPaused ? <MdPlayArrow /> : <MdPause />}
|
||||
aria-label={isPaused ? "Play" : "Pause"}
|
||||
onClick={onClick}
|
||||
borderRadius="full"
|
||||
boxShadow="md"
|
||||
color="gray.50"
|
||||
backgroundColor="blackAlpha.700"
|
||||
position="absolute"
|
||||
bottom="2"
|
||||
left="2"
|
||||
_hover={{ backgroundColor: "blackAlpha.900" }}
|
||||
_focus={{ backgroundColor: "blackAlpha.900" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SpeciesFacesPicker({
|
||||
itemId,
|
||||
selectedSpeciesId,
|
||||
onChange,
|
||||
isLoading,
|
||||
}) {
|
||||
const selectedBorderColor = useColorModeValue("green.600", "green.400");
|
||||
const selectedBackgroundColor = useColorModeValue("green.200", "green.600");
|
||||
const [
|
||||
selectedBorderColorValue,
|
||||
selectedBackgroundColorValue,
|
||||
] = useToken("colors", [selectedBorderColor, selectedBackgroundColor]);
|
||||
|
||||
const allSpeciesFaces = speciesFaces.sort((a, b) =>
|
||||
a.speciesName.localeCompare(b.speciesName)
|
||||
);
|
||||
|
||||
return (
|
||||
<ClassNames>
|
||||
{({ css }) => (
|
||||
<Wrap
|
||||
spacing="0"
|
||||
justify="center"
|
||||
// On mobile, give this a scroll container, and some extra padding so
|
||||
// the selected-face effects still fit inside.
|
||||
maxHeight={{ base: "200px", md: "none" }}
|
||||
overflow={{ base: "auto", md: "visible" }}
|
||||
padding={{ base: "8px", md: "0" }}
|
||||
>
|
||||
{allSpeciesFaces.map(({ speciesId, speciesName, colorId, src }) => (
|
||||
<WrapItem
|
||||
key={speciesId}
|
||||
as="label"
|
||||
cursor={isLoading ? "wait" : "pointer"}
|
||||
position="relative"
|
||||
>
|
||||
<VisuallyHidden
|
||||
as="input"
|
||||
type="radio"
|
||||
aria-label={speciesName}
|
||||
name="species-faces-picker"
|
||||
value={speciesId}
|
||||
checked={speciesId === selectedSpeciesId}
|
||||
disabled={isLoading}
|
||||
onChange={() => onChange({ speciesId, colorId })}
|
||||
/>
|
||||
<Box
|
||||
overflow="hidden"
|
||||
transition="all 0.2s"
|
||||
className={css`
|
||||
input:checked + & {
|
||||
background: ${selectedBackgroundColorValue};
|
||||
border-radius: 6px;
|
||||
box-shadow: ${selectedBorderColorValue} 0 0 0 3px;
|
||||
transform: scale(1.2);
|
||||
z-index: 1;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Box
|
||||
as="img"
|
||||
src={src}
|
||||
alt={speciesName}
|
||||
width={50}
|
||||
height={50}
|
||||
filter="saturate(90%)"
|
||||
opacity="0.9"
|
||||
transition="all 0.2s"
|
||||
className={css`
|
||||
input:checked + * > & {
|
||||
opacity: 1;
|
||||
filter: saturate(110%);
|
||||
}
|
||||
`}
|
||||
/>
|
||||
</Box>
|
||||
</WrapItem>
|
||||
))}
|
||||
</Wrap>
|
||||
)}
|
||||
</ClassNames>
|
||||
);
|
||||
}
|
||||
|
||||
// HACK: I'm just hardcoding all this, rather than connecting up to the
|
||||
// database and adding a loading state. Tbh I'm not sure it's a good idea
|
||||
// to load this dynamically until we have SSR to make it come in fast!
|
||||
// And it's not so bad if this gets out of sync with the database,
|
||||
// because the SpeciesColorPicker will still be usable!
|
||||
const colors = { BLUE: "8", RED: "61", GREEN: "34", YELLOW: "84" };
|
||||
const speciesFaces = [
|
||||
{
|
||||
speciesId: "1",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/obxdjm88/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Acara",
|
||||
},
|
||||
{
|
||||
speciesId: "2",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/n9ozx4z5/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Aisha",
|
||||
},
|
||||
{
|
||||
speciesId: "3",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/kfonqhdc/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Blumaroo",
|
||||
},
|
||||
{
|
||||
speciesId: "4",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/sc2hhvhn/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Bori",
|
||||
},
|
||||
{
|
||||
speciesId: "5",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/wqz8xn4t/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Bruce",
|
||||
},
|
||||
{
|
||||
speciesId: "6",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/jc9klfxm/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Buzz",
|
||||
},
|
||||
{
|
||||
speciesId: "7",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/4lrb4n3f/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Chia",
|
||||
},
|
||||
{
|
||||
speciesId: "8",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/bdml26md/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Chomby",
|
||||
},
|
||||
{
|
||||
speciesId: "9",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/xl6msllv/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Cybunny",
|
||||
},
|
||||
{
|
||||
speciesId: "10",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/bob39shq/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Draik",
|
||||
},
|
||||
{
|
||||
speciesId: "11",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/jhhhbrww/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Elephante",
|
||||
},
|
||||
{
|
||||
speciesId: "12",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/6kngmhvs/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Eyrie",
|
||||
},
|
||||
{
|
||||
speciesId: "13",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/47vt32x2/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Flotsam",
|
||||
},
|
||||
{
|
||||
speciesId: "14",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/5nrd2lvd/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Gelert",
|
||||
},
|
||||
{
|
||||
speciesId: "15",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/6c275jcg/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Gnorbu",
|
||||
},
|
||||
{
|
||||
speciesId: "16",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/j7q65fv4/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Grarrl",
|
||||
},
|
||||
{
|
||||
speciesId: "17",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/5xn4kjf8/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Grundo",
|
||||
},
|
||||
{
|
||||
speciesId: "18",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/jsfvcqwt/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Hissi",
|
||||
},
|
||||
{
|
||||
speciesId: "19",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/w32r74vo/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Ixi",
|
||||
},
|
||||
{
|
||||
speciesId: "20",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/kz43rnld/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Jetsam",
|
||||
},
|
||||
{
|
||||
speciesId: "21",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/m267j935/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Jubjub",
|
||||
},
|
||||
{
|
||||
speciesId: "22",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/4gsrb59g/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Kacheek",
|
||||
},
|
||||
{
|
||||
speciesId: "23",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/ktlxmrtr/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Kau",
|
||||
},
|
||||
{
|
||||
speciesId: "24",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/42j5q3zx/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Kiko",
|
||||
},
|
||||
{
|
||||
speciesId: "25",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/ncfn87wk/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Koi",
|
||||
},
|
||||
{
|
||||
speciesId: "26",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/omx9c876/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Korbat",
|
||||
},
|
||||
{
|
||||
speciesId: "27",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/rfsbh59t/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Kougra",
|
||||
},
|
||||
{
|
||||
speciesId: "28",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/hxgsm5d4/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Krawk",
|
||||
},
|
||||
{
|
||||
speciesId: "29",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/blxmjgbk/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Kyrii",
|
||||
},
|
||||
{
|
||||
speciesId: "30",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/8r94jhfq/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Lenny",
|
||||
},
|
||||
{
|
||||
speciesId: "31",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/z42535zh/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Lupe",
|
||||
},
|
||||
{
|
||||
speciesId: "32",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/qgg6z8s7/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Lutari",
|
||||
},
|
||||
{
|
||||
speciesId: "33",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/kk2nn2jr/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Meerca",
|
||||
},
|
||||
{
|
||||
speciesId: "34",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/jgkoro5z/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Moehog",
|
||||
},
|
||||
{
|
||||
speciesId: "35",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/xwlo9657/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Mynci",
|
||||
},
|
||||
{
|
||||
speciesId: "36",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/bx7fho8x/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Nimmo",
|
||||
},
|
||||
{
|
||||
speciesId: "37",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/rjzmx24v/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Ogrin",
|
||||
},
|
||||
{
|
||||
speciesId: "38",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/kokc52kh/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Peophin",
|
||||
},
|
||||
{
|
||||
speciesId: "39",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/fw6lvf3c/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Poogle",
|
||||
},
|
||||
{
|
||||
speciesId: "40",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/tjhwbro3/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Pteri",
|
||||
},
|
||||
{
|
||||
speciesId: "41",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/jdto7mj4/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Quiggle",
|
||||
},
|
||||
{
|
||||
speciesId: "42",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/qsgbm5f6/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Ruki",
|
||||
},
|
||||
{
|
||||
speciesId: "43",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/hkjoncsx/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Scorchio",
|
||||
},
|
||||
{
|
||||
speciesId: "44",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/mmvn4tkg/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Shoyru",
|
||||
},
|
||||
{
|
||||
speciesId: "45",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/fc4cxk3t/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Skeith",
|
||||
},
|
||||
{
|
||||
speciesId: "46",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/84gvowmj/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Techo",
|
||||
},
|
||||
{
|
||||
speciesId: "47",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/jd433863/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Tonu",
|
||||
},
|
||||
{
|
||||
speciesId: "48",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/q39wn6vq/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Tuskaninny",
|
||||
},
|
||||
{
|
||||
speciesId: "49",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/njzvoflw/1/1.png",
|
||||
colorId: colors.GREEN,
|
||||
speciesName: "Uni",
|
||||
},
|
||||
{
|
||||
speciesId: "50",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/rox4mgh5/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Usul",
|
||||
},
|
||||
{
|
||||
speciesId: "51",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/dnr2kj4b/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Wocky",
|
||||
},
|
||||
{
|
||||
speciesId: "52",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/tdkqr2b6/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Xweetok",
|
||||
},
|
||||
{
|
||||
speciesId: "53",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/h95cs547/1/1.png",
|
||||
colorId: colors.RED,
|
||||
speciesName: "Yurble",
|
||||
},
|
||||
{
|
||||
speciesId: "54",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/x8c57g2l/1/1.png",
|
||||
colorId: colors.BLUE,
|
||||
speciesName: "Zafara",
|
||||
},
|
||||
{
|
||||
speciesId: "55",
|
||||
src: "https://pets.neopets-asset-proxy.openneo.net/cp/xkntzsww/1/1.png",
|
||||
colorId: colors.YELLOW,
|
||||
speciesName: "Vandagyre",
|
||||
},
|
||||
];
|
||||
|
||||
export default ItemPage;
|
||||
|
|
Loading…
Reference in a new issue