mirror of
https://github.com/cheeaun/phanpy.git
synced 2024-11-25 10:45:41 +03:00
Rebuild useScroll, less states
This commit is contained in:
parent
de3787209e
commit
d7d838ebf8
3 changed files with 214 additions and 33 deletions
|
@ -1092,6 +1092,13 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
|
||||||
transform: translate(-50%, 0);
|
transform: translate(-50%, 0);
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
|
||||||
|
header[hidden] & {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, -100%) scale(0.9);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.updates-button .icon {
|
.updates-button .icon {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { groupBoosts, groupContext } from '../utils/timeline-utils';
|
||||||
import useInterval from '../utils/useInterval';
|
import useInterval from '../utils/useInterval';
|
||||||
import usePageVisibility from '../utils/usePageVisibility';
|
import usePageVisibility from '../utils/usePageVisibility';
|
||||||
import useScroll from '../utils/useScroll';
|
import useScroll from '../utils/useScroll';
|
||||||
|
import useScrollFn from '../utils/useScrollFn';
|
||||||
|
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
import Link from './link';
|
import Link from './link';
|
||||||
|
@ -203,17 +204,47 @@ function Timeline({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
// const {
|
||||||
scrollDirection,
|
// scrollDirection,
|
||||||
nearReachStart,
|
// nearReachStart,
|
||||||
nearReachEnd,
|
// nearReachEnd,
|
||||||
reachStart,
|
// reachStart,
|
||||||
reachEnd,
|
// reachEnd,
|
||||||
} = useScroll({
|
// } = useScroll({
|
||||||
scrollableRef,
|
// scrollableRef,
|
||||||
distanceFromEnd: 2,
|
// distanceFromEnd: 2,
|
||||||
scrollThresholdStart: 44,
|
// scrollThresholdStart: 44,
|
||||||
});
|
// });
|
||||||
|
const headerRef = useRef();
|
||||||
|
// const [hiddenUI, setHiddenUI] = useState(false);
|
||||||
|
const [nearReachStart, setNearReachStart] = useState(false);
|
||||||
|
useScrollFn(
|
||||||
|
{
|
||||||
|
scrollableRef,
|
||||||
|
distanceFromEnd: 2,
|
||||||
|
scrollThresholdStart: 44,
|
||||||
|
},
|
||||||
|
({
|
||||||
|
scrollDirection,
|
||||||
|
nearReachStart,
|
||||||
|
nearReachEnd,
|
||||||
|
reachStart,
|
||||||
|
reachEnd,
|
||||||
|
}) => {
|
||||||
|
// setHiddenUI(scrollDirection === 'end' && !nearReachEnd);
|
||||||
|
if (headerRef.current) {
|
||||||
|
const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
||||||
|
headerRef.current.hidden = hiddenUI;
|
||||||
|
}
|
||||||
|
setNearReachStart(nearReachStart);
|
||||||
|
if (reachStart) {
|
||||||
|
loadItems(true);
|
||||||
|
} else if (nearReachEnd || (reachEnd && showMore)) {
|
||||||
|
loadItems();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
scrollableRef.current?.scrollTo({ top: 0 });
|
scrollableRef.current?.scrollTo({ top: 0 });
|
||||||
|
@ -223,17 +254,17 @@ function Timeline({
|
||||||
loadItems(true);
|
loadItems(true);
|
||||||
}, [refresh]);
|
}, [refresh]);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (reachStart) {
|
// if (reachStart) {
|
||||||
loadItems(true);
|
// loadItems(true);
|
||||||
}
|
// }
|
||||||
}, [reachStart]);
|
// }, [reachStart]);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (nearReachEnd || (reachEnd && showMore)) {
|
// if (nearReachEnd || (reachEnd && showMore)) {
|
||||||
loadItems();
|
// loadItems();
|
||||||
}
|
// }
|
||||||
}, [nearReachEnd, showMore]);
|
// }, [nearReachEnd, showMore]);
|
||||||
|
|
||||||
const prevView = useRef(view);
|
const prevView = useRef(view);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -304,7 +335,7 @@ function Timeline({
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
// const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FilterContext.Provider value={filterContext}>
|
<FilterContext.Provider value={filterContext}>
|
||||||
|
@ -321,7 +352,8 @@ function Timeline({
|
||||||
>
|
>
|
||||||
<div class="timeline-deck deck">
|
<div class="timeline-deck deck">
|
||||||
<header
|
<header
|
||||||
hidden={hiddenUI}
|
ref={headerRef}
|
||||||
|
// hidden={hiddenUI}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (!e.target.closest('a, button')) {
|
if (!e.target.closest('a, button')) {
|
||||||
scrollableRef.current?.scrollTo({
|
scrollableRef.current?.scrollTo({
|
||||||
|
@ -356,7 +388,7 @@ function Timeline({
|
||||||
</div>
|
</div>
|
||||||
{items.length > 0 &&
|
{items.length > 0 &&
|
||||||
uiState !== 'loading' &&
|
uiState !== 'loading' &&
|
||||||
!hiddenUI &&
|
// !hiddenUI &&
|
||||||
showNew && (
|
showNew && (
|
||||||
<button
|
<button
|
||||||
class="updates-button shiny-pill"
|
class="updates-button shiny-pill"
|
||||||
|
@ -657,13 +689,27 @@ function TimelineItem({
|
||||||
|
|
||||||
function StatusCarousel({ title, class: className, children }) {
|
function StatusCarousel({ title, class: className, children }) {
|
||||||
const carouselRef = useRef();
|
const carouselRef = useRef();
|
||||||
const { reachStart, reachEnd, init } = useScroll({
|
// const { reachStart, reachEnd, init } = useScroll({
|
||||||
scrollableRef: carouselRef,
|
// scrollableRef: carouselRef,
|
||||||
direction: 'horizontal',
|
// direction: 'horizontal',
|
||||||
});
|
// });
|
||||||
useEffect(() => {
|
const startButtonRef = useRef();
|
||||||
init?.();
|
const endButtonRef = useRef();
|
||||||
}, []);
|
useScrollFn(
|
||||||
|
{
|
||||||
|
scrollableRef: carouselRef,
|
||||||
|
direction: 'horizontal',
|
||||||
|
init: true,
|
||||||
|
},
|
||||||
|
({ reachStart, reachEnd }) => {
|
||||||
|
if (startButtonRef.current) startButtonRef.current.disabled = reachStart;
|
||||||
|
if (endButtonRef.current) endButtonRef.current.disabled = reachEnd;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
// useEffect(() => {
|
||||||
|
// init?.();
|
||||||
|
// }, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={`status-carousel ${className}`}>
|
<div class={`status-carousel ${className}`}>
|
||||||
|
@ -671,9 +717,10 @@ function StatusCarousel({ title, class: className, children }) {
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<span>
|
<span>
|
||||||
<button
|
<button
|
||||||
|
ref={startButtonRef}
|
||||||
type="button"
|
type="button"
|
||||||
class="small plain2"
|
class="small plain2"
|
||||||
disabled={reachStart}
|
// disabled={reachStart}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
carouselRef.current?.scrollBy({
|
carouselRef.current?.scrollBy({
|
||||||
left: -Math.min(320, carouselRef.current?.offsetWidth),
|
left: -Math.min(320, carouselRef.current?.offsetWidth),
|
||||||
|
@ -684,9 +731,10 @@ function StatusCarousel({ title, class: className, children }) {
|
||||||
<Icon icon="chevron-left" />
|
<Icon icon="chevron-left" />
|
||||||
</button>{' '}
|
</button>{' '}
|
||||||
<button
|
<button
|
||||||
|
ref={endButtonRef}
|
||||||
type="button"
|
type="button"
|
||||||
class="small plain2"
|
class="small plain2"
|
||||||
disabled={reachEnd}
|
// disabled={reachEnd}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
carouselRef.current?.scrollBy({
|
carouselRef.current?.scrollBy({
|
||||||
left: Math.min(320, carouselRef.current?.offsetWidth),
|
left: Math.min(320, carouselRef.current?.offsetWidth),
|
||||||
|
|
126
src/utils/useScrollFn.js
Normal file
126
src/utils/useScrollFn.js
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import { useEffect, useLayoutEffect, useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
export default function useScrollFn(
|
||||||
|
{
|
||||||
|
scrollableRef,
|
||||||
|
distanceFromStart = 1, // ratio of clientHeight/clientWidth
|
||||||
|
distanceFromEnd = 1, // ratio of clientHeight/clientWidth
|
||||||
|
scrollThresholdStart = 10,
|
||||||
|
scrollThresholdEnd = 10,
|
||||||
|
direction = 'vertical',
|
||||||
|
distanceFromStartPx: _distanceFromStartPx,
|
||||||
|
distanceFromEndPx: _distanceFromEndPx,
|
||||||
|
init,
|
||||||
|
} = {},
|
||||||
|
callback,
|
||||||
|
deps,
|
||||||
|
) {
|
||||||
|
if (!callback) return;
|
||||||
|
const [scrollDirection, setScrollDirection] = useState(null);
|
||||||
|
const [reachStart, setReachStart] = useState(false);
|
||||||
|
const [reachEnd, setReachEnd] = useState(false);
|
||||||
|
const [nearReachStart, setNearReachStart] = useState(false);
|
||||||
|
const [nearReachEnd, setNearReachEnd] = useState(false);
|
||||||
|
const isVertical = direction === 'vertical';
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const scrollableElement = scrollableRef.current;
|
||||||
|
if (!scrollableElement) return {};
|
||||||
|
let previousScrollStart = isVertical
|
||||||
|
? scrollableElement.scrollTop
|
||||||
|
: scrollableElement.scrollLeft;
|
||||||
|
|
||||||
|
function onScroll() {
|
||||||
|
const {
|
||||||
|
scrollTop,
|
||||||
|
scrollLeft,
|
||||||
|
scrollHeight,
|
||||||
|
scrollWidth,
|
||||||
|
clientHeight,
|
||||||
|
clientWidth,
|
||||||
|
} = scrollableElement;
|
||||||
|
const scrollStart = isVertical ? scrollTop : scrollLeft;
|
||||||
|
const scrollDimension = isVertical ? scrollHeight : scrollWidth;
|
||||||
|
const clientDimension = isVertical ? clientHeight : clientWidth;
|
||||||
|
const scrollDistance = Math.abs(scrollStart - previousScrollStart);
|
||||||
|
const distanceFromStartPx =
|
||||||
|
_distanceFromStartPx ||
|
||||||
|
Math.min(
|
||||||
|
clientDimension * distanceFromStart,
|
||||||
|
scrollDimension,
|
||||||
|
scrollStart,
|
||||||
|
);
|
||||||
|
const distanceFromEndPx =
|
||||||
|
_distanceFromEndPx ||
|
||||||
|
Math.min(
|
||||||
|
clientDimension * distanceFromEnd,
|
||||||
|
scrollDimension,
|
||||||
|
scrollDimension - scrollStart - clientDimension,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
scrollDistance >=
|
||||||
|
(previousScrollStart < scrollStart
|
||||||
|
? scrollThresholdEnd
|
||||||
|
: scrollThresholdStart)
|
||||||
|
) {
|
||||||
|
setScrollDirection(previousScrollStart < scrollStart ? 'end' : 'start');
|
||||||
|
previousScrollStart = scrollStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
setReachStart(scrollStart <= 0);
|
||||||
|
setReachEnd(scrollStart + clientDimension >= scrollDimension);
|
||||||
|
setNearReachStart(scrollStart <= distanceFromStartPx);
|
||||||
|
setNearReachEnd(
|
||||||
|
scrollStart + clientDimension >= scrollDimension - distanceFromEndPx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollableElement.addEventListener('scroll', onScroll, { passive: true });
|
||||||
|
|
||||||
|
return () => scrollableElement.removeEventListener('scroll', onScroll);
|
||||||
|
}, [
|
||||||
|
distanceFromStart,
|
||||||
|
distanceFromEnd,
|
||||||
|
scrollThresholdStart,
|
||||||
|
scrollThresholdEnd,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
callback({
|
||||||
|
scrollDirection,
|
||||||
|
reachStart,
|
||||||
|
reachEnd,
|
||||||
|
nearReachStart,
|
||||||
|
nearReachEnd,
|
||||||
|
});
|
||||||
|
}, [
|
||||||
|
scrollDirection,
|
||||||
|
reachStart,
|
||||||
|
reachEnd,
|
||||||
|
nearReachStart,
|
||||||
|
nearReachEnd,
|
||||||
|
...deps,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (init && scrollableRef.current) {
|
||||||
|
queueMicrotask(() => {
|
||||||
|
scrollableRef.current.dispatchEvent(new Event('scroll'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [init]);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// scrollDirection,
|
||||||
|
// reachStart,
|
||||||
|
// reachEnd,
|
||||||
|
// nearReachStart,
|
||||||
|
// nearReachEnd,
|
||||||
|
// init: () => {
|
||||||
|
// if (scrollableRef.current) {
|
||||||
|
// scrollableRef.current.dispatchEvent(new Event('scroll'));
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
}
|
Loading…
Reference in a new issue