ngx-bsl 0.0.10 → 0.0.13

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.
@@ -1,17 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { ViewEncapsulation, ChangeDetectionStrategy, Component, input, inject, computed, output, contentChildren, ElementRef, signal, forwardRef, HostListener, Pipe, viewChild, effect, afterNextRender, model, Renderer2, DOCUMENT, Directive } from '@angular/core';
2
+ import { ViewEncapsulation, ChangeDetectionStrategy, Component, input, contentChildren, ElementRef, signal, Directive, inject, DestroyRef, output, model, computed, effect, forwardRef, HostListener, afterNextRender, viewChild, Renderer2, DOCUMENT } from '@angular/core';
3
3
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
5
  import { CdkOverlayOrigin, CdkConnectedOverlay, Overlay } from '@angular/cdk/overlay';
6
+ import { Subject } from 'rxjs';
5
7
  import { CdkTrapFocus } from '@angular/cdk/a11y';
6
8
  import { ComponentPortal } from '@angular/cdk/portal';
7
9
 
8
- class IdGenerator {
9
- id = 0;
10
- nextId() {
11
- return this.id++;
12
- }
13
- }
14
-
15
10
  /*
16
11
  eslint-disable max-len,
17
12
  @typescript-eslint/no-extraneous-class
@@ -36,16 +31,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
36
31
  class ListBoxOptionComponent {
37
32
  value = input.required(...(ngDevMode ? [{ debugName: "value" }] : []));
38
33
  id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
39
- optionIdGenerator = inject(IdGenerator);
40
- listBoxOptionId = computed(() => {
41
- const id = this.optionIdGenerator.nextId();
42
- return `${this.id()}-${id}`;
43
- }, ...(ngDevMode ? [{ debugName: "listBoxOptionId" }] : []));
44
34
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxOptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
45
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.0", type: ListBoxOptionComponent, isStandalone: true, selector: "ngx-bsl-list-box-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "option" }, properties: { "attr.id": "listBoxOptionId()" } }, ngImport: i0, template: `
35
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.0", type: ListBoxOptionComponent, isStandalone: true, selector: "ngx-bsl-list-box-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "option" }, properties: { "attr.id": "id()" } }, ngImport: i0, template: `
46
36
  <ng-content></ng-content>
47
37
  <ngx-bsl-icon-check></ngx-bsl-icon-check>
48
- `, isInline: true, styles: ["ngx-bsl-list-box-option{position:relative;padding:.375rem 1.25rem .375rem .5rem;border-radius:4px;font:var(--bsl-font-sm);color:var(--bsl-textColor);cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ngx-bsl-list-box-option:hover,ngx-bsl-list-box-option.visual-focus{background-color:var(--bsl-list-box-backgroundColor-hover)}ngx-bsl-list-box-option[aria-selected=true] ngx-bsl-icon-check{display:inline;visibility:visible}ngx-bsl-list-box-option ngx-bsl-icon-check{display:none;visibility:hidden;position:absolute;right:0;color:var(--bsl-textColor)}\n"], dependencies: [{ kind: "component", type: IconCheckComponent, selector: "ngx-bsl-icon-check" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
38
+ `, isInline: true, styles: ["ngx-bsl-list-box-option{position:relative;padding:.375rem 1.25rem .375rem 1rem;border-radius:4px;font:var(--bsl-font-sm);color:var(--bsl-textColor);cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ngx-bsl-list-box-option:hover,ngx-bsl-list-box-option.visual-focus{background-color:var(--bsl-list-box-backgroundColor-hover)}ngx-bsl-list-box-option[aria-selected=true] ngx-bsl-icon-check{display:inline;visibility:visible}ngx-bsl-list-box-option ngx-bsl-icon-check{display:none;visibility:hidden;position:absolute;right:0;color:var(--bsl-textColor)}\n"], dependencies: [{ kind: "component", type: IconCheckComponent, selector: "ngx-bsl-icon-check" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
49
39
  }
50
40
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxOptionComponent, decorators: [{
51
41
  type: Component,
@@ -56,33 +46,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
56
46
  <ngx-bsl-icon-check></ngx-bsl-icon-check>
57
47
  `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
58
48
  'role': 'option',
59
- '[attr.id]': 'listBoxOptionId()',
60
- }, styles: ["ngx-bsl-list-box-option{position:relative;padding:.375rem 1.25rem .375rem .5rem;border-radius:4px;font:var(--bsl-font-sm);color:var(--bsl-textColor);cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ngx-bsl-list-box-option:hover,ngx-bsl-list-box-option.visual-focus{background-color:var(--bsl-list-box-backgroundColor-hover)}ngx-bsl-list-box-option[aria-selected=true] ngx-bsl-icon-check{display:inline;visibility:visible}ngx-bsl-list-box-option ngx-bsl-icon-check{display:none;visibility:hidden;position:absolute;right:0;color:var(--bsl-textColor)}\n"] }]
49
+ '[attr.id]': 'id()',
50
+ }, styles: ["ngx-bsl-list-box-option{position:relative;padding:.375rem 1.25rem .375rem 1rem;border-radius:4px;font:var(--bsl-font-sm);color:var(--bsl-textColor);cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ngx-bsl-list-box-option:hover,ngx-bsl-list-box-option.visual-focus{background-color:var(--bsl-list-box-backgroundColor-hover)}ngx-bsl-list-box-option[aria-selected=true] ngx-bsl-icon-check{display:inline;visibility:visible}ngx-bsl-list-box-option ngx-bsl-icon-check{display:none;visibility:hidden;position:absolute;right:0;color:var(--bsl-textColor)}\n"] }]
61
51
  }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }] } });
62
52
 
63
- class ListBoxComponent {
64
- id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
65
- ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : []));
66
- ariaLabelledby = input(...(ngDevMode ? [undefined, { debugName: "ariaLabelledby" }] : []));
67
- exposeAriaActiveDescendant = input(false, ...(ngDevMode ? [{ debugName: "exposeAriaActiveDescendant" }] : []));
68
- comparisonField = input(...(ngDevMode ? [undefined, { debugName: "comparisonField" }] : []));
69
- initialValue = input(...(ngDevMode ? [undefined, { debugName: "initialValue" }] : []));
70
- initialFocusedOptionIndex = input(null, ...(ngDevMode ? [{ debugName: "initialFocusedOptionIndex" }] : []));
71
- selectOption = output();
72
- listBoxOptions = contentChildren((ListBoxOptionComponent), ...(ngDevMode ? [{ debugName: "listBoxOptions" }] : []));
53
+ class ListBoxDirective {
54
+ listBoxId = input(...(ngDevMode ? [undefined, { debugName: "listBoxId" }] : []));
55
+ listBoxAriaLabel = input(...(ngDevMode ? [undefined, { debugName: "listBoxAriaLabel" }] : []));
56
+ listBoxAriaLabelledby = input(...(ngDevMode ? [undefined, { debugName: "listBoxAriaLabelledby" }] : []));
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ optionValueEquality = input((value, optionValue) => value === optionValue, ...(ngDevMode ? [{ debugName: "optionValueEquality" }] : []));
59
+ listBoxOptions = contentChildren((ListBoxOptionComponent), ...(ngDevMode ? [{ debugName: "listBoxOptions", descendants: true }] : [{ descendants: true }]));
73
60
  listBoxOptionRefs = contentChildren(ListBoxOptionComponent, ...(ngDevMode ? [{ debugName: "listBoxOptionRefs", descendants: true, read: (ElementRef) }] : [{ descendants: true, read: (ElementRef) }]));
74
61
  ariaActiveDescendant = signal(null, ...(ngDevMode ? [{ debugName: "ariaActiveDescendant" }] : []));
75
- onChange = (_value) => { };
76
- onTouch = () => { };
77
- value = null;
78
- ngOnInit() {
79
- const initialValue = this.initialValue();
80
- if (initialValue) {
81
- this.value = initialValue;
62
+ selectOption = new Subject();
63
+ hasAriaSelected = true;
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ initSelectedOption(value) {
66
+ const optionIndex = this.listBoxOptions()
67
+ .findIndex(option => this.optionValueEquality()(value, option.value()));
68
+ if (optionIndex !== -1) {
69
+ this.setSelectedAttribute(optionIndex);
70
+ this.setVisualFocus(optionIndex);
71
+ }
72
+ else {
73
+ this.clearVisualFocus();
74
+ this.removeSelectedAttribute();
82
75
  }
83
- }
84
- ngAfterContentInit() {
85
- this.initSelectedOption();
86
76
  }
87
77
  onClick(event) {
88
78
  const element = event.target;
@@ -93,9 +83,7 @@ class ListBoxComponent {
93
83
  const optionIndex = this.listBoxOptionRefs().findIndex(o => o.nativeElement.id === element.id);
94
84
  this.setSelectedAttribute(optionIndex);
95
85
  this.setVisualFocus(optionIndex);
96
- this.value = this.listBoxOptions()[optionIndex].value();
97
- this.onChange(this.value);
98
- this.selectOption.emit(this.value);
86
+ this.selectOption.next(this.listBoxOptions()[optionIndex].value());
99
87
  }
100
88
  }
101
89
  onKeydown(event) {
@@ -110,35 +98,19 @@ class ListBoxComponent {
110
98
  return;
111
99
  }
112
100
  }
113
- initSelectedOption() {
114
- const optionIndex = this.listBoxOptions().findIndex(o => this.equalsToCurrentValue(o.value()));
115
- if (optionIndex !== -1) {
116
- this.setSelectedAttribute(optionIndex);
117
- this.setVisualFocus(optionIndex);
118
- return;
119
- }
120
- if (this.initialFocusedOptionIndex() !== null) {
121
- this.setVisualFocus(this.initialFocusedOptionIndex());
122
- }
123
- }
124
- equalsToCurrentValue(value) {
125
- const comparisonField = this.comparisonField();
126
- return comparisonField && this.value && typeof this.value === 'object' && comparisonField in this.value
127
- ? this.value[comparisonField] === value[comparisonField]
128
- : this.value === value;
129
- }
130
101
  handleSelectionKeys() {
131
- const optionIndex = this.getVisuallyFocusedListBoxOptionRefIndex();
102
+ const optionIndex = this.getVisuallyFocusedOptionRefIndex();
132
103
  if (optionIndex !== -1) {
133
104
  this.removeSelectedAttribute();
134
105
  this.setSelectedAttribute(optionIndex);
135
- this.value = this.listBoxOptions()[optionIndex].value();
136
- this.onChange(this.value);
137
- this.selectOption.emit(this.value);
106
+ this.selectOption.next(this.listBoxOptions()[optionIndex].value());
107
+ }
108
+ else {
109
+ this.selectOption.next(null);
138
110
  }
139
111
  }
140
112
  handleArrowKeys(event) {
141
- const optionIndex = this.getVisuallyFocusedListBoxOptionRefIndex();
113
+ const optionIndex = this.getVisuallyFocusedOptionRefIndex();
142
114
  const firstOption = 0;
143
115
  const lastOption = this.listBoxOptionRefs().length - 1;
144
116
  if (optionIndex === -1) {
@@ -168,7 +140,7 @@ class ListBoxComponent {
168
140
  }
169
141
  }
170
142
  }
171
- getVisuallyFocusedListBoxOptionRefIndex() {
143
+ getVisuallyFocusedOptionRefIndex() {
172
144
  return this.listBoxOptionRefs().findIndex(r => r.nativeElement.classList.contains('visual-focus'));
173
145
  }
174
146
  setVisualFocus(optionIndex) {
@@ -181,150 +153,77 @@ class ListBoxComponent {
181
153
  this.ariaActiveDescendant.set(null);
182
154
  }
183
155
  clearVisualFocus() {
184
- const optionIndex = this.getVisuallyFocusedListBoxOptionRefIndex();
156
+ const optionIndex = this.getVisuallyFocusedOptionRefIndex();
185
157
  if (optionIndex !== -1) {
186
158
  this.removeVisualFocus(optionIndex);
187
159
  }
188
160
  }
189
161
  setSelectedAttribute(optionIndex) {
162
+ if (!this.hasAriaSelected)
163
+ return;
190
164
  this.listBoxOptionRefs()[optionIndex].nativeElement.setAttribute('aria-selected', 'true');
191
165
  }
192
166
  removeSelectedAttribute() {
167
+ if (!this.hasAriaSelected)
168
+ return;
193
169
  this.listBoxOptionRefs()
194
- .find(o => o.nativeElement.hasAttribute('aria-selected'))
195
- ?.nativeElement.removeAttribute('aria-selected');
196
- }
197
- registerOnChange(onChange) {
198
- this.onChange = onChange;
199
- }
200
- registerOnTouched(onTouch) {
201
- this.onTouch = onTouch;
202
- }
203
- writeValue(value) {
204
- this.value = value;
205
- }
206
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
207
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.0", type: ListBoxComponent, isStandalone: true, selector: "ngx-bsl-list-box", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, exposeAriaActiveDescendant: { classPropertyName: "exposeAriaActiveDescendant", publicName: "exposeAriaActiveDescendant", isSignal: true, isRequired: false, transformFunction: null }, comparisonField: { classPropertyName: "comparisonField", publicName: "comparisonField", isSignal: true, isRequired: false, transformFunction: null }, initialValue: { classPropertyName: "initialValue", publicName: "initialValue", isSignal: true, isRequired: false, transformFunction: null }, initialFocusedOptionIndex: { classPropertyName: "initialFocusedOptionIndex", publicName: "initialFocusedOptionIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectOption: "selectOption" }, host: { attributes: { "role": "listbox" }, listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)" }, properties: { "attr.id": "id()", "attr.aria-label": "ariaLabel()", "attr.aria-labelledby": "ariaLabelledby()", "attr.aria-activedescendant": "exposeAriaActiveDescendant() ? null : ariaActiveDescendant()" } }, providers: [
208
- IdGenerator,
209
- {
210
- provide: NG_VALUE_ACCESSOR,
211
- useExisting: forwardRef(() => ListBoxComponent),
212
- multi: true,
213
- },
214
- ], queries: [{ propertyName: "listBoxOptions", predicate: (ListBoxOptionComponent), isSignal: true }, { propertyName: "listBoxOptionRefs", predicate: ListBoxOptionComponent, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["ngx-bsl-list-box{width:100%;display:flex;flex-direction:column;padding:4px;border:1px solid var(--bsl-list-box-borderColor);border-radius:6px;background-color:var(--bsl-list-box-backgroundColor);overflow:hidden}ngx-bsl-list-box .list-box-no-options-message{padding:4px;display:flex;justify-content:center;font:var(--bsl-font-sm);color:var(--bsl-textColor)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
215
- }
216
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxComponent, decorators: [{
217
- type: Component,
218
- args: [{ selector: 'ngx-bsl-list-box', imports: [], template: '<ng-content></ng-content>', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [
219
- IdGenerator,
220
- {
221
- provide: NG_VALUE_ACCESSOR,
222
- useExisting: forwardRef(() => ListBoxComponent),
223
- multi: true,
224
- },
225
- ], host: {
226
- 'role': 'listbox',
227
- '[attr.id]': 'id()',
228
- '[attr.aria-label]': 'ariaLabel()',
229
- '[attr.aria-labelledby]': 'ariaLabelledby()',
230
- '[attr.aria-activedescendant]': 'exposeAriaActiveDescendant() ? null : ariaActiveDescendant()',
231
- }, styles: ["ngx-bsl-list-box{width:100%;display:flex;flex-direction:column;padding:4px;border:1px solid var(--bsl-list-box-borderColor);border-radius:6px;background-color:var(--bsl-list-box-backgroundColor);overflow:hidden}ngx-bsl-list-box .list-box-no-options-message{padding:4px;display:flex;justify-content:center;font:var(--bsl-font-sm);color:var(--bsl-textColor)}\n"] }]
232
- }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], exposeAriaActiveDescendant: [{ type: i0.Input, args: [{ isSignal: true, alias: "exposeAriaActiveDescendant", required: false }] }], comparisonField: [{ type: i0.Input, args: [{ isSignal: true, alias: "comparisonField", required: false }] }], initialValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialValue", required: false }] }], initialFocusedOptionIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialFocusedOptionIndex", required: false }] }], selectOption: [{ type: i0.Output, args: ["selectOption"] }], listBoxOptions: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ListBoxOptionComponent), { isSignal: true }] }], listBoxOptionRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ListBoxOptionComponent), { ...{ descendants: true, read: (ElementRef) }, isSignal: true }] }], onClick: [{
233
- type: HostListener,
234
- args: ['click', ['$event']]
235
- }], onKeydown: [{
236
- type: HostListener,
237
- args: ['keydown', ['$event']]
238
- }] } });
239
-
240
- class ListBoxGroupComponent {
241
- title = input(...(ngDevMode ? [undefined, { debugName: "title" }] : []));
242
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
243
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: ListBoxGroupComponent, isStandalone: true, selector: "ngx-bsl-list-box-group", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
244
- @if (title()) {
245
- <div class="group-title">{{ title() }}</div>
246
- }
247
- <ng-content></ng-content>
248
- `, isInline: true, styles: [".group-title{padding:.375rem .5rem;font:var(--bsl-font-xs);color:var(--bsl-textColor-muted);font-weight:500}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
249
- }
250
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxGroupComponent, decorators: [{
251
- type: Component,
252
- args: [{ selector: 'ngx-bsl-list-box-group', imports: [], template: `
253
- @if (title()) {
254
- <div class="group-title">{{ title() }}</div>
255
- }
256
- <ng-content></ng-content>
257
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".group-title{padding:.375rem .5rem;font:var(--bsl-font-xs);color:var(--bsl-textColor-muted);font-weight:500}\n"] }]
258
- }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }] } });
259
-
260
- class ListBoxOptionValueConverterPipe {
261
- transform(value, bindLabel) {
262
- if (!value) {
263
- return '';
264
- }
265
- if (typeof value === 'string') {
266
- return value;
267
- }
268
- if (typeof value === 'number') {
269
- return value.toString();
270
- }
271
- if (typeof value === 'object' && bindLabel) {
272
- if (bindLabel in value) {
273
- return value[bindLabel];
274
- }
275
- throw new Error('Provided label is incorrect');
276
- }
277
- throw new Error('Cannot parse the value');
170
+ .find(o => o.nativeElement.hasAttribute('aria-selected'))?.nativeElement.removeAttribute('aria-selected');
278
171
  }
279
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxOptionValueConverterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
280
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.0", ngImport: i0, type: ListBoxOptionValueConverterPipe, isStandalone: true, name: "listBoxOptionValueConverter" });
172
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
173
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "21.0.0", type: ListBoxDirective, isStandalone: true, selector: "[ngxBslListBox]", inputs: { listBoxId: { classPropertyName: "listBoxId", publicName: "listBoxId", isSignal: true, isRequired: false, transformFunction: null }, listBoxAriaLabel: { classPropertyName: "listBoxAriaLabel", publicName: "listBoxAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, listBoxAriaLabelledby: { classPropertyName: "listBoxAriaLabelledby", publicName: "listBoxAriaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, optionValueEquality: { classPropertyName: "optionValueEquality", publicName: "optionValueEquality", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "listBoxOptions", predicate: (ListBoxOptionComponent), descendants: true, isSignal: true }, { propertyName: "listBoxOptionRefs", predicate: ListBoxOptionComponent, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0 });
281
174
  }
282
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxOptionValueConverterPipe, decorators: [{
283
- type: Pipe,
284
- args: [{ name: 'listBoxOptionValueConverter' }]
285
- }] });
175
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxDirective, decorators: [{
176
+ type: Directive,
177
+ args: [{ selector: '[ngxBslListBox]' }]
178
+ }], propDecorators: { listBoxId: [{ type: i0.Input, args: [{ isSignal: true, alias: "listBoxId", required: false }] }], listBoxAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "listBoxAriaLabel", required: false }] }], listBoxAriaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "listBoxAriaLabelledby", required: false }] }], optionValueEquality: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValueEquality", required: false }] }], listBoxOptions: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ListBoxOptionComponent), { ...{ descendants: true }, isSignal: true }] }], listBoxOptionRefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ListBoxOptionComponent), { ...{ descendants: true, read: (ElementRef) }, isSignal: true }] }] } });
286
179
 
287
180
  class ComboboxComponent {
181
+ listBox = inject((ListBoxDirective));
182
+ destroyRef = inject(DestroyRef);
288
183
  id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
289
- options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
290
- bindLabel = input(...(ngDevMode ? [undefined, { debugName: "bindLabel" }] : []));
291
- groupBy = input(...(ngDevMode ? [undefined, { debugName: "groupBy" }] : []));
292
- comparisonField = input(...(ngDevMode ? [undefined, { debugName: "comparisonField" }] : []));
293
184
  placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
294
185
  ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : []));
295
186
  ariaLabelledBy = input(...(ngDevMode ? [undefined, { debugName: "ariaLabelledBy" }] : []));
187
+ optionValueParse = input((option) => option, ...(ngDevMode ? [{ debugName: "optionValueParse" }] : []));
296
188
  confirmSelection = output();
297
- listBox = viewChild(ListBoxComponent, ...(ngDevMode ? [{ debugName: "listBox" }] : []));
298
189
  onChange = (_value) => { };
299
190
  onTouch = () => { };
300
- value = signal('', ...(ngDevMode ? [{ debugName: "value" }] : []));
191
+ value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
301
192
  open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
302
- ariaActiveDescendant = computed(() => this.listBox()?.ariaActiveDescendant() ?? null, ...(ngDevMode ? [{ debugName: "ariaActiveDescendant" }] : []));
303
- initialFocusedOptionIndex = signal(null, ...(ngDevMode ? [{ debugName: "initialFocusedOptionIndex" }] : []));
304
- groupedOptions = computed(() => {
305
- const groupByKey = this.groupBy();
306
- if (groupByKey) {
307
- return this.groupDataBy(groupByKey, this.options());
308
- }
309
- return [];
310
- }, ...(ngDevMode ? [{ debugName: "groupedOptions" }] : []));
311
- ignoreOptionsUpdate = false;
193
+ ariaActiveDescendant = computed(() => this.listBox.ariaActiveDescendant() ?? null, ...(ngDevMode ? [{ debugName: "ariaActiveDescendant" }] : []));
194
+ optionSelecting = false;
195
+ optionChangedBy = null;
312
196
  constructor() {
313
197
  effect(() => {
314
- if (!this.options().length || this.ignoreOptionsUpdate) {
315
- this.ignoreOptionsUpdate = false;
198
+ const hasOptions = this.listBox.listBoxOptions().length;
199
+ if (this.optionChangedBy === 'input' && hasOptions) {
200
+ this.showListBox();
201
+ }
202
+ else if (this.optionChangedBy === 'selection') {
316
203
  this.hideListBox();
317
204
  }
318
- else {
319
- this.showListBox();
205
+ this.optionChangedBy = null;
206
+ });
207
+ }
208
+ ngOnInit() {
209
+ this.listBox.hasAriaSelected = false;
210
+ this.subscribeSelectOption();
211
+ }
212
+ subscribeSelectOption() {
213
+ this.listBox.selectOption.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(option => {
214
+ if (option) {
215
+ this.optionChangedBy = 'selection';
216
+ this.value.set(this.optionValueParse()(option));
217
+ this.onChange(this.value());
320
218
  }
219
+ this.hideListBox();
321
220
  });
322
221
  }
323
222
  showListBox() {
324
223
  this.open.set(true);
325
224
  }
326
225
  hideListBox() {
327
- this.initialFocusedOptionIndex.set(null);
226
+ this.listBox.clearVisualFocus();
328
227
  this.open.set(false);
329
228
  }
330
229
  onClick() {
@@ -332,53 +231,51 @@ class ComboboxComponent {
332
231
  this.hideListBox();
333
232
  }
334
233
  else {
335
- if (this.options().length) {
234
+ if (this.listBox.listBoxOptions().length) {
336
235
  this.showListBox();
337
236
  }
338
237
  }
339
238
  }
340
- onSelectOption(value) {
341
- let valueToSet;
342
- if (typeof value === 'string') {
343
- valueToSet = value;
344
- }
345
- else {
346
- const comparisonField = this.comparisonField();
347
- if (comparisonField) {
348
- if (typeof value[comparisonField] === 'string') {
349
- valueToSet = value[comparisonField];
350
- }
351
- else {
352
- throw new Error('Type of property pointed by comparisonField must be a string.');
353
- }
354
- }
355
- else {
356
- throw new Error('comparisonField is not provided.');
357
- }
239
+ onBlur() {
240
+ if (!this.optionSelecting) {
241
+ this.hideListBox();
358
242
  }
359
- this.ignoreOptionsUpdate = true;
360
- this.value.set(valueToSet);
361
- this.onChange(this.value());
362
- this.hideListBox();
243
+ }
244
+ onListBoxPointerDown(event) {
245
+ event.preventDefault();
246
+ this.optionSelecting = true;
247
+ }
248
+ onListBoxClick(event) {
249
+ this.listBox.onClick(event);
250
+ this.optionSelecting = false;
363
251
  }
364
252
  onInputChange(event) {
365
253
  const value = event.target.value;
366
254
  this.value.set(value);
255
+ this.optionChangedBy = 'input';
256
+ if (this.listBox.listBoxOptions().length) {
257
+ if (this.open()) {
258
+ this.listBox.initSelectedOption(this.value());
259
+ }
260
+ else {
261
+ this.showListBox();
262
+ }
263
+ }
367
264
  this.onChange(this.value());
368
265
  }
369
266
  onKeydown(event) {
370
267
  event.preventDefault();
371
268
  if (!this.open()) {
372
269
  if (event.code === 'ArrowUp') {
373
- if (!this.options().length)
270
+ if (!this.listBox.listBoxOptions().length)
374
271
  return;
375
- this.initialFocusedOptionIndex.set(this.options().length - 1);
272
+ this.listBox.setVisualFocus(this.listBox.listBoxOptions().length - 1);
376
273
  this.showListBox();
377
274
  }
378
275
  else if (event.code === 'ArrowDown') {
379
- if (!this.options().length)
276
+ if (!this.listBox.listBoxOptions().length)
380
277
  return;
381
- this.initialFocusedOptionIndex.set(0);
278
+ this.listBox.setVisualFocus(0);
382
279
  this.showListBox();
383
280
  }
384
281
  else if (event.code === 'Enter') {
@@ -386,20 +283,9 @@ class ComboboxComponent {
386
283
  }
387
284
  }
388
285
  else {
389
- this.listBox()?.onKeydown(event);
286
+ this.listBox.onKeydown(event);
390
287
  }
391
288
  }
392
- groupDataBy(field, data) {
393
- const groupsMap = data.reduce((map, value) => {
394
- const key = value[field];
395
- if (!map.has(key)) {
396
- map.set(key, []);
397
- }
398
- map.get(key)?.push(value);
399
- return map;
400
- }, new Map);
401
- return Array.from(groupsMap, ([group, data]) => ({ group, data }));
402
- }
403
289
  registerOnChange(onChange) {
404
290
  this.onChange = onChange;
405
291
  }
@@ -410,31 +296,32 @@ class ComboboxComponent {
410
296
  this.value.set(value);
411
297
  }
412
298
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ComboboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
413
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: ComboboxComponent, isStandalone: true, selector: "ngx-bsl-combobox", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, bindLabel: { classPropertyName: "bindLabel", publicName: "bindLabel", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, comparisonField: { classPropertyName: "comparisonField", publicName: "comparisonField", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmSelection: "confirmSelection" }, providers: [
299
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.0", type: ComboboxComponent, isStandalone: true, selector: "ngx-bsl-combobox", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, optionValueParse: { classPropertyName: "optionValueParse", publicName: "optionValueParse", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmSelection: "confirmSelection", value: "valueChange" }, providers: [
414
300
  {
415
301
  provide: NG_VALUE_ACCESSOR,
416
302
  useExisting: forwardRef(() => ComboboxComponent),
417
303
  multi: true,
418
304
  },
419
- ], viewQueries: [{ propertyName: "listBox", first: true, predicate: ListBoxComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<input\n type=\"text\"\n role=\"combobox\"\n #comboboxInput\n cdkOverlayOrigin\n [value]=\"value() | listBoxOptionValueConverter : bindLabel()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"'list-box-' + id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n (input)=\"onInputChange($any($event))\"\n (blur)=\"hideListBox()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\"\n>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"comboboxInput\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"comboboxInput.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <ngx-bsl-list-box\n [initialFocusedOptionIndex]=\"initialFocusedOptionIndex()\"\n [comparisonField]=\"comparisonField()\"\n [id]=\"'list-box-' + id()\"\n [ariaLabel]=\"ariaLabel()\"\n [ariaLabelledby]=\"ariaLabelledBy()\"\n [exposeAriaActiveDescendant]=\"true\"\n (selectOption)=\"onSelectOption($event)\">\n @if (options() && !groupBy()) {\n @for (option of options(); track option; let index = $index) {\n <ngx-bsl-list-box-option\n [id]=\"'list-box-option-' + id()\"\n [value]=\"option\">\n <ng-container>{{ option | listBoxOptionValueConverter : bindLabel() }}</ng-container>\n </ngx-bsl-list-box-option>\n }\n }\n @if (groupedOptions() && groupBy()) {\n @for (group of groupedOptions(); track group) {\n <ngx-bsl-list-box-group [title]=\"group.group\"></ngx-bsl-list-box-group>\n @for (option of group.data; track option; let index = $index) {\n <ngx-bsl-list-box-option\n [id]=\"'list-box-option-' + id()\"\n [value]=\"option\">\n <ng-container>{{ option | listBoxOptionValueConverter : bindLabel() }}</ng-container>\n </ngx-bsl-list-box-option>\n }\n }\n }\n </ngx-bsl-list-box>\n</ng-template>\n", dependencies: [{ kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "component", type: ListBoxComponent, selector: "ngx-bsl-list-box", inputs: ["id", "ariaLabel", "ariaLabelledby", "exposeAriaActiveDescendant", "comparisonField", "initialValue", "initialFocusedOptionIndex"], outputs: ["selectOption"] }, { kind: "component", type: ListBoxOptionComponent, selector: "ngx-bsl-list-box-option", inputs: ["value", "id"] }, { kind: "component", type: ListBoxGroupComponent, selector: "ngx-bsl-list-box-group", inputs: ["title"] }, { kind: "pipe", type: ListBoxOptionValueConverterPipe, name: "listBoxOptionValueConverter" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
305
+ ], hostDirectives: [{ directive: ListBoxDirective, inputs: ["listBoxId", "listBoxId", "listBoxAriaLabel", "listBoxAriaLabel", "listBoxAriaLabelledby", "listBoxAriaLabelledby", "optionValueEquality", "optionValueEquality"] }], ngImport: i0, template: "<input\n type=\"text\"\n role=\"combobox\"\n #comboboxInput\n cdkOverlayOrigin\n [value]=\"value()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n (input)=\"onInputChange($any($event))\"\n (blur)=\"onBlur()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\"\n>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"comboboxInput\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"comboboxInput.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <!-- eslint-disable-next-line\n @angular-eslint/template/interactive-supports-focus,\n @angular-eslint/template/click-events-have-key-events -->\n <div\n role=\"listbox\"\n class=\"ngx-bsl-list-box\"\n [id]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"listBox.listBoxAriaLabel()\"\n [attr.aria-labelledby]=\"listBox.listBoxAriaLabelledby()\"\n [attr.aria-activedescendant]=\"listBox.ariaActiveDescendant()\"\n (pointerdown)=\"onListBoxPointerDown($event)\"\n (click)=\"onListBoxClick($event)\"\n >\n <ng-content></ng-content>\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
420
306
  }
421
307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ComboboxComponent, decorators: [{
422
308
  type: Component,
423
309
  args: [{ selector: 'ngx-bsl-combobox', imports: [
424
310
  CdkOverlayOrigin,
425
311
  CdkConnectedOverlay,
426
- ListBoxComponent,
427
- ListBoxOptionComponent,
428
- ListBoxGroupComponent,
429
- ListBoxOptionValueConverterPipe,
430
312
  ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [
431
313
  {
432
314
  provide: NG_VALUE_ACCESSOR,
433
315
  useExisting: forwardRef(() => ComboboxComponent),
434
316
  multi: true,
435
317
  },
436
- ], template: "<input\n type=\"text\"\n role=\"combobox\"\n #comboboxInput\n cdkOverlayOrigin\n [value]=\"value() | listBoxOptionValueConverter : bindLabel()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"'list-box-' + id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n (input)=\"onInputChange($any($event))\"\n (blur)=\"hideListBox()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\"\n>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"comboboxInput\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"comboboxInput.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <ngx-bsl-list-box\n [initialFocusedOptionIndex]=\"initialFocusedOptionIndex()\"\n [comparisonField]=\"comparisonField()\"\n [id]=\"'list-box-' + id()\"\n [ariaLabel]=\"ariaLabel()\"\n [ariaLabelledby]=\"ariaLabelledBy()\"\n [exposeAriaActiveDescendant]=\"true\"\n (selectOption)=\"onSelectOption($event)\">\n @if (options() && !groupBy()) {\n @for (option of options(); track option; let index = $index) {\n <ngx-bsl-list-box-option\n [id]=\"'list-box-option-' + id()\"\n [value]=\"option\">\n <ng-container>{{ option | listBoxOptionValueConverter : bindLabel() }}</ng-container>\n </ngx-bsl-list-box-option>\n }\n }\n @if (groupedOptions() && groupBy()) {\n @for (group of groupedOptions(); track group) {\n <ngx-bsl-list-box-group [title]=\"group.group\"></ngx-bsl-list-box-group>\n @for (option of group.data; track option; let index = $index) {\n <ngx-bsl-list-box-option\n [id]=\"'list-box-option-' + id()\"\n [value]=\"option\">\n <ng-container>{{ option | listBoxOptionValueConverter : bindLabel() }}</ng-container>\n </ngx-bsl-list-box-option>\n }\n }\n }\n </ngx-bsl-list-box>\n</ng-template>\n" }]
437
- }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], bindLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindLabel", required: false }] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], comparisonField: [{ type: i0.Input, args: [{ isSignal: true, alias: "comparisonField", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], confirmSelection: [{ type: i0.Output, args: ["confirmSelection"] }], listBox: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ListBoxComponent), { isSignal: true }] }] } });
318
+ ], hostDirectives: [
319
+ {
320
+ directive: ListBoxDirective,
321
+ inputs: ['listBoxId', 'listBoxAriaLabel', 'listBoxAriaLabelledby', 'optionValueEquality'],
322
+ },
323
+ ], template: "<input\n type=\"text\"\n role=\"combobox\"\n #comboboxInput\n cdkOverlayOrigin\n [value]=\"value()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n (input)=\"onInputChange($any($event))\"\n (blur)=\"onBlur()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\"\n>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"comboboxInput\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"comboboxInput.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <!-- eslint-disable-next-line\n @angular-eslint/template/interactive-supports-focus,\n @angular-eslint/template/click-events-have-key-events -->\n <div\n role=\"listbox\"\n class=\"ngx-bsl-list-box\"\n [id]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"listBox.listBoxAriaLabel()\"\n [attr.aria-labelledby]=\"listBox.listBoxAriaLabelledby()\"\n [attr.aria-activedescendant]=\"listBox.ariaActiveDescendant()\"\n (pointerdown)=\"onListBoxPointerDown($event)\"\n (click)=\"onListBoxClick($event)\"\n >\n <ng-content></ng-content>\n </div>\n</ng-template>\n" }]
324
+ }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], optionValueParse: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValueParse", required: false }] }], confirmSelection: [{ type: i0.Output, args: ["confirmSelection"] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
438
325
 
439
326
  class CheckboxComponent {
440
327
  id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
@@ -563,6 +450,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
563
450
  }, template: "<div cdkTrapFocus\n cdkTrapFocusAutoCapture=\"true\"\n class=\"ngx-bsl-drawer-container\">\n <header class=\"ngx-bsl-drawer-header\">\n <button class=\"button-icon\"\n (click)=\"close()\">\n <ngx-bsl-icon-x-mark></ngx-bsl-icon-x-mark>\n </button>\n @if (title()) {\n <h2 class=\"text-ellipsis\">{{ title() }}</h2>\n }\n </header>\n <div class=\"ngx-bsl-drawer-content\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: ["ngx-bsl-drawer{position:fixed;left:0;right:0;bottom:0;max-height:95vh;height:100%;transform:translateY(0);transition:transform .15s cubic-bezier(0,0,.2,1)}ngx-bsl-drawer.ngx-bsl-drawer-closed{transform:translateY(100%)}.ngx-bsl-drawer-container{height:100%;padding:24px;display:flex;flex-direction:column;background-color:var(--bsl-backgroundColor);border:1px solid var(--bsl-borderColor-emphasis);border-top-left-radius:1rem;border-top-right-radius:1rem}ngx-bsl-drawer-header{margin-bottom:1rem;display:flex;justify-content:space-between;flex-direction:row-reverse}.ngx-bsl-drawer-content{overflow-y:auto;height:100%}\n"] }]
564
451
  }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
565
452
 
453
+ class ListBoxGroupComponent {
454
+ title = input(...(ngDevMode ? [undefined, { debugName: "title" }] : []));
455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
456
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: ListBoxGroupComponent, isStandalone: true, selector: "ngx-bsl-list-box-group", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
457
+ @if (title()) {
458
+ <div class="ngx-bsl-list-box-group-title">{{ title() }}</div>
459
+ }
460
+ <ng-content></ng-content>
461
+ `, isInline: true, styles: ["ngx-bsl-list-box-group{display:flex;flex-direction:column;font:var(--bsl-font-xs);color:var(--bsl-textColor-muted)}ngx-bsl-list-box-group span:first-child{padding:.375rem .5rem;font:var(--bsl-font-xs);color:var(--bsl-textColor-muted);font-weight:500}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
462
+ }
463
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxGroupComponent, decorators: [{
464
+ type: Component,
465
+ args: [{ selector: 'ngx-bsl-list-box-group', imports: [], template: `
466
+ @if (title()) {
467
+ <div class="ngx-bsl-list-box-group-title">{{ title() }}</div>
468
+ }
469
+ <ng-content></ng-content>
470
+ `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, styles: ["ngx-bsl-list-box-group{display:flex;flex-direction:column;font:var(--bsl-font-xs);color:var(--bsl-textColor-muted)}ngx-bsl-list-box-group span:first-child{padding:.375rem .5rem;font:var(--bsl-font-xs);color:var(--bsl-textColor-muted);font-weight:500}\n"] }]
471
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }] } });
472
+
566
473
  // eslint-disable-next-line @typescript-eslint/no-extraneous-class
567
474
  class ListBoxSeparatorComponent {
568
475
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ListBoxSeparatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -1188,28 +1095,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1188
1095
  }], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], showThumbLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThumbLabels", required: false }] }], thumbFromRef: [{ type: i0.ViewChild, args: ['thumbFrom', { ...{ read: (ElementRef) }, isSignal: true }] }], thumbToRef: [{ type: i0.ViewChild, args: ['thumbTo', { ...{ read: (ElementRef) }, isSignal: true }] }] } });
1189
1096
 
1190
1097
  class SelectComponent {
1098
+ listBox = inject((ListBoxDirective));
1099
+ destroyRef = inject(DestroyRef);
1191
1100
  id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
1192
- options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
1193
- bindLabel = input(...(ngDevMode ? [undefined, { debugName: "bindLabel" }] : []));
1194
1101
  placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
1195
1102
  ariaLabel = input(...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : []));
1196
1103
  ariaLabelledBy = input(...(ngDevMode ? [undefined, { debugName: "ariaLabelledBy" }] : []));
1197
- comparisonField = input(...(ngDevMode ? [undefined, { debugName: "comparisonField" }] : []));
1198
1104
  iconMode = input(false, ...(ngDevMode ? [{ debugName: "iconMode" }] : []));
1199
1105
  customIcon = input(false, ...(ngDevMode ? [{ debugName: "customIcon" }] : []));
1200
1106
  dropdownWidth = input(...(ngDevMode ? [undefined, { debugName: "dropdownWidth" }] : []));
1201
- listBox = viewChild(ListBoxComponent, ...(ngDevMode ? [{ debugName: "listBox" }] : []));
1107
+ optionValueParse = input((option) => option, ...(ngDevMode ? [{ debugName: "optionValueParse" }] : []));
1108
+ displayLabel = input(...(ngDevMode ? [undefined, { debugName: "displayLabel" }] : []));
1202
1109
  onChange = (_value) => { };
1203
1110
  onTouch = () => { };
1204
1111
  value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : []));
1205
1112
  open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
1206
- ariaActiveDescendant = computed(() => this.listBox()?.ariaActiveDescendant() ?? null, ...(ngDevMode ? [{ debugName: "ariaActiveDescendant" }] : []));
1207
- initialFocusedOptionIndex = signal(null, ...(ngDevMode ? [{ debugName: "initialFocusedOptionIndex" }] : []));
1113
+ ariaActiveDescendant = computed(() => this.listBox.ariaActiveDescendant() ?? null, ...(ngDevMode ? [{ debugName: "ariaActiveDescendant" }] : []));
1114
+ displayValue = computed(() => {
1115
+ const displayLabel = this.displayLabel();
1116
+ const value = this.value();
1117
+ if (displayLabel && value && typeof value === 'object' && displayLabel in value) {
1118
+ return value[displayLabel];
1119
+ }
1120
+ else {
1121
+ return value;
1122
+ }
1123
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
1124
+ ngOnInit() {
1125
+ this.listBox.hasAriaSelected = true;
1126
+ this.subscribeSelectOption();
1127
+ }
1128
+ subscribeSelectOption() {
1129
+ this.listBox.selectOption.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(option => {
1130
+ if (option) {
1131
+ this.value.set(this.optionValueParse()(option));
1132
+ this.onChange(this.value());
1133
+ }
1134
+ this.hideListBox();
1135
+ });
1136
+ }
1208
1137
  showListBox() {
1209
1138
  this.open.set(true);
1139
+ this.listBox.initSelectedOption(this.value());
1210
1140
  }
1211
1141
  hideListBox() {
1212
- this.initialFocusedOptionIndex.set(null);
1142
+ this.listBox.clearVisualFocus();
1213
1143
  this.open.set(false);
1214
1144
  }
1215
1145
  onClick() {
@@ -1217,27 +1147,31 @@ class SelectComponent {
1217
1147
  this.hideListBox();
1218
1148
  }
1219
1149
  else {
1220
- this.showListBox();
1150
+ if (this.listBox.listBoxOptions().length) {
1151
+ this.showListBox();
1152
+ }
1221
1153
  }
1222
1154
  }
1223
- onSelectOption(value) {
1224
- this.value.set(value);
1225
- this.onChange(value);
1226
- this.hideListBox();
1155
+ onListBoxClick(event) {
1156
+ this.listBox.onClick(event);
1227
1157
  }
1228
1158
  onKeydown(event) {
1229
1159
  event.preventDefault();
1230
1160
  if (!this.open()) {
1231
1161
  this.showListBox();
1232
1162
  if (event.code === 'ArrowUp') {
1233
- this.initialFocusedOptionIndex.set(this.options().length - 1);
1163
+ this.listBox.setVisualFocus(this.listBox.listBoxOptions().length - 1);
1164
+ this.showListBox();
1234
1165
  }
1235
1166
  else if (event.code === 'ArrowDown') {
1236
- this.initialFocusedOptionIndex.set(0);
1167
+ if (!this.listBox.listBoxOptions().length)
1168
+ return;
1169
+ this.listBox.setVisualFocus(0);
1170
+ this.showListBox();
1237
1171
  }
1238
1172
  }
1239
1173
  else {
1240
- this.listBox()?.onKeydown(event);
1174
+ this.listBox.onKeydown(event);
1241
1175
  }
1242
1176
  }
1243
1177
  registerOnChange(onChange) {
@@ -1250,13 +1184,13 @@ class SelectComponent {
1250
1184
  this.value.set(value);
1251
1185
  }
1252
1186
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1253
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: SelectComponent, isStandalone: true, selector: "ngx-bsl-select", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, bindLabel: { classPropertyName: "bindLabel", publicName: "bindLabel", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, comparisonField: { classPropertyName: "comparisonField", publicName: "comparisonField", isSignal: true, isRequired: false, transformFunction: null }, iconMode: { classPropertyName: "iconMode", publicName: "iconMode", isSignal: true, isRequired: false, transformFunction: null }, customIcon: { classPropertyName: "customIcon", publicName: "customIcon", isSignal: true, isRequired: false, transformFunction: null }, dropdownWidth: { classPropertyName: "dropdownWidth", publicName: "dropdownWidth", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [
1187
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: SelectComponent, isStandalone: true, selector: "ngx-bsl-select", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, iconMode: { classPropertyName: "iconMode", publicName: "iconMode", isSignal: true, isRequired: false, transformFunction: null }, customIcon: { classPropertyName: "customIcon", publicName: "customIcon", isSignal: true, isRequired: false, transformFunction: null }, dropdownWidth: { classPropertyName: "dropdownWidth", publicName: "dropdownWidth", isSignal: true, isRequired: false, transformFunction: null }, optionValueParse: { classPropertyName: "optionValueParse", publicName: "optionValueParse", isSignal: true, isRequired: false, transformFunction: null }, displayLabel: { classPropertyName: "displayLabel", publicName: "displayLabel", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [
1254
1188
  {
1255
1189
  provide: NG_VALUE_ACCESSOR,
1256
1190
  useExisting: forwardRef(() => SelectComponent),
1257
1191
  multi: true,
1258
1192
  },
1259
- ], viewQueries: [{ propertyName: "listBox", first: true, predicate: ListBoxComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<button\n type=\"button\"\n role=\"combobox\"\n #selectButton\n class=\"bsl-select-button\"\n cdkOverlayOrigin\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"'list-box-' + id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n [class.button-icon]=\"iconMode()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.space)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\">\n @if (iconMode()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <span>{{ value() ? (value() | listBoxOptionValueConverter : bindLabel()) : placeholder() }}</span>\n\n @if (customIcon()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <ngx-bsl-icon-chevron-down></ngx-bsl-icon-chevron-down>\n }\n }\n</button>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectButton\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"dropdownWidth() || selectButton.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <ngx-bsl-list-box\n [initialValue]=\"value()\"\n [initialFocusedOptionIndex]=\"initialFocusedOptionIndex()\"\n [comparisonField]=\"comparisonField()\"\n [id]=\"'list-box-' + id()\"\n [ariaLabel]=\"ariaLabel()\"\n [ariaLabelledby]=\"ariaLabelledBy()\"\n [exposeAriaActiveDescendant]=\"true\"\n (selectOption)=\"onSelectOption($event)\">\n @for (option of options(); track option) {\n <ngx-bsl-list-box-option\n [id]=\"'list-box-option-' + id()\"\n [value]=\"option\">\n <ng-container>{{ option | listBoxOptionValueConverter : bindLabel() }}</ng-container>\n </ngx-bsl-list-box-option>\n }\n </ngx-bsl-list-box>\n</ng-template>\n", styles: ["ngx-bsl-select{display:block}.bsl-select-button{width:100%;min-height:2.25rem;justify-content:space-between;gap:.5rem}.bsl-select-button span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"], dependencies: [{ kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "component", type: IconChevronDownComponent, selector: "ngx-bsl-icon-chevron-down" }, { kind: "component", type: ListBoxComponent, selector: "ngx-bsl-list-box", inputs: ["id", "ariaLabel", "ariaLabelledby", "exposeAriaActiveDescendant", "comparisonField", "initialValue", "initialFocusedOptionIndex"], outputs: ["selectOption"] }, { kind: "component", type: ListBoxOptionComponent, selector: "ngx-bsl-list-box-option", inputs: ["value", "id"] }, { kind: "pipe", type: ListBoxOptionValueConverterPipe, name: "listBoxOptionValueConverter" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1193
+ ], hostDirectives: [{ directive: ListBoxDirective, inputs: ["listBoxId", "listBoxId", "listBoxAriaLabel", "listBoxAriaLabel", "listBoxAriaLabelledby", "listBoxAriaLabelledby", "optionValueEquality", "optionValueEquality"] }], ngImport: i0, template: "<button\n type=\"button\"\n role=\"combobox\"\n #selectButton\n class=\"ngx-bsl-select-button\"\n cdkOverlayOrigin\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n [class.button-icon]=\"iconMode()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.space)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\">\n @if (iconMode()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <span>{{ displayValue() }}</span>\n\n @if (customIcon()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <ngx-bsl-icon-chevron-down></ngx-bsl-icon-chevron-down>\n }\n }\n</button>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectButton\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"dropdownWidth() || selectButton.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <!-- eslint-disable-next-line\n @angular-eslint/template/interactive-supports-focus,\n @angular-eslint/template/click-events-have-key-events -->\n <div\n role=\"listbox\"\n class=\"ngx-bsl-list-box\"\n [id]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"listBox.listBoxAriaLabel()\"\n [attr.aria-labelledby]=\"listBox.listBoxAriaLabelledby()\"\n [attr.aria-activedescendant]=\"listBox.ariaActiveDescendant()\"\n (click)=\"onListBoxClick($event)\"\n >\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: ["ngx-bsl-select{display:block}.ngx-bsl-select-button{width:100%;justify-content:space-between;gap:.5rem}.ngx-bsl-select-button span{min-height:var(--bsl-line-height-sm);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"], dependencies: [{ kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "component", type: IconChevronDownComponent, selector: "ngx-bsl-icon-chevron-down" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1260
1194
  }
1261
1195
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: SelectComponent, decorators: [{
1262
1196
  type: Component,
@@ -1264,17 +1198,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1264
1198
  CdkOverlayOrigin,
1265
1199
  CdkConnectedOverlay,
1266
1200
  IconChevronDownComponent,
1267
- ListBoxComponent,
1268
- ListBoxOptionComponent,
1269
- ListBoxOptionValueConverterPipe,
1270
1201
  ], providers: [
1271
1202
  {
1272
1203
  provide: NG_VALUE_ACCESSOR,
1273
1204
  useExisting: forwardRef(() => SelectComponent),
1274
1205
  multi: true,
1275
1206
  },
1276
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<button\n type=\"button\"\n role=\"combobox\"\n #selectButton\n class=\"bsl-select-button\"\n cdkOverlayOrigin\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"'list-box-' + id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n [class.button-icon]=\"iconMode()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.space)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\">\n @if (iconMode()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <span>{{ value() ? (value() | listBoxOptionValueConverter : bindLabel()) : placeholder() }}</span>\n\n @if (customIcon()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <ngx-bsl-icon-chevron-down></ngx-bsl-icon-chevron-down>\n }\n }\n</button>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectButton\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"dropdownWidth() || selectButton.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <ngx-bsl-list-box\n [initialValue]=\"value()\"\n [initialFocusedOptionIndex]=\"initialFocusedOptionIndex()\"\n [comparisonField]=\"comparisonField()\"\n [id]=\"'list-box-' + id()\"\n [ariaLabel]=\"ariaLabel()\"\n [ariaLabelledby]=\"ariaLabelledBy()\"\n [exposeAriaActiveDescendant]=\"true\"\n (selectOption)=\"onSelectOption($event)\">\n @for (option of options(); track option) {\n <ngx-bsl-list-box-option\n [id]=\"'list-box-option-' + id()\"\n [value]=\"option\">\n <ng-container>{{ option | listBoxOptionValueConverter : bindLabel() }}</ng-container>\n </ngx-bsl-list-box-option>\n }\n </ngx-bsl-list-box>\n</ng-template>\n", styles: ["ngx-bsl-select{display:block}.bsl-select-button{width:100%;min-height:2.25rem;justify-content:space-between;gap:.5rem}.bsl-select-button span{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"] }]
1277
- }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], bindLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], comparisonField: [{ type: i0.Input, args: [{ isSignal: true, alias: "comparisonField", required: false }] }], iconMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconMode", required: false }] }], customIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "customIcon", required: false }] }], dropdownWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropdownWidth", required: false }] }], listBox: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ListBoxComponent), { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
1207
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, hostDirectives: [
1208
+ {
1209
+ directive: ListBoxDirective,
1210
+ inputs: ['listBoxId', 'listBoxAriaLabel', 'listBoxAriaLabelledby', 'optionValueEquality'],
1211
+ },
1212
+ ], template: "<button\n type=\"button\"\n role=\"combobox\"\n #selectButton\n class=\"ngx-bsl-select-button\"\n cdkOverlayOrigin\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-labelledby]=\"ariaLabelledBy()\"\n [attr.aria-activedescendant]=\"ariaActiveDescendant()\"\n [class.button-icon]=\"iconMode()\"\n (click)=\"onClick()\"\n (keydown.enter)=\"onKeydown($any($event))\"\n (keydown.space)=\"onKeydown($any($event))\"\n (keydown.arrowUp)=\"onKeydown($any($event))\"\n (keydown.arrowDown)=\"onKeydown($any($event))\"\n (keydown.escape)=\"hideListBox()\">\n @if (iconMode()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <span>{{ displayValue() }}</span>\n\n @if (customIcon()) {\n <ng-content select=\"[data-custom-icon]\"></ng-content>\n } @else {\n <ngx-bsl-icon-chevron-down></ngx-bsl-icon-chevron-down>\n }\n }\n</button>\n<ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"selectButton\"\n [cdkConnectedOverlayOpen]=\"open()\"\n [cdkConnectedOverlayWidth]=\"dropdownWidth() || selectButton.offsetWidth\"\n (overlayOutsideClick)=\"hideListBox()\">\n <!-- eslint-disable-next-line\n @angular-eslint/template/interactive-supports-focus,\n @angular-eslint/template/click-events-have-key-events -->\n <div\n role=\"listbox\"\n class=\"ngx-bsl-list-box\"\n [id]=\"listBox.listBoxId() || id() + '-list-box'\"\n [attr.aria-label]=\"listBox.listBoxAriaLabel()\"\n [attr.aria-labelledby]=\"listBox.listBoxAriaLabelledby()\"\n [attr.aria-activedescendant]=\"listBox.ariaActiveDescendant()\"\n (click)=\"onListBoxClick($event)\"\n >\n <ng-content></ng-content>\n </div>\n</ng-template>\n", styles: ["ngx-bsl-select{display:block}.ngx-bsl-select-button{width:100%;justify-content:space-between;gap:.5rem}.ngx-bsl-select-button span{min-height:var(--bsl-line-height-sm);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n"] }]
1213
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], iconMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconMode", required: false }] }], customIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "customIcon", required: false }] }], dropdownWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "dropdownWidth", required: false }] }], optionValueParse: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValueParse", required: false }] }], displayLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayLabel", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
1278
1214
 
1279
1215
  class TooltipComponent {
1280
1216
  message = '';
@@ -1387,5 +1323,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
1387
1323
  * Generated bundle index. Do not edit.
1388
1324
  */
1389
1325
 
1390
- export { CheckboxComponent, ComboboxComponent, DrawerComponent, IconCheckComponent, IconChevronDownComponent, IconCompanyComponent, IconLanguageComponent, IconLocationComponent, IconMoonComponent, IconSunComponent, IconXMarkComponent, ListBoxComponent, ListBoxGroupComponent, ListBoxOptionComponent, ListBoxOptionValueConverterPipe, ListBoxSeparatorComponent, PaginationComponent, RangeComponent, SelectComponent, TooltipDirective };
1326
+ export { CheckboxComponent, ComboboxComponent, DrawerComponent, IconCheckComponent, IconChevronDownComponent, IconCompanyComponent, IconLanguageComponent, IconLocationComponent, IconMoonComponent, IconSunComponent, IconXMarkComponent, ListBoxDirective, ListBoxGroupComponent, ListBoxOptionComponent, ListBoxSeparatorComponent, PaginationComponent, RangeComponent, SelectComponent, TooltipDirective };
1391
1327
  //# sourceMappingURL=ngx-bsl.mjs.map