Skip to content

Commit

Permalink
window, document event listener interfaces +
Browse files Browse the repository at this point in the history
  • Loading branch information
lue-bird committed Nov 24, 2023
1 parent 21064da commit d91b3b0
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 39 deletions.
4 changes: 3 additions & 1 deletion elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"BrowserApp.Http",
"BrowserApp.Dom",
"BrowserApp.Time",
"BrowserApp.Console"
"BrowserApp.Console",
"BrowserApp.Window",
"BrowserApp.Document"
],
"elm-version": "0.19.1 <= v < 20.0.0",
"dependencies": {
Expand Down
38 changes: 34 additions & 4 deletions runner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
Viewport,
SetViewportOptions,
SetViewportOfOptions,
} from "./browser";
import * as dom from "./browser/dom"; */
} from "./browser/index.js";
import * as dom from "./browser/dom.js"; */

export * from "./http/index.js";
export * from "./browser/index.js";
Expand Down Expand Up @@ -60,10 +60,26 @@ export function start(ports: ElmPorts, appElement: HTMLElement) {
}
},
{
on: event => event?.http,
on: event => event?.addHttpRequest,
run: (config, sendToElm) => {
fetchAdapter.http(config).then(response => { sendToElm(response) })
}
},
{
on: event => event?.addWindowEventListener,
run: windowEventListenerAdd
},
{
on: event => event?.removeWindowEventListener,
run: windowEventListenerRemove
},
{
on: event => event?.addDocumentEventListener,
run: documentEventListenerAdd
},
{
on: event => event?.removeDocumentEventListener,
run: documentEventListenerRemove
}
]

Expand Down Expand Up @@ -161,4 +177,18 @@ function createDomNode(innerPath: number[], node: any, sendToElm: (v: any) => an
const RE_script = /^script$/i;
function noScript(tag: string) {
return RE_script.test(tag) ? 'p' : tag
}
}

function windowEventListenerAdd(eventName: string, sendToElm: (v: any) => any) {
(window as { [key: string]: any })["on" + eventName] = sendToElm;
}
function windowEventListenerRemove(eventName: string) {
(window as { [key: string]: any })["on" + eventName] = null
}

function documentEventListenerAdd(eventName: string, sendToElm: (v: any) => any) {
(window as { [key: string]: any })["on" + eventName] = sendToElm;
}
function documentEventListenerRemove(eventName: string) {
(window as { [key: string]: any })["on" + eventName] = null
}
136 changes: 102 additions & 34 deletions src/BrowserApp.elm
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ type Interface state
| ConsoleLog String
| DomNodeRender (DomNode state)
| HttpRequest (HttpRequest state)
| WindowEventListen { eventName : String, on : Json.Decode.Value -> state }
| DocumentEventListen { eventName : String, on : Json.Decode.Value -> state }


type alias HttpRequest state =
Expand Down Expand Up @@ -223,6 +225,8 @@ type InterfaceId
| IdConsoleLog String
| IdRenderDomNode
| IdHttpRequest HttpRequestId
| IdWindowEventListen String
| IdDocumentEventListen String


{-| Identifier for a [`DomElement`](#DomElement)
Expand Down Expand Up @@ -388,6 +392,14 @@ on stateChange =
}
|> HttpRequest

WindowEventListen listener ->
{ eventName = listener.eventName, on = \value -> listener.on value |> stateChange }
|> WindowEventListen

DocumentEventListen listener ->
{ eventName = listener.eventName, on = \value -> listener.on value |> stateChange }
|> DocumentEventListen


domElementOn : (state -> mappedState) -> (DomElement state -> DomElement mappedState)
domElementOn stateChange =
Expand Down Expand Up @@ -495,6 +507,12 @@ interfaceToId =
HttpRequest httpRequest ->
httpRequest |> httpRequestToId |> IdHttpRequest

WindowEventListen listener ->
IdWindowEventListen listener.eventName

DocumentEventListen listener ->
IdDocumentEventListen listener.eventName


interfaceIdOrder : Ordering InterfaceId InterfaceIdOrder
interfaceIdOrder =
Expand Down Expand Up @@ -571,6 +589,12 @@ interfaceDiffToCmds =

DomNodeRender _ ->
RemoveDom |> Just

WindowEventListen listener ->
RemoveWindowEventListener listener.eventName |> Just

DocumentEventListen listener ->
RemoveDocumentEventListener listener.eventName |> Just
)
)
++ (interfaces.updated
Expand All @@ -582,13 +606,13 @@ interfaceDiffToCmds =
(\interface ->
case interface of
TimeCurrentRequest _ ->
AddRequestTimeNow |> Just
AddTimeCurrentRequest |> Just

TimezoneRequest _ ->
AddRequestTimezone |> Just
AddTimezoneRequest |> Just

TimezoneNameRequest _ ->
AddRequestTimezoneName |> Just
AddTimezoneNameRequest |> Just

ConsoleLog string ->
AddConsoleLog string |> Just
Expand All @@ -598,6 +622,12 @@ interfaceDiffToCmds =

HttpRequest httpRequest ->
AddHttpRequest (httpRequest |> httpRequestToId) |> Just

WindowEventListen listener ->
AddWindowEventListener listener.eventName |> Just

DocumentEventListen listener ->
AddDocumentEventListener listener.eventName |> Just
)
)
++ (case ( interfaces.old |> KeysSet.element interfaceKeys IdRenderDomNode, interfaces.updated |> KeysSet.element interfaceKeys IdRenderDomNode ) of
Expand Down Expand Up @@ -646,36 +676,46 @@ domNodeIdToJson =
interfaceDiffToJson : InterfaceDiff -> Json.Encode.Value
interfaceDiffToJson =
\interfaceDiff ->
case interfaceDiff of
AddRequestTimeNow ->
Json.Encode.object [ ( "addRequestTimeNow", Json.Encode.null ) ]
Json.Encode.object
[ case interfaceDiff of
AddTimeCurrentRequest ->
( "addRequestTimeNow", Json.Encode.null )

AddTimezoneRequest ->
( "addRequestTimezoneOffset", Json.Encode.null )

AddTimezoneNameRequest ->
( "addRequestTimezoneName", Json.Encode.null )

AddConsoleLog string ->
( "addConsoleLog", string |> Json.Encode.string )

AddRequestTimezone ->
Json.Encode.object [ ( "addRequestTimezoneOffset", Json.Encode.null ) ]
ReplaceDomNode domElementToAdd ->
( "replaceDomNode"
, Json.Encode.object
[ ( "path", domElementToAdd.path |> Json.Encode.list Json.Encode.int )
, ( "domNode", domElementToAdd.domNode |> domNodeIdToJson )
]
)

AddRequestTimezoneName ->
Json.Encode.object [ ( "addRequestTimezoneName", Json.Encode.null ) ]
RemoveDom ->
( "removeDom", Json.Encode.null )

AddConsoleLog string ->
Json.Encode.object [ ( "addConsoleLog", string |> Json.Encode.string ) ]
AddHttpRequest httpRequestId ->
( "addHttpRequest", httpRequestId |> httpRequestIdToJson )

ReplaceDomNode domElementToAdd ->
Json.Encode.object
[ ( "replaceDomNode"
, Json.Encode.object
[ ( "path", domElementToAdd.path |> Json.Encode.list Json.Encode.int )
, ( "domNode", domElementToAdd.domNode |> domNodeIdToJson )
]
)
]
AddWindowEventListener eventName ->
( "addWindowEventListener", eventName |> Json.Encode.string )

RemoveDom ->
Json.Encode.object
[ ( "removeDom", Json.Encode.null ) ]
RemoveWindowEventListener eventName ->
( "removeWindowEventListener", eventName |> Json.Encode.string )

AddHttpRequest httpRequestId ->
Json.Encode.object
[ ( "addHttpRequest", httpRequestId |> httpRequestIdToJson ) ]
AddDocumentEventListener eventName ->
( "addDocumentEventListener", eventName |> Json.Encode.string )

RemoveDocumentEventListener eventName ->
( "removeDocumentEventListener", eventName |> Json.Encode.string )
]


{-| The "init" part for an embedded program
Expand Down Expand Up @@ -748,7 +788,7 @@ domNodeIdJsonDecoder =
interfaceDiffJsonDecoder : Json.Decode.Decoder InterfaceDiff
interfaceDiffJsonDecoder =
Json.Decode.oneOf
[ Json.Decode.map (\() -> AddRequestTimeNow)
[ Json.Decode.map (\() -> AddTimeCurrentRequest)
(Json.Decode.field "requestTimeNow" (Json.Decode.null ()))
, Json.Decode.map ReplaceDomNode
(Json.Decode.field "replaceDomNode"
Expand All @@ -767,7 +807,7 @@ eventDataAndConstructStateJsonDecoder interfaceDiff interface =
case interface of
TimeCurrentRequest requestTimeNow ->
case interfaceDiff of
AddRequestTimeNow ->
AddTimeCurrentRequest ->
Json.Decode.succeed requestTimeNow
|> Json.Decode.Extra.andMap (Json.Decode.map Time.millisToPosix Json.Decode.int)
|> Just
Expand All @@ -777,7 +817,7 @@ eventDataAndConstructStateJsonDecoder interfaceDiff interface =

TimezoneRequest requestTimezone ->
case interfaceDiff of
AddRequestTimezone ->
AddTimezoneRequest ->
Json.Decode.succeed requestTimezone
|> Json.Decode.Extra.andMap
(Json.Decode.map (\offset -> Time.customZone offset []) Json.Decode.int)
Expand All @@ -788,7 +828,7 @@ eventDataAndConstructStateJsonDecoder interfaceDiff interface =

TimezoneNameRequest requestTimezoneName ->
case interfaceDiff of
AddRequestTimezoneName ->
AddTimezoneNameRequest ->
Json.Decode.succeed requestTimezoneName
|> Json.Decode.Extra.andMap
(Json.Decode.oneOf
Expand Down Expand Up @@ -847,6 +887,30 @@ eventDataAndConstructStateJsonDecoder interfaceDiff interface =
_ ->
Nothing

WindowEventListen listener ->
case interfaceDiff of
AddWindowEventListener addedEventName ->
if addedEventName == listener.eventName then
Json.Decode.value |> Json.Decode.map listener.on |> Just

else
Nothing

_ ->
Nothing

DocumentEventListen listener ->
case interfaceDiff of
AddDocumentEventListener addedEventName ->
if addedEventName == listener.eventName then
Json.Decode.value |> Json.Decode.map listener.on |> Just

else
Nothing

_ ->
Nothing


domElementAtReversePath : List Int -> (DomNode state -> Maybe (DomNode state))
domElementAtReversePath path =
Expand Down Expand Up @@ -1203,13 +1267,17 @@ httpBodyToJson body =
{-| Individual messages to js. Also used to identify responses with the same part in the interface
-}
type InterfaceDiff
= AddRequestTimeNow
| AddRequestTimezone
| AddRequestTimezoneName
= AddTimeCurrentRequest
| AddTimezoneRequest
| AddTimezoneNameRequest
| AddConsoleLog String
| ReplaceDomNode { path : List Int, domNode : DomNodeId }
| AddHttpRequest HttpRequestId
| RemoveDom
| AddWindowEventListener String
| RemoveWindowEventListener String
| AddDocumentEventListener String
| RemoveDocumentEventListener String


{-| Create an elm [`Program`](https://dark.elm.dmy.fr/packages/elm/core/latest/Platform#Program). Short for
Expand Down
20 changes: 20 additions & 0 deletions src/BrowserApp/Document.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module BrowserApp.Document exposing (listenToEvent)

{-| Helpers for `document` interaction as part of an [`Interface`](BrowserApp#Interface)
@docs listenToEvent
@docs resizeEventJsonDecoder
-}

import BrowserApp
import Json.Decode
import Json.Decode.Extra


{-| An [`Interface`](BrowserApp#Interface) that listens for a specific `document` event
like like keypress, keydown, keyup, click, mousemove, mousedown, mouseup
-}
listenToEvent : String -> BrowserApp.Interface Json.Decode.Value
listenToEvent eventName =
BrowserApp.DocumentEventListen { eventName = eventName, on = identity }
40 changes: 40 additions & 0 deletions src/BrowserApp/Window.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module BrowserApp.Window exposing
( listenToEvent
, listenToResize
)

{-| Helpers for `window` interaction as part of an [`Interface`](BrowserApp#Interface)
@docs listenToEvent
@docs resizeEventJsonDecoder
-}

import BrowserApp
import Json.Decode
import Json.Decode.Extra


{-| An [`Interface`](BrowserApp#Interface) that listens for a specific `window` event
-}
listenToEvent : String -> BrowserApp.Interface Json.Decode.Value
listenToEvent eventName =
BrowserApp.WindowEventListen { eventName = eventName, on = identity }


{-| An [`Interface`](BrowserApp#Interface) that listens changes to the inner window width and height.
-}
listenToResize : BrowserApp.Interface (Result Json.Decode.Error { width : Int, height : Int })
listenToResize =
listenToEvent "resize"
|> BrowserApp.on
(\value ->
value
|> Json.Decode.decodeValue
(Json.Decode.field "target"
(Json.Decode.succeed (\width height -> { width = width, height = height })
|> Json.Decode.Extra.andMap (Json.Decode.field "innerWidth" Json.Decode.int)
|> Json.Decode.Extra.andMap (Json.Decode.field "innerHeight" Json.Decode.int)
)
)
)

0 comments on commit d91b3b0

Please sign in to comment.