nutella_framework 0.4.27 → 0.4.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/VERSION +1 -1
  4. data/framework_components/order.json +2 -1
  5. data/framework_components/room-places-interface/.gitignore +3 -0
  6. data/framework_components/room-places-interface/LICENSE +21 -0
  7. data/framework_components/room-places-interface/Readme.md +0 -0
  8. data/framework_components/room-places-interface/bower.json +29 -0
  9. data/framework_components/room-places-interface/bower_components/bower-mqttws/.bower.json +23 -0
  10. data/framework_components/room-places-interface/bower_components/bower-mqttws/bower.json +14 -0
  11. data/framework_components/room-places-interface/bower_components/bower-mqttws/mqttws31.js +2081 -0
  12. data/framework_components/room-places-interface/bower_components/bower-mqttws/readme.md +4 -0
  13. data/framework_components/room-places-interface/bower_components/nutella_lib/.bower.json +37 -0
  14. data/framework_components/room-places-interface/bower_components/nutella_lib/LICENSE +21 -0
  15. data/framework_components/room-places-interface/bower_components/nutella_lib/README.md +15 -0
  16. data/framework_components/room-places-interface/bower_components/nutella_lib/bower.json +28 -0
  17. data/framework_components/room-places-interface/bower_components/nutella_lib/examples/browser/mqtt_client_hello_world.html +23 -0
  18. data/framework_components/room-places-interface/bower_components/nutella_lib/examples/browser/nutella_hello_world.html +52 -0
  19. data/framework_components/room-places-interface/bower_components/nutella_lib/examples/node/mqtt_client_hello_world.js +14 -0
  20. data/framework_components/room-places-interface/bower_components/nutella_lib/examples/node/nutella_hello_world.js +38 -0
  21. data/framework_components/room-places-interface/bower_components/nutella_lib/nutella_lib.js +789 -0
  22. data/framework_components/room-places-interface/bower_components/nutella_lib/package.json +30 -0
  23. data/framework_components/room-places-interface/css/animation.css +22 -0
  24. data/framework_components/room-places-interface/css/bootstrap.min.css +5 -0
  25. data/framework_components/room-places-interface/css/cursor.css +22 -0
  26. data/framework_components/room-places-interface/css/font-awesome/css/font-awesome.css +1801 -0
  27. data/framework_components/room-places-interface/css/font-awesome/css/font-awesome.min.css +4 -0
  28. data/framework_components/room-places-interface/css/font-awesome/fonts/FontAwesome.otf +0 -0
  29. data/framework_components/room-places-interface/css/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  30. data/framework_components/room-places-interface/css/font-awesome/fonts/fontawesome-webfont.svg +565 -0
  31. data/framework_components/room-places-interface/css/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  32. data/framework_components/room-places-interface/css/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  33. data/framework_components/room-places-interface/css/font-awesome/fonts/fontawesome-webfont.woff2 +0 -0
  34. data/framework_components/room-places-interface/css/font-awesome/less/animated.less +34 -0
  35. data/framework_components/room-places-interface/css/font-awesome/less/bordered-pulled.less +16 -0
  36. data/framework_components/room-places-interface/css/font-awesome/less/core.less +13 -0
  37. data/framework_components/room-places-interface/css/font-awesome/less/fixed-width.less +6 -0
  38. data/framework_components/room-places-interface/css/font-awesome/less/font-awesome.less +17 -0
  39. data/framework_components/room-places-interface/css/font-awesome/less/icons.less +596 -0
  40. data/framework_components/room-places-interface/css/font-awesome/less/larger.less +13 -0
  41. data/framework_components/room-places-interface/css/font-awesome/less/list.less +19 -0
  42. data/framework_components/room-places-interface/css/font-awesome/less/mixins.less +27 -0
  43. data/framework_components/room-places-interface/css/font-awesome/less/path.less +15 -0
  44. data/framework_components/room-places-interface/css/font-awesome/less/rotated-flipped.less +20 -0
  45. data/framework_components/room-places-interface/css/font-awesome/less/stacked.less +20 -0
  46. data/framework_components/room-places-interface/css/font-awesome/less/variables.less +606 -0
  47. data/framework_components/room-places-interface/css/font-awesome/scss/_animated.scss +34 -0
  48. data/framework_components/room-places-interface/css/font-awesome/scss/_bordered-pulled.scss +16 -0
  49. data/framework_components/room-places-interface/css/font-awesome/scss/_core.scss +13 -0
  50. data/framework_components/room-places-interface/css/font-awesome/scss/_fixed-width.scss +6 -0
  51. data/framework_components/room-places-interface/css/font-awesome/scss/_icons.scss +596 -0
  52. data/framework_components/room-places-interface/css/font-awesome/scss/_larger.scss +13 -0
  53. data/framework_components/room-places-interface/css/font-awesome/scss/_list.scss +19 -0
  54. data/framework_components/room-places-interface/css/font-awesome/scss/_mixins.scss +27 -0
  55. data/framework_components/room-places-interface/css/font-awesome/scss/_path.scss +15 -0
  56. data/framework_components/room-places-interface/css/font-awesome/scss/_rotated-flipped.scss +20 -0
  57. data/framework_components/room-places-interface/css/font-awesome/scss/_stacked.scss +20 -0
  58. data/framework_components/room-places-interface/css/font-awesome/scss/_variables.scss +606 -0
  59. data/framework_components/room-places-interface/css/font-awesome/scss/font-awesome.scss +17 -0
  60. data/framework_components/room-places-interface/css/icomoon.css +57 -0
  61. data/framework_components/room-places-interface/css/page_layout.css +82 -0
  62. data/framework_components/room-places-interface/fonts/icomoon.eot +0 -0
  63. data/framework_components/room-places-interface/fonts/icomoon.svg +17 -0
  64. data/framework_components/room-places-interface/fonts/icomoon.ttf +0 -0
  65. data/framework_components/room-places-interface/fonts/icomoon.woff +0 -0
  66. data/framework_components/room-places-interface/gulpfile.js +24 -0
  67. data/framework_components/room-places-interface/index.html +73 -0
  68. data/framework_components/room-places-interface/index2.html +1753 -0
  69. data/framework_components/room-places-interface/js/component.js +438 -0
  70. data/framework_components/room-places-interface/js/lib/JSXTransformer-0.12.2.js +15199 -0
  71. data/framework_components/room-places-interface/js/lib/bootstrap.min.js +7 -0
  72. data/framework_components/room-places-interface/js/lib/d3.v3.min.js +5 -0
  73. data/framework_components/room-places-interface/js/lib/jquery-1.10.0.min.js +6 -0
  74. data/framework_components/room-places-interface/js/lib/nutella_lib.js +4145 -0
  75. data/framework_components/room-places-interface/js/lib/react-with-addons-0.12.2.js +19822 -0
  76. data/framework_components/room-places-interface/js/lib/underscore.js +6 -0
  77. data/framework_components/room-places-interface/js/map.js +1593 -0
  78. data/framework_components/room-places-interface/js/position.js +6 -0
  79. data/framework_components/room-places-interface/js/react/dist/beacon.js +30 -0
  80. data/framework_components/room-places-interface/js/react/dist/discrete.js +282 -0
  81. data/framework_components/room-places-interface/js/react/dist/interactive-label.js +54 -0
  82. data/framework_components/room-places-interface/js/react/dist/resource-add.js +203 -0
  83. data/framework_components/room-places-interface/js/react/dist/resource-table.js +227 -0
  84. data/framework_components/room-places-interface/js/react/dist/resource.js +433 -0
  85. data/framework_components/room-places-interface/js/react/src/beacon.js +30 -0
  86. data/framework_components/room-places-interface/js/react/src/discrete.js +282 -0
  87. data/framework_components/room-places-interface/js/react/src/interactive-label.js +54 -0
  88. data/framework_components/room-places-interface/js/react/src/resource-add.js +203 -0
  89. data/framework_components/room-places-interface/js/react/src/resource-table.js +227 -0
  90. data/framework_components/room-places-interface/js/react/src/resource.js +433 -0
  91. data/framework_components/room-places-interface/js/room.js +65 -0
  92. data/framework_components/room-places-interface/nutella.json +6 -0
  93. data/nutella_framework.gemspec +93 -5
  94. metadata +91 -3
@@ -0,0 +1,438 @@
1
+ var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
2
+
3
+
4
+ var AddResource = React.createClass({
5
+ getInitialState: function() {
6
+ return {name: "", model: "IPAD", range: ""};
7
+ },
8
+ handleSubmit: function(e) {
9
+ e.preventDefault();
10
+
11
+ var name = this.refs.name.getDOMNode().value.trim();
12
+ var model = this.refs.model.getDOMNode().value.trim();
13
+ var range;
14
+ if(this.refs.range != undefined)
15
+ range = this.refs.range.getDOMNode().value.trim();
16
+ else
17
+ range = 0;
18
+
19
+ nutella.net.publish("location/resource/add", {rid: name, model: model, type: this.props.type, proximity_range: parseFloat(range)});
20
+
21
+ // Clean the form
22
+ this.setState({name: "", range: ""});
23
+ },
24
+ handleChangeName: function(e) {
25
+ this.setState({name: event.target.value});
26
+ },
27
+ handleChangeModel: function(e) {
28
+ this.setState({model: event.target.value});
29
+ },
30
+ handleChangeRange: function(e) {
31
+ this.setState({range: event.target.value});
32
+ },
33
+ render: function() {
34
+ var proxRange;
35
+ if(this.props.type == "STATIC")
36
+ proxRange = <input type="text" value={this.state.range} placeholder="Proximity range" onChange={this.handleChangeRange} ref="range"/>
37
+
38
+ return(
39
+ <tr>
40
+ <td>
41
+ <form onSubmit={this.handleSubmit}>
42
+ <input type="text" value={this.state.name} placeholder="Name" onChange={this.handleChangeName} ref="name"/>
43
+ {proxRange}
44
+ <select value={this.state.model} onChange={this.handleChangeModel} ref="model">
45
+ <option value="IMAC">iMac</option>
46
+ <option value="IPHONE">iPhone</option>
47
+ <option value="IPAD">iPad</option>
48
+ <option value="IBEACON">iBeacon</option>
49
+ </select>
50
+ <input type="submit" />
51
+ </form>
52
+ </td>
53
+ </tr>
54
+ );
55
+ }
56
+ });
57
+
58
+ var AddKeyValue = React.createClass({
59
+ getInitialState: function() {
60
+ return {key: "", value: ""};
61
+ },
62
+ handleChangeKey: function(e) {
63
+ this.setState({key: event.target.value});
64
+ },
65
+ handleChangeValue: function(e) {
66
+ this.setState({value: event.target.value});
67
+ },
68
+ handleSubmit: function(event) {
69
+ event.preventDefault();
70
+
71
+ var key = this.state.key;
72
+ var value = this.state.value;
73
+
74
+ if(key != "" && value != "") {
75
+ nutella.net.publish("location/resource/update", {rid: this.props.rid, parameters: [{key: key, value: value}]});
76
+ this.setState({key: "", value: ""});
77
+ }
78
+ },
79
+ render: function() {
80
+ return(
81
+ <tr>
82
+ <td>
83
+ <form onSubmit={this.handleSubmit}><input type="text" value={this.state.key} placeholder="Key" onChange={this.handleChangeKey} onBlur={this.handleSubmit} ref="key"/></form>
84
+ </td>
85
+ <td>
86
+ <form onSubmit={this.handleSubmit}><input type="text" value={this.state.value} placeholder="Value" onChange={this.handleChangeValue} onBlur={this.handleSubmit} ref="value"/></form>
87
+ </td>
88
+ </tr>
89
+ );
90
+ }
91
+ });
92
+
93
+ var KeyValue = React.createClass({
94
+ getInitialState: function() {
95
+ return {keyModification: false, valueModification: false, key: this.props._key, value: this.props.value, previousKey: this.props._key};
96
+ },
97
+ handleKeyChanged: function(event) {
98
+ this.setState({key: event.target.value});
99
+ },
100
+ handleValueChanged: function(event) {
101
+ this.setState({value: event.target.value});
102
+ },
103
+ handleKeyClicked: function() {
104
+ this.setState({keyModification: true}, function() {
105
+ this.refs.key.getDOMNode().focus();
106
+ });
107
+ },
108
+ handleValueClicked: function() {
109
+ this.setState({valueModification: true}, function() {
110
+ this.refs.value.getDOMNode().focus();
111
+ });
112
+ },
113
+ handleSubmit: function(event) {
114
+ event.preventDefault();
115
+
116
+ if(this.state.key == "" || this.state.value == "")
117
+ return;
118
+
119
+ if(this.state.key != this.state.previousKey) {
120
+ nutella.net.publish("location/resource/update", {rid: this.props.rid, parameters: [{key: this.state.previousKey, delete: true}]});
121
+ nutella.net.publish("location/resource/update", {rid: this.props.rid, parameters: [{key: this.state.key, value: this.state.value}]});
122
+ }
123
+ else {
124
+ nutella.net.publish("location/resource/update", {rid: this.props.rid, parameters: [{key: this.state.key, value: this.state.value}]});
125
+ }
126
+
127
+ this.setState({keyModification: false, valueModification: false, previousKey: this.state.key});
128
+ },
129
+ render: function() {
130
+ var key;
131
+ var value;
132
+ if(this.state.keyModification)
133
+ key = <form onSubmit={this.handleSubmit}><input type="text" placeholder="Key" ref="key" value={this.state.key} onChange={this.handleKeyChanged} onBlur={this.handleSubmit} /></form>
134
+ else
135
+ key = <div onClick={this.handleKeyClicked}>{this.state.key}</div>
136
+
137
+ if(this.state.valueModification)
138
+ value = <form onSubmit={this.handleSubmit}><input type="text" placeholder="Key" ref="value" value={this.state.value} onChange={this.handleValueChanged} onBlur={this.handleSubmit} /></form>
139
+ else
140
+ value = <div onClick={this.handleValueClicked}>{this.state.value}</div>
141
+
142
+ return(
143
+ <tr>
144
+ <td>{key}</td>
145
+ <td>{value}</td>
146
+ </tr>
147
+ );
148
+ }
149
+ });
150
+
151
+ var Resource = React.createClass({
152
+ getInitialState: function() {
153
+ return {collapse: false};
154
+ },
155
+ handleDelete: function() {
156
+ this.props.handleDelete(this.props.rid);
157
+ },
158
+ handleCollapse: function() {
159
+ this.setState({collapse: !this.state.collapse});
160
+ },
161
+ handleAddTrackingContinuous: function() {
162
+ this.props.handleAddTracking(this.props.resource, "continuous");
163
+ },
164
+ handleAddTrackingDiscrete: function() {
165
+ this.props.handleAddTracking(this.props.resource, "discrete");
166
+ },
167
+ handleAddTrackingNone: function() {
168
+ this.props.handleAddTracking(this.props.resource, "none");
169
+ },
170
+ render: function() {
171
+ var self = this;
172
+ var keyValues = this.props.keyValues.map(function (keyValue, index) {
173
+ return (
174
+ <KeyValue key={keyValue.key+"-"+keyValue.value} _key={keyValue.key} value={keyValue.value} rid={self.props.rid}/>
175
+ );
176
+ });
177
+
178
+ var _continuous = "";
179
+ var _discrete = "";
180
+ var _none = "";
181
+ if(this.props.resource.discrete != undefined) {
182
+ _discrete = " active";
183
+ }
184
+ else if(this.props.resource.continuous != undefined) {
185
+ _continuous = " active";
186
+ }
187
+ else {
188
+ _none = " active";
189
+ }
190
+
191
+ var addTracking = <div className="btn-group col-md-4 pull-right">
192
+ <button className={"col-md-4 btn btn-default" + _continuous} type="button" aria-label="Left Align" onClick={this.handleAddTrackingContinuous}>
193
+ <span className="glyphicon glyphicon-screenshot" aria-hidden="true" />
194
+ </button>
195
+ <button className={"col-md-4 btn btn-default" + _discrete} type="button" aria-label="Left Align" onClick={this.handleAddTrackingDiscrete}>
196
+ <span className="glyphicon glyphicon-th" aria-hidden="true" />
197
+ </button>
198
+ <button className={"col-md-4 btn btn-default" + _none} type="button" aria-label="Left Align" onClick={this.handleAddTrackingNone}>
199
+ <span className="glyphicon glyphicon-ban-circle" aria-hidden="true" />
200
+ </button>
201
+ </div>;
202
+
203
+ if(this.props.resource.type == "DYNAMIC")
204
+ addTracking = null;
205
+
206
+ return(
207
+ <tr>
208
+ <td>
209
+ <div className="col-md-4">{this.props.rid}</div>
210
+
211
+ <button className="col-md-1 btn btn-default pull-right" type="button" data-toggle="collapse" data-target={"#collapse_"+this.props.rid} aria-expanded="true" aria-controls={"collapse_"+this.props.rid} onClick={this.handleCollapse}>
212
+ <span className={this.state.collapse ? "glyphicon glyphicon-triangle-top" : "glyphicon glyphicon-triangle-bottom"} aria-hidden="true" />
213
+ </button>
214
+
215
+ <button className="col-md-1 btn btn-default pull-right" type="button" aria-label="Left Align" onClick={this.handleDelete}>
216
+ <span className="glyphicon glyphicon-remove" aria-hidden="true" />
217
+ </button>
218
+
219
+ {addTracking}
220
+ <div className="collapse" id={"collapse_"+this.props.rid}>
221
+ <table className="table table-bordered table-striped">
222
+ <ReactCSSTransitionGroup transitionName="example" component="tbody">
223
+ <tr><th>Key</th><th>Value</th></tr>
224
+ {keyValues}
225
+ <AddKeyValue rid={this.props.rid}/>
226
+ </ReactCSSTransitionGroup>
227
+ </table>
228
+ </div>
229
+ </td>
230
+ </tr>
231
+ );
232
+ }
233
+ });
234
+
235
+ var ResourceEstimote = React.createClass({
236
+ handleDynamicAdd: function() {
237
+ this.props.handleDynamicAdd(this.props.rid);
238
+ },
239
+ handleStaticAdd: function() {
240
+ this.props.handleStaticAdd(this.props.rid);
241
+ },
242
+ render: function() {
243
+ return(
244
+ <tr>
245
+ <td>
246
+ <div className="col-md-4">{this.props.rid}</div>
247
+ <button className="col-md-2 col-md-offset-4 btn btn-default" type="button" aria-label="Left Align" onClick={this.handleStaticAdd}>
248
+ <span className="glyphicon glyphicon-plus" aria-hidden="true" />
249
+ </button>
250
+ <button className="col-md-2 btn btn-default" type="button" aria-label="Left Align" onClick={this.handleDynamicAdd}>
251
+ <span className="glyphicon glyphicon-plus" aria-hidden="true" />
252
+ </button>
253
+ </td>
254
+ </tr>
255
+ );
256
+ }
257
+ });
258
+
259
+
260
+ var ResourceTable = React.createClass({
261
+ getInitialState: function() {
262
+ return {
263
+ resourceData: [],
264
+ estimoteData: []
265
+ };
266
+ },
267
+ componentDidMount: function() {
268
+ self = this;
269
+
270
+ // Download all resources
271
+ nutella.net.request("location/resources", {}, function(reply) {
272
+ self.setState({resourceData: reply.resources});
273
+ });
274
+
275
+ // Wait for new added resources
276
+ nutella.net.subscribe("location/resources/added", function(message) {
277
+ var data = self.state.resourceData;
278
+ data = data.concat(message.resources)
279
+
280
+ self.setState({resourceData: data});
281
+ });
282
+
283
+ // Wait for updated resources
284
+ nutella.net.subscribe("location/resources/updated", function(message) {
285
+ var data = self.state.resourceData;
286
+ data = data.filter(function(d) {
287
+ return $.inArray(d.rid, message.resources.map(function(r) {
288
+ return r.rid;
289
+ })) == -1;
290
+ });
291
+ data = data.concat(message.resources)
292
+ self.setState({resourceData: data});
293
+ });
294
+
295
+ // Wait for removed resources
296
+ nutella.net.subscribe("location/resources/removed", function(message) {
297
+ var data = self.state.resourceData;
298
+ data = data.filter(function(d) {
299
+ return $.inArray(d.rid, message.resources.map(function(r) {
300
+ return r.rid;
301
+ })) == -1;
302
+ });
303
+
304
+ self.setState({resourceData: data});
305
+ });
306
+
307
+ // Download estimote beacons data
308
+ nutella.net.request("location/estimote", {}, function(reply) {
309
+ self.setState({estimoteData: reply.resources});
310
+ });
311
+ },
312
+ handleResourceDelete: function(rid) {
313
+ nutella.net.publish("location/resource/remove", {rid: rid});
314
+
315
+ // Delete the corresponding row
316
+ var data = this.state.resourceData;
317
+ data = data.filter(function(d) { return d.rid != rid; });
318
+ this.setState({resourceData: data});
319
+ },
320
+ handleResourceEstimoteDynamicAdd: function(rid) {
321
+ nutella.net.publish("location/resource/add", {rid: rid,
322
+ model: "IBEACON",
323
+ type: "DYNAMIC"
324
+ });
325
+ },
326
+ handleResourceEstimoteStaticAdd: function(rid) {
327
+ nutella.net.publish("location/resource/add", {rid: rid,
328
+ model: "IBEACON",
329
+ type: "STATIC",
330
+ proximity_range: 1
331
+ });
332
+ },
333
+ handleAddTracking: function(resource, type) {
334
+ var x = this.props.room.x / 2;
335
+ var y = this.props.room.y / 2;
336
+
337
+ var message;
338
+
339
+ switch(type) {
340
+ case "continuous":
341
+ message = {rid: resource.rid,
342
+ continuous: {x: x, y: y}
343
+ };
344
+ break;
345
+
346
+ case "discrete":
347
+ message = {rid: resource.rid,
348
+ discrete: {x: 0, y: 0}
349
+ };
350
+ break;
351
+ default:
352
+ message = {rid: resource.rid};
353
+ break;
354
+ }
355
+
356
+ nutella.net.publish("location/resource/update", message);
357
+ },
358
+ render: function() {
359
+ var self = this;
360
+
361
+ // Order the resource list
362
+ var resources = this.state.resourceData;
363
+ resources = resources.sort(function(a, b) {return a.rid.localeCompare(b.rid)});
364
+
365
+ var staticResourcesData = resources.filter(function(resource) { return resource.type == "STATIC"});
366
+ var dynamicResourcesData = resources.filter(function(resource) { return resource.type == "DYNAMIC"});
367
+ var estimoteResourcesData = this.state.estimoteData.filter(function(resource) {
368
+ return $.inArray(resource.name, resources.map(function(r) {
369
+ return r.rid;
370
+ })) == -1;
371
+ });
372
+
373
+ var staticResources = staticResourcesData.map(function (resource, index) {
374
+ var keyValues = [];
375
+ for(key in resource.parameters) {
376
+ keyValues.push({key: key, value: resource.parameters[key]});
377
+ }
378
+ return (
379
+ <Resource key={resource.rid}
380
+ rid={resource.rid}
381
+ resource={resource}
382
+ room={self.props.room}
383
+ handleDelete={self.handleResourceDelete}
384
+ handleAddTracking={self.handleAddTracking}
385
+ keyValues={keyValues}/>
386
+ );
387
+ });
388
+
389
+ var dynamicResources = dynamicResourcesData.map(function (resource, index) {
390
+ var keyValues = [];
391
+ for(key in resource.parameters) {
392
+ keyValues.push({key: key, value: resource.parameters[key]});
393
+ }
394
+ return (
395
+ <Resource key={resource.rid}
396
+ rid={resource.rid}
397
+ resource={resource}
398
+ room={self.props.room}
399
+ handleDelete={self.handleResourceDelete}
400
+ handleAddTracking={self.handleAddTracking}
401
+ keyValues={keyValues}/>
402
+ );
403
+ });
404
+
405
+ var estimoteResources = estimoteResourcesData.map(function (resource, index) {
406
+ return (
407
+ <ResourceEstimote key={resource.name}
408
+ rid={resource.name}
409
+ handleDynamicAdd={self.handleResourceEstimoteDynamicAdd}
410
+ handleStaticAdd={self.handleResourceEstimoteStaticAdd}/>
411
+ );
412
+ });
413
+
414
+ return(
415
+ <div>
416
+ <table className="table table-bordered table-striped">
417
+ <ReactCSSTransitionGroup transitionName="example" component="tbody">
418
+ <tr><th>Static resources</th></tr>
419
+ {staticResources}
420
+ <AddResource type="STATIC"/>
421
+ </ReactCSSTransitionGroup>
422
+ </table>
423
+
424
+ <table className="table table-bordered table-striped">
425
+ <ReactCSSTransitionGroup transitionName="example" component="tbody">
426
+ <tr><th>Dynamic resources</th></tr>
427
+ {dynamicResources}
428
+ <AddResource type="DYNAMIC"/>
429
+
430
+ <tr><th>Estimote iBeacon</th></tr>
431
+ {estimoteResources}
432
+ </ReactCSSTransitionGroup>
433
+
434
+ </table>
435
+ </div>
436
+ );
437
+ }
438
+ });