This commit is contained in:
Jordan Eldredge 2016-09-29 08:15:46 -07:00
parent 785af97700
commit d55f25f424
11 changed files with 203 additions and 202 deletions

View file

@ -17,13 +17,6 @@ body {
text-align: center;
}
#main-window {
margin-top: -58px;
margin-left: -137px;
top: 40%;
left: 50%;
}
.about {
position: absolute;
bottom: 0;

View file

@ -1,9 +1,4 @@
/* Rules used by all windows */
#winamp2-js {
height: 116px;
width: 275px;
}
/* Range input css reset */
#winamp2-js input[type=range]{ -webkit-appearance: none; margin: 0; padding: 0; background: none; border: none; }

View file

@ -1,106 +0,0 @@
import {cloneElement, Children, Component} from 'react';
import {clamp} from '../utils';
const SNAP_DISTANCE = 15;
// Many of the ideas of how to build this as a React componant were stolen
// from: https://github.com/mzabriskie/react-draggable Thanks! @mzabriskie
class DraggableWindow extends Component {
constructor(props) {
super(props);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.state = {};
}
handleMouseDown(e) {
if (!e.target.classList.contains(this.props.handleClass)) {
// Prevent going into drag mode when clicking any of the title
// bar's icons by making sure the click was made directly on the
// handle
return true;
}
// If the element was 'absolutely' positioned we could simply use
// offsetLeft / offsetTop however the element is 'relatively'
// positioned so we're using style.left. parseInt is used to remove the
// 'px' postfix from the value
const winStart = {
left: parseInt(this.body.offsetLeft || 0, 10),
top: parseInt(this.body.offsetTop || 0, 10)
};
const maxLeft = window.innerWidth - this.body.offsetWidth;
const maxTop = window.innerHeight - this.body.offsetHeight;
// Get starting mouse position
const mouseStart = {
left: e.clientX,
top: e.clientY
};
// Mouse move handler function while mouse is down
const handleMove = (moveEvent) => {
// Calculate difference offsets between current and starting positions
const diff = {
left: moveEvent.clientX - mouseStart.left,
top: moveEvent.clientY - mouseStart.top
};
let left = winStart.left + diff.left;
let top = winStart.top + diff.top;
// Snap to top
if (top < SNAP_DISTANCE) {
top = 0;
}
// Snap to right
if (left > maxLeft - SNAP_DISTANCE) {
left = maxLeft;
}
// Snap to left
if (left < SNAP_DISTANCE) {
left = 0;
}
// Snap to bottom
if (top > maxTop - SNAP_DISTANCE) {
top = maxTop;
}
// These margins were only useful for centering the div, now we
// don't need them
this.setState({
marginLeft: '0px',
marginTop: '0px',
left: `${left}px`,
top: `${top}px`,
position: 'absolute'
});
};
// Mouse button up
function handleUp() {
removeListeners();
}
function removeListeners() {
window.removeEventListener('mousemove', handleMove);
window.removeEventListener('mouseup', handleUp);
}
window.addEventListener('mousemove', handleMove);
window.addEventListener('mouseup', handleUp);
return false;
}
render() {
const {children} = this.props;
return cloneElement(Children.only(children), {
ref: (body) => this.body = body,
onMouseDown: this.handleMouseDown,
style: {...children.props.style, ...this.state}
});
}
}
module.exports = DraggableWindow;

View file

@ -11,7 +11,6 @@ import {
setEqToMin
} from '../actionCreators';
import DraggableWindow from './DraggableWindow.jsx';
import Band from './Band.jsx';
import EqOn from './EqOn.jsx';
import EqAuto from './EqAuto.jsx';
@ -47,40 +46,38 @@ class EqualizerWindow extends React.Component {
doubled
});
return (
<DraggableWindow handleClass='title-bar'>
<div id='equalizer-window' className={className} onClick={this.props.focusWindow}>
<div className='equalizer-top title-bar' />
<EqOn />
<EqAuto />
<EqGraph />
<div id='presets' />
<div id='equalizer-window' className={className} onClick={this.props.focusWindow}>
<div className='equalizer-top title-bar' />
<EqOn />
<EqAuto />
<EqGraph />
<div id='presets' />
<Band
id='preamp'
band='preamp'
onChange={this.props.setPreampValue(this.props.mediaPlayer)}
/>
<div
id='plus12db'
onClick={this.props.setEqToMax(this.props.mediaPlayer)}
/>
<div
id='zerodb'
onClick={this.props.setEqToMid(this.props.mediaPlayer)}
/>
<div
id='minus12db'
onClick={this.props.setEqToMin(this.props.mediaPlayer)}
/>
{BANDS.map((hertz) => (
<Band
id='preamp'
band='preamp'
onChange={this.props.setPreampValue(this.props.mediaPlayer)}
key={hertz}
id={bandClassName(hertz)}
band={hertz}
onChange={this.setHertzValue(hertz)}
/>
<div
id='plus12db'
onClick={this.props.setEqToMax(this.props.mediaPlayer)}
/>
<div
id='zerodb'
onClick={this.props.setEqToMid(this.props.mediaPlayer)}
/>
<div
id='minus12db'
onClick={this.props.setEqToMin(this.props.mediaPlayer)}
/>
{BANDS.map((hertz) => (
<Band
key={hertz}
id={bandClassName(hertz)}
band={hertz}
onChange={this.setHertzValue(hertz)}
/>
))}
</div>
</DraggableWindow>
))}
</div>
);
}
}

View file

@ -10,7 +10,6 @@ import Close from './Close.jsx';
import ClutterBar from './ClutterBar.jsx';
import ContextMenu from './ContextMenu.jsx';
import DragTarget from './DragTarget.jsx';
import DraggableWindow from './DraggableWindow.jsx';
import Eject from './Eject.jsx';
import EqToggleButton from './EqToggleButton.jsx';
import Kbps from './Kbps.jsx';
@ -34,7 +33,7 @@ export class MainWindow extends React.Component {
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
handleClick(e) {
this.props.dispatch({type: 'SET_FOCUSED_WINDOW', window: WINDOWS.MAIN});
}
@ -66,47 +65,45 @@ export class MainWindow extends React.Component {
// absolutely positioned, exists at a different location than the main
// window. Drag/Drop still work, because events propogate up to parent
// elements.
return <DragTarget handleFiles={handleDrop}>
<DraggableWindow handleClass='title-bar'>
<div id='main-window' className={className} onClick={this.handleClick}>
<div id='loading'>Loading...</div>
<div id='title-bar' className='selected title-bar'>
<ContextMenu mediaPlayer={this.props.mediaPlayer} winamp={this.props.winamp} />
<ShadeTime />
<div id='minimize' />
<Shade />
<Close mediaPlayer={this.props.mediaPlayer} />
</div>
<div className='status'>
<ClutterBar />
<div id='play-pause' />
<div id='work-indicator' className={classnames({selected: this.props.display.working})} />
<Time />
<Visualizer analyser={this.props.mediaPlayer._analyser}/>
</div>
<div className='media-info'>
<Marquee />
<Kbps />
<Khz />
<MonoStereo />
</div>
<Volume mediaPlayer={this.props.mediaPlayer} />
<Balance mediaPlayer={this.props.mediaPlayer} />
<div className='windows'>
<EqToggleButton />
<div id='playlist-button' />
</div>
<Position mediaPlayer={this.props.mediaPlayer} />
<ActionButtons mediaPlayer={this.props.mediaPlayer} />
<Eject winamp={this.props.winamp} />
<div className='shuffle-repeat'>
<Shuffle mediaPlayer={this.props.mediaPlayer} />
<Repeat mediaPlayer={this.props.mediaPlayer} />
</div>
<a id='about' target='blank' href='https://github.com/captbaritone/winamp2-js' />
return (
<div id='main-window' className={className} onClick={this.handleClick} onMouseDown={this.props.startDrag}>
<div id='loading'>Loading...</div>
<div id='title-bar' className='selected title-bar'>
<ContextMenu mediaPlayer={this.props.mediaPlayer} winamp={this.props.winamp} />
<ShadeTime />
<div id='minimize' />
<Shade />
<Close mediaPlayer={this.props.mediaPlayer} />
</div>
</DraggableWindow>
</DragTarget>;
<div className='status'>
<ClutterBar />
<div id='play-pause' />
<div id='work-indicator' className={classnames({selected: this.props.display.working})} />
<Time />
<Visualizer analyser={this.props.mediaPlayer._analyser}/>
</div>
<div className='media-info'>
<Marquee />
<Kbps />
<Khz />
<MonoStereo />
</div>
<Volume mediaPlayer={this.props.mediaPlayer} />
<Balance mediaPlayer={this.props.mediaPlayer} />
<div className='windows'>
<EqToggleButton />
<div id='playlist-button' />
</div>
<Position mediaPlayer={this.props.mediaPlayer} />
<ActionButtons mediaPlayer={this.props.mediaPlayer} />
<Eject winamp={this.props.winamp} />
<div className='shuffle-repeat'>
<Shuffle mediaPlayer={this.props.mediaPlayer} />
<Repeat mediaPlayer={this.props.mediaPlayer} />
</div>
<a id='about' target='blank' href='https://github.com/captbaritone/winamp2-js' />
</div>
);
}
}

View file

@ -1,8 +1,6 @@
import React from 'react';
import {connect} from 'react-redux';
import DraggableWindow from './DraggableWindow.jsx';
import '../../css/playlist-window.css';
const PlaylistWindow = (props) => {
@ -11,7 +9,7 @@ const PlaylistWindow = (props) => {
style.color = props.Normal;
style.backgroundColor = props.NormalBG;
}
return <DraggableWindow handleClass='title-bar'>
return (
<div id='playlist-window' className='window' style={style}>
<div className='playlist-left'>
<div className='playlist-right'>
@ -31,7 +29,7 @@ const PlaylistWindow = (props) => {
</div>
</div>
</div>
</DraggableWindow>;
);
};
module.exports = connect((state) => state.display.skinPlaylistStyle)(PlaylistWindow);

View file

@ -0,0 +1,124 @@
import React from 'react';
import {findDOMNode} from 'react-dom';
import {snapToMany, snapWithin, applySnap} from '../snapUtils';
const WINDOW_HEIGHT = 116;
const WINDOW_WIDTH = 275;
class WindowManager extends React.Component {
constructor(props) {
super(props);
const state = {};
this.validChildren().forEach((child, i) => {
state[i] = {
x: -(WINDOW_WIDTH / 2),
y: 0
};
});
this.state = state;
this.windowNodes = [];
this.getRef = this.getRef.bind(this);
this.handleMouseDown = this.handleMouseDown.bind(this);
}
getRef(node) {
this.windowNodes.push(node);
}
otherWindows(element) {
const domNodes = this.windowNodes.map(findDOMNode);
return domNodes.filter((node) => node !== element);
}
nodeInfo(node) {
const child = node.childNodes[0];
const {height, width} = child.getBoundingClientRect();
const {offsetLeft, offsetTop} = node;
return {
x: offsetLeft,
y: offsetTop,
height,
width
};
}
handleMouseDown(i, e) {
const mouseStart = {
x: e.clientX,
y: e.clientY
};
const windowStart = this.nodeInfo(e.currentTarget);
const otherWindowNodes = this.otherWindows(e.currentTarget).map(this.nodeInfo);
const browserSize = {
width: window.innerWidth,
height: window.innerHeight
};
const handleMouseMove = (ee) => {
const diff = {
x: ee.clientX - mouseStart.x,
y: ee.clientY - mouseStart.y
};
const proposedWindow = {
...windowStart,
x: windowStart.x + diff.x,
y: windowStart.y + diff.y
};
const snappedToOthers = snapToMany(proposedWindow, otherWindowNodes);
const snappedToBrowser = snapWithin(proposedWindow, browserSize);
const newPosition = applySnap(proposedWindow, snappedToBrowser, snappedToOthers);
this.setState({[i]: {top: newPosition.y, left: newPosition.x}});
};
window.addEventListener('mouseup', () => {
window.removeEventListener('mousemove', handleMouseMove);
});
window.addEventListener('mousemove', handleMouseMove);
}
validChildren() {
return React.Children.toArray(this.props.children).filter((child) => child);
}
render() {
const style = {
position: 'absolute'
};
const parentStyle = {
position: 'absolute',
width: 0,
height: 0,
top: '50vh',
left: '50vw'
};
return (
<div style={parentStyle}>
{this.validChildren().map((child, i) => {
const position = this.state[i] || {};
/* eslint-disable react/jsx-no-bind */
return (
<div
onMouseDown={(e) => this.handleMouseDown(i, e)}
ref={this.getRef}
style={{...style, ...position}}
key={i}
>
{child}
</div>
);
/* eslint-enable react/jsx-no-bind */
})}
</div>
);
}
}
module.exports = WindowManager;

View file

@ -6,6 +6,7 @@ import thunk from 'redux-thunk';
import reducer from './reducers';
import WindowManager from './components/WindowManager.jsx';
import Browser from './browser';
import MainWindow from './components/MainWindow.jsx';
import PlaylistWindow from './components/PlaylistWindow.jsx';
@ -34,9 +35,11 @@ if (new Browser(window).isCompatible) {
{/* This is not technically kosher, since <style> tags should be in
the <head>, but browsers don't really care... */}
</Skin>
<MainWindow winamp={winamp} mediaPlayer={winamp.media} />
{ playlist ? <PlaylistWindow /> : '' }
{ equalizer ? <EqualizerWindow mediaPlayer={winamp.media} /> : '' }
<WindowManager>
<MainWindow winamp={winamp} mediaPlayer={winamp.media} />
{ playlist ? <PlaylistWindow /> : '' }
{ equalizer ? <EqualizerWindow mediaPlayer={winamp.media} /> : '' }
</WindowManager>
</div>
</Provider>,
document.getElementById('winamp2-js')

View file

@ -208,7 +208,6 @@ module.exports = {
// From 0-1
setPreamp: function(value) {
console.log('preamp', value);
this._preamp.gain.value = value / 100;
},

View file

@ -25,7 +25,7 @@ const windows = (state, action) => {
if (!state) {
return {
focused: WINDOWS.MAIN,
equalizer: false
equalizer: true
};
}
switch (action.type) {

View file

@ -9,6 +9,7 @@
"serve": "webpack-dev-server",
"weight": "npm run build && gzip-size built/winamp.js | pretty-bytes",
"test": "jest && npm run lint",
"tdd": "jest --watch",
"deploy": "ssh jordaneldredge.com sh < deploy.sh"
},
"repository": {