import { warning } from '../../vc-util/warning'; import type { ComputedRef, Ref } from 'vue'; import { computed, watchEffect } from 'vue'; import type { ColumnsType, ColumnType, FixedType, Key, GetRowKey, TriggerEventHandler, RenderExpandIcon, ColumnGroupType, } from '../interface'; import { INTERNAL_COL_DEFINE } from '../utils/legacyUtil'; function flatColumns(columns: ColumnsType): ColumnType[] { return columns.reduce((list, column) => { const { fixed } = column; // Convert `fixed='true'` to `fixed='left'` instead const parsedFixed = fixed === true ? 'left' : fixed; const subColumns = (column as ColumnGroupType).children; if (subColumns && subColumns.length > 0) { return [ ...list, ...flatColumns(subColumns).map(subColum => ({ fixed: parsedFixed, ...subColum, })), ]; } return [ ...list, { ...column, fixed: parsedFixed, }, ]; }, []); } function warningFixed(flattenColumns: readonly { fixed?: FixedType }[]) { let allFixLeft = true; for (let i = 0; i < flattenColumns.length; i += 1) { const col = flattenColumns[i]; if (allFixLeft && col.fixed !== 'left') { allFixLeft = false; } else if (!allFixLeft && col.fixed === 'left') { warning(false, `Index ${i - 1} of \`columns\` missing \`fixed='left'\` prop.`); break; } } let allFixRight = true; for (let i = flattenColumns.length - 1; i >= 0; i -= 1) { const col = flattenColumns[i]; if (allFixRight && col.fixed !== 'right') { allFixRight = false; } else if (!allFixRight && col.fixed === 'right') { warning(false, `Index ${i + 1} of \`columns\` missing \`fixed='right'\` prop.`); break; } } } function revertForRtl(columns: ColumnsType): ColumnsType { return columns.map(column => { const { fixed, ...restProps } = column; // Convert `fixed='left'` to `fixed='right'` instead let parsedFixed = fixed; if (fixed === 'left') { parsedFixed = 'right'; } else if (fixed === 'right') { parsedFixed = 'left'; } return { fixed: parsedFixed, ...restProps, }; }); } /** * Parse `columns` & `children` into `columns`. */ function useColumns( { prefixCls, columns: baseColumns, // children, expandable, expandedKeys, getRowKey, onTriggerExpand, expandIcon, rowExpandable, expandIconColumnIndex, direction, expandRowByClick, expandColumnWidth, expandFixed, }: { prefixCls?: Ref; columns?: Ref>; expandable: Ref; expandedKeys: Ref>; getRowKey: Ref>; onTriggerExpand: TriggerEventHandler; expandIcon?: Ref>; rowExpandable?: Ref<(record: RecordType) => boolean>; expandIconColumnIndex?: Ref; direction?: Ref<'ltr' | 'rtl'>; expandRowByClick?: Ref; expandColumnWidth?: Ref; expandFixed?: Ref; }, transformColumns: Ref<(columns: ColumnsType) => ColumnsType>, ): [ComputedRef>, ComputedRef[]>] { // Add expand column const withExpandColumns = computed>(() => { if (expandable.value) { const expandColIndex = expandIconColumnIndex.value || 0; const prevColumn = baseColumns.value[expandColIndex]; let fixedColumn: FixedType | null; if ((expandFixed.value === 'left' || expandFixed.value) && !expandIconColumnIndex.value) { fixedColumn = 'left'; } else if ( (expandFixed.value === 'right' || expandFixed.value) && expandIconColumnIndex.value === baseColumns.value.length ) { fixedColumn = 'right'; } else { fixedColumn = prevColumn ? prevColumn.fixed : null; } const expandedKeysValue = expandedKeys.value; const rowExpandableValue = rowExpandable.value; const expandIconValue = expandIcon.value; const prefixClsValue = prefixCls.value; const expandRowByClickValue = expandRowByClick.value; const expandColumn = { [INTERNAL_COL_DEFINE]: { class: `${prefixCls.value}-expand-icon-col`, }, title: '', fixed: fixedColumn, class: `${prefixCls.value}-row-expand-icon-cell`, width: expandColumnWidth.value, customRender: ({ record, index }) => { const rowKey = getRowKey.value(record, index); const expanded = expandedKeysValue.has(rowKey); const recordExpandable = rowExpandableValue ? rowExpandableValue(record) : true; const icon = expandIconValue({ prefixCls: prefixClsValue, expanded, expandable: recordExpandable, record, onExpand: onTriggerExpand, }); if (expandRowByClickValue) { return e.stopPropagation()}>{icon}; } return icon; }, }; // Insert expand column in the target position const cloneColumns = baseColumns.value.slice(); if (expandColIndex >= 0) { cloneColumns.splice(expandColIndex, 0, expandColumn); } return cloneColumns; } return baseColumns.value; }); const mergedColumns = computed(() => { let finalColumns = withExpandColumns.value; if (transformColumns.value) { finalColumns = transformColumns.value(finalColumns); } // Always provides at least one column for table display if (!finalColumns.length) { finalColumns = [ { customRender: () => null, }, ]; } return finalColumns; }); const flattenColumns = computed(() => { if (direction.value === 'rtl') { return revertForRtl(flatColumns(mergedColumns.value)); } return flatColumns(mergedColumns.value); }); // Only check out of production since it's waste for each render if (process.env.NODE_ENV !== 'production') { watchEffect(() => { setTimeout(() => { warningFixed(flattenColumns.value); }); }); } return [mergedColumns, flattenColumns]; } export default useColumns;