Skip to content

Remove Array JSX "children" argument #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock.json binary
22 changes: 9 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ This package implements an opinionated set of bindings to the React library, opt

## Features

- All React DOM elements and attributes are supported.
- All React DOM elements and attributes are supported (soon, events are a work in progress).
- An intuitive API for specifying props - no arrays of key value pairs, just records.
- Attributes are optional, but type-checked. It is a type error to specify `href` as an integer, for example.

## Getting Started

You can install this package using Bower:

```
```sh
bower install [email protected]:lumihq/purescript-react-basic.git
```

Expand All @@ -24,36 +24,32 @@ module React.Basic.Example where
import Prelude

import Control.Monad.Eff.Uncurried (mkEffFn1)
import React.Basic as R
import React.Basic (ReactComponent, react)
import React.Basic.DOM as R

-- The props for the component
type ExampleProps =
{ label :: String
}

-- The internal state of the component
type ExampleState =
{ counter :: Int
}

-- Create a component by passing a record to the `react` function.
-- The `render` function takes the props and current state, as well as a
-- state update callback, and produces a document.
example :: R.ReactComponent ExampleProps
example = R.react
example :: ReactComponent ExampleProps
example = react
{ initialState: { counter: 0 }
, receiveProps: \_ _ _ -> pure unit
, render: \{ label } { counter } setState ->
R.button { onClick: mkEffFn1 \_ -> do
setState { counter: counter + 1 }
setState \s -> { counter: s.counter + 1 }
, children: [ R.text (label <> ": " <> show counter) ]
}
[ R.text (label <> ": " <> show counter) ]
}
```

This component can be used directly from JavaScript. For example, if you are using `purs-loader`:

```javascript
```jsx
import {example as Example} from 'React.Basic.Example.purs';

const myComponent = () => (
Expand Down
43 changes: 25 additions & 18 deletions codegen/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const props = require('react-html-attributes');
const voids = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
const props = require("react-html-attributes");
const voids = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"];
const types = {
"allowFullScreen": "Boolean",
"async": "Boolean",
"autoPlay": "Boolean",
"capture": "Boolean",
"checked": "Boolean",
"children": "Array JSX",
"cols": "Number",
"controls": "Boolean",
"default": "Boolean",
Expand All @@ -30,29 +31,35 @@ const types = {
"selected": "Boolean",
"size": "Number",
"span": "Number",
"start": "Number",
"zoomAndPan": "String"
"start": "Number"
};
const reserved = ["module", "data", "type", "newtype", "class", "instance", "where", "derive", "if", "then", "else", "case", "of"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're using these as record labels, there's no need to escape keywords (at least in recent versions of PureScript).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They become function names though, like div. The one that was giving me problems was data. Apparently it's an HTML tag.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize data was a html tag. None of the others listed in reserved are html tags are they? @paf31 Was data not causing a problem in the previous version of this script?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed I got a newer version of react-html-attributes or something. It seemed safer to add everything to the reserved list than have to remember and go back later if type or module become elements.


printRecord = (elProps) => `
printRecord = (elProps) => elProps.length ? `
( ${ elProps.map((p) =>
`${p} :: ${types[p] || 'String'}`).join('\n , ')
`${p} :: ${types[p] || "String"}`).join("\n , ")
}
)`
)` : "()"

props.elements.html
.map((e) =>`
type Props_${e} = ${
props[e]
? printRecord(props[e])
: '()'
}
.map((e) => {
const noChildren = voids.includes(e);
const symbol = reserved.includes(e) ? `${e}'` : e;
return `
type Props_${e} = ${printRecord(
(noChildren ? [] : ["children"]).concat(props[e] || []).sort()
)}

${e}
${symbol}
:: forall attrs attrs_
. Union attrs attrs_ (SharedProps Props_${e}))
. Union attrs attrs_ (SharedProps Props_${e})
=> Record attrs
-> Array JSX
-> JSX
${e} = ${voids.indexOf(e) >= 0 ? 'createElementNoChildren' : 'createElement'} "${e}"
`).forEach((x) => console.log(x.replace(/^\n\ {4}/, '').replace(/\n\ {4}/g, '\n')))
${symbol} = createElement (unsafeCreateDOMComponent "${e}")${
noChildren ? "" : `

${e}_ :: Array JSX -> JSX
${e}_ children = ${symbol} { children }`
}
`;
}).forEach((x) => console.log(x.replace(/^\n\ {4}/, "").replace(/\n\ {4}/g, "\n")))
5 changes: 2 additions & 3 deletions examples/component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
"keywords": [],
"author": "",
"dependencies": {
"create-react-class": "^15.6.2",
"react": "^15.6.2",
"react-dom": "^15.6.2"
"react": "^16.2.0",
"react-dom": "^16.2.0"
},
"devDependencies": {
"browserify": "^16.1.0"
Expand Down
35 changes: 19 additions & 16 deletions examples/component/src/Container.purs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
module Container where

import Prelude

import React.Basic as R
import ToggleButton as ToggleButton

component :: R.ReactComponent Unit
component = R.react
{ initialState: unit
, receiveProps: \_ _ _ -> pure unit
, render: \_ _ setState ->
R.div { } [ R.component ToggleButton.component { on: true }
, R.component ToggleButton.component { on: false }
]
}
module Container where

import Prelude

import React.Basic (ReactComponent, createElement, react)
import React.Basic.DOM as R
import ToggleButton as ToggleButton

component :: ReactComponent Unit
component = react
{ displayName: "Container"
, initialState: unit
, receiveProps: \_ _ _ -> pure unit
, render: \_ _ setState ->
R.div { children: [ createElement ToggleButton.component { on: true }
, createElement ToggleButton.component { on: false }
]
}
}
46 changes: 22 additions & 24 deletions examples/component/src/ToggleButton.purs
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
module ToggleButton where

import Prelude

import Control.Monad.Eff.Uncurried (mkEffFn1)
import React.Basic as R

type ExampleProps =
{ on :: Boolean
}

type ExampleState =
{ on :: Boolean
}

component :: R.ReactComponent ExampleProps
component = R.react
{ initialState: { on: false }
, receiveProps: \{ on } _ setState -> setState { on }
, render: \_ { on } setState ->
R.button { onClick: mkEffFn1 \_ -> setState { on: not on }
}
[ R.text if on then "On" else "Off" ]
}
module ToggleButton where

import Prelude

import Control.Monad.Eff.Uncurried (mkEffFn1)
import React.Basic (ReactComponent, react)
import React.Basic.DOM as R

type ExampleProps =
{ on :: Boolean
}

component :: ReactComponent ExampleProps
component = react
{ displayName: "ToggleButton"
, initialState: { on: false }
, receiveProps: \{ on } _ setState -> setState (const { on })
, render: \_ { on } setState ->
R.button { onClick: mkEffFn1 \_ -> setState \s -> { on: not s.on }
, children: [ R.text if on then "On" else "Off" ]
}
}
4 changes: 2 additions & 2 deletions examples/counter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"author": "",
"dependencies": {
"create-react-class": "^15.6.2",
"react": "^15.6.2",
"react-dom": "^15.6.2"
"react": "^16.2.0",
"react-dom": "^16.2.0"
},
"devDependencies": {
"browserify": "^16.1.0"
Expand Down
57 changes: 27 additions & 30 deletions examples/counter/src/Counter.purs
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
module Counter where

import Prelude

import Control.Monad.Eff.Uncurried (mkEffFn1)
import React.Basic as R

-- The props for the component
type ExampleProps =
{ label :: String
}

-- The internal state of the component
type ExampleState =
{ counter :: Int
}

-- Create a component by passing a record to the `react` function.
-- The `render` function takes the props and current state, as well as a
-- state update callback, and produces a document.
component :: R.ReactComponent ExampleProps
component = R.react
{ initialState: { counter: 0 }
, receiveProps: \_ _ _ -> pure unit
, render: \{ label } { counter } setState ->
R.button { onClick: mkEffFn1 \_ -> do
setState { counter: counter + 1 }
}
[ R.text (label <> ": " <> show counter) ]
}
module Counter where

import Prelude

import Control.Monad.Eff.Uncurried (mkEffFn1)
import React.Basic (ReactComponent, react)
import React.Basic.DOM as R

-- The props for the component
type ExampleProps =
{ label :: String
}

-- Create a component by passing a record to the `react` function.
-- The `render` function takes the props and current state, as well as a
-- state update callback, and produces a document.
component :: ReactComponent ExampleProps
component = react
{ displayName: "Counter"
, initialState: { counter: 0 }
, receiveProps: \_ _ _ -> pure unit
, render: \{ label } { counter } setState ->
R.button { onClick: mkEffFn1 \_ -> do
setState \s -> { counter: s.counter + 1 }
, children: [ R.text (label <> ": " <> show counter) ]
}
}
Loading