webamp/js/components/PlaylistWindow/index.tsx
2018-10-21 15:49:57 -07:00

238 lines
6.9 KiB
TypeScript

import React from "react";
import { connect } from "react-redux";
import classnames from "classnames";
import { WINDOWS, TRACK_HEIGHT } from "../../constants";
import { SET_FOCUSED_WINDOW } from "../../actionTypes";
import {
toggleVisualizerStyle,
scrollUpFourTracks,
scrollDownFourTracks,
loadFilesFromReferences,
togglePlaylistShadeMode,
scrollVolume,
closeWindow
} from "../../actionCreators";
import {
getScrollOffset,
getWindowPixelSize,
getWindowSize,
getWindowShade,
getSkinPlaylistStyle
} from "../../selectors";
import { LOAD_STYLE } from "../../constants";
import { clamp } from "../../utils";
import DropTarget from "../DropTarget";
import Visualizer from "../Visualizer";
import PlaylistShade from "./PlaylistShade";
import AddMenu from "./AddMenu";
import RemoveMenu from "./RemoveMenu";
import SelectionMenu from "./SelectionMenu";
import MiscMenu from "./MiscMenu";
import ListMenu from "./ListMenu";
import PlaylistResizeTarget from "./PlaylistResizeTarget";
import PlaylistActionArea from "./PlaylistActionArea";
import TrackList from "./TrackList";
import ScrollBar from "./ScrollBar";
import "../../../css/playlist-window.css";
import { AppState, PlaylistStyle, Dispatch } from "../../types";
interface StateProps {
offset: number;
maxTrackIndex: number;
playlistWindowPixelSize: { width: number; height: number };
focused: string;
skinPlaylistStyle: PlaylistStyle;
playlistSize: [number, number];
playlistShade: boolean;
duration: number | null;
}
interface DispatchProps {
focusPlaylist(): void;
close(): void;
toggleShade(): void;
toggleVisualizerStyle(): void;
scrollUpFourTracks(): void;
scrollDownFourTracks(): void;
loadFilesFromReferences(
e: React.DragEvent<HTMLDivElement>,
startIndex: number
): void;
scrollVolume(e: React.WheelEvent<HTMLDivElement>): void;
}
interface OwnProps {
analyser: AnalyserNode;
}
type Props = StateProps & DispatchProps & OwnProps;
class PlaylistWindow extends React.Component<Props> {
_handleDrop = (
e: React.DragEvent<HTMLDivElement>,
targetCoords: { x: number; y: number }
) => {
const top = e.clientY - targetCoords.y;
const atIndex = clamp(
this.props.offset + Math.round((top - 23) / TRACK_HEIGHT),
0,
this.props.maxTrackIndex + 1
);
this.props.loadFilesFromReferences(e, atIndex);
};
render() {
const {
skinPlaylistStyle,
focusPlaylist,
focused,
playlistSize,
playlistWindowPixelSize,
playlistShade,
close,
toggleShade,
analyser
} = this.props;
if (playlistShade) {
return <PlaylistShade />;
}
const style = {
color: skinPlaylistStyle.normal,
backgroundColor: skinPlaylistStyle.normalbg,
fontFamily: `${skinPlaylistStyle.font}, Arial, sans-serif`,
height: `${playlistWindowPixelSize.height}px`,
width: `${playlistWindowPixelSize.width}px`
};
const classes = classnames("window", "draggable", {
selected: focused === WINDOWS.PLAYLIST,
wide: playlistSize[0] > 2
});
const showSpacers = playlistSize[0] % 2 === 0;
return (
<DropTarget
id="playlist-window"
className={classes}
style={style}
onMouseDown={focusPlaylist}
handleDrop={this._handleDrop}
onWheel={this.props.scrollVolume}
>
<div className="playlist-top draggable" onDoubleClick={toggleShade}>
<div className="playlist-top-left draggable" />
{showSpacers && (
<div className="playlist-top-left-spacer draggable" />
)}
<div className="playlist-top-left-fill draggable" />
<div className="playlist-top-title draggable" />
{showSpacers && (
<div className="playlist-top-right-spacer draggable" />
)}
<div className="playlist-top-right-fill draggable" />
<div className="playlist-top-right draggable">
<div id="playlist-shade-button" onClick={toggleShade} />
<div id="playlist-close-button" onClick={close} />
</div>
</div>
<div className="playlist-middle draggable">
<div className="playlist-middle-left draggable" />
<div className="playlist-middle-center">
<TrackList />
</div>
<div className="playlist-middle-right draggable">
<ScrollBar />
</div>
</div>
<div className="playlist-bottom draggable">
<div className="playlist-bottom-left draggable">
<AddMenu />
<RemoveMenu />
<SelectionMenu />
<MiscMenu />
</div>
<div className="playlist-bottom-center draggable" />
<div className="playlist-bottom-right draggable">
<div
className="playlist-visualizer"
onClick={this.props.toggleVisualizerStyle}
>
{/* TODO: Resize the visualizer so it fits */
false && (
<Visualizer
// @ts-ignore Visualizer is not yet typed
analyser={analyser}
/>
)}
</div>
<PlaylistActionArea />
<ListMenu />
<div
id="playlist-scroll-up-button"
onClick={this.props.scrollUpFourTracks}
/>
<div
id="playlist-scroll-down-button"
onClick={this.props.scrollDownFourTracks}
/>
<PlaylistResizeTarget />
</div>
</div>
</DropTarget>
);
}
}
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => {
return {
focusPlaylist: () =>
dispatch({
type: SET_FOCUSED_WINDOW,
window: WINDOWS.PLAYLIST
}),
close: () => dispatch(closeWindow("playlist")),
toggleShade: () => dispatch(togglePlaylistShadeMode()),
toggleVisualizerStyle: () => dispatch(toggleVisualizerStyle()),
scrollUpFourTracks: () => dispatch(scrollUpFourTracks()),
scrollDownFourTracks: () => dispatch(scrollDownFourTracks()),
loadFilesFromReferences: (e, startIndex) =>
dispatch(
loadFilesFromReferences(
e.dataTransfer.files,
LOAD_STYLE.NONE,
startIndex
)
),
scrollVolume: e => dispatch(scrollVolume(e))
};
};
const mapStateToProps = (state: AppState): StateProps => {
const {
windows: { focused },
media: { length },
playlist: { trackOrder }
} = state;
return {
offset: getScrollOffset(state),
maxTrackIndex: trackOrder.length - 1,
playlistWindowPixelSize: getWindowPixelSize(state)("playlist"),
focused,
skinPlaylistStyle: getSkinPlaylistStyle(state),
playlistSize: getWindowSize(state)("playlist"),
playlistShade: Boolean(getWindowShade(state)("playlist")),
duration: length
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PlaylistWindow);