UNPKG

ivue-material-plus

Version:

A high quality UI components Library with Vue.js

669 lines (560 loc) • 14.5 kB
import { createPopper } from '@popperjs/core'; import { get } from 'lodash-unified'; import { useZIndex } from '../../utils/helpers'; import { throwError } from '../../utils/error'; // type import type { TableColumnCtx } from './table-column/defaults'; import type { Store } from './store'; // 是否是对象 const isObject = function (obj: unknown): boolean { return obj !== null && typeof obj === 'object'; }; // 获取数组的key export const getKeysMap = function ( array: any[], rowKey: string ): Record<string, { row: any; index: number }> { const arrayMap = {}; // arrayMap (array || []).forEach((row, index) => { // 获取rowKey对应的数据 arrayMap[getRowIdentity(row, rowKey)] = { row, index }; }); return arrayMap; }; // 获取rowKey对应的数据 export const getRowIdentity = (row, rowKey): string => { if (!row) { throwError('ivue-table', 'row is required when get row identity'); } // 字符串 if (typeof rowKey === 'string') { // 分割字符串成数组 if (!rowKey.includes('.')) { return `${row[rowKey]}`; } const key = rowKey.split('.'); let current = row; for (const element of key) { current = current[element]; } return `${current}`; } // 函数 else if (typeof rowKey === 'function') { return rowKey.call(null, row); } return ''; }; // 宽度判断 export function parseWidth(width: number | string): number | string { // 空字符串 if (width === '') { return width; } // 没有宽度 if (width !== undefined) { width = Number.parseInt(width as string, 10); if (Number.isNaN(width)) { width = ''; } } return width; } // 最小宽度判断 export function parseMinWidth(minWidth: number | string): number | string { // 空字符串 if (minWidth === '') { return minWidth; } // 没有宽度 if (minWidth !== undefined) { minWidth = parseWidth(minWidth); if (Number.isNaN(minWidth)) { minWidth = 80; } } return minWidth; } // 固定列样式 export const getFixedColumnsClass = ( namespace: string, index: number, fixed: string | boolean, store: any, columns?: TableColumnCtx[] ) => { const classes: string[] = []; const { direction, start } = isFixedColumn(index, fixed, store, columns); // 有方向 if (direction) { const isLeft = direction === 'left'; // 方向 classes.push(`${namespace}-fixed-column--${direction}`); // 左边固定列 if (isLeft && start === store.states.fixedLeafColumnsLength.value - 1) { classes.push('is-last-column'); } // 其他 else if ( !isLeft && start === store.states.columns.value.length - store.states.rightFixedLeafColumnsLength.value ) { classes.push('is-first-column'); } } return classes; }; // 是否是固定列 export const isFixedColumn = ( index: number, fixed: string | boolean, store: any, realColumns?: TableColumnCtx[] ) => { let start = 0; let after = index; // 有列 if (realColumns) { // 判断是否是组合列 if (realColumns[index].colSpan > 1) { return {}; } for (let i = 0; i < index; i++) { start += realColumns[i].colSpan; } after = start + realColumns[index].colSpan - 1; } // 没有列 else { start = index; } let fixedLayout; const columns = store.states.columns; switch (fixed) { // 左固定列 case 'left': if (after < store.states.fixedLeafColumnsLength.value) { fixedLayout = 'left'; } break; // 右固定列 case 'right': if ( start >= columns.value.length - store.states.rightFixedLeafColumnsLength.value ) { fixedLayout = 'right'; } break; // 固定列默认值 default: if (after < store.states.fixedLeafColumnsLength.value) { fixedLayout = 'left'; } else if ( start >= columns.value.length - store.states.rightFixedLeafColumnsLength.value ) { fixedLayout = 'right'; } } return fixedLayout ? { direction: fixedLayout, start, after, } : {}; }; // 固定列偏移位置 export const getFixedColumnOffset = ( index: number, fixed: string | boolean, store: Store, realColumns?: TableColumnCtx[] ) => { const { direction, start = 0 } = isFixedColumn( index, fixed, store, realColumns ); // 没有方向 if (!direction) { return; } // styles const styles: { left?: string; right?: string; } = {}; // 左边 const isLeft = direction === 'left'; const columns = store.states.columns.value; // 左边 if (isLeft) { styles.left = columns.slice(0, index).reduce(getOffset, 0); } // 其他 else { styles.right = columns .slice(start + 1) .reverse() .reduce(getOffset, 0); } return styles; }; // 偏移的位置 function getOffset(offset: number, column: TableColumnCtx) { return ( offset + (column.columnWidth === null || Number.isNaN(column.columnWidth) ? Number(column.width) : column.columnWidth) ); } // 设置定位位置 export const ensurePosition = (style: Record<any, string>, key: string) => { if (!style) { return; } if (!Number.isNaN(style[key])) { style[key] = `${style[key]}px`; } }; // 修改当前行的状态 export function toggleRowStatus( statusArr: any[], row: any, newVal: boolean ): boolean { let changed = false; // 当前选择的行列表中是否存在当前行 const index = statusArr.indexOf(row); const included = index !== -1; // 添加行 const addRow = () => { statusArr.push(row); changed = true; }; // 删除行 const removeRow = () => { statusArr.splice(index, 1); changed = true; }; // boolean if (typeof newVal === 'boolean') { // 添加行 if (newVal && !included) { addRow(); } // 删除行 else if (!newVal && included) { removeRow(); } } // 不是 boolean else { if (included) { removeRow(); } else { addRow(); } } return changed; } // 获取当前单元格元素 export const getCell = (event: Event) => { return (event.target as HTMLElement)?.closest('td'); }; // 单元格 export const getColumnByCell = ( table: { columns: TableColumnCtx[]; }, cell: HTMLElement, namespace: string ): null | TableColumnCtx => { // 获取列id const matches = (cell.className || '').match( new RegExp(`${namespace}-[^\\s]+`, 'gm') ); // ['ivue-table-1-column-3', 'ivue-table-cell'] // 是否有列id if (matches) { return getColumnById(table, matches[0]); } return null; }; // 通过id寻找对应的列数据 export const getColumnById = ( // 列数组 table: { columns: TableColumnCtx[]; }, // 列id columnId: string ): null | TableColumnCtx => { let column: TableColumnCtx = null; // 根据id获取当前列 table.columns.forEach((item) => { // 是否相同 if (item.id === columnId) { column = item; } }); return column; }; // 通过key寻找对应的列数据 export const getColumnByKey = function ( table: { columns: TableColumnCtx[]; }, columnKey: string ): TableColumnCtx { let column: TableColumnCtx = null; for (let i = 0; i < table.columns.length; i++) { const item = table.columns[i]; // 是否有相同的key->对应的列 if (item.columnKey === columnKey) { column = item; break; } } // 没有设置columnKey if (!column) { throw 'column does not have columnKey set'; } return column; }; export let removePopper; // 创建pooper export const createTablePopper = ( parentNode: HTMLElement | undefined, trigger: HTMLElement, popperContent: string, tooltipTheme, popperOptions: any ) => { // 下一个zindex数值 const { nextZIndex } = useZIndex(); // 获取滚动dom const scrollContainer = parentNode?.querySelector('.ivue-scrollbar-wrapper'); // 渲染三角形 const renderArrow = (): HTMLDivElement => { // arrow const arrow = document.createElement('div'); arrow.className = 'ivue-tooltip-arrow'; return arrow; }; // 渲染三角形 const arrow = renderArrow(); // 渲染内容 const renderContent = (): HTMLDivElement => { // wrapper const wrapper = document.createElement('div'); wrapper.className = `ivue-tooltip-popper ivue-tooltip-${tooltipTheme}`; wrapper.style.zIndex = String(nextZIndex()); // content const content = document.createElement('div'); content.className = 'ivue-tooltip-content'; // 文本内容 const inner = document.createElement('div'); inner.className = 'ivue-tooltip-inner'; inner.innerHTML = popperContent; // arrow content.appendChild(arrow); // inner content.appendChild(inner); wrapper.appendChild(content); // Avoid side effects caused by append to body parentNode?.appendChild(wrapper); return wrapper; }; // 渲染内容 const content = renderContent(); let popperInstance = null; popperInstance = createPopper(trigger, content, { strategy: 'absolute', modifiers: [ { name: 'offset', options: { offset: [0, 0], }, }, { name: 'arrow', options: { element: arrow, padding: 10, }, }, ], ...popperOptions, }); // 显示popper const showPopper = () => { popperInstance && popperInstance.update(); }; // 删除popper removePopper = () => { // 销毁popper popperInstance && popperInstance.destroy(); // 删除节点 content && parentNode && parentNode?.removeChild(content); // 删除事件 trigger.removeEventListener('mouseenter', showPopper); trigger.removeEventListener('mouseleave', removePopper); scrollContainer?.removeEventListener('scroll', removePopper); removePopper = undefined; }; // 当前trigger移动 trigger.addEventListener('mouseenter', showPopper); trigger.addEventListener('mouseleave', removePopper); scrollContainer?.addEventListener('scroll', removePopper); return popperInstance; }; // 排序数据 export const orderBy = ( array: any[], sortKey: string, reverse: string | number, sortMethod, sortBy: string | (string | ((a: any, b: any, array?: any[]) => number))[] ) => { // 没有排序的key 对应列内容的字段名 // 没有自定义排序方法 // 没有指定数据按照哪个属性进行排序 if ( !sortKey && !sortMethod && (!sortBy || (Array.isArray(sortBy) && !sortBy.length)) ) { return array; } // string if (typeof reverse === 'string') { reverse = reverse === 'descending' ? -1 : 1; } else { reverse = reverse && reverse < 0 ? -1 : 1; } // 获取key let getKey = null; // 没有自定义排序方法 if (!sortMethod) { getKey = (value, index) => { // 指定数据按照哪个属性进行排序 if (sortBy) { if (!Array.isArray(sortBy)) { sortBy = [sortBy]; } return sortBy.map((by) => { // string if (typeof by === 'string') { // 获取对象中的属性 return get(value, by); } // 函数 else { return by(value, index, array); } }); } // 排序的key对应列内容的字段名 if (sortKey !== '$key') { if (isObject(value) && '$value' in value) { value = value.$value; } } // 对象 ?对象值 | 当前值 return [isObject(value) ? get(value, sortKey) : value]; }; } // 排序方法 const sort = (a, b) => { // 是否有自定义排序方法 if (sortMethod) { return sortMethod(a.value, b.value); } for (let i = 0, length = a.key.length; i < length; i++) { // 第一个小于第二个 if (a.key[i] < b.key[i]) { return -1; } // 第一个大于第二个 if (a.key[i] > b.key[i]) { return 1; } } // 两个相等返回 0 return 0; }; return ( array .map((value, index) => { return { value, index, // 当前行的数据 date key: getKey ? getKey(value, index) : null, }; }) // 排序 .sort((a, b) => { let order = sort(a, b); // 是否需要排序 if (!order) { // make stable https://en.wikipedia.org/wiki/Sorting_algorithm#Stability order = a.index - b.index; } return order * +reverse; }) // 返回当前行值 .map((item) => item.value) ); }; // 行树节点 export function walkTreeNode( root, cb, childrenKey = 'children', lazyKey = 'hasChildren' ) { // 是否有值 const isNull = (array) => !(Array.isArray(array) && array.length); // 递归方法检查是否还有嵌套的数据 function walker(parent, children, level) { cb(parent, children, level); children.forEach((item) => { // 懒加载子节点数据 if (item[lazyKey]) { cb(item, null, level + 1); return; } // 子节点数据 const children = item[childrenKey]; // 是否有值 if (!isNull(children)) { walker(item, children, level + 1); } }); } // 原始数据 root.forEach((item) => { // 懒加载子节点数据 if (item[lazyKey]) { cb(item, null, 0); return; } // 子节点数据 const children = item[childrenKey]; // 是否有值 if (!isNull(children)) { // 递归执行 walker(item, children, 0); } }); }