├── .editorconfig ├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── app ├── app.component.ts ├── build │ ├── app.js │ └── app.js.map ├── index.html ├── main.ts └── webpack.config.js ├── dist ├── index.d.ts ├── index.js ├── index.js.map ├── index.metadata.json ├── sticky.directive.d.ts ├── sticky.directive.js ├── sticky.directive.js.map ├── sticky.directive.metadata.json ├── sticky.module.d.ts ├── sticky.module.js ├── sticky.module.js.map ├── sticky.module.metadata.json ├── sticky.umd.js └── sticky.umd.js.map ├── package.json ├── src ├── index.ts ├── sticky.directive.ts └── sticky.module.ts ├── tsconfig.json ├── tsconfig.ngc.json └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | 9 | [*.{ts}] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.{json}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | typings 4 | .idea 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | test 3 | src 4 | build.sh 5 | tsconfig.json 6 | typings.json 7 | node_modules 8 | typings 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Allen Kim 2 | 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @ngui/sticky 2 | position: sticky implementaion in Angular2 3 | 4 | ## IMPORTANT: NOT-MATAINED. 5 | Accepting volunteers and ready to transfer ownership. 6 | 7 | 8 | 9 | [Demo](https://rawgit.com/ng2-ui/sticky/master/app/index.html) 10 | 11 | Plunker Example: https://plnkr.co/edit/ZKwAHN?p=preview 12 | 13 | ## Install 14 | 15 | 1. install @ngui/sticky 16 | 17 | $ npm install @ngui/sticky --save 18 | 19 | 2. If you are not using webpack, add `map` and `packages` to your `systemjs.config.js` 20 | 21 | map['@ngui/sticky'] = 'node_modules/@ngui/sticky/dist/sticky.umd.js'; 22 | 23 | 3. import NguiStickyModule to your AppModule 24 | 25 | import { NgModule } from '@angular/core'; 26 | import { FormsModule } from "@angular/forms"; 27 | import { BrowserModule } from '@angular/platform-browser'; 28 | import { AppComponent } from './app.component'; 29 | import { NguiStickyModule } from '@ngui/sticky'; 30 | 31 | @NgModule({ 32 | imports: [BrowserModule, FormsModule, NguiStickyModule], 33 | declarations: [AppComponent], 34 | bootstrap: [ AppComponent ] 35 | }) 36 | export class AppModule { } 37 | 38 | 39 | For full example, please check out `test` directory to see the example of; 40 | 41 | - `systemjs.config.js` 42 | - `app.module.ts` 43 | - and `app.component.ts`. 44 | 45 | ## Usage it in your code 46 | 47 |
48 |
One
49 |
After
50 |
Another
51 | 52 |
53 | 54 |


55 |
One
56 |
After
57 |
Another
58 |
59 | 60 | ## Offset after some tag or class 61 | 62 |
Header Container
63 | 64 |
65 | 66 | ## **ng2-ui** welcomes new members and contributors 67 | 68 | This module is only improved and maintained by contributors like you. 69 | 70 | As a contributor, it's NOT required to be skilled in Javascript nor Angular2. 71 | You are only to be open-minded and interested in helping others. 72 | As a contributor, you do following; 73 | 74 | * Updating README.md 75 | * Improving code comments 76 | * Answering issues and building FAQ 77 | * Documentation 78 | * Translation 79 | 80 | In result of your active contribution, you will be listed as a core contributor 81 | on https://ng2-ui.github.io, and a member of ng2-ui too. 82 | 83 | If you are interested in becoming a contributor and/or a member of ng-ui, 84 | please send me email to `allenhwkim AT gmail.com` with your github id. 85 | -------------------------------------------------------------------------------- /app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core' 2 | 3 | @Component({ 4 | selector: 'my-app', 5 | template: ` 6 |
7 | Some sticky after this. 8 |
9 |
10 |
11 |
float: right
12 |
13 | 14 |
15 |
float: left
17 |
18 | 19 |
20 |
One
After
Another
21 |
<div> tag in the middle
22 |


23 |
One
After
Another
24 |
25 |
26 |





















27 |





















28 | `, 29 | styles: [` 30 | .top-bar { 31 | position:fixed; 32 | background-color: #ccc; 33 | border: 1px solid #333; 34 | z-index: 1; 35 | } 36 | .top-bar:active { 37 | background-color: #fff 38 | } 39 | .container { 40 | margin: 0 auto; 41 | padding: 10px; 42 | min-width: 640px; 43 | max-width: 960px; 44 | background-color: #eee; 45 | } 46 | .container > div { 47 | margin: 20px; 48 | border: 1px solid #333; 49 | background-image: linear-gradient(rgba(255, 255, 255, 1) 50%, transparent 50%, transparent); 50 | background-size: 50px 50px; 51 | height: 500px; 52 | } 53 | .float-left { 54 | margin-top: 0; 55 | background-color: rgba(255,0,0, .2); 56 | } 57 | .float-right { 58 | background-color: rgba(0,255,0, .2); 59 | } 60 | .div-middle { 61 | background-color: rgba(0,0,255, .2); 62 | } 63 | div[ngui-sticky] { 64 | margin: 30px 10px 10px 10px; 65 | min-width: 400px; 66 | text-align: center; 67 | background: #333; 68 | color: #fff; 69 | border: 1px solid #333; 70 | padding: 10px; 71 | } 72 | .div-middle div[ngui-sticky] { 73 | margin: 0; 74 | } 75 | .div-middle div[ngui-sticky].ngui-sticky-stuck { 76 | background-color: #339; 77 | } 78 | .div-middle div[ngui-sticky].ngui-sticky-top { 79 | border: 2px solid #0f0; 80 | } 81 | .div-middle div[ngui-sticky].ngui-sticky-bottom { 82 | border: 2px solid #ff0; 83 | } 84 | @media print { 85 | .div-middle div[ngui-sticky] { 86 | visibility: hidden; 87 | height: 1px; 88 | } 89 | .ngui-sticky-filler { 90 | display: none; 91 | } 92 | } 93 | `] 94 | }) 95 | export class AppComponent { 96 | constructor() {} 97 | } 98 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Angular2 Npm Package Example 5 | 6 | 7 | 8 | Loading... 9 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/main.ts: -------------------------------------------------------------------------------- 1 | // polyfills, comment the following out for debugging purpose 2 | import 'core-js/es6'; 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | 6 | // The browser platform with a compiler 7 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 8 | 9 | // The app module 10 | import { NgModule } from '@angular/core'; 11 | import { BrowserModule } from '@angular/platform-browser'; 12 | import { FormsModule } from "@angular/forms"; 13 | 14 | import { AppComponent } from './app.component'; 15 | 16 | //noinspection TypeScriptCheckImport 17 | import {NguiStickyModule} from "@ngui/sticky"; 18 | 19 | @NgModule({ 20 | imports: [BrowserModule, FormsModule, NguiStickyModule], 21 | declarations: [AppComponent], 22 | bootstrap: [ AppComponent ] 23 | }) 24 | export class AppModule { } 25 | 26 | // Compile and launch the module 27 | platformBrowserDynamic().bootstrapModule(AppModule); 28 | 29 | -------------------------------------------------------------------------------- /app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | const config = { 5 | resolve: { 6 | extensions: ['.ts', '.webpack.js', '.web.js', '.js'], 7 | alias: { 8 | '@ngui/sticky': path.join(__dirname, '..', 'src', 'index') 9 | } 10 | }, 11 | devtool: 'source-map', 12 | entry: './app/main.ts', 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.ts$/, 17 | include: [ 18 | path.resolve(__dirname, '..', 'src'), 19 | path.resolve(__dirname, '..', 'app') 20 | ], 21 | use: [ 22 | 'ts-loader', 23 | 'angular2-template-loader' 24 | ], 25 | }, 26 | { test: /\.html$/, use: 'raw' } 27 | ] 28 | }, 29 | plugins: [], 30 | output: { 31 | path: `${__dirname}/build/`, 32 | publicPath: '/build/', 33 | filename: 'app.js' 34 | } 35 | }; 36 | 37 | if (process.env.NODE_ENV === 'prod') { 38 | config.plugins = [ 39 | new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) 40 | ]; 41 | config.module.rules.push({ 42 | test: /\.ts$/, loader: 'strip-loader?strip[]=debug,strip[]=console.log' 43 | }); 44 | } 45 | 46 | module.exports = config; 47 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { NguiStickyDirective } from "./sticky.directive"; 2 | import { NguiStickyModule } from "./sticky.module"; 3 | export { NguiStickyDirective, NguiStickyModule }; 4 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var sticky_directive_1 = require("./sticky.directive"); 4 | exports.NguiStickyDirective = sticky_directive_1.NguiStickyDirective; 5 | var sticky_module_1 = require("./sticky.module"); 6 | exports.NguiStickyModule = sticky_module_1.NguiStickyModule; 7 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,uDAAuD;AAG9C,8BAHD,sCAAmB,CAGC;AAF5B,iDAAiD;AAEnB,2BAFtB,gCAAgB,CAEsB","file":"index.js","sourceRoot":""} -------------------------------------------------------------------------------- /dist/index.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":3,"metadata":{"NguiStickyDirective":{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"},"NguiStickyModule":{"__symbolic":"reference","module":"./sticky.module","name":"NguiStickyModule"}}},{"__symbolic":"module","version":1,"metadata":{"NguiStickyDirective":{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"},"NguiStickyModule":{"__symbolic":"reference","module":"./sticky.module","name":"NguiStickyModule"}}}] -------------------------------------------------------------------------------- /dist/sticky.directive.d.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit, ElementRef, OnDestroy, Renderer2 } from '@angular/core'; 2 | export declare class NguiStickyDirective implements AfterViewInit, OnDestroy { 3 | protected renderer: Renderer2; 4 | stickyAfter: string; 5 | protected el: HTMLElement; 6 | protected parentEl: HTMLElement; 7 | protected fillerEl: HTMLElement; 8 | protected stickyAfterElement: HTMLElement; 9 | protected diff: any; 10 | protected original: any; 11 | protected STICKY_CLASSES: { 12 | STUCK: string; 13 | UNSTUCK: string; 14 | TOP: string; 15 | BOTTOM: string; 16 | FILLER: string; 17 | CONTAINER: string; 18 | }; 19 | constructor(el: ElementRef, renderer: Renderer2); 20 | ngAfterViewInit(): void; 21 | ngOnDestroy(): void; 22 | protected attach(): void; 23 | protected detach(): void; 24 | protected scrollHandler: () => void; 25 | } 26 | -------------------------------------------------------------------------------- /dist/sticky.directive.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var core_1 = require("@angular/core"); 4 | var utils_1 = require("@ngui/utils"); 5 | var NguiStickyDirective = /** @class */ (function () { 6 | function NguiStickyDirective(el, renderer) { 7 | var _this = this; 8 | this.renderer = renderer; 9 | this.STICKY_CLASSES = { 10 | STUCK: 'ngui-sticky-stuck', 11 | UNSTUCK: 'ngui-sticky-unstuck', 12 | TOP: 'ngui-sticky-top', 13 | BOTTOM: 'ngui-sticky-bottom', 14 | FILLER: 'ngui-sticky-filler', 15 | CONTAINER: 'ngui-sticky-container' 16 | }; 17 | this.scrollHandler = function () { 18 | var parentRect = _this.el.parentElement.getBoundingClientRect(); 19 | var bodyRect = document.body.getBoundingClientRect(); 20 | var stickyOffsetTop = _this.stickyAfterElement ? _this.stickyAfterElement.getBoundingClientRect().bottom : 0; 21 | var dynProps; 22 | if (_this.original.float === 'right') { 23 | var right = bodyRect.right - parentRect.right + _this.original.marginRight; 24 | dynProps = { right: right + 'px' }; 25 | } 26 | else if (_this.original.float === 'left') { 27 | var left = parentRect.left - bodyRect.left + _this.original.marginLeft; 28 | dynProps = { left: left + 'px' }; 29 | } 30 | else { 31 | dynProps = { width: parentRect.width + 'px' }; 32 | } 33 | if (_this.original.marginTop + _this.original.marginBottom + 34 | _this.original.boundingClientRect.height + stickyOffsetTop >= parentRect.bottom) { 35 | /** 36 | * sticky element reached to the bottom of the container 37 | */ 38 | var floatAdjustment = _this.original.float === 'right' ? { right: 0 } : 39 | _this.original.float === 'left' ? { left: 0 } : {}; 40 | Object.assign(_this.el.style, { 41 | position: 'absolute', 42 | float: 'none', 43 | top: 'inherit', 44 | bottom: 0 45 | }, dynProps, floatAdjustment); 46 | _this.renderer.removeClass(_this.el, _this.STICKY_CLASSES.STUCK); 47 | _this.renderer.removeClass(_this.el, _this.STICKY_CLASSES.TOP); 48 | _this.renderer.addClass(_this.el, _this.STICKY_CLASSES.UNSTUCK); 49 | _this.renderer.addClass(_this.el, _this.STICKY_CLASSES.BOTTOM); 50 | } 51 | else if (parentRect.top * -1 + _this.original.marginTop + stickyOffsetTop > _this.original.offsetTop) { 52 | /** 53 | * sticky element is in the middle of container 54 | */ 55 | // if not floating, add an empty filler element, since the original elements becames 'fixed' 56 | if (_this.original.float !== 'left' && _this.original.float !== 'right' && !_this.fillerEl) { 57 | _this.fillerEl = document.createElement('div'); 58 | _this.fillerEl.style.height = _this.el.offsetHeight + 'px'; 59 | _this.renderer.addClass(_this.fillerEl, _this.STICKY_CLASSES.FILLER); 60 | _this.parentEl.insertBefore(_this.fillerEl, _this.el); 61 | } 62 | Object.assign(_this.el.style, { 63 | position: 'fixed', 64 | float: 'none', 65 | top: stickyOffsetTop + 'px', 66 | bottom: 'inherit' 67 | }, dynProps); 68 | _this.renderer.removeClass(_this.el, _this.STICKY_CLASSES.UNSTUCK); 69 | _this.renderer.removeClass(_this.el, _this.STICKY_CLASSES.TOP); 70 | _this.renderer.removeClass(_this.el, _this.STICKY_CLASSES.BOTTOM); 71 | _this.renderer.addClass(_this.el, _this.STICKY_CLASSES.STUCK); 72 | } 73 | else { 74 | /** 75 | * sticky element is in the original position 76 | */ 77 | if (_this.fillerEl) { 78 | _this.parentEl.removeChild(_this.fillerEl); //IE11 does not work with el.remove() 79 | _this.fillerEl = undefined; 80 | } 81 | Object.assign(_this.el.style, { 82 | position: _this.original.position, 83 | float: _this.original.float, 84 | top: _this.original.top, 85 | bottom: _this.original.bottom, 86 | width: _this.original.width, 87 | left: _this.original.left 88 | }, dynProps); 89 | _this.renderer.removeClass(_this.el, _this.STICKY_CLASSES.STUCK); 90 | _this.renderer.removeClass(_this.el, _this.STICKY_CLASSES.BOTTOM); 91 | _this.renderer.addClass(_this.el, _this.STICKY_CLASSES.UNSTUCK); 92 | _this.renderer.addClass(_this.el, _this.STICKY_CLASSES.TOP); 93 | } 94 | }; 95 | this.el = this.el = el.nativeElement; 96 | this.parentEl = this.el.parentElement; 97 | } 98 | NguiStickyDirective.prototype.ngAfterViewInit = function () { 99 | this.el.style.boxSizing = 'border-box'; 100 | this.renderer.addClass(this.el, this.STICKY_CLASSES.UNSTUCK); 101 | this.renderer.addClass(this.el, this.STICKY_CLASSES.TOP); 102 | this.renderer.addClass(this.parentEl, this.STICKY_CLASSES.CONTAINER); 103 | if (this.stickyAfter) { 104 | this.stickyAfterElement = document.querySelector(this.stickyAfter); 105 | } 106 | // set the parent relatively positioned 107 | var allowedPositions = ['absolute', 'fixed', 'relative']; 108 | var parentElPosition = utils_1.computedStyle(this.parentEl, 'position'); 109 | if (allowedPositions.indexOf(parentElPosition) === -1) { 110 | this.parentEl.style.position = 'relative'; 111 | } 112 | this.diff = { 113 | top: this.el.offsetTop - this.parentEl.offsetTop, 114 | left: this.el.offsetLeft - this.parentEl.offsetLeft 115 | }; 116 | var elRect = this.el.getBoundingClientRect(); 117 | this.original = { 118 | boundingClientRect: elRect, 119 | position: utils_1.computedStyle(this.el, 'position'), 120 | float: utils_1.computedStyle(this.el, 'float'), 121 | top: utils_1.computedStyle(this.el, 'top'), 122 | bottom: utils_1.computedStyle(this.el, 'bottom'), 123 | left: utils_1.computedStyle(this.el, 'left'), 124 | width: utils_1.computedStyle(this.el, 'width'), 125 | offsetTop: this.el.offsetTop, 126 | offsetLeft: this.el.offsetLeft, 127 | marginTop: parseInt(utils_1.computedStyle(this.el, 'marginTop')), 128 | marginBottom: parseInt(utils_1.computedStyle(this.el, 'marginBottom')), 129 | marginLeft: parseInt(utils_1.computedStyle(this.el, 'marginLeft')), 130 | marginRight: parseInt(utils_1.computedStyle(this.el, 'marginLeft')) 131 | }; 132 | this.attach(); 133 | }; 134 | NguiStickyDirective.prototype.ngOnDestroy = function () { 135 | this.detach(); 136 | }; 137 | NguiStickyDirective.prototype.attach = function () { 138 | window.addEventListener('scroll', this.scrollHandler); 139 | window.addEventListener('resize', this.scrollHandler); 140 | }; 141 | NguiStickyDirective.prototype.detach = function () { 142 | window.removeEventListener('scroll', this.scrollHandler); 143 | window.removeEventListener('resize', this.scrollHandler); 144 | }; 145 | NguiStickyDirective.decorators = [ 146 | { type: core_1.Directive, args: [{ 147 | selector: '[ngui-sticky]' 148 | },] }, 149 | ]; 150 | /** @nocollapse */ 151 | NguiStickyDirective.ctorParameters = function () { return [ 152 | { type: core_1.ElementRef, }, 153 | { type: core_1.Renderer2, }, 154 | ]; }; 155 | NguiStickyDirective.propDecorators = { 156 | 'stickyAfter': [{ type: core_1.Input, args: ['sticky-after',] },], 157 | }; 158 | return NguiStickyDirective; 159 | }()); 160 | exports.NguiStickyDirective = NguiStickyDirective; 161 | //# sourceMappingURL=sticky.directive.js.map -------------------------------------------------------------------------------- /dist/sticky.directive.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/sticky.directive.ts"],"names":[],"mappings":"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","file":"sticky.directive.js","sourceRoot":""} -------------------------------------------------------------------------------- /dist/sticky.directive.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":3,"metadata":{"NguiStickyDirective":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Directive"},"arguments":[{"selector":"[ngui-sticky]"}]}],"members":{"stickyAfter":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Input"},"arguments":["sticky-after"]}]}],"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","module":"@angular/core","name":"ElementRef"},{"__symbolic":"reference","module":"@angular/core","name":"Renderer2"}]}],"ngAfterViewInit":[{"__symbolic":"method"}],"ngOnDestroy":[{"__symbolic":"method"}],"attach":[{"__symbolic":"method"}],"detach":[{"__symbolic":"method"}]}}}},{"__symbolic":"module","version":1,"metadata":{"NguiStickyDirective":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Directive"},"arguments":[{"selector":"[ngui-sticky]"}]}],"members":{"stickyAfter":[{"__symbolic":"property","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Input"},"arguments":["sticky-after"]}]}],"__ctor__":[{"__symbolic":"constructor","parameters":[{"__symbolic":"reference","module":"@angular/core","name":"ElementRef"},{"__symbolic":"reference","module":"@angular/core","name":"Renderer2"}]}],"ngAfterViewInit":[{"__symbolic":"method"}],"ngOnDestroy":[{"__symbolic":"method"}],"attach":[{"__symbolic":"method"}],"detach":[{"__symbolic":"method"}]}}}}] -------------------------------------------------------------------------------- /dist/sticky.module.d.ts: -------------------------------------------------------------------------------- 1 | import { NguiStickyDirective } from "./sticky.directive"; 2 | export { NguiStickyDirective }; 3 | export declare class NguiStickyModule { 4 | } 5 | -------------------------------------------------------------------------------- /dist/sticky.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var core_1 = require("@angular/core"); 4 | var forms_1 = require("@angular/forms"); 5 | var common_1 = require("@angular/common"); 6 | var sticky_directive_1 = require("./sticky.directive"); 7 | exports.NguiStickyDirective = sticky_directive_1.NguiStickyDirective; 8 | var NguiStickyModule = /** @class */ (function () { 9 | function NguiStickyModule() { 10 | } 11 | NguiStickyModule.decorators = [ 12 | { type: core_1.NgModule, args: [{ 13 | imports: [common_1.CommonModule, forms_1.FormsModule], 14 | declarations: [sticky_directive_1.NguiStickyDirective], 15 | exports: [sticky_directive_1.NguiStickyDirective] 16 | },] }, 17 | ]; 18 | /** @nocollapse */ 19 | NguiStickyModule.ctorParameters = function () { return []; }; 20 | return NguiStickyModule; 21 | }()); 22 | exports.NguiStickyModule = NguiStickyModule; 23 | //# sourceMappingURL=sticky.module.js.map -------------------------------------------------------------------------------- /dist/sticky.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/sticky.module.ts"],"names":[],"mappings":";;AAAA,sCAAyC;AACzC,wCAA6C;AAC7C,0CAAgD;AAEhD,uDAAuD;AAE9C,8BAFF,sCAAE,CAEA;AAGT;IAAA;IAUA,CAAC;IAVqC,2BAAU,GAA0B;QAC1E,EAAE,IAAI,EAAE,eAAQ,EAAE,IAAI,EAAE,CAAC;oBACvB,OAAO,EAAE,CAAE,qBAAY,EAAE,mBAAW,CAAE;oBACtC,YAAY,EAAE,CAAC,sCAAmB,CAAC;oBACnC,OAAO,EAAE,CAAE,sCAAmB,CAAE;iBACjC,EAAG,EAAE;KACL,CAAC;IACF,kBAAkB;IACX,+BAAc,GAAmE,cAAM,OAAA,EAC7F,EAD6F,CAC7F,CAAC;IACF,uBAAC;CAVD,AAUC,IAAA;AAVY,4CAAgB","file":"sticky.module.js","sourceRoot":""} -------------------------------------------------------------------------------- /dist/sticky.module.metadata.json: -------------------------------------------------------------------------------- 1 | [{"__symbolic":"module","version":3,"metadata":{"NguiStickyDirective":{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"},"NguiStickyModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule"},"arguments":[{"imports":[{"__symbolic":"reference","module":"@angular/common","name":"CommonModule"},{"__symbolic":"reference","module":"@angular/forms","name":"FormsModule"}],"declarations":[{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"}],"exports":[{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"}]}]}]}}},{"__symbolic":"module","version":1,"metadata":{"NguiStickyDirective":{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"},"NguiStickyModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule"},"arguments":[{"imports":[{"__symbolic":"reference","module":"@angular/common","name":"CommonModule"},{"__symbolic":"reference","module":"@angular/forms","name":"FormsModule"}],"declarations":[{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"}],"exports":[{"__symbolic":"reference","module":"./sticky.directive","name":"NguiStickyDirective"}]}]}]}}}] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ngui/sticky", 3 | "version": "0.6.3", 4 | "description": "CSS position: sticky implementation in Angular2", 5 | "license": "MIT", 6 | "main": "dist/sticky.umd.js", 7 | "module": "dist/index.js", 8 | "typings": "dist/index.d.ts", 9 | "scripts": { 10 | "start": "NODE_ENV=dev webpack-dev-server --quiet --port 9001 --content-base app --config app/webpack.config --open", 11 | "lint": "tslint 'src/**/*.ts' 'app/**/*.ts'", 12 | "clean": "rimraf dist", 13 | "build": "npm-run-all --serial clean build:ngc build:umd build:app", 14 | "build:ngc": "ngc -p tsconfig.ngc.json", 15 | "build:umd": "NODE_ENV=prod webpack", 16 | "build:app": "NODE_ENV=prod webpack --config app/webpack.config", 17 | "upgrade": "npm-check-updates --upgradeAll" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/ng2-ui/sticky.git" 22 | }, 23 | "author": "Allen Kim ", 24 | "bugs": { 25 | "url": "https://github.com/ng2-ui/sticky/issues" 26 | }, 27 | "homepage": "https://github.com/ng2-ui/sticky#readme", 28 | "dependencies": { 29 | "@ngui/utils": "^0.8.1" 30 | }, 31 | "devDependencies": { 32 | "@angular/common": "^4.0.3", 33 | "@angular/compiler": "^4.0.3", 34 | "@angular/compiler-cli": "^4.0.3", 35 | "@angular/core": "^4.0.3", 36 | "@angular/forms": "^4.0.3", 37 | "@angular/http": "^4.0.3", 38 | "@angular/platform-browser": "^4.0.3", 39 | "@angular/platform-browser-dynamic": "^4.0.3", 40 | "@angular/router": "^4.0.3", 41 | "@types/node": "^7.0.13", 42 | "angular2-template-loader": "^0.6.2", 43 | "codelyzer": "^3.0.0", 44 | "core-js": "^2.4.1", 45 | "loader-utils": "^1.1.0", 46 | "npm-check-updates": "^2.11.0", 47 | "npm-run-all": "^4.0.2", 48 | "raw-loader": "^0.5.1", 49 | "rimraf": "^2.6.1", 50 | "rxjs": "^5.3.0", 51 | "strip-loader": "^0.1.2", 52 | "ts-loader": "^2.0.3", 53 | "tslint": "^5.1.0", 54 | "typescript": "^2.2.2", 55 | "webpack": "^2.4.1", 56 | "webpack-dev-server": "^2.4.4", 57 | "zone.js": "^0.8.9" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import {NguiStickyDirective} from "./sticky.directive"; 2 | import {NguiStickyModule} from "./sticky.module"; 3 | 4 | export { NguiStickyDirective, NguiStickyModule }; 5 | 6 | -------------------------------------------------------------------------------- /src/sticky.directive.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { AfterViewInit, Directive, ElementRef, Input, OnDestroy, Renderer2 } from '@angular/core'; 4 | import { computedStyle } from '@ngui/utils'; 5 | 6 | @Directive({ 7 | selector: '[ngui-sticky]' 8 | }) 9 | export class NguiStickyDirective implements AfterViewInit, OnDestroy { 10 | @Input('sticky-after') public stickyAfter: string; // css selector to be sticky after 11 | 12 | protected el: HTMLElement; 13 | protected parentEl: HTMLElement; 14 | protected fillerEl: HTMLElement; 15 | protected stickyAfterElement: HTMLElement; 16 | 17 | protected diff: any; 18 | protected original: any; 19 | 20 | protected STICKY_CLASSES = { 21 | STUCK: 'ngui-sticky-stuck', 22 | UNSTUCK: 'ngui-sticky-unstuck', 23 | TOP: 'ngui-sticky-top', 24 | BOTTOM: 'ngui-sticky-bottom', 25 | FILLER: 'ngui-sticky-filler', 26 | CONTAINER: 'ngui-sticky-container' 27 | }; 28 | 29 | constructor(el: ElementRef, protected renderer: Renderer2) { 30 | this.el = this.el = el.nativeElement; 31 | this.parentEl = this.el.parentElement; 32 | } 33 | 34 | public ngAfterViewInit(): void { 35 | this.el.style.boxSizing = 'border-box'; 36 | this.renderer.addClass(this.el, this.STICKY_CLASSES.UNSTUCK); 37 | this.renderer.addClass(this.el, this.STICKY_CLASSES.TOP); 38 | this.renderer.addClass(this.parentEl, this.STICKY_CLASSES.CONTAINER); 39 | 40 | if (this.stickyAfter) { 41 | this.stickyAfterElement = document.querySelector(this.stickyAfter) as HTMLElement; 42 | } 43 | 44 | // set the parent relatively positioned 45 | let allowedPositions = ['absolute', 'fixed', 'relative']; 46 | let parentElPosition = computedStyle(this.parentEl, 'position'); 47 | if (allowedPositions.indexOf(parentElPosition) === -1) { //inherit, initial, unset 48 | this.parentEl.style.position = 'relative'; 49 | } 50 | 51 | this.diff = { 52 | top: this.el.offsetTop - this.parentEl.offsetTop, 53 | left: this.el.offsetLeft - this.parentEl.offsetLeft 54 | }; 55 | 56 | let elRect = this.el.getBoundingClientRect(); 57 | this.original = { 58 | boundingClientRect: elRect, 59 | position: computedStyle(this.el, 'position'), 60 | float: computedStyle(this.el, 'float'), 61 | top: computedStyle(this.el, 'top'), 62 | bottom: computedStyle(this.el, 'bottom'), 63 | left: computedStyle(this.el, 'left'), 64 | width: computedStyle(this.el, 'width'), 65 | offsetTop: this.el.offsetTop, 66 | offsetLeft: this.el.offsetLeft, 67 | marginTop: parseInt(computedStyle(this.el, 'marginTop')), 68 | marginBottom: parseInt(computedStyle(this.el, 'marginBottom')), 69 | marginLeft: parseInt(computedStyle(this.el, 'marginLeft')), 70 | marginRight: parseInt(computedStyle(this.el, 'marginLeft')) 71 | }; 72 | 73 | this.attach(); 74 | } 75 | 76 | public ngOnDestroy(): void { 77 | this.detach(); 78 | } 79 | 80 | protected attach(): void { 81 | window.addEventListener('scroll', this.scrollHandler); 82 | window.addEventListener('resize', this.scrollHandler); 83 | } 84 | 85 | protected detach(): void { 86 | window.removeEventListener('scroll', this.scrollHandler); 87 | window.removeEventListener('resize', this.scrollHandler); 88 | } 89 | 90 | protected scrollHandler = () => { 91 | let parentRect: ClientRect = this.el.parentElement.getBoundingClientRect(); 92 | let bodyRect: ClientRect = document.body.getBoundingClientRect(); 93 | let stickyOffsetTop = this.stickyAfterElement ? this.stickyAfterElement.getBoundingClientRect().bottom : 0; 94 | let dynProps; 95 | 96 | if (this.original.float === 'right') { 97 | let right = bodyRect.right - parentRect.right + this.original.marginRight; 98 | dynProps = { right: right + 'px' }; 99 | } else if (this.original.float === 'left') { 100 | let left = parentRect.left - bodyRect.left + this.original.marginLeft; 101 | dynProps = { left: left + 'px'}; 102 | } else { 103 | dynProps = {width: parentRect.width + 'px'}; 104 | } 105 | 106 | if (this.original.marginTop + this.original.marginBottom + 107 | this.original.boundingClientRect.height + stickyOffsetTop >= parentRect.bottom) { 108 | /** 109 | * sticky element reached to the bottom of the container 110 | */ 111 | let floatAdjustment = 112 | this.original.float === 'right' ? {right: 0} : 113 | this.original.float === 'left' ? {left: 0} : {}; 114 | Object.assign(this.el.style, { 115 | position: 'absolute', 116 | float: 'none', 117 | top: 'inherit', 118 | bottom: 0 119 | }, dynProps, floatAdjustment); 120 | this.renderer.removeClass(this.el, this.STICKY_CLASSES.STUCK); 121 | this.renderer.removeClass(this.el, this.STICKY_CLASSES.TOP); 122 | this.renderer.addClass(this.el, this.STICKY_CLASSES.UNSTUCK); 123 | this.renderer.addClass(this.el, this.STICKY_CLASSES.BOTTOM); 124 | } else if (parentRect.top * -1 + this.original.marginTop + stickyOffsetTop > this.original.offsetTop) { 125 | /** 126 | * sticky element is in the middle of container 127 | */ 128 | 129 | // if not floating, add an empty filler element, since the original elements becames 'fixed' 130 | if (this.original.float !== 'left' && this.original.float !== 'right' && !this.fillerEl) { 131 | this.fillerEl = document.createElement('div'); 132 | this.fillerEl.style.height = this.el.offsetHeight + 'px'; 133 | this.renderer.addClass(this.fillerEl, this.STICKY_CLASSES.FILLER); 134 | this.parentEl.insertBefore(this.fillerEl, this.el); 135 | } 136 | 137 | Object.assign(this.el.style, { 138 | position: 'fixed', //fixed is a lot smoother than absolute 139 | float: 'none', 140 | top: stickyOffsetTop + 'px', 141 | bottom: 'inherit' 142 | }, dynProps); 143 | this.renderer.removeClass(this.el, this.STICKY_CLASSES.UNSTUCK); 144 | this.renderer.removeClass(this.el, this.STICKY_CLASSES.TOP); 145 | this.renderer.removeClass(this.el, this.STICKY_CLASSES.BOTTOM); 146 | this.renderer.addClass(this.el, this.STICKY_CLASSES.STUCK); 147 | } else { 148 | /** 149 | * sticky element is in the original position 150 | */ 151 | if (this.fillerEl) { 152 | this.parentEl.removeChild(this.fillerEl); //IE11 does not work with el.remove() 153 | this.fillerEl = undefined; 154 | } 155 | Object.assign(this.el.style, { 156 | position: this.original.position, 157 | float: this.original.float, 158 | top: this.original.top, 159 | bottom: this.original.bottom, 160 | width: this.original.width, 161 | left: this.original.left 162 | }, dynProps); 163 | this.renderer.removeClass(this.el, this.STICKY_CLASSES.STUCK); 164 | this.renderer.removeClass(this.el, this.STICKY_CLASSES.BOTTOM); 165 | this.renderer.addClass(this.el, this.STICKY_CLASSES.UNSTUCK); 166 | this.renderer.addClass(this.el, this.STICKY_CLASSES.TOP); 167 | } 168 | } 169 | } 170 | 171 | -------------------------------------------------------------------------------- /src/sticky.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { FormsModule } from "@angular/forms"; 3 | import { CommonModule } from '@angular/common'; 4 | 5 | import {NguiStickyDirective} from "./sticky.directive"; 6 | 7 | export { NguiStickyDirective }; 8 | 9 | @NgModule({ 10 | imports: [ CommonModule, FormsModule ], 11 | declarations: [NguiStickyDirective], 12 | exports: [ NguiStickyDirective ] 13 | }) 14 | export class NguiStickyModule {} 15 | 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es5", "es6", "dom"], 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "sourceMap": true, 10 | "pretty": true, 11 | "allowUnreachableCode": true, 12 | "allowUnusedLabels": true, 13 | "noImplicitAny": false, 14 | "noImplicitReturns": false, 15 | "noImplicitUseStrict": false, 16 | "allowSyntheticDefaultImports": true, 17 | "suppressExcessPropertyErrors": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "skipDefaultLibCheck": true, 20 | "noEmitHelpers": false, 21 | "isolatedModules": false, 22 | "strictNullChecks": false, 23 | "outDir": "dist", 24 | "baseUrl": "src", 25 | "paths": { 26 | "@ngui/sticky": ["./index"] 27 | } 28 | }, 29 | "files": [ 30 | "src/index.ts" 31 | ], 32 | "exclude": [ 33 | "node_modules" 34 | ], 35 | "compileOnSave": false, 36 | "buildOnSave": false, 37 | "angularCompilerOptions": { 38 | "strictMetadataEmit": true, 39 | "skipTemplateCodegen": true 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /tsconfig.ngc.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es5", "es6", "dom"], 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "pretty": true, 12 | "allowUnreachableCode": true, 13 | "allowUnusedLabels": true, 14 | "noImplicitAny": false, 15 | "noImplicitReturns": false, 16 | "noImplicitUseStrict": false, 17 | "allowSyntheticDefaultImports": true, 18 | "suppressExcessPropertyErrors": true, 19 | "suppressImplicitAnyIndexErrors": true, 20 | "skipDefaultLibCheck": true, 21 | "noEmitHelpers": false, 22 | "isolatedModules": false, 23 | "strictNullChecks": false, 24 | "outDir": "dist", 25 | "baseUrl": "src", 26 | "paths": { 27 | "@ngui/sticky": ["./index"] 28 | } 29 | }, 30 | "files": [ 31 | "src/index.ts" 32 | ], 33 | "exclude": [ 34 | "node_modules" 35 | ], 36 | "compileOnSave": false, 37 | "buildOnSave": false, 38 | "angularCompilerOptions": { 39 | "strictMetadataEmit": true, 40 | "skipTemplateCodegen": true 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | entry: { 6 | '@ngui/sticky': path.join(__dirname, 'src', 'index.ts') 7 | }, 8 | resolve: { 9 | extensions: ['.ts', '.js', '.json', '.css', '.html'] 10 | }, 11 | resolveLoader: { 12 | modules: [path.join(__dirname, 'node_modules')] 13 | }, 14 | output: { 15 | path: path.join(__dirname, 'dist'), 16 | filename: "sticky.umd.js", 17 | library: ["sticky"], 18 | libraryTarget: "umd" 19 | }, 20 | externals: [ 21 | /^rxjs\//, //.... any other way? rx.umd.min.js does work? 22 | /^@angular\// 23 | ], 24 | devtool: 'source-map', 25 | module: { 26 | rules: [ 27 | { // Support for .ts files. 28 | test: /\.ts$/, 29 | use: ['ts-loader', 'angular2-template-loader'] 30 | } 31 | ] 32 | } 33 | }; 34 | --------------------------------------------------------------------------------