import React, { useRef, useCallback, ChangeEvent } from 'react';
import Input from '../input';
import Checkbox from '../checkbox';
import useReducer from 'utils/useReducerHelper';
import useRotate from 'styles/animation/useRotate';
import { animated } from 'react-spring';
import { ClearFilter, CollapseTransition, SelectedItemTransition } from './helpers';
import * as S from './style';
import { ISelect, ISelectInput } from './types';
import { useClickOutSide } from 'utils/hooks';
const Down = animated(S.DownIcon);
export const Select = function({
options,
onChange,
value,
width,
filter = false,
checkbox = false,
checkAllLabel = '全部帐号',
multiple,
disabled,
placeholder,
defaultValue,
noFocus,
...rest
}: ISelect) {
if (value && !Array.isArray(value) && multiple) {
throw new Error('多选模式的Select指定的value必须是数组');
}
const [state, dispatch] = useReducer({
isOpen: false,
checkAll: false,
setDefault: false,
selected: '',
multiSelected: [],
selectedIndex: -1,
filterInput: '',
});
let controlledValue = false;
const onFilterFocusBlur = ClearFilter(filter, dispatch);
if ('value' in arguments[0]) {
controlledValue = true;
if (!multiple) {
const find = options.findIndex(o => o.value === value);
if (~find && state.selected !== options[find].label) {
dispatch({ filterInput: options[find].label, selected: options[find].label, selectedIndex: find });
}
} else if (state.multiSelected.join() !== (value as any[]).join()) {
dispatch({ multiSelected: value });
}
}
if (!state.setDefault && 'defaultValue' in arguments[0]) {
if (!multiple) {
const find = options.findIndex(o => o.value === defaultValue);
if (~find && state.selected !== options[find].label) {
dispatch({ selected: options[find].label, setDefault: true, selectedIndex: find });
}
} else {
dispatch({ multiSelected: defaultValue, setDefault: true });
}
}
const multiRef: any = useRef();
const inputRef: any = useRef();
const onClick = useCallback(() => {
!disabled && dispatch({ isOpen: multiple || !state.isOpen });
setTimeout(() => {
if (multiple && multiRef.current && multiRef.current.input) {
multiRef.current.input.focus();
}
}, 0);
}, [state.isOpen, disabled, multiple]);
const rotate = useRotate(state.isOpen);
const ref = useRef() as any;
let filteredOptions: any = filter ? options.filter(o => o.label.includes(state.filterInput)) : options;
if (filteredOptions.length === 0 && filter) {
filteredOptions = (
无匹配结果
);
}
useClickOutSide(ref, () => {
dispatch({ isOpen: false, filterInput: '' });
});
const height =
Array.isArray(filteredOptions) && filteredOptions.length
? (filteredOptions.length + (checkbox ? 1 : 0)) * 36 + 12
: 'auto';
const collapse = CollapseTransition(state.isOpen + '_' + height, height);
const multiSelectedItems = SelectedItemTransition(state.multiSelected);
return (
{
const { key } = e;
if (key === 'Escape') {
dispatch({ isOpen: false });
if (inputRef.current && inputRef.current.input) {
inputRef.current.input.blur();
}
e.preventDefault();
e.stopPropagation();
}
if (multiple && key === 'Backspace' && !state.filterInput && state.multiSelected.length) {
const multiSelected = [...state.multiSelected];
multiSelected.pop();
dispatch({ multiSelected });
onChange && onChange(multiSelected);
setTimeout(() => {
if (inputRef.current && inputRef.current.input) {
inputRef.current.input.focus();
}
}, 0);
}
}}
>
{multiple && state.multiSelected.length ? (
<>
{multiSelectedItems.map(({ item: value, props }: any, index) => (
{
e.preventDefault();
}}
>
{(options.find((o: any) => o.value === value) as any).label}
{
const multiSelected = JSON.parse(JSON.stringify(state.multiSelected));
multiSelected.splice(index, 1);
dispatch({ multiSelected, filterInput: '' });
}}
/>
))}
) => {
dispatch({ filterInput: value });
}}
onFocus={onFilterFocusBlur}
/>
{
dispatch({ isOpen: false, multiSelected: [], checkAll: false });
onChange && onChange([]);
e.stopPropagation();
}}
/>
>
) : (
) => {
dispatch({ filterInput: value });
}}
onFocus={onFilterFocusBlur}
/>
)}
{!(multiple && state.multiSelected.length) && (
)}
{collapse.map(({ item, key, props }) => {
const show = JSON.parse(item.split('_')[0]);
return (
show && (
{checkbox && (
0 &&
state.multiSelected.length < filteredOptions.length
}
onChange={(e: boolean) => {
if (e && Array.isArray(filteredOptions)) {
dispatch({
multiSelected: filteredOptions.map(e => e.value),
isOpen: false,
checkAll: true,
});
onChange && onChange(filteredOptions.map(e => e.value));
} else {
dispatch({ multiSelected: [], checkAll: false });
onChange && onChange([]);
}
}}
>
{checkAllLabel}
)}
{Array.isArray(filteredOptions)
? filteredOptions.map((o, index) => (
{
const payload: any = {
multiSelected: JSON.parse(JSON.stringify(state.multiSelected)),
};
if (multiple) {
const mIndex = state.multiSelected.indexOf(o.value);
if (~mIndex) {
payload.multiSelected.splice(mIndex, 1);
} else {
payload.multiSelected.push(o.value);
}
payload.filterInput = '';
onChange && onChange(payload.multiSelected);
} else {
payload.isOpen = false;
payload.selected = o.label;
payload.selectedIndex = index;
onChange && onChange(o.value);
}
if (!controlledValue) {
dispatch(payload);
}
!controlledValue &&
filter &&
!multiple &&
dispatch({ filterInput: o.label });
}}
>
{multiple && !checkbox && state.multiSelected.includes(o.value) && (
)}
{checkbox ? (
{o.label}
) : (
o.label
)}
))
: filteredOptions}
)
);
})}
);
};
Select.defaultProps = {
width: 158,
};
Select.SelectInput = SelectInput;
export default Select;
export function SelectInput({ input, select }: ISelectInput) {
return (
);
}