@@ -41,6 +41,7 @@ import Data.Map (Map)
4141import qualified Data.Map as Map
4242import Data.Proxy
4343import Data.Semigroup
44+import Data.String
4445import Data.Text (Text)
4546import Data.Traversable
4647import Data.Type.Coercion
@@ -52,20 +53,97 @@ class Default (EventSpec d EventResult) => DomSpace d where
5253 type RawElement d :: *
5354 type RawInputElement d :: *
5455 type RawTextAreaElement d :: *
56+ type RawSelectElement d :: *
5557 addEventSpecFlags :: proxy d -> EventName en -> (Maybe (er en) -> EventFlags) -> EventSpec d er -> EventSpec d er
5658
57−-- | @'DomBuildek' t m@ indicates that @m@ is a 'Monad' capable of building dynamic DOM in the 'Reflex' timeline @t@
59+{-# INLINABLE liftElementConfig #-}
60+liftElementConfig :: (DomBuilderSpace (f m) ~ DomBuilderSpace m) => ElementConfig er t (f m) -> ElementConfig er t m
61+liftElementConfig cfg = cfg
62+ { _elementConfig_eventSpec = _elementConfig_eventSpec cfg
63+ }
64+
65+-- | @'DomBuilder' t m@ indicates that @m@ is a 'Monad' capable of building
66+-- dynamic DOM in the 'Reflex' timeline @t@
5867class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBuilder t m | m -> t where
5968 type DomBuilderSpace m :: *
6069 textNode :: TextNodeConfig t -> m (TextNode (DomBuilderSpace m) t)
70+ default textNode :: ( MonadTrans f
71+ , m ~ f m'
72+ , DomBuilderSpace m' ~ DomBuilderSpace m
73+ , DomBuilder t m'
74+ )
75+ => TextNodeConfig t -> m (TextNode (DomBuilderSpace m) t)
76+ textNode = lift . textNode
77+ {-# INLINABLE textNode #-}
6178 element :: Text -> ElementConfig er t m -> m a -> m (Element er (DomBuilderSpace m) t, a)
79+ default element :: ( MonadTransControl f
80+ , StT f a ~ a
81+ , m ~ f m'
82+ , DomBuilderSpace m' ~ DomBuilderSpace m
83+ , DomBuilder t m'
84+ )
85+ => Text -> ElementConfig er t m -> m a -> m (Element er (DomBuilderSpace m) t, a)
86+ element t cfg child = liftWith $ \run -> element t (liftElementConfig cfg) $ run child
87+ {-# INLINABLE element #-}
6288 -- | Create a placeholder in the DOM, with the ability to insert new DOM before it
6389 -- The provided DOM will be executed after the current frame, so it will not be affected by any occurrences that are concurrent with the occurrence that created it
6490 placeholder :: PlaceholderConfig above t m -> m (Placeholder above t)
6591 inputElement :: InputElementConfig er t m -> m (InputElement er (DomBuilderSpace m) t)
92+ default inputElement :: ( MonadTransControl f
93+ , m ~ f m'
94+ , DomBuilderSpace m' ~ DomBuilderSpace m
95+ , DomBuilder t m'
96+ )
97+ => InputElementConfig er t m -> m (InputElement er (DomBuilderSpace m) t)
98+ inputElement cfg = lift $ inputElement $ cfg
99+ { _inputElementConfig_elementConfig = liftElementConfig $ _inputElementConfig_elementConfig cfg
100+ }
101+ {-# INLINABLE inputElement #-}
66102 textAreaElement :: TextAreaElementConfig er t m -> m (TextAreaElement er (DomBuilderSpace m) t)
103+ default textAreaElement :: ( MonadTransControl f
104+ , m ~ f m'
105+ , DomBuilderSpace m' ~ DomBuilderSpace m
106+ , DomBuilder t m'
107+ )
108+ => TextAreaElementConfig er t m -> m (TextAreaElement er (DomBuilderSpace m) t)
109+ textAreaElement cfg = lift $ textAreaElement $ cfg
110+ { _textAreaElementConfig_elementConfig = liftElementConfig $ _textAreaElementConfig_elementConfig cfg
111+ }
112+ {-# INLINABLE textAreaElement #-}
113+ selectElement :: SelectElementConfig er t m -> m a -> m (SelectElement er (DomBuilderSpace m) t, a)
114+ default selectElement :: ( MonadTransControl f
115+ , StT f a ~ a
116+ , m ~ f m'
117+ , DomBuilderSpace m' ~ DomBuilderSpace m
118+ , DomBuilder t m'
119+ )
120+ => SelectElementConfig er t m -> m a -> m (SelectElement er (DomBuilderSpace m) t, a)
121+ selectElement cfg child = do
122+ let cfg' = cfg
123+ { _selectElementConfig_elementConfig = liftElementConfig $ _selectElementConfig_elementConfig cfg
124+ }
125+ liftWith $ \run -> selectElement cfg' $ run child
126+ {-# INLINABLE selectElement #-}
67127 placeRawElement :: RawElement (DomBuilderSpace m) -> m ()
128+ default placeRawElement :: ( MonadTrans f
129+ , m ~ f m'
130+ , DomBuilderSpace m' ~ DomBuilderSpace m
131+ , DomBuilder t m'
132+ )
133+ => RawElement (DomBuilderSpace m) -> m ()
134+ placeRawElement = lift . placeRawElement
135+ {-# INLINABLE placeRawElement #-}
68136 wrapRawElement :: RawElement (DomBuilderSpace m) -> RawElementConfig er t m -> m (Element er (DomBuilderSpace m) t)
137+ default wrapRawElement :: ( MonadTrans f
138+ , m ~ f m'
139+ , DomBuilderSpace m' ~ DomBuilderSpace m
140+ , DomBuilder t m'
141+ )
142+ => RawElement (DomBuilderSpace m) -> RawElementConfig er t m -> m (Element er (DomBuilderSpace m) t)
143+ wrapRawElement e cfg = lift $ wrapRawElement e $ cfg
144+ { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
145+ }
146+ {-# INLINABLE wrapRawElement #-}
69147
70148type Namespace = Text
71149
@@ -85,7 +163,11 @@ data TextNode d t = TextNode
85163 { _textNode_raw :: RawTextNode d
86164 }
87165
88−type AttributeName = (Maybe Namespace, Text)
166+data AttributeName = AttributeName !(Maybe Namespace) !Text deriving (Show, Read, Eq, Ord)
167+
168+-- | By default, AttributeNames are unnamespaced
169+instance IsString AttributeName where
170+ fromString = AttributeName Nothing . fromString
89171
90172data Propagation
91173 = Propagation_Continue
@@ -220,12 +302,36 @@ instance (Reflex t, DomSpace (DomBuilderSpace m)) => Default (RawElementConfig E
220302 , _rawElementConfig_eventSpec = def
221303 }
222304
223−makeLenses ''TextNodeConfig
224−makeLenses ''ElementConfig
225−makeLenses ''PlaceholderConfig
226−makeLenses ''InputElementConfig
227−makeLenses ''TextAreaElementConfig
228−makeLenses ''RawElementConfig
305+data SelectElementConfig er t m = SelectElementConfig
306+ { _selectElementConfig_initialValue :: Text
307+ , _selectElementConfig_setValue :: Event t Text
308+ , _selectElementConfig_elementConfig :: ElementConfig er t m
309+ }
310+
311+instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (SelectElementConfig er t m) where
312+ def = SelectElementConfig
313+ { _selectElementConfig_initialValue = ""
314+ , _selectElementConfig_setValue = never
315+ , _selectElementConfig_elementConfig = def
316+ }
317+
318+data SelectElement er d t = SelectElement
319+ { _selectElement_element :: Element er d t
320+ , _selectElement_value :: Dynamic t Text
321+ , _selectElement_change :: Event t Text -- ^ Fires when the value is changed by the user, but not when it is set by setValue
322+ , _selectElement_hasFocus :: Dynamic t Bool
323+ , _selectElement_raw :: RawSelectElement d
324+ }
325+
326+concat <$> mapM makeLenses
327+ [ ''TextNodeConfig
328+ , ''ElementConfig
329+ , ''PlaceholderConfig
330+ , ''InputElementConfig
331+ , ''TextAreaElementConfig
332+ , ''SelectElementConfig
333+ , ''RawElementConfig
334+ ]
229335
230336class CanDeleteSelf t a | a -> t where
231337 deleteSelf :: Lens' a (Event t ())
@@ -290,24 +396,8 @@ instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (ElementConfig
290396 , _elementConfig_eventSpec = def
291397 }
292398
293−{-# INLINABLE liftPostBuildTElementConfig #-}
294−liftPostBuildTElementConfig :: ElementConfig er t (PostBuildT t m) -> ElementConfig er t m
295−liftPostBuildTElementConfig cfg = cfg
296− { _elementConfig_eventSpec = _elementConfig_eventSpec cfg
297− }
298−
299−{-# INLINABLE liftDynamicWriterTElementConfig #-}
300−liftDynamicWriterTElementConfig :: ElementConfig er t (DynamicWriterT t w m) -> ElementConfig er t m
301−liftDynamicWriterTElementConfig cfg = cfg
302− { _elementConfig_eventSpec = _elementConfig_eventSpec cfg
303− }
304−
305399instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBuilder t (PostBuildT t m) where
306400 type DomBuilderSpace (PostBuildT t m) = DomBuilderSpace m
307− {-# INLINABLE textNode #-}
308− textNode = lift . textNode
309− {-# INLINABLE element #-}
310− element t cfg child = liftWith $ \run -> element t (liftPostBuildTElementConfig cfg) $ run child
311401 {-# INLINABLE placeholder #-}
312402 placeholder cfg = lift $ do
313403 rec childPostBuild <- deletable (_placeholder_deletedSelf p) $ performEvent $ return () <$ _placeholder_insertedAbove p
@@ -315,11 +405,6 @@ instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBui
315405 { _placeholderConfig_insertAbove = ffor (_placeholderConfig_insertAbove cfg) $ \a -> runPostBuildT a =<< headE childPostBuild
316406 }
317407 return p
318− {-# INLINABLE inputElement #-}
319− inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftPostBuildTElementConfig
320− {-# INLINABLE textAreaElement #-}
321− textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftPostBuildTElementConfig
322− placeRawElement = lift . placeRawElement
323408 wrapRawElement e cfg = liftWith $ \run -> wrapRawElement e $ fmap1 run cfg
324409
325410instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
@@ -327,7 +412,7 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
327412 textNode = liftTextNode
328413 element elementTag cfg (DynamicWriterT child) = DynamicWriterT $ do
329414 s <- get
330− let cfg' = liftDynamicWriterTElementConfig cfg
415+ let cfg' = liftElementConfig cfg
331416 (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
332417 put newS
333418 return (el, a)
@@ -357,8 +442,14 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
357442 return $ p
358443 { _placeholder_insertedAbove = result
359444 }
360− inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
361− textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
445+ inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftElementConfig
446+ textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftElementConfig
447+ selectElement cfg (DynamicWriterT child) = DynamicWriterT $ do
448+ s <- get
449+ let cfg' = cfg & selectElementConfig_elementConfig %~ liftElementConfig
450+ (el, (a, newS)) <- lift $ selectElement cfg' $ runStateT child s
451+ put newS
452+ return (el, a)
362453 placeRawElement = lift . placeRawElement
363454 wrapRawElement e cfg = lift $ wrapRawElement e $ cfg
364455 { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
@@ -393,6 +484,10 @@ instance Functor1 (TextAreaElementConfig er t) where
393484 type Functor1Constraint (TextAreaElementConfig er t) a b = Functor1Constraint (ElementConfig er t) a b
394485 fmap1 f cfg = cfg & textAreaElementConfig_elementConfig %~ fmap1 f
395486
487+instance Functor1 (SelectElementConfig er t) where
488+ type Functor1Constraint (SelectElementConfig er t) a b = Functor1Constraint (ElementConfig er t) a b
489+ fmap1 f cfg = cfg & selectElementConfig_elementConfig %~ fmap1 f
490+
396491instance Functor1 (RawElementConfig er t) where
397492 type Functor1Constraint (RawElementConfig er t) a b = DomBuilderSpace a ~ DomBuilderSpace b
398493 {-# INLINABLE fmap1 #-}
@@ -421,14 +516,7 @@ instance Reflex t => HasDomEvent t (TextAreaElement EventResult d t) en where
421516
422517instance DomBuilder t m => DomBuilder t (ReaderT r m) where
423518 type DomBuilderSpace (ReaderT r m) = DomBuilderSpace m
424− {-# INLINABLE textNode #-}
425− textNode = liftTextNode
426− element = liftElement
427519 placeholder = liftPlaceholder
428− inputElement = liftInputElement
429− textAreaElement = liftTextAreaElement
430− placeRawElement = lift . placeRawElement
431− wrapRawElement = liftWrapRawElement
432520
433521type LiftDomBuilder t f m =
434522 ( Reflex t