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 -%}
|
||||
|
@@ -1,33 +1,89 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include components/children_nav.html -%}
|
||||
Depends on: page, site.
|
||||
Depends on: page, site, nav_breadcrumbs.
|
||||
Results in: HTML for the children-navigation component.
|
||||
Includes:
|
||||
sorted_pages.html
|
||||
toc_heading_custom.html
|
||||
Includes: components/nav/sorted.html, toc_heading_custom.html.
|
||||
Overwrites:
|
||||
child_pages.
|
||||
nav_ancestor_links, nav_top_node_titles, nav_child_candidates, nav_children,
|
||||
nav_child, nav_child_ok, nav_child_ancestor, nav_sorted.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- if page.has_children == true and page.has_toc != false -%}
|
||||
{%- assign child_pages = site[page.collection]
|
||||
| default: site.html_pages
|
||||
| where: "parent", page.title
|
||||
| where: "grand_parent", page.parent -%}
|
||||
{%- comment -%}
|
||||
Whether a page has any children is checked efficiently by inspecting the cached
|
||||
site_nav. If the page has no children, nav_children is set to an empty array;
|
||||
otherwise nav_children is left unset.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- include sorted_pages.html pages = child_pages -%}
|
||||
{%- if page.has_children == false -%}
|
||||
{%- assign nav_children = "" | split: "" -%}
|
||||
{%- else -%}
|
||||
|
||||
{%- assign nav_children = nil -%}
|
||||
|
||||
{%- capture nav_list_link -%}
|
||||
<a href="{{ page.url | relative_url }}" class="nav-list-link">
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture site_nav -%}
|
||||
{%- include_cached components/site_nav.html all=true -%}
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- capture nav_list_simple -%}
|
||||
<ul class="nav-list">
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- assign nav_child_start = site_nav
|
||||
| split: nav_list_link | last
|
||||
| split: "</a>" | slice: 1 | first -%}
|
||||
|
||||
{%- assign nav_child_test = nav_child_start
|
||||
| remove_first: nav_list_simple | prepend: nav_list_simple -%}
|
||||
|
||||
{%- if nav_child_start != nav_child_test -%}
|
||||
{%- assign nav_children = "" | split: "" -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- endif -%}
|
||||
|
||||
{%- unless nav_children -%}
|
||||
|
||||
{%- comment -%}
|
||||
The layout is assumed to include components/breadcrumbs.html before this file,
|
||||
otherwise it needs to be included here.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign nav_ancestors = "" | split: "" -%}
|
||||
{%- for nav_link in nav_breadcrumbs -%}
|
||||
{%- assign nav_title = nav_link | split: ">" | slice: 1 | first | append: ">" | remove: "</a>" -%}
|
||||
{%- assign nav_ancestors = nav_ancestors | push: nav_title -%}
|
||||
{%- endfor -%}
|
||||
|
||||
{%- assign nav_parenthood = site[page.collection] | default: site.html_pages
|
||||
| where_exp: "item", "item.title != nil" | group_by: "parent" -%}
|
||||
|
||||
{%- assign nav_top_nodes = nav_parenthood
|
||||
| where_exp: "item", "item.name == ''" | map: "items" | first -%}
|
||||
|
||||
{% assign nav_top_node_titles = nav_top_nodes | map: "title" -%}
|
||||
|
||||
{%- include components/nav/children.html node=page ancestors=nav_ancestors all=true -%}
|
||||
|
||||
{%- endunless -%}
|
||||
|
||||
{%- if nav_children.size >= 1 -%}
|
||||
|
||||
{%- if page.child_nav_order == 'desc' or page.child_nav_order == 'reversed' -%}
|
||||
{%- assign sorted_pages = sorted_pages | reverse -%}
|
||||
{%- assign nav_children = nav_children | reverse -%}
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
|
||||
<hr>
|
||||
{% include toc_heading_custom.html %}
|
||||
<ul>
|
||||
{% for child in sorted_pages %}
|
||||
{% for nav_child in nav_children %}
|
||||
<li>
|
||||
<a href="{{ child.url | relative_url }}">{{ child.title }}</a>{% if child.summary %} - {{ child.summary }}{% endif %}
|
||||
<a href="{{ nav_child.url | relative_url }}">{{ nav_child.title }}</a>{% if nav_child.summary %} - {{ nav_child.summary }}{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{%- endif -%}
|
||||
|
@@ -1,75 +0,0 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include components/nav.html pages=pages -%}
|
||||
Depends on: include.pages.
|
||||
Results in: HTML for the navigation panel.
|
||||
Includes:
|
||||
sorted_pages.html
|
||||
Overwrites:
|
||||
nav_pages, first_level_pages, second_level_pages, third_level_pages,
|
||||
node, children_list, child, grand_children_list, grand_child.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign nav_pages = include.pages
|
||||
| where_exp: "item", "item.title != nil"
|
||||
| where_exp: "item", "item.nav_exclude != true" -%}
|
||||
|
||||
{%- include sorted_pages.html pages = nav_pages -%}
|
||||
|
||||
{%- comment -%}
|
||||
It might be more efficient to sort the pages at each level separately.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign first_level_pages = sorted_pages
|
||||
| where_exp: "item", "item.parent == nil" -%}
|
||||
{%- assign second_level_pages = sorted_pages
|
||||
| where_exp: "item", "item.parent != nil"
|
||||
| where_exp: "item", "item.grand_parent == nil" -%}
|
||||
{%- assign third_level_pages = sorted_pages
|
||||
| where_exp: "item", "item.grand_parent != nil" -%}
|
||||
|
||||
<ul class="nav-list">
|
||||
{%- for node in first_level_pages -%}
|
||||
<li class="nav-list-item">
|
||||
{%- if node.has_children -%}
|
||||
<button class="nav-list-expander btn-reset" aria-label="toggle items in {{ node.title }} category" aria-pressed="false">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg>
|
||||
</button>
|
||||
{%- endif -%}
|
||||
<a href="{{ node.url | relative_url }}" class="nav-list-link">{{ node.title }}</a>
|
||||
{%- if node.has_children -%}
|
||||
{%- assign children_list = second_level_pages
|
||||
| where: "parent", node.title -%}
|
||||
{%- if node.child_nav_order == 'desc' or node.child_nav_order == 'reversed' -%}
|
||||
{%- assign children_list = children_list | reverse -%}
|
||||
{%- endif -%}
|
||||
<ul class="nav-list">
|
||||
{%- for child in children_list -%}
|
||||
<li class="nav-list-item">
|
||||
{%- if child.has_children -%}
|
||||
<button class="nav-list-expander btn-reset" aria-label="toggle items in {{ child.title }} category" aria-pressed="false">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg>
|
||||
</button>
|
||||
{%- endif -%}
|
||||
<a href="{{ child.url | relative_url }}" class="nav-list-link">{{ child.title }}</a>
|
||||
{%- if child.has_children -%}
|
||||
{%- assign grand_children_list = third_level_pages
|
||||
| where: "parent", child.title
|
||||
| where: "grand_parent", node.title -%}
|
||||
{%- if child.child_nav_order == 'desc' or child.child_nav_order == 'reversed' -%}
|
||||
{%- assign grand_children_list = grand_children_list | reverse -%}
|
||||
{%- endif -%}
|
||||
<ul class="nav-list">
|
||||
{%- for grand_child in grand_children_list -%}
|
||||
<li class="nav-list-item">
|
||||
<a href="{{ grand_child.url | relative_url }}" class="nav-list-link">{{ grand_child.title }}</a>
|
||||
</li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
{%- endif -%}
|
||||
</li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
{%- endif -%}
|
||||
</li>
|
||||
{%- endfor -%}
|
||||
</ul>
|
48
_includes/components/nav/children.html
Normal file
48
_includes/components/nav/children.html
Normal file
@@ -0,0 +1,48 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include components/nav/children.html node=node ancestors=title_array all=bool -%}
|
||||
Depends on: include.node, include.ancestors, include.all, nav_parenthood, nav_top_node_titles.
|
||||
Includes: components/nav/sorted.html.
|
||||
Assigns to: nav_children.
|
||||
Overwrites:
|
||||
nav_candidates, nav_child, nav_child_ok.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign nav_children = "" | split: "" -%}
|
||||
|
||||
{%- if include.all == true or include.node.has_children != false -%}
|
||||
|
||||
{%- assign nav_candidates = nav_parenthood
|
||||
| where: "name", include.node.title | map: "items" | first -%}
|
||||
|
||||
{%- for nav_child in nav_candidates -%}
|
||||
{%- assign nav_child_ok = true -%}
|
||||
|
||||
{%- if nav_child.grand_parent and nav_child.grand_parent != include.node.parent -%}
|
||||
{%- assign nav_child_ok = false -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if nav_child.ancestor and nav_child.ancestor != include.node.title -%}
|
||||
{%- unless include.ancestors contains nav_child.ancestor -%}
|
||||
{%- assign nav_child_ok = false -%}
|
||||
{%- endunless -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- comment -%}
|
||||
The following check rejects nav_child as 3rd-level when include.node is 2nd-level
|
||||
and nav_child can also be 2nd-level. This is for backwards compatibility with
|
||||
existing 3-level sites.
|
||||
{%- endcomment -%}
|
||||
{%- if nav_child.grand_parent == nil and nav_child.ancestor == nil and
|
||||
nav_top_node_titles contains nav_child.parent and include.ancestors.size >= 1 -%}
|
||||
{%- assign nav_child_ok = false -%}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if nav_child_ok -%}
|
||||
{%- assign nav_children = nav_children | push: nav_child -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
|
||||
{%- endif -%}
|
||||
|
||||
{%- include components/nav/sorted.html pages=nav_children -%}
|
||||
{%- assign nav_children = nav_sorted -%}
|
53
_includes/components/nav/links.html
Normal file
53
_includes/components/nav/links.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include components/nav/links.html pages=page_array ancestors=title_array all=bool -%}
|
||||
Depends on: include.pages, include.ancestors, include.all.
|
||||
Results in: HTML for the main navigation when all is nil or false;
|
||||
includes links to pages excluded from the main navigation when all is true.
|
||||
Includes: components/nav/sorted.html, components/nav/children.html, components/nav/links.html.
|
||||
Overwrites:
|
||||
node, nav_children, nav_ancestors.
|
||||
{%- endcomment -%}
|
||||
|
||||
<ul class="nav-list">
|
||||
{%- for node in include.pages -%}
|
||||
{%- if include.all == true or node.nav_exclude != true -%}
|
||||
|
||||
{%- if include.ancestors contains node.title -%}
|
||||
|
||||
<li class="nav-list-item">
|
||||
<a href="{{ node.url | relative_url }}" class="nav-list-link"> ∞ </a>
|
||||
</li>
|
||||
{%- capture nav_error_report -%}
|
||||
<blockquote class="warning">
|
||||
A page has the same title as its parent page or one of its ancestral pages!<br>
|
||||
This causes an incorrect link in the main navigation panel.<br>
|
||||
Page title: <code>{{ node.title }}</code>, location: <code>{{ node.path }}</code>.
|
||||
</blockquote>
|
||||
{%- endcapture -%}
|
||||
|
||||
{%- else -%}
|
||||
|
||||
{%- include components/nav/children.html node=node ancestors=include.ancestors all=include.all -%}
|
||||
|
||||
<li class="nav-list-item">
|
||||
{%- if nav_children.size >= 1 -%}
|
||||
<button class="nav-list-expander btn-reset" aria-label="toggle items in {{ node.title }} category" aria-pressed="false">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg>
|
||||
</button>
|
||||
{%- endif -%}
|
||||
<a href="{{ node.url | relative_url }}" class="nav-list-link">{{ node.title }}</a>
|
||||
{%- if nav_children.size >= 1 -%}
|
||||
{%- if node.child_nav_order == 'desc' or node.child_nav_order == 'reversed' -%}
|
||||
{%- assign nav_children = nav_children | reverse -%}
|
||||
{%- endif -%}
|
||||
{%- assign nav_ancestors = include.ancestors | push: node.title -%}
|
||||
{%- include components/nav/links.html pages=nav_children ancestors=nav_ancestors all=include.all -%}
|
||||
{%- endif -%}
|
||||
</li>
|
||||
|
||||
{%- endif -%}
|
||||
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
</ul>
|
||||
{%- comment -%}{%- endcomment -%}
|
23
_includes/components/nav/pages.html
Normal file
23
_includes/components/nav/pages.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include components/nav/pages.html pages=page_array all=bool -%}
|
||||
Depends on: include.pages.
|
||||
Results in: HTML for the main navigation when all is nil or false;
|
||||
adds links to pages excluded from the main navigation when all is true.
|
||||
Includes: components/nav/links.html
|
||||
Assigns to:
|
||||
nav_parenthood, nav_top_nodes, nav_top_node_titles, nav_ancestors.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign nav_parenthood = include.pages
|
||||
| where_exp: "item", "item.title != nil" | group_by: "parent" -%}
|
||||
|
||||
{%- assign nav_top_nodes = nav_parenthood
|
||||
| where_exp: "item", "item.name == ''" | map: "items" | first -%}
|
||||
|
||||
{%- include components/nav/sorted.html pages=nav_top_nodes -%}
|
||||
|
||||
{% assign nav_top_node_titles = nav_top_nodes | map: "title" -%}
|
||||
|
||||
{%- assign nav_ancestors = "" | split: "" -%}
|
||||
|
||||
{%- include components/nav/links.html pages=nav_sorted ancestors=nav_ancestors all=include.all -%}
|
109
_includes/components/nav/sorted.html
Normal file
109
_includes/components/nav/sorted.html
Normal file
@@ -0,0 +1,109 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include components/nav/sorted.html pages=page_array -%}
|
||||
Depends on: include.pages.
|
||||
Assigns to: nav_sorted.
|
||||
Overwrites:
|
||||
nav_order_pages, title_order_pages, double_quote, empty_array,
|
||||
nav_number_pages, nav_string_pages, nav_order_groups, group,
|
||||
title_number_pages, title_string_pages, title_order_groups.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- comment -%}
|
||||
The `nav_order` values of pages affect the order in which they are shown in
|
||||
the navigation panel and in the automatically generated tables of contents.
|
||||
Sibling pages with the same `nav_order` value may be shown in any order.
|
||||
Sibling pages with no `nav_order` value are shown after all pages that have
|
||||
explicit `nav_order` values, ordered by their `title` values.
|
||||
|
||||
The `nav_order` and `title` values can be numbers or strings. To avoid build
|
||||
failures, we sort numbers and strings separately. We sort numbers by their
|
||||
values, and strings lexicographically. The case-sensitivity of string sorting
|
||||
is determined by the configuration setting of `nav_sort`. Pages with no `title`
|
||||
value are excluded from the navigation.
|
||||
|
||||
Note: Numbers used as `title` or `nav_order` values should not be in quotes,
|
||||
unless you intend them to be lexicographically ordered. Numbers are written
|
||||
without spaces or thousands-separators. Negative numbers are preceded by `-`.
|
||||
Floats are written with the integral and fractional parts separated by `.`.
|
||||
(Bounds on the magnitude and precision are presumably the same as in Liquid.)
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign nav_order_pages = include.pages
|
||||
| where_exp: "item", "item.nav_order != nil" -%}
|
||||
{%- assign title_order_pages = include.pages
|
||||
| where_exp: "item", "item.nav_order == nil" -%}
|
||||
|
||||
{%- comment -%}
|
||||
First, filter `nav_order_pages` and `title_order_pages` according to the type
|
||||
of value to be used for sorting.
|
||||
|
||||
The first character of the result of filtering with `jsonify` is `"` only for
|
||||
strings. Removing `"` from its `slice : 0` has size 0 for strings and 1 for
|
||||
numbers, so grouping the pages gives at most two groups.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- assign double_quote = '"' -%}
|
||||
{%- assign empty_array = "" | split: "" -%}
|
||||
|
||||
{%- assign nav_string_pages = empty_array -%}
|
||||
{%- assign nav_number_pages = empty_array -%}
|
||||
{%- unless nav_order_pages == empty -%}
|
||||
{%- assign nav_order_groups = nav_order_pages
|
||||
| group_by_exp: "item",
|
||||
"item.nav_order | jsonify | slice: 0 | remove: double_quote | size" -%}
|
||||
{%- for group in nav_order_groups -%}
|
||||
{%- if group.name == 0 -%}
|
||||
{%- assign nav_string_pages = group.items -%}
|
||||
{%- elsif group.name == 1 -%}
|
||||
{%- assign nav_number_pages = group.items -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endunless -%}
|
||||
|
||||
{%- assign title_string_pages = empty_array -%}
|
||||
{%- assign title_number_pages = empty_array -%}
|
||||
{%- unless title_order_pages == empty -%}
|
||||
{%- assign title_order_groups = title_order_pages
|
||||
| group_by_exp: "item",
|
||||
"item.title | jsonify | slice: 0 | remove: double_quote | size" -%}
|
||||
{%- for group in title_order_groups -%}
|
||||
{%- if group.name == 0 -%}
|
||||
{%- assign title_string_pages = group.items -%}
|
||||
{%- elsif group.name == 1 -%}
|
||||
{%- assign title_number_pages = group.items -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{%- endunless -%}
|
||||
|
||||
{%- comment -%}
|
||||
Now sort each array of pages separately, then concatenate the sorted arrays.
|
||||
{%- endcomment -%}
|
||||
|
||||
{%- unless nav_number_pages == empty -%}
|
||||
{%- assign nav_number_pages = nav_number_pages | sort: "nav_order" -%}
|
||||
{%- endunless -%}
|
||||
|
||||
{%- unless nav_string_pages == empty -%}
|
||||
{%- if site.nav_sort == 'case_insensitive' -%}
|
||||
{%- assign nav_string_pages = nav_string_pages | sort_natural: "nav_order" -%}
|
||||
{%- else -%}
|
||||
{%- assign nav_string_pages = nav_string_pages | sort: "nav_order" -%}
|
||||
{%- endif -%}
|
||||
{%- endunless -%}
|
||||
|
||||
{%- unless title_number_pages == empty -%}
|
||||
{%- assign title_number_pages = title_number_pages | sort: "title" -%}
|
||||
{%- endunless -%}
|
||||
|
||||
{%- unless title_string_pages == empty -%}
|
||||
{%- if site.nav_sort == 'case_insensitive' -%}
|
||||
{%- assign title_string_pages = title_string_pages | sort_natural: "title" -%}
|
||||
{%- else -%}
|
||||
{%- assign title_string_pages = title_string_pages | sort: "title" -%}
|
||||
{%- endif -%}
|
||||
{%- endunless -%}
|
||||
|
||||
{%- assign nav_sorted = nav_number_pages
|
||||
| concat: nav_string_pages
|
||||
| concat: title_number_pages
|
||||
| concat: title_string_pages -%}
|
@@ -1,9 +1,10 @@
|
||||
{%- comment -%}
|
||||
Include as: {%- include_cached components/site_nav.html -%}
|
||||
Include as: {%- include_cached components/site_nav.html all=bool -%}
|
||||
Depends on: site.
|
||||
Results in: HTML for the site-nav.
|
||||
Results in: cached HTML for the main navigation when `all` is nil or false;
|
||||
includes links to pages excluded from the main navigation when `all` is true.
|
||||
Includes:
|
||||
components/nav.html
|
||||
components/nav/pages.html
|
||||
Overwrites:
|
||||
pages_top_size, collections_size, collection_entry,
|
||||
collection_key, collection_value, collection.
|
||||
@@ -16,7 +17,7 @@
|
||||
| where_exp:"item", "item.nav_exclude != true"
|
||||
| size %}
|
||||
{% if pages_top_size > 0 %}
|
||||
{% include components/nav.html pages=site.html_pages %}
|
||||
{% include components/nav/pages.html pages=site.html_pages all=include.all %}
|
||||
{% endif %}
|
||||
{%- if site.nav_external_links -%}
|
||||
<ul class="nav-list">
|
||||
@@ -51,17 +52,21 @@
|
||||
</button>
|
||||
{%- endif -%}
|
||||
<div class="nav-category">{{ collection_value.name }}</div>
|
||||
{% include components/nav.html pages=collection %}
|
||||
{% include components/nav/pages.html pages=collection all=include.all %}
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<div class="nav-category">{{ collection_value.name }}</div>
|
||||
{% include components/nav.html pages=collection %}
|
||||
{% include components/nav/pages.html pages=collection all=include.all %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% include components/nav.html pages=collection %}
|
||||
{% include components/nav/pages.html pages=collection all=include.all %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</nav>
|
||||
|
||||
{% if site.nav_error_report %}
|
||||
{{ nav_error_report }}
|
||||
{%- endif %}
|
||||
|
Reference in New Issue
Block a user