virtual datagrid

This commit is contained in:
Jan Prochazka 2020-02-01 18:00:44 +01:00
parent 421b30c2f5
commit 6f12b4fd14
7 changed files with 590 additions and 59 deletions

View File

@ -1,5 +1,5 @@
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://paypal.me/JanProchazkaCz)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://paypal.me/JanProchazkaCz/30eur)
# DbGate - database administration tool

View File

@ -16,6 +16,7 @@
"react-dom": "^16.12.0",
"react-modal": "^3.11.1",
"react-scripts": "3.3.0",
"resize-observer-polyfill": "^1.5.1",
"socket.io-client": "^2.3.0",
"styled-components": "^4.4.1",
"uuid": "^3.4.0"

View File

@ -1,11 +1,18 @@
import React, { useState } from 'react';
import React from 'react';
import useFetch from '../utility/useFetch';
import styled from 'styled-components';
import theme from '../theme';
import { HorizontalScrollBar, VerticalScrollBar } from './ScrollBars';
import useDimensions from '../utility/useDimensions';
import { SeriesSizes } from './SeriesSizes';
const GridContainer = styled.div``;
const GridContainer = styled.div`
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
`;
const Table = styled.table`
position: absolute;
@ -62,15 +69,24 @@ export default function DataGrid({ params }) {
params,
});
const { rows, columns } = data || {};
const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = useState(0);
const [firstVisibleRowScrollIndex, setFirstVisibleRowScrollIndex] = React.useState(0);
const [firstVisibleColumnScrollIndex, setFirstVisibleColumnScrollIndex] = React.useState(0);
const [headerRowRef, { height: rowHeight }] = useDimensions();
const [tableBodyRef, { height: gridScrollAreaHeight }] = useDimensions();
const [tableBodyRef] = useDimensions();
const [containerRef, { height: containerHeight, width: containerWidth }] = useDimensions();
// const visibleRowCountUpperBound = Math.ceil(gridScrollAreaHeight / Math.floor(rowHeight));
// const visibleRowCountLowerBound = Math.floor(gridScrollAreaHeight / Math.ceil(rowHeight));
const visibleRowCountUpperBound = 20;
const visibleRowCountLowerBound = 20;
const columnSizes = React.useMemo(() => countColumnSizes(), [data, containerWidth]);
console.log('containerWidth', containerWidth);
const gridScrollAreaHeight = containerHeight - 2 * rowHeight;
const gridScrollAreaWidth = containerWidth - columnSizes.frozenSize;
const visibleRowCountUpperBound = Math.ceil(gridScrollAreaHeight / Math.floor(rowHeight));
const visibleRowCountLowerBound = Math.floor(gridScrollAreaHeight / Math.ceil(rowHeight));
// const visibleRowCountUpperBound = 20;
// const visibleRowCountLowerBound = 20;
if (!columns || !rows) return null;
const rowCountNewIncluded = rows.length;
@ -79,16 +95,119 @@ export default function DataGrid({ params }) {
setFirstVisibleRowScrollIndex(value);
};
console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
console.log('gridScrollAreaHeight', gridScrollAreaHeight);
function countColumnSizes() {
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
//return this.context.measureText(txt).width;
const columnSizes = new SeriesSizes();
if (!rows || !columns) return columnSizes;
console.log('countColumnSizes', rows.length, containerWidth);
columnSizes.maxSize = (containerWidth * 2) / 3;
columnSizes.count = columns.length;
// columnSizes.setExtraordinaryIndexes(this.getHiddenColumnIndexes(), this.getFrozenColumnIndexes());
columnSizes.setExtraordinaryIndexes([], []);
// for (let colIndex = 0; colIndex < columns.length; colIndex++) {
// //this.columnSizes.PutSizeOverride(col, this.columns[col].Name.length * 8);
// let column = columns[colIndex];
// if (column.columnClientObject != null && column.columnClientObject.notNull) context.font = "bold 14px Helvetica";
// else context.font = "14px Helvetica";
// let text = column.headerText;
// let headerWidth = context.measureText(text).width + 32;
// if (column.columnClientObject != null && column.columnClientObject.icon != null) headerWidth += 16;
// if (this.getFilterOnColumn(column.uniquePath)) headerWidth += 16;
// if (this.getSortOrder(column.uniquePath)) headerWidth += 16;
// this.columnSizes.putSizeOverride(colIndex, headerWidth);
// }
// let headerWidth = this.rowHeaderWidthDefault;
// if (this.rowCount) headerWidth = context.measureText(this.rowCount.toString()).width + 8;
// this.rowHeaderWidth = this.rowHeaderWidthDefault;
// if (headerWidth > this.rowHeaderWidth) this.rowHeaderWidth = headerWidth;
context.font = '14px Helvetica';
for (let row of data.rows) {
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
let colName = columns[colIndex].name;
let text = row[colName];
let width = context.measureText(text).width + 8;
// console.log('colName', colName, text, width);
columnSizes.putSizeOverride(colIndex, width);
// let colName = this.columns[colIndex].uniquePath;
// let text: string = row[colName].gridText;
// let width = context.measureText(text).width + 8;
// if (row[colName].dataPrefix) width += context.measureText(row[colName].dataPrefix).width + 3;
// this.columnSizes.putSizeOverride(colIndex, width);
}
}
// for (let modelIndex = 0; modelIndex < this.columns.length; modelIndex++) {
// let width = getHashValue(this.widthHashPrefix + this.columns[modelIndex].uniquePath);
// if (width) this.columnSizes.putSizeOverride(modelIndex, _.toNumber(width), true);
// }
columnSizes.buildIndex();
return columnSizes;
}
// console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
// console.log('gridScrollAreaHeight', gridScrollAreaHeight);
// console.log('containerHeight', containerHeight);
const visibleColumnCount = columnSizes.getVisibleScrollCount(firstVisibleColumnScrollIndex, gridScrollAreaWidth);
console.log('visibleColumnCount', visibleColumnCount);
const visibleRealColumnIndexes = [];
const modelIndexes = {};
const realColumns = [];
// frozen columns
for (let colIndex = 0; colIndex < columnSizes.frozenCount; colIndex++) {
visibleRealColumnIndexes.push(colIndex);
}
// scroll columns
for (
let colIndex = firstVisibleColumnScrollIndex;
colIndex < firstVisibleColumnScrollIndex + visibleColumnCount;
colIndex++
) {
visibleRealColumnIndexes.push(colIndex + columnSizes.frozenCount);
}
// real columns
for (let colIndex of visibleRealColumnIndexes) {
let modelColumnIndex = columnSizes.realToModel(colIndex);
modelIndexes[colIndex] = modelColumnIndex;
let col = columns[modelColumnIndex];
if (!col) continue;
const widthNumber = columnSizes.getSizeByRealIndex(colIndex);
realColumns.push({
...col,
widthPx: `${widthNumber}px`,
});
}
console.log('visibleRealColumnIndexes', visibleRealColumnIndexes);
return (
<GridContainer>
<GridContainer ref={containerRef}>
<Table>
<TableHead>
<TableHeaderRow ref={headerRowRef}>
{columns.map(col => (
<TableHeaderCell key={col.name} style={{ width: '60px' }}>
{realColumns.map(col => (
<TableHeaderCell
key={col.name}
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
>
{col.name}
</TableHeaderCell>
))}
@ -99,8 +218,11 @@ export default function DataGrid({ params }) {
.slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
.map((row, index) => (
<TableBodyRow key={firstVisibleRowScrollIndex + index}>
{columns.map(col => (
<TableBodyCell key={col.name} style={{ width: '60px' }}>
{realColumns.map(col => (
<TableBodyCell
key={col.name}
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
>
{row[col.name]}
</TableBodyCell>
))}
@ -108,7 +230,11 @@ export default function DataGrid({ params }) {
))}
</TableBody>
</Table>
<HorizontalScrollBar minimum={0} maximum={columns.length - 1} />
<HorizontalScrollBar
minimum={0}
maximum={columns.length - 1}
viewportRatio={gridScrollAreaWidth / columnSizes.getVisibleScrollSizeSum()}
/>
<VerticalScrollBar
minimum={0}
maximum={rowCountNewIncluded - visibleRowCountUpperBound + 2}

View File

@ -37,13 +37,13 @@ export function HorizontalScrollBar({
maximum,
viewportRatio = 0.5,
}) {
const [ref, { width }] = useDimensions();
const [ref, { width }, node] = useDimensions();
const contentSize = Math.round(width / viewportRatio);
React.useEffect(() => {
const position01 = (valueToSet - minimum) / (maximum - minimum + 1);
const position = position01 * (contentSize - width);
if (ref.current) ref.current.scrollLeft = Math.floor(position);
if (node) node.scrollLeft = Math.floor(position);
}, [valueToSetDate]);
return (
@ -61,17 +61,17 @@ export function VerticalScrollBar({
maximum,
viewportRatio = 0.5,
}) {
const [ref, { height }] = useDimensions();
const [ref, { height }, node] = useDimensions();
const contentSize = Math.round(height / viewportRatio);
React.useEffect(() => {
const position01 = (valueToSet - minimum) / (maximum - minimum + 1);
const position = position01 * (contentSize - height);
if (ref.current) ref.current.scrollTop = Math.floor(position);
if (node) node.scrollTop = Math.floor(position);
}, [valueToSetDate]);
const handleScroll = () => {
const position = ref.current.scrollTop;
const position = node.scrollTop;
const ratio = position / (contentSize - height);
if (ratio < 0) return 0;
let res = ratio * (maximum - minimum + 1) + minimum;

View File

@ -0,0 +1,340 @@
import _ from 'lodash';
export class SeriesSizeItem {
constructor() {
this.scrollIndex = -1;
this.frozenIndex = -1;
this.modelIndex = 0;
this.size = 0;
this.position = 0;
}
// modelIndex;
// size;
// position;
get endPosition() {
return this.position + this.size;
}
}
export class SeriesSizes {
constructor() {
this.scrollItems = [];
this.sizeOverridesByModelIndex = {};
this.positions = [];
this.scrollIndexes = [];
this.frozenItems = [];
this.hiddenAndFrozenModelIndexes = null;
this.frozenModelIndexes = null;
this.count = 0;
this.maxSize = 1000;
this.defaultSize = 50;
}
// private sizeOverridesByModelIndex: { [id] } = {};
// count;
// defaultSize;
// maxSize;
// private hiddenAndFrozenModelIndexes[] = [];
// private frozenModelIndexes[] = [];
// private hiddenModelIndexes[] = [];
// private scrollItems: SeriesSizeItem[] = [];
// private positions[] = [];
// private scrollIndexes[] = [];
// private frozenItems: SeriesSizeItem[] = [];
get scrollCount() {
return this.count - (this.hiddenAndFrozenModelIndexes != null ? this.hiddenAndFrozenModelIndexes.length : 0);
}
get frozenCount() {
return this.frozenModelIndexes != null ? this.frozenModelIndexes.length : 0;
}
get frozenSize() {
return _.sumBy(this.frozenItems, x => x.size);
}
get realCount() {
return this.frozenCount + this.scrollCount;
}
putSizeOverride(modelIndex, size, sizeByUser = false) {
if (this.maxSize && size > this.maxSize && !sizeByUser) {
size = this.maxSize;
}
let currentSize = this.sizeOverridesByModelIndex[modelIndex];
if (sizeByUser || !currentSize || size > currentSize) {
this.sizeOverridesByModelIndex[modelIndex] = size;
}
// if (!_.has(this.sizeOverridesByModelIndex, modelIndex))
// this.sizeOverridesByModelIndex[modelIndex] = size;
// if (size > this.sizeOverridesByModelIndex[modelIndex])
// this.sizeOverridesByModelIndex[modelIndex] = size;
}
buildIndex() {
this.scrollItems = [];
this.scrollIndexes = _.filter(
_.map(this.intKeys(this.sizeOverridesByModelIndex), x => this.modelToReal(x) - this.frozenCount),
x => x >= 0
);
this.scrollIndexes.sort();
let lastScrollIndex = -1;
let lastEndPosition = 0;
this.scrollIndexes.forEach(scrollIndex => {
let modelIndex = this.realToModel(scrollIndex + this.frozenCount);
let size = this.sizeOverridesByModelIndex[modelIndex];
let item = new SeriesSizeItem();
item.scrollIndex = scrollIndex;
item.modelIndex = modelIndex;
item.size = size;
item.position = lastEndPosition + (scrollIndex - lastScrollIndex - 1) * this.defaultSize;
this.scrollItems.push(item);
lastScrollIndex = scrollIndex;
lastEndPosition = item.endPosition;
});
this.positions = _.map(this.scrollItems, x => x.position);
this.frozenItems = [];
let lastpos = 0;
for (let i = 0; i < this.frozenCount; i++) {
let modelIndex = this.frozenModelIndexes[i];
let size = this.getSizeByModelIndex(modelIndex);
let item = new SeriesSizeItem();
item.frozenIndex = i;
item.modelIndex = modelIndex;
item.size = size;
item.position = lastpos;
this.frozenItems.push(item);
lastpos += size;
}
}
getScrollIndexOnPosition(position) {
let itemOrder = _.sortedIndex(this.positions, position);
if (this.positions[itemOrder] == position) return itemOrder;
if (itemOrder == 0) return Math.floor(position / this.defaultSize);
if (position <= this.scrollItems[itemOrder - 1].endPosition) return this.scrollItems[itemOrder - 1].scrollIndex;
return (
Math.floor((position - this.scrollItems[itemOrder - 1].position) / this.defaultSize) +
this.scrollItems[itemOrder - 1].scrollIndex
);
}
getFrozenIndexOnPosition(position) {
this.frozenItems.forEach(function(item) {
if (position >= item.position && position <= item.endPosition) return item.frozenIndex;
});
return -1;
}
// getSizeSum(startScrollIndex, endScrollIndex) {
// let order1 = _.sortedIndexOf(this.scrollIndexes, startScrollIndex);
// let order2 = _.sortedIndexOf(this.scrollIndexes, endScrollIndex);
// let count = endScrollIndex - startScrollIndex;
// if (order1 < 0)
// order1 = ~order1;
// if (order2 < 0)
// order2 = ~order2;
// let result = 0;
// for (let i = order1; i <= order2; i++) {
// if (i < 0)
// continue;
// if (i >= this.scrollItems.length)
// continue;
// let item = this.scrollItems[i];
// if (item.scrollIndex < startScrollIndex)
// continue;
// if (item.scrollIndex >= endScrollIndex)
// continue;
// result += item.size;
// count--;
// }
// result += count * this.defaultSize;
// return result;
// }
getSizeByModelIndex(modelIndex) {
if (_.has(this.sizeOverridesByModelIndex, modelIndex)) return this.sizeOverridesByModelIndex[modelIndex];
return this.defaultSize;
}
getSizeByScrollIndex(scrollIndex) {
return this.getSizeByRealIndex(scrollIndex + this.frozenCount);
}
getSizeByRealIndex(realIndex) {
let modelIndex = this.realToModel(realIndex);
return this.getSizeByModelIndex(modelIndex);
}
removeSizeOverride(realIndex) {
let modelIndex = this.realToModel(realIndex);
delete this.sizeOverridesByModelIndex[modelIndex];
}
getScroll(sourceScrollIndex, targetScrollIndex) {
if (sourceScrollIndex < targetScrollIndex) {
return -_.sum(
_.map(_.range(sourceScrollIndex, targetScrollIndex - sourceScrollIndex), x => this.getSizeByScrollIndex(x))
);
} else {
return _.sum(
_.map(_.range(targetScrollIndex, sourceScrollIndex - targetScrollIndex), x => this.getSizeByScrollIndex(x))
);
}
}
modelIndexIsInScrollArea(modelIndex) {
let realIndex = this.modelToReal(modelIndex);
return realIndex >= this.frozenCount;
}
getTotalScrollSizeSum() {
let scrollSizeOverrides = _.map(
_.filter(this.intKeys(this.sizeOverridesByModelIndex), x => this.modelIndexIsInScrollArea(x)),
x => this.sizeOverridesByModelIndex[x]
);
return _.sum(scrollSizeOverrides) + (this.count - scrollSizeOverrides.length) * this.defaultSize;
}
getVisibleScrollSizeSum() {
let scrollSizeOverrides = _.map(
_.filter(this.intKeys(this.sizeOverridesByModelIndex), x => !_.includes(this.hiddenAndFrozenModelIndexes, x)),
x => this.sizeOverridesByModelIndex[x]
);
return (
_.sum(scrollSizeOverrides) +
(this.count - this.hiddenModelIndexes.length - scrollSizeOverrides.length) * this.defaultSize
);
}
intKeys(value) {
return _.keys(value).map(x => _.parseInt(x));
}
getPositionByRealIndex(realIndex) {
if (realIndex < 0) return 0;
if (realIndex < this.frozenCount) return this.frozenItems[realIndex].position;
return this.getPositionByScrollIndex(realIndex - this.frozenCount);
}
getPositionByScrollIndex(scrollIndex) {
let order = _.sortedIndex(this.scrollIndexes, scrollIndex);
if (this.scrollIndexes[order] == scrollIndex) return this.scrollItems[order].position;
order--;
if (order < 0) return scrollIndex * this.defaultSize;
return (
this.scrollItems[order].endPosition + (scrollIndex - this.scrollItems[order].scrollIndex - 1) * this.defaultSize
);
}
getVisibleScrollCount(firstVisibleIndex, viewportSize) {
let res = 0;
let index = firstVisibleIndex;
let count = 0;
while (res < viewportSize && index <= this.scrollCount) {
console.log('this.getSizeByScrollIndex(index)', this.getSizeByScrollIndex(index));
res += this.getSizeByScrollIndex(index);
index++;
count++;
}
console.log('getVisibleScrollCount', firstVisibleIndex, viewportSize, count);
return count;
}
getVisibleScrollCountReversed(lastVisibleIndex, viewportSize) {
let res = 0;
let index = lastVisibleIndex;
let count = 0;
while (res < viewportSize && index >= 0) {
res += this.getSizeByScrollIndex(index);
index--;
count++;
}
return count;
}
invalidateAfterScroll(oldFirstVisible, newFirstVisible, invalidate, viewportSize) {
if (newFirstVisible > oldFirstVisible) {
let oldVisibleCount = this.getVisibleScrollCount(oldFirstVisible, viewportSize);
let newVisibleCount = this.getVisibleScrollCount(newFirstVisible, viewportSize);
for (let i = oldFirstVisible + oldVisibleCount - 1; i <= newFirstVisible + newVisibleCount; i++) {
invalidate(i + this.frozenCount);
}
} else {
for (let i = newFirstVisible; i <= oldFirstVisible; i++) {
invalidate(i + this.frozenCount);
}
}
}
isWholeInView(firstVisibleIndex, index, viewportSize) {
let res = 0;
let testedIndex = firstVisibleIndex;
while (res < viewportSize && testedIndex < this.count) {
res += this.getSizeByScrollIndex(testedIndex);
if (testedIndex == index) return res <= viewportSize;
testedIndex++;
}
return false;
}
scrollInView(firstVisibleIndex, scrollIndex, viewportSize) {
if (this.isWholeInView(firstVisibleIndex, scrollIndex, viewportSize)) {
return firstVisibleIndex;
}
if (scrollIndex < firstVisibleIndex) {
return scrollIndex;
}
let res = 0;
let testedIndex = scrollIndex;
while (res < viewportSize && testedIndex >= 0) {
let size = this.getSizeByScrollIndex(testedIndex);
if (res + size > viewportSize) return testedIndex + 1;
testedIndex--;
res += size;
}
if (res >= viewportSize && testedIndex < scrollIndex) return testedIndex + 1;
return firstVisibleIndex;
}
resize(realIndex, newSize) {
if (realIndex < 0) return;
let modelIndex = this.realToModel(realIndex);
if (modelIndex < 0) return;
this.sizeOverridesByModelIndex[modelIndex] = newSize;
this.buildIndex();
}
setExtraordinaryIndexes(hidden, frozen) {
//this._hiddenAndFrozenModelIndexes = _.clone(hidden);
hidden = hidden.filter(x => x >= 0);
frozen = frozen.filter(x => x >= 0);
hidden.sort((a, b) => a - b);
frozen.sort((a, b) => a - b);
this.frozenModelIndexes = _.filter(frozen, x => !_.includes(hidden, x));
this.hiddenModelIndexes = _.filter(hidden, x => !_.includes(frozen, x));
this.hiddenAndFrozenModelIndexes = _.concat(hidden, this.frozenModelIndexes);
this.frozenModelIndexes.sort((a, b) => a - b);
if (this.hiddenAndFrozenModelIndexes.length == 0) this.hiddenAndFrozenModelIndexes = null;
if (this.frozenModelIndexes.length == 0) this.frozenModelIndexes = null;
this.buildIndex();
}
realToModel(realIndex) {
if (this.hiddenAndFrozenModelIndexes == null && this.frozenModelIndexes == null) return realIndex;
if (realIndex < 0) return -1;
if (realIndex < this.frozenCount && this.frozenModelIndexes != null) return this.frozenModelIndexes[realIndex];
if (this.hiddenAndFrozenModelIndexes == null) return realIndex;
realIndex -= this.frozenCount;
for (let hidItem of this.hiddenAndFrozenModelIndexes) {
if (realIndex < hidItem) return realIndex;
realIndex++;
}
return realIndex;
}
modelToReal(modelIndex) {
if (this.hiddenAndFrozenModelIndexes == null && this.frozenModelIndexes == null) return modelIndex;
if (modelIndex < 0) return -1;
let frozenIndex = this.frozenModelIndexes != null ? _.indexOf(this.frozenModelIndexes, modelIndex) : -1;
if (frozenIndex >= 0) return frozenIndex;
if (this.hiddenAndFrozenModelIndexes == null) return modelIndex;
let hiddenIndex = _.sortedIndex(this.hiddenAndFrozenModelIndexes, modelIndex);
if (this.hiddenAndFrozenModelIndexes[hiddenIndex] == modelIndex) return -1;
if (hiddenIndex >= 0) return modelIndex - hiddenIndex + this.frozenCount;
return modelIndex;
}
getFrozenPosition(frozenIndex) {
return this.frozenItems[frozenIndex].position;
}
hasSizeOverride(modelIndex) {
return _.has(this.sizeOverridesByModelIndex, modelIndex);
}
isVisible(testedRealIndex, firstVisibleScrollIndex, viewportSize) {
if (testedRealIndex < 0) return false;
if (testedRealIndex >= 0 && testedRealIndex < this.frozenCount) return true;
let scrollIndex = testedRealIndex - this.frozenCount;
let onPageIndex = scrollIndex - firstVisibleScrollIndex;
return onPageIndex >= 0 && onPageIndex < this.getVisibleScrollCount(firstVisibleScrollIndex, viewportSize);
}
}

View File

@ -1,48 +1,107 @@
import { useState, useCallback, useLayoutEffect } from 'react';
// import { useState, useCallback, useLayoutEffect } from 'react';
function getDimensionObject(node) {
const rect = node.getBoundingClientRect();
// function getDimensionObject(node) {
// const rect = node.getBoundingClientRect();
return {
width: rect.width,
height: rect.height,
top: 'x' in rect ? rect.x : rect.top,
left: 'y' in rect ? rect.y : rect.left,
x: 'x' in rect ? rect.x : rect.left,
y: 'y' in rect ? rect.y : rect.top,
right: rect.right,
bottom: rect.bottom,
};
}
// return {
// width: rect.width,
// height: rect.height,
// top: 'x' in rect ? rect.x : rect.top,
// left: 'y' in rect ? rect.y : rect.left,
// x: 'x' in rect ? rect.x : rect.left,
// y: 'y' in rect ? rect.y : rect.top,
// right: rect.right,
// bottom: rect.bottom,
// };
// }
function useDimensions({ liveMeasure = true } = {}) {
const [dimensions, setDimensions] = useState({});
// function useDimensions({ liveMeasure = true } = {}) {
// const [dimensions, setDimensions] = useState({});
// const [node, setNode] = useState(null);
// const ref = useCallback(node => {
// setNode(node);
// }, []);
// useLayoutEffect(() => {
// if (node) {
// const measure = () => window.requestAnimationFrame(() => setDimensions(getDimensionObject(node)));
// measure();
// if (liveMeasure) {
// window.addEventListener('resize', measure);
// window.addEventListener('scroll', measure);
// return () => {
// window.removeEventListener('resize', measure);
// window.removeEventListener('scroll', measure);
// };
// }
// }
// }, [node]);
// return [ref, dimensions, node];
// }
// export default useDimensions;
import { useLayoutEffect, useState, useCallback } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
// Export hook
export default function useDimensions(dependencies = []) {
const [node, setNode] = useState(null);
const ref = useCallback(node => {
setNode(node);
const ref = useCallback(newNode => {
setNode(newNode);
}, []);
// Keep track of measurements
const [dimensions, setDimensions] = useState({
x: 0,
y: 0,
left: 0,
top: 0,
right: 0,
bottom: 0,
width: 0,
height: 0,
});
// Define measure function
const measure = useCallback(innerNode => {
const rect = innerNode.getBoundingClientRect();
setDimensions({
x: rect.left,
y: rect.top,
left: rect.left,
top: rect.top,
right: rect.right,
bottom: rect.bottom,
width: rect.width,
height: rect.height,
});
}, []);
// @ts-ignore
ref.current = node;
useLayoutEffect(() => {
if (node) {
const measure = () => window.requestAnimationFrame(() => setDimensions(getDimensionObject(node)));
measure();
if (liveMeasure) {
window.addEventListener('resize', measure);
window.addEventListener('scroll', measure);
return () => {
window.removeEventListener('resize', measure);
window.removeEventListener('scroll', measure);
};
}
if (!node) {
return;
}
}, [node]);
// Set initial measurements
measure(node);
// Observe resizing of element
const resizeObserver = new ResizeObserver(() => {
measure(node);
});
resizeObserver.observe(node);
// Cleanup
return () => {
resizeObserver.disconnect();
};
}, [node, measure, ...dependencies]);
return [ref, dimensions, node];
}
export default useDimensions;

View File

@ -8981,6 +8981,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resize-observer-polyfill@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"