feat: select
parent
5a0bb6d50e
commit
106b82ca67
|
@ -95,7 +95,7 @@ const getSlotOptions = ele => {
|
|||
};
|
||||
const findDOMNode = instance => {
|
||||
let node = instance.$el;
|
||||
while (!node.tagName) {
|
||||
while (node && !node.tagName) {
|
||||
node = node.nextSibling;
|
||||
}
|
||||
return node;
|
||||
|
@ -200,7 +200,8 @@ const getAllProps = ele => {
|
|||
return props;
|
||||
};
|
||||
|
||||
const getPropsData = vnode => {
|
||||
const getPropsData = ins => {
|
||||
const vnode = ins.$ ? ins.$ : ins;
|
||||
const res = {};
|
||||
const originProps = vnode.props || {};
|
||||
const props = {};
|
||||
|
|
|
@ -107,10 +107,6 @@ const Select = {
|
|||
choiceTransitionName: PropTypes.string.def('zoom'),
|
||||
},
|
||||
propTypes: SelectPropTypes,
|
||||
// model: {
|
||||
// prop: 'value',
|
||||
// event: 'change',
|
||||
// },
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||
|
|
|
@ -154,7 +154,9 @@ const Menu = {
|
|||
|
||||
render() {
|
||||
const props = { ...getOptionProps(this), ...this.$attrs };
|
||||
props.class += ` ${props.prefixCls}-root`;
|
||||
props.class = props.class
|
||||
? `${props.class} ${props.prefixCls}-root`
|
||||
: `${props.prefixCls}-root`;
|
||||
const subPopupMenuProps = {
|
||||
...props,
|
||||
itemIcon: getComponent(this, 'itemIcon', props),
|
||||
|
|
|
@ -5,7 +5,7 @@ import scrollIntoView from 'dom-scroll-into-view';
|
|||
import { getSelectKeys, preventDefaultEvent, saveRef } from './util';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getSlotOptions, findDOMNode } from '../_util/props-util';
|
||||
import { findDOMNode } from '../_util/props-util';
|
||||
|
||||
export default {
|
||||
name: 'DropdownMenu',
|
||||
|
@ -151,10 +151,11 @@ export default {
|
|||
};
|
||||
|
||||
clonedMenuItems = menuItems.map(item => {
|
||||
debugger;
|
||||
if (getSlotOptions(item).isMenuItemGroup) {
|
||||
const children = item.componentOptions.children.map(clone);
|
||||
return cloneElement(item, { children });
|
||||
if (item.type.isMenuItemGroup) {
|
||||
const children = (item.children?.default() || []).map(clone);
|
||||
const newItem = cloneElement(item);
|
||||
newItem.children = { ...item.children, default: () => children };
|
||||
return newItem;
|
||||
}
|
||||
return clone(item);
|
||||
});
|
||||
|
@ -182,9 +183,8 @@ export default {
|
|||
{...menuProps}
|
||||
selectedKeys={selectedKeys}
|
||||
prefixCls={`${prefixCls}-menu`}
|
||||
>
|
||||
{clonedMenuItems}
|
||||
</Menu>
|
||||
children={clonedMenuItems}
|
||||
></Menu>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import {TransitionGroup} from 'vue';
|
||||
import KeyCode from '../_util/KeyCode';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import classnames from 'classnames';
|
||||
|
@ -8,13 +9,10 @@ import Option from './Option';
|
|||
import OptGroup from './OptGroup';
|
||||
import {
|
||||
hasProp,
|
||||
getSlotOptions,
|
||||
getPropsData,
|
||||
getValueByProp as getValue,
|
||||
getComponent,
|
||||
getEvents,
|
||||
getClass,
|
||||
getAttrs,
|
||||
getOptionProps,
|
||||
} from '../_util/props-util';
|
||||
import getTransitionProps from '../_util/getTransitionProps';
|
||||
|
@ -106,10 +104,6 @@ const Select = {
|
|||
// onDeselect: noop,
|
||||
// onInputKeydown: noop,
|
||||
},
|
||||
// model: {
|
||||
// prop: 'value',
|
||||
// event: 'change',
|
||||
// },
|
||||
created() {
|
||||
this.saveInputRef = saveRef(this, 'inputRef');
|
||||
this.saveInputMirrorRef = saveRef(this, 'inputMirrorRef');
|
||||
|
@ -175,7 +169,7 @@ const Select = {
|
|||
__propsSymbol__() {
|
||||
Object.assign(this.$data, this.getDerivedState(getOptionProps(this), this.$data));
|
||||
},
|
||||
'$data._inputValue'(val) {
|
||||
_inputValue(val) {
|
||||
this.$data._mirrorInputValue = val;
|
||||
},
|
||||
},
|
||||
|
@ -232,8 +226,8 @@ const Select = {
|
|||
if (!child.data || child.data.slot !== undefined) {
|
||||
return;
|
||||
}
|
||||
if (getSlotOptions(child).isSelectOptGroup) {
|
||||
this.getOptionsFromChildren(child.componentOptions.children, options);
|
||||
if (child.type?.isSelectOptGroup) {
|
||||
this.getOptionsFromChildren(child.children?.default(), options);
|
||||
} else {
|
||||
options.push(child);
|
||||
}
|
||||
|
@ -787,32 +781,31 @@ const Select = {
|
|||
_getInputElement() {
|
||||
const props = this.$props;
|
||||
const { _inputValue: inputValue, _mirrorInputValue } = this.$data;
|
||||
const attrs = getAttrs(this);
|
||||
const attrs = this.$attrs;
|
||||
const defaultInput = <input id={attrs.id} autoComplete="off" />;
|
||||
|
||||
const inputElement = props.getInputElement ? props.getInputElement() : defaultInput;
|
||||
const inputCls = classnames(getClass(inputElement), {
|
||||
const inputCls = classnames(inputElement.class, {
|
||||
[`${props.prefixCls}-search__field`]: true,
|
||||
});
|
||||
const inputEvents = getEvents(inputElement);
|
||||
// https://github.com/ant-design/ant-design/issues/4992#issuecomment-281542159
|
||||
// Add space to the end of the inputValue as the width measurement tolerance
|
||||
inputElement.data = inputElement.data || {};
|
||||
return (
|
||||
<div class={`${props.prefixCls}-search__field__wrap`} onClick={this.inputClick}>
|
||||
{cloneElement(inputElement, {
|
||||
disabled: props.disabled,
|
||||
value: inputValue,
|
||||
...(inputElement.data.attrs || {}),
|
||||
...(inputElement.props || {}),
|
||||
disabled: props.disabled,
|
||||
value: inputValue,
|
||||
class: inputCls,
|
||||
ref: this.saveInputRef,
|
||||
directives: [
|
||||
{
|
||||
name: 'ant-input',
|
||||
},
|
||||
],
|
||||
// directives: [
|
||||
// {
|
||||
// name: 'ant-input',
|
||||
// },
|
||||
// ],
|
||||
onInput: this.onInputChange,
|
||||
onKeydown: chaining(
|
||||
this.onInputKeydown,
|
||||
|
@ -1099,6 +1092,7 @@ const Select = {
|
|||
const vls = this.getVLForOnChange(value);
|
||||
const options = this.getOptionsBySingleValue(value);
|
||||
this._valueOptions = options;
|
||||
this.$emit('update:value', vls);
|
||||
this.$emit('change', vls, isMultipleOrTags(this.$props) ? options : options[0]);
|
||||
},
|
||||
|
||||
|
@ -1186,16 +1180,11 @@ const Select = {
|
|||
const { _inputValue: inputValue } = this.$data;
|
||||
const tags = props.tags;
|
||||
children.forEach(child => {
|
||||
const type = child.type;
|
||||
warning(
|
||||
typeof type === 'object' && type.isSelectOption,
|
||||
'the children of `Select` should be `Select.Option` or `Select.OptGroup`, ' +
|
||||
`instead of \`${getSlotOptions(child).name || getSlotOptions(child)}\`.`,
|
||||
);
|
||||
if (typeof type !== 'object' || !type.isSelectOption) {
|
||||
if (!child) {
|
||||
return;
|
||||
}
|
||||
if (type.isSelectOptGroup) {
|
||||
const type = child.type;
|
||||
if (type?.isSelectOptGroup) {
|
||||
let label = getComponent(child, 'label');
|
||||
let key = child.key;
|
||||
if (!key && typeof label === 'string') {
|
||||
|
@ -1211,14 +1200,14 @@ const Select = {
|
|||
const childValueSub = getValuePropValue(subChild) || subChild.key;
|
||||
return (
|
||||
<MenuItem key={childValueSub} value={childValueSub} {...subChild.props}>
|
||||
{subChild.children?.default()}
|
||||
{...(subChild.children?.default())}
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
|
||||
sel.push(
|
||||
<MenuItemGroup key={key} title={label} class={child.props?.class}>
|
||||
{innerItems}
|
||||
{...innerItems}
|
||||
</MenuItemGroup>,
|
||||
);
|
||||
|
||||
|
@ -1232,7 +1221,7 @@ const Select = {
|
|||
if (innerItems.length) {
|
||||
sel.push(
|
||||
<MenuItemGroup key={key} title={label} {...child.props}>
|
||||
{innerItems}
|
||||
{...innerItems}
|
||||
</MenuItemGroup>,
|
||||
);
|
||||
}
|
||||
|
@ -1240,6 +1229,10 @@ const Select = {
|
|||
|
||||
return;
|
||||
}
|
||||
warning(
|
||||
typeof type === 'object' && type.isSelectOption,
|
||||
'the children of `Select` should be `Select.Option` or `Select.OptGroup`, ',
|
||||
);
|
||||
|
||||
const childValue = getValuePropValue(child);
|
||||
|
||||
|
@ -1416,10 +1409,10 @@ const Select = {
|
|||
if (isMultipleOrTags(props) && choiceTransitionName) {
|
||||
const transitionProps = getTransitionProps(choiceTransitionName, {
|
||||
tag: 'ul',
|
||||
afterLeave: this.onChoiceAnimationLeave,
|
||||
onAfterLeave: this.onChoiceAnimationLeave,
|
||||
});
|
||||
innerNode = (
|
||||
<transition-group {...transitionProps}>{selectedValueNodes}</transition-group>
|
||||
<TransitionGroup {...transitionProps}>{selectedValueNodes}</TransitionGroup>
|
||||
);
|
||||
} else {
|
||||
innerNode = <ul>{selectedValueNodes}</ul>;
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
// based on vc-select 9.2.2
|
||||
import ProxySelect, { Select } from './Select';
|
||||
import Select from './Select';
|
||||
import Option from './Option';
|
||||
import { SelectPropTypes } from './PropTypes';
|
||||
import OptGroup from './OptGroup';
|
||||
Select.Option = Option;
|
||||
Select.OptGroup = OptGroup;
|
||||
ProxySelect.Option = Option;
|
||||
ProxySelect.OptGroup = OptGroup;
|
||||
export { Select, Option, OptGroup, SelectPropTypes };
|
||||
export default ProxySelect;
|
||||
export default Select;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { getPropsData, getSlotOptions, getKey, getComponent } from '../_util/props-util';
|
||||
import { getPropsData, getComponent } from '../_util/props-util';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import { isVNode, Text } from 'vue';
|
||||
|
||||
export function toTitle(title) {
|
||||
if (typeof title === 'string') {
|
||||
|
@ -15,8 +16,8 @@ export function getValuePropValue(child) {
|
|||
if ('value' in props) {
|
||||
return props.value;
|
||||
}
|
||||
if (getKey(child) !== undefined) {
|
||||
return getKey(child);
|
||||
if (child.key !== undefined) {
|
||||
return child.key;
|
||||
}
|
||||
if (typeof child.type === 'object' && child.type.isSelectOptGroup) {
|
||||
const label = getComponent(child, 'label');
|
||||
|
@ -32,20 +33,14 @@ export function getPropValue(child, prop) {
|
|||
return getValuePropValue(child);
|
||||
}
|
||||
if (prop === 'children') {
|
||||
const newChild = child.$slots
|
||||
? cloneElement(child.$slots.default)
|
||||
: cloneElement(child.componentOptions.children);
|
||||
if (newChild.length === 1 && !newChild[0].tag) {
|
||||
return newChild[0].text;
|
||||
const newChild = cloneElement(getComponent(child));
|
||||
if (isVNode(newChild) && newChild.type === Text) {
|
||||
return newChild.children;
|
||||
}
|
||||
return newChild;
|
||||
}
|
||||
const data = getPropsData(child);
|
||||
if (prop in data) {
|
||||
return data[prop];
|
||||
} else {
|
||||
return child.props && child.props[prop];
|
||||
}
|
||||
const props = getPropsData(child);
|
||||
return props[prop];
|
||||
}
|
||||
|
||||
export function isMultiple(props) {
|
||||
|
@ -113,14 +108,14 @@ export function getLabelFromPropsValue(value, key) {
|
|||
return label;
|
||||
}
|
||||
|
||||
export function getSelectKeys(menuItems, value) {
|
||||
export function getSelectKeys(menuItems = [], value) {
|
||||
if (value === null || value === undefined) {
|
||||
return [];
|
||||
}
|
||||
let selectedKeys = [];
|
||||
menuItems.forEach(item => {
|
||||
if (getSlotOptions(item).isMenuItemGroup) {
|
||||
selectedKeys = selectedKeys.concat(getSelectKeys(item.componentOptions.children, value));
|
||||
if (item.type?.isMenuItemGroup) {
|
||||
selectedKeys = selectedKeys.concat(getSelectKeys(item.children?.default(), value));
|
||||
} else {
|
||||
const itemValue = getValuePropValue(item);
|
||||
const itemKey = item.key;
|
||||
|
@ -145,8 +140,8 @@ export function findFirstMenuItem(children) {
|
|||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
const props = getPropsData(child);
|
||||
if (getSlotOptions(child).isMenuItemGroup) {
|
||||
const found = findFirstMenuItem(child.componentOptions.children);
|
||||
if (child.type?.isMenuItemGroup) {
|
||||
const found = findFirstMenuItem(child.children?.default());
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ import PopupInner from './PopupInner';
|
|||
import LazyRenderBox from './LazyRenderBox';
|
||||
import animate from '../_util/css-animation';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getListeners, splitAttrs } from '../_util/props-util';
|
||||
import { saveRef } from './utils';
|
||||
import { getListeners, splitAttrs, findDOMNode } from '../_util/props-util';
|
||||
|
||||
export default {
|
||||
name: 'VCTriggerPopup',
|
||||
|
@ -36,6 +37,8 @@ export default {
|
|||
data() {
|
||||
this.domEl = null;
|
||||
this.currentAlignClassName = undefined;
|
||||
this.savePopupRef = saveRef.bind(this, 'popupInstance');
|
||||
this.saveAlignRef = saveRef.bind(this, 'alignInstance');
|
||||
return {
|
||||
// Used for stretch
|
||||
stretchChecked: false,
|
||||
|
@ -62,13 +65,13 @@ export default {
|
|||
this.setStretchSize();
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.$el.parentNode) {
|
||||
this.$el.parentNode.removeChild(this.$el);
|
||||
} else if (this.$el.remove) {
|
||||
this.$el.remove();
|
||||
}
|
||||
},
|
||||
// beforeUnmount() {
|
||||
// if (this.$el.parentNode) {
|
||||
// this.$el.parentNode.removeChild(this.$el);
|
||||
// } else if (this.$el.remove) {
|
||||
// this.$el.remove();
|
||||
// }
|
||||
// },
|
||||
methods: {
|
||||
onAlign(popupDomNode, align) {
|
||||
const props = this.$props;
|
||||
|
@ -111,7 +114,7 @@ export default {
|
|||
},
|
||||
|
||||
getPopupDomNode() {
|
||||
return this.$refs.popupInstance ? this.$refs.popupInstance.$el : null;
|
||||
return findDOMNode(this.popupInstance);
|
||||
},
|
||||
|
||||
getTargetElement() {
|
||||
|
@ -158,6 +161,7 @@ export default {
|
|||
} ${currentAlignClassName}`;
|
||||
},
|
||||
getPopupElement() {
|
||||
const { savePopupRef } = this;
|
||||
const { $props: props, $attrs, $slots, getTransitionName } = this;
|
||||
const { stretchChecked, targetHeight, targetWidth } = this.$data;
|
||||
const { style = {} } = $attrs;
|
||||
|
@ -197,8 +201,8 @@ export default {
|
|||
if (!stretchChecked) {
|
||||
// sizeStyle.visibility = 'hidden'
|
||||
setTimeout(() => {
|
||||
if (this.$refs.alignInstance) {
|
||||
this.$refs.alignInstance.forceAlign();
|
||||
if (this.alignInstance) {
|
||||
this.alignInstance.forceAlign();
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
@ -209,7 +213,7 @@ export default {
|
|||
// hiddenClassName,
|
||||
class: className,
|
||||
...onEvents,
|
||||
ref: 'popupInstance',
|
||||
ref: savePopupRef,
|
||||
style: { ...sizeStyle, ...popupStyle, ...style, ...this.getZIndexStyle() },
|
||||
};
|
||||
let transitionProps = {
|
||||
|
@ -221,13 +225,13 @@ export default {
|
|||
const transitionEvent = {
|
||||
onBeforeEnter: () => {
|
||||
// el.style.display = el.__vOriginalDisplay
|
||||
// this.$refs.alignInstance.forceAlign();
|
||||
// this.alignInstance.forceAlign();
|
||||
},
|
||||
onEnter: (el, done) => {
|
||||
// render 后 vue 会移除通过animate动态添加的 class导致动画闪动,延迟两帧添加动画class,可以进一步定位或者重写 transition 组件
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.alignInstance) {
|
||||
this.$refs.alignInstance.$nextTick(() => {
|
||||
if (this.alignInstance) {
|
||||
this.alignInstance.$nextTick(() => {
|
||||
this.domEl = el;
|
||||
animate(el, `${transitionName}-enter`, done);
|
||||
});
|
||||
|
@ -258,7 +262,7 @@ export default {
|
|||
<Align
|
||||
target={this.getAlignTarget()}
|
||||
key="popup"
|
||||
ref="alignInstance"
|
||||
ref={this.saveAlignRef}
|
||||
monitorWindowResize
|
||||
align={align}
|
||||
onAlign={this.onAlign}
|
||||
|
@ -275,7 +279,7 @@ export default {
|
|||
v-show={visible}
|
||||
target={this.getAlignTarget()}
|
||||
key="popup"
|
||||
ref="alignInstance"
|
||||
ref={this.saveAlignRef}
|
||||
monitorWindowResize
|
||||
disabled={!visible}
|
||||
align={align}
|
||||
|
|
|
@ -318,7 +318,7 @@ export default {
|
|||
return;
|
||||
}
|
||||
const target = event.target;
|
||||
const root = this.$el;
|
||||
const root = findDOMNode(this);
|
||||
if (!contains(root, target) && !this.hasPopupMouseDown) {
|
||||
this.close();
|
||||
}
|
||||
|
@ -417,11 +417,7 @@ export default {
|
|||
...mouseProps,
|
||||
ref: this.savePopup,
|
||||
};
|
||||
return (
|
||||
<Popup ref="popup" {...popupProps}>
|
||||
{getComponent(self, 'popup')}
|
||||
</Popup>
|
||||
);
|
||||
return <Popup {...popupProps}>{getComponent(self, 'popup')}</Popup>;
|
||||
},
|
||||
|
||||
getContainer() {
|
||||
|
@ -434,7 +430,7 @@ export default {
|
|||
popupContainer.style.left = '0';
|
||||
popupContainer.style.width = '100%';
|
||||
const mountNode = props.getPopupContainer
|
||||
? props.getPopupContainer(this.$el, dialogContext)
|
||||
? props.getPopupContainer(findDOMNode(this), dialogContext)
|
||||
: props.getDocument().body;
|
||||
mountNode.appendChild(popupContainer);
|
||||
this.popupContainer = popupContainer;
|
||||
|
|
|
@ -25,3 +25,7 @@ export function getAlignPopupClassName(builtinPlacements, prefixCls, align, isAl
|
|||
return '';
|
||||
}
|
||||
export function noop() {}
|
||||
|
||||
export function saveRef(name, component) {
|
||||
this[name] = component;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue