0 | module Web.Async.View
2 | import Data.Linear.Traverse1
6 | import HTTP.API.Client.FFI
7 | import IO.Async.Logging
12 | import Text.HTML.DomID
13 | import Web.Async.Event
14 | import Web.Async.Util
15 | import Web.Internal.Types
17 | %hide Data.Linear.(.)
24 | %foreign "browser:lambda:(e,f,w) => {const o = new ResizeObserver((es) => f(e.getBoundingClientRect())(w));o.observe(e)}"
25 | prim__observeResize : Element -> (DOMRect -> PrimIO ()) -> PrimIO ()
27 | %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});}"
28 | prim__observeRemove : Element -> PrimIO () -> PrimIO ()
42 | -> (preventDefault, stopPropagation : Bool)
46 | registerDOMEvent prev stop el de =
48 | Input f => inst "input" changeInfo f
49 | Change f => inst "change" changeInfo f
50 | Click f => inst "click" mouseInfo f
51 | DblClick f => inst "dblclick" mouseInfo f
52 | KeyDown f => inst "keydown" keyInfo f
53 | KeyUp f => inst "keyup" keyInfo f
54 | Blur v => inst "blur" {t = Event} (const $
pure v) Just
55 | Focus v => inst "focus" {t = Event} (const $
pure v) Just
56 | Close v => inst "close" {t = Event} (const $
pure v) Just
57 | MouseDown f => inst "mousedown" mouseInfo f
58 | MouseUp f => inst "mouseup" mouseInfo f
59 | MouseEnter f => inst "mouseenter" mouseInfo f
60 | MouseLeave f => inst "mouseleave" mouseInfo f
61 | MouseOver f => inst "mouseover" mouseInfo f
62 | MouseOut f => inst "mouseout" mouseInfo f
63 | MouseMove f => inst "mousemove" mouseInfo f
64 | HashChange v => inst "hashchange" {t = Event} (const $
pure v) Just
65 | Scroll f => inst "scroll" scrollInfo f
66 | Wheel f => inst "wheel" wheelInfo f
67 | Resize f => onresize f
68 | Remove v => onremove v
73 | -> {auto c : SafeCast t}
79 | let cb : Event -> IO1 ()
81 | when1 (cancelable e && prev) (preventDefault e)
82 | when1 (bubbles e && stop) (stopPropagation e)
83 | let Just vt := castTo t e | Nothing => pure ()
85 | maybe (pure ()) h.sink1 (f vb)
87 | in addEventListener el s cb
89 | onresize : (Rect -> Maybe e) -> IO1 ()
91 | let Just va := castTo Element el | Nothing => pure ()
92 | in ffi $
prim__observeResize va $
\r => primRun $
93 | toRect r >>= maybe (pure ()) h.sink1 . f
95 | onremove : e -> IO1 ()
97 | let Just va := castTo Element el | Nothing => pure ()
98 | in ffi (prim__observeRemove va (primRun (h.sink1 v)))
101 | setAttribute : Element -> Attribute t -> IO1 ()
102 | setAttribute el (Id (Id v)) = ffi $
prim__setAttribute el "id" v
103 | setAttribute el (Str name v) = ffi $
prim__setAttribute el name v
104 | setAttribute el (Bool name True) = ffi $
prim__setAttribute el name ""
105 | setAttribute el (Bool name False) = ffi $
prim__removeAttribute el name
106 | setAttribute el (Event_ p s ev) = registerDOMEvent p s (up el) ev
107 | setAttribute el Empty = pure ()
113 | addNodes : ParentNode -> HTMLNodes -> IO1 ()
115 | addNode : ParentNode -> HTMLNode -> IO1 ()
116 | addNode p (El {tag} _ xs ys) t =
117 | let n # t := ffi (prim__createElement tag) t
118 | _ # t := ffi (prim__append p (up n)) t
119 | _ # t := addNodes (up n) ys t
120 | in traverse1_ (setAttribute n) xs t
121 | addNode p (EEl {tag} _ xs) t =
122 | let n # t := ffi (prim__createElement tag) t
123 | _ # t := ffi (prim__append p (up n)) t
124 | in traverse1_ (setAttribute n) xs t
126 | addNode p (Raw s) t =
127 | let el # t := ffi (prim__createElement "template") t
128 | Just temp := castTo HTMLTemplateElement el | Nothing => () # t
129 | _ # t := ffi (prim__setInnerHTML (up temp) s) t
130 | c # t := ffi (prim__content temp) t
131 | in ffi (prim__append p (up c)) t
133 | addNode p (Text s) t = ffi (prim__appendTxt p s) t
135 | addNode p Empty t = () # t
137 | addNodes p = assert_total $
traverse1_ (addNode p)
139 | parameters {auto has : Has JSErr es}
142 | setupNodes : (Element -> Node -> JS es ()) -> Ref t -> HTMLNodes -> JS es ()
143 | setupNodes adj r ns = do
144 | elem <- castElementByRef {t = Element} r
145 | df <- primIO prim__createDocumentFragment
146 | lift1 $
addNodes (up df) ns
151 | (Element -> Node -> JS es ())
155 | setupNode adj r n = setupNodes adj r [n]
160 | children : Ref t -> HTMLNodes -> JS es ()
161 | children = setupNodes (\el => replaceChildren (up el))
166 | child : Ref t -> HTMLNode -> JS es ()
167 | child = setupNode (\el => replaceChildren (up el))
172 | text : Ref t -> String -> JS es ()
173 | text r = child r . Text
178 | show : Show b => Ref t -> b -> JS es ()
179 | show r = text r . show
184 | raw : Ref t -> String -> JS es ()
185 | raw r = child r . Raw
189 | style : Ref Tag.Style -> List (Rule 1) -> JS es ()
190 | style r = raw r . fastUnlines . map interpolate
195 | afterMany : Ref t -> HTMLNodes -> JS es ()
196 | afterMany = setupNodes (\el => after (up el))
201 | after : Ref t -> HTMLNode -> JS es ()
202 | after = setupNode (\el => after (up el))
207 | beforeMany : Ref t -> HTMLNodes -> JS es ()
208 | beforeMany = setupNodes (\el => before (up el))
213 | before : Ref t -> HTMLNode -> JS es ()
214 | before = setupNode (\el => before (up el))
219 | appendMany : Ref t -> HTMLNodes -> JS es ()
220 | appendMany = setupNodes (\el => append (up el))
225 | append : Ref t -> HTMLNode -> JS es ()
226 | append = setupNode (\el => append (up el))
231 | prependMany : Ref t -> HTMLNodes -> JS es ()
232 | prependMany = setupNodes (\el => prepend (up el))
237 | prepend : Ref t -> HTMLNode -> JS es ()
238 | prepend = setupNode (\el => prepend (up el))
243 | replaceMany : Ref t -> HTMLNodes -> JS es ()
244 | replaceMany = setupNodes (\el => replace (up el))
249 | replace : Ref t -> HTMLNode -> JS es ()
250 | replace = setupNode (\el => replace (up el))
256 | replaceBetween : (ri : Ref t) -> (rj : Ref u) -> HTMLNodes -> JS es ()
257 | replaceBetween ri rj ns = Prelude.do
258 | ej <- castElementByRef {t = Element} rj
259 | setupNodes (\el,n => removeTill (up el) (up ej) >> after (up el) n) ri ns
263 | remove : Ref t -> JS es ()
265 | castElementByRef {t = Element} r >>= \el => primIO (prim__remove (up el))
269 | attr : Ref t -> Attribute t -> JS es ()
270 | attr r a = castElementByRef r >>= \el => lift1 $
setAttribute el a
275 | checked : Ref Tag.Input -> Bool -> JS es ()
277 | castElementByRef r >>= \el => primIO (prim__setChecked el $
toFFI b)
281 | disabled : Ref t -> Bool -> JS es ()
282 | disabled r = attr r . disabled
290 | disabledE : {0 a,b : _} -> Ref t -> Either a b -> JS es ()
291 | disabledE r = disabled r . isLeft
299 | disabledM : {0 a : _} -> Ref t -> Maybe a -> JS es ()
300 | disabledM r = disabled r . isNothing
304 | focus : Ref t -> JS es ()
305 | focus r = castElementByRef {t = HTMLElement} r >>= focus
309 | blur : Ref t -> JS es ()
310 | blur r = castElementByRef {t = HTMLElement} r >>= blur
314 | dialogShow : Ref t -> JS es ()
315 | dialogShow r = castElementByRef {t = HTMLDialogElement} r >>= dialogShow
319 | showModal : Ref t -> JS es ()
320 | showModal r = castElementByRef {t = HTMLDialogElement} r >>= showModal
324 | dialogClose : Ref t -> JS es ()
325 | dialogClose r = castElementByRef {t = HTMLDialogElement} r >>= dialogClose
331 | parameters {auto lg : Loggable JS JSErr}
334 | elemChildren : DomID -> HTMLNodes -> JS [] ()
335 | elemChildren i = logErrs . children {es = [JSErr]} (elemRef i)
338 | elemChild : DomID -> HTMLNode -> JS [] ()
339 | elemChild i = logErrs . child {es = [JSErr]} (elemRef i)
342 | elemAppend : DomID -> HTMLNode -> JS [] ()
343 | elemAppend i = logErrs . append {es = [JSErr]} (elemRef i)
346 | elemAppendMany : DomID -> HTMLNodes -> JS [] ()
347 | elemAppendMany i = logErrs . appendMany {es = [JSErr]} (elemRef i)
350 | elemPrepend : DomID -> HTMLNode -> JS [] ()
351 | elemPrepend i = logErrs . prepend {es = [JSErr]} (elemRef i)
354 | elemPrependMany : DomID -> HTMLNodes -> JS [] ()
355 | elemPrependMany i = logErrs . prependMany {es = [JSErr]} (elemRef i)
358 | clearElem : DomID -> JS [] ()
359 | clearElem i = elemChildren i []
362 | removeElem : DomID -> JS [] ()
363 | removeElem = logErrs . remove {es = [JSErr]} . elemRef
366 | replaceElem : DomID -> HTMLNode -> JS [] ()
367 | replaceElem i = logErrs . replace {es = [JSErr]} (elemRef i)
370 | btnAttr : DomID -> Attribute Tag.Button -> JS [] ()
371 | btnAttr v a = logErrs $
attr {es = [JSErr]} (btnRef v) a