mirror of
https://github.com/bobwen-dev/react-templates
synced 2025-04-12 00:56:39 +02:00
Merge pull request #56 from nirnatan/gh-pages
Add support for React Native
This commit is contained in:
commit
e60d366279
@ -40,6 +40,7 @@ http://plugins.jetbrains.com/plugin/7648
|
|||||||
* [rt-props](#rt-props)
|
* [rt-props](#rt-props)
|
||||||
* [rt-class](#rt-class)
|
* [rt-class](#rt-class)
|
||||||
* [rt-require](#rt-require)
|
* [rt-require](#rt-require)
|
||||||
|
* [rt-template](#rt-template)
|
||||||
* [styles](#styles)
|
* [styles](#styles)
|
||||||
* [event handlers](#event-handlers)
|
* [event handlers](#event-handlers)
|
||||||
|
|
||||||
@ -66,6 +67,9 @@ In most cases, this package will be wrapped in a build task, so CLI will not be
|
|||||||
* Browserify plugin: [react-templatify](https://www.npmjs.com/package/react-templatify)
|
* Browserify plugin: [react-templatify](https://www.npmjs.com/package/react-templatify)
|
||||||
* Webpack loader : [react-templates-loader](https://github.com/AlexanderPavlenko/react-templates-loader)
|
* Webpack loader : [react-templates-loader](https://github.com/AlexanderPavlenko/react-templates-loader)
|
||||||
|
|
||||||
|
### Use React Templates for Native Apps?
|
||||||
|
You can get all the react templates functionality and more. [Click here for more info](https://github.com/wix/react-templates/blob/gh-pages/docs/native.md)
|
||||||
|
|
||||||
# Template directives and syntax
|
# Template directives and syntax
|
||||||
|
|
||||||
## Any valid HTML is a template
|
## Any valid HTML is a template
|
||||||
@ -399,7 +403,7 @@ define([
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## properties template functions
|
## rt-template, and defining properties template functions
|
||||||
In cases you'd like to use a property that accepts a function and return renderable React component.
|
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"/>`.
|
You should use a **rt-template** tag that will let you do exactly that: `<rt-template prop="propName" arguments="arg1, arg2"/>`.
|
||||||
|
|
||||||
|
80
docs/native.md
Normal file
80
docs/native.md
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# React Template for Native Apps
|
||||||
|
|
||||||
|
In order to use React Templates for [React Native](https://facebook.github.io/react-native/) you should set the `native` option to true.
|
||||||
|
In native mode the default `modules` option is set to `commonjs` and the default `react-import-path` is set to `react-native`.
|
||||||
|
|
||||||
|
## Default properties templates configuration
|
||||||
|
|
||||||
|
In native mode we define a default properties template configuration in order to easily write native templates.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
ListView: {
|
||||||
|
Row: {prop: 'renderRow', arguments: ['rowData', 'sectionID', 'rowID', 'highlightRow']},
|
||||||
|
Footer: {prop: 'renderFooter', arguments: []},
|
||||||
|
Header: {prop: 'renderHeader', arguments: []},
|
||||||
|
ScrollComponent: {prop: 'renderScrollComponent', arguments: ['props']},
|
||||||
|
SectionHeader: {prop: 'renderSectionHeader', arguments: ['sectionData', 'sectionID']},
|
||||||
|
Separator: {prop: 'renderSeparator', arguments: ['sectionID', 'rowID', 'adjacentRowHighlighted']}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this configuration you can write your ListView component as follow:
|
||||||
|
|
||||||
|
##### With default arguments:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<View>
|
||||||
|
<ListView dataSource="{this.state.dataSource}">
|
||||||
|
<Row>
|
||||||
|
<Text>{rowData}</Text>
|
||||||
|
</Row>
|
||||||
|
</ListView>
|
||||||
|
</View>
|
||||||
|
```
|
||||||
|
|
||||||
|
###### Compiled:
|
||||||
|
```javascript
|
||||||
|
'use strict';
|
||||||
|
var React = require('react-native');
|
||||||
|
var _ = require('lodash');
|
||||||
|
function renderRow1(rowData) {
|
||||||
|
return React.createElement(React.Text, {}, rowData);
|
||||||
|
}
|
||||||
|
module.exports = function () {
|
||||||
|
return React.createElement(React.View, {}, React.createElement(React.ListView, {
|
||||||
|
'dataSource': this.state.dataSource,
|
||||||
|
'renderRow': renderRow1.bind(this)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
##### With custom arguments:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<View>
|
||||||
|
<ListView dataSource="{this.state.dataSource}">
|
||||||
|
<Row arguments="item">
|
||||||
|
<Text>{item}</Text>
|
||||||
|
</Row>
|
||||||
|
</ListView>
|
||||||
|
</View>
|
||||||
|
```
|
||||||
|
|
||||||
|
###### Compiled:
|
||||||
|
```javascript
|
||||||
|
'use strict';
|
||||||
|
var React = require('react-native');
|
||||||
|
var _ = require('lodash');
|
||||||
|
function renderRow1(item) {
|
||||||
|
return React.createElement(React.Text, {}, item);
|
||||||
|
}
|
||||||
|
module.exports = function () {
|
||||||
|
return React.createElement(React.View, {}, React.createElement(React.ListView, {
|
||||||
|
'dataSource': this.state.dataSource,
|
||||||
|
'renderRow': renderRow1.bind(this)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
@ -47,6 +47,7 @@
|
|||||||
"grunt-node-tap": "^0.1.61",
|
"grunt-node-tap": "^0.1.61",
|
||||||
"istanbul": "^0.3.17",
|
"istanbul": "^0.3.17",
|
||||||
"react": "^0.12.2",
|
"react": "^0.12.2",
|
||||||
|
"react-native": "^0.9.0",
|
||||||
"tape": "^4.0.1"
|
"tape": "^4.0.1"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
var optionator = require('optionator');
|
var optionator = require('optionator');
|
||||||
var pkg = require('../package.json');
|
var pkg = require('../package.json');
|
||||||
var reactDOMSupport = require('./reactDOMSupport');
|
var reactDOMSupport = require('./reactDOMSupport');
|
||||||
|
var reactNativeSupport = require('./reactNativeSupport');
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Initialization and Public Interface
|
// Initialization and Public Interface
|
||||||
@ -74,7 +75,7 @@ module.exports = optionator({
|
|||||||
option: 'target-version',
|
option: 'target-version',
|
||||||
alias: 't',
|
alias: 't',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
default: '0.13.1',
|
default: reactDOMSupport.default,
|
||||||
description: 'React version to generate code for (' + Object.keys(reactDOMSupport).join(', ') + ')'
|
description: 'React version to generate code for (' + Object.keys(reactDOMSupport).join(', ') + ')'
|
||||||
}, {
|
}, {
|
||||||
option: 'list-target-version',
|
option: 'list-target-version',
|
||||||
@ -101,5 +102,16 @@ module.exports = optionator({
|
|||||||
default: 'lodash',
|
default: 'lodash',
|
||||||
type: 'String',
|
type: 'String',
|
||||||
description: 'Dependency path for importing lodash.'
|
description: 'Dependency path for importing lodash.'
|
||||||
|
}, {
|
||||||
|
option: 'native',
|
||||||
|
alias: 'rn',
|
||||||
|
type: 'Boolean',
|
||||||
|
description: 'Renders react native templates.'
|
||||||
|
}, {
|
||||||
|
option: 'native-target-version',
|
||||||
|
alias: 'rnv',
|
||||||
|
type: 'String',
|
||||||
|
default: reactNativeSupport.default,
|
||||||
|
description: 'React native version to generate code for (' + Object.keys(reactNativeSupport).join(', ') + ')'
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,8 @@ var versions = {
|
|||||||
'0.11.2': ver0_11_2,
|
'0.11.2': ver0_11_2,
|
||||||
'0.11.1': ver0_11_0,
|
'0.11.1': ver0_11_0,
|
||||||
'0.11.0': ver0_11_0,
|
'0.11.0': ver0_11_0,
|
||||||
'0.10.0': ver0_10_0
|
'0.10.0': ver0_10_0,
|
||||||
|
default: '0.13.1'
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = versions;
|
module.exports = versions;
|
10
src/reactNativeSupport.js
Normal file
10
src/reactNativeSupport.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ver0_9_0 = ['ActivityIndicatorIOS', 'DatePickerIOS', 'Image', 'ListView', 'MapView', 'Navigator', 'NavigatorIOS', 'PickerIOS', 'ScrollView', 'SliderIOS', 'SwitchIOS', 'TabBarIOS', 'Text', 'TextInput', 'TouchableHighlight', 'TouchableOpacity', 'TouchableWithoutFeedback', 'View', 'WebView'];
|
||||||
|
|
||||||
|
var versions = {
|
||||||
|
'0.9.0': ver0_9_0,
|
||||||
|
default: '0.9.0'
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = versions;
|
19
src/reactPropTemplates.js
Normal file
19
src/reactPropTemplates.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var native = {
|
||||||
|
'0.9.0': {
|
||||||
|
ListView: {
|
||||||
|
Row: {prop: 'renderRow', arguments: ['rowData', 'sectionID', 'rowID', 'highlightRow']},
|
||||||
|
Footer: {prop: 'renderFooter', arguments: []},
|
||||||
|
Header: {prop: 'renderHeader', arguments: []},
|
||||||
|
ScrollComponent: {prop: 'renderScrollComponent', arguments: ['props']},
|
||||||
|
SectionHeader: {prop: 'renderSectionHeader', arguments: ['sectionData', 'sectionID']},
|
||||||
|
Separator: {prop: 'renderSeparator', arguments: ['sectionID', 'rowID', 'adjacentRowHighlighted']}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
native: native,
|
||||||
|
dom: {}
|
||||||
|
};
|
@ -7,6 +7,8 @@ var _ = require('lodash');
|
|||||||
var esprima = require('esprima-harmony');
|
var esprima = require('esprima-harmony');
|
||||||
var escodegen = require('escodegen');
|
var escodegen = require('escodegen');
|
||||||
var reactDOMSupport = require('./reactDOMSupport');
|
var reactDOMSupport = require('./reactDOMSupport');
|
||||||
|
var reactNativeSupport = require('./reactNativeSupport');
|
||||||
|
var reactPropTemplates = require('./reactPropTemplates');
|
||||||
var stringUtils = require('./stringUtils');
|
var stringUtils = require('./stringUtils');
|
||||||
var rtError = require('./RTCodeError');
|
var rtError = require('./RTCodeError');
|
||||||
var RTCodeError = rtError.RTCodeError;
|
var RTCodeError = rtError.RTCodeError;
|
||||||
@ -55,7 +57,29 @@ var scopeAttr = 'rt-scope';
|
|||||||
var propsAttr = 'rt-props';
|
var propsAttr = 'rt-props';
|
||||||
var templateNode = 'rt-template';
|
var templateNode = 'rt-template';
|
||||||
|
|
||||||
var defaultOptions = {modules: 'amd', version: false, force: false, format: 'stylish', targetVersion: '0.13.1', reactImportPath: 'react/addons', lodashImportPath: 'lodash'};
|
function getOptions(options) {
|
||||||
|
options = options || {};
|
||||||
|
var defaultOptions = {
|
||||||
|
modules: options.native ? 'commonjs': 'amd',
|
||||||
|
version: false,
|
||||||
|
force: false,
|
||||||
|
format: 'stylish',
|
||||||
|
targetVersion: reactDOMSupport.default,
|
||||||
|
reactImportPath: options.native ? 'react-native' : 'react/addons',
|
||||||
|
lodashImportPath: 'lodash',
|
||||||
|
native: false,
|
||||||
|
nativeTargetVersion: reactNativeSupport.default
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalOptions = _.defaults({}, options, defaultOptions);
|
||||||
|
|
||||||
|
var defaultPropTemplates = finalOptions.native ?
|
||||||
|
reactPropTemplates.native[finalOptions.nativeTargetVersion] :
|
||||||
|
reactPropTemplates.dom[finalOptions.targetVersion];
|
||||||
|
|
||||||
|
finalOptions.propTemplates = _.defaults({}, options.propTemplates, defaultPropTemplates);
|
||||||
|
return finalOptions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Context} context
|
* @param {Context} context
|
||||||
@ -181,7 +205,7 @@ function generateInjectedFunc(context, namePrefix, body, params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generateTemplateProps(node, context) {
|
function generateTemplateProps(node, context) {
|
||||||
var propTemplateDefinition = context.options.templates && context.options.templates[node.name];
|
var propTemplateDefinition = context.options.propTemplates[node.name];
|
||||||
var propertiesTemplates = _(node.children)
|
var propertiesTemplates = _(node.children)
|
||||||
.map(function (child, index) {
|
.map(function (child, index) {
|
||||||
var templateProp = null;
|
var templateProp = null;
|
||||||
@ -190,7 +214,7 @@ function generateTemplateProps(node, context) {
|
|||||||
throw RTCodeError.build('rt-template must have a prop attribute', context, child);
|
throw RTCodeError.build('rt-template must have a prop attribute', context, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
var childTemplate = _.find(context.options.templates, {prop: child.attribs.prop}) || {arguments: []};
|
var childTemplate = _.find(context.options.propTemplates, {prop: child.attribs.prop}) || {arguments: []};
|
||||||
templateProp = {
|
templateProp = {
|
||||||
prop: child.attribs.prop,
|
prop: child.attribs.prop,
|
||||||
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
|
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
|
||||||
@ -300,6 +324,10 @@ function generateProps(node, context) {
|
|||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
function convertTagNameToConstructor(tagName, context) {
|
function convertTagNameToConstructor(tagName, context) {
|
||||||
|
if (context.options.native) {
|
||||||
|
return _.includes(reactNativeSupport[context.options.nativeTargetVersion], tagName) ? 'React.' + tagName : tagName;
|
||||||
|
}
|
||||||
|
|
||||||
var isHtmlTag = _.includes(reactDOMSupport[context.options.targetVersion], tagName);
|
var isHtmlTag = _.includes(reactDOMSupport[context.options.targetVersion], tagName);
|
||||||
if (shouldUseCreateElement(context)) {
|
if (shouldUseCreateElement(context)) {
|
||||||
isHtmlTag = isHtmlTag || tagName.match(/^\w+(-\w+)$/);
|
isHtmlTag = isHtmlTag || tagName.match(/^\w+(-\w+)$/);
|
||||||
@ -508,7 +536,7 @@ function convertTemplateToReact(html, options) {
|
|||||||
*/
|
*/
|
||||||
function convertRT(html, reportContext, options) {
|
function convertRT(html, reportContext, options) {
|
||||||
var rootNode = cheerio.load(html, {lowerCaseTags: false, lowerCaseAttributeNames: false, xmlMode: true, withStartIndices: true});
|
var rootNode = cheerio.load(html, {lowerCaseTags: false, lowerCaseAttributeNames: false, xmlMode: true, withStartIndices: true});
|
||||||
options = _.defaults({}, options, defaultOptions);
|
options = getOptions(options);
|
||||||
|
|
||||||
var defaultDefines = {};
|
var defaultDefines = {};
|
||||||
defaultDefines[options.reactImportPath] = 'React';
|
defaultDefines[options.reactImportPath] = 'React';
|
||||||
@ -576,7 +604,7 @@ function convertRT(html, reportContext, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertJSRTToJS(text, reportContext, options) {
|
function convertJSRTToJS(text, reportContext, options) {
|
||||||
options = _.defaults({}, options, defaultOptions);
|
options = getOptions(options)
|
||||||
options.modules = 'jsrt';
|
options.modules = 'jsrt';
|
||||||
var templateMatcherJSRT = /<template>([^]*?)<\/template>/gm;
|
var templateMatcherJSRT = /<template>([^]*?)<\/template>/gm;
|
||||||
var code = text.replace(templateMatcherJSRT, function (template, html) {
|
var code = text.replace(templateMatcherJSRT, function (template, html) {
|
||||||
|
12
test/data/listViewAndCustomTemplate.rt
Normal file
12
test/data/listViewAndCustomTemplate.rt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<View>
|
||||||
|
<ListView dataSource="{this.state.dataSource}">
|
||||||
|
<Row arguments="rowData">
|
||||||
|
<Text>{rowData}</Text>
|
||||||
|
</Row>
|
||||||
|
</ListView>
|
||||||
|
<MyComp data="{[1,2,3]}">
|
||||||
|
<Row arguments="item">
|
||||||
|
<Text>{item}</Text>
|
||||||
|
</Row>
|
||||||
|
</MyComp>
|
||||||
|
</View>
|
22
test/data/listViewAndCustomTemplate.rt.js
Normal file
22
test/data/listViewAndCustomTemplate.rt.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
var React = require('react-native');
|
||||||
|
var _ = require('lodash');
|
||||||
|
function renderRow1(rowData) {
|
||||||
|
return React.createElement(React.Text, {}, rowData);
|
||||||
|
}
|
||||||
|
function renderRow2(item) {
|
||||||
|
return React.createElement(React.Text, {}, item);
|
||||||
|
}
|
||||||
|
module.exports = function () {
|
||||||
|
return React.createElement(React.View, {}, React.createElement(React.ListView, {
|
||||||
|
'dataSource': this.state.dataSource,
|
||||||
|
'renderRow': renderRow1.bind(this)
|
||||||
|
}), React.createElement(MyComp, {
|
||||||
|
'data': [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
'renderRow': renderRow2.bind(this)
|
||||||
|
}));
|
||||||
|
};
|
7
test/data/listViewTemplate.rt
Normal file
7
test/data/listViewTemplate.rt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<View>
|
||||||
|
<ListView dataSource="{this.state.dataSource}">
|
||||||
|
<Row arguments="rowData">
|
||||||
|
<Text>{rowData}</Text>
|
||||||
|
</Row>
|
||||||
|
</ListView>
|
||||||
|
</View>
|
12
test/data/listViewTemplate.rt.js
Normal file
12
test/data/listViewTemplate.rt.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
var React = require('react-native');
|
||||||
|
var _ = require('lodash');
|
||||||
|
function renderRow1(rowData) {
|
||||||
|
return React.createElement(React.Text, {}, rowData);
|
||||||
|
}
|
||||||
|
module.exports = function () {
|
||||||
|
return React.createElement(React.View, {}, React.createElement(React.ListView, {
|
||||||
|
'dataSource': this.state.dataSource,
|
||||||
|
'renderRow': renderRow1.bind(this)
|
||||||
|
}));
|
||||||
|
};
|
1
test/data/nativeView.rt
Normal file
1
test/data/nativeView.rt
Normal file
@ -0,0 +1 @@
|
|||||||
|
<View></View>
|
6
test/data/nativeView.rt.js
Normal file
6
test/data/nativeView.rt.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
'use strict';
|
||||||
|
var React = require('react-native');
|
||||||
|
var _ = require('lodash');
|
||||||
|
module.exports = function () {
|
||||||
|
return React.createElement(React.View, {});
|
||||||
|
};
|
@ -116,7 +116,7 @@ test('conversion test', function (t) {
|
|||||||
|
|
||||||
test('prop template conversion test', function (t) {
|
test('prop template conversion test', function (t) {
|
||||||
var options = {
|
var options = {
|
||||||
templates: {
|
propTemplates: {
|
||||||
List: {
|
List: {
|
||||||
Row: {prop: 'renderRow', arguments: ['rowData']}
|
Row: {prop: 'renderRow', arguments: ['rowData']}
|
||||||
}
|
}
|
||||||
@ -136,6 +136,30 @@ test('prop template conversion test', function (t) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('conversion test - native', function (t) {
|
||||||
|
var options = {
|
||||||
|
propTemplates: {
|
||||||
|
MyComp: {
|
||||||
|
Row: {prop: 'renderRow', arguments: ['rowData']}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
native: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var files = ['nativeView.rt', 'listViewTemplate.rt', 'listViewAndCustomTemplate.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 expected = fs.readFileSync(filename.replace(".html", ".js")).toString();
|
||||||
|
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