├── projects ├── demo │ ├── src │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── app │ │ │ ├── app.component.scss │ │ │ ├── app.component.html │ │ │ ├── app.service.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app-routing.module.ts │ │ │ ├── directive-test │ │ │ │ ├── directive-test.component.scss │ │ │ │ ├── directive-test.component.spec.ts │ │ │ │ ├── directive-test.component.html │ │ │ │ └── directive-test.component.ts │ │ │ ├── component-test │ │ │ │ ├── component-test.component.spec.ts │ │ │ │ ├── component-test.component.scss │ │ │ │ ├── component-test.component.html │ │ │ │ └── component-test.component.ts │ │ │ ├── app.service.ts │ │ │ ├── app.component.spec.ts │ │ │ └── app.module.ts │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── styles.scss │ │ ├── index.html │ │ ├── main.ts │ │ ├── test.ts │ │ └── polyfills.ts │ ├── cypress.config.ts │ ├── cypress │ │ ├── tsconfig.json │ │ └── e2e │ │ │ └── spec.cy.ts │ ├── tsconfig.app.json │ ├── .browserslistrc │ ├── tsconfig.spec.json │ ├── .eslintrc.json │ └── karma.conf.js └── auto-complete │ ├── ng-package.json │ ├── tsconfig.lib.prod.json │ ├── src │ ├── public-api.ts │ ├── test.ts │ └── lib │ │ ├── auto-complete.module.ts │ │ ├── auto-complete.component.scss │ │ ├── auto-complete.component.html │ │ ├── auto-complete.service.ts │ │ ├── auto-complete.component.ts │ │ └── auto-complete.directive.ts │ ├── tsconfig.spec.json │ ├── tsconfig.lib.json │ ├── package.json │ ├── .eslintrc.json │ └── karma.conf.js ├── .gitattributes ├── docs ├── favicon.ico ├── 3rdpartylicenses.txt ├── polyfills-MH5IBZ74.js └── index.html ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── LICENSE.md ├── .eslintrc.json ├── .github └── ISSUE_TEMPLATE.md ├── package.json ├── CHANGELOG.md ├── angular.json └── README.md /projects/demo/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng2-ui/auto-complete/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | @import "https://fonts.googleapis.com/icon?family=Material+Icons"; 2 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ng2-ui/auto-complete/HEAD/projects/demo/src/favicon.ico -------------------------------------------------------------------------------- /projects/demo/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "@angular/material/prebuilt-themes/deeppurple-amber.css"; 3 | -------------------------------------------------------------------------------- /projects/auto-complete/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/auto-complete/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.lib.json", 3 | "compilerOptions": { 4 | "declarationMap": false 5 | }, 6 | "angularCompilerOptions": { 7 | "compilationMode": "partial" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /projects/demo/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: 'http://localhost:4200/auto-complete', 6 | supportFile: false, 7 | specPattern: 'projects/demo/**/*.cy.ts' 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /projects/demo/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "include": [ 4 | "**/*.ts", 5 | "../cypress.config.ts" 6 | ], 7 | "compilerOptions": { 8 | "types": [ 9 | "cypress" 10 | ], 11 | "sourceMap": false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /projects/auto-complete/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of auto-complete 3 | */ 4 | 5 | export * from './lib/auto-complete.service'; 6 | export * from './lib/auto-complete.component'; 7 | export * from './lib/auto-complete.directive'; 8 | export * from './lib/auto-complete.module'; 9 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/docs", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /projects/auto-complete/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular auto-complete Showcase 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /projects/demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppService', () => { 6 | let service: AppService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(AppService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /projects/demo/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /projects/demo/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts", 13 | "cypress.config.ts" 14 | ], 15 | "include": [ 16 | "src/**/*.spec.ts", 17 | "src/**/*.cy.ts", 18 | "src/**/*.d.ts" 19 | ], 20 | "exclude": [ 21 | "cypress/**/*.ts", 22 | "cypress.config.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'] 8 | }) 9 | export class AppComponent { 10 | public links = [{name: 'Directive', url: '/directive-test'}, {name: 'Component', url: '/component-test'}]; 11 | public activeLink: string = '/directive-test'; 12 | 13 | constructor(public router: Router) { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /projects/auto-complete/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "declarationMap": true, 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "DOM", 11 | "ESNext" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "skipTemplateCodegen": true, 16 | "strictMetadataEmit": true, 17 | "enableResourceInlining": true 18 | }, 19 | "exclude": [ 20 | "src/test.ts", 21 | "**/*.spec.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /projects/demo/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 6 | 7 | // First, initialize the Angular testing environment. 8 | getTestBed().initTestEnvironment( 9 | BrowserDynamicTestingModule, 10 | platformBrowserDynamicTesting(), { 11 | teardown: {destroyAfterEach: false} 12 | } 13 | ); 14 | -------------------------------------------------------------------------------- /projects/auto-complete/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js'; 4 | import 'zone.js/testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 7 | 8 | // First, initialize the Angular testing environment. 9 | getTestBed().initTestEnvironment( 10 | BrowserDynamicTestingModule, 11 | platformBrowserDynamicTesting(), { 12 | teardown: {destroyAfterEach: false} 13 | } 14 | ); 15 | -------------------------------------------------------------------------------- /projects/demo/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { DirectiveTestComponent } from './directive-test/directive-test.component'; 4 | import { ComponentTestComponent } from './component-test/component-test.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: 'directive-test', component: DirectiveTestComponent 9 | }, { 10 | path: 'component-test', component: ComponentTestComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [RouterModule.forRoot(routes)], 16 | exports: [RouterModule] 17 | }) 18 | export class AppRoutingModule { 19 | } 20 | -------------------------------------------------------------------------------- /projects/demo/cypress/e2e/spec.cy.ts: -------------------------------------------------------------------------------- 1 | describe('Smock Tests', () => { 2 | it('Tabs and children containers', () => { 3 | cy.visit('/'); 4 | 5 | cy.get('.mat-mdc-tab-links').find('a').its('length').should('eq', 2); 6 | 7 | cy.get('app-directive-test').should('not.exist'); 8 | cy.get('app-component-test').should('not.exist'); 9 | 10 | cy.get('.mat-mdc-tab-links').find('a').contains('Directive').click(); 11 | 12 | cy.get('app-directive-test').find('fieldset').its('length').should('eq', 11); 13 | 14 | cy.get('.mat-mdc-tab-links').find('a').contains('Component').click(); 15 | 16 | cy.get('app-component-test').find('fieldset').its('length').should('eq', 2); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /projects/auto-complete/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngui/auto-complete", 3 | "version": "18.0.0", 4 | "description": "Angular Input Autocomplete", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/ng2-ui/auto-complete.git" 9 | }, 10 | "author": "", 11 | "bugs": { 12 | "url": "https://github.com/ng2-ui/auto-complete/issues" 13 | }, 14 | "homepage": "https://github.com/ng2-ui/auto-complete#readme", 15 | "keywords": [ 16 | "angular", 17 | "auto-complete", 18 | "input", 19 | "select" 20 | ], 21 | "dependencies": { 22 | "tslib": "^2.7.0" 23 | }, 24 | "peerDependencies": { 25 | "@angular/common": "^18.2.4", 26 | "@angular/core": "^18.2.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/auto-complete/src/lib/auto-complete.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { NguiAutoCompleteComponent } from './auto-complete.component'; 5 | import { NguiAutoCompleteDirective } from './auto-complete.directive'; 6 | import { NguiAutoCompleteService } from './auto-complete.service'; 7 | 8 | @NgModule({ 9 | declarations: [ 10 | NguiAutoCompleteComponent, 11 | NguiAutoCompleteDirective 12 | ], 13 | imports: [ 14 | CommonModule, 15 | FormsModule 16 | ], 17 | providers: [ 18 | NguiAutoCompleteService 19 | ], 20 | exports: [ 21 | NguiAutoCompleteComponent, 22 | NguiAutoCompleteDirective 23 | ] 24 | }) 25 | export class NguiAutoCompleteModule { 26 | } 27 | -------------------------------------------------------------------------------- /projects/demo/src/app/directive-test/directive-test.component.scss: -------------------------------------------------------------------------------- 1 | fieldset { 2 | display: inline-block; 3 | vertical-align: top; 4 | margin: 10px; 5 | padding: 20px 6 | } 7 | 8 | ngui-auto-complete, input { 9 | display: block; 10 | border: 1px solid #ccc; 11 | width: 300px; 12 | } 13 | 14 | .demo-1 .ngui-auto-complete > ul { 15 | max-height: 100px; 16 | overflow-y: auto; 17 | } 18 | 19 | .header-row { 20 | background-color: #505050; 21 | color: #ffffff; 22 | margin: -2px -5px; 23 | } 24 | 25 | .data-row { 26 | margin: -2px -5px; 27 | } 28 | 29 | .col-1 { 30 | border-left: 1px solid #ccc; 31 | padding-left: 5px; 32 | display: inline-block; 33 | width: 100px; 34 | } 35 | 36 | .col-2 { 37 | border-left: 1px solid #ccc; 38 | padding-left: 5px; 39 | display: inline-block; 40 | width: 200px; 41 | } 42 | -------------------------------------------------------------------------------- /projects/demo/src/app/component-test/component-test.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { ComponentTestComponent } from './component-test.component'; 4 | 5 | describe('ComponentTestComponent', () => { 6 | let component: ComponentTestComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ComponentTestComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ComponentTestComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/demo/src/app/directive-test/directive-test.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 2 | 3 | import { DirectiveTestComponent } from './directive-test.component'; 4 | 5 | describe('DirectiveTestComponent', () => { 6 | let component: DirectiveTestComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(waitForAsync(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [DirectiveTestComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DirectiveTestComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.angular/cache 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | *.iml 49 | *.log 50 | /cypress/ 51 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "esModuleInterop": true, 8 | "declaration": false, 9 | "experimentalDecorators": true, 10 | "module": "ES2020", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "ES2022", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "ESNext", 19 | "DOM" 20 | ], 21 | "paths": { 22 | "auto-complete": [ 23 | "dist/auto-complete", 24 | "dist/" 25 | ], 26 | "demo": [ 27 | "docs" 28 | ] 29 | }, 30 | "useDefineForClassFields": false 31 | }, 32 | "angularCompilerOptions": { 33 | "fullTemplateTypeCheck": true, 34 | "strictInjectionParameters": true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /projects/demo/src/app/component-test/component-test.component.scss: -------------------------------------------------------------------------------- 1 | fieldset { 2 | display: inline-block; 3 | vertical-align: top; 4 | margin: 10px; 5 | padding: 20px 6 | } 7 | 8 | .demo-1 { 9 | ngui-auto-complete { 10 | display: inline-block 11 | } 12 | } 13 | 14 | .demo-2 { 15 | ngui-auto-complete { 16 | display: block; 17 | width: 300px; 18 | } 19 | 20 | input { 21 | width: 300px; 22 | } 23 | } 24 | 25 | li.addr { 26 | margin: 5px; 27 | padding: 5px; 28 | list-style: none; 29 | border: 1px solid #ccc; 30 | display: inline-block; 31 | } 32 | 33 | .wrapper { 34 | padding: 10px; 35 | border: 1px solid #ccc 36 | } 37 | 38 | span.remove { 39 | color: red 40 | } 41 | 42 | ::ng-deep ngui-auto-complete { 43 | 44 | display: block; 45 | width: 300px; 46 | 47 | input { 48 | border: 1px solid #ccc !important; 49 | width: 300px; 50 | } 51 | 52 | .ngui-auto-complete ul { 53 | position: absolute 54 | } 55 | 56 | .ngui-auto-complete > ul { 57 | width: 300px 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Injectable() 6 | export class AppService { 7 | 8 | private marvelBase = 'http://gateway.marvel.com:80/v1/public/'; 9 | private marvelPublicKey = 'b9ced31de3874eb2c065a5bce26f8c59'; 10 | 11 | private googleMapsPublicKey = 'YOUR_KEY'; 12 | 13 | constructor(private _http: HttpClient) { 14 | } 15 | 16 | /** 17 | * Find heroes by name 18 | * 19 | * @memberOf AppService 20 | * @param startsWith the starting characters of the hero name 21 | */ 22 | public findHeroes = (startsWith: string): Observable => { 23 | return this._http.get(`${this.marvelBase}characters?nameStartsWith=${startsWith}&apikey=${this.marvelPublicKey}`); 24 | }; 25 | 26 | public getMapsUrl = () => { 27 | return `https://maps.googleapis.com/maps/api/geocode/json?address=:my_own_keyword&key=${this.googleMapsPublicKey}`; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Allen Kim 2 | 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /projects/auto-complete/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "./tsconfig.lib.json", 14 | "./tsconfig.spec.json" 15 | ], 16 | "createDefaultProgram": true 17 | }, 18 | "rules": { 19 | "@angular-eslint/directive-selector": [ 20 | "error", 21 | { 22 | "type": "attribute", 23 | "prefix": "ngui", 24 | "style": "camelCase" 25 | } 26 | ], 27 | "@angular-eslint/component-selector": [ 28 | "error", 29 | { 30 | "type": "element", 31 | "prefix": "ngui", 32 | "style": "kebab-case" 33 | } 34 | ], 35 | "@angular-eslint/no-input-rename": "off" 36 | } 37 | }, 38 | { 39 | "files": [ 40 | "*.html" 41 | ], 42 | "rules": {} 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, waitForAsync } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(waitForAsync(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'demo'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('demo'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement; 29 | expect(compiled.querySelector('.content span').textContent).toContain('demo app is running!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "./tsconfig.app.json", 14 | "./tsconfig.spec.json", 15 | "./cypress/tsconfig.json" 16 | ], 17 | "createDefaultProgram": true 18 | }, 19 | "extends": [ 20 | "plugin:cypress/recommended" 21 | ], 22 | "rules": { 23 | "@angular-eslint/directive-selector": [ 24 | "error", 25 | { 26 | "type": "attribute", 27 | "prefix": "app", 28 | "style": "camelCase" 29 | } 30 | ], 31 | "@angular-eslint/component-selector": [ 32 | "error", 33 | { 34 | "type": "element", 35 | "prefix": "app", 36 | "style": "kebab-case" 37 | } 38 | ] 39 | } 40 | }, 41 | { 42 | "files": [ 43 | "*.html" 44 | ], 45 | "rules": {} 46 | } 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /projects/demo/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/demo'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /projects/auto-complete/src/lib/auto-complete.component.scss: -------------------------------------------------------------------------------- 1 | @keyframes slideDown { 2 | 0% { 3 | transform: translateY(-10px); 4 | } 5 | 100% { 6 | transform: translateY(0px); 7 | } 8 | } 9 | 10 | .ngui-auto-complete { 11 | background-color: transparent; 12 | } 13 | 14 | .ngui-auto-complete > input { 15 | outline: none; 16 | border: 0; 17 | padding: 2px; 18 | box-sizing: border-box; 19 | background-clip: content-box; 20 | } 21 | 22 | .ngui-auto-complete > ul { 23 | background-color: #fff; 24 | margin: 0; 25 | width: 100%; 26 | overflow-y: auto; 27 | list-style-type: none; 28 | padding: 0; 29 | border: 1px solid #ccc; 30 | box-sizing: border-box; 31 | animation: slideDown 0.1s; 32 | } 33 | 34 | .ngui-auto-complete > ul.empty { 35 | display: none; 36 | } 37 | 38 | .ngui-auto-complete > ul li { 39 | padding: 2px 5px; 40 | border-bottom: 1px solid #eee; 41 | } 42 | 43 | .ngui-auto-complete > ul li.selected { 44 | background-color: #ccc; 45 | } 46 | 47 | .ngui-auto-complete > ul li:last-child { 48 | border-bottom: none; 49 | } 50 | 51 | .ngui-auto-complete > ul li:not(.header-item):hover { 52 | background-color: #ccc; 53 | } 54 | -------------------------------------------------------------------------------- /projects/auto-complete/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/auto-complete'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json", 14 | "e2e/tsconfig.json" 15 | ], 16 | "createDefaultProgram": true 17 | }, 18 | "extends": [ 19 | "plugin:@angular-eslint/recommended", 20 | "plugin:@angular-eslint/template/process-inline-templates" 21 | ], 22 | "rules": { 23 | "@angular-eslint/component-selector": [ 24 | "error", 25 | { 26 | "prefix": "ngui", 27 | "style": "kebab-case", 28 | "type": "element" 29 | } 30 | ], 31 | "@angular-eslint/directive-selector": [ 32 | "error", 33 | { 34 | "prefix": "ngui", 35 | "style": "camelCase", 36 | "type": "attribute" 37 | } 38 | ] 39 | } 40 | }, 41 | { 42 | "files": [ 43 | "*.html" 44 | ], 45 | "extends": [ 46 | "plugin:@angular-eslint/template/recommended" 47 | ], 48 | "rules": {} 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | Please answer the following questions for yourself before submitting an issue. 4 | 5 | - [ ] I am running the latest version 6 | - [ ] I checked the documentation and found no answer 7 | - [ ] I checked to make sure that this issue has not already been filed 8 | - [ ] Plunker example is made **REQUIRED** 9 | 10 | **IMPORTANT** 11 | _Please be specific with an example. An issue with_ **no example may be closed**. 12 | 13 | # Expected Behavior 14 | 15 | Please describe the behavior you are expecting 16 | 17 | # Current Behavior 18 | 19 | What is the current behavior? 20 | 21 | 22 | ## Steps to Reproduce 23 | 24 | **Steps to reproduce and a minimal demo** 25 | 26 | - _What steps should we try in your demo to see the problem?_ 27 | - _Plunker example(REQUIRED)_ 28 | - _If you cannot reproduce an issue with a plunker example, it's your environmental issue_ 29 | 30 | Please provide detailed steps for reproducing the issue. 31 | 32 | 1. 33 | 2. 34 | 3. 35 | 36 | ## Issue Logs 37 | 38 | Please include any relevant log snippets or files here, otherwise delete this section. 39 | 40 | ## Angular Info 41 | 42 | Please run `ng version` and paste it here: 43 | 44 | ```shell 45 | 46 | ``` 47 | 48 | 49 | -------------------------------------------------------------------------------- /projects/demo/src/app/component-test/component-test.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Component test - multi autocomplete

4 |
5 |
  • 6 | {{ addr.formatted_address }} 7 | x 8 |
  • 9 | 10 | 25 |
    26 | 27 |
    28 |     {{ templateStr1 }}
    29 |   
    30 |
    31 | 32 |
    33 | 36 | 42 | 43 | 44 |
    45 |     {{ templateStr2 }}
    46 |   
    47 |
    48 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 3 | import { NgModule } from '@angular/core'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; 6 | 7 | import { NguiAutoCompleteModule } from 'auto-complete'; 8 | import { AppRoutingModule } from './app-routing.module'; 9 | import { AppComponent } from './app.component'; 10 | import { ComponentTestComponent } from './component-test/component-test.component'; 11 | import { DirectiveTestComponent } from './directive-test/directive-test.component'; 12 | import { AppService } from './app.service'; 13 | import { MatTabsModule } from '@angular/material/tabs'; 14 | import { MatFormFieldModule } from '@angular/material/form-field'; 15 | import { MatInputModule } from '@angular/material/input'; 16 | import { MatIconModule } from '@angular/material/icon'; 17 | 18 | @NgModule({ 19 | declarations: [ 20 | AppComponent, 21 | ComponentTestComponent, 22 | DirectiveTestComponent 23 | ], 24 | bootstrap: [AppComponent], imports: [BrowserModule, 25 | BrowserAnimationsModule, 26 | FormsModule, 27 | NguiAutoCompleteModule, 28 | MatFormFieldModule, 29 | MatInputModule, 30 | AppRoutingModule, 31 | MatTabsModule, 32 | MatFormFieldModule, 33 | MatIconModule], providers: [ 34 | AppService, 35 | provideHttpClient(withInterceptorsFromDi()) 36 | ] 37 | }) 38 | export class AppModule { 39 | } 40 | -------------------------------------------------------------------------------- /projects/auto-complete/src/lib/auto-complete.component.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 12 | 13 | 14 |
      15 |
    • 17 |
    • {{ loadingText }}
    • 18 |
    • {{ noMatchFoundText || 'No Result Found' }} 21 |
    • 22 |
    • 24 |
    • {{ blankOptionText }} 27 |
    • 28 |
    • 33 |
    • 34 |
    35 | 36 |
    37 | -------------------------------------------------------------------------------- /projects/demo/src/app/component-test/component-test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { AppService } from '../app.service'; 3 | 4 | @Component({ 5 | selector: 'app-component-test', 6 | templateUrl: './component-test.component.html', 7 | styleUrls: ['./component-test.component.scss'] 8 | }) 9 | export class ComponentTestComponent { 10 | 11 | public showAutocomplete = true; 12 | public loadingTemplate = '

    Loading h1

    '; 13 | public addrs: any[] = [ 14 | {formatted_address: 'my addr 1'}, 15 | {formatted_address: 'my addr 2'} 16 | ]; 17 | 18 | myModel; 19 | showMe; 20 | 21 | templateStr1 = `
    22 |
  • 23 | {{addr.formatted_address}} 24 | x 25 |
  • 26 | 27 | 42 |
    `; 43 | 44 | templateStr2 = ` 45 | 48 | 54 | 55 | `; 56 | 57 | constructor(public appSvc: AppService) { 58 | } 59 | 60 | public addToAddress(addr: any): void { 61 | this.addrs.push(addr); 62 | this.showAutocomplete = false; 63 | } 64 | 65 | public removeFromAddress(event, index: number): void { 66 | this.addrs.splice(index, 1); 67 | event.stopPropagation(); 68 | } 69 | 70 | public myListFormatter(data: any): string { 71 | return data['formatted_address']; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto-complete", 3 | "version": "0.0.0", 4 | "description": "Angular Input Autocomplete", 5 | "license": "MIT", 6 | "scripts": { 7 | "ng": "ng", 8 | "start": "ng serve", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e", 12 | "build-lib:watch": "ng build auto-complete --watch", 13 | "build-lib:prod": "ng build auto-complete --configuration production && npm run copy-lib", 14 | "build-docs": "ng build demo --configuration production", 15 | "copy-lib": "copyfiles -f LICENSE.md README.md CHANGELOG.md dist/", 16 | "cypress:open": "ng run demo:cypress-open", 17 | "cypress:run": "ng run demo:cypress-run" 18 | }, 19 | "private": true, 20 | "dependencies": { 21 | "@angular/animations": "^18.2.4", 22 | "@angular/cdk": "^18.2.4", 23 | "@angular/common": "^18.2.4", 24 | "@angular/compiler": "^18.2.4", 25 | "@angular/core": "^18.2.4", 26 | "@angular/forms": "^18.2.4", 27 | "@angular/material": "^18.2.4", 28 | "@angular/platform-browser": "^18.2.4", 29 | "@angular/platform-browser-dynamic": "^18.2.4", 30 | "@angular/router": "^18.2.4", 31 | "rxjs": "~7.8.1", 32 | "tslib": "^2.7.0", 33 | "zone.js": "~0.14.10" 34 | }, 35 | "devDependencies": { 36 | "@angular-devkit/build-angular": "^18.2.4", 37 | "@angular-eslint/builder": "~18.3.1", 38 | "@angular-eslint/eslint-plugin": "~18.3.1", 39 | "@angular-eslint/eslint-plugin-template": "~18.3.1", 40 | "@angular-eslint/schematics": "~18.3.1", 41 | "@angular-eslint/template-parser": "~18.3.1", 42 | "@angular/cli": "^18.2.4", 43 | "@angular/compiler-cli": "^18.2.4", 44 | "@angular/language-service": "^18.2.4", 45 | "@cypress/schematic": "^2.5.2", 46 | "@types/jasmine": "~5.1.4", 47 | "@types/jasminewd2": "~2.0.13", 48 | "@types/node": "^22.5.5", 49 | "@typescript-eslint/eslint-plugin": "^8.5.0", 50 | "@typescript-eslint/parser": "^8.5.0", 51 | "copyfiles": "^2.4.1", 52 | "cypress": "latest", 53 | "eslint": "^8.57.0", 54 | "eslint-plugin-cypress": "^3.5.0", 55 | "jasmine-core": "~5.3.0", 56 | "jasmine-spec-reporter": "~7.0.0", 57 | "karma": "~6.4.4", 58 | "karma-chrome-launcher": "~3.2.0", 59 | "karma-coverage-istanbul-reporter": "~3.0.3", 60 | "karma-jasmine": "~5.1.0", 61 | "karma-jasmine-html-reporter": "^2.1.0", 62 | "ng-packagr": "^18.2.1", 63 | "ts-node": "~10.9.2", 64 | "typescript": "~5.4.5" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /projects/demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** 22 | * By default, zone.js will patch all possible macroTask and DomEvents 23 | * user can disable parts of macroTask/DomEvents patch by setting following flags 24 | * because those flags need to be set before `zone.js` being loaded, and webpack 25 | * will put import in the top of bundle, so user need to create a separate file 26 | * in this directory (for example: zone-flags.ts), and put the following flags 27 | * into that file, and then add the following code before importing zone.js. 28 | * import './zone-flags'; 29 | * 30 | * The flags allowed in zone-flags.ts are listed here. 31 | * 32 | * The following flags will work for all browsers. 33 | * 34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 37 | * 38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 40 | * 41 | * (window as any).__Zone_enable_cross_context_check = true; 42 | * 43 | */ 44 | 45 | /*************************************************************************************************** 46 | * Zone JS is required by default for Angular itself. 47 | */ 48 | import 'zone.js'; // Included with Angular CLI. 49 | 50 | 51 | /*************************************************************************************************** 52 | * APPLICATION IMPORTS 53 | */ 54 | -------------------------------------------------------------------------------- /projects/auto-complete/src/lib/auto-complete.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Optional } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | @Injectable() 7 | export class NguiAutoCompleteService { 8 | 9 | public source: string; 10 | public pathToData: string; 11 | public listFormatter: (arg: any) => string; 12 | 13 | constructor(@Optional() private http: HttpClient) { 14 | // ... 15 | } 16 | 17 | public filter(list: any[], keyword: string, matchFormatted: boolean, accentInsensitive: boolean) { 18 | const objectString = (el) => matchFormatted ? this.getFormattedListItem(el).toLowerCase() : JSON.stringify(el).toLowerCase(); 19 | const loweredKeyword = keyword.toLowerCase(); 20 | 21 | return accentInsensitive 22 | ? list.filter( 23 | (el) => { 24 | return objectString(el).normalize('NFD').replace(/[\u0300-\u036f]/g, '') 25 | .indexOf(loweredKeyword.normalize('NFD').replace(/[\u0300-\u036f]/g, '')) !== -1; 26 | }) 27 | : list.filter( 28 | (el) => { 29 | return objectString(el).indexOf(loweredKeyword) !== -1; 30 | } 31 | ); 32 | } 33 | 34 | public getFormattedListItem(data: any) { 35 | const formatter = this.listFormatter || '(id) value'; 36 | if (typeof formatter === 'function') { 37 | return formatter.apply(this, [data]); 38 | } else if (typeof data !== 'object') { 39 | return data; 40 | } else if (typeof formatter === 'string') { 41 | let formatted = formatter; 42 | const matches = formatter.match(/[a-zA-Z0-9_\$]+/g); 43 | if (matches && typeof data !== 'string') { 44 | matches.forEach((key) => { 45 | formatted = formatted.replace(key, data[key]); 46 | }); 47 | } 48 | return formatted; 49 | } 50 | 51 | return JSON.stringify(data); 52 | } 53 | 54 | /** 55 | * return remote data from the given source and options, and data path 56 | */ 57 | public getRemoteData(keyword: string): Observable { 58 | if (typeof this.source !== 'string') { 59 | throw new TypeError('Invalid type of source, must be a string. e.g. http://www.google.com?q=:my_keyword'); 60 | } else if (!this.http) { 61 | throw new Error('Http is required.'); 62 | } 63 | 64 | const matches = this.source.match(/:[a-zA-Z_]+/); 65 | if (matches === null) { 66 | throw new Error('Replacement word is missing.'); 67 | } 68 | 69 | const replacementWord = matches[0]; 70 | const url = this.source.replace(replacementWord, keyword); 71 | 72 | return this.http.get(url) 73 | .pipe( 74 | map((list) => { 75 | 76 | if (this.pathToData) { 77 | const paths = this.pathToData.split('.'); 78 | paths.forEach((prop) => list = list[prop]); 79 | } 80 | 81 | return list; 82 | }) 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [18.0.0] Major Update 2 | 3 | - Upgraded to support Angular 18 4 | - Migration for deprecated Observable params 5 | - Adding e2e smock test for the project removing deprecated protractor adding cypress 6 | 7 | ## [17.0.0] Major Update 8 | 9 | - Upgraded to support Angular 17 10 | 11 | ## [16.0.0] Major Update 12 | 13 | - Upgraded to support Angular 16 14 | 15 | ## [15.0.0] Major Update 16 | 17 | - Upgraded to support Angular 15 18 | - Remove deprecated modules and services 19 | - Refactor code base splitting SCSS and HTML to seperated files 20 | - Fix Demo/Docs project 21 | 22 | ## [14.0.0] Major Update 23 | 24 | - Upgraded to support Angular 14 25 | - Adding Docs URL for GitHub Pages 26 | 27 | ## [13.0.0] Major Update (new version system) 28 | 29 | - Upgraded to support Angular 13 30 | - Possible fix for issues in `accept-user-input` #372 31 | - Possible fix for getFormattedListItem(...).toLowerCase is not a function #375 32 | - Versioning now match the latest supported Angular version 33 | - TSLint to ESLint migration 34 | - ES version now is ES2020 35 | - Demo fix for observable (still not perfect) 36 | 37 | ## [4.0.0] Major Update 38 | 39 | - Upgraded to support Angular 12 40 | 41 | ## [3.0.0] Major Update 42 | 43 | - Upgraded to support Angular 9 and migrate boilerplate to `@angular/cli` 44 | 45 | ## [2.0.0] Major Update 46 | 47 | - Upgraded to Angular 6 and RxJS 6 48 | - Added new property `ignore-accents`, default is `true` 49 | 50 | ### [1.0.2] 51 | 52 | - Fix build and publish issues. 53 | 54 | ###### [1.0.1] - _BROKEN!_ 55 | 56 | - Fix bug of directive when select an item causes "cannot read property 'renderValue' of undefined" 57 | 58 | ## [1.0.0] Major Update 59 | 60 | - This version publish latest changes from PRs and support Angular 5 and may have some breaking changes. 61 | 62 | ### [0.14.2] 63 | 64 | - return keyword if no items were selected 65 | 66 | ### [0.14.1] 67 | 68 | - add a new property `select-on-blur` 69 | 70 | ### [0.14.0] 71 | 72 | - Upgraded to Angular 4 73 | 74 | ### [0.13.4] 75 | 76 | - now the first item on the suggestion dropdown will not be auto-selected until we press the arrow down key 77 | 78 | ### [0.13.0] 79 | 80 | - renamed module to @ngui/auto-complete 81 | 82 | ### [0.12.0] 83 | 84 | - added a new property `match-formatted` 85 | 86 | ### [0.11.0] 87 | 88 | - added a new property `value-formatter` 89 | 90 | ### [0.10.10] 91 | 92 | - fixed `accept-user-input`, fixed tests 93 | 94 | ### [0.10.9] 95 | 96 | - bug fix on list formatter not applying and dropdown positioning 97 | - fixed (undefined) display when source is list of number or boolean 98 | 99 | ### [0.10.8] 100 | 101 | - fixed (undefined) display when source is list of number or boolean 102 | 103 | ### [0.10.7] 104 | 105 | - introduced `select-valule-of` 106 | - removed `value-property-name` 107 | - list-formatter now accepts string e.g. `(id) value` 108 | 109 | ### [0.10.6] 110 | 111 | - testing with initial focus 112 | 113 | ### [0.10.5] 114 | 115 | - more checking before set value 116 | -------------------------------------------------------------------------------- /projects/demo/src/app/directive-test/directive-test.component.html: -------------------------------------------------------------------------------- 1 |

    Autocomplete Directive Test - Local Source

    2 | 3 |
    4 |

    Source as Array of Strings

    5 |
    13 | 14 |
    15 |
    selected model1: {{ json(model1) }}

    16 |
    {{ template1 }}
    17 |
     arrayOfStrings: {{ json(arrayOfStrings) }}
    18 |
    19 | 20 |
    21 |

    Source as Array of Strings. Drop-down on focus disable

    22 |
    31 | 32 |
    33 |
    selected model1: {{ json(model1) }}

    34 |
    {{ template2 }}
    35 |
     arrayOfStrings: {{ json(arrayOfStrings) }}
    36 |
    37 | 38 |
    39 |

    Source as Array of id/value

    40 | 48 | Change It 49 |
    selected model2: {{ model2 | json }}

    50 |
    {{ template3 }}
    51 |
    arrayOfKeyValues: {{ json(arrayOfKeyValues) }}
    52 |
    53 | 54 |
    55 |

    Source as Array of Key/Name

    56 | 63 |
    selected model3: {{ model3 | json }}

    64 |
    {{ template4 }}
    65 |
    arrayOfKeyValues2: {{ json(arrayOfKeyValues2) }}
    66 |
    67 | 68 |
    69 |

    Source as HTTP URI String

    70 | 82 |
    selected model4: {{ model4 | json }}

    83 |
    {{ template5 }}
    84 |
     source: https://maps.googleapis.com/maps/api/geocode/json?address=:my_own_keyword
    85 |
    86 | 87 |
    88 |

    Source as Observable "Marvel API"

    89 | 98 |
    selected model5: {{ model5 | json }}

    99 |
    {{ template6 }}
    100 |
    101 | 102 |
    103 |

    With Material Design

    104 | 105 | 106 | 112 | .00 113 | 114 |
    {{ template7 }}
    115 |
    arrayOfNumbers: {{ json(arrayOfNumbers) }}
    116 |
    117 | 118 |
    119 |

    Source as Array of Strings (with auto-select-first-item)

    120 |
    124 | 125 |
    126 |
    selected model7: {{ json(model7) }}

    127 |
    {{ template8 }}
    128 |
     arrayOfStrings: {{ json(arrayOfStrings) }}
    129 |
    130 | 131 |
    132 |

    RTL support

    133 |
    139 | 140 |
    141 |
    selected model8: {{ json(model8) }}

    142 |
    {{ template9 }}
    143 |
     arrayOfStrings: {{ json(arrayOfStrings) }}
    144 |
    145 | 146 |
    147 |

    Grid-Style Results with Header Row

    148 | 158 |
    selected model9: {{ json(model9) }}

    159 |
    {{ template10 }}
    160 |
     arrayOfCities: {{ json(arrayOfCities) }}
    161 |
    162 | 163 |
    164 |

    Exact Match Including Accents

    165 |
    174 | 175 |
    176 |
    selected model10: {{ json(model10) }}

    177 |
    {{ template11 }}
    178 |
     arrayOfStrings: {{ json(arrayOfAccentedStrings) }}
    179 |
    180 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "auto-complete": { 7 | "projectType": "library", 8 | "root": "projects/auto-complete", 9 | "sourceRoot": "projects/auto-complete/src", 10 | "prefix": "ngui", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "tsConfig": "projects/auto-complete/tsconfig.lib.json", 16 | "project": "projects/auto-complete/ng-package.json" 17 | }, 18 | "configurations": { 19 | "production": { 20 | "tsConfig": "projects/auto-complete/tsconfig.lib.prod.json" 21 | }, 22 | "development": {} 23 | }, 24 | "defaultConfiguration": "production" 25 | }, 26 | "test": { 27 | "builder": "@angular-devkit/build-angular:karma", 28 | "options": { 29 | "main": "projects/auto-complete/src/test.ts", 30 | "tsConfig": "projects/auto-complete/tsconfig.spec.json", 31 | "karmaConfig": "projects/auto-complete/karma.conf.js" 32 | } 33 | }, 34 | "lint": { 35 | "builder": "@angular-eslint/builder:lint", 36 | "options": { 37 | "lintFilePatterns": [ 38 | "projects/auto-complete/**/*.ts", 39 | "projects/auto-complete/**/*.html" 40 | ] 41 | } 42 | }, 43 | "ct": { 44 | "builder": "@cypress/schematic:cypress", 45 | "options": { 46 | "devServerTarget": "auto-complete:serve", 47 | "watch": true, 48 | "headless": false, 49 | "testingType": "component" 50 | }, 51 | "configurations": { 52 | "development": { 53 | "devServerTarget": "auto-complete:serve:development" 54 | } 55 | } 56 | } 57 | } 58 | }, 59 | "demo": { 60 | "projectType": "application", 61 | "schematics": { 62 | "@schematics/angular:component": { 63 | "style": "scss" 64 | } 65 | }, 66 | "root": "projects/demo", 67 | "sourceRoot": "projects/demo/src", 68 | "prefix": "docs", 69 | "architect": { 70 | "build": { 71 | "builder": "@angular-devkit/build-angular:application", 72 | "options": { 73 | "outputPath": { 74 | "base": "docs", 75 | "browser": "" 76 | }, 77 | "baseHref": "/auto-complete/", 78 | "index": "projects/demo/src/index.html", 79 | "polyfills": [ 80 | "projects/demo/src/polyfills.ts" 81 | ], 82 | "tsConfig": "projects/demo/tsconfig.app.json", 83 | "assets": [ 84 | "projects/demo/src/favicon.ico", 85 | "projects/demo/src/assets" 86 | ], 87 | "styles": [ 88 | "projects/demo/src/styles.scss" 89 | ], 90 | "scripts": [], 91 | "extractLicenses": false, 92 | "sourceMap": true, 93 | "optimization": false, 94 | "namedChunks": true, 95 | "browser": "projects/demo/src/main.ts" 96 | }, 97 | "configurations": { 98 | "production": { 99 | "fileReplacements": [ 100 | { 101 | "replace": "projects/demo/src/environments/environment.ts", 102 | "with": "projects/demo/src/environments/environment.prod.ts" 103 | } 104 | ], 105 | "optimization": true, 106 | "outputHashing": "all", 107 | "sourceMap": false, 108 | "namedChunks": false, 109 | "extractLicenses": true, 110 | "budgets": [ 111 | { 112 | "type": "initial", 113 | "maximumWarning": "2mb", 114 | "maximumError": "5mb" 115 | }, 116 | { 117 | "type": "anyComponentStyle", 118 | "maximumWarning": "6kb", 119 | "maximumError": "10kb" 120 | } 121 | ] 122 | }, 123 | "development": {} 124 | }, 125 | "defaultConfiguration": "production" 126 | }, 127 | "serve": { 128 | "builder": "@angular-devkit/build-angular:dev-server", 129 | "options": {}, 130 | "configurations": { 131 | "production": { 132 | "buildTarget": "demo:build:production" 133 | }, 134 | "development": { 135 | "buildTarget": "demo:build:development" 136 | } 137 | }, 138 | "defaultConfiguration": "development" 139 | }, 140 | "extract-i18n": { 141 | "builder": "@angular-devkit/build-angular:extract-i18n", 142 | "options": { 143 | "buildTarget": "demo:build" 144 | } 145 | }, 146 | "test": { 147 | "builder": "@angular-devkit/build-angular:karma", 148 | "options": { 149 | "main": "projects/demo/src/test.ts", 150 | "polyfills": "projects/demo/src/polyfills.ts", 151 | "tsConfig": "projects/demo/tsconfig.spec.json", 152 | "karmaConfig": "projects/demo/karma.conf.js", 153 | "assets": [ 154 | "projects/demo/src/favicon.ico", 155 | "projects/demo/src/assets" 156 | ], 157 | "styles": [ 158 | "projects/demo/src/styles.scss" 159 | ], 160 | "scripts": [] 161 | } 162 | }, 163 | "lint": { 164 | "builder": "@angular-eslint/builder:lint", 165 | "options": { 166 | "lintFilePatterns": [ 167 | "projects/demo/**/*.ts", 168 | "projects/demo/**/*.html" 169 | ] 170 | } 171 | }, 172 | "cypress-run": { 173 | "builder": "@cypress/schematic:cypress", 174 | "options": { 175 | "devServerTarget": "demo:serve", 176 | "configFile": "projects/demo/cypress.config.ts" 177 | }, 178 | "configurations": { 179 | "production": { 180 | "devServerTarget": "demo:serve:production" 181 | } 182 | } 183 | }, 184 | "cypress-open": { 185 | "builder": "@cypress/schematic:cypress", 186 | "options": { 187 | "watch": true, 188 | "headless": false, 189 | "configFile": "projects/demo/cypress.config.ts" 190 | } 191 | }, 192 | "e2e": { 193 | "builder": "@cypress/schematic:cypress", 194 | "options": { 195 | "devServerTarget": "demo:serve", 196 | "watch": true, 197 | "headless": false 198 | }, 199 | "configurations": { 200 | "production": { 201 | "devServerTarget": "demo:serve:production" 202 | } 203 | } 204 | } 205 | } 206 | } 207 | }, 208 | "cli": { 209 | "schematicCollections": [ 210 | "@cypress/schematic", 211 | "@angular-eslint/schematics", 212 | "@schematics/angular" 213 | ], 214 | "analytics": false 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular UI auto-complete 2 | 3 | [![npm downloads](https://img.shields.io/npm/dt/@ngui/auto-complete.svg)](https://www.npmjs.com/package/@ngui/auto-complete) 4 | [![npm version](https://img.shields.io/npm/v/@ngui/auto-complete.svg)](https://www.npmjs.com/package/@ngui/auto-complete) 5 | [![npm license](https://img.shields.io/npm/l/@ngui/auto-complete.svg)](https://www.npmjs.com/package/@ngui/auto-complete) 6 | [![GitHub issues](https://img.shields.io/github/issues/ng2-ui/auto-complete.svg)](https://github.com/ng2-ui/auto-complete/issues) 7 | 8 | ### IMPORTANT: HELP NEEDED 9 | 10 | The project need your help, any help for fixing bugs and improvements are welcome! 11 | 12 | ## Install 13 | 14 | 1. install `@ngui/auto-complete` 15 | 16 | $ npm install @ngui/auto-complete --save 17 | 18 | 2. import NguiAutoCompleteModule to your AppModule 19 | 20 | import { NguiAutoCompleteModule } from '@ngui/auto-complete'; 21 | 22 | @NgModule({ 23 | imports: [BrowserModule, FormsModule, NguiAutoCompleteModule], 24 | declarations: [AppComponent], 25 | providers: [HTTP_PROVIDERS], 26 | bootstrap: [ AppComponent ] 27 | }) 28 | export class AppModule { } 29 | 30 | ## Usage it in your code 31 | 32 | 1. As a component 33 | 34 | 35 | 36 | 2. As a directive 37 | 38 | 39 | 40 | For full example, please check `demo` directory to see the example of `app.module.ts` and `app.component.ts`. 41 | 42 | ## Demo 43 | 44 | You can look at different showcases for it here as [Component](https://ng2-ui.github.io/auto-complete/component-test) or [Directive](https://ng2-ui.github.io/auto-complete/directive-test). 45 | 46 | ## attributes 47 | 48 | All options are optional except `ngModel` and `source` 49 | 50 | * **`ngModel`**, any, variable that autocomplete result is assigned to 51 | * **`source`**, array or string, required. data source for dropdown list 52 | * **`auto-complete-placeholder`**, string, autocomplete input guide text 53 | * **`value-formatter`**, string or function variable name, custom value formatting function. e.g. `(id) value`, '`myValueFormatter`. 54 | 55 | myValueFormatter(data: any): string { 56 | return `(${data[id]}) ${data[value]}`; 57 | } 58 | * **`list-formatter`**, string or function variable name, custom list formatting function. e.g. `(key) name`, `myListFormatter`. 59 | 60 | myListFormatter(data: any): string { 61 | return `(${data[key]}) ${data[name]}`; 62 | } 63 | 64 | * **`path-to-data`**, string, e.g., `data.myList`, path to array data in http response 65 | * **`min-chars, number`**, when source is remote data, the number of character to see drop-down list 66 | * **`display-property-name`**, string, key name of text to show. default is `value` 67 | * **`select-value-of`**, string, when selected, return the value of this key as a selected item 68 | * **`blank-option-text`**, string, guide text to allow empty value to be selected as in empty value of `option` tag. 69 | * **`no-match-found-text`**, string, guide text to show no result found. 70 | * **`valueChanged`** / **`ngModelChange`**, callback function that is executed when a new drop-down is selected. 71 | e.g. `(valueChanged)="myCallback($event)"` 72 | * **`customSelected`** callback function that is executed when a value selected not included in drop-down, so it will return the keyword used. 73 | e.g. `(customSelected)="customCallback($event)"` 74 | * **`loading-text`**, text to be displayed when loading. Default, "Loading" 75 | * **`loading-template`**, html markup that is to be rendered when loading. Default, null 76 | * **`accept-user-input`** boolean, if `false` and does not match to source given, it goes back to the original value selected., If you don't event want user to type any, please use `readonly="readonly"` to force user to select only from list. Default is `true` 77 | * **`max-num-list`** number, maximum number of drop down list items. Default, unlimited 78 | * **`tab-to-select`** boolean, if `true`, pressing Tab will set the value from the selected item before focus leaves the control. Default is `true` 79 | * **`select-on-blur`** boolean, if `true`, `blur` event will set the value from the selected item before focus leaves the control. Default is `false` 80 | * **`match-formatted`** boolean, if `true`, keyword will be matched against list values formatted with `list-formatter`, instead of raw objects. Default is `false` 81 | * **`auto-select-first-item`**, boolean, if `true`, the first item of the list is automatically selected, if `false`, user must select manually an item. Default is `false` 82 | * **`open-on-focus`**, boolean, if `false` drop down won't open on a focus event, . Default is `true` 83 | * **`close-on-focusout`**, boolean, if `false` drop down will close on a focusout event, . Default is `true` 84 | * **`re-focus-after-select property`**, boolean, if `false` an auto focus behavior after select (example: custom value on blur event or issue #276) is disabled . Default is `true` 85 | * **`autocomplete`**, boolean, default `false`, if `true` remove the attribute `autocomplete="off"` of the input. 86 | * **`header-item-template`**, html markup to optionally create a non-selectable header row above the list of results. Default, null 87 | * **`ignore-accents`**, boolean, default `true`, if `false` user input must match exactly with source given, including accents or diacritics 88 | 89 | ## Below are plunks for different scenarios: 90 | 91 | **Template Driven Forms** 92 | 93 | * _ngModel_ http://plnkr.co/edit/3pB1Gx?p=preview 94 | 95 | **Reactive Forms** 96 | 97 | * _FormGroup_ http://plnkr.co/edit/2osUq6?p=preview 98 | [issue #49](https://github.com/ng2-ui/auto-complete/issues/49) 99 | 100 | * _FormControl_ http://plnkr.co/edit/A5CW2e?p=preview 101 | [issue #100](https://github.com/ng2-ui/auto-complete/issues/100) 102 | 103 | **Material Design** 104 | 105 | * _Example_ http://plnkr.co/edit/2YLDjX?p=preview&open=app/app.component.ts 106 | 107 | **Observable Source** 108 | 109 | * _Example_ http://plnkr.co/edit/ExzNSh?p=preview 110 | 111 | **List Formatter Example** 112 | 113 | * _Example 1_ http://plnkr.co/edit/F9nrWp?p=preview 114 | * _Example 2 (With custom css)_ http://plnkr.co/edit/0QFYFHMmCAFmhbYAGQl7?p=preview 115 | 116 | ## Contributors are welcomed 117 | 118 | This module is only improved and maintained by contributors like you; 119 | 120 | As a contributor, it's NOT required to be skilled in Javascript nor Angular. 121 | You can contribute to the following; 122 | 123 | * Updating README.md 124 | * Making more and clearer comments 125 | * Answering issues and building FAQ 126 | * Documentation 127 | * Translation 128 | 129 | In result of your active contribution, you will be listed as a core contributor 130 | on https://ng2-ui.github.io, and a member of ng2-ui too. 131 | 132 | If you are interested in becoming a contributor and/or a member of ng-ui, 133 | please send me email to `allenhwkim AT gmail.com` with your GitHub id. 134 | 135 | ## For Developers 136 | 137 | ### To start 138 | 139 | $ git clone https://github.com/ng2-ui/auto-complete.git 140 | $ cd auto-complete 141 | $ npm install 142 | $ npm build-lib:watch 143 | 144 | $ # On different instance 145 | 146 | $ npm start 147 | 148 | ### To publish 149 | 150 | $ npm build-lib:prod 151 | $ cd dist 152 | $ npm publish 153 | 154 | ### To build new docs version 155 | 156 | $ npm build-docs 157 | 158 | ### List of available npm tasks 159 | 160 | * `npm run` : List all available tasks 161 | * `npm start`: Run `demo` directory for development using `@angular/cli` with port 4200 162 | * `npm run lint`: Lint TypeScript code 163 | * `npm run build-lib:watch`: Build library in live watch mode for development 164 | * `npm run build-lib:prod`: Build library for publish using view engine (not Ivy renderer) 165 | * `npm run build-docs`: Build a new version for life demo got GitHub pages 166 | -------------------------------------------------------------------------------- /projects/demo/src/app/directive-test/directive-test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation } from '@angular/core'; 2 | import { AppService } from '../app.service'; 3 | 4 | @Component({ 5 | selector: 'app-directive-test', 6 | templateUrl: './directive-test.component.html', 7 | styleUrls: ['./directive-test.component.scss'], 8 | encapsulation: ViewEncapsulation.None 9 | }) 10 | export class DirectiveTestComponent { 11 | 12 | public loadingTemplate = `

    Loading

    `; 13 | public cityHeaderTemplate = ` 14 |
    15 |
    City
    16 |
    State
    17 |
    Nickname
    18 |
    Population
    19 |
    `; 20 | public arrayOfNumbers: number[] = [100, 200, 300, 400, 500]; 21 | public arrayOfStrings: string[] = ['this', 'is', 'array', 'of', 'text', 'with', 'long', 'and long', 'and long', 'list']; 22 | public arrayOfAccentedStrings: string[] = ['Cádiz', 'München']; 23 | 24 | public arrayOfKeyValues: any[] = 25 | [{id: 1, value: 'One'}, {id: 2, value: 'Two'}, {id: 3, value: 'Three'}, { 26 | id: 4, 27 | value: 'Four' 28 | }]; 29 | 30 | public arrayOfKeyValues2: any[] = 31 | [{id: 11, key: 1, name: 'Key One'}, {id: 12, key: 2, name: 'Key Two'}, { 32 | id: 13, 33 | key: 3, 34 | name: 'Key Three' 35 | }, {id: 14, key: 4, name: 'Key Four'}]; 36 | 37 | public arrayOfCities: any[] = 38 | [{city: 'New York', state: 'New York', nickname: 'The Big Apple', population: '8,537,673'}, 39 | {city: 'Los Angeles', state: 'California', nickname: 'City of Angels', population: '3,976,322'}, 40 | {city: 'Chicago', state: 'Illinois', nickname: 'The Windy City', population: '2,704,958'}, 41 | {city: 'Houston', state: 'Texas', nickname: 'Space City', population: '2,303,482'}, 42 | {city: 'Phoenix', state: 'Arizona', nickname: 'Valley of the Sun', population: '1,615,017'}, 43 | {city: 'Philadelphia', state: 'Pennsylvania', nickname: 'City of Brotherly Love', population: '1,567,872'}, 44 | {city: 'San Antonio', state: 'Texas', nickname: 'Alamo City', population: '1,492,510'}, 45 | {city: 'San Diego', state: 'California', nickname: 'America\'s Finest City', population: '1,406,630'}, 46 | {city: 'Dallas', state: 'Texas', nickname: 'The Big D', population: '1,317,929'}, 47 | {city: 'San Jose', state: 'California', nickname: 'Capital of Silicon Valley', population: '1,025,350'}]; 48 | 49 | public model1 = 'is'; 50 | public model2 = {id: 1, value: 'One'}; 51 | public model3 = {key: 3, name: 'Key Three'}; 52 | public model4; 53 | public model5; 54 | public model7 = ''; 55 | public model8 = ''; 56 | public model9 = ''; 57 | public model10 = ''; 58 | public myModel; 59 | 60 | template1 = ` 61 |
    69 | 70 |
    71 | `; 72 | 73 | template2 = ` 74 |
    83 | 84 |
    85 | `; 86 | 87 | template3 = ` 88 | 96 | Change It 97 | `; 98 | 99 | template4 = ` 100 | 107 | `; 108 | 109 | template5 = ` 110 | 122 | `; 123 | 124 | template6 = ` 125 | 134 | `; 135 | 136 | template7 = ` 137 | 138 | 139 | 145 | .00 146 | 147 | `; 148 | 149 | template8 = ` 150 |
    154 | 155 |
    156 | `; 157 | 158 | template9 = ` 159 |
    165 | 166 |
    167 | `; 168 | 169 | template10 = ` 170 | 189 | 190 | 191 | `; 192 | 193 | 194 | constructor(public appSvc: AppService) { 195 | } 196 | 197 | public customCallback(text) { 198 | console.log('keyword ', text); 199 | } 200 | 201 | public myCallback1(newVal1) { 202 | console.log('value is changed to ', newVal1); 203 | this.model1 = newVal1; 204 | } 205 | 206 | public myCallback7(newVal7) { 207 | console.log('value is changed to ', newVal7); 208 | this.model7 = newVal7; 209 | } 210 | 211 | public myCallback8(newVal8) { 212 | console.log('value is changed to ', newVal8); 213 | this.model8 = newVal8; 214 | } 215 | 216 | public myCallback10(newVal10) { 217 | console.log('value is changed to ', newVal10); 218 | this.model10 = newVal10; 219 | } 220 | 221 | public renderHero(data: any): string { 222 | const imgSrc = () => `${data.thumbnail.path}/portrait_small.${data.thumbnail.extension}`; 223 | 224 | return `${data.name} 225 | ${data.name} 226 | ${data.description}`; 227 | } 228 | 229 | public renderCity(data: any): string { 230 | const html = ` 231 |
    232 |
    ${data.city}
    233 |
    ${data.state}
    234 |
    ${data.nickname}
    235 |
    ${data.population}
    236 |
    `; 237 | 238 | return html; 239 | } 240 | 241 | public rightAligned(data: any): string { 242 | return `
    ${data}.00
    `; 243 | } 244 | 245 | public json(obj) { 246 | return JSON.stringify(obj, null, ' '); 247 | } 248 | 249 | } 250 | -------------------------------------------------------------------------------- /projects/auto-complete/src/lib/auto-complete.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; 2 | import { NguiAutoCompleteService } from './auto-complete.service'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Component({ 6 | selector: 'ngui-auto-complete', 7 | templateUrl: './auto-complete.component.html', 8 | styleUrls: ['./auto-complete.component.scss'], 9 | encapsulation: ViewEncapsulation.None 10 | }) 11 | export class NguiAutoCompleteComponent implements OnInit { 12 | 13 | /** 14 | * public input properties 15 | */ 16 | @Input('autocomplete') public autocomplete = false; 17 | @Input('list-formatter') public listFormatter: (arg: any) => string; 18 | @Input('source') public source: any; 19 | @Input('path-to-data') public pathToData: string; 20 | @Input('min-chars') public minChars = 0; 21 | @Input('placeholder') public placeholder: string; 22 | @Input('blank-option-text') public blankOptionText: string; 23 | @Input('no-match-found-text') public noMatchFoundText: string; 24 | @Input('accept-user-input') public acceptUserInput = true; 25 | @Input('loading-text') public loadingText = 'Loading'; 26 | @Input('loading-template') public loadingTemplate = null; 27 | @Input('max-num-list') public maxNumList: number; 28 | @Input('show-input-tag') public showInputTag = true; 29 | @Input('show-dropdown-on-init') public showDropdownOnInit = false; 30 | @Input('tab-to-select') public tabToSelect = true; 31 | @Input('match-formatted') public matchFormatted = false; 32 | @Input('auto-select-first-item') public autoSelectFirstItem = false; 33 | @Input('select-on-blur') public selectOnBlur = false; 34 | @Input('re-focus-after-select') public reFocusAfterSelect = true; 35 | @Input('header-item-template') public headerItemTemplate = null; 36 | @Input('ignore-accents') public ignoreAccents = true; 37 | 38 | @Output() public valueSelected = new EventEmitter(); 39 | @Output() public customSelected = new EventEmitter(); 40 | @Output() public textEntered = new EventEmitter(); 41 | 42 | @ViewChild('autoCompleteInput') public autoCompleteInput: ElementRef; 43 | @ViewChild('autoCompleteContainer') public autoCompleteContainer: ElementRef; 44 | 45 | public dropdownVisible = false; 46 | public isLoading = false; 47 | 48 | public filteredList: any[] = []; 49 | public minCharsEntered = false; 50 | public itemIndex: number = null; 51 | public keyword: string; 52 | 53 | private el: HTMLElement; // this component element `` 54 | private timer = 0; 55 | 56 | private delay = (() => { 57 | let timer = null; 58 | return (callback: any, ms: number) => { 59 | clearTimeout(timer); 60 | timer = setTimeout(callback, ms); 61 | }; 62 | })(); 63 | private selectOnEnter = false; 64 | 65 | /** 66 | * constructor 67 | */ 68 | constructor(elementRef: ElementRef, public autoComplete: NguiAutoCompleteService) { 69 | this.el = elementRef.nativeElement; 70 | } 71 | 72 | /** 73 | * user enters into input el, shows list to select, then select one 74 | */ 75 | ngOnInit(): void { 76 | this.autoComplete.source = this.source; 77 | this.autoComplete.pathToData = this.pathToData; 78 | this.autoComplete.listFormatter = this.listFormatter; 79 | if (this.autoSelectFirstItem) { 80 | this.itemIndex = 0; 81 | } 82 | setTimeout(() => { 83 | if (this.autoCompleteInput && this.reFocusAfterSelect) { 84 | this.autoCompleteInput.nativeElement.focus(); 85 | } 86 | if (this.showDropdownOnInit) { 87 | this.showDropdownList({target: {value: ''}}); 88 | } 89 | }); 90 | } 91 | 92 | public isSrcArr(): boolean { 93 | return Array.isArray(this.source); 94 | } 95 | 96 | public reloadListInDelay = (evt: any): void => { 97 | const delayMs = this.isSrcArr() ? 10 : 500; 98 | const keyword = evt.target.value; 99 | 100 | // executing after user stopped typing 101 | this.delay(() => this.reloadList(keyword), delayMs); 102 | }; 103 | 104 | public showDropdownList(event: any): void { 105 | this.dropdownVisible = true; 106 | this.reloadList(event.target.value); 107 | } 108 | 109 | public hideDropdownList(): void { 110 | this.selectOnEnter = false; 111 | this.dropdownVisible = false; 112 | } 113 | 114 | public findItemFromSelectValue(selectText: string): any { 115 | const matchingItems = this.filteredList.filter((item) => ('' + item) === selectText); 116 | return matchingItems.length ? matchingItems[0] : null; 117 | } 118 | 119 | public reloadList(keyword: string): void { 120 | 121 | this.filteredList = []; 122 | if (keyword.length < (this.minChars || 0)) { 123 | this.minCharsEntered = false; 124 | return; 125 | } else { 126 | this.minCharsEntered = true; 127 | } 128 | 129 | if (this.isSrcArr()) { // local source 130 | this.isLoading = false; 131 | this.filteredList = this.autoComplete.filter(this.source, keyword, this.matchFormatted, this.ignoreAccents); 132 | if (this.maxNumList) { 133 | this.filteredList = this.filteredList.slice(0, this.maxNumList); 134 | } 135 | 136 | } else {// remote source 137 | this.isLoading = true; 138 | 139 | if (typeof this.source === 'function') { 140 | // custom function that returns observable 141 | (this.source(keyword) as Observable).subscribe({ 142 | next: (resp) => { 143 | if (this.pathToData) { 144 | const paths = this.pathToData.split('.'); 145 | paths.forEach((prop) => resp = resp[prop]); 146 | } 147 | 148 | this.filteredList = resp; 149 | if (this.maxNumList) { 150 | this.filteredList = this.filteredList.slice(0, this.maxNumList); 151 | } 152 | }, 153 | error: (error) => console.warn(error), 154 | complete: () => this.isLoading = false 155 | } 156 | ); 157 | } else { 158 | // remote source 159 | this.autoComplete.getRemoteData(keyword).subscribe({ 160 | next: (resp) => { 161 | this.filteredList = resp ? resp : []; 162 | if (this.maxNumList) { 163 | this.filteredList = this.filteredList.slice(0, this.maxNumList); 164 | } 165 | }, 166 | error: (error) => console.warn(error), 167 | complete: () => this.isLoading = false 168 | }); 169 | } 170 | } 171 | } 172 | 173 | public selectOne(data: any) { 174 | if (!!data || data === '') { 175 | this.valueSelected.emit(data); 176 | } else { 177 | this.customSelected.emit(this.keyword); 178 | } 179 | } 180 | 181 | public enterText(data: any) { 182 | this.textEntered.emit(data); 183 | } 184 | 185 | public blurHandler(evt: any) { 186 | if (this.selectOnBlur) { 187 | this.selectOne(this.filteredList[this.itemIndex]); 188 | } 189 | 190 | this.hideDropdownList(); 191 | } 192 | 193 | public inputElKeyHandler = (evt: any) => { 194 | const totalNumItem = this.filteredList.length; 195 | 196 | if (!this.selectOnEnter && this.autoSelectFirstItem && (0 !== totalNumItem)) { 197 | this.selectOnEnter = true; 198 | } 199 | 200 | switch (evt.keyCode) { 201 | case 27: // ESC, hide auto complete 202 | this.selectOnEnter = false; 203 | this.selectOne(undefined); 204 | break; 205 | 206 | case 38: // UP, select the previous li el 207 | if (0 === totalNumItem) { 208 | return; 209 | } 210 | this.selectOnEnter = true; 211 | this.itemIndex = (totalNumItem + this.itemIndex - 1) % totalNumItem; 212 | this.scrollToView(this.itemIndex); 213 | break; 214 | 215 | case 40: // DOWN, select the next li el or the first one 216 | if (0 === totalNumItem) { 217 | return; 218 | } 219 | this.selectOnEnter = true; 220 | this.dropdownVisible = true; 221 | let sum = this.itemIndex; 222 | sum = (this.itemIndex === null) ? 0 : sum + 1; 223 | this.itemIndex = (totalNumItem + sum) % totalNumItem; 224 | this.scrollToView(this.itemIndex); 225 | break; 226 | 227 | case 13: // ENTER, choose it!! 228 | if (this.selectOnEnter) { 229 | this.selectOne(this.filteredList[this.itemIndex]); 230 | } 231 | evt.preventDefault(); 232 | break; 233 | 234 | case 9: // TAB, choose if tab-to-select is enabled 235 | if (this.tabToSelect) { 236 | this.selectOne(this.filteredList[this.itemIndex]); 237 | } 238 | break; 239 | } 240 | }; 241 | 242 | public scrollToView(index) { 243 | const container = this.autoCompleteContainer.nativeElement; 244 | const ul = container.querySelector('ul'); 245 | const li = ul.querySelector('li'); // just sample the first li to get height 246 | const liHeight = li.offsetHeight; 247 | const scrollTop = ul.scrollTop; 248 | const viewport = scrollTop + ul.offsetHeight; 249 | const scrollOffset = liHeight * index; 250 | if (scrollOffset < scrollTop || (scrollOffset + liHeight) > viewport) { 251 | ul.scrollTop = scrollOffset; 252 | } 253 | } 254 | 255 | public trackByIndex(index, item) { 256 | return index; 257 | } 258 | 259 | get emptyList(): boolean { 260 | return !( 261 | this.isLoading || 262 | (this.minCharsEntered && !this.isLoading && !this.filteredList.length) || 263 | (this.filteredList.length) 264 | ); 265 | } 266 | 267 | } 268 | -------------------------------------------------------------------------------- /projects/auto-complete/src/lib/auto-complete.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | ComponentRef, 4 | Directive, 5 | EventEmitter, 6 | Host, 7 | Input, 8 | OnChanges, 9 | OnDestroy, 10 | OnInit, 11 | Optional, 12 | Output, 13 | SimpleChanges, 14 | SkipSelf, 15 | ViewContainerRef 16 | } from '@angular/core'; 17 | import { AbstractControl, ControlContainer, FormGroupName, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; 18 | import { NguiAutoCompleteComponent } from './auto-complete.component'; 19 | 20 | @Directive({ 21 | // eslint-disable-next-line @angular-eslint/directive-selector 22 | selector: '[auto-complete], [ngui-auto-complete]' 23 | }) 24 | export class NguiAutoCompleteDirective implements OnInit, OnChanges, AfterViewInit, OnDestroy { 25 | 26 | @Input('autocomplete') public autocomplete = false; 27 | @Input('auto-complete-placeholder') public autoCompletePlaceholder: string; 28 | @Input('source') public source: any; 29 | @Input('path-to-data') public pathToData: string; 30 | @Input('min-chars') public minChars: number; 31 | @Input('display-property-name') public displayPropertyName: string; 32 | @Input('accept-user-input') public acceptUserInput = true; 33 | @Input('max-num-list') public maxNumList: string; 34 | @Input('select-value-of') public selectValueOf: string; 35 | @Input('loading-template') public loadingTemplate = null; 36 | @Input('list-formatter') public listFormatter; 37 | @Input('loading-text') public loadingText = 'Loading'; 38 | @Input('blank-option-text') public blankOptionText: string; 39 | @Input('no-match-found-text') public noMatchFoundText: string; 40 | @Input('value-formatter') public valueFormatter: any; 41 | @Input('tab-to-select') public tabToSelect = true; 42 | @Input('select-on-blur') public selectOnBlur = false; 43 | @Input('match-formatted') public matchFormatted = false; 44 | @Input('auto-select-first-item') public autoSelectFirstItem = false; 45 | @Input('open-on-focus') public openOnFocus = true; 46 | @Input('close-on-focusout') public closeOnFocusOut = true; 47 | @Input('re-focus-after-select') public reFocusAfterSelect = true; 48 | @Input('header-item-template') public headerItemTemplate = null; 49 | @Input('ignore-accents') public ignoreAccents = true; 50 | 51 | @Input() public ngModel: string; 52 | @Input('formControlName') public formControlName: string; 53 | // if [formControl] is used on the anchor where our directive is sitting 54 | // a form is not necessary to use a formControl we should also support this 55 | @Input('formControl') public extFormControl: UntypedFormControl; 56 | @Input('z-index') public zIndex = '1'; 57 | @Input('is-rtl') public isRtl = false; 58 | 59 | @Output() public ngModelChange = new EventEmitter(); 60 | @Output() public valueChanged = new EventEmitter(); 61 | @Output() public customSelected = new EventEmitter(); 62 | 63 | private componentRef: ComponentRef; 64 | private wrapperEl: HTMLElement; 65 | private el: HTMLElement; // this element element, can be any 66 | private acDropdownEl: HTMLElement; // auto complete element 67 | private inputEl: HTMLInputElement; // input element of this element 68 | private formControl: AbstractControl; 69 | private revertValue: any; 70 | private dropdownJustHidden: boolean; 71 | private scheduledBlurHandler: any; 72 | private documentClickListener: (e: MouseEvent) => any; 73 | 74 | constructor(public viewContainerRef: ViewContainerRef, 75 | @Optional() @Host() @SkipSelf() private parentForm: ControlContainer) { 76 | this.el = this.viewContainerRef.element.nativeElement; 77 | } 78 | 79 | ngOnInit(): void { 80 | // Blur event is handled only after a click event. 81 | // This is to prevent handling of blur events resulting from interacting with a scrollbar 82 | // introduced by content overflow (Internet explorer issue). 83 | // See issue description here: http://stackoverflow.com/questions/2023779/clicking-on-a-divs-scroll-bar-fires-the-blur-event-in-ie 84 | this.documentClickListener = (e) => { 85 | if (this.scheduledBlurHandler) { 86 | this.scheduledBlurHandler(); 87 | this.scheduledBlurHandler = null; 88 | } 89 | }; 90 | 91 | document.addEventListener('click', this.documentClickListener); 92 | // wrap this element with
    93 | this.wrapperEl = document.createElement('div'); 94 | this.wrapperEl.className = 'ngui-auto-complete-wrapper'; 95 | this.wrapperEl.style.position = 'relative'; 96 | this.el.parentElement.insertBefore(this.wrapperEl, this.el.nextSibling); 97 | this.wrapperEl.appendChild(this.el); 98 | 99 | // Check if we were supplied with a [formControlName] and it is inside a [form] 100 | // else check if we are supplied with a [FormControl] regardless if it is inside a [form] tag 101 | if (this.parentForm && this.formControlName) { 102 | if (this.parentForm['form']) { 103 | this.formControl = (this.parentForm['form'] as UntypedFormGroup).get(this.formControlName); 104 | } else if (this.parentForm instanceof FormGroupName) { 105 | this.formControl = (this.parentForm as FormGroupName).control.controls[this.formControlName]; 106 | } 107 | } else if (this.extFormControl) { 108 | this.formControl = this.extFormControl; 109 | } 110 | 111 | // apply toString() method for the object 112 | if (!!this.ngModel) { 113 | this.selectNewValue(this.ngModel); 114 | } else if (!!this.formControl && this.formControl.value) { 115 | this.selectNewValue(this.formControl.value); 116 | } 117 | 118 | } 119 | 120 | ngAfterViewInit() { 121 | // if this element is not an input tag, move dropdown after input tag 122 | // so that it displays correctly 123 | this.inputEl = this.el.tagName === 'INPUT' ? this.el as HTMLInputElement : this.el.querySelector('input'); 124 | 125 | if (this.openOnFocus) { 126 | this.inputEl.addEventListener('focus', (e) => this.showAutoCompleteDropdown(e)); 127 | } 128 | 129 | if (this.closeOnFocusOut) { 130 | this.inputEl.addEventListener('focusout', (e) => this.hideAutoCompleteDropdown(e)); 131 | } 132 | 133 | if (!this.autocomplete) { 134 | this.inputEl.setAttribute('autocomplete', 'off'); 135 | } 136 | this.inputEl.addEventListener('blur', (e) => { 137 | this.scheduledBlurHandler = () => { 138 | return this.blurHandler(e); 139 | }; 140 | }); 141 | this.inputEl.addEventListener('keydown', (e) => this.keydownEventHandler(e)); 142 | this.inputEl.addEventListener('input', (e) => this.inputEventHandler(e)); 143 | } 144 | 145 | ngOnDestroy(): void { 146 | if (this.componentRef) { 147 | this.componentRef.instance.valueSelected.unsubscribe(); 148 | this.componentRef.instance.textEntered.unsubscribe(); 149 | } 150 | if (this.documentClickListener) { 151 | document.removeEventListener('click', this.documentClickListener); 152 | } 153 | } 154 | 155 | ngOnChanges(changes: SimpleChanges): void { 156 | if (changes['ngModel']) { 157 | this.ngModel = this.setToStringFunction(changes['ngModel'].currentValue); 158 | this.renderValue(this.ngModel); 159 | } 160 | } 161 | 162 | // show auto-complete list below the current element 163 | public showAutoCompleteDropdown = (event?: any): void => { 164 | if (this.dropdownJustHidden) { 165 | return; 166 | } 167 | this.hideAutoCompleteDropdown(); 168 | this.scheduledBlurHandler = null; 169 | 170 | this.componentRef = this.viewContainerRef.createComponent(NguiAutoCompleteComponent); 171 | 172 | const component = this.componentRef.instance; 173 | component.keyword = this.inputEl.value; 174 | component.showInputTag = false; // Do NOT display autocomplete input tag separately 175 | 176 | component.pathToData = this.pathToData; 177 | component.minChars = this.minChars; 178 | component.source = this.source; 179 | component.placeholder = this.autoCompletePlaceholder; 180 | component.acceptUserInput = this.acceptUserInput; 181 | component.maxNumList = parseInt(this.maxNumList, 10); 182 | 183 | component.loadingText = this.loadingText; 184 | component.loadingTemplate = this.loadingTemplate; 185 | component.listFormatter = this.listFormatter; 186 | component.blankOptionText = this.blankOptionText; 187 | component.noMatchFoundText = this.noMatchFoundText; 188 | component.tabToSelect = this.tabToSelect; 189 | component.selectOnBlur = this.selectOnBlur; 190 | component.matchFormatted = this.matchFormatted; 191 | component.autoSelectFirstItem = this.autoSelectFirstItem; 192 | component.headerItemTemplate = this.headerItemTemplate; 193 | component.ignoreAccents = this.ignoreAccents; 194 | 195 | component.valueSelected.subscribe(this.selectNewValue); 196 | component.textEntered.subscribe(this.enterNewText); 197 | component.customSelected.subscribe(this.selectCustomValue); 198 | 199 | this.acDropdownEl = this.componentRef.location.nativeElement; 200 | this.acDropdownEl.style.display = 'none'; 201 | 202 | // if this element is not an input tag, move dropdown after input tag 203 | // so that it displays correctly 204 | 205 | // TODO: confirm with owners 206 | // with some reason, viewContainerRef.createComponent is creating element 207 | // to parent div which is created by us on ngOnInit, please try this with demo 208 | 209 | // if (this.el.tagName !== 'INPUT' && this.acDropdownEl) { 210 | this.inputEl.parentElement.insertBefore(this.acDropdownEl, this.inputEl.nextSibling); 211 | // } 212 | this.revertValue = typeof this.revertValue !== 'undefined' ? this.revertValue : ''; 213 | 214 | setTimeout(() => { 215 | component.reloadList(this.inputEl.value); 216 | this.styleAutoCompleteDropdown(); 217 | component.dropdownVisible = true; 218 | }); 219 | }; 220 | 221 | public blurHandler(event: any) { 222 | if (this.componentRef) { 223 | const component = this.componentRef.instance; 224 | 225 | if (this.selectOnBlur) { 226 | component.selectOne(component.filteredList[component.itemIndex]); 227 | } 228 | 229 | if (this.closeOnFocusOut) { 230 | this.hideAutoCompleteDropdown(event); 231 | } 232 | } 233 | } 234 | 235 | public hideAutoCompleteDropdown = (event?: any): void => { 236 | if (this.componentRef) { 237 | let currentItem: any; 238 | const hasRevertValue = (typeof this.revertValue !== 'undefined'); 239 | if (this.inputEl && hasRevertValue && this.acceptUserInput === false) { 240 | currentItem = this.componentRef.instance.findItemFromSelectValue(this.inputEl.value); 241 | } 242 | this.componentRef.destroy(); 243 | this.componentRef = undefined; 244 | 245 | if (this.inputEl && hasRevertValue && this.acceptUserInput === false && currentItem === null && this.inputEl.value !== '') { 246 | this.selectNewValue(this.revertValue); 247 | currentItem = this.revertValue; 248 | } else if (this.inputEl && this.acceptUserInput === true && typeof currentItem === 'undefined' && event && event.target.value) { 249 | this.enterNewText(event.target.value); 250 | } 251 | this.revertValue = currentItem; 252 | } 253 | this.dropdownJustHidden = true; 254 | setTimeout(() => this.dropdownJustHidden = false, 100); 255 | }; 256 | 257 | public styleAutoCompleteDropdown = () => { 258 | if (this.componentRef) { 259 | const component = this.componentRef.instance; 260 | 261 | /* setting width/height auto complete */ 262 | const thisElBCR = this.el.getBoundingClientRect(); 263 | const thisInputElBCR = this.inputEl.getBoundingClientRect(); 264 | const closeToBottom = thisInputElBCR.bottom + 100 > window.innerHeight; 265 | const directionOfStyle = this.isRtl ? 'right' : 'left'; 266 | 267 | this.acDropdownEl.style.width = thisInputElBCR.width + 'px'; 268 | this.acDropdownEl.style.position = 'absolute'; 269 | this.acDropdownEl.style.zIndex = this.zIndex; 270 | this.acDropdownEl.style[directionOfStyle] = '0'; 271 | this.acDropdownEl.style.display = 'inline-block'; 272 | 273 | if (closeToBottom) { 274 | this.acDropdownEl.style.bottom = `${thisInputElBCR.height}px`; 275 | } else { 276 | this.acDropdownEl.style.top = `${thisInputElBCR.height}px`; 277 | } 278 | } 279 | }; 280 | 281 | public setToStringFunction(item: any): any { 282 | if (item && typeof item === 'object') { 283 | let displayVal; 284 | 285 | if (typeof this.valueFormatter === 'string') { 286 | const matches = this.valueFormatter.match(/[a-zA-Z0-9_\$]+/g); 287 | let formatted = this.valueFormatter; 288 | if (matches && typeof item !== 'string') { 289 | matches.forEach((key) => { 290 | formatted = formatted.replace(key, item[key]); 291 | }); 292 | } 293 | displayVal = formatted; 294 | } else if (typeof this.valueFormatter === 'function') { 295 | displayVal = this.valueFormatter(item); 296 | } else if (this.displayPropertyName) { 297 | displayVal = item[this.displayPropertyName]; 298 | } else if (typeof this.listFormatter === 'string' && this.listFormatter.match(/^\w+$/)) { 299 | displayVal = item[this.listFormatter]; 300 | } else { 301 | displayVal = item.value; 302 | } 303 | item.toString = () => displayVal; 304 | } 305 | return item; 306 | } 307 | 308 | public selectNewValue = (item: any) => { 309 | // make displayable value 310 | if (item && typeof item === 'object') { 311 | item = this.setToStringFunction(item); 312 | } 313 | 314 | this.renderValue(item); 315 | 316 | // make return value 317 | let val = item; 318 | if (this.selectValueOf && item[this.selectValueOf]) { 319 | val = item[this.selectValueOf]; 320 | } 321 | if ((this.parentForm && this.formControlName) || this.extFormControl) { 322 | if (!!val) { 323 | this.formControl.patchValue(val); 324 | } 325 | } 326 | if (val !== this.ngModel) { 327 | this.ngModelChange.emit(val); 328 | } 329 | this.valueChanged.emit(val); 330 | this.hideAutoCompleteDropdown(); 331 | setTimeout(() => { 332 | if (this.reFocusAfterSelect) { 333 | this.inputEl.focus(); 334 | } 335 | 336 | return this.inputEl; 337 | }); 338 | }; 339 | 340 | public selectCustomValue = (text: string) => { 341 | this.customSelected.emit(text); 342 | this.hideAutoCompleteDropdown(); 343 | setTimeout(() => { 344 | if (this.reFocusAfterSelect) { 345 | this.inputEl.focus(); 346 | } 347 | 348 | return this.inputEl; 349 | }); 350 | }; 351 | 352 | public enterNewText = (value: any) => { 353 | this.renderValue(value); 354 | this.ngModelChange.emit(value); 355 | this.valueChanged.emit(value); 356 | this.hideAutoCompleteDropdown(); 357 | }; 358 | 359 | private keydownEventHandler = (evt: any) => { 360 | if (this.componentRef) { 361 | const component = this.componentRef.instance; 362 | component.inputElKeyHandler(evt); 363 | } 364 | }; 365 | 366 | private inputEventHandler = (evt: any) => { 367 | if (this.componentRef) { 368 | const component = this.componentRef.instance; 369 | component.dropdownVisible = true; 370 | component.keyword = evt.target.value; 371 | component.reloadListInDelay(evt); 372 | } else { 373 | this.showAutoCompleteDropdown(); 374 | } 375 | }; 376 | 377 | private renderValue(item: any) { 378 | if (!!this.inputEl) { 379 | this.inputEl.value = '' + item; 380 | } 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /docs/3rdpartylicenses.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- 3 | Package: @angular/core 4 | License: "MIT" 5 | 6 | 7 | -------------------------------------------------------------------------------- 8 | Package: rxjs 9 | License: "Apache-2.0" 10 | 11 | Apache License 12 | Version 2.0, January 2004 13 | http://www.apache.org/licenses/ 14 | 15 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 16 | 17 | 1. Definitions. 18 | 19 | "License" shall mean the terms and conditions for use, reproduction, 20 | and distribution as defined by Sections 1 through 9 of this document. 21 | 22 | "Licensor" shall mean the copyright owner or entity authorized by 23 | the copyright owner that is granting the License. 24 | 25 | "Legal Entity" shall mean the union of the acting entity and all 26 | other entities that control, are controlled by, or are under common 27 | control with that entity. For the purposes of this definition, 28 | "control" means (i) the power, direct or indirect, to cause the 29 | direction or management of such entity, whether by contract or 30 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 31 | outstanding shares, or (iii) beneficial ownership of such entity. 32 | 33 | "You" (or "Your") shall mean an individual or Legal Entity 34 | exercising permissions granted by this License. 35 | 36 | "Source" form shall mean the preferred form for making modifications, 37 | including but not limited to software source code, documentation 38 | source, and configuration files. 39 | 40 | "Object" form shall mean any form resulting from mechanical 41 | transformation or translation of a Source form, including but 42 | not limited to compiled object code, generated documentation, 43 | and conversions to other media types. 44 | 45 | "Work" shall mean the work of authorship, whether in Source or 46 | Object form, made available under the License, as indicated by a 47 | copyright notice that is included in or attached to the work 48 | (an example is provided in the Appendix below). 49 | 50 | "Derivative Works" shall mean any work, whether in Source or Object 51 | form, that is based on (or derived from) the Work and for which the 52 | editorial revisions, annotations, elaborations, or other modifications 53 | represent, as a whole, an original work of authorship. For the purposes 54 | of this License, Derivative Works shall not include works that remain 55 | separable from, or merely link (or bind by name) to the interfaces of, 56 | the Work and Derivative Works thereof. 57 | 58 | "Contribution" shall mean any work of authorship, including 59 | the original version of the Work and any modifications or additions 60 | to that Work or Derivative Works thereof, that is intentionally 61 | submitted to Licensor for inclusion in the Work by the copyright owner 62 | or by an individual or Legal Entity authorized to submit on behalf of 63 | the copyright owner. For the purposes of this definition, "submitted" 64 | means any form of electronic, verbal, or written communication sent 65 | to the Licensor or its representatives, including but not limited to 66 | communication on electronic mailing lists, source code control systems, 67 | and issue tracking systems that are managed by, or on behalf of, the 68 | Licensor for the purpose of discussing and improving the Work, but 69 | excluding communication that is conspicuously marked or otherwise 70 | designated in writing by the copyright owner as "Not a Contribution." 71 | 72 | "Contributor" shall mean Licensor and any individual or Legal Entity 73 | on behalf of whom a Contribution has been received by Licensor and 74 | subsequently incorporated within the Work. 75 | 76 | 2. Grant of Copyright License. Subject to the terms and conditions of 77 | this License, each Contributor hereby grants to You a perpetual, 78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 79 | copyright license to reproduce, prepare Derivative Works of, 80 | publicly display, publicly perform, sublicense, and distribute the 81 | Work and such Derivative Works in Source or Object form. 82 | 83 | 3. Grant of Patent License. Subject to the terms and conditions of 84 | this License, each Contributor hereby grants to You a perpetual, 85 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 86 | (except as stated in this section) patent license to make, have made, 87 | use, offer to sell, sell, import, and otherwise transfer the Work, 88 | where such license applies only to those patent claims licensable 89 | by such Contributor that are necessarily infringed by their 90 | Contribution(s) alone or by combination of their Contribution(s) 91 | with the Work to which such Contribution(s) was submitted. If You 92 | institute patent litigation against any entity (including a 93 | cross-claim or counterclaim in a lawsuit) alleging that the Work 94 | or a Contribution incorporated within the Work constitutes direct 95 | or contributory patent infringement, then any patent licenses 96 | granted to You under this License for that Work shall terminate 97 | as of the date such litigation is filed. 98 | 99 | 4. Redistribution. You may reproduce and distribute copies of the 100 | Work or Derivative Works thereof in any medium, with or without 101 | modifications, and in Source or Object form, provided that You 102 | meet the following conditions: 103 | 104 | (a) You must give any other recipients of the Work or 105 | Derivative Works a copy of this License; and 106 | 107 | (b) You must cause any modified files to carry prominent notices 108 | stating that You changed the files; and 109 | 110 | (c) You must retain, in the Source form of any Derivative Works 111 | that You distribute, all copyright, patent, trademark, and 112 | attribution notices from the Source form of the Work, 113 | excluding those notices that do not pertain to any part of 114 | the Derivative Works; and 115 | 116 | (d) If the Work includes a "NOTICE" text file as part of its 117 | distribution, then any Derivative Works that You distribute must 118 | include a readable copy of the attribution notices contained 119 | within such NOTICE file, excluding those notices that do not 120 | pertain to any part of the Derivative Works, in at least one 121 | of the following places: within a NOTICE text file distributed 122 | as part of the Derivative Works; within the Source form or 123 | documentation, if provided along with the Derivative Works; or, 124 | within a display generated by the Derivative Works, if and 125 | wherever such third-party notices normally appear. The contents 126 | of the NOTICE file are for informational purposes only and 127 | do not modify the License. You may add Your own attribution 128 | notices within Derivative Works that You distribute, alongside 129 | or as an addendum to the NOTICE text from the Work, provided 130 | that such additional attribution notices cannot be construed 131 | as modifying the License. 132 | 133 | You may add Your own copyright statement to Your modifications and 134 | may provide additional or different license terms and conditions 135 | for use, reproduction, or distribution of Your modifications, or 136 | for any such Derivative Works as a whole, provided Your use, 137 | reproduction, and distribution of the Work otherwise complies with 138 | the conditions stated in this License. 139 | 140 | 5. Submission of Contributions. Unless You explicitly state otherwise, 141 | any Contribution intentionally submitted for inclusion in the Work 142 | by You to the Licensor shall be under the terms and conditions of 143 | this License, without any additional terms or conditions. 144 | Notwithstanding the above, nothing herein shall supersede or modify 145 | the terms of any separate license agreement you may have executed 146 | with Licensor regarding such Contributions. 147 | 148 | 6. Trademarks. This License does not grant permission to use the trade 149 | names, trademarks, service marks, or product names of the Licensor, 150 | except as required for reasonable and customary use in describing the 151 | origin of the Work and reproducing the content of the NOTICE file. 152 | 153 | 7. Disclaimer of Warranty. Unless required by applicable law or 154 | agreed to in writing, Licensor provides the Work (and each 155 | Contributor provides its Contributions) on an "AS IS" BASIS, 156 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 157 | implied, including, without limitation, any warranties or conditions 158 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 159 | PARTICULAR PURPOSE. You are solely responsible for determining the 160 | appropriateness of using or redistributing the Work and assume any 161 | risks associated with Your exercise of permissions under this License. 162 | 163 | 8. Limitation of Liability. In no event and under no legal theory, 164 | whether in tort (including negligence), contract, or otherwise, 165 | unless required by applicable law (such as deliberate and grossly 166 | negligent acts) or agreed to in writing, shall any Contributor be 167 | liable to You for damages, including any direct, indirect, special, 168 | incidental, or consequential damages of any character arising as a 169 | result of this License or out of the use or inability to use the 170 | Work (including but not limited to damages for loss of goodwill, 171 | work stoppage, computer failure or malfunction, or any and all 172 | other commercial damages or losses), even if such Contributor 173 | has been advised of the possibility of such damages. 174 | 175 | 9. Accepting Warranty or Additional Liability. While redistributing 176 | the Work or Derivative Works thereof, You may choose to offer, 177 | and charge a fee for, acceptance of support, warranty, indemnity, 178 | or other liability obligations and/or rights consistent with this 179 | License. However, in accepting such obligations, You may act only 180 | on Your own behalf and on Your sole responsibility, not on behalf 181 | of any other Contributor, and only if You agree to indemnify, 182 | defend, and hold each Contributor harmless for any liability 183 | incurred by, or claims asserted against, such Contributor by reason 184 | of your accepting any such warranty or additional liability. 185 | 186 | END OF TERMS AND CONDITIONS 187 | 188 | APPENDIX: How to apply the Apache License to your work. 189 | 190 | To apply the Apache License to your work, attach the following 191 | boilerplate notice, with the fields enclosed by brackets "[]" 192 | replaced with your own identifying information. (Don't include 193 | the brackets!) The text should be enclosed in the appropriate 194 | comment syntax for the file format. We also recommend that a 195 | file or class name and description of purpose be included on the 196 | same "printed page" as the copyright notice for easier 197 | identification within third-party archives. 198 | 199 | Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors 200 | 201 | Licensed under the Apache License, Version 2.0 (the "License"); 202 | you may not use this file except in compliance with the License. 203 | You may obtain a copy of the License at 204 | 205 | http://www.apache.org/licenses/LICENSE-2.0 206 | 207 | Unless required by applicable law or agreed to in writing, software 208 | distributed under the License is distributed on an "AS IS" BASIS, 209 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 210 | See the License for the specific language governing permissions and 211 | limitations under the License. 212 | 213 | 214 | -------------------------------------------------------------------------------- 215 | Package: tslib 216 | License: "0BSD" 217 | 218 | Copyright (c) Microsoft Corporation. 219 | 220 | Permission to use, copy, modify, and/or distribute this software for any 221 | purpose with or without fee is hereby granted. 222 | 223 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 224 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 225 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 226 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 227 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 228 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 229 | PERFORMANCE OF THIS SOFTWARE. 230 | -------------------------------------------------------------------------------- 231 | Package: @angular/common 232 | License: "MIT" 233 | 234 | 235 | -------------------------------------------------------------------------------- 236 | Package: @angular/platform-browser 237 | License: "MIT" 238 | 239 | 240 | -------------------------------------------------------------------------------- 241 | Package: @angular/animations 242 | License: "MIT" 243 | 244 | 245 | -------------------------------------------------------------------------------- 246 | Package: @angular/forms 247 | License: "MIT" 248 | 249 | 250 | -------------------------------------------------------------------------------- 251 | Package: @angular/router 252 | License: "MIT" 253 | 254 | 255 | -------------------------------------------------------------------------------- 256 | Package: @angular/cdk 257 | License: "MIT" 258 | 259 | The MIT License 260 | 261 | Copyright (c) 2024 Google LLC. 262 | 263 | Permission is hereby granted, free of charge, to any person obtaining a copy 264 | of this software and associated documentation files (the "Software"), to deal 265 | in the Software without restriction, including without limitation the rights 266 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 267 | copies of the Software, and to permit persons to whom the Software is 268 | furnished to do so, subject to the following conditions: 269 | 270 | The above copyright notice and this permission notice shall be included in 271 | all copies or substantial portions of the Software. 272 | 273 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 274 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 275 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 276 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 277 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 278 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 279 | THE SOFTWARE. 280 | 281 | -------------------------------------------------------------------------------- 282 | Package: @angular/material 283 | License: "MIT" 284 | 285 | The MIT License 286 | 287 | Copyright (c) 2024 Google LLC. 288 | 289 | Permission is hereby granted, free of charge, to any person obtaining a copy 290 | of this software and associated documentation files (the "Software"), to deal 291 | in the Software without restriction, including without limitation the rights 292 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 293 | copies of the Software, and to permit persons to whom the Software is 294 | furnished to do so, subject to the following conditions: 295 | 296 | The above copyright notice and this permission notice shall be included in 297 | all copies or substantial portions of the Software. 298 | 299 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 300 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 301 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 302 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 303 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 304 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 305 | THE SOFTWARE. 306 | 307 | -------------------------------------------------------------------------------- 308 | Package: zone.js 309 | License: "MIT" 310 | 311 | The MIT License 312 | 313 | Copyright (c) 2010-2024 Google LLC. https://angular.io/license 314 | 315 | Permission is hereby granted, free of charge, to any person obtaining a copy 316 | of this software and associated documentation files (the "Software"), to deal 317 | in the Software without restriction, including without limitation the rights 318 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 319 | copies of the Software, and to permit persons to whom the Software is 320 | furnished to do so, subject to the following conditions: 321 | 322 | The above copyright notice and this permission notice shall be included in 323 | all copies or substantial portions of the Software. 324 | 325 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 326 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 327 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 328 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 329 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 330 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 331 | THE SOFTWARE. 332 | 333 | -------------------------------------------------------------------------------- 334 | -------------------------------------------------------------------------------- /docs/polyfills-MH5IBZ74.js: -------------------------------------------------------------------------------- 1 | var ce=globalThis;function ee(t){return(ce.__Zone_symbol_prefix||"__zone_symbol__")+t}function dt(){let t=ce.performance;function r(M){t&&t.mark&&t.mark(M)}function i(M,_){t&&t.measure&&t.measure(M,_)}r("Zone");let n=(()=>{let _=class _{static assertZonePatched(){if(ce.Promise!==L.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")}static get root(){let e=_.current;for(;e.parent;)e=e.parent;return e}static get current(){return b.zone}static get currentTask(){return S}static __load_patch(e,d,O=!1){if(L.hasOwnProperty(e)){let N=ce[ee("forceDuplicateZoneCheck")]===!0;if(!O&&N)throw Error("Already loaded patch: "+e)}else if(!ce["__Zone_disable_"+e]){let N="Zone:"+e;r(N),L[e]=d(ce,_,w),i(N,N)}}get parent(){return this._parent}get name(){return this._name}constructor(e,d){this._parent=e,this._name=d?d.name||"unnamed":"",this._properties=d&&d.properties||{},this._zoneDelegate=new f(this,this._parent&&this._parent._zoneDelegate,d)}get(e){let d=this.getZoneWith(e);if(d)return d._properties[e]}getZoneWith(e){let d=this;for(;d;){if(d._properties.hasOwnProperty(e))return d;d=d._parent}return null}fork(e){if(!e)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,e)}wrap(e,d){if(typeof e!="function")throw new Error("Expecting function got: "+e);let O=this._zoneDelegate.intercept(this,e,d),N=this;return function(){return N.runGuarded(O,this,arguments,d)}}run(e,d,O,N){b={parent:b,zone:this};try{return this._zoneDelegate.invoke(this,e,d,O,N)}finally{b=b.parent}}runGuarded(e,d=null,O,N){b={parent:b,zone:this};try{try{return this._zoneDelegate.invoke(this,e,d,O,N)}catch(D){if(this._zoneDelegate.handleError(this,D))throw D}}finally{b=b.parent}}runTask(e,d,O){if(e.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(e.zone||K).name+"; Execution: "+this.name+")");let N=e,{type:D,data:{isPeriodic:_e=!1,isRefreshable:ae=!1}={}}=e;if(e.state===X&&(D===W||D===y))return;let ne=e.state!=H;ne&&N._transitionTo(H,h);let Ee=S;S=N,b={parent:b,zone:this};try{D==y&&e.data&&!_e&&!ae&&(e.cancelFn=void 0);try{return this._zoneDelegate.invokeTask(this,N,d,O)}catch(l){if(this._zoneDelegate.handleError(this,l))throw l}}finally{let l=e.state;if(l!==X&&l!==Y)if(D==W||_e||ae&&l===k)ne&&N._transitionTo(h,H,k);else{let a=N._zoneDelegates;this._updateTaskCount(N,-1),ne&&N._transitionTo(X,H,X),ae&&(N._zoneDelegates=a)}b=b.parent,S=Ee}}scheduleTask(e){if(e.zone&&e.zone!==this){let O=this;for(;O;){if(O===e.zone)throw Error(`can not reschedule task to ${this.name} which is descendants of the original zone ${e.zone.name}`);O=O.parent}}e._transitionTo(k,X);let d=[];e._zoneDelegates=d,e._zone=this;try{e=this._zoneDelegate.scheduleTask(this,e)}catch(O){throw e._transitionTo(Y,k,X),this._zoneDelegate.handleError(this,O),O}return e._zoneDelegates===d&&this._updateTaskCount(e,1),e.state==k&&e._transitionTo(h,k),e}scheduleMicroTask(e,d,O,N){return this.scheduleTask(new T(F,e,d,O,N,void 0))}scheduleMacroTask(e,d,O,N,D){return this.scheduleTask(new T(y,e,d,O,N,D))}scheduleEventTask(e,d,O,N,D){return this.scheduleTask(new T(W,e,d,O,N,D))}cancelTask(e){if(e.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(e.zone||K).name+"; Execution: "+this.name+")");if(!(e.state!==h&&e.state!==H)){e._transitionTo(G,h,H);try{this._zoneDelegate.cancelTask(this,e)}catch(d){throw e._transitionTo(Y,G),this._zoneDelegate.handleError(this,d),d}return this._updateTaskCount(e,-1),e._transitionTo(X,G),e.runCount=-1,e}}_updateTaskCount(e,d){let O=e._zoneDelegates;d==-1&&(e._zoneDelegates=null);for(let N=0;NM.hasTask(c,e),onScheduleTask:(M,_,c,e)=>M.scheduleTask(c,e),onInvokeTask:(M,_,c,e,d,O)=>M.invokeTask(c,e,d,O),onCancelTask:(M,_,c,e)=>M.cancelTask(c,e)};class f{get zone(){return this._zone}constructor(_,c,e){this._taskCounts={microTask:0,macroTask:0,eventTask:0},this._zone=_,this._parentDelegate=c,this._forkZS=e&&(e&&e.onFork?e:c._forkZS),this._forkDlgt=e&&(e.onFork?c:c._forkDlgt),this._forkCurrZone=e&&(e.onFork?this._zone:c._forkCurrZone),this._interceptZS=e&&(e.onIntercept?e:c._interceptZS),this._interceptDlgt=e&&(e.onIntercept?c:c._interceptDlgt),this._interceptCurrZone=e&&(e.onIntercept?this._zone:c._interceptCurrZone),this._invokeZS=e&&(e.onInvoke?e:c._invokeZS),this._invokeDlgt=e&&(e.onInvoke?c:c._invokeDlgt),this._invokeCurrZone=e&&(e.onInvoke?this._zone:c._invokeCurrZone),this._handleErrorZS=e&&(e.onHandleError?e:c._handleErrorZS),this._handleErrorDlgt=e&&(e.onHandleError?c:c._handleErrorDlgt),this._handleErrorCurrZone=e&&(e.onHandleError?this._zone:c._handleErrorCurrZone),this._scheduleTaskZS=e&&(e.onScheduleTask?e:c._scheduleTaskZS),this._scheduleTaskDlgt=e&&(e.onScheduleTask?c:c._scheduleTaskDlgt),this._scheduleTaskCurrZone=e&&(e.onScheduleTask?this._zone:c._scheduleTaskCurrZone),this._invokeTaskZS=e&&(e.onInvokeTask?e:c._invokeTaskZS),this._invokeTaskDlgt=e&&(e.onInvokeTask?c:c._invokeTaskDlgt),this._invokeTaskCurrZone=e&&(e.onInvokeTask?this._zone:c._invokeTaskCurrZone),this._cancelTaskZS=e&&(e.onCancelTask?e:c._cancelTaskZS),this._cancelTaskDlgt=e&&(e.onCancelTask?c:c._cancelTaskDlgt),this._cancelTaskCurrZone=e&&(e.onCancelTask?this._zone:c._cancelTaskCurrZone),this._hasTaskZS=null,this._hasTaskDlgt=null,this._hasTaskDlgtOwner=null,this._hasTaskCurrZone=null;let d=e&&e.onHasTask,O=c&&c._hasTaskZS;(d||O)&&(this._hasTaskZS=d?e:s,this._hasTaskDlgt=c,this._hasTaskDlgtOwner=this,this._hasTaskCurrZone=this._zone,e.onScheduleTask||(this._scheduleTaskZS=s,this._scheduleTaskDlgt=c,this._scheduleTaskCurrZone=this._zone),e.onInvokeTask||(this._invokeTaskZS=s,this._invokeTaskDlgt=c,this._invokeTaskCurrZone=this._zone),e.onCancelTask||(this._cancelTaskZS=s,this._cancelTaskDlgt=c,this._cancelTaskCurrZone=this._zone))}fork(_,c){return this._forkZS?this._forkZS.onFork(this._forkDlgt,this.zone,_,c):new n(_,c)}intercept(_,c,e){return this._interceptZS?this._interceptZS.onIntercept(this._interceptDlgt,this._interceptCurrZone,_,c,e):c}invoke(_,c,e,d,O){return this._invokeZS?this._invokeZS.onInvoke(this._invokeDlgt,this._invokeCurrZone,_,c,e,d,O):c.apply(e,d)}handleError(_,c){return this._handleErrorZS?this._handleErrorZS.onHandleError(this._handleErrorDlgt,this._handleErrorCurrZone,_,c):!0}scheduleTask(_,c){let e=c;if(this._scheduleTaskZS)this._hasTaskZS&&e._zoneDelegates.push(this._hasTaskDlgtOwner),e=this._scheduleTaskZS.onScheduleTask(this._scheduleTaskDlgt,this._scheduleTaskCurrZone,_,c),e||(e=c);else if(c.scheduleFn)c.scheduleFn(c);else if(c.type==F)z(c);else throw new Error("Task is missing scheduleFn.");return e}invokeTask(_,c,e,d){return this._invokeTaskZS?this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt,this._invokeTaskCurrZone,_,c,e,d):c.callback.apply(e,d)}cancelTask(_,c){let e;if(this._cancelTaskZS)e=this._cancelTaskZS.onCancelTask(this._cancelTaskDlgt,this._cancelTaskCurrZone,_,c);else{if(!c.cancelFn)throw Error("Task is not cancelable");e=c.cancelFn(c)}return e}hasTask(_,c){try{this._hasTaskZS&&this._hasTaskZS.onHasTask(this._hasTaskDlgt,this._hasTaskCurrZone,_,c)}catch(e){this.handleError(_,e)}}_updateTaskCount(_,c){let e=this._taskCounts,d=e[_],O=e[_]=d+c;if(O<0)throw new Error("More tasks executed then were scheduled.");if(d==0||O==0){let N={microTask:e.microTask>0,macroTask:e.macroTask>0,eventTask:e.eventTask>0,change:_};this.hasTask(this._zone,N)}}}class T{constructor(_,c,e,d,O,N){if(this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=_,this.source=c,this.data=d,this.scheduleFn=O,this.cancelFn=N,!e)throw new Error("callback is not defined");this.callback=e;let D=this;_===W&&d&&d.useG?this.invoke=T.invokeTask:this.invoke=function(){return T.invokeTask.call(ce,D,this,arguments)}}static invokeTask(_,c,e){_||(_=this),Q++;try{return _.runCount++,_.zone.runTask(_,c,e)}finally{Q==1&&J(),Q--}}get zone(){return this._zone}get state(){return this._state}cancelScheduleRequest(){this._transitionTo(X,k)}_transitionTo(_,c,e){if(this._state===c||this._state===e)this._state=_,_==X&&(this._zoneDelegates=null);else throw new Error(`${this.type} '${this.source}': can not transition to '${_}', expecting state '${c}'${e?" or '"+e+"'":""}, was '${this._state}'.`)}toString(){return this.data&&typeof this.data.handleId<"u"?this.data.handleId.toString():Object.prototype.toString.call(this)}toJSON(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}}}let g=ee("setTimeout"),p=ee("Promise"),C=ee("then"),E=[],P=!1,j;function V(M){if(j||ce[p]&&(j=ce[p].resolve(0)),j){let _=j[C];_||(_=j.then),_.call(j,M)}else ce[g](M,0)}function z(M){Q===0&&E.length===0&&V(J),M&&E.push(M)}function J(){if(!P){for(P=!0;E.length;){let M=E;E=[];for(let _=0;_b,onUnhandledError:q,microtaskDrainDone:q,scheduleMicroTask:z,showUncaughtError:()=>!n[ee("ignoreConsoleErrorUncaughtError")],patchEventTarget:()=>[],patchOnProperties:q,patchMethod:()=>q,bindArguments:()=>[],patchThen:()=>q,patchMacroTask:()=>q,patchEventPrototype:()=>q,isIEOrEdge:()=>!1,getGlobalObjects:()=>{},ObjectDefineProperty:()=>q,ObjectGetOwnPropertyDescriptor:()=>{},ObjectCreate:()=>{},ArraySlice:()=>[],patchClass:()=>q,wrapWithCurrentZone:()=>q,filterProperties:()=>[],attachOriginToPatched:()=>q,_redefineProperty:()=>q,patchCallbacks:()=>q,nativeScheduleMicroTask:V},b={parent:null,zone:new n(null,null)},S=null,Q=0;function q(){}return i("Zone","Zone"),n}function _t(){let t=globalThis,r=t[ee("forceDuplicateZoneCheck")]===!0;if(t.Zone&&(r||typeof t.Zone.__symbol__!="function"))throw new Error("Zone already loaded.");return t.Zone??=dt(),t.Zone}var be=Object.getOwnPropertyDescriptor,Ae=Object.defineProperty,je=Object.getPrototypeOf,Et=Object.create,Tt=Array.prototype.slice,He="addEventListener",xe="removeEventListener",Le=ee(He),Ie=ee(xe),le="true",ue="false",Pe=ee("");function Ve(t,r){return Zone.current.wrap(t,r)}function Ge(t,r,i,n,s){return Zone.current.scheduleMacroTask(t,r,i,n,s)}var x=ee,De=typeof window<"u",pe=De?window:void 0,$=De&&pe||globalThis,gt="removeAttribute";function Fe(t,r){for(let i=t.length-1;i>=0;i--)typeof t[i]=="function"&&(t[i]=Ve(t[i],r+"_"+i));return t}function yt(t,r){let i=t.constructor.name;for(let n=0;n{let p=function(){return g.apply(this,Fe(arguments,i+"."+s))};return he(p,g),p})(f)}}}function tt(t){return t?t.writable===!1?!1:!(typeof t.get=="function"&&typeof t.set>"u"):!0}var nt=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,Se=!("nw"in $)&&typeof $.process<"u"&&$.process.toString()==="[object process]",Be=!Se&&!nt&&!!(De&&pe.HTMLElement),rt=typeof $.process<"u"&&$.process.toString()==="[object process]"&&!nt&&!!(De&&pe.HTMLElement),Ce={},mt=x("enable_beforeunload"),Ye=function(t){if(t=t||$.event,!t)return;let r=Ce[t.type];r||(r=Ce[t.type]=x("ON_PROPERTY"+t.type));let i=this||t.target||$,n=i[r],s;if(Be&&i===pe&&t.type==="error"){let f=t;s=n&&n.call(this,f.message,f.filename,f.lineno,f.colno,f.error),s===!0&&t.preventDefault()}else s=n&&n.apply(this,arguments),t.type==="beforeunload"&&$[mt]&&typeof s=="string"?t.returnValue=s:s!=null&&!s&&t.preventDefault();return s};function $e(t,r,i){let n=be(t,r);if(!n&&i&&be(i,r)&&(n={enumerable:!0,configurable:!0}),!n||!n.configurable)return;let s=x("on"+r+"patched");if(t.hasOwnProperty(s)&&t[s])return;delete n.writable,delete n.value;let f=n.get,T=n.set,g=r.slice(2),p=Ce[g];p||(p=Ce[g]=x("ON_PROPERTY"+g)),n.set=function(C){let E=this;if(!E&&t===$&&(E=$),!E)return;typeof E[p]=="function"&&E.removeEventListener(g,Ye),T&&T.call(E,null),E[p]=C,typeof C=="function"&&E.addEventListener(g,Ye,!1)},n.get=function(){let C=this;if(!C&&t===$&&(C=$),!C)return null;let E=C[p];if(E)return E;if(f){let P=f.call(this);if(P)return n.set.call(this,P),typeof C[gt]=="function"&&C.removeAttribute(r),P}return null},Ae(t,r,n),t[s]=!0}function ot(t,r,i){if(r)for(let n=0;nfunction(T,g){let p=i(T,g);return p.cbIdx>=0&&typeof g[p.cbIdx]=="function"?Ge(p.name,g[p.cbIdx],p,s):f.apply(T,g)})}function he(t,r){t[x("OriginalDelegate")]=r}var Je=!1,Me=!1;function kt(){try{let t=pe.navigator.userAgent;if(t.indexOf("MSIE ")!==-1||t.indexOf("Trident/")!==-1)return!0}catch{}return!1}function vt(){if(Je)return Me;Je=!0;try{let t=pe.navigator.userAgent;(t.indexOf("MSIE ")!==-1||t.indexOf("Trident/")!==-1||t.indexOf("Edge/")!==-1)&&(Me=!0)}catch{}return Me}function Ke(t){return typeof t=="function"}function Qe(t){return typeof t=="number"}var me=!1;if(typeof window<"u")try{let t=Object.defineProperty({},"passive",{get:function(){me=!0}});window.addEventListener("test",t,t),window.removeEventListener("test",t,t)}catch{me=!1}var bt={useG:!0},te={},st={},it=new RegExp("^"+Pe+"(\\w+)(true|false)$"),ct=x("propagationStopped");function at(t,r){let i=(r?r(t):t)+ue,n=(r?r(t):t)+le,s=Pe+i,f=Pe+n;te[t]={},te[t][ue]=s,te[t][le]=f}function Pt(t,r,i,n){let s=n&&n.add||He,f=n&&n.rm||xe,T=n&&n.listeners||"eventListeners",g=n&&n.rmAll||"removeAllListeners",p=x(s),C="."+s+":",E="prependListener",P="."+E+":",j=function(k,h,H){if(k.isRemoved)return;let G=k.callback;typeof G=="object"&&G.handleEvent&&(k.callback=y=>G.handleEvent(y),k.originalDelegate=G);let Y;try{k.invoke(k,h,[H])}catch(y){Y=y}let F=k.options;if(F&&typeof F=="object"&&F.once){let y=k.originalDelegate?k.originalDelegate:k.callback;h[f].call(h,H.type,y,F)}return Y};function V(k,h,H){if(h=h||t.event,!h)return;let G=k||h.target||t,Y=G[te[h.type][H?le:ue]];if(Y){let F=[];if(Y.length===1){let y=j(Y[0],G,h);y&&F.push(y)}else{let y=Y.slice();for(let W=0;W{throw W})}}}let z=function(k){return V(this,k,!1)},J=function(k){return V(this,k,!0)};function K(k,h){if(!k)return!1;let H=!0;h&&h.useG!==void 0&&(H=h.useG);let G=h&&h.vh,Y=!0;h&&h.chkDup!==void 0&&(Y=h.chkDup);let F=!1;h&&h.rt!==void 0&&(F=h.rt);let y=k;for(;y&&!y.hasOwnProperty(s);)y=je(y);if(!y&&k[s]&&(y=k),!y||y[p])return!1;let W=h&&h.eventNameToString,L={},w=y[p]=y[s],b=y[x(f)]=y[f],S=y[x(T)]=y[T],Q=y[x(g)]=y[g],q;h&&h.prepend&&(q=y[x(h.prepend)]=y[h.prepend]);function M(o,u){return!me&&typeof o=="object"&&o?!!o.capture:!me||!u?o:typeof o=="boolean"?{capture:o,passive:!0}:o?typeof o=="object"&&o.passive!==!1?{...o,passive:!0}:o:{passive:!0}}let _=function(o){if(!L.isExisting)return w.call(L.target,L.eventName,L.capture?J:z,L.options)},c=function(o){if(!o.isRemoved){let u=te[o.eventName],v;u&&(v=u[o.capture?le:ue]);let R=v&&o.target[v];if(R){for(let m=0;mre.zone.cancelTask(re);o.call(Te,"abort",ie,{once:!0}),re.removeAbortListener=()=>Te.removeEventListener("abort",ie)}if(L.target=null,ke&&(ke.taskData=null),Ue&&(L.options.once=!0),!me&&typeof re.options=="boolean"||(re.options=se),re.target=Z,re.capture=Oe,re.eventName=A,U&&(re.originalDelegate=B),I?ge.unshift(re):ge.push(re),m)return Z}};return y[s]=a(w,C,N,D,F),q&&(y[E]=a(q,P,d,D,F,!0)),y[f]=function(){let o=this||t,u=arguments[0];h&&h.transferEventName&&(u=h.transferEventName(u));let v=arguments[2],R=v?typeof v=="boolean"?!0:v.capture:!1,m=arguments[1];if(!m)return b.apply(this,arguments);if(G&&!G(b,m,o,arguments))return;let I=te[u],Z;I&&(Z=I[R?le:ue]);let A=Z&&o[Z];if(A)for(let B=0;Bfunction(s,f){s[ct]=!0,n&&n.apply(s,f)})}function Rt(t,r){r.patchMethod(t,"queueMicrotask",i=>function(n,s){Zone.current.scheduleMicroTask("queueMicrotask",s[0])})}var Re=x("zoneTask");function ye(t,r,i,n){let s=null,f=null;r+=n,i+=n;let T={};function g(C){let E=C.data;E.args[0]=function(){return C.invoke.apply(this,arguments)};let P=s.apply(t,E.args);return Qe(P)?E.handleId=P:(E.handle=P,E.isRefreshable=Ke(P.refresh)),C}function p(C){let{handle:E,handleId:P}=C.data;return f.call(t,E??P)}s=fe(t,r,C=>function(E,P){if(Ke(P[0])){let j={isRefreshable:!1,isPeriodic:n==="Interval",delay:n==="Timeout"||n==="Interval"?P[1]||0:void 0,args:P},V=P[0];P[0]=function(){try{return V.apply(this,arguments)}finally{let{handle:H,handleId:G,isPeriodic:Y,isRefreshable:F}=j;!Y&&!F&&(G?delete T[G]:H&&(H[Re]=null))}};let z=Ge(r,P[0],j,g,p);if(!z)return z;let{handleId:J,handle:K,isRefreshable:X,isPeriodic:k}=z.data;if(J)T[J]=z;else if(K&&(K[Re]=z,X&&!k)){let h=K.refresh;K.refresh=function(){let{zone:H,state:G}=z;return G==="notScheduled"?(z._state="scheduled",H._updateTaskCount(z,1)):G==="running"&&(z._state="scheduling"),h.call(this)}}return K??J??z}else return C.apply(t,P)}),f=fe(t,i,C=>function(E,P){let j=P[0],V;Qe(j)?(V=T[j],delete T[j]):(V=j?.[Re],V?j[Re]=null:V=j),V?.type?V.cancelFn&&V.zone.cancelTask(V):C.apply(t,P)})}function Ct(t,r){let{isBrowser:i,isMix:n}=r.getGlobalObjects();if(!i&&!n||!t.customElements||!("customElements"in t))return;let s=["connectedCallback","disconnectedCallback","adoptedCallback","attributeChangedCallback","formAssociatedCallback","formDisabledCallback","formResetCallback","formStateRestoreCallback"];r.patchCallbacks(r,t.customElements,"customElements","define",s)}function Dt(t,r){if(Zone[r.symbol("patchEventTarget")])return;let{eventNames:i,zoneSymbolEventNames:n,TRUE_STR:s,FALSE_STR:f,ZONE_SYMBOL_PREFIX:T}=r.getGlobalObjects();for(let p=0;pf.target===t);if(!n||n.length===0)return r;let s=n[0].ignoreProperties;return r.filter(f=>s.indexOf(f)===-1)}function et(t,r,i,n){if(!t)return;let s=ut(t,r,i);ot(t,s,n)}function Ze(t){return Object.getOwnPropertyNames(t).filter(r=>r.startsWith("on")&&r.length>2).map(r=>r.substring(2))}function Ot(t,r){if(Se&&!rt||Zone[t.symbol("patchEvents")])return;let i=r.__Zone_ignore_on_properties,n=[];if(Be){let s=window;n=n.concat(["Document","SVGElement","Element","HTMLElement","HTMLBodyElement","HTMLMediaElement","HTMLFrameSetElement","HTMLFrameElement","HTMLIFrameElement","HTMLMarqueeElement","Worker"]);let f=kt()?[{target:s,ignoreProperties:["error"]}]:[];et(s,Ze(s),i&&i.concat(f),je(s))}n=n.concat(["XMLHttpRequest","XMLHttpRequestEventTarget","IDBIndex","IDBRequest","IDBOpenDBRequest","IDBDatabase","IDBTransaction","IDBCursor","WebSocket"]);for(let s=0;s{let i=r[t.__symbol__("legacyPatch")];i&&i()}),t.__load_patch("timers",r=>{let i="set",n="clear";ye(r,i,n,"Timeout"),ye(r,i,n,"Interval"),ye(r,i,n,"Immediate")}),t.__load_patch("requestAnimationFrame",r=>{ye(r,"request","cancel","AnimationFrame"),ye(r,"mozRequest","mozCancel","AnimationFrame"),ye(r,"webkitRequest","webkitCancel","AnimationFrame")}),t.__load_patch("blocking",(r,i)=>{let n=["alert","prompt","confirm"];for(let s=0;sfunction(C,E){return i.current.run(T,r,E,p)})}}),t.__load_patch("EventTarget",(r,i,n)=>{St(r,n),Dt(r,n);let s=r.XMLHttpRequestEventTarget;s&&s.prototype&&n.patchEventTarget(r,n,[s.prototype])}),t.__load_patch("MutationObserver",(r,i,n)=>{ve("MutationObserver"),ve("WebKitMutationObserver")}),t.__load_patch("IntersectionObserver",(r,i,n)=>{ve("IntersectionObserver")}),t.__load_patch("FileReader",(r,i,n)=>{ve("FileReader")}),t.__load_patch("on_property",(r,i,n)=>{Ot(n,r)}),t.__load_patch("customElements",(r,i,n)=>{Ct(r,n)}),t.__load_patch("XHR",(r,i)=>{C(r);let n=x("xhrTask"),s=x("xhrSync"),f=x("xhrListener"),T=x("xhrScheduled"),g=x("xhrURL"),p=x("xhrErrorBeforeScheduled");function C(E){let P=E.XMLHttpRequest;if(!P)return;let j=P.prototype;function V(w){return w[n]}let z=j[Le],J=j[Ie];if(!z){let w=E.XMLHttpRequestEventTarget;if(w){let b=w.prototype;z=b[Le],J=b[Ie]}}let K="readystatechange",X="scheduled";function k(w){let b=w.data,S=b.target;S[T]=!1,S[p]=!1;let Q=S[f];z||(z=S[Le],J=S[Ie]),Q&&J.call(S,K,Q);let q=S[f]=()=>{if(S.readyState===S.DONE)if(!b.aborted&&S[T]&&w.state===X){let _=S[i.__symbol__("loadfalse")];if(S.status!==0&&_&&_.length>0){let c=w.invoke;w.invoke=function(){let e=S[i.__symbol__("loadfalse")];for(let d=0;dfunction(w,b){return w[s]=b[2]==!1,w[g]=b[1],G.apply(w,b)}),Y="XMLHttpRequest.send",F=x("fetchTaskAborting"),y=x("fetchTaskScheduling"),W=fe(j,"send",()=>function(w,b){if(i.current[y]===!0||w[s])return W.apply(w,b);{let S={target:w,url:w[g],isPeriodic:!1,args:b,aborted:!1},Q=Ge(Y,h,S,k,H);w&&w[p]===!0&&!S.aborted&&Q.state===X&&Q.invoke()}}),L=fe(j,"abort",()=>function(w,b){let S=V(w);if(S&&typeof S.type=="string"){if(S.cancelFn==null||S.data&&S.data.aborted)return;S.zone.cancelTask(S)}else if(i.current[F]===!0)return L.apply(w,b)})}}),t.__load_patch("geolocation",r=>{r.navigator&&r.navigator.geolocation&&yt(r.navigator.geolocation,["getCurrentPosition","watchPosition"])}),t.__load_patch("PromiseRejectionEvent",(r,i)=>{function n(s){return function(f){lt(r,s).forEach(g=>{let p=r.PromiseRejectionEvent;if(p){let C=new p(s,{promise:f.promise,reason:f.rejection});g.invoke(C)}})}}r.PromiseRejectionEvent&&(i[x("unhandledPromiseRejectionHandler")]=n("unhandledrejection"),i[x("rejectionHandledHandler")]=n("rejectionhandled"))}),t.__load_patch("queueMicrotask",(r,i,n)=>{Rt(r,n)})}function Lt(t){t.__load_patch("ZoneAwarePromise",(r,i,n)=>{let s=Object.getOwnPropertyDescriptor,f=Object.defineProperty;function T(l){if(l&&l.toString===Object.prototype.toString){let a=l.constructor&&l.constructor.name;return(a||"")+": "+JSON.stringify(l)}return l?l.toString():Object.prototype.toString.call(l)}let g=n.symbol,p=[],C=r[g("DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION")]!==!1,E=g("Promise"),P=g("then"),j="__creationTrace__";n.onUnhandledError=l=>{if(n.showUncaughtError()){let a=l&&l.rejection;a?console.error("Unhandled Promise rejection:",a instanceof Error?a.message:a,"; Zone:",l.zone.name,"; Task:",l.task&&l.task.source,"; Value:",a,a instanceof Error?a.stack:void 0):console.error(l)}},n.microtaskDrainDone=()=>{for(;p.length;){let l=p.shift();try{l.zone.runGuarded(()=>{throw l.throwOriginal?l.rejection:l})}catch(a){z(a)}}};let V=g("unhandledPromiseRejectionHandler");function z(l){n.onUnhandledError(l);try{let a=i[V];typeof a=="function"&&a.call(this,l)}catch{}}function J(l){return l&&l.then}function K(l){return l}function X(l){return D.reject(l)}let k=g("state"),h=g("value"),H=g("finally"),G=g("parentPromiseValue"),Y=g("parentPromiseState"),F="Promise.then",y=null,W=!0,L=!1,w=0;function b(l,a){return o=>{try{M(l,a,o)}catch(u){M(l,!1,u)}}}let S=function(){let l=!1;return function(o){return function(){l||(l=!0,o.apply(null,arguments))}}},Q="Promise resolved with itself",q=g("currentTaskTrace");function M(l,a,o){let u=S();if(l===o)throw new TypeError(Q);if(l[k]===y){let v=null;try{(typeof o=="object"||typeof o=="function")&&(v=o&&o.then)}catch(R){return u(()=>{M(l,!1,R)})(),l}if(a!==L&&o instanceof D&&o.hasOwnProperty(k)&&o.hasOwnProperty(h)&&o[k]!==y)c(o),M(l,o[k],o[h]);else if(a!==L&&typeof v=="function")try{v.call(o,u(b(l,a)),u(b(l,!1)))}catch(R){u(()=>{M(l,!1,R)})()}else{l[k]=a;let R=l[h];if(l[h]=o,l[H]===H&&a===W&&(l[k]=l[Y],l[h]=l[G]),a===L&&o instanceof Error){let m=i.currentTask&&i.currentTask.data&&i.currentTask.data[j];m&&f(o,q,{configurable:!0,enumerable:!1,writable:!0,value:m})}for(let m=0;m{try{let I=l[h],Z=!!o&&H===o[H];Z&&(o[G]=I,o[Y]=R);let A=a.run(m,void 0,Z&&m!==X&&m!==K?[]:[I]);M(o,!0,A)}catch(I){M(o,!1,I)}},o)}let d="function ZoneAwarePromise() { [native code] }",O=function(){},N=r.AggregateError;class D{static toString(){return d}static resolve(a){return a instanceof D?a:M(new this(null),W,a)}static reject(a){return M(new this(null),L,a)}static withResolvers(){let a={};return a.promise=new D((o,u)=>{a.resolve=o,a.reject=u}),a}static any(a){if(!a||typeof a[Symbol.iterator]!="function")return Promise.reject(new N([],"All promises were rejected"));let o=[],u=0;try{for(let m of a)u++,o.push(D.resolve(m))}catch{return Promise.reject(new N([],"All promises were rejected"))}if(u===0)return Promise.reject(new N([],"All promises were rejected"));let v=!1,R=[];return new D((m,I)=>{for(let Z=0;Z{v||(v=!0,m(A))},A=>{R.push(A),u--,u===0&&(v=!0,I(new N(R,"All promises were rejected")))})})}static race(a){let o,u,v=new this((I,Z)=>{o=I,u=Z});function R(I){o(I)}function m(I){u(I)}for(let I of a)J(I)||(I=this.resolve(I)),I.then(R,m);return v}static all(a){return D.allWithCallback(a)}static allSettled(a){return(this&&this.prototype instanceof D?this:D).allWithCallback(a,{thenCallback:u=>({status:"fulfilled",value:u}),errorCallback:u=>({status:"rejected",reason:u})})}static allWithCallback(a,o){let u,v,R=new this((A,B)=>{u=A,v=B}),m=2,I=0,Z=[];for(let A of a){J(A)||(A=this.resolve(A));let B=I;try{A.then(U=>{Z[B]=o?o.thenCallback(U):U,m--,m===0&&u(Z)},U=>{o?(Z[B]=o.errorCallback(U),m--,m===0&&u(Z)):v(U)})}catch(U){v(U)}m++,I++}return m-=2,m===0&&u(Z),R}constructor(a){let o=this;if(!(o instanceof D))throw new Error("Must be an instanceof Promise.");o[k]=y,o[h]=[];try{let u=S();a&&a(u(b(o,W)),u(b(o,L)))}catch(u){M(o,!1,u)}}get[Symbol.toStringTag](){return"Promise"}get[Symbol.species](){return D}then(a,o){let u=this.constructor?.[Symbol.species];(!u||typeof u!="function")&&(u=this.constructor||D);let v=new u(O),R=i.current;return this[k]==y?this[h].push(R,v,a,o):e(this,R,v,a,o),v}catch(a){return this.then(null,a)}finally(a){let o=this.constructor?.[Symbol.species];(!o||typeof o!="function")&&(o=D);let u=new o(O);u[H]=H;let v=i.current;return this[k]==y?this[h].push(v,u,a,a):e(this,v,u,a,a),u}}D.resolve=D.resolve,D.reject=D.reject,D.race=D.race,D.all=D.all;let _e=r[E]=r.Promise;r.Promise=D;let ae=g("thenPatched");function ne(l){let a=l.prototype,o=s(a,"then");if(o&&(o.writable===!1||!o.configurable))return;let u=a.then;a[P]=u,l.prototype.then=function(v,R){return new D((I,Z)=>{u.call(this,I,Z)}).then(v,R)},l[ae]=!0}n.patchThen=ne;function Ee(l){return function(a,o){let u=l.apply(a,o);if(u instanceof D)return u;let v=u.constructor;return v[ae]||ne(v),u}}return _e&&(ne(_e),fe(r,"fetch",l=>Ee(l))),Promise[i.__symbol__("uncaughtPromiseErrors")]=p,D})}function It(t){t.__load_patch("toString",r=>{let i=Function.prototype.toString,n=x("OriginalDelegate"),s=x("Promise"),f=x("Error"),T=function(){if(typeof this=="function"){let E=this[n];if(E)return typeof E=="function"?i.call(E):Object.prototype.toString.call(E);if(this===Promise){let P=r[s];if(P)return i.call(P)}if(this===Error){let P=r[f];if(P)return i.call(P)}}return i.call(this)};T[n]=i,Function.prototype.toString=T;let g=Object.prototype.toString,p="[object Promise]";Object.prototype.toString=function(){return typeof Promise=="function"&&this instanceof Promise?p:g.call(this)}})}function Mt(t,r,i,n,s){let f=Zone.__symbol__(n);if(r[f])return;let T=r[f]=r[n];r[n]=function(g,p,C){return p&&p.prototype&&s.forEach(function(E){let P=`${i}.${n}::`+E,j=p.prototype;try{if(j.hasOwnProperty(E)){let V=t.ObjectGetOwnPropertyDescriptor(j,E);V&&V.value?(V.value=t.wrapWithCurrentZone(V.value,P),t._redefineProperty(p.prototype,E,V)):j[E]&&(j[E]=t.wrapWithCurrentZone(j[E],P))}else j[E]&&(j[E]=t.wrapWithCurrentZone(j[E],P))}catch{}}),T.call(r,g,p,C)},t.attachOriginToPatched(r[n],T)}function Zt(t){t.__load_patch("util",(r,i,n)=>{let s=Ze(r);n.patchOnProperties=ot,n.patchMethod=fe,n.bindArguments=Fe,n.patchMacroTask=pt;let f=i.__symbol__("BLACK_LISTED_EVENTS"),T=i.__symbol__("UNPATCHED_EVENTS");r[T]&&(r[f]=r[T]),r[f]&&(i[f]=i[T]=r[f]),n.patchEventPrototype=wt,n.patchEventTarget=Pt,n.isIEOrEdge=vt,n.ObjectDefineProperty=Ae,n.ObjectGetOwnPropertyDescriptor=be,n.ObjectCreate=Et,n.ArraySlice=Tt,n.patchClass=ve,n.wrapWithCurrentZone=Ve,n.filterProperties=ut,n.attachOriginToPatched=he,n._redefineProperty=Object.defineProperty,n.patchCallbacks=Mt,n.getGlobalObjects=()=>({globalSources:st,zoneSymbolEventNames:te,eventNames:s,isBrowser:Be,isMix:rt,isNode:Se,TRUE_STR:le,FALSE_STR:ue,ZONE_SYMBOL_PREFIX:Pe,ADD_EVENT_LISTENER_STR:He,REMOVE_EVENT_LISTENER_STR:xe})})}function At(t){Lt(t),It(t),Zt(t)}var ft=_t();At(ft);Nt(ft); 3 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular auto-complete Showcase 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | --------------------------------------------------------------------------------