Add expand-on-hover effect to "Customize more"
This commit is contained in:
parent
7620d3f315
commit
6738f07e64
1 changed files with 74 additions and 18 deletions
|
@ -19,6 +19,7 @@ import {
|
||||||
Wrap,
|
Wrap,
|
||||||
WrapItem,
|
WrapItem,
|
||||||
Flex,
|
Flex,
|
||||||
|
usePrefersReducedMotion,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import {
|
import {
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
|
@ -34,7 +35,7 @@ import { useQuery, useMutation } from "@apollo/client";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
|
|
||||||
import ItemPageLayout, { SubtleSkeleton } from "./ItemPageLayout";
|
import ItemPageLayout, { SubtleSkeleton } from "./ItemPageLayout";
|
||||||
import { Delay, usePageTitle } from "./util";
|
import { Delay, logAndCapture, usePageTitle } from "./util";
|
||||||
import {
|
import {
|
||||||
itemAppearanceFragment,
|
itemAppearanceFragment,
|
||||||
petAppearanceFragment,
|
petAppearanceFragment,
|
||||||
|
@ -813,25 +814,80 @@ function CustomizeMoreButton({ speciesId, colorId, pose, itemId }) {
|
||||||
// The default background is good in light mode, but in dark mode it's a
|
// The default background is good in light mode, but in dark mode it's a
|
||||||
// very subtle transparent white... make it a semi-transparent black, for
|
// very subtle transparent white... make it a semi-transparent black, for
|
||||||
// better contrast against light-colored background items!
|
// better contrast against light-colored background items!
|
||||||
const backgroundColor = useColorModeValue(undefined, "blackAlpha.600");
|
const backgroundColor = useColorModeValue(undefined, "blackAlpha.700");
|
||||||
const backgroundColorHover = useColorModeValue(undefined, "blackAlpha.700");
|
const backgroundColorHover = useColorModeValue(undefined, "blackAlpha.900");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label="Customize more" placement="left" colorScheme="white">
|
<Button
|
||||||
<IconButton
|
as={Link}
|
||||||
as={Link}
|
to={url}
|
||||||
to={url}
|
role="group"
|
||||||
icon={<EditIcon />}
|
position="absolute"
|
||||||
position="absolute"
|
top="2"
|
||||||
top="2"
|
right="2"
|
||||||
right="2"
|
size="sm"
|
||||||
size="sm"
|
background={backgroundColor}
|
||||||
background={backgroundColor}
|
_hover={{ backgroundColor: backgroundColorHover }}
|
||||||
_hover={{ backgroundColor: backgroundColorHover }}
|
_focus={{ backgroundColor: backgroundColorHover, boxShadow: "outline" }}
|
||||||
_focus={{ backgroundColor: backgroundColorHover }}
|
boxShadow="sm"
|
||||||
boxShadow="sm"
|
>
|
||||||
/>
|
<ExpandOnGroupHover paddingRight="2">Customize more</ExpandOnGroupHover>
|
||||||
</Tooltip>
|
<EditIcon />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ExpandOnGroupHover starts at width=0, and expands to full width when a
|
||||||
|
* parent with role="group" gains hover or focus state.
|
||||||
|
*/
|
||||||
|
function ExpandOnGroupHover({ children, ...props }) {
|
||||||
|
const [measuredWidth, setMeasuredWidth] = React.useState(null);
|
||||||
|
const measurerRef = React.useRef(null);
|
||||||
|
const prefersReducedMotion = usePrefersReducedMotion();
|
||||||
|
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
if (!measurerRef) {
|
||||||
|
// I don't think this is possible, but I'd like to know if it happens!
|
||||||
|
logAndCapture(
|
||||||
|
new Error(
|
||||||
|
`Measurer node not ready during effect. Transition won't be smooth.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (measuredWidth != null) {
|
||||||
|
// Skip re-measuring when we already have a measured width. This is
|
||||||
|
// mainly defensive, to prevent the possibility of loops, even though
|
||||||
|
// this algorithm should be stable!
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMeasuredWidth = measurerRef.current.offsetWidth;
|
||||||
|
setMeasuredWidth(newMeasuredWidth);
|
||||||
|
}, [measuredWidth]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
// In block layout, the overflowing children would _also_ be constrained
|
||||||
|
// to width 0. But in flex layout, overflowing children _keep_ their
|
||||||
|
// natural size, so we can measure it even when not visible.
|
||||||
|
width="0"
|
||||||
|
overflow="hidden"
|
||||||
|
// Right-align the children, to keep the text feeling right-aligned when
|
||||||
|
// we expand. (To support left-side expansion, make this a prop!)
|
||||||
|
justify="flex-end"
|
||||||
|
// If the width somehow isn't measured yet, expand to width `auto`, which
|
||||||
|
// won't transition smoothly but at least will work!
|
||||||
|
_groupHover={{ width: measuredWidth ? measuredWidth + "px" : "auto" }}
|
||||||
|
_groupFocus={{ width: measuredWidth ? measuredWidth + "px" : "auto" }}
|
||||||
|
transition={!prefersReducedMotion && "width 0.2s"}
|
||||||
|
>
|
||||||
|
<Box ref={measurerRef} {...props}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue