IconGrid: experiment with various list virtualization libraries

This commit is contained in:
Tobias Fried
2020-07-17 13:24:00 -04:00
parent 44ac4455b7
commit e1dfe6aed1
6 changed files with 238 additions and 17 deletions

View File

@@ -10,9 +10,14 @@
"@types/node": "^12.0.0",
"@types/react": "^16.9.43",
"@types/react-dom": "^16.9.8",
"@types/react-list": "^0.8.5",
"@types/react-virtualized": "^9.21.10",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-list": "^0.8.15",
"react-scripts": "3.4.1",
"react-virtual": "^2.2.1",
"react-virtualized": "^9.21.2",
"recoil": "^0.0.10",
"typescript": "^3.9.6"
},

View File

@@ -5,9 +5,13 @@
.grid-item {
margin: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: aquamarine;
filter: grayscale(100%);
transition: 0.5s ease;
transition: filter 0.5s ease;
}
.grid-item:hover {

View File

@@ -1,27 +1,101 @@
import React from "react";
import React, { useRef, useCallback, useMemo } from "react";
import { useRecoilValue } from "recoil";
import { useVirtual } from "react-virtual";
import { filteredQueryResultsSelector } from "../../state/selectors";
import { iconColorAtom, iconSizeAtom } from "../../state/atoms";
import "./IconGrid.css";
type IconGridProps = {};
const COLUMN_COUNT = 5;
const IconGrid: React.FC<IconGridProps> = () => {
const parentRef = useRef<HTMLDivElement>(null);
const color = useRecoilValue(iconColorAtom);
const size = useRecoilValue(iconSizeAtom);
const filteredQueryResults = useRecoilValue(filteredQueryResultsSelector);
// const categorizedQueryResults = useRecoilValue(categorizedQueryResultsSelector);
// console.log(categorizedQueryResults);
// const rowVirtualizer = useVirtual({
// size: useMemo(() => Math.ceil(filteredQueryResults.length / COLUMN_COUNT), [
// filteredQueryResults,
// ]),
// parentRef,
// estimateSize: useCallback(() => 220 * COLUMN_COUNT, []),
// });
const rowVirtualizer = useVirtual({
size: useMemo(() => Math.ceil(filteredQueryResults.length / COLUMN_COUNT), [
filteredQueryResults,
]),
parentRef,
estimateSize: useCallback(() => 240, []),
overscan: 5,
});
const columnVirtualizer = useVirtual({
horizontal: true,
size: COLUMN_COUNT,
parentRef,
estimateSize: useCallback(() => 160, []),
});
return (
<div className="grid">
{filteredQueryResults.map((icon) => (
<div key={`ph-${icon.name}-${icon.style}`} className="grid-item">
<>
<div ref={parentRef} style={{ height: "90vh", width: "100%", overflowY: "auto" }}>
<div
style={{
height: `${rowVirtualizer.totalSize}px`,
// width: `${columnVirtualizer.totalSize}px`,
// height: `${rowVirtualizer.totalSize}px`,
width: "100%",
position: "relative",
}}
className="grid"
>
{rowVirtualizer.virtualItems.map((virtualRow) => (
<React.Fragment key={virtualRow.index}>
{columnVirtualizer.virtualItems.map((virtualColumn) => (
<div
key={virtualColumn.index}
style={{
position: "absolute",
top: 0,
left: 0,
width: `${virtualColumn.size}px`,
height: `${virtualRow.size}px`,
transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`,
}}
className="grid-item"
>
{(function () {
const icon =
filteredQueryResults[
virtualRow.index * COLUMN_COUNT + virtualColumn.index
];
if (!icon) return null;
return (
<>
<img
color={color}
style={{ height: size, width: size }}
src={icon.asset}
alt={`${icon.name} icon`}
width="100%"
/>
<div style={{ padding: 16 }}>{icon.name}</div>
<p style={{ padding: 16 }}>{icon.name}</p>
</>
);
})()}
</div>
))}
</React.Fragment>
))}
</div>
</div>
</>
);
};

View File

@@ -0,0 +1,34 @@
import React from "react";
import { useRecoilValue } from "recoil";
import ReactList from "react-list";
import { filteredQueryResultsSelector } from "../../state/selectors";
import "./IconGrid.css";
type IconGridProps = {};
const IconGridRL: React.FC<IconGridProps> = () => {
const filteredQueryResults = useRecoilValue(filteredQueryResultsSelector);
const renderItem = (index: number, key: number | string) => {
const icon = filteredQueryResults[index];
return (
<div key={key} className="grid-item" style={{ width: 120}}>
<img src={icon.asset} alt={`${icon.name} icon`} width="100%" />
<div style={{ padding: 16 }}>{icon.name}</div>
</div>
);
};
return (
<div style={{ overflow: "auto", maxHeight: 400 }}>
<ReactList
itemRenderer={renderItem}
length={filteredQueryResults.length}
type="uniform"
/>
</div>
);
};
export default IconGridRL;

View File

@@ -0,0 +1,34 @@
import React from "react";
import { useRecoilValue } from "recoil";
import { Grid } from "react-virtualized";
import { filteredQueryResultsSelector } from "../../state/selectors";
import "./IconGrid.css";
type IconGridProps = {};
const COLUMN_COUNT = 5;
export default class IconGridVirtualized extends React.PureComponent {
constructor(props: IconGridProps) {
super(props);
}
render() {
const filteredQueryResults = useRecoilValue(filteredQueryResultsSelector);
const cellRenderer = () => <div></div>
return (
<Grid
columnCount={COLUMN_COUNT}
columnWidth={100}
height={300}
rowCount={Math.ceil(filteredQueryResults.length / COLUMN_COUNT)}
rowHeight={220}
width={300}
cellRenderer={cellRenderer}
/>
)
}
}

View File

@@ -1067,7 +1067,7 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.1", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c"
integrity sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==
@@ -1328,6 +1328,11 @@
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
"@reach/observe-rect@^1.1.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2"
integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==
"@sheerun/mutationobserver-shim@^0.3.2":
version "0.3.3"
resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#5405ee8e444ed212db44e79351f0c70a582aae25"
@@ -1614,6 +1619,21 @@
dependencies:
"@types/react" "*"
"@types/react-list@^0.8.5":
version "0.8.5"
resolved "https://registry.yarnpkg.com/@types/react-list/-/react-list-0.8.5.tgz#813257a341788917541d0e4f631388aa1aa55190"
integrity sha512-DnVqL0peKL/kCyTWHW4JzNLP/hAgAfoSeJdInq4bbRVZAjwzpJ5M7E7xpNelS6NvHRfKFp5km+Q3317hXZM1gA==
dependencies:
"@types/react" "*"
"@types/react-virtualized@^9.21.10":
version "9.21.10"
resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.10.tgz#cd072dc9c889291ace2c4c9de8e8c050da8738b7"
integrity sha512-f5Ti3A7gGdLkPPFNHTrvKblpsPNBiQoSorOEOD+JPx72g/Ng2lOt4MYfhvQFQNgyIrAro+Z643jbcKafsMW2ag==
dependencies:
"@types/prop-types" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^16.9.43":
version "16.9.43"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.43.tgz#c287f23f6189666ee3bebc2eb8d0f84bcb6cdb6b"
@@ -3000,6 +3020,11 @@ clone-deep@^4.0.1:
kind-of "^6.0.2"
shallow-clone "^3.0.0"
clsx@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -3560,7 +3585,7 @@ cssstyle@^1.0.0, cssstyle@^1.1.1:
dependencies:
cssom "0.3.x"
csstype@^2.2.0:
csstype@^2.2.0, csstype@^2.6.7:
version "2.6.11"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.11.tgz#452f4d024149ecf260a852b025e36562a253ffc5"
integrity sha512-l8YyEC9NBkSm783PFTvh0FmJy7s5pFKrDp49ZL7zBGX3fWkO+N4EEyan1qqp8cwPLDcD0OSdyY6hAMoxp34JFw==
@@ -3819,6 +3844,14 @@ dom-converter@^0.2:
dependencies:
utila "~0.4"
dom-helpers@^5.0.0:
version "5.1.4"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b"
integrity sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==
dependencies:
"@babel/runtime" "^7.8.7"
csstype "^2.6.7"
dom-serializer@0:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -6623,7 +6656,7 @@ loglevel@^1.6.6:
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171"
integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -8478,7 +8511,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.4"
prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@15, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -8694,6 +8727,18 @@ react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-list@^0.8.15:
version "0.8.15"
resolved "https://registry.yarnpkg.com/react-list/-/react-list-0.8.15.tgz#990d46170c426b438787800f68f017cb51840e2e"
integrity sha512-ghb0XInIVkxNW0GM+JULAeaDAjxEcCaKa6kNFPvIVC4FDFYIU24B/+gumCoCvXWuPwcUmkcVL7yZ/WLEcFjebA==
dependencies:
prop-types "15"
react-scripts@3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.4.1.tgz#f551298b5c71985cc491b9acf3c8e8c0ae3ada0a"
@@ -8754,6 +8799,26 @@ react-scripts@3.4.1:
optionalDependencies:
fsevents "2.1.2"
react-virtual@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-virtual/-/react-virtual-2.2.1.tgz#639741f203b683630457836e85ddbba7a732d9b5"
integrity sha512-71wjhbm/WgFRKaaS/oAN6znUpQkdSdbRfg3jCyvgP45QAbBKH3lSiTR/nLawgqhlwGjcYp9jN94MLNW1UVoobQ==
dependencies:
"@reach/observe-rect" "^1.1.0"
ts-toolbelt "^6.4.2"
react-virtualized@^9.21.2:
version "9.21.2"
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.21.2.tgz#02e6df65c1e020c8dbf574ec4ce971652afca84e"
integrity sha512-oX7I7KYiUM7lVXQzmhtF4Xg/4UA5duSA+/ZcAvdWlTLFCoFYq1SbauJT5gZK9cZS/wdYR6TPGpX/dqzvTqQeBA==
dependencies:
babel-runtime "^6.26.0"
clsx "^1.0.1"
dom-helpers "^5.0.0"
loose-envify "^1.3.0"
prop-types "^15.6.0"
react-lifecycles-compat "^3.0.4"
react@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
@@ -10170,6 +10235,11 @@ ts-pnp@^1.1.6:
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
ts-toolbelt@^6.4.2:
version "6.13.11"
resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.13.11.tgz#46260d1cff0f2d635931df8d32239b73f2ed4f8d"
integrity sha512-QmvHwAofS1MAn1ndXCn2AVhxJLipaj4tK26SrhBTiydfgKtbLlU2efpIP/8Jocrc9ynNslZqrhqAesruJMlxCQ==
tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"