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:
parent
c5edd20b30
commit
ddfdd5fc11
3 changed files with 110 additions and 1 deletions
92
app/javascript/wardrobe-2020/WardrobePage/LayersInfoModal.js
Normal file
92
app/javascript/wardrobe-2020/WardrobePage/LayersInfoModal.js
Normal 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;
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -139,6 +139,7 @@ export const appearanceLayerFragment = gql`
|
|||
depth
|
||||
label
|
||||
}
|
||||
swfUrl # For the layer info modal
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
Loading…
Reference in a new issue