├── .nvmrc ├── config ├── resource-override.js ├── empty.js ├── setup-jest.ts ├── helpers.js ├── protractor.conf.js ├── spec-bundle.js ├── head-config.common.js ├── github-deploy │ └── index.js ├── webpack.github-deploy.js ├── html-elements-plugin │ └── index.js ├── karma.conf.js ├── webpack.package.ts └── webpack.test.js ├── .gitattributes ├── scripts ├── util.ts ├── gulp │ ├── clean.ts │ ├── release.ts │ ├── misc.ts │ ├── manifest.ts │ ├── compile.ts │ └── build.ts └── util │ ├── index.ts │ ├── state.ts │ ├── gulpclass.ts │ ├── hooks.ts │ ├── fs.ts │ ├── release_mgmt.ts │ ├── rollup.ts │ ├── pure-annotations.ts │ ├── typescript-transpile.ts │ ├── metadata-inlining.ts │ ├── inline-resources.ts │ ├── types.ts │ ├── simulation.ts │ └── util.ts ├── src ├── demo │ ├── assets │ │ ├── css │ │ │ └── .gitkeep │ │ ├── data.json │ │ ├── robots.txt │ │ ├── mock-data │ │ │ └── mock-data.json │ │ ├── service-worker.js │ │ ├── icon │ │ │ ├── favicon.ico │ │ │ ├── apple-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon-96x96.png │ │ │ ├── ms-icon-70x70.png │ │ │ ├── apple-icon-57x57.png │ │ │ ├── apple-icon-60x60.png │ │ │ ├── apple-icon-72x72.png │ │ │ ├── apple-icon-76x76.png │ │ │ ├── ms-icon-144x144.png │ │ │ ├── ms-icon-150x150.png │ │ │ ├── ms-icon-310x310.png │ │ │ ├── android-icon-36x36.png │ │ │ ├── android-icon-48x48.png │ │ │ ├── android-icon-72x72.png │ │ │ ├── android-icon-96x96.png │ │ │ ├── apple-icon-114x114.png │ │ │ ├── apple-icon-120x120.png │ │ │ ├── apple-icon-144x144.png │ │ │ ├── apple-icon-152x152.png │ │ │ ├── apple-icon-180x180.png │ │ │ ├── android-icon-144x144.png │ │ │ ├── android-icon-192x192.png │ │ │ ├── apple-icon-precomposed.png │ │ │ └── browserconfig.xml │ │ ├── img │ │ │ ├── angular-logo.png │ │ │ ├── angularclass-logo.png │ │ │ └── angularclass-avatar.png │ │ ├── humans.txt │ │ └── manifest.json │ ├── app │ │ ├── hierarchical │ │ │ ├── hierarchical.component.scss │ │ │ ├── hierarchical.component.ts │ │ │ └── hierarchical.component.html │ │ ├── index.ts │ │ ├── app.component.ts │ │ ├── app.routes.ts │ │ ├── app.e2e.ts │ │ ├── simple-value │ │ │ ├── simple-value.component.ts │ │ │ └── simple-value.component.html │ │ ├── flat │ │ │ ├── flat.component.ts │ │ │ └── flat.component.html │ │ ├── app.module.js.map │ │ ├── app.component.spec.ts │ │ ├── app.module.ts │ │ ├── app.component.html │ │ ├── environment.ts │ │ └── app.component.js.map │ ├── styles │ │ ├── _variables.scss │ │ ├── headings.css │ │ └── styles.scss │ ├── meta │ │ ├── robots.txt │ │ └── humans.txt │ ├── main.js.map │ ├── main.browser.aot.ts │ ├── main.browser.ts │ ├── polyfills.browser.ts │ ├── index.html │ └── custom-typings.d.ts ├── datas │ ├── index.ts │ └── simple-data.ts ├── typings.d.ts ├── ngx-tree-select │ ├── src │ │ ├── models │ │ │ ├── expand-mode.ts │ │ │ ├── tree-select-default-options.ts │ │ │ ├── select-option.ts │ │ │ └── selectable-item.ts │ │ ├── index.ts │ │ ├── pipes │ │ │ └── item.pipe.ts │ │ ├── directives │ │ │ └── off-click.directive.ts │ │ ├── components │ │ │ ├── tree-select-item.component.scss │ │ │ ├── tree-select-item.component.html │ │ │ ├── tree-select.component.scss │ │ │ ├── tree-select.component.html │ │ │ └── tree-select-item.component.ts │ │ └── module.ts │ ├── build_hooks.js │ ├── package.json │ └── test │ │ ├── select.service.spec.ts │ │ └── tree-select.component.spec.ts └── lib │ ├── src │ ├── model │ │ ├── tree-select-default-options.js.map │ │ ├── ngxTreeSelectDefaultOption.js.map │ │ ├── SelectOption.js.map │ │ └── selectable-item.js.map │ ├── component │ │ ├── tree-select-item.component.css │ │ ├── tree-select.component.css │ │ └── tree-select-item.component.js.map │ ├── pipe │ │ ├── item.pipe.js.map │ │ └── isVisible.pipe.js.map │ ├── directive │ │ └── off-click.directive.js.map │ └── module.js.map │ └── index.js.map ├── karma.conf.js ├── .vscode ├── settings.json └── launch.json ├── protractor.conf.js ├── firebase.json ├── .editorconfig ├── gulpfile.js ├── typedoc.json ├── .dockerignore ├── webpack.config.js ├── webpack-debug.js ├── tsconfig.package.json ├── jest.library.config.json ├── tsconfig.spec.json ├── LICENSE ├── tsconfig.json ├── .gitignore ├── tsconfig.webpack.json ├── .travis.yml ├── Dockerfile ├── tslint.json ├── CODE_OF_CONDUCT.md ├── README.md └── CHANGELOG.md /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /config/resource-override.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | yarn.lock -diff 2 | -------------------------------------------------------------------------------- /scripts/util.ts: -------------------------------------------------------------------------------- 1 | export * from './util/index'; -------------------------------------------------------------------------------- /src/demo/assets/css/.gitkeep: -------------------------------------------------------------------------------- 1 | @AngularClass 2 | -------------------------------------------------------------------------------- /src/demo/app/hierarchical/hierarchical.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/demo/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | $nav-button-color: #00838F; -------------------------------------------------------------------------------- /src/demo/styles/headings.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: #00BCD4; 3 | } -------------------------------------------------------------------------------- /src/demo/assets/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": "AngularClass" 3 | } 4 | -------------------------------------------------------------------------------- /src/demo/meta/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /src/demo/assets/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /src/demo/assets/mock-data/mock-data.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"res": "data"} 3 | ] 4 | -------------------------------------------------------------------------------- /src/demo/app/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * App 3 | */ 4 | export * from './app.module'; 5 | -------------------------------------------------------------------------------- /src/demo/assets/service-worker.js: -------------------------------------------------------------------------------- 1 | // This file is intentionally without code. 2 | -------------------------------------------------------------------------------- /src/demo/assets/icon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/favicon.ico -------------------------------------------------------------------------------- /src/datas/index.ts: -------------------------------------------------------------------------------- 1 | export * from './flat-data'; 2 | export * from './hierarchical-data'; 3 | export * from './simple-data'; 4 | -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon.png -------------------------------------------------------------------------------- /src/demo/assets/img/angular-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/img/angular-logo.png -------------------------------------------------------------------------------- /src/demo/assets/icon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/favicon-16x16.png -------------------------------------------------------------------------------- /src/demo/assets/icon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/favicon-32x32.png -------------------------------------------------------------------------------- /src/demo/assets/icon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/favicon-96x96.png -------------------------------------------------------------------------------- /src/demo/assets/icon/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/ms-icon-70x70.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-57x57.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-60x60.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-72x72.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-76x76.png -------------------------------------------------------------------------------- /src/demo/assets/icon/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/ms-icon-144x144.png -------------------------------------------------------------------------------- /src/demo/assets/icon/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/ms-icon-150x150.png -------------------------------------------------------------------------------- /src/demo/assets/icon/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/ms-icon-310x310.png -------------------------------------------------------------------------------- /src/demo/assets/img/angularclass-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/img/angularclass-logo.png -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace jest { 2 | interface Matchers { 3 | resolves: Matchers; 4 | rejects: Matchers; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/demo/assets/icon/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/android-icon-36x36.png -------------------------------------------------------------------------------- /src/demo/assets/icon/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/android-icon-48x48.png -------------------------------------------------------------------------------- /src/demo/assets/icon/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/android-icon-72x72.png -------------------------------------------------------------------------------- /src/demo/assets/icon/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/android-icon-96x96.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-114x114.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-120x120.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-144x144.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-152x152.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-180x180.png -------------------------------------------------------------------------------- /src/demo/assets/img/angularclass-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/img/angularclass-avatar.png -------------------------------------------------------------------------------- /src/demo/assets/icon/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/android-icon-144x144.png -------------------------------------------------------------------------------- /src/demo/assets/icon/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/android-icon-192x192.png -------------------------------------------------------------------------------- /src/demo/assets/icon/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crazyht/ngx-tree-select/HEAD/src/demo/assets/icon/apple-icon-precomposed.png -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * Look in ./config for karma.conf.js 7 | */ 8 | module.exports = require('./config/karma.conf.js'); 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib/", 3 | "files.exclude": { 4 | "src/**/*.js": true, 5 | "src/**/*.map": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/models/expand-mode.ts: -------------------------------------------------------------------------------- 1 | export class ExpandMode { 2 | public static None = 'None'; 3 | public static Selection = 'Selection'; 4 | public static All = 'All'; 5 | } 6 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * look in ./config for protractor.conf.js 7 | */ 8 | exports.config = require('./config/protractor.conf.js').config; 9 | -------------------------------------------------------------------------------- /config/empty.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NgProbeToken: {}, 3 | HmrState: () => {}, 4 | _createConditionalRootRenderer: (rootRenderer, extraTokens, coreTokens) => { 5 | return rootRenderer; 6 | }, 7 | __platform_browser_private__: {} 8 | }; 9 | -------------------------------------------------------------------------------- /scripts/gulp/clean.ts: -------------------------------------------------------------------------------- 1 | import * as gulp from 'gulp'; 2 | import * as del from 'del'; 3 | 4 | import * as util from '../util'; 5 | 6 | gulp.task('!clean:dist', () => { 7 | return del([util.root(util.FS_REF.PKG_DIST), util.root(util.FS_REF.NG_COMPILE)]); 8 | }); -------------------------------------------------------------------------------- /src/datas/simple-data.ts: -------------------------------------------------------------------------------- 1 | export const FirstnameList = [ 2 | 'Jacques', 3 | 'Jad', 4 | 'Jana', 5 | 'Jasmine', 6 | 'Jeremie', 7 | 'Jeremy', 8 | 'Joachim', 9 | 'Johan', 10 | 'Johanna', 11 | 'Jonathan', 12 | 'Jordan', 13 | 'Joseph', 14 | 'Jules', 15 | 'Justin' 16 | ]; 17 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "destination": "/index.html" 8 | } 9 | ], 10 | "ignore": [ 11 | "firebase.json", 12 | "**/.*", 13 | "**/node_modules/**" 14 | ] 15 | } 16 | } -------------------------------------------------------------------------------- /src/demo/assets/icon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # @AngularClass 2 | # http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | insert_final_newline = false 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /src/demo/assets/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | Crazyht -- @crazyht 12 | 13 | # TECHNOLOGY COLOPHON 14 | 15 | HTML5, CSS3 16 | Angular4, TypeScript, Webpack 17 | Dropdown, Select, Treeview 18 | -------------------------------------------------------------------------------- /src/ngx-tree-select/build_hooks.js: -------------------------------------------------------------------------------- 1 | module.exports.packageJSON = function(pkgJson) { }; 2 | 3 | module.exports.jestConfig = function(jestConfig) { }; 4 | 5 | module.exports.tsconfig = function tsconfig(config) { }; 6 | 7 | module.exports.rollupFESM = function(rollupConfig) { }; 8 | 9 | module.exports.rollupUMD = function(rollupConfig) { }; 10 | -------------------------------------------------------------------------------- /src/demo/meta/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | PatrickJS -- @gdi2290 12 | AngularClass -- @AngularClass 13 | 14 | # TECHNOLOGY COLOPHON 15 | 16 | HTML5, CSS3 17 | Angular2, TypeScript, Webpack 18 | -------------------------------------------------------------------------------- /src/demo/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { HierarchicalCountries } from '../../datas/hierarchical-data'; 3 | import { FlatCountries } from '../../datas/flat-data'; 4 | 5 | @Component({ 6 | selector: 'demo-app', 7 | templateUrl: './app.component.html' 8 | }) 9 | export class AppComponent { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // try { require('gulplog').info = function() {}; } catch (err) {} 2 | // try { require('fancy-log').apply = function() {}; } catch (err) {} 3 | 4 | const path = require('path'); 5 | const gulp = require('gulp'); 6 | 7 | require('ts-node/register'); 8 | require('require-dir')(path.join(__dirname, 'scripts', 'gulp')); 9 | 10 | 11 | gulp.task('build', ['!compile']); -------------------------------------------------------------------------------- /src/demo/main.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":";;AAAA,8EAA2E;AAE3E,+CAA6C;AAE7C,iDAAsB,EAAE,CAAC,eAAe,CAAC,sBAAS,CAAC,CAAC","sourcesContent":["import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\r\n\r\nimport { AppModule } from './app/app.module';\r\n\r\nplatformBrowserDynamic().bootstrapModule(AppModule);\r\n"]} -------------------------------------------------------------------------------- /src/ngx-tree-select/src/index.ts: -------------------------------------------------------------------------------- 1 | import { ExpandMode } from './models/expand-mode'; 2 | export { TreeSelectDefaultOptions } from './models/tree-select-default-options'; 3 | export { TreeSelectComponent } from './components/tree-select.component'; 4 | export { ItemPipe } from './pipes/item.pipe'; 5 | export { NgxTreeSelectModule } from './module'; 6 | export { ExpandMode } from './models/expand-mode'; 7 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/pipes/item.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { SelectableItem } from '../models/selectable-item'; 3 | 4 | @Pipe({ name: 'itemPipe' }) 5 | export class ItemPipe implements PipeTransform { 6 | public transform(value: SelectableItem[]) { 7 | // ES6 array destructuring 8 | return value.filter((item) => item.matchFilter); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "modules", 3 | "out": "doc", 4 | "theme": "default", 5 | "ignoreCompilerErrors": "true", 6 | "experimentalDecorators": "true", 7 | "emitDecoratorMetadata": "true", 8 | "target": "ES5", 9 | "moduleResolution": "node", 10 | "preserveConstEnums": "true", 11 | "stripInternal": "true", 12 | "suppressExcessPropertyErrors": "true", 13 | "suppressImplicitAnyIndexErrors": "true", 14 | "module": "commonjs" 15 | } 16 | -------------------------------------------------------------------------------- /src/ngx-tree-select/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-tree-select", 3 | "version": "0.15.0", 4 | "description": "Angular component for select with tree items.", 5 | "keywords": [ 6 | "angular4", 7 | "treeview", 8 | "dropdown", 9 | "select" 10 | ], 11 | "peerDependencies": { 12 | "@angular/core": ">=4.0.0 <^11.1.0", 13 | "@angular/forms": ">=4.0.0 <^11.1.0" 14 | }, 15 | "libConfig": { 16 | "inlineResources": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ngx-tree-select/test/select.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { inject, TestBed } from '@angular/core/testing'; 2 | import { SelectService } from '../src/services/select.service'; 3 | 4 | describe('SelectService', () => { 5 | beforeEach(() => { 6 | TestBed.configureTestingModule({ 7 | providers: [SelectService] 8 | }); 9 | }); 10 | 11 | it('should create service', inject([SelectService], (service: SelectService) => { 12 | expect(service).toBeTruthy(); 13 | })); 14 | }); 15 | -------------------------------------------------------------------------------- /scripts/util/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './fs'; 3 | export * from './state'; 4 | export * from './config'; 5 | export * from './util'; 6 | export * from './release_mgmt'; 7 | export * from './hooks'; 8 | export * from './pure-annotations'; 9 | export * from './typescript-transpile'; 10 | export * from './simulation'; 11 | export * from './rollup'; 12 | export * from './metadata-inlining'; 13 | export * from './inline-resources'; 14 | export { GulpClass } from './gulpclass'; 15 | -------------------------------------------------------------------------------- /src/demo/styles/styles.scss: -------------------------------------------------------------------------------- 1 | /* this file will be extracted to main dist folder and is imported in index.html */ 2 | /* This file is for setting global styles */ 3 | @import 'variables'; 4 | 5 | nav { 6 | margin-top: 16px; 7 | } 8 | 9 | nav a { 10 | background-color: $nav-button-color; 11 | color: white; 12 | padding: 8px 16px; 13 | margin: 8px; 14 | vertical-align: middle; 15 | line-height: 1.25; 16 | text-align: center; 17 | text-decoration: none; 18 | border-radius: 4px; 19 | } 20 | -------------------------------------------------------------------------------- /src/demo/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { FlatComponent } from './flat/flat.component'; 2 | import { HierarchicalComponent } from './hierarchical/hierarchical.component'; 3 | import { SimpleValueComponent } from './simple-value/simple-value.component'; 4 | 5 | export const AppRoutes = [ 6 | { path: '', redirectTo: '/flat', pathMatch: 'full' }, 7 | { path: 'flat', component: FlatComponent }, 8 | { path: 'hierarchical', component: HierarchicalComponent }, 9 | { path: 'simplevalue', component: SimpleValueComponent }, 10 | 11 | ]; 12 | -------------------------------------------------------------------------------- /src/lib/src/model/tree-select-default-options.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tree-select-default-options.js","sourceRoot":"","sources":["tree-select-default-options.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA2C;AAG3C,IAAa,wBAAwB;IAArC;IAGA,CAAC;IAAD,+BAAC;AAAD,CAAC,AAHD,IAGC;AAHY,wBAAwB;IADpC,iBAAU,EAAE;GACA,wBAAwB,CAGpC;AAHY,4DAAwB","sourcesContent":["import { Injectable } from '@angular/core';\r\n\r\n@Injectable()\r\nexport class TreeSelectDefaultOptions {\r\n public filterPlaceholder?: string;\r\n public maxVisibleItemCount?: number;\r\n}\r\n"]} -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .vscode 4 | coverage 5 | 6 | # OS generated files # 7 | .DS_Store 8 | ehthumbs.db 9 | Icon? 10 | Thumbs.db 11 | 12 | # Node Files # 13 | node_modules 14 | npm-debug.log 15 | npm-debug.log.* 16 | 17 | # Typing # 18 | src/typings/tsd 19 | typings 20 | tsd_typings 21 | 22 | # Dist # 23 | dist 24 | .awcache 25 | .webpack.json 26 | compiled 27 | dll 28 | 29 | # IDE # 30 | .idea 31 | *.swp 32 | 33 | 34 | # Angular # 35 | *.ngfactory.ts 36 | *.css.shim.ts 37 | *.ngsummary.json 38 | *.shim.ngstyle.ts 39 | 40 | -------------------------------------------------------------------------------- /src/demo/app/app.e2e.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | import 'tslib'; 3 | 4 | describe('App', () => { 5 | 6 | beforeEach(async () => { 7 | await browser.get('/'); 8 | }); 9 | 10 | afterEach(() => { 11 | browser.manage().logs().get('browser').then((browserLog: any[]) => { 12 | expect(browserLog).toEqual([]); 13 | }); 14 | }); 15 | 16 | it('should have ', async () => { 17 | const subject = await element(by.css('tree-select')).isPresent(); 18 | expect(true).toBeTruthy(subject); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/models/tree-select-default-options.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { ExpandMode } from './expand-mode'; 3 | 4 | @Injectable() 5 | export class TreeSelectDefaultOptions { 6 | public allowFilter?: boolean; 7 | public filterPlaceholder?: string; 8 | public maxVisibleItemCount?: number; 9 | public allowParentSelection?: boolean; 10 | public idField?: string; 11 | public textField?: string; 12 | public childrenField?: string; 13 | public filterCaseSensitive?: boolean; 14 | public expandMode = ExpandMode.None; 15 | } 16 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * Look in ./config folder for webpack.dev.js 7 | */ 8 | switch (process.env.NODE_ENV) { 9 | case 'prod': 10 | case 'production': 11 | module.exports = require('./config/webpack.prod')({env: 'production'}); 12 | break; 13 | case 'test': 14 | case 'testing': 15 | module.exports = require('./config/webpack.test')({env: 'test'}); 16 | break; 17 | case 'dev': 18 | case 'development': 19 | default: 20 | module.exports = require('./config/webpack.dev')({env: 'development'}); 21 | } 22 | -------------------------------------------------------------------------------- /config/setup-jest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular'; 2 | 3 | const mock = () => { 4 | let storage = {}; 5 | return { 6 | getItem: key => key in storage ? storage[key] : null, 7 | setItem: (key, value) => storage[key] = value || '', 8 | removeItem: key => delete storage[key], 9 | clear: () => storage = {}, 10 | }; 11 | }; 12 | 13 | Object.defineProperty(window, 'localStorage', {value: mock()}); 14 | Object.defineProperty(window, 'sessionStorage', {value: mock()}); 15 | Object.defineProperty(window, 'getComputedStyle', { 16 | value: () => ['-webkit-appearance'] 17 | }); 18 | -------------------------------------------------------------------------------- /src/lib/src/model/ngxTreeSelectDefaultOption.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ngxTreeSelectDefaultOption.js","sourceRoot":"","sources":["ngxTreeSelectDefaultOption.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA2C;AAG3C,IAAa,0BAA0B;IADvC;QAES,sBAAiB,GAAG,kCAAkC,CAAC;QACvD,aAAQ,GAAG,CAAC,CAAC;IACtB,CAAC;IAAD,iCAAC;AAAD,CAAC,AAHD,IAGC;AAHY,0BAA0B;IADtC,iBAAU,EAAE;GACA,0BAA0B,CAGtC;AAHY,gEAA0B","sourcesContent":["import { Injectable } from '@angular/core';\r\n\r\n@Injectable()\r\nexport class NgxTreeSelectDefaultOption {\r\n public FilterPlaceholder = 'Type here for filtering items...';\r\n public MaxItems = 3;\r\n}\r\n"]} -------------------------------------------------------------------------------- /src/lib/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAAA,uFAAmF;AAA1E,iEAAA,wBAAwB,CAAA;AACjC,+EAA4E;AAAnE,sDAAA,mBAAmB,CAAA;AAC5B,kDAAgD;AAAvC,+BAAA,QAAQ,CAAA;AACjB,4DAA0D;AAAjD,yCAAA,aAAa,CAAA;AACtB,uCAAmD;AAA1C,uCAAA,mBAAmB,CAAA","sourcesContent":["export { TreeSelectDefaultOptions } from './src/model/tree-select-default-options';\r\nexport { TreeSelectComponent } from './src/component/tree-select.component';\r\nexport { ItemPipe } from './src/pipe/item.pipe';\r\nexport { IsVisiblePipe } from './src/pipe/isVisible.pipe';\r\nexport { NgxTreeSelectModule } from './src/module';\r\n"]} -------------------------------------------------------------------------------- /src/lib/src/component/tree-select-item.component.css: -------------------------------------------------------------------------------- 1 | .item { 2 | color: #333; 3 | } 4 | 5 | .item ul { 6 | list-style-type: none; 7 | } 8 | 9 | .item > a { 10 | clear: both; 11 | color: inherit; 12 | display: block; 13 | font-weight: 400; 14 | line-height: 1.42857143; 15 | padding: 3px 20px; 16 | padding-left: 10px; 17 | text-decoration: none; 18 | white-space: nowrap; 19 | } 20 | 21 | .item > a:hover { 22 | background-color: #357ebd; 23 | color: #fff; 24 | outline: 0; 25 | text-decoration: none; 26 | } 27 | 28 | .item.active { 29 | background-color: #428bca; 30 | color: #fff; 31 | outline: 0; 32 | text-decoration: none; 33 | } 34 | -------------------------------------------------------------------------------- /scripts/util/state.ts: -------------------------------------------------------------------------------- 1 | import * as jsonfile from 'jsonfile'; 2 | import { root } from './fs'; 3 | import { PackageMetadata, GlobalLibConfig } from './types'; 4 | 5 | export const PKG_METADATA_CACHE: { [dirName: string]: PackageMetadata } = {}; 6 | 7 | export const libConfig: GlobalLibConfig = jsonfile.readFileSync(root('package.json')).libConfig; 8 | 9 | if (libConfig.scope && libConfig.scope.substr(0, 1) !== '@') { 10 | libConfig.scope = '@' + libConfig.scope; 11 | } 12 | 13 | let CURRENT_PACKAGE: PackageMetadata; 14 | 15 | export function currentPackage(value?: PackageMetadata): PackageMetadata { 16 | if (value) { 17 | CURRENT_PACKAGE = value; 18 | } 19 | return CURRENT_PACKAGE; 20 | } 21 | -------------------------------------------------------------------------------- /config/helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | var path = require('path'); 5 | 6 | const EVENT = process.env.npm_lifecycle_event || ''; 7 | 8 | /** 9 | * Helper functions. 10 | */ 11 | var ROOT = path.resolve(__dirname, '..'); 12 | 13 | function hasProcessFlag(flag) { 14 | return process.argv.join('').indexOf(flag) > -1; 15 | } 16 | 17 | function hasNpmFlag(flag) { 18 | return EVENT.includes(flag); 19 | } 20 | 21 | function isWebpackDevServer() { 22 | return process.argv[1] && !! (/webpack-dev-server/.exec(process.argv[1])); 23 | } 24 | 25 | var root = path.join.bind(path, ROOT); 26 | 27 | exports.hasProcessFlag = hasProcessFlag; 28 | exports.hasNpmFlag = hasNpmFlag; 29 | exports.isWebpackDevServer = isWebpackDevServer; 30 | exports.root = root; 31 | -------------------------------------------------------------------------------- /src/lib/src/pipe/item.pipe.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"item.pipe.js","sourceRoot":"","sources":["item.pipe.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAAmD;AAInD,IAAa,QAAQ;IAArB;IAKA,CAAC;IAJC,4BAAS,GAAT,UAAU,KAAuB;QAC/B,0BAA0B;QAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAA,IAAI,IAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IACH,eAAC;AAAD,CAAC,AALD,IAKC;AALY,QAAQ;IADpB,WAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;GACd,QAAQ,CAKpB;AALY,4BAAQ","sourcesContent":["import { Pipe, PipeTransform } from '@angular/core'\r\nimport { SelectableItem } from '../model/selectable-item';\r\n\r\n@Pipe({ name: 'ItemPipe' })\r\nexport class ItemPipe implements PipeTransform {\r\n transform(value: SelectableItem[]) {\r\n // ES6 array destructuring\r\n return value.filter(item => { return item.matchFilter; });\r\n }\r\n}\r\n"]} -------------------------------------------------------------------------------- /src/lib/src/pipe/isVisible.pipe.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"isVisible.pipe.js","sourceRoot":"","sources":["isVisible.pipe.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAAmD;AAInD,IAAa,aAAa;IAA1B;IAKA,CAAC;IAJC,iCAAS,GAAT,UAAU,KAAuB;QAC/B,0BAA0B;QAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAA,IAAI,IAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACH,oBAAC;AAAD,CAAC,AALD,IAKC;AALY,aAAa;IADzB,WAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;GACnB,aAAa,CAKzB;AALY,sCAAa","sourcesContent":["import { Pipe, PipeTransform } from '@angular/core'\r\nimport { SelectableItem } from '../model/selectable-item';\r\n\r\n@Pipe({ name: 'IsVisiblePipe' })\r\nexport class IsVisiblePipe implements PipeTransform {\r\n transform(value: SelectableItem[]) {\r\n // ES6 array destructuring\r\n return value.filter(item => { return item.isVisible; });\r\n }\r\n}\r\n"]} -------------------------------------------------------------------------------- /src/demo/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "/assets/icon/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "/assets/icon/android-icon-48x48.png", 11 | "sizes": "48x48", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "/assets/icon/android-icon-72x72.png", 16 | "sizes": "72x72", 17 | "type": "image/png" 18 | }, 19 | { 20 | "src": "/assets/icon/android-icon-96x96.png", 21 | "sizes": "96x96", 22 | "type": "image/png" 23 | }, 24 | { 25 | "src": "/assets/icon/android-icon-144x144.png", 26 | "sizes": "144x144", 27 | "type": "image/png" 28 | }, 29 | { 30 | "src": "/assets/icon/android-icon-192x192.png", 31 | "sizes": "192x192", 32 | "type": "image/png" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /webpack-debug.js: -------------------------------------------------------------------------------- 1 | // const env = 'production'; 2 | // const flags = [ 'build', 'prod', 'aot' ]; 3 | const env = 'development'; 4 | const flags = []; 5 | 6 | 7 | 8 | const webpack = require("webpack"); 9 | 10 | const isDev = env.indexOf('dev') === 0; 11 | 12 | process.env.npm_lifecycle_event = flags.join(':'); 13 | process.env.NODE_ENV = env; 14 | 15 | const webpackConfig = require(`./config/webpack.${isDev ? 'dev' : 'prod'}`)({env: env}); 16 | 17 | if (isDev) { 18 | webpackConfig.plugins.unshift(new webpack.HotModuleReplacementPlugin()); 19 | } 20 | 21 | function compilerCallback(err, stats) { 22 | if (err) throw err; 23 | } 24 | 25 | const compiler = webpack(webpackConfig); // load webpack 26 | 27 | if (isDev) { 28 | compiler.watch({}, compilerCallback); 29 | } else { 30 | compiler.run(compilerCallback); 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/demo/app/simple-value/simple-value.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FlatCountries } from '../../../datas'; 3 | 4 | @Component({ 5 | selector: 'simple-value-sample', 6 | templateUrl: './simple-value.component.html' 7 | }) 8 | export class SimpleValueComponent { 9 | public items = [ 10 | 'Jacques', 11 | 'Jad', 12 | 'Jana', 13 | 'Jasmine', 14 | 'Jeremie', 15 | 'Jeremy', 16 | 'Joachim', 17 | 'Johan', 18 | 'Johanna', 19 | 'Jonathan', 20 | 'Jordan', 21 | 'Joseph', 22 | 'Jules', 23 | 'Justin' 24 | ]; 25 | 26 | public ShowFilter = true; 27 | public Disabled = false; 28 | public FilterPlaceholder = 'Type here to filter elements...'; 29 | public MaxDisplayed = 5; 30 | 31 | public simpleSelected = 'Jeremy'; 32 | public multipleSelected = ['Jeremy', 'Jordan']; 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig.package.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "target": "es2015", 5 | "module": "es2015", 6 | "moduleResolution": "node", 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "allowSyntheticDefaultImports": true, 10 | "removeComments": false, 11 | "sourceMap": true, 12 | "inlineSources": true, 13 | "strictNullChecks": false, 14 | "lib": [ 15 | "es2015", 16 | "dom" 17 | ], 18 | "typeRoots": [ 19 | "node_modules/@types" 20 | ], 21 | "types": [ 22 | "jasmine", 23 | "hammerjs", 24 | "node" 25 | ], 26 | "noEmit": false, 27 | "declaration": true, 28 | "outDir": "." 29 | }, 30 | "exclude": [ 31 | "scripts", 32 | "node_modules", 33 | "dist", 34 | "dist_package", 35 | "e2e", 36 | "src/**/*.spec.ts", 37 | "src/demo" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /jest.library.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "setupTestFrameworkScriptFile": "/config/setup-jest.ts", 3 | "globals": { 4 | "__TS_CONFIG__": "tsconfig.spec.json", 5 | "__TRANSFORM_HTML__": true 6 | }, 7 | "transform": { 8 | "^.+\\.(ts|js|html)$": "/node_modules/jest-preset-angular/preprocessor.js" 9 | }, 10 | "testRegex": "src/(?!demo).+/.+\\.spec\\.ts$", 11 | "moduleFileExtensions": [ 12 | "ts", 13 | "js", 14 | "html", 15 | "json" 16 | ], 17 | "mapCoverage": true, 18 | "moduleDirectories": [ 19 | "node_modules", 20 | "src" 21 | ], 22 | "testPathIgnorePatterns": [ 23 | "/node_modules/" 24 | ], 25 | "transformIgnorePatterns": [ 26 | "node_modules/(?!@ngrx)" 27 | ], 28 | "moduleNameMapper": { 29 | "^ngx-tree-select$": "/src/ngx-tree-select/src/index.ts", 30 | "^ngx-tree-select/src/(.*)": "/src/ngx-tree-select/src/$1", 31 | "(.*)": "/src/$1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/directives/off-click.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | HostListener, 4 | Inject, 5 | Input, 6 | OnDestroy, 7 | OnInit, 8 | PLATFORM_ID 9 | } from '@angular/core'; 10 | import { isPlatformBrowser } from '@angular/common'; 11 | 12 | @Directive({ 13 | selector: '[off-click]' 14 | }) 15 | export class OffClickDirective implements OnInit, OnDestroy { 16 | /* tslint:disable */ 17 | @Input('off-click') 18 | public offClickHandler: any; 19 | /* tslint:enable */ 20 | 21 | constructor( @Inject(PLATFORM_ID) private platformId: string) { } 22 | 23 | public ngOnInit(): any { 24 | if (isPlatformBrowser(this.platformId)) { 25 | setTimeout(() => { document.addEventListener('click', this.offClickHandler); }, 0); 26 | } 27 | } 28 | 29 | public ngOnDestroy(): any { 30 | if (isPlatformBrowser(this.platformId)) { 31 | document.removeEventListener('click', this.offClickHandler); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/components/tree-select-item.component.scss: -------------------------------------------------------------------------------- 1 | $active-background-color: #428bca; 2 | $hover-background-color: darken($active-background-color, 5); 3 | $text-color: #333; 4 | $active-text-color: #fff; 5 | $hover-text-color: #fff; 6 | 7 | .item { 8 | color: $text-color; 9 | 10 | ul { 11 | list-style-type: none; 12 | } 13 | 14 | &>a { 15 | clear: both; 16 | color: inherit; 17 | display: block; 18 | font-weight: 400; 19 | line-height: 1.42857143; 20 | padding: 3px 20px; 21 | padding-left: 10px; 22 | text-decoration: none; 23 | white-space: nowrap; 24 | 25 | &:hover { 26 | background-color: $hover-background-color; 27 | color: $hover-text-color; 28 | outline: 0; 29 | text-decoration: none; 30 | } 31 | } 32 | 33 | &.active { 34 | background-color: $active-background-color; 35 | color: $active-text-color; 36 | outline: 0; 37 | text-decoration: none; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/demo/app/flat/flat.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { FlatCountries } from '../../../datas'; 3 | 4 | @Component({ 5 | selector: 'flat-sample', 6 | templateUrl: './flat.component.html' 7 | }) 8 | export class FlatComponent { 9 | public items = FlatCountries; 10 | 11 | public ShowFilter = true; 12 | public Disabled = false; 13 | public FilterPlaceholder = 'Type here to filter elements...'; 14 | public MaxDisplayed = 5; 15 | 16 | public simpleSelected = { 17 | id: 'LU', 18 | name: 'Luxembourg', 19 | capital: 'Luxembourg', 20 | phone: '352', 21 | currency: 'EUR' 22 | }; 23 | 24 | public multipleSelected = [ 25 | { 26 | id: 'LU', 27 | name: 'Luxembourg', 28 | capital: 'Luxembourg', 29 | phone: '352', 30 | currency: 'EUR' 31 | }, 32 | { 33 | id: 'FR', 34 | name: 'France', 35 | capital: 'Paris', 36 | phone: '33', 37 | currency: 'EUR' 38 | } 39 | ]; 40 | } 41 | -------------------------------------------------------------------------------- /scripts/gulp/release.ts: -------------------------------------------------------------------------------- 1 | import * as util from '../util'; 2 | 3 | const detect = () => { 4 | const needBump = util.detectVersionBump(); 5 | if (Object.keys(needBump).length > 0) { 6 | util.log('The following packages need a bump: \n' + JSON.stringify(needBump, null, 2)); 7 | } else { 8 | util.log('No bump needed, can publish.'); 9 | } 10 | }; 11 | 12 | @util.GulpClass.Gulpclass() 13 | export class Gulpfile { 14 | 15 | @util.GulpClass.Task({ 16 | name: 'release:detect', 17 | desc: 'Detects which library requires a new version based on the last commit version checkpoint' 18 | }) 19 | releaseDetect() { 20 | detect(); 21 | } 22 | 23 | @util.GulpClass.Task({ 24 | name: 'release:commit', 25 | desc: 'Save the git commit hash for every library. This task should run after a release to mark the last checkpoint then use "release:detect" to find out which library requires a new release.' 26 | }) 27 | releaseCommit() { 28 | detect(); 29 | util.commitVersionBump(); 30 | } 31 | } -------------------------------------------------------------------------------- /src/demo/app/app.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.module.js","sourceRoot":"","sources":["app.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAAyC;AACzC,wCAA6C;AAC7C,8DAA0D;AAC1D,mDAAsD;AAEtD,iDAA+C;AAO/C,IAAa,SAAS;IAAtB;IAAyB,CAAC;IAAD,gBAAC;AAAD,CAAC,AAA1B,IAA0B;AAAb,SAAS;IALrB,eAAQ,CAAC;QACR,OAAO,EAAE,CAAC,gCAAa,EAAE,mBAAW,EAAE,qCAAmB,CAAC,OAAO,CAAC,EAAE,iBAAiB,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC,EAAE,CAAC,CAAC;QACzH,YAAY,EAAE,CAAC,4BAAY,CAAC;QAC5B,SAAS,EAAE,CAAC,4BAAY,CAAC;KAC1B,CAAC;GACW,SAAS,CAAI;AAAb,8BAAS","sourcesContent":["import { NgModule } from '@angular/core';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { BrowserModule } from '@angular/platform-browser';\r\nimport { NgxTreeSelectModule } from 'ngx-tree-select';\r\n\r\nimport { AppComponent } from './app.component';\r\n\r\n@NgModule({\r\n imports: [BrowserModule, FormsModule, NgxTreeSelectModule.forRoot({ filterPlaceholder: 'test', maxVisibleItemCount: 3 })],\r\n declarations: [AppComponent],\r\n bootstrap: [AppComponent]\r\n})\r\nexport class AppModule { }\r\n"]} -------------------------------------------------------------------------------- /src/demo/main.browser.aot.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular bootstrapping 3 | */ 4 | import { platformBrowser } from '@angular/platform-browser'; 5 | import { decorateModuleRef } from './app/environment'; 6 | /** 7 | * App Module 8 | * our top level module that holds all of our components. 9 | */ 10 | import { AppModuleNgFactory } from '../../compiled/src/demo/app/app.module.ngfactory'; 11 | /** 12 | * Bootstrap our Angular app with a top level NgModule. 13 | */ 14 | export function main(): Promise { 15 | return platformBrowser() 16 | .bootstrapModuleFactory(AppModuleNgFactory) 17 | .then(decorateModuleRef) 18 | .catch((err) => console.error(err)); 19 | } 20 | 21 | switch (document.readyState) { 22 | case 'loading': 23 | document.addEventListener('DOMContentLoaded', _domReadyHandler, false); 24 | break; 25 | case 'interactive': 26 | case 'complete': 27 | default: 28 | main(); 29 | } 30 | 31 | function _domReadyHandler() { 32 | document.removeEventListener('DOMContentLoaded', _domReadyHandler, false); 33 | main(); 34 | } 35 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "allowSyntheticDefaultImports": true, 10 | "removeComments": false, 11 | "sourceMap": true, 12 | "inlineSourceMap": true, 13 | "strictNullChecks": false, 14 | "lib": [ 15 | "es2015", 16 | "dom" 17 | ], 18 | "types": [ 19 | "jasmine", 20 | "hammerjs", 21 | "node", 22 | "source-map", 23 | "webpack" 24 | ] 25 | }, 26 | "exclude": [ 27 | "examples/**/*.ts", 28 | "scripts", 29 | "node_modules", 30 | "dist", 31 | "dist_package", 32 | "src/demo" 33 | ], 34 | "awesomeTypescriptLoaderOptions": { 35 | "forkChecker": true, 36 | "useWebpackText": true 37 | }, 38 | "angularCompilerOptions": { 39 | "genDir": "./compiled", 40 | "skipMetadataEmit": true 41 | }, 42 | "compileOnSave": false 43 | } 44 | -------------------------------------------------------------------------------- /src/demo/main.browser.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Angular bootstrapping 3 | */ 4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 5 | import { decorateModuleRef } from './app/environment'; 6 | 7 | /** 8 | * App Module 9 | * our top level module that holds all of our components 10 | */ 11 | import { AppModule } from './app'; 12 | 13 | /** 14 | * Bootstrap our Angular app with a top level NgModule 15 | */ 16 | export function main(): Promise { 17 | return platformBrowserDynamic() 18 | .bootstrapModule(AppModule) 19 | .then(decorateModuleRef) 20 | .catch((err) => console.error(err)); 21 | } 22 | 23 | /** 24 | * Needed for hmr 25 | * in prod this is replace for document ready 26 | */ 27 | switch (document.readyState) { 28 | case 'loading': 29 | document.addEventListener('DOMContentLoaded', _domReadyHandler, false); 30 | break; 31 | case 'interactive': 32 | case 'complete': 33 | default: 34 | main(); 35 | } 36 | 37 | function _domReadyHandler() { 38 | document.removeEventListener('DOMContentLoaded', _domReadyHandler, false); 39 | main(); 40 | } 41 | -------------------------------------------------------------------------------- /src/demo/app/hierarchical/hierarchical.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { HierarchicalCountries } from '../../../datas'; 3 | 4 | @Component({ 5 | selector: 'hierarchical-sample', 6 | templateUrl: './hierarchical.component.html' 7 | }) 8 | export class HierarchicalComponent { 9 | public items = HierarchicalCountries; 10 | 11 | public AllowParentSelection = true; 12 | public RestructureWhenChildSameName = false; 13 | public ShowFilter = true; 14 | public Disabled = false; 15 | public FilterPlaceholder = 'Type here to filter elements...'; 16 | public MaxDisplayed = 5; 17 | 18 | public simpleSelected = { 19 | id: 'LU', 20 | name: 'Luxembourg', 21 | capital: 'Luxembourg', 22 | phone: '352', 23 | currency: 'EUR' 24 | }; 25 | 26 | public multipleSelected = [ 27 | { 28 | id: 'LU', 29 | name: 'Luxembourg', 30 | capital: 'Luxembourg', 31 | phone: '352', 32 | currency: 'EUR' 33 | }, 34 | { 35 | id: 'FR', 36 | name: 'France', 37 | capital: 'Paris', 38 | phone: '33', 39 | currency: 'EUR' 40 | } 41 | ]; 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017 AngularClass LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /scripts/util/gulpclass.ts: -------------------------------------------------------------------------------- 1 | import * as gulpclass from 'gulpclass'; 2 | 3 | export namespace GulpClass { 4 | export interface TaskMetadataArgs { 5 | name?: string; 6 | desc?: string; 7 | dependencies?: string[]; 8 | } 9 | 10 | export const Gulpclass = gulpclass.Gulpclass; 11 | 12 | export function Task(metaArgs?: TaskMetadataArgs | string): Function { 13 | const args = parseMetaArgs(metaArgs); 14 | return gulpclass.Task(args.name, args.dependencies); 15 | } 16 | 17 | export function SequenceTask(metaArgs?: TaskMetadataArgs | string): Function { 18 | const args = parseMetaArgs(metaArgs); 19 | return gulpclass.SequenceTask(args.name); 20 | } 21 | 22 | export function MergedTask(metaArgs?: TaskMetadataArgs | string): Function { 23 | const args = parseMetaArgs(metaArgs); 24 | return gulpclass.MergedTask(args.name); 25 | } 26 | 27 | function parseMetaArgs(metaArgs?: TaskMetadataArgs | string): TaskMetadataArgs { 28 | if (typeof metaArgs === 'string') { 29 | return { name: metaArgs }; 30 | } else if (!metaArgs) { 31 | return {} 32 | } else { 33 | return metaArgs; 34 | } 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { FormsModule } from '@angular/forms'; 3 | import { ItemPipe } from './pipes/item.pipe'; 4 | import { ModuleWithProviders, NgModule } from '@angular/core'; 5 | import { OffClickDirective } from './directives/off-click.directive'; 6 | import { TreeSelectComponent } from './components/tree-select.component'; 7 | import { TreeSelectDefaultOptions } from './models/tree-select-default-options'; 8 | import { TreeSelectItemComponent } from './components/tree-select-item.component'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | FormsModule 14 | ], 15 | declarations: [ 16 | TreeSelectComponent, 17 | TreeSelectItemComponent, 18 | OffClickDirective, 19 | ItemPipe 20 | ], 21 | exports: [ 22 | TreeSelectComponent 23 | ] 24 | }) 25 | 26 | export class NgxTreeSelectModule { 27 | public static forRoot(options: TreeSelectDefaultOptions): ModuleWithProviders { 28 | return { 29 | ngModule: NgxTreeSelectModule, 30 | providers: [ 31 | { provide: TreeSelectDefaultOptions, useValue: options } 32 | ] 33 | }; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/demo/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { AppComponent } from './app.component'; 2 | import { 3 | async, 4 | ComponentFixture, 5 | inject, 6 | TestBed 7 | } from '@angular/core/testing'; 8 | import { NO_ERRORS_SCHEMA } from '@angular/core'; 9 | 10 | /** 11 | * Load the implementations that should be tested 12 | */ 13 | 14 | describe(`App`, () => { 15 | let comp: AppComponent; 16 | let fixture: ComponentFixture; 17 | 18 | /** 19 | * async beforeEach 20 | */ 21 | beforeEach(async(() => { 22 | TestBed.configureTestingModule({ 23 | declarations: [ AppComponent ], 24 | schemas: [NO_ERRORS_SCHEMA] 25 | }) 26 | /** 27 | * Compile template and css 28 | */ 29 | .compileComponents(); 30 | })); 31 | 32 | /** 33 | * Synchronous beforeEach 34 | */ 35 | beforeEach(() => { 36 | fixture = TestBed.createComponent(AppComponent); 37 | comp = fixture.componentInstance; 38 | 39 | /** 40 | * Trigger initial data binding 41 | */ 42 | fixture.detectChanges(); 43 | }); 44 | 45 | it(`should be readly initialized`, () => { 46 | expect(fixture).toBeDefined(); 47 | expect(comp).toBeDefined(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/components/tree-select-item.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 11 | {{item.text}} 12 | {{item.text}} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/demo/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { FormsModule } from '@angular/forms'; 3 | import { RouterModule } from '@angular/router'; 4 | import { BrowserModule } from '@angular/platform-browser'; 5 | import { ExpandMode, NgxTreeSelectModule } from 'ngx-tree-select'; 6 | import { Ng2BootstrapModule } from 'ngx-bootstrap'; 7 | 8 | import { AppComponent } from './app.component'; 9 | import { FlatComponent } from './flat/flat.component'; 10 | import { HierarchicalComponent } from './hierarchical/hierarchical.component'; 11 | import { AppRoutes } from './app.routes'; 12 | import { SimpleValueComponent } from './simple-value/simple-value.component'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | BrowserModule, 17 | FormsModule, 18 | RouterModule.forRoot(AppRoutes), 19 | Ng2BootstrapModule.forRoot(), 20 | NgxTreeSelectModule.forRoot( 21 | { 22 | idField: 'id', 23 | textField: 'name', 24 | expandMode: ExpandMode.Selection 25 | }) 26 | ], 27 | declarations: [ 28 | AppComponent, 29 | FlatComponent, 30 | HierarchicalComponent, 31 | SimpleValueComponent 32 | ], 33 | bootstrap: [AppComponent] 34 | }) 35 | export class AppModule { } 36 | -------------------------------------------------------------------------------- /config/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | require('ts-node/register'); 6 | var helpers = require('./helpers'); 7 | 8 | exports.config = { 9 | baseUrl: 'http://localhost:3000/', 10 | 11 | /** 12 | * Use `npm run e2e` 13 | */ 14 | specs: [ 15 | helpers.root('src/**/**.e2e.ts'), 16 | helpers.root('src/**/*.e2e.ts') 17 | ], 18 | exclude: [], 19 | 20 | framework: 'jasmine2', 21 | 22 | allScriptsTimeout: 110000, 23 | 24 | jasmineNodeOpts: { 25 | showTiming: true, 26 | showColors: true, 27 | isVerbose: false, 28 | includeStackTrace: false, 29 | defaultTimeoutInterval: 400000 30 | }, 31 | directConnect: true, 32 | 33 | capabilities: { 34 | 'browserName': 'chrome', 35 | 'chromeOptions': { 36 | 'args': ['show-fps-counter=true'] 37 | } 38 | }, 39 | 40 | onPrepare: () => { 41 | browser.ignoreSynchronization = true; 42 | }, 43 | 44 | /** 45 | * Angular 2 configuration 46 | * 47 | * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching 48 | * `rootEl` 49 | */ 50 | useAllAngular2AppRoots: true, 51 | 52 | SELENIUM_PROMISE_MANAGER: false, 53 | }; 54 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "experimentalDecorators": true, 7 | "emitDecoratorMetadata": true, 8 | "allowSyntheticDefaultImports": true, 9 | "lib": [ 10 | "es2015", 11 | "dom" 12 | ], 13 | "types": [ 14 | "jasmine", 15 | "hammerjs", 16 | "node", 17 | "source-map", 18 | "uglify-js", 19 | "webpack" 20 | ], 21 | "noEmit": true, 22 | "baseUrl": "./src", 23 | "paths": { 24 | "ngx-tree-select": [ 25 | "ngx-tree-select/src/index.ts" 26 | ], 27 | "ngx-tree-select/src/*": [ 28 | "ngx-tree-select/src/*" 29 | ] 30 | } 31 | }, 32 | "exclude": [ 33 | "examples/**/*.ts", 34 | "scripts/**/*.ts", 35 | "node_modules", 36 | "dist", 37 | "dist_package", 38 | "e2e", 39 | "src/**/*.spec.ts" 40 | ], 41 | "awesomeTypescriptLoaderOptions": { 42 | "forkChecker": true, 43 | "useWebpackText": true 44 | }, 45 | "compileOnSave": false, 46 | "buildOnSave": false, 47 | "atom": { 48 | "rewriteTsconfig": false 49 | }, 50 | "angularCompilerOptions": { 51 | "strictMetadataEmit": false 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /scripts/gulp/misc.ts: -------------------------------------------------------------------------------- 1 | import * as jsonfile from 'jsonfile'; 2 | import * as util from '../util'; 3 | 4 | @util.GulpClass.Gulpclass() 5 | export class Gulpfile { 6 | 7 | @util.GulpClass.Task({ 8 | name: 'misc:syncConfig', 9 | // tslint:disable-next-line:max-line-length 10 | desc: `Sync the main tsconfig file (tsconfig.json) and JEST configuration file with paths information. 11 | This is required after each change to a package configuration that results in a file structure change. 12 | This includes adding, removing or changing a package name. Changing the top-level scope, etc..` 13 | }) 14 | // tslint:disable-next-line:member-access 15 | syncConfig() { 16 | const tsConfig = jsonfile.readFileSync(util.root('tsconfig.json')); 17 | tsConfig.compilerOptions.paths = util.tsConfigPaths(); 18 | util.tryRunHook('./', 'tsconfig', tsConfig); 19 | jsonfile.writeFileSync(util.root('tsconfig.json'), tsConfig, {spaces: 2}); 20 | 21 | const jestConfig = jsonfile.readFileSync(util.root('jest.library.config.json')); 22 | jestConfig.moduleNameMapper = util.jestAlias(); 23 | util.tryRunHook('./', 'jestConfig', jestConfig); 24 | jsonfile.writeFileSync(util.root('jest.library.config.json'), jestConfig, {spaces: 2}); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # @AngularClass 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Users Environment Variables 25 | .lock-wscript 26 | 27 | # OS generated files # 28 | .DS_Store 29 | ehthumbs.db 30 | Icon? 31 | Thumbs.db 32 | 33 | # Node Files # 34 | /node_modules/ 35 | /bower_components/ 36 | npm-debug.log 37 | /npm-debug.log.* 38 | 39 | # Coverage # 40 | /coverage/ 41 | 42 | # Typing # 43 | /src/typings/tsd/ 44 | /typings/ 45 | /tsd_typings/ 46 | 47 | # Dist # 48 | /dist 49 | /public/__build__/ 50 | /src/*/__build__/ 51 | /__build__/** 52 | /public/dist/ 53 | /src/*/dist/ 54 | /dist/** 55 | /.awcache 56 | .webpack.json 57 | /compiled/ 58 | dll/ 59 | 60 | # Doc # 61 | /doc/ 62 | 63 | # IDE # 64 | .idea/ 65 | *.swp 66 | 67 | 68 | # Angular # 69 | *.ngfactory.ts 70 | *.css.shim.ts 71 | *.ngsummary.json 72 | *.shim.ngstyle.ts 73 | 74 | dist_package 75 | .tmp 76 | .tsconfig.*.json 77 | -------------------------------------------------------------------------------- /src/demo/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | Toggle navigation 8 | 9 | 10 | 11 | 12 | Ngx-tree-select 13 | 14 | 15 | 16 | 17 | 18 | Flat 19 | hierarchical 20 | simple value type 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tsconfig.webpack.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "module": "es2015", 6 | "moduleResolution": "node", 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "allowSyntheticDefaultImports": true, 10 | "removeComments": false, 11 | "sourceMap": true, 12 | "inlineSources": true, 13 | "strictNullChecks": false, 14 | "lib": [ 15 | "es2015", 16 | "dom" 17 | ], 18 | "typeRoots": [ 19 | "node_modules/@types" 20 | ], 21 | "types": [ 22 | "jasmine", 23 | "hammerjs", 24 | "node" 25 | ], 26 | "noEmit": false, 27 | "declaration": false, 28 | "outDir": "./dist" 29 | }, 30 | "include": [ 31 | "src/**/*" 32 | ], 33 | "exclude": [ 34 | "scripts/**/*", 35 | "node_modules", 36 | "dist", 37 | "dist_package", 38 | "e2e", 39 | "src/**/*.spec.ts" 40 | ], 41 | "awesomeTypescriptLoaderOptions": { 42 | "forkChecker": true, 43 | "useWebpackText": true 44 | }, 45 | "angularCompilerOptions": { 46 | "genDir": "./compiled", 47 | "skipMetadataEmit": true 48 | }, 49 | "compileOnSave": false, 50 | "buildOnSave": false, 51 | "atom": { 52 | "rewriteTsconfig": false 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/demo/polyfills.browser.ts: -------------------------------------------------------------------------------- 1 | // TODO(gdi2290): switch to DLLs 2 | 3 | /** 4 | * Polyfills 5 | */ 6 | 7 | /* import 'ie-shim'; *//* Internet Explorer 9 support */ 8 | 9 | /* import 'core-js/es6'; */ 10 | 11 | /** 12 | * Added parts of es6 which are necessary for your project or your browser support requirements. 13 | */ 14 | import 'core-js/es6/symbol'; 15 | import 'core-js/es6/object'; 16 | import 'core-js/es6/function'; 17 | import 'core-js/es6/parse-int'; 18 | import 'core-js/es6/parse-float'; 19 | import 'core-js/es6/number'; 20 | import 'core-js/es6/math'; 21 | import 'core-js/es6/string'; 22 | import 'core-js/es6/date'; 23 | import 'core-js/es6/array'; 24 | import 'core-js/es6/regexp'; 25 | import 'core-js/es6/map'; 26 | import 'core-js/es6/set'; 27 | import 'core-js/es6/weak-map'; 28 | import 'core-js/es6/weak-set'; 29 | import 'core-js/es6/typed'; 30 | import 'core-js/es6/reflect'; 31 | /** 32 | * See issue https://github.com/AngularClass/angular2-webpack-starter/issues/709 33 | */ 34 | /* import 'core-js/es6/promise'; */ 35 | 36 | import 'core-js/es7/reflect'; 37 | import 'zone.js/dist/zone'; 38 | 39 | if ('production' === ENV) { 40 | // Production 41 | 42 | } else { 43 | 44 | // Development 45 | Error.stackTraceLimit = Infinity; 46 | 47 | /* tslint:disable no-var-requires */ 48 | require('zone.js/dist/long-stack-trace-zone'); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Chrome against localhost, with sourcemaps", 6 | "type": "chrome", 7 | "request": "launch", 8 | "url": "http://localhost:3000/*", 9 | "runtimeArgs": [ 10 | "--disable-web-security", 11 | "--user-data-dir", 12 | "--remote-debugging-port=9222" 13 | ], 14 | "sourceMaps": true, 15 | "webRoot": "${workspaceRoot}" 16 | }, 17 | { 18 | "name": "Attach to Chrome, with sourcemaps", 19 | "type": "chrome", 20 | "request": "attach", 21 | "url": "http://localhost:3000/*", 22 | "port": 9222, 23 | "sourceMaps": true, 24 | "webRoot": "${workspaceRoot}" 25 | }, 26 | { 27 | "name": "tests", 28 | "type": "node", 29 | "request": "launch", 30 | "program": "${workspaceRoot}/node_modules/jest/bin/jest.js", 31 | "stopOnEntry": false, 32 | "args": [ 33 | "--runInBand", 34 | "--config", 35 | "jest.library.config.json" 36 | ], 37 | "cwd": "${workspaceRoot}", 38 | "preLaunchTask": null, 39 | "console": "internalConsole" 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: node_js 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | addons: 10 | chrome: stable 11 | 12 | node_js: 13 | - "6" 14 | - "8" 15 | - "10" 16 | 17 | matrix: 18 | fast_finish: true 19 | 20 | before_install: 21 | - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc; fi' 22 | - npm prune 23 | - npm update 24 | - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost & 25 | 26 | install: 27 | - npm install 28 | 29 | 30 | before_script: 31 | - npm rebuild node-sass 32 | - ./node_modules/protractor/bin/webdriver-manager update --gecko false 33 | - export DISPLAY=:99.0 34 | - sh -e /etc/init.d/xvfb start 35 | - sleep 3 36 | 37 | script: 38 | - npm run lib:sync 39 | - npm run lib:build 40 | - npm run lib:test 41 | - npm run lint 42 | - npm run test 43 | - npm run build:aot 44 | - npm run e2e:travis 45 | - npm run github-deploy:prod 46 | 47 | deploy: 48 | - provider: pages 49 | skip_cleanup: true 50 | local_dir: dist 51 | github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard 52 | on: 53 | branch: master 54 | node: "10" 55 | - provider: script 56 | skip_cleanup: true 57 | script: npm run travis-publish 58 | on: 59 | branch: master 60 | node: "10" 61 | -------------------------------------------------------------------------------- /src/lib/src/component/tree-select.component.css: -------------------------------------------------------------------------------- 1 | :host.ng-invalid:not(.ng-pristine) span.form-control, 2 | :host.ng-invalid:not(.ng-untouched) span.form-control { 3 | border-color: #a94442; 4 | } 5 | 6 | .input-group > .dropdown { 7 | position: static; 8 | } 9 | 10 | .disabled > span { 11 | background-color: #eceeef; 12 | cursor: not-allowed; 13 | } 14 | 15 | .disabled .btn { 16 | padding-right: 5px; 17 | } 18 | 19 | ul { 20 | height: auto; 21 | list-style-type: none; 22 | margin-top: 0; 23 | max-height: 200px; 24 | overflow-x: hidden; 25 | width: 100%; 26 | } 27 | 28 | .selected-container-text { 29 | padding-left: 7px; 30 | } 31 | 32 | .selected-container-item { 33 | padding-left: 2px; 34 | } 35 | 36 | .selected-item-text { 37 | font-size: 14px; 38 | margin: 3px; 39 | } 40 | 41 | .selected-item-item { 42 | font-size: 14px; 43 | margin: 2px; 44 | outline: 0; 45 | } 46 | 47 | .more-items-icon { 48 | opacity: 0.5; 49 | height: 10px; 50 | margin-top: -2px; 51 | position: absolute; 52 | right: 20px; 53 | top: 30%; 54 | } 55 | 56 | .close { 57 | font-size: 18px; 58 | line-height: .75; 59 | margin-left: 5px; 60 | padding-top: 3px; 61 | position: absolute; 62 | } 63 | 64 | .caret { 65 | height: 10px; 66 | margin-top: -2px; 67 | position: absolute; 68 | right: 10px; 69 | top: 50%; 70 | } 71 | 72 | .btn { 73 | display: table; 74 | padding-right: 20px; 75 | } 76 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/models/select-option.ts: -------------------------------------------------------------------------------- 1 | import { ExpandMode } from './expand-mode'; 2 | 3 | export class SelectOption { 4 | public idProperty = 'id'; 5 | public textProperty = 'text'; 6 | public childProperty: string = null; 7 | public allowMultiple = false; 8 | public closeOnSelection = true; 9 | public items: any[] = []; 10 | public model: any[] | any; 11 | public isOpen = false; 12 | public filter = ''; 13 | public filterCaseSensitive = false; 14 | public allowParentSelection = false; 15 | public restructureWhenChildSameName = false; 16 | public maxVisibleItemCount: number; 17 | public expandMode = ExpandMode.None; 18 | 19 | public isHierarchy(): boolean { 20 | return this.childProperty && this.childProperty.trim().length > 0; 21 | } 22 | 23 | public get filterExpandMode(): ExpandMode { 24 | if (this.filter !== '') { 25 | return ExpandMode.All; 26 | } else { 27 | return this.expandMode; 28 | } 29 | } 30 | 31 | public displayCheckbox(): boolean { 32 | return this.allowMultiple && this.isHierarchy(); 33 | } 34 | 35 | public isValid(): boolean { 36 | // Check id property value 37 | return this.idProperty && this.idProperty.trim().length > 0 && 38 | // Check text property value 39 | this.textProperty && this.textProperty.trim().length > 0 && 40 | // Check items value 41 | this.items && Array.isArray(this.items) && this.items.length > 0; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /scripts/gulp/manifest.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | import * as Path from 'path'; 3 | import * as gulp from 'gulp'; 4 | import * as jsonfile from 'jsonfile'; 5 | 6 | import * as util from '../util'; 7 | 8 | const INTERNAL_PKGJSON_KEYS = [ 9 | 'libConfig', 10 | 'libExtensions' 11 | ]; 12 | 13 | const PKGJSON_KEYS_TO_DELETE = [ 14 | 'scripts', 15 | 'devDependencies' 16 | ].concat(INTERNAL_PKGJSON_KEYS); 17 | 18 | gulp.task('!manifest', function () { 19 | const meta = util.currentPackage(); 20 | const copyInst = util.getCopyInstruction(meta); 21 | 22 | const pkgDest = Path.join(copyInst.to, 'package.json'); 23 | const pkgJson = jsonfile.readFileSync(util.root('package.json')); 24 | 25 | const localPackageJsonPath = util.root(util.FS_REF.SRC_CONTAINER, meta.dir, 'package.json'); 26 | if (fs.existsSync(localPackageJsonPath)) { 27 | Object.assign( 28 | pkgJson, 29 | jsonfile.readFileSync(localPackageJsonPath) 30 | ) 31 | } 32 | 33 | PKGJSON_KEYS_TO_DELETE.forEach( k => { delete pkgJson[k] }); 34 | 35 | pkgJson.name = meta.dir; 36 | pkgJson.main = `${util.FS_REF.BUNDLE_DIR}/${meta.umd}.rollup.umd.js`; 37 | pkgJson.module = `${util.FS_REF.BUNDLE_DIR}/${meta.umd}.es5.js`; 38 | pkgJson.es2015 = `${util.FS_REF.BUNDLE_DIR}/${meta.umd}.js`; 39 | pkgJson.typings = `${util.FS_REF.SRC_CONTAINER}/${util.getMainOutputFileName(meta)}.d.ts`; 40 | 41 | util.tryRunHook(meta.dir, 'packageJSON', pkgJson); 42 | 43 | jsonfile.writeFileSync(pkgDest, pkgJson, {spaces: 2}); 44 | }); -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Usage (given build times depend on machine): 2 | # 3 | # Build SMALL image (no cache; ~20MB, time for build=rebuild = ~360s): 4 | # docker build --squash="true" -t angular-starter . 5 | # 6 | # Build FAST (rebuild) image (cache; >280MB, build time ~360s, rebuild time ~80s): 7 | # docker build -t angular-starter . 8 | # 9 | # Clean (remove intermidiet images): 10 | # docker rmi -f $(docker images -f "dangling=true" -q) 11 | # 12 | # Run image (on localhost:8080): 13 | # docker run --name angular-starter -p 8080:80 angular-starter & 14 | # 15 | # Run image as virtual host (read more: https://github.com/jwilder/nginx-proxy): 16 | # docker run -e VIRTUAL_HOST=angular-starter.your-domain.com --name angular-starter angular-starter & 17 | 18 | FROM nginx:1.13.0-alpine 19 | 20 | # install console and node 21 | RUN apk add --no-cache bash=4.3.46-r5 &&\ 22 | apk add --no-cache openssl=1.0.2k-r0 &&\ 23 | apk add --no-cache nodejs 24 | 25 | # install npm ( in separate dir due to docker cache) 26 | ADD package.json /tmp/npm_inst/package.json 27 | RUN cd /tmp/npm_inst &&\ 28 | npm install &&\ 29 | mkdir -p /tmp/app &&\ 30 | mv /tmp/npm_inst/node_modules /tmp/app/ 31 | 32 | # build and publish application 33 | ADD . /tmp/app 34 | RUN cd /tmp/app &&\ 35 | npm run build:aot &&\ 36 | mv ./dist/* /usr/share/nginx/html/ 37 | 38 | # clean 39 | RUN rm -Rf /tmp/npm_inst &&\ 40 | rm -Rf /tmp/app &&\ 41 | rm -Rf /root/.npm &&\ 42 | apk del nodejs 43 | 44 | # this is for virtual host purposes 45 | EXPOSE 80 46 | -------------------------------------------------------------------------------- /src/demo/app/environment.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationRef, enableProdMode } from '@angular/core'; 2 | import { disableDebugTools, enableDebugTools } from '@angular/platform-browser'; 3 | /** 4 | * Angular 2 5 | */ 6 | /** 7 | * Environment Providers 8 | */ 9 | let PROVIDERS: any[] = [ 10 | /** 11 | * Common env directives 12 | */ 13 | ]; 14 | 15 | /** 16 | * Angular debug tools in the dev console 17 | * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md 18 | */ 19 | let _decorateModuleRef = (value: T): T => value; 20 | 21 | if ('production' === ENV) { 22 | enableProdMode(); 23 | 24 | /** 25 | * Production 26 | */ 27 | _decorateModuleRef = (modRef: any) => { 28 | disableDebugTools(); 29 | 30 | return modRef; 31 | }; 32 | 33 | PROVIDERS = [ 34 | ...PROVIDERS, 35 | /** 36 | * Custom providers in production. 37 | */ 38 | ]; 39 | 40 | } else { 41 | 42 | _decorateModuleRef = (modRef: any) => { 43 | const appRef = modRef.injector.get(ApplicationRef); 44 | const cmpRef = appRef.components[0]; 45 | 46 | const _ng = (window as any).ng; 47 | enableDebugTools(cmpRef); 48 | (window as any).ng.probe = _ng.probe; 49 | (window as any).ng.coreTokens = _ng.coreTokens; 50 | return modRef; 51 | }; 52 | 53 | /** 54 | * Development 55 | */ 56 | PROVIDERS = [ 57 | ...PROVIDERS, 58 | /** 59 | * Custom providers in development. 60 | */ 61 | ]; 62 | 63 | } 64 | 65 | export const decorateModuleRef = _decorateModuleRef; 66 | 67 | export const ENV_PROVIDERS = [ 68 | ...PROVIDERS 69 | ]; 70 | -------------------------------------------------------------------------------- /scripts/util/hooks.ts: -------------------------------------------------------------------------------- 1 | import { FS_REF, root } from './fs'; 2 | 3 | export type HOOKS = 'rollupUMD' | 'rollupFESM' | 'packageJSON' | 'tsconfig' | 'jestConfig'; 4 | const constraints: { [hook: string]: 'global' | 'local' } = { 5 | jestConfig: 'global' 6 | }; 7 | 8 | const runHook = (hookScope: 'global' | 'local', hookName: HOOKS, hookArgs: any[], path: string) => { 9 | if (!constraints[hookName] || constraints[hookName] === hookScope) { 10 | // tslint:disable-next-line:max-line-length 11 | const logPrefix = `[${new Date().toTimeString().split(' ')[0]}] ${hookScope === 'global' ? 'Root ' : 'Local'} : `; 12 | try { 13 | const moduleId = require.resolve(path); 14 | 15 | try { 16 | const module = require(moduleId); 17 | if (typeof module[hookName] === 'function') { 18 | module[hookName](...hookArgs); 19 | console.log(`${logPrefix}Hook for ${hookName} applied`); 20 | } 21 | } catch (err) { 22 | // tslint:disable-next-line:max-line-length 23 | console.log(`${logPrefix}Hook for ${hookName} cannot be applied, probably caused by a syntax error.${err}`); 24 | } 25 | } catch (err) { 26 | // tslint:disable-next-line:max-line-length 27 | console.log(`${logPrefix}Hook for ${hookName} cannot be applied, probably caused by no specific hooks.${err}`); 28 | } 29 | } 30 | }; 31 | 32 | export function tryRunHook(pkgDir: string, 33 | hookName: HOOKS, 34 | ...args: any[]): void { 35 | 36 | runHook('global', hookName, args, root('build_hooks')); 37 | runHook('local', hookName, args, root(FS_REF.SRC_CONTAINER, pkgDir, 'build_hooks')); 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/src/directive/off-click.directive.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"off-click.directive.js","sourceRoot":"","sources":["off-click.directive.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,sCAAuG;AACvG,0CAAoD;AAKpD,IAAa,iBAAiB;IAI5B,mBAAmB;IAEnB,2BAA0C,UAAkB;QAAlB,eAAU,GAAV,UAAU,CAAQ;IAAI,CAAC;IAE1D,oCAAQ,GAAf;QAAA,iBAIC;QAHC,EAAE,CAAC,CAAC,0BAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACvC,UAAU,CAAC,cAAQ,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IAEM,uCAAW,GAAlB;QACE,EAAE,CAAC,CAAC,0BAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACvC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IACH,wBAAC;AAAD,CAAC,AAnBD,IAmBC;AAhBC;IADC,YAAK,CAAC,eAAe,CAAC;;0DACK;AAHjB,iBAAiB;IAH7B,gBAAS,CAAC;QACT,QAAQ,EAAE,iBAAiB;KAC5B,CAAC;IAOc,WAAA,aAAM,CAAC,kBAAW,CAAC,CAAA;;GANtB,iBAAiB,CAmB7B;AAnBY,8CAAiB","sourcesContent":["import { Directive, HostListener, Input, OnInit, OnDestroy, PLATFORM_ID, Inject } from '@angular/core';\r\nimport { isPlatformBrowser } from '@angular/common';\r\n\r\n@Directive({\r\n selector: '[cra-off-click]'\r\n})\r\nexport class OffClickDirective implements OnInit, OnDestroy {\r\n /* tslint:disable */\r\n @Input('cra-off-click')\r\n public offClickHandler: any;\r\n /* tslint:enable */\r\n\r\n constructor( @Inject(PLATFORM_ID) private platformId: string) { }\r\n\r\n public ngOnInit(): any {\r\n if (isPlatformBrowser(this.platformId)) {\r\n setTimeout(() => { document.addEventListener('click', this.offClickHandler); }, 0);\r\n }\r\n }\r\n\r\n public ngOnDestroy(): any {\r\n if (isPlatformBrowser(this.platformId)) {\r\n document.removeEventListener('click', this.offClickHandler);\r\n }\r\n }\r\n}\r\n"]} -------------------------------------------------------------------------------- /scripts/util/fs.ts: -------------------------------------------------------------------------------- 1 | import * as Path from 'path'; 2 | import * as del from 'del'; 3 | import * as jsonfile from 'jsonfile'; 4 | 5 | export const ROOT = Path.resolve(__dirname, '../..'); 6 | 7 | export function root(...args: string[]): string { 8 | return Path.join(ROOT, ...args); 9 | } 10 | 11 | export const FS_REF = { 12 | PKG_DIST: 'dist_package', 13 | BUNDLE_DIR: 'bundle', 14 | NG_COMPILE: 'compiled', 15 | WEBPACK_CONFIG: 'config/webpack.package.ts', 16 | TS_CONFIG_TEMPLATE: 'tsconfig.package.json', 17 | TS_CONFIG_TMP: '.tsconfig.tmp.json', 18 | SRC_CONTAINER: 'src', 19 | NG_FLAT_MODULE_EXT: '.ng-flat', 20 | TEMP_DIR: '.tmp', 21 | VERSION_CACHE: 'version_cache.json' 22 | }; 23 | 24 | export function cleanup(): Promise { 25 | return del([root(FS_REF.TEMP_DIR), root(FS_REF.TS_CONFIG_TMP)]); 26 | } 27 | 28 | /** 29 | * Loads a json, updates via handler, and saves. 30 | * A mix of fluent and curry API. 31 | * 32 | * The handler sent to 'update' must returns an object to be used to save. 33 | * If the returned value is undefined the original object is used, if its null save is aborted. 34 | * @param readPath 35 | * @return {any} 36 | */ 37 | export function jsonPatch(readPath: string): { update: (handler: (data: T) => (T | null | void)) => { save: (savePath: string) => void } } { 38 | return { 39 | update(handler: (data: T) => T): any { 40 | const tsconfig = jsonfile.readFileSync(readPath); 41 | const data = handler(tsconfig); 42 | return { 43 | save(savePath: string): void { 44 | if (data !== null) { 45 | jsonfile.writeFileSync(savePath, data || tsconfig, {spaces: 2}); 46 | } 47 | } 48 | }; 49 | } 50 | }; 51 | } -------------------------------------------------------------------------------- /scripts/util/release_mgmt.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | import { execSync as spawn } from 'child_process'; 3 | 4 | import { CommitVersion } from './types'; 5 | import { root, FS_REF } from './fs'; 6 | import { libConfig } from './state'; 7 | 8 | import { getPackageRoot } from './util'; 9 | 10 | 11 | 12 | export function getLastCommit(dirName: string): string { 13 | // we check the internal 'src' since other changes (e.g. test folder) does not effect version. 14 | return spawn(`git log -n 1 --pretty=format:%H ${getPackageRoot(dirName, FS_REF.SRC_CONTAINER)}`).toString(); 15 | } 16 | 17 | export function createLibVersionMap(): { [pkgName: string]: CommitVersion } { 18 | return libConfig.packages 19 | .reduce( (versionCache, dirName) => { 20 | const version = fs.readJsonSync(getPackageRoot(dirName, 'package.json')).version; 21 | versionCache[dirName] = { 22 | commit: getLastCommit(dirName), 23 | version 24 | }; 25 | return versionCache; 26 | }, {}); 27 | } 28 | 29 | export function detectVersionBump(): { [pkgName: string]: CommitVersion } { 30 | const currState = createLibVersionMap(); 31 | 32 | try { 33 | const lastKnownState = fs.readJsonSync(root(FS_REF.VERSION_CACHE)); 34 | 35 | Object.keys(currState).forEach( pkg => { 36 | const last = lastKnownState[pkg]; 37 | if (last) { 38 | const curr = currState[pkg]; 39 | if (last.commit === curr.commit && last.version === curr.version) { 40 | delete currState[pkg]; 41 | } 42 | } 43 | }); 44 | 45 | } catch (err) { } 46 | 47 | 48 | return currState; 49 | } 50 | 51 | export function commitVersionBump(): void { 52 | fs.writeJsonSync(root(FS_REF.VERSION_CACHE), createLibVersionMap()); 53 | } 54 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/components/tree-select.component.scss: -------------------------------------------------------------------------------- 1 | $disable-background-color: #eceeef; 2 | $error-border-color: #a94442; 3 | 4 | :host.ng-invalid:not(.ng-pristine) span.form-control, 5 | :host.ng-invalid:not(.ng-untouched) span.form-control 6 | { 7 | border-color: $error-border-color; 8 | } 9 | 10 | // Fix Bootstrap dropdown position when inside a input-group 11 | .input-group>.dropdown { 12 | //Instead of relative 13 | position: static; 14 | } 15 | 16 | .disabled { 17 | &>span { 18 | background-color: $disable-background-color; 19 | cursor: not-allowed; 20 | } 21 | 22 | .btn { 23 | border-color: #ccc; 24 | } 25 | } 26 | 27 | .enabled { 28 | &>span { 29 | cursor: context-menu; 30 | } 31 | 32 | } 33 | 34 | ul { 35 | height: auto; 36 | list-style-type: none; 37 | margin-top: 0; 38 | max-height: 200px; 39 | overflow-x: hidden; 40 | width: 100%; 41 | } 42 | 43 | .selected-container-text { 44 | padding-left: 7px; 45 | } 46 | 47 | .selected-container-item { 48 | padding-left: 2px; 49 | } 50 | 51 | .selected-item-text { 52 | font-size: 14px; 53 | margin: 3px; 54 | } 55 | 56 | .selected-item-item { 57 | font-size: 14px; 58 | margin: 2px; 59 | outline: 0; 60 | } 61 | 62 | .more-items-icon { 63 | bottom: 23px; 64 | height: 10px; 65 | opacity: .5; 66 | position: absolute; 67 | right: 20px; 68 | z-index: 100; 69 | } 70 | 71 | .close { 72 | font-size: 18px; 73 | line-height: .75; 74 | margin-left: 5px; 75 | padding-top: 3px; 76 | position: absolute; 77 | z-index: 50; 78 | } 79 | 80 | .caret { 81 | height: 10px; 82 | margin-top: -2px; 83 | position: absolute; 84 | right: 10px; 85 | top: 50%; 86 | } 87 | 88 | .btn { 89 | display: table; 90 | padding-right: 20px; 91 | } 92 | -------------------------------------------------------------------------------- /scripts/util/rollup.ts: -------------------------------------------------------------------------------- 1 | // mostly taken from https://github.com/angular/material2/blob/master/tools/gulp/packaging/rollup-helpers.ts 2 | import * as rollup from 'rollup'; 3 | import * as resolve from 'rollup-plugin-node-resolve'; 4 | import * as commonjs from 'rollup-plugin-commonjs'; 5 | 6 | export interface BundleConfig { 7 | entry: string; 8 | dest: string; 9 | format: string; 10 | moduleName: string; 11 | external?: string[]; 12 | globals?: { [key: string]: string }; 13 | } 14 | 15 | /** 16 | * Creates a rollup bundle of a specified JavaScript file. 17 | */ 18 | export function createRollupBundle(config: BundleConfig): Promise { 19 | const bundleOptions: any = { 20 | context: 'this', 21 | external: config.external, 22 | entry: config.entry, 23 | }; 24 | 25 | const writeOptions = { 26 | // Keep the moduleId empty because we don't want to force developers to a specific moduleId. 27 | moduleId: '', 28 | moduleName: config.moduleName, 29 | // banner: buildConfig.licenseBanner, 30 | format: config.format, 31 | dest: config.dest, 32 | globals: config.globals, 33 | sourceMap: true 34 | }; 35 | 36 | // When creating a UMD, we want to exclude tslib from the `external` bundle option so that it 37 | // is inlined into the bundle. 38 | if (config.format === 'umd') { 39 | bundleOptions.plugins = [ 40 | resolve(), 41 | commonjs({ 42 | include: ['node_modules/rxjs/**'] 43 | }) 44 | ]; 45 | 46 | if (bundleOptions.external && bundleOptions.external.indexOf('tslib') > -1) { 47 | bundleOptions.external.splice(bundleOptions.external.indexOf('tslib'), 1); 48 | } 49 | } 50 | 51 | return rollup.rollup(bundleOptions).then((bundle: any) => bundle.write(writeOptions)); 52 | } 53 | -------------------------------------------------------------------------------- /scripts/util/pure-annotations.ts: -------------------------------------------------------------------------------- 1 | // taken from https://github.com/angular/material2/blob/master/tools/gulp/packaging/pure-annotations.ts 2 | 3 | import {readFileSync, writeFileSync} from 'fs'; 4 | 5 | /** Regex that matches downleveled class IIFE expressions. Used to add the pure annotations. */ 6 | const classIfeeRegex = 7 | new RegExp('^(var (\\S+) = )(\\(function \\(\\) \\{[\\n\\r]*(?: (?:\\/\\*\\*| \\*|\\*\\/|' + 8 | '\\/\\/)[^\\n\\r]*[\\n\\r]*)* function \\2\\([^\\)]*\\) \\{[\\n\\r]*)', 'mg'); 9 | 10 | /** Regex that matches downleveled class IIFE expressions with _extends statements */ 11 | const classExtendsIfeeRegex = 12 | /^(var (\S+) = )(\(function \(_super\) {[\n\r]* tslib.*\.__extends\(\2, _super\);[\n\r]*)/gm; 13 | 14 | /** 15 | * Adds `@__PURE__` annotation comments to IIFEs containing ES5-downleveled classes generated by 16 | * TypeScript so that Uglify can tree-shake classes that are not referenced. 17 | * 18 | * @param fileContent The content of the file for which `@__PURE__` will be added. 19 | * @returns The content of the file with `@__PURE__` annotations added. 20 | */ 21 | export function addPureAnnotations(fileContent: string) { 22 | return fileContent 23 | // Prefix downleveled classes w/ the @__PURE__ annotation. 24 | .replace(classIfeeRegex, '$1/*@__PURE__*/$3') 25 | // Prefix downleveled classes that extend another class w/ the @__PURE__ annotation 26 | .replace(classExtendsIfeeRegex, '$1/*@__PURE__*/$3'); 27 | } 28 | 29 | /** Adds Uglify "@__PURE__" decorations to the specified file. */ 30 | export function addPureAnnotationsToFile(inputFile: string) { 31 | const originalContent = readFileSync(inputFile, 'utf-8'); 32 | const annotatedContent = addPureAnnotations(originalContent); 33 | 34 | writeFileSync(inputFile, annotatedContent, 'utf-8'); 35 | } -------------------------------------------------------------------------------- /src/lib/src/module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"module.js","sourceRoot":"","sources":["module.ts"],"names":[],"mappings":";;;;;;;;AAAA,mFAA+E;AAC/E,sCAA8D;AAC9D,0CAA+C;AAC/C,wCAA6C;AAE7C,2EAAwE;AACxE,qFAAiF;AACjF,uEAAoE;AACpE,8CAA4C;AAC5C,wDAAsD;AAmBtD,IAAa,mBAAmB;IAAhC;IAUA,CAAC;IATQ,2BAAO,GAAd,UAAe,OAAiC;QAC9C,MAAM,CAAC;YACL,QAAQ,EAAE,qBAAmB;YAC7B,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,sDAAwB,EAAE,QAAQ,EAAE,OAAO,EAAE;aACzD;SACF,CAAC;IACJ,CAAC;IAEH,0BAAC;AAAD,CAAC,AAVD,IAUC;AAVY,mBAAmB;IAjB/B,eAAQ,CAAC;QACR,OAAO,EAAE;YACP,qBAAY;YACZ,mBAAW;SACZ;QACD,YAAY,EAAE;YACZ,2CAAmB;YACnB,oDAAuB;YACvB,uCAAiB;YACjB,oBAAQ;YACR,8BAAa;SACd;QACD,OAAO,EAAE;YACP,2CAAmB;SACpB;KACF,CAAC;GAEW,mBAAmB,CAU/B;AAVY,kDAAmB","sourcesContent":["import { TreeSelectDefaultOptions } from './model/tree-select-default-options';\r\nimport { NgModule, ModuleWithProviders } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { FormsModule } from '@angular/forms';\r\n\r\nimport { TreeSelectComponent } from './component/tree-select.component';\r\nimport { TreeSelectItemComponent } from './component/tree-select-item.component';\r\nimport { OffClickDirective } from './directive/off-click.directive';\r\nimport { ItemPipe } from './pipe/item.pipe';\r\nimport { IsVisiblePipe } from './pipe/isVisible.pipe';\r\n\r\n@NgModule({\r\n imports: [\r\n CommonModule,\r\n FormsModule\r\n ],\r\n declarations: [\r\n TreeSelectComponent,\r\n TreeSelectItemComponent,\r\n OffClickDirective,\r\n ItemPipe,\r\n IsVisiblePipe\r\n ],\r\n exports: [\r\n TreeSelectComponent\r\n ]\r\n})\r\n\r\nexport class NgxTreeSelectModule {\r\n static forRoot(options: TreeSelectDefaultOptions): ModuleWithProviders {\r\n return {\r\n ngModule: NgxTreeSelectModule,\r\n providers: [\r\n { provide: TreeSelectDefaultOptions, useValue: options }\r\n ]\r\n };\r\n }\r\n\r\n}\r\n"]} -------------------------------------------------------------------------------- /src/ngx-tree-select/src/components/tree-select.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | {{placeholder}} 8 | 9 | 11 | {{itm.text}} 12 | x 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | (...) 21 | 22 | 23 | 24 | 25 | 0" class="dropdown-menu" role="menu"> 26 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/lib/src/model/SelectOption.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"SelectOption.js","sourceRoot":"","sources":["SelectOption.ts"],"names":[],"mappings":";;AAAA;IAAA;QACS,eAAU,GAAG,IAAI,CAAC;QAClB,iBAAY,GAAG,MAAM,CAAC;QACtB,kBAAa,GAAW,IAAI,CAAC;QAC7B,kBAAa,GAAG,KAAK,CAAC;QACtB,qBAAgB,GAAG,IAAI,CAAC;QACxB,UAAK,GAAU,EAAE,CAAC;QAElB,WAAM,GAAG,KAAK,CAAC;QACf,WAAM,GAAG,EAAE,CAAC;QACZ,qBAAgB,GAAG,KAAK,CAAC;QAEzB,iBAAY,GAAG,KAAK,CAAC;IAmB9B,CAAC;IAjBQ,kCAAW,GAAlB;QACE,MAAM,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;IACpE,CAAC;IAEM,sCAAe,GAAtB;QACE,MAAM,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;IAClD,CAAC;IAEM,8BAAO,GAAd;QACE,MAAM,CAAC,IAAI,CAAC,UAAU;YACpB,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YACjC,IAAI,CAAC,YAAY;YACjB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YACnC,IAAI,CAAC,KAAK;YACV,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;eACtB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,CAAC;IACH,mBAAC;AAAD,CAAC,AA/BD,IA+BC;AA/BY,oCAAY","sourcesContent":["export class SelectOption {\r\n public idProperty = 'id';\r\n public textProperty = 'text';\r\n public childProperty: string = null;\r\n public allowMultiple = false;\r\n public closeOnSelection = true;\r\n public items: any[] = [];\r\n public model: any[] | any;\r\n public isOpen = false;\r\n public filter = '';\r\n public onlySelectParent = false;\r\n public maxVisibleItemCount: number;\r\n public loadAllItems = false;\r\n\r\n public isHierarchy(): boolean {\r\n return this.childProperty && this.childProperty.trim().length > 0;\r\n }\r\n\r\n public displayCheckbox(): boolean {\r\n return this.allowMultiple && this.isHierarchy();\r\n }\r\n\r\n public isValid(): boolean {\r\n return this.idProperty &&\r\n this.idProperty.trim().length > 0 &&\r\n this.textProperty &&\r\n this.textProperty.trim().length > 0 &&\r\n this.items &&\r\n Array.isArray(this.items)\r\n && this.items.length > 0;\r\n }\r\n}\r\n"]} -------------------------------------------------------------------------------- /scripts/util/typescript-transpile.ts: -------------------------------------------------------------------------------- 1 | // taken from https://github.com/angular/material2/blob/master/tools/gulp/packaging/typescript-transpile.ts 2 | 3 | import * as ts from 'typescript'; 4 | import * as path from 'path'; 5 | import * as fs from 'fs'; 6 | import * as chalk from 'chalk'; 7 | 8 | /** Reads a input file and transpiles it into a new file. */ 9 | export function transpileFile(inputPath: string, outputPath: string, options: ts.CompilerOptions) { 10 | const inputFile = fs.readFileSync(inputPath, 'utf-8'); 11 | const transpiled = ts.transpileModule(inputFile, { 12 | fileName: path.basename(outputPath), 13 | compilerOptions: options 14 | }); 15 | 16 | reportDiagnostics(transpiled.diagnostics); 17 | 18 | fs.writeFileSync(outputPath, transpiled.outputText); 19 | 20 | if (transpiled.sourceMapText) { 21 | fs.writeFileSync(`${outputPath}.map`, transpiled.sourceMapText); 22 | } 23 | } 24 | 25 | /** Formats the TypeScript diagnostics into a error string. */ 26 | function formatDiagnostics(diagnostics: ts.Diagnostic[], baseDir: string): string { 27 | return diagnostics.map(diagnostic => { 28 | let res = `• ${chalk.red(`TS${diagnostic.code}`)} - `; 29 | 30 | if (diagnostic.file) { 31 | const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); 32 | const filePath = path.relative(baseDir, diagnostic.file.fileName); 33 | 34 | res += `${filePath}(${line + 1},${character + 1}): `; 35 | } 36 | res += `${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`; 37 | 38 | return res; 39 | }).join('\n'); 40 | } 41 | 42 | /** Checks and reports diagnostics if present. */ 43 | function reportDiagnostics(diagnostics: ts.Diagnostic[], baseDir?: string) { 44 | if (diagnostics && diagnostics.length && diagnostics[0]) { 45 | console.error(formatDiagnostics(diagnostics, baseDir)); 46 | throw new Error('TypeScript compilation failed.'); 47 | } 48 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended" 4 | ], 5 | "rulesDirectory": [ 6 | "node_modules/codelyzer" 7 | ], 8 | "rules": { 9 | // Custom 10 | "trailing-comma": [false, {"multiline": "always", "singleline": "never"}], 11 | "interface-name": [false, "always-prefix"], 12 | // Angular 2 13 | "component-class-suffix": true, 14 | // "component-selector": [true, "element", "my", "kebab-case"], 15 | "directive-class-suffix": true, 16 | // "directive-selector": [true, "attribute", "my", "camelCase"], 17 | "import-destructuring-spacing": true, 18 | "invoke-injectable": true, 19 | "no-access-missing-member": true, 20 | "no-attribute-parameter-decorator": true, 21 | "no-forward-ref": true, 22 | "no-input-rename": true, 23 | "no-namespace": [true, "allow-declarations"], 24 | "no-output-rename": true, 25 | "pipe-naming": [true, "camelCase", ""], 26 | "templates-use-public": true, 27 | "use-host-property-decorator": true, 28 | "use-input-property-decorator": true, 29 | "use-life-cycle-interface": true, 30 | "use-output-property-decorator": true, 31 | "use-pipe-transform-interface": true, 32 | // General 33 | "no-console": [true, 34 | "time", 35 | "timeEnd", 36 | "trace" 37 | ], 38 | "max-line-length": [ 39 | true, 40 | 100 41 | ], 42 | "no-string-literal": false, 43 | "no-use-before-declare": true, 44 | "object-literal-sort-keys": false, 45 | "ordered-imports": [ 46 | true, 47 | { 48 | "import-sources-order": "any", 49 | "named-imports-order": "case-insensitive" 50 | } 51 | ], 52 | "quotemark": [ 53 | true, 54 | "single", 55 | "avoid-escape" 56 | ], 57 | "variable-name": [ 58 | true, 59 | "allow-leading-underscore", 60 | "allow-pascal-case", 61 | "ban-keywords", 62 | "check-format" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/models/selectable-item.ts: -------------------------------------------------------------------------------- 1 | import { SelectService } from '../services/select.service'; 2 | 3 | export class SelectableItem { 4 | public _selected = false; 5 | public children?: SelectableItem[]; 6 | public isOpen = false; 7 | public matchFilter = true; 8 | public isVisible = false; 9 | 10 | constructor(public id: string, public text: string, public data: any, public svc: SelectService) { 11 | } 12 | 13 | get hasChild(): boolean { 14 | return this.children && this.children.length > 0; 15 | } 16 | get checked(): boolean { 17 | if (this.hasChild && this.svc.Configuration.allowMultiple) { 18 | if (this.children.every((child) => child.selected)) { 19 | return true; 20 | } else if (this.children.every((child) => child.selected === false)) { 21 | return this._selected; 22 | } 23 | return null; 24 | } 25 | return this._selected; 26 | } 27 | get selected(): boolean { 28 | if (this.hasChild && this.svc.Configuration.allowMultiple) { 29 | if (this.children.some((child) => child.selected)) { 30 | if (this.svc.Configuration.allowParentSelection) { 31 | this._selected = false; 32 | } 33 | return true; 34 | } else if (this.children.every((child) => child.selected === false)) { 35 | if (this.svc.Configuration.allowParentSelection) { 36 | return this._selected; 37 | } else { 38 | return this._selected = false; 39 | } 40 | } 41 | return false; 42 | } else if (this.hasChild && this._selected === true) { 43 | for (const itm of this.children) { 44 | itm.selected = false; 45 | } 46 | } 47 | 48 | return this._selected; 49 | } 50 | set selected(value: boolean) { 51 | if (this.hasChild && !this.svc.Configuration.allowParentSelection) { 52 | if (value !== null) { 53 | this.children.forEach((child) => child.selected = value); 54 | } 55 | } else { 56 | this._selected = value; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | <% if (webpackConfig.htmlElements.headTags) { %> 13 | 14 | <%= webpackConfig.htmlElements.headTags %> 15 | <% } %> 16 | 17 | 18 | <%= htmlWebpackPlugin.files.webpackManifest %> 19 | 20 | <% if (htmlWebpackPlugin.options.metadata.isDevServer && htmlWebpackPlugin.options.metadata.HMR !== true) { %> 21 | 22 | 23 | <% } %> 24 | 25 | 26 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Loading... 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/ngx-tree-select/src/components/tree-select-item.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | EventEmitter, 4 | Input, 5 | Output 6 | } from '@angular/core'; 7 | import { SelectableItem } from '../models/selectable-item'; 8 | import { SelectService } from '../services/select.service'; 9 | 10 | @Component({ 11 | selector: 'tree-select-item', 12 | templateUrl: './tree-select-item.component.html', 13 | styleUrls: ['./tree-select-item.component.scss'] 14 | }) 15 | export class TreeSelectItemComponent { 16 | public get isOpen() { 17 | return this.item.isOpen; 18 | } 19 | 20 | @Input() public onTouchedCallBack: () => void; 21 | 22 | @Input() 23 | public item: SelectableItem; 24 | 25 | public constructor( 26 | private svc: SelectService 27 | ) { } 28 | 29 | public toggleOpen($event: any) { 30 | $event.stopPropagation(); 31 | if (this.haveChildren) { 32 | this.item.isOpen = !this.item.isOpen; 33 | } else { 34 | this.select($event); 35 | } 36 | } 37 | 38 | get allowParentSelection(): boolean { 39 | return this.svc.Configuration.allowParentSelection; 40 | } 41 | 42 | get restructureWhenChildSameName(): boolean { 43 | return this.svc.Configuration.restructureWhenChildSameName; 44 | } 45 | 46 | get needCheckBox(): boolean { 47 | return this.svc.Configuration.isHierarchy() && this.svc.Configuration.allowMultiple; 48 | } 49 | 50 | public get haveChildren(): boolean { 51 | if (this.restructureWhenChildSameName && this.item && this.item.children 52 | && this.item.children.length === 1 && this.item.text === this.item.children[0].text) { 53 | this.item = this.item.children[0]; 54 | } 55 | return this.item && this.item.children && this.item.children.length > 0; 56 | } 57 | 58 | public select($event: any): void { 59 | $event.stopPropagation(); 60 | if (this.svc.Configuration.allowMultiple || 61 | !this.haveChildren || 62 | this.svc.Configuration.allowParentSelection) { 63 | this.svc.toggleItemSelection(this.item); 64 | } 65 | this.onTouchedCallBack(); 66 | } 67 | 68 | public get filter(): string { 69 | return this.svc.Configuration.filter; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /config/spec-bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | /** 6 | * When testing with webpack and ES6, we have to do some extra 7 | * things to get testing to work right. Because we are gonna write tests 8 | * in ES6 too, we have to compile those as well. That's handled in 9 | * karma.conf.js with the karma-webpack plugin. This is the entry 10 | * file for webpack test. Just like webpack will create a bundle.js 11 | * file for our client, when we run test, it will compile and bundle them 12 | * all here! Crazy huh. So we need to do some setup 13 | */ 14 | Error.stackTraceLimit = Infinity; 15 | 16 | require('core-js/es6'); 17 | require('core-js/es7/reflect'); 18 | 19 | require('zone.js/dist/zone'); 20 | require('zone.js/dist/long-stack-trace-zone'); 21 | require('zone.js/dist/proxy'); // since zone.js 0.6.15 22 | require('zone.js/dist/sync-test'); 23 | require('zone.js/dist/jasmine-patch'); // put here since zone.js 0.6.14 24 | require('zone.js/dist/async-test'); 25 | require('zone.js/dist/fake-async-test'); 26 | 27 | /** 28 | * RxJS 29 | */ 30 | require('rxjs/Rx'); 31 | 32 | var testing = require('@angular/core/testing'); 33 | var browser = require('@angular/platform-browser-dynamic/testing'); 34 | 35 | testing.TestBed.initTestEnvironment( 36 | browser.BrowserDynamicTestingModule, 37 | browser.platformBrowserDynamicTesting() 38 | ); 39 | 40 | /** 41 | * Ok, this is kinda crazy. We can use the context method on 42 | * require that webpack created in order to tell webpack 43 | * what files we actually want to require or import. 44 | * Below, context will be a function/object with file names as keys. 45 | * Using that regex we are saying look in ../src then find 46 | * any file that ends with spec.ts and get its path. By passing in true 47 | * we say do this recursively 48 | */ 49 | var testContext = require.context('../src/demo', true, /\.spec\.ts/); 50 | 51 | /** 52 | * Get all the files, for each file, call the context function 53 | * that will require the file and load it up here. Context will 54 | * loop and require those spec files here 55 | */ 56 | function requireAll(requireContext) { 57 | return requireContext.keys().map(requireContext); 58 | } 59 | 60 | /** 61 | * Requires and returns all modules that match 62 | */ 63 | var modules = requireAll(testContext); 64 | -------------------------------------------------------------------------------- /scripts/util/metadata-inlining.ts: -------------------------------------------------------------------------------- 1 | // taken from https://github.com/angular/material2/blob/master/tools/gulp/packaging/metadata-inlining.ts 2 | import { readFileSync, writeFileSync, existsSync } from 'fs'; 3 | import { basename } from 'path'; 4 | import { sync as glob } from 'glob'; 5 | import { join } from 'path'; 6 | 7 | /** 8 | * Recurse through a parsed metadata.json file and inline all html and css. 9 | * Note: this assumes that all html and css files have a unique name. 10 | */ 11 | export function inlineMetadataResources(metadata: any, componentResources: Map) { 12 | // Convert `templateUrl` to `template` 13 | if (metadata.templateUrl) { 14 | const fullResourcePath = componentResources.get(basename(metadata.templateUrl)); 15 | metadata.template = readFileSync(fullResourcePath, 'utf-8'); 16 | delete metadata.templateUrl; 17 | } 18 | 19 | // Convert `styleUrls` to `styles` 20 | if (metadata.styleUrls && metadata.styleUrls.length) { 21 | metadata.styles = []; 22 | for (const styleUrl of metadata.styleUrls) { 23 | const fullResourcePath = componentResources.get(basename(styleUrl)); 24 | if (fullResourcePath && existsSync(fullResourcePath)) { 25 | metadata.styles.push(readFileSync(fullResourcePath, 'utf-8')); 26 | } 27 | } 28 | delete metadata.styleUrls; 29 | } 30 | 31 | // We we did nothing at this node, go deeper. 32 | if (!metadata.template && !metadata.styles) { 33 | for (const property in metadata) { 34 | if (typeof metadata[property] == 'object' && metadata[property]) { 35 | inlineMetadataResources(metadata[property], componentResources); 36 | } 37 | } 38 | } 39 | } 40 | 41 | 42 | /** Inlines HTML and CSS resources into `metadata.json` files. */ 43 | export function inlinePackageMetadataFiles(packagePath: string) { 44 | // Create a map of fileName -> fullFilePath. This is needed because the templateUrl and 45 | // styleUrls for each component use just the filename because, in the source, the component 46 | // and the resources live in the same directory. 47 | const componentResources = new Map(); 48 | 49 | glob(join(packagePath, '**/*.+(html|css|scss)')).forEach(resourcePath => { 50 | componentResources.set(basename(resourcePath), resourcePath); 51 | }); 52 | 53 | // Find all metadata files. For each one, parse the JSON content, inline the resources, and 54 | // reserialize and rewrite back to the original location. 55 | glob(join(packagePath, '**/*.metadata.json')).forEach(path => { 56 | const metadata = JSON.parse(readFileSync(path, 'utf-8')); 57 | inlineMetadataResources(metadata, componentResources); 58 | writeFileSync(path, JSON.stringify(metadata), 'utf-8'); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /config/head-config.common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration for head elements added during the creation of index.html. 3 | * 4 | * All href attributes are added the publicPath (if exists) by default. 5 | * You can explicitly hint to prefix a publicPath by setting a boolean value to a key that has 6 | * the same name as the attribute you want to operate on, but prefix with = 7 | * 8 | * Example: 9 | * { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true }, 10 | * Will prefix the publicPath to content. 11 | * 12 | * { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png', '=href': false }, 13 | * Will not prefix the publicPath on href (href attributes are added by default 14 | * 15 | */ 16 | module.exports = { 17 | link: [ 18 | /** 19 | * tags for 'apple-touch-icon' (AKA Web Clips). 20 | */ 21 | { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png' }, 22 | { rel: 'apple-touch-icon', sizes: '60x60', href: '/assets/icon/apple-icon-60x60.png' }, 23 | { rel: 'apple-touch-icon', sizes: '72x72', href: '/assets/icon/apple-icon-72x72.png' }, 24 | { rel: 'apple-touch-icon', sizes: '76x76', href: '/assets/icon/apple-icon-76x76.png' }, 25 | { rel: 'apple-touch-icon', sizes: '114x114', href: '/assets/icon/apple-icon-114x114.png' }, 26 | { rel: 'apple-touch-icon', sizes: '120x120', href: '/assets/icon/apple-icon-120x120.png' }, 27 | { rel: 'apple-touch-icon', sizes: '144x144', href: '/assets/icon/apple-icon-144x144.png' }, 28 | { rel: 'apple-touch-icon', sizes: '152x152', href: '/assets/icon/apple-icon-152x152.png' }, 29 | { rel: 'apple-touch-icon', sizes: '180x180', href: '/assets/icon/apple-icon-180x180.png' }, 30 | 31 | /** 32 | * tags for android web app icons 33 | */ 34 | { rel: 'icon', type: 'image/png', sizes: '192x192', href: '/assets/icon/android-icon-192x192.png' }, 35 | 36 | /** 37 | * tags for favicons 38 | */ 39 | { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/assets/icon/favicon-32x32.png' }, 40 | { rel: 'icon', type: 'image/png', sizes: '96x96', href: '/assets/icon/favicon-96x96.png' }, 41 | { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/assets/icon/favicon-16x16.png' }, 42 | 43 | /** 44 | * tags for a Web App Manifest 45 | */ 46 | { rel: 'manifest', href: '/assets/manifest.json' } 47 | ], 48 | meta: [ 49 | { name: 'msapplication-TileColor', content: '#00bcd4' }, 50 | { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true }, 51 | { name: 'theme-color', content: '#00bcd4' }, 52 | { name: "application-name", content: "ngx-tree-select" }, 53 | { name: 'subject', content: 'Angular 4 component for select with tree items' }, 54 | { name: 'robots', content: 'index,follow,noodp' }, 55 | { name: 'googlebot', content: 'index,follow' }, 56 | { name: '', content: '' }, 57 | { name: '', content: '' }, 58 | ] 59 | }; 60 | -------------------------------------------------------------------------------- /config/github-deploy/index.js: -------------------------------------------------------------------------------- 1 | const execSync = require('child_process').execSync; 2 | /** 3 | * Used to merge webpack configs. 4 | */ 5 | const webpackMerge = require('webpack-merge'); // used to merge webpack configs 6 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 7 | const helpers = require('../helpers'); 8 | 9 | const REPO_NAME_RE = /Push {2}URL: ((git@github\.com:)|(https:\/\/github\.com\/)).+\/(.+)\.git/; 10 | 11 | function getWebpackConfigModule(options) { 12 | if (options.githubDev) { 13 | return require('../webpack.dev.js'); 14 | } else if (options.githubProd) { 15 | return require('../webpack.prod.js'); 16 | } else { 17 | throw new Error('Invalid compile option.'); 18 | } 19 | } 20 | 21 | function getRepoName(remoteName) { 22 | remoteName = remoteName || 'origin'; 23 | 24 | var stdout = execSync('git remote show ' + remoteName), 25 | match = REPO_NAME_RE.exec(stdout); 26 | 27 | if (!match) { 28 | throw new Error('Could not find a repository on remote ' + remoteName); 29 | } else { 30 | return match[4]; 31 | } 32 | } 33 | 34 | function stripTrailing(str, char) { 35 | 36 | if (str[0] === char) { 37 | str = str.substr(1); 38 | } 39 | 40 | if (str.substr(-1) === char) { 41 | str = str.substr(0, str.length - 1); 42 | } 43 | 44 | return str; 45 | } 46 | 47 | /** 48 | * Given a string remove trailing slashes and adds 1 slash at the end of the string. 49 | * 50 | * Example: 51 | * safeUrl('/value/') 52 | * // 'value/' 53 | * 54 | * @param url 55 | * @returns {string} 56 | */ 57 | function safeUrl(url) { 58 | const stripped = stripTrailing(url || '', '/'); 59 | return stripped ? stripped + '/' : ''; 60 | } 61 | 62 | function replaceHtmlWebpackPlugin(plugins, ghRepoName) { 63 | for (var i=0; i { console.log(msg); }, 51 | remote: GIT_REMOTE_NAME, 52 | message: COMMIT_MESSAGE, 53 | 54 | dotfiles: true // for .nojekyll 55 | }; 56 | /** 57 | * Since GitHub moved to Jekyll 3.3, their server ignores the "node_modules" and "vendors" folder by default. 58 | * but, as of now, it also ignores "vendors*" files. 59 | * This means vendor.bundle.js or vendor.[chunk].bundle.js will return 404. 60 | * this is the fix for now. 61 | */ 62 | fs.writeFileSync(path.join(webpackConfig.output.path, '.nojekyll'), ''); 63 | console.log(webpackConfig.output.path); 64 | const ghpages = require('gh-pages'); 65 | /*ghpages.publish(webpackConfig.output.path, options, function(err) { 66 | if (err) { 67 | console.log('GitHub deployment done. STATUS: ERROR. (' + err + ')'); 68 | throw err; 69 | } else { 70 | console.log('GitHub deployment done. STATUS: SUCCESS.'); 71 | } 72 | });*/ 73 | }); 74 | } 75 | ] 76 | }); 77 | }; 78 | -------------------------------------------------------------------------------- /src/demo/app/app.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.component.js","sourceRoot":"","sources":["app.component.ts"],"names":[],"mappings":";;;;;;;;AAAA,sCAA0C;AAC1C,mEAAsE;AAMtE,IAAa,YAAY;IAJzB;QAMS,mBAAc,GAAQ,IAAI,CAAC;QAE3B,mBAAc,GAAU;YAC7B,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;SAE1B,CAAC;QAEK,mBAAc,GAAQ;YAC3B,EAAE,EAAE,IAAI;YACR,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,YAAY;YACrB,KAAK,EAAE,KAAK;YACZ,QAAQ,EAAE,KAAK;SAChB,CAAC;QAEK,mBAAc,GAAU;YAC7B;gBACE,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,YAAY;gBACrB,KAAK,EAAE,KAAK;gBACZ,QAAQ,EAAE,KAAK;aAChB;YACD;gBACE,EAAE,EAAE,IAAI;gBACR,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,OAAO;gBAChB,KAAK,EAAE,IAAI;gBACX,QAAQ,EAAE,KAAK;aAChB;SACF,CAAC;QAEK,2BAAsB,GAAQ,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAExD,2BAAsB,GAAU;YACrC,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC1B,CAAC;QAEK,2BAAsB,GAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAE3D,2BAAsB,GAAU;YACrC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;YAC5B,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;YAC5B,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE;SAC7B,CAAC;QAEF,UAAK,GAAG;YACN,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC1B,CAAC;QAEF,cAAS,GAAG,yCAAqB,CAAC;IACpC,CAAC;IAAD,mBAAC;AAAD,CAAC,AA3DD,IA2DC;AA3DY,YAAY;IAJxB,gBAAS,CAAC;QACT,QAAQ,EAAE,UAAU;QACpB,WAAW,EAAE,sBAAsB;KACpC,CAAC;GACW,YAAY,CA2DxB;AA3DY,oCAAY","sourcesContent":["import { Component } from '@angular/core';\r\nimport { HierarchicalCountries } from '../../datas/hierarchical-data';\r\n\r\n@Component({\r\n selector: 'demo-app',\r\n templateUrl: './app.component.html'\r\n})\r\nexport class AppComponent {\r\n\r\n public selectedItems1: any = null;\r\n\r\n public selectedItems2: any[] = [\r\n { id: 2, text: 'item 2' },\r\n { id: 3, text: 'item 3' },\r\n\r\n ];\r\n\r\n public selectedItems3: any = {\r\n id: 'LU',\r\n name: 'Luxembourg',\r\n capital: 'Luxembourg',\r\n phone: '352',\r\n currency: 'EUR'\r\n };\r\n\r\n public selectedItems4: any[] = [\r\n {\r\n id: 'LU',\r\n name: 'Luxembourg',\r\n capital: 'Luxembourg',\r\n phone: '352',\r\n currency: 'EUR'\r\n },\r\n {\r\n id: 'FR',\r\n name: 'France',\r\n capital: 'Paris',\r\n phone: '33',\r\n currency: 'EUR'\r\n }\r\n ];\r\n\r\n public disabledSelectedItems1: any = { id: 1, text: 'item 1' };\r\n\r\n public disabledSelectedItems2: any[] = [\r\n { id: 1, text: 'item 1' },\r\n { id: 2, text: 'item 2' }\r\n ];\r\n\r\n public disabledSelectedItems3: any = { id: 11, text: 'item 1.1' };\r\n\r\n public disabledSelectedItems4: any[] = [\r\n { id: 21, text: 'item 2.1' },\r\n { id: 22, text: 'item 2.2' },\r\n { id: 23, text: 'item 2.3' }\r\n ];\r\n\r\n items = [\r\n { id: 1, text: 'item 1' },\r\n { id: 2, text: 'item 2' },\r\n { id: 3, text: 'item 3' },\r\n { id: 4, text: 'item 4' },\r\n { id: 5, text: 'item 5' },\r\n ];\r\n\r\n itemsTree = HierarchicalCountries;\r\n}\r\n"]} -------------------------------------------------------------------------------- /scripts/util/inline-resources.ts: -------------------------------------------------------------------------------- 1 | // taken from https://github.com/angular/material2/blob/master/tools/gulp/packaging/inline-resources.ts 2 | /* tslint:disable:no-eval */ 3 | 4 | import { dirname, join } from 'path'; 5 | import { readFileSync, writeFileSync, existsSync } from 'fs'; 6 | import { sync as glob } from 'glob'; 7 | import * as del from 'del'; 8 | 9 | /** Finds all JavaScript files in a directory and inlines all resources of Angular components. */ 10 | export function inlineResourcesForDirectory(folderPath: string, deleteResource: boolean = false) { 11 | glob(join(folderPath, '**/*.js')).forEach(filePath => inlineResources(filePath, deleteResource)); 12 | } 13 | 14 | /** Inlines the external resources of Angular components of a file. */ 15 | export function inlineResources(filePath: string, deleteResource: boolean = false) { 16 | let fileContent = readFileSync(filePath, 'utf-8'); 17 | 18 | fileContent = inlineTemplate(fileContent, filePath, deleteResource); 19 | fileContent = inlineStyles(fileContent, filePath, deleteResource); 20 | fileContent = removeModuleId(fileContent); 21 | 22 | writeFileSync(filePath, fileContent, 'utf-8'); 23 | } 24 | 25 | /** Inlines the templates of Angular components for a specified source file. */ 26 | function inlineTemplate(fileContent: string, filePath: string, deleteResource: boolean = false) { 27 | return fileContent.replace(/templateUrl:\s*'([^']+?\.html)'/g, (_match, templateUrl) => { 28 | const templatePath = join(dirname(filePath), templateUrl); 29 | const templateContent = loadResourceFile(templatePath) || ''; 30 | 31 | if (deleteResource === true) { 32 | del.sync(templatePath); 33 | } 34 | 35 | return `template: "${templateContent}"`; 36 | }); 37 | } 38 | 39 | /** Inlines the external styles of Angular components for a specified source file. */ 40 | function inlineStyles(fileContent: string, filePath: string, deleteResource: boolean = false) { 41 | return fileContent.replace(/styleUrls:\s*(\[[\s\S]*?])/gm, (_match, styleUrlsValue) => { 42 | // The RegExp matches the array of external style files. This is a string right now and 43 | // can to be parsed using the `eval` method. The value looks like "['AAA.css', 'BBB.css']" 44 | const styleUrls = eval(styleUrlsValue) as string[]; 45 | 46 | const styleContents = styleUrls 47 | .map(url => join(dirname(filePath), url)) 48 | .map(path => { 49 | const styleContent = loadResourceFile(path) || ''; 50 | if (deleteResource === true) { 51 | del.sync(path); 52 | } 53 | return styleContent; 54 | }); 55 | 56 | return `styles: ["${styleContents.join(' ')}"]`; 57 | }); 58 | } 59 | 60 | /** Remove every mention of `moduleId: module.id` */ 61 | function removeModuleId(fileContent: string) { 62 | return fileContent.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, ''); 63 | } 64 | 65 | /** Loads the specified resource file and drops line-breaks of the content. */ 66 | function loadResourceFile(filePath: string): string { 67 | if (existsSync(filePath)) { 68 | return readFileSync(filePath, 'utf-8') 69 | .replace(/([\n\r]\s*)+/gm, ' ') 70 | .replace(/"/g, '\\"'); 71 | } else { 72 | return null; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/lib/src/component/tree-select-item.component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tree-select-item.component.js","sourceRoot":"","sources":["tree-select-item.component.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,sCAAuE;AACvE,4DAA0D;AAC1D,4DAA0D;AAO1D,IAAa,uBAAuB;IAUlC,iCACU,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IACxB,CAAC;IAXL,sBAAW,2CAAM;aAAjB;YACE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QAC1B,CAAC;;;OAAA;IAWD,4CAAU,GAAV,UAAW,MAAW;QACpB,MAAM,CAAC,eAAe,EAAE,CAAC;QACzB,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QACvC,CAAC;QAAC,IAAI,CAAC,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,sBAAI,qDAAgB;aAApB;YACE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAC;QACjD,CAAC;;;OAAA;IAED,sBAAI,iDAAY;aAAhB;YACE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,aAAa,CAAC;QACtF,CAAC;;;OAAA;IAED,sBAAW,iDAAY;aAAvB;YACE,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1E,CAAC;;;OAAA;IAEM,wCAAM,GAAb,UAAc,MAAW;QACvB,MAAM,CAAC,eAAe,EAAE,CAAC;QACzB,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAC1G,IAAI,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED,sBAAW,2CAAM;aAAjB;YACE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC;QACvC,CAAC;;;OAAA;IACH,8BAAC;AAAD,CAAC,AA9CD,IA8CC;AAzCU;IAAR,YAAK,EAAE;;kEAA+B;AAGvC;IADC,YAAK,EAAE;8BACK,gCAAc;qDAAC;AARjB,uBAAuB;IALnC,gBAAS,CAAC;QACT,QAAQ,EAAE,kBAAkB;QAC5B,WAAW,EAAE,mCAAmC;QAChD,SAAS,EAAE,CAAC,kCAAkC,CAAC;KAChD,CAAC;qCAYe,8BAAa;GAXjB,uBAAuB,CA8CnC;AA9CY,0DAAuB","sourcesContent":["import { Component, Input, Output, EventEmitter } from '@angular/core';\r\nimport { SelectableItem } from '../model/selectable-item';\r\nimport { SelectService } from '../service/select.service';\r\n\r\n@Component({\r\n selector: 'tree-select-item',\r\n templateUrl: './tree-select-item.component.html',\r\n styleUrls: ['./tree-select-item.component.css']\r\n})\r\nexport class TreeSelectItemComponent {\r\n public get isOpen() {\r\n return this.item.isOpen;\r\n }\r\n\r\n @Input() onTouchedCallBack: () => void;\r\n\r\n @Input()\r\n public item: SelectableItem;\r\n\r\n public constructor(\r\n private svc: SelectService\r\n ) { }\r\n\r\n toggleOpen($event: any) {\r\n $event.stopPropagation();\r\n if (this.haveChildren) {\r\n this.item.isOpen = !this.item.isOpen;\r\n } else {\r\n this.select($event);\r\n }\r\n }\r\n\r\n get onlySelectParent(): boolean {\r\n return this.svc.Configuration.onlySelectParent;\r\n }\r\n\r\n get needCheckBox(): boolean {\r\n return this.svc.Configuration.isHierarchy() && this.svc.Configuration.allowMultiple;\r\n }\r\n\r\n public get haveChildren(): boolean {\r\n return this.item && this.item.children && this.item.children.length > 0;\r\n }\r\n\r\n public select($event: any): void {\r\n $event.stopPropagation();\r\n if (this.svc.Configuration.allowMultiple || !this.haveChildren || this.svc.Configuration.onlySelectParent) {\r\n this.svc.toggleItemSelection(this.item);\r\n }\r\n this.onTouchedCallBack();\r\n }\r\n\r\n public get filter(): string {\r\n return this.svc.Configuration.filter;\r\n }\r\n}\r\n"]} -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at crazyht24@hotmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/demo/app/flat/flat.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exemple ngx-tree-select with flat datas 4 | 5 | 6 | Simple select 7 | 8 | 9 | 11 | 12 | Simple select is required 13 | 14 | 15 | 16 | {{simpleSelected | json}} 17 | 18 | 19 | 20 | 21 | Multiple select 22 | 23 | 24 | 28 | 29 | 30 | Multiple select is required 31 | You must choose at least 2 items on Multiple select 32 | You must choose maximum 4 items on Multiple select 33 | 34 | 35 | 36 | {{itm | json}} 37 | 38 | 39 | 40 | 41 | Options 42 | 43 | 44 | 45 | Max elements displayed 46 | 47 | 48 | 49 | Filter 50 | 52 | 53 | 54 | 55 | Show filter 56 | 57 | 58 | 59 | 60 | Disabled 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/demo/app/simple-value/simple-value.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exemple ngx-tree-select with flat datas 4 | 5 | 6 | Simple select 7 | 8 | 9 | 11 | 12 | Simple select is required 13 | 14 | 15 | 16 | {{simpleSelected}} 17 | 18 | 19 | 20 | 21 | Multiple select 22 | 23 | 24 | 28 | 29 | 30 | Multiple select is required 31 | You must choose at least 2 items on Multiple select 32 | You must choose maximum 4 items on Multiple select 33 | 34 | 35 | 36 | {{itm}} 37 | 38 | 39 | 40 | 41 | Options 42 | 43 | 44 | 45 | Max elements displayed 46 | 47 | 48 | 49 | Filter 50 | 52 | 53 | 54 | 55 | Show filter 56 | 57 | 58 | 59 | 60 | Disabled 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/demo/app/hierarchical/hierarchical.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exemple ngx-tree-select with flat datas 4 | 5 | 6 | Simple select 7 | 8 | 9 | 12 | 13 | Simple select is required 14 | 15 | 16 | 17 | {{simpleSelected | json}} 18 | 19 | 20 | 21 | 22 | Multiple select 23 | 24 | 25 | 29 | 30 | 31 | Multiple select is required 32 | You must choose at least 2 items on Multiple select 33 | You must choose maximum 4 items on Multiple select 34 | 35 | 36 | 37 | {{itm | json}} 38 | 39 | 40 | 41 | 42 | Options 43 | 44 | 45 | 46 | Max elements displayed 47 | 48 | 49 | 50 | Filter 51 | 53 | 54 | 55 | 56 | Show filter 57 | 58 | 59 | 60 | 61 | Allow select parent items 62 | 63 | 64 | 65 | 66 | Disabled 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /config/html-elements-plugin/index.js: -------------------------------------------------------------------------------- 1 | function HtmlElementsPlugin(locations) { 2 | this.locations = locations; 3 | } 4 | 5 | const RE_ENDS_WITH_BS = /\/$/; 6 | 7 | /** 8 | * Create an HTML tag with attributes from a map. 9 | * 10 | * Example: 11 | * createTag('link', { rel: "manifest", href: "/assets/manifest.json" }) 12 | * // 13 | * @param tagName The name of the tag 14 | * @param attrMap A Map of attribute names (keys) and their values. 15 | * @param publicPath a path to add to eh start of static asset url 16 | * @returns {string} 17 | */ 18 | function createTag(tagName, attrMap, publicPath) { 19 | publicPath = publicPath || ''; 20 | 21 | /** 22 | * Add trailing slash if we have a publicPath and it doesn't have one. 23 | */ 24 | if (publicPath && !RE_ENDS_WITH_BS.test(publicPath)) { 25 | publicPath += '/'; 26 | } 27 | 28 | const attributes = Object.getOwnPropertyNames(attrMap) 29 | .filter((name) => name[0] !== '=') 30 | .map((name) => { 31 | var value = attrMap[name]; 32 | 33 | if (publicPath) { 34 | /** 35 | * Check if we have explicit instruction, use it if so (e.g: =herf: false) 36 | * if no instruction, use public path if it's href attribute. 37 | */ 38 | const usePublicPath = attrMap.hasOwnProperty('=' + name) ? !!attrMap['=' + name] : name === 'href'; 39 | 40 | if (usePublicPath) { 41 | /** 42 | * Remove a starting trailing slash if the value has one so we wont have // 43 | */ 44 | value = publicPath + (value[0] === '/' ? value.substr(1) : value); 45 | } 46 | } 47 | 48 | return `${name}="${value}"`; 49 | }); 50 | 51 | const closingTag = tagName === 'script' ? '' : ''; 52 | 53 | return `<${tagName} ${attributes.join(' ')}>${closingTag}`; 54 | } 55 | 56 | /** 57 | * Returns a string representing all html elements defined in a data source. 58 | * 59 | * Example: 60 | * 61 | * const ds = { 62 | * link: [ 63 | * { rel: "apple-touch-icon", sizes: "57x57", href: "/assets/icon/apple-icon-57x57.png" } 64 | * ], 65 | * meta: [ 66 | * { name: "msapplication-TileColor", content: "#00bcd4" } 67 | * ] 68 | * } 69 | * 70 | * getHeadTags(ds); 71 | * // "" 72 | * "" 73 | * 74 | * @returns {string} 75 | */ 76 | function getHtmlElementString(dataSource, publicPath) { 77 | return Object.getOwnPropertyNames(dataSource) 78 | .map(function(name) { 79 | if (Array.isArray(dataSource[name])) { 80 | return dataSource[name].map(function(attrs) { return createTag(name, attrs, publicPath); } ); 81 | } else { 82 | return [ createTag(name, dataSource[name], publicPath) ]; 83 | } 84 | }) 85 | .reduce(function(arr, curr) { 86 | return arr.concat(curr); 87 | }, []) 88 | .join('\n\t'); 89 | } 90 | 91 | HtmlElementsPlugin.prototype.apply = function (compiler) { 92 | var self = this; 93 | compiler.plugin('compilation', function (compilation) { 94 | compilation.options.htmlElements = compilation.options.htmlElements || {}; 95 | 96 | compilation.plugin('html-webpack-plugin-before-html-generation', function (htmlPluginData, callback) { 97 | const locations = self.locations; 98 | 99 | if (locations) { 100 | const publicPath = htmlPluginData.assets.publicPath; 101 | 102 | Object.getOwnPropertyNames(locations).forEach(function (loc) { 103 | compilation.options.htmlElements[loc] = getHtmlElementString(locations[loc], publicPath); 104 | }); 105 | } 106 | 107 | 108 | callback(null, htmlPluginData); 109 | }); 110 | }); 111 | 112 | }; 113 | 114 | module.exports = HtmlElementsPlugin; 115 | -------------------------------------------------------------------------------- /config/karma.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | module.exports = function (config) { 6 | var testWebpackConfig = require('./webpack.test.js')({ env: 'test' }); 7 | 8 | var configuration = { 9 | 10 | /** 11 | * Base path that will be used to resolve all patterns (e.g. files, exclude). 12 | */ 13 | basePath: '', 14 | 15 | /** 16 | * Frameworks to use 17 | * 18 | * available frameworks: https://npmjs.org/browse/keyword/karma-adapter 19 | */ 20 | frameworks: ['jasmine'], 21 | 22 | /** 23 | * List of files to exclude. 24 | */ 25 | exclude: [], 26 | 27 | client: { 28 | captureConsole: false 29 | }, 30 | 31 | /** 32 | * List of files / patterns to load in the browser 33 | * 34 | * we are building the test environment in ./spec-bundle.js 35 | */ 36 | files: [ 37 | { pattern: './config/spec-bundle.js', watched: false }, 38 | { pattern: './src/demo/assets/**/*', watched: false, included: false, served: true, nocache: false } 39 | ], 40 | 41 | /** 42 | * By default all assets are served at http://localhost:[PORT]/base/ 43 | */ 44 | proxies: { 45 | "/assets/": "/base/src/demo/assets/" 46 | }, 47 | 48 | /** 49 | * Preprocess matching files before serving them to the browser 50 | * available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 51 | */ 52 | preprocessors: { './config/spec-bundle.js': ['coverage', 'webpack', 'sourcemap'] }, 53 | 54 | /** 55 | * Webpack Config at ./webpack.test.js 56 | */ 57 | webpack: testWebpackConfig, 58 | 59 | coverageReporter: { 60 | type: 'in-memory' 61 | }, 62 | 63 | remapCoverageReporter: { 64 | 'text-summary': null, 65 | json: './coverage/coverage.json', 66 | html: './coverage/html' 67 | }, 68 | 69 | /** 70 | * Webpack please don't spam the console when running in karma! 71 | */ 72 | webpackMiddleware: { 73 | /** 74 | * webpack-dev-middleware configuration 75 | * i.e. 76 | */ 77 | noInfo: true, 78 | /** 79 | * and use stats to turn off verbose output 80 | */ 81 | stats: { 82 | /** 83 | * options i.e. 84 | */ 85 | chunks: false 86 | } 87 | }, 88 | 89 | /** 90 | * Test results reporter to use 91 | * 92 | * possible values: 'dots', 'progress' 93 | * available reporters: https://npmjs.org/browse/keyword/karma-reporter 94 | */ 95 | reporters: ['mocha', 'coverage', 'remap-coverage'], 96 | 97 | /** 98 | * Web server port. 99 | */ 100 | port: 9876, 101 | 102 | /** 103 | * enable / disable colors in the output (reporters and logs) 104 | */ 105 | colors: true, 106 | 107 | /** 108 | * Level of logging 109 | * possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 110 | */ 111 | logLevel: config.LOG_WARN, 112 | 113 | /** 114 | * enable / disable watching file and executing tests whenever any file changes 115 | */ 116 | autoWatch: false, 117 | 118 | /** 119 | * start these browsers 120 | * available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 121 | */ 122 | browsers: [ 123 | 'Chrome' 124 | ], 125 | 126 | customLaunchers: { 127 | ChromeTravisCi: { 128 | base: 'Chrome', 129 | flags: ['--no-sandbox'] 130 | } 131 | }, 132 | 133 | /** 134 | * Continuous Integration mode 135 | * if true, Karma captures browsers, runs the tests and exits 136 | */ 137 | singleRun: true 138 | }; 139 | 140 | if (process.env.TRAVIS) { 141 | configuration.browsers = [ 142 | 'ChromeTravisCi' 143 | ]; 144 | } 145 | 146 | config.set(configuration); 147 | }; 148 | -------------------------------------------------------------------------------- /src/ngx-tree-select/test/tree-select.component.spec.ts: -------------------------------------------------------------------------------- 1 | import * as console from 'console'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { By } from '@angular/platform-browser'; 4 | import { Component, DebugElement, ViewChild } from '@angular/core'; 5 | import { FormsModule } from '@angular/forms'; 6 | import { HierarchicalCountries } from '../../datas'; 7 | import { ItemPipe } from '../src/pipes/item.pipe'; 8 | import { NgxTreeSelectModule } from '../src/index'; 9 | import { OffClickDirective } from '../src/directives/off-click.directive'; 10 | import { SelectService } from '../src/services/select.service'; 11 | import { TreeSelectComponent } from '../src/components/tree-select.component'; 12 | import { TreeSelectDefaultOptions } from '../src/models/tree-select-default-options'; 13 | import { TreeSelectItemComponent } from '../src/components/tree-select-item.component'; 14 | 15 | describe('TreeSelectComponent', () => { 16 | let comp: TreeSelectSimpleBasicComponent; 17 | let fixture: ComponentFixture; 18 | let trigger: HTMLElement; 19 | 20 | beforeEach(async(() => { 21 | TestBed.configureTestingModule({ 22 | imports: [FormsModule, NgxTreeSelectModule.forRoot({})], 23 | declarations: [TreeSelectSimpleBasicComponent], 24 | providers: [{ provide: TreeSelectDefaultOptions, useValue: {} }] 25 | }) 26 | .compileComponents(); 27 | })); 28 | 29 | // For reasons unknown, not using `done` (even calling it synchronously), 30 | // causes Chrome (v58+) to often get disconnected (at least on Windows and Travis). 31 | beforeEach((done: DoneFn) => { 32 | fixture = TestBed.createComponent(TreeSelectSimpleBasicComponent); 33 | comp = fixture.componentInstance; 34 | fixture.detectChanges(); 35 | trigger = fixture.debugElement.query(By.css('tree-select .form-control')).nativeElement; 36 | done(); 37 | }); 38 | 39 | it('should create test component', () => expect(comp).toBeDefined()); 40 | it('should create tree-select component', () => expect(comp.select).toBeDefined()); 41 | 42 | it('should toggle dropdown on select container click', () => { 43 | trigger.click(); 44 | fixture.detectChanges(); 45 | expect(fixture.componentInstance.select.isOpen).toBe(true); 46 | 47 | trigger.click(); 48 | fixture.detectChanges(); 49 | expect(fixture.componentInstance.select.isOpen).toBe(false); 50 | }); 51 | it('should close dropdown on outside click', () => { 52 | trigger.click(); 53 | fixture.detectChanges(); 54 | expect(fixture.componentInstance.select.isOpen).toBe(true); 55 | const outsideElement = fixture.debugElement.query(By.css('.outside')).nativeElement; 56 | outsideElement.click(); 57 | fixture.detectChanges(); 58 | // Not work with jest : Need investigation 59 | // expect(fixture.componentInstance.select.isOpen).toBe(false); 60 | }); 61 | 62 | it('should contains selected', () => { 63 | expect(comp.selectedCountry).toBeDefined(); 64 | expect(comp.selectedCountry.id).toBe('LU'); 65 | fixture.detectChanges(); 66 | const selected = fixture.debugElement.query( 67 | By.css('.selected-item-text') 68 | ).nativeElement as HTMLSpanElement; 69 | expect(selected.innerHTML).toContain(comp.selectedCountry.name); 70 | }); 71 | }); 72 | 73 | @Component({ 74 | template: ` 75 | 76 | 77 | 83 | 84 | 85 | 86 | 87 | ` 88 | }) 89 | class TreeSelectSimpleBasicComponent { 90 | 91 | @ViewChild(TreeSelectComponent) 92 | public select: TreeSelectComponent; 93 | 94 | public selectedCountry = { 95 | id: 'LU', 96 | name: 'Luxembourg', 97 | capital: 'Luxembourg', 98 | phone: '352', 99 | currency: 'EUR' 100 | }; 101 | public countries = HierarchicalCountries; 102 | } 103 | -------------------------------------------------------------------------------- /src/lib/src/model/selectable-item.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"selectable-item.js","sourceRoot":"","sources":["selectable-item.ts"],"names":[],"mappings":";;AAEA;IAOE,wBAAmB,EAAU,EAAS,IAAY,EAAS,IAAS,EAAS,GAAkB;QAA5E,OAAE,GAAF,EAAE,CAAQ;QAAS,SAAI,GAAJ,IAAI,CAAQ;QAAS,SAAI,GAAJ,IAAI,CAAK;QAAS,QAAG,GAAH,GAAG,CAAe;QANxF,cAAS,GAAG,KAAK,CAAC;QAElB,WAAM,GAAG,KAAK,CAAC;QACf,gBAAW,GAAG,IAAI,CAAC;QACnB,cAAS,GAAG,KAAK,CAAC;IAGzB,CAAC;IAED,sBAAI,oCAAQ;aAAZ;YACE,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACnD,CAAC;;;OAAA;IACD,sBAAI,mCAAO;aAAX;YACE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;gBAC1D,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAA,KAAK,IAAI,OAAA,KAAK,CAAC,QAAQ,EAAd,CAAc,CAAC,CAAC,CAAC,CAAC;oBACjD,MAAM,CAAC,IAAI,CAAC;gBACd,CAAC;gBAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAA,KAAK,IAAI,OAAA,KAAK,CAAC,QAAQ,KAAK,KAAK,EAAxB,CAAwB,CAAC,CAAC,CAAC,CAAC;oBAClE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;gBACxB,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC;YACd,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;;;OAAA;IACD,sBAAI,oCAAQ;aAAZ;YACE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;gBAC1D,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAA,KAAK,IAAI,OAAA,KAAK,CAAC,QAAQ,EAAd,CAAc,CAAC,CAAC,CAAC,CAAC;oBAChD,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;wBAC5C,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;oBACzB,CAAC;oBACD,MAAM,CAAC,IAAI,CAAC;gBACd,CAAC;gBAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAA,KAAK,IAAI,OAAA,KAAK,CAAC,QAAQ,KAAK,KAAK,EAAxB,CAAwB,CAAC,CAAC,CAAC,CAAC;oBAClE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;wBAC5C,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;oBACxB,CAAC;oBAAC,IAAI,CAAC,CAAC;wBACN,MAAM,CAAC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;oBAChC,CAAC;gBACH,CAAC;gBACD,MAAM,CAAC,KAAK,CAAC;YACf,CAAC;YAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC;gBACpD,GAAG,CAAC,CAAY,UAAa,EAAb,KAAA,IAAI,CAAC,QAAQ,EAAb,cAAa,EAAb,IAAa;oBAAxB,IAAI,GAAG,SAAA;oBACV,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC;iBACtB;YACH,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QACxB,CAAC;aACD,UAAa,KAAc;YACzB,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBAC9D,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC;oBACnB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAA,KAAK,IAAI,OAAA,KAAK,CAAC,QAAQ,GAAG,KAAK,EAAtB,CAAsB,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;YAAC,IAAI,CAAC,CAAC;gBACN,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACzB,CAAC;QACH,CAAC;;;OATA;IAUH,qBAAC;AAAD,CAAC,AAxDD,IAwDC;AAxDY,wCAAc","sourcesContent":["import { SelectService } from '../service/select.service';\r\n\r\nexport class SelectableItem {\r\n public _selected = false;\r\n public children?: SelectableItem[];\r\n public isOpen = false;\r\n public matchFilter = true;\r\n public isVisible = false;\r\n\r\n constructor(public id: string, public text: string, public data: any, public svc: SelectService) {\r\n }\r\n\r\n get hasChild(): boolean {\r\n return this.children && this.children.length > 0;\r\n }\r\n get checked(): boolean {\r\n if (this.hasChild && this.svc.Configuration.allowMultiple) {\r\n if (this.children.every(child => child.selected)) {\r\n return true;\r\n } else if (this.children.every(child => child.selected === false)) {\r\n return this._selected;\r\n }\r\n return null;\r\n }\r\n return this._selected;\r\n }\r\n get selected(): boolean {\r\n if (this.hasChild && this.svc.Configuration.allowMultiple) {\r\n if (this.children.some(child => child.selected)) {\r\n if (this.svc.Configuration.onlySelectParent) {\r\n this._selected = false;\r\n }\r\n return true;\r\n } else if (this.children.every(child => child.selected === false)) {\r\n if (this.svc.Configuration.onlySelectParent) {\r\n return this._selected;\r\n } else {\r\n return this._selected = false;\r\n }\r\n }\r\n return false;\r\n } else if (this.hasChild && this._selected === true) {\r\n for (let itm of this.children) {\r\n itm.selected = false;\r\n }\r\n }\r\n\r\n return this._selected;\r\n }\r\n set selected(value: boolean) {\r\n if (this.hasChild && !this.svc.Configuration.onlySelectParent) {\r\n if (value !== null) {\r\n this.children.forEach(child => child.selected = value);\r\n }\r\n } else {\r\n this._selected = value;\r\n }\r\n }\r\n}\r\n"]} -------------------------------------------------------------------------------- /scripts/gulp/compile.ts: -------------------------------------------------------------------------------- 1 | import * as Path from 'path'; 2 | import * as chalk from 'chalk'; 3 | const runSequence = require('run-sequence'); 4 | 5 | import * as util from '../util'; 6 | 7 | function filterPackageSelection(packages) { 8 | const idx = process.argv.indexOf('--select'); 9 | 10 | if (idx > -1) { 11 | if (!process.argv[idx+1]) { 12 | throw new Error('Invalid library selection.') 13 | } 14 | const selected = process.argv[idx + 1].split(',').map( v => v.trim() ); 15 | selected.forEach( s => { 16 | if (packages.indexOf(s) === -1) { 17 | throw new Error(`Could not apply selection, "${s}" is not a known package name.`); 18 | } 19 | }); 20 | packages = selected; 21 | } 22 | 23 | return packages; 24 | } 25 | 26 | 27 | 28 | @util.GulpClass.Gulpclass() 29 | export class Gulpfile { 30 | private packages: util.PackageMetadata[]; 31 | private promiseContainer: util.Promisify; 32 | private timeStart: [number, number]; 33 | 34 | @util.GulpClass.Task({ name: '!compile', dependencies: ['!clean:dist'] }) 35 | compile() { 36 | this.packages = filterPackageSelection(util.libConfig.packages.slice()) 37 | .map( pkgName => util.buildPackageMetadata(pkgName) ); 38 | 39 | if (this.packages.length === 0) { 40 | return Promise.reject(new Error('Invalid configuration, no packages found.')); 41 | } 42 | 43 | if (this.packages.length > 1) { 44 | util.log(`Compiling libraries:\n\t- ${this.packages.map( p => p.dirName ).join('\n\t- ')}`); 45 | } 46 | 47 | this.promiseContainer = util.promisify(); 48 | 49 | util.currentPackage(this.packages.shift()); 50 | this.run(); 51 | 52 | return this.promiseContainer.promise; 53 | } 54 | 55 | private run() { 56 | const curPkg = util.currentPackage(); 57 | 58 | util.saveTempTsConfig(curPkg); 59 | 60 | if (curPkg.parent) { 61 | util.log(chalk.yellow(`\n\n============================================= 62 | Compiling extension ${curPkg.dirName} for library ${curPkg.parent.dirName} 63 | =============================================\n\n`)); 64 | } else { 65 | util.log(chalk.yellow(`\n\n============================================= 66 | Compiling library ${curPkg.dirName} 67 | =============================================\n\n`)); 68 | } 69 | 70 | 71 | this.timeStart = process.hrtime(); 72 | 73 | runSequence( 74 | '!build:webpack', 75 | '!build:rollup:fesm', 76 | '!build:fesm:es5', 77 | '!build:rollup:umd', 78 | '!minifyAndGzip', 79 | '!pureAnnotation', 80 | '!manifest', 81 | err => this.handleRunEnd(err) 82 | ); 83 | }; 84 | 85 | private handleRunEnd(err?: any): void { 86 | if (err) { 87 | util.log(chalk.red(`ERROR: ${err.message}`)); 88 | this.cleanup().then( () => this.promiseContainer.reject(err) ); 89 | } else { 90 | 91 | util.log(chalk.green( 92 | `============================================= 93 | Compile OK: ${this.getName()} (${this.getElapsed('ms')} ms) 94 | =============================================`)); 95 | 96 | if (util.currentPackage().libExtensions) { 97 | util.log(`Extensions found (${util.currentPackage().libExtensions.length}), compiling...`); 98 | util.buildExtensionMetadata(util.currentPackage()) 99 | .forEach( ext => this.packages.unshift(ext) ); 100 | } 101 | 102 | if (this.packages.length > 0) { 103 | util.currentPackage(this.packages.shift()); 104 | this.run(); 105 | } else { 106 | util.log('No more libraries to compile. Done!'); 107 | this.cleanup().then( () => this.promiseContainer.resolve() ); 108 | } 109 | } 110 | }; 111 | 112 | private getElapsed(format: 'sec' | 'ms'): number { 113 | const timeEnd = process.hrtime(this.timeStart); 114 | const ms = Math.round((timeEnd[0] * 1000) + (timeEnd[1] / 1000000)); 115 | 116 | switch (format) { 117 | case 'sec': 118 | return ms / 1000; 119 | default: 120 | return ms; 121 | } 122 | } 123 | 124 | private cleanup(): Promise { 125 | return util.cleanup().catch( err => {}); 126 | } 127 | 128 | private getName(): string { 129 | return util.currentPackage().parent 130 | ? util.currentPackage().parent.dirName 131 | : util.currentPackage().dirName 132 | ; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/demo/custom-typings.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Custom Type Definitions 3 | * When including 3rd party modules you also need to include the type definition for the module 4 | * if they don't provide one within the module. You can try to install it with @types 5 | 6 | npm install @types/node 7 | npm install @types/lodash 8 | 9 | * If you can't find the type definition in the registry we can make an ambient/global definition in 10 | * this file for now. For example 11 | 12 | declare module 'my-module' { 13 | export function doesSomething(value: string): string; 14 | } 15 | 16 | * If you are using a CommonJS module that is using module.exports then you will have to write your 17 | * types using export = yourObjectOrFunction with a namespace above it 18 | * notice how we have to create a namespace that is equal to the function we're 19 | * assigning the export to 20 | 21 | declare module 'jwt-decode' { 22 | function jwtDecode(token: string): any; 23 | namespace jwtDecode {} 24 | export = jwtDecode; 25 | } 26 | 27 | * 28 | * If you're prototying and you will fix the types later you can also declare it as type any 29 | * 30 | 31 | declare var assert: any; 32 | declare var _: any; 33 | declare var $: any; 34 | 35 | * 36 | * If you're importing a module that uses Node.js modules which are CommonJS you need to import as 37 | * in the files such as main.browser.ts or any file within app/ 38 | * 39 | 40 | import * as _ from 'lodash' 41 | 42 | * You can include your type definitions in this file until you create one for the @types 43 | * 44 | */ 45 | 46 | // support NodeJS modules without type definitions 47 | declare module '*'; 48 | 49 | /* 50 | // for legacy tslint etc to understand rename 'modern-lru' with your package 51 | // then comment out `declare module '*';`. For each new module copy/paste 52 | // this method of creating an `any` module type definition 53 | declare module 'modern-lru' { 54 | let x: any; 55 | export = x; 56 | } 57 | */ 58 | 59 | // Extra variables that live on Global that will be replaced by webpack DefinePlugin 60 | declare var ENV: string; 61 | declare var HMR: boolean; 62 | declare var System: SystemJS; 63 | 64 | interface SystemJS { 65 | import: (path?: string) => Promise; 66 | } 67 | 68 | interface GlobalEnvironment { 69 | ENV: string; 70 | HMR: boolean; 71 | SystemJS: SystemJS; 72 | System: SystemJS; 73 | } 74 | 75 | interface Es6PromiseLoader { 76 | (id: string): (exportName?: string) => Promise; 77 | } 78 | 79 | type FactoryEs6PromiseLoader = () => Es6PromiseLoader; 80 | type FactoryPromise = () => Promise; 81 | 82 | type AsyncRoutes = { 83 | [component: string]: Es6PromiseLoader | 84 | Function | 85 | FactoryEs6PromiseLoader | 86 | FactoryPromise ; 87 | }; 88 | 89 | type IdleCallbacks = Es6PromiseLoader | 90 | Function | 91 | FactoryEs6PromiseLoader | 92 | FactoryPromise ; 93 | 94 | interface WebpackModule { 95 | hot: { 96 | data?: any, 97 | idle: any, 98 | accept(dependencies?: string | string[], callback?: (updatedDependencies?: any) => void): void; 99 | decline(deps?: any | string | string[]): void; 100 | dispose(callback?: (data?: any) => void): void; 101 | addDisposeHandler(callback?: (data?: any) => void): void; 102 | removeDisposeHandler(callback?: (data?: any) => void): void; 103 | check(autoApply?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void; 104 | apply(options?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void; 105 | status(callback?: (status?: string) => void): void | string; 106 | removeStatusHandler(callback?: (status?: string) => void): void; 107 | }; 108 | } 109 | 110 | interface WebpackRequire { 111 | (id: string): any; 112 | (paths: string[], callback: (...modules: any[]) => void): void; 113 | ensure(ids: string[], callback: (req: WebpackRequire) => void, chunkName?: string): void; 114 | context(directory: string, useSubDirectories?: boolean, regExp?: RegExp): WebpackContext; 115 | } 116 | 117 | interface WebpackContext extends WebpackRequire { 118 | keys(): string[]; 119 | } 120 | 121 | interface ErrorStackTraceLimit { 122 | stackTraceLimit: number; 123 | } 124 | 125 | // Extend typings 126 | // type NodeRequire = WebpackRequire; 127 | // type ErrorConstructor = ErrorStackTraceLimit; 128 | // type NodeRequireFunction = Es6PromiseLoader; 129 | // type NodeModule = WebpackModule; 130 | type Global = GlobalEnvironment; 131 | -------------------------------------------------------------------------------- /scripts/util/types.ts: -------------------------------------------------------------------------------- 1 | import { CompilerOptions } from 'typescript'; 2 | import { AngularCompilerOptions } from '@angular/tsc-wrapped'; 3 | 4 | export interface GlobalLibConfig { 5 | scope?: string; 6 | packages: string[] 7 | } 8 | 9 | export interface LocalLibConfig { 10 | 11 | /** 12 | * The entry file name, without extension. 13 | * defaults to 'index' if not set. 14 | * 15 | * Use in multi-library configuration to avoid AOT compilation hell with 'index' addition 16 | */ 17 | entry?: string; 18 | 19 | /** 20 | * When true, all javascript files (TS output) with Angular components that has a URI template resources 21 | * (templateUrl with HTML and styleUrls with css, scss) will get be inlined. 22 | * 23 | * i.e. "templateUrl" will get replaced with "template" and "styleUrls" with "styles". 24 | * 25 | * This is similar to what "angular2-template-loader" does but instead of a require reference, it will be the raw template. 26 | * 27 | * Each resource will go through webpack's loader chain, this means you can use scss and it will get processed. 28 | * Any loader you put in the chain will work so you can achieve complex things. 29 | * 30 | * ADDITIONALLY, all of angular's "metadata.json" files will go through the same process 31 | * inlining the resources into them. 32 | * 33 | * > The end result after compilation is 100% similar to result in dev mode, 34 | * as long as the loaders and their order are identical in both configurations. 35 | * 36 | * > Setting "inlineResources" to true will activate template code generation by the angular compiler (skipTemplateCodegen=false) 37 | * Template code generation is turned off by default so make sure to active "inlineResources" if you are 38 | * using "templateUrl" / "styleUrls" in your library components. 39 | */ 40 | inlineResources?: boolean; 41 | 42 | /** 43 | * Internal extensions for the library. 44 | * If a library declares a "libExtensions" property in it's internal package.json file 45 | * it is considered as instructions for creating internal umd bundles. 46 | * 47 | * THIS OBJECT IS SUBJECT TO CHANGE. 48 | * Most probably it will be an array of string where metadata for each extension 49 | * will be set inside it's package.json and not in the parent. 50 | */ 51 | libExtensions?: Array; 52 | } 53 | 54 | export interface PackageMetadata { 55 | name: string; 56 | 57 | /** 58 | * The directory name, with scope (if exists) 59 | * 60 | * For example, if the directory name is "lib" and the scope is "corp" dir will be "@corp/lib" 61 | * 62 | * When no scope dir === dirName 63 | */ 64 | dir: string; 65 | 66 | /** 67 | * The directory name, without scope 68 | * 69 | * When no scope dir === dirName 70 | */ 71 | dirName: string; 72 | 73 | umd: string; 74 | 75 | /** 76 | * A list of the names of external dependencies (based on package.json) 77 | */ 78 | externals: string[], 79 | 80 | /** 81 | * A list of regexp of external dependencies (based on package.json) 82 | */ 83 | externalsWebpack: Array; 84 | 85 | /** 86 | * The exported module name for UMD bundles 87 | * 88 | * The default is a camel case version of the name. 89 | * When a scope is set it is prefixed without the @ sign and separated by a dot. 90 | * 91 | * Examples: 92 | * my-library: myLibrary 93 | * @company/my-library: company.myLibrary 94 | */ 95 | moduleName: string; 96 | 97 | /** 98 | * The entry file name, without extension. 99 | * 'index' if not set. 100 | * 101 | * Use in multi-library configuration to avoid AOT compilation hell with 'index' addition 102 | */ 103 | entry: string; 104 | 105 | inlineResources: boolean; 106 | 107 | tsConfig: string; 108 | tsConfigObj: { compilerOptions: CompilerOptions, angularCompilerOptions: AngularCompilerOptions }; 109 | 110 | /** 111 | * Internal extensions for the library. 112 | * If a library declares a "libExtensions" property in it's internal package.json file 113 | * it is considered as instructions for creating internal umd bundles. 114 | */ 115 | libExtensions?: Array; 116 | 117 | parent?: PackageMetadata; 118 | extension?: LibraryExtension; 119 | } 120 | 121 | export interface LibraryExtension { 122 | /** 123 | * The name of the library 124 | */ 125 | name: string; 126 | 127 | /** 128 | * The relative path to the DIRECTORY of the extension. 129 | * Relative to the root directory of the current package. 130 | * 131 | * Optional, if not set taken from the name property. 132 | */ 133 | dir?: string; 134 | 135 | /** 136 | * The entry file, relative to the 'dir' property. 137 | * 138 | * Optional, if not set `index.ts` is used 139 | */ 140 | entry?: string; 141 | } 142 | 143 | 144 | export interface CommitVersion { 145 | commit: string; 146 | version: string; 147 | } -------------------------------------------------------------------------------- /config/webpack.package.ts: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const helpers = require('./helpers'); 4 | const webpack = require('webpack'); 5 | import * as mkdirp from 'mkdirp'; 6 | 7 | 8 | /** 9 | * Webpack Plugins 10 | */ 11 | const DefinePlugin = require('webpack/lib/DefinePlugin'); 12 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 13 | const TsConfigPathsPlugin = require('awesome-typescript-loader').TsConfigPathsPlugin; 14 | const BannerPlugin = webpack.BannerPlugin; 15 | const NgcWebpackPlugin = require('ngc-webpack').NgcWebpackPlugin; 16 | 17 | import { PackageMetadata, FS_REF, getOutDir, webpackAlias, getCopyInstruction, root, getMainOutputFileName } from '../scripts/util'; 18 | 19 | module.exports = function(metadata: PackageMetadata) { 20 | const banner = `/** 21 | * ${metadata.name} Copyright ${new Date().getFullYear()} 22 | * Licensed under MIT 23 | */`; 24 | 25 | 26 | /* 27 | The entry point references a file that does not exists at the time of definition. 28 | This is the generated angular flat module file which will generate once the angular compiler does its thing. 29 | The angular compiler runs before webpack starts via NgcWebpackPlugin so we're safe to assume 30 | that the file will be there (unless an error has occurred) 31 | 32 | The original entry point should have been: 33 | 34 | [metadata.umd]: helpers.root(`src/${metadata.dir}/src/index.ts`) 35 | 36 | But this will result in an incorrect bundle since internal aot compiler exports (ɵ) will not bundle. 37 | */ 38 | const entry = { 39 | [metadata.umd]: path.join(getOutDir(metadata, true, getMainOutputFileName(metadata) + '.js')) 40 | }; 41 | 42 | const ngcWebpackConfig = { 43 | tsConfig: metadata.tsConfig, 44 | resourceTransformer: function(filePath, data) { 45 | if (data) { 46 | const relativePath = path.relative(root('src', metadata.dir, FS_REF.SRC_CONTAINER), filePath); 47 | const absPath = path.resolve(getCopyInstruction(metadata).from, relativePath); 48 | mkdirp.sync(path.dirname(absPath)); 49 | fs.writeFileSync(absPath, data, 'utf-8'); 50 | } 51 | return data; 52 | } 53 | }; 54 | 55 | // don't transform resource if we don't have inlined resources 56 | if (!metadata.inlineResources) { 57 | delete ngcWebpackConfig.resourceTransformer; 58 | } 59 | 60 | return { 61 | bail: true, 62 | devtool: 'source-map', 63 | 64 | resolve: { 65 | extensions: ['.ts', '.js'], 66 | modules: [root('src'), root('node_modules')], 67 | alias: Object.assign(webpackAlias(), webpackAlias(metadata.parent 68 | ? metadata.parent.dirName + '/' + metadata.extension.dir 69 | : metadata.dirName 70 | )) 71 | }, 72 | 73 | entry, 74 | 75 | output: { 76 | path: helpers.root('.'), 77 | publicPath: '/', 78 | filename: `${getCopyInstruction(metadata).toBundle.substr(root().length + 1)}/[name].webpack.umd.js`, 79 | libraryTarget: 'umd', 80 | library: metadata.moduleName 81 | }, 82 | 83 | // require those dependencies but don't bundle them 84 | externals: metadata.externalsWebpack, 85 | 86 | module: { 87 | rules: [ 88 | { 89 | test: /\.ts$/, 90 | use: [ 91 | { 92 | loader: `awesome-typescript-loader`, 93 | options: { 94 | configFileName: '.tsconfig.tmp.json', 95 | declaration: false 96 | } 97 | } 98 | ], 99 | exclude: [/\.e2e\.ts$/] 100 | }, 101 | { 102 | test: /\.css$/, 103 | use: ['to-string-loader', 'css-loader'], 104 | exclude: [root('src', 'demo')] 105 | }, 106 | 107 | /* 108 | * to string and sass loader support for *.scss files (from Angular components) 109 | * Returns compiled css content as string 110 | * 111 | */ 112 | { 113 | test: /\.scss$/, 114 | use: ['to-string-loader', 'css-loader', 'sass-loader'], 115 | exclude: [root('src', 'demo')] 116 | }, 117 | 118 | /* Raw loader support for *.html 119 | * Returns file content as string 120 | * 121 | * See: https://github.com/webpack/raw-loader 122 | */ 123 | { 124 | test: /\.html$/, 125 | use: 'raw-loader', 126 | exclude: [root('src/demo/index.html')] 127 | }, 128 | ] 129 | }, 130 | 131 | plugins: [ 132 | // new webpack.ProvidePlugin({ 133 | // '__assign': ['tslib', '__assign'], 134 | // '__extends': ['tslib', '__extends'], 135 | // }), 136 | 137 | new TsConfigPathsPlugin(), 138 | 139 | // fix the warning in ./~/@angular/core/src/linker/system_js_ng_module_factory_loader.js 140 | new webpack.ContextReplacementPlugin( 141 | /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, 142 | helpers.root('./src') 143 | ), 144 | 145 | new NgcWebpackPlugin(ngcWebpackConfig), 146 | 147 | new BannerPlugin({ 148 | banner: banner, 149 | raw: true, 150 | entryOnly: true 151 | }), 152 | 153 | new CopyWebpackPlugin([ 154 | { from: 'README.md', to: helpers.root(`./${FS_REF.PKG_DIST}/${metadata.dir}`) }, 155 | ]) 156 | ] 157 | }; 158 | }; 159 | -------------------------------------------------------------------------------- /scripts/util/simulation.ts: -------------------------------------------------------------------------------- 1 | import * as Path from 'path'; 2 | import * as del from 'del'; 3 | import * as tsConfigLoader from 'tsconfig'; 4 | import { CompilerOptions, MapLike } from 'typescript'; 5 | import { Configuration, NewModule, NewResolve, Rule, Plugin } from 'webpack'; 6 | import { NgcWebpackPlugin } from 'ngc-webpack'; 7 | 8 | import { root, FS_REF, jsonPatch } from './fs'; 9 | import { tsConfigPaths, tsConfigPathsForSimulation } from './config'; 10 | 11 | function findLoader(rule: any, name: string): any { 12 | if (!rule || rule.loader === name) { 13 | return rule; 14 | } 15 | 16 | let result: Rule; 17 | 18 | if (Array.isArray(rule)) { 19 | for (let i = 0, len = rule.length; i < len; i++) { 20 | result = findLoader(rule[i], name); 21 | if (result) { 22 | break; 23 | } 24 | } 25 | } else { 26 | if (rule.loaders || rule.use) { 27 | result = findLoader(rule.loaders || rule.use, name); 28 | } 29 | 30 | if (!result && rule.rules) { 31 | result = findLoader(rule.rules, name); 32 | } 33 | } 34 | 35 | return result; 36 | } 37 | 38 | function findPlugin(plugins: Plugin[], type: new(...args: any[]) => T): T | undefined { 39 | return plugins.find( p => p instanceof NgcWebpackPlugin); 40 | } 41 | 42 | function assignNonLibPaths(tsConfigPath: string, paths: MapLike): void { 43 | const oldPaths = tsConfigLoader.loadSync(tsConfigPath).config.compilerOptions.paths; 44 | const automatedPaths = tsConfigPaths(); 45 | Object.keys(oldPaths) 46 | .filter( k => !automatedPaths.hasOwnProperty(k) ) 47 | .forEach( k => paths[k] = oldPaths[k] ); 48 | } 49 | 50 | /** 51 | * Alter webpack and typescript configurations (tsconfig) so the compilation process will reference 52 | * the built (compiled) libraries and not the source code of them. 53 | * @param webpackConfig 54 | * @param isProd 55 | */ 56 | export function applySimulation(webpackConfig: Configuration, isProd: boolean): void { 57 | /* 58 | First, lets teach webpack to resolve the libraries from the package dist folder. 59 | we also delete the aliases. 60 | This should fix webpack dev mode. 61 | */ 62 | (webpackConfig.resolve as NewResolve).modules.unshift(root(FS_REF.PKG_DIST)); 63 | webpackConfig.resolve.alias = { }; 64 | 65 | /* 66 | Now we need to make sure the type-checker will load types from the compiled package (d.ts) 67 | and not the source directory. 68 | 69 | We create a 'paths' property referencing the package dist folder and set these paths to the 70 | paths property of "awesome-typescript-loader", this will override any paths in the loaded tsconfig. 71 | 72 | */ 73 | const paths = tsConfigPathsForSimulation(); 74 | 75 | /* 76 | The only issue is that the old paths might contain some custom user defined paths that we need to 77 | include, these are paths set via build_hooks scripts and are not related to a library. 78 | We need to filter out these custom paths, and only them. 79 | We find the awesome-typescript-loader configuration object, extract the tsconfig location and 80 | and filter the relevant paths and assign them. 81 | 82 | Once done we set the new "paths" awesome-typescript-loader configuration, they will override 83 | any existing "paths" property. 84 | */ 85 | const atlLoader = findLoader((webpackConfig.module as NewModule).rules, 'awesome-typescript-loader'); 86 | const tsConfigPath = root(atlLoader.options.configFileName || 'tsconfig.json'); 87 | assignNonLibPaths(tsConfigPath, paths); 88 | atlLoader.options.paths = paths; 89 | 90 | 91 | 92 | if (isProd) { 93 | /* 94 | For production builds there is an additional factor, we need to deal with the AOT compiler. 95 | Since AOT compilation runs in different process BEFORE webpack starts bundling we need to 96 | make sure that the AOT compiler uses the right "tsconfig" settings, which is the right "paths". 97 | 98 | Since it's a different process we need to create a temporary "tsconfig" file based on the original 99 | file used by ATL. We need a temp file since unlike ATL, ngc-webpack does not accept CompilerOptions 100 | properties that override those in the file so we need to create an actual file. 101 | */ 102 | 103 | // CODE ASSUMES SAME "tsconfig" file for ATL and NgcWebpackPlugin config. 104 | const ngcWebpackPluginInstance = findPlugin(webpackConfig.plugins, NgcWebpackPlugin); 105 | const simulatorTsConfigPath = Path.join(Path.dirname(tsConfigPath), '.tsconfig.webpack.sim.json'); 106 | 107 | /* 108 | Create a new "tsconfig" json file, extending the original one used by ATL but with 109 | different paths, we use the same paths we created in the first phase. 110 | */ 111 | jsonPatch<{ extends: string, compilerOptions: CompilerOptions }>(tsConfigPath) 112 | .update( tsConfig => { 113 | tsConfig.extends = `./${Path.basename(tsConfigPath, '.json')}`; 114 | tsConfig.compilerOptions.paths = paths; 115 | }) 116 | .save(simulatorTsConfigPath); 117 | 118 | 119 | /* 120 | Replacing the original NgcWebpackPlugin instance with a new instance pointing at our 121 | temporary "tsconfig". 122 | The new configuration will also delete the temporary "tsconfig" once AOT compiler is done. 123 | */ 124 | webpackConfig.plugins 125 | .splice( 126 | webpackConfig.plugins.indexOf(ngcWebpackPluginInstance), 127 | 1, 128 | new NgcWebpackPlugin(Object.assign({}, ngcWebpackPluginInstance.options, { 129 | tsConfig: simulatorTsConfigPath, 130 | onCompilationSuccess: () => del.sync(simulatorTsConfigPath), 131 | onCompilationError: () => del.sync(simulatorTsConfigPath) 132 | })) 133 | ); 134 | } 135 | } -------------------------------------------------------------------------------- /scripts/util/util.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | import * as Path from 'path'; 3 | import * as voca from 'voca'; 4 | import * as jsonfile from 'jsonfile'; 5 | 6 | 7 | const uglify = require('uglify-js'); 8 | const zlib = require('zlib'); 9 | const deepcopy = require('deepcopy'); 10 | 11 | import { root, FS_REF } from './fs'; 12 | import { PackageMetadata, LocalLibConfig } from './types'; 13 | import { libConfig, currentPackage } from './state'; 14 | import { normalizeLibExtension } from './config'; 15 | 16 | export function log(msg: string): void { 17 | process.stdout.write(msg + '\n'); 18 | } 19 | 20 | /** 21 | * Returns the package name. 22 | * If scope exists, it will be added. 23 | * 24 | * This can also be used as a partial path from the src root. 25 | * @param name optional, if not set, the current dirName property from CURRENT_PACKAGE is taken 26 | * @return {string} 27 | */ 28 | export function getPackageName(name?: string) { 29 | if (!name) { 30 | if (!currentPackage()) { 31 | throw new Error('Invalid operation, name not supplied and CURRENT_PACKAGE is not set.'); 32 | } 33 | name = currentPackage().dirName; 34 | } 35 | 36 | // if (libConfig.packages.indexOf(name) === -1) { 37 | // throw new Error(`Invalid name: ${name} is not part of "libConfig.packages" in package.json`); 38 | // } 39 | 40 | const scope = libConfig.scope ? `${libConfig.scope}/` : ''; 41 | return scope + name; 42 | } 43 | 44 | export function getPackageRoot(dirName: string, ...args: string[]): string { 45 | return root(FS_REF.SRC_CONTAINER, getPackageName(dirName), ...args); 46 | } 47 | 48 | /** 49 | * Returns the local (partial) package.json object of a package. 50 | * @param dirName 51 | * @return {any} 52 | */ 53 | export function getLocalPackageJSON(dirName: string): { [index: string]: any } & { libConfig: LocalLibConfig } { 54 | return jsonfile.readFileSync(root(FS_REF.SRC_CONTAINER, getPackageName(dirName), 'package.json')); 55 | } 56 | 57 | 58 | 59 | export function saveTempTsConfig(meta: PackageMetadata): void { 60 | jsonfile.writeFileSync(root(FS_REF.TS_CONFIG_TMP), meta.tsConfigObj, {spaces: 2}); 61 | } 62 | 63 | export function resolveWebpackConfig(config: string | any, ...args: any[]): any { 64 | if(typeof config === 'string') { 65 | return resolveWebpackConfig(require(config), ...args); 66 | } else if (typeof config === 'function') { 67 | return config(...args); 68 | } else if (config.__esModule === true && !!config.default) { 69 | return resolveWebpackConfig(config.default, ...args); 70 | } else { 71 | return config; 72 | } 73 | } 74 | 75 | 76 | 77 | /** 78 | * Minify and gzip a umd bundle. 79 | * 80 | * The output files will sit along side the umd bundle. 81 | * 82 | * NOTE: this is a sync operation. 83 | * 84 | * @param destDir the destination directory 85 | * @param umd the umd name (from metadata, not the while filename) of the bundle 86 | */ 87 | export function minifyAndGzip(destDir: string, srcNameNoExt: string) { 88 | const unminified = fs.readFileSync(Path.join(destDir, `${srcNameNoExt}.js`)).toString(); 89 | const minified = uglify.minify(unminified); 90 | const gzipBuffer = zlib.gzipSync(Buffer.from(minified.code)); 91 | 92 | fs.writeFileSync(Path.join(destDir, `${srcNameNoExt}.min.js`), minified.code, 'utf-8'); 93 | const zipStream = fs.createWriteStream(Path.join(destDir, `${srcNameNoExt}.js.gz`)); 94 | zipStream.write(gzipBuffer); 95 | zipStream.end(); 96 | 97 | const pct = num => 100 * Math.round(10000 * (1-num)) / 10000; 98 | 99 | console.log(` 100 | -------------------------------------- 101 | UMD Bundle info: ${srcNameNoExt} 102 | -------------------------------------- 103 | unminified: \t${unminified.length / 1000} KB 104 | minified: \t${minified.code.length / 1000} KB \t(${pct(minified.code.length / unminified.length)} %) 105 | gzipped: \t${gzipBuffer.length / 1000} KB \t(${pct(gzipBuffer.length / unminified.length)}) %, ${pct(gzipBuffer.length / minified.code.length)} %) 106 | -------------------------------------- 107 | `); 108 | } 109 | 110 | 111 | 112 | export function getModuleName(dirName: string): string { 113 | const scope = libConfig.scope ? `${libConfig.scope}/` : ''; 114 | return (scope ? scope.substr(1, scope.length - 2) + '.' : '') + voca.camelCase(dirName); 115 | } 116 | 117 | 118 | 119 | /** 120 | * Returns an alias list for webpack's configuration resolve property based on a packages list. 121 | * If no list supplied the whole list from package.json is used. 122 | * @param packages 123 | */ 124 | export function webpackAlias(...packages: string[]): { [id: string]: string } { 125 | if (packages.length === 0) { 126 | packages = libConfig.packages; 127 | } 128 | 129 | const scope = libConfig.scope ? `${libConfig.scope}/` : ''; 130 | 131 | return packages.reduce((curr, pkg) => { 132 | const pkgJson = getLocalPackageJSON(pkg); 133 | const entry = pkgJson.libConfig && pkgJson.libConfig.entry || 'index'; 134 | curr[scope + pkg + '$'] = `${scope + pkg}/src/${entry}.ts`; 135 | 136 | if (pkgJson.libConfig && Array.isArray(pkgJson.libConfig.libExtensions)) { 137 | pkgJson.libConfig.libExtensions.forEach( ext => { 138 | normalizeLibExtension(ext); 139 | // TODO: remove object 'ext', only string... move everything to local package.json of extension 140 | curr[`${scope + pkg}/${ext.dir}$`] = `${scope + pkg}/${ext.dir}/src/${ext.entry}`; 141 | }); 142 | } 143 | 144 | return curr; 145 | }, {}) 146 | } 147 | 148 | 149 | export function jestAlias(...packages: string[]): { [id: string]: string } { 150 | if (packages.length === 0) { 151 | packages = libConfig.packages; 152 | } 153 | 154 | const scope = libConfig.scope ? `${libConfig.scope}/` : ''; 155 | 156 | return packages.reduce((curr, pkg) => { 157 | const pkgJson = getLocalPackageJSON(pkg); 158 | const entry = pkgJson.libConfig && pkgJson.libConfig.entry || 'index'; 159 | curr[`^${scope + pkg}$`] = `/src/${scope + pkg}/src/${entry}.ts`; 160 | curr[`^${scope + pkg}/src/(.*)`] = `/src/${scope + pkg}/src/$1`; 161 | 162 | if (pkgJson.libConfig && Array.isArray(pkgJson.libConfig.libExtensions)) { 163 | pkgJson.libConfig.libExtensions.forEach( ext => { 164 | normalizeLibExtension(ext); 165 | // TODO: remove object 'ext', only string... move everything to local package.json of extension 166 | curr[`^${scope + pkg}/${ext.dir}$`] = `/src/${scope + pkg}/${ext.dir}/src/${ext.entry}`; 167 | curr[`^${scope + pkg}/${ext.dir}/src/(.*)`] = `/src/${scope + pkg}/${ext.dir}/src/$1`; 168 | }); 169 | } 170 | 171 | return curr; 172 | }, {}) 173 | } 174 | 175 | export type Promisify = { promise: Promise; resolve: (value?: T) => void; reject: (err: any) => void }; 176 | 177 | export function promisify(): Promisify { 178 | let resolve, reject; 179 | const promise = new Promise( (rs, rj) => { resolve = rs; reject = rj; }); 180 | return { promise, resolve, reject }; 181 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ngx-tree-select 2 | 3 | [](https://greenkeeper.io/) 4 | [](https://travis-ci.org/Crazyht/ngx-tree-select) 5 | [](https://badge.fury.io/js/ngx-tree-select) 6 | ## Features: 7 | - Dropdown with 'flat' items (Like normal select) 8 | - Dropdown with hierarchical items 9 | - Simple or multiple selected items 10 | - With hierarchical datas you can force child selection or allow select parent 11 | - ngModel & standard validation compliant 12 | - Can limit displayed selected items (... link allow your user to see entire selection) 13 | 14 | ## Installation 15 | 16 | This is how to install the components: 17 | 18 | ```bash 19 | npm install ngx-tree-select 20 | ``` 21 | 22 | or 23 | 24 | ```bash 25 | yarn add ngx-tree-select 26 | ``` 27 | 28 | And on your application module: 29 | 30 | ```ts 31 | import {NgxTreeSelectModule} from 'ngx-tree-select'; 32 | 33 | @NgModule({ 34 | declarations: [ ...], 35 | imports: [ 36 | BrowserModule, 37 | ...., 38 | NgxTreeSelectModule.forRoot({ 39 | allowFilter: true, 40 | filterPlaceholder: 'Type your filter here...', 41 | maxVisibleItemCount: 5, 42 | idField: 'id', 43 | textField: 'name', 44 | childrenField: 'children', 45 | allowParentSelection: true 46 | }) 47 | ], 48 | }) 49 | export class AppModule { } 50 | ``` 51 | 52 | See below for SystemJs / UMD installation. 53 | 54 | # Default options 55 | 56 | When you call ```NgxTreeSelectModule.forRoot``` you must pass default options. This options can be empty object "{}" or you can add one or more settings : 57 | 58 | - **allowFilter** : display filter input on dropdown 59 | - **filterPlaceholder** : determine placeholder text for filter 60 | - **maxVisibleItemCount** : determine maximum number of items are displayed on multiple select 61 | - **idField** : determine which property of your items is used as unique identifier 62 | - **textField** : determine which property of your items is displayed 63 | - **childrenField** : determine which property of yours items contains children items 64 | - **allowParentSelection** : if set to **true**, you can select parent, else when you select parent all children are selected 65 | - **expandMode** : Define which item are expand at initilization. Possible value are : **None**, **Selection** or **All** 66 | 67 | # Using the Tree Select 68 | 69 | We will need to add first a version of Font Awesome to our page, for example: 70 | 71 | ```html 72 | 73 | ``` 74 | 75 | Then we can use the Tree Select like this: 76 | 77 | ```html 78 | 79 | 90 | 91 | Simple select is required 92 | 93 | 94 | 95 | 96 | 111 | 112 | 113 | Multiple select is required 114 | You must choose at least 2 items on Multiple select 115 | You must choose maximum 4 items on Multiple select 116 | 117 | ``` 118 | 119 | # Component attributes 120 | 121 | When you place **tree-select** on HTML template you can define : 122 | 123 | - **items** : list of items 124 | - **multiple** : allow multiple selection 125 | - **disabled** : disable component 126 | - **allowFilter** : display filter input on dropdown 127 | - **filterPlaceholder** : determine placeholder text for filter 128 | - **maxVisibleItemCount** : determine maximum number of items are displayed on multiple select 129 | - **idField** : determine which property of your items is used as unique identifier 130 | - **textField** : determine which property of your items is displayed 131 | - **childrenField** : determine which property of yours items contains children items 132 | - **allowParentSelection** : if set to **true**, you can select parent, else when you select parent all children are selected 133 | - **expandMode** : Define which item are expand at initilization. Possible value are : **None**, **Selection** or **All** 134 | 135 | **tree-select** component use default options define when you call ```NgxTreeSelectModule.forRoot``` except if you override it with attribute on HTML template. 136 | 137 | # Running the Demo Application 138 | This command will build and start the demo application: 139 | 140 | ```bash 141 | npm start 142 | ``` 143 | 144 | # Running This Module In Development 145 | 146 | First let's build the library using this command: 147 | 148 | ```bash 149 | npm run lib:build 150 | ``` 151 | 152 | 153 | Then let's link it: 154 | 155 | ```bash 156 | cd dist_package\ngx-tree-select 157 | npm link 158 | ``` 159 | 160 | 161 | On another folder on the same machine where we have for example a running Angular CLI, we then do: 162 | 163 | ```bash 164 | npm link ngx-tree-select 165 | ``` 166 | 167 | 168 | # Running the Tests 169 | 170 | The tests can be executed with the following commands: 171 | 172 | ```bash 173 | npm run test 174 | npm run e2e 175 | ``` 176 | 177 | ## Using SystemJs via the UMD bundle ? 178 | 179 | Make sure to add this to your `map` configuration, if you need the module served from a CDN: 180 | 181 | ```javascript 182 | map: { 183 | 184 | ... 185 | 'ngx-tree-select': 'https://unpkg.com/ngx-tree-select@/ngx-tree-select.rollup.umd.min.js' 186 | } 187 | ``` 188 | 189 | Otherwise if serving from `node_modules`directly: 190 | 191 | ```javascript 192 | map: { 193 | ... 194 | 'ngx-tree-select': 'node_modules/ngx-tree-select/bundles/ngx-tree-select.umd.min.js' 195 | } 196 | ``` 197 | 198 | And in our packages property: 199 | 200 | ```javascript 201 | packages: { 202 | ... 203 | 'ngx-tree-select': { 204 | main: 'index.js', 205 | defaultExtension: 'js' 206 | } 207 | 208 | } 209 | ``` 210 | 211 | 212 | # License 213 | 214 | [MIT](https://opensource.org/licenses/MIT) 215 | -------------------------------------------------------------------------------- /scripts/gulp/build.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | import * as Path from 'path'; 3 | import * as webpack from 'webpack'; 4 | import * as jsonfile from 'jsonfile'; 5 | import * as del from 'del'; 6 | 7 | import { ScriptTarget, ModuleKind } from 'typescript'; 8 | 9 | import * as util from '../util'; 10 | 11 | const mv = require('mv'); 12 | const rollup = require('rollup-stream'); 13 | const source = require('vinyl-source-stream'); 14 | const buffer = require('vinyl-buffer'); 15 | const rename = require('gulp-rename'); 16 | const sorcery = require('sorcery'); 17 | const convert = require('convert-source-map'); 18 | 19 | @util.GulpClass.Gulpclass() 20 | export class Gulpfile { 21 | 22 | @util.GulpClass.Task('!build:webpack') 23 | buildWebpack() { 24 | const config = util.resolveWebpackConfig(util.root(util.FS_REF.WEBPACK_CONFIG), util.currentPackage()); 25 | 26 | /* There are 2 webpack processes running one after the other. 27 | - 1st webpack pass: es2015 output 28 | - 2nd webpack pass: es5 output. 29 | 30 | In theory we should use only 1 pass (es2015) and bundle it to an ES2015 bundle. 31 | We then take that bundle and use TS to down grade it to a ES5 bundle. 32 | 33 | This works great except for source maps. 34 | When trying to use 'sorcery' to map the code it throws due to memory allocation (probably some kind of recursion) 35 | 36 | If we remove set 'sourceMap: false' & 'inlineSources: false' when doing the down grade 37 | the output source map is miss aligned (it refers to the ES2015 map) 38 | 39 | see task "build:fesm:es5" (and the commented code) for more details.1 40 | */ 41 | 42 | 43 | return new Promise( (resolve, reject) => webpack(config).run((err, stats) => err ? reject(err) : resolve(stats)) ) 44 | .then( () => { 45 | const p = util.root(util.currentPackage().tsConfigObj.compilerOptions.outDir); 46 | del.sync(p); 47 | 48 | const tsConfig = jsonfile.readFileSync(util.root(util.FS_REF.TS_CONFIG_TMP)); 49 | tsConfig.compilerOptions.target = 'es5'; 50 | jsonfile.writeFileSync(util.root(util.FS_REF.TS_CONFIG_TMP), tsConfig, {spaces: 2}); 51 | 52 | return new Promise( (resolve, reject) => webpack(config).run((err, stats) => err ? reject(err) : resolve(stats)) ); 53 | }) 54 | .then( () => { 55 | const p = util.root(util.currentPackage().tsConfigObj.compilerOptions.outDir); 56 | const copyInst = util.getCopyInstruction(util.currentPackage()); 57 | 58 | if (util.currentPackage().inlineResources) { 59 | util.inlinePackageMetadataFiles(copyInst.from); 60 | util.inlineResourcesForDirectory(copyInst.from, true); 61 | } 62 | 63 | 64 | return new Promise( (resolve, reject) => { 65 | mv(copyInst.from, copyInst.toSrc, {mkdirp: true}, (err?) => { 66 | if (err) { 67 | reject(err); 68 | } else { 69 | resolve(del(p)); 70 | } 71 | }); 72 | }) 73 | .then( () => { 74 | /* 75 | Angular compiler with 'flatModuleOutFile' turned on creates an entry JS file with a matching d.ts 76 | file and an aggregated metadata.json file. 77 | 78 | This is done by creating a corresponding TS file (to the output JS file). 79 | The side-effect is a source map reference to the TS file. 80 | 81 | Since the TS is virtual and does not exists we need to remove the comment so the source maps 82 | will not break. 83 | */ 84 | const flatModuleJsPath = Path.join(copyInst.toSrc, `${util.getMainOutputFileName(util.currentPackage())}.js`); 85 | const withoutComments = convert.removeComments(fs.readFileSync(flatModuleJsPath, 'utf-8')); 86 | fs.writeFileSync(flatModuleJsPath, withoutComments, 'utf-8'); 87 | }); 88 | }); 89 | } 90 | 91 | @util.GulpClass.Task('!build:rollup:fesm') 92 | buildRollupFesm() { 93 | const meta = util.currentPackage(); 94 | const copyInst = util.getCopyInstruction(meta); 95 | 96 | const rollupConfig: any = { 97 | external: meta.externals, 98 | moduleName: meta.moduleName 99 | }; 100 | 101 | util.tryRunHook(meta.dir, 'rollupFESM', rollupConfig); 102 | 103 | return util.createRollupBundle({ 104 | moduleName: rollupConfig.moduleName, 105 | entry: `${copyInst.toSrc}/${util.getMainOutputFileName(meta)}.js`, 106 | dest: Path.join(copyInst.toBundle, `${meta.umd}.js`), 107 | format: 'es', 108 | external: rollupConfig.external, 109 | globals: rollupConfig.globals 110 | }).then( () => sorcery.load(Path.join(copyInst.toBundle, `${meta.umd}.js`)).then( chain => chain.write() )); 111 | } 112 | 113 | @util.GulpClass.Task('!build:fesm:es5') 114 | buildFesmEs5() { 115 | const meta = util.currentPackage(); 116 | const copyInst = util.getCopyInstruction(meta); 117 | 118 | const rollupConfig: any = { 119 | external: meta.externals, 120 | moduleName: meta.moduleName 121 | }; 122 | 123 | util.tryRunHook(meta.dir, 'rollupFESM', rollupConfig); 124 | 125 | return util.createRollupBundle({ 126 | moduleName: rollupConfig.moduleName, 127 | entry: `${copyInst.toSrc}/${util.getMainOutputFileName(meta)}.js`, 128 | dest: Path.join(copyInst.toBundle, `${meta.umd}.es5.js`), 129 | format: 'es', 130 | external: rollupConfig.external, 131 | globals: rollupConfig.globals 132 | }).then( () => sorcery.load(Path.join(copyInst.toBundle, `${meta.umd}.es5.js`)).then( chain => chain.write() )); 133 | 134 | // const meta = util.currentPackage(); 135 | // const copyInst = util.getCopyInstruction(meta); 136 | // 137 | // // Downlevel FESM-2015 file to ES5. 138 | // util.transpileFile( 139 | // Path.join(copyInst.toBundle, `${meta.umd}.js`), 140 | // Path.join(copyInst.toBundle, `${meta.umd}.es5.js`), 141 | // { 142 | // importHelpers: true, 143 | // target: ScriptTarget.ES5, 144 | // module: ModuleKind.ES2015, 145 | // allowJs: true, 146 | // sourceMap: true, 147 | // inlineSources: true 148 | // }); 149 | // 150 | // return sorcery.load(Path.join(copyInst.toBundle, `${meta.umd}.es5.js`)).then( chain => chain.write() ); 151 | } 152 | 153 | @util.GulpClass.Task('!build:rollup:umd') // or use provided callback instead 154 | buildRollupUmd() { 155 | const meta = util.currentPackage(); 156 | 157 | const copyInst = util.getCopyInstruction(meta); 158 | 159 | const rollupConfig = { 160 | external: meta.externals, 161 | globals: { 162 | typescript: 'ts' 163 | }, 164 | moduleName: meta.moduleName 165 | }; 166 | 167 | util.tryRunHook(meta.dir, 'rollupUMD', rollupConfig); 168 | 169 | return util.createRollupBundle({ 170 | moduleName: rollupConfig.moduleName, 171 | entry: Path.join(copyInst.toBundle, `${meta.umd}.es5.js`), 172 | dest: Path.join(copyInst.toBundle, `${meta.umd}.rollup.umd.js`), 173 | format: 'umd', 174 | external: rollupConfig.external, 175 | globals: rollupConfig.globals 176 | }).then( () => sorcery.load(Path.join(copyInst.toBundle, `${meta.umd}.rollup.umd.js`)).then( chain => chain.write() )); 177 | } 178 | 179 | @util.GulpClass.Task('!minifyAndGzip') 180 | minifyAndGzip(done) { 181 | try { 182 | const meta = util.currentPackage(); 183 | const copyInst = util.getCopyInstruction(meta); 184 | 185 | util.minifyAndGzip(copyInst.toBundle, `${meta.umd}.webpack.umd`); 186 | util.minifyAndGzip(copyInst.toBundle, `${meta.umd}.rollup.umd`); 187 | done() 188 | } catch (err) { 189 | done(err); 190 | } 191 | } 192 | 193 | @util.GulpClass.Task(`!pureAnnotation`) 194 | pureAnnotation(done) { 195 | try { 196 | const meta = util.currentPackage(); 197 | const copyInst = util.getCopyInstruction(meta); 198 | 199 | util.addPureAnnotationsToFile(Path.join(copyInst.toBundle, `${meta.umd}.es5.js`)); 200 | 201 | done() 202 | } catch (err) { 203 | done(err); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | # [0.15.0](https://github.com/crazyht/ngx-tree-select/compare/v0.12.0...v0.14.0) (2020-02-19) 7 | 8 | ### Bug Fixes 9 | 10 | * Remove rxjs-compat 11 | 12 | 13 | # [0.14.0](https://github.com/crazyht/ngx-tree-select/compare/v0.12.0...v0.14.0) (2019-01-31) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * build ([eb68c2c](https://github.com/crazyht/ngx-tree-select/commit/eb68c2c)) 19 | * Change Angular version support ([5605bdc](https://github.com/crazyht/ngx-tree-select/commit/5605bdc)) 20 | * Click on caret not open dropdown ([d705968](https://github.com/crazyht/ngx-tree-select/commit/d705968)) 21 | * Expand items during search (if match) ([48d14e0](https://github.com/crazyht/ngx-tree-select/commit/48d14e0)) 22 | * Expand items during search (if match) ([14f3c35](https://github.com/crazyht/ngx-tree-select/commit/14f3c35)) 23 | * isDirty is always set to true ([db24dd1](https://github.com/crazyht/ngx-tree-select/commit/db24dd1)) 24 | * lint ([8c03623](https://github.com/crazyht/ngx-tree-select/commit/8c03623)) 25 | * lint error ([8ec7b7c](https://github.com/crazyht/ngx-tree-select/commit/8ec7b7c)) 26 | * tree select expandMode ([586c71b](https://github.com/crazyht/ngx-tree-select/commit/586c71b)) 27 | 28 | 29 | ### Features 30 | 31 | * Add expand mode ([72a9c01](https://github.com/crazyht/ngx-tree-select/commit/72a9c01)) 32 | * restructure hierarchy when item and child have same name ([29ac95f](https://github.com/crazyht/ngx-tree-select/commit/29ac95f)) 33 | 34 | 35 | 36 | 37 | ## [0.13.3](https://github.com/crazyht/ngx-tree-select/compare/v0.13.2...v0.13.3) (2018-02-09) 38 | 39 | 40 | ### Bug Fixes 41 | 42 | * build ([eb68c2c](https://github.com/crazyht/ngx-tree-select/commit/eb68c2c)) 43 | * Expand items during search (if match) ([48d14e0](https://github.com/crazyht/ngx-tree-select/commit/48d14e0)) 44 | * tree select expandMode ([586c71b](https://github.com/crazyht/ngx-tree-select/commit/586c71b)) 45 | 46 | 47 | 48 | 49 | ## [0.13.2](https://github.com/crazyht/ngx-tree-select/compare/v0.12.0...v0.13.2) (2018-02-07) 50 | 51 | 52 | ### Bug Fixes 53 | 54 | * Change Angular version support ([5605bdc](https://github.com/crazyht/ngx-tree-select/commit/5605bdc)) 55 | * Click on caret not open dropdown ([d705968](https://github.com/crazyht/ngx-tree-select/commit/d705968)) 56 | * Expand items during search (if match) ([14f3c35](https://github.com/crazyht/ngx-tree-select/commit/14f3c35)) 57 | * isDirty is always set to true ([db24dd1](https://github.com/crazyht/ngx-tree-select/commit/db24dd1)) 58 | * lint error ([8ec7b7c](https://github.com/crazyht/ngx-tree-select/commit/8ec7b7c)) 59 | 60 | 61 | ### Features 62 | 63 | * Add expand mode ([72a9c01](https://github.com/crazyht/ngx-tree-select/commit/72a9c01)) 64 | 65 | 66 | 67 | 68 | ## [0.13.1](https://github.com/crazyht/ngx-tree-select/compare/v0.13.0...v0.13.1) (2018-02-07) 69 | 70 | 71 | ### Bug Fixes 72 | 73 | * Fix bug ExpandMode ([b05befd](https://github.com/Crazyht/ngx-tree-select/commit/b05befdaef1c7d1b5a8eac7eb58d8567191b605f)) 74 | 75 | 76 | 77 | 78 | # [0.13.0](https://github.com/crazyht/ngx-tree-select/compare/v0.12.0...v0.13.0) (2017-12-13) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * Change Angular version support ([5605bdc](https://github.com/crazyht/ngx-tree-select/commit/5605bdc)) 84 | * Click on caret not open dropdown ([d705968](https://github.com/crazyht/ngx-tree-select/commit/d705968)) 85 | * isDirty is always set to true ([db24dd1](https://github.com/crazyht/ngx-tree-select/commit/db24dd1)) 86 | * lint error ([8ec7b7c](https://github.com/crazyht/ngx-tree-select/commit/8ec7b7c)) 87 | 88 | 89 | ### Features 90 | 91 | * Add expand mode ([72a9c01](https://github.com/crazyht/ngx-tree-select/commit/72a9c01)) 92 | 93 | 94 | 95 | 96 | # [0.12.0](https://github.com/crazyht/ngx-tree-select/compare/v0.10.0...v0.12.0) (2017-10-30) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * Disable autocomplete on filter ([0a5ea4f](https://github.com/crazyht/ngx-tree-select/commit/0a5ea4f)) 102 | * Dropdown no open when click on tree-select ([5213370](https://github.com/crazyht/ngx-tree-select/commit/5213370)) 103 | * initial select is not displayed when list of items contains string value ([280e61c](https://github.com/crazyht/ngx-tree-select/commit/280e61c)) 104 | * set gecko false for webdriver manager. See https://github.com/angular/webdriver-manager/issues/216 ([4c87d6b](https://github.com/crazyht/ngx-tree-select/commit/4c87d6b)) 105 | * Simple Select where undefined at init value ([498112e](https://github.com/crazyht/ngx-tree-select/commit/498112e)) 106 | * Add class for Bootstrap 4 beta 6 107 | 108 | ### Features 109 | 110 | * Add option filterCaseSensitive ([2edfe7d](https://github.com/crazyht/ngx-tree-select/commit/2edfe7d)) 111 | 112 | 113 | 114 | 115 | ## [0.11.1](https://github.com/crazyht/ngx-tree-select/compare/v0.11.0...v0.11.1) (2017-07-31) 116 | 117 | 118 | ### Bug Fixes 119 | 120 | * Dropdown no open when click on tree-select ([5213370](https://github.com/crazyht/ngx-tree-select/commit/5213370)) 121 | 122 | 123 | 124 | 125 | # [0.11.0](https://github.com/crazyht/ngx-tree-select/compare/v0.10.0...v0.11.0) (2017-07-30) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * initial select is not displayed when list of items contains string value ([280e61c](https://github.com/crazyht/ngx-tree-select/commit/280e61c)) 131 | * set gecko false for webdriver manager. See https://github.com/angular/webdriver-manager/issues/216 ([4c87d6b](https://github.com/crazyht/ngx-tree-select/commit/4c87d6b)) 132 | 133 | 134 | 135 | 136 | ## [0.10.1](https://github.com/Crazyht/crazy-select/compare/v0.10.0...v0.10.1) (2017-07-19) 137 | 138 | 139 | ### Bug Fixes 140 | 141 | * initial select is not displayed when list of items contains string value ([280e61c](https://github.com/Crazyht/crazy-select/commit/280e61c)) 142 | * set gecko false for webdriver manager. See https://github.com/angular/webdriver-manager/issues/216 ([4c87d6b](https://github.com/Crazyht/crazy-select/commit/4c87d6b)) 143 | 144 | 145 | 146 | 147 | # [0.10.0](https://github.com/Crazyht/crazy-select/compare/v0.9.8...v0.10.0) (2017-07-13) 148 | 149 | 150 | ### Bug Fixes 151 | 152 | * can select parent on simple tree with onlySelectParent=true ([7f76f9f](https://github.com/Crazyht/crazy-select/commit/7f76f9f)) 153 | * click on show more open dropdown ([2dbf891](https://github.com/Crazyht/crazy-select/commit/2dbf891)) 154 | * **demo:** same id between continent & country (Africa & Afghanistan) ([06027a8](https://github.com/Crazyht/crazy-select/commit/06027a8)) 155 | 156 | 157 | ### Features 158 | 159 | * add default options for allowFilter ([4e255ad](https://github.com/Crazyht/crazy-select/commit/4e255ad)) 160 | * add default options for allowParentSelection ([55d172c](https://github.com/Crazyht/crazy-select/commit/55d172c)) 161 | * add default options for idField, textField & childrenField ([2e6a6c1](https://github.com/Crazyht/crazy-select/commit/2e6a6c1)) 162 | * Allow unshow more items ([0001181](https://github.com/Crazyht/crazy-select/commit/0001181)) 163 | 164 | 165 | 166 | 167 | ## [0.9.8](https://github.com/Crazyht/ngx-tree-select/compare/v0.1.5...v0.9.8) (2017-07-12) 168 | 169 | 170 | ### Bug Fixes 171 | 172 | * **ssr:** fixed ServerRendering error ([2224478](https://github.com/Crazyht/ngx-tree-select/commit/2224478)) 173 | * Error in console when value in model = undefined ([2544f0d](https://github.com/Crazyht/ngx-tree-select/commit/2544f0d)) 174 | 175 | 176 | 177 | 178 | ## [0.9.7](https://github.com/Crazyht/crazy-select/compare/v0.9.6...v0.9.7) (2017-07-12) 179 | 180 | 181 | 182 | 183 | ## [0.9.6](https://github.com/Crazyht/crazy-select/compare/v0.9.5...v0.9.6) (2017-07-10) 184 | 185 | 186 | ### Bug Fixes 187 | 188 | * **ssr:** fixed ServerRendering error ([2224478](https://github.com/Crazyht/crazy-select/commit/2224478)) 189 | 190 | 191 | 192 | 193 | ## [0.9.5](https://github.com/Crazyht/crazy-select/compare/v0.1.5...v0.9.5) (2017-07-10) 194 | -------------------------------------------------------------------------------- /config/webpack.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: @AngularClass 3 | */ 4 | 5 | const helpers = require('./helpers'); 6 | 7 | /** 8 | * Webpack Plugins 9 | */ 10 | const ProvidePlugin = require('webpack/lib/ProvidePlugin'); 11 | const DefinePlugin = require('webpack/lib/DefinePlugin'); 12 | const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); 13 | const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin'); 14 | 15 | /** 16 | * Webpack Constants 17 | */ 18 | const ENV = process.env.ENV = process.env.NODE_ENV = 'test'; 19 | 20 | /** 21 | * Webpack configuration 22 | * 23 | * See: http://webpack.github.io/docs/configuration.html#cli 24 | */ 25 | module.exports = function (options) { 26 | return { 27 | 28 | /** 29 | * Source map for Karma from the help of karma-sourcemap-loader & karma-webpack 30 | * 31 | * Do not change, leave as is or it wont work. 32 | * See: https://github.com/webpack/karma-webpack#source-maps 33 | */ 34 | devtool: 'inline-source-map', 35 | 36 | /** 37 | * Options affecting the resolving of modules. 38 | * 39 | * See: http://webpack.github.io/docs/configuration.html#resolve 40 | */ 41 | resolve: { 42 | 43 | /** 44 | * An array of extensions that should be used to resolve modules. 45 | * 46 | * See: http://webpack.github.io/docs/configuration.html#resolve-extensions 47 | */ 48 | extensions: ['.ts', '.js'], 49 | 50 | /** 51 | * Make sure root is src 52 | */ 53 | modules: [helpers.root('src'), 'node_modules'] 54 | 55 | }, 56 | 57 | /** 58 | * Options affecting the normal modules. 59 | * 60 | * See: http://webpack.github.io/docs/configuration.html#module 61 | * 62 | * 'use:' revered back to 'loader:' as a temp. workaround for #1188 63 | * See: https://github.com/AngularClass/angular2-webpack-starter/issues/1188#issuecomment-262872034 64 | */ 65 | module: { 66 | 67 | rules: [ 68 | 69 | /** 70 | * Source map loader support for *.js files 71 | * Extracts SourceMaps for source files that as added as sourceMappingURL comment. 72 | * 73 | * See: https://github.com/webpack/source-map-loader 74 | */ 75 | { 76 | enforce: 'pre', 77 | test: /\.js$/, 78 | loader: 'source-map-loader', 79 | exclude: [ 80 | /** 81 | * These packages have problems with their sourcemaps 82 | */ 83 | helpers.root('node_modules/rxjs'), 84 | helpers.root('node_modules/@angular') 85 | ] 86 | }, 87 | 88 | /** 89 | * Typescript loader support for .ts and Angular 2 async routes via .async.ts 90 | * 91 | * See: https://github.com/s-panferov/awesome-typescript-loader 92 | */ 93 | { 94 | test: /\.ts$/, 95 | use: [ 96 | { 97 | loader: 'awesome-typescript-loader', 98 | query: { 99 | /** 100 | * Use inline sourcemaps for "karma-remap-coverage" reporter 101 | */ 102 | sourceMap: false, 103 | inlineSourceMap: true, 104 | compilerOptions: { 105 | 106 | /** 107 | * Remove TypeScript helpers to be injected 108 | * below by DefinePlugin 109 | */ 110 | removeComments: true 111 | 112 | } 113 | }, 114 | }, 115 | 'angular2-template-loader' 116 | ], 117 | exclude: [/\.e2e\.ts$/] 118 | }, 119 | 120 | /** 121 | * Json loader support for *.json files. 122 | * 123 | * See: https://github.com/webpack/json-loader 124 | */ 125 | { 126 | test: /\.json$/, 127 | loader: 'json-loader', 128 | exclude: [helpers.root('src/demo/index.html')] 129 | }, 130 | 131 | /** 132 | * Raw loader support for *.css files 133 | * Returns file content as string 134 | * 135 | * See: https://github.com/webpack/raw-loader 136 | */ 137 | { 138 | test: /\.css$/, 139 | loader: ['to-string-loader', 'css-loader'], 140 | exclude: [helpers.root('src/demo/index.html')] 141 | }, 142 | 143 | /** 144 | * Raw loader support for *.scss files 145 | * 146 | * See: https://github.com/webpack/raw-loader 147 | */ 148 | { 149 | test: /\.scss$/, 150 | loader: ['raw-loader', 'sass-loader'], 151 | exclude: [helpers.root('src/demo/index.html')] 152 | }, 153 | 154 | /** 155 | * Raw loader support for *.html 156 | * Returns file content as string 157 | * 158 | * See: https://github.com/webpack/raw-loader 159 | */ 160 | { 161 | test: /\.html$/, 162 | loader: 'raw-loader', 163 | exclude: [helpers.root('src/demo/index.html')] 164 | }, 165 | 166 | /** 167 | * Instruments JS files with Istanbul for subsequent code coverage reporting. 168 | * Instrument only testing sources. 169 | * 170 | * See: https://github.com/deepsweet/istanbul-instrumenter-loader 171 | */ 172 | { 173 | enforce: 'post', 174 | test: /\.(js|ts)$/, 175 | loader: 'istanbul-instrumenter-loader', 176 | include: helpers.root('src'), 177 | exclude: [ 178 | /\.(e2e|spec)\.ts$/, 179 | /node_modules/ 180 | ] 181 | } 182 | 183 | ] 184 | }, 185 | 186 | /** 187 | * Add additional plugins to the compiler. 188 | * 189 | * See: http://webpack.github.io/docs/configuration.html#plugins 190 | */ 191 | plugins: [ 192 | 193 | /** 194 | * Plugin: DefinePlugin 195 | * Description: Define free variables. 196 | * Useful for having development builds with debug logging or adding global constants. 197 | * 198 | * Environment helpers 199 | * 200 | * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin 201 | * 202 | * NOTE: when adding more properties make sure you include them in custom-typings.d.ts 203 | */ 204 | new DefinePlugin({ 205 | 'ENV': JSON.stringify(ENV), 206 | 'HMR': false, 207 | 'process.env': { 208 | 'ENV': JSON.stringify(ENV), 209 | 'NODE_ENV': JSON.stringify(ENV), 210 | 'HMR': false, 211 | } 212 | }), 213 | 214 | /** 215 | * Plugin: ContextReplacementPlugin 216 | * Description: Provides context to Angular's use of System.import 217 | * 218 | * See: https://webpack.github.io/docs/list-of-plugins.html#contextreplacementplugin 219 | * See: https://github.com/angular/angular/issues/11580 220 | */ 221 | new ContextReplacementPlugin( 222 | /** 223 | * The (\\|\/) piece accounts for path separators in *nix and Windows 224 | */ 225 | /angular(\\|\/)core(\\|\/)@angular/, 226 | helpers.root('src'), // location of your src 227 | { 228 | /** 229 | * your Angular Async Route paths relative to this root directory 230 | */ 231 | } 232 | ), 233 | 234 | /** 235 | * Plugin LoaderOptionsPlugin (experimental) 236 | * 237 | * See: https://gist.github.com/sokra/27b24881210b56bbaff7 238 | */ 239 | new LoaderOptionsPlugin({ 240 | debug: false, 241 | options: { 242 | /** 243 | * legacy options go here 244 | */ 245 | } 246 | }), 247 | 248 | ], 249 | 250 | /** 251 | * Disable performance hints 252 | * 253 | * See: https://github.com/a-tarasyuk/rr-boilerplate/blob/master/webpack/dev.config.babel.js#L41 254 | */ 255 | performance: { 256 | hints: false 257 | }, 258 | 259 | /** 260 | * Include polyfills or mocks for various node stuff 261 | * Description: Node configuration 262 | * 263 | * See: https://webpack.github.io/docs/configuration.html#node 264 | */ 265 | node: { 266 | global: true, 267 | process: false, 268 | crypto: 'empty', 269 | module: false, 270 | clearImmediate: false, 271 | setImmediate: false 272 | } 273 | 274 | }; 275 | }; 276 | --------------------------------------------------------------------------------