Animated run layout
This commit is contained in:
@@ -12,9 +12,14 @@ type TreeNode = {
|
|||||||
interface FileTreeProps {
|
interface FileTreeProps {
|
||||||
tree: TreeNode;
|
tree: TreeNode;
|
||||||
level?: number; // for indentation
|
level?: number; // for indentation
|
||||||
|
delay?: number; // stagger delay in ms
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FileTree: React.FC<FileTreeProps> = ({ tree, level = 0 }) => {
|
export const FileTree: React.FC<FileTreeProps> = ({
|
||||||
|
tree,
|
||||||
|
level = 0,
|
||||||
|
delay = 0,
|
||||||
|
}) => {
|
||||||
const [expanded, setExpanded] = useState(
|
const [expanded, setExpanded] = useState(
|
||||||
tree.type !== "directory" || level === 0 ? true : false,
|
tree.type !== "directory" || level === 0 ? true : false,
|
||||||
);
|
);
|
||||||
@@ -32,11 +37,9 @@ export const FileTree: React.FC<FileTreeProps> = ({ tree, level = 0 }) => {
|
|||||||
onClick={toggleExpand}
|
onClick={toggleExpand}
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: level * 20,
|
paddingLeft: level * 20,
|
||||||
display: "flex",
|
animationDelay: `${delay}ms`,
|
||||||
alignItems: "center",
|
|
||||||
cursor: tree.type === "directory" ? "pointer" : "default",
|
|
||||||
userSelect: "none",
|
|
||||||
}}
|
}}
|
||||||
|
className="flex items-center cursor-pointer select-none animate-fade-in opacity-0"
|
||||||
>
|
>
|
||||||
{tree.type === "directory" ? (
|
{tree.type === "directory" ? (
|
||||||
expanded ? (
|
expanded ? (
|
||||||
@@ -52,8 +55,14 @@ export const FileTree: React.FC<FileTreeProps> = ({ tree, level = 0 }) => {
|
|||||||
|
|
||||||
{/* Children */}
|
{/* Children */}
|
||||||
{expanded &&
|
{expanded &&
|
||||||
tree.children?.map((child) => (
|
tree.children?.map((child, index) => (
|
||||||
<FileTree key={child.uri} tree={child} level={level + 1} />
|
<FileTree
|
||||||
|
key={child.uri}
|
||||||
|
tree={child}
|
||||||
|
level={level + 1}
|
||||||
|
// stagger each child by 100ms
|
||||||
|
delay={delay + (index + 1) * 100}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ export default function RunChat({
|
|||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
<div className="flex-1 overflow-y-auto space-y-2">
|
<div className="flex-1 overflow-y-auto space-y-2">
|
||||||
{chatMessages.map((msg, i) => (
|
{chatMessages.map((msg, i) => (
|
||||||
<div key={i} className="rounded-lg bg-secondary/50 p-2 text-sm">
|
<div
|
||||||
|
key={i}
|
||||||
|
className="rounded-lg bg-secondary/50 p-2 text-sm opacity-0 animate-fade-in"
|
||||||
|
style={{ animationDelay: `${i * 100}ms` }} // stagger by 100ms
|
||||||
|
>
|
||||||
<span className="font-semibold text-foreground">{msg.user}</span>:{" "}
|
<span className="font-semibold text-foreground">{msg.user}</span>:{" "}
|
||||||
<span className="text-muted-foreground">{msg.message}</span>
|
<span className="text-muted-foreground">{msg.message}</span>
|
||||||
<span className="ml-2 text-xs text-muted-foreground">
|
<span className="ml-2 text-xs text-muted-foreground">
|
||||||
|
|||||||
@@ -28,12 +28,18 @@ export default function Run() {
|
|||||||
<ProfileCard user={{ username: "selimaj", rank: 1 }} />
|
<ProfileCard user={{ username: "selimaj", rank: 1 }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 animate-fade-in opacity-0 duration-1000">
|
<div className="flex-1 animate-fade-in opacity-0 duration-1000 stagger-2">
|
||||||
{runInfo && <RunTimer runInfo={runInfo} />}
|
{runInfo && <RunTimer runInfo={runInfo} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<ProblemCard name="Idk" difficulty="hard" tags={[]} attempts={0} />
|
<ProblemCard
|
||||||
|
name="Idk"
|
||||||
|
difficulty="hard"
|
||||||
|
tags={[]}
|
||||||
|
attempts={0}
|
||||||
|
index={4}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user