Module

Elmish

Package
purescript-elmish
Repository
collegevine/purescript-elmish

Re-exports from Elmish.Boot

#BootRecord Source

type BootRecord props = { hydrate :: String -> props -> Effect Unit, mount :: String -> props -> Effect Unit, renderToString :: props -> String }

Boot record for a UI component. See comments for this module.

#boot Source

boot :: forall props state msg. (props -> ComponentDef Aff msg state) -> BootRecord props

Creates a boot record for the given component. See comments for this module.

Re-exports from Elmish.Component

#Transition Source

data Transition m msg state

A UI component state transition: wraps the new state value together with a (possibly empty) list of effects that the transition has caused (called "commands"), with each command possibly producing some new messages.

Instances of this type may be created either by using the smart constructor:

update :: State -> Message -> Transition Aff Message State
update state m = transition state [someCommand]

or in monadic style (see comments on fork for more on this):

update :: State -> Message -> Transition Aff Message State
update state m = do
    s1 <- Child1.update state.child1 Child1.SomeMessage # lmap Child1Msg
    s2 <- Child2.modifyFoo state.child2 # lmap Child2Msg
    fork someEffect
    pure state { child1 = s1, child2 = s2 }

or, for simple sub-component delegation, the BiFunctor instance may be used:

update :: State -> Message -> Transition Aff Message State
update state (ChildMsg m) =
    Child.update state.child m
    # bimap ChildMsg (state { child = _ })

Constructors

Instances

#ComponentDef Source

type ComponentDef m msg state = { init :: Transition m msg state, update :: state -> msg -> Transition m msg state, view :: state -> DispatchMsgFn msg -> ReactElement }

Definition of a component according to The Elm Architecture. Consists of three functions - init, view, update, - that together describe the lifecycle of a component.

Type parameters:

  • m - a monad in which the effects produced by update and init functions run.
  • msg - component's message.
  • state - component's state.

#withTrace Source

withTrace :: forall state msg m. DebugWarning => ComponentDef m msg state -> ComponentDef m msg state

Wraps the given component, intercepts its update cycle, and traces (i.e. prints to dev console) every command and every state value (as JSON objects).

#transition Source

transition :: forall msg state m. Bind m => MonadEffect m => state -> Array (m msg) -> Transition m msg state

Smart constructor for the Transition type. See comments there. This function takes the new (i.e. update) state and an array of commands - i.e. effects producing messages - and constructs a Transition out of them

#rmap Source

rmap :: forall c b a f. Bifunctor f => (b -> c) -> f a b -> f a c

Map a function over the second type arguments of a Bifunctor.

#pureUpdate Source

pureUpdate :: forall state msg m. state -> Transition m msg state

Creates a Transition without any commands. This function will be deprecated soon in favor of pure.

#nat Source

nat :: forall state msg n m. (forall a. m a -> n a) -> ComponentDef m msg state -> ComponentDef n msg state

Monad transformation applied to ComponentDef

#lmap Source

lmap :: forall c b a f. Bifunctor f => (a -> b) -> f a c -> f b c

Map a function over the first type argument of a Bifunctor.

#forks Source

forks :: forall message m. Command m message -> Transition m message Unit

Similar to fork (see comments there for detailed explanation), but the parameter is a function that takes a message-dispatching callback. This structure allows the command to produce zero or multiple messages, unlike fork, whose callback has to produce exactly one.

Example:

update :: State -> Message -> Transition Aff Message State
update state msg = do
    forks countTo10
    pure state

countTo10 :: Command Aff Message
countTo10 msgSink =
    for_ (1..10) \n ->
        delay $ Milliseconds 1000.0
        msgSink $ Count n

#forkVoid Source

forkVoid :: forall message m. m Unit -> Transition m message Unit

Similar to fork (see comments there for detailed explanation), but the effect doesn't produce any messages, it's a fire-and-forget sort of effect.

#forkMaybe Source

forkMaybe :: forall message m. MonadEffect m => m (Maybe message) -> Transition m message Unit

Similar to fork (see comments there for detailed explanation), but the effect may or may not produce a message, as modeled by returning Maybe.

#fork Source

fork :: forall message m. MonadEffect m => m message -> Transition m message Unit

Creates a Transition that contains the given command (i.e. a message-producing effect). This is intended to be used for "accumulating" effects while constructing a transition in imperative-ish style. When used as an action inside a do, this function will have the effect of "adding the command to the list" to be executed. The name fork reflects the fact that the given effect will be executed asynchronously, after the update function returns.

In more precise terms, the following:

trs :: Transition m Message State
trs = do
    fork f
    fork g
    pure s

Is equivalent to this:

trs :: Transition m Message State
trs = Transition s [f, g]

At first glance it may seem that it's shorter to just call the Transition constructor, but monadic style comes in handy for composing the update out of smaller pieces. Here's a more full example:

data Message = Nop | ButtonClicked | OnNewItem String

update :: State -> Message -> Transition Aff Message State
update state Nop =
    pure state
update state ButtonClick = do
    fork $ insertItem "new list"
    incButtonClickCount state

insertItem :: Aff Message
insertItem name = do
    delay $ Milliseconds 1000
    pure $ OnNewItem name

incButtonClickCount :: Transition Aff Message State
incButtonClickCount state = do
    fork $ trackingEvent "Button click" *> pure Nop
    pure $ state { buttonsClicked = state.buttonsClicked + 1 }

#construct Source

construct :: forall state msg. ComponentDef Aff msg state -> Effect (DispatchMsgFn Void -> ReactElement)

Given a ComponentDef, binds that def to a freshly created React class, instantiates that class, and returns a rendering function. Note that the return type of this function is almost the same as that of ComponentDef::view - except for state. This is not a coincidence: it is done this way on purpose, so that the result of this call can be used to construct another ComponentDef.

Unlike wrapWithLocalState, this function uses the bullet-proof strategy of storing the component state in a dedicated mutable cell, but that happens at the expense of being effectful.

#bimap Source

bimap :: forall f d c b a. Bifunctor f => (a -> b) -> (c -> d) -> f a c -> f b d

#(<$$>) Source

Operator alias for Elmish.Component.mapCmds (non-associative / precedence 8)

A nested map - useful for mapping over commands in an array: first map maps over the array, second map maps over the monad m.

Example:

 let (Transition subS cmds) = SubComponent.update s.subComponent msg
 in Transition (s { subComponent = subS }) (SubComponentMsg <$$> cmds)

Re-exports from Elmish.Dispatch

#DispatchMsgFn Source

newtype DispatchMsgFn msg

Represents a function that a view can use to report both errors and messages originating from JS/DOM. Underneath it's just a function that takes an Either, but it is wrapped in a newtype in order to provide class instances for it.

Constructors

Instances

#DispatchMsg Source

#DispatchError Source

#handleMaybe Source

handleMaybe :: forall effFn fn msg. MkEventHandler (Maybe msg) fn effFn => MkJsCallback effFn => DispatchMsgFn msg -> fn -> JsCallback effFn

A version of handle (see comments there) with a possibility of not producing a message.

#handle Source

handle :: forall effFn fn msg. MkEventHandler msg fn effFn => MkJsCallback effFn => DispatchMsgFn msg -> fn -> JsCallback effFn

Creates a JsCallback that uses the given DispatchMsgFn to either issue a message or report an error. The fn parameter is either a message or a function that produces a message. When the JS code calls the resulting JsCallback, its parameters are validated, then the fn function is called to produce a message, which is then reported via the given DispatchMsgFn, unless the parameters passed from JS cannot be decoded, in which case an error is reported via DispatchMsgFn.

Example of intended usage:

 -- PureScript
 data Message = A | B Int | C String Boolean

 view state dispatch = createElement' viewCtor_
     { foo: "bar"
     , onA: handle dispatch A
     , onB: handle dispatch B
     , onC: handle dispatch C
     , onBaz: handle dispatch \x y -> B (x+y)
     }

 // JSX:
 export const viewCtor_ = args =>
     <div>
         Foo is {args.bar}<br />
         <button onClick={args.onA}>A</button>
         <button onClick={() => args.onB(42)}>B</button>
         <button onClick={() => args.onC("hello", true)}>C</button>
         <button onClick={() => args.onBaz(21, 21)}>Baz</button>
     </div>

#(>$<) Source

Operator alias for Data.Functor.Contravariant.cmap (left-associative / precedence 4)

#(>#<) Source

Operator alias for Data.Functor.Contravariant.cmapFlipped (left-associative / precedence 4)

Re-exports from Elmish.JsCallback

#JsCallback0 Source

type JsCallback0 = JsCallback (Effect Unit)

A parameterless JsCallback

#JsCallback Source

newtype JsCallback fn

This type represents a function that has been wrapped in a way suitable for passing to JavaScript (including parameter validation). The primary use case for such callbacks is to pass them to JSX code for receiving DOM-generated events and turning them into UI messages. See MkJsCallback for more info and examples.

Instances

#jsCallback Source

jsCallback :: forall fn. MkJsCallback fn => fn -> JsCallback fn

Wraps a given effect fn (possibly with parameters) as a JS non-curried function with parameter type validation, making it suitable for passing to unsafe JS code.

This function should not (or at least rarely) be used directly. In normal scenarios, Elmish.Dispatch.handle should be used instead.

Example:

  -- PureScript:
  createElement' theView_
      { onSave: jsCallback $ Console.log "Save"
      , onCancel: jsCallback $ Console.log "Cancel"
      , onFoo: jsCallback \(bar::String) (baz::Int) ->
          Console.log $ "bar = " <> bar <> ", baz = " <> show baz
      }

 // JSX:
 export const TheView = props =>
   <div>
     <button onClick={props.onSave}>Save</button>
     <button onClick={props.onCancel}>Cancel</button>
     <button onClick={() => props.onFoo("bar", 42)}>Foo</button>
   </div>

In this example, the parameters bar and baz will undergo validation at runtime to make sure they are indeed a String and an Int respectively, and an error will be issued if validation fails.

Re-exports from Elmish.React

#ReactElement Source

data ReactElement :: Type

Instantiated subtree of React DOM. JSX syntax produces values of this type.

Instances

#ReactComponent Source

data ReactComponent :: Type -> Type

This type represents constructor of a React component with a particular behavior. The type prameter is the record of props (in React lingo) that this component expects. Such constructors can be "rendered" into ReactElement via createElement.

#createElement' Source

createElement' :: forall props. ValidReactProps props => ReactComponent props -> props -> ReactElement

Variant of createElement for creating an element without children.

#createElement Source

createElement :: forall content props. ValidReactProps props => ReactChildren content => ReactComponent props -> props -> content -> ReactElement

The PureScript import of the React’s createElement function. Takes a component constructor, a record of props, some children, and returns a React DOM element.

To represent HTML data- attributes, createElement supports the _data :: Object prop.

Example

import Elmish.HTML as H
import Foreign.Object as FO

H.div
  { _data: FO.fromHomogenous { toggle: "buttons } }
  [...]

represents the <div data-toggle="buttons"> DOM element.

Re-exports from Elmish.Ref

#Ref Source

newtype Ref (name :: Symbol) a

An opaque reference for tunneling through JSX code.

This type is a wrapper that lets us pass any PureScript values into JSX code, with the expectation that the JSX code cannot mess with (inspect, mutate) these values, but can pass them back to the PureScript code in messages. This type has instances of CanPassToJavaScript and CanReceiveFromJavaScript, which allows it to be passed in React props or view messages.

One challenge with this type is that we can't just unsafeCoerce its values back and forth, because that would open a very big hole for data corruption to get in. To have some protection against it, we add a weak form of verification: internally values of Ref are represented by a JavaScript hash with a sole key looking like "ref:name", whose value is the target of the ref, and where "name" is the first type argument of this Ref. This way, we have at least something to verify (see the CanReceiveFromJavaScript instance below) that the object passed by the JSX code is not some random value, but actually originated as a Ref a of the right type.

Admittedly, this is only weak protection, because the JSX code can still, if it really wanted to, construct a hash like { "ref:name": "abracadabra"} and pass it to the PureScript code, which would happily accept the "abracadabra" value as if it was the right type.

Here are my arguments for why this weak protection is enough:

  1. The JSX code has to actually try to be destructive. Can't happen by accident.
  2. It's technically impossible to do any better without putting significant restrictions on the type a (i.e. requiring it to be Generic or to provide type name, etc.), and without losing some performance.
  3. If such corruption proves to be a problem in the future, we can always fall back to encoding/decoding Json, and pay some performance for it.

Instances

#ref Source

ref :: forall a name. IsSymbol name => a -> Ref name a

Creates an instance of Ref. See comments on it above.

#deref Source

deref :: forall a name. IsSymbol name => Ref name a -> a

Deconstructs an instance of Ref. See comments on it above.