import { createColumnHelper, useReactTable, getCoreRowModel, Header, RowSelectionState, SortingState, getSortedRowModel, ColumnSort, getExpandedRowModel, ExpandedState, } from '@tanstack/react-table'; import { CSSProperties, Dispatch, SetStateAction, useEffect, useMemo, useRef, useState, } from 'react'; import {DragStartEvent, DragEndEvent} from '@dnd-kit/core'; import {arrayMove} from '@dnd-kit/sortable'; import {createTableSettingContext, useContextSection} from '@hooks'; import TableCheck from './Check'; import {LDColumnsType} from '.'; import {TABLE_CELL_WIDTH} from '@utils'; import {useLatest} from 'ahooks'; function parseColumn>( columns: LDColumnsType[], enableSelect?: boolean, enableNo?: boolean, ) { const helper = createColumnHelper>(); let hasSort = false, hasGroup = false; const parseColumns: LDColumnsType[] = [...columns]; if (enableNo) { parseColumns.unshift({ title: '序号', dataIndex: 'no', align: 'center', width: TABLE_CELL_WIDTH.no, render(_, index, row) { return row.depth > 0 ? null : index + 1; }, }); } let leftPosition = enableSelect ? TABLE_CELL_WIDTH.checkbox : 0; let rightPosition = parseColumns.reduce(function(prev, next) { if (next.fixed !== 'right') return prev; return prev + next.width; }, 0); const columnList = parseColumns.map(function(val) { const {dataIndex, title, render, align, fixed, width, sort, children} = val; let fixedStyle: CSSProperties = {}; if (fixed === 'left') { fixedStyle = {left: leftPosition}; leftPosition += width; } if (fixed === 'right') { rightPosition -= width; fixedStyle = {right: rightPosition}; } const meta = { align, fixed, fixedStyle, disabledSort: Boolean(fixed) || dataIndex === 'no', sort, }; if (children && children.length) { if (!hasGroup) hasGroup = true; const {columnList: columns} = parseColumn( children, false, false, ); const group = helper.group({ header: title, columns, minSize: 0, size: width, meta, }); return group; } if (!hasSort && sort) hasSort = true; if (render) { return helper.display({ id: dataIndex.toString(), header: title.toString(), size: width, minSize: 0, cell({row}) { return render( row.original as T, row.index, row, ); }, meta, }); } return helper.accessor(dataIndex.toString(), { header: title?.toString(), cell: props => props.getValue(), size: width, meta, }); }); enableSelect && columnList.unshift( helper.accessor('select', { header({table}) { return ( ); }, cell({row}) { return ( ); }, size: TABLE_CELL_WIDTH.checkbox, meta: { align: 'center', fixed: 'left', disabledSort: true, fixedStyle: {left: 0}, }, }), ); return { columnList, hasSort, hasGroup, }; } export function useTable>( columns: LDColumnsType[], data: T[], options: { settingContext: ReturnType; rowSelection?: RowSelectionState; setRowSelection?: Dispatch>; rawKey?: keyof T; onSortingChange?: (state: ColumnSort | null) => void; subRowKey?: keyof T; }, ) { const { settingContext, rowSelection, setRowSelection, rawKey, onSortingChange, subRowKey, } = options; const {columnList, hasSort, hasGroup} = useMemo( function() { return parseColumn( columns, Boolean(setRowSelection), true, ); }, [columns, setRowSelection], ); const preloadData = useContextSection(settingContext, state => state[0]); const [columnSizing, setColumnSizing] = useState(function() { if (preloadData?.tableWidth) return JSON.parse(preloadData.tableWidth) as Record; return {}; }); const [columnOrder, setColumnOrder] = useState(function() { if (preloadData?.tableOrder) return JSON.parse(preloadData?.tableOrder) as string[]; const nextList = columns.map(val => val.dataIndex.toString()); return hasGroup ? [] : ['select', 'no', ...nextList]; }); const [sorting, setSorting] = useState([]); const onSortingChangeFn = useLatest(onSortingChange); useEffect(function() { onSortingChangeFn.current?.(sorting.length ? sorting[0] : null); }, [onSortingChangeFn, sorting]); const [expanded, setExpanded] = useState({}); const table = useReactTable({ data, columns: columnList, state: { columnSizing, columnOrder, rowSelection, sorting, expanded, }, getSortedRowModel: onSortingChange ? void 0 : getSortedRowModel(), getCoreRowModel: getCoreRowModel(), onColumnSizingChange: setColumnSizing, columnResizeMode: 'onChange', onColumnOrderChange: setColumnOrder, onRowSelectionChange: setRowSelection, getRowId: row => row[rawKey as string | undefined ?? 'id'], onSortingChange: setSorting, getSubRows: subRowKey ? row => row[subRowKey as string] : void 0, getExpandedRowModel: subRowKey ? getExpandedRowModel() : void 0, onExpandedChange: setExpanded, }); type ActiveState = Header, unknown> | null; const [active, setActive] = useState(null); function onDragStart(event: DragStartEvent) { setActive(event.active.data.current?.header); } function onDragEnd({active, over}: DragEndEvent) { setActive(null); if (!over) return; const {id: activeId} = active, {id: overId} = over; const {disabledSort} = (over.data.current as any).header.column.columnDef .meta; if (disabledSort) return; if (activeId === overId) return; const {setColumnOrder} = table; setColumnOrder(function(prev) { const fromIdx = prev.findIndex(val => val === activeId), toIdx = prev.findIndex(val => val === overId); return arrayMove(prev, fromIdx, toIdx); }); } return [ {...table, active, hasSort, hasGroup}, {onDragStart, onDragEnd}, ] as const; } export function useTableShadow(tableSize: number) { const [scrollProgess, setScrollProgess] = useState<'start' | 'process' | 'end'>( 'start', ); const scrollTableRef = useRef(null); const headerTableRef = useRef(null); useEffect( function() { const el = scrollTableRef.current; function listener() { const elWidth = el?.getBoundingClientRect().width ?? 0, scrollWidth = el?.scrollWidth ?? 0, scrollLeft = el?.scrollLeft ?? 0; headerTableRef.current && ( headerTableRef.current.scrollLeft = el?.scrollLeft ?? 0 ); setScrollProgess(function() { if (scrollLeft + elWidth === scrollWidth) return 'end'; if (scrollLeft === 0) return 'start'; return 'process'; }); } listener(); window.addEventListener('resize', listener); el?.addEventListener('scroll', listener); return function() { window.addEventListener('resize', listener); el?.removeEventListener('scroll', listener); }; }, [tableSize], ); return {scrollProgess, scrollTableRef, headerTableRef}; }