mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-17 20:11:12 +02:00
add rename file support and validate duplicate file names
This commit is contained in:
@@ -555,6 +555,7 @@ export default class ContentWrapFiles extends Component {
|
|||||||
onFileSelect={this.fileSelectHandler.bind(this)}
|
onFileSelect={this.fileSelectHandler.bind(this)}
|
||||||
onAddFile={this.props.onAddFile}
|
onAddFile={this.props.onAddFile}
|
||||||
onRemoveFile={this.props.onRemoveFile}
|
onRemoveFile={this.props.onRemoveFile}
|
||||||
|
onRenameFile={this.props.onRenameFile}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="code-side" id="js-code-side">
|
<div class="code-side" id="js-code-side">
|
||||||
|
@@ -52,15 +52,37 @@ export class SidePane extends Component {
|
|||||||
this.newFileNameInput.focus();
|
this.newFileNameInput.focus();
|
||||||
}, 1);
|
}, 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() {
|
addFile() {
|
||||||
// This gets called twice when enter is pressed, because blur also fires.
|
// This gets called twice when enter is pressed, because blur also fires.
|
||||||
if (!this.newFileNameInput) {
|
if (!this.newFileNameInput) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fileName = this.newFileNameInput.value;
|
const newFileName = this.newFileNameInput.value;
|
||||||
if (fileName) {
|
if (!this.warnForExistingFileName(newFileName)) {
|
||||||
this.props.onAddFile(fileName);
|
return;
|
||||||
|
}
|
||||||
|
if (newFileName) {
|
||||||
|
this.props.onAddFile(newFileName);
|
||||||
}
|
}
|
||||||
this.setState({ isEditing: false });
|
this.setState({ isEditing: false });
|
||||||
}
|
}
|
||||||
@@ -78,6 +100,36 @@ export class SidePane extends Component {
|
|||||||
this.props.onRemoveFile(file);
|
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() {
|
render() {
|
||||||
const { files, onFileSelect, selectedFile, onRemoveFile } = this.props;
|
const { files, onFileSelect, selectedFile, onRemoveFile } = this.props;
|
||||||
|
|
||||||
@@ -112,6 +164,17 @@ export class SidePane extends Component {
|
|||||||
) : null}
|
) : null}
|
||||||
{files.map(file => (
|
{files.map(file => (
|
||||||
<div>
|
<div>
|
||||||
|
{file === this.state.fileBeingRenamed ? (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ref={el => (this.renameFileNameInput = el)}
|
||||||
|
value={file.name}
|
||||||
|
onBlur={this.renameFile.bind(this)}
|
||||||
|
onKeyUpCapture={this.renameFileNameInputKeyDownHandler.bind(
|
||||||
|
this
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<button
|
<button
|
||||||
class={`sidebar__file ${
|
class={`sidebar__file ${
|
||||||
selectedFile === file ? 'selected' : ''
|
selectedFile === file ? 'selected' : ''
|
||||||
@@ -124,6 +187,19 @@ export class SidePane extends Component {
|
|||||||
{file.name}
|
{file.name}
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar__file-options">
|
<div class="sidebar__file-options">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn--dark"
|
||||||
|
onClick={this.renameFileClickHandler.bind(this, file)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
style="vertical-align:middle;width:14px;height:14px"
|
||||||
|
>
|
||||||
|
<path d="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn--dark"
|
class="btn btn--dark"
|
||||||
@@ -138,6 +214,7 @@ export class SidePane extends Component {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -490,7 +490,9 @@ export default class App extends Component {
|
|||||||
isKeyboardShortcutsModalOpen: !this.state.isKeyboardShortcutsModalOpen
|
isKeyboardShortcutsModalOpen: !this.state.isKeyboardShortcutsModalOpen
|
||||||
});
|
});
|
||||||
trackEvent('ui', 'showKeyboardShortcutsShortcut');
|
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();
|
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() {
|
render() {
|
||||||
return (
|
return (
|
||||||
@@ -1236,6 +1251,7 @@ export default class App extends Component {
|
|||||||
onSplitUpdate={this.splitUpdateHandler.bind(this)}
|
onSplitUpdate={this.splitUpdateHandler.bind(this)}
|
||||||
onAddFile={this.addFileHandler.bind(this)}
|
onAddFile={this.addFileHandler.bind(this)}
|
||||||
onRemoveFile={this.removeFileHandler.bind(this)}
|
onRemoveFile={this.removeFileHandler.bind(this)}
|
||||||
|
onRenameFile={this.renameFileHandler.bind(this)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ContentWrap
|
<ContentWrap
|
||||||
|
@@ -1621,9 +1621,10 @@ body:not(.is-app) .show-when-app {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 2px 4px;
|
padding: 5px 4px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.sidebar__file.selected {
|
.sidebar__file.selected {
|
||||||
color: white;
|
color: white;
|
||||||
@@ -1636,10 +1637,13 @@ body:not(.is-app) .show-when-app {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
.sidebar__file-options {
|
.sidebar__file-options {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
.sidebar__file:hover .sidebar__file-options,
|
.sidebar__file:hover .sidebar__file-options,
|
||||||
.sidebar__file:focus .sidebar__file-options {
|
.sidebar__file:focus .sidebar__file-options,
|
||||||
|
.sidebar__file:focus-within .sidebar__file-options {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user