Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lantiga committed Jan 9, 2014
0 parents commit f722dcd
Show file tree
Hide file tree
Showing 9 changed files with 774 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
.module-cache
20 changes: 20 additions & 0 deletions LICENSE
@@ -0,0 +1,20 @@
Copyright (C) 2014 Luca Antiga http://lantiga.github.io

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
366 changes: 366 additions & 0 deletions README.md
@@ -0,0 +1,366 @@

# React.hiccup: React 0% JSX, 100% hiccup

React.hiccup is a complete replacement for [React](http://facebook.github.io/react/) under the form of a [sweet.js](http://sweetjs.org) macro.

Dig React but can't take JSX? React.hiccup to the rescue.


## Syntax

React.hiccup syntax is heavily inspired by [hiccup](https://github.com/weavejester/hiccup), a popular [Clojure](http://clojure.org) HTML rendering library.

In short, the syntax for a React.hiccup element is

```js
hiccup [tag#id.class1.class2 {attributes} child1 child2 ...]
```

e.g.

```js
hiccup [div#foo.bar.baz {some: "property", another: this.props.property} [p "A child element"] "Child text"]
```

where the id, classes, attributes and children are all optional. The className can be also specified
among the attributes, in this case it will be merged with the class names given after the tag.

A child can be a variable identifier

```js
var comment = "Some comment";
hiccup [div#foo.bar.baz "The comment is: " comment]
```

but in case it is anything more complex, e.g. an expression, it needs to be surrounded by parentheses

```js
hiccup [div#foo.bar.baz "The comment is: " (this.state.comment)]
```

or

```js
var one_or_two = 1;
var comment1 = "First comment";
var comment2 = "Second comment";
hiccup [div#foo.bar.baz "The comment is: " (one_or_two == 1 ? comment1 : comment2 )]
```

Note that this is not required in the attributes.


## Complimentary rclass macro

React.hiccup also comes with an optional macro for declaring a React class

```js
rclass FooBar = {
render: function() { ... }
}
```

expands to (omitting the sweet.js gensym)

```js
var FooBar = React.createClass({
render: function() { ... }
});
```

while

```js
rclass window.FooBar = {
render: function() { ... }
}
```

expands to

```js
window.FooBar = React.createClass({
render: function() { ... }
});
```


## Get it

First install [sweet.js](http://sweetjs.org) if you don't have it already

$ npm install -g sweet.js

Then get in your project directory

$ wget https://raw2.github.com/lantiga/react.hiccup/master/react_hiccup.sjs

All set. Now to compile a React.hiccup js file into a plain js file do

$ sjs -m ./react_hiccup.sjs -o foo_build.js foo.js

To watch the file and have it automatically compiled at every change

$ sjs -m ./react_hiccup.sjs -o foo_build.js -w foo.js

(this appears to be currently broken in sweet.js)


## Examples

### React frontpage examples

Here's how [React frontpage examples](http://facebook.github.io/react/) can be
written using React.hiccup.

#### A Simple Component

JSX:

```js
/** @jsx React.DOM */
var HelloMessage = React.createClass({
render: function() {
return <div>{'Hello ' + this.props.name}</div>;
}
});

React.renderComponent(<HelloMessage name="John" />, mountNode);
```

React.hiccup:

```js
var HelloMessage = React.createClass({
render: function() {
return hiccup [div ('Hello' + this.props.name)];
}
});

React.renderComponent(hiccup [HelloMessage {name: "John"}], mountNode);
```

or, using the rclass macro (we'll use it in the remainder of the examples)

```js
rclass HelloMessage = {
render: function() {
return hiccup [div ('Hello' + this.props.name)];
}
}

React.renderComponent(hiccup [HelloMessage {name: "John"}], mountNode);
```

#### A Stateful Component

React.js

```js
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return React.DOM.div({},
'Seconds Elapsed: ', this.state.secondsElapsed
);
}
});

React.renderComponent(Timer({}), mountNode);
```

React.hiccup

```js
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return hiccup [div 'Seconds Elapsed: ' (this.state.secondsElapsed)]
}
});

React.renderComponent(hiccup [Timer], mountNode);
```

#### An Application

JSX:

```js
/** @jsx React.DOM */
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.renderComponent(<TodoApp />, mountNode);
```

React.hiccup

```
rclass TodoList = {
render: function() {
var createItem = function(itemText) {
return hiccup [li itemText];
};
return hiccup [ul (this.props.items.map(createItem))];
}
}
rclass TodoApp = {
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return hiccup
[div
[h3 "TODO"]
[TodoList {items: this.state.items}]
[form {onSubmit: this.handleSubmit}
[input {onChange: this.onChange, value: this.state.text}]
[button ('Add #' + (this.state.items.length + 1))]]];
}
}
React.renderComponent(hiccup [TodoApp], mountNode);
```

#### A Component Using External Plugins

JSX:

```js
/** @jsx React.DOM */

var converter = new Showdown.converter();

var MarkdownEditor = React.createClass({
getInitialState: function() {
return {value: 'Type some *markdown* here!'};
},
handleChange: function() {
this.setState({value: this.refs.textarea.getDOMNode().value});
},
render: function() {
return (
<div className="MarkdownEditor">
<h3>Input</h3>
<textarea
onChange={this.handleChange}
ref="textarea"
defaultValue={this.state.value} />
<h3>Output</h3>
<div
className="content"
dangerouslySetInnerHTML={{
__html: converter.makeHtml(this.state.value)
}}
/>
</div>
);
}
});

React.renderComponent(<MarkdownEditor />, mountNode);
```

React.hiccup:

```js

var converter = new Showdown.converter();

rclass MarkdownEditor = {
getInitialState: function() {
return {value: 'Type some *markdown* here!'};
},
handleChange: function() {
this.setState({value: this.refs.textarea.getDOMNode().value});
},
render: function() {
return hiccup
[div.MarkdownEditor
[h3 "Input"]
[textarea {onChange: this.handleChange,
ref: "textarea",
defaultValue: this.state.value}]
[h3 "Output"]
div.content {dangerouslySetInnerHTML: {__html: converter.makeHtml(this.state.value)}}];
}
}

React.renderComponent(hiccup [MarkdownEditor], mountNode);
```

### React tutorial

For something more involved you can take a look at the [React tutorial](http://facebook.github.io/react/docs/tutorial.html).

Here's the code in [JSX](https://raw2.github.com/lantiga/react.hiccup/master/tutorial/tutorial.jsx), and
here's the same code in [React.hiccup](https://raw2.github.com/lantiga/react.hiccup/master/tutorial/tutorial.jsx).

## License

MIT license http://www.opensource.org/licenses/mit-license.php/

Copyright (C) 2014 Luca Antiga http://lantiga.github.io

0 comments on commit f722dcd

Please sign in to comment.