Feat: Sync Navbar Section Dropdown
Added section context that can be set when section heading scrolls into browser viewport and it's value can be used to set the current value of the navbar dropdown. Added kebab case utility method for converting title to element id.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { Fragment } from 'react';
|
||||
import { themes, useThemeContext } from '../theme';
|
||||
import { themes, useThemeContext } from '../context/theme';
|
||||
import { DocSection } from '../types/types';
|
||||
import styles from './Main.module.css';
|
||||
import Section from './Section';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useState } from 'react';
|
||||
import { themes, useThemeContext } from '../theme';
|
||||
import { themes, useThemeContext } from '../context/theme';
|
||||
import { DocsStructure } from '../types/types';
|
||||
import styles from './NavBar.module.css';
|
||||
import SectionDropdown from './SectionDropdown';
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { themes, useThemeContext } from '../theme';
|
||||
import { useEffect } from 'react';
|
||||
import { useSectionContext } from '../context/section';
|
||||
import { themes, useThemeContext } from '../context/theme';
|
||||
import { DocSection, DocsStructure } from '../types/types';
|
||||
import Main from './Main';
|
||||
import NavBar from './NavBar';
|
||||
@@ -17,26 +18,31 @@ export default function Page({
|
||||
}) {
|
||||
const { theme } = useThemeContext();
|
||||
const themeStyles = themes[theme];
|
||||
const docRef = useRef<HTMLDivElement>(null);
|
||||
const [currentSection, setCurrentSection] = useState('');
|
||||
|
||||
const { setSection } = useSectionContext();
|
||||
|
||||
useEffect(() => {
|
||||
const titles =
|
||||
document.querySelectorAll<HTMLHeadingElement>(
|
||||
'.section-title'
|
||||
'article:nth-child(1) > h1'
|
||||
);
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
setCurrentSection(entry.target.id);
|
||||
setSection(entry.target.id);
|
||||
history.pushState(
|
||||
null,
|
||||
'',
|
||||
`#${entry.target.id}`
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
root: null,
|
||||
rootMargin: '-10% 0% -80% 0%',
|
||||
rootMargin: '-10% 0% -70% 0%',
|
||||
threshold: 1,
|
||||
}
|
||||
);
|
||||
@@ -46,12 +52,12 @@ export default function Page({
|
||||
return () => {
|
||||
titles.forEach(title => observer.unobserve(title));
|
||||
};
|
||||
}, []);
|
||||
}, [setSection]);
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper} style={themeStyles}>
|
||||
<SideBar version={version} />
|
||||
<div ref={docRef} className={styles.container}>
|
||||
<div className={styles.container}>
|
||||
<NavBar version={version} />
|
||||
<Main versionSections={versionSections} />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Fragment } from 'react';
|
||||
import { useSectionContext } from '../context/section';
|
||||
import { Doc, DocsStructure } from '../types/types';
|
||||
import { toKebabCase } from '../utils/stringUtils';
|
||||
import styles from './SectionDropdown.module.css';
|
||||
|
||||
function SectionDropdownOption({ doc }: { doc: Doc }) {
|
||||
@@ -9,6 +11,7 @@ function SectionDropdownOption({ doc }: { doc: Doc }) {
|
||||
<option
|
||||
className={styles.selectItem}
|
||||
label={doc.title}
|
||||
value={toKebabCase(doc.title)}
|
||||
>
|
||||
{doc.title}
|
||||
</option>
|
||||
@@ -17,6 +20,7 @@ function SectionDropdownOption({ doc }: { doc: Doc }) {
|
||||
<optgroup label={doc.title}>
|
||||
{doc.children.map(child => (
|
||||
<option
|
||||
value={toKebabCase(child.title)}
|
||||
className={styles.selectItem}
|
||||
label={child.title}
|
||||
key={child.title}
|
||||
@@ -35,11 +39,10 @@ export default function SectionDropdown({
|
||||
}: {
|
||||
version: DocsStructure;
|
||||
}) {
|
||||
const { section, setSection } = useSectionContext();
|
||||
const handleSelectChange = (e: any) => {
|
||||
const selectedOption = e.target.value;
|
||||
location.hash = selectedOption
|
||||
.replaceAll(' ', '-')
|
||||
.toLowerCase();
|
||||
setSection(e.target.value);
|
||||
location.hash = e.target.value;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -47,6 +50,7 @@ export default function SectionDropdown({
|
||||
onChange={handleSelectChange}
|
||||
title="navigation dropdown"
|
||||
className={styles.dropdownSelect}
|
||||
value={section}
|
||||
>
|
||||
{version.docs.map(doc => {
|
||||
return (
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
import Link from 'next/link.js';
|
||||
import { useState } from 'react';
|
||||
import { useThemeContext } from '../theme';
|
||||
import { Doc, DocsStructure } from '../types/types.js';
|
||||
import { useThemeContext } from '../context/theme';
|
||||
import { Doc, DocsStructure } from '../types/types';
|
||||
import { toKebabCase } from '../utils/stringUtils';
|
||||
import styles from './SideBar.module.css';
|
||||
|
||||
function TreeNode({ doc }: { doc: Doc }) {
|
||||
@@ -19,9 +20,7 @@ function TreeNode({ doc }: { doc: Doc }) {
|
||||
<div className={styles.expandable}>
|
||||
{doc.copy ? (
|
||||
<a
|
||||
href={`#${doc.title
|
||||
.replaceAll(' ', '-')
|
||||
.toLowerCase()}`}
|
||||
href={`#${toKebabCase(doc.title)}`}
|
||||
className={styles.link}
|
||||
>
|
||||
{doc.title}
|
||||
@@ -66,9 +65,7 @@ function TreeNode({ doc }: { doc: Doc }) {
|
||||
</div>
|
||||
) : doc.copy ? (
|
||||
<a
|
||||
href={`#${doc.title
|
||||
.replaceAll(' ', '-')
|
||||
.toLowerCase()}`}
|
||||
href={`#${toKebabCase(doc.title)}`}
|
||||
className={styles.link}
|
||||
>
|
||||
{doc.title}
|
||||
|
||||
Reference in New Issue
Block a user