-
Notifications
You must be signed in to change notification settings - Fork 31
/
Refs.purs
85 lines (75 loc) · 3.27 KB
/
Refs.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
module Refs where
import Prelude
import Data.Int (round)
import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable, null)
import Math (pow, sqrt)
import React.Basic.DOM as R
import React.Basic.Hooks (CreateComponent, Ref, UseEffect, UseRef, UseState, Hook, type (/\), component, element, fragment, readRefMaybe, useEffect, useRef, useState, (/\))
import React.Basic.Hooks as React
import Unsafe.Coerce (unsafeCoerce)
import Web.DOM (Node)
import Web.Event.Event (EventType(..))
import Web.Event.EventTarget (addEventListener, eventListener, removeEventListener)
import Web.HTML (window)
import Web.HTML.HTMLElement (getBoundingClientRect)
import Web.HTML.HTMLElement as HTMLElement
import Web.HTML.Window as Window
mkRefs :: CreateComponent {}
mkRefs = do
component "Refs" \props -> React.do
mouseDistance1 /\ buttonRef1 <- useNodeDistanceFromMouse
mouseDistance2 /\ buttonRef2 <- useNodeDistanceFromMouse
mouseDistance3 /\ buttonRef3 <- useNodeDistanceFromMouse
pure $ fragment
[ element (R.unsafeCreateDOMComponent "button")
{ ref: buttonRef1
, children: [ R.text $ show mouseDistance1 <> "px" ]
, style: R.css { width: "100px", position: "absolute", top: "20px", left: "200px" }
}
, element (R.unsafeCreateDOMComponent "button")
{ ref: buttonRef2
, children: [ R.text $ show mouseDistance2 <> "px" ]
, style: R.css { width: "100px", position: "absolute", top: "60px", left: "40px" }
}
, element (R.unsafeCreateDOMComponent "button")
{ ref: buttonRef3
, children: [ R.text $ show mouseDistance3 <> "px" ]
, style: R.css { width: "100px", position: "absolute", top: "120px", left: "90px" }
}
]
type UseNodeDistance hooks = UseEffect Unit (UseState Int (UseRef (Nullable Node) hooks))
useNodeDistanceFromMouse :: Hook UseNodeDistance (Int /\ (Ref (Nullable Node)))
useNodeDistanceFromMouse = React.do
elementRef <- useRef null
mouseDistance /\ setMouseDistance <- useState 0
useEffect unit do
maybeElement <- map (HTMLElement.fromNode =<< _) (readRefMaybe elementRef)
case maybeElement of
Nothing -> pure (pure unit)
Just element -> do
mouseMoveListener <- eventListener \e -> do
{ top, bottom, left, right } <- getBoundingClientRect element
let
mouseX = (unsafeCoerce e).clientX
mouseY = (unsafeCoerce e).clientY
distanceX =
if mouseX # between left right
then 0.0
else if mouseX < left
then left - mouseX
else mouseX - right
distanceY =
if mouseY # between top bottom
then 0.0
else if mouseY < top
then top - mouseY
else mouseY - bottom
distance = sqrt ((distanceX `pow` 2.0) + (distanceY `pow` 2.0))
setMouseDistance \_ -> round distance
let mouseMoveEventType = EventType "mousemove"
windowEventTarget <- map Window.toEventTarget window
addEventListener mouseMoveEventType mouseMoveListener false windowEventTarget
pure do
removeEventListener mouseMoveEventType mouseMoveListener false windowEventTarget
pure (mouseDistance /\ elementRef)