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-class](#rt-class)
|
||||
* [rt-require](#rt-require)
|
||||
* [rt-template](#rt-template)
|
||||
* [styles](#styles)
|
||||
* [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)
|
||||
* 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
|
||||
|
||||
## 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.
|
||||
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",
|
||||
"istanbul": "^0.3.17",
|
||||
"react": "^0.12.2",
|
||||
"react-native": "^0.9.0",
|
||||
"tape": "^4.0.1"
|
||||
},
|
||||
"keywords": [
|
||||
|
@ -11,6 +11,7 @@
|
||||
var optionator = require('optionator');
|
||||
var pkg = require('../package.json');
|
||||
var reactDOMSupport = require('./reactDOMSupport');
|
||||
var reactNativeSupport = require('./reactNativeSupport');
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Initialization and Public Interface
|
||||
@ -74,7 +75,7 @@ module.exports = optionator({
|
||||
option: 'target-version',
|
||||
alias: 't',
|
||||
type: 'String',
|
||||
default: '0.13.1',
|
||||
default: reactDOMSupport.default,
|
||||
description: 'React version to generate code for (' + Object.keys(reactDOMSupport).join(', ') + ')'
|
||||
}, {
|
||||
option: 'list-target-version',
|
||||
@ -101,5 +102,16 @@ module.exports = optionator({
|
||||
default: 'lodash',
|
||||
type: 'String',
|
||||
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.1': 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;
|
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 escodegen = require('escodegen');
|
||||
var reactDOMSupport = require('./reactDOMSupport');
|
||||
var reactNativeSupport = require('./reactNativeSupport');
|
||||
var reactPropTemplates = require('./reactPropTemplates');
|
||||
var stringUtils = require('./stringUtils');
|
||||
var rtError = require('./RTCodeError');
|
||||
var RTCodeError = rtError.RTCodeError;
|
||||
@ -55,7 +57,29 @@ var scopeAttr = 'rt-scope';
|
||||
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'};
|
||||
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
|
||||
@ -181,7 +205,7 @@ function generateInjectedFunc(context, namePrefix, body, params) {
|
||||
}
|
||||
|
||||
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)
|
||||
.map(function (child, index) {
|
||||
var templateProp = null;
|
||||
@ -190,7 +214,7 @@ function generateTemplateProps(node, context) {
|
||||
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 = {
|
||||
prop: child.attribs.prop,
|
||||
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
|
||||
@ -300,6 +324,10 @@ function generateProps(node, context) {
|
||||
* @return {string}
|
||||
*/
|
||||
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);
|
||||
if (shouldUseCreateElement(context)) {
|
||||
isHtmlTag = isHtmlTag || tagName.match(/^\w+(-\w+)$/);
|
||||
@ -508,7 +536,7 @@ function convertTemplateToReact(html, options) {
|
||||
*/
|
||||
function convertRT(html, reportContext, options) {
|
||||
var rootNode = cheerio.load(html, {lowerCaseTags: false, lowerCaseAttributeNames: false, xmlMode: true, withStartIndices: true});
|
||||
options = _.defaults({}, options, defaultOptions);
|
||||
options = getOptions(options);
|
||||
|
||||
var defaultDefines = {};
|
||||
defaultDefines[options.reactImportPath] = 'React';
|
||||
@ -576,7 +604,7 @@ function convertRT(html, reportContext, options) {
|
||||
}
|
||||
|
||||
function convertJSRTToJS(text, reportContext, options) {
|
||||
options = _.defaults({}, options, defaultOptions);
|
||||
options = getOptions(options)
|
||||
options.modules = 'jsrt';
|
||||
var templateMatcherJSRT = /<template>([^]*?)<\/template>/gm;
|
||||
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) {
|
||||
var options = {
|
||||
templates: {
|
||||
propTemplates: {
|
||||
List: {
|
||||
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 {string} actual
|
||||
|
Loading…
x
Reference in New Issue
Block a user