diff --git a/src/components/main-rows.tsx b/src/components/main-rows.tsx
new file mode 100644
index 0000000..4debf58
--- /dev/null
+++ b/src/components/main-rows.tsx
@@ -0,0 +1,201 @@
+import React, { useCallback } from 'react';
+import clsx from 'clsx';
+
+import { EncodingName } from '../utils';
+
+import { formatTimeFromFrames, Track, Group } from 'netmd-js';
+
+import { makeStyles } from '@material-ui/core/styles';
+import TableCell from '@material-ui/core/TableCell';
+import TableRow from '@material-ui/core/TableRow';
+import * as BadgeImpl from '@material-ui/core/Badge/Badge';
+
+import DragIndicator from '@material-ui/icons/DragIndicator';
+import PlayArrowIcon from '@material-ui/icons/PlayArrow';
+import IconButton from '@material-ui/core/IconButton';
+import FolderIcon from '@material-ui/icons/Folder';
+import DeleteIcon from '@material-ui/icons/Delete';
+
+import { DraggableProvided } from 'react-beautiful-dnd';
+
+const useStyles = makeStyles((theme: any) => ({
+ currentTrackRow: {
+ color: theme.palette.primary.main,
+ '& > td': {
+ color: 'inherit',
+ },
+ },
+ inGroupTrackRow: {
+ '& > $indexCell': {
+ transform: `translateX(${theme.spacing(3)}px)`,
+ },
+ '& > $titleCell': {
+ transform: `translateX(${theme.spacing(3)}px)`,
+ },
+ },
+ playButtonInTrackList: {
+ display: 'none',
+ },
+ trackRow: {
+ '&:hover': {
+ '& $playButtonInTrackList': {
+ display: 'inline-flex',
+ },
+ '& $trackIndex': {
+ display: 'none',
+ },
+ },
+ },
+ controlButtonInTrackCommon: {
+ width: theme.spacing(2),
+ height: theme.spacing(2),
+ verticalAlign: 'middle',
+ marginLeft: theme.spacing(-0.5),
+ },
+ formatBadge: {
+ ...(BadgeImpl as any).styles(theme).badge,
+ ...(BadgeImpl as any).styles(theme).colorPrimary,
+ position: 'static',
+ display: 'inline-flex',
+ border: `2px solid ${theme.palette.background.paper}`,
+ padding: '0 4px',
+ verticalAlign: 'middle',
+ width: theme.spacing(4.5),
+ marginRight: theme.spacing(0.5),
+ },
+ durationCell: {
+ whiteSpace: 'nowrap',
+ },
+ durationCellTime: {
+ verticalAlign: 'middle',
+ },
+ titleCell: {
+ overflow: 'hidden',
+ maxWidth: '40ch',
+ textOverflow: 'ellipsis',
+ // whiteSpace: 'nowrap',
+ },
+ deleteGroupButton: {
+ display: 'none',
+ },
+ indexCell: {
+ whiteSpace: 'nowrap',
+ paddingRight: 0,
+ width: theme.spacing(4),
+ },
+ trackIndex: {
+ display: 'inline-block',
+ height: '16px',
+ width: '16px',
+ },
+ dragHandle: {
+ width: 20,
+ padding: `${theme.spacing(0.5)}px 0 0 0`,
+ },
+ dragHandleEmpty: {
+ width: 20,
+ padding: `${theme.spacing(0.5)}px 0 0 0`,
+ },
+ groupHeadRow: {
+ '&:hover': {
+ '& svg:not($deleteGroupButton)': {
+ display: 'none',
+ },
+ '& $deleteGroupButton': {
+ display: 'inline-block',
+ },
+ },
+ },
+}));
+
+interface TrackRowProps {
+ track: Track;
+ inGroup: boolean;
+ isSelected: boolean;
+ isCurrentTrack: boolean;
+ draggableProvided: DraggableProvided;
+ onSelect: (event: React.MouseEvent, trackIdx: number) => void;
+ onRename: (event: React.MouseEvent, trackIdx: number) => void;
+ onPlay: (event: React.MouseEvent, trackIdx: number) => void;
+}
+
+export function TrackRow({ track, inGroup, isSelected, draggableProvided, isCurrentTrack, onSelect, onRename, onPlay }: TrackRowProps) {
+ const classes = useStyles();
+
+ const handleRename = useCallback(event => onRename(event, track.index), [track.index, onRename]);
+ const handleSelect = useCallback(event => onSelect(event, track.index), [track.index, onSelect]);
+ const handlePlay = useCallback(
+ event => {
+ onPlay(event, track.index);
+ event?.stopPropagation();
+ },
+ [track.index, onPlay]
+ );
+
+ return (
+
+ event.stopPropagation()}>
+
+
+
+ {track.index + 1}
+
+
+
+
+
+ {track.fullWidthTitle ? `${track.fullWidthTitle} / ` : ``}
+ {track.title || `No Title`}
+
+
+ {EncodingName[track.encoding]}
+ {formatTimeFromFrames(track.duration, false)}
+
+
+ );
+}
+
+interface GroupRowProps {
+ group: Group;
+ onDoubleClick: (event: React.MouseEvent) => void;
+ onDelete: (event: React.MouseEvent) => void;
+}
+
+export function GroupRow({ group, onDoubleClick, onDelete }: GroupRowProps) {
+ const classes = useStyles();
+ return (
+
+
+
+
+
+
+
+ {group.fullWidthTitle ? `${group.fullWidthTitle} / ` : ``}
+ {group.title || `No Name`}
+
+
+
+ {formatTimeFromFrames(
+ group.tracks.map(n => n.duration).reduce((a, b) => a + b),
+ false
+ )}
+
+
+
+ );
+}
diff --git a/src/components/main.tsx b/src/components/main.tsx
index a633c38..fd352b1 100644
--- a/src/components/main.tsx
+++ b/src/components/main.tsx
@@ -17,18 +17,10 @@ import { actions as renameDialogActions } from '../redux/rename-dialog-feature';
import { actions as convertDialogActions } from '../redux/convert-dialog-feature';
import { actions as dumpDialogActions } from '../redux/dump-dialog-feature';
-import { formatTimeFromFrames, Track } from 'netmd-js';
+import { DeviceStatus, formatTimeFromFrames, Track } from 'netmd-js';
import { control } from '../redux/actions';
-import {
- belowDesktop,
- forAnyDesktop,
- getGroupedTracks,
- getSortedTracks,
- isSequential,
- useShallowEqualSelector,
- EncodingName,
-} from '../utils';
+import { belowDesktop, forAnyDesktop, getGroupedTracks, getSortedTracks, isSequential, useShallowEqualSelector } from '../utils';
import { lighten, makeStyles } from '@material-ui/core/styles';
import { fade } from '@material-ui/core/styles/colorManipulator';
@@ -39,11 +31,7 @@ import AddIcon from '@material-ui/icons/Add';
import DeleteIcon from '@material-ui/icons/Delete';
import EditIcon from '@material-ui/icons/Edit';
import Backdrop from '@material-ui/core/Backdrop';
-import PlayArrowIcon from '@material-ui/icons/PlayArrow';
-import PauseIcon from '@material-ui/icons/Pause';
-import FolderIcon from '@material-ui/icons/Folder';
import CreateNewFolderIcon from '@material-ui/icons/CreateNewFolder';
-import DragIndicator from '@material-ui/icons/DragIndicator';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
@@ -56,6 +44,7 @@ import Toolbar from '@material-ui/core/Toolbar';
import Tooltip from '@material-ui/core/Tooltip';
import { batchActions } from 'redux-batched-actions';
+import { GroupRow, TrackRow } from './main-rows';
import { RenameDialog } from './rename-dialog';
import { UploadDialog } from './upload-dialog';
import { RecordDialog } from './record-dialog';
@@ -66,7 +55,6 @@ import { AboutDialog } from './about-dialog';
import { DumpDialog } from './dump-dialog';
import { TopMenu } from './topmenu';
import Checkbox from '@material-ui/core/Checkbox';
-import * as BadgeImpl from '@material-ui/core/Badge/Badge';
import Button from '@material-ui/core/Button';
import { W95Main } from './win95/main';
import { useMemo } from 'react';
@@ -121,29 +109,6 @@ const useStyles = makeStyles(theme => ({
spacing: {
marginTop: theme.spacing(1),
},
- formatBadge: {
- ...(BadgeImpl as any).styles(theme).badge,
- ...(BadgeImpl as any).styles(theme).colorPrimary,
- position: 'static',
- display: 'inline-flex',
- border: `2px solid ${theme.palette.background.paper}`,
- padding: '0 4px',
- verticalAlign: 'middle',
- width: theme.spacing(4.5),
- marginRight: theme.spacing(0.5),
- },
- titleCell: {
- overflow: 'hidden',
- maxWidth: '40ch',
- textOverflow: 'ellipsis',
- // whiteSpace: 'nowrap',
- },
- durationCell: {
- whiteSpace: 'nowrap',
- },
- durationCellTime: {
- verticalAlign: 'middle',
- },
indexCell: {
whiteSpace: 'nowrap',
paddingRight: 0,
@@ -157,70 +122,19 @@ const useStyles = makeStyles(theme => ({
textDecoration: 'underline',
textDecorationStyle: 'dotted',
},
- controlButtonInTrackCommon: {
- width: theme.spacing(2),
- height: theme.spacing(2),
- verticalAlign: 'middle',
- marginLeft: theme.spacing(-0.5),
- },
- playButtonInTrackList: {
- display: 'none',
- },
- trackRow: {
- '&:hover': {
- '& $playButtonInTrackList': {
- display: 'inline-flex',
- },
- '& $trackIndex': {
- display: 'none',
- },
- },
- },
- currentTrackRow: {
- color: theme.palette.primary.main,
- '& > td': {
- color: 'inherit',
- },
- },
- inGroupTrackRow: {
- '& > $indexCell': {
- transform: `translateX(${theme.spacing(3)}px)`,
- },
- '& > $titleCell': {
- transform: `translateX(${theme.spacing(3)}px)`,
- },
- },
- deleteGroupButton: {
- display: 'none',
- },
- groupHeadRow: {
- '&:hover': {
- '& svg:not($deleteGroupButton)': {
- display: 'none',
- },
- '& $deleteGroupButton': {
- display: 'inline-block',
- },
- },
- },
hoveringOverGroup: {
backgroundColor: `${fade(theme.palette.secondary.dark, 0.4)}`,
},
- trackIndex: {
- display: 'inline-block',
- height: '16px',
- width: '16px',
- },
- dragHandle: {
- width: 20,
- padding: `${theme.spacing(0.5)}px 0 0 0`,
- },
dragHandleEmpty: {
width: 20,
padding: `${theme.spacing(0.5)}px 0 0 0`,
},
}));
+function isCurrentTrack(track: Track, deviceStatus: DeviceStatus | null) {
+ return track.index === deviceStatus?.track && ['playing', 'paused'].includes(deviceStatus?.state);
+}
+
export const Main = (props: {}) => {
let dispatch = useDispatch();
let disc = useShallowEqualSelector(state => state.main.disc);
@@ -370,12 +284,6 @@ export const Main = (props: {}) => {
}
};
- const handleCurrentClick = async (event: React.MouseEvent) => {
- if (deviceStatus?.state === 'playing') {
- dispatch(control('pause'));
- } else dispatch(control('play'));
- };
-
const canGroup = useMemo(() => {
return (
tracks.filter(n => n.group === null && selected.includes(n.index)).length === selected.length &&
@@ -418,49 +326,6 @@ export const Main = (props: {}) => {
return ;
}
- const getTrackRow = (track: Track, inGroup: boolean, draggableProvided: DraggableProvided) => {
- const isCurrentTrack = track.index === deviceStatus?.track && ['playing', 'paused'].includes(deviceStatus?.state);
- const child = (
- handleRenameDoubleClick(event, track.index)}
- onClick={event => handleSelectClick(event, track.index)}
- color="inherit"
- className={clsx(classes.trackRow, { [classes.inGroupTrackRow]: inGroup, [classes.currentTrackRow]: isCurrentTrack })}
- >
- event.stopPropagation()}>
-
-
-
- {track.index + 1}
- {
- handlePlayTrack(event, track.index);
- event.stopPropagation();
- }}
- >
-
-
-
-
- {track.fullWidthTitle ? `${track.fullWidthTitle} / ` : ``}
- {track.title || `No Title`}
-
-
- {EncodingName[track.encoding]}
- {formatTimeFromFrames(track.duration, false)}
-
-
- );
- return child;
- };
-
return (
@@ -572,40 +437,15 @@ export const Main = (props: {}) => {
className={clsx({ [classes.hoveringOverGroup]: snapshot.isDraggingOver })}
>
{group.title !== null && (
-
handleRenameDoubleClick(event, group.tracks[0].index, true)
}
- >
-
-
-
- {
- handleGroupRemoval(event, group.tracks[0].index);
- }}
- />
-
-
- {group.fullWidthTitle ? `${group.fullWidthTitle} / ` : ``}
- {group.title || `No Name`}
-
-
-
- {formatTimeFromFrames(
- group.tracks.map(n => n.duration).reduce((a, b) => a + b),
- false
- )}
-
-
-
+ onDelete={event => {
+ handleGroupRemoval(event, group.tracks[0].index);
+ }}
+ />
)}
{group.title === null && group.tracks.length === 0 && (
@@ -616,9 +456,18 @@ export const Main = (props: {}) => {
key={`t-${t.index}`}
index={tidx}
>
- {(provided: DraggableProvided) =>
- getTrackRow(t, group.title !== null, provided)
- }
+ {(provided: DraggableProvided) => (
+
+ )}
))}
{provided.placeholder}