Skip to content

Commit

Permalink
Merge pull request #83 from mckervinc/feature/tweaks
Browse files Browse the repository at this point in the history
v0.4.10
  • Loading branch information
mckervinc committed Sep 30, 2023
2 parents df17aab + db359b0 commit 5e9e006
Show file tree
Hide file tree
Showing 14 changed files with 466 additions and 414 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# CHANGELOG

## 0.4.10

_2023-09-30_

###

- added ability to specify the `footerHeight`
- removed some typescript warnings
- slight performance tweaks

## 0.4.7

_2023-09-28_
Expand Down
20 changes: 18 additions & 2 deletions example/src/Props.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,16 @@ const data: PropData[] = [
description:
"This is a fixed height of each row. Subcomponents will not be affected by this value"
},
{
prop: "headerHeight",
type: "number",
description: "This is an optional fixed height of the header"
},
{
prop: "footerHeight",
type: "number",
description: "This is an optional fixed height of the footer"
},
{
prop: "minColumnWidth",
type: "number",
Expand Down Expand Up @@ -411,7 +421,8 @@ const Props = () => (
<Header dividing size="small" color="grey">
Interfaces
</Header>
<Snippet code={`import { CSSProperties, ForwardedRef, ReactNode } from "react";
<Snippet
code={`import { CSSProperties, ForwardedRef, ReactNode } from "react";
type SortDirection = "ASC" | "DESC" | null;
Expand Down Expand Up @@ -647,6 +658,10 @@ interface TableProps<T> {
* specify a fixed header height
*/
headerHeight?: number;
/**
* specify a fixed footer height
*/
footerHeight?: number;
/**
* Enable or disable row borders. Default: \`false\`.
*/
Expand Down Expand Up @@ -716,7 +731,8 @@ interface TableProps<T> {
*/
ref?: ForwardedRef<TableRef>;
}
`} />
`}
/>
</Container>
);

Expand Down
32 changes: 23 additions & 9 deletions example/src/examples/07-controlled.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import {
randCatchPhrase,
randCity,
Expand Down Expand Up @@ -115,19 +114,34 @@ const Controlled = ({ data, height, columns: variableColumns }: ControlledProps)
);
};

interface ToggleType {
data: boolean;
height: boolean;
columns: boolean;
}

interface DataType {
data: TestData[];
height: number;
columns: ColumnProps<TestData>[];
}

type keyM = keyof ToggleType;
type keyD = keyof DataType;

const viewableTypes = new Set(["string", "number", "boolean"]);

const Example7 = () => {
// hooks
const [toggles, setToggles] = useState({
const [toggles, setToggles] = useState<ToggleType>({
data: false,
height: false,
columns: false
});

// variables
const keys = Object.keys(toggles);
const props = {
const keys: keyM[] = ["data", "height", "columns"];
const props: DataType = {
data: toggles.data ? testData2 : testData,
height: toggles.height ? 200 : 400,
columns: toggles.columns
Expand Down Expand Up @@ -187,22 +201,22 @@ const Example7 = () => {
{"{\n"}
{keys.map((key, index) => {
const ending = index !== keys.length - 1 ? ",\n" : "\n";
// @ts-ignore
const val = viewableTypes.has(typeof props[key]);
let color = "rgb(166, 226, 46)";
// @ts-ignore
if (typeof props[key] === "number") {
color = "rgb(174, 129, 255)";
// @ts-ignore
} else if (typeof props[key] === "boolean") {
color = "rgb(102, 217, 239)";
}
return (
<React.Fragment key={key}>
{` ${key}: `}
<span style={{ color }}>
{/* @ts-ignore */}
{val ? props[key] : toggles[key] ? '"altered"' : '"original"'}
{val
? (props[key as keyD] as string | number)
: toggles[key]
? '"altered"'
: '"original"'}
</span>
{ending}
</React.Fragment>
Expand Down
4 changes: 4 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ export interface TableProps<T> {
* specify a fixed header height
*/
headerHeight?: number;
/**
* specify a fixed footer height
*/
footerHeight?: number;
/**
* Enable or disable row borders. Default: `false`.
*/
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-fluid-table",
"version": "0.4.9",
"version": "0.4.10",
"description": "A React table inspired by react-window",
"author": "Mckervin Ceme <mckervinc@live.com>",
"license": "MIT",
Expand Down
90 changes: 52 additions & 38 deletions src/AutoSizer.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import { TableContext } from "./TableContext";
import { DEFAULT_HEADER_HEIGHT, DEFAULT_ROW_HEIGHT } from "./constants";
import { DEFAULT_FOOTER_HEIGHT, DEFAULT_HEADER_HEIGHT, DEFAULT_ROW_HEIGHT } from "./constants";
import { findFooterByUuid, findHeaderByUuid } from "./util";

interface AutoSizerProps {
numRows: number;
rowHeight?: number;
headerHeight?: number;
footerHeight?: number;
tableWidth?: number;
tableHeight?: number;
minTableHeight?: number;
Expand Down Expand Up @@ -64,29 +65,34 @@ const findCorrectHeight = ({
const calculateHeight = (
rowHeight: number,
headerHeight: number,
footerHeight: number,
uuid: string,
size: number,
hasFooter: boolean
) => {
// get the header, footer and nodes
// get the header and the rows of the table
const header = findHeaderByUuid(uuid);
const nodes = [...(header?.nextElementSibling?.children || [])] as HTMLElement[];
let footerHeight = findFooterByUuid(uuid)?.offsetHeight || 0;

if (!!header && !!nodes.length) {
// header height to use
const headerOffset = headerHeight > 0 ? headerHeight : header.offsetHeight;
// calculate header & footer offsets
const headerOffset =
headerHeight > 0 ? headerHeight : header?.offsetHeight || DEFAULT_HEADER_HEIGHT;
let footerOffset = 0;
if (hasFooter) {
footerOffset =
footerHeight > 0
? footerHeight
: findFooterByUuid(uuid)?.offsetHeight || DEFAULT_FOOTER_HEIGHT;
}

// get border height
let borders = 0;
const table = header.parentElement?.parentElement;
if (!!table) {
borders = table.offsetHeight - table.clientHeight;
}
// calculate border offset
const table = header?.parentElement?.parentElement;
const borderOffset = !!table ? table.offsetHeight - table.clientHeight : 0;

// perform calculation
// if there are rows, let's do the calculation
if (!!nodes.length) {
if (rowHeight > 0) {
return headerOffset + nodes.length * rowHeight + footerHeight + borders;
return headerOffset + nodes.length * rowHeight + footerOffset + borderOffset;
}

let overscan = 0;
Expand All @@ -97,20 +103,14 @@ const calculateHeight = (
return pv + c.offsetHeight;
}, 0) +
overscan +
footerHeight +
borders
footerOffset +
borderOffset
);
}

// try and guess the header and footer height
const headerOffset = headerHeight || DEFAULT_HEADER_HEIGHT;
if (!footerHeight && hasFooter) {
footerHeight = headerOffset;
}

// if the header and nodes are not specified, guess the height
// if the nodes are not specified, guess the height
const height = Math.max(rowHeight || DEFAULT_ROW_HEIGHT, 10);
return height * Math.min(size || 10, 10) + headerOffset + footerHeight;
return headerOffset + height * Math.min(size || 10, 10) + footerOffset + borderOffset;
};

/**
Expand All @@ -127,30 +127,38 @@ const AutoSizer = ({
minTableHeight,
maxTableHeight,
headerHeight,
footerHeight,
children
}: AutoSizerProps) => {
// hooks
const resizeRef = useRef(0);
const ref = useRef<HTMLDivElement>(null);
const { uuid, footerComponent, columns } = useContext(TableContext);
const { uuid, columns, footerComponent } = useContext(TableContext);
const [dimensions, setDimensions] = useState({ containerHeight: 0, containerWidth: 0 });

// variables
const { containerHeight, containerWidth } = dimensions;

// check if footer exists
const hasFooter = useMemo(() => {
return !!footerComponent || !!columns.find(c => !!c.footer);
}, [footerComponent, columns]);
const hasFooter = useMemo(
() => !!footerComponent || !!columns.find(c => !!c.footer),
[columns, footerComponent]
);
const fixedTableSize = !!tableHeight && tableHeight > 0 && !!tableWidth && tableWidth > 0;

// calculate the computed height
const computedHeight = useMemo(() => {
if (!!tableHeight && tableHeight > 0) {
return tableHeight;
}

return calculateHeight(rowHeight || 0, headerHeight || 0, uuid, numRows, hasFooter);
}, [tableHeight, rowHeight, headerHeight, numRows, uuid, hasFooter]);
return calculateHeight(
rowHeight || 0,
headerHeight || 0,
footerHeight || 0,
uuid,
numRows,
hasFooter
);
}, [tableHeight, rowHeight, headerHeight, footerHeight, numRows, uuid, hasFooter]);

// calculate the actual height of the table
const height = findCorrectHeight({
Expand All @@ -167,7 +175,7 @@ const AutoSizer = ({
// functions
const calculateDimensions = useCallback(() => {
// base cases
if (!ref.current?.parentElement) {
if (!ref.current?.parentElement || fixedTableSize) {
return;
}

Expand All @@ -184,10 +192,15 @@ const AutoSizer = ({
const newWidth = Math.max((parent.offsetWidth || 0) - paddingLeft - paddingRight, 0);

// update state
if (newHeight !== containerHeight || newWidth !== containerWidth) {
setDimensions({ containerHeight: newHeight, containerWidth: newWidth });
}
}, [containerHeight, containerWidth]);
setDimensions(prev => {
const { containerHeight: oldHeight, containerWidth: oldWidth } = prev;
if (oldHeight !== newHeight || oldWidth !== newWidth) {
return { containerHeight: newHeight, containerWidth: newWidth };
}

return prev;
});
}, [fixedTableSize]);

const onResize = useCallback(() => {
window.clearTimeout(resizeRef.current);
Expand All @@ -196,10 +209,11 @@ const AutoSizer = ({

// effects
// on mount, calculate the dimensions
useEffect(() => calculateDimensions(), []);
useEffect(() => calculateDimensions(), [calculateDimensions]);

// on resize, we have to re-calculate the dimensions
useEffect(() => {
window.removeEventListener("resize", onResize);
window.addEventListener("resize", onResize);
const m = resizeRef.current;
return () => {
Expand Down
5 changes: 3 additions & 2 deletions src/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ const FooterCell = React.memo(function <T>(props: InnerFooterCellProps<T>) {

// instance
const { width, column } = props;
const cellWidth = width ? `${width}px` : undefined;
const style: React.CSSProperties = {
width: width ? `${width}px` : undefined,
minWidth: width ? `${width}px` : undefined,
width: cellWidth,
minWidth: cellWidth,
padding: !column.footer ? 0 : undefined
};

Expand Down
7 changes: 4 additions & 3 deletions src/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ const HeaderCell = React.memo(function <T>({ column, width }: HeaderCellProps<T>
const { dispatch, sortColumn: col, sortDirection, onSort } = useContext(TableContext);

// constants
const cellWidth = width ? `${width}px` : undefined;
const dir = sortDirection ? (sortDirection.toUpperCase() as SortDirection) : null;

const style: React.CSSProperties = {
cursor: column.sortable ? "pointer" : undefined,
width: width ? `${width}px` : undefined,
minWidth: width ? `${width}px` : undefined
width: cellWidth,
minWidth: cellWidth
};

// function(s)
Expand Down Expand Up @@ -61,7 +62,7 @@ const HeaderCell = React.memo(function <T>({ column, width }: HeaderCellProps<T>
<div className="header-cell" onClick={onClick} style={style}>
{column.header ? <div className="header-cell-text">{column.header}</div> : null}
{column.key !== col ? null : (
<div className={`header-cell-arrow ${(dir || "").toLowerCase()}`}></div>
<div className={cx(["header-cell-arrow", dir?.toLowerCase()])}></div>
)}
</div>
);
Expand Down
5 changes: 3 additions & 2 deletions src/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ const TableCell = React.memo(function <T>({
onExpanderClick
}: TableCellProps<T>) {
// cell width
const cellWidth = width ? `${width}px` : undefined;
const style: React.CSSProperties = {
width: width ? `${width}px` : undefined,
minWidth: width ? `${width}px` : undefined
width: cellWidth,
minWidth: cellWidth
};

// expander
Expand Down

0 comments on commit 5e9e006

Please sign in to comment.