@rcarls/rc-pagination 0.0.0-next-20261102181447

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/CHANGELOG.md ADDED
@@ -0,0 +1,85 @@
1
+ # @rcarls/rc-pagination
2
+
3
+ ## 0.0.0-next-20261102181447
4
+
5
+ ### Minor Changes
6
+
7
+ - 34fa00a: Adds `rc-pagination`, a pagination enhancer for a native `<nav>` holding page links or
8
+ buttons tagged with `data-rc-page`. It keeps `aria-current="page"` on the current page, names
9
+ the landmark and numbered controls, disables steps past either end, and fires a cancelable
10
+ `rc-pagination-change` that can hand link navigation to a client-side router. The DOM-free
11
+ `paginationRange()` helper, also at `@rcarls/rc-pagination/range`, builds the page list on a
12
+ server. The aggregate, the React wrappers, and all four themes cover it.
13
+
14
+ ### Patch Changes
15
+
16
+ - 4db22ea: Adds `rc-data-table`, a sortable-header enhancer for a native `<table>` that follows the
17
+ WAI-ARIA sortable table example. Authored `<button>` or link controls in
18
+ `<th data-rc-sort-key>` headers request a sort; the element reflects it as `aria-sort` on the
19
+ active header, draws a distinct indicator for each sort state, and fires a cancelable
20
+ `rc-data-table-sort` for the consumer to re-render the rows. It never sorts data itself. The
21
+ host becomes a focusable group named by the caption while the table overflows. The aggregate,
22
+ the React wrappers, and all four themes cover it.
23
+
24
+ `rc-pagination` now places its live region on the document body instead of inside the
25
+ element, so it never joins the consumer's markup.
26
+
27
+ - b9fd59d: `LiveAnnouncer` mounts its region before each announcement in the open dialog or
28
+ popover around its host, else the document's current body, and creates it on
29
+ first use. An announcement after Turbo replaces `document.body` around a
30
+ permanent element, or after a DOM morph removes the region, now reaches
31
+ assistive technology instead of a detached node. `rc-pagination` and
32
+ `rc-data-table` with `announce` also announce from inside an open modal dialog,
33
+ where the body was inert. `announcerRootFor()` is exported for custom roots.
34
+
35
+ Breaking changes:
36
+
37
+ - `LiveAnnouncer` takes the host element and an optional `{ root }` resolver
38
+ instead of a mount node. Replace `new LiveAnnouncer($root)` with
39
+ `new LiveAnnouncer($host, { root: () => $root })`, or drop `root` to use the
40
+ dialog-aware default.
41
+
42
+ - 760742b: Preserve authored disabled states when pagination controls leave an edge. Numbered targets beyond
43
+ the page total now request the last page, and stale links do not navigate to an out-of-range URL.
44
+ - Updated dependencies [e3dfa59]
45
+ - Updated dependencies [50d5610]
46
+ - Updated dependencies [9ee769c]
47
+ - Updated dependencies [1179f24]
48
+ - Updated dependencies [595fe37]
49
+ - Updated dependencies [38cd00c]
50
+ - Updated dependencies [5c006de]
51
+ - Updated dependencies [a6553ff]
52
+ - Updated dependencies [2205478]
53
+ - Updated dependencies [4341311]
54
+ - Updated dependencies [899bb5e]
55
+ - Updated dependencies [25fcfe0]
56
+ - Updated dependencies [51f6403]
57
+ - Updated dependencies [9caca6b]
58
+ - Updated dependencies [2a499c0]
59
+ - Updated dependencies [b9fd59d]
60
+ - Updated dependencies [b764a3c]
61
+ - Updated dependencies [6c3a2d1]
62
+ - Updated dependencies [54d9ead]
63
+ - Updated dependencies [3c0b1f6]
64
+ - Updated dependencies [9723b63]
65
+ - Updated dependencies [b9175e9]
66
+ - Updated dependencies [c8b762b]
67
+ - Updated dependencies [55e1e7f]
68
+ - Updated dependencies [f1ca82d]
69
+ - Updated dependencies [b87f27b]
70
+ - Updated dependencies [32ebead]
71
+ - Updated dependencies [bfefedd]
72
+ - Updated dependencies [c130173]
73
+ - Updated dependencies [6fef402]
74
+ - Updated dependencies [11680b6]
75
+ - Updated dependencies [223d36c]
76
+ - Updated dependencies [7b91a44]
77
+ - Updated dependencies [aa04ead]
78
+ - Updated dependencies [ac65b5b]
79
+ - Updated dependencies [429a0c1]
80
+ - Updated dependencies [ccdf1cf]
81
+ - Updated dependencies [afcfef8]
82
+ - Updated dependencies [3f21c58]
83
+ - Updated dependencies [e00df90]
84
+ - Updated dependencies [11ef1ad]
85
+ - @rcarls/rc-common@0.0.0-next-20261102181447
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # `@rcarls/rc-pagination`
2
+
3
+ Pagination enhancer for a native `<nav>` holding page links or buttons, with a server-safe
4
+ page range helper.
5
+
6
+ Docs: [https://richardcarls.github.io/rc-webcomponents/components/rc-pagination](https://richardcarls.github.io/rc-webcomponents/components/rc-pagination).
7
+
8
+ ## Installation
9
+
10
+ ```sh
11
+ yarn add @rcarls/rc-pagination lit
12
+ ```
13
+
14
+ ```js
15
+ import '@rcarls/rc-pagination/define';
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```html
21
+ <rc-pagination total="3" page="2">
22
+ <nav>
23
+ <ul>
24
+ <li><a href="?page=1" data-rc-page="previous" rel="prev">Previous</a></li>
25
+ <li><a href="?page=1" data-rc-page="1">1</a></li>
26
+ <li><a href="?page=2" data-rc-page="2" aria-current="page">2</a></li>
27
+ <li><a href="?page=3" data-rc-page="3">3</a></li>
28
+ <li><a href="?page=3" data-rc-page="next" rel="next">Next</a></li>
29
+ </ul>
30
+ </nav>
31
+ </rc-pagination>
32
+ ```
33
+
34
+ The element keeps `aria-current="page"` current, names the landmark and numbered controls,
35
+ disables steps past either end, and fires a cancelable `rc-pagination-change` on activation.
36
+
37
+ Build the list with `paginationRange()` from `@rcarls/rc-pagination/range`, which has no DOM
38
+ dependency and runs on a server:
39
+
40
+ ```js
41
+ import { paginationRange } from '@rcarls/rc-pagination/range';
42
+
43
+ paginationRange({ page: 6, total: 12 }); // [1, 'ellipsis', 5, 6, 7, 'ellipsis', 12]
44
+ ```
package/dist/base.css ADDED
@@ -0,0 +1,2 @@
1
+ /* rc-pagination light-DOM base styles. Link with data-rc-light-dom-base="rc-pagination" to skip the runtime copy. */
2
+ @layer rc-base{rc-pagination{display:block}rc-pagination>nav>:is(ul,ol){align-items:center;gap:var(--rc-pagination-gap,.25em);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}rc-pagination [data-rc-page]{min-inline-size:var(--rc-pagination-item-min-inline-size,auto);justify-content:center;align-items:center;display:inline-flex}rc-pagination a[data-rc-page][aria-disabled=true]{cursor:default}}
@@ -0,0 +1,486 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "readme": "",
4
+ "modules": [
5
+ {
6
+ "kind": "javascript-module",
7
+ "path": "src/define.ts",
8
+ "declarations": [],
9
+ "exports": [
10
+ {
11
+ "kind": "custom-element-definition",
12
+ "name": "rc-pagination",
13
+ "declaration": {
14
+ "name": "RCPagination",
15
+ "module": "/src/index.js"
16
+ }
17
+ },
18
+ {
19
+ "kind": "js",
20
+ "name": "*",
21
+ "declaration": {
22
+ "name": "*",
23
+ "module": "src/index.js"
24
+ }
25
+ }
26
+ ]
27
+ },
28
+ {
29
+ "kind": "javascript-module",
30
+ "path": "src/index.ts",
31
+ "declarations": [],
32
+ "exports": [
33
+ {
34
+ "kind": "js",
35
+ "name": "*",
36
+ "declaration": {
37
+ "name": "*",
38
+ "module": "src/rc-pagination.js"
39
+ }
40
+ },
41
+ {
42
+ "kind": "js",
43
+ "name": "*",
44
+ "declaration": {
45
+ "name": "*",
46
+ "module": "src/paginationBaseStyles.js"
47
+ }
48
+ },
49
+ {
50
+ "kind": "js",
51
+ "name": "*",
52
+ "declaration": {
53
+ "name": "*",
54
+ "module": "src/range.js"
55
+ }
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "kind": "javascript-module",
61
+ "path": "src/paginationBaseStyles.ts",
62
+ "declarations": [
63
+ {
64
+ "kind": "variable",
65
+ "name": "PAGINATION_BASE_CSS",
66
+ "default": "cssText` @layer rc-base { rc-pagination { display: block; } rc-pagination > nav > :is(ul, ol) { display: flex; flex-wrap: wrap; align-items: center; gap: var(--rc-pagination-gap, 0.25em); margin: 0; padding: 0; list-style: none; } rc-pagination [data-rc-page] { display: inline-flex; align-items: center; justify-content: center; min-inline-size: var(--rc-pagination-item-min-inline-size, auto); } rc-pagination a[data-rc-page][aria-disabled='true'] { cursor: default; } } `",
67
+ "description": "Light-DOM base styles for the pagination list.\n\nThe consumer's `<nav>`, list, and controls stay in light DOM. These rules lay\nthe list out as a wrapping inline row and give each control a minimum size;\nthemes supply the shape, color, and state layers."
68
+ },
69
+ {
70
+ "kind": "variable",
71
+ "name": "ensurePaginationBaseStyles",
72
+ "type": {
73
+ "text": "(root: Document | ShadowRoot) => void"
74
+ },
75
+ "description": "Installs the pagination list styles once per containing root."
76
+ }
77
+ ],
78
+ "exports": [
79
+ {
80
+ "kind": "js",
81
+ "name": "PAGINATION_BASE_CSS",
82
+ "declaration": {
83
+ "name": "PAGINATION_BASE_CSS",
84
+ "module": "src/paginationBaseStyles.ts"
85
+ }
86
+ },
87
+ {
88
+ "kind": "js",
89
+ "name": "ensurePaginationBaseStyles",
90
+ "declaration": {
91
+ "name": "ensurePaginationBaseStyles",
92
+ "module": "src/paginationBaseStyles.ts"
93
+ }
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ "kind": "javascript-module",
99
+ "path": "src/range.ts",
100
+ "declarations": [
101
+ {
102
+ "kind": "function",
103
+ "name": "paginationRange",
104
+ "return": {
105
+ "type": {
106
+ "text": "PaginationItem[]"
107
+ }
108
+ },
109
+ "parameters": [
110
+ {
111
+ "name": "{\n page,\n total,\n siblings = 1,\n boundaries = 1,\n}",
112
+ "type": {
113
+ "text": "PaginationRangeOptions"
114
+ }
115
+ }
116
+ ],
117
+ "description": "The page numbers to render for a pagination list, with `'ellipsis'` where a\nrun of pages is hidden. A gap of a single page shows that page instead, so\nan ellipsis always stands for at least two pages.\n\nPure and DOM-free: import it from `@rcarls/rc-pagination/range` to build the\nmarkup on a server without loading the element."
118
+ }
119
+ ],
120
+ "exports": [
121
+ {
122
+ "kind": "js",
123
+ "name": "paginationRange",
124
+ "declaration": {
125
+ "name": "paginationRange",
126
+ "module": "src/range.ts"
127
+ }
128
+ }
129
+ ]
130
+ },
131
+ {
132
+ "kind": "javascript-module",
133
+ "path": "src/rc-pagination.ts",
134
+ "declarations": [
135
+ {
136
+ "kind": "variable",
137
+ "name": "DEFAULT_PAGINATION_MESSAGES",
138
+ "type": {
139
+ "text": "Readonly<RCPaginationMessages>"
140
+ }
141
+ },
142
+ {
143
+ "kind": "class",
144
+ "description": "Pagination enhancer for a native `<nav>` holding a list of page links or\nbuttons.\n\nThere is no WAI-ARIA pattern for pagination; the markup follows the GOV.UK\nand USWDS models. The consumer renders the list, usually from\n`paginationRange()`, and tags each control with `data-rc-page`: a page\nnumber, or `previous`, `next`, `first`, or `last`. The markup works before the\nelement upgrades. The element then keeps `aria-current=\"page\"` on the\ncurrent page, names the landmark and unlabeled controls, disables steps past\neither end, and reports each user request as `rc-pagination-change`.\n\nLinks navigate as usual unless a listener cancels `rc-pagination-change`,\nwhich also cancels the click, for client-side routing. Buttons only report.\nA numbered target above `total` reports the last page; its stale link does\nnot navigate to an out-of-range URL.\nIn uncontrolled use the element moves to the requested page itself; set\n`page` to control it.\n\nBuilt-in text follows the element's own `lang`, else `<html lang>`: per\nelement through `messages`, or for every instance through\n`registerMessages()` from `@rcarls/rc-common` under `rc-pagination`.",
145
+ "name": "RCPagination",
146
+ "cssProperties": [
147
+ {
148
+ "description": "Space between items.",
149
+ "name": "--rc-pagination-gap",
150
+ "default": "0.25em"
151
+ },
152
+ {
153
+ "description": "Minimum inline size of each item. Defers to the content size when unset.",
154
+ "name": "--rc-pagination-item-min-inline-size"
155
+ }
156
+ ],
157
+ "members": [
158
+ {
159
+ "kind": "field",
160
+ "name": "total",
161
+ "type": {
162
+ "text": "number"
163
+ },
164
+ "default": "1",
165
+ "description": "Number of pages."
166
+ },
167
+ {
168
+ "kind": "field",
169
+ "name": "announce",
170
+ "type": {
171
+ "text": "boolean"
172
+ },
173
+ "default": "false",
174
+ "description": "Announces each user change through a polite live region."
175
+ },
176
+ {
177
+ "kind": "field",
178
+ "name": "messages",
179
+ "type": {
180
+ "text": "Partial<RCPaginationMessages> | undefined"
181
+ },
182
+ "description": "Built-in text overrides; unset keys fall back to registered text, then English."
183
+ },
184
+ {
185
+ "kind": "field",
186
+ "name": "_page",
187
+ "type": {
188
+ "text": "number | undefined"
189
+ },
190
+ "privacy": "private"
191
+ },
192
+ {
193
+ "kind": "field",
194
+ "name": "_defaultPage",
195
+ "type": {
196
+ "text": "number"
197
+ },
198
+ "privacy": "private",
199
+ "default": "1"
200
+ },
201
+ {
202
+ "kind": "field",
203
+ "name": "_uncontrolledPage",
204
+ "type": {
205
+ "text": "number | undefined"
206
+ },
207
+ "privacy": "private"
208
+ },
209
+ {
210
+ "kind": "field",
211
+ "name": "_pageInitialized",
212
+ "type": {
213
+ "text": "boolean"
214
+ },
215
+ "privacy": "private",
216
+ "default": "false"
217
+ },
218
+ {
219
+ "kind": "field",
220
+ "name": "_messages",
221
+ "privacy": "private",
222
+ "readonly": true,
223
+ "default": "new MessagesController( this, 'rc-pagination', DEFAULT_PAGINATION_MESSAGES, { overrides: () => this.messages }, )"
224
+ },
225
+ {
226
+ "kind": "field",
227
+ "name": "_observer",
228
+ "type": {
229
+ "text": "MutationObserver | undefined"
230
+ },
231
+ "privacy": "private"
232
+ },
233
+ {
234
+ "kind": "field",
235
+ "name": "_announcer",
236
+ "type": {
237
+ "text": "LiveAnnouncer | undefined"
238
+ },
239
+ "privacy": "private"
240
+ },
241
+ {
242
+ "kind": "field",
243
+ "name": "_writtenLabel",
244
+ "type": {
245
+ "text": "string | null"
246
+ },
247
+ "privacy": "private",
248
+ "default": "null"
249
+ },
250
+ {
251
+ "kind": "field",
252
+ "name": "_writtenNames",
253
+ "privacy": "private",
254
+ "readonly": true,
255
+ "default": "new WeakMap<Element, string | null>()"
256
+ },
257
+ {
258
+ "kind": "field",
259
+ "name": "_$managedCurrent",
260
+ "privacy": "private",
261
+ "readonly": true,
262
+ "default": "new Set<Element>()"
263
+ },
264
+ {
265
+ "kind": "field",
266
+ "name": "_$managedDisabled",
267
+ "privacy": "private",
268
+ "readonly": true,
269
+ "default": "new Set<Element>()"
270
+ },
271
+ {
272
+ "kind": "field",
273
+ "name": "_disabledBeforeEdge",
274
+ "privacy": "private",
275
+ "readonly": true,
276
+ "default": "new Map<Element, string | null>()"
277
+ },
278
+ {
279
+ "kind": "field",
280
+ "name": "page",
281
+ "type": {
282
+ "text": "number"
283
+ },
284
+ "description": "Current page, 1-based and clamped to `[1, total]`. Host writes are silent."
285
+ },
286
+ {
287
+ "kind": "field",
288
+ "name": "defaultPage",
289
+ "type": {
290
+ "text": "number"
291
+ },
292
+ "description": "Initial page for uncontrolled use."
293
+ },
294
+ {
295
+ "kind": "method",
296
+ "name": "createRenderRoot",
297
+ "privacy": "protected",
298
+ "return": {
299
+ "type": {
300
+ "text": "HTMLElement"
301
+ }
302
+ },
303
+ "description": "Renders nothing: the consumer's `<nav>` stays the element's only content."
304
+ },
305
+ {
306
+ "kind": "method",
307
+ "name": "firstUpdated",
308
+ "privacy": "protected",
309
+ "return": {
310
+ "type": {
311
+ "text": "void"
312
+ }
313
+ }
314
+ },
315
+ {
316
+ "kind": "method",
317
+ "name": "updated",
318
+ "privacy": "protected",
319
+ "return": {
320
+ "type": {
321
+ "text": "void"
322
+ }
323
+ }
324
+ },
325
+ {
326
+ "kind": "method",
327
+ "name": "_clamp",
328
+ "privacy": "private",
329
+ "return": {
330
+ "type": {
331
+ "text": "number"
332
+ }
333
+ },
334
+ "parameters": [
335
+ {
336
+ "name": "page",
337
+ "type": {
338
+ "text": "number"
339
+ }
340
+ }
341
+ ]
342
+ },
343
+ {
344
+ "kind": "method",
345
+ "name": "_target",
346
+ "privacy": "private",
347
+ "return": {
348
+ "type": {
349
+ "text": "PageTarget | undefined"
350
+ }
351
+ },
352
+ "parameters": [
353
+ {
354
+ "name": "$item",
355
+ "type": {
356
+ "text": "Element"
357
+ }
358
+ }
359
+ ]
360
+ },
361
+ {
362
+ "kind": "method",
363
+ "name": "_resolve",
364
+ "privacy": "private",
365
+ "return": {
366
+ "type": {
367
+ "text": "number"
368
+ }
369
+ },
370
+ "parameters": [
371
+ {
372
+ "name": "target",
373
+ "type": {
374
+ "text": "PageTarget"
375
+ }
376
+ }
377
+ ]
378
+ },
379
+ {
380
+ "kind": "method",
381
+ "name": "_sync",
382
+ "privacy": "private",
383
+ "return": {
384
+ "type": {
385
+ "text": "void"
386
+ }
387
+ }
388
+ },
389
+ {
390
+ "kind": "method",
391
+ "name": "_syncName",
392
+ "privacy": "private",
393
+ "return": {
394
+ "type": {
395
+ "text": "void"
396
+ }
397
+ },
398
+ "parameters": [
399
+ {
400
+ "name": "$item",
401
+ "type": {
402
+ "text": "Element"
403
+ }
404
+ },
405
+ {
406
+ "name": "name",
407
+ "type": {
408
+ "text": "string"
409
+ }
410
+ },
411
+ {
412
+ "name": "numbered",
413
+ "type": {
414
+ "text": "boolean"
415
+ }
416
+ }
417
+ ],
418
+ "description": "Gives a control its default name unless the author named it. Numbered\ncontrols always get one (\"Page 3\" contains the visible \"3\"); steps only\nwhen they have no visible text, such as an icon-only button."
419
+ },
420
+ {
421
+ "kind": "field",
422
+ "name": "_onClick",
423
+ "privacy": "private"
424
+ }
425
+ ],
426
+ "events": [
427
+ {
428
+ "description": "Fired when the user activates a page control. Cancelable; canceling it also cancels the click, so a link does not navigate.",
429
+ "name": "rc-pagination-change"
430
+ }
431
+ ],
432
+ "attributes": [
433
+ {
434
+ "description": "Controlled current page, 1-based. Host writes are silent.",
435
+ "name": "page"
436
+ },
437
+ {
438
+ "description": "Initial page for uncontrolled use. Defaults to 1.",
439
+ "name": "default-page"
440
+ },
441
+ {
442
+ "description": "Number of pages.",
443
+ "name": "total"
444
+ },
445
+ {
446
+ "description": "Announces the new page through a polite live region after each user change.",
447
+ "name": "announce"
448
+ }
449
+ ],
450
+ "superclass": {
451
+ "name": "ReactiveElement",
452
+ "package": "lit"
453
+ },
454
+ "tagName": "rc-pagination",
455
+ "customElement": true
456
+ }
457
+ ],
458
+ "exports": [
459
+ {
460
+ "kind": "js",
461
+ "name": "DEFAULT_PAGINATION_MESSAGES",
462
+ "declaration": {
463
+ "name": "DEFAULT_PAGINATION_MESSAGES",
464
+ "module": "src/rc-pagination.ts"
465
+ }
466
+ },
467
+ {
468
+ "kind": "js",
469
+ "name": "RCPagination",
470
+ "declaration": {
471
+ "name": "RCPagination",
472
+ "module": "src/rc-pagination.ts"
473
+ }
474
+ },
475
+ {
476
+ "kind": "js",
477
+ "name": "default",
478
+ "declaration": {
479
+ "name": "RCPagination",
480
+ "module": "src/rc-pagination.ts"
481
+ }
482
+ }
483
+ ]
484
+ }
485
+ ]
486
+ }
package/dist/range.js ADDED
@@ -0,0 +1,25 @@
1
+ function p({
2
+ page: u,
3
+ total: l,
4
+ siblings: i = 1,
5
+ boundaries: r = 1
6
+ }) {
7
+ const n = Math.max(0, Math.floor(l));
8
+ if (n === 0)
9
+ return [];
10
+ const h = Math.min(Math.max(1, Math.floor(u)), n), c = /* @__PURE__ */ new Set(), s = (a, o) => {
11
+ for (let t = Math.max(1, a); t <= Math.min(n, o); t += 1)
12
+ c.add(t);
13
+ };
14
+ s(1, r), s(n - r + 1, n), s(h - i, h + i);
15
+ const f = [...c].sort((a, o) => a - o), e = [];
16
+ for (const [a, o] of f.entries()) {
17
+ const t = f[a - 1];
18
+ t !== void 0 && o - t === 2 ? e.push(t + 1) : t !== void 0 && o - t > 2 && e.push("ellipsis"), e.push(o);
19
+ }
20
+ return e;
21
+ }
22
+ export {
23
+ p as paginationRange
24
+ };
25
+ //# sourceMappingURL=range.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"range.js","sources":["../src/range.ts"],"sourcesContent":["/** One slot in a pagination list: a page number or a gap of hidden pages. */\nexport type PaginationItem = number | 'ellipsis';\n\nexport interface PaginationRangeOptions {\n /** The current page, 1-based. Clamped to `[1, total]`. */\n page: number;\n /** The number of pages. */\n total: number;\n /** Pages shown on each side of the current page. Defaults to 1. */\n siblings?: number;\n /** Pages always shown at each end. Defaults to 1. */\n boundaries?: number;\n}\n\n/**\n * The page numbers to render for a pagination list, with `'ellipsis'` where a\n * run of pages is hidden. A gap of a single page shows that page instead, so\n * an ellipsis always stands for at least two pages.\n *\n * Pure and DOM-free: import it from `@rcarls/rc-pagination/range` to build the\n * markup on a server without loading the element.\n *\n * @example\n * paginationRange({ page: 6, total: 12 }); // [1, 'ellipsis', 5, 6, 7, 'ellipsis', 12]\n */\nexport function paginationRange({\n page,\n total,\n siblings = 1,\n boundaries = 1,\n}: PaginationRangeOptions): PaginationItem[] {\n const last = Math.max(0, Math.floor(total));\n\n if (last === 0) {\n return [];\n }\n\n const current = Math.min(Math.max(1, Math.floor(page)), last);\n const shown = new Set<number>();\n\n const add = (from: number, to: number) => {\n for (let value = Math.max(1, from); value <= Math.min(last, to); value += 1) {\n shown.add(value);\n }\n };\n\n add(1, boundaries);\n add(last - boundaries + 1, last);\n add(current - siblings, current + siblings);\n\n const pages = [...shown].sort((a, b) => a - b);\n const items: PaginationItem[] = [];\n\n for (const [index, value] of pages.entries()) {\n const previous = pages[index - 1];\n\n if (previous !== undefined && value - previous === 2) {\n items.push(previous + 1);\n } else if (previous !== undefined && value - previous > 2) {\n items.push('ellipsis');\n }\n\n items.push(value);\n }\n\n return items;\n}\n"],"names":["paginationRange","page","total","siblings","boundaries","last","current","shown","add","from","to","value","pages","b","items","index","previous"],"mappings":"AAyBO,SAASA,EAAgB;AAAA,EAC9B,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,YAAAC,IAAa;AACf,GAA6C;AAC3C,QAAMC,IAAO,KAAK,IAAI,GAAG,KAAK,MAAMH,CAAK,CAAC;AAE1C,MAAIG,MAAS;AACX,WAAO,CAAA;AAGT,QAAMC,IAAU,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,MAAML,CAAI,CAAC,GAAGI,CAAI,GACtDE,wBAAY,IAAA,GAEZC,IAAM,CAACC,GAAcC,MAAe;AACxC,aAASC,IAAQ,KAAK,IAAI,GAAGF,CAAI,GAAGE,KAAS,KAAK,IAAIN,GAAMK,CAAE,GAAGC,KAAS;AACxE,MAAAJ,EAAM,IAAII,CAAK;AAAA,EAEnB;AAEA,EAAAH,EAAI,GAAGJ,CAAU,GACjBI,EAAIH,IAAOD,IAAa,GAAGC,CAAI,GAC/BG,EAAIF,IAAUH,GAAUG,IAAUH,CAAQ;AAE1C,QAAMS,IAAQ,CAAC,GAAGL,CAAK,EAAE,KAAK,CAAC,GAAGM,MAAM,IAAIA,CAAC,GACvCC,IAA0B,CAAA;AAEhC,aAAW,CAACC,GAAOJ,CAAK,KAAKC,EAAM,WAAW;AAC5C,UAAMI,IAAWJ,EAAMG,IAAQ,CAAC;AAEhC,IAAIC,MAAa,UAAaL,IAAQK,MAAa,IACjDF,EAAM,KAAKE,IAAW,CAAC,IACdA,MAAa,UAAaL,IAAQK,IAAW,KACtDF,EAAM,KAAK,UAAU,GAGvBA,EAAM,KAAKH,CAAK;AAAA,EAClB;AAEA,SAAOG;AACT;"}
@@ -0,0 +1,12 @@
1
+ import { R as a } from "./rc-pagination-mkpZ0mCK.js";
2
+ import { D as o, P as i, e as s } from "./rc-pagination-mkpZ0mCK.js";
3
+ import { paginationRange as m } from "./range.js";
4
+ customElements.get("rc-pagination") || customElements.define("rc-pagination", a);
5
+ export {
6
+ o as DEFAULT_PAGINATION_MESSAGES,
7
+ i as PAGINATION_BASE_CSS,
8
+ a as RCPagination,
9
+ s as ensurePaginationBaseStyles,
10
+ m as paginationRange
11
+ };
12
+ //# sourceMappingURL=rc-pagination-define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-pagination-define.js","sources":["../src/define.ts"],"sourcesContent":["import { RCPagination } from './index.js';\n\nif (!customElements.get('rc-pagination')) {\n customElements.define('rc-pagination', RCPagination);\n}\n\nexport * from './index.js';\n"],"names":["RCPagination"],"mappings":";;;AAEK,eAAe,IAAI,eAAe,KACrC,eAAe,OAAO,iBAAiBA,CAAY;"}
@@ -0,0 +1,196 @@
1
+ import { ReactiveElement as v } from "lit";
2
+ import { property as d } from "lit/decorators.js";
3
+ import { defineLightDomBaseStyles as y, cssText as P, MessagesController as E, LiveAnnouncer as w, syncDefaultAttribute as f, numberFormat as A } from "@rcarls/rc-common";
4
+ const $ = P`@layer rc-base{rc-pagination{display:block}rc-pagination>nav>:is(ul,ol){align-items:center;gap:var(--rc-pagination-gap,.25em);flex-wrap:wrap;margin:0;padding:0;list-style:none;display:flex}rc-pagination [data-rc-page]{min-inline-size:var(--rc-pagination-item-min-inline-size,auto);justify-content:center;align-items:center;display:inline-flex}rc-pagination a[data-rc-page][aria-disabled=true]{cursor:default}}`, C = y("rc-pagination", $);
5
+ var N = Object.defineProperty, S = Object.getOwnPropertyDescriptor, h = (o, e, t, r) => {
6
+ for (var i = r > 1 ? void 0 : r ? S(e, t) : e, l = o.length - 1, n; l >= 0; l--)
7
+ (n = o[l]) && (i = (r ? n(e, t, i) : n(i)) || i);
8
+ return r && i && N(e, t, i), i;
9
+ };
10
+ const _ = ":scope > nav", b = "[data-rc-page]", p = (o, e) => A(e, { useGrouping: !1 }).format(o), D = Object.freeze({
11
+ label: "Pagination",
12
+ page: (o, e, t) => `Page ${p(o, t)}`,
13
+ previous: "Previous page",
14
+ next: "Next page",
15
+ first: "First page",
16
+ last: "Last page",
17
+ status: (o, e, t) => `Page ${p(o, t)} of ${p(e, t)}`
18
+ });
19
+ class g extends v {
20
+ constructor() {
21
+ super(...arguments), this.total = 1, this.announce = !1, this._defaultPage = 1, this._pageInitialized = !1, this._messages = new E(
22
+ this,
23
+ "rc-pagination",
24
+ D,
25
+ { overrides: () => this.messages }
26
+ ), this._writtenLabel = null, this._writtenNames = /* @__PURE__ */ new WeakMap(), this._$managedCurrent = /* @__PURE__ */ new Set(), this._$managedDisabled = /* @__PURE__ */ new Set(), this._disabledBeforeEdge = /* @__PURE__ */ new Map(), this._onClick = (e) => {
27
+ const t = e.target?.closest?.(b), r = this.querySelector(_);
28
+ if (!t || !r?.contains(t))
29
+ return;
30
+ const i = this._target(t);
31
+ if (i === void 0)
32
+ return;
33
+ if (this._$managedDisabled.has(t)) {
34
+ e.preventDefault();
35
+ return;
36
+ }
37
+ const l = this.page, n = this._resolve(i);
38
+ if (typeof i == "number" && i !== n && e.preventDefault(), n === l)
39
+ return;
40
+ const c = new CustomEvent("rc-pagination-change", {
41
+ bubbles: !0,
42
+ composed: !0,
43
+ cancelable: !0,
44
+ detail: { page: n, previousPage: l }
45
+ });
46
+ if (!this.dispatchEvent(c)) {
47
+ e.preventDefault();
48
+ return;
49
+ }
50
+ this._page === void 0 && (this._uncontrolledPage = n, this.requestUpdate("page", l)), this.announce && (this._announcer ??= new w(this), this._announcer.announce(
51
+ this._messages.value.status(n, this._clamp(this.total), this._messages.locale)
52
+ ));
53
+ };
54
+ }
55
+ get page() {
56
+ return this._clamp(this._page ?? this._uncontrolledPage ?? this._defaultPage);
57
+ }
58
+ set page(e) {
59
+ const t = this.page;
60
+ this._page = e, this._pageInitialized = !0, this.requestUpdate("page", t);
61
+ }
62
+ get defaultPage() {
63
+ return this._defaultPage;
64
+ }
65
+ set defaultPage(e) {
66
+ const t = this._defaultPage;
67
+ this._defaultPage = e, !this._pageInitialized && this._page === void 0 && this._uncontrolledPage === void 0 && this.requestUpdate("page", this.page), this.requestUpdate("defaultPage", t);
68
+ }
69
+ /** Renders nothing: the consumer's `<nav>` stays the element's only content. */
70
+ createRenderRoot() {
71
+ return this;
72
+ }
73
+ connectedCallback() {
74
+ super.connectedCallback(), C(this.getRootNode()), this._observer ??= new MutationObserver(() => this._sync()), this._observer.observe(this, {
75
+ childList: !0,
76
+ subtree: !0,
77
+ attributes: !0,
78
+ attributeFilter: ["data-rc-page", "aria-label", "aria-labelledby"]
79
+ }), this.addEventListener("click", this._onClick);
80
+ }
81
+ disconnectedCallback() {
82
+ super.disconnectedCallback(), this._observer?.disconnect(), this.removeEventListener("click", this._onClick), this._announcer?.dispose(), this._announcer = void 0;
83
+ }
84
+ firstUpdated() {
85
+ }
86
+ updated() {
87
+ this._sync();
88
+ }
89
+ _clamp(e) {
90
+ const t = Math.max(1, Math.floor(this.total) || 1), r = Number.isFinite(e) ? Math.floor(e) : 1;
91
+ return Math.min(Math.max(1, r), t);
92
+ }
93
+ _target(e) {
94
+ const t = e.getAttribute("data-rc-page")?.trim() ?? "";
95
+ if (t === "previous" || t === "next" || t === "first" || t === "last")
96
+ return t;
97
+ const r = Number(t);
98
+ return Number.isInteger(r) && r > 0 ? r : void 0;
99
+ }
100
+ _resolve(e) {
101
+ switch (e) {
102
+ case "previous":
103
+ return this._clamp(this.page - 1);
104
+ case "next":
105
+ return this._clamp(this.page + 1);
106
+ case "first":
107
+ return 1;
108
+ case "last":
109
+ return this._clamp(this.total);
110
+ default:
111
+ return this._clamp(e);
112
+ }
113
+ }
114
+ _sync() {
115
+ const e = this.querySelector(_);
116
+ if (!e)
117
+ return;
118
+ const t = this._messages.value, r = this._messages.locale;
119
+ e.hasAttribute("aria-labelledby") ? this._writtenLabel = null : this._writtenLabel = f(
120
+ e,
121
+ "aria-label",
122
+ t.label,
123
+ this._writtenLabel
124
+ );
125
+ const i = this.page, l = this._clamp(this.total), n = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set();
126
+ for (const a of e.querySelectorAll(b)) {
127
+ const s = this._target(a);
128
+ s !== void 0 && (typeof s == "number" ? (s === i && n.add(a), this._syncName(a, t.page(s, l, r), !0)) : (((s === "previous" || s === "first") && i <= 1 || (s === "next" || s === "last") && i >= l) && c.add(a), this._syncName(a, t[s], !1)));
129
+ }
130
+ for (const a of this._$managedCurrent)
131
+ n.has(a) || a.removeAttribute("aria-current");
132
+ for (const a of n)
133
+ a.setAttribute("aria-current", "page");
134
+ this._$managedCurrent.clear(), n.forEach((a) => this._$managedCurrent.add(a));
135
+ for (const a of this._$managedDisabled)
136
+ if (!c.has(a)) {
137
+ const s = m(a);
138
+ if (this._disabledBeforeEdge.has(a) && s) {
139
+ const u = this._disabledBeforeEdge.get(a) ?? null;
140
+ a.getAttribute(s) === (s === "disabled" ? "" : "true") && (u === null ? a.removeAttribute(s) : a.setAttribute(s, u)), this._disabledBeforeEdge.delete(a);
141
+ }
142
+ }
143
+ for (const a of c) {
144
+ const s = m(a);
145
+ if (s && (!this._$managedDisabled.has(a) || this._disabledBeforeEdge.has(a) && !a.hasAttribute(s))) {
146
+ const u = a.getAttribute(s);
147
+ u !== (s === "disabled" ? "" : "true") && (this._$managedDisabled.has(a) || this._disabledBeforeEdge.set(a, u), a.setAttribute(s, s === "disabled" ? "" : "true"));
148
+ }
149
+ }
150
+ this._$managedDisabled.clear(), c.forEach((a) => this._$managedDisabled.add(a));
151
+ }
152
+ /**
153
+ * Gives a control its default name unless the author named it. Numbered
154
+ * controls always get one ("Page 3" contains the visible "3"); steps only
155
+ * when they have no visible text, such as an icon-only button.
156
+ */
157
+ _syncName(e, t, r) {
158
+ if (!r && e.textContent?.trim())
159
+ return;
160
+ const i = f(
161
+ e,
162
+ "aria-label",
163
+ t,
164
+ this._writtenNames.get(e) ?? null
165
+ );
166
+ this._writtenNames.set(e, i);
167
+ }
168
+ }
169
+ h([
170
+ d({ type: Number })
171
+ ], g.prototype, "total", 2);
172
+ h([
173
+ d({ type: Boolean })
174
+ ], g.prototype, "announce", 2);
175
+ h([
176
+ d({ attribute: !1 })
177
+ ], g.prototype, "messages", 2);
178
+ h([
179
+ d({ type: Number })
180
+ ], g.prototype, "page", 1);
181
+ h([
182
+ d({ type: Number, attribute: "default-page" })
183
+ ], g.prototype, "defaultPage", 1);
184
+ function m(o) {
185
+ if (o instanceof HTMLButtonElement)
186
+ return "disabled";
187
+ if (o instanceof HTMLAnchorElement)
188
+ return "aria-disabled";
189
+ }
190
+ export {
191
+ D,
192
+ $ as P,
193
+ g as R,
194
+ C as e
195
+ };
196
+ //# sourceMappingURL=rc-pagination-mkpZ0mCK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-pagination-mkpZ0mCK.js","sources":["../src/paginationBaseStyles.ts","../src/rc-pagination.ts"],"sourcesContent":["import { cssText, defineLightDomBaseStyles } from '@rcarls/rc-common';\n\n/**\n * Light-DOM base styles for the pagination list.\n *\n * The consumer's `<nav>`, list, and controls stay in light DOM. These rules lay\n * the list out as a wrapping inline row and give each control a minimum size;\n * themes supply the shape, color, and state layers.\n */\nexport const PAGINATION_BASE_CSS = cssText`\n@layer rc-base {\n rc-pagination {\n display: block;\n }\n\n rc-pagination > nav > :is(ul, ol) {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--rc-pagination-gap, 0.25em);\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n rc-pagination [data-rc-page] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-inline-size: var(--rc-pagination-item-min-inline-size, auto);\n }\n\n rc-pagination a[data-rc-page][aria-disabled='true'] {\n cursor: default;\n }\n}\n`;\n\n/** Installs the pagination list styles once per containing root. */\nexport const ensurePaginationBaseStyles: (root: Document | ShadowRoot) => void =\n defineLightDomBaseStyles('rc-pagination', PAGINATION_BASE_CSS);\n","import { ReactiveElement } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport {\n LiveAnnouncer,\n MessagesController,\n numberFormat,\n syncDefaultAttribute,\n} from '@rcarls/rc-common';\nimport { ensurePaginationBaseStyles } from './paginationBaseStyles.js';\n\nconst NAV_SELECTOR = ':scope > nav';\nconst ITEM_SELECTOR = '[data-rc-page]';\n\n/** A `data-rc-page` value: a page number, or a step relative to the current page. */\ntype PageTarget = number | 'previous' | 'next' | 'first' | 'last';\n\n/** Built-in text of `rc-pagination`. */\nexport interface RCPaginationMessages {\n /** Accessible name for the `<nav>` landmark when the author gives it none. */\n label: string;\n /** Accessible name of a numbered page link or button. */\n page: (page: number, total: number, locale?: string) => string;\n /** Name of a previous control that has no text of its own. */\n previous: string;\n /** Name of a next control that has no text of its own. */\n next: string;\n /** Name of a first-page control that has no text of its own. */\n first: string;\n /** Name of a last-page control that has no text of its own. */\n last: string;\n /** Announcement after a page change, when `announce` is set. */\n status: (page: number, total: number, locale?: string) => string;\n}\n\nconst formatPage = (value: number, locale?: string) =>\n numberFormat(locale, { useGrouping: false }).format(value);\n\nexport const DEFAULT_PAGINATION_MESSAGES: Readonly<RCPaginationMessages> = Object.freeze({\n label: 'Pagination',\n page: (page: number, _total: number, locale?: string) => `Page ${formatPage(page, locale)}`,\n previous: 'Previous page',\n next: 'Next page',\n first: 'First page',\n last: 'Last page',\n status: (page: number, total: number, locale?: string) =>\n `Page ${formatPage(page, locale)} of ${formatPage(total, locale)}`,\n});\n\ndeclare module '@rcarls/rc-common' {\n interface RcMessageCatalog {\n 'rc-pagination': RCPaginationMessages;\n }\n}\n\n/** Detail of `rc-pagination-change`. */\nexport interface RCPaginationChangeEvent {\n /** The page the user asked for. */\n page: number;\n /** The page that was current when the user asked. */\n previousPage: number;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'rc-pagination': RCPagination;\n }\n\n interface HTMLElementEventMap {\n 'rc-pagination-change': CustomEvent<RCPaginationChangeEvent>;\n }\n}\n\n/**\n * Pagination enhancer for a native `<nav>` holding a list of page links or\n * buttons.\n *\n * There is no WAI-ARIA pattern for pagination; the markup follows the GOV.UK\n * and USWDS models. The consumer renders the list, usually from\n * `paginationRange()`, and tags each control with `data-rc-page`: a page\n * number, or `previous`, `next`, `first`, or `last`. The markup works before the\n * element upgrades. The element then keeps `aria-current=\"page\"` on the\n * current page, names the landmark and unlabeled controls, disables steps past\n * either end, and reports each user request as `rc-pagination-change`.\n *\n * Links navigate as usual unless a listener cancels `rc-pagination-change`,\n * which also cancels the click, for client-side routing. Buttons only report.\n * A numbered target above `total` reports the last page; its stale link does\n * not navigate to an out-of-range URL.\n * In uncontrolled use the element moves to the requested page itself; set\n * `page` to control it.\n *\n * Built-in text follows the element's own `lang`, else `<html lang>`: per\n * element through `messages`, or for every instance through\n * `registerMessages()` from `@rcarls/rc-common` under `rc-pagination`.\n *\n * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-pagination rc-pagination docs}\n * @see {@link https://design-system.service.gov.uk/components/pagination/ GOV.UK pagination}\n *\n * @fires rc-pagination-change - Fired when the user activates a page control. Cancelable;\n * canceling it also cancels the click, so a link does not navigate.\n *\n * @attr page - Controlled current page, 1-based. Host writes are silent.\n * @attr default-page - Initial page for uncontrolled use. Defaults to 1.\n * @attr total - Number of pages.\n * @attr announce - Announces the new page through a polite live region after each user change.\n *\n * @cssprop [--rc-pagination-gap=0.25em] - Space between items.\n * @cssprop [--rc-pagination-item-min-inline-size] - Minimum inline size of each item. Defers to\n * the content size when unset.\n */\nexport class RCPagination extends ReactiveElement {\n /** Number of pages. */\n @property({ type: Number })\n total = 1;\n\n /** Announces each user change through a polite live region. */\n @property({ type: Boolean })\n announce = false;\n\n /** Built-in text overrides; unset keys fall back to registered text, then English. */\n @property({ attribute: false })\n messages: Partial<RCPaginationMessages> | undefined;\n\n private _page: number | undefined;\n private _defaultPage = 1;\n private _uncontrolledPage: number | undefined;\n private _pageInitialized = false;\n\n private readonly _messages = new MessagesController(\n this,\n 'rc-pagination',\n DEFAULT_PAGINATION_MESSAGES,\n { overrides: () => this.messages },\n );\n\n // Created on connect or first use, so constructing the element without a\n // DOM (a server render) cannot throw.\n private _observer: MutationObserver | undefined;\n private _announcer: LiveAnnouncer | undefined;\n private _writtenLabel: string | null = null;\n private readonly _writtenNames = new WeakMap<Element, string | null>();\n private readonly _$managedCurrent = new Set<Element>();\n private readonly _$managedDisabled = new Set<Element>();\n private readonly _disabledBeforeEdge = new Map<Element, string | null>();\n\n /** Current page, 1-based and clamped to `[1, total]`. Host writes are silent. */\n @property({ type: Number })\n get page(): number {\n return this._clamp(this._page ?? this._uncontrolledPage ?? this._defaultPage);\n }\n\n set page(value: number | undefined) {\n const oldValue = this.page;\n\n this._page = value;\n this._pageInitialized = true;\n this.requestUpdate('page', oldValue);\n }\n\n /** Initial page for uncontrolled use. */\n @property({ type: Number, attribute: 'default-page' })\n get defaultPage(): number {\n return this._defaultPage;\n }\n\n set defaultPage(value: number) {\n const oldValue = this._defaultPage;\n\n this._defaultPage = value;\n\n if (\n !this._pageInitialized &&\n this._page === undefined &&\n this._uncontrolledPage === undefined\n ) {\n this.requestUpdate('page', this.page);\n }\n\n this.requestUpdate('defaultPage', oldValue);\n }\n\n /** Renders nothing: the consumer's `<nav>` stays the element's only content. */\n protected override createRenderRoot(): HTMLElement {\n return this;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n ensurePaginationBaseStyles(this.getRootNode() as Document | ShadowRoot);\n this._observer ??= new MutationObserver(() => this._sync());\n\n this._observer.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['data-rc-page', 'aria-label', 'aria-labelledby'],\n });\n\n this.addEventListener('click', this._onClick);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._observer?.disconnect();\n this.removeEventListener('click', this._onClick);\n this._announcer?.dispose();\n this._announcer = undefined;\n }\n\n protected override firstUpdated(): void {\n if (import.meta.env.DEV && !this.querySelector(NAV_SELECTOR)) {\n console.warn(\n '[rc-pagination] No direct child <nav> found. Place a native <nav> with a list of ' +\n '<a data-rc-page=\"2\"> or <button data-rc-page=\"2\"> controls inside <rc-pagination>.',\n this,\n );\n }\n }\n\n protected override updated(): void {\n this._sync();\n }\n\n private _clamp(page: number): number {\n const last = Math.max(1, Math.floor(this.total) || 1);\n const value = Number.isFinite(page) ? Math.floor(page) : 1;\n\n return Math.min(Math.max(1, value), last);\n }\n\n private _target($item: Element): PageTarget | undefined {\n const raw = $item.getAttribute('data-rc-page')?.trim() ?? '';\n\n if (raw === 'previous' || raw === 'next' || raw === 'first' || raw === 'last') {\n return raw;\n }\n\n const value = Number(raw);\n\n return Number.isInteger(value) && value > 0 ? value : undefined;\n }\n\n private _resolve(target: PageTarget): number {\n switch (target) {\n case 'previous':\n return this._clamp(this.page - 1);\n case 'next':\n return this._clamp(this.page + 1);\n case 'first':\n return 1;\n case 'last':\n return this._clamp(this.total);\n default:\n return this._clamp(target);\n }\n }\n\n private _sync(): void {\n const $nav = this.querySelector<HTMLElement>(NAV_SELECTOR);\n\n if (!$nav) {\n return;\n }\n\n const messages = this._messages.value;\n const locale = this._messages.locale;\n\n if ($nav.hasAttribute('aria-labelledby')) {\n this._writtenLabel = null;\n } else {\n this._writtenLabel = syncDefaultAttribute(\n $nav,\n 'aria-label',\n messages.label,\n this._writtenLabel,\n );\n }\n\n const page = this.page;\n const total = this._clamp(this.total);\n const $current = new Set<Element>();\n const $disabled = new Set<Element>();\n\n for (const $item of $nav.querySelectorAll(ITEM_SELECTOR)) {\n const target = this._target($item);\n\n if (target === undefined) {\n continue;\n }\n\n if (typeof target === 'number') {\n if (target === page) {\n $current.add($item);\n }\n\n this._syncName($item, messages.page(target, total, locale), true);\n } else {\n const atEdge =\n ((target === 'previous' || target === 'first') && page <= 1) ||\n ((target === 'next' || target === 'last') && page >= total);\n\n if (atEdge) {\n $disabled.add($item);\n }\n\n this._syncName($item, messages[target], false);\n }\n }\n\n for (const $item of this._$managedCurrent) {\n if (!$current.has($item)) {\n $item.removeAttribute('aria-current');\n }\n }\n\n for (const $item of $current) {\n $item.setAttribute('aria-current', 'page');\n }\n\n this._$managedCurrent.clear();\n $current.forEach(($item) => this._$managedCurrent.add($item));\n\n for (const $item of this._$managedDisabled) {\n if (!$disabled.has($item)) {\n const attribute = disabledAttribute($item);\n\n if (this._disabledBeforeEdge.has($item) && attribute) {\n const original = this._disabledBeforeEdge.get($item) ?? null;\n\n if ($item.getAttribute(attribute) === (attribute === 'disabled' ? '' : 'true')) {\n if (original === null) {\n $item.removeAttribute(attribute);\n } else {\n $item.setAttribute(attribute, original);\n }\n }\n\n this._disabledBeforeEdge.delete($item);\n }\n }\n }\n\n for (const $item of $disabled) {\n const attribute = disabledAttribute($item);\n\n if (\n attribute &&\n (!this._$managedDisabled.has($item) ||\n (this._disabledBeforeEdge.has($item) && !$item.hasAttribute(attribute)))\n ) {\n const original = $item.getAttribute(attribute);\n\n if (original !== (attribute === 'disabled' ? '' : 'true')) {\n if (!this._$managedDisabled.has($item)) {\n this._disabledBeforeEdge.set($item, original);\n }\n\n $item.setAttribute(attribute, attribute === 'disabled' ? '' : 'true');\n }\n }\n }\n\n this._$managedDisabled.clear();\n $disabled.forEach(($item) => this._$managedDisabled.add($item));\n }\n\n /**\n * Gives a control its default name unless the author named it. Numbered\n * controls always get one (\"Page 3\" contains the visible \"3\"); steps only\n * when they have no visible text, such as an icon-only button.\n */\n private _syncName($item: Element, name: string, numbered: boolean): void {\n if (!numbered && $item.textContent?.trim()) {\n return;\n }\n\n const written = syncDefaultAttribute(\n $item,\n 'aria-label',\n name,\n this._writtenNames.get($item) ?? null,\n );\n\n this._writtenNames.set($item, written);\n }\n\n private _onClick = (event: MouseEvent): void => {\n const $item = (event.target as Element | null)?.closest?.(ITEM_SELECTOR);\n const $nav = this.querySelector(NAV_SELECTOR);\n\n if (!$item || !$nav?.contains($item)) {\n return;\n }\n\n const target = this._target($item);\n\n if (target === undefined) {\n return;\n }\n\n if (this._$managedDisabled.has($item)) {\n event.preventDefault();\n\n return;\n }\n\n const previousPage = this.page;\n const page = this._resolve(target);\n\n // A stale numbered link may still point beyond `total`; report the\n // clamped page without following that invalid URL.\n if (typeof target === 'number' && target !== page) {\n event.preventDefault();\n }\n\n if (page === previousPage) {\n return;\n }\n\n const change = new CustomEvent<RCPaginationChangeEvent>('rc-pagination-change', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: { page, previousPage },\n });\n\n if (!this.dispatchEvent(change)) {\n event.preventDefault();\n\n return;\n }\n\n if (this._page === undefined) {\n this._uncontrolledPage = page;\n this.requestUpdate('page', previousPage);\n }\n\n if (this.announce) {\n // The live region lives outside the host so it never joins the\n // consumer's list markup, which a framework may own. It mounts in the\n // open dialog around the host, else the current body.\n this._announcer ??= new LiveAnnouncer(this);\n\n this._announcer.announce(\n this._messages.value.status(page, this._clamp(this.total), this._messages.locale),\n );\n }\n };\n}\n\n/** The native or ARIA disabled attribute owned by a pagination control. */\nfunction disabledAttribute($item: Element): 'disabled' | 'aria-disabled' | undefined {\n if ($item instanceof HTMLButtonElement) {\n return 'disabled';\n }\n\n if ($item instanceof HTMLAnchorElement) {\n return 'aria-disabled';\n }\n\n return undefined;\n}\n\nexport default RCPagination;\n"],"names":["PAGINATION_BASE_CSS","cssText","ensurePaginationBaseStyles","defineLightDomBaseStyles","NAV_SELECTOR","ITEM_SELECTOR","formatPage","value","locale","numberFormat","DEFAULT_PAGINATION_MESSAGES","page","_total","total","RCPagination","ReactiveElement","MessagesController","event","$item","$nav","target","previousPage","change","LiveAnnouncer","oldValue","last","raw","messages","syncDefaultAttribute","$current","$disabled","attribute","disabledAttribute","original","name","numbered","written","__decorateClass","property"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { D as n, P as o, R as s, e as A } from "./rc-pagination-mkpZ0mCK.js";
2
+ import { paginationRange as i } from "./range.js";
3
+ export {
4
+ n as DEFAULT_PAGINATION_MESSAGES,
5
+ o as PAGINATION_BASE_CSS,
6
+ s as RCPagination,
7
+ A as ensurePaginationBaseStyles,
8
+ i as paginationRange
9
+ };
10
+ //# sourceMappingURL=rc-pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-pagination.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -0,0 +1 @@
1
+ export * from './index.js';
@@ -0,0 +1,3 @@
1
+ export * from './rc-pagination.js';
2
+ export * from './paginationBaseStyles.js';
3
+ export * from './range.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Light-DOM base styles for the pagination list.
3
+ *
4
+ * The consumer's `<nav>`, list, and controls stay in light DOM. These rules lay
5
+ * the list out as a wrapping inline row and give each control a minimum size;
6
+ * themes supply the shape, color, and state layers.
7
+ */
8
+ export declare const PAGINATION_BASE_CSS: string;
9
+ /** Installs the pagination list styles once per containing root. */
10
+ export declare const ensurePaginationBaseStyles: (root: Document | ShadowRoot) => void;
@@ -0,0 +1,24 @@
1
+ /** One slot in a pagination list: a page number or a gap of hidden pages. */
2
+ export type PaginationItem = number | 'ellipsis';
3
+ export interface PaginationRangeOptions {
4
+ /** The current page, 1-based. Clamped to `[1, total]`. */
5
+ page: number;
6
+ /** The number of pages. */
7
+ total: number;
8
+ /** Pages shown on each side of the current page. Defaults to 1. */
9
+ siblings?: number;
10
+ /** Pages always shown at each end. Defaults to 1. */
11
+ boundaries?: number;
12
+ }
13
+ /**
14
+ * The page numbers to render for a pagination list, with `'ellipsis'` where a
15
+ * run of pages is hidden. A gap of a single page shows that page instead, so
16
+ * an ellipsis always stands for at least two pages.
17
+ *
18
+ * Pure and DOM-free: import it from `@rcarls/rc-pagination/range` to build the
19
+ * markup on a server without loading the element.
20
+ *
21
+ * @example
22
+ * paginationRange({ page: 6, total: 12 }); // [1, 'ellipsis', 5, 6, 7, 'ellipsis', 12]
23
+ */
24
+ export declare function paginationRange({ page, total, siblings, boundaries, }: PaginationRangeOptions): PaginationItem[];
@@ -0,0 +1,121 @@
1
+ import { ReactiveElement } from 'lit';
2
+ /** Built-in text of `rc-pagination`. */
3
+ export interface RCPaginationMessages {
4
+ /** Accessible name for the `<nav>` landmark when the author gives it none. */
5
+ label: string;
6
+ /** Accessible name of a numbered page link or button. */
7
+ page: (page: number, total: number, locale?: string) => string;
8
+ /** Name of a previous control that has no text of its own. */
9
+ previous: string;
10
+ /** Name of a next control that has no text of its own. */
11
+ next: string;
12
+ /** Name of a first-page control that has no text of its own. */
13
+ first: string;
14
+ /** Name of a last-page control that has no text of its own. */
15
+ last: string;
16
+ /** Announcement after a page change, when `announce` is set. */
17
+ status: (page: number, total: number, locale?: string) => string;
18
+ }
19
+ export declare const DEFAULT_PAGINATION_MESSAGES: Readonly<RCPaginationMessages>;
20
+ declare module '@rcarls/rc-common' {
21
+ interface RcMessageCatalog {
22
+ 'rc-pagination': RCPaginationMessages;
23
+ }
24
+ }
25
+ /** Detail of `rc-pagination-change`. */
26
+ export interface RCPaginationChangeEvent {
27
+ /** The page the user asked for. */
28
+ page: number;
29
+ /** The page that was current when the user asked. */
30
+ previousPage: number;
31
+ }
32
+ declare global {
33
+ interface HTMLElementTagNameMap {
34
+ 'rc-pagination': RCPagination;
35
+ }
36
+ interface HTMLElementEventMap {
37
+ 'rc-pagination-change': CustomEvent<RCPaginationChangeEvent>;
38
+ }
39
+ }
40
+ /**
41
+ * Pagination enhancer for a native `<nav>` holding a list of page links or
42
+ * buttons.
43
+ *
44
+ * There is no WAI-ARIA pattern for pagination; the markup follows the GOV.UK
45
+ * and USWDS models. The consumer renders the list, usually from
46
+ * `paginationRange()`, and tags each control with `data-rc-page`: a page
47
+ * number, or `previous`, `next`, `first`, or `last`. The markup works before the
48
+ * element upgrades. The element then keeps `aria-current="page"` on the
49
+ * current page, names the landmark and unlabeled controls, disables steps past
50
+ * either end, and reports each user request as `rc-pagination-change`.
51
+ *
52
+ * Links navigate as usual unless a listener cancels `rc-pagination-change`,
53
+ * which also cancels the click, for client-side routing. Buttons only report.
54
+ * A numbered target above `total` reports the last page; its stale link does
55
+ * not navigate to an out-of-range URL.
56
+ * In uncontrolled use the element moves to the requested page itself; set
57
+ * `page` to control it.
58
+ *
59
+ * Built-in text follows the element's own `lang`, else `<html lang>`: per
60
+ * element through `messages`, or for every instance through
61
+ * `registerMessages()` from `@rcarls/rc-common` under `rc-pagination`.
62
+ *
63
+ * @see {@link https://richardcarls.github.io/rc-webcomponents/components/rc-pagination rc-pagination docs}
64
+ * @see {@link https://design-system.service.gov.uk/components/pagination/ GOV.UK pagination}
65
+ *
66
+ * @fires rc-pagination-change - Fired when the user activates a page control. Cancelable;
67
+ * canceling it also cancels the click, so a link does not navigate.
68
+ *
69
+ * @attr page - Controlled current page, 1-based. Host writes are silent.
70
+ * @attr default-page - Initial page for uncontrolled use. Defaults to 1.
71
+ * @attr total - Number of pages.
72
+ * @attr announce - Announces the new page through a polite live region after each user change.
73
+ *
74
+ * @cssprop [--rc-pagination-gap=0.25em] - Space between items.
75
+ * @cssprop [--rc-pagination-item-min-inline-size] - Minimum inline size of each item. Defers to
76
+ * the content size when unset.
77
+ */
78
+ export declare class RCPagination extends ReactiveElement {
79
+ /** Number of pages. */
80
+ total: number;
81
+ /** Announces each user change through a polite live region. */
82
+ announce: boolean;
83
+ /** Built-in text overrides; unset keys fall back to registered text, then English. */
84
+ messages: Partial<RCPaginationMessages> | undefined;
85
+ private _page;
86
+ private _defaultPage;
87
+ private _uncontrolledPage;
88
+ private _pageInitialized;
89
+ private readonly _messages;
90
+ private _observer;
91
+ private _announcer;
92
+ private _writtenLabel;
93
+ private readonly _writtenNames;
94
+ private readonly _$managedCurrent;
95
+ private readonly _$managedDisabled;
96
+ private readonly _disabledBeforeEdge;
97
+ /** Current page, 1-based and clamped to `[1, total]`. Host writes are silent. */
98
+ get page(): number;
99
+ set page(value: number | undefined);
100
+ /** Initial page for uncontrolled use. */
101
+ get defaultPage(): number;
102
+ set defaultPage(value: number);
103
+ /** Renders nothing: the consumer's `<nav>` stays the element's only content. */
104
+ protected createRenderRoot(): HTMLElement;
105
+ connectedCallback(): void;
106
+ disconnectedCallback(): void;
107
+ protected firstUpdated(): void;
108
+ protected updated(): void;
109
+ private _clamp;
110
+ private _target;
111
+ private _resolve;
112
+ private _sync;
113
+ /**
114
+ * Gives a control its default name unless the author named it. Numbered
115
+ * controls always get one ("Page 3" contains the visible "3"); steps only
116
+ * when they have no visible text, such as an icon-only button.
117
+ */
118
+ private _syncName;
119
+ private _onClick;
120
+ }
121
+ export default RCPagination;
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@rcarls/rc-pagination",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.0.0-next-20261102181447",
7
+ "description": "Pagination enhancer for a native <nav> holding page links or buttons, with a server-safe page range helper.",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
11
+ "directory": "packages/rc-pagination"
12
+ },
13
+ "homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-pagination",
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "module": "./dist/rc-pagination.js",
20
+ "exports": {
21
+ ".": {
22
+ "import": {
23
+ "types": "./dist/types/packages/rc-pagination/src/index.d.ts",
24
+ "default": "./dist/rc-pagination.js"
25
+ }
26
+ },
27
+ "./define": {
28
+ "import": {
29
+ "types": "./dist/types/packages/rc-pagination/src/define.d.ts",
30
+ "default": "./dist/rc-pagination-define.js"
31
+ }
32
+ },
33
+ "./range": {
34
+ "import": {
35
+ "types": "./dist/types/packages/rc-pagination/src/range.d.ts",
36
+ "default": "./dist/range.js"
37
+ }
38
+ },
39
+ "./base.css": "./dist/base.css"
40
+ },
41
+ "types": "./dist/types/packages/rc-pagination/src/index.d.ts",
42
+ "sideEffects": [
43
+ "./dist/rc-pagination-define.js",
44
+ "./dist/base.css"
45
+ ],
46
+ "customElements": "dist/custom-elements.json",
47
+ "scripts": {
48
+ "build": "tsc && vite build && node ../../scripts/emit-light-dom-base-css.mjs && cem analyze",
49
+ "cem:analyze": "cem analyze",
50
+ "test:browser": "vitest --run",
51
+ "test:browser:chrome": "vitest --run --project=chromium",
52
+ "test:browser:firefox": "vitest --run --project=firefox"
53
+ },
54
+ "dependencies": {
55
+ "@rcarls/rc-common": "0.0.0-next-20261102181447"
56
+ },
57
+ "devDependencies": {
58
+ "@custom-elements-manifest/analyzer": "0.11.0",
59
+ "@vitest/browser-playwright": "4.1.5",
60
+ "lit": "^3.0.0",
61
+ "playwright": "^1.56.0",
62
+ "rollup": "^4.60.2",
63
+ "typescript": "~5.9.3",
64
+ "vite": "^7.1.7",
65
+ "vite-plugin-dts": "^4.5.4",
66
+ "vitest": "^4.0.6",
67
+ "vitest-browser-lit": "^1.0.1"
68
+ },
69
+ "peerDependencies": {
70
+ "lit": "^3.0.0"
71
+ }
72
+ }