pagy 3.10.0 → 5.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/lib/config/pagy.rb +121 -52
  4. data/lib/javascripts/pagy-dev.js +117 -0
  5. data/lib/javascripts/pagy.js +1 -106
  6. data/lib/javascripts/pagy.mjs +118 -0
  7. data/lib/locales/ar.yml +26 -0
  8. data/lib/locales/bg.yml +2 -2
  9. data/lib/locales/bs.yml +24 -0
  10. data/lib/locales/ca.yml +2 -2
  11. data/lib/locales/cs.yml +2 -2
  12. data/lib/locales/da.yml +2 -2
  13. data/lib/locales/de.yml +2 -2
  14. data/lib/locales/en.yml +2 -2
  15. data/lib/locales/es.yml +2 -2
  16. data/lib/locales/fr.yml +2 -2
  17. data/lib/locales/hr.yml +24 -0
  18. data/lib/locales/id.yml +2 -2
  19. data/lib/locales/it.yml +2 -2
  20. data/lib/locales/ja.yml +2 -2
  21. data/lib/locales/km.yml +2 -2
  22. data/lib/locales/ko.yml +2 -2
  23. data/lib/locales/nb.yml +2 -2
  24. data/lib/locales/nl.yml +2 -2
  25. data/lib/locales/pl.yml +2 -2
  26. data/lib/locales/pt-BR.yml +2 -2
  27. data/lib/locales/pt.yml +2 -2
  28. data/lib/locales/ru.yml +2 -2
  29. data/lib/locales/sr.yml +23 -0
  30. data/lib/locales/sv-SE.yml +2 -2
  31. data/lib/locales/sv.yml +2 -2
  32. data/lib/locales/sw.yml +22 -0
  33. data/lib/locales/ta.yml +22 -0
  34. data/lib/locales/tr.yml +2 -2
  35. data/lib/locales/uk.yml +24 -0
  36. data/lib/locales/zh-CN.yml +2 -2
  37. data/lib/locales/zh-HK.yml +2 -2
  38. data/lib/locales/zh-TW.yml +3 -3
  39. data/lib/pagy/backend.rb +11 -12
  40. data/lib/pagy/calendar/day.rb +29 -0
  41. data/lib/pagy/calendar/month.rb +16 -0
  42. data/lib/pagy/calendar/month_mixin.rb +49 -0
  43. data/lib/pagy/calendar/quarter.rb +23 -0
  44. data/lib/pagy/calendar/week.rb +39 -0
  45. data/lib/pagy/calendar/year.rb +29 -0
  46. data/lib/pagy/calendar.rb +90 -0
  47. data/lib/pagy/console.rb +23 -0
  48. data/lib/pagy/countless.rb +23 -19
  49. data/lib/pagy/exceptions.rb +16 -13
  50. data/lib/pagy/extras/arel.rb +12 -7
  51. data/lib/pagy/extras/array.rb +10 -9
  52. data/lib/pagy/extras/bootstrap.rb +77 -39
  53. data/lib/pagy/extras/bulma.rb +77 -43
  54. data/lib/pagy/extras/calendar.rb +66 -0
  55. data/lib/pagy/extras/countless.rb +17 -17
  56. data/lib/pagy/extras/elasticsearch_rails.rb +66 -37
  57. data/lib/pagy/extras/foundation.rb +74 -41
  58. data/lib/pagy/extras/frontend_helpers.rb +70 -0
  59. data/lib/pagy/extras/gearbox.rb +42 -0
  60. data/lib/pagy/extras/headers.rb +32 -18
  61. data/lib/pagy/extras/i18n.rb +18 -17
  62. data/lib/pagy/extras/items.rb +42 -53
  63. data/lib/pagy/extras/materialize.rb +68 -43
  64. data/lib/pagy/extras/meilisearch.rb +61 -0
  65. data/lib/pagy/extras/metadata.rb +27 -26
  66. data/lib/pagy/extras/navs.rb +54 -29
  67. data/lib/pagy/extras/overflow.rb +57 -52
  68. data/lib/pagy/extras/searchkick.rb +54 -36
  69. data/lib/pagy/extras/semantic.rb +66 -39
  70. data/lib/pagy/extras/standalone.rb +64 -0
  71. data/lib/pagy/extras/support.rb +34 -17
  72. data/lib/pagy/extras/trim.rb +18 -12
  73. data/lib/pagy/extras/uikit.rb +66 -44
  74. data/lib/pagy/frontend.rb +61 -53
  75. data/lib/pagy/i18n.rb +164 -0
  76. data/lib/pagy/url_helpers.rb +38 -0
  77. data/lib/pagy.rb +96 -30
  78. data/lib/templates/bootstrap_nav.html.erb +1 -1
  79. data/lib/templates/bootstrap_nav.html.haml +1 -1
  80. data/lib/templates/bootstrap_nav.html.slim +1 -1
  81. data/lib/templates/foundation_nav.html.erb +1 -1
  82. data/lib/templates/foundation_nav.html.haml +1 -1
  83. data/lib/templates/foundation_nav.html.slim +1 -1
  84. data/lib/templates/uikit_nav.html.erb +2 -2
  85. data/lib/templates/uikit_nav.html.haml +1 -1
  86. data/lib/templates/uikit_nav.html.slim +2 -2
  87. metadata +37 -16
  88. data/lib/locales/README.md +0 -35
  89. data/lib/locales/utils/i18n.rb +0 -25
  90. data/lib/locales/utils/loader.rb +0 -34
  91. data/lib/locales/utils/p11n.rb +0 -88
  92. data/lib/pagy/extras/pagy_search.rb +0 -18
  93. data/lib/pagy/extras/shared.rb +0 -53
  94. data/pagy.gemspec +0 -16
@@ -0,0 +1,90 @@
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/calendar
2
+ # frozen_string_literal: true
3
+
4
+ require 'pagy'
5
+
6
+ class Pagy # :nodoc:
7
+ # Base class for time units subclasses (Year, Quarter, Month, Week, Day)
8
+ class Calendar < Pagy
9
+ # List of units in desc order of duration. It can be used for custom units.
10
+ UNITS = %i[year quarter month week day] # rubocop:disable Style/MutableConstant
11
+ DAY = 60 * 60 * 24 # One day in seconds
12
+ WEEK = DAY * 7 # One week in seconds
13
+
14
+ attr_reader :order
15
+
16
+ # Merge and validate the options, do some simple arithmetic and set a few instance variables
17
+ def initialize(vars) # rubocop:disable Lint/MissingSuper
18
+ raise InternalError, 'Pagy::Calendar is a base class; use one of its subclasses' if instance_of?(Pagy::Calendar)
19
+
20
+ vars = self.class::DEFAULT.merge(vars) # subclass specific default
21
+ normalize_vars(vars) # general default
22
+ setup_vars(page: 1)
23
+ setup_unit_vars
24
+ setup_params_var
25
+ raise OverflowError.new(self, :page, "in 1..#{@last}", @page) if @page > @last
26
+
27
+ @prev = (@page - 1 unless @page == 1)
28
+ @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
29
+ end
30
+
31
+ # The label for the current page (it can pass along the I18n gem opts when it's used with the i18n extra)
32
+ def label(opts = {})
33
+ label_for(@page, opts)
34
+ end
35
+
36
+ # The label for any page (it can pass along the I18n gem opts when it's used with the i18n extra)
37
+ def label_for(page, opts = {})
38
+ opts[:format] ||= @vars[:format]
39
+ localize(starting_time_for(page.to_i), opts) # page could be a string
40
+ end
41
+
42
+ protected
43
+
44
+ # Base class method for the setup of the unit variables (subclasses must implement it and call super)
45
+ def setup_unit_vars
46
+ raise VariableError.new(self, :format, 'to be a strftime format', @vars[:format]) unless @vars[:format].is_a?(String)
47
+ raise VariableError.new(self, :order, 'to be in [:asc, :desc]', @order) \
48
+ unless %i[asc desc].include?(@order = @vars[:order])
49
+
50
+ @starting, @ending = @vars[:period]
51
+ raise VariableError.new(self, :period, 'to be a an Array of min and max local Time instances', @vars[:period]) \
52
+ unless @starting.is_a?(Time) && @ending.is_a?(Time) && !@starting.utc? && !@ending.utc? && @starting <= @ending \
53
+ && (@utc_offset = @starting.utc_offset) == @ending.utc_offset
54
+ end
55
+
56
+ # Apply the strftime format to the time (overridden by the i18n extra when localization is required)
57
+ def localize(time, opts)
58
+ time.strftime(opts[:format])
59
+ end
60
+
61
+ # Number of units to offset from the @initial time, in order to get the ordered starting time for the page.
62
+ # Used in starting_time_for(page) with a logic equivalent to: @initial + (offset_units_for(page) * unit_time_length)
63
+ def offset_units_for(page)
64
+ @order == :asc ? page - 1 : @pages - page
65
+ end
66
+
67
+ # Create a new local time at the beginning of the day
68
+ def new_time(year, month = 1, day = 1)
69
+ Time.new(year, month, day, 0, 0, 0, @utc_offset)
70
+ end
71
+
72
+ # Period of the active page (used internally for nested units)
73
+ def active_period
74
+ [[@starting, @from].max, [@to - 1, @ending].min] # -1 sec: include only last unit day
75
+ end
76
+
77
+ class << self
78
+ # Create a subclass instance by unit name (internal use)
79
+ def create(unit, vars)
80
+ raise InternalError, "unit must be in #{UNITS.inspect}; got #{unit}" unless UNITS.include?(unit)
81
+
82
+ name = unit.to_s
83
+ name[0] = name[0].capitalize
84
+ Object.const_get("Pagy::Calendar::#{name}").new(vars)
85
+ end
86
+ end
87
+ end
88
+ # Require the subclass files in UNITS (no custom unit at this point yet)
89
+ Calendar::UNITS.each { |unit| require "pagy/calendar/#{unit}" }
90
+ end
@@ -0,0 +1,23 @@
1
+ # See Pagy::Console API documentation: https://ddnexus.github.io/pagy/api/console
2
+ # frozen_string_literal: true
3
+
4
+ require 'pagy' # so you can require just the extra in the console
5
+ require 'pagy/extras/standalone'
6
+
7
+ class Pagy
8
+ # Provide a ready to use pagy environment when included in irb/rails console
9
+ module Console
10
+ # Include Backend, Frontend and set the default URL
11
+ def self.included(main)
12
+ main.include(Backend)
13
+ main.include(Frontend)
14
+ DEFAULT[:url] = 'http://www.example.com/subdir'
15
+ end
16
+
17
+ # Require the extras passed as arguments
18
+ def pagy_extras(*extras)
19
+ extras.each { |extra| require "pagy/extras/#{extra}" }
20
+ puts "Required extras: #{extras.map(&:inspect).join(', ')}"
21
+ end
22
+ end
23
+ end
@@ -1,33 +1,37 @@
1
- # encoding: utf-8
1
+ # See Pagy::Countless API documentation: https://ddnexus.github.io/pagy/api/countless
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'pagy'
5
5
 
6
6
  class Pagy
7
-
7
+ # No need to know the count to paginate
8
8
  class Countless < Pagy
9
-
10
9
  # Merge and validate the options, do some simple arithmetic and set a few instance variables
11
- def initialize(vars={})
12
- @vars = VARS.merge(vars.delete_if{|_,v| v.nil? || v == '' }) # default vars + cleaned vars (can be overridden)
13
- { items:1, outset:0, page:1 }.each do |k,min| # validate instance variables
14
- (@vars[k] && instance_variable_set(:"@#{k}", @vars[k].to_i) >= min) \
15
- or raise(VariableError.new(self), "expected :#{k} >= #{min}; got #{@vars[k].inspect}")
16
- end
17
- @offset = @items * (@page - 1) + @outset # pagination offset + outset (initial offset)
10
+ def initialize(vars = {}) # rubocop:disable Lint/MissingSuper
11
+ normalize_vars(vars)
12
+ setup_vars(page: 1, outset: 0)
13
+ setup_items_var
14
+ setup_params_var
15
+ @offset = (@items * (@page - 1)) + @outset
18
16
  end
19
17
 
20
- # Finalize the instance variables based on the fetched items
21
- def finalize(fetched)
22
- fetched == 0 && @page > 1 and raise(OverflowError.new(self), "page #{@page} got no items")
23
- @pages = @last = (fetched > @items ? @page + 1 : @page) # set the @pages and @last
24
- @items = fetched if fetched < @items && fetched > 0 # adjust items for last non-empty page
25
- @from = fetched == 0 ? 0 : @offset+1 - @outset # page begins from item
26
- @to = fetched == 0 ? 0 : @offset + @items - @outset # page ends to item
27
- @prev = (@page-1 unless @page == 1) # nil if no prev page
28
- @next = @page == @last ? (1 if @vars[:cycle]) : @page+1 # nil if no next page, 1 if :cycle
18
+ # Finalize the instance variables based on the fetched size
19
+ def finalize(fetched_size)
20
+ raise OverflowError.new(self, :page, "to be < #{@page}", @page) if fetched_size.zero? && @page > 1
21
+
22
+ @pages = @last = (fetched_size > @items ? @page + 1 : @page)
23
+ @in = [fetched_size, @items].min
24
+ @from = @in.zero? ? 0 : @offset - @outset + 1
25
+ @to = @offset - @outset + @in
26
+ @prev = (@page - 1 unless @page == 1)
27
+ @next = @page == @last ? (1 if @vars[:cycle]) : @page + 1
29
28
  self
30
29
  end
31
30
 
31
+ # Override the original series.
32
+ # Return nil if :countless_minimal is enabled
33
+ def series(*)
34
+ super unless @vars[:countless_minimal]
35
+ end
32
36
  end
33
37
  end
@@ -1,22 +1,25 @@
1
- class Pagy
1
+ # frozen_string_literal: true
2
2
 
3
+ class Pagy
4
+ # Generic variable error
3
5
  class VariableError < ArgumentError
4
- attr_reader :pagy
5
-
6
- def initialize(pagy)
7
- @pagy = pagy
8
- end
6
+ attr_reader :pagy, :variable, :value
9
7
 
10
- def variable
11
- message =~ /expected :([\w]+)/
12
- $1.to_sym if $1
13
- end
14
-
15
- def value
16
- pagy.vars[variable]
8
+ # Set the variables and prepare the message
9
+ def initialize(pagy, variable, description, value)
10
+ @pagy = pagy
11
+ @variable = variable
12
+ @value = value
13
+ super "expected :#{@variable} #{description}; got #{@value.inspect}"
17
14
  end
18
15
  end
19
16
 
17
+ # Specific overflow error
20
18
  class OverflowError < VariableError; end
21
19
 
20
+ # I18n configuration error
21
+ class I18nError < StandardError; end
22
+
23
+ # Generic internal error
24
+ class InternalError < StandardError; end
22
25
  end
@@ -1,21 +1,26 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/arel
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- class Pagy
6
- module Backend ; private
4
+ class Pagy # :nodoc:
5
+ # Better performance of grouped ActiveRecord collections
6
+ module ArelExtra
7
+ private
7
8
 
8
- def pagy_arel(collection, vars={})
9
+ # Return Pagy object and paginated collection/results
10
+ def pagy_arel(collection, vars = {})
9
11
  pagy = Pagy.new(pagy_arel_get_vars(collection, vars))
10
- return pagy, pagy_get_items(collection, pagy)
12
+ [pagy, pagy_get_items(collection, pagy)]
11
13
  end
12
14
 
15
+ # Sub-method called only by #pagy_arel: here for easy customization of variables by overriding
13
16
  def pagy_arel_get_vars(collection, vars)
17
+ pagy_set_items_from_params(vars) if defined?(ItemsExtra)
14
18
  vars[:count] ||= pagy_arel_count(collection)
15
- vars[:page] ||= params[ vars[:page_param] || VARS[:page_param] ]
19
+ vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
16
20
  vars
17
21
  end
18
22
 
23
+ # Count using Arel when grouping
19
24
  def pagy_arel_count(collection)
20
25
  if collection.group_values.empty?
21
26
  # COUNT(*)
@@ -26,6 +31,6 @@ class Pagy
26
31
  collection.unscope(:order).limit(1).pluck(sql).first.to_i
27
32
  end
28
33
  end
29
-
30
34
  end
35
+ Backend.prepend ArelExtra
31
36
  end
@@ -1,23 +1,24 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/array
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- class Pagy
6
- # Add specialized backend methods to paginate array collections
7
- module Backend ; private
4
+ class Pagy # :nodoc:
5
+ # Paginate arrays efficiently, avoiding expensive array-wrapping and without overriding
6
+ module ArrayExtra
7
+ private
8
8
 
9
- # Return Pagy object and items
10
- def pagy_array(array, vars={})
9
+ # Return Pagy object and paginated items
10
+ def pagy_array(array, vars = {})
11
11
  pagy = Pagy.new(pagy_array_get_vars(array, vars))
12
- return pagy, array[pagy.offset, pagy.items]
12
+ [pagy, array[pagy.offset, pagy.items]]
13
13
  end
14
14
 
15
15
  # Sub-method called only by #pagy_array: here for easy customization of variables by overriding
16
16
  def pagy_array_get_vars(array, vars)
17
+ pagy_set_items_from_params(vars) if defined?(ItemsExtra)
17
18
  vars[:count] ||= array.size
18
- vars[:page] ||= params[ vars[:page_param] || VARS[:page_param] ]
19
+ vars[:page] ||= params[vars[:page_param] || DEFAULT[:page_param]]
19
20
  vars
20
21
  end
21
-
22
22
  end
23
+ Backend.prepend ArrayExtra
23
24
  end
@@ -1,55 +1,93 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/bootstrap
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- require 'pagy/extras/shared'
6
-
7
- class Pagy
8
- module Frontend
4
+ require 'pagy/extras/frontend_helpers'
9
5
 
6
+ class Pagy # :nodoc:
7
+ # Frontend modules are specially optimized for performance.
8
+ # The resulting code may not look very elegant, but produces the best benchmarks
9
+ module BootstrapExtra
10
10
  # Pagination for bootstrap: it returns the html with the series of links to the pages
11
- def pagy_bootstrap_nav(pagy)
12
- link, p_prev, p_next = pagy_link_proc(pagy, 'class="page-link"'), pagy.prev, pagy.next
13
-
14
- html = EMPTY + (p_prev ? %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
15
- : %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev')}</a></li>))
16
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
17
- html << if item.is_a?(Integer); %(<li class="page-item">#{link.call item}</li>) # page link
18
- elsif item.is_a?(String) ; %(<li class="page-item active">#{link.call item}</li>) # active page
19
- elsif item == :gap ; %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap')}</a></li>) # page gap
11
+ def pagy_bootstrap_nav(pagy, pagy_id: nil, link_extra: '', **vars)
12
+ p_id = %( id="#{pagy_id}") if pagy_id
13
+ link = pagy_link_proc(pagy, link_extra: %(class="page-link" #{link_extra}))
14
+
15
+ html = +%(<nav#{p_id} class="pagy-bootstrap-nav" aria-label="pager"><ul class="pagination">)
16
+ html << pagy_bootstrap_prev_html(pagy, link)
17
+ pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
18
+ html << case item
19
+ when Integer
20
+ %(<li class="page-item">#{link.call item}</li>)
21
+ when String
22
+ %(<li class="page-item active">#{link.call item}</li>)
23
+ when :gap
24
+ %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.gap'}</a></li>)
25
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
20
26
  end
21
27
  end
22
- html << (p_next ? %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
23
- : %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next')}</a></li>))
24
- %(<nav class="pagy-bootstrap-nav" role="navigation" aria-label="pager"><ul class="pagination">#{html}</ul></nav>)
28
+ html << pagy_bootstrap_next_html(pagy, link)
29
+ html << %(</ul></nav>)
25
30
  end
26
31
 
27
- # Javascript pagination for bootstrap: it returns a nav and a JSON tag used by the Pagy.nav javascript
28
- def pagy_bootstrap_nav_js(pagy, id=pagy_id)
29
- link, p_prev, p_next = pagy_link_proc(pagy, 'class="page-link"'), pagy.prev, pagy.next
30
- tags = { 'before' => p_prev ? %(<ul class="pagination"><li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
31
- : %(<ul class="pagination"><li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.prev')}</a></li>),
32
- 'link' => %(<li class="page-item">#{mark = link.call(PAGE_PLACEHOLDER)}</li>),
32
+ # Javascript pagination for bootstrap: it returns a nav and a JSON tag used by the pagy.js file
33
+ def pagy_bootstrap_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
34
+ sequels = pagy.sequels(**vars)
35
+ p_id = %( id="#{pagy_id}") if pagy_id
36
+ link = pagy_link_proc(pagy, link_extra: %(class="page-link" #{link_extra}))
37
+ tags = { 'before' => %(<ul class="pagination">#{pagy_bootstrap_prev_html pagy, link}),
38
+ 'link' => %(<li class="page-item">#{mark = link.call(PAGE_PLACEHOLDER, LABEL_PLACEHOLDER)}</li>),
33
39
  'active' => %(<li class="page-item active">#{mark}</li>),
34
- 'gap' => %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.gap')}</a></li>),
35
- 'after' => p_next ? %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li></ul>)
36
- : %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t('pagy.nav.next')}</a></li></ul>) }
37
- %(<nav id="#{id}" class="pagy-bootstrap-nav-js" role="navigation" aria-label="pager"></nav>#{pagy_json_tag(:nav, id, tags, pagy.sequels, defined?(TRIM) && pagy.vars[:page_param])})
40
+ 'gap' => %(<li class="page-item gap disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.gap'}</a></li>),
41
+ 'after' => %(#{pagy_bootstrap_next_html pagy, link}</ul>) }
42
+
43
+ %(<nav#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-bootstrap-nav-js" aria-label="pager" #{
44
+ pagy_json_attr(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></nav>)
45
+ end
46
+
47
+ # Javascript combo pagination for bootstrap: it returns a nav and a JSON tag used by the pagy.js file
48
+ def pagy_bootstrap_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
49
+ p_id = %( id="#{pagy_id}") if pagy_id
50
+ link = pagy_link_proc(pagy, link_extra: link_extra)
51
+ p_page = pagy.page
52
+ p_pages = pagy.pages
53
+ input = %(<input type="number" min="1" max="#{p_pages}" value="#{
54
+ p_page}" class="text-primary" style="padding: 0; border: none; text-align: center; width: #{
55
+ p_pages.to_s.length + 1}rem;">)
56
+
57
+ %(<nav#{p_id} class="pagy-bootstrap-combo-nav-js pagination" aria-label="pager"><div class="btn-group" role="group" #{
58
+ pagy_json_attr pagy, :combo, pagy_marked_link(link)}>#{
59
+ if (p_prev = pagy.prev)
60
+ link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous" class="prev btn btn-primary"'
61
+ else
62
+ %(<a class="prev btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.prev')}</a>)
63
+ end
64
+ }<div class="pagy-combo-input btn btn-primary disabled" style="white-space: nowrap;">#{
65
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</div>#{
66
+ if (p_next = pagy.next)
67
+ link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next" class="next btn btn-primary"'
68
+ else
69
+ %(<a class="next btn btn-primary disabled" href="#">#{pagy_t 'pagy.nav.next'}</a>)
70
+ end
71
+ }</div></nav>)
38
72
  end
39
73
 
40
- # Javascript combo pagination for bootstrap: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
41
- def pagy_bootstrap_combo_nav_js(pagy, id=pagy_id)
42
- link, p_prev, p_next, p_page, p_pages = pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
43
-
44
- html = %(<nav id="#{id}" class="pagy-bootstrap-combo-nav-js pagination" role="navigation" aria-label="pager">) + %(<div class="btn-group" role="group">)
45
- html << (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous" class="prev btn btn-primary"')
46
- : %(<a class="prev btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.prev')}</a>))
47
- input = %(<input type="number" min="1" max="#{p_pages}" value="#{p_page}" class="text-primary" style="padding: 0; border: none; text-align: center; width: #{p_pages.to_s.length+1}rem;">)
48
- html << %(<div class="pagy-combo-input btn btn-primary disabled" style="white-space: nowrap;">#{pagy_t('pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages)}</div>)
49
- html << (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'aria-label="next" class="next btn btn-primary"')
50
- : %(<a class="next btn btn-primary disabled" href="#">#{pagy_t('pagy.nav.next')}</a>))
51
- html << %(</div></nav>#{pagy_json_tag(:combo_nav, id, p_page, pagy_marked_link(link), defined?(TRIM) && pagy.vars[:page_param])})
74
+ private
75
+
76
+ def pagy_bootstrap_prev_html(pagy, link)
77
+ if (p_prev = pagy.prev)
78
+ %(<li class="page-item prev">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'aria-label="previous"'}</li>)
79
+ else
80
+ %(<li class="page-item prev disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.prev'}</a></li>)
81
+ end
52
82
  end
53
83
 
84
+ def pagy_bootstrap_next_html(pagy, link)
85
+ if (p_next = pagy.next)
86
+ %(<li class="page-item next">#{link.call p_next, pagy_t('pagy.nav.next'), 'aria-label="next"'}</li>)
87
+ else
88
+ %(<li class="page-item next disabled"><a href="#" class="page-link">#{pagy_t 'pagy.nav.next'}</a></li>)
89
+ end
90
+ end
54
91
  end
92
+ Frontend.prepend BootstrapExtra
55
93
  end
@@ -1,60 +1,94 @@
1
1
  # See the Pagy documentation: https://ddnexus.github.io/pagy/extras/bulma
2
- # encoding: utf-8
3
2
  # frozen_string_literal: true
4
3
 
5
- require 'pagy/extras/shared'
4
+ require 'pagy/extras/frontend_helpers'
6
5
 
7
- class Pagy
8
- module Frontend
6
+ class Pagy # :nodoc:
7
+ # Frontend modules are specially optimized for performance.
8
+ # The resulting code may not look very elegant, but produces the best benchmarks
9
+ module BulmaExtra
10
+ # Pagination for bulma: it returns the html with the series of links to the pages
11
+ def pagy_bulma_nav(pagy, pagy_id: nil, link_extra: '', **vars)
12
+ p_id = %( id="#{pagy_id}") if pagy_id
13
+ link = pagy_link_proc(pagy, link_extra: link_extra)
9
14
 
10
- # Pagination for Bulma: it returns the html with the series of links to the pages
11
- def pagy_bulma_nav(pagy)
12
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
13
-
14
- html = (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'class="pagination-previous" aria-label="previous page"')
15
- : %(<a class="pagination-previous" disabled>#{pagy_t('pagy.nav.prev')}</a>)) \
16
- + (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'class="pagination-next" aria-label="next page"')
17
- : %(<a class="pagination-next" disabled>#{pagy_t('pagy.nav.next')}</a>))
18
- html << '<ul class="pagination-list">'
19
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
20
- html << if item.is_a?(Integer); %(<li>#{link.call item, item, %(class="pagination-link" aria-label="goto page #{item}") }</li>) # page link
21
- elsif item.is_a?(String) ; %(<li>#{link.call item, item, %(class="pagination-link is-current" aria-label="page #{item}" aria-current="page")}</li>) # active page
22
- elsif item == :gap ; %(<li><span class="pagination-ellipsis">#{pagy_t('pagy.nav.gap')}</span></li>) # page gap
15
+ html = +%(<nav#{p_id} class="pagy-bulma-nav pagination is-centered" aria-label="pagination">)
16
+ html << pagy_bulma_prev_next_html(pagy, link)
17
+ html << %(<ul class="pagination-list">)
18
+ pagy.series(**vars).each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
19
+ html << case item
20
+ when Integer
21
+ %(<li>#{link.call item, pagy.label_for(item), %(class="pagination-link" aria-label="goto page #{item}")}</li>)
22
+ when String
23
+ %(<li>#{link.call item, pagy.label_for(item),
24
+ %(class="pagination-link is-current" aria-label="page #{item}" aria-current="page")}</li>)
25
+ when :gap
26
+ %(<li><span class="pagination-ellipsis">#{pagy_t 'pagy.nav.gap'}</span></li>)
27
+ else raise InternalError, "expected item types in series to be Integer, String or :gap; got #{item.inspect}"
23
28
  end
24
29
  end
25
- html << '</ul>'
26
- %(<nav class="pagy-bulma-nav pagination is-centered" role="navigation" aria-label="pagination">#{html}</nav>)
30
+ html << %(</ul></nav>)
27
31
  end
28
32
 
29
33
  # Javascript pagination for bulma: it returns a nav and a JSON tag used by the Pagy.nav javascript
30
- def pagy_bulma_nav_js(pagy, id=pagy_id)
31
- link, p_prev, p_next = pagy_link_proc(pagy), pagy.prev, pagy.next
32
- tags = { 'before' => ( (p_prev ? link.call(p_prev, pagy_t('pagy.nav.prev'), 'class="pagination-previous" aria-label="previous page"')
33
- : %(<a class="pagination-previous" disabled>#{pagy_t('pagy.nav.prev')}</a>)) \
34
- + (p_next ? link.call(p_next, pagy_t('pagy.nav.next'), 'class="pagination-next" aria-label="next page"')
35
- : %(<a class="pagination-next" disabled>#{pagy_t('pagy.nav.next')}</a>)) \
36
- + '<ul class="pagination-list">' ),
37
- 'link' => %(<li>#{link.call(PAGE_PLACEHOLDER, PAGE_PLACEHOLDER, %(class="pagination-link" aria-label="goto page #{PAGE_PLACEHOLDER}"))}</li>),
38
- 'active' => %(<li>#{link.call(PAGE_PLACEHOLDER, PAGE_PLACEHOLDER, %(class="pagination-link is-current" aria-current="page" aria-label="page #{PAGE_PLACEHOLDER}"))}</li>),
39
- 'gap' => %(<li><span class="pagination-ellipsis">#{pagy_t('pagy.nav.gap')}</span></li>),
34
+ def pagy_bulma_nav_js(pagy, pagy_id: nil, link_extra: '', **vars)
35
+ sequels = pagy.sequels(**vars)
36
+ p_id = %( id="#{pagy_id}") if pagy_id
37
+ link = pagy_link_proc(pagy, link_extra: link_extra)
38
+ tags = { 'before' => %(#{pagy_bulma_prev_next_html(pagy, link)}<ul class="pagination-list">),
39
+ 'link' => %(<li>#{link.call PAGE_PLACEHOLDER, LABEL_PLACEHOLDER,
40
+ %(class="pagination-link" aria-label="goto page #{PAGE_PLACEHOLDER}")}</li>),
41
+ 'active' => %(<li>#{link.call PAGE_PLACEHOLDER, LABEL_PLACEHOLDER,
42
+ %(class="pagination-link is-current" aria-current="page" aria-label="page #{
43
+ PAGE_PLACEHOLDER}")}</li>),
44
+ 'gap' => %(<li><span class="pagination-ellipsis">#{pagy_t 'pagy.nav.gap'}</span></li>),
40
45
  'after' => '</ul>' }
41
- %(<nav id="#{id}" class="pagy-bulma-nav-js pagination is-centered" role="navigation" aria-label="pagination"></nav>#{pagy_json_tag(:nav, id, tags, pagy.sequels, defined?(TRIM) && pagy.vars[:page_param])})
46
+
47
+ %(<nav#{p_id} class="#{'pagy-rjs ' if sequels.size > 1}pagy-bulma-nav-js pagination is-centered" aria-label="pagination" #{
48
+ pagy_json_attr(pagy, :nav, tags, sequels, pagy.label_sequels(sequels))}></nav>)
42
49
  end
43
50
 
44
- # Javascript combo pagination for Bulma: it returns a nav and a JSON tag used by the Pagy.combo_nav javascript
45
- def pagy_bulma_combo_nav_js(pagy, id=pagy_id)
46
- link, p_prev, p_next, p_page, p_pages = pagy_link_proc(pagy), pagy.prev, pagy.next, pagy.page, pagy.pages
47
-
48
- html = %(<nav id="#{id}" class="pagy-bulma-combo-nav-js" role="navigation" aria-label="pagination">) \
49
- + %(<div class="field is-grouped is-grouped-centered" role="group">)
50
- html << (p_prev ? %(<p class="control">#{link.call(p_prev, pagy_t('pagy.nav.prev'), 'class="button" aria-label="previous page"')}</p>)
51
- : %(<p class="control"><a class="button" disabled>#{pagy_t('pagy.nav.prev')}</a></p>))
52
- input = %(<input class="input" type="number" min="1" max="#{p_pages}" value="#{p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length+1}rem; margin:0 0.3rem;">)
53
- html << %(<div class="pagy-combo-input control level is-mobile">#{pagy_t('pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages)}</div>)
54
- html << (p_next ? %(<p class="control">#{link.call(p_next, pagy_t('pagy.nav.next'), 'class="button" aria-label="next page"')}</p>)
55
- : %(<p class="control"><a class="button" disabled>#{pagy_t('pagy.nav.next')}</a></p>))
56
- html << %(</div></nav>#{pagy_json_tag(:combo_nav, id, p_page, pagy_marked_link(link), defined?(TRIM) && pagy.vars[:page_param])})
51
+ # Javascript combo pagination for bulma: it returns a nav and a JSON tag used by the pagy.js file
52
+ def pagy_bulma_combo_nav_js(pagy, pagy_id: nil, link_extra: '')
53
+ p_id = %( id="#{pagy_id}") if pagy_id
54
+ link = pagy_link_proc(pagy, link_extra: link_extra)
55
+ p_page = pagy.page
56
+ p_pages = pagy.pages
57
+ input = %(<input class="input" type="number" min="1" max="#{p_pages}" value="#{
58
+ p_page}" style="padding: 0; text-align: center; width: #{p_pages.to_s.length + 1}rem; margin:0 0.3rem;">)
59
+
60
+ html = %(<nav#{p_id} class="pagy-bulma-combo-nav-js" aria-label="pagination">)
61
+ %(#{html}<div class="field is-grouped is-grouped-centered" role="group" #{
62
+ pagy_json_attr pagy, :combo, pagy_marked_link(link)}>#{
63
+ if (p_prev = pagy.prev)
64
+ %(<p class="control">#{link.call p_prev, pagy_t('pagy.nav.prev'), 'class="button" aria-label="previous page"'}</p>)
65
+ else
66
+ %(<p class="control"><a class="button" disabled>#{pagy_t 'pagy.nav.prev'}</a></p>)
67
+ end
68
+ }<div class="pagy-combo-input control level is-mobile">#{
69
+ pagy_t 'pagy.combo_nav_js', page_input: input, count: p_page, pages: p_pages}</div>#{
70
+ if (p_next = pagy.next)
71
+ %(<p class="control">#{link.call p_next, pagy_t('pagy.nav.next'), 'class="button" aria-label="next page"'}</p>)
72
+ else
73
+ %(<p class="control"><a class="button" disabled>#{pagy_t 'pagy.nav.next'}</a></p>)
74
+ end
75
+ }</div></nav>)
57
76
  end
58
77
 
78
+ private
79
+
80
+ def pagy_bulma_prev_next_html(pagy, link)
81
+ html = +if (p_prev = pagy.prev)
82
+ link.call p_prev, pagy_t('pagy.nav.prev'), 'class="pagination-previous" aria-label="previous page"'
83
+ else
84
+ %(<a class="pagination-previous" disabled>#{pagy_t 'pagy.nav.prev'}</a>)
85
+ end
86
+ html << if (p_next = pagy.next)
87
+ link.call p_next, pagy_t('pagy.nav.next'), 'class="pagination-next" aria-label="next page"'
88
+ else
89
+ %(<a class="pagination-next" disabled>#{pagy_t 'pagy.nav.next'}</a>)
90
+ end
91
+ end
59
92
  end
93
+ Frontend.prepend BulmaExtra
60
94
  end