@ngneers/controls 0.0.1-next.1 → 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 (115) 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-badge.mjs +160 -0
  4. package/fesm2022/ngneers-controls-badge.mjs.map +1 -0
  5. package/fesm2022/ngneers-controls-base.mjs +85 -8
  6. package/fesm2022/ngneers-controls-base.mjs.map +1 -1
  7. package/fesm2022/{ngneers-controls-calendar-time-CJT5AFlA.mjs → ngneers-controls-calendar-time-paM6WVpV.mjs} +2 -2
  8. package/fesm2022/{ngneers-controls-calendar-time-CJT5AFlA.mjs.map → ngneers-controls-calendar-time-paM6WVpV.mjs.map} +1 -1
  9. package/fesm2022/ngneers-controls-calendar.mjs +13 -6
  10. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -1
  11. package/fesm2022/ngneers-controls-checkbox.mjs +3 -3
  12. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -1
  13. package/fesm2022/ngneers-controls-color-picker.mjs +336 -0
  14. package/fesm2022/ngneers-controls-color-picker.mjs.map +1 -0
  15. package/fesm2022/ngneers-controls-default-icons.mjs +5 -0
  16. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -1
  17. package/fesm2022/ngneers-controls-directives.mjs +64 -7
  18. package/fesm2022/ngneers-controls-directives.mjs.map +1 -1
  19. package/fesm2022/ngneers-controls-edit-inplace.mjs +15 -5
  20. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -1
  21. package/fesm2022/ngneers-controls-errors.mjs +203 -148
  22. package/fesm2022/ngneers-controls-errors.mjs.map +1 -1
  23. package/fesm2022/ngneers-controls-filter.mjs +2 -2
  24. package/fesm2022/ngneers-controls-filter.mjs.map +1 -1
  25. package/fesm2022/ngneers-controls-hint.mjs +18 -1
  26. package/fesm2022/ngneers-controls-hint.mjs.map +1 -1
  27. package/fesm2022/ngneers-controls-i18n-translations-de.mjs +14 -0
  28. package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -1
  29. package/fesm2022/ngneers-controls-i18n-translations-en.mjs +16 -0
  30. package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -1
  31. package/fesm2022/ngneers-controls-icon.mjs +23 -6
  32. package/fesm2022/ngneers-controls-icon.mjs.map +1 -1
  33. package/fesm2022/ngneers-controls-input-field.mjs +18 -2
  34. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -1
  35. package/fesm2022/ngneers-controls-input.mjs +49 -9
  36. package/fesm2022/ngneers-controls-input.mjs.map +1 -1
  37. package/fesm2022/ngneers-controls-list-box.mjs +2 -2
  38. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -1
  39. package/fesm2022/ngneers-controls-mask-input.mjs +4 -1
  40. package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -1
  41. package/fesm2022/ngneers-controls-number-input.mjs +4 -4
  42. package/fesm2022/ngneers-controls-number-input.mjs.map +1 -1
  43. package/fesm2022/ngneers-controls-otp.mjs +16 -4
  44. package/fesm2022/ngneers-controls-otp.mjs.map +1 -1
  45. package/fesm2022/ngneers-controls-paginator.mjs +36 -9
  46. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -1
  47. package/fesm2022/ngneers-controls-radio.mjs +4 -4
  48. package/fesm2022/ngneers-controls-radio.mjs.map +1 -1
  49. package/fesm2022/ngneers-controls-rating.mjs +180 -0
  50. package/fesm2022/ngneers-controls-rating.mjs.map +1 -0
  51. package/fesm2022/ngneers-controls-scroll-shadow.mjs +33 -7
  52. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -1
  53. package/fesm2022/ngneers-controls-scroller.mjs +4 -2
  54. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -1
  55. package/fesm2022/ngneers-controls-select-button.mjs +2 -1
  56. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -1
  57. package/fesm2022/ngneers-controls-select.mjs +24 -4
  58. package/fesm2022/ngneers-controls-select.mjs.map +1 -1
  59. package/fesm2022/ngneers-controls-slider.mjs +3 -3
  60. package/fesm2022/ngneers-controls-slider.mjs.map +1 -1
  61. package/fesm2022/ngneers-controls-stepper.mjs +142 -0
  62. package/fesm2022/ngneers-controls-stepper.mjs.map +1 -0
  63. package/fesm2022/ngneers-controls-switch.mjs +3 -3
  64. package/fesm2022/ngneers-controls-switch.mjs.map +1 -1
  65. package/fesm2022/ngneers-controls-table.mjs +282 -15
  66. package/fesm2022/ngneers-controls-table.mjs.map +1 -1
  67. package/fesm2022/ngneers-controls-tabs.mjs +2 -2
  68. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -1
  69. package/fesm2022/ngneers-controls-toggle-button.mjs +3 -3
  70. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -1
  71. package/fesm2022/ngneers-controls-tooltip.mjs +16 -28
  72. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -1
  73. package/fesm2022/ngneers-controls-tree.mjs +2 -2
  74. package/fesm2022/ngneers-controls-tree.mjs.map +1 -1
  75. package/fesm2022/ngneers-controls-utils.mjs +138 -1
  76. package/fesm2022/ngneers-controls-utils.mjs.map +1 -1
  77. package/package.json +18 -2
  78. package/types/ngneers-controls-api-ng.d.ts +12 -9
  79. package/types/ngneers-controls-badge.d.ts +67 -0
  80. package/types/ngneers-controls-base.d.ts +61 -4
  81. package/types/ngneers-controls-breadcrumb.d.ts +14 -0
  82. package/types/ngneers-controls-calendar.d.ts +21 -6
  83. package/types/ngneers-controls-chip.d.ts +14 -0
  84. package/types/ngneers-controls-color-picker.d.ts +119 -0
  85. package/types/ngneers-controls-dialog.d.ts +14 -0
  86. package/types/ngneers-controls-directives.d.ts +31 -2
  87. package/types/ngneers-controls-drawer.d.ts +14 -0
  88. package/types/ngneers-controls-edit-inplace.d.ts +20 -0
  89. package/types/ngneers-controls-errors.d.ts +105 -16
  90. package/types/ngneers-controls-filter.d.ts +27 -13
  91. package/types/ngneers-controls-hint.d.ts +8 -1
  92. package/types/ngneers-controls-i18n-translations-de.d.ts +14 -0
  93. package/types/ngneers-controls-i18n-translations-en.d.ts +14 -0
  94. package/types/ngneers-controls-i18n.d.ts +14 -0
  95. package/types/ngneers-controls-icon.d.ts +2 -2
  96. package/types/ngneers-controls-input-field.d.ts +25 -2
  97. package/types/ngneers-controls-input.d.ts +35 -3
  98. package/types/ngneers-controls-list-box.d.ts +14 -0
  99. package/types/ngneers-controls-mask-input.d.ts +14 -0
  100. package/types/ngneers-controls-otp.d.ts +19 -0
  101. package/types/ngneers-controls-paginator.d.ts +31 -3
  102. package/types/ngneers-controls-rating.d.ts +69 -0
  103. package/types/ngneers-controls-scroll-shadow.d.ts +11 -2
  104. package/types/ngneers-controls-scroller.d.ts +3 -1
  105. package/types/ngneers-controls-select.d.ts +27 -3
  106. package/types/ngneers-controls-snackbar.d.ts +14 -0
  107. package/types/ngneers-controls-splitter.d.ts +14 -0
  108. package/types/ngneers-controls-state.d.ts +2 -2
  109. package/types/ngneers-controls-stepper.d.ts +68 -0
  110. package/types/ngneers-controls-table.d.ts +273 -169
  111. package/types/ngneers-controls-toast.d.ts +14 -0
  112. package/types/ngneers-controls-tooltip.d.ts +0 -1
  113. package/types/ngneers-controls-tree.d.ts +14 -0
  114. package/types/ngneers-controls-upload.d.ts +14 -0
  115. package/types/ngneers-controls-utils.d.ts +27 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ngneers-controls-radio.mjs","sources":["../../src/radio/radio-group.token.ts","../../src/radio/radio-group.ts","../../src/radio/radio-group.html","../../src/radio/radio.ts","../../src/radio/radio.html","../../src/radio/ngneers-controls-radio.ts"],"sourcesContent":["import { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * A single radio option registered with its enclosing group. The group keys\n * selection and keyboard coordination off these refs; `element` correlates a\n * roving-focus item back to its payload `value`.\n */\nexport interface NgnRadioRef<V> {\n readonly element: HTMLElement;\n /** The option payload this radio contributes to the group value. */\n readonly value: Signal<V>;\n /** Effective disabled state (own disabled OR the group being disabled). */\n readonly disabled: Signal<boolean>;\n}\n\n/**\n * The slice of the radio group that child `ngn-radio`s consume. Kept as a\n * token + interface (rather than the concrete class) so the child never imports\n * the group component — avoiding a circular dependency between the two files.\n */\nexport interface NgnRadioGroupApi<V> {\n /** The currently selected group value. */\n readonly value: Signal<V>;\n /** Whether the whole group is disabled. */\n readonly disabled: Signal<boolean>;\n /** Whether the group is read-only (navigable, but selection is frozen). */\n readonly readonly: Signal<boolean>;\n register(ref: NgnRadioRef<V>): void;\n unregister(ref: NgnRadioRef<V>): void;\n}\n\nexport const NGN_RADIO_GROUP = new InjectionToken<NgnRadioGroupApi<unknown>>('NGN_RADIO_GROUP');\n","import { Component, DestroyRef, effect, inject, signal } from '@angular/core';\nimport { provideSelf, ValueControlBase } from '@ngneers/controls/base';\nimport { NgnRovingGroup } from '@ngneers/controls/roving-focus';\nimport { radioGroupControlTemplate } from '@ngneers/controls-themes/templates/radio-group';\n\nimport { NGN_RADIO_GROUP, type NgnRadioGroupApi, type NgnRadioRef } from './radio-group.token';\n\n/**\n * A themed radio group. Owns the selected {@link value}; the individual\n * `ngn-radio` children projected inside are presentational and report their\n * payload up through {@link register}.\n *\n * Keyboard handling and roving tab order are delegated to `NgnRovingGroup`\n * (applied as a host directive). Selection follows focus: moving the roving\n * active item (arrows / Home / End / pointer) selects it, matching native\n * radio-group behavior.\n *\n * @category control\n */\n@Component({\n selector: 'ngn-radio-group',\n templateUrl: './radio-group.html',\n hostDirectives: [{ directive: NgnRovingGroup, inputs: ['orientation', 'rovingWrap'] }],\n providers: [provideSelf(NgnRadioGroup), { provide: NGN_RADIO_GROUP, useExisting: NgnRadioGroup }],\n host: {\n role: 'radiogroup',\n '[attr.aria-label]': 'label()',\n '[attr.aria-labelledby]': 'labelledBy()',\n '[attr.aria-orientation]': 'roving.orientation()',\n '[attr.aria-invalid]': \"invalid() ? 'true' : null\",\n '[attr.aria-disabled]': \"disabled() ? 'true' : null\",\n },\n})\nexport class NgnRadioGroup<V>\n extends ValueControlBase<'radioGroup', V>\n implements NgnRadioGroupApi<V>\n{\n protected readonly theme = this.injectThemeTemplate(radioGroupControlTemplate, {\n root: true,\n invalid: () => this.invalid(),\n });\n\n /** The roving-focus host directive that drives keyboard/tab coordination. */\n protected readonly roving = inject(NgnRovingGroup);\n\n private readonly _radios = signal<readonly NgnRadioRef<V>[]>([]);\n\n public register(ref: NgnRadioRef<V>): void {\n this._radios.update(list => (list.includes(ref) ? list : [...list, ref]));\n }\n\n public unregister(ref: NgnRadioRef<V>): void {\n this._radios.update(list => list.filter(r => r !== ref));\n }\n\n constructor() {\n super();\n\n // Selection follows focus: the roving group emits only on genuine\n // navigation/activation (never on mount or plain Tab-in), so wiring value\n // here gives native \"arrow selects\" behavior without spurious writes.\n const sub = this.roving.activeItemChange.subscribe(index => {\n if (this.readonly() || this.disabled()) {\n return;\n }\n const element = this.roving.items()[index]?.element;\n const radio = this._radios().find(r => r.element === element);\n if (!radio || radio.disabled()) {\n return;\n }\n this.value.set(radio.value());\n this.touched.set(true);\n });\n inject(DestroyRef).onDestroy(() => sub.unsubscribe());\n\n // Keep the roving tab stop on the selected radio so Tab-in lands on the\n // checked option (native behavior). Disabled radios are skipped so a\n // selected-but-disabled option never becomes the tab stop. `syncActiveIndex`\n // updates the tab stop silently — no emit, no focus move — so a programmatic\n // `value` change never yanks DOM focus into the group.\n effect(() => {\n const items = this.roving.items();\n if (!items.length) {\n return;\n }\n const radios = this._radios();\n const value = this.value();\n const index = items.findIndex(item => {\n const radio = radios.find(r => r.element === item.element);\n return radio ? !radio.disabled() && radio.value() === value : false;\n });\n if (index >= 0) {\n this.roving.syncActiveIndex(index);\n }\n });\n }\n}\n","<ng-content />\n","import {\n booleanAttribute,\n Component,\n computed,\n DestroyRef,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { NgnBase, NgnPt, provideSelf } from '@ngneers/controls/base';\nimport { NgnRovingItem } from '@ngneers/controls/roving-focus';\nimport { radioControlTemplate } from '@ngneers/controls-themes/templates/radio';\n\nimport { NGN_RADIO_GROUP } from './radio-group.token';\n\n/**\n * A single radio option. Must be projected inside an `ngn-radio-group`, which\n * owns the selected value — this control only contributes its {@link value}\n * payload and renders the themed dot. The label is authored via content\n * projection (`<ngn-radio [value]=\"…\">Label</ngn-radio>`) with an optional\n * {@link label} input fallback.\n *\n * @category control\n */\n@Component({\n selector: 'ngn-radio',\n templateUrl: './radio.html',\n imports: [NgnPt],\n hostDirectives: [NgnRovingItem],\n providers: [provideSelf(NgnRadio)],\n host: {\n role: 'radio',\n '[attr.aria-checked]': 'checked()',\n '[attr.aria-disabled]': \"effectiveDisabled() ? 'true' : null\",\n '[attr.aria-label]': 'label()',\n },\n})\nexport class NgnRadio<V> extends NgnBase<'radio'> {\n protected readonly theme = this.injectThemeTemplate(radioControlTemplate, {\n root: true,\n });\n\n /** The payload this radio selects into the enclosing group. */\n public readonly value = input.required<V>();\n /** Disables this individual option. */\n public readonly disabled = input(false, { transform: booleanAttribute });\n /** Optional accessible label; overrides projected content for the a11y name. */\n public readonly label = input<string | null>(null);\n\n private readonly _group = inject(NGN_RADIO_GROUP);\n private readonly _rovingItem = inject(NgnRovingItem);\n\n /** Whether this radio is the currently selected option in its group. */\n protected readonly checked = computed(() => this._group.value() === this.value());\n\n /** Disabled if this option or the whole group is disabled. */\n protected readonly effectiveDisabled = computed(() => this.disabled() || this._group.disabled());\n\n constructor() {\n super();\n\n // Feed the roving-focus item so keyboard nav / pointer activation skip a\n // disabled radio.\n effect(() => this._rovingItem.disabled.set(this.effectiveDisabled()));\n\n const ref = {\n element: this.element.nativeElement,\n value: this.value,\n disabled: this.effectiveDisabled,\n };\n this._group.register(ref);\n inject(DestroyRef).onDestroy(() => this._group.unregister(ref));\n }\n}\n","<span\n [ptInt]=\"this\"\n [ptClass]=\"{\n circle: true,\n 'circle-checked': checked(),\n }\"\n>\n <span [ptInt]=\"this\" [ptClass]=\"'dot'\"></span>\n</span>\n<span [ptInt]=\"this\" [ptClass]=\"'label'\"><ng-content /></span>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngneers-controls-radio.mjs","sources":["../../src/radio/radio-group.token.ts","../../src/radio/radio-group.ts","../../src/radio/radio-group.html","../../src/radio/radio.ts","../../src/radio/radio.html","../../src/radio/ngneers-controls-radio.ts"],"sourcesContent":["import { InjectionToken, type Signal } from '@angular/core';\n\n/**\n * A single radio option registered with its enclosing group. The group keys\n * selection and keyboard coordination off these refs; `element` correlates a\n * roving-focus item back to its payload `value`.\n */\nexport interface NgnRadioRef<V> {\n readonly element: HTMLElement;\n /** The option payload this radio contributes to the group value. */\n readonly value: Signal<V>;\n /** Effective disabled state (own disabled OR the group being disabled). */\n readonly disabled: Signal<boolean>;\n}\n\n/**\n * The slice of the radio group that child `ngn-radio`s consume. Kept as a\n * token + interface (rather than the concrete class) so the child never imports\n * the group component — avoiding a circular dependency between the two files.\n */\nexport interface NgnRadioGroupApi<V> {\n /** The currently selected group value. */\n readonly value: Signal<V>;\n /** Whether the whole group is disabled. */\n readonly disabled: Signal<boolean>;\n /** Whether the group is read-only (navigable, but selection is frozen). */\n readonly readonly: Signal<boolean>;\n register(ref: NgnRadioRef<V>): void;\n unregister(ref: NgnRadioRef<V>): void;\n}\n\nexport const NGN_RADIO_GROUP = new InjectionToken<NgnRadioGroupApi<unknown>>('NGN_RADIO_GROUP');\n","import { Component, DestroyRef, effect, inject, signal } from '@angular/core';\nimport { provideSelf, ValueControlBase } from '@ngneers/controls/base';\nimport { NgnRovingGroup } from '@ngneers/controls/roving-focus';\nimport { radioGroupControlTemplate } from '@ngneers/controls-themes/templates/radio-group';\n\nimport { NGN_RADIO_GROUP, type NgnRadioGroupApi, type NgnRadioRef } from './radio-group.token';\n\n/**\n * A themed radio group. Owns the selected {@link value}; the individual\n * `ngn-radio` children projected inside are presentational and report their\n * payload up through {@link register}.\n *\n * Keyboard handling and roving tab order are delegated to `NgnRovingGroup`\n * (applied as a host directive). Selection follows focus: moving the roving\n * active item (arrows / Home / End / pointer) selects it, matching native\n * radio-group behavior.\n *\n * @category control\n */\n@Component({\n selector: 'ngn-radio-group',\n templateUrl: './radio-group.html',\n hostDirectives: [{ directive: NgnRovingGroup, inputs: ['orientation', 'rovingWrap'] }],\n providers: [provideSelf(NgnRadioGroup), { provide: NGN_RADIO_GROUP, useExisting: NgnRadioGroup }],\n host: {\n role: 'radiogroup',\n '[attr.aria-label]': 'label()',\n '[attr.aria-labelledby]': 'labelledBy()',\n '[attr.aria-orientation]': 'roving.orientation()',\n '[attr.aria-invalid]': \"invalidState() ? 'true' : null\",\n '[attr.aria-disabled]': \"disabled() ? 'true' : null\",\n },\n})\nexport class NgnRadioGroup<V>\n extends ValueControlBase<'radioGroup', V>\n implements NgnRadioGroupApi<V>\n{\n protected readonly theme = this.injectThemeTemplate(radioGroupControlTemplate, {\n root: true,\n invalid: () => this.invalidState(),\n });\n\n /** The roving-focus host directive that drives keyboard/tab coordination. */\n protected readonly roving = inject(NgnRovingGroup);\n\n private readonly _radios = signal<readonly NgnRadioRef<V>[]>([]);\n\n public register(ref: NgnRadioRef<V>): void {\n this._radios.update(list => (list.includes(ref) ? list : [...list, ref]));\n }\n\n public unregister(ref: NgnRadioRef<V>): void {\n this._radios.update(list => list.filter(r => r !== ref));\n }\n\n constructor() {\n super();\n\n // Selection follows focus: the roving group emits only on genuine\n // navigation/activation (never on mount or plain Tab-in), so wiring value\n // here gives native \"arrow selects\" behavior without spurious writes.\n const sub = this.roving.activeItemChange.subscribe(index => {\n if (this.readonly() || this.disabled()) {\n return;\n }\n const element = this.roving.items()[index]?.element;\n const radio = this._radios().find(r => r.element === element);\n if (!radio || radio.disabled()) {\n return;\n }\n this.value.set(radio.value());\n this.markTouched();\n });\n inject(DestroyRef).onDestroy(() => sub.unsubscribe());\n\n // Keep the roving tab stop on the selected radio so Tab-in lands on the\n // checked option (native behavior). Disabled radios are skipped so a\n // selected-but-disabled option never becomes the tab stop. `syncActiveIndex`\n // updates the tab stop silently — no emit, no focus move — so a programmatic\n // `value` change never yanks DOM focus into the group.\n effect(() => {\n const items = this.roving.items();\n if (!items.length) {\n return;\n }\n const radios = this._radios();\n const value = this.value();\n const index = items.findIndex(item => {\n const radio = radios.find(r => r.element === item.element);\n return radio ? !radio.disabled() && radio.value() === value : false;\n });\n if (index >= 0) {\n this.roving.syncActiveIndex(index);\n }\n });\n }\n}\n","<ng-content />\n","import {\n booleanAttribute,\n Component,\n computed,\n DestroyRef,\n effect,\n inject,\n input,\n} from '@angular/core';\nimport { NgnBase, NgnPt, provideSelf } from '@ngneers/controls/base';\nimport { NgnRovingItem } from '@ngneers/controls/roving-focus';\nimport { radioControlTemplate } from '@ngneers/controls-themes/templates/radio';\n\nimport { NGN_RADIO_GROUP } from './radio-group.token';\n\n/**\n * A single radio option. Must be projected inside an `ngn-radio-group`, which\n * owns the selected value — this control only contributes its {@link value}\n * payload and renders the themed dot. The label is authored via content\n * projection (`<ngn-radio [value]=\"…\">Label</ngn-radio>`) with an optional\n * {@link label} input fallback.\n *\n * @category control\n */\n@Component({\n selector: 'ngn-radio',\n templateUrl: './radio.html',\n imports: [NgnPt],\n hostDirectives: [NgnRovingItem],\n providers: [provideSelf(NgnRadio)],\n host: {\n role: 'radio',\n '[attr.aria-checked]': 'checked()',\n '[attr.aria-disabled]': \"effectiveDisabled() ? 'true' : null\",\n '[attr.aria-label]': 'label()',\n },\n})\nexport class NgnRadio<V> extends NgnBase<'radio'> {\n protected readonly theme = this.injectThemeTemplate(radioControlTemplate, {\n root: true,\n });\n\n /** The payload this radio selects into the enclosing group. */\n public readonly value = input.required<V>();\n /** Disables this individual option. */\n public readonly disabled = input(false, { transform: booleanAttribute });\n /** Optional accessible label; overrides projected content for the a11y name. */\n public readonly label = input<string | null>(null);\n\n private readonly _group = inject(NGN_RADIO_GROUP);\n private readonly _rovingItem = inject(NgnRovingItem);\n\n /** Whether this radio is the currently selected option in its group. */\n protected readonly checked = computed(() => this._group.value() === this.value());\n\n /** Disabled if this option or the whole group is disabled. */\n protected readonly effectiveDisabled = computed(() => this.disabled() || this._group.disabled());\n\n constructor() {\n super();\n\n // Feed the roving-focus item so keyboard nav / pointer activation skip a\n // disabled radio.\n effect(() => this._rovingItem.disabled.set(this.effectiveDisabled()));\n\n const ref = {\n element: this.element.nativeElement,\n value: this.value,\n disabled: this.effectiveDisabled,\n };\n this._group.register(ref);\n inject(DestroyRef).onDestroy(() => this._group.unregister(ref));\n }\n}\n","<span\n [ptInt]=\"this\"\n [ptClass]=\"{\n circle: true,\n 'circle-checked': checked(),\n }\"\n>\n <span [ptInt]=\"this\" [ptClass]=\"'dot'\"></span>\n</span>\n<span [ptInt]=\"this\" [ptClass]=\"'label'\"><ng-content /></span>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;MA+Ba,eAAe,GAAG,IAAI,cAAc,CAA4B,iBAAiB;;ACxB9F;;;;;;;;;;;AAWG;AAeG,MAAO,aACX,SAAQ,gBAAiC,CAAA;AAGtB,IAAA,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,yBAAyB,EAAE;AAC7E,QAAA,IAAI,EAAE,IAAI;AACV,QAAA,OAAO,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE;AACnC,KAAA,CAAC;;AAGiB,IAAA,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC;IAEjC,OAAO,GAAG,MAAM,CAA4B,EAAE;gFAAC;AAEzD,IAAA,QAAQ,CAAC,GAAmB,EAAA;AACjC,QAAA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3E;AAEO,IAAA,UAAU,CAAC,GAAmB,EAAA;QACnC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IAC1D;AAEA,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;;;;AAKP,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,KAAK,IAAG;YACzD,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE;gBACtC;YACF;AACA,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO;AACnD,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC;YAC7D,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE;gBAC9B;YACF;YACA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,WAAW,EAAE;AACpB,QAAA,CAAC,CAAC;AACF,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;;;;;;QAOrD,MAAM,CAAC,MAAK;YACV,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;AACjC,YAAA,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBACjB;YACF;AACA,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE;AAC7B,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;YAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,IAAG;AACnC,gBAAA,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC;gBAC1D,OAAO,KAAK,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,KAAK,CAAC,KAAK,EAAE,KAAK,KAAK,GAAG,KAAK;AACrE,YAAA,CAAC,CAAC;AACF,YAAA,IAAI,KAAK,IAAI,CAAC,EAAE;AACd,gBAAA,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC;YACpC;AACF,QAAA,CAAC,CAAC;IACJ;uGA9DW,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAb,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,aAAa,gWAVb,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC,yKCvBnG,kBACA,EAAA,CAAA;;2FDgCa,aAAa,EAAA,UAAA,EAAA,CAAA;kBAdzB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,iBAAiB,EAAA,cAAA,EAEX,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,aAAa,EAAE,YAAY,CAAC,EAAE,CAAC,EAAA,SAAA,EAC3E,CAAC,WAAW,CAAA,aAAA,CAAe,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAA,aAAe,EAAE,CAAC,EAAA,IAAA,EAC3F;AACJ,wBAAA,IAAI,EAAE,YAAY;AAClB,wBAAA,mBAAmB,EAAE,SAAS;AAC9B,wBAAA,wBAAwB,EAAE,cAAc;AACxC,wBAAA,yBAAyB,EAAE,sBAAsB;AACjD,wBAAA,qBAAqB,EAAE,gCAAgC;AACvD,wBAAA,sBAAsB,EAAE,4BAA4B;AACrD,qBAAA,EAAA,QAAA,EAAA,kBAAA,EAAA;;;AEhBH;;;;;;;;AAQG;AAcG,MAAO,QAAY,SAAQ,OAAgB,CAAA;AAC5B,IAAA,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,oBAAoB,EAAE;AACxE,QAAA,IAAI,EAAE,IAAI;AACX,KAAA,CAAC;;IAGc,KAAK,GAAG,KAAK,CAAC,QAAQ;8EAAK;;IAE3B,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;IAExD,KAAK,GAAG,KAAK,CAAgB,IAAI;8EAAC;AAEjC,IAAA,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC;AAChC,IAAA,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC;;AAGjC,IAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC,KAAK,EAAE;gFAAC;;AAG9D,IAAA,iBAAiB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;0FAAC;AAEhG,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;;;AAIP,QAAA,MAAM,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;AAErE,QAAA,MAAM,GAAG,GAAG;AACV,YAAA,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa;YACnC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,IAAI,CAAC,iBAAiB;SACjC;AACD,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;AACzB,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACjE;uGAnCW,QAAQ,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAR,QAAQ,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,MAAA,EAAA,OAAA,EAAA,EAAA,UAAA,EAAA,EAAA,mBAAA,EAAA,WAAA,EAAA,oBAAA,EAAA,qCAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,EAAA,EAAA,SAAA,EARR,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAA,eAAA,EAAA,IAAA,EAAA,cAAA,EAAA,CAAA,EAAA,SAAA,EAAA,EAAA,CAAA,aAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC7BpC,gPAUA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDiBY,KAAK,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,SAAA,EAAA,OAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAUJ,QAAQ,EAAA,UAAA,EAAA,CAAA;kBAbpB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,WAAW,EAAA,OAAA,EAEZ,CAAC,KAAK,CAAC,EAAA,cAAA,EACA,CAAC,aAAa,CAAC,EAAA,SAAA,EACpB,CAAC,WAAW,CAAA,QAAA,CAAU,CAAC,EAAA,IAAA,EAC5B;AACJ,wBAAA,IAAI,EAAE,OAAO;AACb,wBAAA,qBAAqB,EAAE,WAAW;AAClC,wBAAA,sBAAsB,EAAE,qCAAqC;AAC7D,wBAAA,mBAAmB,EAAE,SAAS;AAC/B,qBAAA,EAAA,QAAA,EAAA,gPAAA,EAAA;;;AEnCH;;AAEG;;;;"}
@@ -0,0 +1,180 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { contentChild, input, computed, Directive, signal, booleanAttribute, Component } from '@angular/core';
4
+ import { ValueControlBase, NgnPt, provideSelf } from '@ngneers/controls/base';
5
+ import { NgnIcon } from '@ngneers/controls/icon';
6
+ import { ratingControlTemplate } from '@ngneers/controls-themes/templates/rating';
7
+
8
+ class RatingTemplates extends ValueControlBase {
9
+ _userIndicatorTemplate = contentChild('indicator', /* @ts-ignore */
10
+ ...(ngDevMode ? [{ debugName: "_userIndicatorTemplate" }] : /* istanbul ignore next */ []));
11
+ /**
12
+ * Custom template for a single symbol. Receives the fill ratio (`$implicit`,
13
+ * 0..1) and `index`. Can also be set with an `<ng-template #indicator>`.
14
+ * When set, it replaces the default full/empty icon rendering per symbol.
15
+ */
16
+ indicatorTemplate = input(null, /* @ts-ignore */
17
+ ...(ngDevMode ? [{ debugName: "indicatorTemplate" }] : /* istanbul ignore next */ []));
18
+ resolvedIndicatorTemplate = computed(() => this._userIndicatorTemplate() ?? this.indicatorTemplate(), /* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "resolvedIndicatorTemplate" }] : /* istanbul ignore next */ []));
20
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RatingTemplates, deps: null, target: i0.ɵɵFactoryTarget.Directive });
21
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.5", type: RatingTemplates, isStandalone: true, inputs: { indicatorTemplate: { classPropertyName: "indicatorTemplate", publicName: "indicatorTemplate", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "_userIndicatorTemplate", first: true, predicate: ["indicator"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0 });
22
+ }
23
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RatingTemplates, decorators: [{
24
+ type: Directive
25
+ }], propDecorators: { _userIndicatorTemplate: [{ type: i0.ContentChild, args: ['indicator', { isSignal: true }] }], indicatorTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indicatorTemplate", required: false }] }] } });
26
+
27
+ /**
28
+ * @category control
29
+ */
30
+ class NgnRating extends RatingTemplates {
31
+ theme = this.injectThemeTemplate(ratingControlTemplate, {
32
+ root: true,
33
+ invalid: () => this.invalidState(),
34
+ readonly: () => this.readonly(),
35
+ disabled: () => this.disabled(),
36
+ });
37
+ _hover = signal(null, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "_hover" }] : /* istanbul ignore next */ []));
39
+ /** Number of symbols. @default 5 */
40
+ count = input(5, /* @ts-ignore */
41
+ ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
42
+ /** The value increment; supports fractions (e.g. 0.5). @default 1 */
43
+ step = input(1, /* @ts-ignore */
44
+ ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
45
+ /** The filled symbol icon. Falls back to a filled star ({@link https://tabler.io/icons}). */
46
+ iconFull = input(/* @ts-ignore */
47
+ ...(ngDevMode ? [undefined, { debugName: "iconFull" }] : /* istanbul ignore next */ []));
48
+ /** The empty symbol icon. Falls back to an outline star. */
49
+ iconEmpty = input(/* @ts-ignore */
50
+ ...(ngDevMode ? [undefined, { debugName: "iconEmpty" }] : /* istanbul ignore next */ []));
51
+ /** Clicking the current value clears the rating back to `null`. @default true */
52
+ clearable = input(true, { ...(ngDevMode ? { debugName: "clearable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
53
+ /** Accessible value text; overrides the numeric readout. */
54
+ valueText = input(/* @ts-ignore */
55
+ ...(ngDevMode ? [undefined, { debugName: "valueText" }] : /* istanbul ignore next */ []));
56
+ /** Function producing accessible value text from the value. */
57
+ valueTextFn = input(/* @ts-ignore */
58
+ ...(ngDevMode ? [undefined, { debugName: "valueTextFn" }] : /* istanbul ignore next */ []));
59
+ /** The value the UI should paint: hover preview when hovering, else the model value. */
60
+ displayValue = computed(() => this._hover() ?? this.value() ?? 0, /* @ts-ignore */
61
+ ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
62
+ /** Zero-based indices for the symbols. */
63
+ symbols = computed(() => Array.from({ length: this.count() }, (_, i) => i), /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "symbols" }] : /* istanbul ignore next */ []));
65
+ valueTextValue = computed(() => {
66
+ const valueText = this.valueText();
67
+ if (valueText) {
68
+ return valueText;
69
+ }
70
+ const fn = this.valueTextFn();
71
+ if (fn) {
72
+ return fn(this.value() ?? 0);
73
+ }
74
+ return null;
75
+ }, /* @ts-ignore */
76
+ ...(ngDevMode ? [{ debugName: "valueTextValue" }] : /* istanbul ignore next */ []));
77
+ /** Fill ratio 0..1 for symbol `index` given the current display value (hover ?? value). */
78
+ fillRatio(index) {
79
+ const filled = this.displayValue() - index;
80
+ return Math.min(1, Math.max(0, filled));
81
+ }
82
+ onKeyDown(event) {
83
+ if (this.readonly() || this.disabled()) {
84
+ return;
85
+ }
86
+ switch (event.key) {
87
+ case 'ArrowLeft':
88
+ case 'ArrowDown':
89
+ // Decrementing below the first step clears the rating (0 is not a valid value).
90
+ this.value.update(v => {
91
+ const next = (v ?? 0) - this.step();
92
+ return next <= 0 ? null : next;
93
+ });
94
+ event.preventDefault();
95
+ break;
96
+ case 'ArrowRight':
97
+ case 'ArrowUp':
98
+ this.value.update(v => Math.min(this.count(), (v ?? 0) + this.step()));
99
+ event.preventDefault();
100
+ break;
101
+ case 'Home':
102
+ // No value is the lowest state, since 0 is not selectable.
103
+ this.value.set(null);
104
+ event.preventDefault();
105
+ break;
106
+ case 'End':
107
+ this.value.set(this.count());
108
+ event.preventDefault();
109
+ break;
110
+ }
111
+ }
112
+ /** Pointer moved over symbol `index` at fractional `offset` (0..1 within the symbol). */
113
+ onPointerMove(event, index) {
114
+ if (this.readonly() || this.disabled()) {
115
+ return;
116
+ }
117
+ this._hover.set(this.valueAt(event, index));
118
+ }
119
+ clearHover() {
120
+ this._hover.set(null);
121
+ }
122
+ onClick(event, index) {
123
+ if (this.readonly() || this.disabled()) {
124
+ return;
125
+ }
126
+ const next = this.valueAt(event, index);
127
+ if (this.clearable() && next === this.value()) {
128
+ this.value.set(null);
129
+ }
130
+ else {
131
+ this.value.set(next);
132
+ }
133
+ }
134
+ /** Resolve the stepped value for a pointer position within symbol `index`. */
135
+ valueAt(event, index) {
136
+ const target = event.currentTarget;
137
+ const rect = target.getBoundingClientRect();
138
+ const ratio = rect.width ? (event.clientX - rect.left) / rect.width : 1;
139
+ const clamped = Math.min(1, Math.max(0, ratio));
140
+ // Fill on area-entry: within symbol `index`, snap UP to the nearest `step`
141
+ // sub-division. Entering the symbol anywhere (ratio > 0) selects at least its
142
+ // first sub-step, so a whole-step rating fills the entire symbol as soon as the
143
+ // pointer is over it, and a half-step rating fills the left/right half.
144
+ const step = this.step();
145
+ const subStepsPerSymbol = Math.max(1, Math.round(1 / step));
146
+ const subIndex = Math.min(subStepsPerSymbol, Math.max(1, Math.ceil(clamped * subStepsPerSymbol)));
147
+ return this.clamp(index + subIndex * step);
148
+ }
149
+ clamp(value) {
150
+ return Math.min(this.count(), Math.max(0, value));
151
+ }
152
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnRating, deps: null, target: i0.ɵɵFactoryTarget.Component });
153
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnRating, isStandalone: true, selector: "ngn-rating", inputs: { count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, iconFull: { classPropertyName: "iconFull", publicName: "iconFull", isSignal: true, isRequired: false, transformFunction: null }, iconEmpty: { classPropertyName: "iconEmpty", publicName: "iconEmpty", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, valueText: { classPropertyName: "valueText", publicName: "valueText", isSignal: true, isRequired: false, transformFunction: null }, valueTextFn: { classPropertyName: "valueTextFn", publicName: "valueTextFn", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "slider", "aria-valuemin": "0" }, listeners: { "keydown": "onKeyDown($event)", "pointerleave": "clearHover()", "blur": "markTouched()" }, properties: { "attr.aria-valuemax": "count()", "attr.aria-valuenow": "value()", "attr.aria-readonly": "readonly() ? \"true\" : null", "attr.disabled": "disabled() ? \"\" : null", "attr.aria-disabled": "disabled() ? 'true' : null", "attr.aria-labelledby": "labelledBy()", "attr.aria-label": "label()", "attr.aria-valuetext": "valueTextValue()", "tabindex": "disabled() ? -1 : 0" } }, providers: [provideSelf(NgnRating)], usesInheritance: true, ngImport: i0, template: "<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->\n<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->\n@for (index of symbols(); track index) {\n <span\n [ptInt]=\"this\"\n [ptClass]=\"'symbol'\"\n [style.--fillRatio]=\"fillRatio(index)\"\n (pointermove)=\"onPointerMove($event, index)\"\n (click)=\"onClick($event, index)\"\n >\n @if (resolvedIndicatorTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: fillRatio(index), index: index }\"\n />\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'empty'\">\n <ngn-icon [icon]=\"iconEmpty()\" defaultIcon=\"rating-empty\" />\n </span>\n <span [ptInt]=\"this\" [ptClass]=\"'full'\">\n <ngn-icon [icon]=\"iconFull()\" defaultIcon=\"rating-full\" />\n </span>\n }\n </span>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }] });
154
+ }
155
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnRating, decorators: [{
156
+ type: Component,
157
+ args: [{ selector: 'ngn-rating', imports: [NgTemplateOutlet, NgnPt, NgnIcon], providers: [provideSelf(NgnRating)], host: {
158
+ role: 'slider',
159
+ 'aria-valuemin': '0',
160
+ '[attr.aria-valuemax]': 'count()',
161
+ '[attr.aria-valuenow]': 'value()',
162
+ '[attr.aria-readonly]': 'readonly() ? "true" : null',
163
+ '[attr.disabled]': 'disabled() ? "" : null',
164
+ '[attr.aria-disabled]': "disabled() ? 'true' : null",
165
+ '[attr.aria-labelledby]': 'labelledBy()',
166
+ '[attr.aria-label]': 'label()',
167
+ '[attr.aria-valuetext]': 'valueTextValue()',
168
+ '[tabindex]': 'disabled() ? -1 : 0',
169
+ '(keydown)': 'onKeyDown($event)',
170
+ '(pointerleave)': 'clearHover()',
171
+ '(blur)': 'markTouched()',
172
+ }, template: "<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->\n<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->\n@for (index of symbols(); track index) {\n <span\n [ptInt]=\"this\"\n [ptClass]=\"'symbol'\"\n [style.--fillRatio]=\"fillRatio(index)\"\n (pointermove)=\"onPointerMove($event, index)\"\n (click)=\"onClick($event, index)\"\n >\n @if (resolvedIndicatorTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: fillRatio(index), index: index }\"\n />\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'empty'\">\n <ngn-icon [icon]=\"iconEmpty()\" defaultIcon=\"rating-empty\" />\n </span>\n <span [ptInt]=\"this\" [ptClass]=\"'full'\">\n <ngn-icon [icon]=\"iconFull()\" defaultIcon=\"rating-full\" />\n </span>\n }\n </span>\n}\n" }]
173
+ }], propDecorators: { count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], iconFull: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconFull", required: false }] }], iconEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconEmpty", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], valueText: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueText", required: false }] }], valueTextFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueTextFn", required: false }] }] } });
174
+
175
+ /**
176
+ * Generated bundle index. Do not edit.
177
+ */
178
+
179
+ export { NgnRating, RatingTemplates };
180
+ //# sourceMappingURL=ngneers-controls-rating.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-rating.mjs","sources":["../../src/rating/rating-templates.ts","../../src/rating/rating.ts","../../src/rating/rating.html","../../src/rating/ngneers-controls-rating.ts"],"sourcesContent":["import { computed, contentChild, Directive, input, TemplateRef } from '@angular/core';\nimport { ValueControlBase } from '@ngneers/controls/base';\n\n/** Context for a custom {@link NgnRating} symbol template. */\nexport interface RatingIndicatorContext {\n /** Fill ratio for this symbol, 0..1. */\n $implicit: number;\n /** Zero-based symbol index. */\n index: number;\n}\n\n@Directive()\nexport abstract class RatingTemplates extends ValueControlBase<'rating', number | null> {\n private readonly _userIndicatorTemplate =\n contentChild<TemplateRef<RatingIndicatorContext>>('indicator');\n /**\n * Custom template for a single symbol. Receives the fill ratio (`$implicit`,\n * 0..1) and `index`. Can also be set with an `<ng-template #indicator>`.\n * When set, it replaces the default full/empty icon rendering per symbol.\n */\n public readonly indicatorTemplate = input<TemplateRef<RatingIndicatorContext> | null>(null);\n protected readonly resolvedIndicatorTemplate = computed(\n () => this._userIndicatorTemplate() ?? this.indicatorTemplate()\n );\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport { booleanAttribute, Component, computed, input, signal } from '@angular/core';\nimport { NgnPt, provideSelf } from '@ngneers/controls/base';\nimport { NgnIcon } from '@ngneers/controls/icon';\nimport { ratingControlTemplate } from '@ngneers/controls-themes/templates/rating';\n\nimport { RatingTemplates } from './rating-templates';\n\nimport type { IconType } from '@ngneers/controls-custom-types';\n\n/**\n * @category control\n */\n@Component({\n selector: 'ngn-rating',\n templateUrl: './rating.html',\n imports: [NgTemplateOutlet, NgnPt, NgnIcon],\n providers: [provideSelf(NgnRating)],\n host: {\n role: 'slider',\n 'aria-valuemin': '0',\n '[attr.aria-valuemax]': 'count()',\n '[attr.aria-valuenow]': 'value()',\n '[attr.aria-readonly]': 'readonly() ? \"true\" : null',\n '[attr.disabled]': 'disabled() ? \"\" : null',\n '[attr.aria-disabled]': \"disabled() ? 'true' : null\",\n '[attr.aria-labelledby]': 'labelledBy()',\n '[attr.aria-label]': 'label()',\n '[attr.aria-valuetext]': 'valueTextValue()',\n '[tabindex]': 'disabled() ? -1 : 0',\n '(keydown)': 'onKeyDown($event)',\n '(pointerleave)': 'clearHover()',\n '(blur)': 'markTouched()',\n },\n})\nexport class NgnRating extends RatingTemplates {\n protected readonly theme = this.injectThemeTemplate(ratingControlTemplate, {\n root: true,\n invalid: () => this.invalidState(),\n readonly: () => this.readonly(),\n disabled: () => this.disabled(),\n });\n\n private readonly _hover = signal<number | null>(null);\n\n /** Number of symbols. @default 5 */\n public readonly count = input<number>(5);\n /** The value increment; supports fractions (e.g. 0.5). @default 1 */\n public readonly step = input<number>(1);\n /** The filled symbol icon. Falls back to a filled star ({@link https://tabler.io/icons}). */\n public readonly iconFull = input<IconType>();\n /** The empty symbol icon. Falls back to an outline star. */\n public readonly iconEmpty = input<IconType>();\n /** Clicking the current value clears the rating back to `null`. @default true */\n public readonly clearable = input(true, { transform: booleanAttribute });\n /** Accessible value text; overrides the numeric readout. */\n public readonly valueText = input<string>();\n /** Function producing accessible value text from the value. */\n public readonly valueTextFn = input<(value: number) => string>();\n\n /** The value the UI should paint: hover preview when hovering, else the model value. */\n protected readonly displayValue = computed(() => this._hover() ?? this.value() ?? 0);\n\n /** Zero-based indices for the symbols. */\n protected readonly symbols = computed(() => Array.from({ length: this.count() }, (_, i) => i));\n\n protected readonly valueTextValue = computed(() => {\n const valueText = this.valueText();\n if (valueText) {\n return valueText;\n }\n const fn = this.valueTextFn();\n if (fn) {\n return fn(this.value() ?? 0);\n }\n return null;\n });\n\n /** Fill ratio 0..1 for symbol `index` given the current display value (hover ?? value). */\n protected fillRatio(index: number): number {\n const filled = this.displayValue() - index;\n return Math.min(1, Math.max(0, filled));\n }\n\n protected onKeyDown(event: KeyboardEvent) {\n if (this.readonly() || this.disabled()) {\n return;\n }\n switch (event.key) {\n case 'ArrowLeft':\n case 'ArrowDown':\n // Decrementing below the first step clears the rating (0 is not a valid value).\n this.value.update(v => {\n const next = (v ?? 0) - this.step();\n return next <= 0 ? null : next;\n });\n event.preventDefault();\n break;\n case 'ArrowRight':\n case 'ArrowUp':\n this.value.update(v => Math.min(this.count(), (v ?? 0) + this.step()));\n event.preventDefault();\n break;\n case 'Home':\n // No value is the lowest state, since 0 is not selectable.\n this.value.set(null);\n event.preventDefault();\n break;\n case 'End':\n this.value.set(this.count());\n event.preventDefault();\n break;\n }\n }\n\n /** Pointer moved over symbol `index` at fractional `offset` (0..1 within the symbol). */\n protected onPointerMove(event: PointerEvent, index: number) {\n if (this.readonly() || this.disabled()) {\n return;\n }\n this._hover.set(this.valueAt(event, index));\n }\n\n protected clearHover() {\n this._hover.set(null);\n }\n\n protected onClick(event: PointerEvent, index: number) {\n if (this.readonly() || this.disabled()) {\n return;\n }\n const next = this.valueAt(event, index);\n if (this.clearable() && next === this.value()) {\n this.value.set(null);\n } else {\n this.value.set(next);\n }\n }\n\n /** Resolve the stepped value for a pointer position within symbol `index`. */\n private valueAt(event: PointerEvent, index: number): number {\n const target = event.currentTarget as HTMLElement;\n const rect = target.getBoundingClientRect();\n const ratio = rect.width ? (event.clientX - rect.left) / rect.width : 1;\n const clamped = Math.min(1, Math.max(0, ratio));\n // Fill on area-entry: within symbol `index`, snap UP to the nearest `step`\n // sub-division. Entering the symbol anywhere (ratio > 0) selects at least its\n // first sub-step, so a whole-step rating fills the entire symbol as soon as the\n // pointer is over it, and a half-step rating fills the left/right half.\n const step = this.step();\n const subStepsPerSymbol = Math.max(1, Math.round(1 / step));\n const subIndex = Math.min(\n subStepsPerSymbol,\n Math.max(1, Math.ceil(clamped * subStepsPerSymbol))\n );\n return this.clamp(index + subIndex * step);\n }\n\n private clamp(value: number): number {\n return Math.min(this.count(), Math.max(0, value));\n }\n}\n","<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->\n<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->\n@for (index of symbols(); track index) {\n <span\n [ptInt]=\"this\"\n [ptClass]=\"'symbol'\"\n [style.--fillRatio]=\"fillRatio(index)\"\n (pointermove)=\"onPointerMove($event, index)\"\n (click)=\"onClick($event, index)\"\n >\n @if (resolvedIndicatorTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: fillRatio(index), index: index }\"\n />\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'empty'\">\n <ngn-icon [icon]=\"iconEmpty()\" defaultIcon=\"rating-empty\" />\n </span>\n <span [ptInt]=\"this\" [ptClass]=\"'full'\">\n <ngn-icon [icon]=\"iconFull()\" defaultIcon=\"rating-full\" />\n </span>\n }\n </span>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,13 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, ElementRef, Injector, input, afterNextRender, Directive } from '@angular/core';
2
+ import { inject, ElementRef, Injector, Renderer2, input, booleanAttribute, afterNextRender, DestroyRef, Directive } from '@angular/core';
3
3
  import { scrollShadowDirectiveTemplate } from '@ngneers/controls-themes/templates/api';
4
- import { injectThemeTemplate, abortSignalOnDestroy } from '@ngneers/controls/api/ng';
4
+ import { injectThemeTemplate, domEventHandler } from '@ngneers/controls/api/ng';
5
5
  import { toggleClass } from '@ngneers/controls/utils';
6
6
 
7
7
  class NgnScrollShadow {
8
8
  theme = injectThemeTemplate(scrollShadowDirectiveTemplate);
9
9
  _el = inject(ElementRef);
10
10
  _injector = inject(Injector);
11
+ _renderer = inject(Renderer2);
11
12
  /**
12
13
  * Which scroll axis to track and add shadow classes for.
13
14
  * @default 'horizontal'
@@ -21,13 +22,37 @@ class NgnScrollShadow {
21
22
  */
22
23
  scrollShadowTarget = input(undefined, /* @ts-ignore */
23
24
  ...(ngDevMode ? [{ debugName: "scrollShadowTarget" }] : /* istanbul ignore next */ []));
25
+ /**
26
+ * Suppress the built-in edge-shadow overlay's styling (the theme hides it). The overlay element
27
+ * is still injected but rendered inert, so it never disturbs the scroll container's layout.
28
+ * Set this when the consumer paints its own shadows off the `scrolled-*` classes — e.g. the
29
+ * table anchors them to its sticky-column edges, reusing the shared `--ngn-scroll-shadow-color`.
30
+ * @default false
31
+ */
32
+ unstyled = input(false, { ...(ngDevMode ? { debugName: "unstyled" } : /* istanbul ignore next */ {}), alias: 'ngnScrollShadowUnstyled',
33
+ transform: booleanAttribute });
24
34
  constructor() {
25
35
  afterNextRender(() => {
26
36
  const scrollEl = this._el.nativeElement;
27
- const abort = abortSignalOnDestroy({ injector: this._injector });
37
+ // Injected overlay: a zero-size sticky layer pinned to the viewport's top-left corner, with a
38
+ // sized cover inside it. The cover's edge gradients (driven by the theme via the scrolled-*
39
+ // classes) paint over the scrolling content — painting on the container itself would sit
40
+ // *under* its children. When `unstyled`, the theme hides the layer (display:none), so it
41
+ // stays inert and never disturbs the container's layout (e.g. the table's CSS grid).
42
+ const layer = this._renderer.createElement('div');
43
+ layer.className = this.unstyled()
44
+ ? `${this.theme.class('overlay')} ${this.theme.class('unstyled')}`
45
+ : this.theme.class('overlay');
46
+ const surface = this._renderer.createElement('div');
47
+ surface.className = this.theme.class('surface');
48
+ this._renderer.appendChild(layer, surface);
49
+ this._renderer.insertBefore(scrollEl, layer, scrollEl.firstChild);
50
+ this._injector.get(DestroyRef).onDestroy(() => layer.remove());
28
51
  const update = () => {
29
52
  const target = this.scrollShadowTarget() ?? scrollEl;
30
53
  const dir = this.ngnScrollShadow();
54
+ surface.style.setProperty('--ngn-scroll-shadow-w', `${scrollEl.clientWidth}px`);
55
+ surface.style.setProperty('--ngn-scroll-shadow-h', `${scrollEl.clientHeight}px`);
31
56
  if (dir === 'horizontal' || dir === 'both') {
32
57
  const sl = scrollEl.scrollLeft;
33
58
  const maxH = scrollEl.scrollWidth - scrollEl.clientWidth;
@@ -43,19 +68,20 @@ class NgnScrollShadow {
43
68
  };
44
69
  const ro = new ResizeObserver(update);
45
70
  ro.observe(scrollEl);
46
- abort.addEventListener('abort', () => ro.disconnect());
47
- scrollEl.addEventListener('scroll', update, { passive: true, signal: abort });
71
+ this._injector.get(DestroyRef).onDestroy(() => ro.disconnect());
72
+ domEventHandler(scrollEl, 'scroll', update, this._injector, { passive: true });
73
+ update();
48
74
  });
49
75
  }
50
76
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnScrollShadow, deps: [], target: i0.ɵɵFactoryTarget.Directive });
51
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: NgnScrollShadow, isStandalone: true, selector: "[ngnScrollShadow]", inputs: { ngnScrollShadow: { classPropertyName: "ngnScrollShadow", publicName: "ngnScrollShadow", isSignal: true, isRequired: false, transformFunction: null }, scrollShadowTarget: { classPropertyName: "scrollShadowTarget", publicName: "scrollShadowTarget", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
77
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: NgnScrollShadow, isStandalone: true, selector: "[ngnScrollShadow]", inputs: { ngnScrollShadow: { classPropertyName: "ngnScrollShadow", publicName: "ngnScrollShadow", isSignal: true, isRequired: false, transformFunction: null }, scrollShadowTarget: { classPropertyName: "scrollShadowTarget", publicName: "scrollShadowTarget", isSignal: true, isRequired: false, transformFunction: null }, unstyled: { classPropertyName: "unstyled", publicName: "ngnScrollShadowUnstyled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
52
78
  }
53
79
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnScrollShadow, decorators: [{
54
80
  type: Directive,
55
81
  args: [{
56
82
  selector: '[ngnScrollShadow]',
57
83
  }]
58
- }], ctorParameters: () => [], propDecorators: { ngnScrollShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnScrollShadow", required: false }] }], scrollShadowTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollShadowTarget", required: false }] }] } });
84
+ }], ctorParameters: () => [], propDecorators: { ngnScrollShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnScrollShadow", required: false }] }], scrollShadowTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollShadowTarget", required: false }] }], unstyled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnScrollShadowUnstyled", required: false }] }] } });
59
85
 
60
86
  /**
61
87
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ngneers-controls-scroll-shadow.mjs","sources":["../../src/scroll-shadow/scroll-shadow.ts","../../src/scroll-shadow/ngneers-controls-scroll-shadow.ts"],"sourcesContent":["import { afterNextRender, Directive, ElementRef, inject, Injector, input } from '@angular/core';\nimport { scrollShadowDirectiveTemplate } from '@ngneers/controls-themes/templates/api';\n\nimport { abortSignalOnDestroy, injectThemeTemplate } from '@ngneers/controls/api/ng';\nimport { toggleClass } from '@ngneers/controls/utils';\n\n@Directive({\n selector: '[ngnScrollShadow]',\n})\nexport class NgnScrollShadow {\n protected readonly theme = injectThemeTemplate(scrollShadowDirectiveTemplate);\n private readonly _el = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly _injector = inject(Injector);\n\n /**\n * Which scroll axis to track and add shadow classes for.\n * @default 'horizontal'\n */\n public readonly ngnScrollShadow = input<'horizontal' | 'vertical' | 'both'>('horizontal');\n\n /**\n * Element that receives the shadow classes. Defaults to the host element (which\n * is also the scroll container being observed).\n * @default undefined\n */\n public readonly scrollShadowTarget = input<HTMLElement | undefined>(undefined);\n\n constructor() {\n afterNextRender(() => {\n const scrollEl = this._el.nativeElement;\n const abort = abortSignalOnDestroy({ injector: this._injector });\n\n const update = () => {\n const target = this.scrollShadowTarget() ?? scrollEl;\n const dir = this.ngnScrollShadow();\n\n if (dir === 'horizontal' || dir === 'both') {\n const sl = scrollEl.scrollLeft;\n const maxH = scrollEl.scrollWidth - scrollEl.clientWidth;\n toggleClass(target, this.theme.class('scrolled-start'), sl > 0);\n toggleClass(target, this.theme.class('scrolled-end'), maxH > 0 && sl < maxH - 1);\n }\n\n if (dir === 'vertical' || dir === 'both') {\n const st = scrollEl.scrollTop;\n const maxV = scrollEl.scrollHeight - scrollEl.clientHeight;\n toggleClass(target, this.theme.class('scrolled-top'), st > 0);\n toggleClass(target, this.theme.class('scrolled-bottom'), maxV > 0 && st < maxV - 1);\n }\n };\n\n const ro = new ResizeObserver(update);\n ro.observe(scrollEl);\n abort.addEventListener('abort', () => ro.disconnect());\n scrollEl.addEventListener('scroll', update, { passive: true, signal: abort });\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;MASa,eAAe,CAAA;AACP,IAAA,KAAK,GAAG,mBAAmB,CAAC,6BAA6B,CAAC;AAC5D,IAAA,GAAG,GAAG,MAAM,CAA0B,UAAU,CAAC;AACjD,IAAA,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;AAE7C;;;AAGG;IACa,eAAe,GAAG,KAAK,CAAqC,YAAY;wFAAC;AAEzF;;;;AAIG;IACa,kBAAkB,GAAG,KAAK,CAA0B,SAAS;2FAAC;AAE9E,IAAA,WAAA,GAAA;QACE,eAAe,CAAC,MAAK;AACnB,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa;AACvC,YAAA,MAAM,KAAK,GAAG,oBAAoB,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAK;gBAClB,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,QAAQ;AACpD,gBAAA,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,EAAE;gBAElC,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,MAAM,EAAE;AAC1C,oBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU;oBAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW;AACxD,oBAAA,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;oBAC/D,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;gBAClF;gBAEA,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,MAAM,EAAE;AACxC,oBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS;oBAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY;AAC1D,oBAAA,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;oBAC7D,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC;gBACrF;AACF,YAAA,CAAC;AAED,YAAA,MAAM,EAAE,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC;AACrC,YAAA,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;AACpB,YAAA,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,UAAU,EAAE,CAAC;AACtD,YAAA,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC/E,QAAA,CAAC,CAAC;IACJ;uGA/CW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAf,eAAe,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,MAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,kBAAA,EAAA,EAAA,iBAAA,EAAA,oBAAA,EAAA,UAAA,EAAA,oBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAf,eAAe,EAAA,UAAA,EAAA,CAAA;kBAH3B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,mBAAmB;AAC9B,iBAAA;;;ACRD;;AAEG;;;;"}
1
+ {"version":3,"file":"ngneers-controls-scroll-shadow.mjs","sources":["../../src/scroll-shadow/scroll-shadow.ts","../../src/scroll-shadow/ngneers-controls-scroll-shadow.ts"],"sourcesContent":["import {\n afterNextRender,\n booleanAttribute,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n Injector,\n input,\n Renderer2,\n} from '@angular/core';\nimport { scrollShadowDirectiveTemplate } from '@ngneers/controls-themes/templates/api';\n\nimport { domEventHandler, injectThemeTemplate } from '@ngneers/controls/api/ng';\nimport { toggleClass } from '@ngneers/controls/utils';\n\n@Directive({\n selector: '[ngnScrollShadow]',\n})\nexport class NgnScrollShadow {\n protected readonly theme = injectThemeTemplate(scrollShadowDirectiveTemplate);\n private readonly _el = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly _injector = inject(Injector);\n private readonly _renderer = inject(Renderer2);\n\n /**\n * Which scroll axis to track and add shadow classes for.\n * @default 'horizontal'\n */\n public readonly ngnScrollShadow = input<'horizontal' | 'vertical' | 'both'>('horizontal');\n\n /**\n * Element that receives the shadow classes. Defaults to the host element (which\n * is also the scroll container being observed).\n * @default undefined\n */\n public readonly scrollShadowTarget = input<HTMLElement | undefined>(undefined);\n\n /**\n * Suppress the built-in edge-shadow overlay's styling (the theme hides it). The overlay element\n * is still injected but rendered inert, so it never disturbs the scroll container's layout.\n * Set this when the consumer paints its own shadows off the `scrolled-*` classes — e.g. the\n * table anchors them to its sticky-column edges, reusing the shared `--ngn-scroll-shadow-color`.\n * @default false\n */\n public readonly unstyled = input(false, {\n alias: 'ngnScrollShadowUnstyled',\n transform: booleanAttribute,\n });\n\n constructor() {\n afterNextRender(() => {\n const scrollEl = this._el.nativeElement;\n\n // Injected overlay: a zero-size sticky layer pinned to the viewport's top-left corner, with a\n // sized cover inside it. The cover's edge gradients (driven by the theme via the scrolled-*\n // classes) paint over the scrolling content — painting on the container itself would sit\n // *under* its children. When `unstyled`, the theme hides the layer (display:none), so it\n // stays inert and never disturbs the container's layout (e.g. the table's CSS grid).\n const layer = this._renderer.createElement('div') as HTMLElement;\n layer.className = this.unstyled()\n ? `${this.theme.class('overlay')} ${this.theme.class('unstyled')}`\n : this.theme.class('overlay');\n const surface = this._renderer.createElement('div') as HTMLElement;\n surface.className = this.theme.class('surface');\n this._renderer.appendChild(layer, surface);\n this._renderer.insertBefore(scrollEl, layer, scrollEl.firstChild);\n this._injector.get(DestroyRef).onDestroy(() => layer.remove());\n\n const update = () => {\n const target = this.scrollShadowTarget() ?? scrollEl;\n const dir = this.ngnScrollShadow();\n\n surface.style.setProperty('--ngn-scroll-shadow-w', `${scrollEl.clientWidth}px`);\n surface.style.setProperty('--ngn-scroll-shadow-h', `${scrollEl.clientHeight}px`);\n\n if (dir === 'horizontal' || dir === 'both') {\n const sl = scrollEl.scrollLeft;\n const maxH = scrollEl.scrollWidth - scrollEl.clientWidth;\n toggleClass(target, this.theme.class('scrolled-start'), sl > 0);\n toggleClass(target, this.theme.class('scrolled-end'), maxH > 0 && sl < maxH - 1);\n }\n\n if (dir === 'vertical' || dir === 'both') {\n const st = scrollEl.scrollTop;\n const maxV = scrollEl.scrollHeight - scrollEl.clientHeight;\n toggleClass(target, this.theme.class('scrolled-top'), st > 0);\n toggleClass(target, this.theme.class('scrolled-bottom'), maxV > 0 && st < maxV - 1);\n }\n };\n\n const ro = new ResizeObserver(update);\n ro.observe(scrollEl);\n this._injector.get(DestroyRef).onDestroy(() => ro.disconnect());\n domEventHandler(scrollEl, 'scroll', update, this._injector, { passive: true });\n update();\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -41,6 +41,8 @@ class NgnScroller extends ScrollerTemplates {
41
41
  });
42
42
  _el = inject(ElementRef);
43
43
  _scrollAmount = inject(NgnScrollAmount);
44
+ /** Remaining vertical scroll distance to the bottom (px). Proxied from the host scroll directive. */
45
+ distanceFromEnd = this._scrollAmount.distanceFromEnd;
44
46
  /**
45
47
  * The items to be displayed in the scroller.
46
48
  * This is a required input and should be an array of items of type {@link T}.
@@ -233,7 +235,7 @@ class NgnScroller extends ScrollerTemplates {
233
235
  return item.index;
234
236
  }
235
237
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnScroller, deps: [], target: i0.ɵɵFactoryTarget.Component });
236
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnScroller, isStandalone: true, selector: "ngn-scroller, [ngn-scroller]", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, fieldId: { classPropertyName: "fieldId", publicName: "fieldId", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null }, virtualPadding: { classPropertyName: "virtualPadding", publicName: "virtualPadding", isSignal: true, isRequired: false, transformFunction: null }, focusable: { classPropertyName: "focusable", publicName: "focusable", isSignal: true, isRequired: false, transformFunction: null }, fieldSticky: { classPropertyName: "fieldSticky", publicName: "fieldSticky", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "tabIndex": "focusable() ? 0 : -1", "style.--ngn-scroller-item-height.px": "itemHeight() ?? \"auto\"", "style.--ngn-scroller-padding-top.px": "paddingTop()", "style.--ngn-scroller-padding-bottom.px": "paddingBottom()" } }, providers: [provideSelf(NgnScroller)], usesInheritance: true, hostDirectives: [{ directive: i1.NgnScrollAmount, inputs: ["ngnScrollAmountContainer", "scrollContainer"] }], ngImport: i0, template: "@if (latestStickyItem(); as latestStickyItem) {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\n \"\n ></ng-container>\n}\n\n@for (item of visibleItems(); track track(item)) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\n ></ng-container>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
238
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnScroller, isStandalone: true, selector: "ngn-scroller, [ngn-scroller]", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, fieldId: { classPropertyName: "fieldId", publicName: "fieldId", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null }, virtualPadding: { classPropertyName: "virtualPadding", publicName: "virtualPadding", isSignal: true, isRequired: false, transformFunction: null }, focusable: { classPropertyName: "focusable", publicName: "focusable", isSignal: true, isRequired: false, transformFunction: null }, fieldSticky: { classPropertyName: "fieldSticky", publicName: "fieldSticky", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "tabIndex": "focusable() ? 0 : -1", "style.--ngn-scroller-item-height.px": "itemHeight() ?? \"auto\"", "style.--ngn-scroller-padding-top.px": "paddingTop()", "style.--ngn-scroller-padding-bottom.px": "paddingBottom()" } }, providers: [provideSelf(NgnScroller)], usesInheritance: true, hostDirectives: [{ directive: i1.NgnScrollAmount, inputs: ["ngnScrollAmountContainer", "scrollContainer"] }], ngImport: i0, template: "@if (latestStickyItem(); as latestStickyItem) {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\n \"\n ></ng-container>\n}\n\n@for (item of visibleItems(); track track(item)) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\n ></ng-container>\n}\n\n<!-- Projects static content added alongside the scroller (e.g. table skeleton rows). -->\n<ng-content></ng-content>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
237
239
  }
238
240
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnScroller, decorators: [{
239
241
  type: Component,
@@ -244,7 +246,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
244
246
  '[style.--ngn-scroller-item-height.px]': 'itemHeight() ?? "auto"',
245
247
  '[style.--ngn-scroller-padding-top.px]': 'paddingTop()',
246
248
  '[style.--ngn-scroller-padding-bottom.px]': 'paddingBottom()',
247
- }, template: "@if (latestStickyItem(); as latestStickyItem) {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\n \"\n ></ng-container>\n}\n\n@for (item of visibleItems(); track track(item)) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\n ></ng-container>\n}\n" }]
249
+ }, template: "@if (latestStickyItem(); as latestStickyItem) {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\n \"\n ></ng-container>\n}\n\n@for (item of visibleItems(); track track(item)) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\n ></ng-container>\n}\n\n<!-- Projects static content added alongside the scroller (e.g. table skeleton rows). -->\n<ng-content></ng-content>\n" }]
248
250
  }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], fieldId: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldId", required: false }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], itemHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemHeight", required: false }] }], virtualPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualPadding", required: false }] }], focusable: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusable", required: false }] }], fieldSticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldSticky", required: false }] }] } });
249
251
 
250
252
  class NgnScrollerItem {
@@ -1 +1 @@
1
- {"version":3,"file":"ngneers-controls-scroller.mjs","sources":["../../src/scroller/scroller-templates.ts","../../src/scroller/scroller.ts","../../src/scroller/scroller.html","../../src/scroller/scroller-item.ts","../../src/scroller/ngneers-controls-scroller.ts"],"sourcesContent":["import { computed, contentChild, Directive, input, TemplateRef } from '@angular/core';\nimport { templateTypesFn } from '@ngneers/controls/api/ng';\nimport { NgnBase } from '@ngneers/controls/base';\n\n@Directive()\nexport abstract class ScrollerTemplates<T> extends NgnBase<'scroller'> {\n /**\n * The template to be used for rendering each item in the scroller.\n * Can also be set using the `item` content child.\n */\n public readonly templateItem = input<TemplateRef<typeof this.templateTypes.item> | null>(null);\n private readonly _userItemTemplate =\n contentChild<TemplateRef<typeof this.templateTypes.item>>('item');\n protected readonly itemTemplate = computed(() => this._userItemTemplate() ?? this.templateItem());\n\n /**\n * Template types for the scroller.\n * Can be used with the {@link NgnTemplate} directive for type safe ng-templates.\n */\n public readonly templateTypes = templateTypesFn<{\n item: {\n $implicit: T;\n index: number;\n };\n }>();\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n input,\n untracked,\n} from '@angular/core';\nimport { elementSizeSignal } from '@ngneers/controls/api/ng';\nimport { provideSelf } from '@ngneers/controls/base';\nimport { NgnScrollAmount } from '@ngneers/controls/directives';\nimport { type AllKeysOfUnion, getScrollTop, NgnError } from '@ngneers/controls/utils';\nimport { scrollerControlTemplate } from '@ngneers/controls-themes/templates/scroller';\n\nimport { ScrollerTemplates } from './scroller-templates';\n\n/**\n * @category control\n */\n@Component({\n selector: 'ngn-scroller, [ngn-scroller]',\n templateUrl: './scroller.html',\n imports: [NgTemplateOutlet],\n providers: [provideSelf(NgnScroller)],\n hostDirectives: [\n { directive: NgnScrollAmount, inputs: ['ngnScrollAmountContainer: scrollContainer'] },\n ],\n host: {\n '[tabIndex]': 'focusable() ? 0 : -1',\n '[style.--ngn-scroller-item-height.px]': 'itemHeight() ?? \"auto\"',\n '[style.--ngn-scroller-padding-top.px]': 'paddingTop()',\n '[style.--ngn-scroller-padding-bottom.px]': 'paddingBottom()',\n },\n})\nexport class NgnScroller<T> extends ScrollerTemplates<T> {\n protected readonly theme = this.injectThemeTemplate(scrollerControlTemplate, {\n root: true,\n virtual: () => !!this.virtual(),\n });\n private readonly _el = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly _scrollAmount = inject(NgnScrollAmount);\n\n /**\n * The items to be displayed in the scroller.\n * This is a required input and should be an array of items of type {@link T}.\n */\n public readonly items = input.required<readonly T[]>();\n /**\n * The field of the item that uniquely identifies it.\n */\n public readonly fieldId = input<keyof T>();\n /**\n * Whether the scroller should use virtual scrolling.\n * When set to true, the scroller will only render the items that are currently visible\n * on the screen, improving performance for large lists.\n *\n * When virtual scrolling is enabled, {@link itemHeight} must be set to a non-zero value.\n * @default `false`\n */\n public readonly virtual = input(false, { transform: booleanAttribute });\n /**\n * When {@link virtual} scrolling is enabled, this input is required to define the height of each item in pixels.\n */\n public readonly itemHeight = input<number | undefined>(undefined);\n /**\n * When {@link virtual} scrolling is enabled, this input defines the number of items to be rendered above and below the visible area.\n * This can help to reduce flickering when scrolling.\n * @default `2`\n */\n public readonly virtualPadding = input<number>(2);\n /**\n * Determines whether the scroller should have a tab index and be focusable.\n * @default `false`\n */\n public readonly focusable = input(false, { transform: booleanAttribute });\n /**\n * Determines whether an item is sticky or not.\n * If set, the scroller will stick the items with a truthy value for the specified field to the top of the scroller.\n */\n public readonly fieldSticky = input<AllKeysOfUnion<T> | null>(null);\n\n /**\n * Viewport size — tracks the scroll ancestor's dimensions so that virtual\n * scrolling works when an outer element handles overflow (e.g. table wrapper).\n */\n private readonly _viewportSize = elementSizeSignal(\n computed(() => this._scrollAmount.scrollTarget())\n );\n\n private readonly _itemHeightPx = computed(() => this.itemHeight() ?? 0);\n\n /**\n * Vertical offset between the scroll container's top and the scroller element.\n * Non-zero when a parent (e.g. table wrapper) owns the scroll and the scroller\n * is positioned below other content (e.g. a sticky header row).\n */\n private readonly _scrollOffset = computed(() => {\n const target = this._scrollAmount.scrollTarget();\n if (target === this._el.nativeElement) return 0;\n return this._el.nativeElement.offsetTop;\n });\n\n private readonly _visibleItemCount = computed(() => {\n if (!this.virtual() || this._itemHeightPx() === 0) {\n return 0;\n }\n return Math.ceil(\n this._viewportSize().height / this._itemHeightPx() + this.virtualPadding() * 2\n );\n });\n private readonly _scrollTop = computed(() => {\n const raw = this._scrollAmount.scrollTop();\n return Math.max(0, raw - this._scrollOffset());\n });\n /**\n * The index of the first item that is rendered in the (virtual) scroller.\n */\n public readonly itemStartIndex = computed(() => {\n if (!this.virtual() || this._itemHeightPx() === 0) {\n return 0;\n }\n return Math.max(0, Math.ceil(this._scrollTop() / this._itemHeightPx()) - this.virtualPadding());\n });\n private readonly _itemEndIndex = computed(() =>\n this.virtual()\n ? Math.min(this.items().length, this.itemStartIndex() + this._visibleItemCount())\n : 0\n );\n\n protected readonly paddingTop = computed(() => {\n if (!this.virtual()) {\n return 0;\n }\n return (this.itemStartIndex() - (this.latestStickyItem() ? 1 : 0)) * this._itemHeightPx();\n });\n\n protected readonly paddingBottom = computed(() => {\n if (!this.virtual()) {\n return 0;\n }\n return (this.items().length - this._itemEndIndex()) * this._itemHeightPx();\n });\n\n protected readonly visibleItems = computed(() => {\n if (!this.virtual()) {\n return this.items().map((item, index) => ({ item, index }));\n }\n return this.items()\n .slice(this.itemStartIndex(), this._itemEndIndex())\n .map((item, index) => {\n return {\n item,\n index: this.itemStartIndex() + index,\n };\n });\n });\n\n /**\n * Returns the last sticky item that is not rendered regularly in the list.\n * Required because sticky items might need to be rendered longer than they would.\n * Only applicable when virtual scrolling is enabled.\n */\n protected readonly latestStickyItem = computed(() => {\n if (!this.virtual()) {\n return null;\n }\n const stickyField = this.fieldSticky();\n if (!stickyField) {\n return null;\n }\n const unrenderedItemsAtTop = this.items()\n .slice(0, this.itemStartIndex())\n .map((item, index) => ({\n item,\n index,\n }));\n const stickyItems = unrenderedItemsAtTop.filter(item => item.item[stickyField]);\n return stickyItems[stickyItems.length - 1] ?? null;\n });\n\n constructor() {\n super();\n effect(() => {\n if (this.virtual() && !this.itemHeight()) {\n throw new NgnError('scroller', 'itemHeight must be set when virtual is true');\n }\n });\n }\n\n /**\n * Scrolls the item with the given index into view.\n * @param index The index of the item to scroll to.\n */\n public scrollToIndex(index: number, position: ScrollLogicalPosition = 'nearest') {\n untracked(() => {\n const scrollEl = this._scrollAmount.scrollTarget();\n const offset = this._scrollOffset();\n\n const getItemSize = () => {\n if (this.virtual()) {\n const itemHeight = this._itemHeightPx();\n const itemTop = index * itemHeight + offset;\n return { itemHeight, itemTop };\n } else {\n let itemElement = this._el.nativeElement.children[index] as HTMLElement | null;\n while (!itemElement?.clientHeight && itemElement?.children.length) {\n itemElement = itemElement?.children[0] as HTMLElement | null;\n }\n if (itemElement) {\n const itemTop = itemElement.offsetTop;\n const itemHeight = itemElement.clientHeight;\n return { itemTop, itemHeight };\n }\n return null;\n }\n };\n const size = getItemSize();\n if (!size) {\n return;\n }\n const { itemTop, itemHeight } = size;\n scrollEl.scrollTo({\n top: getScrollTop(itemTop, itemHeight, scrollEl.clientHeight, scrollEl.scrollTop, position),\n });\n });\n }\n\n protected track(item: { item: T; index: number }): unknown {\n const fieldId = this.fieldId();\n if (fieldId) {\n return item.item[fieldId];\n }\n return item.index;\n }\n}\n","@if (latestStickyItem(); as latestStickyItem) {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\n \"\n ></ng-container>\n}\n\n@for (item of visibleItems(); track track(item)) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\n ></ng-container>\n}\n","import {\n type AfterViewInit,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { getNearestNgnInstance } from '@ngneers/controls/base';\nimport { NgnError, toggleClass } from '@ngneers/controls/utils';\n\nimport { NgnScroller } from './scroller';\n\n@Directive({ selector: '[ngnScrollerItem]' })\nexport class NgnScrollerItem implements AfterViewInit {\n private readonly _el = inject(ElementRef<HTMLElement>);\n /**\n * The item bound to this element. Used to resolve its sticky state and apply\n * the scroller's item classes from the enclosing {@link NgnScroller}.\n */\n public readonly ngnScrollerItem = input.required<object>();\n private readonly _scroller = signal<NgnScroller<unknown> | null>(null);\n\n public ngAfterViewInit() {\n if (!this._el.nativeElement.isConnected) {\n return;\n }\n const parentInstance = getNearestNgnInstance(this._el.nativeElement, NgnScroller);\n if (!parentInstance) {\n throw new NgnError(\n 'scroller',\n 'ngnScrollerItem must be used within an NgnScroller component'\n );\n }\n this._scroller.set(parentInstance);\n }\n\n constructor() {\n let prevItemClass = '';\n let prevStickyClass = '';\n effect(() => {\n const item = this.ngnScrollerItem() as Record<string, unknown> | null;\n const scroller = this._scroller();\n if (!item || !scroller) {\n return;\n }\n const stickyField = scroller.fieldSticky() as unknown as string | null;\n const isSticky = !!stickyField && stickyField in item ? !!item[stickyField] : false;\n\n const stickyClass = scroller['theme'].class('item-sticky');\n if (prevStickyClass && prevStickyClass !== stickyClass) {\n toggleClass(this._el.nativeElement, prevStickyClass, false);\n }\n toggleClass(this._el.nativeElement, stickyClass, isSticky);\n prevStickyClass = stickyClass;\n\n const itemClass = scroller['theme'].class('item');\n if (prevItemClass && prevItemClass !== itemClass) {\n toggleClass(this._el.nativeElement, prevItemClass, false);\n }\n toggleClass(this._el.nativeElement, itemClass, true);\n prevItemClass = itemClass;\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngneers-controls-scroller.mjs","sources":["../../src/scroller/scroller-templates.ts","../../src/scroller/scroller.ts","../../src/scroller/scroller.html","../../src/scroller/scroller-item.ts","../../src/scroller/ngneers-controls-scroller.ts"],"sourcesContent":["import { computed, contentChild, Directive, input, TemplateRef } from '@angular/core';\nimport { templateTypesFn } from '@ngneers/controls/api/ng';\nimport { NgnBase } from '@ngneers/controls/base';\n\n@Directive()\nexport abstract class ScrollerTemplates<T> extends NgnBase<'scroller'> {\n /**\n * The template to be used for rendering each item in the scroller.\n * Can also be set using the `item` content child.\n */\n public readonly templateItem = input<TemplateRef<typeof this.templateTypes.item> | null>(null);\n private readonly _userItemTemplate =\n contentChild<TemplateRef<typeof this.templateTypes.item>>('item');\n protected readonly itemTemplate = computed(() => this._userItemTemplate() ?? this.templateItem());\n\n /**\n * Template types for the scroller.\n * Can be used with the {@link NgnTemplate} directive for type safe ng-templates.\n */\n public readonly templateTypes = templateTypesFn<{\n item: {\n $implicit: T;\n index: number;\n };\n }>();\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n input,\n untracked,\n} from '@angular/core';\nimport { elementSizeSignal } from '@ngneers/controls/api/ng';\nimport { provideSelf } from '@ngneers/controls/base';\nimport { NgnScrollAmount } from '@ngneers/controls/directives';\nimport { type AllKeysOfUnion, getScrollTop, NgnError } from '@ngneers/controls/utils';\nimport { scrollerControlTemplate } from '@ngneers/controls-themes/templates/scroller';\n\nimport { ScrollerTemplates } from './scroller-templates';\n\n/**\n * @category control\n */\n@Component({\n selector: 'ngn-scroller, [ngn-scroller]',\n templateUrl: './scroller.html',\n imports: [NgTemplateOutlet],\n providers: [provideSelf(NgnScroller)],\n hostDirectives: [\n { directive: NgnScrollAmount, inputs: ['ngnScrollAmountContainer: scrollContainer'] },\n ],\n host: {\n '[tabIndex]': 'focusable() ? 0 : -1',\n '[style.--ngn-scroller-item-height.px]': 'itemHeight() ?? \"auto\"',\n '[style.--ngn-scroller-padding-top.px]': 'paddingTop()',\n '[style.--ngn-scroller-padding-bottom.px]': 'paddingBottom()',\n },\n})\nexport class NgnScroller<T> extends ScrollerTemplates<T> {\n protected readonly theme = this.injectThemeTemplate(scrollerControlTemplate, {\n root: true,\n virtual: () => !!this.virtual(),\n });\n private readonly _el = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly _scrollAmount = inject(NgnScrollAmount);\n\n /** Remaining vertical scroll distance to the bottom (px). Proxied from the host scroll directive. */\n public readonly distanceFromEnd = this._scrollAmount.distanceFromEnd;\n\n /**\n * The items to be displayed in the scroller.\n * This is a required input and should be an array of items of type {@link T}.\n */\n public readonly items = input.required<readonly T[]>();\n /**\n * The field of the item that uniquely identifies it.\n */\n public readonly fieldId = input<keyof T>();\n /**\n * Whether the scroller should use virtual scrolling.\n * When set to true, the scroller will only render the items that are currently visible\n * on the screen, improving performance for large lists.\n *\n * When virtual scrolling is enabled, {@link itemHeight} must be set to a non-zero value.\n * @default `false`\n */\n public readonly virtual = input(false, { transform: booleanAttribute });\n /**\n * When {@link virtual} scrolling is enabled, this input is required to define the height of each item in pixels.\n */\n public readonly itemHeight = input<number | undefined>(undefined);\n /**\n * When {@link virtual} scrolling is enabled, this input defines the number of items to be rendered above and below the visible area.\n * This can help to reduce flickering when scrolling.\n * @default `2`\n */\n public readonly virtualPadding = input<number>(2);\n /**\n * Determines whether the scroller should have a tab index and be focusable.\n * @default `false`\n */\n public readonly focusable = input(false, { transform: booleanAttribute });\n /**\n * Determines whether an item is sticky or not.\n * If set, the scroller will stick the items with a truthy value for the specified field to the top of the scroller.\n */\n public readonly fieldSticky = input<AllKeysOfUnion<T> | null>(null);\n\n /**\n * Viewport size — tracks the scroll ancestor's dimensions so that virtual\n * scrolling works when an outer element handles overflow (e.g. table wrapper).\n */\n private readonly _viewportSize = elementSizeSignal(\n computed(() => this._scrollAmount.scrollTarget())\n );\n\n private readonly _itemHeightPx = computed(() => this.itemHeight() ?? 0);\n\n /**\n * Vertical offset between the scroll container's top and the scroller element.\n * Non-zero when a parent (e.g. table wrapper) owns the scroll and the scroller\n * is positioned below other content (e.g. a sticky header row).\n */\n private readonly _scrollOffset = computed(() => {\n const target = this._scrollAmount.scrollTarget();\n if (target === this._el.nativeElement) return 0;\n return this._el.nativeElement.offsetTop;\n });\n\n private readonly _visibleItemCount = computed(() => {\n if (!this.virtual() || this._itemHeightPx() === 0) {\n return 0;\n }\n return Math.ceil(\n this._viewportSize().height / this._itemHeightPx() + this.virtualPadding() * 2\n );\n });\n private readonly _scrollTop = computed(() => {\n const raw = this._scrollAmount.scrollTop();\n return Math.max(0, raw - this._scrollOffset());\n });\n /**\n * The index of the first item that is rendered in the (virtual) scroller.\n */\n public readonly itemStartIndex = computed(() => {\n if (!this.virtual() || this._itemHeightPx() === 0) {\n return 0;\n }\n return Math.max(0, Math.ceil(this._scrollTop() / this._itemHeightPx()) - this.virtualPadding());\n });\n private readonly _itemEndIndex = computed(() =>\n this.virtual()\n ? Math.min(this.items().length, this.itemStartIndex() + this._visibleItemCount())\n : 0\n );\n\n protected readonly paddingTop = computed(() => {\n if (!this.virtual()) {\n return 0;\n }\n return (this.itemStartIndex() - (this.latestStickyItem() ? 1 : 0)) * this._itemHeightPx();\n });\n\n protected readonly paddingBottom = computed(() => {\n if (!this.virtual()) {\n return 0;\n }\n return (this.items().length - this._itemEndIndex()) * this._itemHeightPx();\n });\n\n protected readonly visibleItems = computed(() => {\n if (!this.virtual()) {\n return this.items().map((item, index) => ({ item, index }));\n }\n return this.items()\n .slice(this.itemStartIndex(), this._itemEndIndex())\n .map((item, index) => {\n return {\n item,\n index: this.itemStartIndex() + index,\n };\n });\n });\n\n /**\n * Returns the last sticky item that is not rendered regularly in the list.\n * Required because sticky items might need to be rendered longer than they would.\n * Only applicable when virtual scrolling is enabled.\n */\n protected readonly latestStickyItem = computed(() => {\n if (!this.virtual()) {\n return null;\n }\n const stickyField = this.fieldSticky();\n if (!stickyField) {\n return null;\n }\n const unrenderedItemsAtTop = this.items()\n .slice(0, this.itemStartIndex())\n .map((item, index) => ({\n item,\n index,\n }));\n const stickyItems = unrenderedItemsAtTop.filter(item => item.item[stickyField]);\n return stickyItems[stickyItems.length - 1] ?? null;\n });\n\n constructor() {\n super();\n effect(() => {\n if (this.virtual() && !this.itemHeight()) {\n throw new NgnError('scroller', 'itemHeight must be set when virtual is true');\n }\n });\n }\n\n /**\n * Scrolls the item with the given index into view.\n * @param index The index of the item to scroll to.\n */\n public scrollToIndex(index: number, position: ScrollLogicalPosition = 'nearest') {\n untracked(() => {\n const scrollEl = this._scrollAmount.scrollTarget();\n const offset = this._scrollOffset();\n\n const getItemSize = () => {\n if (this.virtual()) {\n const itemHeight = this._itemHeightPx();\n const itemTop = index * itemHeight + offset;\n return { itemHeight, itemTop };\n } else {\n let itemElement = this._el.nativeElement.children[index] as HTMLElement | null;\n while (!itemElement?.clientHeight && itemElement?.children.length) {\n itemElement = itemElement?.children[0] as HTMLElement | null;\n }\n if (itemElement) {\n const itemTop = itemElement.offsetTop;\n const itemHeight = itemElement.clientHeight;\n return { itemTop, itemHeight };\n }\n return null;\n }\n };\n const size = getItemSize();\n if (!size) {\n return;\n }\n const { itemTop, itemHeight } = size;\n scrollEl.scrollTo({\n top: getScrollTop(itemTop, itemHeight, scrollEl.clientHeight, scrollEl.scrollTop, position),\n });\n });\n }\n\n protected track(item: { item: T; index: number }): unknown {\n const fieldId = this.fieldId();\n if (fieldId) {\n return item.item[fieldId];\n }\n return item.index;\n }\n}\n","@if (latestStickyItem(); as latestStickyItem) {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: { $implicit: latestStickyItem.item, index: latestStickyItem.index }\n \"\n ></ng-container>\n}\n\n@for (item of visibleItems(); track track(item)) {\n <ng-container\n *ngTemplateOutlet=\"itemTemplate(); context: { $implicit: item.item, index: item.index }\"\n ></ng-container>\n}\n\n<!-- Projects static content added alongside the scroller (e.g. table skeleton rows). -->\n<ng-content></ng-content>\n","import {\n type AfterViewInit,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport { getNearestNgnInstance } from '@ngneers/controls/base';\nimport { NgnError, toggleClass } from '@ngneers/controls/utils';\n\nimport { NgnScroller } from './scroller';\n\n@Directive({ selector: '[ngnScrollerItem]' })\nexport class NgnScrollerItem implements AfterViewInit {\n private readonly _el = inject(ElementRef<HTMLElement>);\n /**\n * The item bound to this element. Used to resolve its sticky state and apply\n * the scroller's item classes from the enclosing {@link NgnScroller}.\n */\n public readonly ngnScrollerItem = input.required<object>();\n private readonly _scroller = signal<NgnScroller<unknown> | null>(null);\n\n public ngAfterViewInit() {\n if (!this._el.nativeElement.isConnected) {\n return;\n }\n const parentInstance = getNearestNgnInstance(this._el.nativeElement, NgnScroller);\n if (!parentInstance) {\n throw new NgnError(\n 'scroller',\n 'ngnScrollerItem must be used within an NgnScroller component'\n );\n }\n this._scroller.set(parentInstance);\n }\n\n constructor() {\n let prevItemClass = '';\n let prevStickyClass = '';\n effect(() => {\n const item = this.ngnScrollerItem() as Record<string, unknown> | null;\n const scroller = this._scroller();\n if (!item || !scroller) {\n return;\n }\n const stickyField = scroller.fieldSticky() as unknown as string | null;\n const isSticky = !!stickyField && stickyField in item ? !!item[stickyField] : false;\n\n const stickyClass = scroller['theme'].class('item-sticky');\n if (prevStickyClass && prevStickyClass !== stickyClass) {\n toggleClass(this._el.nativeElement, prevStickyClass, false);\n }\n toggleClass(this._el.nativeElement, stickyClass, isSticky);\n prevStickyClass = stickyClass;\n\n const itemClass = scroller['theme'].class('item');\n if (prevItemClass && prevItemClass !== itemClass) {\n toggleClass(this._el.nativeElement, prevItemClass, false);\n }\n toggleClass(this._el.nativeElement, itemClass, true);\n prevItemClass = itemClass;\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;;AAKM,MAAgB,iBAAqB,SAAQ,OAAmB,CAAA;AACpE;;;AAGG;IACa,YAAY,GAAG,KAAK,CAAqD,IAAI;qFAAC;IAC7E,iBAAiB,GAChC,YAAY,CAA8C,MAAM;0FAAC;AAChD,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,IAAI,IAAI,CAAC,YAAY,EAAE;qFAAC;AAEjG;;;AAGG;IACa,aAAa,GAAG,eAAe,EAK3C;uGAnBgB,iBAAiB,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAjB,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,mBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,MAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,eAAA,EAAA,IAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAjB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBADtC;0LAQ6D,MAAM,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;ACOpE;;AAEG;AAgBG,MAAO,WAAe,SAAQ,iBAAoB,CAAA;AACnC,IAAA,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,uBAAuB,EAAE;AAC3E,QAAA,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE;AAChC,KAAA,CAAC;AACe,IAAA,GAAG,GAAG,MAAM,CAA0B,UAAU,CAAC;AACjD,IAAA,aAAa,GAAG,MAAM,CAAC,eAAe,CAAC;;AAGxC,IAAA,eAAe,GAAG,IAAI,CAAC,aAAa,CAAC,eAAe;AAEpE;;;AAGG;IACa,KAAK,GAAG,KAAK,CAAC,QAAQ;8EAAgB;AACtD;;AAEG;AACa,IAAA,OAAO,GAAG,KAAK;2FAAW;AAC1C;;;;;;;AAOG;IACa,OAAO,GAAG,KAAK,CAAC,KAAK,+EAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;AACvE;;AAEG;IACa,UAAU,GAAG,KAAK,CAAqB,SAAS;mFAAC;AACjE;;;;AAIG;IACa,cAAc,GAAG,KAAK,CAAS,CAAC;uFAAC;AACjD;;;AAGG;IACa,SAAS,GAAG,KAAK,CAAC,KAAK,iFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;AACzE;;;AAGG;IACa,WAAW,GAAG,KAAK,CAA2B,IAAI;oFAAC;AAEnE;;;AAGG;AACc,IAAA,aAAa,GAAG,iBAAiB,CAChD,QAAQ,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC,CAClD;IAEgB,aAAa,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC;sFAAC;AAEvE;;;;AAIG;AACc,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE;AAChD,QAAA,IAAI,MAAM,KAAK,IAAI,CAAC,GAAG,CAAC,aAAa;AAAE,YAAA,OAAO,CAAC;AAC/C,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,SAAS;IACzC,CAAC;sFAAC;AAEe,IAAA,iBAAiB,GAAG,QAAQ,CAAC,MAAK;AACjD,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE;AACjD,YAAA,OAAO,CAAC;QACV;QACA,OAAO,IAAI,CAAC,IAAI,CACd,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAC/E;IACH,CAAC;0FAAC;AACe,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE;AAC1C,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;IAChD,CAAC;mFAAC;AACF;;AAEG;AACa,IAAA,cAAc,GAAG,QAAQ,CAAC,MAAK;AAC7C,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE;AACjD,YAAA,OAAO,CAAC;QACV;QACA,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACjG,CAAC;uFAAC;IACe,aAAa,GAAG,QAAQ,CAAC,MACxC,IAAI,CAAC,OAAO;UACR,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE;AAChF,UAAE,CAAC;sFACN;AAEkB,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;AAC5C,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;AACnB,YAAA,OAAO,CAAC;QACV;QACA,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE;IAC3F,CAAC;mFAAC;AAEiB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;AACnB,YAAA,OAAO,CAAC;QACV;AACA,QAAA,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,IAAI,CAAC,aAAa,EAAE;IAC5E,CAAC;sFAAC;AAEiB,IAAA,YAAY,GAAG,QAAQ,CAAC,MAAK;AAC9C,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;YACnB,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D;QACA,OAAO,IAAI,CAAC,KAAK;aACd,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE;AACjD,aAAA,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,KAAI;YACnB,OAAO;gBACL,IAAI;AACJ,gBAAA,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,GAAG,KAAK;aACrC;AACH,QAAA,CAAC,CAAC;IACN,CAAC;qFAAC;AAEF;;;;AAIG;AACgB,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAK;AAClD,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE;AACnB,YAAA,OAAO,IAAI;QACb;AACA,QAAA,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE;QACtC,IAAI,CAAC,WAAW,EAAE;AAChB,YAAA,OAAO,IAAI;QACb;AACA,QAAA,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK;AACpC,aAAA,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE;aAC9B,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM;YACrB,IAAI;YACJ,KAAK;AACN,SAAA,CAAC,CAAC;AACL,QAAA,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC/E,OAAO,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI;IACpD,CAAC;yFAAC;AAEF,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;QACP,MAAM,CAAC,MAAK;YACV,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE;AACxC,gBAAA,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,6CAA6C,CAAC;YAC/E;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;;AAGG;AACI,IAAA,aAAa,CAAC,KAAa,EAAE,QAAA,GAAkC,SAAS,EAAA;QAC7E,SAAS,CAAC,MAAK;YACb,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE;AAClD,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE;YAEnC,MAAM,WAAW,GAAG,MAAK;AACvB,gBAAA,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;AAClB,oBAAA,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE;AACvC,oBAAA,MAAM,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM;AAC3C,oBAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE;gBAChC;qBAAO;AACL,oBAAA,IAAI,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAuB;oBAC9E,OAAO,CAAC,WAAW,EAAE,YAAY,IAAI,WAAW,EAAE,QAAQ,CAAC,MAAM,EAAE;AACjE,wBAAA,WAAW,GAAG,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAuB;oBAC9D;oBACA,IAAI,WAAW,EAAE;AACf,wBAAA,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS;AACrC,wBAAA,MAAM,UAAU,GAAG,WAAW,CAAC,YAAY;AAC3C,wBAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE;oBAChC;AACA,oBAAA,OAAO,IAAI;gBACb;AACF,YAAA,CAAC;AACD,YAAA,MAAM,IAAI,GAAG,WAAW,EAAE;YAC1B,IAAI,CAAC,IAAI,EAAE;gBACT;YACF;AACA,YAAA,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI;YACpC,QAAQ,CAAC,QAAQ,CAAC;AAChB,gBAAA,GAAG,EAAE,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC;AAC5F,aAAA,CAAC;AACJ,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,KAAK,CAAC,IAAgC,EAAA;AAC9C,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE;QAC9B,IAAI,OAAO,EAAE;AACX,YAAA,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;QAC3B;QACA,OAAO,IAAI,CAAC,KAAK;IACnB;uGA1MW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAX,WAAW,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,8BAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,UAAA,EAAA,sBAAA,EAAA,qCAAA,EAAA,0BAAA,EAAA,qCAAA,EAAA,cAAA,EAAA,wCAAA,EAAA,iBAAA,EAAA,EAAA,EAAA,SAAA,EAXX,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,EAAA,eAAA,EAAA,IAAA,EAAA,cAAA,EAAA,CAAA,EAAA,SAAA,EAAA,EAAA,CAAA,eAAA,EAAA,MAAA,EAAA,CAAA,0BAAA,EAAA,iBAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC1BvC,8hBAiBA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDQY,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAYf,WAAW,EAAA,UAAA,EAAA,CAAA;kBAfvB,SAAS;+BACE,8BAA8B,EAAA,OAAA,EAE/B,CAAC,gBAAgB,CAAC,aAChB,CAAC,WAAW,CAAA,WAAA,CAAa,CAAC,EAAA,cAAA,EACrB;wBACd,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,2CAA2C,CAAC,EAAE;qBACtF,EAAA,IAAA,EACK;AACJ,wBAAA,YAAY,EAAE,sBAAsB;AACpC,wBAAA,uCAAuC,EAAE,wBAAwB;AACjE,wBAAA,uCAAuC,EAAE,cAAc;AACvD,wBAAA,0CAA0C,EAAE,iBAAiB;AAC9D,qBAAA,EAAA,QAAA,EAAA,8hBAAA,EAAA;;;MEpBU,eAAe,CAAA;AACT,IAAA,GAAG,GAAG,MAAM,EAAC,UAAuB,EAAC;AACtD;;;AAGG;IACa,eAAe,GAAG,KAAK,CAAC,QAAQ;wFAAU;IACzC,SAAS,GAAG,MAAM,CAA8B,IAAI;kFAAC;IAE/D,eAAe,GAAA;QACpB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,WAAW,EAAE;YACvC;QACF;AACA,QAAA,MAAM,cAAc,GAAG,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC;QACjF,IAAI,CAAC,cAAc,EAAE;AACnB,YAAA,MAAM,IAAI,QAAQ,CAChB,UAAU,EACV,8DAA8D,CAC/D;QACH;AACA,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC;IACpC;AAEA,IAAA,WAAA,GAAA;QACE,IAAI,aAAa,GAAG,EAAE;QACtB,IAAI,eAAe,GAAG,EAAE;QACxB,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,EAAoC;AACrE,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE;AACjC,YAAA,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE;gBACtB;YACF;AACA,YAAA,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,EAA8B;YACtE,MAAM,QAAQ,GAAG,CAAC,CAAC,WAAW,IAAI,WAAW,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,KAAK;YAEnF,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC;AAC1D,YAAA,IAAI,eAAe,IAAI,eAAe,KAAK,WAAW,EAAE;gBACtD,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,eAAe,EAAE,KAAK,CAAC;YAC7D;YACA,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,QAAQ,CAAC;YAC1D,eAAe,GAAG,WAAW;YAE7B,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;AACjD,YAAA,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,EAAE;gBAChD,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,aAAa,EAAE,KAAK,CAAC;YAC3D;YACA,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,SAAS,EAAE,IAAI,CAAC;YACpD,aAAa,GAAG,SAAS;AAC3B,QAAA,CAAC,CAAC;IACJ;uGAjDW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAf,eAAe,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,MAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAf,eAAe,EAAA,UAAA,EAAA,CAAA;kBAD3B,SAAS;mBAAC,EAAE,QAAQ,EAAE,mBAAmB,EAAE;;;ACd5C;;AAEG;;;;"}
@@ -12,7 +12,7 @@ import { selectButtonControlTemplate } from '@ngneers/controls-themes/templates/
12
12
  class NgnSelectButton extends ValueControlBase {
13
13
  theme = this.injectThemeTemplate(selectButtonControlTemplate, {
14
14
  root: true,
15
- invalid: () => this.invalid(),
15
+ invalid: () => this.invalidState(),
16
16
  });
17
17
  /**
18
18
  * Defines the orientation of the select buttons.
@@ -41,6 +41,7 @@ class NgnSelectButton extends ValueControlBase {
41
41
  return;
42
42
  }
43
43
  this.value.set(value);
44
+ this.markTouched();
44
45
  }
45
46
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSelectButton, deps: null, target: i0.ɵɵFactoryTarget.Component });
46
47
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnSelectButton, isStandalone: true, selector: "ngn-select-button", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, allowUnselect: { classPropertyName: "allowUnselect", publicName: "allowUnselect", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideSelf(NgnSelectButton)], usesInheritance: true, ngImport: i0, template: "<ngn-button-group\n [orientation]=\"orientation()\"\n [attr.aria-label]=\"label()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [ptInt]=\"this\"\n [ptDep]=\"'group'\"\n>\n @for (item of options(); track item.value) {\n <ngn-toggle-button\n [label]=\"maybeCallback(item.label)\"\n [icon]=\"item.icon\"\n [fixedWidth]=\"true\"\n (valueChange)=\"valueChange(item.value)\"\n [value]=\"value() === item.value\"\n [readonly]=\"readonly() || (value() === item.value && !allowUnselect())\"\n [attr.data-testid]=\"item.testId\"\n [disabled]=\"disabled()\"\n [ptInt]=\"this\"\n [ptDep]=\"'button'\"\n />\n }\n</ngn-button-group>\n", dependencies: [{ kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "component", type: NgnButtonGroup, selector: "ngn-button-group", inputs: ["orientation"] }, { kind: "component", type: NgnToggleButton, selector: "ngn-toggle-button", inputs: ["labelOn", "labelOff", "icon", "iconOn", "iconOff", "fixedWidth"] }] });