Skip to content

Commit 99f603b

Browse files
bendichterclaude
andauthored
Add delete and reorder buttons to complex array items in metadata panel (#60)
Add move up, move down, and delete buttons to each item in complex array fields (contributors, about, ethics approvals, related resources, projects). Uses existing modifyMetadata operations (delete/insert) for reordering. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f541a1a commit 99f603b

1 file changed

Lines changed: 83 additions & 6 deletions

File tree

src/components/Metadata/EditableMetadataView.tsx

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import {
1414
} from '@mui/material';
1515
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
1616
import UndoIcon from '@mui/icons-material/Undo';
17+
import DeleteIcon from '@mui/icons-material/Delete';
18+
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
19+
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
1720
import TitleIcon from '@mui/icons-material/Title';
1821
import DescriptionIcon from '@mui/icons-material/Description';
1922
import LabelIcon from '@mui/icons-material/Label';
@@ -442,17 +445,23 @@ function ComplexArrayDisplay({
442445
items,
443446
fieldKey,
444447
changedPaths,
448+
onDelete,
449+
onMoveUp,
450+
onMoveDown,
445451
}: {
446452
items: unknown[];
447453
fieldKey: string;
448454
changedPaths: Set<string>;
455+
onDelete?: (index: number) => void;
456+
onMoveUp?: (index: number) => void;
457+
onMoveDown?: (index: number) => void;
449458
}) {
450459
const [expanded, setExpanded] = useState<number | false>(false);
451-
460+
452461
if (!items || items.length === 0) {
453462
return <Typography variant="body2" color="text.secondary">None</Typography>;
454463
}
455-
464+
456465
return (
457466
<Box>
458467
{items.map((item, idx) => {
@@ -461,19 +470,19 @@ function ComplexArrayDisplay({
461470
const isItemModified = Array.from(changedPaths).some(
462471
(p) => p === itemPath || p.startsWith(`${itemPath}.`) || p.startsWith(`${itemPath}[`)
463472
);
464-
473+
465474
const obj = item as Record<string, unknown>;
466475
const displayName = getObjectDisplayName(obj);
467476
const schemaKey = obj.schemaKey as string | undefined;
468-
477+
469478
// Get modified paths relative to this item
470479
const itemModifiedPaths = new Set<string>();
471480
changedPaths.forEach((p) => {
472481
if (p.startsWith(`${itemPath}.`)) {
473482
itemModifiedPaths.add(p.slice(itemPath.length + 1));
474483
}
475484
});
476-
485+
477486
return (
478487
<Accordion
479488
key={idx}
@@ -498,9 +507,54 @@ function ComplexArrayDisplay({
498507
{isItemModified && (
499508
<Chip label="modified" size="small" color="warning" sx={{ fontSize: '0.65rem', height: 18 }} />
500509
)}
501-
<Typography variant="body2" sx={{ width: '100%', wordBreak: 'break-word' }}>
510+
<Typography variant="body2" sx={{ flex: 1, wordBreak: 'break-word' }}>
502511
{displayName}
503512
</Typography>
513+
<Box
514+
sx={{ display: 'flex', ml: 'auto' }}
515+
onClick={(e) => e.stopPropagation()}
516+
>
517+
{onMoveUp && (
518+
<Tooltip title="Move up">
519+
<span>
520+
<IconButton
521+
size="small"
522+
onClick={() => onMoveUp(idx)}
523+
disabled={idx === 0}
524+
sx={{ p: 0.25 }}
525+
>
526+
<ArrowUpwardIcon sx={{ fontSize: 16 }} />
527+
</IconButton>
528+
</span>
529+
</Tooltip>
530+
)}
531+
{onMoveDown && (
532+
<Tooltip title="Move down">
533+
<span>
534+
<IconButton
535+
size="small"
536+
onClick={() => onMoveDown(idx)}
537+
disabled={idx === items.length - 1}
538+
sx={{ p: 0.25 }}
539+
>
540+
<ArrowDownwardIcon sx={{ fontSize: 16 }} />
541+
</IconButton>
542+
</span>
543+
</Tooltip>
544+
)}
545+
{onDelete && (
546+
<Tooltip title="Delete">
547+
<IconButton
548+
size="small"
549+
onClick={() => onDelete(idx)}
550+
color="error"
551+
sx={{ p: 0.25 }}
552+
>
553+
<DeleteIcon sx={{ fontSize: 16 }} />
554+
</IconButton>
555+
</Tooltip>
556+
)}
557+
</Box>
504558
</Box>
505559
</AccordionSummary>
506560
<AccordionDetails sx={{ pt: 0 }}>
@@ -636,13 +690,15 @@ function SectionDisplay({
636690
isFieldModified,
637691
revertField,
638692
onEditField,
693+
modifyMetadata,
639694
}: {
640695
section: SectionDef;
641696
modifiedMetadata: Record<string, unknown>;
642697
changedPaths: Set<string>;
643698
isFieldModified: (key: string) => boolean;
644699
revertField: (key: string) => void;
645700
onEditField: (key: string, value: unknown) => { success: boolean; error?: string };
701+
modifyMetadata: (operation: string, path: string, value?: unknown) => { success: boolean; error?: string };
646702
}) {
647703
const SectionIcon = section.icon;
648704

@@ -890,6 +946,26 @@ function SectionDisplay({
890946
items={value as unknown[]}
891947
fieldKey={field.key}
892948
changedPaths={changedPaths}
949+
onDelete={(idx) => {
950+
const result = modifyMetadata('delete', `${field.key}.${idx}`);
951+
if (!result.success) console.error('Delete failed:', result.error);
952+
}}
953+
onMoveUp={(idx) => {
954+
if (idx <= 0) return;
955+
const arr = [...(value as unknown[])];
956+
const [item] = arr.splice(idx, 1);
957+
arr.splice(idx - 1, 0, item);
958+
const result = modifyMetadata('set', field.key, arr);
959+
if (!result.success) console.error('Move up failed:', result.error);
960+
}}
961+
onMoveDown={(idx) => {
962+
const arr = [...(value as unknown[])];
963+
if (idx >= arr.length - 1) return;
964+
const [item] = arr.splice(idx, 1);
965+
arr.splice(idx + 1, 0, item);
966+
const result = modifyMetadata('set', field.key, arr);
967+
if (!result.success) console.error('Move down failed:', result.error);
968+
}}
893969
/>
894970
</Box>
895971
</Box>
@@ -1010,6 +1086,7 @@ export function EditableMetadataView() {
10101086
isFieldModified={isFieldModified}
10111087
revertField={revertField}
10121088
onEditField={handleEditField}
1089+
modifyMetadata={modifyMetadata}
10131090
/>
10141091
))}
10151092
</Box>

0 commit comments

Comments
 (0)