# frozen_string_literal: true require 'will_paginate' module Lipstick module Helpers class PaginationLinkRenderer attr_reader :items, :current, :total, :template private :current, :total, :template def prepare(collection, _options, template) @current = collection.current_page @total = collection.total_pages @items = [*prefix, *middle, *suffix] @template = template end def to_html template.content_tag('nav', class: 'pagination-wrapper') do template.content_tag('ul', class: 'pagination') do items.each do |item| template.concat(render_item(item)) end end end end private def prefix return [:prev] if !show_first? || short_list? return [:prev, 1, :gap] if prefix_gap? [:prev, 1] end def middle return page_range(1, total) if short_list? page_range(*page_window) end # Decides which pages to show in the links surrounding the "current" page. def page_window # If we're close to the start/end we can show a few extra page links. return [1, 8] if current < 5 return [total - 7, total] if current > total - 4 # Otherwise show 3 pages either side of the current page [current - 3, current + 3] end def suffix return [:next] if !show_last? || short_list? return [:gap, total, :next] if suffix_gap? [total, :next] end def page_range(start, finish) [start, 1].max.upto([finish, total].min) end def short_list? total <= 10 end def show_first? !middle.include?(1) end def show_last? !middle.include?(total) end def prefix_gap? !middle.include?(2) end def suffix_gap? !middle.include?(total - 1) end def render_item(item) case item when :prev prev_item when :next next_item when :gap gap_item else link_to_page(item, page: item) end end def prev_item icon = template.content_tag('span', "\u25C0", 'aria-hidden': 'true') return disabled_item(icon) if current < 2 link_to_page(icon, page: current - 1, 'aria-label': 'Previous') end def next_item icon = template.content_tag('span', "\u25B6", 'aria-hidden': 'true') return disabled_item(icon) if current >= total link_to_page(icon, page: current + 1, 'aria-label': 'Next') end def gap_item template.content_tag('li') do template.content_tag('span', "\u2026", 'aria-hidden': 'true') end end def link_to_page(content, page:, **opts) li_opts = {} li_opts[:class] = 'active' if page == current url_opts = template.params.permit( :page, :per_page, :filter, :direction, :sort_by ).merge(page: page) opts = opts.merge(href: template.url_for(url_opts)) template.content_tag('li', li_opts) do template.content_tag('a', content, opts) end end def disabled_item(content) template.content_tag('li', content, class: 'disabled') end end end end