mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-03-09 21:50:36 +00:00
111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
import { AnimatePresence, motion } from 'framer-motion';
|
|
import React, { useState } from 'react';
|
|
import type { ProgressAnnotation } from '~/types/context';
|
|
import { classNames } from '~/utils/classNames';
|
|
import { cubicEasingFn } from '~/utils/easings';
|
|
|
|
export default function ProgressCompilation({ data }: { data?: ProgressAnnotation[] }) {
|
|
const [progressList, setProgressList] = React.useState<ProgressAnnotation[]>([]);
|
|
const [expanded, setExpanded] = useState(false);
|
|
React.useEffect(() => {
|
|
if (!data || data.length == 0) {
|
|
setProgressList([]);
|
|
return;
|
|
}
|
|
|
|
const progressMap = new Map<string, ProgressAnnotation>();
|
|
data.forEach((x) => {
|
|
const existingProgress = progressMap.get(x.label);
|
|
|
|
if (existingProgress && existingProgress.status === 'complete') {
|
|
return;
|
|
}
|
|
|
|
progressMap.set(x.label, x);
|
|
});
|
|
|
|
const newData = Array.from(progressMap.values());
|
|
newData.sort((a, b) => a.order - b.order);
|
|
setProgressList(newData);
|
|
}, [data]);
|
|
|
|
if (progressList.length === 0) {
|
|
return <></>;
|
|
}
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
<div
|
|
className={classNames(
|
|
'bg-bolt-elements-background-depth-2',
|
|
'border border-bolt-elements-borderColor',
|
|
'shadow-lg rounded-lg relative w-full max-w-chat mx-auto z-prompt',
|
|
'p-1',
|
|
)}
|
|
>
|
|
<div
|
|
className={classNames(
|
|
'bg-bolt-elements-item-backgroundAccent',
|
|
'p-1 rounded-lg text-bolt-elements-item-contentAccent',
|
|
'flex ',
|
|
)}
|
|
>
|
|
<div className="flex-1">
|
|
<AnimatePresence>
|
|
{expanded ? (
|
|
<motion.div
|
|
className="actions"
|
|
initial={{ height: 0 }}
|
|
animate={{ height: 'auto' }}
|
|
exit={{ height: '0px' }}
|
|
transition={{ duration: 0.15 }}
|
|
>
|
|
{progressList.map((x, i) => {
|
|
return <ProgressItem key={i} progress={x} />;
|
|
})}
|
|
</motion.div>
|
|
) : (
|
|
<ProgressItem progress={progressList.slice(-1)[0]} />
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
<motion.button
|
|
initial={{ width: 0 }}
|
|
animate={{ width: 'auto' }}
|
|
exit={{ width: 0 }}
|
|
transition={{ duration: 0.15, ease: cubicEasingFn }}
|
|
className=" p-1 rounded-lg bg-bolt-elements-item-backgroundAccent hover:bg-bolt-elements-artifacts-backgroundHover"
|
|
onClick={() => setExpanded((v) => !v)}
|
|
>
|
|
<div className={expanded ? 'i-ph:caret-up-bold' : 'i-ph:caret-down-bold'}></div>
|
|
</motion.button>
|
|
</div>
|
|
</div>
|
|
</AnimatePresence>
|
|
);
|
|
}
|
|
|
|
const ProgressItem = ({ progress }: { progress: ProgressAnnotation }) => {
|
|
return (
|
|
<motion.div
|
|
className={classNames('flex text-sm gap-3')}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.15 }}
|
|
>
|
|
<div className="flex items-center gap-1.5 ">
|
|
<div>
|
|
{progress.status === 'in-progress' ? (
|
|
<div className="i-svg-spinners:90-ring-with-bg"></div>
|
|
) : progress.status === 'complete' ? (
|
|
<div className="i-ph:check"></div>
|
|
) : null}
|
|
</div>
|
|
{/* {x.label} */}
|
|
</div>
|
|
{progress.message}
|
|
</motion.div>
|
|
);
|
|
};
|