Advertisement
wa12rior

React generic select component

Feb 21st, 2022
793
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { ComponentProps, ComponentType, useCallback, useState } from 'react';
  2. import { Interactive } from 'react-interactive';
  3. import classNames from 'classnames';
  4.  
  5. export type onSelectionCallback = (value: string) => void;
  6.  
  7. export type SelectProps = ComponentProps<typeof Interactive> & {
  8.   variant?: 'primary' | 'secondary';
  9.   values: string[];
  10.   placeholder?: string;
  11.   onSelection?: onSelectionCallback;
  12. };
  13.  
  14. const border = `border border-1 border-white
  15.     `;
  16.  
  17. const CLASSES = {
  18.   base: 'py-5 px-8 font-bold text-white hover:no-underline text-center',
  19.   variant: {
  20.     primary: `bg-red ${border}`,
  21.     secondary: `bg-black`,
  22.   },
  23. };
  24.  
  25. const DROPDOWN_CLASSES = {
  26.   base: 'z-10 absolute top-[64px] mt-1 py-2 px-8 text-white list-none',
  27.   variant: {
  28.     primary: 'bg-red border border-white border-1',
  29.     secondary: 'bg-black',
  30.   }
  31. };
  32.  
  33. const DROPDOWN_ITEM_CLASSES = 'block font-bold text-white py-2 hover:text-white';
  34.  
  35. export const Select: ComponentType<SelectProps> = ({
  36.   as = 'button',
  37.   variant = 'primary',
  38.   className,
  39.   onSelection,
  40.   placeholder,
  41.   values,
  42.   ...props
  43. }) => {
  44.   const [active, setActive] = useState(false)
  45.   const [activeSelection, setActiveSelection] = useState(values[0])
  46.  
  47.   const handleDropdown = useCallback(
  48.     ({state, prevState}) => {
  49.       if (state.active) {
  50.         setActive(true)
  51.       }
  52.  
  53.       if (state.focus === false) {
  54.         setActive(false)
  55.       }
  56.     },
  57.     [active]
  58.   )
  59.  
  60.   return (
  61.     <div className="inline-block relative">
  62.       <Interactive
  63.         className={classNames(
  64.           CLASSES.base,
  65.           CLASSES.variant[variant],
  66.           className
  67.         )}
  68.         as={as}
  69.         onStateChange={handleDropdown}
  70.         {...props}
  71.       ><div className="flex items-center">{placeholder ? placeholder : activeSelection} <ArrowDown rotate={active} /></div></Interactive>
  72.       <div className={classNames(DROPDOWN_CLASSES.base, { hidden: !active }, CLASSES.variant[variant])}>
  73.         <ul className="py-1" aria-labelledby="dropdownButton">
  74.           {
  75.             values.map((item, key) => {
  76.               return (
  77.                 <li key={key}>
  78.                   <Interactive className={DROPDOWN_ITEM_CLASSES} onStateChange={({ state, prevState }) => { if (state.focus !== false && prevState.focus === false) { setActiveSelection(item); if (onSelection) onSelection(item) } }}>{item}</Interactive>
  79.                 </li>
  80.               )
  81.             })
  82.           }
  83.         </ul>
  84.       </div>
  85.   </div>
  86. );
  87. }
  88.  
  89. const ArrowDown: React.FC<{rotate: boolean}> = ({ rotate }) => {
  90.   return (
  91.     <div className="ml-4">
  92.       <svg width="13" height="9" viewBox="0 0 13 9" fill="none" xmlns="http://www.w3.org/2000/svg" className={`transition .3s ease ${rotate ? '-rotate-90' : ''}`}>
  93.         <path fillRule="evenodd" clipRule="evenodd" d="M3.09159 1.99951L7.00159 6.82151L10.9866 1.99951H3.09159ZM6.99959 8.99951C6.49359 8.99951 6.00959 8.76651 5.67359 8.35751L1.46059 3.25951C0.956594 2.64851 0.856594 1.78151 1.20159 1.04851C1.50659 0.401512 2.11359 -0.000488281 2.78659 -0.000488281H11.2126C11.8856 -0.000488281 12.4926 0.401512 12.7976 1.04851C13.1436 1.78151 13.0426 2.64851 12.5396 3.25851L8.32559 8.35751C7.98959 8.76651 7.50559 8.99951 6.99959 8.99951Z" fill="white"/>
  94.       </svg>
  95.     </div>
  96.   )
  97. };
  98.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement