Advertisement
Guest User

Untitled

a guest
Jul 29th, 2019
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //@ts-check
  2. import React from 'react';
  3. import jss from 'react-jss';
  4. import Typography from 'application/Modules/Common/Components/Typography';
  5. import Downshift from 'downshift';
  6. import { AutoSizer, List } from 'react-virtualized';
  7. import cx from 'classnames';
  8. import themeType, { cpg } from 'application/Utils/theme';
  9.  
  10. //@ts-ignore
  11. import ArrowDown from './arrow-down.svg';
  12. //@ts-ignore
  13. import Search from './search.svg';
  14. import RadioListElement from '../RadioListElement';
  15.  
  16. /**
  17.  * @param {typeof themeType & typeof cpg} theme
  18.  */
  19. const styles = theme => ({
  20.     label: {
  21.         marginLeft: 8,
  22.         marginBottom: 2,
  23.     },
  24.     content: {
  25.         borderRadius: 3,
  26.         backgroundColor: '#F2F4F7',
  27.         padding: 8,
  28.         cursor: 'pointer',
  29.         position: 'relative',
  30.         '&:focus': {
  31.             outline: '1px dotted #106BFE',
  32.         },
  33.     },
  34.     contentArrow: {
  35.         position: 'absolute',
  36.         top: '50%',
  37.         right: 13,
  38.         transform: 'translateY(-50%)',
  39.         width: 8,
  40.         height: 5,
  41.     },
  42.     wrapper: {
  43.         position: 'relative',
  44.         backgroundColor: '#ffffff',
  45.     },
  46.     wrapperOpen: {
  47.         borderRadius: 2,
  48.         boxShadow:
  49.             '0 4px 8px 0 rgba(76,85,104,0.16), 0 1px 2px 0 rgba(76,85,104,0.24)',
  50.     },
  51.     inputWrapper: {
  52.         position: 'relative',
  53.         display: 'flex',
  54.         alignItems: 'center',
  55.         justifyContent: 'flex-start',
  56.         padding: '7px 5px 7px 36px',
  57.         backgroundImage: `url(${Search})`,
  58.         backgroundRepeat: 'no-repeat',
  59.         backgroundPosition: '8px center',
  60.         borderBottom: '1px solid #E8EBF4',
  61.     },
  62.     input: {
  63.         display: 'block',
  64.         flex: '1 1 100%',
  65.         height: 24,
  66.         border: 'none',
  67.         color: '#818BA3',
  68.         fontFamily: 'inherit',
  69.         fontSize: 14,
  70.         lineHeight: '24px',
  71.     },
  72.     list: {
  73.         position: 'absolute',
  74.         top: '100%',
  75.         width: '100%',
  76.         zIndex: theme.zIndex.modal + 10,
  77.         padding: '18px 11px',
  78.         backgroundColor: '#ffffff',
  79.         boxShadow:
  80.             '0 4px 8px 0 rgba(76,85,104,0.16), 0 1px 2px 0 rgba(76,85,104,0.24)',
  81.         overflowX: 'auto',
  82.         '& > div': {
  83.             height: 360,
  84.         },
  85.     },
  86.     closeMenu: {
  87.         width: 24,
  88.         height: 24,
  89.         flex: '0 0 24px',
  90.         backgroundImage: `url(${ArrowDown})`,
  91.         backgroundRepeat: 'no-repeat',
  92.         backgroundPosition: 'center',
  93.         transform: 'rotate(180deg)',
  94.         cursor: 'pointer',
  95.     },
  96.     listItem: {
  97.         marginBottom: 18,
  98.         '& > div': {
  99.             padding: 2,
  100.         },
  101.         '&:last-child': {
  102.             marginBottom: 0,
  103.         },
  104.     },
  105.     highlighted: {
  106.         '& > div': {
  107.             outline: '1px dotted #106BFE',
  108.             outlineOffset: -1,
  109.         },
  110.     },
  111. });
  112.  
  113. const Closed = ({ openMenu, classes, additionalActionOnOpen, children }) => {
  114.     const action = e => {
  115.         if (typeof additionalActionOnOpen === 'function') additionalActionOnOpen();
  116.         openMenu(e);
  117.     };
  118.  
  119.     return (
  120.         <div
  121.             className={classes.content}
  122.             onClick={action}
  123.             role='button'
  124.             tabIndex={0}
  125.             onKeyPress={e => {
  126.                 if (e.key === 'Enter') {
  127.                     action(e);
  128.                 }
  129.             }}
  130.         >
  131.             <Typography>{children}</Typography>
  132.             <img
  133.                 className={classes.contentArrow}
  134.                 src={ArrowDown}
  135.                 role='presentational'
  136.             />
  137.         </div>
  138.     );
  139. };
  140.  
  141. /**
  142.  * @type React.FC<{
  143.  *   label?: string
  144.  *   classes: { [K in keyof ReturnType<typeof styles>]: string }
  145.  *   items: Array<unknown>
  146.  *   value: unknown
  147.  *   onChange: (selectedItem: any) => void
  148.  *   itemToString: (item: unknown) => string
  149.  *   unique: string
  150.  * }>
  151.  */
  152. const Dropdown = ({
  153.     label,
  154.     classes,
  155.     items,
  156.     onChange,
  157.     itemToString,
  158.     unique,
  159.     value,
  160. }) => {
  161.     const [inputValue, setInputValue] = React.useState('');
  162.  
  163.     return (
  164.         <Downshift
  165.             inputValue={inputValue}
  166.             onInputValueChange={s => setInputValue(s)}
  167.             onChange={onChange}
  168.             selectedItem={value}
  169.             itemToString={itemToString}
  170.         >
  171.             {({
  172.                 getInputProps,
  173.                 getItemProps,
  174.                 getLabelProps,
  175.                 getMenuProps,
  176.                 getToggleButtonProps,
  177.                 isOpen,
  178.                 openMenu,
  179.                 closeMenu,
  180.                 inputValue,
  181.                 highlightedIndex,
  182.                 selectedItem,
  183.             }) => {
  184.                 const newItems = items.filter(
  185.                     item =>
  186.                         !inputValue ||
  187.                         String(itemToString(item))
  188.                             .toLowerCase()
  189.                             .includes(inputValue.toLowerCase()),
  190.                 );
  191.  
  192.                 const rowRenderer = ({ index, key, style }) => {
  193.                     const item = newItems[index];
  194.                     if (item) {
  195.                         return (
  196.                             <li
  197.                                 {...getItemProps({
  198.                                     key: key,
  199.                                     index,
  200.                                     style,
  201.                                     item,
  202.                                     className: cx(
  203.                                         classes.listItem,
  204.                                         highlightedIndex === index && classes.highlighted,
  205.                                     ),
  206.                                 })}
  207.                             >
  208.                                 <RadioListElement
  209.                                     value={null}
  210.                                     onSelect={() => {}}
  211.                                     selected={selectedItem[unique] === item[unique]}
  212.                                     label={itemToString(item)}
  213.                                 />
  214.                             </li>
  215.                         );
  216.                     }
  217.  
  218.                     return null;
  219.                 };
  220.  
  221.                 return (
  222.                     <div>
  223.                         {label && (
  224.                             <Typography
  225.                                 className={classes.label}
  226.                                 contrast='semiContrast'
  227.                                 variant='label'
  228.                                 {...getLabelProps()}
  229.                             >
  230.                                 {label}
  231.                             </Typography>
  232.                         )}
  233.                         <div className={cx(classes.wrapper, isOpen && classes.wrapperOpen)}>
  234.                             {isOpen ? (
  235.                                 <div className={classes.inputWrapper}>
  236.                                     <input
  237.                                         {...getInputProps({})}
  238.                                         autoFocus
  239.                                         className={classes.input}
  240.                                         placeholder='Search'
  241.                                     />
  242.                                     <div
  243.                                         {...getToggleButtonProps({
  244.                                             disabled: !isOpen,
  245.                                         })}
  246.                                         className={classes.closeMenu}
  247.                                     />
  248.                                 </div>
  249.                             ) : (
  250.                                 <Closed
  251.                                     openMenu={openMenu}
  252.                                     additionalActionOnOpen={() => setInputValue('')}
  253.                                     classes={classes}
  254.                                 >
  255.                                     {itemToString(selectedItem) || 'Select'}
  256.                                 </Closed>
  257.                             )}
  258.                             {isOpen && (
  259.                                 <ul {...getMenuProps()} className={classes.list}>
  260.                                     <div>
  261.                                         <AutoSizer disableHeight>
  262.                                             {({ width }) => (
  263.                                                 <List
  264.                                                     width={width}
  265.                                                     height={360}
  266.                                                     rowCount={items.length}
  267.                                                     rowHeight={42}
  268.                                                     rowRenderer={rowRenderer}
  269.                                                 />
  270.                                             )}
  271.                                         </AutoSizer>
  272.                                     </div>
  273.                                 </ul>
  274.                             )}
  275.                         </div>
  276.                     </div>
  277.                 );
  278.             }}
  279.         </Downshift>
  280.     );
  281. };
  282.  
  283. export default jss(styles)(Dropdown);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement