Skip to content

Commit 9776fab

Browse files
committed
Extract sticky mode logic into a custom hook and implement a hide animation on scroll down
- should show sticky mode only on scroll-up because currently it obscures the view as user scrolls down. - the sticky mode hides abruptly when scrolling down, should use the same animation but in reverse - move the sticky mode handling to a separate hook - simplify the sticky mode as possible while preserving the behavior
1 parent 826d25a commit 9776fab

3 files changed

Lines changed: 85 additions & 24 deletions

File tree

‎apps/editor/src/views/panel/frontend/Home/Home.module.scss‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,18 @@
8888
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
8989
position: sticky;
9090
top: 10px;
91-
animation: stickyFadeIn var(--transition-duration-150)
91+
animation: stickyFadeIn var(--transition-duration-250)
9292
var(--transition-timing-function);
9393

9494
&::after {
9595
height: 20px;
9696
}
9797
}
98+
99+
&--hiding {
100+
animation: stickyFadeOut var(--transition-duration-250)
101+
var(--transition-timing-function) forwards;
102+
}
98103
}
99104

100105
&__empty {
@@ -151,14 +156,25 @@
151156
@keyframes stickyFadeIn {
152157
from {
153158
opacity: 0;
154-
transform: translateY(-10px);
159+
transform: translateY(-15px);
155160
}
156161
to {
157162
opacity: 1;
158163
transform: translateY(0);
159164
}
160165
}
161166

167+
@keyframes stickyFadeOut {
168+
from {
169+
opacity: 1;
170+
transform: translateY(0);
171+
}
172+
to {
173+
opacity: 0;
174+
transform: translateY(-15px);
175+
}
176+
}
177+
162178
.add-button {
163179
appearance: none;
164180
background: none;

‎apps/editor/src/views/panel/frontend/Home/Home.tsx‎

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useRef, useEffect } from 'react'
1+
import { useState, useEffect } from 'react'
22
import styles from './Home.module.scss'
33
import { Scrollable as UiScrollable } from '@ui/components/editor/panel/Scrollable'
44
import { Checkpoints as UiCheckpoints } from '@ui/components/editor/panel/Checkpoints'
@@ -15,6 +15,7 @@ import { IconButton as UiIconButton } from '@ui/components/editor/common/IconBut
1515
import { Tasks as UiTasks } from '@ui/components/editor/panel/Tasks'
1616
import { Task } from '@shared/types/task'
1717
import { use_tasks } from './hooks/use-tasks'
18+
import { use_sticky_mode } from './hooks/use-sticky-mode'
1819
import { SettingsButton as UiSettingsButton } from '@ui/components/editor/panel/SettingsButton'
1920

2021
type Props = {
@@ -46,11 +47,8 @@ type Props = {
4647

4748
export const Home: React.FC<Props> = (props) => {
4849
const { t } = use_translation()
49-
const [is_mode_sticky, set_is_mode_sticky] = useState(false)
5050
const [active_tab, set_active_tab] = useState<'tasks' | 'checkpoints'>('tasks')
51-
const full_mode_height_ref = useRef<number>(0)
52-
const responses_ref = useRef<HTMLDivElement>(null)
53-
const mode_ref = useRef<HTMLDivElement>(null)
51+
const { is_mode_sticky, is_hiding, responses_ref, mode_ref, handle_scroll } = use_sticky_mode()
5452

5553
useEffect(() => {
5654
const handle_mouse_up = (event: MouseEvent) => {
@@ -106,22 +104,7 @@ export const Home: React.FC<Props> = (props) => {
106104
/>
107105
</div>
108106

109-
<UiScrollable
110-
on_scroll={(top) => {
111-
const responses_height = responses_ref.current?.clientHeight || 0
112-
const current_mode_height = mode_ref.current?.offsetHeight || 0
113-
114-
if (!is_mode_sticky && current_mode_height > 0) {
115-
full_mode_height_ref.current = current_mode_height
116-
}
117-
118-
const height_to_use = is_mode_sticky
119-
? full_mode_height_ref.current
120-
: current_mode_height
121-
122-
set_is_mode_sticky(top > responses_height + height_to_use + 4)
123-
}}
124-
>
107+
<UiScrollable on_scroll={handle_scroll}>
125108
<div
126109
className={cn(styles.content, {
127110
[styles['content--sticky']]: is_mode_sticky
@@ -150,7 +133,8 @@ export const Home: React.FC<Props> = (props) => {
150133

151134
<div
152135
className={cn(styles.inner__mode, {
153-
[styles['inner__mode--sticky']]: is_mode_sticky
136+
[styles['inner__mode--sticky']]: is_mode_sticky,
137+
[styles['inner__mode--hiding']]: is_hiding
154138
})}
155139
ref={mode_ref}
156140
>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { useState, useRef, useEffect, useCallback } from 'react'
2+
3+
export const use_sticky_mode = () => {
4+
const [state, set_state] = useState<'normal' | 'sticky' | 'hiding'>('normal')
5+
const timeout = useRef<ReturnType<typeof setTimeout> | null>(null)
6+
const normal_height = useRef(0)
7+
const responses_ref = useRef<HTMLDivElement>(null)
8+
const mode_ref = useRef<HTMLDivElement>(null)
9+
const prev_top = useRef(0)
10+
11+
const is_mode_sticky = state !== 'normal'
12+
13+
useEffect(() => {
14+
return () => { if (timeout.current) clearTimeout(timeout.current) }
15+
}, [])
16+
17+
const handle_scroll = useCallback(
18+
(top: number) => {
19+
const r_height = responses_ref.current?.clientHeight || 0
20+
const m_height = mode_ref.current?.offsetHeight || 0
21+
22+
if (!is_mode_sticky && m_height > 0) {
23+
normal_height.current = m_height
24+
}
25+
26+
const height_to_use = is_mode_sticky ? normal_height.current : m_height
27+
const is_past = top > r_height + height_to_use + 4
28+
29+
const is_up = top < prev_top.current
30+
const is_down = top > prev_top.current
31+
prev_top.current = top
32+
33+
set_state((prev) => {
34+
if (!is_past) {
35+
if (timeout.current) clearTimeout(timeout.current)
36+
return 'normal'
37+
}
38+
if (is_up && prev !== 'sticky') {
39+
if (timeout.current) clearTimeout(timeout.current)
40+
return 'sticky'
41+
}
42+
if (is_down && prev === 'sticky') {
43+
timeout.current = setTimeout(() => {
44+
set_state((s) => (s === 'hiding' ? 'normal' : s))
45+
}, 250)
46+
return 'hiding'
47+
}
48+
return prev
49+
})
50+
},
51+
[is_mode_sticky]
52+
)
53+
54+
return {
55+
is_mode_sticky,
56+
is_hiding: state === 'hiding',
57+
responses_ref,
58+
mode_ref,
59+
handle_scroll
60+
}
61+
}

0 commit comments

Comments
 (0)