Skip to content

Commit 0182953

Browse files
authored
Merge pull request #230 from angular-ui/immutable-top
immutableTop option
2 parents 41e4c4f + 07eb2c3 commit 0182953

12 files changed

+501
-202
lines changed

.travis.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ cache:
1313
addons:
1414
firefox: latest
1515

16-
before_script:
17-
- export DISPLAY=:99.0
18-
- sh -e /etc/init.d/xvfb start
19-
- sleep 3
16+
services:
17+
- xvfb
2018

2119
script:
2220
- npm run build

README.md

+14-11
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,7 @@ exactly `count` elements unless it hit eof/bof.
229229

230230
* Properties `minIndex` and `maxIndex`
231231

232-
As the scroller receives the items requested by the `get` method, the value of minimum and maximum values of the item index are placed in the `minIndex` and `maxIndex` properties respectively. These values are used to maintain the appearance of the scrollbar. The values of the properties can be internaly changed by the ui-scroll engine in three cases:
233-
* reset both properties in response to a call to the adapter `reload` method;
234-
* increment `minIndex` in response to deleting the topmost element via adapter `applyUpdates` method;
235-
* decrement `maxIndex` in response to deleting anything other than the topmost element via adapter `applyUpdates` method.
236-
237-
Values of the properties can be assigned programmatically. If the range of the index values is known in advance, assigning them programmatically would improve the usability of the scrollBar.
238-
232+
If the boundaries of the dataset are known, we may virtualize all the dataset by assigning appropriate values to `minIndex` and `maxIndex` datasource properties. This would improve the usability of the scroll bar: the uiScroll will maintain forward and backward padding elements of the viewport assuming the dataset consists of (maxIndex - minIndex) items. So it will be possible to jump to any position immediately.
239233

240234
### Adapter
241235

@@ -286,21 +280,26 @@ Adapter object implements the following methods
286280

287281
* Method `applyUpdates`
288282

289-
applyUpdates(index, newItems)
283+
applyUpdates(index, newItems, options)
290284

291285
Replaces the item in the buffer at the given index with the new items.
292286

293287
Parameters
294288
* **index** provides position of the item to be affected in the dataset (not in the buffer). If the item with the given index currently is not in the buffer no updates will be applied. `$index` property of the item $scope can be used to access the index value for a given item
295289
* **newItems** is an array of items to replace the affected item. If the array is empty (`[]`) the item will be deleted, otherwise the items in the array replace the item. If the newItem array contains the old item, the old item stays in place.
296290

297-
applyUpdates(updater)
291+
applyUpdates(updater, options)
298292

299293
Updates scroller content as determined by the updater function
300294

301295
Parameters
302296
* **updater** is a function to be applied to every item currently in the buffer. The function will receive 3 parameters: `item`, `scope`, and `element`. Here `item` is the item to be affected, `scope` is the item $scope, and `element` is the html element for the item. The return value of the function should be an array of items. Similarly to the `newItem` parameter (see above), if the array is empty(`[]`), the item is deleted, otherwise the item is replaced by the items in the array. If the return value is not an array, the item remains unaffected, unless some updates were made to the item in the updater function. This can be thought of as in place update.
303297

298+
Options for both signatures, an object with following fields
299+
* **immutableTop** is a boolean flag with `false` defalt value. This option has an impact on removing/inserting items procedure. If it's `false`, deleting the topmost item will lead to incrementing min index, also inserting new item(s) before the topmost one will lead to decrementing min index. If it's `true`, min index will not be affected, max index will be shifted instead. If it's `true`, no matter which item is going to be removed/inserted, max index will be reduced/increased respectively.
300+
301+
Let's discuss a little sample. We have `{{$index}}: {{item}}` template and three rows: `1: item1`, `2: item2`, `3: item3`. Then we want to remove the first item. Without `immutableTop` we'll get `2: item2`, `3: item3`. With `immutableTop` we'll get `1: item2`, `2: item3`. The same for inserting, say, `item0` before `item1`. Without `immutableTop` we'll get `0: item0`, `1: item1`, `2: item2`, `3: item3`. With `immutableTop` we'll get `1: item0`, `2: item1`, `3: item2`, `4: item3`.
302+
304303
* Method `append`
305304

306305
append(newItems)
@@ -312,12 +311,13 @@ Adapter object implements the following methods
312311

313312
* Method `prepend`
314313

315-
prepend(newItems)
314+
prepend(newItems, options)
316315

317-
Adds new items before the first item in the buffer.
316+
Adds new items before the first item in the buffer. Works exactly as inserting new item(s) before the topmost one via `applyUpdates` method.
318317

319318
Parameters
320319
* **newItems** provides an array of items to be prepended.
320+
* **options** the same object as the last argument of `applyUpdates` method; `options.immutableTop` set to `true` will make min index unchangeable, max index will be increased. Otherwise (`options.immutableTop = false`, the default case), min index will be increased.
321321

322322
#### Manipulating the scroller content with the adapter methods
323323
Adapter methods `applyUpdates`, `append` and `prepend` provide a way to update the scroller content without full reload of the content from the datasource. The updates are performed by changing the items in the scroller internal buffer after they are loaded from the datasource. Items in the buffer can be deleted or replaced with one or more items.
@@ -477,6 +477,9 @@ Pull Rerquest should include source code (./scr) changes, may include tests (./t
477477

478478
## Change log
479479

480+
### v1.7.6
481+
* Added immutableTop option for applyUpdates and prepend Adapter methods.
482+
480483
### v1.7.5
481484
* Added bufferFirst, bufferLast, bufferLength read-only properties to the Adapter.
482485
* Fixed reload unsubscribe issue [226](https://github.com/angular-ui/ui-scroll/issues/226).

bower.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "angular-ui-scroll",
33
"description": "AngularJS infinite scrolling module",
4-
"version": "1.7.5",
4+
"version": "1.7.6",
55
"main": "./dist/ui-scroll.js",
66
"homepage": "https://github.com/angular-ui/ui-scroll.git",
77
"license": "MIT",

dist/ui-scroll-grid.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ui-scroll.js

+24-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ui-scroll.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ui-scroll.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ui-scroll.min.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "angular-ui-scroll",
33
"description": "AngularJS infinite scrolling module",
4-
"version": "1.7.5",
4+
"version": "1.7.6",
55
"src": "./src/",
66
"public": "./dist/",
77
"main": "./dist/ui-scroll.js",

src/modules/adapter.js

+14-14
Original file line numberDiff line numberDiff line change
@@ -114,45 +114,45 @@ class Adapter {
114114
this.viewport.clipBottom();
115115
}
116116

117-
prepend(newItems) {
118-
this.buffer.prepend(newItems);
117+
prepend(newItems, options = {}) {
118+
this.buffer.prepend(newItems, options.immutableTop);
119119
this.doAdjust();
120120
this.viewport.clipTop();
121121
this.viewport.clipBottom();
122122
}
123123

124-
applyUpdates(arg1, arg2) {
124+
applyUpdates(arg1, arg2, arg3) {
125125
if (typeof arg1 === 'function') {
126-
this.applyUpdatesFunc(arg1);
126+
this.applyUpdatesFunc(arg1, arg2);
127127
} else {
128-
this.applyUpdatesIndex(arg1, arg2);
128+
this.applyUpdatesIndex(arg1, arg2, arg3);
129129
}
130130
this.doAdjust();
131131
}
132132

133-
applyUpdatesFunc(cb) {
133+
applyUpdatesFunc(cb, options = {}) {
134134
this.buffer.slice(0).forEach((wrapper) => {
135135
// we need to do it on the buffer clone, because buffer content
136136
// may change as we iterate through
137-
this.applyUpdate(wrapper, cb(wrapper.item, wrapper.scope, wrapper.element));
137+
this.applyUpdate(wrapper, cb(wrapper.item, wrapper.scope, wrapper.element), options);
138138
});
139139
}
140140

141-
applyUpdatesIndex(index, newItems) {
141+
applyUpdatesIndex(index, newItems, options = {}) {
142142
if (index % 1 !== 0) {
143143
throw new Error('applyUpdates - ' + index + ' is not a valid index (should be an integer)');
144144
}
145145
const _index = index - this.buffer.first;
146146

147147
// apply updates only within buffer
148148
if (_index >= 0 && _index < this.buffer.length) {
149-
this.applyUpdate(this.buffer[_index], newItems);
149+
this.applyUpdate(this.buffer[_index], newItems, options);
150150
}
151151
// out-of-buffer case: deletion may affect Paddings
152152
else if(index >= this.buffer.getAbsMinIndex() && index <= this.buffer.getAbsMaxIndex()) {
153153
if(angular.isArray(newItems) && !newItems.length) {
154-
this.viewport.removeCacheItem(index, index === this.buffer.minIndex);
155-
if(index === this.buffer.getAbsMinIndex()) {
154+
this.viewport.removeCacheItem(index, !options.immutableTop && index === this.buffer.minIndex);
155+
if (!options.immutableTop && index === this.buffer.getAbsMinIndex()) {
156156
this.buffer.incrementMinIndex();
157157
}
158158
else {
@@ -162,14 +162,14 @@ class Adapter {
162162
}
163163
}
164164

165-
applyUpdate(wrapper, newItems) {
165+
applyUpdate(wrapper, newItems, options = {}) {
166166
if (!angular.isArray(newItems)) {
167167
return;
168168
}
169169
let position = this.buffer.indexOf(wrapper);
170170
if (!newItems.reverse().some(newItem => newItem === wrapper.item)) {
171171
wrapper.op = 'remove';
172-
if(position === 0 && !newItems.length) {
172+
if (!options.immutableTop && position === 0 && !newItems.length) {
173173
wrapper._op = 'isTop'; // to catch "first" edge case on remove
174174
}
175175
}
@@ -178,7 +178,7 @@ class Adapter {
178178
position--;
179179
} else {
180180
// 3 parameter (isTop) is to catch "first" edge case on insert
181-
this.buffer.insert(position + 1, newItem, position === -1);
181+
this.buffer.insert(position + 1, newItem, !options.immutableTop && position === -1);
182182
}
183183
});
184184
}

src/modules/buffer.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ export default function ScrollBuffer(elementRoutines, bufferSize, startIndex) {
2424
buffer.maxIndex = buffer.eof ? buffer.next - 1 : Math.max(buffer.next - 1, buffer.maxIndex);
2525
},
2626

27-
prepend(items) {
27+
prepend(items, immutableTop) {
2828
items.reverse().forEach((item) => {
29-
--buffer.first;
29+
if (immutableTop) {
30+
++buffer.next;
31+
}
32+
else {
33+
--buffer.first;
34+
}
3035
buffer.insert('prepend', item);
3136
});
3237
buffer.minIndex = buffer.bof ? buffer.minIndex = buffer.first : Math.min(buffer.first, buffer.minIndex);

0 commit comments

Comments
 (0)