saleor-dashboard/src/components/LinkChoice/LinkChoice.tsx

162 lines
4 KiB
TypeScript
Raw Normal View History

2020-02-07 16:12:01 +00:00
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
import MenuItem from "@material-ui/core/MenuItem";
import Paper from "@material-ui/core/Paper";
2020-02-07 16:12:01 +00:00
import Popper from "@material-ui/core/Popper";
import { fade } from "@material-ui/core/styles/colorManipulator";
import { FormChange } from "@saleor/hooks/useForm";
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
import { makeStyles } from "@saleor/theme";
2020-02-07 16:12:01 +00:00
import classNames from "classnames";
import { codes } from "keycode";
import React from "react";
2020-02-07 16:12:01 +00:00
import Link from "../Link";
import { SingleAutocompleteChoiceType } from "../SingleAutocompleteSelectField";
2020-02-07 16:12:01 +00:00
const useStyles = makeStyles(
theme => ({
arrow: {
position: "relative",
top: 6,
transition: theme.transitions.duration.short + "ms"
},
highlighted: {
background: theme.palette.background.default
},
menuItem: {
"&:not(:last-of-type)": {
marginBottom: theme.spacing()
}
},
paper: {
padding: theme.spacing()
},
popper: {
boxShadow: `0px 5px 10px 0 ${fade(theme.palette.common.black, 0.05)}`,
marginTop: theme.spacing(1),
zIndex: 2
},
root: {
"&:focus": {
textDecoration: "underline"
},
outline: 0,
position: "relative"
},
rotate: {
transform: "rotate(180deg)"
}
}),
{
name: "LinkChoice"
}
);
export interface LinkChoiceProps {
className?: string;
2020-02-07 16:12:01 +00:00
choices: SingleAutocompleteChoiceType[];
name?: string;
2020-02-07 16:12:01 +00:00
value: string;
onChange: FormChange;
}
const LinkChoice: React.FC<LinkChoiceProps> = ({
className,
2020-02-07 16:12:01 +00:00
choices,
name,
value,
onChange
}) => {
const classes = useStyles({});
const [open, setOpen] = React.useState(false);
const anchor = React.useRef<HTMLInputElement>(null);
const current = choices.find(c => c.value === value);
const [highlightedIndex, setHighlightedIndex] = React.useState(0);
const handleChange = (value: string) => {
setOpen(false);
onChange({
target: {
name,
value
}
});
};
const handleKeyPress = (event: React.KeyboardEvent<HTMLSpanElement>) => {
switch (event.keyCode) {
case codes.down:
setHighlightedIndex(
highlightedIndex => (highlightedIndex + 1) % choices.length
);
break;
case codes.up:
setHighlightedIndex(highlightedIndex =>
highlightedIndex === 0
? choices.length - 1
: (highlightedIndex - 1) % choices.length
);
break;
case codes.enter:
if (open) {
handleChange(choices[highlightedIndex].value);
} else {
setOpen(true);
}
break;
}
};
return (
<span
className={classNames(classes.root, className)}
2020-02-07 16:12:01 +00:00
ref={anchor}
onKeyDown={handleKeyPress}
tabIndex={0}
>
2020-04-29 14:14:20 +00:00
<Link onClick={() => setOpen(open => !open)}>
{current.label}
<ArrowDropdown
className={classNames(classes.arrow, {
[classes.rotate]: open
})}
color="primary"
/>
</Link>
2020-02-07 16:12:01 +00:00
<Popper
className={classes.popper}
open={open}
anchorEl={anchor.current}
transition
disablePortal
placement="bottom-start"
>
<ClickAwayListener
onClickAway={() => setOpen(false)}
mouseEvent="onClick"
>
<Paper className={classes.paper}>
{choices.map((choice, choiceIndex) => (
<MenuItem
className={classNames(classes.menuItem, {
[classes.highlighted]: highlightedIndex === choiceIndex
})}
selected={choice.value === value}
key={choice.value}
onClick={() => handleChange(choice.value)}
data-test="select-option"
2020-02-07 16:12:01 +00:00
>
{choice.label}
</MenuItem>
))}
</Paper>
</ClickAwayListener>
</Popper>
</span>
);
};
LinkChoice.displayName = "LinkChoice";
export default LinkChoice;