diff --git a/README.md b/README.md
index 0b3b986e..41c3592b 100644
--- a/README.md
+++ b/README.md
@@ -143,10 +143,10 @@ React.render(, container);
callback when finishing cascader select |
- onSelect |
- Function(selectedOptions, done) |
+ loadData |
+ Function(selectedOptions) |
|
- callback when click any option, argument "done" use for async select |
+ callback when click any option, use for loading more options |
expandTrigger |
diff --git a/examples/dynamic-options.js b/examples/dynamic-options.js
index 24ff96ba..eea587f0 100644
--- a/examples/dynamic-options.js
+++ b/examples/dynamic-options.js
@@ -6,9 +6,11 @@ import ReactDOM from 'react-dom';
const addressOptions = [{
'label': '福建',
+ isLeaf: false,
'value': 'fj',
}, {
'label': '浙江',
+ isLeaf: false,
'value': 'zj',
}];
@@ -19,39 +21,35 @@ const Demo = React.createClass({
options: addressOptions,
};
},
- onSelect(selectedOptions, done) {
- const options = this.state.options;
- const targetOption = selectedOptions[selectedOptions.length - 1];
- if (selectedOptions.length === 1 && !targetOption.children) {
- targetOption.label += ' loading';
- // 动态加载下级数据
- setTimeout(() => {
- targetOption.label = targetOption.label.replace(' loading', '');
- targetOption.children = [{
- 'label': targetOption.label + '动态加载1',
- 'value': 'dynamic1',
- }, {
- 'label': targetOption.label + '动态加载2',
- 'value': 'dynamic2',
- }];
- this.setState({ options });
- done();
- }, 1000);
- return;
- }
- done();
- },
onChange(value, selectedOptions) {
this.setState({
inputValue: selectedOptions.map(o => o.label).join(', '),
});
},
+ loadData(selectedOptions) {
+ const options = this.state.options;
+ const targetOption = selectedOptions[selectedOptions.length - 1];
+ targetOption.label += ' loading';
+ // 动态加载下级数据
+ setTimeout(() => {
+ targetOption.label = targetOption.label.replace(' loading', '');
+ targetOption.children = [{
+ 'label': targetOption.label + '动态加载1',
+ 'value': 'dynamic1',
+ }, {
+ 'label': targetOption.label + '动态加载2',
+ 'value': 'dynamic2',
+ }];
+ this.setState({options});
+ }, 1000);
+ this.setState({options});
+ },
render() {
return (
-
+ loadData={this.loadData}
+ onChange={this.onChange}>
+
);
},
diff --git a/src/Cascader.jsx b/src/Cascader.jsx
index 2bbd800b..12f946f2 100644
--- a/src/Cascader.jsx
+++ b/src/Cascader.jsx
@@ -53,7 +53,7 @@ class Cascader extends React.Component {
handleChange(options) {
this.props.onChange(
options.map(o => o.value),
- options,
+ options
);
this.setPopupVisible(false);
}
diff --git a/src/Menus.jsx b/src/Menus.jsx
index 8262ce28..f8bdad67 100644
--- a/src/Menus.jsx
+++ b/src/Menus.jsx
@@ -11,6 +11,7 @@ class Menus extends React.Component {
value: initialValue,
};
}
+
componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
this.setState({
@@ -18,17 +19,70 @@ class Menus extends React.Component {
});
}
// sync activeValue with value when panel open
- if (nextProps.visible) {
+ if (nextProps.visible && !this.props.visible) {
this.setState({
activeValue: this.state.value,
});
}
}
+
+ onSelect(targetOption, menuIndex) {
+ if (!targetOption) {
+ return;
+ }
+ let activeValue = this.state.activeValue;
+ activeValue = activeValue.slice(0, menuIndex + 1);
+ activeValue[menuIndex] = targetOption.value;
+ const activeOptions = this.getActiveOptions(activeValue);
+ if (targetOption.isLeaf === false && !targetOption.children) {
+ if (this.props.loadData) {
+ this.setState({activeValue});
+ this.props.loadData(activeOptions);
+ }
+ } else if (!targetOption.children || !targetOption.children.length) {
+ this.props.onChange(activeOptions);
+ // finish select
+ // should set value to activeValue
+ this.setState({value: activeValue});
+ } else {
+ this.setState({activeValue});
+ }
+ }
+
+ getOption(option, menuIndex) {
+ const { prefixCls, expandTrigger } = this.props;
+ let menuItemCls = `${prefixCls}-menu-item`;
+ if (this.isActiveOption(option)) {
+ menuItemCls += ` ${prefixCls}-menu-item-active`;
+ }
+ const onSelect = this.onSelect.bind(this, option, menuIndex);
+ let expandProps = {
+ onClick: onSelect,
+ };
+ if (expandTrigger === 'hover' &&
+ option.children &&
+ option.children.length > 0) {
+ expandProps = {
+ onMouseEnter: onSelect,
+ };
+ menuItemCls += ` ${prefixCls}-menu-item-expand`;
+ }
+ return (
+
+ {option.label}
+
+ );
+ }
+
getActiveOptions(values) {
const activeValue = values || this.state.activeValue;
const options = this.props.options;
return arrayTreeFilter(options, (o, level) => o.value === activeValue[level]);
}
+
getShowOptions() {
const { options } = this.props;
const result = this.getActiveOptions()
@@ -37,72 +91,24 @@ class Menus extends React.Component {
result.unshift(options);
return result;
}
- handleSelect(targetOption, menuIndex) {
- if (!targetOption) {
- return;
- }
- let activeValue = this.state.activeValue;
- activeValue = activeValue.slice(0, menuIndex + 1);
- activeValue[menuIndex] = targetOption.value;
- const activeOptions = this.getActiveOptions(activeValue);
- const selectCallback = () => {
- if (!targetOption.children || targetOption.children.length === 0) {
- this.props.onChange(activeOptions);
- // finish select
- // should set value to activeValue
- this.setState({ value: activeValue });
- }
- this.forceUpdate();
- };
- // specify the last argument `done`:
- // onSelect(selectedOptions, done)
- // it means async select
- if (this.props.onSelect.length >= 2) {
- this.props.onSelect(activeOptions, selectCallback);
- } else {
- this.props.onSelect(activeOptions);
- selectCallback();
- }
- // set activeValue, waiting for async callback
- this.setState({ activeValue });
- }
+
isActiveOption(option) {
return this.state.activeValue.some(value => value === option.value);
}
+
render() {
- const { prefixCls, expandTrigger } = this.props;
+ const { prefixCls } = this.props;
return (
{this.getShowOptions().map((options, menuIndex) =>
-
- {options.map(option => {
- let menuItemCls = `${prefixCls}-menu-item`;
- if (this.isActiveOption(option)) {
- menuItemCls += ` ${prefixCls}-menu-item-active`;
- }
- const handleSelect = this.handleSelect.bind(this, option, menuIndex);
- let expandProps = {
- onClick: handleSelect,
- };
- if (expandTrigger === 'hover' &&
- option.children &&
- option.children.length > 0) {
- expandProps = {
- onMouseEnter: handleSelect,
- };
- menuItemCls += ` ${prefixCls}-menu-item-expand`;
- }
- return (
- -
- {option.label}
-
- );
- })}
-
- )}
+
+ {
+ options.map(option => {
+ return this.getOption(option, menuIndex);
+ })
+ }
+
+ )}
);
}
@@ -110,8 +116,10 @@ class Menus extends React.Component {
Menus.defaultProps = {
options: [],
- onChange() {},
- onSelect() {},
+ onChange() {
+ },
+ onSelect() {
+ },
prefixCls: 'rc-cascader-menus',
visible: false,
expandTrigger: 'click',
@@ -122,7 +130,7 @@ Menus.propTypes = {
prefixCls: React.PropTypes.string,
expandTrigger: React.PropTypes.string,
onChange: React.PropTypes.func,
- onSelect: React.PropTypes.func,
+ loadData: React.PropTypes.func,
visible: React.PropTypes.bool,
};
diff --git a/tests/index.spec.js b/tests/index.spec.js
index 0f299cae..f9d9e420 100644
--- a/tests/index.spec.js
+++ b/tests/index.spec.js
@@ -71,9 +71,8 @@ describe('Cascader', () => {
expect(selectedValue).not.to.be.ok();
Simulate.click(menu3Items[0]);
- expect(menu3Items[0].className).to.contain('rc-cascader-menu-item-active');
- expect(selectedValue.join(',')).to.be('fj,fuzhou,mawei');
expect(instance.state.popupVisible).not.to.be.ok();
+ expect(selectedValue.join(',')).to.be('fj,fuzhou,mawei');
done();
});
@@ -126,9 +125,8 @@ describe('Cascader', () => {
expect(selectedValue).not.to.be.ok();
Simulate.click(menu3Items[0]);
- expect(menu3Items[0].className).to.contain('rc-cascader-menu-item-active');
- expect(selectedValue.join(',')).to.be('fj,fuzhou,mawei');
expect(instance.state.popupVisible).not.to.be.ok();
+ expect(selectedValue.join(',')).to.be('fj,fuzhou,mawei');
done();
});