Skip to content

Commit 0e763fe

Browse files
author
Christopher J. Brody
committed
native view option on Android & iOS
1 parent e0361e8 commit 0e763fe

7 files changed

Lines changed: 158 additions & 15 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# create-react-native-module
22

3-
Tool to create a React Native library module with a single command (based on [`react-native-create-library`](https://www.npmjs.com/package/react-native-create-library))
3+
Tool to create a React Native library module, optionally as an extremely simple view component, with a single command (based on [`react-native-create-library`](https://www.npmjs.com/package/react-native-create-library))
44

55
<!-- GONE:
66
![](https://github.com/frostney/react-native-create-library/blob/master/docs/usage.gif)
@@ -15,8 +15,6 @@ If you are looking to create a native module for React Native, you need some nat
1515
This is where this tool comes in. It creates a boilerplate with all current best practices in mind.
1616
Why not use `react-native new-library`? Unfortunately that command doesn't create an up-to-date library, requires an already initialized React Native project and only sets up the iOS side of things.
1717

18-
Caution: This only creates native modules without a view component.
19-
2018
### Alternatives
2119

2220
- [`react-native-create-library`](https://www.npmjs.com/package/react-native-create-library)
@@ -58,6 +56,7 @@ Options:
5856
--author-name <name> The author's name (Default: `Your Name`)
5957
--author-email <email> The author's email (Default: `yourname@email.com`)
6058
--license <license> The license type of this library (Default: `Apache-2.0`)
59+
--view Generate the module as a very simple native view component (Default: `false`)
6160
--generate-example <shouldGenerate> Generate an example project and links the library module to it, requires both react-native-cli and yarn to be installed globally (Default: `false`)
6261
```
6362

@@ -86,6 +85,7 @@ createLibrary({
8685
authorName: String, /* The author's name (Default: `Your Name`) */
8786
authorEmail: String, /* The author's email (Default: `yourname@email.com`) */
8887
license: String, /* The license type of this library (Default: `Apache-2.0`) */
88+
view: Boolean, /* Generate the module as a very simple native view component (Default: `false`) */
8989
generateExample: Boolean, /* Generate an example project and links the library module to it, requires both react-native-cli and yarn to be installed globally (Default: `false`) */
9090
}
9191
```

command.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = {
1717
const authorName = options.authorName;
1818
const authorEmail = options.authorEmail;
1919
const license = options.license;
20+
const view = options.view;
2021
const generateExample = options.generateExample;
2122

2223
const beforeCreation = Date.now();
@@ -31,6 +32,7 @@ module.exports = {
3132
authorName,
3233
authorEmail,
3334
license,
35+
view,
3436
generateExample,
3537
}).then(() => {
3638
console.log(`
@@ -80,6 +82,9 @@ ${emoji.get('arrow_right')} To get started type \`cd ./${name}\` and run \`npm
8082
command: '--license [license]',
8183
description: 'The license type (Default: `Apache-2.0`)',
8284
default: 'Apache-2.0',
85+
}, {
86+
command: '--view',
87+
description: 'Generate the module as a very simple native view component (Default: `false`)',
8388
}, {
8489
command: '--generate-example',
8590
description: 'Generate an example project and links the library module to it, requires both react-native-cli and yarn to be installed globally (Default: `false`)',

lib.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ module.exports = ({
4545
authorName = DEFAULT_AUTHOR_NAME,
4646
authorEmail = DEFAULT_AUTHOR_EMAIL,
4747
license = DEFAULT_LICENSE,
48+
view = false,
4849
generateExample = DEFAULT_GENERATE_EXAMPLE,
4950
}) => {
5051
if (!overridePrefix) {
@@ -119,6 +120,7 @@ module.exports = ({
119120
authorName,
120121
authorEmail,
121122
license,
123+
view,
122124
generateExample,
123125
};
124126

@@ -140,6 +142,7 @@ module.exports = ({
140142
const templateArgs = {
141143
name: className,
142144
moduleName,
145+
view,
143146
};
144147

145148
return Promise.all(

templates/android.js

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,13 @@ afterEvaluate { project ->
133133
</manifest>
134134
`,
135135
}, {
136-
name: ({ packageIdentifier, name }) =>
137-
`${platform}/src/main/java/${packageIdentifier.split('.').join('/')}/${name}Module.java`,
138-
content: ({ packageIdentifier, name }) => `package ${packageIdentifier};
136+
// for module without view:
137+
name: ({ packageIdentifier, name, view }) =>
138+
!view &&
139+
`${platform}/src/main/java/${packageIdentifier.split('.').join('/')}/${name}Module.java`,
140+
content: ({ packageIdentifier, name, view }) =>
141+
!view &&
142+
`package ${packageIdentifier};
139143
140144
import com.facebook.react.bridge.ReactApplicationContext;
141145
import com.facebook.react.bridge.ReactContextBaseJavaModule;
@@ -164,9 +168,47 @@ public class ${name}Module extends ReactContextBaseJavaModule {
164168
}
165169
`,
166170
}, {
167-
name: ({ packageIdentifier, name }) =>
168-
`${platform}/src/main/java/${packageIdentifier.split('.').join('/')}/${name}Package.java`,
169-
content: ({ packageIdentifier, name }) => `package ${packageIdentifier};
171+
// manager for view:
172+
name: ({ packageIdentifier, name, view }) =>
173+
view &&
174+
`${platform}/src/main/java/${packageIdentifier.split('.').join('/')}/${name}Manager.java`,
175+
content: ({ packageIdentifier, name, view }) =>
176+
view &&
177+
`package ${packageIdentifier};
178+
179+
import android.view.View;
180+
181+
import android.support.v7.widget.AppCompatCheckBox;
182+
183+
import com.facebook.react.uimanager.SimpleViewManager;
184+
import com.facebook.react.uimanager.ThemedReactContext;
185+
186+
public class ${name}Manager extends SimpleViewManager<View> {
187+
188+
public static final String REACT_CLASS = "${name}";
189+
190+
@Override
191+
public String getName() {
192+
return REACT_CLASS;
193+
}
194+
195+
@Override
196+
public View createViewInstance(ThemedReactContext c) {
197+
// TODO: Implement some real useful functionality
198+
AppCompatCheckBox cb = new AppCompatCheckBox(c);
199+
cb.setChecked(true);
200+
return cb;
201+
}
202+
}
203+
`,
204+
}, {
205+
// package for module without view:
206+
name: ({ packageIdentifier, name, view }) =>
207+
!view &&
208+
`${platform}/src/main/java/${packageIdentifier.split('.').join('/')}/${name}Package.java`,
209+
content: ({ packageIdentifier, name, view }) =>
210+
!view &&
211+
`package ${packageIdentifier};
170212
171213
import java.util.Arrays;
172214
import java.util.Collections;
@@ -190,6 +232,38 @@ public class ${name}Package implements ReactPackage {
190232
}
191233
}
192234
`,
235+
}, {
236+
// package for manager for view:
237+
name: ({ packageIdentifier, name, view }) =>
238+
view &&
239+
`${platform}/src/main/java/${packageIdentifier.split('.').join('/')}/${name}Package.java`,
240+
content: ({ packageIdentifier, name, view }) =>
241+
view &&
242+
`package ${packageIdentifier};
243+
244+
import java.util.Arrays;
245+
import java.util.Collections;
246+
import java.util.List;
247+
248+
import com.facebook.react.ReactPackage;
249+
import com.facebook.react.bridge.NativeModule;
250+
import com.facebook.react.bridge.ReactApplicationContext;
251+
import com.facebook.react.uimanager.ViewManager;
252+
import com.facebook.react.bridge.JavaScriptModule;
253+
254+
public class ${name}Package implements ReactPackage {
255+
@Override
256+
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
257+
return Collections.emptyList();
258+
}
259+
260+
@Override
261+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
262+
return Arrays.<ViewManager>asList(new ${name}Manager());
263+
}
264+
}
265+
`,
266+
}, {
193267
}, {
194268
name: () => `${platform}/README.md`,
195269
content: () => `README

templates/example.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ module.exports = [{
117117
`
118118
}, {
119119
name: () => 'example/App.js',
120-
content: ({ moduleName, name }) =>
120+
content: ({ moduleName, name, view }) =>
121121
`/**
122122
* Sample React Native App
123123
*
@@ -130,7 +130,9 @@ module.exports = [{
130130
131131
import React, { Component } from 'react';
132132
import { Platform, StyleSheet, Text, View } from 'react-native';
133-
import ${name} from '${moduleName}';
133+
import ${name} from '${moduleName}';` +
134+
(!view
135+
? `
134136
135137
export default class App extends Component<{}> {
136138
state = {
@@ -155,7 +157,22 @@ export default class App extends Component<{}> {
155157
</View>
156158
);
157159
}
158-
}
160+
}`
161+
: `
162+
163+
export default class App extends Component<{}> {
164+
render() {
165+
return (
166+
<View style={styles.container}>
167+
<Text style={styles.welcome}>☆${name} example☆</Text>
168+
<Text style={styles.instructions}>STATUS: loaded</Text>
169+
<Text style={styles.welcome}>☆☆☆</Text>
170+
<${name} />
171+
</View>
172+
);
173+
}
174+
}`) +
175+
`
159176
160177
const styles = StyleSheet.create({
161178
container: {

templates/general.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,21 @@ ${name};
112112
`;
113113
},
114114
}, {
115-
name: () => 'index.js',
115+
// for module without view:
116+
name: ({ view }) => !view && 'index.js',
116117
content: ({ name }) =>`import { NativeModules } from 'react-native';
117118
118119
const { ${name} } = NativeModules;
119120
121+
export default ${name};
122+
`,
123+
}, {
124+
// for module with view:
125+
name: ({ view }) => view && 'index.js',
126+
content: ({ name }) =>`import { requireNativeComponent } from 'react-native';
127+
128+
const ${name} = requireNativeComponent('${name}', null);
129+
120130
export default ${name};
121131
`,
122132
}, {

templates/ios.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,17 @@ end
2929
3030
`,
3131
}, {
32-
name: ({ name }) => `${platform}/${name}.h`,
32+
// header for module without view:
33+
name: ({ name, view }) => !view && `${platform}/${name}.h`,
3334
content: ({ name }) => `#import <React/RCTBridgeModule.h>
3435
3536
@interface ${name} : NSObject <RCTBridgeModule>
3637
3738
@end
3839
`,
3940
}, {
40-
name: ({ name }) => `${platform}/${name}.m`,
41+
// implementation of module without view:
42+
name: ({ name, view }) => !view && `${platform}/${name}.m`,
4143
content: ({ name }) => `#import "${name}.h"
4244
4345
@implementation ${name}
@@ -50,6 +52,38 @@ RCT_EXPORT_METHOD(sampleMethod:(NSString *)stringArgument numberParameter:(nonnu
5052
callback(@[[NSString stringWithFormat: @"numberArgument: %@ stringArgument: %@", numberArgument, stringArgument]]);
5153
}
5254
55+
@end
56+
`,
57+
}, {
58+
// header for module with view:
59+
name: ({ name, view }) => view && `${platform}/${name}.h`,
60+
content: ({ name }) => `#import <React/RCTViewManager.h>
61+
62+
@interface ${name} : RCTViewManager
63+
64+
@end
65+
`,
66+
}, {
67+
// implementation of module with view:
68+
name: ({ name, view }) => view && `${platform}/${name}.m`,
69+
content: ({ name }) => `#import "${name}.h"
70+
71+
@implementation ${name}
72+
73+
RCT_EXPORT_MODULE()
74+
75+
- (UIView *)view
76+
{
77+
// TODO: Implement some real useful functionality
78+
UILabel * label = [[UILabel alloc] init];
79+
[label setTextColor:[UIColor redColor]];
80+
[label setText: @"*****"];
81+
[label sizeToFit];
82+
UIView * wrapper = [[UIView alloc] init];
83+
[wrapper addSubview:label];
84+
return wrapper;
85+
}
86+
5387
@end
5488
`,
5589
}, {

0 commit comments

Comments
 (0)