@coreui/react-pro
Version:
UI Components Library for React.js
378 lines (309 loc) • 9.21 kB
text/typescript
import type {
Column,
ColumnFilter,
ColumnFilterValue,
Group,
Item,
Sorter,
SorterValue,
TableFilter,
} from './types'
export const filterColumns = (
items: Item[],
columnFilter: boolean | ColumnFilter | undefined,
columnFilterState: ColumnFilterValue,
itemsDataColumns: string[],
): Item[] => {
if (columnFilter && typeof columnFilter === 'object' && columnFilter.external) {
return items
}
if (Object.entries(columnFilterState).length === 0) {
return items
}
let filteredItems = [...items]
for (const [key, value] of Object.entries(columnFilterState)) {
if (typeof value === 'function') {
filteredItems = filteredItems.filter((item) => value(item[key]))
} else {
const columnFilterValue = String(value).toLowerCase()
if (columnFilterValue && itemsDataColumns.includes(key)) {
filteredItems = filteredItems.filter((item) =>
String(item[key]).toLowerCase().includes(columnFilterValue),
)
}
}
}
return filteredItems
}
export const filterTable = (
items: Item[],
tableFilter: boolean | TableFilter | undefined,
tableFilterState: string,
itemsDataColumns: string[],
): Item[] => {
if (
!tableFilterState ||
(tableFilter && typeof tableFilter === 'object' && tableFilter.external)
) {
return items
}
const filter = tableFilterState.toLowerCase()
return items.filter((item) =>
itemsDataColumns.some((key) => String(item[key]).toLowerCase().includes(filter)),
)
}
export const getClickedColumnName = (
target: HTMLElement,
columnNames: string[],
selectable?: boolean,
): string => {
const clickedCell = target.closest('td')
if (!clickedCell) {
return ''
}
const row = clickedCell.closest('tr')
if (!row) {
return ''
}
const cells = Array.from(row.children)
let index = cells.indexOf(clickedCell)
if (selectable) {
index -= 1
}
return columnNames[index] || ''
}
export const getColumnKey = (column: Column | string): string =>
typeof column === 'object' ? column.key : column
export const getColumnLabel = (column: Column | string): string =>
typeof column === 'object' ? (column.label ?? pretifyName(column.key)) : pretifyName(column)
export const getColumnName = (column: Column | string): string =>
typeof column === 'object' ? column.key : column
export const getColumnNames = (
columns: (string | Column)[] | undefined,
items: Item[],
): string[] => {
if (columns) {
const _columns = []
for (const column of columns) {
if (typeof column === 'object' && column.children) {
_columns.push(...getColumnNames(column.children, []))
continue
}
typeof column === 'object' ? _columns.push(column.key) : _columns.push(column)
}
return _columns
}
return getColumnNamesFromItems(items)
}
export const getColumns = (_columns: (Column | Group | string)[]): (Column | string)[] => {
const columns = []
for (const column of _columns) {
if (typeof column === 'object' && column.group && column.children) {
columns.push(...getColumns(column.children))
continue
}
if (typeof column === 'object' && column.children) {
columns.push(...getColumns(column.children))
}
columns.push(column)
}
return columns
}
export const countColumns = (columns: Column[]): number => {
let count = 0
for (const column of columns) {
if (!column.children || column.children.length === 0) {
count++
} else {
count += countColumns(column.children)
}
}
return count
}
export const getColumnGroups = (columns: (string | Column)[] | undefined): Group[][] => {
const groups: Group[][] = []
const traverseColumns = (column: Column, deep = 0, colSpan = 0): Group[] => {
const groups = []
if (column.children) {
for (const _column of column.children) {
if (!_column.group) {
colSpan++
}
groups.push(...traverseColumns(_column, deep + 1, colSpan))
}
}
if (typeof column === 'object' && column.group) {
const { children, group, ...rest } = column
groups.push({
deep: deep,
label: group,
...(children && { colspan: countColumns(children) }),
...rest,
})
}
return groups
}
if (columns) {
for (const column of columns) {
if (typeof column === 'object' && column.group) {
const objects = traverseColumns(column)
if (objects) {
for (const object of objects) {
const { deep, ...rest } = object
if (deep === undefined) {
continue
}
for (let i = 0; i < deep; i++) {
if (groups[i]) {
continue
}
groups.push([])
}
if (groups[deep]) {
groups[deep].push(rest)
} else {
groups.push([rest])
}
}
}
}
}
}
return groups
}
export const getColumnNamesFromItems = (items: Item[]): string[] =>
Object.keys(items[0] || {}).filter((el) => el.charAt(0) !== '_')
export const getColumnSorterFunction = (column: Column | undefined) => {
if (
column &&
typeof column === 'object' &&
column.sorter &&
typeof column.sorter === 'function'
) {
return column.sorter
}
return
}
export const getColumnSorterState = (key: string, sorterState?: SorterValue[]): string | number => {
const state = sorterState && sorterState.find((el) => el.column === key)
return state ? state.state : 0
}
export const getColumnValues = (items: Item[], key: string): any[] => items.map((item) => item[key])
export const getTableDataCellProps = (
column: Column | string,
item: Item,
colName: string,
): Record<string, any> => {
const props = {
...(typeof column === 'object' && column._colProps),
...item._cellProps?.all,
...item._cellProps?.[colName],
}
return props
}
export const getTableDataCellStyles = (
column: Column | string,
item: Item,
colName: string,
): Record<string, any> => {
const styles = {
...(typeof column === 'object' && column._colStyle),
...item._cellStyle?.all,
...item._cellStyle?.[colName],
}
return styles
}
export const getTableHeaderCellProps = (column: Column | string) =>
typeof column === 'object' && column._props ? column._props : {}
export const getTableHeaderCellStyles = (
column: Column | string,
columnSorter: boolean | Sorter | undefined,
): Record<string, any> => {
const style: Record<string, any> = {}
const isSortable =
columnSorter &&
(typeof column !== 'object' ||
(typeof column === 'object' && (column.sorter === undefined || column.sorter)))
if (isSortable) {
style.cursor = 'pointer'
}
if (typeof column === 'object' && column._style) {
Object.assign(style, column._style)
}
return style
}
export const isSortable = (
index: number,
columns: (string | Column)[] | undefined,
columnSorter: boolean | Sorter | undefined,
itemsDataColumns: string[],
columnNames: string[],
): boolean => {
const key = columnNames[index]
const isDataColumn = itemsDataColumns.includes(key)
const column = columns ? columns[index] : undefined
return (
!!columnSorter &&
(!columns ||
typeof column !== 'object' ||
(typeof column === 'object' && (column.sorter === undefined || column.sorter))) &&
isDataColumn
)
}
export const pretifyName = (name: string): string => {
return name
.replace(/[-_.]/g, ' ')
.replace(/ +/g, ' ')
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
.split(' ')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
}
export const sortBy = (
column: Column | undefined,
items: Item[],
key: string,
order: SorterValue['state'],
): Item[] => {
const sorterFunction = getColumnSorterFunction(column)
const sortedItems = [...items].sort(
sorterFunction ||
((a, b) => {
const valA = a[key]
const valB = b[key]
const aValue = typeof valA === 'number' ? valA : String(valA).toLowerCase()
const bValue = typeof valB === 'number' ? valB : String(valB).toLowerCase()
return aValue > bValue ? 1 : bValue > aValue ? -1 : 0
}),
)
return order === 'desc' ? sortedItems.reverse() : sortedItems
}
export const sortItems = (
columns: (Column | string)[] | undefined,
columnSorter: boolean | Sorter | undefined,
items: Item[],
itemsDataColumns: string[],
sorterState: SorterValue[],
): Item[] => {
if (
(columnSorter && typeof columnSorter === 'object' && columnSorter.external) ||
sorterState.length === 0
) {
return items
}
let sortedItems = [...items]
for (const sorter of [...sorterState].reverse()) {
const key = sorter.column
const order = sorter.state
const column =
columns &&
(columns.find((column) => typeof column === 'object' && column.key === key) as
| Column
| undefined)
if (!key || !itemsDataColumns.includes(key)) {
continue
}
sortedItems = sortBy(column, sortedItems, key, order)
}
return sortedItems
}