mirror of
https://github.com/snachodog/just-the-docs.git
synced 2025-04-20 10:02:23 -06:00
* Optimize simple navigation cases Fix inefficiency reported in feedback on v0.4.0.rc2 (see discussion #958). This PR: * essentially reverts `_includes/nav.html` to v0.4.0.rc1 * preserves the ARIA labels added by #950 * adds a test to optimize builds of sites that rely on `title` fields to order pages. Building the `endoflife.date` site (130 pages) now takes only about 7 seconds. Building the `machinetranslate.org` site ( 350 pages) takes about 7 minutes. (Without the added test, it takes just over 5 minutes: the condition of the test is merely to compare the size of two arrays, but that is apparently enough to prevent Jekyll from applying some optimization). A warning is added to the docs about the need for numbers to be in quotes when used as title values. * Update navigation-structure.md A clarification is added to the docs about the need for numbers to be in quotes when used as title values. * Simplify the control and data flow - Defer concatenation of `string_order_pages` with `title_order_pages` until needed. - Replace tests on size with tests for `empty`. - Rename variables accordingly. * Fix child nav order This PR started from the navigation in RC1. Some cosmetic improvements had been made in RC2. This commit adds some of those changes to this PR. It also fixes a bug (revealed by a new regression test) due to a reference to `node.child_nav_order` instead of `child.child_nav_order`, which prevented reversal of the order in children of children. Presumably a top-level reversal should apply only to direct children, and not to grandchildren. The latter interpretation would be very confusing in a deep multi-level hierarchy. * Allow pages with numeric titles An omitted `nav_order` value should default to the `title` value, regardless of its type. Jekyll 3 gives build errors when numbers and strings are sorted together. This commit drops the assumption that `title` values are always strings – a 404 page naturally has a numeric title. It updates the docs page accordingly. The extra code does not affect the build time for the `endoflife.date` site (7 seconds). For the `machinetranslate` site, changing the title of the 404 page to a number increases the build time from 7 minutes to 9 minutes – the `nav_order` numbers on that site are program-generated in the range 1..1000, which might be atypical. This commit has not yet been checked using the regression tests. The gemspec used for testing specifies `spec.add_runtime_dependency "jekyll", "~> 3.8.5"`, and `Gemfile.lock` shows `jekyll (3.8.7)`. * Update nav.html Add comment about an optimization that will be possible in Jekyll 4. * Update nav.html - Update the comment about optimization possibility. - TEMPORARILY add Jekyll 3 code for conditionally optimizing. * Update nav.html Minor improvements and cosmetic changes. * Major revision This update is based on extensive experimentation and profiling with alternative versions of the Liquid code used to build the main navigation panel. Due to the fragility of Jekyll's optimizations, combining alternative approaches with conditionals turned out to be too expensive: merely adding a condition to check whether some array of pages is empty can add about 20% to the build time! The current code avoids sorting pages on `nav_order` and `title` fields together. The standard way of doing that in Jekyll is to use the `group_by` filter; but extracting the sorted pages from the groups turned out to be too inefficient (as seen in RC1), as was generating links directly from the groups (in RC2). Making all pages with `nav_order` values come before all those ordered by their `title` values is not ideal (it doesn't support tweaking the relative order of two pages in a list of pages ordered by their titles) but it appears to be necessary for efficient builds on large sites. This version has not yet been fully tested for regression, but otherwise seems to give the expected navigation on the endoflife.date and machinetranslate websites. (I'm unable to install the Python-based how2data repository on my laptop, due to package version issues on Apple silicon). Co-authored-by: Peter Mosses <18308236+pdmosses@users.noreply.github.com>
234 lines
10 KiB
HTML
234 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 -%}
|
|
{%- 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 -%}
|
|
</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: "grand_parent", page.parent -%}
|
|
{%- if page.child_nav_order == "desc" -%}
|
|
{%- assign toc_list = toc_list | reverse -%}
|
|
{%- endif -%}
|
|
{%- endif -%}
|
|
{%- endif -%}
|