Refactor item page preview to grid layout

because I wanna add zones to the area below the faces!
This commit is contained in:
Emi Matchu 2021-02-12 14:35:14 -08:00
parent 67784bd5e3
commit 048fb29c14

View file

@ -20,6 +20,7 @@ import {
WrapItem, WrapItem,
Flex, Flex,
usePrefersReducedMotion, usePrefersReducedMotion,
Grid,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { import {
CheckIcon, CheckIcon,
@ -681,108 +682,130 @@ function ItemPageOutfitPreview({ itemId }) {
} }
return ( return (
<Stack <Grid
direction={{ base: "column", md: "row" }} templateAreas={{
align={{ base: "center", md: "flex-start" }} base: `
justify="center" "preview"
spacing="8" "speciesColorPicker"
width="100%" "speciesFacesPicker"
"zones"
`,
md: `
"preview speciesFacesPicker"
"speciesColorPicker zones"
`,
}}
templateRows={{
base: "auto auto 400px auto",
md: "400px auto",
}}
templateColumns={{
base: "minmax(min-content, 400px)",
md: "minmax(min-content, 400px) fit-content(480px)",
}}
rowGap="4"
columnGap="6"
justifyContent="center"
> >
<VStack spacing="3" maxWidth="100%"> <AspectRatio
<AspectRatio gridArea="preview"
width="400px" maxWidth="400px"
maxWidth="100%" maxHeight="400px"
ratio="1" ratio="1"
border="1px" border="1px"
borderColor={borderColor} borderColor={borderColor}
transition="border-color 0.2s" transition="border-color 0.2s"
borderRadius="lg" borderRadius="lg"
boxShadow="lg" boxShadow="lg"
overflow="hidden" overflow="hidden"
> >
<Box> <Box>
{petState.isValid && preview} {petState.isValid && preview}
<CustomizeMoreButton <CustomizeMoreButton
speciesId={petState.speciesId}
colorId={petState.colorId}
pose={petState.pose}
itemId={itemId}
isDisabled={!petState.isValid}
/>
{hasAnimations && (
<PlayPauseButton
isPaused={isPaused}
onClick={() => setIsPaused(!isPaused)}
/>
)}
</Box>
</AspectRatio>
<Box display="flex" width="100%" alignItems="center">
<Box
// This 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"
display="flex"
justifyContent="center"
paddingRight="2"
>
<HTML5Badge
usesHTML5={usesHTML5}
// If we're not compatible, act the same as if we're loading:
// don't change the badge, but don't show one yet if we don't
// have one yet.
isLoading={appearance.loading || !isCompatible}
/>
</Box>
<SpeciesColorPicker
speciesId={petState.speciesId} speciesId={petState.speciesId}
colorId={petState.colorId} colorId={petState.colorId}
pose={petState.pose} pose={petState.pose}
idealPose={idealPose} itemId={itemId}
onChange={(species, color, isValid, closestPose) => { isDisabled={!petState.isValid}
setPetStateFromUserAction({
speciesId: species.id,
colorId: color.id,
pose: closestPose,
isValid,
appearanceId: null,
});
}}
speciesIsDisabled={isProbablySpeciesSpecific}
size="sm"
showPlaceholders
/> />
<Box flex="1 0 0" lineHeight="1" paddingLeft="1"> {hasAnimations && (
{ <PlayPauseButton
// Wait for us to start _requesting_ the appearance, and _then_ isPaused={isPaused}
// for it to load, and _then_ check compatibility. onClick={() => setIsPaused(!isPaused)}
!loadingGQL && />
!appearance.loading && )}
petState.isValid &&
!isCompatible && (
<Tooltip
label={
couldProbablyModelMoreData
? "Item needs models"
: "Not compatible"
}
placement="top"
>
<WarningIcon
color={errorColor}
transition="color 0.2"
marginLeft="2"
borderRadius="full"
tabIndex="0"
_focus={{ outline: "none", boxShadow: "outline" }}
/>
</Tooltip>
)
}
</Box>
</Box> </Box>
</VStack> </AspectRatio>
<Box maxWidth="460px" paddingTop="2"> <Box gridArea="speciesColorPicker" display="flex" alignItems="center">
<Box
// This 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"
display="flex"
justifyContent="center"
paddingRight="2"
>
<HTML5Badge
usesHTML5={usesHTML5}
// If we're not compatible, act the same as if we're loading:
// don't change the badge, but don't show one yet if we don't
// have one yet.
isLoading={appearance.loading || !isCompatible}
/>
</Box>
<SpeciesColorPicker
speciesId={petState.speciesId}
colorId={petState.colorId}
pose={petState.pose}
idealPose={idealPose}
onChange={(species, color, isValid, closestPose) => {
setPetStateFromUserAction({
speciesId: species.id,
colorId: color.id,
pose: closestPose,
isValid,
appearanceId: null,
});
}}
speciesIsDisabled={isProbablySpeciesSpecific}
size="sm"
showPlaceholders
/>
<Box flex="1 0 0" lineHeight="1" paddingLeft="1">
{
// Wait for us to start _requesting_ the appearance, and _then_
// for it to load, and _then_ check compatibility.
!loadingGQL &&
!appearance.loading &&
petState.isValid &&
!isCompatible && (
<Tooltip
label={
couldProbablyModelMoreData
? "Item needs models"
: "Not compatible"
}
placement="top"
>
<WarningIcon
color={errorColor}
transition="color 0.2"
marginLeft="2"
borderRadius="full"
tabIndex="0"
_focus={{ outline: "none", boxShadow: "outline" }}
/>
</Tooltip>
)
}
</Box>
</Box>
<Box
gridArea="speciesFacesPicker"
paddingTop="2"
overflow="auto"
padding="8px"
>
<SpeciesFacesPicker <SpeciesFacesPicker
selectedSpeciesId={petState.speciesId} selectedSpeciesId={petState.speciesId}
selectedColorId={petState.colorId} selectedColorId={petState.colorId}
@ -802,7 +825,10 @@ function ItemPageOutfitPreview({ itemId }) {
isLoading={loadingGQL || loadingValids} isLoading={loadingGQL || loadingValids}
/> />
</Box> </Box>
</Stack> <Box gridArea="zones" alignSelf="center" justifySelf="center">
<ItemZonesInfo />
</Box>
</Grid>
); );
} }
@ -983,15 +1009,7 @@ function SpeciesFacesPicker({
return ( return (
<Box> <Box>
<Wrap <Wrap spacing="0" justify="center">
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((speciesFace) => ( {allSpeciesFaces.map((speciesFace) => (
<WrapItem key={speciesFace.speciesId}> <WrapItem key={speciesFace.speciesId}>
<SpeciesFaceOption <SpeciesFaceOption
@ -1237,6 +1255,14 @@ function SpeciesFaceOption({
); );
} }
function ItemZonesInfo() {
return (
<Box fontSize="sm" textAlign="center">
{/* TODO */}
</Box>
);
}
/** /**
* CrossFadeImage is like <img>, but listens for successful load events, and * CrossFadeImage is like <img>, but listens for successful load events, and
* fades from the previous image to the new image once it loads. * fades from the previous image to the new image once it loads.