@angular-bootstrap/ngbootstrap 0.0.7 → 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.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Input, Directive, EventEmitter, Output, ChangeDetectionStrategy, Component, inject, Injectable, TemplateRef, QueryList, ContentChildren, ContentChild, InjectionToken, signal, Optional, Inject, ElementRef, HostListener, HostBinding, ViewChild, ViewChildren } from '@angular/core';
2
+ import { Input, Directive, EventEmitter, Output, ChangeDetectionStrategy, Component, inject, Injectable, TemplateRef, QueryList, ContentChildren, ContentChild, InjectionToken, signal, Optional, Inject, ElementRef, HostListener, HostBinding, ViewChild, forwardRef, ViewChildren } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i2 from '@angular/forms';
6
- import { FormBuilder, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
6
+ import { FormBuilder, FormControl, Validators, FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
7
  import { firstValueFrom, isObservable } from 'rxjs';
8
8
 
9
9
  class NgbCellTemplate {
@@ -1755,365 +1755,576 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
1755
1755
  }] } });
1756
1756
 
1757
1757
  class NgbSplitterPaneComponent {
1758
- templateRef;
1759
- size;
1760
- min;
1761
- max;
1758
+ size = ''; // e.g., '30%' or '200px'
1759
+ min = '0px';
1760
+ max = '100%';
1761
+ scrollable = true;
1762
1762
  collapsible = false;
1763
1763
  collapsed = false;
1764
1764
  collapsedChange = new EventEmitter();
1765
- toggleCollapsed(next) {
1766
- const value = next ?? !this.collapsed;
1767
- if (value !== this.collapsed) {
1768
- this.collapsed = value;
1769
- this.collapsedChange.emit(this.collapsed);
1770
- }
1771
- }
1765
+ template;
1772
1766
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbSplitterPaneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1773
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbSplitterPaneComponent, isStandalone: true, selector: "ngb-splitter-pane", inputs: { size: "size", min: "min", max: "max", collapsible: "collapsible", collapsed: "collapsed" }, outputs: { collapsedChange: "collapsedChange" }, viewQueries: [{ propertyName: "templateRef", first: true, predicate: ["paneTpl"], descendants: true, static: true }], ngImport: i0, template: `<ng-template #paneTpl><ng-content></ng-content></ng-template>`, isInline: true });
1767
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbSplitterPaneComponent, isStandalone: true, selector: "ngb-splitter-pane", inputs: { size: "size", min: "min", max: "max", scrollable: "scrollable", collapsible: "collapsible", collapsed: "collapsed" }, outputs: { collapsedChange: "collapsedChange" }, viewQueries: [{ propertyName: "template", first: true, predicate: ["content"], descendants: true, static: true }], ngImport: i0, template: `<ng-template #content>
1768
+ <div [style.overflow]="scrollable ? 'auto' : 'hidden'" style="height: 100%; width: 100%;">
1769
+ <ng-content></ng-content>
1770
+ </div>
1771
+ </ng-template>`, isInline: true, styles: [":host{display:block;position:relative}\n"] });
1774
1772
  }
1775
1773
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbSplitterPaneComponent, decorators: [{
1776
1774
  type: Component,
1777
- args: [{
1778
- selector: 'ngb-splitter-pane',
1779
- standalone: true,
1780
- template: `<ng-template #paneTpl><ng-content></ng-content></ng-template>`,
1781
- }]
1782
- }], propDecorators: { templateRef: [{
1783
- type: ViewChild,
1784
- args: ['paneTpl', { static: true }]
1785
- }], size: [{
1775
+ args: [{ selector: 'ngb-splitter-pane', standalone: true, template: `<ng-template #content>
1776
+ <div [style.overflow]="scrollable ? 'auto' : 'hidden'" style="height: 100%; width: 100%;">
1777
+ <ng-content></ng-content>
1778
+ </div>
1779
+ </ng-template>`, styles: [":host{display:block;position:relative}\n"] }]
1780
+ }], propDecorators: { size: [{
1786
1781
  type: Input
1787
1782
  }], min: [{
1788
1783
  type: Input
1789
1784
  }], max: [{
1790
1785
  type: Input
1786
+ }], scrollable: [{
1787
+ type: Input
1791
1788
  }], collapsible: [{
1792
1789
  type: Input
1793
1790
  }], collapsed: [{
1794
1791
  type: Input
1795
1792
  }], collapsedChange: [{
1796
1793
  type: Output
1794
+ }], template: [{
1795
+ type: ViewChild,
1796
+ args: ['content', { static: true }]
1797
1797
  }] } });
1798
1798
 
1799
1799
  class NgbSplitterComponent {
1800
+ el;
1800
1801
  panes;
1801
- container;
1802
1802
  orientation = 'horizontal';
1803
1803
  handleThickness = 12;
1804
- paneStates = [];
1805
- resizeSub;
1806
- currentDrag;
1807
- ngAfterContentInit() {
1808
- this.initStates();
1809
- this.panes.changes.subscribe(() => this.initStates());
1810
- }
1811
- ngOnChanges(changes) {
1812
- if (changes['orientation'] && !changes['orientation'].firstChange) {
1813
- this.initStates();
1814
- }
1815
- }
1816
- ngOnDestroy() {
1817
- this.detachDragListeners();
1804
+ handleIconColor = '#000';
1805
+ verticalMinHeight = '280px';
1806
+ resizable = true; // New Resize Input
1807
+ barColor = '#ccc'; // Custom Bar Color
1808
+ lineColor = '#000'; // Custom Line Color
1809
+ isDragging = false;
1810
+ currentHandleIndex = -1;
1811
+ constructor(el) {
1812
+ this.el = el;
1813
+ }
1814
+ // Listeners for global mouse movement
1815
+ onMouseMove(event) {
1816
+ if (!this.isDragging || !this.resizable)
1817
+ return;
1818
+ this.resize(event);
1818
1819
  }
1819
- onWindowResize() {
1820
- this.initStates();
1820
+ onMouseUp() {
1821
+ this.isDragging = false;
1822
+ document.body.style.cursor = 'default';
1821
1823
  }
1822
- startResize(index, event) {
1824
+ onMouseDown(event, index) {
1825
+ if (!this.resizable)
1826
+ return;
1823
1827
  event.preventDefault();
1824
- const total = this.getContainerSize();
1825
- const sizes = this.computePaneSizes(total);
1826
- this.currentDrag = {
1827
- index,
1828
- startPos: this.pointerPosition(event),
1829
- startSizes: [sizes[index], sizes[index + 1]],
1830
- };
1831
- const move = (e) => this.onDrag(e);
1832
- const up = () => this.stopDrag();
1833
- document.addEventListener('mousemove', move);
1834
- document.addEventListener('mouseup', up, { once: true });
1835
- this.resizeSub = { move, up };
1836
- }
1837
- onHandleKeydown(index, event) {
1828
+ this.isDragging = true;
1829
+ this.currentHandleIndex = index;
1830
+ document.body.style.cursor = this.orientation === 'horizontal' ? 'col-resize' : 'row-resize';
1831
+ }
1832
+ resize(event) {
1833
+ const container = this.el.nativeElement.querySelector('.splitter-container');
1834
+ const rect = container.getBoundingClientRect();
1835
+ const paneArr = this.panes.toArray();
1836
+ const paneWrappers = Array.from(this.el.nativeElement.querySelectorAll('.pane-wrapper'));
1837
+ const targetPane = paneArr[this.currentHandleIndex];
1838
+ if (!targetPane || !paneWrappers.length) {
1839
+ return;
1840
+ }
1838
1841
  const isHorizontal = this.orientation === 'horizontal';
1839
- const delta = this.handleThickness;
1840
- if ((isHorizontal && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) ||
1841
- (!isHorizontal && (event.key === 'ArrowUp' || event.key === 'ArrowDown'))) {
1842
- event.preventDefault();
1843
- const direction = event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 1;
1844
- this.adjustPaneSizes(index, delta * direction);
1842
+ const totalPixels = isHorizontal ? rect.width : rect.height;
1843
+ if (totalPixels <= 0) {
1844
+ return;
1845
1845
  }
1846
- if (event.key === 'Enter' || event.key === ' ') {
1847
- event.preventDefault();
1848
- this.toggleFromHandle(index);
1846
+ const mousePos = isHorizontal ? event.clientX - rect.left : event.clientY - rect.top;
1847
+ let consumed = 0;
1848
+ for (let i = 0; i < this.currentHandleIndex; i++) {
1849
+ const prevPaneElement = paneWrappers[i];
1850
+ if (!prevPaneElement) {
1851
+ continue;
1852
+ }
1853
+ const prevRect = prevPaneElement.getBoundingClientRect();
1854
+ consumed += isHorizontal ? prevRect.width : prevRect.height;
1855
+ consumed += this.handleThickness;
1849
1856
  }
1850
- }
1851
- toggleFromHandle(index) {
1852
- const panesArray = this.panes.toArray();
1853
- const target = panesArray[index].collapsible ? panesArray[index] : panesArray[index + 1];
1854
- if (target?.collapsible) {
1855
- target.toggleCollapsed();
1856
- this.initStates();
1857
+ let newSizeValue = ((mousePos - consumed) / totalPixels) * 100;
1858
+ const minPercent = this.sizeToPercent(targetPane.min, totalPixels);
1859
+ const maxPercent = this.sizeToPercent(targetPane.max, totalPixels);
1860
+ if (minPercent !== null) {
1861
+ newSizeValue = Math.max(newSizeValue, minPercent);
1857
1862
  }
1858
- }
1859
- onDrag(event) {
1860
- if (!this.currentDrag) {
1861
- return;
1863
+ if (maxPercent !== null) {
1864
+ newSizeValue = Math.min(newSizeValue, maxPercent);
1862
1865
  }
1866
+ newSizeValue = Math.max(0, Math.min(100, newSizeValue));
1867
+ targetPane.size = `${newSizeValue}%`;
1868
+ }
1869
+ onHandleToggle(event, index) {
1870
+ event.stopPropagation();
1863
1871
  event.preventDefault();
1864
- const delta = this.pointerPosition(event) - this.currentDrag.startPos;
1865
- this.adjustPaneSizes(this.currentDrag.index, delta, this.currentDrag.startSizes);
1866
- }
1867
- stopDrag() {
1868
- this.detachDragListeners();
1869
- this.currentDrag = undefined;
1870
- }
1871
- adjustPaneSizes(index, delta, startSizes) {
1872
- const total = this.getContainerSize();
1873
- const sizes = startSizes ?? this.computePaneSizes(total);
1874
- const panesArray = this.panes.toArray();
1875
- const first = panesArray[index];
1876
- const second = panesArray[index + 1];
1877
- if (first.collapsed || second.collapsed) {
1878
- return;
1879
- }
1880
- let firstSize = sizes[0] + delta;
1881
- let secondSize = sizes[1] - delta;
1882
- const firstLimits = this.getLimits(first, total);
1883
- const secondLimits = this.getLimits(second, total);
1884
- firstSize = this.clamp(firstSize, firstLimits.minPx, firstLimits.maxPx);
1885
- secondSize = this.clamp(secondSize, secondLimits.minPx, secondLimits.maxPx);
1886
- if (firstSize + secondSize > 0) {
1887
- this.paneStates[index].sizePx = firstSize;
1888
- this.paneStates[index + 1].sizePx = secondSize;
1889
- }
1890
- }
1891
- initStates() {
1892
- const total = this.getContainerSize();
1893
- const sizes = this.computePaneSizes(total);
1894
- const panesArray = this.panes.toArray();
1895
- this.paneStates = panesArray.map((pane, idx) => {
1896
- const limits = this.getLimits(pane, total);
1897
- return {
1898
- id: `ngb-splitter-pane-${idx}`,
1899
- sizePx: pane.collapsed ? 0 : sizes[idx],
1900
- minPx: limits.minPx,
1901
- maxPx: limits.maxPx,
1902
- };
1903
- });
1872
+ this.togglePane(index);
1904
1873
  }
1905
- computePaneSizes(total) {
1906
- const panesArray = this.panes?.toArray() ?? [];
1907
- if (!panesArray.length)
1908
- return [];
1909
- const resolved = panesArray.map((pane) => pane.collapsed ? 0 : this.resolveSize(pane.size, total));
1910
- const specifiedTotal = resolved.reduce((sum, val) => sum + (val ?? 0), 0);
1911
- const unspecified = resolved.filter((v) => v == null).length;
1912
- const remaining = Math.max(total - specifiedTotal, 0);
1913
- const autoSize = unspecified > 0 ? remaining / unspecified : 0;
1914
- return resolved.map((val) => {
1915
- if (val != null) {
1916
- return val;
1917
- }
1918
- if (autoSize) {
1919
- return autoSize;
1920
- }
1921
- return total / panesArray.length;
1922
- });
1874
+ getCollapseIcon(pane) {
1875
+ if (!pane.collapsible) {
1876
+ return '';
1877
+ }
1878
+ if (this.orientation === 'horizontal') {
1879
+ return pane.collapsed ? 'bi-caret-right-fill' : 'bi-caret-left-fill';
1880
+ }
1881
+ return pane.collapsed ? 'bi-caret-down-fill' : 'bi-caret-up-fill';
1923
1882
  }
1924
- resolveSize(value, total) {
1925
- if (!value)
1926
- return undefined;
1883
+ getPaneFlex(pane) {
1884
+ if (pane.collapsed) {
1885
+ return '0 0 0px';
1886
+ }
1887
+ if (pane.size) {
1888
+ return `0 0 ${pane.size}`;
1889
+ }
1890
+ return '1 1 0px';
1891
+ }
1892
+ sizeToPercent(value, totalPixels) {
1893
+ if (!value) {
1894
+ return null;
1895
+ }
1927
1896
  const trimmed = value.trim();
1897
+ if (!trimmed) {
1898
+ return null;
1899
+ }
1928
1900
  if (trimmed.endsWith('%')) {
1929
- const pct = Number.parseFloat(trimmed.slice(0, -1));
1930
- return (pct / 100) * total;
1901
+ const percent = parseFloat(trimmed);
1902
+ return Number.isFinite(percent) ? percent : null;
1903
+ }
1904
+ const numeric = parseFloat(trimmed);
1905
+ if (!Number.isFinite(numeric)) {
1906
+ return null;
1931
1907
  }
1932
1908
  if (trimmed.endsWith('px')) {
1933
- return Number.parseFloat(trimmed.replace('px', ''));
1909
+ return totalPixels > 0 ? (numeric / totalPixels) * 100 : null;
1910
+ }
1911
+ return numeric;
1912
+ }
1913
+ togglePane(index) {
1914
+ const pane = this.panes.toArray()[index];
1915
+ pane.collapsed = !pane.collapsed;
1916
+ pane.collapsedChange.emit(pane.collapsed);
1917
+ }
1918
+ getSeparatorAriaOrientation() {
1919
+ // For left/right panes, the separator is vertical. For top/bottom panes, it is horizontal.
1920
+ return this.orientation === 'horizontal' ? 'vertical' : 'horizontal';
1921
+ }
1922
+ getHandleValueNow(index) {
1923
+ const container = this.el.nativeElement.querySelector('.splitter-container');
1924
+ const rect = container.getBoundingClientRect();
1925
+ const paneWrappers = Array.from(this.el.nativeElement.querySelectorAll('.pane-wrapper'));
1926
+ const target = paneWrappers[index];
1927
+ const totalPixels = this.orientation === 'horizontal' ? rect.width : rect.height;
1928
+ if (!target || totalPixels <= 0) {
1929
+ return null;
1934
1930
  }
1935
- const asNumber = Number.parseFloat(trimmed);
1936
- return Number.isFinite(asNumber) ? asNumber : undefined;
1931
+ const paneRect = target.getBoundingClientRect();
1932
+ const panePixels = this.orientation === 'horizontal' ? paneRect.width : paneRect.height;
1933
+ return Math.round((panePixels / totalPixels) * 100);
1937
1934
  }
1938
- getLimits(pane, total) {
1939
- return {
1940
- minPx: pane.collapsed ? 0 : this.resolveSize(pane.min, total),
1941
- maxPx: pane.collapsed ? 0 : this.resolveSize(pane.max, total),
1942
- };
1935
+ getHandleValueMin(index) {
1936
+ const pane = this.panes?.toArray?.()[index];
1937
+ const container = this.el.nativeElement.querySelector('.splitter-container');
1938
+ const rect = container.getBoundingClientRect();
1939
+ const totalPixels = this.orientation === 'horizontal' ? rect.width : rect.height;
1940
+ if (!pane || totalPixels <= 0) {
1941
+ return null;
1942
+ }
1943
+ return this.sizeToPercent(pane.min, totalPixels);
1944
+ }
1945
+ getHandleValueMax(index) {
1946
+ const pane = this.panes?.toArray?.()[index];
1947
+ const container = this.el.nativeElement.querySelector('.splitter-container');
1948
+ const rect = container.getBoundingClientRect();
1949
+ const totalPixels = this.orientation === 'horizontal' ? rect.width : rect.height;
1950
+ if (!pane || totalPixels <= 0) {
1951
+ return null;
1952
+ }
1953
+ return this.sizeToPercent(pane.max, totalPixels);
1943
1954
  }
1944
- clamp(value, min, max) {
1945
- let next = value;
1946
- if (min != null)
1947
- next = Math.max(min, next);
1948
- if (max != null)
1949
- next = Math.min(max, next);
1950
- return next;
1951
- }
1952
- pointerPosition(event) {
1953
- return this.orientation === 'horizontal' ? event.clientX : event.clientY;
1954
- }
1955
- getContainerSize() {
1956
- const rect = this.container?.nativeElement.getBoundingClientRect();
1957
- return rect && this.orientation === 'horizontal' ? rect.width : rect?.height || 0;
1958
- }
1959
- detachDragListeners() {
1960
- if (this.resizeSub) {
1961
- document.removeEventListener('mousemove', this.resizeSub.move);
1962
- document.removeEventListener('mouseup', this.resizeSub.up);
1963
- this.resizeSub = undefined;
1964
- }
1965
- }
1966
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbSplitterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1967
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbSplitterComponent, isStandalone: true, selector: "ngb-splitter", inputs: { orientation: "orientation", handleThickness: "handleThickness" }, host: { listeners: { "window:resize": "onWindowResize()" } }, queries: [{ propertyName: "panes", predicate: NgbSplitterPaneComponent }], viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
1968
- <div
1969
- #container
1970
- class="d-flex"
1971
- [class.flex-row]="orientation === 'horizontal'"
1972
- [class.flex-column]="orientation === 'vertical'"
1973
- role="group"
1974
- >
1975
- <ng-container *ngFor="let pane of panes.toArray(); let i = index">
1976
- <div
1977
- class="d-flex flex-column overflow-auto"
1978
- [attr.data-pane-id]="paneStates[i]?.id"
1979
- [class.d-none]="pane.collapsed"
1980
- [style.flex-basis.px]="paneStates[i]?.sizePx"
1981
- [style.min-width.px]="orientation === 'horizontal' ? paneStates[i]?.minPx : undefined"
1982
- [style.max-width.px]="orientation === 'horizontal' ? paneStates[i]?.maxPx : undefined"
1983
- [style.min-height.px]="orientation === 'vertical' ? paneStates[i]?.minPx : undefined"
1984
- [style.max-height.px]="orientation === 'vertical' ? paneStates[i]?.maxPx : undefined"
1955
+ onHandleKeyDown(event, index) {
1956
+ if (event.key === 'Enter') {
1957
+ const pane = this.panes.toArray()[index];
1958
+ if (pane?.collapsible) {
1959
+ event.preventDefault();
1960
+ this.togglePane(index);
1961
+ }
1962
+ return;
1963
+ }
1964
+ const isHorizontal = this.orientation === 'horizontal';
1965
+ const negativeKey = isHorizontal ? 'ArrowLeft' : 'ArrowUp';
1966
+ const positiveKey = isHorizontal ? 'ArrowRight' : 'ArrowDown';
1967
+ if (event.key !== negativeKey && event.key !== positiveKey) {
1968
+ return;
1969
+ }
1970
+ if (!this.resizable) {
1971
+ return;
1972
+ }
1973
+ event.preventDefault();
1974
+ const delta = (event.shiftKey ? 5 : 2) * (event.key === positiveKey ? 1 : -1);
1975
+ this.resizeByPercent(index, delta);
1976
+ }
1977
+ resizeByPercent(index, deltaPercent) {
1978
+ const container = this.el.nativeElement.querySelector('.splitter-container');
1979
+ const rect = container.getBoundingClientRect();
1980
+ const paneArr = this.panes.toArray();
1981
+ const paneWrappers = Array.from(this.el.nativeElement.querySelectorAll('.pane-wrapper'));
1982
+ const targetPane = paneArr[index];
1983
+ const targetWrapper = paneWrappers[index];
1984
+ if (!targetPane || !targetWrapper) {
1985
+ return;
1986
+ }
1987
+ const totalPixels = this.orientation === 'horizontal' ? rect.width : rect.height;
1988
+ if (totalPixels <= 0) {
1989
+ return;
1990
+ }
1991
+ const wrapperRect = targetWrapper.getBoundingClientRect();
1992
+ const currentPixels = this.orientation === 'horizontal' ? wrapperRect.width : wrapperRect.height;
1993
+ let nextPercent = (currentPixels / totalPixels) * 100 + deltaPercent;
1994
+ const minPercent = this.sizeToPercent(targetPane.min, totalPixels);
1995
+ const maxPercent = this.sizeToPercent(targetPane.max, totalPixels);
1996
+ if (minPercent !== null) {
1997
+ nextPercent = Math.max(nextPercent, minPercent);
1998
+ }
1999
+ if (maxPercent !== null) {
2000
+ nextPercent = Math.min(nextPercent, maxPercent);
2001
+ }
2002
+ nextPercent = Math.max(0, Math.min(100, nextPercent));
2003
+ targetPane.size = `${nextPercent}%`;
2004
+ }
2005
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbSplitterComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
2006
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbSplitterComponent, isStandalone: true, selector: "ngb-splitter", inputs: { orientation: "orientation", handleThickness: "handleThickness", handleIconColor: "handleIconColor", verticalMinHeight: "verticalMinHeight", resizable: "resizable", barColor: "barColor", lineColor: "lineColor" }, host: { listeners: { "window:mousemove": "onMouseMove($event)", "window:mouseup": "onMouseUp()" } }, queries: [{ propertyName: "panes", predicate: NgbSplitterPaneComponent }], ngImport: i0, template: `
2007
+ <div class="splitter-container" [class.vertical]="orientation === 'vertical'"
2008
+ [style.min-height]="orientation === 'vertical' ? verticalMinHeight : null"
2009
+ [style.height]="orientation === 'vertical' ? verticalMinHeight : null">
2010
+ <!-- Iterate through the QueryList of panes -->
2011
+ <ng-container *ngFor="let pane of panes; let i = index">
2012
+ <div class="pane-wrapper" [style.flex]="getPaneFlex(pane)"
2013
+ [style.min-width]="orientation === 'horizontal' ? (pane.min || '0px') : null"
2014
+ [style.max-width]="orientation === 'horizontal' ? pane.max : null"
2015
+ [style.min-height]="orientation === 'vertical' ? (pane.min || '0px') : null"
2016
+ [style.max-height]="orientation === 'vertical' ? pane.max : null"
1985
2017
  >
1986
- <ng-container *ngTemplateOutlet="pane.templateRef"></ng-container>
2018
+ <!-- Access the 'template' property we defined in the Pane component -->
2019
+ <ng-container [ngTemplateOutlet]="pane.template"></ng-container>
1987
2020
  </div>
1988
2021
 
1989
- <button
1990
- *ngIf="i < panes.length - 1"
1991
- type="button"
1992
- class="bg-transparent border-0 p-0 d-flex align-items-center justify-content-center"
1993
- [style.width.px]="orientation === 'horizontal' ? handleThickness : undefined"
1994
- [style.height.px]="orientation === 'vertical' ? handleThickness : undefined"
1995
- [class.w-100]="orientation === 'vertical'"
1996
- [class.h-100]="orientation === 'horizontal'"
1997
- role="separator"
1998
- [attr.aria-orientation]="orientation"
1999
- [attr.aria-controls]="paneStates[i]?.id + ' ' + paneStates[i + 1]?.id"
2000
- tabindex="0"
2001
- (mousedown)="startResize(i, $event)"
2002
- (keydown)="onHandleKeydown(i, $event)"
2003
- (dblclick)="toggleFromHandle(i)"
2004
- aria-label="Splitter resize handle"
2005
- >
2006
- <span class="border bg-light" [style.width.px]="orientation === 'horizontal' ? 2 : handleThickness" [style.height.px]="orientation === 'vertical' ? 2 : handleThickness"></span>
2007
- </button>
2022
+ <div *ngIf="i < panes.length - 1" class="splitbar"
2023
+ [class.resizable]="resizable"
2024
+ [style.background-color]="barColor"
2025
+ [style.width.px]="orientation === 'horizontal' ? handleThickness : '100%'"
2026
+ [style.height.px]="orientation === 'vertical' ? handleThickness : '100%'"
2027
+ role="separator"
2028
+ tabindex="0"
2029
+ [attr.aria-orientation]="getSeparatorAriaOrientation()"
2030
+ [attr.aria-valuenow]="getHandleValueNow(i)"
2031
+ [attr.aria-valuemin]="getHandleValueMin(i)"
2032
+ [attr.aria-valuemax]="getHandleValueMax(i)"
2033
+ (keydown)="onHandleKeyDown($event, i)"
2034
+ (mousedown)="onMouseDown($event, i)">
2035
+ <div class="handle-line" [style.background-color]="lineColor">
2036
+ <span
2037
+ *ngIf="pane.collapsible"
2038
+ class="collapse-arrow bi"
2039
+ [ngClass]="getCollapseIcon(pane)"
2040
+ [style.color]="handleIconColor"
2041
+ (click)="onHandleToggle($event, i)"
2042
+ aria-hidden="true"
2043
+ ></span>
2044
+ </div>
2045
+ </div>
2008
2046
  </ng-container>
2009
2047
  </div>
2010
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2048
+ `, isInline: true, styles: [":host{display:block;width:100%}.splitter-container{display:flex;height:100%;width:100%}.splitter-container.vertical{flex-direction:column}.splitbar{background:#ccc;width:5px;position:relative;align-items:center;justify-content:center;flex:0 0 auto;z-index:10}.splitbar.resizable{cursor:col-resize}.splitbar:hover{filter:brightness(90%)}.handle-line{border-radius:2px;opacity:.8;left:45%;position:absolute;top:45%;display:flex;align-items:center;justify-content:center}.splitter-container:not(.vertical) .handle-line{width:2px;height:10px}.pane-wrapper{position:relative;min-width:0;min-height:0;overflow:hidden;flex-shrink:1}.vertical .handle-line{height:2px;width:10px}.vertical .splitbar.resizable{cursor:row-resize}.splitter-container.vertical .splitter-container .splitbar.resizable{cursor:col-resize}.vertical .splitbar{width:100%}.collapse-btn{cursor:pointer;background:#666;width:10px;height:10px;position:absolute;top:50%}.collapse-arrow{font-size:.95rem;-webkit-user-select:none;user-select:none;cursor:pointer;line-height:1;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;position:absolute;top:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2011
2049
  }
2012
2050
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbSplitterComponent, decorators: [{
2013
2051
  type: Component,
2014
- args: [{
2015
- selector: 'ngb-splitter',
2016
- standalone: true,
2017
- imports: [CommonModule, NgbSplitterPaneComponent],
2018
- changeDetection: ChangeDetectionStrategy.OnPush,
2019
- template: `
2020
- <div
2021
- #container
2022
- class="d-flex"
2023
- [class.flex-row]="orientation === 'horizontal'"
2024
- [class.flex-column]="orientation === 'vertical'"
2025
- role="group"
2026
- >
2027
- <ng-container *ngFor="let pane of panes.toArray(); let i = index">
2028
- <div
2029
- class="d-flex flex-column overflow-auto"
2030
- [attr.data-pane-id]="paneStates[i]?.id"
2031
- [class.d-none]="pane.collapsed"
2032
- [style.flex-basis.px]="paneStates[i]?.sizePx"
2033
- [style.min-width.px]="orientation === 'horizontal' ? paneStates[i]?.minPx : undefined"
2034
- [style.max-width.px]="orientation === 'horizontal' ? paneStates[i]?.maxPx : undefined"
2035
- [style.min-height.px]="orientation === 'vertical' ? paneStates[i]?.minPx : undefined"
2036
- [style.max-height.px]="orientation === 'vertical' ? paneStates[i]?.maxPx : undefined"
2052
+ args: [{ selector: 'ngb-splitter', standalone: true, imports: [CommonModule, NgbSplitterPaneComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2053
+ <div class="splitter-container" [class.vertical]="orientation === 'vertical'"
2054
+ [style.min-height]="orientation === 'vertical' ? verticalMinHeight : null"
2055
+ [style.height]="orientation === 'vertical' ? verticalMinHeight : null">
2056
+ <!-- Iterate through the QueryList of panes -->
2057
+ <ng-container *ngFor="let pane of panes; let i = index">
2058
+ <div class="pane-wrapper" [style.flex]="getPaneFlex(pane)"
2059
+ [style.min-width]="orientation === 'horizontal' ? (pane.min || '0px') : null"
2060
+ [style.max-width]="orientation === 'horizontal' ? pane.max : null"
2061
+ [style.min-height]="orientation === 'vertical' ? (pane.min || '0px') : null"
2062
+ [style.max-height]="orientation === 'vertical' ? pane.max : null"
2037
2063
  >
2038
- <ng-container *ngTemplateOutlet="pane.templateRef"></ng-container>
2064
+ <!-- Access the 'template' property we defined in the Pane component -->
2065
+ <ng-container [ngTemplateOutlet]="pane.template"></ng-container>
2039
2066
  </div>
2040
2067
 
2041
- <button
2042
- *ngIf="i < panes.length - 1"
2043
- type="button"
2044
- class="bg-transparent border-0 p-0 d-flex align-items-center justify-content-center"
2045
- [style.width.px]="orientation === 'horizontal' ? handleThickness : undefined"
2046
- [style.height.px]="orientation === 'vertical' ? handleThickness : undefined"
2047
- [class.w-100]="orientation === 'vertical'"
2048
- [class.h-100]="orientation === 'horizontal'"
2049
- role="separator"
2050
- [attr.aria-orientation]="orientation"
2051
- [attr.aria-controls]="paneStates[i]?.id + ' ' + paneStates[i + 1]?.id"
2052
- tabindex="0"
2053
- (mousedown)="startResize(i, $event)"
2054
- (keydown)="onHandleKeydown(i, $event)"
2055
- (dblclick)="toggleFromHandle(i)"
2056
- aria-label="Splitter resize handle"
2057
- >
2058
- <span class="border bg-light" [style.width.px]="orientation === 'horizontal' ? 2 : handleThickness" [style.height.px]="orientation === 'vertical' ? 2 : handleThickness"></span>
2059
- </button>
2068
+ <div *ngIf="i < panes.length - 1" class="splitbar"
2069
+ [class.resizable]="resizable"
2070
+ [style.background-color]="barColor"
2071
+ [style.width.px]="orientation === 'horizontal' ? handleThickness : '100%'"
2072
+ [style.height.px]="orientation === 'vertical' ? handleThickness : '100%'"
2073
+ role="separator"
2074
+ tabindex="0"
2075
+ [attr.aria-orientation]="getSeparatorAriaOrientation()"
2076
+ [attr.aria-valuenow]="getHandleValueNow(i)"
2077
+ [attr.aria-valuemin]="getHandleValueMin(i)"
2078
+ [attr.aria-valuemax]="getHandleValueMax(i)"
2079
+ (keydown)="onHandleKeyDown($event, i)"
2080
+ (mousedown)="onMouseDown($event, i)">
2081
+ <div class="handle-line" [style.background-color]="lineColor">
2082
+ <span
2083
+ *ngIf="pane.collapsible"
2084
+ class="collapse-arrow bi"
2085
+ [ngClass]="getCollapseIcon(pane)"
2086
+ [style.color]="handleIconColor"
2087
+ (click)="onHandleToggle($event, i)"
2088
+ aria-hidden="true"
2089
+ ></span>
2090
+ </div>
2091
+ </div>
2060
2092
  </ng-container>
2061
2093
  </div>
2062
- `,
2063
- }]
2064
- }], propDecorators: { panes: [{
2094
+ `, styles: [":host{display:block;width:100%}.splitter-container{display:flex;height:100%;width:100%}.splitter-container.vertical{flex-direction:column}.splitbar{background:#ccc;width:5px;position:relative;align-items:center;justify-content:center;flex:0 0 auto;z-index:10}.splitbar.resizable{cursor:col-resize}.splitbar:hover{filter:brightness(90%)}.handle-line{border-radius:2px;opacity:.8;left:45%;position:absolute;top:45%;display:flex;align-items:center;justify-content:center}.splitter-container:not(.vertical) .handle-line{width:2px;height:10px}.pane-wrapper{position:relative;min-width:0;min-height:0;overflow:hidden;flex-shrink:1}.vertical .handle-line{height:2px;width:10px}.vertical .splitbar.resizable{cursor:row-resize}.splitter-container.vertical .splitter-container .splitbar.resizable{cursor:col-resize}.vertical .splitbar{width:100%}.collapse-btn{cursor:pointer;background:#666;width:10px;height:10px;position:absolute;top:50%}.collapse-arrow{font-size:.95rem;-webkit-user-select:none;user-select:none;cursor:pointer;line-height:1;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;position:absolute;top:100%}\n"] }]
2095
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { panes: [{
2065
2096
  type: ContentChildren,
2066
2097
  args: [NgbSplitterPaneComponent]
2067
- }], container: [{
2068
- type: ViewChild,
2069
- args: ['container', { static: true }]
2070
2098
  }], orientation: [{
2071
2099
  type: Input
2072
2100
  }], handleThickness: [{
2073
2101
  type: Input
2074
- }], onWindowResize: [{
2102
+ }], handleIconColor: [{
2103
+ type: Input
2104
+ }], verticalMinHeight: [{
2105
+ type: Input
2106
+ }], resizable: [{
2107
+ type: Input
2108
+ }], barColor: [{
2109
+ type: Input
2110
+ }], lineColor: [{
2111
+ type: Input
2112
+ }], onMouseMove: [{
2113
+ type: HostListener,
2114
+ args: ['window:mousemove', ['$event']]
2115
+ }], onMouseUp: [{
2075
2116
  type: HostListener,
2076
- args: ['window:resize']
2117
+ args: ['window:mouseup']
2118
+ }] } });
2119
+
2120
+ class NgbChipsComponent {
2121
+ items = [];
2122
+ removable = true;
2123
+ ariaLabel;
2124
+ removeLabel;
2125
+ remove = new EventEmitter();
2126
+ trackById = (_, item) => item.id;
2127
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbChipsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2128
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbChipsComponent, isStandalone: true, selector: "ngb-chips", inputs: { items: "items", removable: "removable", ariaLabel: "ariaLabel", removeLabel: "removeLabel" }, outputs: { remove: "remove" }, ngImport: i0, template: `
2129
+ <div class="ngb-chips" [attr.aria-label]="ariaLabel">
2130
+ <span
2131
+ *ngFor="let item of items; trackBy: trackById"
2132
+ class="badge rounded-pill bg-secondary ngb-chip"
2133
+ [class.opacity-50]="!!item.disabled"
2134
+ >
2135
+ <span class="ngb-chip-label">{{ item.label }}</span>
2136
+ <button
2137
+ *ngIf="removable"
2138
+ type="button"
2139
+ class="ngb-chip-remove"
2140
+ [disabled]="!!item.disabled"
2141
+ (click)="remove.emit(item)"
2142
+ [attr.aria-label]="removeLabel || 'Remove'"
2143
+ >
2144
+ &times;
2145
+ </button>
2146
+ </span>
2147
+ </div>
2148
+ `, isInline: true, styles: [":host{display:block}.ngb-chips{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}.ngb-chip{display:inline-flex;align-items:center;gap:.25rem;max-width:100%}.ngb-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ngb-chip-remove{border:0;background:transparent;padding:0;line-height:1;cursor:pointer;font-size:1rem;opacity:.85}.ngb-chip-remove:disabled{cursor:default;opacity:.4}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2149
+ }
2150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbChipsComponent, decorators: [{
2151
+ type: Component,
2152
+ args: [{ selector: 'ngb-chips', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2153
+ <div class="ngb-chips" [attr.aria-label]="ariaLabel">
2154
+ <span
2155
+ *ngFor="let item of items; trackBy: trackById"
2156
+ class="badge rounded-pill bg-secondary ngb-chip"
2157
+ [class.opacity-50]="!!item.disabled"
2158
+ >
2159
+ <span class="ngb-chip-label">{{ item.label }}</span>
2160
+ <button
2161
+ *ngIf="removable"
2162
+ type="button"
2163
+ class="ngb-chip-remove"
2164
+ [disabled]="!!item.disabled"
2165
+ (click)="remove.emit(item)"
2166
+ [attr.aria-label]="removeLabel || 'Remove'"
2167
+ >
2168
+ &times;
2169
+ </button>
2170
+ </span>
2171
+ </div>
2172
+ `, styles: [":host{display:block}.ngb-chips{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}.ngb-chip{display:inline-flex;align-items:center;gap:.25rem;max-width:100%}.ngb-chip-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ngb-chip-remove{border:0;background:transparent;padding:0;line-height:1;cursor:pointer;font-size:1rem;opacity:.85}.ngb-chip-remove:disabled{cursor:default;opacity:.4}\n"] }]
2173
+ }], propDecorators: { items: [{
2174
+ type: Input
2175
+ }], removable: [{
2176
+ type: Input
2177
+ }], ariaLabel: [{
2178
+ type: Input
2179
+ }], removeLabel: [{
2180
+ type: Input
2181
+ }], remove: [{
2182
+ type: Output
2077
2183
  }] } });
2078
2184
 
2079
2185
  class NgbTypeaheadComponent {
2186
+ host;
2187
+ cdr;
2080
2188
  data = [];
2081
2189
  debounceTime = 200;
2082
2190
  characterTyped = 1;
2083
2191
  limit = 10;
2084
2192
  selectExact = false;
2085
2193
  multiSelect = false;
2086
- i18n;
2194
+ matchSelection = false;
2195
+ showDropdownButton = false;
2196
+ showClearButton = false;
2197
+ updateOnBlur = false;
2198
+ updateOnTab = true;
2199
+ separator = ',';
2200
+ chips = false;
2201
+ vScroll = false;
2202
+ vItemSize = 40;
2203
+ // `TemplateRef` types can become non-assignable in monorepo setups with multiple Angular installations.
2204
+ // Using `any` keeps the API flexible while still supporting Angular templates at runtime.
2087
2205
  itemTemplate;
2206
+ i18n;
2207
+ projectedItemTemplate;
2208
+ completeMethod = new EventEmitter();
2209
+ onSelect = new EventEmitter();
2210
+ onUnselect = new EventEmitter();
2211
+ onAdd = new EventEmitter();
2212
+ onFocus = new EventEmitter();
2213
+ onBlur = new EventEmitter();
2214
+ onDropdownClick = new EventEmitter();
2215
+ onClear = new EventEmitter();
2216
+ onInputKeydown = new EventEmitter();
2217
+ onKeyUp = new EventEmitter();
2218
+ onShow = new EventEmitter();
2219
+ onHide = new EventEmitter();
2220
+ onLazyLoad = new EventEmitter();
2088
2221
  selectedItems = new EventEmitter();
2089
2222
  selectionChange = new EventEmitter();
2090
2223
  onChange = new EventEmitter();
2091
- onScrollEvent = new EventEmitter();
2224
+ onScrollEvent = new EventEmitter(); // legacy, kept for compatibility
2092
2225
  scroller;
2093
2226
  inputEl;
2094
2227
  itemButtons;
2228
+ disabled = false;
2095
2229
  query = '';
2096
2230
  filtered = [];
2097
2231
  visible = [];
2098
2232
  selected = [];
2233
+ overlayVisible = false;
2234
+ openedByDropdown = false;
2235
+ hasSearched = false;
2236
+ activeIndex = -1;
2237
+ inputFocused = false;
2238
+ pointerDownInside = false;
2239
+ lastFilterTerm = '';
2240
+ keepOpenOnEmpty = false;
2241
+ overlayId = `ngb-typeahead-overlay-${Math.random().toString(36).slice(2)}`;
2099
2242
  viewportHeight = 240;
2100
2243
  itemHeight = 40;
2101
2244
  beforePadding = 0;
2102
2245
  afterPadding = 0;
2246
+ viewportStartIndex = 0;
2103
2247
  debounceId;
2248
+ onControlChange = () => { };
2249
+ onControlTouched = () => { };
2250
+ controlValue = null;
2104
2251
  ngAfterViewInit() {
2252
+ // do not open overlay on init; just ensure viewport state is consistent
2105
2253
  this.applyFilter('');
2106
2254
  }
2107
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
+ }
2108
2261
  if (changes['data'] && !changes['data'].firstChange) {
2109
2262
  this.applyFilter(this.query);
2110
2263
  }
2264
+ if (changes['data'] && this.controlValue != null && !this.inputFocused && !this.overlayVisible) {
2265
+ this.applyControlValue(this.controlValue);
2266
+ }
2111
2267
  }
2112
2268
  ngOnDestroy() {
2113
2269
  if (this.debounceId) {
2114
2270
  clearTimeout(this.debounceId);
2115
2271
  }
2116
2272
  }
2273
+ constructor(host, cdr) {
2274
+ this.host = host;
2275
+ this.cdr = cdr;
2276
+ }
2277
+ onDocumentMouseDown(event) {
2278
+ const target = event.target;
2279
+ if (!target)
2280
+ return;
2281
+ const inside = this.host.nativeElement.contains(target);
2282
+ this.pointerDownInside = inside;
2283
+ if (inside)
2284
+ return;
2285
+ if (!this.overlayVisible)
2286
+ return;
2287
+ this.commitInputOnClose();
2288
+ this.hideOverlay(event);
2289
+ }
2290
+ onDocumentFocusIn(event) {
2291
+ if (!this.overlayVisible)
2292
+ return;
2293
+ const target = event.target;
2294
+ if (!target)
2295
+ return;
2296
+ if (this.host.nativeElement.contains(target))
2297
+ return;
2298
+ this.commitInputOnClose();
2299
+ this.hideOverlay(event);
2300
+ }
2301
+ get activeDescendantId() {
2302
+ if (!this.overlayVisible)
2303
+ return null;
2304
+ if (this.activeIndex < 0 || this.activeIndex >= this.visible.length)
2305
+ return null;
2306
+ return this.getOptionId(this.activeIndex);
2307
+ }
2308
+ get showNoResults() {
2309
+ return this.overlayVisible && this.hasSearched && this.filtered.length === 0;
2310
+ }
2311
+ get displayQuery() {
2312
+ if (!this.multiSelect) {
2313
+ return this.query;
2314
+ }
2315
+ if (this.chips) {
2316
+ return this.query;
2317
+ }
2318
+ const term = (this.query || '').trim();
2319
+ const prefix = this.selected.map((s) => s.label).join(', ');
2320
+ if (!prefix) {
2321
+ return this.query;
2322
+ }
2323
+ if (term) {
2324
+ return `${prefix}, ${term}`;
2325
+ }
2326
+ return prefix;
2327
+ }
2117
2328
  get selectionSummary() {
2118
2329
  if (!this.selected.length)
2119
2330
  return '';
@@ -2123,28 +2334,201 @@ class NgbTypeaheadComponent {
2123
2334
  const firstTwo = this.selected.slice(0, 2).map((s) => s.label).join(', ');
2124
2335
  return `${firstTwo} (+${this.selected.length - 2})`;
2125
2336
  }
2337
+ get resolvedItemTemplate() {
2338
+ return this.itemTemplate ?? this.projectedItemTemplate;
2339
+ }
2340
+ writeValue(value) {
2341
+ this.controlValue = value;
2342
+ this.applyControlValue(value);
2343
+ }
2344
+ applyControlValue(value) {
2345
+ if (this.multiSelect) {
2346
+ const asArray = Array.isArray(value) ? value : value == null ? [] : [value];
2347
+ const next = asArray.map((v) => this.coerceToItem(v)).filter(Boolean);
2348
+ const seen = new Set();
2349
+ this.selected = next.filter((i) => (seen.has(i.id) ? false : (seen.add(i.id), true)));
2350
+ this.query = '';
2351
+ this.keepOpenOnEmpty = false;
2352
+ this.applyFilter('');
2353
+ this.cdr.markForCheck();
2354
+ return;
2355
+ }
2356
+ if (value == null) {
2357
+ this.selected = [];
2358
+ this.query = '';
2359
+ this.keepOpenOnEmpty = false;
2360
+ this.applyFilter('');
2361
+ this.cdr.markForCheck();
2362
+ return;
2363
+ }
2364
+ const item = this.coerceToItem(value);
2365
+ if (!item)
2366
+ return;
2367
+ this.selected = [item];
2368
+ this.query = item.label;
2369
+ if (this.inputEl?.nativeElement) {
2370
+ this.inputEl.nativeElement.value = item.label;
2371
+ }
2372
+ this.keepOpenOnEmpty = false;
2373
+ this.applyFilter(this.query);
2374
+ this.cdr.markForCheck();
2375
+ }
2376
+ registerOnChange(fn) {
2377
+ this.onControlChange = fn;
2378
+ }
2379
+ registerOnTouched(fn) {
2380
+ this.onControlTouched = fn;
2381
+ }
2382
+ setDisabledState(isDisabled) {
2383
+ this.disabled = isDisabled;
2384
+ if (isDisabled) {
2385
+ this.hideOverlay();
2386
+ }
2387
+ this.cdr.markForCheck();
2388
+ }
2126
2389
  onInput(value) {
2127
- this.query = value;
2390
+ if (this.disabled)
2391
+ return;
2392
+ this.keepOpenOnEmpty = false;
2393
+ const trimmed = (value ?? '').trim();
2394
+ if (!this.multiSelect && this.selected.length) {
2395
+ const selectedLabel = (this.selected[0]?.label ?? '').trim();
2396
+ if (trimmed.toLowerCase() !== selectedLabel.toLowerCase()) {
2397
+ const previous = this.selected[0];
2398
+ this.selected = [];
2399
+ this.selectedItems.emit(this.selected);
2400
+ this.selectionChange.emit(this.selected);
2401
+ if (previous) {
2402
+ this.onUnselect.emit(previous);
2403
+ }
2404
+ this.propagateControlValue();
2405
+ }
2406
+ }
2407
+ if (this.chips && this.multiSelect) {
2408
+ const tokens = this.splitBySeparators(value);
2409
+ if (tokens.tokensToAdd.length) {
2410
+ for (const token of tokens.tokensToAdd) {
2411
+ this.addToken(token);
2412
+ }
2413
+ }
2414
+ this.query = tokens.remaining;
2415
+ }
2416
+ else {
2417
+ this.query = value;
2418
+ }
2128
2419
  this.onChange.emit(value);
2420
+ // Input events imply the user is interacting with the field; treat as focused so the overlay can open immediately.
2421
+ this.inputFocused = true;
2129
2422
  if (this.debounceId)
2130
2423
  clearTimeout(this.debounceId);
2131
2424
  if (this.debounceTime <= 0) {
2132
2425
  this.applyFilter(value);
2426
+ this.completeMethod.emit(value);
2133
2427
  }
2134
2428
  else {
2135
- this.debounceId = setTimeout(() => this.applyFilter(value), this.debounceTime);
2429
+ this.debounceId = setTimeout(() => {
2430
+ this.applyFilter(value);
2431
+ this.completeMethod.emit(value);
2432
+ this.cdr.markForCheck();
2433
+ }, this.debounceTime);
2434
+ }
2435
+ }
2436
+ onNativeInput(event) {
2437
+ const target = event.target;
2438
+ const value = target?.value ?? '';
2439
+ if (this.multiSelect && !this.chips && this.selected.length) {
2440
+ const prefix = this.selected.map((s) => s.label).join(', ');
2441
+ const normalizedPrefix = prefix.trim();
2442
+ if (normalizedPrefix && value.toLowerCase().startsWith(normalizedPrefix.toLowerCase())) {
2443
+ let rest = value.slice(normalizedPrefix.length);
2444
+ rest = rest.replace(/^[\s,]+/, '');
2445
+ this.onInput(rest);
2446
+ return;
2447
+ }
2136
2448
  }
2449
+ this.onInput(value);
2137
2450
  }
2138
2451
  onKeydown(event) {
2452
+ if (this.disabled)
2453
+ return;
2454
+ this.onInputKeydown.emit(event);
2455
+ if (this.chips && this.multiSelect && event.key === 'Tab' && this.updateOnTab) {
2456
+ event.preventDefault();
2457
+ this.addToken((this.query || '').trim());
2458
+ this.focusInput();
2459
+ return;
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
+ }
2139
2469
  if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
2140
2470
  event.preventDefault();
2141
- this.focusSibling(event.key === 'ArrowDown' ? 1 : -1);
2471
+ this.moveActive(event.key === 'ArrowDown' ? 1 : -1);
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
+ }
2142
2484
  }
2143
2485
  else if (event.key === 'Enter') {
2144
2486
  event.preventDefault();
2145
- const first = this.visible[0];
2146
- if (first)
2147
- this.selectItem(first);
2487
+ const active = this.visible[this.activeIndex] || this.visible[0];
2488
+ if (active) {
2489
+ this.selectItem(active);
2490
+ this.hideOverlay(event);
2491
+ }
2492
+ }
2493
+ else if (event.key === 'Escape') {
2494
+ if (this.overlayVisible) {
2495
+ event.preventDefault();
2496
+ this.hideOverlay(event);
2497
+ }
2498
+ }
2499
+ }
2500
+ onKeyup(event) {
2501
+ this.onKeyUp.emit(event);
2502
+ }
2503
+ onInputFocus(event) {
2504
+ if (this.disabled)
2505
+ return;
2506
+ this.onFocus.emit(event);
2507
+ this.inputFocused = true;
2508
+ if (this.multiSelect && !this.overlayVisible) {
2509
+ // When multi-select is displaying the selection summary, start searching with a clean query.
2510
+ this.query = '';
2511
+ // After a multi-select session, re-opening the field should show the list again even with an empty query.
2512
+ if (!this.chips && this.selected.length) {
2513
+ this.keepOpenOnEmpty = true;
2514
+ this.applyFilter('');
2515
+ this.activatePreferredOption();
2516
+ return;
2517
+ }
2518
+ }
2519
+ // Opening behavior: only show overlay when threshold met, or dropdown button explicitly used.
2520
+ if (this.query.length >= this.characterTyped) {
2521
+ this.showOverlay(event);
2522
+ }
2523
+ }
2524
+ onInputBlur(event) {
2525
+ this.onBlur.emit(event);
2526
+ this.onControlTouched();
2527
+ this.inputFocused = false;
2528
+ // Do not close on blur directly: scrolling/clicking within the overlay can blur the input.
2529
+ // Closing/commit is handled by outside click (document:mousedown) and focus moving outside (document:focusin).
2530
+ if (this.chips && this.multiSelect && this.updateOnBlur) {
2531
+ this.addToken((this.query || '').trim());
2148
2532
  }
2149
2533
  }
2150
2534
  onScroll(event) {
@@ -2152,8 +2536,14 @@ class NgbTypeaheadComponent {
2152
2536
  const scrollTop = target.scrollTop;
2153
2537
  this.onScrollEvent.emit();
2154
2538
  this.updateViewport(scrollTop);
2539
+ const remaining = target.scrollHeight - (target.scrollTop + target.clientHeight);
2540
+ if (remaining < 48) {
2541
+ this.onLazyLoad.emit();
2542
+ }
2155
2543
  }
2156
2544
  selectItem(item) {
2545
+ if (this.disabled)
2546
+ return;
2157
2547
  if (!item || item.disabled)
2158
2548
  return;
2159
2549
  this.validateItem(item);
@@ -2161,29 +2551,118 @@ class NgbTypeaheadComponent {
2161
2551
  const exists = this.selected.find((s) => s.id === item.id);
2162
2552
  if (exists) {
2163
2553
  this.selected = this.selected.filter((s) => s.id !== item.id);
2554
+ this.onUnselect.emit(item);
2164
2555
  }
2165
2556
  else {
2166
2557
  this.selected = [...this.selected, item];
2558
+ this.onSelect.emit(item);
2167
2559
  }
2560
+ this.propagateControlValue();
2561
+ // After selecting/unselecting, show the chosen values in the input and keep the panel open.
2562
+ this.keepOpenOnEmpty = true;
2563
+ if (this.chips) {
2564
+ this.query = '';
2565
+ }
2566
+ else {
2567
+ this.query = '';
2568
+ }
2569
+ this.applyFilter('');
2168
2570
  }
2169
2571
  else {
2572
+ if (this.selected.length && this.selected[0]?.id !== item.id) {
2573
+ this.onUnselect.emit(this.selected[0]);
2574
+ }
2170
2575
  this.selected = [item];
2576
+ this.query = item.label;
2577
+ if (this.inputEl?.nativeElement) {
2578
+ this.inputEl.nativeElement.value = item.label;
2579
+ }
2580
+ this.onSelect.emit(item);
2581
+ this.propagateControlValue();
2582
+ this.hideOverlay();
2171
2583
  }
2172
2584
  this.selectedItems.emit(this.selected);
2173
2585
  this.selectionChange.emit(this.selected);
2586
+ this.cdr.markForCheck();
2587
+ }
2588
+ focusInput() {
2589
+ this.inputEl?.nativeElement?.focus();
2590
+ }
2591
+ removeSelected(item) {
2592
+ if (this.disabled)
2593
+ return;
2594
+ if (!item)
2595
+ return;
2596
+ const exists = this.selected.find((s) => s.id === item.id);
2597
+ if (!exists)
2598
+ return;
2599
+ this.selected = this.selected.filter((s) => s.id !== item.id);
2600
+ this.onUnselect.emit(item);
2601
+ this.propagateControlValue();
2602
+ this.selectedItems.emit(this.selected);
2603
+ this.selectionChange.emit(this.selected);
2604
+ this.cdr.markForCheck();
2174
2605
  }
2175
2606
  clearSelection() {
2607
+ const previous = [...this.selected];
2176
2608
  this.selected = [];
2177
2609
  this.selectedItems.emit(this.selected);
2178
2610
  this.selectionChange.emit(this.selected);
2611
+ if (!this.multiSelect && previous[0]) {
2612
+ this.onUnselect.emit(previous[0]);
2613
+ }
2614
+ this.propagateControlValue();
2615
+ this.cdr.markForCheck();
2179
2616
  }
2180
2617
  isSelected(item) {
2181
2618
  return this.selected.some((s) => s.id === item.id);
2182
2619
  }
2183
2620
  trackById = (_, item) => item.id;
2621
+ getOptionId(index) {
2622
+ return `${this.overlayId}-opt-${index}`;
2623
+ }
2624
+ onDropdownButtonClick(event) {
2625
+ if (this.disabled)
2626
+ return;
2627
+ this.onDropdownClick.emit(event);
2628
+ if (this.overlayVisible) {
2629
+ this.hideOverlay(event);
2630
+ this.openedByDropdown = false;
2631
+ }
2632
+ else {
2633
+ this.openedByDropdown = true;
2634
+ if (this.scroller?.nativeElement) {
2635
+ this.scroller.nativeElement.scrollTop = 0;
2636
+ }
2637
+ this.showOverlay(event, true);
2638
+ // Show all items when the dropdown button is used, regardless of current query.
2639
+ this.applyFilter('');
2640
+ this.activatePreferredOption();
2641
+ }
2642
+ this.inputEl?.nativeElement.focus();
2643
+ this.cdr.markForCheck();
2644
+ }
2645
+ onClearClick(event) {
2646
+ if (this.disabled)
2647
+ return;
2648
+ this.onClear.emit(event);
2649
+ this.query = '';
2650
+ this.clearSelection();
2651
+ this.applyFilter('');
2652
+ this.hideOverlay(event);
2653
+ this.inputEl?.nativeElement.focus();
2654
+ this.cdr.markForCheck();
2655
+ }
2184
2656
  applyFilter(value) {
2185
2657
  const term = (value || '').toLowerCase();
2186
- const meetsThreshold = term.length >= this.characterTyped;
2658
+ if (term !== this.lastFilterTerm) {
2659
+ this.lastFilterTerm = term;
2660
+ if (this.scroller?.nativeElement) {
2661
+ this.scroller.nativeElement.scrollTop = 0;
2662
+ }
2663
+ }
2664
+ const meetsThreshold = term.length >= this.characterTyped || this.openedByDropdown || (this.multiSelect && this.keepOpenOnEmpty);
2665
+ this.hasSearched = term.length >= this.characterTyped || this.openedByDropdown;
2187
2666
  const base = meetsThreshold ? this.data.filter((d) => (d.label || '').toLowerCase().includes(term)) : [];
2188
2667
  const limited = this.limit ? base.slice(0, this.limit) : base;
2189
2668
  this.filtered = limited;
@@ -2191,8 +2670,23 @@ class NgbTypeaheadComponent {
2191
2670
  this.selectItem(limited[0]);
2192
2671
  }
2193
2672
  this.updateViewport(this.scroller?.nativeElement.scrollTop || 0);
2673
+ if (meetsThreshold && !this.overlayVisible && (this.inputFocused || this.openedByDropdown)) {
2674
+ this.showOverlay();
2675
+ }
2676
+ if (!meetsThreshold && this.overlayVisible && !this.openedByDropdown) {
2677
+ this.hideOverlay();
2678
+ }
2679
+ this.cdr.markForCheck();
2194
2680
  }
2195
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
+ }
2196
2690
  const total = this.filtered.length;
2197
2691
  const visibleCount = Math.ceil(this.viewportHeight / this.itemHeight) + 2;
2198
2692
  const start = Math.max(Math.floor(scrollTop / this.itemHeight), 0);
@@ -2200,140 +2694,489 @@ class NgbTypeaheadComponent {
2200
2694
  this.beforePadding = start * this.itemHeight;
2201
2695
  this.afterPadding = Math.max(total - end, 0) * this.itemHeight;
2202
2696
  this.visible = this.filtered.slice(start, end);
2697
+ this.viewportStartIndex = start;
2698
+ this.activeIndex = this.visible.length ? Math.min(this.activeIndex, this.visible.length - 1) : -1;
2203
2699
  }
2204
- focusSibling(direction) {
2205
- const buttons = this.scroller?.nativeElement.querySelectorAll('button.list-group-item');
2206
- if (!buttons || buttons.length === 0)
2700
+ activatePreferredOption() {
2701
+ if (!this.filtered.length || !this.scroller?.nativeElement) {
2702
+ this.activeIndex = -1;
2207
2703
  return;
2208
- const active = document.activeElement;
2209
- const index = Array.prototype.indexOf.call(buttons, active);
2210
- const next = buttons[index + direction] || buttons[(index + direction + buttons.length) % buttons.length];
2211
- next?.focus();
2704
+ }
2705
+ const trimmed = (this.query || '').trim().toLowerCase();
2706
+ let preferredIndex = -1;
2707
+ if (trimmed) {
2708
+ preferredIndex = this.filtered.findIndex((d) => (d.label || '').trim().toLowerCase() === trimmed);
2709
+ }
2710
+ if (preferredIndex === -1 && this.selected.length) {
2711
+ const firstSelected = this.selected[0];
2712
+ preferredIndex = this.filtered.findIndex((d) => d.id === firstSelected.id);
2713
+ }
2714
+ if (preferredIndex === -1) {
2715
+ preferredIndex = 0;
2716
+ }
2717
+ const scrollTop = Math.max(0, preferredIndex * this.itemHeight - this.itemHeight);
2718
+ this.scroller.nativeElement.scrollTop = scrollTop;
2719
+ this.updateViewport(scrollTop);
2720
+ const start = Math.max(Math.floor(scrollTop / this.itemHeight), 0);
2721
+ this.activeIndex = Math.max(0, Math.min(preferredIndex - start, this.visible.length - 1));
2722
+ }
2723
+ moveActive(direction) {
2724
+ if (!this.overlayVisible) {
2725
+ this.showOverlay();
2726
+ }
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;
2748
+ return;
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();
2212
2767
  }
2213
2768
  validateItem(item) {
2214
- if (this.itemTemplate) {
2215
- if (item.id == null || (item.value == null && item.value !== 0)) {
2216
- throw new Error('Custom templates require items to provide both id and value.');
2769
+ if (item.id == null) {
2770
+ throw new Error('Typeahead items must include an id.');
2771
+ }
2772
+ }
2773
+ showOverlay(event, openedByDropdown = false) {
2774
+ this.openedByDropdown = openedByDropdown;
2775
+ if (this.overlayVisible)
2776
+ return;
2777
+ this.overlayVisible = true;
2778
+ this.activeIndex = this.visible.length ? 0 : -1;
2779
+ this.onShow.emit(event ?? new Event('show'));
2780
+ this.cdr.markForCheck();
2781
+ }
2782
+ hideOverlay(event) {
2783
+ if (!this.overlayVisible)
2784
+ return;
2785
+ this.overlayVisible = false;
2786
+ this.openedByDropdown = false;
2787
+ this.activeIndex = -1;
2788
+ this.onHide.emit(event ?? new Event('hide'));
2789
+ this.cdr.markForCheck();
2790
+ }
2791
+ commitInputOnClose() {
2792
+ const trimmed = (this.query || '').trim();
2793
+ if (!trimmed) {
2794
+ return;
2795
+ }
2796
+ if (this.matchSelection) {
2797
+ const exact = this.data.find((d) => (d.label || '').toLowerCase() === trimmed.toLowerCase());
2798
+ if (!exact) {
2799
+ this.query = '';
2800
+ if (!this.multiSelect) {
2801
+ this.clearSelection();
2802
+ }
2803
+ }
2804
+ else if (!this.multiSelect) {
2805
+ this.selected = [exact];
2806
+ this.selectionChange.emit(this.selected);
2807
+ this.selectedItems.emit(this.selected);
2808
+ this.propagateControlValue();
2217
2809
  }
2810
+ return;
2811
+ }
2812
+ if (this.updateOnBlur && !this.multiSelect && this.selected.length === 0) {
2813
+ const added = { id: trimmed, label: trimmed, value: trimmed };
2814
+ this.selected = [added];
2815
+ this.onAdd.emit(added);
2816
+ this.selectionChange.emit(this.selected);
2817
+ this.selectedItems.emit(this.selected);
2818
+ this.propagateControlValue();
2819
+ }
2820
+ }
2821
+ splitBySeparators(value) {
2822
+ const separators = Array.isArray(this.separator) ? this.separator : [this.separator];
2823
+ const safe = separators.filter((s) => s != null && `${s}`.length > 0).map((s) => `${s}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
2824
+ if (!safe.length) {
2825
+ return { tokensToAdd: [], remaining: value };
2826
+ }
2827
+ const regex = new RegExp(`(?:${safe.join('|')})`);
2828
+ if (!regex.test(value)) {
2829
+ return { tokensToAdd: [], remaining: value };
2830
+ }
2831
+ const parts = value.split(new RegExp(`(?:${safe.join('|')})`, 'g')).map((p) => p.trim());
2832
+ const endsWithSeparator = separators.some((sep) => value.endsWith(sep));
2833
+ const tokensToAdd = (endsWithSeparator ? parts : parts.slice(0, -1)).filter(Boolean);
2834
+ const remaining = endsWithSeparator ? '' : (parts[parts.length - 1] || '');
2835
+ return { tokensToAdd, remaining };
2836
+ }
2837
+ addToken(raw) {
2838
+ const token = (raw || '').trim();
2839
+ if (!this.chips || !this.multiSelect)
2840
+ return;
2841
+ if (!token)
2842
+ return;
2843
+ const match = this.data.find((d) => (d.label || '').trim().toLowerCase() === token.toLowerCase());
2844
+ const item = match ?? { id: token, label: token, value: token };
2845
+ if (this.matchSelection && !match) {
2846
+ this.query = '';
2847
+ return;
2848
+ }
2849
+ if (this.selected.some((s) => s.id === item.id)) {
2850
+ this.query = '';
2851
+ return;
2218
2852
  }
2853
+ this.selected = [...this.selected, item];
2854
+ this.onAdd.emit(item);
2855
+ this.propagateControlValue();
2856
+ this.selectedItems.emit(this.selected);
2857
+ this.selectionChange.emit(this.selected);
2858
+ this.query = '';
2859
+ this.keepOpenOnEmpty = true;
2860
+ this.applyFilter('');
2219
2861
  }
2220
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbTypeaheadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2221
- 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", i18n: "i18n" }, outputs: { selectedItems: "selectedItems", selectionChange: "selectionChange", onChange: "onChange", onScrollEvent: "onScrollEvent" }, queries: [{ propertyName: "itemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, static: true }, { propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, static: true }, { propertyName: "itemButtons", predicate: ["itemButton"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
2222
- <div class="mb-2">
2223
- <input
2224
- #inputEl
2225
- type="text"
2226
- class="form-control"
2227
- [attr.placeholder]="i18n?.placeholder || 'Type to search'"
2228
- [ngModel]="query"
2229
- (ngModelChange)="onInput($event)"
2230
- (keydown)="onKeydown($event)"
2231
- aria-autocomplete="list"
2232
- role="combobox"
2233
- [attr.aria-expanded]="filtered.length > 0"
2234
- />
2235
- </div>
2862
+ propagateControlValue() {
2863
+ if (this.multiSelect) {
2864
+ const next = this.selected.map((item) => (item.value !== undefined ? item.value : item));
2865
+ this.controlValue = next;
2866
+ this.onControlChange(next);
2867
+ return;
2868
+ }
2869
+ const first = this.selected[0];
2870
+ const next = first ? (first.value !== undefined ? first.value : first) : null;
2871
+ this.controlValue = next;
2872
+ this.onControlChange(next);
2873
+ }
2874
+ coerceToItem(value) {
2875
+ if (value == null)
2876
+ return null;
2877
+ if (typeof value === 'object' && value.id != null && value.label != null) {
2878
+ return value;
2879
+ }
2880
+ const match = this.data.find((d) => d.value === value || d.id === value || (d.label || '').toLowerCase() === `${value}`.toLowerCase());
2881
+ if (match)
2882
+ return match;
2883
+ return { id: `${value}`, label: `${value}`, value };
2884
+ }
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 });
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: [
2887
+ {
2888
+ provide: NG_VALUE_ACCESSOR,
2889
+ useExisting: forwardRef(() => NgbTypeaheadComponent),
2890
+ multi: true,
2891
+ },
2892
+ ], queries: [{ propertyName: "projectedItemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, static: true }, { propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true }, { propertyName: "itemButtons", predicate: ["itemButton"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
2893
+ <div class="typeahead-root">
2894
+ <div class="input-group mb-2">
2895
+ <ng-container *ngIf="chips && multiSelect; else plainInput">
2896
+ <div class="form-control typeahead-chips-control" (click)="disabled ? null : focusInput()">
2897
+ <ngb-chips
2898
+ [items]="selected"
2899
+ [removable]="true"
2900
+ (remove)="removeSelected($event)"
2901
+ ariaLabel="Selected items"
2902
+ ></ngb-chips>
2903
+ <input
2904
+ #inputEl
2905
+ type="text"
2906
+ [disabled]="disabled"
2907
+ [attr.placeholder]="selected.length ? '' : (i18n?.placeholder || 'Type to search')"
2908
+ [value]="query"
2909
+ (input)="onNativeInput($event)"
2910
+ (focus)="onInputFocus($event)"
2911
+ (blur)="onInputBlur($event)"
2912
+ (keydown)="onKeydown($event)"
2913
+ (keyup)="onKeyup($event)"
2914
+ aria-autocomplete="list"
2915
+ role="combobox"
2916
+ [attr.aria-expanded]="overlayVisible"
2917
+ [attr.aria-controls]="overlayId"
2918
+ [attr.aria-activedescendant]="activeDescendantId"
2919
+ />
2920
+ </div>
2921
+ </ng-container>
2922
+ <ng-template #plainInput>
2923
+ <input
2924
+ #inputEl
2925
+ type="text"
2926
+ class="form-control"
2927
+ [disabled]="disabled"
2928
+ [attr.placeholder]="i18n?.placeholder || 'Type to search'"
2929
+ [value]="displayQuery"
2930
+ (input)="onNativeInput($event)"
2931
+ (focus)="onInputFocus($event)"
2932
+ (blur)="onInputBlur($event)"
2933
+ (keydown)="onKeydown($event)"
2934
+ (keyup)="onKeyup($event)"
2935
+ aria-autocomplete="list"
2936
+ role="combobox"
2937
+ [attr.aria-expanded]="overlayVisible"
2938
+ [attr.aria-controls]="overlayId"
2939
+ [attr.aria-activedescendant]="activeDescendantId"
2940
+ />
2941
+ </ng-template>
2942
+
2943
+ <button
2944
+ *ngIf="showClearButton && (query || (!multiSelect && selected.length))"
2945
+ type="button"
2946
+ class="btn btn-outline-secondary"
2947
+ (click)="onClearClick($event)"
2948
+ [disabled]="disabled"
2949
+ [attr.aria-label]="i18n?.clear || 'Clear'"
2950
+ >
2951
+ &times;
2952
+ </button>
2953
+
2954
+ <button
2955
+ *ngIf="showDropdownButton"
2956
+ type="button"
2957
+ class="btn btn-outline-secondary"
2958
+ (click)="onDropdownButtonClick($event)"
2959
+ [disabled]="disabled"
2960
+ [attr.aria-label]="i18n?.dropdownButtonLabel || 'Toggle suggestions'"
2961
+ >
2962
+ &#9662;
2963
+ </button>
2964
+ </div>
2236
2965
 
2237
- <div
2238
- #scroller
2239
- class="border rounded list-group"
2240
- style="max-height: 240px; overflow: auto;"
2241
- role="listbox"
2242
- (scroll)="onScroll($event)"
2243
- >
2244
- <div [style.height.px]="beforePadding"></div>
2245
- <button
2246
- *ngFor="let item of visible; trackBy: trackById"
2247
- type="button"
2248
- class="list-group-item list-group-item-action d-flex align-items-center justify-content-between"
2249
- [class.active]="isSelected(item)"
2250
- [attr.aria-selected]="isSelected(item)"
2251
- (click)="selectItem(item)"
2252
- [disabled]="item.disabled"
2966
+ <div
2967
+ #scroller
2968
+ class="dropdown-menu w-100 p-0 typeahead-panel"
2969
+ [class.show]="overlayVisible"
2970
+ [id]="overlayId"
2971
+ role="listbox"
2972
+ (scroll)="onScroll($event)"
2253
2973
  >
2254
- <ng-container *ngIf="itemTemplate; else defaultTpl" [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item }"></ng-container>
2255
- <ng-template #defaultTpl>
2256
- <span>{{ item.label }}</span>
2257
- <span *ngIf="multiSelect && isSelected(item)" class="badge bg-secondary">✓</span>
2258
- </ng-template>
2259
- </button>
2260
- <div [style.height.px]="afterPadding"></div>
2974
+ <div *ngIf="vScroll" [style.height.px]="beforePadding"></div>
2975
+
2976
+ <button
2977
+ *ngFor="let item of visible; trackBy: trackById; let idx = index"
2978
+ type="button"
2979
+ class="dropdown-item"
2980
+ [class.active]="idx === activeIndex"
2981
+ [class.fw-semibold]="isSelected(item)"
2982
+ [attr.aria-selected]="isSelected(item)"
2983
+ [attr.id]="getOptionId(idx)"
2984
+ (click)="selectItem(item)"
2985
+ [disabled]="item.disabled"
2986
+ >
2987
+ <div class="typeahead-item">
2988
+ <span *ngIf="multiSelect" class="typeahead-item-checkbox">
2989
+ <input
2990
+ type="checkbox"
2991
+ class="form-check-input m-0"
2992
+ [checked]="isSelected(item)"
2993
+ [disabled]="item.disabled"
2994
+ tabindex="-1"
2995
+ aria-hidden="true"
2996
+ />
2997
+ </span>
2998
+ <ng-container *ngIf="resolvedItemTemplate as tpl; else defaultTpl">
2999
+ <ng-container
3000
+ [ngTemplateOutlet]="tpl"
3001
+ [ngTemplateOutletContext]="{
3002
+ $implicit: item,
3003
+ item: item,
3004
+ query: query,
3005
+ selected: isSelected(item),
3006
+ index: idx
3007
+ }"
3008
+ ></ng-container>
3009
+ </ng-container>
3010
+ <ng-template #defaultTpl>
3011
+ <span>{{ item.label }}</span>
3012
+ </ng-template>
3013
+ </div>
3014
+ </button>
2261
3015
 
2262
- <div *ngIf="!filtered.length" class="list-group-item text-muted text-center">
2263
- {{ i18n?.noResults || 'No results' }}
3016
+ <div *ngIf="vScroll" [style.height.px]="afterPadding"></div>
3017
+
3018
+ <div *ngIf="showNoResults" class="dropdown-item text-muted text-center">
3019
+ {{ i18n?.noResults || 'No results' }}
3020
+ </div>
2264
3021
  </div>
2265
- </div>
2266
3022
 
2267
- <div *ngIf="multiSelect && selected.length" class="mt-2 small text-muted">
2268
- {{ selectionSummary }}
2269
- <button type="button" class="btn btn-link btn-sm ps-1" (click)="clearSelection()">
2270
- {{ i18n?.clearSelection || 'Clear' }}
2271
- </button>
3023
+ <div *ngIf="multiSelect && selected.length" class="mt-2 small text-muted">
3024
+ {{ selectionSummary }}
3025
+ <button type="button" class="btn btn-link btn-sm ps-1" (click)="clearSelection()">
3026
+ {{ i18n?.clearSelection || 'Clear' }}
3027
+ </button>
3028
+ </div>
2272
3029
  </div>
2273
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3030
+ `, isInline: true, styles: [":host{display:block}.typeahead-root{position:relative}.typeahead-chips-control{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem;min-height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem}.typeahead-chips-control input{border:0;outline:0;flex:1 1 9rem;min-width:6rem;padding:0;margin:0;background:transparent}.typeahead-chips-control input:focus{outline:0;box-shadow:none}.typeahead-panel{position:absolute;top:calc(100% + .25rem);left:0;right:0;z-index:1050;max-height:240px;overflow:auto;opacity:0;transform:translateY(-.25rem);pointer-events:none;transition:opacity .12s ease,transform .12s ease}.typeahead-panel.show{opacity:1;transform:translateY(0);pointer-events:auto}.typeahead-item{display:flex;align-items:center;gap:.5rem}.typeahead-item-checkbox{display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgbChipsComponent, selector: "ngb-chips", inputs: ["items", "removable", "ariaLabel", "removeLabel"], outputs: ["remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2274
3031
  }
2275
3032
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbTypeaheadComponent, decorators: [{
2276
3033
  type: Component,
2277
- args: [{
2278
- selector: 'ngb-typeahead',
2279
- standalone: true,
2280
- imports: [CommonModule, FormsModule],
2281
- changeDetection: ChangeDetectionStrategy.OnPush,
2282
- template: `
2283
- <div class="mb-2">
2284
- <input
2285
- #inputEl
2286
- type="text"
2287
- class="form-control"
2288
- [attr.placeholder]="i18n?.placeholder || 'Type to search'"
2289
- [ngModel]="query"
2290
- (ngModelChange)="onInput($event)"
2291
- (keydown)="onKeydown($event)"
2292
- aria-autocomplete="list"
2293
- role="combobox"
2294
- [attr.aria-expanded]="filtered.length > 0"
2295
- />
2296
- </div>
3034
+ args: [{ selector: 'ngb-typeahead', standalone: true, imports: [CommonModule, NgbChipsComponent], providers: [
3035
+ {
3036
+ provide: NG_VALUE_ACCESSOR,
3037
+ useExisting: forwardRef(() => NgbTypeaheadComponent),
3038
+ multi: true,
3039
+ },
3040
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: `
3041
+ <div class="typeahead-root">
3042
+ <div class="input-group mb-2">
3043
+ <ng-container *ngIf="chips && multiSelect; else plainInput">
3044
+ <div class="form-control typeahead-chips-control" (click)="disabled ? null : focusInput()">
3045
+ <ngb-chips
3046
+ [items]="selected"
3047
+ [removable]="true"
3048
+ (remove)="removeSelected($event)"
3049
+ ariaLabel="Selected items"
3050
+ ></ngb-chips>
3051
+ <input
3052
+ #inputEl
3053
+ type="text"
3054
+ [disabled]="disabled"
3055
+ [attr.placeholder]="selected.length ? '' : (i18n?.placeholder || 'Type to search')"
3056
+ [value]="query"
3057
+ (input)="onNativeInput($event)"
3058
+ (focus)="onInputFocus($event)"
3059
+ (blur)="onInputBlur($event)"
3060
+ (keydown)="onKeydown($event)"
3061
+ (keyup)="onKeyup($event)"
3062
+ aria-autocomplete="list"
3063
+ role="combobox"
3064
+ [attr.aria-expanded]="overlayVisible"
3065
+ [attr.aria-controls]="overlayId"
3066
+ [attr.aria-activedescendant]="activeDescendantId"
3067
+ />
3068
+ </div>
3069
+ </ng-container>
3070
+ <ng-template #plainInput>
3071
+ <input
3072
+ #inputEl
3073
+ type="text"
3074
+ class="form-control"
3075
+ [disabled]="disabled"
3076
+ [attr.placeholder]="i18n?.placeholder || 'Type to search'"
3077
+ [value]="displayQuery"
3078
+ (input)="onNativeInput($event)"
3079
+ (focus)="onInputFocus($event)"
3080
+ (blur)="onInputBlur($event)"
3081
+ (keydown)="onKeydown($event)"
3082
+ (keyup)="onKeyup($event)"
3083
+ aria-autocomplete="list"
3084
+ role="combobox"
3085
+ [attr.aria-expanded]="overlayVisible"
3086
+ [attr.aria-controls]="overlayId"
3087
+ [attr.aria-activedescendant]="activeDescendantId"
3088
+ />
3089
+ </ng-template>
2297
3090
 
2298
- <div
2299
- #scroller
2300
- class="border rounded list-group"
2301
- style="max-height: 240px; overflow: auto;"
2302
- role="listbox"
2303
- (scroll)="onScroll($event)"
2304
- >
2305
- <div [style.height.px]="beforePadding"></div>
2306
- <button
2307
- *ngFor="let item of visible; trackBy: trackById"
2308
- type="button"
2309
- class="list-group-item list-group-item-action d-flex align-items-center justify-content-between"
2310
- [class.active]="isSelected(item)"
2311
- [attr.aria-selected]="isSelected(item)"
2312
- (click)="selectItem(item)"
2313
- [disabled]="item.disabled"
3091
+ <button
3092
+ *ngIf="showClearButton && (query || (!multiSelect && selected.length))"
3093
+ type="button"
3094
+ class="btn btn-outline-secondary"
3095
+ (click)="onClearClick($event)"
3096
+ [disabled]="disabled"
3097
+ [attr.aria-label]="i18n?.clear || 'Clear'"
3098
+ >
3099
+ &times;
3100
+ </button>
3101
+
3102
+ <button
3103
+ *ngIf="showDropdownButton"
3104
+ type="button"
3105
+ class="btn btn-outline-secondary"
3106
+ (click)="onDropdownButtonClick($event)"
3107
+ [disabled]="disabled"
3108
+ [attr.aria-label]="i18n?.dropdownButtonLabel || 'Toggle suggestions'"
3109
+ >
3110
+ &#9662;
3111
+ </button>
3112
+ </div>
3113
+
3114
+ <div
3115
+ #scroller
3116
+ class="dropdown-menu w-100 p-0 typeahead-panel"
3117
+ [class.show]="overlayVisible"
3118
+ [id]="overlayId"
3119
+ role="listbox"
3120
+ (scroll)="onScroll($event)"
2314
3121
  >
2315
- <ng-container *ngIf="itemTemplate; else defaultTpl" [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item }"></ng-container>
2316
- <ng-template #defaultTpl>
2317
- <span>{{ item.label }}</span>
2318
- <span *ngIf="multiSelect && isSelected(item)" class="badge bg-secondary">✓</span>
2319
- </ng-template>
2320
- </button>
2321
- <div [style.height.px]="afterPadding"></div>
3122
+ <div *ngIf="vScroll" [style.height.px]="beforePadding"></div>
3123
+
3124
+ <button
3125
+ *ngFor="let item of visible; trackBy: trackById; let idx = index"
3126
+ type="button"
3127
+ class="dropdown-item"
3128
+ [class.active]="idx === activeIndex"
3129
+ [class.fw-semibold]="isSelected(item)"
3130
+ [attr.aria-selected]="isSelected(item)"
3131
+ [attr.id]="getOptionId(idx)"
3132
+ (click)="selectItem(item)"
3133
+ [disabled]="item.disabled"
3134
+ >
3135
+ <div class="typeahead-item">
3136
+ <span *ngIf="multiSelect" class="typeahead-item-checkbox">
3137
+ <input
3138
+ type="checkbox"
3139
+ class="form-check-input m-0"
3140
+ [checked]="isSelected(item)"
3141
+ [disabled]="item.disabled"
3142
+ tabindex="-1"
3143
+ aria-hidden="true"
3144
+ />
3145
+ </span>
3146
+ <ng-container *ngIf="resolvedItemTemplate as tpl; else defaultTpl">
3147
+ <ng-container
3148
+ [ngTemplateOutlet]="tpl"
3149
+ [ngTemplateOutletContext]="{
3150
+ $implicit: item,
3151
+ item: item,
3152
+ query: query,
3153
+ selected: isSelected(item),
3154
+ index: idx
3155
+ }"
3156
+ ></ng-container>
3157
+ </ng-container>
3158
+ <ng-template #defaultTpl>
3159
+ <span>{{ item.label }}</span>
3160
+ </ng-template>
3161
+ </div>
3162
+ </button>
2322
3163
 
2323
- <div *ngIf="!filtered.length" class="list-group-item text-muted text-center">
2324
- {{ i18n?.noResults || 'No results' }}
3164
+ <div *ngIf="vScroll" [style.height.px]="afterPadding"></div>
3165
+
3166
+ <div *ngIf="showNoResults" class="dropdown-item text-muted text-center">
3167
+ {{ i18n?.noResults || 'No results' }}
3168
+ </div>
2325
3169
  </div>
2326
- </div>
2327
3170
 
2328
- <div *ngIf="multiSelect && selected.length" class="mt-2 small text-muted">
2329
- {{ selectionSummary }}
2330
- <button type="button" class="btn btn-link btn-sm ps-1" (click)="clearSelection()">
2331
- {{ i18n?.clearSelection || 'Clear' }}
2332
- </button>
3171
+ <div *ngIf="multiSelect && selected.length" class="mt-2 small text-muted">
3172
+ {{ selectionSummary }}
3173
+ <button type="button" class="btn btn-link btn-sm ps-1" (click)="clearSelection()">
3174
+ {{ i18n?.clearSelection || 'Clear' }}
3175
+ </button>
3176
+ </div>
2333
3177
  </div>
2334
- `,
2335
- }]
2336
- }], propDecorators: { data: [{
3178
+ `, styles: [":host{display:block}.typeahead-root{position:relative}.typeahead-chips-control{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem;min-height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem}.typeahead-chips-control input{border:0;outline:0;flex:1 1 9rem;min-width:6rem;padding:0;margin:0;background:transparent}.typeahead-chips-control input:focus{outline:0;box-shadow:none}.typeahead-panel{position:absolute;top:calc(100% + .25rem);left:0;right:0;z-index:1050;max-height:240px;overflow:auto;opacity:0;transform:translateY(-.25rem);pointer-events:none;transition:opacity .12s ease,transform .12s ease}.typeahead-panel.show{opacity:1;transform:translateY(0);pointer-events:auto}.typeahead-item{display:flex;align-items:center;gap:.5rem}.typeahead-item-checkbox{display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem}\n"] }]
3179
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { data: [{
2337
3180
  type: Input
2338
3181
  }], debounceTime: [{
2339
3182
  type: Input
@@ -2345,11 +3188,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
2345
3188
  type: Input
2346
3189
  }], multiSelect: [{
2347
3190
  type: Input
2348
- }], i18n: [{
3191
+ }], matchSelection: [{
3192
+ type: Input
3193
+ }], showDropdownButton: [{
3194
+ type: Input
3195
+ }], showClearButton: [{
3196
+ type: Input
3197
+ }], updateOnBlur: [{
3198
+ type: Input
3199
+ }], updateOnTab: [{
3200
+ type: Input
3201
+ }], separator: [{
3202
+ type: Input
3203
+ }], chips: [{
3204
+ type: Input
3205
+ }], vScroll: [{
3206
+ type: Input
3207
+ }], vItemSize: [{
2349
3208
  type: Input
2350
3209
  }], itemTemplate: [{
3210
+ type: Input
3211
+ }], i18n: [{
3212
+ type: Input
3213
+ }], projectedItemTemplate: [{
2351
3214
  type: ContentChild,
2352
3215
  args: [TemplateRef]
3216
+ }], completeMethod: [{
3217
+ type: Output
3218
+ }], onSelect: [{
3219
+ type: Output
3220
+ }], onUnselect: [{
3221
+ type: Output
3222
+ }], onAdd: [{
3223
+ type: Output
3224
+ }], onFocus: [{
3225
+ type: Output
3226
+ }], onBlur: [{
3227
+ type: Output
3228
+ }], onDropdownClick: [{
3229
+ type: Output
3230
+ }], onClear: [{
3231
+ type: Output
3232
+ }], onInputKeydown: [{
3233
+ type: Output
3234
+ }], onKeyUp: [{
3235
+ type: Output
3236
+ }], onShow: [{
3237
+ type: Output
3238
+ }], onHide: [{
3239
+ type: Output
3240
+ }], onLazyLoad: [{
3241
+ type: Output
2353
3242
  }], selectedItems: [{
2354
3243
  type: Output
2355
3244
  }], selectionChange: [{
@@ -2363,10 +3252,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
2363
3252
  args: ['scroller', { static: true }]
2364
3253
  }], inputEl: [{
2365
3254
  type: ViewChild,
2366
- args: ['inputEl', { static: true }]
3255
+ args: ['inputEl']
2367
3256
  }], itemButtons: [{
2368
3257
  type: ViewChildren,
2369
3258
  args: ['itemButton']
3259
+ }], onDocumentMouseDown: [{
3260
+ type: HostListener,
3261
+ args: ['document:mousedown', ['$event']]
3262
+ }], onDocumentFocusIn: [{
3263
+ type: HostListener,
3264
+ args: ['document:focusin', ['$event']]
2370
3265
  }] } });
2371
3266
 
2372
3267
  class NgbTreeComponent {
@@ -2374,6 +3269,10 @@ class NgbTreeComponent {
2374
3269
  type = 'text';
2375
3270
  showCheckbox = false;
2376
3271
  i18n;
3272
+ expandIcon = 'bi-caret-right-fill';
3273
+ collapseIcon = 'bi-caret-down-fill';
3274
+ plusIcon = 'bi-file-plus-fill';
3275
+ minusIcon = 'bi-file-minus-fill';
2377
3276
  expand = new EventEmitter();
2378
3277
  collapse = new EventEmitter();
2379
3278
  expandAll = new EventEmitter();
@@ -2454,7 +3353,7 @@ class NgbTreeComponent {
2454
3353
  return acc;
2455
3354
  }
2456
3355
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2457
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbTreeComponent, isStandalone: true, selector: "ngb-tree", inputs: { nodes: "nodes", type: "type", showCheckbox: "showCheckbox", i18n: "i18n" }, outputs: { expand: "expand", collapse: "collapse", expandAll: "expandAll", collapseAll: "collapseAll", selectionChange: "selectionChange" }, viewQueries: [{ propertyName: "nodeButtons", predicate: ["nodeBtn"], descendants: true, read: ElementRef }], ngImport: i0, template: `
3356
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: NgbTreeComponent, isStandalone: true, selector: "ngb-tree", inputs: { nodes: "nodes", type: "type", showCheckbox: "showCheckbox", i18n: "i18n", expandIcon: "expandIcon", collapseIcon: "collapseIcon", plusIcon: "plusIcon", minusIcon: "minusIcon" }, outputs: { expand: "expand", collapse: "collapse", expandAll: "expandAll", collapseAll: "collapseAll", selectionChange: "selectionChange" }, viewQueries: [{ propertyName: "nodeButtons", predicate: ["nodeBtn"], descendants: true, read: ElementRef }], ngImport: i0, template: `
2458
3357
  <div class="d-flex gap-2 mb-2">
2459
3358
  <button type="button" class="btn btn-sm btn-outline-secondary" (click)="expandAllNodes()" [attr.aria-label]="i18n?.expandAll || 'Expand all'">
2460
3359
  {{ i18n?.expandAll || 'Expand all' }}
@@ -2477,21 +3376,22 @@ class NgbTreeComponent {
2477
3376
  [attr.aria-expanded]="hasChildren(node) ? !!node.expanded : null"
2478
3377
  [attr.aria-level]="level"
2479
3378
  >
2480
- <button
2481
- #nodeBtn
2482
- type="button"
2483
- class="btn btn-sm btn-link px-1"
2484
- [disabled]="!hasChildren(node)"
2485
- (click)="toggleNode(node)"
2486
- (keydown)="onNodeKeydown($event, node)"
2487
- [attr.aria-label]="(node.expanded ? i18n?.collapse || 'Collapse' : i18n?.expand || 'Expand') + ' ' + node.label"
2488
- >
2489
- <span *ngIf="hasChildren(node)">
2490
- <span *ngIf="type === 'json'">{{ node.expanded ? '-' : '+' }}</span>
2491
- <span *ngIf="type !== 'json'">{{ node.expanded ? '▾' : '▸' }}</span>
2492
- </span>
2493
- <span *ngIf="!hasChildren(node)" class="text-muted">•</span>
2494
- </button>
3379
+ <ng-container *ngIf="hasChildren(node); else leafSpacer">
3380
+ <button
3381
+ #nodeBtn
3382
+ type="button"
3383
+ class="btn btn-sm px-1"
3384
+ (click)="toggleNode(node)"
3385
+ (keydown)="onNodeKeydown($event, node)"
3386
+ [attr.aria-label]="(node.expanded ? i18n?.collapse || 'Collapse' : i18n?.expand || 'Expand') + ' ' + node.label"
3387
+ >
3388
+ <span *ngIf="type === 'json'" class="bi" [ngClass]="node.expanded ? minusIcon : plusIcon" aria-hidden="true"></span>
3389
+ <span *ngIf="type !== 'json'" class="bi" [ngClass]="node.expanded ? collapseIcon : expandIcon" aria-hidden="true"></span>
3390
+ </button>
3391
+ </ng-container>
3392
+ <ng-template #leafSpacer>
3393
+ <span class="d-inline-block px-1" style="width: 1.5rem;"></span>
3394
+ </ng-template>
2495
3395
 
2496
3396
  <label class="form-check d-inline-flex align-items-center gap-2 flex-grow-1 mb-0">
2497
3397
  <input
@@ -2517,7 +3417,7 @@ class NgbTreeComponent {
2517
3417
  </ng-container>
2518
3418
  </ul>
2519
3419
  </ng-template>
2520
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3420
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2521
3421
  }
2522
3422
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: NgbTreeComponent, decorators: [{
2523
3423
  type: Component,
@@ -2549,21 +3449,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
2549
3449
  [attr.aria-expanded]="hasChildren(node) ? !!node.expanded : null"
2550
3450
  [attr.aria-level]="level"
2551
3451
  >
2552
- <button
2553
- #nodeBtn
2554
- type="button"
2555
- class="btn btn-sm btn-link px-1"
2556
- [disabled]="!hasChildren(node)"
2557
- (click)="toggleNode(node)"
2558
- (keydown)="onNodeKeydown($event, node)"
2559
- [attr.aria-label]="(node.expanded ? i18n?.collapse || 'Collapse' : i18n?.expand || 'Expand') + ' ' + node.label"
2560
- >
2561
- <span *ngIf="hasChildren(node)">
2562
- <span *ngIf="type === 'json'">{{ node.expanded ? '-' : '+' }}</span>
2563
- <span *ngIf="type !== 'json'">{{ node.expanded ? '▾' : '▸' }}</span>
2564
- </span>
2565
- <span *ngIf="!hasChildren(node)" class="text-muted">•</span>
2566
- </button>
3452
+ <ng-container *ngIf="hasChildren(node); else leafSpacer">
3453
+ <button
3454
+ #nodeBtn
3455
+ type="button"
3456
+ class="btn btn-sm px-1"
3457
+ (click)="toggleNode(node)"
3458
+ (keydown)="onNodeKeydown($event, node)"
3459
+ [attr.aria-label]="(node.expanded ? i18n?.collapse || 'Collapse' : i18n?.expand || 'Expand') + ' ' + node.label"
3460
+ >
3461
+ <span *ngIf="type === 'json'" class="bi" [ngClass]="node.expanded ? minusIcon : plusIcon" aria-hidden="true"></span>
3462
+ <span *ngIf="type !== 'json'" class="bi" [ngClass]="node.expanded ? collapseIcon : expandIcon" aria-hidden="true"></span>
3463
+ </button>
3464
+ </ng-container>
3465
+ <ng-template #leafSpacer>
3466
+ <span class="d-inline-block px-1" style="width: 1.5rem;"></span>
3467
+ </ng-template>
2567
3468
 
2568
3469
  <label class="form-check d-inline-flex align-items-center gap-2 flex-grow-1 mb-0">
2569
3470
  <input
@@ -2599,6 +3500,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
2599
3500
  type: Input
2600
3501
  }], i18n: [{
2601
3502
  type: Input
3503
+ }], expandIcon: [{
3504
+ type: Input
3505
+ }], collapseIcon: [{
3506
+ type: Input
3507
+ }], plusIcon: [{
3508
+ type: Input
3509
+ }], minusIcon: [{
3510
+ type: Input
2602
3511
  }], expand: [{
2603
3512
  type: Output
2604
3513
  }], collapse: [{
@@ -2620,5 +3529,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
2620
3529
  * Generated bundle index. Do not edit.
2621
3530
  */
2622
3531
 
2623
- export { DATAGRID_TEMPLATE_DIRECTIVES, DND_I18N, DND_LIVE_ANNOUNCE, Datagrid, ExcelExportAdapter, ExportButtonDirective, JsPdfAdapter, NgbCellTemplate, NgbDndItemDirective, NgbDndListDirective, NgbDndState, NgbEditorTemplate, NgbExportService, NgbFilterTemplate, NgbGlobalFilterTemplate, NgbLiveAnnouncer, NgbPaginationComponent, NgbRowDetailTemplate, NgbSplitterComponent, NgbStepLabelDirective, NgbStepperComponent, NgbTreeComponent, NgbTypeaheadComponent, PdfExportAdapter, XlsxAdapter, defaultDndI18n };
3532
+ export { DATAGRID_TEMPLATE_DIRECTIVES, DND_I18N, DND_LIVE_ANNOUNCE, Datagrid, ExcelExportAdapter, ExportButtonDirective, JsPdfAdapter, NgbCellTemplate, NgbChipsComponent, NgbDndItemDirective, NgbDndListDirective, NgbDndState, NgbEditorTemplate, NgbExportService, NgbFilterTemplate, NgbGlobalFilterTemplate, NgbLiveAnnouncer, NgbPaginationComponent, NgbRowDetailTemplate, NgbSplitterComponent, NgbSplitterPaneComponent, NgbStepLabelDirective, NgbStepperComponent, NgbTreeComponent, NgbTypeaheadComponent, PdfExportAdapter, XlsxAdapter, defaultDndI18n };
2624
3533
  //# sourceMappingURL=angular-bootstrap-ngbootstrap.mjs.map