diff --git a/js/DOMService.js b/js/DOMService.js index 6180105..99fc1a6 100644 --- a/js/DOMService.js +++ b/js/DOMService.js @@ -224,11 +224,13 @@ export class DOMService { link.href = `?${doc.slug}`; link.textContent = doc.title || doc.path.split('/').pop().replace('.md', ''); link.dataset.path = doc.path; + link.dataset.slug = doc.slug; link.style.paddingLeft = `${(level * 0.6) + 0.8}rem`; link.setAttribute('role', 'treeitem'); link.onclick = (e) => { e.preventDefault(); + this.setLinkLoading(link); this.eventBus.emit('navigation:requested', { slug: doc.slug }); history.pushState(null, '', link.href); if (window.innerWidth <= 1000) { @@ -239,11 +241,30 @@ export class DOMService { container.appendChild(link); } + /** + * Sets a link to loading state. + * @param {HTMLAnchorElement} link - The link element. + */ + setLinkLoading(link) { + this.clearAllLoading(); + link.classList.add('loading'); + } + + /** + * Clears loading state from all links. + */ + clearAllLoading() { + this.elements.fileIndex.querySelectorAll('a.loading, .folder-link.loading').forEach(link => { + link.classList.remove('loading'); + }); + } + /** * Updates the active document highlight in the sidebar. * @param {string} path - The path of the active document. */ updateActiveDocument(path) { + this.clearAllLoading(); this.elements.fileIndex.querySelectorAll('a').forEach(link => { const isActive = link.dataset.path === path; link.classList.toggle('active', isActive); @@ -326,6 +347,8 @@ export class DOMService { folderHeader.addEventListener('click', (e) => { if (e.target.closest('.folder-link')) { e.preventDefault(); + const folderLink = e.target.closest('.folder-link'); + this.setFolderLinkLoading(folderLink); this.eventBus.emit('navigation:requested', { slug: doc.slug }); history.pushState(null, '', `?${doc.slug}`); if (window.innerWidth <= 1000) { @@ -356,6 +379,15 @@ export class DOMService { }); } + /** + * Sets a folder link to loading state. + * @param {HTMLAnchorElement} link - The folder link element. + */ + setFolderLinkLoading(link) { + this.clearAllLoading(); + link.classList.add('loading'); + } + /** * Scrolls smoothly to an element with header offset. * @param {HTMLElement} element - The element to scroll to. diff --git a/styles.css b/styles.css index 224269f..aa8dade 100644 --- a/styles.css +++ b/styles.css @@ -276,6 +276,38 @@ body { display: block; padding: 0.4rem 0.8rem; font-size: 0.875rem; + position: relative; + transition: background-color 0.2s ease; +} + +#file-index a.loading { + pointer-events: none; + color: var(--ifm-color-content-secondary); +} + +#file-index a.loading::after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + height: 2px; + background: var(--ifm-color-primary); + animation: loading-progress 1s ease-in-out infinite; +} + +@keyframes loading-progress { + 0% { + width: 0%; + left: 0; + } + 50% { + width: 70%; + left: 0; + } + 100% { + width: 0%; + left: 100%; + } } #file-index a:hover { @@ -334,6 +366,21 @@ body { right: 0; top: 50%; transform: translateY(-50%); + transition: opacity 0.2s ease; +} + +.folder-link.loading { + opacity: 1; + color: var(--ifm-color-primary); +} + +.folder-link.loading i { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } } .folder-header:hover .folder-link {