Hi all,
I’d like to set an array index with a simple widget in Pluto via @bind
but instead of a slider I’d like to use “next” and “previous” buttons. Does anyone know how to do that?
A toy example:
### A Pluto.jl notebook ###
# v0.14.5
using Markdown
using InteractiveUtils
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
quote
local el = $(esc(element))
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
el
end
end
# ╔═╡ 1be6b7e0-b7de-11eb-0172-b739f6bd01e6
using PlutoUI
# ╔═╡ 7c65e821-4725-475c-92b3-87f35082004c
md"""
$(@bind prev_b Button("Previous"))
$(@bind next_b Button("Next"))
"""
# ╔═╡ 8091a4e7-ac52-4e0b-ab7b-8577b8fce9b3
x = Ref(0)
# ╔═╡ 535cf060-2642-4a94-851e-395bc823b253
begin
prev_b
x[] -= 1
end;
# ╔═╡ def84af6-934a-4b29-9d2e-73af9a49207f
begin
next_b
x[] += 1
end;
# ╔═╡ b1eed2b6-54c7-4725-b8b1-916c6f947c28
begin
prev_b
next_b
x[]
end
# ╔═╡ b60bdf2d-345c-4a1a-b348-74472b233d73
# ╔═╡ Cell order:
# ╠═1be6b7e0-b7de-11eb-0172-b739f6bd01e6
# ╠═7c65e821-4725-475c-92b3-87f35082004c
# ╠═8091a4e7-ac52-4e0b-ab7b-8577b8fce9b3
# ╠═535cf060-2642-4a94-851e-395bc823b253
# ╠═def84af6-934a-4b29-9d2e-73af9a49207f
# ╠═b1eed2b6-54c7-4725-b8b1-916c6f947c28
# ╠═b60bdf2d-345c-4a1a-b348-74472b233d73
That works, thanks for the quick reply! It’s surprisingly clunky though, I wonder if there is a solution which doesn’t require that many cells.
A nicer solution would be to write some JS code inside the Pluto cell so that the bind value itself already contains the target value. But my JS knowledge is very limited.
Yes, probably. But I don’t know any JS either so this will have to do for now Thanks again!
Another toy example, using JavaScript:
function Carousel(elementsList)
carouselHTML = map(elementsList) do element
@htl("""<div class="carousel-slide">
$(element)
</div>""")
end
@htl("""
<div>
<style>
.carousel-box{
width: 100%;
overflow: hidden;
}
.carousel-container{
top: 0;
left: 0;
display: flex;
width: 100%;
flex-flow: row nowrap;
transform: translate(0px, 0px);
transition: transform 700ms;
}
.carousel-controls{
display: flex;
justify-content: center;
align-items: center;
}
.carousel-controls button{
margin: 8px;
width: 7em;
}
.carousel-slide {
min-width: 100%;
}
</style>
<script>
const div = currentScript.parentElement
const buttons = div.querySelectorAll("button")
let count = 0
const onclick = (e) => {
const max = $(length(elementsList))
count = (count + parseInt(e.target.dataset.value)) % max;
count = count < 0 ? max - 1 : count;
div.querySelector(".carousel-container").style = `transform: translate(\${-count*100}%, 0px)`;
div.value = count
div.dispatchEvent(new CustomEvent("input"))
e.preventDefault()
}
buttons.forEach(button => button.addEventListener("click", onclick))
div.value = count
</script>
<div class="carousel-box">
<div class="carousel-container">
$(carouselHTML)
</div>
</div>
<div class="carousel-controls">
<button data-value="-1">Previous</button>
<button data-value="1">Next</button>
</div>
</div>
""")
end
Then you can pass an array of pluto-showables and bind the index of the visible one!
@bind index Carousel([plot(sqrt.(1:1000)), plot(sqrt.(1:1000)), @htl("<div>test</div>"), md"> wow"])
Coming soon to a package near you (PlutoUI)!
4 Likes
That’s perfect, thanks! Looking forward to using this in PlutoUI.