crutch12

Untitled

Mar 17th, 2021 (edited)
702
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import React from 'react';
  2. import Select, { Styles, Props, ValueType } from 'react-select';
  3. import type { OptionTypeBase } from 'react-select/src/types';
  4. import { ActionMeta } from 'react-select/src/types';
  5.  
  6. type KnownKeys<T> = {
  7.   [K in keyof T]: string extends K ? never : number extends K ? never : K
  8. } extends { [_ in keyof T]: infer U } ? U : never;
  9.  
  10. /**
  11.  * Тип опций по умолчанию
  12.  */
  13. export interface DefaultOptionType<T = string> extends OptionTypeBase {
  14.   label: string;
  15.   value: T;
  16. }
  17.  
  18. /**
  19.  * Убираем null | undefined, если IsMulti = true (https://github.com/JedWatson/react-select/issues/3632)
  20.  * @description + убираем OptionType<OptionType> -> OptionType[], потому что ReadonlyArray нигде не используется, зато создаёт кучу проблем
  21.  */
  22. export type FixedValueType<OptionType extends OptionTypeBase, IsMulti extends boolean> = IsMulti extends true
  23.   ? OptionType[]
  24.   : OptionType | null | undefined;
  25.  
  26. /**
  27.  * Fix onChange, чтобы он возвращал массив, если IsMulti = true
  28.  * @description Pick<Type, KnownKeys> нужен, чтобы выпилить [key: sting]: any из Type, без этого Omit<Props, 'onChange'> выпиливает ВСЕ ключи
  29.  */
  30. export interface FixedProps<OptionType extends OptionTypeBase = DefaultOptionType, IsMulti extends boolean = false>
  31.   extends Omit<Pick<Props<OptionType, IsMulti>, KnownKeys<Props<OptionType, IsMulti>>>, 'onChange'>,
  32.     CustomProps {
  33.   // [key: string]: any, // Костыль, т.к. пришлось юзать KnownKeys, потому что Omit с [key: sting]: any работает неправильно
  34.   /**
  35.    * Исправляем onChange, чтобы он больше не emit'ил null | undefined, если IsMulti
  36.    * @param value
  37.    * @param action
  38.    */
  39.   onChange?: (value: FixedValueType<OptionType, IsMulti>, action: ActionMeta<OptionType>) => void;
  40. }
  41.  
  42. export enum MultiselectProportion {
  43.   small = 'small',
  44.   default = 'default',
  45. }
  46.  
  47. /**
  48.  * Пропы, которые не входят в оригинальный react-select
  49.  */
  50. export interface CustomProps {
  51.   proportion?: keyof typeof MultiselectProportion;
  52.   testField?: boolean;
  53.   error?: boolean;
  54. }
  55.  
  56. /**
  57.  * Все доступные пропы для Multiselect компонента
  58.  */
  59. export type MultiselectProps<
  60.   OptionType extends OptionTypeBase = DefaultOptionType,
  61.   IsMulti extends boolean = false
  62. > = FixedProps<OptionType, IsMulti> & CustomProps;
  63.  
  64. export const getDefaultCustomProps = (): CustomProps => ({
  65.   testField: false,
  66.   proportion: 'default',
  67. });
  68.  
  69. /**
  70.  * Default styles
  71.  */
  72. export const getDefaultStyles = <
  73.   OptionType extends OptionTypeBase = DefaultOptionType,
  74.   IsMulti extends boolean = false
  75. >(): Styles<OptionType, IsMulti> => ({
  76.   placeholder: (provided) => ({
  77.     ...provided,
  78.     whiteSpace: 'nowrap',
  79.     textOverflow: 'ellipsis',
  80.     overflow: 'hidden',
  81.     maxWidth: '90%',
  82.   }),
  83.   control: (provided, state) => ({
  84.     ...provided,
  85.     // @ts-ignore // @NOTE: у react-select нельзя в Styles передать generic, отсюда props === any
  86.     borderColor: state.selectProps.error ? 'red !important' : provided.borderColor,
  87.     // @ts-ignore // @NOTE: у react-select нельзя в Styles передать generic, отсюда props === any
  88.     boxShadow: (state.isFocused && state.selectProps.error) ? 'inset 0 0 0 1px red !important' : provided.boxShadow,
  89.   }),
  90. });
  91.  
  92. export const getDefaultOriginalProps = <
  93.   OptionType extends OptionTypeBase = DefaultOptionType,
  94.   IsMulti extends boolean = false
  95. >(
  96.   originalProps: FixedProps<OptionType, IsMulti>,
  97. ): FixedProps<OptionType, IsMulti> => ({
  98.   backspaceRemovesValue: false,
  99.   styles: getDefaultStyles<OptionType, IsMulti>(),
  100.   isClearable: true,
  101.   hideSelectedOptions: false,
  102.   closeMenuOnSelect: !originalProps.isMulti,
  103.   classNamePrefix: 'react-select',
  104.   noOptionsMessage: () => 'Ничего не найдено',
  105.   placeholder: 'Выберите значение',
  106. });
  107.  
  108. export const getDefaultProps = <
  109.   OptionType extends OptionTypeBase = DefaultOptionType,
  110.   IsMulti extends boolean = false
  111. >(): MultiselectProps<OptionType, IsMulti> => ({
  112.   ...getDefaultOriginalProps<OptionType, IsMulti>({}),
  113.   ...getDefaultCustomProps(),
  114. });
  115.  
  116. const Multiselect = <OptionType extends OptionTypeBase = DefaultOptionType, IsMulti extends boolean = false>(
  117.   { testField, proportion, error, ...props }: MultiselectProps<OptionType, IsMulti> = getDefaultProps<OptionType, IsMulti>(),
  118. ) => {
  119.   const originalProps: FixedProps<OptionType, IsMulti> = {
  120.     ...getDefaultOriginalProps<OptionType, IsMulti>(props),
  121.     ...props,
  122.   };
  123.  
  124.   const customProps: CustomProps = {
  125.     ...getDefaultCustomProps(),
  126.     testField,
  127.   };
  128.  
  129.   if (customProps.testField) {
  130.     console.log('Remove test field ', customProps.testField);
  131.   }
  132.  
  133.   /**
  134.    * Fix default nullable behaviour: https://github.com/JedWatson/react-select/issues/3632
  135.    */
  136.   const handleChange = React.useCallback(
  137.     (value: ValueType<OptionType, IsMulti>, action: ActionMeta<OptionType>) => {
  138.       if (props.onChange) {
  139.         // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  140.         // @ts-ignore
  141.         props.onChange(props.isMulti ? value || [] : value, action);
  142.       }
  143.     },
  144.     [props],
  145.   );
  146.  
  147.   return (
  148.     <Select
  149.       {...originalProps}
  150.       onChange={handleChange}
  151.       error={error}
  152.     />
  153.   );
  154. };
  155.  
  156. export default Multiselect;
  157.  
Add Comment
Please, Sign In to add comment