flex_infinite_scroll 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/flex_infinite_scroll.rb +3 -0
- data/lib/flex_infinite_scroll/actionview.rb +6 -0
- data/lib/flex_infinite_scroll/actionview/extension.rb +39 -0
- data/lib/flex_infinite_scroll/activerecord.rb +6 -0
- data/lib/flex_infinite_scroll/activerecord/extension.rb +19 -0
- data/lib/flex_infinite_scroll/activerecord/helpers.rb +38 -0
- data/vendor/assets/javascript/flex_infinite_scroll.js +63 -69
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 566821164973e19e83ef507c84b5b77f0d66e148
|
4
|
+
data.tar.gz: e22f3d0148db3746989eef48649e82376c12f02d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8a8bf1db18cdb92f26ef9953c5fee972f64c43de1e05f1fe0afae24fe21bbd07e425032d5675a8db5921c4bdab062ae80f2d6914bb542da046e9645be0a0c49
|
7
|
+
data.tar.gz: 4b6567f7892c2bc453d32a45a3a1866c8dd2eade75192f8386c3c7502262febd9e5295e6cad57c59a94f872619887eaaa734839ecd5241b5741c975e2b9fb61b
|
data/lib/flex_infinite_scroll.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlexInfiniteScroll
|
4
|
+
module ActionViewExtension
|
5
|
+
def fis_tag(data = nil, fis_config = {}, container_config = {})
|
6
|
+
container_config[:data] ||= {}
|
7
|
+
container_config[:class] = "fis-container #{container_config[:class]}"
|
8
|
+
|
9
|
+
virtual_el_size = fis_config[:virtualScrollElementSize]
|
10
|
+
|
11
|
+
page = fis_config[:startPage] || 1
|
12
|
+
fis_config[:startPage] = page + 1
|
13
|
+
|
14
|
+
fis_config.each do |k, _v|
|
15
|
+
container_config[:data][k.to_s.underscore] = fis_config[k]
|
16
|
+
end
|
17
|
+
|
18
|
+
content_tag :div, container_config do
|
19
|
+
data = data.fis(page: page, per_page: fis_config[:perPage])
|
20
|
+
data_html = data.map { |el| yield el }.join
|
21
|
+
|
22
|
+
baloons = []
|
23
|
+
if virtual_el_size
|
24
|
+
(fis_config[:startPage]..data.total_pages).each do |page_number|
|
25
|
+
baloons.push(
|
26
|
+
content_tag(:div, nil,
|
27
|
+
class: 'fis-baloon',
|
28
|
+
data: {
|
29
|
+
baloon_page: page_number
|
30
|
+
},
|
31
|
+
style: "height: #{virtual_el_size * data.per_page}px")
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
(data_html + baloons.join).html_safe
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlexInfiniteScroll
|
4
|
+
module ActiveRecordExtension
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
require 'flex_infinite_scroll/activerecord/helpers'
|
8
|
+
|
9
|
+
included do
|
10
|
+
def self.fis(config = { page: 1 })
|
11
|
+
page = config[:page].to_i
|
12
|
+
per_page = config[:per_page] ? config[:per_page].to_i : 20
|
13
|
+
offset(per_page * (page - 1)).limit(per_page).extending do
|
14
|
+
include FlexInfiniteScroll::ActiveRecordHelpers
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FlexInfiniteScroll
|
4
|
+
module ActiveRecordHelpers
|
5
|
+
def render_json
|
6
|
+
prepare_render.merge(data: as_json)
|
7
|
+
end
|
8
|
+
|
9
|
+
def render_html
|
10
|
+
prepare_render.merge(data: map { |el| yield el }.join.html_safe)
|
11
|
+
end
|
12
|
+
|
13
|
+
def per_page
|
14
|
+
values[:limit]
|
15
|
+
end
|
16
|
+
|
17
|
+
def page
|
18
|
+
(values[:offset] / per_page) + 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_page
|
22
|
+
page == total_pages ? nil : page + 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def total_pages
|
26
|
+
(except(:offset, :limit, :order).count / per_page).ceil
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def prepare_render
|
32
|
+
{
|
33
|
+
next_page: next_page,
|
34
|
+
total_pages: total_pages
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -43,30 +43,39 @@ class flexIS {
|
|
43
43
|
|
44
44
|
function prepareEventTarget(object) {
|
45
45
|
var eventTarget = document.querySelector(object.config.eventTarget) || object.targetObject;
|
46
|
-
if (object.config.windowScroll) {
|
46
|
+
if (["true", true].includes(object.config.windowScroll)) {
|
47
47
|
return window;
|
48
48
|
} else {
|
49
49
|
return eventTarget;
|
50
50
|
}
|
51
51
|
}
|
52
52
|
|
53
|
-
if (this.config.perfectScrollbarSupport) {
|
53
|
+
if (["true", true].includes(this.config.perfectScrollbarSupport)) {
|
54
54
|
let PSConfig = this.config.perfectScrollbarConfig || {}
|
55
55
|
PSConfig.minScrollbarLength = PSConfig.minScrollbarLength || 40
|
56
56
|
this.perfectScrollbar = new PerfectScrollbar(
|
57
57
|
this.targetObject,
|
58
58
|
PSConfig
|
59
|
-
)
|
59
|
+
);
|
60
60
|
}
|
61
61
|
}
|
62
62
|
|
63
63
|
// Init infinite scroll
|
64
64
|
init() {
|
65
|
-
this.#
|
65
|
+
if(this.#virtualScroll().enabled()) {
|
66
|
+
this.#virtualScroll().pagesLoad();
|
67
|
+
} else {
|
68
|
+
this.#getData();
|
69
|
+
}
|
70
|
+
|
66
71
|
this.config.eventTarget.addEventListener('scroll', () => {
|
67
|
-
|
68
|
-
this.#
|
69
|
-
|
72
|
+
if(this.#virtualScroll().enabled()) {
|
73
|
+
this.#virtualScroll().pagesLoad();
|
74
|
+
} else {
|
75
|
+
if (this.#scrollHitBottom() && this.nextPage) {
|
76
|
+
this.#getData();
|
77
|
+
};
|
78
|
+
}
|
70
79
|
this.#hideInvisibleContent();
|
71
80
|
});
|
72
81
|
return this;
|
@@ -89,13 +98,13 @@ class flexIS {
|
|
89
98
|
// Private methods
|
90
99
|
|
91
100
|
// Run data request for next page
|
92
|
-
#getData = (page = this.nextPage ) => {
|
101
|
+
#getData = (page = this.nextPage, forceLoad = false) => {
|
93
102
|
var xhr = new XMLHttpRequest();
|
94
103
|
var params;
|
95
104
|
const beforeLoadEvent = new CustomEvent('FlexIS:beforeLoad');
|
96
105
|
const afterLoadEvent = new CustomEvent('FlexIS:afterLoad');
|
97
106
|
|
98
|
-
if (!page || this.loading) return false;
|
107
|
+
if (!page || (this.loading && !forceLoad)) return false;
|
99
108
|
|
100
109
|
this.loading = true;
|
101
110
|
|
@@ -109,13 +118,11 @@ class flexIS {
|
|
109
118
|
this.loading = false;
|
110
119
|
|
111
120
|
if (xhr.status === 200) {
|
112
|
-
this.#customResponse(json);
|
113
|
-
this.
|
114
|
-
|
115
|
-
this
|
116
|
-
this.#virtualScroll().update();
|
121
|
+
this.#customResponse(json, page);
|
122
|
+
if (!this.#virtualScroll().enabled()) {
|
123
|
+
this.nextPage = json[this.config.customResponseAttributes.next_page];
|
124
|
+
if (this.#scrollHitBottom()) this.#getData();
|
117
125
|
}
|
118
|
-
if (this.#scrollHitBottom()) this.#getData();
|
119
126
|
}
|
120
127
|
|
121
128
|
this.targetObject.dispatchEvent(afterLoadEvent);
|
@@ -141,7 +148,7 @@ class flexIS {
|
|
141
148
|
|
142
149
|
// Container position
|
143
150
|
#containerPosition = () => {
|
144
|
-
return this.#scrollTop() + this.#containerSize() + this.config.loadMargin
|
151
|
+
return this.#scrollTop() + this.#containerSize() + this.config.loadMargin;
|
145
152
|
}
|
146
153
|
|
147
154
|
// Add custom params to request
|
@@ -160,12 +167,12 @@ class flexIS {
|
|
160
167
|
}
|
161
168
|
|
162
169
|
// Response handling
|
163
|
-
#customResponse = (json) => {
|
170
|
+
#customResponse = (json, page) => {
|
164
171
|
var customResponse = this.config.customResponse;
|
165
172
|
var data = json[this.config.customResponseAttributes.data];
|
166
173
|
var div;
|
167
174
|
|
168
|
-
delete json[this.config.customResponseAttributes.data]
|
175
|
+
delete json[this.config.customResponseAttributes.data];
|
169
176
|
|
170
177
|
if (data.constructor === Array) {
|
171
178
|
data.forEach((el) => {
|
@@ -173,32 +180,37 @@ class flexIS {
|
|
173
180
|
if (typeof customResponse === "function") {
|
174
181
|
htmlEl = customResponse(el, json);
|
175
182
|
} else {
|
176
|
-
|
183
|
+
let div = document.createElement('div');
|
184
|
+
div.innerHTML = el;
|
185
|
+
htmlEl = div.children[0];
|
177
186
|
}
|
178
|
-
this.#appendElementToContainer(htmlEl)
|
187
|
+
this.#appendElementToContainer(htmlEl, page);
|
179
188
|
})
|
180
189
|
} else if (data.constructor === String) {
|
181
190
|
div = document.createElement('div');
|
182
191
|
div.innerHTML = data;
|
183
192
|
while (div.children.length > 0) {
|
184
|
-
this.#appendElementToContainer(div.children[0])
|
193
|
+
this.#appendElementToContainer(div.children[0], page);
|
185
194
|
}
|
186
195
|
}
|
187
196
|
|
188
197
|
this.#hideInvisibleContent();
|
189
198
|
|
199
|
+
if (this.#virtualScroll().enabled()) {
|
200
|
+
this.#virtualScroll().destroyBaloon(page);
|
201
|
+
}
|
202
|
+
|
190
203
|
if (this.perfectScrollbar) {
|
191
204
|
this.perfectScrollbar.update();
|
192
205
|
}
|
193
206
|
}
|
194
207
|
|
195
|
-
#appendElementToContainer = (el) => {
|
196
|
-
if (this.#virtualScroll().
|
197
|
-
this
|
208
|
+
#appendElementToContainer = (el, page) => {
|
209
|
+
if (this.#virtualScroll().enabled()) {
|
210
|
+
this.#virtualScroll().insertBeforeBaloon(page, el);
|
198
211
|
} else {
|
199
212
|
this.targetObject.appendChild(el);
|
200
213
|
}
|
201
|
-
this.elementHeight = el.offsetHeight;
|
202
214
|
}
|
203
215
|
|
204
216
|
// Hide invisible content, when leaving active zone
|
@@ -222,45 +234,33 @@ class flexIS {
|
|
222
234
|
}
|
223
235
|
|
224
236
|
#virtualScroll = () => {
|
225
|
-
// Find baloon
|
226
|
-
var baloon = this.targetObject.getElementsByClassName('fis-baloon')[0];
|
227
|
-
|
228
|
-
// Create new baloon
|
229
|
-
var createBaloon = () => {
|
230
|
-
baloon = document.createElement('div');
|
231
|
-
baloon.className = 'fis-baloon';
|
232
|
-
this.targetObject.appendChild(baloon);
|
233
|
-
return baloon;
|
234
|
-
}
|
235
|
-
|
236
|
-
// Check if baloon exist and return it
|
237
|
-
var baloonAdded = () => {
|
238
|
-
if (baloon) {
|
239
|
-
return baloon;
|
240
|
-
}
|
241
|
-
return false
|
242
|
-
}
|
243
|
-
|
244
|
-
// Update baloon size
|
245
|
-
var updateScroll = () => {
|
246
|
-
var elementHeight = this.config.virtualScrollElementSize || this.elementHeight;
|
247
|
-
if (!baloonAdded()) {
|
248
|
-
baloon = createBaloon();
|
249
|
-
}
|
250
|
-
if (this.elementsLeft > 0) {
|
251
|
-
baloon.style.height = (elementHeight * this.elementsLeft) + 'px';
|
252
|
-
} else {
|
253
|
-
// Remove baloon, if no more elements to load.
|
254
|
-
baloon.remove();
|
255
|
-
}
|
256
|
-
}
|
257
|
-
|
258
237
|
return {
|
259
|
-
|
260
|
-
|
238
|
+
enabled: () => {
|
239
|
+
return ["true", true].includes(this.config.virtualScroll);
|
261
240
|
},
|
262
|
-
|
263
|
-
|
241
|
+
insertBeforeBaloon: (page, el) => {
|
242
|
+
var baloon = this.targetObject.querySelector(`[data-baloon-page="${page}"]`)
|
243
|
+
var newHeight;
|
244
|
+
this.targetObject.insertBefore(el, baloon);
|
245
|
+
newHeight = baloon.offsetHeight - el.offsetHeight;
|
246
|
+
baloon.style.height = newHeight + 'px';
|
247
|
+
|
248
|
+
},
|
249
|
+
pagesLoad: () => {
|
250
|
+
var baloons = [...this.targetObject.getElementsByClassName('fis-baloon')];
|
251
|
+
var containerPosition = this.#containerPosition();
|
252
|
+
baloons.forEach(baloon => {
|
253
|
+
var visibleBottom = this.#containerPosition() > baloon.offsetTop;
|
254
|
+
var visibleTop = this.#scrollTop() < baloon.offsetTop + baloon.offsetHeight;
|
255
|
+
if (baloon.dataset.loading) return;
|
256
|
+
if (visibleBottom && visibleTop) {
|
257
|
+
baloon.dataset.loading = true;
|
258
|
+
this.#getData(baloon.dataset.baloonPage, true);
|
259
|
+
}
|
260
|
+
})
|
261
|
+
},
|
262
|
+
destroyBaloon: (page) => {
|
263
|
+
this.targetObject.querySelector(`[data-baloon-page="${page}"]`).remove();
|
264
264
|
}
|
265
265
|
}
|
266
266
|
}
|
@@ -287,13 +287,7 @@ class flexIS {
|
|
287
287
|
|
288
288
|
// Check when load next page
|
289
289
|
#scrollHitBottom = () => {
|
290
|
-
|
291
|
-
if (this.#virtualScroll().baloon()) {
|
292
|
-
hitBottom = this.#containerPosition() > this.#virtualScroll().baloon().offsetTop;
|
293
|
-
} else {
|
294
|
-
hitBottom = this.#scrollHeight() - this.#containerPosition() <= 0;
|
295
|
-
}
|
296
|
-
return hitBottom;
|
290
|
+
return this.#scrollHeight() - this.#containerPosition() <= 0;
|
297
291
|
}
|
298
292
|
}
|
299
293
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flex_infinite_scroll
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anton Ignatov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -31,6 +31,11 @@ extensions: []
|
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
33
|
- lib/flex_infinite_scroll.rb
|
34
|
+
- lib/flex_infinite_scroll/actionview.rb
|
35
|
+
- lib/flex_infinite_scroll/actionview/extension.rb
|
36
|
+
- lib/flex_infinite_scroll/activerecord.rb
|
37
|
+
- lib/flex_infinite_scroll/activerecord/extension.rb
|
38
|
+
- lib/flex_infinite_scroll/activerecord/helpers.rb
|
34
39
|
- vendor/assets/javascript/flex_infinite_scroll.js
|
35
40
|
homepage: https://github.com/aignatov-bio/flex_infinite_scroll
|
36
41
|
licenses:
|
@@ -55,7 +60,5 @@ rubyforge_project:
|
|
55
60
|
rubygems_version: 2.6.14.3
|
56
61
|
signing_key:
|
57
62
|
specification_version: 4
|
58
|
-
summary:
|
59
|
-
features: - Custom response handling. - Virtual scrollbar. - Perfect scrollbar support.
|
60
|
-
- Kaminari support.'
|
63
|
+
summary: Infinite scroll for Ruby on Rails applications on pure JavaScript.
|
61
64
|
test_files: []
|