Last active
September 7, 2022 15:33
-
-
Save rupertlssmith/13f48be67a7d1f1fed0cd480ec1e1f66 to your computer and use it in GitHub Desktop.
FFI in Elm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main exposing (main) | |
import Browser | |
import Html exposing (Html) | |
import Html.Events | |
import Json.Decode | |
import Json.Encode | |
type alias Flags = | |
Json.Decode.Value | |
type alias Model = | |
{ proxy : Json.Decode.Value } | |
type Msg | |
= Refresh | |
main : Program Flags Model Msg | |
main = | |
Browser.element | |
{ init = \f -> ( { proxy = f }, Cmd.none ) | |
, update = \_ m -> ( m, Cmd.none ) | |
, subscriptions = \_ -> Sub.none | |
, view = view | |
} | |
ffiCall : Json.Decode.Value -> String -> Json.Decode.Decoder a -> Maybe a | |
ffiCall proxy key expecting = | |
proxy | |
|> Json.Decode.decodeValue (Json.Decode.field key expecting) | |
|> Result.toMaybe | |
view : Model -> Html Msg | |
view model = | |
Html.button | |
[ Html.Events.onClick Refresh ] | |
[ Html.text | |
(Debug.toString | |
(List.repeat 5 () | |
|> List.filterMap (\_ -> ffiCall model.proxy "randomInt" Json.Decode.int) | |
) | |
) | |
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<style> | |
/* you can style your program here */ | |
</style> | |
</head> | |
<body> | |
<main></main> | |
<script> | |
var proxy = new Proxy({ | |
randomInt() { | |
return Math.round(Math.random() * 10000); | |
} | |
}, { | |
get(target, property) { | |
if (typeof target[property] == 'function') { | |
return target[property](); | |
} else { | |
return target[property]; | |
} | |
} | |
}); | |
var app = Elm.Main.init({ | |
node: document.querySelector('main'), | |
flags: proxy, | |
}) | |
// you can use ports and stuff here | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Interop exposing (..) | |
import Json.Decode as Decode | |
import Json.Encode as Encode | |
import Random | |
import Time | |
type alias Interop = | |
{ windowSize : () -> (Int, Int) | |
, seed : () -> Random.Seed | |
, querySelector : String -> List Decode.Value | |
, now : () -> Time.Posix | |
, isLocalhost : () -> Bool | |
} | |
interop : Interop | |
interop = | |
let | |
interface = | |
Encode.object [] | |
in | |
{ windowSize = \_ -> | |
Decode.decodeValue | |
(Decode.at ["__elm_interop__", "__windowSize__"] (Decode.map2 Tuple.pair (Decode.index 0 Decode.int) (Decode.index 1 Decode.int))) | |
interface | |
|> Result.withDefault (0, 0) | |
, isLocalhost = \_ -> | |
Decode.decodeValue | |
(Decode.at ["__elm_interop__", "window", "location", "host"] Decode.string | |
|> Decode.map (String.contains "localhost") | |
) | |
interface | |
|> Result.withDefault False | |
, seed = \_ -> | |
Decode.decodeValue | |
(Decode.at ["__elm_interop__", "__seed__"] (Decode.int)) | |
interface | |
|> Result.withDefault 0 | |
|> Random.initialSeed | |
, now = \_ -> | |
Decode.decodeValue | |
(Decode.at ["__elm_interop__", "__now__"] (Decode.int)) | |
interface | |
|> Result.withDefault 0 | |
|> Time.millisToPosix | |
, querySelector = \selector -> | |
Decode.decodeValue | |
(querySelector selector) | |
interface | |
|> Result.withDefault [] | |
} | |
querySelector selector = (Decode.at ["__elm_interop__", "__querySelectorAll__"] (Decode.field selector (Decode.list Decode.value))) | |
computedStyle = Decode.at ["__elm_interop__", "__getComputedStyle__"] (Decode.dict Decode.string) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const domMonkeyPatches = () => { | |
// prevent files from opening when dropping them in the browser | |
window.addEventListener("dragover", (ev) => ev.preventDefault()); | |
window.addEventListener("drop", (ev) => ev.preventDefault()); | |
// preven safari pinch-to-zoom default behaviors | |
window.addEventListener("gesturestart", (ev) => ev.preventDefault()); | |
window.addEventListener("gesturechange", (e) => e.preventDefault()); | |
window.addEventListener("gestureend", (e) => e.preventDefault()); | |
// monkey-patch HTML so that elm can read properties from HTML elements | |
HTMLElement.prototype.__defineGetter__("boundingClientRect", function () { | |
return this.getBoundingClientRect(); | |
}); | |
}; | |
domMonkeyPatches(); | |
const initSyncInterop = () => { | |
Object.defineProperty({}.__proto__, "__elm_interop__", { | |
get() { | |
const ctx = this; | |
return { | |
get __windowSize__() { | |
return [window.innerWidth, window.innerHeight]; | |
}, | |
get __window__() { | |
return window; | |
}, | |
get __seed__() { | |
return randomNumber(); | |
}, | |
get __now__() { | |
return Date.now(); | |
}, | |
get __querySelectorAll__() { | |
return querySelectorProxy(ctx); | |
}, | |
get __getComputedStyle__() { | |
return window.getComputedStyle(ctx); | |
}, | |
}; | |
}, | |
}); | |
}; | |
const querySelectorProxy = (ctx) => | |
new Proxy( | |
{}, | |
{ | |
get: (obj, key) => { | |
return Array.from( | |
(ctx.querySelectorAll && ctx.querySelectorAll(key)) || | |
document.querySelectorAll(key) | |
); | |
}, | |
has: (_, key) => { | |
return true; | |
}, | |
} | |
); | |
initSyncInterop(); | |
export const randomNumber = () => crypto.getRandomValues(new Uint32Array(1))[0]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment