PluginProbe
Code Snippets / 3.10.0
Code Snippets v3.10.0
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 3.10.0, at js/components/common/ListTable/ListTable.tsx

227 lines 5.8 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 endTableNav?: (which: 'top' | 'bottom') => ReactNode
35 }
36
37 export interface ListTableRowsProps<T, K extends Key> {
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 pageSearchParam?: string
47 }
48
49 export interface ListTableBorderProps {
50 fixed?: boolean
51 striped?: boolean
52 className?: string
53 }
54
55 export const sortTableItems = <T, >(
56 items: T[],
57 sortColumn: ListTableColumn<T> | undefined,
58 sortDirection: ListTableSortDirection
59 ): T[] =>
60 items.toSorted((itemA, itemB) => {
61 const valueA = sortColumn?.sortedValue?.(itemA)
62 const valueB = sortColumn?.sortedValue?.(itemB)
63
64 if (valueA === undefined || valueB === undefined) {
65 return 0
66 }
67
68 if (valueA < valueB) {
69 return 'asc' === sortDirection ? -1 : 1
70 }
71
72 if (valueA > valueB) {
73 return 'asc' === sortDirection ? 1 : -1
74 }
75
76 return 0
77 })
78
79 const pageItems = <T, >(
80 items: T[],
81 { currentPage, totalPages }: { currentPage: number; totalPages?: number }
82 ): T[] => {
83 if (totalPages) {
84 const itemsPerPage = Math.ceil(items.length / totalPages)
85 const start = (currentPage - 1) * itemsPerPage
86 const end = start + itemsPerPage
87 return items.slice(start, end)
88 } else {
89 return items
90 }
91 }
92
93 const getVisibleSelected = <T, K extends Key>(
94 visibleItems: T[],
95 getKey: (item: T) => K,
96 selected: Set<K>
97 ): Set<K> =>
98 new Set(visibleItems.map(getKey).filter(key => selected.has(key)))
99
100 interface TableBorderProps<T, K extends Key>
101 extends ListTableBorderProps, Omit<ColumnHeadingsProps<T, K>, 'which'> {
102 children: ReactNode
103 }
104
105 const TableBorder = <T, K extends Key>({
106 fixed,
107 striped,
108 children,
109 className,
110 ...tableHeadingsProps
111 }: TableBorderProps<T, K>) => (
112 <table className={classnames('wp-list-table widefat', { striped, fixed }, className)}>
113 <thead>
114 <ColumnHeadings which="head" {...tableHeadingsProps} />
115 </thead>
116 <tbody>
117 {children}
118 </tbody>
119 <tfoot>
120 <ColumnHeadings which="foot" {...tableHeadingsProps} />
121 </tfoot>
122 </table>
123 )
124
125 export interface ListTableProps<T, K extends Key, A extends string> extends ListTableBorderProps,
126 ListTableNavProps<K, A>,
127 ListTablePaginationProps,
128 ListTableRowsProps<T, K> {
129 items: T[]
130 beforeTable?: ReactNode
131 selectAllControl?: boolean
132 }
133
134 export const ListTable = <T, K extends Key, A extends string = never>({
135 items,
136 getKey,
137 totalPages,
138 pageSearchParam = 'paged',
139 ...tableProps
140 }: ListTableProps<T, K, A>) => {
141 const [sortColumn, setSortColumn] = useState<ListTableColumn<T>>()
142 const [currentPage, setCurrentPage] = useState(
143 () => pageSearchParam && Number(fetchQueryParam(pageSearchParam)) || 1
144 )
145 const [sortDirection, setSortDirection] = useState<ListTableSortDirection>('asc')
146
147 const visibleItems: T[] = useMemo(
148 () => pageItems(sortTableItems(items, sortColumn, sortDirection), { currentPage, totalPages }),
149 [items, sortColumn, sortDirection, currentPage, totalPages])
150
151 return (
152 <PartialDataListTable
153 getKey={getKey}
154 totalItems={items.length}
155 totalPages={totalPages}
156 visibleItems={visibleItems}
157 pageSearchParam={pageSearchParam}
158 {...{ sortColumn, sortDirection, currentPage, setSortColumn, setSortDirection, setCurrentPage }}
159 {...tableProps}
160 />
161 )
162 }
163
164 export interface PartialDataListTableProps<T, K extends Key, A extends string>
165 extends ListTablePaginationProps,
166 ListTableBorderProps,
167 ListTableRowsProps<T, K>,
168 ListTableNavProps<K, A> {
169 sortColumn: ListTableColumn<T> | undefined
170 totalItems: number
171 currentPage: number
172 visibleItems: T[]
173 beforeTable?: ReactNode
174 selectAllControl?: boolean
175 setSortColumn: (column: ListTableColumn<T> | undefined) => void
176 sortDirection?: ListTableSortDirection
177 setCurrentPage: (page: number) => void
178 setSortDirection: (direction: ListTableSortDirection) => void
179 }
180
181 export const PartialDataListTable = <T, K extends Key, A extends string>({
182 getKey,
183 actions,
184 columns,
185 noItems,
186 doAction,
187 disabled = false,
188 totalItems,
189 totalPages,
190 currentPage,
191 beforeTable,
192 endTableNav,
193 rowClassName,
194 visibleItems,
195 sortDirection = 'asc',
196 extraTableNav,
197 selectAllControl,
198 setCurrentPage,
199 pageSearchParam,
200 ...tableBorderProps
201 }: PartialDataListTableProps<T, K, A>) => {
202 const [selected, setSelected] = useState(() => new Set<K>())
203
204 return (
205 <TableNavigation
206 totalItems={totalItems}
207 selected={getVisibleSelected(visibleItems, getKey, selected)}
208 selectAllKeys={selectAllControl ? visibleItems.map(getKey) : undefined}
209 {...{ actions, doAction, extraTableNav, endTableNav, disabled, currentPage, totalPages }}
210 {...{ pageSearchParam, setSelected, setCurrentPage }}
211 >
212 {beforeTable}
213
214 <TableBorder
215 items={visibleItems}
216 {...tableBorderProps}
217 {...{ getKey, columns, selected, setSelected, sortDirection }}
218 >
219 <TableRows
220 items={visibleItems}
221 {...{ getKey, columns, noItems, rowClassName, selected, setSelected }}
222 />
223 </TableBorder>
224 </TableNavigation>
225 )
226 }
227