Skip to content

Commit 96d2754

Browse files
authored
Merge pull request #84 from mckervinc/feature/resize-observer
v0.5.0 - handle resize in a modern fashion
2 parents 5e9e006 + 11d26d8 commit 96d2754

File tree

10 files changed

+82
-124
lines changed

10 files changed

+82
-124
lines changed

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
# CHANGELOG
22

3+
## 0.5.0
4+
5+
_2023-10-02_
6+
7+
### Features
8+
9+
- uses a wrapper library to handle observing the table container for width/height changes
10+
- initial flicker for variable-row-size tables should be mitigated
11+
- more typescript specifications
12+
313
## 0.4.10
414

515
_2023-09-30_
616

7-
###
17+
### Features
818

919
- added ability to specify the `footerHeight`
1020
- removed some typescript warnings

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import { Table } from "react-fluid-table";
2323

2424
const data = _.range(100).map(i => ({
2525
id: i + 1,
26-
firstName: faker.name.firstName(),
27-
lastName: faker.name.lastName(),
28-
email: faker.internet.email()
26+
firstName: randFirstName(),
27+
lastName: randLastName(),
28+
email: randEmail()
2929
}));
3030

3131
const columns = [

example/src/ColumnProps.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,16 @@ const data: PropData[] = [
129129
type: "FooterElement",
130130
description: (
131131
<div>
132-
This property allows you to customize the content inside of a footer cell. The library will create
133-
a cell container for you with the proper column widths and resizability. If this field is
134-
defined, then this will get rendered inside of the cell container in the footer.
132+
This property allows you to customize the content inside of a footer cell. The library will
133+
create a cell container for you with the proper column widths and resizability. If this
134+
field is defined, then this will get rendered inside of the cell container in the footer.
135135
</div>
136136
)
137137
}
138138
];
139139

140-
const ColumnPropsTable = () => <StyledTable data={data} columns={columns} tableHeight={400} />;
140+
const ColumnPropsTable = () => (
141+
<StyledTable borders data={data} columns={columns} tableHeight={400} />
142+
);
141143

142144
export default ColumnPropsTable;

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-fluid-table",
3-
"version": "0.4.10",
3+
"version": "0.5.0",
44
"description": "A React table inspired by react-window",
55
"author": "Mckervin Ceme <mckervinc@live.com>",
66
"license": "MIT",
@@ -65,28 +65,29 @@
6565
"@testing-library/react-hooks": "^8.0.1",
6666
"@types/react-window": "^1.8.5",
6767
"cross-env": "^7.0.3",
68-
"eslint-config-standard-react": "^13.0.0",
68+
"eslint": "8.50.0",
6969
"eslint-config-standard": "^17.1.0",
70+
"eslint-config-standard-react": "^13.0.0",
7071
"eslint-plugin-flowtype": "^8.0.3",
7172
"eslint-plugin-import": "^2.28.1",
7273
"eslint-plugin-node": "^11.1.0",
7374
"eslint-plugin-prettier": "^5.0.0",
7475
"eslint-plugin-promise": "^6.1.1",
7576
"eslint-plugin-react": "^7.33.2",
76-
"eslint": "8.50.0",
7777
"gh-pages": "^6.0.0",
7878
"postcss": "^8.4.30",
7979
"prettier": "^3.0.3",
8080
"react": "^18.2.0",
81+
"rollup": "^3.29.3",
8182
"rollup-plugin-analyzer": "^4.0.0",
8283
"rollup-plugin-bundle-size": "^1.0.3",
8384
"rollup-plugin-peer-deps-external": "^2.2.4",
8485
"rollup-plugin-postcss": "^4.0.2",
8586
"rollup-plugin-visualizer": "^5.9.2",
86-
"rollup": "^3.29.3",
8787
"typescript": "^5.2.2"
8888
},
8989
"dependencies": {
90+
"react-resize-detector": "^9.1.0",
9091
"react-window": "^1.8.9"
9192
},
9293
"volta": {

rollup.config.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ const config = {
3737
url({ exclude: ["**/*.svg"] }),
3838
babel({
3939
extensions,
40-
exclude: "node_modules/**"
40+
exclude: "node_modules/**",
41+
babelHelpers: "bundled"
4142
}),
4243
resolve({ extensions }),
4344
commonjs(),

src/AutoSizer.tsx

Lines changed: 13 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
1+
import React, { useContext, useMemo } from "react";
2+
import { useResizeDetector } from "react-resize-detector";
23
import { TableContext } from "./TableContext";
34
import { DEFAULT_FOOTER_HEIGHT, DEFAULT_HEADER_HEIGHT, DEFAULT_ROW_HEIGHT } from "./constants";
45
import { findFooterByUuid, findHeaderByUuid } from "./util";
@@ -90,7 +91,7 @@ const calculateHeight = (
9091
const borderOffset = !!table ? table.offsetHeight - table.clientHeight : 0;
9192

9293
// if there are rows, let's do the calculation
93-
if (!!nodes.length) {
94+
if (nodes.length) {
9495
if (rowHeight > 0) {
9596
return headerOffset + nodes.length * rowHeight + footerOffset + borderOffset;
9697
}
@@ -116,8 +117,8 @@ const calculateHeight = (
116117
/**
117118
* This is a skinny AutoSizer based on react-virtualized-auto-sizer.
118119
* This removes the `bailout` functionality in order to allow the Table
119-
* to generate its own height. This also ignores a resize if the
120-
* dimensions of the window did not actually change (one less render).
120+
* to generate its own height. This uses ResizeObserver to observe the
121+
* container when it changes in order to provide the correct height
121122
*/
122123
const AutoSizer = ({
123124
numRows,
@@ -131,22 +132,22 @@ const AutoSizer = ({
131132
children
132133
}: AutoSizerProps) => {
133134
// hooks
134-
const resizeRef = useRef(0);
135-
const ref = useRef<HTMLDivElement>(null);
135+
const {
136+
ref,
137+
width: containerWidth,
138+
height: containerHeight
139+
} = useResizeDetector<HTMLDivElement>();
136140
const { uuid, columns, footerComponent } = useContext(TableContext);
137-
const [dimensions, setDimensions] = useState({ containerHeight: 0, containerWidth: 0 });
138141

139142
// variables
140-
const { containerHeight, containerWidth } = dimensions;
141143
const hasFooter = useMemo(
142144
() => !!footerComponent || !!columns.find(c => !!c.footer),
143145
[columns, footerComponent]
144146
);
145-
const fixedTableSize = !!tableHeight && tableHeight > 0 && !!tableWidth && tableWidth > 0;
146147

147148
// calculate the computed height
148149
const computedHeight = useMemo(() => {
149-
if (!!tableHeight && tableHeight > 0) {
150+
if (tableHeight && tableHeight > 0) {
150151
return tableHeight;
151152
}
152153

@@ -163,64 +164,14 @@ const AutoSizer = ({
163164
// calculate the actual height of the table
164165
const height = findCorrectHeight({
165166
computedHeight,
166-
containerHeight,
167+
containerHeight: containerHeight || 0,
167168
tableHeight: tableHeight || 0,
168169
minHeight: minTableHeight || 0,
169170
maxHeight: maxTableHeight || 0
170171
});
171172

172173
// get actual width
173-
const width = !!tableWidth && tableWidth > 0 ? tableWidth : containerWidth;
174-
175-
// functions
176-
const calculateDimensions = useCallback(() => {
177-
// base cases
178-
if (!ref.current?.parentElement || fixedTableSize) {
179-
return;
180-
}
181-
182-
// get style
183-
const parent = ref.current.parentElement;
184-
const style = window.getComputedStyle(parent);
185-
const paddingLeft = parseInt(style.paddingLeft) || 0;
186-
const paddingRight = parseInt(style.paddingRight) || 0;
187-
const paddingTop = parseInt(style.paddingTop) || 0;
188-
const paddingBottom = parseInt(style.paddingBottom) || 0;
189-
190-
// find new dimensions
191-
const newHeight = Math.max((parent.offsetHeight || 0) - paddingTop - paddingBottom, 0);
192-
const newWidth = Math.max((parent.offsetWidth || 0) - paddingLeft - paddingRight, 0);
193-
194-
// update state
195-
setDimensions(prev => {
196-
const { containerHeight: oldHeight, containerWidth: oldWidth } = prev;
197-
if (oldHeight !== newHeight || oldWidth !== newWidth) {
198-
return { containerHeight: newHeight, containerWidth: newWidth };
199-
}
200-
201-
return prev;
202-
});
203-
}, [fixedTableSize]);
204-
205-
const onResize = useCallback(() => {
206-
window.clearTimeout(resizeRef.current);
207-
resizeRef.current = window.setTimeout(calculateDimensions, 40);
208-
}, [calculateDimensions]);
209-
210-
// effects
211-
// on mount, calculate the dimensions
212-
useEffect(() => calculateDimensions(), [calculateDimensions]);
213-
214-
// on resize, we have to re-calculate the dimensions
215-
useEffect(() => {
216-
window.removeEventListener("resize", onResize);
217-
window.addEventListener("resize", onResize);
218-
const m = resizeRef.current;
219-
return () => {
220-
window.clearTimeout(m);
221-
window.removeEventListener("resize", onResize);
222-
};
223-
}, [onResize]);
174+
const width = tableWidth && tableWidth > 0 ? tableWidth : containerWidth || 0;
224175

225176
return <div ref={ref}>{height || width ? children({ height, width }) : null}</div>;
226177
};

src/Row.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import React, { SVGProps, useCallback, useContext, useLayoutEffect, useRef } fro
22
import { CacheFunction, ColumnProps, RowRenderProps, SubComponentProps } from "../index";
33
//@ts-ignore TS2307
44
import Minus from "./svg/minus-circle.svg";
5+
import { ListChildComponentProps } from "react-window";
6+
import { TableContext } from "./TableContext";
57
//@ts-ignore TS2307
68
import Plus from "./svg/plus-circle.svg";
7-
import { TableContext } from "./TableContext";
8-
import { ListChildComponentProps } from "react-window";
99
import { cx } from "./util";
1010

1111
interface TableCellProps<T> {
@@ -197,7 +197,7 @@ function Row<T>({
197197
const containerHeight = !rowHeight ? undefined : isExpanded && SubComponent ? rowHeight : "100%";
198198

199199
// sub component props
200-
const subProps = { row, index, isExpanded, clearSizeCache };
200+
const subProps: SubComponentProps<T> = { row, index, isExpanded, clearSizeCache };
201201

202202
// row styling
203203
const borderBottom = borders ? undefined : "none";
@@ -221,7 +221,7 @@ function Row<T>({
221221
if (height !== calculateHeight(rowRef.current, index)) {
222222
clearSizeCache(index);
223223
}
224-
}, [rowRef, index, height, calculateHeight, clearSizeCache, pixelWidths]);
224+
}, [index, height, calculateHeight, clearSizeCache, pixelWidths]);
225225

226226
// effects
227227
// on expansion, clear the cache
@@ -234,7 +234,7 @@ function Row<T>({
234234
}
235235

236236
expandedCalledRef.current = false;
237-
}, [isExpanded, expandedCalledRef, resetHeight, index, clearSizeCache]);
237+
}, [isExpanded, resetHeight, index, clearSizeCache]);
238238

239239
return (
240240
<div

0 commit comments

Comments
 (0)