Skip to content

Commit

Permalink
upgrade rc-cascader to 1.13.0 for support filedNames (ant-design#10365)
Browse files Browse the repository at this point in the history
  • Loading branch information
愚指导 authored May 4, 2018
1 parent f4b8a58 commit 0f77949
Showing 6 changed files with 154 additions and 27 deletions.
22 changes: 22 additions & 0 deletions components/cascader/__tests__/__snapshots__/demo.test.js.snap
Original file line number Diff line number Diff line change
@@ -143,6 +143,28 @@ exports[`renders ./components/cascader/demo/disabled-option.md correctly 1`] = `
</span>
`;

exports[`renders ./components/cascader/demo/fileds-name.md correctly 1`] = `
<span
class="ant-cascader-picker"
tabindex="0"
>
<span
class="ant-cascader-picker-label"
/>
<input
autocomplete="off"
class="ant-input ant-cascader-input "
placeholder="Please select"
readonly=""
type="text"
value=""
/>
<i
class="anticon anticon-down ant-cascader-picker-arrow"
/>
</span>
`;

exports[`renders ./components/cascader/demo/hover.md correctly 1`] = `
<span
class="ant-cascader-picker"
50 changes: 50 additions & 0 deletions components/cascader/demo/fileds-name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
order: 10
title:
zh-CN: 自定义字段名
en-US: Custom Filed Names
---

## zh-CN

自定义字段名。

## en-US

Custom filed names.

````jsx
import { Cascader } from 'antd';

const options = [{
code: 'zhejiang',
name: 'Zhejiang',
items: [{
code: 'hangzhou',
name: 'Hangzhou',
items: [{
code: 'xihu',
name: 'West Lake',
}],
}],
}, {
code: 'jiangsu',
name: 'Jiangsu',
items: [{
code: 'nanjing',
name: 'Nanjing',
items: [{
code: 'zhonghuamen',
name: 'Zhong Hua Men',
}],
}],
}];

function onChange(value) {
console.log(value);
}

ReactDOM.render(
<Cascader filedNames={{ label: 'name', value: 'code', children: 'items' }} options={options} onChange={onChange} placeholder="Please select" />
, mountNode);
````
1 change: 1 addition & 0 deletions components/cascader/index.en-US.md
Original file line number Diff line number Diff line change
@@ -42,6 +42,7 @@ Cascade selection box.
| value | selected value | string\[] | - |
| onChange | callback when finishing cascader select | `(value, selectedOptions) => void` | - |
| onPopupVisibleChange | callback when popup shown or hidden | `(value) => void` | - |
| filedNames | custom field name for label and value and children | object | `{ label: 'label', value: 'value', children: 'children' }` |

Fields in `showSearch`:

105 changes: 79 additions & 26 deletions components/cascader/index.tsx
Original file line number Diff line number Diff line change
@@ -8,19 +8,37 @@ import Input from '../input';
import Icon from '../icon';

export interface CascaderOptionType {
value: string;
label: React.ReactNode;
value?: string;
label?: React.ReactNode;
disabled?: boolean;
children?: Array<CascaderOptionType>;
__IS_FILTERED_OPTION?: boolean;
[key: string]: any;
}

export interface FiledNamesType {
value?: string;
label?: string;
children?: string;
}

export interface FilledFiledNamesType {
value: string;
label: string;
children: string;
}

export type CascaderExpandTrigger = 'click' | 'hover';

export interface ShowSearchType {
filter?: (inputValue: string, path: CascaderOptionType[]) => boolean;
render?: (inputValue: string, path: CascaderOptionType[], prefixCls: string | undefined) => React.ReactNode;
sort?: (a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string) => number;
filter?: (inputValue: string, path: CascaderOptionType[], names: FilledFiledNamesType) => boolean;
render?: (
inputValue: string,
path: CascaderOptionType[],
prefixCls: string | undefined,
names: FilledFiledNamesType,
) => React.ReactNode;
sort?: (a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string, names: FilledFiledNamesType) => number;
matchInputWidth?: boolean;
}

@@ -64,6 +82,7 @@ export interface CascaderProps {
inputPrefixCls?: string;
getPopupContainer?: (triggerNode?: HTMLElement) => HTMLElement;
popupVisible?: boolean;
filedNames?: FiledNamesType;
}

export interface CascaderState {
@@ -82,26 +101,41 @@ function highlightKeyword(str: string, keyword: string, prefixCls: string | unde
]);
}

function defaultFilterOption(inputValue: string, path: CascaderOptionType[]) {
return path.some(option => (option.label as string).indexOf(inputValue) > -1);
function defaultFilterOption(inputValue: string, path: CascaderOptionType[], names: FilledFiledNamesType) {
return path.some(option => (option[names.label] as string).indexOf(inputValue) > -1);
}

function defaultRenderFilteredOption(inputValue: string, path: CascaderOptionType[], prefixCls: string | undefined) {
return path.map(({ label }, index) => {
function defaultRenderFilteredOption(
inputValue: string,
path: CascaderOptionType[],
prefixCls: string | undefined,
names: FilledFiledNamesType,
) {
return path.map((option, index) => {
const label = option[names.label];
const node = (label as string).indexOf(inputValue) > -1 ?
highlightKeyword(label as string, inputValue, prefixCls) : label;
return index === 0 ? node : [' / ', node];
});
}

function defaultSortFilteredOption(a: any[], b: any[], inputValue: string) {
function defaultSortFilteredOption(a: any[], b: any[], inputValue: string, names: FilledFiledNamesType) {
function callback(elem: CascaderOptionType) {
return (elem.label as string).indexOf(inputValue) > -1;
return (elem[names.label] as string).indexOf(inputValue) > -1;
}

return a.findIndex(callback) - b.findIndex(callback);
}

function getFilledFieldNames(filedNames: FiledNamesType = {}) {
const names: FilledFiledNamesType = {
children: filedNames.children || 'children',
label: filedNames.label || 'label',
value: filedNames.value || 'value',
};
return names;
}

const defaultDisplayRender = (label: string[]) => label.join(' / ');

export default class Cascader extends React.Component<CascaderProps, CascaderState> {
@@ -128,7 +162,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
inputValue: '',
inputFocused: false,
popupVisible: props.popupVisible,
flattenOptions: props.showSearch && this.flattenTree(props.options, props.changeOnSelect),
flattenOptions: props.showSearch && this.flattenTree(props.options, props.changeOnSelect, props.filedNames),
};
}

@@ -140,7 +174,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
this.setState({ popupVisible: nextProps.popupVisible });
}
if (nextProps.showSearch && this.props.options !== nextProps.options) {
this.setState({ flattenOptions: this.flattenTree(nextProps.options, nextProps.changeOnSelect) });
this.setState({
flattenOptions: this.flattenTree(nextProps.options, nextProps.changeOnSelect, nextProps.filedNames),
});
}
}

@@ -207,13 +243,14 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
}

getLabel() {
const { options, displayRender = defaultDisplayRender as Function } = this.props;
const { options, displayRender = defaultDisplayRender as Function, filedNames } = this.props;
const names = getFilledFieldNames(filedNames);
const value = this.state.value;
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
const selectedOptions: CascaderOptionType[] = arrayTreeFilter(options,
(o: CascaderOptionType, level: number) => o.value === unwrappedValue[level],
(o: CascaderOptionType, level: number) => o[names.value] === unwrappedValue[level],
);
const label = selectedOptions.map(o => o.label);
const label = selectedOptions.map(o => o[names.label]);
return displayRender(label, selectedOptions);
}

@@ -228,43 +265,58 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
}
}

flattenTree(options: CascaderOptionType[], changeOnSelect: boolean | undefined, ancestor: CascaderOptionType[] = []) {
flattenTree(
options: CascaderOptionType[],
changeOnSelect: boolean | undefined,
filedNames: FiledNamesType | undefined,
ancestor: CascaderOptionType[] = [],
) {
const names: FiledNamesType = getFilledFieldNames(filedNames);
let flattenOptions: any = [];
let childrenName: any = names.children;
options.forEach((option) => {
const path = ancestor.concat(option);
if (changeOnSelect || !option.children || !option.children.length) {
if (changeOnSelect || !option[childrenName] || !option[childrenName].length) {
flattenOptions.push(path);
}
if (option.children) {
flattenOptions = flattenOptions.concat(this.flattenTree(option.children, changeOnSelect, path));
if (option[childrenName]) {
flattenOptions = flattenOptions.concat(
this.flattenTree(
option[childrenName],
changeOnSelect,
filedNames,
path,
),
);
}
});
return flattenOptions;
}

generateFilteredOptions(prefixCls: string | undefined) {
const { showSearch, notFoundContent } = this.props;
const { showSearch, notFoundContent, filedNames } = this.props;
const names: FilledFiledNamesType = getFilledFieldNames(filedNames);
const {
filter = defaultFilterOption,
render = defaultRenderFilteredOption,
sort = defaultSortFilteredOption,
} = showSearch as ShowSearchType;
const { flattenOptions, inputValue } = this.state;
const filtered = flattenOptions.filter((path) => filter(this.state.inputValue, path))
.sort((a, b) => sort(a, b, inputValue));
const filtered = flattenOptions.filter((path) => filter(this.state.inputValue, path, names))
.sort((a, b) => sort(a, b, inputValue, names));

if (filtered.length > 0) {
return filtered.map((path: any) => {
return {
__IS_FILTERED_OPTION: true,
path,
label: render(inputValue, path, prefixCls),
value: path.map((o: CascaderOptionType) => o.value),
[names.label]: render(inputValue, path, prefixCls, names),
[names.value]: path.map((o: CascaderOptionType) => o[names.value]),
disabled: path.some((o: CascaderOptionType) => o.disabled),
} as CascaderOptionType;
});
}
return [{ label: notFoundContent, value: 'ANT_CASCADER_NOT_FOUND', disabled: true }];
return [{ [names.label]: notFoundContent, [names.value]: 'ANT_CASCADER_NOT_FOUND', disabled: true }];
}

focus() {
@@ -328,6 +380,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
'renderFilteredOption',
'sortFilteredOption',
'notFoundContent',
'filedNames',
]);

let options = props.options;
1 change: 1 addition & 0 deletions components/cascader/index.zh-CN.md
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ subtitle: 级联选择
| value | 指定选中项 | string\[] | - |
| onChange | 选择完成后的回调 | `(value, selectedOptions) => void` | - |
| onPopupVisibleChange | 显示/隐藏浮层的回调 | `(value) => void` | - |
| filedNames | 自定义 options 中 label name children 的字段 | object | `{ label: 'label', value: 'value', children: 'children' }` |

`showSearch` 为对象时,其中的字段:

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@
"prop-types": "^15.5.7",
"rc-animate": "^2.4.1",
"rc-calendar": "~9.6.0",
"rc-cascader": "~0.12.0",
"rc-cascader": "~0.13.0",
"rc-checkbox": "~2.1.5",
"rc-collapse": "~1.8.0",
"rc-dialog": "~7.1.0",

0 comments on commit 0f77949

Please sign in to comment.