Plot recipes with nested subplots

Hi. I have a custom data type which, after doing some calculations on, I would like to plot in a specific way. A basic example of the setup is this:

struct Data
    x
    y1s
    y2s
end

x = 1:10
y1s = [[rand(10) for _ in 1:5] for _ in 1:3]
y2s = [[rand(10) for _ in 1:5] for _ in 1:3]

data = Data(x, y1s, y2s)

What I would like is an nxm grid of subplots, where each subplot has its own title, and each row has a title for that entire row. Below is an mspaint of what I want:

I can get pretty close with nested plots, but I have two problems:

  1. This requires my package to have a dependency on Plots, which I would like to avoid.
  2. This method also causes the legend to contain duplicate labels, one for each individual subplot.

This is my attempt with Plots:

using Plots
plotly()

titles = ["Title $k" for k in 1:5]
supertitles = ["SuperTitle $k" for k in 1:3]

ps_all = []
for k in 1:3
    ps = []
    for i in 1:5
        # Process the signals in some way
        y1 = data.y1s[k][i] .+ k
        y2 = data.y2s[k][i] .+ k
        # Create a single plot of of one x vs 2 ys, add a title to each
        push!(ps, plot(data.x, [y1, y2], title=titles[i], lab=["signal1" "signal2"]))
    end
    # Plot all three x vs ys plots in one row, add a super title
    push!(ps_all, plot(ps..., plot_title=supertitles[k], layout=(1, 5), size=(250*3, 250)))
end
# Plot all five rows of three plots, each row with its own super title
plot(ps_all..., layout=(3, 1), size=(250*5, 250*3), legend=:outertopright)

To solve (1), I tried doing the same thing with RecipesBase. I am not very familiar with this, so I’m not really sure what I’m supposed to be doing. In any event, the legend is not placed where I want it, the plot_title element is squashed together with the regular titles, and I cannot plot all the rows together - they all end up sharing subplots on one row.

using RecipesBase

titles = ["Title $k" for k in 1:5]
supertitles = ["SuperTitle $k" for k in 1:3]

# Create (user?) recipe for a single row
@recipe function f(data::Data, k::Integer)
    ncol = length(data.y1s[k])
    layout := (1, ncol)
    size := (250*ncol, 250)
    plot_title := supertitles[k]
    legend := :outertopright

    for i in 1:ncol
        @series begin
            subplot := i
            title := titles[i]
            labels := ["signal1" "signal2"]
            y1 = data.y1s[k][i] .+ offsets[k]
            y2 = data.y2s[k][i] .+ offsets[k]
            data.x, [y1, y2]
        end
    end
end

# Try plotting single row
plot(data, 1)
# Problem: plot_title is squashed together with the regular titles, and the legend is not placed in the outertopright

# Create (user?) recipe for all the rows
@recipe function f(data::Data)
    nrow = length(data.y1s)
    ncol = length(data.y1s[1])
    layout := (nrow, 1)
    size := (250*ncol, 250*nrow)
    for k in 1:nrow
        @series begin
            subplot := k
            data, k
        end
    end
end
# Try plotting all rows
plot(data)

I don’t know how to solve (2). Does anyone know how to solve these problems, or what I should be doing instead to get the result I want?