Fix scrollbar disappearing on search and All Notes list
This commit is contained in:
1 parent
67468d9fac
commit
61705e3f0c
2 files changed
+37
-10
No files matched your search
+10
-6
@@ -9,20 +9,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom thin scrollbars */
|
/* Custom scrollbars across all modern browsers */
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;
|
||||||
|
}
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 7px;
|
||||||
height: 6px;
|
height: 7px;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: rgba(156, 163, 175, 0.45);
|
background: rgba(156, 163, 175, 0.5);
|
||||||
border-radius: 3px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(107, 114, 128, 0.7);
|
background: rgba(107, 114, 128, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TipTap / ProseMirror Evernote Editor Styles */
|
/* TipTap / ProseMirror Evernote Editor Styles */
|
||||||
|
|||||||
@@ -412,6 +412,8 @@ export default function EvercloneWorkspace() {
|
|||||||
async (preferSelectNoteId?: string) => {
|
async (preferSelectNoteId?: string) => {
|
||||||
const currentReqId = ++listRequestIdRef.current;
|
const currentReqId = ++listRequestIdRef.current;
|
||||||
setLoadingList(true);
|
setLoadingList(true);
|
||||||
|
setLoadingMore(false);
|
||||||
|
loadingMoreRef.current = false;
|
||||||
try {
|
try {
|
||||||
const sp = buildNoteQueryParams(0, NOTES_PAGE_SIZE);
|
const sp = buildNoteQueryParams(0, NOTES_PAGE_SIZE);
|
||||||
const res = await fetch(`/api/notes?${sp.toString()}`);
|
const res = await fetch(`/api/notes?${sp.toString()}`);
|
||||||
@@ -423,6 +425,11 @@ export default function EvercloneWorkspace() {
|
|||||||
setTotalNotes(data.total || 0);
|
setTotalNotes(data.total || 0);
|
||||||
setHasMoreNotes(loadedNotes.length < (data.total || 0));
|
setHasMoreNotes(loadedNotes.length < (data.total || 0));
|
||||||
|
|
||||||
|
// Always reset list scroll position to 0 when fresh results are loaded
|
||||||
|
if (listParentRef.current) {
|
||||||
|
listParentRef.current.scrollTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (preferSelectNoteId) {
|
if (preferSelectNoteId) {
|
||||||
setSelectedNoteId(preferSelectNoteId);
|
setSelectedNoteId(preferSelectNoteId);
|
||||||
} else if (loadedNotes.length > 0) {
|
} else if (loadedNotes.length > 0) {
|
||||||
@@ -476,10 +483,8 @@ export default function EvercloneWorkspace() {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loadingMoreRef.current = false;
|
loadingMoreRef.current = false;
|
||||||
if (currentReqId === listRequestIdRef.current) {
|
|
||||||
setLoadingMore(false);
|
setLoadingMore(false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, [hasMoreNotes, loadingList, buildNoteQueryParams, notes.length]);
|
}, [hasMoreNotes, loadingList, buildNoteQueryParams, notes.length]);
|
||||||
|
|
||||||
// Safety filter: In Trash view, only trashed notes appear. In all other views, trashed notes never appear.
|
// Safety filter: In Trash view, only trashed notes appear. In all other views, trashed notes never appear.
|
||||||
@@ -1445,14 +1450,32 @@ export default function EvercloneWorkspace() {
|
|||||||
if (listParentRef.current) {
|
if (listParentRef.current) {
|
||||||
listParentRef.current.scrollTop = 0;
|
listParentRef.current.scrollTop = 0;
|
||||||
}
|
}
|
||||||
|
rowVirtualizer.scrollToOffset(0);
|
||||||
rowVirtualizer.measure();
|
rowVirtualizer.measure();
|
||||||
}
|
}
|
||||||
}, [listMode, rowVirtualizer]);
|
}, [listMode, rowVirtualizer]);
|
||||||
|
|
||||||
|
// Whenever view, search query, notebook/tag, or active filters change:
|
||||||
|
// Reset list scroll position to 0 and remeasure virtualizer so the scrollbar and rows never desync
|
||||||
|
const prevQueryKeyRef = useRef('');
|
||||||
|
const currentQueryKey = `${debouncedSearch}|${activeView}|${selectedNotebookId}|${selectedStackId}|${selectedTagId}|${filterNotebookId}|${filterTagIds.join(',')}|${filterUpdatedDays}|${filterHasImages}|${filterHasAttachments}|${filterHasTodo}|${filterHasWebClip}|${filterOnlyPinned}|${filterUntagged}|${sortBy}|${sortDir}`;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (prevQueryKeyRef.current !== currentQueryKey) {
|
||||||
|
prevQueryKeyRef.current = currentQueryKey;
|
||||||
|
if (listParentRef.current) {
|
||||||
|
listParentRef.current.scrollTop = 0;
|
||||||
|
}
|
||||||
|
rowVirtualizer.scrollToOffset(0);
|
||||||
|
rowVirtualizer.measure();
|
||||||
|
}
|
||||||
|
}, [currentQueryKey, rowVirtualizer]);
|
||||||
|
|
||||||
// User-scroll driven infinite pagination: only triggers when user deliberately scrolls near bottom
|
// User-scroll driven infinite pagination: only triggers when user deliberately scrolls near bottom
|
||||||
const handleListScroll = useCallback(
|
const handleListScroll = useCallback(
|
||||||
(e: React.UIEvent<HTMLDivElement>) => {
|
(e: React.UIEvent<HTMLDivElement>) => {
|
||||||
const el = e.currentTarget;
|
const el = e.currentTarget;
|
||||||
|
if (el.scrollHeight <= el.clientHeight) return;
|
||||||
const distanceToBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
const distanceToBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
||||||
if (distanceToBottom < 300) {
|
if (distanceToBottom < 300) {
|
||||||
if (!loadingMoreRef.current && hasMoreNotes && !loadingList) {
|
if (!loadingMoreRef.current && hasMoreNotes && !loadingList) {
|
||||||
@@ -2259,7 +2282,7 @@ export default function EvercloneWorkspace() {
|
|||||||
listMode === 'cards'
|
listMode === 'cards'
|
||||||
? 'md:w-96 lg:w-[460px] xl:w-[500px]'
|
? 'md:w-96 lg:w-[460px] xl:w-[500px]'
|
||||||
: 'md:w-80 lg:w-96'
|
: 'md:w-80 lg:w-96'
|
||||||
} border-r border-gray-200 bg-gray-50/50 flex-col shrink-0 transition-all duration-150 relative`}
|
} h-full min-h-0 overflow-hidden border-r border-gray-200 bg-gray-50/50 flex-col shrink-0 transition-all duration-150 relative`}
|
||||||
>
|
>
|
||||||
{/* List Header */}
|
{/* List Header */}
|
||||||
<div className="px-3 sm:px-4 py-2.5 border-b border-gray-200 bg-white space-y-2">
|
<div className="px-3 sm:px-4 py-2.5 border-b border-gray-200 bg-white space-y-2">
|
||||||
@@ -2796,7 +2819,7 @@ export default function EvercloneWorkspace() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Note List Body: Card Grid vs Virtualized Snippet / Compact List */}
|
{/* Note List Body: Card Grid vs Virtualized Snippet / Compact List */}
|
||||||
<div ref={listParentRef} onScroll={handleListScroll} className="flex-1 overflow-y-scroll">
|
<div ref={listParentRef} onScroll={handleListScroll} className="flex-1 min-h-0 overflow-y-scroll">
|
||||||
{loadingList && displayedNotes.length === 0 ? (
|
{loadingList && displayedNotes.length === 0 ? (
|
||||||
<div className="p-8 text-center text-xs text-gray-400">Loading notes…</div>
|
<div className="p-8 text-center text-xs text-gray-400">Loading notes…</div>
|
||||||
) : displayedNotes.length === 0 ? (
|
) : displayedNotes.length === 0 ? (
|
||||||
|
|||||||
Reference in new issue
Block a user