mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
F OpenNebula/one#5422: Refactor vm table
This commit is contained in:
parent
694280f5f6
commit
8a65245d54
@ -2,65 +2,79 @@ import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { useVirtual } from 'react-virtual'
|
||||
import { Box } from '@material-ui/core'
|
||||
import { debounce, Box, LinearProgress } from '@material-ui/core'
|
||||
|
||||
const ListVirtualized = ({ list = [] }) => {
|
||||
const parentRef = React.useRef()
|
||||
import { useNearScreen } from 'client/hooks'
|
||||
|
||||
const rowVirtualizer = useVirtual({
|
||||
size: list.length,
|
||||
parentRef,
|
||||
overscan: 20,
|
||||
estimateSize: React.useCallback(() => 35, []),
|
||||
keyExtractor: index => list[index]?.ID
|
||||
const ListVirtualized = ({
|
||||
canFetchMore,
|
||||
containerProps,
|
||||
data,
|
||||
isLoading,
|
||||
fetchMore,
|
||||
children
|
||||
}) => {
|
||||
// OBSERVER
|
||||
const loaderRef = React.useRef()
|
||||
const { isNearScreen } = useNearScreen({
|
||||
distance: '100px',
|
||||
externalRef: isLoading ? null : loaderRef,
|
||||
once: false
|
||||
})
|
||||
|
||||
// VIRTUALIZER
|
||||
const parentRef = React.useRef()
|
||||
const rowVirtualizer = useVirtual({
|
||||
size: data.length,
|
||||
parentRef,
|
||||
overscan: 20,
|
||||
estimateSize: React.useCallback(() => 40, []),
|
||||
keyExtractor: index => data[index]?.id
|
||||
})
|
||||
|
||||
const debounceHandleNextPage = React.useCallback(debounce(fetchMore, 200), [])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isNearScreen && !canFetchMore) debounceHandleNextPage()
|
||||
}, [isNearScreen, canFetchMore, debounceHandleNextPage])
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<div
|
||||
ref={parentRef}
|
||||
style={{
|
||||
height: '150px',
|
||||
overflow: 'auto'
|
||||
}}
|
||||
<Box ref={parentRef} height={1} overflow='auto'>
|
||||
<Box {...containerProps}
|
||||
height={`${rowVirtualizer.totalSize}px`}
|
||||
width={1}
|
||||
position='relative'
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: `${rowVirtualizer.totalSize}px`,
|
||||
width: '100%',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
{rowVirtualizer.virtualItems.map(virtualRow => {
|
||||
console.log(virtualRow)
|
||||
return (
|
||||
<div
|
||||
key={virtualRow.index}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: `${virtualRow.size}px`,
|
||||
transform: `translateY(${virtualRow.start}px)`
|
||||
}}
|
||||
>
|
||||
Row {virtualRow.index}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{children(rowVirtualizer.virtualItems)}
|
||||
</Box>
|
||||
|
||||
{!canFetchMore && (
|
||||
<LinearProgress
|
||||
ref={loaderRef}
|
||||
color='secondary'
|
||||
style={{ width: '100%', marginTop: 10 }}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
ListVirtualized.propTypes = {
|
||||
list: PropTypes.arrayOf(PropTypes.any)
|
||||
canFetchMore: PropTypes.bool,
|
||||
containerProps: PropTypes.object,
|
||||
data: PropTypes.arrayOf(PropTypes.any),
|
||||
isLoading: PropTypes.bool,
|
||||
fetchMore: PropTypes.func,
|
||||
children: PropTypes.func
|
||||
}
|
||||
|
||||
ListVirtualized.defaultProps = {
|
||||
list: []
|
||||
canFetchMore: false,
|
||||
containerProps: undefined,
|
||||
data: [],
|
||||
isLoading: false,
|
||||
fetchMore: () => undefined,
|
||||
children: () => undefined
|
||||
}
|
||||
|
||||
export default ListVirtualized
|
||||
|
@ -8,20 +8,19 @@ const useStyles = makeStyles(theme => ({
|
||||
search: {
|
||||
position: 'relative',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: fade(theme.palette.common.white, 0.15),
|
||||
backgroundColor: fade(theme.palette.primary.dark, 0.15),
|
||||
'&:hover': {
|
||||
backgroundColor: fade(theme.palette.common.white, 0.25)
|
||||
backgroundColor: fade(theme.palette.primary.dark, 0.25)
|
||||
},
|
||||
marginRight: theme.spacing(2),
|
||||
marginLeft: 0,
|
||||
margin: theme.spacing(1, 0),
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginLeft: theme.spacing(3),
|
||||
marginLeft: theme.spacing(1),
|
||||
width: 'auto'
|
||||
}
|
||||
},
|
||||
searchIcon: {
|
||||
width: theme.spacing(7),
|
||||
padding: theme.spacing(0, 2),
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
pointerEvents: 'none',
|
||||
@ -33,12 +32,10 @@ const useStyles = makeStyles(theme => ({
|
||||
color: 'inherit'
|
||||
},
|
||||
inputInput: {
|
||||
padding: theme.spacing(1, 1, 1, 7),
|
||||
transition: theme.transitions.create('width'),
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: 200
|
||||
}
|
||||
padding: theme.spacing(1, 1, 1, 0),
|
||||
// vertical padding + font size from searchIcon
|
||||
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
|
||||
width: '100%'
|
||||
}
|
||||
}))
|
||||
|
||||
@ -59,6 +56,22 @@ const GlobalFilter = props => {
|
||||
// This may not be a problem for server side pagination when
|
||||
// only the current page is downloaded.
|
||||
|
||||
/* <Box className={classes.search}>
|
||||
<Box className={classes.searchIcon}>
|
||||
<SearchIcon />
|
||||
</Box>
|
||||
<InputBase
|
||||
type='search'
|
||||
onChange={searchProps.handleChange}
|
||||
fullWidth
|
||||
placeholder={`${T.Search}...`}
|
||||
classes={{
|
||||
root: classes.inputRoot,
|
||||
input: classes.inputInput
|
||||
}}
|
||||
/>
|
||||
</Box> */
|
||||
|
||||
return (
|
||||
<div className={classes.search}>
|
||||
<div className={classes.searchIcon}>
|
||||
|
@ -33,7 +33,6 @@ export default [
|
||||
<StatusChip stateColor={Colors.debug.light} text={`#${value}`} />
|
||||
},
|
||||
{ Header: 'Name', accessor: 'NAME' },
|
||||
{ Header: 'Owner/Group', accessor: row => `${row.UNAME}/${row.GNAME}` },
|
||||
{
|
||||
Header: 'State',
|
||||
id: 'STATE',
|
||||
@ -47,6 +46,7 @@ export default [
|
||||
filter: (rows, id, filterValue) =>
|
||||
rows.filter(row => row.values[id]?.name === filterValue)
|
||||
},
|
||||
{ Header: 'Owner/Group', accessor: row => `${row.UNAME}/${row.GNAME}` },
|
||||
{
|
||||
Header: 'Ips',
|
||||
accessor: row => VirtualMachineModel.getIps(row),
|
||||
|
57
src/fireedge/src/client/components/Tables/VmTable/header.js
Normal file
57
src/fireedge/src/client/components/Tables/VmTable/header.js
Normal file
@ -0,0 +1,57 @@
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Box } from '@material-ui/core'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
textTransform: 'uppercase',
|
||||
fontSize: '0.9em',
|
||||
fontWeight: 700,
|
||||
lineHeight: '1rem',
|
||||
letterSpacing: '0.05em',
|
||||
|
||||
overflowWrap: 'break-word',
|
||||
textAlign: 'start',
|
||||
padding: '1em',
|
||||
|
||||
color: '#4A5568',
|
||||
backgroundColor: '#e6e8f7',
|
||||
borderBlock: '0.5px solid #EDF2F7'
|
||||
}
|
||||
}))
|
||||
|
||||
const Header = ({ useTableProps }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
/** @type {import('react-table').UseTableInstanceProps} */
|
||||
const { headerGroups } = useTableProps
|
||||
|
||||
const renderHeaderColumn = React.useCallback(column => (
|
||||
<Box {...column.getHeaderProps()}>
|
||||
{column.render('Header')}
|
||||
</Box>
|
||||
), [])
|
||||
|
||||
const renderHeaderGroup = React.useCallback(headerGroup => (
|
||||
<Box {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(renderHeaderColumn)}
|
||||
</Box>
|
||||
), [])
|
||||
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
{headerGroups.map(renderHeaderGroup)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
useTableProps: PropTypes.object
|
||||
}
|
||||
|
||||
Header.defaultProps = {
|
||||
useTableProps: {}
|
||||
}
|
||||
|
||||
export default Header
|
@ -2,155 +2,79 @@
|
||||
/* eslint-disable react/jsx-key */
|
||||
import * as React from 'react'
|
||||
|
||||
import { Paper, debounce, LinearProgress, CircularProgress } from '@material-ui/core'
|
||||
import { useVirtual } from 'react-virtual'
|
||||
import { Box, CircularProgress } from '@material-ui/core'
|
||||
import {
|
||||
useTable,
|
||||
useGlobalFilter,
|
||||
useSortBy,
|
||||
useRowSelect,
|
||||
useFilters,
|
||||
usePagination,
|
||||
useFlexLayout
|
||||
} from 'react-table'
|
||||
|
||||
import { useNearScreen } from 'client/hooks'
|
||||
import { EnhancedTable, DefaultFilter } from 'client/components/Table'
|
||||
|
||||
import { ListVirtualized } from 'client/components/List'
|
||||
import Toolbar from 'client/components/Tables/VmTable/toolbar'
|
||||
import Header from 'client/components/Tables/VmTable/header'
|
||||
import Row from 'client/components/Tables/VmTable/row'
|
||||
import Columns from 'client/components/Tables/VmTable/columns'
|
||||
import { console } from 'window-or-global'
|
||||
|
||||
const VmTable = ({ data, isLoading, finish, getNextData }) => {
|
||||
const parentRef = React.useRef()
|
||||
|
||||
// <----------- USE TABLE ----------->
|
||||
const VmTable = ({ data, isLoading, canFetchMore, fetchMore }) => {
|
||||
const columns = React.useMemo(() => Columns, [])
|
||||
|
||||
const defaultColumn = React.useMemo(() => ({
|
||||
Filter: DefaultFilter
|
||||
// Filter: DefaultFilter,
|
||||
Cell: React.memo(({ value }) => value ?? '--')
|
||||
}), [])
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
totalColumnsWidth,
|
||||
prepareRow
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
defaultColumn
|
||||
},
|
||||
const useTableProps = useTable(
|
||||
{ columns, data, defaultColumn },
|
||||
useRowSelect,
|
||||
useFlexLayout
|
||||
useFlexLayout,
|
||||
useGlobalFilter
|
||||
)
|
||||
// <----------- FINISH USE TABLE ----------->
|
||||
|
||||
// <----------- VIRTUALIZER ----------->
|
||||
const rowVirtualizer = useVirtual({
|
||||
size: rows.length,
|
||||
parentRef,
|
||||
overscan: 10,
|
||||
estimateSize: React.useCallback(() => 50, []),
|
||||
keyExtractor: index => rows[index]?.id
|
||||
})
|
||||
// <----------- FINISH VIRTUALIZER ----------->
|
||||
|
||||
// <----------- OBSERVER ----------->
|
||||
const loaderRef = React.useRef()
|
||||
const { isNearScreen } = useNearScreen({
|
||||
distance: '100px',
|
||||
externalRef: isLoading ? null : loaderRef,
|
||||
once: false
|
||||
})
|
||||
|
||||
const debounceHandleNextPage = React.useCallback(debounce(getNextData, 200), [])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isNearScreen && !finish) debounceHandleNextPage()
|
||||
}, [isNearScreen, finish, debounceHandleNextPage])
|
||||
// <----------- FINISH OBSERVER ----------->
|
||||
|
||||
const RenderRow = React.useCallback(({ row, virtualRow }) => (
|
||||
<div
|
||||
{...row.getRowProps()}
|
||||
ref={virtualRow.measureRef}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: `${virtualRow.size}px`,
|
||||
transform: `translateY(${virtualRow.start}px)`
|
||||
}}
|
||||
>
|
||||
{row.cells.map(cell => (
|
||||
<div {...cell.getCellProps()}>
|
||||
{cell.render('Cell')}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
), [prepareRow, rows])
|
||||
const { getTableProps, getTableBodyProps, rows } = useTableProps
|
||||
|
||||
return (
|
||||
<Paper style={{ height: '100%', overflow: 'hidden' }}>
|
||||
<div
|
||||
{...getTableProps()}
|
||||
style={{ height: '100%', display: 'flex', flexFlow: 'column' }}
|
||||
<Box {...getTableProps()}
|
||||
height={1}
|
||||
display='flex'
|
||||
flexDirection='column'
|
||||
border='1px solid #d7dde3'
|
||||
borderRadius={5}
|
||||
>
|
||||
<Toolbar useTableProps={useTableProps} />
|
||||
<Header useTableProps={useTableProps} />
|
||||
|
||||
<ListVirtualized
|
||||
containerProps={{ ...getTableBodyProps() }}
|
||||
canFetchMore={canFetchMore}
|
||||
data={rows}
|
||||
isLoading={isLoading}
|
||||
fetchMore={fetchMore}
|
||||
>
|
||||
<div>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<div {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map(column => (
|
||||
<div {...column.getHeaderProps()}>
|
||||
{column.render('Header')}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{virtualItems => virtualItems?.map(virtualRow => (
|
||||
<Row key={virtualRow.index}
|
||||
virtualRow={virtualRow}
|
||||
useTableProps={useTableProps}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</ListVirtualized>
|
||||
|
||||
<div ref={parentRef} style={{ height: '100%', overflow: 'auto' }}>
|
||||
<div
|
||||
{...getTableBodyProps()}
|
||||
style={{
|
||||
height: `${rowVirtualizer.totalSize}px`,
|
||||
width: '100%',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
{rowVirtualizer.virtualItems?.map(virtualRow => {
|
||||
const row = rows[virtualRow.index]
|
||||
prepareRow(row)
|
||||
|
||||
return <RenderRow
|
||||
key={row.getRowProps().key}
|
||||
row={row}
|
||||
virtualRow={virtualRow}
|
||||
/>
|
||||
})}
|
||||
</div>
|
||||
|
||||
{!finish && (
|
||||
<LinearProgress
|
||||
ref={loaderRef}
|
||||
color='secondary'
|
||||
style={{ width: '100%', marginTop: 10 }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p style={{ display: 'flex', alignItems: 'center', gap: '1em' }}>
|
||||
<span>Total loaded: {rows.length}</span>
|
||||
{isLoading && <CircularProgress size='1em' />}
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
padding: '1em',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '1em',
|
||||
color: '#4a5568',
|
||||
backgroundColor: '#e6e8f7',
|
||||
borderBlock: '0.5px solid #eef2f7'
|
||||
}}
|
||||
>
|
||||
<span>Total loaded: {useTableProps.rows.length}</span>
|
||||
{isLoading && <CircularProgress size='1em' />}
|
||||
</div>
|
||||
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
|
72
src/fireedge/src/client/components/Tables/VmTable/row.js
Normal file
72
src/fireedge/src/client/components/Tables/VmTable/row.js
Normal file
@ -0,0 +1,72 @@
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Box } from '@material-ui/core'
|
||||
import clsx from 'clsx'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
// <-- it's needed to virtualize -->
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
|
||||
fontSize: '1em',
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
lineHeight: '1rem',
|
||||
|
||||
overflowWrap: 'break-word',
|
||||
textAlign: 'start',
|
||||
padding: '1em',
|
||||
alignItems: 'center',
|
||||
|
||||
boxShadow: '0 0 0 0.5px #e6e8f7'
|
||||
},
|
||||
virtual: ({ size, start }) => ({
|
||||
height: size,
|
||||
transform: `translateY(${start}px)`
|
||||
})
|
||||
}))
|
||||
|
||||
const Row = ({ virtualRow, useTableProps }) => {
|
||||
/** @type {import('react-virtual').VirtualItem} */
|
||||
const { index, measureRef, size, start } = virtualRow
|
||||
|
||||
const classes = useStyles({ size, start })
|
||||
|
||||
/** @type {import('react-table').UseTableInstanceProps} */
|
||||
const { rows, prepareRow } = useTableProps
|
||||
|
||||
/** @type {import('react-table').UseTableRowProps} */
|
||||
const row = rows[index]
|
||||
|
||||
prepareRow(row)
|
||||
|
||||
const renderCell = React.useCallback(cell => (
|
||||
<Box {...cell.getCellProps()}>
|
||||
{cell.render('Cell')}
|
||||
</Box>
|
||||
), [])
|
||||
|
||||
return (
|
||||
<Box {...row.getRowProps()}
|
||||
ref={measureRef}
|
||||
className={clsx(classes.root, classes.virtual)}
|
||||
>
|
||||
{row?.cells?.map(renderCell)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Row.propTypes = {
|
||||
virtualRow: PropTypes.object,
|
||||
useTableProps: PropTypes.object
|
||||
}
|
||||
|
||||
Row.defaultProps = {
|
||||
virtualRow: {},
|
||||
useTableProps: {}
|
||||
}
|
||||
|
||||
export default Row
|
77
src/fireedge/src/client/components/Tables/VmTable/toolbar.js
Normal file
77
src/fireedge/src/client/components/Tables/VmTable/toolbar.js
Normal file
@ -0,0 +1,77 @@
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { makeStyles, Toolbar as MToolbar, Button } from '@material-ui/core'
|
||||
import { Filter as FilterIcon } from 'iconoir-react'
|
||||
|
||||
import GlobalFilter from 'client/components/Table/Filters/GlobalFilter'
|
||||
|
||||
const useToolbarStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(1)
|
||||
},
|
||||
filterWrapper: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '1em'
|
||||
},
|
||||
filterButton: {
|
||||
...theme.typography.body1,
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
textTransform: 'none'
|
||||
},
|
||||
filters: {
|
||||
...theme.typography.body1,
|
||||
color: theme.palette.grey[700]
|
||||
}
|
||||
}))
|
||||
|
||||
const Toolbar = ({ useTableProps }) => {
|
||||
const classes = useToolbarStyles()
|
||||
|
||||
/** @type {import('react-table').UseGlobalFiltersInstanceProps} */
|
||||
const { preGlobalFilteredRows, setGlobalFilter, state } = useTableProps
|
||||
|
||||
// const { selectedRowIds, globalFilter } = state
|
||||
// const numSelected = Object.keys(selectedRowIds).length
|
||||
|
||||
return (
|
||||
<MToolbar className={classes.root}>
|
||||
<div className={classes.filterWrapper}>
|
||||
<Button
|
||||
variant='outlined'
|
||||
startIcon={<FilterIcon size='1rem' />}
|
||||
className={classes.filterButton}
|
||||
>
|
||||
Filters
|
||||
</Button>
|
||||
<span className={classes.filters}>
|
||||
No filters selected
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* numSelected > 0 && (
|
||||
<Typography className={classes.title} color='inherit' variant='subtitle1'>
|
||||
{numSelected} selected
|
||||
</Typography>
|
||||
) */}
|
||||
|
||||
{/* <GlobalFilter
|
||||
preGlobalFilteredRows={preGlobalFilteredRows}
|
||||
globalFilter={globalFilter}
|
||||
setGlobalFilter={setGlobalFilter}
|
||||
/> */}
|
||||
</MToolbar>
|
||||
)
|
||||
}
|
||||
|
||||
Toolbar.propTypes = {
|
||||
useTableProps: PropTypes.object
|
||||
}
|
||||
|
||||
Toolbar.defaultProps = {
|
||||
useTableProps: {}
|
||||
}
|
||||
|
||||
export default Toolbar
|
@ -1,14 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { Container } from '@material-ui/core'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useVm, useVmApi } from 'client/features/One'
|
||||
import { useFetch } from 'client/hooks'
|
||||
|
||||
import { VmTable } from 'client/components/Tables'
|
||||
|
||||
const INITIAL_ELEMENT = -1
|
||||
const INITIAL_ELEMENT = 0
|
||||
const NUMBER_OF_INTERVAL = -100
|
||||
|
||||
function VirtualMachines () {
|
||||
@ -22,9 +20,7 @@ function VirtualMachines () {
|
||||
|
||||
useEffect(() => { fetchRequest({ start, end }) }, [filterPool])
|
||||
|
||||
const handleGetMoreData = () => {
|
||||
console.log('FETCH MORE')
|
||||
|
||||
const fetchMore = () => {
|
||||
setPage(prevState => {
|
||||
const newStart = prevState.start + NUMBER_OF_INTERVAL
|
||||
const newEnd = prevState.end - NUMBER_OF_INTERVAL
|
||||
@ -36,17 +32,14 @@ function VirtualMachines () {
|
||||
}
|
||||
|
||||
const finish = data?.length < NUMBER_OF_INTERVAL
|
||||
// console.log({ start, end, loading, finish, vms })
|
||||
|
||||
return (
|
||||
<Container disableGutters style={{ height: '100%' }}>
|
||||
<VmTable
|
||||
data={vms}
|
||||
isLoading={(vms.length === 0 && (loading || reloading))}
|
||||
finish={finish}
|
||||
getNextData={handleGetMoreData}
|
||||
/>
|
||||
</Container>
|
||||
<VmTable
|
||||
data={vms}
|
||||
isLoading={loading || reloading}
|
||||
finish={finish}
|
||||
fetchMore={fetchMore}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user