···2525 });
26262727 export type PaginationVariants = VariantProps<typeof pagination>;
2828+2929+ export type PaginationItem = number | "gap-start" | "gap-end";
3030+3131+ const range = (from: number, to: number): number[] =>
3232+ Array.from({ length: Math.max(to - from + 1, 0) }, (_, i) => from + i);
3333+3434+ // both ends, the current page and its two neighbours, and the two gaps
3535+ const SLOTS = 7;
3636+3737+ // the row is always SLOTS long once a gap is needed, so both gaps are permanent
3838+ // residents. that is what lets the plate be placed from an index rather than
3939+ // measured, and why paging the middle of a long range moves the numbers, not the row
4040+ export function paginate(page: number, count: number): PaginationItem[] {
4141+ if (count <= SLOTS) return range(1, count);
4242+ // near either end a gap would hide fewer pages than the slots it costs, so the
4343+ // window opens flush against that end and only the far gap is drawn
4444+ if (page < 4) return [...range(1, 5), "gap-end", count];
4545+ if (page > count - 3) return [1, "gap-start", ...range(count - 4, count)];
4646+ return [1, "gap-start", ...range(page - 1, page + 1), "gap-end", count];
4747+ }
2848</script>
29493050<script lang="ts">
···3353 import { untrack } from "svelte";
3454 import { Spring, prefersReducedMotion } from "svelte/motion";
3555 import Button from "./Button.svelte";
3636- import { paginate } from "./paginate";
37563857 interface Props {
3958 page: number;
4059 total: number;
4141- siblings?: number;
4242- boundaries?: number;
4360 onchange?: (page: number) => void;
4461 class?: string;
4562 labels?: boolean;
···4966 let {
5067 page = $bindable(1),
5168 total,
5252- siblings = 1,
5353- boundaries = 1,
5469 onchange,
5570 class: className,
5671 labels,
···59746075 const count = $derived(Math.max(total, 1));
6176 const current = $derived(Math.min(Math.max(page, 1), count));
6262- const items = $derived(paginate(current, count, siblings, boundaries));
7777+ const items = $derived(paginate(current, count));
6378 const index = $derived(items.indexOf(current));
64796580 const digits = $derived(String(count).length);
66816767- const plate = new Spring(index, { stiffness: 0.139, damping: 0.61 });
8282+ const plate = new Spring(
8383+ untrack(() => index),
8484+ { stiffness: 0.139, damping: 0.61 }
8585+ );
68866987 let direction = $state(1);
7088 let rolled = $state(false);
7171- let previous = current;
8989+ let previous = untrack(() => current);
7290 $effect.pre(() => {
7391 if (current !== previous) {
7492 direction = current > previous ? 1 : -1;