@xyo-network/react-payload-table 2.37.24 → 2.37.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cjs/components/Table/Table.d.ts +8 -4
  2. package/dist/cjs/components/Table/Table.d.ts.map +1 -1
  3. package/dist/cjs/components/Table/Table.js +23 -18
  4. package/dist/cjs/components/Table/Table.js.map +1 -1
  5. package/dist/cjs/components/Table/TablePagination.d.ts.map +1 -1
  6. package/dist/cjs/components/Table/TablePagination.js +9 -1
  7. package/dist/cjs/components/Table/TablePagination.js.map +1 -1
  8. package/dist/cjs/components/Table/index.d.ts +2 -0
  9. package/dist/cjs/components/Table/index.d.ts.map +1 -1
  10. package/dist/cjs/components/Table/index.js +2 -0
  11. package/dist/cjs/components/Table/index.js.map +1 -1
  12. package/dist/cjs/components/Table/types/PaginationEventNouns.d.ts +2 -0
  13. package/dist/cjs/components/Table/types/PaginationEventNouns.d.ts.map +1 -0
  14. package/dist/cjs/components/Table/types/PaginationEventNouns.js +3 -0
  15. package/dist/cjs/components/Table/types/PaginationEventNouns.js.map +1 -0
  16. package/dist/cjs/components/Table/types/index.d.ts +2 -0
  17. package/dist/cjs/components/Table/types/index.d.ts.map +1 -0
  18. package/dist/cjs/components/Table/types/index.js +5 -0
  19. package/dist/cjs/components/Table/types/index.js.map +1 -0
  20. package/dist/docs.json +2732 -231
  21. package/dist/esm/components/Table/Table.d.ts +8 -4
  22. package/dist/esm/components/Table/Table.d.ts.map +1 -1
  23. package/dist/esm/components/Table/Table.js +22 -15
  24. package/dist/esm/components/Table/Table.js.map +1 -1
  25. package/dist/esm/components/Table/TablePagination.d.ts.map +1 -1
  26. package/dist/esm/components/Table/TablePagination.js +7 -1
  27. package/dist/esm/components/Table/TablePagination.js.map +1 -1
  28. package/dist/esm/components/Table/index.d.ts +2 -0
  29. package/dist/esm/components/Table/index.d.ts.map +1 -1
  30. package/dist/esm/components/Table/index.js +2 -0
  31. package/dist/esm/components/Table/index.js.map +1 -1
  32. package/dist/esm/components/Table/types/PaginationEventNouns.d.ts +2 -0
  33. package/dist/esm/components/Table/types/PaginationEventNouns.d.ts.map +1 -0
  34. package/dist/esm/components/Table/types/PaginationEventNouns.js +2 -0
  35. package/dist/esm/components/Table/types/PaginationEventNouns.js.map +1 -0
  36. package/dist/esm/components/Table/types/index.d.ts +2 -0
  37. package/dist/esm/components/Table/types/index.d.ts.map +1 -0
  38. package/dist/esm/components/Table/types/index.js +2 -0
  39. package/dist/esm/components/Table/types/index.js.map +1 -0
  40. package/package.json +7 -6
  41. package/src/components/Table/FetchMoreTable.stories.tsx +26 -9
  42. package/src/components/Table/Table.tsx +134 -116
  43. package/src/components/Table/TablePagination.tsx +15 -2
  44. package/src/components/Table/index.ts +2 -0
  45. package/src/components/Table/types/PaginationEventNouns.ts +1 -0
  46. package/src/components/Table/types/index.ts +1 -0
@@ -3,7 +3,7 @@ import { useBreakpoint } from '@xylabs/react-shared'
3
3
  import { PayloadWrapper, XyoPayload } from '@xyo-network/payload'
4
4
  import { XyoApiThrownErrorBoundary } from '@xyo-network/react-auth-service'
5
5
  import { TableEx, TableExProps, TableFooterEx } from '@xyo-network/react-table'
6
- import { useEffect, useMemo, useState } from 'react'
6
+ import { forwardRef, useEffect, useState } from 'react'
7
7
 
8
8
  import { payloadColumnNames, PayloadTableColumnConfig, payloadTableColumnConfigDefaults } from './PayloadTableColumnConfig'
9
9
  import { TablePaginationActions } from './TablePagination'
@@ -13,140 +13,158 @@ export interface PayloadTableProps extends TableExProps {
13
13
  exploreDomain?: string
14
14
  archive?: string
15
15
  onRowClick?: (value: XyoPayload) => void
16
- fetchMorePayloads?: () => boolean
17
16
  rowsPerPage?: number
18
17
  payloads?: XyoPayload[] | null
18
+ loading?: boolean
19
19
  columns?: PayloadTableColumnConfig
20
+ /** External trigger to fetch more payloads */
21
+ fetchMorePayloads?: () => void
22
+ /** set number of schema parts to display starting from the end */
20
23
  maxSchemaDepth?: number
21
- count?: number | null
22
- loading?: boolean
24
+ /** Total number of payloads passed */
25
+ count?: number
23
26
  }
24
27
 
25
- export const PayloadTable: React.FC<PayloadTableProps> = ({
26
- exploreDomain,
27
- archive,
28
- onRowClick,
29
- fetchMorePayloads,
30
- rowsPerPage: rowsPerPageProp = 25,
31
- payloads,
32
- children,
33
- columns = payloadTableColumnConfigDefaults(),
34
- maxSchemaDepth,
35
- count,
36
- loading = false,
37
- variant = 'scrollable',
38
- ...props
39
- }) => {
40
- const breakPoint = useBreakpoint()
41
- const [page, setPage] = useState(0)
42
- const [rowsPerPage, setRowsPerPage] = useState(rowsPerPageProp)
43
- const [payloadCount, setPayloadCount] = useState(0)
28
+ export const PayloadTableWithRef = forwardRef<HTMLTableElement, PayloadTableProps>(
29
+ (
30
+ {
31
+ exploreDomain,
32
+ archive,
33
+ onRowClick,
34
+ fetchMorePayloads,
35
+ rowsPerPage: rowsPerPageProp = 25,
36
+ payloads,
37
+ children,
38
+ columns = payloadTableColumnConfigDefaults(),
39
+ maxSchemaDepth,
40
+ count = 0,
41
+ loading = false,
42
+ variant = 'scrollable',
43
+ ...props
44
+ },
45
+ ref,
46
+ ) => {
47
+ const breakPoint = useBreakpoint()
48
+ const [page, setPage] = useState(0)
49
+ const [rowsPerPage, setRowsPerPage] = useState(rowsPerPageProp)
50
+ const [visiblePayloads, setVisiblePayloads] = useState<XyoPayload[]>([])
44
51
 
45
- const visiblePayloads = useMemo(() => payloads?.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage), [payloads, rowsPerPage, page])
46
- // Avoid a layout jump when reaching the last page with empty rows.
47
- const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - payloadCount || 0) : 0
52
+ // Avoid a layout jump when reaching the last page with empty rows.
53
+ const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - count || 0) : 0
48
54
 
49
- // when the count changes but the payload reference does not, update the count manually
50
- useEffect(() => {
51
- setPayloadCount(payloads !== undefined ? payloads?.length ?? 0 : 0)
52
- }, [count, payloads])
55
+ useEffect(() => {
56
+ setRowsPerPage(rowsPerPageProp)
57
+ }, [rowsPerPageProp])
53
58
 
54
- useEffect(() => {
55
- setRowsPerPage(rowsPerPageProp)
56
- }, [rowsPerPageProp])
59
+ // React to various prop changes to derive new visible payloads
60
+ // count is needed to show initial payloads added async to the same payloads reference
61
+ useEffect(() => {
62
+ if (payloads) {
63
+ setVisiblePayloads(payloads.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage))
64
+ }
65
+ }, [count, page, payloads, rowsPerPage])
57
66
 
58
- // If the payload reference changes, assume we have a new list and reset current page
59
- useEffect(() => {
60
- setPage(0)
61
- }, [payloads])
67
+ // If the payload reference changes, assume we have a new list and reset current page
68
+ useEffect(() => {
69
+ setPage(0)
70
+ }, [payloads])
62
71
 
63
- const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, newPage: number) => {
64
- if (fetchMorePayloads) {
65
- const buffer = rowsPerPage * 2
66
- const lastVisiblePayload = visiblePayloads?.at(-1)
67
- if (lastVisiblePayload) {
68
- const lastVisibleIndex = payloads?.indexOf(lastVisiblePayload)
69
- if (payloads && lastVisibleIndex !== undefined && payloads?.length - (lastVisibleIndex + 1) <= buffer) {
70
- fetchMorePayloads()
72
+ const handleAdditionalPayloads = () => {
73
+ if (fetchMorePayloads && payloads) {
74
+ const buffer = rowsPerPage * 2
75
+ const lastVisiblePayload = visiblePayloads?.at(-1)
76
+ if (lastVisiblePayload) {
77
+ const lastVisibleIndex = payloads?.indexOf(lastVisiblePayload)
78
+ if (lastVisibleIndex !== undefined && payloads.length - (lastVisibleIndex + 1) <= buffer) {
79
+ fetchMorePayloads()
80
+ }
71
81
  }
72
82
  }
73
83
  }
74
- setPage(newPage)
75
- }
76
84
 
77
- const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
78
- setRowsPerPage(parseInt(event.target.value, 10))
79
- setPage(0)
80
- }
85
+ const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, newPage: number) => {
86
+ handleAdditionalPayloads()
87
+ setPage(newPage)
88
+ }
89
+
90
+ const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
91
+ setRowsPerPage(parseInt(event.target.value, 10))
92
+ setPage(0)
93
+ }
81
94
 
82
- return breakPoint ? (
83
- <TableEx variant={variant} {...props}>
84
- <TableHead>
85
- <TableRow>
86
- {columns[breakPoint]?.map((column, index) => {
95
+ return breakPoint ? (
96
+ <TableEx variant={variant} ref={ref} {...props}>
97
+ <TableHead>
98
+ <TableRow>
99
+ {columns[breakPoint]?.map((column, index) => {
100
+ return (
101
+ <TableCell key={index} width={index === 0 ? '100%' : undefined} align={index === 0 ? 'left' : 'center'}>
102
+ <Typography variant="body2" noWrap>
103
+ {payloadColumnNames[column]}
104
+ </Typography>
105
+ </TableCell>
106
+ )
107
+ })}
108
+ </TableRow>
109
+ </TableHead>
110
+ <TableBody>
111
+ {visiblePayloads?.map((payload, index) => {
112
+ const wrapper = new PayloadWrapper(payload)
87
113
  return (
88
- <TableCell key={index} width={index === 0 ? '100%' : undefined} align={index === 0 ? 'left' : 'center'}>
89
- <Typography variant="body2" noWrap>
90
- {payloadColumnNames[column]}
91
- </Typography>
92
- </TableCell>
114
+ <XyoApiThrownErrorBoundary
115
+ key={`${wrapper.hash}-${index}`}
116
+ errorComponent={(e: Error) => (
117
+ <Alert severity="error">
118
+ Error Loading Payload: <Typography fontWeight="bold">{e.message}</Typography>
119
+ </Alert>
120
+ )}
121
+ >
122
+ <PayloadTableRow
123
+ maxSchemaDepth={maxSchemaDepth}
124
+ archive={archive}
125
+ onClick={
126
+ onRowClick
127
+ ? () => {
128
+ onRowClick(payload)
129
+ }
130
+ : undefined
131
+ }
132
+ exploreDomain={exploreDomain}
133
+ payload={payload}
134
+ />
135
+ </XyoApiThrownErrorBoundary>
93
136
  )
94
137
  })}
95
- </TableRow>
96
- </TableHead>
97
- <TableBody>
98
- {visiblePayloads?.map((payload, index) => {
99
- const wrapper = new PayloadWrapper(payload)
100
- return (
101
- <XyoApiThrownErrorBoundary
102
- key={`${wrapper.hash}-${index}`}
103
- errorComponent={(e: Error) => (
104
- <Alert severity="error">
105
- Error Loading Payload: <Typography fontWeight="bold">{e.message}</Typography>
106
- </Alert>
107
- )}
108
- >
109
- <PayloadTableRow
110
- maxSchemaDepth={maxSchemaDepth}
111
- archive={archive}
112
- onClick={
113
- onRowClick
114
- ? () => {
115
- onRowClick(payload)
116
- }
117
- : undefined
118
- }
119
- exploreDomain={exploreDomain}
120
- payload={payload}
121
- />
122
- </XyoApiThrownErrorBoundary>
123
- )
124
- })}
125
- {children}
126
- {emptyRows > 0 && Array(emptyRows).fill(<PayloadTableRow />)}
127
- </TableBody>
128
- <TableFooterEx variant={variant}>
129
- <TableRow>
130
- <StyledTablePagination
131
- rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
132
- count={payloadCount}
133
- rowsPerPage={rowsPerPage}
134
- page={page}
135
- SelectProps={{
136
- inputProps: {
137
- 'aria-label': 'rows per page',
138
- },
139
- native: true,
140
- }}
141
- onPageChange={handleChangePage}
142
- onRowsPerPageChange={handleChangeRowsPerPage}
143
- ActionsComponent={(props) => <TablePaginationActions enableNextPage={!!fetchMorePayloads} loading={loading} {...props} />}
144
- />
145
- </TableRow>
146
- </TableFooterEx>
147
- </TableEx>
148
- ) : null
149
- }
138
+ {children}
139
+ {emptyRows > 0 && Array(emptyRows).fill(<PayloadTableRow />)}
140
+ </TableBody>
141
+ <TableFooterEx variant={variant}>
142
+ <TableRow>
143
+ <StyledTablePagination
144
+ rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
145
+ count={count ?? 0}
146
+ rowsPerPage={rowsPerPage}
147
+ page={page}
148
+ SelectProps={{
149
+ inputProps: {
150
+ 'aria-label': 'rows per page',
151
+ },
152
+ native: true,
153
+ }}
154
+ onPageChange={handleChangePage}
155
+ onRowsPerPageChange={handleChangeRowsPerPage}
156
+ ActionsComponent={(props) => <TablePaginationActions enableNextPage={!!fetchMorePayloads} loading={loading} {...props} />}
157
+ />
158
+ </TableRow>
159
+ </TableFooterEx>
160
+ </TableEx>
161
+ ) : null
162
+ },
163
+ )
164
+
165
+ PayloadTableWithRef.displayName = 'PayloadTable'
166
+
167
+ export const PayloadTable = PayloadTableWithRef
150
168
 
151
169
  const StyledTablePagination = styled(TablePagination)(({ theme }) => ({
152
170
  '& > .MuiToolbar-root': {
@@ -3,6 +3,9 @@ import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'
3
3
  import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'
4
4
  import LastPageIcon from '@mui/icons-material/LastPage'
5
5
  import { Box, CircularProgress, IconButton, useTheme } from '@mui/material'
6
+ import { useXyoEvent } from '@xyo-network/react-event'
7
+
8
+ import { PaginationNouns } from './types'
6
9
 
7
10
  interface TablePaginationActionsProps {
8
11
  count: number
@@ -15,20 +18,25 @@ interface TablePaginationActionsProps {
15
18
 
16
19
  export function TablePaginationActions({ count, page, rowsPerPage, onPageChange, enableNextPage, loading }: TablePaginationActionsProps) {
17
20
  const theme = useTheme()
21
+ const [paginationRef, paginationDispatch] = useXyoEvent<HTMLButtonElement, PaginationNouns>()
18
22
 
19
23
  const handleFirstPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
24
+ paginationDispatch('firstPage', 'click', 'true')
20
25
  onPageChange(event, 0)
21
26
  }
22
27
 
23
28
  const handleBackButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
29
+ paginationDispatch('previousPage', 'click', (page - 1)?.toString())
24
30
  onPageChange(event, page - 1)
25
31
  }
26
32
 
27
33
  const handleNextButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
34
+ paginationDispatch('nextPage', 'click', (page + 1)?.toString())
28
35
  onPageChange(event, page + 1)
29
36
  }
30
37
 
31
38
  const handleLastPageButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
39
+ paginationDispatch('lastPage', 'click', 'true')
32
40
  onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1))
33
41
  }
34
42
 
@@ -39,10 +47,15 @@ export function TablePaginationActions({ count, page, rowsPerPage, onPageChange,
39
47
  <IconButton onClick={handleFirstPageButtonClick} disabled={page === 0} aria-label="first page">
40
48
  {theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
41
49
  </IconButton>
42
- <IconButton onClick={handleBackButtonClick} disabled={page === 0} aria-label="previous page">
50
+ <IconButton ref={paginationRef} onClick={handleBackButtonClick} disabled={page === 0} aria-label="previous page">
43
51
  {theme.direction === 'rtl' ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
44
52
  </IconButton>
45
- <IconButton onClick={handleNextButtonClick} disabled={!enableNextPage && page >= Math.ceil(count / rowsPerPage) - 1} aria-label="next page">
53
+ <IconButton
54
+ ref={paginationRef}
55
+ onClick={handleNextButtonClick}
56
+ disabled={!enableNextPage && page >= Math.ceil(count / rowsPerPage) - 1}
57
+ aria-label="next page"
58
+ >
46
59
  {theme.direction === 'rtl' ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
47
60
  </IconButton>
48
61
  <IconButton onClick={handleLastPageButtonClick} disabled={page >= Math.ceil(count / rowsPerPage) - 1} aria-label="last page">
@@ -1,3 +1,5 @@
1
1
  export * from './PayloadTableColumnConfig'
2
2
  export * from './Table'
3
+ export * from './TablePagination'
3
4
  export * from './TableRow'
5
+ export * from './types'
@@ -0,0 +1 @@
1
+ export type PaginationNouns = 'nextPage' | 'previousPage' | 'firstPage' | 'lastPage'
@@ -0,0 +1 @@
1
+ export * from './PaginationEventNouns'