Using Nokogiri to generate static header list in Slate/Middleman

521 views Asked by At

I'm inexperienced with middleman and ruby, but I've been trying to get Slate working so it generates a side navigation/list of header during build time instead of client side using javascript. The problem I am running into is getting the code to include the headers from partials.

An example of the directory structure:

Source
+--config.rb
+--includes
  +--file.md
  +--otherfile.md
+--index.html
+--layouts
  +--layout.erb

Gist of layout and config.rb

Config.rb snippet for this:

require 'nokogiri'

helpers do   
def toc_data(page_content)
    html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content)

    # get a flat list of headers
    headers = []
    html_doc.css('h1, h2, h3').each do |header|
      headers.push({
        id: header.attribute('id').to_s,
        content: header.content,
        level: header.name[1].to_i,
        children: []
      })
    end

    [3,2].each do |header_level|
      header_to_nest = nil
      headers = headers.reject do |header|
        if header[:level] == header_level
          header_to_nest[:children].push header if header_to_nest
          true
        else
          header_to_nest = header if header[:level] == (header_level - 1)
          false
        end
      end
    end
    headers   
  end
end

Layout snippet for this:

<ul id="toc" class="toc">
    <% toc_data(page_content).each do |h1| %>
      <li>
        <a href="#<%= h1[:id] %>" class="toc-h1"><%= h1[:content] %></a>
          <ul class="toc-section">
            <% h1[:children].each do |h2| %>
              <li>
                <a href="#<%= h2[:id] %>" class="toc-h2"><%= h2[:content] %></a>
                <ul class="toc-submenu">
                  <% h2[:children].each do |h3| %>
                    <li>
                      <a href="#<%= h3[:id] %>" class="toc-h3"><%= h3[:content] %></a>
                    </li>
                  <% end %>
                </ul>
              </li>
            <% end %>
          </ul>
      </li>
    <% end %>
  </ul>
...
    <div class="page-wrapper">
  <div class="content">
    <%= page_content %>
    <% current_page.data.includes && current_page.data.includes.each do |include| %>
      <%= partial "includes/#{include}" %>
    <% end %>
  </div>
</div>

Currently, only headers from the index.html file are getting populated and nothing from the included partials. I believe I may need the existing helper to occur post build similar to what is described in the Middleman docs for sitemaps using a ready helper. I believe I have to make another change to the config code so that it captures additional content outside of the page_content, but I'm not sure what that is due to lack of familiarity. Any pointers would be appreciated.

Edit: After looking into the middleman basics docs, there appear to be two helpers from the Padrino framework that I could make use of: capture_html and concat_content. I'm trying to find where the helper page_content is defined to get extra context for the specific changes I'm making.

2

There are 2 answers

0
Alyss On BEST ANSWER

In order to concatenate the current page data with partials with the page_content, use the code below. This also changes what all is needed to yield a complete page.

<%
  if current_page.data.includes
    current_page.data.includes.each do |include|
      page_content += partial("includes/#{include}")
    end
  end
%>
...
<%= page_content %>
2
akuhn On

Not familiar with that framework but looks like toc_data(page_content) only looks at the main content but not at the current_page.data.includes partials.

So guess you need to pass the partial to your toc_data function as well.

Maybe this works?

<%
  full_content = page_content
  current_page.data.includes && current_page.data.includes.each do |include|  
    full_content += partial("includes/#{include}")
  end 
  toc_data(full_content).each do |h1| 
%>
  ...
<% end %>

Hope that helps.