Peter Mosses 7c124a210a
Fix incorrect disambiguation in generated TOCs (#999)
The regression tests in https://just-the-docs.github.io/just-the-docs-tests/navigation/grandparent/index/ include a section "A grandchild with the same parent title as a child or top-level page". Its last item fails, as can currently be seen at https://just-the-docs.github.io/just-the-docs-tests/navigation/grandparent/f/ : the first occurrence of `G` links to the grandchild of `E`., and shouldn't be included.

The regression can be fixed by changing `where: "grand_parent", page.parent` to `where_exp: "item", "item.grand_parent == page.parent"`. This should be apparent  when running the regressions tests with this PR branch as remote theme.

The two filters give the same results in Jekyll 4, but not in Jekyll 3. It seems that this was overlooked because the sanity-testing of v0.4.0.rc3 was inadvertently using Jekyll 4.2.2.
2022-10-14 13:15:25 -07:00

236 lines
10 KiB
HTML

{%- 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 title_pages = include.pages
| where_exp: "item", "item.title != nil" -%}
{%- comment -%}
A page with `nav_exclude: true` does not appear in the main navigation.
If it has a `parent`, it may appear in the parent's table of contents.
If it specifies `has_children: true`, it should appear in the breadcrumbs
of the child pages, but its order in relation to other pages is irrelevant.
Pages that never appear can be removed from the pages that need to be sorted.
This optimisation can be significant on a site with many pages.
In Jekyll 4, the pages to be sorted can be filtered by:
{%- assign title_pages = title_pages
| where_exp: "item", "item.nav_exclude != true or item.parent != nil" -%}
That filter is not allowed in Jekyll 3. The following iterative code gives the
same effect, but it is activated only when it will filter more than 50% of the
pages.
{%- endcomment -%}
{%- assign unsorted_pages = title_pages
| where_exp: "item", "item.parent == nil"
| where_exp: "item", "item.nav_exclude == true"-%}
{%- assign title_pages_size = title_pages.size -%}
{%- assign unsorted_pages_percent = unsorted_pages.size
| times: 100 | divided_by: title_pages_size -%}
{%- if unsorted_pages_percent > 50 -%}
{%- assign sorted_pages = "" | split: "" -%}
{%- for item in title_pages -%}
{%- if item.nav_exclude != true or item.parent -%}
{%- assign sorted_pages = sorted_pages | push: item -%}
{%- endif -%}
{%- endfor -%}
{%- assign title_pages = sorted_pages -%}
{%- endif -%}
{%- assign nav_order_pages = title_pages
| where_exp: "item", "item.nav_order != nil" -%}
{%- assign title_order_pages = title_pages
| where_exp: "item", "item.nav_order == nil" -%}
{%- comment -%}
Divide the arrays of `nav_order_pages` and `title_order_pages` according to
the type of value.
The first character of the result of `jsonify` is `"` only for strings.
Grouping by a single character also ensures the number of groups is small.
{%- endcomment -%}
{%- assign nav_number_pages = "" | split: "" -%}
{%- assign nav_string_pages = "" | split: "" -%}
{%- assign nav_order_groups = nav_order_pages
| group_by_exp: "item", "item.nav_order | jsonify | slice: 0" -%}
{%- for group in nav_order_groups -%}
{%- if group.name == '"' -%}
{%- assign nav_string_pages = group.items -%}
{%- else -%}
{%- assign nav_number_pages = nav_number_pages | concat: group.items -%}
{%- endif -%}
{%- endfor -%}
{%- 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 -%}
{%- assign title_number_pages = "" | split: "" -%}
{%- assign title_string_pages = "" | split: "" -%}
{%- assign title_order_groups = title_order_pages
| group_by_exp: "item", "item.title | jsonify | slice: 0" -%}
{%- for group in title_order_groups -%}
{%- if group.name == '"' -%}
{%- assign title_string_pages = group.items -%}
{%- else -%}
{%- assign title_number_pages = title_number_pages | concat: group.items -%}
{%- endif -%}
{%- endfor -%}
{%- 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 pages_list = nav_number_pages | concat: nav_string_pages
| concat: title_number_pages | concat: title_string_pages -%}
{%- assign first_level_pages = pages_list
| where_exp: "item", "item.parent == nil" -%}
{%- assign second_level_pages = pages_list
| where_exp: "item", "item.parent != nil"
| where_exp: "item", "item.grand_parent == nil" -%}
{%- assign third_level_pages = pages_list
| where_exp: "item", "item.grand_parent != nil" -%}
{%- comment -%}
The order of sibling pages in `pages_list` determines the order of display of
links to them in lists of navigation links and in auto-generated TOCs.
{%- endcomment -%}
<ul class="nav-list">
{%- for node in first_level_pages -%}
{%- unless node.nav_exclude -%}
<li class="nav-list-item{% if page.collection == include.key and page.url == node.url or page.parent == node.title or page.grand_parent == node.title %} active{% endif %}">
{%- if node.has_children -%}
<a href="#" class="nav-list-expander" aria-label="toggle links in {{ node.title }} category">
<svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg>
</a>
{%- endif -%}
<a href="{{ node.url | relative_url }}" class="nav-list-link{% if page.url == node.url %} active{% endif %}">{{ node.title }}</a>
{%- if node.has_children -%}
{%- assign children_list = second_level_pages
| where: "parent", node.title -%}
{%- if node.child_nav_order == 'desc' -%}
{%- assign children_list = children_list | reverse -%}
{%- endif -%}
<ul class="nav-list ">
{%- for child in children_list -%}
{%- unless child.nav_exclude -%}
<li class="nav-list-item {% if page.url == child.url or page.parent == child.title %} active{% endif %}">
{%- if child.has_children -%}
<a href="#" class="nav-list-expander" aria-label="toggle links in {{ child.title }} category">
<svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg>
</a>
{%- endif -%}
<a href="{{ child.url | relative_url }}" class="nav-list-link{% if page.url == child.url %} active{% endif %}">{{ 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' -%}
{%- assign grand_children_list = grand_children_list | reverse -%}
{%- endif -%}
<ul class="nav-list">
{%- for grand_child in grand_children_list -%}
{%- unless grand_child.nav_exclude -%}
<li class="nav-list-item {% if page.url == grand_child.url %} active{% endif %}">
<a href="{{ grand_child.url | relative_url }}" class="nav-list-link{% if page.url == grand_child.url %} active{% endif %}">{{ grand_child.title }}</a>
</li>
{%- endunless -%}
{%- endfor -%}
</ul>
{%- endif -%}
</li>
{%- endunless -%}
{%- endfor -%}
</ul>
{%- endif -%}
</li>
{%- endunless -%}
{%- endfor -%}
{%- unless include.key -%}
{%- assign nav_external_links = site.nav_external_links -%}
{%- for node in nav_external_links -%}
<li class="nav-list-item external">
<a href="{{ node.url | absolute_url }}" class="nav-list-link external">
{{ node.title }}
{% unless node.hide_icon %}<svg viewBox="0 0 24 24" aria-labelledby="svg-external-link-title"><use xlink:href="#svg-external-link"></use></svg>{% endunless %}
</a>
</li>
{%- endfor -%}
{%- endunless -%}
</ul>
{%- comment -%}
`page.collection` is the name of the Jekyll collection that contains the page,
if any, and otherwise nil. Similarly for `include.key`.
If the current page is in the collection (if any) whose navigation is currently
being generated, the following code sets `first_level_url` to the URL used in
the page's top-level breadcrumb (if any), and `second_level_url` to that used
in the page's second-level breadcrumb (if any).
For pages with children, the code also sets `toc_list` to the list of child pages.
{%- endcomment -%}
{%- if page.collection == include.key -%}
{%- for node in first_level_pages -%}
{%- if page.grand_parent == node.title or page.parent == node.title and page.grand_parent == nil -%}
{%- assign first_level_url = node.url | relative_url -%}
{%- endif -%}
{%- if node.has_children -%}
{%- assign children_list = second_level_pages | where: "parent", node.title -%}
{%- for child in children_list -%}
{%- if child.has_children -%}
{%- if page.url == child.url or page.parent == child.title and page.grand_parent == child.parent -%}
{%- assign second_level_url = child.url | relative_url -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- endfor -%}
{%- if page.has_children == true and page.has_toc != false -%}
{%- assign toc_list = pages_list
| where: "parent", page.title
| where_exp: "item", "item.grand_parent == page.parent" -%}
{%- if page.child_nav_order == "desc" -%}
{%- assign toc_list = toc_list | reverse -%}
{%- endif -%}
{%- endif -%}
{%- endif -%}