mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-17 20:11:12 +02:00
add support for templates
This commit is contained in:
@@ -934,6 +934,7 @@ export default class ContentWrap extends Component {
|
|||||||
<use xlinkHref="#chevron-icon" />
|
<use xlinkHref="#chevron-icon" />
|
||||||
</svg>
|
</svg>
|
||||||
<input
|
<input
|
||||||
|
tabIndex={this.state.isConsoleOpen ? 0 : -1}
|
||||||
onKeyUp={this.evalConsoleExpr.bind(this)}
|
onKeyUp={this.evalConsoleExpr.bind(this)}
|
||||||
class="console-exec-input"
|
class="console-exec-input"
|
||||||
/>
|
/>
|
||||||
|
32
src/components/CreateNewModal.jsx
Normal file
32
src/components/CreateNewModal.jsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { h } from 'preact';
|
||||||
|
import Modal from './Modal';
|
||||||
|
import { ItemTile } from './ItemTile';
|
||||||
|
import templates from '../templateList';
|
||||||
|
|
||||||
|
export function CreateNewModal({
|
||||||
|
show,
|
||||||
|
closeHandler,
|
||||||
|
onBlankTemplateSelect,
|
||||||
|
onTemplateSelect
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Modal show={show} closeHandler={closeHandler} smll>
|
||||||
|
<div class="tac">
|
||||||
|
<button className="btn" onClick={onBlankTemplateSelect}>
|
||||||
|
Start a blank creation
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
Or choose from a template:
|
||||||
|
<div class="saved-items-pane__container">
|
||||||
|
{templates.map(template => (
|
||||||
|
<ItemTile
|
||||||
|
item={template}
|
||||||
|
focusable
|
||||||
|
onClick={onTemplateSelect.bind(null, template)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
60
src/components/ItemTile.jsx
Normal file
60
src/components/ItemTile.jsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { h } from 'preact';
|
||||||
|
import { getHumanDate } from '../utils';
|
||||||
|
import Modal from './Modal';
|
||||||
|
|
||||||
|
export function ItemTile({
|
||||||
|
item,
|
||||||
|
onClick,
|
||||||
|
itemForkBtnClick,
|
||||||
|
itemRemoveBtnClick,
|
||||||
|
focusable
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role={focusable ? 'button' : null}
|
||||||
|
tabindex={focusable ? 0 : null}
|
||||||
|
class="js-saved-item-tile saved-item-tile"
|
||||||
|
data-item-id={item.id}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<div class="saved-item-tile__btns">
|
||||||
|
{itemForkBtnClick ? (
|
||||||
|
<a
|
||||||
|
class="js-saved-item-tile__fork-btn saved-item-tile__btn hint--left hint--medium"
|
||||||
|
aria-label="Creates a duplicate of this creation (Ctrl/⌘ + F)"
|
||||||
|
onClick={itemForkBtnClick}
|
||||||
|
>
|
||||||
|
Fork<span class="show-when-selected">(Ctrl/⌘ + F)</span>
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
{itemRemoveBtnClick ? (
|
||||||
|
<a
|
||||||
|
class="js-saved-item-tile__remove-btn saved-item-tile__btn hint--left"
|
||||||
|
aria-label="Remove"
|
||||||
|
onClick={itemRemoveBtnClick}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-v-center">
|
||||||
|
{item.img ? (
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
class="saved-item-tile__img"
|
||||||
|
height="40"
|
||||||
|
src={item.img}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<h3 class="saved-item-tile__title">{item.title}</h3>
|
||||||
|
</div>
|
||||||
|
{item.updatedOn ? (
|
||||||
|
<span class="saved-item-tile__meta">
|
||||||
|
Last updated: {getHumanDate(item.updatedOn)}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,9 +1,10 @@
|
|||||||
import { h, Component } from 'preact';
|
import { h, Component } from 'preact';
|
||||||
import { log, getHumanDate } from '../utils';
|
import { log } from '../utils';
|
||||||
import { trackEvent } from '../analytics';
|
import { trackEvent } from '../analytics';
|
||||||
import { itemService } from '../itemService';
|
import { itemService } from '../itemService';
|
||||||
import { alertsService } from '../notifications';
|
import { alertsService } from '../notifications';
|
||||||
import { deferred } from '../deferred';
|
import { deferred } from '../deferred';
|
||||||
|
import { ItemTile } from './ItemTile';
|
||||||
|
|
||||||
export default class SavedItemPane extends Component {
|
export default class SavedItemPane extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -230,32 +231,12 @@ export default class SavedItemPane extends Component {
|
|||||||
<div class="mt-1">No match found.</div>
|
<div class="mt-1">No match found.</div>
|
||||||
) : null}
|
) : null}
|
||||||
{this.state.filteredItems.map(item => (
|
{this.state.filteredItems.map(item => (
|
||||||
<div
|
<ItemTile
|
||||||
class="js-saved-item-tile saved-item-tile"
|
item={item}
|
||||||
data-item-id={item.id}
|
|
||||||
onClick={this.itemClickHandler.bind(this, item)}
|
onClick={this.itemClickHandler.bind(this, item)}
|
||||||
>
|
onForkBtnClick={this.itemForkBtnClickHandler.bind(this, item)}
|
||||||
<div class="saved-item-tile__btns">
|
onRemoveBtnClick={this.itemRemoveBtnClickHandler.bind(this, item)}
|
||||||
<a
|
/>
|
||||||
class="js-saved-item-tile__fork-btn saved-item-tile__btn hint--left hint--medium"
|
|
||||||
aria-label="Creates a duplicate of this creation (Ctrl/⌘ + F)"
|
|
||||||
onClick={this.itemForkBtnClickHandler.bind(this, item)}
|
|
||||||
>
|
|
||||||
Fork<span class="show-when-selected">(Ctrl/⌘ + F)</span>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="js-saved-item-tile__remove-btn saved-item-tile__btn hint--left"
|
|
||||||
aria-label="Remove"
|
|
||||||
onClick={this.itemRemoveBtnClickHandler.bind(this, item)}
|
|
||||||
>
|
|
||||||
X
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h3 class="saved-item-tile__title">{item.title}</h3>
|
|
||||||
<span class="saved-item-tile__meta">
|
|
||||||
Last updated: {getHumanDate(item.updatedOn)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
{!this.items.length ? (
|
{!this.items.length ? (
|
||||||
<h2 class="opacity--30">Nothing saved here.</h2>
|
<h2 class="opacity--30">Nothing saved here.</h2>
|
||||||
|
@@ -41,6 +41,7 @@ import Portal from 'preact-portal';
|
|||||||
import { HelpModal } from './HelpModal';
|
import { HelpModal } from './HelpModal';
|
||||||
import { OnboardingModal } from './OnboardingModal';
|
import { OnboardingModal } from './OnboardingModal';
|
||||||
import { Js13KModal } from './Js13KModal';
|
import { Js13KModal } from './Js13KModal';
|
||||||
|
import { CreateNewModal } from './CreateNewModal';
|
||||||
import { Icons } from './Icons';
|
import { Icons } from './Icons';
|
||||||
import JSZip from 'jszip';
|
import JSZip from 'jszip';
|
||||||
|
|
||||||
@@ -59,8 +60,7 @@ export default class App extends Component {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.AUTO_SAVE_INTERVAL = 15000; // 15 seconds
|
this.AUTO_SAVE_INTERVAL = 15000; // 15 seconds
|
||||||
this.state = {
|
this.modalDefaultStates = {
|
||||||
isSavedItemPaneOpen: false,
|
|
||||||
isModalOpen: false,
|
isModalOpen: false,
|
||||||
isAddLibraryModalOpen: false,
|
isAddLibraryModalOpen: false,
|
||||||
isSettingsModalOpen: false,
|
isSettingsModalOpen: false,
|
||||||
@@ -73,6 +73,11 @@ export default class App extends Component {
|
|||||||
isAskToImportModalOpen: false,
|
isAskToImportModalOpen: false,
|
||||||
isOnboardModalOpen: false,
|
isOnboardModalOpen: false,
|
||||||
isJs13KModalOpen: false,
|
isJs13KModalOpen: false,
|
||||||
|
isCreateNewModalOpen: false
|
||||||
|
};
|
||||||
|
this.state = {
|
||||||
|
isSavedItemPaneOpen: false,
|
||||||
|
...this.modalDefaultStates,
|
||||||
prefs: {},
|
prefs: {},
|
||||||
currentItem: {
|
currentItem: {
|
||||||
title: '',
|
title: '',
|
||||||
@@ -490,17 +495,7 @@ export default class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isAddLibraryModalOpen: false,
|
...this.modalDefaultStates
|
||||||
isSettingsModalOpen: false,
|
|
||||||
isHelpModalOpen: false,
|
|
||||||
isNotificationsModalOpen: false,
|
|
||||||
isLoginModalOpen: false,
|
|
||||||
isProfileModalOpen: false,
|
|
||||||
isSupportDeveloperModalOpen: false,
|
|
||||||
isKeyboardShortcutsModalOpen: false,
|
|
||||||
isAskToImportModalOpen: false,
|
|
||||||
isOnboardModalOpen: false,
|
|
||||||
isJs13KModalOpen: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onExternalLibChange(newValues) {
|
onExternalLibChange(newValues) {
|
||||||
@@ -704,7 +699,9 @@ export default class App extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.calculateCodeSize();
|
if (this.state.prefs.isJs13kModeOn) {
|
||||||
|
this.calculateCodeSize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onCodeSettingsChange(type, settings) {
|
onCodeSettingsChange(type, settings) {
|
||||||
this.state.currentItem[`${type}Settings`] = {
|
this.state.currentItem[`${type}Settings`] = {
|
||||||
@@ -832,10 +829,14 @@ export default class App extends Component {
|
|||||||
'You have unsaved changes. Do you still want to create something new?'
|
'You have unsaved changes. Do you still want to create something new?'
|
||||||
);
|
);
|
||||||
if (shouldDiscard) {
|
if (shouldDiscard) {
|
||||||
this.createNewItem();
|
this.setState({
|
||||||
|
isCreateNewModalOpen: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.createNewItem();
|
this.setState({
|
||||||
|
isCreateNewModalOpen: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
openBtnClickHandler() {
|
openBtnClickHandler() {
|
||||||
@@ -1112,6 +1113,19 @@ export default class App extends Component {
|
|||||||
isJs13KModalOpen: true
|
isJs13KModalOpen: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
blankTemplateSelectHandler() {
|
||||||
|
this.createNewItem();
|
||||||
|
this.setState({ isCreateNewModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
templateSelectHandler(template) {
|
||||||
|
fetch(`templates/template-${template.id}.json`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(json => {
|
||||||
|
this.forkItem(json);
|
||||||
|
});
|
||||||
|
this.setState({ isCreateNewModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
@@ -1293,6 +1307,13 @@ export default class App extends Component {
|
|||||||
closeHandler={() => this.setState({ isJs13KModalOpen: false })}
|
closeHandler={() => this.setState({ isJs13KModalOpen: false })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<CreateNewModal
|
||||||
|
show={this.state.isCreateNewModalOpen}
|
||||||
|
closeHandler={() => this.setState({ isCreateNewModalOpen: false })}
|
||||||
|
onBlankTemplateSelect={this.blankTemplateSelectHandler.bind(this)}
|
||||||
|
onTemplateSelect={this.templateSelectHandler.bind(this)}
|
||||||
|
/>
|
||||||
|
|
||||||
<Portal into="body">
|
<Portal into="body">
|
||||||
<div
|
<div
|
||||||
class="modal-overlay"
|
class="modal-overlay"
|
||||||
|
BIN
src/js13kgames.png
Normal file
BIN
src/js13kgames.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
9
src/preact-logo.svg
Executable file
9
src/preact-logo.svg
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg width="256px" height="296px" viewBox="0 0 256 296" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||||
|
<g>
|
||||||
|
<polygon fill="#673AB8" points="128 0 256 73.8999491 256 221.699847 128 295.599796 0 221.699847 0 73.8999491"></polygon>
|
||||||
|
<path d="M34.8647584,220.478469 C51.8814262,242.25881 105.959701,225.662965 157.014868,185.774297 C208.070035,145.885628 237.255632,97.428608 220.238964,75.6482664 C203.222296,53.8679249 149.144022,70.4637701 98.0888543,110.352439 C47.0336869,150.241107 17.8480906,198.698127 34.8647584,220.478469 Z M42.1343351,214.798853 C36.4908625,207.575537 38.9565723,193.395881 49.7081913,175.544904 C61.0297348,156.747677 80.2490923,135.997367 103.76847,117.622015 C127.287848,99.2466634 152.071368,85.6181573 173.049166,79.1803727 C192.970945,73.066665 207.325915,74.1045667 212.969387,81.3278822 C218.61286,88.5511977 216.14715,102.730854 205.395531,120.581832 C194.073987,139.379058 174.85463,160.129368 151.335252,178.50472 C127.815874,196.880072 103.032354,210.508578 82.054556,216.946362 C62.1327769,223.06007 47.7778077,222.022168 42.1343351,214.798853 Z" fill="#FFFFFF"></path>
|
||||||
|
<path d="M220.238964,220.478469 C237.255632,198.698127 208.070035,150.241107 157.014868,110.352439 C105.959701,70.4637701 51.8814262,53.8679249 34.8647584,75.6482664 C17.8480906,97.428608 47.0336869,145.885628 98.0888543,185.774297 C149.144022,225.662965 203.222296,242.25881 220.238964,220.478469 Z M212.969387,214.798853 C207.325915,222.022168 192.970945,223.06007 173.049166,216.946362 C152.071368,210.508578 127.287848,196.880072 103.76847,178.50472 C80.2490923,160.129368 61.0297348,139.379058 49.7081913,120.581832 C38.9565723,102.730854 36.4908625,88.5511977 42.1343351,81.3278822 C47.7778077,74.1045667 62.1327769,73.066665 82.054556,79.1803727 C103.032354,85.6181573 127.815874,99.2466634 151.335252,117.622015 C174.85463,135.997367 194.073987,156.747677 205.395531,175.544904 C216.14715,193.395881 218.61286,207.575537 212.969387,214.798853 Z" fill="#FFFFFF"></path>
|
||||||
|
<path d="M127.551861,167.666971 C138.378632,167.666971 147.155465,158.890139 147.155465,148.063368 C147.155465,137.236596 138.378632,128.459764 127.551861,128.459764 C116.72509,128.459764 107.948257,137.236596 107.948257,148.063368 C107.948257,158.890139 116.72509,167.666971 127.551861,167.666971 L127.551861,167.666971 Z" fill="#FFFFFF"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
7
src/react-logo.svg
Executable file
7
src/react-logo.svg
Executable file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.9 KiB |
24
src/templateList.js
Normal file
24
src/templateList.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export default [
|
||||||
|
{
|
||||||
|
id: 'preact',
|
||||||
|
title: 'Preact',
|
||||||
|
img: 'preact-logo.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'react',
|
||||||
|
title: 'React',
|
||||||
|
img: 'react-logo.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'html5-canvas',
|
||||||
|
title: 'HTML5 Canvas',
|
||||||
|
lastUpdatedTime: Date.now(),
|
||||||
|
img: 'html5-logo.svg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'html5-canvas-game',
|
||||||
|
title: 'HTML5 Canvas Game',
|
||||||
|
lastUpdatedTime: Date.now(),
|
||||||
|
img: 'html5-logo.svg'
|
||||||
|
}
|
||||||
|
];
|
16
src/templates/template-preact.json
Normal file
16
src/templates/template-preact.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"title": "Preact template",
|
||||||
|
"externalLibs": {
|
||||||
|
"js": "\nhttps://cdnjs.cloudflare.com/ajax/libs/preact/8.2.9/preact.min.js",
|
||||||
|
"css": ""
|
||||||
|
},
|
||||||
|
"sizes": ["calc(30% - 3px)", "30px", "calc(70% - 3px)"],
|
||||||
|
"mainSizes": [68.1051, 31.6949],
|
||||||
|
"htmlMode": "html",
|
||||||
|
"cssMode": "css",
|
||||||
|
"jsMode": "es6",
|
||||||
|
"layoutMode": 1,
|
||||||
|
"js":
|
||||||
|
"\nconst { h, Component, render, createElement } = window.preact;\nconst React = {createElement}\n\nclass App extends Component {\n constructor() {\n super();\n this.message = 'World';\n }\n render() {\n return (<div>Hello {this.message}</div>)\n }\n}\nrender(<App/>, window.root)\n\n",
|
||||||
|
"html": "<div id=\"root\"></div>\n"
|
||||||
|
}
|
18
src/templates/template-react.json
Normal file
18
src/templates/template-react.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"title": "React template",
|
||||||
|
"externalLibs": {
|
||||||
|
"js":
|
||||||
|
"\nhttps://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js\nhttps://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js",
|
||||||
|
"css": ""
|
||||||
|
},
|
||||||
|
"updatedOn": 1531063870556,
|
||||||
|
"sizes": ["", "calc(17.4842% - 6px)", "calc(49.1221% - 3px)"],
|
||||||
|
"mainSizes": [68.1051, 31.6949],
|
||||||
|
"htmlMode": "html",
|
||||||
|
"cssMode": "css",
|
||||||
|
"jsMode": "es6",
|
||||||
|
"layoutMode": 1,
|
||||||
|
"js":
|
||||||
|
"class App extends React.Component {\n constructor() {\n super();\n this.message = 'World';\n }\n render() {\n return (<h1>Hello {this.message}</h1>);\n }\n}\nReactDOM.render(<App/>, window.root);\n\n\n",
|
||||||
|
"html": "<div id=\"root\"></div>\n"
|
||||||
|
}
|
@@ -345,7 +345,9 @@ export function getCompleteHtml(html, css, js, item, isForExport) {
|
|||||||
'<script src="' +
|
'<script src="' +
|
||||||
(chrome.extension
|
(chrome.extension
|
||||||
? chrome.extension.getURL('lib/screenlog.js')
|
? chrome.extension.getURL('lib/screenlog.js')
|
||||||
: `${location.origin}${BASE_PATH}/lib/screenlog.js`) +
|
: `${location.origin}${
|
||||||
|
window.DEBUG ? '' : BASE_PATH
|
||||||
|
}/lib/screenlog.js`) +
|
||||||
'"></script>';
|
'"></script>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user