From 65d253ebe7e6d29a4707b4d262497dc13ad42216 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 28 Nov 2015 15:02:18 -0500 Subject: [PATCH 1/4] Updates for react 0.14 Resolves #50 --- .gitignore | 1 + bower.json | 15 ++-- package.json | 7 ++ src/React.js | 186 ++++++++++++++++++++++--------------------------- src/React.purs | 22 +++--- 5 files changed, 110 insertions(+), 121 deletions(-) create mode 100644 package.json diff --git a/.gitignore b/.gitignore index b15fc4a..8d84c2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ output bower_components node_modules +.pulp-cache/ diff --git a/bower.json b/bower.json index 189a061..6123ce1 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,5 @@ { "name": "purescript-react", - "version": "0.0.5", "homepage": "https://github.com/purescript-contrib/purescript-react", "description": "Purescript bindings for React.js", "keywords": [ @@ -15,15 +14,15 @@ "test", "tests" ], - "repository": { - "type": "git", + "repository": { + "type": "git", "url": "git://github.com/purescript-contrib/purescript-react.git" - }, - "dependencies": { - "purescript-dom": "~0.2.6" }, "devDependencies": { - "purescript-console": "~0.1.1", - "react": "~0.13.3" + "purescript-console": "~0.1.1" + }, + "dependencies": { + "purescript-eff": "~0.1.2", + "purescript-unsafe-coerce": "~0.1.0" } } diff --git a/package.json b/package.json new file mode 100644 index 0000000..d6126cc --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "purescript-react", + "files": [], + "peerDependencies": { + "react": "^0.14.3" + } +} diff --git a/src/React.js b/src/React.js index 61bea49..d606a1e 100644 --- a/src/React.js +++ b/src/React.js @@ -3,21 +3,25 @@ // module React -exports.getProps = function(ctx) { - return function() { - return ctx.props; - }; -}; +var React = require('react'); -exports.getRefs = function(ctx) { - return function() { - return ctx.refs; - }; -}; +function getProps(this_) { + return function(){ + return this_.props; + }; +} +exports.getProps = getProps; + +function getRefs(this_) { + return function(){ + return this_.refs; + }; +} +exports.getRefs = getRefs; -exports.getChildren = function(ctx) { - return function() { - var children = ctx.props.children; +function getChildren(this_) { + return function(){ + var children = this_.props.children; var result = []; @@ -27,103 +31,83 @@ exports.getChildren = function(ctx) { return result; }; -}; +} +exports.getChildren = getChildren; -exports.writeState = function(ctx) { - return function(state) { - return function() { - ctx.replaceState({ - state: state - }); - return function() { - return state; - } - }; +function writeState(this_) { + return function(state){ + return function(){ + this_.replaceState({ + state: state + }); + return function(){ + return state; + } }; -}; + }; +} +exports.writeState = writeState; -exports.readState = function(ctx) { - return function() { - return ctx.state.state; - }; -}; +function readState(this_) { + return function(){ + return this_.state.state; + }; +} +exports.readState = readState; -exports.createClass = function(ss) { - var result = {}; - for (var s in ss) { - if (ss.hasOwnProperty(s)) { - if (s === "displayName") { - result[s] = ss[s]; - } - else if (s === "componentWillReceiveProps") { - result[s] = (function(impl) { - return function(nextProps) { - return impl(this)(nextProps)(); - } - })(ss[s]); - } - else if (s === "shouldComponentUpdate") { - result[s] = (function(impl) { - return function(nextProps, nextState) { - return impl(this)(nextProps)(nextState.state)(); - } - })(ss[s]); - } - else if (s === "componentWillUpdate") { - result[s] = (function(impl) { - return function(nextProps, nextState) { - return impl(this)(nextProps)(nextState.state)(); - } - })(ss[s]); - } - else if (s === "componentDidUpdate") { - result[s] = (function(impl) { - return function(prevProps, prevState) { - return impl(this)(prevProps)(prevState.state)(); - } - })(ss[s]); - } - else { - result[s] = (function(impl) { - return function() { - return impl(this)(); - } - })(ss[s]); - } - } +function createClass(spec) { + var result = { + displayName: spec.displayName, + render: function(){ + return spec.render(this)(); + }, + getInitialState: function(){ + return spec.getInitialState(this)(); + }, + componentWillMount: function(){ + return spec.componentWillMount(this)(); + }, + componentDidMount: function(){ + return spec.componentDidMount(this)(); + }, + componentWillReceiveProps: function(nextProps){ + return spec.componentWillReceiveProps(this)(nextProps)(); + }, + shouldComponentUpdate: function(nextProps, nextState){ + return spec.shouldComponentUpdate(this)(nextProps)(nextState.state)(); + }, + componentWillUpdate: function(nextProps, nextState){ + return spec.componentWillUpdate(this)(nextProps)(nextState.state)(); + }, + componentDidUpdate: function(prevProps, prevState){ + return spec.componentDidUpdate(this)(prevProps)(prevState.state)(); + }, + componentWillUnmount: function(){ + return spec.componentWillUnmount(this)(); } - result.getInitialState = function() { - return { - state: ss.getInitialState(this)() - }; - }; - return React.createClass(result); -}; + }; -exports.handle = function(f) { - return function(e) { - return f(e)(); - }; -}; + return React.createClass(result); +} +exports.createClass = createClass; -exports.createElement = function(clazz) { - return function(props) { - return function(children){ - return React.createElement.apply(React, [clazz, props].concat(children)); - }; +function handle(f) { + return function(e){ + return f(e)(); }; }; +exports.handle = handle; -exports.createFactory = function(clazz) { - return React.createFactory(clazz); -}; - -exports.render = function(element) { - return function(container) { - return function() { - return React.render(element, container); - } +function createElement(class_) { + return function(props){ + return function(children){ + return React.createElement.apply(React, [class_, props].concat(children)); + }; }; -}; +} +exports.createElement = createElement; -exports.renderToString = React.renderToString; +function createFactory(class_) { + return React.createFactory(class_); +} +exports.createFactory = createFactory; diff --git a/src/React.purs b/src/React.purs index 980d660..ebdffa5 100644 --- a/src/React.purs +++ b/src/React.purs @@ -2,6 +2,7 @@ module React ( ReactElement() + , ReactComponent() , ReactThis() , EventHandler() @@ -52,21 +53,20 @@ module React , createClass , createElement , createFactory - - , render - , renderToString ) where import Prelude (Unit(), ($), bind, pure, return, unit) -import DOM (DOM()) -import DOM.Node.Types (Element()) - import Control.Monad.Eff (Eff()) +import Unsafe.Coerce (unsafeCoerce) + -- | A virtual DOM node, or component. foreign import data ReactElement :: * +-- | A mounted react component +foreign import data ReactComponent :: * + -- | A reference to a component, essentially React's `this`. foreign import data ReactThis :: * -> * -> * @@ -284,6 +284,10 @@ transformState ctx f = do -- | Create a React class from a specification. foreign import createClass :: forall props state eff. ReactSpec props state eff -> ReactClass props +-- | Create a stateless React class. +createClassStateless :: forall props. (props -> ReactElement) -> ReactClass props +createClassStateless = unsafeCoerce + -- | Create an event handler. foreign import handle :: forall eff ev props state result. (ev -> EventHandlerContext eff props state result) -> EventHandler ev @@ -292,9 +296,3 @@ foreign import createElement :: forall props. ReactClass props -> props -> Array -- | Create a factory from a React class. foreign import createFactory :: forall props. ReactClass props -> props -> ReactElement - --- | Render a React element in a document element. -foreign import render :: forall eff. ReactElement -> Element -> Eff (dom :: DOM | eff) ReactElement - --- | Render a React element as a string. -foreign import renderToString :: ReactElement -> String From 0106a98688ca768b10453c4b3a4d688fe94d0625 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 28 Nov 2015 15:03:30 -0500 Subject: [PATCH 2/4] Switch to setState instead of replaceState Resolves #48 and resolves #55 --- src/React.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/React.js b/src/React.js index d606a1e..f03839a 100644 --- a/src/React.js +++ b/src/React.js @@ -37,12 +37,10 @@ exports.getChildren = getChildren; function writeState(this_) { return function(state){ return function(){ - this_.replaceState({ + this_.setState({ state: state }); - return function(){ - return state; - } + return state; }; }; } From da4076f1cbfa91b510fd89828de58fda7871f42f Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 28 Nov 2015 16:09:31 -0500 Subject: [PATCH 3/4] Move out example to separate repository --- README.md | 64 ++++------------------------------ bower.json | 13 +++---- example/.gitignore | 1 - example/index.html | 10 ------ src/React.js | 4 ++- src/React.purs | 1 + src/React/DOM.js | 2 ++ src/React/DOM/Props.js | 44 ++++++++++++----------- test/Container.purs | 19 ---------- test/Main.js | 12 ------- test/Main.purs | 79 ------------------------------------------ 11 files changed, 40 insertions(+), 209 deletions(-) delete mode 100644 example/.gitignore delete mode 100644 example/index.html delete mode 100644 test/Container.purs delete mode 100644 test/Main.js delete mode 100644 test/Main.purs diff --git a/README.md b/README.md index 2da2941..09ffe96 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -purescript-react -================ +# purescript-react [![Maintainer: paf31](https://img.shields.io/badge/maintainer-paf31-lightgrey.svg)](http://github.com/paf31) ![React: 0.13.3](https://img.shields.io/badge/react-0.13.3-lightgrey.svg) @@ -9,63 +8,12 @@ For a more high-level set of bindings, you might like to look at `purescript-the - [Module Documentation](docs/) -## Building +## Installation -The library and example can be built with Pulp as follows: - - pulp dep update - pulp build - - pulp test -r cat > example/index.js - open example/index.html +``` +bower install purescript-react +``` ## Example -```purescript -module Main where - -import Prelude - -import Control.Monad.Eff - -import Data.Maybe.Unsafe (fromJust) -import Data.Nullable (toMaybe) - -import DOM (DOM()) -import DOM.HTML (window) -import DOM.HTML.Document (body) -import DOM.HTML.Types (htmlElementToElement) -import DOM.HTML.Window (document) - -import DOM.Node.Types (Element()) - -import React - -import qualified React.DOM as D -import qualified React.DOM.Props as P - -incrementCounter ctx e = do - val <- readState ctx - writeState ctx (val + 1) - -counter = createClass $ spec 0 \ctx -> do - val <- readState ctx - return $ D.p [ P.className "Counter" - , P.onClick (incrementCounter ctx) - ] - [ D.text (show val) - , D.text " Click me to increment!" - ] - -main = container >>= render ui - where - ui :: ReactElement - ui = D.div [] [ createFactory counter {} ] - - container :: forall eff. Eff (dom :: DOM | eff) Element - container = do - win <- window - doc <- document win - elm <- fromJust <$> toMaybe <$> body doc - return $ htmlElementToElement elm -``` +Please refer to [purescript-react-example](https://github.com/purescript-contrib/purescript-react) diff --git a/bower.json b/bower.json index 6123ce1..93e66b0 100644 --- a/bower.json +++ b/bower.json @@ -1,26 +1,21 @@ { "name": "purescript-react", "homepage": "https://github.com/purescript-contrib/purescript-react", - "description": "Purescript bindings for React.js", + "description": "PureScript bindings for react", + "main": "", "keywords": [ "purescript", "react" ], "license": "MIT", "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" + "*", + "!src/**/*" ], "repository": { "type": "git", "url": "git://github.com/purescript-contrib/purescript-react.git" }, - "devDependencies": { - "purescript-console": "~0.1.1" - }, "dependencies": { "purescript-eff": "~0.1.2", "purescript-unsafe-coerce": "~0.1.0" diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 012a3cd..0000000 --- a/example/.gitignore +++ /dev/null @@ -1 +0,0 @@ -index.js diff --git a/example/index.html b/example/index.html deleted file mode 100644 index 3c2916c..0000000 --- a/example/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - purescript-react example - - - - - - diff --git a/src/React.js b/src/React.js index f03839a..46f34dd 100644 --- a/src/React.js +++ b/src/React.js @@ -60,7 +60,9 @@ function createClass(spec) { return spec.render(this)(); }, getInitialState: function(){ - return spec.getInitialState(this)(); + return { + state: spec.getInitialState(this)() + }; }, componentWillMount: function(){ return spec.componentWillMount(this)(); diff --git a/src/React.purs b/src/React.purs index ebdffa5..af9f6bc 100644 --- a/src/React.purs +++ b/src/React.purs @@ -51,6 +51,7 @@ module React , handle , createClass + , createClassStateless , createElement , createFactory ) where diff --git a/src/React/DOM.js b/src/React/DOM.js index 25727ad..5a70f0f 100644 --- a/src/React/DOM.js +++ b/src/React/DOM.js @@ -3,6 +3,8 @@ // module React.DOM +var React = require('react'); + function mkProps(props) { var result = {}; diff --git a/src/React/DOM/Props.js b/src/React/DOM/Props.js index 989dcfa..8f55bb8 100644 --- a/src/React/DOM/Props.js +++ b/src/React/DOM/Props.js @@ -3,26 +3,30 @@ // module React.DOM.Props -exports.unsafeMkProps = function(key) { - return function(value) { - var result = {}; - result[key] = value; - return result; - }; -}; +var React = require('react'); -exports.unsafeUnfoldProps = function(key) { - return function(value) { - var result = {}; - var props = {}; - props[key] = result; +function unsafeMkProps(key) { + return function(value){ + var result = {}; + result[key] = value; + return result; + }; +} +exports.unsafeMkProps = unsafeMkProps; - for (var subprop in value) { - if (value.hasOwnProperty(subprop)) { - result[subprop] = value[subprop]; - } - } +function unsafeUnfoldProps(key) { + return function(value){ + var result = {}; + var props = {}; + props[key] = result; - return props; - }; -}; + for (var subprop in value) { + if (value.hasOwnProperty(subprop)) { + result[subprop] = value[subprop]; + } + } + + return props; + }; +} +exports.unsafeUnfoldProps = unsafeUnfoldProps; diff --git a/test/Container.purs b/test/Container.purs deleted file mode 100644 index d2e4465..0000000 --- a/test/Container.purs +++ /dev/null @@ -1,19 +0,0 @@ -module Test.Container where - -import Prelude - -import React - -import qualified React.DOM as D -import qualified React.DOM.Props as P - -container = createClass $ spec unit \ctx -> do - children <- getChildren ctx - - let ui = D.div [ P.style { borderColor: "red" - , borderWidth: 2 - , padding: 10 - } - ] children - - return ui diff --git a/test/Main.js b/test/Main.js deleted file mode 100644 index d78c8de..0000000 --- a/test/Main.js +++ /dev/null @@ -1,12 +0,0 @@ -/* global exports */ -"use strict"; - -// module Test.Main - -exports.interval = function(ms) { - return function(action) { - return function() { - return setInterval(action, ms); - } - } -}; diff --git a/test/Main.purs b/test/Main.purs deleted file mode 100644 index 32c266e..0000000 --- a/test/Main.purs +++ /dev/null @@ -1,79 +0,0 @@ -module Test.Main where - -import Prelude - -import Control.Monad.Eff -import Control.Monad.Eff.Console - -import Data.Maybe.Unsafe (fromJust) -import Data.Nullable (toMaybe) - -import DOM (DOM()) -import DOM.HTML (window) -import DOM.HTML.Document (body) -import DOM.HTML.Types (htmlElementToElement) -import DOM.HTML.Window (document) - -import DOM.Node.Types (Element()) - -import React - -import qualified React.DOM as D -import qualified React.DOM.Props as P - -import Test.Container (container) - -foreign import interval :: forall eff a. - Int -> - Eff eff a -> - Eff eff Unit - -hello :: forall props. ReactClass { name :: String | props } -hello = createClass $ spec unit \ctx -> do - props <- getProps ctx - return $ D.h1 [ P.className "Hello" - , P.style { background: "lightgray" } - ] - [ D.text "Hello, " - , D.text props.name - ] - -counter :: forall props. ReactClass props -counter = createClass counterSpec - where - counterSpec = (spec 0 render) - { componentDidMount = \ctx -> - interval 1000 $ do - val <- readState ctx - print val - } - - render ctx = do - val <- readState ctx - return $ D.button [ P.className "Counter" - , P.onClick \_ -> do - val <- readState ctx - writeState ctx (val + 1) - ] - [ D.text (show val) - , D.text " Click me to increment!" - ] - -main :: forall eff. Eff (dom :: DOM | eff) Unit -main = void (body' >>= render ui) - where - ui :: ReactElement - ui = D.div' [ createFactory hello { name: "World" } - , createFactory counter unit - , createElement container unit - [ D.p [] [ D.text "This is line one" ] - , D.p [] [ D.text "This is line two" ] - ] - ] - - body' :: Eff (dom :: DOM | eff) Element - body' = do - win <- window - doc <- document win - elm <- fromJust <$> toMaybe <$> body doc - return $ htmlElementToElement elm From 6886401e68dc472b2eb363142048c97b0de86f38 Mon Sep 17 00:00:00 2001 From: eric thul Date: Sat, 28 Nov 2015 16:10:31 -0500 Subject: [PATCH 4/4] Updating documentation --- docs/React.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/React.md b/docs/React.md index 6de9af3..fa21842 100644 --- a/docs/React.md +++ b/docs/React.md @@ -10,6 +10,14 @@ data ReactElement :: * A virtual DOM node, or component. +#### `ReactComponent` + +``` purescript +data ReactComponent :: * +``` + +A mounted react component + #### `ReactThis` ``` purescript @@ -294,6 +302,14 @@ createClass :: forall props state eff. ReactSpec props state eff -> ReactClass p Create a React class from a specification. +#### `createClassStateless` + +``` purescript +createClassStateless :: forall props. (props -> ReactElement) -> ReactClass props +``` + +Create a stateless React class. + #### `handle` ``` purescript @@ -318,20 +334,4 @@ createFactory :: forall props. ReactClass props -> props -> ReactElement Create a factory from a React class. -#### `render` - -``` purescript -render :: forall eff. ReactElement -> Element -> Eff (dom :: DOM | eff) ReactElement -``` - -Render a React element in a document element. - -#### `renderToString` - -``` purescript -renderToString :: ReactElement -> String -``` - -Render a React element as a string. -