├── schematics ├── library-starter │ ├── files │ │ └── projects │ │ │ └── __name@dasherize__ │ │ │ ├── README.md │ │ │ ├── src │ │ │ ├── public-api.ts │ │ │ └── test.ts │ │ │ ├── ng-package.json │ │ │ ├── tsconfig.spec.json │ │ │ ├── package.json │ │ │ ├── tsconfig.lib.json │ │ │ ├── karma.conf.js │ │ │ └── LICENSE │ ├── schema.json │ └── index.ts └── collection.json ├── commitlint.config.js ├── prettier.config.js ├── projects ├── demo │ ├── src │ │ ├── environments │ │ │ ├── environment.ts │ │ │ └── environment.prod.ts │ │ ├── favicon.ico │ │ ├── polyfills.ts │ │ ├── app │ │ │ ├── one │ │ │ │ └── one.component.ts │ │ │ ├── two │ │ │ │ └── two.component.ts │ │ │ ├── app.component.ts │ │ │ ├── user │ │ │ │ ├── skeleton │ │ │ │ │ └── user-skeleton.component.ts │ │ │ │ ├── user.service.ts │ │ │ │ ├── user.resolve.ts │ │ │ │ ├── user.component.ts │ │ │ │ └── user.module.ts │ │ │ ├── app.component.html │ │ │ ├── app.server.module.ts │ │ │ ├── app.routes.ts │ │ │ └── app.browser.module.ts │ │ ├── index.html │ │ ├── styles.css │ │ ├── main.server.ts │ │ ├── main.browser.ts │ │ └── test.ts │ ├── tsconfig.json │ ├── tsconfig.demo.json │ ├── tsconfig.spec.json │ ├── tsconfig.server.json │ ├── .gitignore │ ├── package.json │ ├── karma.conf.js │ ├── angular.json │ └── server.ts └── navigation-skeleton │ ├── src │ ├── navigation-skeleton.component.less │ ├── public-api.ts │ ├── animations │ │ ├── content-appear.ts │ │ └── skeleton-leave.ts │ ├── navigation-skeleton.module.ts │ ├── navigation-skeleton.component.html │ ├── navigation-skeleton.module.spec.ts │ ├── test.ts │ ├── navigation-skeleton.component.ts │ └── navigation-skeleton.component.spec.ts │ ├── ng-package.json │ ├── tsconfig.spec.json │ ├── tsconfig.lib.json │ ├── package.json │ ├── karma.conf.js │ └── LICENSE ├── tslint.json ├── .editorconfig ├── .stylelintrc ├── scripts ├── copyReadme.js └── syncVersions.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── workflows │ └── ci.yml └── PULL_REQUEST_TEMPLATE.md ├── tsconfig.schematics.json ├── tsconfig.json ├── .gitignore ├── CONTRIBUTING.md ├── CHANGELOG.md ├── README.md ├── CODE_OF_CONDUCT.md ├── package.json ├── angular.json └── LICENSE /schematics/library-starter/files/projects/__name@dasherize__/README.md: -------------------------------------------------------------------------------- 1 | # <%= name %> 2 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ['@commitlint/config-conventional']}; 2 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require('@tinkoff/linters/prettier/prettier.config'), 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | }; 4 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/navigation-skeleton.component.less: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | } 4 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tinkoff/navigation-skeleton/HEAD/projects/demo/src/favicon.ico -------------------------------------------------------------------------------- /projects/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "files": ["src/main.browser.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /projects/demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/es6/reflect'; 2 | import 'core-js/es7/reflect'; 3 | import 'zone.js/dist/zone'; 4 | -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Public API Surface of <%= dasherize(name) %> 3 | */ 4 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.demo.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@tinkoff/linters/tslint/bases/prettier.tslint.json"], 3 | "rules": { 4 | "ordered-imports": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /projects/demo/src/app/one/one.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | template: `

1

`, 5 | }) 6 | export class OneComponent {} 7 | -------------------------------------------------------------------------------- /projects/demo/src/app/two/two.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | template: `

2

`, 5 | }) 6 | export class TwoComponent {} 7 | -------------------------------------------------------------------------------- /projects/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Library demo 4 | 5 | 6 | loading 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/demo/src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, 'BlinkMacSystemFont', 'Segoe UI', system-ui, 'Roboto', 'Helvetica Neue', sans-serif; 3 | color: #333; 4 | margin: 0; 5 | } 6 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'my-app', 5 | templateUrl: './app.component.html', 6 | }) 7 | export class AppComponent {} 8 | -------------------------------------------------------------------------------- /projects/demo/src/app/user/skeleton/user-skeleton.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | template: `

Loading, please wait...

`, 5 | }) 6 | export class UserSkeletonComponent {} 7 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/navigation-skeleton", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/<%= dasherize(name) %>", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["src/test.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Public API Surface of navigation-skeleton 3 | */ 4 | export * from './animations/content-appear'; 5 | export * from './animations/skeleton-leave'; 6 | export * from './navigation-skeleton.component'; 7 | export * from './navigation-skeleton.module'; 8 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["src/test.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": ["jasmine", "node"] 6 | }, 7 | "files": ["src/test.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /projects/demo/src/main.server.ts: -------------------------------------------------------------------------------- 1 | import {enableProdMode} from '@angular/core'; 2 | import {environment} from './environments/environment'; 3 | 4 | if (environment.production) { 5 | enableProdMode(); 6 | } 7 | 8 | export {AppServerModule} from './app/app.server.module'; 9 | export {renderModule, renderModuleFactory} from '@angular/platform-server'; 10 | -------------------------------------------------------------------------------- /schematics/collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json", 3 | "schematics": { 4 | "library-starter": { 5 | "description": "A blank schematic.", 6 | "factory": "./library-starter/index#libraryStarter", 7 | "schema": "./library-starter/schema.json" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@tinkoff/linters/stylelint/bases/prettier.stylelint.json"], 3 | "rules": { 4 | "value-keyword-case": null, 5 | "selector-type-no-unknown": [ 6 | true, 7 | { 8 | "ignore": ["custom-elements"], 9 | "ignoreTypes": ["/^/deep/"] 10 | } 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 1 | 2 | 3 | 2 | 4 | 5 | User 6 | 7 |

Click on "User" to see skeleton

8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/animations/content-appear.ts: -------------------------------------------------------------------------------- 1 | import { 2 | animate, 3 | AnimationTriggerMetadata, 4 | style, 5 | transition, 6 | trigger, 7 | } from '@angular/animations'; 8 | 9 | export const contentAppear: AnimationTriggerMetadata = trigger('contentAppear', [ 10 | transition(':enter', [style({opacity: 0}), animate(200, style({opacity: 1}))]), 11 | ]); 12 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc-server", 5 | "module": "commonjs", 6 | "types": ["node"] 7 | }, 8 | "files": ["src/main.server.ts", "server.ts"], 9 | "angularCompilerOptions": { 10 | "entryModule": "./src/app/app.server.module#AppServerModule" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= dasherize(name) %>", 3 | "version": "1.0.0", 4 | "peerDependencies": { 5 | "@angular/core": ">=9.0.0" 6 | }, 7 | "description": "", 8 | "keywords": [], 9 | "license": "Apache-2.0", 10 | "authors": [], 11 | "repository": "", 12 | "bugs": "", 13 | "homepage": "" 14 | } 15 | -------------------------------------------------------------------------------- /scripts/copyReadme.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const DIST_LIB_PATH = 'dist/navigation-skeleton/'; 3 | const README_PATH = 'README.md'; 4 | const DIST_README_PATH = DIST_LIB_PATH + README_PATH; 5 | 6 | // Copy README.md into dist folder 7 | copyReadme(); 8 | 9 | function copyReadme() { 10 | if (fs.existsSync(README_PATH)) { 11 | fs.copyFile(README_PATH, DIST_README_PATH, () => {}); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /projects/demo/src/app/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Observable, timer} from 'rxjs'; 3 | import {mapTo} from 'rxjs/operators'; 4 | 5 | export interface User { 6 | name: string; 7 | } 8 | 9 | @Injectable({providedIn: 'root'}) 10 | export class UsersService { 11 | getCurrentUser(): Observable { 12 | return timer(2500).pipe(mapTo({name: 'Excelsior'})); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/animations/skeleton-leave.ts: -------------------------------------------------------------------------------- 1 | import { 2 | animate, 3 | AnimationTriggerMetadata, 4 | style, 5 | transition, 6 | trigger, 7 | } from '@angular/animations'; 8 | 9 | export const skeletonLeave: AnimationTriggerMetadata = trigger('skeletonLeave', [ 10 | transition(':leave', [ 11 | style({opacity: 1, position: 'absolute', top: 0, left: 0, right: 0}), 12 | animate(200, style({opacity: 0})), 13 | ]), 14 | ]); 15 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/navigation-skeleton.module.ts: -------------------------------------------------------------------------------- 1 | import {CommonModule} from '@angular/common'; 2 | import {NgModule, Type} from '@angular/core'; 3 | 4 | import {NavigationSkeletonComponent} from './navigation-skeleton.component'; 5 | 6 | const COMPONENTS: Type[] = [NavigationSkeletonComponent]; 7 | 8 | @NgModule({ 9 | imports: [CommonModule], 10 | declarations: COMPONENTS, 11 | exports: COMPONENTS, 12 | }) 13 | export class NavigationSkeletonModule {} 14 | -------------------------------------------------------------------------------- /projects/demo/src/app/user/user.resolve.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | import {Resolve} from '@angular/router'; 3 | import {Observable} from 'rxjs'; 4 | import {User, UsersService} from './user.service'; 5 | 6 | @Injectable({providedIn: 'root'}) 7 | export class UserResolve implements Resolve { 8 | constructor(private userService: UsersService) {} 9 | 10 | resolve(): Observable { 11 | return this.userService.getCurrentUser(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {ServerModule} from '@angular/platform-server'; 3 | import {UNIVERSAL_PROVIDERS} from '@ng-web-apis/universal'; 4 | import {AppBrowserModule} from './app.browser.module'; 5 | import {AppComponent} from './app.component'; 6 | 7 | @NgModule({ 8 | imports: [AppBrowserModule, ServerModule], 9 | bootstrap: [AppComponent], 10 | providers: UNIVERSAL_PROVIDERS, 11 | }) 12 | export class AppServerModule {} 13 | -------------------------------------------------------------------------------- /projects/demo/src/app/user/user.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {ActivatedRoute} from '@angular/router'; 3 | import {Observable} from 'rxjs'; 4 | import {map} from 'rxjs/operators'; 5 | import {User} from './user.service'; 6 | 7 | @Component({ 8 | template: `

Hello, {{ (user | async).name }}

`, 9 | }) 10 | export class UserComponent { 11 | user: Observable = this.activatedRoute.data.pipe(map(data => data.user)); 12 | 13 | constructor(private activatedRoute: ActivatedRoute) {} 14 | } 15 | -------------------------------------------------------------------------------- /schematics/library-starter/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema", 3 | "id": "LibraryStarterSchematics", 4 | "title": "Library Starter options schema", 5 | "type": "object", 6 | "description": "Generate Angular library for project", 7 | "properties": { 8 | "name": { 9 | "type": "string", 10 | "description": "The name of the library", 11 | "$default": { 12 | "$source": "argv", 13 | "index": 0 14 | }, 15 | "x-prompt": "What is the name of your library?" 16 | } 17 | }, 18 | "required": ["name"] 19 | } 20 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": ["dom", "es2018"] 10 | }, 11 | "angularCompilerOptions": { 12 | "skipTemplateCodegen": true, 13 | "strictMetadataEmit": true, 14 | "fullTemplateTypeCheck": true, 15 | "strictInjectionParameters": true, 16 | "enableResourceInlining": true, 17 | "enableIvy": false 18 | }, 19 | "exclude": ["src/test.ts", "**/*.spec.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /projects/demo/src/main.browser.ts: -------------------------------------------------------------------------------- 1 | import './polyfills'; 2 | 3 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; 4 | import {AppBrowserModule} from './app/app.browser.module'; 5 | 6 | document.addEventListener('DOMContentLoaded', () => { 7 | platformBrowserDynamic() 8 | .bootstrapModule(AppBrowserModule) 9 | .then(ref => { 10 | const windowRef: any = window; 11 | 12 | // Ensure Angular destroys itself on hot reloads for Stackblitz 13 | if (windowRef['ngRef']) { 14 | windowRef['ngRef'].destroy(); 15 | } 16 | 17 | windowRef['ngRef'] = ref; 18 | }) 19 | .catch(console.error); 20 | }); 21 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/navigation-skeleton.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 6 |
7 |
8 | 9 | 10 |
11 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐞 Bug report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: '' 6 | --- 7 | 8 | # 🐞 Bug report 9 | 10 | ### Description 11 | 12 | 13 | 14 | ### Reproduction 15 | 16 | 17 | 18 | http://www.stackblitz.com/... 19 | 20 | ### Expected behavior 21 | 22 | 23 | 24 | ### Versions 25 | 26 | - OS: [e.g. iOS] 27 | - Browser [e.g. chrome, safari] 28 | - Angular [e.g. 8] 29 | 30 | ### Additional context 31 | 32 | 33 | -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": ["dom", "es2018"] 10 | }, 11 | "angularCompilerOptions": { 12 | "annotateForClosureCompiler": true, 13 | "skipTemplateCodegen": true, 14 | "strictMetadataEmit": true, 15 | "fullTemplateTypeCheck": true, 16 | "strictInjectionParameters": true, 17 | "enableResourceInlining": true, 18 | "enableIvy": false 19 | }, 20 | "exclude": ["src/test.ts", "**/*.spec.ts"] 21 | } 22 | -------------------------------------------------------------------------------- /projects/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | /out-tsc 5 | # Only exists if Bazel was run 6 | /bazel-out 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # profiling files 12 | chrome-profiler-events.json 13 | speed-measure-plugin.json 14 | 15 | # IDEs and editors 16 | /.idea 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # IDE - VSCode 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | .history/* 31 | 32 | # misc 33 | /.sass-cache 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | npm-debug.log 38 | yarn-error.log 39 | testem.log 40 | /typings 41 | 42 | # System Files 43 | .DS_Store 44 | Thumbs.db 45 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {RouterModule, Routes} from '@angular/router'; 3 | import {OneComponent} from './one/one.component'; 4 | import {TwoComponent} from './two/two.component'; 5 | 6 | export const appRoutes: Routes = [ 7 | {path: '1', component: OneComponent}, 8 | {path: '2', component: TwoComponent}, 9 | { 10 | path: 'user', 11 | loadChildren: () => import('./user/user.module').then(m => m.UserModule), 12 | }, 13 | {path: '**', redirectTo: '2'}, 14 | ]; 15 | 16 | @NgModule({ 17 | imports: [ 18 | RouterModule.forRoot(appRoutes, { 19 | initialNavigation: 'enabled', 20 | }), 21 | ], 22 | exports: [RouterModule], 23 | }) 24 | export class AppRoutingModule {} 25 | -------------------------------------------------------------------------------- /projects/demo/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | import 'zone.js/dist/zone'; 3 | import 'zone.js/dist/zone-testing'; 4 | 5 | import {getTestBed} from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting, 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | declare const require: any; 12 | 13 | // First, initialize the Angular testing environment. 14 | getTestBed().initTestEnvironment( 15 | BrowserDynamicTestingModule, 16 | platformBrowserDynamicTesting(), 17 | ); 18 | 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | 22 | // And load the modules. 23 | context.keys().map(context); 24 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/navigation-skeleton.module.spec.ts: -------------------------------------------------------------------------------- 1 | import {TestBed} from '@angular/core/testing'; 2 | import {RouterTestingModule} from '@angular/router/testing'; 3 | 4 | import {NavigationSkeletonComponent} from './navigation-skeleton.component'; 5 | import {NavigationSkeletonModule} from './navigation-skeleton.module'; 6 | 7 | describe('NavigationSkeletonRootModule', () => { 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({ 10 | imports: [RouterTestingModule, NavigationSkeletonModule], 11 | }); 12 | }); 13 | 14 | it('Should export the component that is used to display navigation skeletons', () => { 15 | // assert 16 | expect(TestBed.createComponent(NavigationSkeletonComponent)).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | import 'zone.js/dist/zone'; 3 | import 'zone.js/dist/zone-testing'; 4 | 5 | import {getTestBed} from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting, 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | declare const require: any; 12 | 13 | // First, initialize the Angular testing environment. 14 | getTestBed().initTestEnvironment( 15 | BrowserDynamicTestingModule, 16 | platformBrowserDynamicTesting(), 17 | ); 18 | 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | 22 | // And load the modules. 23 | context.keys().map(context); 24 | -------------------------------------------------------------------------------- /tsconfig.schematics.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "tsconfig", 4 | "lib": ["es2018", "dom"], 5 | "declaration": true, 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noEmitOnError": true, 9 | "noFallthroughCasesInSwitch": true, 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noUnusedParameters": true, 13 | "noUnusedLocals": true, 14 | "rootDir": "schematics/", 15 | "skipDefaultLibCheck": true, 16 | "skipLibCheck": true, 17 | "sourceMap": true, 18 | "strictNullChecks": true, 19 | "target": "es6", 20 | "types": ["jasmine", "node"] 21 | }, 22 | "include": ["schematics/**/*"], 23 | "exclude": ["schematics/*/files/**/*"] 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "importHelpers": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitReturns": true, 14 | "noUnusedParameters": true, 15 | "noUnusedLocals": true, 16 | "target": "es2015", 17 | "typeRoots": ["node_modules/@types"], 18 | "lib": ["es2018", "dom"], 19 | "paths": { 20 | "@tinkoff/navigation-skeleton": [ 21 | "projects/navigation-skeleton/src/public-api" 22 | ] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | import 'zone.js/dist/zone'; 3 | import 'zone.js/dist/zone-testing'; 4 | 5 | import {getTestBed} from '@angular/core/testing'; 6 | import { 7 | BrowserDynamicTestingModule, 8 | platformBrowserDynamicTesting, 9 | } from '@angular/platform-browser-dynamic/testing'; 10 | 11 | declare const require: any; 12 | 13 | // First, initialize the Angular testing environment. 14 | getTestBed().initTestEnvironment( 15 | BrowserDynamicTestingModule, 16 | platformBrowserDynamicTesting(), 17 | ); 18 | 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | 22 | // And load the modules. 23 | context.keys().map(context); 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE]' 5 | labels: '' 6 | --- 7 | 8 | # 🚀 Feature request 9 | 10 | ### Is your feature request related to a problem? 11 | 12 | 13 | I'm always frustrated when... 14 | 15 | ### Describe the solution you'd like 16 | 17 | 18 | 19 | 20 | ### Describe alternatives you've considered 21 | 22 | 23 | 24 | 25 | ### Additional context 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /scripts/syncVersions.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const glob = require('glob'); 3 | const JSON_INDENTATION_LEVEL = 4; 4 | const {version} = require('../package.json'); 5 | 6 | // Sync libraries package.json versions with main package.json 7 | syncVersions('projects'); 8 | 9 | function syncVersions(root) { 10 | glob(root + '/**/package.json', (_, files) => { 11 | files.forEach(file => { 12 | const packageJson = JSON.parse(fs.readFileSync(file)); 13 | 14 | fs.writeFileSync( 15 | file, 16 | JSON.stringify( 17 | { 18 | ...packageJson, 19 | version, 20 | }, 21 | null, 22 | JSON_INDENTATION_LEVEL, 23 | ), 24 | ); 25 | }); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled schematics 2 | schematics/library-starter/*.js 3 | schematics/library-starter/*.js.map 4 | schematics/library-starter/*.d.ts 5 | 6 | # compiled output 7 | /dist 8 | /tmp 9 | /out-tsc 10 | # Only exists if Bazel was run 11 | /bazel-out 12 | 13 | # dependencies 14 | /node_modules 15 | 16 | # profiling files 17 | chrome-profiler-events.json 18 | speed-measure-plugin.json 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | .history/* 36 | 37 | # misc 38 | /.sass-cache 39 | /connect.lock 40 | /coverage 41 | /libpeerconnection.log 42 | npm-debug.log 43 | yarn-error.log 44 | testem.log 45 | /typings 46 | 47 | # System Files 48 | .DS_Store 49 | Thumbs.db 50 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI of all packages 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Use Node.js 11 | uses: actions/setup-node@v1 12 | with: 13 | node-version: '12.x' 14 | - name: Cache Node.js modules 15 | uses: actions/cache@v2 16 | with: 17 | path: ~/.npm 18 | key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }} 19 | restore-keys: | 20 | ${{ runner.OS }}-node- 21 | ${{ runner.OS }}- 22 | - name: Install dependencies 23 | run: npm ci 24 | - name: CI checks 25 | run: | 26 | npm run build 27 | npm run lint 28 | npm run test 29 | - name: Coveralls 30 | uses: coverallsapp/github-action@master 31 | with: 32 | github-token: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.browser.module.ts: -------------------------------------------------------------------------------- 1 | import {LocationStrategy, PathLocationStrategy} from '@angular/common'; 2 | import {NgModule} from '@angular/core'; 3 | import {BrowserModule} from '@angular/platform-browser'; 4 | import {NavigationSkeletonModule} from '@tinkoff/navigation-skeleton'; 5 | import {AppComponent} from './app.component'; 6 | import {AppRoutingModule} from './app.routes'; 7 | import {OneComponent} from './one/one.component'; 8 | import {TwoComponent} from './two/two.component'; 9 | 10 | @NgModule({ 11 | bootstrap: [AppComponent], 12 | imports: [ 13 | BrowserModule.withServerTransition({ 14 | appId: 'demo', 15 | }), 16 | AppRoutingModule, 17 | NavigationSkeletonModule, 18 | ], 19 | declarations: [AppComponent, OneComponent, TwoComponent], 20 | providers: [ 21 | { 22 | provide: LocationStrategy, 23 | useClass: PathLocationStrategy, 24 | }, 25 | ], 26 | }) 27 | export class AppBrowserModule {} 28 | -------------------------------------------------------------------------------- /projects/demo/src/app/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import {CommonModule} from '@angular/common'; 2 | import {NgModule} from '@angular/core'; 3 | import {RouterModule} from '@angular/router'; 4 | import {NavigationSkeletonRoute} from '@tinkoff/navigation-skeleton'; 5 | import {UserSkeletonComponent} from './skeleton/user-skeleton.component'; 6 | import {UserComponent} from './user.component'; 7 | import {UserResolve} from './user.resolve'; 8 | 9 | @NgModule({ 10 | declarations: [UserComponent, UserSkeletonComponent], 11 | imports: [ 12 | CommonModule, 13 | RouterModule.forChild([ 14 | { 15 | path: '', 16 | component: UserComponent, 17 | resolve: { 18 | user: UserResolve, 19 | }, 20 | skeleton: { 21 | component: UserSkeletonComponent, 22 | }, 23 | }, 24 | ] as NavigationSkeletonRoute[]), 25 | ], 26 | }) 27 | export class UserModule {} 28 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tinkoff/navigation-skeleton", 3 | "version": "1.0.5", 4 | "description": "Navigation skeleton is a component for showing skeletons during navigation process", 5 | "peerDependencies": { 6 | "@angular/core": ">=9.0.0", 7 | "@angular/animations": ">=9.0.0", 8 | "@angular/router": ">=9.0.0", 9 | "rxjs": "~6.6.3" 10 | }, 11 | "keywords": [ 12 | "angular", 13 | "ng", 14 | "navigation", 15 | "skeleton", 16 | "perfomance", 17 | "router", 18 | "tinkoff" 19 | ], 20 | "license": "Apache-2.0", 21 | "author": { 22 | "name": "Oleg Teterin", 23 | "email": "dersizes@dersizes.ru" 24 | }, 25 | "repository": "https://github.com/TinkoffCreditSystems/navigation-skeleton", 26 | "bugs": "https://github.com/TinkoffCreditSystems/navigation-skeleton/issues", 27 | "homepage": "https://github.com/TinkoffCreditSystems/navigation-skeleton#README" 28 | } 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | 3 | Please check if your PR fulfills the following requirements: 4 | 5 | - [ ] The commit message follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) 6 | - [ ] Tests for the changes have been added (for bug fixes / features) 7 | - [ ] Docs have been added / updated (for bug fixes / features) 8 | 9 | ## PR Type 10 | 11 | What kind of change does this PR introduce? 12 | 13 | 14 | 15 | - [ ] Bugfix 16 | - [ ] Feature 17 | - [ ] Refactoring (no functional changes, no api changes) 18 | - [ ] Other... Please describe: 19 | 20 | ## What is the current behavior? 21 | 22 | 23 | 24 | Issue Number: N/A 25 | 26 | ## What is the new behavior? 27 | 28 | ## Does this PR introduce a breaking change? 29 | 30 | - [ ] Yes 31 | - [ ] No 32 | 33 | 34 | 35 | ## Other information 36 | -------------------------------------------------------------------------------- /projects/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.5", 4 | "private": true, 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build" 9 | }, 10 | "dependencies": { 11 | "@angular/common": "11.0.4", 12 | "@angular/compiler": "11.0.4", 13 | "@angular/core": "11.0.4", 14 | "@angular/forms": "11.0.4", 15 | "@angular/platform-browser": "11.0.4", 16 | "@angular/platform-browser-dynamic": "11.0.4", 17 | "@angular/router": "11.0.4", 18 | "@ng-web-apis/common": "^1.9.0", 19 | "@ng-web-apis/universal": "^1.9.2", 20 | "core-js": "2.6.9", 21 | "rxjs": "~6.6.3", 22 | "zone.js": "~0.10.2", 23 | "@tinkoff/navigation-skeleton": "latest" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/architect": "0.1100.4", 27 | "@angular-devkit/build-angular": "0.1100.4", 28 | "@angular/cli": "11.0.4", 29 | "@angular/compiler-cli": "11.0.4", 30 | "@angular/language-service": "11.0.4", 31 | "@types/node": "^12.11.1", 32 | "ts-node": "9.0.0", 33 | "tslint": "6.1.3", 34 | "typescript": "4.0.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | > Thank you for considering contributing to our project. Your help if very welcome! 4 | 5 | When contributing, it's better to first discuss the change you wish to make via issue, 6 | email, or any other method with the owners of this repository before making a change. 7 | 8 | All members of our community are expected to follow our [Code of Conduct](CODE_OF_CONDUCT.md). 9 | Please make sure you are welcoming and friendly in all of our spaces. 10 | 11 | ## Getting started 12 | 13 | In order to make your contribution please make a fork of the repository. After you've pulled 14 | the code, follow these steps to kick start the development: 15 | 16 | 1. Run `npm ci` to install dependencies 17 | 2. Run `npm start` to launch demo project where you could test your changes 18 | 3. Use following commands to ensure code quality 19 | 20 | ``` 21 | npm run lint 22 | npm run build 23 | npm run test 24 | ``` 25 | 26 | ## Pull Request Process 27 | 28 | 1. We follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) 29 | in our commit messages, i.e. `feat(core): improve typing` 30 | 2. Update [README.md](README.md) to reflect changes related to public API and everything relevant 31 | 3. Make sure you cover all code changes with unit tests 32 | 4. When you are ready, create Pull Request of your fork into original repository 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 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 | ### [1.0.5](https://github.com/TinkoffCreditSystems/navigation-skeleton/compare/v1.0.4...v1.0.5) (2021-02-01) 6 | 7 | ### Bug Fixes 8 | 9 | - **navigation-skeleton:** fix expression form not supported compliation error ([6db78ec](https://github.com/TinkoffCreditSystems/navigation-skeleton/commit/6db78ec0945cc5a5b8828ddad2e865dbf6458935)) 10 | - **navigation-skeleton:** removed mandatory dependency on animations modules ([c59e38d](https://github.com/TinkoffCreditSystems/navigation-skeleton/commit/c59e38dc59dac84f04f1d5b6a77cb0fac4d3c853)) 11 | - **navigation-skeleton:** translate specs to english ([588247b](https://github.com/TinkoffCreditSystems/navigation-skeleton/commit/588247b13ac537162e4b4bcfa07f8e51db694f32)) 12 | 13 | ### [1.0.4](https://github.com/TinkoffCreditSystems/navigation-skeleton/compare/v1.0.3...v1.0.4) (2021-01-28) 14 | 15 | ### Features 16 | 17 | - **navigation-skeleton:** add demo link ([234537c](https://github.com/TinkoffCreditSystems/navigation-skeleton/commit/234537c40521224cd0bb4300de06cf20fff725a0)) 18 | 19 | ### 1.0.3 (2021-01-27) 20 | 21 | # Changelog 22 | 23 | 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. 24 | -------------------------------------------------------------------------------- /projects/demo/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma'), 14 | ], 15 | client: { 16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/demo'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true, 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['ChromeHeadless'], 29 | singleRun: true, 30 | customLaunchers: { 31 | ChromeHeadless: { 32 | base: 'Chrome', 33 | flags: [ 34 | '--no-sandbox', 35 | '--headless', 36 | '--disable-gpu', 37 | '--remote-debugging-port=9222', 38 | ], 39 | }, 40 | }, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma'), 14 | ], 15 | client: { 16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true, 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['ChromeHeadless'], 29 | singleRun: true, 30 | customLaunchers: { 31 | ChromeHeadless: { 32 | base: 'Chrome', 33 | flags: [ 34 | '--no-sandbox', 35 | '--headless', 36 | '--disable-gpu', 37 | '--remote-debugging-port=9222', 38 | ], 39 | }, 40 | }, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma'), 14 | ], 15 | client: { 16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/<%= dasherize(name) %>'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true, 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['ChromeHeadless'], 29 | singleRun: true, 30 | customLaunchers: { 31 | ChromeHeadless: { 32 | base: 'Chrome', 33 | flags: [ 34 | '--no-sandbox', 35 | '--headless', 36 | '--disable-gpu', 37 | '--remote-debugging-port=9222', 38 | ], 39 | }, 40 | }, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tinkoff Navigation Skeleton 2 | 3 | [![Build](https://github.com/TinkoffCreditSystems/navigation-skeleton/workflows/CI%20of%20all%20packages/badge.svg)](https://github.com/TinkoffCreditSystems/navigation-skeleton/actions?query=workflow%3A%22CI+of+all+packages%22) 4 | [![npm version](https://img.shields.io/npm/v/@tinkoff/navigation-skeleton.svg)](https://www.npmjs.com/package/@tinkoff/navigation-skeleton) 5 | [![angular-open-source-starter](https://img.shields.io/badge/made%20with-angular--open--source--starter-d81676?logo=angular)](https://github.com/TinkoffCreditSystems/angular-open-source-starter) 6 | 7 | > This component allows you to show skeletons of pages during navigation process. 8 | 9 | ## Install 10 | 11 | ``` 12 | $ npm install @tinkoff/navigation-skeleton 13 | ``` 14 | 15 | ## How to use 16 | 17 | 1. Add `NavigationSkeletonModule,` to the `imports` section of root module. 18 | 19 | ``` 20 | @NgModule({ 21 | ... 22 | imports: [ 23 | ... 24 | RouterModule.forRoot(...), 25 | NavigationSkeletonModule, 26 | ], 27 | }) 28 | export class AppModule {} 29 | ``` 30 | 31 | 2. Change 32 | `` 33 | to 34 | ``` 35 | 36 | 37 | 38 | ``` 39 | 3. Add skeleton component to the route definition 40 | ``` 41 | const route: NavigationSkeletonRoute = { 42 | path: '...', 43 | component: ..., 44 | skeleton: { 45 | component: MySkeletonComponent, 46 | }, 47 | }; 48 | ``` 49 | 50 | ## Demo 51 | 52 | [https://stackblitz.com/edit/tcs-navigation-skeleton](https://stackblitz.com/edit/tcs-navigation-skeleton?file=app%2Fuser%2Fuser.component.ts) 53 | -------------------------------------------------------------------------------- /projects/demo/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "demo": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/demo", 17 | "index": "src/index.html", 18 | "main": "src/main.browser.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.demo.json", 21 | "aot": false, 22 | "assets": ["src/favicon.ico"], 23 | "styles": ["src/styles.css"], 24 | "scripts": [] 25 | }, 26 | "configurations": { 27 | "production": { 28 | "optimization": true, 29 | "outputHashing": "all", 30 | "sourceMap": false, 31 | "extractCss": true, 32 | "namedChunks": false, 33 | "aot": true, 34 | "extractLicenses": true, 35 | "vendorChunk": false, 36 | "buildOptimizer": true 37 | } 38 | } 39 | }, 40 | "serve": { 41 | "builder": "@angular-devkit/build-angular:dev-server", 42 | "options": { 43 | "browserTarget": "demo:build" 44 | }, 45 | "configurations": { 46 | "production": { 47 | "browserTarget": "demo:build:production" 48 | } 49 | } 50 | } 51 | } 52 | } 53 | }, 54 | "defaultProject": "demo" 55 | } 56 | -------------------------------------------------------------------------------- /projects/demo/server.ts: -------------------------------------------------------------------------------- 1 | import '@ng-web-apis/universal/mocks'; 2 | import 'zone.js/dist/zone-node'; 3 | 4 | import {APP_BASE_HREF} from '@angular/common'; 5 | import {provideLocation, provideUserAgent} from '@ng-web-apis/universal'; 6 | import {ngExpressEngine} from '@nguniversal/express-engine'; 7 | import * as express from 'express'; 8 | import {Express} from 'express'; 9 | import {existsSync} from 'fs'; 10 | import {join} from 'path'; 11 | import {AppServerModule} from './src/main.server'; 12 | 13 | // The Express app is exported so that it can be used by serverless Functions. 14 | export function app(): Express { 15 | const server = express(); 16 | const distFolder = join(process.cwd(), 'dist/demo/browser'); 17 | const indexHtml = existsSync(join(distFolder, 'index.original.html')) 18 | ? 'index.original.html' 19 | : 'index'; 20 | 21 | // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) 22 | server.engine( 23 | 'html', 24 | ngExpressEngine({ 25 | bootstrap: AppServerModule, 26 | }) as any, 27 | ); 28 | 29 | server.set('view engine', 'html'); 30 | server.set('views', distFolder); 31 | 32 | // Example Express Rest API endpoints 33 | // server.get('/api/**', (req, res) => { }); 34 | // Serve static files from /browser 35 | server.get( 36 | '*.*', 37 | express.static(distFolder, { 38 | maxAge: '1y', 39 | }), 40 | ); 41 | 42 | // All regular routes use the Universal engine 43 | server.get('*', (req, res) => { 44 | res.render(indexHtml, { 45 | req, 46 | providers: [ 47 | {provide: APP_BASE_HREF, useValue: req.baseUrl}, 48 | provideLocation(req), 49 | provideUserAgent(req), 50 | ], 51 | }); 52 | }); 53 | 54 | return server; 55 | } 56 | 57 | function run() { 58 | const port = process.env.PORT || 4000; 59 | 60 | // Start up the Node server 61 | const server = app(); 62 | 63 | server.listen(port, () => { 64 | // tslint:disable-next-line:no-console 65 | console.log(`Node Express server listening on http://localhost:${port}`); 66 | }); 67 | } 68 | 69 | // Webpack will replace 'require' with '__webpack_require__' 70 | // '__non_webpack_require__' is a proxy to Node 'require' 71 | // The below code is to ensure that the server is run only when not requiring the bundle. 72 | declare const __non_webpack_require__: NodeRequire; 73 | const mainModule = __non_webpack_require__.main; 74 | const moduleFilename = (mainModule && mainModule.filename) || ''; 75 | 76 | if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { 77 | run(); 78 | } 79 | 80 | export * from './src/main.server'; 81 | -------------------------------------------------------------------------------- /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 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at opensource@tinkoff.ru. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/navigation-skeleton.component.ts: -------------------------------------------------------------------------------- 1 | import {animateChild, query, transition, trigger} from '@angular/animations'; 2 | import { 3 | ChangeDetectionStrategy, 4 | Component, 5 | Inject, 6 | Injector, 7 | NgModuleRef, 8 | Optional, 9 | Type, 10 | } from '@angular/core'; 11 | import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; 12 | import { 13 | GuardsCheckStart, 14 | NavigationCancel, 15 | NavigationEnd, 16 | Route, 17 | Router, 18 | RouterStateSnapshot, 19 | RoutesRecognized, 20 | } from '@angular/router'; 21 | import {concat, Observable, of} from 'rxjs'; 22 | import {filter, map, mapTo, switchMap, takeUntil} from 'rxjs/operators'; 23 | 24 | export interface NavigationSkeletonRouteData { 25 | component?: Type; 26 | } 27 | 28 | export interface NavigationSkeletonRoute extends Route { 29 | skeleton?: NavigationSkeletonRouteData; 30 | children?: NavigationSkeletonRoute[]; 31 | } 32 | 33 | @Component({ 34 | selector: 'tcs-navigation-skeleton', 35 | templateUrl: './navigation-skeleton.component.html', 36 | styleUrls: ['./navigation-skeleton.component.less'], 37 | changeDetection: ChangeDetectionStrategy.OnPush, 38 | animations: [ 39 | trigger('triggerChildAnimation', [ 40 | transition('* => void', [query('@*', [animateChild()], {optional: true})]), 41 | ]), 42 | ], 43 | }) 44 | export class NavigationSkeletonComponent { 45 | readonly skeleton: Observable; 46 | 47 | constructor( 48 | router: Router, 49 | @Inject(ANIMATION_MODULE_TYPE) 50 | @Optional() 51 | public readonly animations?: string | null, 52 | ) { 53 | const start = router.events.pipe( 54 | filter(event => event instanceof GuardsCheckStart), 55 | ); 56 | const end = router.events.pipe( 57 | filter( 58 | event => 59 | event instanceof NavigationEnd || event instanceof NavigationCancel, 60 | ), 61 | ); 62 | const skeleton = router.events.pipe( 63 | filter(event => event instanceof RoutesRecognized), 64 | map((event: RoutesRecognized) => this.getSkeleton(event.state)), 65 | ); 66 | 67 | this.skeleton = skeleton.pipe( 68 | switchMap(skeleton => 69 | skeleton 70 | ? concat(start.pipe(mapTo(skeleton), takeUntil(end)), of(null)) 71 | : of(null), 72 | ), 73 | ); 74 | } 75 | 76 | private getSkeleton(state: RouterStateSnapshot): NavigationSkeleton | null { 77 | let route = state.root; 78 | let injector = this.getRouteInjector(route.routeConfig); 79 | 80 | while (route.firstChild) { 81 | route = route.firstChild; 82 | injector = this.getRouteInjector(route.routeConfig) || injector; 83 | } 84 | 85 | const component = (route?.routeConfig as NavigationSkeletonRoute | null)?.skeleton 86 | ?.component; 87 | 88 | return component ? {component, injector} : null; 89 | } 90 | 91 | private getRouteInjector(route: Route | null): Injector | null { 92 | return (route as InternalRoute)?._loadedConfig?.module?.injector || null; 93 | } 94 | } 95 | 96 | interface NavigationSkeleton { 97 | component: Type; 98 | injector: Injector; 99 | } 100 | 101 | // TODO: https://github.com/angular/angular/issues/24069 102 | // https://github.com/angular/angular/blob/9.1.11/packages/router/src/config.ts#L484-L488 103 | interface InternalRoute extends Route { 104 | _loadedConfig?: LoadedRouterConfig; 105 | } 106 | interface LoadedRouterConfig { 107 | module: NgModuleRef; 108 | } 109 | -------------------------------------------------------------------------------- /schematics/library-starter/index.ts: -------------------------------------------------------------------------------- 1 | import {strings} from '@angular-devkit/core'; 2 | import {dasherize} from '@angular-devkit/core/src/utils/strings'; 3 | import { 4 | apply, 5 | mergeWith, 6 | Rule, 7 | SchematicContext, 8 | template, 9 | Tree, 10 | url, 11 | } from '@angular-devkit/schematics'; 12 | 13 | export interface Schema { 14 | readonly name: string; 15 | } 16 | 17 | const DEMO_PACKAGE_JSON_PATH = 'projects/demo/package.json'; 18 | const TSCONFIG_JSON_PATH = 'tsconfig.json'; 19 | const ANGULAR_JSON_PATH = 'angular.json'; 20 | const PACKAGE_JSON_PATH = 'package.json'; 21 | 22 | export function libraryStarter(options: Schema): Rule { 23 | return (tree: Tree, context: SchematicContext) => { 24 | const {name} = options; 25 | const dasherizedName = dasherize(name); 26 | const sourceTemplates = url('./files'); 27 | const sourceParametrizedTemplates = apply(sourceTemplates, [ 28 | template({ 29 | ...options, 30 | ...strings, 31 | }), 32 | ]); 33 | 34 | updateDemoPackage(tree, dasherizedName); 35 | updateTSConfig(tree, dasherizedName); 36 | updateAngular(tree, dasherizedName); 37 | updatePackage(tree, dasherizedName); 38 | 39 | return mergeWith(sourceParametrizedTemplates)(tree, context); 40 | }; 41 | } 42 | 43 | function updateDemoPackage(tree: Tree, name: string) { 44 | const packageJson: Buffer | null = tree.read(DEMO_PACKAGE_JSON_PATH); 45 | const packageObject = JSON.parse(String(packageJson)); 46 | const {dependencies} = packageObject; 47 | 48 | dependencies[name] = 'latest'; 49 | 50 | tree.overwrite(DEMO_PACKAGE_JSON_PATH, JSON.stringify(packageObject, null, 4)); 51 | } 52 | 53 | function updateTSConfig(tree: Tree, name: string) { 54 | const tsconfigJson: Buffer | null = tree.read(TSCONFIG_JSON_PATH); 55 | const tsconfigObject = JSON.parse(String(tsconfigJson)); 56 | const {compilerOptions} = tsconfigObject; 57 | const {paths} = compilerOptions; 58 | 59 | paths[name] = [`projects/${name}/src/public-api`]; 60 | 61 | tree.overwrite(TSCONFIG_JSON_PATH, JSON.stringify(tsconfigObject, null, 4)); 62 | } 63 | 64 | function updateAngular(tree: Tree, name: string) { 65 | const project = { 66 | projectType: 'library', 67 | root: `projects/${name}`, 68 | sourceRoot: `projects/${name}/src`, 69 | architect: { 70 | build: { 71 | builder: '@angular-devkit/build-ng-packagr:build', 72 | options: { 73 | tsConfig: `projects/${name}/tsconfig.lib.json`, 74 | project: `projects/${name}/ng-package.json`, 75 | }, 76 | }, 77 | test: { 78 | builder: '@angular-devkit/build-angular:karma', 79 | options: { 80 | main: `projects/${name}/src/test.ts`, 81 | tsConfig: `projects/${name}/tsconfig.spec.json`, 82 | karmaConfig: `projects/${name}/karma.conf.js`, 83 | codeCoverage: true, 84 | browsers: 'ChromeHeadless', 85 | }, 86 | }, 87 | lint: { 88 | builder: '@angular-devkit/build-angular:tslint', 89 | options: { 90 | tsConfig: [ 91 | `projects/${name}/tsconfig.lib.json`, 92 | `projects/${name}/tsconfig.spec.json`, 93 | ], 94 | exclude: ['**/node_modules/**'], 95 | }, 96 | }, 97 | }, 98 | }; 99 | const angularJson: Buffer | null = tree.read(ANGULAR_JSON_PATH); 100 | const angularObject = JSON.parse(String(angularJson)); 101 | const {projects} = angularObject; 102 | 103 | projects[name] = project; 104 | 105 | tree.overwrite(ANGULAR_JSON_PATH, JSON.stringify(angularObject, null, 4)); 106 | } 107 | 108 | function updatePackage(tree: Tree, name: string) { 109 | const packageJson: Buffer | null = tree.read(PACKAGE_JSON_PATH); 110 | const packageObject = JSON.parse(String(packageJson)); 111 | const {scripts} = packageObject; 112 | const build = `ng run ${name}:build`; 113 | const buildKey = `build:${name}`; 114 | const buildAll = scripts['build:all'] || ''; 115 | const buildAllValue = buildAll 116 | ? `${buildAll} && npm run ${buildKey}` 117 | : `npm run ${buildKey}`; 118 | const publish = `npm publish ./dist/${name}`; 119 | const publishKey = `publish:${name}`; 120 | const publishAll = scripts['publish:all'] || ''; 121 | const publishAllValue = publishAll 122 | ? `${publishAll} && npm run ${publishKey}` 123 | : `npm run ${publishKey}`; 124 | const newScripts = { 125 | 'build:all': buildAllValue, 126 | 'publish:all': publishAllValue, 127 | [buildKey]: build, 128 | [`test:${name}`]: `ng run ${name}:test`, 129 | [`test:${name}:watch`]: `ng run ${name}:test --watch=true`, 130 | [publishKey]: publish, 131 | }; 132 | 133 | packageObject['scripts'] = { 134 | ...scripts, 135 | ...newScripts, 136 | }; 137 | 138 | tree.overwrite(PACKAGE_JSON_PATH, JSON.stringify(packageObject, null, 4)); 139 | } 140 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tinkoff/navigation-skeleton", 3 | "version": "1.0.5", 4 | "description": "Navigation skeleton is a component for showing skeletons during navigation process", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "demo": "ng serve demo", 9 | "dev:ssr": "ng run demo:serve-ssr", 10 | "build:ssr": "ng build --prod && ng run demo:server:production", 11 | "prerender": "ng run demo:prerender", 12 | "build": "ng build", 13 | "build:schematics": "tsc -p tsconfig.schematics.json", 14 | "serve:prerender": "cd dist/demo/browser && http-server", 15 | "compile:server": "tsc -p ./projects/demo/tsconfig.ssr.json", 16 | "generate:prerender": "node dist/demo/ssr/prerender", 17 | "test": "ng test --code-coverage=true", 18 | "preadd": "npm run build:schematics", 19 | "add": "schematics ./schematics/collection.json:library-starter --debug=false", 20 | "postadd": "git add ./projects", 21 | "lint": "ng lint", 22 | "lint:less": "stylelint '**/*.less'", 23 | "typecheck": "tsc --noEmit --skipLibCheck", 24 | "release": "standard-version", 25 | "release:patch": "npm run release -- --release-as patch", 26 | "release:minor": "npm run release -- --release-as minor", 27 | "release:major": "npm run release -- --release-as major", 28 | "publish": "npm run build:all && npm run publish:all", 29 | "build:all": "npm run build:navigation-skeleton", 30 | "publish:all": "npm run publish:navigation-skeleton", 31 | "build:navigation-skeleton": "ng run navigation-skeleton:build && npm run copy-readme-to:navigation-skeleton", 32 | "test:navigation-skeleton": "ng run navigation-skeleton:test", 33 | "test:navigation-skeleton:watch": "ng run navigation-skeleton:test --watch=true", 34 | "publish:navigation-skeleton": "npm publish ./dist/navigation-skeleton", 35 | "copy-readme-to:navigation-skeleton": "node scripts/copyReadme.js" 36 | }, 37 | "keywords": [ 38 | "angular", 39 | "ng", 40 | "navigation", 41 | "skeleton", 42 | "perfomance", 43 | "router", 44 | "tinkoff" 45 | ], 46 | "license": "Apache-2.0", 47 | "author": { 48 | "name": "Oleg Teterin", 49 | "email": "dersizes@dersizes.ru" 50 | }, 51 | "repository": "https://github.com/TinkoffCreditSystems/navigation-skeleton", 52 | "bugs": "https://github.com/TinkoffCreditSystems/navigation-skeleton/issues", 53 | "homepage": "https://github.com/TinkoffCreditSystems/navigation-skeleton#README", 54 | "schematics": "./schematics/collection.json", 55 | "dependencies": { 56 | "@angular/animations": "^9.1.13", 57 | "@angular/common": "^9.1.13", 58 | "@angular/compiler": "^9.1.13", 59 | "@angular/core": "^9.1.13", 60 | "@angular/forms": "^9.1.13", 61 | "@angular/platform-browser": "^9.1.13", 62 | "@angular/platform-browser-dynamic": "^9.1.13", 63 | "@angular/platform-server": "^9.1.13", 64 | "@angular/router": "^9.1.13", 65 | "@nguniversal/express-engine": "^9.1.1", 66 | "@ng-web-apis/common": "^1.9.0", 67 | "@ng-web-apis/universal": "^1.9.2", 68 | "core-js": "^2.6.9", 69 | "express": "^4.15.2", 70 | "rxjs": "~6.6.3", 71 | "tslib": "^1.10.0", 72 | "zone.js": "~0.10.2" 73 | }, 74 | "devDependencies": { 75 | "@angular-devkit/build-angular": "~0.901.13", 76 | "@angular-devkit/build-ng-packagr": "~0.901.13", 77 | "@angular-devkit/core": "^9.1.13", 78 | "@angular-devkit/schematics": "^9.1.13", 79 | "@angular-devkit/schematics-cli": "~0.901.13", 80 | "@angular/cli": "^9.1.13", 81 | "@angular/compiler-cli": "^9.1.13", 82 | "@angular/language-service": "^9.1.13", 83 | "@commitlint/cli": "^11.0.0", 84 | "@commitlint/config-conventional": "^11.0.0", 85 | "@nguniversal/builders": "^9.1.1", 86 | "@tinkoff/linters": "^0.6.0", 87 | "@types/express": "^4.17.0", 88 | "@types/jasmine": "^3.6.2", 89 | "@types/jasminewd2": "^2.0.8", 90 | "@types/node": "12.19.9", 91 | "coveralls": "^3.1.0", 92 | "husky": "^4.3.6", 93 | "jasmine-core": "^3.6.0", 94 | "jasmine-spec-reporter": "^6.0.0", 95 | "karma": "^5.2.3", 96 | "karma-chrome-launcher": "^3.1.0", 97 | "karma-coverage-istanbul-reporter": "^3.0.3", 98 | "karma-jasmine": "^4.0.1", 99 | "karma-jasmine-html-reporter": "^1.5.4", 100 | "lint-staged": "^10.5.3", 101 | "ng-packagr": "9.1.5", 102 | "prettier": "^2.2.1", 103 | "standard-version": "^9.0.0", 104 | "stylelint": "^10.0.1", 105 | "ts-node": "^9.1.1", 106 | "tslint": "~5.15.0", 107 | "tsutils": "^3.17.1", 108 | "typescript": "~3.8.3", 109 | "ts-mockito": "^2.4.2" 110 | }, 111 | "husky": { 112 | "hooks": { 113 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 114 | "pre-commit": "lint-staged && npm run typecheck" 115 | } 116 | }, 117 | "lint-staged": { 118 | "*.{js,ts,html,md,less,json}": ["prettier --write", "git add"], 119 | "*.ts": "tslint --fix" 120 | }, 121 | "standard-version": { 122 | "scripts": { 123 | "postbump": "node scripts/syncVersions.js && git add **/package.json" 124 | } 125 | }, 126 | "engines": { 127 | "node": ">= 10", 128 | "npm": ">= 3" 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "demo": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "projects/demo", 10 | "sourceRoot": "projects/demo/src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "baseHref": "/", 17 | "deployUrl": "/", 18 | "outputPath": "dist/demo/browser", 19 | "index": "projects/demo/src/index.html", 20 | "main": "projects/demo/src/main.browser.ts", 21 | "polyfills": "projects/demo/src/polyfills.ts", 22 | "tsConfig": "projects/demo/tsconfig.demo.json", 23 | "aot": false, 24 | "assets": ["projects/demo/src/favicon.ico"], 25 | "styles": ["projects/demo/src/styles.css"], 26 | "scripts": [] 27 | }, 28 | "configurations": { 29 | "production": { 30 | "fileReplacements": [ 31 | { 32 | "replace": "projects/demo/src/environments/environment.ts", 33 | "with": "projects/demo/src/environments/environment.prod.ts" 34 | } 35 | ], 36 | "optimization": true, 37 | "outputHashing": "all", 38 | "sourceMap": false, 39 | "extractCss": true, 40 | "namedChunks": false, 41 | "aot": true, 42 | "extractLicenses": true, 43 | "vendorChunk": false, 44 | "buildOptimizer": true, 45 | "budgets": [ 46 | { 47 | "type": "initial", 48 | "maximumWarning": "2mb", 49 | "maximumError": "5mb" 50 | } 51 | ] 52 | } 53 | } 54 | }, 55 | "serve": { 56 | "builder": "@angular-devkit/build-angular:dev-server", 57 | "options": { 58 | "browserTarget": "demo:build" 59 | }, 60 | "configurations": { 61 | "production": { 62 | "browserTarget": "demo:build:production" 63 | } 64 | } 65 | }, 66 | "lint": { 67 | "builder": "@angular-devkit/build-angular:tslint", 68 | "options": { 69 | "tsConfig": ["tsconfig.json"], 70 | "exclude": ["**/node_modules/**"] 71 | } 72 | }, 73 | "server": { 74 | "builder": "@angular-devkit/build-angular:server", 75 | "options": { 76 | "outputPath": "dist/demo/server", 77 | "main": "projects/demo/server.ts", 78 | "tsConfig": "projects/demo/tsconfig.server.json" 79 | }, 80 | "configurations": { 81 | "production": { 82 | "outputHashing": "media", 83 | "sourceMap": false, 84 | "optimization": true 85 | } 86 | } 87 | }, 88 | "serve-ssr": { 89 | "builder": "@nguniversal/builders:ssr-dev-server", 90 | "options": { 91 | "browserTarget": "demo:build", 92 | "serverTarget": "demo:server" 93 | }, 94 | "configurations": { 95 | "production": { 96 | "browserTarget": "demo:build:production", 97 | "serverTarget": "demo:server:production" 98 | } 99 | } 100 | }, 101 | "prerender": { 102 | "builder": "@nguniversal/builders:prerender", 103 | "options": { 104 | "browserTarget": "demo:build:production", 105 | "serverTarget": "demo:server:production", 106 | "routes": ["/"] 107 | }, 108 | "configurations": { 109 | "production": {} 110 | } 111 | } 112 | } 113 | }, 114 | "navigation-skeleton": { 115 | "projectType": "library", 116 | "root": "projects/navigation-skeleton", 117 | "sourceRoot": "projects/navigation-skeleton/src", 118 | "architect": { 119 | "build": { 120 | "builder": "@angular-devkit/build-ng-packagr:build", 121 | "options": { 122 | "tsConfig": "projects/navigation-skeleton/tsconfig.lib.json", 123 | "project": "projects/navigation-skeleton/ng-package.json" 124 | } 125 | }, 126 | "test": { 127 | "builder": "@angular-devkit/build-angular:karma", 128 | "options": { 129 | "main": "projects/navigation-skeleton/src/test.ts", 130 | "tsConfig": "projects/navigation-skeleton/tsconfig.spec.json", 131 | "karmaConfig": "projects/navigation-skeleton/karma.conf.js", 132 | "codeCoverage": true, 133 | "browsers": "ChromeHeadless" 134 | } 135 | }, 136 | "lint": { 137 | "builder": "@angular-devkit/build-angular:tslint", 138 | "options": { 139 | "tsConfig": [ 140 | "projects/navigation-skeleton/tsconfig.lib.json", 141 | "projects/navigation-skeleton/tsconfig.spec.json" 142 | ], 143 | "exclude": ["**/node_modules/**"] 144 | } 145 | } 146 | } 147 | } 148 | }, 149 | "defaultProject": "demo" 150 | } 151 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2019 Tinkoff Bank 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. -------------------------------------------------------------------------------- /projects/navigation-skeleton/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2019 Tinkoff Bank 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. -------------------------------------------------------------------------------- /schematics/library-starter/files/projects/__name@dasherize__/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2019 Tinkoff Bank 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. -------------------------------------------------------------------------------- /projects/navigation-skeleton/src/navigation-skeleton.component.spec.ts: -------------------------------------------------------------------------------- 1 | import {CommonModule} from '@angular/common'; 2 | import { 3 | Component, 4 | Inject, 5 | Injectable, 6 | InjectionToken, 7 | NgModule, 8 | NgZone, 9 | } from '@angular/core'; 10 | import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; 11 | import { 12 | ANIMATION_MODULE_TYPE, 13 | BrowserAnimationsModule, 14 | } from '@angular/platform-browser/animations'; 15 | import {CanActivate, Resolve, Router, RouterModule} from '@angular/router'; 16 | import {RouterTestingModule} from '@angular/router/testing'; 17 | import {NEVER, Observable, Subject, timer} from 'rxjs'; 18 | import {mapTo} from 'rxjs/operators'; 19 | import {anything, instance, mock, when} from 'ts-mockito'; 20 | 21 | import { 22 | NavigationSkeletonComponent, 23 | NavigationSkeletonRoute, 24 | } from './navigation-skeleton.component'; 25 | 26 | @Component({ 27 | selector: 'test-routing', 28 | template: `projected-content`, 29 | }) 30 | class TestRoutingComponent {} 31 | 32 | @Injectable() 33 | export class TestRoutingResolve implements Resolve { 34 | resolve(): Observable { 35 | return timer(0); 36 | } 37 | } 38 | 39 | @Injectable() 40 | export class TestRoutingCanActivate implements CanActivate { 41 | canActivate(): Observable { 42 | return timer(0).pipe(mapTo(true)); 43 | } 44 | } 45 | 46 | @Component({ 47 | selector: 'test', 48 | template: '', 49 | }) 50 | class TestComponent {} 51 | 52 | const TEST_SKELETON_DEPENDENCY = new InjectionToken('[TEST] Skeleton dependency'); 53 | 54 | @Component({ 55 | selector: 'test-skeleton-1', 56 | template: 'test-skeleton-1 with {{ dependency }}', 57 | }) 58 | class TestSkeleton1Component { 59 | constructor(@Inject(TEST_SKELETON_DEPENDENCY) public readonly dependency: string) {} 60 | } 61 | 62 | @Component({ 63 | selector: 'test-skeleton-2', 64 | template: 'test-skeleton-2 with {{ dependency }}', 65 | }) 66 | class TestSkeleton2Component { 67 | constructor(@Inject(TEST_SKELETON_DEPENDENCY) public readonly dependency: string) {} 68 | } 69 | 70 | @Component({ 71 | selector: 'fin-test-lazy', 72 | template: '', 73 | }) 74 | class TestLazyComponent {} 75 | 76 | @Component({ 77 | selector: 'test-skeleton-lazy', 78 | template: 'test-skeleton-lazy with {{ dependency }}', 79 | }) 80 | class TestSkeletonLazyComponent { 81 | constructor(@Inject(TEST_SKELETON_DEPENDENCY) public readonly dependency: string) {} 82 | } 83 | 84 | @NgModule({ 85 | imports: [ 86 | RouterModule.forChild([ 87 | { 88 | path: '2', 89 | component: TestLazyComponent, 90 | skeleton: { 91 | component: TestSkeletonLazyComponent, 92 | }, 93 | resolve: { 94 | test: TestRoutingResolve, 95 | }, 96 | }, 97 | ] as NavigationSkeletonRoute[]), 98 | ], 99 | declarations: [TestLazyComponent, TestSkeletonLazyComponent], 100 | providers: [ 101 | { 102 | provide: TEST_SKELETON_DEPENDENCY, 103 | useValue: 'dependency from lazy module', 104 | }, 105 | ], 106 | }) 107 | export class TestSkeletonLazyModule {} 108 | 109 | describe('NavigationSkeletonComponent | This component allows you to show skeletons of pages during navigation process', () => { 110 | let resolveMock: Resolve; 111 | let canActivateMock: CanActivate; 112 | 113 | let routes: NavigationSkeletonRoute[] = []; 114 | 115 | let router: Router; 116 | let ngZone: NgZone; 117 | let fixture: ComponentFixture; 118 | 119 | beforeEach(() => { 120 | resolveMock = mock(TestRoutingResolve); 121 | canActivateMock = mock(TestRoutingCanActivate); 122 | 123 | routes = [ 124 | { 125 | path: '1', 126 | component: TestComponent, 127 | skeleton: { 128 | component: TestSkeleton1Component, 129 | }, 130 | canActivate: [TestRoutingCanActivate], 131 | }, 132 | { 133 | path: '2', 134 | component: TestComponent, 135 | skeleton: { 136 | component: TestSkeleton2Component, 137 | }, 138 | resolve: { 139 | test: TestRoutingResolve, 140 | }, 141 | }, 142 | { 143 | path: '3', 144 | canActivate: [TestRoutingCanActivate], 145 | loadChildren: () => TestSkeletonLazyModule, 146 | }, 147 | { 148 | path: '4', 149 | component: TestComponent, 150 | canActivate: [TestRoutingCanActivate], 151 | }, 152 | { 153 | path: '5', 154 | canActivate: [TestRoutingCanActivate], 155 | loadChildren: () => TestSkeletonLazyModule, 156 | }, 157 | ]; 158 | }); 159 | 160 | beforeEach(() => { 161 | TestBed.configureTestingModule({ 162 | imports: [ 163 | CommonModule, 164 | RouterTestingModule.withRoutes(routes), 165 | BrowserAnimationsModule, 166 | ], 167 | declarations: [ 168 | TestRoutingComponent, 169 | TestComponent, 170 | TestSkeleton1Component, 171 | TestSkeleton2Component, 172 | NavigationSkeletonComponent, 173 | ], 174 | providers: [ 175 | {provide: TestRoutingResolve, useFactory: () => instance(resolveMock)}, 176 | { 177 | provide: TestRoutingCanActivate, 178 | useFactory: () => instance(canActivateMock), 179 | }, 180 | { 181 | provide: TEST_SKELETON_DEPENDENCY, 182 | useValue: 'dependency from parent module', 183 | }, 184 | ], 185 | }); 186 | }); 187 | 188 | function setupComponent() { 189 | fixture = TestBed.createComponent(TestRoutingComponent); 190 | router = TestBed.inject(Router); 191 | ngZone = TestBed.inject(NgZone); 192 | 193 | fixture.detectChanges(); 194 | } 195 | 196 | describe('When target route has a skeleton component', () => { 197 | it('Skeleton component must be taken from target route', fakeAsync(() => { 198 | // arrange 199 | when(canActivateMock.canActivate(anything(), anything())).thenReturn(NEVER); 200 | 201 | // act 202 | setupComponent(); 203 | ngZone.run(() => router.navigateByUrl('/3/2')); 204 | tick(); 205 | fixture.detectChanges(); 206 | 207 | // assert 208 | expect(fixture.debugElement.nativeElement.textContent).toBe( 209 | 'test-skeleton-lazy with dependency from lazy module', 210 | ); 211 | })); 212 | 213 | it('Skeleton component can be reused', fakeAsync(() => { 214 | // arrange 215 | const canActivate = new Subject(); 216 | 217 | when(canActivateMock.canActivate(anything(), anything())).thenReturn( 218 | canActivate, 219 | ); 220 | 221 | // act 222 | setupComponent(); 223 | ngZone.run(() => router.navigateByUrl('/3/2')); 224 | tick(); 225 | canActivate.next(true); 226 | fixture.detectChanges(); 227 | 228 | ngZone.run(() => router.navigateByUrl('/5/2')); 229 | tick(); 230 | fixture.detectChanges(); 231 | 232 | // assert 233 | expect(fixture.debugElement.nativeElement.textContent).toBe( 234 | 'test-skeleton-lazy with dependency from lazy module', 235 | ); 236 | })); 237 | 238 | it('In the process of route activating, skeleton component of target route is shown', fakeAsync(() => { 239 | // arrange 240 | when(canActivateMock.canActivate(anything(), anything())).thenReturn(NEVER); 241 | 242 | // act 243 | setupComponent(); 244 | ngZone.run(() => router.navigateByUrl('/1')); 245 | tick(); 246 | fixture.detectChanges(); 247 | 248 | // assert 249 | expect(fixture.debugElement.nativeElement.textContent).toBe( 250 | 'test-skeleton-1 with dependency from parent module', 251 | ); 252 | })); 253 | 254 | it('After route is activated, projected content is shown', fakeAsync(() => { 255 | // arrange 256 | when(canActivateMock.canActivate(anything(), anything())).thenReturn(true); 257 | 258 | // act 259 | setupComponent(); 260 | ngZone.run(() => router.navigateByUrl('/1')); 261 | tick(); 262 | fixture.detectChanges(); 263 | 264 | // assert 265 | expect(fixture.debugElement.nativeElement.textContent).toBe( 266 | 'projected-content', 267 | ); 268 | })); 269 | 270 | it('In the process of route data resolving, skeleton component of target route is shown', fakeAsync(() => { 271 | // arrange 272 | when(resolveMock.resolve(anything(), anything())).thenReturn(NEVER); 273 | 274 | // act 275 | setupComponent(); 276 | ngZone.run(() => router.navigateByUrl('/2')); 277 | tick(); 278 | fixture.detectChanges(); 279 | 280 | // assert 281 | expect(fixture.debugElement.nativeElement.textContent).toBe( 282 | 'test-skeleton-2 with dependency from parent module', 283 | ); 284 | })); 285 | 286 | it('After resolving route data, projected content is shown', fakeAsync(() => { 287 | // arrange 288 | when(resolveMock.resolve(anything(), anything())).thenReturn('data'); 289 | 290 | // act 291 | setupComponent(); 292 | ngZone.run(() => router.navigateByUrl('/2')); 293 | tick(); 294 | fixture.detectChanges(); 295 | 296 | // assert 297 | expect(fixture.debugElement.nativeElement.textContent).toBe( 298 | 'projected-content', 299 | ); 300 | })); 301 | }); 302 | 303 | it('When target route does not have a skeleton component - projected content is shown', fakeAsync(() => { 304 | // arrange 305 | when(canActivateMock.canActivate(anything(), anything())).thenReturn(NEVER); 306 | 307 | // act 308 | setupComponent(); 309 | ngZone.run(() => router.navigateByUrl('/4')); 310 | tick(); 311 | fixture.detectChanges(); 312 | 313 | // assert 314 | expect(fixture.debugElement.nativeElement.textContent).toBe('projected-content'); 315 | })); 316 | 317 | it('When animations are enabled - component works with them', fakeAsync(() => { 318 | // arrange 319 | when(resolveMock.resolve(anything(), anything())).thenReturn(NEVER); 320 | TestBed.overrideProvider(ANIMATION_MODULE_TYPE, {useValue: 'BrowserAnimations'}); 321 | 322 | // act 323 | setupComponent(); 324 | ngZone.run(() => router.navigateByUrl('/2')); 325 | tick(); 326 | fixture.detectChanges(); 327 | 328 | // assert 329 | expect( 330 | fixture.debugElement.query( 331 | element => element.classes['ng-trigger-triggerChildAnimation'], 332 | ), 333 | ).toBeTruthy(); 334 | })); 335 | 336 | it('When animations are off - component does not work with them', fakeAsync(() => { 337 | // arrange 338 | when(resolveMock.resolve(anything(), anything())).thenReturn(NEVER); 339 | TestBed.overrideProvider(ANIMATION_MODULE_TYPE, {useValue: null}); 340 | 341 | // act 342 | setupComponent(); 343 | ngZone.run(() => router.navigateByUrl('/2')); 344 | tick(); 345 | fixture.detectChanges(); 346 | 347 | // assert 348 | expect( 349 | fixture.debugElement.query( 350 | element => element.classes['ng-trigger-triggerChildAnimation'], 351 | ), 352 | ).toBeFalsy(); 353 | })); 354 | }); 355 | --------------------------------------------------------------------------------