sketched out the pet compatibility chooser
can't actually set things properly yet, but you can scroll through options and see how it fits!
This commit is contained in:
parent
488299353a
commit
213f30b2f2
6 changed files with 243 additions and 89 deletions
|
@ -2,6 +2,10 @@ import * as React from "react";
|
|||
import {
|
||||
Button,
|
||||
Box,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Modal,
|
||||
ModalBody,
|
||||
|
@ -9,42 +13,41 @@ import {
|
|||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
} from "@chakra-ui/core";
|
||||
import { ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
|
||||
function ItemSupportAppearanceLayerModal({ item, itemLayer, isOpen, onClose }) {
|
||||
import { OutfitLayers } from "../../components/OutfitPreview";
|
||||
import SpeciesColorPicker from "../../components/SpeciesColorPicker";
|
||||
import useOutfitAppearance from "../../components/useOutfitAppearance";
|
||||
|
||||
function ItemSupportAppearanceLayerModal({
|
||||
item,
|
||||
itemLayer,
|
||||
outfitState,
|
||||
isOpen,
|
||||
onClose,
|
||||
}) {
|
||||
return (
|
||||
<Modal size="xl" isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay>
|
||||
<ModalContent>
|
||||
<ModalContent color="green.800">
|
||||
<ModalHeader>
|
||||
Layer {itemLayer.id}: {item.name}
|
||||
</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody mb="4" pb="4">
|
||||
<Box
|
||||
as="dl"
|
||||
display="grid"
|
||||
gridTemplateColumns="max-content auto"
|
||||
gridRowGap="1"
|
||||
gridColumnGap="2"
|
||||
>
|
||||
<Box as="dt" gridColumn="1" fontWeight="bold">
|
||||
ID:
|
||||
</Box>
|
||||
<Box as="dd" gridColumn="2">
|
||||
{itemLayer.id}
|
||||
</Box>
|
||||
<Box as="dt" gridColumn="1" fontWeight="bold">
|
||||
Zone:
|
||||
</Box>
|
||||
<Box as="dd" gridColumn="2">
|
||||
<Metadata>
|
||||
<MetadataLabel>ID:</MetadataLabel>
|
||||
<MetadataValue>{itemLayer.id}</MetadataValue>
|
||||
<MetadataLabel>Zone:</MetadataLabel>
|
||||
<MetadataValue>
|
||||
{itemLayer.zone.label} ({itemLayer.zone.id})
|
||||
</Box>
|
||||
<Box as="dt" gridColumn="1" fontWeight="bold">
|
||||
Assets:
|
||||
</Box>
|
||||
<HStack as="dd" gridColumn="2" spacing="2">
|
||||
</MetadataValue>
|
||||
<MetadataLabel>Assets:</MetadataLabel>
|
||||
<MetadataValue>
|
||||
<HStack spacing="2">
|
||||
{itemLayer.svgUrl ? (
|
||||
<Button
|
||||
as="a"
|
||||
|
@ -76,7 +79,14 @@ function ItemSupportAppearanceLayerModal({ item, itemLayer, isOpen, onClose }) {
|
|||
</Button>
|
||||
)}
|
||||
</HStack>
|
||||
</Box>
|
||||
</MetadataValue>
|
||||
</Metadata>
|
||||
<Box height="8" />
|
||||
<ItemSupportAppearanceLayerPetCompatibility
|
||||
item={item}
|
||||
itemLayer={itemLayer}
|
||||
outfitState={outfitState}
|
||||
/>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</ModalOverlay>
|
||||
|
@ -84,4 +94,122 @@ function ItemSupportAppearanceLayerModal({ item, itemLayer, isOpen, onClose }) {
|
|||
);
|
||||
}
|
||||
|
||||
function ItemSupportAppearanceLayerPetCompatibility({
|
||||
item,
|
||||
itemLayer,
|
||||
outfitState,
|
||||
}) {
|
||||
const [selectedBiology, setSelectedBiology] = React.useState({
|
||||
speciesId: outfitState.speciesId,
|
||||
colorId: outfitState.colorId,
|
||||
pose: outfitState.pose,
|
||||
isValid: true,
|
||||
});
|
||||
const [visibleBiology, setVisibleBiology] = React.useState(selectedBiology);
|
||||
console.log(selectedBiology, visibleBiology);
|
||||
|
||||
const { loading, error, visibleLayers } = useOutfitAppearance({
|
||||
speciesId: visibleBiology.speciesId,
|
||||
colorId: visibleBiology.colorId,
|
||||
pose: visibleBiology.pose,
|
||||
wornItemIds: [item.id],
|
||||
});
|
||||
|
||||
const biologyLayers = visibleLayers.filter((l) => l.source === "pet");
|
||||
|
||||
return (
|
||||
<FormControl isInvalid={error || !selectedBiology.isValid ? true : false}>
|
||||
<FormLabel>Pet compatibility</FormLabel>
|
||||
<RadioGroup
|
||||
colorScheme="green"
|
||||
value={itemLayer.bodyId}
|
||||
onChange={(e) => console.log(e)}
|
||||
marginBottom="4"
|
||||
>
|
||||
<Radio value="0">
|
||||
Fits all pets{" "}
|
||||
<Box display="inline" color="gray.400" fontSize="sm">
|
||||
(Body ID: 0)
|
||||
</Box>
|
||||
</Radio>
|
||||
<Radio as="div" value="100" marginTop="2">
|
||||
Fits all pets with the same body as:{" "}
|
||||
<Box display="inline" color="gray.400" fontSize="sm">
|
||||
(Body ID: 100)
|
||||
</Box>
|
||||
</Radio>
|
||||
</RadioGroup>
|
||||
<Box display="flex" flexDirection="column" alignItems="center">
|
||||
<Box
|
||||
width="150px"
|
||||
height="150px"
|
||||
marginTop="2"
|
||||
marginBottom="2"
|
||||
boxShadow="md"
|
||||
borderRadius="md"
|
||||
>
|
||||
<OutfitLayers
|
||||
loading={loading}
|
||||
visibleLayers={[...biologyLayers, itemLayer]}
|
||||
/>
|
||||
</Box>
|
||||
<SpeciesColorPicker
|
||||
speciesId={selectedBiology.speciesId}
|
||||
colorId={selectedBiology.colorId}
|
||||
idealPose={outfitState.pose}
|
||||
isDisabled={itemLayer.bodyId === "0"}
|
||||
size="sm"
|
||||
showPlaceholders
|
||||
onChange={(species, color, isValid, pose) => {
|
||||
const speciesId = species.id;
|
||||
const colorId = color.id;
|
||||
|
||||
setSelectedBiology({ speciesId, colorId, isValid, pose });
|
||||
if (isValid) {
|
||||
setVisibleBiology({ speciesId, colorId, isValid, pose });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Box height="1" />
|
||||
{!error && (
|
||||
<FormHelperText>
|
||||
If it doesn't look right, try some other options until it does!
|
||||
</FormHelperText>
|
||||
)}
|
||||
{error && <FormErrorMessage>{error.message}</FormErrorMessage>}
|
||||
</Box>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
function Metadata({ children }) {
|
||||
return (
|
||||
<Box
|
||||
as="dl"
|
||||
display="grid"
|
||||
gridTemplateColumns="max-content auto"
|
||||
gridRowGap="1"
|
||||
gridColumnGap="2"
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function MetadataLabel({ children }) {
|
||||
return (
|
||||
<Box as="dt" gridColumn="1" fontWeight="bold">
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function MetadataValue({ children }) {
|
||||
return (
|
||||
<Box as="dd" gridColumn="2">
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default ItemSupportAppearanceLayerModal;
|
||||
|
|
|
@ -250,9 +250,10 @@ function ItemSupportAppearanceFields({ item, outfitState }) {
|
|||
<HStack spacing="4" overflow="auto" paddingX="1">
|
||||
{itemLayers.map((itemLayer) => (
|
||||
<ItemSupportAppearanceLayer
|
||||
biologyLayers={biologyLayers}
|
||||
itemLayer={itemLayer}
|
||||
item={item}
|
||||
itemLayer={itemLayer}
|
||||
biologyLayers={biologyLayers}
|
||||
outfitState={outfitState}
|
||||
/>
|
||||
))}
|
||||
</HStack>
|
||||
|
@ -261,7 +262,12 @@ function ItemSupportAppearanceFields({ item, outfitState }) {
|
|||
);
|
||||
}
|
||||
|
||||
function ItemSupportAppearanceLayer({ biologyLayers, itemLayer, item }) {
|
||||
function ItemSupportAppearanceLayer({
|
||||
item,
|
||||
itemLayer,
|
||||
biologyLayers,
|
||||
outfitState,
|
||||
}) {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
return (
|
||||
|
@ -311,6 +317,7 @@ function ItemSupportAppearanceLayer({ biologyLayers, itemLayer, item }) {
|
|||
<ItemSupportAppearanceLayerModal
|
||||
item={item}
|
||||
itemLayer={itemLayer}
|
||||
outfitState={outfitState}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
|
|
@ -167,7 +167,7 @@ export function OutfitLayers({
|
|||
opacity={isMounted && loading ? 1 : 0}
|
||||
transition={`opacity 0.2s ${loading ? loadingDelay : "0s"}`}
|
||||
>
|
||||
<FullScreenCenter>
|
||||
<FullScreenCenter zIndex="9000">
|
||||
<Box
|
||||
width="100%"
|
||||
height="100%"
|
||||
|
@ -175,7 +175,7 @@ export function OutfitLayers({
|
|||
opacity="0.8"
|
||||
/>
|
||||
</FullScreenCenter>
|
||||
<FullScreenCenter>
|
||||
<FullScreenCenter zIndex="9001">
|
||||
<HangerSpinner color="green.300" boxSize="48px" />
|
||||
</FullScreenCenter>
|
||||
</Box>
|
||||
|
|
|
@ -15,7 +15,9 @@ function SpeciesColorPicker({
|
|||
speciesId,
|
||||
colorId,
|
||||
idealPose,
|
||||
showPlaceholders,
|
||||
showPlaceholders = false,
|
||||
isDisabled = false,
|
||||
size = "md",
|
||||
dark = false,
|
||||
onChange,
|
||||
}) {
|
||||
|
@ -51,10 +53,23 @@ function SpeciesColorPicker({
|
|||
const backgroundColor = dark ? "gray.600" : "white";
|
||||
const borderColor = dark ? "transparent" : "green.600";
|
||||
const textColor = dark ? "gray.50" : "inherit";
|
||||
const SpeciesColorSelect = ({ ...props }) => (
|
||||
const SpeciesColorSelect = ({ isDisabled, isLoading, ...props }) => {
|
||||
const loadingProps = isLoading
|
||||
? {
|
||||
// Visually the disabled state is the same as the normal state, but
|
||||
// with a wait cursor. We don't expect this to take long, and the flash
|
||||
// of content is rough! (The caret still flashes, but that's small and
|
||||
// harder to style in Chakra.)
|
||||
opacity: 1,
|
||||
cursor: "wait",
|
||||
}
|
||||
: {};
|
||||
|
||||
return (
|
||||
<Select
|
||||
backgroundColor={backgroundColor}
|
||||
color={textColor}
|
||||
size={size}
|
||||
border="1px"
|
||||
borderColor={borderColor}
|
||||
boxShadow="md"
|
||||
|
@ -62,19 +77,14 @@ function SpeciesColorPicker({
|
|||
_hover={{
|
||||
borderColor: "green.400",
|
||||
}}
|
||||
_disabled={{
|
||||
// Visually the disabled state is the same as the normal state, but
|
||||
// with a wait cursor. We don't expect this to take long, and the flash
|
||||
// of content is rough! (The caret still flashes, but that's small and
|
||||
// harder to style in Chakra.)
|
||||
opacity: 1,
|
||||
cursor: "wait",
|
||||
}}
|
||||
isInvalid={valids && !pairIsValid(valids, speciesId, colorId)}
|
||||
isDisabled={isDisabled || isLoading}
|
||||
errorBorderColor="red.300"
|
||||
{...props}
|
||||
{...loadingProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if ((loadingMeta || loadingValids) && !showPlaceholders) {
|
||||
return (
|
||||
|
@ -125,7 +135,8 @@ function SpeciesColorPicker({
|
|||
<SpeciesColorSelect
|
||||
aria-label="Pet color"
|
||||
value={colorId}
|
||||
isDisabled={allColors.length === 0}
|
||||
isLoading={allColors.length === 0}
|
||||
isDisabled={isDisabled}
|
||||
onChange={onChangeColor}
|
||||
>
|
||||
{allColors.length === 0 && (
|
||||
|
@ -141,11 +152,12 @@ function SpeciesColorPicker({
|
|||
</option>
|
||||
))}
|
||||
</SpeciesColorSelect>
|
||||
<Box width="4" />
|
||||
<Box width={size === "sm" ? 2 : 4} />
|
||||
<SpeciesColorSelect
|
||||
aria-label="Pet species"
|
||||
value={speciesId}
|
||||
isDisabled={allSpecies.length === 0}
|
||||
isLoading={allSpecies.length === 0}
|
||||
isDisabled={isDisabled}
|
||||
onChange={onChangeSpecies}
|
||||
>
|
||||
{allSpecies.length === 0 && (
|
||||
|
|
|
@ -117,6 +117,7 @@ export const itemAppearanceFragment = gql`
|
|||
id
|
||||
svgUrl
|
||||
imageUrl(size: SIZE_600)
|
||||
bodyId
|
||||
zone {
|
||||
id
|
||||
depth
|
||||
|
|
|
@ -104,6 +104,12 @@ const typeDefs = gql`
|
|||
or if it's not as simple as a single SVG (e.g. animated).
|
||||
"""
|
||||
svgUrl: String
|
||||
|
||||
"""
|
||||
This layer can fit on PetAppearances with the same bodyId. "0" is a
|
||||
special body ID that indicates it fits all PetAppearances.
|
||||
"""
|
||||
bodyId: ID!
|
||||
}
|
||||
|
||||
# Cache for 1 week (unlikely to change)
|
||||
|
|
Loading…
Reference in a new issue