UNPKG

rabbit-simple-ui

Version:

A simple UI component library based on JavaScript

276 lines (227 loc) 9.73 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { $el, bind, removeAttrs, siblings, slider } from '../../dom-utils'; import { getArrTypeAttr, getBooleanTypeAttr, getStrTypeAttr, setCss, setHtml } from '../../dom-utils/elem'; import { type, validComps } from '../../utils'; import PREFIX from '../prefix'; interface CollapseEvents { onChange?: (index: []) => void; } interface Config { config( el: string ): { activeIndex: string | number | string[] | number[]; events({ onChange }: CollapseEvents): void; }; } class Collapse implements Config { readonly VERSION: string; readonly COMPONENTS: NodeListOf<Element>; constructor() { this.VERSION = 'v1.0'; this.COMPONENTS = $el('r-collapse', { all: true }); this._create(this.COMPONENTS); } public config( el: string ): { activeIndex: string | number | string[] | number[]; events({ onChange }: { onChange: (key: []) => void }): void; } { const target = $el(el); validComps(target, 'collapse'); const { _attrs, _dataSetActiveKey, _openByKey } = Collapse.prototype; const activeIndex = JSON.parse(target.dataset['activeIndex']); return { get activeIndex() { return activeIndex; }, set activeIndex(newVal: string | number | string[] | number[]) { if (newVal == activeIndex) return; _dataSetActiveKey(target, newVal); _openByKey(target, newVal, target.getAttribute('accordion')); }, events({ onChange }) { const panels = target.querySelectorAll('r-collapse-panel'); // 储存已展开面板的 key const key: string[] = []; const pushKey = (el: Element, toggle: boolean) => { const { accordion } = _attrs(target); // @ts-ignore const panelKey = el.dataset.panelKey; if (el.classList.contains(`${PREFIX.collapse}-item-active`)) { key.push(panelKey); } else if (toggle) { const idx = key.indexOf(panelKey); idx > -1 ? key.splice(idx, 1) : ''; } // 手风琴模式下每展开一个面板就删除其他的 key if (accordion) { siblings(el).forEach((o) => { const otherIdx = key.indexOf(o.dataset.panelKey); otherIdx > -1 ? key.splice(otherIdx, 1) : ''; }); } }; panels.forEach((panel: Element) => { const header = panel.querySelector(`.${PREFIX.collapse}-header`); // 存放初始化面板时默认已展开的 key pushKey(panel, false); bind(header, 'click', () => { // 这里用定时器是为了跟移除显示面板样式类名的时机同步 setTimeout(() => { pushKey(panel, true); onChange && type.isFn(onChange, key); }, 150); }); }); } }; } private _create(COMPONENTS: NodeListOf<Element>): void { COMPONENTS.forEach((node) => { const { simple, ghost, activekey, accordion } = this._attrs(node); this._dataSetActiveKey(node, activekey); this._setGhost(node, ghost); this._setSimple(node, simple); this._createChildNodes(node); this._openByKey(node, activekey, accordion); removeAttrs(node, ['simple', 'ghost', 'active-key']); }); } private _dataSetActiveKey( node: Element, activeIndex: string | number | string[] | number[] ): void { if (activeIndex) { // @ts-ignore node.dataset['activeIndex'] = Array.isArray(activeIndex) ? `[${activeIndex}]` : activeIndex; } } private _setGhost(node: Element, ghost: boolean): void { ghost ? node.classList.add(`${PREFIX.collapse}-ghost`) : ''; } private _setSimple(node: Element, simple: boolean): void { simple ? node.classList.add(`${PREFIX.collapse}-simple`) : ''; } private _createChildNodes(node: Element): void { const collapsePanels = node.querySelectorAll('r-collapse-panel'); this._setPanel(node, collapsePanels); } private _setPanel(parent: Element, panels: NodeListOf<Element>): void { // 遍历所有面板节点 panels.forEach((panel, index) => { const { index: key, title, hideArrow } = this._attrs(panel); // @ts-ignore // 面板的 key 如果没填则默认为索引值 panel.dataset.panelKey = !key ? index : key; // 保存面板原先的第一个节点,也就是要填充的内容 const content = panel.firstElementChild; const arrowIcon = `<i class="${PREFIX.icon} ${PREFIX.icon}-ios-arrow-forward"></i>`; const template = ` <div class="${PREFIX.collapse}-header"> ${!hideArrow ? arrowIcon : ''} ${title} </div> <div class="${PREFIX.collapse}-content"> <div class="${PREFIX.collapse}-content-box"></div> </div>`; // 清空面板原先的所有内容 setHtml(panel, ''); // 追加html模板 setHtml(panel, template); // 将原先的占位内容填充至面板内容盒子 const panelContentBox = panel.querySelector(`.${PREFIX.collapse}-content-box`); content ? panelContentBox?.appendChild(content) : null; setCss(panel, 'display', 'block'); this._handleToggle(parent, panel); removeAttrs(panel, ['index', 'title', 'hide-arrow']); }); } private _handleToggle(parent: Element, panel: Element): void { const { accordion } = this._attrs(parent); const collapseHeader = panel.querySelector(`.${PREFIX.collapse}-header`); const collapseContent = panel.querySelector(`.${PREFIX.collapse}-content`); bind(collapseHeader, 'click', () => this._slide(panel, collapseContent!, accordion)); } private _slide(p: Element, c: Element, accordion: boolean): void { const activeCls = `${PREFIX.collapse}-item-active`; const slideUp = (arg1: Element, arg2: Element) => { slider.slideUp(arg2, 150); setTimeout(() => { arg1.classList.remove(activeCls); }, 150); }; if (p.classList.contains(activeCls)) { slideUp(p, c); } else { slider.slideDown(c, 150); p.classList.add(activeCls); } // 手风琴模式 if (accordion) { // 获取除了已展开的面板外的所有其他面板 siblings(p).forEach((otherP) => { const otherC = otherP.querySelector(`.${PREFIX.collapse}-content`); slideUp(otherP, otherC); }); } } private _openByKey( node: Element, key: string | number | string[] | number[], accordion: boolean ): void { // 获取选中的 key 的面板 let selected: Element | null; const open = () => { if (selected) { selected.classList.add(`${PREFIX.collapse}-item-active`); if (accordion) { siblings(selected).forEach((o) => { o.classList.remove(`${PREFIX.collapse}-item-active`); }); } } }; // 如果 activeIndex 是数组则对其进行遍历获取所有面板 // 且如果是手风琴模式则只选取数组的第一项,只展开一个面板 if (Array.isArray(key)) { const { length } = key; // length == 1 说明数组只有一项所以不必对其进行遍历 if (accordion || length == 1) { selected = node.querySelector(`[data-panel-key="${key[0]}"]`); open(); } else { let i = 0; for (; i < length; i++) { selected = node.querySelector(`[data-panel-key="${key[i]}"]`); open(); } } } else { selected = node.querySelector(`[data-panel-key="${key}"]`); open(); } } private _attrs(node: Element) { return { index: getStrTypeAttr(node, 'index', ''), title: getStrTypeAttr(node, 'title', ''), ghost: getBooleanTypeAttr(node, 'ghost'), simple: getBooleanTypeAttr(node, 'simple'), hideArrow: getBooleanTypeAttr(node, 'hide-arrow'), accordion: getBooleanTypeAttr(node, 'accordion'), activekey: getStrTypeAttr(node, 'active-index', '') && getArrTypeAttr(node, 'active-index') }; } } export default Collapse;