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:
parent
35aaaeba8e
commit
c5edd20b30
1 changed files with 177 additions and 133 deletions
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue