@djangocfg/ui-core 2.1.574 → 2.1.576
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": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.576",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"check:contrast": "node scripts/check-preset-contrast.mjs"
|
|
131
131
|
},
|
|
132
132
|
"peerDependencies": {
|
|
133
|
-
"@djangocfg/i18n": "^2.1.
|
|
133
|
+
"@djangocfg/i18n": "^2.1.576",
|
|
134
134
|
"consola": "^3.4.2",
|
|
135
135
|
"lucide-react": "^0.545.0",
|
|
136
136
|
"moment": "^2.30.1",
|
|
@@ -206,9 +206,9 @@
|
|
|
206
206
|
"vaul": "1.1.2"
|
|
207
207
|
},
|
|
208
208
|
"devDependencies": {
|
|
209
|
-
"@djangocfg/eslint-config": "^2.1.
|
|
210
|
-
"@djangocfg/i18n": "^2.1.
|
|
211
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
209
|
+
"@djangocfg/eslint-config": "^2.1.576",
|
|
210
|
+
"@djangocfg/i18n": "^2.1.576",
|
|
211
|
+
"@djangocfg/typescript-config": "^2.1.576",
|
|
212
212
|
"@storybook/react-vite": "^10.5.0",
|
|
213
213
|
"@types/node": "^24.13.3",
|
|
214
214
|
"@types/react": "19.2.15",
|
|
@@ -9,7 +9,7 @@ import { useIsMobile } from '../../../hooks';
|
|
|
9
9
|
import { useLocation, useQueryParams } from '../../../hooks/router';
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
-
Pagination, PaginationContent,
|
|
12
|
+
Pagination, PaginationContent, PaginationItem, PaginationLink,
|
|
13
13
|
PaginationNext, PaginationPrevious
|
|
14
14
|
} from './pagination';
|
|
15
15
|
|
|
@@ -64,16 +64,23 @@ export const SSRPagination: React.FC<SSRPaginationProps> = ({
|
|
|
64
64
|
const t = useAppT();
|
|
65
65
|
const { locale } = useI18n();
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
/* The URL wins when it carries a page, the prop otherwise.
|
|
68
|
+
*
|
|
69
|
+
* During the server render `useQueryParams()` is empty, so the old
|
|
70
|
+
* `getCurrentPageFromUrl() || currentPage` always resolved to 1 — the
|
|
71
|
+
* fallback to the prop could never run, because the function returned 1
|
|
72
|
+
* rather than a falsy value. Every server-rendered page built its number
|
|
73
|
+
* window around page 1: `/search?page=100` linked 2–6 and the last page,
|
|
74
|
+
* never 99 or 101. Pages 7..N-1 were linked from nowhere in the HTML a
|
|
75
|
+
* crawler reads, which is the only HTML a crawler reads. */
|
|
76
|
+
const getCurrentPageFromUrl = (): number | null => {
|
|
68
77
|
const pageParam = queryParams.get('page');
|
|
69
|
-
if (pageParam)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
return 1;
|
|
78
|
+
if (!pageParam) return null;
|
|
79
|
+
const pageNum = parseInt(pageParam, 10);
|
|
80
|
+
return isNaN(pageNum) || pageNum < 1 ? null : pageNum;
|
|
74
81
|
};
|
|
75
82
|
|
|
76
|
-
const actualCurrentPage = getCurrentPageFromUrl()
|
|
83
|
+
const actualCurrentPage = getCurrentPageFromUrl() ?? currentPage;
|
|
77
84
|
const actualHasPreviousPage = actualCurrentPage > 1;
|
|
78
85
|
|
|
79
86
|
const getPageUrl = (page: number): string => {
|
|
@@ -99,7 +106,16 @@ export const SSRPagination: React.FC<SSRPaginationProps> = ({
|
|
|
99
106
|
* cheap to build and answers a question no one asks.
|
|
100
107
|
*
|
|
101
108
|
* Two chevrons and the numbers. Anything more is furniture. */
|
|
102
|
-
|
|
109
|
+
/* An ellipsis is a link to the MIDDLE of the pages it hides.
|
|
110
|
+
*
|
|
111
|
+
* It used to be inert text, so the only pages a crawler could reach were the
|
|
112
|
+
* ones in the window, one step at a time: 267 pages took ~90 hops. Linking
|
|
113
|
+
* each gap's midpoint halves the unexplored range with every hop, so any
|
|
114
|
+
* page is ~log2(N) hops from page 1 (8 for 267). A reader gets a "jump
|
|
115
|
+
* roughly there" control, which is what an ellipsis promises anyway. */
|
|
116
|
+
type PageItem = number | { ellipsis: number };
|
|
117
|
+
|
|
118
|
+
const getVisiblePages = (): PageItem[] => {
|
|
103
119
|
const mobileMaxVisible = 3;
|
|
104
120
|
const effectiveMaxVisible = isMobile ? mobileMaxVisible : maxVisiblePages;
|
|
105
121
|
|
|
@@ -107,7 +123,7 @@ export const SSRPagination: React.FC<SSRPaginationProps> = ({
|
|
|
107
123
|
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
108
124
|
}
|
|
109
125
|
|
|
110
|
-
const pages:
|
|
126
|
+
const pages: PageItem[] = [];
|
|
111
127
|
const halfVisible = Math.floor(effectiveMaxVisible / 2);
|
|
112
128
|
|
|
113
129
|
if (isMobile) {
|
|
@@ -126,9 +142,11 @@ export const SSRPagination: React.FC<SSRPaginationProps> = ({
|
|
|
126
142
|
start = Math.max(2, totalPages - effectiveMaxVisible + 2);
|
|
127
143
|
}
|
|
128
144
|
|
|
129
|
-
if (start > 2) pages.push(
|
|
145
|
+
if (start > 2) pages.push({ ellipsis: Math.floor((2 + start - 1) / 2) });
|
|
130
146
|
for (let i = start; i <= end; i++) pages.push(i);
|
|
131
|
-
if (end < totalPages - 1)
|
|
147
|
+
if (end < totalPages - 1) {
|
|
148
|
+
pages.push({ ellipsis: Math.floor((end + 1 + totalPages - 1) / 2) });
|
|
149
|
+
}
|
|
132
150
|
if (totalPages > 1) pages.push(totalPages);
|
|
133
151
|
}
|
|
134
152
|
|
|
@@ -173,8 +191,14 @@ export const SSRPagination: React.FC<SSRPaginationProps> = ({
|
|
|
173
191
|
|
|
174
192
|
{visiblePages.map((page, index) => (
|
|
175
193
|
<PaginationItem key={index}>
|
|
176
|
-
{page === '
|
|
177
|
-
<
|
|
194
|
+
{typeof page === 'object' ? (
|
|
195
|
+
<PaginationLink
|
|
196
|
+
href={getPageUrl(page.ellipsis)}
|
|
197
|
+
aria-label={t('ui.pagination.page', { page: page.ellipsis })}
|
|
198
|
+
scroll
|
|
199
|
+
>
|
|
200
|
+
…
|
|
201
|
+
</PaginationLink>
|
|
178
202
|
) : (
|
|
179
203
|
<PaginationLink
|
|
180
204
|
href={getPageUrl(page)}
|