add vc-cascader
parent
10764b705b
commit
57c6c9ca46
|
@ -62,13 +62,18 @@ export function cloneElement (n, nodeProps, deep) {
|
||||||
const node = cloneVNode(ele, deep)
|
const node = cloneVNode(ele, deep)
|
||||||
const { props = {}, key, on = {}, children } = nodeProps
|
const { props = {}, key, on = {}, children } = nodeProps
|
||||||
const data = node.data || {}
|
const data = node.data || {}
|
||||||
const { style = data.style,
|
const { style = {},
|
||||||
class: cls = data.class,
|
class: cls = {},
|
||||||
attrs = data.attrs,
|
attrs = {},
|
||||||
ref,
|
ref,
|
||||||
domProps = data.domProps,
|
domProps = {},
|
||||||
} = nodeProps
|
} = nodeProps
|
||||||
node.data = Object.assign({}, data, { style, attrs, class: cls, domProps })
|
node.data = Object.assign({}, data, {
|
||||||
|
style: { ...data.style, ...style },
|
||||||
|
attrs: { ...data.attrs, ...attrs },
|
||||||
|
class: { ...data.class, ...cls },
|
||||||
|
domProps: { ...data.domProps, ...domProps },
|
||||||
|
})
|
||||||
if (node.componentOptions) {
|
if (node.componentOptions) {
|
||||||
node.componentOptions.propsData = node.componentOptions.propsData || {}
|
node.componentOptions.propsData = node.componentOptions.propsData || {}
|
||||||
node.componentOptions.listeners = node.componentOptions.listeners || {}
|
node.componentOptions.listeners = node.componentOptions.listeners || {}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import React, { Component, cloneElement } from 'react';
|
import PropTypes from '../_util/vue-types'
|
||||||
import PropTypes from 'prop-types';
|
import Trigger from '../trigger'
|
||||||
import Trigger from 'rc-trigger';
|
import Menus from './Menus'
|
||||||
import Menus from './Menus';
|
import KeyCode from '../_util/KeyCode'
|
||||||
import KeyCode from 'rc-util/lib/KeyCode';
|
import arrayTreeFilter from 'array-tree-filter'
|
||||||
import arrayTreeFilter from 'array-tree-filter';
|
import shallowEqualArrays from 'shallow-equal/arrays'
|
||||||
import shallowEqualArrays from 'shallow-equal/arrays';
|
import { hasProp, getEvents } from '../_util/props-util'
|
||||||
|
import BaseMixin from '../_util/BaseMixin'
|
||||||
|
import { cloneElement } from '../_util/vnode'
|
||||||
|
|
||||||
const BUILT_IN_PLACEMENTS = {
|
const BUILT_IN_PLACEMENTS = {
|
||||||
bottomLeft: {
|
bottomLeft: {
|
||||||
|
@ -40,274 +42,278 @@ const BUILT_IN_PLACEMENTS = {
|
||||||
adjustY: 1,
|
adjustY: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
class Cascader extends Component {
|
export default {
|
||||||
constructor(props) {
|
props: {
|
||||||
super(props);
|
value: PropTypes.array,
|
||||||
let initialValue = [];
|
defaultValue: PropTypes.array,
|
||||||
if ('value' in props) {
|
options: PropTypes.array.def([]).isRequired,
|
||||||
initialValue = props.value || [];
|
// onChange: PropTypes.func,
|
||||||
} else if ('defaultValue' in props) {
|
// onPopupVisibleChange: PropTypes.func,
|
||||||
initialValue = props.defaultValue || [];
|
popupVisible: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool.def(false),
|
||||||
|
transitionName: PropTypes.string.def(''),
|
||||||
|
popupClassName: PropTypes.string.def(''),
|
||||||
|
popupStyle: PropTypes.object.def({}),
|
||||||
|
popupPlacement: PropTypes.string.def('bottomLeft'),
|
||||||
|
prefixCls: PropTypes.string.def('rc-cascader'),
|
||||||
|
dropdownMenuColumnStyle: PropTypes.object,
|
||||||
|
builtinPlacements: PropTypes.object.def(BUILT_IN_PLACEMENTS),
|
||||||
|
loadData: PropTypes.func,
|
||||||
|
changeOnSelect: PropTypes.bool,
|
||||||
|
// onKeyDown: PropTypes.func,
|
||||||
|
expandTrigger: PropTypes.string.def('click'),
|
||||||
|
},
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
data () {
|
||||||
|
let initialValue = []
|
||||||
|
const { value, defaultValue, popupVisible } = this
|
||||||
|
if (hasProp(this, 'value')) {
|
||||||
|
initialValue = value || []
|
||||||
|
} else if (hasProp(this, 'defaultValue')) {
|
||||||
|
initialValue = defaultValue || []
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
this.state = {
|
sPopupVisible: popupVisible,
|
||||||
popupVisible: props.popupVisible,
|
sActiveValue: initialValue,
|
||||||
activeValue: initialValue,
|
sValue: initialValue,
|
||||||
value: initialValue,
|
}
|
||||||
};
|
},
|
||||||
}
|
watch: {
|
||||||
componentWillReceiveProps(nextProps) {
|
value (val, oldValue) {
|
||||||
if ('value' in nextProps && !shallowEqualArrays(this.props.value, nextProps.value)) {
|
if (!shallowEqualArrays(val, oldValue)) {
|
||||||
const newValues = {
|
const newValues = {
|
||||||
value: nextProps.value || [],
|
value: oldValue || [],
|
||||||
activeValue: nextProps.value || [],
|
activeValue: oldValue || [],
|
||||||
};
|
}
|
||||||
// allow activeValue diff from value
|
// allow activeValue diff from value
|
||||||
// https://github.com/ant-design/ant-design/issues/2767
|
// https://github.com/ant-design/ant-design/issues/2767
|
||||||
if ('loadData' in nextProps) {
|
if (hasProp(this, 'loadData')) {
|
||||||
delete newValues.activeValue;
|
delete newValues.activeValue
|
||||||
|
}
|
||||||
|
this.setState(newValues)
|
||||||
}
|
}
|
||||||
this.setState(newValues);
|
},
|
||||||
}
|
popupVisible (val) {
|
||||||
if ('popupVisible' in nextProps) {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
popupVisible: nextProps.popupVisible,
|
sPopupVisible: val,
|
||||||
});
|
})
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
getPopupDOMNode() {
|
methods: {
|
||||||
return this.trigger.getPopupDomNode();
|
getPopupDOMNode () {
|
||||||
}
|
return this.$refs.trigger.getPopupDomNode()
|
||||||
getCurrentLevelOptions() {
|
},
|
||||||
const { options } = this.props;
|
getCurrentLevelOptions () {
|
||||||
const { activeValue = [] } = this.state;
|
const { options, sActiveValue = [] } = this
|
||||||
const result = arrayTreeFilter(options, (o, level) => o.value === activeValue[level]);
|
const result = arrayTreeFilter(options, (o, level) => o.value === sActiveValue[level])
|
||||||
if (result[result.length - 2]) {
|
if (result[result.length - 2]) {
|
||||||
return result[result.length - 2].children;
|
return result[result.length - 2].children
|
||||||
}
|
}
|
||||||
return [...options].filter(o => !o.disabled);
|
return [...options].filter(o => !o.disabled)
|
||||||
}
|
},
|
||||||
getActiveOptions(activeValue) {
|
getActiveOptions (activeValue) {
|
||||||
return arrayTreeFilter(this.props.options, (o, level) => o.value === activeValue[level]);
|
return arrayTreeFilter(this.options, (o, level) => o.value === activeValue[level])
|
||||||
}
|
},
|
||||||
setPopupVisible = (popupVisible) => {
|
setPopupVisible (popupVisible) {
|
||||||
if (!('popupVisible' in this.props)) {
|
if (!hasProp(this, 'popupVisible')) {
|
||||||
this.setState({ popupVisible });
|
this.setState({ sPopupVisible: popupVisible })
|
||||||
}
|
}
|
||||||
// sync activeValue with value when panel open
|
// sync activeValue with value when panel open
|
||||||
if (popupVisible && !this.state.visible) {
|
if (popupVisible && !this.sVisible) {
|
||||||
this.setState({
|
this.setState({
|
||||||
activeValue: this.state.value,
|
sActiveValue: this.sValue,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
this.props.onPopupVisibleChange(popupVisible);
|
this.__emit('popupVisibleChange', popupVisible)
|
||||||
}
|
},
|
||||||
handleChange = (options, setProps, e) => {
|
handleChange (options, setProps, e) {
|
||||||
if (e.type !== 'keydown' || e.keyCode === KeyCode.ENTER) {
|
if (e.type !== 'keydown' || e.keyCode === KeyCode.ENTER) {
|
||||||
this.props.onChange(options.map(o => o.value), options);
|
this.__emit('change', options.map(o => o.value), options)
|
||||||
this.setPopupVisible(setProps.visible);
|
this.setPopupVisible(setProps.visible)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
handlePopupVisibleChange = (popupVisible) => {
|
handlePopupVisibleChange (popupVisible) {
|
||||||
this.setPopupVisible(popupVisible);
|
this.setPopupVisible(popupVisible)
|
||||||
}
|
},
|
||||||
handleMenuSelect = (targetOption, menuIndex, e) => {
|
handleMenuSelect (targetOption, menuIndex, e) {
|
||||||
// Keep focused state for keyboard support
|
// Keep focused state for keyboard support
|
||||||
const triggerNode = this.trigger.getRootDomNode();
|
const triggerNode = this.$refs.trigger.getRootDomNode()
|
||||||
if (triggerNode && triggerNode.focus) {
|
if (triggerNode && triggerNode.focus) {
|
||||||
triggerNode.focus();
|
triggerNode.focus()
|
||||||
}
|
|
||||||
const { changeOnSelect, loadData, expandTrigger } = this.props;
|
|
||||||
if (!targetOption || targetOption.disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let { activeValue } = this.state;
|
|
||||||
activeValue = activeValue.slice(0, menuIndex + 1);
|
|
||||||
activeValue[menuIndex] = targetOption.value;
|
|
||||||
const activeOptions = this.getActiveOptions(activeValue);
|
|
||||||
if (targetOption.isLeaf === false && !targetOption.children && loadData) {
|
|
||||||
if (changeOnSelect) {
|
|
||||||
this.handleChange(activeOptions, { visible: true }, e);
|
|
||||||
}
|
}
|
||||||
this.setState({ activeValue });
|
const { changeOnSelect, loadData, expandTrigger } = this
|
||||||
loadData(activeOptions);
|
if (!targetOption || targetOption.disabled) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const newState = {};
|
let { sActiveValue } = this
|
||||||
if (!targetOption.children || !targetOption.children.length) {
|
sActiveValue = sActiveValue.slice(0, menuIndex + 1)
|
||||||
this.handleChange(activeOptions, { visible: false }, e);
|
sActiveValue[menuIndex] = targetOption.value
|
||||||
// set value to activeValue when select leaf option
|
const activeOptions = this.getActiveOptions(sActiveValue)
|
||||||
newState.value = activeValue;
|
if (targetOption.isLeaf === false && !targetOption.children && loadData) {
|
||||||
|
if (changeOnSelect) {
|
||||||
|
this.handleChange(activeOptions, { visible: true }, e)
|
||||||
|
}
|
||||||
|
this.setState({ sActiveValue })
|
||||||
|
loadData(activeOptions)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newState = {}
|
||||||
|
if (!targetOption.children || !targetOption.children.length) {
|
||||||
|
this.handleChange(activeOptions, { visible: false }, e)
|
||||||
|
// set value to activeValue when select leaf option
|
||||||
|
newState.sValue = sActiveValue
|
||||||
// add e.type judgement to prevent `onChange` being triggered by mouseEnter
|
// add e.type judgement to prevent `onChange` being triggered by mouseEnter
|
||||||
} else if (changeOnSelect && (e.type === 'click' || e.type === 'keydown')) {
|
} else if (changeOnSelect && (e.type === 'click' || e.type === 'keydown')) {
|
||||||
if (expandTrigger === 'hover') {
|
if (expandTrigger === 'hover') {
|
||||||
this.handleChange(activeOptions, { visible: false }, e);
|
this.handleChange(activeOptions, { visible: false }, e)
|
||||||
} else {
|
} else {
|
||||||
this.handleChange(activeOptions, { visible: true }, e);
|
this.handleChange(activeOptions, { visible: true }, e)
|
||||||
|
}
|
||||||
|
// set value to activeValue on every select
|
||||||
|
newState.sValue = sActiveValue
|
||||||
}
|
}
|
||||||
// set value to activeValue on every select
|
newState.sActiveValue = sActiveValue
|
||||||
newState.value = activeValue;
|
// not change the value by keyboard
|
||||||
}
|
if (hasProp(this, 'value') ||
|
||||||
newState.activeValue = activeValue;
|
|
||||||
// not change the value by keyboard
|
|
||||||
if ('value' in this.props ||
|
|
||||||
(e.type === 'keydown' && e.keyCode !== KeyCode.ENTER)) {
|
(e.type === 'keydown' && e.keyCode !== KeyCode.ENTER)) {
|
||||||
delete newState.value;
|
delete newState.sValue
|
||||||
}
|
}
|
||||||
this.setState(newState);
|
this.setState(newState)
|
||||||
}
|
},
|
||||||
handleKeyDown = (e) => {
|
handleKeyDown (e) {
|
||||||
const { children } = this.props;
|
const { $slots } = this
|
||||||
// https://github.com/ant-design/ant-design/issues/6717
|
const children = $slots.default && $slots.default[0]
|
||||||
// Don't bind keyboard support when children specify the onKeyDown
|
// https://github.com/ant-design/ant-design/issues/6717
|
||||||
if (children && children.props.onKeyDown) {
|
// Don't bind keyboard support when children specify the onKeyDown
|
||||||
children.props.onKeyDown(e);
|
if (children) {
|
||||||
return;
|
const keydown = getEvents(children).keydown
|
||||||
}
|
if (keydown) {
|
||||||
const activeValue = [...this.state.activeValue];
|
keydown(e)
|
||||||
const currentLevel = activeValue.length - 1 < 0 ? 0 : activeValue.length - 1;
|
return
|
||||||
const currentOptions = this.getCurrentLevelOptions();
|
}
|
||||||
const currentIndex = currentOptions.map(o => o.value).indexOf(activeValue[currentLevel]);
|
}
|
||||||
if (e.keyCode !== KeyCode.DOWN &&
|
const activeValue = [...this.sActiveValue]
|
||||||
|
const currentLevel = activeValue.length - 1 < 0 ? 0 : activeValue.length - 1
|
||||||
|
const currentOptions = this.getCurrentLevelOptions()
|
||||||
|
const currentIndex = currentOptions.map(o => o.value).indexOf(activeValue[currentLevel])
|
||||||
|
if (e.keyCode !== KeyCode.DOWN &&
|
||||||
e.keyCode !== KeyCode.UP &&
|
e.keyCode !== KeyCode.UP &&
|
||||||
e.keyCode !== KeyCode.LEFT &&
|
e.keyCode !== KeyCode.LEFT &&
|
||||||
e.keyCode !== KeyCode.RIGHT &&
|
e.keyCode !== KeyCode.RIGHT &&
|
||||||
e.keyCode !== KeyCode.ENTER &&
|
e.keyCode !== KeyCode.ENTER &&
|
||||||
e.keyCode !== KeyCode.BACKSPACE &&
|
e.keyCode !== KeyCode.BACKSPACE &&
|
||||||
e.keyCode !== KeyCode.ESC) {
|
e.keyCode !== KeyCode.ESC) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
// Press any keys above to reopen menu
|
// Press any keys above to reopen menu
|
||||||
if (!this.state.popupVisible &&
|
if (!this.sPopupVisible &&
|
||||||
e.keyCode !== KeyCode.BACKSPACE &&
|
e.keyCode !== KeyCode.BACKSPACE &&
|
||||||
e.keyCode !== KeyCode.LEFT &&
|
e.keyCode !== KeyCode.LEFT &&
|
||||||
e.keyCode !== KeyCode.RIGHT &&
|
e.keyCode !== KeyCode.RIGHT &&
|
||||||
e.keyCode !== KeyCode.ESC) {
|
e.keyCode !== KeyCode.ESC) {
|
||||||
this.setPopupVisible(true);
|
this.setPopupVisible(true)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (e.keyCode === KeyCode.DOWN || e.keyCode === KeyCode.UP) {
|
if (e.keyCode === KeyCode.DOWN || e.keyCode === KeyCode.UP) {
|
||||||
let nextIndex = currentIndex;
|
let nextIndex = currentIndex
|
||||||
if (nextIndex !== -1) {
|
if (nextIndex !== -1) {
|
||||||
if (e.keyCode === KeyCode.DOWN) {
|
if (e.keyCode === KeyCode.DOWN) {
|
||||||
nextIndex += 1;
|
nextIndex += 1
|
||||||
nextIndex = nextIndex >= currentOptions.length ? 0 : nextIndex;
|
nextIndex = nextIndex >= currentOptions.length ? 0 : nextIndex
|
||||||
|
} else {
|
||||||
|
nextIndex -= 1
|
||||||
|
nextIndex = nextIndex < 0 ? currentOptions.length - 1 : nextIndex
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
nextIndex -= 1;
|
nextIndex = 0
|
||||||
nextIndex = nextIndex < 0 ? currentOptions.length - 1 : nextIndex;
|
|
||||||
}
|
}
|
||||||
} else {
|
activeValue[currentLevel] = currentOptions[nextIndex].value
|
||||||
nextIndex = 0;
|
} else if (e.keyCode === KeyCode.LEFT || e.keyCode === KeyCode.BACKSPACE) {
|
||||||
|
activeValue.splice(activeValue.length - 1, 1)
|
||||||
|
} else if (e.keyCode === KeyCode.RIGHT) {
|
||||||
|
if (currentOptions[currentIndex] && currentOptions[currentIndex].children) {
|
||||||
|
activeValue.push(currentOptions[currentIndex].children[0].value)
|
||||||
|
}
|
||||||
|
} else if (e.keyCode === KeyCode.ESC) {
|
||||||
|
this.setPopupVisible(false)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
activeValue[currentLevel] = currentOptions[nextIndex].value;
|
if (!activeValue || activeValue.length === 0) {
|
||||||
} else if (e.keyCode === KeyCode.LEFT || e.keyCode === KeyCode.BACKSPACE) {
|
this.setPopupVisible(false)
|
||||||
activeValue.splice(activeValue.length - 1, 1);
|
|
||||||
} else if (e.keyCode === KeyCode.RIGHT) {
|
|
||||||
if (currentOptions[currentIndex] && currentOptions[currentIndex].children) {
|
|
||||||
activeValue.push(currentOptions[currentIndex].children[0].value);
|
|
||||||
}
|
}
|
||||||
} else if (e.keyCode === KeyCode.ESC) {
|
const activeOptions = this.getActiveOptions(activeValue)
|
||||||
this.setPopupVisible(false);
|
const targetOption = activeOptions[activeOptions.length - 1]
|
||||||
return;
|
this.handleMenuSelect(targetOption, activeOptions.length - 1, e)
|
||||||
}
|
this.__emit('keydown', e)
|
||||||
if (!activeValue || activeValue.length === 0) {
|
},
|
||||||
this.setPopupVisible(false);
|
},
|
||||||
}
|
|
||||||
const activeOptions = this.getActiveOptions(activeValue);
|
|
||||||
const targetOption = activeOptions[activeOptions.length - 1];
|
|
||||||
this.handleMenuSelect(targetOption, activeOptions.length - 1, e);
|
|
||||||
|
|
||||||
if (this.props.onKeyDown) {
|
render () {
|
||||||
this.props.onKeyDown(e);
|
const {
|
||||||
}
|
$props, $slots, sValue, sActiveValue, handleMenuSelect,
|
||||||
}
|
sPopupVisible, handlePopupVisibleChange, handleKeyDown,
|
||||||
|
} = this
|
||||||
saveTrigger = (node) => {
|
|
||||||
this.trigger = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
const {
|
||||||
prefixCls, transitionName, popupClassName, options, disabled,
|
prefixCls, transitionName, popupClassName, options, disabled,
|
||||||
builtinPlacements, popupPlacement, children, ...restProps,
|
builtinPlacements, popupPlacement, ...restProps } = $props
|
||||||
} = this.props;
|
|
||||||
// Did not show popup when there is no options
|
// Did not show popup when there is no options
|
||||||
let menus = <div />;
|
let menus = <div />
|
||||||
let emptyMenuClassName = '';
|
let emptyMenuClassName = ''
|
||||||
if (options && options.length > 0) {
|
if (options && options.length > 0) {
|
||||||
|
const menusProps = {
|
||||||
|
props: {
|
||||||
|
...$props,
|
||||||
|
value: sValue,
|
||||||
|
activeValue: sActiveValue,
|
||||||
|
visible: sPopupVisible,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
select: handleMenuSelect,
|
||||||
|
},
|
||||||
|
}
|
||||||
menus = (
|
menus = (
|
||||||
<Menus
|
<Menus
|
||||||
{...this.props}
|
{...menusProps}
|
||||||
value={this.state.value}
|
|
||||||
activeValue={this.state.activeValue}
|
|
||||||
onSelect={this.handleMenuSelect}
|
|
||||||
visible={this.state.popupVisible}
|
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
emptyMenuClassName = ` ${prefixCls}-menus-empty`;
|
emptyMenuClassName = ` ${prefixCls}-menus-empty`
|
||||||
|
}
|
||||||
|
const triggerProps = {
|
||||||
|
props: {
|
||||||
|
...restProps,
|
||||||
|
disabled: disabled,
|
||||||
|
popupPlacement: popupPlacement,
|
||||||
|
builtinPlacements: builtinPlacements,
|
||||||
|
popupTransitionName: transitionName,
|
||||||
|
action: disabled ? [] : ['click'],
|
||||||
|
popupVisible: disabled ? false : sPopupVisible,
|
||||||
|
prefixCls: `${prefixCls}-menus`,
|
||||||
|
popupClassName: popupClassName + emptyMenuClassName,
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
popupVisibleChange: handlePopupVisibleChange,
|
||||||
|
},
|
||||||
|
ref: 'trigger',
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Trigger
|
<Trigger {...triggerProps}>
|
||||||
ref={this.saveTrigger}
|
{$slots.default && cloneElement($slots.default[0], {
|
||||||
{...restProps}
|
on: {
|
||||||
options={options}
|
keydown: handleKeyDown,
|
||||||
disabled={disabled}
|
},
|
||||||
popupPlacement={popupPlacement}
|
attrs: {
|
||||||
builtinPlacements={builtinPlacements}
|
tabIndex: disabled ? undefined : 0,
|
||||||
popupTransitionName={transitionName}
|
},
|
||||||
action={disabled ? [] : ['click']}
|
|
||||||
popupVisible={disabled ? false : this.state.popupVisible}
|
|
||||||
onPopupVisibleChange={this.handlePopupVisibleChange}
|
|
||||||
prefixCls={`${prefixCls}-menus`}
|
|
||||||
popupClassName={popupClassName + emptyMenuClassName}
|
|
||||||
popup={menus}
|
|
||||||
>
|
|
||||||
{cloneElement(children, {
|
|
||||||
onKeyDown: this.handleKeyDown,
|
|
||||||
tabIndex: disabled ? undefined : 0,
|
|
||||||
})}
|
})}
|
||||||
|
<template slot='popup'>
|
||||||
|
{menus}
|
||||||
|
</template>
|
||||||
</Trigger>
|
</Trigger>
|
||||||
);
|
)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Cascader.defaultProps = {
|
|
||||||
options: [],
|
|
||||||
onChange() {},
|
|
||||||
onPopupVisibleChange() {},
|
|
||||||
disabled: false,
|
|
||||||
transitionName: '',
|
|
||||||
prefixCls: 'rc-cascader',
|
|
||||||
popupClassName: '',
|
|
||||||
popupPlacement: 'bottomLeft',
|
|
||||||
builtinPlacements: BUILT_IN_PLACEMENTS,
|
|
||||||
expandTrigger: 'click',
|
|
||||||
};
|
|
||||||
|
|
||||||
Cascader.propTypes = {
|
|
||||||
value: PropTypes.array,
|
|
||||||
defaultValue: PropTypes.array,
|
|
||||||
options: PropTypes.array.isRequired,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
onPopupVisibleChange: PropTypes.func,
|
|
||||||
popupVisible: PropTypes.bool,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
transitionName: PropTypes.string,
|
|
||||||
popupClassName: PropTypes.string,
|
|
||||||
popupPlacement: PropTypes.string,
|
|
||||||
prefixCls: PropTypes.string,
|
|
||||||
dropdownMenuColumnStyle: PropTypes.object,
|
|
||||||
builtinPlacements: PropTypes.object,
|
|
||||||
loadData: PropTypes.func,
|
|
||||||
changeOnSelect: PropTypes.bool,
|
|
||||||
children: PropTypes.node,
|
|
||||||
onKeyDown: PropTypes.func,
|
|
||||||
expandTrigger: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Cascader;
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,156 +1,150 @@
|
||||||
<script>
|
<script>
|
||||||
import React from 'react'
|
import PropTypes from '../_util/vue-types'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import arrayTreeFilter from 'array-tree-filter'
|
import arrayTreeFilter from 'array-tree-filter'
|
||||||
import { findDOMNode } from 'react-dom'
|
import BaseMixin from '../_util/BaseMixin'
|
||||||
|
|
||||||
class Menus extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [BaseMixin],
|
||||||
|
props: {
|
||||||
|
value: PropTypes.array.def([]),
|
||||||
|
activeValue: PropTypes.array.def([]),
|
||||||
|
options: PropTypes.array.isRequired,
|
||||||
|
prefixCls: PropTypes.string.def('rc-cascader-menus'),
|
||||||
|
expandTrigger: PropTypes.string.def('click'),
|
||||||
|
// onSelect: PropTypes.func,
|
||||||
|
visible: PropTypes.bool.def(false),
|
||||||
|
dropdownMenuColumnStyle: PropTypes.object,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
this.menuItems = {}
|
this.menuItems = {}
|
||||||
}
|
return {}
|
||||||
|
},
|
||||||
componentDidMount () {
|
mounted () {
|
||||||
this.scrollActiveItemToView()
|
this.$nextTick(() => {
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
|
||||||
if (!prevProps.visible && this.props.visible) {
|
|
||||||
this.scrollActiveItemToView()
|
this.scrollActiveItemToView()
|
||||||
}
|
})
|
||||||
}
|
},
|
||||||
|
watch: {
|
||||||
getOption (option, menuIndex) {
|
visible (val) {
|
||||||
const { prefixCls, expandTrigger } = this.props
|
if (val) {
|
||||||
const onSelect = this.props.onSelect.bind(this, option, menuIndex)
|
this.$nextTick(() => {
|
||||||
let expandProps = {
|
this.scrollActiveItemToView()
|
||||||
onClick: onSelect,
|
})
|
||||||
}
|
|
||||||
let menuItemCls = `${prefixCls}-menu-item`
|
|
||||||
const hasChildren = option.children && option.children.length > 0
|
|
||||||
if (hasChildren || option.isLeaf === false) {
|
|
||||||
menuItemCls += ` ${prefixCls}-menu-item-expand`
|
|
||||||
}
|
|
||||||
if (expandTrigger === 'hover' && hasChildren) {
|
|
||||||
expandProps = {
|
|
||||||
onMouseEnter: this.delayOnSelect.bind(this, onSelect),
|
|
||||||
onMouseLeave: this.delayOnSelect.bind(this),
|
|
||||||
onClick: onSelect,
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
if (this.isActiveOption(option, menuIndex)) {
|
},
|
||||||
menuItemCls += ` ${prefixCls}-menu-item-active`
|
methods: {
|
||||||
expandProps.ref = this.saveMenuItem(menuIndex)
|
getOption (option, menuIndex) {
|
||||||
}
|
const { prefixCls, expandTrigger } = this
|
||||||
if (option.disabled) {
|
const onSelect = (e) => {
|
||||||
menuItemCls += ` ${prefixCls}-menu-item-disabled`
|
this.__emit('select', option, menuIndex, e)
|
||||||
}
|
}
|
||||||
if (option.loading) {
|
const expandProps = {
|
||||||
menuItemCls += ` ${prefixCls}-menu-item-loading`
|
attrs: {
|
||||||
}
|
},
|
||||||
let title = ''
|
on: {
|
||||||
if (option.title) {
|
click: onSelect,
|
||||||
title = option.title
|
},
|
||||||
} else if (typeof option.label === 'string') {
|
key: option.value,
|
||||||
title = option.label
|
}
|
||||||
}
|
let menuItemCls = `${prefixCls}-menu-item`
|
||||||
return (
|
const hasChildren = option.children && option.children.length > 0
|
||||||
<li
|
if (hasChildren || option.isLeaf === false) {
|
||||||
key={option.value}
|
menuItemCls += ` ${prefixCls}-menu-item-expand`
|
||||||
className={menuItemCls}
|
}
|
||||||
title={title}
|
if (expandTrigger === 'hover' && hasChildren) {
|
||||||
{...expandProps}
|
expandProps.on = {
|
||||||
>
|
mouseenter: this.delayOnSelect.bind(this, onSelect),
|
||||||
{option.label}
|
mouseleave: this.delayOnSelect.bind(this),
|
||||||
</li>
|
click: onSelect,
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
if (this.isActiveOption(option, menuIndex)) {
|
||||||
|
menuItemCls += ` ${prefixCls}-menu-item-active`
|
||||||
|
expandProps.ref = this.getMenuItemRef(menuIndex)
|
||||||
|
}
|
||||||
|
if (option.disabled) {
|
||||||
|
menuItemCls += ` ${prefixCls}-menu-item-disabled`
|
||||||
|
}
|
||||||
|
if (option.loading) {
|
||||||
|
menuItemCls += ` ${prefixCls}-menu-item-loading`
|
||||||
|
}
|
||||||
|
let title = ''
|
||||||
|
if (option.title) {
|
||||||
|
title = option.title
|
||||||
|
} else if (typeof option.label === 'string') {
|
||||||
|
title = option.label
|
||||||
|
}
|
||||||
|
expandProps.attrs.title = title
|
||||||
|
expandProps.class = menuItemCls
|
||||||
|
return (
|
||||||
|
<li {...expandProps}>
|
||||||
|
{option.label}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
getActiveOptions (values) {
|
getActiveOptions (values) {
|
||||||
const activeValue = values || this.props.activeValue
|
const activeValue = values || this.activeValue
|
||||||
const options = this.props.options
|
const options = this.options
|
||||||
return arrayTreeFilter(options, (o, level) => o.value === activeValue[level])
|
return arrayTreeFilter(options, (o, level) => o.value === activeValue[level])
|
||||||
}
|
},
|
||||||
|
|
||||||
getShowOptions () {
|
getShowOptions () {
|
||||||
const { options } = this.props
|
const { options } = this
|
||||||
const result = this.getActiveOptions()
|
const result = this.getActiveOptions()
|
||||||
.map(activeOption => activeOption.children)
|
.map(activeOption => activeOption.children)
|
||||||
.filter(activeOption => !!activeOption)
|
.filter(activeOption => !!activeOption)
|
||||||
result.unshift(options)
|
result.unshift(options)
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
|
|
||||||
delayOnSelect (onSelect, ...args) {
|
delayOnSelect (onSelect, ...args) {
|
||||||
if (this.delayTimer) {
|
if (this.delayTimer) {
|
||||||
clearTimeout(this.delayTimer)
|
clearTimeout(this.delayTimer)
|
||||||
this.delayTimer = null
|
|
||||||
}
|
|
||||||
if (typeof onSelect === 'function') {
|
|
||||||
this.delayTimer = setTimeout(() => {
|
|
||||||
onSelect(args)
|
|
||||||
this.delayTimer = null
|
this.delayTimer = null
|
||||||
}, 150)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollActiveItemToView () {
|
|
||||||
// scroll into view
|
|
||||||
const optionsLength = this.getShowOptions().length
|
|
||||||
for (let i = 0; i < optionsLength; i++) {
|
|
||||||
const itemComponent = this.menuItems[i]
|
|
||||||
if (itemComponent) {
|
|
||||||
const target = findDOMNode(itemComponent)
|
|
||||||
target.parentNode.scrollTop = target.offsetTop
|
|
||||||
}
|
}
|
||||||
}
|
if (typeof onSelect === 'function') {
|
||||||
}
|
this.delayTimer = setTimeout(() => {
|
||||||
|
onSelect(args)
|
||||||
|
this.delayTimer = null
|
||||||
|
}, 150)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
isActiveOption (option, menuIndex) {
|
scrollActiveItemToView () {
|
||||||
const { activeValue = [] } = this.props
|
// scroll into view
|
||||||
return activeValue[menuIndex] === option.value
|
const optionsLength = this.getShowOptions().length
|
||||||
}
|
for (let i = 0; i < optionsLength; i++) {
|
||||||
|
const itemComponent = this.$refs[`menuItems_${i}`]
|
||||||
|
if (itemComponent) {
|
||||||
|
const target = itemComponent
|
||||||
|
target.parentNode.scrollTop = target.offsetTop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
saveMenuItem = (index) => (node) => {
|
isActiveOption (option, menuIndex) {
|
||||||
this.menuItems[index] = node
|
const { activeValue = [] } = this
|
||||||
}
|
return activeValue[menuIndex] === option.value
|
||||||
|
},
|
||||||
|
|
||||||
|
getMenuItemRef (index) {
|
||||||
|
return `menuItems_${index}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { prefixCls, dropdownMenuColumnStyle } = this.props
|
const { prefixCls, dropdownMenuColumnStyle } = this
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.getShowOptions().map((options, menuIndex) =>
|
{this.getShowOptions().map((options, menuIndex) =>
|
||||||
<ul className={`${prefixCls}-menu`} key={menuIndex} style={dropdownMenuColumnStyle}>
|
<ul class={`${prefixCls}-menu`} key={menuIndex} style={dropdownMenuColumnStyle}>
|
||||||
{options.map(option => this.getOption(option, menuIndex))}
|
{options.map(option => this.getOption(option, menuIndex))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Menus.defaultProps = {
|
|
||||||
options: [],
|
|
||||||
value: [],
|
|
||||||
activeValue: [],
|
|
||||||
onSelect () {},
|
|
||||||
prefixCls: 'rc-cascader-menus',
|
|
||||||
visible: false,
|
|
||||||
expandTrigger: 'click',
|
|
||||||
}
|
|
||||||
|
|
||||||
Menus.propTypes = {
|
|
||||||
value: PropTypes.array,
|
|
||||||
activeValue: PropTypes.array,
|
|
||||||
options: PropTypes.array.isRequired,
|
|
||||||
prefixCls: PropTypes.string,
|
|
||||||
expandTrigger: PropTypes.string,
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
visible: PropTypes.bool,
|
|
||||||
dropdownMenuColumnStyle: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Menus
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script>
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
import '../assets/index.less'
|
||||||
|
import Cascader from '../index'
|
||||||
|
|
||||||
|
const addressOptions = [{
|
||||||
|
label: '福建',
|
||||||
|
value: 'fj',
|
||||||
|
children: [{
|
||||||
|
label: '福州',
|
||||||
|
value: 'fuzhou',
|
||||||
|
children: [{
|
||||||
|
label: '马尾',
|
||||||
|
value: 'mawei',
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
label: '泉州',
|
||||||
|
value: 'quanzhou',
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
label: '浙江',
|
||||||
|
value: 'zj',
|
||||||
|
children: [{
|
||||||
|
label: '杭州',
|
||||||
|
value: 'hangzhou',
|
||||||
|
children: [{
|
||||||
|
label: '余杭',
|
||||||
|
value: 'yuhang',
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
label: '北京',
|
||||||
|
value: 'bj',
|
||||||
|
children: [{
|
||||||
|
label: '朝阳区',
|
||||||
|
value: 'chaoyang',
|
||||||
|
}, {
|
||||||
|
label: '海淀区',
|
||||||
|
value: 'haidian',
|
||||||
|
}],
|
||||||
|
}]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
inputValue: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onChange (value, selectedOptions) {
|
||||||
|
console.log(value, selectedOptions)
|
||||||
|
this.inputValue = selectedOptions.map(o => o.label).join(', ')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Cascader options={addressOptions} onChange={this.onChange} transitionName='slide-up'>
|
||||||
|
<input value={this.inputValue}/>
|
||||||
|
</Cascader>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -3,7 +3,7 @@ const AsyncComp = () => {
|
||||||
const hashs = window.location.hash.split('/')
|
const hashs = window.location.hash.split('/')
|
||||||
const d = hashs[hashs.length - 1]
|
const d = hashs[hashs.length - 1]
|
||||||
return {
|
return {
|
||||||
component: import(`../components/affix/demo/${d}`),
|
component: import(`../components/vc-cascader/demo/${d}`),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default [
|
export default [
|
||||||
|
|
|
@ -416,6 +416,11 @@
|
||||||
"integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
|
"integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"array-tree-filter": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "http://registry.npm.taobao.org/array-tree-filter/download/array-tree-filter-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-hzrAD+yDdJ8lWsjdCDgUtPYykZA="
|
||||||
|
},
|
||||||
"array-union": {
|
"array-union": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "http://10.134.27.134:7001/array-union/download/array-union-1.0.2.tgz",
|
"resolved": "http://10.134.27.134:7001/array-union/download/array-union-1.0.2.tgz",
|
||||||
|
@ -10935,6 +10940,16 @@
|
||||||
"safe-buffer": "5.1.1"
|
"safe-buffer": "5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"shallow-equal": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "http://registry.npm.taobao.org/shallow-equal/download/shallow-equal-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-UI0YOLPeWQq4dXsBGyXkMJAJRfc="
|
||||||
|
},
|
||||||
|
"shallowequal": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "http://registry.npm.taobao.org/shallowequal/download/shallowequal-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-FWHb3vuMAUCBADGQhXZNo/z4P48="
|
||||||
|
},
|
||||||
"shebang-command": {
|
"shebang-command": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz",
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"add-dom-event-listener": "^1.0.2",
|
"add-dom-event-listener": "^1.0.2",
|
||||||
|
"array-tree-filter": "^2.1.0",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"component-classes": "^1.2.6",
|
"component-classes": "^1.2.6",
|
||||||
"css-animation": "^1.4.1",
|
"css-animation": "^1.4.1",
|
||||||
|
@ -100,6 +101,7 @@
|
||||||
"lodash.isplainobject": "^4.0.6",
|
"lodash.isplainobject": "^4.0.6",
|
||||||
"moment": "^2.20.1",
|
"moment": "^2.20.1",
|
||||||
"omit.js": "^1.0.0",
|
"omit.js": "^1.0.0",
|
||||||
|
"shallow-equal": "^1.0.0",
|
||||||
"shallowequal": "^1.0.2",
|
"shallowequal": "^1.0.2",
|
||||||
"vue": "^2.5.13",
|
"vue": "^2.5.13",
|
||||||
"vue-clipboard2": "0.0.8",
|
"vue-clipboard2": "0.0.8",
|
||||||
|
|
Loading…
Reference in New Issue