Skip to content

Commit

Permalink
Merge pull request #100 from commonsku/calendar
Browse files Browse the repository at this point in the history
added windowed table
  • Loading branch information
Haseeb Ahmad authored Dec 14, 2021
2 parents 38d8214 + 6eeaec6 commit 5fab48d
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 3 deletions.
4 changes: 1 addition & 3 deletions src/@commonsku/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,4 @@ export {default as Theme} from './Theme'
export {default as GlobalStyle} from './globalStyles'

export * from './calendar';

export * as reactTable from 'react-table';
export * as reactTableSticky from 'react-table-sticky';
export * from './tables';
193 changes: 193 additions & 0 deletions src/@commonsku/styles/tables/SimpleWindowedTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import styled from 'styled-components';
import React, { useRef, useLayoutEffect } from 'react';
import {
useTable,
useSortBy,
useFlexLayout,
SortingRule,
Column,
} from 'react-table';
import { FixedSizeList, ListOnScrollProps } from 'react-window';
import { SortByTableInstance, SortByTableOptions } from './types';
import { colors } from '../Theme';
import { DownArrowIcon, UpArrowIcon, UpDownArrowsIcon } from '../icons';
import scrollbarWidth from './scrollbarWidth';

export const SimpleWindowedTableStyles = styled.div<{ rowClickable?: boolean; }>`
padding: 1rem;
.project-table-list-rows {
width: 100% !important;
${p => p.rowClickable ? `
.tr {
cursor: pointer;
}
` : ''}
}
.table {
display: inline-block;
border-spacing: 0;
width: 100%;
min-width: 100% !important;
.header.tr {
width: 100% !important;
min-width: 100% !important;
overflow-x: hidden;
}
.tr {
:last-child {
.td {
border-bottom: 0;
}
}
}
.th {
border-bottom: 2px solid ${colors.disabledButtonBorder};
div {
display: inline-block;
}
}
.td {
border-bottom: 1px solid ${colors.disabledButtonBorder};
}
.th,
.td {
margin: 0;
padding: 0.5rem;
}
}
`;

export type SimpleWindowedTableProps = {
columns: Column<object>[];
data: object[];
minWidth?: number;
maxWidth?: number;
defaultSort?: SortingRule<string>;
onClickRow?: (row?: object) => void;
onScroll?: ((props: ListOnScrollProps) => any);
};

function SimpleWindowedTable({
columns,
data,
minWidth = 140,
maxWidth = 500,
defaultSort,
onClickRow,
onScroll,
}: SimpleWindowedTableProps) {
const defaultColumn = React.useMemo(
() => ({
minWidth: minWidth,
maxWidth: maxWidth,
}),
[minWidth, maxWidth]
);

const scrollBarSize = React.useMemo(() => scrollbarWidth(), []);

const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
totalColumnsWidth,
prepareRow,
} = useTable(
{
columns,
data,
defaultColumn,
initialState: {
...(defaultSort ? { sortBy: [defaultSort] } : {}),
},
} as SortByTableOptions,
useSortBy,
useFlexLayout
) as SortByTableInstance;
const headerRef = useRef<HTMLDivElement | null>(null);
const rowsRef = useRef<HTMLDivElement | HTMLSpanElement>();

const onListScroll = (e: Event) => {
if (headerRef.current && e && e.target) {
const target = e.target as HTMLDivElement | HTMLSpanElement;
headerRef.current.scrollLeft = target.scrollLeft;
}
};

useLayoutEffect(() => {
rowsRef.current && rowsRef.current.addEventListener('scroll', onListScroll);
return () => {
rowsRef.current && rowsRef.current.removeEventListener('scroll', onListScroll);
};
}, [rowsRef]);

const RenderRow = React.useCallback(
({ index, style }) => {
const row = rows[index];
prepareRow(row);
return (
<div
{...row.getRowProps({
style
})}
className="tr"
>
{row.cells.map((cell) => {
return (
<div
onClick={() => onClickRow ? onClickRow(cell.row.original) : null}
{...cell.getCellProps()}
className="td">{cell.render("Cell")}</div>
);
})}
</div>
);
},
[prepareRow, rows, onClickRow]
);

return (
<div {...getTableProps()} className="table">
<div className="header-wrapper">
{headerGroups.map((headerGroup) => (
<div {...headerGroup.getHeaderGroupProps()} className="header tr" ref={headerRef}>
{headerGroup.headers.map((column) => (
<div {...column.getHeaderProps(column.getSortByToggleProps())} className="th">
{column.render("Header")}
<span style={{ display: 'inline-block', paddingLeft: 5, verticalAlign: 'text-top' }}>
{column.isSorted ? (
column.isSortedDesc ? <DownArrowIcon width="15px" /> : <UpArrowIcon width="15px" />
) : <UpDownArrowsIcon width="15px" />}
</span>
</div>
))}
</div>
))}
</div>

<div {...getTableBodyProps()}>
<FixedSizeList
height={500}
itemCount={rows.length}
itemSize={80}
width={totalColumnsWidth + scrollBarSize}
className="project-table-list-rows"
outerRef={rowsRef}
onScroll={onScroll}
>{RenderRow}</FixedSizeList>
</div>
</div>
);
}

export default SimpleWindowedTable;
3 changes: 3 additions & 0 deletions src/@commonsku/styles/tables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

export * from './SimpleWindowedTable';
export { default as SimpleWindowedTable } from './SimpleWindowedTable';
11 changes: 11 additions & 0 deletions src/@commonsku/styles/tables/scrollbarWidth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const scrollbarWidth = () => {
// thanks to https://davidwalsh.name/detect-scrollbar-width
const scrollDiv = document.createElement('div');
scrollDiv.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position:absolute; top:-9999px;');
document.body.appendChild(scrollDiv);
const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
document.body.removeChild(scrollDiv);
return scrollbarWidth;
};

export default scrollbarWidth;
25 changes: 25 additions & 0 deletions src/@commonsku/styles/tables/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
HeaderGroup,
TableInstance,
TableState,
TableOptions,
SortingRule,
UseSortByColumnProps,
} from 'react-table';

export interface BaseSortByHeaderGroup<D extends object = {}> extends HeaderGroup<D>, UseSortByColumnProps<D> {}
export interface SortByHeaderGroup<D extends object = {}> extends Omit<BaseSortByHeaderGroup, 'headers'> {
headers: Array<BaseSortByHeaderGroup<D>>;
}

export interface TableInitialStateStateWithSortBy<D extends object = {}> extends Partial<TableState<D>> {
sortBy?: Array<SortingRule<string>>;
}

export interface SortByTableInstance<D extends object = {}> extends Omit<TableInstance<D>, 'headerGroups'> {
headerGroups: SortByHeaderGroup<object>[];
};

export interface SortByTableOptions<D extends object = {}> extends Omit<TableOptions<D>, 'initialState'> {
initialState: TableInitialStateStateWithSortBy<D>;
};

0 comments on commit 5fab48d

Please sign in to comment.