@ngneers/controls 0.0.1-next.0 → 0.0.1-next.2

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 (119) hide show
  1. package/fesm2022/ngneers-controls-api-ng.mjs +11 -18
  2. package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -1
  3. package/fesm2022/ngneers-controls-api-resize.mjs +7 -0
  4. package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -1
  5. package/fesm2022/ngneers-controls-badge.mjs +160 -0
  6. package/fesm2022/ngneers-controls-badge.mjs.map +1 -0
  7. package/fesm2022/ngneers-controls-base.mjs +85 -8
  8. package/fesm2022/ngneers-controls-base.mjs.map +1 -1
  9. package/fesm2022/{ngneers-controls-calendar-time-CJT5AFlA.mjs → ngneers-controls-calendar-time-paM6WVpV.mjs} +2 -2
  10. package/fesm2022/{ngneers-controls-calendar-time-CJT5AFlA.mjs.map → ngneers-controls-calendar-time-paM6WVpV.mjs.map} +1 -1
  11. package/fesm2022/ngneers-controls-calendar.mjs +13 -6
  12. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -1
  13. package/fesm2022/ngneers-controls-checkbox.mjs +3 -3
  14. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -1
  15. package/fesm2022/ngneers-controls-color-picker.mjs +336 -0
  16. package/fesm2022/ngneers-controls-color-picker.mjs.map +1 -0
  17. package/fesm2022/ngneers-controls-default-icons.mjs +5 -0
  18. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -1
  19. package/fesm2022/ngneers-controls-directives.mjs +64 -7
  20. package/fesm2022/ngneers-controls-directives.mjs.map +1 -1
  21. package/fesm2022/ngneers-controls-edit-inplace.mjs +15 -5
  22. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -1
  23. package/fesm2022/ngneers-controls-errors.mjs +203 -148
  24. package/fesm2022/ngneers-controls-errors.mjs.map +1 -1
  25. package/fesm2022/ngneers-controls-filter.mjs +2 -2
  26. package/fesm2022/ngneers-controls-filter.mjs.map +1 -1
  27. package/fesm2022/ngneers-controls-hint.mjs +18 -1
  28. package/fesm2022/ngneers-controls-hint.mjs.map +1 -1
  29. package/fesm2022/ngneers-controls-i18n-translations-de.mjs +17 -0
  30. package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -1
  31. package/fesm2022/ngneers-controls-i18n-translations-en.mjs +19 -0
  32. package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -1
  33. package/fesm2022/ngneers-controls-icon.mjs +23 -6
  34. package/fesm2022/ngneers-controls-icon.mjs.map +1 -1
  35. package/fesm2022/ngneers-controls-input-field.mjs +18 -2
  36. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -1
  37. package/fesm2022/ngneers-controls-input.mjs +49 -9
  38. package/fesm2022/ngneers-controls-input.mjs.map +1 -1
  39. package/fesm2022/ngneers-controls-list-box.mjs +2 -2
  40. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -1
  41. package/fesm2022/ngneers-controls-mask-input.mjs +4 -1
  42. package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -1
  43. package/fesm2022/ngneers-controls-number-input.mjs +4 -4
  44. package/fesm2022/ngneers-controls-number-input.mjs.map +1 -1
  45. package/fesm2022/ngneers-controls-otp.mjs +263 -0
  46. package/fesm2022/ngneers-controls-otp.mjs.map +1 -0
  47. package/fesm2022/ngneers-controls-paginator.mjs +36 -9
  48. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -1
  49. package/fesm2022/ngneers-controls-radio.mjs +4 -4
  50. package/fesm2022/ngneers-controls-radio.mjs.map +1 -1
  51. package/fesm2022/ngneers-controls-rating.mjs +180 -0
  52. package/fesm2022/ngneers-controls-rating.mjs.map +1 -0
  53. package/fesm2022/ngneers-controls-scroll-shadow.mjs +33 -7
  54. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -1
  55. package/fesm2022/ngneers-controls-scroller.mjs +4 -2
  56. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -1
  57. package/fesm2022/ngneers-controls-select-button.mjs +2 -1
  58. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -1
  59. package/fesm2022/ngneers-controls-select.mjs +24 -4
  60. package/fesm2022/ngneers-controls-select.mjs.map +1 -1
  61. package/fesm2022/ngneers-controls-slider.mjs +3 -3
  62. package/fesm2022/ngneers-controls-slider.mjs.map +1 -1
  63. package/fesm2022/ngneers-controls-splitter.mjs +43 -12
  64. package/fesm2022/ngneers-controls-splitter.mjs.map +1 -1
  65. package/fesm2022/ngneers-controls-stepper.mjs +142 -0
  66. package/fesm2022/ngneers-controls-stepper.mjs.map +1 -0
  67. package/fesm2022/ngneers-controls-switch.mjs +3 -3
  68. package/fesm2022/ngneers-controls-switch.mjs.map +1 -1
  69. package/fesm2022/ngneers-controls-table.mjs +282 -15
  70. package/fesm2022/ngneers-controls-table.mjs.map +1 -1
  71. package/fesm2022/ngneers-controls-tabs.mjs +2 -2
  72. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -1
  73. package/fesm2022/ngneers-controls-toggle-button.mjs +3 -3
  74. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -1
  75. package/fesm2022/ngneers-controls-tooltip.mjs +16 -28
  76. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -1
  77. package/fesm2022/ngneers-controls-tree.mjs +2 -2
  78. package/fesm2022/ngneers-controls-tree.mjs.map +1 -1
  79. package/fesm2022/ngneers-controls-utils.mjs +138 -1
  80. package/fesm2022/ngneers-controls-utils.mjs.map +1 -1
  81. package/package.json +22 -2
  82. package/types/ngneers-controls-api-ng.d.ts +12 -9
  83. package/types/ngneers-controls-badge.d.ts +67 -0
  84. package/types/ngneers-controls-base.d.ts +61 -4
  85. package/types/ngneers-controls-breadcrumb.d.ts +17 -0
  86. package/types/ngneers-controls-calendar.d.ts +24 -6
  87. package/types/ngneers-controls-chip.d.ts +17 -0
  88. package/types/ngneers-controls-color-picker.d.ts +119 -0
  89. package/types/ngneers-controls-dialog.d.ts +17 -0
  90. package/types/ngneers-controls-directives.d.ts +31 -2
  91. package/types/ngneers-controls-drawer.d.ts +17 -0
  92. package/types/ngneers-controls-edit-inplace.d.ts +23 -0
  93. package/types/ngneers-controls-errors.d.ts +105 -16
  94. package/types/ngneers-controls-filter.d.ts +30 -13
  95. package/types/ngneers-controls-hint.d.ts +8 -1
  96. package/types/ngneers-controls-i18n-translations-de.d.ts +17 -0
  97. package/types/ngneers-controls-i18n-translations-en.d.ts +17 -0
  98. package/types/ngneers-controls-i18n.d.ts +17 -0
  99. package/types/ngneers-controls-icon.d.ts +2 -2
  100. package/types/ngneers-controls-input-field.d.ts +28 -2
  101. package/types/ngneers-controls-input.d.ts +35 -3
  102. package/types/ngneers-controls-list-box.d.ts +17 -0
  103. package/types/ngneers-controls-mask-input.d.ts +17 -0
  104. package/types/ngneers-controls-otp.d.ts +262 -0
  105. package/types/ngneers-controls-paginator.d.ts +34 -3
  106. package/types/ngneers-controls-rating.d.ts +69 -0
  107. package/types/ngneers-controls-scroll-shadow.d.ts +11 -2
  108. package/types/ngneers-controls-scroller.d.ts +3 -1
  109. package/types/ngneers-controls-select.d.ts +30 -3
  110. package/types/ngneers-controls-snackbar.d.ts +17 -0
  111. package/types/ngneers-controls-splitter.d.ts +26 -2
  112. package/types/ngneers-controls-state.d.ts +2 -2
  113. package/types/ngneers-controls-stepper.d.ts +68 -0
  114. package/types/ngneers-controls-table.d.ts +276 -169
  115. package/types/ngneers-controls-toast.d.ts +17 -0
  116. package/types/ngneers-controls-tooltip.d.ts +0 -1
  117. package/types/ngneers-controls-tree.d.ts +17 -0
  118. package/types/ngneers-controls-upload.d.ts +17 -0
  119. package/types/ngneers-controls-utils.d.ts +27 -2
@@ -9,7 +9,7 @@ import { switchControlTemplate } from '@ngneers/controls-themes/templates/switch
9
9
  class NgnSwitch extends ValueControlBase {
10
10
  theme = this.injectThemeTemplate(switchControlTemplate, {
11
11
  root: true,
12
- invalid: () => this.invalid(),
12
+ invalid: () => this.invalidState(),
13
13
  });
14
14
  changed(event) {
15
15
  const target = event.target;
@@ -24,11 +24,11 @@ class NgnSwitch extends ValueControlBase {
24
24
  super();
25
25
  }
26
26
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSwitch, deps: [], target: i0.ɵɵFactoryTarget.Component });
27
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.5", type: NgnSwitch, isStandalone: true, selector: "ngn-switch", providers: [provideSelf(NgnSwitch)], usesInheritance: true, ngImport: i0, template: "<div [ptInt]=\"this\" [ptClass]=\"{ track: true, 'track-checked': !!value() }\">\n <div [ptInt]=\"this\" [ptClass]=\"'thumb'\"></div>\n</div>\n<input\n type=\"checkbox\"\n [checked]=\"value()\"\n (change)=\"changed($event)\"\n (click)=\"onClick($event)\"\n [ptInt]=\"this\"\n [ptClass]=\"'input'\"\n [attr.disabled]=\"disabled() ? '' : undefined\"\n [ariaReadOnly]=\"readonly() ? '' : undefined\"\n [ariaInvalid]=\"invalid() ? 'true' : undefined\"\n [attr.aria-label]=\"label()\"\n [ariaLabelledByElements]=\"labelledBy()\"\n (blur)=\"touched.set(true)\"\n [id]=\"inputId()\"\n/>\n", dependencies: [{ kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }] });
27
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.5", type: NgnSwitch, isStandalone: true, selector: "ngn-switch", providers: [provideSelf(NgnSwitch)], usesInheritance: true, ngImport: i0, template: "<div [ptInt]=\"this\" [ptClass]=\"{ track: true, 'track-checked': !!value() }\">\n <div [ptInt]=\"this\" [ptClass]=\"'thumb'\"></div>\n</div>\n<input\n type=\"checkbox\"\n [checked]=\"value()\"\n (change)=\"changed($event)\"\n (click)=\"onClick($event)\"\n [ptInt]=\"this\"\n [ptClass]=\"'input'\"\n [attr.disabled]=\"disabled() ? '' : undefined\"\n [ariaReadOnly]=\"readonly() ? '' : undefined\"\n [ariaInvalid]=\"invalidState() ? 'true' : undefined\"\n [attr.aria-label]=\"label()\"\n [ariaLabelledByElements]=\"labelledBy()\"\n [id]=\"inputId()\"\n (blur)=\"markTouched()\"\n/>\n", dependencies: [{ kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }] });
28
28
  }
29
29
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSwitch, decorators: [{
30
30
  type: Component,
31
- args: [{ selector: 'ngn-switch', imports: [NgnPt], providers: [provideSelf(NgnSwitch)], template: "<div [ptInt]=\"this\" [ptClass]=\"{ track: true, 'track-checked': !!value() }\">\n <div [ptInt]=\"this\" [ptClass]=\"'thumb'\"></div>\n</div>\n<input\n type=\"checkbox\"\n [checked]=\"value()\"\n (change)=\"changed($event)\"\n (click)=\"onClick($event)\"\n [ptInt]=\"this\"\n [ptClass]=\"'input'\"\n [attr.disabled]=\"disabled() ? '' : undefined\"\n [ariaReadOnly]=\"readonly() ? '' : undefined\"\n [ariaInvalid]=\"invalid() ? 'true' : undefined\"\n [attr.aria-label]=\"label()\"\n [ariaLabelledByElements]=\"labelledBy()\"\n (blur)=\"touched.set(true)\"\n [id]=\"inputId()\"\n/>\n" }]
31
+ args: [{ selector: 'ngn-switch', imports: [NgnPt], providers: [provideSelf(NgnSwitch)], template: "<div [ptInt]=\"this\" [ptClass]=\"{ track: true, 'track-checked': !!value() }\">\n <div [ptInt]=\"this\" [ptClass]=\"'thumb'\"></div>\n</div>\n<input\n type=\"checkbox\"\n [checked]=\"value()\"\n (change)=\"changed($event)\"\n (click)=\"onClick($event)\"\n [ptInt]=\"this\"\n [ptClass]=\"'input'\"\n [attr.disabled]=\"disabled() ? '' : undefined\"\n [ariaReadOnly]=\"readonly() ? '' : undefined\"\n [ariaInvalid]=\"invalidState() ? 'true' : undefined\"\n [attr.aria-label]=\"label()\"\n [ariaLabelledByElements]=\"labelledBy()\"\n [id]=\"inputId()\"\n (blur)=\"markTouched()\"\n/>\n" }]
32
32
  }], ctorParameters: () => [] });
33
33
 
34
34
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ngneers-controls-switch.mjs","sources":["../../src/switch/switch.ts","../../src/switch/switch.html","../../src/switch/ngneers-controls-switch.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { NgnPt, provideSelf, ValueControlBase } from '@ngneers/controls/base';\nimport { switchControlTemplate } from '@ngneers/controls-themes/templates/switch';\n\n/**\n * @category control\n */\n@Component({\n selector: 'ngn-switch',\n templateUrl: './switch.html',\n imports: [NgnPt],\n providers: [provideSelf(NgnSwitch)],\n})\nexport class NgnSwitch extends ValueControlBase<'switch', boolean> {\n protected readonly theme = this.injectThemeTemplate(switchControlTemplate, {\n root: true,\n invalid: () => this.invalid(),\n });\n\n protected changed(event: Event) {\n const target = event.target as HTMLInputElement;\n this.value.set(target.checked);\n }\n\n protected onClick(event: Event) {\n if (this.readonly() || this.disabled()) {\n event.preventDefault();\n }\n }\n\n constructor() {\n super();\n }\n}\n","<div [ptInt]=\"this\" [ptClass]=\"{ track: true, 'track-checked': !!value() }\">\n <div [ptInt]=\"this\" [ptClass]=\"'thumb'\"></div>\n</div>\n<input\n type=\"checkbox\"\n [checked]=\"value()\"\n (change)=\"changed($event)\"\n (click)=\"onClick($event)\"\n [ptInt]=\"this\"\n [ptClass]=\"'input'\"\n [attr.disabled]=\"disabled() ? '' : undefined\"\n [ariaReadOnly]=\"readonly() ? '' : undefined\"\n [ariaInvalid]=\"invalid() ? 'true' : undefined\"\n [attr.aria-label]=\"label()\"\n [ariaLabelledByElements]=\"labelledBy()\"\n (blur)=\"touched.set(true)\"\n [id]=\"inputId()\"\n/>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAIA;;AAEG;AAOG,MAAO,SAAU,SAAQ,gBAAmC,CAAA;AAC7C,IAAA,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,qBAAqB,EAAE;AACzE,QAAA,IAAI,EAAE,IAAI;AACV,QAAA,OAAO,EAAE,MAAM,IAAI,CAAC,OAAO,EAAE;AAC9B,KAAA,CAAC;AAEQ,IAAA,OAAO,CAAC,KAAY,EAAA;AAC5B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;QAC/C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;IAChC;AAEU,IAAA,OAAO,CAAC,KAAY,EAAA;QAC5B,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;YACtC,KAAK,CAAC,cAAc,EAAE;QACxB;IACF;AAEA,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;IACT;uGAnBW,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAT,SAAS,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,YAAA,EAAA,SAAA,EAFT,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECXrC,qlBAkBA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDRY,KAAK,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,SAAA,EAAA,OAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAGJ,SAAS,EAAA,UAAA,EAAA,CAAA;kBANrB,SAAS;+BACE,YAAY,EAAA,OAAA,EAEb,CAAC,KAAK,CAAC,aACL,CAAC,WAAW,WAAW,CAAC,EAAA,QAAA,EAAA,qlBAAA,EAAA;;;AEXrC;;AAEG;;;;"}
1
+ {"version":3,"file":"ngneers-controls-switch.mjs","sources":["../../src/switch/switch.ts","../../src/switch/switch.html","../../src/switch/ngneers-controls-switch.ts"],"sourcesContent":["import { Component } from '@angular/core';\nimport { NgnPt, provideSelf, ValueControlBase } from '@ngneers/controls/base';\nimport { switchControlTemplate } from '@ngneers/controls-themes/templates/switch';\n\n/**\n * @category control\n */\n@Component({\n selector: 'ngn-switch',\n templateUrl: './switch.html',\n imports: [NgnPt],\n providers: [provideSelf(NgnSwitch)],\n})\nexport class NgnSwitch extends ValueControlBase<'switch', boolean> {\n protected readonly theme = this.injectThemeTemplate(switchControlTemplate, {\n root: true,\n invalid: () => this.invalidState(),\n });\n\n protected changed(event: Event) {\n const target = event.target as HTMLInputElement;\n this.value.set(target.checked);\n }\n\n protected onClick(event: Event) {\n if (this.readonly() || this.disabled()) {\n event.preventDefault();\n }\n }\n\n constructor() {\n super();\n }\n}\n","<div [ptInt]=\"this\" [ptClass]=\"{ track: true, 'track-checked': !!value() }\">\n <div [ptInt]=\"this\" [ptClass]=\"'thumb'\"></div>\n</div>\n<input\n type=\"checkbox\"\n [checked]=\"value()\"\n (change)=\"changed($event)\"\n (click)=\"onClick($event)\"\n [ptInt]=\"this\"\n [ptClass]=\"'input'\"\n [attr.disabled]=\"disabled() ? '' : undefined\"\n [ariaReadOnly]=\"readonly() ? '' : undefined\"\n [ariaInvalid]=\"invalidState() ? 'true' : undefined\"\n [attr.aria-label]=\"label()\"\n [ariaLabelledByElements]=\"labelledBy()\"\n [id]=\"inputId()\"\n (blur)=\"markTouched()\"\n/>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAIA;;AAEG;AAOG,MAAO,SAAU,SAAQ,gBAAmC,CAAA;AAC7C,IAAA,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,qBAAqB,EAAE;AACzE,QAAA,IAAI,EAAE,IAAI;AACV,QAAA,OAAO,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE;AACnC,KAAA,CAAC;AAEQ,IAAA,OAAO,CAAC,KAAY,EAAA;AAC5B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B;QAC/C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;IAChC;AAEU,IAAA,OAAO,CAAC,KAAY,EAAA;QAC5B,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;YACtC,KAAK,CAAC,cAAc,EAAE;QACxB;IACF;AAEA,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;IACT;uGAnBW,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAT,SAAS,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,YAAA,EAAA,SAAA,EAFT,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECXrC,slBAkBA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDRY,KAAK,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,SAAA,EAAA,OAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAGJ,SAAS,EAAA,UAAA,EAAA,CAAA;kBANrB,SAAS;+BACE,YAAY,EAAA,OAAA,EAEb,CAAC,KAAK,CAAC,aACL,CAAC,WAAW,WAAW,CAAC,EAAA,QAAA,EAAA,slBAAA,EAAA;;;AEXrC;;AAEG;;;;"}
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, afterRenderEffect, input, inject, ElementRef, effect, Directive, contentChild, Component, viewChild, model, booleanAttribute, afterNextRender, Renderer2, ViewContainerRef, viewChildren, EnvironmentInjector, ApplicationRef, createComponent, NgModule } from '@angular/core';
2
+ import { signal, computed, afterRenderEffect, input, inject, ElementRef, effect, Directive, contentChild, Component, viewChild, model, booleanAttribute, untracked, afterNextRender, Renderer2, ViewContainerRef, viewChildren, EnvironmentInjector, ApplicationRef, createComponent, NgModule } from '@angular/core';
3
3
  import { NgTemplateOutlet } from '@angular/common';
4
4
  import { elementSizeSignal, injectThemeTemplate, templateTypesFn, NgnTemplate, domEventHandler, setComponentInput } from '@ngneers/controls/api/ng';
5
5
  import { NgnBase, NgnPt, provideSelf, getNearestNgnInstance, getNearestNgnInstanceSig } from '@ngneers/controls/base';
6
+ import { NgnButton, NgnActionButton } from '@ngneers/controls/button';
6
7
  import { I18n } from '@ngneers/controls/i18n';
7
8
  import { NgnIcon } from '@ngneers/controls/icon';
8
9
  import { NgnPaginator } from '@ngneers/controls/paginator';
@@ -14,7 +15,6 @@ import { toggleClass, NgnError } from '@ngneers/controls/utils';
14
15
  import { setInputSignalValue } from '@ngneers/controls/utils-ng';
15
16
  import { executeMultiFilter } from '@ngneers/controls/api';
16
17
  import { openMenuAt } from '@ngneers/controls/menu';
17
- import { NgnActionButton } from '@ngneers/controls/button';
18
18
  import { NgnCheckbox } from '@ngneers/controls/checkbox';
19
19
  import { NgnFilter } from '@ngneers/controls/filter';
20
20
 
@@ -514,6 +514,144 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
514
514
  }]
515
515
  }], ctorParameters: () => [], propDecorators: { ngnTableGroupHeaderTr: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnTableGroupHeaderTr", required: true }] }], ngnScrollerItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnScrollerItem", required: false }] }] } });
516
516
 
517
+ /**
518
+ * Owns lazy-loading state for {@link NgnTable}: the loaded row window,
519
+ * request status, per-page cache, and an epoch that invalidates on
520
+ * sort/filter/pageSize/dataSource change. Plain signal class (no DI), mirroring
521
+ * `TableSelectionModel`.
522
+ */
523
+ class TableLazyModel {
524
+ _deps;
525
+ loaded = signal([], /* @ts-ignore */
526
+ ...(ngDevMode ? [{ debugName: "loaded" }] : /* istanbul ignore next */ []));
527
+ status = signal('idle', /* @ts-ignore */
528
+ ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
529
+ error = signal(null, /* @ts-ignore */
530
+ ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
531
+ total = signal(undefined, /* @ts-ignore */
532
+ ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
533
+ hasMore = signal(true, /* @ts-ignore */
534
+ ...(ngDevMode ? [{ debugName: "hasMore" }] : /* istanbul ignore next */ []));
535
+ /** page index -> result, valid within the current epoch. */
536
+ _cache = new Map();
537
+ _epoch = 0;
538
+ _controller = null;
539
+ _lastState = null;
540
+ _nextWindow = 0;
541
+ /** The most recent load attempt, replayed by {@link retry}. */
542
+ _lastLoad = null;
543
+ constructor(_deps) {
544
+ this._deps = _deps;
545
+ }
546
+ /** Load (or serve from cache) the page described by `state`. */
547
+ async setPage(state) {
548
+ this._lastState = state;
549
+ const cached = this._cache.get(state.page.current);
550
+ if (cached) {
551
+ // Supersede any in-flight load so it can't resolve over the cached page.
552
+ this._controller?.abort();
553
+ this._controller = null;
554
+ this.loaded.set(cached.rows);
555
+ this.total.set(cached.total);
556
+ this.hasMore.set(cached.hasMore);
557
+ this.status.set('idle');
558
+ this.error.set(null);
559
+ return;
560
+ }
561
+ await this._load(state, 'replace');
562
+ }
563
+ /**
564
+ * Invalidate everything and refetch the current page/window from scratch.
565
+ * No-op if {@link setPage} has never been called (nothing to refetch yet).
566
+ */
567
+ async reload() {
568
+ const state = this._lastState;
569
+ // Capture the cursor before _bumpEpoch clears the cache it's read from.
570
+ const cursor = state ? this.cursorFor(state.page.current) : undefined;
571
+ this._bumpEpoch();
572
+ if (state)
573
+ await this._load(state, 'replace', cursor);
574
+ }
575
+ /** Called by the component when sort/filter/pageSize/dataSource change. */
576
+ invalidate() {
577
+ this._bumpEpoch();
578
+ }
579
+ /**
580
+ * Infinite scroll: load and append the next window of `pageSize` rows.
581
+ * No-op while a load is in flight or when `hasMore` is false.
582
+ */
583
+ async loadNext(pageSize) {
584
+ if (this.status() === 'loading' || !this.hasMore())
585
+ return;
586
+ const current = this._nextWindow;
587
+ const state = {
588
+ page: { current, size: pageSize },
589
+ slice: { skip: current * pageSize, take: pageSize },
590
+ };
591
+ await this._load(state, 'append');
592
+ }
593
+ /** Re-issue the last load attempt after a rejection; keeps loaded rows in infinite scroll. */
594
+ async retry() {
595
+ const last = this._lastLoad;
596
+ if (last)
597
+ await this._load(last.state, last.apply, last.cursor);
598
+ }
599
+ cursorFor(pageIndex) {
600
+ return this._cache.get(pageIndex - 1)?.cursor;
601
+ }
602
+ _bumpEpoch() {
603
+ this._epoch++;
604
+ this._cache.clear();
605
+ this._controller?.abort();
606
+ this._controller = null;
607
+ this.loaded.set([]);
608
+ this.total.set(undefined);
609
+ this.hasMore.set(true);
610
+ this.status.set('idle');
611
+ this.error.set(null);
612
+ this._nextWindow = 0;
613
+ this._lastLoad = null;
614
+ }
615
+ async _load(state, apply, cursorOverride) {
616
+ const loader = this._deps.dataSource();
617
+ if (!loader)
618
+ return;
619
+ const cursor = cursorOverride ?? this.cursorFor(state.page.current);
620
+ this._lastLoad = { state, apply, cursor };
621
+ const epoch = this._epoch;
622
+ this._controller?.abort();
623
+ const controller = new AbortController();
624
+ this._controller = controller;
625
+ this.status.set('loading');
626
+ this.error.set(null);
627
+ try {
628
+ const result = await loader({
629
+ pagination: state,
630
+ sort: this._deps.sort(),
631
+ filters: this._deps.filters(),
632
+ cursor,
633
+ signal: controller.signal,
634
+ });
635
+ if (epoch !== this._epoch || controller !== this._controller)
636
+ return; // superseded
637
+ this._cache.set(state.page.current, result);
638
+ this.total.set(result.total);
639
+ this.hasMore.set(result.hasMore);
640
+ this.loaded.set(apply === 'append' ? [...this.loaded(), ...result.rows] : result.rows);
641
+ // Advance only on success, so a failed append retries the same window.
642
+ if (apply === 'append')
643
+ this._nextWindow = state.page.current + 1;
644
+ this.status.set('idle');
645
+ }
646
+ catch (err) {
647
+ if (epoch !== this._epoch || controller !== this._controller || controller.signal.aborted)
648
+ return;
649
+ this.error.set(err);
650
+ this.status.set('error');
651
+ }
652
+ }
653
+ }
654
+
517
655
  /** Applies the active multi-column filter set. Returns the input untouched when no filters are set. */
518
656
  function filterRows(rows, filters) {
519
657
  if (!filters) {
@@ -935,23 +1073,32 @@ class NgnTableTemplates extends NgnBase {
935
1073
  ...(ngDevMode ? [{ debugName: "_bodyTemplate" }] : /* istanbul ignore next */ []));
936
1074
  _groupHeaderTemplate = contentChild('groupHeader', /* @ts-ignore */
937
1075
  ...(ngDevMode ? [{ debugName: "_groupHeaderTemplate" }] : /* istanbul ignore next */ []));
1076
+ _loadingTemplate = contentChild('loading', /* @ts-ignore */
1077
+ ...(ngDevMode ? [{ debugName: "_loadingTemplate" }] : /* istanbul ignore next */ []));
1078
+ _errorTemplate = contentChild('error', /* @ts-ignore */
1079
+ ...(ngDevMode ? [{ debugName: "_errorTemplate" }] : /* istanbul ignore next */ []));
938
1080
  headerTemplate = this._headerTemplate;
939
1081
  bodyTemplate = this._bodyTemplate;
940
1082
  groupHeaderTemplate = this._groupHeaderTemplate;
1083
+ /** Custom rows shown while a lazy load is in flight, via `<ng-template #loading>`. */
1084
+ loadingTemplate = this._loadingTemplate;
1085
+ /** Custom error row via `<ng-template #error>`; receives `{ error, retry }`, where `retry` re-issues the failed load. */
1086
+ errorTemplate = this._errorTemplate;
941
1087
  /**
942
1088
  * Types for the table templates.
943
1089
  */
944
1090
  templateTypes = templateTypesFn();
945
1091
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnTableTemplates, deps: null, target: i0.ɵɵFactoryTarget.Component });
946
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.5", type: NgnTableTemplates, isStandalone: true, selector: "ng-component", queries: [{ propertyName: "_headerTemplate", first: true, predicate: ["header"], descendants: true, isSignal: true }, { propertyName: "_bodyTemplate", first: true, predicate: ["body"], descendants: true, isSignal: true }, { propertyName: "_groupHeaderTemplate", first: true, predicate: ["groupHeader"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: '', isInline: true });
1092
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.5", type: NgnTableTemplates, isStandalone: true, selector: "ng-component", queries: [{ propertyName: "_headerTemplate", first: true, predicate: ["header"], descendants: true, isSignal: true }, { propertyName: "_bodyTemplate", first: true, predicate: ["body"], descendants: true, isSignal: true }, { propertyName: "_groupHeaderTemplate", first: true, predicate: ["groupHeader"], descendants: true, isSignal: true }, { propertyName: "_loadingTemplate", first: true, predicate: ["loading"], descendants: true, isSignal: true }, { propertyName: "_errorTemplate", first: true, predicate: ["error"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: '', isInline: true });
947
1093
  }
948
1094
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnTableTemplates, decorators: [{
949
1095
  type: Component,
950
1096
  args: [{
951
1097
  template: '',
952
1098
  }]
953
- }], propDecorators: { _headerTemplate: [{ type: i0.ContentChild, args: ['header', { isSignal: true }] }], _bodyTemplate: [{ type: i0.ContentChild, args: ['body', { isSignal: true }] }], _groupHeaderTemplate: [{ type: i0.ContentChild, args: ['groupHeader', { isSignal: true }] }] } });
1099
+ }], propDecorators: { _headerTemplate: [{ type: i0.ContentChild, args: ['header', { isSignal: true }] }], _bodyTemplate: [{ type: i0.ContentChild, args: ['body', { isSignal: true }] }], _groupHeaderTemplate: [{ type: i0.ContentChild, args: ['groupHeader', { isSignal: true }] }], _loadingTemplate: [{ type: i0.ContentChild, args: ['loading', { isSignal: true }] }], _errorTemplate: [{ type: i0.ContentChild, args: ['error', { isSignal: true }] }] } });
954
1100
 
1101
+ const DEFAULT_LAZY_PAGE_SIZE = 25;
955
1102
  /**
956
1103
  * @category control
957
1104
  */
@@ -963,10 +1110,11 @@ class NgnTable extends NgnTableTemplates {
963
1110
  selectable: () => !!this.selectionMode(),
964
1111
  reorderable: () => this.reorderable(),
965
1112
  reordering: () => this._columns?.isReordering() ?? false,
1113
+ loading: () => this.loadStatus() === 'loading',
966
1114
  });
967
1115
  _scroller = viewChild.required(NgnScroller);
968
- /** The data rows to render. */
969
- rows = input.required(/* @ts-ignore */
1116
+ /** The data rows to render in client-side mode. Ignored when {@link dataSource} is set. */
1117
+ rows = input([], /* @ts-ignore */
970
1118
  ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
971
1119
  /**
972
1120
  * When {@link virtual} is enabled, this defines the height of each row in pixels.
@@ -1000,6 +1148,27 @@ class NgnTable extends NgnTableTemplates {
1000
1148
  */
1001
1149
  paginator = input(false, /* @ts-ignore */
1002
1150
  ...(ngDevMode ? [{ debugName: "paginator" }] : /* istanbul ignore next */ []));
1151
+ /**
1152
+ * A loader callback for server-driven lazy loading. When set, the table fetches
1153
+ * rows on demand instead of reading {@link rows}, and client-side sort/filter are
1154
+ * delegated to the loader. With {@link paginator} it pages lazily; without, it
1155
+ * infinite-scrolls. Incompatible with {@link groupBy} (throws).
1156
+ *
1157
+ * @remarks Bind to a stable reference (a class field/method), not an inline
1158
+ * arrow — a new function identity each change-detection cycle invalidates the
1159
+ * page cache and refetches every cycle.
1160
+ * @default null
1161
+ */
1162
+ dataSource = input(null, /* @ts-ignore */
1163
+ ...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
1164
+ /**
1165
+ * Infinite scroll only: set by the header select-all checkbox to request a
1166
+ * "select everything matching the current filters" bulk operation, since the
1167
+ * full set is not loaded. Two-way bindable.
1168
+ * @default false
1169
+ */
1170
+ selectAllMatching = model(false, /* @ts-ignore */
1171
+ ...(ngDevMode ? [{ debugName: "selectAllMatching" }] : /* istanbul ignore next */ []));
1003
1172
  /**
1004
1173
  * The selection mode for the table.
1005
1174
  * - `'single'`: Only one row can be selected at a time.
@@ -1091,6 +1260,21 @@ class NgnTable extends NgnTableTemplates {
1091
1260
  ...(ngDevMode ? [{ debugName: "filters" }] : /* istanbul ignore next */ []));
1092
1261
  pageState = signal(null, /* @ts-ignore */
1093
1262
  ...(ngDevMode ? [{ debugName: "pageState" }] : /* istanbul ignore next */ []));
1263
+ // --- Lazy (server-driven) loading ---
1264
+ lazy = computed(() => !!this.dataSource(), /* @ts-ignore */
1265
+ ...(ngDevMode ? [{ debugName: "lazy" }] : /* istanbul ignore next */ []));
1266
+ lazyMode = computed(() => this.paginator() ? 'paginate' : 'infinite', /* @ts-ignore */
1267
+ ...(ngDevMode ? [{ debugName: "lazyMode" }] : /* istanbul ignore next */ []));
1268
+ _lazyModel = new TableLazyModel({
1269
+ dataSource: this.dataSource,
1270
+ sort: this.sort,
1271
+ filters: computed(() => this.filters()),
1272
+ mode: this.lazyMode,
1273
+ });
1274
+ loadStatus = this._lazyModel.status;
1275
+ loadError = this._lazyModel.error;
1276
+ loadTotal = this._lazyModel.total;
1277
+ lazyHasMore = this._lazyModel.hasMore;
1094
1278
  i18n = inject(I18n).translations;
1095
1279
  /**
1096
1280
  * Polite live-region text announcing sort / filter / pagination / selection
@@ -1144,6 +1328,15 @@ class NgnTable extends NgnTableTemplates {
1144
1328
  */
1145
1329
  totalColumnCount = computed(() => this._columns.columnCount() + (this._columns.hasSelectionColumn() ? 1 : 0), /* @ts-ignore */
1146
1330
  ...(ngDevMode ? [{ debugName: "totalColumnCount" }] : /* istanbul ignore next */ []));
1331
+ /** Number of placeholder rows to show while a lazy load is in flight. */
1332
+ skeletonRows = computed(() => {
1333
+ if (this.loadStatus() !== 'loading')
1334
+ return 0;
1335
+ return this.paginator()
1336
+ ? (this.pageState()?.page.size ?? DEFAULT_LAZY_PAGE_SIZE)
1337
+ : this.virtualPadding() * 2 + 1;
1338
+ }, /* @ts-ignore */
1339
+ ...(ngDevMode ? [{ debugName: "skeletonRows" }] : /* istanbul ignore next */ []));
1147
1340
  formattedRows = computed(() => {
1148
1341
  const groupBy = this.groupBy();
1149
1342
  if (groupBy) {
@@ -1166,15 +1359,27 @@ class NgnTable extends NgnTableTemplates {
1166
1359
  }, /* @ts-ignore */
1167
1360
  ...(ngDevMode ? [{ debugName: "_groupedRows" }] : /* istanbul ignore next */ []));
1168
1361
  pagedRows = computed(() => {
1169
- if (!this.paginator()) {
1362
+ // Lazy pagination clears rows while fetching so skeletons replace the page.
1363
+ if (this.lazy() && this.lazyMode() === 'paginate' && this.loadStatus() === 'loading') {
1364
+ return [];
1365
+ }
1366
+ // Lazy loaders return exactly one page — never re-slice client-side.
1367
+ if (!this.paginator() || this.lazy()) {
1170
1368
  return this.formattedRows();
1171
1369
  }
1172
1370
  return [...paginateRows(this.formattedRows(), this.pageState())];
1173
1371
  }, /* @ts-ignore */
1174
1372
  ...(ngDevMode ? [{ debugName: "pagedRows" }] : /* istanbul ignore next */ []));
1175
- _filteredRows = computed(() => filterRows(this.rows(), this.filters()), /* @ts-ignore */
1373
+ _baseRows = computed(() => this.lazy() ? this._lazyModel.loaded() : this.rows(), /* @ts-ignore */
1374
+ ...(ngDevMode ? [{ debugName: "_baseRows" }] : /* istanbul ignore next */ []));
1375
+ _filteredRows = computed(() => this.lazy() ? this._baseRows() : filterRows(this._baseRows(), this.filters()), /* @ts-ignore */
1176
1376
  ...(ngDevMode ? [{ debugName: "_filteredRows" }] : /* istanbul ignore next */ []));
1177
- _sortedRows = computed(() => sortRows(this._filteredRows(), this.sort(), this.sortComparator()), /* @ts-ignore */
1377
+ /** Page size only page navigation must not invalidate the per-page cache. */
1378
+ _lazyPageSize = computed(() => this.pageState()?.page.size, /* @ts-ignore */
1379
+ ...(ngDevMode ? [{ debugName: "_lazyPageSize" }] : /* istanbul ignore next */ []));
1380
+ _sortedRows = computed(() => this.lazy()
1381
+ ? this._baseRows()
1382
+ : sortRows(this._filteredRows(), this.sort(), this.sortComparator()), /* @ts-ignore */
1178
1383
  ...(ngDevMode ? [{ debugName: "_sortedRows" }] : /* istanbul ignore next */ []));
1179
1384
  constructor() {
1180
1385
  super();
@@ -1197,7 +1402,9 @@ class NgnTable extends NgnTableTemplates {
1197
1402
  openMenu: index => this.getRowActions(index)?.openMenuFromKeyboard() ?? false,
1198
1403
  focusHost: () => this.element.nativeElement.focus(),
1199
1404
  });
1200
- this.headerCheckboxValue = computed(() => this._selection.headerCheckboxValue(), /* @ts-ignore */
1405
+ this.headerCheckboxValue = computed(() => this.lazy() && this.lazyMode() === 'infinite'
1406
+ ? this.selectAllMatching()
1407
+ : this._selection.headerCheckboxValue(), /* @ts-ignore */
1201
1408
  ...(ngDevMode ? [{ debugName: "headerCheckboxValue" }] : /* istanbul ignore next */ []));
1202
1409
  // Minor #3: clear the current-row highlight whenever the underlying row
1203
1410
  // set changes identity (sort/filter/rows replaced), so it never lingers
@@ -1221,9 +1428,13 @@ class NgnTable extends NgnTableTemplates {
1221
1428
  let prevSelCount = 0;
1222
1429
  effect(() => {
1223
1430
  const sort = this.sort();
1224
- const filterCount = this._filteredRows().length;
1431
+ // Lazy: announce the server total; fall back to the loaded window count.
1432
+ const filterCount = this.lazy()
1433
+ ? (this.loadTotal() ?? this._filteredRows().length)
1434
+ : this._filteredRows().length;
1225
1435
  const page = this.pageState();
1226
1436
  const selCount = this.selection().length;
1437
+ const selTotal = this.lazy() ? (this.loadTotal() ?? this.rows().length) : this.rows().length;
1227
1438
  const sortKey = sort ? `${sort.column}:${sort.direction}` : '';
1228
1439
  const pageKey = page ? `${page.page.current}/${page.page.size}` : '';
1229
1440
  if (!seeded) {
@@ -1254,7 +1465,7 @@ class NgnTable extends NgnTableTemplates {
1254
1465
  message = this.i18n['table_page']({ page: (page?.page.current ?? 0) + 1, pages });
1255
1466
  }
1256
1467
  else if (selCount !== prevSelCount) {
1257
- message = this.i18n['table_selectedCount']({ count: selCount, total: this.rows().length });
1468
+ message = this.i18n['table_selectedCount']({ count: selCount, total: selTotal });
1258
1469
  }
1259
1470
  [prevSort, prevFilterCount, prevPageKey, prevSelCount] = [
1260
1471
  sortKey,
@@ -1266,6 +1477,57 @@ class NgnTable extends NgnTableTemplates {
1266
1477
  this.liveAnnouncement.set(message);
1267
1478
  }
1268
1479
  });
1480
+ // Lazy grouping needs the full row set, which lazy mode never has.
1481
+ effect(() => {
1482
+ if (this.lazy() && this.groupBy()) {
1483
+ throw new NgnError('table', 'groupBy is not supported with a lazy dataSource (v1)');
1484
+ }
1485
+ });
1486
+ // Reset the lazy cache when sort, filter, page size or dataSource change.
1487
+ effect(() => {
1488
+ this.sort();
1489
+ this.filters();
1490
+ this._lazyPageSize();
1491
+ this.dataSource();
1492
+ if (!this.lazy())
1493
+ return;
1494
+ untracked(() => this._lazyModel.invalidate());
1495
+ });
1496
+ // Lazy pagination: load the current page when it, sort, filters or dataSource change.
1497
+ effect(() => {
1498
+ const state = this.pageState();
1499
+ this.sort();
1500
+ this.filters();
1501
+ this.dataSource();
1502
+ if (!this.lazy() || this.lazyMode() !== 'paginate' || !state)
1503
+ return;
1504
+ untracked(() => void this._lazyModel.setPage(state));
1505
+ });
1506
+ // Infinite scroll: load the next window when the viewport nears the end.
1507
+ effect(() => {
1508
+ if (!this.lazy() || this.lazyMode() !== 'infinite')
1509
+ return;
1510
+ const distance = this._scroller().distanceFromEnd();
1511
+ const threshold = (this.rowHeight() ?? 40) * this.virtualPadding();
1512
+ const pageSize = this.pageState()?.page.size ?? DEFAULT_LAZY_PAGE_SIZE;
1513
+ if (distance <= threshold) {
1514
+ untracked(() => void this._lazyModel.loadNext(pageSize));
1515
+ }
1516
+ });
1517
+ }
1518
+ /** Force a lazy refetch, clearing the page cache. No-op in client-side mode. */
1519
+ reload() {
1520
+ // Infinite scroll has no current page — restart from the first window.
1521
+ if (this.lazy() && this.lazyMode() === 'infinite') {
1522
+ this._lazyModel.invalidate();
1523
+ void this._lazyModel.loadNext(this.pageState()?.page.size ?? DEFAULT_LAZY_PAGE_SIZE);
1524
+ return;
1525
+ }
1526
+ void this._lazyModel.reload();
1527
+ }
1528
+ /** Retry the failed load, keeping already-loaded rows in infinite scroll. */
1529
+ retryLoad() {
1530
+ void this._lazyModel.retry();
1269
1531
  }
1270
1532
  pageChanged(event) {
1271
1533
  this.pageState.set(event);
@@ -1341,6 +1603,10 @@ class NgnTable extends NgnTableTemplates {
1341
1603
  this._selection.handleCheckboxChange(row);
1342
1604
  }
1343
1605
  toggleSelectAll() {
1606
+ if (this.lazy() && this.lazyMode() === 'infinite') {
1607
+ this.selectAllMatching.update(v => !v);
1608
+ return;
1609
+ }
1344
1610
  this._selection.toggleSelectAll();
1345
1611
  }
1346
1612
  // --- Row actions registry ---
@@ -1405,7 +1671,7 @@ class NgnTable extends NgnTableTemplates {
1405
1671
  this.toggleGroup(row.groupKey);
1406
1672
  }
1407
1673
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
1408
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnTable, isStandalone: true, selector: "ngn-table", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, fieldId: { classPropertyName: "fieldId", publicName: "fieldId", isSignal: true, isRequired: true, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, virtualPadding: { classPropertyName: "virtualPadding", publicName: "virtualPadding", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, expandedGroups: { classPropertyName: "expandedGroups", publicName: "expandedGroups", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resizeMode: { classPropertyName: "resizeMode", publicName: "resizeMode", isSignal: true, isRequired: false, transformFunction: null }, lockSizes: { classPropertyName: "lockSizes", publicName: "lockSizes", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, columnOrder: { classPropertyName: "columnOrder", publicName: "columnOrder", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, sortComparator: { classPropertyName: "sortComparator", publicName: "sortComparator", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", expandedGroups: "expandedGroupsChange", columnOrder: "columnOrderChange", sort: "sortChange", filters: "filtersChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" } }, providers: [provideSelf(NgnTable)], viewQueries: [{ propertyName: "_scroller", first: true, predicate: NgnScroller, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<span\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style=\"\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n border: 0;\n \"\n >{{ liveAnnouncement() }}</span\n>\n<table\n #scrollContainer\n role=\"grid\"\n ngnScrollShadow=\"horizontal\"\n [ptInt]=\"this\"\n [ptClass]=\"{ table: true, striped: striped() }\"\n [attr.aria-multiselectable]=\"selectionMode() === 'multi' ? true : null\"\n [attr.aria-rowcount]=\"formattedRows().length\"\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\n [style.grid-template-columns]=\"gridTemplateColumns()\"\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\n>\n <thead role=\"rowgroup\" [ptInt]=\"this\" [ptClass]=\"'head'\">\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </thead>\n @if (dropIndicatorState()) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'drop-indicator'\"\n [style.left.px]=\"dropIndicatorState()!.leftPx\"\n [style.top.px]=\"dropIndicatorState()!.topPx\"\n [style.height.px]=\"dropIndicatorState()!.heightPx\"\n ></div>\n }\n\n <tbody\n role=\"rowgroup\"\n [ptInt]=\"this\"\n [ptClass]=\"'body'\"\n [ptDep]=\"'scroller'\"\n ngn-scroller\n #scroller\n [scrollContainer]=\"scrollContainer\"\n [virtual]=\"virtual()\"\n [items]=\"pagedRows()\"\n [fieldId]=\"'id'\"\n [itemHeight]=\"rowHeight() || 0\"\n [virtualPadding]=\"virtualPadding()\"\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\n >\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\n @if (item.kind === 'group-header') {\n @if (groupHeaderTemplate(); as customGroupHeader) {\n <ng-container\n [ngTemplateOutlet]=\"customGroupHeader\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n groupKey: item.groupKey,\n groupValue: item.groupValue,\n count: item.count,\n expanded: item.expanded,\n },\n }\"\n ></ng-container>\n } @else {\n <tr\n [ngnTableGroupHeaderTr]=\"item\"\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\n [attr.aria-expanded]=\"item.expanded\"\n role=\"row\"\n (click)=\"toggleGroupFromRow(item)\"\n >\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'group-toggle'\"\n [ptDep]=\"'group-toggle-icon'\"\n [defaultIcon]=\"'table-group-toggle'\"\n />\n <span>{{ item.groupValue }}</span>\n <span>({{ item.count }})</span>\n </td>\n </tr>\n }\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"bodyTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n ></ng-container>\n }\n </ng-template>\n </tbody>\n</table>\n@if (paginator()) {\n <ngn-paginator\n [ptInt]=\"this\"\n [ptDep]=\"'paginator'\"\n [totalItems]=\"rows().length\"\n (value)=\"pageChanged($event)\"\n />\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgnScroller, selector: "ngn-scroller, [ngn-scroller]", inputs: ["items", "fieldId", "virtual", "itemHeight", "virtualPadding", "focusable", "fieldSticky"] }, { kind: "component", type: NgnPaginator, selector: "ngn-paginator", inputs: ["totalItems", "pageSize", "possiblePageSizes", "fixedPageSize", "page"], outputs: ["pageSizeChange", "value", "pageChange"] }, { kind: "directive", type: NgnTemplate, selector: "ng-template[ngnTemplate]", inputs: ["ngnTemplate"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "directive", type: NgnTableGroupHeaderTr, selector: "[ngnTableGroupHeaderTr]", inputs: ["ngnTableGroupHeaderTr", "ngnScrollerItem"] }, { kind: "directive", type: NgnScrollShadow, selector: "[ngnScrollShadow]", inputs: ["ngnScrollShadow", "scrollShadowTarget"] }] });
1674
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnTable, isStandalone: true, selector: "ngn-table", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, fieldId: { classPropertyName: "fieldId", publicName: "fieldId", isSignal: true, isRequired: true, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, virtualPadding: { classPropertyName: "virtualPadding", publicName: "virtualPadding", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: false, transformFunction: null }, selectAllMatching: { classPropertyName: "selectAllMatching", publicName: "selectAllMatching", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, expandedGroups: { classPropertyName: "expandedGroups", publicName: "expandedGroups", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resizeMode: { classPropertyName: "resizeMode", publicName: "resizeMode", isSignal: true, isRequired: false, transformFunction: null }, lockSizes: { classPropertyName: "lockSizes", publicName: "lockSizes", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, columnOrder: { classPropertyName: "columnOrder", publicName: "columnOrder", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, sortComparator: { classPropertyName: "sortComparator", publicName: "sortComparator", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectAllMatching: "selectAllMatchingChange", selection: "selectionChange", expandedGroups: "expandedGroupsChange", columnOrder: "columnOrderChange", sort: "sortChange", filters: "filtersChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" } }, providers: [provideSelf(NgnTable)], viewQueries: [{ propertyName: "_scroller", first: true, predicate: NgnScroller, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<span\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style=\"\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n border: 0;\n \"\n >{{ liveAnnouncement() }}</span\n>\n<table\n #scrollContainer\n role=\"grid\"\n ngnScrollShadow=\"horizontal\"\n ngnScrollShadowUnstyled\n [ptInt]=\"this\"\n [ptClass]=\"{ table: true, striped: striped() }\"\n [attr.aria-multiselectable]=\"selectionMode() === 'multi' ? true : null\"\n [attr.aria-rowcount]=\"formattedRows().length\"\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\n [style.grid-template-columns]=\"gridTemplateColumns()\"\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\n>\n <thead role=\"rowgroup\" [ptInt]=\"this\" [ptClass]=\"'head'\">\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </thead>\n @if (dropIndicatorState()) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'drop-indicator'\"\n [style.left.px]=\"dropIndicatorState()!.leftPx\"\n [style.top.px]=\"dropIndicatorState()!.topPx\"\n [style.height.px]=\"dropIndicatorState()!.heightPx\"\n ></div>\n }\n\n <tbody\n role=\"rowgroup\"\n [ptInt]=\"this\"\n [ptClass]=\"'body'\"\n [ptDep]=\"'scroller'\"\n ngn-scroller\n #scroller\n [scrollContainer]=\"scrollContainer\"\n [virtual]=\"virtual()\"\n [items]=\"pagedRows()\"\n [fieldId]=\"'id'\"\n [itemHeight]=\"rowHeight() || 0\"\n [virtualPadding]=\"virtualPadding()\"\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\n >\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\n @if (item.kind === 'group-header') {\n @if (groupHeaderTemplate(); as customGroupHeader) {\n <ng-container\n [ngTemplateOutlet]=\"customGroupHeader\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n groupKey: item.groupKey,\n groupValue: item.groupValue,\n count: item.count,\n expanded: item.expanded,\n },\n }\"\n ></ng-container>\n } @else {\n <tr\n [ngnTableGroupHeaderTr]=\"item\"\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\n [attr.aria-expanded]=\"item.expanded\"\n role=\"row\"\n (click)=\"toggleGroupFromRow(item)\"\n >\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'group-toggle'\"\n [ptDep]=\"'group-toggle-icon'\"\n [defaultIcon]=\"'table-group-toggle'\"\n />\n <span>{{ item.groupValue }}</span>\n <span>({{ item.count }})</span>\n </td>\n </tr>\n }\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"bodyTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n ></ng-container>\n }\n </ng-template>\n @if (skeletonRows() > 0) {\n @if (loadingTemplate(); as customLoading) {\n <ng-container [ngTemplateOutlet]=\"customLoading\"></ng-container>\n } @else {\n @for (i of [].constructor(skeletonRows()); track $index) {\n <tr [class]=\"theme.class('skeleton-row')\" role=\"row\" aria-hidden=\"true\">\n <td [class]=\"theme.class('skeleton-cell')\" role=\"gridcell\"></td>\n </tr>\n }\n }\n }\n @if (loadStatus() === 'error') {\n @if (errorTemplate(); as customError) {\n <ng-container\n [ngTemplateOutlet]=\"customError\"\n [ngTemplateOutletContext]=\"{\n $implicit: { error: loadError(), retry: retryLoad.bind(this) },\n }\"\n ></ng-container>\n } @else {\n <tr [class]=\"theme.class('error-row')\" role=\"row\">\n <td [attr.colspan]=\"totalColumnCount()\" role=\"gridcell\">\n <span>{{ i18n['table_loadError']() }}</span>\n <button ngnButton [kind]=\"'text'\" (click)=\"retryLoad()\">\n {{ i18n['table_retry']() }}\n </button>\n </td>\n </tr>\n }\n }\n </tbody>\n</table>\n@if (paginator()) {\n <ngn-paginator\n [ptInt]=\"this\"\n [ptDep]=\"'paginator'\"\n [mode]=\"lazy() && loadTotal() === undefined ? 'compact' : 'pages'\"\n [totalItems]=\"lazy() ? (loadTotal() ?? 0) : rows().length\"\n [hasNext]=\"lazyHasMore()\"\n (value)=\"pageChanged($event)\"\n />\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgnScroller, selector: "ngn-scroller, [ngn-scroller]", inputs: ["items", "fieldId", "virtual", "itemHeight", "virtualPadding", "focusable", "fieldSticky"] }, { kind: "component", type: NgnPaginator, selector: "ngn-paginator", inputs: ["totalItems", "pageSize", "mode", "hasNext", "possiblePageSizes", "fixedPageSize", "page"], outputs: ["pageSizeChange", "value", "pageChange"] }, { kind: "directive", type: NgnTemplate, selector: "ng-template[ngnTemplate]", inputs: ["ngnTemplate"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "directive", type: NgnTableGroupHeaderTr, selector: "[ngnTableGroupHeaderTr]", inputs: ["ngnTableGroupHeaderTr", "ngnScrollerItem"] }, { kind: "directive", type: NgnScrollShadow, selector: "[ngnScrollShadow]", inputs: ["ngnScrollShadow", "scrollShadowTarget", "ngnScrollShadowUnstyled"] }, { kind: "directive", type: NgnButton, selector: "button[ngnButton], a[ngnButton]", inputs: ["ngnButtonInline"], exportAs: ["ngnButton"] }] });
1409
1675
  }
1410
1676
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnTable, decorators: [{
1411
1677
  type: Component,
@@ -1418,11 +1684,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1418
1684
  NgnIcon,
1419
1685
  NgnTableGroupHeaderTr,
1420
1686
  NgnScrollShadow,
1687
+ NgnButton,
1421
1688
  ], providers: [provideSelf(NgnTable)], host: {
1422
1689
  tabindex: '0',
1423
1690
  '(keydown)': 'onKeyDown($event)',
1424
- }, template: "<span\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style=\"\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n border: 0;\n \"\n >{{ liveAnnouncement() }}</span\n>\n<table\n #scrollContainer\n role=\"grid\"\n ngnScrollShadow=\"horizontal\"\n [ptInt]=\"this\"\n [ptClass]=\"{ table: true, striped: striped() }\"\n [attr.aria-multiselectable]=\"selectionMode() === 'multi' ? true : null\"\n [attr.aria-rowcount]=\"formattedRows().length\"\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\n [style.grid-template-columns]=\"gridTemplateColumns()\"\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\n>\n <thead role=\"rowgroup\" [ptInt]=\"this\" [ptClass]=\"'head'\">\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </thead>\n @if (dropIndicatorState()) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'drop-indicator'\"\n [style.left.px]=\"dropIndicatorState()!.leftPx\"\n [style.top.px]=\"dropIndicatorState()!.topPx\"\n [style.height.px]=\"dropIndicatorState()!.heightPx\"\n ></div>\n }\n\n <tbody\n role=\"rowgroup\"\n [ptInt]=\"this\"\n [ptClass]=\"'body'\"\n [ptDep]=\"'scroller'\"\n ngn-scroller\n #scroller\n [scrollContainer]=\"scrollContainer\"\n [virtual]=\"virtual()\"\n [items]=\"pagedRows()\"\n [fieldId]=\"'id'\"\n [itemHeight]=\"rowHeight() || 0\"\n [virtualPadding]=\"virtualPadding()\"\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\n >\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\n @if (item.kind === 'group-header') {\n @if (groupHeaderTemplate(); as customGroupHeader) {\n <ng-container\n [ngTemplateOutlet]=\"customGroupHeader\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n groupKey: item.groupKey,\n groupValue: item.groupValue,\n count: item.count,\n expanded: item.expanded,\n },\n }\"\n ></ng-container>\n } @else {\n <tr\n [ngnTableGroupHeaderTr]=\"item\"\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\n [attr.aria-expanded]=\"item.expanded\"\n role=\"row\"\n (click)=\"toggleGroupFromRow(item)\"\n >\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'group-toggle'\"\n [ptDep]=\"'group-toggle-icon'\"\n [defaultIcon]=\"'table-group-toggle'\"\n />\n <span>{{ item.groupValue }}</span>\n <span>({{ item.count }})</span>\n </td>\n </tr>\n }\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"bodyTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n ></ng-container>\n }\n </ng-template>\n </tbody>\n</table>\n@if (paginator()) {\n <ngn-paginator\n [ptInt]=\"this\"\n [ptDep]=\"'paginator'\"\n [totalItems]=\"rows().length\"\n (value)=\"pageChanged($event)\"\n />\n}\n" }]
1425
- }], ctorParameters: () => [], propDecorators: { _scroller: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgnScroller), { isSignal: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], fieldId: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldId", required: true }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], virtualPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualPadding", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], expandedGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedGroups", required: false }] }, { type: i0.Output, args: ["expandedGroupsChange"] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], resizeMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeMode", required: false }] }], lockSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockSizes", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], columnOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnOrder", required: false }] }, { type: i0.Output, args: ["columnOrderChange"] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], sortComparator: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortComparator", required: false }] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }, { type: i0.Output, args: ["filtersChange"] }] } });
1691
+ }, template: "<span\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style=\"\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n border: 0;\n \"\n >{{ liveAnnouncement() }}</span\n>\n<table\n #scrollContainer\n role=\"grid\"\n ngnScrollShadow=\"horizontal\"\n ngnScrollShadowUnstyled\n [ptInt]=\"this\"\n [ptClass]=\"{ table: true, striped: striped() }\"\n [attr.aria-multiselectable]=\"selectionMode() === 'multi' ? true : null\"\n [attr.aria-rowcount]=\"formattedRows().length\"\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\n [style.grid-template-columns]=\"gridTemplateColumns()\"\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\n>\n <thead role=\"rowgroup\" [ptInt]=\"this\" [ptClass]=\"'head'\">\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </thead>\n @if (dropIndicatorState()) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'drop-indicator'\"\n [style.left.px]=\"dropIndicatorState()!.leftPx\"\n [style.top.px]=\"dropIndicatorState()!.topPx\"\n [style.height.px]=\"dropIndicatorState()!.heightPx\"\n ></div>\n }\n\n <tbody\n role=\"rowgroup\"\n [ptInt]=\"this\"\n [ptClass]=\"'body'\"\n [ptDep]=\"'scroller'\"\n ngn-scroller\n #scroller\n [scrollContainer]=\"scrollContainer\"\n [virtual]=\"virtual()\"\n [items]=\"pagedRows()\"\n [fieldId]=\"'id'\"\n [itemHeight]=\"rowHeight() || 0\"\n [virtualPadding]=\"virtualPadding()\"\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\n >\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\n @if (item.kind === 'group-header') {\n @if (groupHeaderTemplate(); as customGroupHeader) {\n <ng-container\n [ngTemplateOutlet]=\"customGroupHeader\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n groupKey: item.groupKey,\n groupValue: item.groupValue,\n count: item.count,\n expanded: item.expanded,\n },\n }\"\n ></ng-container>\n } @else {\n <tr\n [ngnTableGroupHeaderTr]=\"item\"\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\n [attr.aria-expanded]=\"item.expanded\"\n role=\"row\"\n (click)=\"toggleGroupFromRow(item)\"\n >\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\n <ngn-icon\n [ptInt]=\"this\"\n [ptClass]=\"'group-toggle'\"\n [ptDep]=\"'group-toggle-icon'\"\n [defaultIcon]=\"'table-group-toggle'\"\n />\n <span>{{ item.groupValue }}</span>\n <span>({{ item.count }})</span>\n </td>\n </tr>\n }\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"bodyTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\n ></ng-container>\n }\n </ng-template>\n @if (skeletonRows() > 0) {\n @if (loadingTemplate(); as customLoading) {\n <ng-container [ngTemplateOutlet]=\"customLoading\"></ng-container>\n } @else {\n @for (i of [].constructor(skeletonRows()); track $index) {\n <tr [class]=\"theme.class('skeleton-row')\" role=\"row\" aria-hidden=\"true\">\n <td [class]=\"theme.class('skeleton-cell')\" role=\"gridcell\"></td>\n </tr>\n }\n }\n }\n @if (loadStatus() === 'error') {\n @if (errorTemplate(); as customError) {\n <ng-container\n [ngTemplateOutlet]=\"customError\"\n [ngTemplateOutletContext]=\"{\n $implicit: { error: loadError(), retry: retryLoad.bind(this) },\n }\"\n ></ng-container>\n } @else {\n <tr [class]=\"theme.class('error-row')\" role=\"row\">\n <td [attr.colspan]=\"totalColumnCount()\" role=\"gridcell\">\n <span>{{ i18n['table_loadError']() }}</span>\n <button ngnButton [kind]=\"'text'\" (click)=\"retryLoad()\">\n {{ i18n['table_retry']() }}\n </button>\n </td>\n </tr>\n }\n }\n </tbody>\n</table>\n@if (paginator()) {\n <ngn-paginator\n [ptInt]=\"this\"\n [ptDep]=\"'paginator'\"\n [mode]=\"lazy() && loadTotal() === undefined ? 'compact' : 'pages'\"\n [totalItems]=\"lazy() ? (loadTotal() ?? 0) : rows().length\"\n [hasNext]=\"lazyHasMore()\"\n (value)=\"pageChanged($event)\"\n />\n}\n" }]
1692
+ }], ctorParameters: () => [], propDecorators: { _scroller: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgnScroller), { isSignal: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], fieldId: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldId", required: true }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], virtualPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualPadding", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: false }] }], selectAllMatching: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllMatching", required: false }] }, { type: i0.Output, args: ["selectAllMatchingChange"] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], expandedGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedGroups", required: false }] }, { type: i0.Output, args: ["expandedGroupsChange"] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], resizeMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeMode", required: false }] }], lockSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockSizes", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], columnOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnOrder", required: false }] }, { type: i0.Output, args: ["columnOrderChange"] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], sortComparator: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortComparator", required: false }] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }, { type: i0.Output, args: ["filtersChange"] }] } });
1426
1693
 
1427
1694
  /**
1428
1695
  * Lightweight body-cell directive. Deliberately does **not** extend `NgnBase`: