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}