From 195df444366a6702059e022f8b806fd478fb85d9 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 12 Jun 2026 15:06:48 +0300 Subject: [PATCH 1/3] refactor(pagination): extract shared site-wide Pagination component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six surfaces each carried their own pagination: IoT Hub's link-based PaginationLink + runtime updater, a button shell shared by Device Library and Partners (with the windowing JS copy-pasted into both), and bespoke innerHTML renderers on the blog and case-studies indexes. Three drifted copies of the windowing algorithm, four styling vocabularies, and recurring a11y gaps. Replace all of it with src/components/Pagination/: - Pagination.astro — one nav, two modes: basePath renders real anchors (SSG, rel=prev/next, crawlable); without it, buttons that dispatch bubbling tb-pagination:page-change events. Numbered list on desktop, compact 'Page X of Y' row below lg. Brand tokens (indigo/lavender current-page pill), aria-current in both modes, focus-visible rings. - pagination-client.ts — updatePagination() rebuilds the lists for client-driven surfaces; restores keyboard focus to the equivalent control after each rebuild (fixes a focus-loss bug all the old implementations shared). - pagination-shared.ts — the single windowing algorithm + strings. - PerPageSelector.astro — the IoT-Hub-only items-per-page dropdown, composed in via the 'controls' slot from consumer files so its script/styles ship only to IoT Hub pages (Astro bundles per import graph, not per render). Consumers keep what is theirs: URL state (?page= on blog), scroll behavior, sessionStorage restore (case-studies), and the dynamic search pipeline (IoT Hub) — the component only renders and reports. Intended deltas: unified look on all surfaces, blog gains prev/next chevrons, grid surfaces show full number rows up to 7 pages (was 5), Device Library/Partners drop the redundant always-visible 'Page X of Y' line (lives in the sub-lg compact layout now). --- .../DeviceLibrary/DeviceLibrary.astro | 119 +--- src/components/DeviceLibrary/Pagination.astro | 160 ----- src/components/IotHub/PaginationChevron.astro | 31 - src/components/IotHub/PaginationLink.astro | 567 ------------------ src/components/IotHub/SearchFilterBar.astro | 12 +- .../IotHub/iot-hub-dynamic-search.ts | 27 +- .../IotHub/iot-hub-pagination-update.ts | 168 ------ src/components/Pagination/Pagination.astro | 366 +++++++++++ .../Pagination/PerPageSelector.astro | 294 +++++++++ .../Pagination/pagination-client.ts | 151 +++++ .../Pagination/pagination-shared.ts | 37 ++ src/components/Partners/PartnerLibrary.astro | 100 +-- src/models/iot-hub.ts | 9 - src/pages/blog/index.astro | 94 +-- src/pages/case-studies/index.astro | 167 +----- src/pages/iot-hub/[category]/[...page].astro | 9 +- 16 files changed, 961 insertions(+), 1350 deletions(-) delete mode 100644 src/components/DeviceLibrary/Pagination.astro delete mode 100644 src/components/IotHub/PaginationChevron.astro delete mode 100644 src/components/IotHub/PaginationLink.astro delete mode 100644 src/components/IotHub/iot-hub-pagination-update.ts create mode 100644 src/components/Pagination/Pagination.astro create mode 100644 src/components/Pagination/PerPageSelector.astro create mode 100644 src/components/Pagination/pagination-client.ts create mode 100644 src/components/Pagination/pagination-shared.ts diff --git a/src/components/DeviceLibrary/DeviceLibrary.astro b/src/components/DeviceLibrary/DeviceLibrary.astro index b58cf7ac7b..c4c54f5619 100644 --- a/src/components/DeviceLibrary/DeviceLibrary.astro +++ b/src/components/DeviceLibrary/DeviceLibrary.astro @@ -3,7 +3,7 @@ import { Icon } from 'astro-icon/components'; import DeviceCard from './DeviceCard.astro'; import FilterSidebar from './FilterSidebar.astro'; import SearchBar from './SearchBar.astro'; -import Pagination from './Pagination.astro'; +import Pagination from '@components/Pagination/Pagination.astro'; import { PLATFORM_VALUES } from '~/util/device-platform'; interface Device { @@ -39,10 +39,6 @@ const t = { noResults: 'No devices match your filters.', devicesFound: 'devices found', searchPlaceholder: 'Search devices...', - previous: 'Previous', - next: 'Next', - page: 'Page', - of: 'of', }; // Extract unique filter values with counts @@ -158,7 +154,7 @@ const initialDevices = devicesWithHref.slice(0, PAGE_SIZE); {t.noResults} - +