react-table#useTable TypeScript Examples
The following examples show how to use
react-table#useTable.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: table.container.tsx From master-frontend-lemoncode with MIT License | 5 votes |
TableContainer: React.FunctionComponent<Props> = props => {
const { className } = props;
const labels = { ...createEmptyLabelProps(), ...props.labels };
const columns = React.useMemo(
() => mapColumnListFromStringToColumn(props.columns),
[props.columns]
);
const data = React.useMemo(() => props.rows, [props.rows]);
const {
getTableProps,
headerGroups,
rows,
prepareRow,
page,
gotoPage,
pageOptions,
state,
} = useTable(
{
columns,
data,
initialState: { pageSize: props.pageSize } as any,
},
usePagination
) as TableProps;
const { pageIndex } = state as any;
const {
isOpen,
itemToDelete,
onOpenDialog,
onClose,
onAccept,
} = useConfirmationDialog();
const handleDelete = () => {
if (props.onDelete) {
props.onDelete(itemToDelete.id);
onAccept();
}
};
return (
<TableComponent
className={className}
tableProps={{ ...getTableProps() }}
headerGroups={headerGroups}
rows={props.enablePagination ? page : rows}
prepareRow={prepareRow}
rowRenderer={rowProps =>
props.rowRenderer({
...rowProps,
onEdit: props.onEdit,
onDelete: Boolean(props.onDelete) ? onOpenDialog : undefined,
})
}
labels={labels}
enableSearch={props.enableSearch}
search={props.search}
onSearch={props.onSearch}
enablePagination={Boolean(
props.enablePagination && pageOptions.length > 1
)}
pageIndex={pageIndex}
pageCount={pageOptions.length}
goToPage={gotoPage}
onCreate={props.onCreate}
onDelete={Boolean(props.onDelete) ? handleDelete : undefined}
isOpenConfirmation={isOpen}
onCloseConfirmation={onClose}
itemToDeleteName={itemToDelete.name}
/>
);
}
Example #2
Source File: Table.tsx From korona-info with MIT License | 5 votes |
Table: React.FC<TableProps> = ({ columns, data, height }) => {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable({
columns,
data
});
// Render the UI for your table
return (
<Styles style={{ height: `${height}px`, overflowY: 'scroll' }}>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</Styles>
);
}
Example #3
Source File: Table.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
Table: FC<Props> = memo(({ data, height, onCellClick, width, columnMinWidth }) => {
const theme = useTheme();
const [ref, headerRowMeasurements] = useMeasure();
const tableStyles = getTableStyles(theme);
const { getTableProps, headerGroups, rows, prepareRow } = useTable(
{
columns: useMemo(() => getColumns(data, width, columnMinWidth ?? 150), [data, width, columnMinWidth]),
data: useMemo(() => getTableRows(data), [data]),
},
useSortBy,
useBlockLayout
);
const RenderRow = React.useCallback(
({ index, style }) => {
const row = rows[index];
prepareRow(row);
return (
<div {...row.getRowProps({ style })} className={tableStyles.row}>
{row.cells.map((cell: Cell, index: number) => (
<TableCell
key={index}
field={data.fields[cell.column.index]}
tableStyles={tableStyles}
cell={cell}
onCellClick={onCellClick}
/>
))}
</div>
);
},
[prepareRow, rows]
);
let totalWidth = 0;
for (const headerGroup of headerGroups) {
for (const header of headerGroup.headers) {
totalWidth += header.width as number;
}
}
return (
<div {...getTableProps()} className={tableStyles.table}>
<CustomScrollbar>
<div>
{headerGroups.map((headerGroup: any) => (
<div className={tableStyles.thead} {...headerGroup.getHeaderGroupProps()} ref={ref}>
{headerGroup.headers.map((column: any) =>
renderHeaderCell(column, tableStyles.headerCell, data.fields[column.index])
)}
</div>
))}
</div>
<FixedSizeList
height={height - headerRowMeasurements.height}
itemCount={rows.length}
itemSize={tableStyles.rowHeight}
width={totalWidth ?? width}
style={{ overflow: 'hidden auto' }}
>
{RenderRow}
</FixedSizeList>
</CustomScrollbar>
</div>
);
})
Example #4
Source File: FeTable.tsx From frontegg-react with MIT License | 5 votes |
FeTable: FC<TableProps> = <T extends object>(props: TableProps<T>) => { const tableRef = useRef<HTMLDivElement>(null); const firstRender = useRef<boolean>(true); const columns = useMemo(() => { const columns = props.columns.map( ({ sortable, Filter, Header, ...rest }) => ({ ...rest, disableSortBy: !sortable, disableFilters: !Filter, Filter, Header: Header ?? <div style={{ minWidth: rest.minWidth, maxWidth: rest.maxWidth }} />, } as FeTableColumnOptions<T>) ); if (props.expandable) { columns.unshift({ id: 'fe-expander', minWidth: 60, maxWidth: '60px' as any, Header: <div style={{ minWidth: '2rem', maxWidth: '2rem' }} />, Cell: (cell: Cell<T>) => { const row = cell.row as Row<T> & UseExpandedRowProps<T>; return ( <FeButton className={classNames('fe-table__expand-button', { 'is-expanded': row.isExpanded })} {...row.getToggleRowExpandedProps()} variant={row.isExpanded ? 'primary' : undefined} > <FeIcon name='right-arrow' /> </FeButton> ); }, }); } if (props.selection) { columns.unshift({ id: 'fe-selection', minWidth: 60, maxWidth: '60px' as any, Cell: (cell: Cell<T>) => { const row = cell.row as Row<T> & UseRowSelectRowProps<T>; return ( <FeCheckbox {...row.getToggleRowSelectedProps()} checked={row.isSelected} onChange={(e) => onRowSelected(row.original, e.target.checked)} /> ); }, }); } return columns as Column<T>[]; }, [props.columns, props.expandable]); const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, state, // The page controls ;) page, canPreviousPage, canNextPage, pageOptions, pageCount, gotoPage, nextPage, previousPage, setPageSize, // select props toggleAllRowsSelected, isAllRowsSelected, selectedFlatRows, toggleRowSelected, } = useTable( { columns, data: props.data, getRowId: (row: any) => row[props.rowKey], manualSortBy: !!props.onSortChange, manualFilters: !!props.onFilterChange, manualPagination: !!props.onPageChange, manualRowSelectedKey: props.rowKey, pageCount: !!props.onPageChange ? props.pageCount : undefined, autoResetPage: !props.onPageChange, useControlledState: (state1: any, meta) => ({ ...state1, sortBy: props.sortBy ?? state1.sortBy, filters: props.filters ?? state1.filters, selectedRowIds: props.selectedRowIds ?? state1.selectedRowIds, } as FeTableState<T>), expandSubRows: false, autoResetExpanded: false, initialState: { pageIndex: 0, pageSize: props.pageSize, selectedRowIds: props.selectedRowIds || {}, }, } as FeUseTable<T>, useFilters, useSortBy, useExpanded, usePagination, useRowSelect, useFlexLayout ) as FeTableInstance<T>; checkTableProps(props); const tableState = state as UseSortByState<T> & UseFiltersState<T> & UsePaginationState<T> & UseRowSelectState<T>; const onSortChange = useCallback( (column: FeTableColumnProps<T>) => { if (props.hasOwnProperty('sortBy')) { const sortBy = props.isMultiSort ? tableState.sortBy.filter(({ id }) => id !== column.id) : []; if (!column.isSorted) { sortBy.push({ id: column.id, desc: false }); } else if (!column.isSortedDesc) { sortBy.push({ id: column.id, desc: true }); } props.onSortChange?.(sortBy); } else { if (column.isSorted && column.isSortedDesc) { column.clearSortBy(); } else { column.toggleSortBy(column.isSorted, props.isMultiSort ?? false); } } }, [props.onSortChange] ); const onFilterChange = useCallback( (column: FeTableColumnProps<T>, filterValue?: any) => { if (props.hasOwnProperty('filters')) { const filters = tableState.filters.filter(({ id }) => id !== column.id); if (filterValue != null) { filters.push({ id: column.id, value: filterValue }); } props.onFilterChange?.(filters); } else { column.setFilter(filterValue); } }, [props.onFilterChange, tableState] ); const onToggleAllRowsSelected = useCallback( (value: boolean) => { if (props.hasOwnProperty('selectedRowIds')) { const selectedIds = props.data.reduce((p, n: any) => ({ ...p, [n[props.rowKey]]: true }), {}); props.onRowSelected?.(value ? selectedIds : {}); } else { toggleAllRowsSelected(value); } }, [props.onRowSelected] ); const onRowSelected = useCallback( (row: any, value: boolean) => { const id = row[props.rowKey]; if (props.hasOwnProperty('selectedRowIds')) { const newSelectedRows: any = { ...props.selectedRowIds }; if (value) { newSelectedRows[id] = true; } else { delete newSelectedRows[id]; } props.onRowSelected?.(newSelectedRows); } else { toggleRowSelected(id, value); } }, [props.onRowSelected] ); const handleOnPageChange = useCallback(() => { if (pagination === 'pages') { tableRef.current?.querySelector(`.${prefixCls}__tbody`)?.scroll?.({ top: 0, left: 0, behavior: 'smooth' }); } props.onPageChange?.(tableState.pageSize, tableState.pageIndex); }, [tableState.pageIndex]); useEffect(() => { !props.hasOwnProperty('sortBy') && props.onSortChange?.(tableState.sortBy); }, [props.sortBy, tableState.sortBy]); useEffect(() => { !props.hasOwnProperty('filters') && props.onFilterChange?.(tableState.filters); }, [props.filters, tableState.filters]); useEffect(() => { firstRender.current ? (firstRender.current = false) : handleOnPageChange(); }, [tableState.pageIndex]); useEffect(() => { !props.hasOwnProperty('selectedRowIds') && props.onRowSelected?.(tableState.selectedRowIds as any); }, [tableState.selectedRowIds]); const tableHeadProps: FeTableTHeadProps<T> = { prefixCls, headerGroups, onSortChange, onFilterChange, toggleAllRowsSelected, isAllRowsSelected, selectedFlatRows, }; const tableRows: (Row<T> & UseExpandedRowProps<T>)[] = useMemo( () => (props.pagination ? page : rows) as (Row<T> & UseExpandedRowProps<T>)[], [page, rows, props.pagination] ); const tablePaginationProps: FeTablePaginationProps<T> = { pageIndex: tableState.pageIndex, pageSize: tableState.pageSize, canPreviousPage, canNextPage, pageOptions, pageCount, gotoPage, nextPage, previousPage, setPageSize, }; const { className, toolbar, loading, pagination, pageSize } = props; return ( <div className='fe-table__container'> <div ref={tableRef} className={classNames(prefixCls, className)} {...getTableProps()}> {toolbar && <FeTableToolbar />} <div className={classNames( `${prefixCls}__table-container`, loading && pagination === 'pages' && `${prefixCls}__table-container-loading` )} > <FeTableTBody pageSize={pageSize} pagination={pagination} onInfiniteScroll={handleOnPageChange} loading={props.loading} prefixCls={prefixCls} prepareRow={prepareRow} getTableBodyProps={getTableBodyProps} renderExpandedComponent={props.renderExpandedComponent} rows={tableRows} /> <FeTableTHead {...tableHeadProps} /> </div> {loading && pagination === 'pages' && rows.length > 0 && <FeLoader center size={24} />} {pagination === 'pages' && <FeTablePagination {...tablePaginationProps} />} </div> </div> ); }
Example #5
Source File: Table.tsx From solo with MIT License | 5 votes |
Table = <T extends object>({ columns, data, initialSortBy, manualPagination = true, manualSortBy = true, pageCount = 0, fetchData, onSelectedRowsChange, renderSubComponent, renderPagination, renderFilterControls }: TableProps<T>) => { const stateReducer = (nextState: any, action: any, prevState: any) => { if (action.type === "toggleSortBy" || action.type === "setGlobalFilter") { return { ...nextState, pageIndex: 0 }; } return nextState; }; const instance = useTable( { columns, data, manualGlobalFilter: true, disableMultiSort: true, manualSortBy, manualPagination, autoResetSortBy: !manualSortBy, pageCount, stateReducer, initialState: { sortBy: initialSortBy ?? [], pageSize: 20, globalFilter: [] } }, useGlobalFilter, useSortBy, useExpanded, usePagination, useRowSelect ); const { state: { sortBy, pageIndex, globalFilter, selectedRowIds } } = instance; useEffect(() => { fetchData && fetchData({ sort: sortBy, page: pageIndex + 1, filters: globalFilter }); }, [fetchData, sortBy, globalFilter, pageIndex]); useEffect(() => { onSelectedRowsChange && onSelectedRowsChange(instance); }, [selectedRowIds, instance, onSelectedRowsChange]); return ( <> {renderFilterControls && renderFilterControls(instance)} <USWDSTable {...instance.getTableProps()}> <TableHead headerGroups={instance.headerGroups} /> <TableBody {...instance} renderSubComponent={renderSubComponent} /> </USWDSTable> {renderPagination && renderPagination(instance)} </> ); }
Example #6
Source File: Table.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 5 votes |
Table = <T extends object>(props: TableProps<T>) => { const { columns, data, initialSortBy, initialFilterBy, pageCount = 0, pageSize = 20, fetchData, disableFilters = false, renderPagination, renderExpanded, tableRef } = props; const stateReducer = (nextState: any, action: any, prevState: any) => { if (action.type === 'toggleSortBy' || action.type === 'setGlobalFilter') { return { ...nextState, pageIndex: 0 }; } return nextState; }; const instance = useTable( { columns, data, disableMultiSort: true, manualSortBy: Boolean(fetchData), manualPagination: Boolean(fetchData), manualFilters: Boolean(fetchData), autoResetSortBy: false, disableFilters, pageCount, stateReducer, initialState: { sortBy: initialSortBy ?? [], pageSize, pageIndex: 0, filters: initialFilterBy ?? [], globalFilter: [] } }, useFilters, useSortBy, useExpanded, usePagination ); useImperativeHandle(tableRef, () => instance); const { state: { sortBy, pageIndex, filters } } = instance; useEffect(() => { fetchData && fetchData({ sort: sortBy, page: pageIndex + 1, pageSize, filters }); }, [fetchData, sortBy, filters, pageIndex, pageSize]); return ( <div className={classes.root}> <div className={classes.tableInner}> <UsaTable {...instance.getTableProps()} bordered={false}> <TableHead<T> {...instance} /> <TableBody<T> {...instance} renderExpanded={renderExpanded} /> </UsaTable> </div> {props.noResults && ( <NoResults message={props.noResultsMessage!}></NoResults> )} {renderPagination && renderPagination(instance)} </div> ); }
Example #7
Source File: RawQueryFetcher.tsx From companion-kit with MIT License | 5 votes |
function Table({ columns, data }: TableOptions<{}>) {
// Use the state and functions returned from useTable to build your UI
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data,
});
// Render the UI for your table
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(
(row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>;
})}
</tr>
);
},
)}
</tbody>
</table>
);
}
Example #8
Source File: DisplayTable.tsx From devex with GNU General Public License v3.0 | 5 votes |
DisplayTable: React.FC<IDisplayTableParams<DsBlockObj | TxBlockObj | TransactionDetails | TransactionStatus>> =
({ columns, data }) => {
const { getTableProps, headerGroups, rows, prepareRow } = useTable<DsBlockObj | TxBlockObj | TransactionDetails | TransactionStatus>({
columns,
data,
})
return (
<div className='display-table'>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup: HeaderGroup<DsBlockObj | TxBlockObj | TransactionDetails | TransactionStatus>) => (
<tr {...headerGroup.getHeaderGroupProps()} key={headerGroup.getHeaderGroupProps().key}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps()}
key={column.getHeaderProps().key}
id={column.id}
>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody>
{rows.map((row: Row<DsBlockObj | TxBlockObj | TransactionDetails | TransactionStatus >) => {
prepareRow(row)
return (
<tr {...row.getRowProps()} key={row.getRowProps().key}>
{row.cells.map((cell: Cell<DsBlockObj | TxBlockObj | TransactionDetails | TransactionStatus>) => {
return (
<td {...cell.getCellProps()}
key={cell.getCellProps().key}>
{cell.render('Cell')}
</td>
)
})}
</tr>
);
}
)}
</tbody>
</table>
</div>
);
}
Example #9
Source File: index.tsx From livepeer-com with MIT License | 4 votes |
Table = <T extends Record<string, unknown>>({ columns, data, header, pageSize = 100, rowSelection, onRowSelectionChange, initialSortBy, filters, showOverflow, }: Props<T>) => { const someColumnCanSort = useMemo(() => { // To see if we show the sort help tooltip or not // @ts-ignore return columns.some((column) => !column.disableSortBy); }, [columns]); const getRowId = useCallback((row, relativeIndex, parent) => { return row?.id ? row.id : relativeIndex; }, []); const { getTableProps, getTableBodyProps, prepareRow, headerGroups, // @ts-ignore page, // @ts-ignore nextPage, // @ts-ignore previousPage, // @ts-ignore canPreviousPage, // @ts-ignore canNextPage, // @ts-ignore toggleAllRowsSelected, // @ts-ignore selectedFlatRows, // @ts-ignore setFilter, // @ts-ignore state: { filters: currentFilters }, } = useTable( { // @ts-ignore columns, data, getRowId, initialState: { // @ts-ignore pageSize, pageIndex: 0, ...(initialSortBy ? { sortBy: initialSortBy } : undefined), }, manualSortBy: false, autoResetFilters: false, autoResetSortBy: false, autoResetPage: false, autoResetSelectedRows: false, }, useFilters, useSortBy, usePagination, useRowSelect, (hooks) => { if (rowSelection) { const isIndividualSelection = rowSelection === "individual"; hooks.visibleColumns.push((columns) => [ // Let's make a column for selection { id: "selection", // The header can use the table's getToggleAllRowsSelectedProps method // to render a checkbox // @ts-ignore Header: ({ getToggleAllPageRowsSelectedProps }) => { const props = getToggleAllPageRowsSelectedProps(); return isIndividualSelection ? null : ( <Checkbox onClick={props.onChange} value={props.checked} /> ); }, // The cell can use the individual row's getToggleRowSelectedProps method // to the render a checkbox Cell: ({ row }) => { return ( <Checkbox // @ts-ignore value={row.isSelected} onClick={() => { isIndividualSelection && toggleAllRowsSelected(false); // @ts-ignore row.toggleRowSelected(!row.isSelected); }} /> ); }, }, ...columns, ]); } } ); useEffect(() => { onRowSelectionChange?.(selectedFlatRows); }, [selectedFlatRows, onRowSelectionChange]); return ( <div> {header || filters ? ( <Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", mb: 3, }}> <div>{header}</div> {filters ? ( <Box sx={{ flex: "1", display: "flex", alignItems: "center", justifyContent: "flex-end", }}> {filters.map((f) => { let filter: JSX.Element; switch (f.type) { case "text": filter = ( <TextFilter {...f.props} setFilter={setFilter} currentFilters={currentFilters} /> ); break; case "checkbox": filter = ( <CheckboxFilter {...f.props} setFilter={setFilter} currentFilters={currentFilters} /> ); break; default: return null; } return ( <Box key={`${f.type}-${f.props.columnId}`} sx={{ ":not(:last-of-type)": { mr: 3 } }}> {filter} </Box> ); })} </Box> ) : null} </Box> ) : null} <Box sx={{ overflow: showOverflow ? "visible" : "hidden" }}> <Box sx={{ overflowX: showOverflow ? "visible" : "auto" }}> <Box as="table" {...getTableProps()} sx={{ minWidth: "100%", borderCollapse: "separate", borderSpacing: 0, }}> <thead> {headerGroups.map((headerGroup) => ( <Box as="tr" {...headerGroup.getHeaderGroupProps()} sx={{ borderRadius: "8px" }}> {headerGroup.headers.map((column, i) => { const withHelpTooltip = someColumnCanSort && i === headerGroup.headers.length - 1; return ( <Box as="th" scope="col" {...column.getHeaderProps( // @ts-ignore column.getSortByToggleProps() )} sx={{ textTransform: "uppercase", bg: "rgba(0,0,0,.03)", border: 0, borderBottom: "1px solid", borderTop: "1px solid", borderColor: "muted", fontSize: 0, color: "gray", px: 4, py: 2, fontWeight: 400, position: "relative", "&:first-of-type": { borderLeft: "1px solid", borderColor: "muted", borderTopLeftRadius: 6, borderBottomLeftRadius: 6, }, "&:last-of-type": { borderRight: "1px solid", borderColor: "muted", borderTopRightRadius: 6, borderBottomRightRadius: 6, }, }}> <Box sx={{ display: "flex", alignItems: "center", mr: withHelpTooltip ? 3 : 0, }}> <Box as="span" sx={{ whiteSpace: "nowrap" }}> {column.render("Header")} </Box> {/*@ts-ignore */} {column.canSort && ( <Box as="span" sx={{ ml: 2 }}> {/* @ts-ignore */} {column.isSorted ? // @ts-ignore column.isSortedDesc ? " ⭣" : " ⭡" : " ⭥"} </Box> )} </Box> {withHelpTooltip && ( <Box sx={{ alignItems: "center", display: "flex", position: "absolute", right: 3, top: "50%", transform: "translateY(-50%)", }}> <ReactTooltip id={`tooltip-multiorder`} className="tooltip" place="top" type="dark" effect="solid"> To multi-sort (sort by two column simultaneously) hold shift while clicking on second column name. </ReactTooltip> <Help data-tip data-for={`tooltip-multiorder`} sx={{ cursor: "pointer", ml: 1, }} /> </Box> )} </Box> ); })} </Box> ))} </thead> <tbody {...getTableBodyProps()}> {page.map((row: Row<object>) => { prepareRow(row); return ( <tr {...row.getRowProps()}> {row.cells.map((cell) => ( <Box as="td" {...cell.getCellProps()} sx={{ px: 4, py: 3, border: 0, borderBottom: "1px solid", borderBottomColor: "muted", bg: "background", fontSize: 1, }}> {cell.render("Cell")} </Box> ))} </tr> ); })} </tbody> </Box> </Box> <Paginator canPreviousPage={canPreviousPage} canNextPage={canNextPage} onPreviousPage={previousPage} onNextPage={nextPage} /> </Box> </div> ); }
Example #10
Source File: index.tsx From polkabtc-ui with Apache License 2.0 | 4 votes |
VaultScoresTable = ({
challengeTime
}: Props): JSX.Element => {
const { polkaBtcLoaded } = useSelector((state: StoreType) => state.general);
const statsApi = usePolkabtcStats();
const [data, setData] = React.useState<PatchedVaultData[]>([]);
const [status, setStatus] = React.useState(STATUSES.IDLE);
const [error, setError] = React.useState<Error | null>(null);
const { t } = useTranslation();
// TODO: should add an abort-controller
React.useEffect(() => {
// TODO: should follow `<AuthenticatedApp />` vs. `<UnauthenticatedApp />` approach
// - (Re: https://kentcdodds.com/blog/authentication-in-react-applications)
if (!polkaBtcLoaded) return;
if (!statsApi) return;
(async () => {
try {
setStatus(STATUSES.PENDING);
const response = await statsApi.getChallengeVaults(challengeTime);
const sortedVaults = response.data.sort((a, b) => b.lifetime_sla - a.lifetime_sla);
const transformedVaults = sortedVaults.map(vault => ({
...vault,
lifetime_sla: Number(vault.lifetime_sla).toFixed(2)
}));
setStatus(STATUSES.RESOLVED);
setData(transformedVaults);
} catch (error) {
setStatus(STATUSES.REJECTED);
setError(error);
}
})();
}, [
polkaBtcLoaded,
challengeTime,
statsApi
]);
const columns = React.useMemo(
() => [
// TODO: should type properly
{
Header: t('leaderboard.account_id'),
accessor: 'id',
Filter: DefaultColumnFilter,
classNames: [
'text-left'
]
},
{
Header: `${t('leaderboard.collateral')} (DOT)`,
accessor: 'collateral',
classNames: [
'text-right'
]
},
{
Header: t('leaderboard.request_issue_count'),
accessor: 'request_issue_count',
classNames: [
'text-right'
]
},
{
Header: t('leaderboard.execute_issue_count'),
accessor: 'execute_issue_count',
classNames: [
'text-right'
]
},
{
Header: t('leaderboard.request_redeem_count'),
accessor: 'request_redeem_count',
classNames: [
'text-right'
]
},
{
Header: t('leaderboard.execute_redeem_count'),
accessor: 'execute_redeem_count',
classNames: [
'text-right'
]
},
{
Header: t('leaderboard.lifetime_sla'),
accessor: 'lifetime_sla',
Filter: NumberRangeColumnFilter,
filter: 'between',
classNames: [
'text-right'
]
}
],
[t]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable(
{
columns,
data
},
useFilters,
useGlobalFilter,
useSortBy
);
if (status === STATUSES.IDLE || status === STATUSES.PENDING) {
return (
<div
className={clsx(
'flex',
'justify-center'
)}>
<EllipsisLoader dotClassName='bg-interlayTreePoppy-400' />
</div>
);
}
if (status === STATUSES.REJECTED && error) {
return (
<ErrorHandler error={error} />
);
}
if (status === STATUSES.RESOLVED) {
// TODO: should optimize re-renders https://kentcdodds.com/blog/optimize-react-re-renders
return (
<InterlayTableContainer>
<InterlayTable {...getTableProps()}>
<InterlayThead>
{headerGroups.map(headerGroup => (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
// eslint-disable-next-line react/jsx-key
<InterlayTh
{...column.getHeaderProps([
{
className: clsx(column.classNames),
style: column.style
},
column.getSortByToggleProps()
])}>
<SortByContainer>
<span>{column.render('Header')}</span>
<SortBy
isSorted={column.isSorted}
isSortedDesc={column.isSortedDesc} />
</SortByContainer>
{column.canFilter && column.Filter && (
<div>{column.render('Filter', { placeholder: 'Search by Account ID' })}</div>
)}
</InterlayTh>
))}
</InterlayTr>
))}
</InterlayThead>
<InterlayTbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
// eslint-disable-next-line react/jsx-key
<InterlayTd
{...cell.getCellProps([
{
className: clsx(cell.column.classNames),
style: cell.column.style
}
])}>
{cell.render('Cell')}
</InterlayTd>
);
})}
</InterlayTr>
);
})}
</InterlayTbody>
</InterlayTable>
</InterlayTableContainer>
);
}
return null;
}
Example #11
Source File: index.tsx From livepeer-com with MIT License | 4 votes |
DataTableComponent = <T extends Record<string, unknown>>({ columns, header, rowSelection, initialSortBy, filterItems, showOverflow, cursor = "default", stateSetter, state, tableData, selectAction, createAction, emptyState, viewAll, noPagination = false, border = false, tableLayout = "fixed", }: DataTableProps<T>) => { const { isLoading, data } = tableData; const dataMemo = useMemo(() => data?.rows ?? [], [data?.rows]); const someColumnCanSort = useMemo(() => { // To see if we show the sort help tooltip or not // @ts-ignore return columns.some((column) => !column.disableSortBy); }, [columns]); const getRowId = useCallback((row, relativeIndex) => { return row?.id ? row.id : relativeIndex; }, []); const { getTableProps, getTableBodyProps, prepareRow, headerGroups, rows, // @ts-ignore toggleAllRowsSelected, // @ts-ignore selectedFlatRows, // @ts-ignore state: { sortBy }, } = useTable( { // @ts-ignore columns, getRowId, data: dataMemo, initialState: { // @ts-ignore pageSize: state.pageSize, pageIndex: 0, ...(initialSortBy ? { sortBy: initialSortBy } : undefined), }, disableSortBy: !!viewAll, manualSortBy: false, autoResetFilters: false, autoResetSortBy: false, autoResetPage: false, autoResetSelectedRows: false, }, useSortBy, useRowSelect, (hooks) => { if (rowSelection) { const isIndividualSelection = rowSelection === "individual"; hooks.visibleColumns.push((columns) => [ // Let's make a column for selection { id: "selection", // The header can use the table's getToggleAllRowsSelectedProps method // to render a checkbox Header: ({ // @ts-ignore getToggleAllRowsSelectedProps, // @ts-ignore isAllRowsSelected, }) => { if (isIndividualSelection) return null; const props = getToggleAllRowsSelectedProps(); return ( <Checkbox css={{ display: "flex" }} onClick={props.onChange} value="toggle-all" checked={isAllRowsSelected ? true : false} /> ); }, // The cell can use the individual row's getToggleRowSelectedProps method // to the render a checkbox Cell: ({ row }) => { return ( <Checkbox css={{ display: "flex" }} // @ts-ignore value={row.isSelected} // @ts-ignore checked={row.isSelected} onClick={() => { isIndividualSelection && toggleAllRowsSelected(false); // @ts-ignore row.toggleRowSelected(!row.isSelected); }} /> ); }, }, ...columns, ]); } } ); useEffect(() => { stateSetter.setSelectedRows(selectedFlatRows); }, [selectedFlatRows, stateSetter.setSelectedRows]); useEffect(() => { const order = sortBy?.map((o) => `${o.id}-${o.desc}`).join(",") ?? ""; stateSetter.setOrder(order); }, [sortBy, stateSetter.setOrder]); useEffect(() => { stateSetter.setNextCursor(data?.nextCursor); }, [data?.nextCursor, stateSetter.setNextCursor]); const handlePreviousPage = useCallback(() => { stateSetter.setNextCursor(state.cursor); // current cursor will be next const prevCursorsClone = [...state.prevCursors]; const newCursor = prevCursorsClone.pop(); stateSetter.setCursor(newCursor); stateSetter.setPrevCursors([...prevCursorsClone]); }, [ stateSetter.setNextCursor, stateSetter.setCursor, stateSetter.setPrevCursors, state.prevCursors, state.cursor, ]); const handleNextPage = useCallback(() => { stateSetter.setPrevCursors((p) => [...p, state.cursor]); stateSetter.setCursor(state.nextCursor); stateSetter.setNextCursor(""); }, [ stateSetter.setPrevCursors, stateSetter.setCursor, stateSetter.setNextCursor, state.nextCursor, state.cursor, ]); const onSetFilters = (e) => { stateSetter.setCursor(""); stateSetter.setPrevCursors([]); stateSetter.setFilters(e); }; return ( <Box> <Flex align="end" justify="between" css={{ mb: "$3", borderBottom: "1px solid", borderColor: border ? "$neutral5" : "transparent", pb: border ? "$2" : 0, }}> <Box>{header}</Box> <Flex css={{ alignItems: "center" }}> {state.selectedRows.length ? ( <Flex css={{ ai: "center" }}> <Flex css={{ ai: "center", mr: "$3" }}> <Box css={{ fontSize: "$2", color: "$primary9" }}> {state.selectedRows.length} selected </Box> <Box css={{ height: 18, width: "1px", bc: "$primary7", mx: "$3" }} /> <Box css={{ cursor: "pointer", fontSize: "$2", color: "$violet11", }} onClick={() => toggleAllRowsSelected(false)}> Deselect </Box> </Flex> {selectAction && ( <Button size="2" // @ts-ignore css={{ display: "flex", alignItems: "center", }} {...selectAction} /> )} </Flex> ) : ( <> {!viewAll && filterItems && ( <TableFilter items={filterItems} onDone={(e) => onSetFilters(e)} /> )} {createAction && ( <Button variant="primary" size="2" // @ts-ignore css={{ display: "flex", alignItems: "center" }} {...createAction} /> )} </> )} </Flex> </Flex> {isLoading ? ( <Flex align="center" justify="center" css={{ height: "calc(100vh - 400px)" }}> <Spinner /> </Flex> ) : !data?.count ? ( !JSON.parse(state.stringifiedFilters).length ? ( emptyState ) : ( <Flex direction="column" justify="center" css={{ margin: "0 auto", height: "calc(100vh - 400px)", maxWidth: 300, }}> <Heading css={{ fontWeight: 500, mb: "$3" }}> No results found </Heading> <Text variant="gray" css={{ lineHeight: 1.5, mb: "$3" }}> There aren't any results for that query. </Text> </Flex> ) ) : ( <Box css={{ overflow: showOverflow ? "visible" : "hidden" }}> <Box css={{ overflowX: showOverflow ? "visible" : "auto" }}> <Table {...getTableProps()}> <Thead> {headerGroups.map((headerGroup) => ( <Tr {...headerGroup.getHeaderGroupProps()}> {headerGroup.headers.map((column, i) => { const withHelpTooltip = someColumnCanSort && i === headerGroup.headers.length - 1; return ( <Td as={i === 0 ? Th : Td} scope="col" css={{ pl: i === 0 ? "$1" : 0, width: i === 0 && rowSelection === "all" ? "30px" : "auto", }} {...column.getHeaderProps( // @ts-ignore column.getSortByToggleProps() )}> <Flex css={{ ai: "center", mr: withHelpTooltip ? "$3" : 0, }}> <Box css={{ fontSize: "$2", whiteSpace: "nowrap" }}> {column.render("Header")} </Box> {/*@ts-ignore */} {column.canSort && ( <Box css={{ ml: "$2" }}> {/* @ts-ignore */} {column.isSorted ? // @ts-ignore column.isSortedDesc ? " ⭣" : " ⭡" : " ⭥"} </Box> )} </Flex> </Td> ); })} </Tr> ))} </Thead> <Tbody {...getTableBodyProps()}> {rows.map((row: Row<object>) => { prepareRow(row); return ( <Tr css={{ "&:hover": { backgroundColor: "$neutral2", cursor, }, }} {...row.getRowProps()}> {row.cells.map((cell, i) => ( <Td as={i === 0 ? Th : Td} css={{ py: 0, width: i === 0 && rowSelection === "all" ? "30px" : "auto", ...cell.value?.css, }} {...cell.getCellProps()}> {cell.value?.href ? ( <Link href={cell.value.href} passHref> <A css={{ textDecoration: "none", py: "$2", pl: i === 0 ? "$1" : 0, display: "block", "&:hover": { textDecoration: "none", }, }}> {cell.render("Cell")} </A> </Link> ) : ( <Box css={{ py: "$2", pl: i === 0 ? "$1" : 0 }}> {cell.render("Cell")} </Box> )} </Td> ))} </Tr> ); })} </Tbody> </Table> </Box> {!noPagination && ( <Flex justify="between" align="center" css={{ mt: "$4", p: "$1" }}> <Text> <b>{data?.count}</b> results </Text> {viewAll ? ( <Link href={viewAll} passHref> <A variant="primary" css={{ display: "flex", ai: "center" }}> <Box>View all</Box> <ArrowRightIcon /> </A> </Link> ) : ( <Flex> <Button css={{ marginRight: "6px" }} onClick={handlePreviousPage} disabled={state.prevCursors.length <= 0}> Previous </Button> <Button onClick={handleNextPage} disabled={ state.nextCursor === "" || // @ts-ignore state.pageSize >= parseFloat(data?.count) }> Next </Button> </Flex> )} </Flex> )} </Box> )} </Box> ); }
Example #12
Source File: add-product-table.tsx From admin with MIT License | 4 votes |
AddProductsTable: React.FC<AddProductsTableProps> = ({
existingRelations,
onSubmit,
onClose,
}) => {
const PAGE_SIZE = 10
const [query, setQuery] = useState("")
const [offset, setOffset] = useState(0)
const [numPages, setNumPages] = useState(0)
const [currentPage, setCurrentPage] = useState(0)
const [selectedProducts, setSelectedProducts] = useState<any[]>([])
const [removedProducts, setRemovedProducts] = useState<any[]>([])
const debouncedSearchTerm = useDebounce(query, 500)
const { isLoading, count, products } = useAdminProducts({
q: debouncedSearchTerm,
limit: PAGE_SIZE,
offset,
})
const columns = useCollectionProductColumns()
const {
rows,
prepareRow,
getTableBodyProps,
getTableProps,
canPreviousPage,
canNextPage,
pageCount,
nextPage,
previousPage,
state: { pageIndex, pageSize, selectedRowIds },
} = useTable(
{
data: products || [],
columns: columns,
manualPagination: true,
initialState: {
pageIndex: currentPage,
pageSize: PAGE_SIZE,
selectedRowIds: existingRelations.reduce((prev, { id }) => {
prev[id] = true
return prev
}, {}),
},
pageCount: numPages,
autoResetSelectedRows: false,
autoResetPage: false,
getRowId: (row) => row.id,
},
usePagination,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
{
id: "selection",
Cell: ({ row }) => {
return (
<Table.Cell className="w-[5%] pl-base">
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</Table.Cell>
)
},
},
...columns,
])
}
)
useEffect(() => {
setSelectedProducts((selectedProducts) =>
[
...selectedProducts.filter(
(sv) =>
Object.keys(selectedRowIds).findIndex((id) => id === sv.id) > -1
),
...(products?.filter(
(p) =>
selectedProducts.findIndex((sv) => sv.id === p.id) < 0 &&
Object.keys(selectedRowIds).findIndex((id) => id === p.id) > -1
) || []),
].filter((p) => existingRelations.findIndex((ap) => ap.id === p.id) < 0)
)
setRemovedProducts([
...existingRelations.filter(
(ap) => Object.keys(selectedRowIds).findIndex((id) => id === ap.id) < 0
),
])
}, [selectedRowIds])
useEffect(() => {
const controlledPageCount = Math.ceil(count! / PAGE_SIZE)
setNumPages(controlledPageCount)
}, [products, count, PAGE_SIZE])
const handleNext = () => {
if (canNextPage) {
setOffset((old) => old + pageSize)
setCurrentPage((old) => old + 1)
nextPage()
}
}
const handlePrev = () => {
if (canPreviousPage) {
setOffset((old) => old - pageSize)
setCurrentPage((old) => old - 1)
previousPage()
}
}
const handleSearch = (q) => {
setOffset(0)
setQuery(q)
}
const [disabled, setDisabled] = useState(true)
useEffect(() => {
if (selectedProducts.length > 0 || removedProducts.length > 0) {
setDisabled(false)
return
}
setDisabled(true)
}, [selectedProducts, removedProducts])
const handleSubmit = () => {
onSubmit(
selectedProducts.map((p) => p.id),
removedProducts.map((p) => p.id)
)
}
return (
<Modal handleClose={onClose}>
<Modal.Body>
<Modal.Header handleClose={onClose}>
<h3 className="inter-xlarge-semibold">Add Products</h3>
</Modal.Header>
<Modal.Content>
<div className="w-full flex flex-col justify-between h-[650px]">
<Table
enableSearch
handleSearch={handleSearch}
searchPlaceholder="Search Products"
{...getTableProps()}
className="flex-grow"
>
{isLoading || !products ? (
<div className="inter-small-regular text-grey-40 flex flex-grow justify-center items-center">
<Spinner size="large" variant="secondary" />
</div>
) : (
<Table.Body {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row)
return (
<Table.Row
color={"inherit"}
{...row.getRowProps()}
className="px-base"
>
{row.cells.map((cell, index) => {
return cell.render("Cell", { index })
})}
</Table.Row>
)
})}
</Table.Body>
)}
</Table>
<TablePagination
count={count!}
limit={PAGE_SIZE}
offset={offset}
pageSize={offset + rows.length}
title="Products"
currentPage={pageIndex + 1}
pageCount={pageCount}
nextPage={handleNext}
prevPage={handlePrev}
hasNext={canNextPage}
hasPrev={canPreviousPage}
/>
</div>
</Modal.Content>
<Modal.Footer>
<div className="flex items-center justify-end gap-x-xsmall w-full">
<Button
variant="ghost"
size="small"
className="w-eventButton"
onClick={onClose}
>
Cancel
</Button>
<Button
variant="primary"
size="small"
className="w-eventButton"
onClick={handleSubmit}
disabled={disabled}
>
Save
</Button>
</div>
</Modal.Footer>
</Modal.Body>
</Modal>
)
}
Example #13
Source File: ProposalTable.tsx From mysterium-vpn-desktop with MIT License | 4 votes |
Table: React.FC<TableProps> = observer(function Table({ columns, data }) {
const { proposals, filters } = useStores()
const defaultColumn = React.useMemo(
() => ({
width: 50,
}),
[],
)
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, setHiddenColumns, state } =
useTable<UIProposal>(
{
columns,
data,
defaultColumn,
autoResetSortBy: false,
initialState: {
sortBy: [{ id: "countryName" }, { id: "qualityLevel", desc: true }],
hiddenColumns: filters.country == null ? hiddenColsAllCountries : hiddenColsSingleCountry,
},
},
useBlockLayout,
useSortBy,
)
useEffect(() => {
if (filters.country == null) {
if (state.hiddenColumns != hiddenColsAllCountries) {
setHiddenColumns(hiddenColsAllCountries)
}
} else {
if (state.hiddenColumns != hiddenColsSingleCountry) {
setHiddenColumns(hiddenColsSingleCountry)
}
}
}, [filters.country])
const listRef = useRef<FixedSizeList>(null)
useEffect(() => {
if (proposals.suggestion) {
const idx = rows.findIndex((row) => row.original.providerId === proposals.suggestion?.providerId)
if (idx != -1) {
listRef.current?.scrollToItem(idx, "center")
}
}
}, [proposals.suggestion, data])
const renderRow = React.useCallback(
({ index, style }: { index: number; style: CSSProperties }): JSX.Element => {
return <RowRenderer prepareRow={prepareRow} rows={rows} index={index} style={style} />
},
[prepareRow, rows],
)
return (
<div className="table" {...getTableProps()}>
<div className="thead">
{headerGroups.map((headerGroup) => {
const { style, key, ...restHeaderGroupProps } = headerGroup.getHeaderGroupProps()
return (
<div key={key} className="tr" style={{ ...style, width: "100%" }} {...restHeaderGroupProps}>
{headerGroup.headers.map((column) => {
const { key, ...restHeaderProps } = column.getHeaderProps(
column.getSortByToggleProps({
title: column.canSort ? `Sort by ${column.Header}` : undefined,
}),
)
return (
<div
key={key}
className={`th ${
column.isSorted ? (column.isSortedDesc ? "sorted-desc" : "sorted-asc") : ""
}`}
{...restHeaderProps}
>
{column.render("Header")}
</div>
)
})}
</div>
)
})}
</div>
<div className="tbody" {...getTableBodyProps()}>
<AutoSizer>
{({ width, height }): JSX.Element => (
<FixedSizeList
itemCount={data.length}
itemSize={30}
width={width}
height={height}
ref={listRef}
>
{renderRow}
</FixedSizeList>
)}
</AutoSizer>
</div>
</div>
)
})
Example #14
Source File: MinerTable.tsx From devex with GNU General Public License v3.0 | 4 votes |
MinerTable: React.FC<IMinerTableParams> = ({ addresses }) => {
const generatePagination = useCallback((currentPage: number, pageCount: number, delta = 1) => {
const separate = (a: number, b: number, isLower: boolean) => {
const temp = b - a
if (temp === 0)
return [a]
else if (temp === 1)
return [a, b]
else if (temp === 2)
return [a, a + 1, b]
else
return [a, isLower ? -1 : -2, b]
}
return Array(delta * 2 + 1)
.fill(0)
.map((_, index) => currentPage - delta + index)
.filter(page => 0 < page && page <= pageCount)
.flatMap((page, index, { length }) => {
if (!index) {
return separate(1, page, true)
}
if (index === length - 1) {
return separate(page, pageCount, false)
}
return [page]
})
}, [])
const columns = useMemo(
() => [{
id: 'address-col',
Header: 'Addresses',
accessor: 'address',
// eslint-disable-next-line react/display-name
Cell: (props: Cell<IMinerObj>) =>
(<>
[{props.row.index}]
{' '}
<QueryPreservingLink to={`/address/${pubKeyToZilAddr(props.value)}`}>{pubKeyToZilAddr(props.value)}</QueryPreservingLink>
</>)
}], []
) as Array<Column<IMinerObj>>
const data = useMemo(() => (addresses.map((x) => ({ address: x })) as IMinerObj[]), [addresses])
const {
getTableProps,
getTableBodyProps,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageCount,
gotoPage,
nextPage,
previousPage,
state: { pageIndex },
} = useTable<IMinerObj>({
columns,
data,
initialState: { pageIndex: 0 },
}, usePagination)
return (
<>
<div className='py-3'>
<table {...getTableProps()}>
<tbody {...getTableBodyProps()}>
{page.map((row: Row<IMinerObj>) => {
prepareRow(row)
return (
<tr {...row.getRowProps()} key={row.getRowProps().key}>
{row.cells.map((cell: Cell<IMinerObj>) => {
return (
<td {...cell.getCellProps()} key={cell.getCellProps().key}>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div>
<div className='mx-auto'>
{data.length !== 0 &&
<Pagination className='viewall-pagination'>
<Pagination.Prev onClick={() => previousPage()} disabled={!canPreviousPage} />
{generatePagination(pageIndex + 1, pageCount).map((page) => {
if (page === -1)
return <Pagination.Ellipsis key={page} onClick={() => gotoPage(pageIndex - 5)} />
else if (page === -2)
return <Pagination.Ellipsis key={page} onClick={() => gotoPage(pageIndex + 5)} />
else if (page === pageIndex + 1)
return <Pagination.Item key={page} active>{page}</Pagination.Item>
else
return <Pagination.Item key={page} onClick={() => gotoPage(Number(page) - 1)}>{page}</Pagination.Item>
})}
<Pagination.Next onClick={() => nextPage()} disabled={!canNextPage} />
</Pagination>
}
</div>
</>
)
}
Example #15
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 4 votes |
VaultsTable = (): JSX.Element => {
const { t } = useTranslation();
const { bridgeLoaded } = useSelector((state: StoreType) => state.general);
const history = useHistory();
const {
isIdle: currentActiveBlockNumberIdle,
isLoading: currentActiveBlockNumberLoading,
data: currentActiveBlockNumber,
error: currentActiveBlockNumberError
} = useQuery<number, Error>([GENERIC_FETCHER, 'system', 'getCurrentActiveBlockNumber'], genericFetcher<number>(), {
enabled: !!bridgeLoaded
});
useErrorHandler(currentActiveBlockNumberError);
const {
isIdle: secureCollateralThresholdIdle,
isLoading: secureCollateralThresholdLoading,
data: secureCollateralThreshold,
error: secureCollateralThresholdError
} = useQuery<Big, Error>(
[GENERIC_FETCHER, 'vaults', 'getSecureCollateralThreshold', COLLATERAL_TOKEN],
genericFetcher<Big>(),
{
enabled: !!bridgeLoaded
}
);
useErrorHandler(secureCollateralThresholdError);
const {
isIdle: liquidationCollateralThresholdIdle,
isLoading: liquidationCollateralThresholdLoading,
data: liquidationCollateralThreshold,
error: liquidationCollateralThresholdError
} = useQuery<Big, Error>(
[GENERIC_FETCHER, 'vaults', 'getLiquidationCollateralThreshold', COLLATERAL_TOKEN],
genericFetcher<Big>(),
{
enabled: !!bridgeLoaded
}
);
useErrorHandler(liquidationCollateralThresholdError);
const {
isIdle: btcToCollateralTokenRateIdle,
isLoading: btcToCollateralTokenRateLoading,
data: btcToCollateralTokenRate,
error: btcToCollateralTokenRateError
} = useQuery<BTCToCollateralTokenRate, Error>(
[GENERIC_FETCHER, 'oracle', 'getExchangeRate', COLLATERAL_TOKEN],
genericFetcher<BTCToCollateralTokenRate>(),
{
enabled: !!bridgeLoaded
}
);
useErrorHandler(btcToCollateralTokenRateError);
const { isIdle: vaultsExtIdle, isLoading: vaultsExtLoading, data: vaultsExt, error: vaultsExtError } = useQuery<
Array<VaultExt<BitcoinUnit>>,
Error
>([GENERIC_FETCHER, 'vaults', 'list'], genericFetcher<Array<VaultExt<BitcoinUnit>>>(), {
enabled: !!bridgeLoaded
});
useErrorHandler(vaultsExtError);
const columns = React.useMemo(
() => [
{
Header: t('account_id'),
accessor: 'vaultId',
classNames: ['text-left'],
Cell: function FormattedCell({ value }: { value: string }) {
return <>{shortAddress(value)}</>;
}
},
{
Header: t('locked_dot', {
collateralTokenSymbol: COLLATERAL_TOKEN_SYMBOL
}),
accessor: 'lockedDOT',
classNames: ['text-right']
},
{
Header: t('locked_btc'),
accessor: 'lockedBTC',
classNames: ['text-right']
},
{
Header: t('pending_btc'),
accessor: 'pendingBTC',
classNames: ['text-right'],
tooltip: t('vault.tip_pending_btc')
},
{
Header: t('collateralization'),
accessor: '',
classNames: ['text-left'],
tooltip: t('vault.tip_collateralization'),
Cell: function FormattedCell({ row: { original } }: { row: { original: Vault } }) {
if (original.unsettledCollateralization === undefined && original.settledCollateralization === undefined) {
return <span>∞</span>;
} else {
return (
<>
{secureCollateralThreshold && (
<div>
<p
className={getCollateralizationColor(
original.settledCollateralization,
secureCollateralThreshold
)}
>
{original.settledCollateralization === undefined
? '∞'
: roundTwoDecimals(original.settledCollateralization.toString()) + '%'}
</p>
<p className='text-xs'>
<span>{t('vault.pending_table_subcell')}</span>
<span
className={getCollateralizationColor(
original.unsettledCollateralization,
secureCollateralThreshold
)}
>
{original.unsettledCollateralization === undefined
? '∞'
: roundTwoDecimals(original.unsettledCollateralization.toString()) + '%'}
</span>
</p>
</div>
)}
</>
);
}
}
},
{
Header: t('status'),
accessor: 'status',
classNames: ['text-left'],
Cell: function FormattedCell({ value }: { value: string }) {
let statusClasses;
if (value === constants.VAULT_STATUS_ACTIVE) {
statusClasses = clsx('text-interlayConifer', 'font-medium');
}
if (value === constants.VAULT_STATUS_UNDER_COLLATERALIZED) {
statusClasses = clsx('text-interlayCalifornia', 'font-medium');
}
if (value === constants.VAULT_STATUS_THEFT || value === constants.VAULT_STATUS_LIQUIDATED) {
statusClasses = clsx('text-interlayCinnabar', 'font-medium');
}
return <span className={statusClasses}>{value}</span>;
}
}
],
[t, secureCollateralThreshold]
);
const vaults: Array<Vault> | undefined = React.useMemo(() => {
if (
vaultsExt &&
btcToCollateralTokenRate &&
liquidationCollateralThreshold &&
secureCollateralThreshold &&
currentActiveBlockNumber
) {
const rawVaults = vaultsExt.map((vaultExt) => {
const statusLabel = getVaultStatusLabel(
vaultExt,
currentActiveBlockNumber,
liquidationCollateralThreshold,
secureCollateralThreshold,
btcToCollateralTokenRate,
t
);
const vaultCollateral = vaultExt.backingCollateral;
const settledTokens = vaultExt.issuedTokens;
const settledCollateralization = getCollateralization(vaultCollateral, settledTokens, btcToCollateralTokenRate);
const unsettledTokens = vaultExt.toBeIssuedTokens;
const unsettledCollateralization = getCollateralization(
vaultCollateral,
unsettledTokens.add(settledTokens),
btcToCollateralTokenRate
);
return {
vaultId: vaultExt.id.accountId.toString(),
// TODO: fetch collateral reserved
lockedBTC: displayMonetaryAmount(settledTokens),
lockedDOT: displayMonetaryAmount(vaultCollateral),
pendingBTC: displayMonetaryAmount(unsettledTokens),
status: statusLabel,
unsettledCollateralization: unsettledCollateralization?.toString(),
settledCollateralization: settledCollateralization?.toString()
};
});
const sortedVaults = rawVaults.sort((vaultA, vaultB) => {
const vaultALockedBTC = vaultA.lockedBTC;
const vaultBLockedBTC = vaultB.lockedBTC;
return vaultALockedBTC < vaultBLockedBTC ? 1 : vaultALockedBTC > vaultBLockedBTC ? -1 : 0;
});
return sortedVaults;
}
}, [
btcToCollateralTokenRate,
currentActiveBlockNumber,
liquidationCollateralThreshold,
secureCollateralThreshold,
t,
vaultsExt
]);
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({
columns,
data: vaults ?? []
});
const renderTable = () => {
if (
currentActiveBlockNumberIdle ||
currentActiveBlockNumberLoading ||
secureCollateralThresholdIdle ||
secureCollateralThresholdLoading ||
liquidationCollateralThresholdIdle ||
liquidationCollateralThresholdLoading ||
btcToCollateralTokenRateIdle ||
btcToCollateralTokenRateLoading ||
vaultsExtIdle ||
vaultsExtLoading
) {
return <PrimaryColorEllipsisLoader />;
}
const handleRowClick = (vaultId: string) => () => {
history.push(PAGES.VAULTS.replace(`:${URL_PARAMETERS.VAULT.ACCOUNT}`, vaultId));
};
return (
<InterlayTable {...getTableProps()}>
<InterlayThead>
{/* TODO: should type properly */}
{headerGroups.map((headerGroup: any) => (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...headerGroup.getHeaderGroupProps()}>
{/* TODO: should type properly */}
{headerGroup.headers.map((column: any) => (
// eslint-disable-next-line react/jsx-key
<InterlayTh
{...column.getHeaderProps([
{
className: clsx(column.classNames),
style: column.style
}
])}
>
{column.render('Header')}
{column.tooltip && (
<InformationTooltip className={clsx('inline-block', 'ml-1')} label={column.tooltip} />
)}
</InterlayTh>
))}
</InterlayTr>
))}
</InterlayThead>
<InterlayTbody {...getTableBodyProps()}>
{/* TODO: should type properly */}
{rows.map((row: any) => {
prepareRow(row);
const { key, className: rowClassName, ...restRowProps } = row.getRowProps();
return (
<InterlayTr
key={key}
className={clsx(rowClassName, 'cursor-pointer')}
{...restRowProps}
onClick={handleRowClick(row.original.vaultId)}
>
{/* TODO: should type properly */}
{row.cells.map((cell: any) => {
return (
// eslint-disable-next-line react/jsx-key
<InterlayTd
{...cell.getCellProps([
{
className: clsx(cell.column.classNames),
style: cell.column.style
}
])}
>
{cell.render('Cell')}
</InterlayTd>
);
})}
</InterlayTr>
);
})}
</InterlayTbody>
</InterlayTable>
);
};
// TODO: should add pagination
return (
<InterlayTableContainer className='space-y-6'>
<SectionTitle>{t('dashboard.vault.active_vaults')}</SectionTitle>
{renderTable()}
</InterlayTableContainer>
);
}
Example #16
Source File: index.tsx From ke with MIT License | 4 votes |
Table = ({
resourceName,
listFilters,
listFilterTemplates,
columns,
data,
pageCount: controlledPageCount,
setBackendPage,
user,
analytics,
filterable = false,
provider,
}: TableProps): JSX.Element => {
const {
getTableProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
gotoPage,
nextPage,
previousPage,
pageCount,
state: { pageIndex },
} = useTable(
{
columns,
data,
manualPagination: true,
initialState: { pageIndex: 0 },
pageCount: controlledPageCount,
autoResetPage: false,
stateReducer: (newState: TableState, action: ActionType) => {
if (action.type === 'gotoPage' && setBackendPage) {
const newPageIndex = newState.pageIndex
setBackendPage(newPageIndex + 1)
}
return newState
},
},
useFilters,
usePagination
)
return (
<Flex flexDirection="row" width="100%" flex={1} bg="gray.50" p={4}>
<Flex
flexDirection="column"
flex={1}
maxWidth="100%"
bg="white"
width="auto"
rounded="md"
borderWidth="1px"
onClick={() => false}
>
{filterable && listFilters && (
<FilterBlock
listFilters={listFilters}
listFilterTemplates={listFilterTemplates}
user={user}
analytics={analytics}
resourceName={resourceName}
provider={provider}
gotoPage={gotoPage}
/>
)}
<StyledTable {...getTableProps()}>
<TableHead>{mountHeader(headerGroups)}</TableHead>
<Flex flexDirection="column">{mountRows(page, prepareRow)}</Flex>
</StyledTable>
<Bottom
analytics={analytics}
resourceName={resourceName}
pageIndex={pageIndex}
canPreviousPage={canPreviousPage}
canNextPage={canNextPage}
pageOptions={pageOptions}
pageCount={pageCount}
gotoPage={gotoPage}
nextPage={nextPage}
previousPage={previousPage}
/>
</Flex>
</Flex>
)
}
Example #17
Source File: AuditLogTable.tsx From camunda-cockpit-plugins with Apache License 2.0 | 4 votes |
AuditLogTable: React.FC<Props> = ({ activities, decisions }) => { const columns = React.useMemo( () => [ { Header: 'Activity Name', accessor: 'activityName', Cell: ({ value }: any) => { const baseUrl = `${window.location.href.split('#')[0]}/` .replace(/\/+$/, '/') .replace(/\/app\/tasklist\//, '/app/cockpit/'); if (value.activityType === 'businessRuleTask' && decisions.has(value.id)) { return <a href={`${baseUrl}#/decision-instance/${decisions.get(value.id)}`}>{value.activityName}</a>; } else if (value.activityType === 'callActivity' && value.calledProcessInstanceId && value.endTime) { return ( <a href={`${baseUrl}#/history/process-instance/${value.calledProcessInstanceId}`}>{value.activityName}</a> ); } else if (value.activityType === 'callActivity' && value.calledProcessInstanceId) { return ( <a href={`${baseUrl}#/process-instance/${value.calledProcessInstanceId}/runtime`}>{value.activityName}</a> ); } return <Clippy value={value.activityName}>{value.activityName}</Clippy>; }, }, { Header: 'Start Time', accessor: 'startDate', Cell: ({ value }: any) => ( <Clippy value={value ? value.format('YYYY-MM-DDTHH:mm:ss') : value}> {value ? value.format('YYYY-MM-DDTHH:mm:ss') : value} </Clippy> ), }, { Header: 'End Time', accessor: 'endDate', Cell: ({ value }: any) => ( <Clippy value={value ? value.format('YYYY-MM-DDTHH:mm:ss') : value}> {value ? value.format('YYYY-MM-DDTHH:mm:ss') : value} </Clippy> ), }, { Header: 'Duration', accessor: 'duration', Cell: ({ value }: any) => <Clippy value={value}>{value}</Clippy>, }, { Header: 'Type', accessor: 'type', Cell: ({ value }: any) => <Clippy value={value}>{value}</Clippy>, }, { Header: 'User', accessor: 'assignee', Cell: ({ value }: any) => <Clippy value={value}>{value}</Clippy>, }, { Header: 'Canceled', accessor: 'canceled', Cell: ({ value }: any) => <Clippy value={value}>{value}</Clippy>, }, ], [activities, decisions] ); const data = React.useMemo( () => activities.map((activity: any) => { return { activityName: activity, startDate: moment(activity.startTime), endDate: activity.endTime ? moment(activity.endTime) : '', duration: activity.endTime ? asctime(new Date(activity.endTime).getTime() - new Date(activity.startTime).getTime()) : '', type: activity.activityType, assignee: activity.assignee, canceled: activity.canceled ? 'true' : 'false', }; }), [activities, decisions] ); const tableInstance = useTable({ columns: columns as any, data }, useSortBy); const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = tableInstance; return ( <table className="cam-table" {...getTableProps()}> <thead> {headerGroups.map(headerGroup => ( <tr {...headerGroup.getHeaderGroupProps()}> {headerGroup.headers.map(column => ( /* @ts-ignore */ <th {...column.getHeaderProps(column.getSortByToggleProps())}> {column.render('Header')} <span style={{ position: 'absolute', fontSize: '125%' }}> { /* @ts-ignore */ column.isSorted ? ( /* @ts-ignore */ column.isSortedDesc ? ( <GoChevronDown style={{ color: '#155cb5' }} /> ) : ( <GoChevronUp style={{ color: '#155cb5' }} /> ) ) : ( <TiMinus style={{ color: '#155cb5' }} /> ) } </span> </th> ))} </tr> ))} </thead> <tbody {...getTableBodyProps()}> {rows.map(row => { prepareRow(row); return ( <tr {...row.getRowProps()}> {row.cells.map(cell => { return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>; })} </tr> ); })} </tbody> </table> ); }
Example #18
Source File: ViewAllTable.tsx From devex with GNU General Public License v3.0 | 4 votes |
ViewAllTable: React.FC<IViewAllTableParams<DsBlockObj | TxBlockObj | TransactionDetails>> =
({ columns, data, isLoading, fetchData, pageCount: controlledPageCount }) => {
const { getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageCount,
gotoPage,
nextPage,
previousPage,
// Get the state from the instance
state: { pageIndex } } = useTable<DsBlockObj | TxBlockObj | TransactionDetails>({
columns,
data,
initialState: { pageIndex: 0 },
manualPagination: true,
pageCount: controlledPageCount,
}, usePagination)
const fetchDataDebounce = useAsyncDebounce(fetchData, 300)
useEffect(() => {
fetchDataDebounce({ pageIndex })
// fetchDataDebounce changes when fetchData function changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageIndex, fetchData])
const generatePagination = useCallback((currentPage: number, pageCount: number, delta = 2) => {
const separate = (a: number, b: number, isLower: boolean) => {
const temp = b - a
if (temp === 0)
return [a]
else if (temp === 1)
return [a, b]
else if (temp === 2)
return [a, a + 1, b]
else
return [a, isLower ? -1 : -2, b]
}
return Array(delta * 2 + 1)
.fill(0)
.map((_, index) => currentPage - delta + index)
.filter(page => 0 < page && page <= pageCount)
.flatMap((page, index, { length }) => {
if (!index) {
return separate(1, page, true)
}
if (index === length - 1) {
return separate(page, pageCount, false)
}
return [page]
})
}, [])
return (
<>
<BRow>
<BCol className='align-self-center pl-3'>
{data.length === 0
? null
: <span className='subtext'>Items Per Page: <strong>10</strong></span>}
</BCol>
<BCol>
<Pagination className='justify-content-end'>
<Pagination.Prev onClick={() => previousPage()} disabled={!canPreviousPage} />
{generatePagination(pageIndex + 1, pageCount).map((page) => {
if (page === -1)
return <Pagination.Ellipsis key={page} onClick={() => gotoPage(pageIndex - 5)} />
else if (page === -2)
return <Pagination.Ellipsis key={page} onClick={() => gotoPage(pageIndex + 5)} />
else if (page === pageIndex + 1)
return <Pagination.Item key={page} active>{page}</Pagination.Item>
else
return <Pagination.Item key={page} onClick={() => gotoPage(Number(page) - 1)}>{page}</Pagination.Item>
})}
<Pagination.Next onClick={() => nextPage()} disabled={!canNextPage} />
</Pagination>
</BCol>
</BRow>
<div className='viewall-table table'>
{isLoading ? <div className='center-spinner mt-4'><Spinner animation="border" /></div> : null}
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup: HeaderGroup<DsBlockObj | TxBlockObj | TransactionDetails>) => (
<tr {...headerGroup.getHeaderGroupProps()} key={headerGroup.getHeaderGroupProps().key} >
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()} key={column.getHeaderProps().key} id={column.id}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody style={isLoading ? { opacity: '0.5' } : {}}{...getTableBodyProps()}>
{page.map((row: Row<DsBlockObj | TxBlockObj | TransactionDetails>) => {
prepareRow(row)
return (
<tr {...row.getRowProps()} key={row.getRowProps().key}>
{row.cells.map((cell: Cell<DsBlockObj | TxBlockObj | TransactionDetails>) => {
return (
<td {...cell.getCellProps()}
key={cell.getCellProps().key}>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
</table>
</div>
</>
)
}
Example #19
Source File: Table.tsx From frontegg-react with MIT License | 4 votes |
Table: FC<TableProps> = <T extends object>(props: TableProps<T>) => { const classes = useStyles(); const tableRef = useRef<HTMLTableElement>(null); const firstRender = useRef<boolean>(true); const columns = useMemo(() => { const columns = props.columns.map( ({ sortable, Filter, Header, ...rest }) => ({ ...rest, disableSortBy: !sortable, disableFilters: !Filter, Filter, Header: Header ?? <div style={{ minWidth: rest.minWidth, maxWidth: rest.maxWidth }} />, } as FeTableColumnOptions<T>) ); if (props.expandable) { columns.unshift({ id: 'fe-expander', minWidth: 60, maxWidth: '60px' as any, Header: <div style={{ minWidth: '1.5rem', maxWidth: '1.5rem' }} />, Cell: (cell: Cell<T>) => { const row = cell.row as Row<T> & UseExpandedRowProps<T>; return ( <IconButton className={classes.expandIcon} {...row.getToggleRowExpandedProps()}> {row.isExpanded ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />} </IconButton> ); }, }); } if (props.selection) { columns.unshift({ id: 'fe-selection', minWidth: 60, maxWidth: '60px' as any, Cell: (cell: Cell<T>) => { const row = cell.row as Row<T> & UseRowSelectRowProps<T>; return ( <Checkbox className={classes.checkBox} {...row.getToggleRowSelectedProps()} checked={row.isSelected} onChange={(e) => onRowSelected(row.original, e.target.checked)} /> ); }, }); } return columns as Column<T>[]; }, [props.columns, props.expandable]); const tableHooks: PluginHook<T>[] = [useFilters, useSortBy]; if (props.expandable) { tableHooks.push(useExpanded); } if (props.pagination) { tableHooks.push(usePagination); } if (props.selection) { tableHooks.push(useRowSelect); } tableHooks.push(useFlexLayout); const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, state, // The page controls ;) page, // canPreviousPage, // canNextPage, pageOptions, pageCount, gotoPage, nextPage, previousPage, // setPageSize, // select props toggleAllRowsSelected, isAllRowsSelected, selectedFlatRows, toggleRowSelected, } = useTable( { columns, data: props.data, getRowId: (row: any) => row[props.rowKey], manualSortBy: !!props.onSortChange, manualFilters: !!props.onFilterChange, manualPagination: !!props.onPageChange, manualRowSelectedKey: props.rowKey, pageCount: !!props.onPageChange ? props.pageCount : undefined, autoResetPage: !props.onPageChange, useControlledState: (state1: any) => { return { ...state1, sortBy: props.sortBy ?? state1.sortBy, filters: props.filters ?? state1.filters, selectedRowIds: props.selectedRowIds ?? state1.selectedRowIds, } as TableState<T> & UseFiltersState<T> & UseSortByState<T> & UseRowSelectState<T>; }, expandSubRows: false, initialState: { pageIndex: 0, pageSize: props.pageSize ?? 0, selectedRowIds: props.selectedRowIds || {}, }, } as UseTableOptions<T> & UseFiltersOptions<T> & UseSortByOptions<T> & UseExpandedOptions<T> & UseRowSelectOptions<T> & UsePaginationOptions<T>, ...tableHooks ) as TableInstance<T> & UseTableInstanceProps<T> & UsePaginationInstanceProps<T> & UseRowSelectInstanceProps<T>; if (props.expandable && !props.renderExpandedComponent) { throw Error('FeTable: you must provide renderExpandedComponent property if the table is expandable'); } if (props.hasOwnProperty('sortBy') && !props.onSortChange) { throw Error('FeTable: you must provide onSortChange property if sortBy is controlled'); } if (props.hasOwnProperty('filters') && !props.onFilterChange) { throw Error('FeTable: you must provide onFilterChange property if filters is controlled'); } if (props.hasOwnProperty('pagination') && !props.pageSize) { throw Error('FeTable: you must provide pageSize property if pagination enabled'); } if (props.hasOwnProperty('onPageChange') && !props.pageCount) { throw Error('FeTable: you must provide pageCount property if onPageChange is controlled'); } const tableState = state as UseSortByState<T> & UseFiltersState<T> & UsePaginationState<T> & UseRowSelectState<T>; const onSortChange = useCallback( (column: FeTableColumnProps<T>) => { if (props.hasOwnProperty('sortBy')) { const sortBy = props.isMultiSort ? tableState.sortBy.filter(({ id }) => id !== column.id) : []; if (!column.isSorted) { sortBy.push({ id: column.id, desc: false }); } else if (!column.isSortedDesc) { sortBy.push({ id: column.id, desc: true }); } props.onSortChange?.(sortBy); } else { if (column.isSorted && column.isSortedDesc) { column.clearSortBy(); } else { column.toggleSortBy(column.isSorted, props.isMultiSort ?? false); } } }, [props.onSortChange] ); const onFilterChange = useCallback( (column: FeTableColumnProps<T>, filterValue?: any) => { if (props.hasOwnProperty('filters')) { const filters = tableState.filters.filter(({ id }) => id !== column.id); if (filterValue != null) { filters.push({ id: column.id, value: filterValue }); } props.onFilterChange?.(filters); } else { column.setFilter(filterValue); } }, [props.onFilterChange, tableState] ); const onToggleAllRowsSelected = useCallback( (value: boolean) => { if (props.hasOwnProperty('selectedRowIds')) { const selectedIds = props.data.reduce((p, n: any) => ({ ...p, [n[props.rowKey]]: true }), {}); props.onRowSelected?.(value ? selectedIds : {}); } else { toggleAllRowsSelected(value); } }, [props.onRowSelected] ); const onRowSelected = useCallback( (row: any, value: boolean) => { const id = row[props.rowKey]; if (props.hasOwnProperty('selectedRowIds')) { const newSelectedRows: any = { ...props.selectedRowIds }; if (value) { newSelectedRows[id] = true; } else { delete newSelectedRows[id]; } props.onRowSelected?.(newSelectedRows); } else { toggleRowSelected(id, value); } }, [props.onRowSelected] ); const handleOnPageChange = useCallback(() => { if (pagination === 'pages') { tableRef.current?.scroll?.({ top: 0, left: 0, behavior: 'smooth' }); } props.onPageChange?.(tableState.pageSize, tableState.pageIndex); }, [tableState.pageIndex]); useEffect(() => { !props.hasOwnProperty('sortBy') && props.onSortChange?.(tableState.sortBy); }, [props.sortBy, tableState.sortBy]); useEffect(() => { !props.hasOwnProperty('filters') && props.onFilterChange?.(tableState.filters); }, [props.filters, tableState.filters]); useEffect(() => { firstRender.current ? (firstRender.current = false) : handleOnPageChange(); }, [tableState.pageIndex]); useEffect(() => { !props.hasOwnProperty('selectedRowIds') && props.onRowSelected?.(tableState.selectedRowIds as any); }, [tableState.selectedRowIds]); const onPageChangeHandler = (page: number) => { if (page > tableState.pageIndex) { nextPage(); } else { previousPage(); } }; const { className, loading, pagination, totalData, pageSize } = props; return ( <Paper ref={tableRef} className={classes.paper}> <MaUTable className={classNames(classes.table, className)} {...getTableProps()}> <TableHead headerGroups={headerGroups} onSortChange={onSortChange} onFilterChange={onFilterChange} toggleAllRowsSelected={onToggleAllRowsSelected} isAllRowsSelected={isAllRowsSelected} selectedFlatRows={selectedFlatRows} /> <TableBody pageSize={pageSize} pagination={pagination} getTableBodyProps={getTableBodyProps} prepareRow={prepareRow} loading={loading} rows={(pagination ? page : rows) as (Row<T> & UseExpandedRowProps<T>)[]} renderExpandedComponent={props.renderExpandedComponent} onInfiniteScroll={handleOnPageChange} /> </MaUTable> {loading && pagination === 'pages' && rows.length > 0 && <Loader center size={24} />} {pagination === 'pages' && ( <TablePagination className={classes.footer} rowsPerPageOptions={[]} component='div' count={totalData || rows.length} rowsPerPage={tableState.pageSize} page={tableState.pageIndex} onChangePage={(e, page) => onPageChangeHandler(page)} ActionsComponent={(props) => ( <TablePaginationActions {...props} gotoPage={gotoPage} pageOptions={pageOptions} /> )} /> )} </Paper> ); }
Example #20
Source File: Table.tsx From opensaas with MIT License | 4 votes |
Table: React.FC<TableProps> = <T extends object>(props: TableProps<T>) => { const { expandable, selection, pagination, sortBy, pageCount, data, selectedRowIds, pageSize, rowKey, loading, isMultiSort, renderExpandedComponent, onPageChange, filters: propsFilters, onSortChange: propsOnSortChange, onRowSelected: propsOnRowSelected, onFilterChange: propsOnFilterChange, columns: propsColumns, } = props; const classes = useStyles(); const tableRef = useRef<HTMLTableElement>(null); const hasSortBy = props.hasOwnProperty('sortBy'); const hasFilters = props.hasOwnProperty('filters'); const hasPagination = props.hasOwnProperty('pagination'); const hasOnPageChange = props.hasOwnProperty('onPageChange'); const hasSelectedRowIds = props.hasOwnProperty('selectedRowIds'); const onRowSelected = useCallback( (row: UseRowSelectRowProps<T> & Row<T> & UseTableRowProps<T>, value: boolean) => { const id = (row.original as any)[rowKey]; if (hasSelectedRowIds) { const newSelectedRows: any = { ...selectedRowIds }; if (value) { newSelectedRows[id] = true; } else { delete newSelectedRows[id]; } propsOnRowSelected?.(newSelectedRows); } else { row.toggleRowSelected(value); } }, [hasSelectedRowIds, rowKey, selectedRowIds, propsOnRowSelected], ); const columns = useMemo(() => { const columns = propsColumns.map( ({ sortable, Filter, Header, ...rest }) => ({ ...rest, disableSortBy: !sortable, disableFilters: !Filter, Filter, Header: Header ?? <div style={{ minWidth: rest.minWidth, maxWidth: rest.maxWidth }} />, } as TableColumnOptions<T>), ); if (expandable) { columns.unshift({ id: 'expander', minWidth: 60, maxWidth: '60px' as any, Cell: (cell: UseTableCellProps<T>) => { const row = cell.row as Row<T> & UseExpandedRowProps<T>; return ( <IconButton className={classes.expandIcon} {...row.getToggleRowExpandedProps()}> {row.isExpanded ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />} </IconButton> ); }, }); } if (selection) { columns.unshift({ id: 'selection', minWidth: 60, maxWidth: '60px' as any, Cell: (cell: UseTableCellProps<T>) => { const row = cell.row as UseRowSelectRowProps<T> & Row<T> & UseTableRowProps<T>; return ( <Checkbox className={classes.checkBox} {...row.getToggleRowSelectedProps()} checked={row.isSelected} onChange={(e) => onRowSelected(row, e.target.checked)} /> ); }, }); } return columns as Column<T>[]; }, [propsColumns, expandable, classes, selection, onRowSelected]); const tableHooks: PluginHook<T>[] = [useFilters, useSortBy]; if (expandable) { tableHooks.push(useExpanded); } if (pagination) { tableHooks.push(usePagination); } if (selection) { tableHooks.push(useRowSelect); } tableHooks.push(useFlexLayout); const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, state, page, pageOptions, gotoPage, nextPage, previousPage, toggleAllRowsSelected, isAllRowsSelected, selectedFlatRows, } = useTable( { columns, data, getRowId: (row: any) => row[rowKey], manualSortBy: !!propsOnSortChange, manualFilters: !!propsOnFilterChange, manualPagination: !!onPageChange, manualRowSelectedKey: rowKey, pageCount: pageCount ?? 0, useControlledState: (state1: any) => ({ ...state1, sortBy: sortBy ?? state1.sortBy, filters: propsFilters ?? state1.filters, selectedRowIds: selectedRowIds ?? state1.selectedRowIds, } as TableState<T> & UseFiltersState<T> & UseSortByState<T> & UseRowSelectState<T>), expandSubRows: false, initialState: { pageIndex: 0, pageSize: pageSize ?? 0, selectedRowIds: selectedRowIds || {}, }, } as UseTableOptions<T> & UseFiltersOptions<T> & UseSortByOptions<T> & UseExpandedOptions<T> & UseRowSelectOptions<T> & UsePaginationOptions<T>, ...tableHooks, ) as TableInstance<T> & UseTableInstanceProps<T> & UsePaginationInstanceProps<T> & UseRowSelectInstanceProps<T>; if (expandable && !renderExpandedComponent) { throw Error('Table: you must provide renderExpandedComponent property if the table is expandable'); } if (hasSortBy && !propsOnSortChange) { throw Error('Table: you must provide onSortChange property if sortBy is controlled'); } if (hasFilters && !propsOnFilterChange) { throw Error('Table: you must provide onFilterChange property if filters is controlled'); } if (hasPagination && !pageSize) { throw Error('Table: you must provide pageSize property if pagination enabled'); } if (hasOnPageChange && !pageCount) { throw Error('Table: you must provide pageCount property if onPageChange is controlled'); } const tableState = state as UseSortByState<T> & UseFiltersState<T> & UsePaginationState<T> & UseRowSelectState<T>; const onSortChange = useCallback( (column: TableColumnProps<T>) => { if (hasSortBy) { const sortBy = isMultiSort ? tableState.sortBy.filter(({ id }) => id !== column.id) : []; if (!column.isSorted) { sortBy.push({ id: column.id, desc: false }); } else if (!column.isSortedDesc) { sortBy.push({ id: column.id, desc: true }); } propsOnSortChange?.(sortBy); } else { if (column.isSorted && column.isSortedDesc) { column.clearSortBy(); } else { column.toggleSortBy(column.isSorted, isMultiSort ?? false); } } }, [hasSortBy, isMultiSort, tableState.sortBy, propsOnSortChange], ); const onFilterChange = useCallback( (column: TableColumnProps<T>, filterValue?: any) => { if (hasFilters) { const filters = tableState.filters.filter(({ id }) => id !== column.id); if (filterValue != null) { filters.push({ id: column.id, value: filterValue }); } propsOnFilterChange?.(filters); } else { column.setFilter(filterValue); } }, [propsOnFilterChange, hasFilters, tableState.filters], ); const onToggleAllRowsSelected = useCallback( (value: boolean) => { if (hasSelectedRowIds) { const selectedIds = data.reduce((p, n: any) => ({ ...p, [n[rowKey]]: true }), {}); propsOnRowSelected?.(value ? selectedIds : {}); } else { toggleAllRowsSelected(value); } }, [hasSelectedRowIds, data, rowKey, propsOnRowSelected, toggleAllRowsSelected], ); useEffect(() => { if (!hasSortBy) propsOnSortChange?.(tableState.sortBy); }, [hasSortBy, propsOnSortChange, tableState.sortBy]); useEffect(() => { if (!hasFilters) propsOnFilterChange?.(tableState.filters); }, [hasFilters, propsOnFilterChange, tableState.filters]); useEffect(() => { tableRef.current?.querySelector('.table-tbody')?.scroll?.({ top: 0, left: 0, behavior: 'smooth' }); onPageChange?.(tableState.pageSize, tableState.pageIndex); }, [onPageChange, tableState.pageSize, tableState.pageIndex]); useEffect(() => { if (!hasSelectedRowIds) propsOnRowSelected?.(tableState.selectedRowIds as any); }, [hasSelectedRowIds, propsOnRowSelected, tableState.selectedRowIds]); const onPageChangeHandler = (page: number) => { if (page > tableState.pageIndex) { nextPage(); } else { previousPage(); } }; return ( <Paper className={classes.paper}> <MaterialUITable className={classes.table} ref={tableRef} {...getTableProps()}> <TableHead headerGroups={headerGroups} onSortChange={onSortChange} onFilterChange={onFilterChange} toggleAllRowsSelected={onToggleAllRowsSelected} isAllRowsSelected={isAllRowsSelected} selectedFlatRows={selectedFlatRows} /> <TableBody getTableBodyProps={getTableBodyProps} prepareRow={prepareRow} loading={loading} rows={(pagination ? page : rows) as (Row<T> & UseExpandedRowProps<T>)[]} renderExpandedComponent={renderExpandedComponent} /> </MaterialUITable> {pagination === 'pages' && ( <TablePagination className={classes.footer} rowsPerPageOptions={[]} component='div' count={rows.length} rowsPerPage={tableState.pageSize} page={tableState.pageIndex} onChangePage={(e, page) => onPageChangeHandler(page)} ActionsComponent={(props) => ( <TablePaginationActions {...props} gotoPage={gotoPage} pageOptions={pageOptions} /> )} /> )} </Paper> ); }
Example #21
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 4 votes |
BlocksTable = (): JSX.Element => {
const { t } = useTranslation();
const queryParams = useQueryParams();
const selectedPage = Number(queryParams.get(QUERY_PARAMETERS.PAGE)) || 1;
const selectedPageIndex = selectedPage - 1;
const updateQueryParameters = useUpdateQueryParameters();
const {
isIdle: btcBlocksIdle,
isLoading: btcBlocksLoading,
data: btcBlocks,
error: btcBlocksError
// TODO: should type properly (`Relay`)
} = useQuery<GraphqlReturn<any>, Error>(
[
GRAPHQL_FETCHER,
btcBlocksQuery(),
{
limit: TABLE_PAGE_LIMIT,
offset: selectedPageIndex * TABLE_PAGE_LIMIT
}
],
graphqlFetcher<GraphqlReturn<any>>()
);
useErrorHandler(btcBlocksError);
const {
isIdle: btcBlocksCountIdle,
isLoading: btcBlocksCountLoading,
data: btcBlocksCount,
error: btcBlocksCountError
// TODO: should type properly (`Relay`)
} = useQuery<GraphqlReturn<any>, Error>(
[GRAPHQL_FETCHER, btcBlocksCountQuery()],
graphqlFetcher<GraphqlReturn<any>>()
);
useErrorHandler(btcBlocksCountError);
const columns = React.useMemo(
() => [
{
Header: t('dashboard.relay.block_height'),
accessor: 'backingHeight',
classNames: ['text-right']
},
{
Header: t('dashboard.relay.block_hash'),
accessor: 'blockHash',
classNames: ['text-right'],
Cell: function FormattedCell({ value }: { value: string }) {
const hash = stripHexPrefix(value);
return <ExternalLink href={`${BTC_EXPLORER_BLOCK_API}${hash}`}>{hash}</ExternalLink>;
}
},
{
Header: t('dashboard.relay.inclusion_timestamp'),
accessor: 'timestamp',
classNames: ['text-left'],
Cell: function FormattedCell({ value }: { value: string }) {
return <>{formatDateTimePrecise(new Date(value))}</>;
}
},
{
Header: t('dashboard.relay.inclusion_block'),
accessor: 'relayedAtHeight',
classNames: ['text-right'],
Cell: function FormattedCell({ value }: { value: any }) {
return <>{value.absolute}</>;
}
},
{
Header: t('dashboard.relay.relayed_by'),
accessor: 'relayer',
classNames: ['text-right']
}
],
[t]
);
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({
columns,
data: btcBlocks?.data?.relayedBlocks ?? []
});
if (btcBlocksIdle || btcBlocksLoading || btcBlocksCountIdle || btcBlocksCountLoading) {
return <PrimaryColorEllipsisLoader />;
}
if (!btcBlocks) {
throw new Error('Something went wrong!');
}
if (!btcBlocksCount) {
throw new Error('Something went wrong!');
}
const handlePageChange = ({ selected: newSelectedPageIndex }: { selected: number }) => {
updateQueryParameters({
[QUERY_PARAMETERS.PAGE]: (newSelectedPageIndex + 1).toString()
});
};
const pageCount = Math.ceil((btcBlocksCount.data.relayedBlocksConnection.totalCount || 0) / TABLE_PAGE_LIMIT);
return (
<InterlayTableContainer className={clsx('space-y-6', 'container', 'mx-auto')}>
<SectionTitle>{t('dashboard.relay.blocks')}</SectionTitle>
<InterlayTable {...getTableProps()}>
<InterlayThead>
{headerGroups.map((headerGroup: any) => (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column: any) => (
// eslint-disable-next-line react/jsx-key
<InterlayTh
{...column.getHeaderProps([
{
className: clsx(column.classNames),
style: column.style
}
])}
>
{column.render('Header')}
</InterlayTh>
))}
</InterlayTr>
))}
</InterlayThead>
<InterlayTbody {...getTableBodyProps()}>
{rows.map((row: any) => {
prepareRow(row);
return (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...row.getRowProps()}>
{row.cells.map((cell: any) => {
return (
// eslint-disable-next-line react/jsx-key
<InterlayTd
{...cell.getCellProps([
{
className: clsx(cell.column.classNames),
style: cell.column.style
}
])}
>
{cell.render('Cell')}
</InterlayTd>
);
})}
</InterlayTr>
);
})}
</InterlayTbody>
</InterlayTable>
{pageCount > 0 && (
<div className={clsx('flex', 'justify-end')}>
<InterlayPagination
pageCount={pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={5}
onPageChange={handlePageChange}
forcePage={selectedPageIndex}
/>
</div>
)}
</InterlayTableContainer>
);
}
Example #22
Source File: index.tsx From livepeer-com with MIT License | 4 votes |
CommonAdminTable = ({
id,
loading,
onFetchData,
onRowSelected,
setNextCursor,
columns,
data,
nextCursor,
err,
filtersDesc,
rowsPerPage,
initialSortBy = [],
children,
}: CommonAdminTableProps) => {
const [cursor, setCursor] = useState("");
const [prevCursor, setPrevCursor] = useState([]);
const fetchDataDebounced = useAsyncDebounce(({ sortBy, cursor, filters }) => {
let order;
if (sortBy.length) {
order = sortBy.map((o) => `${o.id}-${o.desc}`).join(",");
}
onFetchData({ order, cursor, filters });
}, 1000);
const dm = useMemo(() => data, [data]);
const tableOptions: any = {
columns,
data: dm,
manualFilters: true,
autoResetSortBy: false,
manualSortBy: true,
maxMultiSortColCount: 2,
initialState: { sortBy: initialSortBy },
};
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
selectedFlatRows,
toggleAllRowsSelected,
setFilter,
state: { filters, sortBy },
rows,
}: any = useTable(
tableOptions,
useFilters,
useSortBy,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ rows }) => {
return (
<>
<Button
variant="secondarySmall"
disabled={prevCursor.length === 0}
sx={{ margin: 0, padding: "2px", px: "4px" }}
onClick={() => {
setNextCursor(cursor);
setCursor(prevCursor.pop());
setPrevCursor([...prevCursor]);
}}>
⭠
</Button>
<Button
variant="secondarySmall"
disabled={rows.length < rowsPerPage || nextCursor === ""}
sx={{ margin: 0, ml: 2, padding: "2px", px: "4px" }}
onClick={() => {
prevCursor.push(cursor);
setPrevCursor([...prevCursor]);
setCursor(nextCursor);
setNextCursor("");
}}>
⭢
</Button>
</>
);
},
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<Checkbox
// @ts-ignore
value={row.isSelected}
onClick={(e) => {
toggleAllRowsSelected(false);
// @ts-ignore
row.toggleRowSelected(!row.isSelected);
}}
/>
</div>
),
},
...columns,
]);
}
);
useEffect(() => {
onRowSelected(selectedFlatRows[0]?.original);
}, [selectedFlatRows]);
useEffect(() => {
setPrevCursor([]);
setNextCursor("");
setCursor("");
}, [sortBy, filters]);
useEffect(() => {
fetchDataDebounced({ sortBy, cursor, filters });
}, [sortBy, cursor, filters]);
return (
<Box
id={id}
sx={{
mb: 0,
mt: 0,
}}>
<Flex
sx={{
justifyContent: "flex-start",
alignItems: "baseline",
my: "1em",
}}>
{children}
{filtersDesc.map((fd) => {
if (typeof fd.render === "function") {
return fd.render({
value: (filters.find((o) => o.id === fd.id) || [])[0]?.value,
setValue: (v) => setFilter(fd.id, v),
});
}
return (
<Input
key={fd.id}
sx={{ width: "10em", ml: "1em" }}
label={`${fd.placeholder || fd.id} filter input`}
value={(filters.find((o) => o.id === fd.id) || [])[0]?.value}
onChange={(e) => setFilter(fd.id, e.target.value)}
placeholder={fd.placeholder}></Input>
);
})}
</Flex>
{err && <Box>{err}</Box>}
<Box>
<Box
as="table"
sx={{
display: "table",
width: "100%",
borderCollapse: "inherit",
borderSpacing: "0",
border: 0,
}}
{...getTableProps()}>
<Box as="thead" sx={{ position: "relative" }}>
{headerGroups.map((headerGroup, i) => (
<Box as="tr" key={i} {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column: any, i) => (
<Box
as="th"
sx={{
userSelect: "none",
fontWeight: "normal",
textTransform: "uppercase",
bg: "rgba(0,0,0,.03)",
borderBottom: "1px solid",
borderTop: "1px solid",
borderColor: "muted",
borderLeft: "0px solid",
borderRight: "0px solid",
fontSize: 0,
color: "gray",
py: 2,
"&:first-of-type": {
borderLeft: "1px solid",
borderColor: "muted",
borderTopLeftRadius: 6,
borderBottomLeftRadius: 6,
},
"&:last-of-type": {
borderRight: "1px solid",
borderColor: "muted",
borderTopRightRadius: 6,
borderBottomRightRadius: 6,
},
}}
align="left"
{...column.getHeaderProps(
column.getSortByToggleProps({ title: "" })
)}
key={i}>
<Flex sx={{ mr: "-18px" }}>
{column.render("Header")}
<span>
{column.canSort &&
(column.isSorted
? column.isSortedDesc
? "⭣"
: "⭡"
: "⭥")}
</span>
{i === headerGroup.headers.length - 1 && (
<Flex
sx={{ alignItems: "center", ml: "auto", mr: "1em" }}>
<Flex>
<ReactTooltip
id={`tooltip-multiorder`}
className="tooltip"
place="top"
type="dark"
effect="solid">
To multi-sort (sort by two column simultaneously)
hold shift while clicking on second column name.
</ReactTooltip>
<Help
data-tip
data-for={`tooltip-multiorder`}
sx={{
cursor: "pointer",
ml: 1,
}}
/>
</Flex>
</Flex>
)}
</Flex>
</Box>
))}
</Box>
))}
{loading && (
<Box
as="tr"
sx={{
height: "0px",
border: 0,
bg: "transparent !important",
margin: 0,
padding: 0,
}}>
<Box
as="th"
sx={{ border: 0, bg: "transparent", margin: 0, padding: 0 }}
colSpan={1000}>
<Box sx={{ width: "100%", position: "relative" }}>
<Box
sx={{
position: "absolute",
top: "-1px",
left: "6px",
right: "0px",
}}>
<Box
sx={{
backgroundColor: "dodgerblue",
height: "1px",
animation: `${loadingAnim} 3s ease-in-out infinite`,
}}
/>
</Box>
</Box>
</Box>
</Box>
)}
</Box>
<tbody {...getTableBodyProps()}>
{rows.map((row: any, rowIndex) => {
prepareRow(row);
return (
<Box
as="tr"
sx={{
bg: "transparent !important",
}}
{...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<Box
as="td"
sx={{
fontSize: 1,
borderBottomColor: "muted",
borderBottomWidth: "1px",
borderBottomStyle: "solid",
borderTop: "0px solid",
borderLeft: "0px solid",
borderRight: "0px solid",
}}
{...cell.getCellProps()}>
{renderCell(cell)}
</Box>
);
})}
</Box>
);
})}
</tbody>
</Box>
</Box>
</Box>
);
}
Example #23
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 4 votes |
IssueRequestsTable = (): JSX.Element => {
const dispatch = useDispatch();
const { t } = useTranslation();
const queryParams = useQueryParams();
const selectedIssueRequestId = queryParams.get(QUERY_PARAMETERS.ISSUE_REQUEST_ID);
const selectedPage = Number(queryParams.get(QUERY_PARAMETERS.ISSUE_REQUESTS_PAGE)) || 1;
const selectedPageIndex = selectedPage - 1;
const updateQueryParameters = useUpdateQueryParameters();
const { address, extensions, bridgeLoaded } = useSelector((state: StoreType) => state.general);
const {
isIdle: btcConfirmationsIdle,
isLoading: btcConfirmationsLoading,
data: btcConfirmations,
error: btcConfirmationsError
} = useQuery<number, Error>(
[GENERIC_FETCHER, 'btcRelay', 'getStableBitcoinConfirmations'],
genericFetcher<number>(),
{
enabled: !!bridgeLoaded
}
);
useErrorHandler(btcConfirmationsError);
const {
isIdle: latestParachainActiveBlockIdle,
isLoading: latestParachainActiveBlockLoading,
data: latestParachainActiveBlock,
error: latestParachainActiveBlockError
} = useQuery<number, Error>([GENERIC_FETCHER, 'system', 'getCurrentActiveBlockNumber'], genericFetcher<number>(), {
enabled: !!bridgeLoaded
});
useErrorHandler(latestParachainActiveBlockError);
const {
isIdle: parachainConfirmationsIdle,
isLoading: parachainConfirmationsLoading,
data: parachainConfirmations,
error: parachainConfirmationsError
} = useQuery<number, Error>(
[GENERIC_FETCHER, 'btcRelay', 'getStableParachainConfirmations'],
genericFetcher<number>(),
{
enabled: !!bridgeLoaded
}
);
useErrorHandler(parachainConfirmationsError);
const {
isIdle: issueRequestsTotalCountIdle,
isLoading: issueRequestsTotalCountLoading,
data: issueRequestsTotalCount,
error: issueRequestsTotalCountError
// TODO: should type properly (`Relay`)
} = useQuery<GraphqlReturn<any>, Error>(
[GRAPHQL_FETCHER, issueCountQuery(`userParachainAddress_eq: "${address}"`)],
graphqlFetcher<GraphqlReturn<any>>()
);
useErrorHandler(issueRequestsTotalCountError);
const {
isIdle: issueRequestsIdle,
isLoading: issueRequestsLoading,
data: issueRequests,
error: issueRequestsError
// TODO: should type properly (`Relay`)
} = useQuery<any, Error>(
[
ISSUE_FETCHER,
selectedPageIndex * TABLE_PAGE_LIMIT, // offset
TABLE_PAGE_LIMIT, // limit
`userParachainAddress_eq: "${address}"` // `WHERE` condition
],
issueFetcher
);
useErrorHandler(issueRequestsError);
const columns = React.useMemo(
() => [
{
Header: t('issue_page.updated'),
classNames: ['text-left'],
// TODO: should type properly (`Relay`)
Cell: function FormattedCell({ row: { original: issue } }: any) {
let date;
if (issue.execution) {
date = issue.execution.timestamp;
} else if (issue.cancellation) {
date = issue.cancellation.timestamp;
} else {
date = issue.request.timestamp;
}
return <>{formatDateTimePrecise(new Date(date))}</>;
}
},
{
Header: `${t('issue_page.amount')} (${WRAPPED_TOKEN_SYMBOL})`,
classNames: ['text-right'],
// TODO: should type properly (`Relay`)
Cell: function FormattedCell({ row: { original: issue } }: any) {
let wrappedTokenAmount;
if (issue.execution) {
wrappedTokenAmount = issue.execution.amountWrapped;
} else {
wrappedTokenAmount = issue.request.amountWrapped;
}
return <>{displayMonetaryAmount(wrappedTokenAmount)}</>;
}
},
{
Header: t('issue_page.btc_transaction'),
classNames: ['text-right'],
// TODO: should type properly (`Relay`)
Cell: function FormattedCell({ row: { original: issueRequest } }: any) {
return (
<>
{issueRequest.backingPayment.btcTxId ? (
<ExternalLink
href={`${BTC_EXPLORER_TRANSACTION_API}${issueRequest.backingPayment.btcTxId}`}
onClick={(event) => {
event.stopPropagation();
}}
>
{shortTxId(issueRequest.backingPayment.btcTxId)}
</ExternalLink>
) : issueRequest.status === IssueStatus.Expired || issueRequest.status === IssueStatus.Cancelled ? (
t('redeem_page.failed')
) : (
`${t('pending')}...`
)}
</>
);
}
},
{
Header: t('issue_page.confirmations'),
classNames: ['text-right'],
// TODO: should type properly (`Relay`)
Cell: function FormattedCell({ row: { original: issue } }: any) {
const value = issue.backingPayment.confirmations;
return <>{value === undefined ? t('not_applicable') : Math.max(value, 0)}</>;
}
},
{
Header: t('status'),
accessor: 'status',
classNames: ['text-left'],
Cell: function FormattedCell({ value }: { value: IssueStatus }) {
let icon;
let notice;
let colorClassName;
switch (value) {
case IssueStatus.RequestedRefund:
case IssueStatus.Completed: {
icon = <FaCheck />;
notice = t('completed');
colorClassName = 'text-interlayConifer';
break;
}
case IssueStatus.Cancelled:
case IssueStatus.Expired: {
icon = <FaRegTimesCircle />;
notice = t('cancelled');
colorClassName = 'text-interlayCinnabar';
break;
}
default: {
icon = <FaRegClock />;
notice = t('pending');
colorClassName = 'text-interlayCalifornia';
break;
}
}
// TODO: double-check with `src\components\UI\InterlayTable\StatusCell\index.tsx`
return (
<div className={clsx('inline-flex', 'items-center', 'space-x-1.5', colorClassName)}>
{icon}
<span>{notice}</span>
</div>
);
}
}
],
[t]
);
const data =
issueRequests === undefined ||
btcConfirmations === undefined ||
parachainConfirmations === undefined ||
latestParachainActiveBlock === undefined
? []
: issueRequests.map(
// TODO: should type properly (`Relay`)
(issueRequest: any) =>
getIssueWithStatus(issueRequest, btcConfirmations, parachainConfirmations, latestParachainActiveBlock)
);
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({
columns,
data
});
if (
btcConfirmationsIdle ||
btcConfirmationsLoading ||
parachainConfirmationsIdle ||
parachainConfirmationsLoading ||
latestParachainActiveBlockIdle ||
latestParachainActiveBlockLoading ||
issueRequestsTotalCountIdle ||
issueRequestsTotalCountLoading ||
issueRequestsIdle ||
issueRequestsLoading
) {
return <PrimaryColorEllipsisLoader />;
}
if (issueRequestsTotalCount === undefined) {
throw new Error('Something went wrong!');
}
const handlePageChange = ({ selected: newSelectedPageIndex }: { selected: number }) => {
updateQueryParameters({
[QUERY_PARAMETERS.ISSUE_REQUESTS_PAGE]: (newSelectedPageIndex + 1).toString()
});
};
const handleIssueModalClose = () => {
updateQueryParameters({
[QUERY_PARAMETERS.ISSUE_REQUEST_ID]: ''
});
};
const handleRowClick = (requestId: string) => () => {
if (extensions.length && address) {
updateQueryParameters({
[QUERY_PARAMETERS.ISSUE_REQUEST_ID]: requestId
});
} else {
dispatch(showAccountModalAction(true));
}
};
const totalSuccessfulIssueCount = issueRequestsTotalCount.data.issuesConnection.totalCount || 0;
const pageCount = Math.ceil(totalSuccessfulIssueCount / TABLE_PAGE_LIMIT);
// TODO: should type properly (`Relay`)
const selectedIssueRequest = data.find((issueRequest: any) => issueRequest.id === selectedIssueRequestId);
return (
<>
<InterlayTableContainer className={clsx('space-y-6', 'container', 'mx-auto')}>
<SectionTitle>{t('issue_page.issue_requests')}</SectionTitle>
<InterlayTable {...getTableProps()}>
<InterlayThead>
{/* TODO: should type properly */}
{headerGroups.map((headerGroup: any) => (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...headerGroup.getHeaderGroupProps()}>
{/* TODO: should type properly */}
{headerGroup.headers.map((column: any) => (
// eslint-disable-next-line react/jsx-key
<InterlayTh
{...column.getHeaderProps([
{
className: clsx(column.classNames),
style: column.style
}
])}
>
{column.render('Header')}
</InterlayTh>
))}
</InterlayTr>
))}
</InterlayThead>
<InterlayTbody {...getTableBodyProps()}>
{/* TODO: should type properly */}
{rows.map((row: any) => {
prepareRow(row);
const { className: rowClassName, ...restRowProps } = row.getRowProps();
return (
// eslint-disable-next-line react/jsx-key
<InterlayTr
className={clsx(rowClassName, 'cursor-pointer')}
{...restRowProps}
onClick={handleRowClick(row.original.id)}
>
{/* TODO: should type properly */}
{row.cells.map((cell: any) => {
return (
// eslint-disable-next-line react/jsx-key
<InterlayTd
{...cell.getCellProps([
{
className: clsx(cell.column.classNames),
style: cell.column.style
}
])}
>
{cell.render('Cell')}
</InterlayTd>
);
})}
</InterlayTr>
);
})}
</InterlayTbody>
</InterlayTable>
{pageCount > 0 && (
<div className={clsx('flex', 'justify-end')}>
<InterlayPagination
pageCount={pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={5}
onPageChange={handlePageChange}
forcePage={selectedPageIndex}
/>
</div>
)}
</InterlayTableContainer>
{selectedIssueRequest && (
<IssueRequestModal
open={!!selectedIssueRequest}
onClose={handleIssueModalClose}
request={selectedIssueRequest}
/>
)}
</>
);
}
Example #24
Source File: index.tsx From polkabtc-ui with Apache License 2.0 | 4 votes |
IssueRequestsTable = ({
totalIssueRequests
}: Props): JSX.Element | null => {
const query = useQuery();
const selectedPage: number = query.get(QUERY_PARAMETERS.page) || 1;
const updateQueryParameters = useUpdateQueryParameters();
const statsApi = usePolkabtcStats();
const [data, setData] = React.useState<DashboardRequestInfo[]>([]);
const [status, setStatus] = React.useState(STATUSES.IDLE);
const [error, setError] = React.useState<Error | null>(null);
const { t } = useTranslation();
React.useEffect(() => {
if (!statsApi) return;
if (!selectedPage) return;
const selectedPageIndex = selectedPage - 1;
try {
(async () => {
setStatus(STATUSES.PENDING);
const response = await statsApi.getIssues(
selectedPageIndex,
PAGE_SIZE,
undefined,
undefined,
constants.BITCOIN_NETWORK as BtcNetworkName // Not sure why cast is necessary here, but TS complains
);
setStatus(STATUSES.RESOLVED);
setData(response.data);
})();
} catch (error) {
setStatus(STATUSES.REJECTED);
setError(error);
}
}, [
statsApi,
selectedPage
]);
const columns = React.useMemo(
() => [
{
Header: t('date'),
accessor: 'timestamp',
classNames: [
'text-left'
],
Cell: function FormattedCell({ value }) {
return (
<>
{formatDateTimePrecise(new Date(Number(value)))}
</>
);
}
},
{
Header: t('issue_page.amount'),
accessor: 'amountBTC',
classNames: [
'text-right'
]
},
{
Header: t('issue_page.parachain_block'),
accessor: 'creation',
classNames: [
'text-right'
]
},
{
Header: t('issue_page.vault_dot_address'),
accessor: 'vaultDOTAddress',
classNames: [
'text-left'
],
Cell: function FormattedCell({ value }) {
return (
<>
{shortAddress(value)}
</>
);
}
},
{
Header: t('issue_page.vault_btc_address'),
accessor: 'vaultBTCAddress',
classNames: [
'text-left'
],
Cell: function FormattedCell({ value }) {
return (
<InterlayLink
className={clsx(
'text-interlayDodgerBlue',
'space-x-1.5',
'flex',
'items-center'
)}
href={`${BTC_ADDRESS_API}${value}`}
target='_blank'
rel='noopener noreferrer'>
<span>{shortAddress(value)}</span>
<FaExternalLinkAlt />
</InterlayLink>
);
}
},
{
Header: t('status'),
classNames: [
'text-left'
],
Cell: function FormattedCell(props) {
return (
<StatusCell
status={{
completed: props.row.original.completed,
cancelled: props.row.original.cancelled,
isExpired: props.row.original.isExpired,
reimbursed: props.row.original.reimbursed
}} />
);
}
}
],
[t]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable(
{
columns,
data
}
);
if (status === STATUSES.REJECTED && error) {
return (
<ErrorHandler error={error} />
);
}
const handlePageChange = (newPage: number) => {
updateQueryParameters({
[QUERY_PARAMETERS.page]: newPage
});
};
return (
<InterlayTableContainer className='space-y-6'>
<h2
className={clsx(
'text-2xl',
'font-bold'
)}>
{t('issue_page.recent_requests')}
</h2>
{(status === STATUSES.IDLE || status === STATUSES.PENDING) && (
<div
className={clsx(
'flex',
'justify-center'
)}>
<EllipsisLoader dotClassName='bg-interlayTreePoppy-400' />
</div>
)}
{status === STATUSES.RESOLVED && (
<InterlayTable {...getTableProps()}>
<InterlayThead>
{headerGroups.map(headerGroup => (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
// eslint-disable-next-line react/jsx-key
<InterlayTh
{...column.getHeaderProps([
{
className: clsx(column.classNames),
style: column.style
}
])}>
{column.render('Header')}
</InterlayTh>
))}
</InterlayTr>
))}
</InterlayThead>
<InterlayTbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row);
return (
// eslint-disable-next-line react/jsx-key
<InterlayTr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
// eslint-disable-next-line react/jsx-key
<InterlayTd
{...cell.getCellProps([
{
className: clsx(cell.column.classNames),
style: cell.column.style
}
])}>
{cell.render('Cell')}
</InterlayTd>
);
})}
</InterlayTr>
);
})}
</InterlayTbody>
</InterlayTable>
)}
{totalIssueRequests > 0 && (
// TODO: error-prone in UI/UX
<Pagination
pageSize={PAGE_SIZE}
total={totalIssueRequests}
current={selectedPage}
onChange={handlePageChange} />
)}
</InterlayTableContainer>
);
}
Example #25
Source File: index.tsx From platform with MIT License | 4 votes |
function Table({ columns, data, searchPlaceholder = "games...", colour = "bg-teal-700" }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
setPageSize,
state,
preGlobalFilteredRows,
setGlobalFilter,
} = useTable(
{
columns,
data,
initialState: {
hiddenColumns: [
"id",
"whiteMemberId",
"blackMemberId",
"liChessUrl",
"chesscomUrl",
"eventId",
"formArray",
"bulletDiff",
"blitzDiff",
"rapidDiff",
"isOnline",
"standardChange",
"rapidChange"
]
}
},
useFilters,
useGlobalFilter,
useSortBy,
usePagination
);
return (
<>
<div className="sm:gap-x-2">
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
searchPlaceholder={searchPlaceholder}
/>
{headerGroups.map((headerGroup) =>
headerGroup.headers.map((column) =>
column.Filter ? (
<div className="mt-0" key={column.id}>
{column.render("Filter")}
</div>
) : null
)
)}
</div>
{/* table */}
<div className="relative mt-4 sm:flex sm:flex-col">
<div className="overflow-auto w-full shadow border-b border-gray-200 rounded-lg">
<table
{...getTableProps()}
className="w-full table-auto divide-y divide-gray-200"
>
<thead className="">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
scope="col"
className={classNames(colour, "group px-2 py-3 text-center text-xs font-medium text-gray-100 uppercase")}
{...column.getHeaderProps(column.getSortByToggleProps())}
>
<div className=" flex items-center text-center justify-between">
{column.render("Header")}
{/* Add a sort direction indicator */}
<span>
{column.isSorted ? (
column.isSortedDesc ? (
<SortDownIcon className="w-4 h-4 text-gray-200" />
) : (
<SortUpIcon className="w-4 h-4 text-gray-200" />
)
) : (
<SortIcon className="w-4 h-4 text-gray-400 opacity-0 group-hover:opacity-100" />
)}
</span>
</div>
</th>
))}
</tr>
))}
</thead>
<tbody
{...getTableBodyProps()}
className="bg-white divide-y divide-gray-200"
>
{page.map((row, i) => {
// new
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td
{...cell.getCellProps()}
className="py-4 px-2 whitespace-nowrap"
role="cell"
>
{cell.column.Cell.name === "defaultRenderer" ? (
<div className="text-sm text-gray-500">
{cell.render("Cell")}
</div>
) : (
cell.render("Cell")
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
</div>
{/* Pagination */}
<div className="py-3 flex items-center justify-between">
<div className="flex-1 flex items-center justify-between">
<div className="flex gap-x-2 items-baseline">
<span className="text-xs text-gray-700">
Page <span className="font-medium">{state.pageIndex + 1}</span> of{" "}
<span className="font-medium">{pageOptions.length}</span>
</span>
<label>
<span className="sr-only">Items Per Page</span>
<select
className="mt-1 block w-full text-xs rounded-md border-gray-300 shadow-sm focus:border-teal-300 focus:ring focus:ring-teal-500 focus:ring-opacity-50"
value={state.pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[5, 10, 20].map((pageSize) => (
<option
className="hover:bg-teal-200"
key={pageSize}
value={pageSize}
>
Show {pageSize}
</option>
))}
</select>
</label>
</div>
<div>
<nav
className="relative mt-1 sm:mt-0 z-0 inline-flex rounded-md shadow-sm -space-x-px"
aria-label="Pagination"
>
<PageButton
className="rounded-l-md -mr-1 hidden sm:block"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
>
<span className="sr-only">First</span>
<ChevronDoubleLeftIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</PageButton>
<PageButton
className="rounded-l-md sm:rounded-none"
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
<span className="sr-only">Previous</span>
<ChevronLeftIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</PageButton>
<PageButton
className="rounded-r-md sm:rounded-none"
onClick={() => nextPage()}
disabled={!canNextPage}
>
<span className="sr-only">Next</span>
<ChevronRightIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</PageButton>
<PageButton
className="rounded-r-md hidden sm:block"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
>
<span className="sr-only">Last</span>
<ChevronDoubleRightIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</PageButton>
</nav>
</div>
</div>
</div>
</>
);
}