Skip to content

Commit da3623f

Browse files
authored
Merge pull request #395 from coderoad/fix/scroll
Fix/scroll
2 parents 91eae34 + 4e6b79c commit da3623f

File tree

4 files changed

+96
-15
lines changed

4 files changed

+96
-15
lines changed

Diff for: web-app/src/containers/Tutorial/components/Level.tsx

-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ const styles = {
1515
display: 'flex' as 'flex',
1616
flexDirection: 'column' as 'column',
1717
padding: 0,
18-
paddingBottom: '5rem',
1918
},
2019

2120
text: {
@@ -39,13 +38,6 @@ type Props = {
3938
}
4039

4140
const Level = ({ level }: Props) => {
42-
const pageBottomRef = React.useRef(null)
43-
const scrollToBottom = () => {
44-
// @ts-ignore
45-
pageBottomRef.current.scrollIntoView({ behavior: 'smooth' })
46-
}
47-
React.useEffect(scrollToBottom, [level.id])
48-
4941
return (
5042
<div css={styles.page}>
5143
<div css={styles.content}>
@@ -54,8 +46,6 @@ const Level = ({ level }: Props) => {
5446
{level.content.length && level.steps.length ? <div css={styles.separator} /> : null}
5547

5648
<Steps steps={level.steps} />
57-
58-
<div ref={pageBottomRef} />
5949
</div>
6050
</div>
6151
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import * as React from 'react'
2+
import { Icon } from '@alifd/next'
3+
4+
const styles = {
5+
scrollIndicator: {
6+
position: 'fixed' as 'fixed',
7+
display: 'flex' as 'flex',
8+
justifyContent: 'center' as 'center',
9+
alignItems: 'center' as 'center',
10+
left: 'calc(50% - 8px)',
11+
borderRadius: '100%',
12+
zIndex: 100,
13+
bottom: '2.2rem',
14+
boxShadow: '0 0 0 5px transparent',
15+
},
16+
}
17+
18+
type Props = {
19+
item: string
20+
children: React.ReactElement
21+
}
22+
23+
const ScrollContent = ({ item, children }: Props) => {
24+
const [showScrollIndicator, setShowScrollIndicator] = React.useState<'UNDETERMINED' | 'SHOW' | 'HIDE'>('UNDETERMINED')
25+
const pageTopRef: React.RefObject<any> = React.useRef(null)
26+
const pageBottomRef: React.RefObject<any> = React.useRef(null)
27+
28+
const scrollToTop = () => {
29+
pageTopRef.current.scrollIntoView({ behavior: 'smooth' })
30+
let hideTimeout: any
31+
32+
// API to detect if an HTML element is in the viewport
33+
// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
34+
const observer = new IntersectionObserver(
35+
([entry]) => {
36+
// show a scroll indicator to let the user know
37+
// they can scroll down for more
38+
const isVisible = entry.isIntersecting
39+
if (!isVisible && showScrollIndicator === 'UNDETERMINED') {
40+
setShowScrollIndicator('SHOW')
41+
}
42+
hideTimeout = setTimeout(() => {
43+
setShowScrollIndicator('HIDE')
44+
observer.unobserve(pageBottomRef.current)
45+
}, 2000)
46+
},
47+
{ rootMargin: '0px' },
48+
)
49+
50+
const showTimeout = setTimeout(() => {
51+
// detect if bottom of page is visible
52+
53+
if (pageBottomRef.current) {
54+
observer.observe(pageBottomRef.current)
55+
}
56+
}, 600)
57+
return () => {
58+
// cleanup timeouts & subs
59+
observer.unobserve(pageBottomRef.current)
60+
clearTimeout(showTimeout)
61+
clearTimeout(hideTimeout)
62+
}
63+
}
64+
65+
React.useEffect(scrollToTop, [item])
66+
67+
return (
68+
<div css={{ position: 'relative' }}>
69+
<div ref={pageTopRef} />
70+
{children}
71+
{showScrollIndicator === 'SHOW' ? (
72+
<div style={styles.scrollIndicator}>
73+
<Icon type="arrow-down" size="small" />
74+
</div>
75+
) : null}
76+
<div ref={pageBottomRef} />
77+
</div>
78+
)
79+
}
80+
81+
export default ScrollContent

Diff for: web-app/src/containers/Tutorial/index.tsx

+11-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ import { DISPLAY_RUN_TEST_BUTTON } from '../../environment'
1313
import formatLevels from './formatLevels'
1414
import Reset from './components/Reset'
1515
import Continue from './components/Continue'
16+
import ScrollContent from './components/ScrollContent'
1617

1718
const styles = {
19+
page: {
20+
paddingBottom: '5rem',
21+
},
1822
header: {
1923
display: 'flex' as 'flex',
2024
alignItems: 'center',
@@ -47,6 +51,7 @@ const styles = {
4751
left: 0,
4852
right: 0,
4953
color: 'white',
54+
zIndex: 1000,
5055
},
5156
processes: {
5257
padding: '0 1rem',
@@ -112,15 +117,19 @@ const TutorialPage = (props: PageProps) => {
112117

113118
return (
114119
<div>
115-
<div>
120+
<div css={styles.page}>
116121
<div css={styles.header}>
117122
<a onClick={() => setMenuVisible(!menuVisible)}>
118123
<Icon type="toggle-left" size="small" style={{ color: '#333' }} />
119124
</a>
120125
<span css={styles.title}>{tutorial.summary.title}</span>
121126
</div>
122127

123-
{page === 'level' && <Level level={level} />}
128+
{page === 'level' && (
129+
<ScrollContent item={level.id}>
130+
<Level level={level} />
131+
</ScrollContent>
132+
)}
124133
{page === 'review' && <ReviewPage levels={levels} />}
125134
{/* {page === 'settings' && <SettingsPage />} */}
126135
</div>

Diff for: web-app/stories/Tutorial.stories.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ const context: Partial<T.MachineContext> = {
3636
id: '1',
3737
title: 'First Level',
3838
summary: 'A summary of the first level',
39-
content: 'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!',
39+
content:
40+
'Should support markdown test\n ```js\nvar a = 1\n```\nwhew it works!\nShould support markdown test\n ```js\nvar a = 1\n```\nwhew it works!\nShould support markdown test\n ```js\nvar a = 1\n```\nwhew it works!\nShould support markdown test\n ```js\nvar a = 1\n```\nwhew it works!',
4041
setup: null,
4142
status: 'COMPLETE' as 'COMPLETE',
4243
steps: [
@@ -147,6 +148,6 @@ storiesOf('Tutorial', module)
147148
position: { levelId: '1', stepId: '1.2' },
148149
progress: { levels: {}, steps: {}, complete: false },
149150
}
150-
return <Tutorial context={firstLevel} send={action('send')} />
151+
return <Tutorial state="Normal" context={firstLevel} send={action('send')} />
151152
})
152-
.add('3 step', () => <Tutorial context={context} send={action('send')} />)
153+
.add('3 step', () => <Tutorial state="Normal" context={context} send={action('send')} />)

0 commit comments

Comments
 (0)