Dropdown.jsx 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import React, { useRef, useState, useCallback, useEffect } from 'react';
  2. import { CSSTransition } from 'react-transition-group';
  3. import styles from './Dropdown.module.css';
  4. export const Item = ({ value, display, onSelect, children }) => (
  5. <div className={styles.item} onClick={() => onSelect(value ?? children, display ?? value ?? children)}>
  6. {children ?? display ?? value}
  7. </div>
  8. );
  9. export const Dropdown = ({ open, onSelect, onClick, children }) => {
  10. const transitionRef = useRef(null);
  11. const [ displayed, setDisplayed ] = useState(null);
  12. const onSelectCallback = useCallback((value, display) => {
  13. setDisplayed(display);
  14. onSelect(value);
  15. }, [onSelect]);
  16. useEffect(() => {
  17. if (displayed) {
  18. return;
  19. }
  20. let found = null;
  21. children.forEach(element => {
  22. if (React.isValidElement(element) && !found && element.props['default']) {
  23. const { value, display } = element.props;
  24. found = display ?? value;
  25. }
  26. });
  27. setDisplayed(found);
  28. }, [children, displayed]);
  29. return (
  30. <div className={styles.container}>
  31. <div className={styles.button} onClick={onClick}>{displayed}</div>
  32. <CSSTransition nodeRef={transitionRef} in={open} timeout={200} mountOnEnter unmountOnExit classNames={{
  33. enter: styles['list-enter'],
  34. enterActive: styles['list-enter-active'],
  35. exit: styles['list-exit'],
  36. exitActive: styles['list-exit-active'],
  37. }}>
  38. <div className={styles.list} ref={transitionRef}>
  39. {children.map((child, key) => React.cloneElement(child, { onSelect: onSelectCallback, key }))}
  40. </div>
  41. </CSSTransition>
  42. </div>
  43. )
  44. };
  45. export const DropdownGroup = ({ children }) => {
  46. const [ open, setOpen ] = useState(null);
  47. return (
  48. <>
  49. {
  50. children.map((child, key) => React.cloneElement(child, {
  51. open: open === child.props.open,
  52. onClick: () => setOpen(o => o === child.props.open ? null : child.props.open),
  53. onSelect: v => { child.props.onSelect(v); setOpen(null); },
  54. key,
  55. }))
  56. }
  57. </>
  58. );
  59. }