Add outfit context menu, with Download button

A better affordance than the popup telling people not to do this lol

and I'm planning to maybe add the button for SWF etc info here too!
This commit is contained in:
Emi Matchu 2023-10-15 13:38:04 -07:00
parent 35aaaeba8e
commit c5edd20b30

View file

@ -11,6 +11,9 @@ import {
HStack,
IconButton,
ListItem,
Menu,
MenuItem,
MenuList,
Popover,
PopoverArrow,
PopoverBody,
@ -106,6 +109,7 @@ function OutfitControls({
return (
<ClassNames>
{({ css, cx }) => (
<OutfitControlsContextMenu outfitState={outfitState}>
<Box
role="group"
pos="absolute"
@ -160,18 +164,6 @@ function OutfitControls({
setFocusIsLocked(true);
}
}}
onContextMenuCapture={() => {
if (!toast.isActive("outfit-controls-context-menu-hint")) {
toast({
id: "outfit-controls-context-menu-hint",
title:
"By the way, to save this image, use the Download button!",
description: "It's in the top right of the preview area.",
duration: 10000,
isClosable: true,
});
}
}}
data-test-id="wardrobe-outfit-controls"
>
<Box gridArea="back" onClick={maybeUnlockFocus}>
@ -212,7 +204,11 @@ function OutfitControls({
</Stack>
<Box gridArea="space" onClick={maybeUnlockFocus} />
{outfitState.speciesId && outfitState.colorId && (
<Flex gridArea="picker" justify="center" onClick={maybeUnlockFocus}>
<Flex
gridArea="picker"
justify="center"
onClick={maybeUnlockFocus}
>
{/**
* We try to center the species/color picker, but the left spacer will
* shrink more than the pose picker container if we run out of space!
@ -251,11 +247,59 @@ function OutfitControls({
</Flex>
)}
</Box>
</OutfitControlsContextMenu>
)}
</ClassNames>
);
}
function OutfitControlsContextMenu({ outfitState, children }) {
// NOTE: We track these separately, rather than in one atomic state object,
// because I want to still keep the menu in the right position when it's
// animating itself closed!
const [isOpen, setIsOpen] = React.useState(false);
const [position, setPosition] = React.useState({ x: 0, y: 0 });
const { visibleLayers } = useOutfitAppearance(outfitState);
const [downloadImageUrl, prepareDownload] =
useDownloadableImage(visibleLayers);
return (
<Box
onContextMenuCapture={(e) => {
setIsOpen(true);
setPosition({ x: e.pageX, y: e.pageY });
e.preventDefault();
}}
>
{children}
<Menu isOpen={isOpen} onClose={() => setIsOpen(false)}>
<Portal>
<MenuList position="absolute" left={position.x} top={position.y}>
<MenuItem
icon={<DownloadIcon />}
as="a"
// eslint-disable-next-line no-script-url
href={downloadImageUrl || "#"}
onClick={(e) => {
if (!downloadImageUrl) {
e.preventDefault();
}
}}
download={(outfitState.name || "Outfit") + ".png"}
onMouseEnter={prepareDownload}
onFocus={prepareDownload}
cursor={!downloadImageUrl && "wait"}
>
Download
</MenuItem>
</MenuList>
</Portal>
</Menu>
</Box>
);
}
function OutfitHTML5Badge({ appearance }) {
const petIsUsingHTML5 =
appearance.petAppearance?.layers.every(layerUsesHTML5);