Hook up save and load buttons

These use a technique where the save/load functionality is passed from the component to its children via a function argument.  I opted for this rather than a higher order component because it is more readable and requires fewer intermediary components.

This could just as easily been done with a higher order component and would be happy to change it to that instead.

There is a performance implication since there is a function being defined in render. However the menu bar is not a highly re-rendered component, so I thought it would be ok in this case.
This commit is contained in:
Ray Schamp committed 2018-04-12 13:34:18 -04:00
1 parent bab8ddabc4
commit cde8a78e86
5 files changed
+29 -93

No files matched your search

@@ -1,3 +0,0 @@
.file-input {
display: none;
}
@@ -1,49 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import ButtonComponent from '../button/button.jsx';
import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
import styles from './load-button.css';
const LoadButtonComponent = ({
inputRef,
onChange,
onClick,
title,
...props
}) => (
<span {...props}>
<ComingSoonTooltip
place="bottom"
tooltipId="load-button"
>
<ButtonComponent
disabled
onClick={onClick}
>
{title}
</ButtonComponent>
<input
disabled
accept=".sb2,.sb3"
className={styles.fileInput}
ref={inputRef}
type="file"
onChange={onChange}
/>
</ComingSoonTooltip>
</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'
};
export default LoadButtonComponent;
+13 -2
View File
@@ -8,8 +8,10 @@ import Box from '../box/box.jsx';
import Button from '../button/button.jsx';
import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
import LanguageSelector from '../../containers/language-selector.jsx';
import LoadButton from '../../containers/load-button.jsx';
import Menu from '../../containers/menu.jsx';
import {MenuItem} from '../menu/menu.jsx';
import SaveButton from '../../containers/save-button.jsx';
import {openFeedbackForm} from '../../reducers/modals';
import {openFileMenu, closeFileMenu, MENU_FILE} from '../../reducers/menus';
@@ -117,8 +119,17 @@ const MenuBar = props => (
<MenuItemTooltip id="copy">
<MenuItem>Save as a copy</MenuItem>
</MenuItemTooltip>
<MenuItem>Upload from your computer</MenuItem>
<MenuItem>Download to your computer</MenuItem>
<LoadButton>{(renderFileInput, loadProject) => (
<MenuItem onClick={loadProject}>
Upload from your computer
{renderFileInput()}
</MenuItem>
)}</LoadButton>
<SaveButton>{saveProject => (
<MenuItem onClick={saveProject}>
Download to your computer
</MenuItem>
)}</SaveButton>
</MenuBarMenu>
</div>
<div className={classNames(styles.menuBarItem)}>
+12 -14
View File
@@ -3,8 +3,6 @@ import PropTypes from 'prop-types';
import React from 'react';
import {connect} from 'react-redux';
import LoadButtonComponent from '../components/load-button/load-button.jsx';
import {
openLoadingProject,
closeLoadingProject
@@ -14,6 +12,7 @@ class LoadButton extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'renderFileInput',
'setFileInput',
'handleChange',
'handleClick'
@@ -49,26 +48,25 @@ class LoadButton extends React.Component {
setFileInput (input) {
this.fileInput = input;
}
render () {
if (this.state.loadingError) throw new Error(`Failed to load project: ${this.state.errorMessage}`);
const {
closeLoadingState, // eslint-disable-line no-unused-vars
openLoadingState, // eslint-disable-line no-unused-vars
vm, // eslint-disable-line no-unused-vars
...props
} = this.props;
renderFileInput () {
return (
<LoadButtonComponent
inputRef={this.setFileInput}
<input
accept=".sb2,.sb3"
ref={this.setFileInput}
style={{display: 'none'}}
type="file"
onChange={this.handleChange}
onClick={this.handleClick}
{...props}
/>
);
}
render () {
if (this.state.loadingError) throw new Error(`Failed to load project: ${this.state.errorMessage}`);
return this.props.children(this.renderFileInput, this.handleClick);
}
}
LoadButton.propTypes = {
children: PropTypes.func,
closeLoadingState: PropTypes.func,
openLoadingState: PropTypes.func,
vm: PropTypes.shape({
+4 -25
View File
@@ -3,18 +3,14 @@ import PropTypes from 'prop-types';
import React from 'react';
import {connect} from 'react-redux';
import ButtonComponent from '../components/button/button.jsx';
import {ComingSoonTooltip} from '../components/coming-soon/coming-soon.jsx';
class SaveButton extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleClick'
'saveProject'
]);
}
handleClick () {
saveProject () {
const saveLink = document.createElement('a');
document.body.appendChild(saveLink);
@@ -27,7 +23,6 @@ class SaveButton extends React.Component {
// File name: project-DATE-TIME
const date = new Date();
const timestamp = `${date.toLocaleDateString()}-${date.toLocaleTimeString()}`;
// TODO change extension to sb3
saveLink.download = `untitled-project-${timestamp}.sb3`;
saveLink.click();
window.URL.revokeObjectURL(url);
@@ -35,28 +30,12 @@ class SaveButton extends React.Component {
});
}
render () {
const {
vm, // eslint-disable-line no-unused-vars
...props
} = this.props;
return (
<ComingSoonTooltip
place="bottom"
tooltipId="save-button"
>
<ButtonComponent
disabled
onClick={this.handleClick}
{...props}
>
Save
</ButtonComponent>
</ComingSoonTooltip>
);
return this.props.children(this.saveProject);
}
}
SaveButton.propTypes = {
children: PropTypes.func,
vm: PropTypes.shape({
saveProjectSb3: PropTypes.func
})