Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to scrollto particular index in the list #2

Open
wants to merge 1 commit into
base: fs-vc
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions addon/-private/data-view/radar/dynamic-radar.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ export default class DynamicRadar extends Radar {
this.skipList = null;
}

scheduleUpdate(didUpdateItems) {
scheduleUpdate(didUpdateItems, promiseResolve) {
// Cancel incremental render check, since we'll be remeasuring anyways
if (this._nextIncrementalRender !== null) {
this._nextIncrementalRender.cancel();
this._nextIncrementalRender = null;
}

super.scheduleUpdate(didUpdateItems);
super.scheduleUpdate(didUpdateItems, promiseResolve);
}

afterUpdate() {
Expand Down
13 changes: 9 additions & 4 deletions addon/-private/data-view/radar/radar.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export default class Radar {
*
* @private
*/
scheduleUpdate(didUpdateItems) {
scheduleUpdate(didUpdateItems, promiseResolve) {
if (didUpdateItems === true) {
// Set the update items flag first, in case scheduleUpdate has already been called
// but the RAF hasn't yet run
Expand All @@ -238,11 +238,11 @@ export default class Radar {
this._nextUpdate = null;
this._scrollTop = this._scrollContainer.scrollTop;

this.update();
this.update(promiseResolve);
});
}

update() {
update(promiseResolve) {
if (this._didUpdateItems === true) {
this._determineUpdateType();
this._didUpdateItems = false;
Expand All @@ -252,7 +252,12 @@ export default class Radar {
this._updateIndexes();
this._updateVirtualComponents();

this.schedule('measure', this.afterUpdate.bind(this));
this.schedule('measure', () => {
if (promiseResolve) {
promiseResolve();
}
this.afterUpdate();
});
}

afterUpdate() {
Expand Down
56 changes: 56 additions & 0 deletions addon/components/vertical-collection/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,24 @@ const VerticalCollection = Component.extend({
}
},

/* Public API Methods
@index => number
This will return offset height of the indexed item.
*/
scrollToItem(index) {
const { _radar } = this;
// Getting the offset height from Radar
let scrollTop = _radar.getOffsetForIndex(index);
_radar._scrollContainer.scrollTop = scrollTop;
// To scroll exactly to specified index, we are changing the prevIndex values to specified index
_radar._prevFirstVisibleIndex = _radar._prevFirstItemIndex = index;
// Components will be rendered after schedule 'measure' inside 'update' method.
// In our case, we need to focus the element after component is rendered. So passing the promise.
return new Promise ((resolve) => {
_radar.scheduleUpdate(false, resolve);
});
},

// –––––––––––––– Setup/Teardown
didInsertElement() {
this.schedule('sync', () => {
Expand All @@ -210,6 +228,10 @@ const VerticalCollection = Component.extend({
willDestroy() {
this.token.cancel();
this._radar.destroy();
let registerAPI = this.get('registerAPI');
if (registerAPI) {
registerAPI(null);
}
clearTimeout(this._nextSendActions);
},

Expand Down Expand Up @@ -280,6 +302,40 @@ const VerticalCollection = Component.extend({
}
};
}

/* Public methods to Expose to parent

Usage:
Template:
{{vertical-collection registerAPI=(action "registerAPI")}}
Component:

export default Component.extend({
actions: {
registerAPI(api) {
this.set('collectionAPI', api);
}
},
scrollToItem() {
let collectionAPI = this.get('collectionAPI');
collectionAPI.scrollToItem(index);
}
});

Need to pass this property in the vertical-collection template
Listen in the component actions and do your custom logic
This API will have below methods.
1. scrollToItem
*/

let registerAPI = get(this, 'registerAPI');
if (registerAPI) {
/* List of methods to be exposed to public should be added here */
let publicAPI = {
scrollToItem: this.scrollToItem.bind(this)
};
registerAPI(publicAPI);
}
}
});

Expand Down
33 changes: 33 additions & 0 deletions tests/integration/scroll-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,39 @@ testScenarios(
}
);

testScenarios(
'Can scroll to particular item if we pass registerApi in the component and use scrollToItem method in the component',
dynamicSimpleScenarioFor(getNumbers(0, 50)),
hbs`
<div style="height: 200px; width: 200px;" class="scroll-parent scrollable">
<div style="height: 400px; width: 100px;" class="scroll-child scrollable">
{{#vertical-collection items
estimateHeight=20
bufferSize=0
registerAPI=(action registerAPI)
as |item i|}}
<div class="vertical-item" style="height:40px;">
{{item.number}} {{i}}
</div>
{{/vertical-collection}}
</div>
</div>
`,

async function(assert) {
assert.expect(1);
await this.collection.scrollToItem(20);
assert.equal(find('.vertical-item:first-of-type').textContent.trim(), '20 20', 'the first item in the list should be the scroll to item');
},
false,
function() {
let registerAPI = function(collection) {
this.set('collection', collection);
};
this.set('registerAPI', registerAPI);
}
);

testScenarios(
'Can scroll to last item when actual item sizes are significantly larger than default item size.',
dynamicSimpleScenarioFor(getNumbers(0, 50), { itemHeight: 100 }),
Expand Down