nanoc

Next and Prev buttons for pages in the same folder in nanoc


I'm building a static generator for a getting started guide and I am grouping pages for the same guide into a folder.

how would I add a footer to the bottom of the generated pages for next and previous pages?


Solution

  • Pages (“items“) in Nanoc do not have an intrinsic ordering, and there is thus no concept of “previous” and “next” unless you add one. There are ways to introduce an ordering to a list of items, though:

    1. Sort the items by title (@items.sort_by { |i| i[:title] })
    2. Sort the items by an ordering attribute (@items.sort_by { |i| i.fetch(:order, 0) })
    3. Define an ordering in a configuration file (this is how nanoc.ws does it)

    Once there is an ordering defined for a collection of items, you can use #index to find out the index of the current page, and thus also the indices of the previous and next pages. For example, assuming that ordered_items is the list of ordered items:

    def previous_item(item)
      idx = ordered_items.index(item)
      if idx && idx > 0
        ordered_items[idx - 1]
      end
    end
    

    A #next_item function would be similar.

    How the #ordered_items function is implemented, depends on what you would like to achieve. Do you want to order all items on the site? If so, an implementation could look like this:

    def ordered_items
      @items.select { |i| i.key?(:order) }.sort_by { |i| i[:order] }
    end
    

    If, on the other hand, you’d prefer the ordered items to be scoped to a certain section of the site, you could have an implementation like

    def ordered_items_for_manual
      @items.find_all('/manual/**/*')
        .select { |i| i.key?(:order) }
        .sort_by { |i| i[:order] }
    end
    

    With the ChildParent helper, you could determine the ordered items for the current section automatically:

    def ordered_siblings_and_self
      children_of(parent_of(@item))
        .select { |i| i.key?(:order) }
        .sort_by { |i| i[:order] }
    end
    

    Finally, in the footer (e.g. in the default layout), you could stick something like

    <% if previous_item %>
      <%= link_to(previous_item[:title], previous_item) %>
    <% end %>
    

    and then you’d have a link to the previous page. Similar for the next link.