mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
free floating "go to top" button on inifinite scroll pages
This commit is contained in:
1 parent
ac7cbe5aca
commit
574b1417dc
7 files changed
+68
-8
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in new issue
Block a user