1 module NumberedPaginationHelper
 
   2   def element_versions_pagination(top_version, active_version: top_version + 1, window_half_size: 50, step_size: 50, &)
 
   6       lists << tag.ul(:class => "pagination pagination-sm mt-1") do
 
   7         (1..top_version).each do |v|
 
   8           concat element_versions_pagination_item(v, **yield(v), :active => v == active_version)
 
  12       start_list_versions = 1..(active_version < 3 ? active_version + 1 : 1)
 
  13       end_list_versions = (active_version > top_version - 2 ? active_version - 1 : top_version)..top_version
 
  15       middle_list_version_sentinels = [start_list_versions.last, end_list_versions.first]
 
  16       middle_list_version_steps = Range.new(*middle_list_version_sentinels).filter { |v| (v % step_size).zero? }
 
  17       middle_list_version_window = Range.new([active_version - window_half_size, start_list_versions.last].max,
 
  18                                              [active_version + window_half_size, end_list_versions.first].min).to_a
 
  19       middle_list_versions_with_sentinels = (middle_list_version_sentinels | middle_list_version_steps | middle_list_version_window).sort
 
  20       middle_list_versions_with_sentinels_and_gaps = [middle_list_versions_with_sentinels.first] +
 
  21                                                      middle_list_versions_with_sentinels.each_cons(2).flat_map do |previous_version, v|
 
  22                                                        v == previous_version + 1 ? [v] : [:gap, v]
 
  24       middle_list_versions_with_small_gaps_filled = middle_list_versions_with_sentinels_and_gaps.each_cons(3).map do |previous_version, v, next_version|
 
  25         if v == :gap && previous_version != :gap && next_version != :gap && next_version - previous_version == 2
 
  31       middle_list_versions = middle_list_versions_with_small_gaps_filled
 
  33       lists << tag.ul(:id => "versions-navigation-list-start",
 
  34                       :class => "pagination pagination-sm mt-1") do
 
  35         start_list_versions.each do |v|
 
  36           concat element_versions_pagination_item(v, **yield(v), :active => v == active_version,
 
  37                                                                  :edge => [false, v == start_list_versions.last],
 
  41       lists << tag.ul(:id => "versions-navigation-list-middle",
 
  43                         "pagination pagination-sm",
 
  44                         "overflow-x-auto pb-3", # horizontal scrollbar with reserved space below
 
  45                         "pt-1 px-1 mx-n1", # space reserved for focus outlines
 
  46                         "position-relative" # required for centering when clicking "Version #n"
 
  48         middle_list_versions.each_with_index do |v, i|
 
  49           edge = [i.zero?, i == middle_list_versions.length - 1]
 
  51             concat element_versions_pagination_item("...", :edge => edge)
 
  53             concat element_versions_pagination_item(v, **yield(v), :active => v == active_version, :edge => edge)
 
  57       lists << tag.ul(:id => "versions-navigation-list-end",
 
  58                       :class => "pagination pagination-sm mt-1") do
 
  59         end_list_versions.each do |v|
 
  60           concat element_versions_pagination_item(v, **yield(v), :active => v == active_version,
 
  61                                                                  :edge => [v == end_list_versions.first, false],
 
  67     tag.div safe_join(lists), :class => "d-flex align-items-start"
 
  72   def element_versions_pagination_item(body, href: nil, title: nil, active: false, edge: [false, false], edge_border: false)
 
  73     link_class = ["page-link", { "rounded-start-0" => edge.first,
 
  74                                  "border-start-0" => edge.first && !edge_border,
 
  75                                  "rounded-end-0" => edge.last,
 
  76                                  "border-end-0" => edge.last && !edge_border }]
 
  78              link_to body, href, :class => link_class, :title => title
 
  80              tag.span body, :class => link_class
 
  82     tag.li link, :id => ("versions-navigation-active-page-item" if active),
 
  83                  :class => ["page-item", { "disabled" => !href, "active" => active }]