Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: clean up SelectMenu #1049

Merged
merged 3 commits into from
Apr 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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