├── .bowerrc
├── index.js
├── bundles
└── index
│ ├── index.bemdecl.js
│ └── index.html
├── .gitignore
├── blocks
├── react
│ └── react.js
├── commentBox
│ ├── commentBox.css
│ └── commentBox.jsx
├── document
│ ├── document.css
│ ├── document.jsx
│ └── document.node.js
├── showdown
│ └── showdown.js
├── comment
│ ├── comment.css
│ └── comment.jsx
├── commentList
│ └── commentList.jsx
└── commentForm
│ └── commentForm.jsx
├── bower.json
├── README.md
├── .bem
├── techs
│ └── jsx.js
└── enb-make.js
└── package.json
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "libs"
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | require('./bundles/index/index.node.js');
--------------------------------------------------------------------------------
/bundles/index/index.bemdecl.js:
--------------------------------------------------------------------------------
1 | exports.deps = [
2 | { block: 'document' }
3 | ]
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | node_modules
3 | .DS_Store
4 | .idea
5 |
6 | .bem/tmp
7 | libs
8 |
9 | bundles/*/*.*
10 | !bundles/*/*.html
11 | !bundles/*/*.bemdecl.js
12 |
--------------------------------------------------------------------------------
/blocks/react/react.js:
--------------------------------------------------------------------------------
1 | modules.define('react', ['loader_type_js'], function(provide, loader) {
2 |
3 | loader('http://fb.me/react-0.10.0.min.js', function() {
4 | provide(React);
5 | });
6 |
7 | });
--------------------------------------------------------------------------------
/blocks/commentBox/commentBox.css:
--------------------------------------------------------------------------------
1 | .commentBox__title
2 | {
3 | border-bottom: 1px solid #ddd;
4 | font-size: 2.5em;
5 | font-weight: bold;
6 | margin: 0 0 15px;
7 | padding: 0;
8 | }
9 |
--------------------------------------------------------------------------------
/blocks/document/document.css:
--------------------------------------------------------------------------------
1 | body
2 | {
3 | background: #fff;
4 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;;
5 | font-size: 15px;
6 | line-height: 1.7;
7 | margin: 0;
8 | padding: 30px;
9 | }
--------------------------------------------------------------------------------
/blocks/showdown/showdown.js:
--------------------------------------------------------------------------------
1 | modules.define('showdown', ['loader_type_js'], function(provide, loader) {
2 |
3 | loader('http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js', function() {
4 | provide(Showdown);
5 | });
6 |
7 | });
--------------------------------------------------------------------------------
/blocks/document/document.jsx:
--------------------------------------------------------------------------------
1 | modules.require(['react', 'commentBox'], function(React, CommentBox) {
2 |
3 | React.renderComponent(
4 | ,
5 | document.getElementById('content')
6 | );
7 |
8 | });
9 |
--------------------------------------------------------------------------------
/blocks/comment/comment.css:
--------------------------------------------------------------------------------
1 | .comment__author
2 | {
3 | font-weight: bold;
4 | margin: 0 0 15px;
5 | padding: 0;
6 | border-bottom: 1px solid #eee;
7 | font-size: 2em;
8 | }
9 |
10 | .comment__text p
11 | {
12 | margin: 15px 0;
13 | }
--------------------------------------------------------------------------------
/bundles/index/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hello React
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-tutorial",
3 | "version": "0.0.0",
4 | "authors": [
5 | "Vyacheslav Aristov "
6 | ],
7 | "dependencies": {
8 | "bem-core": "git://github.com/bem/bem-core.git"
9 | },
10 | "ignore": [
11 | "node_modules",
12 | "libs",
13 | "bundles"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React comment box example with BEM!
2 |
3 | This is the React comment box example from [the React tutorial](http://facebook.github.io/react/docs/tutorial.html).
4 | It's built on [BEM](http://bem.info) with React integration.
5 |
6 | ## To use
7 |
8 | ```
9 | npm install
10 | ./node_modules/.bin/bower install
11 | ./node_modules/.bin/enb make
12 | node .
13 | ```
14 |
15 | And visit http://localhost:3000/.
16 |
--------------------------------------------------------------------------------
/.bem/techs/jsx.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs'),
2 | reactTools = require('react-tools');
3 |
4 | module.exports = require('enb/lib/build-flow').create()
5 | .name('jsx')
6 | .target('target', '?.js')
7 | .useFileList(['js', 'jsx'])
8 | .builder(function (jsxFiles) {
9 | var jsx = jsxFiles.map(function(file) {
10 | return [
11 | '/* ' + file.name + ' (begin) */',
12 | fs.readFileSync(file.fullname, 'utf-8'),
13 | '/* ' + file.name + ' (end) */'
14 | ].join('\n');
15 | });
16 | return reactTools.transform(['/** @jsx React.DOM */'].concat(jsx).join('\n'));
17 | })
18 | .createTech();
--------------------------------------------------------------------------------
/blocks/comment/comment.jsx:
--------------------------------------------------------------------------------
1 | modules.define('comment', ['react', 'showdown'], function(provide, React, Showdown) {
2 |
3 | var converter = new Showdown.converter();
4 |
5 | var Comment = React.createClass({
6 | render: function() {
7 | var rawMarkup = converter.makeHtml(this.props.children.toString());
8 | return (
9 |
10 |
11 | {this.props.author}
12 |
13 |
14 |
15 | );
16 | }
17 | });
18 |
19 | provide(Comment);
20 |
21 | });
22 |
--------------------------------------------------------------------------------
/blocks/document/document.node.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var bodyParser = require('body-parser');
3 | var app = express();
4 |
5 | var comments = [{author: 'Pete Hunt', text: 'Hey there!'}];
6 |
7 | app.use('/', express.static(__dirname));
8 | app.use(bodyParser.json());
9 | app.use(bodyParser.urlencoded({extended: true}));
10 |
11 | app.get('/comments.json', function(req, res) {
12 | res.setHeader('Content-Type', 'application/json');
13 | res.send(JSON.stringify(comments));
14 | });
15 |
16 | app.post('/comments.json', function(req, res) {
17 | comments.push(req.body);
18 | res.setHeader('Content-Type', 'application/json');
19 | res.send(JSON.stringify(comments));
20 | });
21 |
22 | app.listen(3000);
23 |
24 | console.log('Server started: http://localhost:3000/');
25 |
--------------------------------------------------------------------------------
/blocks/commentList/commentList.jsx:
--------------------------------------------------------------------------------
1 | modules.define('commentList', ['react', 'comment'], function(provide, React, Comment) {
2 |
3 | var CommentList = React.createClass({
4 | render: function() {
5 | var commentNodes = this.props.data.map(function(comment, index) {
6 | return (
7 | // `key` is a React-specific concept and is not mandatory for the
8 | // purpose of this tutorial. if you're curious, see more here:
9 | // http://facebook.github.io/react/docs/multiple-components.html#dynamic-children
10 |
11 | {comment.text}
12 |
13 | );
14 | });
15 | return (
16 |
17 | {commentNodes}
18 |
19 | );
20 | }
21 | });
22 |
23 | provide(CommentList);
24 |
25 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-tutorial",
3 | "version": "0.0.0",
4 | "description": "Code from the React tutorial.",
5 | "main": "server.js",
6 | "dependencies": {
7 | "body-parser": "^1.4.3",
8 | "express": "^4.4.5",
9 | "enb": "~0.13.7",
10 | "enb-diverse-js": "~0.1.0",
11 | "react-tools": "~0.11.1",
12 | "ym": "~0.1.0",
13 | "enb-modules": "~0.2.0",
14 | "bower": "~1.3.9"
15 | },
16 | "devDependencies": {},
17 | "scripts": {
18 | "test": "echo \"Error: no test specified\" && exit 1",
19 | "start": "node server.js"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/reactjs/react-tutorial.git"
24 | },
25 | "keywords": [
26 | "react",
27 | "tutorial",
28 | "comment",
29 | "example"
30 | ],
31 | "author": "petehunt",
32 | "license": "MIT",
33 | "bugs": {
34 | "url": "https://github.com/reactjs/react-tutorial/issues"
35 | },
36 | "homepage": "https://github.com/reactjs/react-tutorial"
37 | }
38 |
--------------------------------------------------------------------------------
/blocks/commentForm/commentForm.jsx:
--------------------------------------------------------------------------------
1 | modules.define('commentForm', ['react'], function(provide, React) {
2 |
3 | var CommentForm = React.createClass({
4 | handleSubmit: function(e) {
5 | e.preventDefault();
6 | var author = this.refs.author.getDOMNode().value.trim();
7 | var text = this.refs.text.getDOMNode().value.trim();
8 | if (!text || !author) {
9 | return;
10 | }
11 | this.props.onCommentSubmit({author: author, text: text});
12 | this.refs.author.getDOMNode().value = '';
13 | this.refs.text.getDOMNode().value = '';
14 | return;
15 | },
16 | render: function() {
17 | return (
18 |
23 | );
24 | }
25 | });
26 |
27 | provide(CommentForm);
28 |
29 | });
--------------------------------------------------------------------------------
/.bem/enb-make.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.mode("development", function() {
3 | config.node("bundles/index", function(nodeConfig) {
4 | nodeConfig.addTechs([
5 | [ require("enb/techs/file-copy"), { sourceTarget: "?.css", destTarget: "_?.css" } ],
6 | [ require("enb/techs/file-copy"), { sourceTarget: "?.js", destTarget: "_?.js" } ]
7 | ]);
8 | });
9 | });
10 | config.mode("production", function() {
11 | config.node("bundles/index", function(nodeConfig) {
12 | nodeConfig.addTechs([
13 | [ require("enb/techs/borschik"), { sourceTarget: "?.css", destTarget: "_?.css", minify: true, freeze: false } ],
14 | [ require("enb/techs/borschik"), { sourceTarget: "?.js", destTarget: "_?.js", minify: true, freeze: false } ]
15 | ]);
16 | });
17 | });
18 |
19 | config.node("bundles/index", function(nodeConfig) {
20 | nodeConfig.addTechs([
21 | [ require("enb/techs/levels"), { levels: getLevels() } ],
22 | [ require("enb/techs/file-provider"), { target: "?.bemdecl.js" } ],
23 | [ require("enb-modules/techs/deps-with-modules"), { sourceSuffixes: ['js', 'jsx'] } ],
24 | require("enb/techs/files"),
25 | require("enb/techs/css"),
26 | [ require("./techs/jsx"), { target: '?.pre.js' } ],
27 | [ require("enb-modules/techs/prepend-modules"), { source: '?.pre.js', target: '?.js' } ],
28 | [ require("enb/techs/js"), { sourceSuffixes: 'node.js', target: '?.node.js' } ]
29 | ]);
30 | nodeConfig.addTargets(["_?.css", "_?.js", "?.node.js"]);
31 |
32 | function getLevels() {
33 | return [
34 | {"path":"libs/bem-core/common.blocks","check":false},
35 | {"path":"blocks","check":true}
36 | ].map(function(l) { return config.resolvePath(l); });
37 | }
38 | });
39 | }
--------------------------------------------------------------------------------
/blocks/commentBox/commentBox.jsx:
--------------------------------------------------------------------------------
1 | modules.define('commentBox', [
2 | 'react',
3 | 'commentList',
4 | 'commentForm',
5 | 'jquery'
6 | ], function(provide, React, CommentList, CommentForm, $) {
7 |
8 | var CommentBox = React.createClass({
9 | loadCommentsFromServer: function() {
10 | $.ajax({
11 | url: this.props.url,
12 | dataType: 'json',
13 | success: function(data) {
14 | this.setState({data: data});
15 | }.bind(this),
16 | error: function(xhr, status, err) {
17 | console.error(this.props.url, status, err.toString());
18 | }.bind(this)
19 | });
20 | },
21 | handleCommentSubmit: function(comment) {
22 | var comments = this.state.data;
23 | comments.push(comment);
24 | this.setState({data: comments}, function() {
25 | // `setState` accepts a callback. To avoid (improbable) race condition,
26 | // `we'll send the ajax request right after we optimistically set the new
27 | // `state.
28 | $.ajax({
29 | url: this.props.url,
30 | dataType: 'json',
31 | type: 'POST',
32 | data: comment,
33 | success: function(data) {
34 | this.setState({data: data});
35 | }.bind(this),
36 | error: function(xhr, status, err) {
37 | console.error(this.props.url, status, err.toString());
38 | }.bind(this)
39 | });
40 | });
41 | },
42 | getInitialState: function() {
43 | return {data: []};
44 | },
45 | componentDidMount: function() {
46 | this.loadCommentsFromServer();
47 | setInterval(this.loadCommentsFromServer, this.props.pollInterval);
48 | },
49 | render: function() {
50 | return (
51 |
52 |
Comments
53 |
54 |
55 |
56 | );
57 | }
58 | });
59 |
60 | provide(CommentBox);
61 |
62 | });
63 |
--------------------------------------------------------------------------------