Introduce WinampButton

This commit is contained in:
Jordan Eldredge 2022-01-31 22:42:36 -08:00
parent 83d18ed9b7
commit 85a80ef084
6 changed files with 527 additions and 1298 deletions

File diff suppressed because one or more lines are too long

View file

@ -4,6 +4,7 @@ import { useTypedSelector, useActionCreator } from "../../hooks";
import * as Selectors from "../../selectors";
import * as Actions from "../../actionCreators";
import VerticalSlider from "../VerticalSlider";
import WinampButton from "../WinampButton";
interface Props {
id: string;
@ -46,7 +47,11 @@ export default function Band({ id, onChange, band }: Props) {
// Note: The band background is actually one pixel taller (63) than the slider
// it contains (62).
return (
<div id={id} className="band" style={{ backgroundPosition, height: 63 }}>
<WinampButton
id={id}
className="band"
style={{ backgroundPosition, height: 63 }}
>
<VerticalSlider
height={62}
width={14}
@ -57,6 +62,6 @@ export default function Band({ id, onChange, band }: Props) {
onAfterChange={usetFocus}
handle={<Handle />}
/>
</div>
</WinampButton>
);
}

View file

@ -1,6 +1,7 @@
import { memo } from "react";
import * as Actions from "../../actionCreators";
import { useActionCreator } from "../../hooks";
import WinampButton from "../WinampButton";
const ActionButtons = memo(() => {
const previous = useActionCreator(Actions.previous);
@ -10,11 +11,11 @@ const ActionButtons = memo(() => {
const stop = useActionCreator(Actions.stop);
return (
<div className="actions">
<div id="previous" onClick={previous} title="Previous Track" />
<div id="play" onClick={play} title="Play" />
<div id="pause" onClick={pause} title="Pause" />
<div id="stop" onClick={stop} title="Stop" />
<div id="next" onClick={next} title="Next Track" />
<WinampButton id="previous" onClick={previous} title="Previous Track" />
<WinampButton id="play" onClick={play} title="Play" />
<WinampButton id="pause" onClick={pause} title="Pause" />
<WinampButton id="stop" onClick={stop} title="Stop" />
<WinampButton id="next" onClick={next} title="Next Track" />
</div>
);
});

View file

@ -22,6 +22,7 @@ import PlaylistScrollBar from "./PlaylistScrollBar";
import { AppState } from "../../types";
import FocusTarget from "../FocusTarget";
import { useActionCreator, useTypedSelector } from "../../hooks";
import WinampButton from "../WinampButton";
interface Props {
analyser: AnalyserNode;
@ -122,9 +123,9 @@ function PlaylistWindow({ analyser }: Props) {
<div className="playlist-middle-center">
<TrackList />
</div>
<div className="playlist-middle-right draggable">
<WinampButton className="playlist-middle-right draggable">
<PlaylistScrollBar />
</div>
</WinampButton>
</div>
<div className="playlist-bottom draggable">
<div className="playlist-bottom-left draggable">

View file

@ -0,0 +1,83 @@
import classnames from "classnames";
import {
useCallback,
DetailedHTMLProps,
HTMLAttributes,
useState,
} from "react";
const ACTIVE_CLASSNAME = "winamp-active";
const LEFT_MOUSE_NUMBER = 0;
type Props = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
/**
* Renders a `div` with an `.winamp-active` class if the element is being clicked/tapped.
*
* For now this mimicks the behavior of `:active`, but in the future we will use
* this component to mimic Winamp's behavior, which is quite different than
* `:active`.
*
* https://html.spec.whatwg.org/multipage/semantics-other.html#selector-active
*
* > An element is said to be being actively pointed at while the user indicates
* > the element using a pointing device while that pointing device is in the
* > "down" state (e.g. for a mouse, between the time the mouse button is pressed
* > and the time it is depressed; for a finger in a multitouch environment, while
* > the finger is touching the display surface).
*/
export default function WinampButton(props: Props): JSX.Element {
const [active, setActive] = useState(false);
const originalOnMouseDown = props.onMouseDown;
const onMouseDown = useCallback(
(e) => {
if (originalOnMouseDown != null) {
originalOnMouseDown(e);
}
// We only care about left mouse.
if (e.nativeEvent.button !== LEFT_MOUSE_NUMBER) {
return;
}
setActive(true);
function onUp(ee: MouseEvent) {
if (ee.button !== LEFT_MOUSE_NUMBER) {
return;
}
setActive(false);
document.removeEventListener("mouseup", onUp);
}
document.addEventListener("mouseup", onUp);
},
[originalOnMouseDown]
);
const originalOnTouchStart = props.onTouchStart;
const onTouchStart = useCallback(
(e) => {
if (originalOnTouchStart != null) {
originalOnTouchStart(e);
}
setActive(true);
function onUp() {
setActive(false);
document.removeEventListener("touchend", onUp);
}
document.addEventListener("touchend", onUp);
},
[originalOnTouchStart]
);
const className = classnames(props.className, { [ACTIVE_CLASSNAME]: active });
return (
<div
{...props}
className={className}
onMouseDown={onMouseDown}
onTouchStart={onTouchStart}
/>
);
}

View file

@ -16,17 +16,17 @@ export const imageSelectors: Selectors = {
"#balance:active::-moz-range-thumb",
],
MAIN_PREVIOUS_BUTTON: [".actions #previous"],
MAIN_PREVIOUS_BUTTON_ACTIVE: [".actions #previous:active"],
MAIN_PREVIOUS_BUTTON_ACTIVE: [".actions #previous.winamp-active"],
MAIN_PLAY_BUTTON: [".actions #play"],
MAIN_PLAY_BUTTON_ACTIVE: [".actions #play:active"],
MAIN_PLAY_BUTTON_ACTIVE: [".actions #play.winamp-active"],
MAIN_PAUSE_BUTTON: [".actions #pause"],
MAIN_PAUSE_BUTTON_ACTIVE: [".actions #pause:active"],
MAIN_PAUSE_BUTTON_ACTIVE: [".actions #pause.winamp-active"],
MAIN_STOP_BUTTON: [".actions #stop"],
MAIN_STOP_BUTTON_ACTIVE: [".actions #stop:active"],
MAIN_STOP_BUTTON_ACTIVE: [".actions #stop.winamp-active"],
MAIN_NEXT_BUTTON: [".actions #next"],
MAIN_NEXT_BUTTON_ACTIVE: [".actions #next:active"],
MAIN_NEXT_BUTTON_ACTIVE: [".actions #next.winamp-active"],
MAIN_EJECT_BUTTON: ["#eject"],
MAIN_EJECT_BUTTON_ACTIVE: ["#eject:active"],
MAIN_EJECT_BUTTON_ACTIVE: ["#eject.winamp-active"],
MAIN_WINDOW_BACKGROUND: ["#main-window"],
MAIN_STEREO: [".media-info #stereo", ".stop .media-info #stereo.selected"],
MAIN_STEREO_SELECTED: [".media-info #stereo.selected"],
@ -83,7 +83,7 @@ export const imageSelectors: Selectors = {
PLAYLIST_RIGHT_TILE: [".playlist-middle-right"],
PLAYLIST_SCROLL_HANDLE: [".playlist-scrollbar-handle"],
PLAYLIST_SCROLL_HANDLE_SELECTED: [
".playlist-middle-right:active .playlist-scrollbar-handle",
".playlist-middle-right.winamp-active .playlist-scrollbar-handle",
],
PLAYLIST_BOTTOM_TILE: [".playlist-bottom"],
PLAYLIST_BOTTOM_LEFT_CORNER: [".playlist-bottom-left"],
@ -158,7 +158,7 @@ export const imageSelectors: Selectors = {
EQ_SLIDER_THUMB: [".band .slider-handle"],
// But the "active" pseudo selector on the parent, since clicking
// anywhere on the track moves the slider.
EQ_SLIDER_THUMB_SELECTED: [".band:active .slider-handle"],
EQ_SLIDER_THUMB_SELECTED: [".band.winamp-active .slider-handle"],
EQ_ON_BUTTON: ["#on"],
EQ_ON_BUTTON_DEPRESSED: ["#on:active"],
EQ_ON_BUTTON_SELECTED: ["#on.selected"],