UNPKG

mdui

Version:

a CSS Framework based on material design

373 lines (315 loc) 8.99 kB
import $ from 'mdui.jq/es/$'; import extend from 'mdui.jq/es/functions/extend'; import { JQ } from 'mdui.jq/es/JQ'; import 'mdui.jq/es/methods/addClass'; import 'mdui.jq/es/methods/appendTo'; import 'mdui.jq/es/methods/attr'; import 'mdui.jq/es/methods/children'; import 'mdui.jq/es/methods/css'; import 'mdui.jq/es/methods/each'; import 'mdui.jq/es/methods/eq'; import 'mdui.jq/es/methods/first'; import 'mdui.jq/es/methods/get'; import 'mdui.jq/es/methods/hasClass'; import 'mdui.jq/es/methods/hide'; import 'mdui.jq/es/methods/index'; import 'mdui.jq/es/methods/innerWidth'; import 'mdui.jq/es/methods/offset'; import 'mdui.jq/es/methods/on'; import 'mdui.jq/es/methods/removeClass'; import 'mdui.jq/es/methods/show'; import Selector from 'mdui.jq/es/types/Selector'; import { isNumber } from 'mdui.jq/es/utils'; import mdui from '../../mdui'; import '../../jq_extends/static/throttle'; import { componentEvent } from '../../utils/componentEvent'; import { $window } from '../../utils/dom'; declare module '../../interfaces/MduiStatic' { interface MduiStatic { /** * Tab 选项卡组件 * * 请通过 `new mdui.Tab()` 调用 */ Tab: { /** * 实例化 Tab 组件 * @param selector CSS 选择器、或 DOM 元素、或 JQ 对象 * @param options 配置参数 */ new ( selector: Selector | HTMLElement | ArrayLike<HTMLElement>, options?: OPTIONS, ): Tab; }; } } type OPTIONS = { /** * 切换选项卡的触发方式。`click`: 点击切换;`hover`: 鼠标悬浮切换 */ trigger?: 'click' | 'hover'; /** * 是否启用循环切换,若为 `true`,则最后一个选项激活时调用 `next` 方法将回到第一个选项,第一个选项激活时调用 `prev` 方法将回到最后一个选项。 */ loop?: boolean; }; type EVENT = 'change' | 'show'; const DEFAULT_OPTIONS: OPTIONS = { trigger: 'click', loop: false, }; class Tab { /** * tab 元素的 JQ 对象 */ public $element: JQ; /** * 配置参数 */ public options: OPTIONS = extend({}, DEFAULT_OPTIONS); /** * 当前激活的 tab 的索引号。为 -1 时表示没有激活的选项卡,或不存在选项卡 */ public activeIndex = -1; /** * 选项数组 JQ 对象 */ private $tabs: JQ; /** * 激活状态的 tab 底部的指示符 */ private $indicator: JQ; public constructor( selector: Selector | HTMLElement | ArrayLike<HTMLElement>, options: OPTIONS = {}, ) { this.$element = $(selector).first(); extend(this.options, options); this.$tabs = this.$element.children('a'); this.$indicator = $('<div class="mdui-tab-indicator"></div>').appendTo( this.$element, ); // 根据 url hash 获取默认激活的选项卡 const hash = window.location.hash; if (hash) { this.$tabs.each((index, tab) => { if ($(tab).attr('href') === hash) { this.activeIndex = index; return false; } return true; }); } // 含 .mdui-tab-active 的元素默认激活 if (this.activeIndex === -1) { this.$tabs.each((index, tab) => { if ($(tab).hasClass('mdui-tab-active')) { this.activeIndex = index; return false; } return true; }); } // 存在选项卡时,默认激活第一个选项卡 if (this.$tabs.length && this.activeIndex === -1) { this.activeIndex = 0; } // 设置激活状态选项卡 this.setActive(); // 监听窗口大小变化事件,调整指示器位置 $window.on( 'resize', $.throttle(() => this.setIndicatorPosition(), 100), ); // 监听点击选项卡事件 this.$tabs.each((_, tab) => { this.bindTabEvent(tab); }); } /** * 指定选项卡是否已禁用 * @param $tab */ private isDisabled($tab: JQ): boolean { return $tab.attr('disabled') !== undefined; } /** * 绑定在 Tab 上点击或悬浮的事件 * @param tab */ private bindTabEvent(tab: HTMLElement): void { const $tab = $(tab); // 点击或鼠标移入触发的事件 const clickEvent = (): void | false => { // 禁用状态的选项卡无法选中 if (this.isDisabled($tab)) { return false; } this.activeIndex = this.$tabs.index(tab); this.setActive(); }; // 无论 trigger 是 click 还是 hover,都会响应 click 事件 $tab.on('click', clickEvent); // trigger 为 hover 时,额外响应 mouseenter 事件 if (this.options.trigger === 'hover') { $tab.on('mouseenter', clickEvent); } // 阻止链接的默认点击动作 $tab.on('click', (): void | false => { if (($tab.attr('href') || '').indexOf('#') === 0) { return false; } }); } /** * 触发组件事件 * @param name * @param $element * @param parameters */ private triggerEvent(name: EVENT, $element: JQ, parameters = {}): void { componentEvent(name, 'tab', $element, this, parameters); } /** * 设置激活状态的选项卡 */ private setActive(): void { this.$tabs.each((index, tab) => { const $tab = $(tab); const targetId = $tab.attr('href') || ''; // 设置选项卡激活状态 if (index === this.activeIndex && !this.isDisabled($tab)) { if (!$tab.hasClass('mdui-tab-active')) { this.triggerEvent('change', this.$element, { index: this.activeIndex, id: targetId.substr(1), }); this.triggerEvent('show', $tab); $tab.addClass('mdui-tab-active'); } $(targetId).show(); this.setIndicatorPosition(); } else { $tab.removeClass('mdui-tab-active'); $(targetId).hide(); } }); } /** * 设置选项卡指示器的位置 */ private setIndicatorPosition(): void { // 选项卡数量为 0 时,不显示指示器 if (this.activeIndex === -1) { this.$indicator.css({ left: 0, width: 0, }); return; } const $activeTab = this.$tabs.eq(this.activeIndex); if (this.isDisabled($activeTab)) { return; } const activeTabOffset = $activeTab.offset(); this.$indicator.css({ left: `${ activeTabOffset.left + this.$element[0].scrollLeft - this.$element[0].getBoundingClientRect().left }px`, width: `${$activeTab.innerWidth()}px`, }); } /** * 切换到下一个选项卡 */ public next(): void { if (this.activeIndex === -1) { return; } if (this.$tabs.length > this.activeIndex + 1) { this.activeIndex++; } else if (this.options.loop) { this.activeIndex = 0; } this.setActive(); } /** * 切换到上一个选项卡 */ public prev(): void { if (this.activeIndex === -1) { return; } if (this.activeIndex > 0) { this.activeIndex--; } else if (this.options.loop) { this.activeIndex = this.$tabs.length - 1; } this.setActive(); } /** * 显示指定索引号、或指定id的选项卡 * @param index 索引号、或id */ public show(index: number | string): void { if (this.activeIndex === -1) { return; } if (isNumber(index)) { this.activeIndex = index; } else { this.$tabs.each((i, tab): void | false => { if (tab.id === index) { this.activeIndex = i; return false; } }); } this.setActive(); } /** * 在父元素的宽度变化时,需要调用该方法重新调整指示器位置 * 在添加或删除选项卡时,需要调用该方法 */ public handleUpdate(): void { const $oldTabs = this.$tabs; // 旧的 tabs JQ对象 const $newTabs = this.$element.children('a'); // 新的 tabs JQ对象 const oldTabsElement = $oldTabs.get(); // 旧的 tabs 元素数组 const newTabsElement = $newTabs.get(); // 新的 tabs 元素数组 if (!$newTabs.length) { this.activeIndex = -1; this.$tabs = $newTabs; this.setIndicatorPosition(); return; } // 重新遍历选项卡,找出新增的选项卡 $newTabs.each((index, tab) => { // 有新增的选项卡 if (oldTabsElement.indexOf(tab) < 0) { this.bindTabEvent(tab); if (this.activeIndex === -1) { this.activeIndex = 0; } else if (index <= this.activeIndex) { this.activeIndex++; } } }); // 找出被移除的选项卡 $oldTabs.each((index, tab) => { // 有被移除的选项卡 if (newTabsElement.indexOf(tab) < 0) { if (index < this.activeIndex) { this.activeIndex--; } else if (index === this.activeIndex) { this.activeIndex = 0; } } }); this.$tabs = $newTabs; this.setActive(); } } mdui.Tab = Tab;