Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add virtualization in province contacts page #134

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 104 additions & 76 deletions components/contact-list.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useRef } from "react";

import { CopyButton } from "../components/copy-button";
import { anchorTransformer } from "../lib/htmr-transformers";
import { Contact } from "../lib/provinces";
Expand All @@ -12,6 +14,7 @@ import {
import htmr from "htmr";
import { HtmrOptions } from "htmr/src/types";
import Link from "next/link";
import { useVirtual } from "react-virtual";

type ContactListProps = {
data: Contact[];
Expand All @@ -23,90 +26,115 @@ export function ContactList(props: ContactListProps) {
a: anchorTransformer,
};

const parentRef = useRef<HTMLUListElement>(null);

const rowVirtualizer = useVirtual({
size: props.data.length,
parentRef,
});

return (
<div className="bg-white shadow overflow-hidden sm:rounded-md">
<ul className="divide-y divide-gray-200">
{props.data.map((contact, index) => (
<li key={index}>
<div className="px-4 py-4 sm:px-6 relative hover:bg-gray-50">
<div className="flex items-center justify-between">
<Link href={`/provinces/${props.provinceSlug}/${contact.slug}`}>
<a className="text-sm font-semibold text-blue-600 truncate block helper-link-cover">
{isNotEmpty(contact.penyedia)
? contact.penyedia
: contact.keterangan}
</a>
</Link>
<div className="ml-2 flex-shrink-0 flex">
<p className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
{contact.kebutuhan}
<div className="bg-white shadow sm:rounded-md overflow-hidden">
<ul
className="divide-y divide-gray-200 h-screen relative overflow-y-auto overflow-x-hidden"
ref={parentRef}
>
{rowVirtualizer.virtualItems.map((virtualRow) => {
const contact: Contact = props.data[virtualRow.index];
return (
<li
key={virtualRow.index}
ref={virtualRow.measureRef}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
width: "100%",
height: `${rowVirtualizer.totalSize}px`,
width: "100%",

As @andriawan mentioned in his comment, have you tried doing this before?

Copy link
Author

@suliskh suliskh Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've tried that, Mas, but unfortunately, it does not solve the main problem. Instead, I use totalSize to create an inner container of the list.

My actual concern is that we ended up having two vertical scrolls which I think is a bad scrolling experience, particularly on a mobile viewport. Are you okay with that behaviour, Mas? Would you mind confirming that scrolling experience yourself via the deploy preview, Mas @zainfathoni? :sungkem:

Double.Scroll.mp4
Double.Scroll.Mobile.mp4

Copy link
Member

@zainfathoni zainfathoni Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am aware of it. In my opinion, before we need to merge this, we need to simplify the Search and Filter UI, because it takes too much space.

I think we can use something like this to squeeze the filtering & sorting UI into a smaller space, what do you think, @resir014?

Upload.from.GitHub.for.iOS.MOV

If we can squeeze the filtering & sorting UI into a smaller space, we can make the height of the page absolute, so no more scrolling on the page itself.

Copy link
Member

@resir014 resir014 Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, agree @zainfathoni.

I'm not sure about the double scroll though. It feels a little too janky. Unless we can simplify the filter to make the virtualised list fit the whole screen we should hold off on merging this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zainfathoni Let's create a separate issue for the simplified filter. I can work on this later tonight.

transform: `translateY(${virtualRow.start}px)`,
}}
>
<div className="px-4 py-4 sm:px-6 relative hover:bg-gray-50">
<div className="flex items-center justify-between">
<Link
href={`/provinces/${props.provinceSlug}/${contact.slug}`}
>
<a className="text-sm font-semibold text-blue-600 truncate block helper-link-cover">
{isNotEmpty(contact.penyedia)
? contact.penyedia
: contact.keterangan}
</a>
</Link>
<div className="ml-2 flex-shrink-0 flex">
<p className="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
{contact.kebutuhan}
</p>
</div>
</div>
<div className="mt-2 sm:flex sm:justify-between">
<p className="text-sm font-medium text-gray-600 truncate">
{contact.keterangan}
</p>
{isNotEmpty(contact.terakhir_update) ? (
<div className="mt-2 mb-3 flex items-center text-xs text-gray-500 sm:my-0">
<BadgeCheckIcon
aria-hidden="true"
className="flex-shrink-0 h-4 w-4 sm:order-2 text-green-400"
/>
<p className="ml-2 mr-1">
Terverifikasi{" "}
{contact.terakhir_update && (
<time dateTime={contact.terakhir_update}>
{contact.terakhir_update}
</time>
)}
</p>
</div>
) : (
<div className="mt-2 mb-3 flex items-center text-xs text-gray-400 sm:my-0">
<BadgeCheckIconUnverified
aria-hidden="true"
className="flex-shrink-0 h-4 w-4 sm:order-2 text-gray-400"
/>
<p className="ml-2 mr-1">Belum terverifkasi</p>
</div>
)}
</div>
</div>
<div className="mt-2 sm:flex sm:justify-between">
<p className="text-sm font-medium text-gray-600 truncate">
{contact.keterangan}
</p>
{isNotEmpty(contact.terakhir_update) ? (
<div className="mt-2 mb-3 flex items-center text-xs text-gray-500 sm:my-0">
<BadgeCheckIcon
aria-hidden="true"
className="flex-shrink-0 h-4 w-4 sm:order-2 text-green-400"
/>
<p className="ml-2 mr-1">
Terverifikasi{" "}
{contact.terakhir_update && (
<time dateTime={contact.terakhir_update}>
{contact.terakhir_update}
</time>
)}
{isNotEmpty(contact.kontak) && (
<div className="mt-2 flex justify-between w-full">
<p className="flex items-center text-sm text-gray-500">
<PhoneIcon
aria-hidden="true"
className="flex-shrink-0 mr-2 h-4 w-4 text-gray-400"
/>
{htmr(contact.kontak as string, {
transform: htmrTransform,
})}
</p>
{typeof contact.kontak == "string" && (
<CopyButton text={stripTags(contact.kontak)} />
)}
</div>
) : (
<div className="mt-2 mb-3 flex items-center text-xs text-gray-400 sm:my-0">
<BadgeCheckIconUnverified
aria-hidden="true"
className="flex-shrink-0 h-4 w-4 sm:order-2 text-gray-400"
/>
<p className="ml-2 mr-1">Belum terverifkasi</p>
)}
{isNotEmpty(contact.alamat) && (
<div className="mt-2 flex justify-between w-full">
<p className="mt-2 flex items-start text-sm text-gray-500 sm:mt-0">
<LocationMarkerIcon
aria-hidden="true"
className="flex-shrink-0 mr-2 h-4 w-4 text-gray-400"
/>
{htmr(contact.alamat as string, {
transform: htmrTransform,
})}
</p>
{typeof contact.alamat == "string" && (
<CopyButton text={stripTags(contact.alamat)} />
)}
</div>
)}
</div>
{isNotEmpty(contact.kontak) && (
<div className="mt-2 flex justify-between w-full">
<p className="flex items-center text-sm text-gray-500">
<PhoneIcon
aria-hidden="true"
className="flex-shrink-0 mr-2 h-4 w-4 text-gray-400"
/>
{htmr(contact.kontak as string, {
transform: htmrTransform,
})}
</p>
{typeof contact.kontak == "string" && (
<CopyButton text={stripTags(contact.kontak)} />
)}
</div>
)}
{isNotEmpty(contact.alamat) && (
<div className="mt-2 flex justify-between w-full">
<p className="mt-2 flex items-start text-sm text-gray-500 sm:mt-0">
<LocationMarkerIcon
aria-hidden="true"
className="flex-shrink-0 mr-2 h-4 w-4 text-gray-400"
/>
{htmr(contact.alamat as string, {
transform: htmrTransform,
})}
</p>
{typeof contact.alamat == "string" && (
<CopyButton text={stripTags(contact.alamat)} />
)}
</div>
)}
</div>
</li>
))}
</li>
);
})}
</ul>
</div>
);
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
"next-seo": "^4.26.0",
"preact": "^10.5.14",
"react": "17.0.2",
"react-dom": "17.0.2"
"react-dom": "17.0.2",
"react-virtual": "^2.8.0",
"react-virtualized-auto-sizer": "^1.0.5",
"react-window": "^1.8.6"
suliskh marked this conversation as resolved.
Show resolved Hide resolved
},
"devDependencies": {
"@netlify/plugin-lighthouse": "2.1.2",
Expand Down
32 changes: 31 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,7 @@
dependencies:
regenerator-runtime "^0.13.4"

"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.9.6":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.9.6":
version "7.14.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
Expand Down Expand Up @@ -1198,6 +1198,11 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@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==

"@rushstack/eslint-patch@^1.0.6":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.0.6.tgz#023d72a5c4531b4ce204528971700a78a85a0c50"
Expand Down Expand Up @@ -6628,6 +6633,11 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=

"memoize-one@>=3.1.1 <6":
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==

meow@^3.3.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
Expand Down Expand Up @@ -8381,6 +8391,26 @@ react-refresh@0.8.3:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==

react-virtual@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/react-virtual/-/react-virtual-2.8.0.tgz#d05d9a5e0c9c594708ce4ce88bb33e2b0b66487e"
integrity sha512-VATjk/+4fk8daERyz/hOcZ20yMErSh/v9g9Ayqp+zcNgPAT1pnjpnUmZGscp87TP1aqLKMbl05+mcUbg65pJAg==
dependencies:
"@reach/observe-rect" "^1.1.0"

react-virtualized-auto-sizer@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.5.tgz#9eeeb8302022de56fbd7a860b08513120ce36509"
integrity sha512-kivjYVWX15TX2IUrm8F1jaCEX8EXrpy3DD+u41WGqJ1ZqbljWpiwscV+VxOM1l7sSIM1jwi2LADjhhAJkJ9dxA==

react-window@^1.8.6:
version "1.8.6"
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112"
integrity sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==
dependencies:
"@babel/runtime" "^7.0.0"
memoize-one ">=3.1.1 <6"

react@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
Expand Down