impress-2020/app/src/WardrobePage.js

193 lines
4.7 KiB
JavaScript
Raw Normal View History

2020-04-21 20:32:53 -07:00
import React from "react";
import {
Box,
Flex,
Grid,
Heading,
Image,
Stack,
PseudoBox,
} from "@chakra-ui/core";
import { ITEMS } from "./data.js";
function WardrobePage() {
return (
<Grid templateRows="50vh 50vh">
<Box boxShadow="md">
<OutfitPreview />
</Box>
<Box overflow="auto">
<Box px="5" py="5">
<ItemsPanel />
</Box>
</Box>
</Grid>
);
}
function OutfitPreview() {
return (
<Flex
alignItems="center"
justifyContent="center"
height="100%"
width="100%"
backgroundColor="gray.900"
>
<Image
src="http://pets.neopets.com/cp/wgmdtdwz/1/7.png"
maxHeight="100%"
maxWidth="100%"
/>
</Flex>
);
}
function ItemsPanel() {
const [wornItemIds, setWornItemIds] = React.useState([1, 2, 3, 4, 6, 7]);
const [closetedItemIds, setClosetedItemIds] = React.useState([5]);
const wearItem = React.useCallback(
(itemIdToAdd) => {
if (wornItemIds.includes(itemIdToAdd)) {
return;
}
let newWornItemIds = wornItemIds;
let newClosetedItemIds = closetedItemIds;
const itemToAdd = ITEMS.find((item) => item.id === itemIdToAdd);
// Move the item out of the closet.
newClosetedItemIds = newClosetedItemIds.filter(
(id) => id !== itemIdToAdd
);
// Move conflicting items to the closet.
const conflictingItemIds = newWornItemIds.filter((wornItemId) => {
const wornItem = ITEMS.find((item) => item.id === wornItemId);
return wornItem.zoneName === itemToAdd.zoneName;
});
newWornItemIds = newWornItemIds.filter(
(id) => !conflictingItemIds.includes(id)
);
newClosetedItemIds = [...newClosetedItemIds, ...conflictingItemIds];
// Add this item to the worn set.
newWornItemIds = [...newWornItemIds, itemIdToAdd];
setWornItemIds(newWornItemIds);
setClosetedItemIds(newClosetedItemIds);
},
[wornItemIds, setWornItemIds, closetedItemIds, setClosetedItemIds]
);
const wornItems = wornItemIds.map((id) =>
ITEMS.find((item) => item.id === id)
);
const closetedItems = closetedItemIds.map((id) =>
ITEMS.find((item) => item.id === id)
);
const allItems = [...wornItems, ...closetedItems];
const allZoneNames = [...new Set(allItems.map((item) => item.zoneName))];
allZoneNames.sort();
const zonesAndItems = allZoneNames.map((zoneName) => {
const items = allItems.filter((item) => item.zoneName === zoneName);
items.sort((a, b) => a.name.localeCompare(b.name));
const wornItemId =
items.map((item) => item.id).find((id) => wornItemIds.includes(id)) ||
null;
return { zoneName, items, wornItemId };
});
return (
<Stack spacing="10">
{zonesAndItems.map(({ zoneName, items, wornItemId }) => (
<Box key={zoneName}>
<ItemsForZone
zoneName={zoneName}
items={items}
wornItemId={wornItemId}
onWearItem={wearItem}
/>
</Box>
))}
</Stack>
);
}
function ItemsForZone({ zoneName, items, wornItemId, onWearItem }) {
return (
<Box>
<Heading size="lg" mb="3">
{zoneName}
</Heading>
<Stack spacing="3">
{items.map((item) => (
<Box key={item.id}>
<Item
item={item}
isWorn={item.id === wornItemId}
onWear={() => onWearItem(item.id)}
/>
</Box>
))}
</Stack>
</Box>
);
}
function Item({ item, isWorn, onWear }) {
return (
<PseudoBox
role="group"
d="flex"
alignItems="center"
cursor="pointer"
onClick={onWear}
>
<Flex w="50px" h="50px" mr="3" align="center" justify="center">
<PseudoBox
rounded="full"
border="2px"
boxShadow="md"
borderColor={isWorn ? "green.700" : "gray.400"}
opacity={isWorn ? 1 : 0.7}
width={isWorn ? "50px" : "40px"}
height={isWorn ? "50px" : "40px"}
overflow="hidden"
transition="all 0.2s"
transformOrigin="center"
_groupHover={
!isWorn && {
opacity: 0.9,
transform: "scale(1.1)",
borderColor: "gray.600",
}
}
>
<Image src={item.thumbnailSrc} />
</PseudoBox>
</Flex>
<PseudoBox
fontSize="md"
fontWeight={isWorn && "bold"}
color={isWorn ? "gray.800" : "gray.600"}
transition="all 0.2s"
_groupHover={
!isWorn && {
color: "gray.800",
}
}
>
{item.name}
</PseudoBox>
</PseudoBox>
);
}
export default WardrobePage;