mirror of
https://github.com/bobwen-dev/react-templates
synced 2025-04-12 00:56:39 +02:00
Merge branch 'gh-pages' of https://github.com/nirnatan/react-templates into nirnatan-gh-pages
This commit is contained in:
commit
295f2f41f0
75
README.md
75
README.md
@ -399,6 +399,81 @@ define([
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## properties template functions
|
||||||
|
In cases you'd like to use a property that accepts a function and return renderable React component.
|
||||||
|
You should use a **rt-template** tag that will let you do exactly that: `<rt-template prop="propName" arguments="arg1, arg2"/>`.
|
||||||
|
|
||||||
|
Templates can be used only as an immediate child of the component that it will be used in. All scope variable will be available in the template function.
|
||||||
|
|
||||||
|
###### Sample:
|
||||||
|
```html
|
||||||
|
<MyComp data="{[1,2,3]}">
|
||||||
|
<rt-template prop="renderItem" arguments="item">
|
||||||
|
<div>{item}</div>
|
||||||
|
</rt-template>
|
||||||
|
</MyComp>
|
||||||
|
```
|
||||||
|
###### Compiled (AMD):
|
||||||
|
```javascript
|
||||||
|
define([
|
||||||
|
'react/addons',
|
||||||
|
'lodash'
|
||||||
|
], function (React, _) {
|
||||||
|
'use strict';
|
||||||
|
function renderItem1(item) {
|
||||||
|
return React.createElement('div', {}, item);
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
return React.createElement(MyComp, {
|
||||||
|
'data': [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
'renderItem': renderItem1.bind(this)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
```
|
||||||
|
###### Compiled (with CommonJS flag):
|
||||||
|
```javascript
|
||||||
|
'use strict';
|
||||||
|
var React = require('react/addons');
|
||||||
|
var _ = require('lodash');
|
||||||
|
function renderItem1(item) {
|
||||||
|
return React.createElement('div', {}, item);
|
||||||
|
}
|
||||||
|
module.exports = function () {
|
||||||
|
return React.createElement(MyComp, {
|
||||||
|
'data': [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
'renderItem': renderItem1.bind(this)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
###### Compiled (with ES6 flag):
|
||||||
|
```javascript
|
||||||
|
import React from 'react/addons';
|
||||||
|
import _ from 'lodash';
|
||||||
|
function renderItem1(item) {
|
||||||
|
return React.createElement('div', {}, item);
|
||||||
|
}
|
||||||
|
export default function () {
|
||||||
|
return React.createElement(MyComp, {
|
||||||
|
'data': [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
'renderItem': renderItem1.bind(this)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
See the [Contributing page](CONTRIBUTING.md).
|
See the [Contributing page](CONTRIBUTING.md).
|
||||||
|
@ -53,6 +53,7 @@ var classSetAttr = 'rt-class';
|
|||||||
var classAttr = 'class';
|
var classAttr = 'class';
|
||||||
var scopeAttr = 'rt-scope';
|
var scopeAttr = 'rt-scope';
|
||||||
var propsAttr = 'rt-props';
|
var propsAttr = 'rt-props';
|
||||||
|
var templateNode = 'rt-template';
|
||||||
|
|
||||||
var defaultOptions = {modules: 'amd', version: false, force: false, format: 'stylish', targetVersion: '0.13.1', reactImportPath: 'react/addons', lodashImportPath: 'lodash'};
|
var defaultOptions = {modules: 'amd', version: false, force: false, format: 'stylish', targetVersion: '0.13.1', reactImportPath: 'react/addons', lodashImportPath: 'lodash'};
|
||||||
|
|
||||||
@ -179,6 +180,55 @@ function generateInjectedFunc(context, namePrefix, body, params) {
|
|||||||
return generatedFuncName;
|
return generatedFuncName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateTemplateProps(node, context) {
|
||||||
|
var propTemplateDefinition = context.options.templates && context.options.templates[node.name];
|
||||||
|
var propertiesTemplates = _(node.children)
|
||||||
|
.map(function (child, index) {
|
||||||
|
var templateProp = null;
|
||||||
|
if (child.name === templateNode) { // Generic explicit template tag
|
||||||
|
if (!_.has(child.attribs, 'prop')) {
|
||||||
|
throw RTCodeError.build('rt-template must have a prop attribute', context, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
var childTemplate = _.find(context.options.templates, {prop: child.attribs.prop}) || {arguments: []};
|
||||||
|
templateProp = {
|
||||||
|
prop: child.attribs.prop,
|
||||||
|
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
|
||||||
|
};
|
||||||
|
} else if (propTemplateDefinition && propTemplateDefinition[child.name]) { // Implicit child template from configuration
|
||||||
|
templateProp = {
|
||||||
|
prop: propTemplateDefinition[child.name].prop,
|
||||||
|
arguments: child.attribs.arguments ? child.attribs.arguments.split(',') : propTemplateDefinition[child.name].arguments
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (templateProp) {
|
||||||
|
_.assign(templateProp, {childIndex: index, content: _.find(child.children, {type: 'tag'})});
|
||||||
|
}
|
||||||
|
|
||||||
|
return templateProp;
|
||||||
|
})
|
||||||
|
.compact()
|
||||||
|
.value();
|
||||||
|
|
||||||
|
return _.transform(propertiesTemplates, function (props, templateProp) {
|
||||||
|
var functionParams = _.values(context.boundParams).concat(templateProp.arguments);
|
||||||
|
|
||||||
|
var oldBoundParams = context.boundParams;
|
||||||
|
context.boundParams = context.boundParams.concat(templateProp.arguments);
|
||||||
|
|
||||||
|
var functionBody = 'return ' + convertHtmlToReact(templateProp.content, context);
|
||||||
|
context.boundParams = oldBoundParams;
|
||||||
|
|
||||||
|
var generatedFuncName = generateInjectedFunc(context, templateProp.prop, functionBody, functionParams);
|
||||||
|
var boundArguments = _.values(context.boundParams).join(',');
|
||||||
|
props[templateProp.prop] = generatedFuncName + '.bind(this' + (boundArguments.length ? ', ' + boundArguments : '') + ')';
|
||||||
|
|
||||||
|
// Remove the template child from the children definition.
|
||||||
|
node.children.splice(templateProp.childIndex, 1);
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param node
|
* @param node
|
||||||
* @param {Context} context
|
* @param {Context} context
|
||||||
@ -237,6 +287,8 @@ function generateProps(node, context) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_.assign(props, generateTemplateProps(node, context));
|
||||||
|
|
||||||
return '{' + _.map(props, function (val, key) {
|
return '{' + _.map(props, function (val, key) {
|
||||||
return JSON.stringify(key) + ' : ' + val;
|
return JSON.stringify(key) + ' : ' + val;
|
||||||
}).join(',') + '}';
|
}).join(',') + '}';
|
||||||
@ -490,15 +542,23 @@ function convertRT(html, reportContext, options) {
|
|||||||
throw RTCodeError.build('Document should have a single root element', context, rootNode.root()[0]);
|
throw RTCodeError.build('Document should have a single root element', context, rootNode.root()[0]);
|
||||||
}
|
}
|
||||||
var body = convertHtmlToReact(firstTag, context);
|
var body = convertHtmlToReact(firstTag, context);
|
||||||
var requirePaths = _(defines).keys().map(function (reqName) { return '"' + reqName + '"'; }).value().join(',');
|
var requirePaths = _(defines).keys().map(function (reqName) {
|
||||||
|
return '"' + reqName + '"';
|
||||||
|
}).value().join(',');
|
||||||
var requireVars = _(defines).values().value().join(',');
|
var requireVars = _(defines).values().value().join(',');
|
||||||
var vars;
|
var vars;
|
||||||
if (options.modules === 'typescript') {
|
if (options.modules === 'typescript') {
|
||||||
vars = _(defines).map(function (reqVar, reqPath) { return 'import ' + reqVar + " = require('" + reqPath + "');"; }).join('\n');
|
vars = _(defines).map(function (reqVar, reqPath) {
|
||||||
|
return 'import ' + reqVar + " = require('" + reqPath + "');";
|
||||||
|
}).join('\n');
|
||||||
} else if (options.modules === 'es6') {
|
} else if (options.modules === 'es6') {
|
||||||
vars = _(defines).map(function (reqVar, reqPath) { return 'import ' + reqVar + " from '" + reqPath + "';"; }).join('\n');
|
vars = _(defines).map(function (reqVar, reqPath) {
|
||||||
|
return 'import ' + reqVar + " from '" + reqPath + "';";
|
||||||
|
}).join('\n');
|
||||||
} else {
|
} else {
|
||||||
vars = _(defines).map(function (reqVar, reqPath) { return 'var ' + reqVar + " = require('" + reqPath + "');"; }).join('\n');
|
vars = _(defines).map(function (reqVar, reqPath) {
|
||||||
|
return 'var ' + reqVar + " = require('" + reqPath + "');";
|
||||||
|
}).join('\n');
|
||||||
}
|
}
|
||||||
var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths, vars: vars, name: options.name};
|
var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths, vars: vars, name: options.name};
|
||||||
data.injectedFunctions = context.injectedFunctions.join('\n');
|
data.injectedFunctions = context.injectedFunctions.join('\n');
|
||||||
|
7
test/data/propTemplates/implicitTemplate.rt
Normal file
7
test/data/propTemplates/implicitTemplate.rt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<div>
|
||||||
|
<List data="{[1,2,3]}">
|
||||||
|
<Row>
|
||||||
|
<div>{rowData}</div>
|
||||||
|
</Row>
|
||||||
|
</List>
|
||||||
|
</div>
|
19
test/data/propTemplates/implicitTemplate.rt.js
Normal file
19
test/data/propTemplates/implicitTemplate.rt.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
define([
|
||||||
|
'react/addons',
|
||||||
|
'lodash'
|
||||||
|
], function (React, _) {
|
||||||
|
'use strict';
|
||||||
|
function renderRow1(rowData) {
|
||||||
|
return React.createElement('div', {}, rowData);
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
return React.createElement('div', {}, React.createElement(List, {
|
||||||
|
'data': [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
'renderRow': renderRow1.bind(this)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
});
|
5
test/data/propTemplates/simpleTemplate.rt
Normal file
5
test/data/propTemplates/simpleTemplate.rt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
<rt-template prop="templateProp" arguments="arg1">
|
||||||
|
<div>{arg1}</div>
|
||||||
|
</rt-template>
|
||||||
|
</div>
|
12
test/data/propTemplates/simpleTemplate.rt.js
Normal file
12
test/data/propTemplates/simpleTemplate.rt.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
define([
|
||||||
|
'react/addons',
|
||||||
|
'lodash'
|
||||||
|
], function (React, _) {
|
||||||
|
'use strict';
|
||||||
|
function templateProp1(arg1) {
|
||||||
|
return React.createElement('div', {}, arg1);
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
return React.createElement('div', { 'templateProp': templateProp1.bind(this) });
|
||||||
|
};
|
||||||
|
});
|
5
test/data/propTemplates/templateInScope.rt
Normal file
5
test/data/propTemplates/templateInScope.rt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<div rt-scope="'boten' as name">
|
||||||
|
<rt-template prop="templateProp" arguments="arg1">
|
||||||
|
<div>Name: {name} {arg1}</div>
|
||||||
|
</rt-template>
|
||||||
|
</div>
|
16
test/data/propTemplates/templateInScope.rt.js
Normal file
16
test/data/propTemplates/templateInScope.rt.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
define([
|
||||||
|
'react/addons',
|
||||||
|
'lodash'
|
||||||
|
], function (React, _) {
|
||||||
|
'use strict';
|
||||||
|
function templateProp1(name, arg1) {
|
||||||
|
return React.createElement('div', {}, 'Name: ', name, ' ', arg1);
|
||||||
|
}
|
||||||
|
function scopeName2() {
|
||||||
|
var name = 'boten';
|
||||||
|
return React.createElement('div', { 'templateProp': templateProp1.bind(this, name) });
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
return scopeName2.apply(this, []);
|
||||||
|
};
|
||||||
|
});
|
10
test/data/propTemplates/twoTemplates.rt
Normal file
10
test/data/propTemplates/twoTemplates.rt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<div>
|
||||||
|
<rt-template prop="templateProp" arguments="arg1">
|
||||||
|
<div>
|
||||||
|
<rt-template prop="templateProp2" arguments="inner1, inner2">
|
||||||
|
<div>{arg1 + inner1 + inner2}</div>
|
||||||
|
</rt-template>
|
||||||
|
<div>{arg1}</div>
|
||||||
|
</div>
|
||||||
|
</rt-template>
|
||||||
|
</div>
|
15
test/data/propTemplates/twoTemplates.rt.js
Normal file
15
test/data/propTemplates/twoTemplates.rt.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
define([
|
||||||
|
'react/addons',
|
||||||
|
'lodash'
|
||||||
|
], function (React, _) {
|
||||||
|
'use strict';
|
||||||
|
function templateProp21(arg1, inner1, inner2) {
|
||||||
|
return React.createElement('div', {}, arg1 + inner1 + inner2);
|
||||||
|
}
|
||||||
|
function templateProp2(arg1) {
|
||||||
|
return React.createElement('div', { 'templateProp2': templateProp21.bind(this, arg1) }, React.createElement('div', {}, arg1));
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
return React.createElement('div', { 'templateProp': templateProp2.bind(this) });
|
||||||
|
};
|
||||||
|
});
|
@ -114,6 +114,28 @@ test('conversion test', function (t) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('prop template conversion test', function (t) {
|
||||||
|
var options = {
|
||||||
|
templates: {
|
||||||
|
List: {
|
||||||
|
Row: {prop: 'renderRow', arguments: ['rowData']}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var files = ['propTemplates/simpleTemplate.rt', 'propTemplates/templateInScope.rt', 'propTemplates/implicitTemplate.rt', 'propTemplates/twoTemplates.rt'];
|
||||||
|
t.plan(files.length);
|
||||||
|
files.forEach(check);
|
||||||
|
|
||||||
|
function check(testFile) {
|
||||||
|
var filename = path.join(dataPath, testFile);
|
||||||
|
var html = readFileNormalized(filename);
|
||||||
|
var expected = readFileNormalized(filename + '.js');
|
||||||
|
var actual = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '').trim();
|
||||||
|
compareAndWrite(t, actual, expected, filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {*} t
|
* @param {*} t
|
||||||
* @param {string} actual
|
* @param {string} actual
|
||||||
|
Loading…
x
Reference in New Issue
Block a user