on develop help sign in
Add wrapRawElement; switch to using EventSpec
commitaf60a0e5770fd3027466d31ce244e0a48e1eb079 authorRyan Trinkle <ryan.trinkle@gmail.com> dateTue 9 Aug 2016, 13:31 (10 years ago) parenta903490
7 files changed+184−153
src/Reflex/Dom/Builder/Class.hs+40−20 src/Reflex/Dom/Builder/Immediate.hs+54−34 src/Reflex/Dom/Builder/Static.hs+18−19 src/Reflex/Dom/DynamicWriter.hs+11−13 src/Reflex/Dom/Old.hs+5−5 src/Reflex/Dom/PostBuild/Class.hs+7−9 src/Reflex/Dom/Widget/Input.hs+49−53
src/Reflex/Dom/Builder/Class.hs+40−20
@@ -29,8 +29,6 @@ import Control.Lens hiding (element)
2929import Control.Monad.Reader
3030import Control.Monad.Trans.Control
3131import Data.Default
32−import Data.Dependent.Map (DMap)
33−import qualified Data.Dependent.Map as DMap
3432import Data.Functor.Misc
3533import Data.Map (Map)
3634import Data.Proxy
@@ -39,19 +37,12 @@ import Data.Text (Text)
3937import Data.Type.Coercion
4038import GHC.Exts (Constraint)
4139
42−data Pair1 (f :: k -> *) (g :: k -> *) (a :: k) = Pair1 (f a) (g a)
43−
44−data Maybe1 f a = Nothing1 | Just1 (f a)
45−
46−class DomSpace d where
47− type DomHandler d :: * -> * -> *
48− type DomHandler1 d :: (EventTag -> *) -> (EventTag -> *) -> * --TODO: Why can't this be k -> * ?
49− type RawEvent d :: EventTag -> *
40+class Default (EventSpec d EventResult) => DomSpace d where
41+ type EventSpec d :: (EventTag -> *) -> *
5042 type RawTextNode d :: *
5143 type RawElement d :: *
5244 type RawInputElement d :: *
5345 type RawTextAreaElement d :: *
54− defaultEventHandler :: proxy d -> DomHandler1 d (Pair1 EventName (RawEvent d)) (Maybe1 EventResult)
5546
5647-- | @'DomBuilder' t m@ indicates that @m@ is a 'Monad' capable of building dynamic DOM in the 'Reflex' timeline @t@
5748class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBuilder t m | m -> t where
@@ -63,6 +54,7 @@ class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBui
6354 placeholder :: PlaceholderConfig above t m -> m (Placeholder above t)
6455 inputElement :: InputElementConfig er t m -> m (InputElement er (DomBuilderSpace m) t)
6556 textAreaElement :: TextAreaElementConfig er t m -> m (TextAreaElement er (DomBuilderSpace m) t)
57+ wrapRawElement :: RawElement (DomBuilderSpace m) -> RawElementConfig er t m -> m (Element er (DomBuilderSpace m) t)
6658
6759type Namespace = Text
6860
@@ -121,14 +113,11 @@ preventDefault = mempty { _eventFlags_preventDefault = True }
121113stopPropagation :: EventFlags
122114stopPropagation = mempty { _eventFlags_propagation = Propagation_Stop }
123115
124−newtype EventFilter d er en = EventFilter (DomHandler d (RawEvent d en) (EventFlags, DomHandler d () (Maybe (er en))))
125−
126116data ElementConfig er t m
127117 = ElementConfig { _elementConfig_namespace :: Maybe Namespace
128118 , _elementConfig_initialAttributes :: Map AttributeName Text
129119 , _elementConfig_modifyAttributes :: Event t (Map AttributeName (Maybe Text))
130− , _elementConfig_eventFilters :: DMap EventName (EventFilter (DomBuilderSpace m) er)
131− , _elementConfig_eventHandler :: DomHandler1 (DomBuilderSpace m) (Pair1 EventName (RawEvent (DomBuilderSpace m))) (Maybe1 er)
120+ , _elementConfig_eventSpec :: EventSpec (DomBuilderSpace m) er
132121 }
133122
134123instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (ElementConfig er t m) where
@@ -137,8 +126,7 @@ instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (ElementConfig
137126 { _elementConfig_namespace = Nothing
138127 , _elementConfig_initialAttributes = mempty
139128 , _elementConfig_modifyAttributes = never
140− , _elementConfig_eventFilters = DMap.empty
141− , _elementConfig_eventHandler = defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))
129+ , _elementConfig_eventSpec = def
142130 }
143131
144132data Element er d t
@@ -213,11 +201,29 @@ data TextAreaElement er d t
213201 , _textAreaElement_raw :: RawTextAreaElement d
214202 }
215203
204+extractRawElementConfig :: ElementConfig er t m -> RawElementConfig er t m
205+extractRawElementConfig cfg = RawElementConfig
206+ { _rawElementConfig_modifyAttributes = _elementConfig_modifyAttributes cfg
207+ , _rawElementConfig_eventSpec = _elementConfig_eventSpec cfg
208+ }
209+
210+data RawElementConfig er t m = RawElementConfig
211+ { _rawElementConfig_modifyAttributes :: Event t (Map AttributeName (Maybe Text))
212+ , _rawElementConfig_eventSpec :: EventSpec (DomBuilderSpace m) er
213+ }
214+
215+instance (Reflex t, DomSpace (DomBuilderSpace m)) => Default (RawElementConfig EventResult t m) where
216+ def = RawElementConfig
217+ { _rawElementConfig_modifyAttributes = never
218+ , _rawElementConfig_eventSpec = def
219+ }
220+
216221makeLenses ''TextNodeConfig
217222makeLenses ''ElementConfig
218223makeLenses ''PlaceholderConfig
219224makeLenses ''InputElementConfig
220225makeLenses ''TextAreaElementConfig
226+makeLenses ''RawElementConfig
221227
222228class CanDeleteSelf t a | a -> t where
223229 deleteSelf :: Lens' a (Event t ())
@@ -239,13 +245,17 @@ instance InitialAttributes (ElementConfig er t m) where
239245 {-# INLINABLE initialAttributes #-}
240246 initialAttributes = elementConfig_initialAttributes
241247
242−class InitialAttributes a => ModifyAttributes t a | a -> t where
248+class ModifyAttributes t a | a -> t where
243249 modifyAttributes :: Lens' a (Event t (Map AttributeName (Maybe Text)))
244250
245251instance ModifyAttributes t (ElementConfig er t m) where
246252 {-# INLINABLE modifyAttributes #-}
247253 modifyAttributes = elementConfig_modifyAttributes
248254
255+instance ModifyAttributes t (RawElementConfig er t m) where
256+ {-# INLINABLE modifyAttributes #-}
257+ modifyAttributes = rawElementConfig_modifyAttributes
258+
249259class HasNamespace a where
250260 namespace :: Lens' a (Maybe Namespace)
251261
@@ -265,8 +275,7 @@ instance Functor1 (ElementConfig er t) where
265275 type Functor1Constraint (ElementConfig er t) a b = DomBuilderSpace a ~ DomBuilderSpace b
266276 {-# INLINABLE fmap1 #-}
267277 fmap1 _ cfg = cfg
268− { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
269− , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
278+ { _elementConfig_eventSpec = _elementConfig_eventSpec cfg
270279 }
271280
272281instance Reflex t => Functor1 (PlaceholderConfig above t) where
@@ -283,6 +292,13 @@ instance Functor1 (TextAreaElementConfig er t) where
283292 type Functor1Constraint (TextAreaElementConfig er t) a b = Functor1Constraint (ElementConfig er t) a b
284293 fmap1 f cfg = cfg & textAreaElementConfig_elementConfig %~ fmap1 f
285294
295+instance Functor1 (RawElementConfig er t) where
296+ type Functor1Constraint (RawElementConfig er t) a b = DomBuilderSpace a ~ DomBuilderSpace b
297+ {-# INLINABLE fmap1 #-}
298+ fmap1 _ cfg = cfg
299+ { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
300+ }
301+
286302class HasDomEvent t target eventName where
287303 type DomEventType target eventName :: *
288304 domEvent :: EventName eventName -> target -> Event t (DomEventType target eventName)
@@ -310,6 +326,7 @@ instance DomBuilder t m => DomBuilder t (ReaderT r m) where
310326 placeholder = liftPlaceholder
311327 inputElement = liftInputElement
312328 textAreaElement = liftTextAreaElement
329+ wrapRawElement = liftWrapRawElement
313330
314331type LiftDomBuilder t f m =
315332 ( Reflex t
@@ -351,3 +368,6 @@ liftInputElement cfg = liftWithStateless $ \run -> inputElement $ fmap1 run cfg
351368
352369liftTextAreaElement :: LiftDomBuilder t f m => TextAreaElementConfig er t (f m) -> f m (TextAreaElement er (DomBuilderSpace m) t)
353370liftTextAreaElement cfg = liftWithStateless $ \run -> textAreaElement $ fmap1 run cfg
371+
372+liftWrapRawElement :: LiftDomBuilder t f m => RawElement (DomBuilderSpace m) -> RawElementConfig er t (f m) -> f m (Element er (DomBuilderSpace m) t)
373+liftWrapRawElement e es = liftWithStateless $ \run -> wrapRawElement e $ fmap1 run es
src/Reflex/Dom/Builder/Immediate.hs+54−34
@@ -10,6 +10,7 @@
1010{-# LANGUAGE PolyKinds #-}
1111{-# LANGUAGE RankNTypes #-}
1212{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE TemplateHaskell #-}
1314{-# LANGUAGE TypeFamilies #-}
1415{-# LANGUAGE TypeOperators #-}
1516{-# LANGUAGE UndecidableInstances #-}
@@ -127,37 +128,23 @@ type SupportsImmediateDomBuilder t m = (Reflex t, MonadIO m, MonadHold t m, Mona
127128
128129newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IORef (Maybe (EventTrigger t (er en))))
129130
130−{-# INLINABLE makeElement #-}
131−makeElement :: forall er t m a. SupportsImmediateDomBuilder t m => Text -> ElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.HTMLElement)
132−makeElement elementTag cfg child = do
133− doc <- askDocument
134− Just e <- ImmediateDomBuilderT $ fmap DOM.castToHTMLElement <$> case cfg ^. namespace of
135− Nothing -> createElement doc (Just elementTag)
136− Just ens -> createElementNS doc (Just ens) (Just elementTag)
137− ImmediateDomBuilderT $ iforM_ (cfg ^. initialAttributes) $ \(mAttrNamespace, n) v -> case mAttrNamespace of
138− Nothing -> setAttribute e n v
139− Just ans -> setAttributeNS e (Just ans) n v
131+wrap :: forall m er t. SupportsImmediateDomBuilder t m => RawElement GhcjsDomSpace -> RawElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m (Element er GhcjsDomSpace t)
132+wrap e cfg = do
140133 events <- askEvents
141− result <- lift $ runImmediateDomBuilderT child $ ImmediateDomBuilderEnv
142− { _immediateDomBuilderEnv_parent = toNode e
143− , _immediateDomBuilderEnv_document = doc
144− , _immediateDomBuilderEnv_events = events
145− }
146− append e
147134 lift $ performEvent_ $ ffor (cfg ^. modifyAttributes) $ imapM_ $ \(mAttrNamespace, n) mv -> case mAttrNamespace of
148135 Nothing -> maybe (removeAttribute e n) (setAttribute e n) mv
149136 Just ns -> maybe (removeAttributeNS e (Just ns) n) (setAttributeNS e (Just ns) n) mv
150− eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftIO $ fmap DMap.fromList $ forM (DMap.toList $ _elementConfig_eventFilters cfg) $ \(en :=> EventFilter (GhcjsDomHandler f)) -> do
137+ eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftIO $ fmap DMap.fromList $ forM (DMap.toList $ _ghcjsEventSpec_filters $ _rawElementConfig_eventSpec cfg) $ \(en :=> GhcjsEventFilter f) -> do
151138 triggerRef <- newIORef Nothing
152139 _ <- elementOnEventName en e $ do
153140 evt <- DOM.event
154− (flags, GhcjsDomHandler k) <- liftIO $ f $ GhcjsDomEvent evt
141+ (flags, k) <- liftIO $ f $ GhcjsDomEvent evt
155142 when (_eventFlags_preventDefault flags) $ withIsEvent en DOM.preventDefault
156143 case _eventFlags_propagation flags of
157144 Propagation_Continue -> return ()
158145 Propagation_Stop -> withIsEvent en DOM.stopPropagation
159146 Propagation_StopImmediate -> withIsEvent en DOM.stopImmediatePropagation
160− mv <- liftIO $ k ()
147+ mv <- liftIO k --TODO: Only do this when the event is subscribed
161148 liftIO $ forM_ mv $ \v -> writeChan events [TriggerRef triggerRef :=> TriggerInvocation v (return ())]
162149 return $ en :=> EventFilterTriggerRef triggerRef
163150 es <- newFanEventWithTrigger $ \(WrapArg en) t -> do
@@ -168,15 +155,35 @@ makeElement elementTag cfg child = do
168155 writeIORef r Nothing
169156 Nothing -> elementOnEventName en e $ do
170157 evt <- DOM.event
171− let GhcjsDomHandler1 h = _elementConfig_eventHandler cfg
172− mv <- liftIO $ h $ Pair1 en (GhcjsDomEvent evt)
158+ let h = _ghcjsEventSpec_handler $ _rawElementConfig_eventSpec cfg
159+ mv <- liftIO $ h (en, GhcjsDomEvent evt)
173160 case mv of
174− Nothing1 -> return ()
175− Just1 v -> liftIO $ do
161+ Nothing -> return ()
162+ Just v -> liftIO $ do
176163 --TODO: I don't think this is quite right: if a new trigger is created between when this is enqueued and when it fires, this may not work quite right
177164 ref <- newIORef $ Just t
178165 writeChan events [TriggerRef ref :=> TriggerInvocation v (return ())]
179− return ((Element es e, result), e)
166+ return $ Element es e
167+
168+{-# INLINABLE makeElement #-}
169+makeElement :: forall er t m a. SupportsImmediateDomBuilder t m => Text -> ElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.HTMLElement)
170+makeElement elementTag cfg child = do
171+ doc <- askDocument
172+ events <- askEvents
173+ Just e <- ImmediateDomBuilderT $ fmap DOM.castToHTMLElement <$> case cfg ^. namespace of
174+ Nothing -> createElement doc (Just elementTag)
175+ Just ens -> createElementNS doc (Just ens) (Just elementTag)
176+ ImmediateDomBuilderT $ iforM_ (cfg ^. initialAttributes) $ \(mAttrNamespace, n) v -> case mAttrNamespace of
177+ Nothing -> setAttribute e n v
178+ Just ans -> setAttributeNS e (Just ans) n v
179+ result <- lift $ runImmediateDomBuilderT child $ ImmediateDomBuilderEnv
180+ { _immediateDomBuilderEnv_parent = toNode e
181+ , _immediateDomBuilderEnv_document = doc
182+ , _immediateDomBuilderEnv_events = events
183+ }
184+ append e
185+ wrapped <- wrap e $ extractRawElementConfig cfg
186+ return ((wrapped, result), e)
180187
181188newtype GhcjsDomHandler a b = GhcjsDomHandler { unGhcjsDomHandler :: a -> IO b }
182189
@@ -187,20 +194,30 @@ newtype GhcjsDomEvent en = GhcjsDomEvent { unGhcjsDomEvent :: EventType en }
187194data GhcjsDomSpace
188195
189196instance DomSpace GhcjsDomSpace where
190− type DomHandler GhcjsDomSpace = GhcjsDomHandler
191− type DomHandler1 GhcjsDomSpace = GhcjsDomHandler1
192− type RawEvent GhcjsDomSpace = GhcjsDomEvent
197+ type EventSpec GhcjsDomSpace = GhcjsEventSpec
193198 type RawTextNode GhcjsDomSpace = DOM.Text
194199 type RawElement GhcjsDomSpace = DOM.HTMLElement
195200 type RawInputElement GhcjsDomSpace = DOM.HTMLInputElement
196201 type RawTextAreaElement GhcjsDomSpace = DOM.HTMLTextAreaElement
197− {-# INLINABLE defaultEventHandler #-}
198− defaultEventHandler _ = GhcjsDomHandler1 $ \(Pair1 en (GhcjsDomEvent evt)) -> do
199− Just t <- withIsEvent en $ Event.getTarget evt
200− mr <- runReaderT (defaultDomEventHandler (Element.castToElement t) en) evt
201− return $ case mr of
202− Nothing -> Nothing1
203− Just r -> Just1 r
202+
203+newtype GhcjsEventFilter er en = GhcjsEventFilter (GhcjsDomEvent en -> IO (EventFlags, IO (Maybe (er en))))
204+
205+data Pair1 (f :: k -> *) (g :: k -> *) (a :: k) = Pair1 (f a) (g a)
206+
207+data Maybe1 f a = Nothing1 | Just1 (f a)
208+
209+data GhcjsEventSpec er = GhcjsEventSpec
210+ { _ghcjsEventSpec_filters :: DMap EventName (GhcjsEventFilter er)
211+ , _ghcjsEventSpec_handler :: forall en. (EventName en, GhcjsDomEvent en) -> IO (Maybe (er en))
212+ }
213+
214+instance er ~ EventResult => Default (GhcjsEventSpec er) where
215+ def = GhcjsEventSpec
216+ { _ghcjsEventSpec_filters = mempty
217+ , _ghcjsEventSpec_handler = \(en, GhcjsDomEvent evt) -> do
218+ Just t <- withIsEvent en $ Event.getTarget evt --TODO: Rework this; defaultDomEventHandler shouldn't need to take this as an argument
219+ runReaderT (defaultDomEventHandler (Element.castToElement t) en) evt
220+ }
204221
205222instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t m) where
206223 type DomBuilderSpace (ImmediateDomBuilderT t m) = GhcjsDomSpace
@@ -288,6 +305,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
288305 , _textAreaElement_element = e
289306 , _textAreaElement_raw = domTextAreaElement
290307 }
308+ wrapRawElement = wrap
291309
292310{-# INLINABLE insertImmediateAbove #-}
293311insertImmediateAbove :: (Reflex t, IsNode placeholder, PerformEvent t m, MonadIO (Performable m)) => placeholder -> Event t (ImmediateDomBuilderT t (Performable m) a) -> ImmediateDomBuilderT t m (Event t a)
@@ -766,3 +784,5 @@ wrapWindow wv _ = do
766784 { _window_events = events
767785 , _window_raw = wv
768786 }
787+
788+makeLenses ''GhcjsEventSpec
src/Reflex/Dom/Builder/Static.hs+18−19
@@ -15,28 +15,28 @@
1515{-# LANGUAGE UndecidableInstances #-}
1616module Reflex.Dom.Builder.Static where
1717
18−import Control.Monad.Identity
19−import Control.Monad.Ref
20−import Data.Dependent.Sum (DSum (..))
21−import Reflex
22−import Reflex.Dom.Builder.Class
23−import Reflex.Dom.PerformEvent.Base
24−import Reflex.Dom.PerformEvent.Class
25−import Reflex.Dom.PostBuild.Class
26−import Reflex.Dom.Widget.Basic (applyMap)
27−import Reflex.Host.Class
28−
2918import Blaze.ByteString.Builder.Html.Utf8
3019import Control.Lens hiding (element)
3120import Control.Monad.Exception
21+import Control.Monad.Identity
22+import Control.Monad.Ref
3223import Control.Monad.State.Strict
3324import Control.Monad.Trans.Control
3425import Data.ByteString (ByteString)
3526import Data.ByteString.Builder (toLazyByteString)
3627import qualified Data.ByteString.Lazy as BL
28+import Data.Default
29+import Data.Dependent.Sum (DSum (..))
3730import qualified Data.Map as Map
3831import Data.Monoid
3932import Data.Text.Encoding
33+import Reflex
34+import Reflex.Dom.Builder.Class
35+import Reflex.Dom.PerformEvent.Base
36+import Reflex.Dom.PerformEvent.Class
37+import Reflex.Dom.PostBuild.Class
38+import Reflex.Dom.Widget.Basic (applyMap)
39+import Reflex.Host.Class
4040
4141
4242newtype StaticDomBuilderT t m a = StaticDomBuilderT
@@ -109,24 +109,25 @@ data StaticDomEvent (a :: k)
109109-- | Static documents don't process events, so all handlers are equivalent
110110data StaticDomHandler (a :: k) (b :: k) = StaticDomHandler
111111
112+--TODO: Move this upstream into the data-default package
113+instance Default a => Default (Const a b) where
114+ def = Const def
115+
112116instance DomSpace StaticDomSpace where
117+ type EventSpec StaticDomSpace = Const ()
113118 type RawTextNode StaticDomSpace = ()
114119 type RawElement StaticDomSpace = ()
115120 type RawInputElement StaticDomSpace = ()
116121 type RawTextAreaElement StaticDomSpace = ()
117− type RawEvent StaticDomSpace = StaticDomEvent
118− type DomHandler StaticDomSpace = StaticDomHandler
119− type DomHandler1 StaticDomSpace = StaticDomHandler
120− defaultEventHandler _ = StaticDomHandler
121122
122123instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) where
123124 type DomBuilderSpace (StaticDomBuilderT t m) = StaticDomSpace
124125 {-# INLINABLE textNode #-}
125126 textNode (TextNodeConfig initialContents setContents) = StaticDomBuilderT $ do
127+ --TODO: Do not escape quotation marks; see https://stackoverflow.com/questions/25612166/what-characters-must-be-escaped-in-html-5
126128 let escape = BL.toStrict . toLazyByteString . fromHtmlEscapedText
127129 modify . (:) <=< hold (escape initialContents) $ fmap escape setContents
128130 return $ TextNode ()
129−
130131 {-# INLINABLE element #-}
131132 element elementTag cfg child = do
132133 let toAttr (_mns, k) v = encodeUtf8 k <> "=\"" <> BL.toStrict (toLazyByteString $ fromHtmlEscapedText v) <> "\""
@@ -140,14 +141,12 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
140141 let close = constant $ "</" <> tagBS <> ">" -- TODO handle elements without closing tags
141142 modify $ (:) $ mconcat [open, innerHtml, close]
142143 return (Element es (), result)
143−
144144 {-# INLINABLE placeholder #-}
145145 placeholder (PlaceholderConfig toInsertAbove _delete) = StaticDomBuilderT $ do
146146 result <- lift $ performEvent (fmap runStaticDomBuilderT toInsertAbove)
147147 acc <- foldDyn (:) [] (fmap snd result)
148148 modify $ (:) $ join $ mconcat . reverse <$> current acc
149149 return $ Placeholder (fmap fst result) never
150−
151150 {-# INLINABLE inputElement #-}
152151 inputElement cfg = do
153152 (e, _result) <- element "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
@@ -163,7 +162,6 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
163162 , _inputElement_element = e
164163 , _inputElement_raw = ()
165164 }
166−
167165 {-# INLINABLE textAreaElement #-}
168166 textAreaElement cfg = do
169167 --TODO: Support setValue event
@@ -177,6 +175,7 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
177175 , _textAreaElement_element = e
178176 , _textAreaElement_raw = ()
179177 }
178+ wrapRawElement _ _ = return $ Element (EventSelector $ const never) ()
180179
181180--TODO: Make this more abstract --TODO: Put the WithWebView underneath PerformEventT - I think this would perform better
182181type StaticWidget x = PostBuildT Spider (StaticDomBuilderT Spider (PerformEventT Spider (SpiderHost Global)))
src/Reflex/Dom/DynamicWriter.hs+11−13
@@ -11,13 +11,6 @@
1111{-# LANGUAGE UndecidableInstances #-}
1212module Reflex.Dom.DynamicWriter where
1313
14−import Reflex
15−import Reflex.Dom.Builder.Class
16−import Reflex.Dom.Class
17−import Reflex.Dom.PerformEvent.Class
18−import Reflex.Dom.PostBuild.Class
19−import Reflex.Host.Class
20−
2114import Control.Lens hiding (element)
2215import Control.Monad.Exception
2316import Control.Monad.IO.Class
@@ -33,6 +26,12 @@ import Data.Map (Map)
3326import qualified Data.Map as Map
3427import Data.Semigroup
3528import Data.Traversable
29+import Reflex
30+import Reflex.Dom.Builder.Class
31+import Reflex.Dom.Class
32+import Reflex.Dom.PerformEvent.Class
33+import Reflex.Dom.PostBuild.Class
34+import Reflex.Host.Class
3635
3736instance MonadTrans (DynamicWriterT t w) where
3837 lift = DynamicWriterT . lift
@@ -166,8 +165,7 @@ liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
166165{-# INLINABLE liftDynamicWriterTElementConfig #-}
167166liftDynamicWriterTElementConfig :: ElementConfig er t (DynamicWriterT t w m) -> ElementConfig er t m
168167liftDynamicWriterTElementConfig cfg = cfg
169− { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
170− , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
168+ { _elementConfig_eventSpec = _elementConfig_eventSpec cfg
171169 }
172170
173171instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
@@ -175,10 +173,7 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
175173 textNode = liftTextNode
176174 element elementTag cfg (DynamicWriterT child) = DynamicWriterT $ do
177175 s <- get
178− let cfg' = cfg
179− { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
180− , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
181− }
176+ let cfg' = liftDynamicWriterTElementConfig cfg
182177 (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
183178 put newS
184179 return (el, a)
@@ -210,6 +205,9 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
210205 }
211206 inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
212207 textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
208+ wrapRawElement e cfg = lift $ wrapRawElement e $ cfg
209+ { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
210+ }
213211
214212instance (Deletable t m, Reflex t, Monoid w, MonadHold t m, MonadFix m) => Deletable t (DynamicWriterT t w m) where
215213 deletable delete child = do
src/Reflex/Dom/Old.hs+5−5
@@ -29,7 +29,7 @@ module Reflex.Dom.Old
2929 ) where
3030
3131import Control.Arrow (first)
32−import Control.Lens ((&), (.~), (^.), makeLenses)
32+import Control.Lens (makeLenses, (&), (.~), (^.))
3333import Control.Monad
3434import Control.Monad.Exception
3535import Control.Monad.Fix
@@ -102,7 +102,7 @@ buildEmptyElementNS :: (MonadWidget t m, Attributes m attrs t) => Maybe Text ->
102102buildEmptyElementNS ns elementTag attrs = fst <$> buildElementNS ns elementTag attrs blank
103103
104104buildElementNS :: (MonadWidget t m, Attributes m attrs t) => Maybe Text -> Text -> attrs -> m a -> m (RawElement (DomBuilderSpace m), a)
105−buildElementNS ns elementTag attrs child = fmap (first _element_raw) $ buildElementInternal ns elementTag attrs child
105+buildElementNS ns elementTag attrs child = first _element_raw <$> buildElementInternal ns elementTag attrs child
106106
107107class Attributes m attrs t where
108108 buildElementInternal :: MonadWidget t m => Maybe Text -> Text -> attrs -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
@@ -114,7 +114,7 @@ instance Attributes m (Map Text Text) t where
114114
115115addStaticAttributes :: Applicative m => Map Text Text -> ElementConfig er t m -> m (ElementConfig er t m)
116116addStaticAttributes attrs cfg = do
117− let initialAttrs = Map.fromList $ fmap (first ((,) Nothing)) $ Map.toList attrs
117+ let initialAttrs = Map.fromList $ first ((,) Nothing) <$> Map.toList attrs
118118 pure $ cfg & elementConfig_initialAttributes .~ initialAttrs
119119
120120instance PostBuild t m => Attributes m (Dynamic t (Map Text Text)) t where
@@ -166,13 +166,13 @@ elWith :: (MonadWidget t m, Attributes m attrs t) => Text -> ElConfig attrs -> m
166166elWith elementTag cfg child = snd <$> elWith' elementTag cfg child
167167
168168elWith' :: (MonadWidget t m, Attributes m attrs t) => Text -> ElConfig attrs -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
169−elWith' elementTag cfg child = buildElementInternal (cfg ^. namespace) elementTag (cfg ^. attributes) child
169+elWith' elementTag cfg = buildElementInternal (cfg ^. namespace) elementTag $ cfg ^. attributes
170170
171171emptyElWith :: (MonadWidget t m, Attributes m attrs t) => Text -> ElConfig attrs -> m ()
172172emptyElWith elementTag cfg = void $ emptyElWith' elementTag cfg
173173
174174emptyElWith' :: (MonadWidget t m, Attributes m attrs t) => Text -> ElConfig attrs -> m (Element EventResult (DomBuilderSpace m) t)
175−emptyElWith' elementTag cfg = liftM fst $ elWith' elementTag cfg $ return ()
175+emptyElWith' elementTag cfg = fmap fst $ elWith' elementTag cfg $ return ()
176176
177177{-# DEPRECATED _el_clicked "Use 'domEvent Click' instead" #-}
178178_el_clicked :: Reflex t => Element EventResult d t -> Event t ()
src/Reflex/Dom/PostBuild/Class.hs+7−9
@@ -3,7 +3,6 @@
33{-# LANGUAGE FunctionalDependencies #-}
44{-# LANGUAGE GeneralizedNewtypeDeriving #-}
55{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE PolyKinds #-}
76{-# LANGUAGE RecursiveDo #-}
87{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
@@ -11,17 +10,16 @@
1110{-# LANGUAGE UndecidableInstances #-}
1211module Reflex.Dom.PostBuild.Class where
1312
14−import Foreign.JavaScript.TH
15−import Reflex
16−import Reflex.Dom.Builder.Class
17−import Reflex.Dom.PerformEvent.Class
18−import Reflex.Host.Class
19−
2013import Control.Lens hiding (element)
2114import Control.Monad.Exception
2215import Control.Monad.Reader
2316import Control.Monad.Ref
2417import Control.Monad.Trans.Control
18+import Foreign.JavaScript.TH
19+import Reflex
20+import Reflex.Dom.Builder.Class
21+import Reflex.Dom.PerformEvent.Class
22+import Reflex.Host.Class
2523
2624class (Reflex t, Monad m) => PostBuild t m | m -> t where
2725 getPostBuild :: m (Event t ())
@@ -46,8 +44,7 @@ instance Deletable t m => Deletable t (PostBuildT t m) where
4644{-# INLINABLE liftPostBuildTElementConfig #-}
4745liftPostBuildTElementConfig :: ElementConfig er t (PostBuildT t m) -> ElementConfig er t m
4846liftPostBuildTElementConfig cfg = cfg
49− { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
50− , _elementConfig_eventHandler = _elementConfig_eventHandler cfg -- This requires PolyKinds, and will fail to unify types otherwise
47+ { _elementConfig_eventSpec = _elementConfig_eventSpec cfg
5148 }
5249
5350instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBuilder t (PostBuildT t m) where
@@ -67,6 +64,7 @@ instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBui
6764 inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftPostBuildTElementConfig
6865 {-# INLINABLE textAreaElement #-}
6966 textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftPostBuildTElementConfig
67+ wrapRawElement e cfg = liftWith $ \run -> wrapRawElement e $ fmap1 run cfg
7068
7169instance MonadSample t m => MonadSample t (PostBuildT t m) where
7270 {-# INLINABLE sample #-}
src/Reflex/Dom/Widget/Input.hs+49−53
@@ -15,33 +15,10 @@ module Reflex.Dom.Widget.Input (module Reflex.Dom.Widget.Input, def, (&), (.~))
1515
1616import Prelude
1717
18−import Reflex.Dom.Builder.Class
19−import Reflex.Dom.Builder.Immediate
20−import Reflex.Dom.Class
21−import Reflex.Dom.PostBuild.Class
22−import Reflex.Dom.Widget.Basic
23−
24−-- For dropdown
18+import Control.Lens hiding (element, ix)
2519import Control.Monad.Fix
2620import Control.Monad.IO.Class
27−import Data.Maybe
28−import Data.Semigroup
29−import GHCJS.DOM.HTMLInputElement (HTMLInputElement, castToHTMLInputElement)
30−import GHCJS.DOM.HTMLSelectElement (castToHTMLSelectElement)
31−import qualified GHCJS.DOM.HTMLSelectElement as HTMLSelectElement
32−import GHCJS.DOM.HTMLTextAreaElement (HTMLTextAreaElement, castToHTMLTextAreaElement)
33−import Reflex.Dom.PerformEvent.Class
34−import qualified Text.Read as T
35−
36−-- For fileInput
37−import qualified GHCJS.DOM.Element as Element
38−import GHCJS.DOM.EventM (on)
39−import GHCJS.DOM.Types (File)
40−-- import qualified GHCJS.DOM.File as File
41−import qualified GHCJS.DOM.FileList as FileList
42−
43−import Control.Lens hiding (element, ix)
44−import Control.Monad hiding (forM_)
21+import Control.Monad.Reader
4522import qualified Data.Bimap as Bimap
4623import Data.Default
4724import Data.Dependent.Map (DMap)
@@ -49,10 +26,27 @@ import qualified Data.Dependent.Map as DMap
4926import Data.Functor.Misc
5027import Data.Map (Map)
5128import qualified Data.Map as Map
52−import Data.Proxy
29+import Data.Maybe
30+import Data.Semigroup
5331import Data.Text (Text)
5432import qualified Data.Text as T
33+import GHCJS.DOM.Element (castToElement)
34+import qualified GHCJS.DOM.Element as Element
35+import GHCJS.DOM.EventM (on)
36+import qualified GHCJS.DOM.FileList as FileList
37+import GHCJS.DOM.HTMLInputElement (HTMLInputElement, castToHTMLInputElement)
38+import GHCJS.DOM.HTMLSelectElement (castToHTMLSelectElement)
39+import qualified GHCJS.DOM.HTMLSelectElement as HTMLSelectElement
40+import GHCJS.DOM.HTMLTextAreaElement (HTMLTextAreaElement, castToHTMLTextAreaElement)
41+import GHCJS.DOM.Types (File)
5542import Reflex
43+import Reflex.Dom.Builder.Class
44+import Reflex.Dom.Builder.Immediate
45+import Reflex.Dom.Class
46+import Reflex.Dom.PerformEvent.Class
47+import Reflex.Dom.PostBuild.Class
48+import Reflex.Dom.Widget.Basic
49+import qualified Text.Read as T
5650
5751import qualified GHCJS.DOM.Event as Event
5852import qualified GHCJS.DOM.HTMLInputElement as Input
@@ -279,23 +273,24 @@ checkboxView dAttrs dValue = do
279273 let permanentAttrs = "type" =: "checkbox"
280274 modifyAttrs <- dynamicAttributesToModifyAttributes $ fmap (Map.union permanentAttrs) dAttrs
281275 postBuild <- getPostBuild
282− let filters :: DMap EventName (EventFilter (DomBuilderSpace m) CheckboxViewEventResult)
283− filters = DMap.singleton Click $ EventFilter $ GhcjsDomHandler $ \(GhcjsDomEvent evt) -> do
276+ let filters :: DMap EventName (GhcjsEventFilter CheckboxViewEventResult)
277+ filters = DMap.singleton Click $ GhcjsEventFilter $ \(GhcjsDomEvent evt) -> do
284278 Just t <- Event.getTarget evt
285279 b <- Input.getChecked $ Input.castToHTMLInputElement t
286− return $ (,) preventDefault $ GhcjsDomHandler $ \_ -> return $ Just $ CheckboxViewEventResult b
280+ return $ (,) preventDefault $ return $ Just $ CheckboxViewEventResult b
287281 elementConfig :: ElementConfig CheckboxViewEventResult t m
288282 elementConfig = (def :: ElementConfig EventResult t m)
289283 { _elementConfig_modifyAttributes = modifyAttrs
290284 , _elementConfig_initialAttributes = Map.mapKeys ((,) Nothing) permanentAttrs
291− , _elementConfig_eventFilters = filters
292− , _elementConfig_eventHandler = GhcjsDomHandler1 $ \p@(Pair1 en _) -> case en of
293− Click -> error "impossible"
294− _ -> do
295− mr <- unGhcjsDomHandler1 (defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))) p
296− case mr of
297− Nothing1 -> return Nothing1
298− Just1 (EventResult r) -> return $ Just1 $ CheckboxViewEventResult $ regularToCheckboxViewEventType en r
285+ , _elementConfig_eventSpec = GhcjsEventSpec
286+ { _ghcjsEventSpec_filters = filters
287+ , _ghcjsEventSpec_handler = \(en, GhcjsDomEvent evt) -> case en of
288+ Click -> error "impossible"
289+ _ -> do
290+ Just e <- withIsEvent en $ Event.getTarget evt
291+ mr <- runReaderT (defaultDomEventHandler (castToElement e) en) evt
292+ return $ ffor mr $ \(EventResult r) -> CheckboxViewEventResult $ regularToCheckboxViewEventType en r
293+ }
299294 }
300295 inputElementConfig :: InputElementConfig CheckboxViewEventResult t m
301296 inputElementConfig = (def :: InputElementConfig EventResult t m)
@@ -323,11 +318,11 @@ fileInput config = do
323318 let insertType = Map.insert "type" "file"
324319 dAttrs = insertType <$> _fileInputConfig_attributes config
325320 modifyAttrs <- dynamicAttributesToModifyAttributes dAttrs
326− let elCfg = (def :: ElementConfig EventResult t m)
327− { _elementConfig_modifyAttributes = modifyAttrs
328− , _elementConfig_eventFilters = DMap.singleton Change . EventFilter . GhcjsDomHandler $ \_ -> do
329− return . (,) mempty . GhcjsDomHandler $ \_ -> return . Just $ EventResult ()
330− }
321+ let filters = DMap.singleton Change . GhcjsEventFilter $ \_ -> do
322+ return . (,) mempty $ return . Just $ EventResult ()
323+ elCfg = (def :: ElementConfig EventResult t m)
324+ & modifyAttributes .~ modifyAttrs
325+ & elementConfig_eventSpec . ghcjsEventSpec_filters .~ filters
331326 cfg = (def :: InputElementConfig EventResult t m) & inputElementConfig_elementConfig .~ elCfg
332327 eRaw <- inputElement cfg
333328 let e = _inputElement_raw eRaw
@@ -426,17 +421,18 @@ dropdown k0 options (DropdownConfig setK attrs) = do
426421 modifyAttrs <- dynamicAttributesToModifyAttributes attrs
427422 let cfg = (def :: ElementConfig EventResult t m)
428423 { _elementConfig_modifyAttributes = modifyAttrs
429− , _elementConfig_eventFilters = DMap.singleton Change $ EventFilter $ GhcjsDomHandler $ \(GhcjsDomEvent evt) -> do
430− Just t <- Event.getTarget evt
431− Just v <- HTMLSelectElement.getValue $ HTMLSelectElement.castToHTMLSelectElement t
432− return $ (,) mempty $ GhcjsDomHandler $ \_ -> return $ Just $ DropdownViewEventResult v
433− , _elementConfig_eventHandler = GhcjsDomHandler1 $ \p@(Pair1 en _) -> case en of
434− Click -> error "impossible"
435− _ -> do
436− mr <- unGhcjsDomHandler1 (defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))) p
437− case mr of
438− Nothing1 -> return Nothing1
439− Just1 (EventResult r) -> return $ Just1 $ DropdownViewEventResult $ regularToDropdownViewEventType en r
424+ , _elementConfig_eventSpec = GhcjsEventSpec
425+ { _ghcjsEventSpec_filters = DMap.singleton Change $ GhcjsEventFilter $ \(GhcjsDomEvent evt) -> do
426+ Just t <- Event.getTarget evt
427+ Just v <- HTMLSelectElement.getValue $ HTMLSelectElement.castToHTMLSelectElement t
428+ return $ (,) mempty $ return $ Just $ DropdownViewEventResult v
429+ , _ghcjsEventSpec_handler = \(en, GhcjsDomEvent evt) -> case en of
430+ Click -> error "impossible"
431+ _ -> do
432+ Just e <- withIsEvent en $ Event.getTarget evt
433+ mr <- runReaderT (defaultDomEventHandler (castToElement e) en) evt
434+ return $ ffor mr $ \(EventResult r) -> DropdownViewEventResult $ regularToDropdownViewEventType en r
435+ }
440436 }
441437 (eRaw, _) <- element "select" cfg $ listWithKey indexedOptions $ \(ix, k) v -> do
442438 let optionAttrs = fmap (\dk -> "value" =: T.pack (show ix) <> if dk == k then "selected" =: "selected" else mempty) defaultKey