import { getSearchReg } from '@vev/utils'; import classnames from 'classnames/bind'; import { isArray, isEqual, isObject } from 'lodash'; import React, { HTMLAttributes, useMemo, useState } from 'react'; import { SilkeBox } from '../silke-box'; import { ButtonContext, SilkeButton } from '../silke-button'; import { SilkeCheckbox } from '../silke-checkbox'; import TextFieldContext from '../silke-text-field/text-field-context'; import styles from './silke-table.scss'; import { TableRow } from './table-row'; import { TableColumn, TableRowMenu } from './table-types'; import { SilkeText } from '../silke-text'; const cl = classnames.bind(styles); type BaseProps> = { children?: React.ReactNode; menu?: TableRowMenu[]; fixedColumns?: boolean; hideHeaders?: boolean; columns: Array | keyof T>; /** Will filter the list based on the value of the cells (can only search when cell value is string) */ search?: string; items: T[]; loadMoreLabel?: string; onLoadMore?: () => Promise; onRowHovered?: (index: number) => void; } & Omit, 'cellPadding' | 'cellSpacing' | 'onSelect'>; type SingleSelect> = BaseProps & { multiselect?: false | undefined; multiselectLabel?: string; selected?: T; onSelect?: (item: T) => void; }; type MultiSelect> = BaseProps & { multiselect: true; multiselectLabel?: string; selected?: T[]; onSelect: (items: T[]) => void; }; type SilkeTableProps> = SingleSelect | MultiSelect; export function SilkeTable>({ items, columns: _columns, children, fixedColumns, className, search, hideHeaders, menu, multiselect, multiselectLabel, selected, onSelect, loadMoreLabel, onLoadMore, onRowHovered, ...rest }: SilkeTableProps) { const [loading, setLoading] = useState(false); const columns = useMemo( () => _columns.map((c) => (isObject(c) ? c : ({ attr: c } as TableColumn))), [_columns], ); const selectedRows = useMemo(() => { if (!selected || items?.length === 0) return [false]; return items.map((value) => { if (isArray(selected)) return selected.includes(value); return isEqual(value, selected); }); }, [items, selected]); // If selectedRows does not contain any false values then all is selected const allSelected = selectedRows.indexOf(false) === -1; const searchReg = search ? getSearchReg(search) : undefined; const handleSelect = (value: T, select: boolean) => { if (onSelect) { if (multiselect === true) { const newValue = (selected as T[])?.slice() || []; if (select) newValue.push(value); else { const index = newValue.findIndex((v) => isEqual(value, v)); if (index !== -1) newValue.splice(index, 1); } onSelect(newValue as any); } else { onSelect(select ? (value as any) : undefined); } } }; const handleLoadMore = async () => { if (onLoadMore) { try { setLoading(true); await onLoadMore(); } finally { setLoading(false); } } }; const handleRowHovered = (index: number) => { if (onRowHovered) { onRowHovered(index); } }; let columnCount = columns.length; if (multiselect) columnCount++; if (menu) columnCount++; return ( {children && ( {children} )} 0 : Boolean(selected), })} > {columns.map((col, index) => { return ( ); })} {menu && } {multiselect && } {!hideHeaders && ( {multiselect && ( )} {multiselectLabel ? ( ) : ( columns.map((c, i) => ) )} {menu && )} handleRowHovered(-1)}> {items.map((item, i) => ( handleRowHovered(i)} /> ))} {onLoadMore && ( )}
onSelect && onSelect(v ? (items as any) : [])} /> {multiselectLabel} {c.component || c.label}}
); }