ng-primitives 0.99.6 → 0.100.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.
package/a11y/index.d.ts CHANGED
@@ -1,50 +1,94 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { Signal, ElementRef } from '@angular/core';
2
+ import { Signal } from '@angular/core';
3
3
 
4
- interface ActiveDescendantManagerProps<T extends NgpActivatable> {
4
+ interface NgpActiveDescendantManagerProps {
5
5
  /**
6
6
  * The disabled state of the active descendant group.
7
7
  * @default false
8
8
  */
9
9
  disabled?: Signal<boolean>;
10
10
  /**
11
- * The items in the active descendant group.
11
+ * The number of items in the active descendant group.
12
12
  */
13
- items: Signal<T[]>;
13
+ count: Signal<number>;
14
+ /**
15
+ * Get the id for the item at a given index.
16
+ */
17
+ getItemId: (index: number) => string | undefined;
18
+ /**
19
+ * Whether the item at a given index is disabled.
20
+ */
21
+ isItemDisabled: (index: number) => boolean;
22
+ /**
23
+ * Scroll the active descendant item into view.
24
+ */
25
+ scrollIntoView: (index: number) => void;
14
26
  /**
15
27
  * Whether active descendant should wrap around.
16
28
  * @default false
17
29
  */
18
30
  wrap?: Signal<boolean>;
31
+ }
32
+ interface NgpActiveDescendantManagerState {
19
33
  /**
20
- * A callback invoked when the active descendant changes.
34
+ * The index of the active descendant item.
21
35
  */
22
- onActiveDescendantChange?: (activeDescendant: T | undefined) => void;
23
- }
24
- interface NgpActivatable {
36
+ index: Signal<number>;
25
37
  /**
26
- * The id of the item.
38
+ * The id of the active descendant item.
27
39
  */
28
- id: Signal<string>;
40
+ id: Signal<string | undefined>;
29
41
  /**
30
- * Whether the item is disabled.
42
+ * Activate an item in the active descendant group.
31
43
  */
32
- disabled?: Signal<boolean>;
44
+ activateByIndex: (index: number, options?: ActivationOptions) => void;
45
+ /**
46
+ * Activate an item in the active descendant group by id.
47
+ */
48
+ activateById: (id: string, options?: ActivationOptions) => void;
33
49
  /**
34
- * The element that represents the item.
50
+ * Activate the first enabled item in the active descendant group.
35
51
  */
36
- elementRef: ElementRef<HTMLElement>;
52
+ first: (options?: ActivationOptions) => void;
53
+ /**
54
+ * Activate the last enabled item in the active descendant group.
55
+ */
56
+ last: (options?: ActivationOptions) => void;
57
+ /**
58
+ * Activate the next enabled item in the active descendant group.
59
+ */
60
+ next: (options?: ActivationOptions) => void;
61
+ /**
62
+ * Activate the previous enabled item in the active descendant group.
63
+ */
64
+ previous: (options?: ActivationOptions) => void;
65
+ /**
66
+ * Ensure there is an active descendant, this is useful when the items in the group change.
67
+ */
68
+ validate: () => void;
69
+ /**
70
+ * Reset the active descendant group, clearing the active index.
71
+ */
72
+ reset: (options?: ActivationOptions) => void;
37
73
  }
38
- declare function activeDescendantManager<T extends NgpActivatable>({ items, disabled: _disabled, wrap, onActiveDescendantChange, }: ActiveDescendantManagerProps<T>): {
39
- activeDescendant: Signal<string | undefined>;
40
- activeItem: Signal<T | undefined>;
41
- activate: (item: T | undefined) => void;
42
- first: () => void;
43
- last: () => void;
44
- next: () => void;
45
- previous: () => void;
46
- reset: () => void;
74
+ declare function activeDescendantManager({ disabled: _disabled, wrap, count, getItemId, isItemDisabled, scrollIntoView, }: NgpActiveDescendantManagerProps): {
75
+ id: Signal<string | undefined>;
76
+ index: _angular_core.WritableSignal<number>;
77
+ activateByIndex: (index: number, { scroll, origin }?: ActivationOptions) => void;
78
+ activateById: (id: string, options?: ActivationOptions) => void;
79
+ first: (options?: ActivationOptions) => void;
80
+ last: (options?: ActivationOptions) => void;
81
+ next: (options?: ActivationOptions) => void;
82
+ previous: (options?: ActivationOptions) => void;
83
+ reset: ({ scroll, origin }?: ActivationOptions) => void;
84
+ validate: () => void;
47
85
  };
86
+ interface ActivationOptions {
87
+ /** Whether to scroll the activated item into view. */
88
+ scroll?: boolean;
89
+ /** Define the source of activation. */
90
+ origin?: 'keyboard' | 'pointer';
91
+ }
48
92
 
49
93
  /**
50
94
  * The state interface for the VisuallyHidden pattern.
@@ -103,4 +147,4 @@ declare class NgpVisuallyHidden {
103
147
  }
104
148
 
105
149
  export { NgpVisuallyHidden, NgpVisuallyHiddenStateToken, activeDescendantManager, injectVisuallyHiddenState, ngpVisuallyHidden, provideVisuallyHiddenState };
106
- export type { NgpActivatable, NgpVisuallyHiddenProps, NgpVisuallyHiddenState };
150
+ export type { ActivationOptions, NgpActiveDescendantManagerProps, NgpActiveDescendantManagerState, NgpVisuallyHiddenProps, NgpVisuallyHiddenState };
@@ -4,10 +4,10 @@ import * as ng_primitives_state from 'ng-primitives/state';
4
4
  import * as ng_primitives_combobox from 'ng-primitives/combobox';
5
5
  import * as ng_primitives_internal from 'ng-primitives/internal';
6
6
  import * as ng_primitives_utils from 'ng-primitives/utils';
7
- import { BooleanInput } from '@angular/cdk/coercion';
8
- import { NgpActivatable } from 'ng-primitives/a11y';
7
+ import { BooleanInput, NumberInput } from '@angular/cdk/coercion';
9
8
  import * as ng_primitives_portal from 'ng-primitives/portal';
10
9
  import { NgpOverlay } from 'ng-primitives/portal';
10
+ import * as ng_primitives_a11y from 'ng-primitives/a11y';
11
11
 
12
12
  declare class NgpComboboxButton {
13
13
  /** Access the combobox state. */
@@ -84,7 +84,7 @@ declare class NgpComboboxInput {
84
84
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgpComboboxInput, "input[ngpComboboxInput]", ["ngpComboboxInput"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
85
85
  }
86
86
 
87
- declare class NgpComboboxOption implements OnInit, OnDestroy, NgpActivatable {
87
+ declare class NgpComboboxOption implements OnInit, OnDestroy {
88
88
  /** Access the combobox state. */
89
89
  protected readonly state: _angular_core.Signal<ng_primitives_state.State<ng_primitives_combobox.NgpCombobox>>;
90
90
  /**
@@ -98,6 +98,11 @@ declare class NgpComboboxOption implements OnInit, OnDestroy, NgpActivatable {
98
98
  readonly value: _angular_core.InputSignal<any>;
99
99
  /** The disabled state of the option. */
100
100
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
101
+ /**
102
+ * The index of the option in the combobox. This can be used to define the order of options
103
+ * when virtual scrolling is used or when the order is not determined by DOM order.
104
+ */
105
+ readonly index: _angular_core.InputSignalWithTransform<number | undefined, NumberInput>;
101
106
  /**
102
107
  * Whether this option is the active descendant.
103
108
  * @internal
@@ -129,7 +134,7 @@ declare class NgpComboboxOption implements OnInit, OnDestroy, NgpActivatable {
129
134
  */
130
135
  protected onPointerLeave(): void;
131
136
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgpComboboxOption, never>;
132
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgpComboboxOption, "[ngpComboboxOption]", ["ngpComboboxOption"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "ngpComboboxOptionValue"; "required": false; "isSignal": true; }; "disabled": { "alias": "ngpComboboxOptionDisabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
137
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgpComboboxOption, "[ngpComboboxOption]", ["ngpComboboxOption"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "ngpComboboxOptionValue"; "required": false; "isSignal": true; }; "disabled": { "alias": "ngpComboboxOptionDisabled"; "required": false; "isSignal": true; }; "index": { "alias": "ngpComboboxOptionIndex"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
133
138
  }
134
139
 
135
140
  declare class NgpComboboxPortal implements OnDestroy {
@@ -224,6 +229,25 @@ declare class NgpCombobox {
224
229
  readonly placement: _angular_core.InputSignal<NgpComboboxPlacement>;
225
230
  /** The container for the dropdown. */
226
231
  readonly container: _angular_core.InputSignal<string | HTMLElement | null>;
232
+ /**
233
+ * A function that will scroll the active option into view. This can be overridden
234
+ * for cases such as virtual scrolling where we cannot scroll the option directly because
235
+ * it may not be rendered.
236
+ */
237
+ readonly scrollToOption: _angular_core.InputSignal<((index: number) => void) | undefined>;
238
+ /**
239
+ * The number of options within the combobox. By default this is calculated based on the
240
+ * options added to the combobox, but in virtual scrolling scenarios the total number of options
241
+ * may be different from the number of rendered options.
242
+ */
243
+ readonly optionCount: _angular_core.InputSignalWithTransform<number | undefined, NumberInput>;
244
+ /**
245
+ * Provide all the option values to the combobox. This is useful for virtual scrolling scenarios
246
+ * where not all options are rendered in the DOM. This is not an alternative to adding the options
247
+ * in the DOM, it is only to provide the combobox with the full list of options. This list should match
248
+ * the order of the options as they would appear in the DOM.
249
+ */
250
+ readonly allOptions: _angular_core.InputSignal<any[] | undefined>;
227
251
  /**
228
252
  * Store the combobox input
229
253
  * @internal
@@ -259,19 +283,26 @@ declare class NgpCombobox {
259
283
  * @internal
260
284
  */
261
285
  readonly open: _angular_core.Signal<boolean>;
286
+ /**
287
+ * The options sorted by their index or DOM position.
288
+ * @internal
289
+ */
290
+ readonly sortedOptions: _angular_core.Signal<NgpComboboxOption[]>;
262
291
  /**
263
292
  * The active key descendant manager.
264
293
  * @internal
265
294
  */
266
295
  readonly activeDescendantManager: {
267
- activeDescendant: _angular_core.Signal<string | undefined>;
268
- activeItem: _angular_core.Signal<NgpComboboxOption | undefined>;
269
- activate: (item: NgpComboboxOption | undefined) => void;
270
- first: () => void;
271
- last: () => void;
272
- next: () => void;
273
- previous: () => void;
274
- reset: () => void;
296
+ id: _angular_core.Signal<string | undefined>;
297
+ index: _angular_core.WritableSignal<number>;
298
+ activateByIndex: (index: number, { scroll, origin }?: ng_primitives_a11y.ActivationOptions) => void;
299
+ activateById: (id: string, options?: ng_primitives_a11y.ActivationOptions) => void;
300
+ first: (options?: ng_primitives_a11y.ActivationOptions) => void;
301
+ last: (options?: ng_primitives_a11y.ActivationOptions) => void;
302
+ next: (options?: ng_primitives_a11y.ActivationOptions) => void;
303
+ previous: (options?: ng_primitives_a11y.ActivationOptions) => void;
304
+ reset: ({ scroll, origin }?: ng_primitives_a11y.ActivationOptions) => void;
305
+ validate: () => void;
275
306
  };
276
307
  /** The control status */
277
308
  protected readonly controlStatus: _angular_core.Signal<ng_primitives_utils.NgpControlStatus | undefined>;
@@ -307,16 +338,16 @@ declare class NgpCombobox {
307
338
  deselectOption(option: NgpComboboxOption): void;
308
339
  /**
309
340
  * Toggle the selection of an option.
310
- * @param option The option to toggle.
341
+ * @param id The id of the option to toggle.
311
342
  * @internal
312
343
  */
313
- toggleOption(option: NgpComboboxOption): void;
344
+ toggleOption(id: string): void;
314
345
  /**
315
346
  * Determine if an option is selected.
316
347
  * @param option The option to check.
317
348
  * @internal
318
349
  */
319
- isOptionSelected(option: NgpComboboxOption): boolean;
350
+ isOptionSelected(option: T): boolean;
320
351
  /**
321
352
  * Activate the next option in the list if there is one.
322
353
  * If there is no option currently active, activate the selected option or the first option.
@@ -396,8 +427,10 @@ declare class NgpCombobox {
396
427
  * @internal
397
428
  */
398
429
  protected onBlur(event: FocusEvent): void;
430
+ private scrollTo;
431
+ private getOptionAtIndex;
399
432
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgpCombobox, never>;
400
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgpCombobox, "[ngpCombobox]", ["ngpCombobox"], { "value": { "alias": "ngpComboboxValue"; "required": false; "isSignal": true; }; "multiple": { "alias": "ngpComboboxMultiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "ngpComboboxDisabled"; "required": false; "isSignal": true; }; "allowDeselect": { "alias": "ngpComboboxAllowDeselect"; "required": false; "isSignal": true; }; "compareWith": { "alias": "ngpComboboxCompareWith"; "required": false; "isSignal": true; }; "placement": { "alias": "ngpComboboxDropdownPlacement"; "required": false; "isSignal": true; }; "container": { "alias": "ngpComboboxDropdownContainer"; "required": false; "isSignal": true; }; }, { "valueChange": "ngpComboboxValueChange"; "openChange": "ngpComboboxOpenChange"; }, never, never, true, never>;
433
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NgpCombobox, "[ngpCombobox]", ["ngpCombobox"], { "value": { "alias": "ngpComboboxValue"; "required": false; "isSignal": true; }; "multiple": { "alias": "ngpComboboxMultiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "ngpComboboxDisabled"; "required": false; "isSignal": true; }; "allowDeselect": { "alias": "ngpComboboxAllowDeselect"; "required": false; "isSignal": true; }; "compareWith": { "alias": "ngpComboboxCompareWith"; "required": false; "isSignal": true; }; "placement": { "alias": "ngpComboboxDropdownPlacement"; "required": false; "isSignal": true; }; "container": { "alias": "ngpComboboxDropdownContainer"; "required": false; "isSignal": true; }; "scrollToOption": { "alias": "ngpComboboxScrollToOption"; "required": false; "isSignal": true; }; "optionCount": { "alias": "ngpComboboxOptionCount"; "required": false; "isSignal": true; }; "allOptions": { "alias": "ngpComboboxOptions"; "required": false; "isSignal": true; }; }, { "valueChange": "ngpComboboxValueChange"; "openChange": "ngpComboboxOpenChange"; }, never, never, true, never>;
401
434
  }
402
435
  type NgpComboboxPlacement = 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
403
436
 
@@ -1,126 +1,146 @@
1
1
  import * as i0 from '@angular/core';
2
- import { computed, signal, inject, ChangeDetectorRef, Directive } from '@angular/core';
3
- import { explicitEffect, injectElementRef } from 'ng-primitives/internal';
4
- import { createPrimitive, controlled, styleBinding } from 'ng-primitives/state';
2
+ import { signal, computed, inject, ChangeDetectorRef, Directive } from '@angular/core';
3
+ import { controlled, createPrimitive, styleBinding } from 'ng-primitives/state';
4
+ import { injectDisposables } from 'ng-primitives/utils';
5
+ import { injectElementRef } from 'ng-primitives/internal';
5
6
 
6
- function activeDescendantManager({ items, disabled: _disabled, wrap, onActiveDescendantChange, }) {
7
- const sortedOptions = computed(() => items()
8
- .slice()
9
- .sort((a, b) => {
10
- const aElement = a.elementRef.nativeElement;
11
- const bElement = b.elementRef.nativeElement;
12
- return aElement.compareDocumentPosition(bElement) & Node.DOCUMENT_POSITION_FOLLOWING
13
- ? -1
14
- : 1;
15
- }), ...(ngDevMode ? [{ debugName: "sortedOptions" }] : []));
7
+ function activeDescendantManager({ disabled: _disabled = signal(false), wrap, count, getItemId, isItemDisabled, scrollIntoView, }) {
8
+ const disposables = injectDisposables();
16
9
  const activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
17
- const activeItem = computed(() => sortedOptions()?.[activeIndex()], ...(ngDevMode ? [{ debugName: "activeItem" }] : []));
18
- const disabled = computed(() => _disabled?.() || items().every(item => item.disabled?.()), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
19
- // any time the item list changes, check if the active index is still valid
20
- explicitEffect([sortedOptions], ([items]) => {
21
- if (activeIndex() >= items.length || activeIndex() < 0) {
22
- activateByIndex(items.findIndex(item => !item.disabled?.()));
23
- }
24
- if (activeIndex() === -1 && items.length > 0) {
25
- activateByIndex(0);
10
+ const disabled = controlled(_disabled);
11
+ let isIgnoringPointer = false;
12
+ let clearPointerIgnoreTimer;
13
+ // compute the id of the active descendant
14
+ const id = computed(() => {
15
+ const index = activeIndex();
16
+ return index >= 0 && index < count() ? getItemId(index) : undefined;
17
+ }, ...(ngDevMode ? [{ debugName: "id" }] : []));
18
+ /**
19
+ * Start ignoring pointer interactions temporarily.
20
+ */
21
+ function startIgnoringPointer() {
22
+ isIgnoringPointer = true;
23
+ // Clear any existing timer
24
+ clearPointerIgnoreTimer?.();
25
+ // Reset ignore state after a short delay
26
+ clearPointerIgnoreTimer = disposables.setTimeout(() => {
27
+ isIgnoringPointer = false;
28
+ clearPointerIgnoreTimer = undefined;
29
+ }, 200); // 200ms should be enough for scroll to complete
30
+ }
31
+ function activateByIndex(index, { scroll = true, origin } = {}) {
32
+ if (disabled() || (index >= 0 && isItemDisabled(index))) {
33
+ return;
26
34
  }
27
- if (disabled() || items.length === 0) {
28
- activateByIndex(-1);
35
+ // if the origin is the pointer but we are ignoring pointer interactions, do nothing
36
+ if (origin === 'pointer' && isIgnoringPointer) {
37
+ return;
29
38
  }
30
- });
31
- const activeDescendant = computed(() => {
32
- const item = activeItem();
33
- if (disabled() || !item) {
34
- return undefined;
39
+ // ensure any pointer interactions triggered via scrolling due to keyboard navigation are ignored
40
+ if (origin === 'keyboard') {
41
+ startIgnoringPointer();
35
42
  }
36
- return item.id();
37
- }, ...(ngDevMode ? [{ debugName: "activeDescendant" }] : []));
38
- function activateByIndex(index) {
39
43
  activeIndex.set(index);
40
- onActiveDescendantChange?.(activeItem());
41
- }
42
- /**
43
- * Activate an item in the active descendant group.
44
- * @param item The item to activate.
45
- */
46
- const activate = (item) => {
47
- if (item === undefined) {
48
- activateByIndex(-1);
44
+ if (index < 0 || index >= count()) {
49
45
  return;
50
46
  }
51
- if (disabled() || item.disabled?.()) {
52
- return;
47
+ if (scroll) {
48
+ scrollIntoView(index);
53
49
  }
54
- activateByIndex(sortedOptions().indexOf(item));
55
- };
56
- /**
57
- * Activate the first enabled item in the active descendant group.
58
- */
59
- const first = () => {
60
- const item = sortedOptions().findIndex(item => !item.disabled?.());
61
- if (item !== -1) {
62
- activateByIndex(item);
50
+ }
51
+ function activateById(id, options = {}) {
52
+ for (let i = 0; i < count(); i++) {
53
+ if (getItemId(i) === id) {
54
+ activateByIndex(i, options);
55
+ return;
56
+ }
63
57
  }
64
- };
65
- /**
66
- * Activate the last enabled item in the active descendant group.
67
- */
68
- const last = () => {
69
- const item = [...sortedOptions()].reverse().findIndex(item => !item.disabled?.());
70
- if (item !== -1) {
71
- activateByIndex(sortedOptions().length - 1 - item);
58
+ }
59
+ function first(options = {}) {
60
+ for (let i = 0; i < count(); i++) {
61
+ if (!isItemDisabled(i)) {
62
+ activateByIndex(i, options);
63
+ return;
64
+ }
72
65
  }
73
- };
74
- const findNextIndex = (items, currentIndex, direction, wrap) => {
75
- let index = (currentIndex + direction + items.length) % items.length;
76
- while (index !== currentIndex) {
77
- const item = items[index];
78
- if (item && !item.disabled?.()) {
79
- return index;
66
+ }
67
+ function last(options = {}) {
68
+ for (let i = count() - 1; i >= 0; i--) {
69
+ if (!isItemDisabled(i)) {
70
+ activateByIndex(i, options);
71
+ return;
72
+ }
73
+ }
74
+ }
75
+ function next(options = {}) {
76
+ let index = activeIndex() + 1;
77
+ while (index !== activeIndex()) {
78
+ if (index >= count()) {
79
+ if (wrap?.()) {
80
+ index = 0;
81
+ }
82
+ else {
83
+ return;
84
+ }
80
85
  }
81
- index = (index + direction + items.length) % items.length;
82
- if (!wrap &&
83
- ((direction === 1 && index === 0) || (direction === -1 && index === items.length - 1))) {
84
- break;
86
+ if (!isItemDisabled(index)) {
87
+ activateByIndex(index, options);
88
+ return;
85
89
  }
90
+ index++;
86
91
  }
87
- return undefined;
88
- };
89
- /**
90
- * Activate the next enabled item in the active descendant group.
91
- */
92
- const next = () => {
93
- const items = sortedOptions();
94
- const nextIndex = findNextIndex(items, activeIndex(), 1, wrap?.() ?? false);
95
- if (nextIndex !== undefined) {
96
- activateByIndex(nextIndex);
92
+ }
93
+ function previous(options = {}) {
94
+ let index = activeIndex() - 1;
95
+ while (index !== activeIndex()) {
96
+ if (index < 0) {
97
+ if (wrap?.()) {
98
+ index = count() - 1;
99
+ }
100
+ else {
101
+ return;
102
+ }
103
+ }
104
+ if (!isItemDisabled(index)) {
105
+ activateByIndex(index, options);
106
+ return;
107
+ }
108
+ index--;
97
109
  }
98
- };
99
- /**
100
- * Activate the previous enabled item in the active descendant group.
101
- */
102
- const previous = () => {
103
- const items = sortedOptions();
104
- const prevIndex = findNextIndex(items, activeIndex(), -1, wrap?.() ?? false);
105
- if (prevIndex !== undefined) {
106
- activateByIndex(prevIndex);
110
+ }
111
+ // any time the item list changes, check if the active index is still valid
112
+ function validate() {
113
+ const index = activeIndex();
114
+ // if the index is out of bounds, reset it
115
+ if (index >= count() || index < 0 || isItemDisabled(index)) {
116
+ // find the first enabled item
117
+ for (let i = 0; i < count(); i++) {
118
+ if (!isItemDisabled(i)) {
119
+ activateByIndex(i);
120
+ return;
121
+ }
122
+ }
123
+ // if no enabled items, deactivate
124
+ activateByIndex(-1);
107
125
  }
108
- };
126
+ }
109
127
  /**
110
128
  * Reset the active descendant group, clearing the active index.
111
129
  */
112
- const reset = () => {
113
- activateByIndex(-1);
130
+ const reset = ({ scroll = false, origin } = {}) => {
131
+ activateByIndex(-1, { scroll, origin });
114
132
  };
115
133
  return {
116
- activeDescendant,
117
- activeItem,
118
- activate,
134
+ id,
135
+ index: activeIndex,
136
+ activateByIndex,
137
+ activateById,
119
138
  first,
120
139
  last,
121
140
  next,
122
141
  previous,
123
142
  reset,
143
+ validate,
124
144
  };
125
145
  }
126
146
 
@@ -1 +1 @@
1
- {"version":3,"file":"ng-primitives-a11y.mjs","sources":["../../../../packages/ng-primitives/a11y/src/active-descendant/active-descendant.ts","../../../../packages/ng-primitives/a11y/src/visually-hidden/visually-hidden-state.ts","../../../../packages/ng-primitives/a11y/src/visually-hidden/visually-hidden.ts","../../../../packages/ng-primitives/a11y/src/ng-primitives-a11y.ts"],"sourcesContent":["import { computed, ElementRef, Signal, signal } from '@angular/core';\nimport { explicitEffect } from 'ng-primitives/internal';\n\ninterface ActiveDescendantManagerProps<T extends NgpActivatable> {\n /**\n * The disabled state of the active descendant group.\n * @default false\n */\n disabled?: Signal<boolean>;\n /**\n * The items in the active descendant group.\n */\n items: Signal<T[]>;\n /**\n * Whether active descendant should wrap around.\n * @default false\n */\n wrap?: Signal<boolean>;\n /**\n * A callback invoked when the active descendant changes.\n */\n onActiveDescendantChange?: (activeDescendant: T | undefined) => void;\n}\n\nexport interface NgpActivatable {\n /**\n * The id of the item.\n */\n id: Signal<string>;\n /**\n * Whether the item is disabled.\n */\n disabled?: Signal<boolean>;\n /**\n * The element that represents the item.\n */\n elementRef: ElementRef<HTMLElement>;\n}\n\nexport function activeDescendantManager<T extends NgpActivatable>({\n items,\n disabled: _disabled,\n wrap,\n onActiveDescendantChange,\n}: ActiveDescendantManagerProps<T>) {\n const sortedOptions = computed(() =>\n items()\n .slice()\n .sort((a, b) => {\n const aElement = a.elementRef.nativeElement;\n const bElement = b.elementRef.nativeElement;\n return aElement.compareDocumentPosition(bElement) & Node.DOCUMENT_POSITION_FOLLOWING\n ? -1\n : 1;\n }),\n );\n\n const activeIndex = signal<number>(0);\n const activeItem = computed<T | undefined>(() => sortedOptions()?.[activeIndex()]);\n const disabled = computed(() => _disabled?.() || items().every(item => item.disabled?.()));\n\n // any time the item list changes, check if the active index is still valid\n explicitEffect([sortedOptions], ([items]) => {\n if (activeIndex() >= items.length || activeIndex() < 0) {\n activateByIndex(items.findIndex(item => !item.disabled?.()));\n }\n if (activeIndex() === -1 && items.length > 0) {\n activateByIndex(0);\n }\n if (disabled() || items.length === 0) {\n activateByIndex(-1);\n }\n });\n\n const activeDescendant = computed(() => {\n const item = activeItem();\n\n if (disabled() || !item) {\n return undefined;\n }\n\n return item.id();\n });\n\n function activateByIndex(index: number) {\n activeIndex.set(index);\n onActiveDescendantChange?.(activeItem());\n }\n\n /**\n * Activate an item in the active descendant group.\n * @param item The item to activate.\n */\n const activate = (item: T | undefined) => {\n if (item === undefined) {\n activateByIndex(-1);\n return;\n }\n\n if (disabled() || item.disabled?.()) {\n return;\n }\n\n activateByIndex(sortedOptions().indexOf(item));\n };\n\n /**\n * Activate the first enabled item in the active descendant group.\n */\n const first = () => {\n const item = sortedOptions().findIndex(item => !item.disabled?.());\n\n if (item !== -1) {\n activateByIndex(item);\n }\n };\n\n /**\n * Activate the last enabled item in the active descendant group.\n */\n const last = () => {\n const item = [...sortedOptions()].reverse().findIndex(item => !item.disabled?.());\n\n if (item !== -1) {\n activateByIndex(sortedOptions().length - 1 - item);\n }\n };\n\n const findNextIndex = (\n items: NgpActivatable[],\n currentIndex: number,\n direction: 1 | -1,\n wrap: boolean,\n ): number | undefined => {\n let index = (currentIndex + direction + items.length) % items.length;\n\n while (index !== currentIndex) {\n const item = items[index];\n if (item && !item.disabled?.()) {\n return index;\n }\n index = (index + direction + items.length) % items.length;\n\n if (\n !wrap &&\n ((direction === 1 && index === 0) || (direction === -1 && index === items.length - 1))\n ) {\n break;\n }\n }\n\n return undefined;\n };\n\n /**\n * Activate the next enabled item in the active descendant group.\n */\n const next = () => {\n const items = sortedOptions();\n const nextIndex = findNextIndex(items, activeIndex(), 1, wrap?.() ?? false);\n\n if (nextIndex !== undefined) {\n activateByIndex(nextIndex);\n }\n };\n\n /**\n * Activate the previous enabled item in the active descendant group.\n */\n const previous = () => {\n const items = sortedOptions();\n const prevIndex = findNextIndex(items, activeIndex(), -1, wrap?.() ?? false);\n\n if (prevIndex !== undefined) {\n activateByIndex(prevIndex);\n }\n };\n\n /**\n * Reset the active descendant group, clearing the active index.\n */\n const reset = () => {\n activateByIndex(-1);\n };\n\n return {\n activeDescendant,\n activeItem,\n activate,\n first,\n last,\n next,\n previous,\n reset,\n };\n}\n","import { ChangeDetectorRef, inject, Signal, signal } from '@angular/core';\nimport { injectElementRef } from 'ng-primitives/internal';\nimport { controlled, createPrimitive, styleBinding } from 'ng-primitives/state';\n\n/**\n * The state interface for the VisuallyHidden pattern.\n */\nexport interface NgpVisuallyHiddenState {\n /**\n * Whether the element is hidden.\n */\n readonly hidden: Signal<boolean>;\n\n /**\n * Set the element visibility.\n * @param visible\n */\n setVisibility(visible: boolean): void;\n}\n\n/**\n * The props interface for the VisuallyHidden pattern.\n */\nexport interface NgpVisuallyHiddenProps {\n /**\n * Whether the element is hidden. Default is true.\n */\n readonly hidden?: Signal<boolean>;\n}\n\nexport const [\n NgpVisuallyHiddenStateToken,\n ngpVisuallyHidden,\n injectVisuallyHiddenState,\n provideVisuallyHiddenState,\n] = createPrimitive(\n 'NgpVisuallyHidden',\n ({ hidden: _hidden = signal(true) }: NgpVisuallyHiddenProps): NgpVisuallyHiddenState => {\n const element = injectElementRef();\n const hidden = controlled(_hidden);\n const changeDetector = inject(ChangeDetectorRef);\n\n // Apply styles to visually hide the element\n styleBinding(element, 'position', () => (hidden() ? 'absolute' : null));\n styleBinding(element, 'width', () => (hidden() ? '1px' : null));\n styleBinding(element, 'height', () => (hidden() ? '1px' : null));\n styleBinding(element, 'margin', () => (hidden() ? '-1px' : null));\n styleBinding(element, 'padding', () => (hidden() ? '0' : null));\n styleBinding(element, 'overflow', () => (hidden() ? 'hidden' : null));\n styleBinding(element, 'clip', () => (hidden() ? 'rect(0, 0, 0, 0)' : null));\n styleBinding(element, 'white-space', () => (hidden() ? 'nowrap' : null));\n styleBinding(element, 'border', () => (hidden() ? '0' : null));\n styleBinding(element, 'word-wrap', () => (hidden() ? 'normal' : null));\n styleBinding(element, 'outline', () => (hidden() ? '0' : null));\n styleBinding(element, '-webkit-appearance', () => (hidden() ? 'none' : null));\n styleBinding(element, '-moz-appearance', () => (hidden() ? 'none' : null));\n styleBinding(element, 'inset-inline-start', () => (hidden() ? '0' : null));\n\n function setVisibility(visible: boolean): void {\n hidden.set(!visible);\n // If a change-detection cycle might be running, schedule detection asynchronously and exit\n // to avoid re-entrancy. Otherwise fall through to call detectChanges synchronously.\n Promise.resolve().then(() => changeDetector.detectChanges());\n }\n\n return {\n hidden: hidden.asReadonly(),\n setVisibility,\n } satisfies NgpVisuallyHiddenState;\n },\n);\n","import { Directive } from '@angular/core';\nimport { ngpVisuallyHidden, provideVisuallyHiddenState } from './visually-hidden-state';\n\n/**\n * Hide an element visually while keeping it present in the DOM.\n */\n@Directive({\n selector: '[ngpVisuallyHidden]',\n exportAs: 'ngpVisuallyHidden',\n providers: [provideVisuallyHiddenState()],\n})\nexport class NgpVisuallyHidden {\n protected readonly state = ngpVisuallyHidden({});\n\n /**\n * Set the element visibility.\n * @param visible\n */\n setVisibility(visible: boolean): void {\n this.state.setVisibility(visible);\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":"ng-primitives-a11y.mjs","sources":["../../../../packages/ng-primitives/a11y/src/active-descendant/active-descendant.ts","../../../../packages/ng-primitives/a11y/src/visually-hidden/visually-hidden-state.ts","../../../../packages/ng-primitives/a11y/src/visually-hidden/visually-hidden.ts","../../../../packages/ng-primitives/a11y/src/ng-primitives-a11y.ts"],"sourcesContent":["import { computed, Signal, signal } from '@angular/core';\nimport { controlled } from 'ng-primitives/state';\nimport { injectDisposables } from 'ng-primitives/utils';\n\nexport interface NgpActiveDescendantManagerProps {\n /**\n * The disabled state of the active descendant group.\n * @default false\n */\n disabled?: Signal<boolean>;\n\n /**\n * The number of items in the active descendant group.\n */\n count: Signal<number>;\n\n /**\n * Get the id for the item at a given index.\n */\n getItemId: (index: number) => string | undefined;\n\n /**\n * Whether the item at a given index is disabled.\n */\n isItemDisabled: (index: number) => boolean;\n\n /**\n * Scroll the active descendant item into view.\n */\n scrollIntoView: (index: number) => void;\n\n /**\n * Whether active descendant should wrap around.\n * @default false\n */\n wrap?: Signal<boolean>;\n}\n\nexport interface NgpActiveDescendantManagerState {\n /**\n * The index of the active descendant item.\n */\n index: Signal<number>;\n\n /**\n * The id of the active descendant item.\n */\n id: Signal<string | undefined>;\n\n /**\n * Activate an item in the active descendant group.\n */\n activateByIndex: (index: number, options?: ActivationOptions) => void;\n\n /**\n * Activate an item in the active descendant group by id.\n */\n activateById: (id: string, options?: ActivationOptions) => void;\n\n /**\n * Activate the first enabled item in the active descendant group.\n */\n first: (options?: ActivationOptions) => void;\n /**\n * Activate the last enabled item in the active descendant group.\n */\n last: (options?: ActivationOptions) => void;\n /**\n * Activate the next enabled item in the active descendant group.\n */\n next: (options?: ActivationOptions) => void;\n /**\n * Activate the previous enabled item in the active descendant group.\n */\n previous: (options?: ActivationOptions) => void;\n /**\n * Ensure there is an active descendant, this is useful when the items in the group change.\n */\n validate: () => void;\n /**\n * Reset the active descendant group, clearing the active index.\n */\n reset: (options?: ActivationOptions) => void;\n}\n\nexport function activeDescendantManager({\n disabled: _disabled = signal(false),\n wrap,\n count,\n getItemId,\n isItemDisabled,\n scrollIntoView,\n}: NgpActiveDescendantManagerProps) {\n const disposables = injectDisposables();\n\n const activeIndex = signal<number>(0);\n const disabled = controlled(_disabled);\n let isIgnoringPointer = false;\n\n let clearPointerIgnoreTimer: (() => void) | undefined;\n\n // compute the id of the active descendant\n const id = computed(() => {\n const index = activeIndex();\n return index >= 0 && index < count() ? getItemId(index) : undefined;\n });\n\n /**\n * Start ignoring pointer interactions temporarily.\n */\n function startIgnoringPointer(): void {\n isIgnoringPointer = true;\n\n // Clear any existing timer\n clearPointerIgnoreTimer?.();\n\n // Reset ignore state after a short delay\n clearPointerIgnoreTimer = disposables.setTimeout(() => {\n isIgnoringPointer = false;\n clearPointerIgnoreTimer = undefined;\n }, 200); // 200ms should be enough for scroll to complete\n }\n\n function activateByIndex(index: number, { scroll = true, origin }: ActivationOptions = {}): void {\n if (disabled() || (index >= 0 && isItemDisabled(index))) {\n return;\n }\n\n // if the origin is the pointer but we are ignoring pointer interactions, do nothing\n if (origin === 'pointer' && isIgnoringPointer) {\n return;\n }\n\n // ensure any pointer interactions triggered via scrolling due to keyboard navigation are ignored\n if (origin === 'keyboard') {\n startIgnoringPointer();\n }\n\n activeIndex.set(index);\n\n if (index < 0 || index >= count()) {\n return;\n }\n\n if (scroll) {\n scrollIntoView(index);\n }\n }\n\n function activateById(id: string, options: ActivationOptions = {}): void {\n for (let i = 0; i < count(); i++) {\n if (getItemId(i) === id) {\n activateByIndex(i, options);\n return;\n }\n }\n }\n\n function first(options: ActivationOptions = {}): void {\n for (let i = 0; i < count(); i++) {\n if (!isItemDisabled(i)) {\n activateByIndex(i, options);\n return;\n }\n }\n }\n\n function last(options: ActivationOptions = {}): void {\n for (let i = count() - 1; i >= 0; i--) {\n if (!isItemDisabled(i)) {\n activateByIndex(i, options);\n return;\n }\n }\n }\n\n function next(options: ActivationOptions = {}): void {\n let index = activeIndex() + 1;\n\n while (index !== activeIndex()) {\n if (index >= count()) {\n if (wrap?.()) {\n index = 0;\n } else {\n return;\n }\n }\n\n if (!isItemDisabled(index)) {\n activateByIndex(index, options);\n return;\n }\n\n index++;\n }\n }\n\n function previous(options: ActivationOptions = {}): void {\n let index = activeIndex() - 1;\n\n while (index !== activeIndex()) {\n if (index < 0) {\n if (wrap?.()) {\n index = count() - 1;\n } else {\n return;\n }\n }\n\n if (!isItemDisabled(index)) {\n activateByIndex(index, options);\n return;\n }\n\n index--;\n }\n }\n\n // any time the item list changes, check if the active index is still valid\n function validate(): void {\n const index = activeIndex();\n\n // if the index is out of bounds, reset it\n if (index >= count() || index < 0 || isItemDisabled(index)) {\n // find the first enabled item\n for (let i = 0; i < count(); i++) {\n if (!isItemDisabled(i)) {\n activateByIndex(i);\n return;\n }\n }\n // if no enabled items, deactivate\n activateByIndex(-1);\n }\n }\n\n /**\n * Reset the active descendant group, clearing the active index.\n */\n const reset = ({ scroll = false, origin }: ActivationOptions = {}) => {\n activateByIndex(-1, { scroll, origin });\n };\n\n return {\n id,\n index: activeIndex,\n activateByIndex,\n activateById,\n first,\n last,\n next,\n previous,\n reset,\n validate,\n } satisfies NgpActiveDescendantManagerState;\n}\n\nexport interface ActivationOptions {\n /** Whether to scroll the activated item into view. */\n scroll?: boolean;\n /** Define the source of activation. */\n origin?: 'keyboard' | 'pointer';\n}\n","import { ChangeDetectorRef, inject, Signal, signal } from '@angular/core';\nimport { injectElementRef } from 'ng-primitives/internal';\nimport { controlled, createPrimitive, styleBinding } from 'ng-primitives/state';\n\n/**\n * The state interface for the VisuallyHidden pattern.\n */\nexport interface NgpVisuallyHiddenState {\n /**\n * Whether the element is hidden.\n */\n readonly hidden: Signal<boolean>;\n\n /**\n * Set the element visibility.\n * @param visible\n */\n setVisibility(visible: boolean): void;\n}\n\n/**\n * The props interface for the VisuallyHidden pattern.\n */\nexport interface NgpVisuallyHiddenProps {\n /**\n * Whether the element is hidden. Default is true.\n */\n readonly hidden?: Signal<boolean>;\n}\n\nexport const [\n NgpVisuallyHiddenStateToken,\n ngpVisuallyHidden,\n injectVisuallyHiddenState,\n provideVisuallyHiddenState,\n] = createPrimitive(\n 'NgpVisuallyHidden',\n ({ hidden: _hidden = signal(true) }: NgpVisuallyHiddenProps): NgpVisuallyHiddenState => {\n const element = injectElementRef();\n const hidden = controlled(_hidden);\n const changeDetector = inject(ChangeDetectorRef);\n\n // Apply styles to visually hide the element\n styleBinding(element, 'position', () => (hidden() ? 'absolute' : null));\n styleBinding(element, 'width', () => (hidden() ? '1px' : null));\n styleBinding(element, 'height', () => (hidden() ? '1px' : null));\n styleBinding(element, 'margin', () => (hidden() ? '-1px' : null));\n styleBinding(element, 'padding', () => (hidden() ? '0' : null));\n styleBinding(element, 'overflow', () => (hidden() ? 'hidden' : null));\n styleBinding(element, 'clip', () => (hidden() ? 'rect(0, 0, 0, 0)' : null));\n styleBinding(element, 'white-space', () => (hidden() ? 'nowrap' : null));\n styleBinding(element, 'border', () => (hidden() ? '0' : null));\n styleBinding(element, 'word-wrap', () => (hidden() ? 'normal' : null));\n styleBinding(element, 'outline', () => (hidden() ? '0' : null));\n styleBinding(element, '-webkit-appearance', () => (hidden() ? 'none' : null));\n styleBinding(element, '-moz-appearance', () => (hidden() ? 'none' : null));\n styleBinding(element, 'inset-inline-start', () => (hidden() ? '0' : null));\n\n function setVisibility(visible: boolean): void {\n hidden.set(!visible);\n // If a change-detection cycle might be running, schedule detection asynchronously and exit\n // to avoid re-entrancy. Otherwise fall through to call detectChanges synchronously.\n Promise.resolve().then(() => changeDetector.detectChanges());\n }\n\n return {\n hidden: hidden.asReadonly(),\n setVisibility,\n } satisfies NgpVisuallyHiddenState;\n },\n);\n","import { Directive } from '@angular/core';\nimport { ngpVisuallyHidden, provideVisuallyHiddenState } from './visually-hidden-state';\n\n/**\n * Hide an element visually while keeping it present in the DOM.\n */\n@Directive({\n selector: '[ngpVisuallyHidden]',\n exportAs: 'ngpVisuallyHidden',\n providers: [provideVisuallyHiddenState()],\n})\nexport class NgpVisuallyHidden {\n protected readonly state = ngpVisuallyHidden({});\n\n /**\n * Set the element visibility.\n * @param visible\n */\n setVisibility(visible: boolean): void {\n this.state.setVisibility(visible);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}