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:
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;
|
||||
@@ -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)}>
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user