glazier-react-widget
Generic widget library using glazier-react
https://github.com/louispan/glazier-react-widget#readme
| Version on this page: | 0.6.0.0 |
| LTS Haskell 9.21: | 0.6.0.0 |
| Stackage Nightly 2018-09-28: | 1.0.0.0 |
| Latest on Hackage: | 1.0.0.0 |
glazier-react-widget-0.6.0.0@sha256:5cf9c9f988b418deb6f59fc7339b8ec016db1e6a9a27526146ec8e21b9a19134,1770Module documentation for 0.6.0.0
- Glazier
- Glazier.React
This is a library of reusable composable widget using
Glazier.React. Please help me to add more widgets to this library!
Prerequisite reading
Glazier
Please read the README.md for a brief overview of glazier.
Glazier.React
Please read the README.md for a brief overview of glazier-react.
Widget best practice
The following documents the expected conventions and best practices when defining a Glazier.React.Widgets widget.
Exports
All widgets should export at the minimum the following:
module Glazier.React.Widgets.Input
( Command(..)
, Action(..)
, AsAction(..)
, Design(..)
, HasDesign(..)
, Plan(..)
, HasPlan(..)
, Outline
, Model
, Widget
, widget
) where
This provides a consistent way to interact and use every widget.
Since all widgets export the same names, any widget should be imported qualified.
Command
Commands are the result of the Gadget stateful processing of Action. It is a pure value that is interpreted effectfully.
data Command
= RenderCommand (SuperModel Model Plan) [Property] JSVal
| DisposeCommand SomeDisposable
| MakerCommand (F (Maker Action) Action)
Some common commands are:
RenderCommand
RenderCommand (SuperModel Model Plan) [Property] JSVal
This is send by Gadget when the re-rendering is required. It contains theSuperModel of the widget (to swap the latest Design into the Frame), the new React component state as a list of properties (usually just a sequence number), and the javascript reference to the javascript component.
DisposeCommand
DisposeCommand SomeDisposable
This contains the list of callbacks to dispose after the next render frame (after componentDidUpdate is called.
MakerCommand
MakerCommand (F (Maker Action) Action)
This is the command to run the Maker instruction in the Maker interpreter which results in an Action to dispatch back tot he gadget.
Action
This contains the events that the widget Gadget processes.
data Action
= ComponentRefAction JSVal
| RenderAction
| ComponentDidUpdateAction
makeClassyPrisms ''Action
Actions should have makeClassyPrisms generated to facilitate embedding it in larger Gadget with magnify.
Some common Actions are:
ComponentRefAction
ComponentRefAction JSVal
This action is generated by the ref event listener and contains a javascript reference to the react component. This ref is used in the RenderCommand.
RenderAction
RenderAction
You can generate this action to force a widget to return the RenderCommand to force a re-render.
ComponentDidUpdateAction
ComponentDidUpdateAction JSVal
This action is generated by the componentDidUpdate event listener. This event is usually used to generate the DisposeCommand to dispose callbacks from removed widgets.
Design
This contains the template for pure data for state processing logic (the nouns).
data Design = Design
{ _blah :: Foo
}
makeClassy ''Design
type Model = Design
type Outline = Design
instance R.ToOutline Model Outline where outline = id
mkModel :: Outline -> F (R.Maker Action) Model
mkModel = pure
Designs should have makeClassy generated to facilitate embedding it in larger widget with magnify and zoom.
If the Design contains child widgets, then it should have use a type parameter and DesignType to allow specializations of Model and Outline
data Design t = Design
{ _input :: R.DesignType t W.Input.Widget
, _todos :: R.DesignType t (W.List.Widget TodosKey TD.Todo.Widget)
, _footer :: R.DesignType t TD.Footer.Widget
}
type Model = Design R.WithGizmo
type Outline = Design R.WithOutline
instance R.ToOutline Model Outline where
outline (Design a b c) = Design (R.outline a) (R.outline b) (R.outline c)
mkModel :: R.ReactMlT Identity () -> Outline -> F (R.Maker Action) Model
mkModel separator (Design a b c) = Design
<$> (R.hoistWithAction InputAction (R.mkGizmo' W.Input.widget a))
<*> (R.hoistWithAction TodosAction (R.mkGizmo' (W.List.widget separator TD.Todo.widget) b))
<*> (R.hoistWithAction FooterAction (R.mkGizmo' TD.Footer.widget c))
Plan
The Plan contains the callbacks for integrating with React (the verbs). It also contains a javascript reference to the instance of shim component used for the widget. This reference is used to trigger rendering with setState.
data Plan = Plan
{ _component :: R.ReactComponent
, _key :: J.JSString
, _frameNum :: Int
, _componentRef :: J.JSVal
, _deferredDisposables :: D.DList CD.SomeDisposable
, _onRender :: J.Callback (J.JSVal -> IO J.JSVal)
, _onComponentRef :: J.Callback (J.JSVal -> IO ())
, _onComponentDidUpdate :: J.Callback (J.JSVal -> IO ()) makeClassy ''Plan
Plans should have makeClassy generated to allow consistent usage of lens to access Model and Plan fields.
Some common Plan fields are
key
_key :: JSString
key is used to ensure a unique key for React’s efficient rendering of a list.
componentRef
_componentRef :: JSVal
componentRef is used to store the reference to the instance of the React shim component from the ComponentRefAction and used in the RenderCommand
frameNum
_frameNum :: Int
frameNum is the sequence number used in RenderCommand.
_deferredDisposables
_deferredDisposables :: DList SomeDisposable
deferredDisposables keep the list of disposables to dispose at the next ComponentDidUpdateAction.
_component
_component :: ReactComponent
This contains the reference to the shim React.PureComponent class that is used to start the rendering.
_onRender
_onRender :: Callback (JSVal -> IO JSVal)
The is the callback from the shim component’s render handler. It contains a javascript reference to the shim component’s state, which is currently not used, but might be in the future.
_onComponentRef
_onComponentRef :: Callback (JSVal -> IO ())
The is the callback from the shim component’s ref event listener. The callback is expected to generate the ComponentRefAction.
_onComponentDidUpdate
_onComponentDidUpdate :: Callback (JSVal -> IO ())
The is the callback from the shim component’s componentDidUpdate event listener. The callback is expected to generate the ComponentDidUpdateAction.
mkPlan
This is the missing piece required to construct a widget’s SuperModel.
It contains the code to create a widget’s Plan using the Maker DSL.
The Applicative typeclass makes this easy to define.
mkPlan :: Frame Model Plan -> F (Maker Action) Plan
mkPlan frm = Plan
<$> R.getComponent
<*> R.mkKey
<*> pure 0
<*> pure J.nullRef
<*> pure mempty
<*> (mkRenderer frm $ const render)
<*> (mkHandler $ pure . pure . InputRefAction)
<*> (mkHandler $ pure . pure . ComponentRefAction)
<*> (mkHandler $ pure . pure . const ComponentDidUpdateAction)
Common code
All widgets should have implementation of the following
Disposing Model and Plan
instance Disposing Plan
instance Disposing Model where
disposing _ = DisposeNone
Link HasPlan and HasModel
Link Glazier.React.Model‘s genericHasPlan/HasModel with this widget’s specific HasPlan/HasModel from generated from makeClassy
instance HasPlan (R.Scene Model Plan) where
plan = R.plan
instance HasDesign (R.Scene Model Plan) where
design = R.model
instance HasPlan (R.Gizmo Model Plan) where
plan = R.scene . plan
instance HasDesign (R.Gizmo Model Plan) where
design = R.scene . design
Widget definitions
widget is a record of functions of the essential functions required to make, render and interact with the widget. By convention, mkPlan, window, and gadget is exported, but sometimes it’s convenient to have all three grouped together in a record.
type Widget = Widget Command Action Model Plan
widget :: Widget
widget = R.Widget
mkModel
mkPlan
window
gadget
widget is always an instance of IsWidget typeclass, so exporting a type synomym Widget will allow generic widget manipulation code.
For example, the List widget uses the IsWidget typeclass of the item widgets in order to define the widget record value.
window
This is the starting rendering function to start the rendering. It always only renders the shim React component with the specific callbacks:
window :: WindowT (Design Model Plan) (ReactMlT Identity) ()
window = do
s <- ask
lift $ lf (s ^. component . to toJS)
[ ("key", s ^. key . to toJS)
, ("render", s ^. onRender . to toJS)
, ("ref", s ^. onComponentRef . to toJS)
, ("componentDidUpdate", s ^. onComponentDidUpdate . to toJS)
]
This a a monad transformer stack over Identity. This ensures only pure effects are allowed.
render
This is the inner rendering function. React will render the shim component from window above, and then call the Plan’s onRender callback of the shim component, which triggers this rendering function.
This contains the widget specific rendering instructions.
render :: WindowT (Design Model Plan) (ReactMlT Identity) ()
This a a monad transformer stack over Identity. This ensures only pure effects are allowed.
gadget
This contains the state update logic:
gadget :: G.GadgetT Action (R.SuperModel Model Plan) Identity (DList Command)
This a a monad transformer stack over Identity. This ensures only pure effects are allowed.
When required, STM can always be hoist (hoist generalize) into the gadget using Control.Monad.Morph.