start adding appearance layers support tool

right now it just shows stuff. next step is to make them clickable to open tools for managing the layer!
This commit is contained in:
Emi Matchu 2020-08-01 01:35:27 -07:00
parent 0cdb6d0da1
commit 63b865ef7c
4 changed files with 87 additions and 18 deletions

View file

@ -82,6 +82,7 @@ export function Item({ item, itemNameId, outfitState, dispatchToOutfit }) {
<SupportOnly> <SupportOnly>
<LoadableItemSupportDrawer <LoadableItemSupportDrawer
item={item} item={item}
outfitState={outfitState}
isOpen={supportDrawerIsOpen} isOpen={supportDrawerIsOpen}
onClose={() => setSupportDrawerIsOpen(false)} onClose={() => setSupportDrawerIsOpen(false)}
/> />

View file

@ -14,13 +14,17 @@ import {
FormErrorMessage, FormErrorMessage,
FormHelperText, FormHelperText,
FormLabel, FormLabel,
HStack,
Link, Link,
Select, Select,
Spinner, Spinner,
Stack,
useBreakpointValue, useBreakpointValue,
} from "@chakra-ui/core"; } from "@chakra-ui/core";
import { CheckCircleIcon, ExternalLinkIcon } from "@chakra-ui/icons"; import { CheckCircleIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import { OutfitLayers } from "../../components/OutfitPreview";
import useOutfitAppearance from "../../components/useOutfitAppearance";
import useSupportSecret from "./useSupportSecret"; import useSupportSecret from "./useSupportSecret";
/** /**
@ -29,7 +33,7 @@ import useSupportSecret from "./useSupportSecret";
* This component controls the drawer element. The actual content is imported * This component controls the drawer element. The actual content is imported
* from another lazy-loaded component! * from another lazy-loaded component!
*/ */
function ItemSupportDrawer({ item, isOpen, onClose }) { function ItemSupportDrawer({ item, outfitState, isOpen, onClose }) {
const placement = useBreakpointValue({ const placement = useBreakpointValue({
base: "bottom", base: "bottom",
lg: "right", lg: "right",
@ -53,17 +57,26 @@ function ItemSupportDrawer({ item, isOpen, onClose }) {
blockScrollOnMount={false} blockScrollOnMount={false}
> >
<DrawerOverlay> <DrawerOverlay>
<DrawerContent> <DrawerContent
maxHeight={placement === "bottom" ? "90vh" : undefined}
overflow="auto"
>
<DrawerCloseButton /> <DrawerCloseButton />
<DrawerHeader> <DrawerHeader color="green.800">
{item.name} {item.name}
<Badge colorScheme="pink" marginLeft="3"> <Badge colorScheme="pink" marginLeft="3">
Support <span aria-hidden="true">💖</span> Support <span aria-hidden="true">💖</span>
</Badge> </Badge>
</DrawerHeader> </DrawerHeader>
<DrawerBody> <DrawerBody color="green.800">
<Box paddingBottom="5"> <Box paddingBottom="5">
<SpecialColorFields item={item} /> <Stack spacing="8">
<ItemSupportSpecialColorFields item={item} />
<ItemSupportAppearanceFields
item={item}
outfitState={outfitState}
/>
</Stack>
</Box> </Box>
</DrawerBody> </DrawerBody>
</DrawerContent> </DrawerContent>
@ -72,7 +85,7 @@ function ItemSupportDrawer({ item, isOpen, onClose }) {
); );
} }
function SpecialColorFields({ item }) { function ItemSupportSpecialColorFields({ item }) {
const supportSecret = useSupportSecret(); const supportSecret = useSupportSecret();
const { loading: itemLoading, error: itemError, data: itemData } = useQuery( const { loading: itemLoading, error: itemError, data: itemData } = useQuery(
@ -215,4 +228,54 @@ function SpecialColorFields({ item }) {
); );
} }
function ItemSupportAppearanceFields({ item, outfitState }) {
const { speciesId, colorId, pose } = outfitState;
const { error, visibleLayers } = useOutfitAppearance({
speciesId,
colorId,
pose,
wornItemIds: [item.id],
});
const biologyLayers = visibleLayers.filter((l) => l.source === "pet");
const itemLayers = visibleLayers.filter((l) => l.source === "item");
itemLayers.sort((a, b) => a.zone.depth - b.zone.depth);
return (
<FormControl>
<FormLabel>Appearance layers</FormLabel>
<HStack spacing="4" overflow="auto">
{itemLayers.map((itemLayer) => (
<ItemSupportAppearanceLayer
biologyLayers={biologyLayers}
itemLayer={itemLayer}
/>
))}
</HStack>
{error && <FormErrorMessage>{error.message}</FormErrorMessage>}
</FormControl>
);
}
function ItemSupportAppearanceLayer({ biologyLayers, itemLayer }) {
return (
<Box width="150px" textAlign="center" fontSize="xs">
<Box
width="150px"
height="150px"
marginBottom="1"
boxShadow="md"
borderRadius="md"
>
<OutfitLayers visibleLayers={[...biologyLayers, itemLayer]} />
</Box>
<Box>
<b>{itemLayer.zone.label}</b>
</Box>
<Box>Zone ID: {itemLayer.zone.id}</Box>
<Box>Layer ID: {itemLayer.id}</Box>
</Box>
);
}
export default ItemSupportDrawer; export default ItemSupportDrawer;

View file

@ -80,7 +80,13 @@ export function OutfitLayers({
}, []); }, []);
return ( return (
<Box pos="relative" height="100%" width="100%"> <Box
pos="relative"
height="100%"
width="100%"
// Create a stacking context, so the z-indexed layers don't escape!
zIndex="0"
>
{placeholder && ( {placeholder && (
<FullScreenCenter> <FullScreenCenter>
<Box <Box
@ -111,7 +117,7 @@ export function OutfitLayers({
`} `}
timeout={200} timeout={200}
> >
<FullScreenCenter> <FullScreenCenter zIndex={layer.zone.depth}>
<img <img
src={getBestImageUrlForLayer(layer)} src={getBestImageUrlForLayer(layer)}
alt="" alt=""
@ -177,7 +183,7 @@ export function OutfitLayers({
); );
} }
export function FullScreenCenter({ children }) { export function FullScreenCenter({ children, zIndex }) {
return ( return (
<Flex <Flex
pos="absolute" pos="absolute"
@ -187,6 +193,7 @@ export function FullScreenCenter({ children }) {
left="0" left="0"
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
zIndex={zIndex}
> >
{children} {children}
</Flex> </Flex>

View file

@ -91,15 +91,12 @@ export function getVisibleLayers(petAppearance, itemAppearances) {
const validItemAppearances = itemAppearances.filter((a) => a); const validItemAppearances = itemAppearances.filter((a) => a);
const allAppearances = [petAppearance, ...validItemAppearances]; const petLayers = petAppearance.layers.map((l) => ({ ...l, source: "pet" }));
let allLayers = allAppearances.map((a) => a.layers).flat(); const itemLayers = validItemAppearances
.map((a) => a.layers)
// Clean up our data a bit, by ensuring only one layer per zone. This .flat()
// shouldn't happen in theory, but sometimes our database doesn't clean up .map((l) => ({ ...l, source: "item" }));
// after itself correctly :( let allLayers = [...petLayers, ...itemLayers];
allLayers = allLayers.filter((l, i) => {
return allLayers.findIndex((l2) => l2.zone.id === l.zone.id) === i;
});
const allRestrictedZoneIds = validItemAppearances const allRestrictedZoneIds = validItemAppearances
.map((l) => l.restrictedZones) .map((l) => l.restrictedZones)
@ -123,6 +120,7 @@ export const itemAppearanceFragment = gql`
zone { zone {
id id
depth depth
label # HACK: This is for Support tools, but other views don't need it
} }
} }