-
Notifications
You must be signed in to change notification settings - Fork 116
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
Update: Scroll into view #906
Changes from all commits
429838e
7380f00
53ebdbe
cf3fc74
0f614cd
40d0cf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import isFinite from 'lodash/isFinite'; | ||
import VirtualScroller from './VirtualScroller'; | ||
import { CLASS_HIDDEN } from './constants'; | ||
|
||
const CLASS_BOX_PREVIEW_THUMBNAIL = 'bp-thumbnail'; | ||
const CLASS_BOX_PREVIEW_THUMBNAIL_IMAGE = 'bp-thumbnail-image'; | ||
|
@@ -137,6 +138,7 @@ class ThumbnailsSidebar { | |
const scaledViewport = page.getViewport(this.scale); | ||
|
||
this.virtualScroller.init({ | ||
initialRowIndex: this.currentPage - 1, | ||
totalItems: this.pdfViewer.pagesCount, | ||
itemHeight: scaledViewport.height, | ||
containerHeight: this.anchorEl.parentNode.clientHeight, | ||
|
@@ -342,6 +344,7 @@ class ThumbnailsSidebar { | |
if (parsedPageNumber >= 1 && parsedPageNumber <= this.pdfViewer.pagesCount) { | ||
this.currentPage = parsedPageNumber; | ||
this.applyCurrentPageSelection(); | ||
this.virtualScroller.scrollIntoView(parsedPageNumber - 1); | ||
} | ||
} | ||
|
||
|
@@ -361,6 +364,56 @@ class ThumbnailsSidebar { | |
} | ||
}); | ||
} | ||
|
||
/** | ||
* Toggles the thumbnails sidebar | ||
* @return {void} | ||
*/ | ||
toggle() { | ||
if (!this.anchorEl) { | ||
return; | ||
} | ||
|
||
if (!this.isOpen()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could probably just use |
||
this.toggleOpen(); | ||
} else { | ||
this.toggleClose(); | ||
} | ||
} | ||
|
||
/** | ||
* Returns whether the sidebar is open or not | ||
* @return {boolean} true if the sidebar is open, false if not | ||
*/ | ||
isOpen() { | ||
return this.anchorEl && !this.anchorEl.classList.contains(CLASS_HIDDEN); | ||
} | ||
|
||
/** | ||
* Toggles the sidebar open. This will scroll the current page into view | ||
* @return {void} | ||
*/ | ||
toggleOpen() { | ||
if (!this.anchorEl) { | ||
return; | ||
} | ||
|
||
this.anchorEl.classList.remove(CLASS_HIDDEN); | ||
|
||
this.virtualScroller.scrollIntoView(this.currentPage - 1); | ||
} | ||
|
||
/** | ||
* Toggles the sidebar closed | ||
* @return {void} | ||
*/ | ||
toggleClose() { | ||
if (!this.anchorEl) { | ||
return; | ||
} | ||
|
||
this.anchorEl.classList.add(CLASS_HIDDEN); | ||
} | ||
} | ||
|
||
export default ThumbnailsSidebar; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,10 +56,12 @@ class VirtualScroller { | |
this.previousScrollTop = 0; | ||
|
||
this.createListElement = this.createListElement.bind(this); | ||
this.isVisible = this.isVisible.bind(this); | ||
this.onScrollEndHandler = this.onScrollEndHandler.bind(this); | ||
this.onScrollHandler = this.onScrollHandler.bind(this); | ||
this.getCurrentListInfo = this.getCurrentListInfo.bind(this); | ||
this.renderItems = this.renderItems.bind(this); | ||
this.scrollIntoView = this.scrollIntoView.bind(this); | ||
|
||
this.debouncedOnScrollEndHandler = debounce(this.onScrollEndHandler, DEBOUNCE_SCROLL_THRESHOLD); | ||
this.throttledOnScrollHandler = throttle(this.onScrollHandler, THROTTLE_SCROLL_THRESHOLD); | ||
|
@@ -110,7 +112,8 @@ class VirtualScroller { | |
this.scrollingEl.appendChild(this.listEl); | ||
this.anchorEl.appendChild(this.scrollingEl); | ||
|
||
this.renderItems(); | ||
// If initialRowIndex is < the first window into the list, then just render from the first item | ||
this.renderItems(config.initialRowIndex < this.maxRenderedItems ? 0 : config.initialRowIndex); | ||
|
||
this.bindDOMListeners(); | ||
|
||
|
@@ -245,7 +248,10 @@ class VirtualScroller { | |
return; | ||
} | ||
|
||
let newStartOffset = offset; | ||
// If specified offset is in the last window into the list then | ||
// render that last window instead of starting at that offset | ||
const lastWindowOffset = this.totalItems - this.maxRenderedItems; | ||
let newStartOffset = offset > lastWindowOffset ? lastWindowOffset : offset; | ||
let newEndOffset = offset + this.maxRenderedItems; | ||
// If the default count of items to render exceeds the totalItems count | ||
// then just render up to the end | ||
|
@@ -372,6 +378,53 @@ class VirtualScroller { | |
newListEl.style.height = `${this.totalItems * (this.itemHeight + this.margin) + this.margin}px`; | ||
return newListEl; | ||
} | ||
|
||
/** | ||
* Scrolls the provided row index into view. | ||
* @param {number} rowIndex - the index of the row in the overall list | ||
* @return {void} | ||
*/ | ||
scrollIntoView(rowIndex) { | ||
if (!this.scrollingEl || rowIndex < 0 || rowIndex >= this.totalItems) { | ||
return; | ||
} | ||
|
||
// See if the list item indexed by `rowIndex` is already present | ||
const foundItem = Array.prototype.slice.call(this.listEl.children).find((listItem) => { | ||
const { bpVsRowIndex } = listItem.dataset; | ||
const parsedRowIndex = parseInt(bpVsRowIndex, 10); | ||
return parsedRowIndex === rowIndex; | ||
}); | ||
|
||
if (foundItem) { | ||
// If it is already present and visible, do nothing, but if not visible | ||
// then scroll it into view | ||
if (!this.isVisible(foundItem)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it that bad to call scrollIntoView even the thumbnail is already visible? that would allow you to get rid of isVisible and it could be kind of nice to top align the thumbnail even if it's already visible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's not necessarily bad, but since cross browser support for the "smooth" |
||
foundItem.scrollIntoView(); | ||
} | ||
} else { | ||
// If it is not present, then adjust the scrollTop so that the list item | ||
// will get rendered. | ||
const topPosition = (this.itemHeight + this.margin) * rowIndex; | ||
this.scrollingEl.scrollTop = topPosition; | ||
} | ||
} | ||
|
||
/** | ||
* Checks to see whether the provided list item element is currently visible | ||
* @param {HTMLElement} listItemEl - the list item elment | ||
* @return {boolean} Returns true if the list item is visible, false otherwise | ||
*/ | ||
isVisible(listItemEl) { | ||
if (!this.scrollingEl || !listItemEl) { | ||
return false; | ||
} | ||
|
||
const { scrollTop } = this.scrollingEl; | ||
const { offsetTop } = listItemEl; | ||
|
||
return scrollTop <= offsetTop && offsetTop <= scrollTop + this.containerHeight; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you put a () around |
||
} | ||
} | ||
|
||
export default VirtualScroller; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're already checking for
anchorEl
in each function below you could remove this , if you wantThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I can because if there is no anchorEl, then
isOpen
will return false and it will attempt to invoketoggleOpen