1{-# LANGUAGE ScopedTypeVariables, LambdaCase, ConstraintKinds, TypeFamilies, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances, RecursiveDo #-}
3module Reflex.Dom.Widget.Basic where
8import Reflex.Host.Class
9import Data.Functor.Misc
11import qualified Data.Map as Map
13import qualified Data.Set as Set
14import Data.Dependent.Sum (DSum (..))
15import Control.Monad.Trans
16import Control.Monad.Reader
17import Control.Monad.State
18import Control.Monad.Ref
20import GHCJS.DOM.UIEvent
21import GHCJS.DOM.EventM (event, Signal)
22import GHCJS.DOM.Document
23import GHCJS.DOM.Element
24import GHCJS.DOM.HTMLElement
25import GHCJS.DOM.Types hiding (Widget (..), unWidget, Event)
26import GHCJS.DOM.NamedNodeMap
34type AttributeMap = Map String String
37 = El { _el_element :: HTMLElement
38 , _el_clicked :: Event t ()
39 , _el_keypress :: Event t Int
40 , _el_scrolled :: Event t Int
43class Attributes m a where
44 addAttributes :: IsElement e => a -> e -> m ()
46instance MonadIO m => Attributes m AttributeMap where
47 addAttributes curAttrs e = liftIO $ imapM_ (elementSetAttribute e) curAttrs
49instance MonadWidget t m => Attributes m (Dynamic t AttributeMap) where
50 addAttributes attrs e = do
51 schedulePostBuild $ do
52 curAttrs <- sample $ current attrs
53 liftIO $ imapM_ (elementSetAttribute e) curAttrs
54 addVoidAction $ flip fmap (updated attrs) $ \newAttrs -> liftIO $ do
55 oldAttrs <- maybe (return Set.empty) namedNodeMapGetNames =<< elementGetAttributes e
56 forM_ (Set.toList $ oldAttrs `Set.difference` Map.keysSet newAttrs) $ elementRemoveAttribute e
57 imapM_ (elementSetAttribute e) newAttrs --TODO: avoid re-setting unchanged attributes; possibly do the compare using Align in haskell
59buildEmptyElement :: (MonadWidget t m, Attributes m attrs) => String -> attrs -> m HTMLElement
60buildEmptyElement elementTag attrs = do
63 Just e <- liftIO $ documentCreateElement doc elementTag
65 _ <- liftIO $ nodeAppendChild p $ Just e
66 return $ castToHTMLElement e
68-- We need to decide what type of attrs we've got statically, because it will often be a recursively defined value, in which case inspecting it will lead to a cycle
69buildElement :: (MonadWidget t m, Attributes m attrs) => String -> attrs -> m a -> m (HTMLElement, a)
70buildElement elementTag attrs child = do
71 e <- buildEmptyElement elementTag attrs
72 result <- subWidget (toNode e) child
75namedNodeMapGetNames :: IsNamedNodeMap self => self -> IO (Set String)
76namedNodeMapGetNames self = do
77 l <- namedNodeMapGetLength self
78 let locations = if l == 0 then [] else [0..l-1] -- Can't use 0..l-1 if l is 0 because l is unsigned and will wrap around
79 liftM Set.fromList $ forM locations $ \i -> do
80 Just n <- namedNodeMapItem self i
83text :: MonadWidget t m => String -> m ()
86--TODO: Wrap the result
87text' :: MonadWidget t m => String -> m Text
91 Just n <- liftIO $ documentCreateTextNode doc s
92 _ <- liftIO $ nodeAppendChild p $ Just n
95dynText :: MonadWidget t m => Dynamic t String -> m ()
98 schedulePostBuild $ do
99 curS <- sample $ current s
100 liftIO $ nodeSetNodeValue n curS
101 addVoidAction $ fmap (liftIO . nodeSetNodeValue n) $ updated s
103display :: (MonadWidget t m, Show a) => Dynamic t a -> m ()
104display a = dynText =<< mapDyn show a
106--TODO: Should this be renamed to 'widgetView' for consistency with 'widgetHold'?
107dyn :: MonadWidget t m => Dynamic t (m a) -> m (Event t a)
109 startPlaceholder <- text' ""
110 endPlaceholder <- text' ""
111 (newChildBuilt, newChildBuiltTriggerRef) <- newEventWithTriggerRef
112 let e = fmap snd newChildBuilt --TODO: Get rid of this hack
113 childVoidAction <- hold never e
114 performEvent_ $ fmap (const $ return ()) e --TODO: Get rid of this hack
115 addVoidAction $ switch childVoidAction
117 runWidget <- getRunWidget
119 Just df <- liftIO $ documentCreateDocumentFragment doc
120 (result, postBuild, voidActions) <- runWidget df c
121 runFrameWithTriggerRef newChildBuiltTriggerRef (result, voidActions)
123 Just p <- liftIO $ nodeGetParentNode endPlaceholder
124 liftIO $ nodeInsertBefore p (Just df) (Just endPlaceholder)
126 schedulePostBuild $ do
127 c <- sample $ current child
129 addVoidAction $ ffor (updated child) $ \newChild -> do
130 liftIO $ deleteBetweenExclusive startPlaceholder endPlaceholder
132 return $ fmap fst newChildBuilt
134widgetHold :: MonadWidget t m => m a -> Event t (m a) -> m (Dynamic t a)
135widgetHold child0 newChild = do
136 startPlaceholder <- text' ""
137 result0 <- child0 -- I'm pretty sure this is wrong; the void actions should get removed when the child is swapped out
138 endPlaceholder <- text' ""
139 (newChildBuilt, newChildBuiltTriggerRef) <- newEventWithTriggerRef
140 performEvent_ $ fmap (const $ return ()) newChildBuilt --TODO: Get rid of this hack
141 childVoidAction <- hold never $ fmap snd newChildBuilt
142 addVoidAction $ switch childVoidAction
144 runWidget <- getRunWidget
146 Just df <- liftIO $ documentCreateDocumentFragment doc
147 (result, postBuild, voidActions) <- runWidget df c
148 runFrameWithTriggerRef newChildBuiltTriggerRef (result, voidActions)
150 Just p <- liftIO $ nodeGetParentNode endPlaceholder
151 liftIO $ nodeInsertBefore p (Just df) (Just endPlaceholder)
153 addVoidAction $ ffor newChild $ \c -> do
154 liftIO $ deleteBetweenExclusive startPlaceholder endPlaceholder
156 holdDyn result0 $ fmap fst newChildBuilt
158--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
159listWithKey :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
160listWithKey vals mkChild = do
162 startPlaceholder <- text' ""
163 endPlaceholder <- text' ""
164 (newChildren, newChildrenTriggerRef) <- newEventWithTriggerRef
165 performEvent_ $ fmap (const $ return ()) newChildren --TODO: Get rid of this hack
166 children <- hold Map.empty $ traceEventWith (\x -> "newChildren: " <> show (Map.size x)) newChildren
167 addVoidAction $ switch $ fmap (mergeWith (>>) . map snd . Map.elems) children
168 runWidget <- getRunWidget
169 let buildChild df k v = runWidget df $ do
170 childStart <- text' ""
171 result <- mkChild k =<< holdDyn v (fmapMaybe (Map.lookup k) (updated vals))
173 return (result, (childStart, childEnd))
174 schedulePostBuild $ do
175 Just df <- liftIO $ documentCreateDocumentFragment doc
176 curVals <- sample $ current vals
177 initialState <- iforM curVals $ \k v -> do
178 (result, postBuild, voidAction) <- buildChild df k v
179 return ((result, voidAction), postBuild)
180 runFrameWithTriggerRef newChildrenTriggerRef $ fmap fst initialState --TODO: Do all these in a single runFrame
181 sequence $ fmap snd initialState
182 Just p <- liftIO $ nodeGetParentNode endPlaceholder
183 liftIO $ nodeInsertBefore p (Just df) (Just endPlaceholder)
185 addVoidAction $ flip fmap (updated vals) $ \newVals -> do
186 curState <- sample children
187 --TODO: Should we remove the parent from the DOM first to avoid reflows?
188 (newState, postBuild) <- flip runStateT (return ()) $ liftM (Map.mapMaybe id) $ iforM (align curState newVals) $ \k -> \case
189 This ((_, (start, end)), _) -> do
190 liftIO $ putStrLn "Deleting item"
191 liftIO $ deleteBetweenInclusive start end
194 liftIO $ putStrLn "Creating item"
195 Just df <- liftIO $ documentCreateDocumentFragment doc
196 (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
197 let s = (childResult, childVoidAction)
198 modify (>>childPostBuild)
199 let placeholder = case Map.lookupGT k curState of
200 Nothing -> endPlaceholder
201 Just (_, ((_, (start, _)), _)) -> start
202 Just p <- liftIO $ nodeGetParentNode placeholder
203 liftIO $ nodeInsertBefore p (Just df) (Just placeholder)
206 liftIO $ putStrLn "Leaving item unchanged"
208 liftIO $ putStrLn "Triggering newChildren"
209 liftIO $ putStrLn . ("newChildrenTriggerRef is Just? " <>) . show . isJust =<< readRef newChildrenTriggerRef
210 runFrameWithTriggerRef newChildrenTriggerRef newState
212 liftIO $ putStrLn "Triggering newChildren done"
213 holdDyn Map.empty $ fmap (fmap (fst . fst)) newChildren
215listWithKey' :: 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))
216listWithKey' initialVals valsChanged mkChild = do
218 startPlaceholder <- text' ""
219 endPlaceholder <- text' ""
220 (newChildren, newChildrenTriggerRef) <- newEventWithTriggerRef
221-- performEvent_ $ fmap (const $ return ()) newChildren --TODO: Get rid of this hack
222 runWidget <- getRunWidget
223 let childValChangedSelector :: EventSelector t (Const2 k v)
224 childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
225 buildChild df k v = runWidget df $ wrapChild k v
227 childStart <- text' ""
228 result <- mkChild k v $ select childValChangedSelector $ Const2 k
230 return (result, (childStart, childEnd))
231 Just df <- liftIO $ documentCreateDocumentFragment doc
232 initialState <- iforM initialVals $ \k v -> subWidgetWithVoidActions (toNode df) $ wrapChild k v --Note: we have to use subWidgetWithVoidActions rather than runWidget here, because running post-build actions during build can cause not-yet-constructed values to be read
233 children <- holdDyn initialState $ traceEventWith (\x -> "newChildren: " <> show (Map.size x)) newChildren
234 addVoidAction $ switch $ fmap (mergeWith (>>) . map snd . Map.elems) $ current children
235 Just p <- liftIO $ nodeGetParentNode endPlaceholder
236 liftIO $ nodeInsertBefore p (Just df) (Just endPlaceholder)
237 addVoidAction $ flip fmap valsChanged $ \newVals -> do
238 curState <- sample $ current children
239 --TODO: Should we remove the parent from the DOM first to avoid reflows?
240 (newState, postBuild) <- flip runStateT (return ()) $ liftM (Map.mapMaybe id) $ iforM (align curState newVals) $ \k -> \case
241 These ((_, (start, end)), _) Nothing -> do -- Deleting child
242 liftIO $ deleteBetweenInclusive start end
244 These ((_, (start, end)), _) (Just v) -> do -- Replacing existing child
245 liftIO $ deleteBetweenExclusive start end
246 Just df <- liftIO $ documentCreateDocumentFragment doc
247 (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
248 let s = (childResult, childVoidAction)
249 modify (>>childPostBuild)
250 Just p <- liftIO $ nodeGetParentNode end
251 liftIO $ nodeInsertBefore p (Just df) (Just end)
253 That Nothing -> return Nothing -- Deleting non-existent child
254 That (Just v) -> do -- Creating new child
255 Just df <- liftIO $ documentCreateDocumentFragment doc
256 (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
257 let s = (childResult, childVoidAction)
258 modify (>>childPostBuild)
259 let placeholder = case Map.lookupGT k curState of
260 Nothing -> endPlaceholder
261 Just (_, ((_, (start, _)), _)) -> start
262 Just p <- liftIO $ nodeGetParentNode placeholder
263 liftIO $ nodeInsertBefore p (Just df) (Just placeholder)
265 This state -> do -- No change
267 liftIO $ putStrLn "Triggering newChildren"
268 liftIO $ putStrLn . ("newChildrenTriggerRef is Just? " <>) . show . isJust =<< readRef newChildrenTriggerRef
269 runFrameWithTriggerRef newChildrenTriggerRef newState
271 liftIO $ putStrLn "Triggering newChildren done"
272 mapDyn (fmap (fst . fst)) children
274--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
275listViewWithKey :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
276listViewWithKey vals mkChild = liftM (switch . fmap mergeMap) $ listViewWithKey' vals mkChild
278listViewWithKey' :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
279listViewWithKey' vals mkChild = do
281 startPlaceholder <- text' ""
282 endPlaceholder <- text' ""
283 (newChildren, newChildrenTriggerRef) <- newEventWithTriggerRef
284 performEvent_ $ fmap (const $ return ()) newChildren --TODO: Get rid of this hack
285 children <- hold Map.empty $ traceEventWith (\x -> "newChildren: " <> show (Map.size x)) newChildren
286 addVoidAction $ switch $ fmap (mergeWith (>>) . map snd . Map.elems) children
287 runWidget <- getRunWidget
288 let buildChild df k v = runWidget df $ do
289 childStart <- text' ""
290 result <- mkChild k =<< holdDyn v (fmapMaybe (Map.lookup k) (updated vals))
292 return (result, (childStart, childEnd))
293 schedulePostBuild $ do
294 Just df <- liftIO $ documentCreateDocumentFragment doc
295 curVals <- sample $ current vals
296 initialState <- iforM curVals $ \k v -> do
297 (result, postBuild, voidAction) <- buildChild df k v
298 return ((result, voidAction), postBuild)
299 runFrameWithTriggerRef newChildrenTriggerRef $ fmap fst initialState --TODO: Do all these in a single runFrame
300 sequence $ fmap snd initialState
301 Just p <- liftIO $ nodeGetParentNode endPlaceholder
302 liftIO $ nodeInsertBefore p (Just df) (Just endPlaceholder)
304 addVoidAction $ flip fmap (updated vals) $ \newVals -> do
305 curState <- sample children
306 --TODO: Should we remove the parent from the DOM first to avoid reflows?
307 (newState, postBuild) <- flip runStateT (return ()) $ liftM (Map.mapMaybe id) $ iforM (align curState newVals) $ \k -> \case
308 This ((_, (start, end)), _) -> do
309 liftIO $ putStrLn "Deleting item"
310 liftIO $ deleteBetweenInclusive start end
313 liftIO $ putStrLn "Creating item"
314 Just df <- liftIO $ documentCreateDocumentFragment doc
315 (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
316 let s = (childResult, childVoidAction)
317 modify (>>childPostBuild)
318 let placeholder = case Map.lookupGT k curState of
319 Nothing -> endPlaceholder
320 Just (_, ((_, (start, _)), _)) -> start
321 Just p <- liftIO $ nodeGetParentNode placeholder
322 liftIO $ nodeInsertBefore p (Just df) (Just placeholder)
325 liftIO $ putStrLn "Leaving item unchanged"
327 liftIO $ putStrLn "Triggering newChildren"
328 liftIO $ putStrLn . ("newChildrenTriggerRef is Just? " <>) . show . isJust =<< readRef newChildrenTriggerRef
329 runFrameWithTriggerRef newChildrenTriggerRef newState
331 liftIO $ putStrLn "Triggering newChildren done"
332 return $ fmap (fmap (fst . fst)) children
334--TODO: Deduplicate the various list*WithKey* implementations
336selectViewListWithKey_ :: 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)
337selectViewListWithKey_ selection vals mkChild = do
338 let selectionDemux = demux selection -- For good performance, this value must be shared across all children
339 selectChild <- listWithKey vals $ \k v -> do
340 selected <- getDemuxed selectionDemux k
341 selectSelf <- mkChild k v selected
342 return $ fmap (const k) selectSelf
343 liftM switchPromptlyDyn $ mapDyn (leftmost . Map.elems) selectChild
345--------------------------------------------------------------------------------
346-- Basic DOM manipulation helpers
347--------------------------------------------------------------------------------
349-- | s and e must both be children of the same node and s must precede e
350deleteBetweenExclusive s e = do
351 Just currentParent <- nodeGetParentNode e -- May be different than it was at initial construction, e.g., because the parent may have dumped us in from a DocumentFragment
353 Just x <- nodeGetPreviousSibling e -- This can't be Nothing because we should hit 's' first
354 when (unNode (toNode s) /= unNode (toNode x)) $ do
355 nodeRemoveChild currentParent $ Just x
359-- | s and e must both be children of the same node and s must precede e
360deleteBetweenInclusive s e = do
361 Just currentParent <- nodeGetParentNode e -- May be different than it was at initial construction, e.g., because the parent may have dumped us in from a DocumentFragment
363 Just x <- nodeGetPreviousSibling e -- This can't be Nothing because we should hit 's' first
364 nodeRemoveChild currentParent $ Just x
365 when (unNode (toNode s) /= unNode (toNode x)) go
367 nodeRemoveChild currentParent $ Just e
369--------------------------------------------------------------------------------
371--------------------------------------------------------------------------------
373--TODO: Get rid of extra version of this function
374wrapDomEvent element elementOnevent getValue = do
375 postGui <- askPostGui
376 runWithActions <- askRunWithActions
377 e <- newEventWithTrigger $ \et -> do
378 unsubscribe <- {-# SCC "a" #-} liftIO $ {-# SCC "b" #-} elementOnevent element $ {-# SCC "c" #-} do
379 v <- {-# SCC "d" #-} getValue
380 liftIO $ postGui $ runWithActions [et :=> v]
382 {-# SCC "e" #-} unsubscribe
383 return $! {-# SCC "f" #-} e
385wrapElement :: (Functor (Event t), MonadIO m, MonadSample t m, MonadReflexCreateTrigger t m, Reflex t, HasPostGui t h m) => HTMLElement -> m (El t)
387 clicked <- wrapDomEvent e elementOnclick (return ())
388 keypress <- wrapDomEvent e elementOnkeypress $ liftIO . uiEventGetKeyCode =<< event
389 scrolled <- wrapDomEvent e elementOnscroll $ liftIO $ elementGetScrollTop e
390 return $ El e clicked keypress scrolled
392elDynAttr' :: forall t m a. MonadWidget t m => String -> Dynamic t (Map String String) -> m a -> m (El t, a)
393elDynAttr' elementTag attrs child = do
394 (e, result) <- buildElement elementTag attrs child
398{-# INLINABLE elAttr #-}
399elAttr :: forall t m a. MonadWidget t m => String -> Map String String -> m a -> m a
400elAttr elementTag attrs child = do
401 (_, result) <- buildElement elementTag attrs child
405el' :: forall t m a. MonadWidget t m => String -> m a -> m (El t, a)
406el' tag child = elAttr' tag (Map.empty :: AttributeMap) child
408{-# INLINABLE elAttr' #-}
409elAttr' :: forall t m a. MonadWidget t m => String -> Map String String -> m a -> m (El t, a)
410elAttr' elementTag attrs child = do
411 (e, result) <- buildElement elementTag attrs child
415{-# INLINABLE elDynAttr #-}
416elDynAttr :: forall t m a. MonadWidget t m => String -> Dynamic t (Map String String) -> m a -> m a
417elDynAttr elementTag attrs child = do
418 (_, result) <- buildElement elementTag attrs child
422el :: forall t m a. MonadWidget t m => String -> m a -> m a
423el tag child = elAttr tag Map.empty child
425elClass :: forall t m a. MonadWidget t m => String -> String -> m a -> m a
426elClass tag c child = elAttr tag ("class" =: c) child
428--------------------------------------------------------------------------------
429-- Copied and pasted from Reflex.Widget.Class
430--------------------------------------------------------------------------------
432list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
434elDynHtml' :: MonadWidget t m => String -> Dynamic t String -> m (El t)
435elDynHtml' elementTag html = do
436 e <- buildEmptyElement elementTag (Map.empty :: Map String String)
437 schedulePostBuild $ liftIO . htmlElementSetInnerHTML e =<< sample (current html)
438 addVoidAction $ fmap (liftIO . htmlElementSetInnerHTML e) $ updated html
443--TODO: Update dynamically
444{-# INLINABLE dynHtml #-}
445dynHtml :: MonadWidget t m => Dynamic t String -> m ()
449 Just e <- liftIO $ liftM (fmap castToHTMLElement) $ documentCreateElement doc "div"
450 liftIO $ htmlElementSetInnerHTML e h
452 eCreated <- performEvent . fmap mkSelf . tagDyn ds =<< getEInit
453 putEChildren $ fmap ((:[]) . toNode) eCreated
458 = Link { _link_clicked :: Event t ()
461linkClass :: MonadWidget t m => String -> String -> m (Link t)
463 (l,_) <- elAttr' "a" ("class" =: c) $ text s
464 return $ Link $ _el_clicked l
466link :: MonadWidget t m => String -> m (Link t)
467link s = linkClass s ""
469newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
471workflowView :: forall t m a. MonadWidget t m => Workflow t m a -> m (Event t a)
473 rec eResult <- dyn =<< mapDyn unWorkflow =<< holdDyn w0 eReplace
474 eReplace <- liftM switch $ hold never $ fmap snd eResult
475 eResult `seq` eReplace `seq` return ()
476 return $ fmap fst eResult
478divClass :: forall t m a. MonadWidget t m => String -> m a -> m a
479divClass = elClass "div"
481dtdd :: forall t m a. MonadWidget t m => String -> m a -> m a
486blank :: forall t m. MonadWidget t m => m ()
489tableDynAttr :: 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])))
490tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zoom: 1; overflow: auto; background: white;") $ do
491 elAttr "table" (Map.singleton "class" klass) $ do
492 el "thead" $ el "tr" $ do
493 mapM (\(h, _) -> el "th" $ text h) cols
495 listWithKey dRows (\k r -> do
497 elDynAttr' "tr" dAttrs $ mapM (\x -> el "td" $ snd x k r) cols)
499--TODO preselect a tab on open
500tabDisplay :: forall t m k. (MonadFix m, MonadWidget t m, Show k, Ord k) => String -> String -> Map k (String, m ()) -> m ()
501tabDisplay ulClass activeClass tabItems = do
502 rec dCurrentTab <- holdDyn Nothing (updated dTabClicks)
503 dTabClicks :: Dynamic t (Maybe k) <- elAttr "ul" (Map.singleton "class" ulClass) $ do
504 tabClicksList :: [Event t k] <- (liftM Map.elems) $ imapM (\k (s,_) -> headerBarLink s k =<< mapDyn (== (Just k)) dCurrentTab) tabItems
505 let eTabClicks :: Event t k = leftmost tabClicksList
506 holdDyn Nothing $ fmap Just eTabClicks :: m (Dynamic t (Maybe k))
508 let dTabs :: Dynamic t (Map k (String, m ())) = constDyn tabItems
509 listWithKey dTabs (\k dTab -> do
510 dAttrs <- mapDyn (\sel -> do
511 let t1 = listToMaybe $ Map.keys tabItems
512 if sel == Just k || (sel == Nothing && t1 == Just k) then Map.empty else Map.singleton "style" "display:none;") dCurrentTab
513 elDynAttr "div" dAttrs $ dyn =<< mapDyn snd dTab)
516 headerBarLink :: (MonadWidget t m, Ord k) => String -> k -> Dynamic t Bool -> m (Event t k)
517 headerBarLink x k dBool = do
518 dAttributes <- mapDyn (\b -> if b then Map.singleton "class" activeClass else Map.empty) dBool
519 elDynAttr "li" dAttributes $ do
521 return $ fmap (const k) (_link_clicked a)
523-- | Place an element into the DOM and wrap it with Reflex event handlers. Note: undefined behavior may result if the element is placed multiple times, removed from the DOM after being placed, or in other situations. Don't use this unless you understand the internals of MonadWidget.
524unsafePlaceElement :: MonadWidget t m => HTMLElement -> m (El t)
525unsafePlaceElement e = do
527 liftIO $ nodeAppendChild p $ Just e
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list