ngwr 7.2.0 → 7.3.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.
Files changed (54) hide show
  1. package/README.md +3 -3
  2. package/carousel/styles/_index.scss +2 -0
  3. package/color-picker/styles/_index.scss +7 -0
  4. package/density/styles/_index.scss +10 -0
  5. package/drawer/styles/_index.scss +2 -0
  6. package/fesm2022/ngwr-carousel.mjs +45 -4
  7. package/fesm2022/ngwr-carousel.mjs.map +1 -1
  8. package/fesm2022/ngwr-context-menu.mjs +77 -5
  9. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  10. package/fesm2022/ngwr-density.mjs +3 -3
  11. package/fesm2022/ngwr-density.mjs.map +1 -1
  12. package/fesm2022/ngwr-drag-drop.mjs +13 -3
  13. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  14. package/fesm2022/ngwr-drawer.mjs +66 -5
  15. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  16. package/fesm2022/ngwr-lightbox.mjs +45 -2
  17. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  18. package/fesm2022/ngwr-pagination.mjs +11 -3
  19. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  20. package/fesm2022/ngwr-rotating-text.mjs +15 -4
  21. package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
  22. package/fesm2022/ngwr-table.mjs +9 -3
  23. package/fesm2022/ngwr-table.mjs.map +1 -1
  24. package/fesm2022/ngwr-tabs.mjs +38 -6
  25. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  26. package/fesm2022/ngwr-toast.mjs +54 -2
  27. package/fesm2022/ngwr-toast.mjs.map +1 -1
  28. package/fesm2022/ngwr-toolbar.mjs +11 -4
  29. package/fesm2022/ngwr-toolbar.mjs.map +1 -1
  30. package/fesm2022/ngwr-version.mjs +1 -1
  31. package/fesm2022/ngwr-version.mjs.map +1 -1
  32. package/lightbox/styles/_index.scss +2 -0
  33. package/llms-full.txt +673 -0
  34. package/llms.txt +106 -0
  35. package/package.json +1 -1
  36. package/pagination/styles/_index.scss +34 -0
  37. package/slider/styles/_index.scss +3 -0
  38. package/splitter/styles/_index.scss +3 -0
  39. package/table/styles/_index.scss +61 -0
  40. package/tabs/styles/_index.scss +24 -0
  41. package/toast/styles/_index.scss +2 -0
  42. package/toolbar/styles/_index.scss +29 -0
  43. package/types/ngwr-carousel.d.ts +8 -1
  44. package/types/ngwr-context-menu.d.ts +25 -3
  45. package/types/ngwr-density.d.ts +2 -2
  46. package/types/ngwr-drag-drop.d.ts +13 -1
  47. package/types/ngwr-drawer.d.ts +11 -3
  48. package/types/ngwr-lightbox.d.ts +5 -0
  49. package/types/ngwr-pagination.d.ts +7 -1
  50. package/types/ngwr-rotating-text.d.ts +1 -0
  51. package/types/ngwr-table.d.ts +7 -1
  52. package/types/ngwr-tabs.d.ts +6 -0
  53. package/types/ngwr-toolbar.d.ts +7 -1
  54. package/types/ngwr-version.d.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-lightbox.mjs","sources":["../../../projects/lib/lightbox/lightbox.ts","../../../projects/lib/lightbox/lightbox.html","../../../projects/lib/lightbox/ngwr-lightbox.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { type OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport type { TemplateRef } from '@angular/core';\nimport {\n Component,\n DestroyRef,\n ViewContainerRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { WR_OVERLAY } from 'ngwr/overlay';\n\n/**\n * Image with click-to-zoom lightbox. The thumbnail is a plain `<img>`\n * with whatever sizing/`object-fit` you give it; clicking opens a CDK\n * overlay showing the full image, with Escape + close button + backdrop\n * to dismiss.\n *\n * @example\n * ```html\n * <wr-lightbox\n * src=\"/photo.jpg\"\n * alt=\"Mountain\"\n * style=\"width: 16rem; height: 10rem; object-fit: cover\"\n * />\n * ```\n *\n * @see https://ngwr.dev/components/lightbox\n */\n@Component({\n selector: 'wr-lightbox',\n templateUrl: './lightbox.html',\n styleUrl: './lightbox.scss',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrLightbox {\n /** Image source. */\n readonly src = input.required<string>();\n\n /** Alt text. Required for a11y; falls back to an empty string. @default '' */\n readonly alt = input<string>('');\n\n /** Optional preview src — shown until the full image loads. */\n readonly preview = input<string | null>(null);\n\n /** Disable the click-to-zoom lightbox. @default false */\n readonly disablePreview = input(false, { transform: coerceBooleanProperty });\n\n /** Caption shown under the full image in the lightbox. */\n readonly caption = input<string>('');\n\n protected readonly open = signal(false);\n protected readonly loaded = signal(false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-lightbox'];\n if (this.open()) parts.push('wr-lightbox--open');\n if (!this.loaded()) parts.push('wr-lightbox--loading');\n return parts.join(' ');\n });\n\n // Query by name — `@if` blocks register their own TemplateRefs, so an\n // unqualified TemplateRef query would grab the thumbnail branch instead.\n protected readonly panelTpl = viewChild.required<TemplateRef<unknown>>('viewerTpl');\n\n private readonly overlay = inject(WR_OVERLAY);\n private readonly vcr = inject(ViewContainerRef);\n private readonly destroyRef = inject(DestroyRef);\n private overlayRef: OverlayRef | null = null;\n\n constructor() {\n effect(() => {\n if (this.open()) this.openOverlay();\n else this.closeOverlay();\n });\n }\n\n protected onClick(): void {\n if (this.disablePreview()) return;\n this.open.set(true);\n }\n\n protected onClose(): void {\n this.open.set(false);\n }\n\n protected onLoad(): void {\n this.loaded.set(true);\n }\n\n // Overlay\n\n private openOverlay(): void {\n if (this.overlayRef) return;\n\n this.overlayRef = this.overlay.create({\n hasBackdrop: true,\n backdropClass: 'wr-lightbox-backdrop',\n positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),\n scrollStrategy: this.overlay.scrollStrategies.block(),\n panelClass: 'wr-lightbox-overlay',\n });\n\n const portal = new TemplatePortal(this.panelTpl(), this.vcr);\n this.overlayRef.attach(portal);\n\n this.overlayRef\n .backdropClick()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.open.set(false));\n\n this.overlayRef\n .keydownEvents()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(event => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.open.set(false);\n }\n });\n }\n\n private closeOverlay(): void {\n if (!this.overlayRef) return;\n this.overlayRef.dispose();\n this.overlayRef = null;\n }\n}\n","@if (disablePreview()) {\n <img\n class=\"wr-lightbox__thumb\"\n [src]=\"preview() || src()\"\n [alt]=\"alt()\"\n (load)=\"onLoad()\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n} @else {\n <button type=\"button\" class=\"wr-lightbox__trigger\" [attr.aria-label]=\"alt() || 'Open preview'\" (click)=\"onClick()\">\n <img\n class=\"wr-lightbox__thumb\"\n [src]=\"preview() || src()\"\n [alt]=\"alt()\"\n (load)=\"onLoad()\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n </button>\n}\n\n<ng-template #viewerTpl>\n <div class=\"wr-lightbox-viewer\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"alt() || 'Image preview'\">\n <button\n type=\"button\"\n class=\"wr-lightbox-viewer__close\"\n aria-label=\"Close preview\"\n (click)=\"onClose()\"\n (keydown.enter)=\"onClose()\"\n (keydown.space)=\"onClose()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n <img class=\"wr-lightbox-viewer__full\" [src]=\"src()\" [alt]=\"alt()\" />\n @if (caption()) {\n <div class=\"wr-lightbox-viewer__caption\">{{ caption() }}</div>\n }\n </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-lightbox.mjs","sources":["../../../projects/lib/lightbox/lightbox.ts","../../../projects/lib/lightbox/lightbox.html","../../../projects/lib/lightbox/ngwr-lightbox.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { type OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport type { TemplateRef } from '@angular/core';\nimport {\n Component,\n DestroyRef,\n ViewContainerRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { WR_OVERLAY } from 'ngwr/overlay';\n\n/**\n * Image with click-to-zoom lightbox. The thumbnail is a plain `<img>`\n * with whatever sizing/`object-fit` you give it; clicking opens a CDK\n * overlay showing the full image, with Escape + close button + backdrop\n * to dismiss.\n *\n * @example\n * ```html\n * <wr-lightbox\n * src=\"/photo.jpg\"\n * alt=\"Mountain\"\n * style=\"width: 16rem; height: 10rem; object-fit: cover\"\n * />\n * ```\n *\n * @see https://ngwr.dev/components/lightbox\n */\n@Component({\n selector: 'wr-lightbox',\n templateUrl: './lightbox.html',\n styleUrl: './lightbox.scss',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrLightbox {\n /** Image source. */\n readonly src = input.required<string>();\n\n /** Alt text. Required for a11y; falls back to an empty string. @default '' */\n readonly alt = input<string>('');\n\n /** Optional preview src — shown until the full image loads. */\n readonly preview = input<string | null>(null);\n\n /** Disable the click-to-zoom lightbox. @default false */\n readonly disablePreview = input(false, { transform: coerceBooleanProperty });\n\n /** Caption shown under the full image in the lightbox. */\n readonly caption = input<string>('');\n\n protected readonly open = signal(false);\n protected readonly loaded = signal(false);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-lightbox'];\n if (this.open()) parts.push('wr-lightbox--open');\n if (!this.loaded()) parts.push('wr-lightbox--loading');\n return parts.join(' ');\n });\n\n // Query by name — `@if` blocks register their own TemplateRefs, so an\n // unqualified TemplateRef query would grab the thumbnail branch instead.\n protected readonly panelTpl = viewChild.required<TemplateRef<unknown>>('viewerTpl');\n\n private readonly overlay = inject(WR_OVERLAY);\n private readonly vcr = inject(ViewContainerRef);\n private readonly destroyRef = inject(DestroyRef);\n private overlayRef: OverlayRef | null = null;\n\n constructor() {\n effect(() => {\n if (this.open()) this.openOverlay();\n else this.closeOverlay();\n });\n }\n\n protected onClick(): void {\n if (this.disablePreview()) return;\n this.open.set(true);\n }\n\n protected onClose(): void {\n this.open.set(false);\n }\n\n // Swipe-down-to-dismiss on the open viewer. The image follows the finger and\n // fades; releasing past ~20% of the viewer height closes it.\n private viewerStartY = 0;\n private viewerSwiping = false;\n\n protected onViewerSwipeStart(event: TouchEvent): void {\n const touch = event.touches[0];\n if (!touch) return;\n this.viewerStartY = touch.clientY;\n this.viewerSwiping = true;\n }\n\n protected onViewerSwipeMove(event: TouchEvent, viewer: HTMLElement): void {\n if (!this.viewerSwiping) return;\n const touch = event.touches[0];\n if (!touch) return;\n const dy = touch.clientY - this.viewerStartY;\n if (dy <= 0) {\n viewer.style.transform = '';\n viewer.style.opacity = '';\n return;\n }\n event.preventDefault();\n viewer.style.transition = 'none';\n viewer.style.transform = `translateY(${dy}px)`;\n viewer.style.opacity = String(Math.max(0.3, 1 - dy / (viewer.offsetHeight || 1)));\n }\n\n protected onViewerSwipeEnd(event: TouchEvent, viewer: HTMLElement): void {\n if (!this.viewerSwiping) return;\n this.viewerSwiping = false;\n const touch = event.changedTouches[0];\n const dy = touch ? touch.clientY - this.viewerStartY : 0;\n if (dy > (viewer.offsetHeight || 0) * 0.2) {\n this.onClose();\n } else {\n viewer.style.transition = '';\n viewer.style.transform = '';\n viewer.style.opacity = '';\n }\n }\n\n protected onLoad(): void {\n this.loaded.set(true);\n }\n\n // Overlay\n\n private openOverlay(): void {\n if (this.overlayRef) return;\n\n this.overlayRef = this.overlay.create({\n hasBackdrop: true,\n backdropClass: 'wr-lightbox-backdrop',\n positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),\n scrollStrategy: this.overlay.scrollStrategies.block(),\n panelClass: 'wr-lightbox-overlay',\n });\n\n const portal = new TemplatePortal(this.panelTpl(), this.vcr);\n this.overlayRef.attach(portal);\n\n this.overlayRef\n .backdropClick()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(() => this.open.set(false));\n\n this.overlayRef\n .keydownEvents()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe(event => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.open.set(false);\n }\n });\n }\n\n private closeOverlay(): void {\n if (!this.overlayRef) return;\n this.overlayRef.dispose();\n this.overlayRef = null;\n }\n}\n","@if (disablePreview()) {\n <img\n class=\"wr-lightbox__thumb\"\n [src]=\"preview() || src()\"\n [alt]=\"alt()\"\n (load)=\"onLoad()\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n} @else {\n <button type=\"button\" class=\"wr-lightbox__trigger\" [attr.aria-label]=\"alt() || 'Open preview'\" (click)=\"onClick()\">\n <img\n class=\"wr-lightbox__thumb\"\n [src]=\"preview() || src()\"\n [alt]=\"alt()\"\n (load)=\"onLoad()\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n </button>\n}\n\n<ng-template #viewerTpl>\n <div\n #viewer\n class=\"wr-lightbox-viewer\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"alt() || 'Image preview'\"\n (touchstart)=\"onViewerSwipeStart($event)\"\n (touchmove)=\"onViewerSwipeMove($event, viewer)\"\n (touchend)=\"onViewerSwipeEnd($event, viewer)\"\n (touchcancel)=\"onViewerSwipeEnd($event, viewer)\"\n >\n <button\n type=\"button\"\n class=\"wr-lightbox-viewer__close\"\n aria-label=\"Close preview\"\n (click)=\"onClose()\"\n (keydown.enter)=\"onClose()\"\n (keydown.space)=\"onClose()\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n <img class=\"wr-lightbox-viewer__full\" [src]=\"src()\" [alt]=\"alt()\" />\n @if (caption()) {\n <div class=\"wr-lightbox-viewer__caption\">{{ caption() }}</div>\n }\n </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -70,6 +70,12 @@ class WrPagination {
70
70
  ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
71
71
  /** Disable interaction. @default false */
72
72
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
73
+ /**
74
+ * Collapse to a compact `‹ page / total ›` pager when the control's own box
75
+ * is too narrow for the full numbered strip (a container query on its own
76
+ * width, not the viewport). @default false
77
+ */
78
+ responsive = input(false, { ...(ngDevMode ? { debugName: "responsive" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
73
79
  /** Localised text between range and total ("X–Y of Z"). Falls back to `pagination.of` then `'of'`. */
74
80
  ofLabel = input(null, /* @ts-ignore */
75
81
  ...(ngDevMode ? [{ debugName: "ofLabel" }] : /* istanbul ignore next */ []));
@@ -144,6 +150,8 @@ class WrPagination {
144
150
  ];
145
151
  if (this.disabled())
146
152
  parts.push('wr-pagination--disabled');
153
+ if (this.responsive())
154
+ parts.push('wr-pagination--responsive');
147
155
  return parts.join(' ');
148
156
  }, /* @ts-ignore */
149
157
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
@@ -166,12 +174,12 @@ class WrPagination {
166
174
  this.currentPage.set(cap);
167
175
  }
168
176
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrPagination, deps: [], target: i0.ɵɵFactoryTarget.Component });
169
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: WrPagination, isStandalone: true, selector: "wr-pagination", inputs: { currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, showSizeChanger: { classPropertyName: "showSizeChanger", publicName: "showSizeChanger", isSignal: true, isRequired: false, transformFunction: null }, showTotal: { classPropertyName: "showTotal", publicName: "showTotal", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, ofLabel: { classPropertyName: "ofLabel", publicName: "ofLabel", isSignal: true, isRequired: false, transformFunction: null }, prevLabel: { classPropertyName: "prevLabel", publicName: "prevLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, itemsPerPageLabel: { classPropertyName: "itemsPerPageLabel", publicName: "itemsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange", pageSize: "pageSizeChange" }, host: { attributes: { "role": "navigation", "aria-label": "Pagination" }, properties: { "class": "classes()" } }, ngImport: i0, template: "<div class=\"wr-pagination__inner\">\n @if (showTotal()) {\n <span class=\"wr-pagination__total\">{{ rangeLabel() }}</span>\n }\n\n <div class=\"wr-pagination__nav\">\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedPrev()\"\n [disabled]=\"disabled() || currentPage() === 1\"\n (click)=\"goTo(currentPage() - 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n </wr-btn>\n\n @for (page of pages(); track $index) {\n @if (page === ellipsis) {\n <span class=\"wr-pagination__ellipsis\">{{ ellipsis }}</span>\n } @else {\n <wr-btn\n [size]=\"buttonSize()\"\n [outlined]=\"!isCurrent(page)\"\n [color]=\"isCurrent(page) ? 'primary' : null\"\n [attr.aria-current]=\"isCurrent(page) ? 'page' : null\"\n [attr.aria-label]=\"`Go to page ${page}`\"\n [disabled]=\"disabled()\"\n (click)=\"goTo(page)\"\n >\n {{ page }}\n </wr-btn>\n }\n }\n\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedNext()\"\n [disabled]=\"disabled() || currentPage() === totalPages()\"\n (click)=\"goTo(currentPage() + 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </wr-btn>\n </div>\n\n @if (showSizeChanger()) {\n <wr-select\n class=\"wr-pagination__size\"\n [attr.aria-label]=\"resolvedItemsPerPage()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onSizeChange($event)\"\n >\n @for (option of pageSizeOptions(); track option) {\n <wr-option [value]=\"option\">{{ option }} / page</wr-option>\n }\n </wr-select>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "isDisabledWhenLoading"] }, { kind: "component", type: WrSelect, selector: "wr-select", inputs: ["placeholder", "clearLabel", "disabled", "rounded", "responsive", "mode", "options", "displayWith", "loader", "debounceMs", "minChars", "freeText", "noResultsText", "loadingText", "separators", "allowDuplicates", "validate", "clearable", "maxItems", "maxTagCount"] }, { kind: "component", type: WrOption, selector: "wr-option", inputs: ["value", "disabled"] }], encapsulation: i0.ViewEncapsulation.None });
177
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: WrPagination, isStandalone: true, selector: "wr-pagination", inputs: { currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, showSizeChanger: { classPropertyName: "showSizeChanger", publicName: "showSizeChanger", isSignal: true, isRequired: false, transformFunction: null }, showTotal: { classPropertyName: "showTotal", publicName: "showTotal", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, ofLabel: { classPropertyName: "ofLabel", publicName: "ofLabel", isSignal: true, isRequired: false, transformFunction: null }, prevLabel: { classPropertyName: "prevLabel", publicName: "prevLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, itemsPerPageLabel: { classPropertyName: "itemsPerPageLabel", publicName: "itemsPerPageLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange", pageSize: "pageSizeChange" }, host: { attributes: { "role": "navigation", "aria-label": "Pagination" }, properties: { "class": "classes()" } }, ngImport: i0, template: "<div class=\"wr-pagination__inner\">\n @if (showTotal()) {\n <span class=\"wr-pagination__total\">{{ rangeLabel() }}</span>\n }\n\n <div class=\"wr-pagination__nav\">\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedPrev()\"\n [disabled]=\"disabled() || currentPage() === 1\"\n (click)=\"goTo(currentPage() - 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n </wr-btn>\n\n @for (page of pages(); track $index) {\n @if (page === ellipsis) {\n <span class=\"wr-pagination__ellipsis\">{{ ellipsis }}</span>\n } @else {\n <wr-btn\n class=\"wr-pagination__page\"\n [size]=\"buttonSize()\"\n [outlined]=\"!isCurrent(page)\"\n [color]=\"isCurrent(page) ? 'primary' : null\"\n [attr.aria-current]=\"isCurrent(page) ? 'page' : null\"\n [attr.aria-label]=\"`Go to page ${page}`\"\n [disabled]=\"disabled()\"\n (click)=\"goTo(page)\"\n >\n {{ page }}\n </wr-btn>\n }\n }\n\n <!-- Compact pager \u2014 shown only in responsive mode on a narrow box (CSS). -->\n <span class=\"wr-pagination__current\" aria-hidden=\"true\">{{ currentPage() }} / {{ totalPages() }}</span>\n\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedNext()\"\n [disabled]=\"disabled() || currentPage() === totalPages()\"\n (click)=\"goTo(currentPage() + 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </wr-btn>\n </div>\n\n @if (showSizeChanger()) {\n <wr-select\n class=\"wr-pagination__size\"\n [attr.aria-label]=\"resolvedItemsPerPage()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onSizeChange($event)\"\n >\n @for (option of pageSizeOptions(); track option) {\n <wr-option [value]=\"option\">{{ option }} / page</wr-option>\n }\n </wr-select>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: WrButton, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: ["color", "size", "shape", "icon", "iconPosition", "disabled", "outlined", "block", "loading", "isDisabledWhenLoading"] }, { kind: "component", type: WrSelect, selector: "wr-select", inputs: ["placeholder", "clearLabel", "disabled", "rounded", "responsive", "mode", "options", "displayWith", "loader", "debounceMs", "minChars", "freeText", "noResultsText", "loadingText", "separators", "allowDuplicates", "validate", "clearable", "maxItems", "maxTagCount"] }, { kind: "component", type: WrOption, selector: "wr-option", inputs: ["value", "disabled"] }], encapsulation: i0.ViewEncapsulation.None });
170
178
  }
171
179
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrPagination, decorators: [{
172
180
  type: Component,
173
- args: [{ selector: 'wr-pagination', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', role: 'navigation', 'aria-label': 'Pagination' }, imports: [FormsModule, WrButton, WrSelect, WrOption], template: "<div class=\"wr-pagination__inner\">\n @if (showTotal()) {\n <span class=\"wr-pagination__total\">{{ rangeLabel() }}</span>\n }\n\n <div class=\"wr-pagination__nav\">\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedPrev()\"\n [disabled]=\"disabled() || currentPage() === 1\"\n (click)=\"goTo(currentPage() - 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n </wr-btn>\n\n @for (page of pages(); track $index) {\n @if (page === ellipsis) {\n <span class=\"wr-pagination__ellipsis\">{{ ellipsis }}</span>\n } @else {\n <wr-btn\n [size]=\"buttonSize()\"\n [outlined]=\"!isCurrent(page)\"\n [color]=\"isCurrent(page) ? 'primary' : null\"\n [attr.aria-current]=\"isCurrent(page) ? 'page' : null\"\n [attr.aria-label]=\"`Go to page ${page}`\"\n [disabled]=\"disabled()\"\n (click)=\"goTo(page)\"\n >\n {{ page }}\n </wr-btn>\n }\n }\n\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedNext()\"\n [disabled]=\"disabled() || currentPage() === totalPages()\"\n (click)=\"goTo(currentPage() + 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </wr-btn>\n </div>\n\n @if (showSizeChanger()) {\n <wr-select\n class=\"wr-pagination__size\"\n [attr.aria-label]=\"resolvedItemsPerPage()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onSizeChange($event)\"\n >\n @for (option of pageSizeOptions(); track option) {\n <wr-option [value]=\"option\">{{ option }} / page</wr-option>\n }\n </wr-select>\n }\n</div>\n" }]
174
- }], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], showSizeChanger: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSizeChanger", required: false }] }], showTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTotal", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], itemsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemsPerPageLabel", required: false }] }] } });
181
+ args: [{ selector: 'wr-pagination', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()', role: 'navigation', 'aria-label': 'Pagination' }, imports: [FormsModule, WrButton, WrSelect, WrOption], template: "<div class=\"wr-pagination__inner\">\n @if (showTotal()) {\n <span class=\"wr-pagination__total\">{{ rangeLabel() }}</span>\n }\n\n <div class=\"wr-pagination__nav\">\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedPrev()\"\n [disabled]=\"disabled() || currentPage() === 1\"\n (click)=\"goTo(currentPage() - 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n </wr-btn>\n\n @for (page of pages(); track $index) {\n @if (page === ellipsis) {\n <span class=\"wr-pagination__ellipsis\">{{ ellipsis }}</span>\n } @else {\n <wr-btn\n class=\"wr-pagination__page\"\n [size]=\"buttonSize()\"\n [outlined]=\"!isCurrent(page)\"\n [color]=\"isCurrent(page) ? 'primary' : null\"\n [attr.aria-current]=\"isCurrent(page) ? 'page' : null\"\n [attr.aria-label]=\"`Go to page ${page}`\"\n [disabled]=\"disabled()\"\n (click)=\"goTo(page)\"\n >\n {{ page }}\n </wr-btn>\n }\n }\n\n <!-- Compact pager \u2014 shown only in responsive mode on a narrow box (CSS). -->\n <span class=\"wr-pagination__current\" aria-hidden=\"true\">{{ currentPage() }} / {{ totalPages() }}</span>\n\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedNext()\"\n [disabled]=\"disabled() || currentPage() === totalPages()\"\n (click)=\"goTo(currentPage() + 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </wr-btn>\n </div>\n\n @if (showSizeChanger()) {\n <wr-select\n class=\"wr-pagination__size\"\n [attr.aria-label]=\"resolvedItemsPerPage()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onSizeChange($event)\"\n >\n @for (option of pageSizeOptions(); track option) {\n <wr-option [value]=\"option\">{{ option }} / page</wr-option>\n }\n </wr-select>\n }\n</div>\n" }]
182
+ }], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], showSizeChanger: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSizeChanger", required: false }] }], showTotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTotal", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], ofLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ofLabel", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], itemsPerPageLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemsPerPageLabel", required: false }] }] } });
175
183
 
176
184
  /**
177
185
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-pagination.mjs","sources":["../../../projects/lib/pagination/pagination.ts","../../../projects/lib/pagination/pagination.html","../../../projects/lib/pagination/ngwr-pagination.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input, model } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { WrButton, type WrButtonSize } from 'ngwr/button';\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrOption, WrSelect } from 'ngwr/select';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrPaginationAlign, WrPaginationShape, WrPaginationSize } from './interfaces';\n\nconst ELLIPSIS = '…' as const;\ntype PageEntry = number | typeof ELLIPSIS;\n\n/**\n * Numbered page navigator with optional total / page-size controls.\n *\n * Two-way binds `currentPage` and `pageSize` via signal `model()` inputs.\n *\n * @example\n * ```html\n * <wr-pagination\n * [total]=\"120\"\n * [(currentPage)]=\"page\"\n * [(pageSize)]=\"size\"\n * showTotal\n * showSizeChanger\n * />\n * ```\n *\n * @see https://ngwr.dev/components/pagination\n */\n@Component({\n selector: 'wr-pagination',\n templateUrl: './pagination.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()', role: 'navigation', 'aria-label': 'Pagination' },\n imports: [FormsModule, WrButton, WrSelect, WrOption],\n})\nexport class WrPagination {\n /** Currently displayed page (1-based). Two-way bindable. */\n readonly currentPage = model<number>(1);\n\n /** Items per page. Two-way bindable. */\n readonly pageSize = model<number>(10);\n\n /** Total item count across all pages. */\n readonly total = input(0, { transform: numAttr(0) });\n\n /** Options shown in the page-size dropdown. */\n readonly pageSizeOptions = input<readonly number[]>([10, 20, 50, 100]);\n\n /** Render the page-size dropdown. @default false */\n readonly showSizeChanger = input(false, { transform: coerceBooleanProperty });\n\n /** Render the \"X–Y of Z\" total label. @default false */\n readonly showTotal = input(false, { transform: coerceBooleanProperty });\n\n /** Horizontal alignment. @default 'start' */\n readonly align = input<WrPaginationAlign>('start');\n\n /** Size variant — cascades to every internal button. @default 'md' */\n readonly size = input<WrPaginationSize>('sm');\n\n /** Resolved button size — clamps xs/xl to the closest button size. @internal */\n protected readonly buttonSize = computed<WrButtonSize>(() => {\n const s = this.size();\n if (s === 'xs') return 'sm';\n if (s === 'xl') return 'lg';\n return s;\n });\n\n /** Cell corner treatment. @default 'rounded' */\n readonly shape = input<WrPaginationShape>('rounded');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Localised text between range and total (\"X–Y of Z\"). Falls back to `pagination.of` then `'of'`. */\n readonly ofLabel = input<string | null>(null);\n\n /** Previous-page button aria-label. Falls back to `pagination.prev`. */\n readonly prevLabel = input<string | null>(null);\n\n /** Next-page button aria-label. Falls back to `pagination.next`. */\n readonly nextLabel = input<string | null>(null);\n\n /** \"Items per page\" label. Falls back to `pagination.itemsPerPage`. */\n readonly itemsPerPageLabel = input<string | null>(null);\n\n /** Resolved labels. */\n protected readonly resolvedOf = useI18nText(this.ofLabel, 'pagination.of', 'of');\n protected readonly resolvedPrev = useI18nText(this.prevLabel, 'pagination.prev', 'Previous page');\n protected readonly resolvedNext = useI18nText(this.nextLabel, 'pagination.next', 'Next page');\n protected readonly resolvedItemsPerPage = useI18nText(\n this.itemsPerPageLabel,\n 'pagination.itemsPerPage',\n 'Items per page'\n );\n\n /** Internal: total page count. */\n protected readonly totalPages = computed(() => Math.max(1, Math.ceil(this.total() / this.pageSize())));\n\n /** Internal: \"X–Y of Z\" string. */\n protected readonly rangeLabel = computed(() => {\n const start = this.total() === 0 ? 0 : (this.currentPage() - 1) * this.pageSize() + 1;\n const end = Math.min(this.currentPage() * this.pageSize(), this.total());\n return `${start}-${end} ${this.resolvedOf()} ${this.total()}`;\n });\n\n /**\n * Internal: page list with ellipses. Window of 7 visible page slots.\n *\n * 1 … 5 6 [7] 8 9 … 20\n */\n protected readonly pages = computed<readonly PageEntry[]>(() => {\n const total = this.totalPages();\n const current = this.currentPage();\n\n if (total <= 7) {\n return Array.from({ length: total }, (_, i) => i + 1);\n }\n\n const items: PageEntry[] = [1];\n if (current > 4) items.push(ELLIPSIS);\n\n let start: number;\n let end: number;\n if (current <= 4) {\n start = 2;\n end = 5;\n } else if (current >= total - 3) {\n start = total - 4;\n end = total - 1;\n } else {\n start = current - 2;\n end = current + 2;\n }\n\n for (let i = start; i <= end; i++) items.push(i);\n if (current < total - 3) items.push(ELLIPSIS);\n if (end !== total) items.push(total);\n\n return items;\n });\n\n protected readonly ellipsis = ELLIPSIS;\n\n protected readonly classes = computed(() => {\n const parts = [\n 'wr-pagination',\n `wr-pagination--${this.align()}`,\n `wr-pagination--${this.size()}`,\n `wr-pagination--${this.shape()}`,\n ];\n if (this.disabled()) parts.push('wr-pagination--disabled');\n return parts.join(' ');\n });\n\n protected isCurrent(page: number): boolean {\n return this.currentPage() === page;\n }\n\n protected goTo(page: number | typeof ELLIPSIS): void {\n if (typeof page !== 'number') return;\n if (this.disabled() || page < 1 || page > this.totalPages() || page === this.currentPage()) return;\n this.currentPage.set(page);\n }\n\n protected onSizeChange(size: number): void {\n if (this.disabled() || size === this.pageSize()) return;\n this.pageSize.set(size);\n const cap = Math.max(1, Math.ceil(this.total() / size));\n if (this.currentPage() > cap) this.currentPage.set(cap);\n }\n}\n","<div class=\"wr-pagination__inner\">\n @if (showTotal()) {\n <span class=\"wr-pagination__total\">{{ rangeLabel() }}</span>\n }\n\n <div class=\"wr-pagination__nav\">\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedPrev()\"\n [disabled]=\"disabled() || currentPage() === 1\"\n (click)=\"goTo(currentPage() - 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n </wr-btn>\n\n @for (page of pages(); track $index) {\n @if (page === ellipsis) {\n <span class=\"wr-pagination__ellipsis\">{{ ellipsis }}</span>\n } @else {\n <wr-btn\n [size]=\"buttonSize()\"\n [outlined]=\"!isCurrent(page)\"\n [color]=\"isCurrent(page) ? 'primary' : null\"\n [attr.aria-current]=\"isCurrent(page) ? 'page' : null\"\n [attr.aria-label]=\"`Go to page ${page}`\"\n [disabled]=\"disabled()\"\n (click)=\"goTo(page)\"\n >\n {{ page }}\n </wr-btn>\n }\n }\n\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedNext()\"\n [disabled]=\"disabled() || currentPage() === totalPages()\"\n (click)=\"goTo(currentPage() + 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </wr-btn>\n </div>\n\n @if (showSizeChanger()) {\n <wr-select\n class=\"wr-pagination__size\"\n [attr.aria-label]=\"resolvedItemsPerPage()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onSizeChange($event)\"\n >\n @for (option of pageSizeOptions(); track option) {\n <wr-option [value]=\"option\">{{ option }} / page</wr-option>\n }\n </wr-select>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-pagination.mjs","sources":["../../../projects/lib/pagination/pagination.ts","../../../projects/lib/pagination/pagination.html","../../../projects/lib/pagination/ngwr-pagination.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ViewEncapsulation, computed, input, model } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\nimport { WrButton, type WrButtonSize } from 'ngwr/button';\nimport { useI18nText } from 'ngwr/i18n';\nimport { WrOption, WrSelect } from 'ngwr/select';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrPaginationAlign, WrPaginationShape, WrPaginationSize } from './interfaces';\n\nconst ELLIPSIS = '…' as const;\ntype PageEntry = number | typeof ELLIPSIS;\n\n/**\n * Numbered page navigator with optional total / page-size controls.\n *\n * Two-way binds `currentPage` and `pageSize` via signal `model()` inputs.\n *\n * @example\n * ```html\n * <wr-pagination\n * [total]=\"120\"\n * [(currentPage)]=\"page\"\n * [(pageSize)]=\"size\"\n * showTotal\n * showSizeChanger\n * />\n * ```\n *\n * @see https://ngwr.dev/components/pagination\n */\n@Component({\n selector: 'wr-pagination',\n templateUrl: './pagination.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()', role: 'navigation', 'aria-label': 'Pagination' },\n imports: [FormsModule, WrButton, WrSelect, WrOption],\n})\nexport class WrPagination {\n /** Currently displayed page (1-based). Two-way bindable. */\n readonly currentPage = model<number>(1);\n\n /** Items per page. Two-way bindable. */\n readonly pageSize = model<number>(10);\n\n /** Total item count across all pages. */\n readonly total = input(0, { transform: numAttr(0) });\n\n /** Options shown in the page-size dropdown. */\n readonly pageSizeOptions = input<readonly number[]>([10, 20, 50, 100]);\n\n /** Render the page-size dropdown. @default false */\n readonly showSizeChanger = input(false, { transform: coerceBooleanProperty });\n\n /** Render the \"X–Y of Z\" total label. @default false */\n readonly showTotal = input(false, { transform: coerceBooleanProperty });\n\n /** Horizontal alignment. @default 'start' */\n readonly align = input<WrPaginationAlign>('start');\n\n /** Size variant — cascades to every internal button. @default 'md' */\n readonly size = input<WrPaginationSize>('sm');\n\n /** Resolved button size — clamps xs/xl to the closest button size. @internal */\n protected readonly buttonSize = computed<WrButtonSize>(() => {\n const s = this.size();\n if (s === 'xs') return 'sm';\n if (s === 'xl') return 'lg';\n return s;\n });\n\n /** Cell corner treatment. @default 'rounded' */\n readonly shape = input<WrPaginationShape>('rounded');\n\n /** Disable interaction. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Collapse to a compact `‹ page / total ›` pager when the control's own box\n * is too narrow for the full numbered strip (a container query on its own\n * width, not the viewport). @default false\n */\n readonly responsive = input(false, { transform: coerceBooleanProperty });\n\n /** Localised text between range and total (\"X–Y of Z\"). Falls back to `pagination.of` then `'of'`. */\n readonly ofLabel = input<string | null>(null);\n\n /** Previous-page button aria-label. Falls back to `pagination.prev`. */\n readonly prevLabel = input<string | null>(null);\n\n /** Next-page button aria-label. Falls back to `pagination.next`. */\n readonly nextLabel = input<string | null>(null);\n\n /** \"Items per page\" label. Falls back to `pagination.itemsPerPage`. */\n readonly itemsPerPageLabel = input<string | null>(null);\n\n /** Resolved labels. */\n protected readonly resolvedOf = useI18nText(this.ofLabel, 'pagination.of', 'of');\n protected readonly resolvedPrev = useI18nText(this.prevLabel, 'pagination.prev', 'Previous page');\n protected readonly resolvedNext = useI18nText(this.nextLabel, 'pagination.next', 'Next page');\n protected readonly resolvedItemsPerPage = useI18nText(\n this.itemsPerPageLabel,\n 'pagination.itemsPerPage',\n 'Items per page'\n );\n\n /** Internal: total page count. */\n protected readonly totalPages = computed(() => Math.max(1, Math.ceil(this.total() / this.pageSize())));\n\n /** Internal: \"X–Y of Z\" string. */\n protected readonly rangeLabel = computed(() => {\n const start = this.total() === 0 ? 0 : (this.currentPage() - 1) * this.pageSize() + 1;\n const end = Math.min(this.currentPage() * this.pageSize(), this.total());\n return `${start}-${end} ${this.resolvedOf()} ${this.total()}`;\n });\n\n /**\n * Internal: page list with ellipses. Window of 7 visible page slots.\n *\n * 1 … 5 6 [7] 8 9 … 20\n */\n protected readonly pages = computed<readonly PageEntry[]>(() => {\n const total = this.totalPages();\n const current = this.currentPage();\n\n if (total <= 7) {\n return Array.from({ length: total }, (_, i) => i + 1);\n }\n\n const items: PageEntry[] = [1];\n if (current > 4) items.push(ELLIPSIS);\n\n let start: number;\n let end: number;\n if (current <= 4) {\n start = 2;\n end = 5;\n } else if (current >= total - 3) {\n start = total - 4;\n end = total - 1;\n } else {\n start = current - 2;\n end = current + 2;\n }\n\n for (let i = start; i <= end; i++) items.push(i);\n if (current < total - 3) items.push(ELLIPSIS);\n if (end !== total) items.push(total);\n\n return items;\n });\n\n protected readonly ellipsis = ELLIPSIS;\n\n protected readonly classes = computed(() => {\n const parts = [\n 'wr-pagination',\n `wr-pagination--${this.align()}`,\n `wr-pagination--${this.size()}`,\n `wr-pagination--${this.shape()}`,\n ];\n if (this.disabled()) parts.push('wr-pagination--disabled');\n if (this.responsive()) parts.push('wr-pagination--responsive');\n return parts.join(' ');\n });\n\n protected isCurrent(page: number): boolean {\n return this.currentPage() === page;\n }\n\n protected goTo(page: number | typeof ELLIPSIS): void {\n if (typeof page !== 'number') return;\n if (this.disabled() || page < 1 || page > this.totalPages() || page === this.currentPage()) return;\n this.currentPage.set(page);\n }\n\n protected onSizeChange(size: number): void {\n if (this.disabled() || size === this.pageSize()) return;\n this.pageSize.set(size);\n const cap = Math.max(1, Math.ceil(this.total() / size));\n if (this.currentPage() > cap) this.currentPage.set(cap);\n }\n}\n","<div class=\"wr-pagination__inner\">\n @if (showTotal()) {\n <span class=\"wr-pagination__total\">{{ rangeLabel() }}</span>\n }\n\n <div class=\"wr-pagination__nav\">\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedPrev()\"\n [disabled]=\"disabled() || currentPage() === 1\"\n (click)=\"goTo(currentPage() - 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m12 19-7-7 7-7\" />\n <path d=\"M19 12H5\" />\n </svg>\n </wr-btn>\n\n @for (page of pages(); track $index) {\n @if (page === ellipsis) {\n <span class=\"wr-pagination__ellipsis\">{{ ellipsis }}</span>\n } @else {\n <wr-btn\n class=\"wr-pagination__page\"\n [size]=\"buttonSize()\"\n [outlined]=\"!isCurrent(page)\"\n [color]=\"isCurrent(page) ? 'primary' : null\"\n [attr.aria-current]=\"isCurrent(page) ? 'page' : null\"\n [attr.aria-label]=\"`Go to page ${page}`\"\n [disabled]=\"disabled()\"\n (click)=\"goTo(page)\"\n >\n {{ page }}\n </wr-btn>\n }\n }\n\n <!-- Compact pager — shown only in responsive mode on a narrow box (CSS). -->\n <span class=\"wr-pagination__current\" aria-hidden=\"true\">{{ currentPage() }} / {{ totalPages() }}</span>\n\n <wr-btn\n [size]=\"buttonSize()\"\n outlined\n [attr.aria-label]=\"resolvedNext()\"\n [disabled]=\"disabled() || currentPage() === totalPages()\"\n (click)=\"goTo(currentPage() + 1)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 12h14\" />\n <path d=\"m12 5 7 7-7 7\" />\n </svg>\n </wr-btn>\n </div>\n\n @if (showSizeChanger()) {\n <wr-select\n class=\"wr-pagination__size\"\n [attr.aria-label]=\"resolvedItemsPerPage()\"\n [disabled]=\"disabled()\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"onSizeChange($event)\"\n >\n @for (option of pageSizeOptions(); track option) {\n <wr-option [value]=\"option\">{{ option }} / page</wr-option>\n }\n </wr-select>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
2
2
  import { isPlatformBrowser } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { input, output, signal, computed, inject, ElementRef, DestroyRef, PLATFORM_ID, afterNextRender, effect, ViewEncapsulation, Component } from '@angular/core';
4
+ import { input, output, signal, computed, inject, ElementRef, Injector, DestroyRef, PLATFORM_ID, afterNextRender, effect, ViewEncapsulation, Component } from '@angular/core';
5
5
  import { WrPlatform } from 'ngwr/platform';
6
6
  import { numAttr } from 'ngwr/utils';
7
7
 
@@ -93,6 +93,7 @@ class WrRotatingText {
93
93
  words = computed(() => splitWords(this.current(), this.splitBy()), /* @ts-ignore */
94
94
  ...(ngDevMode ? [{ debugName: "words" }] : /* istanbul ignore next */ []));
95
95
  host = inject(ElementRef);
96
+ injector = inject(Injector);
96
97
  destroyRef = inject(DestroyRef);
97
98
  isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
98
99
  platform = inject(WrPlatform);
@@ -157,11 +158,16 @@ class WrRotatingText {
157
158
  await this.animateOut();
158
159
  this.index.set(target);
159
160
  this.nextChange.emit(target);
160
- // Wait one microtask so the new spans are rendered, then animate in.
161
- queueMicrotask(() => {
161
+ // Animate in only AFTER Angular has rendered the new word's spans. A single
162
+ // microtask isn't enough under zoneless change detection — the re-render
163
+ // hasn't flushed yet, so `animateIn()` would query the OLD spans, animate
164
+ // them, and then the render would swap their content mid-flight (and any
165
+ // spans the longer word adds would never get animated). `afterNextRender`
166
+ // fires after the render the `index` change schedules.
167
+ afterNextRender(() => {
162
168
  this.animateIn();
163
169
  this.isAnimating = false;
164
- });
170
+ }, { injector: this.injector });
165
171
  }
166
172
  animateIn() {
167
173
  const targets = this.host.nativeElement.querySelectorAll('.wr-rotating-text__char');
@@ -213,6 +219,11 @@ class WrRotatingText {
213
219
  ], { duration: this.duration() * 1000, delay, easing: this.easing(), fill: 'forwards' });
214
220
  promises.push(new Promise(resolve => {
215
221
  anim.onfinish = () => {
222
+ // Commit the exited (hidden, lifted) state to inline style before
223
+ // cancelling, so the char stays out instead of snapping back to
224
+ // visible-in-place between the swap and the enter animation.
225
+ el.style.opacity = '0';
226
+ el.style.transform = 'translate3d(0, -120%, 0)';
216
227
  try {
217
228
  anim.cancel();
218
229
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-rotating-text.mjs","sources":["../../../projects/lib/rotating-text/rotating-text.ts","../../../projects/lib/rotating-text/rotating-text.html","../../../projects/lib/rotating-text/ngwr-rotating-text.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the RotatingText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/rotating-text\n *\n * The reactbits version uses `motion/react` + `AnimatePresence`. This\n * port is dependency-free — vanilla DOM split + Web Animations API for\n * the enter/exit transitions.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrRotatingTextSplit, WrRotatingTextStaggerFrom } from './interfaces';\n\nconst DEFAULT_EASING = 'cubic-bezier(0.16, 1, 0.3, 1)';\n\ninterface Word {\n readonly characters: readonly string[];\n readonly needsSpace: boolean;\n}\n\nfunction graphemes(text: string): readonly string[] {\n if (typeof Intl !== 'undefined' && 'Segmenter' in Intl) {\n const seg = new Intl.Segmenter('en', { granularity: 'grapheme' });\n return Array.from(seg.segment(text), s => s.segment);\n }\n return Array.from(text);\n}\n\nfunction splitWords(text: string, by: WrRotatingTextSplit): readonly Word[] {\n if (!text) return [];\n if (by === 'lines') {\n const lines = text.split('\\n');\n return lines.map((line, i) => ({ characters: [line], needsSpace: i !== lines.length - 1 }));\n }\n if (by === 'words') {\n const words = text.split(' ');\n return words.map((w, i) => ({ characters: [w], needsSpace: i !== words.length - 1 }));\n }\n // characters: split each word into graphemes, keep word boundaries for spacing.\n const words = text.split(' ');\n return words.map((w, i) => ({ characters: graphemes(w), needsSpace: i !== words.length - 1 }));\n}\n\n/**\n * Cycles through a list of strings, animating each transition\n * character-by-character (or word/line). Auto-advances on a timer by\n * default; can also be controlled imperatively via `next()` /\n * `previous()` / `jumpTo()` / `reset()`.\n *\n * @example\n * ```html\n * <wr-rotating-text [texts]=\"['design', 'ship', 'iterate']\" />\n *\n * // Manual control:\n * @ViewChild(WrRotatingText) rotator!: WrRotatingText;\n * onClick(): void { this.rotator.next(); }\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/rotating-text\n */\n@Component({\n selector: 'wr-rotating-text',\n templateUrl: './rotating-text.html',\n styleUrl: './rotating-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-rotating-text' },\n})\nexport class WrRotatingText {\n /** Strings to cycle. Required. */\n readonly texts = input.required<readonly string[]>();\n\n /** Auto-advance interval in ms. @default 2000 */\n readonly rotationInterval = input(2000, { transform: numAttr(2000) });\n\n /** Granularity of the split. @default 'characters' */\n readonly splitBy = input<WrRotatingTextSplit>('characters');\n\n /** Auto-advance on a timer. @default true */\n readonly auto = input(true, { transform: coerceBooleanProperty });\n\n /** Loop back to the first string after the last. @default true */\n readonly loop = input(true, { transform: coerceBooleanProperty });\n\n /** Per-swap tween duration in seconds. @default 0.6 */\n readonly duration = input(0.6, { transform: numAttr(0.6) });\n\n /** CSS easing of the per-piece tween. @default 'cubic-bezier(0.16, 1, 0.3, 1)' (~power3.out) */\n readonly easing = input(DEFAULT_EASING);\n\n /** Per-piece stagger in seconds. @default 0 */\n readonly staggerDuration = input(0, { transform: numAttr(0) });\n\n /** Stagger origin. @default 'first' */\n readonly staggerFrom = input<WrRotatingTextStaggerFrom>('first');\n\n /** Emitted with the new index on every rotation. */\n readonly nextChange = output<number>();\n\n protected readonly index = signal(0);\n\n protected readonly current = computed(() => {\n const arr = this.texts();\n return arr.length === 0 ? '' : arr[Math.min(this.index(), arr.length - 1)];\n });\n\n protected readonly words = computed<readonly Word[]>(() => splitWords(this.current(), this.splitBy()));\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private timerId: ReturnType<typeof setInterval> | undefined;\n private isAnimating = false;\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => this.animateIn());\n\n // Restart the timer when interval / auto change.\n effect(() => {\n this.rotationInterval();\n this.auto();\n this.restartTimer();\n });\n\n this.destroyRef.onDestroy(() => clearInterval(this.timerId));\n }\n\n // Public imperative API\n\n next(): void {\n const arr = this.texts();\n if (arr.length === 0) return;\n const cur = this.index();\n const last = arr.length - 1;\n const target = cur === last ? (this.loop() ? 0 : cur) : cur + 1;\n if (target !== cur) void this.transitionTo(target);\n }\n\n previous(): void {\n const arr = this.texts();\n if (arr.length === 0) return;\n const cur = this.index();\n const last = arr.length - 1;\n const target = cur === 0 ? (this.loop() ? last : cur) : cur - 1;\n if (target !== cur) void this.transitionTo(target);\n }\n\n jumpTo(target: number): void {\n const arr = this.texts();\n if (arr.length === 0) return;\n const clamped = Math.max(0, Math.min(target, arr.length - 1));\n if (clamped !== this.index()) void this.transitionTo(clamped);\n }\n\n reset(): void {\n if (this.index() !== 0) void this.transitionTo(0);\n }\n\n // Internals\n\n private restartTimer(): void {\n clearInterval(this.timerId);\n if (!this.auto()) return;\n this.timerId = setInterval(() => this.next(), this.rotationInterval());\n }\n\n private async transitionTo(target: number): Promise<void> {\n if (this.isAnimating) return;\n this.isAnimating = true;\n\n await this.animateOut();\n this.index.set(target);\n this.nextChange.emit(target);\n // Wait one microtask so the new spans are rendered, then animate in.\n queueMicrotask(() => {\n this.animateIn();\n this.isAnimating = false;\n });\n }\n\n private animateIn(): void {\n const targets = this.host.nativeElement.querySelectorAll<HTMLElement>('.wr-rotating-text__char');\n if (targets.length === 0) return;\n\n // Reduced motion: the word swap itself is content and keeps cycling,\n // but each swap is instant — no per-char rise/fall tween.\n if (this.platform.prefersReducedMotion()) {\n targets.forEach(el => {\n el.style.opacity = '1';\n el.style.transform = 'translate3d(0, 0, 0)';\n });\n return;\n }\n\n const stagger = this.staggerDuration() * 1000;\n targets.forEach((el, i) => {\n const delay = this.delayFor(i, targets.length, stagger);\n el.style.opacity = '0';\n el.style.transform = 'translate3d(0, 100%, 0)';\n const anim = el.animate(\n [\n { opacity: 0, transform: 'translate3d(0, 100%, 0)' },\n { opacity: 1, transform: 'translate3d(0, 0, 0)' },\n ],\n { duration: this.duration() * 1000, delay, easing: this.easing(), fill: 'forwards' }\n );\n anim.onfinish = (): void => {\n el.style.opacity = '1';\n el.style.transform = 'translate3d(0, 0, 0)';\n try {\n anim.cancel();\n } catch {\n /* noop */\n }\n };\n });\n }\n\n private animateOut(): Promise<void> {\n const targets = this.host.nativeElement.querySelectorAll<HTMLElement>('.wr-rotating-text__char');\n if (targets.length === 0) return Promise.resolve();\n if (this.platform.prefersReducedMotion()) return Promise.resolve();\n const stagger = this.staggerDuration() * 1000;\n const promises: Promise<void>[] = [];\n targets.forEach((el, i) => {\n const delay = this.delayFor(i, targets.length, stagger);\n const anim = el.animate(\n [\n { opacity: 1, transform: 'translate3d(0, 0, 0)' },\n { opacity: 0, transform: 'translate3d(0, -120%, 0)' },\n ],\n { duration: this.duration() * 1000, delay, easing: this.easing(), fill: 'forwards' }\n );\n promises.push(\n new Promise<void>(resolve => {\n anim.onfinish = (): void => {\n try {\n anim.cancel();\n } catch {\n /* noop */\n }\n resolve();\n };\n })\n );\n });\n return Promise.all(promises).then(() => undefined);\n }\n\n private delayFor(i: number, total: number, stagger: number): number {\n if (stagger === 0) return 0;\n switch (this.staggerFrom()) {\n case 'last':\n return (total - 1 - i) * stagger;\n case 'center': {\n const center = Math.floor(total / 2);\n return Math.abs(center - i) * stagger;\n }\n case 'first':\n default:\n return i * stagger;\n }\n }\n}\n\nexport type { WrRotatingTextSplit, WrRotatingTextStaggerFrom } from './interfaces';\n","<!-- Screen-reader-only live region with the canonical text. -->\n<span class=\"wr-rotating-text__sr-only\">{{ current() }}</span>\n\n<!-- Animated visual content. -->\n<span class=\"wr-rotating-text__inner\" aria-hidden=\"true\">\n @for (word of words(); track $index) {\n <span class=\"wr-rotating-text__word\">\n @for (char of word.characters; track $index) {\n <span class=\"wr-rotating-text__char\">{{ char }}</span>\n }\n @if (word.needsSpace) {\n <span class=\"wr-rotating-text__space\">&nbsp;</span>\n }\n </span>\n }\n</span>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-rotating-text.mjs","sources":["../../../projects/lib/rotating-text/rotating-text.ts","../../../projects/lib/rotating-text/rotating-text.html","../../../projects/lib/rotating-text/ngwr-rotating-text.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n *\n * Angular adaptation of the RotatingText effect by David Haz / reactbits.dev.\n * Original: https://www.reactbits.dev/text-animations/rotating-text\n *\n * The reactbits version uses `motion/react` + `AnimatePresence`. This\n * port is dependency-free — vanilla DOM split + Web Animations API for\n * the enter/exit transitions.\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n ElementRef,\n Injector,\n PLATFORM_ID,\n ViewEncapsulation,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { numAttr } from 'ngwr/utils';\n\nimport type { WrRotatingTextSplit, WrRotatingTextStaggerFrom } from './interfaces';\n\nconst DEFAULT_EASING = 'cubic-bezier(0.16, 1, 0.3, 1)';\n\ninterface Word {\n readonly characters: readonly string[];\n readonly needsSpace: boolean;\n}\n\nfunction graphemes(text: string): readonly string[] {\n if (typeof Intl !== 'undefined' && 'Segmenter' in Intl) {\n const seg = new Intl.Segmenter('en', { granularity: 'grapheme' });\n return Array.from(seg.segment(text), s => s.segment);\n }\n return Array.from(text);\n}\n\nfunction splitWords(text: string, by: WrRotatingTextSplit): readonly Word[] {\n if (!text) return [];\n if (by === 'lines') {\n const lines = text.split('\\n');\n return lines.map((line, i) => ({ characters: [line], needsSpace: i !== lines.length - 1 }));\n }\n if (by === 'words') {\n const words = text.split(' ');\n return words.map((w, i) => ({ characters: [w], needsSpace: i !== words.length - 1 }));\n }\n // characters: split each word into graphemes, keep word boundaries for spacing.\n const words = text.split(' ');\n return words.map((w, i) => ({ characters: graphemes(w), needsSpace: i !== words.length - 1 }));\n}\n\n/**\n * Cycles through a list of strings, animating each transition\n * character-by-character (or word/line). Auto-advances on a timer by\n * default; can also be controlled imperatively via `next()` /\n * `previous()` / `jumpTo()` / `reset()`.\n *\n * @example\n * ```html\n * <wr-rotating-text [texts]=\"['design', 'ship', 'iterate']\" />\n *\n * // Manual control:\n * @ViewChild(WrRotatingText) rotator!: WrRotatingText;\n * onClick(): void { this.rotator.next(); }\n * ```\n *\n * @see https://www.reactbits.dev/text-animations/rotating-text\n */\n@Component({\n selector: 'wr-rotating-text',\n templateUrl: './rotating-text.html',\n styleUrl: './rotating-text.scss',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-rotating-text' },\n})\nexport class WrRotatingText {\n /** Strings to cycle. Required. */\n readonly texts = input.required<readonly string[]>();\n\n /** Auto-advance interval in ms. @default 2000 */\n readonly rotationInterval = input(2000, { transform: numAttr(2000) });\n\n /** Granularity of the split. @default 'characters' */\n readonly splitBy = input<WrRotatingTextSplit>('characters');\n\n /** Auto-advance on a timer. @default true */\n readonly auto = input(true, { transform: coerceBooleanProperty });\n\n /** Loop back to the first string after the last. @default true */\n readonly loop = input(true, { transform: coerceBooleanProperty });\n\n /** Per-swap tween duration in seconds. @default 0.6 */\n readonly duration = input(0.6, { transform: numAttr(0.6) });\n\n /** CSS easing of the per-piece tween. @default 'cubic-bezier(0.16, 1, 0.3, 1)' (~power3.out) */\n readonly easing = input(DEFAULT_EASING);\n\n /** Per-piece stagger in seconds. @default 0 */\n readonly staggerDuration = input(0, { transform: numAttr(0) });\n\n /** Stagger origin. @default 'first' */\n readonly staggerFrom = input<WrRotatingTextStaggerFrom>('first');\n\n /** Emitted with the new index on every rotation. */\n readonly nextChange = output<number>();\n\n protected readonly index = signal(0);\n\n protected readonly current = computed(() => {\n const arr = this.texts();\n return arr.length === 0 ? '' : arr[Math.min(this.index(), arr.length - 1)];\n });\n\n protected readonly words = computed<readonly Word[]>(() => splitWords(this.current(), this.splitBy()));\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly platform = inject(WrPlatform);\n\n private timerId: ReturnType<typeof setInterval> | undefined;\n private isAnimating = false;\n\n constructor() {\n if (!this.isBrowser) return;\n\n afterNextRender(() => this.animateIn());\n\n // Restart the timer when interval / auto change.\n effect(() => {\n this.rotationInterval();\n this.auto();\n this.restartTimer();\n });\n\n this.destroyRef.onDestroy(() => clearInterval(this.timerId));\n }\n\n // Public imperative API\n\n next(): void {\n const arr = this.texts();\n if (arr.length === 0) return;\n const cur = this.index();\n const last = arr.length - 1;\n const target = cur === last ? (this.loop() ? 0 : cur) : cur + 1;\n if (target !== cur) void this.transitionTo(target);\n }\n\n previous(): void {\n const arr = this.texts();\n if (arr.length === 0) return;\n const cur = this.index();\n const last = arr.length - 1;\n const target = cur === 0 ? (this.loop() ? last : cur) : cur - 1;\n if (target !== cur) void this.transitionTo(target);\n }\n\n jumpTo(target: number): void {\n const arr = this.texts();\n if (arr.length === 0) return;\n const clamped = Math.max(0, Math.min(target, arr.length - 1));\n if (clamped !== this.index()) void this.transitionTo(clamped);\n }\n\n reset(): void {\n if (this.index() !== 0) void this.transitionTo(0);\n }\n\n // Internals\n\n private restartTimer(): void {\n clearInterval(this.timerId);\n if (!this.auto()) return;\n this.timerId = setInterval(() => this.next(), this.rotationInterval());\n }\n\n private async transitionTo(target: number): Promise<void> {\n if (this.isAnimating) return;\n this.isAnimating = true;\n\n await this.animateOut();\n this.index.set(target);\n this.nextChange.emit(target);\n // Animate in only AFTER Angular has rendered the new word's spans. A single\n // microtask isn't enough under zoneless change detection — the re-render\n // hasn't flushed yet, so `animateIn()` would query the OLD spans, animate\n // them, and then the render would swap their content mid-flight (and any\n // spans the longer word adds would never get animated). `afterNextRender`\n // fires after the render the `index` change schedules.\n afterNextRender(\n () => {\n this.animateIn();\n this.isAnimating = false;\n },\n { injector: this.injector }\n );\n }\n\n private animateIn(): void {\n const targets = this.host.nativeElement.querySelectorAll<HTMLElement>('.wr-rotating-text__char');\n if (targets.length === 0) return;\n\n // Reduced motion: the word swap itself is content and keeps cycling,\n // but each swap is instant — no per-char rise/fall tween.\n if (this.platform.prefersReducedMotion()) {\n targets.forEach(el => {\n el.style.opacity = '1';\n el.style.transform = 'translate3d(0, 0, 0)';\n });\n return;\n }\n\n const stagger = this.staggerDuration() * 1000;\n targets.forEach((el, i) => {\n const delay = this.delayFor(i, targets.length, stagger);\n el.style.opacity = '0';\n el.style.transform = 'translate3d(0, 100%, 0)';\n const anim = el.animate(\n [\n { opacity: 0, transform: 'translate3d(0, 100%, 0)' },\n { opacity: 1, transform: 'translate3d(0, 0, 0)' },\n ],\n { duration: this.duration() * 1000, delay, easing: this.easing(), fill: 'forwards' }\n );\n anim.onfinish = (): void => {\n el.style.opacity = '1';\n el.style.transform = 'translate3d(0, 0, 0)';\n try {\n anim.cancel();\n } catch {\n /* noop */\n }\n };\n });\n }\n\n private animateOut(): Promise<void> {\n const targets = this.host.nativeElement.querySelectorAll<HTMLElement>('.wr-rotating-text__char');\n if (targets.length === 0) return Promise.resolve();\n if (this.platform.prefersReducedMotion()) return Promise.resolve();\n const stagger = this.staggerDuration() * 1000;\n const promises: Promise<void>[] = [];\n targets.forEach((el, i) => {\n const delay = this.delayFor(i, targets.length, stagger);\n const anim = el.animate(\n [\n { opacity: 1, transform: 'translate3d(0, 0, 0)' },\n { opacity: 0, transform: 'translate3d(0, -120%, 0)' },\n ],\n { duration: this.duration() * 1000, delay, easing: this.easing(), fill: 'forwards' }\n );\n promises.push(\n new Promise<void>(resolve => {\n anim.onfinish = (): void => {\n // Commit the exited (hidden, lifted) state to inline style before\n // cancelling, so the char stays out instead of snapping back to\n // visible-in-place between the swap and the enter animation.\n el.style.opacity = '0';\n el.style.transform = 'translate3d(0, -120%, 0)';\n try {\n anim.cancel();\n } catch {\n /* noop */\n }\n resolve();\n };\n })\n );\n });\n return Promise.all(promises).then(() => undefined);\n }\n\n private delayFor(i: number, total: number, stagger: number): number {\n if (stagger === 0) return 0;\n switch (this.staggerFrom()) {\n case 'last':\n return (total - 1 - i) * stagger;\n case 'center': {\n const center = Math.floor(total / 2);\n return Math.abs(center - i) * stagger;\n }\n case 'first':\n default:\n return i * stagger;\n }\n }\n}\n\nexport type { WrRotatingTextSplit, WrRotatingTextStaggerFrom } from './interfaces';\n","<!-- Screen-reader-only live region with the canonical text. -->\n<span class=\"wr-rotating-text__sr-only\">{{ current() }}</span>\n\n<!-- Animated visual content. -->\n<span class=\"wr-rotating-text__inner\" aria-hidden=\"true\">\n @for (word of words(); track $index) {\n <span class=\"wr-rotating-text__word\">\n @for (char of word.characters; track $index) {\n <span class=\"wr-rotating-text__char\">{{ char }}</span>\n }\n @if (word.needsSpace) {\n <span class=\"wr-rotating-text__space\">&nbsp;</span>\n }\n </span>\n }\n</span>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -172,6 +172,12 @@ class WrTable {
172
172
  ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
173
173
  /** Show the loading spinner overlay. @default false */
174
174
  loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
175
+ /**
176
+ * Collapse each row to a labelled card when the table's own box is too narrow
177
+ * for columns (a container query on its own width, not the viewport). Every
178
+ * cell shows its column title as a label. @default false
179
+ */
180
+ responsive = input(false, { ...(ngDevMode ? { debugName: "responsive" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
175
181
  /** Two-way bindable sort array. Order in array = application order. */
176
182
  sort = model([], /* @ts-ignore */
177
183
  ...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
@@ -254,12 +260,12 @@ class WrTable {
254
260
  return this.cellMap().get(String(key));
255
261
  }
256
262
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
257
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: WrTable, isStandalone: true, selector: "wr-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", 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 }, totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sortChange", filterChange: "filterChange", page: "pageChange" }, host: { classAttribute: "wr-table" }, queries: [{ propertyName: "cellTemplates", predicate: WrTableCell, isSignal: true }], ngImport: i0, template: "@if (loading()) {\n <div class=\"wr-table__loading\">\n <wr-spinner size=\"lg\" />\n </div>\n}\n\n<div class=\"wr-table__scroll\">\n <table class=\"wr-table__table\">\n <thead>\n <tr>\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <th class=\"wr-table__th\">\n <div class=\"wr-table__th-inner\">\n <span class=\"wr-table__title\">{{ entry.value.title }}</span>\n\n @if (entry.value.sortable) {\n <button\n type=\"button\"\n class=\"wr-table__sort-btn\"\n aria-label=\"Sort column\"\n (click)=\"cycleSort(entry.key)\"\n >\n <wr-table-sort [direction]=\"directionFor(entry.key)\" />\n </button>\n }\n\n @if (entry.value.filterItems; as filterItems) {\n @if (filterItems.length) {\n <wr-table-filter [items]=\"filterItems\" (selectionChange)=\"onFilter(entry.key, $event)\" />\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody>\n @if (visibleItems(); as rows) {\n @for (row of rows; track $index) {\n <tr class=\"wr-table__tr\">\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <td class=\"wr-table__td\">\n @if (templateFor(entry.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"cellContext(row, entry.key, entry.value)\"\n />\n } @else {\n {{ getValue(row, entry.key) }}\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td class=\"wr-table__empty\" [attr.colspan]=\"(columns() | keyvalue).length\">No data</td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPager()) {\n <div class=\"wr-table__pager\">\n <wr-pagination [(currentPage)]=\"page\" [pageSize]=\"pageSize()\" [total]=\"resolvedTotal()\" />\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: WrPagination, selector: "wr-pagination", inputs: ["currentPage", "pageSize", "total", "pageSizeOptions", "showSizeChanger", "showTotal", "align", "size", "shape", "disabled", "ofLabel", "prevLabel", "nextLabel", "itemsPerPageLabel"], outputs: ["currentPageChange", "pageSizeChange"] }, { kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size"] }, { kind: "component", type: WrTableSort, selector: "wr-table-sort", inputs: ["direction"] }, { kind: "component", type: WrTableFilter, selector: "wr-table-filter", inputs: ["items"], outputs: ["selectionChange"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], encapsulation: i0.ViewEncapsulation.None });
263
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: WrTable, isStandalone: true, selector: "wr-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", 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 }, totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sortChange", filterChange: "filterChange", page: "pageChange" }, host: { properties: { "class.wr-table--responsive": "responsive()" }, classAttribute: "wr-table" }, queries: [{ propertyName: "cellTemplates", predicate: WrTableCell, isSignal: true }], ngImport: i0, template: "@if (loading()) {\n <div class=\"wr-table__loading\">\n <wr-spinner size=\"lg\" />\n </div>\n}\n\n<div class=\"wr-table__scroll\">\n <table class=\"wr-table__table\">\n <thead>\n <tr>\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <th class=\"wr-table__th\">\n <div class=\"wr-table__th-inner\">\n <span class=\"wr-table__title\">{{ entry.value.title }}</span>\n\n @if (entry.value.sortable) {\n <button\n type=\"button\"\n class=\"wr-table__sort-btn\"\n aria-label=\"Sort column\"\n (click)=\"cycleSort(entry.key)\"\n >\n <wr-table-sort [direction]=\"directionFor(entry.key)\" />\n </button>\n }\n\n @if (entry.value.filterItems; as filterItems) {\n @if (filterItems.length) {\n <wr-table-filter [items]=\"filterItems\" (selectionChange)=\"onFilter(entry.key, $event)\" />\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody>\n @if (visibleItems(); as rows) {\n @for (row of rows; track $index) {\n <tr class=\"wr-table__tr\">\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <td class=\"wr-table__td\" [attr.data-label]=\"entry.value.title\">\n @if (templateFor(entry.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"cellContext(row, entry.key, entry.value)\"\n />\n } @else {\n {{ getValue(row, entry.key) }}\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td class=\"wr-table__empty\" [attr.colspan]=\"(columns() | keyvalue).length\">No data</td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPager()) {\n <div class=\"wr-table__pager\">\n <wr-pagination [(currentPage)]=\"page\" [pageSize]=\"pageSize()\" [total]=\"resolvedTotal()\" />\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: WrPagination, selector: "wr-pagination", inputs: ["currentPage", "pageSize", "total", "pageSizeOptions", "showSizeChanger", "showTotal", "align", "size", "shape", "disabled", "responsive", "ofLabel", "prevLabel", "nextLabel", "itemsPerPageLabel"], outputs: ["currentPageChange", "pageSizeChange"] }, { kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size"] }, { kind: "component", type: WrTableSort, selector: "wr-table-sort", inputs: ["direction"] }, { kind: "component", type: WrTableFilter, selector: "wr-table-filter", inputs: ["items"], outputs: ["selectionChange"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], encapsulation: i0.ViewEncapsulation.None });
258
264
  }
259
265
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: WrTable, decorators: [{
260
266
  type: Component,
261
- args: [{ selector: 'wr-table', encapsulation: ViewEncapsulation.None, host: { class: 'wr-table' }, imports: [NgTemplateOutlet, KeyValuePipe, WrPagination, WrSpinner, WrTableSort, WrTableFilter], template: "@if (loading()) {\n <div class=\"wr-table__loading\">\n <wr-spinner size=\"lg\" />\n </div>\n}\n\n<div class=\"wr-table__scroll\">\n <table class=\"wr-table__table\">\n <thead>\n <tr>\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <th class=\"wr-table__th\">\n <div class=\"wr-table__th-inner\">\n <span class=\"wr-table__title\">{{ entry.value.title }}</span>\n\n @if (entry.value.sortable) {\n <button\n type=\"button\"\n class=\"wr-table__sort-btn\"\n aria-label=\"Sort column\"\n (click)=\"cycleSort(entry.key)\"\n >\n <wr-table-sort [direction]=\"directionFor(entry.key)\" />\n </button>\n }\n\n @if (entry.value.filterItems; as filterItems) {\n @if (filterItems.length) {\n <wr-table-filter [items]=\"filterItems\" (selectionChange)=\"onFilter(entry.key, $event)\" />\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody>\n @if (visibleItems(); as rows) {\n @for (row of rows; track $index) {\n <tr class=\"wr-table__tr\">\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <td class=\"wr-table__td\">\n @if (templateFor(entry.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"cellContext(row, entry.key, entry.value)\"\n />\n } @else {\n {{ getValue(row, entry.key) }}\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td class=\"wr-table__empty\" [attr.colspan]=\"(columns() | keyvalue).length\">No data</td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPager()) {\n <div class=\"wr-table__pager\">\n <wr-pagination [(currentPage)]=\"page\" [pageSize]=\"pageSize()\" [total]=\"resolvedTotal()\" />\n </div>\n}\n" }]
262
- }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: false }] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrTableCell), { isSignal: true }] }] } });
267
+ args: [{ selector: 'wr-table', encapsulation: ViewEncapsulation.None, host: { class: 'wr-table', '[class.wr-table--responsive]': 'responsive()' }, imports: [NgTemplateOutlet, KeyValuePipe, WrPagination, WrSpinner, WrTableSort, WrTableFilter], template: "@if (loading()) {\n <div class=\"wr-table__loading\">\n <wr-spinner size=\"lg\" />\n </div>\n}\n\n<div class=\"wr-table__scroll\">\n <table class=\"wr-table__table\">\n <thead>\n <tr>\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <th class=\"wr-table__th\">\n <div class=\"wr-table__th-inner\">\n <span class=\"wr-table__title\">{{ entry.value.title }}</span>\n\n @if (entry.value.sortable) {\n <button\n type=\"button\"\n class=\"wr-table__sort-btn\"\n aria-label=\"Sort column\"\n (click)=\"cycleSort(entry.key)\"\n >\n <wr-table-sort [direction]=\"directionFor(entry.key)\" />\n </button>\n }\n\n @if (entry.value.filterItems; as filterItems) {\n @if (filterItems.length) {\n <wr-table-filter [items]=\"filterItems\" (selectionChange)=\"onFilter(entry.key, $event)\" />\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody>\n @if (visibleItems(); as rows) {\n @for (row of rows; track $index) {\n <tr class=\"wr-table__tr\">\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <td class=\"wr-table__td\" [attr.data-label]=\"entry.value.title\">\n @if (templateFor(entry.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"cellContext(row, entry.key, entry.value)\"\n />\n } @else {\n {{ getValue(row, entry.key) }}\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td class=\"wr-table__empty\" [attr.colspan]=\"(columns() | keyvalue).length\">No data</td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPager()) {\n <div class=\"wr-table__pager\">\n <wr-pagination [(currentPage)]=\"page\" [pageSize]=\"pageSize()\" [total]=\"resolvedTotal()\" />\n </div>\n}\n" }]
268
+ }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: false }] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => WrTableCell), { isSignal: true }] }] } });
263
269
 
264
270
  /**
265
271
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-table.mjs","sources":["../../../projects/lib/table/table-cell.ts","../../../projects/lib/table/table-filter.ts","../../../projects/lib/table/table-filter.html","../../../projects/lib/table/table-sort.ts","../../../projects/lib/table/table.ts","../../../projects/lib/table/table.html","../../../projects/lib/table/ngwr-table.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, TemplateRef, inject, input } from '@angular/core';\n\nimport type { WrTableCellContext } from './interfaces';\n\n/**\n * Provide a custom template for a specific column's cells.\n *\n * @example\n * ```html\n * <wr-table [columns]=\"columns\" [items]=\"items\">\n * <ng-template wrTableCell=\"role\" let-value let-row=\"item\">\n * <wr-tag [color]=\"value === 'admin' ? 'danger' : 'medium'\">{{ value }}</wr-tag>\n * </ng-template>\n * </wr-table>\n * ```\n */\n@Directive({\n selector: '[wrTableCell]',\n})\nexport class WrTableCell {\n /** Column key the template applies to. */\n readonly columnKey = input.required<string>({ alias: 'wrTableCell' });\n\n readonly template = inject<TemplateRef<WrTableCellContext>>(TemplateRef);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input, output, signal } from '@angular/core';\n\nimport { WrDropdown, WrDropdownMenu } from 'ngwr/dropdown';\n\nimport type { WrTableFilterItem } from './interfaces';\n\n/**\n * Filter dropdown rendered in a filterable column's header.\n *\n * @internal — used internally by `<wr-table>`.\n */\n@Component({\n selector: 'wr-table-filter',\n templateUrl: './table-filter.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrDropdown, WrDropdownMenu],\n})\nexport class WrTableFilter {\n /** Available filter options. */\n readonly items = input.required<readonly WrTableFilterItem[]>();\n\n /** Fires whenever the selection changes. */\n readonly selectionChange = output<readonly WrTableFilterItem[]>();\n\n protected readonly query = signal('');\n\n /** Filtered + selection-tracked view of items. */\n protected readonly visible = computed<WrTableFilterItem[]>(() => {\n const q = this.query().trim().toLowerCase();\n const all = this.items();\n if (!q) return [...all];\n return all.filter(i => i.title.toLowerCase().includes(q));\n });\n\n protected readonly selectedCount = computed(() => this.items().filter(i => i.selected).length);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-table-filter'];\n if (this.selectedCount() > 0) parts.push('wr-table-filter--active');\n return parts.join(' ');\n });\n\n protected onSearchInput(event: Event): void {\n this.query.set((event.target as HTMLInputElement).value);\n }\n\n protected onToggle(item: WrTableFilterItem): void {\n item.selected = !item.selected;\n this.emitSelected();\n }\n\n protected onReset(): void {\n for (const item of this.items()) item.selected = false;\n this.selectionChange.emit([]);\n }\n\n private emitSelected(): void {\n this.selectionChange.emit(this.items().filter(i => i.selected));\n }\n}\n","<button type=\"button\" class=\"wr-table-filter__trigger\" [wrDropdown]=\"menu\" aria-label=\"Filter column\">\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z\"\n />\n </svg>\n @if (selectedCount() > 0) {\n <span class=\"wr-table-filter__count\">{{ selectedCount() }}</span>\n }\n</button>\n\n<wr-dropdown-menu #menu>\n <div class=\"wr-table-filter__panel\">\n <input\n class=\"wr-table-filter__search\"\n type=\"search\"\n placeholder=\"Search\"\n [value]=\"query()\"\n (input)=\"onSearchInput($event)\"\n />\n\n <div class=\"wr-table-filter__list\">\n @for (item of visible(); track item.value) {\n <button\n type=\"button\"\n class=\"wr-table-filter__item\"\n [class.wr-table-filter__item--selected]=\"item.selected\"\n (click)=\"onToggle(item)\"\n >\n <span class=\"wr-table-filter__check\">\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n <span class=\"wr-table-filter__title\">{{ item.title }}</span>\n </button>\n } @empty {\n <div class=\"wr-table-filter__empty\">No matches</div>\n }\n </div>\n\n @if (selectedCount() > 0) {\n <button type=\"button\" class=\"wr-table-filter__reset\" (click)=\"onReset()\">Reset</button>\n }\n </div>\n</wr-dropdown-menu>\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport type { WrTableSortDirection } from './interfaces';\n\n/**\n * Sort indicator rendered in a sortable column's header.\n *\n * @internal — used internally by `<wr-table>`. Parent listens for clicks\n * on the indicator to cycle direction.\n */\n@Component({\n selector: 'wr-table-sort',\n template:\n '<svg class=\"wr-icon__svg wr-table-sort__icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m5 12 7-7 7 7\"/><path d=\"M12 19V5\"/></svg>',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrTableSort {\n readonly direction = input<WrTableSortDirection>(null);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-table-sort'];\n const dir = this.direction();\n if (dir) parts.push(`wr-table-sort--${dir}`);\n return parts.join(' ');\n });\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { KeyValuePipe, NgTemplateOutlet } from '@angular/common';\nimport type { TemplateRef } from '@angular/core';\nimport { Component, ViewEncapsulation, computed, contentChildren, input, model, output } from '@angular/core';\n\nimport { WrPagination } from 'ngwr/pagination';\nimport { WrSpinner } from 'ngwr/spinner';\n\nimport type {\n WrTableCellContext,\n WrTableColumns,\n WrTableFilterChange,\n WrTableFilterItem,\n WrTableSortState,\n WrTableSortDirection,\n} from './interfaces';\nimport { WrTableCell } from './table-cell';\nimport { WrTableFilter } from './table-filter';\nimport { WrTableSort } from './table-sort';\n\n/**\n * Data table with sortable / filterable headers and custom cell templates.\n *\n * Columns are defined via the `columns` input (a `Record<key, column>`).\n * Rows are objects whose property names match the column keys. Cells can\n * be customised by projecting an `[wrTableCell]=\"key\"` template.\n *\n * @example\n * ```html\n * <wr-table\n * [columns]=\"columns\"\n * [items]=\"rows\"\n * [(sort)]=\"sort\"\n * (filterChange)=\"onFilter($event)\"\n * >\n * <ng-template wrTableCell=\"role\" let-value>\n * <wr-tag [color]=\"value === 'admin' ? 'danger' : 'medium'\">{{ value }}</wr-tag>\n * </ng-template>\n * </wr-table>\n * ```\n *\n * @see https://ngwr.dev/components/table\n */\n@Component({\n selector: 'wr-table',\n templateUrl: './table.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-table' },\n imports: [NgTemplateOutlet, KeyValuePipe, WrPagination, WrSpinner, WrTableSort, WrTableFilter],\n})\nexport class WrTable {\n /** Column definitions, keyed by row property name. */\n readonly columns = input.required<WrTableColumns>();\n\n /** Row items. `null`/`undefined` → loading skeleton. */\n readonly items = input<readonly Record<string, unknown>[] | null | undefined>(null);\n\n /** Show the loading spinner overlay. @default false */\n readonly loading = input(false, { transform: coerceBooleanProperty });\n\n /** Two-way bindable sort array. Order in array = application order. */\n readonly sort = model<readonly WrTableSortState[]>([]);\n\n /** Fires whenever a column's filter selection changes. */\n readonly filterChange = output<WrTableFilterChange>();\n\n /**\n * Rows per page. Set to `0` (default) to disable client-side pagination\n * and render every row at once.\n */\n readonly pageSize = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /** Two-way bindable 1-based current page. */\n readonly page = model<number>(1);\n\n /**\n * Total row count for server-side pagination — when set, the table\n * shows the pager but does NOT slice `items` (you provide the current\n * page's slice yourself and react to `(page)` changes).\n */\n readonly totalItems = input<number | null>(null);\n\n /** Total row count derived for the pager (server-mode wins). */\n protected readonly resolvedTotal = computed<number>(() => {\n const server = this.totalItems();\n if (server !== null) return server;\n return this.items()?.length ?? 0;\n });\n\n /** Items visible on the current page (client mode slices; server mode passes through). */\n protected readonly visibleItems = computed<readonly Record<string, unknown>[] | null | undefined>(() => {\n const items = this.items();\n if (!items) return items;\n const size = this.pageSize();\n if (size <= 0 || this.totalItems() !== null) return items;\n const start = (this.page() - 1) * size;\n return items.slice(start, start + size);\n });\n\n /** Show the pager footer only when paging is enabled and rows overflow. */\n protected readonly showPager = computed<boolean>(() => {\n const size = this.pageSize();\n return size > 0 && this.resolvedTotal() > size;\n });\n\n private readonly cellTemplates = contentChildren(WrTableCell);\n\n /** Map of column key → custom template (if provided via `[wrTableCell]`). */\n protected readonly cellMap = computed(() => {\n const map = new Map<string, TemplateRef<WrTableCellContext>>();\n for (const cell of this.cellTemplates()) map.set(cell.columnKey(), cell.template);\n return map;\n });\n\n /** Keep KeyValuePipe in declaration order (comparator that treats all keys as equal). */\n protected readonly keepOrder = (): number => 0;\n\n protected directionFor(key: string): WrTableSortDirection {\n return this.sort().find(s => s.key === key)?.direction ?? null;\n }\n\n protected cycleSort(key: string): void {\n const current = this.directionFor(key);\n const next: WrTableSortDirection = current === null ? 'asc' : current === 'asc' ? 'desc' : null;\n\n const list = this.sort().filter(s => s.key !== key);\n if (next !== null) list.push({ key, direction: next });\n this.sort.set(list);\n }\n\n protected onFilter(key: string, items: readonly WrTableFilterItem[]): void {\n this.filterChange.emit({ key, items });\n }\n\n protected cellContext(item: Record<string, unknown>, key: string, column: unknown): WrTableCellContext {\n return { $implicit: item[key], item, column: column as WrTableCellContext['column'] };\n }\n\n protected getValue(item: Record<string, unknown>, key: unknown): unknown {\n return item[String(key)];\n }\n\n protected templateFor(key: unknown): TemplateRef<WrTableCellContext> | undefined {\n return this.cellMap().get(String(key));\n }\n}\n","@if (loading()) {\n <div class=\"wr-table__loading\">\n <wr-spinner size=\"lg\" />\n </div>\n}\n\n<div class=\"wr-table__scroll\">\n <table class=\"wr-table__table\">\n <thead>\n <tr>\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <th class=\"wr-table__th\">\n <div class=\"wr-table__th-inner\">\n <span class=\"wr-table__title\">{{ entry.value.title }}</span>\n\n @if (entry.value.sortable) {\n <button\n type=\"button\"\n class=\"wr-table__sort-btn\"\n aria-label=\"Sort column\"\n (click)=\"cycleSort(entry.key)\"\n >\n <wr-table-sort [direction]=\"directionFor(entry.key)\" />\n </button>\n }\n\n @if (entry.value.filterItems; as filterItems) {\n @if (filterItems.length) {\n <wr-table-filter [items]=\"filterItems\" (selectionChange)=\"onFilter(entry.key, $event)\" />\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody>\n @if (visibleItems(); as rows) {\n @for (row of rows; track $index) {\n <tr class=\"wr-table__tr\">\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <td class=\"wr-table__td\">\n @if (templateFor(entry.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"cellContext(row, entry.key, entry.value)\"\n />\n } @else {\n {{ getValue(row, entry.key) }}\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td class=\"wr-table__empty\" [attr.colspan]=\"(columns() | keyvalue).length\">No data</td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPager()) {\n <div class=\"wr-table__pager\">\n <wr-pagination [(currentPage)]=\"page\" [pageSize]=\"pageSize()\" [total]=\"resolvedTotal()\" />\n </div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-table.mjs","sources":["../../../projects/lib/table/table-cell.ts","../../../projects/lib/table/table-filter.ts","../../../projects/lib/table/table-filter.html","../../../projects/lib/table/table-sort.ts","../../../projects/lib/table/table.ts","../../../projects/lib/table/table.html","../../../projects/lib/table/ngwr-table.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, TemplateRef, inject, input } from '@angular/core';\n\nimport type { WrTableCellContext } from './interfaces';\n\n/**\n * Provide a custom template for a specific column's cells.\n *\n * @example\n * ```html\n * <wr-table [columns]=\"columns\" [items]=\"items\">\n * <ng-template wrTableCell=\"role\" let-value let-row=\"item\">\n * <wr-tag [color]=\"value === 'admin' ? 'danger' : 'medium'\">{{ value }}</wr-tag>\n * </ng-template>\n * </wr-table>\n * ```\n */\n@Directive({\n selector: '[wrTableCell]',\n})\nexport class WrTableCell {\n /** Column key the template applies to. */\n readonly columnKey = input.required<string>({ alias: 'wrTableCell' });\n\n readonly template = inject<TemplateRef<WrTableCellContext>>(TemplateRef);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input, output, signal } from '@angular/core';\n\nimport { WrDropdown, WrDropdownMenu } from 'ngwr/dropdown';\n\nimport type { WrTableFilterItem } from './interfaces';\n\n/**\n * Filter dropdown rendered in a filterable column's header.\n *\n * @internal — used internally by `<wr-table>`.\n */\n@Component({\n selector: 'wr-table-filter',\n templateUrl: './table-filter.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n imports: [WrDropdown, WrDropdownMenu],\n})\nexport class WrTableFilter {\n /** Available filter options. */\n readonly items = input.required<readonly WrTableFilterItem[]>();\n\n /** Fires whenever the selection changes. */\n readonly selectionChange = output<readonly WrTableFilterItem[]>();\n\n protected readonly query = signal('');\n\n /** Filtered + selection-tracked view of items. */\n protected readonly visible = computed<WrTableFilterItem[]>(() => {\n const q = this.query().trim().toLowerCase();\n const all = this.items();\n if (!q) return [...all];\n return all.filter(i => i.title.toLowerCase().includes(q));\n });\n\n protected readonly selectedCount = computed(() => this.items().filter(i => i.selected).length);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-table-filter'];\n if (this.selectedCount() > 0) parts.push('wr-table-filter--active');\n return parts.join(' ');\n });\n\n protected onSearchInput(event: Event): void {\n this.query.set((event.target as HTMLInputElement).value);\n }\n\n protected onToggle(item: WrTableFilterItem): void {\n item.selected = !item.selected;\n this.emitSelected();\n }\n\n protected onReset(): void {\n for (const item of this.items()) item.selected = false;\n this.selectionChange.emit([]);\n }\n\n private emitSelected(): void {\n this.selectionChange.emit(this.items().filter(i => i.selected));\n }\n}\n","<button type=\"button\" class=\"wr-table-filter__trigger\" [wrDropdown]=\"menu\" aria-label=\"Filter column\">\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z\"\n />\n </svg>\n @if (selectedCount() > 0) {\n <span class=\"wr-table-filter__count\">{{ selectedCount() }}</span>\n }\n</button>\n\n<wr-dropdown-menu #menu>\n <div class=\"wr-table-filter__panel\">\n <input\n class=\"wr-table-filter__search\"\n type=\"search\"\n placeholder=\"Search\"\n [value]=\"query()\"\n (input)=\"onSearchInput($event)\"\n />\n\n <div class=\"wr-table-filter__list\">\n @for (item of visible(); track item.value) {\n <button\n type=\"button\"\n class=\"wr-table-filter__item\"\n [class.wr-table-filter__item--selected]=\"item.selected\"\n (click)=\"onToggle(item)\"\n >\n <span class=\"wr-table-filter__check\">\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n <span class=\"wr-table-filter__title\">{{ item.title }}</span>\n </button>\n } @empty {\n <div class=\"wr-table-filter__empty\">No matches</div>\n }\n </div>\n\n @if (selectedCount() > 0) {\n <button type=\"button\" class=\"wr-table-filter__reset\" (click)=\"onReset()\">Reset</button>\n }\n </div>\n</wr-dropdown-menu>\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport type { WrTableSortDirection } from './interfaces';\n\n/**\n * Sort indicator rendered in a sortable column's header.\n *\n * @internal — used internally by `<wr-table>`. Parent listens for clicks\n * on the indicator to cycle direction.\n */\n@Component({\n selector: 'wr-table-sort',\n template:\n '<svg class=\"wr-icon__svg wr-table-sort__icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m5 12 7-7 7 7\"/><path d=\"M12 19V5\"/></svg>',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrTableSort {\n readonly direction = input<WrTableSortDirection>(null);\n\n protected readonly classes = computed(() => {\n const parts = ['wr-table-sort'];\n const dir = this.direction();\n if (dir) parts.push(`wr-table-sort--${dir}`);\n return parts.join(' ');\n });\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { KeyValuePipe, NgTemplateOutlet } from '@angular/common';\nimport type { TemplateRef } from '@angular/core';\nimport { Component, ViewEncapsulation, computed, contentChildren, input, model, output } from '@angular/core';\n\nimport { WrPagination } from 'ngwr/pagination';\nimport { WrSpinner } from 'ngwr/spinner';\n\nimport type {\n WrTableCellContext,\n WrTableColumns,\n WrTableFilterChange,\n WrTableFilterItem,\n WrTableSortState,\n WrTableSortDirection,\n} from './interfaces';\nimport { WrTableCell } from './table-cell';\nimport { WrTableFilter } from './table-filter';\nimport { WrTableSort } from './table-sort';\n\n/**\n * Data table with sortable / filterable headers and custom cell templates.\n *\n * Columns are defined via the `columns` input (a `Record<key, column>`).\n * Rows are objects whose property names match the column keys. Cells can\n * be customised by projecting an `[wrTableCell]=\"key\"` template.\n *\n * @example\n * ```html\n * <wr-table\n * [columns]=\"columns\"\n * [items]=\"rows\"\n * [(sort)]=\"sort\"\n * (filterChange)=\"onFilter($event)\"\n * >\n * <ng-template wrTableCell=\"role\" let-value>\n * <wr-tag [color]=\"value === 'admin' ? 'danger' : 'medium'\">{{ value }}</wr-tag>\n * </ng-template>\n * </wr-table>\n * ```\n *\n * @see https://ngwr.dev/components/table\n */\n@Component({\n selector: 'wr-table',\n templateUrl: './table.html',\n encapsulation: ViewEncapsulation.None,\n host: { class: 'wr-table', '[class.wr-table--responsive]': 'responsive()' },\n imports: [NgTemplateOutlet, KeyValuePipe, WrPagination, WrSpinner, WrTableSort, WrTableFilter],\n})\nexport class WrTable {\n /** Column definitions, keyed by row property name. */\n readonly columns = input.required<WrTableColumns>();\n\n /** Row items. `null`/`undefined` → loading skeleton. */\n readonly items = input<readonly Record<string, unknown>[] | null | undefined>(null);\n\n /** Show the loading spinner overlay. @default false */\n readonly loading = input(false, { transform: coerceBooleanProperty });\n\n /**\n * Collapse each row to a labelled card when the table's own box is too narrow\n * for columns (a container query on its own width, not the viewport). Every\n * cell shows its column title as a label. @default false\n */\n readonly responsive = input(false, { transform: coerceBooleanProperty });\n\n /** Two-way bindable sort array. Order in array = application order. */\n readonly sort = model<readonly WrTableSortState[]>([]);\n\n /** Fires whenever a column's filter selection changes. */\n readonly filterChange = output<WrTableFilterChange>();\n\n /**\n * Rows per page. Set to `0` (default) to disable client-side pagination\n * and render every row at once.\n */\n readonly pageSize = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n /** Two-way bindable 1-based current page. */\n readonly page = model<number>(1);\n\n /**\n * Total row count for server-side pagination — when set, the table\n * shows the pager but does NOT slice `items` (you provide the current\n * page's slice yourself and react to `(page)` changes).\n */\n readonly totalItems = input<number | null>(null);\n\n /** Total row count derived for the pager (server-mode wins). */\n protected readonly resolvedTotal = computed<number>(() => {\n const server = this.totalItems();\n if (server !== null) return server;\n return this.items()?.length ?? 0;\n });\n\n /** Items visible on the current page (client mode slices; server mode passes through). */\n protected readonly visibleItems = computed<readonly Record<string, unknown>[] | null | undefined>(() => {\n const items = this.items();\n if (!items) return items;\n const size = this.pageSize();\n if (size <= 0 || this.totalItems() !== null) return items;\n const start = (this.page() - 1) * size;\n return items.slice(start, start + size);\n });\n\n /** Show the pager footer only when paging is enabled and rows overflow. */\n protected readonly showPager = computed<boolean>(() => {\n const size = this.pageSize();\n return size > 0 && this.resolvedTotal() > size;\n });\n\n private readonly cellTemplates = contentChildren(WrTableCell);\n\n /** Map of column key → custom template (if provided via `[wrTableCell]`). */\n protected readonly cellMap = computed(() => {\n const map = new Map<string, TemplateRef<WrTableCellContext>>();\n for (const cell of this.cellTemplates()) map.set(cell.columnKey(), cell.template);\n return map;\n });\n\n /** Keep KeyValuePipe in declaration order (comparator that treats all keys as equal). */\n protected readonly keepOrder = (): number => 0;\n\n protected directionFor(key: string): WrTableSortDirection {\n return this.sort().find(s => s.key === key)?.direction ?? null;\n }\n\n protected cycleSort(key: string): void {\n const current = this.directionFor(key);\n const next: WrTableSortDirection = current === null ? 'asc' : current === 'asc' ? 'desc' : null;\n\n const list = this.sort().filter(s => s.key !== key);\n if (next !== null) list.push({ key, direction: next });\n this.sort.set(list);\n }\n\n protected onFilter(key: string, items: readonly WrTableFilterItem[]): void {\n this.filterChange.emit({ key, items });\n }\n\n protected cellContext(item: Record<string, unknown>, key: string, column: unknown): WrTableCellContext {\n return { $implicit: item[key], item, column: column as WrTableCellContext['column'] };\n }\n\n protected getValue(item: Record<string, unknown>, key: unknown): unknown {\n return item[String(key)];\n }\n\n protected templateFor(key: unknown): TemplateRef<WrTableCellContext> | undefined {\n return this.cellMap().get(String(key));\n }\n}\n","@if (loading()) {\n <div class=\"wr-table__loading\">\n <wr-spinner size=\"lg\" />\n </div>\n}\n\n<div class=\"wr-table__scroll\">\n <table class=\"wr-table__table\">\n <thead>\n <tr>\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <th class=\"wr-table__th\">\n <div class=\"wr-table__th-inner\">\n <span class=\"wr-table__title\">{{ entry.value.title }}</span>\n\n @if (entry.value.sortable) {\n <button\n type=\"button\"\n class=\"wr-table__sort-btn\"\n aria-label=\"Sort column\"\n (click)=\"cycleSort(entry.key)\"\n >\n <wr-table-sort [direction]=\"directionFor(entry.key)\" />\n </button>\n }\n\n @if (entry.value.filterItems; as filterItems) {\n @if (filterItems.length) {\n <wr-table-filter [items]=\"filterItems\" (selectionChange)=\"onFilter(entry.key, $event)\" />\n }\n }\n </div>\n </th>\n }\n </tr>\n </thead>\n\n <tbody>\n @if (visibleItems(); as rows) {\n @for (row of rows; track $index) {\n <tr class=\"wr-table__tr\">\n @for (entry of columns() | keyvalue: keepOrder; track entry.key) {\n <td class=\"wr-table__td\" [attr.data-label]=\"entry.value.title\">\n @if (templateFor(entry.key); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"cellContext(row, entry.key, entry.value)\"\n />\n } @else {\n {{ getValue(row, entry.key) }}\n }\n </td>\n }\n </tr>\n } @empty {\n <tr>\n <td class=\"wr-table__empty\" [attr.colspan]=\"(columns() | keyvalue).length\">No data</td>\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPager()) {\n <div class=\"wr-table__pager\">\n <wr-pagination [(currentPage)]=\"page\" [pageSize]=\"pageSize()\" [total]=\"resolvedTotal()\" />\n </div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}