impress-2020/app/src/WardrobePage.js

307 lines
6.9 KiB
JavaScript
Raw Normal View History

2020-04-21 20:32:53 -07:00
import React from "react";
import {
Box,
2020-04-22 02:39:06 -07:00
Editable,
EditablePreview,
EditableInput,
2020-04-21 20:32:53 -07:00
Flex,
Grid,
Heading,
2020-04-22 02:39:06 -07:00
Icon,
2020-04-22 01:10:24 -07:00
IconButton,
2020-04-21 20:32:53 -07:00
Image,
2020-04-22 02:39:06 -07:00
Input,
InputGroup,
InputLeftElement,
InputRightElement,
2020-04-21 20:32:53 -07:00
PseudoBox,
2020-04-22 02:39:06 -07:00
Stack,
Text,
2020-04-21 20:32:53 -07:00
} from "@chakra-ui/core";
2020-04-21 20:46:53 -07:00
import useOutfitState from "./useOutfitState.js";
2020-04-22 02:39:06 -07:00
import { ITEMS } from "./data";
2020-04-21 20:32:53 -07:00
function WardrobePage() {
2020-04-22 02:39:06 -07:00
const [data, wearItem] = useOutfitState();
const [searchQuery, setSearchQuery] = React.useState("");
2020-04-21 20:32:53 -07:00
return (
2020-04-22 00:39:35 -07:00
<Grid
// Fullscreen, split into a vertical stack on smaller screens
// or a horizontal stack on larger ones!
2020-04-22 02:39:06 -07:00
templateAreas={{
base: `"outfit"
"search"
"items"`,
lg: `"outfit search"
"outfit items"`,
}}
templateRows={{
base: "50% auto 1fr",
lg: "auto 1fr",
}}
templateColumns={{
base: "100%",
lg: "50% 50%",
}}
2020-04-22 00:39:35 -07:00
position="absolute"
top="0"
bottom="0"
left="0"
right="0"
>
2020-04-22 02:39:06 -07:00
<Box gridArea="outfit">
2020-04-21 20:32:53 -07:00
<OutfitPreview />
</Box>
2020-04-22 02:39:06 -07:00
<Box gridArea="search" boxShadow="sm">
<Box px="5" py="3">
<SearchToolbar query={searchQuery} onChange={setSearchQuery} />
</Box>
</Box>
<Box gridArea="items" overflow="auto">
2020-04-21 20:32:53 -07:00
<Box px="5" py="5">
2020-04-22 02:39:06 -07:00
{searchQuery ? (
<SearchPanel
query={searchQuery}
wornItemIds={data.wornItemIds}
onWearItem={wearItem}
/>
) : (
<ItemsPanel
zonesAndItems={data.zonesAndItems}
onWearItem={wearItem}
/>
)}
2020-04-21 20:32:53 -07:00
</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>
);
}
2020-04-22 02:39:06 -07:00
function SearchToolbar({ query, onChange }) {
return (
<InputGroup>
<InputLeftElement>
<Icon name="search" color="gray.400" />
</InputLeftElement>
<Input
placeholder="Search items…"
focusBorderColor="green.600"
color="green.800"
value={query}
onChange={(e) => onChange(e.target.value)}
/>
{query && (
<InputRightElement>
<IconButton
icon="close"
color="gray.400"
variant="ghost"
variantColor="green"
aria-label="Clear search"
onClick={() => onChange("")}
/>
</InputRightElement>
)}
</InputGroup>
);
}
function SearchPanel({ query, wornItemIds, onWearItem }) {
const normalize = (s) => s.toLowerCase();
const results = ITEMS.filter((item) =>
normalize(item.name).includes(normalize(query))
);
results.sort((a, b) => a.name.localeCompare(b.name));
const resultsSection =
results.length > 0 ? (
<ItemList
items={results}
wornItemIds={wornItemIds}
onWearItem={onWearItem}
/>
) : (
<Text color="green.500">
We couldn't find any matching items{" "}
<span role="img" aria-label="(thinking emoji)">
🤔
</span>{" "}
Try again?
</Text>
);
2020-04-21 20:32:53 -07:00
2020-04-22 02:39:06 -07:00
return (
<Box color="green.800">
<Heading fontFamily="Delicious" fontWeight="800" size="2xl" mb="6">
Searching for "{query}"
</Heading>
{resultsSection}
</Box>
);
}
function ItemsPanel({ zonesAndItems, onWearItem }) {
2020-04-21 20:32:53 -07:00
return (
<Box color="green.800">
<OutfitHeading />
2020-04-22 01:10:24 -07:00
<Stack spacing="10">
{zonesAndItems.map(({ zoneName, items, wornItemId }) => (
<Box key={zoneName}>
<ItemsForZone
zoneName={zoneName}
items={items}
wornItemId={wornItemId}
2020-04-22 02:39:06 -07:00
onWearItem={onWearItem}
2020-04-22 01:10:24 -07:00
/>
</Box>
))}
</Stack>
</Box>
2020-04-21 20:32:53 -07:00
);
}
function OutfitHeading() {
return (
<PseudoBox role="group" d="inline-block">
<Heading
size="2xl"
mb="6"
wordBreak="break-word"
fontFamily="Delicious"
fontWeight="800"
>
2020-04-22 01:32:59 -07:00
<Editable defaultValue="Zafara Agent (roopal27)">
{({ isEditing, onRequestEdit }) => (
<>
<EditablePreview d="inline" />
<EditableInput />
{!isEditing && (
<PseudoBox
d="inline-block"
opacity="0"
transition="opacity 0.5s"
_groupHover={{ opacity: "1" }}
onClick={onRequestEdit}
>
<IconButton
icon="edit"
variant="link"
color="green.600"
aria-label="Edit outfit name"
title="Edit outfit name"
/>
</PseudoBox>
)}
</>
)}
</Editable>
</Heading>
</PseudoBox>
);
}
2020-04-21 20:32:53 -07:00
function ItemsForZone({ zoneName, items, wornItemId, onWearItem }) {
return (
<Box>
<Heading size="xl" color="green.800" mb="3" fontFamily="Delicious">
2020-04-21 20:32:53 -07:00
{zoneName}
</Heading>
2020-04-22 02:39:06 -07:00
<ItemList
items={items}
wornItemIds={[wornItemId]}
onWearItem={onWearItem}
/>
2020-04-21 20:32:53 -07:00
</Box>
);
}
2020-04-22 02:39:06 -07:00
function ItemList({ items, wornItemIds, onWearItem }) {
return (
<Stack spacing="3">
{items.map((item) => (
<Box key={item.id}>
<Item
item={item}
isWorn={wornItemIds.includes(item.id)}
onWear={() => onWearItem(item.id)}
/>
</Box>
))}
</Stack>
);
}
2020-04-21 20:32:53 -07:00
function Item({ item, isWorn, onWear }) {
return (
<PseudoBox
role="group"
d="flex"
alignItems="center"
cursor="pointer"
onClick={onWear}
>
<PseudoBox
rounded="lg"
boxShadow="md"
border="1px"
borderColor={isWorn ? "green.700" : "green.700"}
opacity={isWorn ? 1 : 0.7}
width="50px"
height="50px"
overflow="hidden"
transition="all 0.15s"
transformOrigin="center"
transform={isWorn ? null : "scale(0.8)"}
_groupHover={
!isWorn && {
opacity: 0.9,
transform: "scale(0.9)",
borderColor: "green.600",
2020-04-21 20:32:53 -07:00
}
}
>
<Image src={item.thumbnailSrc} />
</PseudoBox>
2020-04-21 20:32:53 -07:00
<PseudoBox
marginLeft="3"
2020-04-21 20:32:53 -07:00
fontSize="md"
fontWeight={isWorn && "bold"}
color="green.800"
transition="all 0.15s"
opacity={isWorn ? 1 : 0.8}
2020-04-21 20:32:53 -07:00
_groupHover={
!isWorn && {
color: "green.800",
fontWeight: "medium",
opacity: 0.9,
2020-04-21 20:32:53 -07:00
}
}
>
{item.name}
</PseudoBox>
</PseudoBox>
);
}
export default WardrobePage;