mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-21 00:59:29 +00:00
Refactor how tracks are listed so we can support ellipsis
This commit is contained in:
parent
a5c7fbc90a
commit
943cf47caa
6 changed files with 115 additions and 81 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div
|
||||
className={classnames("playlist-track", { selected, current })}
|
||||
className={classnames({ selected, current })}
|
||||
style={style}
|
||||
onClick={clickTrack}
|
||||
onDoubleClick={playTrack}
|
||||
onContextMenu={ctrlClickTrack}
|
||||
>
|
||||
<div className="playlist-track-number">{number}.</div>
|
||||
<div className="playlist-track-title">
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
<div className="playlist-track-duration">{getTimeStr(duration)}</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -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);
|
||||
44
js/components/PlaylistWindow/TrackList.js
Normal file
44
js/components/PlaylistWindow/TrackList.js
Normal file
|
|
@ -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 (
|
||||
<div className="playlist-tracks">
|
||||
<div className="playlist-track-numbers">
|
||||
{trackIds.map((id, i) => (
|
||||
<TrackCell key={id} id={id}>
|
||||
{`${i + 1}.`}
|
||||
</TrackCell>
|
||||
))}
|
||||
</div>
|
||||
<div className="playlist-track-titles">
|
||||
{trackIds.map(id => (
|
||||
<TrackCell key={id} id={id}>
|
||||
{tracks[id].title}
|
||||
</TrackCell>
|
||||
))}
|
||||
</div>
|
||||
<div className="playlist-track-durations">
|
||||
{trackIds.map(id => (
|
||||
<TrackCell key={id} id={id}>
|
||||
{getTimeStr(tracks[id].duration)}
|
||||
</TrackCell>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const mapDispatchToProps = () => ({});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
trackIds: getVisibleTrackIds(state),
|
||||
tracks: state.tracks
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TrackList);
|
||||
|
|
@ -52,7 +52,15 @@ exports[`PlaylistWindow renders to snapshot 1`] = `
|
|||
<div
|
||||
className="playlist-tracks"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
className="playlist-track-numbers"
|
||||
/>
|
||||
<div
|
||||
className="playlist-track-titles"
|
||||
/>
|
||||
<div
|
||||
className="playlist-track-durations"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import Slider from "rc-slider/lib/Slider";
|
|||
import DropTarget from "../DropTarget";
|
||||
import MiniTime from "../MiniTime";
|
||||
import PlaylistShade from "./PlaylistShade";
|
||||
import Track from "./Track";
|
||||
import AddMenu from "./AddMenu";
|
||||
import RemoveMenu from "./RemoveMenu";
|
||||
import SelectionMenu from "./SelectionMenu";
|
||||
|
|
@ -14,7 +13,7 @@ import MiscMenu from "./MiscMenu";
|
|||
import ListMenu from "./ListMenu";
|
||||
import ResizeTarget from "./ResizeTarget";
|
||||
import RunningTimeDisplay from "./RunningTimeDisplay";
|
||||
import { percentToIndex } from "../../utils";
|
||||
import TrackList from "./TrackList";
|
||||
import {
|
||||
WINDOWS,
|
||||
PLAYLIST_RESIZE_SEGMENT_WIDTH,
|
||||
|
|
@ -27,7 +26,7 @@ import {
|
|||
SET_FOCUSED_WINDOW,
|
||||
SET_PLAYLIST_SCROLL_POSITION
|
||||
} from "../../actionTypes";
|
||||
import { getOrderedTracks } from "../../selectors";
|
||||
import { getVisibleTrackIds } from "../../selectors";
|
||||
import {
|
||||
play,
|
||||
pause,
|
||||
|
|
@ -38,7 +37,6 @@ import {
|
|||
|
||||
import "../../../css/playlist-window.css";
|
||||
|
||||
const TRACK_HEIGHT = 13;
|
||||
const MIN_WINDOW_HEIGHT = 116;
|
||||
|
||||
const Handle = () => <div className="playlist-scrollbar-handle" />;
|
||||
|
|
@ -50,12 +48,13 @@ const PlaylistWindow = props => {
|
|||
focused,
|
||||
playlistScrollPosition,
|
||||
setPlaylistScrollPosition,
|
||||
trackOrder,
|
||||
playlistSize,
|
||||
playlistShade,
|
||||
close,
|
||||
toggleShade
|
||||
toggleShade,
|
||||
allTracksAreVisible
|
||||
} = props;
|
||||
console.log({ allTracksAreVisible });
|
||||
if (playlistShade) {
|
||||
return <PlaylistShade />;
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<DropTarget
|
||||
id="playlist-window"
|
||||
|
|
@ -109,13 +92,7 @@ const PlaylistWindow = props => {
|
|||
<div className="playlist-middle draggable">
|
||||
<div className="playlist-middle-left draggable" />
|
||||
<div className="playlist-middle-center">
|
||||
<div className="playlist-tracks">
|
||||
<div>
|
||||
{tracks.map((id, i) => (
|
||||
<Track number={i + 1 + offset} id={id} key={id} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<TrackList />
|
||||
</div>
|
||||
<div className="playlist-middle-right draggable">
|
||||
<Slider
|
||||
|
|
@ -128,7 +105,7 @@ const PlaylistWindow = props => {
|
|||
onChange={setPlaylistScrollPosition}
|
||||
vertical
|
||||
handle={Handle}
|
||||
disabled={overflowTracks === 0}
|
||||
disabled={allTracksAreVisible}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue