on develop help sign in
Merge pull request #17 from dbbnrl/develop

Add Quickref.md

commitc25f17f48167a181e1f8b10078ba448e68074388 authorRyan Trinkle <ryan.trinkle@gmail.com> dateFri 14 Aug 2015, 18:21 (11 years ago) parent459558add28333
4 files changed+253−5
Quickref.md+204 src/Reflex/Dom/Time.hs+1 src/Reflex/Dom/Widget/Basic.hs+43−5 src/Reflex/Dom/Xhr.hs+5
Quickref.mdnew file+204
@@ -0,0 +1,204 @@
1+# Reflex-Dom Quick(ish) Reference
2+
3+## Typeclasses
4+
5+Function signatures have been simplified. The 't' type parameter and many typeclass constraints are removed, and annotations are added regarding the monad of the result type (See the Reflex Quickstart for more discussion). Annotations:
6+
7+```haskell
8+[ ] -- Pure function
9+[W] -- Function runs in any monad supporting MonadWidget
10+[I] -- Function runs in IO
11+```
12+
13+Functions from Reflex that run in MonadSample or MonadHold context ([S] or [H]) will also run in [W].
14+
15+## Creating widgets (DOM elements hooked into the FRP system)
16+
17+### Basic widgets
18+
19+These functions generally take an element tag (such as "div", "h1", etc) and a child widget, and produce a widget of the given type containing the child. Some of these functions return data of type `El`, which is a handle to the created DOM element along with a set of potential Events that might occur on that element.
20+
21+Widgets may return any type (this is 'a' in many of the functions below). Often this will be an Event carrying user interactions with the widget.
22+
23+```haskell
24+-- Simplest form. Create a widget of given type containing the given child.
25+-- Return whatever the child returns.
26+[W] el :: String -> m a -> m a
27+
28+-- This version returns the 'El' as well.
29+[W] el' :: String -> m a -> m (El, a)
30+
31+-- These two additionally apply attributes to the element, such as ("class" =: "blah")
32+[W] elAttr :: String -> Map String String -> m a -> m a
33+[W] elAttr' :: String -> Map String String -> m a -> m (El, a)
34+
35+-- As above, but now the attribute map is Dynamic
36+[W] elDynAttr :: String -> Dynamic (Map String String) -> m a -> m a
37+[W] elDynAttr' :: String -> Dynamic (Map String String) -> m a -> m (El, a)
38+
39+-- Shortcut for elAttr when you only want to set the "class" attribute.
40+[W] elClass :: String -> String -> m a -> m a
41+
42+-- Even shorter-cut for above when element type is "div". Create a div of given class.
43+[W] divClass :: String -> m a -> m a
44+
45+-- Create a widget of given type with arbitrary, Dymamic HTML inside.
46+[W] elDynHtml' :: String -> Dynamic String -> m El
47+[W] elDynHtmlAttr' :: String -> Map String String -> Dynamic String -> m El
48+
49+-- Shortcut for elDynHtml' where the type is "div" and you don't need the 'El'.
50+[W] dynHtml :: Dynamic String -> m ()
51+
52+-- Create a static text element
53+[W] text :: String -> m ()
54+
55+-- Create a dynamic text element
56+[W] dynText :: Dynamic String -> m ()
57+[W] display :: Show a => Dynamic a -> m ()
58+
59+-- Create a "button" element with given label, return onClick Event
60+[W] button :: String -> m (Event ())
61+
62+-- Empty widget
63+[W] blank :: m ()
64+```
65+
66+### Dynamic widgets
67+
68+In the Dynamic cases so far, the *content* of a widget is dynamic but the *definition* of the widget is static. The functions below enable the definition and/or structure of the widget itself to change over time.
69+
70+Note the "list" functions do not imply particular HTML tags (ul, li, etc), though the widgets they create can have those tags if you construct them appropriately.
71+
72+```haskell
73+-- Create a dynamically-redefined widget from a Dynamic of widget actions.
74+[W] dyn :: Dynamic (m a) -> m (Event a)
75+
76+-- Same as dyn, but takes initial value and an update Event instead of a Dynamic.
77+[W] widgetHold :: m a -> Event (m a) -> m (Dynamic a)
78+
79+-- Turn a Dynamic key/value map into a set of dynamically-changing widgets.
80+[W] listWithKey :: Ord k =>
81+ Dynamic (Map k v) -> (k -> Dynamic v -> m a ) -> m (Dynamic (Map k a))
82+
83+-- Same as above where the widget constructor doesn't care about the key.
84+[W] list :: Ord k =>
85+ Dynamic (Map k v) -> ( Dynamic v -> m a ) -> m (Dynamic (Map k a))
86+
87+-- Even simpler version where there are no keys and we just use a list.
88+[W] simpleList ::
89+ Dynamic [v] -> ( Dynamic v -> m a ) -> m (Dynamic [a])
90+
91+-- Like listWithKey specialized for widgets returning (Event a).
92+[W] listViewWithKey :: Ord k =>
93+ Dynamic (Map k v) -> (k -> Dynamic v -> m (Event a)) -> m (Event (Map k a))
94+
95+-- Create a dynamically-changing set of widgets, one of which is selected at any time.
96+[W] selectViewListWithKey_ :: Ord k => Dynamic k ->
97+ Dynamic (Map k v) -> (k -> Dynamic v -> Dynamic Bool -> m (Event a)) -> m (Event k)
98+
99+-- Same as listWithKey, but takes initial values and an updates Event instead of a Dynamic.
100+[W] listWithKey' :: Ord k =>
101+ Map k v -> Event (Map k (Maybe v)) -> (k -> v -> Event v -> m a) -> m (Dynamic (Map k a))
102+```
103+
104+### Utility widgets
105+
106+These are useful widgets that are implemented (or could be implemented) in terms of the low-level widgets above.
107+
108+Some of these widget builders take a configuration record and return a record containing Events or other useful data associated with the created widget (similar to 'El'). The configuration records have default values, so you can just supply 'def'. See Reflex/Dom/Widget/Input.hs for record fields (Lenses are provided).
109+
110+```haskell
111+-- Text input.
112+[W] textInput :: TextInputConfig -> m TextInput
113+[ ] textInputGetEnter :: TextInput -> Event ()
114+[W] textArea :: TextAreaConfig -> m TextArea
115+
116+-- Checkbox. The Bool supplies the initial state.
117+[W] checkbox :: Bool -> CheckboxConfig -> m Checkbox
118+
119+-- Dropdown with Dynamic options. First argument is initial state.
120+[W] dropdown :: (Ord k, Show k, Read k) =>
121+ k -> Dynamic (Map k String) -> DropdownConfig k -> m (Dropdown k)
122+
123+-- Table with static columns and dynamic rows.
124+[W] tableDynAttr :: ... -- See Reflex.Dom.Widget.Basic
125+
126+-- Tabbed view that shows only one of its child widgets at a time.
127+[W] tabDisplay :: (Show k, Ord k) => String -> String -> Map k (String, m ()) -> m ()
128+
129+-- Widget to efficiently display long scrolling lists.
130+[W] virtualListWithSelection :: ... -- See Reflex.Dom.Widget.Lazy
131+```
132+
133+## Connecting to the real world (I/O)
134+
135+### Connecting to DOM events
136+
137+```haskell
138+-- Extract the specified Event from an 'El'. See Reflex.Dom.Widget.Basic
139+[ ] domEvent :: EventName en -> El -> Event (EventResultType en)
140+```
141+
142+### Performing arbitrary I/O in response to Events
143+
144+```haskell
145+-- Run side-effecting actions in Event when it occurs; returned Event contains
146+-- results. Side effects run in (WidgetHost m) monad, which includes [S] and [H]
147+-- and can also do I/O via liftIO
148+[W] performEvent :: Event ( WidgetHost m a) -> m (Event a)
149+
150+-- Just run side-effects; no return Event
151+[W] performEvent_ :: Event ( WidgetHost m ()) -> m ()
152+
153+-- Actions run asynchronously; actions are given a callback to send return values
154+[W] performEventAsync :: Event ((a -> IO ()) -> WidgetHost m ()) -> m (Event a)
155+```
156+
157+### XMLHttpRequest
158+
159+Convenience functions for XMLHttpRequest. see Reflex.Dom.Xhr
160+
161+```haskell
162+-- Given method, URL, and config record (with default instance), construct a request.
163+[ ] xhrRequest :: String -> String -> XhrRequestConfig -> XhrRequest
164+
165+-- Given Event of requests, issue them and produce Event of responses.
166+[W] performRequestAsync :: Event XhrRequest -> m (Event XhrResponse)
167+
168+-- Issue a collection of requests, wait for them ALL to complete, return collected results.
169+[W] performRequestsAsync :: Traversable f => Event (f XhrRequest) -> m (Event (f XhrResponse))
170+
171+-- Convenience function to decode JSON-encoded responses.
172+[ ] decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
173+
174+-- Simplified interface to "GET" URLs and return decoded results.
175+[W] getAndDecode :: FromJSON a => Event String -> m (Event (Maybe a))
176+```
177+
178+### Time
179+
180+```haskell
181+-- Create Event at given interval with given basis time.
182+[W] tickLossy :: NominalDiffTime -> UTCTime -> m (Event t TickInfo)
183+
184+-- Delay an Event's occurrences by a given amount in seconds.
185+[W] delay :: NominalDiffTime -> Event t a -> m (Event t a)
186+```
187+
188+## Startup
189+
190+```haskell
191+-- Reflex-Dom entry point. Takes a monadic widget-building action of lengthy
192+-- type and turns it into an IO action.
193+[I] mainWidget ::
194+ Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
195+[I] mainWidgetWithHead ::
196+ Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () ->
197+ Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
198+[I] mainWidgetWithCss ::
199+ ByteString ->
200+ Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
201+
202+-- One-shot Event that is triggered once all initial widgets are built
203+[W] getPostBuild :: m (Event ())
204+```
src/Reflex/Dom/Time.hs+1
@@ -45,6 +45,7 @@ tickLossyFrom dt t0 e = performEventAsync $ fmap callAtNextInterval e
4545 threadDelay $ ceiling $ (dt - alreadyElapsed) * 1000000
4646 cb $ TickInfo t n alreadyElapsed
4747
48+-- | Delay an Event's occurrences by a given amount in seconds.
4849delay :: MonadWidget t m => NominalDiffTime -> Event t a -> m (Event t a)
4950delay dt e = performEventAsync $ ffor e $ \a cb -> liftIO $ void $ forkIO $ do
5051 threadDelay $ ceiling $ dt * 1000000
src/Reflex/Dom/Widget/Basic.hs+43−5
@@ -106,6 +106,9 @@ display :: (MonadWidget t m, Show a) => Dynamic t a -> m ()
106106display a = dynText =<< mapDyn show a
107107
108108--TODO: Should this be renamed to 'widgetView' for consistency with 'widgetHold'?
109+-- | Given a Dynamic of widget-creating actions, create a widget that is recreated whenever the Dynamic updates.
110+-- The returned Event of widget results occurs when the Dynamic does.
111+-- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events that would typically be flattened.
109112dyn :: MonadWidget t m => Dynamic t (m a) -> m (Event t a)
110113dyn child = do
111114 startPlaceholder <- text' ""
@@ -133,6 +136,9 @@ dyn child = do
133136 build newChild
134137 return $ fmap fst newChildBuilt
135138
139+-- | Given an initial widget and an Event of widget-creating actions, create a widget that is recreated whenever the Event fires.
140+-- The returned Dynamic of widget results occurs when the Event does.
141+-- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
136142widgetHold :: MonadWidget t m => m a -> Event t (m a) -> m (Dynamic t a)
137143widgetHold child0 newChild = do
138144 startPlaceholder <- text' ""
@@ -162,7 +168,11 @@ widgetHold child0 newChild = do
162168 holdDyn result0 $ fmap fst newChildBuilt
163169
164170--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
165−listWithKey :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
171+-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
172+listWithKey :: (Ord k, MonadWidget t m)
173+ => Dynamic t (Map k v) -- ^ Dynamic key/value map
174+ -> (k -> Dynamic t v -> m a) -- ^ Function to create a widget for a given key from Dynamic value
175+ -> m (Dynamic t (Map k a)) -- ^ Dynamic map from keys to widget results
166176listWithKey vals mkChild = do
167177 doc <- askDocument
168178 endPlaceholder <- text' ""
@@ -211,7 +221,12 @@ listWithKey vals mkChild = do
211221 postBuild
212222 holdDyn Map.empty $ fmap (fmap (fst . fst)) newChildren
213223
214−listWithKey' :: forall t m k v a. (Ord k, MonadWidget t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
224+-- | Create a dynamically-changing set of widgets from a key/value map with update Events.
225+listWithKey' :: forall t m k v a. (Ord k, MonadWidget t m)
226+ => Map k v -- ^ Initial key/value map
227+ -> Event t (Map k (Maybe v)) -- ^ Event of map updates
228+ -> (k -> v -> Event t v -> m a) -- ^ Function to create a widget for a given key from initial value and update Event
229+ -> m (Dynamic t (Map k a)) -- ^ Dynamic map from keys to widget results
215230listWithKey' initialVals valsChanged mkChild = do
216231 doc <- askDocument
217232 endPlaceholder <- text' ""
@@ -267,6 +282,8 @@ listWithKey' initialVals valsChanged mkChild = do
267282 mapDyn (fmap (fst . fst)) children
268283
269284--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
285+-- | Create a dynamically-changing set of Event-valued widgets.
286+-- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'.
270287listViewWithKey :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
271288listViewWithKey vals mkChild = liftM (switch . fmap mergeMap) $ listViewWithKey' vals mkChild
272289
@@ -321,7 +338,12 @@ listViewWithKey' vals mkChild = do
321338
322339--TODO: Deduplicate the various list*WithKey* implementations
323340
324−selectViewListWithKey_ :: forall t m k v a. (MonadWidget t m, Ord k) => Dynamic t k -> Dynamic t (Map k v) -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -> m (Event t k)
341+-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
342+selectViewListWithKey_ :: forall t m k v a. (MonadWidget t m, Ord k)
343+ => Dynamic t k -- ^ Current selection key
344+ -> Dynamic t (Map k v) -- ^ Dynamic key/value map
345+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
346+ -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
325347selectViewListWithKey_ selection vals mkChild = do
326348 let selectionDemux = demux selection -- For good performance, this value must be shared across all children
327349 selectChild <- listWithKey vals $ \k v -> do
@@ -676,9 +698,12 @@ elClass elementTag c child = elAttr elementTag ("class" =: c) child
676698-- Copied and pasted from Reflex.Widget.Class
677699--------------------------------------------------------------------------------
678700
701+-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
702+-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
679703list :: (MonadWidget t m, Ord k) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
680704list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
681705
706+-- | Create a dynamically-changing set of widgets from a Dynamic list.
682707simpleList :: MonadWidget t m => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
683708simpleList xs mkChild = mapDyn (map snd . Map.toList) =<< flip list mkChild =<< mapDyn (Map.fromList . zip [(1::Int)..]) xs
684709
@@ -759,7 +784,13 @@ dtdd h w = do
759784blank :: forall t m. MonadWidget t m => m ()
760785blank = return ()
761786
762−tableDynAttr :: forall t m r k v. (MonadWidget t m, Show k, Ord k) => String -> [(String, k -> Dynamic t r -> m v)] -> Dynamic t (Map k r) -> (k -> m (Dynamic t (Map String String))) -> m (Dynamic t (Map k (El t, [v])))
787+-- | A widget to display a table with static columns and dynamic rows.
788+tableDynAttr :: forall t m r k v. (MonadWidget t m, Show k, Ord k)
789+ => String -- ^ Class applied to <table> element
790+ -> [(String, k -> Dynamic t r -> m v)] -- ^ Columns of (header, row key -> row value -> child widget)
791+ -> Dynamic t (Map k r) -- ^ Map from row key to row value
792+ -> (k -> m (Dynamic t (Map String String))) -- ^ Function to compute <tr> element attributes from row key
793+ -> m (Dynamic t (Map k (El t, [v]))) -- ^ Map from row key to (El, list of widget return values)
763794tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zoom: 1; overflow: auto; background: white;") $ do
764795 elAttr "table" (Map.singleton "class" klass) $ do
765796 el "thead" $ el "tr" $ do
@@ -770,7 +801,14 @@ tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zo
770801 elDynAttr' "tr" dAttrs $ mapM (\x -> el "td" $ snd x k r) cols)
771802
772803--TODO preselect a tab on open
773−tabDisplay :: forall t m k. (MonadFix m, MonadWidget t m, Show k, Ord k) => String -> String -> Map k (String, m ()) -> m ()
804+-- | A widget to construct a tabbed view that shows only one of its child widgets at a time.
805+-- Creates a header bar containing a <ul> with one <li> per child; clicking a <li> displays
806+-- the corresponding child and hides all others.
807+tabDisplay :: forall t m k. (MonadFix m, MonadWidget t m, Show k, Ord k)
808+ => String -- ^ Class applied to <ul> element
809+ -> String -- ^ Class applied to currently active <li> element
810+ -> Map k (String, m ()) -- ^ Map from (arbitrary) key to (tab label, child widget)
811+ -> m ()
774812tabDisplay ulClass activeClass tabItems = do
775813 rec dCurrentTab <- holdDyn Nothing (updated dTabClicks)
776814 dTabClicks :: Dynamic t (Maybe k) <- elAttr "ul" (Map.singleton "class" ulClass) $ do
src/Reflex/Dom/Xhr.hs+5
@@ -62,6 +62,7 @@ instance Default XhrRequestConfig where
6262 , _xhrRequestConfig_sendData = Nothing
6363 }
6464
65+-- | Construct a request object from method, URL, and config record.
6566xhrRequest :: String -> String -> XhrRequestConfig -> XhrRequest
6667xhrRequest = XhrRequest
6768
@@ -92,11 +93,13 @@ newXMLHttpRequest req cb = do
9293 _ <- xmlHttpRequestSend xhr (_xhrRequestConfig_sendData c)
9394 return xhr
9495
96+-- | Given Event of requests, issue them when the Event fires. Returns Event of corresponding responses.
9597performRequestAsync :: (MonadWidget t m) => Event t XhrRequest -> m (Event t XhrResponse)
9698performRequestAsync req = performEventAsync $ ffor req $ \r cb -> do
9799 _ <- newXMLHttpRequest r $ liftIO . cb
98100 return ()
99101
102+-- | Issues a collection of requests when the supplied Event fires. When ALL requests from a given firing complete, the results are collected and returned via the return Event.
100103performRequestsAsync :: (Traversable f, MonadWidget t m) => Event t (f XhrRequest) -> m (Event t (f XhrResponse))
101104performRequestsAsync req = performEventAsync $ ffor req $ \rs cb -> do
102105 resps <- forM rs $ \r -> do
@@ -106,6 +109,7 @@ performRequestsAsync req = performEventAsync $ ffor req $ \rs cb -> do
106109 _ <- liftIO $ forkIO $ cb =<< forM resps takeMVar
107110 return ()
108111
112+-- | Simplified interface to "GET" URLs and return decoded results.
109113getAndDecode :: (FromJSON a, MonadWidget t m) => Event t String -> m (Event t (Maybe a))
110114getAndDecode url = do
111115 r <- performRequestAsync $ fmap (\x -> XhrRequest "GET" x def) url
@@ -119,6 +123,7 @@ getMay f e = do
119123decodeText :: FromJSON a => Text -> Maybe a
120124decodeText = decode . BL.fromStrict . encodeUtf8
121125
126+-- | Convenience function to decode JSON-encoded responses.
122127decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
123128decodeXhrResponse = join . fmap decodeText . _xhrResponse_body
124129