diff --git a/css/playlist-window.css b/css/playlist-window.css index 74825f50..213879d0 100644 --- a/css/playlist-window.css +++ b/css/playlist-window.css @@ -50,52 +50,32 @@ #winamp2-js .playlist-middle-center { flex-grow: 1; padding: 3px 0; + min-width: 0; /* Not sure why this is needed */ } #winamp2-js .playlist-tracks { - overflow: hidden; - height: 100%; -} -#winamp2-js .playlist-tracks > div { - width: 100%; - display: table; -} - -#winamp2-js .playlist-track { + display: flex; + flex: 1 0 auto; font-size: 9px; - display: table-row; line-height: 13px; letter-spacing: 0.5px; user-select: none; } -#winamp2-js .playlist-track-number { - text-align: right; +#winamp2-js .playlist-track-numbers > div, +#winamp2-js .playlist-track-durations > div { padding-right: 3px; - display: table-cell; - width: 1%; - white-space: nowrap; } -#winamp2-js .playlist-track-title { - flex-grow: 1; - display: table-cell; - width: auto; +#winamp2-js .playlist-track-titles { + flex: 1 1 auto; + overflow: hidden; } -#winamp2-js .playlist-track-title > span { +#winamp2-js .playlist-track-titles > div { + text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - display: block; - text-overflow: ellipsis; - position: relative; -} - -#winamp2-js .playlist-track-duration { - padding-right: 3px; - display: table-cell; - width: 1%; - white-space: nowrap; } #winamp2-js .playlist-middle-right { diff --git a/js/components/PlaylistWindow/Track.js b/js/components/PlaylistWindow/TrackCell.js similarity index 74% rename from js/components/PlaylistWindow/Track.js rename to js/components/PlaylistWindow/TrackCell.js index 22085780..abe027fd 100644 --- a/js/components/PlaylistWindow/Track.js +++ b/js/components/PlaylistWindow/TrackCell.js @@ -1,24 +1,21 @@ import React from "react"; import { connect } from "react-redux"; import classnames from "classnames"; -import { getTimeStr } from "../../utils"; import { CLICKED_TRACK, CTRL_CLICKED_TRACK, PLAY_TRACK } from "../../actionTypes"; -const Track = props => { +const TrackCell = props => { const { skinPlaylistStyle, selected, current, - title, - number, - duration, clickTrack, ctrlClickTrack, - playTrack + playTrack, + children } = props; const style = { backgroundColor: selected ? skinPlaylistStyle.selectedbg : null, @@ -26,17 +23,13 @@ const Track = props => { }; return (
-
{number}.
-
- {title} -
-
{getTimeStr(duration)}
+ {children}
); }; @@ -51,8 +44,6 @@ const mapStateToProps = (state, ownProps) => { return { skinPlaylistStyle, selected: track.selected, - title: track.title, - duration: track.duration, current: currentTrack === ownProps.id }; }; @@ -70,4 +61,4 @@ const mapDispatchToProps = (dispatch, ownProps) => ({ playTrack: () => dispatch({ type: PLAY_TRACK, id: ownProps.id }) }); -export default connect(mapStateToProps, mapDispatchToProps)(Track); +export default connect(mapStateToProps, mapDispatchToProps)(TrackCell); diff --git a/js/components/PlaylistWindow/TrackList.js b/js/components/PlaylistWindow/TrackList.js new file mode 100644 index 00000000..b256909a --- /dev/null +++ b/js/components/PlaylistWindow/TrackList.js @@ -0,0 +1,44 @@ +import React from "react"; +import { connect } from "react-redux"; + +import { getTimeStr } from "../../utils"; +import { getVisibleTrackIds } from "../../selectors"; +import TrackCell from "./TrackCell"; + +const TrackList = props => { + const { trackIds, tracks } = props; + return ( +
+
+ {trackIds.map((id, i) => ( + + {`${i + 1}.`} + + ))} +
+
+ {trackIds.map(id => ( + + {tracks[id].title} + + ))} +
+
+ {trackIds.map(id => ( + + {getTimeStr(tracks[id].duration)} + + ))} +
+
+ ); +}; + +const mapDispatchToProps = () => ({}); + +const mapStateToProps = state => ({ + trackIds: getVisibleTrackIds(state), + tracks: state.tracks +}); + +export default connect(mapStateToProps, mapDispatchToProps)(TrackList); diff --git a/js/components/PlaylistWindow/__snapshots__/index.test.js.snap b/js/components/PlaylistWindow/__snapshots__/index.test.js.snap index 2ef00cc5..136c940a 100644 --- a/js/components/PlaylistWindow/__snapshots__/index.test.js.snap +++ b/js/components/PlaylistWindow/__snapshots__/index.test.js.snap @@ -52,7 +52,15 @@ exports[`PlaylistWindow renders to snapshot 1`] = `
-
+
+
+
; @@ -50,12 +48,13 @@ const PlaylistWindow = props => { focused, playlistScrollPosition, setPlaylistScrollPosition, - trackOrder, playlistSize, playlistShade, close, - toggleShade + toggleShade, + allTracksAreVisible } = props; + console.log({ allTracksAreVisible }); if (playlistShade) { return ; } @@ -75,22 +74,6 @@ const PlaylistWindow = props => { wide: playlistSize[0] > 2 }); - const BASE_WINDOW_HEIGHT = 52; - const numberOfVisibleTracks = Math.floor( - (BASE_WINDOW_HEIGHT + PLAYLIST_RESIZE_SEGMENT_HEIGHT * playlistSize[1]) / - TRACK_HEIGHT - ); - const overflowTracks = Math.max(0, trackOrder.length - numberOfVisibleTracks); - const offset = percentToIndex( - playlistScrollPosition / 100, - overflowTracks + 1 - ); - - // Ugh. By not rendering some tracks, we can end up in a situation where - // scrolling causes the number of digits in the tracks to go up, thus causing - // a horizontal jump. - const tracks = trackOrder.slice(offset, offset + numberOfVisibleTracks); - return ( {
-
-
- {tracks.map((id, i) => ( - - ))} -
-
+
{ onChange={setPlaylistScrollPosition} vertical handle={Handle} - disabled={overflowTracks === 0} + disabled={allTracksAreVisible} />
@@ -177,6 +154,7 @@ const mapDispatchToProps = (dispatch, ownProps) => ({ stop: () => dispatch(stop()), openFileDialog: () => dispatch(openFileDialog(ownProps.fileInput)), setPlaylistScrollPosition: position => + // TODO: Move this to an action creator so we can see if this is actually changing dispatch({ type: SET_PLAYLIST_SCROLL_POSITION, position: 100 - position }), close: () => dispatch({ type: TOGGLE_PLAYLIST_WINDOW }), toggleShade: () => dispatch({ type: TOGGLE_PLAYLIST_SHADE_MODE }), @@ -192,16 +170,18 @@ const mapStateToProps = state => { playlistSize, playlistShade }, - media: { duration } + media: { duration }, + playlist: { trackOrder } } = state; + return { focused, skinPlaylistStyle, playlistScrollPosition, playlistSize, playlistShade, - trackOrder: getOrderedTracks(state), - duration + duration, + allTracksAreVisible: getVisibleTrackIds(state).length === trackOrder.length }; }; diff --git a/js/selectors.js b/js/selectors.js index 032506a1..d0ba9447 100644 --- a/js/selectors.js +++ b/js/selectors.js @@ -1,5 +1,5 @@ -import { denormalize, getTimeStr, clamp } from "./utils"; -import { BANDS } from "./constants"; +import { denormalize, getTimeStr, clamp, percentToIndex } from "./utils"; +import { BANDS, PLAYLIST_RESIZE_SEGMENT_HEIGHT } from "./constants"; import { createSelector } from "reselect"; export const getEqfData = state => { @@ -87,3 +87,34 @@ export const nextTrack = (state, n = 1) => { nextIndex = clamp(nextIndex, 0, trackOrder.length - 1); return trackOrder[nextIndex]; }; + +export const getPlaylistScrollPosition = state => + state.display.playlistScrollPosition; + +const TRACK_HEIGHT = 13; +const BASE_WINDOW_HEIGHT = 52; +export const getNumberOfVisibleTracks = state => { + const { playlistSize } = state.display; + return Math.floor( + (BASE_WINDOW_HEIGHT + PLAYLIST_RESIZE_SEGMENT_HEIGHT * playlistSize[1]) / + TRACK_HEIGHT + ); +}; + +export const getOverflowTrackCount = createSelector( + getTrackOrder, + getNumberOfVisibleTracks, + (trackOrder, numberOfVisibleTracks) => + Math.max(0, trackOrder.length - numberOfVisibleTracks) +); + +export const getVisibleTrackIds = createSelector( + getPlaylistScrollPosition, + getTrackOrder, + getNumberOfVisibleTracks, + (playlistScrollPosition, trackOrder, numberOfVisibleTracks) => { + const overflow = Math.max(0, trackOrder.length - numberOfVisibleTracks); + const offset = percentToIndex(playlistScrollPosition / 100, overflow + 1); + return trackOrder.slice(offset, offset + numberOfVisibleTracks); + } +);