Skip to content

Commit 8cc3cb4

Browse files
committed
feat: enhance LockIndicator with countdown timer
- Added a countdown timer to the LockIndicator component that activates when scrolling stops. - Updated styles for the lock icon and countdown timer to improve visibility and positioning. - Implemented polling and event listeners to manage the countdown state effectively.
1 parent 1c1740c commit 8cc3cb4

File tree

2 files changed

+117
-5
lines changed

2 files changed

+117
-5
lines changed

src/frontend/src/CustomEmbeddableRenderer.scss

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,57 @@
3232
&.visible {
3333
opacity: 1;
3434
}
35+
36+
.lock-container {
37+
position: relative;
38+
display: flex;
39+
align-items: center;
40+
justify-content: center;
41+
width: 28px;
42+
height: 28px;
43+
}
44+
45+
.countdown-timer {
46+
position: absolute;
47+
top: 50%;
48+
left: 50%;
49+
transform: translate(-50%, -50%);
50+
width: 28px;
51+
height: 28px;
52+
z-index: -1; /* Place behind the lock icon */
53+
}
54+
55+
.circular-timer {
56+
width: 100%;
57+
height: 100%;
58+
transform: rotate(-90deg);
59+
60+
.timer-background {
61+
fill: none;
62+
stroke: rgba(211, 211, 211, 0.2);
63+
stroke-width: 2;
64+
}
65+
66+
.timer-progress {
67+
fill: none;
68+
stroke: #d3d3d3;
69+
stroke-width: 2;
70+
stroke-dasharray: 100.53; /* Circumference of circle with r=16: 2 * PI * 16 */
71+
stroke-dashoffset: 0;
72+
animation-name: countdown;
73+
animation-timing-function: linear;
74+
animation-fill-mode: forwards;
75+
}
76+
}
77+
}
78+
79+
@keyframes countdown {
80+
from {
81+
stroke-dashoffset: 0;
82+
}
83+
to {
84+
stroke-dashoffset: 100.53; /* Same as stroke-dasharray */
85+
}
3586
}
3687

3788
&__content {

src/frontend/src/CustomEmbeddableRenderer.tsx

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,27 +90,88 @@ export const renderCustomEmbeddable = (
9090
}
9191
};
9292

93-
// Lock icon component that shows when scrolling
93+
// Lock icon component that shows when scrolling with countdown timer
9494
const LockIndicator = () => {
9595
const [visible, setVisible] = useState(false);
96+
const [counting, setCounting] = useState(false);
97+
const debounceTime = 500; // Match the debounce time from lockEmbeddables
9698

9799
useEffect(() => {
100+
// Use polling to check the scrolling state directly
101+
const checkScrollState = () => {
102+
// If we're scrolling and not already visible, show the lock
103+
if (isScrolling && !visible) {
104+
setVisible(true);
105+
setCounting(false);
106+
}
107+
// If we're not scrolling but the lock is visible and not counting down
108+
else if (!isScrolling && visible && !counting) {
109+
// Start the countdown
110+
setCounting(true);
111+
112+
// After the debounce time, hide the lock
113+
setTimeout(() => {
114+
setVisible(false);
115+
setCounting(false);
116+
}, debounceTime);
117+
}
118+
};
119+
120+
// Check the scroll state every 50ms
121+
const intervalId = setInterval(checkScrollState, 50);
122+
123+
// Also listen for the custom event as a backup
98124
const handleScrollStateChange = (event: CustomEvent<{ isScrolling: boolean }>) => {
99-
setVisible(event.detail.isScrolling);
125+
if (event.detail.isScrolling) {
126+
// When scrolling starts, show the lock and reset countdown
127+
setVisible(true);
128+
setCounting(false);
129+
} else if (!counting) {
130+
// When scrolling stops, start the countdown
131+
setCounting(true);
132+
133+
// After the debounce time, hide the lock
134+
setTimeout(() => {
135+
setVisible(false);
136+
setCounting(false);
137+
}, debounceTime);
138+
}
100139
};
101140

102141
// Add event listener for scroll state changes
103142
document.addEventListener('scrollStateChange', handleScrollStateChange as EventListener);
104143

105144
// Clean up
106145
return () => {
146+
clearInterval(intervalId);
107147
document.removeEventListener('scrollStateChange', handleScrollStateChange as EventListener);
108148
};
109-
}, []);
149+
}, [visible, counting]);
110150

111151
return (
112152
<div className={`custom-embed__lock-icon ${visible ? 'visible' : ''}`}>
113-
<Lock size={16} />
153+
<div className="lock-container">
154+
<Lock size={18} />
155+
{counting && (
156+
<div className="countdown-timer">
157+
<svg viewBox="0 0 36 36" className="circular-timer">
158+
<circle
159+
className="timer-background"
160+
cx="18"
161+
cy="18"
162+
r="16"
163+
/>
164+
<circle
165+
className="timer-progress"
166+
cx="18"
167+
cy="18"
168+
r="16"
169+
style={{ animationDuration: `${debounceTime}ms` }}
170+
/>
171+
</svg>
172+
</div>
173+
)}
174+
</div>
114175
</div>
115176
);
116177
};
@@ -142,4 +203,4 @@ const debouncedScrollEnd = debounce(() => {
142203
// Dispatch event with updated scrolling state
143204
scrollStateChangeEvent.detail.isScrolling = false;
144205
document.dispatchEvent(scrollStateChangeEvent);
145-
}, 500); // 500ms debounce seems reasonable, but can be adjusted as needed
206+
}, 1000); // 500ms debounce seems reasonable, but can be adjusted as needed

0 commit comments

Comments
 (0)