1 module NumberedPaginationHelper
2 def numbered_pagination(top_page, active_page: top_page + 1, window_half_size: 50, step_size: 50, &)
6 lists << tag.ul(:class => "pagination pagination-sm mt-1") do
7 (1..top_page).each do |page|
8 concat numbered_pagination_item(page, **yield(page), :active => page == active_page)
12 start_list_pages = 1..(active_page < 3 ? active_page + 1 : 1)
13 end_list_pages = (active_page > top_page - 2 ? active_page - 1 : top_page)..top_page
15 middle_list_page_sentinels = [start_list_pages.last, end_list_pages.first]
16 middle_list_page_steps = Range.new(*middle_list_page_sentinels).filter { |page| (page % step_size).zero? }
17 middle_list_page_window = Range.new([active_page - window_half_size, start_list_pages.last].max,
18 [active_page + window_half_size, end_list_pages.first].min).to_a
19 middle_list_pages_with_sentinels = (middle_list_page_sentinels | middle_list_page_steps | middle_list_page_window).sort
20 middle_list_pages_with_sentinels_and_gaps = [middle_list_pages_with_sentinels.first] +
21 middle_list_pages_with_sentinels.each_cons(2).flat_map do |previous_page, page|
22 page == previous_page + 1 ? [page] : [:gap, page]
24 middle_list_pages_with_small_gaps_filled = middle_list_pages_with_sentinels_and_gaps.each_cons(3).map do |previous_page, page, next_page|
25 if page == :gap && previous_page != :gap && next_page != :gap && next_page - previous_page == 2
31 middle_list_pages = middle_list_pages_with_small_gaps_filled
33 lists << tag.ul(:id => "versions-navigation-list-start",
34 :class => "pagination pagination-sm mt-1") do
35 start_list_pages.each do |page|
36 concat numbered_pagination_item(page, **yield(page), :active => page == active_page,
37 :edge => [false, page == start_list_pages.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 current page links ("Version #n" on pages showing element versions)
48 middle_list_pages.each_with_index do |page, i|
49 edge = [i.zero?, i == middle_list_pages.length - 1]
51 concat numbered_pagination_item("...", :edge => edge)
53 concat numbered_pagination_item(page, **yield(page), :active => page == active_page, :edge => edge)
57 lists << tag.ul(:id => "versions-navigation-list-end",
58 :class => "pagination pagination-sm mt-1") do
59 end_list_pages.each do |page|
60 concat numbered_pagination_item(page, **yield(page), :active => page == active_page,
61 :edge => [page == end_list_pages.first, false],
67 tag.div safe_join(lists), :class => "d-flex align-items-start"
72 def numbered_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 }]