fix(export): add top horizontal scrollbar for session table

This commit is contained in:
aits2026
2026-03-06 19:31:20 +08:00
parent ecd64f62bc
commit a8c05fd26c
2 changed files with 48 additions and 4 deletions

View File

@@ -1975,12 +1975,11 @@
} }
} }
.table-top-scrollbar,
.table-bottom-scrollbar { .table-bottom-scrollbar {
flex: 0 0 auto; flex: 0 0 auto;
height: 16px;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
border-top: 1px solid color-mix(in srgb, var(--border-color) 80%, transparent);
background: color-mix(in srgb, var(--bg-primary) 86%, var(--bg-secondary)); background: color-mix(in srgb, var(--bg-primary) 86%, var(--bg-secondary));
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: color-mix(in srgb, var(--text-tertiary) 70%, transparent) transparent; scrollbar-color: color-mix(in srgb, var(--text-tertiary) 70%, transparent) transparent;
@@ -1999,10 +1998,24 @@
} }
} }
.table-top-scrollbar {
height: 14px;
border-bottom: 1px solid color-mix(in srgb, var(--border-color) 72%, transparent);
}
.table-bottom-scrollbar-inner { .table-bottom-scrollbar-inner {
height: 1px; height: 1px;
} }
.table-bottom-scrollbar {
height: 16px;
border-top: 1px solid color-mix(in srgb, var(--border-color) 80%, transparent);
}
.table-top-scrollbar-inner {
height: 1px;
}
.selection-clear-btn { .selection-clear-btn {
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 8px; border-radius: 8px;

View File

@@ -1537,10 +1537,11 @@ function ExportPage() {
const contactsAvatarCacheRef = useRef<Record<string, configService.ContactsAvatarCacheEntry>>({}) const contactsAvatarCacheRef = useRef<Record<string, configService.ContactsAvatarCacheEntry>>({})
const contactsVirtuosoRef = useRef<VirtuosoHandle | null>(null) const contactsVirtuosoRef = useRef<VirtuosoHandle | null>(null)
const sessionTableSectionRef = useRef<HTMLDivElement | null>(null) const sessionTableSectionRef = useRef<HTMLDivElement | null>(null)
const contactsTopScrollbarRef = useRef<HTMLDivElement | null>(null)
const contactsHorizontalViewportRef = useRef<HTMLDivElement | null>(null) const contactsHorizontalViewportRef = useRef<HTMLDivElement | null>(null)
const contactsHorizontalContentRef = useRef<HTMLDivElement | null>(null) const contactsHorizontalContentRef = useRef<HTMLDivElement | null>(null)
const contactsBottomScrollbarRef = useRef<HTMLDivElement | null>(null) const contactsBottomScrollbarRef = useRef<HTMLDivElement | null>(null)
const contactsScrollSyncSourceRef = useRef<'viewport' | 'bottom' | null>(null) const contactsScrollSyncSourceRef = useRef<'viewport' | 'top' | 'bottom' | null>(null)
const sessionFormatDropdownRef = useRef<HTMLDivElement | null>(null) const sessionFormatDropdownRef = useRef<HTMLDivElement | null>(null)
const detailRequestSeqRef = useRef(0) const detailRequestSeqRef = useRef(0)
const sessionsRef = useRef<SessionRow[]>([]) const sessionsRef = useRef<SessionRow[]>([])
@@ -5665,13 +5666,18 @@ function ExportPage() {
row.mutualFriends.statusLabel.startsWith('加载中') row.mutualFriends.statusLabel.startsWith('加载中')
)) ))
), [sessionLoadDetailRows]) ), [sessionLoadDetailRows])
const syncContactsHorizontalScroll = useCallback((source: 'viewport' | 'bottom', scrollLeft: number) => { const syncContactsHorizontalScroll = useCallback((source: 'viewport' | 'top' | 'bottom', scrollLeft: number) => {
if (contactsScrollSyncSourceRef.current && contactsScrollSyncSourceRef.current !== source) return if (contactsScrollSyncSourceRef.current && contactsScrollSyncSourceRef.current !== source) return
contactsScrollSyncSourceRef.current = source contactsScrollSyncSourceRef.current = source
const topScrollbar = contactsTopScrollbarRef.current
const viewport = contactsHorizontalViewportRef.current const viewport = contactsHorizontalViewportRef.current
const bottomScrollbar = contactsBottomScrollbarRef.current const bottomScrollbar = contactsBottomScrollbarRef.current
if (source !== 'top' && topScrollbar && Math.abs(topScrollbar.scrollLeft - scrollLeft) > 1) {
topScrollbar.scrollLeft = scrollLeft
}
if (source !== 'viewport' && viewport && Math.abs(viewport.scrollLeft - scrollLeft) > 1) { if (source !== 'viewport' && viewport && Math.abs(viewport.scrollLeft - scrollLeft) > 1) {
viewport.scrollLeft = scrollLeft viewport.scrollLeft = scrollLeft
} }
@@ -5689,6 +5695,9 @@ function ExportPage() {
const handleContactsHorizontalViewportScroll = useCallback((event: UIEvent<HTMLDivElement>) => { const handleContactsHorizontalViewportScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
syncContactsHorizontalScroll('viewport', event.currentTarget.scrollLeft) syncContactsHorizontalScroll('viewport', event.currentTarget.scrollLeft)
}, [syncContactsHorizontalScroll]) }, [syncContactsHorizontalScroll])
const handleContactsTopScrollbarScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
syncContactsHorizontalScroll('top', event.currentTarget.scrollLeft)
}, [syncContactsHorizontalScroll])
const handleContactsBottomScrollbarScroll = useCallback((event: UIEvent<HTMLDivElement>) => { const handleContactsBottomScrollbarScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
syncContactsHorizontalScroll('bottom', event.currentTarget.scrollLeft) syncContactsHorizontalScroll('bottom', event.currentTarget.scrollLeft)
}, [syncContactsHorizontalScroll]) }, [syncContactsHorizontalScroll])
@@ -5714,6 +5723,17 @@ function ExportPage() {
viewport.scrollLeft = clampedScrollLeft viewport.scrollLeft = clampedScrollLeft
} }
const topScrollbar = contactsTopScrollbarRef.current
if (topScrollbar) {
const nextScrollLeft = Math.min(topScrollbar.scrollLeft, maxScrollLeft)
if (Math.abs(topScrollbar.scrollLeft - nextScrollLeft) > 1) {
topScrollbar.scrollLeft = nextScrollLeft
}
if (Math.abs(nextScrollLeft - clampedScrollLeft) > 1) {
topScrollbar.scrollLeft = clampedScrollLeft
}
}
const bottomScrollbar = contactsBottomScrollbarRef.current const bottomScrollbar = contactsBottomScrollbarRef.current
if (bottomScrollbar) { if (bottomScrollbar) {
const nextScrollLeft = Math.min(bottomScrollbar.scrollLeft, maxScrollLeft) const nextScrollLeft = Math.min(bottomScrollbar.scrollLeft, maxScrollLeft)
@@ -6317,6 +6337,17 @@ function ExportPage() {
</div> </div>
)} )}
{hasFilteredContacts && hasContactsHorizontalOverflow && (
<div
ref={contactsTopScrollbarRef}
className="table-top-scrollbar"
onScroll={handleContactsTopScrollbarScroll}
aria-label="会话列表顶部横向滚动条"
>
<div className="table-top-scrollbar-inner" style={contactsBottomScrollbarInnerStyle} />
</div>
)}
{hasFilteredContacts && ( {hasFilteredContacts && (
<div className="contacts-list-header"> <div className="contacts-list-header">
<span className="contacts-list-header-select"> <span className="contacts-list-header-select">