ant-design-vue
Version:
An enterprise-class UI design language and Vue-based implementation
364 lines (298 loc) • 13.3 kB
JavaScript
import { createVNode as _createVNode, isVNode as _isVNode } from "vue";
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
var __rest = this && this.__rest || function (s, e) {
var t = {};
for (var p in s) {
if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
}
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
}
return t;
};
import PropTypes from '../_util/vue-types';
import ResizeObserver from 'resize-observer-polyfill';
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';
function _isSlot(s) {
return typeof s === 'function' || Object.prototype.toString.call(s) === '[object Object]' && !_isVNode(s);
}
var MENUITEM_OVERFLOWED_CLASSNAME = 'menuitem-overflowed';
var FLOAT_PRECISION_ADJUST = 0.5;
var MENUITEM_OVERFLOWED_UNI_KEY = 'MENUITEM_OVERFLOWED_UNI_KEY';
var MENUITEM_OVERFLOWED_UNI_KEYS = [MENUITEM_OVERFLOWED_UNI_KEY];
var DOMWrap = {
name: 'DOMWrap',
mixins: [BaseMixin],
data: function 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: function mounted() {
var _this = this;
this.$nextTick(function () {
_this.setChildrenWidthAndResize();
if (_this.level === 1 && _this.mode === 'horizontal') {
var menuUl = findDOMNode(_this);
if (!menuUl) {
return;
}
_this.resizeObserver = new ResizeObserver(function (entries) {
entries.forEach(_this.setChildrenWidthAndResize);
});
[].slice.call(menuUl.children).concat(menuUl).forEach(function (el) {
_this.resizeObserver.observe(el);
});
if (typeof MutationObserver !== 'undefined') {
_this.mutationObserver = new MutationObserver(function () {
_this.resizeObserver.disconnect();
[].slice.call(menuUl.children).concat(menuUl).forEach(function (el) {
_this.resizeObserver.observe(el);
});
_this.setChildrenWidthAndResize();
});
_this.mutationObserver.observe(menuUl, {
attributes: false,
childList: true,
subTree: false
});
}
}
});
},
beforeUnmount: function beforeUnmount() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
},
methods: {
// get all valid menuItem nodes
getMenuItemNodes: function getMenuItemNodes() {
var prefixCls = this.$props.prefixCls;
var ul = findDOMNode(this);
if (!ul) {
return [];
} // filter out all overflowed indicator placeholder
return [].slice.call(ul.children).filter(function (node) {
return node.className.split(' ').indexOf("".concat(prefixCls, "-overflowed-submenu")) < 0;
});
},
getOverflowedSubMenuItem: function getOverflowedSubMenuItem(keyPrefix, overflowedItems, renderPlaceholder) {
var _this$$props = this.$props,
overflowedIndicator = _this$$props.overflowedIndicator,
level = _this$$props.level,
mode = _this$$props.mode,
prefixCls = _this$$props.prefixCls,
theme = _this$$props.theme;
if (level !== 1 || mode !== 'horizontal') {
return null;
} // put all the overflowed item inside a submenu
// with a title of overflow indicator ('...')
var copy = getSlot(this)[0];
var allProps = getAllProps(copy) || {};
var _a = _extends(_extends({}, allProps), allProps.extraProps),
title = _a.title,
extraProps = _a.extraProps,
rest = __rest(_a, ["title", "extraProps"]); // eslint-disable-line no-unused-vars
var style = {};
var key = "".concat(keyPrefix, "-overflowed-indicator");
var eventKey = "".concat(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 = "".concat(key, "-placeholder");
eventKey = "".concat(eventKey, "-placeholder");
}
var popupClassName = theme ? "".concat(prefixCls, "-").concat(theme) : '';
var props = {};
menuAllProps.forEach(function (k) {
if (rest[k] !== undefined) {
props[k] = rest[k];
}
});
var subMenuProps = _extends(_extends({
title: overflowedIndicator,
popupClassName: popupClassName
}, props), {
eventKey: eventKey,
disabled: false,
class: "".concat(prefixCls, "-overflowed-submenu"),
key: key,
style: style,
isOverflowedSubMenu: true
});
return _createVNode(SubMenu, subMenuProps, _isSlot(overflowedItems) ? overflowedItems : {
default: function _default() {
return [overflowedItems];
}
});
},
// memorize rendered menuSize
setChildrenWidthAndResize: function setChildrenWidthAndResize() {
if (this.mode !== 'horizontal') {
return;
}
var ul = findDOMNode(this);
if (!ul) {
return;
}
var ulChildrenNodes = ul.children;
if (!ulChildrenNodes || ulChildrenNodes.length === 0) {
return;
}
var lastOverflowedIndicatorPlaceholder = ul.children[ulChildrenNodes.length - 1]; // need last overflowed indicator for calculating length;
setStyle(lastOverflowedIndicatorPlaceholder, 'display', 'inline-block');
var 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
var overflowedItems = menuItemNodes.filter(function (c) {
return c.className.split(' ').indexOf(MENUITEM_OVERFLOWED_CLASSNAME) >= 0;
});
overflowedItems.forEach(function (c) {
setStyle(c, 'display', 'inline-block');
});
this.menuItemSizes = menuItemNodes.map(function (c) {
return getWidth(c);
});
overflowedItems.forEach(function (c) {
setStyle(c, 'display', 'none');
});
this.overflowedIndicatorWidth = getWidth(ul.children[ul.children.length - 1]);
this.originalTotalWidth = this.menuItemSizes.reduce(function (acc, cur) {
return acc + cur;
}, 0);
this.handleResize(); // prevent the overflowed indicator from taking space;
setStyle(lastOverflowedIndicatorPlaceholder, 'display', 'none');
},
handleResize: function handleResize() {
var _this2 = this;
if (this.mode !== 'horizontal') {
return;
}
var ul = findDOMNode(this);
if (!ul) {
return;
}
var width = getWidth(ul);
this.overflowedItems = [];
var currentSumWidth = 0; // index for last visible child in horizontal mode
var 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(function (liWidth) {
currentSumWidth += liWidth;
if (currentSumWidth + _this2.overflowedIndicatorWidth <= width) {
lastVisibleIndex += 1;
}
});
}
this.setState({
lastVisibleIndex: lastVisibleIndex
});
},
renderChildren: function renderChildren(children) {
var _this3 = this;
// need to take care of overflowed items in horizontal mode
var lastVisibleIndex = this.$data.lastVisibleIndex;
var className = this.$attrs.class || '';
return (children || []).reduce(function (acc, childNode, index) {
var item = childNode;
var _ref = item.props || {},
_ref$extraProps = _ref.extraProps,
extraProps = _ref$extraProps === void 0 ? {} : _ref$extraProps;
var eventKey = extraProps.eventKey;
if (_this3.mode === 'horizontal') {
var overflowed = _this3.getOverflowedSubMenuItem(eventKey, []);
if (lastVisibleIndex !== undefined && className.indexOf("".concat(_this3.prefixCls, "-root")) !== -1) {
if (index > lastVisibleIndex) {
item = cloneElement(childNode, // 这里修改 eventKey 是为了防止隐藏状态下还会触发 openkeys 事件
{
extraProps: _extends(_extends({}, extraProps), {
style: {
display: 'none'
},
eventKey: "".concat(eventKey, "-hidden"),
class: MENUITEM_OVERFLOWED_CLASSNAME,
parentUniKey: MENUITEM_OVERFLOWED_UNI_KEY,
parentUniKeys: MENUITEM_OVERFLOWED_UNI_KEYS
})
});
}
if (index === lastVisibleIndex + 1) {
_this3.overflowedItems = children.slice(lastVisibleIndex + 1).map(function (c) {
var _ref2 = c.props || {},
_ref2$extraProps = _ref2.extraProps,
extraProps = _ref2$extraProps === void 0 ? {} : _ref2$extraProps;
var eventKey = extraProps.eventKey;
return cloneElement(c, // children[index].key will become '.$key' in clone by default,
// we have to overwrite with the correct key explicitly
{
extraProps: _extends(_extends({}, extraProps), {
key: eventKey,
mode: 'vertical-left',
parentUniKey: MENUITEM_OVERFLOWED_UNI_KEY,
parentUniKeys: MENUITEM_OVERFLOWED_UNI_KEYS
})
});
});
overflowed = _this3.getOverflowedSubMenuItem(eventKey, _this3.overflowedItems);
}
}
var ret = [].concat(_toConsumableArray(acc), [overflowed, item]);
if (index === children.length - 1) {
// need a placeholder for calculating overflowed indicator width
ret.push(_this3.getOverflowedSubMenuItem(eventKey, [], true));
}
return ret;
}
return [].concat(_toConsumableArray(acc), [item]);
}, []);
}
},
render: function render() {
var _slot;
var Tag = this.$props.tag;
return _createVNode(Tag, null, _isSlot(_slot = this.renderChildren(getSlot(this))) ? _slot : {
default: function _default() {
return [_slot];
}
});
}
};
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;