@@ -3,7 +3,7 @@ module Lumi.Components.Modal where
3
3
import Prelude
4
4
5
5
import Color (cssStringHSLA , hsla , toHSLA )
6
- import Data.Foldable (foldMap )
6
+ import Data.Foldable (foldMap , for_ )
7
7
import Data.Maybe (Maybe (..))
8
8
import Data.Monoid (guard )
9
9
import Data.Nullable (Nullable , toMaybe , toNullable )
@@ -18,12 +18,17 @@ import Lumi.Components.Link as Link
18
18
import Lumi.Components.Size (Size (..))
19
19
import Lumi.Components.Text (sectionHeader_ , subsectionHeader , text )
20
20
import Lumi.Components.ZIndex (ziModal )
21
+ import Prim.Row (class Nub , class Union )
21
22
import React.Basic (Component , JSX , ReactComponent , createComponent , element , empty , make , makeStateless , toReactComponent )
22
23
import React.Basic.DOM as R
24
+ import React.Basic.DOM.Components.GlobalEvents (windowEvent )
23
25
import React.Basic.DOM.Events (currentTarget , stopPropagation , target )
24
26
import React.Basic.Events as Events
25
27
import Record (merge )
26
- import Prim.Row (class Nub , class Union )
28
+ import Web.Event.Event (Event , EventType (..))
29
+ import Web.Event.Event as E
30
+ import Web.UIEvent.KeyboardEvent (fromEvent , key )
31
+
27
32
28
33
foreign import toggleBodyClass :: EffectFn2 String Boolean Unit
29
34
@@ -232,63 +237,80 @@ modalPortal = toReactComponent identity modalPortalComponent
232
237
render { props, state } =
233
238
if not props.modalOpen
234
239
then empty
235
- else lumiModalContainer
236
- { onClick: Events .handler (Events .merge { target, currentTarget })
237
- \{ target, currentTarget } -> do
240
+ else windowEvent
241
+ { eventType: EventType " keydown"
242
+ , options: { capture: false , once: false , passive: false }
243
+ , handler: \e -> do
244
+ keydownEventHandler e
245
+ }
246
+ $ lumiModalContainer
247
+ { onClick: Events .handler (Events .merge { target, currentTarget })
248
+ \{ target, currentTarget } -> do
249
+ props.requestClose
250
+ closeModal
251
+ , children:
252
+ lumiModalOverlay
253
+ { " data-variant" : props.variant
254
+ , children:
255
+ lumiModal
256
+ { " data-size" : show props.size
257
+ , onClick: Events .handler stopPropagation \_ -> pure unit
258
+ , children:
259
+ [ lumiModalHeader
260
+ { children:
261
+ [ if props.variant == " dialog"
262
+ then empty
263
+ else lumiModalClose
264
+ { children: icon_ Remove
265
+ , onClick: mkEffectFn1 \_ -> do
266
+ props.requestClose
267
+ closeModal
268
+ }
269
+ , if not null props.title
270
+ then sectionHeader_ props.title
271
+ else empty
272
+ ]
273
+ }
274
+ , lumiModalContent
275
+ { children: props.children
276
+ , " data-internal-borders" : props.internalBorders
277
+ }
278
+ , lumiModalFooter
279
+ { children:
280
+ [ guard props.closeButton $
281
+ Button .button Button .secondary
282
+ { title =
283
+ case toMaybe props.onActionButtonClick of
284
+ Nothing -> " Close"
285
+ Just _ -> " Cancel"
286
+ , onPress = mkEffectFn1 \_ -> do
287
+ props.requestClose
288
+ closeModal
289
+ }
290
+ , toMaybe props.onActionButtonClick # foldMap \actionFn ->
291
+ Button .button Button .primary
292
+ { title = props.actionButtonTitle
293
+ , buttonState = props.actionButtonState
294
+ , onPress = mkEffectFn1 \_ -> actionFn
295
+ , style = R .css { marginLeft: " 12px" }
296
+ }
297
+ ]
298
+ }
299
+ ]
300
+ }
301
+ }
302
+ }
303
+ where
304
+ keydownEventHandler :: Event -> Effect Unit
305
+ keydownEventHandler e = do
306
+ let mKey = eventKey e
307
+ for_ mKey case _ of
308
+ " Escape" -> do
309
+ E .preventDefault e
310
+ E .stopPropagation e
238
311
props.requestClose
239
312
closeModal
240
- , children:
241
- lumiModalOverlay
242
- { " data-variant" : props.variant
243
- , children:
244
- lumiModal
245
- { " data-size" : show props.size
246
- , onClick: Events .handler stopPropagation \_ -> pure unit
247
- , children:
248
- [ lumiModalHeader
249
- { children:
250
- [ if props.variant == " dialog"
251
- then empty
252
- else lumiModalClose
253
- { children: icon_ Remove
254
- , onClick: mkEffectFn1 \_ -> do
255
- props.requestClose
256
- closeModal
257
- }
258
- , if not null props.title
259
- then sectionHeader_ props.title
260
- else empty
261
- ]
262
- }
263
- , lumiModalContent
264
- { children: props.children
265
- , " data-internal-borders" : props.internalBorders
266
- }
267
- , lumiModalFooter
268
- { children:
269
- [ guard props.closeButton $
270
- Button .button Button .secondary
271
- { title =
272
- case toMaybe props.onActionButtonClick of
273
- Nothing -> " Close"
274
- Just _ -> " Cancel"
275
- , onPress = mkEffectFn1 \_ -> do
276
- props.requestClose
277
- closeModal
278
- }
279
- , toMaybe props.onActionButtonClick # foldMap \actionFn ->
280
- Button .button Button .primary
281
- { title = props.actionButtonTitle
282
- , buttonState = props.actionButtonState
283
- , onPress = mkEffectFn1 \_ -> actionFn
284
- , style = R .css { marginLeft: " 12px" }
285
- }
286
- ]
287
- }
288
- ]
289
- }
290
- }
291
- }
313
+ _ -> pure unit
292
314
293
315
lumiModalContainer = element (R .unsafeCreateDOMComponent " lumi-modal-container" )
294
316
lumiModalOverlay = element (R .unsafeCreateDOMComponent " lumi-modal-overlay" )
@@ -466,3 +488,6 @@ styles = jss
466
488
}
467
489
where
468
490
alpha a = toHSLA >>> \{ h, s, l } -> hsla h s l a
491
+
492
+ eventKey :: Event -> Maybe String
493
+ eventKey e = map key (fromEvent e)
0 commit comments