Concur UI Lib is a brand new client side Web UI framework that explores an entirely new paradigm. It does not follow FRP (think Reflex or Reactive Banana), or Elm architecture, but aims to combine the best parts of both. This repo contains the Concur implementation for Purescript, using the React backend.
Work in progress tutorials are published in the Concur Documentation site
API documentation is published on Pursuit.
Purescript-Concur is reasonably light. The entire uncompressed JS bundle, including react and all libraries, for the entire example application in this repo clocks in at 180KB. You can build this bundle yourself with the command npm run prod
(currently broken due to the move to spago).
This leads to pretty fast initial load times. Running the Chrome audit on https://purescript-concur.github.io/purescript-concur-react/ produces -
Concur's model translates well to other platforms.
You can quickly get a production setup going (using Spago and Parcel) by cloning the Purescript Concur Starter.
Else, if you use Spago -
spago install concur-react
git clone https://github.com/purescript-concur/purescript-concur-react.git
cd purescript-concur-react
npm install
# Build library sources
npm run build
# Build examples
npm run examples
# Start a local server
npm run examples-start
# Check examples
open localhost:1234 in the browser
Concur Widgets can be exported as both React classes or React Elements, which would allow them to be used within Javascript code.
Let's say you have a counter :: Int -> Widget HTML a
concur widget that you want to expose to React.
Step 1: Convert the Widget
to a ReactClass
using Concur.React.toReactClass
. Here you would like the react class to accept a {conut :: Int}
as props.
Here, the "Counter"
is the name of the component that will be visible to React. You can use any name.
And mempty
represents the initial view shown until the widget has finished initialising. We can leave this empty (views have a Monoid
instance).
-- Counter.purs
counterReactClass :: ReactClass { count :: Int }
counterReactClass = toReactClass "Counter" mempty \ {count: i} -> counter i
Step 2: Import the class from within Javascript, and give it a name starting with an uppercase letter as required by React.
// MyApp.jsx
import {counterClass} from '<path/to/output/folder>/Counter/index.js';
// React requires all component names to start with an uppercase letter -
let Counter = counterClass;
Step 3: Now you can use it normally from within React.
// MyApp.jsx
class ReactComponent extends Component {
render(props) {
let {count} = this.state;
return (
<div>
<h4>The counter below was imported from a Concur widget. The starting count of 10 was passed from within React</h4>
<Counter count={10} />
</div>
);
}
It's easy to add external React components to Concur. Usually all you would require to wrap an external component is to import it as a ReactClass
, and then wrapping it with one of the el
functions.
For example, let's say you want to wrap the Button
component provided by the material-ui library.
Step 1: First write an FFI module that exposes the ReactClass
component -
// Button.js
exports.classButton = require('@material-ui/core/Button').default
And import it into your purescript program
-- Button.purs
foreign import classButton :: forall a. ReactClass a
If you are using the Purescript React MUI bindings, then you can simply import the class component from the library without defining the FFI module -
import MaterialUI.Button (classButton)
Step 2: Then wrap up the imported ReactClass
into a widget to make it usable within Concur -
import Concur.React.DOM (El, el')
import React (unsafeCreateElement)
import React.DOM.Props (unsafeFromPropsArray)
button :: El
button = el' (unsafeCreateElement classButton <<< unsafeFromPropsArray)
Step 3: Now you can use button
normally within Concur. For example -
import Concur.React.DOM as D
import Concur.React.Props as P
helloButton = button [P.onClick] [D.text "Hello World!"]
Note that you can mix in the default widgets and props with the MUI ones.
Demo and Source for composing all the examples in one page.
Individual example sources -