@angular-bootstrap/ngbootstrap 0.0.8 → 0.0.9

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.
@@ -2198,6 +2198,8 @@ class NgbTypeaheadComponent {
2198
2198
  updateOnTab = true;
2199
2199
  separator = ',';
2200
2200
  chips = false;
2201
+ vScroll = false;
2202
+ vItemSize = 40;
2201
2203
  // `TemplateRef` types can become non-assignable in monorepo setups with multiple Angular installations.
2202
2204
  // Using `any` keeps the API flexible while still supporting Angular templates at runtime.
2203
2205
  itemTemplate;
@@ -2241,6 +2243,7 @@ class NgbTypeaheadComponent {
2241
2243
  itemHeight = 40;
2242
2244
  beforePadding = 0;
2243
2245
  afterPadding = 0;
2246
+ viewportStartIndex = 0;
2244
2247
  debounceId;
2245
2248
  onControlChange = () => { };
2246
2249
  onControlTouched = () => { };
@@ -2250,6 +2253,11 @@ class NgbTypeaheadComponent {
2250
2253
  this.applyFilter('');
2251
2254
  }
2252
2255
  ngOnChanges(changes) {
2256
+ if (changes['vScroll'] || changes['vItemSize']) {
2257
+ const nextSize = Number(this.vItemSize);
2258
+ this.itemHeight = Number.isFinite(nextSize) && nextSize > 0 ? nextSize : 40;
2259
+ this.updateViewport(this.scroller?.nativeElement.scrollTop || 0);
2260
+ }
2253
2261
  if (changes['data'] && !changes['data'].firstChange) {
2254
2262
  this.applyFilter(this.query);
2255
2263
  }
@@ -2450,15 +2458,37 @@ class NgbTypeaheadComponent {
2450
2458
  this.focusInput();
2451
2459
  return;
2452
2460
  }
2461
+ if (event.key === 'Tab' && this.overlayVisible) {
2462
+ const active = this.visible[this.activeIndex];
2463
+ if (active) {
2464
+ this.selectItem(active);
2465
+ this.hideOverlay(event);
2466
+ }
2467
+ return;
2468
+ }
2453
2469
  if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
2454
2470
  event.preventDefault();
2455
2471
  this.moveActive(event.key === 'ArrowDown' ? 1 : -1);
2456
2472
  }
2473
+ else if (event.key === 'Home') {
2474
+ if (this.overlayVisible) {
2475
+ event.preventDefault();
2476
+ this.setActiveGlobalIndex(0);
2477
+ }
2478
+ }
2479
+ else if (event.key === 'End') {
2480
+ if (this.overlayVisible) {
2481
+ event.preventDefault();
2482
+ this.setActiveGlobalIndex(this.filtered.length - 1);
2483
+ }
2484
+ }
2457
2485
  else if (event.key === 'Enter') {
2458
2486
  event.preventDefault();
2459
2487
  const active = this.visible[this.activeIndex] || this.visible[0];
2460
- if (active)
2488
+ if (active) {
2461
2489
  this.selectItem(active);
2490
+ this.hideOverlay(event);
2491
+ }
2462
2492
  }
2463
2493
  else if (event.key === 'Escape') {
2464
2494
  if (this.overlayVisible) {
@@ -2649,6 +2679,14 @@ class NgbTypeaheadComponent {
2649
2679
  this.cdr.markForCheck();
2650
2680
  }
2651
2681
  updateViewport(scrollTop) {
2682
+ if (!this.vScroll) {
2683
+ this.beforePadding = 0;
2684
+ this.afterPadding = 0;
2685
+ this.visible = this.filtered;
2686
+ this.viewportStartIndex = 0;
2687
+ this.activeIndex = this.visible.length ? Math.min(this.activeIndex, this.visible.length - 1) : -1;
2688
+ return;
2689
+ }
2652
2690
  const total = this.filtered.length;
2653
2691
  const visibleCount = Math.ceil(this.viewportHeight / this.itemHeight) + 2;
2654
2692
  const start = Math.max(Math.floor(scrollTop / this.itemHeight), 0);
@@ -2656,6 +2694,7 @@ class NgbTypeaheadComponent {
2656
2694
  this.beforePadding = start * this.itemHeight;
2657
2695
  this.afterPadding = Math.max(total - end, 0) * this.itemHeight;
2658
2696
  this.visible = this.filtered.slice(start, end);
2697
+ this.viewportStartIndex = start;
2659
2698
  this.activeIndex = this.visible.length ? Math.min(this.activeIndex, this.visible.length - 1) : -1;
2660
2699
  }
2661
2700
  activatePreferredOption() {
@@ -2685,12 +2724,46 @@ class NgbTypeaheadComponent {
2685
2724
  if (!this.overlayVisible) {
2686
2725
  this.showOverlay();
2687
2726
  }
2688
- if (!this.visible.length)
2727
+ if (!this.filtered.length)
2728
+ return;
2729
+ const currentGlobal = this.activeGlobalIndex();
2730
+ const total = this.filtered.length;
2731
+ const nextGlobal = currentGlobal < 0 ? 0 : (currentGlobal + direction + total) % total;
2732
+ this.setActiveGlobalIndex(nextGlobal);
2733
+ }
2734
+ activeGlobalIndex() {
2735
+ if (this.activeIndex < 0)
2736
+ return -1;
2737
+ return this.viewportStartIndex + this.activeIndex;
2738
+ }
2739
+ setActiveGlobalIndex(globalIndex) {
2740
+ if (!this.filtered.length) {
2741
+ this.activeIndex = -1;
2742
+ return;
2743
+ }
2744
+ const clamped = Math.max(0, Math.min(globalIndex, this.filtered.length - 1));
2745
+ const scroller = this.scroller?.nativeElement;
2746
+ if (!scroller) {
2747
+ this.activeIndex = 0;
2689
2748
  return;
2690
- const nextIndex = this.activeIndex < 0 ? 0 : (this.activeIndex + direction + this.visible.length) % this.visible.length;
2691
- this.activeIndex = nextIndex;
2692
- const buttons = this.scroller?.nativeElement.querySelectorAll('button.dropdown-item');
2693
- buttons?.[this.activeIndex]?.focus();
2749
+ }
2750
+ const itemTop = clamped * this.itemHeight;
2751
+ const itemBottom = itemTop + this.itemHeight;
2752
+ const viewTop = scroller.scrollTop;
2753
+ const viewBottom = viewTop + (scroller.clientHeight || this.viewportHeight);
2754
+ let nextScrollTop = viewTop;
2755
+ if (itemTop < viewTop) {
2756
+ nextScrollTop = itemTop;
2757
+ }
2758
+ else if (itemBottom > viewBottom) {
2759
+ nextScrollTop = Math.max(0, itemBottom - (scroller.clientHeight || this.viewportHeight));
2760
+ }
2761
+ if (nextScrollTop !== viewTop) {
2762
+ scroller.scrollTop = nextScrollTop;
2763
+ }
2764
+ this.updateViewport(scroller.scrollTop);
2765
+ this.activeIndex = Math.max(0, Math.min(clamped - this.viewportStartIndex, this.visible.length - 1));
2766
+ this.cdr.markForCheck();
2694
2767
  }
2695
2768
  validateItem(item) {
2696
2769
  if (item.id == null) {
@@ -2810,7 +2883,7 @@ class NgbTypeaheadComponent {
2810
2883
  return { id: `${value}`, label: `${value}`, value };
2811
2884
  }
2812
2885
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbTypeaheadComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2813
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbTypeaheadComponent, isStandalone: true, selector: "ngb-typeahead", inputs: { data: "data", debounceTime: "debounceTime", characterTyped: "characterTyped", limit: "limit", selectExact: "selectExact", multiSelect: "multiSelect", matchSelection: "matchSelection", showDropdownButton: "showDropdownButton", showClearButton: "showClearButton", updateOnBlur: "updateOnBlur", updateOnTab: "updateOnTab", separator: "separator", chips: "chips", itemTemplate: "itemTemplate", i18n: "i18n" }, outputs: { completeMethod: "completeMethod", onSelect: "onSelect", onUnselect: "onUnselect", onAdd: "onAdd", onFocus: "onFocus", onBlur: "onBlur", onDropdownClick: "onDropdownClick", onClear: "onClear", onInputKeydown: "onInputKeydown", onKeyUp: "onKeyUp", onShow: "onShow", onHide: "onHide", onLazyLoad: "onLazyLoad", selectedItems: "selectedItems", selectionChange: "selectionChange", onChange: "onChange", onScrollEvent: "onScrollEvent" }, host: { listeners: { "document:mousedown": "onDocumentMouseDown($event)", "document:focusin": "onDocumentFocusIn($event)" } }, providers: [
2886
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbTypeaheadComponent, isStandalone: true, selector: "ngb-typeahead", inputs: { data: "data", debounceTime: "debounceTime", characterTyped: "characterTyped", limit: "limit", selectExact: "selectExact", multiSelect: "multiSelect", matchSelection: "matchSelection", showDropdownButton: "showDropdownButton", showClearButton: "showClearButton", updateOnBlur: "updateOnBlur", updateOnTab: "updateOnTab", separator: "separator", chips: "chips", vScroll: "vScroll", vItemSize: "vItemSize", itemTemplate: "itemTemplate", i18n: "i18n" }, outputs: { completeMethod: "completeMethod", onSelect: "onSelect", onUnselect: "onUnselect", onAdd: "onAdd", onFocus: "onFocus", onBlur: "onBlur", onDropdownClick: "onDropdownClick", onClear: "onClear", onInputKeydown: "onInputKeydown", onKeyUp: "onKeyUp", onShow: "onShow", onHide: "onHide", onLazyLoad: "onLazyLoad", selectedItems: "selectedItems", selectionChange: "selectionChange", onChange: "onChange", onScrollEvent: "onScrollEvent" }, host: { listeners: { "document:mousedown": "onDocumentMouseDown($event)", "document:focusin": "onDocumentFocusIn($event)" } }, providers: [
2814
2887
  {
2815
2888
  provide: NG_VALUE_ACCESSOR,
2816
2889
  useExisting: forwardRef(() => NgbTypeaheadComponent),
@@ -2898,7 +2971,7 @@ class NgbTypeaheadComponent {
2898
2971
  role="listbox"
2899
2972
  (scroll)="onScroll($event)"
2900
2973
  >
2901
- <div [style.height.px]="beforePadding"></div>
2974
+ <div *ngIf="vScroll" [style.height.px]="beforePadding"></div>
2902
2975
 
2903
2976
  <button
2904
2977
  *ngFor="let item of visible; trackBy: trackById; let idx = index"
@@ -2940,7 +3013,7 @@ class NgbTypeaheadComponent {
2940
3013
  </div>
2941
3014
  </button>
2942
3015
 
2943
- <div [style.height.px]="afterPadding"></div>
3016
+ <div *ngIf="vScroll" [style.height.px]="afterPadding"></div>
2944
3017
 
2945
3018
  <div *ngIf="showNoResults" class="dropdown-item text-muted text-center">
2946
3019
  {{ i18n?.noResults || 'No results' }}
@@ -3046,7 +3119,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
3046
3119
  role="listbox"
3047
3120
  (scroll)="onScroll($event)"
3048
3121
  >
3049
- <div [style.height.px]="beforePadding"></div>
3122
+ <div *ngIf="vScroll" [style.height.px]="beforePadding"></div>
3050
3123
 
3051
3124
  <button
3052
3125
  *ngFor="let item of visible; trackBy: trackById; let idx = index"
@@ -3088,7 +3161,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
3088
3161
  </div>
3089
3162
  </button>
3090
3163
 
3091
- <div [style.height.px]="afterPadding"></div>
3164
+ <div *ngIf="vScroll" [style.height.px]="afterPadding"></div>
3092
3165
 
3093
3166
  <div *ngIf="showNoResults" class="dropdown-item text-muted text-center">
3094
3167
  {{ i18n?.noResults || 'No results' }}
@@ -3129,6 +3202,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
3129
3202
  type: Input
3130
3203
  }], chips: [{
3131
3204
  type: Input
3205
+ }], vScroll: [{
3206
+ type: Input
3207
+ }], vItemSize: [{
3208
+ type: Input
3132
3209
  }], itemTemplate: [{
3133
3210
  type: Input
3134
3211
  }], i18n: [{