├── .gitignore ├── README.md ├── angular.json ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.e2e.json ├── package.json ├── src ├── app │ ├── accessibility │ │ ├── active-descendant │ │ │ ├── active-item-option │ │ │ │ └── item-active-option.component.ts │ │ │ ├── cdk-active-descendant.component.html │ │ │ ├── cdk-active-descendant.component.less │ │ │ └── cdk-active-descendant.component.ts │ │ ├── cdk-accessibility-routing.module.ts │ │ ├── cdk-accessibility.component.html │ │ ├── cdk-accessibility.component.less │ │ ├── cdk-accessibility.component.ts │ │ ├── cdk-accessibility.module.ts │ │ ├── filter.pipe.ts │ │ ├── focus-monitor │ │ │ ├── cdk-focus-monitor.component.html │ │ │ ├── cdk-focus-monitor.component.less │ │ │ └── cdk-focus-monitor.component.ts │ │ ├── focus-trap-factory │ │ │ ├── cdk-focus-trap-factory.component.html │ │ │ ├── cdk-focus-trap-factory.component.less │ │ │ └── cdk-focus-trap-factory.component.ts │ │ ├── focus │ │ │ ├── cdk-focus.component.html │ │ │ ├── cdk-focus.component.less │ │ │ ├── cdk-focus.component.ts │ │ │ └── focus-item-option │ │ │ │ └── item-focus-option.component.ts │ │ ├── interactivity-checker │ │ │ ├── cdk-interactivity-checker.component.html │ │ │ ├── cdk-interactivity-checker.component.less │ │ │ └── cdk-interactivity-checker.component.ts │ │ └── live-announcer │ │ │ ├── cdk-live-announcer.component.html │ │ │ ├── cdk-live-announcer.component.less │ │ │ └── cdk-live-announcer.component.ts │ ├── app-routing.module.ts │ ├── app.component.html │ ├── app.component.less │ ├── app.component.ts │ ├── app.module.ts │ ├── bidi │ │ ├── cdk-bidi-routing.module.ts │ │ ├── cdk-bidi.component.html │ │ ├── cdk-bidi.component.less │ │ ├── cdk-bidi.component.ts │ │ └── cdk-bidi.module.ts │ ├── coercion │ │ ├── cdk-coercion-routing.module.ts │ │ ├── cdk-coercion.component.html │ │ ├── cdk-coercion.component.less │ │ ├── cdk-coercion.component.ts │ │ └── cdk-coercion.module.ts │ ├── drag-drop │ │ ├── cdk-drag-drop-routing.module.ts │ │ ├── cdk-drag-drop.component.html │ │ ├── cdk-drag-drop.component.less │ │ ├── cdk-drag-drop.component.ts │ │ ├── cdk-drag-drop.module.ts │ │ ├── controlling-with-item │ │ │ ├── drag-drop-controlling-with-item.component.html │ │ │ ├── drag-drop-controlling-with-item.component.less │ │ │ └── drag-drop-controlling-with-item.component.ts │ │ ├── customizing-drag-place-holder │ │ │ ├── drag-drop-customizing-drag-place-holder.component.html │ │ │ ├── drag-drop-customizing-drag-place-holder.component.less │ │ │ └── drag-drop-customizing-drag-place-holder.component.ts │ │ ├── customizing-drag-preview │ │ │ ├── drag-drop-customizing-drag-preview.component.html │ │ │ ├── drag-drop-customizing-drag-preview.component.less │ │ │ └── drag-drop-customizing-drag-preview.component.ts │ │ ├── disable-drag │ │ │ ├── drag-drop-disable-drag.component.html │ │ │ ├── drag-drop-disable-drag.component.less │ │ │ └── drag-drop-disable-drag.component.ts │ │ ├── drag-dialog │ │ │ ├── drag-drop-drag-dialog.component.html │ │ │ ├── drag-drop-drag-dialog.component.less │ │ │ └── drag-drop-drag-dialog.component.ts │ │ ├── drop-directive │ │ │ ├── drag-drop-drop.component.html │ │ │ ├── drag-drop-drop.component.less │ │ │ └── drag-drop-drop.component.ts │ │ ├── drop-list-directive │ │ │ ├── drag-drop-drop-list.component.html │ │ │ ├── drag-drop-drop-list.component.less │ │ │ └── drag-drop-drop-list.component.ts │ │ ├── handle-drag-area │ │ │ ├── drag-drop-handle-drag-area.component.html │ │ │ ├── drag-drop-handle-drag-area.component.less │ │ │ └── drag-drop-handle-drag-area.component.ts │ │ ├── orientation-drag │ │ │ ├── drag-drop-orientation-drag.component.html │ │ │ ├── drag-drop-orientation-drag.component.less │ │ │ └── drag-drop-orientation-drag.component.ts │ │ ├── restricting-move-area │ │ │ ├── drag-drop-restricting-move-area.component.html │ │ │ ├── drag-drop-restricting-move-area.component.less │ │ │ └── drag-drop-restricting-move-area.component.ts │ │ └── transferring-item │ │ │ ├── drag-drop-transferring-item.component.html │ │ │ ├── drag-drop-transferring-item.component.less │ │ │ └── drag-drop-transferring-item.component.ts │ ├── keycodes │ │ ├── cdk-key-codes-routing.module.ts │ │ ├── cdk-key-codes.component.html │ │ ├── cdk-key-codes.component.less │ │ ├── cdk-key-codes.component.ts │ │ └── cdk-key-codes.module.ts │ ├── layout │ │ ├── cdk-layout-routing.module.ts │ │ ├── cdk-layout.component.html │ │ ├── cdk-layout.component.less │ │ ├── cdk-layout.component.ts │ │ └── cdk-layout.module.ts │ ├── observers │ │ ├── cdk-observers-routing.module.ts │ │ ├── cdk-observers.component.html │ │ ├── cdk-observers.component.less │ │ ├── cdk-observers.component.ts │ │ ├── cdk-observers.module.ts │ │ └── child │ │ │ ├── observers-child.component.html │ │ │ ├── observers-child.component.less │ │ │ └── observers-child.component.ts │ ├── overlay │ │ ├── cdk-overlay-routing.module.ts │ │ ├── cdk-overlay.component.html │ │ ├── cdk-overlay.component.less │ │ ├── cdk-overlay.component.ts │ │ ├── cdk-overlay.module.ts │ │ └── panel │ │ │ └── overlay-panel.component.ts │ ├── platform │ │ ├── cdk-platform-routing.module.ts │ │ ├── cdk-platform.component.html │ │ ├── cdk-platform.component.less │ │ ├── cdk-platform.component.ts │ │ └── cdk-platform.module.ts │ ├── portal │ │ ├── cdk-portal-routing.module.ts │ │ ├── cdk-portal.component.html │ │ ├── cdk-portal.component.less │ │ ├── cdk-portal.component.ts │ │ ├── cdk-portal.module.ts │ │ ├── portal-child-component │ │ │ └── portal-child.component.ts │ │ ├── portal-component │ │ │ └── portal-component.component.ts │ │ ├── portal-template │ │ │ └── portal-template.component.ts │ │ └── portal-tool-tip │ │ │ ├── tool-tip.component.ts │ │ │ └── tool-tip.directive.ts │ ├── scrolling │ │ ├── cdk-scrolling-routing.module.ts │ │ ├── cdk-scrolling.component.html │ │ ├── cdk-scrolling.component.less │ │ ├── cdk-scrolling.component.ts │ │ ├── cdk-scrolling.module.ts │ │ ├── drag-scrolling │ │ │ ├── drag-scrolling.component.html │ │ │ ├── drag-scrolling.component.less │ │ │ └── drag-scrolling.component.ts │ │ ├── virtual-scroll-data-source │ │ │ ├── customer-data-source.ts │ │ │ ├── virtual-scroll-data-source.component.html │ │ │ ├── virtual-scroll-data-source.component.less │ │ │ └── virtual-scroll-data-source.component.ts │ │ ├── virtual-scroll-horizontal │ │ │ ├── virtual-scroll-horizontal.component.html │ │ │ ├── virtual-scroll-horizontal.component.less │ │ │ └── virtual-scroll-horizontal.component.ts │ │ ├── virtual-scroll-strategies │ │ │ ├── custom-virtual-scroll-strategies.ts │ │ │ ├── virtual-scroll-strategies.component.html │ │ │ ├── virtual-scroll-strategies.component.less │ │ │ └── virtual-scroll-strategies.component.ts │ │ └── virtual-scroll │ │ │ ├── virtual-scroll.component.html │ │ │ ├── virtual-scroll.component.less │ │ │ └── virtual-scroll.component.ts │ ├── table │ │ ├── cdk-base-table-routing.module.ts │ │ ├── cdk-base-table.component.html │ │ ├── cdk-base-table.component.less │ │ ├── cdk-base-table.component.ts │ │ └── cdk-base-table.module.ts │ ├── text-field │ │ ├── cdk-text-field-routing.module.ts │ │ ├── cdk-text-field.component.html │ │ ├── cdk-text-field.component.less │ │ ├── cdk-text-field.component.ts │ │ └── cdk-text-field.module.ts │ ├── tip │ │ ├── tip.component.html │ │ ├── tip.component.less │ │ ├── tip.component.ts │ │ └── tip.directive.ts │ └── workflow-stepper │ │ ├── cdk-workflow-stepper-routing.module.ts │ │ ├── cdk-workflow-stepper.component.html │ │ ├── cdk-workflow-stepper.component.less │ │ ├── cdk-workflow-stepper.component.ts │ │ ├── cdk-workflow-stepper.module.ts │ │ └── stepper │ │ ├── stepper-animations.ts │ │ ├── stepper-header.component.html │ │ ├── stepper-header.component.less │ │ ├── stepper-header.component.ts │ │ ├── stepper.component.html │ │ ├── stepper.component.less │ │ └── stepper.component.ts ├── assets │ └── .gitkeep ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── karma.conf.js ├── main.ts ├── polyfills.ts ├── styles.less ├── test.ts ├── tsconfig.app.json ├── tsconfig.spec.json └── tslint.json ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AngularCdkStudy 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.8. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-cdk-study": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "styleext": "less" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/angular-cdk-study", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "src/tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "src/styles.less" 31 | ], 32 | "scripts": [] 33 | }, 34 | "configurations": { 35 | "production": { 36 | "fileReplacements": [ 37 | { 38 | "replace": "src/environments/environment.ts", 39 | "with": "src/environments/environment.prod.ts" 40 | } 41 | ], 42 | "optimization": true, 43 | "outputHashing": "all", 44 | "sourceMap": false, 45 | "extractCss": true, 46 | "namedChunks": false, 47 | "aot": true, 48 | "extractLicenses": true, 49 | "vendorChunk": false, 50 | "buildOptimizer": true 51 | } 52 | } 53 | }, 54 | "serve": { 55 | "builder": "@angular-devkit/build-angular:dev-server", 56 | "options": { 57 | "browserTarget": "angular-cdk-study:build" 58 | }, 59 | "configurations": { 60 | "production": { 61 | "browserTarget": "angular-cdk-study:build:production" 62 | } 63 | } 64 | }, 65 | "extract-i18n": { 66 | "builder": "@angular-devkit/build-angular:extract-i18n", 67 | "options": { 68 | "browserTarget": "angular-cdk-study:build" 69 | } 70 | }, 71 | "test": { 72 | "builder": "@angular-devkit/build-angular:karma", 73 | "options": { 74 | "main": "src/test.ts", 75 | "polyfills": "src/polyfills.ts", 76 | "tsConfig": "src/tsconfig.spec.json", 77 | "karmaConfig": "src/karma.conf.js", 78 | "styles": [ 79 | "src/styles.less" 80 | ], 81 | "scripts": [], 82 | "assets": [ 83 | "src/favicon.ico", 84 | "src/assets" 85 | ] 86 | } 87 | }, 88 | "lint": { 89 | "builder": "@angular-devkit/build-angular:tslint", 90 | "options": { 91 | "tsConfig": [ 92 | "src/tsconfig.app.json", 93 | "src/tsconfig.spec.json" 94 | ], 95 | "exclude": [ 96 | "**/node_modules/**" 97 | ] 98 | } 99 | } 100 | } 101 | }, 102 | "angular-cdk-study-e2e": { 103 | "root": "e2e/", 104 | "projectType": "application", 105 | "architect": { 106 | "e2e": { 107 | "builder": "@angular-devkit/build-angular:protractor", 108 | "options": { 109 | "protractorConfig": "e2e/protractor.conf.js", 110 | "devServerTarget": "angular-cdk-study:serve" 111 | }, 112 | "configurations": { 113 | "production": { 114 | "devServerTarget": "angular-cdk-study:serve:production" 115 | } 116 | } 117 | }, 118 | "lint": { 119 | "builder": "@angular-devkit/build-angular:tslint", 120 | "options": { 121 | "tsConfig": "e2e/tsconfig.e2e.json", 122 | "exclude": [ 123 | "**/node_modules/**" 124 | ] 125 | } 126 | } 127 | } 128 | } 129 | }, 130 | "defaultProject": "angular-cdk-study" 131 | } -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to angular-cdk-study!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-cdk-study", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^6.0.3", 15 | "@angular/cdk": "^7.0.4", 16 | "@angular/common": "^6.0.3", 17 | "@angular/compiler": "^6.0.3", 18 | "@angular/core": "^6.0.3", 19 | "@angular/forms": "^6.0.3", 20 | "@angular/http": "^6.0.3", 21 | "@angular/platform-browser": "^6.0.3", 22 | "@angular/platform-browser-dynamic": "^6.0.3", 23 | "@angular/router": "^6.0.3", 24 | "core-js": "^2.5.4", 25 | "rxjs": "^6.0.0", 26 | "zone.js": "^0.8.26" 27 | }, 28 | "devDependencies": { 29 | "@angular/compiler-cli": "^6.0.3", 30 | "@angular-devkit/build-angular": "~0.6.8", 31 | "typescript": "~2.7.2", 32 | "@angular/cli": "~6.0.8", 33 | "@angular/language-service": "^6.0.3", 34 | "@types/jasmine": "~2.8.6", 35 | "@types/jasminewd2": "~2.0.3", 36 | "@types/node": "~8.9.4", 37 | "codelyzer": "~4.2.1", 38 | "jasmine-core": "~2.99.1", 39 | "jasmine-spec-reporter": "~4.2.1", 40 | "karma": "~1.7.1", 41 | "karma-chrome-launcher": "~2.2.0", 42 | "karma-coverage-istanbul-reporter": "~2.0.0", 43 | "karma-jasmine": "~1.1.1", 44 | "karma-jasmine-html-reporter": "^0.2.2", 45 | "protractor": "~5.3.0", 46 | "ts-node": "~5.0.1", 47 | "tslint": "~5.9.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/accessibility/active-descendant/active-item-option/item-active-option.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, HostBinding, Input} from '@angular/core'; 2 | import {Highlightable} from "@angular/cdk/a11y"; 3 | 4 | @Component({ 5 | selector: 'app-item-active-option', 6 | template: ` 7 |
8 | 9 |
10 | `, 11 | styles: [` 12 | .active { 13 | background-color: lightblue; 14 | color: #fff; 15 | } 16 | 17 | .disabled { 18 | opacity: 0.3; 19 | } 20 | `] 21 | }) 22 | export class ItemActiveOptionComponent implements Highlightable { 23 | @Input() item; 24 | @Input() disabled = false; 25 | private _isActive = false; 26 | 27 | @HostBinding('class.active') get isActive() { 28 | return this._isActive; 29 | } 30 | 31 | setActiveStyles() { 32 | this._isActive = true; 33 | } 34 | 35 | setInactiveStyles() { 36 | this._isActive = false; 37 | } 38 | 39 | getLabel() { 40 | return this.item.name; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/accessibility/active-descendant/cdk-active-descendant.component.html: -------------------------------------------------------------------------------- 1 |

ActiveDescendantKeyManager使用

2 |
3 | 4 |
5 | 6 |
7 | 9 | {{user.name}} 10 | 11 |
12 | 13 |

Model: {{model}}

-------------------------------------------------------------------------------- /src/app/accessibility/active-descendant/cdk-active-descendant.component.less: -------------------------------------------------------------------------------- 1 | .form-control { 2 | display: block; 3 | width: 100%; 4 | height: calc(2.25rem + 2px); 5 | padding: .375rem .75rem; 6 | font-size: 1rem; 7 | line-height: 1.5; 8 | color: #495057; 9 | background-color: #fff; 10 | background-clip: padding-box; 11 | border: 1px solid #ced4da; 12 | border-radius: .25rem; 13 | transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; 14 | } 15 | 16 | .form-control:focus { 17 | color: #495057; 18 | background-color: #fff; 19 | border-color: #80bdff; 20 | outline: 0; 21 | box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); 22 | } 23 | 24 | 25 | .list-group-item:first-child { 26 | margin-top: 1rem; 27 | border-top-left-radius: .25rem; 28 | border-top-right-radius: .25rem; 29 | } 30 | 31 | .list-group-item  { 32 | position: relative; 33 | display: block; 34 | padding: .75rem 1.25rem; 35 | margin-bottom: -1px; 36 | background-color: #fff; 37 | border: 1px solid rgba(0, 0, 0, .125); 38 | } 39 | 40 | .list-group-item:last-child { 41 | margin-bottom: 0; 42 | border-bottom-right-radius: .25rem; 43 | border-bottom-left-radius: .25rem; 44 | } 45 | 46 | .list-group-item.active  { 47 | z-index: 2; 48 | color: #fff; 49 | background-color: #007bff; 50 | border-color: #007bff; 51 | } -------------------------------------------------------------------------------- /src/app/accessibility/active-descendant/cdk-active-descendant.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, QueryList, ViewChildren} from '@angular/core'; 2 | import {ItemActiveOptionComponent} from "./active-item-option/item-active-option.component"; 3 | import {ActiveDescendantKeyManager} from "@angular/cdk/a11y"; 4 | import {ENTER} from "@angular/cdk/keycodes"; 5 | 6 | @Component({ 7 | selector: 'app-cdk-active-descendant', 8 | templateUrl: './cdk-active-descendant.component.html', 9 | styleUrls: ['./cdk-active-descendant.component.less'] 10 | }) 11 | export class CdkActiveDescendantComponent implements AfterViewInit { 12 | 13 | @ViewChildren(ItemActiveOptionComponent) items: QueryList; 14 | users = [ 15 | { 16 | "id": "5b902934d965e7501f4e1c6f", 17 | "name": "Caroline Hodges" 18 | }, 19 | { 20 | "id": "5b9029348f7eed8b6f5f02db", 21 | "name": "Delores Rivas" 22 | }, 23 | { 24 | "id": "5b9029346f48c8407c64d0d5", 25 | "name": "Darlene Franklin" 26 | }, 27 | { 28 | "id": "5b9029341eff315fa87f9e21", 29 | "name": "Alfreda Love" 30 | }, 31 | { 32 | "id": "5b9029342e8917c6ccdb9865", 33 | "name": "Marcy Ratliff" 34 | }, 35 | { 36 | "id": "5b9029349dbb48013460e01f", 37 | "name": "Beulah Nielsen" 38 | }, 39 | { 40 | "id": "5b902934f4f1586e5e72d74a", 41 | "name": "Morton Kerr" 42 | }, 43 | { 44 | "id": "5b9029347918bb204bf7014e", 45 | "name": "Autumn Tillman" 46 | }, 47 | { 48 | "id": "5b902934b86f80e1fc60c626", 49 | "name": "Diane Bennett" 50 | }, 51 | { 52 | "id": "5b9029348999f59215020349", 53 | "name": "June Eaton" 54 | } 55 | ]; 56 | 57 | private keyManager: ActiveDescendantKeyManager; 58 | model = ''; 59 | 60 | ngAfterViewInit() { 61 | this.keyManager = new ActiveDescendantKeyManager(this.items).withWrap() 62 | .withTypeAhead(); 63 | 64 | } 65 | 66 | onKeyDown(event) { 67 | if (event.keyCode === ENTER) { 68 | this.model = this.keyManager.activeItem.item.name; 69 | } else { 70 | this.keyManager.onKeydown(event); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/app/accessibility/cdk-accessibility-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkAccessibilityComponent} from './cdk-accessibility.component'; 5 | 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: CdkAccessibilityComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | RouterModule.forChild(routes), 17 | CommonModule 18 | ], 19 | exports: [ 20 | RouterModule 21 | ], 22 | providers: [] 23 | }) 24 | export class CdkAccessibilityRoutingModule { } 25 | -------------------------------------------------------------------------------- /src/app/accessibility/cdk-accessibility.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |
7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 | 28 | 29 |
30 | 31 | 32 |
33 | 34 | -------------------------------------------------------------------------------- /src/app/accessibility/cdk-accessibility.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/accessibility/cdk-accessibility.component.less -------------------------------------------------------------------------------- /src/app/accessibility/cdk-accessibility.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-cdk-accessibility', 5 | templateUrl: './cdk-accessibility.component.html', 6 | styleUrls: ['./cdk-accessibility.component.less'] 7 | }) 8 | export class CdkAccessibilityComponent { 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/app/accessibility/cdk-accessibility.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule} from '@angular/router'; 4 | import {CdkAccessibilityRoutingModule} from './cdk-accessibility-routing.module'; 5 | import {CdkAccessibilityComponent} from './cdk-accessibility.component'; 6 | import {ItemActiveOptionComponent} from './active-descendant/active-item-option/item-active-option.component'; 7 | import {FilterPipe} from "./filter.pipe"; 8 | import {A11yModule} from "@angular/cdk/a11y"; 9 | import {CdkActiveDescendantComponent} from './active-descendant/cdk-active-descendant.component'; 10 | import {CdkFocusComponent} from './focus/cdk-focus.component'; 11 | import {ItemFocusOptionComponent} from './focus/focus-item-option/item-focus-option.component'; 12 | import { CdkFocusTrapFactoryComponent } from './focus-trap-factory/cdk-focus-trap-factory.component'; 13 | import { CdkFocusMonitorComponent } from './focus-monitor/cdk-focus-monitor.component'; 14 | import { CdkInteractivityCheckerComponent } from './interactivity-checker/cdk-interactivity-checker.component'; 15 | import { CdkLiveAnnouncerComponent } from './live-announcer/cdk-live-announcer.component'; 16 | 17 | @NgModule({ 18 | imports: [ 19 | CommonModule, 20 | RouterModule, 21 | A11yModule, 22 | CdkAccessibilityRoutingModule 23 | ], 24 | declarations: [ 25 | CdkAccessibilityComponent, 26 | ItemActiveOptionComponent, 27 | FilterPipe, 28 | CdkActiveDescendantComponent, 29 | CdkFocusComponent, 30 | ItemFocusOptionComponent, 31 | CdkFocusTrapFactoryComponent, 32 | CdkFocusMonitorComponent, 33 | CdkInteractivityCheckerComponent, 34 | CdkLiveAnnouncerComponent 35 | ] 36 | }) 37 | export class CdkAccessibilityModule { 38 | } 39 | -------------------------------------------------------------------------------- /src/app/accessibility/filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import {Pipe, PipeTransform} from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'filter' 5 | }) 6 | export class FilterPipe implements PipeTransform { 7 | 8 | transform(items: any[], field: string, value: string): any[] { 9 | if (!items) { 10 | return []; 11 | } 12 | if (!value || value.length === 0) { 13 | return items; 14 | } 15 | return items.filter(it => 16 | it[field].toLowerCase().indexOf(value.toLowerCase()) !== -1); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/app/accessibility/focus-monitor/cdk-focus-monitor.component.html: -------------------------------------------------------------------------------- 1 |

FocusMonitor使用(监控元素focus状态,以及focus来源是什么)

2 |
3 | 4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /src/app/accessibility/focus-monitor/cdk-focus-monitor.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/accessibility/focus-monitor/cdk-focus-monitor.component.less -------------------------------------------------------------------------------- /src/app/accessibility/focus-monitor/cdk-focus-monitor.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef, OnDestroy, OnInit, Renderer2} from '@angular/core'; 2 | import {FocusMonitor} from "@angular/cdk/a11y"; 3 | 4 | @Component({ 5 | selector: 'app-cdk-focus-monitor', 6 | templateUrl: './cdk-focus-monitor.component.html', 7 | styleUrls: ['./cdk-focus-monitor.component.less'] 8 | }) 9 | export class CdkFocusMonitorComponent implements AfterViewInit, OnDestroy { 10 | 11 | constructor(private _elementRef: ElementRef, 12 | private _focusMonitor: FocusMonitor) { 13 | } 14 | 15 | ngAfterViewInit() { 16 | this._focusMonitor.monitor(this._elementRef.nativeElement, true).subscribe(mode => { 17 | console.log('元素获取到焦点 focused 来源 ' + mode); 18 | }); 19 | } 20 | 21 | ngOnDestroy() { 22 | this._focusMonitor.stopMonitoring(this._elementRef.nativeElement); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/app/accessibility/focus-trap-factory/cdk-focus-trap-factory.component.html: -------------------------------------------------------------------------------- 1 |

FocusTrapFactory使用(给元素添加cdkFocusTrap指令)

2 |
3 | 4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /src/app/accessibility/focus-trap-factory/cdk-focus-trap-factory.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/accessibility/focus-trap-factory/cdk-focus-trap-factory.component.less -------------------------------------------------------------------------------- /src/app/accessibility/focus-trap-factory/cdk-focus-trap-factory.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef} from '@angular/core'; 2 | import {FocusTrap, FocusTrapFactory} from "@angular/cdk/a11y"; 3 | 4 | @Component({ 5 | selector: 'app-cdk-focus-trap-factory', 6 | templateUrl: './cdk-focus-trap-factory.component.html', 7 | styleUrls: ['./cdk-focus-trap-factory.component.less'] 8 | }) 9 | export class CdkFocusTrapFactoryComponent implements AfterViewInit { 10 | 11 | private _focusTrap: FocusTrap; 12 | 13 | constructor(private _elementRef: ElementRef, 14 | private _focusTrapFactory: FocusTrapFactory) { 15 | } 16 | 17 | ngAfterViewInit() { 18 | this._focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/accessibility/focus/cdk-focus.component.html: -------------------------------------------------------------------------------- 1 |

FocusKeyManager使用

2 | 3 |
4 | 7 | {{user.name}} 8 | 9 |
-------------------------------------------------------------------------------- /src/app/accessibility/focus/cdk-focus.component.less: -------------------------------------------------------------------------------- 1 | .form-control { 2 | display: block; 3 | width: 100%; 4 | height: calc(2.25rem + 2px); 5 | padding: .375rem .75rem; 6 | font-size: 1rem; 7 | line-height: 1.5; 8 | color: #495057; 9 | background-color: #fff; 10 | background-clip: padding-box; 11 | border: 1px solid #ced4da; 12 | border-radius: .25rem; 13 | transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; 14 | } 15 | 16 | .form-control:focus { 17 | color: #495057; 18 | background-color: #fff; 19 | border-color: #80bdff; 20 | outline: 0; 21 | box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); 22 | } 23 | 24 | 25 | .list-group-item:first-child { 26 | margin-top: 1rem; 27 | border-top-left-radius: .25rem; 28 | border-top-right-radius: .25rem; 29 | } 30 | 31 | .list-group-item  { 32 | position: relative; 33 | display: block; 34 | padding: .75rem 1.25rem; 35 | margin-bottom: -1px; 36 | background-color: #fff; 37 | border: 1px solid rgba(0, 0, 0, .125); 38 | } 39 | 40 | .list-group-item:last-child { 41 | margin-bottom: 0; 42 | border-bottom-right-radius: .25rem; 43 | border-bottom-left-radius: .25rem; 44 | } 45 | 46 | .list-group-item.focus { 47 | z-index: 2; 48 | color: #fff; 49 | background-color: #007bff; 50 | border-color: #007bff; 51 | } -------------------------------------------------------------------------------- /src/app/accessibility/focus/cdk-focus.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, QueryList, ViewChildren} from '@angular/core'; 2 | import {FocusKeyManager} from "@angular/cdk/a11y"; 3 | import {ItemFocusOptionComponent} from "./focus-item-option/item-focus-option.component"; 4 | 5 | @Component({ 6 | selector: 'app-cdk-focus', 7 | templateUrl: './cdk-focus.component.html', 8 | styleUrls: ['./cdk-focus.component.less'] 9 | }) 10 | export class CdkFocusComponent implements AfterViewInit { 11 | 12 | @ViewChildren(ItemFocusOptionComponent) items: QueryList; 13 | users = [ 14 | { 15 | "id": "5b902934d965e7501f4e1c6f", 16 | "name": "Caroline Hodges" 17 | }, 18 | { 19 | "id": "5b9029348f7eed8b6f5f02db", 20 | "name": "Delores Rivas" 21 | }, 22 | { 23 | "id": "5b9029346f48c8407c64d0d5", 24 | "name": "Darlene Franklin" 25 | }, 26 | { 27 | "id": "5b9029341eff315fa87f9e21", 28 | "name": "Alfreda Love" 29 | }, 30 | { 31 | "id": "5b9029342e8917c6ccdb9865", 32 | "name": "Marcy Ratliff" 33 | }, 34 | { 35 | "id": "5b9029349dbb48013460e01f", 36 | "name": "Beulah Nielsen" 37 | }, 38 | { 39 | "id": "5b902934f4f1586e5e72d74a", 40 | "name": "Morton Kerr" 41 | }, 42 | { 43 | "id": "5b9029347918bb204bf7014e", 44 | "name": "Autumn Tillman" 45 | }, 46 | { 47 | "id": "5b902934b86f80e1fc60c626", 48 | "name": "Diane Bennett" 49 | }, 50 | { 51 | "id": "5b9029348999f59215020349", 52 | "name": "June Eaton" 53 | } 54 | ]; 55 | 56 | private keyManager: FocusKeyManager; 57 | 58 | ngAfterViewInit() { 59 | this.keyManager = new FocusKeyManager(this.items).withWrap() 60 | .withTypeAhead(); 61 | 62 | } 63 | 64 | onKeyDown(event) { 65 | this.keyManager.onKeydown(event); 66 | } 67 | 68 | selectItem(index: number) { 69 | this.keyManager.setActiveItem(index); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/app/accessibility/focus/focus-item-option/item-focus-option.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ElementRef, HostBinding, Input} from '@angular/core'; 2 | import {FocusableOption, FocusOrigin} from "@angular/cdk/a11y"; 3 | 4 | @Component({ 5 | selector: 'app-item-focus-option', 6 | template: ` 7 | 8 | `, 9 | styles: [ 10 | `:host:focus { 11 | background: lightblue; 12 | color: #fff; 13 | }` 14 | ] 15 | }) 16 | 17 | export class ItemFocusOptionComponent implements FocusableOption { 18 | @Input() item; 19 | 20 | /** 21 | * 屏蔽掉默认的键盘事件,js里面自己控制键盘事件 22 | */ 23 | @HostBinding('tabindex') tabindex = '-1'; 24 | 25 | constructor(private host: ElementRef) { 26 | } 27 | 28 | getLabel() { 29 | return this.item.name; 30 | } 31 | 32 | focus(origin?: FocusOrigin): void { 33 | this.host.nativeElement.focus(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/accessibility/interactivity-checker/cdk-interactivity-checker.component.html: -------------------------------------------------------------------------------- 1 |

InteractivityChecker使用

2 | 3 |

上面button是否disable: {{disable}}

4 |

上面button是否visible: {{visible}}

5 |

上面button是否可以tabable: {{tabable}}

6 |

上面button是否可以focusable: {{focusable}}

7 | -------------------------------------------------------------------------------- /src/app/accessibility/interactivity-checker/cdk-interactivity-checker.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/accessibility/interactivity-checker/cdk-interactivity-checker.component.less -------------------------------------------------------------------------------- /src/app/accessibility/interactivity-checker/cdk-interactivity-checker.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; 2 | import {InteractivityChecker} from "@angular/cdk/a11y"; 3 | 4 | @Component({ 5 | selector: 'app-cdk-interactivity-checker', 6 | templateUrl: './cdk-interactivity-checker.component.html', 7 | styleUrls: ['./cdk-interactivity-checker.component.less'] 8 | }) 9 | export class CdkInteractivityCheckerComponent implements OnInit { 10 | 11 | @ViewChild('interactivityCheckerButton') button: ElementRef; 12 | disable: boolean; 13 | visible: boolean; 14 | tabable: boolean; 15 | focusable: boolean; 16 | 17 | constructor(private _interactivityChecker: InteractivityChecker) { 18 | } 19 | 20 | ngOnInit() { 21 | this.disable = this._interactivityChecker.isDisabled(this.button.nativeElement); 22 | this.visible = this._interactivityChecker.isVisible(this.button.nativeElement); 23 | this.tabable = this._interactivityChecker.isTabbable(this.button.nativeElement); 24 | this.focusable = this._interactivityChecker.isFocusable(this.button.nativeElement); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/app/accessibility/live-announcer/cdk-live-announcer.component.html: -------------------------------------------------------------------------------- 1 |

LiveAnnouncer使用(在body上append一个内容,一般在做html aria的时候用到)

-------------------------------------------------------------------------------- /src/app/accessibility/live-announcer/cdk-live-announcer.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/accessibility/live-announcer/cdk-live-announcer.component.less -------------------------------------------------------------------------------- /src/app/accessibility/live-announcer/cdk-live-announcer.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {LiveAnnouncer} from "@angular/cdk/a11y"; 3 | 4 | @Component({ 5 | selector: 'app-cdk-live-announcer', 6 | templateUrl: './cdk-live-announcer.component.html', 7 | styleUrls: ['./cdk-live-announcer.component.less'] 8 | }) 9 | export class CdkLiveAnnouncerComponent { 10 | 11 | index = 1; 12 | 13 | constructor(private liveAnnouncer: LiveAnnouncer) { 14 | liveAnnouncer.announce("Hey Google"); 15 | setTimeout(() => { 16 | this.timerTask(); 17 | }, 3000); 18 | } 19 | 20 | timerTask() { 21 | this.index = this.index + 1; 22 | this.liveAnnouncer.announce("Hey Google " + this.index.toString(), "assertive"); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkCoercionModule} from './coercion/cdk-coercion.module'; 4 | import {RouterModule, Routes} from '@angular/router'; 5 | import {CdkLayoutModule} from './layout/cdk-layout.module'; 6 | import {CdkKeyCodesModule} from './keycodes/cdk-key-codes.module'; 7 | import {CdkAccessibilityModule} from './accessibility/cdk-accessibility.module'; 8 | import {CdkBidiModule} from './bidi/cdk-bidi.module'; 9 | import {CdkScrollingModule} from './scrolling/cdk-scrolling.module'; 10 | import {CdkLPortalModule} from './portal/cdk-portal.module'; 11 | import {CdkOverlayModule} from './overlay/cdk-overlay.module'; 12 | import {TipComponent} from './tip/tip.component'; 13 | import {CdkPlatformModule} from './platform/cdk-platform.module'; 14 | import {CdkObserversModule} from './observers/cdk-observers.module'; 15 | import {CdkDragDropModule} from './drag-drop/cdk-drag-drop.module'; 16 | import {CdkTextFieldModule} from "./text-field/cdk-text-field.module"; 17 | import {CdkWorkflowStepperModule} from "./workflow-stepper/cdk-workflow-stepper.module"; 18 | import {CdkBaseTableModule} from './table/cdk-base-table.module'; 19 | 20 | const appRoutes: Routes = [ 21 | { 22 | path: 'coercion', 23 | loadChildren: () => CdkCoercionModule 24 | }, { 25 | path: 'layout', 26 | loadChildren: () => CdkLayoutModule 27 | }, { 28 | path: 'keycodes', 29 | loadChildren: () => CdkKeyCodesModule 30 | }, { 31 | path: 'accessibility', 32 | loadChildren: () => CdkAccessibilityModule 33 | }, { 34 | path: 'bidi', 35 | loadChildren: () => CdkBidiModule 36 | }, { 37 | path: 'scrolling', 38 | loadChildren: () => CdkScrollingModule 39 | }, { 40 | path: 'portal', 41 | loadChildren: () => CdkLPortalModule 42 | }, { 43 | path: 'overlay', 44 | loadChildren: () => CdkOverlayModule 45 | }, { 46 | path: 'platform', 47 | loadChildren: () => CdkPlatformModule 48 | }, { 49 | path: 'observers', 50 | loadChildren: () => CdkObserversModule 51 | }, { 52 | path: 'dragDrop', 53 | loadChildren: () => CdkDragDropModule 54 | }, { 55 | path: 'stepper', 56 | loadChildren: () => CdkWorkflowStepperModule 57 | }, { 58 | path: 'table', 59 | loadChildren: () => CdkBaseTableModule 60 | }, { 61 | path: 'textField', 62 | loadChildren: () => CdkTextFieldModule 63 | }, { 64 | path: 'tip', 65 | component: TipComponent 66 | }, { 67 | path: '', 68 | redirectTo: '/coercion', 69 | pathMatch: 'full' 70 | }, 71 | ]; 72 | 73 | @NgModule({ 74 | imports: [ 75 | RouterModule.forRoot(appRoutes), 76 | CommonModule 77 | ], 78 | exports: [ 79 | RouterModule 80 | ], 81 | providers: [] 82 | }) 83 | export class AppRoutingModule { 84 | } 85 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

@angular/cdk 内容学习

2 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/app/app.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/app.component.less -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.less'] 7 | }) 8 | export class AppComponent { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import {BrowserModule} from '@angular/platform-browser'; 2 | import {NgModule} from '@angular/core'; 3 | 4 | import {AppComponent} from './app.component'; 5 | import {CdkLayoutModule} from './layout/cdk-layout.module'; 6 | import {CdkCoercionModule} from './coercion/cdk-coercion.module'; 7 | import {AppRoutingModule} from './app-routing.module'; 8 | import {FormsModule} from '@angular/forms'; 9 | import {RouterModule} from '@angular/router'; 10 | import {CdkKeyCodesModule} from './keycodes/cdk-key-codes.module'; 11 | import {CdkOverlayModule} from './overlay/cdk-overlay.module'; 12 | import {CdkScrollingModule} from './scrolling/cdk-scrolling.module'; 13 | import {TipDirective} from './tip/tip.directive'; 14 | import {TipComponent} from './tip/tip.component'; 15 | import {CdkPlatformModule} from './platform/cdk-platform.module'; 16 | import {CdkObserversModule} from './observers/cdk-observers.module'; 17 | import {CdkDragDropModule} from './drag-drop/cdk-drag-drop.module'; 18 | import {CdkTextFieldModule} from "./text-field/cdk-text-field.module"; 19 | import {CdkWorkflowStepperModule} from "./workflow-stepper/cdk-workflow-stepper.module"; 20 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 21 | import {FullscreenOverlayContainer, OverlayContainer} from '@angular/cdk/overlay'; 22 | import {CdkBaseTableModule} from './table/cdk-base-table.module'; 23 | 24 | @NgModule({ 25 | declarations: [ 26 | AppComponent, 27 | TipDirective, 28 | TipComponent 29 | ], 30 | imports: [ 31 | RouterModule, 32 | FormsModule, 33 | BrowserModule, 34 | BrowserAnimationsModule, 35 | CdkCoercionModule, 36 | CdkLayoutModule, 37 | CdkKeyCodesModule, 38 | CdkScrollingModule, 39 | CdkOverlayModule, 40 | CdkPlatformModule, 41 | CdkObserversModule, 42 | CdkDragDropModule, 43 | CdkTextFieldModule, 44 | CdkWorkflowStepperModule, 45 | CdkBaseTableModule, 46 | AppRoutingModule 47 | ], 48 | providers: [ 49 | {provide: OverlayContainer, useClass: FullscreenOverlayContainer}, 50 | 51 | ], 52 | bootstrap: [AppComponent] 53 | }) 54 | export class AppModule { 55 | } 56 | -------------------------------------------------------------------------------- /src/app/bidi/cdk-bidi-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkBidiComponent} from './cdk-bidi.component'; 5 | 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: CdkBidiComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | RouterModule.forChild(routes), 17 | CommonModule 18 | ], 19 | exports: [ 20 | RouterModule 21 | ], 22 | providers: [] 23 | }) 24 | export class CdkBidiRoutingModule { 25 | } 26 | -------------------------------------------------------------------------------- /src/app/bidi/cdk-bidi.component.html: -------------------------------------------------------------------------------- 1 | 2 |

3 | 从左到右排列 4 |

5 | 6 |

7 | 从右到左排列 8 |

9 |
10 | 11 |
第一个位置--内容靠左 13 |
14 |
第二个位置--内容靠右 16 |
17 | 18 |
19 | 20 |

21 | 可以动态切换方向 22 |

23 | 24 | -------------------------------------------------------------------------------- /src/app/bidi/cdk-bidi.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/bidi/cdk-bidi.component.less -------------------------------------------------------------------------------- /src/app/bidi/cdk-bidi.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Inject, OnDestroy, OnInit} from '@angular/core'; 2 | import {DIR_DOCUMENT, Directionality} from '@angular/cdk/bidi'; 3 | import {Subscription} from "rxjs"; 4 | 5 | @Component({ 6 | selector: 'app-cdk-bidi', 7 | templateUrl: './cdk-bidi.component.html', 8 | styleUrls: ['./cdk-bidi.component.less'] 9 | }) 10 | export class CdkBidiComponent implements OnInit, OnDestroy { 11 | dir = "rtl"; 12 | /** Subscription to the Directionality change EventEmitter. */ 13 | private _dirChangeSubscription = Subscription.EMPTY; 14 | 15 | constructor( 16 | @Inject(DIR_DOCUMENT) public dirDoc: any, 17 | public directionality: Directionality 18 | ) { 19 | this._dirChangeSubscription = directionality.change.subscribe(() => { 20 | }); 21 | } 22 | 23 | ngOnInit() { 24 | // 获取document 25 | console.log(this.dirDoc); 26 | // ltr 获取当前值 27 | const dir = this.directionality.value; 28 | console.log("dir is ", dir); 29 | } 30 | 31 | ngOnDestroy() { 32 | this._dirChangeSubscription.unsubscribe(); 33 | } 34 | 35 | switchDir() { 36 | if (this.dir === "rtl") { 37 | this.dir = "ltr"; 38 | } else { 39 | this.dir = "rtl"; 40 | } 41 | } 42 | 43 | dirChange() { 44 | console.log('aaa'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/bidi/cdk-bidi.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule} from '@angular/router'; 4 | import {CdkBidiRoutingModule} from './cdk-bidi-routing.module'; 5 | import {CdkBidiComponent} from './cdk-bidi.component'; 6 | import {BidiModule} from "@angular/cdk/bidi"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | RouterModule, 12 | BidiModule, 13 | CdkBidiRoutingModule 14 | ], 15 | declarations: [ 16 | CdkBidiComponent 17 | ] 18 | }) 19 | export class CdkBidiModule { 20 | } 21 | -------------------------------------------------------------------------------- /src/app/coercion/cdk-coercion-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkCoercionComponent} from './cdk-coercion.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkCoercionComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkCoercionRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/coercion/cdk-coercion.component.html: -------------------------------------------------------------------------------- 1 |

coercion cdk 里面常用转换工具类

2 |

转boolean: coerceBooleanProperty

3 |

转number: coerceNumberProperty

4 |

是否是number: _isNumberValue

5 |

转换为数组: coerceArray

6 |

转CSS像素: coerceCssPixelValue

7 | -------------------------------------------------------------------------------- /src/app/coercion/cdk-coercion.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/coercion/cdk-coercion.component.less -------------------------------------------------------------------------------- /src/app/coercion/cdk-coercion.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import { 3 | _isNumberValue, 4 | coerceArray, 5 | coerceBooleanProperty, 6 | coerceCssPixelValue, 7 | coerceNumberProperty 8 | } from "@angular/cdk/coercion"; 9 | 10 | @Component({ 11 | selector: 'app-cdk-coercion', 12 | templateUrl: './cdk-coercion.component.html', 13 | styleUrls: ['./cdk-coercion.component.less'] 14 | }) 15 | export class CdkCoercionComponent implements OnInit { 16 | 17 | constructor() { 18 | } 19 | 20 | ngOnInit() { 21 | // 转boolean 22 | console.log("转boolean: " + coerceBooleanProperty('false')); 23 | // 转number 24 | console.log("转number: " + coerceNumberProperty(10.5)); 25 | console.log("是否是number类型: " + coerceNumberProperty('a', this.coerceNumberFallback())); 26 | console.log("是否是number类型: " + _isNumberValue('a')); 27 | // 转数组 28 | console.log("转换为数组: " + coerceArray(1204)); 29 | // 转CSS pixel value 30 | console.log("转CSS像素: " + coerceCssPixelValue('10')); 31 | } 32 | 33 | /** 34 | * 当转number的时候发生了错误时候的返回值 35 | */ 36 | coerceNumberFallback() { 37 | return 10; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/app/coercion/cdk-coercion.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkCoercionComponent} from './cdk-coercion.component'; 4 | import {CdkCoercionRoutingModule} from './cdk-coercion-routing.module'; 5 | import {RouterModule} from '@angular/router'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | RouterModule, 11 | CdkCoercionRoutingModule 12 | ], 13 | declarations: [ 14 | CdkCoercionComponent 15 | ] 16 | }) 17 | export class CdkCoercionModule { 18 | } 19 | -------------------------------------------------------------------------------- /src/app/drag-drop/cdk-drag-drop-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkDragDropComponent} from './cdk-drag-drop.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkDragDropComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkDragDropRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/drag-drop/cdk-drag-drop.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |
7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | 15 |
16 | 17 | 18 |
19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 | 27 |
28 | 29 | 30 |
31 | 32 | -------------------------------------------------------------------------------- /src/app/drag-drop/cdk-drag-drop.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/drag-drop/cdk-drag-drop.component.less -------------------------------------------------------------------------------- /src/app/drag-drop/cdk-drag-drop.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-cdk-drag-drop', 5 | templateUrl: './cdk-drag-drop.component.html', 6 | styleUrls: ['./cdk-drag-drop.component.less'] 7 | }) 8 | export class CdkDragDropComponent { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/app/drag-drop/cdk-drag-drop.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkDragDropComponent} from './cdk-drag-drop.component'; 4 | import {CdkDragDropRoutingModule} from './cdk-drag-drop-routing.module'; 5 | import {DragDropModule} from '@angular/cdk/drag-drop'; 6 | import {RouterModule} from '@angular/router'; 7 | import {DragDropDropListComponent} from './drop-list-directive/drag-drop-drop-list.component'; 8 | import {DragDropDropComponent} from './drop-directive/drag-drop-drop.component'; 9 | import {DragDropTransferringItemComponent} from './transferring-item/drag-drop-transferring-item.component'; 10 | import {DragDropHandleDragAreaComponent} from './handle-drag-area/drag-drop-handle-drag-area.component'; 11 | import {DragDropCustomizingDragPreviewComponent} from './customizing-drag-preview/drag-drop-customizing-drag-preview.component'; 12 | import {DragDropCustomizingDragPlaceHolderComponent} from './customizing-drag-place-holder/drag-drop-customizing-drag-place-holder.component'; 13 | import {DragDropOrientationDragComponent} from './orientation-drag/drag-drop-orientation-drag.component'; 14 | import {DragDropRestrictingMoveAreaComponent} from './restricting-move-area/drag-drop-restricting-move-area.component'; 15 | import {DragDropDragDialogComponent} from './drag-dialog/drag-drop-drag-dialog.component'; 16 | import { DragDropControllingWithItemComponent } from './controlling-with-item/drag-drop-controlling-with-item.component'; 17 | import { DragDropDisableDragComponent } from './disable-drag/drag-drop-disable-drag.component'; 18 | 19 | @NgModule({ 20 | imports: [ 21 | CommonModule, 22 | RouterModule, 23 | DragDropModule, 24 | CdkDragDropRoutingModule 25 | ], 26 | declarations: [ 27 | CdkDragDropComponent, 28 | DragDropDropListComponent, 29 | DragDropDropComponent, 30 | DragDropTransferringItemComponent, 31 | DragDropHandleDragAreaComponent, 32 | DragDropCustomizingDragPreviewComponent, 33 | DragDropCustomizingDragPlaceHolderComponent, 34 | DragDropOrientationDragComponent, 35 | DragDropRestrictingMoveAreaComponent, 36 | DragDropDragDialogComponent, 37 | DragDropControllingWithItemComponent, 38 | DragDropDisableDragComponent 39 | ] 40 | }) 41 | export class CdkDragDropModule { 42 | } 43 | -------------------------------------------------------------------------------- /src/app/drag-drop/controlling-with-item/drag-drop-controlling-with-item.component.html: -------------------------------------------------------------------------------- 1 |

controlling with item

2 |
3 |

Available numbers

4 | 5 |
13 |
18 | {{number}} 19 |
20 |
21 |
22 | 23 |
24 |

Even numbers

25 | 26 |
34 |
{{number}} 39 |
40 |
41 |
42 | -------------------------------------------------------------------------------- /src/app/drag-drop/controlling-with-item/drag-drop-controlling-with-item.component.less: -------------------------------------------------------------------------------- 1 | .example-container { 2 | width: 400px; 3 | max-width: 100%; 4 | margin: 0 25px 25px 0; 5 | display: inline-block; 6 | vertical-align: top; 7 | } 8 | 9 | .example-list { 10 | border: solid 1px #ccc; 11 | min-height: 60px; 12 | background: white; 13 | border-radius: 4px; 14 | overflow: hidden; 15 | display: block; 16 | } 17 | 18 | .example-box { 19 | padding: 20px 10px; 20 | border-bottom: solid 1px #ccc; 21 | color: rgba(0, 0, 0, 0.87); 22 | display: flex; 23 | flex-direction: row; 24 | align-items: center; 25 | justify-content: space-between; 26 | box-sizing: border-box; 27 | cursor: move; 28 | background: white; 29 | font-size: 14px; 30 | } 31 | 32 | .cdk-drag-preview { 33 | box-sizing: border-box; 34 | border-radius: 4px; 35 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 36 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 37 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 38 | } 39 | 40 | .cdk-drag-placeholder { 41 | opacity: 0; 42 | } 43 | 44 | .cdk-drag-animating { 45 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 46 | } 47 | 48 | .example-box:last-child { 49 | border: none; 50 | } 51 | 52 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 53 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 54 | } 55 | -------------------------------------------------------------------------------- /src/app/drag-drop/controlling-with-item/drag-drop-controlling-with-item.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {CdkDrag, CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop'; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-controlling-with-item', 6 | templateUrl: './drag-drop-controlling-with-item.component.html', 7 | styleUrls: ['./drag-drop-controlling-with-item.component.less'] 8 | }) 9 | export class DragDropControllingWithItemComponent { 10 | 11 | all = [1, 2, 3, 4, 5, 6, 7, 8, 9]; 12 | even = [10]; 13 | 14 | drop(event: CdkDragDrop) { 15 | if (event.previousContainer === event.container) { 16 | moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); 17 | } else { 18 | transferArrayItem(event.previousContainer.data, 19 | event.container.data, 20 | event.previousIndex, 21 | event.currentIndex); 22 | } 23 | } 24 | 25 | /** Predicate function that only allows even numbers to be dropped into a list. */ 26 | evenPredicate(item: CdkDrag) { 27 | return item.data % 2 === 0; 28 | } 29 | 30 | /** Predicate function that doesn't allow items to be dropped into a list. */ 31 | noReturnPredicate() { 32 | return false; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/app/drag-drop/customizing-drag-place-holder/drag-drop-customizing-drag-place-holder.component.html: -------------------------------------------------------------------------------- 1 |

customizing drag place holder

2 |
3 |
4 |
5 | {{movie}} 6 |
7 |
-------------------------------------------------------------------------------- /src/app/drag-drop/customizing-drag-place-holder/drag-drop-customizing-drag-place-holder.component.less: -------------------------------------------------------------------------------- 1 | .example-list { 2 | width: 500px; 3 | max-width: 100%; 4 | border: solid 1px #ccc; 5 | min-height: 60px; 6 | display: block; 7 | background: white; 8 | border-radius: 4px; 9 | overflow: hidden; 10 | } 11 | 12 | .example-box { 13 | padding: 20px 10px; 14 | border-bottom: solid 1px #ccc; 15 | color: rgba(0, 0, 0, 0.87); 16 | display: flex; 17 | flex-direction: row; 18 | align-items: center; 19 | justify-content: space-between; 20 | box-sizing: border-box; 21 | cursor: move; 22 | background: white; 23 | font-size: 14px; 24 | } 25 | 26 | .cdk-drag-preview { 27 | box-sizing: border-box; 28 | border-radius: 4px; 29 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 30 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 31 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 32 | } 33 | 34 | .cdk-drag-animating { 35 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 36 | } 37 | 38 | .example-box:last-child { 39 | border: none; 40 | } 41 | 42 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 43 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 44 | } 45 | 46 | .example-custom-placeholder { 47 | background: #ccc; 48 | border: dotted 3px #999; 49 | min-height: 60px; 50 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 51 | } 52 | -------------------------------------------------------------------------------- /src/app/drag-drop/customizing-drag-place-holder/drag-drop-customizing-drag-place-holder.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {CdkDragDrop, moveItemInArray} from "@angular/cdk/drag-drop"; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-customizing-drag-place-holder', 6 | templateUrl: './drag-drop-customizing-drag-place-holder.component.html', 7 | styleUrls: ['./drag-drop-customizing-drag-place-holder.component.less'] 8 | }) 9 | export class DragDropCustomizingDragPlaceHolderComponent { 10 | 11 | movies = [ 12 | 'Episode I - The Phantom Menace', 13 | 'Episode II - Attack of the Clones', 14 | 'Episode III - Revenge of the Sith', 15 | 'Episode IV - A New Hope', 16 | 'Episode V - The Empire Strikes Back', 17 | 'Episode VI - Return of the Jedi', 18 | 'Episode VII - The Force Awakens', 19 | 'Episode VIII - The Last Jedi' 20 | ]; 21 | 22 | drop(event: CdkDragDrop) { 23 | moveItemInArray(this.movies, event.previousIndex, event.currentIndex); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/drag-drop/customizing-drag-preview/drag-drop-customizing-drag-preview.component.html: -------------------------------------------------------------------------------- 1 |

customizing drag preview

2 |
3 |
4 | {{movie.title}} 5 | 6 |
7 |
-------------------------------------------------------------------------------- /src/app/drag-drop/customizing-drag-preview/drag-drop-customizing-drag-preview.component.less: -------------------------------------------------------------------------------- 1 | .example-list { 2 | width: 500px; 3 | max-width: 100%; 4 | border: solid 1px #ccc; 5 | min-height: 60px; 6 | display: block; 7 | background: white; 8 | border-radius: 4px; 9 | overflow: hidden; 10 | } 11 | 12 | .example-box { 13 | padding: 20px 10px; 14 | border-bottom: solid 1px #ccc; 15 | color: rgba(0, 0, 0, 0.87); 16 | display: flex; 17 | flex-direction: row; 18 | align-items: center; 19 | justify-content: space-between; 20 | box-sizing: border-box; 21 | cursor: move; 22 | background: white; 23 | font-size: 14px; 24 | } 25 | 26 | .cdk-drag-preview { 27 | box-sizing: border-box; 28 | border-radius: 4px; 29 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 30 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 31 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 32 | } 33 | 34 | .cdk-drag-placeholder { 35 | opacity: 0; 36 | } 37 | 38 | .cdk-drag-animating { 39 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 40 | } 41 | 42 | .example-box:last-child { 43 | border: none; 44 | } 45 | 46 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 47 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 48 | } 49 | -------------------------------------------------------------------------------- /src/app/drag-drop/customizing-drag-preview/drag-drop-customizing-drag-preview.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop'; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-customizing-drag-preview', 6 | templateUrl: './drag-drop-customizing-drag-preview.component.html', 7 | styleUrls: ['./drag-drop-customizing-drag-preview.component.less'] 8 | }) 9 | export class DragDropCustomizingDragPreviewComponent { 10 | 11 | movies = [ 12 | { 13 | title: 'Episode I - The Phantom Menace', 14 | poster: 'https://upload.wikimedia.org/wikipedia/en/4/40/Star_Wars_Phantom_Menace_poster.jpg' 15 | }, 16 | { 17 | title: 'Episode II - Attack of the Clones', 18 | poster: 'https://upload.wikimedia.org/wikipedia/en/3/32/Star_Wars_-_Episode_II_Attack_of_the_Clones_%28movie_poster%29.jpg' 19 | }, 20 | { 21 | title: 'Episode III - Revenge of the Sith', 22 | poster: 'https://upload.wikimedia.org/wikipedia/en/9/93/Star_Wars_Episode_III_Revenge_of_the_Sith_poster.jpg' 23 | }, 24 | { 25 | title: 'Episode IV - A New Hope', 26 | poster: 'https://upload.wikimedia.org/wikipedia/en/8/87/StarWarsMoviePoster1977.jpg' 27 | }, 28 | { 29 | title: 'Episode V - The Empire Strikes Back', 30 | poster: 'https://upload.wikimedia.org/wikipedia/en/3/3c/SW_-_Empire_Strikes_Back.jpg' 31 | }, 32 | { 33 | title: 'Episode VI - Return of the Jedi', 34 | poster: 'https://upload.wikimedia.org/wikipedia/en/b/b2/ReturnOfTheJediPoster1983.jpg' 35 | }, 36 | { 37 | title: 'Episode VII - The Force Awakens', 38 | poster: 'https://upload.wikimedia.org/wikipedia/en/a/a2/Star_Wars_The_Force_Awakens_Theatrical_Poster.jpg' 39 | }, 40 | { 41 | title: 'Episode VIII - The Last Jedi', 42 | poster: 'https://upload.wikimedia.org/wikipedia/en/7/7f/Star_Wars_The_Last_Jedi.jpg' 43 | } 44 | ]; 45 | 46 | // tslint:enable:max-line-length 47 | 48 | drop(event: CdkDragDrop<{ title: string, poster: string }[]>) { 49 | moveItemInArray(this.movies, event.previousIndex, event.currentIndex); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/app/drag-drop/disable-drag/drag-drop-disable-drag.component.html: -------------------------------------------------------------------------------- 1 |

disable drag

2 | 3 |
4 | 5 | 6 |
{{item.value}}
10 |
{{item.value}}
14 |
15 | 16 |
-------------------------------------------------------------------------------- /src/app/drag-drop/disable-drag/drag-drop-disable-drag.component.less: -------------------------------------------------------------------------------- 1 | .example-list { 2 | width: 500px; 3 | max-width: 100%; 4 | border: solid 1px #ccc; 5 | min-height: 60px; 6 | display: block; 7 | background: white; 8 | border-radius: 4px; 9 | overflow: hidden; 10 | } 11 | 12 | .example-box { 13 | padding: 20px 10px; 14 | border-bottom: solid 1px #ccc; 15 | color: rgba(0, 0, 0, 0.87); 16 | display: flex; 17 | flex-direction: row; 18 | align-items: center; 19 | justify-content: space-between; 20 | box-sizing: border-box; 21 | cursor: move; 22 | background: white; 23 | font-size: 14px; 24 | } 25 | 26 | .cdk-drag-preview { 27 | box-sizing: border-box; 28 | border-radius: 4px; 29 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 30 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 31 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 32 | } 33 | 34 | .cdk-drag-placeholder { 35 | opacity: 0; 36 | } 37 | 38 | .cdk-drag-animating { 39 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 40 | } 41 | 42 | .example-box:last-child { 43 | border: none; 44 | } 45 | 46 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 47 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 48 | } 49 | -------------------------------------------------------------------------------- /src/app/drag-drop/disable-drag/drag-drop-disable-drag.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {CdkDragDrop, moveItemInArray} from "@angular/cdk/drag-drop"; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-disable-drag', 6 | templateUrl: './drag-drop-disable-drag.component.html', 7 | styleUrls: ['./drag-drop-disable-drag.component.less'] 8 | }) 9 | export class DragDropDisableDragComponent { 10 | 11 | items = [ 12 | {value: 'I can be dragged', disabled: false}, 13 | {value: 'I cannot be dragged', disabled: true}, 14 | {value: 'I can also be dragged', disabled: false} 15 | ]; 16 | 17 | drop(event: CdkDragDrop) { 18 | moveItemInArray(this.items, event.previousIndex, event.currentIndex); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/drag-drop/drag-dialog/drag-drop-drag-dialog.component.html: -------------------------------------------------------------------------------- 1 |

drag dialog

2 | 3 | 4 | 5 |
6 | Drag the dialog around! 7 |
8 |
9 | -------------------------------------------------------------------------------- /src/app/drag-drop/drag-dialog/drag-drop-drag-dialog.component.less: -------------------------------------------------------------------------------- 1 | .example-dialog-content { 2 | width: 200px; 3 | height: 200px; 4 | border: solid 1px #ccc; 5 | color: rgba(0, 0, 0, 0.87); 6 | cursor: move; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | background: #fff; 11 | border-radius: 4px; 12 | transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); 13 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 14 | 0 2px 2px 0 rgba(0, 0, 0, 0.14), 15 | 0 1px 5px 0 rgba(0, 0, 0, 0.12); 16 | } 17 | 18 | .example-dialog-content:active { 19 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 20 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 21 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 22 | } 23 | -------------------------------------------------------------------------------- /src/app/drag-drop/drag-dialog/drag-drop-drag-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, OnDestroy, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core'; 2 | import {Overlay, OverlayRef} from '@angular/cdk/overlay'; 3 | import {TemplatePortal} from '@angular/cdk/portal'; 4 | 5 | @Component({ 6 | selector: 'app-drag-drop-drag-dialog', 7 | templateUrl: './drag-drop-drag-dialog.component.html', 8 | styleUrls: ['./drag-drop-drag-dialog.component.less'] 9 | }) 10 | export class DragDropDragDialogComponent implements AfterViewInit, OnDestroy { 11 | @ViewChild(TemplateRef) _dialogTemplate: TemplateRef; 12 | private _overlayRef: OverlayRef; 13 | private _portal: TemplatePortal; 14 | 15 | constructor(private _overlay: Overlay, private _viewContainerRef: ViewContainerRef) {} 16 | 17 | ngAfterViewInit() { 18 | this._portal = new TemplatePortal(this._dialogTemplate, this._viewContainerRef); 19 | this._overlayRef = this._overlay.create({ 20 | positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(), 21 | hasBackdrop: true 22 | }); 23 | this._overlayRef.backdropClick().subscribe(() => this._overlayRef.detach()); 24 | } 25 | 26 | ngOnDestroy() { 27 | this._overlayRef.dispose(); 28 | } 29 | 30 | openDialog() { 31 | this._overlayRef.attach(this._portal); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/drag-drop/drop-directive/drag-drop-drop.component.html: -------------------------------------------------------------------------------- 1 |

cdkDrop

2 |
3 | 可以到处拖拽 4 |
5 | -------------------------------------------------------------------------------- /src/app/drag-drop/drop-directive/drag-drop-drop.component.less: -------------------------------------------------------------------------------- 1 | .example-box { 2 | width: 100px; 3 | height: 100px; 4 | border: solid 1px #ccc; 5 | color: rgba(0, 0, 0, 0.87); 6 | cursor: move; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | text-align: center; 11 | background: #fff; 12 | border-radius: 4px; 13 | position: relative; 14 | z-index: 1; 15 | transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); 16 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 17 | 0 2px 2px 0 rgba(0, 0, 0, 0.14), 18 | 0 1px 5px 0 rgba(0, 0, 0, 0.12); 19 | } 20 | 21 | .example-box:active { 22 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 23 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 24 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 25 | } 26 | -------------------------------------------------------------------------------- /src/app/drag-drop/drop-directive/drag-drop-drop.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core'; 2 | import {CdkDrag} from "@angular/cdk/drag-drop"; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-drop', 6 | templateUrl: './drag-drop-drop.component.html', 7 | styleUrls: ['./drag-drop-drop.component.less'] 8 | }) 9 | export class DragDropDropComponent implements AfterViewInit { 10 | 11 | @ViewChild(CdkDrag) cdkDragDirective: CdkDrag; 12 | 13 | constructor() { 14 | } 15 | 16 | ngAfterViewInit() { 17 | console.log(this.cdkDragDirective.getPlaceholderElement()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/app/drag-drop/drop-list-directive/drag-drop-drop-list.component.html: -------------------------------------------------------------------------------- 1 |

cdkDropList

2 |
3 |
{{movie}}
4 |
5 | -------------------------------------------------------------------------------- /src/app/drag-drop/drop-list-directive/drag-drop-drop-list.component.less: -------------------------------------------------------------------------------- 1 | .example-list { 2 | width: 500px; 3 | max-width: 100%; 4 | border: solid 1px #ccc; 5 | min-height: 60px; 6 | display: block; 7 | background: white; 8 | border-radius: 4px; 9 | overflow: hidden; 10 | } 11 | 12 | .example-box { 13 | padding: 20px 10px; 14 | border-bottom: solid 1px #ccc; 15 | color: rgba(0, 0, 0, 0.87); 16 | display: flex; 17 | flex-direction: row; 18 | align-items: center; 19 | justify-content: space-between; 20 | box-sizing: border-box; 21 | cursor: move; 22 | background: white; 23 | font-size: 14px; 24 | } 25 | 26 | .cdk-drag-preview { 27 | box-sizing: border-box; 28 | border-radius: 4px; 29 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 30 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 31 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 32 | } 33 | 34 | .cdk-drag-placeholder { 35 | opacity: 0; 36 | } 37 | 38 | .cdk-drag-animating { 39 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 40 | } 41 | 42 | .example-box:last-child { 43 | border: none; 44 | } 45 | 46 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 47 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 48 | } 49 | -------------------------------------------------------------------------------- /src/app/drag-drop/drop-list-directive/drag-drop-drop-list.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop'; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-drop-list', 6 | templateUrl: './drag-drop-drop-list.component.html', 7 | styleUrls: ['./drag-drop-drop-list.component.less'] 8 | }) 9 | export class DragDropDropListComponent { 10 | 11 | movies = [ 12 | 'Episode I - The Phantom Menace', 13 | 'Episode II - Attack of the Clones', 14 | 'Episode III - Revenge of the Sith', 15 | 'Episode IV - A New Hope', 16 | 'Episode V - The Empire Strikes Back', 17 | 'Episode VI - Return of the Jedi', 18 | 'Episode VII - The Force Awakens', 19 | 'Episode VIII - The Last Jedi' 20 | ]; 21 | 22 | drop(event: CdkDragDrop) { 23 | moveItemInArray(this.movies, event.previousIndex, event.currentIndex); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/app/drag-drop/handle-drag-area/drag-drop-handle-drag-area.component.html: -------------------------------------------------------------------------------- 1 |

cdkDragHandle

2 |
3 | I can only be dragged using the handle 4 | 5 |
6 | 7 | 8 | 9 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /src/app/drag-drop/handle-drag-area/drag-drop-handle-drag-area.component.less: -------------------------------------------------------------------------------- 1 | .example-box { 2 | width: 200px; 3 | height: 200px; 4 | padding: 10px; 5 | box-sizing: border-box; 6 | border: solid 1px #ccc; 7 | color: rgba(0, 0, 0, 0.87); 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | text-align: center; 12 | background: #fff; 13 | border-radius: 4px; 14 | position: relative; 15 | z-index: 1; 16 | transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); 17 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 18 | 0 2px 2px 0 rgba(0, 0, 0, 0.14), 19 | 0 1px 5px 0 rgba(0, 0, 0, 0.12); 20 | } 21 | 22 | .example-box:active { 23 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 24 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 25 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 26 | } 27 | 28 | .example-handle { 29 | position: absolute; 30 | top: 10px; 31 | right: 10px; 32 | color: #ccc; 33 | cursor: move; 34 | width: 24px; 35 | height: 24px; 36 | } 37 | -------------------------------------------------------------------------------- /src/app/drag-drop/handle-drag-area/drag-drop-handle-drag-area.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-drag-drop-handle-drag-area', 5 | templateUrl: './drag-drop-handle-drag-area.component.html', 6 | styleUrls: ['./drag-drop-handle-drag-area.component.less'] 7 | }) 8 | export class DragDropHandleDragAreaComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/drag-drop/orientation-drag/drag-drop-orientation-drag.component.html: -------------------------------------------------------------------------------- 1 |

水平方向的drag

2 |
3 |
{{timePeriod}}
4 |
5 | -------------------------------------------------------------------------------- /src/app/drag-drop/orientation-drag/drag-drop-orientation-drag.component.less: -------------------------------------------------------------------------------- 1 | .example-list { 2 | width: 1000px; 3 | max-width: 100%; 4 | border: solid 1px #ccc; 5 | min-height: 60px; 6 | display: flex; 7 | flex-direction: row; 8 | background: white; 9 | border-radius: 4px; 10 | overflow: hidden; 11 | } 12 | 13 | .example-box { 14 | padding: 20px 10px; 15 | border-right: solid 1px #ccc; 16 | color: rgba(0, 0, 0, 0.87); 17 | display: flex; 18 | flex-direction: row; 19 | align-items: center; 20 | justify-content: space-between; 21 | box-sizing: border-box; 22 | cursor: move; 23 | background: white; 24 | font-size: 14px; 25 | flex-grow: 1; 26 | flex-basis: 0; 27 | } 28 | 29 | .cdk-drag-preview { 30 | box-sizing: border-box; 31 | border-radius: 4px; 32 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 33 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 34 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 35 | } 36 | 37 | .cdk-drag-placeholder { 38 | opacity: 0; 39 | } 40 | 41 | .cdk-drag-animating { 42 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 43 | } 44 | 45 | .example-box:last-child { 46 | border: none; 47 | } 48 | 49 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 50 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 51 | } 52 | -------------------------------------------------------------------------------- /src/app/drag-drop/orientation-drag/drag-drop-orientation-drag.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop'; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-orientation-drag', 6 | templateUrl: './drag-drop-orientation-drag.component.html', 7 | styleUrls: ['./drag-drop-orientation-drag.component.less'] 8 | }) 9 | export class DragDropOrientationDragComponent { 10 | 11 | timePeriods = [ 12 | 'Bronze age', 13 | 'Iron age', 14 | 'Middle ages', 15 | 'Early modern period', 16 | 'Long nineteenth century' 17 | ]; 18 | 19 | drop(event: CdkDragDrop) { 20 | moveItemInArray(this.timePeriods, event.previousIndex, event.currentIndex); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/app/drag-drop/restricting-move-area/drag-drop-restricting-move-area.component.html: -------------------------------------------------------------------------------- 1 |

限制移动的区域

2 |
3 | 只能上下移动 4 |
5 | 6 |
7 | 只能左右移动 8 |
9 | -------------------------------------------------------------------------------- /src/app/drag-drop/restricting-move-area/drag-drop-restricting-move-area.component.less: -------------------------------------------------------------------------------- 1 | .example-box { 2 | width: 200px; 3 | height: 200px; 4 | border: solid 1px #ccc; 5 | color: rgba(0, 0, 0, 0.87); 6 | cursor: move; 7 | display: inline-flex; 8 | justify-content: center; 9 | align-items: center; 10 | text-align: center; 11 | background: #fff; 12 | border-radius: 4px; 13 | margin-right: 25px; 14 | position: relative; 15 | z-index: 1; 16 | transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); 17 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 18 | 0 2px 2px 0 rgba(0, 0, 0, 0.14), 19 | 0 1px 5px 0 rgba(0, 0, 0, 0.12); 20 | } 21 | 22 | .example-box:active { 23 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 24 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 25 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 26 | } 27 | -------------------------------------------------------------------------------- /src/app/drag-drop/restricting-move-area/drag-drop-restricting-move-area.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-drag-drop-restricting-move-area', 5 | templateUrl: './drag-drop-restricting-move-area.component.html', 6 | styleUrls: ['./drag-drop-restricting-move-area.component.less'] 7 | }) 8 | export class DragDropRestrictingMoveAreaComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/drag-drop/transferring-item/drag-drop-transferring-item.component.html: -------------------------------------------------------------------------------- 1 |

两个list之间交换item

2 | 3 |
4 |

To do

5 |
12 |
{{item}}
13 |
14 |
15 | 16 |
17 |

Done

18 |
25 |
{{item}}
26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /src/app/drag-drop/transferring-item/drag-drop-transferring-item.component.less: -------------------------------------------------------------------------------- 1 | .example-container { 2 | width: 400px; 3 | max-width: 100%; 4 | margin: 0 25px 25px 0; 5 | display: inline-block; 6 | vertical-align: top; 7 | } 8 | 9 | .example-list { 10 | border: solid 1px #ccc; 11 | min-height: 60px; 12 | background: white; 13 | border-radius: 4px; 14 | overflow: hidden; 15 | display: block; 16 | } 17 | 18 | .example-box { 19 | padding: 20px 10px; 20 | border-bottom: solid 1px #ccc; 21 | color: rgba(0, 0, 0, 0.87); 22 | display: flex; 23 | flex-direction: row; 24 | align-items: center; 25 | justify-content: space-between; 26 | box-sizing: border-box; 27 | cursor: move; 28 | background: white; 29 | font-size: 14px; 30 | } 31 | 32 | .cdk-drag-preview { 33 | box-sizing: border-box; 34 | border-radius: 4px; 35 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 36 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 37 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 38 | } 39 | 40 | .cdk-drag-placeholder { 41 | opacity: 0; 42 | } 43 | 44 | .cdk-drag-animating { 45 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 46 | } 47 | 48 | .example-box:last-child { 49 | border: none; 50 | } 51 | 52 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 53 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 54 | } 55 | -------------------------------------------------------------------------------- /src/app/drag-drop/transferring-item/drag-drop-transferring-item.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop'; 3 | 4 | @Component({ 5 | selector: 'app-drag-drop-transferring-item', 6 | templateUrl: './drag-drop-transferring-item.component.html', 7 | styleUrls: ['./drag-drop-transferring-item.component.less'] 8 | }) 9 | export class DragDropTransferringItemComponent { 10 | 11 | todo = [ 12 | 'Get to work', 13 | 'Pick up groceries', 14 | 'Go home', 15 | 'Fall asleep' 16 | ]; 17 | 18 | done = [ 19 | 'Get up', 20 | 'Brush teeth', 21 | 'Take a shower', 22 | 'Check e-mail', 23 | 'Walk dog' 24 | ]; 25 | 26 | drop(event: CdkDragDrop) { 27 | if (event.previousContainer === event.container) { 28 | moveItemInArray(event.container.data, event.previousIndex, event.currentIndex); 29 | } else { 30 | transferArrayItem(event.previousContainer.data, 31 | event.container.data, 32 | event.previousIndex, 33 | event.currentIndex); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/app/keycodes/cdk-key-codes-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkKeyCodesComponent} from './cdk-key-codes.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkKeyCodesComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkKeyCodesRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/keycodes/cdk-key-codes.component.html: -------------------------------------------------------------------------------- 1 |

键盘常用键码

2 | 3 |

ps: 结果都在ts文件里面,通过console打印出来

4 | -------------------------------------------------------------------------------- /src/app/keycodes/cdk-key-codes.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/keycodes/cdk-key-codes.component.less -------------------------------------------------------------------------------- /src/app/keycodes/cdk-key-codes.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {DELETE, ENTER, hasModifierKey, MAC_ENTER, TAB} from '@angular/cdk/keycodes'; 3 | 4 | @Component({ 5 | selector: 'app-key-codes', 6 | templateUrl: './cdk-key-codes.component.html', 7 | styleUrls: ['./cdk-key-codes.component.less'] 8 | }) 9 | export class CdkKeyCodesComponent { 10 | 11 | onKeyDown(event) { 12 | /** 13 | * 组合按键判断(如果按下的是 ctr按键 + 其他的按键的时候 返回true) 14 | */ 15 | console.log(hasModifierKey(event, 'ctrlKey')); 16 | /** 17 | * 打印按键值 18 | */ 19 | console.log(event.keyCode); 20 | /** 21 | * 通过 cdk 判断按键类型 22 | */ 23 | switch (event.keyCode) { 24 | case MAC_ENTER: 25 | case ENTER: 26 | console.log('当前按键: Enter键'); 27 | break; 28 | case TAB: 29 | console.log('当前按键: Tab键'); 30 | break; 31 | case DELETE: 32 | console.log('当前按键: Delete键'); 33 | break; 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/app/keycodes/cdk-key-codes.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkKeyCodesComponent} from './cdk-key-codes.component'; 4 | import {RouterModule} from '@angular/router'; 5 | import {CdkKeyCodesRoutingModule} from './cdk-key-codes-routing.module'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | RouterModule, 11 | CdkKeyCodesRoutingModule 12 | ], 13 | declarations: [ 14 | CdkKeyCodesComponent 15 | ] 16 | }) 17 | export class CdkKeyCodesModule { 18 | } 19 | -------------------------------------------------------------------------------- /src/app/layout/cdk-layout-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkLayoutComponent} from './cdk-layout.component'; 5 | 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: CdkLayoutComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | RouterModule.forChild(routes), 17 | CommonModule 18 | ], 19 | exports: [ 20 | RouterModule 21 | ], 22 | providers: [] 23 | }) 24 | export class CdkLayoutRoutingModule { 25 | } 26 | -------------------------------------------------------------------------------- /src/app/layout/cdk-layout.component.html: -------------------------------------------------------------------------------- 1 |

cdk layout 所有的内容都在ts中哦!

2 |

BreakpointObserver MediaMatcher

-------------------------------------------------------------------------------- /src/app/layout/cdk-layout.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/layout/cdk-layout.component.less -------------------------------------------------------------------------------- /src/app/layout/cdk-layout.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnDestroy, OnInit} from '@angular/core'; 2 | import {BreakpointObserver, Breakpoints, BreakpointState, MediaMatcher} from '@angular/cdk/layout'; 3 | 4 | @Component({ 5 | selector: 'app-cdk-layout', 6 | templateUrl: './cdk-layout.component.html', 7 | styleUrls: ['./cdk-layout.component.less'] 8 | }) 9 | export class CdkLayoutComponent implements OnInit, OnDestroy { 10 | 11 | matcher: MediaQueryList; 12 | 13 | // @HostListener('window:resize') 14 | // public onWindowResize(): void { 15 | // if (window.innerWidth >= 960) { 16 | // console.log('>= 960'); 17 | // } else if (window.innerWidth >= 600 && window.innerWidth < 960) { 18 | // console.log('>= 600 && < 960'); 19 | // } else { 20 | // console.log('< 600'); 21 | // } 22 | // } 23 | 24 | constructor(public breakpointObserver: BreakpointObserver, public mediaMatcher: MediaMatcher) { 25 | } 26 | 27 | ngOnInit() { 28 | 29 | // 简单的一次性匹配,可以使用isMatching方法。如果组件初始化时窗口至少为40rem高,则输出到控制台 30 | if (this.breakpointObserver.isMatched('(min-height: 40rem)')) { 31 | console.log('窗口至少为40rem高!'); 32 | } else { 33 | console.log('窗口没有40rem高!'); 34 | } 35 | 36 | // 注意哦,不会一直回调哦,只会在第一次满足或者不满足条件的时候回调。 37 | this.breakpointObserver 38 | .observe(['(min-width: 500px)']) 39 | .subscribe((state: BreakpointState) => { 40 | if (state.matches) { 41 | console.log('窗口宽度大于或者等于500px!'); 42 | } else { 43 | console.log('窗口不满足宽度大于或者等于500px!'); 44 | } 45 | }); 46 | 47 | // 也可以使用Breakpoints对象,而不是使用手写的媒体查询,它为我们提供了常见断点的键。如果多个参数,当有一个条件满足或者不满足的时候就会触发 48 | this.breakpointObserver 49 | .observe([Breakpoints.Small, Breakpoints.HandsetPortrait]) 50 | .subscribe((state: BreakpointState) => { 51 | if (state.matches) { 52 | console.log( 53 | 'Breakpoints.Small or Breakpoints.HandsetPortrait' 54 | ); 55 | } 56 | }); 57 | 58 | 59 | this.matcher = this.mediaMatcher.matchMedia('(min-width: 500px)'); 60 | this.matcher.addListener(this.matchMediaListener); 61 | 62 | // // 在程序加载时判断当前设备是横屏还是竖屏 63 | // const isPortrait = window.matchMedia('(orientation: portrait)').matches; 64 | // if (isPortrait) { 65 | // console.log('This is portrait'); 66 | // } 67 | // 68 | // // 初始化时窗口的大小等等 69 | // if (window.innerWidth >= 960) { 70 | // console.log('>= 960'); 71 | // } else { 72 | // console.log('< 960'); 73 | // } 74 | } 75 | 76 | ngOnDestroy() { 77 | this.matcher.removeListener(this.matchMediaListener); 78 | } 79 | 80 | matchMediaListener(event) { 81 | console.log(event.matches ? 'match' : 'no match'); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/app/layout/cdk-layout.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkLayoutRoutingModule} from './cdk-layout-routing.module'; 4 | import {CdkLayoutComponent} from './cdk-layout.component'; 5 | import {RouterModule} from '@angular/router'; 6 | import {LayoutModule} from "@angular/cdk/layout"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | RouterModule, 12 | LayoutModule, 13 | CdkLayoutRoutingModule 14 | ], 15 | declarations: [ 16 | CdkLayoutComponent 17 | ] 18 | }) 19 | export class CdkLayoutModule { 20 | } 21 | -------------------------------------------------------------------------------- /src/app/observers/cdk-observers-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkObserversComponent} from './cdk-observers.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkObserversComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkObserversRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/observers/cdk-observers.component.html: -------------------------------------------------------------------------------- 1 |

通过 cdk observers 可以监听dom变化

2 | 3 | 4 |
5 | {{content}} 6 |
7 |
8 | 9 | 10 | {{content}} 11 | -------------------------------------------------------------------------------- /src/app/observers/cdk-observers.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/observers/cdk-observers.component.less -------------------------------------------------------------------------------- /src/app/observers/cdk-observers.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef, ViewChild} from '@angular/core'; 2 | import {ContentObserver} from '@angular/cdk/observers'; 3 | 4 | @Component({ 5 | selector: 'app-cdk-observers', 6 | templateUrl: './cdk-observers.component.html', 7 | styleUrls: ['./cdk-observers.component.less'] 8 | }) 9 | export class CdkObserversComponent implements AfterViewInit { 10 | 11 | @ViewChild('observerSource') 12 | observerSource: ElementRef; 13 | 14 | content = 1; 15 | 16 | constructor(private observer: ContentObserver) { 17 | setInterval(() => { 18 | this.content++; 19 | }, 3000); 20 | } 21 | 22 | ngAfterViewInit(): void { 23 | // ContentObserver service监听变化 24 | this.observer.observe(this.observerSource.nativeElement).subscribe((event: MutationRecord[]) => console.log(event)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/observers/cdk-observers.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkObserversRoutingModule} from './cdk-observers-routing.module'; 4 | import {CdkObserversComponent} from './cdk-observers.component'; 5 | import {ObserversModule} from '@angular/cdk/observers'; 6 | import { ObserversChildComponent } from './child/observers-child.component'; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | ObserversModule, 12 | CdkObserversRoutingModule 13 | ], 14 | declarations: [CdkObserversComponent, ObserversChildComponent] 15 | }) 16 | export class CdkObserversModule { 17 | } 18 | -------------------------------------------------------------------------------- /src/app/observers/child/observers-child.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
-------------------------------------------------------------------------------- /src/app/observers/child/observers-child.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/observers/child/observers-child.component.less -------------------------------------------------------------------------------- /src/app/observers/child/observers-child.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-observers-child', 5 | templateUrl: './observers-child.component.html', 6 | styleUrls: ['./observers-child.component.less'] 7 | }) 8 | export class ObserversChildComponent { 9 | 10 | count = 0; 11 | 12 | projectContentChanged($event: MutationRecord[]) { 13 | ++this.count; 14 | console.log(`ng-content内容改变了,第${this.count}次`); 15 | console.log($event, this.count); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/app/overlay/cdk-overlay-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from "@angular/router"; 4 | import {CdkOverlayComponent} from "./cdk-overlay.component"; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkOverlayComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkOverlayRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/overlay/cdk-overlay.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

组件或者ng-template实现overlay

4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 |

ng-temtortelliniTemplateplate显示

16 |
17 | 18 | 19 | 24 | 25 | 28 | 29 | 30 | 33 | 34 | 35 |
36 |

指令(Directives)实现overlay

37 | 40 | 41 | 47 | 50 | 51 |
52 | -------------------------------------------------------------------------------- /src/app/overlay/cdk-overlay.component.less: -------------------------------------------------------------------------------- 1 | 2 | .template-overlay-pane { 3 | padding: 10px; 4 | border: 1px solid black; 5 | background-color: skyblue; 6 | } 7 | 8 | .menu-wrap { 9 | margin: 0; 10 | padding: 10px; 11 | border: 1px solid black; 12 | color: white; 13 | background-color: orangered; 14 | opacity: 0.5; 15 | } 16 | 17 | .backdrop-with-out-transparent { 18 | background-color: transparent; 19 | } -------------------------------------------------------------------------------- /src/app/overlay/cdk-overlay.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ElementRef, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core'; 2 | import {Overlay, OverlayConfig, OverlayRef} from '@angular/cdk/overlay'; 3 | import {ComponentPortal, TemplatePortalDirective} from '@angular/cdk/portal'; 4 | import {OverlayPanelComponent} from './panel/overlay-panel.component'; 5 | 6 | @Component({ 7 | selector: 'app-cdk-overlay', 8 | templateUrl: './cdk-overlay.component.html', 9 | styleUrls: ['./cdk-overlay.component.less'], 10 | encapsulation: ViewEncapsulation.None, 11 | preserveWhitespaces: false, 12 | }) 13 | export class CdkOverlayComponent { 14 | 15 | 16 | isMenuOpen = false; 17 | globalOverlayPosition = 0; 18 | actionMenuItemList = ['第一项item', '第二项item']; 19 | private _overlayTemplateRef: OverlayRef; 20 | private _overlayConnectRef: OverlayRef; 21 | 22 | @ViewChild('overlayGlobalTemplate') templateGlobalPortals: TemplatePortalDirective; 23 | @ViewChild('connectComponentOrigin') _overlayConnectComponentOrigin: ElementRef; 24 | @ViewChild('connectTemplateOrigin') _overlayConnectTemplateOrigin: ElementRef; 25 | @ViewChild('overlayConnectTemplate') _overlayOriginTemplateDirective: TemplatePortalDirective; 26 | 27 | constructor(public overlay: Overlay 28 | , public viewContainerRef: ViewContainerRef) { 29 | } 30 | 31 | /** 32 | * overlay 在整个屏幕的中间显示 33 | */ 34 | showOverlayGlobalPanelCenter() { 35 | // config: OverlayConfig overlay的配置,配置显示位置,和滑动策略 36 | const config = new OverlayConfig(); 37 | config.positionStrategy = this.overlay.position() 38 | .global() // 全局显示 39 | .centerHorizontally() // 水平居中 40 | .centerVertically(); // 垂直居中 41 | config.hasBackdrop = true; // 设置overlay后面有一层背景, 当然你也可以设置backdropClass 来设置这层背景的class 42 | const overlayRef = this.overlay.create(config); // OverlayRef, overlay层 43 | overlayRef.backdropClick().subscribe(() => { 44 | // 点击了backdrop背景 45 | overlayRef.dispose(); 46 | }); 47 | // OverlayPanelComponent是动态组件 48 | // 创建一个ComponentPortal,attach到OverlayRef,这个时候我们这个overlay层就显示出来了。 49 | overlayRef.attach(new ComponentPortal(OverlayPanelComponent, this.viewContainerRef)); 50 | // 监听overlayRef上的键盘按键事件 51 | overlayRef.keydownEvents().subscribe((event: KeyboardEvent) => { 52 | console.log(overlayRef._keydownEventSubscriptions + ' times'); 53 | console.log(event); 54 | }); 55 | } 56 | 57 | /** 58 | * overlay 在整个屏幕位置,自己控制位置 59 | */ 60 | showOverlayGlobalPanelPosition() { 61 | const config = new OverlayConfig(); 62 | config.positionStrategy = this.overlay.position() 63 | .global() 64 | .left(`${this.globalOverlayPosition}px`) 65 | .top(`${this.globalOverlayPosition}px`); 66 | this.globalOverlayPosition += 30; 67 | config.hasBackdrop = true; 68 | const overlayRef = this.overlay.create(config); 69 | overlayRef.backdropClick().subscribe(() => { 70 | overlayRef.dispose(); 71 | }); 72 | overlayRef.attach(new ComponentPortal(OverlayPanelComponent, this.viewContainerRef)); 73 | } 74 | 75 | /** 76 | * 显示 ng-template 的内容 77 | */ 78 | showOverlayPanelTemplate() { 79 | const config = new OverlayConfig(); 80 | config.positionStrategy = this.overlay.position() 81 | .global() 82 | .centerHorizontally() 83 | .top(`${this.globalOverlayPosition}px`); 84 | this.globalOverlayPosition += 30; 85 | this._overlayTemplateRef = this.overlay.create(config); 86 | this._overlayTemplateRef.attach(this.templateGlobalPortals); 87 | } 88 | 89 | /** 90 | * 移除 ng-template 内容 91 | */ 92 | dismissOverlayPanelTemplate() { 93 | if (this._overlayTemplateRef && this._overlayTemplateRef.hasAttached()) { 94 | this._overlayTemplateRef.dispose(); 95 | } 96 | } 97 | 98 | /** 99 | * overlay connect origin 显示,依附某个组件显示 100 | */ 101 | showOverlayPanelConnectComponent() { 102 | const strategy = this.overlay.position() 103 | .flexibleConnectedTo(this._overlayConnectComponentOrigin.nativeElement) 104 | .withPositions([{ 105 | originX: 'center', 106 | originY: 'bottom', 107 | overlayX: 'center', 108 | overlayY: 'top', 109 | offsetX: 0, 110 | offsetY: 0 111 | }]); 112 | strategy.withLockedPosition(true); 113 | const config = new OverlayConfig({positionStrategy: strategy}); 114 | config.scrollStrategy = this.overlay.scrollStrategies.reposition(); 115 | this._overlayConnectRef = this.overlay.create(config); 116 | this._overlayConnectRef.attach(new ComponentPortal(OverlayPanelComponent, this.viewContainerRef)); 117 | } 118 | 119 | dismissOverlayPanelConnectComponent() { 120 | if (this._overlayConnectRef && this._overlayConnectRef.hasAttached()) { 121 | this._overlayConnectRef.dispose(); 122 | } 123 | } 124 | 125 | /** 126 | * overlay connect origin 显示,依附ng-template 127 | */ 128 | showOverlayPanelConnectTemplate() { 129 | const strategy = this.overlay.position() 130 | .flexibleConnectedTo(this._overlayConnectTemplateOrigin.nativeElement) 131 | .withPositions([{ 132 | originX: 'start', 133 | originY: 'bottom', 134 | overlayX: 'end', 135 | overlayY: 'top', 136 | offsetX: 0, 137 | offsetY: 0 138 | }]); 139 | const config = new OverlayConfig({positionStrategy: strategy}); 140 | config.hasBackdrop = true; 141 | config.backdropClass = 'backdrop-with-out'; 142 | const overlayRef = this.overlay.create(config); 143 | overlayRef.backdropClick().subscribe(() => { 144 | overlayRef.dispose(); 145 | }); 146 | overlayRef.attach(this._overlayOriginTemplateDirective); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/app/overlay/cdk-overlay.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {OverlayModule} from "@angular/cdk/overlay"; 4 | import {CdkOverlayComponent} from './cdk-overlay.component'; 5 | import {CdkOverlayRoutingModule} from "./cdk-overlay-routing.module"; 6 | import {OverlayPanelComponent} from './panel/overlay-panel.component'; 7 | import {PortalModule} from "@angular/cdk/portal"; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | PortalModule, 13 | OverlayModule, 14 | CdkOverlayRoutingModule 15 | ], 16 | declarations: [ 17 | CdkOverlayComponent, 18 | OverlayPanelComponent 19 | ], 20 | entryComponents: [ 21 | OverlayPanelComponent 22 | ], 23 | providers: [ 24 | ], 25 | }) 26 | export class CdkOverlayModule { 27 | } 28 | -------------------------------------------------------------------------------- /src/app/overlay/panel/overlay-panel.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-overlay-panel', 5 | template: ` 6 |

Overlay展示

7 | `, 8 | styles: [` 9 | .wu-overlay-pane { 10 | margin: 0; 11 | padding: 10px; 12 | border: 1px solid black; 13 | background-color: skyblue; 14 | } 15 | `] 16 | }) 17 | export class OverlayPanelComponent implements OnInit { 18 | 19 | constructor() { } 20 | 21 | ngOnInit() { 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/platform/cdk-platform-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkOverlayComponent} from '../overlay/cdk-overlay.component'; 5 | import {CdkPlatformComponent} from './cdk-platform.component'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: CdkPlatformComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | RouterModule.forChild(routes), 17 | CommonModule 18 | ], 19 | exports: [ 20 | RouterModule 21 | ], 22 | providers: [] 23 | }) 24 | export class CdkPlatformRoutingModule { } 25 | -------------------------------------------------------------------------------- /src/app/platform/cdk-platform.component.html: -------------------------------------------------------------------------------- 1 |

通过 cdk Platform 获取到的平台信息如下:

2 |

是否 Android: {{platform.ANDROID}}

3 |

是否 iOS: {{platform.IOS}}

4 |

是否 Firefox: {{platform.FIREFOX}}

5 |

是否 Blink: {{platform.BLINK}}

6 |

是否 Webkit: {{platform.WEBKIT}}

7 |

是否 Trident: {{platform.TRIDENT}}

8 |

是否 Edge: {{platform.EDGE}}

9 |

是否浏览器: {{platform.isBrowser}}

10 |

支持输入的类型: {{supportedInputTypes}}

11 |

是否支持被动监听: {{supportsPassiveEventListeners}}

12 |

是否支持 scroll 行为: {{supportsScrollBehavior}}

-------------------------------------------------------------------------------- /src/app/platform/cdk-platform.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/platform/cdk-platform.component.less -------------------------------------------------------------------------------- /src/app/platform/cdk-platform.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {getSupportedInputTypes, Platform, supportsPassiveEventListeners, supportsScrollBehavior} from '@angular/cdk/platform'; 3 | 4 | @Component({ 5 | selector: 'app-cdk-platform', 6 | templateUrl: './cdk-platform.component.html', 7 | styleUrls: ['./cdk-platform.component.less'] 8 | }) 9 | export class CdkPlatformComponent { 10 | 11 | /** 12 | * 获取支持的输入类型 13 | */ 14 | supportedInputTypes = Array.from(getSupportedInputTypes()).join(', '); 15 | /** 16 | * 是否支持被动事件监听器 17 | */ 18 | supportsPassiveEventListeners = supportsPassiveEventListeners(); 19 | /** 20 | * 是否支持滑动行为 21 | */ 22 | supportsScrollBehavior = supportsScrollBehavior(); 23 | 24 | /** 25 | * Platform Service引入进来 26 | */ 27 | constructor(public platform: Platform) { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/platform/cdk-platform.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkPlatformRoutingModule} from './cdk-platform-routing.module'; 4 | import {CdkPlatformComponent} from './cdk-platform.component'; 5 | import {PlatformModule} from '@angular/cdk/platform'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | PlatformModule, 11 | CdkPlatformRoutingModule 12 | ], 13 | declarations: [CdkPlatformComponent] 14 | }) 15 | export class CdkPlatformModule { 16 | } 17 | -------------------------------------------------------------------------------- /src/app/portal/cdk-portal-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkPortalComponent} from './cdk-portal.component'; 5 | 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: CdkPortalComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | RouterModule.forChild(routes), 17 | CommonModule 18 | ], 19 | exports: [ 20 | RouterModule 21 | ], 22 | providers: [] 23 | }) 24 | export class CdkPortalRoutingModule { 25 | } 26 | -------------------------------------------------------------------------------- /src/app/portal/cdk-portal.component.html: -------------------------------------------------------------------------------- 1 |

通过 cdk portal 显示view

2 | 3 | 4 | 5 |
6 |
7 | 8 |
9 | 11 | 12 |
13 |
14 | 17 | 20 | 23 | 24 | 25 | 26 |

ng-template 指定的内容(first) 外部参数 {{obj.age}}

27 |
28 | 29 |
30 |

ng-template 指定的内容(last)

31 |
32 | 33 | 34 |

这部分内容在app-root标签之外

35 |
36 | -------------------------------------------------------------------------------- /src/app/portal/cdk-portal.component.less: -------------------------------------------------------------------------------- 1 | .demo-portal-host { 2 | border: 1px dashed black; 3 | width: 500px; 4 | height: 100px; 5 | } -------------------------------------------------------------------------------- /src/app/portal/cdk-portal.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | ApplicationRef, 4 | Component, 5 | ComponentFactoryResolver, 6 | ElementRef, 7 | Inject, 8 | Injector, 9 | QueryList, 10 | TemplateRef, 11 | ViewChild, 12 | ViewChildren, 13 | ViewContainerRef 14 | } from '@angular/core'; 15 | import {ComponentPortal, DomPortalOutlet, TemplatePortal, TemplatePortalDirective} from '@angular/cdk/portal'; 16 | import {PortalChildComponent} from './portal-child-component/portal-child.component'; 17 | import {DOCUMENT} from '@angular/common'; 18 | 19 | @Component({ 20 | selector: 'app-cdk-portal', 21 | templateUrl: './cdk-portal.component.html', 22 | styleUrls: ['./cdk-portal.component.less'] 23 | }) 24 | export class CdkPortalComponent implements AfterViewInit { 25 | 26 | // 获取到对应html里面所有添加了cdkPortal指令的元素的TemplatePortal 27 | @ViewChildren(TemplatePortalDirective) templatePortals: QueryList>; 28 | // 获取单个的cdkPortal指令的元素的TemplatePortal 【#templatePortal="cdkPortal"】 29 | @ViewChild('templatePortal') divTemplatePortal: TemplatePortal; 30 | 31 | @ViewChild('outOfApp') templateOutOfApp: TemplateRef; 32 | private _domPortalOutletOutOfApp: DomPortalOutlet; 33 | 34 | selectedPortal; 35 | 36 | ctx = { 37 | $implicit: { 38 | name: 'John', 39 | age: 34 40 | }, 41 | location: 'USA' 42 | }; 43 | 44 | 45 | constructor(@Inject(DOCUMENT) private document: any, 46 | private elementRef: ElementRef, 47 | private injector: Injector, 48 | private appRef: ApplicationRef, 49 | private viewContainerRef: ViewContainerRef, 50 | private componentFactoryResolver: ComponentFactoryResolver) { 51 | } 52 | 53 | ngAfterViewInit(): void { 54 | /** 55 | * 把内容放置在 56 | */ 57 | const element = this.document.createElement('div'); 58 | this.document.body.appendChild(element); 59 | this._domPortalOutletOutOfApp = new DomPortalOutlet(element, this.componentFactoryResolver, this.appRef, this.injector); 60 | const templatePortal = new TemplatePortal( 61 | this.templateOutOfApp, 62 | this.viewContainerRef 63 | ); 64 | this._domPortalOutletOutOfApp.attach(templatePortal); 65 | 66 | console.log(this.divTemplatePortal); 67 | } 68 | 69 | cdkPortalFirst() { 70 | this.templatePortals.first.context = this.ctx; 71 | this.selectedPortal = this.templatePortals.first; 72 | } 73 | 74 | cdkPortalLast() { 75 | this.selectedPortal = this.templatePortals.last; 76 | } 77 | 78 | cdkPortalComponent() { 79 | this.selectedPortal = new ComponentPortal(PortalChildComponent); 80 | } 81 | 82 | onPortalAttached() { 83 | console.log('cdkPortalOutlet上有组件attach上来了'); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/app/portal/cdk-portal.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule} from '@angular/router'; 4 | import {CdkPortalRoutingModule} from './cdk-portal-routing.module'; 5 | import {CdkPortalComponent} from './cdk-portal.component'; 6 | import {PortalChildComponent} from './portal-child-component/portal-child.component'; 7 | import {PortalComponentComponent} from './portal-component/portal-component.component'; 8 | import {PortalTemplateComponent} from './portal-template/portal-template.component'; 9 | import {PortalModule} from "@angular/cdk/portal"; 10 | import {ToolTipDirective} from './portal-tool-tip/tool-tip.directive'; 11 | import { ToolTipComponent } from './portal-tool-tip/tool-tip.component'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | RouterModule, 17 | PortalModule, 18 | CdkPortalRoutingModule 19 | ], 20 | declarations: [ 21 | CdkPortalComponent, 22 | PortalChildComponent, 23 | PortalComponentComponent, 24 | PortalTemplateComponent, 25 | ToolTipDirective, 26 | ToolTipComponent 27 | ], 28 | entryComponents: [ 29 | PortalChildComponent, 30 | ToolTipComponent 31 | ] 32 | }) 33 | export class CdkLPortalModule { 34 | } 35 | -------------------------------------------------------------------------------- /src/app/portal/portal-child-component/portal-child.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Inject, InjectionToken} from '@angular/core'; 2 | 3 | /** 4 | * 用于动态创建PortalChildComponent的时候传递参数 5 | */ 6 | export const PORTAL_CHILD_DATA = new InjectionToken('PORTAL_CHILD_DATA'); 7 | 8 | @Component({ 9 | selector: 'app-portal-child', 10 | template: ` 11 |

portal child show

12 | 13 | ` 14 | }) 15 | export class PortalChildComponent { 16 | 17 | outEvent: EventEmitter; 18 | 19 | /** 20 | * 构造函数 21 | * @param initData 创建组件的时候传递过来的参数(为了测试用了any类型,推荐根据业务使用特定的类型,尽量不要使用any) 22 | */ 23 | constructor(@Inject(PORTAL_CHILD_DATA) public initData: any) { 24 | console.log(initData); 25 | } 26 | 27 | /** 28 | * 用来测试把Portal里面的事件返回上去 29 | */ 30 | onButtonClick() { 31 | if (this.outEvent != null) { 32 | this.outEvent.emit('child 里面被点击了'); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/app/portal/portal-component/portal-component.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationRef, 3 | Component, 4 | ComponentFactoryResolver, ComponentRef, 5 | ElementRef, EventEmitter, 6 | Injector, OnDestroy, 7 | OnInit, 8 | ViewContainerRef 9 | } from '@angular/core'; 10 | import {ComponentPortal, DomPortalHost, PortalInjector} from '@angular/cdk/portal'; 11 | import {PORTAL_CHILD_DATA, PortalChildComponent} from '../portal-child-component/portal-child.component'; 12 | import {Subject} from "rxjs"; 13 | import {takeUntil} from "rxjs/operators"; 14 | 15 | @Component({ 16 | selector: 'app-portal-component', 17 | template: `` 18 | }) 19 | export class PortalComponentComponent implements OnInit, OnDestroy { 20 | 21 | private portalHost: DomPortalHost; 22 | private _$destroy = new Subject(); 23 | 24 | constructor( 25 | private elementRef: ElementRef, 26 | private injector: Injector, 27 | private appRef: ApplicationRef, 28 | private viewContainerRef: ViewContainerRef, 29 | private componentFactoryResolver: ComponentFactoryResolver, 30 | ) { 31 | } 32 | 33 | ngOnInit() { 34 | // 1. 创建DomPortalHost 35 | this.portalHost = new DomPortalHost( 36 | this.elementRef.nativeElement as HTMLElement, 37 | this.componentFactoryResolver, 38 | this.appRef, 39 | this.injector 40 | ); 41 | // injectionTokens用于传递参数,如果不想传递参数,直接const templatePortal = new ComponentPortal(PortalChildComponent) 就可以了 42 | const injectionTokens = new WeakMap(); 43 | injectionTokens.set(PORTAL_CHILD_DATA, '构建组件传递的参数'); 44 | 45 | // 2. 创建ComponentPortal 46 | const templatePortal = new ComponentPortal(PortalChildComponent 47 | , this.viewContainerRef 48 | , new PortalInjector(this.injector, injectionTokens) 49 | , this.componentFactoryResolver); 50 | 51 | // 3. ComponentPortal attach 到DomPortalHost里面去, 并且把ComponentPortal里面的时间返回上来 52 | // 如果不需要传出参数,this.portalHost.attach(templatePortal); 就可以了 53 | const portalComponentRef: ComponentRef = this.portalHost.attachComponentPortal(templatePortal); 54 | // 处理返回回来的事件 55 | const eventEmitter: EventEmitter = new EventEmitter(); 56 | portalComponentRef.instance.outEvent = eventEmitter; 57 | eventEmitter.pipe(takeUntil(this._$destroy)) 58 | .subscribe((event: string) => this.handlerPortalEvent(event)); 59 | } 60 | 61 | private handlerPortalEvent(event: string): void { 62 | console.log('收到了Portal返回上来的事件信息:' + event); 63 | } 64 | 65 | ngOnDestroy(): void { 66 | this._$destroy.next(); 67 | this._$destroy.complete(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/app/portal/portal-template/portal-template.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationRef, 3 | Component, 4 | ComponentFactoryResolver, 5 | ElementRef, 6 | Injector, 7 | OnInit, 8 | TemplateRef, 9 | ViewChild, 10 | ViewContainerRef 11 | } from '@angular/core'; 12 | import {DomPortalHost, TemplatePortal} from "@angular/cdk/portal"; 13 | 14 | @Component({ 15 | selector: 'app-portal-template', 16 | template: ` 17 | 18 | 19 |
参数: {{ data }}
20 |
21 | ` 22 | }) 23 | export class PortalTemplateComponent implements OnInit { 24 | 25 | @ViewChild('portalTemplate') testTemplate: TemplateRef; 26 | 27 | constructor( 28 | private elementRef: ElementRef, 29 | private injector: Injector, 30 | private appRef: ApplicationRef, 31 | private viewContainerRef: ViewContainerRef, 32 | private componentFactoryResolver: ComponentFactoryResolver, 33 | ) { 34 | } 35 | 36 | ngOnInit() { 37 | 38 | // 1. DomPortalHost 39 | const portalHost = new DomPortalHost( 40 | this.elementRef.nativeElement as HTMLElement, 41 | this.componentFactoryResolver, 42 | this.appRef, 43 | this.injector 44 | ); 45 | // 2. TemplatePortal 46 | const templatePortal = new TemplatePortal( 47 | this.testTemplate, 48 | this.viewContainerRef, 49 | { 50 | $implicit: "我是传递进来的数据", 51 | } 52 | ); 53 | // 3. attach 54 | portalHost.attach(templatePortal); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/app/portal/portal-tool-tip/tool-tip.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, TemplateRef, ViewChild} from '@angular/core'; 2 | 3 | @Component({ 4 | template: ` 5 | 6 | {{ tooltipText }} 7 | `, 8 | styles: [ 9 | ` 10 | .tooltip { 11 | position: absolute; 12 | color: red; 13 | top: 20px; 14 | left: 20px; 15 | } 16 | ` 17 | ] 18 | }) 19 | export class ToolTipComponent { 20 | @ViewChild('tooltip') tooltip: TemplateRef; 21 | } 22 | -------------------------------------------------------------------------------- /src/app/portal/portal-tool-tip/tool-tip.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationRef, ComponentFactoryResolver, 3 | Directive, 4 | ElementRef, 5 | HostBinding, 6 | HostListener, 7 | Injector, 8 | Input, 9 | OnInit, 10 | ViewContainerRef 11 | } from '@angular/core'; 12 | import {DomPortalHost, TemplatePortal} from "@angular/cdk/portal"; 13 | import {ToolTipComponent} from "./tool-tip.component"; 14 | import {ComponentRef} from "@angular/core"; 15 | 16 | @Directive({ 17 | selector: '[appToolTip]' 18 | }) 19 | export class ToolTipDirective implements OnInit { 20 | 21 | @Input('tooltipText') 22 | tooltipText: string; 23 | 24 | private _tooltipPortalHost: DomPortalHost; 25 | private _templatePortal: TemplatePortal; 26 | 27 | @HostBinding('style.position') position = 'relative'; 28 | 29 | /** 30 | * 鼠标移入的时候显示 31 | */ 32 | @HostListener('mouseenter') 33 | onMouseEnter() { 34 | this.show(); 35 | } 36 | 37 | /** 38 | * 鼠标移出的时候隐藏 39 | */ 40 | @HostListener('mouseleave') 41 | onMouseLeave() { 42 | this.hide(); 43 | } 44 | 45 | constructor( 46 | private elementRef: ElementRef, 47 | private injector: Injector, 48 | private appRef: ApplicationRef, 49 | private viewContainerRef: ViewContainerRef, 50 | private componentFactoryResolver: ComponentFactoryResolver, 51 | ) { 52 | } 53 | 54 | ngOnInit() { 55 | this.createContainerTemplate(); 56 | } 57 | 58 | private createContainerTemplate() { 59 | this._tooltipPortalHost = new DomPortalHost( 60 | (this.elementRef.nativeElement as HTMLElement), 61 | this.componentFactoryResolver, 62 | this.appRef, 63 | this.injector 64 | ); 65 | 66 | const tooltipComponent = this.componentFactoryResolver.resolveComponentFactory(ToolTipComponent); 67 | const tooltipComponentRef: ComponentRef = tooltipComponent.create(this.injector); 68 | 69 | this._templatePortal = new TemplatePortal( 70 | tooltipComponentRef.instance.tooltip, 71 | this.viewContainerRef, 72 | { 73 | $implicit: this.tooltipText, 74 | } 75 | ); 76 | } 77 | 78 | /** 79 | * 显示 80 | */ 81 | private show() { 82 | if (!this._templatePortal.isAttached) { 83 | this._tooltipPortalHost.attach(this._templatePortal); 84 | } 85 | } 86 | 87 | /** 88 | * 隐藏 89 | */ 90 | private hide() { 91 | this._tooltipPortalHost.detach(); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/app/scrolling/cdk-scrolling-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkScrollingComponent} from './cdk-scrolling.component'; 5 | 6 | 7 | const routes: Routes = [ 8 | { 9 | path: '', 10 | component: CdkScrollingComponent 11 | } 12 | ]; 13 | 14 | @NgModule({ 15 | imports: [ 16 | RouterModule.forChild(routes), 17 | CommonModule 18 | ], 19 | exports: [ 20 | RouterModule 21 | ], 22 | providers: [] 23 | }) 24 | export class CdkScrollingRoutingModule { 25 | } 26 | -------------------------------------------------------------------------------- /src/app/scrolling/cdk-scrolling.component.html: -------------------------------------------------------------------------------- 1 |

cdk scrolling

2 | 3 |
4 |
item 1
5 |
item 2
6 |
item 3
7 |
8 |
9 |
item 1
10 |
item 2
11 |
item 3
12 |
13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 | -------------------------------------------------------------------------------- /src/app/scrolling/cdk-scrolling.component.less: -------------------------------------------------------------------------------- 1 | .scrolling-parent { 2 | height: 100px; 3 | width: 200px; 4 | border: 1px solid black; 5 | padding: 8px; 6 | overflow-y: auto; 7 | } 8 | 9 | .scrolling-item { 10 | height: 50px; 11 | } -------------------------------------------------------------------------------- /src/app/scrolling/cdk-scrolling.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; 2 | import {CdkScrollable, ScrollDispatcher, ViewportRuler} from '@angular/cdk/overlay'; 3 | 4 | @Component({ 5 | selector: 'app-cdk-scrolling', 6 | templateUrl: './cdk-scrolling.component.html', 7 | styleUrls: ['./cdk-scrolling.component.less'] 8 | }) 9 | export class CdkScrollingComponent implements OnInit, AfterViewInit { 10 | 11 | @ViewChild('scrollingParent') 12 | childDiv: ElementRef; 13 | 14 | constructor(private scrollDispatcher: ScrollDispatcher, private viewPortRuler: ViewportRuler) { 15 | } 16 | 17 | ngOnInit() { 18 | this.scrollDispatcher.scrolled().subscribe((scrollable: CdkScrollable) => { 19 | if (scrollable) { 20 | console.log('发生scroll了,來源于:'); 21 | console.log(scrollable.getElementRef().nativeElement); 22 | } 23 | }); 24 | 25 | /** 26 | * ViewportRuler 用来监听窗口的大小 27 | */ 28 | // { width, height } 29 | console.log(this.viewPortRuler.getViewportSize()); 30 | 31 | // { bottom, height, left, right, top, width } 32 | console.log(this.viewPortRuler.getViewportRect()); 33 | 34 | // { top, left } 35 | console.log(this.viewPortRuler.getViewportScrollPosition()); 36 | 37 | // native resize event object 38 | this.viewPortRuler.change().subscribe(resizeEvent => console.log(resizeEvent)); 39 | 40 | } 41 | 42 | ngAfterViewInit(): void { 43 | /** 44 | * 第二个参数auditTimeInMs表示事件延时多少秒发生 45 | * 当祖先设置了cdkScrollable指令,在孩子里面也能抓到scrolling事件 46 | */ 47 | this.scrollDispatcher.ancestorScrolled(this.childDiv).subscribe((scrollable: CdkScrollable) => { 48 | if (scrollable) { 49 | console.log('祖先发生scroll了,來源于:'); 50 | console.log(scrollable.getElementRef().nativeElement); 51 | } 52 | }); 53 | 54 | // 获取ScrollDispatcher里面所有注册了scrolling的组件信息 55 | console.log(this.scrollDispatcher.scrollContainers); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/app/scrolling/cdk-scrolling.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkScrollingRoutingModule} from './cdk-scrolling-routing.module'; 4 | import {CdkScrollingComponent} from './cdk-scrolling.component'; 5 | import {RouterModule} from '@angular/router'; 6 | import {VirtualScrollComponent} from './virtual-scroll/virtual-scroll.component'; 7 | import {ScrollingModule} from "@angular/cdk/scrolling"; 8 | import {VirtualScrollDataSourceComponent} from './virtual-scroll-data-source/virtual-scroll-data-source.component'; 9 | import {VirtualScrollHorizontalComponent} from './virtual-scroll-horizontal/virtual-scroll-horizontal.component'; 10 | import {VirtualScrollStrategiesComponent} from './virtual-scroll-strategies/virtual-scroll-strategies.component'; 11 | import {DragDropModule} from '@angular/cdk/drag-drop'; 12 | import { DragScrollingComponent } from './drag-scrolling/drag-scrolling.component'; 13 | 14 | @NgModule({ 15 | imports: [ 16 | CommonModule, 17 | RouterModule, 18 | DragDropModule, 19 | ScrollingModule, 20 | CdkScrollingRoutingModule 21 | ], 22 | declarations: [ 23 | CdkScrollingComponent, 24 | VirtualScrollComponent, 25 | VirtualScrollDataSourceComponent, 26 | VirtualScrollHorizontalComponent, 27 | VirtualScrollStrategiesComponent, 28 | DragScrollingComponent 29 | ] 30 | }) 31 | export class CdkScrollingModule { 32 | } 33 | -------------------------------------------------------------------------------- /src/app/scrolling/drag-scrolling/drag-scrolling.component.html: -------------------------------------------------------------------------------- 1 |

2 | drag和scrolling结合使用 3 |

4 |
5 |
6 |
7 |
{{movie}}
8 |
9 |
10 |
-------------------------------------------------------------------------------- /src/app/scrolling/drag-scrolling/drag-scrolling.component.less: -------------------------------------------------------------------------------- 1 | .example-list { 2 | width: 500px; 3 | max-width: 100%; 4 | border: solid 1px #ccc; 5 | min-height: 60px; 6 | display: block; 7 | background: white; 8 | border-radius: 4px; 9 | overflow: hidden; 10 | } 11 | 12 | .example-box { 13 | padding: 20px 10px; 14 | border-bottom: solid 1px #ccc; 15 | color: rgba(0, 0, 0, 0.87); 16 | display: flex; 17 | flex-direction: row; 18 | align-items: center; 19 | justify-content: space-between; 20 | box-sizing: border-box; 21 | cursor: move; 22 | background: white; 23 | font-size: 14px; 24 | } 25 | 26 | .cdk-drag-preview { 27 | box-sizing: border-box; 28 | border-radius: 4px; 29 | box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 30 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 31 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 32 | } 33 | 34 | .cdk-drag-placeholder { 35 | opacity: 0; 36 | } 37 | 38 | .cdk-drag-animating { 39 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 40 | } 41 | 42 | .example-box:last-child { 43 | border: none; 44 | } 45 | 46 | .example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { 47 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 48 | } -------------------------------------------------------------------------------- /src/app/scrolling/drag-scrolling/drag-scrolling.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef, OnDestroy, QueryList, ViewChild, ViewChildren} from '@angular/core'; 2 | import {CdkDrag, CdkDragDrop, CdkDragMove, moveItemInArray} from '@angular/cdk/drag-drop'; 3 | import {merge, Subscription} from 'rxjs'; 4 | import {map, startWith, switchMap, tap} from 'rxjs/operators'; 5 | 6 | const speed = 10; 7 | 8 | @Component({ 9 | selector: 'app-drag-scrolling', 10 | templateUrl: './drag-scrolling.component.html', 11 | styleUrls: ['./drag-scrolling.component.less'] 12 | }) 13 | export class DragScrollingComponent implements AfterViewInit, OnDestroy { 14 | movies = [ 15 | 'Episode I - The Phantom Menace', 16 | 'Episode II - Attack of the Clones', 17 | 'Episode III - Revenge of the Sith', 18 | 'Episode IV - A New Hope', 19 | 'Episode V - The Empire Strikes Back', 20 | 'Episode VI - Return of the Jedi', 21 | 'Episode VII - The Force Awakens', 22 | 'Episode VIII - The Last Jedi', 23 | 'Rouge One', 24 | 'Solo', 25 | 'Clone Wars' 26 | ]; 27 | 28 | animationFrame: number | undefined; 29 | 30 | @ViewChild('scrollEl') 31 | scrollEl: ElementRef; 32 | 33 | @ViewChildren(CdkDrag) 34 | dragEls: QueryList; 35 | 36 | subs = new Subscription(); 37 | 38 | ngOnDestroy(): void { 39 | this.subs.unsubscribe(); 40 | } 41 | 42 | ngAfterViewInit() { 43 | const onMove$ = this.dragEls.changes.pipe( 44 | startWith(this.dragEls) 45 | , map((d: QueryList) => d.toArray()) 46 | , map(dragels => dragels.map(drag => drag.moved)) 47 | , switchMap(obs => merge(...obs)) 48 | , tap(this.triggerScroll) 49 | ); 50 | 51 | this.subs.add(onMove$.subscribe()); 52 | 53 | const onDown$ = this.dragEls.changes.pipe( 54 | startWith(this.dragEls) 55 | , map((d: QueryList) => d.toArray()) 56 | , map(dragels => dragels.map(drag => drag.ended)) 57 | , switchMap(obs => merge(...obs)) 58 | , tap(this.cancelScroll) 59 | ); 60 | 61 | this.subs.add(onDown$.subscribe()); 62 | } 63 | 64 | drop(event: CdkDragDrop) { 65 | moveItemInArray(this.movies, event.previousIndex, event.currentIndex); 66 | } 67 | 68 | public triggerScroll($event: CdkDragMove) { 69 | if (this.animationFrame) { 70 | cancelAnimationFrame(this.animationFrame); 71 | this.animationFrame = undefined; 72 | } 73 | this.animationFrame = requestAnimationFrame(() => this.scroll($event)); 74 | } 75 | 76 | private cancelScroll() { 77 | if (this.animationFrame) { 78 | cancelAnimationFrame(this.animationFrame); 79 | this.animationFrame = undefined; 80 | } 81 | } 82 | 83 | private scroll($event: CdkDragMove) { 84 | const {y} = $event.pointerPosition; 85 | const baseEl = this.scrollEl.nativeElement; 86 | const box = baseEl.getBoundingClientRect(); 87 | const scrollTop = baseEl.scrollTop; 88 | const top = box.top + -y; 89 | if (top > 0 && scrollTop !== 0) { 90 | const newScroll = scrollTop - speed * Math.exp(top / 50); 91 | baseEl.scrollTop = newScroll; 92 | this.animationFrame = requestAnimationFrame(() => this.scroll($event)); 93 | return; 94 | } 95 | 96 | const bottom = y - box.bottom; 97 | if (bottom > 0 && scrollTop < box.bottom) { 98 | const newScroll = scrollTop + speed * Math.exp(bottom / 50); 99 | baseEl.scrollTop = newScroll; 100 | this.animationFrame = requestAnimationFrame(() => this.scroll($event)); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-data-source/customer-data-source.ts: -------------------------------------------------------------------------------- 1 | import {BehaviorSubject, Observable, Subscription} from "rxjs"; 2 | import {CollectionViewer, DataSource} from "@angular/cdk/collections"; 3 | 4 | export class CustomerDataSource extends DataSource{ 5 | 6 | private length = 100000; 7 | private pageSize = 100; 8 | private cachedData = Array.from({length: this.length}); 9 | private fetchedPages = new Set(); 10 | private dataStream = new BehaviorSubject<(string | undefined)[]>(this.cachedData); 11 | private subscription = new Subscription(); 12 | 13 | connect(collectionViewer: CollectionViewer): Observable<(string | undefined)[]> { 14 | this.subscription.add(collectionViewer.viewChange.subscribe(range => { 15 | const startPage = this.getPageForIndex(range.start); 16 | const endPage = this.getPageForIndex(range.end - 1); 17 | for (let i = startPage; i <= endPage; i++) { 18 | this.fetchPage(i); 19 | } 20 | })); 21 | return this.dataStream; 22 | } 23 | 24 | disconnect(): void { 25 | this.subscription.unsubscribe(); 26 | } 27 | 28 | private getPageForIndex(index: number): number { 29 | return Math.floor(index / this.pageSize); 30 | } 31 | 32 | private fetchPage(page: number) { 33 | if (this.fetchedPages.has(page)) { 34 | return; 35 | } 36 | this.fetchedPages.add(page); 37 | 38 | // Use `setTimeout` to simulate fetching data from server. 39 | setTimeout(() => { 40 | this.cachedData.splice(page * this.pageSize, this.pageSize, 41 | ...Array.from({length: this.pageSize}) 42 | .map((_, i) => `Item #${page * this.pageSize + i}`)); 43 | this.dataStream.next(this.cachedData); 44 | }, Math.random() * 1000 + 200); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-data-source/virtual-scroll-data-source.component.html: -------------------------------------------------------------------------------- 1 | 2 |

DataSource

3 | 4 |
{{item || 'Loading...'}}
5 |
6 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-data-source/virtual-scroll-data-source.component.less: -------------------------------------------------------------------------------- 1 | .example-viewport { 2 | height: 200px; 3 | width: 200px; 4 | border: 1px solid black; 5 | } 6 | 7 | .example-item { 8 | height: 50px; 9 | } -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-data-source/virtual-scroll-data-source.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {CustomerDataSource} from "./customer-data-source"; 3 | 4 | @Component({ 5 | selector: 'app-virtual-scroll-data-source', 6 | templateUrl: './virtual-scroll-data-source.component.html', 7 | styleUrls: ['./virtual-scroll-data-source.component.less'] 8 | }) 9 | export class VirtualScrollDataSourceComponent { 10 | 11 | ds = new CustomerDataSource(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-horizontal/virtual-scroll-horizontal.component.html: -------------------------------------------------------------------------------- 1 | 2 |

cdk-virtual-scroll-viewport horizontal

3 |
4 | 5 |
{{item}}
6 |
7 |
8 | 9 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-horizontal/virtual-scroll-horizontal.component.less: -------------------------------------------------------------------------------- 1 | .cdk-virtual-scroll-data-source-example .example-viewport { 2 | height: 200px; 3 | width: 200px; 4 | border: 1px solid black; 5 | } 6 | 7 | .cdk-virtual-scroll-data-source-example .example-viewport .cdk-virtual-scroll-content-wrapper { 8 | display: flex; 9 | flex-direction: row; 10 | } 11 | 12 | .cdk-virtual-scroll-data-source-example .example-item { 13 | width: 50px; 14 | height: 100%; 15 | writing-mode: vertical-lr; 16 | } 17 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-horizontal/virtual-scroll-horizontal.component.ts: -------------------------------------------------------------------------------- 1 | import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-virtual-scroll-horizontal', 5 | templateUrl: './virtual-scroll-horizontal.component.html', 6 | styleUrls: ['./virtual-scroll-horizontal.component.less'], 7 | encapsulation: ViewEncapsulation.None, 8 | changeDetection: ChangeDetectionStrategy.OnPush, 9 | }) 10 | export class VirtualScrollHorizontalComponent { 11 | 12 | items = Array.from({length: 100000}).map((_, i) => `Item #${i}`); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-strategies/custom-virtual-scroll-strategies.ts: -------------------------------------------------------------------------------- 1 | import {FixedSizeVirtualScrollStrategy} from "@angular/cdk/scrolling"; 2 | 3 | export class CustomVirtualScrollStrategies extends FixedSizeVirtualScrollStrategy { 4 | constructor() { 5 | super(50, 250, 500); 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-strategies/virtual-scroll-strategies.component.html: -------------------------------------------------------------------------------- 1 | 2 |

通过provider提供缓存策略

3 | 4 |
{{item}}
5 |
-------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-strategies/virtual-scroll-strategies.component.less: -------------------------------------------------------------------------------- 1 | .example-viewport { 2 | height: 200px; 3 | width: 200px; 4 | border: 1px solid black; 5 | } 6 | 7 | .example-item { 8 | height: 50px; 9 | } 10 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll-strategies/virtual-scroll-strategies.component.ts: -------------------------------------------------------------------------------- 1 | import {ChangeDetectionStrategy, Component} from '@angular/core'; 2 | import {VIRTUAL_SCROLL_STRATEGY} from "@angular/cdk/scrolling"; 3 | import {CustomVirtualScrollStrategies} from "./custom-virtual-scroll-strategies"; 4 | 5 | @Component({ 6 | selector: 'app-virtual-scroll-strategies', 7 | templateUrl: './virtual-scroll-strategies.component.html', 8 | styleUrls: ['./virtual-scroll-strategies.component.less'], 9 | changeDetection: ChangeDetectionStrategy.OnPush, 10 | providers: [{provide: VIRTUAL_SCROLL_STRATEGY, useClass: CustomVirtualScrollStrategies}] 11 | }) 12 | export class VirtualScrollStrategiesComponent { 13 | 14 | items = Array.from({length: 100000}).map((_, i) => `Item #${i}`); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll/virtual-scroll.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

cdk-virtual-scroll-viewport组件使用

4 | 5 |
12 |
Item: {{item}}
13 |
Index: {{index}}
14 |
Count: {{count}}
15 |
First: {{first ? 'Yes' : 'No'}}
16 |
Last: {{last ? 'Yes' : 'No'}}
17 |
Even: {{even ? 'Yes' : 'No'}}
18 |
Odd: {{odd ? 'Yes' : 'No'}}
19 |
20 |
21 | 22 | 25 |
26 | 27 | -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll/virtual-scroll.component.less: -------------------------------------------------------------------------------- 1 | .example-viewport { 2 | height: 200px; 3 | width: 200px; 4 | border: 1px solid black; 5 | } 6 | 7 | .example-item-detail { 8 | height: 18px; 9 | } 10 | 11 | .example-alternate { 12 | background: rgba(127, 127, 127, 0.3); 13 | } -------------------------------------------------------------------------------- /src/app/scrolling/virtual-scroll/virtual-scroll.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ViewChild} from '@angular/core'; 2 | import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling"; 3 | 4 | @Component({ 5 | selector: 'app-virtual-scroll', 6 | templateUrl: './virtual-scroll.component.html', 7 | styleUrls: ['./virtual-scroll.component.less'] 8 | }) 9 | export class VirtualScrollComponent implements AfterViewInit{ 10 | 11 | @ViewChild('scrollComponent') 12 | private _scrollViewport: CdkVirtualScrollViewport; 13 | 14 | items = Array.from({length: 100000}).map((_, i) => `Item #${i}`); 15 | 16 | ngAfterViewInit(): void { 17 | } 18 | 19 | onButtonClick() { 20 | this._scrollViewport.scrollToIndex(10); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/table/cdk-base-table-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from '@angular/router'; 4 | import {CdkBaseTableComponent} from './cdk-base-table.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkBaseTableComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkBaseTableRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/table/cdk-base-table.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
No. {{element.position}} Name {{element.name}} Weight {{element.weight}} Symbol {{element.symbol}}
-------------------------------------------------------------------------------- /src/app/table/cdk-base-table.component.less: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | } 4 | 5 | th { 6 | text-align: left; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/table/cdk-base-table.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {DataSource} from '@angular/cdk/table'; 3 | import {BehaviorSubject, Observable} from 'rxjs'; 4 | 5 | export interface PeriodicElement { 6 | name: string; 7 | position: number; 8 | weight: number; 9 | symbol: string; 10 | } 11 | 12 | const ELEMENT_DATA: PeriodicElement[] = [ 13 | {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'}, 14 | {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'}, 15 | {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'}, 16 | {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'}, 17 | {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'}, 18 | {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'}, 19 | {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'}, 20 | {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'}, 21 | {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'}, 22 | {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'}, 23 | ]; 24 | 25 | @Component({ 26 | selector: 'app-cdk-table', 27 | templateUrl: './cdk-base-table.component.html', 28 | styleUrls: ['./cdk-base-table.component.less'] 29 | }) 30 | export class CdkBaseTableComponent { 31 | 32 | displayedColumns: string[] = ['position', 'name', 'weight', 'symbol']; 33 | dataSource = new ExampleDataSource(); 34 | 35 | } 36 | 37 | /** 38 | * Data source to provide what data should be rendered in the table. Note that the data source 39 | * can retrieve its data in any way. In this case, the data source is provided a reference 40 | * to a common data base, ExampleDatabase. It is not the data source's responsibility to manage 41 | * the underlying data. Instead, it only needs to take the data and send the table exactly what 42 | * should be rendered. 43 | */ 44 | export class ExampleDataSource extends DataSource { 45 | /** Stream of data that is provided to the table. */ 46 | data = new BehaviorSubject(ELEMENT_DATA); 47 | 48 | /** Connect function called by the table to retrieve one stream containing the data to render. */ 49 | connect(): Observable { 50 | return this.data; 51 | } 52 | 53 | disconnect() {} 54 | } 55 | -------------------------------------------------------------------------------- /src/app/table/cdk-base-table.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkBaseTableComponent} from './cdk-base-table.component'; 4 | import {CdkBaseTableRoutingModule} from './cdk-base-table-routing.module'; 5 | import {CdkTableModule} from '@angular/cdk/table'; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | CdkTableModule, 11 | CdkBaseTableRoutingModule 12 | ], 13 | declarations: [CdkBaseTableComponent] 14 | }) 15 | export class CdkBaseTableModule { 16 | } 17 | -------------------------------------------------------------------------------- /src/app/text-field/cdk-text-field-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from "@angular/router"; 4 | import {CdkTextFieldComponent} from "./cdk-text-field.component"; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkTextFieldComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkTextFieldRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/text-field/cdk-text-field.component.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 | -------------------------------------------------------------------------------- /src/app/text-field/cdk-text-field.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/text-field/cdk-text-field.component.less -------------------------------------------------------------------------------- /src/app/text-field/cdk-text-field.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild} from '@angular/core'; 2 | import {AutofillMonitor, CdkTextareaAutosize} from "@angular/cdk/text-field"; 3 | import {take} from "rxjs/operators"; 4 | 5 | @Component({ 6 | selector: 'app-cdk-text-field', 7 | templateUrl: './cdk-text-field.component.html', 8 | styleUrls: ['./cdk-text-field.component.less'] 9 | }) 10 | export class CdkTextFieldComponent implements OnDestroy, OnInit { 11 | @ViewChild('autosize') autosize: CdkTextareaAutosize; 12 | @ViewChild('first', {read: ElementRef}) firstName: ElementRef; 13 | @ViewChild('last', {read: ElementRef}) lastName: ElementRef; 14 | firstNameAutofilled: boolean; 15 | lastNameAutofilled: boolean; 16 | 17 | 18 | constructor(private ngZone: NgZone, private autofill: AutofillMonitor) { 19 | } 20 | 21 | ngOnInit() { 22 | this.autofill.monitor(this.firstName) 23 | .subscribe(e => this.firstNameAutofilled = e.isAutofilled); 24 | this.autofill.monitor(this.lastName) 25 | .subscribe(e => this.lastNameAutofilled = e.isAutofilled); 26 | } 27 | 28 | ngOnDestroy() { 29 | this.autofill.stopMonitoring(this.firstName); 30 | this.autofill.stopMonitoring(this.lastName); 31 | } 32 | 33 | /** 34 | * 外部调用触发调整大小的逻辑,比如当fontSize改变之后,调用该函数 35 | */ 36 | triggerResize() { 37 | // 重新适应内容,比如fontSize改变了,可以调用该方法重新适应内容 38 | this.ngZone.onStable.pipe(take(1)) 39 | .subscribe(() => this.autosize.resizeToFitContent(true)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/text-field/cdk-text-field.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkTextFieldComponent} from './cdk-text-field.component'; 4 | import {CdkTextFieldRoutingModule} from "./cdk-text-field-routing.module"; 5 | import {RouterModule} from "@angular/router"; 6 | import {TextFieldModule} from "@angular/cdk/text-field"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | RouterModule, 12 | TextFieldModule, 13 | CdkTextFieldRoutingModule 14 | ], 15 | declarations: [CdkTextFieldComponent] 16 | }) 17 | export class CdkTextFieldModule { 18 | } 19 | -------------------------------------------------------------------------------- /src/app/tip/tip.component.html: -------------------------------------------------------------------------------- 1 |
2 |
tootip on left
3 |
tootip on top
4 |
tootip on bottom
5 |
tootip on right
6 |
7 | -------------------------------------------------------------------------------- /src/app/tip/tip.component.less: -------------------------------------------------------------------------------- 1 | .tooltip-example { 2 | text-align: center; 3 | padding: 0 50px; 4 | } 5 | 6 | .tooltip-example [appTip] { 7 | display: inline-block; 8 | margin: 50px 20px; 9 | width: 180px; 10 | height: 50px; 11 | border: 1px solid gray; 12 | border-radius: 5px; 13 | line-height: 50px; 14 | text-align: center; 15 | } -------------------------------------------------------------------------------- /src/app/tip/tip.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-tip', 5 | templateUrl: './tip.component.html', 6 | styleUrls: ['./tip.component.less'] 7 | }) 8 | export class TipComponent { 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/app/tip/tip.directive.ts: -------------------------------------------------------------------------------- 1 | import {Directive, ElementRef, HostListener, Input, Renderer2} from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[appTip]' 5 | }) 6 | export class TipDirective { 7 | 8 | @Input() tooltipTitle: string; 9 | @Input() placement: string; 10 | @Input() delay: string; 11 | tooltip: HTMLElement; 12 | offset = 10; 13 | 14 | constructor(private el: ElementRef, private renderer: Renderer2) { 15 | } 16 | 17 | @HostListener('mouseenter') onMouseEnter() { 18 | if (!this.tooltip) { 19 | this.show(); 20 | } 21 | } 22 | 23 | @HostListener('mouseleave') onMouseLeave() { 24 | if (this.tooltip) { 25 | this.hide(); 26 | } 27 | } 28 | 29 | show() { 30 | this.create(); 31 | this.setPosition(); 32 | this.renderer.addClass(this.tooltip, 'ng-tooltip-show'); 33 | } 34 | 35 | hide() { 36 | this.renderer.removeClass(this.tooltip, 'ng-tooltip-show'); 37 | window.setTimeout(() => { 38 | this.renderer.removeChild(document.body, this.tooltip); 39 | this.tooltip = null; 40 | }, this.delay); 41 | } 42 | 43 | create() { 44 | this.tooltip = this.renderer.createElement('span'); 45 | 46 | this.renderer.appendChild( 47 | this.tooltip, 48 | this.renderer.createText(this.tooltipTitle) // textNode 49 | ); 50 | 51 | this.renderer.appendChild(document.body, this.tooltip); 52 | // this.renderer.appendChild(this.el.nativeElement, this.tooltip); 53 | 54 | this.renderer.addClass(this.tooltip, 'ng-tooltip'); 55 | this.renderer.addClass(this.tooltip, `ng-tooltip-${this.placement}`); 56 | 57 | // delay 58 | this.renderer.setStyle(this.tooltip, '-webkit-transition', `opacity ${this.delay}ms`); 59 | this.renderer.setStyle(this.tooltip, '-moz-transition', `opacity ${this.delay}ms`); 60 | this.renderer.setStyle(this.tooltip, '-o-transition', `opacity ${this.delay}ms`); 61 | this.renderer.setStyle(this.tooltip, 'transition', `opacity ${this.delay}ms`); 62 | } 63 | 64 | setPosition() { 65 | const hostPos = this.el.nativeElement.getBoundingClientRect(); 66 | 67 | // tooltip 68 | const tooltipPos = this.tooltip.getBoundingClientRect(); 69 | 70 | // getBoundingClientRect 71 | const scrollPos = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; 72 | 73 | let top, left; 74 | 75 | if (this.placement === 'top') { 76 | top = hostPos.top - tooltipPos.height - this.offset; 77 | left = hostPos.left + (hostPos.width - tooltipPos.width) / 2; 78 | } 79 | 80 | if (this.placement === 'bottom') { 81 | top = hostPos.bottom + this.offset; 82 | left = hostPos.left + (hostPos.width - tooltipPos.width) / 2; 83 | } 84 | 85 | if (this.placement === 'left') { 86 | top = hostPos.top + (hostPos.height - tooltipPos.height) / 2; 87 | left = hostPos.left - tooltipPos.width - this.offset; 88 | } 89 | 90 | if (this.placement === 'right') { 91 | top = hostPos.top + (hostPos.height - tooltipPos.height) / 2; 92 | left = hostPos.right + this.offset; 93 | } 94 | 95 | this.renderer.setStyle(this.tooltip, 'top', `${top + scrollPos}px`); 96 | this.renderer.setStyle(this.tooltip, 'left', `${left}px`); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/app/workflow-stepper/cdk-workflow-stepper-routing.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {RouterModule, Routes} from "@angular/router"; 4 | import {CdkWorkflowStepperComponent} from "./cdk-workflow-stepper.component"; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | component: CdkWorkflowStepperComponent 10 | } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ 15 | RouterModule.forChild(routes), 16 | CommonModule 17 | ], 18 | exports: [ 19 | RouterModule 20 | ], 21 | providers: [] 22 | }) 23 | export class CdkWorkflowStepperRoutingModule { } 24 | -------------------------------------------------------------------------------- /src/app/workflow-stepper/cdk-workflow-stepper.component.html: -------------------------------------------------------------------------------- 1 |

cdk stepper

2 | 3 | 4 | 第一步 5 |
6 | 7 |
8 | 9 |
10 | 11 | 第二步 12 |
13 | 14 |
15 | 16 | 17 |
18 |
-------------------------------------------------------------------------------- /src/app/workflow-stepper/cdk-workflow-stepper.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/app/workflow-stepper/cdk-workflow-stepper.component.less -------------------------------------------------------------------------------- /src/app/workflow-stepper/cdk-workflow-stepper.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit, ViewChild} from '@angular/core'; 2 | import {FormControl} from '@angular/forms'; 3 | import {CdkStep, CdkStepper} from '@angular/cdk/stepper'; 4 | 5 | @Component({ 6 | selector: 'app-cdk-workflow-stepper', 7 | templateUrl: './cdk-workflow-stepper.component.html', 8 | styleUrls: ['./cdk-workflow-stepper.component.less'] 9 | }) 10 | export class CdkWorkflowStepperComponent { 11 | 12 | name = 'Angular 5'; 13 | form1 = new FormControl(); 14 | @ViewChild('stepper') stepper: CdkStepper; 15 | @ViewChild('step1') step1: CdkStep; 16 | @ViewChild('step2') step2: CdkStep; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/app/workflow-stepper/cdk-workflow-stepper.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | import {CommonModule} from '@angular/common'; 3 | import {CdkWorkflowStepperComponent} from './cdk-workflow-stepper.component'; 4 | import {CdkWorkflowStepperRoutingModule} from './cdk-workflow-stepper-routing.module'; 5 | import {CdkStepperModule} from '@angular/cdk/stepper'; 6 | import {StepperComponent} from './stepper/stepper.component'; 7 | import {FormsModule} from '@angular/forms'; 8 | import {StepperHeaderComponent} from './stepper/stepper-header.component'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | FormsModule, 14 | CdkStepperModule, 15 | CdkWorkflowStepperRoutingModule 16 | ], 17 | declarations: [CdkWorkflowStepperComponent, StepperComponent, StepperHeaderComponent] 18 | }) 19 | export class CdkWorkflowStepperModule { 20 | } 21 | -------------------------------------------------------------------------------- /src/app/workflow-stepper/stepper/stepper-animations.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Google LLC All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file at https://angular.io/license 7 | */ 8 | import { 9 | animate, 10 | state, 11 | style, 12 | transition, 13 | trigger, 14 | AnimationTriggerMetadata, 15 | } from '@angular/animations'; 16 | 17 | /** 18 | * Animations used by the Material steppers. 19 | * @docs-private 20 | */ 21 | export const yxStepperAnimations: { 22 | readonly horizontalStepTransition: AnimationTriggerMetadata; 23 | readonly verticalStepTransition: AnimationTriggerMetadata; 24 | } = { 25 | /** Animation that transitions the step along the X axis in a horizontal stepper. */ 26 | horizontalStepTransition: trigger('stepTransition', [ 27 | state('previous', style({transform: 'translate3d(-100%, 0, 0)', visibility: 'hidden'})), 28 | state('current', style({transform: 'none', visibility: 'visible'})), 29 | state('next', style({transform: 'translate3d(100%, 0, 0)', visibility: 'hidden'})), 30 | transition('* => *', animate('500ms cubic-bezier(0.35, 0, 0.25, 1)')) 31 | ]), 32 | 33 | /** Animation that transitions the step along the Y axis in a vertical stepper. */ 34 | verticalStepTransition: trigger('stepTransition', [ 35 | state('previous', style({height: '0px', visibility: 'hidden'})), 36 | state('next', style({height: '0px', visibility: 'hidden'})), 37 | state('current', style({height: '*', visibility: 'visible'})), 38 | transition('* <=> current', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')) 39 | ]) 40 | }; -------------------------------------------------------------------------------- /src/app/workflow-stepper/stepper/stepper-header.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 | 6 | {{index + 1}} 7 | 8 | 9 | 10 | {{index + 1}} 11 | 12 | 13 | 14 | {{index + 1}} 15 | 16 | 17 | 18 | {{index + 1}} 19 | 20 | 21 | 22 | {{index + 1}} 23 | 24 |
25 |
26 | 27 | 28 |
29 | 30 | 31 | 32 |
{{label}}
33 |
34 | 35 | -------------------------------------------------------------------------------- /src/app/workflow-stepper/stepper/stepper-header.component.less: -------------------------------------------------------------------------------- 1 | .yx-step-icon { 2 | border-radius: 50%; 3 | height: 24px; 4 | width: 24px; 5 | flex-shrink: 0; 6 | position: relative; 7 | } 8 | 9 | .yx-step-icon-content { 10 | // Use absolute positioning to center the content, because it works better with text. 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | transform: translate(-50%, -50%); 15 | } 16 | 17 | .yx-step-label { 18 | display: inline-block; 19 | white-space: nowrap; 20 | overflow: hidden; 21 | text-overflow: ellipsis; 22 | min-width: 50px; 23 | vertical-align: middle; 24 | } -------------------------------------------------------------------------------- /src/app/workflow-stepper/stepper/stepper-header.component.ts: -------------------------------------------------------------------------------- 1 | import {ChangeDetectorRef, Component, ElementRef, Input, OnDestroy} from '@angular/core'; 2 | import {CdkStepHeader, CdkStepLabel, StepState} from '@angular/cdk/stepper'; 3 | import {FocusMonitor} from '@angular/cdk/a11y'; 4 | 5 | @Component({ 6 | selector: 'yx-stepper-header', 7 | templateUrl: './stepper-header.component.html', 8 | styleUrls: ['./stepper-header.component.less'] 9 | }) 10 | export class StepperHeaderComponent extends CdkStepHeader implements OnDestroy { 11 | 12 | /** State of the given step. */ 13 | @Input() state: StepState; 14 | 15 | /** Label of the given step. */ 16 | @Input() label: CdkStepLabel | string; 17 | 18 | /** Index of the given step. */ 19 | @Input() index: number; 20 | 21 | /** Whether the given step is selected. */ 22 | @Input() selected: boolean; 23 | 24 | /** Whether the given step label is active. */ 25 | @Input() active: boolean; 26 | 27 | /** Whether the given step is optional. */ 28 | @Input() optional: boolean; 29 | 30 | constructor( 31 | private _focusMonitor: FocusMonitor, 32 | _elementRef: ElementRef, 33 | changeDetectorRef: ChangeDetectorRef) { 34 | super(_elementRef); 35 | _focusMonitor.monitor(_elementRef, true); 36 | } 37 | 38 | ngOnDestroy() { 39 | this._focusMonitor.stopMonitoring(this._elementRef); 40 | } 41 | 42 | /** Returns the host HTML element. */ 43 | _getHostElement() { 44 | return this._elementRef.nativeElement; 45 | } 46 | 47 | /** Returns MatStepLabel if the label of given step is a template label. */ 48 | _templateLabel(): CdkStepLabel | null { 49 | return this.label instanceof CdkStepLabel ? this.label : null; 50 | } 51 | 52 | /** Returns string label of given step if it is a text label. */ 53 | _stringLabel(): string | null { 54 | return this.label instanceof CdkStepLabel ? null : this.label; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/app/workflow-stepper/stepper/stepper.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 21 | 22 | 23 |
24 | 25 | 26 |
27 |
35 | 36 |
37 |
38 | -------------------------------------------------------------------------------- /src/app/workflow-stepper/stepper/stepper.component.less: -------------------------------------------------------------------------------- 1 | .yx-stepper-header-container { 2 | white-space: nowrap; 3 | display: flex; 4 | align-items: center; 5 | } 6 | 7 | .yx-stepper-header { 8 | display: flex; 9 | height: 24px; 10 | overflow: hidden; 11 | align-items: center; 12 | padding: 0 24px; 13 | } 14 | 15 | .yx-content-container { 16 | overflow: hidden; 17 | } 18 | 19 | /** 20 | 保证值显示一个step 21 | */ 22 | .yx-stepper-content[aria-expanded='false'] { 23 | height: 0; 24 | overflow: hidden; 25 | } -------------------------------------------------------------------------------- /src/app/workflow-stepper/stepper/stepper.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterContentInit, 3 | ChangeDetectionStrategy, 4 | Component, 5 | ContentChildren, 6 | EventEmitter, 7 | Output, 8 | QueryList, 9 | ViewEncapsulation 10 | } from '@angular/core'; 11 | import {AnimationEvent} from '@angular/animations'; 12 | import {CdkStep, CdkStepper, StepContentPositionState} from '@angular/cdk/stepper'; 13 | import {Subject} from 'rxjs'; 14 | import {distinctUntilChanged, takeUntil} from 'rxjs/operators'; 15 | import {yxStepperAnimations} from './stepper-animations'; 16 | 17 | @Component({ 18 | selector: 'yx-stepper', 19 | templateUrl: './stepper.component.html', 20 | styleUrls: ['./stepper.component.less'], 21 | providers: [{provide: CdkStepper, useExisting: StepperComponent}], 22 | animations: [yxStepperAnimations.horizontalStepTransition], 23 | encapsulation: ViewEncapsulation.None, 24 | changeDetection: ChangeDetectionStrategy.OnPush, 25 | }) 26 | export class StepperComponent extends CdkStepper implements AfterContentInit { 27 | 28 | /** Steps that the stepper holds. */ 29 | @ContentChildren(CdkStep) _steps: QueryList; 30 | 31 | /** Event emitted when the current step is done transitioning in. */ 32 | @Output() readonly animationDone: EventEmitter = new EventEmitter(); 33 | 34 | _animationDone = new Subject(); 35 | 36 | ngAfterContentInit() { 37 | 38 | // Mark the component for change detection whenever the content children query changes 39 | this._steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => this._stateChanged()); 40 | 41 | this._animationDone.pipe( 42 | // This needs a `distinctUntilChanged` in order to avoid emitting the same event twice due 43 | // to a bug in animations where the `.done` callback gets invoked twice on some browsers. 44 | // See https://github.com/angular/angular/issues/24084 45 | distinctUntilChanged((x, y) => x.fromState === y.fromState && x.toState === y.toState), 46 | takeUntil(this._destroyed) 47 | ).subscribe(event => { 48 | if ((event.toState as StepContentPositionState) === 'current') { 49 | this.animationDone.emit(); 50 | } 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * In development mode, to ignore zone related error stack frames such as 11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 12 | * import the following file, but please comment it out in production mode 13 | * because it will have performance impact when throw error 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuacy/angular-cdk-study/83abe74d0a6473d544ab3320fe696c654642da80/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularCdkStudy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/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: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.log(err)); 13 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/styles.less: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | /* Master Styles */ 3 | @import '~@angular/cdk/overlay-prebuilt.css'; 4 | 5 | h1 { 6 | color: #369; 7 | font-family: Arial, Helvetica, sans-serif; 8 | font-size: 250%; 9 | } 10 | h2, h3 { 11 | color: #444; 12 | font-family: Arial, Helvetica, sans-serif; 13 | font-weight: lighter; 14 | } 15 | body { 16 | margin: 2em; 17 | } 18 | body, input[text], button { 19 | color: #888; 20 | font-family: Cambria, Georgia; 21 | } 22 | a { 23 | cursor: pointer; 24 | cursor: hand; 25 | } 26 | button { 27 | font-family: Arial; 28 | background-color: #eee; 29 | border: none; 30 | padding: 5px 10px; 31 | border-radius: 4px; 32 | cursor: pointer; 33 | cursor: hand; 34 | } 35 | button:hover { 36 | background-color: #cfd8dc; 37 | } 38 | button:disabled { 39 | background-color: #eee; 40 | color: #aaa; 41 | cursor: auto; 42 | } 43 | 44 | /* Navigation link styles */ 45 | nav a { 46 | padding: 5px 10px; 47 | text-decoration: none; 48 | margin-right: 10px; 49 | margin-top: 10px; 50 | display: inline-block; 51 | background-color: #eee; 52 | border-radius: 4px; 53 | } 54 | nav a:visited, a:link { 55 | color: #607D8B; 56 | } 57 | nav a:hover { 58 | color: #039be5; 59 | background-color: #CFD8DC; 60 | } 61 | nav a.active { 62 | color: #039be5; 63 | } 64 | 65 | /* everywhere else */ 66 | * { 67 | font-family: Arial, Helvetica, sans-serif; 68 | } 69 | 70 | .badge { 71 | display: inline-block; 72 | min-width: 10px; 73 | padding: 3px 7px; 74 | font-size: 12px; 75 | font-weight: bold; 76 | line-height: 1; 77 | color: #fff; 78 | text-align: center; 79 | white-space: nowrap; 80 | vertical-align: middle; 81 | background-color: #777; 82 | border-radius: 10px; 83 | } 84 | 85 | .ng-tooltip { 86 | position: absolute; 87 | max-width: 150px; 88 | font-size: 14px; 89 | text-align: center; 90 | color: #f8f8f2; 91 | padding: 3px 8px; 92 | background: #282a36; 93 | border-radius: 4px; 94 | z-index: 1000; 95 | opacity: 0; 96 | } 97 | 98 | .ng-tooltip:after { 99 | content: ""; 100 | position: absolute; 101 | border-style: solid; 102 | } 103 | 104 | .ng-tooltip-top:after { 105 | top: 100%; 106 | left: 50%; 107 | margin-left: -5px; 108 | border-width: 5px; 109 | border-color: black transparent transparent transparent; 110 | } 111 | 112 | .ng-tooltip-bottom:after { 113 | bottom: 100%; 114 | left: 50%; 115 | margin-left: -5px; 116 | border-width: 5px; 117 | border-color: transparent transparent black transparent; 118 | } 119 | 120 | .ng-tooltip-left:after { 121 | top: 50%; 122 | left: 100%; 123 | margin-top: -5px; 124 | border-width: 5px; 125 | border-color: transparent transparent transparent black; 126 | } 127 | 128 | .ng-tooltip-right:after { 129 | top: 50%; 130 | right: 100%; 131 | margin-top: -5px; 132 | border-width: 5px; 133 | border-color: transparent black transparent transparent; 134 | } 135 | 136 | .ng-tooltip-show { 137 | opacity: 1; 138 | } -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "types": [] 7 | }, 8 | "exclude": [ 9 | "src/test.ts", 10 | "**/*.spec.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "types": [ 7 | "jasmine", 8 | "node" 9 | ] 10 | }, 11 | "files": [ 12 | "test.ts", 13 | "polyfills.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | false, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | false, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2017", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-shadowed-variable": true, 69 | "no-string-literal": false, 70 | "no-string-throw": true, 71 | "no-switch-case-fall-through": true, 72 | "no-trailing-whitespace": true, 73 | "no-unnecessary-initializer": true, 74 | "no-unused-expression": true, 75 | "no-use-before-declare": true, 76 | "no-var-keyword": true, 77 | "object-literal-sort-keys": false, 78 | "one-line": [ 79 | true, 80 | "check-open-brace", 81 | "check-catch", 82 | "check-else", 83 | "check-whitespace" 84 | ], 85 | "prefer-const": true, 86 | "quotemark": [ 87 | false, 88 | "single" 89 | ], 90 | "radix": true, 91 | "semicolon": [ 92 | true, 93 | "always" 94 | ], 95 | "triple-equals": [ 96 | true, 97 | "allow-null-check" 98 | ], 99 | "typedef-whitespace": [ 100 | true, 101 | { 102 | "call-signature": "nospace", 103 | "index-signature": "nospace", 104 | "parameter": "nospace", 105 | "property-declaration": "nospace", 106 | "variable-declaration": "nospace" 107 | } 108 | ], 109 | "unified-signatures": true, 110 | "variable-name": false, 111 | "whitespace": [ 112 | true, 113 | "check-branch", 114 | "check-decl", 115 | "check-operator", 116 | "check-separator", 117 | "check-type" 118 | ], 119 | "no-output-on-prefix": true, 120 | "use-input-property-decorator": true, 121 | "use-output-property-decorator": true, 122 | "use-host-property-decorator": true, 123 | "no-input-rename": true, 124 | "no-output-rename": true, 125 | "use-life-cycle-interface": true, 126 | "use-pipe-transform-interface": true, 127 | "component-class-suffix": true, 128 | "directive-class-suffix": true 129 | } 130 | } 131 | --------------------------------------------------------------------------------