Add outfit layers info modal to download PNGs etc

This is an important workflow for people doing art stuff, I'm told! They used to use the Classic DTI broken image UI for this, but now that that's uhh Fully Gone, let's add this more explicitly!
This commit is contained in:
Emi Matchu 2023-10-15 14:20:55 -07:00
parent c5edd20b30
commit ddfdd5fc11
3 changed files with 110 additions and 1 deletions

View file

@ -0,0 +1,92 @@
import React from "react";
import {
Box,
Button,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Table,
Tbody,
Td,
Th,
Thead,
Tr,
} from "@chakra-ui/react";
function LayersInfoModal({ isOpen, onClose, visibleLayers }) {
return (
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay>
<ModalContent maxWidth="800px">
<ModalHeader>Outfit layers</ModalHeader>
<ModalCloseButton />
<ModalBody>
<LayerTable layers={visibleLayers} />
</ModalBody>
</ModalContent>
</ModalOverlay>
</Modal>
);
}
function LayerTable({ layers }) {
return (
<Table>
<Thead>
<Tr>
<Th>Preview</Th>
<Th>DTI ID</Th>
<Th>Zone</Th>
<Th>Links</Th>
</Tr>
</Thead>
<Tbody>
{layers.map((layer) => (
<LayerTableRow key={layer.id} layer={layer} />
))}
</Tbody>
</Table>
);
}
function LayerTableRow({ layer, ...props }) {
return (
<Tr {...props}>
<Td>
<Box
as="img"
src={layer.imageUrl}
width="60px"
height="60px"
boxShadow="md"
/>
</Td>
<Td>{layer.id}</Td>
<Td>{layer.zone.label}</Td>
<Td>
<Box display="flex" gap=".5em">
{layer.imageUrl && (
<Button as="a" href={layer.imageUrl} target="_blank" size="sm">
PNG
</Button>
)}
{layer.swfUrl && (
<Button as="a" href={layer.swfUrl} size="sm" download>
SWF
</Button>
)}
{layer.svgUrl && (
<Button as="a" href={layer.svgUrl} target="_blank" size="sm">
SVG
</Button>
)}
</Box>
</Td>
</Tr>
);
}
export default LayersInfoModal;

View file

@ -41,12 +41,14 @@ import { getBestImageUrlForLayer } from "../components/OutfitPreview";
import HTML5Badge, { layerUsesHTML5 } from "../components/HTML5Badge";
import PosePicker from "./PosePicker";
import SpeciesColorPicker from "../components/SpeciesColorPicker";
import { loadImage, useLocalStorage } from "../util";
import { loadImage, loadable, useLocalStorage } from "../util";
import useCurrentUser from "../components/useCurrentUser";
import useOutfitAppearance from "../components/useOutfitAppearance";
import OutfitKnownGlitchesBadge from "./OutfitKnownGlitchesBadge";
import usePreferArchive from "../components/usePreferArchive";
const LoadableLayersInfoModal = loadable(() => import("./LayersInfoModal"));
/**
* OutfitControls is the set of controls layered over the outfit preview, to
* control things like species/color and sharing links!
@ -260,6 +262,9 @@ function OutfitControlsContextMenu({ outfitState, children }) {
const [isOpen, setIsOpen] = React.useState(false);
const [position, setPosition] = React.useState({ x: 0, y: 0 });
const [layersInfoModalIsOpen, setLayersInfoModalIsOpen] =
React.useState(false);
const { visibleLayers } = useOutfitAppearance(outfitState);
const [downloadImageUrl, prepareDownload] =
useDownloadableImage(visibleLayers);
@ -293,9 +298,20 @@ function OutfitControlsContextMenu({ outfitState, children }) {
>
Download
</MenuItem>
<MenuItem
icon={<LinkIcon />}
onClick={() => setLayersInfoModalIsOpen(true)}
>
Layers (SWF, PNG)
</MenuItem>
</MenuList>
</Portal>
</Menu>
<LoadableLayersInfoModal
isOpen={layersInfoModalIsOpen}
onClose={() => setLayersInfoModalIsOpen(false)}
visibleLayers={visibleLayers}
/>
</Box>
);
}

View file

@ -139,6 +139,7 @@ export const appearanceLayerFragment = gql`
depth
label
}
swfUrl # For the layer info modal
}
`;