It’s easy to create a Kino.frame and render some status text like “Loading” or “Idle”. Ideally I’d like to disable the submit button in my form and display a spinner inside it.
I’m wondering what elixirforum friendly folks do in their Livebooks!
It’s easy to create a Kino.frame and render some status text like “Loading” or “Idle”. Ideally I’d like to disable the submit button in my form and display a spinner inside it.
I’m wondering what elixirforum friendly folks do in their Livebooks!
Hey, looks like you’re really lucky: Elixir Blog Posts - #1129 by lccezinha
I’m not sure - that blog post was written for Liveview. Is it applicable to Livebook?
Oh, you’re right… I think you could do something similar with Kino.HTML?
Yeah, I think Kino.HTML might be it. I could create a reusable spinner component, then render it into a Frame whenever there is some async task.
There is a little bit of prior art in the form of hex.pm/packages/kino_progress_bar.
Even if it doesn’t meet your needs, it may serve as inspiration for a new solution.
You can build your own spinner UI component based on Kino.HTML like that:
defmodule KinoSpinner do
def new(dimensions \\ "30px") do
Kino.HTML.new("""
<div class="loader"></div>
<style>
.loader {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: #{dimensions};
height: #{dimensions};
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
""")
end
end
And use like this in a form:
form =
Kino.Control.form(
[
name: Kino.Input.text("Data", default: "some data to process")
],
submit: "Submit"
)
output_frame = frame()
Kino.listen(form, fn _event ->
Kino.Frame.render(output_frame, grid([text("Processing..."), KinoSpinner.new()]))
Process.sleep(2_000)
Kino.Frame.render(output_frame, "Processing is done. ✅")
end)
grid([form, output_frame])
Here’s a quick video demo: CleanShot 2024-09-18 at 13.47.38 · CleanShot Cloud
Or you can use a custom kino from the community, like this one from Andrés Alejos:
form =
Kino.Control.form(
[
name: Kino.Input.text("Data", default: "some data to process")
],
submit: "Submit"
)
output_frame = frame()
Kino.listen(form, fn _event ->
progress_bar = KinoProgressBar.new(max: 100)
Kino.Frame.render(output_frame, progress_bar)
Enum.each(1..100, fn index ->
KinoProgressBar.update(progress_bar, index, 100)
Process.sleep(50)
end)
Kino.Frame.render(output_frame, "Processing is done. ✅")
end)
grid([form, output_frame])
Here’s a quick video demo: CleanShot 2024-09-18 at 13.49.15 · CleanShot Cloud
Here’s the notebook with both demos: How to display progress using Kino · GitHub
Wow, thanks for the high effort post Hugo!
~btw I think you meant to write Kino.Frame.new() instead of frame()? Where does frame() come from?~
Nevermind, import Kino.Shorts
is the answer.
It seems that grid() breaks the <enter>
key behavior in the form’s text input. Where should I report that?
You can report on Issues · livebook-dev/livebook · GitHub