@fesjs/fes-design
Version:
fes-design for PC
175 lines (171 loc) • 6.54 kB
JavaScript
import { defineComponent, ref, computed, provide, toRef, watch, nextTick, createVNode, mergeProps, TransitionGroup } from 'vue';
import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties';
import { UPDATE_MODEL_EVENT, CHANGE_EVENT, CLOSE_EVENT } from '../_util/constants';
import getPrefixCls from '../_util/getPrefixCls';
import { useNormalModel } from '../_util/use/useModel';
import { flatten } from '../_util/vnode';
import { useTheme } from '../_theme/useTheme';
import PlusOutlined from '../icon/PlusOutlined';
import Scrollbar from '../scrollbar';
import { COMPONENT_NAME, ADD_EVENT, CLICK_TAB_EVENT, TABS_INJECTION_KEY } from './constants';
import { mapTabPane } from './helper';
import Tab from './tab';
import TabPane from './tab-pane.js';
import { tabsProps } from './props';
const _excluded = ["render", "renderTab"];
const prefixCls = getPrefixCls('tabs');
var tabs = defineComponent({
name: COMPONENT_NAME,
props: tabsProps,
emits: [UPDATE_MODEL_EVENT, CHANGE_EVENT, CLOSE_EVENT, ADD_EVENT, CLICK_TAB_EVENT],
setup(props, _ref) {
let {
emit,
slots
} = _ref;
useTheme();
const [currentValue, updateCurrentValue] = useNormalModel(props, emit);
const tabPaneLazyCache = {};
const tabRefs = ref([]);
const tabNavRef = ref(null);
const tabsLength = ref(0);
const isCard = computed(() => props.type === 'card');
const position = computed(() => isCard.value ? 'top' : props.position);
const setTabRefs = (el, index) => {
if (el) {
tabRefs.value[index] = el;
}
};
const handleTabClick = key => {
if (key !== currentValue.value) {
updateCurrentValue(key);
emit(CHANGE_EVENT, key);
}
emit(CLICK_TAB_EVENT, key);
};
const handleAddClick = event => {
emit(ADD_EVENT, event);
};
const handleClose = key => {
emit(CLOSE_EVENT, key);
};
const autoScrollTab = el => {
if (!tabNavRef.value || !el) {
return;
}
const {
scrollLeft,
scrollTop,
offsetWidth,
offsetHeight
} = tabNavRef.value.containerRef;
if (['top', 'bottom'].includes(props.position) && (scrollLeft + offsetWidth < el.offsetLeft + el.offsetWidth || el.offsetLeft < scrollLeft)) {
tabNavRef.value.setScrollLeft(el.offsetLeft - offsetWidth + el.offsetWidth, 0);
} else if (['left', 'right'].includes(props.position) && (scrollTop + offsetHeight < el.offsetTop + el.offsetHeight || el.offsetTop < scrollTop)) {
tabNavRef.value.setScrollTop(el.offsetTop - offsetHeight + el.offsetHeight, 0);
}
};
// 当没有默认值时,设置第一项为默认值,在Tab组件调用
const setDefaultValue = value => {
if (!currentValue.value && currentValue.value !== 0) {
updateCurrentValue(value);
}
};
provide(TABS_INJECTION_KEY, {
valueRef: currentValue,
closableRef: toRef(props, 'closable'),
closeModeRef: toRef(props, 'closeMode'),
isCard,
tabsLength,
handleTabClick,
handleClose,
setDefaultValue
});
watch(() => [currentValue.value, position.value], () => {
nextTick(() => {
const tab = tabRefs.value.find(item => item.value === currentValue.value);
autoScrollTab(tab === null || tab === void 0 ? void 0 : tab.$el);
});
}, {
immediate: true
});
const mergeRenderPanes = () => {
var _props$panes;
const children = slots.default && flatten(slots.default()).filter(vNode => vNode.type.name === 'FTabPane') || [];
if ((_props$panes = props.panes) !== null && _props$panes !== void 0 && _props$panes.length) {
return children.concat(props.panes.map(pane => {
const {
render,
renderTab
} = pane,
paneProps = _objectWithoutProperties(pane, _excluded);
if (!render) {
console.warn(`[${COMPONENT_NAME}]: panes 需要提供 render`);
}
const slots = {
default: () => render === null || render === void 0 ? void 0 : render(paneProps),
tab: renderTab ? () => renderTab(paneProps) : null
};
return createVNode(TabPane, mergeProps(paneProps, {
"value": paneProps.value
}), slots);
}));
}
return children;
};
return () => {
const children = mergeRenderPanes();
let navItems = children.map((vNode, index) => {
var _vNode$children;
const tabSlot = (_vNode$children = vNode.children) === null || _vNode$children === void 0 ? void 0 : _vNode$children.tab;
return createVNode(Tab, mergeProps(vNode.props, {
"ref": el => setTabRefs(el, index)
}), {
default: tabSlot
});
});
if (isCard.value) {
if (props.addable) {
navItems.push(createVNode("div", {
"onClick": handleAddClick,
"class": `${prefixCls}-tab ${prefixCls}-tab-card addable`
}, [createVNode(PlusOutlined, null, null)]));
}
// 添加 card pad
navItems = navItems.map((item, index) => [item, createVNode("div", {
"class": index !== navItems.length - 1 ? `${prefixCls}-tab-pad` : `${prefixCls}-tab-pad--last`
}, null)]).flat(1);
}
return createVNode("div", {
"class": {
[`${prefixCls}`]: true,
[`${prefixCls}-${position.value}`]: true,
[`${prefixCls}-card`]: isCard.value
}
}, [createVNode("div", {
"class": `${prefixCls}-nav`
}, [slots.prefix && createVNode("div", {
"class": `${prefixCls}-nav-prefix`
}, [slots.prefix()]), createVNode(Scrollbar, {
"ref": tabNavRef,
"class": `${prefixCls}-nav-scroll`,
"shadow": true
}, {
default: () => [createVNode("div", {
"class": `${prefixCls}-nav-scroll-content`
}, [navItems])]
}), slots.suffix && createVNode("div", {
"class": `${prefixCls}-nav-suffix`
}, [slots.suffix()])]), createVNode("div", {
"class": `${prefixCls}-tab-pane-wrapper`
}, [createVNode(TransitionGroup, {
"name": props.transition ? props.transition === true ? `${prefixCls}-slide-fade` : props.transition : null
}, {
default: () => [mapTabPane(mergeRenderPanes(),
// TODO: 待优化
currentValue.value, tabPaneLazyCache)]
})])]);
};
}
});
export { tabs as default };