piano/js/components/Note/Board.js

191 lines
5.7 KiB
JavaScript

/*globals React, App */
(function () {
'use strict';
/**
* Displays a noteboard from given notes
*
* @prop {notes: array} Array of notes to show
* @prop {time: int} Playback time
* @prop {length: int} Playback length
* @prop {speed: int} Playback speed
* @prop {opened: bool} Whether a file is opened
* @prop {startWheel: function} Called when wheeling starts
* @prop {setTime: function} Called to change time
* @prop {open: function} Called to load a file
* @child Note
*/
App.components.create('Note', {
displayName: 'Board',
mixins: [React.addons.PureRenderMixin],
statics: {
/**
* List of black keys
*/
black: [22, 25, 27, 30, 32, 34, 37, 39, 42, 44,
46, 49, 51, 54, 56, 58, 61, 63, 66, 68,
70, 73, 75, 78, 80, 82, 85, 87, 90, 92,
94, 97, 99, 102, 104, 106]
},
/**
* State and props config
*/
propTypes: {
notes: React.PropTypes.array.isRequired,
time: React.PropTypes.number,
length: React.PropTypes.number,
speed: React.PropTypes.number,
opened: React.PropTypes.bool,
startWheel: React.PropTypes.func,
setTime: React.PropTypes.func,
open: React.PropTypes.func,
create: React.PropTypes.func
},
getDefaultProps: function () {
return {
notes: [],
playing: false,
time: 0,
length: 0,
speed: 1,
opened: false,
startWheel: function () {},
setTime: function () {},
open: function () {},
create: function () {}
};
},
/**
* Events
*/
componentDidMount: function () {
window.addEventListener('keyup', this.keyUp);
window.addEventListener('keydown', this.keyDown);
},
componentWillUnmount: function () {
window.removeEventListener('keyup', this.keyUp);
window.removeEventListener('keydown', this.keyDown);
},
/* keyboard shortcuts */
keyDown: function (e) {
var code = e.keyCode;
// top arrow: scroll top
if (code === 38) {
this.scroll(-0.5);
e.preventDefault();
}
// bottom arrow: scroll bottom
if (code === 40) {
this.scroll(0.5);
e.preventDefault();
}
},
keyUp: function (e) {
var code = e.keyCode;
// home: go to start
if (code === 36) {
this.props.setTime(0);
e.preventDefault();
}
// end: go to end
if (code === 35) {
this.props.setTime(this.props.length - 1);
e.preventDefault();
}
},
/* scroll noteboard */
scroll: function (delta) {
var nextValue = this.props.time - delta;
if (nextValue < 0) {
nextValue = 0;
}
if (nextValue > this.props.length) {
nextValue = this.props.length;
}
this.props.setTime(nextValue);
},
/* reduce wheel amplitude */
wheel: function (e) {
this.props.startWheel();
this.scroll(e.deltaY * 0.01);
},
/* trigger opening window */
open: function () {
this.props.open('', true);
},
/**
* Render noteboard
*/
render: function () {
var children, notes,
controls = [], id = -1, className = 'noteboard';
if (this.props.opened) {
className += ' opened';
children = App.components.Note.Score({
key: 'score',
className: 'score',
notes: this.props.notes,
time: this.props.time
});
} else {
className += ' closed';
// add opening controls
controls.push(React.DOM.button({
key: 'create',
className: 'bt primary',
onClick: this.props.create
}, 'Composer'));
controls.push(React.DOM.button({
key: 'open',
className: 'bt primary',
onClick: this.open
}, 'Ouvrir un fichier'));
children = React.DOM.p({
key: 'controls',
className: 'controls'
}, [
React.DOM.img({
key: 'image',
src: 'images/logos/logo_u128.png',
alt: 'Aucun fichier ouvert'
})
].concat(controls));
}
return React.DOM.div({
className: className,
onWheel: this.wheel,
onClick: this.click
}, children);
}
});
}());