概要
wsl(wsl2じゃない)で、elixirやってみた。
Livebookの、kino.jsを調査してみた。
setup
Mix.install([
{:kino, "~> 0.15.0"}
])
サンプルコード
defmodule KinoGuide.HTML do
use Kino.JS
def new(html) when is_binary(html) do
Kino.JS.new(__MODULE__, html)
end
asset "main.js" do
"""
export function init(ctx, html) {
ctx.root.innerHTML = html;
}
"""
end
end
KinoGuide.HTML.new("""
<h3>Look!</h3>
<p>I wrote this HTML from <strong>Kino</strong>!</p>
""")
defmodule KinoGuide.Counter do
use Kino.JS
use Kino.JS.Live
def new(count) do
Kino.JS.Live.new(__MODULE__, count)
end
def bump(kino) do
Kino.JS.Live.cast(kino, :bump)
end
@impl true
def init(count, ctx) do
{:ok, assign(ctx, count: count)}
end
@impl true
def handle_connect(ctx) do
{:ok, ctx.assigns.count, ctx}
end
@impl true
def handle_cast(:bump, ctx) do
{:noreply, bump_count(ctx)}
end
@impl true
def handle_event("bump", _, ctx) do
IO.puts("ok")
{:noreply, bump_count(ctx)}
end
defp bump_count(ctx) do
ctx = update(ctx, :count, &(&1 + 1))
broadcast_event(ctx, "update", ctx.assigns.count)
ctx
end
asset "main.js" do
"""
export function init(ctx, count) {
ctx.root.innerHTML = `
<div id="count"></div>
<button id="bump">Bump</button>
`;
const countEl = document.getElementById("count");
const bumpEl = document.getElementById("bump");
countEl.innerHTML = count;
ctx.handleEvent("update", (count) => {
countEl.innerHTML = count;
});
bumpEl.addEventListener("click", (event) => {
ctx.pushEvent("bump");
});
}
"""
end
end
counter = KinoGuide.Counter.new(0)
KinoGuide.Counter.bump(counter)
defmodule KinoDocs.LiveHTML do
use Kino.JS
use Kino.JS.Live
def new(html) do
Kino.JS.Live.new(__MODULE__, html)
end
def replace(kino, html) do
Kino.JS.Live.cast(kino, {:replace, html})
end
@impl true
def init(html, ctx) do
{:ok, assign(ctx, html: html)}
end
@impl true
def handle_connect(ctx) do
{:ok, ctx.assigns.html, ctx}
end
@impl true
def handle_cast({:replace, html}, ctx) do
broadcast_event(ctx, "replace", html)
{:noreply, assign(ctx, html: html)}
end
asset "main.js" do
"""
export function init(ctx, html) {
ctx.root.innerHTML = html;
ctx.handleEvent("replace", (html) => {
ctx.root.innerHTML = html;
});
}
"""
end
end
list = KinoDocs.LiveHTML.new("""
<h1>Hello</h1>
""")
KinoDocs.LiveHTML.replace(list, """
<h2 style="color: red">World</h2>
""")
defmodule KinoCustom.Bar do
use Kino.JS
use Kino.JS.Live
def new(width) do
Kino.JS.Live.new(__MODULE__, width)
end
def update(kino, width) do
Kino.JS.Live.cast(kino, {:update, width})
end
@impl true
def init(html, ctx) do
{:ok, assign(ctx, html: html)}
end
@impl true
def handle_connect(ctx) do
{:ok, ctx.assigns.html, ctx}
end
@impl true
def handle_cast({:update, width}, ctx) do
broadcast_event(ctx, "update", width)
{:noreply, assign(ctx, width: width)}
end
asset "main.js" do
"""
export function init(ctx, width) {
const bar = document.createElement("div");
bar.className = "bar";
bar.style.width = width;
bar.style.height = "40px";
bar.style.backgroundColor = "aqua";
ctx.root.appendChild(bar);
ctx.handleEvent("update", (width) => {
bar.style.width = width
});
}
"""
end
end
bar = KinoCustom.Bar.new("50%")
Stream.interval(250)
|> Stream.take(100)
|> Kino.animate(fn width ->
KinoCustom.Bar.update(bar, "#{width}%")
end)
elixirからjs呼び出し
KinoCustom.Bar.update(bar, "#{width}%")
jsからelixir呼び出し
ctx.pushEvent("bump");
以上。