From 430793cd097de53794c0d545992cc046184be4dc Mon Sep 17 00:00:00 2001 From: Dylan De Faoite Date: Wed, 1 Apr 2026 00:09:20 +0100 Subject: [PATCH] feat(frontend): add "show more" functionality to corpus explorer --- frontend/src/components/CorpusExplorer.tsx | 225 ++++++++++++++------- 1 file changed, 151 insertions(+), 74 deletions(-) diff --git a/frontend/src/components/CorpusExplorer.tsx b/frontend/src/components/CorpusExplorer.tsx index e382b51..b80bda8 100644 --- a/frontend/src/components/CorpusExplorer.tsx +++ b/frontend/src/components/CorpusExplorer.tsx @@ -1,9 +1,13 @@ +import { useEffect, useMemo, useState } from "react"; import { Dialog, DialogPanel, DialogTitle } from "@headlessui/react"; import StatsStyling from "../styles/stats_styling"; import type { DatasetRecord } from "../utils/corpusExplorer"; const styles = StatsStyling; +const INITIAL_RECORD_COUNT = 60; +const RECORD_BATCH_SIZE = 60; +const EXCERPT_LENGTH = 320; const cleanText = (value: unknown) => { if (typeof value !== "string") { @@ -79,15 +83,6 @@ const getRecordTitle = (record: DatasetRecord) => { return content.length > 120 ? `${content.slice(0, 117)}...` : content; }; -const getRecordExcerpt = (record: DatasetRecord) => { - const content = cleanText(record.content); - if (!content) { - return "No content available."; - } - - return content.length > 320 ? `${content.slice(0, 317)}...` : content; -}; - const CorpusExplorer = ({ open, onClose, @@ -97,79 +92,161 @@ const CorpusExplorer = ({ loading, error, emptyMessage, -}: CorpusExplorerProps) => ( - -
+}: CorpusExplorerProps) => { + const [visibleCount, setVisibleCount] = useState(INITIAL_RECORD_COUNT); + const [expandedKeys, setExpandedKeys] = useState>({}); -
- -
-
- {title} -

- {description} {loading ? "Loading records..." : `${records.length.toLocaleString()} records.`} -

+ useEffect(() => { + if (open) { + setVisibleCount(INITIAL_RECORD_COUNT); + setExpandedKeys({}); + } + }, [open, title, records.length]); + + const visibleRecords = useMemo( + () => records.slice(0, visibleCount), + [records, visibleCount], + ); + + const hasMoreRecords = visibleCount < records.length; + + return ( + +
+ +
+ +
+
+ {title} +

+ {description} {loading ? "Loading records..." : `${records.length.toLocaleString()} records.`} +

+
+ +
- -
+ {error ?

{error}

: null} - {error ?

{error}

: null} + {!loading && !error && !records.length ? ( +

{emptyMessage}

+ ) : null} - {!loading && !error && !records.length ? ( -

{emptyMessage}

- ) : null} + {loading ?
Preparing corpus slice...
: null} - {loading ? ( -
Preparing corpus slice...
- ) : null} + {!loading && !error && records.length ? ( + <> +
+ {visibleRecords.map((record, index) => { + const recordKey = getRecordKey(record, index); + const titleText = getRecordTitle(record); + const content = cleanText(record.content); + const isExpanded = !!expandedKeys[recordKey]; + const canExpand = content.length > EXCERPT_LENGTH; + const excerpt = + canExpand && !isExpanded + ? `${content.slice(0, EXCERPT_LENGTH - 3)}...` + : content || "No content available."; - {!loading && !error && records.length ? ( -
- {records.map((record, index) => ( -
-
-
- {getRecordTitle(record) ? ( -
{getRecordTitle(record)}
- ) : null} -
- {displayText(record.author, "Unknown author")} • {displayText(record.source, "Unknown source")} • {displayText(record.type, "record")} • {formatRecordDate(record)} + return ( +
+
+
+ {titleText ?
{titleText}
: null} +
+ {displayText(record.author, "Unknown author")} • {displayText(record.source, "Unknown source")} • {displayText(record.type, "record")} • {formatRecordDate(record)} +
+
+
+ {cleanText(record.topic) ? `Topic: ${cleanText(record.topic)}` : ""} +
+
+ +
+ {excerpt} +
+ + {canExpand ? ( +
+ +
+ ) : null}
-
-
- {cleanText(record.topic) ? `Topic: ${cleanText(record.topic)}` : ""} -
-
- -
- {getRecordExcerpt(record)} -
+ ); + })}
- ))} -
- ) : null} - -
-
-); + + {hasMoreRecords ? ( +
+ +
+ ) : null} + + ) : null} + +
+
+ ); +}; export default CorpusExplorer;