choerodon-ui
Version:
An enterprise-class UI design language and React-based implementation
607 lines (536 loc) • 21.6 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _extends from "@babel/runtime/helpers/extends";
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import classnames from 'classnames';
import noop from 'lodash/noop';
import debounce from 'lodash/debounce';
import Button from '../../pro/es/button';
import { FuncType } from '../../pro/es/button/enum';
import { useModal } from '../../pro/es/modal-provider/ModalProvider';
import { $l } from '../../pro/es/locale-context';
import { getActiveKeyByGroupKey, getDataAttr, getHeader, getLeft, getTop, isTransformSupported, isVertical, setTransform } from './utils';
import warning from '../_util/warning';
import Ripple from '../ripple';
import TabBarInner from './TabBarInner';
import EventManager from '../_util/EventManager';
import { TabsType } from './enum';
import Icon from '../icon';
import Menu from '../menu';
import MenuItem from '../menu/MenuItem';
import Badge from '../badge';
import TabsContext from './TabsContext';
import KeyCode from '../_util/KeyCode';
import { Size } from '../_util/enum';
import CustomizationSettings from './customization-settings';
import Count from './Count';
var TabBar = function TabBar(props) {
var scrollAnimated = props.scrollAnimated,
className = props.className,
style = props.style,
inkBarStyle = props.inkBarStyle,
extraContent = props.extraContent,
tabBarGutter = props.tabBarGutter,
inkBarAnimated = props.inkBarAnimated,
type = props.type,
onRemoveTab = props.onRemoveTab,
restProps = _objectWithoutProperties(props, ["scrollAnimated", "className", "style", "inkBarStyle", "extraContent", "tabBarGutter", "inkBarAnimated", "type", "onRemoveTab"]);
var _useContext = useContext(TabsContext),
keyboard = _useContext.keyboard,
customizable = _useContext.customizable,
prefixCls = _useContext.prefixCls,
activeKey = _useContext.activeKey,
activeGroupKey = _useContext.activeGroupKey,
tabBarPosition = _useContext.tabBarPosition,
_useContext$hideOnlyG = _useContext.hideOnlyGroup,
hideOnlyGroup = _useContext$hideOnlyG === void 0 ? false : _useContext$hideOnlyG,
groupedPanelsMap = _useContext.groupedPanelsMap,
currentPanelMap = _useContext.currentPanelMap,
onTabClick = _useContext.onTabClick,
_useContext$onPrevCli = _useContext.onPrevClick,
onPrevClick = _useContext$onPrevCli === void 0 ? noop : _useContext$onPrevCli,
_useContext$onNextCli = _useContext.onNextClick,
onNextClick = _useContext$onNextCli === void 0 ? noop : _useContext$onNextCli,
changeActiveKey = _useContext.changeActiveKey;
var modal = useModal();
var openCustomizationModal = useCallback(function () {
if (customizable) {
var modalProps = {
drawer: true,
size: Size.small,
title: $l('Tabs', 'customization_settings'),
children: React.createElement(CustomizationSettings, null),
bodyStyle: {
overflow: 'hidden auto',
padding: 0
}
};
modalProps.okText = $l('Tabs', 'save');
modal.open(modalProps);
}
}, [customizable, modal]);
var getNextActiveKey = useCallback(function (next) {
var list = [];
currentPanelMap.forEach(function (c, key) {
if (!c.disabled) {
if (next) {
list.push(key);
} else {
list.unshift(key);
}
}
});
var length = list.length;
if (activeKey && length) {
var i = list.indexOf(activeKey);
var itemIndex = i === length - 1 ? 0 : i + 1;
return list[itemIndex] || list[0];
}
return undefined;
}, [activeKey, currentPanelMap]);
var handleKeyDown = useCallback(function (e) {
if (keyboard === false) {
return noop;
}
var keyCode = e.keyCode;
if (keyCode === KeyCode.RIGHT || keyCode === KeyCode.DOWN) {
e.preventDefault();
var nextKey = getNextActiveKey(true);
if (nextKey) {
changeActiveKey(nextKey);
}
} else if (keyCode === KeyCode.LEFT || keyCode === KeyCode.UP) {
e.preventDefault();
var previousKey = getNextActiveKey(false);
if (previousKey) {
changeActiveKey(previousKey);
}
}
}, [keyboard, changeActiveKey, getNextActiveKey]);
var handleTabClick = useCallback(function (key) {
if (onTabClick) {
onTabClick(key);
}
changeActiveKey(key);
}, [changeActiveKey, onTabClick]);
var handleGroupSelect = useCallback(function (param) {
var key = param.key;
if (activeGroupKey !== key) {
var newActiveKey = getActiveKeyByGroupKey(groupedPanelsMap, key);
if (newActiveKey) {
changeActiveKey(newActiveKey, true);
}
}
}, [changeActiveKey, activeGroupKey, groupedPanelsMap]);
var resizeEvent = useMemo(function () {
return new EventManager(typeof window === 'undefined' ? undefined : window);
}, []);
var lastNextPrevShownRef = useRef();
var offsetRef = useRef(0);
var containerRef = useRef(null);
var navWrapRef = useRef(null);
var navRef = useRef(null);
var rootRef = useRef(null);
var activeTabRef = useRef(null);
var inkBarRef = useRef(null);
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
next = _useState2[0],
setNext = _useState2[1];
var _useState3 = useState(false),
_useState4 = _slicedToArray(_useState3, 2),
prev = _useState4[0],
setPrev = _useState4[1];
var _useState5 = useState(activeKey),
_useState6 = _slicedToArray(_useState5, 2),
prevActiveKey = _useState6[0],
setActiveKey = _useState6[1];
var getTabs = function getTabs() {
return _toConsumableArray(currentPanelMap.entries()).reduce(function (rst, _ref, index, list) {
var _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
child = _ref2[1];
var disabled = child.disabled,
_child$closable = child.closable,
closable = _child$closable === void 0 ? true : _child$closable,
count = child.count,
overflowCount = child.overflowCount,
showCount = child.showCount;
var classes = ["".concat(prefixCls, "-tab")];
var tabProps = {
tabKey: key,
role: 'tab',
'aria-disabled': 'false',
'aria-selected': 'false',
style: {
marginRight: tabBarGutter && index === list.length - 1 ? 0 : tabBarGutter
}
};
if (disabled) {
classes.push("".concat(prefixCls, "-tab-disabled"));
tabProps['aria-disabled'] = 'true';
} else {
tabProps.onTabClick = handleTabClick;
}
if (activeKey === key) {
classes.push("".concat(prefixCls, "-tab-active"));
tabProps.ref = activeTabRef;
tabProps['aria-selected'] = 'true';
}
tabProps.className = classes.join(' ');
warning('tab' in child || 'title' in child, 'There must be `tab` or `title` property on children of Tabs.');
var title = React.createElement(React.Fragment, null, getHeader(child), showCount && React.createElement(Count, {
prefixCls: prefixCls,
count: count,
overflowCount: overflowCount
}));
rst.push(React.createElement(Ripple, {
disabled: disabled,
key: key
}, React.createElement(TabBarInner, _extends({}, tabProps), type === TabsType['editable-card'] ? React.createElement("div", {
className: closable ? undefined : "".concat(prefixCls, "-tab-unclosable")
}, title, closable && React.createElement(Icon, {
type: "close",
onClick: function onClick(e) {
return onRemoveTab(key, e);
}
})) : title)));
return rst;
}, []);
};
var getContent = function getContent(contents) {
if (extraContent || customizable) {
return [contents, React.createElement("div", {
key: "extra",
className: "".concat(prefixCls, "-extra-content")
}, customizable && React.createElement(Button, {
className: "".concat(prefixCls, "-hover-button"),
funcType: FuncType.flat,
icon: "predefine",
size: Size.small,
onClick: openCustomizationModal
}), extraContent)];
}
return [contents];
};
var getGroupNode = function getGroupNode() {
if (groupedPanelsMap.size > Number(hideOnlyGroup)) {
return React.createElement(Menu, {
prefixCls: "".concat(prefixCls, "-group"),
selectedKeys: activeGroupKey ? [activeGroupKey] : [],
onSelect: handleGroupSelect,
mode: isVertical(tabBarPosition) ? 'vertical' : 'horizontal'
}, _toConsumableArray(groupedPanelsMap.entries()).map(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
key = _ref4[0],
_ref4$1$group = _ref4[1].group,
tab = _ref4$1$group.tab,
disabled = _ref4$1$group.disabled,
dot = _ref4$1$group.dot;
return React.createElement(MenuItem, {
key: key,
disabled: disabled
}, React.createElement(Badge, {
dot: dot
}, tab));
}));
}
return undefined;
};
var getInkBarNode = function getInkBarNode() {
var inkBarClassName = "".concat(prefixCls, "-ink-bar");
var classes = classnames(inkBarClassName, inkBarAnimated ? "".concat(inkBarClassName, "-animated") : "".concat(inkBarClassName, "-no-animated"));
return React.createElement("div", {
style: inkBarStyle,
className: classes,
key: "inkBar",
ref: inkBarRef
});
};
var getOffsetWH = useCallback(function (node) {
return node[isVertical(tabBarPosition) ? 'offsetHeight' : 'offsetWidth'];
}, [tabBarPosition]);
var getScrollWH = useCallback(function (node) {
return node[isVertical(tabBarPosition) ? 'scrollHeight' : 'scrollWidth'];
}, [tabBarPosition]);
var getOffsetLT = useCallback(function (node) {
return node.getBoundingClientRect()[isVertical(tabBarPosition) ? 'top' : 'left'];
}, [tabBarPosition]);
var setOffset = useCallback(function (offset, callback) {
var nav = navRef.current;
if (nav) {
var target = Math.min(0, offset);
if (offsetRef.current !== target) {
offsetRef.current = target;
var navOffset = {};
var navStyle = nav.style;
var transformSupported = isTransformSupported(navStyle);
if (isVertical(tabBarPosition)) {
if (transformSupported) {
navOffset.value = "translate3d(0,".concat(target, "px,0)");
} else {
navOffset.name = 'top';
navOffset.value = "".concat(target, "px");
}
} else if (transformSupported) {
navOffset.value = "translate3d(".concat(target, "px,0,0)");
} else {
navOffset.name = 'left';
navOffset.value = "".concat(target, "px");
}
if (transformSupported) {
setTransform(navStyle, navOffset.value);
} else if (navOffset.name) {
navStyle[navOffset.name] = navOffset.value;
}
if (callback) {
callback();
}
}
}
}, [offsetRef, navRef, tabBarPosition]);
var setNextPrev = useCallback(function () {
var navNode = navRef.current;
var container = containerRef.current;
var navWrap = navWrapRef.current;
if (navNode && container && navWrap) {
var navNodeWH = getScrollWH(navNode);
var containerWH = getOffsetWH(container);
var navWrapNodeWH = getOffsetWH(navWrap);
var offset = offsetRef.current; // 当容器小于tab的时候使用最小值才可以防止回弹问题。
var navNodeWHValue = Math.min(containerWH, navWrapNodeWH);
var minOffset = navNodeWHValue - navNodeWH; // -165
var $next = next;
var $prev = prev;
if (minOffset >= 0) {
$next = false;
setOffset(0);
offset = 0;
} else if (minOffset < offset) {
$next = true;
} else {
$next = false; // Test with container offset which is stable
// and set the offset of the nav wrap node
var realOffset = navWrapNodeWH - navNodeWH;
setOffset(realOffset);
offset = realOffset;
}
if (offset < 0) {
$prev = true;
} else {
$prev = false;
}
if (prev !== $prev) {
setPrev($prev);
}
if (next !== $next) {
setNext($next);
}
return {
next: $next,
prev: $prev
};
}
}, [next, prev, navRef, containerRef, navWrapRef, offsetRef, getScrollWH, getOffsetWH, setOffset]);
var isNextPrevShown = useCallback(function (state) {
if (state) {
return state.next || state.prev;
}
return next || prev;
}, [next, prev]);
var toPrev = useCallback(function (e) {
onPrevClick(e);
var navWrapNode = navWrapRef.current;
if (navWrapNode) {
var navWrapNodeWH = getOffsetWH(navWrapNode);
setOffset(offsetRef.current + navWrapNodeWH, setNextPrev);
}
}, [getOffsetWH, setOffset, navWrapRef, onPrevClick, setNextPrev]);
var toNext = useCallback(function (e) {
onNextClick(e);
var navWrapNode = navWrapRef.current;
if (navWrapNode) {
var navWrapNodeWH = getOffsetWH(navWrapNode);
var offset = offsetRef.current;
setOffset(offset - navWrapNodeWH, setNextPrev);
}
}, [getOffsetWH, setOffset, navWrapRef, onNextClick, setNextPrev]);
var scrollToActiveTab = useCallback(function (e) {
var activeTab = activeTabRef.current;
var navWrap = navWrapRef.current;
if (e && e.target !== e.currentTarget || !activeTab || !navWrap) {
return;
} // when not scrollable or enter scrollable first time, don't emit scrolling
var needToSroll = isNextPrevShown() && lastNextPrevShownRef.current;
lastNextPrevShownRef.current = isNextPrevShown();
if (!needToSroll) {
return;
}
var activeTabWH = getScrollWH(activeTab);
var navWrapNodeWH = getOffsetWH(navWrap);
var offset = offsetRef.current;
var wrapOffset = getOffsetLT(navWrap);
var activeTabOffset = getOffsetLT(activeTab);
if (wrapOffset > activeTabOffset) {
offset += wrapOffset - activeTabOffset;
setOffset(offset, setNextPrev);
} else if (wrapOffset + navWrapNodeWH < activeTabOffset + activeTabWH) {
offset -= activeTabOffset + activeTabWH - (wrapOffset + navWrapNodeWH);
setOffset(offset, setNextPrev);
}
}, [activeTabRef, navWrapRef, lastNextPrevShownRef, getScrollWH, getOffsetWH, getOffsetLT, setOffset, setNextPrev, isNextPrevShown]);
var prevTransitionEnd = useCallback(function (e) {
if (e.propertyName !== 'opacity') {
return;
}
var current = containerRef.current;
if (current) {
scrollToActiveTab({
target: current,
currentTarget: current
});
}
}, [scrollToActiveTab, containerRef]);
var getScrollBarNode = function getScrollBarNode(content) {
var _classnames, _classnames2, _classnames3;
var showNextPrev = prev || next;
var prevButton = React.createElement("span", {
onClick: prev ? toPrev : undefined,
unselectable: "on",
className: classnames("".concat(prefixCls, "-tab-prev"), (_classnames = {}, _defineProperty(_classnames, "".concat(prefixCls, "-tab-btn-disabled"), !prev), _defineProperty(_classnames, "".concat(prefixCls, "-tab-arrow-show"), showNextPrev), _classnames)),
onTransitionEnd: prevTransitionEnd
}, React.createElement("span", {
className: "".concat(prefixCls, "-tab-prev-icon")
}));
var nextButton = React.createElement("span", {
onClick: next ? toNext : undefined,
unselectable: "on",
className: classnames((_classnames2 = {}, _defineProperty(_classnames2, "".concat(prefixCls, "-tab-next"), 1), _defineProperty(_classnames2, "".concat(prefixCls, "-tab-btn-disabled"), !next), _defineProperty(_classnames2, "".concat(prefixCls, "-tab-arrow-show"), showNextPrev), _classnames2))
}, React.createElement("span", {
className: "".concat(prefixCls, "-tab-next-icon")
}));
var navClassName = "".concat(prefixCls, "-nav");
var navClasses = classnames(navClassName, scrollAnimated ? "".concat(navClassName, "-animated") : "".concat(navClassName, "-no-animated"));
return React.createElement("div", {
className: classnames((_classnames3 = {}, _defineProperty(_classnames3, "".concat(prefixCls, "-nav-container"), 1), _defineProperty(_classnames3, "".concat(prefixCls, "-nav-container-scrolling"), showNextPrev), _classnames3)),
key: "container",
ref: containerRef
}, prevButton, nextButton, React.createElement("div", {
className: "".concat(prefixCls, "-nav-wrap"),
ref: navWrapRef
}, React.createElement("div", {
className: "".concat(prefixCls, "-nav-scroll")
}, React.createElement("div", {
className: navClasses,
ref: navRef
}, content))));
};
useLayoutEffect(function () {
var inkBarNode = inkBarRef.current;
if (inkBarNode) {
var inkBarNodeStyle = inkBarNode.style;
var activeTab = activeTabRef.current;
var rootNode = rootRef.current;
if (activeTab && rootNode) {
var wrapNode = navRef.current || rootNode;
var transformSupported = isTransformSupported(inkBarNodeStyle);
if (!isVertical(tabBarPosition)) {
var left = getLeft(activeTab, wrapNode);
var width = activeTab.offsetWidth; // If tabNode'width width equal to wrapNode'width when tabBarPosition is top or bottom
// It means no css working, then ink bar should not have width until css is loaded
if (width === rootNode.offsetWidth) {
width = 0;
} else if (inkBarStyle && inkBarStyle.width !== undefined) {
width = parseFloat(inkBarStyle.width);
if (width) {
left += (activeTab.offsetWidth - width) / 2;
}
} // use 3d gpu to optimize render
if (transformSupported) {
setTransform(inkBarNodeStyle, "translate3d(".concat(left, "px,0,0)"));
inkBarNodeStyle.width = "".concat(width, "px");
inkBarNodeStyle.height = '';
} else {
inkBarNodeStyle.left = "".concat(left, "px");
inkBarNodeStyle.top = '';
inkBarNodeStyle.bottom = '';
inkBarNodeStyle.right = "".concat(wrapNode.offsetWidth - left - width, "px");
}
} else {
var top = getTop(activeTab, wrapNode);
var height = activeTab.offsetHeight;
if (inkBarStyle && inkBarStyle.height !== undefined) {
height = parseFloat(inkBarStyle.height);
if (height) {
top += (activeTab.offsetHeight - height) / 2;
}
}
if (transformSupported) {
setTransform(inkBarNodeStyle, "translate3d(0,".concat(top, "px,0)"));
inkBarNodeStyle.height = "".concat(height, "px");
inkBarNodeStyle.width = '';
} else {
inkBarNodeStyle.left = '';
inkBarNodeStyle.right = '';
inkBarNodeStyle.top = "".concat(top, "px");
inkBarNodeStyle.bottom = "".concat(wrapNode.offsetHeight - top - height, "px");
}
}
}
inkBarNodeStyle.visibility = activeTab ? 'visible' : 'hidden';
}
});
useEffect(function () {
setOffset(0, setNextPrev);
}, [tabBarPosition]);
useLayoutEffect(function () {
var currentNextPrev = {
prev: prev,
next: next
};
var nextPrev = setNextPrev(); // wait next, prev show hide
/* eslint react/no-did-update-set-state:0 */
if (isNextPrevShown(currentNextPrev) !== isNextPrevShown(nextPrev)) {
scrollToActiveTab();
} else if (activeKey !== prevActiveKey) {
setActiveKey(activeKey); // can not use props.activeKey
scrollToActiveTab();
}
}, [setNextPrev, isNextPrevShown, prev, next, activeKey, prevActiveKey]);
useEffect(function () {
var debouncedResize = debounce(function () {
setNextPrev();
scrollToActiveTab();
}, 200);
resizeEvent.addEventListener('resize', debouncedResize);
return function () {
resizeEvent.removeEventListener('resize', debouncedResize);
debouncedResize.cancel();
};
}, [setNextPrev, scrollToActiveTab, resizeEvent]);
var inkBarNode = getInkBarNode();
var tabs = getTabs();
var groupNode = getGroupNode();
var scrollbarNode = getScrollBarNode([inkBarNode, tabs]);
return React.createElement("div", _extends({
role: "tablist",
className: classnames("".concat(prefixCls, "-bar"), _defineProperty({}, "".concat(prefixCls, "-bar-with-groups"), groupNode), className),
tabIndex: 0,
ref: rootRef,
onKeyDown: handleKeyDown,
style: style
}, getDataAttr(restProps)), React.createElement("div", {
className: "".concat(prefixCls, "-bar-inner")
}, groupNode, groupNode && React.createElement("div", {
className: "".concat(prefixCls, "-bar-divider")
}), getContent(scrollbarNode)));
};
TabBar.displayName = 'TabBar';
TabBar.defaultProps = {
inkBarAnimated: true,
scrollAnimated: true
};
export default TabBar;
//# sourceMappingURL=TabBar.js.map