ssg  
Where to Discuss?

Local Group

Preface

Goal: Explaining Glenn McComb Pagination using Math and Table.

Source Code

This article use tutor-07 theme. We will create it step by step.


1: Preview: Detail

Consider, have a look at the pagination below in a stripped down model.

Layout: Nunjucks Blog

We are still using pagination/03-adjacent partial, in blog.njk.

  {% include "pagination/03-adjacent.njk" %}

And the pagination/03-adjacent partial is rely on this filter.

  {% set showCursorFlag = cursor | 
           isShowAdjacent(current, totalPages, adjacentLinks) %}

Filter: Pagination Helper

Which the filter itself has this skeleton below:

exports.isShowAdjacent = function(...) {
  // initialize variables ...
  
  if (totalPages > maxLinks) {
  // Complex page numbers.    
    if (current <= lowerLimit) {
      // Lower limit pages ...
    } else if (current >= upperLimit) {
      // Upper limit pages ...
    } else {
      // Middle pages ...
    }
  } else {
  // Simple page numbers.
    ...
  }

  return showCursorFlag;
}

Structure

This will only show one part:

  • Middle Pagination: Glenn McComb

Each Pagination

Consider, have a look at the animation above, frame by frame. I’m going to do some reverse engineering, to accomplish better understanding on how this pagination works.

We have from first page (1), to last page (9).

11ty Pagination: Adjacent Page 1

11ty Pagination: Adjacent Page 2

11ty Pagination: Adjacent Page 3

11ty Pagination: Adjacent Page 4

11ty Pagination: Adjacent Page 5

11ty Pagination: Adjacent Page 6

11ty Pagination: Adjacent Page 7

11ty Pagination: Adjacent Page 8

11ty Pagination: Adjacent Page 9

Table

We can rewrite the table with additional rows as below.

+------------+-------+-------+-------+-------+-------+
| pagination |   2   |   3   |   5   |  10   |  20   |
+------------+-------+-------+-------+-------+-------+
| VARIABLE                                           |
| totalPages |   9   |   6   |   4   |   2   |  N/A  |
| maxLinks   |   5   |   5   |   5   |   5   |  N/A  |
| lowerLimit |   3   |   3   |   3   |   3   |  N/A  |
| upperLimit |   7   |   4   |   2   |   0   |  N/A  |
+------------+-------+-------+-------+-------+-------+
| MIDDLE PAGINATION                                  |
| page  = 1  | 1..5  | 1..5  | 1..4  | 1..2  |-------+
| page  = 2  | 1..5  | 1..5  | 1..4  | 1..2  |       |
| page  = 3  | 1..5  | 1..5  | 1..4  |-------+       |
| page  = 4  | 2..6  | 4..6  | 1..4  |               |
| page  = 5  | 3..7  | 4..6  |-------+               |
| page  = 6  | 4..8  | 4..6  |                       |
| page  = 7  | 5..9  |-------+                       |
| page  = 8  | 5..9  |                               |
| page  = 9  | 5..9  |                               |
+------------+-------+-------------------------------+

2: Math: Conditional

Part: Middle Pages

This is already discussed in, so I won’t explain it nomore.

    if (current <= lowerLimit) {
      // Lower limit pages.
      ...
    } else if (current >= upperLimit) {
      // Upper limit pages.
      ...
    } else {
      // Middle pages.
      if ( (cursor >= current - adjacentLinks)
      &&   (cursor <= current + adjacentLinks) )
         showCursorFlag = true;
    }

What you need to know is the conditional result in table:

+------------+-------+
| pagination |   2   |
| adjacent   |   2   |
| total post |  17   |
+------------+-------+
| VARIABLE           |
| totalPages |   9   |
| maxLinks   |   5   |
| lowerLimit |   3   |
| upperLimit |   7   |
+------------+-------+--+
| selected   | adjacent |
+------------+----------+
| page  =  1 |   1..3   |
| page  =  2 |   1..4   |
| page  =  3 |   1..5   |
| page  =  4 |   2..6   |
| page  =  5 |   3..7   |
| page  =  6 |   4..8   |
| page  =  7 |   5..9   |
| page  =  8 |   5..9   |
| page  =  9 |   5..9   |
+------------+----------+

Part: Lower Limit Pages

Consider stripped more for each part.

    if (current <= lowerLimit) {
      // Lower limit pages.
      // If the user is on a page which is in the lower limit.
      if (cursor <= maxLinks)
         showCursorFlag = true;
    } else if (current >= upperLimit) {
      // Upper limit pages.
      ...
    } else {
      // Middle pages.
      ...
    }

Notice that there is two part of conditional.

  • Outer conditional: result true for the first three row, as defined by lowerLimit.

  • Inner conditional: always result 1..5.

Thus, the conditional result in table:

+------------+-------+-------+--------+
| selected   | lower | l max | result |
+------------+-------+-------+--------+
| page  =  1 |   T   | 1..5  |  1..5  |
| page  =  2 |   T   | 1..5  |  1..5  |
| page  =  3 |   T   | 1..5  |  1..5  |
| page  =  4 |       | 1..5  |        |
| page  =  5 |       | 1..5  |        |
| page  =  6 |       | 1..5  |        |
| page  =  7 |       | 1..5  |        |
| page  =  8 |       | 1..5  |        |
| page  =  9 |       | 1..5  |        |
+------------+-------+-------+--------+

Combined: All Conditional

Now we have all the logic combined at once.

+------------+-------+
| pagination |   1   |
| adjacent   |   2   |
| totalPost  |  10   |
+------------+-------+
| VARIABLE           |
| totalPages |  10   |
| maxLinks   |   5   |
| lowerLimit |   3   |
| upperLimit |   7   |
+------------+-------+-+-------+-------+-------+-------+
| selected   | adjacent| lower | l max | upper | u max |
+------------+---------+-------+-------+-------+-------+
| cursor = 1 |  1..3   |   T   | 1..5  |       | 5..9  |
| cursor = 2 |  1..4   |   T   | 1..5  |       | 5..9  |
| cursor = 3 |  1..5   |   T   | 1..5  |       | 5..9  |
| cursor = 4 |  2..6   |       | 1..5  |       | 5..9  |
| cursor = 5 |  3..7   |       | 1..5  |       | 5..9  |
| cursor = 6 |  4..8   |       | 1..5  |       | 5..9  |
| cursor = 7 |  5..9   |       | 1..5  |   T   | 5..9  |
| cursor = 8 |  5..9   |       | 1..5  |   T   | 5..9  |
| cursor = 9 |  5..9   |       | 1..5  |   T   | 5..9  |
+------------+---------+-------+-------+-------+-------+

Final Result

As a conclusion table.

+------------+-------+
| VARIABLE           |
| totalPages |  10   |
| maxLinks   |   5   |
| lowerLimit |   3   |
| upperLimit |   8   |
+------------+-------+-------+---------+
| selected   | lower | upper | adjacent|
+------------+-------+-------+---------+
| cursor = 1 | 1..5  |       |         |
| cursor = 2 | 1..5  |       |         |
| cursor = 3 | 1..5  |       |         |
| cursor = 4 |       |       |  2..6   |
| cursor = 5 |       |       |  3..7   |
| cursor = 6 |       |       |  4..8   |
| cursor = 7 |       | 5..9  |         |
| cursor = 8 |       | 5..9  |         |
| cursor = 9 |       | 5..9  |         |
+------------+-------+-------+---------+
| selected   | if elsif else | result  |
+------------+---------------+---------+
| cursor = 1 |               |  1..5   |
| cursor = 2 |               |  1..5   |
| cursor = 3 |               |  1..5   |
| cursor = 4 |               |  2..6   |
| cursor = 5 |               |  3..7   |
| cursor = 6 |               |  4..8   |
| cursor = 7 |               |  5..9   |
| cursor = 8 |               |  5..9   |
| cursor = 9 |               |  5..9   |
+------------+---------------+---------+

3: Legacy Code

If you find the javascript above in filter is long, you need to consider this legacy code below. This is what I use before I put the logic into javascript filter.

Whoaaa… This legacy code is complex!

The script in javascript filter is direct port, of this nunjucks template script, that I have made about a year ago.

    {# <!-- Page numbers. --> #}
    {% for cursor, link in pagination.links | hashIt %}

      {% set showCursorFlag = false %}

      {# Set Flag: Complex code. #}
      {% if (totalPages > maxLinks) %}
        {# <!-- Complex page numbers. --> #}

        {# <!-- Lower limit pages. --> #}
        {# <!-- If the user is on a page which is in the lower limit.  --> #}
        {% if (current <= lowerLimit) %}

          {# <!-- If the current loop page is less than max_links. --> #}
          {% if (cursor <= maxLinks) %}
            {% set showCursorFlag = true %}
          {% endif %}

        {# <!-- Upper limit pages. --> #}
        {# <!-- If the user is on a page which is in the upper limit. --> #}
        {% elif (current >= upperLimit) %}

          {# <!-- If the current loop page is greater than total pages minus $max_links --> #}
          {% if (cursor > (totalPages - maxLinks)) %}
            {% set showCursorFlag = true %}
          {% endif %}

        {# <!-- Middle pages. --> #}
        {% else %}
          
          {% if ( (cursor >= current - adjacentLinks) 
              and (cursor <= current + adjacentLinks) ) %}
            {% set showCursorFlag = true %}
          {% endif %}

        {% endif %}

      {% else %}
        {# <!-- Simple page numbers. --> #}
        {% set showCursorFlag = true %}
      {% endif %}

      {# Use Flag: Show Pager. #}
      {% if showCursorFlag %}
      <li>...</li>
      {% endif %}

    {% endfor %}

You can compare the legacy script with the new filter below.

11ty Pagination: Pagination Helper: isShowAdjacent

We are not going to use this legacy script anymore. But this script is still useful to debug, or to check value that I need to compare with table above.


What is Next ?

Consider continue reading [ Eleventy - Pagination - Indicator ]. We are going to add indicator, and putting all pagination part together.

Thank you for reading.