glazier-react 0.4.0.0 → 0.5.0.0
raw patch · 7 files changed
+171/−133 lines, 7 filesdep ~disposablePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: disposable
API changes (from Hackage documentation)
- Glazier.React.Maker: mapAction :: (act -> act') -> Maker act a -> Maker act' a
- Glazier.React.Model: Design :: mdl -> pln -> Design mdl pln
- Glazier.React.Model: SuperModel :: Design mdl pln -> Frame mdl pln -> SuperModel mdl pln
- Glazier.React.Model: [_design] :: SuperModel mdl pln -> Design mdl pln
- Glazier.React.Model: class HasDesign c mdl pln | c -> mdl pln
- Glazier.React.Model: class (HasDesign c mdl pln, HasFrame c mdl pln) => HasSuperModel c mdl pln | c -> mdl pln
- Glazier.React.Model: data Design mdl pln
- Glazier.React.Model: data SuperModel mdl pln
- Glazier.React.Model: design :: HasDesign c mdl pln => Lens' c (Design mdl pln)
- Glazier.React.Model: instance (Control.Disposable.Disposing pln, Control.Disposable.Disposing mdl) => Control.Disposable.Disposing (Glazier.React.Model.Design mdl pln)
- Glazier.React.Model: instance Control.Disposable.Disposing (Glazier.React.Model.Design mdl pln) => Control.Disposable.Disposing (Glazier.React.Model.SuperModel mdl pln)
- Glazier.React.Model: instance GHC.Generics.Generic (Glazier.React.Model.Design mdl pln)
- Glazier.React.Model: instance GHC.Generics.Generic (Glazier.React.Model.SuperModel mdl pln)
- Glazier.React.Model: instance Glazier.React.Model.HasDesign (Glazier.React.Model.Design mdl pln) mdl pln
- Glazier.React.Model: instance Glazier.React.Model.HasDesign (Glazier.React.Model.SuperModel mdl pln) mdl pln
- Glazier.React.Model: instance Glazier.React.Model.HasFrame (Glazier.React.Model.SuperModel mdl pln) mdl pln
- Glazier.React.Model: instance Glazier.React.Model.HasModel (Glazier.React.Model.Design mdl pln) mdl
- Glazier.React.Model: instance Glazier.React.Model.HasModel (Glazier.React.Model.SuperModel mdl pln) mdl
- Glazier.React.Model: instance Glazier.React.Model.HasPlan (Glazier.React.Model.Design mdl pln) pln
- Glazier.React.Model: instance Glazier.React.Model.HasPlan (Glazier.React.Model.SuperModel mdl pln) pln
- Glazier.React.Model: instance Glazier.React.Model.HasSuperModel (Glazier.React.Model.SuperModel mdl pln) mdl pln
- Glazier.React.Model: superModel :: HasSuperModel c mdl pln => Lens' c (SuperModel mdl pln)
- Glazier.React.Widget: Widget :: (Frame m p -> F (Maker a) p) -> (WindowT (Design m p) (ReactMlT Identity) ()) -> (GadgetT a (SuperModel m p) Identity (DList c)) -> Widget c a m p
- Glazier.React.Widget: instance (Control.Disposable.Disposing m, Control.Disposable.Disposing p) => Glazier.React.Widget.IsWidget (Glazier.React.Widget.Widget c a m p)
- Glazier.React.Widget: mkSuperModel :: IsWidget w => w -> ModelOf w -> F (Maker (ActionOf w)) (SuperModel (ModelOf w) (PlanOf w))
+ Glazier.React.Maker: hoistWithAction :: (act -> act') -> F (Maker act) a -> F (Maker act') a
+ Glazier.React.Maker: withAction :: (act -> act') -> Maker act a -> Maker act' a
+ Glazier.React.Model: Gizmo :: Scene mdl pln -> Frame mdl pln -> Gizmo mdl pln
+ Glazier.React.Model: Scene :: mdl -> pln -> Scene mdl pln
+ Glazier.React.Model: [_scene] :: Gizmo mdl pln -> Scene mdl pln
+ Glazier.React.Model: class (HasScene c mdl pln, HasFrame c mdl pln) => HasGizmo c mdl pln | c -> mdl pln
+ Glazier.React.Model: class HasScene c mdl pln | c -> mdl pln
+ Glazier.React.Model: class ToOutline c o | c -> o
+ Glazier.React.Model: data Gizmo mdl pln
+ Glazier.React.Model: data Scene mdl pln
+ Glazier.React.Model: gizmo :: HasGizmo c mdl pln => Lens' c (Gizmo mdl pln)
+ Glazier.React.Model: instance (Control.Disposable.Disposing pln, Control.Disposable.Disposing mdl) => Control.Disposable.Disposing (Glazier.React.Model.Scene mdl pln)
+ Glazier.React.Model: instance Control.Disposable.Disposing (Glazier.React.Model.Scene mdl pln) => Control.Disposable.Disposing (Glazier.React.Model.Gizmo mdl pln)
+ Glazier.React.Model: instance GHC.Generics.Generic (Glazier.React.Model.Gizmo mdl pln)
+ Glazier.React.Model: instance GHC.Generics.Generic (Glazier.React.Model.Scene mdl pln)
+ Glazier.React.Model: instance Glazier.React.Model.HasFrame (Glazier.React.Model.Gizmo mdl pln) mdl pln
+ Glazier.React.Model: instance Glazier.React.Model.HasGizmo (Glazier.React.Model.Gizmo mdl pln) mdl pln
+ Glazier.React.Model: instance Glazier.React.Model.HasModel (Glazier.React.Model.Gizmo mdl pln) mdl
+ Glazier.React.Model: instance Glazier.React.Model.HasModel (Glazier.React.Model.Scene mdl pln) mdl
+ Glazier.React.Model: instance Glazier.React.Model.HasPlan (Glazier.React.Model.Gizmo mdl pln) pln
+ Glazier.React.Model: instance Glazier.React.Model.HasPlan (Glazier.React.Model.Scene mdl pln) pln
+ Glazier.React.Model: instance Glazier.React.Model.HasScene (Glazier.React.Model.Gizmo mdl pln) mdl pln
+ Glazier.React.Model: instance Glazier.React.Model.HasScene (Glazier.React.Model.Scene mdl pln) mdl pln
+ Glazier.React.Model: instance Glazier.React.Model.ToOutline mdl o => Glazier.React.Model.ToOutline (Glazier.React.Model.Gizmo mdl pln) o
+ Glazier.React.Model: instance Glazier.React.Model.ToOutline mdl ol => Glazier.React.Model.ToOutline (Glazier.React.Model.Scene mdl pln) ol
+ Glazier.React.Model: outline :: ToOutline c o => c -> o
+ Glazier.React.Model: scene :: HasScene c mdl pln => Lens' c (Scene mdl pln)
+ Glazier.React.Widget: [Widget] :: (Disposing m, Disposing p, ToOutline m o) => (o -> F (Maker a) m) -> (Frame m p -> F (Maker a) p) -> WindowT (Scene m p) (ReactMlT Identity) () -> GadgetT a (Gizmo m p) Identity (DList c) -> Widget c a o m p
+ Glazier.React.Widget: data WithGizmo
+ Glazier.React.Widget: data WithOutline
+ Glazier.React.Widget: instance (Control.Disposable.Disposing m, Control.Disposable.Disposing p, Glazier.React.Model.ToOutline m o) => Glazier.React.Widget.IsWidget (Glazier.React.Widget.Widget c a o m p)
+ Glazier.React.Widget: mkGizmo :: IsWidget w => w -> ModelOf w -> F (Maker (ActionOf w)) (Gizmo (ModelOf w) (PlanOf w))
+ Glazier.React.Widget: mkGizmo' :: IsWidget w => w -> OutlineOf w -> F (Maker (ActionOf w)) (Gizmo (ModelOf w) (PlanOf w))
+ Glazier.React.Widget: mkModel :: IsWidget w => w -> OutlineOf w -> F (Maker (ActionOf w)) (ModelOf w)
+ Glazier.React.Widget: type FrameOf w = Frame (ModelOf w) (PlanOf w)
+ Glazier.React.Widget: type GadgetOf w = GadgetT (ActionOf w) (GizmoOf w) Identity (DList (CommandOf w))
+ Glazier.React.Widget: type GizmoOf w = Gizmo (ModelOf w) (PlanOf w)
+ Glazier.React.Widget: type SceneOf w = Scene (ModelOf w) (PlanOf w)
+ Glazier.React.Widget: type WindowOf w = WindowT (SceneOf w) (ReactMlT Identity) ()
- Glazier.React.Command.Run: componentSetState :: HasSuperModel sm mdl pln => sm -> [Property] -> JSVal -> IO ()
+ Glazier.React.Command.Run: componentSetState :: HasGizmo giz mdl pln => giz -> [Property] -> JSVal -> IO ()
- Glazier.React.Maker: [MkRenderer] :: Frame mdl pln -> (JSVal -> WindowT (Design mdl pln) ReactMl ()) -> (Callback (JSVal -> IO JSVal) -> nxt) -> Maker act nxt
+ Glazier.React.Maker: [MkRenderer] :: Frame mdl pln -> (JSVal -> WindowT (Scene mdl pln) ReactMl ()) -> (Callback (JSVal -> IO JSVal) -> nxt) -> Maker act nxt
- Glazier.React.Maker: [PutFrame] :: Frame mdl pln -> Design mdl pln -> nxt -> Maker act nxt
+ Glazier.React.Maker: [PutFrame] :: Frame mdl pln -> Scene mdl pln -> nxt -> Maker act nxt
- Glazier.React.Maker: getComponent :: forall m_aq59 act_apP1. MonadFree (Maker act_apP1) m_aq59 => m_aq59 ReactComponent
+ Glazier.React.Maker: getComponent :: forall m_aqf7 act_apYZ. MonadFree (Maker act_apYZ) m_aqf7 => m_aqf7 ReactComponent
- Glazier.React.Maker: mkEmptyFrame :: forall (mdl_XpPh :: Type) (pln_XpPj :: Type) m_aq4X act_apP1. MonadFree (Maker act_apP1) m_aq4X => m_aq4X (Frame mdl_XpPh pln_XpPj)
+ Glazier.React.Maker: mkEmptyFrame :: forall (mdl_XpZf :: Type) (pln_XpZh :: Type) m_aqeV act_apYZ. MonadFree (Maker act_apYZ) m_aqeV => m_aqeV (Frame mdl_XpZf pln_XpZh)
- Glazier.React.Maker: mkHandler :: forall m_aq4T act_apP1. MonadFree (Maker act_apP1) m_aq4T => (JSVal -> MaybeT IO [act_apP1]) -> m_aq4T (Callback (JSVal -> IO ()))
+ Glazier.React.Maker: mkHandler :: forall m_aqeR act_apYZ. MonadFree (Maker act_apYZ) m_aqeR => (JSVal -> MaybeT IO [act_apYZ]) -> m_aqeR (Callback (JSVal -> IO ()))
- Glazier.React.Maker: mkKey :: forall m_aq5c act_apP1. MonadFree (Maker act_apP1) m_aq5c => m_aq5c JSString
+ Glazier.React.Maker: mkKey :: forall m_aqfa act_apYZ. MonadFree (Maker act_apYZ) m_aqfa => m_aqfa JSString
- Glazier.React.Maker: mkRenderer :: forall (mdl_XpPl :: Type) (pln_XpPn :: Type) m_aq50 act_apP1. MonadFree (Maker act_apP1) m_aq50 => Frame mdl_XpPl pln_XpPn -> (JSVal -> WindowT (Design mdl_XpPl pln_XpPn) ReactMl ()) -> m_aq50 (Callback (JSVal -> IO JSVal))
+ Glazier.React.Maker: mkRenderer :: forall (mdl_XpZj :: Type) (pln_XpZl :: Type) m_aqeY act_apYZ. MonadFree (Maker act_apYZ) m_aqeY => Frame mdl_XpZj pln_XpZl -> (JSVal -> WindowT (Scene mdl_XpZj pln_XpZl) ReactMl ()) -> m_aqeY (Callback (JSVal -> IO JSVal))
- Glazier.React.Maker: putFrame :: forall (mdl_XpPp :: Type) (pln_XpPr :: Type) m_aq55 act_apP1. MonadFree (Maker act_apP1) m_aq55 => Frame mdl_XpPp pln_XpPr -> Design mdl_XpPp pln_XpPr -> m_aq55 ()
+ Glazier.React.Maker: putFrame :: forall (mdl_XpZn :: Type) (pln_XpZp :: Type) m_aqf3 act_apYZ. MonadFree (Maker act_apYZ) m_aqf3 => Frame mdl_XpZn pln_XpZp -> Scene mdl_XpZn pln_XpZp -> m_aqf3 ()
- Glazier.React.Model: [_frame] :: SuperModel mdl pln -> Frame mdl pln
+ Glazier.React.Model: [_frame] :: Gizmo mdl pln -> Frame mdl pln
- Glazier.React.Model: [_model] :: Design mdl pln -> mdl
+ Glazier.React.Model: [_model] :: Scene mdl pln -> mdl
- Glazier.React.Model: [_plan] :: Design mdl pln -> pln
+ Glazier.React.Model: [_plan] :: Scene mdl pln -> pln
- Glazier.React.Model: type Frame mdl pln = MVar (Design mdl pln)
+ Glazier.React.Model: type Frame mdl pln = MVar (Scene mdl pln)
- Glazier.React.Widget: class (Disposing (ModelOf w), Disposing (PlanOf w)) => IsWidget w
+ Glazier.React.Widget: class (Disposing (ModelOf w), Disposing (PlanOf w), ToOutline (ModelOf w) (OutlineOf w)) => IsWidget w
- Glazier.React.Widget: data Widget c a m p
+ Glazier.React.Widget: data Widget c a o m p
- Glazier.React.Widget: gadget :: IsWidget w => w -> GadgetT (ActionOf w) (SuperModel (ModelOf w) (PlanOf w)) Identity (DList (CommandOf w))
+ Glazier.React.Widget: gadget :: IsWidget w => w -> GadgetT (ActionOf w) (Gizmo (ModelOf w) (PlanOf w)) Identity (DList (CommandOf w))
- Glazier.React.Widget: window :: IsWidget w => w -> WindowT (Design (ModelOf w) (PlanOf w)) (ReactMlT Identity) ()
+ Glazier.React.Widget: window :: IsWidget w => w -> WindowT (Scene (ModelOf w) (PlanOf w)) (ReactMlT Identity) ()
Files
- README.md +27/−31
- glazier-react.cabal +2/−2
- src/Glazier/React/Command/Run.hs +5/−5
- src/Glazier/React/Maker.hs +15/−11
- src/Glazier/React/Maker/Run.hs +2/−2
- src/Glazier/React/Model.hs +53/−33
- src/Glazier/React/Widget.hs +67/−49
README.md view
@@ -103,75 +103,71 @@ ## Modelling [`Glazier.React.Model`](https://github.com/louispan/glazier-react/blob/master/src/Glazier/React/Model.hs) contain many nuanced concepts of Model. +### Schema+The `Schema` is a template of the pure data for stateful logic (the nouns). It is parameterized by a type variable which specializes it to either an `Outline` or 'Model'.++### Outline+The `Outline` is the pure data for stateful logic (the nouns). It may contain 'Outline's of child widgets.+The `Outline` does not contain enough information for rendering the child widgets.+ ### Model-The `Model` is the pure data used for rendering and stateful logic (the nouns).-It may contain `SuperModel` (see below) of other widgets.+The `Model` is similar to `Outline`, except that it may also contain `Gizmos` of child widgets.+It may contain `Gizmo` (see below) of other widgets.+The `Model` contains enough information to render child widgets, but not this widget. ### 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`](https://facebook.github.io/react/docs/react-component.html#setstate). -### Design-`Design` is basically a tuple of `Model` and `Plan`. It is a separate data type in order to generate convenient lenses to the fields.-`Design` is all that a `Window` needs to purely generate rendering instructions.+### Scene+`Scene` is basically a tuple of `Model` and `Plan`. It is a separate data type in order to generate convenient lenses to the fields.+`Scene` is all that a `Window` needs to purely generate rendering instructions. ### Frame-`Frame` is a type synonym of `MVar Design`. It is a mutable holder of a copy of `Design`. This is so how the official state from Haskell is communicated to the React [`render`](https://facebook.github.io/react/docs/react-component.html#render) callback. The [`render`](https://facebook.github.io/react/docs/react-component.html#render) callback will read the latest copy of `Design` from the `MVar` and pass it to the widget `Window` for rendering.+`Frame` is a type synonym of `MVar Scene`. It is a mutable holder of a copy of `Scene`. This is so how the official state from Haskell is communicated to the React [`render`](https://facebook.github.io/react/docs/react-component.html#render) callback. The [`render`](https://facebook.github.io/react/docs/react-component.html#render) callback will read the latest copy of `Scene` from the `MVar` and pass it to the widget `Window` for rendering. -### SuperModel-`SuperModel` is basically a tuple of `Design` and `Frame`. It is a separate data type in order to generate convenient lenses to the fields.+### Gizmo+`Gizmo` is basically a tuple of `Scene` and `Frame`. It is a separate data type in order to generate convenient lenses to the fields. This contains everything a widget needs for rendering and state processing.-Most state processing is performed using the pure `Design`. The `Frame` is only used for the `RenderCommand`, to copy the latest `Design` into the `Frame` when re-rendering is required.+Most state processing is performed using the pure `Model`. The `Frame` is only used for the `RenderCommand`, to put the latest `Scene` into the `Frame` when re-rendering is required. ## Maker `MVars` for `Frame`s and `Callback`s for `Plan`s may only be created in IO. Using Free Monads, [`Glazier.React.Maker`](https://github.com/louispan/glazier-react/blob/master/src/Glazier/React/Maker.hs) provides a safe way to create them without allowing other arbitrary IO. -The `Maker` can also be used create the initial `SuperModel` state for the widgets.+The `Maker` can also be used create the initial `Gizmo` state for the widgets. The `Maker` DSL has an `action` type parameter which indicated the type of action that is dispatched by the widget. The `action` type can be mapped and hoisted to a larger `action` type, allow for embedding the smaller widget action in larger widget actions. -For example, the [TodoMVC application](https://github.com/louispan/glazier-react-examples/blob/32b5b077faa499e7501cb8e5417105b340de9ad3/examples/todo/haskell/app/Main.hs#L44) uses `Maker` to create the initial application `SuperModel` which involves making and hoisting the `SuperModel` of the input, list of todos widget, and footer widget.- ## Disposable GHCJS `Callback`s has resources that are not automatically collected by the garbage collector. `Callback`s need to be released manually. The [disposable](https://github.com/louispan/disposable) library provides a safe and easy way to convert the `Callback` into a storable `SomeDisposable` that can be queued up to be released after the next rendering frame. -[disposable](https://github.com/louispan/disposable) allows generic instances of `Disposing` to be easily created, which make it easy to create instances of `Disposing`-for a `Plan` of `Callback`s, and therefore the parent container`Design`, `SuperModel`, and `Model` (which may contain other widget `SuperModel`s)+[disposable](https://github.com/louispan/disposable) allows generic instances of `Disposing` to be easily created, which make it easy to create instances of `Disposing` for a `Plan` of `Callback`s, and therefore for the parent container `Scene`, `Gizmo`, and `Model` (which may contain other widget `Gizmo`s) The [`List` widget](https://github.com/louispan/glazier-react-widget/blob/54a771f492b864ff422e31949284ea4b23aa02c6/src/Glazier/React/Widgets/List.hs#L181) shows how the disposables can be queued for destruction after the next rendered frame. ## Widget A [`Glazier.React.Widget`](https://github.com/louispan/glazier-react/blob/master/src/Glazier/React/Widget.hs) is the combination of:-+The `Maker` instruction on how to create the `Model` of that widget from an `Outline`:+```+mkModel :: Outline -> F (Maker Action) Model+``` The `Maker` instruction on how to create the `Plan` of that widget: ``` mkPlan :: Frame Model Plan -> F (Maker Action) Plan ``` The rendering instructions for that widget: ```-window:: WindowT (Design Model Plan) (ReactMlT Identity) ()+window:: WindowT (Scene Model Plan) (ReactMlT Identity) () ``` The state changes from `Action` events: ```-gadget :: GadgetT Action (SuperModel Model Plan) Identity (DList Command)+gadget :: GadgetT Action (Gizmo Model Plan) Identity (DList Command) ```-This is everything you need in order to create, render and interact with a widget.+This is everything you need in order to serialize, deserialize, create, render and interact with a widget. `Glazier.React.IsWidget` is a typeclass that provides handy XXXOf type functions to get to the type of `Command`, `Action`, `Model`, `Plan` of the Widget. It also ensures that the `Model` and `Plan` is an instance of `Disposing`. -```-instance (CD.Disposing m, CD.Disposing p) =>- IsWidget (Widget c a m p) where- type CommandOf (Widget c a m p) = c- type ActionOf (Widget c a m p) = a- type ModelOf (Widget c a m p) = m- type PlanOf (Widget c a m p) = p- mkPlan (Widget f _ _) = f- window (Widget _ f _) = f- gadget (Widget _ _ f) = f-``` -This is useful for creating widgets that is composed of other Widgets. For example:-The [List widget](https://github.com/louispan/glazier-react-widget/blob/54a771f492b864ff422e31949284ea4b23aa02c6/src/Glazier/React/Widgets/List.hs#L122) uses the IsWidget typeclass in order to ensure that the `itemWidget` can be disposed.+This is useful for creating widgets that is composed of other Widgets. ## Widget best practices Please refer to [`glazier-react-widget`](https://github.com/louispan/glazier-react-widget) for documentation on the best practices for creating `Glazier.React.Widgets`
glazier-react.cabal view
@@ -1,5 +1,5 @@ name: glazier-react-version: 0.4.0.0+version: 0.5.0.0 synopsis: ReactJS binding using Glazier and Pipes.Fluid description: ReactJS binding using Glazier and Pipes.Fluid, which is more functional and composable than Elm/Flux.@@ -32,7 +32,7 @@ build-depends: base >= 4.7 && < 5 , containers >= 0.5 && < 0.6 , deepseq >= 1.4 && < 1.5- , disposable >= 0.2.0.3 && < 1+ , disposable >= 0.2.0.4 && < 1 , dlist >= 0.8 && < 0.9 , free >= 4.12 && < 5 , glazier >= 0.10 && < 1
src/Glazier/React/Command/Run.hs view
@@ -14,11 +14,11 @@ import qualified JavaScript.Extras as JE import qualified JavaScript.Object as JO -componentSetState :: R.HasSuperModel sm mdl pln => sm -> [JE.Property] -> J.JSVal -> IO ()-componentSetState sm props j = do- let dsn = sm ^. R.design- frm = sm ^. R.frame- void $ swapMVar frm dsn+componentSetState :: R.HasGizmo giz mdl pln => giz -> [JE.Property] -> J.JSVal -> IO ()+componentSetState giz props j = do+ let scn = giz ^. R.scene+ frm = giz ^. R.frame+ void $ swapMVar frm scn js_componentSetState (JE.fromProperties props) j #ifdef __GHCJS__
src/Glazier/React/Maker.hs view
@@ -8,6 +8,7 @@ module Glazier.React.Maker where import Control.Monad.Free.Class+import Control.Monad.Free.Church import Control.Monad.Free.TH import Control.Monad.Trans.Maybe import qualified GHCJS.Foreign.Callback as J@@ -30,12 +31,12 @@ -> Maker act nxt MkRenderer :: R.Frame mdl pln- -> (J.JSVal -> G.WindowT (R.Design mdl pln) R.ReactMl ())+ -> (J.JSVal -> G.WindowT (R.Scene mdl pln) R.ReactMl ()) -> (J.Callback (J.JSVal -> IO J.JSVal) -> nxt) -> Maker act nxt PutFrame :: R.Frame mdl pln- -> R.Design mdl pln+ -> R.Scene mdl pln -> nxt -> Maker act nxt GetComponent@@ -48,18 +49,21 @@ instance Functor (Maker act) where fmap f (MkHandler handler g) = MkHandler handler (f . g) fmap f (MkEmptyFrame g) = MkEmptyFrame (f . g)- fmap f (MkRenderer ms render g) = MkRenderer ms render (f . g)- fmap f (PutFrame frm dsn x) = PutFrame frm dsn (f x)+ fmap f (MkRenderer frm render g) = MkRenderer frm render (f . g)+ fmap f (PutFrame frm scn x) = PutFrame frm scn (f x) fmap f (GetComponent g) = GetComponent (f . g) fmap f (MkKey g) = MkKey (f . g) makeFree ''Maker -- | Allows changing the action type of Maker-mapAction :: (act -> act') -> Maker act a -> Maker act' a-mapAction f (MkHandler handler g) = MkHandler (\v -> fmap f <$> handler v) g-mapAction _ (MkEmptyFrame g) = MkEmptyFrame g-mapAction _ (MkRenderer ms render g) = MkRenderer ms render g-mapAction _ (PutFrame frm dsn x) = PutFrame frm dsn x-mapAction _ (GetComponent g) = GetComponent g-mapAction _ (MkKey g) = MkKey g+withAction :: (act -> act') -> Maker act a -> Maker act' a+withAction f (MkHandler handler g) = MkHandler (\v -> fmap f <$> handler v) g+withAction _ (MkEmptyFrame g) = MkEmptyFrame g+withAction _ (MkRenderer frm render g) = MkRenderer frm render g+withAction _ (PutFrame frm scn x) = PutFrame frm scn x+withAction _ (GetComponent g) = GetComponent g+withAction _ (MkKey g) = MkKey g++hoistWithAction :: (act -> act') -> F (Maker act) a -> F (Maker act') a+hoistWithAction f = hoistF (withAction f)
src/Glazier/React/Maker/Run.hs view
@@ -38,11 +38,11 @@ run _ _ _ (R.MkEmptyFrame g) = newEmptyMVar >>= g -run _ _ _ (R.MkRenderer ms render g) = J.syncCallback1' (onRender ms render') >>= g+run _ _ _ (R.MkRenderer frm render g) = J.syncCallback1' (onRender frm render') >>= g where render' v = hoist (hoist generalize) (render v) -run _ _ _ (R.PutFrame frm dsn g) = putMVar frm dsn >> g+run _ _ _ (R.PutFrame frm scn g) = putMVar frm scn >> g run _ component _ (R.GetComponent g) = g component
src/Glazier/React/Model.hs view
@@ -21,65 +21,85 @@ class HasModel c mdl | c -> mdl where model :: Lens' c mdl +-- | Convert to the pure serializable model for saving and restoring+class ToOutline c o | c -> o where+ outline :: c -> o+ -- | A record of Model and Plan-data Design mdl pln = Design+data Scene mdl pln = Scene { _model :: mdl , _plan :: pln } deriving (G.Generic) --- | All designs should be disposable to make it easier for cleanup of callbacks.-instance (CD.Disposing pln, CD.Disposing mdl) => CD.Disposing (Design mdl pln)+class HasScene c mdl pln | c -> mdl pln where+ scene :: Lens' c (Scene mdl pln) -instance HasPlan (Design mdl pln) pln where- plan f (Design mdl pln) = fmap (\pln' -> Design mdl pln') (f pln)+instance HasScene (Scene mdl pln) mdl pln where+ scene = id+ {-# INLINE scene #-}++-- | All scenes should be disposable to make it easier for cleanup of callbacks.+instance (CD.Disposing pln, CD.Disposing mdl) => CD.Disposing (Scene mdl pln)++instance HasPlan (Scene mdl pln) pln where+ plan f (Scene mdl pln) = fmap (\pln' -> Scene mdl pln') (f pln) {-# INLINE plan #-} -instance HasModel (Design mdl pln) mdl where- model f (Design mdl pln) = fmap (\mdl' -> Design mdl' pln) (f mdl)+instance HasModel (Scene mdl pln) mdl where+ model f (Scene mdl pln) = fmap (\mdl' -> Scene mdl' pln) (f mdl) {-# INLINE model #-} -class HasDesign c mdl pln | c -> mdl pln where- design :: Lens' c (Design mdl pln)--instance HasDesign (Design mdl pln) mdl pln where- design = id+-- | A Scene can be converted to Outline by using the Model+instance ToOutline mdl ol => ToOutline (Scene mdl pln) ol where+ outline = view (model . to outline)+ {-# INLINE outline #-} --- | Frame is a Mvar of Design. React rendering callback uses this MVar for rendering.-type Frame mdl pln = MVar (Design mdl pln)+-- | Frame is a Mvar of Scene. React rendering callback uses this MVar for rendering.+type Frame mdl pln = MVar (Scene mdl pln) class HasFrame c mdl pln | c -> mdl pln where frame :: Lens' c (Frame mdl pln) instance HasFrame (Frame mdl pln) mdl pln where frame = id+ {-# INLINE frame #-} --- | A record of Design and Frame.-data SuperModel mdl pln = SuperModel- { _design :: Design mdl pln+-- | A record of Scene and Frame.+data Gizmo mdl pln = Gizmo+ { _scene :: Scene mdl pln , _frame :: Frame mdl pln } deriving (G.Generic) -- | Undecidableinstances!--- But this is safe because Design is definitely smaller than SuperModel-instance CD.Disposing (Design mdl pln) => CD.Disposing (SuperModel mdl pln) where- disposing s = CD.disposing $ s ^. design+-- But this is safe because Scene is definitely smaller than Gizmo+instance CD.Disposing (Scene mdl pln) => CD.Disposing (Gizmo mdl pln) where+ disposing s = CD.disposing $ s ^. scene+ {-# INLINE disposing #-} -class (HasDesign c mdl pln, HasFrame c mdl pln) => HasSuperModel c mdl pln | c -> mdl pln where- superModel :: Lens' c (SuperModel mdl pln)+class (HasScene c mdl pln, HasFrame c mdl pln) => HasGizmo c mdl pln | c -> mdl pln where+ gizmo :: Lens' c (Gizmo mdl pln) -instance HasSuperModel (SuperModel mdl pln) mdl pln where- superModel = id+instance HasGizmo (Gizmo mdl pln) mdl pln where+ gizmo = id+ {-# INLINE gizmo #-} -instance HasFrame (SuperModel mdl pln) mdl pln where- frame f (SuperModel dsn frm) = fmap (\frm' -> SuperModel dsn frm') (f frm)+instance HasFrame (Gizmo mdl pln) mdl pln where+ frame f (Gizmo scn frm) = fmap (\frm' -> Gizmo scn frm') (f frm) {-# INLINE frame #-} -instance HasDesign (SuperModel mdl pln) mdl pln where- design f (SuperModel dsn frm) = fmap (\dsn' -> SuperModel dsn' frm) (f dsn)- {-# INLINE design #-}+instance HasScene (Gizmo mdl pln) mdl pln where+ scene f (Gizmo scn frm) = fmap (\scn' -> Gizmo scn' frm) (f scn)+ {-# INLINE scene #-} -instance HasPlan (SuperModel mdl pln) pln where- plan = design . plan+instance HasPlan (Gizmo mdl pln) pln where+ plan = scene . plan+ {-# INLINE plan #-} -instance HasModel (SuperModel mdl pln) mdl where- model = design . model+instance HasModel (Gizmo mdl pln) mdl where+ model = scene . model+ {-# INLINE model #-}++-- | A Gizmo can be converted to Outline by using the Model+instance ToOutline mdl o => ToOutline (Gizmo mdl pln) o where+ outline = view (model . to outline)+ {-# INLINE outline #-}
src/Glazier/React/Widget.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-} module Glazier.React.Widget where @@ -16,71 +16,89 @@ import qualified Glazier.React.Model as R type family CommandOf w where- CommandOf (Widget c a m p) = c+ CommandOf (Widget c a o m p) = c type family ActionOf w where- ActionOf (Widget c a m p) = a+ ActionOf (Widget c a o m p) = a +type family OutlineOf w where+ OutlineOf (Widget c a o m p) = o+ type family ModelOf w where- ModelOf (Widget c a m p) = m+ ModelOf (Widget c a o m p) = m type family PlanOf w where- PlanOf (Widget c a m p) = p+ PlanOf (Widget c a o m p) = p --- | Undecidable instances! But this is safe because 'ModelOf w' and 'PlanOf w'--- is guaranteed to be smaller than closed type family 'DesignOf w'.-type family DesignOf w where- DesignOf w = R.Design (ModelOf w) (PlanOf w)+type SceneOf w = R.Scene (ModelOf w) (PlanOf w) --- | Undecidable instances! But this is safe because 'ModelOf w' and 'PlanOf w'--- is guaranteed to be smaller than closed type family 'FrameOf w'.-type family FrameOf w where- FrameOf w = R.Frame (ModelOf w) (PlanOf w)+type FrameOf w = R.Frame (ModelOf w) (PlanOf w) --- | Undecidable instances! But this is safe because 'ModelOf w' and 'PlanOf w'--- is guaranteed to be smaller than closed type family 'SuperModelOf w'.-type family SuperModelOf w where- SuperModelOf w = R.SuperModel (ModelOf w) (PlanOf w)+type GizmoOf w = R.Gizmo (ModelOf w) (PlanOf w) +type WindowOf w = G.WindowT (SceneOf w) (R.ReactMlT Identity) ()++type GadgetOf w = G.GadgetT (ActionOf w) (GizmoOf w) Identity (D.DList (CommandOf w))++-- | tag used to choose Schema that contains Gizmos+data WithGizmo+-- | tag used to choose Schema that contains Outlines+data WithOutline++-- | You can't use type family as a type variable for a data type. The workaround is to use+-- a tag to choose between different type family functions.+-- ModelType takes a tag to choose between Gizmo or Outline.+-- This enables creating a @data@ type that can specialize to+-- using the tag.+type family SchemaType tag w where+ SchemaType WithGizmo w = GizmoOf w+ SchemaType WithOutline w = OutlineOf w+ -- | Record of functions for a widget. Contains everything you need to make the model, -- render, and run the event processing.-data Widget c a m p = Widget- (R.Frame m p -> F (R.Maker a) p)- (G.WindowT (R.Design m p) (R.ReactMlT Identity) ())- (G.GadgetT a (R.SuperModel m p) Identity (D.DList c))+-- This is a GADT to enforce the Disposing and ToOutline constraints at the time+-- of creating the Widget record.+data Widget c a o m p where+ Widget+ :: (CD.Disposing m, CD.Disposing p, R.ToOutline m o)+ => (o -> F (R.Maker a) m)+ -> (R.Frame m p -> F (R.Maker a) p)+ -> G.WindowT (R.Scene m p) (R.ReactMlT Identity) ()+ -> G.GadgetT a (R.Gizmo m p) Identity (D.DList c)+ -> Widget c a o m p -- | This typeclass is convenient as it carries the 'Disposing Model' and 'Disposing Plan' constraints -- and allows treating 'Widget c a m p' as a type 'w' class (CD.Disposing (ModelOf w)- , CD.Disposing (PlanOf w)) => IsWidget w where+ , CD.Disposing (PlanOf w)+ , R.ToOutline (ModelOf w) (OutlineOf w)) => IsWidget w where+ -- | Make a Model from an Outline+ mkModel :: w -> OutlineOf w -> F (R.Maker (ActionOf w)) (ModelOf w)+ -- | Given an empty frame, make the Plan that uses the frame for rendering mkPlan :: w -> R.Frame (ModelOf w) (PlanOf w) -> F (R.Maker (ActionOf w)) (PlanOf w)- window :: w -> G.WindowT (R.Design (ModelOf w) (PlanOf w)) (R.ReactMlT Identity) ()- gadget :: w -> G.GadgetT (ActionOf w) (R.SuperModel (ModelOf w) (PlanOf w)) Identity (D.DList (CommandOf w))--instance (CD.Disposing m, CD.Disposing p) => IsWidget (Widget c a m p) where- mkPlan (Widget f _ _) = f- window (Widget _ f _) = f- gadget (Widget _ _ f) = f---- data Widget c a m p where--- Widget :: (CD.Disposing m, CD.Disposing p) =>--- (R.Frame m p -> F (R.Maker a) p)--- -> G.WindowT (R.Design m p) (R.ReactMlT Identity) ()--- -> G.GadgetT a (R.SuperModel m p) Identity (D.DList c)--- -> Widget c a m p---- mkPlan :: Widget c a m p -> R.Frame m p -> F (R.Maker a) p--- mkPlan (Widget f _ _) = f---- window :: Widget c a m p -> G.WindowT (R.Design m p) (R.ReactMlT Identity) ()--- window (Widget _ f _) = f+ -- | Rendering function that uses the Scene of Model and Plan+ window :: w -> G.WindowT (R.Scene (ModelOf w) (PlanOf w)) (R.ReactMlT Identity) ()+ -- | Update function that processes Action to update the Frame and Scene+ gadget :: w -> G.GadgetT (ActionOf w) (R.Gizmo (ModelOf w) (PlanOf w)) Identity (D.DList (CommandOf w)) --- gadget :: Widget c a m p -> G.GadgetT a (R.SuperModel m p) Identity (D.DList c)--- gadget (Widget _ _ f) = f+instance (CD.Disposing m, CD.Disposing p, R.ToOutline m o) => IsWidget (Widget c a o m p) where+ mkModel (Widget f _ _ _) = f+ mkPlan (Widget _ f _ _) = f+ window (Widget _ _ f _) = f+ gadget (Widget _ _ _ f) = f -mkSuperModel :: IsWidget w => w -> ModelOf w -> F (R.Maker (ActionOf w)) (R.SuperModel (ModelOf w) (PlanOf w))-mkSuperModel w mdl = do+-- | Make the required Frame and Plan for a Model+mkGizmo :: IsWidget w => w -> ModelOf w -> F (R.Maker (ActionOf w)) (R.Gizmo (ModelOf w) (PlanOf w))+mkGizmo w mdl = do frm <- R.mkEmptyFrame- dsn <- R.Design mdl <$> mkPlan w frm- R.putFrame frm dsn- pure (R.SuperModel dsn frm)+ scn <- R.Scene mdl <$> mkPlan w frm+ R.putFrame frm scn+ pure (R.Gizmo scn frm)++-- | Make the required Frame and Plan from an Outline+mkGizmo' ::+ IsWidget w =>+ w+ -> OutlineOf w+ -> F (R.Maker (ActionOf w)) (R.Gizmo (ModelOf w) (PlanOf w))+mkGizmo' w i = mkModel w i >>= mkGizmo w