fix(web): keep older trajectory history loadable

This commit is contained in:
_Kerman
2026-08-13 14:04:06 +08:00
parent 2c456a00ea
commit 14cf3334be
12 files changed
+175 -25

No files matched your search

@@ -55,6 +55,40 @@
animation: history-loading-spin 700ms linear infinite;
}
.table tbody .historyLoadRow td {
height: 30px;
padding: 0;
border-bottom-color: var(--dsw-alias-border-l2);
}
.historyLoadButton {
display: flex;
width: 100%;
height: 29px;
align-items: center;
justify-content: center;
gap: 6px;
border: 0;
background: var(--dsw-alias-bg-layer-1);
color: var(--dsw-alias-label-secondary);
cursor: pointer;
font: var(--dsw-font-xxs-12);
}
.historyLoadButton:hover:not(:disabled) {
background: var(--dsw-alias-interactive-bg-hover);
color: var(--dsw-alias-label-primary);
}
.historyLoadButton:focus-visible {
outline: 2px solid var(--dsw-alias-state-business-primary);
outline-offset: -2px;
}
.historyLoadButton:disabled {
cursor: default;
}
.table:not([data-scroll-ready='true']) {
visibility: hidden;
}
@@ -137,7 +171,7 @@
border: 0;
}
.table tbody tr:not([data-collapsed-summary]):not([data-virtual-spacer]) {
.table tbody tr:not([data-collapsed-summary]):not([data-virtual-spacer]):not([data-history-load]) {
cursor: default;
outline: none;
transition:
@@ -149,7 +183,7 @@
opacity: 0.24;
}
.table tbody tr:not([data-collapsed-summary]):not([data-virtual-spacer]):not([data-selected='true']):hover {
.table tbody tr:not([data-collapsed-summary]):not([data-virtual-spacer]):not([data-history-load]):not([data-selected='true']):hover {
background: var(--dsw-alias-interactive-bg-hover);
}
@@ -364,6 +364,8 @@ export interface TrajectoryTableProps {
recordFocus?: { readonly index: number } | null
/** Whether the initial history tail is still loading. */
historyLoading?: boolean
/** Whether another surface is loading one older history page. */
olderHistoryLoading?: boolean
/** First loaded raw event, used to preserve scroll position after prepending a page. */
historyStartSeq?: number | undefined
/** Whether one older history page can be requested. */
@@ -1698,6 +1700,7 @@ export function TrajectoryTable({
recordSelection = null,
recordFocus = null,
historyLoading = false,
olderHistoryLoading = false,
historyStartSeq,
hasOlderRecords = false,
onLoadOlder,
@@ -2123,12 +2126,13 @@ export function TrajectoryTable({
virtualIndexByRecordId,
virtualizationEnabled,
])
const requestOlder = useCallback((pane: HTMLDivElement) => {
const requestOlder = useCallback((pane: HTMLDivElement, requireTop: boolean) => {
if (
!hasOlderRecords
|| onLoadOlder === undefined
|| loadingOlder.current
|| pane.scrollTop > OLDER_LOAD_THRESHOLD_PX
|| olderHistoryLoading
|| (requireTop && pane.scrollTop > OLDER_LOAD_THRESHOLD_PX)
) return
loadingOlder.current = true
setOlderLoading(true)
@@ -2143,7 +2147,7 @@ export function TrajectoryTable({
loadingOlder.current = false
setOlderLoading(false)
})
}, [hasOlderRecords, historyStartSeq, onLoadOlder])
}, [hasOlderRecords, historyStartSeq, olderHistoryLoading, onLoadOlder])
useLayoutEffect(() => {
const pane = tablePaneRef.current
if (pane === null) return
@@ -2176,10 +2180,9 @@ export function TrajectoryTable({
virtualizationEnabled,
])
const loadingLabel = olderLoading
? 'Loading earlier history…'
: 'Loading trajectory…'
const showLoading = historyLoading || olderLoading || !tableScrollReady
const olderBusy = olderHistoryLoading || olderLoading
const showInitialLoading = historyLoading || !tableScrollReady
const historyRowOffset = hasOlderRecords ? 1 : 0
return (
<div ref={rootRef} className={css.split} style={splitStyle}>
@@ -2192,30 +2195,55 @@ export function TrajectoryTable({
followsTableTail.current =
pane.scrollHeight - pane.clientHeight - pane.scrollTop
<= BOTTOM_FOLLOW_THRESHOLD_PX
requestOlder(pane)
requestOlder(pane, true)
}}
onClick={(event) => {
if (event.target === event.currentTarget) clearAllSelections()
}}
>
{showLoading && (
{showInitialLoading && (
<div className={css.historyLoading} role="status" aria-live="polite">
<span className={css.historyLoadingBar}>
<span className={css.historyLoadingSpinner} aria-hidden="true" />
{loadingLabel}
Loading trajectory
</span>
</div>
)}
<table
className={css.table}
data-scroll-ready={tableScrollReady || undefined}
aria-rowcount={records.length}
aria-rowcount={records.length + historyRowOffset}
>
<colgroup>
<col className={css.eventColumn} />
<col className={css.contentColumn} />
</colgroup>
<tbody>
{hasOlderRecords && (
<tr className={css.historyLoadRow} data-history-load="">
<td colSpan={2}>
<button
type="button"
className={css.historyLoadButton}
disabled={olderBusy || onLoadOlder === undefined}
onClick={() => {
const pane = tablePaneRef.current
if (pane !== null) requestOlder(pane, false)
}}
>
{olderBusy && (
<span className={css.historyLoadingSpinner} aria-hidden="true" />
)}
<span
role={olderBusy ? 'status' : undefined}
aria-live={olderBusy ? 'polite' : undefined}
>
{olderBusy ? 'Loading earlier history…' : 'Load earlier history'}
</span>
</button>
</td>
</tr>
)}
{virtualTop > 0 && (
<tr className={css.virtualSpacer} data-virtual-spacer="top" aria-hidden="true">
<td
@@ -2263,7 +2291,7 @@ export function TrajectoryTable({
return (
<tr
tabIndex={isRequestOnly ? -1 : 0}
aria-rowindex={position + 1}
aria-rowindex={position + 1 + historyRowOffset}
aria-label={isCollapsedSummary
? `Collapsed ${record.collapsedSummaryKind} summary, ${record.collapsedSummary}`
: isRequestOnly
@@ -141,8 +141,8 @@ export function TrajectoryView({
} | null>(null)
const inspection = useSession(snapshot =>
snapshot.views.get('trajectory') ?? EMPTY_TRAJECTORY_SNAPSHOT)
const historyLoading = useSession(snapshot =>
snapshot.openState === 'loading' || snapshot.loadingOlder)
const historyLoading = useSession(snapshot => snapshot.openState === 'loading')
const olderHistoryLoading = useSession(snapshot => snapshot.loadingOlder)
const hasOlderHistory = useSession(snapshot => snapshot.hasMore)
const nodes = inspection.eventNodes
const eventLocations = inspection.eventLocations
@@ -488,6 +488,7 @@ export function TrajectoryView({
recordSelection={timelineRecordSelection}
recordFocus={timelineRecordFocus}
historyLoading={historyLoading}
olderHistoryLoading={olderHistoryLoading}
historyStartSeq={historyBaseSeq}
hasOlderRecords={hasOlderHistory}
onLoadOlder={loadEarlierHistory}