@ngneers/controls 0.0.1-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/fesm2022/ngneers-controls-accordion.mjs +234 -0
  2. package/fesm2022/ngneers-controls-accordion.mjs.map +1 -0
  3. package/fesm2022/ngneers-controls-api-ng.mjs +959 -0
  4. package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -0
  5. package/fesm2022/ngneers-controls-api-resize.mjs +712 -0
  6. package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -0
  7. package/fesm2022/ngneers-controls-api.mjs +347 -0
  8. package/fesm2022/ngneers-controls-api.mjs.map +1 -0
  9. package/fesm2022/ngneers-controls-avatar.mjs +77 -0
  10. package/fesm2022/ngneers-controls-avatar.mjs.map +1 -0
  11. package/fesm2022/ngneers-controls-base.mjs +620 -0
  12. package/fesm2022/ngneers-controls-base.mjs.map +1 -0
  13. package/fesm2022/ngneers-controls-breadcrumb.mjs +100 -0
  14. package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -0
  15. package/fesm2022/ngneers-controls-button-group.mjs +110 -0
  16. package/fesm2022/ngneers-controls-button-group.mjs.map +1 -0
  17. package/fesm2022/ngneers-controls-button.mjs +83 -0
  18. package/fesm2022/ngneers-controls-button.mjs.map +1 -0
  19. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs +74 -0
  20. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs.map +1 -0
  21. package/fesm2022/ngneers-controls-calendar.mjs +801 -0
  22. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -0
  23. package/fesm2022/ngneers-controls-checkbox.mjs +63 -0
  24. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -0
  25. package/fesm2022/ngneers-controls-chip.mjs +55 -0
  26. package/fesm2022/ngneers-controls-chip.mjs.map +1 -0
  27. package/fesm2022/ngneers-controls-default-icons.mjs +82 -0
  28. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
  29. package/fesm2022/ngneers-controls-defer.mjs +72 -0
  30. package/fesm2022/ngneers-controls-defer.mjs.map +1 -0
  31. package/fesm2022/ngneers-controls-dialog.mjs +407 -0
  32. package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
  33. package/fesm2022/ngneers-controls-directives.mjs +498 -0
  34. package/fesm2022/ngneers-controls-directives.mjs.map +1 -0
  35. package/fesm2022/ngneers-controls-drawer.mjs +271 -0
  36. package/fesm2022/ngneers-controls-drawer.mjs.map +1 -0
  37. package/fesm2022/ngneers-controls-edit-inplace.mjs +127 -0
  38. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -0
  39. package/fesm2022/ngneers-controls-errors.mjs +329 -0
  40. package/fesm2022/ngneers-controls-errors.mjs.map +1 -0
  41. package/fesm2022/ngneers-controls-filter.mjs +708 -0
  42. package/fesm2022/ngneers-controls-filter.mjs.map +1 -0
  43. package/fesm2022/ngneers-controls-hint.mjs +153 -0
  44. package/fesm2022/ngneers-controls-hint.mjs.map +1 -0
  45. package/fesm2022/ngneers-controls-i18n-translations-de.mjs +170 -0
  46. package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -0
  47. package/fesm2022/ngneers-controls-i18n-translations-en.mjs +170 -0
  48. package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -0
  49. package/fesm2022/ngneers-controls-i18n.mjs +57 -0
  50. package/fesm2022/ngneers-controls-i18n.mjs.map +1 -0
  51. package/fesm2022/ngneers-controls-icon.mjs +195 -0
  52. package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
  53. package/fesm2022/ngneers-controls-inplace.mjs +150 -0
  54. package/fesm2022/ngneers-controls-inplace.mjs.map +1 -0
  55. package/fesm2022/ngneers-controls-input-field.mjs +165 -0
  56. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -0
  57. package/fesm2022/ngneers-controls-input.mjs +72 -0
  58. package/fesm2022/ngneers-controls-input.mjs.map +1 -0
  59. package/fesm2022/ngneers-controls-item-view.mjs +473 -0
  60. package/fesm2022/ngneers-controls-item-view.mjs.map +1 -0
  61. package/fesm2022/ngneers-controls-list-box.mjs +281 -0
  62. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
  63. package/fesm2022/ngneers-controls-mask-input.mjs +888 -0
  64. package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -0
  65. package/fesm2022/ngneers-controls-menu.mjs +371 -0
  66. package/fesm2022/ngneers-controls-menu.mjs.map +1 -0
  67. package/fesm2022/ngneers-controls-message.mjs +30 -0
  68. package/fesm2022/ngneers-controls-message.mjs.map +1 -0
  69. package/fesm2022/ngneers-controls-number-input.mjs +366 -0
  70. package/fesm2022/ngneers-controls-number-input.mjs.map +1 -0
  71. package/fesm2022/ngneers-controls-paginator.mjs +129 -0
  72. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -0
  73. package/fesm2022/ngneers-controls-popover.mjs +215 -0
  74. package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
  75. package/fesm2022/ngneers-controls-progress.mjs +98 -0
  76. package/fesm2022/ngneers-controls-progress.mjs.map +1 -0
  77. package/fesm2022/ngneers-controls-radio.mjs +152 -0
  78. package/fesm2022/ngneers-controls-radio.mjs.map +1 -0
  79. package/fesm2022/ngneers-controls-roving-focus.mjs +323 -0
  80. package/fesm2022/ngneers-controls-roving-focus.mjs.map +1 -0
  81. package/fesm2022/ngneers-controls-scroll-shadow.mjs +65 -0
  82. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
  83. package/fesm2022/ngneers-controls-scroller.mjs +308 -0
  84. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
  85. package/fesm2022/ngneers-controls-select-button.mjs +58 -0
  86. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -0
  87. package/fesm2022/ngneers-controls-select.mjs +417 -0
  88. package/fesm2022/ngneers-controls-select.mjs.map +1 -0
  89. package/fesm2022/ngneers-controls-slider.mjs +167 -0
  90. package/fesm2022/ngneers-controls-slider.mjs.map +1 -0
  91. package/fesm2022/ngneers-controls-snackbar.mjs +414 -0
  92. package/fesm2022/ngneers-controls-snackbar.mjs.map +1 -0
  93. package/fesm2022/ngneers-controls-spin-buttons.mjs +126 -0
  94. package/fesm2022/ngneers-controls-spin-buttons.mjs.map +1 -0
  95. package/fesm2022/ngneers-controls-spinner.mjs +184 -0
  96. package/fesm2022/ngneers-controls-spinner.mjs.map +1 -0
  97. package/fesm2022/ngneers-controls-splitter.mjs +604 -0
  98. package/fesm2022/ngneers-controls-splitter.mjs.map +1 -0
  99. package/fesm2022/ngneers-controls-state.mjs +185 -0
  100. package/fesm2022/ngneers-controls-state.mjs.map +1 -0
  101. package/fesm2022/ngneers-controls-switch.mjs +39 -0
  102. package/fesm2022/ngneers-controls-switch.mjs.map +1 -0
  103. package/fesm2022/ngneers-controls-table.mjs +2634 -0
  104. package/fesm2022/ngneers-controls-table.mjs.map +1 -0
  105. package/fesm2022/ngneers-controls-tabs.mjs +336 -0
  106. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -0
  107. package/fesm2022/ngneers-controls-tag.mjs +30 -0
  108. package/fesm2022/ngneers-controls-tag.mjs.map +1 -0
  109. package/fesm2022/ngneers-controls-toast.mjs +350 -0
  110. package/fesm2022/ngneers-controls-toast.mjs.map +1 -0
  111. package/fesm2022/ngneers-controls-toggle-button.mjs +70 -0
  112. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -0
  113. package/fesm2022/ngneers-controls-tooltip.mjs +506 -0
  114. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -0
  115. package/fesm2022/ngneers-controls-tree.mjs +599 -0
  116. package/fesm2022/ngneers-controls-tree.mjs.map +1 -0
  117. package/fesm2022/ngneers-controls-upload.mjs +377 -0
  118. package/fesm2022/ngneers-controls-upload.mjs.map +1 -0
  119. package/fesm2022/ngneers-controls-utils-ng.mjs +411 -0
  120. package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -0
  121. package/fesm2022/ngneers-controls-utils.mjs +261 -0
  122. package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
  123. package/fesm2022/ngneers-controls.mjs +6 -0
  124. package/fesm2022/ngneers-controls.mjs.map +1 -0
  125. package/package.json +302 -0
  126. package/types/ngneers-controls-accordion.d.ts +139 -0
  127. package/types/ngneers-controls-api-ng.d.ts +364 -0
  128. package/types/ngneers-controls-api-resize.d.ts +318 -0
  129. package/types/ngneers-controls-api.d.ts +145 -0
  130. package/types/ngneers-controls-avatar.d.ts +54 -0
  131. package/types/ngneers-controls-base.d.ts +277 -0
  132. package/types/ngneers-controls-breadcrumb.d.ts +272 -0
  133. package/types/ngneers-controls-button-group.d.ts +46 -0
  134. package/types/ngneers-controls-button.d.ts +48 -0
  135. package/types/ngneers-controls-calendar.d.ts +466 -0
  136. package/types/ngneers-controls-checkbox.d.ts +40 -0
  137. package/types/ngneers-controls-chip.d.ts +202 -0
  138. package/types/ngneers-controls-default-icons.d.ts +5 -0
  139. package/types/ngneers-controls-defer.d.ts +48 -0
  140. package/types/ngneers-controls-dialog.d.ts +399 -0
  141. package/types/ngneers-controls-directives.d.ts +171 -0
  142. package/types/ngneers-controls-drawer.d.ts +308 -0
  143. package/types/ngneers-controls-edit-inplace.d.ts +263 -0
  144. package/types/ngneers-controls-errors.d.ts +155 -0
  145. package/types/ngneers-controls-filter.d.ts +609 -0
  146. package/types/ngneers-controls-hint.d.ts +88 -0
  147. package/types/ngneers-controls-i18n-translations-de.d.ts +164 -0
  148. package/types/ngneers-controls-i18n-translations-en.d.ts +164 -0
  149. package/types/ngneers-controls-i18n.d.ts +186 -0
  150. package/types/ngneers-controls-icon.d.ts +68 -0
  151. package/types/ngneers-controls-inplace.d.ts +98 -0
  152. package/types/ngneers-controls-input-field.d.ts +248 -0
  153. package/types/ngneers-controls-input.d.ts +30 -0
  154. package/types/ngneers-controls-item-view.d.ts +215 -0
  155. package/types/ngneers-controls-list-box.d.ts +334 -0
  156. package/types/ngneers-controls-mask-input.d.ts +434 -0
  157. package/types/ngneers-controls-menu.d.ts +192 -0
  158. package/types/ngneers-controls-message.d.ts +21 -0
  159. package/types/ngneers-controls-number-input.d.ts +146 -0
  160. package/types/ngneers-controls-paginator.d.ts +255 -0
  161. package/types/ngneers-controls-popover.d.ts +116 -0
  162. package/types/ngneers-controls-progress.d.ts +49 -0
  163. package/types/ngneers-controls-radio.d.ts +91 -0
  164. package/types/ngneers-controls-roving-focus.d.ts +107 -0
  165. package/types/ngneers-controls-scroll-shadow.d.ts +25 -0
  166. package/types/ngneers-controls-scroller.d.ts +151 -0
  167. package/types/ngneers-controls-select-button.d.ts +51 -0
  168. package/types/ngneers-controls-select.d.ts +409 -0
  169. package/types/ngneers-controls-slider.d.ts +57 -0
  170. package/types/ngneers-controls-snackbar.d.ts +330 -0
  171. package/types/ngneers-controls-spin-buttons.d.ts +69 -0
  172. package/types/ngneers-controls-spinner.d.ts +117 -0
  173. package/types/ngneers-controls-splitter.d.ts +465 -0
  174. package/types/ngneers-controls-state.d.ts +102 -0
  175. package/types/ngneers-controls-switch.d.ts +18 -0
  176. package/types/ngneers-controls-table.d.ts +3127 -0
  177. package/types/ngneers-controls-tabs.d.ts +118 -0
  178. package/types/ngneers-controls-tag.d.ts +21 -0
  179. package/types/ngneers-controls-toast.d.ts +298 -0
  180. package/types/ngneers-controls-toggle-button.d.ts +48 -0
  181. package/types/ngneers-controls-tooltip.d.ts +189 -0
  182. package/types/ngneers-controls-tree.d.ts +409 -0
  183. package/types/ngneers-controls-upload.d.ts +347 -0
  184. package/types/ngneers-controls-utils-ng.d.ts +129 -0
  185. package/types/ngneers-controls-utils.d.ts +130 -0
  186. package/types/ngneers-controls.d.ts +2 -0
@@ -0,0 +1,167 @@
1
+ import * as i0 from '@angular/core';
2
+ import { viewChild, input, computed, Component } from '@angular/core';
3
+ import { ValueControlBase, NgnPt, provideSelf } from '@ngneers/controls/base';
4
+ import { NgnDrag } from '@ngneers/controls/directives';
5
+ import { sliderControlTemplate } from '@ngneers/controls-themes/templates/slider';
6
+
7
+ /**
8
+ * @category control
9
+ */
10
+ class NgnSlider extends ValueControlBase {
11
+ theme = this.injectThemeTemplate(sliderControlTemplate, {
12
+ root: true,
13
+ invalid: () => this.invalid(),
14
+ horizontal: () => !this.vertical(),
15
+ vertical: () => this.vertical(),
16
+ });
17
+ _track = viewChild.required('track');
18
+ /**
19
+ * The minimum value of the slider.
20
+ * @default 0
21
+ */
22
+ min = input(0, /* @ts-ignore */
23
+ ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
24
+ /**
25
+ * Whether the slider is oriented vertically instead of horizontally.
26
+ * @default false
27
+ */
28
+ vertical = input(false, /* @ts-ignore */
29
+ ...(ngDevMode ? [{ debugName: "vertical" }] : /* istanbul ignore next */ []));
30
+ /**
31
+ * The maximum value of the slider.
32
+ * @default 100
33
+ */
34
+ max = input(100, /* @ts-ignore */
35
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
36
+ /**
37
+ * The increment between selectable values.
38
+ * @default 1
39
+ */
40
+ step = input(1, /* @ts-ignore */
41
+ ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
42
+ /**
43
+ * The value text representation for accessibility.
44
+ * If both {@link valueText} and {@link valueTextFn} are provided, {@link valueText} takes precedence.
45
+ */
46
+ valueText = input(/* @ts-ignore */
47
+ ...(ngDevMode ? [undefined, { debugName: "valueText" }] : /* istanbul ignore next */ []));
48
+ /**
49
+ * A function that generates the value text representation for accessibility.
50
+ * If both {@link valueText} and {@link valueTextFn} are provided, {@link valueText} takes precedence.
51
+ */
52
+ valueTextFn = input(/* @ts-ignore */
53
+ ...(ngDevMode ? [undefined, { debugName: "valueTextFn" }] : /* istanbul ignore next */ []));
54
+ left = computed(() => {
55
+ const percent = ((this.value() - this.min()) / (this.max() - this.min())) * 100;
56
+ return Math.min(100, Math.max(0, percent));
57
+ }, /* @ts-ignore */
58
+ ...(ngDevMode ? [{ debugName: "left" }] : /* istanbul ignore next */ []));
59
+ constructor() {
60
+ super();
61
+ }
62
+ valueTextValue = computed(() => {
63
+ const valueText = this.valueText();
64
+ if (valueText) {
65
+ return valueText;
66
+ }
67
+ const valueTextFn = this.valueTextFn();
68
+ if (valueTextFn) {
69
+ return valueTextFn(this.value());
70
+ }
71
+ return null;
72
+ }, /* @ts-ignore */
73
+ ...(ngDevMode ? [{ debugName: "valueTextValue" }] : /* istanbul ignore next */ []));
74
+ onDragged(delta) {
75
+ if (this.readonly() || this.disabled()) {
76
+ return;
77
+ }
78
+ const cursorPos = this.vertical() ? delta.absoluteY : delta.absoluteX;
79
+ this.dragToPosition(cursorPos);
80
+ }
81
+ dragToPosition(cursorPos) {
82
+ const trackEl = this._track().nativeElement;
83
+ const trackRect = trackEl.getBoundingClientRect();
84
+ const trackStart = this.vertical() ? trackRect.top : trackRect.left;
85
+ const trackLength = this.vertical() ? trackRect.height : trackRect.width;
86
+ const positionInTrack = cursorPos - trackStart;
87
+ const correctedPositionInTrack = this.vertical()
88
+ ? trackLength - positionInTrack
89
+ : positionInTrack;
90
+ const positionPercent = correctedPositionInTrack / trackLength;
91
+ const newValueUnstepped = this.min() + positionPercent * (this.max() - this.min());
92
+ const steppedValue = Math.round((newValueUnstepped - this.min()) / this.step()) * this.step() + this.min();
93
+ this.value.set(this.clampValue(steppedValue));
94
+ }
95
+ clampValue(value) {
96
+ return Math.min(this.max(), Math.max(this.min(), value));
97
+ }
98
+ onDragEnd() { }
99
+ onKeyDown(event) {
100
+ if (this.readonly() || this.disabled()) {
101
+ return;
102
+ }
103
+ switch (event.key) {
104
+ case 'ArrowLeft':
105
+ case 'ArrowDown':
106
+ this.value.update(v => this.clampValue(v - this.step()));
107
+ event.preventDefault();
108
+ break;
109
+ case 'ArrowRight':
110
+ case 'ArrowUp':
111
+ this.value.update(v => this.clampValue(v + this.step()));
112
+ event.preventDefault();
113
+ break;
114
+ case 'Home':
115
+ this.value.set(this.min());
116
+ this.onDragEnd();
117
+ event.preventDefault();
118
+ break;
119
+ case 'End':
120
+ this.value.set(this.max());
121
+ this.onDragEnd();
122
+ event.preventDefault();
123
+ break;
124
+ }
125
+ }
126
+ thumbClicked(event) {
127
+ event.stopPropagation();
128
+ }
129
+ trackClicked(event) {
130
+ if (this.readonly() || this.disabled()) {
131
+ return;
132
+ }
133
+ const trackEl = this._track().nativeElement;
134
+ const trackRect = trackEl.getBoundingClientRect();
135
+ const clickPosition = this.vertical()
136
+ ? event.clientY - trackRect.top
137
+ : event.clientX - trackRect.left;
138
+ this.dragToPosition(this.vertical() ? trackRect.top + clickPosition : trackRect.left + clickPosition);
139
+ }
140
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSlider, deps: [], target: i0.ɵɵFactoryTarget.Component });
141
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.5", type: NgnSlider, isStandalone: true, selector: "ngn-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, vertical: { classPropertyName: "vertical", publicName: "vertical", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", 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" }, listeners: { "keydown": "onKeyDown($event)", "blur": "touched.set(true)" }, properties: { "attr.aria-valuemin": "min()", "attr.aria-valuemax": "max()", "attr.aria-valuenow": "value()", "attr.aria-readonly": "readonly() ? \"true\" : null", "attr.disabled": "disabled() ? \"\" : null", "attr.aria-labelledby": "labelledBy()", "attr.aria-label": "label()", "attr.aria-orientation": "vertical() ? \"vertical\" : \"horizontal\"", "attr.aria-valuetext": "valueTextValue()", "tabindex": "disabled() ? -1 : 0" } }, providers: [provideSelf(NgnSlider)], viewQueries: [{ propertyName: "_track", first: true, predicate: ["track"], descendants: true, isSignal: true }], 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<div #track [ptInt]=\"this\" [ptClass]=\"'track'\" (click)=\"trackClicked($event)\">\n <div [ptInt]=\"this\" [ptClass]=\"'fill'\" [style.--valuePercent.%]=\"left()\"></div>\n <div\n #thumb\n [ptInt]=\"this\"\n [ptClass]=\"'thumb'\"\n ngnDrag\n (dragged)=\"onDragged($event)\"\n (dragEnd)=\"onDragEnd()\"\n [style.--valuePercent.%]=\"left()\"\n (click)=\"thumbClicked($event)\"\n ></div>\n</div>\n", dependencies: [{ kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "directive", type: NgnDrag, selector: "[ngnDrag]" }] });
142
+ }
143
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSlider, decorators: [{
144
+ type: Component,
145
+ args: [{ selector: 'ngn-slider', imports: [NgnPt, NgnDrag], providers: [provideSelf(NgnSlider)], host: {
146
+ role: 'slider',
147
+ '[attr.aria-valuemin]': 'min()',
148
+ '[attr.aria-valuemax]': 'max()',
149
+ '[attr.aria-valuenow]': 'value()',
150
+ '[attr.aria-readonly]': 'readonly() ? "true" : null',
151
+ '[attr.disabled]': 'disabled() ? "" : null',
152
+ '[attr.aria-labelledby]': 'labelledBy()',
153
+ '[attr.aria-label]': 'label()',
154
+ '[attr.aria-orientation]': 'vertical() ? "vertical" : "horizontal"',
155
+ '[attr.aria-valuetext]': 'valueTextValue()',
156
+ '[tabindex]': 'disabled() ? -1 : 0',
157
+ '(keydown)': 'onKeyDown($event)',
158
+ '(blur)': 'touched.set(true)',
159
+ }, template: "<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->\n<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->\n<div #track [ptInt]=\"this\" [ptClass]=\"'track'\" (click)=\"trackClicked($event)\">\n <div [ptInt]=\"this\" [ptClass]=\"'fill'\" [style.--valuePercent.%]=\"left()\"></div>\n <div\n #thumb\n [ptInt]=\"this\"\n [ptClass]=\"'thumb'\"\n ngnDrag\n (dragged)=\"onDragged($event)\"\n (dragEnd)=\"onDragEnd()\"\n [style.--valuePercent.%]=\"left()\"\n (click)=\"thumbClicked($event)\"\n ></div>\n</div>\n" }]
160
+ }], ctorParameters: () => [], propDecorators: { _track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], vertical: [{ type: i0.Input, args: [{ isSignal: true, alias: "vertical", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], valueText: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueText", required: false }] }], valueTextFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueTextFn", required: false }] }] } });
161
+
162
+ /**
163
+ * Generated bundle index. Do not edit.
164
+ */
165
+
166
+ export { NgnSlider };
167
+ //# sourceMappingURL=ngneers-controls-slider.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngneers-controls-slider.mjs","sources":["../../src/slider/slider.ts","../../src/slider/slider.html","../../src/slider/ngneers-controls-slider.ts"],"sourcesContent":["import { Component, input, viewChild, ElementRef, computed } from '@angular/core';\nimport { NgnPt, provideSelf, ValueControlBase } from '@ngneers/controls/base';\nimport { NgnDrag, type NgnDragInfo } from '@ngneers/controls/directives';\nimport { sliderControlTemplate } from '@ngneers/controls-themes/templates/slider';\n\n/**\n * @category control\n */\n@Component({\n selector: 'ngn-slider',\n templateUrl: './slider.html',\n imports: [NgnPt, NgnDrag],\n providers: [provideSelf(NgnSlider)],\n host: {\n role: 'slider',\n '[attr.aria-valuemin]': 'min()',\n '[attr.aria-valuemax]': 'max()',\n '[attr.aria-valuenow]': 'value()',\n '[attr.aria-readonly]': 'readonly() ? \"true\" : null',\n '[attr.disabled]': 'disabled() ? \"\" : null',\n '[attr.aria-labelledby]': 'labelledBy()',\n '[attr.aria-label]': 'label()',\n '[attr.aria-orientation]': 'vertical() ? \"vertical\" : \"horizontal\"',\n '[attr.aria-valuetext]': 'valueTextValue()',\n '[tabindex]': 'disabled() ? -1 : 0',\n '(keydown)': 'onKeyDown($event)',\n '(blur)': 'touched.set(true)',\n },\n})\nexport class NgnSlider extends ValueControlBase<'slider', number> {\n protected readonly theme = this.injectThemeTemplate(sliderControlTemplate, {\n root: true,\n invalid: () => this.invalid(),\n horizontal: () => !this.vertical(),\n vertical: () => this.vertical(),\n });\n\n private readonly _track = viewChild.required<ElementRef<HTMLDivElement>>('track');\n\n /**\n * The minimum value of the slider.\n * @default 0\n */\n public readonly min = input<number>(0);\n\n /**\n * Whether the slider is oriented vertically instead of horizontally.\n * @default false\n */\n public readonly vertical = input<boolean>(false);\n\n /**\n * The maximum value of the slider.\n * @default 100\n */\n public readonly max = input<number>(100);\n\n /**\n * The increment between selectable values.\n * @default 1\n */\n public readonly step = input<number>(1);\n\n /**\n * The value text representation for accessibility.\n * If both {@link valueText} and {@link valueTextFn} are provided, {@link valueText} takes precedence.\n */\n public readonly valueText = input<string>();\n\n /**\n * A function that generates the value text representation for accessibility.\n * If both {@link valueText} and {@link valueTextFn} are provided, {@link valueText} takes precedence.\n */\n public readonly valueTextFn = input<(value: number) => string>();\n\n protected readonly left = computed(() => {\n const percent = ((this.value() - this.min()) / (this.max() - this.min())) * 100;\n return Math.min(100, Math.max(0, percent));\n });\n\n constructor() {\n super();\n }\n\n protected readonly valueTextValue = computed(() => {\n const valueText = this.valueText();\n if (valueText) {\n return valueText;\n }\n const valueTextFn = this.valueTextFn();\n if (valueTextFn) {\n return valueTextFn(this.value());\n }\n return null;\n });\n\n protected onDragged(delta: NgnDragInfo) {\n if (this.readonly() || this.disabled()) {\n return;\n }\n const cursorPos = this.vertical() ? delta.absoluteY : delta.absoluteX;\n this.dragToPosition(cursorPos);\n }\n\n private dragToPosition(cursorPos: number) {\n const trackEl = this._track().nativeElement;\n const trackRect = trackEl.getBoundingClientRect();\n const trackStart = this.vertical() ? trackRect.top : trackRect.left;\n const trackLength = this.vertical() ? trackRect.height : trackRect.width;\n const positionInTrack = cursorPos - trackStart;\n const correctedPositionInTrack = this.vertical()\n ? trackLength - positionInTrack\n : positionInTrack;\n const positionPercent = correctedPositionInTrack / trackLength;\n const newValueUnstepped = this.min() + positionPercent * (this.max() - this.min());\n const steppedValue =\n Math.round((newValueUnstepped - this.min()) / this.step()) * this.step() + this.min();\n this.value.set(this.clampValue(steppedValue));\n }\n\n private clampValue(value: number): number {\n return Math.min(this.max(), Math.max(this.min(), value));\n }\n\n protected onDragEnd() {}\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 this.value.update(v => this.clampValue(v - this.step()));\n event.preventDefault();\n break;\n case 'ArrowRight':\n case 'ArrowUp':\n this.value.update(v => this.clampValue(v + this.step()));\n event.preventDefault();\n break;\n case 'Home':\n this.value.set(this.min());\n this.onDragEnd();\n event.preventDefault();\n break;\n case 'End':\n this.value.set(this.max());\n this.onDragEnd();\n event.preventDefault();\n break;\n }\n }\n\n protected thumbClicked(event: PointerEvent) {\n event.stopPropagation();\n }\n protected trackClicked(event: PointerEvent) {\n if (this.readonly() || this.disabled()) {\n return;\n }\n const trackEl = this._track().nativeElement;\n const trackRect = trackEl.getBoundingClientRect();\n const clickPosition = this.vertical()\n ? event.clientY - trackRect.top\n : event.clientX - trackRect.left;\n this.dragToPosition(\n this.vertical() ? trackRect.top + clickPosition : trackRect.left + clickPosition\n );\n }\n}\n","<!-- eslint-disable @angular-eslint/template/interactive-supports-focus -->\n<!-- eslint-disable @angular-eslint/template/click-events-have-key-events -->\n<div #track [ptInt]=\"this\" [ptClass]=\"'track'\" (click)=\"trackClicked($event)\">\n <div [ptInt]=\"this\" [ptClass]=\"'fill'\" [style.--valuePercent.%]=\"left()\"></div>\n <div\n #thumb\n [ptInt]=\"this\"\n [ptClass]=\"'thumb'\"\n ngnDrag\n (dragged)=\"onDragged($event)\"\n (dragEnd)=\"onDragEnd()\"\n [style.--valuePercent.%]=\"left()\"\n (click)=\"thumbClicked($event)\"\n ></div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,414 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, Directive, inject, ElementRef, output, booleanAttribute, computed, signal, Component, viewChildren, afterNextRender, ApplicationRef, createComponent, Injectable, InjectionToken, runInInjectionContext } from '@angular/core';
3
+ import { Platform } from '@ngneers/controls/api/ng';
4
+ import { NgnError } from '@ngneers/controls/utils';
5
+ import { NotificationRegionController, injectOrThrow } from '@ngneers/controls/utils-ng';
6
+ import { NgnBase, NgnPt, provideSelf } from '@ngneers/controls/base';
7
+ import { I18n } from '@ngneers/controls/i18n';
8
+ import { snackbarControlTemplate } from '@ngneers/controls-themes/templates/snackbar';
9
+ import { NgTemplateOutlet } from '@angular/common';
10
+ import { NgnButton, NgnActionButton } from '@ngneers/controls/button';
11
+ import { NgnIcon } from '@ngneers/controls/icon';
12
+
13
+ const DEFAULT_SNACKBAR_OPTIONS = {
14
+ closable: false,
15
+ autoHide: 5000,
16
+ showProgress: true,
17
+ pauseOnHover: true,
18
+ };
19
+
20
+ /**
21
+ * Template inputs for {@link NgnSnackbar}, extracted into a dedicated base class so the
22
+ * component itself stays focused on behavior.
23
+ */
24
+ class SnackbarTemplates extends NgnBase {
25
+ /** Custom template for the snackbar body, rendered in place of the plain `content` string. */
26
+ templateContent = input(/* @ts-ignore */
27
+ ...(ngDevMode ? [undefined, { debugName: "templateContent" }] : /* istanbul ignore next */ []));
28
+ /** Custom template for the snackbar header, rendered in place of the plain `header` string. */
29
+ templateHeader = input(/* @ts-ignore */
30
+ ...(ngDevMode ? [undefined, { debugName: "templateHeader" }] : /* istanbul ignore next */ []));
31
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SnackbarTemplates, deps: null, target: i0.ɵɵFactoryTarget.Directive });
32
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: SnackbarTemplates, isStandalone: true, inputs: { templateContent: { classPropertyName: "templateContent", publicName: "templateContent", isSignal: true, isRequired: false, transformFunction: null }, templateHeader: { classPropertyName: "templateHeader", publicName: "templateHeader", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0 });
33
+ }
34
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: SnackbarTemplates, decorators: [{
35
+ type: Directive
36
+ }], propDecorators: { templateContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateContent", required: false }] }], templateHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateHeader", required: false }] }] } });
37
+
38
+ /**
39
+ * @category control
40
+ */
41
+ class NgnSnackbar extends SnackbarTemplates {
42
+ theme = this.injectThemeTemplate(snackbarControlTemplate, 'root');
43
+ i18n = inject(I18n).translations;
44
+ _host = inject(ElementRef);
45
+ /** Heading text shown at the top of the snackbar. Overridden by {@link templateHeader}. */
46
+ header = input(/* @ts-ignore */
47
+ ...(ngDevMode ? [undefined, { debugName: "header" }] : /* istanbul ignore next */ []));
48
+ /** Body text of the snackbar. Overridden by {@link templateContent}. */
49
+ content = input(/* @ts-ignore */
50
+ ...(ngDevMode ? [undefined, { debugName: "content" }] : /* istanbul ignore next */ []));
51
+ /** Icon shown at the start of the snackbar. */
52
+ icon = input(/* @ts-ignore */
53
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
54
+ /** Icon used for the close button when {@link closable} is enabled. */
55
+ iconClose = input(/* @ts-ignore */
56
+ ...(ngDevMode ? [undefined, { debugName: "iconClose" }] : /* istanbul ignore next */ []));
57
+ /**
58
+ * Whether to render a close button that lets the user dismiss the snackbar.
59
+ * @default false
60
+ */
61
+ closable = input(DEFAULT_SNACKBAR_OPTIONS.closable, /* @ts-ignore */
62
+ ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
63
+ /**
64
+ * Time in ms before the snackbar auto-dismisses, or `false` to keep it open
65
+ * until dismissed manually.
66
+ * @default 5000
67
+ */
68
+ autoHide = input(DEFAULT_SNACKBAR_OPTIONS.autoHide, /* @ts-ignore */
69
+ ...(ngDevMode ? [{ debugName: "autoHide" }] : /* istanbul ignore next */ []));
70
+ /** Emitted when the snackbar is dismissed (by the timer, close button, or an action). */
71
+ closeSnackbar = output();
72
+ /**
73
+ * Action buttons rendered at the end of the snackbar. Clicking any action always
74
+ * dismisses the snackbar; its `action` callback fires first. Import the config
75
+ * type from `@ngneers/controls/api`.
76
+ */
77
+ actions = input(/* @ts-ignore */
78
+ ...(ngDevMode ? [undefined, { debugName: "actions" }] : /* istanbul ignore next */ []));
79
+ /**
80
+ * Shows a thin progress bar at the bottom of the snackbar that depletes over the
81
+ * {@link autoHide} duration.
82
+ * @default true
83
+ */
84
+ showProgress = input(DEFAULT_SNACKBAR_OPTIONS.showProgress ?? true, { ...(ngDevMode ? { debugName: "showProgress" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
85
+ /**
86
+ * Pauses the auto-hide timer and progress bar while the snackbar is hovered,
87
+ * resuming from the current position on mouse leave.
88
+ * @default true
89
+ */
90
+ pauseOnHover = input(DEFAULT_SNACKBAR_OPTIONS.pauseOnHover ?? true, { ...(ngDevMode ? { debugName: "pauseOnHover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
91
+ /**
92
+ * Politeness of the live region used to announce the snackbar to assistive tech.
93
+ * When omitted, it is derived from {@link color}: `error`/`warning` announce as
94
+ * `assertive` (mapped to `role="alert"`), everything else as `polite`
95
+ * (`role="status"`).
96
+ */
97
+ ariaLive = input(/* @ts-ignore */
98
+ ...(ngDevMode ? [undefined, { debugName: "ariaLive" }] : /* istanbul ignore next */ []));
99
+ /** Resolved live-region politeness — explicit input, else derived from color. */
100
+ liveMode = computed(() => {
101
+ const explicit = this.ariaLive();
102
+ if (explicit) {
103
+ return explicit;
104
+ }
105
+ const color = this.color();
106
+ return color === 'error' || color === 'warning' ? 'assertive' : 'polite';
107
+ }, /* @ts-ignore */
108
+ ...(ngDevMode ? [{ debugName: "liveMode" }] : /* istanbul ignore next */ []));
109
+ /** Landmark role paired with {@link liveMode} — `alert` when assertive, else `status`. */
110
+ role = computed(() => (this.liveMode() === 'assertive' ? 'alert' : 'status'), /* @ts-ignore */
111
+ ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
112
+ /**
113
+ * A visually-hidden severity prefix (e.g. "Error:") announced before the header
114
+ * so screen-reader users get the semantic that sighted users read from color.
115
+ * Only emitted for the known semantic colors.
116
+ */
117
+ severityLabel = computed(() => {
118
+ switch (this.color()) {
119
+ case 'error':
120
+ return this.i18n['snackbar_severity_error']();
121
+ case 'warning':
122
+ return this.i18n['snackbar_severity_warning']();
123
+ case 'success':
124
+ return this.i18n['snackbar_severity_success']();
125
+ case 'info':
126
+ return this.i18n['snackbar_severity_info']();
127
+ default:
128
+ return undefined;
129
+ }
130
+ }, /* @ts-ignore */
131
+ ...(ngDevMode ? [{ debugName: "severityLabel" }] : /* istanbul ignore next */ []));
132
+ paused = signal(false, /* @ts-ignore */
133
+ ...(ngDevMode ? [{ debugName: "paused" }] : /* istanbul ignore next */ []));
134
+ /** Auto-hide duration in ms, or 0 when auto-hide is disabled. Drives the progress bar. */
135
+ autoHideMs = computed(() => {
136
+ const autoHide = this.autoHide();
137
+ return typeof autoHide === 'number' && autoHide > 0 ? autoHide : 0;
138
+ }, /* @ts-ignore */
139
+ ...(ngDevMode ? [{ debugName: "autoHideMs" }] : /* istanbul ignore next */ []));
140
+ _closeTimeout;
141
+ _remaining = 0;
142
+ _startedAt = 0;
143
+ _hovered = false;
144
+ _focused = false;
145
+ _regionPaused = false;
146
+ /** Element that had focus before the user moved into the snackbar; focus returns here on close. */
147
+ _returnFocus = null;
148
+ ngOnInit() {
149
+ // autoHideMs() is the single source of truth for "is auto-hide enabled".
150
+ this._remaining = this.autoHideMs();
151
+ this.startHideTimer();
152
+ }
153
+ startHideTimer() {
154
+ // Idempotent: a timer already running must not be restarted (would reset _remaining tracking).
155
+ if (this._closeTimeout !== undefined) {
156
+ return;
157
+ }
158
+ if (this._remaining <= 0) {
159
+ // Distinguish "auto-hide disabled" (never started, _startedAt still 0) from
160
+ // "a pause consumed all the remaining time" (_startedAt > 0). In the latter
161
+ // case the snackbar is overdue, so close now rather than leave it stuck open
162
+ // with no active timer — otherwise a non-closable snackbar can never dismiss.
163
+ if (this._startedAt > 0) {
164
+ this.close();
165
+ }
166
+ return;
167
+ }
168
+ this._startedAt = performance.now();
169
+ this.paused.set(false);
170
+ this._closeTimeout = setTimeout(() => this.close(), this._remaining);
171
+ }
172
+ pauseHideTimer() {
173
+ if (this._closeTimeout === undefined) {
174
+ return;
175
+ }
176
+ clearTimeout(this._closeTimeout);
177
+ this._closeTimeout = undefined;
178
+ this._remaining -= performance.now() - this._startedAt;
179
+ this.paused.set(true);
180
+ }
181
+ /**
182
+ * Pause the auto-hide timer while the snackbar is hovered (opt-in via
183
+ * {@link pauseOnHover}), focused (always — a WCAG 2.2.1 requirement so keyboard
184
+ * and screen-reader users can read it), or while the whole region holds focus,
185
+ * resuming once none holds.
186
+ */
187
+ updatePause() {
188
+ const shouldPause = this._regionPaused || (this.pauseOnHover() && this._hovered) || this._focused;
189
+ if (shouldPause) {
190
+ this.pauseHideTimer();
191
+ }
192
+ else {
193
+ this.startHideTimer();
194
+ }
195
+ }
196
+ /** Move DOM focus to the snackbar root (used by the region's roving/hotkey navigation). */
197
+ focus() {
198
+ this._host.nativeElement.focus();
199
+ }
200
+ /** Freeze the timer because the notification region as a whole holds focus. */
201
+ regionPause() {
202
+ this._regionPaused = true;
203
+ this.updatePause();
204
+ }
205
+ /** Release the region-level freeze; resumes unless still hovered/focused. */
206
+ regionResume() {
207
+ this._regionPaused = false;
208
+ this.updatePause();
209
+ }
210
+ // Only ever called immediately before the snackbar is removed (by the timer,
211
+ // the close button, or an action), so it need not reset paused/_remaining —
212
+ // the component is torn down right after. Revisit if close() ever keeps the
213
+ // snackbar mounted.
214
+ close() {
215
+ this.restoreFocus();
216
+ this.closeSnackbar.emit();
217
+ if (this._closeTimeout !== undefined) {
218
+ clearTimeout(this._closeTimeout);
219
+ }
220
+ }
221
+ /** Return focus to the pre-snackbar element when it is dismissed while focused within. */
222
+ restoreFocus() {
223
+ const host = this._host.nativeElement;
224
+ if (host.contains(document.activeElement) && this._returnFocus?.isConnected) {
225
+ this._returnFocus.focus();
226
+ }
227
+ }
228
+ onEscape() {
229
+ if (this.closable()) {
230
+ this.close();
231
+ }
232
+ }
233
+ mouseEnter() {
234
+ this._hovered = true;
235
+ this.updatePause();
236
+ }
237
+ mouseLeave() {
238
+ this._hovered = false;
239
+ this.updatePause();
240
+ }
241
+ focusEnter(event) {
242
+ const from = event.relatedTarget;
243
+ if (from && !this._host.nativeElement.contains(from)) {
244
+ this._returnFocus = from;
245
+ }
246
+ this._focused = true;
247
+ this.updatePause();
248
+ }
249
+ focusLeave(event) {
250
+ // focusout bubbles; ignore focus moving between the snackbar's own children.
251
+ const next = event.relatedTarget;
252
+ if (next && this._host.nativeElement.contains(next)) {
253
+ return;
254
+ }
255
+ this._focused = false;
256
+ this.updatePause();
257
+ }
258
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSnackbar, deps: null, target: i0.ɵɵFactoryTarget.Component });
259
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnSnackbar, isStandalone: true, selector: "ngn-snackbar", inputs: { header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconClose: { classPropertyName: "iconClose", publicName: "iconClose", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, autoHide: { classPropertyName: "autoHide", publicName: "autoHide", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showProgress: { classPropertyName: "showProgress", publicName: "showProgress", isSignal: true, isRequired: false, transformFunction: null }, pauseOnHover: { classPropertyName: "pauseOnHover", publicName: "pauseOnHover", isSignal: true, isRequired: false, transformFunction: null }, ariaLive: { classPropertyName: "ariaLive", publicName: "ariaLive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeSnackbar: "closeSnackbar" }, host: { attributes: { "aria-atomic": "true" }, listeners: { "mouseenter": "mouseEnter()", "mouseleave": "mouseLeave()", "focusin": "focusEnter($event)", "focusout": "focusLeave($event)", "keydown.escape": "onEscape()" }, properties: { "animate.enter": "theme.class('anim-enter')", "animate.leave": "theme.class('anim-leave')", "attr.role": "role()", "attr.aria-live": "liveMode()" } }, providers: [provideSelf(NgnSnackbar)], usesInheritance: true, ngImport: i0, template: "<div [ptInt]=\"this\" [ptClass]=\"'body'\">\n @if (severityLabel(); as severityLabel) {\n <span [ptInt]=\"this\" [ptClass]=\"'sr-only'\">{{ severityLabel }}</span>\n }\n @if (templateHeader(); as headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"headerTemplate\"\n [ngTemplateOutletContext]=\"{\n header: header(),\n closable: !!closable(),\n closeSnackbar: close.bind(this),\n }\"\n ></ng-container>\n } @else {\n <div [ptInt]=\"this\" [ptClass]=\"'defaultHeader'\">\n <span [ptInt]=\"this\" [ptClass]=\"'defaultHeaderText'\">\n @if (icon(); as icon) {\n <ngn-icon [icon]=\"icon\" [attr.aria-hidden]=\"true\" />\n }\n {{ header() }}\n </span>\n </div>\n }\n @if (templateContent(); as contentTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"contentTemplate\"\n [ngTemplateOutletContext]=\"{\n content: content(),\n }\"\n ></ng-container>\n } @else {\n <div [ptInt]=\"this\" [ptClass]=\"'defaultContent'\">\n {{ content() }}\n </div>\n }\n</div>\n@if (actions()?.length || (!templateHeader() && closable())) {\n <div [ptInt]=\"this\" [ptClass]=\"'actions'\">\n @for (action of actions(); track $index) {\n <ngn-action-button [config]=\"action\" (clicked)=\"close()\" />\n }\n @if (!templateHeader() && closable()) {\n <button\n ngnButton\n kind=\"icon\"\n [ptInt]=\"this\"\n [ptClass]=\"'closeButton'\"\n [attr.aria-label]=\"i18n['snackbar_close']()\"\n (click)=\"close()\"\n >\n <ngn-icon [defaultIcon]=\"'snackbar-close'\" [icon]=\"iconClose()\" [attr.aria-hidden]=\"true\" />\n </button>\n }\n </div>\n}\n@if (showProgress() && autoHideMs() > 0) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'progressBar'\"\n aria-hidden=\"true\"\n [style.animationDuration.ms]=\"autoHideMs()\"\n [style.animationPlayState]=\"paused() ? 'paused' : 'running'\"\n ></div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }, { kind: "directive", type: NgnButton, selector: "button[ngnButton], a[ngnButton]", inputs: ["ngnButtonInline"], exportAs: ["ngnButton"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "component", type: NgnActionButton, selector: "ngn-action-button", inputs: ["config", "inline"], outputs: ["clicked"] }] });
260
+ }
261
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSnackbar, decorators: [{
262
+ type: Component,
263
+ args: [{ selector: 'ngn-snackbar', imports: [NgTemplateOutlet, NgnPt, NgnButton, NgnIcon, NgnActionButton], providers: [provideSelf(NgnSnackbar)], host: {
264
+ '(mouseenter)': 'mouseEnter()',
265
+ '(mouseleave)': 'mouseLeave()',
266
+ '(focusin)': 'focusEnter($event)',
267
+ '(focusout)': 'focusLeave($event)',
268
+ '(keydown.escape)': 'onEscape()',
269
+ '[animate.enter]': "theme.class('anim-enter')",
270
+ '[animate.leave]': "theme.class('anim-leave')",
271
+ '[attr.role]': 'role()',
272
+ '[attr.aria-live]': 'liveMode()',
273
+ 'aria-atomic': 'true',
274
+ }, template: "<div [ptInt]=\"this\" [ptClass]=\"'body'\">\n @if (severityLabel(); as severityLabel) {\n <span [ptInt]=\"this\" [ptClass]=\"'sr-only'\">{{ severityLabel }}</span>\n }\n @if (templateHeader(); as headerTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"headerTemplate\"\n [ngTemplateOutletContext]=\"{\n header: header(),\n closable: !!closable(),\n closeSnackbar: close.bind(this),\n }\"\n ></ng-container>\n } @else {\n <div [ptInt]=\"this\" [ptClass]=\"'defaultHeader'\">\n <span [ptInt]=\"this\" [ptClass]=\"'defaultHeaderText'\">\n @if (icon(); as icon) {\n <ngn-icon [icon]=\"icon\" [attr.aria-hidden]=\"true\" />\n }\n {{ header() }}\n </span>\n </div>\n }\n @if (templateContent(); as contentTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"contentTemplate\"\n [ngTemplateOutletContext]=\"{\n content: content(),\n }\"\n ></ng-container>\n } @else {\n <div [ptInt]=\"this\" [ptClass]=\"'defaultContent'\">\n {{ content() }}\n </div>\n }\n</div>\n@if (actions()?.length || (!templateHeader() && closable())) {\n <div [ptInt]=\"this\" [ptClass]=\"'actions'\">\n @for (action of actions(); track $index) {\n <ngn-action-button [config]=\"action\" (clicked)=\"close()\" />\n }\n @if (!templateHeader() && closable()) {\n <button\n ngnButton\n kind=\"icon\"\n [ptInt]=\"this\"\n [ptClass]=\"'closeButton'\"\n [attr.aria-label]=\"i18n['snackbar_close']()\"\n (click)=\"close()\"\n >\n <ngn-icon [defaultIcon]=\"'snackbar-close'\" [icon]=\"iconClose()\" [attr.aria-hidden]=\"true\" />\n </button>\n }\n </div>\n}\n@if (showProgress() && autoHideMs() > 0) {\n <div\n [ptInt]=\"this\"\n [ptClass]=\"'progressBar'\"\n aria-hidden=\"true\"\n [style.animationDuration.ms]=\"autoHideMs()\"\n [style.animationPlayState]=\"paused() ? 'paused' : 'running'\"\n ></div>\n}\n" }]
275
+ }], propDecorators: { header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconClose", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], autoHide: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoHide", required: false }] }], closeSnackbar: [{ type: i0.Output, args: ["closeSnackbar"] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], showProgress: [{ type: i0.Input, args: [{ isSignal: true, alias: "showProgress", required: false }] }], pauseOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseOnHover", required: false }] }], ariaLive: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLive", required: false }] }] } });
276
+
277
+ /**
278
+ * The host component that renders snackbars.
279
+ */
280
+ class NgnSnackbarHost extends NgnBase {
281
+ theme = this.injectThemeTemplate(snackbarControlTemplate, 'host');
282
+ i18n = inject(I18n).translations;
283
+ _snackbarManager = inject(NgnSnackbarManager);
284
+ _el = inject(ElementRef);
285
+ _items = viewChildren(NgnSnackbar, /* @ts-ignore */
286
+ ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
287
+ // The first snackbar is anchored at the bottom; each new one stacks above it (mirroring how a
288
+ // second toast appears below the first). Paired with the host's `column-reverse`, the first item
289
+ // in DOM order sits at the bottom anchor, so a leaving item's following siblings are exactly the
290
+ // ones that reflow toward the anchor — keeping the `& ~ *` leave-shift animation correct.
291
+ snackbars = this._snackbarManager.snackbars;
292
+ /**
293
+ * Drives keyboard access to the region: `F6` jumps focus to the newest snackbar,
294
+ * arrow keys rove between them, and focus anywhere inside pauses every timer.
295
+ */
296
+ region = new NotificationRegionController(this._el.nativeElement, () => this._items());
297
+ constructor() {
298
+ super();
299
+ afterNextRender(() => {
300
+ this._el.nativeElement.showPopover();
301
+ });
302
+ }
303
+ removeSnackbar(id) {
304
+ this._snackbarManager.removeSnackbar(id);
305
+ }
306
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSnackbarHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
307
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnSnackbarHost, isStandalone: true, selector: "ngn-snackbar-host", host: { attributes: { "role": "region" }, listeners: { "document:keydown": "region.handleGlobalKeydown($event)", "keydown": "region.handleKeydown($event)", "focusin": "region.handleFocusIn()", "focusout": "region.handleFocusOut($event)" }, properties: { "attr.popover": "\"manual\"", "attr.aria-label": "i18n['snackbar_region']()" } }, providers: [provideSelf(NgnSnackbarHost)], viewQueries: [{ propertyName: "_items", predicate: NgnSnackbar, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@for (snackbar of snackbars(); track snackbar.id; let i = $index) {\n <ngn-snackbar\n [attr.tabindex]=\"region.tabbableIndex() === i ? 0 : -1\"\n [header]=\"snackbar.header\"\n [content]=\"snackbar.content\"\n [color]=\"snackbar.color\"\n [icon]=\"snackbar.icon\"\n [autoHide]=\"snackbar.autoHide\"\n [actions]=\"snackbar.actions\"\n [showProgress]=\"!!snackbar.showProgress\"\n [pauseOnHover]=\"!!snackbar.pauseOnHover\"\n [ariaLive]=\"snackbar.ariaLive\"\n [templateContent]=\"snackbar.contentTemplate\"\n [templateHeader]=\"snackbar.headerTemplate\"\n [closable]=\"!!snackbar.closable\"\n (closeSnackbar)=\"removeSnackbar(snackbar.id)\"\n />\n}\n", dependencies: [{ kind: "component", type: NgnSnackbar, selector: "ngn-snackbar", inputs: ["header", "content", "icon", "iconClose", "closable", "autoHide", "actions", "showProgress", "pauseOnHover", "ariaLive"], outputs: ["closeSnackbar"] }] });
308
+ }
309
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSnackbarHost, decorators: [{
310
+ type: Component,
311
+ args: [{ selector: 'ngn-snackbar-host', imports: [NgnSnackbar], providers: [provideSelf(NgnSnackbarHost)], host: {
312
+ '[attr.popover]': '"manual"',
313
+ role: 'region',
314
+ '[attr.aria-label]': "i18n['snackbar_region']()",
315
+ '(document:keydown)': 'region.handleGlobalKeydown($event)',
316
+ '(keydown)': 'region.handleKeydown($event)',
317
+ '(focusin)': 'region.handleFocusIn()',
318
+ '(focusout)': 'region.handleFocusOut($event)',
319
+ }, template: "@for (snackbar of snackbars(); track snackbar.id; let i = $index) {\n <ngn-snackbar\n [attr.tabindex]=\"region.tabbableIndex() === i ? 0 : -1\"\n [header]=\"snackbar.header\"\n [content]=\"snackbar.content\"\n [color]=\"snackbar.color\"\n [icon]=\"snackbar.icon\"\n [autoHide]=\"snackbar.autoHide\"\n [actions]=\"snackbar.actions\"\n [showProgress]=\"!!snackbar.showProgress\"\n [pauseOnHover]=\"!!snackbar.pauseOnHover\"\n [ariaLive]=\"snackbar.ariaLive\"\n [templateContent]=\"snackbar.contentTemplate\"\n [templateHeader]=\"snackbar.headerTemplate\"\n [closable]=\"!!snackbar.closable\"\n (closeSnackbar)=\"removeSnackbar(snackbar.id)\"\n />\n}\n" }]
320
+ }], ctorParameters: () => [], propDecorators: { _items: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => NgnSnackbar), { isSignal: true }] }] } });
321
+
322
+ class NgnSnackbarManager {
323
+ _snackbars = signal([], /* @ts-ignore */
324
+ ...(ngDevMode ? [{ debugName: "_snackbars" }] : /* istanbul ignore next */ []));
325
+ _appRef = inject(ApplicationRef);
326
+ _userDefaults = injectOrThrow(NGN_SNACKBAR_USER_DEFAULTS, 'NgnSnackbarManager', 'Failed to inject NGN_SNACKBAR_USER_DEFAULTS, make sure to use withSnackbars() to provide ngn snackbars!');
327
+ _nextId = 0;
328
+ snackbars = this._snackbars.asReadonly();
329
+ _component;
330
+ constructor() {
331
+ if (!inject(Platform).isBrowser) {
332
+ return;
333
+ }
334
+ // Defer creation to avoid issues with Angular's injection tree during app initialization
335
+ queueMicrotask(() => {
336
+ // Create and attach the snackbar host component to the application's root
337
+ this._component = createComponent(NgnSnackbarHost, {
338
+ environmentInjector: this._appRef.injector,
339
+ });
340
+ this._appRef.attachView(this._component.hostView);
341
+ const hostEl = this._component.location.nativeElement;
342
+ const appRootEl = this._appRef.components[0]?.location.nativeElement;
343
+ if (!appRootEl) {
344
+ throw new NgnError('NgnSnackbarManager', 'Failed to find application root element to attach snackbar host!');
345
+ }
346
+ appRootEl.appendChild(hostEl);
347
+ });
348
+ }
349
+ ngOnDestroy() {
350
+ if (this._component) {
351
+ this._appRef.detachView(this._component.hostView);
352
+ this._component.destroy();
353
+ this._component = undefined;
354
+ }
355
+ }
356
+ removeSnackbar(id) {
357
+ this._snackbars.update(snackbars => snackbars.filter(t => t.id !== id));
358
+ }
359
+ addSnackbar(options) {
360
+ const id = this._nextId++;
361
+ const snackbar = {
362
+ ...DEFAULT_SNACKBAR_OPTIONS,
363
+ ...this._userDefaults,
364
+ ...options,
365
+ id,
366
+ };
367
+ this._snackbars.update(snackbars => [...snackbars, snackbar]);
368
+ return id;
369
+ }
370
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSnackbarManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
371
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSnackbarManager });
372
+ }
373
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnSnackbarManager, decorators: [{
374
+ type: Injectable
375
+ }], ctorParameters: () => [] });
376
+
377
+ const NGN_SNACKBAR_USER_DEFAULTS = new InjectionToken('NGN_SNACKBAR_USER_DEFAULTS');
378
+ function withSnackbars(defaultOptions) {
379
+ return {
380
+ providers: [
381
+ NgnSnackbarManager,
382
+ {
383
+ provide: NGN_SNACKBAR_USER_DEFAULTS,
384
+ useValue: defaultOptions || {},
385
+ },
386
+ ],
387
+ };
388
+ }
389
+
390
+ class SnackbarCreator {
391
+ _manager;
392
+ constructor() {
393
+ this._manager = injectOrThrow(NgnSnackbarManager, 'injectSnackbarCreator', 'NgnSnackbarManager not found. Make sure to use withSnackbars() to provide ngn snackbars!');
394
+ }
395
+ show(options) {
396
+ const id = this._manager.addSnackbar(options);
397
+ return {
398
+ hide: () => this._manager.removeSnackbar(id),
399
+ };
400
+ }
401
+ }
402
+ function injectSnackbarCreator(injector) {
403
+ if (injector) {
404
+ return runInInjectionContext(injector, () => injectSnackbarCreator());
405
+ }
406
+ return new SnackbarCreator();
407
+ }
408
+
409
+ /**
410
+ * Generated bundle index. Do not edit.
411
+ */
412
+
413
+ export { NGN_SNACKBAR_USER_DEFAULTS, NgnSnackbar, SnackbarCreator, injectSnackbarCreator, withSnackbars };
414
+ //# sourceMappingURL=ngneers-controls-snackbar.mjs.map