Skip to content

Commit

Permalink
refactor: clean up SelectMenu (youzan#1049)
Browse files Browse the repository at this point in the history
* refactor: clean up SelectMenu

* fix: use React.createRef

* fix: remove componentWillReceiveProps
  • Loading branch information
cpylua authored Apr 2, 2019
1 parent 10ee1ac commit a30d5ad
Show file tree
Hide file tree
Showing 5 changed files with 343 additions and 322 deletions.
4 changes: 2 additions & 2 deletions .lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"*.js": ["yarn format", "eslint", "git add"],
"packages/zent/src/*.{ts,tsx}": ["yarn format", "tslint --format stylish --project packages/zent/tsconfig.json", "git add"],
"packages/zent/scripts/cruiser/*.{ts,tsx}": ["yarn format", "tslint --format stylish --project packages/zent/scripts/cruiser/tsconfig.json", "git add"],
"packages/zent/src/**/*.{ts,tsx}": ["yarn format", "tslint --format stylish --project packages/zent/tsconfig.json", "git add"],
"packages/zent/scripts/cruiser/**/*.{ts,tsx}": ["yarn format", "tslint --format stylish --project packages/zent/scripts/cruiser/tsconfig.json", "git add"],
"*.scss": ["yarn format", "sass-lint -vq", "git add"]
}
177 changes: 79 additions & 98 deletions packages/zent/src/auto-complete/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@
* AutoComplete
*/
import * as React from 'react';
import { Component, Children } from 'react';
import { Component } from 'react';
import cn from 'classnames';
import * as keycode from 'keycode';
import isUndefined from 'lodash-es/isUndefined';

import memoize from '../utils/memorize-one';
import Input from '../input';
import Popover from '../popover';
import SelectMenu from '../select-menu';
import SelectMenu, { ISelectMenuItem } from '../select-menu';
import { Omit } from 'utility-types';

const { caselessMatchFilterOption } = SelectMenu;

export interface IAutoCompleteMenuObjectItem {
export interface IAutoCompleteMenuObjectItem
extends Omit<ISelectMenuItem, 'items'> {
value: string;
content?: React.ReactNode;
isGroup?: boolean;
isDivider?: boolean;
valueField?: string;
textField?: string;
contentField?: string;
Expand All @@ -39,24 +40,24 @@ export interface IAutoCompleteProps {
onSearch?: (searchText: string) => void;
filterOption?: (
searchText: string,
menuItem: IAutoCompleteMenuItem
menuItem: IAutoCompleteMenuObjectItem
) => boolean;
valueFromOption?: boolean;
className?: string;
popupClassName?: string;
width?: number | string;
valueFromOptions?: boolean;
valueField?: string;
contentField?: string;
textField?: string;
valueField: string;
contentField: string;
textField: string;
disabled?: boolean;
children?: any;
}

export interface IAutoCompleteState {
open: boolean;
value: unknown;
searchText: string;
items: IAutoCompleteMenuObjectItem[];
}

export class AutoComplete extends Component<
Expand All @@ -67,10 +68,13 @@ export class AutoComplete extends Component<
prefix: 'zent',
filterOption: caselessMatchFilterOption,
valueFromOptions: false,
valueField: 'value',
contentField: 'content',
textField: 'text',
};

blurHandlerPrevented = false;
refMenuItemList: SelectMenu | null = null;
refMenuItemList = React.createRef<SelectMenu>();

constructor(props) {
super(props);
Expand All @@ -79,25 +83,19 @@ export class AutoComplete extends Component<
open: false,
value: props.initialValue || props.value || null,
searchText: '', // combo specific

items: this.getTransformedItemConfigs(props),
};
}

componentWillReceiveProps(nextProps) {
this.setControlled(nextProps, 'value');

// items transform
const { items, data } = nextProps;
if (
this.props.data !== data ||
this.props.items !== items ||
!!nextProps.children
) {
this.setState({
items: this.getTransformedItemConfigs(nextProps),
});
}
static getDerivedStateFromProps(
props: IAutoCompleteProps,
state: IAutoCompleteState
) {
const { value } = props;
return isUndefined(value) || state.value === value
? null
: {
value: props.value,
};
}

onSearchTextChange = e => {
Expand Down Expand Up @@ -173,7 +171,7 @@ export class AutoComplete extends Component<

getSelectedValueFromSearchText = searchText => {
let selectedValue = null;
(this.state.items || []).some(item => {
this.getTransformedItemConfigsFromProps().some(item => {
if (
item.searchContent === searchText ||
item.content === searchText ||
Expand All @@ -200,77 +198,53 @@ export class AutoComplete extends Component<
};

/** Helpers */

/**
* Set the controlled props if needed.
* @param nextProps
* @param k
*/
setControlled = (nextProps, k) => {
if (nextProps[k] !== undefined) {
this.setState({
[k]: nextProps[k],
} as any);
}
};

getPropsItems(props) {
return props.items || props.data || [];
}

/**
* Convert passed in data to item config list.
*
* @param props
* @returns {*}
* @private
*/
getTransformedItemConfigs = (props = this.props) => {
let transformedItems = this.getPropsItems(props);

// handle items
transformedItems = transformedItems.map(item => {
if (typeof item === 'string' || typeof item === 'number') {
return {
value: item,
content: item,
};
}

if (typeof item === 'object') {
const {
valueField = 'value',
textField = 'text',
contentField = 'content',
} = props;

return {
...item,
value: item[valueField],
content: item[contentField] || item[textField] || item[valueField],
};
/* eslint-disable no-else-return */
}

throw new Error('AutoComplete unresolvable option!');
});
getTransformedItemConfigs = memoize(
(
valueField: string,
textField: string,
contentField: string,
items?: IAutoCompleteMenuItem[],
data?: IAutoCompleteMenuItem[]
): IAutoCompleteMenuObjectItem[] => {
return (items || data || []).map(item => {
if (typeof item === 'string' || typeof item === 'number') {
return {
value: item,
content: item,
};
}

// handle option children
if (props.children) {
transformedItems = transformedItems.concat(
Children.map(props.children, (item: any) => {
let value = item.props.value;
value = typeof value === 'undefined' ? item : value;
if (typeof item === 'object') {
return {
...item.props,
value,
content: item.props.children,
...item,
value: item[valueField],
content: item[contentField] || item[textField] || item[valueField],
};
})
);
}

throw new Error('AutoComplete unresolvable option!');
});
}
return transformedItems;
};
);

getTransformedItemConfigsFromProps() {
const { items, data, textField, valueField, contentField } = this.props;

return this.getTransformedItemConfigs(
valueField,
textField,
contentField,
items,
data
);
}

/**
* Get the display text of selected value, since the value and content might be different, and content might be node.
Expand Down Expand Up @@ -349,23 +323,29 @@ export class AutoComplete extends Component<
* @param value
* @private
*/
getItemByValue = value => this.iterateItems(this.state.items, value);
getItemByValue = value =>
this.iterateItems(this.getTransformedItemConfigsFromProps(), value);

moveFocusIndexDown = () => {
if (this.refMenuItemList) {
return this.refMenuItemList.moveFocusIndexDown();
const menuList = this.refMenuItemList.current;
if (menuList) {
return menuList.moveFocusIndexDown();
}
};

moveFocusIndexUp = () => {
if (this.refMenuItemList) {
return this.refMenuItemList.moveFocusIndexUp();
const menuList = this.refMenuItemList.current;

if (menuList) {
return menuList.moveFocusIndexUp();
}
};

selectCurrentFocusIndex = e => {
if (this.refMenuItemList) {
return this.refMenuItemList.selectCurrentFocusIndex(e);
const menuList = this.refMenuItemList.current;

if (menuList) {
return menuList.selectCurrentFocusIndex(e);
}
};

Expand All @@ -377,7 +357,8 @@ export class AutoComplete extends Component<
popupClassName,
disabled,
} = this.props;
const { open, items, searchText } = this.state;
const { open, searchText } = this.state;
const items = this.getTransformedItemConfigsFromProps();

const prefixCls = 'zent-auto-complete';

Expand Down Expand Up @@ -409,7 +390,7 @@ export class AutoComplete extends Component<
</Popover.Trigger.Click>
<Popover.Content>
<SelectMenu
ref={el => (this.refMenuItemList = el)}
ref={this.refMenuItemList}
items={items}
value={this.state.value}
searchText={this.state.searchText}
Expand Down
Loading

0 comments on commit a30d5ad

Please sign in to comment.