mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-12-05 05:29:01 +08:00
refactor: remove rc-menu
This commit is contained in:
parent
8179361803
commit
3d0edbc292
@ -134,8 +134,8 @@ export default defineComponent({
|
||||
|
||||
watch(
|
||||
() => props.selectedKeys,
|
||||
(selectedKeys = mergedSelectedKeys.value) => {
|
||||
mergedSelectedKeys.value = selectedKeys;
|
||||
selectedKeys => {
|
||||
mergedSelectedKeys.value = selectedKeys || mergedSelectedKeys.value;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import classNames from '../../_util/classNames';
|
||||
import { FunctionalComponent, provide } from 'vue';
|
||||
import { FunctionalComponent } from 'vue';
|
||||
import { useInjectMenu } from './hooks/useMenuContext';
|
||||
const InternalSubMenuList: FunctionalComponent<any> = (_props, { slots, attrs }) => {
|
||||
const { prefixCls, mode } = useInjectMenu();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { reactive, defineComponent, nextTick, computed, watch } from 'vue';
|
||||
import FilterFilled from '@ant-design/icons-vue/FilterFilled';
|
||||
import Menu, { SubMenu, Item as MenuItem } from '../vc-menu';
|
||||
import Menu, { SubMenu, MenuItem } from '../menu';
|
||||
import closest from '../_util/dom-closest';
|
||||
import classNames from '../_util/classNames';
|
||||
import shallowequal from '../_util/shallowequal';
|
||||
|
@ -1,310 +0,0 @@
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import SubMenu from './SubMenu';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getWidth, setStyle, menuAllProps } from './util';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import { getAllProps, getSlot, findDOMNode } from '../_util/props-util';
|
||||
|
||||
const MENUITEM_OVERFLOWED_CLASSNAME = 'menuitem-overflowed';
|
||||
const FLOAT_PRECISION_ADJUST = 0.5;
|
||||
const MENUITEM_OVERFLOWED_UNI_KEY = 'MENUITEM_OVERFLOWED_UNI_KEY';
|
||||
const MENUITEM_OVERFLOWED_UNI_KEYS = [MENUITEM_OVERFLOWED_UNI_KEY];
|
||||
|
||||
const DOMWrap = {
|
||||
name: 'DOMWrap',
|
||||
mixins: [BaseMixin],
|
||||
data() {
|
||||
this.resizeObserver = null;
|
||||
this.mutationObserver = null;
|
||||
|
||||
// original scroll size of the list
|
||||
this.originalTotalWidth = 0;
|
||||
|
||||
// copy of overflowed items
|
||||
this.overflowedItems = [];
|
||||
|
||||
// cache item of the original items (so we can track the size and order)
|
||||
this.menuItemSizes = [];
|
||||
return {
|
||||
lastVisibleIndex: undefined,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.setChildrenWidthAndResize();
|
||||
if (this.level === 1 && this.mode === 'horizontal') {
|
||||
const menuUl = findDOMNode(this);
|
||||
if (!menuUl) {
|
||||
return;
|
||||
}
|
||||
this.resizeObserver = new ResizeObserver(entries => {
|
||||
entries.forEach(this.setChildrenWidthAndResize);
|
||||
});
|
||||
|
||||
[].slice
|
||||
.call(menuUl.children)
|
||||
.concat(menuUl)
|
||||
.forEach(el => {
|
||||
this.resizeObserver.observe(el);
|
||||
});
|
||||
|
||||
if (typeof MutationObserver !== 'undefined') {
|
||||
this.mutationObserver = new MutationObserver(() => {
|
||||
this.resizeObserver.disconnect();
|
||||
[].slice
|
||||
.call(menuUl.children)
|
||||
.concat(menuUl)
|
||||
.forEach(el => {
|
||||
this.resizeObserver.observe(el);
|
||||
});
|
||||
this.setChildrenWidthAndResize();
|
||||
});
|
||||
this.mutationObserver.observe(menuUl, {
|
||||
attributes: false,
|
||||
childList: true,
|
||||
subTree: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// get all valid menuItem nodes
|
||||
getMenuItemNodes() {
|
||||
const { prefixCls } = this.$props;
|
||||
const ul = findDOMNode(this);
|
||||
if (!ul) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// filter out all overflowed indicator placeholder
|
||||
return [].slice
|
||||
.call(ul.children)
|
||||
.filter(node => node.className.split(' ').indexOf(`${prefixCls}-overflowed-submenu`) < 0);
|
||||
},
|
||||
|
||||
getOverflowedSubMenuItem(keyPrefix, overflowedItems, renderPlaceholder) {
|
||||
const { overflowedIndicator, level, mode, prefixCls, theme } = this.$props;
|
||||
if (level !== 1 || mode !== 'horizontal') {
|
||||
return null;
|
||||
}
|
||||
// put all the overflowed item inside a submenu
|
||||
// with a title of overflow indicator ('...')
|
||||
const copy = getSlot(this)[0];
|
||||
const allProps = getAllProps(copy) || {};
|
||||
const { title, extraProps, ...rest } = { ...allProps, ...allProps.extraProps }; // eslint-disable-line no-unused-vars
|
||||
let style = {};
|
||||
let key = `${keyPrefix}-overflowed-indicator`;
|
||||
let eventKey = `${keyPrefix}-overflowed-indicator`;
|
||||
|
||||
if (overflowedItems.length === 0 && renderPlaceholder !== true) {
|
||||
style = {
|
||||
display: 'none',
|
||||
};
|
||||
} else if (renderPlaceholder) {
|
||||
style = {
|
||||
visibility: 'hidden',
|
||||
// prevent from taking normal dom space
|
||||
position: 'absolute',
|
||||
};
|
||||
key = `${key}-placeholder`;
|
||||
eventKey = `${eventKey}-placeholder`;
|
||||
}
|
||||
|
||||
const popupClassName = theme ? `${prefixCls}-${theme}` : '';
|
||||
const props = {};
|
||||
menuAllProps.forEach(k => {
|
||||
if (rest[k] !== undefined) {
|
||||
props[k] = rest[k];
|
||||
}
|
||||
});
|
||||
const subMenuProps = {
|
||||
title: overflowedIndicator,
|
||||
popupClassName,
|
||||
...props,
|
||||
eventKey,
|
||||
disabled: false,
|
||||
class: `${prefixCls}-overflowed-submenu`,
|
||||
key,
|
||||
style,
|
||||
isOverflowedSubMenu: true,
|
||||
};
|
||||
return <SubMenu {...subMenuProps}>{overflowedItems}</SubMenu>;
|
||||
},
|
||||
|
||||
// memorize rendered menuSize
|
||||
setChildrenWidthAndResize() {
|
||||
if (this.mode !== 'horizontal') {
|
||||
return;
|
||||
}
|
||||
const ul = findDOMNode(this);
|
||||
|
||||
if (!ul) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ulChildrenNodes = ul.children;
|
||||
|
||||
if (!ulChildrenNodes || ulChildrenNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastOverflowedIndicatorPlaceholder = ul.children[ulChildrenNodes.length - 1];
|
||||
|
||||
// need last overflowed indicator for calculating length;
|
||||
setStyle(lastOverflowedIndicatorPlaceholder, 'display', 'inline-block');
|
||||
|
||||
const menuItemNodes = this.getMenuItemNodes();
|
||||
|
||||
// reset display attribute for all hidden elements caused by overflow to calculate updated width
|
||||
// and then reset to original state after width calculation
|
||||
|
||||
const overflowedItems = menuItemNodes.filter(
|
||||
c => c.className.split(' ').indexOf(MENUITEM_OVERFLOWED_CLASSNAME) >= 0,
|
||||
);
|
||||
|
||||
overflowedItems.forEach(c => {
|
||||
setStyle(c, 'display', 'inline-block');
|
||||
});
|
||||
|
||||
this.menuItemSizes = menuItemNodes.map(c => getWidth(c));
|
||||
|
||||
overflowedItems.forEach(c => {
|
||||
setStyle(c, 'display', 'none');
|
||||
});
|
||||
this.overflowedIndicatorWidth = getWidth(ul.children[ul.children.length - 1]);
|
||||
this.originalTotalWidth = this.menuItemSizes.reduce((acc, cur) => acc + cur, 0);
|
||||
this.handleResize();
|
||||
// prevent the overflowed indicator from taking space;
|
||||
setStyle(lastOverflowedIndicatorPlaceholder, 'display', 'none');
|
||||
},
|
||||
|
||||
handleResize() {
|
||||
if (this.mode !== 'horizontal') {
|
||||
return;
|
||||
}
|
||||
|
||||
const ul = findDOMNode(this);
|
||||
if (!ul) {
|
||||
return;
|
||||
}
|
||||
const width = getWidth(ul);
|
||||
|
||||
this.overflowedItems = [];
|
||||
let currentSumWidth = 0;
|
||||
|
||||
// index for last visible child in horizontal mode
|
||||
let lastVisibleIndex;
|
||||
|
||||
// float number comparison could be problematic
|
||||
// e.g. 0.1 + 0.2 > 0.3 =====> true
|
||||
// thus using FLOAT_PRECISION_ADJUST as buffer to help the situation
|
||||
if (this.originalTotalWidth > width + FLOAT_PRECISION_ADJUST) {
|
||||
lastVisibleIndex = -1;
|
||||
|
||||
this.menuItemSizes.forEach(liWidth => {
|
||||
currentSumWidth += liWidth;
|
||||
if (currentSumWidth + this.overflowedIndicatorWidth <= width) {
|
||||
lastVisibleIndex += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ lastVisibleIndex });
|
||||
},
|
||||
|
||||
renderChildren(children) {
|
||||
// need to take care of overflowed items in horizontal mode
|
||||
const { lastVisibleIndex } = this.$data;
|
||||
const className = this.$attrs.class || '';
|
||||
return (children || []).reduce((acc, childNode, index) => {
|
||||
let item = childNode;
|
||||
const { extraProps = {} } = item.props || {};
|
||||
const { eventKey } = extraProps;
|
||||
if (this.mode === 'horizontal') {
|
||||
let overflowed = this.getOverflowedSubMenuItem(eventKey, []);
|
||||
if (
|
||||
lastVisibleIndex !== undefined &&
|
||||
className.indexOf(`${this.prefixCls}-root`) !== -1
|
||||
) {
|
||||
if (index > lastVisibleIndex) {
|
||||
item = cloneElement(
|
||||
childNode,
|
||||
// 这里修改 eventKey 是为了防止隐藏状态下还会触发 openkeys 事件
|
||||
{
|
||||
extraProps: {
|
||||
...extraProps,
|
||||
style: { display: 'none' },
|
||||
eventKey: `${eventKey}-hidden`,
|
||||
class: MENUITEM_OVERFLOWED_CLASSNAME,
|
||||
parentUniKey: MENUITEM_OVERFLOWED_UNI_KEY,
|
||||
parentUniKeys: MENUITEM_OVERFLOWED_UNI_KEYS,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
if (index === lastVisibleIndex + 1) {
|
||||
this.overflowedItems = children.slice(lastVisibleIndex + 1).map(c => {
|
||||
const { extraProps = {} } = c.props || {};
|
||||
const { eventKey } = extraProps;
|
||||
return cloneElement(
|
||||
c,
|
||||
// children[index].key will become '.$key' in clone by default,
|
||||
// we have to overwrite with the correct key explicitly
|
||||
{
|
||||
extraProps: {
|
||||
...extraProps,
|
||||
key: eventKey,
|
||||
mode: 'vertical-left',
|
||||
parentUniKey: MENUITEM_OVERFLOWED_UNI_KEY,
|
||||
parentUniKeys: MENUITEM_OVERFLOWED_UNI_KEYS,
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
overflowed = this.getOverflowedSubMenuItem(eventKey, this.overflowedItems);
|
||||
}
|
||||
}
|
||||
|
||||
const ret = [...acc, overflowed, item];
|
||||
|
||||
if (index === children.length - 1) {
|
||||
// need a placeholder for calculating overflowed indicator width
|
||||
ret.push(this.getOverflowedSubMenuItem(eventKey, [], true));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return [...acc, item];
|
||||
}, []);
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const Tag = this.$props.tag;
|
||||
return <Tag>{this.renderChildren(getSlot(this))}</Tag>;
|
||||
},
|
||||
};
|
||||
|
||||
DOMWrap.props = {
|
||||
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
|
||||
prefixCls: PropTypes.string,
|
||||
level: PropTypes.number,
|
||||
theme: PropTypes.string,
|
||||
overflowedIndicator: PropTypes.any,
|
||||
visible: PropTypes.looseBool,
|
||||
hiddenClassName: PropTypes.string,
|
||||
tag: PropTypes.string.def('div'),
|
||||
};
|
||||
|
||||
export default DOMWrap;
|
@ -1,23 +0,0 @@
|
||||
import { inject } from 'vue';
|
||||
import { injectExtraPropsKey } from './FunctionProvider';
|
||||
export default {
|
||||
name: 'MenuDivider',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
rootPrefixCls: String,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
injectExtraProps: inject(injectExtraPropsKey, () => ({})),
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const { rootPrefixCls } = { ...this.$props, ...this.injectExtraProps };
|
||||
const { class: className = '', style } = this.$attrs;
|
||||
return <li class={[className, `${rootPrefixCls}-item-divider`]} style={style} />;
|
||||
},
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
// import PropTypes from '../_util/vue-types';
|
||||
import { computed, provide } from 'vue';
|
||||
import { propTypes } from '../vc-progress/src/types';
|
||||
export const injectExtraPropsKey = Symbol();
|
||||
const FunctionProvider = {
|
||||
inheritAttrs: false,
|
||||
isMenuProvider: true,
|
||||
props: {
|
||||
extraProps: propTypes.object,
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
provide(
|
||||
injectExtraPropsKey,
|
||||
computed(() => props.extraProps),
|
||||
);
|
||||
return () => slots.default?.();
|
||||
},
|
||||
};
|
||||
|
||||
export default FunctionProvider;
|
@ -1,46 +0,0 @@
|
||||
import { createVNode, defineComponent, inject, provide, watch } from 'vue';
|
||||
import { injectExtraPropsKey } from './FunctionProvider';
|
||||
|
||||
export default function wrapWithConnect(WrappedComponent) {
|
||||
const tempProps = WrappedComponent.props || {};
|
||||
const props = {};
|
||||
Object.keys(tempProps).forEach(k => {
|
||||
props[k] = { ...tempProps[k], required: false };
|
||||
});
|
||||
const Connect = {
|
||||
name: `Connect_${WrappedComponent.name}`,
|
||||
inheritAttrs: false,
|
||||
props,
|
||||
setup(props) {
|
||||
provide(injectExtraPropsKey, undefined); // 断掉 injectExtraPropsKey 的依赖
|
||||
const injectExtraProps = injectExtraPropsKey ? inject(injectExtraPropsKey, () => ({})) : {};
|
||||
watch(injectExtraProps, () => {
|
||||
// 神奇的问题,vue 3.0.3 之后,没能正确响应式,暂时加个 watch hack 一下
|
||||
});
|
||||
return {
|
||||
props,
|
||||
injectExtraProps,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getWrappedInstance() {
|
||||
return this.$refs.wrappedInstance;
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const { $slots = {}, $attrs } = this;
|
||||
const props = { ...this.props, ...this.injectExtraProps };
|
||||
const wrapProps = {
|
||||
...$attrs,
|
||||
...props,
|
||||
ref: 'wrappedInstance',
|
||||
};
|
||||
// const slots = {};
|
||||
// for (let [key, value] of Object.entries($slots)) {
|
||||
// slots[key] = () => value();
|
||||
// }
|
||||
return createVNode(WrappedComponent, wrapProps, $slots);
|
||||
},
|
||||
};
|
||||
return defineComponent(Connect);
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { default as SubPopupMenu } from './SubPopupMenu';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import hasProp, { getOptionProps, getComponent } from '../_util/props-util';
|
||||
import commonPropsType from './commonPropsType';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
provide,
|
||||
reactive,
|
||||
ref,
|
||||
toRaw,
|
||||
watch,
|
||||
} from 'vue';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
|
||||
const Menu = {
|
||||
name: 'Menu',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
...commonPropsType,
|
||||
onClick: PropTypes.func,
|
||||
selectable: PropTypes.looseBool.def(true),
|
||||
},
|
||||
mixins: [BaseMixin],
|
||||
setup(props) {
|
||||
const menuChildrenInfo = reactive({});
|
||||
const selectedKeys = ref(props.selectedKeys || props.defaultSelectedKeys || []);
|
||||
const openKeys = ref(props.openKeys || props.defaultOpenKeys || []);
|
||||
// computed(() => {
|
||||
// return props.openKeys || props.defaultOpenKeys || [];
|
||||
// });
|
||||
watch(
|
||||
() => props.selectedKeys,
|
||||
() => {
|
||||
selectedKeys.value = props.selectedKeys || [];
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.openKeys,
|
||||
() => {
|
||||
openKeys.value = props.openKeys || [];
|
||||
},
|
||||
);
|
||||
const activeKey = reactive({
|
||||
'0-menu-': props.activeKey,
|
||||
});
|
||||
const defaultActiveFirst = reactive({});
|
||||
const addChildrenInfo = (key, info) => {
|
||||
menuChildrenInfo[key] = info;
|
||||
};
|
||||
const removeChildrenInfo = key => {
|
||||
delete menuChildrenInfo[key];
|
||||
};
|
||||
const getActiveKey = key => {
|
||||
return key;
|
||||
}; // TODO
|
||||
const selectedParentUniKeys = ref([]);
|
||||
watch(menuChildrenInfo, () => {
|
||||
const keys = Object.values(menuChildrenInfo)
|
||||
.filter(info => info.isSelected)
|
||||
.reduce((allKeys, { parentUniKeys = [] }) => {
|
||||
return [...allKeys, ...toRaw(parentUniKeys)];
|
||||
}, []);
|
||||
if (!isEqual(selectedParentUniKeys.value, keys)) {
|
||||
selectedParentUniKeys.value = keys || [];
|
||||
}
|
||||
});
|
||||
const store = reactive({
|
||||
selectedKeys,
|
||||
openKeys,
|
||||
activeKey,
|
||||
defaultActiveFirst,
|
||||
menuChildrenInfo,
|
||||
selectedParentUniKeys,
|
||||
addChildrenInfo,
|
||||
removeChildrenInfo,
|
||||
getActiveKey,
|
||||
});
|
||||
const ins = getCurrentInstance();
|
||||
const getEl = () => {
|
||||
return ins.vnode.el;
|
||||
};
|
||||
provide('menuStore', store);
|
||||
provide(
|
||||
'parentMenu',
|
||||
reactive({
|
||||
isRootMenu: computed(() => props.isRootMenu),
|
||||
getPopupContainer: computed(() => props.getPopupContainer),
|
||||
getEl,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
store,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleSelect(selectInfo) {
|
||||
const props = this.$props;
|
||||
if (props.selectable) {
|
||||
// root menu
|
||||
let selectedKeys = this.store.selectedKeys;
|
||||
const selectedKey = selectInfo.key;
|
||||
if (props.multiple) {
|
||||
selectedKeys = selectedKeys.concat([selectedKey]);
|
||||
} else {
|
||||
selectedKeys = [selectedKey];
|
||||
}
|
||||
if (!hasProp(this, 'selectedKeys')) {
|
||||
this.store.selectedKeys = selectedKeys;
|
||||
}
|
||||
this.__emit('select', {
|
||||
...selectInfo,
|
||||
selectedKeys,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
handleClick(e) {
|
||||
this.__emit('click', e);
|
||||
},
|
||||
// onKeyDown needs to be exposed as a instance method
|
||||
// e.g., in rc-select, we need to navigate menu item while
|
||||
// current active item is rc-select input box rather than the menu itself
|
||||
onKeyDown(e, callback) {
|
||||
this.innerMenu.getWrappedInstance().onKeyDown(e, callback);
|
||||
},
|
||||
onOpenChange(event) {
|
||||
const openKeys = this.store.openKeys.concat();
|
||||
let changed = false;
|
||||
const processSingle = e => {
|
||||
let oneChanged = false;
|
||||
if (e.open) {
|
||||
oneChanged = openKeys.indexOf(e.key) === -1;
|
||||
if (oneChanged) {
|
||||
openKeys.push(e.key);
|
||||
}
|
||||
} else {
|
||||
const index = openKeys.indexOf(e.key);
|
||||
oneChanged = index !== -1;
|
||||
if (oneChanged) {
|
||||
openKeys.splice(index, 1);
|
||||
}
|
||||
}
|
||||
changed = changed || oneChanged;
|
||||
};
|
||||
if (Array.isArray(event)) {
|
||||
// batch change call
|
||||
event.forEach(processSingle);
|
||||
} else {
|
||||
processSingle(event);
|
||||
}
|
||||
if (changed) {
|
||||
if (!hasProp(this, 'openKeys')) {
|
||||
this.store.openKeys = openKeys;
|
||||
}
|
||||
this.__emit('openChange', openKeys);
|
||||
}
|
||||
},
|
||||
|
||||
handleDeselect(selectInfo) {
|
||||
const props = this.$props;
|
||||
if (props.selectable) {
|
||||
const selectedKeys = this.store.selectedKeys.concat();
|
||||
const selectedKey = selectInfo.key;
|
||||
const index = selectedKeys.indexOf(selectedKey);
|
||||
if (index !== -1) {
|
||||
selectedKeys.splice(index, 1);
|
||||
}
|
||||
if (!hasProp(this, 'selectedKeys')) {
|
||||
this.store.selectedKeys = selectedKeys;
|
||||
}
|
||||
this.__emit('deselect', {
|
||||
...selectInfo,
|
||||
selectedKeys,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getOpenTransitionName() {
|
||||
const props = this.$props;
|
||||
let transitionName = props.openTransitionName;
|
||||
const animationName = props.openAnimation;
|
||||
if (!transitionName && typeof animationName === 'string') {
|
||||
transitionName = `${props.prefixCls}-open-${animationName}`;
|
||||
}
|
||||
return transitionName;
|
||||
},
|
||||
saveInnerMenu(ref) {
|
||||
this.innerMenu = ref;
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const props = { ...getOptionProps(this), ...this.$attrs };
|
||||
props.class = props.class
|
||||
? `${props.class} ${props.prefixCls}-root`
|
||||
: `${props.prefixCls}-root`;
|
||||
const subPopupMenuProps = {
|
||||
...props,
|
||||
itemIcon: getComponent(this, 'itemIcon', props),
|
||||
expandIcon: getComponent(this, 'expandIcon', props),
|
||||
overflowedIndicator: getComponent(this, 'overflowedIndicator', props) || <span>···</span>,
|
||||
openTransitionName: this.getOpenTransitionName(),
|
||||
onClick: this.handleClick,
|
||||
onOpenChange: this.onOpenChange,
|
||||
onDeselect: this.handleDeselect,
|
||||
onSelect: this.handleSelect,
|
||||
ref: this.saveInnerMenu,
|
||||
store: this.store,
|
||||
};
|
||||
return <SubPopupMenu {...subPopupMenuProps} v-slots={this.$slots} />;
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent(Menu);
|
@ -1,224 +0,0 @@
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import KeyCode from '../_util/KeyCode';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import scrollIntoView from 'dom-scroll-into-view';
|
||||
import { noop, menuAllProps } from './util';
|
||||
import { getComponent, getSlot, findDOMNode } from '../_util/props-util';
|
||||
import { computed, defineComponent, inject, onBeforeUnmount, onMounted, toRaw } from 'vue';
|
||||
import InjectExtraProps from './InjectExtraProps';
|
||||
const props = {
|
||||
attribute: PropTypes.object,
|
||||
rootPrefixCls: PropTypes.string,
|
||||
eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
active: PropTypes.looseBool,
|
||||
selectedKeys: PropTypes.array,
|
||||
disabled: PropTypes.looseBool,
|
||||
title: PropTypes.any,
|
||||
index: PropTypes.number,
|
||||
inlineIndent: PropTypes.number.def(24),
|
||||
level: PropTypes.number.def(1),
|
||||
mode: PropTypes.oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
|
||||
multiple: PropTypes.looseBool,
|
||||
value: PropTypes.any,
|
||||
manualRef: PropTypes.func.def(noop),
|
||||
role: PropTypes.any,
|
||||
subMenuKey: PropTypes.string,
|
||||
itemIcon: PropTypes.any,
|
||||
parentUniKeys: PropTypes.array.def(() => []),
|
||||
parentUniKey: PropTypes.string,
|
||||
// clearSubMenuTimers: PropTypes.func.def(noop),
|
||||
};
|
||||
let indexGuid = 0;
|
||||
const MenuItem = defineComponent({
|
||||
name: 'AMenuItem',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
props,
|
||||
isMenuItem: true,
|
||||
setup(props) {
|
||||
const uniKey = `menu_item_${++indexGuid}`;
|
||||
const store = inject('menuStore', () => ({}));
|
||||
const isSelected = computed(
|
||||
() => store.selectedKeys && store.selectedKeys.indexOf(props.eventKey) !== -1,
|
||||
);
|
||||
onMounted(() => {
|
||||
store.addChildrenInfo(
|
||||
uniKey,
|
||||
computed(() => ({
|
||||
parentUniKeys: props.parentUniKeys,
|
||||
parentUniKey: props.parentUniKey,
|
||||
eventKey: props.eventKey,
|
||||
isSelected: isSelected.value,
|
||||
disabled: props.disabled,
|
||||
})),
|
||||
);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
store.removeChildrenInfo(uniKey);
|
||||
});
|
||||
|
||||
return {
|
||||
parentMenu: inject('parentMenu', undefined),
|
||||
isSelected,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.prevActive = this.active;
|
||||
// invoke customized ref to expose component to mixin
|
||||
this.callRef();
|
||||
},
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
const { active, parentMenu, eventKey } = this;
|
||||
if (!this.prevActive && active && (!parentMenu || !parentMenu[`scrolled-${eventKey}`])) {
|
||||
scrollIntoView(findDOMNode(this.node), parentMenu.getEl(), {
|
||||
onlyScrollIfNeeded: true,
|
||||
});
|
||||
parentMenu[`scrolled-${eventKey}`] = true;
|
||||
} else if (parentMenu && parentMenu[`scrolled-${eventKey}`]) {
|
||||
delete parentMenu[`scrolled-${eventKey}`];
|
||||
}
|
||||
this.prevActive = active;
|
||||
});
|
||||
this.callRef();
|
||||
},
|
||||
methods: {
|
||||
onKeyDown(e) {
|
||||
const keyCode = e.keyCode;
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
this.onClick(e);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
onMouseLeave(e) {
|
||||
const { eventKey } = this.$props;
|
||||
this.__emit('itemHover', {
|
||||
key: eventKey,
|
||||
hover: false,
|
||||
});
|
||||
this.__emit('mouseleave', {
|
||||
key: eventKey,
|
||||
domEvent: e,
|
||||
});
|
||||
},
|
||||
|
||||
onMouseEnter(e) {
|
||||
const { eventKey } = this;
|
||||
this.__emit('itemHover', {
|
||||
key: eventKey,
|
||||
hover: true,
|
||||
});
|
||||
this.__emit('mouseenter', {
|
||||
key: eventKey,
|
||||
domEvent: e,
|
||||
});
|
||||
},
|
||||
|
||||
onClick(e) {
|
||||
const { eventKey, multiple } = this.$props;
|
||||
const { isSelected } = this;
|
||||
const info = {
|
||||
key: eventKey,
|
||||
keyPath: [eventKey],
|
||||
item: { ...toRaw(this.$props), isSelected },
|
||||
domEvent: e,
|
||||
};
|
||||
|
||||
this.__emit('click', info);
|
||||
if (multiple) {
|
||||
if (isSelected) {
|
||||
this.__emit('deselect', info);
|
||||
} else {
|
||||
this.__emit('select', info);
|
||||
}
|
||||
} else if (!isSelected) {
|
||||
this.__emit('select', info);
|
||||
}
|
||||
},
|
||||
|
||||
getPrefixCls() {
|
||||
return `${this.$props.rootPrefixCls}-item`;
|
||||
},
|
||||
|
||||
getActiveClassName() {
|
||||
return `${this.getPrefixCls()}-active`;
|
||||
},
|
||||
|
||||
getSelectedClassName() {
|
||||
return `${this.getPrefixCls()}-selected`;
|
||||
},
|
||||
|
||||
getDisabledClassName() {
|
||||
return `${this.getPrefixCls()}-disabled`;
|
||||
},
|
||||
saveNode(node) {
|
||||
this.node = node;
|
||||
},
|
||||
callRef() {
|
||||
if (this.manualRef) {
|
||||
this.manualRef(this);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const { class: cls, style, ...props } = { ...this.$props, ...this.$attrs };
|
||||
|
||||
const className = {
|
||||
[cls]: !!cls,
|
||||
[this.getPrefixCls()]: true,
|
||||
[this.getActiveClassName()]: !props.disabled && this.active,
|
||||
[this.getSelectedClassName()]: this.isSelected,
|
||||
[this.getDisabledClassName()]: props.disabled,
|
||||
};
|
||||
let attrs = {
|
||||
...props.attribute,
|
||||
title: props.title,
|
||||
role: props.role || 'menuitem',
|
||||
'aria-disabled': props.disabled,
|
||||
};
|
||||
if (props.role === 'option') {
|
||||
// overwrite to option
|
||||
attrs = {
|
||||
...attrs,
|
||||
role: 'option',
|
||||
'aria-selected': this.isSelected,
|
||||
};
|
||||
} else if (props.role === null || props.role === 'none') {
|
||||
// sometimes we want to specify role inside <li/> element
|
||||
// <li><a role='menuitem'>Link</a></li> would be a good example
|
||||
// in this case the role on <li/> should be "none" to
|
||||
// remove the implied listitem role.
|
||||
// https://www.w3.org/TR/wai-aria-practices-1.1/examples/menubar/menubar-1/menubar-1.html
|
||||
attrs.role = 'none';
|
||||
}
|
||||
// In case that onClick/onMouseLeave/onMouseEnter is passed down from owner
|
||||
const mouseEvent = {
|
||||
onClick: props.disabled ? noop : this.onClick,
|
||||
onMouseleave: props.disabled ? noop : this.onMouseLeave,
|
||||
onMouseenter: props.disabled ? noop : this.onMouseEnter,
|
||||
};
|
||||
|
||||
const styles = { ...(style || {}) };
|
||||
if (props.mode === 'inline') {
|
||||
styles.paddingLeft = `${props.inlineIndent * props.level}px`;
|
||||
}
|
||||
menuAllProps.forEach(key => delete props[key]);
|
||||
const liProps = {
|
||||
...props,
|
||||
...attrs,
|
||||
...mouseEvent,
|
||||
ref: this.saveNode,
|
||||
};
|
||||
return (
|
||||
<li {...liProps} style={styles} class={className}>
|
||||
{getSlot(this)}
|
||||
{getComponent(this, 'itemIcon', props)}
|
||||
</li>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default InjectExtraProps(MenuItem);
|
||||
export { props as menuItemProps };
|
@ -1,51 +0,0 @@
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { getComponent, getSlot } from '../_util/props-util';
|
||||
import { menuAllProps } from './util';
|
||||
import { defineComponent, inject } from 'vue';
|
||||
import classNames from '../_util/classNames';
|
||||
import { injectExtraPropsKey } from './FunctionProvider';
|
||||
const MenuItemGroup = {
|
||||
name: 'MenuItemGroup',
|
||||
inheritAttrs: false,
|
||||
setup() {
|
||||
return {
|
||||
injectExtraProps: inject(injectExtraPropsKey, () => ({})),
|
||||
};
|
||||
},
|
||||
props: {
|
||||
renderMenuItem: PropTypes.func,
|
||||
index: PropTypes.number,
|
||||
className: PropTypes.string,
|
||||
subMenuKey: PropTypes.string,
|
||||
rootPrefixCls: PropTypes.string,
|
||||
disabled: PropTypes.looseBool.def(true),
|
||||
title: PropTypes.any,
|
||||
},
|
||||
isMenuItemGroup: true,
|
||||
methods: {
|
||||
renderInnerMenuItem(item) {
|
||||
const { renderMenuItem, index, subMenuKey } = { ...this.$props, ...this.injectExtraProps };
|
||||
return renderMenuItem(item, index, subMenuKey);
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const props = { ...this.$props, ...this.injectExtraProps, ...this.$attrs };
|
||||
const { class: cls = '', rootPrefixCls, title } = props;
|
||||
const titleClassName = `${rootPrefixCls}-item-group-title`;
|
||||
const listClassName = `${rootPrefixCls}-item-group-list`;
|
||||
menuAllProps.forEach(key => delete props[key]);
|
||||
// Set onClick to null, to ignore propagated onClick event
|
||||
delete props.onClick;
|
||||
const children = getSlot(this);
|
||||
return (
|
||||
<li {...props} class={classNames(cls, `${rootPrefixCls}-item-group`)}>
|
||||
<div class={titleClassName} title={typeof title === 'string' ? title : undefined}>
|
||||
{getComponent(this, 'title')}
|
||||
</div>
|
||||
<ul class={listClassName}>{children && children.map(this.renderInnerMenuItem)}</ul>
|
||||
</li>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent(MenuItemGroup);
|
@ -1,552 +0,0 @@
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
inject,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
provide,
|
||||
reactive,
|
||||
} from 'vue';
|
||||
import omit from 'omit.js';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import Trigger from '../vc-trigger';
|
||||
import KeyCode from '../_util/KeyCode';
|
||||
import SubPopupMenu from './SubPopupMenu';
|
||||
import placements from './placements';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { getComponent, splitAttrs, findDOMNode, getSlot } from '../_util/props-util';
|
||||
import { requestAnimationTimeout, cancelAnimationTimeout } from '../_util/requestAnimationTimeout';
|
||||
import { noop, getMenuIdFromSubMenuEventKey, loopMenuItemRecursively } from './util';
|
||||
import { getTransitionProps, Transition } from '../_util/transition';
|
||||
import InjectExtraProps from './InjectExtraProps';
|
||||
|
||||
let guid = 0;
|
||||
const popupPlacementMap = {
|
||||
horizontal: 'bottomLeft',
|
||||
vertical: 'rightTop',
|
||||
'vertical-left': 'rightTop',
|
||||
'vertical-right': 'leftTop',
|
||||
};
|
||||
|
||||
const updateDefaultActiveFirst = (store, eventKey, defaultActiveFirst) => {
|
||||
const menuId = getMenuIdFromSubMenuEventKey(eventKey);
|
||||
store.defaultActiveFirst[menuId] = defaultActiveFirst;
|
||||
};
|
||||
let indexGuid = 0;
|
||||
const SubMenu = defineComponent({
|
||||
name: 'SubMenu',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
isSubMenu: true,
|
||||
props: {
|
||||
title: PropTypes.any,
|
||||
openKeys: PropTypes.array.def([]),
|
||||
openChange: PropTypes.func.def(noop),
|
||||
rootPrefixCls: PropTypes.string,
|
||||
eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
multiple: PropTypes.looseBool,
|
||||
isRootMenu: PropTypes.looseBool.def(false),
|
||||
index: PropTypes.number,
|
||||
triggerSubMenuAction: PropTypes.string,
|
||||
popupClassName: PropTypes.string,
|
||||
getPopupContainer: PropTypes.func,
|
||||
forceSubMenuRender: PropTypes.looseBool.def(false),
|
||||
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
disabled: PropTypes.looseBool,
|
||||
subMenuOpenDelay: PropTypes.number.def(0.1),
|
||||
subMenuCloseDelay: PropTypes.number.def(0.1),
|
||||
level: PropTypes.number.def(1),
|
||||
inlineIndent: PropTypes.number.def(24),
|
||||
openTransitionName: PropTypes.string,
|
||||
popupOffset: PropTypes.array,
|
||||
mode: PropTypes.oneOf([
|
||||
'horizontal',
|
||||
'vertical',
|
||||
'vertical-left',
|
||||
'vertical-right',
|
||||
'inline',
|
||||
]).def('vertical'),
|
||||
manualRef: PropTypes.func.def(noop),
|
||||
builtinPlacements: PropTypes.object.def(() => ({})),
|
||||
itemIcon: PropTypes.any,
|
||||
expandIcon: PropTypes.any,
|
||||
subMenuKey: PropTypes.string,
|
||||
theme: PropTypes.string,
|
||||
parentUniKeys: PropTypes.array.def(() => []),
|
||||
parentUniKey: PropTypes.string,
|
||||
isOverflowedSubMenu: PropTypes.looseBool.def(false),
|
||||
},
|
||||
|
||||
isSubMenu: true,
|
||||
setup(props) {
|
||||
const uniKey = props.isOverflowedSubMenu
|
||||
? 'MENUITEM_OVERFLOWED_UNI_KEY'
|
||||
: `sub_menu_${++indexGuid}`;
|
||||
const store = inject('menuStore', () => ({}));
|
||||
onMounted(() => {
|
||||
store.addChildrenInfo(
|
||||
uniKey,
|
||||
computed(() => ({
|
||||
parentUniKeys: props.parentUniKeys,
|
||||
parentUniKey: props.parentUniKey,
|
||||
eventKey: props.eventKey,
|
||||
disabled: props.disabled,
|
||||
})),
|
||||
);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
store.removeChildrenInfo(uniKey);
|
||||
});
|
||||
const isChildrenSelected = computed(() => {
|
||||
return store.selectedParentUniKeys.indexOf(uniKey) !== -1;
|
||||
});
|
||||
const ins = getCurrentInstance();
|
||||
const getEl = () => {
|
||||
return ins.vnode.el;
|
||||
};
|
||||
provide(
|
||||
'parentMenu',
|
||||
reactive({
|
||||
isRootMenu: computed(() => props.isRootMenu),
|
||||
getPopupContainer: props.getPopupContainer,
|
||||
getEl,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
parentMenu: inject('parentMenu', undefined),
|
||||
store,
|
||||
isChildrenSelected,
|
||||
childrenUniKeys: [...props.parentUniKeys, uniKey],
|
||||
uniKey,
|
||||
isOpen: computed(() => store.openKeys.indexOf(props.eventKey) > -1),
|
||||
active: computed(() => store.activeKey[props.subMenuKey] === props.eventKey),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
const props = this.$props;
|
||||
const store = this.store;
|
||||
const eventKey = props.eventKey;
|
||||
const defaultActiveFirst = store.defaultActiveFirst;
|
||||
let value = false;
|
||||
|
||||
if (defaultActiveFirst) {
|
||||
value = defaultActiveFirst[eventKey];
|
||||
}
|
||||
|
||||
updateDefaultActiveFirst(store, eventKey, value);
|
||||
this.internalMenuId = undefined;
|
||||
this.haveRendered = undefined;
|
||||
this.haveOpened = undefined;
|
||||
this.subMenuTitle = undefined;
|
||||
return {};
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.handleUpdated();
|
||||
});
|
||||
},
|
||||
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
this.handleUpdated();
|
||||
});
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
const { eventKey } = this;
|
||||
this.__emit('destroy', eventKey);
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (this.minWidthTimeout) {
|
||||
cancelAnimationTimeout(this.minWidthTimeout);
|
||||
this.minWidthTimeout = null;
|
||||
}
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (this.mouseenterTimeout) {
|
||||
cancelAnimationTimeout(this.mouseenterTimeout);
|
||||
this.mouseenterTimeout = null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isChildrenSelected2() {
|
||||
if (this.haveOpened) return this.isChildrenSelected;
|
||||
const ret = { find: false };
|
||||
loopMenuItemRecursively(getSlot(this), this.store.selectedKeys, ret);
|
||||
return ret.find;
|
||||
},
|
||||
handleUpdated() {
|
||||
const { mode, manualRef } = this.$props;
|
||||
// invoke customized ref to expose component to mixin
|
||||
if (manualRef) {
|
||||
manualRef(this);
|
||||
}
|
||||
if (mode !== 'horizontal' || !this.parentMenu.isRootMenu || !this.isOpen) {
|
||||
return;
|
||||
}
|
||||
this.minWidthTimeout = requestAnimationTimeout(() => this.adjustWidth(), 0);
|
||||
},
|
||||
|
||||
onKeyDown(e) {
|
||||
const keyCode = e.keyCode;
|
||||
const menu = this.menuInstance;
|
||||
const { isOpen } = this;
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
this.onTitleClick(e);
|
||||
updateDefaultActiveFirst(this.store, this.$props.eventKey, true);
|
||||
return true;
|
||||
}
|
||||
if (keyCode === KeyCode.RIGHT) {
|
||||
if (isOpen) {
|
||||
menu.onKeyDown(e);
|
||||
} else {
|
||||
this.triggerOpenChange(true);
|
||||
// need to update current menu's defaultActiveFirst value
|
||||
updateDefaultActiveFirst(this.store, this.$props.eventKey, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (keyCode === KeyCode.LEFT) {
|
||||
let handled;
|
||||
if (isOpen) {
|
||||
handled = menu.onKeyDown(e);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
if (!handled) {
|
||||
this.triggerOpenChange(false);
|
||||
handled = true;
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
if (isOpen && (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN)) {
|
||||
return menu.onKeyDown(e);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
onPopupVisibleChange(visible) {
|
||||
this.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave');
|
||||
},
|
||||
|
||||
onMouseEnter(e) {
|
||||
const { eventKey: key } = this.$props;
|
||||
updateDefaultActiveFirst(this.store, key, false);
|
||||
this.__emit('mouseenter', {
|
||||
key,
|
||||
domEvent: e,
|
||||
});
|
||||
},
|
||||
|
||||
onMouseLeave(e) {
|
||||
const { eventKey } = this.$props;
|
||||
this.__emit('mouseleave', {
|
||||
key: eventKey,
|
||||
domEvent: e,
|
||||
});
|
||||
},
|
||||
|
||||
onTitleMouseEnter(domEvent) {
|
||||
const { eventKey: key } = this.$props;
|
||||
this.__emit('itemHover', {
|
||||
key,
|
||||
hover: true,
|
||||
});
|
||||
this.__emit('titleMouseenter', {
|
||||
key,
|
||||
domEvent,
|
||||
});
|
||||
},
|
||||
|
||||
onTitleMouseLeave(e) {
|
||||
const { eventKey } = this.$props;
|
||||
this.__emit('itemHover', {
|
||||
key: eventKey,
|
||||
hover: false,
|
||||
});
|
||||
this.__emit('titleMouseleave', {
|
||||
key: eventKey,
|
||||
domEvent: e,
|
||||
});
|
||||
},
|
||||
|
||||
onTitleClick(e) {
|
||||
const { triggerSubMenuAction, eventKey } = this.$props;
|
||||
this.__emit('titleClick', {
|
||||
key: eventKey,
|
||||
domEvent: e,
|
||||
});
|
||||
if (triggerSubMenuAction === 'hover') {
|
||||
return;
|
||||
}
|
||||
this.triggerOpenChange(!this.isOpen, 'click');
|
||||
updateDefaultActiveFirst(this.store, eventKey, false);
|
||||
},
|
||||
|
||||
onSubMenuClick(info) {
|
||||
this.__emit('click', this.addKeyPath(info));
|
||||
},
|
||||
|
||||
getPrefixCls() {
|
||||
return `${this.$props.rootPrefixCls}-submenu`;
|
||||
},
|
||||
|
||||
getActiveClassName() {
|
||||
return `${this.getPrefixCls()}-active`;
|
||||
},
|
||||
|
||||
getDisabledClassName() {
|
||||
return `${this.getPrefixCls()}-disabled`;
|
||||
},
|
||||
|
||||
getSelectedClassName() {
|
||||
return `${this.getPrefixCls()}-selected`;
|
||||
},
|
||||
|
||||
getOpenClassName() {
|
||||
return `${this.$props.rootPrefixCls}-submenu-open`;
|
||||
},
|
||||
|
||||
saveMenuInstance(c) {
|
||||
// children menu instance
|
||||
this.menuInstance = c;
|
||||
},
|
||||
|
||||
addKeyPath(info) {
|
||||
return {
|
||||
...info,
|
||||
keyPath: (info.keyPath || []).concat(this.$props.eventKey),
|
||||
};
|
||||
},
|
||||
triggerOpenChange(open, type) {
|
||||
const key = this.$props.eventKey;
|
||||
const openChange = () => {
|
||||
this.__emit('openChange', {
|
||||
key,
|
||||
item: this.$props,
|
||||
trigger: type,
|
||||
open,
|
||||
});
|
||||
};
|
||||
if (type === 'mouseenter') {
|
||||
// make sure mouseenter happen after other menu item's mouseleave
|
||||
this.mouseenterTimeout = requestAnimationTimeout(() => {
|
||||
openChange();
|
||||
}, 0);
|
||||
} else {
|
||||
openChange();
|
||||
}
|
||||
},
|
||||
adjustWidth() {
|
||||
/* istanbul ignore if */
|
||||
if (!this.subMenuTitle || !this.menuInstance) {
|
||||
return;
|
||||
}
|
||||
const popupMenu = findDOMNode(this.menuInstance);
|
||||
if (popupMenu.offsetWidth >= this.subMenuTitle.offsetWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
popupMenu.style.minWidth = `${this.subMenuTitle.offsetWidth}px`;
|
||||
},
|
||||
saveSubMenuTitle(subMenuTitle) {
|
||||
this.subMenuTitle = subMenuTitle;
|
||||
},
|
||||
renderChildren() {
|
||||
const props = { ...this.$props, ...this.$attrs };
|
||||
|
||||
const subPopupMenuProps = {
|
||||
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
|
||||
visible: this.isOpen,
|
||||
level: props.level + 1,
|
||||
inlineIndent: props.inlineIndent,
|
||||
focusable: false,
|
||||
eventKey: `${props.eventKey}-menu-`,
|
||||
openKeys: props.openKeys,
|
||||
openTransitionName: props.openTransitionName,
|
||||
openAnimation: props.openAnimation,
|
||||
subMenuOpenDelay: props.subMenuOpenDelay,
|
||||
subMenuCloseDelay: props.subMenuCloseDelay,
|
||||
forceSubMenuRender: props.forceSubMenuRender,
|
||||
triggerSubMenuAction: props.triggerSubMenuAction,
|
||||
builtinPlacements: props.builtinPlacements,
|
||||
multiple: props.multiple,
|
||||
prefixCls: props.rootPrefixCls,
|
||||
manualRef: this.saveMenuInstance,
|
||||
itemIcon: getComponent(this, 'itemIcon'),
|
||||
expandIcon: getComponent(this, 'expandIcon'),
|
||||
onClick: this.onSubMenuClick,
|
||||
onSelect: props.onSelect || noop,
|
||||
onDeselect: props.onDeselect || noop,
|
||||
onOpenChange: props.onOpenChange || noop,
|
||||
id: this.internalMenuId,
|
||||
parentUniKeys: this.childrenUniKeys,
|
||||
parentUniKey: this.uniKey,
|
||||
};
|
||||
const haveRendered = this.haveRendered;
|
||||
this.haveRendered = true;
|
||||
|
||||
this.haveOpened =
|
||||
this.haveOpened || subPopupMenuProps.visible || subPopupMenuProps.forceSubMenuRender;
|
||||
// never rendered not planning to, don't render
|
||||
if (!this.haveOpened) {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
// don't show transition on first rendering (no animation for opened menu)
|
||||
// show appear transition if it's not visible (not sure why)
|
||||
// show appear transition if it's not inline mode
|
||||
const transitionAppear =
|
||||
haveRendered || !subPopupMenuProps.visible || !subPopupMenuProps.mode === 'inline';
|
||||
subPopupMenuProps.class = ` ${subPopupMenuProps.prefixCls}-sub`;
|
||||
let transitionProps = { appear: transitionAppear, css: false };
|
||||
|
||||
if (subPopupMenuProps.openTransitionName) {
|
||||
transitionProps = getTransitionProps(subPopupMenuProps.openTransitionName, {
|
||||
appear: transitionAppear,
|
||||
});
|
||||
} else if (typeof subPopupMenuProps.openAnimation === 'object') {
|
||||
transitionProps = { ...transitionProps, ...(subPopupMenuProps.openAnimation || {}) };
|
||||
if (!transitionAppear) {
|
||||
transitionProps.appear = false;
|
||||
}
|
||||
} else if (typeof subPopupMenuProps.openAnimation === 'string') {
|
||||
transitionProps = getTransitionProps(subPopupMenuProps.openAnimation, {
|
||||
appear: transitionAppear,
|
||||
});
|
||||
}
|
||||
return (
|
||||
<Transition {...transitionProps}>
|
||||
<SubPopupMenu v-show={this.isOpen} {...subPopupMenuProps} v-slots={this.$slots} />
|
||||
</Transition>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const props = { ...this.$props, ...this.$attrs };
|
||||
const { onEvents } = splitAttrs(props);
|
||||
const isOpen = this.isOpen;
|
||||
const prefixCls = this.getPrefixCls();
|
||||
const isInlineMode = props.mode === 'inline';
|
||||
if (!this.internalMenuId) {
|
||||
if (props.eventKey) {
|
||||
this.internalMenuId = `${props.eventKey}$Menu`;
|
||||
} else {
|
||||
this.internalMenuId = `$__$${++guid}$Menu`;
|
||||
}
|
||||
}
|
||||
const children = this.renderChildren();
|
||||
const className = {
|
||||
[prefixCls]: true,
|
||||
[`${prefixCls}-${props.mode}`]: true,
|
||||
[props.class]: !!props.class,
|
||||
[this.getOpenClassName()]: isOpen,
|
||||
[this.getActiveClassName()]: this.active || (isOpen && !isInlineMode),
|
||||
[this.getDisabledClassName()]: props.disabled,
|
||||
[this.getSelectedClassName()]: this.isChildrenSelected || this.isChildrenSelected2(),
|
||||
};
|
||||
let mouseEvents = {};
|
||||
let titleClickEvents = {};
|
||||
let titleMouseEvents = {};
|
||||
if (!props.disabled) {
|
||||
mouseEvents = {
|
||||
onMouseleave: this.onMouseLeave,
|
||||
onMouseenter: this.onMouseEnter,
|
||||
};
|
||||
|
||||
// only works in title, not outer li
|
||||
titleClickEvents = {
|
||||
onClick: this.onTitleClick,
|
||||
};
|
||||
titleMouseEvents = {
|
||||
onMouseenter: this.onTitleMouseEnter,
|
||||
onMouseleave: this.onTitleMouseLeave,
|
||||
};
|
||||
}
|
||||
|
||||
const style = {};
|
||||
if (isInlineMode) {
|
||||
style.paddingLeft = `${props.inlineIndent * props.level}px`;
|
||||
}
|
||||
let ariaOwns = {};
|
||||
// only set aria-owns when menu is open
|
||||
// otherwise it would be an invalid aria-owns value
|
||||
// since corresponding node cannot be found
|
||||
if (isOpen) {
|
||||
ariaOwns = {
|
||||
'aria-owns': this.internalMenuId,
|
||||
};
|
||||
}
|
||||
const titleProps = {
|
||||
'aria-expanded': isOpen,
|
||||
...ariaOwns,
|
||||
'aria-haspopup': 'true',
|
||||
title: typeof props.title === 'string' ? props.title : undefined,
|
||||
...titleMouseEvents,
|
||||
...titleClickEvents,
|
||||
style,
|
||||
class: `${prefixCls}-title`,
|
||||
ref: this.saveSubMenuTitle,
|
||||
};
|
||||
|
||||
// expand custom icon should NOT be displayed in menu with horizontal mode.
|
||||
let icon = null;
|
||||
if (props.mode !== 'horizontal') {
|
||||
icon = getComponent(this, 'expandIcon', props);
|
||||
}
|
||||
const title = (
|
||||
<div {...titleProps}>
|
||||
{getComponent(this, 'title')}
|
||||
{icon || <i class={`${prefixCls}-arrow`} />}
|
||||
</div>
|
||||
);
|
||||
const getPopupContainer = this.parentMenu.isRootMenu
|
||||
? this.parentMenu.getPopupContainer
|
||||
: triggerNode => triggerNode.parentNode;
|
||||
const popupPlacement = popupPlacementMap[props.mode];
|
||||
const popupAlign = props.popupOffset ? { offset: props.popupOffset } : {};
|
||||
let popupClassName = props.mode === 'inline' ? '' : props.popupClassName || '';
|
||||
popupClassName = `${prefixCls}-popup ${popupClassName}`;
|
||||
const liProps = {
|
||||
...omit(onEvents, ['onClick']),
|
||||
...mouseEvents,
|
||||
class: className,
|
||||
style: props.style,
|
||||
};
|
||||
|
||||
return (
|
||||
<li {...liProps} role="menuitem">
|
||||
{isInlineMode && title}
|
||||
{isInlineMode && children}
|
||||
{!isInlineMode && (
|
||||
<Trigger
|
||||
prefixCls={prefixCls}
|
||||
popupClassName={popupClassName}
|
||||
getPopupContainer={getPopupContainer}
|
||||
builtinPlacements={placements}
|
||||
builtinPlacements={Object.assign({}, placements, props.builtinPlacements)}
|
||||
popupPlacement={popupPlacement}
|
||||
popupVisible={isOpen}
|
||||
popupAlign={popupAlign}
|
||||
action={props.disabled ? [] : [props.triggerSubMenuAction]}
|
||||
mouseEnterDelay={props.subMenuOpenDelay}
|
||||
mouseLeaveDelay={props.subMenuCloseDelay}
|
||||
onPopupVisibleChange={this.onPopupVisibleChange}
|
||||
forceRender={props.forceSubMenuRender}
|
||||
// popupTransitionName='rc-menu-open-slide-up'
|
||||
// popupAnimation={transitionProps}
|
||||
popup={children}
|
||||
>
|
||||
{title}
|
||||
</Trigger>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default InjectExtraProps(SubMenu);
|
@ -1,389 +0,0 @@
|
||||
import { Comment, inject } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import KeyCode from '../_util/KeyCode';
|
||||
import classNames from '../_util/classNames';
|
||||
import { getKeyFromChildrenIndex, noop, isMobileDevice, menuAllProps } from './util';
|
||||
import DOMWrap from './DOMWrap';
|
||||
import {
|
||||
initDefaultProps,
|
||||
getOptionProps,
|
||||
getComponent,
|
||||
splitAttrs,
|
||||
getSlot,
|
||||
} from '../_util/props-util';
|
||||
import FunctionProvider from './FunctionProvider';
|
||||
// import { getActiveKey } from '../vc-tabs/src/utils';
|
||||
function allDisabled(arr) {
|
||||
if (!arr.length) {
|
||||
return true;
|
||||
}
|
||||
return arr.every(c => {
|
||||
return !!c.disabled;
|
||||
});
|
||||
}
|
||||
|
||||
function updateActiveKey(store, menuId, activeKey) {
|
||||
store.activeKey[menuId] = activeKey;
|
||||
}
|
||||
|
||||
function getEventKey(props) {
|
||||
// when eventKey not available ,it's menu and return menu id '0-menu-'
|
||||
return props.eventKey || '0-menu-';
|
||||
}
|
||||
|
||||
export function saveRef(key, c) {
|
||||
if (c) {
|
||||
const index = this.instanceArrayKeyIndexMap[key];
|
||||
this.instanceArray[index] = c;
|
||||
}
|
||||
}
|
||||
// export function getActiveKey(props, originalActiveKey) {
|
||||
// let activeKey = originalActiveKey;
|
||||
// const { eventKey, defaultActiveFirst, children } = props;
|
||||
// if (activeKey !== undefined && activeKey !== null) {
|
||||
// let found;
|
||||
// loopMenuItem(children, (c, i) => {
|
||||
// const propsData = getPropsData(c);
|
||||
// if (c && !propsData.disabled && activeKey === getKeyFromChildrenIndex(c, eventKey, i)) {
|
||||
// found = true;
|
||||
// }
|
||||
// });
|
||||
// if (found) {
|
||||
// return activeKey;
|
||||
// }
|
||||
// }
|
||||
// activeKey = null;
|
||||
// if (defaultActiveFirst) {
|
||||
// loopMenuItem(children, (c, i) => {
|
||||
// const propsData = getPropsData(c);
|
||||
// const noActiveKey = activeKey === null || activeKey === undefined;
|
||||
// if (noActiveKey && c && !propsData.disabled) {
|
||||
// activeKey = getKeyFromChildrenIndex(c, eventKey, i);
|
||||
// }
|
||||
// });
|
||||
// return activeKey;
|
||||
// }
|
||||
// return activeKey;
|
||||
// }
|
||||
|
||||
const SubPopupMenu = {
|
||||
name: 'SubPopupMenu',
|
||||
inheritAttrs: false,
|
||||
props: initDefaultProps(
|
||||
{
|
||||
// onSelect: PropTypes.func,
|
||||
// onClick: PropTypes.func,
|
||||
// onDeselect: PropTypes.func,
|
||||
// onOpenChange: PropTypes.func,
|
||||
// onDestroy: PropTypes.func,
|
||||
prefixCls: PropTypes.string,
|
||||
openTransitionName: PropTypes.string,
|
||||
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
openKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
visible: PropTypes.looseBool,
|
||||
eventKey: PropTypes.string,
|
||||
|
||||
// adding in refactor
|
||||
focusable: PropTypes.looseBool,
|
||||
multiple: PropTypes.looseBool,
|
||||
defaultActiveFirst: PropTypes.looseBool,
|
||||
activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
defaultSelectedKeys: PropTypes.arrayOf(
|
||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
),
|
||||
defaultOpenKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
level: PropTypes.number,
|
||||
mode: PropTypes.oneOf([
|
||||
'horizontal',
|
||||
'vertical',
|
||||
'vertical-left',
|
||||
'vertical-right',
|
||||
'inline',
|
||||
]),
|
||||
triggerSubMenuAction: PropTypes.oneOf(['click', 'hover']),
|
||||
inlineIndent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
manualRef: PropTypes.func,
|
||||
itemIcon: PropTypes.any,
|
||||
expandIcon: PropTypes.any,
|
||||
overflowedIndicator: PropTypes.any,
|
||||
children: PropTypes.any.def([]),
|
||||
subMenuOpenDelay: PropTypes.number.def(0.1),
|
||||
subMenuCloseDelay: PropTypes.number.def(0.1),
|
||||
forceSubMenuRender: PropTypes.looseBool.def(false),
|
||||
parentUniKeys: PropTypes.array.def(() => []),
|
||||
parentUniKey: PropTypes.string,
|
||||
theme: PropTypes.string,
|
||||
},
|
||||
{
|
||||
prefixCls: 'rc-menu',
|
||||
mode: 'vertical',
|
||||
level: 1,
|
||||
inlineIndent: 24,
|
||||
visible: true,
|
||||
focusable: true,
|
||||
manualRef: noop,
|
||||
},
|
||||
),
|
||||
|
||||
mixins: [BaseMixin],
|
||||
setup() {
|
||||
const store = inject('menuStore', () => ({}));
|
||||
return { store };
|
||||
},
|
||||
created() {
|
||||
const props = getOptionProps(this);
|
||||
this.prevProps = { ...props };
|
||||
this.store.activeKey[props.eventKey] = this.store.getActiveKey(props.activeKey);
|
||||
this.instanceArray = [];
|
||||
},
|
||||
mounted() {
|
||||
// invoke customized ref to expose component to mixin
|
||||
if (this.manualRef) {
|
||||
this.manualRef(this);
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
const props = getOptionProps(this);
|
||||
const prevProps = this.prevProps;
|
||||
const originalActiveKey =
|
||||
'activeKey' in props ? props.activeKey : this.store.activeKey[getEventKey(props)];
|
||||
const activeKey = this.store.getActiveKey(originalActiveKey);
|
||||
if (activeKey !== originalActiveKey) {
|
||||
updateActiveKey(this.store, getEventKey(props), activeKey);
|
||||
} else if ('activeKey' in prevProps) {
|
||||
// If prev activeKey is not same as current activeKey,
|
||||
// we should set it.
|
||||
const prevActiveKey = this.store.getActiveKey(prevProps.activeKey);
|
||||
if (activeKey !== prevActiveKey) {
|
||||
updateActiveKey(this.store, getEventKey(props), activeKey);
|
||||
}
|
||||
}
|
||||
this.prevProps = { ...props };
|
||||
},
|
||||
methods: {
|
||||
// all keyboard events callbacks run from here at first
|
||||
onKeyDown(e, callback) {
|
||||
const keyCode = e.keyCode;
|
||||
let handled;
|
||||
this.getFlatInstanceArray().forEach(obj => {
|
||||
if (obj && obj.active && obj.onKeyDown) {
|
||||
handled = obj.onKeyDown(e);
|
||||
}
|
||||
});
|
||||
if (handled) {
|
||||
return 1;
|
||||
}
|
||||
let activeItem = null;
|
||||
if (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN) {
|
||||
activeItem = this.step(keyCode === KeyCode.UP ? -1 : 1);
|
||||
}
|
||||
if (activeItem) {
|
||||
e.preventDefault();
|
||||
updateActiveKey(this.store, getEventKey(this.$props), activeItem.eventKey);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(activeItem);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
onItemHover(e) {
|
||||
const { key, hover } = e;
|
||||
updateActiveKey(this.store, getEventKey(this.$props), hover ? key : null);
|
||||
},
|
||||
|
||||
onDeselect(selectInfo) {
|
||||
this.__emit('deselect', selectInfo);
|
||||
},
|
||||
|
||||
onSelect(selectInfo) {
|
||||
this.__emit('select', selectInfo);
|
||||
},
|
||||
|
||||
onClick(e) {
|
||||
this.__emit('click', e);
|
||||
},
|
||||
|
||||
onOpenChange(e) {
|
||||
this.__emit('openChange', e);
|
||||
},
|
||||
|
||||
onDestroy(key) {
|
||||
this.__emit('destroy', key);
|
||||
},
|
||||
|
||||
getFlatInstanceArray() {
|
||||
return this.instanceArray;
|
||||
},
|
||||
|
||||
getOpenTransitionName() {
|
||||
return this.$props.openTransitionName;
|
||||
},
|
||||
|
||||
step(direction) {
|
||||
let children = this.getFlatInstanceArray();
|
||||
const activeKey = this.store.activeKey[getEventKey(this.$props)];
|
||||
const len = children.length;
|
||||
if (!len) {
|
||||
return null;
|
||||
}
|
||||
if (direction < 0) {
|
||||
children = children.concat().reverse();
|
||||
}
|
||||
// find current activeIndex
|
||||
let activeIndex = -1;
|
||||
children.every((c, ci) => {
|
||||
if (c && c.eventKey === activeKey) {
|
||||
activeIndex = ci;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (
|
||||
!this.defaultActiveFirst &&
|
||||
activeIndex !== -1 &&
|
||||
allDisabled(children.slice(activeIndex, len - 1))
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
const start = (activeIndex + 1) % len;
|
||||
let i = start;
|
||||
|
||||
do {
|
||||
const child = children[i];
|
||||
if (!child || child.disabled) {
|
||||
i = (i + 1) % len;
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
} while (i !== start);
|
||||
|
||||
return null;
|
||||
},
|
||||
getIcon(instance, name) {
|
||||
return getComponent(instance, name);
|
||||
},
|
||||
renderCommonMenuItem(child, i, extraProps) {
|
||||
if (child.type === Comment) {
|
||||
return child;
|
||||
}
|
||||
const state = this.store;
|
||||
const props = this.$props;
|
||||
const key = getKeyFromChildrenIndex(child, props.eventKey, i);
|
||||
const childProps = child.props || {}; // child.props 包含事件
|
||||
|
||||
const isActive = key === state.activeKey[getEventKey(this.$props)];
|
||||
if (!childProps.disabled) {
|
||||
// manualRef的执行顺序不能保证,使用key映射ref在this.instanceArray中的位置
|
||||
this.instanceArrayKeyIndexMap[key] = Object.keys(this.instanceArrayKeyIndexMap).length;
|
||||
}
|
||||
const newChildProps = {
|
||||
mode: childProps.mode || props.mode,
|
||||
level: props.level,
|
||||
inlineIndent: props.inlineIndent,
|
||||
renderMenuItem: this.renderMenuItem,
|
||||
rootPrefixCls: props.prefixCls,
|
||||
index: i,
|
||||
// customized ref function, need to be invoked manually in child's componentDidMount
|
||||
manualRef: childProps.disabled ? noop : saveRef.bind(this, key),
|
||||
eventKey: key,
|
||||
active: !childProps.disabled && isActive,
|
||||
multiple: props.multiple,
|
||||
openTransitionName: this.getOpenTransitionName(),
|
||||
openAnimation: props.openAnimation,
|
||||
subMenuOpenDelay: props.subMenuOpenDelay,
|
||||
subMenuCloseDelay: props.subMenuCloseDelay,
|
||||
builtinPlacements: props.builtinPlacements,
|
||||
itemIcon: this.getIcon(child, 'itemIcon') || this.getIcon(this, 'itemIcon'),
|
||||
expandIcon: this.getIcon(child, 'expandIcon') || this.getIcon(this, 'expandIcon'),
|
||||
...extraProps,
|
||||
onClick: e => {
|
||||
(childProps.onClick || noop)(e);
|
||||
this.onClick(e);
|
||||
},
|
||||
onItemHover: this.onItemHover,
|
||||
onOpenChange: this.onOpenChange,
|
||||
onDeselect: this.onDeselect,
|
||||
// destroy: this.onDestroy,
|
||||
onSelect: this.onSelect,
|
||||
parentUniKeys: this.parentUniKeys,
|
||||
parentUniKey: this.parentUniKey,
|
||||
};
|
||||
if (props.forceSubMenuRender !== undefined) {
|
||||
newChildProps.forceSubMenuRender = props.forceSubMenuRender;
|
||||
}
|
||||
// ref: https://github.com/ant-design/ant-design/issues/13943
|
||||
if (props.mode === 'inline' || isMobileDevice()) {
|
||||
newChildProps.triggerSubMenuAction = 'click';
|
||||
}
|
||||
return <FunctionProvider extraProps={newChildProps}>{child}</FunctionProvider>;
|
||||
},
|
||||
|
||||
renderMenuItem(c, i, subMenuKey) {
|
||||
if (!c) {
|
||||
return null;
|
||||
}
|
||||
const state = this.store;
|
||||
const extraProps = {
|
||||
openKeys: state.openKeys,
|
||||
selectedKeys: state.selectedKeys,
|
||||
triggerSubMenuAction: this.triggerSubMenuAction,
|
||||
isRootMenu: false,
|
||||
subMenuKey,
|
||||
};
|
||||
return this.renderCommonMenuItem(c, i, extraProps);
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const props = { ...this.$props };
|
||||
const { onEvents, extraAttrs } = splitAttrs(this.$attrs);
|
||||
const { eventKey, prefixCls, visible, level, mode, theme } = props;
|
||||
this.instanceArray = [];
|
||||
this.instanceArrayKeyIndexMap = {};
|
||||
const className = classNames(
|
||||
extraAttrs.class,
|
||||
props.prefixCls,
|
||||
`${props.prefixCls}-${props.mode}`,
|
||||
);
|
||||
menuAllProps.forEach(key => delete props[key]);
|
||||
// Otherwise, the propagated click event will trigger another onClick
|
||||
delete onEvents.onClick;
|
||||
const domWrapProps = {
|
||||
...props,
|
||||
tag: 'ul',
|
||||
// hiddenClassName: `${prefixCls}-hidden`,
|
||||
visible,
|
||||
prefixCls,
|
||||
level,
|
||||
mode,
|
||||
theme,
|
||||
overflowedIndicator: getComponent(this, 'overflowedIndicator'),
|
||||
role: props.role || 'menu',
|
||||
class: className,
|
||||
style: extraAttrs.style,
|
||||
...onEvents,
|
||||
};
|
||||
if (extraAttrs.id !== undefined) {
|
||||
domWrapProps.id = extraAttrs.id;
|
||||
}
|
||||
if (props.focusable) {
|
||||
domWrapProps.tabindex = '0';
|
||||
domWrapProps.onKeydown = this.onKeyDown;
|
||||
}
|
||||
delete domWrapProps.children;
|
||||
return (
|
||||
// ESLint is not smart enough to know that the type of `children` was checked.
|
||||
/* eslint-disable */
|
||||
<DOMWrap {...domWrapProps}>
|
||||
{getSlot(this).map((c, i) => this.renderMenuItem(c, i, eventKey || '0-menu-'))}
|
||||
</DOMWrap>
|
||||
/*eslint -enable */
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default SubPopupMenu;
|
@ -1,318 +0,0 @@
|
||||
@menuPrefixCls: rc-menu;
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src: url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.eot');
|
||||
src: url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?#iefix')
|
||||
format('embedded-opentype'),
|
||||
url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.woff')
|
||||
format('woff'),
|
||||
url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf')
|
||||
format('truetype'),
|
||||
url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/fonts/fontawesome-webfont.svg?#fontawesomeregular')
|
||||
format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.@{menuPrefixCls} {
|
||||
outline: none;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0; // Override default ul/ol
|
||||
list-style: none;
|
||||
border: 1px solid #d9d9d9;
|
||||
box-shadow: 0 0 4px #d9d9d9;
|
||||
border-radius: 3px;
|
||||
color: #666;
|
||||
|
||||
&-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-collapse {
|
||||
overflow: hidden;
|
||||
&-active {
|
||||
transition: height 0.3s ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
&-item-group-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-item-group-title {
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid #dedede;
|
||||
}
|
||||
|
||||
&-item-active,
|
||||
&-submenu-active > &-submenu-title {
|
||||
background-color: #eaf8fe;
|
||||
}
|
||||
|
||||
&-item-selected {
|
||||
background-color: #eaf8fe;
|
||||
// fix chrome render bug
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
&-submenu-selected {
|
||||
background-color: #eaf8fe;
|
||||
}
|
||||
|
||||
& > li&-submenu {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-horizontal&-sub,
|
||||
&-vertical&-sub,
|
||||
&-vertical-left&-sub,
|
||||
&-vertical-right&-sub {
|
||||
min-width: 160px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-submenu-title {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 7px 7px 7px 16px;
|
||||
white-space: nowrap;
|
||||
|
||||
// Disabled state sets text to gray and nukes hover/tab effects
|
||||
&.@{menuPrefixCls}-item-disabled,
|
||||
&.@{menuPrefixCls}-submenu-disabled {
|
||||
color: #777 !important;
|
||||
}
|
||||
}
|
||||
& > &-item-divider {
|
||||
height: 1px;
|
||||
margin: 1px 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
&-submenu {
|
||||
&-popup {
|
||||
position: absolute;
|
||||
|
||||
.submenu-title-wrapper {
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
> .@{menuPrefixCls} {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.@{menuPrefixCls}-submenu-title,
|
||||
.@{menuPrefixCls}-item {
|
||||
.anticon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 8px;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
&-horizontal {
|
||||
background-color: #f3f5f7;
|
||||
border: none;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
box-shadow: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
& > .@{menuPrefixCls}-item,
|
||||
& > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
& > .@{menuPrefixCls}-submenu,
|
||||
& > .@{menuPrefixCls}-item {
|
||||
border-bottom: 2px solid transparent;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
|
||||
&-active {
|
||||
border-bottom: 2px solid #2db7f5;
|
||||
background-color: #f3f5f7;
|
||||
color: #2baee9;
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '\20';
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&-vertical,
|
||||
&-vertical-left,
|
||||
&-vertical-right,
|
||||
&-inline {
|
||||
padding: 12px 0;
|
||||
& > .@{menuPrefixCls}-item,
|
||||
& > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title {
|
||||
padding: 12px 8px 12px 24px;
|
||||
}
|
||||
.@{menuPrefixCls}-submenu-arrow {
|
||||
display: inline-block;
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
font-size: inherit;
|
||||
vertical-align: baseline;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
text-rendering: auto;
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
line-height: 1.5em;
|
||||
&:before {
|
||||
content: '\f0da';
|
||||
}
|
||||
}
|
||||
}
|
||||
&-inline {
|
||||
.@{menuPrefixCls}-submenu-arrow {
|
||||
transform: rotate(90deg);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
& .@{menuPrefixCls}-submenu-open > .@{menuPrefixCls}-submenu-title {
|
||||
.@{menuPrefixCls}-submenu-arrow {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-vertical&-sub,
|
||||
&-vertical-left&-sub,
|
||||
&-vertical-right&-sub {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-sub&-inline {
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
|
||||
& > .@{menuPrefixCls}-item,
|
||||
& > .@{menuPrefixCls}-submenu > .@{menuPrefixCls}-submenu-title {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.effect() {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
&-open {
|
||||
&-slide-up-enter,
|
||||
&-slide-up-appear {
|
||||
.effect();
|
||||
opacity: 0;
|
||||
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-slide-up-leave {
|
||||
.effect();
|
||||
opacity: 1;
|
||||
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-slide-up-enter&-slide-up-enter-active,
|
||||
&-slide-up-appear&-slide-up-appear-active {
|
||||
animation-name: rcMenuOpenSlideUpIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
&-slide-up-leave&-slide-up-leave-active {
|
||||
animation-name: rcMenuOpenSlideUpOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
@keyframes rcMenuOpenSlideUpIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
}
|
||||
@keyframes rcMenuOpenSlideUpOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform-origin: 0% 0%;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
}
|
||||
|
||||
&-zoom-enter,
|
||||
&-zoom-appear {
|
||||
opacity: 0;
|
||||
.effect();
|
||||
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-zoom-leave {
|
||||
.effect();
|
||||
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-zoom-enter&-zoom-enter-active,
|
||||
&-zoom-appear&-zoom-appear-active {
|
||||
animation-name: rcMenuOpenZoomIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
&-zoom-leave&-zoom-leave-active {
|
||||
animation-name: rcMenuOpenZoomOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
@keyframes rcMenuOpenZoomIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
@keyframes rcMenuOpenZoomOut {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import PropTypes from '../_util/vue-types';
|
||||
export default {
|
||||
prefixCls: PropTypes.string.def('rc-menu'),
|
||||
focusable: PropTypes.looseBool.def(true),
|
||||
multiple: PropTypes.looseBool,
|
||||
visible: PropTypes.looseBool.def(true),
|
||||
activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
defaultSelectedKeys: PropTypes.arrayOf(
|
||||
PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
).def([]),
|
||||
defaultOpenKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).def(
|
||||
[],
|
||||
),
|
||||
openKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
mode: PropTypes.oneOf([
|
||||
'horizontal',
|
||||
'vertical',
|
||||
'vertical-left',
|
||||
'vertical-right',
|
||||
'inline',
|
||||
]).def('vertical'),
|
||||
triggerSubMenuAction: PropTypes.string.def('hover'),
|
||||
subMenuOpenDelay: PropTypes.number.def(0.1),
|
||||
subMenuCloseDelay: PropTypes.number.def(0.1),
|
||||
level: PropTypes.number.def(1),
|
||||
inlineIndent: PropTypes.number.def(24),
|
||||
theme: PropTypes.oneOf(['light', 'dark']).def('light'),
|
||||
getPopupContainer: PropTypes.func,
|
||||
openTransitionName: PropTypes.string,
|
||||
forceSubMenuRender: PropTypes.looseBool.def(false),
|
||||
selectable: PropTypes.looseBool,
|
||||
isRootMenu: PropTypes.looseBool.def(true),
|
||||
builtinPlacements: PropTypes.object.def(() => ({})),
|
||||
itemIcon: PropTypes.any,
|
||||
expandIcon: PropTypes.any,
|
||||
overflowedIndicator: PropTypes.any,
|
||||
onClick: PropTypes.func,
|
||||
onSelect: PropTypes.func,
|
||||
onDeselect: PropTypes.func,
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
// based on rc-menu 7.5.5
|
||||
import Menu from './Menu';
|
||||
import SubMenu from './SubMenu';
|
||||
import MenuItem, { menuItemProps } from './MenuItem';
|
||||
import MenuItemGroup from './MenuItemGroup';
|
||||
import Divider from './Divider';
|
||||
|
||||
export {
|
||||
SubMenu,
|
||||
MenuItem as Item,
|
||||
menuItemProps as itemProps,
|
||||
MenuItem,
|
||||
MenuItemGroup,
|
||||
MenuItemGroup as ItemGroup,
|
||||
Divider,
|
||||
};
|
||||
|
||||
export default Menu;
|
@ -1,29 +0,0 @@
|
||||
const autoAdjustOverflow = {
|
||||
adjustX: 1,
|
||||
adjustY: 1,
|
||||
};
|
||||
|
||||
export const placements = {
|
||||
topLeft: {
|
||||
points: ['bl', 'tl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [0, -7],
|
||||
},
|
||||
bottomLeft: {
|
||||
points: ['tl', 'bl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [0, 7],
|
||||
},
|
||||
leftTop: {
|
||||
points: ['tr', 'tl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [-4, 0],
|
||||
},
|
||||
rightTop: {
|
||||
points: ['tl', 'tr'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [4, 0],
|
||||
},
|
||||
};
|
||||
|
||||
export default placements;
|
@ -1,147 +0,0 @@
|
||||
import isMobile from './utils/isMobile';
|
||||
import isObject from 'lodash-es/isObject';
|
||||
|
||||
export function noop() {}
|
||||
|
||||
export function getKeyFromChildrenIndex(child, menuEventKey, index) {
|
||||
const prefix = menuEventKey || '';
|
||||
return child.key === null ? `${prefix}item_${index}` : child.key;
|
||||
}
|
||||
|
||||
export function getMenuIdFromSubMenuEventKey(eventKey) {
|
||||
return `${eventKey}-menu-`;
|
||||
}
|
||||
|
||||
// export function loopMenuItem(children, cb) {
|
||||
// let index = -1;
|
||||
// children.forEach(c => {
|
||||
// index++;
|
||||
// if (c && c.type && c.type.isMenuItemGroup) {
|
||||
// c.children.default &&
|
||||
// c.children.default().forEach(c2 => {
|
||||
// index++;
|
||||
// cb(c2, index);
|
||||
// });
|
||||
// } else {
|
||||
// cb(c, index);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
export function loopMenuItemRecursively(children, keys, ret) {
|
||||
if (!children || ret.find) {
|
||||
return;
|
||||
}
|
||||
children.forEach(c => {
|
||||
if (ret.find) {
|
||||
return;
|
||||
}
|
||||
const construct = c.type;
|
||||
if (construct && isObject(construct)) {
|
||||
if (
|
||||
!construct ||
|
||||
!(
|
||||
construct.isSubMenu ||
|
||||
construct.isMenuItem ||
|
||||
construct.isMenuItemGroup ||
|
||||
construct.isMenuProvider
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (keys.indexOf(c.key) !== -1) {
|
||||
ret.find = true;
|
||||
} else if (c.children && c.children.default) {
|
||||
loopMenuItemRecursively(c.children.default(), keys, ret);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const menuAllProps = [
|
||||
'defaultSelectedKeys',
|
||||
'selectedKeys',
|
||||
'defaultOpenKeys',
|
||||
'openKeys',
|
||||
'mode',
|
||||
'getPopupContainer',
|
||||
'openTransitionName',
|
||||
'openAnimation',
|
||||
'subMenuOpenDelay',
|
||||
'subMenuCloseDelay',
|
||||
'forceSubMenuRender',
|
||||
'triggerSubMenuAction',
|
||||
'level',
|
||||
'selectable',
|
||||
'multiple',
|
||||
'visible',
|
||||
'focusable',
|
||||
'defaultActiveFirst',
|
||||
'prefixCls',
|
||||
'inlineIndent',
|
||||
'title',
|
||||
'rootPrefixCls',
|
||||
'eventKey',
|
||||
'active',
|
||||
'popupAlign',
|
||||
'popupOffset',
|
||||
'isOpen',
|
||||
'renderMenuItem',
|
||||
'manualRef',
|
||||
'subMenuKey',
|
||||
'disabled',
|
||||
'index',
|
||||
'isSelected',
|
||||
'store',
|
||||
'activeKey',
|
||||
'builtinPlacements',
|
||||
'overflowedIndicator',
|
||||
|
||||
// the following keys found need to be removed from test regression
|
||||
'attribute',
|
||||
'value',
|
||||
'popupClassName',
|
||||
'inlineCollapsed',
|
||||
'menu',
|
||||
'theme',
|
||||
'itemIcon',
|
||||
'expandIcon',
|
||||
|
||||
'onSelect',
|
||||
'onDeselect',
|
||||
'onDestroy',
|
||||
'onOpenChange',
|
||||
'onItemHover',
|
||||
'onTitleMouseenter',
|
||||
'onTitleMouseleave',
|
||||
'onTitleClick',
|
||||
'slots',
|
||||
'ref',
|
||||
'isRootMenu',
|
||||
'parentUniKeys',
|
||||
'parentUniKey',
|
||||
];
|
||||
|
||||
// ref: https://github.com/ant-design/ant-design/issues/14007
|
||||
// ref: https://bugs.chromium.org/p/chromium/issues/detail?id=360889
|
||||
// getBoundingClientRect return the full precision value, which is
|
||||
// not the same behavior as on chrome. Set the precision to 6 to
|
||||
// unify their behavior
|
||||
export const getWidth = elem => {
|
||||
let width =
|
||||
elem && typeof elem.getBoundingClientRect === 'function' && elem.getBoundingClientRect().width;
|
||||
if (width) {
|
||||
width = +width.toFixed(6);
|
||||
}
|
||||
return width || 0;
|
||||
};
|
||||
|
||||
export const setStyle = (elem, styleProperty, value) => {
|
||||
if (elem && typeof elem.style === 'object') {
|
||||
elem.style[styleProperty] = value;
|
||||
}
|
||||
};
|
||||
|
||||
export const isMobileDevice = () => {
|
||||
return isMobile.any;
|
||||
};
|
@ -1,110 +0,0 @@
|
||||
// MIT License from https://github.com/kaimallea/isMobile
|
||||
|
||||
const applePhone = /iPhone/i;
|
||||
const appleIpod = /iPod/i;
|
||||
const appleTablet = /iPad/i;
|
||||
const androidPhone = /\bAndroid(?:.+)Mobile\b/i; // Match 'Android' AND 'Mobile'
|
||||
const androidTablet = /Android/i;
|
||||
const amazonPhone = /\bAndroid(?:.+)SD4930UR\b/i;
|
||||
const amazonTablet = /\bAndroid(?:.+)(?:KF[A-Z]{2,4})\b/i;
|
||||
const windowsPhone = /Windows Phone/i;
|
||||
const windowsTablet = /\bWindows(?:.+)ARM\b/i; // Match 'Windows' AND 'ARM'
|
||||
const otherBlackberry = /BlackBerry/i;
|
||||
const otherBlackberry10 = /BB10/i;
|
||||
const otherOpera = /Opera Mini/i;
|
||||
const otherChrome = /\b(CriOS|Chrome)(?:.+)Mobile/i;
|
||||
const otherFirefox = /Mobile(?:.+)Firefox\b/i; // Match 'Mobile' AND 'Firefox'
|
||||
|
||||
function match(regex, userAgent) {
|
||||
return regex.test(userAgent);
|
||||
}
|
||||
|
||||
function isMobile(userAgent) {
|
||||
let ua = userAgent || (typeof navigator !== 'undefined' ? navigator.userAgent : '');
|
||||
|
||||
// Facebook mobile app's integrated browser adds a bunch of strings that
|
||||
// match everything. Strip it out if it exists.
|
||||
let tmp = ua.split('[FBAN');
|
||||
if (typeof tmp[1] !== 'undefined') {
|
||||
[ua] = tmp;
|
||||
}
|
||||
|
||||
// Twitter mobile app's integrated browser on iPad adds a "Twitter for
|
||||
// iPhone" string. Same probably happens on other tablet platforms.
|
||||
// This will confuse detection so strip it out if it exists.
|
||||
tmp = ua.split('Twitter');
|
||||
if (typeof tmp[1] !== 'undefined') {
|
||||
[ua] = tmp;
|
||||
}
|
||||
|
||||
const result = {
|
||||
apple: {
|
||||
phone: match(applePhone, ua) && !match(windowsPhone, ua),
|
||||
ipod: match(appleIpod, ua),
|
||||
tablet: !match(applePhone, ua) && match(appleTablet, ua) && !match(windowsPhone, ua),
|
||||
device:
|
||||
(match(applePhone, ua) || match(appleIpod, ua) || match(appleTablet, ua)) &&
|
||||
!match(windowsPhone, ua),
|
||||
},
|
||||
amazon: {
|
||||
phone: match(amazonPhone, ua),
|
||||
tablet: !match(amazonPhone, ua) && match(amazonTablet, ua),
|
||||
device: match(amazonPhone, ua) || match(amazonTablet, ua),
|
||||
},
|
||||
android: {
|
||||
phone:
|
||||
(!match(windowsPhone, ua) && match(amazonPhone, ua)) ||
|
||||
(!match(windowsPhone, ua) && match(androidPhone, ua)),
|
||||
tablet:
|
||||
!match(windowsPhone, ua) &&
|
||||
!match(amazonPhone, ua) &&
|
||||
!match(androidPhone, ua) &&
|
||||
(match(amazonTablet, ua) || match(androidTablet, ua)),
|
||||
device:
|
||||
(!match(windowsPhone, ua) &&
|
||||
(match(amazonPhone, ua) ||
|
||||
match(amazonTablet, ua) ||
|
||||
match(androidPhone, ua) ||
|
||||
match(androidTablet, ua))) ||
|
||||
match(/\bokhttp\b/i, ua),
|
||||
},
|
||||
windows: {
|
||||
phone: match(windowsPhone, ua),
|
||||
tablet: match(windowsTablet, ua),
|
||||
device: match(windowsPhone, ua) || match(windowsTablet, ua),
|
||||
},
|
||||
other: {
|
||||
blackberry: match(otherBlackberry, ua),
|
||||
blackberry10: match(otherBlackberry10, ua),
|
||||
opera: match(otherOpera, ua),
|
||||
firefox: match(otherFirefox, ua),
|
||||
chrome: match(otherChrome, ua),
|
||||
device:
|
||||
match(otherBlackberry, ua) ||
|
||||
match(otherBlackberry10, ua) ||
|
||||
match(otherOpera, ua) ||
|
||||
match(otherFirefox, ua) ||
|
||||
match(otherChrome, ua),
|
||||
},
|
||||
|
||||
// Additional
|
||||
any: null,
|
||||
phone: null,
|
||||
tablet: null,
|
||||
};
|
||||
result.any =
|
||||
result.apple.device || result.android.device || result.windows.device || result.other.device;
|
||||
|
||||
// excludes 'other' devices and ipods, targeting touchscreen phones
|
||||
result.phone = result.apple.phone || result.android.phone || result.windows.phone;
|
||||
result.tablet = result.apple.tablet || result.android.tablet || result.windows.tablet;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const defaultResult = {
|
||||
...isMobile(),
|
||||
isMobile,
|
||||
};
|
||||
|
||||
export default defaultResult;
|
Loading…
Reference in New Issue
Block a user