feat(MainModal): implement sidebar skeleton loader and enhance loading experience

This commit is contained in:
alexsparkes
2026-01-24 00:28:31 +00:00
parent 7b48b3a4d8
commit e43615bcdf
5 changed files with 107 additions and 5 deletions

View File

@@ -182,7 +182,7 @@ function MainModal({ modalClose, deepLinkData }) {
canGoForward={canGoForward}
/>
<div style={{ flex: 1, display: 'flex', overflow: 'hidden', minHeight: 0 }}>
<Suspense fallback={<ModalLoader />}>
<Suspense fallback={<ModalLoader currentTab={currentTab} />}>
<TabComponent
key={currentTab}
changeTab={handleChangeTab}

View File

@@ -1,9 +1,10 @@
import variables from 'config/variables';
import SidebarSkeleton from './SidebarSkeleton';
const ModalLoader = () => (
const ModalLoader = ({ currentTab }) => (
<div style={{ display: 'flex', width: '100%', minHeight: '100%' }}>
<div className="modalSidebar">
<span className="mainTitle">Mue</span>
<SidebarSkeleton currentTab={currentTab} />
</div>
<div className="modalTabContent">
<div className="emptyItems">

View File

@@ -0,0 +1,50 @@
import { TAB_TYPES } from '../constants/tabConfig';
// Tab-specific configurations with exact divider positions
const TAB_CONFIGS = {
[TAB_TYPES.SETTINGS]: {
itemCount: 16, // Excluding experimental
dividerPositions: [10, 12], // After Weather, Language
textWidths: [80, 100, 70, 90, 85, 75, 80, 95, 90, 75, 85, 90, 85, 80, 70, 95], // Fixed widths in pixels
},
[TAB_TYPES.DISCOVER]: {
itemCount: 5,
dividerPositions: [0], // After "All"
textWidths: [60, 95, 95, 110, 90], // Fixed widths
},
[TAB_TYPES.LIBRARY]: {
itemCount: 0, // Library doesn't show sidebar
dividerPositions: [],
textWidths: [],
},
};
const SidebarSkeleton = ({ currentTab = TAB_TYPES.SETTINGS }) => {
const config = TAB_CONFIGS[currentTab] || TAB_CONFIGS[TAB_TYPES.SETTINGS];
// Library tab doesn't show sidebar
if (config.itemCount === 0) {
return null;
}
return (
<div className="sidebarSkeleton">
{Array.from({ length: config.itemCount }).map((_, index) => {
const hasDivider = config.dividerPositions.includes(index);
const textWidth = config.textWidths[index] || 80;
return (
<div key={index}>
<div className="skeletonItem">
<div className="iconPlaceholder pulse" />
<div className="textPlaceholder pulse" style={{ width: `${textWidth}px` }} />
</div>
{hasDivider && <hr className="skeletonDivider" />}
</div>
);
})}
</div>
);
};
export default SidebarSkeleton;

View File

@@ -74,3 +74,54 @@
margin-left: 0 !important;
margin-right: 0 !important;
}
// Sidebar skeleton loader
.sidebarSkeleton {
padding: 0.5rem 0.2rem;
.skeletonItem {
display: flex;
align-items: center;
padding: 0.5rem;
margin: 0.2rem;
pointer-events: none;
.iconPlaceholder {
width: 17px;
height: 17px;
border-radius: 6px;
margin-left: 20px;
margin-right: 20px;
flex-shrink: 0;
@include themed {
background: t($modal-sidebarActive);
}
}
.textPlaceholder {
height: 18px;
border-radius: 6px;
@include themed {
background: t($modal-sidebarActive);
}
}
}
.skeletonDivider {
@include themed {
height: 1px;
background: linear-gradient(
90deg,
transparent 0%,
t($modal-sidebarActive) 20%,
t($modal-sidebarActive) 80%,
transparent 100%
);
margin: 0.5rem 1.75rem;
border: none;
opacity: 0.6;
}
}
}

View File

@@ -210,7 +210,7 @@ body {
width: 100%;
background: linear-gradient(-90deg, #efefef 0%, #ccc 50%, #efefef 100%);
background-size: 400% 400%;
animation: pulse 1.2s ease-in-out infinite;
animation: pulse 0.8s ease-in-out infinite;
@keyframes pulse {
0% {
@@ -230,7 +230,7 @@ body {
width: 100%;
background: linear-gradient(-90deg, #000 0%, rgb(83 83 83) 50%, #000 100%);
background-size: 400% 400%;
animation: pulse 1.2s ease-in-out infinite;
animation: pulse 0.8s ease-in-out infinite;
@keyframes pulse {
0% {