From 8d1e6989c27a8539bfa8945e355766253daa9ded Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Mon, 10 Jan 2022 16:47:29 +0700 Subject: [PATCH] added experimental scroll helper --- .../common/ScrollHelperBottom/index.tsx | 27 ++++++++++++++ .../ScrollHelperBottom/styles.module.scss | 26 ++++++++++++++ .../profile/ProfileAvatar/styles.module.scss | 2 +- src/hooks/dom/useScrollHeight.ts | 35 +++++++++++++++++++ src/hooks/dom/useScrollToBottom.ts | 10 ++++++ src/hooks/dom/useScrollToTop.ts | 1 - src/hooks/dom/useScrollTop.ts | 2 +- src/layouts/NodeLayout/index.tsx | 6 +++- 8 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 src/components/common/ScrollHelperBottom/index.tsx create mode 100644 src/components/common/ScrollHelperBottom/styles.module.scss create mode 100644 src/hooks/dom/useScrollHeight.ts create mode 100644 src/hooks/dom/useScrollToBottom.ts diff --git a/src/components/common/ScrollHelperBottom/index.tsx b/src/components/common/ScrollHelperBottom/index.tsx new file mode 100644 index 00000000..31c5ccf5 --- /dev/null +++ b/src/components/common/ScrollHelperBottom/index.tsx @@ -0,0 +1,27 @@ +import React, { VFC } from 'react'; +import styles from './styles.module.scss'; +import { useScrollTop } from '~/hooks/dom/useScrollTop'; +import { useScrollHeight } from '~/hooks/dom/useScrollHeight'; +import classNames from 'classnames'; +import { useScrollToBottom } from '~/hooks/dom/useScrollToBottom'; + +interface ScrollHelperBottomProps {} + +const ScrollHelperBottom: VFC = () => { + const top = useScrollTop(); + const scrollHeight = useScrollHeight(); + const scrollToBottom = useScrollToBottom(); + + const isVisible = scrollHeight > 2000 && top < scrollHeight * 0.25; + + return ( +
+ Вниз +
+ ); +}; + +export { ScrollHelperBottom }; diff --git a/src/components/common/ScrollHelperBottom/styles.module.scss b/src/components/common/ScrollHelperBottom/styles.module.scss new file mode 100644 index 00000000..263b9c74 --- /dev/null +++ b/src/components/common/ScrollHelperBottom/styles.module.scss @@ -0,0 +1,26 @@ +@import "src/styles/variables"; + +.helper { + position: fixed; + bottom: 0; + background: radial-gradient($red, transparent) 50% 24px no-repeat; + background-size: 100% 48px; + display: none; + width: calc(100% - 20px); + z-index: 4; + text-align: center; + max-width: 500px; + height: 64px; + align-items: flex-end; + justify-content: center; + padding: $gap; + text-transform: uppercase; + font: $font_16_medium; + border-radius: $radius $radius 0 0; + user-select: none; + cursor: pointer; + + &.visible { + display: flex; + } +} diff --git a/src/components/profile/ProfileAvatar/styles.module.scss b/src/components/profile/ProfileAvatar/styles.module.scss index bb3b6741..2d829e6a 100644 --- a/src/components/profile/ProfileAvatar/styles.module.scss +++ b/src/components/profile/ProfileAvatar/styles.module.scss @@ -19,7 +19,7 @@ left: 0; width: 100%; height: 100%; - opacity: 0; + opacity: 1; } &:hover { diff --git a/src/hooks/dom/useScrollHeight.ts b/src/hooks/dom/useScrollHeight.ts new file mode 100644 index 00000000..80596fec --- /dev/null +++ b/src/hooks/dom/useScrollHeight.ts @@ -0,0 +1,35 @@ +import { useEffect, useState } from 'react'; + +const getHeight = () => { + if (typeof document === 'undefined') { + return 0; + } + + const body = document.body; + const html = document.documentElement; + + return Math.max( + body.scrollHeight, + body.offsetHeight, + html.clientHeight, + html.scrollHeight, + html.offsetHeight + ); +}; +export const useScrollHeight = () => { + const [scrollHeight, setScrollHeight] = useState(getHeight()); + + useEffect(() => { + const measure = () => setScrollHeight(getHeight()); + + window.addEventListener('scroll', measure); + window.addEventListener('resize', measure); + + return () => { + window.removeEventListener('scroll', measure); + window.removeEventListener('resize', measure); + }; + }, []); + + return scrollHeight; +}; diff --git a/src/hooks/dom/useScrollToBottom.ts b/src/hooks/dom/useScrollToBottom.ts new file mode 100644 index 00000000..2622e1aa --- /dev/null +++ b/src/hooks/dom/useScrollToBottom.ts @@ -0,0 +1,10 @@ +import { useCallback } from 'react'; +import { useScrollHeight } from '~/hooks/dom/useScrollHeight'; + +export const useScrollToBottom = () => { + const top = useScrollHeight(); + + return useCallback(() => { + window.scrollTo({ top }); + }, [top]); +}; diff --git a/src/hooks/dom/useScrollToTop.ts b/src/hooks/dom/useScrollToTop.ts index 5adc71d0..3eeb1cd0 100644 --- a/src/hooks/dom/useScrollToTop.ts +++ b/src/hooks/dom/useScrollToTop.ts @@ -14,7 +14,6 @@ export const useScrollToTop = (deps?: any[]) => { const bounds = targetElement.getBoundingClientRect(); window.scrollTo({ top: bounds.top - 100, - behavior: 'smooth', }); }, deps && Array.isArray(deps) ? deps : [] diff --git a/src/hooks/dom/useScrollTop.ts b/src/hooks/dom/useScrollTop.ts index 90341f42..701d6712 100644 --- a/src/hooks/dom/useScrollTop.ts +++ b/src/hooks/dom/useScrollTop.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; export const useScrollTop = () => { - const [top, setTop] = useState(0); + const [top, setTop] = useState(typeof window !== 'undefined' ? window.scrollY : 0); useEffect(() => { const onScroll = () => { diff --git a/src/layouts/NodeLayout/index.tsx b/src/layouts/NodeLayout/index.tsx index df0831f4..05cd2a80 100644 --- a/src/layouts/NodeLayout/index.tsx +++ b/src/layouts/NodeLayout/index.tsx @@ -17,6 +17,8 @@ import { useNodeContext } from '~/utils/context/NodeContextProvider'; import { useNodePermissions } from '~/hooks/node/useNodePermissions'; import { useNodeActions } from '~/hooks/node/useNodeActions'; import { NodeTitle } from '~/components/node/NodeTitle'; +import { ScrollHelperBottom } from '~/components/common/ScrollHelperBottom'; +import { Superpower } from '~/components/boris/Superpower'; type IProps = {}; @@ -65,7 +67,9 @@ const NodeLayout: FC = () => { - + + + ); };