123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- import React, { useRef, useState, useCallback, useEffect } from "react";
- import { CSSTransition } from "react-transition-group";
- import flagLookup from "../../../domain/flagLookup";
- import styles from "./Dropdown.module.css";
- export const Item = ({ value, display, onSelect, children }) => {
- const onClick = () =>
- onSelect(value ?? children, display ?? value ?? children);
- return (
- <div
- tabIndex="0"
- role="menuitem"
- className={styles.item}
- onClick={onClick}
- onKeyDown={({ key }) => {
- if (key === "Enter") {
- onClick();
- }
- }}
- >
- {children ?? display ?? value}
- </div>
- );
- };
- export const Dropdown = ({ selected, open, onSelect, onClick, children }) => {
- const transitionRef = useRef(null);
- const [displayed, setDisplayed] = useState(null);
- const onSelectCallback = useCallback(
- (value, display) => {
- setDisplayed(display);
- onSelect(value);
- },
- [onSelect]
- );
- useEffect(() => {
- if (selected === undefined) {
- return;
- }
- let found = null;
- React.Children.toArray(children).forEach(element => {
- if (
- React.isValidElement(element) &&
- found === null &&
- element.props.value === selected
- ) {
- const { value, display } = element.props;
- found = display ?? value;
- }
- });
- setDisplayed(found);
- }, [children, selected]);
- return (
- <div className={styles.container}>
- <div
- className={styles.button}
- role="button"
- tabIndex="0"
- onClick={onClick}
- onKeyDown={({ key }) => {
- if (key === "Enter") {
- onClick();
- }
- }}
- >
- {displayed}
- </div>
- <CSSTransition
- nodeRef={transitionRef}
- in={open}
- timeout={200}
- mountOnEnter
- unmountOnExit
- classNames={{
- enter: styles["list-enter"],
- enterActive: styles["list-enter-active"],
- exit: styles["list-exit"],
- exitActive: styles["list-exit-active"],
- }}
- >
- <div className={styles.list} role="menu" ref={transitionRef}>
- {React.Children.toArray(children).map(child =>
- React.cloneElement(child, {
- onSelect: onSelectCallback,
- key: JSON.stringify(child.props.value),
- })
- )}
- </div>
- </CSSTransition>
- </div>
- );
- };
- export const CountryDropdown = ({
- countryLookup,
- selected,
- onSelect,
- onClick,
- open,
- }) => {
- const transitionRef = useRef(null);
- const [search, setSearch] = useState("");
- const found = countryLookup(search) ?? [];
- const onSelectCallback = useCallback(
- code => {
- setSearch("");
- onSelect(code);
- },
- [onSelect]
- );
- return (
- <div className={styles.container}>
- <div
- className={styles.button}
- role="button"
- tabIndex="0"
- onClick={onClick}
- onKeyDown={({ key }) => {
- if (key === "Enter") {
- onClick();
- }
- }}
- >
- {flagLookup(selected)}
- </div>
- <CSSTransition
- nodeRef={transitionRef}
- in={open}
- timeout={200}
- mountOnEnter
- unmountOnExit
- classNames={{
- enter: styles["list-enter"],
- enterActive: styles["list-enter-active"],
- exit: styles["list-exit"],
- exitActive: styles["list-exit-active"],
- }}
- >
- <div className={styles.list} role="menu" ref={transitionRef}>
- <input
- className={styles.search}
- autoFocus
- type="text"
- value={search}
- onChange={({ target }) => setSearch(target.value)}
- onKeyDown={({ key }) => {
- if (key === "Enter") {
- onSelectCallback(found?.[0]?.item?.alpha2);
- } else if (key === "Escape") {
- onSelectCallback(selected);
- }
- }}
- />
- {found.map(({ item: { country, alpha2 } }) => (
- <div
- role="button"
- tabIndex="0"
- key={alpha2}
- className={styles.item}
- onClick={() => onSelectCallback(alpha2)}
- onKeyDown={({ key }) => {
- if (key === "Enter") {
- onSelectCallback(alpha2);
- }
- }}
- >
- {flagLookup(alpha2)} - {country}
- </div>
- ))}
- <div
- className={styles.item}
- role="menuitem"
- tabIndex="0"
- onClick={() => onSelectCallback(null)}
- onKeyDown={({ key }) => {
- if (key === "Enter") {
- onSelectCallback(null);
- }
- }}
- >
- {flagLookup(null)} - All Countries
- </div>
- </div>
- </CSSTransition>
- </div>
- );
- };
- export const DropdownGroup = ({ children }) => {
- const [open, setOpen] = useState(null);
- return (
- <>
- {children.map(child =>
- React.cloneElement(child, {
- open: open === child.props.open,
- onClick: () =>
- setOpen(o => (o === child.props.open ? null : child.props.open)),
- onSelect: v => {
- child.props.onSelect(v);
- setOpen(null);
- },
- key: child.props.open,
- })
- )}
- </>
- );
- };
|