92 lines
2.4 KiB
TypeScript
92 lines
2.4 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState } from 'react';
|
||
import { loadComponent } from '@/lib/material-components';
|
||
|
||
const sizeMap = {
|
||
small: '24px',
|
||
medium: '48px',
|
||
large: '64px',
|
||
} as const;
|
||
|
||
interface LoadingSpinnerProps {
|
||
size?: 'small' | 'medium' | 'large';
|
||
inline?: boolean;
|
||
/** Занимает всё доступное пространство страницы - предотвращает layout shift */
|
||
fullPage?: boolean;
|
||
}
|
||
|
||
export function LoadingSpinner({
|
||
size = 'medium',
|
||
inline = false,
|
||
fullPage = false,
|
||
}: LoadingSpinnerProps) {
|
||
const [mounted, setMounted] = useState(false);
|
||
const [componentsLoaded, setComponentsLoaded] = useState(false);
|
||
|
||
useEffect(() => {
|
||
setMounted(true);
|
||
loadComponent('circular-progress').then(() => {
|
||
setComponentsLoaded(true);
|
||
});
|
||
}, []);
|
||
|
||
// Стили для fullPage режима - занимает всё пространство, предотвращает дёрганье
|
||
const fullPageStyle: React.CSSProperties = fullPage ? {
|
||
display: 'flex',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
width: '100%',
|
||
height: '100%',
|
||
minHeight: '100%',
|
||
flex: 1,
|
||
background: 'var(--md-sys-color-background)',
|
||
} : {};
|
||
|
||
if (!mounted || !componentsLoaded) {
|
||
if (inline) {
|
||
return (
|
||
<span
|
||
className="material-symbols-outlined"
|
||
style={{ fontSize: sizeMap[size], animation: 'lk-spin 1s linear infinite' }}
|
||
>
|
||
progress_activity
|
||
</span>
|
||
);
|
||
}
|
||
if (fullPage) {
|
||
return (
|
||
<div style={fullPageStyle}>
|
||
<span
|
||
className="material-symbols-outlined"
|
||
style={{ fontSize: sizeMap[size], animation: 'lk-spin 1s linear infinite', color: 'var(--md-sys-color-primary)' }}
|
||
>
|
||
progress_activity
|
||
</span>
|
||
</div>
|
||
);
|
||
}
|
||
return <div>Загрузка...</div>;
|
||
}
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
display: 'flex',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
...(fullPage ? fullPageStyle : {}),
|
||
...(inline ? { padding: 0, width: sizeMap[size], height: sizeMap[size] } : { padding: '20px' }),
|
||
}}
|
||
>
|
||
<md-circular-progress
|
||
indeterminate
|
||
style={{
|
||
width: sizeMap[size],
|
||
height: sizeMap[size],
|
||
}}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|