mirror of
https://github.com/snachodog/just-the-docs.git
synced 2025-09-13 05:13:33 -06:00
Feature: Allow unlimited multi-level navigation (#1431)
* Allow unlimited multi-level navigation This PR supersedes #462. The only user-level difference from #462 is that disambiguation of parent pages has to use either `grand_parent` or `ancestor` titles: the somewhat unnatural `section_id` and `in_section` fields are not supported. The implementation has been significantly simplified by the changes introduced in v0.7.0 of the theme. * Detect cyclic parenthood A page should not have a parent or ancestor with the same title. If it does, the location of the repeated link is marked by ∞, to facilitate debugging the navigation (and an unbounded loop leading to a build exception is avoided). * Add nav_error_report warning in main navigation When activated by `nav_error_report: true` in `_config.yml`, displays warnings about pages with the same title as their parent page or an ancestral page. * Cache site-nav with links to all pages The extra cached site-nav is used for determining breadcrumbs and children navigation, which may involve pages that are excluded from the main navigation. * Replace code for determining children by inclusion of components/nav/children.html * Update CHANGELOG.md --------- Co-authored-by: Matt Wang <matt@matthewwang.me>
This commit is contained in:
@@ -1,144 +1,96 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include components/breadcrumbs.html -%}
|
||||
Depends on: page, site.
|
||||
Includes: components/site_nav.html.
|
||||
Results in: HTML for the breadcrumbs component.
|
||||
Overwrites:
|
||||
node, pages_list, parent_page, grandparent_page.
|
||||
nav_list_link, site_nav, nav_list_simple, nav_list_link_class, nav_category,
|
||||
nav_anchor_splits, nav_breadcrumbs, nav_split, nav_split_next, nav_split_test,
|
||||
nav_breadcrumb_link, nav_list_end_less, nav_list_end_count, nav_end_index, nav_breadcrumb.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- if page.url != "/" and page.parent -%}
|
||||
{%- if page.url != "/" and page.parent and page.title -%}
|
||||
|
||||
{%- capture nav_list_link -%}
|
||||
<a href="{{ page.url | relative_url }}" class="nav-list-link">
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture site_nav -%}
|
||||
{%- include_cached components/site_nav.html -%}
|
||||
{%- include_cached components/site_nav.html all=true -%}
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- if site_nav contains nav_list_link -%}
|
||||
{%- capture nav_list_simple -%}
|
||||
<ul class="nav-list">
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture nav_list_simple -%}
|
||||
<ul class="nav-list">
|
||||
{%- endcapture -%}
|
||||
{%- capture nav_list_link_class %} class="nav-list-link">
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture nav_list_link_class %} class="nav-list-link">
|
||||
{%- endcapture -%}
|
||||
{%- capture nav_category -%}
|
||||
<div class="nav-category">
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture nav_category -%}
|
||||
<div class="nav-category">
|
||||
{%- endcapture -%}
|
||||
{%- assign nav_anchor_splits =
|
||||
site_nav | split: nav_list_link |
|
||||
first | split: nav_category |
|
||||
last | split: "</a>" -%}
|
||||
|
||||
{%- assign nav_anchor_splits =
|
||||
site_nav | split: nav_list_link |
|
||||
first | split: nav_category |
|
||||
last | split: "</a>" -%}
|
||||
|
||||
{%- comment -%}
|
||||
The ordinary pages (if any) and the collections pages (if any) are separated by
|
||||
occurrences of nav_category.
|
||||
|
||||
Any ancestor nav-links of the page are contained in the last group of pages,
|
||||
immediately preceding nav-lists. After splitting at "</a>", the anchor that
|
||||
was split is a potential ancestor link when the following split starts with
|
||||
a nav-list.
|
||||
|
||||
The array nav_breadcrumbs is the stack of current potential ancestors of the
|
||||
current page. A split that contains one or more "</ul>"s requires that number
|
||||
of potential ancestors to be popped from the stack.
|
||||
|
||||
The number of occurrences of a string in nav_split_next is computed by removing
|
||||
them all, then dividing the resulting size difference by the length of the string.
|
||||
{%- endcomment %}
|
||||
|
||||
{%- assign nav_breadcrumbs = "" | split: "" -%}
|
||||
|
||||
{%- for nav_split in nav_anchor_splits -%}
|
||||
{%- unless forloop.last -%}
|
||||
|
||||
{%- assign nav_split_next = nav_anchor_splits[forloop.index] | strip -%}
|
||||
|
||||
{%- assign nav_split_test =
|
||||
nav_split_next | remove_first: nav_list_simple | prepend: nav_list_simple -%}
|
||||
{%- if nav_split_test == nav_split_next -%}
|
||||
{%- assign nav_breadcrumb_link =
|
||||
nav_split | split: "<a " | last | prepend: "<a " |
|
||||
replace: nav_list_link_class, ">" | append: "</a>" -%}
|
||||
{%- assign nav_breadcrumbs = nav_breadcrumbs | push: nav_breadcrumb_link -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if nav_split_next contains "</ul>" -%}
|
||||
{%- assign nav_list_end_less = nav_split_next | remove: "</ul>" -%}
|
||||
{%- assign nav_list_end_count =
|
||||
nav_split_next.size | minus: nav_list_end_less.size | divided_by: 5 -%}
|
||||
{% for nav_end_index in (1..nav_list_end_count) %}
|
||||
{%- assign nav_breadcrumbs = nav_breadcrumbs | pop -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- endunless -%}
|
||||
{%- endfor -%}
|
||||
|
||||
{%- assign nav_parent_link = nav_breadcrumbs[-1] -%}
|
||||
{%- assign nav_grandparent_link = nav_breadcrumbs[-2] -%}
|
||||
|
||||
{%- else -%}
|
||||
|
||||
{%- comment -%}
|
||||
Pages whose links are excluded from the main navigation may still have
|
||||
breadcrumbs. Determining them appears to require inspecting the front matter
|
||||
of all the pages in the same group. For sites with 100s of pages, this is too
|
||||
inefficient in Jekyll 3 (also when the for-loop is replaced by where-filters).
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign pages_list = site[page.collection] | default: site.html_pages -%}
|
||||
{%- comment -%}
|
||||
The ordinary pages (if any) and the collections pages (if any) are separated by
|
||||
occurrences of nav_category.
|
||||
|
||||
{%- assign parent_page = nil -%}
|
||||
{%- assign grandparent_page = nil -%}
|
||||
|
||||
{%- for node in pages_list -%}
|
||||
Any ancestor nav-links of the page are contained in the last group of pages,
|
||||
immediately preceding nav-lists. After splitting at "</a>", the anchor that
|
||||
was split is a potential ancestor link when the following split starts with
|
||||
a nav-list.
|
||||
|
||||
{%- if node.has_children and page.grand_parent -%}
|
||||
|
||||
{%- if node.title == page.parent and node.parent == page.grand_parent -%}
|
||||
{%- assign parent_page = node -%}
|
||||
{%- endif -%}
|
||||
{%- if node.title == page.grand_parent -%}
|
||||
{%- assign grandparent_page = node -%}
|
||||
{%- endif -%}
|
||||
{%- if parent_page and grandparent_page -%}
|
||||
{%- break -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- elsif node.has_children and node.title == page.parent and node.parent == nil -%}
|
||||
|
||||
{%- assign parent_page = node -%}
|
||||
{%- break -%}
|
||||
|
||||
{%- endif -%}
|
||||
|
||||
{%- endfor -%}
|
||||
The array nav_breadcrumbs is the stack of current potential ancestors of the
|
||||
current page. A split that contains one or more "</ul>"s requires that number
|
||||
of potential ancestors to be popped from the stack.
|
||||
|
||||
{%- capture nav_parent_link -%}
|
||||
<a href="{{ parent_page.url | relative_url }}">{{ page.parent }}</a>
|
||||
{%- endcapture -%}
|
||||
The number of occurrences of a string in nav_split_next is computed by removing
|
||||
them all, then dividing the resulting size difference by the length of the string.
|
||||
{%- endcomment %}
|
||||
|
||||
{%- if page.grand_parent %}
|
||||
{%- capture nav_grandparent_link -%}
|
||||
<a href="{{ grandparent_page.url | relative_url }}">{{ page.grand_parent }}</a>
|
||||
{%- endcapture -%}
|
||||
{%- endif -%}
|
||||
{%- assign nav_breadcrumbs = "" | split: "" -%}
|
||||
|
||||
{%- for nav_split in nav_anchor_splits -%}
|
||||
{%- unless forloop.last -%}
|
||||
|
||||
{%- assign nav_split_next = nav_anchor_splits[forloop.index] | strip -%}
|
||||
|
||||
{%- assign nav_split_test =
|
||||
nav_split_next | remove_first: nav_list_simple | prepend: nav_list_simple -%}
|
||||
{%- if nav_split_test == nav_split_next -%}
|
||||
{%- assign nav_breadcrumb_link =
|
||||
nav_split | split: "<a " | last | prepend: "<a " |
|
||||
replace: nav_list_link_class, ">" | append: "</a>" -%}
|
||||
{%- assign nav_breadcrumbs = nav_breadcrumbs | push: nav_breadcrumb_link -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if nav_split_next contains "</ul>" -%}
|
||||
{%- assign nav_list_end_less = nav_split_next | remove: "</ul>" -%}
|
||||
{%- assign nav_list_end_count =
|
||||
nav_split_next.size | minus: nav_list_end_less.size | divided_by: 5 -%}
|
||||
{% for nav_end_index in (1..nav_list_end_count) %}
|
||||
{%- assign nav_breadcrumbs = nav_breadcrumbs | pop -%}
|
||||
{%- endfor -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- endunless -%}
|
||||
{%- endfor -%}
|
||||
|
||||
<nav aria-label="Breadcrumb" class="breadcrumb-nav">
|
||||
<ol class="breadcrumb-nav-list">
|
||||
{%- if nav_grandparent_link %}
|
||||
<li class="breadcrumb-nav-list-item">{{ nav_grandparent_link }}</li>
|
||||
{%- endif %}
|
||||
<li class="breadcrumb-nav-list-item">{{ nav_parent_link }}</li>
|
||||
{%- for nav_breadcrumb in nav_breadcrumbs %}
|
||||
<li class="breadcrumb-nav-list-item">{{ nav_breadcrumb }}</li>
|
||||
{%- endfor %}
|
||||
<li class="breadcrumb-nav-list-item"><span>{{ page.title }}</span></li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
{% if site.nav_error_report %}
|
||||
{{ nav_error_report }}
|
||||
{% endif %}
|
||||
|
||||
{%- endif -%}
|
||||
|
Reference in New Issue
Block a user