Skip to content

Commit

Permalink
doc engine: fix scroll bar for the content sidebar (#1284)
Browse files Browse the repository at this point in the history
* freeze buttons block

* move tutorials to buttons block

* buttons block go right after the sidebar

* header out of scroll

* always visible scroll

* refactored some names in RightPanel

* scroll to active heading

* fix scrolling when no scroll in block

* fix
  • Loading branch information
iKBAHT committed May 21, 2020
1 parent 80a2fe1 commit b291fce
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 83 deletions.
192 changes: 116 additions & 76 deletions src/components/Documentation/RightPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'
import React, { useState, useEffect, useRef } from 'react'
import cn from 'classnames'
import throttle from 'lodash/throttle'

Expand Down Expand Up @@ -27,12 +27,14 @@ const RightPanel: React.FC<IRightPanelProps> = ({
tutorials,
githubLink
}) => {
const [height, setHeight] = useState(0)
const [documentHeight, setDocumentHeight] = useState(0)
const [headingsOffsets, setHeadingsOffsets] = useState<IHeadingsCoordinates>(
{}
)
const [current, setCurrent] = useState<string | null>(null)
const setCurrentHeader = (): void => {
const [currentHeadingSlug, setCurrentHeadingSlug] = useState<string | null>(
null
)
const updateCurrentHeader = (): void => {
const currentScroll = getScrollPosition()
const coordinateKeys = Object.keys(headingsOffsets)

Expand All @@ -41,14 +43,15 @@ const RightPanel: React.FC<IRightPanelProps> = ({
const headerHeight = getHeaderHeight()
const filteredKeys = coordinateKeys.filter(
offsetTop =>
parseInt(offsetTop, 10) <= currentScroll + (height - headerHeight) / 2
parseInt(offsetTop, 10) <=
currentScroll + (documentHeight - headerHeight) / 2
)

const newCurrent = filteredKeys.length
const newCurrentHeadingSlug = filteredKeys.length
? headingsOffsets[filteredKeys[filteredKeys.length - 1]]
: null

setCurrent(newCurrent)
setCurrentHeadingSlug(newCurrentHeadingSlug)
}

const updateHeadingsPosition = (): void => {
Expand All @@ -65,7 +68,7 @@ const RightPanel: React.FC<IRightPanelProps> = ({
{}
)
setHeadingsOffsets(offsets)
setHeight(document.documentElement.clientHeight)
setDocumentHeight(document.documentElement.clientHeight)
}

const initHeadingsPosition = (): void => {
Expand All @@ -75,7 +78,7 @@ const RightPanel: React.FC<IRightPanelProps> = ({
}

useEffect(() => {
const throttledSetCurrentHeader = throttle(setCurrentHeader, 100)
const throttledSetCurrentHeader = throttle(updateCurrentHeader, 100)

document.addEventListener('scroll', throttledSetCurrentHeader)
window.addEventListener('resize', updateHeadingsPosition)
Expand All @@ -84,93 +87,130 @@ const RightPanel: React.FC<IRightPanelProps> = ({
document.removeEventListener('scroll', throttledSetCurrentHeader)
window.removeEventListener('resize', updateHeadingsPosition)
}
}, [setCurrentHeader])
}, [updateCurrentHeader])
useEffect(initHeadingsPosition, [headings])
useEffect(setCurrentHeader, [headingsOffsets, height])
useEffect(updateCurrentHeader, [headingsOffsets, documentHeight])

const contentBlockRef = useRef<HTMLDivElement>(null)
const [
isScrollToCurrentHeadingHappened,
setIsScrollToCurrentHeadingHappened
] = useState(false)
useEffect(() => {
if (isScrollToCurrentHeadingHappened) {
return
}
if (!document.location.hash) {
setIsScrollToCurrentHeadingHappened(true)
return
}
if (currentHeadingSlug) {
setIsScrollToCurrentHeadingHappened(true)
const currentHeadingSlugElem = document.getElementById(
`link-${currentHeadingSlug}`
)
const contentBlockElem = contentBlockRef.current
if (currentHeadingSlugElem && contentBlockElem) {
const hasVerticalScrollbar =
contentBlockElem.scrollHeight > contentBlockElem.clientHeight
if (hasVerticalScrollbar) {
currentHeadingSlugElem.scrollIntoView({
block: 'start',
inline: 'nearest'
})
}
}
}
})

return (
<div className={styles.container}>
<div className={styles.list}>
{headings.length > 0 && (
<>
{headings.length > 0 && (
<>
<div>
<h5 className={styles.header}>Content</h5>
<hr className={styles.separator} />
</>
</div>
<div className={styles.contentBlock} ref={contentBlockRef}>
{headings.map(({ slug, text }) => (
<div id={`link-${slug}`} key={`link-${slug}`}>
<Link
className={cn(
styles.headingLink,
currentHeadingSlug === slug && styles.current,
'link-with-focus'
)}
href={`#${slug}`}
>
{text}
</Link>
</div>
))}
</div>
</>
)}
<div className={styles.buttonsBlock}>
{Object.keys(tutorials || {}).length > 0 && (
<div className={styles.buttonSection}>
<p className={styles.buttonSectionDescription}>
<span
className={styles.buttonSectionIcon}
role="img"
aria-label="run"
>
▶️
</span>{' '}
It can be run online:
</p>
<Tutorials
buttonClassName={cn(styles.button, styles.tutorials)}
tutorials={tutorials}
/>
</div>
)}
{headings.map(({ slug, text }) => (
<div className={styles.buttonSection}>
<p className={styles.buttonSectionDescription}>
<span
className={styles.buttonSectionIcon}
role="img"
aria-label="bug"
>
🐛
</span>{' '}
Found an issue? Let us know! Or fix it:
</p>

<Link
className={cn(
styles.headingLink,
current === slug && styles.current,
'link-with-focus'
)}
key={`link-${slug}`}
href={`#${slug}`}
className={cn(sharedStyles.button, styles.button)}
href={githubLink}
target="_blank"
>
{text}
<i className={cn(sharedStyles.buttonIcon, styles.githubIcon)} />
Edit on GitHub
</Link>
))}
</div>
{Object.keys(tutorials || {}).length > 0 && (
</div>

<div className={styles.buttonSection}>
<p className={styles.buttonSectionDescription}>
<span
className={styles.buttonSectionIcon}
role="img"
aria-label="run"
aria-label="question"
>
▶️
</span>{' '}
It can be run online:
Have a question? Join our chat, we will help you:
</p>
<Tutorials
buttonClassName={cn(styles.button, styles.tutorials)}
tutorials={tutorials}
/>
</div>
)}
<div className={styles.buttonSection}>
<p className={styles.buttonSectionDescription}>
<span
className={styles.buttonSectionIcon}
role="img"
aria-label="bug"
>
🐛
</span>{' '}
Found an issue? Let us know! Or fix it:
</p>

<Link
className={cn(sharedStyles.button, styles.button)}
href={githubLink}
target="_blank"
>
<i className={cn(sharedStyles.buttonIcon, styles.githubIcon)} />
Edit on GitHub
</Link>
</div>

<div className={styles.buttonSection}>
<p className={styles.buttonSectionDescription}>
<span
className={styles.buttonSectionIcon}
role="img"
aria-label="question"
<Link
className={cn(sharedStyles.button, styles.button)}
href="/chat"
target="_blank"
>
</span>{' '}
Have a question? Join our chat, we will help you:
</p>

<Link
className={cn(sharedStyles.button, styles.button)}
href="/chat"
target="_blank"
>
<i className={cn(sharedStyles.buttonIcon, styles.discordIcon)} />
Discord Chat
</Link>
<i className={cn(sharedStyles.buttonIcon, styles.discordIcon)} />
Discord Chat
</Link>
</div>
</div>
</div>
)
Expand Down
32 changes: 25 additions & 7 deletions src/components/Documentation/RightPanel/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,39 @@
position: sticky;
top: var(--layout-header-height-collapsed);
flex-shrink: 0;
box-sizing: border-box;
width: 170px;
height: calc(100vh - 78px);
padding-top: 57px;
font-size: 16px;
overflow-y: auto;
display: flex;
flex-direction: column;

@media only screen and (max-width: 1200px) {
display: none;
}
}

.contentBlock {
overflow-y: scroll;
margin-bottom: 27px;

&::-webkit-scrollbar {
-webkit-appearance: none;
width: 7px;
}

&::-webkit-scrollbar-thumb {
border-radius: 5px;
background-color: #7e7e7e;
}
}

.separator {
border: none;
border-top: 1px solid var(--color-lighter-blue);
}

.list {
margin-top: 57px;
margin-bottom: 27px;
}

.header {
font-size: 14px;
text-transform: uppercase;
Expand Down Expand Up @@ -49,9 +62,14 @@
}
}

.buttonsBlock {
margin-bottom: 20px;
flex-shrink: 0;
}

.buttonSection {
& + & {
margin-top: 25px;
margin-top: 15px;
}
}

Expand Down

0 comments on commit b291fce

Please sign in to comment.