ng-primitives 0.120.4 → 0.121.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 (147) hide show
  1. package/example-theme/index.css +9 -0
  2. package/fesm2022/ng-primitives-a11y.mjs +5 -5
  3. package/fesm2022/ng-primitives-a11y.mjs.map +1 -1
  4. package/fesm2022/ng-primitives-accordion.mjs +34 -51
  5. package/fesm2022/ng-primitives-accordion.mjs.map +1 -1
  6. package/fesm2022/ng-primitives-ai.mjs +39 -52
  7. package/fesm2022/ng-primitives-ai.mjs.map +1 -1
  8. package/fesm2022/ng-primitives-autofill.mjs +4 -4
  9. package/fesm2022/ng-primitives-autofill.mjs.map +1 -1
  10. package/fesm2022/ng-primitives-avatar.mjs +14 -17
  11. package/fesm2022/ng-primitives-avatar.mjs.map +1 -1
  12. package/fesm2022/ng-primitives-breadcrumbs.mjs +21 -21
  13. package/fesm2022/ng-primitives-breadcrumbs.mjs.map +1 -1
  14. package/fesm2022/ng-primitives-button.mjs +7 -9
  15. package/fesm2022/ng-primitives-button.mjs.map +1 -1
  16. package/fesm2022/ng-primitives-checkbox.mjs +15 -30
  17. package/fesm2022/ng-primitives-checkbox.mjs.map +1 -1
  18. package/fesm2022/ng-primitives-combobox.mjs +60 -92
  19. package/fesm2022/ng-primitives-combobox.mjs.map +1 -1
  20. package/fesm2022/ng-primitives-common.mjs +4 -4
  21. package/fesm2022/ng-primitives-common.mjs.map +1 -1
  22. package/fesm2022/ng-primitives-context-menu.mjs +68 -121
  23. package/fesm2022/ng-primitives-context-menu.mjs.map +1 -1
  24. package/fesm2022/ng-primitives-date-picker.mjs +91 -115
  25. package/fesm2022/ng-primitives-date-picker.mjs.map +1 -1
  26. package/fesm2022/ng-primitives-dialog.mjs +35 -51
  27. package/fesm2022/ng-primitives-dialog.mjs.map +1 -1
  28. package/fesm2022/ng-primitives-file-upload.mjs +26 -53
  29. package/fesm2022/ng-primitives-file-upload.mjs.map +1 -1
  30. package/fesm2022/ng-primitives-focus-trap.mjs +5 -8
  31. package/fesm2022/ng-primitives-focus-trap.mjs.map +1 -1
  32. package/fesm2022/ng-primitives-form-field.mjs +39 -44
  33. package/fesm2022/ng-primitives-form-field.mjs.map +1 -1
  34. package/fesm2022/ng-primitives-input-otp.mjs +30 -43
  35. package/fesm2022/ng-primitives-input-otp.mjs.map +1 -1
  36. package/fesm2022/ng-primitives-input.mjs +6 -8
  37. package/fesm2022/ng-primitives-input.mjs.map +1 -1
  38. package/fesm2022/ng-primitives-interactions.mjs +31 -48
  39. package/fesm2022/ng-primitives-interactions.mjs.map +1 -1
  40. package/fesm2022/ng-primitives-internal.mjs +39 -35
  41. package/fesm2022/ng-primitives-internal.mjs.map +1 -1
  42. package/fesm2022/ng-primitives-listbox.mjs +34 -48
  43. package/fesm2022/ng-primitives-listbox.mjs.map +1 -1
  44. package/fesm2022/ng-primitives-menu.mjs +85 -151
  45. package/fesm2022/ng-primitives-menu.mjs.map +1 -1
  46. package/fesm2022/ng-primitives-meter.mjs +25 -36
  47. package/fesm2022/ng-primitives-meter.mjs.map +1 -1
  48. package/fesm2022/ng-primitives-navigation-menu.mjs +60 -108
  49. package/fesm2022/ng-primitives-navigation-menu.mjs.map +1 -1
  50. package/fesm2022/ng-primitives-number-field.mjs +35 -59
  51. package/fesm2022/ng-primitives-number-field.mjs.map +1 -1
  52. package/fesm2022/ng-primitives-pagination.mjs +270 -297
  53. package/fesm2022/ng-primitives-pagination.mjs.map +1 -1
  54. package/fesm2022/ng-primitives-popover.mjs +209 -223
  55. package/fesm2022/ng-primitives-popover.mjs.map +1 -1
  56. package/fesm2022/ng-primitives-portal.mjs +88 -59
  57. package/fesm2022/ng-primitives-portal.mjs.map +1 -1
  58. package/fesm2022/ng-primitives-progress.mjs +30 -41
  59. package/fesm2022/ng-primitives-progress.mjs.map +1 -1
  60. package/fesm2022/ng-primitives-radio.mjs +20 -30
  61. package/fesm2022/ng-primitives-radio.mjs.map +1 -1
  62. package/fesm2022/ng-primitives-resize.mjs +3 -3
  63. package/fesm2022/ng-primitives-resize.mjs.map +1 -1
  64. package/fesm2022/ng-primitives-roving-focus.mjs +20 -34
  65. package/fesm2022/ng-primitives-roving-focus.mjs.map +1 -1
  66. package/fesm2022/ng-primitives-search.mjs +9 -9
  67. package/fesm2022/ng-primitives-search.mjs.map +1 -1
  68. package/fesm2022/ng-primitives-select.mjs +720 -620
  69. package/fesm2022/ng-primitives-select.mjs.map +1 -1
  70. package/fesm2022/ng-primitives-separator.mjs +4 -6
  71. package/fesm2022/ng-primitives-separator.mjs.map +1 -1
  72. package/fesm2022/ng-primitives-slider.mjs +63 -100
  73. package/fesm2022/ng-primitives-slider.mjs.map +1 -1
  74. package/fesm2022/ng-primitives-state.mjs +3 -3
  75. package/fesm2022/ng-primitives-state.mjs.map +1 -1
  76. package/fesm2022/ng-primitives-switch.mjs +15 -21
  77. package/fesm2022/ng-primitives-switch.mjs.map +1 -1
  78. package/fesm2022/ng-primitives-tabs.mjs +31 -41
  79. package/fesm2022/ng-primitives-tabs.mjs.map +1 -1
  80. package/fesm2022/ng-primitives-textarea.mjs +5 -7
  81. package/fesm2022/ng-primitives-textarea.mjs.map +1 -1
  82. package/fesm2022/ng-primitives-toast.mjs +30 -23
  83. package/fesm2022/ng-primitives-toast.mjs.map +1 -1
  84. package/fesm2022/ng-primitives-toggle-group.mjs +20 -36
  85. package/fesm2022/ng-primitives-toggle-group.mjs.map +1 -1
  86. package/fesm2022/ng-primitives-toggle.mjs +10 -19
  87. package/fesm2022/ng-primitives-toggle.mjs.map +1 -1
  88. package/fesm2022/ng-primitives-toolbar.mjs +4 -6
  89. package/fesm2022/ng-primitives-toolbar.mjs.map +1 -1
  90. package/fesm2022/ng-primitives-tooltip.mjs +401 -402
  91. package/fesm2022/ng-primitives-tooltip.mjs.map +1 -1
  92. package/fesm2022/ng-primitives-utils.mjs +5 -4
  93. package/fesm2022/ng-primitives-utils.mjs.map +1 -1
  94. package/package.json +56 -55
  95. package/schematics/ng-generate/templates/select/select.__fileSuffix@dasherize__.ts.template +48 -48
  96. package/{a11y/index.d.ts → types/ng-primitives-a11y.d.ts} +18 -24
  97. package/{accordion/index.d.ts → types/ng-primitives-accordion.d.ts} +85 -98
  98. package/{autofill/index.d.ts → types/ng-primitives-autofill.d.ts} +3 -10
  99. package/{avatar/index.d.ts → types/ng-primitives-avatar.d.ts} +7 -30
  100. package/{breadcrumbs/index.d.ts → types/ng-primitives-breadcrumbs.d.ts} +15 -70
  101. package/{button/index.d.ts → types/ng-primitives-button.d.ts} +23 -29
  102. package/{checkbox/index.d.ts → types/ng-primitives-checkbox.d.ts} +68 -74
  103. package/{combobox/index.d.ts → types/ng-primitives-combobox.d.ts} +35 -35
  104. package/{context-menu/index.d.ts → types/ng-primitives-context-menu.d.ts} +2 -1
  105. package/{date-picker/index.d.ts → types/ng-primitives-date-picker.d.ts} +2 -2
  106. package/{dialog/index.d.ts → types/ng-primitives-dialog.d.ts} +7 -2
  107. package/{file-upload/index.d.ts → types/ng-primitives-file-upload.d.ts} +5 -20
  108. package/{focus-trap/index.d.ts → types/ng-primitives-focus-trap.d.ts} +3 -10
  109. package/{form-field/index.d.ts → types/ng-primitives-form-field.d.ts} +9 -40
  110. package/{input/index.d.ts → types/ng-primitives-input.d.ts} +3 -10
  111. package/{menu/index.d.ts → types/ng-primitives-menu.d.ts} +17 -63
  112. package/{navigation-menu/index.d.ts → types/ng-primitives-navigation-menu.d.ts} +15 -70
  113. package/{number-field/index.d.ts → types/ng-primitives-number-field.d.ts} +80 -110
  114. package/types/ng-primitives-pagination.d.ts +502 -0
  115. package/{popover/index.d.ts → types/ng-primitives-popover.d.ts} +244 -57
  116. package/{portal/index.d.ts → types/ng-primitives-portal.d.ts} +22 -13
  117. package/{progress/index.d.ts → types/ng-primitives-progress.d.ts} +3 -10
  118. package/{roving-focus/index.d.ts → types/ng-primitives-roving-focus.d.ts} +65 -79
  119. package/types/ng-primitives-select.d.ts +687 -0
  120. package/{separator/index.d.ts → types/ng-primitives-separator.d.ts} +3 -10
  121. package/{slider/index.d.ts → types/ng-primitives-slider.d.ts} +76 -138
  122. package/{state/index.d.ts → types/ng-primitives-state.d.ts} +26 -21
  123. package/{switch/index.d.ts → types/ng-primitives-switch.d.ts} +50 -64
  124. package/{tabs/index.d.ts → types/ng-primitives-tabs.d.ts} +9 -40
  125. package/{textarea/index.d.ts → types/ng-primitives-textarea.d.ts} +3 -10
  126. package/{toast/index.d.ts → types/ng-primitives-toast.d.ts} +13 -1
  127. package/{toggle-group/index.d.ts → types/ng-primitives-toggle-group.d.ts} +75 -89
  128. package/{toggle/index.d.ts → types/ng-primitives-toggle.d.ts} +50 -56
  129. package/{toolbar/index.d.ts → types/ng-primitives-toolbar.d.ts} +3 -10
  130. package/types/ng-primitives-tooltip.d.ts +691 -0
  131. package/pagination/index.d.ts +0 -211
  132. package/select/index.d.ts +0 -396
  133. package/tooltip/index.d.ts +0 -384
  134. /package/{ai/index.d.ts → types/ng-primitives-ai.d.ts} +0 -0
  135. /package/{common/index.d.ts → types/ng-primitives-common.d.ts} +0 -0
  136. /package/{date-time-luxon/index.d.ts → types/ng-primitives-date-time-luxon.d.ts} +0 -0
  137. /package/{date-time/index.d.ts → types/ng-primitives-date-time.d.ts} +0 -0
  138. /package/{input-otp/index.d.ts → types/ng-primitives-input-otp.d.ts} +0 -0
  139. /package/{interactions/index.d.ts → types/ng-primitives-interactions.d.ts} +0 -0
  140. /package/{internal/index.d.ts → types/ng-primitives-internal.d.ts} +0 -0
  141. /package/{listbox/index.d.ts → types/ng-primitives-listbox.d.ts} +0 -0
  142. /package/{meter/index.d.ts → types/ng-primitives-meter.d.ts} +0 -0
  143. /package/{radio/index.d.ts → types/ng-primitives-radio.d.ts} +0 -0
  144. /package/{resize/index.d.ts → types/ng-primitives-resize.d.ts} +0 -0
  145. /package/{search/index.d.ts → types/ng-primitives-search.d.ts} +0 -0
  146. /package/{utils/index.d.ts → types/ng-primitives-utils.d.ts} +0 -0
  147. /package/{index.d.ts → types/ng-primitives.d.ts} +0 -0
@@ -1,29 +1,34 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, booleanAttribute, Directive, output, computed, HostListener, inject, ViewContainerRef, TemplateRef, Injector, signal, InjectionToken } from '@angular/core';
2
+ import { signal, input, booleanAttribute, Directive, computed, output, inject, TemplateRef, ViewContainerRef, Injector, InjectionToken } from '@angular/core';
3
+ import { uniqueId } from 'ng-primitives/utils';
3
4
  import { ngpFormControl } from 'ng-primitives/form-field';
4
5
  import { ngpInteractions } from 'ng-primitives/interactions';
5
- import { uniqueId } from 'ng-primitives/utils';
6
- import { createStateToken, createStateProvider, createStateInjector, createState } from 'ng-primitives/state';
7
- import { observeResize, injectElementRef, scrollIntoViewIfNeeded, domSort } from 'ng-primitives/internal';
8
- import { createOverlay, coerceFlip, coerceOffset } from 'ng-primitives/portal';
6
+ import { injectElementRef, domSort, observeResize, scrollIntoViewIfNeeded } from 'ng-primitives/internal';
7
+ import { createPrimitive, controlled, attrBinding, deprecatedSetter, emitter, dataBinding, listener, styleBinding, onDestroy } from 'ng-primitives/state';
8
+ import { provideControlContainerIsolation, createOverlay, coerceFlip, coerceOffset } from 'ng-primitives/portal';
9
9
  import { activeDescendantManager } from 'ng-primitives/a11y';
10
10
 
11
- /**
12
- * The state token for the Select primitive.
13
- */
14
- const NgpNativeSelectStateToken = createStateToken('Select');
15
- /**
16
- * Provides the Select state.
17
- */
18
- const provideNativeSelectState = createStateProvider(NgpNativeSelectStateToken);
19
- /**
20
- * Injects the Select state.
21
- */
22
- const injectNativeSelectState = createStateInjector(NgpNativeSelectStateToken);
23
- /**
24
- * The Select state registration function.
25
- */
26
- const selectNativeSelectState = createState(NgpNativeSelectStateToken);
11
+ const [NgpNativeSelectStateToken, ngpNativeSelect, injectNativeSelectState, provideNativeSelectState,] = createPrimitive('NgpNativeSelect', ({ disabled: _disabled = signal(false), id = signal(uniqueId('ngp-native-select')), }) => {
12
+ const element = injectElementRef();
13
+ const disabled = controlled(_disabled);
14
+ // Setup interactions
15
+ ngpInteractions({
16
+ hover: true,
17
+ press: true,
18
+ focus: true,
19
+ focusVisible: true,
20
+ disabled: disabled,
21
+ });
22
+ ngpFormControl({ id: id, disabled: disabled });
23
+ attrBinding(element, 'disabled', disabled);
24
+ function setDisabled(value) {
25
+ disabled.set(value);
26
+ }
27
+ return {
28
+ disabled: deprecatedSetter(disabled, 'setDisabled'),
29
+ setDisabled,
30
+ };
31
+ });
27
32
 
28
33
  /**
29
34
  * Apply the `ngpNativeSelect` directive to a select element that you want to enhance.
@@ -33,543 +38,149 @@ class NgpNativeSelect {
33
38
  /**
34
39
  * The id of the select. If not provided, a unique id will be generated.
35
40
  */
36
- this.id = input(uniqueId('ngp-native-select'), ...(ngDevMode ? [{ debugName: "id" }] : []));
41
+ this.id = input(uniqueId('ngp-native-select'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
37
42
  /**
38
43
  * Whether the select is disabled.
39
44
  */
40
- this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpNativeSelectDisabled',
41
- transform: booleanAttribute }] : [{
42
- alias: 'ngpNativeSelectDisabled',
43
- transform: booleanAttribute,
44
- }]));
45
- /**
46
- * The select state.
47
- */
48
- this.state = selectNativeSelectState(this);
49
- ngpInteractions({
50
- hover: true,
51
- press: true,
52
- focus: true,
53
- focusVisible: true,
54
- disabled: this.state.disabled,
45
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'ngpNativeSelectDisabled',
46
+ transform: booleanAttribute });
47
+ ngpNativeSelect({
48
+ id: this.id,
49
+ disabled: this.disabled,
55
50
  });
56
- ngpFormControl({ id: this.state.id, disabled: this.state.disabled });
57
51
  }
58
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpNativeSelect, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
59
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpNativeSelect, isStandalone: true, selector: "select[ngpNativeSelect]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpNativeSelectDisabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.disabled": "state.disabled() || null" } }, providers: [provideNativeSelectState()], exportAs: ["ngpNativeSelect"], ngImport: i0 }); }
52
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpNativeSelect, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
53
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: NgpNativeSelect, isStandalone: true, selector: "select[ngpNativeSelect]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpNativeSelectDisabled", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideNativeSelectState()], exportAs: ["ngpNativeSelect"], ngImport: i0 }); }
60
54
  }
61
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpNativeSelect, decorators: [{
55
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpNativeSelect, decorators: [{
62
56
  type: Directive,
63
57
  args: [{
64
58
  selector: 'select[ngpNativeSelect]',
65
59
  exportAs: 'ngpNativeSelect',
66
60
  providers: [provideNativeSelectState()],
67
- host: {
68
- '[attr.disabled]': 'state.disabled() || null',
69
- },
70
61
  }]
71
62
  }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpNativeSelectDisabled", required: false }] }] } });
72
63
 
73
- /**
74
- * The state token for the Select primitive.
75
- */
76
- const NgpSelectStateToken = createStateToken('Select');
77
- /**
78
- * Provides the Select state.
79
- */
80
- const provideSelectState = createStateProvider(NgpSelectStateToken);
81
- /**
82
- * Injects the Select state.
83
- */
84
- const injectSelectState = createStateInjector(NgpSelectStateToken);
85
- /**
86
- * The Select state registration function.
87
- */
88
- const selectState = createState(NgpSelectStateToken);
89
-
90
- class NgpSelectDropdown {
91
- constructor() {
92
- /** Access the select state. */
93
- this.state = injectSelectState();
94
- /** The dimensions of the select. */
95
- this.selectDimensions = observeResize(() => this.state().elementRef.nativeElement);
96
- /**
97
- * Access the element reference.
98
- * @internal
99
- */
100
- this.elementRef = injectElementRef();
101
- /** The id of the dropdown. */
102
- this.id = input(uniqueId('ngp-select-dropdown'), ...(ngDevMode ? [{ debugName: "id" }] : []));
103
- this.state().registerDropdown(this);
104
- }
105
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelectDropdown, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
106
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpSelectDropdown, isStandalone: true, selector: "[ngpSelectDropdown]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listbox" }, properties: { "id": "id()", "style.left.px": "state().overlay()?.position()?.x", "style.top.px": "state().overlay()?.position()?.y", "style.--ngp-select-transform-origin": "state().overlay()?.transformOrigin()", "style.--ngp-select-available-width.px": "state().overlay()?.availableWidth()", "style.--ngp-select-available-height.px": "state().overlay()?.availableHeight()", "style.--ngp-select-width.px": "selectDimensions().width" } }, exportAs: ["ngpSelectDropdown"], ngImport: i0 }); }
107
- }
108
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelectDropdown, decorators: [{
109
- type: Directive,
110
- args: [{
111
- selector: '[ngpSelectDropdown]',
112
- exportAs: 'ngpSelectDropdown',
113
- host: {
114
- role: 'listbox',
115
- '[id]': 'id()',
116
- '[style.left.px]': 'state().overlay()?.position()?.x',
117
- '[style.top.px]': 'state().overlay()?.position()?.y',
118
- '[style.--ngp-select-transform-origin]': 'state().overlay()?.transformOrigin()',
119
- '[style.--ngp-select-available-width.px]': 'state().overlay()?.availableWidth()',
120
- '[style.--ngp-select-available-height.px]': 'state().overlay()?.availableHeight()',
121
- '[style.--ngp-select-width.px]': 'selectDimensions().width',
122
- },
123
- }]
124
- }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
125
-
126
- class NgpSelectOption {
127
- constructor() {
128
- /** Access the select state. */
129
- this.state = injectSelectState();
130
- /**
131
- * The element reference of the option.
132
- * @internal
133
- */
134
- this.elementRef = injectElementRef();
135
- /** The id of the option. */
136
- this.id = input(uniqueId('ngp-select-option'), ...(ngDevMode ? [{ debugName: "id" }] : []));
137
- /** @required The value of the option. */
138
- this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value", alias: 'ngpSelectOptionValue' }] : [{
139
- alias: 'ngpSelectOptionValue',
140
- }]));
141
- /** The disabled state of the option. */
142
- this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpSelectOptionDisabled',
143
- transform: booleanAttribute }] : [{
144
- alias: 'ngpSelectOptionDisabled',
145
- transform: booleanAttribute,
146
- }]));
147
- /** The index of the option in the list. */
148
- this.index = input(undefined, ...(ngDevMode ? [{ debugName: "index", alias: 'ngpSelectOptionIndex' }] : [{
149
- alias: 'ngpSelectOptionIndex',
150
- }]));
151
- /**
152
- * Event emitted when the option is activated via click or keyboard.
153
- * This is useful for options without values that need custom behavior.
154
- */
155
- this.activated = output({
156
- alias: 'ngpSelectOptionActivated',
157
- });
158
- /**
159
- * Whether this option is the active descendant.
160
- * @internal
161
- */
162
- this.active = computed(() => {
163
- // if the option has an index, use that to determine if it's active because this
164
- // is required for virtual scrolling scenarios
165
- const index = this.index();
166
- if (index !== undefined) {
167
- return this.state().activeDescendantManager.index() === index;
168
- }
169
- return this.state().activeDescendantManager.id() === this.id();
170
- }, ...(ngDevMode ? [{ debugName: "active" }] : []));
171
- /** Whether this option is selected. */
172
- this.selected = computed(() => {
173
- const value = this.value();
174
- const stateValue = this.state().value();
175
- // Only treat `undefined` as "no value" (allow '', 0, false).
176
- if (value === undefined) {
177
- return false;
178
- }
179
- if (this.state().multiple()) {
180
- return (Array.isArray(stateValue) && stateValue.some(v => this.state().compareWith()(value, v)));
181
- }
182
- // Only treat `undefined` as "no selection" (allow '', 0, false).
183
- if (stateValue === undefined) {
184
- return false;
64
+ const [NgpSelectStateToken, ngpSelect, _injectSelectState, provideSelectState] = createPrimitive('NgpSelect', ({ id = signal(uniqueId('ngp-select')), value: _value = signal(undefined), multiple = signal(false), disabled: _disabled = signal(false), compareWith = signal(Object.is), placement = signal('bottom'), container = signal('body'), flip = signal(true), offset = signal(0), scrollToOption = signal(undefined), allOptions = signal(undefined), onValueChange, onOpenChange, }) => {
65
+ const elementRef = injectElementRef();
66
+ const value = controlled(_value);
67
+ const disabled = controlled(_disabled, false);
68
+ const valueChangeEmitter = emitter();
69
+ function setValue(newValue, options) {
70
+ value.set(newValue);
71
+ if (options?.emit !== false) {
72
+ onValueChange?.(newValue);
73
+ valueChangeEmitter.emit(newValue);
74
+ }
75
+ }
76
+ function setDisabled(isDisabled) {
77
+ disabled.set(isDisabled);
78
+ }
79
+ ngpInteractions({
80
+ focus: true,
81
+ focusWithin: true,
82
+ hover: true,
83
+ press: true,
84
+ disabled,
85
+ });
86
+ ngpFormControl({ id, disabled });
87
+ const portal = signal(undefined, ...(ngDevMode ? [{ debugName: "portal" }] : /* istanbul ignore next */ []));
88
+ const dropdown = signal(undefined, ...(ngDevMode ? [{ debugName: "dropdown" }] : /* istanbul ignore next */ []));
89
+ const options = signal([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
90
+ const overlay = computed(() => portal()?.overlay(), ...(ngDevMode ? [{ debugName: "overlay" }] : /* istanbul ignore next */ []));
91
+ const open = computed(() => overlay()?.isOpen() ?? false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
92
+ const sortedOptions = computed(() => domSort(options(), option => option.elementRef.nativeElement, option => option.index?.()), ...(ngDevMode ? [{ debugName: "sortedOptions" }] : /* istanbul ignore next */ []));
93
+ const activeDescendantManagerInstance = activeDescendantManager({
94
+ // we must wrap the signal in a computed to ensure it is not used before it is defined
95
+ disabled: computed(() => disabled()),
96
+ wrap: signal(true),
97
+ count: computed(() => allOptions()?.length ?? options().length),
98
+ getItemId: index => getOptionAtIndex(index)?.id(),
99
+ isItemDisabled: index => getOptionAtIndex(index)?.disabled() ?? false,
100
+ scrollIntoView: index => {
101
+ const isPositioned = portal()?.overlay()?.isPositioned() ?? false;
102
+ if (!isPositioned || index === -1) {
103
+ return;
185
104
  }
186
- return this.state().compareWith()(value, stateValue);
187
- }, ...(ngDevMode ? [{ debugName: "selected" }] : []));
188
- this.state().registerOption(this);
189
- ngpInteractions({
190
- hover: true,
191
- press: true,
192
- disabled: this.disabled,
193
- });
194
- }
195
- ngOnDestroy() {
196
- this.state().unregisterOption(this);
197
- }
198
- /**
199
- * Select the option.
200
- * @internal
201
- */
202
- select() {
203
- if (this.disabled()) {
204
- return;
205
- }
206
- this.activated.emit();
207
- this.state().toggleOption(this.id());
208
- }
209
- /**
210
- * Scroll the option into view.
211
- * @internal
212
- */
213
- scrollIntoView() {
214
- scrollIntoViewIfNeeded(this.elementRef.nativeElement);
215
- }
216
- /**
217
- * Whenever the pointer enters the option, activate it.
218
- * @internal
219
- */
220
- onPointerEnter() {
221
- // if we have a known index, use that to activate the option because this
222
- // is required for virtual scrolling scenarios
223
- const index = this.index();
224
- if (index !== undefined) {
225
- this.state().activeDescendantManager.activateByIndex(index, {
226
- scroll: false,
227
- origin: 'pointer',
228
- });
229
- return;
230
- }
231
- // otherwise, activate by id
232
- this.state().activeDescendantManager.activateById(this.id(), {
233
- scroll: false,
234
- origin: 'pointer',
235
- });
236
- }
237
- /**
238
- * Whenever the pointer leaves the option, deactivate it.
239
- * @internal
240
- */
241
- onPointerLeave() {
242
- if (this.state().activeDescendantManager.id() === this.id()) {
243
- this.state().activeDescendantManager.reset({ origin: 'pointer' });
244
- }
245
- }
246
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelectOption, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
247
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpSelectOption, isStandalone: true, selector: "[ngpSelectOption]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "ngpSelectOptionValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpSelectOptionDisabled", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "ngpSelectOptionIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activated: "ngpSelectOptionActivated" }, host: { attributes: { "role": "option" }, listeners: { "click": "select()", "pointerenter": "onPointerEnter()", "pointerleave": "onPointerLeave()" }, properties: { "id": "id()", "attr.tabindex": "-1", "attr.aria-selected": "selected() ? \"true\" : undefined", "attr.data-selected": "selected() ? \"\" : undefined", "attr.data-active": "active() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, exportAs: ["ngpSelectOption"], ngImport: i0 }); }
248
- }
249
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelectOption, decorators: [{
250
- type: Directive,
251
- args: [{
252
- selector: '[ngpSelectOption]',
253
- exportAs: 'ngpSelectOption',
254
- host: {
255
- role: 'option',
256
- '[id]': 'id()',
257
- '[attr.tabindex]': '-1',
258
- '[attr.aria-selected]': 'selected() ? "true" : undefined',
259
- '[attr.data-selected]': 'selected() ? "" : undefined',
260
- '[attr.data-active]': 'active() ? "" : undefined',
261
- '[attr.data-disabled]': 'disabled() ? "" : undefined',
262
- '(click)': 'select()',
263
- },
264
- }]
265
- }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptionValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptionDisabled", required: false }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptionIndex", required: false }] }], activated: [{ type: i0.Output, args: ["ngpSelectOptionActivated"] }], onPointerEnter: [{
266
- type: HostListener,
267
- args: ['pointerenter']
268
- }], onPointerLeave: [{
269
- type: HostListener,
270
- args: ['pointerleave']
271
- }] } });
272
-
273
- class NgpSelectPortal {
274
- constructor() {
275
- /** Access the select state. */
276
- this.state = injectSelectState();
277
- /** Access the view container reference. */
278
- this.viewContainerRef = inject(ViewContainerRef);
279
- /** Access the template reference. */
280
- this.templateRef = inject(TemplateRef);
281
- /** Access the injector. */
282
- this.injector = inject(Injector);
283
- /**
284
- * The overlay that manages the popover
285
- * @internal
286
- */
287
- this.overlay = signal(null, ...(ngDevMode ? [{ debugName: "overlay" }] : []));
288
- this.state().registerPortal(this);
289
- }
290
- /** Cleanup the portal. */
291
- ngOnDestroy() {
292
- this.overlay()?.destroy();
293
- }
294
- /**
295
- * Attach the portal.
296
- * @internal
297
- */
298
- show() {
299
- // Create the overlay if it doesn't exist yet
300
- if (!this.overlay()) {
301
- this.createOverlay();
302
- }
303
- // Show the overlay
304
- return this.overlay().show();
305
- }
306
- /**
307
- * Detach the portal.
308
- * @internal
309
- */
310
- async detach() {
311
- this.overlay()?.hide();
312
- }
313
- /**
314
- * Create the overlay that will contain the dropdown
315
- */
316
- createOverlay() {
317
- // Create config for the overlay
318
- const config = {
319
- content: this.templateRef,
320
- viewContainerRef: this.viewContainerRef,
321
- triggerElement: this.state().elementRef.nativeElement,
322
- injector: this.injector,
323
- placement: this.state().placement,
324
- offset: this.state().offset(),
325
- flip: this.state().flip(),
326
- closeOnOutsideClick: true,
327
- closeOnEscape: true,
328
- restoreFocus: false,
329
- scrollBehaviour: 'reposition',
330
- container: this.state().container(),
331
- onClose: () => this.state().onOverlayClosed(),
332
- };
333
- this.overlay.set(createOverlay(config));
334
- }
335
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelectPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
336
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.9", type: NgpSelectPortal, isStandalone: true, selector: "[ngpSelectPortal]", exportAs: ["ngpSelectPortal"], ngImport: i0 }); }
337
- }
338
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelectPortal, decorators: [{
339
- type: Directive,
340
- args: [{
341
- selector: '[ngpSelectPortal]',
342
- exportAs: 'ngpSelectPortal',
343
- }]
344
- }], ctorParameters: () => [] });
345
-
346
- const defaultSelectConfig = {
347
- placement: 'bottom',
348
- container: 'body',
349
- flip: true,
350
- offset: 0,
351
- };
352
- const NgpSelectConfigToken = new InjectionToken('NgpSelectConfigToken');
353
- /**
354
- * Provide the default Select configuration
355
- * @param config The Select configuration
356
- * @returns The provider
357
- */
358
- function provideSelectConfig(config) {
359
- return [
360
- {
361
- provide: NgpSelectConfigToken,
362
- useValue: { ...defaultSelectConfig, ...config },
105
+ scrollTo(index);
363
106
  },
364
- ];
365
- }
366
- /**
367
- * Inject the Select configuration
368
- * @returns The global Select configuration
369
- */
370
- function injectSelectConfig() {
371
- return inject(NgpSelectConfigToken, { optional: true }) ?? defaultSelectConfig;
372
- }
373
-
374
- class NgpSelect {
375
- constructor() {
376
- /** Access the select configuration. */
377
- this.config = injectSelectConfig();
378
- /** @internal Access the select element. */
379
- this.elementRef = injectElementRef();
380
- /** Access the injector. */
381
- this.injector = inject(Injector);
382
- /** The unique id of the select. */
383
- this.id = input(uniqueId('ngp-select'), ...(ngDevMode ? [{ debugName: "id" }] : []));
384
- /** The value of the select. */
385
- this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value", alias: 'ngpSelectValue' }] : [{
386
- alias: 'ngpSelectValue',
387
- }]));
388
- /** Event emitted when the value changes. */
389
- this.valueChange = output({
390
- alias: 'ngpSelectValueChange',
391
- });
392
- /** Whether the select is multiple selection. */
393
- this.multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple", alias: 'ngpSelectMultiple',
394
- transform: booleanAttribute }] : [{
395
- alias: 'ngpSelectMultiple',
396
- transform: booleanAttribute,
397
- }]));
398
- /** Whether the select is disabled. */
399
- this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpSelectDisabled',
400
- transform: booleanAttribute }] : [{
401
- alias: 'ngpSelectDisabled',
402
- transform: booleanAttribute,
403
- }]));
404
- /** Emit when the dropdown open state changes. */
405
- this.openChange = output({
406
- alias: 'ngpSelectOpenChange',
407
- });
408
- /** The comparator function used to compare options. */
409
- this.compareWith = input(Object.is, ...(ngDevMode ? [{ debugName: "compareWith", alias: 'ngpSelectCompareWith' }] : [{
410
- alias: 'ngpSelectCompareWith',
411
- }]));
412
- /** The position of the dropdown. */
413
- this.placement = input(this.config.placement, ...(ngDevMode ? [{ debugName: "placement", alias: 'ngpSelectDropdownPlacement' }] : [{
414
- alias: 'ngpSelectDropdownPlacement',
415
- }]));
416
- /** The container for the dropdown. */
417
- this.container = input(this.config.container, ...(ngDevMode ? [{ debugName: "container", alias: 'ngpSelectDropdownContainer' }] : [{
418
- alias: 'ngpSelectDropdownContainer',
419
- }]));
420
- /** Whether the dropdown should flip when there is not enough space. Can be a boolean to enable/disable, or an object with padding and fallbackPlacements options. */
421
- this.flip = input(this.config.flip, ...(ngDevMode ? [{ debugName: "flip", alias: 'ngpSelectDropdownFlip',
422
- transform: coerceFlip }] : [{
423
- alias: 'ngpSelectDropdownFlip',
424
- transform: coerceFlip,
425
- }]));
426
- /**
427
- * Define the offset of the select dropdown relative to the trigger.
428
- * Can be a number (applies to mainAxis) or an object with mainAxis, crossAxis, and alignmentAxis.
429
- * @default 0
430
- */
431
- this.offset = input(this.config.offset, ...(ngDevMode ? [{ debugName: "offset", alias: 'ngpSelectDropdownOffset',
432
- transform: coerceOffset }] : [{
433
- alias: 'ngpSelectDropdownOffset',
434
- transform: coerceOffset,
435
- }]));
436
- /**
437
- * A function that will scroll the active option into view. This can be overridden
438
- * for cases such as virtual scrolling where we cannot scroll the option directly because
439
- * it may not be rendered.
440
- */
441
- this.scrollToOption = input(undefined, ...(ngDevMode ? [{ debugName: "scrollToOption", alias: 'ngpSelectScrollToOption' }] : [{
442
- alias: 'ngpSelectScrollToOption',
443
- }]));
444
- /**
445
- * Provide all the option values to the select. This is useful for virtual scrolling scenarios
446
- * where not all options are rendered in the DOM. This is not an alternative to adding the options
447
- * in the DOM, it is only to provide the select with the full list of options. This list should match
448
- * the order of the options as they would appear in the DOM.
449
- */
450
- this.allOptions = input(undefined, ...(ngDevMode ? [{ debugName: "allOptions", alias: 'ngpSelectOptions' }] : [{ alias: 'ngpSelectOptions' }]));
451
- /**
452
- * Store the select portal.
453
- * @internal
454
- */
455
- this.portal = signal(undefined, ...(ngDevMode ? [{ debugName: "portal" }] : []));
456
- /**
457
- * Store the select dropdown.
458
- * @internal
459
- */
460
- this.dropdown = signal(undefined, ...(ngDevMode ? [{ debugName: "dropdown" }] : []));
461
- /**
462
- * Store the select options.
463
- * @internal
464
- */
465
- this.options = signal([], ...(ngDevMode ? [{ debugName: "options" }] : []));
466
- /**
467
- * Access the overlay
468
- * @internal
469
- */
470
- this.overlay = computed(() => this.portal()?.overlay(), ...(ngDevMode ? [{ debugName: "overlay" }] : []));
471
- /**
472
- * The open state of the select.
473
- * @internal
474
- */
475
- this.open = computed(() => this.overlay()?.isOpen() ?? false, ...(ngDevMode ? [{ debugName: "open" }] : []));
476
- /**
477
- * The options sorted by their index or DOM position.
478
- * @internal
479
- */
480
- this.sortedOptions = computed(() => domSort(this.options(), option => option.elementRef.nativeElement, option => option.index?.()), ...(ngDevMode ? [{ debugName: "sortedOptions" }] : []));
481
- /**
482
- * The active key descendant manager.
483
- * @internal
484
- */
485
- this.activeDescendantManager = activeDescendantManager({
486
- // we must wrap the signal in a computed to ensure it is not used before it is defined
487
- disabled: computed(() => this.state.disabled()),
488
- wrap: signal(true),
489
- count: computed(() => this.allOptions()?.length ?? this.options().length),
490
- getItemId: index => this.getOptionAtIndex(index)?.id(),
491
- isItemDisabled: index => this.getOptionAtIndex(index)?.disabled() ?? false,
492
- scrollIntoView: index => {
493
- const isPositioned = this.portal()?.overlay()?.isPositioned() ?? false;
494
- if (!isPositioned || index === -1) {
495
- return;
496
- }
497
- this.scrollTo(index);
498
- },
499
- });
500
- /** The state of the select. */
501
- this.state = selectState(this);
502
- ngpInteractions({
503
- focus: true,
504
- focusWithin: true,
505
- hover: true,
506
- press: true,
507
- disabled: this.state.disabled,
508
- });
509
- ngpFormControl({ id: this.state.id, disabled: this.state.disabled });
510
- }
107
+ });
108
+ // Host bindings
109
+ attrBinding(elementRef, 'role', 'combobox');
110
+ attrBinding(elementRef, 'id', id);
111
+ attrBinding(elementRef, 'aria-expanded', open);
112
+ attrBinding(elementRef, 'aria-controls', () => (open() ? dropdown()?.id() : undefined));
113
+ attrBinding(elementRef, 'aria-activedescendant', () => open() ? activeDescendantManagerInstance.id() : undefined);
114
+ attrBinding(elementRef, 'tabindex', () => (disabled() ? -1 : 0));
115
+ dataBinding(elementRef, 'data-open', () => (open() ? '' : null));
116
+ dataBinding(elementRef, 'data-disabled', () => (disabled() ? '' : null));
117
+ dataBinding(elementRef, 'data-multiple', () => (multiple() ? '' : null));
118
+ // Event listeners
119
+ listener(elementRef, 'click', () => void toggleDropdown());
120
+ listener(elementRef, 'keydown', handleKeydown);
121
+ listener(elementRef, 'blur', onBlur);
511
122
  /**
512
123
  * Open the dropdown.
513
124
  * @internal
514
125
  */
515
- async openDropdown() {
516
- if (this.state.disabled() || this.open()) {
126
+ async function openDropdown() {
127
+ if (disabled() || open()) {
517
128
  return;
518
129
  }
519
- this.openChange.emit(true);
520
- await this.portal()?.show();
130
+ onOpenChange?.(true);
131
+ await portal()?.show();
521
132
  let selectedOptionIdx = -1;
522
133
  // if we have been provided with allOptions, we need to find the selected option(s) from that list
523
- if (this.state.allOptions()) {
524
- selectedOptionIdx = this.state
525
- .allOptions()
526
- .findIndex(option => this.isOptionSelected(option));
134
+ if (allOptions()) {
135
+ selectedOptionIdx = allOptions().findIndex(option => isOptionSelected(option));
527
136
  }
528
137
  // if we don't have allOptions, find the selected option(s) from the registered options
529
138
  if (selectedOptionIdx === -1) {
530
139
  // if there is a selected option(s), set the active descendant to the first selected option
531
- selectedOptionIdx = this.sortedOptions().findIndex(option => this.isOptionSelected(option.value()));
140
+ selectedOptionIdx = sortedOptions().findIndex(option => isOptionSelected(option.value()));
532
141
  }
533
142
  // if after checking there is a selected option, set the active descendant to the first option
534
143
  if (selectedOptionIdx !== -1) {
535
144
  // scroll to and activate the selected option
536
- this.scrollTo(selectedOptionIdx);
537
- this.activeDescendantManager.activateByIndex(selectedOptionIdx);
145
+ scrollTo(selectedOptionIdx);
146
+ activeDescendantManagerInstance.activateByIndex(selectedOptionIdx);
538
147
  return;
539
148
  }
540
149
  // activate the selected option or the first option
541
- this.activeDescendantManager.first();
150
+ activeDescendantManagerInstance.first();
542
151
  }
543
152
  /**
544
153
  * Close the dropdown.
545
154
  * @internal
546
155
  */
547
- closeDropdown() {
548
- if (!this.open()) {
156
+ function closeDropdown() {
157
+ if (!open()) {
549
158
  return;
550
159
  }
551
- this.portal()?.detach();
160
+ portal()?.hide();
552
161
  }
553
162
  /**
554
- * Handles the dropdown being closed.
555
- * Emits the openChange event and resets the active descendant.
163
+ * Handle the overlay being closed (whether via imperative closeDropdown(),
164
+ * outside click, or Escape). Emits openChange(false) and resets the active
165
+ * descendant. Wired in from select-portal-state.ts via the overlay onClose
166
+ * config — this is the single source of truth for the close-side emit so
167
+ * external close paths (outside click, Escape) also fire openChange.
556
168
  * @internal
557
169
  */
558
- onOverlayClosed() {
559
- this.openChange.emit(false);
560
- // clear the active descendant
561
- this.activeDescendantManager.reset();
170
+ function onOverlayClose() {
171
+ onOpenChange?.(false);
172
+ activeDescendantManagerInstance.reset();
562
173
  }
563
174
  /**
564
175
  * Toggle the dropdown.
565
176
  * @internal
566
177
  */
567
- async toggleDropdown() {
568
- if (this.open()) {
569
- this.closeDropdown();
178
+ async function toggleDropdown() {
179
+ if (open()) {
180
+ closeDropdown();
570
181
  }
571
182
  else {
572
- await this.openDropdown();
183
+ await openDropdown();
573
184
  }
574
185
  }
575
186
  /**
@@ -577,14 +188,14 @@ class NgpSelect {
577
188
  * @param index The index of the option to select.
578
189
  * @internal
579
190
  */
580
- selectOption(id) {
581
- if (this.state.disabled()) {
191
+ function selectOption(id) {
192
+ if (disabled()) {
582
193
  return;
583
194
  }
584
- const option = this.sortedOptions().find(opt => opt.id() === id);
195
+ const option = sortedOptions().find(opt => opt.id() === id);
585
196
  if (!option) {
586
- this.state.value.set(undefined);
587
- this.closeDropdown();
197
+ setValue(undefined, { emit: false });
198
+ closeDropdown();
588
199
  return;
589
200
  }
590
201
  const optionValue = option.value();
@@ -592,21 +203,19 @@ class NgpSelect {
592
203
  if (optionValue === undefined) {
593
204
  return;
594
205
  }
595
- if (this.state.multiple()) {
206
+ if (multiple()) {
596
207
  // if the option is already selected, do nothing
597
- if (this.isOptionSelected(optionValue)) {
208
+ if (isOptionSelected(optionValue)) {
598
209
  return;
599
210
  }
600
- const value = [...(this.state.value() ?? []), optionValue];
211
+ const newValue = [...(value() ?? []), optionValue];
601
212
  // add the option to the value
602
- this.state.value.set(value);
603
- this.valueChange.emit(value);
213
+ setValue(newValue);
604
214
  }
605
215
  else {
606
- this.state.value.set(optionValue);
607
- this.valueChange.emit(optionValue);
216
+ setValue(optionValue);
608
217
  // close the dropdown on single selection
609
- this.closeDropdown();
218
+ closeDropdown();
610
219
  }
611
220
  }
612
221
  /**
@@ -614,7 +223,7 @@ class NgpSelect {
614
223
  * @param option The option to deselect.
615
224
  * @internal
616
225
  */
617
- deselectOption(option) {
226
+ function deselectOption(option) {
618
227
  const optionValue = option.value();
619
228
  // Options without values cannot be deselected (and should never be selected).
620
229
  if (optionValue === undefined) {
@@ -622,25 +231,24 @@ class NgpSelect {
622
231
  }
623
232
  // if the select is disabled or the option is not selected, do nothing
624
233
  // if the select is single selection, we don't allow deselecting
625
- if (this.state.disabled() || !this.isOptionSelected(optionValue) || !this.state.multiple()) {
234
+ if (disabled() || !isOptionSelected(optionValue) || !multiple()) {
626
235
  return;
627
236
  }
628
- const values = this.state.value() ?? [];
629
- const newValue = values.filter(v => !this.state.compareWith()(v, optionValue));
237
+ const values = value() ?? [];
238
+ const newValue = values.filter(v => !compareWith()(v, optionValue));
630
239
  // remove the option from the value
631
- this.state.value.set(newValue);
632
- this.valueChange.emit(newValue);
240
+ setValue(newValue);
633
241
  }
634
242
  /**
635
243
  * Toggle the selection of an option.
636
244
  * @param id The id of the option to toggle.
637
245
  * @internal
638
246
  */
639
- toggleOption(id) {
640
- if (this.state.disabled()) {
247
+ function toggleOption(id) {
248
+ if (disabled()) {
641
249
  return;
642
250
  }
643
- const option = this.sortedOptions().find(opt => opt.id() === id);
251
+ const option = sortedOptions().find(opt => opt.id() === id);
644
252
  if (!option) {
645
253
  return;
646
254
  }
@@ -650,17 +258,17 @@ class NgpSelect {
650
258
  return;
651
259
  }
652
260
  // if the state is single selection, we don't allow toggling
653
- if (!this.state.multiple()) {
261
+ if (!multiple()) {
654
262
  // always select the option in single selection mode even if it is already selected so that we update the input
655
- this.selectOption(id);
263
+ selectOption(id);
656
264
  return;
657
265
  }
658
266
  // otherwise toggle the option
659
- if (this.isOptionSelected(optionValue)) {
660
- this.deselectOption(option);
267
+ if (isOptionSelected(optionValue)) {
268
+ deselectOption(option);
661
269
  }
662
270
  else {
663
- this.selectOption(id);
271
+ selectOption(id);
664
272
  }
665
273
  }
666
274
  /**
@@ -668,224 +276,716 @@ class NgpSelect {
668
276
  * @param option The option to check.
669
277
  * @internal
670
278
  */
671
- isOptionSelected(option) {
672
- if (this.state.disabled()) {
279
+ function isOptionSelected(option) {
280
+ if (disabled()) {
673
281
  return false;
674
282
  }
675
- const value = this.state.value();
283
+ const currentValue = value();
676
284
  // Only treat `undefined` as "no selection" (allow '', 0, false).
677
- if (value === undefined) {
285
+ if (currentValue === undefined) {
678
286
  return false;
679
287
  }
680
- if (this.state.multiple()) {
681
- return Array.isArray(value) && value.some(v => this.state.compareWith()(option, v));
288
+ if (multiple()) {
289
+ return Array.isArray(currentValue) && currentValue.some(v => compareWith()(option, v));
682
290
  }
683
- return this.state.compareWith()(option, value);
291
+ return compareWith()(option, currentValue);
684
292
  }
685
293
  /**
686
294
  * Activate the next option in the list if there is one.
687
295
  * If there is no option currently active, activate the selected option or the first option.
688
296
  * @internal
689
297
  */
690
- activateNextOption() {
691
- if (this.state.disabled()) {
298
+ function activateNextOption() {
299
+ if (disabled()) {
692
300
  return;
693
301
  }
694
- const options = this.sortedOptions();
302
+ const currentOptions = sortedOptions();
695
303
  // if there are no options, do nothing
696
- if (options.length === 0) {
304
+ if (currentOptions.length === 0) {
697
305
  return;
698
306
  }
699
307
  // if there is no active option, activate the first option
700
- if (this.activeDescendantManager.index() === -1) {
701
- const selectedOption = options.findIndex(option => this.isOptionSelected(option.value()));
308
+ if (activeDescendantManagerInstance.index() === -1) {
309
+ const selectedOption = currentOptions.findIndex(option => isOptionSelected(option.value()));
702
310
  // if there is a selected option(s), set the active descendant to the first selected option
703
311
  const targetOption = selectedOption !== -1 ? selectedOption : 0;
704
- this.activeDescendantManager.activateByIndex(targetOption, { origin: 'keyboard' });
312
+ activeDescendantManagerInstance.activateByIndex(targetOption, { origin: 'keyboard' });
705
313
  return;
706
314
  }
707
315
  // otherwise activate the next option
708
- this.activeDescendantManager.next({ origin: 'keyboard' });
316
+ activeDescendantManagerInstance.next({ origin: 'keyboard' });
709
317
  }
710
318
  /**
711
319
  * Activate the previous option in the list if there is one.
712
320
  * @internal
713
321
  */
714
- activatePreviousOption() {
715
- if (this.state.disabled()) {
322
+ function activatePreviousOption() {
323
+ if (disabled()) {
716
324
  return;
717
325
  }
718
- const options = this.sortedOptions();
326
+ const currentOptions = sortedOptions();
719
327
  // if there are no options, do nothing
720
- if (options.length === 0) {
328
+ if (currentOptions.length === 0) {
721
329
  return;
722
330
  }
723
331
  // if there is no active option, activate the last option
724
- if (this.activeDescendantManager.index() === -1) {
725
- const selectedOption = options.findIndex(option => this.isOptionSelected(option.value()));
332
+ if (activeDescendantManagerInstance.index() === -1) {
333
+ const selectedOption = currentOptions.findIndex(option => isOptionSelected(option.value()));
726
334
  // if there is a selected option(s), set the active descendant to the first selected option
727
- const targetOption = selectedOption !== -1 ? selectedOption : options.length - 1;
728
- this.activeDescendantManager.activateByIndex(targetOption, { origin: 'keyboard' });
335
+ const targetOption = selectedOption !== -1 ? selectedOption : currentOptions.length - 1;
336
+ activeDescendantManagerInstance.activateByIndex(targetOption, { origin: 'keyboard' });
729
337
  return;
730
338
  }
731
339
  // otherwise activate the previous option
732
- this.activeDescendantManager.previous({ origin: 'keyboard' });
340
+ activeDescendantManagerInstance.previous({ origin: 'keyboard' });
733
341
  }
734
342
  /**
735
343
  * Register the dropdown portal with the select.
736
344
  * @param portal The dropdown portal.
737
345
  * @internal
738
346
  */
739
- registerPortal(portal) {
740
- this.portal.set(portal);
347
+ function registerPortal(portalInstance) {
348
+ portal.set(portalInstance);
741
349
  }
742
350
  /**
743
351
  * Register the dropdown with the select.
744
352
  * @param dropdown The dropdown to register.
745
353
  * @internal
746
354
  */
747
- registerDropdown(dropdown) {
748
- this.dropdown.set(dropdown);
355
+ function registerDropdown(dropdownInstance) {
356
+ dropdown.set(dropdownInstance);
749
357
  }
750
358
  /**
751
359
  * Register an option with the select.
752
360
  * @param option The option to register.
753
361
  * @internal
754
362
  */
755
- registerOption(option) {
756
- this.options.update(options => [...options, option]);
363
+ function registerOption(option) {
364
+ options.update(current => [...current, option]);
757
365
  }
758
366
  /**
759
367
  * Unregister an option from the select.
760
368
  * @param option The option to unregister.
761
369
  * @internal
762
370
  */
763
- unregisterOption(option) {
764
- this.options.update(options => options.filter(o => o !== option));
371
+ function unregisterOption(option) {
372
+ options.update(current => current.filter(o => o !== option));
765
373
  }
766
374
  /**
767
375
  * Focus the select.
768
376
  * @internal
769
377
  */
770
- focus() {
771
- this.elementRef.nativeElement.focus();
378
+ function focus() {
379
+ elementRef.nativeElement.focus();
772
380
  }
773
381
  /** Handle keydown events for accessibility. */
774
- handleKeydown(event) {
382
+ function handleKeydown(event) {
775
383
  switch (event.key) {
776
384
  case 'ArrowDown':
777
- if (this.open()) {
778
- this.activateNextOption();
385
+ if (open()) {
386
+ activateNextOption();
779
387
  }
780
388
  else {
781
- this.openDropdown();
389
+ void openDropdown();
782
390
  }
783
391
  event.preventDefault();
784
392
  break;
785
393
  case 'ArrowUp':
786
- if (this.open()) {
787
- this.activatePreviousOption();
394
+ if (open()) {
395
+ activatePreviousOption();
788
396
  }
789
397
  else {
790
- this.openDropdown();
791
- this.activeDescendantManager.last();
398
+ void openDropdown();
399
+ activeDescendantManagerInstance.last();
792
400
  }
793
401
  event.preventDefault();
794
402
  break;
795
403
  case 'Home':
796
- if (this.open()) {
797
- this.activeDescendantManager.first({ origin: 'keyboard' });
404
+ if (open()) {
405
+ activeDescendantManagerInstance.first({ origin: 'keyboard' });
798
406
  }
799
407
  event.preventDefault();
800
408
  break;
801
409
  case 'End':
802
- if (this.open()) {
803
- this.activeDescendantManager.last({ origin: 'keyboard' });
410
+ if (open()) {
411
+ activeDescendantManagerInstance.last({ origin: 'keyboard' });
804
412
  }
805
413
  event.preventDefault();
806
414
  break;
807
415
  case 'Enter':
808
- if (this.open()) {
809
- const activeId = this.activeDescendantManager.id();
416
+ if (open()) {
417
+ const activeId = activeDescendantManagerInstance.id();
810
418
  if (activeId) {
811
- const option = this.sortedOptions().find(opt => opt.id() === activeId);
419
+ const option = sortedOptions().find(opt => opt.id() === activeId);
812
420
  option?.select();
813
421
  }
814
422
  }
815
423
  else {
816
- this.openDropdown();
424
+ void openDropdown();
817
425
  }
818
426
  event.preventDefault();
819
427
  break;
820
428
  case ' ':
821
- this.toggleDropdown();
429
+ void toggleDropdown();
822
430
  event.preventDefault();
823
431
  break;
824
432
  }
825
433
  }
826
- onBlur(event) {
434
+ function onBlur(event) {
827
435
  const relatedTarget = event.relatedTarget;
828
436
  // if the blur was caused by focus moving to the dropdown, don't close
829
- if (relatedTarget && this.dropdown()?.elementRef.nativeElement.contains(relatedTarget)) {
437
+ if (relatedTarget && dropdown()?.elementRef.nativeElement.contains(relatedTarget)) {
830
438
  return;
831
439
  }
832
- this.closeDropdown();
440
+ closeDropdown();
833
441
  event.preventDefault();
834
442
  }
835
- scrollTo(index) {
836
- const scrollToOption = this.state.scrollToOption();
837
- if (scrollToOption) {
838
- scrollToOption(index);
443
+ function scrollTo(index) {
444
+ const customScrollToOption = scrollToOption();
445
+ if (customScrollToOption) {
446
+ customScrollToOption(index);
839
447
  return;
840
448
  }
841
- const option = this.getOptionAtIndex(index);
449
+ const option = getOptionAtIndex(index);
842
450
  if (option) {
843
451
  option.scrollIntoView();
844
452
  }
845
453
  }
846
- getOptionAtIndex(index) {
454
+ function getOptionAtIndex(index) {
847
455
  // if the option has an index, use that to get the option because this is required for virtual scrolling scenarios
848
- const optionIndex = this.options().findIndex(opt => opt.index?.() === index);
456
+ const optionIndex = options().findIndex(opt => opt.index?.() === index);
849
457
  if (optionIndex !== -1) {
850
- return this.options()[optionIndex];
458
+ return options()[optionIndex];
459
+ }
460
+ return sortedOptions()[index];
461
+ }
462
+ return {
463
+ elementRef,
464
+ id,
465
+ value: deprecatedSetter(value, 'setValue', v => setValue(v)),
466
+ multiple,
467
+ disabled: deprecatedSetter(disabled, 'setDisabled', setDisabled),
468
+ compareWith,
469
+ placement,
470
+ container,
471
+ flip,
472
+ offset,
473
+ scrollToOption,
474
+ allOptions,
475
+ portal,
476
+ dropdown,
477
+ options,
478
+ overlay,
479
+ open,
480
+ sortedOptions,
481
+ activeDescendantManager: activeDescendantManagerInstance,
482
+ openDropdown,
483
+ closeDropdown,
484
+ onOverlayClose,
485
+ toggleDropdown,
486
+ selectOption,
487
+ deselectOption,
488
+ toggleOption,
489
+ isOptionSelected,
490
+ activateNextOption,
491
+ activatePreviousOption,
492
+ setValue,
493
+ setDisabled,
494
+ valueChange: valueChangeEmitter.asObservable(),
495
+ registerPortal,
496
+ registerDropdown,
497
+ registerOption,
498
+ unregisterOption,
499
+ focus,
500
+ };
501
+ });
502
+ function injectSelectState(options) {
503
+ return _injectSelectState(options);
504
+ }
505
+
506
+ const [NgpSelectDropdownStateToken, ngpSelectDropdown, _injectSelectDropdownState, provideSelectDropdownState,] = createPrimitive('NgpSelectDropdown', ({ id: _id = signal(uniqueId('ngp-select-dropdown')) }) => {
507
+ const elementRef = injectElementRef();
508
+ const selectState = injectSelectState();
509
+ const selectDimensions = observeResize(() => selectState().elementRef.nativeElement);
510
+ // Host bindings
511
+ attrBinding(elementRef, 'role', 'listbox');
512
+ attrBinding(elementRef, 'id', _id);
513
+ styleBinding(elementRef, 'left.px', () => selectState().overlay()?.position()?.x ?? null);
514
+ styleBinding(elementRef, 'top.px', () => selectState().overlay()?.position()?.y ?? null);
515
+ styleBinding(elementRef, '--ngp-select-transform-origin', () => selectState().overlay()?.transformOrigin() ?? null);
516
+ styleBinding(elementRef, '--ngp-select-available-width.px', () => selectState().overlay()?.availableWidth() ?? null);
517
+ styleBinding(elementRef, '--ngp-select-available-height.px', () => selectState().overlay()?.availableHeight() ?? null);
518
+ styleBinding(elementRef, '--ngp-select-width.px', () => selectDimensions().width ?? null);
519
+ const state = {
520
+ elementRef,
521
+ id: _id,
522
+ };
523
+ selectState().registerDropdown(state);
524
+ return state;
525
+ });
526
+ function injectSelectDropdownState(options) {
527
+ return _injectSelectDropdownState(options);
528
+ }
529
+
530
+ class NgpSelectDropdown {
531
+ constructor() {
532
+ /** The id of the dropdown. */
533
+ this.id = input(uniqueId('ngp-select-dropdown'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
534
+ ngpSelectDropdown({
535
+ id: this.id,
536
+ });
537
+ }
538
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelectDropdown, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
539
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: NgpSelectDropdown, isStandalone: true, selector: "[ngpSelectDropdown]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideControlContainerIsolation()], exportAs: ["ngpSelectDropdown"], ngImport: i0 }); }
540
+ }
541
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelectDropdown, decorators: [{
542
+ type: Directive,
543
+ args: [{
544
+ selector: '[ngpSelectDropdown]',
545
+ exportAs: 'ngpSelectDropdown',
546
+ providers: [provideControlContainerIsolation()],
547
+ }]
548
+ }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
549
+
550
+ const [NgpSelectOptionStateToken, ngpSelectOption, _injectSelectOptionState, provideSelectOptionState,] = createPrimitive('NgpSelectOption', ({ id = signal(uniqueId('ngp-select-option')), value = signal(undefined), disabled = signal(false), index = signal(undefined), onActivated, }) => {
551
+ const elementRef = injectElementRef();
552
+ const selectState = injectSelectState();
553
+ ngpInteractions({
554
+ hover: true,
555
+ press: true,
556
+ disabled: disabled,
557
+ });
558
+ // Computed states
559
+ const active = computed(() => {
560
+ const idx = index();
561
+ if (idx !== undefined) {
562
+ return selectState().activeDescendantManager.index() === idx;
563
+ }
564
+ return selectState().activeDescendantManager.id() === id();
565
+ }, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
566
+ const selected = computed(() => {
567
+ const val = value();
568
+ const stateValue = selectState().value();
569
+ if (val === undefined) {
570
+ return false;
571
+ }
572
+ if (selectState().multiple()) {
573
+ return (Array.isArray(stateValue) && stateValue.some(v => selectState().compareWith()(val, v)));
574
+ }
575
+ if (stateValue === undefined) {
576
+ return false;
851
577
  }
852
- return this.sortedOptions()[index];
578
+ return selectState().compareWith()(val, stateValue);
579
+ }, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
580
+ // Host bindings
581
+ attrBinding(elementRef, 'role', 'option');
582
+ attrBinding(elementRef, 'tabindex', -1);
583
+ attrBinding(elementRef, 'id', id);
584
+ attrBinding(elementRef, 'aria-selected', () => (selected() ? 'true' : undefined));
585
+ dataBinding(elementRef, 'data-selected', () => (selected() ? '' : null));
586
+ dataBinding(elementRef, 'data-active', () => (active() ? '' : null));
587
+ dataBinding(elementRef, 'data-disabled', () => (disabled() ? '' : null));
588
+ // Event listeners
589
+ listener(elementRef, 'click', () => select());
590
+ listener(elementRef, 'pointerenter', () => activateOnPointerEnter());
591
+ listener(elementRef, 'pointerleave', () => deactivateOnPointerLeave());
592
+ // Methods
593
+ function select() {
594
+ if (disabled()) {
595
+ return;
596
+ }
597
+ onActivated?.();
598
+ selectState().toggleOption(id());
599
+ }
600
+ function scrollIntoView() {
601
+ scrollIntoViewIfNeeded(elementRef.nativeElement);
602
+ }
603
+ function activateOnPointerEnter() {
604
+ const idx = index();
605
+ if (idx !== undefined) {
606
+ selectState().activeDescendantManager.activateByIndex(idx, {
607
+ scroll: false,
608
+ origin: 'pointer',
609
+ });
610
+ return;
611
+ }
612
+ selectState().activeDescendantManager.activateById(id(), {
613
+ scroll: false,
614
+ origin: 'pointer',
615
+ });
616
+ }
617
+ function deactivateOnPointerLeave() {
618
+ if (selectState().activeDescendantManager.id() === id()) {
619
+ selectState().activeDescendantManager.reset({ origin: 'pointer' });
620
+ }
621
+ }
622
+ const state = {
623
+ elementRef,
624
+ id,
625
+ value,
626
+ disabled,
627
+ index,
628
+ active,
629
+ selected,
630
+ select,
631
+ scrollIntoView,
632
+ activateOnPointerEnter,
633
+ deactivateOnPointerLeave,
634
+ emitActivated: () => onActivated?.(),
635
+ };
636
+ selectState().registerOption(state);
637
+ onDestroy(() => {
638
+ selectState().unregisterOption(state);
639
+ });
640
+ return state;
641
+ });
642
+ function injectSelectOptionState(options) {
643
+ return _injectSelectOptionState(options);
644
+ }
645
+
646
+ class NgpSelectOption {
647
+ constructor() {
648
+ /** Access the select state. */
649
+ this.selectState = injectSelectState();
650
+ /** The id of the option. */
651
+ this.id = input(uniqueId('ngp-select-option'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
652
+ /** @required The value of the option. */
653
+ this.value = input(undefined, { ...(ngDevMode ? { debugName: "value" } : /* istanbul ignore next */ {}), alias: 'ngpSelectOptionValue' });
654
+ /** The disabled state of the option. */
655
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'ngpSelectOptionDisabled',
656
+ transform: booleanAttribute });
657
+ /** The index of the option in the list. */
658
+ this.index = input(undefined, { ...(ngDevMode ? { debugName: "index" } : /* istanbul ignore next */ {}), alias: 'ngpSelectOptionIndex' });
659
+ /**
660
+ * Event emitted when the option is activated via click or keyboard.
661
+ * This is useful for options without values that need custom behavior.
662
+ */
663
+ this.activated = output({
664
+ alias: 'ngpSelectOptionActivated',
665
+ });
666
+ /** Access the select option state */
667
+ this.state = ngpSelectOption({
668
+ id: this.id,
669
+ value: this.value,
670
+ disabled: this.disabled,
671
+ index: this.index,
672
+ onActivated: () => this.activated.emit(),
673
+ });
674
+ }
675
+ /**
676
+ * Select the option.
677
+ * @internal
678
+ */
679
+ select() {
680
+ this.state.select();
681
+ }
682
+ /**
683
+ * Scroll the option into view.
684
+ * @internal
685
+ */
686
+ scrollIntoView() {
687
+ this.state.scrollIntoView();
688
+ }
689
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelectOption, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
690
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: NgpSelectOption, isStandalone: true, selector: "[ngpSelectOption]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "ngpSelectOptionValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpSelectOptionDisabled", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "ngpSelectOptionIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activated: "ngpSelectOptionActivated" }, exportAs: ["ngpSelectOption"], ngImport: i0 }); }
691
+ }
692
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelectOption, decorators: [{
693
+ type: Directive,
694
+ args: [{
695
+ selector: '[ngpSelectOption]',
696
+ exportAs: 'ngpSelectOption',
697
+ }]
698
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptionValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptionDisabled", required: false }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptionIndex", required: false }] }], activated: [{ type: i0.Output, args: ["ngpSelectOptionActivated"] }] } });
699
+
700
+ const [NgpSelectPortalStateToken, ngpSelectPortal, _injectSelectPortalState, provideSelectPortalState,] = createPrimitive('NgpSelectPortal', ({}) => {
701
+ const templateRef = inject(TemplateRef);
702
+ const viewContainerRef = inject(ViewContainerRef);
703
+ const injector = inject(Injector);
704
+ const selectState = injectSelectState();
705
+ const overlay = signal(null, ...(ngDevMode ? [{ debugName: "overlay" }] : /* istanbul ignore next */ []));
706
+ // Methods
707
+ async function show() {
708
+ if (!overlay()) {
709
+ createOverlayInstance();
710
+ }
711
+ return overlay()?.show();
712
+ }
713
+ function hide() {
714
+ overlay()?.hide();
715
+ }
716
+ function destroy() {
717
+ overlay()?.destroy();
718
+ }
719
+ function createOverlayInstance() {
720
+ const overlayConfig = {
721
+ content: templateRef,
722
+ viewContainerRef,
723
+ triggerElement: selectState().elementRef.nativeElement,
724
+ injector,
725
+ placement: selectState().placement,
726
+ offset: selectState().offset(),
727
+ flip: selectState().flip(),
728
+ container: selectState().container(),
729
+ closeOnOutsideClick: true,
730
+ closeOnEscape: true,
731
+ restoreFocus: false,
732
+ scrollBehaviour: 'reposition',
733
+ onClose: () => selectState().onOverlayClose(),
734
+ };
735
+ overlay.set(createOverlay(overlayConfig));
736
+ }
737
+ const state = {
738
+ overlay,
739
+ show,
740
+ hide,
741
+ destroy,
742
+ };
743
+ selectState().registerPortal(state);
744
+ return state;
745
+ });
746
+ function injectSelectPortalState(options) {
747
+ return _injectSelectPortalState(options);
748
+ }
749
+
750
+ class NgpSelectPortal {
751
+ constructor() {
752
+ this.state = ngpSelectPortal({});
753
+ }
754
+ /**
755
+ * Attach the portal.
756
+ * @internal
757
+ */
758
+ show() {
759
+ return this.state.show();
760
+ }
761
+ /**
762
+ * Detach the portal.
763
+ * @internal
764
+ */
765
+ detach() {
766
+ this.state.hide();
767
+ }
768
+ ngOnDestroy() {
769
+ this.state.destroy();
770
+ }
771
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelectPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
772
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.14", type: NgpSelectPortal, isStandalone: true, selector: "[ngpSelectPortal]", exportAs: ["ngpSelectPortal"], ngImport: i0 }); }
773
+ }
774
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelectPortal, decorators: [{
775
+ type: Directive,
776
+ args: [{
777
+ selector: '[ngpSelectPortal]',
778
+ exportAs: 'ngpSelectPortal',
779
+ }]
780
+ }] });
781
+
782
+ const defaultSelectConfig = {
783
+ placement: 'bottom',
784
+ container: 'body',
785
+ flip: true,
786
+ offset: 0,
787
+ };
788
+ const NgpSelectConfigToken = new InjectionToken('NgpSelectConfigToken');
789
+ /**
790
+ * Provide the default Select configuration
791
+ * @param config The Select configuration
792
+ * @returns The provider
793
+ */
794
+ function provideSelectConfig(config) {
795
+ return [
796
+ {
797
+ provide: NgpSelectConfigToken,
798
+ useValue: { ...defaultSelectConfig, ...config },
799
+ },
800
+ ];
801
+ }
802
+ /**
803
+ * Inject the Select configuration
804
+ * @returns The global Select configuration
805
+ */
806
+ function injectSelectConfig() {
807
+ return inject(NgpSelectConfigToken, { optional: true }) ?? defaultSelectConfig;
808
+ }
809
+
810
+ class NgpSelect {
811
+ constructor() {
812
+ /** Access the select configuration. */
813
+ this.config = injectSelectConfig();
814
+ /** The unique id of the select. */
815
+ this.id = input(uniqueId('ngp-select'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
816
+ /** The value of the select. */
817
+ this.value = input(undefined, { ...(ngDevMode ? { debugName: "value" } : /* istanbul ignore next */ {}), alias: 'ngpSelectValue' });
818
+ /** Event emitted when the value changes. */
819
+ this.valueChange = output({
820
+ alias: 'ngpSelectValueChange',
821
+ });
822
+ /** Whether the select is multiple selection. */
823
+ this.multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), alias: 'ngpSelectMultiple',
824
+ transform: booleanAttribute });
825
+ /** Whether the select is disabled. */
826
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'ngpSelectDisabled',
827
+ transform: booleanAttribute });
828
+ /** Emit when the dropdown open state changes. */
829
+ this.openChange = output({
830
+ alias: 'ngpSelectOpenChange',
831
+ });
832
+ /** The comparator function used to compare options. */
833
+ this.compareWith = input(Object.is, { ...(ngDevMode ? { debugName: "compareWith" } : /* istanbul ignore next */ {}), alias: 'ngpSelectCompareWith' });
834
+ /** The position of the dropdown. */
835
+ this.placement = input(this.config.placement, { ...(ngDevMode ? { debugName: "placement" } : /* istanbul ignore next */ {}), alias: 'ngpSelectDropdownPlacement' });
836
+ /** The container for the dropdown. */
837
+ this.container = input(this.config.container, { ...(ngDevMode ? { debugName: "container" } : /* istanbul ignore next */ {}), alias: 'ngpSelectDropdownContainer' });
838
+ /** Whether the dropdown should flip when there is not enough space. Can be a boolean to enable/disable, or an object with padding and fallbackPlacements options. */
839
+ this.flip = input(this.config.flip, { ...(ngDevMode ? { debugName: "flip" } : /* istanbul ignore next */ {}), alias: 'ngpSelectDropdownFlip',
840
+ transform: coerceFlip });
841
+ /**
842
+ * Define the offset of the select dropdown relative to the trigger.
843
+ * Can be a number (applies to mainAxis) or an object with mainAxis, crossAxis, and alignmentAxis.
844
+ * @default 0
845
+ */
846
+ this.offset = input(this.config.offset, { ...(ngDevMode ? { debugName: "offset" } : /* istanbul ignore next */ {}), alias: 'ngpSelectDropdownOffset',
847
+ transform: coerceOffset });
848
+ /**
849
+ * A function that will scroll the active option into view. This can be overridden
850
+ * for cases such as virtual scrolling where we cannot scroll the option directly because
851
+ * it may not be rendered.
852
+ */
853
+ this.scrollToOption = input(undefined, { ...(ngDevMode ? { debugName: "scrollToOption" } : /* istanbul ignore next */ {}), alias: 'ngpSelectScrollToOption' });
854
+ /**
855
+ * Provide all the option values to the select. This is useful for virtual scrolling scenarios
856
+ * where not all options are rendered in the DOM. This is not an alternative to adding the options
857
+ * in the DOM, it is only to provide the select with the full list of options. This list should match
858
+ * the order of the options as they would appear in the DOM.
859
+ */
860
+ this.allOptions = input(undefined, { ...(ngDevMode ? { debugName: "allOptions" } : /* istanbul ignore next */ {}), alias: 'ngpSelectOptions' });
861
+ /** The state of the select. */
862
+ this.state = ngpSelect({
863
+ id: this.id,
864
+ value: this.value,
865
+ multiple: this.multiple,
866
+ disabled: this.disabled,
867
+ compareWith: this.compareWith,
868
+ placement: this.placement,
869
+ container: this.container,
870
+ flip: this.flip,
871
+ offset: this.offset,
872
+ scrollToOption: this.scrollToOption,
873
+ allOptions: this.allOptions,
874
+ onValueChange: value => this.valueChange.emit(value),
875
+ onOpenChange: open => this.openChange.emit(open),
876
+ });
877
+ /** @internal Access the select element. */
878
+ this.elementRef = this.state.elementRef;
879
+ /**
880
+ * Store the select portal.
881
+ * @internal
882
+ */
883
+ this.portal = this.state.portal;
884
+ /**
885
+ * Store the select dropdown.
886
+ * @internal
887
+ */
888
+ this.dropdown = this.state.dropdown;
889
+ /**
890
+ * Store the select options.
891
+ * @internal
892
+ */
893
+ this.options = this.state.options;
894
+ /**
895
+ * Access the overlay
896
+ * @internal
897
+ */
898
+ this.overlay = this.state.overlay;
899
+ /**
900
+ * The open state of the select.
901
+ * @internal
902
+ */
903
+ this.open = this.state.open;
904
+ /**
905
+ * The options sorted by their index or DOM position.
906
+ * @internal
907
+ */
908
+ this.sortedOptions = this.state.sortedOptions;
909
+ /**
910
+ * The active key descendant manager.
911
+ * @internal
912
+ */
913
+ this.activeDescendantManager = this.state.activeDescendantManager;
914
+ }
915
+ /**
916
+ * Open the dropdown.
917
+ * @internal
918
+ */
919
+ openDropdown() {
920
+ return this.state.openDropdown();
921
+ }
922
+ /**
923
+ * Close the dropdown.
924
+ * @internal
925
+ */
926
+ closeDropdown() {
927
+ return this.state.closeDropdown();
928
+ }
929
+ /**
930
+ * Toggle the dropdown.
931
+ * @internal
932
+ */
933
+ toggleDropdown() {
934
+ return this.state.toggleDropdown();
935
+ }
936
+ /**
937
+ * Select an option.
938
+ * @param id The id of the option to select.
939
+ * @internal
940
+ */
941
+ selectOption(id) {
942
+ this.state.selectOption(id);
943
+ }
944
+ /**
945
+ * Determine if an option is selected.
946
+ * @param option The option to check.
947
+ * @internal
948
+ */
949
+ isOptionSelected(option) {
950
+ return this.state.isOptionSelected(option);
951
+ }
952
+ /**
953
+ * Activate the next option in the list if there is one.
954
+ * If there is no option currently active, activate the selected option or the first option.
955
+ * @internal
956
+ */
957
+ activateNextOption() {
958
+ this.state.activateNextOption();
959
+ }
960
+ /**
961
+ * Activate the previous option in the list if there is one.
962
+ * @internal
963
+ */
964
+ activatePreviousOption() {
965
+ this.state.activatePreviousOption();
966
+ }
967
+ /**
968
+ * Focus the select.
969
+ * @internal
970
+ */
971
+ focus() {
972
+ this.state.focus();
853
973
  }
854
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelect, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
855
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpSelect, isStandalone: true, selector: "[ngpSelect]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "ngpSelectValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "ngpSelectMultiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpSelectDisabled", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "ngpSelectCompareWith", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "ngpSelectDropdownPlacement", isSignal: true, isRequired: false, transformFunction: null }, container: { classPropertyName: "container", publicName: "ngpSelectDropdownContainer", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "ngpSelectDropdownFlip", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "ngpSelectDropdownOffset", isSignal: true, isRequired: false, transformFunction: null }, scrollToOption: { classPropertyName: "scrollToOption", publicName: "ngpSelectScrollToOption", isSignal: true, isRequired: false, transformFunction: null }, allOptions: { classPropertyName: "allOptions", publicName: "ngpSelectOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "ngpSelectValueChange", openChange: "ngpSelectOpenChange" }, host: { attributes: { "role": "combobox" }, listeners: { "click": "toggleDropdown()", "keydown": "handleKeydown($event)", "blur": "onBlur($event)" }, properties: { "id": "state.id()", "attr.aria-expanded": "open()", "attr.aria-controls": "open() ? dropdown()?.id() : undefined", "attr.aria-activedescendant": "open() ? activeDescendantManager.id() : undefined", "attr.tabindex": "state.disabled() ? -1 : 0", "attr.data-open": "open() ? \"\" : undefined", "attr.data-disabled": "state.disabled() ? \"\" : undefined", "attr.data-multiple": "state.multiple() ? \"\" : undefined" } }, providers: [provideSelectState()], exportAs: ["ngpSelect"], ngImport: i0 }); }
974
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelect, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
975
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: NgpSelect, isStandalone: true, selector: "[ngpSelect]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "ngpSelectValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "ngpSelectMultiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "ngpSelectDisabled", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "ngpSelectCompareWith", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "ngpSelectDropdownPlacement", isSignal: true, isRequired: false, transformFunction: null }, container: { classPropertyName: "container", publicName: "ngpSelectDropdownContainer", isSignal: true, isRequired: false, transformFunction: null }, flip: { classPropertyName: "flip", publicName: "ngpSelectDropdownFlip", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "ngpSelectDropdownOffset", isSignal: true, isRequired: false, transformFunction: null }, scrollToOption: { classPropertyName: "scrollToOption", publicName: "ngpSelectScrollToOption", isSignal: true, isRequired: false, transformFunction: null }, allOptions: { classPropertyName: "allOptions", publicName: "ngpSelectOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "ngpSelectValueChange", openChange: "ngpSelectOpenChange" }, providers: [provideSelectState()], exportAs: ["ngpSelect"], ngImport: i0 }); }
856
976
  }
857
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpSelect, decorators: [{
977
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: NgpSelect, decorators: [{
858
978
  type: Directive,
859
979
  args: [{
860
980
  selector: '[ngpSelect]',
861
981
  exportAs: 'ngpSelect',
862
982
  providers: [provideSelectState()],
863
- host: {
864
- role: 'combobox',
865
- '[id]': 'state.id()',
866
- '[attr.aria-expanded]': 'open()',
867
- '[attr.aria-controls]': 'open() ? dropdown()?.id() : undefined',
868
- '[attr.aria-activedescendant]': 'open() ? activeDescendantManager.id() : undefined',
869
- '[attr.tabindex]': 'state.disabled() ? -1 : 0',
870
- '[attr.data-open]': 'open() ? "" : undefined',
871
- '[attr.data-disabled]': 'state.disabled() ? "" : undefined',
872
- '[attr.data-multiple]': 'state.multiple() ? "" : undefined',
873
- },
874
983
  }]
875
- }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectValue", required: false }] }], valueChange: [{ type: i0.Output, args: ["ngpSelectValueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectMultiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDisabled", required: false }] }], openChange: [{ type: i0.Output, args: ["ngpSelectOpenChange"] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectCompareWith", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownPlacement", required: false }] }], container: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownContainer", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownFlip", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownOffset", required: false }] }], scrollToOption: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectScrollToOption", required: false }] }], allOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptions", required: false }] }], toggleDropdown: [{
876
- type: HostListener,
877
- args: ['click']
878
- }], handleKeydown: [{
879
- type: HostListener,
880
- args: ['keydown', ['$event']]
881
- }], onBlur: [{
882
- type: HostListener,
883
- args: ['blur', ['$event']]
884
- }] } });
984
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectValue", required: false }] }], valueChange: [{ type: i0.Output, args: ["ngpSelectValueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectMultiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDisabled", required: false }] }], openChange: [{ type: i0.Output, args: ["ngpSelectOpenChange"] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectCompareWith", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownPlacement", required: false }] }], container: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownContainer", required: false }] }], flip: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownFlip", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectDropdownOffset", required: false }] }], scrollToOption: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectScrollToOption", required: false }] }], allOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpSelectOptions", required: false }] }] } });
885
985
 
886
986
  /**
887
987
  * Generated bundle index. Do not edit.
888
988
  */
889
989
 
890
- export { NgpNativeSelect, NgpSelect, NgpSelectDropdown, NgpSelectOption, NgpSelectPortal, injectNativeSelectState, injectSelectConfig, injectSelectState, provideNativeSelectState, provideSelectConfig, provideSelectState };
990
+ export { NgpNativeSelect, NgpSelect, NgpSelectDropdown, NgpSelectDropdownStateToken, NgpSelectOption, NgpSelectOptionStateToken, NgpSelectPortal, NgpSelectPortalStateToken, NgpSelectStateToken, injectNativeSelectState, injectSelectConfig, injectSelectDropdownState, injectSelectOptionState, injectSelectPortalState, injectSelectState, ngpSelect, ngpSelectDropdown, ngpSelectOption, ngpSelectPortal, provideNativeSelectState, provideSelectConfig, provideSelectDropdownState, provideSelectOptionState, provideSelectPortalState, provideSelectState };
891
991
  //# sourceMappingURL=ng-primitives-select.mjs.map