├── .editorconfig ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── examples ├── sticky-table-column │ ├── bs-config.json │ ├── karma-test-shim.js │ ├── karma.conf.js │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── app.component.js │ │ │ ├── app.component.spec.js │ │ │ ├── app.component.ts │ │ │ ├── app.html │ │ │ ├── app.module.js │ │ │ ├── app.module.ts │ │ │ ├── sticky.td.left.directive.js │ │ │ ├── sticky.td.left.directive.ts │ │ │ └── sticky_td_left.directive.js │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.js │ │ ├── main.ts │ │ ├── styles.css │ │ ├── systemjs-angular-loader.js │ │ ├── systemjs.config.js │ │ └── tsconfig.json │ └── tslint.json └── sticky-table-head │ ├── package.json │ └── src │ ├── main.component.ts │ └── sticky-thead-top │ ├── sticky_thead_top.directive.ts │ └── sticky_thead_top.module.ts ├── karma-test-shim.js ├── karma.conf.js ├── package.json ├── src ├── app │ ├── dom.utils.ts │ ├── sticky.element.directive.spec.ts │ ├── sticky.element.directive.ts │ └── sticky.element.module.ts ├── main.ts ├── systemjs-angular-loader.js ├── systemjs.config.js └── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | 11 | [*.md] 12 | max_line_length = 0 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | jspm_packages 4 | npm-debug.log 5 | debug.log 6 | src/**/*.js 7 | !src/systemjs.config.extras.js 8 | !src/systemjs.config.js 9 | !src/systemjs-angular-loader.js 10 | *.js.map 11 | e2e/**/*.js 12 | e2e/**/*.js.map 13 | _test-output 14 | _temp 15 | .vscode -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution, 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2017 Google Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Angular Sticky Element 2 | ====================== 3 | A dynamic element that'll stick to walls, ceilings, and your heart. 4 | 5 | Usage 6 | ----- 7 | Add the package to your dependencies. 8 | 9 | `npm install --save angular-sticky-element` 10 | 11 | Compile the library 12 | 1. Download or clone this repository. 13 | 2. Link the included module with your angular project. 14 | 3. Create a create a sticky-element of your own by subclassing the 15 | sticky_element_directive.ts, or use one of the included ones. 16 | 4. To include a sticky-element, use directive syntax in one of your templates. 17 | To see required arguments, refer to the self-documenting code+comments 18 | inside sticky_element_directive.ts. 19 | 20 | Create your own 21 | --------------- 22 | 1. Subclass sticky_element_directive.ts. 23 | 2. Most of the behavior of a sticky_x.ts can be defined by the two handle 24 | scroll events (horizontal+vertical). 25 | 3. Refer to existing subclasses for behavior hints. 26 | 4. When you're done with your new sticky element, add it to the module so the 27 | rest of your project can see it. 28 | -------------------------------------------------------------------------------- /examples/sticky-table-column/bs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "baseDir": "src", 4 | "routes": { 5 | "/node_modules": "node_modules" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/sticky-table-column/karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // /*global jasmine, __karma__, window*/ 2 | Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing. 3 | 4 | // Uncomment to get full stacktrace output. Sometimes helpful, usually not. 5 | // Error.stackTraceLimit = Infinity; // 6 | 7 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 8 | 9 | // builtPaths: root paths for output ("built") files 10 | // get from karma.config.js, then prefix with '/base/' (default is 'src/') 11 | var builtPaths = (__karma__.config.builtPaths || ['src/']) 12 | .map(function(p) { return '/base/'+p;}); 13 | 14 | __karma__.loaded = function () { }; 15 | 16 | function isJsFile(path) { 17 | return path.slice(-3) == '.js'; 18 | } 19 | 20 | function isSpecFile(path) { 21 | return /\.spec\.(.*\.)?js$/.test(path); 22 | } 23 | 24 | // Is a "built" file if is JavaScript file in one of the "built" folders 25 | function isBuiltFile(path) { 26 | return isJsFile(path) && 27 | builtPaths.reduce(function(keep, bp) { 28 | return keep || (path.substr(0, bp.length) === bp); 29 | }, false); 30 | } 31 | 32 | var allSpecFiles = Object.keys(window.__karma__.files) 33 | .filter(isSpecFile) 34 | .filter(isBuiltFile); 35 | 36 | System.config({ 37 | // Base URL for System.js calls. 'base/' is where Karma serves files from. 38 | baseURL: 'base/src', 39 | // Extend usual application package list with test folder 40 | packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } }, 41 | 42 | // Assume npm: is set in `paths` in systemjs.config 43 | // Map the angular testing umd bundles 44 | map: { 45 | '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', 46 | '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', 47 | '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js', 48 | '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js', 49 | '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', 50 | '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js', 51 | '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js', 52 | '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js', 53 | }, 54 | }); 55 | 56 | System.import('systemjs.config.js') 57 | .then(initTestBed) 58 | .then(initTesting); 59 | 60 | function initTestBed(){ 61 | return Promise.all([ 62 | System.import('@angular/core/testing'), 63 | System.import('@angular/platform-browser-dynamic/testing') 64 | ]) 65 | 66 | .then(function (providers) { 67 | var coreTesting = providers[0]; 68 | var browserTesting = providers[1]; 69 | 70 | coreTesting.TestBed.initTestEnvironment( 71 | browserTesting.BrowserDynamicTestingModule, 72 | browserTesting.platformBrowserDynamicTesting()); 73 | }) 74 | } 75 | 76 | // Import all spec files and start karma 77 | function initTesting () { 78 | return Promise.all( 79 | allSpecFiles.map(function (moduleName) { 80 | return System.import(moduleName); 81 | }) 82 | ) 83 | .then(__karma__.start, __karma__.error); 84 | } 85 | -------------------------------------------------------------------------------- /examples/sticky-table-column/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | 3 | var appBase = 'src/'; // transpiled app JS and map files 4 | var appSrcBase = appBase; // app source TS files 5 | 6 | // Testing helpers (optional) are conventionally in a folder called `testing` 7 | var testingBase = 'testing/'; // transpiled test JS and map files 8 | var testingSrcBase = 'testing/'; // test source TS files 9 | 10 | config.set({ 11 | basePath: '', 12 | frameworks: ['jasmine'], 13 | 14 | plugins: [ 15 | require('karma-jasmine'), 16 | require('karma-chrome-launcher'), 17 | require('karma-jasmine-html-reporter') 18 | ], 19 | 20 | client: { 21 | builtPaths: [appBase, testingBase], // add more spec base paths as needed 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | 25 | customLaunchers: { 26 | // From the CLI. Not used here but interesting 27 | // chrome setup for travis CI using chromium 28 | Chrome_travis_ci: { 29 | base: 'Chrome', 30 | flags: ['--no-sandbox'] 31 | } 32 | }, 33 | 34 | files: [ 35 | // System.js for module loading 36 | 'node_modules/systemjs/dist/system.src.js', 37 | 38 | // Polyfills 39 | 'node_modules/core-js/client/shim.js', 40 | 41 | // zone.js 42 | 'node_modules/zone.js/dist/zone.js', 43 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 44 | 'node_modules/zone.js/dist/proxy.js', 45 | 'node_modules/zone.js/dist/sync-test.js', 46 | 'node_modules/zone.js/dist/jasmine-patch.js', 47 | 'node_modules/zone.js/dist/async-test.js', 48 | 'node_modules/zone.js/dist/fake-async-test.js', 49 | 50 | // RxJs 51 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 52 | { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, 53 | 54 | // Paths loaded via module imports: 55 | // Angular itself 56 | { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false }, 57 | { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false }, 58 | 59 | { pattern: appBase + '/systemjs.config.js', included: false, watched: false }, 60 | 'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels 61 | 62 | // transpiled application & spec code paths loaded via module imports 63 | { pattern: appBase + '**/*.js', included: false, watched: true }, 64 | { pattern: testingBase + '**/*.js', included: false, watched: true }, 65 | 66 | 67 | // Asset (HTML & CSS) paths loaded via Angular's component compiler 68 | // (these paths need to be rewritten, see proxies section) 69 | { pattern: appBase + '**/*.html', included: false, watched: true }, 70 | { pattern: appBase + '**/*.css', included: false, watched: true }, 71 | 72 | // Paths for debugging with source maps in dev tools 73 | { pattern: appBase + '**/*.ts', included: false, watched: false }, 74 | { pattern: appBase + '**/*.js.map', included: false, watched: false }, 75 | { pattern: testingSrcBase + '**/*.ts', included: false, watched: false }, 76 | { pattern: testingBase + '**/*.js.map', included: false, watched: false} 77 | ], 78 | 79 | // Proxied base paths for loading assets 80 | proxies: { 81 | // required for modules fetched by SystemJS 82 | '/base/src/node_modules/': '/base/node_modules/' 83 | }, 84 | 85 | exclude: [], 86 | preprocessors: {}, 87 | reporters: ['progress', 'kjhtml'], 88 | 89 | port: 9876, 90 | colors: true, 91 | logLevel: config.LOG_INFO, 92 | autoWatch: true, 93 | browsers: ['Chrome'], 94 | singleRun: false 95 | }) 96 | } 97 | -------------------------------------------------------------------------------- /examples/sticky-table-column/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-sticky-element-example-sticky-table-column", 3 | "version": "1.0.2", 4 | "author": "Brandon Wylie", 5 | "description": "An example to help you get started with angular-sticky-element", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/google/angular-sticky-element.git" 9 | }, 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/google/angular-sticky-element/issues" 13 | }, 14 | "homepage": "https://github.com/google/angular-sticky-element#readme", 15 | "keywords": [ 16 | "angular2", 17 | "angular", 18 | "typescript", 19 | "node" 20 | ], 21 | "scripts": { 22 | "build": "tsc -p src/", 23 | "build:watch": "tsc -p src/ -w", 24 | "build:e2e": "tsc -p e2e/", 25 | "serve": "lite-server -c=bs-config.json", 26 | "serve:e2e": "lite-server -c=bs-config.e2e.json", 27 | "prestart": "npm run build", 28 | "start": "concurrently \"npm run build:watch\" \"npm run serve\"", 29 | "pree2e": "npm run build:e2e", 30 | "e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first", 31 | "preprotractor": "webdriver-manager update", 32 | "protractor": "protractor protractor.config.js", 33 | "pretest": "npm run build", 34 | "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"", 35 | "pretest:once": "npm run build", 36 | "test:once": "karma start karma.conf.js --single-run", 37 | "lint": "tslint ./src/**/*.ts -t verbose" 38 | }, 39 | "dependencies": { 40 | "@angular/common": "~4.0.0", 41 | "@angular/compiler": "~4.0.0", 42 | "@angular/core": "~4.0.0", 43 | "@angular/forms": "~4.0.0", 44 | "@angular/http": "~4.0.0", 45 | "@angular/platform-browser": "~4.0.0", 46 | "@angular/platform-browser-dynamic": "~4.0.0", 47 | "@angular/router": "~4.0.0", 48 | "angular-in-memory-web-api": "~0.3.0", 49 | "angular-sticky-element": "^1.0.3", 50 | "core-js": "^2.4.1", 51 | "rxjs": "5.0.1", 52 | "systemjs": "0.19.40", 53 | "zone.js": "^0.8.4" 54 | }, 55 | "devDependencies": { 56 | "concurrently": "^3.2.0", 57 | "lite-server": "^2.2.2", 58 | "typescript": "~2.1.0", 59 | "canonical-path": "0.0.2", 60 | "tslint": "^3.15.1", 61 | "lodash": "^4.16.4", 62 | "jasmine-core": "~2.4.1", 63 | "karma": "^1.3.0", 64 | "karma-chrome-launcher": "^2.0.0", 65 | "karma-cli": "^1.0.1", 66 | "karma-jasmine": "^1.0.2", 67 | "karma-jasmine-html-reporter": "^0.2.2", 68 | "protractor": "~4.0.14", 69 | "rimraf": "^2.5.4", 70 | "@types/node": "^6.0.46", 71 | "@types/jasmine": "2.5.36" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/app.component.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var core_1 = require("@angular/core"); 9 | // import {StickyTdLeftDirective} from './sticky.td.left.directive'; 10 | var AppComponent = (function () { 11 | function AppComponent() { 12 | this.name = 'Angular'; 13 | } 14 | return AppComponent; 15 | }()); 16 | AppComponent = __decorate([ 17 | core_1.Component({ 18 | selector: 'my-app', 19 | templateUrl: 'app/app.html' 20 | }) 21 | ], AppComponent); 22 | exports.AppComponent = AppComponent; 23 | //# sourceMappingURL=app.component.js.map -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/app.component.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var app_component_1 = require("./app.component"); 3 | var testing_1 = require("@angular/core/testing"); 4 | var platform_browser_1 = require("@angular/platform-browser"); 5 | describe('AppComponent', function () { 6 | var de; 7 | var comp; 8 | var fixture; 9 | beforeEach(testing_1.async(function () { 10 | testing_1.TestBed.configureTestingModule({ 11 | declarations: [app_component_1.AppComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | beforeEach(function () { 16 | fixture = testing_1.TestBed.createComponent(app_component_1.AppComponent); 17 | comp = fixture.componentInstance; 18 | de = fixture.debugElement.query(platform_browser_1.By.css('h1')); 19 | }); 20 | it('should create component', function () { return expect(comp).toBeDefined(); }); 21 | it('should have expected

text', function () { 22 | fixture.detectChanges(); 23 | var h1 = de.nativeElement; 24 | expect(h1.innerText).toMatch(/angular/i, '

should say something about "Angular"'); 25 | }); 26 | }); 27 | //# sourceMappingURL=app.component.spec.js.map -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | // import {StickyTdLeftDirective} from './sticky.td.left.directive'; 3 | 4 | @Component({ 5 | selector: 'my-app', 6 | templateUrl: 'app/app.html' 7 | }) 8 | export class AppComponent { name = 'Angular'; } 9 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/app.html: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
scrolldescriptiona third thinga fourth thinga fifth thingand anotha oneand anotha 1& anotha one& anotha 1& anothaaaah 1
rightdescriptiona third thinga fourth thinga fifth thingand anotha oneand anotha 1& anotha one& anotha 1& anothaaaah 1
pleasedescriptiona third thinga fourth thinga fifth thingand anotha oneand anotha 1& anotha one& anotha 1& anothaaaah 1
thanks!descriptiona third thinga fourth thinga fifth thingand anotha oneand anotha 1& anotha one& anotha 1& anothaaaah 1
65 | 66 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/app.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var core_1 = require("@angular/core"); 9 | var platform_browser_1 = require("@angular/platform-browser"); 10 | var app_component_1 = require("./app.component"); 11 | var angular_sticky_element_1 = require("angular-sticky-element"); 12 | var AppModule = (function () { 13 | function AppModule() { 14 | } 15 | return AppModule; 16 | }()); 17 | AppModule = __decorate([ 18 | core_1.NgModule({ 19 | imports: [platform_browser_1.BrowserModule, angular_sticky_element_1.StickyElementModule], 20 | declarations: [app_component_1.AppComponent], 21 | bootstrap: [app_component_1.AppComponent] 22 | }) 23 | ], AppModule); 24 | exports.AppModule = AppModule; 25 | //# sourceMappingURL=app.module.js.map -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | import {StickyElementModule} from 'angular-sticky-element'; 7 | 8 | @NgModule({ 9 | imports: [ BrowserModule, StickyElementModule ], 10 | declarations: [ AppComponent ], 11 | bootstrap: [ AppComponent ] 12 | }) 13 | export class AppModule { } 14 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/sticky.td.left.directive.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | "use strict"; 17 | var __extends = (this && this.__extends) || function (d, b) { 18 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 19 | function __() { this.constructor = d; } 20 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 21 | }; 22 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 23 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 24 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 25 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 26 | return c > 3 && r && Object.defineProperty(target, key, r), r; 27 | }; 28 | var core_1 = require("@angular/core"); 29 | var sticky_element_directive_1 = require("../../../../src/app/sticky.element.directive"); 30 | var StickyTdLeftDirective = (function (_super) { 31 | __extends(StickyTdLeftDirective, _super); 32 | function StickyTdLeftDirective() { 33 | return _super !== null && _super.apply(this, arguments) || this; 34 | } 35 | StickyTdLeftDirective.prototype.handleHorizontalScrollEvent = function (event) { 36 | console.log("got horizontal scroll event"); 37 | var leftOffset = event.target.scrollLeft; 38 | this.elements.forEach(function (el) { 39 | el.style.left = leftOffset + 'px'; 40 | }); 41 | }; 42 | return StickyTdLeftDirective; 43 | }(sticky_element_directive_1.StickyElementDirective)); 44 | StickyTdLeftDirective = __decorate([ 45 | core_1.Directive({ selector: '[sticky-td-left]' }) 46 | ], StickyTdLeftDirective); 47 | exports.StickyTdLeftDirective = StickyTdLeftDirective; 48 | //# sourceMappingURL=sticky.td.left.directive.js.map -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/sticky.td.left.directive.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import {Directive} from '@angular/core'; 18 | 19 | import {StickyElementDirective} from '../../../../src/app/sticky.element.directive'; 20 | 21 | @Directive({selector: '[sticky-td-left]'}) 22 | export class StickyTdLeftDirective extends StickyElementDirective { 23 | handleHorizontalScrollEvent(event: UIEvent): void { 24 | console.log("got horizontal scroll event"); 25 | const leftOffset = (event.target as HTMLElement).scrollLeft; 26 | this.elements.forEach((el: HTMLElement) => { 27 | el.style.left = leftOffset + 'px'; 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/app/sticky_td_left.directive.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | "use strict"; 17 | var __extends = (this && this.__extends) || function (d, b) { 18 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 19 | function __() { this.constructor = d; } 20 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 21 | }; 22 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 23 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 24 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 25 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 26 | return c > 3 && r && Object.defineProperty(target, key, r), r; 27 | }; 28 | var core_1 = require("@angular/core"); 29 | var sticky_element_directive_1 = require("./sticky_element_directive"); 30 | var StickyTdLeftDirective = (function (_super) { 31 | __extends(StickyTdLeftDirective, _super); 32 | function StickyTdLeftDirective() { 33 | return _super !== null && _super.apply(this, arguments) || this; 34 | } 35 | StickyTdLeftDirective.prototype.handleHorizontalScrollEvent = function (event) { 36 | var leftOffset = event.target.scrollLeft; 37 | this.elements.forEach(function (el) { 38 | el.style.left = leftOffset + 'px'; 39 | }); 40 | }; 41 | return StickyTdLeftDirective; 42 | }(sticky_element_directive_1.StickyElementDirective)); 43 | StickyTdLeftDirective = __decorate([ 44 | core_1.Directive({ selector: '[sticky-td-left]' }) 45 | ], StickyTdLeftDirective); 46 | exports.StickyTdLeftDirective = StickyTdLeftDirective; 47 | //# sourceMappingURL=sticky_td_left.directive.js.map -------------------------------------------------------------------------------- /examples/sticky-table-column/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/angular-sticky-element/773c7851282409d3a52d80778cfef905fc171f50/examples/sticky-table-column/src/favicon.ico -------------------------------------------------------------------------------- /examples/sticky-table-column/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Angular QuickStart 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | Loading AppComponent content here ... 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var platform_browser_dynamic_1 = require("@angular/platform-browser-dynamic"); 3 | var app_module_1 = require("./app/app.module"); 4 | platform_browser_dynamic_1.platformBrowserDynamic().bootstrapModule(app_module_1.AppModule); 5 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /examples/sticky-table-column/src/main.ts: -------------------------------------------------------------------------------- 1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 2 | 3 | import { AppModule } from './app/app.module'; 4 | 5 | platformBrowserDynamic().bootstrapModule(AppModule); 6 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/styles.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: #369; 3 | font-family: Arial, Helvetica, sans-serif; 4 | font-size: 250%; 5 | } 6 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/systemjs-angular-loader.js: -------------------------------------------------------------------------------- 1 | var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm; 2 | var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g; 3 | var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g; 4 | 5 | module.exports.translate = function(load){ 6 | if (load.source.indexOf('moduleId') != -1) return load; 7 | 8 | var url = document.createElement('a'); 9 | url.href = load.address; 10 | 11 | var basePathParts = url.pathname.split('/'); 12 | 13 | basePathParts.pop(); 14 | var basePath = basePathParts.join('/'); 15 | 16 | var baseHref = document.createElement('a'); 17 | baseHref.href = this.baseURL; 18 | baseHref = baseHref.pathname; 19 | 20 | if (!baseHref.startsWith('/base/')) { // it is not karma 21 | basePath = basePath.replace(baseHref, ''); 22 | } 23 | 24 | load.source = load.source 25 | .replace(templateUrlRegex, function(match, quote, url){ 26 | var resolvedUrl = url; 27 | 28 | if (url.startsWith('.')) { 29 | resolvedUrl = basePath + url.substr(1); 30 | } 31 | 32 | return 'templateUrl: "' + resolvedUrl + '"'; 33 | }) 34 | .replace(stylesRegex, function(match, relativeUrls) { 35 | var urls = []; 36 | 37 | while ((match = stringRegex.exec(relativeUrls)) !== null) { 38 | if (match[2].startsWith('.')) { 39 | urls.push('"' + basePath + match[2].substr(1) + '"'); 40 | } else { 41 | urls.push('"' + match[2] + '"'); 42 | } 43 | } 44 | 45 | return "styleUrls: [" + urls.join(', ') + "]"; 46 | }); 47 | 48 | return load; 49 | }; 50 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular samples 3 | * Adjust as necessary for your application needs. 4 | */ 5 | (function (global) { 6 | System.config({ 7 | paths: { 8 | // paths serve as alias 9 | 'npm:': 'node_modules/' 10 | }, 11 | // map tells the System loader where to look for things 12 | map: { 13 | // our app is within the app folder 14 | 'app': 'app', 15 | 16 | // angular bundles 17 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/http': 'npm:@angular/http/bundles/http.umd.js', 23 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 24 | '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 25 | 26 | // other libraries 27 | 'rxjs': 'npm:rxjs', 28 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js' 29 | }, 30 | // packages tells the System loader how to load when no filename and/or no extension 31 | packages: { 32 | app: { 33 | defaultExtension: 'js', 34 | meta: { 35 | './*.js': { 36 | loader: 'systemjs-angular-loader.js' 37 | } 38 | } 39 | }, 40 | rxjs: { 41 | defaultExtension: 'js' 42 | } 43 | } 44 | }); 45 | })(this); 46 | -------------------------------------------------------------------------------- /examples/sticky-table-column/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/sticky-table-column/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/sticky-table-head/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sticky-table-head", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "angular-sticky-element": "^1.0.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/sticky-table-head/src/main.component.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import { Component } from '@angular/core'; 18 | 19 | @Component({ 20 | template: "" 21 | + "exmaple
" 22 | }) 23 | export class MainComponent { } 24 | -------------------------------------------------------------------------------- /examples/sticky-table-head/src/sticky-thead-top/sticky_thead_top.directive.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import {Directive} from '@angular/core'; 18 | 19 | import {getOffset} from './dom_utils'; 20 | import {StickyElementDirective} from './sticky_element_directive'; 21 | 22 | @Directive({selector: '[sticky-thead-top]'}) 23 | export class StickyTheadTopDirective extends StickyElementDirective { 24 | handleVerticalScrollEvent(event: UIEvent): void { 25 | this.elements.forEach((el: HTMLElement) => { 26 | const topOffset = getOffset(el.parentNode as HTMLElement).top; 27 | 28 | if ((topOffset + el.clientHeight) < 0) { 29 | el.style.position = 'fixed'; 30 | el.style.top = 0 + 'px'; 31 | } else { 32 | el.style.position = 'relative'; 33 | el.style.top = 0 + 'px'; 34 | } 35 | }); 36 | } 37 | 38 | handleHorizontalScrollEvent(event: UIEvent): void { 39 | this.elements.forEach((el: HTMLElement) => { 40 | const leftOffset = (event.target as HTMLElement).scrollLeft; 41 | el.style.left = (-leftOffset) + 'px'; 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/sticky-table-head/src/sticky-thead-top/sticky_thead_top.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import {CommonModule} from '@angular/common'; 18 | import {NgModule} from '@angular/core'; 19 | 20 | import {StickyTheadTopDirective} from './sticky_thead_top.directive'; 21 | 22 | @NgModule({ 23 | imports: [CommonModule], 24 | declarations: [ 25 | StickyTheadTopDirective 26 | ], 27 | exports: [ 28 | StickyTheadTopDirective 29 | ], 30 | }) 31 | export class StickyTheadTopModule { 32 | } 33 | -------------------------------------------------------------------------------- /karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // /*global jasmine, __karma__, window*/ 2 | Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing. 3 | 4 | // Uncomment to get full stacktrace output. Sometimes helpful, usually not. 5 | // Error.stackTraceLimit = Infinity; // 6 | 7 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 8 | 9 | // builtPaths: root paths for output ("built") files 10 | // get from karma.config.js, then prefix with '/base/' (default is 'src/') 11 | var builtPaths = (__karma__.config.builtPaths || ['src/']) 12 | .map(function(p) { return '/base/'+p;}); 13 | 14 | __karma__.loaded = function () { }; 15 | 16 | function isJsFile(path) { 17 | return path.slice(-3) == '.js'; 18 | } 19 | 20 | function isSpecFile(path) { 21 | return /\.spec\.(.*\.)?js$/.test(path); 22 | } 23 | 24 | // Is a "built" file if is JavaScript file in one of the "built" folders 25 | function isBuiltFile(path) { 26 | return isJsFile(path) && 27 | builtPaths.reduce(function(keep, bp) { 28 | return keep || (path.substr(0, bp.length) === bp); 29 | }, false); 30 | } 31 | 32 | var allSpecFiles = Object.keys(window.__karma__.files) 33 | .filter(isSpecFile) 34 | .filter(isBuiltFile); 35 | 36 | System.config({ 37 | // Base URL for System.js calls. 'base/' is where Karma serves files from. 38 | baseURL: 'base/src', 39 | // Extend usual application package list with test folder 40 | packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } }, 41 | 42 | // Assume npm: is set in `paths` in systemjs.config 43 | // Map the angular testing umd bundles 44 | map: { 45 | '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js', 46 | '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js', 47 | '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js', 48 | '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js', 49 | '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', 50 | '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js', 51 | '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js', 52 | '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js', 53 | }, 54 | }); 55 | 56 | System.import('systemjs.config.js') 57 | .then(initTestBed) 58 | .then(initTesting); 59 | 60 | function initTestBed(){ 61 | return Promise.all([ 62 | System.import('@angular/core/testing'), 63 | System.import('@angular/platform-browser-dynamic/testing') 64 | ]) 65 | 66 | .then(function (providers) { 67 | var coreTesting = providers[0]; 68 | var browserTesting = providers[1]; 69 | 70 | coreTesting.TestBed.initTestEnvironment( 71 | browserTesting.BrowserDynamicTestingModule, 72 | browserTesting.platformBrowserDynamicTesting()); 73 | }) 74 | } 75 | 76 | // Import all spec files and start karma 77 | function initTesting () { 78 | return Promise.all( 79 | allSpecFiles.map(function (moduleName) { 80 | return System.import(moduleName); 81 | }) 82 | ) 83 | .then(__karma__.start, __karma__.error); 84 | } 85 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | 3 | var appBase = 'src/'; // transpiled app JS and map files 4 | var appSrcBase = appBase; // app source TS files 5 | 6 | // Testing helpers (optional) are conventionally in a folder called `testing` 7 | var testingBase = 'testing/'; // transpiled test JS and map files 8 | var testingSrcBase = 'testing/'; // test source TS files 9 | 10 | config.set({ 11 | basePath: '', 12 | frameworks: ['jasmine'], 13 | 14 | plugins: [ 15 | require('karma-jasmine'), 16 | require('karma-chrome-launcher'), 17 | require('karma-jasmine-html-reporter') 18 | ], 19 | 20 | client: { 21 | builtPaths: [appBase, testingBase], // add more spec base paths as needed 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | 25 | customLaunchers: { 26 | // From the CLI. Not used here but interesting 27 | // chrome setup for travis CI using chromium 28 | Chrome_travis_ci: { 29 | base: 'Chrome', 30 | flags: ['--no-sandbox'] 31 | } 32 | }, 33 | 34 | files: [ 35 | // System.js for module loading 36 | 'node_modules/systemjs/dist/system.src.js', 37 | 38 | // Polyfills 39 | 'node_modules/core-js/client/shim.js', 40 | 41 | // zone.js 42 | 'node_modules/zone.js/dist/zone.js', 43 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 44 | 'node_modules/zone.js/dist/proxy.js', 45 | 'node_modules/zone.js/dist/sync-test.js', 46 | 'node_modules/zone.js/dist/jasmine-patch.js', 47 | 'node_modules/zone.js/dist/async-test.js', 48 | 'node_modules/zone.js/dist/fake-async-test.js', 49 | 50 | // RxJs 51 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 52 | { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, 53 | 54 | // Paths loaded via module imports: 55 | // Angular itself 56 | { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false }, 57 | { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false }, 58 | 59 | { pattern: appBase + '/systemjs.config.js', included: false, watched: false }, 60 | 'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels 61 | 62 | // transpiled application & spec code paths loaded via module imports 63 | { pattern: appBase + '**/*.js', included: false, watched: true }, 64 | { pattern: testingBase + '**/*.js', included: false, watched: true }, 65 | 66 | 67 | // Asset (HTML & CSS) paths loaded via Angular's component compiler 68 | // (these paths need to be rewritten, see proxies section) 69 | { pattern: appBase + '**/*.html', included: false, watched: true }, 70 | { pattern: appBase + '**/*.css', included: false, watched: true }, 71 | 72 | // Paths for debugging with source maps in dev tools 73 | { pattern: appBase + '**/*.ts', included: false, watched: false }, 74 | { pattern: appBase + '**/*.js.map', included: false, watched: false }, 75 | { pattern: testingSrcBase + '**/*.ts', included: false, watched: false }, 76 | { pattern: testingBase + '**/*.js.map', included: false, watched: false} 77 | ], 78 | 79 | // Proxied base paths for loading assets 80 | proxies: { 81 | // required for modules fetched by SystemJS 82 | '/base/src/node_modules/': '/base/node_modules/' 83 | }, 84 | 85 | exclude: [], 86 | preprocessors: {}, 87 | reporters: ['progress', 'kjhtml'], 88 | 89 | port: 9876, 90 | colors: true, 91 | logLevel: config.LOG_INFO, 92 | autoWatch: true, 93 | browsers: ['Chrome'], 94 | singleRun: false 95 | }) 96 | } 97 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-sticky-element", 3 | "version": "1.0.3", 4 | "author": "Brandon Wylie", 5 | "description": "A dynamic element that'll stick to walls, ceilings, and your heart.", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/google/angular-sticky-element.git" 9 | }, 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/google/angular-sticky-element/issues" 13 | }, 14 | "homepage": "https://github.com/google/angular-sticky-element#readme", 15 | "keywords": [ 16 | "angular2", 17 | "angular", 18 | "typescript", 19 | "node" 20 | ], 21 | "scripts": { 22 | "build": "tsc -p src/", 23 | "build:watch": "tsc -p src/ -w", 24 | "build:e2e": "tsc -p e2e/", 25 | "serve": "lite-server -c=bs-config.json", 26 | "serve:e2e": "lite-server -c=bs-config.e2e.json", 27 | "prestart": "npm run build", 28 | "start": "concurrently \"npm run build:watch\" \"npm run serve\"", 29 | "pree2e": "npm run build:e2e", 30 | "e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first", 31 | "preprotractor": "webdriver-manager update", 32 | "protractor": "protractor protractor.config.js", 33 | "pretest": "npm run build", 34 | "test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"", 35 | "pretest:once": "npm run build", 36 | "test:once": "karma start karma.conf.js --single-run", 37 | "lint": "tslint ./src/**/*.ts -t verbose" 38 | }, 39 | "dependencies": { 40 | "@angular/common": "~4.0.0", 41 | "@angular/compiler": "~4.0.0", 42 | "@angular/core": "~4.0.0", 43 | "@angular/forms": "~4.0.0", 44 | "@angular/http": "~4.0.0", 45 | "@angular/platform-browser": "~4.0.0", 46 | "@angular/platform-browser-dynamic": "~4.0.0", 47 | "@angular/router": "~4.0.0", 48 | 49 | "angular-in-memory-web-api": "~0.3.0", 50 | "systemjs": "0.19.40", 51 | "core-js": "^2.4.1", 52 | "rxjs": "5.0.1", 53 | "zone.js": "^0.8.4" 54 | }, 55 | "devDependencies": { 56 | "concurrently": "^3.2.0", 57 | "lite-server": "^2.2.2", 58 | "typescript": "~2.1.0", 59 | 60 | "canonical-path": "0.0.2", 61 | "tslint": "^3.15.1", 62 | "lodash": "^4.16.4", 63 | "jasmine-core": "~2.4.1", 64 | "karma": "^1.3.0", 65 | "karma-chrome-launcher": "^2.0.0", 66 | "karma-cli": "^1.0.1", 67 | "karma-jasmine": "^1.0.2", 68 | "karma-jasmine-html-reporter": "^0.2.2", 69 | "protractor": "~4.0.14", 70 | "rimraf": "^2.5.4", 71 | 72 | "@types/node": "^6.0.46", 73 | "@types/jasmine": "2.5.36" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/app/dom.utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /** 18 | * Utilities for use in DOM related operations. 19 | */ 20 | 21 | /** 22 | * Gets the offset of the given element. 23 | * @param el - DOM element to be measured. 24 | * @returns the total offset of the element. 25 | */ 26 | export function getOffset(el: HTMLElement): {top: number, left: number} { 27 | let _x:number = 0; 28 | let _y:number = 0; 29 | while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) { 30 | _x += el.offsetLeft - el.scrollLeft; 31 | _y += el.offsetTop - el.scrollTop; 32 | el = el.offsetParent as HTMLElement; 33 | } 34 | return {top: _y, left: _x}; 35 | } 36 | -------------------------------------------------------------------------------- /src/app/sticky.element.directive.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import {Component} from '@angular/core'; 18 | import {By} from '@angular/platform-browser'; 19 | import {async, ComponentFixture, TestBed} from '@angular/core/testing'; 20 | 21 | import {StickyElementDirective} from './sticky.element.directive'; 22 | 23 | @Component({ 24 | selector: 'mock-sticky-element-component', 25 | template: ` 26 | 27 | 34 |
35 |
36 | 37 | hello 38 | hello 39 | hello 40 |
41 |
42 | 43 | ` 44 | }) 45 | class MockStickyElementComponent { 46 | } 47 | 48 | describe('Sticky Element Directive', () => { 49 | let fixture: ComponentFixture; 50 | let directive: StickyElementDirective; 51 | 52 | 53 | beforeEach(async(() => { 54 | TestBed 55 | .configureTestingModule({ 56 | declarations: [ 57 | MockStickyElementComponent, 58 | StickyElementDirective, 59 | ], 60 | }) 61 | .compileComponents(); 62 | fixture = TestBed.createComponent(MockStickyElementComponent); 63 | 64 | const directiveEl = 65 | fixture.debugElement.query(By.directive(StickyElementDirective)); 66 | expect(directiveEl).not.toBeNull(); 67 | 68 | directive = directiveEl.injector.get(StickyElementDirective); 69 | expect(directive).not.toBeNull(); 70 | })); 71 | 72 | it('Should query scroll parents', async(() => { 73 | fixture.detectChanges(); 74 | fixture.whenStable().then(() => { 75 | expect(directive.horizontalScrollSelector).toEqual('#main'); 76 | expect(directive.verticalScrollSelector).toEqual('#main'); 77 | expect(directive.horizontalScrollElement).not.toBeNull(); 78 | expect(directive.verticalScrollElement).not.toBeNull(); 79 | }); 80 | })); 81 | 82 | it('Should query elements to stick', async(() => { 83 | fixture.detectChanges(); 84 | fixture.whenStable().then(() => { 85 | expect(directive.elementSelector).toEqual('.stick-me'); 86 | expect(directive.elements).not.toBeNull(); 87 | expect(directive.elements.length).toEqual(3); 88 | }); 89 | })); 90 | }); -------------------------------------------------------------------------------- /src/app/sticky.element.directive.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import {Input} from '@angular/core'; 18 | import {AfterViewInit, Directive, ElementRef} from '@angular/core'; 19 | import {Observable} from 'rxjs/Rx'; 20 | import 'rxjs/Rx'; 21 | 22 | @Directive({selector: '[sticky-element]'}) 23 | export class StickyElementDirective implements AfterViewInit { 24 | /** 25 | * Required 26 | * Css selector for the element responsible for horizontal scrolling. 27 | */ 28 | @Input() horizontalScrollSelector: string; 29 | 30 | /** 31 | * Required 32 | * Css selector for the element responsible for vertical scrolling. 33 | */ 34 | @Input() verticalScrollSelector: string; 35 | 36 | /** 37 | * Required 38 | * Css selector for the element(s) that you want to stick to the side. 39 | */ 40 | @Input() elementSelector: string; 41 | 42 | // References to the DOM elements responsible for scrolling, controlling stick 43 | // behavior, and the elements to be stuck, respectively. 44 | horizontalScrollElement: HTMLElement; 45 | verticalScrollElement: HTMLElement; 46 | stickyParentElement: HTMLElement; 47 | elements: HTMLElement[]; 48 | 49 | /** 50 | * __Don't use this class__ 51 | * Use a derived class for any behavioral stuff. This class is purely common 52 | * functionality between different stick element directives. 53 | * @param element 54 | */ 55 | constructor(element: ElementRef) { 56 | this.stickyParentElement = element.nativeElement; 57 | } 58 | 59 | /** 60 | * Angular2 lifecycle hook to be called after the view side has been 61 | * initialized. This is necessary because we're querying for DOM elements. 62 | */ 63 | ngAfterViewInit(): void { 64 | const parentQuerySubscription = 65 | Observable.timer(100, 100).subscribe((_) => { 66 | if (!this.queryForScrollParent()) { 67 | console.error( 68 | 'Couldn\'t find scrollable parent for sticky element: ' + 69 | this.stickyParentElement.tagName + '\nretrying.'); 70 | return; 71 | } else { 72 | parentQuerySubscription.unsubscribe(); 73 | } 74 | }); 75 | const elementsQuerySubscription = 76 | Observable.timer(100, 100).subscribe((_) => { 77 | if (!this.queryForElements()) { 78 | console.error( 79 | 'Couldn\'t find sticky elements for parent: ' + 80 | this.stickyParentElement.tagName + '\nretrying.'); 81 | return; 82 | } else { 83 | elementsQuerySubscription.unsubscribe(); 84 | } 85 | }); 86 | } 87 | 88 | /** 89 | * Debounced scroll handler, to be implemented in derived classes. 90 | * @param event 91 | */ 92 | handleHorizontalScrollEvent(event: UIEvent): void {} 93 | 94 | /** 95 | * Debounced scroll handler, to be implemented in derived classes. 96 | * @param event 97 | */ 98 | handleVerticalScrollEvent(event: UIEvent): void {} 99 | 100 | /** 101 | * Query for element that will be scrolled. 102 | * @returns True if the query was successful. 103 | */ 104 | protected queryForScrollParent(): boolean { 105 | const document = this.stickyParentElement.ownerDocument; 106 | this.horizontalScrollElement = 107 | document.querySelector(this.horizontalScrollSelector) as HTMLElement; 108 | this.verticalScrollElement = 109 | document.querySelector(this.verticalScrollSelector) as HTMLElement; 110 | 111 | if (!this.horizontalScrollElement || !this.verticalScrollElement) { 112 | return false; 113 | } 114 | 115 | Observable.fromEvent(this.horizontalScrollElement, 'scroll') 116 | .debounce((_) => Observable.interval(50)) 117 | .subscribe((ev: UIEvent) => { 118 | this.handleHorizontalScrollEvent(ev); 119 | }); 120 | 121 | Observable.fromEvent(this.verticalScrollElement, 'scroll') 122 | .debounce((_) => Observable.interval(50)) 123 | .subscribe((ev: UIEvent) => { 124 | this.handleVerticalScrollEvent(ev); 125 | }); 126 | 127 | return true; 128 | } 129 | 130 | /** 131 | * Query for elements to be stuck. 132 | * @returns True if the query was successful. 133 | */ 134 | protected queryForElements(): boolean { 135 | const document = this.stickyParentElement.ownerDocument; 136 | const nodeList = document.querySelectorAll(this.elementSelector); 137 | 138 | this.elements = []; 139 | for (let i = 0; i < nodeList.length; i++) { 140 | const node = nodeList.item(i) as HTMLElement; 141 | this.elements.push(node as HTMLElement); 142 | } 143 | 144 | return this.elements != null; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/app/sticky.element.module.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | import {CommonModule} from '@angular/common'; 18 | import {NgModule} from '@angular/core'; 19 | 20 | import {StickyElementDirective} from './sticky.element.directive'; 21 | 22 | @NgModule({ 23 | imports: [CommonModule], 24 | declarations: [ 25 | StickyElementDirective 26 | ], 27 | exports: [ 28 | StickyElementDirective 29 | ], 30 | }) 31 | export class StickyElementModule { 32 | } 33 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export * from './public.api'; -------------------------------------------------------------------------------- /src/systemjs-angular-loader.js: -------------------------------------------------------------------------------- 1 | var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm; 2 | var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g; 3 | var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g; 4 | 5 | module.exports.translate = function(load){ 6 | if (load.source.indexOf('moduleId') != -1) return load; 7 | 8 | var url = document.createElement('a'); 9 | url.href = load.address; 10 | 11 | var basePathParts = url.pathname.split('/'); 12 | 13 | basePathParts.pop(); 14 | var basePath = basePathParts.join('/'); 15 | 16 | var baseHref = document.createElement('a'); 17 | baseHref.href = this.baseURL; 18 | baseHref = baseHref.pathname; 19 | 20 | if (!baseHref.startsWith('/base/')) { // it is not karma 21 | basePath = basePath.replace(baseHref, ''); 22 | } 23 | 24 | load.source = load.source 25 | .replace(templateUrlRegex, function(match, quote, url){ 26 | var resolvedUrl = url; 27 | 28 | if (url.startsWith('.')) { 29 | resolvedUrl = basePath + url.substr(1); 30 | } 31 | 32 | return 'templateUrl: "' + resolvedUrl + '"'; 33 | }) 34 | .replace(stylesRegex, function(match, relativeUrls) { 35 | var urls = []; 36 | 37 | while ((match = stringRegex.exec(relativeUrls)) !== null) { 38 | if (match[2].startsWith('.')) { 39 | urls.push('"' + basePath + match[2].substr(1) + '"'); 40 | } else { 41 | urls.push('"' + match[2] + '"'); 42 | } 43 | } 44 | 45 | return "styleUrls: [" + urls.join(', ') + "]"; 46 | }); 47 | 48 | return load; 49 | }; 50 | -------------------------------------------------------------------------------- /src/systemjs.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * System configuration for Angular samples 3 | * Adjust as necessary for your application needs. 4 | */ 5 | (function (global) { 6 | System.config({ 7 | paths: { 8 | // paths serve as alias 9 | 'npm:': 'node_modules/' 10 | }, 11 | // map tells the System loader where to look for things 12 | map: { 13 | // our app is within the app folder 14 | 'app': 'app', 15 | 16 | // angular bundles 17 | '@angular/core': 'npm:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'npm:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/http': 'npm:@angular/http/bundles/http.umd.js', 23 | '@angular/router': 'npm:@angular/router/bundles/router.umd.js', 24 | '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js', 25 | 26 | // other libraries 27 | 'rxjs': 'npm:rxjs', 28 | 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js' 29 | }, 30 | // packages tells the System loader how to load when no filename and/or no extension 31 | packages: { 32 | app: { 33 | defaultExtension: 'js', 34 | meta: { 35 | './*.js': { 36 | loader: 'systemjs-angular-loader.js' 37 | } 38 | } 39 | }, 40 | rxjs: { 41 | defaultExtension: 'js' 42 | } 43 | } 44 | }); 45 | })(this); 46 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "lib": [ "es2015", "dom" ], 10 | "noImplicitAny": true, 11 | "suppressImplicitAnyIndexErrors": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [ 12 | true, 13 | "spaces" 14 | ], 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": [ 18 | true, 19 | 140 20 | ], 21 | "member-access": false, 22 | "member-ordering": [ 23 | true, 24 | "static-before-instance", 25 | "variables-before-functions" 26 | ], 27 | "no-arg": true, 28 | "no-bitwise": true, 29 | "no-console": [ 30 | true, 31 | "debug", 32 | "info", 33 | "time", 34 | "timeEnd", 35 | "trace" 36 | ], 37 | "no-construct": true, 38 | "no-debugger": true, 39 | "no-duplicate-key": true, 40 | "no-duplicate-variable": true, 41 | "no-empty": false, 42 | "no-eval": true, 43 | "no-inferrable-types": true, 44 | "no-shadowed-variable": true, 45 | "no-string-literal": false, 46 | "no-switch-case-fall-through": true, 47 | "no-trailing-whitespace": true, 48 | "no-unused-expression": true, 49 | "no-unused-variable": true, 50 | "no-unreachable": true, 51 | "no-use-before-declare": true, 52 | "no-var-keyword": true, 53 | "object-literal-sort-keys": false, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "radix": true, 66 | "semicolon": [ 67 | "always" 68 | ], 69 | "triple-equals": [ 70 | true, 71 | "allow-null-check" 72 | ], 73 | "typedef-whitespace": [ 74 | true, 75 | { 76 | "call-signature": "nospace", 77 | "index-signature": "nospace", 78 | "parameter": "nospace", 79 | "property-declaration": "nospace", 80 | "variable-declaration": "nospace" 81 | } 82 | ], 83 | "variable-name": false, 84 | "whitespace": [ 85 | true, 86 | "check-branch", 87 | "check-decl", 88 | "check-operator", 89 | "check-separator", 90 | "check-type" 91 | ] 92 | } 93 | } 94 | --------------------------------------------------------------------------------