PluginProbe
Code Snippets / 4.0.0-beta.1
Code Snippets v4.0.0-beta.1
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / js / components / common / ListTable / ListTable.tsx

ListTable.tsx in Code Snippets 4.0.0-beta.1, at js/components/common/ListTable/ListTable.tsx

175 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React, { useMemo, useState } from 'react'
2 import classnames from 'classnames'
3 import { fetchQueryParam } from '../../../utils/urls'
4 import { ColumnHeadings } from './ColumnHeadings'
5 import { TableRows } from './TableRows'
6 import { TableNavigation } from './TableNavigation'
7 import type { ColumnHeadingsProps } from './ColumnHeadings'
8 import type { Key, ReactNode } from 'react'
9
10 export interface ListTableColumn<T> {
11 id: Key
12 title?: ReactNode
13 render: (item: T) => ReactNode
14 isHidden?: boolean
15 isPrimary?: boolean
16 isHeading?: boolean
17 sortedValue?: (item: T) => Key
18 defaultSortDirection?: ListTableSortDirection
19 }
20
21 export type ListTableSortDirection = 'asc' | 'desc'
22
23 export interface ListTableAction<A extends string> {
24 key: A
25 label: string
26 group?: string
27 }
28
29 export interface ListTableNavProps<K extends Key, A extends string> {
30 actions?: ListTableAction<A>[]
31 doAction: (action: A, selected: Set<K>) => Promise<void>
32 disabled?: boolean
33 extraTableNav?: (which: 'top' | 'bottom') => ReactNode
34 }
35
36 export interface ListTableRowsProps<T, K extends Key> {
37 items: T[]
38 getKey: (item: T) => K
39 columns: ListTableColumn<T>[]
40 noItems?: ReactNode
41 rowClassName?: (item: T) => string
42 }
43
44 export interface ListTablePaginationProps {
45 totalPages?: number
46 useQueryVars?: boolean
47 }
48
49 export interface ListTableBorderProps {
50 fixed?: boolean
51 striped?: boolean
52 className?: string
53 }
54
55 export type ListTableProps<T, K extends Key, A extends string> =
56 ListTableBorderProps &
57 ListTableNavProps<K, A> &
58 ListTablePaginationProps &
59 ListTableRowsProps<T, K>
60
61 const sortItems = <T, >(
62 items: T[],
63 sortColumn: ListTableColumn<T> | undefined,
64 sortDirection: ListTableSortDirection
65 ): T[] =>
66 items.toSorted((itemA, itemB) => {
67 const valueA = sortColumn?.sortedValue?.(itemA)
68 const valueB = sortColumn?.sortedValue?.(itemB)
69
70 if (valueA === undefined || valueB === undefined) {
71 return 0
72 }
73
74 if (valueA < valueB) {
75 return 'asc' === sortDirection ? -1 : 1
76 }
77
78 if (valueA > valueB) {
79 return 'asc' === sortDirection ? 1 : -1
80 }
81
82 return 0
83 })
84
85 const pageItems = <T, >(
86 items: T[],
87 { currentPage, totalPages }: { currentPage: number; totalPages?: number }
88 ): T[] => {
89 if (totalPages) {
90 const itemsPerPage = Math.ceil(items.length / totalPages)
91 const start = (currentPage - 1) * itemsPerPage
92 const end = start + itemsPerPage
93 return items.slice(start, end)
94 } else {
95 return items
96 }
97 }
98
99 const getVisibleSelected = <T, K extends Key>(
100 visibleItems: T[],
101 getKey: (item: T) => K,
102 selected: Set<K>
103 ): Set<K> =>
104 new Set(visibleItems.map(getKey).filter(key => selected.has(key)))
105
106 interface TableBorderProps<T, K extends Key> extends ListTableBorderProps, Omit<ColumnHeadingsProps<T, K>, 'which'> {
107 children: ReactNode
108 }
109
110 const TableBorder = <T, K extends Key>({
111 fixed,
112 striped,
113 children,
114 className,
115 ...tableHeadingsProps
116 }: TableBorderProps<T, K>) => (
117 <table className={classnames('wp-list-table widefat', { striped, fixed }, className)}>
118 <thead>
119 <ColumnHeadings which="head" {...tableHeadingsProps} />
120 </thead>
121 <tbody>
122 {children}
123 </tbody>
124 <tfoot>
125 <ColumnHeadings which="foot" {...tableHeadingsProps} />
126 </tfoot>
127 </table>
128 )
129
130 export const ListTable = <T, K extends Key, A extends string = never>({
131 items,
132 getKey,
133 columns,
134 actions,
135 doAction,
136 totalPages,
137 extraTableNav,
138 disabled = false,
139 useQueryVars = true,
140 fixed,
141 striped,
142 className,
143 ...tableRowsProps
144 }: ListTableProps<T, K, A>) => {
145 const [selected, setSelected] = useState(() => new Set<K>())
146 const [sortColumn, setSortColumn] = useState<ListTableColumn<T>>()
147 const [currentPage, setCurrentPage] = useState(() => useQueryVars && Number(fetchQueryParam('paged')) || 1)
148 const [sortDirection, setSortDirection] = useState<ListTableSortDirection>('asc')
149
150 const visibleItems: T[] = useMemo(
151 () => pageItems(sortItems(items, sortColumn, sortDirection), { currentPage, totalPages }),
152 [items, sortColumn, sortDirection, currentPage, totalPages])
153 return (
154 <TableNavigation
155 totalItems={items.length}
156 selected={getVisibleSelected(visibleItems, getKey, selected)}
157 {...{ actions, doAction, extraTableNav, disabled, currentPage, totalPages, useQueryVars, setSelected, setCurrentPage }}
158 >
159 <TableBorder
160 items={visibleItems}
161 fixed={fixed}
162 striped={striped}
163 className={className}
164 {...{ columns, getKey, selected, sortColumn, sortDirection, setSelected, setSortColumn, setSortDirection }}
165 >
166 <TableRows
167 items={visibleItems}
168 {...{ getKey, columns, selected, setSelected }}
169 {...tableRowsProps}
170 />
171 </TableBorder>
172 </TableNavigation>
173 )
174 }
175