allow generation of globals + ui fixes

This commit is contained in:
ido 2014-12-28 11:47:34 +02:00
parent 133f2645d2
commit 349057188a
16 changed files with 15259 additions and 15219 deletions

View File

@ -105,7 +105,7 @@ module.exports = function (grunt) {
grunt.registerTask('rt', function () {
var reactTemplates = require('./src/cli');
var files = grunt.file.expand('playground/*.rt');
var conf = {commonjs: false, force: true, _: files};
var conf = {modules: 'amd', force: true, _: files};
var ret = reactTemplates.execute(conf);
return ret === 0;
});

View File

@ -35,7 +35,12 @@
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">
<!--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">-->
<link rel="stylesheet" href="playground/libs/codemirror-4.8/lib/codemirror.css" />
<link rel="stylesheet" href="playground/libs/codemirror-4.8/theme/solarized.css" />
<link rel="stylesheet" href="playground/libs/codemirror-4.8/addon/hint/show-hint.css">
<link rel="stylesheet" href="playground/libs/codemirror-4.8/addon/lint/lint.css">
<link rel="stylesheet" href="playground/css/home.css"/>
</head>
@ -150,9 +155,9 @@
<!--<script src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.js"></script>-->
<!--http://requirejs.org/docs/release/2.1.15/minified/require.js-->
<!--<script data-main="playground/home-main.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.min.js"></script>-->
<script data-main="playground/home-main.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.min.js"></script>
<!--<script src="playground/home-main.js"></script>-->
<script src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.min.js"></script>
<script src="playground/dist/home.min.js"></script>
<!--<script src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.min.js"></script>-->
<!--<script src="playground/dist/home.min.js"></script>-->
</body>
</html>

View File

@ -1,46 +1,4 @@
.large-text-area {
height: 400px;
/*min-height: 100px;*/
/*height: 100%;*/
margin: 10px;
}
.code-area.horizontal {
width: 70%;
float: left;
}
.code-area.vertical {
width: 100%;
float: none;
}
.result-area.horizontal {
width: 30%;
float: left;
}
.result-area.vertical {
width: 100%;
float: none;
}
.result-area {
width: 50%;
float: left;
}
.sample-view {
margin: 10px;
}
.sample-view button {
margin: 5px;
}
.sample-view input[type=checkbox] {
margin-right: 5px;
}
html {
font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
@ -155,11 +113,6 @@ a:focus {
text-align: center
}
html * {
color-profile: sRGB;
rendering-intent: auto
}
.cm-s-solarized-light {
background-color: #f8f5ec;
color: #637c84
@ -377,15 +330,6 @@ html * {
color: #268bd2
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border: none;
margin: 0;
padding: 0
}
html {
background: #f9f9f9
}
@ -944,29 +888,29 @@ h1:after {
margin-top: 60px
}
div.CodeMirror pre, div.CodeMirror-linenumber, code {
font-family: 'source-code-pro', Menlo, Consolas, 'Courier New', monospace;
font-size: 0.8em;
line-height: 1.5
}
/*div.CodeMirror pre, div.CodeMirror-linenumber, code {*/
/*font-family: 'source-code-pro', Menlo, Consolas, 'Courier New', monospace;*/
/*font-size: 0.8em;*/
/*line-height: 1.5*/
/*}*/
div.CodeMirror-linenumber:after {
content: '.'
}
/*div.CodeMirror-linenumber:after {*/
/*content: '.'*/
/*}*/
.CodeMirror, div.CodeMirror-gutters, div.highlight {
border: none
}
/*.CodeMirror, div.CodeMirror-gutters, div.highlight {*/
/*border: none*/
/*}*/
.CodeMirror-readonly div.CodeMirror-cursor {
visibility: hidden
}
/*.CodeMirror-readonly div.CodeMirror-cursor {*/
/*visibility: hidden*/
/*}*/
small code, li code, p code {
color: #555;
background-color: rgba(0, 0, 0, 0.04);
padding: 1px 3px
}
/*small code, li code, p code {*/
/*color: #555;*/
/*background-color: rgba(0, 0, 0, 0.04);*/
/*padding: 1px 3px*/
/*}*/
.cm-s-default span.cm-string-2 {
color: inherit
@ -1134,4 +1078,68 @@ div[data-twttr-id] iframe {
.three-column > ul:first-child {
margin-left: 20px
}
}
.nav-tabs {
margin-left: 0; margin-bottom: 1px;border-bottom:none;
}
.nav-tabs>li{margin-left:0}
.preview-title{display:block; padding:10px 15px 13px; line-height: 1.42857; color: #333; margin-left:30px; }
.large-text-area {
border:1px solid #ddd;
border-radius: 0 4px 4px 4px;
/*height: 400px;*/
/*min-height: 100px;*/
/*height: 100%;*/
/*margin: 10px;*/
}
.code-area, .result-area{
box-sizing:border-box;
}
.code-area.horizontal {
width: 70%;
float: left;
}
.code-area.vertical {
width: 100%;
float: none;
}
.result-area.horizontal {
width: 30%;
float: left;
}
.result-area.vertical {
width: 100%;
float: none;
}
.result-area {
width: 50%;
float: left;
}
.sample-view {
border:1px solid #ddd;
border-radius: 4px;
background-color: #f8f8f5;
padding: 20px 30px;
margin-left:30px;
}
.sample-view button {
margin: 5px;
}
.sample-view input[type=checkbox] {
margin-right: 5px;
}

View File

@ -2,7 +2,7 @@
* Created by avim on 12/2/2014.
*/
/*eslint global-strict:0, no-alert:0*/
/*global Firebase:true,alert:true*/
/*global alert:true*/
define(['react', 'firebase', 'lodash', './fiddle.rt', 'jquery'], function (React, Firebase, _, fiddleTemplate, $) {
'use strict';

View File

@ -1,8 +1,10 @@
<rt-require dependency="./aceEditor" as="CodeEditor"/>
<!--<rt-require dependency="./aceEditor" as="CodeEditor"/>-->
<!--suppress CheckEmptyScriptTag -->
<rt-require dependency="./CodeMirrorEditor" as="CodeEditor"/>
<div class="playground">
<div id="{this.props.id}-myTab" role="tabpanel" class="code-area {(this.props.direction === 'horizontal' && 'horizontal') ||'vertical'}">
<!-- Nav tabs -->
<ul class="nav nav-pills" role="tablist">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"><a href="#{this.props.id}-template" aria-controls="template" role="tab" data-toggle="tab">Template</a></li>
<li rt-if="this.props.codeVisible" role="presentation"><a href="#{this.props.id}-classCode" aria-controls="classCode" role="tab" data-toggle="tab">Class</a></li>
<li role="presentation"><a href="#{this.props.id}-generatedCode" aria-controls="generatedCode" role="tab" data-toggle="tab">Generated code</a></li>
@ -10,30 +12,28 @@
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="{this.props.id}-template">
<CodeEditor class="large-text-area" style="border: {this.validHTML? '1px solid black':'2px solid red'};"
<CodeEditor ref="editorRT" class="large-text-area" style="border: {this.validHTML? '':'2px solid red'};"
value="{this.state.templateHTML}"
mode="html"
onChange="(evt) => this.setState({'templateHTML':evt.target.value})"
/>
onChange="(evt) => this.setState({'templateHTML':evt.target.value})" />
</div>
<div rt-if="this.props.codeVisible" role="tabpanel" class="tab-pane" id="{this.props.id}-classCode">
<CodeEditor class="large-text-area" style="border: {this.validProps? '1px solid black':'2px solid red'};"
<CodeEditor ref="editorCode" class="large-text-area" style="border: {this.validProps? '':'2px solid red'};"
value="{this.state.templateProps}"
mode="javascript"
onChange="(evt) => this.setState({'templateProps':evt.target.value})"
/>
onChange="(evt) => this.setState({'templateProps':evt.target.value})" />
</div>
<div role="tabpanel" class="tab-pane" id="{this.props.id}-generatedCode">
<CodeEditor class="large-text-area" style="border:1px solid black;"
<CodeEditor class="large-text-area"
value="{this.templateSource}"
mode="javascript"
readOnly="{true}"
/>
readOnly="{true}" />
</div>
</div>
</div>
<div key="result-area" class="result-area well {(this.props.direction === 'horizontal' && 'horizontal') ||'vertical'}" style="margin-top: 48px;">
<h2>Preview:</h2>
<div key="result-area" class="result-area {(this.props.direction === 'horizontal' && 'horizontal') ||'vertical'}">
<span class="preview-title">{'\u00A0'}</span>
<form class="sample-view" onSubmit="(e) => e.preventDefault();">
<this.sample key="sample">
</this.sample>

View File

@ -2,7 +2,7 @@
define([
'react/addons',
'lodash',
'./aceEditor'
'./CodeMirrorEditor'
], function (React, _, CodeEditor) {
'use strict';
function onChange1(evt) {
@ -20,7 +20,7 @@ define([
'role': 'tabpanel',
'className': 'code-area ' + (this.props.direction === 'horizontal' && 'horizontal' || 'vertical')
} /* Nav tabs */, React.createElement('ul', {
'className': 'nav nav-pills',
'className': 'nav nav-tabs',
'role': 'tablist'
}, React.createElement('li', {
'role': 'presentation',
@ -45,8 +45,9 @@ define([
'className': 'tab-pane active',
'id': this.props.id + '-template'
}, React.createElement(CodeEditor, {
'ref': 'editorRT',
'className': 'large-text-area',
'style': { border: this.validHTML ? '1px solid black' : '2px solid red' },
'style': { border: this.validHTML ? '' : '2px solid red' },
'value': this.state.templateHTML,
'mode': 'html',
'onChange': onChange1.bind(this)
@ -55,8 +56,9 @@ define([
'className': 'tab-pane',
'id': this.props.id + '-classCode'
}, React.createElement(CodeEditor, {
'ref': 'editorCode',
'className': 'large-text-area',
'style': { border: this.validProps ? '1px solid black' : '2px solid red' },
'style': { border: this.validProps ? '' : '2px solid red' },
'value': this.state.templateProps,
'mode': 'javascript',
'onChange': onChange2.bind(this)
@ -66,15 +68,13 @@ define([
'id': this.props.id + '-generatedCode'
}, React.createElement(CodeEditor, {
'className': 'large-text-area',
'style': { border: '1px solid black' },
'value': this.templateSource,
'mode': 'javascript',
'readOnly': true
})))), React.createElement('div', {
'key': 'result-area',
'className': 'result-area well ' + (this.props.direction === 'horizontal' && 'horizontal' || 'vertical'),
'style': { marginTop: '48px' }
}, React.createElement('h2', {}, 'Preview:'), React.createElement('form', {
'className': 'result-area ' + (this.props.direction === 'horizontal' && 'horizontal' || 'vertical')
}, React.createElement('span', { 'className': 'preview-title' }, '\xA0'), React.createElement('form', {
'className': 'sample-view',
'onSubmit': onSubmit3.bind(this)
}, React.createElement(this.sample, { 'key': 'sample' }))), React.createElement('br', { 'style': { clear: 'both' } }));

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<h1>Hello world</h1>
<h2>Hello world</h2>

View File

@ -1,4 +1,7 @@
<div>
<h4 style="cursor:pointer" onClick="()=>this.toggle()">Click to {this.state.open ? 'close' : 'open'}</h4>
<p style="display:{this.state.open?'block':'none'}">This is my paragraph. It opens and closes</p>
<h4 style="cursor:pointer" onClick="()=>this.toggle()">
Click to {this.state.open ? 'close' : 'open'}
</h4>
<p rt-if="this.state.open">This is my paragraph. It opens and
closes</p>
</div>

View File

@ -1,4 +1,5 @@
<div onClick="(evt)=>this.reportClick(evt.target)" rt-props="this.getProps()">
<div onClick="(evt)=>this.reportClick(evt.target)"
rt-props="this.getProps()">
All kinds of stuff,
<span>some inner child, </span> and a sibling
</div>

View File

@ -2,16 +2,11 @@
mixins: [React.addons.LinkedStateMixin],
getInitialState: function () {
return {
media: [
{ id:0, type: 'Text', title: 'I\'m a title', body: 'I\'m a paragraph' },
{ id:1, type: 'Image', src: 'https://facebook.github.io/react/img/logo.svg' },
{ id:2, type: 'Text', title: 'Also a title', body: 'Also a paragraph' }
items: [
'One',
'Two',
'Three'
]
};
},
swap: function(from, to) {
var newMedia = _.clone(this.state.media);
newMedia.splice(to, 0, newMedia.splice(from, 1)[0]);
this.setState({media:newMedia});
}
}

View File

@ -1,13 +1,5 @@
<div>
<div rt-repeat="media in this.state.media" key="{media.id}">
<button onClick="()=>this.swap(mediaIndex,mediaIndex-1)" rt-scope="mediaIndex === 0 as first" disabled="{first}" style="color:{first?'grey':'black'}">Up</button>
<button onClick="()=>this.swap(mediaIndex,mediaIndex+1)" rt-scope="mediaIndex === (this.state.media.length - 1) as last" disabled="{last}" style="color:{last?'grey':'black'}">Down</button>
<div rt-if="media.type == 'Image'">
<img src="{media.src}" width="80"/>
</div>
<div rt-if="media.type == 'Text'">
<h4>{media.title}</h4>
<p>{media.body}</p>
</div>
</div>
<ul rt-repeat="item in this.state.items">
<li>{item}</li>
</ul>
</div>

View File

@ -1,11 +1,17 @@
<div>
<h4>Cities weather report</h4>
<input placeholder="Type a city to add" valueLink="{this.linkState('cityToAdd')}" onKeyDown="(e)=>if (e.keyCode === 13) { e.preventDefault(); this.addCity(); }"/>
<input placeholder="Type a city to add"
valueLink="{this.linkState('cityToAdd')}"
onKeyDown="(e)=>if (e.keyCode === 13) { e.preventDefault(); this.addCity(); }"/>
<button onClick="{this.addCity}">Add</button>
<div key="preloader" rt-if="this.state.loading">-- Loading --</div>
<div key="preloader" rt-if="this.state.loading">
-- Loading --
</div>
<div rt-repeat="city in this.state.info" key="{city.id}">
{cityIndex+1})
<img rt-repeat="weather in city.weather" src="http://openweathermap.org/img/w/{weather.icon}.png" title="{weather.description}"/>
<img rt-repeat="weather in city.weather"
src="http://openweathermap.org/img/w/{weather.icon}.png"
title="{weather.description}"/>
{city.name}, {city.sys.country}
</div>
<button onClick="{this.refresh}">Refresh</button>

View File

@ -1,13 +1,14 @@
'use strict';
var fs = require('fs');
var path = require('path');
var chalk = require('chalk');
var reactTemplates = require('./reactTemplates');
var convertTemplateToReact = reactTemplates.convertTemplateToReact;
/**
* @param {string} source
* @param {{commonJS:boolean, dryRun:boolean}?} options
* @param {{modules:string, dryRun:boolean}?} options
* @param {string} target
* @param {CONTEXT} context
*/
@ -25,6 +26,9 @@ function convertFile(source, target, options, context) {
}
var html = fs.readFileSync(source).toString();
if (!options.name) {
options.name = path.basename(source, path.extname(source)) + 'RT';
}
var js = convertTemplateToReact(html, options);
if (!options.dryRun) {
fs.writeFileSync(target, js);

View File

@ -41,11 +41,17 @@ module.exports = optionator({
type: 'Boolean',
description: 'Use colors in output.'
}, {
option: 'commonJS',
option: 'modules',
alias: 'm',
default: 'false',
type: 'Boolean',
description: 'Use Common JS output.'
default: 'none',
type: 'String',
description: 'Use output modules. (amd|commonjs|none)'
}, {
option: 'name',
alias: 'n',
default: 'filenameRT',
type: 'String',
description: 'When using globals, the name for the variable. The default is the [file name]RT'
}, {
option: 'dry-run',
alias: 'd',

View File

@ -18,8 +18,12 @@ var tagTemplate = _.template('<%= name %>.apply(this,_.flatten([<%= props %><%=
var simpleTagTemplateCreateElement = _.template('React.createElement(<%= name %>,<%= props %><%= children %>)');
var tagTemplateCreateElement = _.template('React.createElement.apply(this,_.flatten([<%= name %>,<%= props %><%= children %>]))');
var commentTemplate = _.template(' /* <%= data %> */ ');
var templateAMDTemplate = _.template("/*eslint new-cap:0,no-unused-vars:0*/\ndefine([<%= requirePaths %>], function (<%= requireNames %>) {\n'use strict';\n <%= injectedFunctions %>\nreturn function(){ return <%= body %>};\n});");
var templateAMDTemplate = _.template("define([<%= requirePaths %>], function (<%= requireNames %>) {\n'use strict';\n <%= injectedFunctions %>\nreturn function(){ return <%= body %>};\n});");
var templateCommonJSTemplate = _.template("<%= vars %>\n\n'use strict';\n <%= injectedFunctions %>\nmodule.exports = function(){ return <%= body %>};\n");
var templatePJSTemplate = _.template('var <%= name %> = function () {\n' +
'<%= injectedFunctions %>\n' +
'return <%= body %>\n' +
'};\n');
var templateProp = 'rt-repeat';
var ifProp = 'rt-if';
@ -27,7 +31,7 @@ var classSetProp = 'rt-class';
var scopeProp = 'rt-scope';
var propsProp = 'rt-props';
var defaultOptions = {commonJS: false, version: false, force: false, format: 'stylish', targetVersion: '0.12.1'};
var defaultOptions = {modules: 'amd', version: false, force: false, format: 'stylish', targetVersion: '0.12.1'};
function shouldUseCreateElement(context) {
switch (context.options.targetVersion) {
@ -332,7 +336,7 @@ function convertHtmlToReact(node, context) {
function removeDocType(html) {
html = html.replace(/^\s*\<\!doctype\s+rt\s*>/mi, function () {
return "";
return '';
});
return html;
}
@ -340,7 +344,7 @@ function removeDocType(html) {
/**
* @param {string} html
* @param {{commonJS:boolean}?} options
* @param {{modules:string}?} options
* @return {string}
*/
function convertTemplateToReact(html, options) {
@ -354,13 +358,13 @@ function convertTemplateToReact(html, options) {
}
var firstTag = null;
_.forEach(rootTags, function(tag) {
if (tag.name == 'rt-require') {
if (!tag.attribs['dependency'] || !tag.attribs['as']) {
throw buildError('rt-require needs \'dependency\' and \'as\' attributes', context, tag);
if (tag.name === 'rt-require') {
if (!tag.attribs.dependency || !tag.attribs.as) {
throw buildError("rt-require needs 'dependency' and 'as' attributes", context, tag);
} else if (tag.children.length) {
throw buildError('rt-require may have no children', context, tag);
} else {
defines[tag.attribs['dependency']] = tag.attribs['as'];
defines[tag.attribs.dependency] = tag.attribs.as;
}
} else if (firstTag === null) {
firstTag = tag;
@ -375,9 +379,9 @@ function convertTemplateToReact(html, options) {
var requirePaths = _(defines).keys().map(function (reqName) { return '"' + reqName + '"'; }).value().join(',');
var requireVars = _(defines).values().value().join(',');
var vars = _(defines).map(function (reqVar, reqPath) { return 'var ' + reqVar + " = require('" + reqPath + "');"; }).join('\n');
var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths, vars: vars};
var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths, vars: vars, name: options.name};
data.injectedFunctions = context.injectedFunctions.join('\n');
var code = options.commonJS ? templateCommonJSTemplate(data) : templateAMDTemplate(data);
var code = generate(data, options);
try {
var tree = esprima.parse(code, {range: true, tokens: true, comment: true});
tree = escodegen.attachComments(tree, tree.comments, tree.tokens);
@ -389,6 +393,16 @@ function convertTemplateToReact(html, options) {
return code;
}
function generate(data, options) {
if (options.modules === 'amd') {
return templateAMDTemplate(data);
}
if (options.modules === 'commonjs') {
return templateCommonJSTemplate(data);
}
return templatePJSTemplate(data);
}
module.exports = {
convertTemplateToReact: convertTemplateToReact,
RTCodeError: RTCodeError,