diff --git a/src/components/ContentWrapFiles.jsx b/src/components/ContentWrapFiles.jsx
index 73788f1..ec1fb80 100644
--- a/src/components/ContentWrapFiles.jsx
+++ b/src/components/ContentWrapFiles.jsx
@@ -555,6 +555,7 @@ export default class ContentWrapFiles extends Component {
onFileSelect={this.fileSelectHandler.bind(this)}
onAddFile={this.props.onAddFile}
onRemoveFile={this.props.onRemoveFile}
+ onRenameFile={this.props.onRenameFile}
/>
diff --git a/src/components/SidePane.jsx b/src/components/SidePane.jsx
index 8faa2d9..699a1bf 100644
--- a/src/components/SidePane.jsx
+++ b/src/components/SidePane.jsx
@@ -52,15 +52,37 @@ export class SidePane extends Component {
this.newFileNameInput.focus();
}, 1);
}
+ /**
+ * Checks if the passed filename already exists and if so, warns the user.
+ * Also it passes false if the validation fails.
+ * @param {string} newFileName New file name to validate
+ */
+ warnForExistingFileName(newFileName) {
+ // We also check for fileBeingRenamed !== file because when a file being renamed is
+ // asked to be set same name, then that should not match and warn here.
+ if (
+ this.props.files.some(
+ file =>
+ file.name === newFileName && this.state.fileBeingRenamed !== file
+ )
+ ) {
+ alert(`A file with name ${newFileName} already exists.`);
+ return false;
+ }
+ return true;
+ }
addFile() {
// This gets called twice when enter is pressed, because blur also fires.
if (!this.newFileNameInput) {
return;
}
- const fileName = this.newFileNameInput.value;
- if (fileName) {
- this.props.onAddFile(fileName);
+ const newFileName = this.newFileNameInput.value;
+ if (!this.warnForExistingFileName(newFileName)) {
+ return;
+ }
+ if (newFileName) {
+ this.props.onAddFile(newFileName);
}
this.setState({ isEditing: false });
}
@@ -78,6 +100,36 @@ export class SidePane extends Component {
this.props.onRemoveFile(file);
}
}
+ renameFile() {
+ // This gets called twice when enter is pressed, because blur also fires.
+ if (!this.renameFileNameInput) {
+ return;
+ }
+ const newFileName = this.renameFileNameInput.value;
+ if (!this.warnForExistingFileName(newFileName)) {
+ return;
+ }
+ if (newFileName) {
+ this.props.onRenameFile(this.state.fileBeingRenamed.name, newFileName);
+ }
+ this.setState({ fileBeingRenamed: null });
+ }
+ renameFileNameInputKeyDownHandler(e) {
+ if (e.which === ENTER_KEY) {
+ this.renameFile();
+ } else if (e.which === ESCAPE_KEY) {
+ this.setState({ fileBeingRenamed: null });
+ }
+ }
+ renameFileClickHandler(file, e) {
+ e.stopPropagation();
+ this.setState({
+ fileBeingRenamed: file
+ });
+ setTimeout(() => {
+ this.renameFileNameInput.focus();
+ }, 1);
+ }
render() {
const { files, onFileSelect, selectedFile, onRemoveFile } = this.props;
@@ -112,32 +164,57 @@ export class SidePane extends Component {
) : null}
{files.map(file => (
-
+ )}
))}
diff --git a/src/components/app.jsx b/src/components/app.jsx
index c0d23dc..ffbc561 100644
--- a/src/components/app.jsx
+++ b/src/components/app.jsx
@@ -490,7 +490,9 @@ export default class App extends Component {
isKeyboardShortcutsModalOpen: !this.state.isKeyboardShortcutsModalOpen
});
trackEvent('ui', 'showKeyboardShortcutsShortcut');
- } else if (event.keyCode === 27) {
+ } else if (event.keyCode === 27 && event.target.tagName !== 'INPUT') {
+ // We might be listening on keydown for some input inside the app. In that case
+ // we don't want this to trigger which in turn focuses back the last editor.
this.closeSavedItemsPane();
}
});
@@ -1202,6 +1204,19 @@ export default class App extends Component {
}
});
}
+ renameFileHandler(oldFileName, newFileName) {
+ this.setState({
+ currentItem: {
+ ...this.state.currentItem,
+ files: this.state.currentItem.files.map(file => {
+ if (file.name === oldFileName) {
+ return { ...file, name: newFileName };
+ }
+ return file;
+ })
+ }
+ });
+ }
render() {
return (
@@ -1236,6 +1251,7 @@ export default class App extends Component {
onSplitUpdate={this.splitUpdateHandler.bind(this)}
onAddFile={this.addFileHandler.bind(this)}
onRemoveFile={this.removeFileHandler.bind(this)}
+ onRenameFile={this.renameFileHandler.bind(this)}
/>
) : (