Skip to content

Commit

Permalink
Added items extra and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnexus committed Jun 17, 2018
1 parent 5902e8a commit 623ce9e
Show file tree
Hide file tree
Showing 11 changed files with 384 additions and 21 deletions.
24 changes: 13 additions & 11 deletions README.md
Expand Up @@ -78,25 +78,29 @@ _(see [Quick Start](https://ddnexus.github.io/pagy/how-to#quick-start))_

Use the official extras, or write your own in just a few lines.

### Bootstrap Extra

Nav helper and templates for Bootstrap pagination. _(see [more...](http://ddnexus.github.io/pagy/extras/bootstrap))_
### Array Extra

### Responsive Extra
Paginate arrays efficiently avoiding expensive array-wrapping and without any overriding. _(see [more...](http://ddnexus.github.io/pagy/extras/array))_

On resize, the number of page links will adapt in real-time to the available window or container width. _(see [more...](http://ddnexus.github.io/pagy/extras/responsive))_
### Bootstrap Extra

![pagy-responsive](docs/assets/images/pagy-responsive-w.png)
Nav helper and templates for Bootstrap pagination. _(see [more...](http://ddnexus.github.io/pagy/extras/bootstrap))_

### Compact Extra

An alternative UI that combines the pagination feature with the navigation info in one compact element. _(see [more...](http://ddnexus.github.io/pagy/extras/compact))_

![pagy-compact](docs/assets/images/pagy-compact-w.png)

### Array Extra
## Items Extra

Paginate arrays efficiently avoiding expensive array-wrapping and without any overriding. _(see [more...](http://ddnexus.github.io/pagy/extras/array))_
Allow the client to request a custom number of items per page with a ready to use UI selector. _(see [more...](http://ddnexus.github.io/pagy/extras/items))_

### Responsive Extra

On resize, the number of page links will adapt in real-time to the available window or container width. _(see [more...](http://ddnexus.github.io/pagy/extras/responsive))_

![pagy-responsive](docs/assets/images/pagy-responsive-w.png)

## Chat Support and Feedback

Expand All @@ -115,9 +119,7 @@ Paginate arrays efficiently avoiding expensive array-wrapping and without any ov

## Please Star and Share!

Pagy is young and needs to be known.

**You** can really help, even with just a click. Thank you!
Pagy is young and needs to be known, and **you** can really help, even with just a click on the star, or sharing a tweet with friends and collegues. Thank you!

## Help Wanted

Expand Down
20 changes: 17 additions & 3 deletions Rakefile
Expand Up @@ -3,12 +3,26 @@ require "bundler/gem_tasks"
require "rake/testtask"
require "rubocop/rake_task"

Rake::TestTask.new(:test) do |t|
# The extras that override the built-in methods need to be tested in isolation in order
# to prevent them to change also the behavior and the result of the built-in tests.
# We exclude them from the :test_common task and create a new task for them, then added to the :default task

Rake::TestTask.new(:test_common) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/*_test.rb"]
t.test_files = FileList.new.include("test/**/*_test.rb").exclude('test/**/items_test.rb')
end

RuboCop::RakeTask.new
Rake::TestTask.new(:test_extra_items) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList['test/**/items_test.rb']
end

task :test => [:test_common, :test_extra_items]

RuboCop::RakeTask.new(:rubocop) do |t|
t.options = `git ls-files -z`.split("\x0") # limit rubocop to the files in the repo
end

task :default => [:test, :rubocop]
1 change: 1 addition & 0 deletions docs/_layouts/default.html
Expand Up @@ -37,6 +37,7 @@
<a href="{{ site.baseurl }}/extras/bootstrap"><p class="indent1" {% if page.title == 'Bootstrap' %}id="active"{% endif %} >Bootstrap</p></a>
<a href="{{ site.baseurl }}/extras/compact"><p class="indent1" {% if page.title == 'Compact' %}id="active"{% endif %} >Compact</p></a>
<a href="{{ site.baseurl }}/extras/i18n"><p class="indent1" {% if page.title == 'I18n' %}id="active"{% endif %} >I18n</p></a>
<a href="{{ site.baseurl }}/extras/items"><p class="indent1" {% if page.title == 'Items' %}id="active"{% endif %} >Items</p></a>
<a href="{{ site.baseurl }}/extras/responsive"><p class="indent1" {% if page.title == 'Responsive' %}id="active"{% endif %} >Responsive</p></a>
<a href="{{ site.baseurl }}/migration-tips"><p {% if page.title == 'Migration Tips' %}id="active"{% endif %} >Migration Tips</p></a>
<p id="gitter-support"><a href="https://gitter.im/ruby-pagy/Lobby" rel="nofollow" target="_blank">&gt; Chat Support on Gitter &lt;</a></p>
Expand Down
6 changes: 3 additions & 3 deletions docs/assets/css/style.scss
Expand Up @@ -50,17 +50,17 @@ header {
}

#toc {
margin: 2em 0;
margin: 1.5em 0;
white-space: nowrap;
p {
margin: .4em 0;
margin: .2em 0;
}
p.indent1 {
margin-left: 2rem;
}
p#gitter-support {
font-weight: bold;
margin-top: 2em;
margin-top: 1.5em;
a:hover {
font-weight: bold;
}
Expand Down
3 changes: 2 additions & 1 deletion docs/extras.md
Expand Up @@ -6,11 +6,12 @@ title: Extras
Pagy comes with a few optional extensions/extras:

| Extra | Description | Links |
|--------------|--------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ------------ | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `array` | Paginate arrays efficiently avoiding expensive array-wrapping and wihout overriding | [array.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/array.rb), [documentation](extras/array.md) |
| `bootstrap` | Nav helper and templates for Bootstrap pagination | [bootstrap.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/bootstrap.rb), [documentation](extras/bootstrap.md) |
| `compact` | An alternative UI that combines the pagination with the nav info in a single compact element | [compact.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/compact.rb), [pagy-compact.js](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/javascripts/pagy-compact.js), [documentation](extras/compact.md) |
| `i18n` | Use the `I18n` gem instead of the pagy implementation | [i18n.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/i81n.rb), [documentation](extras/i18n.md) |
| `items` | Allow the client to request a custom number of items per page with a ready to use UI selector | [items.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/items.rb), [documentation](extras/items.md) |
| `responsive` | On resize, the number of page links will adapt in real-time to the available window or container width | [responsive.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/responsive.rb), [pagy-responsive.js](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/javascripts/pagy-responsive.js), [documentation](extras/responsive.md) |

## Synopsys
Expand Down
99 changes: 99 additions & 0 deletions docs/extras/items.md
@@ -0,0 +1,99 @@
---
title: Items
---
# Items Extra

Allow the client to request a custom number of items per page with a ready to use UI selector. It is useful with APIs or higly user-customizable apps.

## Synopsys

See [extras](../extras.md) for general usage info.

In the Pagy initializer:

```ruby
require 'pagy/extra/items'

Pagy::VARS[:items_param] = :custom_param # default :items
Pagy::VARS[:max_items] = 100 # default

# in rails apps: add the assets-path (only required if you use the pagy_items_selector helper)
Rails.application.config.assets.paths << Pagy.root.join('pagy', 'extras', 'javascripts')
```

In rails: add the javascript file to the application.js

```js
// only required if you use the pagy_items_selector helper
//= require pagy-items
```

In non-rails apps: ensure the `pagy/extras/javascripts/pagy-items.js` script gets served with the page.

Then you may want to use the `pagy_items_selector` helper in any view:

```erb
<%== pagy_items_selector(@pagy) %>
```

## Files

This extra is composed of 2 small files:

- [items.rb](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/items.rb)
- [pagy-items.js](https://github.com/ddnexus/pagy/blob/master/lib/pagy/extras/javascripts/pagy-items.js)

## Variables

| Variable | Description | Default |
| -------------- | -------------------------------------------------------------------- | -------- |
| `:items_param` | the name of the items param used in the url. | `:items` |
| `:max_items` | the max items allowed to be requested. Set it to `nil` for no limit. | `100` |

This extra uses the `:items_param` variable to determine the param it should get the number of `:items` from.

The `:max_items` is used to cap the number of items to that max. It is set to `100` by default. If you don't want to limit the max requested number of items per page, you can set it to `nil`.

You may want to customize the variables. Depending on the scope of the customization, you have a couple of options:

As a global default:

```ruby
Pagy::VARS[:items_param] = :custom_param
Pagy::VARS[:max_items] = 50
```

For a single instance (overriding the global default):

```ruby
pagy(scope, items_param: :custom_param, max_items: 50)
Pagy.new(count:100, items_param: :custom_param, max_items: 50)
```

**Notice**: you can override items that the client send with the params by passing the `:items` explicitly. For example:

```ruby
# this will ignore the params[:item] (or any custom :param_name)
# from the client for this instance, and serve 30 items
pagy(scope, items: 30)
```

## Methods

The `items` extra overrides a couple of builtin methods and adds a helper to the `Pagy::Frontend` module.

### pagy_get_vars(collection, vars)

This extra overrides the `pagy_get_vars` method of the `Pagy::Backend` module in order to set the `:items` variable, pulled from the request-params. The built-in `pagy_get_vars` method is aliased as `built_in_pagy_get_vars` and is called internally and still available.

### pagy_url_for(page, pagy)

This extra overrides also the `pagy_url_for` method of the `Pagy::Frontend` module in order to add the `:items_param` param to the url of the links.

### pagy_items_selector(pagy)

This helper provides an items selector UI, which allows the user to select any arbitrary number of items per page below the `:max_items` number in a numeric input field. It looks like:

<span>Show <input type="number" min="1" max="100" value="20" style="padding: 0; text-align: center; width: 3rem;"> items per page</span>

When the items number is changed with the selector, pagy will reload the pagination UI using the selected items per page. It will also request the _right_ page number calculated in order to contain the first item of the previously displayed page. That way the new displayed page will roughly show the same items in the collection.
7 changes: 5 additions & 2 deletions lib/locales/pagy.yml
Expand Up @@ -17,5 +17,8 @@ en:
one: "item"
other: "items"
compact:
page: Page
of: of
page: "Page"
of: "of"
items:
show: "Show"
items: "items per page"
8 changes: 7 additions & 1 deletion lib/pagy/extras/initializer_example.rb
Expand Up @@ -21,6 +21,12 @@
# See https://ddnexus.github.io/pagy/extras/i18n
# require 'pagy/extras/i18n'

# Items: Handle the page :items passed with the params
# See https://ddnexus.github.io/pagy/extras/items
# require 'pagy/extras/items'
# Pagy::VARS[:items_param] = :items # default
# Pagy::VARS[:max_items] = 100 # default

# Responsive: On resize, the number of page links will adapt in real-time to the available window or container width
# See https://ddnexus.github.io/pagy/extras/responsive
# require 'pagy/extras/responsive'
Expand Down Expand Up @@ -49,6 +55,6 @@
# Pagy::Frontend::I18N.load_file('path/to/dictionary.yml') # load a custom file


# Rails: extras assets path required by compact or responsive extras
# Rails: extras assets path required by compact, items qnd responsive extras
# See https://ddnexus.github.io/pagy/extras/compact and https://ddnexus.github.io/pagy/extras/responsive
# Rails.application.config.assets.paths << Pagy.root.join('pagy', 'extras', 'javascripts')
44 changes: 44 additions & 0 deletions lib/pagy/extras/items.rb
@@ -0,0 +1,44 @@
# See the Pagy documentation: https://ddnexus.github.io/pagy/extras/items

class Pagy

# Default variables for this extra
VARS[:items_param] = :items
VARS[:max_items] = 100

# Handle a custom number of :items from params
module Backend ; private

alias_method :built_in_pagy_get_vars, :pagy_get_vars

def pagy_get_vars(collection, vars)
vars[:items] ||= (items = params[vars[:items_param] || VARS[:items_param]]) && # :items from :items_param
[items&.to_i, vars.key?(:max_items) ? vars[:max_items] : VARS[:max_items]].compact.min # :items capped to :max_items
built_in_pagy_get_vars(collection, vars)
end

end

module Frontend

# this works with all Rack-based frameworks (Sinatra, Padrino, Rails, ...)
def pagy_url_for(page, pagy)
p_vars = pagy.vars; params = request.GET.merge(p_vars[:page_param] => page, p_vars[:items_param] => p_vars[:items], **p_vars[:params])
"#{request.path}?#{Rack::Utils.build_nested_query(pagy_get_params(params))}#{p_vars[:anchor]}"
end

# return the items selector HTML. For example "Show [20] items per page"
def pagy_items_selector(pagy, id=caller(1,1)[0].hash)
pagy = pagy.clone; p_vars = pagy.vars; p_items = p_vars[:items]; p_vars[:items] = "#{MARKER}-items-"

tags = %(<span id="pagy-items-#{id}">)

tags << %(<a href="#{pagy_url_for("#{MARKER}-page-", pagy)}"></a>)
input = %(<input type="number" min="1" max="#{p_vars[:max_items]}" value="#{p_items}" style="padding: 0; text-align: center; width: #{p_items.to_s.length+1}rem;">)
tags << %(#{pagy_t('pagy.items.show'.freeze)} #{input} #{pagy_t('pagy.items.items'.freeze)})

tags << %(</span><script>PagyItems('#{id}', '#{MARKER}', #{pagy.from});</script>)
end

end
end
26 changes: 26 additions & 0 deletions lib/pagy/extras/javascripts/pagy-items.js
@@ -0,0 +1,26 @@
// See the Pagy documentation: https://ddnexus.github.io/pagy/extras/items

function PagyItems(id, marker, from){
var pagyNav = document.getElementById('pagy-items-'+id),
input = pagyNav.getElementsByTagName('input')[0],
current = input.value,
link = pagyNav.getElementsByTagName('a')[0];

this.go = function(){
var items = input.value;
if (current !== items) {
var page = Math.max(Math.ceil(from / items),1);
var href = link.getAttribute('href').replace(marker+'-page-', page).replace(marker+'-items-', items);
link.setAttribute('href', href);
link.click();
}
};

// select the content on click: easier for typing a number
input.addEventListener('click', function(){ this.select() });
// jump to page number from input when the input looses focus
input.addEventListener('focusout', this.go);
// … and when pressing enter inside the input
input.addEventListener('keyup', function(e){ if (e.which === 13) this.go() }.bind(this));

}

0 comments on commit 623ce9e

Please sign in to comment.