From 754ddeaa8d3e3e4f3efc93d5bb22c68c31bb5c36 Mon Sep 17 00:00:00 2001
From: Ellie Huxtable <ellie@elliehuxtable.com>
Date: Mon, 6 May 2024 08:11:47 +0100
Subject: feat(ui): scroll history infinitely (#1999)

* wip, history scrolls right!

* wip

* virtual scroll fucking worksssss

* paging works :)

* scroll search results now too
---
 ui/src/components/HistoryList.tsx | 126 +++++++++++++++++++++-----------------
 1 file changed, 69 insertions(+), 57 deletions(-)

(limited to 'ui/src/components/HistoryList.tsx')

diff --git a/ui/src/components/HistoryList.tsx b/ui/src/components/HistoryList.tsx
index 9616ecf0..7cdeacd8 100644
--- a/ui/src/components/HistoryList.tsx
+++ b/ui/src/components/HistoryList.tsx
@@ -1,3 +1,4 @@
+import { useRef } from "react";
 import { ChevronRightIcon } from "@heroicons/react/20/solid";
 
 // @ts-ignore
@@ -19,70 +20,81 @@ function msToTime(ms: number) {
 
 export default function HistoryList(props: any) {
   return (
-    <ul
+    <div
       role="list"
-      className="divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5"
+      className="divide-y divide-gray-100 bg-white shadow-sm ring-1 ring-gray-900/5 overflow-auto"
+      style={{
+        height: `${props.height}px`,
+        position: "relative",
+      }}
     >
-      {props.history.map((h: any) => (
-        <li
-          key={h.id}
-          className="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6"
-        >
-          <div className="flex min-w-0 gap-x-4">
-            <div className="flex flex-col justify-center">
-              <p className="flex text-xs text-gray-500 justify-center">
-                {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
-                  DateTime.TIME_WITH_SECONDS,
-                )}
-              </p>
-              <p className="flex text-xs mt-1 text-gray-400 justify-center">
-                {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
-                  DateTime.DATE_SHORT,
-                )}
-              </p>
-            </div>
-            <div className="min-w-0 flex-col justify-center">
-              <pre className="whitespace-pre-wrap">
-                <code className="text-sm">{h.command}</code>
-              </pre>
-              <p className="mt-1 flex text-xs leading-5 text-gray-500">
-                <span className="relative truncate ">{h.user}</span>
+      {props.items.map((i: any) => {
+        let h = props.history[i.index];
 
-                <span>&nbsp;on&nbsp;</span>
+        return (
+          <li
+            key={h.id}
+            className="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6"
+            style={{
+              position: "absolute",
+              top: 0,
+              left: 0,
+              width: "100%",
+              height: `${i.size}px`,
+              transform: `translateY(${i.start}px)`,
+            }}
+          >
+            <div className="flex min-w-0 gap-x-4">
+              <div className="flex flex-col justify-center">
+                <p className="flex text-xs text-gray-500 justify-center">
+                  {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
+                    DateTime.TIME_WITH_SECONDS,
+                  )}
+                </p>
+                <p className="flex text-xs mt-1 text-gray-400 justify-center">
+                  {DateTime.fromMillis(h.timestamp / 1000000).toLocaleString(
+                    DateTime.DATE_SHORT,
+                  )}
+                </p>
+              </div>
+              <div className="min-w-0 flex-col justify-center">
+                <pre className="whitespace-pre-wrap">
+                  <code className="text-sm">{h.command}</code>
+                </pre>
+                <p className="mt-1 flex text-xs leading-5 text-gray-500">
+                  <span className="relative truncate ">{h.user}</span>
 
-                <span className="relative truncate ">{h.host}</span>
+                  <span>&nbsp;on&nbsp;</span>
 
-                <span>&nbsp;in&nbsp;</span>
+                  <span className="relative truncate ">{h.host}</span>
 
-                <span className="relative truncate ">{h.cwd}</span>
-              </p>
-            </div>
-          </div>
-          <div className="flex shrink-0 items-center gap-x-4">
-            <div className="hidden sm:flex sm:flex-col sm:items-end">
-              <p className="text-sm leading-6 text-gray-900">{h.exit}</p>
-              {h.duration ? (
-                <p className="mt-1 text-xs leading-5 text-gray-500">
-                  <time dateTime={h.duration}>
-                    {msToTime(h.duration / 1000000)}
-                  </time>
+                  <span>&nbsp;in&nbsp;</span>
+
+                  <span className="relative truncate ">{h.cwd}</span>
                 </p>
-              ) : (
-                <div className="mt-1 flex items-center gap-x-1.5">
-                  <div className="flex-none rounded-full bg-emerald-500/20 p-1">
-                    <div className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
-                  </div>
-                  <p className="text-xs leading-5 text-gray-500">Online</p>
-                </div>
-              )}
+              </div>
+            </div>
+            <div className="flex shrink-0 items-center gap-x-4">
+              <div className="hidden sm:flex sm:flex-col sm:items-end">
+                <p className="text-sm leading-6 text-gray-900">{h.exit}</p>
+                {h.duration ? (
+                  <p className="mt-1 text-xs leading-5 text-gray-500">
+                    <time dateTime={h.duration}>
+                      {msToTime(h.duration / 1000000)}
+                    </time>
+                  </p>
+                ) : (
+                  <div />
+                )}
+              </div>
+              <ChevronRightIcon
+                className="h-5 w-5 flex-none text-gray-400"
+                aria-hidden="true"
+              />
             </div>
-            <ChevronRightIcon
-              className="h-5 w-5 flex-none text-gray-400"
-              aria-hidden="true"
-            />
-          </div>
-        </li>
-      ))}
-    </ul>
+          </li>
+        );
+      })}
+    </div>
   );
 }
-- 
cgit v1.3.1

