git.iko.soy/typeable/reflex-dom-nested-routing
on master help sign in
6 files changed+368−53
since splitsnapshots
unifiedsplit
CHANGELOG.md+13 LICENSE+30 README.md+20−4 Reflex/Dom/NestedRoute.hs → Reflex/Dom/Routing/Nested.hs+93−49 Reflex/Dom/Routing/Writer.hs+169 reflex-dom-nested-routing.cabal+43
CHANGELOG.mdnew file+13
@@ -0,0 +1,13 @@
1+# Change Log
2+
3+## 0.0.3
4+
5+ * **[Breaking change]** Reorganize modules.
6+
7+## 0.0.2
8+
9+ * **[Breaking change]** Fix `parentRouteSegments` which was giving "grandparent" route instead of parent.
10+
11+## 0.0.1
12+
13+ * Initial prototype.
LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2017 Elliot Cameron
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Elliot Cameron nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md+20−4
@@ -1,14 +1,30 @@
11# Nested Routing for Reflex-DOM
22
3+[![Haskell Programming Language](https://img.shields.io/badge/language-Haskell-blue.svg)](http://www.haskell.org)
4+[![BSD3 License](http://img.shields.io/badge/license-BSD3-brightgreen.svg)](https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29)
5+
36Example:
47
58```haskell
69app = runRouteWithPathInFragment $ do
7− switchPromptly never =<< withRoute (\route -> case fromMaybe "" route of
8− "" -> dashboard user
9− "users" -> users user
10+ switchPromptly never <=< withRoute $ \route -> case fromMaybe "" route of
11+ "" -> (["users"] <$) <$> buttonClick "Open users"
12+ "users" -> users
1013 "test" -> codeToRun >> pure never
1114 "settings" -> text "Settings" >> pure never
1215 _ -> redirectLocally []
13− )
1416```
17+
18+`RouteWriter` can make plumbing easer:
19+
20+
21+```haskell
22+app = runRouteWithPathInFragment $ fmap snd $ runRouteWriterT $ do
23+ void $ withRoute $ \route -> case fromMaybe "" route of
24+ "" -> tellRouteAs ["users"] =<< buttonClick "Open users"
25+ "users" -> users
26+ "test" -> codeToRun
27+ "settings" -> text "Settings"
28+ _ -> tellRedirectLocally []
29+```
30+
Reflex/Dom/NestedRoute.hs → Reflex/Dom/Routing/Nested.hsrenamed+93−49
@@ -7,13 +7,16 @@
77{-# LANGUAGE RankNTypes #-}
88{-# LANGUAGE RecursiveDo #-}
99{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE StandaloneDeriving #-}
1011{-# LANGUAGE TypeFamilies #-}
1112{-# LANGUAGE UndecidableInstances #-}
1213
13−module Reflex.Dom.NestedRoute where
14+module Reflex.Dom.Routing.Nested where
1415
15−import Control.Lens (Rewrapped, Wrapped(..), iso, to, (^.))
16−import Control.Monad.Exception (MonadAsyncException, MonadException)
16+import Control.Lens (Rewrapped, Wrapped (..), iso, to,
17+ (^.))
18+import Control.Monad.Exception (MonadAsyncException,
19+ MonadException)
1720import Control.Monad.Fix
1821import Control.Monad.Primitive (PrimMonad, PrimState, primitive)
1922import Control.Monad.Reader
@@ -29,7 +32,6 @@ import Data.Maybe (listToMaybe)
2932import Data.Text (Text)
3033import qualified Data.Text as T
3134import Data.Text.Encoding (decodeUtf8, encodeUtf8)
32−import Foreign.JavaScript.TH
3335import Language.Javascript.JSaddle (MonadJSM)
3436import Reflex
3537import Reflex.Dom.Builder.Class
@@ -41,51 +43,53 @@ import URI.ByteString (URIRef, fragmentL, pathL)
4143
4244
4345data RouteContext segment t = RouteContext
44− { _routeContext_allSegments :: Dynamic t [segment]
45− , _routeContext_nextSegments :: Dynamic t [segment]
46− , _routeContext_currentSegment :: Dynamic t (Maybe segment)
46+ { _routeContext_allSegments :: !(Dynamic t [segment])
47+ , _routeContext_nextSegments :: !(Dynamic t [segment])
48+ , _routeContext_currentSegment :: !(Dynamic t (Maybe segment))
49+ , _routeContext_currentDepth :: !Int
4750 }
4851
4952
50−class (Reflex t, Monad m) => HasRoute segment t m | m -> segment, m -> t where
53+class (Reflex t, Monad m) => HasRoute t segment m | m -> segment, m -> t where
5154 routeContext :: m (RouteContext segment t)
5255 withSegments :: (RouteContext segment t -> RouteContext segment t) -> m a -> m a
5356
5457
55−newtype RouteT segment t m a = RouteT { unRouteT :: ReaderT (RouteContext segment t) m a }
58+newtype RouteT t segment m a = RouteT { unRouteT :: ReaderT (RouteContext segment t) m a }
5659 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t,
5760 MonadSample t, MonadAsyncException, MonadException, MonadTrans, PostBuild t,
5861 MonadReflexCreateTrigger t, TriggerEvent t, MonadAtomicRef)
5962
60−instance (MonadWidget t m) => HasRoute segment t (RouteT segment t m) where
63+
64+instance (MonadWidget t m) => HasRoute t segment (RouteT t segment m) where
6165 routeContext = RouteT ask
6266 withSegments f (RouteT m) = RouteT $ local f m
6367
64−instance Requester t m => Requester t (RouteT segment t m) where
65− type Request (RouteT segment t m) = Request m
66− type Response (RouteT segment t m) = Response m
68+instance Requester t m => Requester t (RouteT t segment m) where
69+ type Request (RouteT t segment m) = Request m
70+ type Response (RouteT t segment m) = Response m
6771 requesting = lift . requesting
6872 requesting_ = lift . requesting_
6973
70−instance HasRoute segment t m => HasRoute segment t (RequesterT t request response m) where
74+instance HasRoute t segment m => HasRoute t segment (RequesterT t request response m) where
7175 routeContext = lift routeContext
7276 withSegments f (RequesterT a) = RequesterT $ StrictState.mapStateT (mapReaderT $ withSegments f) a
7377
74−instance Wrapped (RouteT segment t m a) where
75− type Unwrapped (RouteT segment t m a) = ReaderT (RouteContext segment t) m a
78+instance Wrapped (RouteT t segment m a) where
79+ type Unwrapped (RouteT t segment m a) = ReaderT (RouteContext segment t) m a
7680 _Wrapped' = iso coerce coerce
7781
78−instance RouteT segment t m a ~ x => Rewrapped (RouteT segment t m a) x
82+instance RouteT t segment m a ~ x => Rewrapped (RouteT t segment m a) x
7983
80−instance PerformEvent t m => PerformEvent t (RouteT segment t m) where
81− type Performable (RouteT segment t m) = Performable m
84+instance PerformEvent t m => PerformEvent t (RouteT t segment m) where
85+ type Performable (RouteT t segment m) = Performable m
8286 {-# INLINABLE performEvent_ #-}
8387 performEvent_ = lift . performEvent_
8488 {-# INLINABLE performEvent #-}
8589 performEvent = lift . performEvent
8690
87−instance MonadRef m => MonadRef (RouteT segment t m) where
88− type Ref (RouteT segment t m) = Ref m
91+instance MonadRef m => MonadRef (RouteT t segment m) where
92+ type Ref (RouteT t segment m) = Ref m
8993 {-# INLINABLE newRef #-}
9094 newRef = lift . newRef
9195 {-# INLINABLE readRef #-}
@@ -93,14 +97,14 @@ instance MonadRef m => MonadRef (RouteT segment t m) where
9397 {-# INLINABLE writeRef #-}
9498 writeRef r = lift . writeRef r
9599
96−instance (MonadAdjust t m, MonadHold t m) => MonadAdjust t (RouteT segment t m) where
100+instance (Adjustable t m, MonadHold t m) => Adjustable t (RouteT t segment m) where
97101 runWithReplace a0 a' = RouteT $ runWithReplace (unRouteT a0) (fmapCheap unRouteT a')
98102 traverseDMapWithKeyWithAdjust f dm edm = RouteT $ traverseDMapWithKeyWithAdjust (\k v -> unRouteT $ f k v) (coerce dm) (coerceEvent edm)
99103 {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
100104 traverseDMapWithKeyWithAdjustWithMove f dm edm = RouteT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRouteT $ f k v) (coerce dm) (coerceEvent edm)
101105
102−instance (DomBuilder t m, MonadHold t m, MonadFix m) => DomBuilder t (RouteT segment t m) where
103− type DomBuilderSpace (RouteT segment t m) = DomBuilderSpace m
106+instance (DomBuilder t m, MonadHold t m, MonadFix m) => DomBuilder t (RouteT t segment m) where
107+ type DomBuilderSpace (RouteT t segment m) = DomBuilderSpace m
104108 textNode = lift . textNode
105109 element elementTag cfg (RouteT child) = RouteT $ element elementTag cfg child
106110 inputElement = lift . inputElement
@@ -109,39 +113,55 @@ instance (DomBuilder t m, MonadHold t m, MonadFix m) => DomBuilder t (RouteT seg
109113 placeRawElement = lift . placeRawElement
110114 wrapRawElement e = lift . wrapRawElement e
111115
112−instance MonadReader r m => MonadReader r (RouteT segment t m) where
116+instance (Monad m, NotReady t m) => NotReady t (RouteT t segment m)
117+
118+instance MonadReader r m => MonadReader r (RouteT t segment m) where
113119 ask = lift ask
114120 local f (RouteT a) = RouteT $ mapReaderT (local f) a
115121
116−instance MonadState s m => MonadState s (RouteT segment t m) where
117− get = lift get
118− put s = lift $ put s
122+deriving instance MonadState s m => MonadState s (RouteT t segment m)
119123
120−instance EventWriter t w m => EventWriter t w (RouteT segment t m) where
124+instance HasRoute t segment m => HasRoute t segment (EventWriterT t w m) where
125+ routeContext = lift routeContext
126+ withSegments f (EventWriterT a) = EventWriterT $ StrictState.mapStateT (withSegments f) a
127+
128+instance EventWriter t w m => EventWriter t w (RouteT t segment m) where
121129 tellEvent = lift . tellEvent
122130
131+instance HasRoute t segment m => HasRoute t segment (DynamicWriterT t w m) where
132+ routeContext = lift routeContext
133+ withSegments f (DynamicWriterT a) = DynamicWriterT $ StrictState.mapStateT (withSegments f) a
134+
135+instance DynamicWriter t w m => DynamicWriter t w (RouteT t segment m) where
136+ tellDyn = lift . tellDyn
137+
138+instance HasDocument m => HasDocument (RouteT t segment m)
123139
124−instance HasDocument m => HasDocument (RouteT segment t m)
125−instance HasJSContext m => HasJSContext (RouteT segment t m) where
126− type JSContextPhantom (RouteT segment t m) = JSContextPhantom m
127− askJSContext = RouteT askJSContext
128140#ifndef ghcjs_HOST_OS
129−instance MonadJSM m => MonadJSM (RouteT segment t m)
141+instance MonadJSM m => MonadJSM (RouteT t segment m)
130142#endif
131143
144+deriving instance DomRenderHook t m => DomRenderHook t (RouteT t segment m)
145+
146+instance (Prerender t m, Monad m, Reflex t) => Prerender t (RouteT t segment m) where
147+ type Client (RouteT t segment m) = RouteT t segment (Client m)
148+ prerender (RouteT a) (RouteT b) = RouteT $ prerender a b
132149
133−instance PrimMonad m => PrimMonad (RouteT segment t m) where
134− type PrimState (RouteT segment t m) = PrimState m
150+
151+instance PrimMonad m => PrimMonad (RouteT t segment m) where
152+ type PrimState (RouteT t segment m) = PrimState m
135153 primitive = lift . primitive
136154
137155--instance HasMountStatus t m => HasMountStatus t (RouteT segment r m) where
138156-- getMountStatus = lift getMountStatus
139157
140−
158+-- | Runs a monadic action (in 'RouteT') over the current URL as "interpreted"
159+-- by the given URL-parser. The action must return an 'Event' which can trigger
160+-- the route to change.
141161runRoute :: forall segment t m. (MonadWidget t m, Eq segment)
142162 => (forall a. URIRef a -> [segment])
143163 -> (forall a. URIRef a -> [segment] -> URIRef a)
144− -> RouteT segment t m (Event t [segment])
164+ -> RouteT t segment m (Event t [segment])
145165 -> m ()
146166runRoute toSegments fromSegments (RouteT f) = do
147167 let
@@ -159,23 +179,29 @@ runRoute toSegments fromSegments (RouteT f) = do
159179 ctx = RouteContext{ _routeContext_allSegments = allSegments
160180 , _routeContext_nextSegments = allSegments
161181 , _routeContext_currentSegment = pure Nothing
182+ , _routeContext_currentDepth = 0
162183 }
163184 newSegments <- runReaderT f ctx
164185 let x = ffor uri $ \uri' -> fromSegments uri' <$> newSegments
165186 pure (switch (current x))
166187
167−
188+-- | A very simple version of 'runRoute' that parses only the URL fragment
189+-- and splits it over @/@. Each path segment is therefore nothing more than 'Text'.
168190runRouteWithPathInFragment
169191 :: forall t m. (MonadWidget t m)
170− => RouteT Text t m (Event t [Text])
192+ => RouteT t Text m (Event t [Text])
171193 -> m ()
172194runRouteWithPathInFragment = runRoute
173− (T.splitOn "/" . T.dropAround (=='/') . fragAsText)
195+ (nullTextToEmptyList . T.splitOn "/" . T.dropAround (=='/') . fragAsText)
174196 (\oldUrl -> setFrag oldUrl . T.intercalate "/")
197+ where
198+ nullTextToEmptyList [""] = []
199+ nullTextToEmptyList x = x
175200
176−
201+-- | Introduces a new "layer" in the nested routing tree. The given function takes
202+-- the current layer's route segment and builds the DOM for that segment.
177203withRoute
178− :: forall a segment t m. (DomBuilder t m, MonadFix m, PostBuild t m, MonadHold t m, HasRoute segment t m, Eq segment)
204+ :: forall a segment t m. (DomBuilder t m, MonadFix m, PostBuild t m, MonadHold t m, HasRoute t segment m, Eq segment)
179205 => (Maybe segment -> m a)
180206 -- ^ A routing function that produces a widget from a segment.
181207 -> m (Event t a)
@@ -185,12 +211,15 @@ withRoute f = do
185211 segmentsNested <- maybeDyn segmentsFlat
186212
187213 let
214+ nextDepth = 1 + _routeContext_currentDepth ctx
215+
188216 component = ffor segmentsNested $ \x -> case x of
189217 Nothing -> do
190218 let
191219 segment = Nothing
192220 newCtx = ctx{ _routeContext_currentSegment = pure segment
193221 , _routeContext_nextSegments = pure []
222+ , _routeContext_currentDepth = nextDepth
194223 }
195224
196225 a <- withSegments (const newCtx) (f segment)
@@ -201,6 +230,7 @@ withRoute f = do
201230 segmentDyn <- fmap Just <$> holdUniqDyn (fst <$> segmentUncons)
202231 let newCtx = ctx{ _routeContext_currentSegment = segmentDyn
203232 , _routeContext_nextSegments = snd <$> segmentUncons
233+ , _routeContext_currentDepth = nextDepth
204234 }
205235
206236 dyn $ ffor segmentDyn $ \segment ->
@@ -208,19 +238,34 @@ withRoute f = do
208238
209239 switchPromptly never =<< dyn component
210240
211−
212−allRouteSegments :: (Reflex t, MonadHold t m, MonadFix m, HasRoute segment t m, Eq segment) => m (Dynamic t [segment])
241+-- | All routing segments in the current URL.
242+allRouteSegments :: (MonadHold t m, MonadFix m, HasRoute t segment m, Eq segment) => m (Dynamic t [segment])
213243allRouteSegments = _routeContext_allSegments <$> routeContext
214244
215−currentRouteSegment :: (Functor m, HasRoute segment t m) => m (Dynamic t (Maybe segment))
245+-- | The routing segment at "this layer" in the tree. "This layer" is defined by how many
246+-- nested 'withRoute's exist above the caller of this function.
247+currentRouteSegment :: (Functor m, HasRoute t segment m) => m (Dynamic t (Maybe segment))
216248currentRouteSegment = _routeContext_currentSegment <$> routeContext
217249
218−nextRouteSegment :: (Reflex t, MonadHold t m, MonadFix m, HasRoute segment t m, Eq segment) => m (Dynamic t (Maybe segment))
250+-- | The next layer's segment in the tree. Like 'withRoute' this can be used to switch
251+-- over route segments, but it does not place a new layer in the tree.
252+nextRouteSegment :: (MonadHold t m, MonadFix m, HasRoute t segment m, Eq segment) => m (Dynamic t (Maybe segment))
219253nextRouteSegment = do
220254 ctx <- routeContext
221255 holdUniqDyn (listToMaybe <$> _routeContext_nextSegments ctx)
222256
223−redirectLocally :: (MonadWidget t m, HasRoute segment t m) => [segment] -> m (Event t [segment])
257+-- | Route segments from parent layers.
258+parentRouteSegments :: (MonadHold t m, MonadFix m, HasRoute t segment m, Eq segment) => m (Dynamic t [segment])
259+parentRouteSegments = do
260+ ctx <- routeContext
261+ holdUniqDyn $ take (_routeContext_currentDepth ctx) <$> _routeContext_allSegments ctx
262+
263+-- | Route segments from child layers.
264+childRouteSegments :: (MonadHold t m, MonadFix m, HasRoute t segment m, Eq segment) => m (Dynamic t [segment])
265+childRouteSegments = holdUniqDyn =<< _routeContext_nextSegments <$> routeContext
266+
267+-- | A simple helper that produces a routing event on 'getPostBuild'.
268+redirectLocally :: (PostBuild t m) => [segment] -> m (Event t [segment])
224269redirectLocally segments = (segments <$) <$> getPostBuild
225270
226271
@@ -232,4 +277,3 @@ fragAsText uri = maybe "" decodeUtf8 (uri ^. fragmentL)
232277
233278pathSegments :: URIRef a -> [ByteString]
234279pathSegments uri = uri ^. pathL . to (B8.split '/')
235−
Reflex/Dom/Routing/Writer.hsnew file+169
@@ -0,0 +1,169 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FunctionalDependencies #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE StandaloneDeriving #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+
12+module Reflex.Dom.Routing.Writer where
13+
14+import Control.Lens (Rewrapped, Wrapped (..), iso)
15+import Control.Monad.Exception (MonadAsyncException,
16+ MonadException)
17+import Control.Monad.Fix
18+import Control.Monad.Primitive (PrimMonad, PrimState, primitive)
19+import Control.Monad.Reader
20+import Control.Monad.Ref
21+import qualified Control.Monad.State.Strict as StrictState
22+import Data.Coerce (coerce)
23+import Language.Javascript.JSaddle (MonadJSM)
24+import Reflex
25+import Reflex.Dom.Builder.Class
26+import Reflex.Dom.Builder.Immediate
27+import Reflex.Dom.Core
28+import Reflex.Host.Class
29+
30+import Reflex.Dom.Routing.Nested
31+
32+
33+class (Reflex t, Monad m) => RouteWriter t segment m | m -> segment, m -> t where
34+ tellRoute :: Event t [segment] -> m ()
35+
36+newtype RouteWriterT t segment m a = RouteWriterT { unRouteWriterT :: EventWriterT t [segment] m a }
37+ deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t,
38+ MonadSample t, MonadAsyncException, MonadException, MonadTrans, PostBuild t,
39+ MonadReflexCreateTrigger t, TriggerEvent t, MonadAtomicRef)
40+
41+
42+instance (MonadWidget t m) => RouteWriter t segment (RouteWriterT t segment m) where
43+ tellRoute = RouteWriterT . tellEvent
44+
45+deriving instance DomRenderHook t m => DomRenderHook t (RouteWriterT t segment m)
46+
47+instance (Prerender t m, Monad m, Reflex t) => Prerender t (RouteWriterT t segment m) where
48+ type Client (RouteWriterT t segment m) = RouteWriterT t segment (Client m)
49+ prerender (RouteWriterT a) (RouteWriterT b) = RouteWriterT $ prerender a b
50+
51+instance Requester t m => Requester t (RouteWriterT t segment m) where
52+ type Request (RouteWriterT t segment m) = Request m
53+ type Response (RouteWriterT t segment m) = Response m
54+ requesting = lift . requesting
55+ requesting_ = lift . requesting_
56+
57+instance RouteWriter t segment m => RouteWriter t segment (RequesterT t request response m) where
58+ tellRoute = lift . tellRoute
59+
60+instance Wrapped (RouteWriterT t segment m a) where
61+ type Unwrapped (RouteWriterT t segment m a) = EventWriterT t [segment] m a
62+ _Wrapped' = iso coerce coerce
63+
64+instance RouteWriterT t segment m a ~ x => Rewrapped (RouteWriterT t segment m a) x
65+
66+instance PerformEvent t m => PerformEvent t (RouteWriterT t segment m) where
67+ type Performable (RouteWriterT t segment m) = Performable m
68+ {-# INLINABLE performEvent_ #-}
69+ performEvent_ = lift . performEvent_
70+ {-# INLINABLE performEvent #-}
71+ performEvent = lift . performEvent
72+
73+instance MonadRef m => MonadRef (RouteWriterT t segment m) where
74+ type Ref (RouteWriterT t segment m) = Ref m
75+ {-# INLINABLE newRef #-}
76+ newRef = lift . newRef
77+ {-# INLINABLE readRef #-}
78+ readRef = lift . readRef
79+ {-# INLINABLE writeRef #-}
80+ writeRef r = lift . writeRef r
81+
82+instance (Adjustable t m, MonadHold t m) => Adjustable t (RouteWriterT t segment m) where
83+ runWithReplace a0 a' = RouteWriterT $ runWithReplace (unRouteWriterT a0) (fmapCheap unRouteWriterT a')
84+ traverseDMapWithKeyWithAdjust f dm edm = RouteWriterT $ traverseDMapWithKeyWithAdjust (\k v -> unRouteWriterT $ f k v) (coerce dm) (coerceEvent edm)
85+ {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
86+ traverseDMapWithKeyWithAdjustWithMove f dm edm = RouteWriterT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRouteWriterT $ f k v) (coerce dm) (coerceEvent edm)
87+
88+instance (DomBuilder t m, MonadHold t m, MonadFix m) => DomBuilder t (RouteWriterT t segment m) where
89+ type DomBuilderSpace (RouteWriterT t segment m) = DomBuilderSpace m
90+ textNode = lift . textNode
91+ element elementTag cfg (RouteWriterT child) = RouteWriterT $ element elementTag cfg child
92+ inputElement = lift . inputElement
93+ textAreaElement = lift . textAreaElement
94+ selectElement cfg (RouteWriterT child) = RouteWriterT $ selectElement cfg child
95+ placeRawElement = lift . placeRawElement
96+ wrapRawElement e = lift . wrapRawElement e
97+
98+instance (Monad m, NotReady t m) => NotReady t (RouteWriterT t segment m)
99+
100+deriving instance MonadReader r m => MonadReader r (RouteWriterT t segment m)
101+
102+instance RouteWriter t segment m => RouteWriter t segment (EventWriterT t w m) where
103+ tellRoute = lift . tellRoute
104+instance EventWriter t w m => EventWriter t w (RouteWriterT t segment m) where
105+ tellEvent = lift . tellEvent
106+
107+instance RouteWriter t segment m => RouteWriter t segment (DynamicWriterT t w m) where
108+ tellRoute = lift . tellRoute
109+instance DynamicWriter t w m => DynamicWriter t w (RouteWriterT t segment m) where
110+ tellDyn = lift . tellDyn
111+
112+
113+instance HasDocument m => HasDocument (RouteWriterT t segment m)
114+#ifndef ghcjs_HOST_OS
115+instance MonadJSM m => MonadJSM (RouteWriterT segment t m)
116+#endif
117+
118+
119+instance PrimMonad m => PrimMonad (RouteWriterT t segment m) where
120+ type PrimState (RouteWriterT t segment m) = PrimState m
121+ primitive = lift . primitive
122+
123+deriving instance HasRoute t segment m => HasRoute t segment (RouteWriterT t segment m)
124+
125+--instance HasMountStatus t m => HasMountStatus t (RouteWriterT r segment m) where
126+-- getMountStatus = lift getMountStatus
127+
128+-- | Runs an action that can easily set new routes at will anywhere in the action.
129+runRouteWriterT :: (Monad m, Reflex t) => RouteWriterT t segment m a -> m (a, Event t [segment])
130+runRouteWriterT (RouteWriterT m) = runEventWriterT m
131+
132+-- | Sets a new route immediately (on post-build) to the given route.
133+tellRedirectLocally :: (PostBuild t m, RouteWriter t segment m) => [segment] -> m ()
134+tellRedirectLocally segments = tellRoute =<< redirectLocally segments
135+
136+-- | Alias for 'tellRedirectLocally'
137+localRedirect :: (PostBuild t m, RouteWriter t segment m) => [segment] -> m ()
138+localRedirect = tellRedirectLocally
139+
140+
141+-- | Sets a new route to the given route whenever the given 'Event' fires, ignoring the 'Event' payload.
142+tellRouteAs :: (RouteWriter t segment m) => [segment] -> Event t a -> m ()
143+tellRouteAs segments ev = tellRoute (segments <$ ev)
144+
145+-- | Sets a new route by transforming an arbitrary 'Event' payload into a route.
146+tellRouteBy :: (RouteWriter t segment m) => (a -> [segment]) -> Event t a -> m ()
147+tellRouteBy toSegments ev = tellRoute (toSegments <$> ev)
148+
149+-- | Changes the route with an arbitrary function over the current route.
150+tellRouteModifyWith
151+ :: (RouteWriter t segment m, HasRoute t segment m, MonadHold t m, MonadFix m, Eq segment)
152+ => Event t ([segment] -> [segment]) -> m ()
153+tellRouteModifyWith ev = do
154+ segments <- allRouteSegments
155+ tellRoute $ attachWith (&) (current segments) ev
156+
157+-- | Sets a new route relative to the current layer in the routing hierarchy.
158+tellRouteRelative
159+ :: (RouteWriter t segment m, HasRoute t segment m, MonadHold t m, MonadFix m, Eq segment)
160+ => Event t [segment] -> m ()
161+tellRouteRelative ev = do
162+ parents <- parentRouteSegments
163+ tellRoute $ attachWith (++) (current parents) ev
164+
165+-- Like 'tellRouteAs' and 'tellRouteRelative'.
166+tellRouteRelativeAs
167+ :: (RouteWriter t segment m, HasRoute t segment m, MonadHold t m, MonadFix m, Eq segment)
168+ => [segment] -> Event t a -> m ()
169+tellRouteRelativeAs segments ev = tellRouteRelative (segments <$ ev)
reflex-dom-nested-routing.cabalnew file+43
@@ -0,0 +1,43 @@
1+name: reflex-dom-nested-routing
2+version: 0.0.3
3+synopsis: Nested client-side routing for Reflex-DOM
4+description: See README at <https://github.com/3noch/reflex-dom-nested-routing#readme>
5+category: Web
6+maintainer: Elliot Cameron <elliot@graftedin.io>
7+copyright: 2017 Elliot Cameron
8+license: BSD3
9+license-file: LICENSE
10+build-type: Simple
11+cabal-version: >= 1.10
12+extra-source-files:
13+ CHANGELOG.md
14+ README.md
15+
16+
17+library
18+ hs-source-dirs: .
19+
20+ ghc-options:
21+ -Wall
22+ -Wincomplete-uni-patterns
23+ -Wincomplete-record-updates
24+ -Wno-unused-do-bind
25+ -O2
26+ build-depends:
27+ base
28+ , bytestring
29+ , exception-transformers
30+ , jsaddle
31+ , lens
32+ , mtl
33+ , primitive
34+ , ref-tf
35+ , reflex
36+ , reflex-dom-core
37+ , reflex-dom-contrib
38+ , text
39+ , uri-bytestring
40+ exposed-modules:
41+ Reflex.Dom.Routing.Nested
42+ Reflex.Dom.Routing.Writer
43+ default-language: Haskell2010