@@ -1,92 +1,90 @@
1−module Yoga.Block.Molecule.Modal.View (component, Props) where
1+module Yoga.Block.Molecule.Modal.View where
22
33import Yoga.Prelude.View
4−import Data.Nullable as Nullable
4+
55import Effect.Unsafe (unsafePerformEffect)
6−import Framer.Motion (withMotion)
7−import Framer.Motion as Motion
8−import React.Basic.DOM (createPortal, css)
6+import Fahrtwind.Style.Color.Background (background)
7+import Fahrtwind.Style.Color.Tailwind as TW
8+import Fahrtwind.Style.Color.Util (withAlpha)
9+import Framer.Motion as M
910import React.Basic.DOM as R
10−import React.Basic.Emotion as Emotion
11−import React.Basic.Hooks (reactComponent)
1211import React.Basic.Hooks as React
13−import Web.DOM (Element)
14−import Yoga.Block.Hook.Key as KeyCode
15−import Yoga.Block.Hook.UseKeyDown (useKeyDown)
16−import Yoga.Block.Layout.Imposter as Imposter
1712import Yoga.Block.Molecule.Modal.Style as Style
13+import Yoga.Block.Hook.UseRenderInPortal (useRenderInPortal)
14+import Yoga.Block.Layout.Centre.View as Centre
15+import Yoga.Block.Layout.Cover.View as Cover
16+import Yoga.Block.Quark.ClickAway.View as ClickAway
17+
18+type ModalIds = { clickAwayId ∷ String, modalContainerId ∷ String }
1819
1920type Props =
20− { content ∷ JSX
21− , isOpen ∷ Boolean
22− , onDismiss ∷ Effect Unit
23− , target ∷ Element
24− | ()
21+ { hide ∷ Effect Unit
22+ , isVisible ∷ Boolean
23+ , content ∷ JSX
24+ , allowClickAway ∷ Boolean
25+ , clickAwayId ∷ String
26+ , modalContainerId ∷ String
2527 }
2628
2729component ∷ ReactComponent Props
28−component =
29− unsafePerformEffect
30− $ reactComponent "Modal Wrapper" \{ content, isOpen, onDismiss, target } -> React.do
31− useKeyDown $ \_ _ -> case _ of
32− KeyCode.Escape -> onDismiss
33− _ -> mempty
34− clickAwayRef <- React.useRef Nullable.null
35− let
36− toRender ∷ JSX
37− toRender =
38− R.div' </ {}
39− />
40− [ Motion.animatePresence </ {} /> [ guard isOpen $ element clickaway { theRef: clickAwayRef, onDismiss } ]
41− , Motion.animatePresence </ {} /> [ guard isOpen $ element window { clickAwayRef, onDismiss, content } ]
42− ]
43− pure (createPortal toRender target)
44−
45−clickaway ∷ ReactComponent { theRef ∷ Ref (Nullable Node), onDismiss ∷ Effect Unit }
46−clickaway =
47− unsafePerformEffect
48− $ reactComponent "Modal Clickaway" \{ theRef, onDismiss } -> React.do
49− pure $ Emotion.elementKeyed Motion.div
50− $
51− { key: "ry-modal-clickaway"
52− , onClick: handler_ onDismiss
53− , className: "ry-modal-clickaway"
54− , css: Style.clickaway
55− , initial: Motion.prop $ css { opacity: 0.0 }
56− , animate: Motion.prop $ css { opacity: 1.0 }
57− , exit: Motion.prop $ css { opacity: 0.0 }
58− , ref: theRef
59− }
60−
61−window ∷ ReactComponent { clickAwayRef ∷ NodeRef, content ∷ JSX, onDismiss ∷ Effect Unit }
62−window =
63− unsafePerformEffect
64− $ reactComponent "Modal Window" \{ clickAwayRef, content, onDismiss } -> React.do
65− imposterRef <- useRef null
66− pure
67− $ Emotion.element motionImposter
68− ( { className: "ry-modal-window"
69− , css: Style.modal
70− , ref: imposterRef
71− , onClick: handler_ onDismiss
72− , children:
73− [ Motion.div
74− </*
75− { className: "ry-modal"
76− , css: Style.modal
77− , drag: Motion.prop true
78− , dragMomentum: Motion.prop false
79− , dragConstraints: Motion.prop clickAwayRef
80− , onClick: handler stopPropagation mempty
30+component = unsafePerformEffect do
31+ React.reactComponent "Modal" \props → React.do
32+ let
33+ { hide
34+ , isVisible
35+ , content
36+ , allowClickAway
37+ , clickAwayId
38+ , modalContainerId
39+ } = props
40+ renderInPortal ← useRenderInPortal modalContainerId
41+ let
42+ child = div "ry-modal" Style.modal [ content ]
43+ pure $ fragment
44+ [ ClickAway.component </>
45+ { css: background (TW.gray._900 # withAlpha 0.5)
46+ , hide: if allowClickAway then hide else mempty
47+ , isVisible
48+ , clickAwayId
49+ }
50+ , renderInPortal
51+ $ Cover.component
52+ </ {}
53+ />
54+ [ Centre.component </ {} />
55+ [ M.animatePresence </ {} />
56+ [ guard isVisible
57+ $ M.div
58+ </
59+ { key: "popOver"
60+ , initial: M.initial
61+ ( R.css
62+ { opacity: 0.0
63+ , scale: 0.3
64+ }
65+ )
66+ , animate: M.animate
67+ ( R.css
68+ { opacity: 1
69+ , scale: 1.0
70+ , transition:
71+ { duration: 0.4, type: "spring" }
72+ }
73+ )
74+ , exit: M.exit
75+ ( R.css
76+ { opacity: 0
77+ , scale: 0.3
78+ , transition: { duration: 0.2 }
79+ }
80+ )
81+ -- , onAnimationComplete
82+ -- , onAnimationStart
83+ -- , ref: motionRef
8184 }
82− /> [ content ]
85+ />
86+ [ child ]
8387 ]
84− }
85− `withMotion`
86− { initial: css { transform: "translate(-50%, -50%) scale3d(0.1,0.1,0.1)", opacity: 0 }
87− , animate: css { transform: "translate(-50%, -50%) scale3d(1,1,1)", opacity: 1 }
88− , exit: css { transform: "translate(-50%, -50%) scale3d(0.1,0.1,0.1)", opacity: 0 }
89− }
90− )
91− where
92− motionImposter = unsafePerformEffect $ Motion.custom Imposter.component
88+ ]
89+ ]
90+ ]