free floating "go to top" button on inifinite scroll pages

This commit is contained in:
Victor committed 2024-01-31 16:19:58 -08:00
1 parent ac7cbe5aca
commit 574b1417dc
7 files changed
+68 -8

No files matched your search

+13
View File
@@ -329,6 +329,19 @@ $headings-font-family: "Fira Sans", sans-serif;
height: 100%;
pointer-events: none;
}
.to-top-button-off-screen {
position: fixed;
width: fit-content;
white-space: nowrap;
top: 5rem;
margin: 0 auto;
left: 0;
right: 0;
z-index: 3;
box-shadow: 0px 5px 20px rgba($black, 0.3);
}
}
@media screen and (max-height: 740px) {
@@ -7,6 +7,7 @@ import Tippy from "@tippyjs/react";
import "tippy.js/dist/tippy.css";
export default function Button({
buttonRef,
label,
icon,
small,
@@ -81,7 +82,7 @@ export default function Button({
{ButtonContent}
</a>
) : (
<button type="button" {...commonProps} {...rest}>
<button {...{ ...commonProps, ...rest, type: "button", ref: buttonRef }}>
{ButtonContent}
</button>
);
@@ -2,6 +2,7 @@ import { ButtonHTMLAttributes } from "react";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
export default interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
buttonRef?: any;
label?: string | null;
icon?: IconDefinition;
small?: boolean;
+2
View File
@@ -12,6 +12,7 @@ import useLazyLists from './useLazyLists';
import useListContent from './useListContent';
import useLocalize from './useLocalize';
import useMedia from './useMedia';
import useOnScreen from "./useOnScreen";
import useRatings from './useRatings';
import useSdUpload from './useSdUpload';
import useSession from './useSession';
@@ -33,6 +34,7 @@ export {
useListContent,
useLocalize,
useMedia,
useOnScreen,
useRatings,
useSdUpload,
useSession,
@@ -45,6 +45,9 @@ export default function useLazyLists({
status === FetchStatus.ready || status === FetchStatus.in_progress;
const fetchError = status === FetchStatus.error;
const [goingtToTop,goingtToTopSet] = useState(false);
const [y,ySet] = useState(0);
const getMore = () => {
if (next) statusSet(1);
};
@@ -67,6 +70,8 @@ export default function useLazyLists({
nextSet("");
previousSet("");
statusSet(FetchStatus.ready);
window.scrollTo(0,0);
goingtToTopSet(true);
}
useEffect(() => {
@@ -76,7 +81,15 @@ export default function useLazyLists({
...(sort ? { sort_ascending: true } : {}),
};
if (status === FetchStatus.ready) {
const adjustY = () => ySet(window.pageYOffset);
if (goingtToTop) {
window.addEventListener('scroll', adjustY, { passive: true });
if (y === 0) setTimeout(() => goingtToTopSet(false),500);
}
else window.removeEventListener('scroll',adjustY);
if (status === FetchStatus.ready && !goingtToTop) {
let search = new URLSearchParams(queries).toString();
statusSet(FetchStatus.in_progress);
!disableUrlQueries && history.replace({ pathname, search });
@@ -114,6 +127,7 @@ export default function useLazyLists({
addQueries,
debug,
fetcher,
goingtToTop,
history,
listKeys,
listSet,
@@ -124,6 +138,7 @@ export default function useLazyLists({
status,
totalKeys,
disableUrlQueries,
y
]);
return {
@@ -11,7 +11,7 @@ import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContex
import SkeletonCard from "components/common/Card/SkeletonCard";
import { ListMediaFiles } from "@storyteller/components/src/api/media_files/ListMediaFiles";
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia";
import { useBookmarks, useLazyLists, useRatings } from "hooks";
import { useBookmarks, useLazyLists, useOnScreen, useRatings } from "hooks";
import InfiniteScroll from "react-infinite-scroll-component";
import prepFilter from "resources/prepFilter";
@@ -20,6 +20,9 @@ export default function MediaTab() {
const urlQueries = new URLSearchParams(search);
const bookmarks = useBookmarks();
const ratings = useRatings();
const toTopBtnRef = useRef<HTMLDivElement | null>(null);
const onScreen = useOnScreen(toTopBtnRef,"0px");
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const [mediaType, mediaTypeSet] = useState(
urlQueries.get("filter_media_type") || "all"
@@ -59,8 +62,6 @@ export default function MediaTab() {
// { value: "mostliked", label: "Most Liked" },
];
console.log("💎",media.urlCursor);
return (
<>
<div className="d-flex flex-wrap gap-3 mb-3">
@@ -84,8 +85,20 @@ export default function MediaTab() {
}}
/>
</div>
{ media.urlCursor ? <Button {...{ label: "Back to top", onClick: () => media.reset() }}/> : null }
{ media.urlCursor ?
<Button {...{
className: `to-top-button`,
buttonRef: toTopBtnRef,
label: "Back to top",
onClick: () => media.reset(),
}}/> : null }
</div>
{ media.urlCursor && !onScreen ?
<Button {...{
className: `to-top-button-off-screen`,
label: "Back to top",
onClick: () => media.reset(),
}}/> : null }
<AudioPlayerProvider>
{media.isLoading && !media.list.length ? (
<div className="row gx-3 gy-3">
@@ -94,6 +107,7 @@ export default function MediaTab() {
))}
</div>
) : (
<InfiniteScroll
dataLength={media.list.length}
next={media.getMore}
@@ -12,7 +12,7 @@ import SkeletonCard from "components/common/Card/SkeletonCard";
import { ListWeights } from "@storyteller/components/src/api/weights/ListWeights";
import { Weight } from "@storyteller/components/src/api/weights/GetWeight";
import InfiniteScroll from "react-infinite-scroll-component";
import { useBookmarks, useLazyLists, useRatings } from "hooks";
import { useBookmarks, useLazyLists, useOnScreen, useRatings } from "hooks";
import prepFilter from "resources/prepFilter";
export default function WeightsTab() {
@@ -25,6 +25,8 @@ export default function WeightsTab() {
const [weightCategory, weightCategorySet] = useState(urlQueries.get("maybe_scoped_weight_category") || "all");
const [showMasonryGrid, setShowMasonryGrid] = useState(true);
const [list, listSet] = useState<Weight[]>([]);
const toTopBtnRef = useRef<HTMLDivElement | null>(null);
const onScreen = useOnScreen(toTopBtnRef,"0px");
const weights = useLazyLists({
addQueries: {
page_size: 24,
@@ -145,8 +147,20 @@ export default function WeightsTab() {
/>
)} */}
</div>
{ weights.urlCursor ? <Button {...{ label: "Back to top", onClick: () => weights.reset() }}/> : null }
{ weights.urlCursor ?
<Button {...{
className: `to-top-button`,
buttonRef: toTopBtnRef,
label: "Back to top",
onClick: () => weights.reset(),
}}/> : null }
</div>
{ weights.urlCursor && !onScreen ?
<Button {...{
className: `to-top-button-off-screen`,
label: "Back to top",
onClick: () => weights.reset(),
}}/> : null }
<AudioPlayerProvider>
{weights.isLoading && !weights.list.length ? (
<div className="row gx-3 gy-3">