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

NTP Pagination Quality of Life Improvements #14197

Merged
merged 4 commits into from Jul 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 18 additions & 7 deletions components/brave_new_tab_ui/containers/newTab/gridPageButtons.tsx
Expand Up @@ -6,7 +6,7 @@ const PageIndicator = <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/s
<circle cx="50" cy="50" r="50" />
</svg>

const ListPageButtonContainer = styled('div')<{}>`
const ListPageButtonContainer = styled('div') <{}>`
display: flex;
flex-direction: row;

Expand Down Expand Up @@ -36,16 +36,27 @@ interface GridPageButtonProps {
}

function GridPageButton (props: GridPageButtonProps) {
return <StyledButton onClick={() => props
.pageContainerRef
.current
?.children[props.page]
.scrollIntoView({ behavior: 'smooth' })}>
const handleClick = () => {
const element = props
.pageContainerRef
.current
?.children[props.page] as HTMLElement
if (!element) return

// Ideally we'd use |element.scrollIntoView| here but it applies a
// vertical scroll even when it's not needed, which triggers the Brave
// News peek.
// |element.scrollIntoViewIfNeeded| is also out, because it doesn't
// support animating the scroll.
props.pageContainerRef.current?.scrollTo({ left: element.offsetLeft, behavior: 'smooth' })
}

return <StyledButton onClick={handleClick}>
{PageIndicator}
</StyledButton>
}

const GridPageIndicatorContainer = styled('div')<{}>`
const GridPageIndicatorContainer = styled('div') <{}>`
position: absolute;
color: var(--brave-palette-white);

Expand Down
7 changes: 6 additions & 1 deletion components/brave_new_tab_ui/containers/newTab/gridSites.tsx
Expand Up @@ -16,6 +16,7 @@ import { GridPagesContainer, List, PagesContainer } from '../../components/defau
import createWidget from '../../components/default/widget'
// Constants
import { MAX_GRID_SIZE } from '../../constants/new_tab_ui'
import { useMaintainScrollPosition } from '../../helpers/scrolling'
import AddSiteTile from './addSiteTile'
import { GridPageButtons } from './gridPageButtons'
// Component groups
Expand Down Expand Up @@ -88,6 +89,8 @@ function TopSitesList (props: Props) {
props.actions.tilesReordered(gridSites, draggingIndex, droppedIndex)
}

useMaintainScrollPosition('grid-pages-container-scroll-position', gridPagesContainerRef)

return <PagesContainer>
<GridPagesContainer customLinksEnabled={customLinksEnabled} ref={gridPagesContainerRef as any}>
<DndContext onDragEnd={handleDragEnd} autoScroll={autoScrollOptions} sensors={sensors}>
Expand All @@ -97,7 +100,9 @@ function TopSitesList (props: Props) {
</SortableContext>
</DndContext>
</GridPagesContainer>
{customLinksEnabled && <GridPageButtons numPages={pageCount} pageContainerRef={gridPagesContainerRef} />}
{customLinksEnabled &&
pageCount > 1 &&
<GridPageButtons numPages={pageCount} pageContainerRef={gridPagesContainerRef} />}
</PagesContainer>
}

Expand Down
29 changes: 29 additions & 0 deletions components/brave_new_tab_ui/helpers/scrolling.ts
@@ -1,4 +1,5 @@
import * as React from 'react'
import { debounce } from '../../common/debounce'

const overflowScrollableRegex = /(auto)|(scroll)/g
const isScrollable = (element: Element) => {
Expand Down Expand Up @@ -33,3 +34,31 @@ export const useParentScrolled = (element: HTMLElement | null, handler: (e: Even
}
}, [handler, element])
}

export const useMaintainScrollPosition = (localStorageKey: string, elementRef: React.MutableRefObject<HTMLElement | undefined>, bufferRate = 200) => {
React.useEffect(() => {
if (!elementRef.current) return

const debouncedSave = debounce(() => {
window.localStorage.setItem(localStorageKey, JSON.stringify({ top: elementRef.current?.scrollTop, left: elementRef.current?.scrollLeft }))
}, bufferRate)

const handler = () => {
debouncedSave()
}

elementRef.current.addEventListener('scroll', handler)

return () => {
elementRef.current?.removeEventListener('scroll', handler)
}
}, [elementRef, localStorageKey, bufferRate])

React.useEffect(() => {
if (!elementRef.current) return

const scrollPosition: { top: number, left: number } | null = JSON.parse(localStorage.getItem(localStorageKey) ?? 'null')
if (!scrollPosition) return
elementRef.current.scrollTo(scrollPosition)
}, [elementRef, localStorageKey])
}