Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import React from 'react';
- import Select, { Styles, Props, ValueType } from 'react-select';
- import type { OptionTypeBase } from 'react-select/src/types';
- import { ActionMeta } from 'react-select/src/types';
- type KnownKeys<T> = {
- [K in keyof T]: string extends K ? never : number extends K ? never : K
- } extends { [_ in keyof T]: infer U } ? U : never;
- /**
- * Тип опций по умолчанию
- */
- export interface DefaultOptionType<T = string> extends OptionTypeBase {
- label: string;
- value: T;
- }
- /**
- * Убираем null | undefined, если IsMulti = true (https://github.com/JedWatson/react-select/issues/3632)
- * @description + убираем OptionType<OptionType> -> OptionType[], потому что ReadonlyArray нигде не используется, зато создаёт кучу проблем
- */
- export type FixedValueType<OptionType extends OptionTypeBase, IsMulti extends boolean> = IsMulti extends true
- ? OptionType[]
- : OptionType | null | undefined;
- /**
- * Fix onChange, чтобы он возвращал массив, если IsMulti = true
- * @description Pick<Type, KnownKeys> нужен, чтобы выпилить [key: sting]: any из Type, без этого Omit<Props, 'onChange'> выпиливает ВСЕ ключи
- */
- export interface FixedProps<OptionType extends OptionTypeBase = DefaultOptionType, IsMulti extends boolean = false>
- extends Omit<Pick<Props<OptionType, IsMulti>, KnownKeys<Props<OptionType, IsMulti>>>, 'onChange'>,
- CustomProps {
- // [key: string]: any, // Костыль, т.к. пришлось юзать KnownKeys, потому что Omit с [key: sting]: any работает неправильно
- /**
- * Исправляем onChange, чтобы он больше не emit'ил null | undefined, если IsMulti
- * @param value
- * @param action
- */
- onChange?: (value: FixedValueType<OptionType, IsMulti>, action: ActionMeta<OptionType>) => void;
- }
- export enum MultiselectProportion {
- small = 'small',
- default = 'default',
- }
- /**
- * Пропы, которые не входят в оригинальный react-select
- */
- export interface CustomProps {
- proportion?: keyof typeof MultiselectProportion;
- testField?: boolean;
- error?: boolean;
- }
- /**
- * Все доступные пропы для Multiselect компонента
- */
- export type MultiselectProps<
- OptionType extends OptionTypeBase = DefaultOptionType,
- IsMulti extends boolean = false
- > = FixedProps<OptionType, IsMulti> & CustomProps;
- export const getDefaultCustomProps = (): CustomProps => ({
- testField: false,
- proportion: 'default',
- });
- /**
- * Default styles
- */
- export const getDefaultStyles = <
- OptionType extends OptionTypeBase = DefaultOptionType,
- IsMulti extends boolean = false
- >(): Styles<OptionType, IsMulti> => ({
- placeholder: (provided) => ({
- ...provided,
- whiteSpace: 'nowrap',
- textOverflow: 'ellipsis',
- overflow: 'hidden',
- maxWidth: '90%',
- }),
- control: (provided, state) => ({
- ...provided,
- // @ts-ignore // @NOTE: у react-select нельзя в Styles передать generic, отсюда props === any
- borderColor: state.selectProps.error ? 'red !important' : provided.borderColor,
- // @ts-ignore // @NOTE: у react-select нельзя в Styles передать generic, отсюда props === any
- boxShadow: (state.isFocused && state.selectProps.error) ? 'inset 0 0 0 1px red !important' : provided.boxShadow,
- }),
- });
- export const getDefaultOriginalProps = <
- OptionType extends OptionTypeBase = DefaultOptionType,
- IsMulti extends boolean = false
- >(
- originalProps: FixedProps<OptionType, IsMulti>,
- ): FixedProps<OptionType, IsMulti> => ({
- backspaceRemovesValue: false,
- styles: getDefaultStyles<OptionType, IsMulti>(),
- isClearable: true,
- hideSelectedOptions: false,
- closeMenuOnSelect: !originalProps.isMulti,
- classNamePrefix: 'react-select',
- noOptionsMessage: () => 'Ничего не найдено',
- placeholder: 'Выберите значение',
- });
- export const getDefaultProps = <
- OptionType extends OptionTypeBase = DefaultOptionType,
- IsMulti extends boolean = false
- >(): MultiselectProps<OptionType, IsMulti> => ({
- ...getDefaultOriginalProps<OptionType, IsMulti>({}),
- ...getDefaultCustomProps(),
- });
- const Multiselect = <OptionType extends OptionTypeBase = DefaultOptionType, IsMulti extends boolean = false>(
- { testField, proportion, error, ...props }: MultiselectProps<OptionType, IsMulti> = getDefaultProps<OptionType, IsMulti>(),
- ) => {
- const originalProps: FixedProps<OptionType, IsMulti> = {
- ...getDefaultOriginalProps<OptionType, IsMulti>(props),
- ...props,
- };
- const customProps: CustomProps = {
- ...getDefaultCustomProps(),
- testField,
- };
- if (customProps.testField) {
- console.log('Remove test field ', customProps.testField);
- }
- /**
- * Fix default nullable behaviour: https://github.com/JedWatson/react-select/issues/3632
- */
- const handleChange = React.useCallback(
- (value: ValueType<OptionType, IsMulti>, action: ActionMeta<OptionType>) => {
- if (props.onChange) {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- props.onChange(props.isMulti ? value || [] : value, action);
- }
- },
- [props],
- );
- return (
- <Select
- {...originalProps}
- onChange={handleChange}
- error={error}
- />
- );
- };
- export default Multiselect;
Add Comment
Please, Sign In to add comment