Fix sidebar scroll preservation per page with x-navigate directive

- Add x-navigate directive to all sidebar document links for Alpine navigation
- Store scroll position per page using URL path as key in sessionStorage
- Each page now maintains its own scroll position in the sidebar
- Save scroll position before navigation and restore after navigation
- Scroll position is preserved when clicking links in the sidebar
- Works correctly with Alpine navigate events triggered by x-navigate directive
This commit is contained in:
2025-12-04 02:11:06 +09:00
parent 00a5951654
commit ec7aaf44a9
3 changed files with 54 additions and 44 deletions

View File

@@ -259,64 +259,72 @@ class="fixed inset-y-0 left-0 top-16 w-64 bg-white border-r border-gray-200 over
<!-- Global Keyboard Shortcuts --> <!-- Global Keyboard Shortcuts -->
<script> <script>
// Sidebar scroll position management // Sidebar scroll position management per page
function getSidebarScrollKey() {
// Use current page URL to create a unique key for scroll position
return 'kb_sidebar_scroll_' + window.location.pathname;
}
function saveSidebarScroll() { function saveSidebarScroll() {
const sidebar = document.getElementById('kb-sidebar'); const sidebar = document.getElementById('kb-sidebar');
if (sidebar) { if (sidebar) {
const scrollPos = sidebar.scrollTop; const scrollPos = sidebar.scrollTop;
sessionStorage.setItem('kb_sidebar_scroll_pos', scrollPos); const key = getSidebarScrollKey();
console.log('Saved sidebar scroll:', scrollPos); sessionStorage.setItem(key, scrollPos);
console.log('Saved sidebar scroll for ' + window.location.pathname + ':', scrollPos);
} }
} }
function restoreSidebarScroll() { function restoreSidebarScroll() {
const sidebar = document.getElementById('kb-sidebar'); const sidebar = document.getElementById('kb-sidebar');
if (sidebar) { if (!sidebar) return;
// Use setTimeout to ensure DOM is fully ready
setTimeout(() => { // Use requestAnimationFrame to ensure DOM is fully rendered
const savedPos = sessionStorage.getItem('kb_sidebar_scroll_pos'); requestAnimationFrame(() => {
if (savedPos !== null) { const key = getSidebarScrollKey();
const pos = parseInt(savedPos, 10); const savedPos = sessionStorage.getItem(key);
sidebar.scrollTop = pos; console.log('Retrieved from sessionStorage for ' + window.location.pathname + ':', savedPos);
console.log('Restored sidebar scroll:', pos);
sessionStorage.removeItem('kb_sidebar_scroll_pos'); if (savedPos !== null && parseInt(savedPos, 10) > 0) {
} const pos = parseInt(savedPos, 10);
}, 100); sidebar.scrollTop = pos;
} console.log('Restored sidebar scroll for ' + window.location.pathname + ' to:', pos);
}
});
} }
// For Livewire navigation // Intercept sidebar link clicks
document.addEventListener('livewire:navigated', saveSidebarScroll);
document.addEventListener('livewire:navigating', () => {
// Clear on any Livewire navigation
saveSidebarScroll();
});
// For Alpine navigate events
document.addEventListener('alpine:navigating', saveSidebarScroll);
document.addEventListener('alpine:navigated', restoreSidebarScroll);
// On page load, restore scroll position
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', restoreSidebarScroll);
} else {
restoreSidebarScroll();
}
// Also restore on window load
window.addEventListener('load', restoreSidebarScroll);
// Intercept all link clicks in sidebar to save scroll position
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
// Check if click is within sidebar
const sidebar = document.getElementById('kb-sidebar'); const sidebar = document.getElementById('kb-sidebar');
const link = e.target.closest('a'); if (!sidebar) return;
if (sidebar && link && sidebar.contains(link)) { const link = e.target.closest('a');
if (sidebar.contains(link)) {
console.log('Sidebar link clicked, saving scroll');
saveSidebarScroll(); saveSidebarScroll();
} }
}, true);
// Save before Alpine navigation
document.addEventListener('alpine:navigating', saveSidebarScroll);
// Restore after Alpine navigation
document.addEventListener('alpine:navigated', () => {
console.log('Alpine navigated event fired, attempting restore');
restoreSidebarScroll();
}); });
// For page load/reload
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
console.log('DOMContentLoaded - calling restoreSidebarScroll');
restoreSidebarScroll();
});
} else {
console.log('Document already loaded - calling restoreSidebarScroll');
setTimeout(restoreSidebarScroll, 100);
}
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') { if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault(); e.preventDefault();

View File

@@ -18,6 +18,7 @@
@auth @auth
<div class="mt-6 pt-6 border-t border-gray-200"> <div class="mt-6 pt-6 border-t border-gray-200">
<a <a
x-navigate
href="{{ route('documents.create') }}" href="{{ route('documents.create') }}"
class="flex items-center justify-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700" class="flex items-center justify-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700"
> >

View File

@@ -8,6 +8,7 @@
$displayTitle = basename($file['document']->title); $displayTitle = basename($file['document']->title);
@endphp @endphp
<a <a
x-navigate
href="{{ route('documents.show', $file['document']) }}" href="{{ route('documents.show', $file['document']) }}"
class="flex items-center px-2 py-1.5 text-sm text-gray-700 rounded hover:bg-gray-100 group" class="flex items-center px-2 py-1.5 text-sm text-gray-700 rounded hover:bg-gray-100 group"
> >