"Next" and "Previous" Buttons in Pluto

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 :smiley: 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.