trimmer jsx cleanup
This commit is contained in:
@ -164,7 +164,7 @@ app
|
|||||||
.whenReady()
|
.whenReady()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// if (app.isPackaged) {
|
// if (app.isPackaged) {
|
||||||
pythonManager.start();
|
// pythonManager.start();
|
||||||
// }
|
// }
|
||||||
createWindow();
|
createWindow();
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
verticalListSortingStrategy,
|
verticalListSortingStrategy,
|
||||||
} from '@dnd-kit/sortable';
|
} from '@dnd-kit/sortable';
|
||||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||||
import AudioTrimmer from './AudioTrimer';
|
import AudioTrimmer from './Trimmer/AudioTrimer';
|
||||||
import { ClipMetadata } from '../../redux/types';
|
import { ClipMetadata } from '../../redux/types';
|
||||||
import { useAppDispatch, useAppSelector } from '../hooks';
|
import { useAppDispatch, useAppSelector } from '../hooks';
|
||||||
import { apiFetch } from '../api';
|
import { apiFetch } from '../api';
|
||||||
|
|||||||
@ -5,27 +5,22 @@ import React, {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import Dialog from '@mui/material/Dialog';
|
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
|
||||||
import DialogActions from '@mui/material/DialogActions';
|
|
||||||
import Slider from '@mui/material/Slider';
|
import Slider from '@mui/material/Slider';
|
||||||
import ToggleButton from '@mui/material/ToggleButton';
|
import ToggleButton from '@mui/material/ToggleButton';
|
||||||
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
|
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
|
||||||
import { useWavesurfer } from '@wavesurfer/react';
|
import { useWavesurfer } from '@wavesurfer/react';
|
||||||
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js';
|
import RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.esm.js';
|
||||||
import ZoomPlugin from 'wavesurfer.js/dist/plugins/zoom.esm.js';
|
import ZoomPlugin from 'wavesurfer.js/dist/plugins/zoom.esm.js';
|
||||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
|
||||||
import PauseIcon from '@mui/icons-material/Pause';
|
|
||||||
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
|
|
||||||
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { ClipMetadata, PlaybackType } from '../../redux/types';
|
import { ClipMetadata, PlaybackType } from '../../../redux/types';
|
||||||
import { useAppSelector } from '../hooks';
|
import { useAppSelector } from '../../hooks';
|
||||||
import PlayStopIcon from './playStopIcon';
|
import PlayStopIcon from '../icons/playStopIcon';
|
||||||
import PlayOverlapIcon from './playOverlapIcon';
|
import PlayOverlapIcon from '../icons/playOverlapIcon';
|
||||||
|
import NameEditDialog from './dialogs/NameEditDialog';
|
||||||
|
import DeleteClipDialog from './dialogs/DeleteClipDialog';
|
||||||
|
import TitleBlock from './TitleBlock';
|
||||||
|
import ClipButtonRow from './ClipButtonRow';
|
||||||
|
|
||||||
export interface AudioTrimmerProps {
|
export interface AudioTrimmerProps {
|
||||||
metadata: ClipMetadata;
|
metadata: ClipMetadata;
|
||||||
@ -42,35 +37,23 @@ export default function AudioTrimmer({
|
|||||||
}: AudioTrimmerProps) {
|
}: AudioTrimmerProps) {
|
||||||
const { attributes, listeners, setNodeRef, transform, transition } =
|
const { attributes, listeners, setNodeRef, transform, transition } =
|
||||||
useSortable({ id: metadata.filename });
|
useSortable({ id: metadata.filename });
|
||||||
|
|
||||||
// Dialog state for editing name
|
|
||||||
const rootRef = useRef<HTMLDivElement | null>(null);
|
const rootRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
||||||
const [nameInput, setNameInput] = useState<string>(metadata.name);
|
|
||||||
const [volumeInput, setVolumeInput] = useState<number>(metadata.volume ?? 1);
|
const [volumeInput, setVolumeInput] = useState<number>(metadata.volume ?? 1);
|
||||||
const collectionNames = useAppSelector((state) =>
|
const collectionNames = useAppSelector((state) =>
|
||||||
state.collections.map((col) => col.name),
|
state.collections.map((col) => col.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const handleDialogSave = (newName: string) => {
|
||||||
setNameInput(metadata.name);
|
if (newName.trim() && newName !== metadata.name) {
|
||||||
}, [metadata.name]);
|
const updated = { ...metadata, name: newName.trim() };
|
||||||
|
|
||||||
const openEditDialog = () => setEditDialogOpen(true);
|
|
||||||
const closeEditDialog = () => setEditDialogOpen(false);
|
|
||||||
|
|
||||||
const handleDialogSave = () => {
|
|
||||||
if (nameInput.trim() && nameInput !== metadata.name) {
|
|
||||||
const updated = { ...metadata, name: nameInput.trim() };
|
|
||||||
if (onSave) onSave(updated);
|
if (onSave) onSave(updated);
|
||||||
}
|
}
|
||||||
closeEditDialog();
|
setEditDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [blobUrl, setBlobUrl] = useState<string | undefined>(undefined);
|
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
// const [clipStart, setClipStart] = useState<number | undefined>(undefined);
|
// const [clipStart, setClipStart] = useState<number | undefined>(undefined);
|
||||||
// const [clipEnd, setClipEnd] = useState<number | undefined>(undefined);
|
// const [clipEnd, setClipEnd] = useState<number | undefined>(undefined);
|
||||||
@ -85,16 +68,12 @@ export default function AudioTrimmer({
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const fileBaseName =
|
|
||||||
metadata.filename.split('\\').pop()?.split('/').pop() || 'Unknown';
|
|
||||||
|
|
||||||
const { wavesurfer, isReady, isPlaying } = useWavesurfer({
|
const { wavesurfer, isReady, isPlaying } = useWavesurfer({
|
||||||
container: containerRef,
|
container: containerRef,
|
||||||
height: 100,
|
height: 100,
|
||||||
waveColor: '#ccb1ff',
|
waveColor: '#ccb1ff',
|
||||||
progressColor: '#6e44ba',
|
progressColor: '#6e44ba',
|
||||||
hideScrollbar: true,
|
hideScrollbar: true,
|
||||||
url: blobUrl,
|
|
||||||
plugins,
|
plugins,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -276,6 +255,21 @@ export default function AudioTrimmer({
|
|||||||
}}
|
}}
|
||||||
className="shadow-[0_2px_8px_rgba(0,0,0,0.5)] m-2 rounded-lg bg-darkDrop"
|
className="shadow-[0_2px_8px_rgba(0,0,0,0.5)] m-2 rounded-lg bg-darkDrop"
|
||||||
>
|
>
|
||||||
|
<NameEditDialog
|
||||||
|
open={editDialogOpen}
|
||||||
|
onCancel={() => setEditDialogOpen(false)}
|
||||||
|
startValue={metadata.name}
|
||||||
|
onSave={handleDialogSave}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DeleteClipDialog
|
||||||
|
open={deleteDialogOpen}
|
||||||
|
onCancel={() => setDeleteDialogOpen(false)}
|
||||||
|
onDelete={() => {
|
||||||
|
setDeleteDialogOpen(false);
|
||||||
|
if (onDelete) onDelete(metadata);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...attributes}
|
{...attributes}
|
||||||
@ -295,137 +289,22 @@ export default function AudioTrimmer({
|
|||||||
{/* <div className="flex flex-col"> */}
|
{/* <div className="flex flex-col"> */}
|
||||||
<div className="ml-4 mr-2 p-2">
|
<div className="ml-4 mr-2 p-2">
|
||||||
<div className="grid justify-items-stretch grid-cols-2">
|
<div className="grid justify-items-stretch grid-cols-2">
|
||||||
<div className="mb-5px flex flex-col">
|
<TitleBlock
|
||||||
<span
|
name={metadata.name}
|
||||||
className="font-bold text-lg text-white mb-1 cursor-pointer"
|
filename={metadata.filename}
|
||||||
onClick={openEditDialog}
|
onNameClick={() => setEditDialogOpen(true)}
|
||||||
onKeyDown={(e) => {
|
/>
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
<ClipButtonRow
|
||||||
e.preventDefault();
|
isPlaying={isPlaying}
|
||||||
openEditDialog();
|
collectionNames={collectionNames}
|
||||||
|
onPlayPause={onPlayPause}
|
||||||
|
onMove={(collectionName) => {
|
||||||
|
if (onMove !== undefined) {
|
||||||
|
onMove(collectionName, metadata);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
title="Click to edit name"
|
onDelete={() => setDeleteDialogOpen(true)}
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
style={{ outline: 'none' }}
|
|
||||||
>
|
|
||||||
{metadata.name}
|
|
||||||
</span>
|
|
||||||
<span className="text-sm text-neutral-500">{fileBaseName}</span>
|
|
||||||
</div>
|
|
||||||
<Dialog
|
|
||||||
open={editDialogOpen}
|
|
||||||
onClose={closeEditDialog}
|
|
||||||
slotProps={{
|
|
||||||
paper: { sx: { backgroundColor: '#1a1a1a', color: 'white' } },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogTitle>Edit Clip Name</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<textarea
|
|
||||||
autoFocus
|
|
||||||
className="font-bold text-lg bg-transparent outline-none border-b border-plum focus:border-plumDark text-white mb-1 w-full text-center resize-y"
|
|
||||||
value={nameInput}
|
|
||||||
onChange={(e) => setNameInput(e.target.value)}
|
|
||||||
rows={3}
|
|
||||||
onFocus={(event) => event.target.select()}
|
|
||||||
aria-label="Edit clip name"
|
|
||||||
style={{ minHeight: '3em' }}
|
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={closeEditDialog}
|
|
||||||
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={handleDialogSave}
|
|
||||||
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={deleteDialogOpen}
|
|
||||||
onClose={() => setDeleteDialogOpen(false)}
|
|
||||||
slotProps={{
|
|
||||||
paper: { sx: { backgroundColor: '#1a1a1a', color: 'white' } },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DialogTitle>Confirm Delete</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
Are you sure you want to delete this clip?
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setDeleteDialogOpen(false)}
|
|
||||||
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
setDeleteDialogOpen(false);
|
|
||||||
if (onDelete) onDelete(metadataRef.current);
|
|
||||||
}}
|
|
||||||
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="bg-plum hover:bg-plumDark text-white font-bold h-11 w-11 rounded-md ml-1"
|
|
||||||
onClick={onPlayPause}
|
|
||||||
>
|
|
||||||
{isPlaying ? <PauseIcon /> : <PlayArrowIcon />}
|
|
||||||
</button>
|
|
||||||
<div className="relative inline-block">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="bg-plum hover:bg-plumDark text-white font-bold h-11 w-11 rounded-md ml-1"
|
|
||||||
onClick={() => setDropdownOpen((prev) => !prev)}
|
|
||||||
>
|
|
||||||
{dropdownOpen ? <ArrowDownwardIcon /> : <ArrowForwardIcon />}
|
|
||||||
</button>
|
|
||||||
{dropdownOpen && (
|
|
||||||
<div className="absolute z-10 mt-2 w-40 bg-midnight rounded-md shadow-lg">
|
|
||||||
{collectionNames.map((name) => (
|
|
||||||
<button
|
|
||||||
key={name}
|
|
||||||
type="button"
|
|
||||||
className="block w-full text-left px-4 py-2 text-white hover:bg-plumDark"
|
|
||||||
onClick={() => {
|
|
||||||
setDropdownOpen(false);
|
|
||||||
if (onMove) onMove(name, metadata);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="bg-plum hover:bg-plumDark text-white font-bold h-11 w-11 rounded-md ml-1"
|
|
||||||
onClick={() => setDeleteDialogOpen(true)}
|
|
||||||
>
|
|
||||||
<DeleteIcon />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="m-1 wavesurfer-scroll-container">
|
<div className="m-1 wavesurfer-scroll-container">
|
||||||
<div ref={containerRef} className="wavesurfer-inner" />
|
<div ref={containerRef} className="wavesurfer-inner" />
|
||||||
@ -450,24 +329,6 @@ export default function AudioTrimmer({
|
|||||||
color="secondary"
|
color="secondary"
|
||||||
className="p-0 m-0"
|
className="p-0 m-0"
|
||||||
/>
|
/>
|
||||||
{/* <input
|
|
||||||
type="range"
|
|
||||||
min={0}
|
|
||||||
max={1}
|
|
||||||
step={0.01}
|
|
||||||
value={volumeInput}
|
|
||||||
onChange={(e) => {
|
|
||||||
const newVolume = parseFloat(e.target.value);
|
|
||||||
setVolumeInput(newVolume);
|
|
||||||
}}
|
|
||||||
onDragEnd={(e) => {
|
|
||||||
console.log('Volume change:');
|
|
||||||
// const newVolume = parseFloat(e.target.value);
|
|
||||||
// if (onSave) onSave({ ...metadata, volume: newVolume });
|
|
||||||
}}
|
|
||||||
className="mx-2 w-full accent-plum"
|
|
||||||
aria-label="Volume slider"
|
|
||||||
/> */}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-1/5 flex justify-end text-sm text-neutral-500">
|
<div className="w-1/5 flex justify-end text-sm text-neutral-500">
|
||||||
<ToggleButtonGroup value={metadata.playbackType}>
|
<ToggleButtonGroup value={metadata.playbackType}>
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
|
import PauseIcon from '@mui/icons-material/Pause';
|
||||||
|
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
|
||||||
|
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
||||||
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export default function ClipButtonRow({
|
||||||
|
isPlaying,
|
||||||
|
collectionNames,
|
||||||
|
onPlayPause,
|
||||||
|
onMove,
|
||||||
|
onDelete,
|
||||||
|
}: {
|
||||||
|
isPlaying: boolean;
|
||||||
|
collectionNames: string[];
|
||||||
|
onPlayPause: () => void;
|
||||||
|
onMove?: (collectionName: string) => void;
|
||||||
|
onDelete?: () => void;
|
||||||
|
}) {
|
||||||
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-plum hover:bg-plumDark text-white font-bold h-11 w-11 rounded-md ml-1"
|
||||||
|
onClick={onPlayPause}
|
||||||
|
>
|
||||||
|
{isPlaying ? <PauseIcon /> : <PlayArrowIcon />}
|
||||||
|
</button>
|
||||||
|
<div className="relative inline-block">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-plum hover:bg-plumDark text-white font-bold h-11 w-11 rounded-md ml-1"
|
||||||
|
onClick={() => setDropdownOpen((prev) => !prev)}
|
||||||
|
>
|
||||||
|
{dropdownOpen ? <ArrowDownwardIcon /> : <ArrowForwardIcon />}
|
||||||
|
</button>
|
||||||
|
{dropdownOpen && (
|
||||||
|
<div className="absolute z-10 mt-2 w-40 bg-midnight rounded-md shadow-lg">
|
||||||
|
{collectionNames.map((name) => (
|
||||||
|
<button
|
||||||
|
key={name}
|
||||||
|
type="button"
|
||||||
|
className="block w-full text-left px-4 py-2 text-white hover:bg-plumDark"
|
||||||
|
onClick={() => {
|
||||||
|
setDropdownOpen(false);
|
||||||
|
if (onMove) onMove(name);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-plum hover:bg-plumDark text-white font-bold h-11 w-11 rounded-md ml-1"
|
||||||
|
onClick={() => onDelete && onDelete()}
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
27
electron-ui/src/renderer/components/Trimmer/TitleBlock.tsx
Normal file
27
electron-ui/src/renderer/components/Trimmer/TitleBlock.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
export default function TitleBlock({
|
||||||
|
name,
|
||||||
|
filename,
|
||||||
|
onNameClick,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
filename: string;
|
||||||
|
onNameClick: () => void;
|
||||||
|
}) {
|
||||||
|
const basename = filename.split('\\').pop()?.split('/').pop() || 'Unknown';
|
||||||
|
return (
|
||||||
|
<div className="mb-5px flex flex-col">
|
||||||
|
<span
|
||||||
|
className="font-bold text-lg text-white mb-1 cursor-pointer"
|
||||||
|
onClick={onNameClick}
|
||||||
|
onKeyDown={(e) => {}}
|
||||||
|
title="Click to edit name"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
style={{ outline: 'none' }}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-neutral-500">{basename}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
export default function DeleteClipDialog({
|
||||||
|
open,
|
||||||
|
onCancel,
|
||||||
|
onDelete,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
onDelete: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={onCancel}
|
||||||
|
slotProps={{
|
||||||
|
paper: { sx: { backgroundColor: '#1a1a1a', color: 'white' } },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle>Confirm Delete</DialogTitle>
|
||||||
|
<DialogContent>Are you sure you want to delete this clip?</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onCancel}
|
||||||
|
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onDelete}
|
||||||
|
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
export default function NameEditDialog({
|
||||||
|
open,
|
||||||
|
startValue,
|
||||||
|
onCancel,
|
||||||
|
onSave,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
startValue: string;
|
||||||
|
onCancel: () => void;
|
||||||
|
onSave: (newName: string) => void;
|
||||||
|
}) {
|
||||||
|
const [input, setInput] = useState(startValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setInput(startValue);
|
||||||
|
}
|
||||||
|
}, [open, startValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={onCancel}
|
||||||
|
slotProps={{
|
||||||
|
paper: { sx: { backgroundColor: '#1a1a1a', color: 'white' } },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle>Edit Clip Name</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<textarea
|
||||||
|
autoFocus
|
||||||
|
className="font-bold text-lg bg-transparent outline-none border-b border-plum focus:border-plumDark text-white mb-1 w-full text-center resize-y"
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => {
|
||||||
|
setInput(e.target.value);
|
||||||
|
}}
|
||||||
|
rows={3}
|
||||||
|
onFocus={(event) => event.target.select()}
|
||||||
|
aria-label="Edit clip name"
|
||||||
|
style={{ minHeight: '3em' }}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onCancel}
|
||||||
|
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onSave(input)}
|
||||||
|
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user