Initial commit
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Infinity, FileText } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const STORAGE_KEY = "pinkudex.infiniteScroll";
|
||||
const EVENT_NAME = "pinkudex:infinite-scroll-toggled";
|
||||
|
||||
export function readInfiniteScrollEnabled(): boolean {
|
||||
if (typeof window === "undefined") return true;
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
if (raw === "0") return false;
|
||||
return true;
|
||||
} catch { return true; }
|
||||
}
|
||||
|
||||
function writeInfiniteScrollEnabled(value: boolean): void {
|
||||
try { localStorage.setItem(STORAGE_KEY, value ? "1" : "0"); } catch { /* ignore */ }
|
||||
try { window.dispatchEvent(new CustomEvent(EVENT_NAME, { detail: value })); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to toggle changes from anywhere in the app. Returns the
|
||||
* current value. Updates synchronously when the toggle is flipped.
|
||||
*/
|
||||
export function useInfiniteScrollEnabled(): boolean {
|
||||
const [enabled, setEnabled] = useState<boolean>(true);
|
||||
useEffect(() => {
|
||||
setEnabled(readInfiniteScrollEnabled());
|
||||
const onChange = (e: Event) => {
|
||||
const next = (e as CustomEvent<boolean>).detail;
|
||||
setEnabled(next);
|
||||
};
|
||||
window.addEventListener(EVENT_NAME, onChange);
|
||||
// Cross-tab updates via the storage event.
|
||||
const onStorage = (e: StorageEvent) => {
|
||||
if (e.key === STORAGE_KEY) setEnabled(readInfiniteScrollEnabled());
|
||||
};
|
||||
window.addEventListener("storage", onStorage);
|
||||
return () => {
|
||||
window.removeEventListener(EVENT_NAME, onChange);
|
||||
window.removeEventListener("storage", onStorage);
|
||||
};
|
||||
}, []);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
export function InfiniteScrollToggle() {
|
||||
const enabled = useInfiniteScrollEnabled();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => writeInfiniteScrollEnabled(!enabled)}
|
||||
title={enabled ? "Infinite scroll on — click to disable (paginated only)" : "Paginated only — click to enable infinite scroll"}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center w-9 h-9 rounded-lg border transition-colors cursor-pointer",
|
||||
enabled
|
||||
? "border-[var(--color-cyan)]/50 bg-[var(--color-cyan)]/10 text-[var(--color-cyan)]"
|
||||
: "border-[var(--color-glass-border)] bg-[var(--color-glass)] text-[var(--color-fg-dim)] hover:text-[var(--color-fg)]",
|
||||
)}
|
||||
aria-pressed={enabled}
|
||||
aria-label={enabled ? "Disable infinite scroll" : "Enable infinite scroll"}
|
||||
>
|
||||
{enabled ? <Infinity className="w-4 h-4" /> : <FileText className="w-4 h-4" />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user