feat: add collapsible sections to StepGenerate component with reusable CollapsibleSection

This commit is contained in:
Ender 2025-10-24 20:51:11 +02:00
parent 4afb21c38b
commit b3418e3c96
3 changed files with 65 additions and 21 deletions

View File

@ -0,0 +1,44 @@
import { Box, Collapse, IconButton, Stack, Typography, type SxProps, type Theme } from '@mui/material';
import ExpandMore from '@mui/icons-material/ExpandMore';
import { useState, type PropsWithChildren } from 'react';
export default function CollapsibleSection({ title, defaultCollapsed = true, right, sx, children }: PropsWithChildren<{
title: string;
defaultCollapsed?: boolean;
right?: React.ReactNode;
sx?: SxProps<Theme>;
}>) {
const [open, setOpen] = useState(!defaultCollapsed);
return (
<Box sx={{ border: '1px solid', borderColor: 'divider', borderRadius: 1, overflow: 'hidden', ...sx }}>
<Stack
direction="row"
alignItems="center"
justifyContent="space-between"
sx={{ px: 1.25, py: 0.75, cursor: 'pointer', bgcolor: 'background.paper' }}
onClick={() => setOpen(v => !v)}
>
<Stack direction="row" alignItems="center" spacing={1}>
<IconButton
size="small"
onClick={(e) => {
e.stopPropagation();
setOpen(v => !v);
}}
sx={{ transform: open ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 150ms ease' }}
>
<ExpandMore fontSize="small" />
</IconButton>
<Typography variant="subtitle2">{title}</Typography>
</Stack>
{right}
</Stack>
<Collapse in={open} timeout="auto" unmountOnExit={false}>
<Box sx={{ p: 1.25 }}>
{children}
</Box>
</Collapse>
</Box>
);
}

View File

@ -8,8 +8,6 @@ export default function SelectedImages({
onRemove: (key: string) => void;
}) {
return (
<Box>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Selected Images</Typography>
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', gap: 1 }}>
{imageKeys.map((k) => (
<Box key={k} sx={{ p: 1, border: '1px solid', borderColor: 'divider', borderRadius: 1, textAlign: 'center', bgcolor: '#fafafa' }}>
@ -21,6 +19,5 @@ export default function SelectedImages({
<Typography variant="body2" sx={{ color: 'text.secondary' }}>(No images selected)</Typography>
)}
</Box>
</Box>
);
}

View File

@ -1,6 +1,7 @@
import { Box, Stack, TextField, Typography } from '@mui/material';
import MediaLibrary from '../MediaLibrary';
import SelectedImages from './SelectedImages';
import CollapsibleSection from './CollapsibleSection';
import type { Clip } from './StepAssets';
export default function StepGenerate({
@ -24,8 +25,7 @@ export default function StepGenerate({
</Typography>
{/* Audio transcriptions in order */}
<Box>
<Typography variant="subtitle2" sx={{ mb: 1 }}>Audio Transcriptions</Typography>
<CollapsibleSection title="Audio Transcriptions">
<Stack spacing={1}>
{[...postClips]
.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
@ -39,17 +39,20 @@ export default function StepGenerate({
<Typography variant="body2" sx={{ color: 'text.secondary' }}>(No audio clips)</Typography>
)}
</Stack>
</Box>
</CollapsibleSection>
{/* Selected images */}
<CollapsibleSection title="Selected Images">
<SelectedImages imageKeys={genImageKeys} onRemove={onToggleGenImage} />
</CollapsibleSection>
{/* Media library */}
<CollapsibleSection title="Media Library">
<MediaLibrary selectionMode selectedKeys={genImageKeys} onToggleSelect={onToggleGenImage} />
</CollapsibleSection>
{/* Prompt */}
<Box>
<Typography variant="subtitle2" sx={{ mb: 1 }}>AI Prompt</Typography>
<CollapsibleSection title="AI Prompt">
<TextField
label="Instructions + context for AI generation"
value={promptText}
@ -58,7 +61,7 @@ export default function StepGenerate({
multiline
minRows={4}
/>
</Box>
</CollapsibleSection>
</Box>
);
}