@at-flux/astroflare 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@at-flux/astroflare",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "Reusable headless components, styles, and utilities for Astro + Tailwind v4 + Cloudflare projects",
6
6
  "author": "atflux <dev@atflux.uk>",
@@ -11,6 +11,9 @@ const {
11
11
  maxPageButtons = 7,
12
12
  filtersClass = "mb-8",
13
13
  pagerClass = "mt-6",
14
+ sizeOptions = [4, 2, 8, 12, 16],
15
+ showPageSize = true,
16
+ pageSizeClass = "",
14
17
  perPage = 9,
15
18
  class: className = "",
16
19
  } = Astro.props;
@@ -47,8 +50,12 @@ const pagerItems = buildPageSequence(totalPages, currentPage, maxPageButtons).ma
47
50
  page: item,
48
51
  href: buildCollectionHref(pathname, query, { page: item, offset: (item - 1) * query.size }),
49
52
  active: item === currentPage,
53
+ disabled: totalPages <= 1 || item === currentPage,
50
54
  },
51
55
  );
56
+ const sanitizedSizeOptions = Array.from(
57
+ new Set([query.size, ...sizeOptions].filter((value) => Number.isFinite(value) && value > 0)),
58
+ ).sort((a, b) => a - b);
52
59
  ---
53
60
 
54
61
  {useServer && (
@@ -67,7 +74,32 @@ const pagerItems = buildPageSequence(totalPages, currentPage, maxPageButtons).ma
67
74
  </slot>
68
75
  <slot />
69
76
  <slot name="pager">
70
- <Pager items={pagerItems} class={pagerClass} />
77
+ <div class:list={["af-query-pager-row flex flex-col gap-3 md:flex-row md:items-center md:justify-between", pagerClass]}>
78
+ <Pager items={pagerItems} class="md:order-1 md:flex-1 md:justify-center" />
79
+ {showPageSize && (
80
+ <form method="get" action={pathname} class:list={["af-query-size-form md:order-2 md:ml-auto", pageSizeClass]}>
81
+ <input type="hidden" name="page" value="1" />
82
+ <input type="hidden" name="offset" value="0" />
83
+ {Object.keys(activeFilters).length > 0 && (
84
+ <input type="hidden" name="filters" value={JSON.stringify(activeFilters)} />
85
+ )}
86
+ <label class="inline-flex items-center gap-2 text-xs uppercase tracking-[0.08em] text-light-text-subtle dark:text-dark-text-subtle">
87
+ <span>Page size</span>
88
+ <select
89
+ name="size"
90
+ class="rounded-full border border-light-border bg-light-surface px-3 py-1.5 text-xs text-light-text-body dark:border-dark-border dark:bg-dark-surface dark:text-dark-text-body"
91
+ onchange="this.form?.submit()"
92
+ >
93
+ {sanitizedSizeOptions.map((size) => (
94
+ <option value={String(size)} selected={size === query.size}>
95
+ {size}
96
+ </option>
97
+ ))}
98
+ </select>
99
+ </label>
100
+ </form>
101
+ )}
102
+ </div>
71
103
  </slot>
72
104
  </>
73
105
  )}
@@ -8,7 +8,7 @@ interface Props {
8
8
  class?: string;
9
9
  /** Optional explicit pager items for link-based server rendering. */
10
10
  items?: Array<
11
- | { page: number; label?: string; href?: string; active?: boolean }
11
+ | { page: number; label?: string; href?: string; active?: boolean; disabled?: boolean }
12
12
  | { ellipsis: true; label?: string }
13
13
  >;
14
14
  }
@@ -22,7 +22,7 @@ const fallbackPages = Array.from({ length: Math.max(0, pageCount) }, (_, index)
22
22
  ? items.map((item) =>
23
23
  "ellipsis" in item ? (
24
24
  <span class="af-pager-ellipsis" aria-hidden="true">{item.label ?? "…"}</span>
25
- ) : item.href ? (
25
+ ) : item.href && !item.active && !item.disabled ? (
26
26
  <a
27
27
  href={item.href}
28
28
  data-page={String(item.page)}
@@ -34,6 +34,8 @@ const fallbackPages = Array.from({ length: Math.max(0, pageCount) }, (_, index)
34
34
  <button
35
35
  type="button"
36
36
  data-page={String(item.page)}
37
+ disabled={item.disabled ?? item.active}
38
+ aria-disabled={item.disabled ?? item.active}
37
39
  class:list={["af-pager-pill", item.active && "is-active"]}
38
40
  >
39
41
  {item.label ?? item.page}
@@ -68,6 +70,12 @@ const fallbackPages = Array.from({ length: Math.max(0, pageCount) }, (_, index)
68
70
  align-items: center;
69
71
  justify-content: center;
70
72
  }
73
+ .af-pager .af-pager-pill:disabled,
74
+ .af-pager .af-pager-pill[aria-disabled="true"] {
75
+ cursor: not-allowed;
76
+ opacity: 0.72;
77
+ pointer-events: none;
78
+ }
71
79
  [data-theme="dark"] .af-pager .af-pager-pill {
72
80
  border-color: var(--color-dark-border);
73
81
  color: color-mix(in oklab, var(--color-dark-text-subtle) 78%, transparent);
@@ -15,6 +15,12 @@ interface CollectionQueryBaseProps {
15
15
  filtersClass?: string;
16
16
  /** Optional wrapper class for pager row. */
17
17
  pagerClass?: string;
18
+ /** Available page sizes for server-mode size selector. */
19
+ sizeOptions?: number[];
20
+ /** Toggle server-mode page size selector visibility. */
21
+ showPageSize?: boolean;
22
+ /** Optional wrapper class for page-size selector. */
23
+ pageSizeClass?: string;
18
24
  /** Visible cards per page in client mode. */
19
25
  perPage?: number;
20
26
  /** Classes applied to the host wrapper element in client mode. */