Skip to content

Commit 7ddcc7e

Browse files
committed
Add Prop implementation
1 parent 4faf604 commit 7ddcc7e

File tree

3 files changed

+316
-49
lines changed

3 files changed

+316
-49
lines changed

src/Halogen/VDom/DOM/Prop.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"use strict";
2+
3+
exports.setAttribute = function (ns, attr, val, el) {
4+
return function () {
5+
if (ns != null) {
6+
el.setAttributeNS(ns, attr, val);
7+
} else {
8+
el.setAttribute(attr, val);
9+
}
10+
};
11+
};
12+
13+
exports.removeAttribute = function (ns, attr, el) {
14+
return function () {
15+
if (ns != null) {
16+
el.removeAttributeNS(ns, attr);
17+
} else {
18+
el.removeAttribute(attr);
19+
}
20+
};
21+
};
22+
23+
exports.addEventListener = function (ev, listener, el) {
24+
return function () {
25+
el.addEventListener(ev, listener, false);
26+
};
27+
};
28+
29+
exports.removeEventListener = function (ev, listener, el) {
30+
return function () {
31+
el.removeEventListener(ev, listener, false);
32+
};
33+
};
34+
35+
exports.setProperty = function (prop, val, el) {
36+
return function () {
37+
el[prop] = val;
38+
};
39+
};
40+
41+
exports.getProperty = function (prop, el) {
42+
return function () {
43+
return el[prop];
44+
};
45+
};
46+
47+
exports.removeProperty = function (prop, el) {
48+
return function () {
49+
delete el[prop];
50+
};
51+
};
52+
53+
exports.unsafeLookup = function (key, obj) {
54+
return obj[key];
55+
};
56+
57+
exports.pokeMutMap = exports.setProperty;

src/Halogen/VDom/DOM/Prop.purs

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
module Halogen.VDom.DOM.Prop
2+
( Prop(..)
3+
, Namespace(..)
4+
, ElemRef(..)
5+
, PropValue
6+
, propFromString
7+
, propFromBoolean
8+
, propFromInt
9+
, propFromNumber
10+
, buildProp
11+
) where
12+
13+
import Prelude
14+
import Control.Monad.Eff (Eff)
15+
import Control.Monad.Eff.Ref (REF)
16+
import Control.Monad.Eff.Ref as Ref
17+
import Data.Maybe (Maybe(..), maybe)
18+
import Data.StrMap (StrMap)
19+
import Data.StrMap as StrMap
20+
import Data.StrMap.ST (STStrMap)
21+
import Data.StrMap.ST as STStrMap
22+
import Data.Nullable (Nullable, toNullable)
23+
import Data.Function.Uncurried as Fn
24+
import Data.Newtype (class Newtype)
25+
import Data.Tuple (Tuple(..), fst, snd)
26+
import DOM (DOM)
27+
import DOM.Event.EventTarget (EventListener, eventListener) as DOM
28+
import DOM.Event.Types (EventType(..), Event) as DOM
29+
import DOM.HTML.Types (HTMLElement) as DOM
30+
import DOM.Node.Types (Element) as DOM
31+
import Halogen.VDom as V
32+
import Halogen.VDom.Util (refEq, diffWithKeyAndIxE, strMapWithIxE)
33+
import Unsafe.Coerce (unsafeCoerce)
34+
35+
data Prop a
36+
= Attribute (Maybe Namespace) String String
37+
| Property String PropValue
38+
| Handler DOM.EventType (DOM.Event Maybe a)
39+
| Ref (ElemRef DOM.HTMLElement Maybe a)
40+
41+
instance functorPropFunctor Prop where
42+
map f (Handler ty g) = Handler ty (map f <$> g)
43+
map f (Ref g) = Ref (map f <$> g)
44+
map f p = unsafeCoerce p
45+
46+
newtype Namespace = Namespace String
47+
48+
derive instance newtypeNamespaceNewtype Namespace _
49+
50+
data ElemRef a
51+
= Created a
52+
| Removed a
53+
54+
instance functorElemRefFunctor ElemRef where
55+
map f (Created a) = Created (f a)
56+
map f (Removed a) = Removed (f a)
57+
58+
foreign import data PropValue ∷ *
59+
60+
propFromString String PropValue
61+
propFromString = unsafeCoerce
62+
63+
propFromBoolean Boolean PropValue
64+
propFromBoolean = unsafeCoerce
65+
66+
propFromInt Int PropValue
67+
propFromInt = unsafeCoerce
68+
69+
propFromNumber Number PropValue
70+
propFromNumber = unsafeCoerce
71+
72+
type PropEff eff a =
73+
Eff (dom DOM, ref REF | eff) a
74+
75+
buildProp
76+
eff a
77+
. (a Eff (ref REF, dom DOM | eff) Unit)
78+
DOM.Element
79+
V.VDomMachine (ref REF, dom DOM | eff) (Array (Prop a)) Unit
80+
buildProp emit el = render
81+
where
82+
render ps1 = do
83+
events ← newMutMap
84+
ps1' ← Fn.runFn3 strMapWithIxE ps1 propToStrKey (applyProp events)
85+
pure
86+
(V.Step unit
87+
(Fn.runFn2 patch (unsafeFreeze events) ps1')
88+
(done ps1'))
89+
90+
patch = Fn.mkFn2 \prevEvents ps1 → \ps2 → do
91+
events ← newMutMap
92+
let
93+
onThese = Fn.runFn2 diffProp prevEvents events
94+
onThis = removeProp prevEvents
95+
onThat = applyProp events
96+
ps2' ← Fn.runFn6 diffWithKeyAndIxE ps1 ps2 propToStrKey onThese onThis onThat
97+
pure
98+
(V.Step unit
99+
(Fn.runFn2 patch (unsafeFreeze events) ps2')
100+
(done ps2'))
101+
102+
done ps = do
103+
case StrMap.lookup "ref" ps of
104+
Just (Ref f) → do
105+
mbEmit (f (Removed (unsafeElementToHTMLElement el)))
106+
_ → do
107+
effUnit
108+
109+
mbEmit =
110+
maybe effUnit emit
111+
112+
applyProp events = Fn.mkFn3 \_ _ v →
113+
case v of
114+
Attribute ns attr val → do
115+
Fn.runFn4 setAttribute (toNullable ns) attr val el
116+
pure v
117+
Property prop val → do
118+
Fn.runFn3 setProperty prop val el
119+
pure v
120+
Handler (DOM.EventType ty) f → do
121+
ref ← Ref.newRef f
122+
let
123+
listener = DOM.eventListener \ev → do
124+
f' ← Ref.readRef ref
125+
mbEmit (f' ev)
126+
Fn.runFn3 pokeMutMap ty (Tuple listener ref) events
127+
Fn.runFn3 addEventListener ty listener el
128+
pure v
129+
Ref f → do
130+
mbEmit (f (Created (unsafeElementToHTMLElement el)))
131+
pure v
132+
133+
diffProp = Fn.mkFn2 \prevEvents events → Fn.mkFn4 \_ _ v1 v2 →
134+
case v1, v2 of
135+
Attribute _ _ val1, Attribute ns2 attr2 val2 →
136+
case val1 /= val2 of
137+
truedo
138+
Fn.runFn4 setAttribute (toNullable ns2) attr2 val2 el
139+
pure v2
140+
_ →
141+
effPure v2
142+
Property _ val1, Property prop2 val2 →
143+
case Fn.runFn2 refEq val1 val2, prop2 == "value" of
144+
true, _ →
145+
effPure v2
146+
_, falsedo
147+
Fn.runFn3 setProperty prop2 val2 el
148+
pure v2
149+
_, _ → do
150+
elVal ← Fn.runFn2 getProperty "value" el
151+
case not (Fn.runFn2 refEq elVal val2) of
152+
truedo
153+
Fn.runFn3 setProperty prop2 val2 el
154+
pure v2
155+
_ →
156+
pure v2
157+
Handler _ _, Handler (DOM.EventType ty) f → do
158+
let
159+
handler = Fn.runFn2 unsafeLookup ty prevEvents
160+
Ref.writeRef (snd handler) f
161+
Fn.runFn3 pokeMutMap ty handler events
162+
pure v2
163+
_, _ →
164+
effPure v2
165+
166+
removeProp prevEvents = Fn.mkFn2 \_ v →
167+
case v of
168+
Attribute ns attr _ →
169+
Fn.runFn3 removeAttribute (toNullable ns) attr el
170+
Property prop _ →
171+
Fn.runFn2 removeProperty prop el
172+
Handler (DOM.EventType ty) _ → do
173+
let
174+
handler = Fn.runFn2 unsafeLookup ty prevEvents
175+
Fn.runFn3 removeEventListener ty (fst handler) el
176+
Ref _ →
177+
effUnit
178+
179+
propToStrKey i. Prop i String
180+
propToStrKey = case _ of
181+
Attribute (Just (Namespace ns)) attr _ → "attr/" <> ns <> ":" <> attr
182+
Attribute _ attr _ → "attr/:" <> attr
183+
Property prop _ → "prop/" <> prop
184+
Handler (DOM.EventType ty) _ → "handler/" <> ty
185+
Ref _ → "ref"
186+
187+
unsafeElementToHTMLElement DOM.Element DOM.HTMLElement
188+
unsafeElementToHTMLElement = unsafeCoerce
189+
190+
type MutStrMap = STStrMap Void
191+
192+
newMutMap eff a. Eff (ref REF | eff) (MutStrMap a)
193+
newMutMap = unsafeCoerce STStrMap.new
194+
195+
unsafeFreeze a. MutStrMap a StrMap a
196+
unsafeFreeze = unsafeCoerce
197+
198+
-- To avoid dictionary passing in some cases
199+
effPure eff a. a Eff eff a
200+
effPure = pure
201+
202+
effUnit eff. Eff eff Unit
203+
effUnit = pure unit
204+
205+
foreign import setAttribute
206+
eff
207+
. Fn.Fn4 (Nullable Namespace) String String DOM.Element (Eff (dom DOM | eff) Unit)
208+
209+
foreign import removeAttribute
210+
eff
211+
. Fn.Fn3 (Nullable Namespace) String DOM.Element (Eff (dom DOM | eff) Unit)
212+
213+
foreign import addEventListener
214+
eff
215+
. Fn.Fn3 String (DOM.EventListener (dom DOM | eff)) DOM.Element (Eff (dom DOM | eff) Unit)
216+
217+
foreign import removeEventListener
218+
eff
219+
. Fn.Fn3 String (DOM.EventListener (dom DOM | eff)) DOM.Element (Eff (dom DOM | eff) Unit)
220+
221+
foreign import setProperty
222+
eff
223+
. Fn.Fn3 String PropValue DOM.Element (Eff (dom DOM | eff) Unit)
224+
225+
foreign import getProperty
226+
eff
227+
. Fn.Fn2 String DOM.Element (Eff (dom DOM | eff) PropValue)
228+
229+
foreign import removeProperty
230+
eff
231+
. Fn.Fn2 String DOM.Element (Eff (dom DOM | eff) Unit)
232+
233+
foreign import unsafeLookup
234+
a. Fn.Fn2 String (StrMap a) a
235+
236+
foreign import pokeMutMap
237+
eff a
238+
. Fn.Fn3 String a (MutStrMap a) (Eff (ref REF | eff) Unit)

0 commit comments

Comments
 (0)