0 | module Web.Async.View
2 | import Data.Linear.Traverse1
6 | import IO.Async.Logging
11 | import Text.HTML.DomID
12 | import Web.Async.Event
13 | import Web.Async.Util
14 | import Web.Internal.Types
16 | %hide Data.Linear.(.)
23 | %foreign "browser:lambda:(e,f,w) => {const o = new ResizeObserver((es) => f(e.getBoundingClientRect())(w));o.observe(e)}"
24 | prim__observeResize : Element -> (DOMRect -> PrimIO ()) -> PrimIO ()
26 | %foreign "browser:lambda:(e,f,w) => {const o = new MutationObserver(() => {if (!e.isConnected) {o.disconnect(); f(w);}}); o.observe(document.body, {childList : true, subtree : true});}"
27 | prim__observeRemove : Element -> PrimIO () -> PrimIO ()
30 | %foreign "browser:lambda:x=>x.bubbles?1:0"
31 | bubbles : Event -> Bool
34 | %foreign "browser:lambda:x=>x.cancelable?1:0"
35 | cancelable : Event -> Bool
37 | %foreign "browser:lambda:(x,s,f,w)=>x.addEventListener(s,\e => f(e)(w))"
38 | prim__addlistener : EventTarget -> String -> (Event -> PrimIO ()) -> PrimIO ()
40 | %foreign "browser:lambda:(x,w)=>x.preventDefault()"
41 | prim__preventDefault : Event -> PrimIO ()
43 | %foreign "browser:lambda:(x,w)=>x.stopPropagation()"
44 | prim__stopPropagation : Event -> PrimIO ()
51 | addEventListener : EventTarget -> String -> (Event -> IO1 ()) -> IO1 ()
52 | addEventListener et ev cb = ffi $
prim__addlistener et ev (primRun . cb)
55 | preventDefault : Event -> IO1 ()
56 | preventDefault ev = ffi $
prim__preventDefault ev
59 | stopPropagation : Event -> IO1 ()
60 | stopPropagation ev = ffi $
prim__stopPropagation ev
74 | -> (preventDefault, stopPropagation : Bool)
78 | registerDOMEvent prev stop el de =
80 | Input f => inst "input" changeInfo f
81 | Change f => inst "change" changeInfo f
82 | Click f => inst "click" mouseInfo f
83 | DblClick f => inst "dblclick" mouseInfo f
84 | KeyDown f => inst "keydown" keyInfo f
85 | KeyUp f => inst "keyup" keyInfo f
86 | Blur v => inst "blur" {t = Event} (const $
pure v) Just
87 | Focus v => inst "focus" {t = Event} (const $
pure v) Just
88 | Close v => inst "close" {t = Event} (const $
pure v) Just
89 | MouseDown f => inst "mousedown" mouseInfo f
90 | MouseUp f => inst "mouseup" mouseInfo f
91 | MouseEnter f => inst "mouseenter" mouseInfo f
92 | MouseLeave f => inst "mouseleave" mouseInfo f
93 | MouseOver f => inst "mouseover" mouseInfo f
94 | MouseOut f => inst "mouseout" mouseInfo f
95 | MouseMove f => inst "mousemove" mouseInfo f
96 | HashChange v => inst "hashchange" {t = Event} (const $
pure v) Just
97 | Scroll f => inst "scroll" scrollInfo f
98 | Wheel f => inst "wheel" wheelInfo f
99 | Resize f => onresize f
100 | Remove v => onremove v
105 | -> {auto c : SafeCast t}
110 | inst {t} s conv f =
111 | let cb : Event -> IO1 ()
113 | when1 (cancelable e && prev) (preventDefault e)
114 | when1 (bubbles e && stop) (stopPropagation e)
115 | let Just vt := castTo t e | Nothing => pure ()
117 | maybe (pure ()) h.sink1 (f vb)
119 | in addEventListener el s cb
121 | onresize : (Rect -> Maybe e) -> IO1 ()
123 | let Just va := castTo Element el | Nothing => pure ()
124 | in ffi $
prim__observeResize va $
\r => primRun $
125 | toRect r >>= maybe (pure ()) h.sink1 . f
127 | onremove : e -> IO1 ()
129 | let Just va := castTo Element el | Nothing => pure ()
130 | in ffi (prim__observeRemove va (primRun (h.sink1 v)))
133 | setAttribute : Element -> Attribute t -> IO1 ()
134 | setAttribute el (Id (Id v)) = ffi $
prim__setAttribute el "id" v
135 | setAttribute el (Str name v) = ffi $
prim__setAttribute el name v
136 | setAttribute el (Bool name True) = ffi $
prim__setAttribute el name ""
137 | setAttribute el (Bool name False) = ffi $
prim__removeAttribute el name
138 | setAttribute el (Event_ p s ev) = registerDOMEvent p s (up el) ev
139 | setAttribute el Empty = pure ()
145 | addNodes : ParentNode -> HTMLNodes -> IO1 ()
147 | addNode : ParentNode -> HTMLNode -> IO1 ()
148 | addNode p (El {tag} _ xs ys) t =
149 | let n # t := ffi (prim__createElement tag) t
150 | _ # t := ffi (prim__append p (up n)) t
151 | _ # t := addNodes (up n) ys t
152 | in traverse1_ (setAttribute n) xs t
153 | addNode p (EEl {tag} _ xs) t =
154 | let n # t := ffi (prim__createElement tag) t
155 | _ # t := ffi (prim__append p (up n)) t
156 | in traverse1_ (setAttribute n) xs t
158 | addNode p (Raw s) t =
159 | let el # t := ffi (prim__createElement "template") t
160 | Just temp := castTo HTMLTemplateElement el | Nothing => () # t
161 | _ # t := ffi (prim__setInnerHTML (up temp) s) t
162 | c # t := ffi (prim__content temp) t
163 | in ffi (prim__append p (up c)) t
165 | addNode p (Text s) t = ffi (prim__appendTxt p s) t
167 | addNode p Empty t = () # t
169 | addNodes p = assert_total $
traverse1_ (addNode p)
171 | parameters {auto has : Has JSErr es}
174 | setupNodes : (Element -> Node -> JS es ()) -> Ref t -> HTMLNodes -> JS es ()
175 | setupNodes adj r ns = do
176 | elem <- castElementByRef {t = Element} r
177 | df <- primIO prim__createDocumentFragment
178 | lift1 $
addNodes (up df) ns
183 | (Element -> Node -> JS es ())
187 | setupNode adj r n = setupNodes adj r [n]
192 | children : Ref t -> HTMLNodes -> JS es ()
193 | children = setupNodes (\el => replaceChildren (up el))
198 | child : Ref t -> HTMLNode -> JS es ()
199 | child = setupNode (\el => replaceChildren (up el))
204 | text : Ref t -> String -> JS es ()
205 | text r = child r . Text
210 | show : Show b => Ref t -> b -> JS es ()
211 | show r = text r . show
216 | raw : Ref t -> String -> JS es ()
217 | raw r = child r . Raw
221 | style : Ref Tag.Style -> List (Rule 1) -> JS es ()
222 | style r = raw r . fastUnlines . map interpolate
227 | afterMany : Ref t -> HTMLNodes -> JS es ()
228 | afterMany = setupNodes (\el => after (up el))
233 | after : Ref t -> HTMLNode -> JS es ()
234 | after = setupNode (\el => after (up el))
239 | beforeMany : Ref t -> HTMLNodes -> JS es ()
240 | beforeMany = setupNodes (\el => before (up el))
245 | before : Ref t -> HTMLNode -> JS es ()
246 | before = setupNode (\el => before (up el))
251 | appendMany : Ref t -> HTMLNodes -> JS es ()
252 | appendMany = setupNodes (\el => append (up el))
257 | append : Ref t -> HTMLNode -> JS es ()
258 | append = setupNode (\el => append (up el))
263 | prependMany : Ref t -> HTMLNodes -> JS es ()
264 | prependMany = setupNodes (\el => prepend (up el))
269 | prepend : Ref t -> HTMLNode -> JS es ()
270 | prepend = setupNode (\el => prepend (up el))
275 | replaceMany : Ref t -> HTMLNodes -> JS es ()
276 | replaceMany = setupNodes (\el => replace (up el))
281 | replace : Ref t -> HTMLNode -> JS es ()
282 | replace = setupNode (\el => replace (up el))
286 | remove : Ref t -> JS es ()
288 | castElementByRef {t = Element} r >>= \el => primIO (prim__remove (up el))
292 | attr : Ref t -> Attribute t -> JS es ()
293 | attr r a = castElementByRef r >>= \el => lift1 $
setAttribute el a
298 | checked : Ref Tag.Input -> Bool -> JS es ()
300 | castElementByRef r >>= \el => primIO (prim__setChecked el $
toFFI b)
304 | disabled : Ref t -> Bool -> JS es ()
305 | disabled r = attr r . disabled
313 | disabledE : {0 a,b : _} -> Ref t -> Either a b -> JS es ()
314 | disabledE r = disabled r . isLeft
322 | disabledM : {0 a : _} -> Ref t -> Maybe a -> JS es ()
323 | disabledM r = disabled r . isNothing
327 | focus : Ref t -> JS es ()
328 | focus r = castElementByRef {t = HTMLElement} r >>= focus
332 | blur : Ref t -> JS es ()
333 | blur r = castElementByRef {t = HTMLElement} r >>= blur
337 | dialogShow : Ref t -> JS es ()
338 | dialogShow r = castElementByRef {t = HTMLDialogElement} r >>= dialogShow
342 | showModal : Ref t -> JS es ()
343 | showModal r = castElementByRef {t = HTMLDialogElement} r >>= showModal
347 | dialogClose : Ref t -> JS es ()
348 | dialogClose r = castElementByRef {t = HTMLDialogElement} r >>= dialogClose
354 | parameters {auto lg : Loggable JS JSErr}
357 | elemChildren : DomID -> HTMLNodes -> JS [] ()
358 | elemChildren i = logErrs . children {es = [JSErr]} (elemRef i)
361 | elemChild : DomID -> HTMLNode -> JS [] ()
362 | elemChild i = logErrs . child {es = [JSErr]} (elemRef i)
365 | elemAppend : DomID -> HTMLNode -> JS [] ()
366 | elemAppend i = logErrs . append {es = [JSErr]} (elemRef i)
369 | elemAppendMany : DomID -> HTMLNodes -> JS [] ()
370 | elemAppendMany i = logErrs . appendMany {es = [JSErr]} (elemRef i)
373 | elemPrepend : DomID -> HTMLNode -> JS [] ()
374 | elemPrepend i = logErrs . prepend {es = [JSErr]} (elemRef i)
377 | elemPrependMany : DomID -> HTMLNodes -> JS [] ()
378 | elemPrependMany i = logErrs . prependMany {es = [JSErr]} (elemRef i)
381 | clearElem : DomID -> JS [] ()
382 | clearElem i = elemChildren i []
385 | removeElem : DomID -> JS [] ()
386 | removeElem = logErrs . remove {es = [JSErr]} . elemRef
389 | replaceElem : DomID -> HTMLNode -> JS [] ()
390 | replaceElem i = logErrs . replace {es = [JSErr]} (elemRef i)
393 | btnAttr : DomID -> Attribute Tag.Button -> JS [] ()
394 | btnAttr v a = logErrs $
attr {es = [JSErr]} (btnRef v) a