@bootkit/ng0 0.0.0-alpha.25 → 0.0.0-alpha.27

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,16 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, ElementRef, ViewEncapsulation, ChangeDetectionStrategy, Component, ChangeDetectorRef, signal, booleanAttribute, EventEmitter, effect, untracked, computed, TemplateRef, forwardRef, HostListener, Output, ContentChild, ViewChildren, NgModule } from '@angular/core';
2
+ import { input, inject, ElementRef, Directive, ChangeDetectorRef, signal, Renderer2, booleanAttribute, EventEmitter, effect, untracked, computed, TemplateRef, forwardRef, HostListener, Output, ViewChildren, ContentChild, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import { dataSourceAttribute, DataRequest } from '@bootkit/ng0/data';
6
6
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
7
- import { LocalizationService, defaultObjectFormatter, objectFormatterAttribute } from '@bootkit/ng0/localization';
7
+ import { LocalizationService, defaultFormatter, objectFormatterAttribute } from '@bootkit/ng0/localization';
8
8
  import { defaultEqualityComparer, equalityComparerAttribute, defaultValueWriter, valueWriterAttribute, noopFilter, filterPredicateAttribute, trackByIndex, TrackByAttribute, CssClassAttribute, IdGeneratorAttribute, IfDirective } from '@bootkit/ng0/common';
9
9
 
10
10
  /**
11
- * ListItemComponent represents an individual item within a ListComponent.
11
+ * ListItem represents an individual item within a ListComponent.
12
12
  */
13
- class ListItemComponent {
13
+ class ListItem {
14
14
  /**
15
15
  * The value associated with the item. This can be of any type.
16
16
  */
@@ -83,7 +83,7 @@ class ListItemComponent {
83
83
  this.elementRef.nativeElement.focus();
84
84
  }
85
85
  _getTabIndex() {
86
- let focus = this.list.focus();
86
+ let focus = this.list.focusMode();
87
87
  // if (this.list.isDisabled()) {
88
88
  // return undefined;
89
89
  // }
@@ -95,22 +95,22 @@ class ListItemComponent {
95
95
  return this.isActive() ? 0 : -1;
96
96
  }
97
97
  }
98
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
99
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: ListItemComponent, isStandalone: true, selector: "ng0-list-item", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.active": "isActive()", "class.selected": "isSelected()", "attr.id": "id()", "attr.tabIndex": "_getTabIndex()" } }, exportAs: ["ng0ListItem"], ngImport: i0, template: "@let formatter = list.formatBy();\r\n\r\n@if(list.itemTemplate) {\r\n<ng-container *ngTemplateOutlet=\"list.itemTemplate; context: { $implicit: {ref: this, value: value()}}\" />\r\n} @else {\r\n@if(list.showSelectionIndicator()) {\r\n<input class=\"form-check-input ng0-list-selection-indicator\"\r\n tabindex=\"-1\"\r\n [checked]=\"isSelected()\"\r\n [attr.type]=\"list.multiple() ? 'checkbox' : 'radio'\">\r\n}\r\n\r\n{{formatter(value())}}\r\n}", styles: ["ng0-list-item{display:flex;padding:.5rem}ng0-list-item .ng0-list-selection-indicator{margin-inline-end:.5rem}ng0-list-item.selected{background-color:var(--bs-primary);color:var(--bs-light)}ng0-list-item.active:not(.selected){background-color:color-mix(in srgb,var(--bs-primary),white 85%)}ng0-list-item:hover:not(.selected):not(.disabled):not(.active){background-color:color-mix(in srgb,var(--bs-primary),white 95%)}ng0-list-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}ng0-list-item:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}ng0-list-item:focus,ng0-list-item:focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
98
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ListItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
99
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.2.1", type: ListItem, isStandalone: true, selector: "ng0-list-item", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.id": "id()", "attr.tabIndex": "_getTabIndex()" } }, exportAs: ["ng0ListItem"], ngImport: i0 });
100
100
  }
101
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ListItemComponent, decorators: [{
102
- type: Component,
103
- args: [{ selector: 'ng0-list-item', exportAs: 'ng0ListItem', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
104
- CommonModule,
105
- ], host: {
106
- '[class.active]': 'isActive()',
107
- '[class.selected]': 'isSelected()',
101
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ListItem, decorators: [{
102
+ type: Directive,
103
+ args: [{
104
+ selector: 'ng0-list-item',
105
+ exportAs: 'ng0ListItem',
106
+ standalone: true,
107
+ host: {
108
108
  '[attr.id]': 'id()',
109
109
  '[attr.tabIndex]': '_getTabIndex()'
110
- }, template: "@let formatter = list.formatBy();\r\n\r\n@if(list.itemTemplate) {\r\n<ng-container *ngTemplateOutlet=\"list.itemTemplate; context: { $implicit: {ref: this, value: value()}}\" />\r\n} @else {\r\n@if(list.showSelectionIndicator()) {\r\n<input class=\"form-check-input ng0-list-selection-indicator\"\r\n tabindex=\"-1\"\r\n [checked]=\"isSelected()\"\r\n [attr.type]=\"list.multiple() ? 'checkbox' : 'radio'\">\r\n}\r\n\r\n{{formatter(value())}}\r\n}", styles: ["ng0-list-item{display:flex;padding:.5rem}ng0-list-item .ng0-list-selection-indicator{margin-inline-end:.5rem}ng0-list-item.selected{background-color:var(--bs-primary);color:var(--bs-light)}ng0-list-item.active:not(.selected){background-color:color-mix(in srgb,var(--bs-primary),white 85%)}ng0-list-item:hover:not(.selected):not(.disabled):not(.active){background-color:color-mix(in srgb,var(--bs-primary),white 95%)}ng0-list-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}ng0-list-item:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}ng0-list-item:focus,ng0-list-item:focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}\n"] }]
110
+ }
111
+ }]
111
112
  }] });
112
113
 
113
- let uuid = 0;
114
114
  /**
115
115
  * ListComponent is a versatile component that displays a list of items with support for single or multiple selection,
116
116
  * custom item templates, filtering, and keyboard navigation.
@@ -118,15 +118,19 @@ let uuid = 0;
118
118
  class ListComponent {
119
119
  _localizationService = inject(LocalizationService);
120
120
  _changeDetector = inject(ChangeDetectorRef);
121
- _value = signal(undefined, ...(ngDevMode ? [{ debugName: "_value" }] : []));
122
121
  _changeCallback;
123
122
  _touchCallback;
123
+ _selectedItems = new Set();
124
124
  _sourceItems = signal([], ...(ngDevMode ? [{ debugName: "_sourceItems" }] : []));
125
- _selectedValues = new Set();
125
+ _itemTemplate;
126
126
  _activeItem = signal(undefined, ...(ngDevMode ? [{ debugName: "_activeItem" }] : []));
127
- _visibleItems;
128
127
  _isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_isDisabled" }] : []));
129
- itemTemplate;
128
+ _value = signal(undefined, ...(ngDevMode ? [{ debugName: "_value" }] : []));
129
+ _renderer = inject(Renderer2);
130
+ /**
131
+ * A list of all visible list items.
132
+ */
133
+ listItems;
130
134
  /**
131
135
  * Reference to the host element
132
136
  */
@@ -153,28 +157,28 @@ class ListComponent {
153
157
  transform: booleanAttribute
154
158
  }]));
155
159
  /**
156
- * A custom comparer function or the name of a field for comparing two objects.
160
+ * A comparer to compare items for selection.
161
+ * Default uses strict equality (===).
157
162
  */
158
163
  compareBy = input(defaultEqualityComparer, ...(ngDevMode ? [{ debugName: "compareBy", transform: equalityComparerAttribute }] : [{
159
164
  transform: equalityComparerAttribute
160
165
  }]));
161
166
  /**
162
- * Custom format function to convert an item to a string for display.
167
+ * A fromatter to convert each item to a string for display.
163
168
  * Default converts the item to a string using its toString method.
164
169
  */
165
- formatBy = input(defaultObjectFormatter, ...(ngDevMode ? [{ debugName: "formatBy", transform: objectFormatterAttribute(this._localizationService.get()) }] : [{
170
+ formatBy = input(defaultFormatter, ...(ngDevMode ? [{ debugName: "formatBy", transform: objectFormatterAttribute(this._localizationService.get()) }] : [{
166
171
  transform: objectFormatterAttribute(this._localizationService.get())
167
172
  }]));
168
173
  /**
169
- * Custom value extractor function to extract the value of any object while writing values.
174
+ * Custom value writer to extract the value of any object while writing values.
170
175
  */
171
176
  writeBy = input(defaultValueWriter, ...(ngDevMode ? [{ debugName: "writeBy", transform: valueWriterAttribute }] : [{
172
177
  transform: valueWriterAttribute
173
178
  }]));
174
179
  /**
175
- * A custom filter predicate function to filter items based on a search string.
176
- * Default checks if the item's string representation contains the filter string (case-insensitive).
177
- * The filter predicate can be a function or a string representing the property name to filter.
180
+ * Custom filter function to filter items.
181
+ * Default is a noop filter that does not filter any items.
178
182
  */
179
183
  filterBy = input(noopFilter, ...(ngDevMode ? [{ debugName: "filterBy", transform: filterPredicateAttribute }] : [{
180
184
  transform: filterPredicateAttribute
@@ -200,7 +204,7 @@ class ListComponent {
200
204
  * CSS class or classes to apply to the list container.
201
205
  * Default is undefined.
202
206
  */
203
- itemClass = input((item) => ['ng0-list-item'], ...(ngDevMode ? [{ debugName: "itemClass", transform: CssClassAttribute }] : [{
207
+ itemClass = input(undefined, ...(ngDevMode ? [{ debugName: "itemClass", transform: CssClassAttribute }] : [{
204
208
  transform: CssClassAttribute
205
209
  }]));
206
210
  /**
@@ -210,7 +214,7 @@ class ListComponent {
210
214
  * - 'activeDescendant': The list can be focused, but no item is tabbable. The active item is indicated using aria-activedescendant.
211
215
  * @default 'activeDescendant'.
212
216
  */
213
- focus = input('activeDescendant', ...(ngDevMode ? [{ debugName: "focus" }] : []));
217
+ focusMode = input('activeDescendant', ...(ngDevMode ? [{ debugName: "focusMode" }] : []));
214
218
  /**
215
219
  * A function that generates unique ids for each item in the list.
216
220
  * If set to a function, it will be called with the item as an argument to generate the id.
@@ -224,16 +228,18 @@ class ListComponent {
224
228
  * Event emitted when the selection state of an item changes by user interaction.
225
229
  */
226
230
  selectionChange = new EventEmitter();
227
- _uuid;
231
+ /**
232
+ * The template to use for each item in the list.
233
+ */
234
+ itemTemplate = input(undefined, ...(ngDevMode ? [{ debugName: "itemTemplate" }] : []));
228
235
  constructor() {
229
- this._uuid = ++uuid;
230
236
  effect(() => {
231
- let source = this.source();
232
- source.load(new DataRequest()).subscribe(res => {
237
+ this.source().load(new DataRequest()).subscribe(res => {
233
238
  untracked(() => {
234
239
  this._activeItem.set(undefined);
235
240
  this._sourceItems.set(res.data);
236
- this._findAndSelectItems();
241
+ this._updateSelectedItems();
242
+ this._activateFirstSelectedItem();
237
243
  });
238
244
  });
239
245
  });
@@ -252,15 +258,24 @@ class ListComponent {
252
258
  * @returns
253
259
  */
254
260
  isSelected(value) {
255
- return this._selectedValues.has(value);
261
+ return this._selectedItems.has(value);
256
262
  }
257
263
  /**
258
264
  * Selects the given value.
259
265
  * @param item
260
266
  */
261
267
  select(value) {
262
- if (!this._selectedValues.has(value())) {
263
- this._selectedValues.add(value());
268
+ if (this.multiple()) {
269
+ if (!this._selectedItems.has(value)) {
270
+ this._selectedItems.add(value);
271
+ this._updateValue();
272
+ this._changeCallback?.(this._value());
273
+ }
274
+ }
275
+ else {
276
+ this._selectedItems.clear();
277
+ this._selectedItems.add(value);
278
+ this._updateValue();
264
279
  this._changeCallback?.(this._value());
265
280
  }
266
281
  }
@@ -269,7 +284,8 @@ class ListComponent {
269
284
  * @param item
270
285
  */
271
286
  deselect(value) {
272
- this._selectedValues.delete(value);
287
+ this._selectedItems.delete(value);
288
+ this._updateValue();
273
289
  this._changeCallback?.(this._value());
274
290
  }
275
291
  /**
@@ -288,7 +304,8 @@ class ListComponent {
288
304
  * Deselects all items in the list.
289
305
  */
290
306
  deselectAll() {
291
- this._selectedValues.clear();
307
+ this._selectedItems.clear();
308
+ this._updateValue();
292
309
  this._changeCallback?.(this._value());
293
310
  }
294
311
  /**
@@ -296,11 +313,26 @@ class ListComponent {
296
313
  */
297
314
  selectAll() {
298
315
  if (this.multiple()) {
299
- this._selectedValues.clear();
300
- this._sourceItems().forEach(i => this._selectedValues.add(i));
316
+ this._selectedItems.clear();
317
+ this._sourceItems().forEach(i => this._selectedItems.add(i));
318
+ this._updateValue();
301
319
  this._changeCallback?.(this._value());
302
320
  }
303
- throw new Error('selectAll is only available in multiple selection mode.');
321
+ else {
322
+ throw new Error('selectAll is only available in multiple selection mode.');
323
+ }
324
+ }
325
+ /**
326
+ * Gets the current value(s) of the list.
327
+ */
328
+ get value() {
329
+ return this._value();
330
+ }
331
+ /**
332
+ * Gets the current items in the list.
333
+ */
334
+ get items() {
335
+ return this._sourceItems();
304
336
  }
305
337
  writeValue(value) {
306
338
  if (this.multiple()) {
@@ -312,7 +344,8 @@ class ListComponent {
312
344
  }
313
345
  }
314
346
  this._value.set(value);
315
- this._findAndSelectItems();
347
+ this._updateSelectedItems();
348
+ this._activateFirstSelectedItem();
316
349
  this._changeDetector.markForCheck();
317
350
  }
318
351
  registerOnChange(fn) {
@@ -324,43 +357,34 @@ class ListComponent {
324
357
  setDisabledState(isDisabled) {
325
358
  this._isDisabled.set(isDisabled);
326
359
  }
327
- _handleUserSelection(item) {
360
+ _handleUserSelection(item, index) {
328
361
  let value = item.value();
329
- this._activeItem.set(item);
330
362
  if (this.multiple()) {
331
- if (this.isSelected(value)) {
332
- this._selectedValues.delete(value);
333
- }
334
- else {
335
- this._selectedValues.add(value);
336
- }
363
+ this.toggle(value);
337
364
  }
338
365
  else {
339
- this._selectedValues.clear();
340
- this._selectedValues.add(value);
366
+ this.select(value);
341
367
  }
342
- this._updateValue();
343
- this._changeCallback?.(this._value());
344
- this.selectionChange.emit({
345
- item: item,
346
- list: this,
347
- });
368
+ this._activeItem.set(item);
369
+ this.selectionChange.emit({ index, value: item.value(), item: item, list: this });
348
370
  this._changeDetector.detectChanges();
349
371
  }
350
372
  _showLoadingSppiner = computed(() => {
351
373
  let source = this.source();
352
374
  return source.isLoading() && source.type == 'remote';
353
375
  }, ...(ngDevMode ? [{ debugName: "_showLoadingSppiner" }] : []));
354
- _findAndSelectItems() {
376
+ _updateSelectedItems() {
355
377
  let value = this._value();
356
378
  let compareBy = this.compareBy();
357
379
  let findAndSelect = (v) => {
358
380
  let index = this._sourceItems().findIndex(i => compareBy(i, v));
359
381
  if (index > -1) {
360
382
  let item = this._sourceItems().at(index);
361
- this._selectedValues.add(item);
383
+ this._selectedItems.add(item);
362
384
  }
385
+ return index;
363
386
  };
387
+ this._selectedItems.clear();
364
388
  if (this.multiple()) {
365
389
  if (Array.isArray(value)) {
366
390
  value.forEach(v => findAndSelect(v));
@@ -369,39 +393,49 @@ class ListComponent {
369
393
  else {
370
394
  findAndSelect(value);
371
395
  }
372
- this._changeDetector.markForCheck();
396
+ }
397
+ _activateFirstSelectedItem() {
398
+ if (this._selectedItems.size > 0) {
399
+ let value = this._selectedItems.values().next().value;
400
+ let item = this.listItems?.find(i => i.value() === value);
401
+ if (item) {
402
+ this._activeItem.set(item);
403
+ }
404
+ }
373
405
  }
374
406
  _updateValue() {
407
+ let value;
375
408
  if (this.multiple()) {
376
409
  let values = [];
377
- this._selectedValues.forEach(v => {
410
+ this._selectedItems.forEach(v => {
378
411
  values.push(this.writeBy()(v));
379
412
  });
380
- this._value.set(values);
413
+ value = values;
381
414
  }
382
415
  else {
383
- if (this._selectedValues.size > 0) {
384
- let first = this._selectedValues.values().next().value;
385
- this._value.set(this.writeBy()(first));
416
+ if (this._selectedItems.size > 0) {
417
+ let first = this._selectedItems.values().next().value;
418
+ value = this.writeBy()(first);
386
419
  }
387
420
  else {
388
- this._value.set(undefined);
421
+ value = undefined;
389
422
  }
390
423
  }
424
+ this._value.set(value);
391
425
  }
392
426
  _hostAriaActiveDescendant = computed(() => {
393
- if (this._activeItem() && !this._isDisabled() && this.focus() == 'activeDescendant') {
427
+ if (this._activeItem() && !this._isDisabled() && this.focusMode() == 'activeDescendant') {
394
428
  return this._activeItem().id();
395
429
  }
396
430
  return undefined;
397
431
  }, ...(ngDevMode ? [{ debugName: "_hostAriaActiveDescendant" }] : []));
398
432
  _hostTabIndex = computed(() => {
399
- let isDisabled = this._isDisabled(); // track _isDisabled
400
- let activeItem = this._activeItem(); // track _activeItem
433
+ let isDisabled = this._isDisabled(); // track the value
434
+ let activeItem = this._activeItem(); // track the value
401
435
  if (isDisabled) {
402
436
  return undefined;
403
437
  }
404
- switch (this.focus()) {
438
+ switch (this.focusMode()) {
405
439
  case 'none':
406
440
  return undefined;
407
441
  case 'activeDescendant':
@@ -410,79 +444,71 @@ class ListComponent {
410
444
  return activeItem ? undefined : 0;
411
445
  }
412
446
  }, ...(ngDevMode ? [{ debugName: "_hostTabIndex" }] : []));
413
- _onHostClick() {
414
- // if (this.focus() != 'none') {
415
- // this.elementRef.nativeElement.focus();
416
- // }
417
- }
418
447
  _onHostBlur() {
419
448
  this._touchCallback?.();
420
449
  }
421
450
  _onKeydown(e) {
422
451
  if (this._isDisabled())
423
452
  return;
424
- let visibleItemsCount = this._visibleItems.length;
425
- if (visibleItemsCount == 0) {
453
+ let listLength = this.listItems.length;
454
+ if (listLength == 0) {
426
455
  return;
427
456
  }
428
- let activeItemindex = this._visibleItems.toArray().findIndex(i => i === this._activeItem());
429
- console.log(this._activeItem(), activeItemindex);
457
+ let activeItem = this._activeItem();
458
+ let activeItemindex = activeItem ? this.listItems.toArray().findIndex(i => i === activeItem) : -1;
430
459
  switch (e.key) {
431
460
  case 'ArrowDown':
432
- if (activeItemindex == -1) {
433
- const first = this._visibleItems.get(0);
434
- this._activeItem.set(first);
435
- }
436
- else if (activeItemindex < visibleItemsCount - 1) {
437
- const next = this._visibleItems.get(activeItemindex + 1);
461
+ if (activeItemindex < listLength - 1) {
462
+ const next = this.listItems.get(activeItemindex + 1);
438
463
  this._activeItem.set(next);
439
464
  }
440
465
  e.preventDefault();
441
466
  break;
442
467
  case 'ArrowUp':
443
468
  if (activeItemindex == -1) {
444
- const last = this._visibleItems.get(visibleItemsCount - 1);
469
+ const last = this.listItems.get(listLength - 1);
445
470
  this._activeItem.set(last);
446
471
  }
447
472
  else if (activeItemindex > 0) {
448
- const previous = this._visibleItems.get(activeItemindex - 1);
473
+ const previous = this.listItems.get(activeItemindex - 1);
449
474
  this._activeItem.set(previous);
450
475
  }
451
476
  e.preventDefault();
452
477
  break;
453
478
  case 'Enter':
454
- if (activeItemindex > -1) {
455
- this._handleUserSelection(this._visibleItems.get(activeItemindex));
479
+ if (activeItem) {
480
+ this._handleUserSelection(activeItem, activeItemindex);
456
481
  }
457
482
  break;
458
483
  case 'Home':
459
- const first = this._visibleItems.get(0);
484
+ const first = this.listItems.get(0);
460
485
  this._activeItem.set(first);
461
486
  e.preventDefault();
462
487
  break;
463
488
  case 'End':
464
- const last = this._visibleItems.get(visibleItemsCount - 1);
489
+ const last = this.listItems.get(listLength - 1);
465
490
  this._activeItem.set(last);
466
491
  e.preventDefault();
467
492
  break;
468
493
  }
469
- if (this.focus() === 'roving') {
494
+ this._activeItem()?.scrollIntoView('nearest', listLength > 100 ? 'instant' : 'smooth');
495
+ if (this.focusMode() === 'roving') {
470
496
  this._activeItem()?.focus();
471
497
  }
472
498
  }
473
499
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
474
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: ListComponent, isStandalone: true, selector: "ng0-list", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showSelectionIndicator: { classPropertyName: "showSelectionIndicator", publicName: "showSelectionIndicator", isSignal: true, isRequired: false, transformFunction: null }, compareBy: { classPropertyName: "compareBy", publicName: "compareBy", isSignal: true, isRequired: false, transformFunction: null }, formatBy: { classPropertyName: "formatBy", publicName: "formatBy", isSignal: true, isRequired: false, transformFunction: null }, writeBy: { classPropertyName: "writeBy", publicName: "writeBy", isSignal: true, isRequired: false, transformFunction: null }, filterBy: { classPropertyName: "filterBy", publicName: "filterBy", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null }, focus: { classPropertyName: "focus", publicName: "focus", isSignal: true, isRequired: false, transformFunction: null }, idGenerator: { classPropertyName: "idGenerator", publicName: "idGenerator", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "click": "_onHostClick()", "blur": "_onHostBlur()", "keydown": "_onKeydown($event)" }, properties: { "class.ng0-list-loading": "source().isLoading()", "attr.aria-activedescendant": "_hostAriaActiveDescendant()", "attr.disabled": "_isDisabled() ? \"\" : undefined", "attr.aria-disabled": "_isDisabled() ? \"\" : undefined", "attr.tabindex": "_hostTabIndex()" } }, providers: [{
500
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.1", type: ListComponent, isStandalone: true, selector: "ng0-list, ng0-select-list", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, showSelectionIndicator: { classPropertyName: "showSelectionIndicator", publicName: "showSelectionIndicator", isSignal: true, isRequired: false, transformFunction: null }, compareBy: { classPropertyName: "compareBy", publicName: "compareBy", isSignal: true, isRequired: false, transformFunction: null }, formatBy: { classPropertyName: "formatBy", publicName: "formatBy", isSignal: true, isRequired: false, transformFunction: null }, writeBy: { classPropertyName: "writeBy", publicName: "writeBy", isSignal: true, isRequired: false, transformFunction: null }, filterBy: { classPropertyName: "filterBy", publicName: "filterBy", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, itemClass: { classPropertyName: "itemClass", publicName: "itemClass", isSignal: true, isRequired: false, transformFunction: null }, focusMode: { classPropertyName: "focusMode", publicName: "focusMode", isSignal: true, isRequired: false, transformFunction: null }, idGenerator: { classPropertyName: "idGenerator", publicName: "idGenerator", isSignal: true, isRequired: false, transformFunction: null }, itemTemplate: { classPropertyName: "itemTemplate", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "blur": "_onHostBlur()", "keydown": "_onKeydown($event)" }, properties: { "class.ng0-list-loading": "source().isLoading()", "attr.aria-activedescendant": "_hostAriaActiveDescendant()", "attr.disabled": "_isDisabled() ? \"\" : undefined", "attr.aria-disabled": "_isDisabled() ? \"\" : undefined", "attr.tabindex": "_hostTabIndex()" } }, providers: [{
475
501
  provide: NG_VALUE_ACCESSOR,
476
502
  useExisting: forwardRef(() => ListComponent),
477
503
  multi: true
478
- }], queries: [{ propertyName: "itemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "_visibleItems", predicate: ListItemComponent, descendants: true }], exportAs: ["ng0List"], ngImport: i0, template: "@let filter = filterBy();\r\n\r\n@for (item of _sourceItems(); track trackBy()($index, item)) {\r\n<ng0-list-item *ng0If=\"filter(item)\"\r\n #listItem=\"ng0ListItem\"\r\n [id]=\"idGenerator()?.(item)\"\r\n [value]=\"item\"\r\n [ngClass]=\"itemClass()(item)\"\r\n (click)=\"_handleUserSelection(listItem);\">\r\n</ng0-list-item>\r\n}\r\n\r\n@if(_showLoadingSppiner()) {\r\n@if(_sourceItems().length == 0) {\r\n<div style=\"text-align: center; padding: 0.5rem 0;\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}@else {\r\n<div class=\"ng0-list-loading-cover\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}\r\n}\r\n\r\n<ng-template #spinner>\r\n <div class=\"spinner-border spinner-sm text-primary ng0-list-loading-indicator\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading...</span>\r\n </div>\r\n</ng-template>", styles: ["ng0-list{display:block;position:relative;-webkit-user-select:none;user-select:none;border:1px solid var(--bs-border-color);border-radius:var(--bs-border-radius);min-height:1rem}ng0-list:focus,ng0-list:focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}ng0-list[disabled],ng0-list.disabled{opacity:.5;pointer-events:none}ng0-list .ng0-list-loading-cover{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffffb3;display:flex;align-items:center;justify-content:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: IfDirective, selector: "[ng0If]", inputs: ["ng0If"], exportAs: ["ng0If"] }, { kind: "component", type: ListItemComponent, selector: "ng0-list-item", inputs: ["value", "id"], exportAs: ["ng0ListItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
504
+ }], queries: [{ propertyName: "_itemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "listItems", predicate: ListItem, descendants: true }], exportAs: ["ng0List"], ngImport: i0, template: "@let filter = filterBy();\r\n@let formatter = formatBy();\r\n\r\n@for (item of _sourceItems(); track trackBy()($index, item)) {\r\n\r\n<ng0-list-item #listItem=\"ng0ListItem\"\r\n *ng0If=\"!filter || filter(item)\"\r\n [value]=\"item\"\r\n [id]=\"idGenerator()?.(item)\"\r\n [class.active]=\"isActive(listItem)\"\r\n [class.selected]=\"isSelected(item)\"\r\n [ngClass]=\"itemClass()?.(item)\"\r\n (click)=\"_handleUserSelection(listItem, $index);\">\r\n\r\n @if(itemTemplate() || _itemTemplate) {\r\n @let template = itemTemplate() || _itemTemplate;\r\n <ng-container *ngTemplateOutlet=\"template; context: { $implicit: {item: listItem, value: item}}\" />\r\n } @else {\r\n @if(showSelectionIndicator()) {\r\n <input class=\"form-check-input ng0-selection-indicator\"\r\n tabindex=\"-1\"\r\n [checked]=\"isSelected(item)\"\r\n [attr.type]=\"multiple() ? 'checkbox' : 'radio'\">\r\n }\r\n\r\n {{formatter(item)}}\r\n }\r\n</ng0-list-item>\r\n}\r\n\r\n@if(_showLoadingSppiner()) {\r\n@if(_sourceItems().length == 0) {\r\n<div style=\"text-align: center; padding: 0.5rem 0;\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}@else {\r\n<div class=\"ng0-list-loading-cover\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}\r\n}\r\n\r\n<ng-template #spinner>\r\n <div class=\"spinner-border spinner-sm text-primary ng0-list-loading-indicator\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading...</span>\r\n </div>\r\n</ng-template>", styles: ["ng0-list:not(.ng0-list-unstyled){display:block;position:relative;-webkit-user-select:none;user-select:none;border:1px solid var(--bs-border-color);border-radius:var(--bs-border-radius)}ng0-list:not(.ng0-list-unstyled):focus,ng0-list:not(.ng0-list-unstyled):focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}ng0-list:not(.ng0-list-unstyled)[disabled],ng0-list:not(.ng0-list-unstyled).disabled{opacity:.5;pointer-events:none}ng0-list:not(.ng0-list-unstyled) .ng0-list-loading-cover{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffffb3;display:flex;align-items:center;justify-content:center}ng0-list:not(.ng0-list-unstyled) ng0-list-item{display:flex;padding:.5rem}ng0-list:not(.ng0-list-unstyled) ng0-list-item .ng0-selection-indicator{margin-inline-end:.5rem}ng0-list:not(.ng0-list-unstyled) ng0-list-item:focus,ng0-list:not(.ng0-list-unstyled) ng0-list-item:focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}ng0-list:not(.ng0-list-unstyled) ng0-list-item.selected{background-color:var(--bs-primary);color:var(--bs-light)}ng0-list:not(.ng0-list-unstyled) ng0-list-item.active:not(.selected){background-color:color-mix(in srgb,var(--bs-primary),white 85%)}ng0-list:not(.ng0-list-unstyled) ng0-list-item:hover:not(.selected):not(.disabled):not(.active){background-color:color-mix(in srgb,var(--bs-primary),white 95%)}ng0-list:not(.ng0-list-unstyled) ng0-list-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}ng0-list:not(.ng0-list-unstyled) ng0-list-item:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: IfDirective, selector: "[ng0If]", inputs: ["ng0If"], exportAs: ["ng0If"] }, { kind: "directive", type: ListItem, selector: "ng0-list-item", inputs: ["value", "id"], exportAs: ["ng0ListItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
479
505
  }
480
506
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImport: i0, type: ListComponent, decorators: [{
481
507
  type: Component,
482
- args: [{ selector: 'ng0-list', exportAs: 'ng0List', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
508
+ args: [{ selector: 'ng0-list, ng0-select-list', exportAs: 'ng0List', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
483
509
  CommonModule,
484
510
  IfDirective,
485
- ListItemComponent
511
+ ListItem
486
512
  ], providers: [{
487
513
  provide: NG_VALUE_ACCESSOR,
488
514
  useExisting: forwardRef(() => ListComponent),
@@ -493,18 +519,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.1", ngImpor
493
519
  '[attr.disabled]': '_isDisabled() ? "" : undefined',
494
520
  '[attr.aria-disabled]': '_isDisabled() ? "" : undefined',
495
521
  '[attr.tabindex]': '_hostTabIndex()',
496
- }, template: "@let filter = filterBy();\r\n\r\n@for (item of _sourceItems(); track trackBy()($index, item)) {\r\n<ng0-list-item *ng0If=\"filter(item)\"\r\n #listItem=\"ng0ListItem\"\r\n [id]=\"idGenerator()?.(item)\"\r\n [value]=\"item\"\r\n [ngClass]=\"itemClass()(item)\"\r\n (click)=\"_handleUserSelection(listItem);\">\r\n</ng0-list-item>\r\n}\r\n\r\n@if(_showLoadingSppiner()) {\r\n@if(_sourceItems().length == 0) {\r\n<div style=\"text-align: center; padding: 0.5rem 0;\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}@else {\r\n<div class=\"ng0-list-loading-cover\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}\r\n}\r\n\r\n<ng-template #spinner>\r\n <div class=\"spinner-border spinner-sm text-primary ng0-list-loading-indicator\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading...</span>\r\n </div>\r\n</ng-template>", styles: ["ng0-list{display:block;position:relative;-webkit-user-select:none;user-select:none;border:1px solid var(--bs-border-color);border-radius:var(--bs-border-radius);min-height:1rem}ng0-list:focus,ng0-list:focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}ng0-list[disabled],ng0-list.disabled{opacity:.5;pointer-events:none}ng0-list .ng0-list-loading-cover{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffffb3;display:flex;align-items:center;justify-content:center}\n"] }]
497
- }], ctorParameters: () => [], propDecorators: { _visibleItems: [{
498
- type: ViewChildren,
499
- args: [ListItemComponent]
500
- }], itemTemplate: [{
522
+ }, template: "@let filter = filterBy();\r\n@let formatter = formatBy();\r\n\r\n@for (item of _sourceItems(); track trackBy()($index, item)) {\r\n\r\n<ng0-list-item #listItem=\"ng0ListItem\"\r\n *ng0If=\"!filter || filter(item)\"\r\n [value]=\"item\"\r\n [id]=\"idGenerator()?.(item)\"\r\n [class.active]=\"isActive(listItem)\"\r\n [class.selected]=\"isSelected(item)\"\r\n [ngClass]=\"itemClass()?.(item)\"\r\n (click)=\"_handleUserSelection(listItem, $index);\">\r\n\r\n @if(itemTemplate() || _itemTemplate) {\r\n @let template = itemTemplate() || _itemTemplate;\r\n <ng-container *ngTemplateOutlet=\"template; context: { $implicit: {item: listItem, value: item}}\" />\r\n } @else {\r\n @if(showSelectionIndicator()) {\r\n <input class=\"form-check-input ng0-selection-indicator\"\r\n tabindex=\"-1\"\r\n [checked]=\"isSelected(item)\"\r\n [attr.type]=\"multiple() ? 'checkbox' : 'radio'\">\r\n }\r\n\r\n {{formatter(item)}}\r\n }\r\n</ng0-list-item>\r\n}\r\n\r\n@if(_showLoadingSppiner()) {\r\n@if(_sourceItems().length == 0) {\r\n<div style=\"text-align: center; padding: 0.5rem 0;\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}@else {\r\n<div class=\"ng0-list-loading-cover\">\r\n <ng-container *ngTemplateOutlet=\"spinner\"></ng-container>\r\n</div>\r\n}\r\n}\r\n\r\n<ng-template #spinner>\r\n <div class=\"spinner-border spinner-sm text-primary ng0-list-loading-indicator\" role=\"status\">\r\n <span class=\"visually-hidden\">Loading...</span>\r\n </div>\r\n</ng-template>", styles: ["ng0-list:not(.ng0-list-unstyled){display:block;position:relative;-webkit-user-select:none;user-select:none;border:1px solid var(--bs-border-color);border-radius:var(--bs-border-radius)}ng0-list:not(.ng0-list-unstyled):focus,ng0-list:not(.ng0-list-unstyled):focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}ng0-list:not(.ng0-list-unstyled)[disabled],ng0-list:not(.ng0-list-unstyled).disabled{opacity:.5;pointer-events:none}ng0-list:not(.ng0-list-unstyled) .ng0-list-loading-cover{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#ffffffb3;display:flex;align-items:center;justify-content:center}ng0-list:not(.ng0-list-unstyled) ng0-list-item{display:flex;padding:.5rem}ng0-list:not(.ng0-list-unstyled) ng0-list-item .ng0-selection-indicator{margin-inline-end:.5rem}ng0-list:not(.ng0-list-unstyled) ng0-list-item:focus,ng0-list:not(.ng0-list-unstyled) ng0-list-item:focus-visible{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}ng0-list:not(.ng0-list-unstyled) ng0-list-item.selected{background-color:var(--bs-primary);color:var(--bs-light)}ng0-list:not(.ng0-list-unstyled) ng0-list-item.active:not(.selected){background-color:color-mix(in srgb,var(--bs-primary),white 85%)}ng0-list:not(.ng0-list-unstyled) ng0-list-item:hover:not(.selected):not(.disabled):not(.active){background-color:color-mix(in srgb,var(--bs-primary),white 95%)}ng0-list:not(.ng0-list-unstyled) ng0-list-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}ng0-list:not(.ng0-list-unstyled) ng0-list-item:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}\n"] }]
523
+ }], ctorParameters: () => [], propDecorators: { _itemTemplate: [{
501
524
  type: ContentChild,
502
525
  args: [TemplateRef]
526
+ }], listItems: [{
527
+ type: ViewChildren,
528
+ args: [ListItem]
503
529
  }], selectionChange: [{
504
530
  type: Output
505
- }], _onHostClick: [{
506
- type: HostListener,
507
- args: ['click']
508
531
  }], _onHostBlur: [{
509
532
  type: HostListener,
510
533
  args: ['blur']