diff --git a/addon/-private/data-view/radar/dynamic-radar.js b/addon/-private/data-view/radar/dynamic-radar.js index 8ada4f3c..a5876d99 100644 --- a/addon/-private/data-view/radar/dynamic-radar.js +++ b/addon/-private/data-view/radar/dynamic-radar.js @@ -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() { diff --git a/addon/-private/data-view/radar/radar.js b/addon/-private/data-view/radar/radar.js index f5af6163..b7d65354 100644 --- a/addon/-private/data-view/radar/radar.js +++ b/addon/-private/data-view/radar/radar.js @@ -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 @@ -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; @@ -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() { diff --git a/addon/components/vertical-collection/component.js b/addon/components/vertical-collection/component.js index 1b262336..194e60e7 100644 --- a/addon/components/vertical-collection/component.js +++ b/addon/components/vertical-collection/component.js @@ -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', () => { @@ -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); }, @@ -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); + } } }); diff --git a/tests/integration/scroll-test.js b/tests/integration/scroll-test.js index 7a96d941..d61228b6 100644 --- a/tests/integration/scroll-test.js +++ b/tests/integration/scroll-test.js @@ -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` +