@spartan-ng/brain 0.0.1-alpha.603 → 0.0.1-alpha.605
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/combobox/README.md +3 -0
- package/combobox/index.d.ts +377 -0
- package/core/index.d.ts +4 -1
- package/fesm2022/spartan-ng-brain-combobox.mjs +919 -0
- package/fesm2022/spartan-ng-brain-combobox.mjs.map +1 -0
- package/fesm2022/spartan-ng-brain-core.mjs +30 -1
- package/fesm2022/spartan-ng-brain-core.mjs.map +1 -1
- package/package.json +9 -5
|
@@ -0,0 +1,919 @@
|
|
|
1
|
+
import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { Directive, InjectionToken, inject, forwardRef, Injector, input, booleanAttribute, linkedSignal, computed, model, signal, contentChild, ElementRef, contentChildren, effect, Renderer2, TemplateRef, ViewContainerRef, PLATFORM_ID } from '@angular/core';
|
|
4
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
|
+
import { BrnPopover } from '@spartan-ng/brain/popover';
|
|
6
|
+
import { stringifyAsLabel } from '@spartan-ng/brain/core';
|
|
7
|
+
import { BrnDialog } from '@spartan-ng/brain/dialog';
|
|
8
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
9
|
+
|
|
10
|
+
class BrnComboboxInputWrapper {
|
|
11
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxInputWrapper, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
12
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxInputWrapper, isStandalone: true, selector: "[brnComboboxInputWrapper]", ngImport: i0 });
|
|
13
|
+
}
|
|
14
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxInputWrapper, decorators: [{
|
|
15
|
+
type: Directive,
|
|
16
|
+
args: [{ selector: '[brnComboboxInputWrapper]' }]
|
|
17
|
+
}] });
|
|
18
|
+
|
|
19
|
+
const BrnComboboxItemToken = new InjectionToken('BrnComboboxItemToken');
|
|
20
|
+
function provideBrnComboboxItem(Combobox) {
|
|
21
|
+
return { provide: BrnComboboxItemToken, useExisting: Combobox };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const comboboxContainsFilter = (item, query, collator, itemToString) => {
|
|
25
|
+
if (!query) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
const itemString = stringifyAsLabel(item, itemToString);
|
|
29
|
+
for (let i = 0; i <= itemString.length - query.length; i += 1) {
|
|
30
|
+
if (collator.compare(itemString.slice(i, i + query.length), query) === 0) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
};
|
|
36
|
+
const comboboxStartsWithFilter = (item, query, collator, itemToString) => {
|
|
37
|
+
if (!query) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
const itemString = stringifyAsLabel(item, itemToString);
|
|
41
|
+
return collator.compare(itemString.slice(0, query.length), query) === 0;
|
|
42
|
+
};
|
|
43
|
+
const comboboxEndsWithFilter = (item, query, collator, itemToString) => {
|
|
44
|
+
if (!query) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
const itemString = stringifyAsLabel(item, itemToString);
|
|
48
|
+
const queryLength = query.length;
|
|
49
|
+
return (itemString.length >= queryLength && collator.compare(itemString.slice(itemString.length - queryLength), query) === 0);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const BrnComboboxBaseToken = new InjectionToken('BrnComboboxBaseToken');
|
|
53
|
+
function provideBrnComboboxBase(instance) {
|
|
54
|
+
return { provide: BrnComboboxBaseToken, useExisting: instance };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Inject the combobox component.
|
|
58
|
+
*/
|
|
59
|
+
function injectBrnComboboxBase() {
|
|
60
|
+
return inject(BrnComboboxBaseToken);
|
|
61
|
+
}
|
|
62
|
+
function getDefaultConfig() {
|
|
63
|
+
return {
|
|
64
|
+
filterOptions: {
|
|
65
|
+
usage: 'search',
|
|
66
|
+
sensitivity: 'base',
|
|
67
|
+
ignorePunctuation: true,
|
|
68
|
+
},
|
|
69
|
+
filter: (itemValue, search, collator, itemToString) => comboboxContainsFilter(itemValue, search, collator, itemToString),
|
|
70
|
+
isItemEqualToValue: (itemValue, selectedValue) => Object.is(itemValue, selectedValue),
|
|
71
|
+
itemToString: undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const BrnComboboxConfigToken = new InjectionToken('BrnComboboxConfig');
|
|
75
|
+
function provideBrnComboboxConfig(config) {
|
|
76
|
+
return { provide: BrnComboboxConfigToken, useValue: { ...getDefaultConfig(), ...config } };
|
|
77
|
+
}
|
|
78
|
+
function injectBrnComboboxConfig() {
|
|
79
|
+
const injectedConfig = inject(BrnComboboxConfigToken, { optional: true });
|
|
80
|
+
return injectedConfig ? injectedConfig : getDefaultConfig();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const BRN_COMBOBOX_VALUE_ACCESSOR = {
|
|
84
|
+
provide: NG_VALUE_ACCESSOR,
|
|
85
|
+
useExisting: forwardRef(() => BrnCombobox),
|
|
86
|
+
multi: true,
|
|
87
|
+
};
|
|
88
|
+
class BrnCombobox {
|
|
89
|
+
_injector = inject(Injector);
|
|
90
|
+
_config = injectBrnComboboxConfig();
|
|
91
|
+
/** Access the popover if present */
|
|
92
|
+
_brnPopover = inject(BrnPopover, { optional: true });
|
|
93
|
+
/** Whether the combobox is disabled */
|
|
94
|
+
disabled = input(false, { transform: booleanAttribute });
|
|
95
|
+
_disabled = linkedSignal(this.disabled);
|
|
96
|
+
/** @internal The disabled state as a readonly signal */
|
|
97
|
+
disabledState = this._disabled.asReadonly();
|
|
98
|
+
/** Options for filtering the combobox items */
|
|
99
|
+
filterOptions = input({});
|
|
100
|
+
/** @internal The collator used for string comparison */
|
|
101
|
+
collator = computed(() => {
|
|
102
|
+
const options = this.filterOptions();
|
|
103
|
+
const mergedOptions = { ...this._config.filterOptions, ...options };
|
|
104
|
+
return new Intl.Collator(options.locale, mergedOptions);
|
|
105
|
+
});
|
|
106
|
+
/** A function to compare an item with the selected value. */
|
|
107
|
+
isItemEqualToValue = input(this._config.isItemEqualToValue);
|
|
108
|
+
/** A function to convert an item to a string for display. */
|
|
109
|
+
itemToString = input(this._config.itemToString);
|
|
110
|
+
/** A custom filter function to use when searching. */
|
|
111
|
+
filter = input(this._config.filter);
|
|
112
|
+
/** The selected value of the combobox. */
|
|
113
|
+
value = model(null);
|
|
114
|
+
/** The current search query. */
|
|
115
|
+
search = signal('');
|
|
116
|
+
_searchInputWrapper = contentChild(BrnComboboxInputWrapper, {
|
|
117
|
+
read: ElementRef,
|
|
118
|
+
});
|
|
119
|
+
/** @internal The width of the search input wrapper */
|
|
120
|
+
searchInputWrapperWidth = computed(() => {
|
|
121
|
+
const inputElement = this._searchInputWrapper()?.nativeElement;
|
|
122
|
+
return inputElement ? inputElement.offsetWidth : null;
|
|
123
|
+
});
|
|
124
|
+
/** @internal Access all the items within the combobox */
|
|
125
|
+
items = contentChildren(BrnComboboxItemToken, {
|
|
126
|
+
descendants: true,
|
|
127
|
+
});
|
|
128
|
+
/** Determine if the combobox has any visible items */
|
|
129
|
+
visibleItems = computed(() => this.items().some((item) => item.visible()));
|
|
130
|
+
/** @internal The key manager for managing active descendant */
|
|
131
|
+
keyManager = new ActiveDescendantKeyManager(this.items, this._injector);
|
|
132
|
+
/** @internal Whether the combobox is expanded */
|
|
133
|
+
isExpanded = computed(() => this._brnPopover?.stateComputed() === 'open');
|
|
134
|
+
_onChange;
|
|
135
|
+
_onTouched;
|
|
136
|
+
constructor() {
|
|
137
|
+
this.keyManager
|
|
138
|
+
.withVerticalOrientation()
|
|
139
|
+
.withHomeAndEnd()
|
|
140
|
+
.withWrap()
|
|
141
|
+
.skipPredicate((item) => item.disabled || !item.visible());
|
|
142
|
+
this._brnPopover?.closed.subscribe(() => {
|
|
143
|
+
this.search.set('');
|
|
144
|
+
this.keyManager.setActiveItem(-1);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
isSelected(itemValue) {
|
|
148
|
+
return this.isItemEqualToValue()(itemValue, this.value());
|
|
149
|
+
}
|
|
150
|
+
select(itemValue) {
|
|
151
|
+
this.value.set(itemValue);
|
|
152
|
+
this._onChange?.(itemValue);
|
|
153
|
+
this.close();
|
|
154
|
+
}
|
|
155
|
+
/** Select the active item with Enter key. */
|
|
156
|
+
selectActiveItem() {
|
|
157
|
+
if (!this.isExpanded())
|
|
158
|
+
return;
|
|
159
|
+
const value = this.keyManager.activeItem?.value();
|
|
160
|
+
if (value === undefined)
|
|
161
|
+
return;
|
|
162
|
+
this.select(value);
|
|
163
|
+
}
|
|
164
|
+
resetValue() {
|
|
165
|
+
this.value.set(null);
|
|
166
|
+
this._onChange?.(null);
|
|
167
|
+
}
|
|
168
|
+
removeValue(_) {
|
|
169
|
+
console.warn('BrnComboboxChipRemove only works with multiple selection comboboxes.');
|
|
170
|
+
}
|
|
171
|
+
removeLastSelectedItem() {
|
|
172
|
+
console.warn('BrnComboboxChipInput only works with multiple selection comboboxes.');
|
|
173
|
+
}
|
|
174
|
+
open() {
|
|
175
|
+
if (this._disabled() || this.isExpanded())
|
|
176
|
+
return;
|
|
177
|
+
this._brnPopover?.open();
|
|
178
|
+
}
|
|
179
|
+
close() {
|
|
180
|
+
if (this._disabled() || !this.isExpanded())
|
|
181
|
+
return;
|
|
182
|
+
this._brnPopover?.close();
|
|
183
|
+
}
|
|
184
|
+
/** CONTROL VALUE ACCESSOR */
|
|
185
|
+
writeValue(value) {
|
|
186
|
+
this.value.set(value);
|
|
187
|
+
}
|
|
188
|
+
registerOnChange(fn) {
|
|
189
|
+
this._onChange = fn;
|
|
190
|
+
}
|
|
191
|
+
registerOnTouched(fn) {
|
|
192
|
+
this._onTouched = fn;
|
|
193
|
+
}
|
|
194
|
+
setDisabledState(isDisabled) {
|
|
195
|
+
this._disabled.set(isDisabled);
|
|
196
|
+
}
|
|
197
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnCombobox, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
198
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.0.7", type: BrnCombobox, isStandalone: true, selector: "[brnCombobox]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, filterOptions: { classPropertyName: "filterOptions", publicName: "filterOptions", isSignal: true, isRequired: false, transformFunction: null }, isItemEqualToValue: { classPropertyName: "isItemEqualToValue", publicName: "isItemEqualToValue", isSignal: true, isRequired: false, transformFunction: null }, itemToString: { classPropertyName: "itemToString", publicName: "itemToString", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [provideBrnComboboxBase(BrnCombobox), BRN_COMBOBOX_VALUE_ACCESSOR], queries: [{ propertyName: "_searchInputWrapper", first: true, predicate: BrnComboboxInputWrapper, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "items", predicate: BrnComboboxItemToken, descendants: true, isSignal: true }], ngImport: i0 });
|
|
199
|
+
}
|
|
200
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnCombobox, decorators: [{
|
|
201
|
+
type: Directive,
|
|
202
|
+
args: [{
|
|
203
|
+
selector: '[brnCombobox]',
|
|
204
|
+
providers: [provideBrnComboboxBase(BrnCombobox), BRN_COMBOBOX_VALUE_ACCESSOR],
|
|
205
|
+
}]
|
|
206
|
+
}], ctorParameters: () => [] });
|
|
207
|
+
|
|
208
|
+
class BrnComboboxContent {
|
|
209
|
+
_combobox = injectBrnComboboxBase();
|
|
210
|
+
/** Determine if the combobox has any visible items */
|
|
211
|
+
_visibleItems = this._combobox.visibleItems;
|
|
212
|
+
_comboboxWidth = this._combobox.searchInputWrapperWidth;
|
|
213
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
214
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxContent, isStandalone: true, selector: "[brnComboboxContent]", host: { properties: { "attr.data-empty": "!_visibleItems() ? \"\" : null", "style.--brn-combobox-width.px": "_comboboxWidth()" } }, ngImport: i0 });
|
|
215
|
+
}
|
|
216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxContent, decorators: [{
|
|
217
|
+
type: Directive,
|
|
218
|
+
args: [{
|
|
219
|
+
selector: '[brnComboboxContent]',
|
|
220
|
+
host: {
|
|
221
|
+
'[attr.data-empty]': '!_visibleItems() ? "" : null',
|
|
222
|
+
'[style.--brn-combobox-width.px]': '_comboboxWidth()',
|
|
223
|
+
},
|
|
224
|
+
}]
|
|
225
|
+
}] });
|
|
226
|
+
|
|
227
|
+
class BrnComboboxAnchor {
|
|
228
|
+
_host = inject(ElementRef, { host: true });
|
|
229
|
+
_brnDialog = inject(BrnDialog, { optional: true });
|
|
230
|
+
_content = inject(BrnComboboxContent, { optional: true });
|
|
231
|
+
constructor() {
|
|
232
|
+
// skip if it's inside a combobox content
|
|
233
|
+
if (this._content)
|
|
234
|
+
return;
|
|
235
|
+
if (!this._brnDialog)
|
|
236
|
+
return;
|
|
237
|
+
this._brnDialog.mutableAttachTo.set(this._host.nativeElement);
|
|
238
|
+
}
|
|
239
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxAnchor, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
240
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxAnchor, isStandalone: true, selector: "[brnComboboxAnchor]", ngImport: i0 });
|
|
241
|
+
}
|
|
242
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxAnchor, decorators: [{
|
|
243
|
+
type: Directive,
|
|
244
|
+
args: [{ selector: '[brnComboboxAnchor]' }]
|
|
245
|
+
}], ctorParameters: () => [] });
|
|
246
|
+
|
|
247
|
+
class BrnComboboxChip {
|
|
248
|
+
_combobox = injectBrnComboboxBase();
|
|
249
|
+
_disabled = this._combobox.disabledState;
|
|
250
|
+
value = input.required();
|
|
251
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxChip, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
252
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnComboboxChip, isStandalone: true, selector: "[brnComboboxChip]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "tabindex": "-1" }, properties: { "attr.data-disabled": "_disabled() ? \"\" : null", "attr.aria-disabled": "_disabled() ? \"true\" : null" } }, ngImport: i0 });
|
|
253
|
+
}
|
|
254
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxChip, decorators: [{
|
|
255
|
+
type: Directive,
|
|
256
|
+
args: [{
|
|
257
|
+
selector: '[brnComboboxChip]',
|
|
258
|
+
host: {
|
|
259
|
+
tabindex: '-1',
|
|
260
|
+
'[attr.data-disabled]': '_disabled() ? "" : null',
|
|
261
|
+
'[attr.aria-disabled]': '_disabled() ? "true" : null',
|
|
262
|
+
},
|
|
263
|
+
}]
|
|
264
|
+
}] });
|
|
265
|
+
|
|
266
|
+
class BrnComboboxChipInput {
|
|
267
|
+
static _id = 0;
|
|
268
|
+
_el = inject(ElementRef);
|
|
269
|
+
_combobox = injectBrnComboboxBase();
|
|
270
|
+
/** The id of the combobox input */
|
|
271
|
+
id = input(`brn-combobox-input-${++BrnComboboxChipInput._id}`);
|
|
272
|
+
_disabled = this._combobox.disabledState;
|
|
273
|
+
/** Whether the combobox panel is expanded */
|
|
274
|
+
_isExpanded = this._combobox.isExpanded;
|
|
275
|
+
constructor() {
|
|
276
|
+
effect(() => {
|
|
277
|
+
const search = this._combobox.search();
|
|
278
|
+
if (search === '') {
|
|
279
|
+
// reset input on popover close
|
|
280
|
+
this._el.nativeElement.value = '';
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
onInput(event) {
|
|
285
|
+
const value = event.target.value;
|
|
286
|
+
this._combobox.search.set(value);
|
|
287
|
+
this._combobox.open();
|
|
288
|
+
}
|
|
289
|
+
/** Listen for keydown events */
|
|
290
|
+
onKeyDown(event) {
|
|
291
|
+
if (event.key === 'Enter') {
|
|
292
|
+
// prevent form submission if inside a form
|
|
293
|
+
event.preventDefault();
|
|
294
|
+
this._combobox.selectActiveItem();
|
|
295
|
+
}
|
|
296
|
+
if (!this._isExpanded()) {
|
|
297
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
298
|
+
this._combobox.open();
|
|
299
|
+
}
|
|
300
|
+
if (event.key === 'Escape') {
|
|
301
|
+
this._combobox.resetValue();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (this._combobox.search() === '' && event.key === 'Backspace') {
|
|
305
|
+
this._combobox.removeLastSelectedItem();
|
|
306
|
+
}
|
|
307
|
+
this._combobox.keyManager.onKeydown(event);
|
|
308
|
+
}
|
|
309
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxChipInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
310
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnComboboxChipInput, isStandalone: true, selector: "input[brnComboboxChipInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "text", "role": "combobox", "autocomplete": "off", "autocorrect": "off", "autocapitalize": "none", "spellcheck": "false", "aria-autocomplete": "list", "aria-haspopup": "listbox" }, listeners: { "keydown": "onKeyDown($event)", "input": "onInput($event)" }, properties: { "id": "id()", "attr.aria-expanded": "_isExpanded()", "attr.disabled": "_disabled() ? \"\" : null", "attr.data-disabled": "_disabled() ? \"\" : null" } }, ngImport: i0 });
|
|
311
|
+
}
|
|
312
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxChipInput, decorators: [{
|
|
313
|
+
type: Directive,
|
|
314
|
+
args: [{
|
|
315
|
+
selector: 'input[brnComboboxChipInput]',
|
|
316
|
+
host: {
|
|
317
|
+
'[id]': 'id()',
|
|
318
|
+
type: 'text',
|
|
319
|
+
role: 'combobox',
|
|
320
|
+
autocomplete: 'off',
|
|
321
|
+
autocorrect: 'off',
|
|
322
|
+
autocapitalize: 'none',
|
|
323
|
+
spellcheck: 'false',
|
|
324
|
+
'aria-autocomplete': 'list',
|
|
325
|
+
'aria-haspopup': 'listbox',
|
|
326
|
+
'[attr.aria-expanded]': '_isExpanded()',
|
|
327
|
+
'[attr.disabled]': '_disabled() ? "" : null',
|
|
328
|
+
'[attr.data-disabled]': '_disabled() ? "" : null',
|
|
329
|
+
'(keydown)': 'onKeyDown($event)',
|
|
330
|
+
'(input)': 'onInput($event)',
|
|
331
|
+
},
|
|
332
|
+
}]
|
|
333
|
+
}], ctorParameters: () => [] });
|
|
334
|
+
|
|
335
|
+
class BrnComboboxChipRemove {
|
|
336
|
+
_combobox = injectBrnComboboxBase();
|
|
337
|
+
_chip = inject((BrnComboboxChip));
|
|
338
|
+
_disabled = this._combobox.disabledState;
|
|
339
|
+
removeChip(event) {
|
|
340
|
+
event.stopPropagation();
|
|
341
|
+
if (this._combobox.disabledState()) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const value = this._chip.value();
|
|
345
|
+
this._combobox.removeValue(value);
|
|
346
|
+
}
|
|
347
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxChipRemove, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
348
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxChipRemove, isStandalone: true, selector: "button[brnComboboxChipRemove]", host: { attributes: { "type": "button", "tabindex": "-1", "aria-disabled": "true" }, listeners: { "click": "removeChip($event)" }, properties: { "attr.data-disabled": "_disabled() ? \"\" : null", "attr.aria-disabled": "_disabled() ? \"true\" : null", "attr.disabled": "_disabled() ? \"\" : null" } }, ngImport: i0 });
|
|
349
|
+
}
|
|
350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxChipRemove, decorators: [{
|
|
351
|
+
type: Directive,
|
|
352
|
+
args: [{
|
|
353
|
+
selector: 'button[brnComboboxChipRemove]',
|
|
354
|
+
host: {
|
|
355
|
+
type: 'button',
|
|
356
|
+
tabindex: '-1',
|
|
357
|
+
'aria-disabled': 'true',
|
|
358
|
+
'[attr.data-disabled]': '_disabled() ? "" : null',
|
|
359
|
+
'[attr.aria-disabled]': '_disabled() ? "true" : null',
|
|
360
|
+
'[attr.disabled]': '_disabled() ? "" : null',
|
|
361
|
+
'(click)': 'removeChip($event)',
|
|
362
|
+
},
|
|
363
|
+
}]
|
|
364
|
+
}] });
|
|
365
|
+
|
|
366
|
+
class BrnComboboxClear {
|
|
367
|
+
_combobox = injectBrnComboboxBase();
|
|
368
|
+
_renderer = inject(Renderer2);
|
|
369
|
+
_templateRef = inject(TemplateRef);
|
|
370
|
+
_viewContainerRef = inject(ViewContainerRef);
|
|
371
|
+
/** Determine if the combobox has a value */
|
|
372
|
+
_hasValue = computed(() => this._combobox.value() !== null);
|
|
373
|
+
constructor() {
|
|
374
|
+
effect(() => {
|
|
375
|
+
this._viewContainerRef.clear();
|
|
376
|
+
if (this._hasValue()) {
|
|
377
|
+
const view = this._viewContainerRef.createEmbeddedView(this._templateRef);
|
|
378
|
+
view.rootNodes.forEach((node) => {
|
|
379
|
+
this._renderer.listen(node, 'click', (e) => {
|
|
380
|
+
e.preventDefault();
|
|
381
|
+
this.clear();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
clear() {
|
|
388
|
+
this._combobox.resetValue();
|
|
389
|
+
}
|
|
390
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxClear, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
391
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxClear, isStandalone: true, selector: "[brnComboboxClear]", ngImport: i0 });
|
|
392
|
+
}
|
|
393
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxClear, decorators: [{
|
|
394
|
+
type: Directive,
|
|
395
|
+
args: [{
|
|
396
|
+
selector: '[brnComboboxClear]',
|
|
397
|
+
}]
|
|
398
|
+
}], ctorParameters: () => [] });
|
|
399
|
+
|
|
400
|
+
class BrnComboboxEmpty {
|
|
401
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
402
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxEmpty, isStandalone: true, selector: "[brnComboboxEmpty]", host: { attributes: { "role": "status", "aria-live": "polite", "aria-atomic": "true" } }, ngImport: i0 });
|
|
403
|
+
}
|
|
404
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxEmpty, decorators: [{
|
|
405
|
+
type: Directive,
|
|
406
|
+
args: [{
|
|
407
|
+
selector: '[brnComboboxEmpty]',
|
|
408
|
+
host: {
|
|
409
|
+
role: 'status',
|
|
410
|
+
'aria-live': 'polite',
|
|
411
|
+
'aria-atomic': 'true',
|
|
412
|
+
},
|
|
413
|
+
}]
|
|
414
|
+
}] });
|
|
415
|
+
|
|
416
|
+
class BrnComboboxLabel {
|
|
417
|
+
static _id = 0;
|
|
418
|
+
/** The id of the combobox label */
|
|
419
|
+
id = input(`brn-combobox-label-${++BrnComboboxLabel._id}`);
|
|
420
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
421
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnComboboxLabel, isStandalone: true, selector: "[brnComboboxLabel]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "id()" } }, ngImport: i0 });
|
|
422
|
+
}
|
|
423
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxLabel, decorators: [{
|
|
424
|
+
type: Directive,
|
|
425
|
+
args: [{
|
|
426
|
+
selector: '[brnComboboxLabel]',
|
|
427
|
+
host: {
|
|
428
|
+
'[id]': 'id()',
|
|
429
|
+
},
|
|
430
|
+
}]
|
|
431
|
+
}] });
|
|
432
|
+
|
|
433
|
+
class BrnComboboxGroup {
|
|
434
|
+
/** Get the items in the group */
|
|
435
|
+
_items = contentChildren(BrnComboboxItemToken, {
|
|
436
|
+
descendants: true,
|
|
437
|
+
});
|
|
438
|
+
/** Determine if there are any visible items in the group */
|
|
439
|
+
_visible = computed(() => this._items().some((item) => item.visible()));
|
|
440
|
+
/** Get the label associated with the group */
|
|
441
|
+
_label = contentChild(BrnComboboxLabel);
|
|
442
|
+
_labelledBy = computed(() => {
|
|
443
|
+
const label = this._label();
|
|
444
|
+
return label ? label.id() : null;
|
|
445
|
+
});
|
|
446
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
447
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.0.7", type: BrnComboboxGroup, isStandalone: true, selector: "[brnComboboxGroup]", host: { attributes: { "role": "group" }, properties: { "attr.data-hidden": "!_visible() ? \"\" : null", "attr.aria-labelledby": "_labelledBy()" } }, queries: [{ propertyName: "_items", predicate: BrnComboboxItemToken, descendants: true, isSignal: true }, { propertyName: "_label", first: true, predicate: BrnComboboxLabel, descendants: true, isSignal: true }], ngImport: i0 });
|
|
448
|
+
}
|
|
449
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxGroup, decorators: [{
|
|
450
|
+
type: Directive,
|
|
451
|
+
args: [{
|
|
452
|
+
selector: '[brnComboboxGroup]',
|
|
453
|
+
host: {
|
|
454
|
+
role: 'group',
|
|
455
|
+
'[attr.data-hidden]': '!_visible() ? "" : null',
|
|
456
|
+
'[attr.aria-labelledby]': '_labelledBy()',
|
|
457
|
+
},
|
|
458
|
+
}]
|
|
459
|
+
}] });
|
|
460
|
+
|
|
461
|
+
class BrnComboboxInput {
|
|
462
|
+
static _id = 0;
|
|
463
|
+
_el = inject(ElementRef);
|
|
464
|
+
_combobox = injectBrnComboboxBase();
|
|
465
|
+
_content = inject(BrnComboboxContent, { optional: true });
|
|
466
|
+
_mode = computed(() => (this._content ? 'popup' : 'combobox'));
|
|
467
|
+
/** The id of the combobox input */
|
|
468
|
+
id = input(`brn-combobox-input-${++BrnComboboxInput._id}`);
|
|
469
|
+
disabled = this._combobox.disabledState;
|
|
470
|
+
/** Whether the combobox panel is expanded */
|
|
471
|
+
_isExpanded = this._combobox.isExpanded;
|
|
472
|
+
constructor() {
|
|
473
|
+
effect(() => {
|
|
474
|
+
const mode = this._mode();
|
|
475
|
+
const value = this._combobox.value();
|
|
476
|
+
const search = this._combobox.search();
|
|
477
|
+
switch (mode) {
|
|
478
|
+
case 'combobox':
|
|
479
|
+
if (value && search === '') {
|
|
480
|
+
this._el.nativeElement.value = stringifyAsLabel(value, this._combobox.itemToString());
|
|
481
|
+
}
|
|
482
|
+
else if (search === '') {
|
|
483
|
+
this._el.nativeElement.value = '';
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
case 'popup':
|
|
487
|
+
if (search === '') {
|
|
488
|
+
this._el.nativeElement.value = '';
|
|
489
|
+
}
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
onInput(event) {
|
|
495
|
+
const value = event.target.value;
|
|
496
|
+
this._combobox.search.set(value);
|
|
497
|
+
this._combobox.open();
|
|
498
|
+
if (value === '' && this._mode() === 'combobox') {
|
|
499
|
+
this._combobox.resetValue();
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
/** Listen for keydown events */
|
|
503
|
+
onKeyDown(event) {
|
|
504
|
+
if (event.key === 'Enter') {
|
|
505
|
+
// prevent form submission if inside a form
|
|
506
|
+
event.preventDefault();
|
|
507
|
+
this._combobox.selectActiveItem();
|
|
508
|
+
}
|
|
509
|
+
if (!this._isExpanded()) {
|
|
510
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
511
|
+
this._combobox.open();
|
|
512
|
+
}
|
|
513
|
+
if (event.key === 'Escape') {
|
|
514
|
+
this._combobox.resetValue();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
this._combobox.keyManager.onKeydown(event);
|
|
518
|
+
}
|
|
519
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
520
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnComboboxInput, isStandalone: true, selector: "input[brnComboboxInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "text", "role": "combobox", "autocomplete": "off", "autocorrect": "off", "autocapitalize": "none", "spellcheck": "false", "aria-autocomplete": "list", "aria-haspopup": "listbox" }, listeners: { "keydown": "onKeyDown($event)", "input": "onInput($event)" }, properties: { "id": "id()", "attr.aria-expanded": "_isExpanded()", "attr.disabled": "disabled() ? \"\" : null" } }, exportAs: ["brnComboboxInput"], ngImport: i0 });
|
|
521
|
+
}
|
|
522
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxInput, decorators: [{
|
|
523
|
+
type: Directive,
|
|
524
|
+
args: [{
|
|
525
|
+
selector: 'input[brnComboboxInput]',
|
|
526
|
+
exportAs: 'brnComboboxInput',
|
|
527
|
+
host: {
|
|
528
|
+
'[id]': 'id()',
|
|
529
|
+
type: 'text',
|
|
530
|
+
role: 'combobox',
|
|
531
|
+
autocomplete: 'off',
|
|
532
|
+
autocorrect: 'off',
|
|
533
|
+
autocapitalize: 'none',
|
|
534
|
+
spellcheck: 'false',
|
|
535
|
+
'aria-autocomplete': 'list',
|
|
536
|
+
'aria-haspopup': 'listbox',
|
|
537
|
+
'[attr.aria-expanded]': '_isExpanded()',
|
|
538
|
+
'[attr.disabled]': 'disabled() ? "" : null',
|
|
539
|
+
'(keydown)': 'onKeyDown($event)',
|
|
540
|
+
'(input)': 'onInput($event)',
|
|
541
|
+
},
|
|
542
|
+
}]
|
|
543
|
+
}], ctorParameters: () => [] });
|
|
544
|
+
|
|
545
|
+
class BrnComboboxItem {
|
|
546
|
+
static _id = 0;
|
|
547
|
+
_platform = inject(PLATFORM_ID);
|
|
548
|
+
_elementRef = inject(ElementRef);
|
|
549
|
+
/** Access the combobox component */
|
|
550
|
+
_combobox = injectBrnComboboxBase();
|
|
551
|
+
/** A unique id for the item */
|
|
552
|
+
id = input(`brn-combobox-item-${++BrnComboboxItem._id}`);
|
|
553
|
+
/** The value this item represents. */
|
|
554
|
+
value = input.required();
|
|
555
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
556
|
+
_disabled = input(false, {
|
|
557
|
+
alias: 'disabled',
|
|
558
|
+
transform: booleanAttribute,
|
|
559
|
+
});
|
|
560
|
+
/** Expose disabled as a value - used by the Highlightable interface */
|
|
561
|
+
get disabled() {
|
|
562
|
+
return this._disabled();
|
|
563
|
+
}
|
|
564
|
+
/** Whether the item is selected. */
|
|
565
|
+
active = computed(() => this._combobox.isSelected(this.value()));
|
|
566
|
+
_highlighted = signal(false);
|
|
567
|
+
/** @internal Determine if this item is visible based on the current search query */
|
|
568
|
+
visible = computed(() => {
|
|
569
|
+
return this._combobox.filter()(this.value(), this._combobox.search(), this._combobox.collator(), this._combobox.itemToString());
|
|
570
|
+
});
|
|
571
|
+
setActiveStyles() {
|
|
572
|
+
this._highlighted.set(true);
|
|
573
|
+
// ensure the item is in view
|
|
574
|
+
if (isPlatformBrowser(this._platform)) {
|
|
575
|
+
this._elementRef.nativeElement.scrollIntoView({ block: 'nearest' });
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
setInactiveStyles() {
|
|
579
|
+
this._highlighted.set(false);
|
|
580
|
+
}
|
|
581
|
+
getLabel() {
|
|
582
|
+
return stringifyAsLabel(this.value(), this._combobox.itemToString());
|
|
583
|
+
}
|
|
584
|
+
select() {
|
|
585
|
+
if (this._disabled()) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
this._combobox.keyManager.setActiveItem(this);
|
|
589
|
+
this._combobox.select(this.value());
|
|
590
|
+
}
|
|
591
|
+
activate() {
|
|
592
|
+
if (this._disabled()) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
this._combobox.keyManager.setActiveItem(this);
|
|
596
|
+
}
|
|
597
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
598
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnComboboxItem, isStandalone: true, selector: "[brnComboboxItem]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, _disabled: { classPropertyName: "_disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "click": "select()", "mouseenter": "activate()" }, properties: { "id": "id()", "attr.data-highlighted": "_highlighted() ? \"\" : null", "attr.data-value": "value()", "attr.data-hidden": "!visible() ? '' : null", "attr.aria-selected": "active()", "attr.aria-disabled": "_disabled()" } }, providers: [provideBrnComboboxItem(BrnComboboxItem)], ngImport: i0 });
|
|
599
|
+
}
|
|
600
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxItem, decorators: [{
|
|
601
|
+
type: Directive,
|
|
602
|
+
args: [{
|
|
603
|
+
selector: '[brnComboboxItem]',
|
|
604
|
+
providers: [provideBrnComboboxItem(BrnComboboxItem)],
|
|
605
|
+
host: {
|
|
606
|
+
role: 'option',
|
|
607
|
+
'[id]': 'id()',
|
|
608
|
+
'[attr.data-highlighted]': '_highlighted() ? "" : null',
|
|
609
|
+
'[attr.data-value]': 'value()',
|
|
610
|
+
'[attr.data-hidden]': "!visible() ? '' : null",
|
|
611
|
+
'[attr.aria-selected]': 'active()',
|
|
612
|
+
'[attr.aria-disabled]': '_disabled()',
|
|
613
|
+
'(click)': 'select()',
|
|
614
|
+
'(mouseenter)': 'activate()',
|
|
615
|
+
},
|
|
616
|
+
}]
|
|
617
|
+
}] });
|
|
618
|
+
|
|
619
|
+
class BrnComboboxList {
|
|
620
|
+
static _id = 0;
|
|
621
|
+
_combobox = injectBrnComboboxBase();
|
|
622
|
+
/** Determine if the combobox has any visible items */
|
|
623
|
+
_visibleItems = this._combobox.visibleItems;
|
|
624
|
+
/** The id of the combobox list */
|
|
625
|
+
id = input(`brn-combobox-list-${++BrnComboboxList._id}`);
|
|
626
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxList, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
627
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnComboboxList, isStandalone: true, selector: "[brnComboboxList]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listbox", "tabIndex": "-1", "aria-orientation": "vertical" }, properties: { "id": "id()", "attr.data-empty": "!_visibleItems() ? \"\" : null" } }, ngImport: i0 });
|
|
628
|
+
}
|
|
629
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxList, decorators: [{
|
|
630
|
+
type: Directive,
|
|
631
|
+
args: [{
|
|
632
|
+
selector: '[brnComboboxList]',
|
|
633
|
+
host: {
|
|
634
|
+
role: 'listbox',
|
|
635
|
+
tabIndex: '-1',
|
|
636
|
+
'aria-orientation': 'vertical',
|
|
637
|
+
'[id]': 'id()',
|
|
638
|
+
'[attr.data-empty]': '!_visibleItems() ? "" : null',
|
|
639
|
+
},
|
|
640
|
+
}]
|
|
641
|
+
}] });
|
|
642
|
+
|
|
643
|
+
const BRN_COMBOBOX_MULTIPLE_VALUE_ACCESSOR = {
|
|
644
|
+
provide: NG_VALUE_ACCESSOR,
|
|
645
|
+
useExisting: forwardRef(() => BrnComboboxMultiple),
|
|
646
|
+
multi: true,
|
|
647
|
+
};
|
|
648
|
+
class BrnComboboxMultiple {
|
|
649
|
+
_injector = inject(Injector);
|
|
650
|
+
_config = injectBrnComboboxConfig();
|
|
651
|
+
/** Access the popover if present */
|
|
652
|
+
_brnPopover = inject(BrnPopover, { optional: true });
|
|
653
|
+
/** Whether the combobox is disabled */
|
|
654
|
+
disabled = input(false, { transform: booleanAttribute });
|
|
655
|
+
_disabled = linkedSignal(this.disabled);
|
|
656
|
+
/** @internal The disabled state as a readonly signal */
|
|
657
|
+
disabledState = this._disabled.asReadonly();
|
|
658
|
+
/** Options for filtering the combobox items */
|
|
659
|
+
filterOptions = input({});
|
|
660
|
+
/** @internal The collator used for string comparison */
|
|
661
|
+
collator = computed(() => {
|
|
662
|
+
const options = this.filterOptions();
|
|
663
|
+
const mergedOptions = { ...this._config.filterOptions, ...options };
|
|
664
|
+
return new Intl.Collator(options.locale, mergedOptions);
|
|
665
|
+
});
|
|
666
|
+
/** A function to compare an item with the selected value. */
|
|
667
|
+
isItemEqualToValue = input(this._config.isItemEqualToValue);
|
|
668
|
+
/** A function to convert an item to a string for display. */
|
|
669
|
+
itemToString = input(this._config.itemToString);
|
|
670
|
+
/** A custom filter function to use when searching. */
|
|
671
|
+
filter = input(this._config.filter);
|
|
672
|
+
/** The selected values of the combobox. */
|
|
673
|
+
value = model(null);
|
|
674
|
+
/** The current search query. */
|
|
675
|
+
search = signal('');
|
|
676
|
+
_searchInputWrapper = contentChild(BrnComboboxInputWrapper, {
|
|
677
|
+
read: ElementRef,
|
|
678
|
+
});
|
|
679
|
+
searchInputWrapperWidth = computed(() => {
|
|
680
|
+
const inputElement = this._searchInputWrapper()?.nativeElement;
|
|
681
|
+
return inputElement ? inputElement.offsetWidth : null;
|
|
682
|
+
});
|
|
683
|
+
/** @internal Access all the items within the combobox */
|
|
684
|
+
items = contentChildren(BrnComboboxItemToken, {
|
|
685
|
+
descendants: true,
|
|
686
|
+
});
|
|
687
|
+
/** Determine if the combobox has any visible items */
|
|
688
|
+
visibleItems = computed(() => this.items().some((item) => item.visible()));
|
|
689
|
+
/** @internal The key manager for managing active descendant */
|
|
690
|
+
keyManager = new ActiveDescendantKeyManager(this.items, this._injector);
|
|
691
|
+
/** @internal Whether the autocomplete is expanded */
|
|
692
|
+
isExpanded = computed(() => this._brnPopover?.stateComputed() === 'open');
|
|
693
|
+
_onChange;
|
|
694
|
+
_onTouched;
|
|
695
|
+
constructor() {
|
|
696
|
+
this.keyManager
|
|
697
|
+
.withVerticalOrientation()
|
|
698
|
+
.withHomeAndEnd()
|
|
699
|
+
.withWrap()
|
|
700
|
+
.skipPredicate((item) => item.disabled || !item.visible());
|
|
701
|
+
this._brnPopover?.closed.subscribe(() => {
|
|
702
|
+
this.search.set('');
|
|
703
|
+
this.keyManager.setActiveItem(-1);
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
isSelected(itemValue) {
|
|
707
|
+
return this.value()?.some((v) => this.isItemEqualToValue()(itemValue, v)) ?? false;
|
|
708
|
+
}
|
|
709
|
+
select(itemValue) {
|
|
710
|
+
const selected = this.value() ?? [];
|
|
711
|
+
if (this.isSelected(itemValue)) {
|
|
712
|
+
this.value.set(selected.filter((d) => !this.isItemEqualToValue()(d, itemValue)) ?? []);
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
this.value.set([...selected, itemValue]);
|
|
716
|
+
}
|
|
717
|
+
if (this.search() !== '') {
|
|
718
|
+
// reset search after selection and close popover
|
|
719
|
+
this.search.set('');
|
|
720
|
+
this.close();
|
|
721
|
+
}
|
|
722
|
+
this._onChange?.(this.value() ?? []);
|
|
723
|
+
}
|
|
724
|
+
/** Select the active item with Enter key. */
|
|
725
|
+
selectActiveItem() {
|
|
726
|
+
if (!this.isExpanded())
|
|
727
|
+
return;
|
|
728
|
+
const value = this.keyManager.activeItem?.value();
|
|
729
|
+
if (value === undefined)
|
|
730
|
+
return;
|
|
731
|
+
this.select(value);
|
|
732
|
+
}
|
|
733
|
+
resetValue() {
|
|
734
|
+
this.value.set(null);
|
|
735
|
+
this._onChange?.(null);
|
|
736
|
+
}
|
|
737
|
+
removeValue(itemValue) {
|
|
738
|
+
const selected = this.value() ?? [];
|
|
739
|
+
this.value.set(selected.filter((value) => !this.isItemEqualToValue()(itemValue, value)) ?? []);
|
|
740
|
+
this._onChange?.(this.value());
|
|
741
|
+
}
|
|
742
|
+
removeLastSelectedItem() {
|
|
743
|
+
const selected = this.value() ?? [];
|
|
744
|
+
if (selected.length === 0)
|
|
745
|
+
return;
|
|
746
|
+
selected.pop();
|
|
747
|
+
this.value.set([...selected]);
|
|
748
|
+
this._onChange?.(this.value());
|
|
749
|
+
}
|
|
750
|
+
open() {
|
|
751
|
+
if (this._disabled() || this.isExpanded())
|
|
752
|
+
return;
|
|
753
|
+
this._brnPopover?.open();
|
|
754
|
+
}
|
|
755
|
+
close() {
|
|
756
|
+
if (this._disabled() || !this.isExpanded())
|
|
757
|
+
return;
|
|
758
|
+
this._brnPopover?.close();
|
|
759
|
+
}
|
|
760
|
+
/** CONTROL VALUE ACCESSOR */
|
|
761
|
+
writeValue(value) {
|
|
762
|
+
this.value.set(value);
|
|
763
|
+
}
|
|
764
|
+
registerOnChange(fn) {
|
|
765
|
+
this._onChange = fn;
|
|
766
|
+
}
|
|
767
|
+
registerOnTouched(fn) {
|
|
768
|
+
this._onTouched = fn;
|
|
769
|
+
}
|
|
770
|
+
setDisabledState(isDisabled) {
|
|
771
|
+
this._disabled.set(isDisabled);
|
|
772
|
+
}
|
|
773
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxMultiple, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
774
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.0.7", type: BrnComboboxMultiple, isStandalone: true, selector: "[brnCombobox]", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, filterOptions: { classPropertyName: "filterOptions", publicName: "filterOptions", isSignal: true, isRequired: false, transformFunction: null }, isItemEqualToValue: { classPropertyName: "isItemEqualToValue", publicName: "isItemEqualToValue", isSignal: true, isRequired: false, transformFunction: null }, itemToString: { classPropertyName: "itemToString", publicName: "itemToString", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [provideBrnComboboxBase(BrnComboboxMultiple), BRN_COMBOBOX_MULTIPLE_VALUE_ACCESSOR], queries: [{ propertyName: "_searchInputWrapper", first: true, predicate: BrnComboboxInputWrapper, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "items", predicate: BrnComboboxItemToken, descendants: true, isSignal: true }], ngImport: i0 });
|
|
775
|
+
}
|
|
776
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxMultiple, decorators: [{
|
|
777
|
+
type: Directive,
|
|
778
|
+
args: [{
|
|
779
|
+
selector: '[brnCombobox]',
|
|
780
|
+
providers: [provideBrnComboboxBase(BrnComboboxMultiple), BRN_COMBOBOX_MULTIPLE_VALUE_ACCESSOR],
|
|
781
|
+
}]
|
|
782
|
+
}], ctorParameters: () => [] });
|
|
783
|
+
|
|
784
|
+
class BrnComboboxPopoverTrigger {
|
|
785
|
+
_combobox = injectBrnComboboxBase();
|
|
786
|
+
_brnDialog = inject(BrnDialog, { optional: true });
|
|
787
|
+
open() {
|
|
788
|
+
if (this._combobox.disabledState())
|
|
789
|
+
return;
|
|
790
|
+
this._brnDialog?.open();
|
|
791
|
+
}
|
|
792
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxPopoverTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
793
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxPopoverTrigger, isStandalone: true, selector: "[brnComboboxPopoverTrigger]", host: { listeners: { "click": "open()" } }, ngImport: i0 });
|
|
794
|
+
}
|
|
795
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxPopoverTrigger, decorators: [{
|
|
796
|
+
type: Directive,
|
|
797
|
+
args: [{
|
|
798
|
+
selector: '[brnComboboxPopoverTrigger]',
|
|
799
|
+
host: {
|
|
800
|
+
'(click)': 'open()',
|
|
801
|
+
},
|
|
802
|
+
}]
|
|
803
|
+
}] });
|
|
804
|
+
|
|
805
|
+
class BrnComboboxSeparator {
|
|
806
|
+
orientation = input('horizontal');
|
|
807
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
808
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.0.7", type: BrnComboboxSeparator, isStandalone: true, selector: "[brnComboboxSeparator]", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "attr.aria-orientation": "orientation()", "attr.data-orientation": "orientation()" } }, ngImport: i0 });
|
|
809
|
+
}
|
|
810
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxSeparator, decorators: [{
|
|
811
|
+
type: Directive,
|
|
812
|
+
args: [{
|
|
813
|
+
selector: '[brnComboboxSeparator]',
|
|
814
|
+
host: {
|
|
815
|
+
role: 'separator',
|
|
816
|
+
'[attr.aria-orientation]': 'orientation()',
|
|
817
|
+
'[attr.data-orientation]': 'orientation()',
|
|
818
|
+
},
|
|
819
|
+
}]
|
|
820
|
+
}] });
|
|
821
|
+
|
|
822
|
+
class BrnComboboxTrigger {
|
|
823
|
+
_combobox = injectBrnComboboxBase();
|
|
824
|
+
_disabled = this._combobox.disabledState;
|
|
825
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
826
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxTrigger, isStandalone: true, selector: "button[brnComboboxTrigger]", host: { attributes: { "type": "button" }, properties: { "attr.aria-disabled": "_disabled() ? \"true\" : null", "attr.disabled": "_disabled() ? \"\" : null" } }, ngImport: i0 });
|
|
827
|
+
}
|
|
828
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxTrigger, decorators: [{
|
|
829
|
+
type: Directive,
|
|
830
|
+
args: [{
|
|
831
|
+
selector: 'button[brnComboboxTrigger]',
|
|
832
|
+
host: {
|
|
833
|
+
type: 'button',
|
|
834
|
+
'[attr.aria-disabled]': '_disabled() ? "true" : null',
|
|
835
|
+
'[attr.disabled]': '_disabled() ? "" : null',
|
|
836
|
+
},
|
|
837
|
+
}]
|
|
838
|
+
}] });
|
|
839
|
+
|
|
840
|
+
class BrnComboboxValue {
|
|
841
|
+
_combobox = injectBrnComboboxBase();
|
|
842
|
+
_value = computed(() => stringifyAsLabel(this._combobox.value(), this._combobox.itemToString()));
|
|
843
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxValue, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
844
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxValue, isStandalone: true, selector: "[brnComboboxValue]", host: { properties: { "textContent": "_value()" } }, ngImport: i0 });
|
|
845
|
+
}
|
|
846
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxValue, decorators: [{
|
|
847
|
+
type: Directive,
|
|
848
|
+
args: [{
|
|
849
|
+
selector: '[brnComboboxValue]',
|
|
850
|
+
host: {
|
|
851
|
+
'[textContent]': '_value()',
|
|
852
|
+
},
|
|
853
|
+
}]
|
|
854
|
+
}] });
|
|
855
|
+
|
|
856
|
+
class BrnComboboxValues {
|
|
857
|
+
_templateRef = inject((TemplateRef));
|
|
858
|
+
_viewContainerRef = inject(ViewContainerRef);
|
|
859
|
+
_combobox = injectBrnComboboxBase();
|
|
860
|
+
_values = computed(() => {
|
|
861
|
+
const values = this._combobox.value();
|
|
862
|
+
if (values == null) {
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
return Array.isArray(values) ? values : [values];
|
|
866
|
+
});
|
|
867
|
+
constructor() {
|
|
868
|
+
effect(() => {
|
|
869
|
+
const values = this._values();
|
|
870
|
+
if (values?.length) {
|
|
871
|
+
this._viewContainerRef.clear();
|
|
872
|
+
this._viewContainerRef.createEmbeddedView(this._templateRef, {
|
|
873
|
+
$implicit: values,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
this._viewContainerRef.clear();
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxValues, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
882
|
+
/** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.7", type: BrnComboboxValues, isStandalone: true, selector: "[brnComboboxValues]", ngImport: i0 });
|
|
883
|
+
}
|
|
884
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.7", ngImport: i0, type: BrnComboboxValues, decorators: [{
|
|
885
|
+
type: Directive,
|
|
886
|
+
args: [{
|
|
887
|
+
selector: '[brnComboboxValues]',
|
|
888
|
+
}]
|
|
889
|
+
}], ctorParameters: () => [] });
|
|
890
|
+
|
|
891
|
+
const BrnComboboxImports = [
|
|
892
|
+
BrnCombobox,
|
|
893
|
+
BrnComboboxAnchor,
|
|
894
|
+
BrnComboboxChip,
|
|
895
|
+
BrnComboboxChipInput,
|
|
896
|
+
BrnComboboxChipRemove,
|
|
897
|
+
BrnComboboxClear,
|
|
898
|
+
BrnComboboxContent,
|
|
899
|
+
BrnComboboxEmpty,
|
|
900
|
+
BrnComboboxGroup,
|
|
901
|
+
BrnComboboxInputWrapper,
|
|
902
|
+
BrnComboboxInput,
|
|
903
|
+
BrnComboboxItem,
|
|
904
|
+
BrnComboboxLabel,
|
|
905
|
+
BrnComboboxList,
|
|
906
|
+
BrnComboboxMultiple,
|
|
907
|
+
BrnComboboxPopoverTrigger,
|
|
908
|
+
BrnComboboxSeparator,
|
|
909
|
+
BrnComboboxTrigger,
|
|
910
|
+
BrnComboboxValue,
|
|
911
|
+
BrnComboboxValues,
|
|
912
|
+
];
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Generated bundle index. Do not edit.
|
|
916
|
+
*/
|
|
917
|
+
|
|
918
|
+
export { BRN_COMBOBOX_MULTIPLE_VALUE_ACCESSOR, BRN_COMBOBOX_VALUE_ACCESSOR, BrnCombobox, BrnComboboxAnchor, BrnComboboxBaseToken, BrnComboboxChip, BrnComboboxChipInput, BrnComboboxChipRemove, BrnComboboxClear, BrnComboboxContent, BrnComboboxEmpty, BrnComboboxGroup, BrnComboboxImports, BrnComboboxInput, BrnComboboxInputWrapper, BrnComboboxItem, BrnComboboxItemToken, BrnComboboxLabel, BrnComboboxList, BrnComboboxMultiple, BrnComboboxPopoverTrigger, BrnComboboxSeparator, BrnComboboxTrigger, BrnComboboxValue, BrnComboboxValues, comboboxContainsFilter, comboboxEndsWithFilter, comboboxStartsWithFilter, injectBrnComboboxBase, injectBrnComboboxConfig, provideBrnComboboxBase, provideBrnComboboxConfig, provideBrnComboboxItem };
|
|
919
|
+
//# sourceMappingURL=spartan-ng-brain-combobox.mjs.map
|