import { createEffect, createMemo, createSignal, For, Show, untrack } from "solid-js";
import { clamp } from "../../utils";
import { createPointerGestureElement } from "../PointerGesture";
const DRAG_PIXEL_STEP = 18;
const PAGE_BAR_CELL_CLASS = "!ui-hit-w-sm !ui-hit-h-sm !p-0 ui-rounded-sm cursor-pointer text-center align-middle select-none";
const PAGE_BAR_LINK_CLASS = "flex !ui-hit-w-sm !ui-hit-h-sm items-center justify-center box-border !p-0 ui-rounded-sm !border textsize-sm font-inherit no-underline hover:no-underline active:no-underline";
const PAGE_BAR_LINK_COLOR_CLASS = "!border-transparent !bg-transparent !text-[var(--color-site-text)] visited:!text-[var(--color-site-text)] hover:!bg-[var(--color-site-item-hover)] hover:!text-[var(--color-site-text)] active:!text-[var(--color-site-text)]";
const PAGE_BAR_CURRENT_COLOR_CLASS = "!border-transparent !bg-[color-mix(in_srgb,var(--color-site-page)_82%,black)] !text-[var(--color-site-text)]";
const PAGE_BAR_DISABLED_COLOR_CLASS = "!border-transparent !bg-[color-mix(in_srgb,var(--color-site-page)_82%,black)] !text-[var(--color-site-text)] opacity-40 cursor-default";
const PAGE_BAR_TABLE_CLASS = "border-separate border-spacing-[var(--ui-space-xs)]";
type PageBarItemState = "link" | "current" | "disabled";
export function GalleryPageDescription(props: { text: string }) {
return
{props.text}
;
}
export function ScrollPageBar(props: {
currentIndex: number;
maxIndex: number;
urlForIndex: (index: number) => string;
onNavigate: (previewIndex: number, scrollToPageBar: "bottom" | "top") => void;
}) {
const maxIndex = createMemo(() => Math.max(0, props.maxIndex));
const currentIndex = createMemo(() => clamp(props.currentIndex, 0, maxIndex()));
const [visibleCenterIndex, setVisibleCenterIndex] = createSignal(untrack(currentIndex));
let gestureHost!: HTMLDivElement;
let dragStartVisibleCenterIndex = untrack(visibleCenterIndex);
const draggable = () => maxIndex() + 1 > 7;
const slots = createMemo(() => pageSlots(visibleCenterIndex(), maxIndex()));
const firstSlotIndex = createMemo(() => slots()[0] ?? currentIndex());
const lastSlotIndex = createMemo(() => slots()[slots().length - 1] ?? currentIndex());
const currentBeforeWindow = () => currentIndex() < firstSlotIndex();
const currentAfterWindow = () => currentIndex() > lastSlotIndex();
const scrollTargetForIndex = (pageIndex: number): "bottom" | "top" =>
pageIndex === currentIndex() - 1 || pageIndex === maxIndex() ? "bottom" : "top";
createEffect(() => {
setVisibleCenterIndex(currentIndex());
});
const linkCell = (
text: string | (() => string),
pageIndex: number | (() => number),
itemState: () => PageBarItemState = () => "link",
) => {
const resolvedText = (): string => typeof text === "function" ? text() : text;
const resolvedPageIndex = (): number => typeof pageIndex === "function" ? pageIndex() : pageIndex;
return
{resolvedText()}
}
>
{
event.preventDefault();
event.stopPropagation();
props.onNavigate(
resolvedPageIndex(),
scrollTargetForIndex(resolvedPageIndex()),
);
}}
>
{resolvedText()}
| ;
};
const emptyCell = () => (
|
);
createPointerGestureElement(
() => gestureHost,
() => ({
shouldCaptureDrag: draggable,
dragAxis: "x",
onStart: () => {
dragStartVisibleCenterIndex = visibleCenterIndex();
},
onMove: (info) => {
if (Math.abs(info.dx) < Math.abs(info.dy)) {
return;
}
const nextIndex = clamp(
dragStartVisibleCenterIndex - acceleratedPageOffset(info.dx),
0,
maxIndex(),
);
if (nextIndex === visibleCenterIndex()) {
return;
}
setVisibleCenterIndex(nextIndex);
},
}),
);
return (
{linkCell("<<", 0, () => currentIndex() === 0 ? "disabled" : "link")}
{linkCell(() => String(currentIndex() + 1), currentIndex, () => "current")}
{linkCell("<", () => Math.max(0, currentIndex() - 1), () => currentIndex() === 0 ? "disabled" : "link")}
{(pageIndex) => {
const itemState = createMemo(() =>
pageIndex === currentIndex() ? "current" : "link",
);
return pageIndex !== null
? |
{pageIndex + 1}
}
>
{
event.preventDefault();
event.stopPropagation();
props.onNavigate(pageIndex, scrollTargetForIndex(pageIndex));
}}
>
{pageIndex + 1}
|
: emptyCell();
}}
{linkCell(">", () => Math.min(maxIndex(), currentIndex() + 1), () => currentIndex() === maxIndex() ? "disabled" : "link")}
{linkCell(() => String(currentIndex() + 1), currentIndex, () => "current")}
{linkCell(">>", maxIndex, () => currentIndex() === maxIndex() ? "disabled" : "link")}
);
}
function pageSlots(visibleCenterIndex: number, maxIndex: number): Array {
if (maxIndex + 1 <= 7) {
return range(0, maxIndex);
}
const visibleStartIndex = clamp(visibleCenterIndex - 3, -1, maxIndex - 5);
return range(visibleStartIndex, visibleStartIndex + 6).map((pageIndex) =>
pageIndex >= 0 && pageIndex <= maxIndex ? pageIndex : null,
);
}
function range(start: number, end: number): number[] {
const output: number[] = [];
for (let index = start; index <= end; index += 1) {
output.push(index);
}
return output;
}
function acceleratedPageOffset(dx: number): number {
const distance = Math.abs(dx);
const direction = dx > 0 ? 1 : -1;
const pages = Math.floor((distance / DRAG_PIXEL_STEP) ** 1.35);
return direction * pages;
}