Skip to content

Commit

Permalink
javascript responsive refactoring:
Browse files Browse the repository at this point in the history
- replaced problematic loop functions with plain for loops
- better naming in responsive function
- shortened rendering time
- simpler doc
  • Loading branch information
ddnexus committed Mar 25, 2019
1 parent 3eacc8d commit 3cce19a
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 45 deletions.
38 changes: 10 additions & 28 deletions docs/extras/plain.md
Expand Up @@ -96,48 +96,30 @@ The `:breakpoints` variable is a non-core variable used by the `responsive` navs
For example:

```ruby
Pagy::VARS[:breakpoints] = { 0 => [1,0,0,1], 540 => [2,3,3,2], 720 => [3,4,4,3] }
Pagy::VARS[:breakpoints] = { 0 => [2,3,3,2], 540 => [3,5,5,3], 720 => [5,7,7,5] }
```

The above statement means that from `0` to `540` pixels width, Pagy will use the `[1,0,0,1]` size, from `540` to `720` it will use the `[2,3,3,4]` size and over `720` it will use the `[3,4,4,3]` size. (Read more about the `:size` variable in the [How to control the page links](../how-to.md#controlling-the-page-links) section).
The above statement means that from `0` to `540` pixels width, Pagy will use the `[2,3,3,2]` size, from `540` to `720` it will use the `[3,5,5,3]` size and over `720` it will use the `[5,7,7,5]` size. (Read more about the `:size` variable in the [How to control the page links](../how-to.md#controlling-the-page-links) section).

**IMPORTANT**: You can set any number of breakpoints with any arbitrary width and size. The only requirement is that the `:breakpoints` hash must contain always the `0` size. An `ArgumentError` exception will be raises if it is missing.

**Notice**: Each added breakpoint slowers down Pagy of almost 10%. For example: with 5 breakpoints (which are actually quite a lot) the nav will be rendered rougly in twice the normal time. However, that will still run about 15 times faster than Kaminari and 6 times faster than WillPaginate.
**Notice**: Each added breakpoint slowers down Pagy of less than 10%. For example: with 5 breakpoints the nav will be rendered rougly in less than twice the normal time. That will still run about 16 times faster than Kaminari and 7 times faster than WillPaginate, so it doesn't look like an issue.

#### Setting the right breakpoints

Setting the width and the size of your breakpoint is what could create a nice transition between sizes... or some apparently erratic behavior.

Here is what you should consider.

The transition from one breakpoint/size to another depends by the width available to your nav. That width is the _internal available width_ of its container (excluding eventual horizontal padding), so the pagy breakpoint widths that you set should reflect the container internal available widths.

The container width can change as a continous range (normal behavior for a div) or in discrete steps (for example when using bootstrap the container has classes like `sm-md-lg`).

##### Continous Width-ranges
Setting the width and the size of your breakpoints is what can create a nice transition between sizes... or some apparently erratic behavior.

For continous width-range containers you should ensure that the resulting navs can be contained in the breakpoint widths that you set. In other words if you create a size as `[20,20,20,20]`, is pretty obvious that it could not be contained in a `540` width, so you should assign reasonable sizes based on the available widths.
Here is what you should consider/ensure:

##### Discrete Step Widths
1. The pagy size can only change in discreet steps: each widht/size pair in your `:breakpoints` represents a step.

If you use frameworks like bootstrap (but the same applies to many others) you can assign classes to your container that will snap to specific widths (e.g. `sm-md-lg`). In that case you should sync the quantity and widths of the pagy brakpoints to the quantity and internal container widths of the bootstrap classes.
2. The transition from one breakpoint/size to another depends on the width available to the pagy nav. That width is the _internal available width_ of its container (excluding eventual horizontal padding).

**IMPORTANT**: The pagy breakpoint widths should not be the same bootstrap breakpoints widths, but their container internal available widths.
3. You should ensure that each pagy `:size` in your breakpoints produces a nav that can be contained in its its width.

For example: if you assign the following classes:
4. You should ensure that the minimum internal width for the container div be equal (or a bit bigger) to the smaller positive `:breakpoints` width. (`540` pixels in our previous example).

```
sm = Small ≥576px
Max container width 540px
md = Medium ≥768px
Max container width 720px
lg = Large ≥992px
Max container width 960px
```
You should use the `0`, `540` and `720` width (or less if there is padding), and assign consistent sizes.
5. If the container width snaps to specific widths in discrete steps, you should sync the quantity and widths of the pagy `:brakpoints` to the quantity and internal widths for each discrete step of the container.

## Methods

Expand Down
4 changes: 2 additions & 2 deletions lib/config/pagy.rb
Expand Up @@ -60,8 +60,8 @@

# Breakpoints var used by the responsive nav helpers
# See https://ddnexus.github.io/pagy/extras/plain#breakpoints
# width/size pairs: example for bootstrap4 sm-md-lg internal container widths
# Pagy::VARS[:breakpoints] = { 0 => [1,0,0,1], 540 => [2,3,3,2], 720 => [3,4,4,3] }
# Pagy::VARS[:breakpoints] = { 0 => [2,3,3,2], 540 => [3,5,5,3], 720 => [5,7,7,5] }


# Feature Extras

Expand Down
26 changes: 14 additions & 12 deletions lib/javascripts/pagy.js
Expand Up @@ -53,27 +53,29 @@ Pagy.responsive = function(id, tags, widths, series){
var pagyEl = document.getElementById(id),
container = pagyEl.parentElement,
lastWidth = undefined,
resizeId = 0,
timeoutId = 0,
render = function(){
if (container.clientWidth === 0){ clearTimeout(resizeId); return setTimeout(render, 300) }
var width = widths.find(function(w) {return container.clientWidth > w});
if (container.clientWidth === 0) { rendering() }
var width, i, len;
for (i = 0, len = widths.length; i < len; i++) {
if (container.clientWidth > widths[i]) { width = widths[i]; break }
}
if (width !== lastWidth) {
while (pagyEl.firstChild) { pagyEl.removeChild(pagyEl.firstChild) }
var html = tags['before'];
series[width].forEach(function(item) {html += tags[item]});
var html = tags['before'],
items = series[width];
for (i = 0, len = items.length; i < len; i++) { html += tags[items[i]] }
html += tags['after'];
pagyEl.insertAdjacentHTML('beforeend', html);
lastWidth = width;
}
},
resize = function(){ // call render once, after window.resize is done
clearTimeout(resizeId);
resizeId = setTimeout(render, 300);
};
// remove the previous window resize listener which may result in firing the render multiple times
// suppress rapid firing rendering
rendering = function(){ clearTimeout(timeoutId); timeoutId = setTimeout(render, 150) };
// refresh the window resize listener (avoiding rendering multiple times)
window.removeEventListener('resize', Pagy.windowListeners[id], true);
window.addEventListener('resize', resize, true);
Pagy.windowListeners[id] = resize;
window.addEventListener('resize', rendering, true);
Pagy.windowListeners[id] = rendering;
render();
};

Expand Down
6 changes: 3 additions & 3 deletions lib/pagy/extras/shared.rb
Expand Up @@ -14,9 +14,9 @@ class Pagy
# Pagy.new count:1000, page: 20, breakpoints: {0 => [1,2,2,1], 350 => [2,3,3,2], 550 => [3,4,4,3]}
# it returns something like:
# { :items => [1, :gap, 18, 19, "20", 21, 22, 50, 2, 17, 23, 49, 3, 16, 24, 48],
# :series => { 0 =>[1, :gap, 18, 19, "20", 21, 22, :gap, 50],
# 350 =>[1, 2, :gap, 17, 18, 19, "20", 21, 22, 23, :gap, 49, 50],
# 550 =>[1, 2, 3, :gap, 16, 17, 18, 19, "20", 21, 22, 23, 24, :gap, 48, 49, 50] },
# :series => { 0 => [1, :gap, 18, 19, "20", 21, 22, :gap, 50],
# 350 => [1, 2, :gap, 17, 18, 19, "20", 21, 22, 23, :gap, 49, 50],
# 550 => [1, 2, 3, :gap, 16, 17, 18, 19, "20", 21, 22, 23, 24, :gap, 48, 49, 50] },
# :widths => [550, 350, 0] }
# where :items is the unordered array union of all the page numbers for all sizes (passed to the PagyResponsive javascript function)
# :series is the hash of the series keyed by width (used by the *_responsive helpers to create the JSON string)
Expand Down

0 comments on commit 3cce19a

Please sign in to comment.