forked from 77media/video-flow
138 lines
3.9 KiB
TypeScript
138 lines
3.9 KiB
TypeScript
import { Spin } from "antd";
|
||
import { ReactNode } from "react";
|
||
|
||
interface GlobalLoadProps {
|
||
/** 是否显示加载状态 */
|
||
show: boolean;
|
||
/** 子元素内容 */
|
||
children?: ReactNode;
|
||
/** 进度条数值 0-100,非必须 */
|
||
progress?: number;
|
||
/** 旋转圆环直径,非必须 */
|
||
spinnerDiameter?: number;
|
||
/** 进度条宽度,非必须 */
|
||
progressWidth?: number;
|
||
/** 是否显示旋转圆圈,默认显示 */
|
||
showSpinner?: boolean;
|
||
}
|
||
|
||
/**
|
||
* 全局加载组件,支持标签包裹形式渲染子元素
|
||
* @param props - 组件属性
|
||
* @returns 加载组件
|
||
*/
|
||
export default function GlobalLoad({
|
||
show,
|
||
children=<></>,
|
||
progress,
|
||
spinnerDiameter,
|
||
progressWidth,
|
||
showSpinner = true,
|
||
}: GlobalLoadProps) {
|
||
if (!show) {
|
||
return <>{children}</>;
|
||
}
|
||
|
||
// 自定义加载图标:组合两个组件
|
||
const customIndicator = (
|
||
<div
|
||
data-alt="custom-loading-indicator"
|
||
className="!w-max !h-max !flex flex-col items-center justify-center gap-4 -translate-x-1/2 -translate-y-1/2"
|
||
>
|
||
{showSpinner && <TailwindSpinner diameter={spinnerDiameter} />}
|
||
{Boolean(progress) && <TailwindLinearLoader progress={progress as number} width={progressWidth} />}
|
||
</div>
|
||
);
|
||
return (
|
||
<div data-alt="loading-container" className="relative">
|
||
<Spin spinning={true} tip="" indicator={customIndicator}>
|
||
{children}
|
||
</Spin>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Tailwind CSS loading spinner component for global loading overlay.
|
||
* @param diameter - Diameter of the spinner in pixels
|
||
* @returns {JSX.Element} - Spinner visual.
|
||
*/
|
||
export const TailwindSpinner = ({ diameter = 50 }: { diameter?: number }) => {
|
||
const radius = diameter / 2;
|
||
|
||
return (
|
||
<div data-alt="loading-spinner" className="relative" style={{ width: `${diameter}px`, height: `${diameter}px` }}>
|
||
{/* 主旋转圆环 */}
|
||
<div
|
||
className="w-full h-full animate-spin"
|
||
style={{
|
||
width: `${diameter}px`,
|
||
height: `${diameter}px`,
|
||
borderRadius: `${radius}px`,
|
||
backgroundImage:
|
||
"linear-gradient(rgb(186, 66, 255) 35%, rgb(0, 225, 255))",
|
||
filter: "blur(1px)",
|
||
boxShadow:
|
||
"0px -5px 20px 0px rgb(186, 66, 255), 0px 5px 20px 0px rgb(0, 225, 255)",
|
||
animation: "spinning82341 1.7s linear infinite",
|
||
}}
|
||
data-alt="spinner-main"
|
||
/>
|
||
{/* 背景模糊圆环 */}
|
||
<div
|
||
className="absolute inset-0"
|
||
style={{
|
||
width: `${diameter}px`,
|
||
height: `${diameter}px`,
|
||
borderRadius: `${radius}px`,
|
||
backgroundColor: "rgb(36, 36, 36)",
|
||
filter: "blur(10px)",
|
||
}}
|
||
data-alt="spinner-blur-bg"
|
||
/>
|
||
{/* 自定义动画关键帧 */}
|
||
<style>
|
||
{`
|
||
@keyframes spinning82341 {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
`}
|
||
</style>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
/**
|
||
* Tailwind CSS linear progress bar loader with animated light sweep.
|
||
* @param progress - Progress value from 0 to 100
|
||
* @param width - Width of the progress bar in pixels
|
||
* @returns {JSX.Element} - Linear loader visual.
|
||
*/
|
||
export const TailwindLinearLoader = ({
|
||
progress,
|
||
width = 160
|
||
}: {
|
||
progress: number;
|
||
width?: number;
|
||
}) => (
|
||
<div
|
||
data-alt="linear-loader-container"
|
||
className="relative h-[2px] bg-gray-200 dark:bg-gray-700 overflow-hidden rounded-full"
|
||
style={{ width: `${width}px` }}
|
||
>
|
||
{/* Animated light sweep - position controlled by progress */}
|
||
<div
|
||
data-alt="linear-loader-light"
|
||
className="absolute top-0 h-full w-[70px]"
|
||
style={{
|
||
background:
|
||
"linear-gradient(87deg, rgba(0, 0, 0, 0) 0%, rgb(0, 204, 255) 40%, rgb(0, 204, 255) 60%, rgba(0, 0, 0, 0) 100%)",
|
||
left: `${progress - 10}%`,
|
||
}}
|
||
/>
|
||
{/* Keyframes for light sweep - no longer needed since position is controlled by progress */}
|
||
</div>
|
||
);
|