Skip to content

Instantly share code, notes, and snippets.

@andrconstruction
Created June 15, 2020 17:31
Show Gist options
  • Save andrconstruction/0c7fc2acc0df9e528f5ab755b68f2a43 to your computer and use it in GitHub Desktop.
Save andrconstruction/0c7fc2acc0df9e528f5ab755b68f2a43 to your computer and use it in GitHub Desktop.
import React from 'react'
import {Button, Card, Modal, OverlayTrigger, Tooltip} from 'react-bootstrap'
import {usePagination, useRowSelect, useSortBy, useTable} from 'react-table'
import UserService from "../service/userService";
import Loader from "../../shared/Loader";
import PerfectScrollbar from "perfect-scrollbar";
import moment from "moment";
import IndeterminateCheckbox from './IndeterminateCheckbox'
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
function TableComponent( {
data,
fetchData,
loading,
getRowProps,
removeButton,
editButton,
revertButton,
withinCard,
} ) {
const columns = React.useMemo( () => [
{
Header : 'ID',
accessor : 'id',
canSort : true
},
{
Header : 'Логин',
accessor : 'username',
canSort : true
},
{
Header : 'Полное имя',
accessor : 'displayName'
},
{
Header : 'Email',
accessor : 'email'
},
{
id : 'created',
Header : 'Создано',
accessor : d => {
return moment( d.created.date )
.local()
.format( "DD.MM.YY hh:mm" )
}
},
{
id : 'updated',
Header : 'Обновлено',
accessor : d => {
return moment( d.updated.date )
.local()
.format( "DD.MM.YY hh:mm" )
}
},
], [] )
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
gotoPage,
nextPage,
previousPage,
setPageSize,
// Get the state from the instance
state : {pageIndex, pageSize, sortBy, selectedRowIds},
} = useTable( {
columns,
getRowProps,
removeButton,
editButton,
revertButton,
data : data._embedded.user,
initialState : {
pageSize : 100,
// autoResetPage : false,
pageIndex : data.page,
sortBy : data.sortBy,
}, // Pass our hoisted table state
manualPagination : true, // Tell the usePagination,
manualSortBy : true,
// hook that we'll handle our own data fetching
// This means we'll also have to provide our own
// pageCount.
pageCount : data.page_count + 1,
autoResetPage : false,
autoResetSortBy : false,
},
// useResizeColumns,
// useFlexLayout,
useSortBy,
usePagination,
useRowSelect,
hooks => {
hooks.visibleColumns.push( columns => {
return [
{
id : 'selection',
// Make this column a groupByBoundary. This ensures that groupBy columns
// are placed after it
groupByBoundary : true,
// disableResizing: true,
// minWidth: 20,
// width: 20,
// maxWidth: 20,
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header : ( {getToggleAllRowsSelectedProps} ) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell : ( {row} ) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
{
id : 'edit',
Header : ( {getToggleAllRowsSelectedProps} ) => (
<>
</>
),
Cell : ( {row} ) => (
<div>
<OverlayTrigger
placement="left"
overlay={<Tooltip className="tooltip-primary">Редактировать ( или дважды щёлкниет на
строке )</Tooltip>}>
<Button onClick={() => editButton( row )} size="xs"
variant="outline-primary icon-btn"><FontAwesomeIcon
icon={['fal', 'pen']} size="sm"/></Button>
</OverlayTrigger>
{row.original.removed
? <OverlayTrigger
placement="left"
overlay={<Tooltip className="tooltip-warning">Восстановить объект</Tooltip>}>
<Button onClick={() => {
revertButton( row )
}} size="xs"
variant="outline-warning icon-btn"><FontAwesomeIcon
icon={['fal', 'trash-undo-alt']} size="sm"/></Button>
</OverlayTrigger>
:
<OverlayTrigger
placement="left"
overlay={<Tooltip className="tooltip-danger">Удалить объект</Tooltip>}>
<Button onClick={() => {
console.log( 'Rmoved clicked' )
// console.log( removedButton )
{
removeButton( row )
}
}
} size="xs"
variant="outline-danger icon-btn"><FontAwesomeIcon
icon={['fal', 'times']} size="sm"/></Button>
</OverlayTrigger>
}
</div>
),
},
]
} )
// hooks.useInstanceBeforeDimensions.push(({ headerGroups }) => {
// // fix the parent group of the selection button to not be resizable
// const selectionGroupHeader = headerGroups[0].headers[0]
// selectionGroupHeader.canResize = true
// })
}
)
React.useEffect( () => {
fetchData( {pageIndex, pageSize, sortBy} )
const ps = new PerfectScrollbar( "#table-body" );
}, [fetchData, pageIndex, pageSize, sortBy] )
const divStyle = {height : 'calc(100vh - 276px)', overflowY : 'scroll'}
return (
<div style={divStyle}>
<table
id={'table-react'}
style={divStyle}
className={`table table-striped table-sm table-hover ${withinCard ? 'card-table' : ''}`} {...getTableProps()}>
<thead>
{headerGroups.map( headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map( column => (
<th {...column.getHeaderProps( column.getSortByToggleProps() )}>
{column.render( 'Header' )}
{column.isSorted
? column.isSortedDesc
? <i className="ion ion-ios-arrow-down ml-2"/>
: <i className="ion ion-ios-arrow-up ml-2"/>
: ''}
</th>
) )}
</tr>
) )}
</thead>
<tbody id={'table-body'} {...getTableBodyProps()}>
{page.map( ( row, i ) => {
prepareRow( row )
return (
<tr {...row.getRowProps( getRowProps( row ) )}>
{row.cells.map( cell => {
return <td {...cell.getCellProps()}>{cell.render( 'Cell' )}</td>
} )}
</tr>
)
} )}
<tr>
{loading ? (
// Use our custom loading state to show a loading indicator
<td colSpan="10000">Загружаем...</td>
) : (
<td colSpan="10000">
Показано c
( {pageIndex * pageSize - pageSize + 1} по {pageIndex * pageSize - pageSize + page.length} )
из {data.total_items}{' '}
результатов
</td>
)}
</tr>
</tbody>
</table>
<hr/>
<div className={`d-flex align-items-center flex-wrap ${withinCard ? 'px-4 pb-4' : ''}`}>
<div className="mr-2">
Страница{' '}
<strong>
{pageIndex} из {pageOptions.length - 1}
</strong>
</div>
<select className="custom-select custom-select-sm mr-auto" style={{width : '150px'}} value={pageSize}
onChange={e => setPageSize( Number( e.target.value ) )}>
{[50, 100, 200].map( pageSize => (
<option key={pageSize} value={pageSize}>
Показывать {pageSize}
</option>
) )}
</select>
<div className="mr-2">
Перейти на страницу:&nbsp;
<input
className="form-control form-control-sm d-inline-block"
type="number"
defaultValue={pageIndex}
onChange={e => {
const page = e.target.value ? Number( e.target.value ) : 1
gotoPage( page )
}}
style={{width : '100px'}}
/>
</div>
<ul className="pagination mb-0">
<li className="page-item">
<a className={`page-link ${canPreviousPage ? '' : 'disabled'}`} href="#start" onClick={e => {
e.preventDefault()
gotoPage( 1 )
}}>{'<<'}</a>
</li>
<li className="page-item">
<a className={`page-link ${canPreviousPage && pageIndex > 1 ? '' : 'disabled'}`} href="#prev"
onClick={e => {
e.preventDefault()
if ( pageIndex === 1 ) {
gotoPage( 1 )
} else {
previousPage()
}
}}>{'<'}</a>
</li>
<li className="page-item">
<a className={`page-link ${canNextPage ? '' : 'disabled'}`} href="#next" onClick={e => {
e.preventDefault()
nextPage()
}}>{'>'}</a>
</li>
<li className="page-item">
<a className={`page-link ${canNextPage ? '' : 'disabled'}`} href="#end" onClick={e => {
e.preventDefault()
gotoPage( data.page_count )
}}>{'>>'}</a>
</li>
</ul>
</div>
</div>
)
}
function ListUsersTableComponent() {
const [data, setData] = React.useState(
{
sortBy : [{id : 'id', desc : true}],
filter : {},
page : 1,
page_size : 100,
total_items : 0,
page_count : 0,
_embedded : {
user : []
}
}
)
const [loading, setLoading] = React.useState( false )
const fetchIdRef = React.useRef( 0 )
const [slideModalShow, setSlideModalShow] = React.useState( false )
const onRowClick = async ( row ) => {
return {
onDoubleClick : e => {
// setSlideModalShow( true )
console.log( row )
}
}
}
const onRemoveButtonClick = async row => {
return {
onClick : e => {
console.log( 'REMOVE : ' + row )
}
}
}
const onEditButtonClick = async row => {
return {
onClick : e => {
console.log( 'EDIT : ' + row )
}
}
}
const onRevertButtonClick = async row => {
return {
onClick : e => {
console.log( 'REVERT : ' + row )
}
}
}
const onSlideModalClose = () => {
setSlideModalShow( false )
}
const fetchData = React.useCallback( ( {pageSize, pageIndex, sortBy} ) => {
const userService = new UserService();
const fetchId = ++ fetchIdRef.current
setLoading( true )
userService.getUsers( pageIndex, pageSize, sortBy ).then( response => {
if ( fetchId === fetchIdRef.current ) {
setData( response )
setLoading( false )
}
} )
}, [] )
return (
<div>
<Card className="mb-3 mt-0">
<Card.Header as="h6">Пользователи <span
className={'float-right label label-primary'}> {data.total_items} </span>
<Button onClick={() => setSlideModalShow( true ) } variant="outline-primary" size="xs"><FontAwesomeIcon icon={['fal', 'plus']} size="sm"/> Добавить</Button>
</Card.Header>
<Button variant="default" onClick={() =>
setSlideModalShow( true )
}>Show</Button>
{/* Modal template */}
<Modal className="modal-slide w-75" size={"xl"} show={slideModalShow} animation={true} onHide={onSlideModalClose}>
<button type="rounded-pill btn btn-danger btn-lg" className="close" aria-label="Close"
onClick={onSlideModalClose}><FontAwesomeIcon size={"lg"} color={"red"} icon={['fas', 'times']} /></button>
<Modal.Body>
<p className="text-center text-big mb-4">Before you proceed, you have to login to make the
necessary changes</p>
<Button variant="primary" block onClick={onSlideModalClose}>Continue</Button>
<Button variant="default" block onClick={onSlideModalClose}>Cancel</Button>
</Modal.Body>
</Modal>
{loading && <Loader/>}
<TableComponent data={data}
LoadingComponent={Loader}
fetchData={fetchData}
loading={loading}
noDataText={"Грузим..."}
getRowProps={onRowClick}
removeButton={onRemoveButtonClick}
editButton={onEditButtonClick}
revertButton={onRevertButtonClick}
withinCard={true}/>
</Card>
</div>
)
}
export default ListUsersTableComponent
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment