mirror of
https://github.com/chinchang/web-maker.git
synced 2025-05-13 22:05:21 +02:00
new file template n add file support
This commit is contained in:
parent
e0ae68ac2c
commit
799ae55abd
@ -8,35 +8,11 @@ import { trackEvent } from '../analytics';
|
|||||||
import CodeMirror from '../CodeMirror';
|
import CodeMirror from '../CodeMirror';
|
||||||
import CodeMirrorBox from './CodeMirrorBox';
|
import CodeMirrorBox from './CodeMirrorBox';
|
||||||
import { deferred } from '../deferred';
|
import { deferred } from '../deferred';
|
||||||
|
import { SidePane } from './SidePane';
|
||||||
const minCodeWrapSize = 33;
|
const minCodeWrapSize = 33;
|
||||||
|
|
||||||
/* global htmlCodeEl, jsCodeEl, cssCodeEl, logCountEl
|
/* global htmlCodeEl, jsCodeEl, cssCodeEl, logCountEl
|
||||||
*/
|
*/
|
||||||
class Sidebar extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div class="sidebar">
|
|
||||||
Sidebar
|
|
||||||
{this.props.files.map(file => (
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="sidebar__file"
|
|
||||||
type="button"
|
|
||||||
onClick={this.props.onFileSelect.bind(null, file)}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="http://icons-for-free.com/icon/download-css_html_html5_icon-498356.png"
|
|
||||||
width="16"
|
|
||||||
/>
|
|
||||||
{file.name}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ContentWrap2 extends Component {
|
export default class ContentWrap2 extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -66,6 +42,15 @@ export default class ContentWrap2 extends Component {
|
|||||||
this.props.prefs !== nextProps.prefs
|
this.props.prefs !== nextProps.prefs
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
componentWillUpdate(nextProps) {
|
||||||
|
if (
|
||||||
|
this.props.currentItem.createdOn !== nextProps.currentItem.createdOn ||
|
||||||
|
this.props.currentItem.id !== nextProps.currentItem.id
|
||||||
|
) {
|
||||||
|
this.fileBuffers = {};
|
||||||
|
this.state.selectedFile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (
|
if (
|
||||||
this.props.currentItem &&
|
this.props.currentItem &&
|
||||||
@ -97,7 +82,7 @@ export default class ContentWrap2 extends Component {
|
|||||||
mode = modes[HtmlModes.HTML].cmMode;
|
mode = modes[HtmlModes.HTML].cmMode;
|
||||||
}
|
}
|
||||||
console.log('mode', mode);
|
console.log('mode', mode);
|
||||||
this.fileBuffers[file.name] = CodeMirror.Doc(file.content, mode);
|
this.fileBuffers[file.name] = CodeMirror.Doc(file.content || '', mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
onHtmlCodeChange(editor, change) {
|
onHtmlCodeChange(editor, change) {
|
||||||
@ -195,38 +180,12 @@ export default class ContentWrap2 extends Component {
|
|||||||
? this.detachedWindow.document.querySelector('iframe')
|
? this.detachedWindow.document.querySelector('iframe')
|
||||||
: this.frame;
|
: this.frame;
|
||||||
|
|
||||||
const cssMode = this.props.currentItem.cssMode;
|
this.createPreviewFile();
|
||||||
var htmlPromise = computeHtml(
|
// result.forEach(resultItem => {
|
||||||
currentCode.html,
|
// if (resultItem.errors) {
|
||||||
this.props.currentItem.htmlMode
|
// this.showErrors(resultItem.errors.lang, resultItem.errors.data);
|
||||||
);
|
// }
|
||||||
var cssPromise = computeCss(
|
// });
|
||||||
cssMode === CssModes.ACSS ? currentCode.html : currentCode.css,
|
|
||||||
cssMode,
|
|
||||||
this.props.currentItem.cssSettings
|
|
||||||
);
|
|
||||||
var jsPromise = computeJs(
|
|
||||||
currentCode.js,
|
|
||||||
this.props.currentItem.jsMode,
|
|
||||||
true,
|
|
||||||
this.props.prefs.infiniteLoopTimeout
|
|
||||||
);
|
|
||||||
Promise.all([htmlPromise, cssPromise, jsPromise]).then(result => {
|
|
||||||
/* if (cssMode === CssModes.ACSS) {
|
|
||||||
this.cm.css.setValue(result[1].code || '');
|
|
||||||
} */
|
|
||||||
|
|
||||||
this.createPreviewFile(
|
|
||||||
result[0].code || '',
|
|
||||||
result[1].code || '',
|
|
||||||
result[2].code || ''
|
|
||||||
);
|
|
||||||
result.forEach(resultItem => {
|
|
||||||
if (resultItem.errors) {
|
|
||||||
this.showErrors(resultItem.errors.lang, resultItem.errors.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.codeInPreview.html = currentCode.html;
|
this.codeInPreview.html = currentCode.html;
|
||||||
this.codeInPreview.css = currentCode.css;
|
this.codeInPreview.css = currentCode.css;
|
||||||
@ -406,12 +365,12 @@ export default class ContentWrap2 extends Component {
|
|||||||
updateHtmlMode(value) {
|
updateHtmlMode(value) {
|
||||||
// this.props.onCodeModeChange('html', value);
|
// this.props.onCodeModeChange('html', value);
|
||||||
// this.props.currentItem.htmlMode = value;
|
// this.props.currentItem.htmlMode = value;
|
||||||
this.cm.setOption('mode', modes[value].cmMode);
|
// this.cm.setOption('mode', modes[value].cmMode);
|
||||||
CodeMirror.autoLoadMode(
|
// CodeMirror.autoLoadMode(
|
||||||
this.cm,
|
// this.cm,
|
||||||
modes[value].cmPath || modes[value].cmMode
|
// modes[value].cmPath || modes[value].cmMode
|
||||||
);
|
// );
|
||||||
return this.handleModeRequirements(value);
|
// return this.handleModeRequirements(value);
|
||||||
}
|
}
|
||||||
updateCssMode(value) {
|
updateCssMode(value) {
|
||||||
// this.props.onCodeModeChange('css', value);
|
// this.props.onCodeModeChange('css', value);
|
||||||
@ -474,9 +433,11 @@ export default class ContentWrap2 extends Component {
|
|||||||
onDragEnd={this.mainSplitDragEndHandler.bind(this)}
|
onDragEnd={this.mainSplitDragEndHandler.bind(this)}
|
||||||
>
|
>
|
||||||
<div id="js-sidebar">
|
<div id="js-sidebar">
|
||||||
<Sidebar
|
<SidePane
|
||||||
files={this.props.currentItem.files || []}
|
files={this.props.currentItem.files || []}
|
||||||
|
selectedFile={this.state.selectedFile}
|
||||||
onFileSelect={this.fileSelectHandler.bind(this)}
|
onFileSelect={this.fileSelectHandler.bind(this)}
|
||||||
|
onAddFile={this.props.onAddFile}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="code-side" id="js-code-side">
|
<div class="code-side" id="js-code-side">
|
||||||
|
@ -7,6 +7,7 @@ export function CreateNewModal({
|
|||||||
show,
|
show,
|
||||||
closeHandler,
|
closeHandler,
|
||||||
onBlankTemplateSelect,
|
onBlankTemplateSelect,
|
||||||
|
onBlankFileTemplateSelect,
|
||||||
onTemplateSelect
|
onTemplateSelect
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
@ -15,6 +16,9 @@ export function CreateNewModal({
|
|||||||
<button className="btn" onClick={onBlankTemplateSelect}>
|
<button className="btn" onClick={onBlankTemplateSelect}>
|
||||||
Start a blank creation
|
Start a blank creation
|
||||||
</button>
|
</button>
|
||||||
|
<button className="btn" onClick={onBlankFileTemplateSelect}>
|
||||||
|
Start a blank files creation
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
Or choose from a template:
|
Or choose from a template:
|
||||||
|
116
src/components/SidePane.jsx
Normal file
116
src/components/SidePane.jsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
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];
|
||||||
|
console.log(type);
|
||||||
|
switch (type) {
|
||||||
|
case 'html':
|
||||||
|
return (
|
||||||
|
<svg class="sidebar__file-icon" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="rgb(225, 187, 21)"
|
||||||
|
d="M12,17.56L16.07,16.43L16.62,10.33H9.38L9.2,8.3H16.8L17,6.31H7L7.56,12.32H14.45L14.22,14.9L12,15.5L9.78,14.9L9.64,13.24H7.64L7.93,16.43L12,17.56M4.07,3H19.93L18.5,19.2L12,21L5.5,19.2L4.07,3Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'js':
|
||||||
|
return (
|
||||||
|
<svg class="sidebar__file-icon" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="rgb(255, 165, 0)"
|
||||||
|
d="M3,3H21V21H3V3M7.73,18.04C8.13,18.89 8.92,19.59 10.27,19.59C11.77,19.59 12.8,18.79 12.8,17.04V11.26H11.1V17C11.1,17.86 10.75,18.08 10.2,18.08C9.62,18.08 9.38,17.68 9.11,17.21L7.73,18.04M13.71,17.86C14.21,18.84 15.22,19.59 16.8,19.59C18.4,19.59 19.6,18.76 19.6,17.23C19.6,15.82 18.79,15.19 17.35,14.57L16.93,14.39C16.2,14.08 15.89,13.87 15.89,13.37C15.89,12.96 16.2,12.64 16.7,12.64C17.18,12.64 17.5,12.85 17.79,13.37L19.1,12.5C18.55,11.54 17.77,11.17 16.7,11.17C15.19,11.17 14.22,12.13 14.22,13.4C14.22,14.78 15.03,15.43 16.25,15.95L16.67,16.13C17.45,16.47 17.91,16.68 17.91,17.26C17.91,17.74 17.46,18.09 16.76,18.09C15.93,18.09 15.45,17.66 15.09,17.06L13.71,17.86Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'css':
|
||||||
|
return (
|
||||||
|
<svg class="sidebar__file-icon" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="rgb(95, 158, 160)"
|
||||||
|
d="M5,3L4.35,6.34H17.94L17.5,8.5H3.92L3.26,11.83H16.85L16.09,15.64L10.61,17.45L5.86,15.64L6.19,14H2.85L2.06,18L9.91,21L18.96,18L20.16,11.97L20.4,10.76L21.94,3H5Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SidePane extends Component {
|
||||||
|
addFileButtonClickHandler() {
|
||||||
|
this.setState({ isEditing: true });
|
||||||
|
setTimeout(() => {
|
||||||
|
this.newFileNameInput.focus();
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
this.setState({ isEditing: false });
|
||||||
|
}
|
||||||
|
newFileNameInputKeyDownHandler(e) {
|
||||||
|
if (e.which === ENTER_KEY) {
|
||||||
|
this.addFile();
|
||||||
|
} else if (e.which === ESCAPE_KEY) {
|
||||||
|
this.setState({ isEditing: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { files, onFileSelect, selectedFile } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="flex jc-sb">
|
||||||
|
Files
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn--dark"
|
||||||
|
onClick={this.addFileButtonClickHandler.bind(this)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
style="vertical-align:middle;width:14px;height:14px"
|
||||||
|
>
|
||||||
|
<path d="M13,9H18.5L13,3.5V9M6,2H14L20,8V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V4C4,2.89 4.89,2 6,2M11,15V12H9V15H6V17H9V20H11V17H14V15H11Z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{this.state.isEditing ? (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
ref={el => (this.newFileNameInput = el)}
|
||||||
|
onBlur={this.addFile.bind(this)}
|
||||||
|
onKeyUp={this.newFileNameInputKeyDownHandler.bind(this)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{files.map(file => (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class={`sidebar__file ${
|
||||||
|
selectedFile === file ? 'selected' : ''
|
||||||
|
}`}
|
||||||
|
type="button"
|
||||||
|
onClick={onFileSelect.bind(null, file)}
|
||||||
|
>
|
||||||
|
<FileIcon fileName={file.name} />
|
||||||
|
{file.name}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -264,7 +264,7 @@ export default class App extends Component {
|
|||||||
alertsService.add(`"${sourceItem.title}" was forked`);
|
alertsService.add(`"${sourceItem.title}" was forked`);
|
||||||
trackEvent('fn', 'itemForked');
|
trackEvent('fn', 'itemForked');
|
||||||
}
|
}
|
||||||
createNewItem(isProject = true) {
|
createNewItem(isFileMode = false) {
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
let item = {
|
let item = {
|
||||||
title:
|
title:
|
||||||
@ -275,15 +275,17 @@ export default class App extends Component {
|
|||||||
'-' +
|
'-' +
|
||||||
d.getHours() +
|
d.getHours() +
|
||||||
':' +
|
':' +
|
||||||
d.getMinutes()
|
d.getMinutes(),
|
||||||
|
createdOn: +d,
|
||||||
|
content: ''
|
||||||
};
|
};
|
||||||
if (isProject) {
|
if (isFileMode) {
|
||||||
item = {
|
item = {
|
||||||
...item,
|
...item,
|
||||||
files: [
|
files: [
|
||||||
{ name: 'index.html' },
|
{ name: 'index.html', content: '' },
|
||||||
{ name: 'style.css' },
|
{ name: 'style.css', content: '' },
|
||||||
{ name: 'script.js' }
|
{ name: 'script.js', content: '' }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -1165,6 +1167,10 @@ export default class App extends Component {
|
|||||||
this.createNewItem();
|
this.createNewItem();
|
||||||
this.setState({ isCreateNewModalOpen: false });
|
this.setState({ isCreateNewModalOpen: false });
|
||||||
}
|
}
|
||||||
|
blankFileTemplateSelectHandler() {
|
||||||
|
this.createNewItem(true);
|
||||||
|
this.setState({ isCreateNewModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
templateSelectHandler(template) {
|
templateSelectHandler(template) {
|
||||||
fetch(`templates/template-${template.id}.json`)
|
fetch(`templates/template-${template.id}.json`)
|
||||||
@ -1174,6 +1180,17 @@ export default class App extends Component {
|
|||||||
});
|
});
|
||||||
this.setState({ isCreateNewModalOpen: false });
|
this.setState({ isCreateNewModalOpen: false });
|
||||||
}
|
}
|
||||||
|
addFileHandler(fileName) {
|
||||||
|
this.setState({
|
||||||
|
currentItem: {
|
||||||
|
...this.state.currentItem,
|
||||||
|
files: [
|
||||||
|
...this.state.currentItem.files,
|
||||||
|
{ name: fileName, content: '' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
@ -1204,6 +1221,7 @@ export default class App extends Component {
|
|||||||
prefs={this.state.prefs}
|
prefs={this.state.prefs}
|
||||||
onEditorFocus={this.editorFocusHandler.bind(this)}
|
onEditorFocus={this.editorFocusHandler.bind(this)}
|
||||||
onSplitUpdate={this.splitUpdateHandler.bind(this)}
|
onSplitUpdate={this.splitUpdateHandler.bind(this)}
|
||||||
|
onAddFile={this.addFileHandler.bind(this)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Footer
|
<Footer
|
||||||
@ -1361,6 +1379,9 @@ export default class App extends Component {
|
|||||||
show={this.state.isCreateNewModalOpen}
|
show={this.state.isCreateNewModalOpen}
|
||||||
closeHandler={() => this.setState({ isCreateNewModalOpen: false })}
|
closeHandler={() => this.setState({ isCreateNewModalOpen: false })}
|
||||||
onBlankTemplateSelect={this.blankTemplateSelectHandler.bind(this)}
|
onBlankTemplateSelect={this.blankTemplateSelectHandler.bind(this)}
|
||||||
|
onBlankFileTemplateSelect={this.blankFileTemplateSelectHandler.bind(
|
||||||
|
this
|
||||||
|
)}
|
||||||
onTemplateSelect={this.templateSelectHandler.bind(this)}
|
onTemplateSelect={this.templateSelectHandler.bind(this)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ textarea {
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
[role='button'] {
|
[role='button'] {
|
||||||
@ -96,6 +97,9 @@ button {
|
|||||||
.flex-shrink-0 {
|
.flex-shrink-0 {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
.jc-sb {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.fr {
|
.fr {
|
||||||
float: right;
|
float: right;
|
||||||
@ -1605,9 +1609,23 @@ body:not(.is-app) .show-when-app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar__file {
|
.sidebar__file {
|
||||||
color: white;
|
color: #f3f3f3;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.sidebar__file.selected {
|
||||||
|
color: white;
|
||||||
|
background-color: mediumblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar__file-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
@media screen and (max-width: 600px) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user