ng-primitives 0.90.0 → 0.91.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 (93) hide show
  1. package/a11y/index.d.ts +38 -46
  2. package/accordion/index.d.ts +252 -104
  3. package/ai/index.d.ts +1 -1
  4. package/autofill/index.d.ts +49 -9
  5. package/avatar/index.d.ts +96 -61
  6. package/breadcrumbs/index.d.ts +156 -16
  7. package/button/index.d.ts +23 -28
  8. package/checkbox/index.d.ts +93 -14
  9. package/combobox/index.d.ts +1 -1
  10. package/date-picker/index.d.ts +12 -11
  11. package/fesm2022/ng-primitives-a11y.mjs +36 -52
  12. package/fesm2022/ng-primitives-a11y.mjs.map +1 -1
  13. package/fesm2022/ng-primitives-accordion.mjs +210 -189
  14. package/fesm2022/ng-primitives-accordion.mjs.map +1 -1
  15. package/fesm2022/ng-primitives-ai.mjs +4 -4
  16. package/fesm2022/ng-primitives-ai.mjs.map +1 -1
  17. package/fesm2022/ng-primitives-autofill.mjs +53 -36
  18. package/fesm2022/ng-primitives-autofill.mjs.map +1 -1
  19. package/fesm2022/ng-primitives-avatar.mjs +97 -138
  20. package/fesm2022/ng-primitives-avatar.mjs.map +1 -1
  21. package/fesm2022/ng-primitives-breadcrumbs.mjs +92 -35
  22. package/fesm2022/ng-primitives-breadcrumbs.mjs.map +1 -1
  23. package/fesm2022/ng-primitives-button.mjs +14 -36
  24. package/fesm2022/ng-primitives-button.mjs.map +1 -1
  25. package/fesm2022/ng-primitives-checkbox.mjs +87 -65
  26. package/fesm2022/ng-primitives-checkbox.mjs.map +1 -1
  27. package/fesm2022/ng-primitives-combobox.mjs +9 -9
  28. package/fesm2022/ng-primitives-combobox.mjs.map +1 -1
  29. package/fesm2022/ng-primitives-date-picker.mjs +5 -4
  30. package/fesm2022/ng-primitives-date-picker.mjs.map +1 -1
  31. package/fesm2022/ng-primitives-form-field.mjs +48 -16
  32. package/fesm2022/ng-primitives-form-field.mjs.map +1 -1
  33. package/fesm2022/ng-primitives-input.mjs +32 -48
  34. package/fesm2022/ng-primitives-input.mjs.map +1 -1
  35. package/fesm2022/ng-primitives-interactions.mjs +4 -4
  36. package/fesm2022/ng-primitives-interactions.mjs.map +1 -1
  37. package/fesm2022/ng-primitives-listbox.mjs.map +1 -1
  38. package/fesm2022/ng-primitives-menu.mjs +13 -6
  39. package/fesm2022/ng-primitives-menu.mjs.map +1 -1
  40. package/fesm2022/ng-primitives-pagination.mjs +6 -6
  41. package/fesm2022/ng-primitives-pagination.mjs.map +1 -1
  42. package/fesm2022/ng-primitives-progress.mjs +2 -2
  43. package/fesm2022/ng-primitives-progress.mjs.map +1 -1
  44. package/fesm2022/ng-primitives-radio.mjs +3 -3
  45. package/fesm2022/ng-primitives-radio.mjs.map +1 -1
  46. package/fesm2022/ng-primitives-roving-focus.mjs +259 -236
  47. package/fesm2022/ng-primitives-roving-focus.mjs.map +1 -1
  48. package/fesm2022/ng-primitives-search.mjs.map +1 -1
  49. package/fesm2022/ng-primitives-select.mjs +8 -8
  50. package/fesm2022/ng-primitives-select.mjs.map +1 -1
  51. package/fesm2022/ng-primitives-slider.mjs +195 -172
  52. package/fesm2022/ng-primitives-slider.mjs.map +1 -1
  53. package/fesm2022/ng-primitives-state.mjs +172 -2
  54. package/fesm2022/ng-primitives-state.mjs.map +1 -1
  55. package/fesm2022/ng-primitives-switch.mjs +90 -88
  56. package/fesm2022/ng-primitives-switch.mjs.map +1 -1
  57. package/fesm2022/ng-primitives-tabs.mjs +4 -1
  58. package/fesm2022/ng-primitives-tabs.mjs.map +1 -1
  59. package/fesm2022/ng-primitives-textarea.mjs +27 -35
  60. package/fesm2022/ng-primitives-textarea.mjs.map +1 -1
  61. package/fesm2022/ng-primitives-toggle-group.mjs +134 -136
  62. package/fesm2022/ng-primitives-toggle-group.mjs.map +1 -1
  63. package/fesm2022/ng-primitives-toggle.mjs +69 -58
  64. package/fesm2022/ng-primitives-toggle.mjs.map +1 -1
  65. package/fesm2022/ng-primitives-toolbar.mjs +26 -35
  66. package/fesm2022/ng-primitives-toolbar.mjs.map +1 -1
  67. package/fesm2022/ng-primitives-utils.mjs +48 -35
  68. package/fesm2022/ng-primitives-utils.mjs.map +1 -1
  69. package/form-field/index.d.ts +7 -3
  70. package/input/index.d.ts +61 -24
  71. package/interactions/index.d.ts +5 -5
  72. package/listbox/index.d.ts +1 -1
  73. package/menu/index.d.ts +3 -2
  74. package/package.json +1 -1
  75. package/pagination/index.d.ts +7 -7
  76. package/roving-focus/index.d.ts +77 -101
  77. package/schematics/ng-add/schema.d.ts +0 -1
  78. package/schematics/ng-generate/templates/checkbox/checkbox.__fileSuffix@dasherize__.ts.template +2 -2
  79. package/schematics/ng-generate/templates/slider/slider.__fileSuffix@dasherize__.ts.template +6 -3
  80. package/schematics/ng-generate/templates/switch/switch.__fileSuffix@dasherize__.ts.template +2 -2
  81. package/schematics/ng-generate/templates/toggle/toggle.__fileSuffix@dasherize__.ts.template +2 -2
  82. package/schematics/ng-generate/templates/toggle-group/toggle-group.__fileSuffix@dasherize__.ts.template +2 -2
  83. package/schematics/ng-generate/templates/toolbar/toolbar.__fileSuffix@dasherize__.ts.template +1 -1
  84. package/search/index.d.ts +1 -1
  85. package/select/index.d.ts +2 -2
  86. package/slider/index.d.ts +195 -56
  87. package/state/index.d.ts +57 -3
  88. package/switch/index.d.ts +103 -28
  89. package/textarea/index.d.ts +63 -8
  90. package/toggle/index.d.ts +65 -24
  91. package/toggle-group/index.d.ts +79 -54
  92. package/toolbar/index.d.ts +27 -17
  93. package/utils/index.d.ts +1 -0
@@ -1,193 +1,92 @@
1
- import { Directionality } from '@angular/cdk/bidi';
2
1
  import * as i0 from '@angular/core';
3
- import { InjectionToken, inject, input, booleanAttribute, signal, Directive, ElementRef, computed, HostListener } from '@angular/core';
4
- import { createStateToken, createStateProvider, createStateInjector, createState } from 'ng-primitives/state';
2
+ import { inject, signal, InjectionToken, input, booleanAttribute, Directive, computed } from '@angular/core';
3
+ import { Directionality } from '@angular/cdk/bidi';
4
+ import { createPrimitive, injectInheritedState, controlled, attrBinding, listener, onDestroy } from 'ng-primitives/state';
5
5
  import { FocusMonitor } from '@angular/cdk/a11y';
6
+ import { injectElementRef } from 'ng-primitives/internal';
7
+ import { uniqueId } from 'ng-primitives/utils';
6
8
 
7
- /**
8
- * The state token for the RovingFocusGroup primitive.
9
- */
10
- const NgpRovingFocusGroupStateToken = createStateToken('RovingFocusGroup');
11
- /**
12
- * Provides the RovingFocusGroup state.
13
- */
14
- const provideRovingFocusGroupState = createStateProvider(NgpRovingFocusGroupStateToken);
15
- /**
16
- * Injects the RovingFocusGroup state.
17
- */
18
- const injectRovingFocusGroupState = createStateInjector(NgpRovingFocusGroupStateToken);
19
- /**
20
- * The RovingFocusGroup state registration function.
21
- */
22
- const rovingFocusGroupState = createState(NgpRovingFocusGroupStateToken);
23
-
24
- const NgpRovingFocusGroupToken = new InjectionToken('NgpRovingFocusGroupToken');
25
- /**
26
- * Inject the RovingFocusGroup directive instance
27
- * @returns The RovingFocusGroup directive instance
28
- */
29
- function injectRovingFocusGroup() {
30
- return inject(NgpRovingFocusGroupToken);
31
- }
32
- /**
33
- * Provide the RovingFocusGroup directive instance
34
- * @param type The RovingFocusGroup directive type
35
- * @returns The RovingFocusGroup token
36
- */
37
- function provideRovingFocusGroup(type, { inherit = true } = {}) {
38
- return {
39
- provide: NgpRovingFocusGroupToken,
40
- // Roving focus groups may be nested, in this case, the parent group should be used
41
- useFactory: () => {
42
- if (!inherit) {
43
- return inject(type, { self: true });
44
- }
45
- // If the parent group is not found, return the current group
46
- // This is useful for nested groups
47
- return (inject(NgpRovingFocusGroupToken, { skipSelf: true, optional: true }) ??
48
- inject(type, { self: true }));
49
- },
50
- };
51
- }
52
-
53
- /**
54
- * Apply the `ngpRovingFocusGroup` directive to an element to manage focus for a group of child elements.
55
- */
56
- class NgpRovingFocusGroup {
57
- constructor() {
58
- /**
59
- * Access the directionality service.
60
- */
61
- this.directionality = inject(Directionality);
62
- /**
63
- * Determine the orientation of the roving focus group.
64
- * @default 'vertical'
65
- */
66
- this.orientation = input('vertical', ...(ngDevMode ? [{ debugName: "orientation", alias: 'ngpRovingFocusGroupOrientation' }] : [{
67
- alias: 'ngpRovingFocusGroupOrientation',
68
- }]));
69
- /**
70
- * Determine if focus should wrap when the end or beginning is reached.
71
- */
72
- this.wrap = input(true, ...(ngDevMode ? [{ debugName: "wrap", alias: 'ngpRovingFocusGroupWrap',
73
- transform: booleanAttribute }] : [{
74
- alias: 'ngpRovingFocusGroupWrap',
75
- transform: booleanAttribute,
76
- }]));
77
- /**
78
- * Determine if the home and end keys should navigate to the first and last items.
79
- */
80
- this.homeEnd = input(true, ...(ngDevMode ? [{ debugName: "homeEnd", alias: 'ngpRovingFocusGroupHomeEnd',
81
- transform: booleanAttribute }] : [{
82
- alias: 'ngpRovingFocusGroupHomeEnd',
83
- transform: booleanAttribute,
84
- }]));
85
- /**
86
- * Determine if the roving focus group is disabled.
87
- */
88
- this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpRovingFocusGroupDisabled',
89
- transform: booleanAttribute }] : [{
90
- alias: 'ngpRovingFocusGroupDisabled',
91
- transform: booleanAttribute,
92
- }]));
93
- /**
94
- * Store the items in the roving focus group.
95
- */
96
- this.items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
97
- /**
98
- * Store the active item in the roving focus group.
99
- * @internal
100
- */
101
- this.activeItem = signal(null, ...(ngDevMode ? [{ debugName: "activeItem" }] : []));
102
- /**
103
- * The state of the roving focus group.
104
- */
105
- this.state = rovingFocusGroupState(this);
9
+ const [NgpRovingFocusGroupStateToken, ngpRovingFocusGroup, injectRovingFocusGroupState, provideRovingFocusGroupState,] = createPrimitive('NgpRovingFocusGroup', ({ orientation: _orientation = signal('vertical'), wrap = signal(false), homeEnd = signal(true), disabled = signal(false), inherit = true, }) => {
10
+ const parentGroup = inherit
11
+ ? injectInheritedState(() => NgpRovingFocusGroupStateToken)?.()
12
+ : null;
13
+ if (parentGroup) {
14
+ return parentGroup;
106
15
  }
16
+ const directionality = inject(Directionality);
17
+ const items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
18
+ const orientation = controlled(_orientation);
107
19
  /**
108
20
  * Get the items in the roving focus group sorted by order.
109
21
  */
110
- get sortedItems() {
111
- return this.items().sort((a, b) => {
22
+ function getSortedItems() {
23
+ return items().sort((a, b) => {
112
24
  // sort the items by their position in the document
113
- return a.elementRef.nativeElement.compareDocumentPosition(b.elementRef.nativeElement) &
25
+ return a.element.nativeElement.compareDocumentPosition(b.element.nativeElement) &
114
26
  Node.DOCUMENT_POSITION_FOLLOWING
115
27
  ? -1
116
28
  : 1;
117
29
  });
118
30
  }
119
31
  /**
120
- * Register an item with the roving focus group.
121
- * @param item The item to register
122
- * @internal
32
+ * Store the active item in the roving focus group.
123
33
  */
124
- register(item) {
125
- this.items.update(items => [...items, item]);
126
- // if there is no active item, make the first item the tabbable item
127
- if (!this.activeItem()) {
128
- this.activeItem.set(item);
129
- }
130
- }
131
- /**
132
- * Unregister an item with the roving focus group.
133
- * @param item The item to unregister
134
- * @internal
135
- */
136
- unregister(item) {
137
- this.items.update(items => items.filter(i => i !== item));
138
- // check if the unregistered item is the active item
139
- if (this.activeItem() === item) {
140
- // if the active item is unregistered, activate the first item
141
- this.activeItem.set(this.items()[0] ?? null);
142
- }
143
- }
34
+ const activeItem = signal(null, ...(ngDevMode ? [{ debugName: "activeItem" }] : []));
144
35
  /**
145
36
  * Activate an item in the roving focus group.
146
37
  * @param item The item to activate
147
38
  * @param origin The origin of the focus change
148
39
  */
149
- setActiveItem(item, origin = 'program') {
150
- this.activeItem.set(item);
151
- item?.focus(origin);
40
+ function setActiveItem(id, origin = 'program') {
41
+ activeItem.set(id);
42
+ const item = items().find(i => i.id() === id) ?? null;
43
+ if (item) {
44
+ item.focus(origin);
45
+ }
152
46
  }
153
47
  /**
154
48
  * Activate the first item in the roving focus group.
155
49
  * @param origin The origin of the focus change
156
50
  */
157
- activateFirstItem(origin) {
51
+ function activateFirstItem(origin) {
158
52
  // find the first item that is not disabled
159
- const item = this.sortedItems.find(i => !i.disabled()) ?? null;
53
+ const item = getSortedItems().find(i => !i.disabled()) ?? null;
160
54
  // set the first item as the active item
161
- this.setActiveItem(item, origin);
55
+ if (item) {
56
+ setActiveItem(item.id(), origin);
57
+ }
162
58
  }
163
59
  /**
164
60
  * Activate the last item in the roving focus group.
165
61
  * @param origin The origin of the focus change
166
62
  */
167
- activateLastItem(origin) {
63
+ function activateLastItem(origin) {
168
64
  // find the last item that is not disabled
169
- const item = [...this.sortedItems].reverse().find(i => !i.disabled()) ?? null;
65
+ const item = [...getSortedItems()].reverse().find(i => !i.disabled()) ?? null;
170
66
  // set the last item as the active item
171
- this.setActiveItem(item, origin);
67
+ if (item) {
68
+ setActiveItem(item.id(), origin);
69
+ }
172
70
  }
173
71
  /**
174
72
  * Activate the next item in the roving focus group.
175
73
  * @param origin The origin of the focus change
176
74
  */
177
- activateNextItem(origin) {
178
- const activeItem = this.activeItem();
75
+ function activateNextItem(origin) {
76
+ const currentActiveItem = activeItem();
179
77
  // if there is no active item, activate the first item
180
- if (!activeItem) {
181
- this.activateFirstItem(origin);
78
+ if (!currentActiveItem) {
79
+ activateFirstItem(origin);
182
80
  return;
183
81
  }
184
82
  // find the index of the active item
185
- const index = this.sortedItems.indexOf(activeItem);
83
+ const sortedItems = getSortedItems();
84
+ const index = sortedItems.findIndex(i => i.id() === currentActiveItem);
186
85
  // find the next item that is not disabled
187
- const item = this.sortedItems.slice(index + 1).find(i => !i.disabled()) ?? null;
86
+ const item = sortedItems.slice(index + 1).find(i => !i.disabled()) ?? null;
188
87
  // if we are at the end of the list, wrap to the beginning
189
- if (!item && this.state.wrap()) {
190
- this.activateFirstItem(origin);
88
+ if (!item && wrap()) {
89
+ activateFirstItem(origin);
191
90
  return;
192
91
  }
193
92
  // if there is no next item, do nothing
@@ -195,29 +94,30 @@ class NgpRovingFocusGroup {
195
94
  return;
196
95
  }
197
96
  // set the next item as the active item
198
- this.setActiveItem(item, origin);
97
+ setActiveItem(item.id(), origin);
199
98
  }
200
99
  /**
201
100
  * Activate the previous item in the roving focus group.
202
101
  * @param origin The origin of the focus change
203
102
  */
204
- activatePreviousItem(origin) {
205
- const activeItem = this.activeItem();
103
+ function activatePreviousItem(origin) {
104
+ const currentActiveItem = activeItem();
206
105
  // if there is no active item, activate the last item
207
- if (!activeItem) {
208
- this.activateLastItem(origin);
106
+ if (!currentActiveItem) {
107
+ activateLastItem(origin);
209
108
  return;
210
109
  }
211
110
  // find the index of the active item
212
- const index = this.sortedItems.indexOf(activeItem);
111
+ const sortedItems = getSortedItems();
112
+ const index = sortedItems.findIndex(i => i.id() === currentActiveItem);
213
113
  // find the previous item that is not disabled
214
- const item = this.sortedItems
114
+ const item = sortedItems
215
115
  .slice(0, index)
216
116
  .reverse()
217
117
  .find(i => !i.disabled()) ?? null;
218
118
  // if we are at the beginning of the list, wrap to the end
219
- if (!item && this.state.wrap()) {
220
- this.activateLastItem(origin);
119
+ if (!item && wrap()) {
120
+ activateLastItem(origin);
221
121
  return;
222
122
  }
223
123
  // if there is no previous item, do nothing
@@ -225,169 +125,292 @@ class NgpRovingFocusGroup {
225
125
  return;
226
126
  }
227
127
  // set the previous item as the active item
228
- this.setActiveItem(item, origin);
128
+ setActiveItem(item.id(), origin);
229
129
  }
230
130
  /**
231
131
  * Handle keyboard navigation for the roving focus group.
232
132
  * @param event The keyboard event
233
- * @internal
234
133
  */
235
- onKeydown(event) {
236
- if (this.state.disabled()) {
134
+ function onKeydown(event) {
135
+ if (disabled()) {
237
136
  return;
238
137
  }
239
138
  switch (event.key) {
240
139
  case 'ArrowUp':
241
- if (this.state.orientation() === 'vertical') {
140
+ if (orientation() === 'vertical') {
242
141
  event.preventDefault();
243
- this.activatePreviousItem('keyboard');
142
+ activatePreviousItem('keyboard');
244
143
  }
245
144
  break;
246
145
  case 'ArrowDown':
247
- if (this.state.orientation() === 'vertical') {
146
+ if (orientation() === 'vertical') {
248
147
  event.preventDefault();
249
- this.activateNextItem('keyboard');
148
+ activateNextItem('keyboard');
250
149
  }
251
150
  break;
252
151
  case 'ArrowLeft':
253
- if (this.state.orientation() === 'horizontal') {
152
+ if (orientation() === 'horizontal') {
254
153
  event.preventDefault();
255
- if (this.directionality.value === 'ltr') {
256
- this.activatePreviousItem('keyboard');
154
+ if (directionality.value === 'ltr') {
155
+ activatePreviousItem('keyboard');
257
156
  }
258
157
  else {
259
- this.activateNextItem('keyboard');
158
+ activateNextItem('keyboard');
260
159
  }
261
160
  }
262
161
  break;
263
162
  case 'ArrowRight':
264
- if (this.state.orientation() === 'horizontal') {
163
+ if (orientation() === 'horizontal') {
265
164
  event.preventDefault();
266
- if (this.directionality.value === 'ltr') {
267
- this.activateNextItem('keyboard');
165
+ if (directionality.value === 'ltr') {
166
+ activateNextItem('keyboard');
268
167
  }
269
168
  else {
270
- this.activatePreviousItem('keyboard');
169
+ activatePreviousItem('keyboard');
271
170
  }
272
171
  }
273
172
  break;
274
173
  case 'Home':
275
- if (this.state.homeEnd()) {
174
+ if (homeEnd()) {
276
175
  event.preventDefault();
277
- this.activateFirstItem('keyboard');
176
+ activateFirstItem('keyboard');
278
177
  }
279
178
  break;
280
179
  case 'End':
281
- if (this.state.homeEnd()) {
180
+ if (homeEnd()) {
282
181
  event.preventDefault();
283
- this.activateLastItem('keyboard');
182
+ activateLastItem('keyboard');
284
183
  }
285
184
  break;
286
185
  }
287
186
  }
288
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpRovingFocusGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
289
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpRovingFocusGroup, isStandalone: true, selector: "[ngpRovingFocusGroup]", inputs: { orientation: { classPropertyName: "orientation", publicName: "ngpRovingFocusGroupOrientation", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "ngpRovingFocusGroupWrap", isSignal: true, isRequired: false, transformFunction: null }, homeEnd: { classPropertyName: "homeEnd", publicName: "ngpRovingFocusGroupHomeEnd", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpRovingFocusGroupDisabled", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideRovingFocusGroup(NgpRovingFocusGroup), provideRovingFocusGroupState()], exportAs: ["ngpRovingFocusGroup"], ngImport: i0 }); }
187
+ function register(item) {
188
+ items.update(items => [...items, item]);
189
+ // if there is no active item, make the first item the tabbable item
190
+ if (!activeItem()) {
191
+ activeItem.set(item.id());
192
+ }
193
+ }
194
+ /**
195
+ * Unregister an item with the roving focus group.
196
+ * @param item The item to unregister
197
+ * @internal
198
+ */
199
+ function unregister(item) {
200
+ items.update(items => items.filter(i => i !== item));
201
+ // check if the unregistered item is the active item
202
+ if (activeItem() === item.id()) {
203
+ // if the active item is unregistered, activate the first item
204
+ activeItem.set(items()[0]?.id() ?? null);
205
+ }
206
+ }
207
+ /**
208
+ * Set the orientation of the roving focus group.
209
+ * @param value The orientation value
210
+ */
211
+ function setOrientation(value) {
212
+ orientation.set(value);
213
+ }
214
+ return {
215
+ orientation: orientation.asReadonly(),
216
+ setOrientation,
217
+ wrap,
218
+ homeEnd,
219
+ disabled,
220
+ activeItem,
221
+ setActiveItem,
222
+ onKeydown,
223
+ register,
224
+ unregister,
225
+ };
226
+ });
227
+
228
+ const NgpRovingFocusGroupToken = new InjectionToken('NgpRovingFocusGroupToken');
229
+ /**
230
+ * Inject the RovingFocusGroup directive instance
231
+ * @returns The RovingFocusGroup directive instance
232
+ */
233
+ function injectRovingFocusGroup() {
234
+ return inject(NgpRovingFocusGroupToken);
235
+ }
236
+ /**
237
+ * Provide the RovingFocusGroup directive instance
238
+ * @param type The RovingFocusGroup directive type
239
+ * @returns The RovingFocusGroup token
240
+ */
241
+ function provideRovingFocusGroup(type, { inherit = true } = {}) {
242
+ return {
243
+ provide: NgpRovingFocusGroupToken,
244
+ // Roving focus groups may be nested, in this case, the parent group should be used
245
+ useFactory: () => {
246
+ if (!inherit) {
247
+ return inject(type, { self: true });
248
+ }
249
+ // If the parent group is not found, return the current group
250
+ // This is useful for nested groups
251
+ return (inject(NgpRovingFocusGroupToken, { skipSelf: true, optional: true }) ??
252
+ inject(type, { self: true }));
253
+ },
254
+ };
290
255
  }
291
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpRovingFocusGroup, decorators: [{
292
- type: Directive,
293
- args: [{
294
- selector: '[ngpRovingFocusGroup]',
295
- exportAs: 'ngpRovingFocusGroup',
296
- providers: [provideRovingFocusGroup(NgpRovingFocusGroup), provideRovingFocusGroupState()],
297
- }]
298
- }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupOrientation", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupWrap", required: false }] }], homeEnd: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupHomeEnd", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupDisabled", required: false }] }] } });
299
256
 
300
257
  /**
301
- * Apply the `ngpRovingFocusItem` directive to an element within a roving focus group to automatically manage focus.
258
+ * Apply the `ngpRovingFocusGroup` directive to an element to manage focus for a group of child elements.
302
259
  */
303
- class NgpRovingFocusItem {
260
+ class NgpRovingFocusGroup {
304
261
  constructor() {
305
262
  /**
306
- * Access the group the roving focus item belongs to.
263
+ * Determine the orientation of the roving focus group.
264
+ * @default 'vertical'
307
265
  */
308
- this.group = injectRovingFocusGroup();
266
+ this.orientation = input('vertical', ...(ngDevMode ? [{ debugName: "orientation", alias: 'ngpRovingFocusGroupOrientation' }] : [{
267
+ alias: 'ngpRovingFocusGroupOrientation',
268
+ }]));
309
269
  /**
310
- * Access the focus monitor service.
270
+ * Determine if focus should wrap when the end or beginning is reached.
311
271
  */
312
- this.focusMonitor = inject(FocusMonitor);
272
+ this.wrap = input(true, ...(ngDevMode ? [{ debugName: "wrap", alias: 'ngpRovingFocusGroupWrap',
273
+ transform: booleanAttribute }] : [{
274
+ alias: 'ngpRovingFocusGroupWrap',
275
+ transform: booleanAttribute,
276
+ }]));
313
277
  /**
314
- * Access the element the roving focus item is attached to.
278
+ * Determine if the home and end keys should navigate to the first and last items.
315
279
  */
316
- this.elementRef = inject(ElementRef);
280
+ this.homeEnd = input(true, ...(ngDevMode ? [{ debugName: "homeEnd", alias: 'ngpRovingFocusGroupHomeEnd',
281
+ transform: booleanAttribute }] : [{
282
+ alias: 'ngpRovingFocusGroupHomeEnd',
283
+ transform: booleanAttribute,
284
+ }]));
317
285
  /**
318
- * Define if the item is disabled.
286
+ * Determine if the roving focus group is disabled.
319
287
  */
320
- this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpRovingFocusItemDisabled',
288
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpRovingFocusGroupDisabled',
321
289
  transform: booleanAttribute }] : [{
322
- alias: 'ngpRovingFocusItemDisabled',
290
+ alias: 'ngpRovingFocusGroupDisabled',
323
291
  transform: booleanAttribute,
324
292
  }]));
325
293
  /**
326
- * Derive the tabindex of the roving focus item.
294
+ * Store the active item in the roving focus group.
295
+ * @internal
327
296
  */
328
- this.tabindex = computed(() => !this.group.disabled() && this.group.activeItem() === this ? 0 : -1, ...(ngDevMode ? [{ debugName: "tabindex" }] : []));
329
- }
330
- /**
331
- * Initialize the roving focus item.
332
- */
333
- ngOnInit() {
334
- this.group.register(this);
297
+ this.activeItem = signal(null, ...(ngDevMode ? [{ debugName: "activeItem" }] : []));
298
+ /**
299
+ * The state of the roving focus group.
300
+ */
301
+ this.state = ngpRovingFocusGroup({
302
+ orientation: this.orientation,
303
+ wrap: this.wrap,
304
+ homeEnd: this.homeEnd,
305
+ disabled: this.disabled,
306
+ });
335
307
  }
336
308
  /**
337
- * Clean up the roving focus item.
309
+ * Activate an item in the roving focus group.
310
+ * @param item The item to activate
311
+ * @param origin The origin of the focus change
338
312
  */
339
- ngOnDestroy() {
340
- this.group.unregister(this);
313
+ setActiveItem(item, origin = 'program') {
314
+ if (item) {
315
+ this.state.setActiveItem(item.id(), origin);
316
+ }
341
317
  }
318
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpRovingFocusGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
319
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpRovingFocusGroup, isStandalone: true, selector: "[ngpRovingFocusGroup]", inputs: { orientation: { classPropertyName: "orientation", publicName: "ngpRovingFocusGroupOrientation", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "ngpRovingFocusGroupWrap", isSignal: true, isRequired: false, transformFunction: null }, homeEnd: { classPropertyName: "homeEnd", publicName: "ngpRovingFocusGroupHomeEnd", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpRovingFocusGroupDisabled", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
320
+ provideRovingFocusGroup(NgpRovingFocusGroup),
321
+ provideRovingFocusGroupState({ inherit: true }),
322
+ ], exportAs: ["ngpRovingFocusGroup"], ngImport: i0 }); }
323
+ }
324
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpRovingFocusGroup, decorators: [{
325
+ type: Directive,
326
+ args: [{
327
+ selector: '[ngpRovingFocusGroup]',
328
+ exportAs: 'ngpRovingFocusGroup',
329
+ providers: [
330
+ provideRovingFocusGroup(NgpRovingFocusGroup),
331
+ provideRovingFocusGroupState({ inherit: true }),
332
+ ],
333
+ }]
334
+ }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupOrientation", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupWrap", required: false }] }], homeEnd: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupHomeEnd", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusGroupDisabled", required: false }] }] } });
335
+
336
+ const [NgpRovingFocusItemStateToken, ngpRovingFocusItem, injectRovingFocusItemState, provideRovingFocusItemState,] = createPrimitive('NgpRovingFocusItem', ({ disabled = signal(false) }) => {
337
+ const element = injectElementRef();
338
+ const group = injectRovingFocusGroupState();
339
+ const focusMonitor = inject(FocusMonitor);
340
+ // genertate a unique id for the roving focus item - this is not a DOM id but an internal identifier
341
+ const id = uniqueId('ngp-roving-focus-item');
342
342
  /**
343
- * Forward the keydown event to the roving focus group.
344
- * @param event The keyboard event
343
+ * Derive the tabindex of the roving focus item.
345
344
  */
346
- onKeydown(event) {
347
- if (this.disabled()) {
345
+ const tabindex = computed(() => !group()?.disabled() && group()?.activeItem() === id ? 0 : -1, ...(ngDevMode ? [{ debugName: "tabindex" }] : []));
346
+ // Setup host attribute bindings
347
+ attrBinding(element, 'tabindex', () => tabindex().toString());
348
+ // Setup keyboard event listener
349
+ listener(element, 'keydown', (event) => {
350
+ if (disabled()) {
348
351
  return;
349
352
  }
350
- this.group.onKeydown(event);
351
- }
352
- /**
353
- * Activate the roving focus item on click.
354
- */
355
- activate() {
356
- if (this.disabled()) {
353
+ group()?.onKeydown(event);
354
+ });
355
+ // Setup click event listener
356
+ listener(element, 'click', () => {
357
+ if (disabled()) {
357
358
  return;
358
359
  }
359
- this.group.setActiveItem(this, 'mouse');
360
+ group()?.setActiveItem(id, 'mouse');
361
+ });
362
+ function focus(origin) {
363
+ focusMonitor.focusVia(element, origin);
360
364
  }
361
- /**
362
- * Focus the roving focus item.
363
- * @param origin The origin of the focus
364
- */
365
- focus(origin) {
366
- this.focusMonitor.focusVia(this.elementRef, origin);
365
+ const state = {
366
+ id: signal(id),
367
+ disabled,
368
+ tabindex,
369
+ focus,
370
+ element,
371
+ };
372
+ // Register the item when created
373
+ group()?.register(state);
374
+ // Unregister the item when destroyed
375
+ onDestroy(() => group()?.unregister(state));
376
+ return state;
377
+ });
378
+
379
+ /**
380
+ * Apply the `ngpRovingFocusItem` directive to an element within a roving focus group to automatically manage focus.
381
+ */
382
+ class NgpRovingFocusItem {
383
+ constructor() {
384
+ /**
385
+ * Define if the item is disabled.
386
+ */
387
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpRovingFocusItemDisabled',
388
+ transform: booleanAttribute }] : [{
389
+ alias: 'ngpRovingFocusItemDisabled',
390
+ transform: booleanAttribute,
391
+ }]));
392
+ this.state = ngpRovingFocusItem({ disabled: this.disabled });
393
+ /**
394
+ * Expose the internal id of the roving focus item.
395
+ * @internal
396
+ */
397
+ this.id = computed(() => this.state.id(), ...(ngDevMode ? [{ debugName: "id" }] : []));
367
398
  }
368
399
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpRovingFocusItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
369
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpRovingFocusItem, isStandalone: true, selector: "[ngpRovingFocusItem]", inputs: { disabled: { classPropertyName: "disabled", publicName: "ngpRovingFocusItemDisabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)", "click": "activate()" }, properties: { "attr.tabindex": "tabindex()" } }, exportAs: ["ngpRovingFocusItem"], ngImport: i0 }); }
400
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpRovingFocusItem, isStandalone: true, selector: "[ngpRovingFocusItem]", inputs: { disabled: { classPropertyName: "disabled", publicName: "ngpRovingFocusItemDisabled", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideRovingFocusItemState()], exportAs: ["ngpRovingFocusItem"], ngImport: i0 }); }
370
401
  }
371
402
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpRovingFocusItem, decorators: [{
372
403
  type: Directive,
373
404
  args: [{
374
405
  selector: '[ngpRovingFocusItem]',
375
406
  exportAs: 'ngpRovingFocusItem',
376
- host: {
377
- '[attr.tabindex]': 'tabindex()',
378
- },
407
+ providers: [provideRovingFocusItemState()],
379
408
  }]
380
- }], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusItemDisabled", required: false }] }], onKeydown: [{
381
- type: HostListener,
382
- args: ['keydown', ['$event']]
383
- }], activate: [{
384
- type: HostListener,
385
- args: ['click']
386
- }] } });
409
+ }], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpRovingFocusItemDisabled", required: false }] }] } });
387
410
 
388
411
  /**
389
412
  * Generated bundle index. Do not edit.
390
413
  */
391
414
 
392
- export { NgpRovingFocusGroup, NgpRovingFocusGroupToken, NgpRovingFocusItem, injectRovingFocusGroup, injectRovingFocusGroupState, provideRovingFocusGroup, provideRovingFocusGroupState };
415
+ export { NgpRovingFocusGroup, NgpRovingFocusGroupStateToken, NgpRovingFocusGroupToken, NgpRovingFocusItem, NgpRovingFocusItemStateToken, injectRovingFocusGroup, injectRovingFocusGroupState, injectRovingFocusItemState, ngpRovingFocusGroup, ngpRovingFocusItem, provideRovingFocusGroup, provideRovingFocusGroupState, provideRovingFocusItemState };
393
416
  //# sourceMappingURL=ng-primitives-roving-focus.mjs.map