mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-23 01:57:29 +00:00
209 lines
6.5 KiB
TypeScript
209 lines
6.5 KiB
TypeScript
import React, { useCallback } from "react";
|
|
import { connect } from "react-redux";
|
|
import classnames from "classnames";
|
|
|
|
import { WINDOWS, TRACK_HEIGHT, LOAD_STYLE } from "../../constants";
|
|
import * as Actions from "../../actionCreators";
|
|
import * as Selectors from "../../selectors";
|
|
|
|
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";
|
|
import FocusTarget from "../FocusTarget";
|
|
|
|
interface StateProps {
|
|
offset: number;
|
|
maxTrackIndex: number;
|
|
playlistWindowPixelSize: { width: number; height: number };
|
|
showVisualizer: boolean;
|
|
activateVisualizer: boolean;
|
|
selected: boolean;
|
|
skinPlaylistStyle: PlaylistStyle;
|
|
playlistSize: [number, number];
|
|
playlistShade: boolean;
|
|
duration: number | null;
|
|
}
|
|
|
|
interface DispatchProps {
|
|
close(): void;
|
|
toggleShade(): void;
|
|
scrollUpFourTracks(): void;
|
|
scrollDownFourTracks(): void;
|
|
loadMedia(e: React.DragEvent<HTMLDivElement>, startIndex: number): void;
|
|
scrollVolume(e: React.WheelEvent<HTMLDivElement>): void;
|
|
}
|
|
|
|
interface OwnProps {
|
|
analyser: AnalyserNode;
|
|
}
|
|
|
|
type Props = StateProps & DispatchProps & OwnProps;
|
|
|
|
function PlaylistWindow({
|
|
skinPlaylistStyle,
|
|
selected,
|
|
playlistSize,
|
|
playlistWindowPixelSize,
|
|
playlistShade,
|
|
close,
|
|
toggleShade,
|
|
analyser,
|
|
showVisualizer,
|
|
activateVisualizer,
|
|
maxTrackIndex,
|
|
offset,
|
|
loadMedia,
|
|
scrollUpFourTracks,
|
|
scrollDownFourTracks,
|
|
scrollVolume,
|
|
}: Props) {
|
|
const _handleDrop = useCallback(
|
|
(
|
|
e: React.DragEvent<HTMLDivElement>,
|
|
targetCoords: { x: number; y: number }
|
|
) => {
|
|
const top = e.clientY - targetCoords.y;
|
|
const atIndex = clamp(
|
|
offset + Math.round((top - 23) / TRACK_HEIGHT),
|
|
0,
|
|
maxTrackIndex + 1
|
|
);
|
|
loadMedia(e, atIndex);
|
|
},
|
|
[loadMedia, offset, maxTrackIndex]
|
|
);
|
|
|
|
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 });
|
|
|
|
const showSpacers = playlistSize[0] % 2 === 0;
|
|
|
|
return (
|
|
<FocusTarget windowId={WINDOWS.PLAYLIST}>
|
|
<DropTarget
|
|
id="playlist-window"
|
|
className={classes}
|
|
style={style}
|
|
handleDrop={_handleDrop}
|
|
onWheel={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">
|
|
{showVisualizer && (
|
|
<div className="playlist-visualizer">
|
|
{activateVisualizer && (
|
|
<div className="visualizer-wrapper">
|
|
<Visualizer
|
|
// @ts-ignore Visualizer is not yet typed
|
|
analyser={analyser}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
<PlaylistActionArea />
|
|
<ListMenu />
|
|
<div id="playlist-scroll-up-button" onClick={scrollUpFourTracks} />
|
|
<div
|
|
id="playlist-scroll-down-button"
|
|
onClick={scrollDownFourTracks}
|
|
/>
|
|
<PlaylistResizeTarget />
|
|
</div>
|
|
</div>
|
|
</DropTarget>
|
|
</FocusTarget>
|
|
);
|
|
}
|
|
|
|
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => {
|
|
return {
|
|
close: () => dispatch(Actions.closeWindow(WINDOWS.PLAYLIST)),
|
|
toggleShade: () => dispatch(Actions.togglePlaylistShadeMode()),
|
|
scrollUpFourTracks: () => dispatch(Actions.scrollUpFourTracks()),
|
|
scrollDownFourTracks: () => dispatch(Actions.scrollDownFourTracks()),
|
|
loadMedia: (e, startIndex) =>
|
|
dispatch(Actions.loadMedia(e, LOAD_STYLE.NONE, startIndex)),
|
|
scrollVolume: e => dispatch(Actions.scrollVolume(e)),
|
|
};
|
|
};
|
|
|
|
const mapStateToProps = (state: AppState): StateProps => {
|
|
const {
|
|
playlist: { trackOrder },
|
|
} = state;
|
|
const playlistSize = Selectors.getWindowSize(state)(WINDOWS.PLAYLIST);
|
|
|
|
return {
|
|
offset: Selectors.getScrollOffset(state),
|
|
maxTrackIndex: trackOrder.length - 1,
|
|
playlistWindowPixelSize: Selectors.getWindowPixelSize(state)(
|
|
WINDOWS.PLAYLIST
|
|
),
|
|
showVisualizer: playlistSize[0] > 2,
|
|
activateVisualizer: !Selectors.getWindowOpen(state)(WINDOWS.MAIN),
|
|
playlistSize,
|
|
selected: Selectors.getFocusedWindow(state) === WINDOWS.PLAYLIST,
|
|
skinPlaylistStyle: Selectors.getSkinPlaylistStyle(state),
|
|
playlistShade: Boolean(Selectors.getWindowShade(state)(WINDOWS.PLAYLIST)),
|
|
duration: Selectors.getDuration(state),
|
|
};
|
|
};
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(PlaylistWindow);
|