mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-22 09:37:17 +00:00
Implement VerticalScrollbar
This gets rid of rc-slider which was way too heavy, was out of date and I never quiet trusted. We only ever used a portion of it any way. Also, this sets us up to have more direct control over the sliders which we will need if we want to support the behavior where you can click once and drag across the EQ sliders to draw a line.
This commit is contained in:
parent
bb8de18a73
commit
cfeef49ba7
13 changed files with 162 additions and 563 deletions
File diff suppressed because one or more lines are too long
|
|
@ -195,20 +195,3 @@
|
|||
top: 38px;
|
||||
}
|
||||
|
||||
#webamp .band {
|
||||
width: 14px;
|
||||
height: 63px;
|
||||
}
|
||||
|
||||
#webamp .band .rc-slider {
|
||||
height: 51px;
|
||||
width: 14px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
#webamp .band .rc-slider .rc-slider-handle {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
margin: -6px 0 0 1px;
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,17 +102,6 @@
|
|||
padding-bottom: 18px;
|
||||
}
|
||||
|
||||
#webamp .playlist-scrollbar {
|
||||
height: 100%;
|
||||
width: 8px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#webamp .playlist-scrollbar-handle {
|
||||
width: 8x;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
#webamp .playlist-bottom {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ Coordination between the playing media (which is inherently stateful, and change
|
|||
|
||||
We use [react-redux] to bind Redux state into our [React components](../js/components/). Any component which is not reusable, and even some that are, are connected using [connect]. This works well for us, since most components are "one off", and connecting most components allows for state changes to result in a mininmal set of react components needing to re-render. This does, however, require that our selectors be performant.
|
||||
|
||||
In some places we use [rc-slider](https://github.com/react-component/slider) instead of `<input type='range'>` since it gives us more precise control over styling. This is particualarly true for the vertial sliders in the equalizer window. As far as I can tell, there is no way to style the handle of a vertical `<input type='range'>` in a way that gives pixel-level accuracy.
|
||||
|
||||
## Media
|
||||
|
||||
Media (audio files) is managed by our [Media](../js/media/index.ts) class. It encapsulates the Web Audio API complexity. Audio manipulation (volume, balance, EQ) is handled in the main `Media` class, but the audio source is managed in [elementSource.ts](../js/media/elementSource.ts). This class tries to encapsulate some of the complexity required to get the playing of audio files working across all browsers seamlessly. We handle playing audio from a URL source (subject to CORs) and from a local file. Both of these are normalized to a URL before they are passed into our audio aparatus. For local files, we convert the `ArrayBuffer` we get, into a Blob url using `URL.createObjectURL()`. This transformation is handlded inside our Action Creators (see the Redux section above).
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { useMemo } from "react";
|
||||
import Slider from "rc-slider";
|
||||
import { Slider as SliderType } from "../../types";
|
||||
import { useTypedSelector, useActionCreator } from "../../hooks";
|
||||
import * as Selectors from "../../selectors";
|
||||
import * as Actions from "../../actionCreators";
|
||||
import VerticalSlider from "../VerticalSlider";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
|
|
@ -12,9 +12,10 @@ interface Props {
|
|||
}
|
||||
|
||||
const MAX_VALUE = 100;
|
||||
|
||||
// Given a value between 1-100, return the sprite number (0-27)
|
||||
export const spriteNumber = (value: number): number => {
|
||||
const percent = value / 100;
|
||||
const percent = value / MAX_VALUE;
|
||||
return Math.round(percent * 27);
|
||||
};
|
||||
|
||||
|
|
@ -25,7 +26,10 @@ export const spriteOffsets = (number: number): { x: number; y: number } => {
|
|||
return { x, y };
|
||||
};
|
||||
|
||||
const Handle = () => <div className="rc-slider-handle" />;
|
||||
const Handle = () => {
|
||||
const style = { width: 11, height: 11, marginLeft: 1 };
|
||||
return <div style={style} className="slider-handle" />;
|
||||
};
|
||||
|
||||
export default function Band({ id, onChange, band }: Props) {
|
||||
const sliders = useTypedSelector(Selectors.getSliders);
|
||||
|
|
@ -39,20 +43,17 @@ export default function Band({ id, onChange, band }: Props) {
|
|||
const focusBand = useActionCreator(Actions.focusBand);
|
||||
const usetFocus = useActionCreator(Actions.unsetFocus);
|
||||
|
||||
const handleMouseDown = () => focusBand(band);
|
||||
|
||||
return (
|
||||
<div id={id} className="band" style={{ backgroundPosition }}>
|
||||
<Slider
|
||||
min={0}
|
||||
max={MAX_VALUE}
|
||||
step={1}
|
||||
value={MAX_VALUE - value}
|
||||
vertical
|
||||
onChange={onChange}
|
||||
onBeforeChange={handleMouseDown}
|
||||
<VerticalSlider
|
||||
height={62}
|
||||
width={14}
|
||||
handleHeight={11}
|
||||
value={1 - value / MAX_VALUE}
|
||||
onBeforeChange={() => focusBand(band)}
|
||||
onChange={(val) => onChange((1 - val) * MAX_VALUE)}
|
||||
onAfterChange={usetFocus}
|
||||
handle={Handle}
|
||||
handle={<Handle />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import VerticalSlider from "../VerticalSlider";
|
||||
|
||||
import * as Selectors from "../../selectors";
|
||||
import * as Actions from "../../actionCreators";
|
||||
import { useTypedSelector, useActionCreator } from "../../hooks";
|
||||
import { WINDOWS } from "../../constants";
|
||||
|
||||
const HANDLE_HEIGHT = 18;
|
||||
|
||||
const Handle = () => (
|
||||
<div
|
||||
className="playlist-scrollbar-handle"
|
||||
style={{ height: HANDLE_HEIGHT }}
|
||||
/>
|
||||
);
|
||||
|
||||
export default function PlaylistScrollBar() {
|
||||
const getWindowPixelSize = useTypedSelector(Selectors.getWindowPixelSize);
|
||||
const playlistHeight = getWindowPixelSize(WINDOWS.PLAYLIST).height;
|
||||
const playlistScrollPosition = useTypedSelector(
|
||||
Selectors.getPlaylistScrollPosition
|
||||
);
|
||||
const allTracksAreVisible = useTypedSelector(
|
||||
Selectors.getAllTracksAreVisible
|
||||
);
|
||||
const setPlaylistScrollPosition = useActionCreator(
|
||||
Actions.setPlaylistScrollPosition
|
||||
);
|
||||
return (
|
||||
<div className="playlist-scrollbar" style={{ marginLeft: 5 }}>
|
||||
<VerticalSlider
|
||||
height={playlistHeight - 58}
|
||||
handleHeight={HANDLE_HEIGHT}
|
||||
width={8}
|
||||
value={playlistScrollPosition / 100}
|
||||
onChange={(val) => setPlaylistScrollPosition(val * 100)}
|
||||
handle={<Handle />}
|
||||
disabled={allTracksAreVisible}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import { memo } from "react";
|
||||
|
||||
// Here we import the rc-slider class just to get it's type.
|
||||
// We expect the Typescript compiler to not actually include this in the bundle.
|
||||
import RcSlider from "rc-slider";
|
||||
// @ts-ignore
|
||||
import SliderComponent from "rc-slider/lib/Slider";
|
||||
|
||||
import {
|
||||
getAllTracksAreVisible,
|
||||
getPlaylistScrollPosition,
|
||||
} from "../../selectors";
|
||||
import * as Actions from "../../actionCreators";
|
||||
import { useTypedSelector, useActionCreator } from "../../hooks";
|
||||
|
||||
// Here we inform TypeScript to use the default export's type for our partial import.
|
||||
const Slider = SliderComponent as typeof RcSlider;
|
||||
|
||||
const Handle = () => <div className="playlist-scrollbar-handle" />;
|
||||
|
||||
const ScrollBar = () => {
|
||||
const playlistScrollPosition = useTypedSelector(getPlaylistScrollPosition);
|
||||
const allTracksAreVisible = useTypedSelector(getAllTracksAreVisible);
|
||||
const setPlaylistScrollPosition = useActionCreator((position: number) =>
|
||||
Actions.setPlaylistScrollPosition(100 - position)
|
||||
);
|
||||
return (
|
||||
<Slider
|
||||
className="playlist-scrollbar"
|
||||
min={0}
|
||||
max={100}
|
||||
step={1}
|
||||
value={playlistScrollPosition}
|
||||
onChange={setPlaylistScrollPosition}
|
||||
vertical
|
||||
handle={Handle}
|
||||
disabled={allTracksAreVisible}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ScrollBar);
|
||||
|
|
@ -1,436 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`PlaylistWindow renders to snapshot 1`] = `
|
||||
<div
|
||||
onFocus={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": "100%",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div
|
||||
className="window draggable"
|
||||
id="playlist-window"
|
||||
onDragEnter={[Function]}
|
||||
onDragOver={[Function]}
|
||||
onDragStart={[Function]}
|
||||
onDrop={[Function]}
|
||||
onWheel={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#000000",
|
||||
"color": "#00FF00",
|
||||
"fontFamily": "Arial, Arial, sans-serif",
|
||||
"height": "116px",
|
||||
"width": "275px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="playlist-top draggable"
|
||||
onDoubleClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="playlist-top-left draggable"
|
||||
/>
|
||||
<div
|
||||
className="playlist-top-left-spacer draggable"
|
||||
/>
|
||||
<div
|
||||
className="playlist-top-left-fill draggable"
|
||||
/>
|
||||
<div
|
||||
className="playlist-top-title draggable"
|
||||
/>
|
||||
<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={[Function]}
|
||||
/>
|
||||
<div
|
||||
id="playlist-close-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-middle draggable"
|
||||
>
|
||||
<div
|
||||
className="playlist-middle-left draggable"
|
||||
/>
|
||||
<div
|
||||
className="playlist-middle-center"
|
||||
>
|
||||
<div
|
||||
className="playlist-tracks"
|
||||
onClick={[Function]}
|
||||
onWheel={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": "100%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="playlist-track-titles"
|
||||
/>
|
||||
<div
|
||||
className="playlist-track-durations"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-middle-right draggable"
|
||||
>
|
||||
<div
|
||||
className="rc-slider rc-slider-disabled rc-slider-vertical playlist-scrollbar"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
>
|
||||
<div
|
||||
className="rc-slider-rail"
|
||||
style={Object {}}
|
||||
/>
|
||||
<div
|
||||
className="rc-slider-track"
|
||||
style={
|
||||
Object {
|
||||
"bottom": "0%",
|
||||
"height": "0%",
|
||||
"top": "auto",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="rc-slider-step"
|
||||
/>
|
||||
<div
|
||||
className="playlist-scrollbar-handle"
|
||||
/>
|
||||
<div
|
||||
className="rc-slider-mark"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-bottom draggable"
|
||||
>
|
||||
<div
|
||||
className="playlist-bottom-left draggable"
|
||||
>
|
||||
<div
|
||||
className="playlist-menu"
|
||||
id="playlist-add-menu"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="bar"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-menu"
|
||||
id="playlist-remove-menu"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="bar"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-menu"
|
||||
id="playlist-selection-menu"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="bar"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-menu"
|
||||
id="playlist-misc-menu"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="bar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-bottom-center draggable"
|
||||
/>
|
||||
<div
|
||||
className="playlist-bottom-right draggable"
|
||||
>
|
||||
<div
|
||||
className="playlist-running-time-display draggable"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
className=" character character-48"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
<span
|
||||
className=" character character-58"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className=" character character-48"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
<span
|
||||
className=" character character-48"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
<span
|
||||
className=" character character-47"
|
||||
>
|
||||
/
|
||||
</span>
|
||||
<span
|
||||
className=" character character-48"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
<span
|
||||
className=" character character-58"
|
||||
>
|
||||
:
|
||||
</span>
|
||||
<span
|
||||
className=" character character-48"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
<span
|
||||
className=" character character-48"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-action-buttons"
|
||||
>
|
||||
<div
|
||||
className="playlist-previous-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="playlist-play-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="playlist-pause-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="playlist-stop-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="playlist-next-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
className="playlist-eject-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="mini-time countdown"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="background-character character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className="background-character character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 7,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className="background-character character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 12,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className="background-character character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className="background-character character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 25,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 7,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 12,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 20,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
<span
|
||||
className=" character character-32"
|
||||
style={
|
||||
Object {
|
||||
"left": 25,
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="playlist-menu"
|
||||
id="playlist-list-menu"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<div
|
||||
className="bar"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
id="playlist-scroll-up-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
id="playlist-scroll-down-button"
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<div
|
||||
id="playlist-resize-target"
|
||||
onMouseDown={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
import { Provider } from "react-redux";
|
||||
import renderer from "react-test-renderer";
|
||||
import getStore from "../../store";
|
||||
|
||||
import PlaylistWindow from "./index";
|
||||
const media = {
|
||||
addEventListener: jest.fn(),
|
||||
setVolume: jest.fn(),
|
||||
setPreamp: jest.fn(),
|
||||
setBalance: jest.fn(),
|
||||
getAnalyser: () => null,
|
||||
on: jest.fn(),
|
||||
};
|
||||
|
||||
describe("PlaylistWindow", () => {
|
||||
let store;
|
||||
beforeEach(() => {
|
||||
store = getStore(media);
|
||||
});
|
||||
|
||||
it("renders to snapshot", () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Provider store={store}>
|
||||
<PlaylistWindow />
|
||||
</Provider>
|
||||
)
|
||||
.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -17,7 +17,7 @@ import ListMenu from "./ListMenu";
|
|||
import PlaylistResizeTarget from "./PlaylistResizeTarget";
|
||||
import PlaylistActionArea from "./PlaylistActionArea";
|
||||
import TrackList from "./TrackList";
|
||||
import ScrollBar from "./ScrollBar";
|
||||
import PlaylistScrollBar from "./PlaylistScrollBar";
|
||||
|
||||
import "../../../css/playlist-window.css";
|
||||
import { AppState } from "../../types";
|
||||
|
|
@ -124,7 +124,7 @@ function PlaylistWindow({ analyser }: Props) {
|
|||
<TrackList />
|
||||
</div>
|
||||
<div className="playlist-middle-right draggable">
|
||||
<ScrollBar />
|
||||
<PlaylistScrollBar />
|
||||
</div>
|
||||
</div>
|
||||
<div className="playlist-bottom draggable">
|
||||
|
|
|
|||
97
packages/webamp/js/components/VerticalSlider.tsx
Normal file
97
packages/webamp/js/components/VerticalSlider.tsx
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import * as Utils from "../utils";
|
||||
import { ReactNode, useRef } from "react";
|
||||
|
||||
type Props = {
|
||||
height: number;
|
||||
width: number;
|
||||
handleHeight: number;
|
||||
value: number;
|
||||
handle: ReactNode;
|
||||
onBeforeChange?: () => void;
|
||||
onChange: (value: number) => void;
|
||||
onAfterChange?: () => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
// `<input type="range" />` can be rotated to become a vertical slider using
|
||||
// CSS, but that makes it impossible to style the handle in a pixel perfect
|
||||
// manner. Instead we reimplement it in React.
|
||||
export default function VerticalSlider({
|
||||
value,
|
||||
height,
|
||||
width,
|
||||
handle,
|
||||
handleHeight,
|
||||
onBeforeChange,
|
||||
onChange,
|
||||
onAfterChange,
|
||||
disabled,
|
||||
}: Props) {
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
const handleRef = useRef<HTMLDivElement | null>(null);
|
||||
const offset = (height - handleHeight) * value;
|
||||
|
||||
function handleMouseDown(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
const sliderNode = ref.current;
|
||||
const handleNode = handleRef.current;
|
||||
if (sliderNode == null || handleNode == null) {
|
||||
// I don't think this could ever happen in practice
|
||||
return null;
|
||||
}
|
||||
|
||||
const sliderRect = sliderNode.getBoundingClientRect();
|
||||
const handleRect = handleNode.getBoundingClientRect();
|
||||
|
||||
const { top: sliderTop, height: sliderHeight } = sliderRect;
|
||||
const { top: handleTop, height: realHandleHeight } = handleRect;
|
||||
|
||||
// If the user clicks on the handle we want them to continue to hold onto
|
||||
// that point of te handle. If they click outside of the handle (in the
|
||||
// slider itself) we want to center the handle at that point and have them
|
||||
// move the handle from the center.
|
||||
const handleOffset = handleNode.contains(e.target as HTMLElement)
|
||||
? e.clientY - handleTop
|
||||
: realHandleHeight / 2;
|
||||
|
||||
const baseOffset = sliderTop + handleOffset;
|
||||
// Measure the actual rect height rather than use the `height` prop, becuase
|
||||
// we might be in double-size mode.
|
||||
const spanSize = sliderHeight - realHandleHeight;
|
||||
|
||||
function moveToCursor(event: MouseEvent) {
|
||||
// Ensure dragging does not cause elements/text to be selected.
|
||||
// https://stackoverflow.com/a/19164149/1263117
|
||||
event.preventDefault();
|
||||
const startOffset = event.clientY - baseOffset;
|
||||
onChange(Utils.clamp(startOffset / spanSize, 0, 1));
|
||||
}
|
||||
|
||||
function handleMouseUp() {
|
||||
if (onAfterChange != null) {
|
||||
onAfterChange();
|
||||
}
|
||||
document.removeEventListener("mousemove", moveToCursor);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
}
|
||||
document.addEventListener("mousemove", moveToCursor);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
|
||||
if (onBeforeChange != null) {
|
||||
onBeforeChange();
|
||||
}
|
||||
|
||||
// Move the slider to where they've started.
|
||||
moveToCursor(e.nativeEvent);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={{ height, width }}
|
||||
onMouseDown={disabled ? undefined : handleMouseDown}
|
||||
ref={ref}
|
||||
>
|
||||
<div style={{ transform: `translateY(${offset}px)` }} ref={handleRef}>
|
||||
{handle}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -155,10 +155,10 @@ export const imageSelectors: Selectors = {
|
|||
EQ_TITLE_BAR: [".equalizer-top"],
|
||||
EQ_TITLE_BAR_SELECTED: [".selected .equalizer-top"],
|
||||
EQ_SLIDER_BACKGROUND: [".band"],
|
||||
EQ_SLIDER_THUMB: [".band .rc-slider-handle"],
|
||||
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 .rc-slider:active .rc-slider-handle"],
|
||||
EQ_SLIDER_THUMB_SELECTED: [".band:active .slider-handle"],
|
||||
EQ_ON_BUTTON: ["#on"],
|
||||
EQ_ON_BUTTON_DEPRESSED: ["#on:active"],
|
||||
EQ_ON_BUTTON_SELECTED: ["#on.selected"],
|
||||
|
|
@ -349,7 +349,7 @@ export const cursorSelectors: Selectors = {
|
|||
CLOSE: ["#title-bar #close"],
|
||||
// This is not quite right. There are some areas that show this cursor
|
||||
// but are not clickable.
|
||||
EQSLID: ["#equalizer-window .band .rc-slider"],
|
||||
EQSLID: ["#equalizer-window .band"],
|
||||
EQNORMAL: ["#equalizer-window"],
|
||||
EQCLOSE: ["#equalizer-window #equalizer-close"],
|
||||
EQTITLE: [
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@
|
|||
"@types/jszip": "^3.1.5",
|
||||
"@types/lodash": "^4.14.116",
|
||||
"@types/lodash-es": "^4.17.1",
|
||||
"@types/rc-slider": "^8.6.3",
|
||||
"@types/react": "^16.8.13",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/react-redux": "^7.1.1",
|
||||
|
|
@ -116,7 +115,6 @@
|
|||
"postcss-loader": "^3.0.0",
|
||||
"prettier": "^2.0.4",
|
||||
"puppeteer": "^1.15.0",
|
||||
"rc-slider": "^8.7.1",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-redux": "^7.2.2",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue