views: factor out paginator component.
* Refactor the paginator into a ViewComponent.
* Fix inconsistent spacing between paginator items.
* Fix a bug where the sequential paginator generated the wrong next /
previous page links in the <link rel="{next|prev}"> tags in the <head>.
* Always include the final page as a hidden html element, so that it can
be unhidden with custom CSS.
* Make it easier to change the pagination window.
This commit is contained in:
44
app/components/paginator_component.rb
Normal file
44
app/components/paginator_component.rb
Normal file
@@ -0,0 +1,44 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PaginatorComponent < ApplicationComponent
|
||||
attr_reader :records, :window, :params
|
||||
delegate :current_page, :prev_page, :next_page, :total_pages, :paginator_mode, :paginator_page_limit, to: :records
|
||||
delegate :ellipsis_icon, :chevron_left_icon, :chevron_right_icon, to: :helpers
|
||||
|
||||
def initialize(records:, params:, window: 4)
|
||||
@records = records
|
||||
@window = window
|
||||
@params = params
|
||||
end
|
||||
|
||||
def use_sequential_paginator?
|
||||
paginator_mode != :numbered || current_page >= paginator_page_limit
|
||||
end
|
||||
|
||||
def pages
|
||||
last_page = total_pages
|
||||
left = (current_page - window).clamp(2..)
|
||||
right = (current_page + window).clamp(..last_page - 1)
|
||||
|
||||
[
|
||||
1,
|
||||
("..." unless left == 2),
|
||||
(left..right).to_a,
|
||||
("..." unless right == last_page - 1),
|
||||
last_page
|
||||
].flatten.compact
|
||||
end
|
||||
|
||||
def link_to_page(anchor, page = anchor, **options)
|
||||
if page.nil?
|
||||
tag.span anchor, **options
|
||||
else
|
||||
hidden = paginator_mode == :numbered && page > paginator_page_limit
|
||||
link_to anchor, url_for_page(page), **options, hidden: hidden
|
||||
end
|
||||
end
|
||||
|
||||
def url_for_page(page)
|
||||
url_for(**params.merge(page: page).permit!)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
<% content_for(:html_header) do %>
|
||||
<% if prev_page.present? %>
|
||||
<%= tag.link rel: "prev", href: url_for_page(prev_page) %>
|
||||
<% end %>
|
||||
|
||||
<% if next_page.present? %>
|
||||
<%= tag.link rel: "next", href: url_for_page(next_page) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="paginator numbered-paginator mt-8 mb-4 space-x-2 flex justify-center items-center">
|
||||
<%= link_to_page chevron_left_icon, prev_page, class: "paginator-prev", rel: "prev", "data-shortcut": "a left" %>
|
||||
|
||||
<% pages.each do |page| %>
|
||||
<% if page == "..." %>
|
||||
<%= ellipsis_icon class: "paginator-ellipsis text-muted desktop-only" %>
|
||||
<% elsif page == current_page %>
|
||||
<%= tag.span page, class: "paginator-current font-bold" %>
|
||||
<% else %>
|
||||
<%= link_to_page page, class: "paginator-page desktop-only" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to_page chevron_right_icon, next_page, class: "paginator-next", rel: "next", "data-shortcut": "d right" %>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="paginator sequential-paginator mt-8 mb-4 space-x-2 flex justify-center items-center">
|
||||
<%= link_to_page chevron_left_icon, prev_page, class: "paginator-prev", rel: "prev", "data-shortcut": "a left" %>
|
||||
<%= link_to_page chevron_right_icon, next_page, class: "paginator-next", rel: "next", "data-shortcut": "d right" %>
|
||||
</div>
|
||||
10
app/components/paginator_component/paginator_component.scss
Normal file
10
app/components/paginator_component/paginator_component.scss
Normal file
@@ -0,0 +1,10 @@
|
||||
.paginator {
|
||||
> * {
|
||||
padding: 0.25rem 0.75rem;
|
||||
}
|
||||
|
||||
> a:hover {
|
||||
background: var(--paginator-arrow-color);
|
||||
color: var(--paginator-arrow-background-color);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user