123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- <script>
- import TabBar from './tab-bar';
- import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
- function noop() {}
- const firstUpperCase = str => {
- return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
- };
- export default {
- name: 'TabNav',
- components: {
- TabBar
- },
- inject: ['rootTabs'],
- props: {
- panes: Array,
- currentName: String,
- editable: Boolean,
- onTabClick: {
- type: Function,
- default: noop
- },
- onTabRemove: {
- type: Function,
- default: noop
- },
- type: String,
- stretch: Boolean
- },
- data() {
- return {
- scrollable: false,
- navOffset: 0,
- isFocus: false,
- focusable: true
- };
- },
- computed: {
- navStyle() {
- const dir = ['top', 'bottom'].indexOf(this.rootTabs.tabPosition) !== -1 ? 'X' : 'Y';
- return {
- transform: `translate${dir}(-${this.navOffset}px)`
- };
- },
- sizeName() {
- return ['top', 'bottom'].indexOf(this.rootTabs.tabPosition) !== -1 ? 'width' : 'height';
- }
- },
- methods: {
- scrollPrev() {
- const containerSize = this.$refs.navScroll[`offset${firstUpperCase(this.sizeName)}`];
- const currentOffset = this.navOffset;
- if (!currentOffset) return;
- let newOffset = currentOffset > containerSize
- ? currentOffset - containerSize
- : 0;
- this.navOffset = newOffset;
- },
- scrollNext() {
- const navSize = this.$refs.nav[`offset${firstUpperCase(this.sizeName)}`];
- const containerSize = this.$refs.navScroll[`offset${firstUpperCase(this.sizeName)}`];
- const currentOffset = this.navOffset;
- if (navSize - currentOffset <= containerSize) return;
- let newOffset = navSize - currentOffset > containerSize * 2
- ? currentOffset + containerSize
- : (navSize - containerSize);
- this.navOffset = newOffset;
- },
- scrollToActiveTab() {
- if (!this.scrollable) return;
- const nav = this.$refs.nav;
- const activeTab = this.$el.querySelector('.is-active');
- if (!activeTab) return;
- const navScroll = this.$refs.navScroll;
- const isHorizontal = ['top', 'bottom'].indexOf(this.rootTabs.tabPosition) !== -1;
- const activeTabBounding = activeTab.getBoundingClientRect();
- const navScrollBounding = navScroll.getBoundingClientRect();
- const maxOffset = isHorizontal
- ? nav.offsetWidth - navScrollBounding.width
- : nav.offsetHeight - navScrollBounding.height;
- const currentOffset = this.navOffset;
- let newOffset = currentOffset;
- if (isHorizontal) {
- if (activeTabBounding.left < navScrollBounding.left) {
- newOffset = currentOffset - (navScrollBounding.left - activeTabBounding.left);
- }
- if (activeTabBounding.right > navScrollBounding.right) {
- newOffset = currentOffset + activeTabBounding.right - navScrollBounding.right;
- }
- } else {
- if (activeTabBounding.top < navScrollBounding.top) {
- newOffset = currentOffset - (navScrollBounding.top - activeTabBounding.top);
- }
- if (activeTabBounding.bottom > navScrollBounding.bottom) {
- newOffset = currentOffset + (activeTabBounding.bottom - navScrollBounding.bottom);
- }
- }
- newOffset = Math.max(newOffset, 0);
- this.navOffset = Math.min(newOffset, maxOffset);
- },
- update() {
- if (!this.$refs.nav) return;
- const sizeName = this.sizeName;
- const navSize = this.$refs.nav[`offset${firstUpperCase(sizeName)}`];
- const containerSize = this.$refs.navScroll[`offset${firstUpperCase(sizeName)}`];
- const currentOffset = this.navOffset;
- if (containerSize < navSize) {
- const currentOffset = this.navOffset;
- this.scrollable = this.scrollable || {};
- this.scrollable.prev = currentOffset;
- this.scrollable.next = currentOffset + containerSize < navSize;
- if (navSize - currentOffset < containerSize) {
- this.navOffset = navSize - containerSize;
- }
- } else {
- this.scrollable = false;
- if (currentOffset > 0) {
- this.navOffset = 0;
- }
- }
- },
- changeTab(e) {
- const keyCode = e.keyCode;
- let nextIndex;
- let currentIndex, tabList;
- if ([37, 38, 39, 40].indexOf(keyCode) !== -1) { // 左右上下键更换tab
- tabList = e.currentTarget.querySelectorAll('[role=tab]');
- currentIndex = Array.prototype.indexOf.call(tabList, e.target);
- } else {
- return;
- }
- if (keyCode === 37 || keyCode === 38) { // left
- if (currentIndex === 0) { // first
- nextIndex = tabList.length - 1;
- } else {
- nextIndex = currentIndex - 1;
- }
- } else { // right
- if (currentIndex < tabList.length - 1) { // not last
- nextIndex = currentIndex + 1;
- } else {
- nextIndex = 0;
- }
- }
- tabList[nextIndex].focus(); // 改变焦点元素
- tabList[nextIndex].click(); // 选中下一个tab
- this.setFocus();
- },
- setFocus() {
- if (this.focusable) {
- this.isFocus = true;
- }
- },
- removeFocus() {
- this.isFocus = false;
- },
- visibilityChangeHandler() {
- const visibility = document.visibilityState;
- if (visibility === 'hidden') {
- this.focusable = false;
- } else if (visibility === 'visible') {
- setTimeout(() => {
- this.focusable = true;
- }, 50);
- }
- },
- windowBlurHandler() {
- this.focusable = false;
- },
- windowFocusHandler() {
- setTimeout(() => {
- this.focusable = true;
- }, 50);
- }
- },
- updated() {
- this.update();
- },
- render(h) {
- const {
- type,
- panes,
- editable,
- stretch,
- onTabClick,
- onTabRemove,
- navStyle,
- scrollable,
- scrollNext,
- scrollPrev,
- changeTab,
- setFocus,
- removeFocus
- } = this;
- const scrollBtn = scrollable
- ? [
- <span class={['el-tabs__nav-prev', scrollable.prev ? '' : 'is-disabled']} on-click={scrollPrev}><i class="el-icon-arrow-left"></i></span>,
- <span class={['el-tabs__nav-next', scrollable.next ? '' : 'is-disabled']} on-click={scrollNext}><i class="el-icon-arrow-right"></i></span>
- ] : null;
- const tabs = this._l(panes, (pane, index) => {
- let tabName = pane.name || pane.index || index;
- const closable = pane.isClosable || editable;
- pane.index = `${index}`;
- const btnClose = closable
- ? <span class="el-icon-close" on-click={(ev) => { onTabRemove(pane, ev); }}></span>
- : null;
- const tabLabelContent = pane.$slots.label || pane.label;
- const tabindex = pane.active ? 0 : -1;
- return (
- <div
- class={{
- 'el-tabs__item': true,
- [`is-${ this.rootTabs.tabPosition }`]: true,
- 'is-active': pane.active,
- 'is-disabled': pane.disabled,
- 'is-closable': closable,
- 'is-focus': this.isFocus
- }}
- id={`tab-${tabName}`}
- key={`tab-${tabName}`}
- aria-controls={`pane-${tabName}`}
- role="tab"
- aria-selected={ pane.active }
- ref="tabs"
- tabindex={tabindex}
- refInFor
- on-focus={ ()=> { setFocus(); }}
- on-blur ={ ()=> { removeFocus(); }}
- on-click={(ev) => { removeFocus(); onTabClick(pane, tabName, ev); }}
- on-keydown={(ev) => { if (closable && (ev.keyCode === 46 || ev.keyCode === 8)) { onTabRemove(pane, ev);} }}
- >
- {tabLabelContent}
- {btnClose}
- </div>
- );
- });
- return (
- <div class={['el-tabs__nav-wrap', scrollable ? 'is-scrollable' : '', `is-${ this.rootTabs.tabPosition }`]}>
- {scrollBtn}
- <div class={['el-tabs__nav-scroll']} ref="navScroll">
- <div
- class={['el-tabs__nav', `is-${ this.rootTabs.tabPosition }`, stretch && ['top', 'bottom'].indexOf(this.rootTabs.tabPosition) !== -1 ? 'is-stretch' : '']}
- ref="nav"
- style={navStyle}
- role="tablist"
- on-keydown={ changeTab }
- >
- {!type ? <tab-bar tabs={panes}></tab-bar> : null}
- {tabs}
- </div>
- </div>
- </div>
- );
- },
- mounted() {
- addResizeListener(this.$el, this.update);
- document.addEventListener('visibilitychange', this.visibilityChangeHandler);
- window.addEventListener('blur', this.windowBlurHandler);
- window.addEventListener('focus', this.windowFocusHandler);
- setTimeout(() => {
- this.scrollToActiveTab();
- }, 0);
- },
- beforeDestroy() {
- if (this.$el && this.update) removeResizeListener(this.$el, this.update);
- document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
- window.removeEventListener('blur', this.windowBlurHandler);
- window.removeEventListener('focus', this.windowFocusHandler);
- }
- };
- </script>
|