From 770f25a6eead8758b576161e061a7bcd272f4cb9 Mon Sep 17 00:00:00 2001 From: Kushagra Gour Date: Mon, 8 Oct 2018 16:28:17 +0530 Subject: [PATCH] add rename file support and validate duplicate file names --- src/components/ContentWrapFiles.jsx | 1 + src/components/SidePane.jsx | 133 ++++++++++++++++++++++------ src/components/app.jsx | 18 +++- src/style.css | 8 +- 4 files changed, 129 insertions(+), 31 deletions(-) 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)} /> ) : (