embed item page in wardrobe page drawer
This commit is contained in:
parent
056b238462
commit
dcf2ec6a26
5 changed files with 77 additions and 27 deletions
|
@ -23,16 +23,24 @@ import OutfitPreview from "./components/OutfitPreview";
|
||||||
|
|
||||||
function ItemPage() {
|
function ItemPage() {
|
||||||
const { itemId } = useParams();
|
const { itemId } = useParams();
|
||||||
|
return <ItemPageContent itemId={itemId} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ItemPageContent is the content of ItemPage, but we also use it as the
|
||||||
|
* entry point for ItemPageDrawer! When embedded in ItemPageDrawer, the
|
||||||
|
* `isEmbedded` prop is true, so we know not to e.g. set the page title.
|
||||||
|
*/
|
||||||
|
export function ItemPageContent({ itemId, isEmbedded }) {
|
||||||
return (
|
return (
|
||||||
<VStack spacing="6">
|
<VStack spacing="6">
|
||||||
<ItemPageHeader itemId={itemId} />
|
<ItemPageHeader itemId={itemId} isEmbedded={isEmbedded} />
|
||||||
<ItemPageOutfitPreview itemId={itemId} />
|
<ItemPageOutfitPreview itemId={itemId} />
|
||||||
</VStack>
|
</VStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ItemPageHeader({ itemId }) {
|
function ItemPageHeader({ itemId, isEmbedded }) {
|
||||||
const { error, data } = useQuery(
|
const { error, data } = useQuery(
|
||||||
gql`
|
gql`
|
||||||
query ItemPage($itemId: ID!) {
|
query ItemPage($itemId: ID!) {
|
||||||
|
@ -47,7 +55,7 @@ function ItemPageHeader({ itemId }) {
|
||||||
{ variables: { itemId }, returnPartialData: true }
|
{ variables: { itemId }, returnPartialData: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
usePageTitle(data?.item?.name);
|
usePageTitle(data?.item?.name, { skip: isEmbedded });
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <Box color="red.400">{error.message}</Box>;
|
return <Box color="red.400">{error.message}</Box>;
|
||||||
|
@ -67,7 +75,14 @@ function ItemPageHeader({ itemId }) {
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
<Box>
|
<Box>
|
||||||
<Skeleton isLoaded={item?.name}>
|
<Skeleton isLoaded={item?.name}>
|
||||||
<Heading1 lineHeight="1.1">{item?.name || "Item name here"}</Heading1>
|
<Heading1
|
||||||
|
lineHeight="1.1"
|
||||||
|
// Nudge down the size a bit in the embed case, to better fit the
|
||||||
|
// tighter layout!
|
||||||
|
size={isEmbedded ? "xl" : "2xl"}
|
||||||
|
>
|
||||||
|
{item?.name || "Item name here"}
|
||||||
|
</Heading1>
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
<ItemPageBadges item={item} />
|
<ItemPageBadges item={item} />
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -131,7 +146,7 @@ function ItemPageBadges({ item }) {
|
||||||
encodeURIComponent(item.name)
|
encodeURIComponent(item.name)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Trades
|
Trade Post
|
||||||
</LinkBadge>
|
</LinkBadge>
|
||||||
)}
|
)}
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
|
|
30
src/app/ItemPageDrawer.js
Normal file
30
src/app/ItemPageDrawer.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerBody,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerCloseButton,
|
||||||
|
DrawerOverlay,
|
||||||
|
useBreakpointValue,
|
||||||
|
} from "@chakra-ui/core";
|
||||||
|
|
||||||
|
import { ItemPageContent } from "./ItemPage";
|
||||||
|
|
||||||
|
function ItemPageDrawer({ item, isOpen, onClose }) {
|
||||||
|
const placement = useBreakpointValue({ base: "bottom", lg: "right" });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer placement={placement} size="md" isOpen={isOpen} onClose={onClose}>
|
||||||
|
<DrawerOverlay>
|
||||||
|
<DrawerContent>
|
||||||
|
<DrawerCloseButton />
|
||||||
|
<DrawerBody>
|
||||||
|
<ItemPageContent itemId={item.id} isEmbedded />
|
||||||
|
</DrawerBody>
|
||||||
|
</DrawerContent>
|
||||||
|
</DrawerOverlay>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ItemPageDrawer;
|
|
@ -16,6 +16,7 @@ import {
|
||||||
InfoIcon,
|
InfoIcon,
|
||||||
NotAllowedIcon,
|
NotAllowedIcon,
|
||||||
} from "@chakra-ui/icons";
|
} from "@chakra-ui/icons";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
import loadable from "@loadable/component";
|
import loadable from "@loadable/component";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -27,6 +28,7 @@ import {
|
||||||
} from "../components/ItemCard";
|
} from "../components/ItemCard";
|
||||||
import SupportOnly from "./support/SupportOnly";
|
import SupportOnly from "./support/SupportOnly";
|
||||||
|
|
||||||
|
const LoadableItemPageDrawer = loadable(() => import("../ItemPageDrawer"));
|
||||||
const LoadableItemSupportDrawer = loadable(() =>
|
const LoadableItemSupportDrawer = loadable(() =>
|
||||||
import("./support/ItemSupportDrawer")
|
import("./support/ItemSupportDrawer")
|
||||||
);
|
);
|
||||||
|
@ -58,6 +60,7 @@ function Item({
|
||||||
onRemove,
|
onRemove,
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
}) {
|
}) {
|
||||||
|
const [infoDrawerIsOpen, setInfoDrawerIsOpen] = React.useState(false);
|
||||||
const [supportDrawerIsOpen, setSupportDrawerIsOpen] = React.useState(false);
|
const [supportDrawerIsOpen, setSupportDrawerIsOpen] = React.useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -97,13 +100,25 @@ function Item({
|
||||||
<ItemActionButton
|
<ItemActionButton
|
||||||
icon={<InfoIcon />}
|
icon={<InfoIcon />}
|
||||||
label="More info"
|
label="More info"
|
||||||
href={`https://impress.openneo.net/items/${
|
to={`/items/${item.id}`}
|
||||||
item.id
|
onClick={(e) => {
|
||||||
}-${item.name.replace(/ /g, "-")}`}
|
const willProbablyOpenInNewTab =
|
||||||
onClick={(e) => e.stopPropagation()}
|
e.metaKey || e.shiftKey || e.altKey || e.ctrlKey;
|
||||||
|
if (willProbablyOpenInNewTab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setInfoDrawerIsOpen(true);
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</ItemContainer>
|
</ItemContainer>
|
||||||
|
<LoadableItemPageDrawer
|
||||||
|
item={item}
|
||||||
|
isOpen={infoDrawerIsOpen}
|
||||||
|
onClose={() => setInfoDrawerIsOpen(false)}
|
||||||
|
/>
|
||||||
<SupportOnly>
|
<SupportOnly>
|
||||||
<LoadableItemSupportDrawer
|
<LoadableItemSupportDrawer
|
||||||
item={item}
|
item={item}
|
||||||
|
@ -220,7 +235,7 @@ function ItemBadges({ item }) {
|
||||||
/**
|
/**
|
||||||
* ItemActionButton is one of a list of actions a user can take for this item.
|
* ItemActionButton is one of a list of actions a user can take for this item.
|
||||||
*/
|
*/
|
||||||
function ItemActionButton({ icon, label, href, onClick }) {
|
function ItemActionButton({ icon, label, to, onClick }) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const focusBackgroundColor = useColorModeValue(
|
const focusBackgroundColor = useColorModeValue(
|
||||||
|
@ -235,13 +250,12 @@ function ItemActionButton({ icon, label, href, onClick }) {
|
||||||
return (
|
return (
|
||||||
<Tooltip label={label} placement="top">
|
<Tooltip label={label} placement="top">
|
||||||
<IconButton
|
<IconButton
|
||||||
as={href ? "a" : "button"}
|
as={to ? Link : "button"}
|
||||||
icon={icon}
|
icon={icon}
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
color="gray.400"
|
color="gray.400"
|
||||||
href={href}
|
to={to}
|
||||||
target={href ? "_blank" : null}
|
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={css`
|
className={css`
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
|
@ -41,22 +41,12 @@ import useSupport from "./useSupport";
|
||||||
* from another lazy-loaded component!
|
* from another lazy-loaded component!
|
||||||
*/
|
*/
|
||||||
function ItemSupportDrawer({ item, isOpen, onClose }) {
|
function ItemSupportDrawer({ item, isOpen, onClose }) {
|
||||||
const placement = useBreakpointValue({
|
const placement = useBreakpointValue({ base: "bottom", lg: "right" });
|
||||||
base: "bottom",
|
|
||||||
lg: "right",
|
|
||||||
|
|
||||||
// TODO: There's a bug in the Chakra RC that doesn't read the breakpoint
|
|
||||||
// specification correctly - we need these extra keys until it's fixed!
|
|
||||||
// https://github.com/chakra-ui/chakra-ui/issues/1444
|
|
||||||
0: "bottom",
|
|
||||||
1: "bottom",
|
|
||||||
2: "right",
|
|
||||||
3: "right",
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
placement={placement}
|
placement={placement}
|
||||||
|
size="md"
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
// blockScrollOnMount doesn't matter on our fullscreen UI, but the
|
// blockScrollOnMount doesn't matter on our fullscreen UI, but the
|
||||||
|
|
|
@ -111,10 +111,11 @@ export function useDebounce(
|
||||||
/**
|
/**
|
||||||
* usePageTitle sets the page title!
|
* usePageTitle sets the page title!
|
||||||
*/
|
*/
|
||||||
export function usePageTitle(title) {
|
export function usePageTitle(title, { skip = false } = {}) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
if (skip) return;
|
||||||
document.title = title ? `${title} | Dress to Impress` : "Dress to Impress";
|
document.title = title ? `${title} | Dress to Impress` : "Dress to Impress";
|
||||||
}, [title]);
|
}, [title, skip]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue