diff --git a/src/components/button/button.css b/src/components/button/button.css new file mode 100644 index 0000000000000000000000000000000000000000..44243650f970d16aa787b8a44da3763e595802ce --- /dev/null +++ b/src/components/button/button.css @@ -0,0 +1,3 @@ +.button { + cursor: pointer; +} diff --git a/src/components/button/button.jsx b/src/components/button/button.jsx new file mode 100644 index 0000000000000000000000000000000000000000..34b485154ad168f05bb2542d15b9dcdfc5f387e1 --- /dev/null +++ b/src/components/button/button.jsx @@ -0,0 +1,30 @@ +const classNames = require('classnames'); +const PropTypes = require('prop-types'); +const React = require('react'); + +const styles = require('./button.css'); + +const ButtonComponent = ({ + className, + onClick, + children, + ...props +}) => ( + <span + className={classNames( + styles.button, + className + )} + onClick={onClick} + {...props} + > + {children} + </span> +); + +ButtonComponent.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + onClick: PropTypes.func.isRequired +}; +module.exports = ButtonComponent; diff --git a/src/components/load-button/load-button.css b/src/components/load-button/load-button.css new file mode 100644 index 0000000000000000000000000000000000000000..527718028aedbf6ec6636afe0df0625e2005c35c --- /dev/null +++ b/src/components/load-button/load-button.css @@ -0,0 +1,3 @@ +.file-input { + display: none; +} diff --git a/src/components/load-button/load-button.jsx b/src/components/load-button/load-button.jsx new file mode 100644 index 0000000000000000000000000000000000000000..29b2962e5a6f2b6642c80ac9449f458f2ea70f85 --- /dev/null +++ b/src/components/load-button/load-button.jsx @@ -0,0 +1,36 @@ +const PropTypes = require('prop-types'); +const React = require('react'); + +const ButtonComponent = require('../button/button.jsx'); + +const styles = require('./load-button.css'); + +const LoadButtonComponent = ({ + inputRef, + onChange, + onClick, + title, + ...props +}) => ( + <span {...props}> + <ButtonComponent onClick={onClick}>{title}</ButtonComponent> + <input + className={styles.fileInput} + ref={inputRef} + type="file" + onChange={onChange} + /> + </span> +); + +LoadButtonComponent.propTypes = { + className: PropTypes.string, + inputRef: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + onClick: PropTypes.func.isRequired, + title: PropTypes.string +}; +LoadButtonComponent.defaultProps = { + title: 'Load' +}; +module.exports = LoadButtonComponent; diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index b34a2e1900a937ecba22f8d9944f48563136223f..4397b5002ea47789ed47cd3283a6ba917c24373d 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -2,6 +2,9 @@ const classNames = require('classnames'); const React = require('react'); const Box = require('../box/box.jsx'); +const LoadButton = require('../../containers/load-button.jsx'); +const SaveButton = require('../../containers/save-button.jsx'); + const styles = require('./menu-bar.css'); const scratchLogo = require('./scratch-logo.svg'); @@ -18,7 +21,8 @@ const MenuBar = function MenuBar () { src={scratchLogo} /> </div> - <div className={styles.menuItem} >Animation Playtest Prototype</div> + <SaveButton className={styles.menuItem} /> + <LoadButton className={styles.menuItem} /> </Box> ); }; diff --git a/src/containers/load-button.jsx b/src/containers/load-button.jsx new file mode 100644 index 0000000000000000000000000000000000000000..dcbd4fe016094c04c940428758c6c858b682deb0 --- /dev/null +++ b/src/containers/load-button.jsx @@ -0,0 +1,55 @@ +const bindAll = require('lodash.bindall'); +const PropTypes = require('prop-types'); +const React = require('react'); +const {connect} = require('react-redux'); + +const LoadButtonComponent = require('../components/load-button/load-button.jsx'); + +class LoadButton extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'setFileInput', + 'handleChange', + 'handleClick' + ]); + } + handleChange (e) { + const reader = new FileReader(); + reader.onload = () => this.props.loadProject(reader.result); + reader.readAsText(e.target.files[0]); + } + handleClick () { + this.fileInput.click(); + } + setFileInput (input) { + this.fileInput = input; + } + render () { + const { + loadProject, // eslint-disable-line no-unused-vars + ...props + } = this.props; + return ( + <LoadButtonComponent + inputRef={this.setFileInput} + onChange={this.handleChange} + onClick={this.handleClick} + {...props} + /> + ); + } +} + +LoadButton.propTypes = { + loadProject: PropTypes.func.isRequired +}; + +const mapStateToProps = state => ({ + loadProject: state.vm.fromJSON.bind(state.vm) +}); + +module.exports = connect( + mapStateToProps, + () => ({}) // omit dispatch prop +)(LoadButton); diff --git a/src/containers/save-button.jsx b/src/containers/save-button.jsx new file mode 100644 index 0000000000000000000000000000000000000000..f5e7ab018681e8008171ac64e8964db3e77a59ec --- /dev/null +++ b/src/containers/save-button.jsx @@ -0,0 +1,62 @@ +const bindAll = require('lodash.bindall'); +const PropTypes = require('prop-types'); +const React = require('react'); +const {connect} = require('react-redux'); + +const ButtonComponent = require('../components/button/button.jsx'); + +class SaveButton extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleClick' + ]); + } + handleClick () { + const json = this.props.saveProjectSb3(); + + // Download project data into a file - create link element, + // simulate click on it, and then remove it. + const saveLink = document.createElement('a'); + document.body.appendChild(saveLink); + + const data = new Blob([json], {type: 'text'}); + const url = window.URL.createObjectURL(data); + saveLink.href = url; + + // File name: project-DATE-TIME + const date = new Date(); + const timestamp = `${date.toLocaleDateString()}-${date.toLocaleTimeString()}`; + saveLink.download = `project-${timestamp}.json`; + saveLink.click(); + window.URL.revokeObjectURL(url); + document.body.removeChild(saveLink); + } + render () { + const { + saveProjectSb3, // eslint-disable-line no-unused-vars + ...props + } = this.props; + return ( + <ButtonComponent + onClick={this.handleClick} + {...props} + > + Save + </ButtonComponent> + ); + } +} + +SaveButton.propTypes = { + saveProjectSb3: PropTypes.func.isRequired +}; + +const mapStateToProps = state => ({ + saveProjectSb3: state.vm.saveProjectSb3.bind(state.vm) +}); + +module.exports = connect( + mapStateToProps, + () => ({}) // omit dispatch prop +)(SaveButton);