@cdevhub/ngx-tw 0.2.0
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/LICENSE +21 -0
- package/README.md +134 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs +108 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs +240 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs +287 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs +332 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +452 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs +227 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +417 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +232 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs +4482 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs +157 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs +1454 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +593 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs +228 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +510 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs +1721 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +721 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs +861 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +1558 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +1833 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs +693 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +331 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +289 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs +572 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs +36 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs +278 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs +355 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs +241 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs +433 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs +691 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs +678 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +335 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs +708 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +319 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs +1722 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs +115 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs +768 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs +149 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs +1147 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs +346 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs +186 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs +1082 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs +579 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs +443 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs +391 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +315 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs +994 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs +508 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs +147 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs +178 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +1344 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs +1136 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +1311 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +533 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw.mjs +61 -0
- package/fesm2022/cdevhub-ngx-tw.mjs.map +1 -0
- package/package.json +265 -0
- package/theme/_base.css +394 -0
- package/theme/_dark.css +510 -0
- package/theme/_high-contrast.css +290 -0
- package/theme/_semantic.css +317 -0
- package/theme/_typography.css +9 -0
- package/theme/index.css +54 -0
- package/types/cdevhub-ngx-tw-accordion.d.ts +48 -0
- package/types/cdevhub-ngx-tw-alert.d.ts +67 -0
- package/types/cdevhub-ngx-tw-avatar.d.ts +79 -0
- package/types/cdevhub-ngx-tw-badge.d.ts +94 -0
- package/types/cdevhub-ngx-tw-breadcrumbs.d.ts +227 -0
- package/types/cdevhub-ngx-tw-button.d.ts +74 -0
- package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +200 -0
- package/types/cdevhub-ngx-tw-calendar-testing.d.ts +113 -0
- package/types/cdevhub-ngx-tw-calendar.d.ts +1704 -0
- package/types/cdevhub-ngx-tw-card.d.ts +48 -0
- package/types/cdevhub-ngx-tw-carousel.d.ts +406 -0
- package/types/cdevhub-ngx-tw-checkbox.d.ts +136 -0
- package/types/cdevhub-ngx-tw-code-block.d.ts +93 -0
- package/types/cdevhub-ngx-tw-collapsible.d.ts +151 -0
- package/types/cdevhub-ngx-tw-combobox.d.ts +389 -0
- package/types/cdevhub-ngx-tw-command-palette.d.ts +215 -0
- package/types/cdevhub-ngx-tw-core.d.ts +719 -0
- package/types/cdevhub-ngx-tw-date-picker.d.ts +317 -0
- package/types/cdevhub-ngx-tw-date-range-picker.d.ts +313 -0
- package/types/cdevhub-ngx-tw-dialog.d.ts +303 -0
- package/types/cdevhub-ngx-tw-empty-state.d.ts +104 -0
- package/types/cdevhub-ngx-tw-flip-card.d.ts +95 -0
- package/types/cdevhub-ngx-tw-form-field.d.ts +210 -0
- package/types/cdevhub-ngx-tw-icon-lucide.d.ts +29 -0
- package/types/cdevhub-ngx-tw-icon.d.ts +134 -0
- package/types/cdevhub-ngx-tw-input.d.ts +110 -0
- package/types/cdevhub-ngx-tw-item.d.ts +70 -0
- package/types/cdevhub-ngx-tw-menu.d.ts +99 -0
- package/types/cdevhub-ngx-tw-paginator.d.ts +305 -0
- package/types/cdevhub-ngx-tw-popover.d.ts +157 -0
- package/types/cdevhub-ngx-tw-progress-bar.d.ts +100 -0
- package/types/cdevhub-ngx-tw-radio.d.ts +174 -0
- package/types/cdevhub-ngx-tw-segmented-control.d.ts +66 -0
- package/types/cdevhub-ngx-tw-select.d.ts +384 -0
- package/types/cdevhub-ngx-tw-separator.d.ts +28 -0
- package/types/cdevhub-ngx-tw-sheet.d.ts +335 -0
- package/types/cdevhub-ngx-tw-skeleton.d.ts +52 -0
- package/types/cdevhub-ngx-tw-slider.d.ts +258 -0
- package/types/cdevhub-ngx-tw-sort.d.ts +136 -0
- package/types/cdevhub-ngx-tw-spinner.d.ts +50 -0
- package/types/cdevhub-ngx-tw-split.d.ts +265 -0
- package/types/cdevhub-ngx-tw-stat.d.ts +140 -0
- package/types/cdevhub-ngx-tw-stepper.d.ts +127 -0
- package/types/cdevhub-ngx-tw-switch.d.ts +88 -0
- package/types/cdevhub-ngx-tw-tab-nav.d.ts +107 -0
- package/types/cdevhub-ngx-tw-table.d.ts +479 -0
- package/types/cdevhub-ngx-tw-tabs.d.ts +121 -0
- package/types/cdevhub-ngx-tw-textarea.d.ts +62 -0
- package/types/cdevhub-ngx-tw-theme.d.ts +106 -0
- package/types/cdevhub-ngx-tw-time-picker.d.ts +303 -0
- package/types/cdevhub-ngx-tw-timeline.d.ts +278 -0
- package/types/cdevhub-ngx-tw-toast.d.ts +388 -0
- package/types/cdevhub-ngx-tw-tooltip.d.ts +95 -0
- package/types/cdevhub-ngx-tw.d.ts +49 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, inject, TemplateRef, Directive, ElementRef, model, output, contentChildren, contentChild, viewChildren, computed, afterNextRender, isDevMode, effect, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
4
|
+
import { LiveAnnouncer, FocusKeyManager } from '@angular/cdk/a11y';
|
|
5
|
+
import { tv } from 'tailwind-variants';
|
|
6
|
+
|
|
7
|
+
// ── Default labels ────────────────────────────────────────────────
|
|
8
|
+
const DEFAULT_LABELS = {
|
|
9
|
+
ariaLabel: 'Pagination',
|
|
10
|
+
previous: 'Previous',
|
|
11
|
+
next: 'Next',
|
|
12
|
+
first: 'First page',
|
|
13
|
+
last: 'Last page',
|
|
14
|
+
pageInfo: 'Page',
|
|
15
|
+
pageInfoSeparator: ' of ',
|
|
16
|
+
pageRange: '{start}\u2013{end} of {total}',
|
|
17
|
+
pageSizeLabel: 'Items per page:',
|
|
18
|
+
announcement: 'Page {page} of {totalPages}',
|
|
19
|
+
pageButtonAriaLabel: 'Go to page {page}',
|
|
20
|
+
currentPageAriaLabel: 'Page {page}, current page',
|
|
21
|
+
ellipsis: 'More pages',
|
|
22
|
+
empty: 'No results',
|
|
23
|
+
};
|
|
24
|
+
// ── Static active-page class maps (Tailwind v4 requires statically written class strings) ──
|
|
25
|
+
// Mirrors the checkbox SOLID_BOX / SOLID_ICON split: a per-color background + border map
|
|
26
|
+
// and a per-color on-color text map. `text-on-{color}` keeps the active button readable
|
|
27
|
+
// against any consumer theme without hardcoding `text-white` (would fail for warning's
|
|
28
|
+
// dark-on-amber convention) or `text-black`. The legacy `--color-on-*` alias tokens still
|
|
29
|
+
// resolve to the canonical solid-fg slot per `theme/_semantic.css` lines 284-296.
|
|
30
|
+
const PAGE_BUTTON_ACTIVE_BG = {
|
|
31
|
+
primary: 'bg-primary-600 hover:bg-primary-700 border-primary-600 focus-visible:outline-primary-500',
|
|
32
|
+
secondary: 'bg-secondary-600 hover:bg-secondary-700 border-secondary-600 focus-visible:outline-secondary-500',
|
|
33
|
+
accent: 'bg-accent-600 hover:bg-accent-700 border-accent-600 focus-visible:outline-accent-500',
|
|
34
|
+
neutral: 'bg-fg hover:bg-fg border-fg focus-visible:outline-fg',
|
|
35
|
+
info: 'bg-info-600 hover:bg-info-700 border-info-600 focus-visible:outline-info-500',
|
|
36
|
+
success: 'bg-success-600 hover:bg-success-700 border-success-600 focus-visible:outline-success-500',
|
|
37
|
+
// Warning keeps -500 because the amber-500/amber-950 pairing meets contrast — see
|
|
38
|
+
// `theme/_semantic.css` warning role comment ("yellow signage convention").
|
|
39
|
+
warning: 'bg-warning-500 hover:bg-warning-600 border-warning-500 focus-visible:outline-warning-500',
|
|
40
|
+
error: 'bg-error-600 hover:bg-error-700 border-error-600 focus-visible:outline-error-500',
|
|
41
|
+
};
|
|
42
|
+
const PAGE_BUTTON_ACTIVE_FG = {
|
|
43
|
+
primary: 'text-on-primary',
|
|
44
|
+
secondary: 'text-on-secondary',
|
|
45
|
+
accent: 'text-on-accent',
|
|
46
|
+
neutral: 'text-on-neutral',
|
|
47
|
+
info: 'text-on-info',
|
|
48
|
+
success: 'text-on-success',
|
|
49
|
+
warning: 'text-on-warning',
|
|
50
|
+
error: 'text-on-error',
|
|
51
|
+
};
|
|
52
|
+
// ── Pure helpers (exported for unit testing only — not re-exported from index.ts) ──
|
|
53
|
+
/**
|
|
54
|
+
* Builds the visible page range for the numbered paginator, inserting ellipsis
|
|
55
|
+
* sentinels where ranges are collapsed. Port of the shadcn/Radix algorithm.
|
|
56
|
+
*
|
|
57
|
+
* @internal exported for unit tests; not part of the public API.
|
|
58
|
+
*/
|
|
59
|
+
function buildPaginationRange(currentPage, totalPages, siblingCount, boundaryCount) {
|
|
60
|
+
const total = Math.max(0, Math.floor(totalPages));
|
|
61
|
+
if (total <= 0)
|
|
62
|
+
return [];
|
|
63
|
+
const siblings = Math.max(0, Math.floor(siblingCount));
|
|
64
|
+
const boundaries = Math.max(0, Math.floor(boundaryCount));
|
|
65
|
+
const current = Math.min(Math.max(1, Math.floor(currentPage)), total);
|
|
66
|
+
// If all pages fit without ellipses, show them all.
|
|
67
|
+
const totalPageButtons = boundaries * 2 + siblings * 2 + 3;
|
|
68
|
+
if (total <= totalPageButtons) {
|
|
69
|
+
return range(1, total);
|
|
70
|
+
}
|
|
71
|
+
const leftBoundary = range(1, Math.min(boundaries, total));
|
|
72
|
+
const rightBoundary = range(Math.max(total - boundaries + 1, 1), total);
|
|
73
|
+
const leftSibling = Math.max(current - siblings, boundaries + 2);
|
|
74
|
+
const rightSibling = Math.min(current + siblings, total - boundaries - 1);
|
|
75
|
+
const showLeftEllipsis = leftSibling > boundaries + 2;
|
|
76
|
+
const showRightEllipsis = rightSibling < total - boundaries - 1;
|
|
77
|
+
const middle = [];
|
|
78
|
+
if (showLeftEllipsis) {
|
|
79
|
+
middle.push('ellipsis-left');
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Fill the gap between left boundary and leftSibling with explicit numbers.
|
|
83
|
+
for (let i = boundaries + 1; i < leftSibling; i++)
|
|
84
|
+
middle.push(i);
|
|
85
|
+
}
|
|
86
|
+
for (let i = leftSibling; i <= rightSibling; i++)
|
|
87
|
+
middle.push(i);
|
|
88
|
+
if (showRightEllipsis) {
|
|
89
|
+
middle.push('ellipsis-right');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
for (let i = rightSibling + 1; i <= total - boundaries; i++)
|
|
93
|
+
middle.push(i);
|
|
94
|
+
}
|
|
95
|
+
return [...leftBoundary, ...middle, ...rightBoundary];
|
|
96
|
+
}
|
|
97
|
+
function range(start, end) {
|
|
98
|
+
if (end < start)
|
|
99
|
+
return [];
|
|
100
|
+
const out = new Array(end - start + 1);
|
|
101
|
+
for (let i = 0; i < out.length; i++)
|
|
102
|
+
out[i] = start + i;
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/** Replaces `{key}` placeholders in `template` with values from `vars`. */
|
|
106
|
+
function formatLabel(template, vars) {
|
|
107
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => Object.prototype.hasOwnProperty.call(vars, key) ? String(vars[key]) : match);
|
|
108
|
+
}
|
|
109
|
+
// ── tv() config ───────────────────────────────────────────────────
|
|
110
|
+
const paginatorVariants = tv({
|
|
111
|
+
slots: {
|
|
112
|
+
root: 'flex w-full',
|
|
113
|
+
inner: 'flex items-center gap-3 flex-wrap w-full min-w-0',
|
|
114
|
+
pageSizeGroup: 'flex items-center gap-2 shrink-0',
|
|
115
|
+
pageSizeLabel: 'text-sm text-fg-muted whitespace-nowrap',
|
|
116
|
+
pageSizeSelect: 'rounded-md border border-border bg-surface text-fg cursor-pointer transition-colors duration-normal motion-reduce:transition-none hover:bg-surface-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 disabled:opacity-50 disabled:pointer-events-none',
|
|
117
|
+
pageInfo: 'text-sm text-fg-muted whitespace-nowrap',
|
|
118
|
+
navGroup: 'flex items-center gap-1',
|
|
119
|
+
pageList: 'flex items-center gap-1',
|
|
120
|
+
navButton: 'inline-flex items-center justify-center rounded-md border border-border bg-surface text-fg transition-colors duration-normal motion-reduce:transition-none cursor-pointer hover:bg-surface-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed',
|
|
121
|
+
pageButton: 'inline-flex items-center justify-center rounded-md border border-transparent text-fg transition-colors duration-normal motion-reduce:transition-none cursor-pointer hover:bg-surface-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed',
|
|
122
|
+
ellipsis: 'inline-flex items-center justify-center text-fg-subtle select-none pointer-events-none',
|
|
123
|
+
emptyState: 'text-sm text-fg-muted',
|
|
124
|
+
icon: 'size-4 shrink-0',
|
|
125
|
+
},
|
|
126
|
+
variants: {
|
|
127
|
+
size: {
|
|
128
|
+
xs: {
|
|
129
|
+
navButton: 'px-2 py-1 text-xs min-w-7 h-7',
|
|
130
|
+
pageButton: 'px-2 py-1 text-xs min-w-7 h-7',
|
|
131
|
+
pageSizeSelect: 'px-2 py-1 text-xs',
|
|
132
|
+
ellipsis: 'min-w-7 h-7 text-xs',
|
|
133
|
+
// `size-3.5` (14px) half-step: neither size-3 nor size-4 aligns with
|
|
134
|
+
// text-xs glyph metrics inside a 28px nav button.
|
|
135
|
+
icon: 'size-3.5',
|
|
136
|
+
},
|
|
137
|
+
sm: {
|
|
138
|
+
navButton: 'px-2.5 py-1.5 text-sm min-w-8 h-8',
|
|
139
|
+
pageButton: 'px-2.5 py-1.5 text-sm min-w-8 h-8',
|
|
140
|
+
pageSizeSelect: 'px-2.5 py-1.5 text-sm',
|
|
141
|
+
ellipsis: 'min-w-8 h-8 text-sm',
|
|
142
|
+
icon: 'size-4',
|
|
143
|
+
},
|
|
144
|
+
md: {
|
|
145
|
+
navButton: 'px-3 py-2 text-sm min-w-9 h-9',
|
|
146
|
+
pageButton: 'px-3 py-2 text-sm min-w-9 h-9',
|
|
147
|
+
pageSizeSelect: 'px-3 py-2 text-sm',
|
|
148
|
+
ellipsis: 'min-w-9 h-9 text-sm',
|
|
149
|
+
icon: 'size-4',
|
|
150
|
+
},
|
|
151
|
+
lg: {
|
|
152
|
+
navButton: 'px-4 py-2.5 text-base min-w-10 h-10',
|
|
153
|
+
pageButton: 'px-4 py-2.5 text-base min-w-10 h-10',
|
|
154
|
+
pageSizeSelect: 'px-4 py-2.5 text-base',
|
|
155
|
+
ellipsis: 'min-w-10 h-10 text-base',
|
|
156
|
+
icon: 'size-5',
|
|
157
|
+
},
|
|
158
|
+
xl: {
|
|
159
|
+
navButton: 'px-5 py-3 text-base min-w-11 h-11',
|
|
160
|
+
pageButton: 'px-5 py-3 text-base min-w-11 h-11',
|
|
161
|
+
pageSizeSelect: 'px-5 py-3 text-base',
|
|
162
|
+
ellipsis: 'min-w-11 h-11 text-base',
|
|
163
|
+
icon: 'size-5',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
layout: {
|
|
167
|
+
compact: { inner: 'justify-start' },
|
|
168
|
+
spread: { inner: 'justify-between' },
|
|
169
|
+
},
|
|
170
|
+
type: {
|
|
171
|
+
basic: { pageList: 'hidden' },
|
|
172
|
+
numbered: {},
|
|
173
|
+
},
|
|
174
|
+
responsive: {
|
|
175
|
+
auto: { root: '@container' },
|
|
176
|
+
off: {},
|
|
177
|
+
},
|
|
178
|
+
disabled: {
|
|
179
|
+
true: { root: 'opacity-50 pointer-events-none' },
|
|
180
|
+
false: {},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
compoundVariants: [
|
|
184
|
+
// Auto-collapse numbered page list under 30rem container width.
|
|
185
|
+
{
|
|
186
|
+
type: 'numbered',
|
|
187
|
+
responsive: 'auto',
|
|
188
|
+
class: { pageList: 'hidden @[30rem]:flex' },
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
defaultVariants: {
|
|
192
|
+
size: 'md',
|
|
193
|
+
layout: 'compact',
|
|
194
|
+
type: 'numbered',
|
|
195
|
+
responsive: 'auto',
|
|
196
|
+
disabled: false,
|
|
197
|
+
},
|
|
198
|
+
}, { twMerge: true });
|
|
199
|
+
// ── Template directives ───────────────────────────────────────────
|
|
200
|
+
/**
|
|
201
|
+
* Structural directive on an `<ng-template>` that replaces a specific label slot.
|
|
202
|
+
* Usage: `<ng-template twPaginatorLabel slot="pageInfo" let-ctx> … </ng-template>`.
|
|
203
|
+
*/
|
|
204
|
+
class PaginatorLabelDirective {
|
|
205
|
+
/** The label slot this template overrides. */
|
|
206
|
+
slot = input.required(...(ngDevMode ? [{ debugName: "slot" }] : /* istanbul ignore next */ []));
|
|
207
|
+
/** @internal */
|
|
208
|
+
templateRef = inject((TemplateRef));
|
|
209
|
+
/** @internal */
|
|
210
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
214
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: PaginatorLabelDirective, isStandalone: true, selector: "ng-template[twPaginatorLabel]", inputs: { slot: { classPropertyName: "slot", publicName: "slot", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
215
|
+
}
|
|
216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorLabelDirective, decorators: [{
|
|
217
|
+
type: Directive,
|
|
218
|
+
args: [{ selector: 'ng-template[twPaginatorLabel]' }]
|
|
219
|
+
}], propDecorators: { slot: [{ type: i0.Input, args: [{ isSignal: true, alias: "slot", required: true }] }] } });
|
|
220
|
+
/**
|
|
221
|
+
* Structural directive on an `<ng-template>` that renders when `totalItems === 0`
|
|
222
|
+
* and `hideOnEmpty` is `false`. When absent, `labels.empty` renders as fallback.
|
|
223
|
+
*/
|
|
224
|
+
class PaginatorEmptyDirective {
|
|
225
|
+
/** @internal */
|
|
226
|
+
templateRef = inject((TemplateRef));
|
|
227
|
+
/** @internal */
|
|
228
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorEmptyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
232
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: PaginatorEmptyDirective, isStandalone: true, selector: "ng-template[twPaginatorEmpty]", ngImport: i0 });
|
|
233
|
+
}
|
|
234
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorEmptyDirective, decorators: [{
|
|
235
|
+
type: Directive,
|
|
236
|
+
args: [{ selector: 'ng-template[twPaginatorEmpty]' }]
|
|
237
|
+
}] });
|
|
238
|
+
/**
|
|
239
|
+
* Structural directive on an `<ng-template>` that replaces the default page-size
|
|
240
|
+
* selector UI entirely. Context `$implicit` exposes `{ pageSize, options, setPageSize }`.
|
|
241
|
+
*/
|
|
242
|
+
class PaginatorPageSizeSelectorDirective {
|
|
243
|
+
/** @internal */
|
|
244
|
+
templateRef = inject((TemplateRef));
|
|
245
|
+
/** @internal */
|
|
246
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorPageSizeSelectorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
250
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: PaginatorPageSizeSelectorDirective, isStandalone: true, selector: "ng-template[twPaginatorPageSizeSelector]", ngImport: i0 });
|
|
251
|
+
}
|
|
252
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorPageSizeSelectorDirective, decorators: [{
|
|
253
|
+
type: Directive,
|
|
254
|
+
args: [{ selector: 'ng-template[twPaginatorPageSizeSelector]' }]
|
|
255
|
+
}] });
|
|
256
|
+
// ── PaginatorFocusableDirective ───────────────────────────────────
|
|
257
|
+
/**
|
|
258
|
+
* @internal Wraps every nav-group focusable (first/prev/page/next/last) as a CDK
|
|
259
|
+
* `FocusableOption` so `FocusKeyManager` can drive roving focus across the group.
|
|
260
|
+
* Applied via attribute selector on the existing `data-tw-paginator-focusable`
|
|
261
|
+
* marker so the spec's button queries continue to match.
|
|
262
|
+
*/
|
|
263
|
+
class PaginatorFocusableDirective {
|
|
264
|
+
elementRef = inject((ElementRef));
|
|
265
|
+
/** When true, the element is skipped by `FocusKeyManager` roving focus. */
|
|
266
|
+
isDisabled = input(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
267
|
+
focus() {
|
|
268
|
+
this.elementRef.nativeElement.focus();
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* `FocusableOption.disabled` resolver. NOTE: returning the signal function
|
|
272
|
+
* itself would always be truthy, so `FocusKeyManager` would skip every
|
|
273
|
+
* element. Call the signal here so the manager sees the actual value.
|
|
274
|
+
*/
|
|
275
|
+
get disabled() {
|
|
276
|
+
return this.isDisabled();
|
|
277
|
+
}
|
|
278
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorFocusableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
279
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: PaginatorFocusableDirective, isStandalone: true, selector: "[data-tw-paginator-focusable]", inputs: { isDisabled: { classPropertyName: "isDisabled", publicName: "isDisabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
280
|
+
}
|
|
281
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorFocusableDirective, decorators: [{
|
|
282
|
+
type: Directive,
|
|
283
|
+
args: [{
|
|
284
|
+
selector: '[data-tw-paginator-focusable]',
|
|
285
|
+
}]
|
|
286
|
+
}], propDecorators: { isDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDisabled", required: false }] }] } });
|
|
287
|
+
// ── PaginatorComponent ────────────────────────────────────────────
|
|
288
|
+
let nextPaginatorId = 0;
|
|
289
|
+
class PaginatorComponent {
|
|
290
|
+
// ── Inputs ──
|
|
291
|
+
/** Total number of items across all pages. Defaults to `0`. */
|
|
292
|
+
totalItems = input(0, ...(ngDevMode ? [{ debugName: "totalItems" }] : /* istanbul ignore next */ []));
|
|
293
|
+
/** Items per page. Two-way bindable via `[(pageSize)]`. Defaults to `10`. */
|
|
294
|
+
pageSize = model(10, ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
|
|
295
|
+
/** 1-based current page. Two-way bindable via `[(page)]`. Clamped internally. Defaults to `1`. */
|
|
296
|
+
page = model(1, ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
|
|
297
|
+
/** Rendering type. Defaults to `'numbered'`. */
|
|
298
|
+
type = input('numbered', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
299
|
+
/** Layout density. Defaults to `'compact'`. */
|
|
300
|
+
layout = input('compact', ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
301
|
+
/** Controls padding, font size, and icon size. Defaults to `'md'`. */
|
|
302
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
303
|
+
/** Semantic color used for the active page indicator. Defaults to `'primary'`. */
|
|
304
|
+
color = input('primary', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
305
|
+
/** How many sibling pages to show on each side of the current page. Defaults to `1`. */
|
|
306
|
+
siblingCount = input(1, ...(ngDevMode ? [{ debugName: "siblingCount" }] : /* istanbul ignore next */ []));
|
|
307
|
+
/** How many pages to always show at the start and end boundaries. Defaults to `1`. */
|
|
308
|
+
boundaryCount = input(1, ...(ngDevMode ? [{ debugName: "boundaryCount" }] : /* istanbul ignore next */ []));
|
|
309
|
+
/** When true, renders jump-to-first and jump-to-last buttons. Defaults to `true`. */
|
|
310
|
+
// TRUE-default: first/last jumps are the standard pagination affordance for
|
|
311
|
+
// any list large enough to need a paginator; opt-out is for compact paginators.
|
|
312
|
+
showFirstLastButtons = input(true, ...(ngDevMode ? [{ debugName: "showFirstLastButtons" }] : /* istanbul ignore next */ []));
|
|
313
|
+
/** When true, renders the page-size selector region. Defaults to `false`. */
|
|
314
|
+
showPageSizeSelector = input(false, ...(ngDevMode ? [{ debugName: "showPageSizeSelector" }] : /* istanbul ignore next */ []));
|
|
315
|
+
/** Options for the default page-size selector. Ignored when `*twPaginatorPageSizeSelector` is projected. Defaults to `[10, 25, 50, 100]`. */
|
|
316
|
+
pageSizeOptions = input([10, 25, 50, 100], ...(ngDevMode ? [{ debugName: "pageSizeOptions" }] : /* istanbul ignore next */ []));
|
|
317
|
+
/** When true, renders the page-info text region. Defaults to `true`. */
|
|
318
|
+
// TRUE-default: showing "X–Y of Z" is the expected pagination context; opt-out
|
|
319
|
+
// is for ultra-compact paginators that fit in tight UI.
|
|
320
|
+
showPageInfo = input(true, ...(ngDevMode ? [{ debugName: "showPageInfo" }] : /* istanbul ignore next */ []));
|
|
321
|
+
/** When true, renders nothing when `totalItems === 0`. Defaults to `true`. */
|
|
322
|
+
// TRUE-default: hiding the paginator on empty data is expected UX; opt-out is
|
|
323
|
+
// for layouts that need to reserve the paginator's vertical space.
|
|
324
|
+
hideOnEmpty = input(true, ...(ngDevMode ? [{ debugName: "hideOnEmpty" }] : /* istanbul ignore next */ []));
|
|
325
|
+
/** When true, renders nothing when `totalPages <= 1`. Defaults to `false`. */
|
|
326
|
+
hideOnSinglePage = input(false, ...(ngDevMode ? [{ debugName: "hideOnSinglePage" }] : /* istanbul ignore next */ []));
|
|
327
|
+
/** Responsive collapse mode. Defaults to `'auto'`. */
|
|
328
|
+
responsive = input('auto', ...(ngDevMode ? [{ debugName: "responsive" }] : /* istanbul ignore next */ []));
|
|
329
|
+
/** When true, every button is disabled. Defaults to `false`. */
|
|
330
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
331
|
+
/** Partial string labels object for i18n. Unset keys fall back to English defaults. Defaults to `{}`. */
|
|
332
|
+
labels = input({}, ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
|
|
333
|
+
/** When provided, buttons render as anchor links using the returned `href`. Defaults to `undefined` (renders as buttons). */
|
|
334
|
+
linkFactory = input(undefined, ...(ngDevMode ? [{ debugName: "linkFactory" }] : /* istanbul ignore next */ []));
|
|
335
|
+
/** Overrides `labels.ariaLabel` for the root `<nav>` element. */
|
|
336
|
+
customAriaLabel = input(undefined, { ...(ngDevMode ? { debugName: "customAriaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
|
|
337
|
+
// ── Outputs ──
|
|
338
|
+
/** Fires when `page` or `pageSize` changes. Payload includes derived helpers (`start`, `end`, `totalPages`, `source`). Use `[(page)]` / `[(pageSize)]` for two-way binding; subscribe to this for the rich event payload. */
|
|
339
|
+
paginated = output();
|
|
340
|
+
// ── Projected templates ──
|
|
341
|
+
/** @internal */
|
|
342
|
+
labelDirectives = contentChildren(PaginatorLabelDirective, ...(ngDevMode ? [{ debugName: "labelDirectives" }] : /* istanbul ignore next */ []));
|
|
343
|
+
/** @internal */
|
|
344
|
+
emptyDirective = contentChild(PaginatorEmptyDirective, ...(ngDevMode ? [{ debugName: "emptyDirective" }] : /* istanbul ignore next */ []));
|
|
345
|
+
/** @internal */
|
|
346
|
+
pageSizeSelectorDirective = contentChild(PaginatorPageSizeSelectorDirective, ...(ngDevMode ? [{ debugName: "pageSizeSelectorDirective" }] : /* istanbul ignore next */ []));
|
|
347
|
+
// ── Injected services ──
|
|
348
|
+
_liveAnnouncer = inject(LiveAnnouncer);
|
|
349
|
+
_elementRef = inject((ElementRef));
|
|
350
|
+
// ── View children ──
|
|
351
|
+
/** @internal All nav-group focusables (first/prev/page/next/last) for `FocusKeyManager`. */
|
|
352
|
+
focusableItems = viewChildren(PaginatorFocusableDirective, ...(ngDevMode ? [{ debugName: "focusableItems" }] : /* istanbul ignore next */ []));
|
|
353
|
+
// ── IDs ──
|
|
354
|
+
_componentId = `tw-paginator-${nextPaginatorId++}`;
|
|
355
|
+
/** @internal */
|
|
356
|
+
pageSizeSelectId = `${this._componentId}-size`;
|
|
357
|
+
// ── Derived state ──
|
|
358
|
+
/** @internal Effective page size, coerced to at least 1. */
|
|
359
|
+
effectivePageSize = computed(() => Math.max(1, this.pageSize()), ...(ngDevMode ? [{ debugName: "effectivePageSize" }] : /* istanbul ignore next */ []));
|
|
360
|
+
/** @internal Total number of pages; always ≥ 1 to keep clamping math well-defined. */
|
|
361
|
+
totalPages = computed(() => {
|
|
362
|
+
const total = this.totalItems();
|
|
363
|
+
if (total <= 0)
|
|
364
|
+
return 1;
|
|
365
|
+
return Math.max(1, Math.ceil(total / this.effectivePageSize()));
|
|
366
|
+
}, ...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
|
|
367
|
+
/** @internal Current page clamped into `[1, totalPages]`. */
|
|
368
|
+
clampedPage = computed(() => Math.min(Math.max(1, this.page()), this.totalPages()), ...(ngDevMode ? [{ debugName: "clampedPage" }] : /* istanbul ignore next */ []));
|
|
369
|
+
/** @internal 1-based start item index on the current page. `0` when empty. */
|
|
370
|
+
startItem = computed(() => {
|
|
371
|
+
if (this.totalItems() <= 0)
|
|
372
|
+
return 0;
|
|
373
|
+
return (this.clampedPage() - 1) * this.effectivePageSize() + 1;
|
|
374
|
+
}, ...(ngDevMode ? [{ debugName: "startItem" }] : /* istanbul ignore next */ []));
|
|
375
|
+
/** @internal 1-based end item index on the current page. `0` when empty. */
|
|
376
|
+
endItem = computed(() => {
|
|
377
|
+
if (this.totalItems() <= 0)
|
|
378
|
+
return 0;
|
|
379
|
+
return Math.min(this.clampedPage() * this.effectivePageSize(), this.totalItems());
|
|
380
|
+
}, ...(ngDevMode ? [{ debugName: "endItem" }] : /* istanbul ignore next */ []));
|
|
381
|
+
/** @internal Visible page range for the numbered type. */
|
|
382
|
+
range = computed(() => buildPaginationRange(this.clampedPage(), this.totalPages(), this.siblingCount(), this.boundaryCount()), ...(ngDevMode ? [{ debugName: "range" }] : /* istanbul ignore next */ []));
|
|
383
|
+
/** @internal Merged labels. */
|
|
384
|
+
resolvedLabels = computed(() => ({
|
|
385
|
+
...DEFAULT_LABELS,
|
|
386
|
+
...this.labels(),
|
|
387
|
+
}), ...(ngDevMode ? [{ debugName: "resolvedLabels" }] : /* istanbul ignore next */ []));
|
|
388
|
+
/** @internal Resolved aria-label for the root. */
|
|
389
|
+
resolvedAriaLabel = computed(() => this.customAriaLabel() ?? this.resolvedLabels().ariaLabel, ...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
390
|
+
/** @internal `container-name` for the CSS container query (set only when responsive="auto"). */
|
|
391
|
+
containerName = computed(() => this.responsive() === 'auto' ? 'paginator' : null, ...(ngDevMode ? [{ debugName: "containerName" }] : /* istanbul ignore next */ []));
|
|
392
|
+
isEmpty = computed(() => this.totalItems() <= 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
|
|
393
|
+
isAtFirstPage = computed(() => this.clampedPage() <= 1, ...(ngDevMode ? [{ debugName: "isAtFirstPage" }] : /* istanbul ignore next */ []));
|
|
394
|
+
isAtLastPage = computed(() => this.clampedPage() >= this.totalPages(), ...(ngDevMode ? [{ debugName: "isAtLastPage" }] : /* istanbul ignore next */ []));
|
|
395
|
+
/** @internal Target page for the Previous button (clamped to `[1, totalPages]`). */
|
|
396
|
+
previousTarget = computed(() => Math.max(1, this.clampedPage() - 1), ...(ngDevMode ? [{ debugName: "previousTarget" }] : /* istanbul ignore next */ []));
|
|
397
|
+
/** @internal Target page for the Next button (clamped to `[1, totalPages]`). */
|
|
398
|
+
nextTarget = computed(() => Math.min(this.totalPages(), this.clampedPage() + 1), ...(ngDevMode ? [{ debugName: "nextTarget" }] : /* istanbul ignore next */ []));
|
|
399
|
+
/** @internal Whether the component should render at all. */
|
|
400
|
+
shouldRender = computed(() => {
|
|
401
|
+
if (this.isEmpty() && this.hideOnEmpty())
|
|
402
|
+
return false;
|
|
403
|
+
if (this.hideOnSinglePage() && this.totalPages() <= 1 && !this.isEmpty())
|
|
404
|
+
return false;
|
|
405
|
+
return true;
|
|
406
|
+
}, ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
|
|
407
|
+
// ── Variant class computation ──
|
|
408
|
+
_variantResult = computed(() => paginatorVariants({
|
|
409
|
+
size: this.size(),
|
|
410
|
+
layout: this.layout(),
|
|
411
|
+
type: this.type(),
|
|
412
|
+
responsive: this.responsive(),
|
|
413
|
+
disabled: this.disabled(),
|
|
414
|
+
}), ...(ngDevMode ? [{ debugName: "_variantResult" }] : /* istanbul ignore next */ []));
|
|
415
|
+
rootClasses = computed(() => this._variantResult().root(), ...(ngDevMode ? [{ debugName: "rootClasses" }] : /* istanbul ignore next */ []));
|
|
416
|
+
innerClasses = computed(() => this._variantResult().inner(), ...(ngDevMode ? [{ debugName: "innerClasses" }] : /* istanbul ignore next */ []));
|
|
417
|
+
pageSizeGroupClasses = computed(() => this._variantResult().pageSizeGroup(), ...(ngDevMode ? [{ debugName: "pageSizeGroupClasses" }] : /* istanbul ignore next */ []));
|
|
418
|
+
pageSizeLabelClasses = computed(() => this._variantResult().pageSizeLabel(), ...(ngDevMode ? [{ debugName: "pageSizeLabelClasses" }] : /* istanbul ignore next */ []));
|
|
419
|
+
pageSizeSelectClasses = computed(() => this._variantResult().pageSizeSelect(), ...(ngDevMode ? [{ debugName: "pageSizeSelectClasses" }] : /* istanbul ignore next */ []));
|
|
420
|
+
pageInfoClasses = computed(() => this._variantResult().pageInfo(), ...(ngDevMode ? [{ debugName: "pageInfoClasses" }] : /* istanbul ignore next */ []));
|
|
421
|
+
navGroupClasses = computed(() => this._variantResult().navGroup(), ...(ngDevMode ? [{ debugName: "navGroupClasses" }] : /* istanbul ignore next */ []));
|
|
422
|
+
pageListClasses = computed(() => this._variantResult().pageList(), ...(ngDevMode ? [{ debugName: "pageListClasses" }] : /* istanbul ignore next */ []));
|
|
423
|
+
navButtonClasses = computed(() => this._variantResult().navButton(), ...(ngDevMode ? [{ debugName: "navButtonClasses" }] : /* istanbul ignore next */ []));
|
|
424
|
+
ellipsisClasses = computed(() => this._variantResult().ellipsis(), ...(ngDevMode ? [{ debugName: "ellipsisClasses" }] : /* istanbul ignore next */ []));
|
|
425
|
+
emptyStateClasses = computed(() => this._variantResult().emptyState(), ...(ngDevMode ? [{ debugName: "emptyStateClasses" }] : /* istanbul ignore next */ []));
|
|
426
|
+
iconClasses = computed(() => this._variantResult().icon(), ...(ngDevMode ? [{ debugName: "iconClasses" }] : /* istanbul ignore next */ []));
|
|
427
|
+
/** @internal Applies active-color classes to the current page button. */
|
|
428
|
+
pageButtonClasses(active) {
|
|
429
|
+
const base = this._variantResult().pageButton();
|
|
430
|
+
if (!active)
|
|
431
|
+
return base;
|
|
432
|
+
const color = this.color();
|
|
433
|
+
return `${base} ${PAGE_BUTTON_ACTIVE_BG[color]} ${PAGE_BUTTON_ACTIVE_FG[color]}`;
|
|
434
|
+
}
|
|
435
|
+
// ── Projected template lookup ──
|
|
436
|
+
/** @internal */
|
|
437
|
+
labelTemplateFor(slot) {
|
|
438
|
+
const match = this.labelDirectives().find((d) => d.slot() === slot);
|
|
439
|
+
return match ? match.templateRef : null;
|
|
440
|
+
}
|
|
441
|
+
/** @internal Template-context wrapper so projected templates receive `$implicit: TwPaginatorLabelContext`. */
|
|
442
|
+
labelContext = computed(() => ({
|
|
443
|
+
$implicit: {
|
|
444
|
+
page: this.clampedPage(),
|
|
445
|
+
totalPages: this.totalPages(),
|
|
446
|
+
start: this.startItem(),
|
|
447
|
+
end: this.endItem(),
|
|
448
|
+
totalItems: this.totalItems(),
|
|
449
|
+
pageSize: this.effectivePageSize(),
|
|
450
|
+
disabled: this.disabled(),
|
|
451
|
+
},
|
|
452
|
+
}), ...(ngDevMode ? [{ debugName: "labelContext" }] : /* istanbul ignore next */ []));
|
|
453
|
+
/** @internal */
|
|
454
|
+
pageSizeContext = computed(() => ({
|
|
455
|
+
$implicit: {
|
|
456
|
+
pageSize: this.effectivePageSize(),
|
|
457
|
+
options: this.pageSizeOptions(),
|
|
458
|
+
setPageSize: (n) => this._setPageSize(n),
|
|
459
|
+
},
|
|
460
|
+
}), ...(ngDevMode ? [{ debugName: "pageSizeContext" }] : /* istanbul ignore next */ []));
|
|
461
|
+
// ── Rendered strings ──
|
|
462
|
+
/** @internal Default page-info string (type-aware). */
|
|
463
|
+
formattedPageInfo = computed(() => {
|
|
464
|
+
const labels = this.resolvedLabels();
|
|
465
|
+
if (this.isEmpty())
|
|
466
|
+
return labels.empty;
|
|
467
|
+
if (this.type() === 'basic') {
|
|
468
|
+
return formatLabel(labels.pageRange, {
|
|
469
|
+
start: this.startItem(),
|
|
470
|
+
end: this.endItem(),
|
|
471
|
+
total: this.totalItems(),
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
return `${labels.pageInfo} ${this.clampedPage()}${labels.pageInfoSeparator}${this.totalPages()}`;
|
|
475
|
+
}, ...(ngDevMode ? [{ debugName: "formattedPageInfo" }] : /* istanbul ignore next */ []));
|
|
476
|
+
/** @internal ARIA label for a numbered page button. */
|
|
477
|
+
formatPageButtonLabel(page) {
|
|
478
|
+
return formatLabel(this.resolvedLabels().pageButtonAriaLabel, { page });
|
|
479
|
+
}
|
|
480
|
+
/** @internal ARIA label for the current page button. */
|
|
481
|
+
formatCurrentPageLabel(page) {
|
|
482
|
+
return formatLabel(this.resolvedLabels().currentPageAriaLabel, { page });
|
|
483
|
+
}
|
|
484
|
+
/** @internal Returns the anchor href for a page when `linkFactory` is set and the page is navigable. */
|
|
485
|
+
hrefFor(page) {
|
|
486
|
+
const lf = this.linkFactory();
|
|
487
|
+
if (!lf)
|
|
488
|
+
return null;
|
|
489
|
+
if (this.disabled())
|
|
490
|
+
return null;
|
|
491
|
+
return lf(Math.min(Math.max(1, page), this.totalPages()));
|
|
492
|
+
}
|
|
493
|
+
// ── State bookkeeping (for emission) ──
|
|
494
|
+
_previousPage = this.page();
|
|
495
|
+
_previousPageSize = this.pageSize();
|
|
496
|
+
_pendingSource = null;
|
|
497
|
+
_initialized = false;
|
|
498
|
+
// ── Keyboard navigation (CDK FocusKeyManager) ──
|
|
499
|
+
/** @internal */
|
|
500
|
+
_keyManager = null;
|
|
501
|
+
constructor() {
|
|
502
|
+
afterNextRender(() => {
|
|
503
|
+
this._initialized = true;
|
|
504
|
+
if (isDevMode()) {
|
|
505
|
+
this._runDevWarnings();
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
// Emit pageChange whenever page or pageSize changes (after initial render).
|
|
509
|
+
// When an out-of-range page is detected, clamp and emit with source 'programmatic'.
|
|
510
|
+
effect(() => {
|
|
511
|
+
const newPage = this.page();
|
|
512
|
+
const newSize = this.pageSize();
|
|
513
|
+
const tp = this.totalPages();
|
|
514
|
+
untracked(() => {
|
|
515
|
+
const oldPage = this._previousPage;
|
|
516
|
+
const oldSize = this._previousPageSize;
|
|
517
|
+
// Clamp out-of-bounds page — do this first, then allow a follow-up tick to emit.
|
|
518
|
+
const clamped = Math.min(Math.max(1, newPage), tp);
|
|
519
|
+
if (clamped !== newPage) {
|
|
520
|
+
// Set pending source only if nothing else is pending (preserve originator).
|
|
521
|
+
if (this._pendingSource === null)
|
|
522
|
+
this._pendingSource = 'programmatic';
|
|
523
|
+
this.page.set(clamped);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (newPage === oldPage && newSize === oldSize) {
|
|
527
|
+
// No effective change (first run after init).
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
this._previousPage = newPage;
|
|
531
|
+
this._previousPageSize = newSize;
|
|
532
|
+
if (!this._initialized)
|
|
533
|
+
return;
|
|
534
|
+
const source = this._pendingSource ?? 'programmatic';
|
|
535
|
+
this._pendingSource = null;
|
|
536
|
+
this._emitChange(newPage, newSize, oldPage, oldSize, source);
|
|
537
|
+
this._announce(newPage);
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
// Rebuild the FocusKeyManager whenever the focusable set changes (e.g. the
|
|
541
|
+
// numbered page list re-renders when total or current page shifts). No
|
|
542
|
+
// `.withWrap()` — paginators should NOT loop from page 1 ArrowLeft to the
|
|
543
|
+
// last page; that would be disorienting. Mirrors `accordion.ts` and the S12
|
|
544
|
+
// tabs/tab-nav migration. The `disabled` getter on `PaginatorFocusableDirective`
|
|
545
|
+
// resolves the `isDisabled()` signal so the manager skips disabled controls.
|
|
546
|
+
// `onCleanup` fires both on rebuild AND on component destroy, so no separate
|
|
547
|
+
// `_destroyRef.onDestroy` is needed.
|
|
548
|
+
effect((onCleanup) => {
|
|
549
|
+
const items = this.focusableItems();
|
|
550
|
+
if (items.length === 0) {
|
|
551
|
+
this._keyManager = null;
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const manager = new FocusKeyManager(items)
|
|
555
|
+
.withHorizontalOrientation('ltr')
|
|
556
|
+
.withHomeAndEnd();
|
|
557
|
+
this._keyManager = manager;
|
|
558
|
+
onCleanup(() => {
|
|
559
|
+
manager.destroy();
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
// ── User-facing action methods ──
|
|
564
|
+
/** @internal Navigate to a specific page (click/keyboard handler). */
|
|
565
|
+
goTo(target, source) {
|
|
566
|
+
if (this.disabled())
|
|
567
|
+
return;
|
|
568
|
+
const tp = this.totalPages();
|
|
569
|
+
const next = Math.min(Math.max(1, target), tp);
|
|
570
|
+
if (next === this.clampedPage())
|
|
571
|
+
return;
|
|
572
|
+
this._pendingSource = source;
|
|
573
|
+
this.page.set(next);
|
|
574
|
+
}
|
|
575
|
+
/** @internal Click handler for anchor-link mode. Suppresses navigation for disabled targets. */
|
|
576
|
+
onLinkClick(event, target, disabled, source) {
|
|
577
|
+
if (disabled || this.disabled()) {
|
|
578
|
+
event.preventDefault();
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
// Let the browser follow the anchor; still emit a pageChange.
|
|
582
|
+
this.goTo(target, source);
|
|
583
|
+
}
|
|
584
|
+
/** @internal Handler for the default page-size `<select>`. */
|
|
585
|
+
onPageSizeSelectChange(event) {
|
|
586
|
+
const target = event.target;
|
|
587
|
+
const parsed = Number(target.value);
|
|
588
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
589
|
+
this._setPageSize(parsed);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
_setPageSize(size) {
|
|
593
|
+
if (this.disabled())
|
|
594
|
+
return;
|
|
595
|
+
const newSize = Math.max(1, Math.floor(size));
|
|
596
|
+
const oldPage = this.clampedPage();
|
|
597
|
+
const oldSize = this.effectivePageSize();
|
|
598
|
+
if (newSize === oldSize)
|
|
599
|
+
return;
|
|
600
|
+
const firstItem = (oldPage - 1) * oldSize + 1;
|
|
601
|
+
const total = this.totalItems();
|
|
602
|
+
const newTp = total <= 0 ? 1 : Math.max(1, Math.ceil(total / newSize));
|
|
603
|
+
const newPage = Math.min(Math.max(1, Math.ceil(firstItem / newSize)), newTp);
|
|
604
|
+
this._pendingSource = 'pageSizeChange';
|
|
605
|
+
this.pageSize.set(newSize);
|
|
606
|
+
if (newPage !== oldPage)
|
|
607
|
+
this.page.set(newPage);
|
|
608
|
+
// If only pageSize changed but page value is unchanged, the effect still fires
|
|
609
|
+
// because pageSize is tracked.
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* @internal Keyboard navigation inside the nav group. Delegates ArrowLeft /
|
|
613
|
+
* ArrowRight / Home / End to CDK `FocusKeyManager`. The manager skips
|
|
614
|
+
* disabled controls automatically via the `disabled` getter on
|
|
615
|
+
* `PaginatorFocusableDirective`.
|
|
616
|
+
*/
|
|
617
|
+
onKeydown(event) {
|
|
618
|
+
if (!this._keyManager)
|
|
619
|
+
return;
|
|
620
|
+
const items = this.focusableItems();
|
|
621
|
+
if (items.length === 0)
|
|
622
|
+
return;
|
|
623
|
+
// Sync the manager's active index to whichever focusable currently owns DOM
|
|
624
|
+
// focus, so arrow keys move from the user's perceived position rather than
|
|
625
|
+
// the stale post-render default of -1.
|
|
626
|
+
const focusedIdx = items.findIndex((item) => item.elementRef.nativeElement === event.target);
|
|
627
|
+
if (focusedIdx >= 0 && focusedIdx !== this._keyManager.activeItemIndex) {
|
|
628
|
+
this._keyManager.setActiveItem(focusedIdx);
|
|
629
|
+
}
|
|
630
|
+
this._keyManager.onKeydown(event);
|
|
631
|
+
}
|
|
632
|
+
// ── Emission + announcement ──
|
|
633
|
+
_emitChange(page, pageSize, previousPage, previousPageSize, source) {
|
|
634
|
+
const total = this.totalItems();
|
|
635
|
+
const effSize = Math.max(1, pageSize);
|
|
636
|
+
const tp = total <= 0 ? 1 : Math.max(1, Math.ceil(total / effSize));
|
|
637
|
+
const start = total <= 0 ? 0 : (page - 1) * effSize + 1;
|
|
638
|
+
const end = total <= 0 ? 0 : Math.min(page * effSize, total);
|
|
639
|
+
this.paginated.emit({
|
|
640
|
+
page,
|
|
641
|
+
pageSize: effSize,
|
|
642
|
+
previousPage,
|
|
643
|
+
previousPageSize,
|
|
644
|
+
totalItems: total,
|
|
645
|
+
totalPages: tp,
|
|
646
|
+
start,
|
|
647
|
+
end,
|
|
648
|
+
source,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
_announce(page) {
|
|
652
|
+
const msg = formatLabel(this.resolvedLabels().announcement, {
|
|
653
|
+
page,
|
|
654
|
+
totalPages: this.totalPages(),
|
|
655
|
+
start: this.startItem(),
|
|
656
|
+
end: this.endItem(),
|
|
657
|
+
total: this.totalItems(),
|
|
658
|
+
});
|
|
659
|
+
this._liveAnnouncer.announce(msg, 'polite');
|
|
660
|
+
}
|
|
661
|
+
// ── Dev warnings ──
|
|
662
|
+
_runDevWarnings() {
|
|
663
|
+
if (this.totalItems() < 0) {
|
|
664
|
+
console.warn('[tw-paginator] totalItems is negative — coercing to 0.');
|
|
665
|
+
}
|
|
666
|
+
if (this.pageSize() < 1) {
|
|
667
|
+
console.warn('[tw-paginator] pageSize is less than 1 — coercing to 1.');
|
|
668
|
+
}
|
|
669
|
+
if (this.showPageSizeSelector() && this.pageSizeOptions().length === 0) {
|
|
670
|
+
console.warn('[tw-paginator] showPageSizeSelector is true but pageSizeOptions is empty — selector will not render.');
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
674
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PaginatorComponent, isStandalone: true, selector: "tw-paginator", inputs: { totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, siblingCount: { classPropertyName: "siblingCount", publicName: "siblingCount", isSignal: true, isRequired: false, transformFunction: null }, boundaryCount: { classPropertyName: "boundaryCount", publicName: "boundaryCount", isSignal: true, isRequired: false, transformFunction: null }, showFirstLastButtons: { classPropertyName: "showFirstLastButtons", publicName: "showFirstLastButtons", isSignal: true, isRequired: false, transformFunction: null }, showPageSizeSelector: { classPropertyName: "showPageSizeSelector", publicName: "showPageSizeSelector", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, showPageInfo: { classPropertyName: "showPageInfo", publicName: "showPageInfo", isSignal: true, isRequired: false, transformFunction: null }, hideOnEmpty: { classPropertyName: "hideOnEmpty", publicName: "hideOnEmpty", isSignal: true, isRequired: false, transformFunction: null }, hideOnSinglePage: { classPropertyName: "hideOnSinglePage", publicName: "hideOnSinglePage", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, linkFactory: { classPropertyName: "linkFactory", publicName: "linkFactory", isSignal: true, isRequired: false, transformFunction: null }, customAriaLabel: { classPropertyName: "customAriaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageSize: "pageSizeChange", page: "pageChange", paginated: "paginated" }, host: { properties: { "attr.role": "shouldRender() ? \"navigation\" : null", "class": "rootClasses()", "attr.aria-label": "shouldRender() ? resolvedAriaLabel() : null", "style.container-name": "containerName()" } }, queries: [{ propertyName: "labelDirectives", predicate: PaginatorLabelDirective, isSignal: true }, { propertyName: "emptyDirective", first: true, predicate: PaginatorEmptyDirective, descendants: true, isSignal: true }, { propertyName: "pageSizeSelectorDirective", first: true, predicate: PaginatorPageSizeSelectorDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "focusableItems", predicate: PaginatorFocusableDirective, descendants: true, isSignal: true }], exportAs: ["twPaginator"], ngImport: i0, template: "@if (shouldRender()) {\n <div [class]=\"innerClasses()\">\n @if (isEmpty()) {\n <!-- Empty state -->\n @if (emptyDirective(); as emptyDir) {\n <ng-container\n [ngTemplateOutlet]=\"emptyDir.templateRef\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <span [class]=\"emptyStateClasses()\">{{ resolvedLabels().empty }}</span>\n }\n } @else {\n <!-- Page-size selector region -->\n @if (showPageSizeSelector() && pageSizeOptions().length > 0) {\n <div [class]=\"pageSizeGroupClasses()\">\n <label\n [attr.for]=\"pageSizeSelectId\"\n [class]=\"pageSizeLabelClasses()\"\n >\n @if (labelTemplateFor('pageSizeLabel'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n {{ resolvedLabels().pageSizeLabel }}\n }\n </label>\n\n @if (pageSizeSelectorDirective(); as dir) {\n <ng-container\n [ngTemplateOutlet]=\"dir.templateRef\"\n [ngTemplateOutletContext]=\"pageSizeContext()\"\n />\n } @else {\n <select\n [id]=\"pageSizeSelectId\"\n [class]=\"pageSizeSelectClasses()\"\n [disabled]=\"disabled()\"\n [value]=\"effectivePageSize()\"\n (change)=\"onPageSizeSelectChange($event)\"\n >\n @for (opt of pageSizeOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n }\n </div>\n }\n\n <!-- Page info region -->\n @if (showPageInfo()) {\n <div [class]=\"pageInfoClasses()\">\n @if (labelTemplateFor('pageInfo'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n {{ formattedPageInfo() }}\n }\n </div>\n }\n\n <!-- Nav buttons region -->\n <div\n [class]=\"navGroupClasses()\"\n (keydown)=\"onKeydown($event)\"\n >\n <!-- First -->\n @if (showFirstLastButtons()) {\n @let firstDisabled = disabled() || isAtFirstPage();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"firstDisabled\"\n data-tw-paginator-nav=\"first\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"firstDisabled ? null : hrefFor(1)\"\n [attr.aria-disabled]=\"firstDisabled ? 'true' : null\"\n [attr.tabindex]=\"firstDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().first\"\n (click)=\"onLinkClick($event, 1, firstDisabled, 'click')\"\n >\n @if (labelTemplateFor('first'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M10.78 4.22a.75.75 0 0 1 0 1.06L6.06 10l4.72 4.72a.75.75 0 0 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Zm6 0a.75.75 0 0 1 0 1.06L12.06 10l4.72 4.72a.75.75 0 1 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"firstDisabled\"\n data-tw-paginator-nav=\"first\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"firstDisabled\"\n [attr.aria-label]=\"resolvedLabels().first\"\n (click)=\"goTo(1, 'click')\"\n >\n @if (labelTemplateFor('first'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M10.78 4.22a.75.75 0 0 1 0 1.06L6.06 10l4.72 4.72a.75.75 0 0 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Zm6 0a.75.75 0 0 1 0 1.06L12.06 10l4.72 4.72a.75.75 0 1 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n }\n\n <!-- Previous -->\n @let prevDisabled = disabled() || isAtFirstPage();\n @let prevTarget = previousTarget();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"prevDisabled\"\n data-tw-paginator-nav=\"prev\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"prevDisabled ? null : hrefFor(prevTarget)\"\n [attr.aria-disabled]=\"prevDisabled ? 'true' : null\"\n [attr.tabindex]=\"prevDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().previous\"\n (click)=\"onLinkClick($event, prevTarget, prevDisabled, 'click')\"\n >\n @if (labelTemplateFor('previous'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M12.78 5.22a.75.75 0 0 1 0 1.06L9.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"prevDisabled\"\n data-tw-paginator-nav=\"prev\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"prevDisabled\"\n [attr.aria-label]=\"resolvedLabels().previous\"\n (click)=\"goTo(prevTarget, 'click')\"\n >\n @if (labelTemplateFor('previous'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M12.78 5.22a.75.75 0 0 1 0 1.06L9.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n\n <!-- Numbered page list -->\n @if (type() === 'numbered') {\n <div [class]=\"pageListClasses()\">\n @for (item of range(); track $index) {\n @if (item === 'ellipsis-left' || item === 'ellipsis-right') {\n <span\n [class]=\"ellipsisClasses()\"\n [attr.aria-label]=\"resolvedLabels().ellipsis\"\n >\n <span aria-hidden=\"true\">…</span>\n </span>\n } @else {\n @let pageNumber = item;\n @let isActive = pageNumber === clampedPage();\n @let pageDisabled = disabled();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"pageDisabled\"\n data-tw-paginator-nav=\"page\"\n [class]=\"pageButtonClasses(isActive)\"\n [attr.href]=\"pageDisabled ? null : hrefFor(pageNumber)\"\n [attr.aria-disabled]=\"pageDisabled ? 'true' : null\"\n [attr.tabindex]=\"pageDisabled ? -1 : 0\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"\n isActive\n ? formatCurrentPageLabel(pageNumber)\n : formatPageButtonLabel(pageNumber)\n \"\n (click)=\"onLinkClick($event, pageNumber, pageDisabled, 'click')\"\n >{{ pageNumber }}</a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"pageDisabled\"\n data-tw-paginator-nav=\"page\"\n [class]=\"pageButtonClasses(isActive)\"\n [disabled]=\"pageDisabled\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"\n isActive\n ? formatCurrentPageLabel(pageNumber)\n : formatPageButtonLabel(pageNumber)\n \"\n (click)=\"goTo(pageNumber, 'click')\"\n >{{ pageNumber }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Next -->\n @let nextDisabled = disabled() || isAtLastPage();\n @let nextTargetPage = nextTarget();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"nextDisabled\"\n data-tw-paginator-nav=\"next\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"nextDisabled ? null : hrefFor(nextTargetPage)\"\n [attr.aria-disabled]=\"nextDisabled ? 'true' : null\"\n [attr.tabindex]=\"nextDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().next\"\n (click)=\"onLinkClick($event, nextTargetPage, nextDisabled, 'click')\"\n >\n @if (labelTemplateFor('next'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M7.22 14.78a.75.75 0 0 1 0-1.06L10.94 10 7.22 6.28a.75.75 0 0 1 1.06-1.06l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"nextDisabled\"\n data-tw-paginator-nav=\"next\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"nextDisabled\"\n [attr.aria-label]=\"resolvedLabels().next\"\n (click)=\"goTo(nextTargetPage, 'click')\"\n >\n @if (labelTemplateFor('next'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M7.22 14.78a.75.75 0 0 1 0-1.06L10.94 10 7.22 6.28a.75.75 0 0 1 1.06-1.06l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n\n <!-- Last -->\n @if (showFirstLastButtons()) {\n @let lastDisabled = disabled() || isAtLastPage();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"lastDisabled\"\n data-tw-paginator-nav=\"last\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"lastDisabled ? null : hrefFor(totalPages())\"\n [attr.aria-disabled]=\"lastDisabled ? 'true' : null\"\n [attr.tabindex]=\"lastDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().last\"\n (click)=\"onLinkClick($event, totalPages(), lastDisabled, 'click')\"\n >\n @if (labelTemplateFor('last'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M3.22 4.22a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 0 1-1.06-1.06L7.94 10 3.22 5.28a.75.75 0 0 1 0-1.06Zm6 0a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 1 1-1.06-1.06L13.94 10 9.22 5.28a.75.75 0 0 1 0-1.06Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"lastDisabled\"\n data-tw-paginator-nav=\"last\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"lastDisabled\"\n [attr.aria-label]=\"resolvedLabels().last\"\n (click)=\"goTo(totalPages(), 'click')\"\n >\n @if (labelTemplateFor('last'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M3.22 4.22a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 0 1-1.06-1.06L7.94 10 3.22 5.28a.75.75 0 0 1 0-1.06Zm6 0a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 1 1-1.06-1.06L13.94 10 9.22 5.28a.75.75 0 0 1 0-1.06Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n }\n </div>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: PaginatorFocusableDirective, selector: "[data-tw-paginator-focusable]", inputs: ["isDisabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
675
|
+
}
|
|
676
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PaginatorComponent, decorators: [{
|
|
677
|
+
type: Component,
|
|
678
|
+
args: [{ selector: 'tw-paginator', exportAs: 'twPaginator', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, PaginatorFocusableDirective], host: {
|
|
679
|
+
'[attr.role]': 'shouldRender() ? "navigation" : null',
|
|
680
|
+
'[class]': 'rootClasses()',
|
|
681
|
+
'[attr.aria-label]': 'shouldRender() ? resolvedAriaLabel() : null',
|
|
682
|
+
'[style.container-name]': 'containerName()',
|
|
683
|
+
}, template: "@if (shouldRender()) {\n <div [class]=\"innerClasses()\">\n @if (isEmpty()) {\n <!-- Empty state -->\n @if (emptyDirective(); as emptyDir) {\n <ng-container\n [ngTemplateOutlet]=\"emptyDir.templateRef\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <span [class]=\"emptyStateClasses()\">{{ resolvedLabels().empty }}</span>\n }\n } @else {\n <!-- Page-size selector region -->\n @if (showPageSizeSelector() && pageSizeOptions().length > 0) {\n <div [class]=\"pageSizeGroupClasses()\">\n <label\n [attr.for]=\"pageSizeSelectId\"\n [class]=\"pageSizeLabelClasses()\"\n >\n @if (labelTemplateFor('pageSizeLabel'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n {{ resolvedLabels().pageSizeLabel }}\n }\n </label>\n\n @if (pageSizeSelectorDirective(); as dir) {\n <ng-container\n [ngTemplateOutlet]=\"dir.templateRef\"\n [ngTemplateOutletContext]=\"pageSizeContext()\"\n />\n } @else {\n <select\n [id]=\"pageSizeSelectId\"\n [class]=\"pageSizeSelectClasses()\"\n [disabled]=\"disabled()\"\n [value]=\"effectivePageSize()\"\n (change)=\"onPageSizeSelectChange($event)\"\n >\n @for (opt of pageSizeOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n }\n </div>\n }\n\n <!-- Page info region -->\n @if (showPageInfo()) {\n <div [class]=\"pageInfoClasses()\">\n @if (labelTemplateFor('pageInfo'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n {{ formattedPageInfo() }}\n }\n </div>\n }\n\n <!-- Nav buttons region -->\n <div\n [class]=\"navGroupClasses()\"\n (keydown)=\"onKeydown($event)\"\n >\n <!-- First -->\n @if (showFirstLastButtons()) {\n @let firstDisabled = disabled() || isAtFirstPage();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"firstDisabled\"\n data-tw-paginator-nav=\"first\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"firstDisabled ? null : hrefFor(1)\"\n [attr.aria-disabled]=\"firstDisabled ? 'true' : null\"\n [attr.tabindex]=\"firstDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().first\"\n (click)=\"onLinkClick($event, 1, firstDisabled, 'click')\"\n >\n @if (labelTemplateFor('first'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M10.78 4.22a.75.75 0 0 1 0 1.06L6.06 10l4.72 4.72a.75.75 0 0 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Zm6 0a.75.75 0 0 1 0 1.06L12.06 10l4.72 4.72a.75.75 0 1 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"firstDisabled\"\n data-tw-paginator-nav=\"first\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"firstDisabled\"\n [attr.aria-label]=\"resolvedLabels().first\"\n (click)=\"goTo(1, 'click')\"\n >\n @if (labelTemplateFor('first'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M10.78 4.22a.75.75 0 0 1 0 1.06L6.06 10l4.72 4.72a.75.75 0 0 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Zm6 0a.75.75 0 0 1 0 1.06L12.06 10l4.72 4.72a.75.75 0 1 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n }\n\n <!-- Previous -->\n @let prevDisabled = disabled() || isAtFirstPage();\n @let prevTarget = previousTarget();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"prevDisabled\"\n data-tw-paginator-nav=\"prev\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"prevDisabled ? null : hrefFor(prevTarget)\"\n [attr.aria-disabled]=\"prevDisabled ? 'true' : null\"\n [attr.tabindex]=\"prevDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().previous\"\n (click)=\"onLinkClick($event, prevTarget, prevDisabled, 'click')\"\n >\n @if (labelTemplateFor('previous'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M12.78 5.22a.75.75 0 0 1 0 1.06L9.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"prevDisabled\"\n data-tw-paginator-nav=\"prev\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"prevDisabled\"\n [attr.aria-label]=\"resolvedLabels().previous\"\n (click)=\"goTo(prevTarget, 'click')\"\n >\n @if (labelTemplateFor('previous'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M12.78 5.22a.75.75 0 0 1 0 1.06L9.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n\n <!-- Numbered page list -->\n @if (type() === 'numbered') {\n <div [class]=\"pageListClasses()\">\n @for (item of range(); track $index) {\n @if (item === 'ellipsis-left' || item === 'ellipsis-right') {\n <span\n [class]=\"ellipsisClasses()\"\n [attr.aria-label]=\"resolvedLabels().ellipsis\"\n >\n <span aria-hidden=\"true\">…</span>\n </span>\n } @else {\n @let pageNumber = item;\n @let isActive = pageNumber === clampedPage();\n @let pageDisabled = disabled();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"pageDisabled\"\n data-tw-paginator-nav=\"page\"\n [class]=\"pageButtonClasses(isActive)\"\n [attr.href]=\"pageDisabled ? null : hrefFor(pageNumber)\"\n [attr.aria-disabled]=\"pageDisabled ? 'true' : null\"\n [attr.tabindex]=\"pageDisabled ? -1 : 0\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"\n isActive\n ? formatCurrentPageLabel(pageNumber)\n : formatPageButtonLabel(pageNumber)\n \"\n (click)=\"onLinkClick($event, pageNumber, pageDisabled, 'click')\"\n >{{ pageNumber }}</a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"pageDisabled\"\n data-tw-paginator-nav=\"page\"\n [class]=\"pageButtonClasses(isActive)\"\n [disabled]=\"pageDisabled\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"\n isActive\n ? formatCurrentPageLabel(pageNumber)\n : formatPageButtonLabel(pageNumber)\n \"\n (click)=\"goTo(pageNumber, 'click')\"\n >{{ pageNumber }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Next -->\n @let nextDisabled = disabled() || isAtLastPage();\n @let nextTargetPage = nextTarget();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"nextDisabled\"\n data-tw-paginator-nav=\"next\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"nextDisabled ? null : hrefFor(nextTargetPage)\"\n [attr.aria-disabled]=\"nextDisabled ? 'true' : null\"\n [attr.tabindex]=\"nextDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().next\"\n (click)=\"onLinkClick($event, nextTargetPage, nextDisabled, 'click')\"\n >\n @if (labelTemplateFor('next'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M7.22 14.78a.75.75 0 0 1 0-1.06L10.94 10 7.22 6.28a.75.75 0 0 1 1.06-1.06l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"nextDisabled\"\n data-tw-paginator-nav=\"next\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"nextDisabled\"\n [attr.aria-label]=\"resolvedLabels().next\"\n (click)=\"goTo(nextTargetPage, 'click')\"\n >\n @if (labelTemplateFor('next'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M7.22 14.78a.75.75 0 0 1 0-1.06L10.94 10 7.22 6.28a.75.75 0 0 1 1.06-1.06l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n\n <!-- Last -->\n @if (showFirstLastButtons()) {\n @let lastDisabled = disabled() || isAtLastPage();\n @if (linkFactory()) {\n <a\n data-tw-paginator-focusable\n [isDisabled]=\"lastDisabled\"\n data-tw-paginator-nav=\"last\"\n [class]=\"navButtonClasses()\"\n [attr.href]=\"lastDisabled ? null : hrefFor(totalPages())\"\n [attr.aria-disabled]=\"lastDisabled ? 'true' : null\"\n [attr.tabindex]=\"lastDisabled ? -1 : 0\"\n [attr.aria-label]=\"resolvedLabels().last\"\n (click)=\"onLinkClick($event, totalPages(), lastDisabled, 'click')\"\n >\n @if (labelTemplateFor('last'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M3.22 4.22a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 0 1-1.06-1.06L7.94 10 3.22 5.28a.75.75 0 0 1 0-1.06Zm6 0a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 1 1-1.06-1.06L13.94 10 9.22 5.28a.75.75 0 0 1 0-1.06Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </a>\n } @else {\n <button\n type=\"button\"\n data-tw-paginator-focusable\n [isDisabled]=\"lastDisabled\"\n data-tw-paginator-nav=\"last\"\n [class]=\"navButtonClasses()\"\n [disabled]=\"lastDisabled\"\n [attr.aria-label]=\"resolvedLabels().last\"\n (click)=\"goTo(totalPages(), 'click')\"\n >\n @if (labelTemplateFor('last'); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"labelContext()\"\n />\n } @else {\n <svg\n [class]=\"iconClasses()\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n fill-rule=\"evenodd\"\n d=\"M3.22 4.22a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 0 1-1.06-1.06L7.94 10 3.22 5.28a.75.75 0 0 1 0-1.06Zm6 0a.75.75 0 0 1 1.06 0l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 1 1-1.06-1.06L13.94 10 9.22 5.28a.75.75 0 0 1 0-1.06Z\"\n clip-rule=\"evenodd\"\n />\n </svg>\n }\n </button>\n }\n }\n </div>\n }\n </div>\n}\n" }]
|
|
684
|
+
}], ctorParameters: () => [], propDecorators: { totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], siblingCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "siblingCount", required: false }] }], boundaryCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundaryCount", required: false }] }], showFirstLastButtons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFirstLastButtons", required: false }] }], showPageSizeSelector: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPageSizeSelector", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], showPageInfo: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPageInfo", required: false }] }], hideOnEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideOnEmpty", required: false }] }], hideOnSinglePage: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideOnSinglePage", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], linkFactory: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkFactory", required: false }] }], customAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], paginated: [{ type: i0.Output, args: ["paginated"] }], labelDirectives: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => PaginatorLabelDirective), { isSignal: true }] }], emptyDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => PaginatorEmptyDirective), { isSignal: true }] }], pageSizeSelectorDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => PaginatorPageSizeSelectorDirective), { isSignal: true }] }], focusableItems: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => PaginatorFocusableDirective), { isSignal: true }] }] } });
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Generated bundle index. Do not edit.
|
|
688
|
+
*/
|
|
689
|
+
|
|
690
|
+
export { PaginatorComponent, PaginatorEmptyDirective, PaginatorLabelDirective, PaginatorPageSizeSelectorDirective };
|
|
691
|
+
//# sourceMappingURL=cdevhub-ngx-tw-paginator.mjs.map
|