[Refactoring] seMarkScrolleduseScrollNotch: Use pseudo-element instead of border

This commit is contained in:
Alexander Zinchuk 2025-08-21 12:05:22 +02:00
parent 2520dae3b7
commit 745f4c6ba2
7 changed files with 91 additions and 71 deletions

View File

@ -3,6 +3,10 @@
#Settings {
height: 100%;
.with-notch::before {
top: var(--header-height);
}
> .Transition_slide {
overflow: hidden;
display: flex;
@ -40,12 +44,6 @@
.settings-content {
overflow-y: scroll;
height: calc(100% - var(--header-height));
border-top: 1px solid transparent;
transition: border-top-color 0.2s ease-in-out;
&.scrolled {
border-top-color: var(--color-borders);
}
&.password-form .input-group.error label::first-letter {
text-transform: uppercase;

View File

@ -11,7 +11,7 @@ import { resolveTransitionName } from '../../../util/resolveTransitionName.ts';
import useTwoFaReducer from '../../../hooks/reducers/useTwoFaReducer';
import useLastCallback from '../../../hooks/useLastCallback';
import useMarkScrolled from '../../../hooks/useMarkScrolled/useMarkScrolled';
import useScrollNotch from '../../../hooks/useScrollNotch.ts';
import Transition from '../../ui/Transition';
import SettingsFolders from './folders/SettingsFolders';
@ -172,7 +172,7 @@ const Settings: FC<OwnProps> = ({
const [twoFaState, twoFaDispatch] = useTwoFaReducer();
const [privacyPasscode, setPrivacyPasscode] = useState<string>('');
useMarkScrolled({
useScrollNotch({
containerRef,
selector: '.settings-content',
}, [currentScreen]);

View File

@ -8,29 +8,6 @@
height: 100%;
&::before {
content: "";
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 1px;
opacity: 0;
background-color: var(--color-borders);
transition: opacity 0.2s ease-in-out;
}
&.scrolled {
&::before {
opacity: 1;
}
}
> .profile-info > .ChatInfo {
grid-area: chat_info;

View File

@ -22,7 +22,7 @@ import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
import useHistoryBack from '../../hooks/useHistoryBack';
import useLastCallback from '../../hooks/useLastCallback';
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
import useMarkScrolled from '../../hooks/useMarkScrolled/useMarkScrolled';
import useScrollNotch from '../../hooks/useScrollNotch.ts';
import useWindowSize from '../../hooks/window/useWindowSize';
import Transition from '../ui/Transition';
@ -140,7 +140,7 @@ const RightColumn: FC<OwnProps & StateProps> = ({
const renderingContentKey = useCurrentOrPrev(contentKey, true, !isChatSelected) ?? -1;
useMarkScrolled({
useScrollNotch({
containerRef,
selector: ':scope .custom-scroll, :scope .panel-content',
}, [contentKey, managementScreen, chatId, threadId]);

View File

@ -1,38 +0,0 @@
import type { ElementRef } from '../../lib/teact/teact';
import { useEffect } from '../../lib/teact/teact';
import { requestMutation } from '../../lib/fasterdom/fasterdom';
import { throttle } from '../../util/schedulers';
const THROTTLE_DELAY = 100;
const useMarkScrolled = ({
containerRef, selector,
}: {
containerRef: ElementRef<HTMLDivElement>;
selector: string;
}, deps: unknown[]) => {
useEffect(() => {
const elements = containerRef?.current?.querySelectorAll(selector);
if (!elements?.length) return undefined;
const handleScroll = throttle((event: Event) => {
const target = event.target as HTMLElement;
const isScrolled = target.scrollTop > 0;
requestMutation(() => {
target.classList.toggle('scrolled', isScrolled);
});
}, THROTTLE_DELAY);
elements.forEach((el) => el.addEventListener('scroll', handleScroll, { passive: true }));
// Trigger the scroll handler immediately to apply the current state
elements.forEach((el) => el.dispatchEvent(new Event('scroll', { bubbles: false })));
return () => {
elements.forEach((el) => el.removeEventListener('scroll', handleScroll));
};
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
}, [containerRef, selector, ...deps]);
};
export default useMarkScrolled;

View File

@ -0,0 +1,58 @@
import type { ElementRef } from '@teact';
import { useEffect, useLayoutEffect } from '@teact';
import { addExtraClass, removeExtraClass, toggleExtraClass } from '@teact/teact-dom.ts';
import { requestMutation } from '../lib/fasterdom/fasterdom.ts';
import { throttle } from '../util/schedulers.ts';
const THROTTLE_DELAY = 100;
const useScrollNotch = ({
containerRef,
selector,
}: {
containerRef: ElementRef<HTMLDivElement>;
selector: string;
}, deps: unknown[]) => {
useLayoutEffect(() => {
const elements = containerRef.current?.querySelectorAll<HTMLElement>(selector);
if (!elements?.length) return undefined;
const handleScroll = throttle((event: Event) => {
const target = event.target as HTMLElement;
const isScrolled = target.scrollTop > 0;
requestMutation(() => {
toggleExtraClass(target, 'scrolled', isScrolled);
});
}, THROTTLE_DELAY);
elements.forEach((el) => {
addExtraClass(el, 'with-notch');
el.addEventListener('scroll', handleScroll, { passive: true });
});
return () => {
elements.forEach((el) => {
el.removeEventListener('scroll', handleScroll);
removeExtraClass(el, 'with-notch');
});
};
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
}, [containerRef, selector, ...deps]);
useEffect(() => {
const elements = containerRef.current?.querySelectorAll<HTMLElement>(selector);
if (!elements?.length) return undefined;
elements.forEach((el) => {
const isScrolled = el.scrollTop > 0;
requestMutation(() => {
toggleExtraClass(el, 'scrolled', isScrolled);
});
});
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
}, [containerRef, selector, ...deps]);
};
export default useScrollNotch;

View File

@ -359,6 +359,31 @@ body:not(.is-ios) {
height: 100%;
}
.with-notch {
&::before {
content: "";
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 1px;
opacity: 0;
background-color: var(--color-borders);
transition: opacity 0.2s ease-in-out;
}
&.scrolled {
&::before {
opacity: 1;
}
}
}
@keyframes grow-icon {
0% {
transform: scale(0.5);