add loading state functionality to ItemDetailsPanel and improve routing logic with caching

This commit is contained in:
Danilo Znamerovszkij
2025-10-14 18:43:23 +02:00
parent 5647690c84
commit 152d878363
4 changed files with 102 additions and 3 deletions

View File

@@ -12,7 +12,7 @@
border-left: 1px solid rgba(148, 163, 184, 0.2);
box-shadow: -10px 0 30px rgba(0, 0, 0, 0.3),
inset 0 0 50px rgba(59, 130, 246, 0.05);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 1000;
overflow-y: auto;
transform: translateX(0);
@@ -124,7 +124,7 @@
}
.item-content {
animation: slideInUp 0.5s ease-out;
animation: slideInUp 0.2s ease-out;
.item-header {
margin-bottom: 24px;
@@ -218,7 +218,7 @@
}
.theory-content {
animation: slideInUp 0.5s ease-out;
animation: slideInUp 0.2s ease-out;
.theory-header {
margin-bottom: 28px;
@@ -558,6 +558,40 @@
font-weight: 400;
}
}
.loading-content {
animation: slideInUp 0.5s ease-out;
text-align: center;
padding: 48px 24px;
.theory-breadcrumb {
color: #fcd771;
font-size: 14px;
margin-bottom: 24px;
font-weight: 500;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(148, 163, 184, 0.2);
border-top: 3px solid #fcd771;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 16px;
}
h3 {
color: #fcd771;
margin-bottom: 8px;
font-size: 18px;
}
p {
color: #94a3b8;
font-size: 14px;
}
}
}
}
}
@@ -595,6 +629,15 @@
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
// Responsive adjustments
@media (max-width: 1024px) {
.item-details-panel {

View File

@@ -649,6 +649,41 @@ export class ItemDetailsPanel {
}
}
public showLoading(category: string, theory: string) {
this.isVisible = true
const panel = this.container.querySelector('.item-details-panel')
panel?.classList.add('visible')
panel?.setAttribute('aria-hidden', 'false')
// Convert theory slug to display name
const theoryName = theory.replace(/-/g, ' ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
// Convert category slug to display name
const categoryName = category.charAt(0).toUpperCase() + category.slice(1)
// Update the title
const titleElement = this.container.querySelector('#item-title')
if (titleElement) {
titleElement.textContent = theoryName
}
// Update the content with loading message and breadcrumb
const infoElement = this.container.querySelector('#item-info')
if (infoElement) {
infoElement.innerHTML = `
<div class="loading-content">
<div class="theory-breadcrumb">${categoryName}${theoryName}</div>
<div class="loading-spinner"></div>
<h3>Loading theory data...</h3>
<p>Please wait while we fetch the detailed information.</p>
</div>
`
}
}
public hide() {
this.isVisible = false
const panel = this.container.querySelector('.item-details-panel')

View File

@@ -61,6 +61,11 @@ itemDetailsPanel.setCloseCallback(() => {
router.goHome()
})
// Set up loading callback to show loading state
router.setLoadingCallback((category, theory) => {
itemDetailsPanel.showLoading(category, theory)
})
// Set up routing callback to show theory data when URL changes
router.setTheoryChangeCallback((theory, error) => {
if (theory) {

View File

@@ -1,8 +1,14 @@
import type { TheoryData } from '../types/theory'
import { generateSlug } from './slugUtils'
// Simple cache for theory data
const theoryCache = new Map<string, TheoryData>()
async function loadTheoryByName(theoryName: string): Promise<TheoryData> {
// Check cache first
if (theoryCache.has(theoryName)) {
return theoryCache.get(theoryName)!
}
const fileName = `${theoryName}.json`
const filePath = `/data/${fileName}`
@@ -12,6 +18,8 @@ async function loadTheoryByName(theoryName: string): Promise<TheoryData> {
throw new Error(`Failed to load theory data: ${response.statusText}`)
}
const theoryData = await response.json() as TheoryData
// Cache the result
theoryCache.set(theoryName, theoryData)
return theoryData
} catch (error) {
throw new Error(`Failed to load theory data: ${error}`)
@@ -22,6 +30,7 @@ export class Router {
private static instance: Router
private currentTheory: TheoryData | null = null
private onTheoryChange: ((theory: TheoryData | null, error?: string) => void) | null = null
private onLoading: ((category: string, theory: string) => void) | null = null
private constructor() {
this.setupPopstateListener()
@@ -39,6 +48,10 @@ export class Router {
this.onTheoryChange = callback
}
public setLoadingCallback(callback: (category: string, theory: string) => void) {
this.onLoading = callback
}
private setupPopstateListener() {
window.addEventListener('popstate', () => {
this.parseCurrentURL()
@@ -53,6 +66,9 @@ export class Router {
const category = segments[0]
const theory = segments[1]
// Show loading state with category and theory info
this.onLoading?.(category, theory)
try {
const theoryData = await this.loadTheory(category, theory)
this.currentTheory = theoryData