<script> import TabNav from './tab-nav'; export default { name: 'ElTabs', components: { TabNav }, props: { type: String, activeName: String, closable: Boolean, addable: Boolean, value: {}, editable: Boolean, tabPosition: { type: String, default: 'top' }, beforeLeave: Function, stretch: Boolean }, provide() { return { rootTabs: this }; }, data() { return { currentName: this.value || this.activeName, panes: [] }; }, watch: { activeName(value) { this.setCurrentName(value); }, value(value) { this.setCurrentName(value); }, currentName(value) { if (this.$refs.nav) { this.$nextTick(() => { this.$refs.nav.$nextTick(_ => { this.$refs.nav.scrollToActiveTab(); }); }); } } }, methods: { calcPaneInstances(isForceUpdate = false) { if (this.$slots.default) { const paneSlots = this.$slots.default.filter(vnode => vnode.tag && vnode.componentOptions && vnode.componentOptions.Ctor.options.name === 'ElTabPane'); // update indeed const panes = paneSlots.map(({ componentInstance }) => componentInstance); const panesChanged = !(panes.length === this.panes.length && panes.every((pane, index) => pane === this.panes[index])); if (isForceUpdate || panesChanged) { this.panes = panes; } } else if (this.panes.length !== 0) { this.panes = []; } }, handleTabClick(tab, tabName, event) { if (tab.disabled) return; this.setCurrentName(tabName); this.$emit('tab-click', tab, event); }, handleTabRemove(pane, ev) { if (pane.disabled) return; ev.stopPropagation(); this.$emit('edit', pane.name, 'remove'); this.$emit('tab-remove', pane.name); }, handleTabAdd() { this.$emit('edit', null, 'add'); this.$emit('tab-add'); }, setCurrentName(value) { const changeCurrentName = () => { this.currentName = value; this.$emit('input', value); }; if (this.currentName !== value && this.beforeLeave) { const before = this.beforeLeave(value, this.currentName); if (before && before.then) { before .then(() => { changeCurrentName(); this.$refs.nav && this.$refs.nav.removeFocus(); }, () => { // https://github.com/ElemeFE/element/pull/14816 // ignore promise rejection in `before-leave` hook }); } else if (before !== false) { changeCurrentName(); } } else { changeCurrentName(); } } }, render(h) { let { type, handleTabClick, handleTabRemove, handleTabAdd, currentName, panes, editable, addable, tabPosition, stretch } = this; const newButton = editable || addable ? ( <span class="el-tabs__new-tab" on-click={ handleTabAdd } tabindex="0" on-keydown={ (ev) => { if (ev.keyCode === 13) { handleTabAdd(); }} } > <i class="el-icon-plus"></i> </span> ) : null; const navData = { props: { currentName, onTabClick: handleTabClick, onTabRemove: handleTabRemove, editable, type, panes, stretch }, ref: 'nav' }; const header = ( <div class={['el-tabs__header', `is-${tabPosition}`]}> {newButton} <tab-nav { ...navData }></tab-nav> </div> ); const panels = ( <div class="el-tabs__content"> {this.$slots.default} </div> ); return ( <div class={{ 'el-tabs': true, 'el-tabs--card': type === 'card', [`el-tabs--${tabPosition}`]: true, 'el-tabs--border-card': type === 'border-card' }}> { tabPosition !== 'bottom' ? [header, panels] : [panels, header] } </div> ); }, created() { if (!this.currentName) { this.setCurrentName('0'); } this.$on('tab-nav-update', this.calcPaneInstances.bind(null, true)); }, mounted() { this.calcPaneInstances(); }, updated() { this.calcPaneInstances(); } }; </script>