Skip to content

Commit

Permalink
add: Theme in table, loader and custom messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Hector Rodrigues da Silva committed Nov 29, 2022
1 parent f5738b0 commit 12ca6d8
Show file tree
Hide file tree
Showing 3 changed files with 323 additions and 3 deletions.
80 changes: 78 additions & 2 deletions public/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,83 @@
import React, {useEffect, useState} from 'react';

import {DuckTable} from '../src';

type Item = {
id: number,
description: string,
value: number,
store: string,
date: string
}

type Results = {
start: number,
end: number,
total: number
}

export const App = (): JSX.Element => {

const [data, setData] = useState<object[]>([]);
const [loading, setLoading] = useState<boolean>(true);

const actions = () => {
return <span>Custom</span>;
};

const columns = [{
header: 'ID',
value: 'id',
hide: true
}, {
header: 'Description',
value: 'description',
}, {
header: 'Value',
value: 'value',
}, {
header: 'Store',
value: 'store',
}, {
header: 'Date',
value: 'date',
}, {
header: 'Actions',
value: '',
formatter: actions,
style: { width: 200 },
}];

useEffect(() => {
let item: Item = {
id: 0,
value: 0,
store: 'Apple',
date: '12/11/2002',
description: 'Iphone'
};
let arr: Item[] = []
const rows: number = 10000;
for(let i = 1; i <= rows; i++) {
arr.push({ ...item, id: i, value: i });
if(i === rows) setLoading(false);
}
setData(arr);
},[]);

return (
<div>Hello</div>
)
<DuckTable
data={data}
columns={columns}
/* Optional */
maxRows={7}
loading={loading}
themeTable='dark'
themeLoader='primary'
emptyDataMessage={() => <span>My message.</span>}
showResultsMessage={({ start, end, total }: Results) => (
<span style={{ color: 'white' }}>My results</span>
)}
/>
);
}
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" type="text/css" href="styles.css">
<title>React Block Duck</title>
<title>React Table</title>
</head>
<body>
<noscript>Not support this javascript</noscript>
Expand Down
244 changes: 244 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import 'bootstrap/dist/css/bootstrap.min.css';
import React, {Fragment, memo, useCallback, useEffect, useState} from 'react';

type Results = {
start: number,
end: number,
total: number
}

interface Props {
data: object[],
maxRows?: number,
loading?: boolean,
columns: object[],
themeTable?: string,
themeLoader?: string,
maxPagination?: number,
emptyDataMessage?: () => JSX.Element | string,
showResultsMessage?: (object: Results) => JSX.Element | string
}

interface Column {
value: string,
header: string,
hide?: boolean,
style?: object,
formatter?: (cell: any, item: object, index: number) => JSX.Element
}

export const DuckTable = memo((props: Props): JSX.Element => {

const {
data = [],
columns = [],
maxRows = 10,
loading = false,
emptyDataMessage,
showResultsMessage,
maxPagination = 10,
themeLoader = 'dark',
themeTable = 'default'
} = props;

const [rows, setRows] = useState<object[] | []>([]);

const [initialPagination, setInitialPagination] = useState<number>(0);
const [endPagination, setEndPagination] = useState<number>(maxPagination);

const [countPagination, setCountPagination] = useState<number>(0);
const [currentPagination, setCurrentPagination] = useState<number>(1);

const renderPagination: number[] = Array.from(Array(countPagination).keys()).slice(initialPagination, endPagination);

const isDecimal = (n: number) => {
return n % 1 === 0;
};

const renderEmptyMessage = (): JSX.Element | string => {
if(typeof emptyDataMessage === 'function') {
return emptyDataMessage();
}
return !emptyDataMessage ? 'Results not found.' : emptyDataMessage;
};

const renderResultsMessage = (): JSX.Element | string => {
const results: Results = { start: startTable, end: endTable, total: data.length };
if(typeof showResultsMessage === 'function') {
return showResultsMessage(results);
}
return (
<span className="text-muted">
{!showResultsMessage
? `Showing from ${results.start} to ${results.end} of ${results.total} results`
: showResultsMessage // string custom
}
</span>
);
};

const renderCell = useCallback((index: number, value: any) => {
if(index < 1) {
return <th key={index} scope="row">{value}</th>
} else {
return <td key={index}>{value}</td>
}
},[]);

const fullPrevious = () => {
setCurrentPagination(1);
setInitialPagination(0);
setEndPagination(maxPagination);
}

const fullNext = () => {
setCurrentPagination(countPagination);
setInitialPagination(countPagination - (maxPagination - 1));
setEndPagination(countPagination);
}

const previous = () => {
if(currentPagination > 1) {
setPagination(currentPagination - 1);
}
}

const next = () => {
if(currentPagination !== countPagination) {
setPagination(currentPagination + 1);
}
}

const setPagination = (pagination: number): void => {
setCurrentPagination(pagination);
const index: number = pagination - 1;
if(index < renderPagination[0]) {
setInitialPagination(index - (maxPagination - 1));
return setEndPagination(index + 1);
}
if(index > renderPagination.pop()) {
setInitialPagination(index);
return setEndPagination(index + maxPagination);
}
};

useEffect(() => {
if(data.length) {
const length: number = data.length;
const pagination: number = length / maxRows;
if(isDecimal(pagination)) {
setCountPagination(pagination);
} else {
const count: number = parseInt(String(pagination)) + 1;
setCountPagination(count);
}
if(pagination < countPagination) {
previous();
}
}
},[data]);

useEffect(() => {
if(data.length) {
let dataRows: object[] | undefined;
let arr: object[] = data.slice();
if(currentPagination > 1) {
const min = (maxRows * currentPagination) - maxRows;
const max = min + maxRows;
dataRows = arr.slice(min, max);
} else {
dataRows = arr.slice(0, maxRows);
}
setRows(dataRows);
}
},[currentPagination, data]);

const startTable: number = (maxRows * currentPagination + 1) - maxRows;
const endTable: number = (currentPagination * maxRows) - (maxRows - rows.length);
return (
<Fragment>
<table className={`table table-${themeTable} mb-0`}>
<thead>
<tr>
{columns.map((column: Column, index) => {
if(!column.hide) {
return <th key={index} style={!column.style ? {} : column.style} scope="col">{column.header}</th>;
} else {
return null;
}
})}
</tr>
</thead>
{(data.length && !loading) ?
<tbody>
{rows.map((item: any, index: number) =>
<tr key={index}>
{columns.map((column: Column, index) => {
if(!column.hide) {
const cell = item[column.value];
if (column.formatter) {
const formatterCell = column.formatter(cell, item, index);
return renderCell(index, formatterCell);
} else {
return renderCell(index, cell);
}
} else {
return null;
}
})}
</tr>
)}
</tbody>
:
<tbody/>
}
</table>
{loading ?
<div className='w-100 text-center p-5'>
<div className={`spinner-border text-${themeLoader}`} role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
:
Boolean(!data.length) &&
<div className='w-100 text-center p-5'>
<small>{renderEmptyMessage()}</small>
</div>
}
{countPagination > 1 &&
<nav
aria-label="pagination"
className={`d-flex align-items-center justify-content-between bg-${themeTable} p-3`}
>
{countPagination > 1 && renderResultsMessage()}
<ul className="pagination mb-0">
<li className="page-item" onClick={fullPrevious}>
<a className="page-link" href="#">{'<<'}</a>
</li>
<li className="page-item" onClick={previous}>
<a className="page-link" href="#">{'<'}</a>
</li>
{renderPagination.map(pagination => {
return (
<li
key={pagination}
aria-current="page"
onClick={() => setPagination(pagination + 1)}
className={currentPagination === pagination + 1 ? 'page-item active' : 'page-item'}
>
<a className="page-link" href="#">{pagination + 1}</a>
</li>
)
})}
<li className="page-item" onClick={next}>
<a className="page-link" href="#">{'>'}</a>
</li>
<li className="page-item" onClick={fullNext}>
<a className="page-link" href="#">{'>>'}</a>
</li>
</ul>
</nav>
}
</Fragment>
);
});

0 comments on commit 12ca6d8

Please sign in to comment.