diff --git a/src/components/ContentWrapFiles.jsx b/src/components/ContentWrapFiles.jsx
index ca7fd2a..2292d7c 100644
--- a/src/components/ContentWrapFiles.jsx
+++ b/src/components/ContentWrapFiles.jsx
@@ -58,15 +58,16 @@ export default class ContentWrapFiles extends Component {
}
componentDidUpdate() {
const { currentItem } = this.props;
+ const linearFiles = this.linearizeFiles(currentItem.files);
// Select a new file if nothing is selected already or the selected file exists no more.
if (
currentItem &&
currentItem.files &&
(!this.state.selectedFile ||
- !currentItem.files.includes(this.state.selectedFile))
+ !linearFiles.includes(this.state.selectedFile))
) {
- this.fileSelectHandler(this.props.currentItem.files[0]);
+ this.fileSelectHandler(linearFiles[0]);
}
// HACK: becuase its a DOM manipulation
// window.logCountEl.textContent = this.logCount;
@@ -78,6 +79,18 @@ export default class ContentWrapFiles extends Component {
componentDidMount() {
this.props.onRef(this);
}
+ linearizeFiles(files) {
+ function reduceToLinearFiles(files) {
+ return files.reduce((list, currentFile) => {
+ if (currentFile.isFolder) {
+ return [...list, ...reduceToLinearFiles(currentFile.children)];
+ } else {
+ return [...list, currentFile];
+ }
+ }, []);
+ }
+ return reduceToLinearFiles(files);
+ }
getEditorOptions(fileName = '') {
let options = {
gutters: [
@@ -440,6 +453,10 @@ export default class ContentWrapFiles extends Component {
this.props.onEditorFocus(editor);
}
fileSelectHandler(file) {
+ if (file.isFolder) {
+ this.props.onFolderSelect(file);
+ return;
+ }
this.setState({
editorOptions: this.getEditorOptions(file.name),
selectedFile: file
diff --git a/src/components/SidePane.jsx b/src/components/SidePane.jsx
index 162a6b5..e5c3619 100644
--- a/src/components/SidePane.jsx
+++ b/src/components/SidePane.jsx
@@ -2,10 +2,25 @@ import { h, Component } from 'preact';
const ENTER_KEY = 13;
const ESCAPE_KEY = 27;
-function FileIcon({ fileName }) {
- if (!fileName) fileName = 'sd.sd';
-
- const type = fileName.match(/.(\w+)$/)[1];
+function FileIcon({ file }) {
+ if (file.isFolder) {
+ return (
+
+ );
+ }
+ const type = file.name.match(/.(\w+)$/)[1];
switch (type) {
case 'html':
return (
@@ -64,19 +79,95 @@ function FileIcon({ fileName }) {
/>
);
- case 'jsx':
- case 'tsx':
- return (
-
- );
}
}
+function File({
+ file,
+ selectedFile,
+ fileBeingRenamed,
+ onFileSelect,
+ onRenameBtnClick,
+ onRemoveBtnClick,
+ onNameInputBlur,
+ onNameInputKeyUp
+}) {
+ function focusInput(el) {
+ el &&
+ setTimeout(() => {
+ el.focus();
+ }, 1);
+ }
+ return (
+
+ {file === fileBeingRenamed ? (
+
+ ) : (
+
+ )}
+
+ );
+}
+function Folder(props) {
+ return (
+
+
+
+
+ );
+}
+
export class SidePane extends Component {
addFileButtonClickHandler() {
this.setState({ isEditing: true });
@@ -132,12 +223,12 @@ export class SidePane extends Component {
this.props.onRemoveFile(file);
}
}
- renameFile() {
+ renameFile(e) {
// This gets called twice when enter is pressed, because blur also fires.
- if (!this.renameFileNameInput) {
+ if (!e.target || !this.state.fileBeingRenamed) {
return;
}
- const newFileName = this.renameFileNameInput.value;
+ const newFileName = e.target.value;
if (!this.warnForExistingFileName(newFileName)) {
return;
}
@@ -146,9 +237,9 @@ export class SidePane extends Component {
}
this.setState({ fileBeingRenamed: null });
}
- renameFileNameInputKeyDownHandler(e) {
+ renameFileNameInputKeyUpHandler(e) {
if (e.which === ENTER_KEY) {
- this.renameFile();
+ this.renameFile(e);
} else if (e.which === ESCAPE_KEY) {
this.setState({ fileBeingRenamed: null });
}
@@ -158,12 +249,16 @@ export class SidePane extends Component {
this.setState({
fileBeingRenamed: file
});
- setTimeout(() => {
- this.renameFileNameInput.focus();
- }, 1);
}
render() {
const { files, onFileSelect, selectedFile, onRemoveFile } = this.props;
+ const moreProps = {
+ onRemoveBtnClick: this.removeFileClickHandler.bind(this),
+ onRenameBtnClick: this.renameFileClickHandler.bind(this),
+ onNameInputBlur: this.renameFile.bind(this),
+ onNameInputKeyUp: this.renameFileNameInputKeyUpHandler.bind(this),
+ fileBeingRenamed: this.state.fileBeingRenamed
+ };
return (
) : null}
- {files.map(file => (
-
- {file === this.state.fileBeingRenamed ? (
- (this.renameFileNameInput = el)}
- value={file.name}
- onBlur={this.renameFile.bind(this)}
- onKeyUpCapture={this.renameFileNameInputKeyDownHandler.bind(
- this
- )}
- />
+ {files.map(
+ file =>
+ file.isFolder ? (
+
) : (
-
- )}
-
- ))}
+
+ )
+ )}
);
}
diff --git a/src/components/app.jsx b/src/components/app.jsx
index ffbc561..c52f7ee 100644
--- a/src/components/app.jsx
+++ b/src/components/app.jsx
@@ -285,7 +285,11 @@ export default class App extends Component {
...item,
files: [
{ name: 'index.html', content: '' },
- { name: 'style.css', content: '' },
+ {
+ name: 'styles',
+ isFolder: true,
+ children: [{ name: 'style.css', content: '' }]
+ },
{ name: 'script.js', content: '' }
]
};
@@ -1193,6 +1197,7 @@ export default class App extends Component {
]
}
});
+ console.log(11, this.state.currentItem);
}
removeFileHandler(fileToRemove) {
this.setState({
@@ -1218,6 +1223,20 @@ export default class App extends Component {
});
}
+ folderSelectHandler(folder) {
+ this.setState({
+ currentItem: {
+ ...this.state.currentItem,
+ files: this.state.currentItem.files.map(file => {
+ if (file === folder) {
+ return { ...file, isCollapsed: !folder.isCollapsed };
+ }
+ return file;
+ })
+ }
+ });
+ }
+
render() {
return (
@@ -1252,6 +1271,7 @@ export default class App extends Component {
onAddFile={this.addFileHandler.bind(this)}
onRemoveFile={this.removeFileHandler.bind(this)}
onRenameFile={this.renameFileHandler.bind(this)}
+ onFolderSelect={this.folderSelectHandler.bind(this)}
/>
) : (
div > .sidebar__file {
+ padding-left: 1rem;
+}
+
.sidebar__file:hover,
.sidebar__file:focus {
background-color: rgba(255, 255, 255, 0.05);
@@ -1653,6 +1657,10 @@ body:not(.is-app) .show-when-app {
visibility: visible;
}
+.sidebar__folder-wrap[data-collapsed='true'] {
+ display: none;
+}
+
.is-project .hide-in-file-mode {
display: none !important;
}