mirror of
https://github.com/mue/mue.git
synced 2026-06-08 14:10:42 +02:00
feat(MainModal): implement sidebar skeleton loader and enhance loading experience
This commit is contained in:
@@ -182,7 +182,7 @@ function MainModal({ modalClose, deepLinkData }) {
|
|||||||
canGoForward={canGoForward}
|
canGoForward={canGoForward}
|
||||||
/>
|
/>
|
||||||
<div style={{ flex: 1, display: 'flex', overflow: 'hidden', minHeight: 0 }}>
|
<div style={{ flex: 1, display: 'flex', overflow: 'hidden', minHeight: 0 }}>
|
||||||
<Suspense fallback={<ModalLoader />}>
|
<Suspense fallback={<ModalLoader currentTab={currentTab} />}>
|
||||||
<TabComponent
|
<TabComponent
|
||||||
key={currentTab}
|
key={currentTab}
|
||||||
changeTab={handleChangeTab}
|
changeTab={handleChangeTab}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import variables from 'config/variables';
|
import variables from 'config/variables';
|
||||||
|
import SidebarSkeleton from './SidebarSkeleton';
|
||||||
|
|
||||||
const ModalLoader = () => (
|
const ModalLoader = ({ currentTab }) => (
|
||||||
<div style={{ display: 'flex', width: '100%', minHeight: '100%' }}>
|
<div style={{ display: 'flex', width: '100%', minHeight: '100%' }}>
|
||||||
<div className="modalSidebar">
|
<div className="modalSidebar">
|
||||||
<span className="mainTitle">Mue</span>
|
<SidebarSkeleton currentTab={currentTab} />
|
||||||
</div>
|
</div>
|
||||||
<div className="modalTabContent">
|
<div className="modalTabContent">
|
||||||
<div className="emptyItems">
|
<div className="emptyItems">
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -74,3 +74,54 @@
|
|||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
margin-right: 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background: linear-gradient(-90deg, #efefef 0%, #ccc 50%, #efefef 100%);
|
background: linear-gradient(-90deg, #efefef 0%, #ccc 50%, #efefef 100%);
|
||||||
background-size: 400% 400%;
|
background-size: 400% 400%;
|
||||||
animation: pulse 1.2s ease-in-out infinite;
|
animation: pulse 0.8s ease-in-out infinite;
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
@@ -230,7 +230,7 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background: linear-gradient(-90deg, #000 0%, rgb(83 83 83) 50%, #000 100%);
|
background: linear-gradient(-90deg, #000 0%, rgb(83 83 83) 50%, #000 100%);
|
||||||
background-size: 400% 400%;
|
background-size: 400% 400%;
|
||||||
animation: pulse 1.2s ease-in-out infinite;
|
animation: pulse 0.8s ease-in-out infinite;
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
|
|||||||
Reference in New Issue
Block a user