Skip to content

Commit

Permalink
fix: fix selectedsection scroll syncing
Browse files Browse the repository at this point in the history
  • Loading branch information
Birkbjo committed Nov 12, 2024
1 parent 58d7d0a commit ee1c659
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 16 deletions.
35 changes: 21 additions & 14 deletions src/lib/form/sectionedForm/useSelectedSection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import {
useQueryParam,
createEnumParam,
Expand Down Expand Up @@ -30,7 +30,12 @@ export const scrollToSection = (
section: string,
scrollOptions?: ScrollOptions
) => {
document.getElementById(section)?.scrollIntoView(scrollOptions)
// wait for the event loop to be cleared to ensure that the element is in the DOM
// before scrolling to it, eg if navigating from a link
setTimeout(
() => document.getElementById(section)?.scrollIntoView(scrollOptions),
0
)
}

export const useSelectedSection = () => {
Expand Down Expand Up @@ -73,40 +78,42 @@ export const useSelectedSection = () => {
* Update the selected section (in searchParams) based on the section that is in view
* This keeps the selected section in sync with the section that is in view.
*/
export const useUpdateSelectedSectionOnScroll = () => {
export const useSyncSelectedSectionWithScroll = () => {
const { sections } = useSectionedFormContext()
const [selected, setSection] = useSelectedSection()
const [selectedSection, setSection] = useSelectedSection()

useEffect(() => {
// scroll to section on initial render based on search-param
document
.getElementById(selected)
?.scrollIntoView({ behavior: 'instant' })
const elem = document.getElementById(selectedSection)
if (elem) {
scrollToSection(selectedSection, { behavior: 'instant' })
}
// we only do this on first render, for links - scrollToSection should be called imperatively
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

useEffect(() => {
const sectionElements = sections
.map((section) => document.getElementById(section.name))
.filter((s) => !!s)
const currentInView = new Set<string>()

const currentInView = new Map<string, IntersectionObserverEntry>()
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
currentInView.add(entry.target.id)
currentInView.set(entry.target.id, entry)
} else {
currentInView.delete(entry.target.id)
}
})
const firstSectionInView = sections.find((s) =>
const firstVisible = sections.find((s) =>
currentInView.has(s.name)
)
if (firstSectionInView) {
setSection(firstSectionInView.name)
if (firstVisible) {
setSection(firstVisible.name)
}
},
{ threshold: 0.6 }
{ threshold: 0.5 }
)
sectionElements.forEach((section) => {
observer.observe(section)
Expand Down
4 changes: 2 additions & 2 deletions src/pages/dataSets/form/DataSetFormContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import {
SECTIONS_MAP,
useSectionedFormContext,
useSelectedSection,
useUpdateSelectedSectionOnScroll,
useSyncSelectedSectionWithScroll,
} from '../../../lib'
import { DataSetFormDescriptor } from './formDescriptor'

const section = SECTIONS_MAP.dataSet

export const DataSetFormContents = () => {
const descriptor = useSectionedFormContext<typeof DataSetFormDescriptor>()
useUpdateSelectedSectionOnScroll()
useSyncSelectedSectionWithScroll()
const [selectedSection] = useSelectedSection()
return (
<>
Expand Down

0 comments on commit ee1c659

Please sign in to comment.